diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/hcd.c | 27 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 18 |
2 files changed, 37 insertions, 8 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 6b5a2f3d9e08..94d22551fc1b 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2435,6 +2435,19 @@ EXPORT_SYMBOL_GPL(usb_hcd_irq); /*-------------------------------------------------------------------------*/ +/* Workqueue routine for when the root-hub has died. */ +static void hcd_died_work(struct work_struct *work) +{ + struct usb_hcd *hcd = container_of(work, struct usb_hcd, died_work); + static char *env[] = { + "ERROR=DEAD", + NULL + }; + + /* Notify user space that the host controller has died */ + kobject_uevent_env(&hcd->self.root_hub->dev.kobj, KOBJ_OFFLINE, env); +} + /** * usb_hc_died - report abnormal shutdown of a host controller (bus glue) * @hcd: pointer to the HCD representing the controller @@ -2475,6 +2488,13 @@ void usb_hc_died (struct usb_hcd *hcd) usb_kick_hub_wq(hcd->self.root_hub); } } + + /* Handle the case where this function gets called with a shared HCD */ + if (usb_hcd_is_primary_hcd(hcd)) + schedule_work(&hcd->died_work); + else + schedule_work(&hcd->primary_hcd->died_work); + spin_unlock_irqrestore (&hcd_root_hub_lock, flags); /* Make sure that the other roothub is also deallocated. */ } @@ -2542,6 +2562,8 @@ struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver, INIT_WORK(&hcd->wakeup_work, hcd_resume_work); #endif + INIT_WORK(&hcd->died_work, hcd_died_work); + hcd->driver = driver; hcd->speed = driver->flags & HCD_MASK; hcd->product_desc = (driver->product_desc) ? driver->product_desc : @@ -2895,6 +2917,7 @@ error_create_attr_group: #ifdef CONFIG_PM cancel_work_sync(&hcd->wakeup_work); #endif + cancel_work_sync(&hcd->died_work); mutex_lock(&usb_bus_idr_lock); usb_disconnect(&rhdev); /* Sets rhdev to NULL */ mutex_unlock(&usb_bus_idr_lock); @@ -2955,6 +2978,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) #ifdef CONFIG_PM cancel_work_sync(&hcd->wakeup_work); #endif + cancel_work_sync(&hcd->died_work); mutex_lock(&usb_bus_idr_lock); usb_disconnect(&rhdev); /* Sets rhdev to NULL */ @@ -3007,6 +3031,9 @@ usb_hcd_platform_shutdown(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); + /* No need for pm_runtime_put(), we're shutting down */ + pm_runtime_get_sync(&dev->dev); + if (hcd->driver->shutdown) hcd->driver->shutdown(hcd); } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5e8f3fa7ae5a..2f94568ba385 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3669,7 +3669,6 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) struct usb_hub *hub = usb_get_intfdata(intf); struct usb_device *hdev = hub->hdev; unsigned port1; - int status; /* * Warn if children aren't already suspended. @@ -3703,12 +3702,12 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) { /* Enable hub to send remote wakeup for all ports. */ for (port1 = 1; port1 <= hdev->maxchild; port1++) { - status = set_port_feature(hdev, - port1 | - USB_PORT_FEAT_REMOTE_WAKE_CONNECT | - USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT | - USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT, - USB_PORT_FEAT_REMOTE_WAKE_MASK); + set_port_feature(hdev, + port1 | + USB_PORT_FEAT_REMOTE_WAKE_CONNECT | + USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT | + USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT, + USB_PORT_FEAT_REMOTE_WAKE_MASK); } } @@ -5903,7 +5902,10 @@ int usb_reset_device(struct usb_device *udev) cintf->needs_binding = 1; } } - usb_unbind_and_rebind_marked_interfaces(udev); + + /* If the reset failed, hub_wq will unbind drivers later */ + if (ret == 0) + usb_unbind_and_rebind_marked_interfaces(udev); } usb_autosuspend_device(udev); |