Kaynağa Gözat

(bare bones) Interrupt transfer support

main
PaulStoffregen 7 yıl önce
ebeveyn
işleme
252b9a2f24
4 değiştirilmiş dosya ile 113 ekleme ve 8 silme
  1. +3
    -1
      USBHost.h
  2. +87
    -6
      ehci.cpp
  3. +1
    -0
      enumeration.cpp
  4. +22
    -1
      hub.cpp

+ 3
- 1
USBHost.h Dosyayı Görüntüle

@@ -139,7 +139,7 @@ public:
static void begin();
protected:
static Pipe_t * new_Pipe(Device_t *dev, uint32_t type, uint32_t endpoint,
uint32_t direction, uint32_t max_packet_len);
uint32_t direction, uint32_t maxlen, uint32_t interval=0);
static bool queue_Control_Transfer(Device_t *dev, setup_t *setup,
void *buf, USBDriver *driver);
static bool queue_Data_Transfer(Pipe_t *pipe, void *buffer,
@@ -232,6 +232,8 @@ protected:
virtual bool claim(Device_t *device, int type, const uint8_t *descriptors);
virtual bool control(const Transfer_t *transfer);
void poweron(uint32_t port);
static void callback(const Transfer_t *transfer);
void status_change(const Transfer_t *transfer);
setup_t setup;
uint8_t hub_desc[16];
uint8_t endpoint;

+ 87
- 6
ehci.cpp Dosyayı Görüntüle

@@ -24,7 +24,7 @@
#include <Arduino.h>
#include "USBHost.h"

#define PERIODIC_LIST_SIZE 64
#define PERIODIC_LIST_SIZE 32

static uint32_t periodictable[PERIODIC_LIST_SIZE] __attribute__ ((aligned(4096), used));
static uint8_t port_state;
@@ -47,6 +47,9 @@ static void add_to_async_followup_list(Transfer_t *first, Transfer_t *last);
static void remove_from_async_followup_list(Transfer_t *transfer);
static void add_to_periodic_followup_list(Transfer_t *first, Transfer_t *last);
static void remove_from_periodic_followup_list(Transfer_t *transfer);
static bool allocate_interrupt_pipe_bandwidth(uint32_t speed, uint32_t maxlen,
uint32_t interval, uint32_t direction, uint32_t *offset, uint32_t *smask,
uint32_t *cmask);

void USBHost::begin()
{
@@ -132,7 +135,7 @@ void USBHost::begin()
USBHS_FRINDEX = 0;
USBHS_ASYNCLISTADDR = 0;
USBHS_USBCMD = USBHS_USBCMD_ITC(8) | USBHS_USBCMD_RS |
USBHS_USBCMD_ASP(3) | USBHS_USBCMD_ASPE |
USBHS_USBCMD_ASP(3) | USBHS_USBCMD_ASPE | USBHS_USBCMD_PSE |
#if PERIODIC_LIST_SIZE == 8
USBHS_USBCMD_FS2 | USBHS_USBCMD_FS(3);
#elif PERIODIC_LIST_SIZE == 16
@@ -342,15 +345,23 @@ static uint32_t QH_capabilities2(uint32_t high_bw_mult, uint32_t hub_port_number
(split_completion_mask << 8) | (interrupt_schedule_mask << 0) );
}



// Create a new pipe. It's QH is added to the async or periodic schedule,
// and a halt qTD is added to the QH, so we can grow the qTD list later.
// dev: device owning this pipe/endpoint
// type: 0=control, 2=bulk, 3=interrupt
// endpoint: 0 for control, 1-15 for bulk or interrupt
// direction: 0=OUT, 1=IN (unused for control)
// maxlen: maximum packet size
// interval: polling interval for interrupt, power of 2, unused if control or bulk
//
Pipe_t * USBHost::new_Pipe(Device_t *dev, uint32_t type, uint32_t endpoint,
uint32_t direction, uint32_t max_packet_len)
uint32_t direction, uint32_t maxlen, uint32_t interval)
{
Pipe_t *pipe;
Transfer_t *halt;
uint32_t c=0, dtc=0;
uint32_t c=0, dtc=0, smask=0, cmask=0, offset=0;

Serial.println("new_Pipe");
pipe = allocate_Pipe();
@@ -360,6 +371,17 @@ Pipe_t * USBHost::new_Pipe(Device_t *dev, uint32_t type, uint32_t endpoint,
free_Pipe(pipe);
return NULL;
}
if (type == 3) {
// interrupt transfers require bandwidth & microframe scheduling
if (interval > PERIODIC_LIST_SIZE*8) interval = PERIODIC_LIST_SIZE*8;
if (dev->speed < 2 && interval < 8) interval = 8;
if (!allocate_interrupt_pipe_bandwidth(dev->speed,
maxlen, interval, direction, &offset, &smask, &cmask)) {
free_Transfer(halt);
free_Pipe(pipe);
return NULL;
}
}
memset(pipe, 0, sizeof(Pipe_t));
memset(halt, 0, sizeof(Transfer_t));
halt->qtd.next = 1;
@@ -378,10 +400,10 @@ Pipe_t * USBHost::new_Pipe(Device_t *dev, uint32_t type, uint32_t endpoint,
} else if (type == 3) {
// interrupt
}
pipe->qh.capabilities[0] = QH_capabilities1(15, c, max_packet_len, 0,
pipe->qh.capabilities[0] = QH_capabilities1(15, c, maxlen, 0,
dtc, dev->speed, endpoint, 0, dev->address);
pipe->qh.capabilities[1] = QH_capabilities2(1, dev->hub_port,
dev->hub_address, 0, 0);
dev->hub_address, cmask, smask);

if (type == 0 || type == 2) {
// control or bulk: add to async queue
@@ -401,6 +423,18 @@ Pipe_t * USBHost::new_Pipe(Device_t *dev, uint32_t type, uint32_t endpoint,
} else if (type == 3) {
// interrupt: add to periodic schedule
// TODO: link it into the periodic table

// TODO: built tree...
//uint32_t finterval = interval >> 3;
//for (uint32_t i=offset; i < PERIODIC_LIST_SIZE; i += finterval) {
// uint32_t list = periodictable[i];
//}

// quick hack for testing, just put it into the first table entry
pipe->qh.horizontal_link = periodictable[0];
periodictable[0] = (uint32_t)&(pipe->qh) | 2; // 2=QH
Serial.print("init periodictable with ");
Serial.println(periodictable[0], HEX);
}
return pipe;
}
@@ -668,3 +702,50 @@ static void remove_from_periodic_followup_list(Transfer_t *transfer)
}
}


