usb: Flush altsetting 0 endpoints before reinitializating them after reset.

commit 89bb3dc13ac29a563f4e4c555e422882f64742bd upstream.

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: 4fe0387afa ("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>
This commit is contained in:
Mathias Nyman
2025-05-14 16:25:20 +03:00
committed by Greg Kroah-Hartman
parent c40b5e0aad
commit 3c7e52cfa8

View File

@@ -6103,6 +6103,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
struct usb_hub *parent_hub; struct usb_hub *parent_hub;
struct usb_hcd *hcd = bus_to_hcd(udev->bus); struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_device_descriptor descriptor; struct usb_device_descriptor descriptor;
struct usb_interface *intf;
struct usb_host_bos *bos; struct usb_host_bos *bos;
int i, j, ret = 0; int i, j, ret = 0;
int port1 = udev->portnum; int port1 = udev->portnum;
@@ -6160,6 +6161,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (!udev->actconfig) if (!udev->actconfig)
goto done; 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); mutex_lock(hcd->bandwidth_mutex);
ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL); ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
if (ret < 0) { if (ret < 0) {
@@ -6191,12 +6204,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
*/ */
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
struct usb_host_config *config = udev->actconfig; struct usb_host_config *config = udev->actconfig;
struct usb_interface *intf = config->interface[i];
struct usb_interface_descriptor *desc; struct usb_interface_descriptor *desc;
intf = config->interface[i];
desc = &intf->cur_altsetting->desc; desc = &intf->cur_altsetting->desc;
if (desc->bAlternateSetting == 0) { if (desc->bAlternateSetting == 0) {
usb_disable_interface(udev, intf, true);
usb_enable_interface(udev, intf, true); usb_enable_interface(udev, intf, true);
ret = 0; ret = 0;
} else { } else {