aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLuben Tuikov <ltuikov@yahoo.com>2011-02-11 14:33:10 -0500
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-02-20 10:07:04 -0500
commit07194ab7be63a972096309ab0ea747df455c6a20 (patch)
tree83fa4d4706a3404723da0f66db536fee6eea098e /drivers
parentbcd2fde05341cef0052e49566ec88b406a521cf3 (diff)
USB: Reset USB 3.0 devices on (re)discovery
If the device isn't reset, the XHCI HCD sends SET ADDRESS to address 0 while the device is already in Addressed state, and the request is dropped on the floor as it is addressed to the default address. This sequence of events, which this patch fixes looks like this: usb_reset_and_verify_device() hub_port_init() hub_set_address() SET_ADDRESS to 0 with 1 usb_get_device_descriptor(udev, 8) usb_get_device_descriptor(udev, 18) descriptors_changed() --> goto re_enumerate: hub_port_logical_disconnect() kick_khubd() And then: hub_events() hub_port_connect_change() usb_disconnect() usb_disable_device() new device struct sets device state to Powered choose_address() hub_port_init() <-- no reset, but SET ADDRESS to 0 with 1, timeout! The solution is to always reset the device in hub_port_init() to put it in a known state. Note from Sarah Sharp: This patch should be queued for stable trees all the way back to 2.6.34, since that was the first kernel that supported configured device reset. The code this patch touches has been there since 2.6.32, but the bug would never be hit before 2.6.34 because the xHCI driver would completely reject an attempt to reset a configured device under xHCI. Signed-off-by: Luben Tuikov <ltuikov@yahoo.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: stable@kernel.org
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/core/hub.c18
1 files changed, 7 insertions, 11 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index d041c6826e4..0f299b7aad6 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2681,17 +2681,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
2681 2681
2682 mutex_lock(&usb_address0_mutex); 2682 mutex_lock(&usb_address0_mutex);
2683 2683
2684 if (!udev->config && oldspeed == USB_SPEED_SUPER) { 2684 /* Reset the device; full speed may morph to high speed */
2685 /* Don't reset USB 3.0 devices during an initial setup */ 2685 /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
2686 usb_set_device_state(udev, USB_STATE_DEFAULT); 2686 retval = hub_port_reset(hub, port1, udev, delay);
2687 } else { 2687 if (retval < 0) /* error or disconnect */
2688 /* Reset the device; full speed may morph to high speed */ 2688 goto fail;
2689 /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ 2689 /* success, speed is known */
2690 retval = hub_port_reset(hub, port1, udev, delay); 2690
2691 if (retval < 0) /* error or disconnect */
2692 goto fail;
2693 /* success, speed is known */
2694 }
2695 retval = -ENODEV; 2691 retval = -ENODEV;
2696 2692
2697 if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { 2693 if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {