USB: Wait for extra delay time after USB_PORT_FEAT_RESET for quirky hub
authorKai-Heng Feng <kai.heng.feng@canonical.com>
Fri, 19 Oct 2018 08:14:50 +0000 (16:14 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 7 Nov 2018 12:23:18 +0000 (13:23 +0100)
Devices connected under Terminus Technology Inc. Hub (1a40:0101) may
fail to work after the system resumes from suspend:
[  206.063325] usb 3-2.4: reset full-speed USB device number 4 using xhci_hcd
[  206.143691] usb 3-2.4: device descriptor read/64, error -32
[  206.351671] usb 3-2.4: device descriptor read/64, error -32

Info for this hub:
T:  Bus=03 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#=  2 Spd=480 MxCh= 4
D:  Ver= 2.00 Cls=09(hub  ) Sub=00 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=1a40 ProdID=0101 Rev=01.11
S:  Product=USB 2.0 Hub
C:  #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=100mA
I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub

Some expirements indicate that the USB devices connected to the hub are
innocent, it's the hub itself is to blame. The hub needs extra delay
time after it resets its port.

Hence wait for extra delay, if the device is connected to this quirky
hub.

Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
Cc: stable <stable@vger.kernel.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/admin-guide/kernel-parameters.txt
drivers/usb/core/hub.c
drivers/usb/core/quirks.c
include/linux/usb/quirks.h

index 81d1d5a7472804e50a663480ef92dc67abc71ab5..19f4423e70d913ea2bf0801e4e14b57dc74e320f 100644 (file)
                                        prevent spurious wakeup);
                                n = USB_QUIRK_DELAY_CTRL_MSG (Device needs a
                                        pause after every control message);
+                               o = USB_QUIRK_HUB_SLOW_RESET (Hub needs extra
+                                       delay after resetting its port);
                        Example: quirks=0781:5580:bk,0a5c:5834:gij
 
        usbhid.mousepoll=
index c6077d582d2966a4fbeffd092092c1bf58429db1..d9bd7576786a79e8a27b75fcbbd5805a662ffaaa 100644 (file)
@@ -2794,6 +2794,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
        int i, status;
        u16 portchange, portstatus;
        struct usb_port *port_dev = hub->ports[port1 - 1];
+       int reset_recovery_time;
 
        if (!hub_is_superspeed(hub->hdev)) {
                if (warm) {
@@ -2885,11 +2886,18 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
 
 done:
        if (status == 0) {
-               /* TRSTRCY = 10 ms; plus some extra */
                if (port_dev->quirks & USB_PORT_QUIRK_FAST_ENUM)
                        usleep_range(10000, 12000);
-               else
-                       msleep(10 + 40);
+               else {
+                       /* TRSTRCY = 10 ms; plus some extra */
+                       reset_recovery_time = 10 + 40;
+
+                       /* Hub needs extra delay after resetting its port. */
+                       if (hub->hdev->quirks & USB_QUIRK_HUB_SLOW_RESET)
+                               reset_recovery_time += 100;
+
+                       msleep(reset_recovery_time);
+               }
 
                if (udev) {
                        struct usb_hcd *hcd = bus_to_hcd(udev->bus);
index 178d6c6063c0280a06c6dc3dc6611394d8233479..4d7d948eae636ed939682c06fa1a66529442c290 100644 (file)
@@ -128,6 +128,9 @@ static int quirks_param_set(const char *val, const struct kernel_param *kp)
                        case 'n':
                                flags |= USB_QUIRK_DELAY_CTRL_MSG;
                                break;
+                       case 'o':
+                               flags |= USB_QUIRK_HUB_SLOW_RESET;
+                               break;
                        /* Ignore unrecognized flag characters */
                        }
                }
@@ -380,6 +383,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        { USB_DEVICE(0x1a0a, 0x0200), .driver_info =
                        USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
 
+       /* Terminus Technology Inc. Hub */
+       { USB_DEVICE(0x1a40, 0x0101), .driver_info = USB_QUIRK_HUB_SLOW_RESET },
+
        /* Corsair K70 RGB */
        { USB_DEVICE(0x1b1c, 0x1b13), .driver_info = USB_QUIRK_DELAY_INIT },
 
index b7a99ce56bc9ad6cd4c8ff2c5681d6fbb781b875..a1be64c9940fb4ad4e2365f226419f050d6addba 100644 (file)
@@ -66,4 +66,7 @@
 /* Device needs a pause after every control message. */
 #define USB_QUIRK_DELAY_CTRL_MSG               BIT(13)
 
+/* Hub needs extra delay after resetting its port. */
+#define USB_QUIRK_HUB_SLOW_RESET               BIT(14)
+
 #endif /* __LINUX_USB_QUIRKS_H */