From a6f54f6c4de867c56065a5d257eb8fbde2998e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerm=C3=A1k?= Date: Wed, 6 Mar 2024 16:49:54 +0100 Subject: [PATCH] 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 --- ...changes-causing-issues-with-Z-Wave.m.patch | 677 ++++++++++++++++++ ...changes-causing-issues-with-Z-Wave.m.patch | 652 +++++++++++++++++ 2 files changed, 1329 insertions(+) create mode 100644 buildroot-external/board/raspberrypi/patches/linux/0002-Revert-USB-core-changes-causing-issues-with-Z-Wave.m.patch create mode 100644 buildroot-external/patches/linux/6.6.20/0001-Revert-USB-core-changes-causing-issues-with-Z-Wave.m.patch diff --git a/buildroot-external/board/raspberrypi/patches/linux/0002-Revert-USB-core-changes-causing-issues-with-Z-Wave.m.patch b/buildroot-external/board/raspberrypi/patches/linux/0002-Revert-USB-core-changes-causing-issues-with-Z-Wave.m.patch new file mode 100644 index 000000000..72b6cc605 --- /dev/null +++ b/buildroot-external/board/raspberrypi/patches/linux/0002-Revert-USB-core-changes-causing-issues-with-Z-Wave.m.patch @@ -0,0 +1,677 @@ +From 74c48f9493b3e95ec1f76b92b88061194c1874fc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cerm=C3=A1k?= +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 +--- + 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 + diff --git a/buildroot-external/patches/linux/6.6.20/0001-Revert-USB-core-changes-causing-issues-with-Z-Wave.m.patch b/buildroot-external/patches/linux/6.6.20/0001-Revert-USB-core-changes-causing-issues-with-Z-Wave.m.patch new file mode 100644 index 000000000..bd05bc669 --- /dev/null +++ b/buildroot-external/patches/linux/6.6.20/0001-Revert-USB-core-changes-causing-issues-with-Z-Wave.m.patch @@ -0,0 +1,652 @@ +From 85cfbef1cf29aba24abe21409b180b7bea76e562 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cerm=C3=A1k?= +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 +--- + 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 +