// Allocate bandwidth for an interrupt pipe. Given the packet size
// and other parameters, find the best place to schedule this pipe.
// Returns true if enough bandwidth is available, and the best
// frame offset, smask and cmask. Or returns false if no group
// of microframes has enough bandwidth available.
//
// speed: [in] 0=full speed, 1=low speed, 2=high speed
// maxlen: [in] maximum packet length
// interval: [in] polling interval, in 125 us micro frames
// direction: [in] 0=OUT, 1=IN
// offset: [out] frame offset, 0 to PERIODIC_LIST_SIZE-1
// smask: [out] Start Mask
// cmask: [out] Complete Mask
//
static bool allocate_interrupt_pipe_bandwidth(uint32_t speed, uint32_t maxlen,
uint32_t interval, uint32_t direction, uint32_t *offset, uint32_t *smask,
uint32_t *cmask)
{
// TODO: actual bandwidth planning needs to go here... but for
// now we'll just always pile up everything at the same offset
// and same microframe schedule for split transactions, without
// even the slighest check whether it all fits.

if (speed == 2) {
// high speed 480 Mbit/sec
if (interval == 1) {
*smask = 0xFF;
} else if (interval == 2) {
*smask = 0x55;
} else if (interval <= 4) {
*smask = 0x11;
} else {
*smask = 0x01;
}
*cmask = 0;
*offset = 0;
} else {
// full speed 12 Mbit/sec or low speed 1.5 Mbit/sec
*smask = 0x01;
*cmask = 0x3C;
*offset = 0;
}
return true;
}



+ 1
- 0
enumeration.cpp Dosyayı Görüntüle

@@ -113,6 +113,7 @@ void USBHost::enumeration(const Transfer_t *transfer)
dev->enum_state = 1;
return;
case 1: // request all 18 bytes of device descriptor
dev->address = enumsetup.wValue;
pipe_set_addr(dev->control_pipe, enumsetup.wValue);
mk_setup(enumsetup, 0x80, 6, 0x0100, 0, 18); // 6=GET_DESCRIPTOR
queue_Control_Transfer(dev, &enumsetup, enumbuf, NULL);

+ 22
- 1
hub.cpp Dosyayı Görüntüle

@@ -114,7 +114,13 @@ bool USBHub::control(const Transfer_t *transfer)
} else if (state == numports) {
Serial.println("power turned on to all ports");
// TODO: create interrupt pipe for status change notifications
changepipe = new_Pipe(device, 3, endpoint, 1, 1);
Serial.print("device addr = ");
Serial.println(device->address);
changepipe = new_Pipe(device, 3, endpoint, 1, 1, 512);
Serial.print("pipe cap1 = ");
Serial.println(changepipe->qh.capabilities[0], HEX);
changepipe->callback_function = callback;
queue_Data_Transfer(changepipe, &changebits, 1, this);
state = 255;
} else if (state == 255) {
// parse a status response
@@ -122,6 +128,21 @@ bool USBHub::control(const Transfer_t *transfer)
return true;
}

void USBHub::callback(const Transfer_t *transfer)
{
Serial.println("HUB Callback (static)");
if (transfer->driver) ((USBHub *)(transfer->driver))->status_change(transfer);
}

void USBHub::status_change(const Transfer_t *transfer)
{
Serial.println("HUB Callback (member)");
Serial.print("status = ");
Serial.println(changebits, HEX);
// TODO: do something with the status change info
queue_Data_Transfer(changepipe, &changebits, 1, this);
}


/*
config descriptor from a Multi-TT hub

Yükleniyor…
İptal
Kaydet