aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core/hub.c
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2012-11-15 17:58:04 -0500
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2013-01-03 17:10:26 -0500
commit4f43447e62b37ee19c82a13f72f35b1ca60a74d3 (patch)
tree9a34ec190c212541cc7b7ffe42225ce2ce962115 /drivers/usb/core/hub.c
parent77c7f072c87fa951e9a74805febf26466f31170c (diff)
USB: Ignore port state until reset completes.
The port reset code bails out early if the current connect status is cleared (device disconnected). If we're issuing a hot reset, it may also look at the link state before the reset is finished. Section 10.14.2.6 of the USB 3.0 spec says that when a port enters the Error state or Resetting state, the port connection bit retains the value from the previous state. Therefore we can't trust it until the reset finishes. Also, the xHCI spec section 4.19.1.2.5 says software shall ignore the link state while the port is resetting, as it can be in an unknown state. The port state during reset is also unknown for USB 2.0 hubs. The hub sends a reset signal by driving the bus into an SE0 state. This overwhelms the "connect" signal from the device, so the port can't tell whether anything is connected or not. Fix the port reset code to ignore the port link state and current connect bit until the reset finishes, and USB_PORT_STAT_RESET is cleared. Remove the check for USB_PORT_STAT_C_BH_RESET in the warm reset case, because it's redundant. When the warm reset finishes, the port reset bit will be cleared at the same time USB_PORT_STAT_C_BH_RESET is set. Remove the now-redundant check for a cleared USB_PORT_STAT_RESET bit in the code to deal with the finished reset. This patch should be backported to all stable kernels. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Cc: stable@vger.kernel.org
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r--drivers/usb/core/hub.c14
1 files changed, 7 insertions, 7 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b9ce5e8bda51..42566d774d18 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2534,6 +2534,10 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
2534 if (ret < 0) 2534 if (ret < 0)
2535 return ret; 2535 return ret;
2536 2536
2537 /* The port state is unknown until the reset completes. */
2538 if ((portstatus & USB_PORT_STAT_RESET))
2539 goto delay;
2540
2537 /* 2541 /*
2538 * Some buggy devices require a warm reset to be issued even 2542 * Some buggy devices require a warm reset to be issued even
2539 * when the port appears not to be connected. 2543 * when the port appears not to be connected.
@@ -2579,11 +2583,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
2579 if ((portchange & USB_PORT_STAT_C_CONNECTION)) 2583 if ((portchange & USB_PORT_STAT_C_CONNECTION))
2580 return -ENOTCONN; 2584 return -ENOTCONN;
2581 2585
2582 /* if we`ve finished resetting, then break out of 2586 if ((portstatus & USB_PORT_STAT_ENABLE)) {
2583 * the loop
2584 */
2585 if (!(portstatus & USB_PORT_STAT_RESET) &&
2586 (portstatus & USB_PORT_STAT_ENABLE)) {
2587 if (hub_is_wusb(hub)) 2587 if (hub_is_wusb(hub))
2588 udev->speed = USB_SPEED_WIRELESS; 2588 udev->speed = USB_SPEED_WIRELESS;
2589 else if (hub_is_superspeed(hub->hdev)) 2589 else if (hub_is_superspeed(hub->hdev))
@@ -2597,10 +2597,10 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
2597 return 0; 2597 return 0;
2598 } 2598 }
2599 } else { 2599 } else {
2600 if (portchange & USB_PORT_STAT_C_BH_RESET) 2600 return 0;
2601 return 0;
2602 } 2601 }
2603 2602
2603delay:
2604 /* switch to the long delay after two short delay failures */ 2604 /* switch to the long delay after two short delay failures */
2605 if (delay_time >= 2 * HUB_SHORT_RESET_TIME) 2605 if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
2606 delay = HUB_LONG_RESET_TIME; 2606 delay = HUB_LONG_RESET_TIME;