diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2014-07-18 16:26:12 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-07-18 16:34:07 -0700 |
commit | 81e38333513cec155c720432226dabe9f9f76a77 (patch) | |
tree | 27889eb4e928866b5ea6496dbcdd9abc1c23e9e3 /drivers/usb/host/ohci.h | |
parent | cdb4dd15e62eb984d9461b520d15d00ff2b88d9d (diff) | |
download | linux-81e38333513cec155c720432226dabe9f9f76a77.tar.gz linux-81e38333513cec155c720432226dabe9f9f76a77.tar.bz2 linux-81e38333513cec155c720432226dabe9f9f76a77.zip |
USB: OHCI: add I/O watchdog for orphan TDs
Some OHCI controllers have a bug: They fail to add completed TDs to
the done queue. Examining this queue is the only method ohci-hcd has
for telling when a transfer is complete; failure to add a TD can
result in an URB that never completes and cannot be unlinked.
This patch adds a watchdog routine to ohci-hcd. The routine
periodically scans the active ED and TD lists, looking for TDs which
are finished but not on the done queue. When one is found, and it is
certain that the controller hardware will never add the TD to the done
queue, the watchdog routine manually puts the TD on the done list so
that it can be handled normally.
The watchdog routine also checks for a condition indicating the
controller has died. If the done queue is non-empty but the
HccaDoneHead pointer hasn't been updated for a few hundred
milliseconds, we assume the controller will never update it and
therefore is dead.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host/ohci.h')
-rw-r--r-- | drivers/usb/host/ohci.h | 13 |
1 files changed, 13 insertions, 0 deletions
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index ef348c2e1e4b..0548f5ca18e2 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -47,6 +47,7 @@ struct ed { struct ed *ed_next; /* on schedule or rm_list */ struct ed *ed_prev; /* for non-interrupt EDs */ struct list_head td_list; /* "shadow list" of our TDs */ + struct list_head in_use_list; /* create --> IDLE --> OPER --> ... --> IDLE --> destroy * usually: OPER --> UNLINK --> (IDLE | OPER) --> ... @@ -66,6 +67,13 @@ struct ed { /* HC may see EDs on rm_list until next frame (frame_no == tick) */ u16 tick; + + /* Detect TDs not added to the done queue */ + unsigned takeback_wdh_cnt; + struct td *pending_td; +#define OKAY_TO_TAKEBACK(ohci, ed) \ + ((int) (ohci->wdh_cnt - ed->takeback_wdh_cnt) >= 0) + } __attribute__ ((aligned(16))); #define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */ @@ -382,6 +390,7 @@ struct ohci_hcd { struct td *td_hash [TD_HASH_SIZE]; struct td *dl_start, *dl_end; /* the done list */ struct list_head pending; + struct list_head eds_in_use; /* all EDs with at least 1 TD */ /* * driver state @@ -412,6 +421,10 @@ struct ohci_hcd { // there are also chip quirks/bugs in init logic + unsigned wdh_cnt, prev_wdh_cnt; + u32 prev_donehead; + struct timer_list io_watchdog; + struct work_struct nec_work; /* Worker for NEC quirk */ struct dentry *debug_dir; |