History

USB: OHCI: add check for stopped frame counter

This patch adds an extra check to ohci-hcd's I/O watchdog routine. If the controller stops updating the frame counter, we will assume it is dead. But there has to be an exception: Some controllers stop the frame counter when no ports are connected. Check to make sure there is at least one active port before deciding the controller is dead. (This test may appear racy, but it isn't. Enabling a newly connected port takes several milliseconds, during which time the frame counter must advance.)

Alan Stern committed 12 years ago

Commit 499b3801 parent 81e38332 files · +35 −2Browse files

Unified · Split

2 files
  1. drivers/usb/host/ohci-hcd.c +34 −2
  2. drivers/usb/host/ohci.h +1 −0
drivers/usb/host/ohci-hcd.c+34−2
@@ -72,7 +72,7 @@ · 71 unmodified lines
7272 static const char hcd_name [] = "ohci_hcd";
7373
7474 #define STATECHANGE_DELAY msecs_to_jiffies(300)
75−#define IO_WATCHDOG_DELAY msecs_to_jiffies(250)
75+#define IO_WATCHDOG_DELAY msecs_to_jiffies(250)
7676
7777 #include "ohci.h"
7878 #include "pci-quirks.h"
@@ -230,9 +230,11 @@ static int ohci_urb_enqueue ( · 151 unmodified lines
230230
231231 /* Start up the I/O watchdog timer, if it's not running */
232232 if (!timer_pending(&ohci->io_watchdog) &&
233− list_empty(&ohci->eds_in_use))
233+ list_empty(&ohci->eds_in_use)) {
234+ ohci->prev_frame_no = ohci_frame_no(ohci);
234235 mod_timer(&ohci->io_watchdog,
235236 jiffies + IO_WATCHDOG_DELAY);
237+ }
236238 list_add(&ed->in_use_list, &ohci->eds_in_use);
237239
238240 if (ed->type == PIPE_ISOCHRONOUS) {
@@ -727,6 +729,7 @@ static void io_watchdog_func(unsigned long _ohci) · 488 unmodified lines
727729 u32 head;
728730 struct ed *ed;
729731 struct td *td, *td_start, *td_next;
732+ unsigned frame_no;
730733 unsigned long flags;
731734
732735 spin_lock_irqsave(&ohci->lock, flags);
@@ -742,6 +745,7 @@ static void io_watchdog_func(unsigned long _ohci) · 9 unmodified lines
742745 if (!(status & OHCI_INTR_WDH) && ohci->wdh_cnt == ohci->prev_wdh_cnt) {
743746 if (ohci->prev_donehead) {
744747 ohci_err(ohci, "HcDoneHead not written back; disabled\n");
748+ died:
745749 usb_hc_died(ohci_to_hcd(ohci));
746750 ohci_dump(ohci);
747751 ohci_shutdown(ohci_to_hcd(ohci));
@@ -802,7 +806,35 @@ static void io_watchdog_func(unsigned long _ohci) · 54 unmodified lines
802806 ohci_work(ohci);
803807
804808 if (ohci->rh_state == OHCI_RH_RUNNING) {
809+
810+ /*
811+ * Sometimes a controller just stops working. We can tell
812+ * by checking that the frame counter has advanced since
813+ * the last time we ran.
814+ *
815+ * But be careful: Some controllers violate the spec by
816+ * stopping their frame counter when no ports are active.
817+ */
818+ frame_no = ohci_frame_no(ohci);
819+ if (frame_no == ohci->prev_frame_no) {
820+ int active_cnt = 0;
821+ int i;
822+ unsigned tmp;
823+
824+ for (i = 0; i < ohci->num_ports; ++i) {
825+ tmp = roothub_portstatus(ohci, i);
826+ /* Enabled and not suspended? */
827+ if ((tmp & RH_PS_PES) && !(tmp & RH_PS_PSS))
828+ ++active_cnt;
829+ }
830+
831+ if (active_cnt > 0) {
832+ ohci_err(ohci, "frame counter not updating; disabled\n");
833+ goto died;
834+ }
835+ }
805836 if (!list_empty(&ohci->eds_in_use)) {
837+ ohci->prev_frame_no = frame_no;
806838 ohci->prev_wdh_cnt = ohci->wdh_cnt;
807839 ohci->prev_donehead = ohci_readl(ohci,
808840 &ohci->regs->donehead);
drivers/usb/host/ohci.h+1−0
@@ -421,6 +421,7 @@ struct ohci_hcd { · 420 unmodified lines
421421
422422 // there are also chip quirks/bugs in init logic
423423
424+ unsigned prev_frame_no;
424425 unsigned wdh_cnt, prev_wdh_cnt;
425426 u32 prev_donehead;
426427 struct timer_list io_watchdog;

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t