| 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) | ||||
| { | { |