dev_index only ever counted up, so every device that showed up got an address one higher than the previous one. Without device removal that was fine - there is a limited number of devices on a bus - but once devices come and go, the counter keeps climbing. USB addresses are 7 bit wide, so after 127 devices usb_set_address() starts handing out values that cannot be represented and enumeration breaks until the next reset.
Keep a bitmap of the addresses in use instead and hand back the address when the device is freed. As a side effect an exhausted address space is now reported instead of silently producing broken addresses. Signed-off-by: Sascha Hauer <[email protected]> Assisted-by: Claude:claude-opus-5 --- drivers/usb/core/usb.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index bc80e66fcb..ad0d0965b1 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -38,6 +38,7 @@ #include <init.h> #include <dma.h> +#include <linux/bitmap.h> #include <linux/usb/usb.h> #include <linux/usb/ch9.h> @@ -46,7 +47,33 @@ #define USB_BUFSIZ 512 static int dev_count; -static int dev_index; + +/* + * USB addresses are 7 bit wide and 0 is reserved for the default address, + * so the usable range is 1..127. Track them in a bitmap rather than just + * counting up, otherwise a board that sees enough plug/unplug cycles + * eventually hands out addresses a device cannot have. + */ +static DECLARE_BITMAP(usb_addresses, 128); + +static int usb_alloc_address(void) +{ + int addr; + + addr = find_next_zero_bit(usb_addresses, 128, 1); + if (addr >= 128) + return -EADDRNOTAVAIL; + + set_bit(addr, usb_addresses); + + return addr; +} + +static void usb_free_address(int addr) +{ + if (addr > 0) + clear_bit(addr, usb_addresses); +} LIST_HEAD(usb_host_list); LIST_HEAD(usb_device_list); @@ -487,7 +514,12 @@ int usb_new_device(struct usb_device *dev) usb_setup_descriptor(dev, !host->no_desc_before_addr); - dev->devnum = ++dev_index; + err = usb_alloc_address(); + if (err < 0) { + dev_err(&dev->dev, "out of USB addresses\n"); + goto err_out; + } + dev->devnum = err; err = usb_set_address(dev); /* set address */ @@ -605,6 +637,7 @@ int usb_new_device(struct usb_device *dev) void usb_free_device(struct usb_device *usbdev) { + usb_free_address(usbdev->devnum); dma_free(usbdev->descriptor); dma_free(usbdev->setup_packet); free_device_res(&usbdev->dev); -- 2.47.3
