path: root/drivers/usb/host/ehci-q.c
diff options
authorAlan Stern <stern@rowland.harvard.edu>2013-03-15 14:40:26 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-03-15 12:07:53 -0700
commit2a40f324541ee61c22146214349c2ce9f5c30bcf (patch)
treee1040e2c207c32561a9596ef40f51bf0ad328ed7 /drivers/usb/host/ehci-q.c
parent29f86e66428ee083aec106cca1748dc63d98ce23 (diff)
USB: EHCI: fix regression during bus resume
This patch (as1663) fixes a regression caused by commit 6e0c3339a6f19d748f16091d0a05adeb1e1f822b (USB: EHCI: unlink one async QH at a time). In order to avoid keeping multiple QHs in an unusable intermediate state, that commit changed unlink_empty_async() so that it unlinks only one empty QH at a time. However, when the EHCI root hub is suspended, _all_ async QHs need to be unlinked. ehci_bus_suspend() used to do this by calling unlink_empty_async(), but now this only unlinks one of the QHs, not all of them. The symptom is that when the root hub is resumed, USB communications don't work for some period of time. This is because ehci-hcd doesn't realize it needs to restart the async schedule; it assumes that because some QHs are already on the schedule, the schedule must be running. The easiest way to fix the problem is add a new function that unlinks all the async QHs when the root hub is suspended. This patch should be applied to all kernels that have the 6e0c3339a6f1 commit. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-and-tested-by: Adrian Bassett <adrian.bassett@hotmail.co.uk> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host/ehci-q.c')
1 files changed, 13 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 5464665f0b6a..23d136904285 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1316,6 +1316,19 @@ static void unlink_empty_async(struct ehci_hcd *ehci)
+/* The root hub is suspended; unlink all the async QHs */
+static void unlink_empty_async_suspended(struct ehci_hcd *ehci)
+ struct ehci_qh *qh;
+ while (ehci->async->qh_next.qh) {
+ qh = ehci->async->qh_next.qh;
+ WARN_ON(!list_empty(&qh->qtd_list));
+ single_unlink_async(ehci, qh);
+ }
+ start_iaa_cycle(ehci, false);
/* makes sure the async qh will become idle */
/* caller must own ehci->lock */