Add error recovery for async schedule errors & halted pipes

This commit is contained in:
PaulStoffregen 2017-07-02 02:15:43 -07:00
parent 50016717b3
commit e345c0360d
2 changed files with 113 additions and 23 deletions

View File

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

View File

@ -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