diff options
author | Andiry Xu <andiry.xu@amd.com> | 2012-04-14 02:54:30 +0800 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2012-05-03 13:10:17 -0700 |
commit | f370b9968a220a3d79d870dd7dee674cc0ff3d10 (patch) | |
tree | 65353e2712388752b40769ca6e53bdb7147a0d20 /drivers/usb/host/xhci-hub.c | |
parent | 32445605fc2ff967326c14156f199e620ed81c10 (diff) | |
download | blackbird-op-linux-f370b9968a220a3d79d870dd7dee674cc0ff3d10.tar.gz blackbird-op-linux-f370b9968a220a3d79d870dd7dee674cc0ff3d10.zip |
xHCI: keep track of ports being resumed and indicate in hub_status_data
This commit adds a bit-array to xhci bus_state for keeping track of
which ports are undergoing a resume transition. If any of the bits
are set when xhci_hub_status_data() is called, the routine will return
a non-zero value even if no ports have any status changes pending.
This will allow usbcore to handle races between root-hub suspend and
port wakeup.
This patch should be backported to kernels as old as 3.4, that contain
the commit 879d38e6bc36d73b0ac40ec9b0d839fda9fa8b1a "USB: fix race
between root-hub suspend and remote wakeup".
Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: stable@vger.kernel.org
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 22 |
1 files changed, 12 insertions, 10 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 673ad120c43e..89850a82d51b 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -558,6 +558,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_dbg(xhci, "Resume USB2 port %d\n", wIndex + 1); bus_state->resume_done[wIndex] = 0; + clear_bit(wIndex, &bus_state->resuming_ports); xhci_set_link_state(xhci, port_array, wIndex, XDEV_U0); xhci_dbg(xhci, "set port %d resume\n", @@ -845,7 +846,12 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) /* Initial status is no changes */ retval = (max_ports + 8) / 8; memset(buf, 0, retval); - status = 0; + + /* + * Inform the usbcore about resume-in-progress by returning + * a non-zero value even if there are no status changes. + */ + status = bus_state->resuming_ports; mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC; @@ -885,15 +891,11 @@ int xhci_bus_suspend(struct usb_hcd *hcd) spin_lock_irqsave(&xhci->lock, flags); if (hcd->self.root_hub->do_remote_wakeup) { - port_index = max_ports; - while (port_index--) { - if (bus_state->resume_done[port_index] != 0) { - spin_unlock_irqrestore(&xhci->lock, flags); - xhci_dbg(xhci, "suspend failed because " - "port %d is resuming\n", - port_index + 1); - return -EBUSY; - } + if (bus_state->resuming_ports) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_dbg(xhci, "suspend failed because " + "a port is resuming\n"); + return -EBUSY; } } |