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); | static void add_qh_to_periodic_schedule(Pipe_t *pipe); | ||||
static bool followup_Transfer(Transfer_t *transfer); | |||||
static void followup_Error(void); | |||||
protected: | protected: | ||||
#ifdef USBHOST_PRINT_DEBUG | #ifdef USBHOST_PRINT_DEBUG | ||||
static void print(const Transfer_t *transfer); | static void print(const Transfer_t *transfer); | ||||
static void println() { Serial.println(); } | static void println() { Serial.println(); } | ||||
static void print(uint32_t n, uint8_t b) { Serial.print(n, b); } | 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(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); } | 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); } | 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); } | 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); } | Serial.print(s); Serial.println(n, b); } | ||||
#else | #else | ||||
static void print(const Transfer_t *transfer) {} | static void print(const Transfer_t *transfer) {} | ||||
static void println() {} | static void println() {} | ||||
static void print(uint32_t n, uint8_t b) {} | static void print(uint32_t n, uint8_t b) {} | ||||
static void println(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 | #endif | ||||
static void mk_setup(setup_t &s, uint32_t bmRequestType, uint32_t bRequest, | static void mk_setup(setup_t &s, uint32_t bmRequestType, uint32_t bRequest, | ||||
uint32_t wValue, uint32_t wIndex, uint32_t wLength) { | uint32_t wValue, uint32_t wIndex, uint32_t wLength) { |
static void init_qTD(volatile Transfer_t *t, void *buf, uint32_t len, | static void init_qTD(volatile Transfer_t *t, void *buf, uint32_t len, | ||||
uint32_t pid, uint32_t data01, bool irq); | 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 add_to_async_followup_list(Transfer_t *first, Transfer_t *last); | ||||
static void remove_from_async_followup_list(Transfer_t *transfer); | 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 add_to_periodic_followup_list(Transfer_t *first, Transfer_t *last); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (stat & USBHS_USBSTS_UEI) { | |||||
followup_Error(); | |||||
} | |||||
if (stat & USBHS_USBSTS_PCI) { // port change detected | if (stat & USBHS_USBSTS_PCI) { // port change detected | ||||
const uint32_t portstat = USBHS_PORTSC1; | const uint32_t portstat = USBHS_PORTSC1; | ||||
return true; | 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)) { | if (!(transfer->qtd.token & 0x80)) { | ||||
// TODO: check error status | // TODO: check error status | ||||
return false; | 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) | static void add_to_async_followup_list(Transfer_t *first, Transfer_t *last) | ||||
{ | { | ||||
last->next_followup = NULL; // always add to end of list | last->next_followup = NULL; // always add to end of list |