From 418add62c1391ae9195ec1c145906332732dc93c Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Sat, 26 Jul 2014 17:24:41 +0200 Subject: USB: improve error paths and tear-down USB core isn't too strict about allocation/deallocation and add/remove sequences of usb devices. Especially, error paths and device tear-down are kind of broken and cause hangs on failing usb device detect. This patch improves the situation by introducing a usb_free_device() that tears down allocated resources by usb_alloc_new_device(). usb_remove_device() now only deals with resources that have been added by usb_new_device(). Also, error handling is fixed or improved to ensure that no devices are unregistered that have not been previously registered. Signed-off-by: Sebastian Hesselbarth Signed-off-by: Sascha Hauer --- drivers/usb/core/usb.c | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) (limited to 'drivers/usb/core/usb.c') diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 7307a6f103..dff29361a8 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -430,7 +430,7 @@ int usb_new_device(struct usb_device *dev) err = register_device(&dev->dev); if (err) { printf("Failed to register device: %s\n", strerror(-err)); - return err; + goto err_out; } dev_add_param_int_ro(&dev->dev, "iManufacturer", @@ -457,42 +457,45 @@ err_out: return err; } +void usb_free_device(struct usb_device *usbdev) +{ + dma_free(usbdev->descriptor); + dma_free(usbdev->setup_packet); + free(usbdev); +} + void usb_remove_device(struct usb_device *usbdev) { - int i, ret; + int i; - for (i = 0; i < usbdev->maxchild; i++) { - if (usbdev->children[i]) - usb_remove_device(usbdev->children[i]); - } + if (!usbdev) + return; - dev_info(&usbdev->dev, "removing\n"); + for (i = 0; i < usbdev->maxchild; i++) + usb_remove_device(usbdev->children[i]); + if (usbdev->parent && usbdev->portnr) + usbdev->parent->children[usbdev->portnr - 1] = NULL; + list_del(&usbdev->list); + dev_count--; - ret = unregister_device(&usbdev->dev); - if (ret) + if (unregister_device(&usbdev->dev)) dev_err(&usbdev->dev, "failed to unregister\n"); + else + dev_info(&usbdev->dev, "removed\n"); - usbdev->parent->children[usbdev->portnr - 1] = NULL; - list_del(&usbdev->list); - free(usbdev); - dev_count--; + usb_free_device(usbdev); } struct usb_device *usb_alloc_new_device(void) { struct usb_device *usbdev = xzalloc(sizeof (*usbdev)); - if (!usbdev) - return NULL; - - usbdev->devnum = dev_index + 1; + usbdev->devnum = ++dev_index; usbdev->maxchild = 0; usbdev->dev.bus = &usb_bus_type; usbdev->setup_packet = dma_alloc(sizeof(*usbdev->setup_packet)); usbdev->descriptor = dma_alloc(sizeof(*usbdev->descriptor)); - dev_index++; - return usbdev; } @@ -508,7 +511,12 @@ int usb_host_detect(struct usb_host *host) host->root_dev = usb_alloc_new_device(); host->root_dev->dev.parent = host->hw_dev; host->root_dev->host = host; - usb_new_device(host->root_dev); + + ret = usb_new_device(host->root_dev); + if (ret) { + usb_free_device(host->root_dev); + return ret; + } } device_detect(&host->root_dev->dev); -- cgit v1.2.3