mirror of
https://github.com/home-assistant/operating-system
synced 2024-11-14 01:54:54 +01:00
Revert USB core changes to workaround Z-Wave stick issues (#3224)
Revert changes in the USB driver causing Z-Wave sticks (Z-Wave.me a and Aeotec at least) failing to enumerate. Issue is reported upstream but reverting the patches is a feasible workaround for the time being. Refs #2995
This commit is contained in:
parent
6ac4f3435b
commit
a6f54f6c4d
@ -0,0 +1,677 @@
|
||||
From 74c48f9493b3e95ec1f76b92b88061194c1874fc Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20=C4=8Cerm=C3=A1k?= <sairon@sairon.cz>
|
||||
Date: Mon, 4 Mar 2024 13:52:34 +0100
|
||||
Subject: [PATCH] Revert USB core changes causing issues with Z-Wave.me UZB
|
||||
stick
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Workaround for issues described on GH [1] and reported in [2].
|
||||
|
||||
* Revert "USB: core: Fix oversight in SuperSpeed initialization"
|
||||
This reverts commit f705617bab4766567874715eeed1b39dacb58671.
|
||||
|
||||
* Revert "USB: core: Fix race by not overwriting udev->descriptor in hub_port_init()"
|
||||
This reverts commit 8186596a663506b1124bede9fde6f243ef9f37ee.
|
||||
|
||||
* Revert "USB: core: Change usb_get_device_descriptor() API"
|
||||
This reverts commit d309fa69c2e3c5e6134ac9386f833f683e66ad1a.
|
||||
|
||||
* Revert "USB: core: Unite old scheme and new scheme descriptor reads"
|
||||
This reverts commit 90b01f8df56844cb4ac8f188eed92a5ee866020a.
|
||||
|
||||
[1] https://github.com/home-assistant/operating-system/issues/2995
|
||||
[2] https://lore.kernel.org/linux-usb/1e954652-dfb3-4248-beea-b8a449128ff0@sairon.cz/
|
||||
|
||||
Signed-off-by: Jan Čermák <sairon@sairon.cz>
|
||||
---
|
||||
drivers/usb/core/hcd.c | 10 +-
|
||||
drivers/usb/core/hub.c | 349 ++++++++++++++++---------------------
|
||||
drivers/usb/core/message.c | 29 +--
|
||||
drivers/usb/core/usb.h | 4 +-
|
||||
4 files changed, 169 insertions(+), 223 deletions(-)
|
||||
|
||||
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
|
||||
index be96045e4d6e3..4e240151c06af 100644
|
||||
--- a/drivers/usb/core/hcd.c
|
||||
+++ b/drivers/usb/core/hcd.c
|
||||
@@ -983,7 +983,6 @@ static int register_root_hub(struct usb_hcd *hcd)
|
||||
{
|
||||
struct device *parent_dev = hcd->self.controller;
|
||||
struct usb_device *usb_dev = hcd->self.root_hub;
|
||||
- struct usb_device_descriptor *descr;
|
||||
const int devnum = 1;
|
||||
int retval;
|
||||
|
||||
@@ -995,16 +994,13 @@ static int register_root_hub(struct usb_hcd *hcd)
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
|
||||
usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
- descr = usb_get_device_descriptor(usb_dev);
|
||||
- if (IS_ERR(descr)) {
|
||||
- retval = PTR_ERR(descr);
|
||||
+ retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
|
||||
+ if (retval != sizeof usb_dev->descriptor) {
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
|
||||
dev_name(&usb_dev->dev), retval);
|
||||
- return retval;
|
||||
+ return (retval < 0) ? retval : -EMSGSIZE;
|
||||
}
|
||||
- usb_dev->descriptor = *descr;
|
||||
- kfree(descr);
|
||||
|
||||
if (le16_to_cpu(usb_dev->descriptor.bcdUSB) >= 0x0201) {
|
||||
retval = usb_get_bos_descriptor(usb_dev);
|
||||
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
|
||||
index 574b8104a8199..708b63bcd38af 100644
|
||||
--- a/drivers/usb/core/hub.c
|
||||
+++ b/drivers/usb/core/hub.c
|
||||
@@ -2664,17 +2664,12 @@ int usb_authorize_device(struct usb_device *usb_dev)
|
||||
}
|
||||
|
||||
if (usb_dev->wusb) {
|
||||
- struct usb_device_descriptor *descr;
|
||||
-
|
||||
- descr = usb_get_device_descriptor(usb_dev);
|
||||
- if (IS_ERR(descr)) {
|
||||
- result = PTR_ERR(descr);
|
||||
+ result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor));
|
||||
+ if (result < 0) {
|
||||
dev_err(&usb_dev->dev, "can't re-read device descriptor for "
|
||||
"authorization: %d\n", result);
|
||||
goto error_device_descriptor;
|
||||
}
|
||||
- usb_dev->descriptor = *descr;
|
||||
- kfree(descr);
|
||||
}
|
||||
|
||||
usb_dev->authorized = 1;
|
||||
@@ -4685,67 +4680,6 @@ static int hub_enable_device(struct usb_device *udev)
|
||||
return hcd->driver->enable_device(hcd, udev);
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Get the bMaxPacketSize0 value during initialization by reading the
|
||||
- * device's device descriptor. Since we don't already know this value,
|
||||
- * the transfer is unsafe and it ignores I/O errors, only testing for
|
||||
- * reasonable received values.
|
||||
- *
|
||||
- * For "old scheme" initialization, size will be 8 so we read just the
|
||||
- * start of the device descriptor, which should work okay regardless of
|
||||
- * the actual bMaxPacketSize0 value. For "new scheme" initialization,
|
||||
- * size will be 64 (and buf will point to a sufficiently large buffer),
|
||||
- * which might not be kosher according to the USB spec but it's what
|
||||
- * Windows does and what many devices expect.
|
||||
- *
|
||||
- * Returns: bMaxPacketSize0 or a negative error code.
|
||||
- */
|
||||
-static int get_bMaxPacketSize0(struct usb_device *udev,
|
||||
- struct usb_device_descriptor *buf, int size, bool first_time)
|
||||
-{
|
||||
- int i, rc;
|
||||
-
|
||||
- /*
|
||||
- * Retry on all errors; some devices are flakey.
|
||||
- * 255 is for WUSB devices, we actually need to use
|
||||
- * 512 (WUSB1.0[4.8.1]).
|
||||
- */
|
||||
- for (i = 0; i < GET_MAXPACKET0_TRIES; ++i) {
|
||||
- /* Start with invalid values in case the transfer fails */
|
||||
- buf->bDescriptorType = buf->bMaxPacketSize0 = 0;
|
||||
- rc = usb_control_msg(udev, usb_rcvaddr0pipe(),
|
||||
- USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
|
||||
- USB_DT_DEVICE << 8, 0,
|
||||
- buf, size,
|
||||
- initial_descriptor_timeout);
|
||||
- switch (buf->bMaxPacketSize0) {
|
||||
- case 8: case 16: case 32: case 64: case 9:
|
||||
- if (buf->bDescriptorType == USB_DT_DEVICE) {
|
||||
- rc = buf->bMaxPacketSize0;
|
||||
- break;
|
||||
- }
|
||||
- fallthrough;
|
||||
- default:
|
||||
- if (rc >= 0)
|
||||
- rc = -EPROTO;
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- /*
|
||||
- * Some devices time out if they are powered on
|
||||
- * when already connected. They need a second
|
||||
- * reset, so return early. But only on the first
|
||||
- * attempt, lest we get into a time-out/reset loop.
|
||||
- */
|
||||
- if (rc > 0 || (rc == -ETIMEDOUT && first_time &&
|
||||
- udev->speed > USB_SPEED_FULL))
|
||||
- break;
|
||||
- }
|
||||
- return rc;
|
||||
-}
|
||||
-
|
||||
-#define GET_DESCRIPTOR_BUFSIZE 64
|
||||
-
|
||||
/* Reset device, (re)assign address, get device descriptor.
|
||||
* Device connection must be stable, no more debouncing needed.
|
||||
* Returns device in USB_STATE_ADDRESS, except on error.
|
||||
@@ -4755,17 +4689,10 @@ static int get_bMaxPacketSize0(struct usb_device *udev,
|
||||
* the port lock. For a newly detected device that is not accessible
|
||||
* through any global pointers, it's not necessary to lock the device,
|
||||
* but it is still necessary to lock the port.
|
||||
- *
|
||||
- * For a newly detected device, @dev_descr must be NULL. The device
|
||||
- * descriptor retrieved from the device will then be stored in
|
||||
- * @udev->descriptor. For an already existing device, @dev_descr
|
||||
- * must be non-NULL. The device descriptor will be stored there,
|
||||
- * not in @udev->descriptor, because descriptors for registered
|
||||
- * devices are meant to be immutable.
|
||||
*/
|
||||
static int
|
||||
hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
- int retry_counter, struct usb_device_descriptor *dev_descr)
|
||||
+ int retry_counter)
|
||||
{
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
|
||||
@@ -4777,13 +4704,6 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
int devnum = udev->devnum;
|
||||
const char *driver_name;
|
||||
bool do_new_scheme;
|
||||
- const bool initial = !dev_descr;
|
||||
- int maxp0;
|
||||
- struct usb_device_descriptor *buf, *descr;
|
||||
-
|
||||
- buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
|
||||
- if (!buf)
|
||||
- return -ENOMEM;
|
||||
|
||||
/* root hub ports have a slightly longer reset period
|
||||
* (from USB 2.0 spec, section 7.1.7.5)
|
||||
@@ -4816,34 +4736,32 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
}
|
||||
oldspeed = udev->speed;
|
||||
|
||||
- if (initial) {
|
||||
- /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
|
||||
- * it's fixed size except for full speed devices.
|
||||
- * For Wireless USB devices, ep0 max packet is always 512 (tho
|
||||
- * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
|
||||
+ /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
|
||||
+ * it's fixed size except for full speed devices.
|
||||
+ * For Wireless USB devices, ep0 max packet is always 512 (tho
|
||||
+ * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
|
||||
+ */
|
||||
+ switch (udev->speed) {
|
||||
+ case USB_SPEED_SUPER_PLUS:
|
||||
+ case USB_SPEED_SUPER:
|
||||
+ case USB_SPEED_WIRELESS: /* fixed at 512 */
|
||||
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
+ break;
|
||||
+ case USB_SPEED_HIGH: /* fixed at 64 */
|
||||
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
+ break;
|
||||
+ case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
|
||||
+ /* to determine the ep0 maxpacket size, try to read
|
||||
+ * the device descriptor to get bMaxPacketSize0 and
|
||||
+ * then correct our initial guess.
|
||||
*/
|
||||
- switch (udev->speed) {
|
||||
- case USB_SPEED_SUPER_PLUS:
|
||||
- case USB_SPEED_SUPER:
|
||||
- case USB_SPEED_WIRELESS: /* fixed at 512 */
|
||||
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
- break;
|
||||
- case USB_SPEED_HIGH: /* fixed at 64 */
|
||||
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
- break;
|
||||
- case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
|
||||
- /* to determine the ep0 maxpacket size, try to read
|
||||
- * the device descriptor to get bMaxPacketSize0 and
|
||||
- * then correct our initial guess.
|
||||
- */
|
||||
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
- break;
|
||||
- case USB_SPEED_LOW: /* fixed at 8 */
|
||||
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
|
||||
- break;
|
||||
- default:
|
||||
- goto fail;
|
||||
- }
|
||||
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
+ break;
|
||||
+ case USB_SPEED_LOW: /* fixed at 8 */
|
||||
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
|
||||
+ break;
|
||||
+ default:
|
||||
+ goto fail;
|
||||
}
|
||||
|
||||
if (udev->speed == USB_SPEED_WIRELESS)
|
||||
@@ -4866,24 +4784,22 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
if (udev->speed < USB_SPEED_SUPER)
|
||||
dev_info(&udev->dev,
|
||||
"%s %s USB device number %d using %s\n",
|
||||
- (initial ? "new" : "reset"), speed,
|
||||
+ (udev->config) ? "reset" : "new", speed,
|
||||
devnum, driver_name);
|
||||
|
||||
- if (initial) {
|
||||
- /* Set up TT records, if needed */
|
||||
- if (hdev->tt) {
|
||||
- udev->tt = hdev->tt;
|
||||
- udev->ttport = hdev->ttport;
|
||||
- } else if (udev->speed != USB_SPEED_HIGH
|
||||
- && hdev->speed == USB_SPEED_HIGH) {
|
||||
- if (!hub->tt.hub) {
|
||||
- dev_err(&udev->dev, "parent hub has no TT\n");
|
||||
- retval = -EINVAL;
|
||||
- goto fail;
|
||||
- }
|
||||
- udev->tt = &hub->tt;
|
||||
- udev->ttport = port1;
|
||||
+ /* Set up TT records, if needed */
|
||||
+ if (hdev->tt) {
|
||||
+ udev->tt = hdev->tt;
|
||||
+ udev->ttport = hdev->ttport;
|
||||
+ } else if (udev->speed != USB_SPEED_HIGH
|
||||
+ && hdev->speed == USB_SPEED_HIGH) {
|
||||
+ if (!hub->tt.hub) {
|
||||
+ dev_err(&udev->dev, "parent hub has no TT\n");
|
||||
+ retval = -EINVAL;
|
||||
+ goto fail;
|
||||
}
|
||||
+ udev->tt = &hub->tt;
|
||||
+ udev->ttport = port1;
|
||||
}
|
||||
|
||||
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
|
||||
@@ -4902,6 +4818,9 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
|
||||
for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
|
||||
if (do_new_scheme) {
|
||||
+ struct usb_device_descriptor *buf;
|
||||
+ int r = 0;
|
||||
+
|
||||
retval = hub_enable_device(udev);
|
||||
if (retval < 0) {
|
||||
dev_err(&udev->dev,
|
||||
@@ -4910,14 +4829,52 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
- maxp0 = get_bMaxPacketSize0(udev, buf,
|
||||
- GET_DESCRIPTOR_BUFSIZE, retries == 0);
|
||||
- if (maxp0 > 0 && !initial &&
|
||||
- maxp0 != udev->descriptor.bMaxPacketSize0) {
|
||||
- dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n");
|
||||
- retval = -ENODEV;
|
||||
- goto fail;
|
||||
+#define GET_DESCRIPTOR_BUFSIZE 64
|
||||
+ buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
|
||||
+ if (!buf) {
|
||||
+ retval = -ENOMEM;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ /* Retry on all errors; some devices are flakey.
|
||||
+ * 255 is for WUSB devices, we actually need to use
|
||||
+ * 512 (WUSB1.0[4.8.1]).
|
||||
+ */
|
||||
+ for (operations = 0; operations < GET_MAXPACKET0_TRIES;
|
||||
+ ++operations) {
|
||||
+ buf->bMaxPacketSize0 = 0;
|
||||
+ r = usb_control_msg(udev, usb_rcvaddr0pipe(),
|
||||
+ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
|
||||
+ USB_DT_DEVICE << 8, 0,
|
||||
+ buf, GET_DESCRIPTOR_BUFSIZE,
|
||||
+ initial_descriptor_timeout);
|
||||
+ switch (buf->bMaxPacketSize0) {
|
||||
+ case 8: case 16: case 32: case 64: case 255:
|
||||
+ if (buf->bDescriptorType ==
|
||||
+ USB_DT_DEVICE) {
|
||||
+ r = 0;
|
||||
+ break;
|
||||
+ }
|
||||
+ fallthrough;
|
||||
+ default:
|
||||
+ if (r == 0)
|
||||
+ r = -EPROTO;
|
||||
+ break;
|
||||
+ }
|
||||
+ /*
|
||||
+ * Some devices time out if they are powered on
|
||||
+ * when already connected. They need a second
|
||||
+ * reset. But only on the first attempt,
|
||||
+ * lest we get into a time out/reset loop
|
||||
+ */
|
||||
+ if (r == 0 || (r == -ETIMEDOUT &&
|
||||
+ retries == 0 &&
|
||||
+ udev->speed > USB_SPEED_FULL))
|
||||
+ break;
|
||||
}
|
||||
+ udev->descriptor.bMaxPacketSize0 =
|
||||
+ buf->bMaxPacketSize0;
|
||||
+ kfree(buf);
|
||||
|
||||
retval = hub_port_reset(hub, port1, udev, delay, false);
|
||||
if (retval < 0) /* error or disconnect */
|
||||
@@ -4928,13 +4885,14 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
retval = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
- if (maxp0 < 0) {
|
||||
- if (maxp0 != -ENODEV)
|
||||
+ if (r) {
|
||||
+ if (r != -ENODEV)
|
||||
dev_err(&udev->dev, "device descriptor read/64, error %d\n",
|
||||
- maxp0);
|
||||
- retval = maxp0;
|
||||
+ r);
|
||||
+ retval = -EMSGSIZE;
|
||||
continue;
|
||||
}
|
||||
+#undef GET_DESCRIPTOR_BUFSIZE
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4980,22 +4938,18 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
break;
|
||||
}
|
||||
|
||||
- /* !do_new_scheme || wusb */
|
||||
- maxp0 = get_bMaxPacketSize0(udev, buf, 8, retries == 0);
|
||||
- if (maxp0 < 0) {
|
||||
- retval = maxp0;
|
||||
+ retval = usb_get_device_descriptor(udev, 8);
|
||||
+ if (retval < 8) {
|
||||
if (retval != -ENODEV)
|
||||
dev_err(&udev->dev,
|
||||
"device descriptor read/8, error %d\n",
|
||||
retval);
|
||||
+ if (retval >= 0)
|
||||
+ retval = -EMSGSIZE;
|
||||
} else {
|
||||
u32 delay;
|
||||
|
||||
- if (!initial && maxp0 != udev->descriptor.bMaxPacketSize0) {
|
||||
- dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n");
|
||||
- retval = -ENODEV;
|
||||
- goto fail;
|
||||
- }
|
||||
+ retval = 0;
|
||||
|
||||
delay = udev->parent->hub_delay;
|
||||
udev->hub_delay = min_t(u32, delay,
|
||||
@@ -5014,61 +4968,48 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
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).
|
||||
+ * Some superspeed devices have finished the link training process
|
||||
+ * and attached to a superspeed hub port, but the device descriptor
|
||||
+ * got from those devices show they aren't superspeed devices. Warm
|
||||
+ * reset the port attached by the devices can fix them.
|
||||
*/
|
||||
- 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_SUPER) &&
|
||||
+ (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
|
||||
+ dev_err(&udev->dev, "got a wrong device descriptor, "
|
||||
+ "warm reset device\n");
|
||||
+ hub_port_reset(hub, port1, udev,
|
||||
+ HUB_BH_RESET_TIME, true);
|
||||
+ retval = -EINVAL;
|
||||
+ goto fail;
|
||||
+ }
|
||||
+
|
||||
+ if (udev->descriptor.bMaxPacketSize0 == 0xff ||
|
||||
+ udev->speed >= USB_SPEED_SUPER)
|
||||
+ i = 512;
|
||||
+ else
|
||||
+ i = udev->descriptor.bMaxPacketSize0;
|
||||
+ 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;
|
||||
+ }
|
||||
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);
|
||||
- if (IS_ERR(descr)) {
|
||||
- retval = PTR_ERR(descr);
|
||||
+ retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
|
||||
+ if (retval < (signed)sizeof(udev->descriptor)) {
|
||||
if (retval != -ENODEV)
|
||||
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
|
||||
retval);
|
||||
- goto fail;
|
||||
- }
|
||||
- if (initial)
|
||||
- udev->descriptor = *descr;
|
||||
- else
|
||||
- *dev_descr = *descr;
|
||||
- kfree(descr);
|
||||
-
|
||||
- /*
|
||||
- * Some superspeed devices have finished the link training process
|
||||
- * and attached to a superspeed hub port, but the device descriptor
|
||||
- * got from those devices show they aren't superspeed devices. Warm
|
||||
- * reset the port attached by the devices can fix them.
|
||||
- */
|
||||
- if ((udev->speed >= USB_SPEED_SUPER) &&
|
||||
- (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
|
||||
- dev_err(&udev->dev, "got a wrong device descriptor, warm reset device\n");
|
||||
- hub_port_reset(hub, port1, udev, HUB_BH_RESET_TIME, true);
|
||||
- retval = -EINVAL;
|
||||
+ if (retval >= 0)
|
||||
+ retval = -ENOMSG;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -5094,7 +5035,6 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
hub_port_disable(hub, port1, 0);
|
||||
update_devnum(udev, devnum); /* for disconnect processing */
|
||||
}
|
||||
- kfree(buf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -5175,7 +5115,7 @@ hub_power_remaining(struct usb_hub *hub)
|
||||
|
||||
|
||||
static int descriptors_changed(struct usb_device *udev,
|
||||
- struct usb_device_descriptor *new_device_descriptor,
|
||||
+ struct usb_device_descriptor *old_device_descriptor,
|
||||
struct usb_host_bos *old_bos)
|
||||
{
|
||||
int changed = 0;
|
||||
@@ -5186,8 +5126,8 @@ static int descriptors_changed(struct usb_device *udev,
|
||||
int length;
|
||||
char *buf;
|
||||
|
||||
- if (memcmp(&udev->descriptor, new_device_descriptor,
|
||||
- sizeof(*new_device_descriptor)) != 0)
|
||||
+ if (memcmp(&udev->descriptor, old_device_descriptor,
|
||||
+ sizeof(*old_device_descriptor)) != 0)
|
||||
return 1;
|
||||
|
||||
if ((old_bos && !udev->bos) || (!old_bos && udev->bos))
|
||||
@@ -5360,7 +5300,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
|
||||
}
|
||||
|
||||
/* reset (non-USB 3.0 devices) and get descriptor */
|
||||
- status = hub_port_init(hub, udev, port1, i, NULL);
|
||||
+ status = hub_port_init(hub, udev, port1, i);
|
||||
if (status < 0)
|
||||
goto loop;
|
||||
|
||||
@@ -5507,8 +5447,9 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||
{
|
||||
struct usb_port *port_dev = hub->ports[port1 - 1];
|
||||
struct usb_device *udev = port_dev->child;
|
||||
- struct usb_device_descriptor *descr;
|
||||
+ struct usb_device_descriptor descriptor;
|
||||
int status = -ENODEV;
|
||||
+ int retval;
|
||||
|
||||
dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus,
|
||||
portchange, portspeed(hub, portstatus));
|
||||
@@ -5535,20 +5476,23 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||
* changed device descriptors before resuscitating the
|
||||
* device.
|
||||
*/
|
||||
- descr = usb_get_device_descriptor(udev);
|
||||
- if (IS_ERR(descr)) {
|
||||
+ descriptor = udev->descriptor;
|
||||
+ retval = usb_get_device_descriptor(udev,
|
||||
+ sizeof(udev->descriptor));
|
||||
+ if (retval < 0) {
|
||||
dev_dbg(&udev->dev,
|
||||
- "can't read device descriptor %ld\n",
|
||||
- PTR_ERR(descr));
|
||||
+ "can't read device descriptor %d\n",
|
||||
+ retval);
|
||||
} else {
|
||||
- if (descriptors_changed(udev, descr,
|
||||
+ if (descriptors_changed(udev, &descriptor,
|
||||
udev->bos)) {
|
||||
dev_dbg(&udev->dev,
|
||||
"device descriptor has changed\n");
|
||||
+ /* for disconnect() calls */
|
||||
+ udev->descriptor = descriptor;
|
||||
} else {
|
||||
status = 0; /* Nothing to do */
|
||||
}
|
||||
- kfree(descr);
|
||||
}
|
||||
#ifdef CONFIG_PM
|
||||
} else if (udev->state == USB_STATE_SUSPENDED &&
|
||||
@@ -5986,7 +5930,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
||||
struct usb_device *parent_hdev = udev->parent;
|
||||
struct usb_hub *parent_hub;
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
- struct usb_device_descriptor descriptor;
|
||||
+ struct usb_device_descriptor descriptor = udev->descriptor;
|
||||
struct usb_host_bos *bos;
|
||||
int i, j, ret = 0;
|
||||
int port1 = udev->portnum;
|
||||
@@ -6018,7 +5962,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
||||
/* ep0 maxpacket size may change; let the HCD know about it.
|
||||
* Other endpoints will be handled by re-enumeration. */
|
||||
usb_ep0_reinit(udev);
|
||||
- ret = hub_port_init(parent_hub, udev, port1, i, &descriptor);
|
||||
+ ret = hub_port_init(parent_hub, udev, port1, i);
|
||||
if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
|
||||
break;
|
||||
}
|
||||
@@ -6030,6 +5974,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
||||
/* Device might have changed firmware (DFU or similar) */
|
||||
if (descriptors_changed(udev, &descriptor, bos)) {
|
||||
dev_info(&udev->dev, "device firmware changed\n");
|
||||
+ udev->descriptor = descriptor; /* for disconnect() calls */
|
||||
goto re_enumerate;
|
||||
}
|
||||
|
||||
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
|
||||
index 678c43234d65f..e8045997d6ec2 100644
|
||||
--- a/drivers/usb/core/message.c
|
||||
+++ b/drivers/usb/core/message.c
|
||||
@@ -1039,35 +1039,40 @@ char *usb_cache_string(struct usb_device *udev, int index)
|
||||
}
|
||||
|
||||
/*
|
||||
- * usb_get_device_descriptor - read the device descriptor
|
||||
- * @udev: the device whose device descriptor should be read
|
||||
+ * usb_get_device_descriptor - (re)reads the device descriptor (usbcore)
|
||||
+ * @dev: the device whose device descriptor is being updated
|
||||
+ * @size: how much of the descriptor to read
|
||||
*
|
||||
* Context: task context, might sleep.
|
||||
*
|
||||
+ * Updates the copy of the device descriptor stored in the device structure,
|
||||
+ * which dedicates space for this purpose.
|
||||
+ *
|
||||
* Not exported, only for use by the core. If drivers really want to read
|
||||
* the device descriptor directly, they can call usb_get_descriptor() with
|
||||
* type = USB_DT_DEVICE and index = 0.
|
||||
*
|
||||
- * Returns: a pointer to a dynamically allocated usb_device_descriptor
|
||||
- * structure (which the caller must deallocate), or an ERR_PTR value.
|
||||
+ * This call is synchronous, and may not be used in an interrupt context.
|
||||
+ *
|
||||
+ * Return: The number of bytes received on success, or else the status code
|
||||
+ * returned by the underlying usb_control_msg() call.
|
||||
*/
|
||||
-struct usb_device_descriptor *usb_get_device_descriptor(struct usb_device *udev)
|
||||
+int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
|
||||
{
|
||||
struct usb_device_descriptor *desc;
|
||||
int ret;
|
||||
|
||||
+ if (size > sizeof(*desc))
|
||||
+ return -EINVAL;
|
||||
desc = kmalloc(sizeof(*desc), GFP_NOIO);
|
||||
if (!desc)
|
||||
- return ERR_PTR(-ENOMEM);
|
||||
-
|
||||
- ret = usb_get_descriptor(udev, USB_DT_DEVICE, 0, desc, sizeof(*desc));
|
||||
- if (ret == sizeof(*desc))
|
||||
- return desc;
|
||||
+ return -ENOMEM;
|
||||
|
||||
+ ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);
|
||||
if (ret >= 0)
|
||||
- ret = -EMSGSIZE;
|
||||
+ memcpy(&dev->descriptor, desc, size);
|
||||
kfree(desc);
|
||||
- return ERR_PTR(ret);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
|
||||
index 3bb2e1db42b5d..82538daac8b89 100644
|
||||
--- a/drivers/usb/core/usb.h
|
||||
+++ b/drivers/usb/core/usb.h
|
||||
@@ -42,8 +42,8 @@ extern bool usb_endpoint_is_ignored(struct usb_device *udev,
|
||||
struct usb_endpoint_descriptor *epd);
|
||||
extern int usb_remove_device(struct usb_device *udev);
|
||||
|
||||
-extern struct usb_device_descriptor *usb_get_device_descriptor(
|
||||
- struct usb_device *udev);
|
||||
+extern int usb_get_device_descriptor(struct usb_device *dev,
|
||||
+ unsigned int size);
|
||||
extern int usb_set_isoch_delay(struct usb_device *dev);
|
||||
extern int usb_get_bos_descriptor(struct usb_device *dev);
|
||||
extern void usb_release_bos_descriptor(struct usb_device *dev);
|
||||
--
|
||||
2.37.3
|
||||
|
@ -0,0 +1,652 @@
|
||||
From 85cfbef1cf29aba24abe21409b180b7bea76e562 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jan=20=C4=8Cerm=C3=A1k?= <sairon@sairon.cz>
|
||||
Date: Wed, 6 Mar 2024 13:25:41 +0100
|
||||
Subject: [PATCH] Revert USB core changes causing issues with Z-Wave.me UZB
|
||||
stick
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Workaround for issues described on GH [1] and reported in [2].
|
||||
|
||||
* Revert "USB: core: Fix oversight in SuperSpeed initialization"
|
||||
This reverts commit 59cf445754566984fd55af19ba7146c76e6627bc.
|
||||
|
||||
* Revert "USB: core: Fix race by not overwriting udev->descriptor in hub_port_init()"
|
||||
This reverts commit ff33299ec8bb80cdcc073ad9c506bd79bb2ed20b.
|
||||
|
||||
* Revert "USB: core: Change usb_get_device_descriptor() API"
|
||||
This reverts commit de28e469da75359a2bb8cd8778b78aa64b1be1f4.
|
||||
|
||||
* Revert "USB: core: Unite old scheme and new scheme descriptor reads"
|
||||
This reverts commit 85d07c55621676d47d873d2749b88f783cd4d5a1.
|
||||
|
||||
[1] https://github.com/home-assistant/operating-system/issues/2995
|
||||
[2] https://lore.kernel.org/linux-usb/1e954652-dfb3-4248-beea-b8a449128ff0@sairon.cz/
|
||||
|
||||
Signed-off-by: Jan Čermák <sairon@sairon.cz>
|
||||
---
|
||||
drivers/usb/core/hcd.c | 10 +-
|
||||
drivers/usb/core/hub.c | 335 ++++++++++++++++---------------------
|
||||
drivers/usb/core/message.c | 29 ++--
|
||||
drivers/usb/core/usb.h | 4 +-
|
||||
4 files changed, 166 insertions(+), 212 deletions(-)
|
||||
|
||||
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
|
||||
index 12b6dfeaf658c..d895f6ebbc400 100644
|
||||
--- a/drivers/usb/core/hcd.c
|
||||
+++ b/drivers/usb/core/hcd.c
|
||||
@@ -958,7 +958,6 @@ static int register_root_hub(struct usb_hcd *hcd)
|
||||
{
|
||||
struct device *parent_dev = hcd->self.controller;
|
||||
struct usb_device *usb_dev = hcd->self.root_hub;
|
||||
- struct usb_device_descriptor *descr;
|
||||
const int devnum = 1;
|
||||
int retval;
|
||||
|
||||
@@ -970,16 +969,13 @@ static int register_root_hub(struct usb_hcd *hcd)
|
||||
mutex_lock(&usb_bus_idr_lock);
|
||||
|
||||
usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
- descr = usb_get_device_descriptor(usb_dev);
|
||||
- if (IS_ERR(descr)) {
|
||||
- retval = PTR_ERR(descr);
|
||||
+ retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
|
||||
+ if (retval != sizeof usb_dev->descriptor) {
|
||||
mutex_unlock(&usb_bus_idr_lock);
|
||||
dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
|
||||
dev_name(&usb_dev->dev), retval);
|
||||
- return retval;
|
||||
+ return (retval < 0) ? retval : -EMSGSIZE;
|
||||
}
|
||||
- usb_dev->descriptor = *descr;
|
||||
- kfree(descr);
|
||||
|
||||
if (le16_to_cpu(usb_dev->descriptor.bcdUSB) >= 0x0201) {
|
||||
retval = usb_get_bos_descriptor(usb_dev);
|
||||
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
|
||||
index 71635dfa741d2..b937bf8b6b3d3 100644
|
||||
--- a/drivers/usb/core/hub.c
|
||||
+++ b/drivers/usb/core/hub.c
|
||||
@@ -4722,67 +4722,6 @@ static int hub_enable_device(struct usb_device *udev)
|
||||
return hcd->driver->enable_device(hcd, udev);
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Get the bMaxPacketSize0 value during initialization by reading the
|
||||
- * device's device descriptor. Since we don't already know this value,
|
||||
- * the transfer is unsafe and it ignores I/O errors, only testing for
|
||||
- * reasonable received values.
|
||||
- *
|
||||
- * For "old scheme" initialization, size will be 8 so we read just the
|
||||
- * start of the device descriptor, which should work okay regardless of
|
||||
- * the actual bMaxPacketSize0 value. For "new scheme" initialization,
|
||||
- * size will be 64 (and buf will point to a sufficiently large buffer),
|
||||
- * which might not be kosher according to the USB spec but it's what
|
||||
- * Windows does and what many devices expect.
|
||||
- *
|
||||
- * Returns: bMaxPacketSize0 or a negative error code.
|
||||
- */
|
||||
-static int get_bMaxPacketSize0(struct usb_device *udev,
|
||||
- struct usb_device_descriptor *buf, int size, bool first_time)
|
||||
-{
|
||||
- int i, rc;
|
||||
-
|
||||
- /*
|
||||
- * Retry on all errors; some devices are flakey.
|
||||
- * 255 is for WUSB devices, we actually need to use
|
||||
- * 512 (WUSB1.0[4.8.1]).
|
||||
- */
|
||||
- for (i = 0; i < GET_MAXPACKET0_TRIES; ++i) {
|
||||
- /* Start with invalid values in case the transfer fails */
|
||||
- buf->bDescriptorType = buf->bMaxPacketSize0 = 0;
|
||||
- rc = usb_control_msg(udev, usb_rcvaddr0pipe(),
|
||||
- USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
|
||||
- USB_DT_DEVICE << 8, 0,
|
||||
- buf, size,
|
||||
- initial_descriptor_timeout);
|
||||
- switch (buf->bMaxPacketSize0) {
|
||||
- case 8: case 16: case 32: case 64: case 9:
|
||||
- if (buf->bDescriptorType == USB_DT_DEVICE) {
|
||||
- rc = buf->bMaxPacketSize0;
|
||||
- break;
|
||||
- }
|
||||
- fallthrough;
|
||||
- default:
|
||||
- if (rc >= 0)
|
||||
- rc = -EPROTO;
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- /*
|
||||
- * Some devices time out if they are powered on
|
||||
- * when already connected. They need a second
|
||||
- * reset, so return early. But only on the first
|
||||
- * attempt, lest we get into a time-out/reset loop.
|
||||
- */
|
||||
- if (rc > 0 || (rc == -ETIMEDOUT && first_time &&
|
||||
- udev->speed > USB_SPEED_FULL))
|
||||
- break;
|
||||
- }
|
||||
- return rc;
|
||||
-}
|
||||
-
|
||||
-#define GET_DESCRIPTOR_BUFSIZE 64
|
||||
-
|
||||
/* Reset device, (re)assign address, get device descriptor.
|
||||
* Device connection must be stable, no more debouncing needed.
|
||||
* Returns device in USB_STATE_ADDRESS, except on error.
|
||||
@@ -4792,17 +4731,10 @@ static int get_bMaxPacketSize0(struct usb_device *udev,
|
||||
* the port lock. For a newly detected device that is not accessible
|
||||
* through any global pointers, it's not necessary to lock the device,
|
||||
* but it is still necessary to lock the port.
|
||||
- *
|
||||
- * For a newly detected device, @dev_descr must be NULL. The device
|
||||
- * descriptor retrieved from the device will then be stored in
|
||||
- * @udev->descriptor. For an already existing device, @dev_descr
|
||||
- * must be non-NULL. The device descriptor will be stored there,
|
||||
- * not in @udev->descriptor, because descriptors for registered
|
||||
- * devices are meant to be immutable.
|
||||
*/
|
||||
static int
|
||||
hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
- int retry_counter, struct usb_device_descriptor *dev_descr)
|
||||
+ int retry_counter)
|
||||
{
|
||||
struct usb_device *hdev = hub->hdev;
|
||||
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
|
||||
@@ -4814,13 +4746,6 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
int devnum = udev->devnum;
|
||||
const char *driver_name;
|
||||
bool do_new_scheme;
|
||||
- const bool initial = !dev_descr;
|
||||
- int maxp0;
|
||||
- struct usb_device_descriptor *buf, *descr;
|
||||
-
|
||||
- buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
|
||||
- if (!buf)
|
||||
- return -ENOMEM;
|
||||
|
||||
/* root hub ports have a slightly longer reset period
|
||||
* (from USB 2.0 spec, section 7.1.7.5)
|
||||
@@ -4853,31 +4778,31 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
}
|
||||
oldspeed = udev->speed;
|
||||
|
||||
- if (initial) {
|
||||
- /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
|
||||
- * it's fixed size except for full speed devices.
|
||||
+ /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
|
||||
+ * it's fixed size except for full speed devices.
|
||||
+ * For Wireless USB devices, ep0 max packet is always 512 (tho
|
||||
+ * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
|
||||
+ */
|
||||
+ switch (udev->speed) {
|
||||
+ case USB_SPEED_SUPER_PLUS:
|
||||
+ case USB_SPEED_SUPER:
|
||||
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
+ break;
|
||||
+ case USB_SPEED_HIGH: /* fixed at 64 */
|
||||
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
+ break;
|
||||
+ case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
|
||||
+ /* to determine the ep0 maxpacket size, try to read
|
||||
+ * the device descriptor to get bMaxPacketSize0 and
|
||||
+ * then correct our initial guess.
|
||||
*/
|
||||
- switch (udev->speed) {
|
||||
- case USB_SPEED_SUPER_PLUS:
|
||||
- case USB_SPEED_SUPER:
|
||||
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
|
||||
- break;
|
||||
- case USB_SPEED_HIGH: /* fixed at 64 */
|
||||
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
- break;
|
||||
- case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
|
||||
- /* to determine the ep0 maxpacket size, try to read
|
||||
- * the device descriptor to get bMaxPacketSize0 and
|
||||
- * then correct our initial guess.
|
||||
- */
|
||||
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
- break;
|
||||
- case USB_SPEED_LOW: /* fixed at 8 */
|
||||
- udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
|
||||
- break;
|
||||
- default:
|
||||
- goto fail;
|
||||
- }
|
||||
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
|
||||
+ break;
|
||||
+ case USB_SPEED_LOW: /* fixed at 8 */
|
||||
+ udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
|
||||
+ break;
|
||||
+ default:
|
||||
+ goto fail;
|
||||
}
|
||||
|
||||
speed = usb_speed_string(udev->speed);
|
||||
@@ -4897,24 +4822,22 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
if (udev->speed < USB_SPEED_SUPER)
|
||||
dev_info(&udev->dev,
|
||||
"%s %s USB device number %d using %s\n",
|
||||
- (initial ? "new" : "reset"), speed,
|
||||
+ (udev->config) ? "reset" : "new", speed,
|
||||
devnum, driver_name);
|
||||
|
||||
- if (initial) {
|
||||
- /* Set up TT records, if needed */
|
||||
- if (hdev->tt) {
|
||||
- udev->tt = hdev->tt;
|
||||
- udev->ttport = hdev->ttport;
|
||||
- } else if (udev->speed != USB_SPEED_HIGH
|
||||
- && hdev->speed == USB_SPEED_HIGH) {
|
||||
- if (!hub->tt.hub) {
|
||||
- dev_err(&udev->dev, "parent hub has no TT\n");
|
||||
- retval = -EINVAL;
|
||||
- goto fail;
|
||||
- }
|
||||
- udev->tt = &hub->tt;
|
||||
- udev->ttport = port1;
|
||||
+ /* Set up TT records, if needed */
|
||||
+ if (hdev->tt) {
|
||||
+ udev->tt = hdev->tt;
|
||||
+ udev->ttport = hdev->ttport;
|
||||
+ } else if (udev->speed != USB_SPEED_HIGH
|
||||
+ && hdev->speed == USB_SPEED_HIGH) {
|
||||
+ if (!hub->tt.hub) {
|
||||
+ dev_err(&udev->dev, "parent hub has no TT\n");
|
||||
+ retval = -EINVAL;
|
||||
+ goto fail;
|
||||
}
|
||||
+ udev->tt = &hub->tt;
|
||||
+ udev->ttport = port1;
|
||||
}
|
||||
|
||||
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
|
||||
@@ -4938,6 +4861,9 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
}
|
||||
|
||||
if (do_new_scheme) {
|
||||
+ struct usb_device_descriptor *buf;
|
||||
+ int r = 0;
|
||||
+
|
||||
retval = hub_enable_device(udev);
|
||||
if (retval < 0) {
|
||||
dev_err(&udev->dev,
|
||||
@@ -4946,14 +4872,52 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
- maxp0 = get_bMaxPacketSize0(udev, buf,
|
||||
- GET_DESCRIPTOR_BUFSIZE, retries == 0);
|
||||
- if (maxp0 > 0 && !initial &&
|
||||
- maxp0 != udev->descriptor.bMaxPacketSize0) {
|
||||
- dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n");
|
||||
- retval = -ENODEV;
|
||||
- goto fail;
|
||||
+#define GET_DESCRIPTOR_BUFSIZE 64
|
||||
+ buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
|
||||
+ if (!buf) {
|
||||
+ retval = -ENOMEM;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ /* Retry on all errors; some devices are flakey.
|
||||
+ * 255 is for WUSB devices, we actually need to use
|
||||
+ * 512 (WUSB1.0[4.8.1]).
|
||||
+ */
|
||||
+ for (operations = 0; operations < GET_MAXPACKET0_TRIES;
|
||||
+ ++operations) {
|
||||
+ buf->bMaxPacketSize0 = 0;
|
||||
+ r = usb_control_msg(udev, usb_rcvaddr0pipe(),
|
||||
+ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
|
||||
+ USB_DT_DEVICE << 8, 0,
|
||||
+ buf, GET_DESCRIPTOR_BUFSIZE,
|
||||
+ initial_descriptor_timeout);
|
||||
+ switch (buf->bMaxPacketSize0) {
|
||||
+ case 8: case 16: case 32: case 64: case 255:
|
||||
+ if (buf->bDescriptorType ==
|
||||
+ USB_DT_DEVICE) {
|
||||
+ r = 0;
|
||||
+ break;
|
||||
+ }
|
||||
+ fallthrough;
|
||||
+ default:
|
||||
+ if (r == 0)
|
||||
+ r = -EPROTO;
|
||||
+ break;
|
||||
+ }
|
||||
+ /*
|
||||
+ * Some devices time out if they are powered on
|
||||
+ * when already connected. They need a second
|
||||
+ * reset. But only on the first attempt,
|
||||
+ * lest we get into a time out/reset loop
|
||||
+ */
|
||||
+ if (r == 0 || (r == -ETIMEDOUT &&
|
||||
+ retries == 0 &&
|
||||
+ udev->speed > USB_SPEED_FULL))
|
||||
+ break;
|
||||
}
|
||||
+ udev->descriptor.bMaxPacketSize0 =
|
||||
+ buf->bMaxPacketSize0;
|
||||
+ kfree(buf);
|
||||
|
||||
retval = hub_port_reset(hub, port1, udev, delay, false);
|
||||
if (retval < 0) /* error or disconnect */
|
||||
@@ -4964,13 +4928,14 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
retval = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
- if (maxp0 < 0) {
|
||||
- if (maxp0 != -ENODEV)
|
||||
+ if (r) {
|
||||
+ if (r != -ENODEV)
|
||||
dev_err(&udev->dev, "device descriptor read/64, error %d\n",
|
||||
- maxp0);
|
||||
- retval = maxp0;
|
||||
+ r);
|
||||
+ retval = -EMSGSIZE;
|
||||
continue;
|
||||
}
|
||||
+#undef GET_DESCRIPTOR_BUFSIZE
|
||||
}
|
||||
|
||||
for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
|
||||
@@ -5011,21 +4976,18 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
if (do_new_scheme)
|
||||
break;
|
||||
|
||||
- maxp0 = get_bMaxPacketSize0(udev, buf, 8, retries == 0);
|
||||
- if (maxp0 < 0) {
|
||||
- retval = maxp0;
|
||||
+ retval = usb_get_device_descriptor(udev, 8);
|
||||
+ if (retval < 8) {
|
||||
if (retval != -ENODEV)
|
||||
dev_err(&udev->dev,
|
||||
"device descriptor read/8, error %d\n",
|
||||
retval);
|
||||
+ if (retval >= 0)
|
||||
+ retval = -EMSGSIZE;
|
||||
} else {
|
||||
u32 delay;
|
||||
|
||||
- if (!initial && maxp0 != udev->descriptor.bMaxPacketSize0) {
|
||||
- dev_err(&udev->dev, "device reset changed ep0 maxpacket size!\n");
|
||||
- retval = -ENODEV;
|
||||
- goto fail;
|
||||
- }
|
||||
+ retval = 0;
|
||||
|
||||
delay = udev->parent->hub_delay;
|
||||
udev->hub_delay = min_t(u32, delay,
|
||||
@@ -5044,61 +5006,48 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
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).
|
||||
+ * Some superspeed devices have finished the link training process
|
||||
+ * and attached to a superspeed hub port, but the device descriptor
|
||||
+ * got from those devices show they aren't superspeed devices. Warm
|
||||
+ * reset the port attached by the devices can fix them.
|
||||
*/
|
||||
- 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_SUPER) &&
|
||||
+ (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
|
||||
+ dev_err(&udev->dev, "got a wrong device descriptor, "
|
||||
+ "warm reset device\n");
|
||||
+ hub_port_reset(hub, port1, udev,
|
||||
+ HUB_BH_RESET_TIME, true);
|
||||
+ retval = -EINVAL;
|
||||
+ goto fail;
|
||||
+ }
|
||||
+
|
||||
+ if (udev->descriptor.bMaxPacketSize0 == 0xff ||
|
||||
+ udev->speed >= USB_SPEED_SUPER)
|
||||
+ i = 512;
|
||||
+ else
|
||||
+ i = udev->descriptor.bMaxPacketSize0;
|
||||
+ 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;
|
||||
+ }
|
||||
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);
|
||||
- if (IS_ERR(descr)) {
|
||||
- retval = PTR_ERR(descr);
|
||||
+ retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
|
||||
+ if (retval < (signed)sizeof(udev->descriptor)) {
|
||||
if (retval != -ENODEV)
|
||||
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
|
||||
retval);
|
||||
- goto fail;
|
||||
- }
|
||||
- if (initial)
|
||||
- udev->descriptor = *descr;
|
||||
- else
|
||||
- *dev_descr = *descr;
|
||||
- kfree(descr);
|
||||
-
|
||||
- /*
|
||||
- * Some superspeed devices have finished the link training process
|
||||
- * and attached to a superspeed hub port, but the device descriptor
|
||||
- * got from those devices show they aren't superspeed devices. Warm
|
||||
- * reset the port attached by the devices can fix them.
|
||||
- */
|
||||
- if ((udev->speed >= USB_SPEED_SUPER) &&
|
||||
- (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
|
||||
- dev_err(&udev->dev, "got a wrong device descriptor, warm reset device\n");
|
||||
- hub_port_reset(hub, port1, udev, HUB_BH_RESET_TIME, true);
|
||||
- retval = -EINVAL;
|
||||
+ if (retval >= 0)
|
||||
+ retval = -ENOMSG;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -5124,7 +5073,6 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
hub_port_disable(hub, port1, 0);
|
||||
update_devnum(udev, devnum); /* for disconnect processing */
|
||||
}
|
||||
- kfree(buf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -5205,7 +5153,7 @@ hub_power_remaining(struct usb_hub *hub)
|
||||
|
||||
|
||||
static int descriptors_changed(struct usb_device *udev,
|
||||
- struct usb_device_descriptor *new_device_descriptor,
|
||||
+ struct usb_device_descriptor *old_device_descriptor,
|
||||
struct usb_host_bos *old_bos)
|
||||
{
|
||||
int changed = 0;
|
||||
@@ -5216,8 +5164,8 @@ static int descriptors_changed(struct usb_device *udev,
|
||||
int length;
|
||||
char *buf;
|
||||
|
||||
- if (memcmp(&udev->descriptor, new_device_descriptor,
|
||||
- sizeof(*new_device_descriptor)) != 0)
|
||||
+ if (memcmp(&udev->descriptor, old_device_descriptor,
|
||||
+ sizeof(*old_device_descriptor)) != 0)
|
||||
return 1;
|
||||
|
||||
if ((old_bos && !udev->bos) || (!old_bos && udev->bos))
|
||||
@@ -5394,7 +5342,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
|
||||
}
|
||||
|
||||
/* reset (non-USB 3.0 devices) and get descriptor */
|
||||
- status = hub_port_init(hub, udev, port1, i, NULL);
|
||||
+ status = hub_port_init(hub, udev, port1, i);
|
||||
if (status < 0)
|
||||
goto loop;
|
||||
|
||||
@@ -5541,8 +5489,9 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||
{
|
||||
struct usb_port *port_dev = hub->ports[port1 - 1];
|
||||
struct usb_device *udev = port_dev->child;
|
||||
- struct usb_device_descriptor *descr;
|
||||
+ struct usb_device_descriptor descriptor;
|
||||
int status = -ENODEV;
|
||||
+ int retval;
|
||||
|
||||
dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus,
|
||||
portchange, portspeed(hub, portstatus));
|
||||
@@ -5569,20 +5518,23 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||
* changed device descriptors before resuscitating the
|
||||
* device.
|
||||
*/
|
||||
- descr = usb_get_device_descriptor(udev);
|
||||
- if (IS_ERR(descr)) {
|
||||
+ descriptor = udev->descriptor;
|
||||
+ retval = usb_get_device_descriptor(udev,
|
||||
+ sizeof(udev->descriptor));
|
||||
+ if (retval < 0) {
|
||||
dev_dbg(&udev->dev,
|
||||
- "can't read device descriptor %ld\n",
|
||||
- PTR_ERR(descr));
|
||||
+ "can't read device descriptor %d\n",
|
||||
+ retval);
|
||||
} else {
|
||||
- if (descriptors_changed(udev, descr,
|
||||
+ if (descriptors_changed(udev, &descriptor,
|
||||
udev->bos)) {
|
||||
dev_dbg(&udev->dev,
|
||||
"device descriptor has changed\n");
|
||||
+ /* for disconnect() calls */
|
||||
+ udev->descriptor = descriptor;
|
||||
} else {
|
||||
status = 0; /* Nothing to do */
|
||||
}
|
||||
- kfree(descr);
|
||||
}
|
||||
#ifdef CONFIG_PM
|
||||
} else if (udev->state == USB_STATE_SUSPENDED &&
|
||||
@@ -6039,7 +5991,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
||||
struct usb_device *parent_hdev = udev->parent;
|
||||
struct usb_hub *parent_hub;
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
- struct usb_device_descriptor descriptor;
|
||||
+ struct usb_device_descriptor descriptor = udev->descriptor;
|
||||
struct usb_host_bos *bos;
|
||||
int i, j, ret = 0;
|
||||
int port1 = udev->portnum;
|
||||
@@ -6075,7 +6027,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
||||
/* ep0 maxpacket size may change; let the HCD know about it.
|
||||
* Other endpoints will be handled by re-enumeration. */
|
||||
usb_ep0_reinit(udev);
|
||||
- ret = hub_port_init(parent_hub, udev, port1, i, &descriptor);
|
||||
+ ret = hub_port_init(parent_hub, udev, port1, i);
|
||||
if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
|
||||
break;
|
||||
}
|
||||
@@ -6087,6 +6039,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
||||
/* Device might have changed firmware (DFU or similar) */
|
||||
if (descriptors_changed(udev, &descriptor, bos)) {
|
||||
dev_info(&udev->dev, "device firmware changed\n");
|
||||
+ udev->descriptor = descriptor; /* for disconnect() calls */
|
||||
goto re_enumerate;
|
||||
}
|
||||
|
||||
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
|
||||
index 077dfe48d01c1..0d2bfc909019b 100644
|
||||
--- a/drivers/usb/core/message.c
|
||||
+++ b/drivers/usb/core/message.c
|
||||
@@ -1041,35 +1041,40 @@ char *usb_cache_string(struct usb_device *udev, int index)
|
||||
EXPORT_SYMBOL_GPL(usb_cache_string);
|
||||
|
||||
/*
|
||||
- * usb_get_device_descriptor - read the device descriptor
|
||||
- * @udev: the device whose device descriptor should be read
|
||||
+ * usb_get_device_descriptor - (re)reads the device descriptor (usbcore)
|
||||
+ * @dev: the device whose device descriptor is being updated
|
||||
+ * @size: how much of the descriptor to read
|
||||
*
|
||||
* Context: task context, might sleep.
|
||||
*
|
||||
+ * Updates the copy of the device descriptor stored in the device structure,
|
||||
+ * which dedicates space for this purpose.
|
||||
+ *
|
||||
* Not exported, only for use by the core. If drivers really want to read
|
||||
* the device descriptor directly, they can call usb_get_descriptor() with
|
||||
* type = USB_DT_DEVICE and index = 0.
|
||||
*
|
||||
- * Returns: a pointer to a dynamically allocated usb_device_descriptor
|
||||
- * structure (which the caller must deallocate), or an ERR_PTR value.
|
||||
+ * This call is synchronous, and may not be used in an interrupt context.
|
||||
+ *
|
||||
+ * Return: The number of bytes received on success, or else the status code
|
||||
+ * returned by the underlying usb_control_msg() call.
|
||||
*/
|
||||
-struct usb_device_descriptor *usb_get_device_descriptor(struct usb_device *udev)
|
||||
+int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
|
||||
{
|
||||
struct usb_device_descriptor *desc;
|
||||
int ret;
|
||||
|
||||
+ if (size > sizeof(*desc))
|
||||
+ return -EINVAL;
|
||||
desc = kmalloc(sizeof(*desc), GFP_NOIO);
|
||||
if (!desc)
|
||||
- return ERR_PTR(-ENOMEM);
|
||||
-
|
||||
- ret = usb_get_descriptor(udev, USB_DT_DEVICE, 0, desc, sizeof(*desc));
|
||||
- if (ret == sizeof(*desc))
|
||||
- return desc;
|
||||
+ return -ENOMEM;
|
||||
|
||||
+ ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);
|
||||
if (ret >= 0)
|
||||
- ret = -EMSGSIZE;
|
||||
+ memcpy(&dev->descriptor, desc, size);
|
||||
kfree(desc);
|
||||
- return ERR_PTR(ret);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
|
||||
index 60363153fc3f3..69ca59841083b 100644
|
||||
--- a/drivers/usb/core/usb.h
|
||||
+++ b/drivers/usb/core/usb.h
|
||||
@@ -43,8 +43,8 @@ extern bool usb_endpoint_is_ignored(struct usb_device *udev,
|
||||
struct usb_endpoint_descriptor *epd);
|
||||
extern int usb_remove_device(struct usb_device *udev);
|
||||
|
||||
-extern struct usb_device_descriptor *usb_get_device_descriptor(
|
||||
- struct usb_device *udev);
|
||||
+extern int usb_get_device_descriptor(struct usb_device *dev,
|
||||
+ unsigned int size);
|
||||
extern int usb_set_isoch_delay(struct usb_device *dev);
|
||||
extern int usb_get_bos_descriptor(struct usb_device *dev);
|
||||
extern void usb_release_bos_descriptor(struct usb_device *dev);
|
||||
--
|
||||
2.34.1
|
||||
|
Loading…
Reference in New Issue
Block a user