1
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:
Jan Čermák 2024-03-06 16:49:54 +01:00 committed by GitHub
parent 6ac4f3435b
commit a6f54f6c4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 1329 additions and 0 deletions

View File

@ -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

View File

@ -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