| @@ -237,6 +237,8 @@ private: | |||
| 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); | |||
| static bool followup_Transfer(Transfer_t *transfer); | |||
| static void followup_Error(void); | |||
| protected: | |||
| #ifdef USBHOST_PRINT_DEBUG | |||
| static void print(const Transfer_t *transfer); | |||
| @@ -259,21 +261,21 @@ protected: | |||
| static void println() { Serial.println(); } | |||
| static void print(uint32_t n, uint8_t b) { Serial.print(n, b); } | |||
| static void println(uint32_t n, uint8_t b) { Serial.println(n, b); } | |||
| static void println(const char *s, int n) { | |||
| Serial.print(s); Serial.println(n); } | |||
| static void println(const char *s, unsigned int n) { | |||
| Serial.print(s); Serial.println(n); } | |||
| static void println(const char *s, long n) { | |||
| Serial.print(s); Serial.println(n); } | |||
| static void println(const char *s, unsigned long n) { | |||
| Serial.print(s); Serial.println(n); } | |||
| static void println(const char *s, int n, uint8_t b) { | |||
| static void print(const char *s, int n, uint8_t b = DEC) { | |||
| Serial.print(s); Serial.print(n, b); } | |||
| static void print(const char *s, unsigned int n, uint8_t b = DEC) { | |||
| Serial.print(s); Serial.print(n, b); } | |||
| static void print(const char *s, long n, uint8_t b = DEC) { | |||
| Serial.print(s); Serial.print(n, b); } | |||
| static void print(const char *s, unsigned long n, uint8_t b = DEC) { | |||
| Serial.print(s); Serial.print(n, b); } | |||
| static void println(const char *s, int n, uint8_t b = DEC) { | |||
| Serial.print(s); Serial.println(n, b); } | |||
| static void println(const char *s, unsigned int n, uint8_t b) { | |||
| static void println(const char *s, unsigned int n, uint8_t b = DEC) { | |||
| Serial.print(s); Serial.println(n, b); } | |||
| static void println(const char *s, long n, uint8_t b) { | |||
| static void println(const char *s, long n, uint8_t b = DEC) { | |||
| Serial.print(s); Serial.println(n, b); } | |||
| static void println(const char *s, unsigned long n, uint8_t b) { | |||
| static void println(const char *s, unsigned long n, uint8_t b = DEC) { | |||
| Serial.print(s); Serial.println(n, b); } | |||
| #else | |||
| static void print(const Transfer_t *transfer) {} | |||
| @@ -296,14 +298,14 @@ protected: | |||
| static void println() {} | |||
| static void print(uint32_t n, uint8_t b) {} | |||
| static void println(uint32_t n, uint8_t b) {} | |||
| static void println(const char *s, int n) {} | |||
| static void println(const char *s, unsigned int n) {} | |||
| static void println(const char *s, long n) {} | |||
| static void println(const char *s, unsigned long n) {} | |||
| static void println(const char *s, int n, uint8_t b) {} | |||
| static void println(const char *s, unsigned int n, uint8_t b) {} | |||
| static void println(const char *s, long n, uint8_t b) {} | |||
| static void println(const char *s, unsigned long n, uint8_t b) {} | |||
| static void print(const char *s, int n, uint8_t b = DEC) {} | |||
| static void print(const char *s, unsigned int n, uint8_t b = DEC) {} | |||
| static void print(const char *s, long n, uint8_t b = DEC) {} | |||
| static void print(const char *s, unsigned long n, uint8_t b = DEC) {} | |||
| static void println(const char *s, int n, uint8_t b = DEC) {} | |||
| static void println(const char *s, unsigned int n, uint8_t b = DEC) {} | |||
| static void println(const char *s, long n, uint8_t b = DEC) {} | |||
| static void println(const char *s, unsigned long n, uint8_t b = DEC) {} | |||
| #endif | |||
| static void mk_setup(setup_t &s, uint32_t bmRequestType, uint32_t bRequest, | |||
| uint32_t wValue, uint32_t wIndex, uint32_t wLength) { | |||
| @@ -92,7 +92,6 @@ static USBDriverTimer *active_timers=NULL; | |||
| static void init_qTD(volatile Transfer_t *t, void *buf, uint32_t len, | |||
| uint32_t pid, uint32_t data01, bool irq); | |||
| static bool followup_Transfer(Transfer_t *transfer); | |||
| 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); | |||
| @@ -305,6 +304,9 @@ void USBHost::isr() | |||
| } | |||
| } | |||
| } | |||
| if (stat & USBHS_USBSTS_UEI) { | |||
| followup_Error(); | |||
| } | |||
| if (stat & USBHS_USBSTS_PCI) { // port change detected | |||
| const uint32_t portstat = USBHS_PORTSC1; | |||
| @@ -753,9 +755,10 @@ bool USBHost::queue_Transfer(Pipe_t *pipe, Transfer_t *transfer) | |||
| return true; | |||
| } | |||
| static bool followup_Transfer(Transfer_t *transfer) | |||
| bool USBHost::followup_Transfer(Transfer_t *transfer) | |||
| { | |||
| //println(" Followup ", (uint32_t)transfer, HEX); | |||
| print(" Followup ", (uint32_t)transfer, HEX); | |||
| println(" token=", transfer->qtd.token, HEX); | |||
| if (!(transfer->qtd.token & 0x80)) { | |||
| // TODO: check error status | |||
| @@ -773,6 +776,91 @@ static bool followup_Transfer(Transfer_t *transfer) | |||
| return false; | |||
| } | |||
| void USBHost::followup_Error(void) | |||
| { | |||
| println("ERROR Followup"); | |||
| Transfer_t *p = async_followup_first; | |||
| while (p) { | |||
| if (followup_Transfer(p)) { | |||
| // transfer completed | |||
| Transfer_t *next = p->next_followup; | |||
| remove_from_async_followup_list(p); | |||
| println(" remove from followup list"); | |||
| if (p->qtd.token & 0x40) { | |||
| Pipe_t *haltedpipe = p->pipe; | |||
| free_Transfer(p); | |||
| // traverse the rest of the list for unfinished work | |||
| // from this halted pipe. Remove from the followup | |||
| // list and put onto our own temporary list | |||
| Transfer_t *first = NULL; | |||
| Transfer_t *last = NULL; | |||
| p = next; | |||
| while (p) { | |||
| Transfer_t *next2 = p->next_followup; | |||
| if (p->pipe == haltedpipe) { | |||
| println(" stray halted ", (uint32_t)p, HEX); | |||
| remove_from_async_followup_list(p); | |||
| if (first == NULL) { | |||
| first = p; | |||
| last = p; | |||
| } else { | |||
| last->next_followup = p; | |||
| } | |||
| p->next_followup = NULL; | |||
| if (next == p) next = next2; | |||
| } | |||
| p = next2; | |||
| } | |||
| // halted pipe (probably) still has unfinished transfers | |||
| // find the halted pipe's dummy halt transfer | |||
| p = (Transfer_t *)(haltedpipe->qh.next & ~0x1F); | |||
| while (p && ((p->qtd.token & 0x40) == 0)) { | |||
| print(" qtd: ", (uint32_t)p, HEX); | |||
| print(", token=", (uint32_t)p->qtd.token, HEX); | |||
| println(", next=", (uint32_t)p->qtd.next, HEX); | |||
| p = (Transfer_t *)(p->qtd.next & ~0x1F); | |||
| } | |||
| if (p) { | |||
| // unhalt the pipe, "forget" unfinished transfers | |||
| // hopefully they're all on the list we made! | |||
| println(" dummy halt: ", (uint32_t)p, HEX); | |||
| haltedpipe->qh.next = (uint32_t)p; | |||
| haltedpipe->qh.current = 0; | |||
| haltedpipe->qh.token = 0; | |||
| } else { | |||
| println(" no dummy halt found, yikes!"); | |||
| // TODO: this should never happen, but what if it does? | |||
| } | |||
| // Do any driver callbacks belonging to the unfinished | |||
| // transfers. This is done last, after retoring the | |||
| // pipe to a working state (if possible) so the driver | |||
| // callback can use the pipe. | |||
| p = first; | |||
| while (p) { | |||
| uint32_t token = p->qtd.token; | |||
| if (token & 0x8000 && haltedpipe->callback_function) { | |||
| // driver expects a callback | |||
| p->qtd.token = token | 0x40; | |||
| (*(p->pipe->callback_function))(p); | |||
| } | |||
| Transfer_t *next2 = p->next_followup; | |||
| free_Transfer(p); | |||
| p = next2; | |||
| } | |||
| } else { | |||
| free_Transfer(p); | |||
| } | |||
| p = next; | |||
| } else { | |||
| // transfer still pending | |||
| println(" remain on followup list"); | |||
| p = p->next_followup; | |||
| } | |||
| } | |||
| // TODO: handle errors from periodic schedule! | |||
| } | |||
| static void add_to_async_followup_list(Transfer_t *first, Transfer_t *last) | |||
| { | |||
| last->next_followup = NULL; // always add to end of list | |||