Skip to content

Commit

Permalink
USB: core: Fix oversight in SuperSpeed initialization
Browse files Browse the repository at this point in the history
commit 59cf445 upstream.

Commit 85d07c5 ("USB: core: Unite old scheme and new scheme
descriptor reads") altered the way USB devices are enumerated
following detection, and in the process it messed up the
initialization of SuperSpeed (or faster) devices:

[   31.650759] usb 2-1: new SuperSpeed Plus Gen 2x1 USB device number 2 using xhci_hcd
[   31.663107] usb 2-1: device descriptor read/8, error -71
[   31.952697] usb 2-1: new SuperSpeed Plus Gen 2x1 USB device number 3 using xhci_hcd
[   31.965122] usb 2-1: device descriptor read/8, error -71
[   32.080991] usb usb2-port1: attempt power cycle
...

The problem was caused by the commit forgetting that in SuperSpeed or
faster devices, the device descriptor uses a logarithmic encoding of
the bMaxPacketSize0 value.  (For some reason I thought the 255 case in
the switch statement was meant for these devices, but it isn't -- it
was meant for Wireless USB and is no longer needed.)

We can fix the oversight by testing for buf->bMaxPacketSize0 = 9
(meaning 512, the actual maxpacket size for ep0 on all SuperSpeed
devices) and straightening out the logic that checks and adjusts our
initial guesses of the maxpacket value.

Reported-and-tested-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Closes: https://lore.kernel.org/linux-usb/20230810002257.nadxmfmrobkaxgnz@synopsys.com/
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Fixes: 85d07c5 ("USB: core: Unite old scheme and new scheme descriptor reads")
Link: https://lore.kernel.org/r/8809e6c5-59d5-4d2d-ac8f-6d106658ad73@rowland.harvard.edu
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
AlanStern authored and gregkh committed Sep 13, 2023
1 parent 8186596 commit f705617
Showing 1 changed file with 24 additions and 12 deletions.
36 changes: 24 additions & 12 deletions drivers/usb/core/hub.c
Expand Up @@ -4700,7 +4700,7 @@ static int get_bMaxPacketSize0(struct usb_device *udev,
buf, size,
initial_descriptor_timeout);
switch (buf->bMaxPacketSize0) {
case 8: case 16: case 32: case 64: case 255:
case 8: case 16: case 32: case 64: case 9:
if (buf->bDescriptorType == USB_DT_DEVICE) {
rc = buf->bMaxPacketSize0;
break;
Expand Down Expand Up @@ -4994,23 +4994,35 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
if (retval)
goto fail;

if (maxp0 == 0xff || udev->speed >= USB_SPEED_SUPER)
i = 512;
else
i = maxp0;
if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
if (udev->speed == USB_SPEED_LOW ||
!(i == 8 || i == 16 || i == 32 || i == 64)) {
dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
retval = -EMSGSIZE;
goto fail;
}
/*
* Check the ep0 maxpacket guess and correct it if necessary.
* maxp0 is the value stored in the device descriptor;
* i is the value it encodes (logarithmic for SuperSpeed or greater).
*/
i = maxp0;
if (udev->speed >= USB_SPEED_SUPER) {
if (maxp0 <= 16)
i = 1 << maxp0;
else
i = 0; /* Invalid */
}
if (usb_endpoint_maxp(&udev->ep0.desc) == i) {
; /* Initial ep0 maxpacket guess is right */
} else if ((udev->speed == USB_SPEED_FULL ||
udev->speed == USB_SPEED_HIGH) &&
(i == 8 || i == 16 || i == 32 || i == 64)) {
/* Initial guess is wrong; use the descriptor's value */
if (udev->speed == USB_SPEED_FULL)
dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
else
dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i);
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
} else {
/* Initial guess is wrong and descriptor's value is invalid */
dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", maxp0);
retval = -EMSGSIZE;
goto fail;
}

descr = usb_get_device_descriptor(udev);
Expand Down

0 comments on commit f705617

Please sign in to comment.