]>
Commit | Line | Data |
---|---|---|
ac193935 GKH |
1 | From 481f2d4f89f87a0baa26147f323380e31cfa7c44 Mon Sep 17 00:00:00 2001 |
2 | From: Julius Werner <jwerner@chromium.org> | |
3 | Date: Tue, 30 Jul 2013 19:51:20 -0700 | |
4 | Subject: usb: core: don't try to reset_device() a port that got just disconnected | |
5 | ||
6 | From: Julius Werner <jwerner@chromium.org> | |
7 | ||
8 | commit 481f2d4f89f87a0baa26147f323380e31cfa7c44 upstream. | |
9 | ||
10 | The USB hub driver's event handler contains a check to catch SuperSpeed | |
11 | devices that transitioned into the SS.Inactive state and tries to fix | |
12 | them with a reset. It decides whether to do a plain hub port reset or | |
13 | call the usb_reset_device() function based on whether there was a device | |
14 | attached to the port. | |
15 | ||
16 | However, there are device/hub combinations (found with a JetFlash | |
17 | Transcend mass storage stick (8564:1000) on the root hub of an Intel | |
18 | LynxPoint PCH) which can transition to the SS.Inactive state on | |
19 | disconnect (and stay there long enough for the host to notice). In this | |
20 | case, above-mentioned reset check will call usb_reset_device() on the | |
21 | stale device data structure. The kernel will send pointless LPM control | |
22 | messages to the no longer connected device address and can even cause | |
23 | several 5 second khubd stalls on some (buggy?) host controllers, before | |
24 | finally accepting the device's fate amongst a flurry of error messages. | |
25 | ||
26 | This patch makes the choice of reset dependent on the port status that | |
27 | has just been read from the hub in addition to the existence of an | |
28 | in-kernel data structure for the device, and only proceeds with the more | |
29 | extensive reset if both are valid. | |
30 | ||
31 | Signed-off-by: Julius Werner <jwerner@chromium.org> | |
32 | Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> | |
33 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
34 | ||
35 | --- | |
36 | drivers/usb/core/hub.c | 5 +++-- | |
37 | 1 file changed, 3 insertions(+), 2 deletions(-) | |
38 | ||
39 | --- a/drivers/usb/core/hub.c | |
40 | +++ b/drivers/usb/core/hub.c | |
41 | @@ -3890,7 +3890,8 @@ static void hub_events(void) | |
42 | hub->hdev->children[i - 1]; | |
43 | ||
44 | dev_dbg(hub_dev, "warm reset port %d\n", i); | |
45 | - if (!udev) { | |
46 | + if (!udev || !(portstatus & | |
47 | + USB_PORT_STAT_CONNECTION)) { | |
48 | status = hub_port_reset(hub, i, | |
49 | NULL, HUB_BH_RESET_TIME, | |
50 | true); | |
51 | @@ -3900,8 +3901,8 @@ static void hub_events(void) | |
52 | usb_lock_device(udev); | |
53 | status = usb_reset_device(udev); | |
54 | usb_unlock_device(udev); | |
55 | + connect_change = 0; | |
56 | } | |
57 | - connect_change = 0; | |
58 | } | |
59 | ||
60 | if (connect_change) |