From 80225dfe227d61b6c2a33f208d6178fb4a9d4039 Mon Sep 17 00:00:00 2001 From: PaulStoffregen Date: Sun, 26 Feb 2017 09:40:49 -0800 Subject: [PATCH] Add to periodic sched using interval+offset & binary tree --- USBHost.h | 8 ++++--- ehci.cpp | 67 ++++++++++++++++++++++++++++++++++++++++------------ hub.cpp | 3 ++- keyboard.cpp | 2 +- print.cpp | 19 +++++++++++++++ 5 files changed, 79 insertions(+), 20 deletions(-) diff --git a/USBHost.h b/USBHost.h index 9c0534a..9aa2b8e 100644 --- a/USBHost.h +++ b/USBHost.h @@ -95,12 +95,12 @@ struct Pipe_struct { Device_t *device; 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 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; void (*callback_function)(const Transfer_t *); uint16_t periodic_interval; - uint16_t periodic_offset; + uint16_t periodic_offset; // TODO: is this redundant? uint32_t unused1; uint32_t unused2; uint32_t unused3; @@ -174,12 +174,14 @@ private: static void free_Transfer(Transfer_t *q); static bool allocate_interrupt_pipe_bandwidth(Pipe_t *pipe, uint32_t maxlen, uint32_t interval); + static void add_qh_to_periodic_schedule(Pipe_t *pipe); protected: static void print(const Transfer_t *transfer); static void print(const Transfer_t *first, const Transfer_t *last); static void print_token(uint32_t token); static void print(const Pipe_t *pipe); 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(const char *s) { Serial.print(s); } static void print(int n) { Serial.print(n); } diff --git a/ehci.cpp b/ehci.cpp index 04c48bd..8bc61ef 100644 --- a/ehci.cpp +++ b/ehci.cpp @@ -24,6 +24,10 @@ #include #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 static uint32_t periodictable[PERIODIC_LIST_SIZE] __attribute__ ((aligned(4096), used)); @@ -422,21 +426,7 @@ 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 - - - //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; } @@ -867,6 +857,53 @@ bool USBHost::allocate_interrupt_pipe_bandwidth(Pipe_t *pipe, uint32_t maxlen, u 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) { diff --git a/hub.cpp b/hub.cpp index 2855c9d..ad2d8a2 100644 --- a/hub.cpp +++ b/hub.cpp @@ -138,7 +138,8 @@ void USBHub::control(const Transfer_t *transfer) } else if (state == numports) { println("power turned on to all ports"); 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); changepipe->callback_function = callback; queue_Data_Transfer(changepipe, &changebits, 1, this); diff --git a/keyboard.cpp b/keyboard.cpp index 42784f2..63688ef 100644 --- a/keyboard.cpp +++ b/keyboard.cpp @@ -59,7 +59,7 @@ bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descripto if (size != 8) return false; // must be 8 bytes for Keyboard Boot Protocol uint32_t interval = descriptors[24]; 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; queue_Data_Transfer(datapipe, report, 8, this); return true; diff --git a/print.cpp b/print.cpp index 29ad89a..6303fb6 100644 --- a/print.cpp +++ b/print.cpp @@ -146,6 +146,25 @@ void USBHost::print_driverlist(const char *name, const USBDriver *driver) 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) {