Skip to content

Commit

Permalink
USB: core: Change usb_get_device_descriptor() API
Browse files Browse the repository at this point in the history
commit de28e46 upstream.

The usb_get_device_descriptor() routine reads the device descriptor
from the udev device and stores it directly in udev->descriptor.  This
interface is error prone, because the USB subsystem expects in-memory
copies of a device's descriptors to be immutable once the device has
been initialized.

The interface is changed so that the device descriptor is left in a
kmalloc-ed buffer, not copied into the usb_device structure.  A
pointer to the buffer is returned to the caller, who is then
responsible for kfree-ing it.  The corresponding changes needed in the
various callers are fairly small.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Link: https://lore.kernel.org/r/d0111bb6-56c1-4f90-adf2-6cfe152f6561@rowland.harvard.edu
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
AlanStern authored and gregkh committed Sep 13, 2023
1 parent 90b01f8 commit d309fa6
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 43 deletions.
10 changes: 7 additions & 3 deletions drivers/usb/core/hcd.c
Expand Up @@ -983,6 +983,7 @@ 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;

Expand All @@ -994,13 +995,16 @@ static int register_root_hub(struct usb_hcd *hcd)
mutex_lock(&usb_bus_idr_lock);

usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
if (retval != sizeof usb_dev->descriptor) {
descr = usb_get_device_descriptor(usb_dev);
if (IS_ERR(descr)) {
retval = PTR_ERR(descr);
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 < 0) ? retval : -EMSGSIZE;
return retval;
}
usb_dev->descriptor = *descr;
kfree(descr);

if (le16_to_cpu(usb_dev->descriptor.bcdUSB) >= 0x0201) {
retval = usb_get_bos_descriptor(usb_dev);
Expand Down
44 changes: 23 additions & 21 deletions drivers/usb/core/hub.c
Expand Up @@ -2656,12 +2656,17 @@ int usb_authorize_device(struct usb_device *usb_dev)
}

if (usb_dev->wusb) {
result = usb_get_device_descriptor(usb_dev, sizeof(usb_dev->descriptor));
if (result < 0) {
struct usb_device_descriptor *descr;

descr = usb_get_device_descriptor(usb_dev);
if (IS_ERR(descr)) {
result = PTR_ERR(descr);
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;
Expand Down Expand Up @@ -4747,7 +4752,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
const char *driver_name;
bool do_new_scheme;
int maxp0;
struct usb_device_descriptor *buf;
struct usb_device_descriptor *buf, *descr;

buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
if (!buf)
Expand Down Expand Up @@ -4984,15 +4989,16 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
usb_ep0_reinit(udev);
}

retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval < (signed)sizeof(udev->descriptor)) {
descr = usb_get_device_descriptor(udev);
if (IS_ERR(descr)) {
retval = PTR_ERR(descr);
if (retval != -ENODEV)
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
retval);
if (retval >= 0)
retval = -ENOMSG;
goto fail;
}
udev->descriptor = *descr;
kfree(descr);

/*
* Some superspeed devices have finished the link training process
Expand Down Expand Up @@ -5111,7 +5117,7 @@ hub_power_remaining(struct usb_hub *hub)


static int descriptors_changed(struct usb_device *udev,
struct usb_device_descriptor *old_device_descriptor,
struct usb_device_descriptor *new_device_descriptor,
struct usb_host_bos *old_bos)
{
int changed = 0;
Expand All @@ -5122,8 +5128,8 @@ static int descriptors_changed(struct usb_device *udev,
int length;
char *buf;

if (memcmp(&udev->descriptor, old_device_descriptor,
sizeof(*old_device_descriptor)) != 0)
if (memcmp(&udev->descriptor, new_device_descriptor,
sizeof(*new_device_descriptor)) != 0)
return 1;

if ((old_bos && !udev->bos) || (!old_bos && udev->bos))
Expand Down Expand Up @@ -5443,9 +5449,8 @@ 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 descriptor;
struct usb_device_descriptor *descr;
int status = -ENODEV;
int retval;

dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus,
portchange, portspeed(hub, portstatus));
Expand All @@ -5472,23 +5477,20 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
* changed device descriptors before resuscitating the
* device.
*/
descriptor = udev->descriptor;
retval = usb_get_device_descriptor(udev,
sizeof(udev->descriptor));
if (retval < 0) {
descr = usb_get_device_descriptor(udev);
if (IS_ERR(descr)) {
dev_dbg(&udev->dev,
"can't read device descriptor %d\n",
retval);
"can't read device descriptor %ld\n",
PTR_ERR(descr));
} else {
if (descriptors_changed(udev, &descriptor,
if (descriptors_changed(udev, descr,
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 &&
Expand Down
29 changes: 12 additions & 17 deletions drivers/usb/core/message.c
Expand Up @@ -1039,40 +1039,35 @@ char *usb_cache_string(struct usb_device *udev, int index)
}

/*
* 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
* usb_get_device_descriptor - read the device descriptor
* @udev: the device whose device descriptor should be 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.
*
* 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.
* Returns: a pointer to a dynamically allocated usb_device_descriptor
* structure (which the caller must deallocate), or an ERR_PTR value.
*/
int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
struct usb_device_descriptor *usb_get_device_descriptor(struct usb_device *udev)
{
struct usb_device_descriptor *desc;
int ret;

if (size > sizeof(*desc))
return -EINVAL;
desc = kmalloc(sizeof(*desc), GFP_NOIO);
if (!desc)
return -ENOMEM;
return ERR_PTR(-ENOMEM);

ret = usb_get_descriptor(udev, USB_DT_DEVICE, 0, desc, sizeof(*desc));
if (ret == sizeof(*desc))
return desc;

ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);
if (ret >= 0)
memcpy(&dev->descriptor, desc, size);
ret = -EMSGSIZE;
kfree(desc);
return ret;
return ERR_PTR(ret);
}

/*
Expand Down
4 changes: 2 additions & 2 deletions drivers/usb/core/usb.h
Expand Up @@ -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 int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
extern struct usb_device_descriptor *usb_get_device_descriptor(
struct usb_device *udev);
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);
Expand Down

0 comments on commit d309fa6

Please sign in to comment.