Device_t *device; | Device_t *device; | ||||
uint8_t type; // 0=control, 1=isochronous, 2=bulk, 3=interrupt | uint8_t type; // 0=control, 1=isochronous, 2=bulk, 3=interrupt | ||||
uint8_t direction; // 0=out, 1=in (changes for control, others fixed) | uint8_t direction; // 0=out, 1=in (changes for control, others fixed) | ||||
uint8_t start_mask; | |||||
uint8_t complete_mask; | |||||
uint8_t start_mask; // TODO: is this redundant? | |||||
uint8_t complete_mask; // TODO: is this redundant? | |||||
Pipe_t *next; | Pipe_t *next; | ||||
void (*callback_function)(const Transfer_t *); | void (*callback_function)(const Transfer_t *); | ||||
uint16_t periodic_interval; | uint16_t periodic_interval; | ||||
uint16_t periodic_offset; | |||||
uint16_t periodic_offset; // TODO: is this redundant? | |||||
uint32_t unused1; | uint32_t unused1; | ||||
uint32_t unused2; | uint32_t unused2; | ||||
uint32_t unused3; | uint32_t unused3; | ||||
static void free_Transfer(Transfer_t *q); | static void free_Transfer(Transfer_t *q); | ||||
static bool allocate_interrupt_pipe_bandwidth(Pipe_t *pipe, | static bool allocate_interrupt_pipe_bandwidth(Pipe_t *pipe, | ||||
uint32_t maxlen, uint32_t interval); | uint32_t maxlen, uint32_t interval); | ||||
static void add_qh_to_periodic_schedule(Pipe_t *pipe); | |||||
protected: | protected: | ||||
static void print(const Transfer_t *transfer); | static void print(const Transfer_t *transfer); | ||||
static void print(const Transfer_t *first, const Transfer_t *last); | static void print(const Transfer_t *first, const Transfer_t *last); | ||||
static void print_token(uint32_t token); | static void print_token(uint32_t token); | ||||
static void print(const Pipe_t *pipe); | static void print(const Pipe_t *pipe); | ||||
static void print_driverlist(const char *name, const USBDriver *driver); | static void print_driverlist(const char *name, const USBDriver *driver); | ||||
static void print_qh_list(const Pipe_t *list); | |||||
static void print_hexbytes(const void *ptr, uint32_t len); | static void print_hexbytes(const void *ptr, uint32_t len); | ||||
static void print(const char *s) { Serial.print(s); } | static void print(const char *s) { Serial.print(s); } | ||||
static void print(int n) { Serial.print(n); } | static void print(int n) { Serial.print(n); } |
#include <Arduino.h> | #include <Arduino.h> | ||||
#include "USBHost.h" | #include "USBHost.h" | ||||
// Size of the periodic list, in milliseconds. This determines the | |||||
// slowest rate we can poll interrupt endpoints. Each entry uses | |||||
// 12 bytes (4 for a pointer, 8 for bandwidth management). | |||||
// may be 8, 16, 32, 64, 128, 256, 512, 1024 | |||||
#define PERIODIC_LIST_SIZE 32 | #define PERIODIC_LIST_SIZE 32 | ||||
static uint32_t periodictable[PERIODIC_LIST_SIZE] __attribute__ ((aligned(4096), used)); | static uint32_t periodictable[PERIODIC_LIST_SIZE] __attribute__ ((aligned(4096), used)); | ||||
} | } | ||||
} else if (type == 3) { | } else if (type == 3) { | ||||
// interrupt: add to periodic schedule | // interrupt: add to periodic schedule | ||||
// TODO: link it into the periodic table | |||||
//add_qh_to_periodic_schedule(pipe); | |||||
// 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 | |||||
println("init periodictable with ", periodictable[0], HEX); | |||||
add_qh_to_periodic_schedule(pipe); | |||||
} | } | ||||
return pipe; | return pipe; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
// put a new pipe into the periodic schedule tree | |||||
// according to periodic_interval and periodic_offset | |||||
// | |||||
void USBHost::add_qh_to_periodic_schedule(Pipe_t *pipe) | |||||
{ | |||||
// quick hack for testing, just put it into the first table entry | |||||
println("add_qh_to_periodic_schedule:"); | |||||
#if 0 | |||||
pipe->qh.horizontal_link = periodictable[0]; | |||||
periodictable[0] = (uint32_t)&(pipe->qh) | 2; // 2=QH | |||||
println("init periodictable with ", periodictable[0], HEX); | |||||
#else | |||||
uint32_t interval = pipe->periodic_interval; | |||||
uint32_t offset = pipe->periodic_offset; | |||||
println(" interval = ", interval); | |||||
println(" offset = ", offset); | |||||
// TODO: does this really make an inverted tree like EHCI figure 4-18, page 93 | |||||
for (uint32_t i=offset; i < PERIODIC_LIST_SIZE; i += interval) { | |||||
uint32_t num = periodictable[i]; | |||||
Pipe_t *node = (Pipe_t *)(num & 0xFFFFFFE0); | |||||
if ((num & 1) || ((num & 6) == 2 && node->periodic_interval < interval)) { | |||||
println(" add to slot ", i); | |||||
pipe->qh.horizontal_link = num; | |||||
periodictable[i] = (uint32_t)&(pipe->qh) | 2; // 2=QH | |||||
} else { | |||||
println(" traverse list ", i); | |||||
// TODO: skip past iTD, siTD when/if we support isochronous | |||||
while (node->periodic_interval >= interval) { | |||||
if (node->qh.horizontal_link & 1) break; | |||||
num = node->qh.horizontal_link; | |||||
node = (Pipe_t *)(num & 0xFFFFFFE0); | |||||
} | |||||
pipe->qh.horizontal_link = num; | |||||
node->qh.horizontal_link = (uint32_t)pipe | 2; // 2=QH | |||||
} | |||||
} | |||||
#endif | |||||
println("Periodic Schedule:"); | |||||
for (uint32_t i=0; i < PERIODIC_LIST_SIZE; i++) { | |||||
if (i < 10) print(" "); | |||||
print(i); | |||||
print(": "); | |||||
print_qh_list((Pipe_t *)(periodictable[i] & 0xFFFFFFE0)); | |||||
} | |||||
} | |||||
void USBHost::delete_Pipe(Pipe_t *pipe) | void USBHost::delete_Pipe(Pipe_t *pipe) | ||||
{ | { |
} else if (state == numports) { | } else if (state == numports) { | ||||
println("power turned on to all ports"); | println("power turned on to all ports"); | ||||
println("device addr = ", device->address); | println("device addr = ", device->address); | ||||
changepipe = new_Pipe(device, 3, endpoint, 1, 1, 512); | |||||
// TODO: use hub's interrupt endpoint interval | |||||
changepipe = new_Pipe(device, 3, endpoint, 1, 1, 64); | |||||
println("pipe cap1 = ", changepipe->qh.capabilities[0], HEX); | println("pipe cap1 = ", changepipe->qh.capabilities[0], HEX); | ||||
changepipe->callback_function = callback; | changepipe->callback_function = callback; | ||||
queue_Data_Transfer(changepipe, &changebits, 1, this); | queue_Data_Transfer(changepipe, &changebits, 1, this); |
if (size != 8) return false; // must be 8 bytes for Keyboard Boot Protocol | if (size != 8) return false; // must be 8 bytes for Keyboard Boot Protocol | ||||
uint32_t interval = descriptors[24]; | uint32_t interval = descriptors[24]; | ||||
println("polling interval = ", interval); | println("polling interval = ", interval); | ||||
datapipe = new_Pipe(dev, 3, endpoint, 1, 8, 64); | |||||
datapipe = new_Pipe(dev, 3, endpoint, 1, 8, interval); | |||||
datapipe->callback_function = callback; | datapipe->callback_function = callback; | ||||
queue_Data_Transfer(datapipe, report, 8, this); | queue_Data_Transfer(datapipe, report, 8, this); | ||||
return true; | return true; |
Serial.println(); | Serial.println(); | ||||
} | } | ||||
void USBHost::print_qh_list(const Pipe_t *list) | |||||
{ | |||||
if (!list) { | |||||
Serial.println("(empty)"); | |||||
return; | |||||
} | |||||
const Pipe_t *node = list; | |||||
while (1) { | |||||
Serial.print((uint32_t)node, HEX); | |||||
node = (const Pipe_t *)(node->qh.horizontal_link & 0xFFFFFFE0); | |||||
if (!node) break; | |||||
if (node == list) { | |||||
Serial.print(" (loops)"); | |||||
break; | |||||
} | |||||
Serial.print(" -> "); | |||||
} | |||||
Serial.println(); | |||||
} | |||||
void USBHost::print_hexbytes(const void *ptr, uint32_t len) | void USBHost::print_hexbytes(const void *ptr, uint32_t len) | ||||
{ | { |