aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2012-11-14 20:58:04 -0500
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2013-01-03 17:10:28 -0500
commit65bdac5effd15d6af619b3b7218627ef4d84ed6a (patch)
treef9c1663e31b0dda69dce000e9940a9f1b31c5a70
parent4f43447e62b37ee19c82a13f72f35b1ca60a74d3 (diff)
USB: Handle warm reset failure on empty port.
An empty port can transition to either Inactive or Compliance Mode if a newly connected USB 3.0 device fails to link train. In that case, we issue a warm reset. Some devices, such as John's Roseweil eusb3 enclosure, slip back into Compliance Mode after the warm reset. The current warm reset code does not check for device connect status on warm reset completion, and it incorrectly reports the warm reset succeeded. This causes the USB core to attempt to send a Set Address control transfer to a port in Compliance Mode, which will always fail. Make hub_port_wait_reset check the current connect status and link state after the warm reset completes. Return a failure status if the device is disconnected or the link state is Compliance Mode or SS.Inactive. Make hub_events disable the port if warm reset fails. This will disable the port, and then bring it back into the RxDetect state. Make the USB core ignore the connect change until the device reconnects. Note that this patch does NOT handle connected devices slipping into the Inactive state very well. This is a concern, because devices can go into the Inactive state on U1/U2 exit failure. However, the fix for that case is too large for stable, so it will be submitted in a separate patch. This patch should be backported to kernels as old as 3.2, contain the commit ID 75d7cf72ab9fa01dc70877aa5c68e8ef477229dc "usbcore: refine warm reset logic" Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Reported-by: John Covici <covici@ccs.covici.com> Cc: stable@vger.kernel.org
-rw-r--r--drivers/usb/core/hub.c12
1 files changed, 11 insertions, 1 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 42566d774d1..9641e9c1dec 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2597,6 +2597,11 @@ 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 (!(portstatus & USB_PORT_STAT_CONNECTION) ||
2601 hub_port_warm_reset_required(hub,
2602 portstatus))
2603 return -ENOTCONN;
2604
2600 return 0; 2605 return 0;
2601 } 2606 }
2602 2607
@@ -4694,9 +4699,14 @@ static void hub_events(void)
4694 * SS.Inactive state. 4699 * SS.Inactive state.
4695 */ 4700 */
4696 if (hub_port_warm_reset_required(hub, portstatus)) { 4701 if (hub_port_warm_reset_required(hub, portstatus)) {
4702 int status;
4703
4697 dev_dbg(hub_dev, "warm reset port %d\n", i); 4704 dev_dbg(hub_dev, "warm reset port %d\n", i);
4698 hub_port_reset(hub, i, NULL, 4705 status = hub_port_reset(hub, i, NULL,
4699 HUB_BH_RESET_TIME, true); 4706 HUB_BH_RESET_TIME, true);
4707 if (status < 0)
4708 hub_port_disable(hub, i, 1);
4709 connect_change = 0;
4700 } 4710 }
4701 4711
4702 if (connect_change) 4712 if (connect_change)