usb: Flush altsetting 0 endpoints before reinitializating them after reset.
authorMathias Nyman <mathias.nyman@linux.intel.com>
Wed, 14 May 2025 13:25:20 +0000 (16:25 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 21 May 2025 11:11:27 +0000 (13:11 +0200)
usb core avoids sending a Set-Interface altsetting 0 request after device
reset, and instead relies on calling usb_disable_interface() and
usb_enable_interface() to flush and reset host-side of those endpoints.

xHCI hosts allocate and set up endpoint ring buffers and host_ep->hcpriv
during usb_hcd_alloc_bandwidth() callback, which in this case is called
before flushing the endpoint in usb_disable_interface().

Call usb_disable_interface() before usb_hcd_alloc_bandwidth() to ensure
URBs are flushed before new ring buffers for the endpoints are allocated.

Otherwise host driver will attempt to find and remove old stale URBs
from a freshly allocated new ringbuffer.

Cc: stable <stable@kernel.org>
Fixes: 4fe0387afa89 ("USB: don't send Set-Interface after reset")
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20250514132520.225345-1-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/core/hub.c

index cfb3abafeacd30b990d672cc4a1b18bb8a5e96a1..416af6d76374008b880f6dc8d935b9f558f12a70 100644 (file)
@@ -6137,6 +6137,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        struct usb_hub                  *parent_hub;
        struct usb_hcd                  *hcd = bus_to_hcd(udev->bus);
        struct usb_device_descriptor    descriptor;
+       struct usb_interface            *intf;
        struct usb_host_bos             *bos;
        int                             i, j, ret = 0;
        int                             port1 = udev->portnum;
@@ -6194,6 +6195,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        if (!udev->actconfig)
                goto done;
 
+       /*
+        * Some devices can't handle setting default altsetting 0 with a
+        * Set-Interface request. Disable host-side endpoints of those
+        * interfaces here. Enable and reset them back after host has set
+        * its internal endpoint structures during usb_hcd_alloc_bandwith()
+        */
+       for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+               intf = udev->actconfig->interface[i];
+               if (intf->cur_altsetting->desc.bAlternateSetting == 0)
+                       usb_disable_interface(udev, intf, true);
+       }
+
        mutex_lock(hcd->bandwidth_mutex);
        ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
        if (ret < 0) {
@@ -6225,12 +6238,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
         */
        for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
                struct usb_host_config *config = udev->actconfig;
-               struct usb_interface *intf = config->interface[i];
                struct usb_interface_descriptor *desc;
 
+               intf = config->interface[i];
                desc = &intf->cur_altsetting->desc;
                if (desc->bAlternateSetting == 0) {
-                       usb_disable_interface(udev, intf, true);
                        usb_enable_interface(udev, intf, true);
                        ret = 0;
                } else {