summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci-hub.c
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2012-04-14 02:54:30 +0800
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2012-05-03 13:10:17 -0700
commitf370b9968a220a3d79d870dd7dee674cc0ff3d10 (patch)
tree65353e2712388752b40769ca6e53bdb7147a0d20 /drivers/usb/host/xhci-hub.c
parent32445605fc2ff967326c14156f199e620ed81c10 (diff)
downloadlinux-f370b9968a220a3d79d870dd7dee674cc0ff3d10.tar.bz2
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.c22
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;
}
}