aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/hub.c76
1 files changed, 12 insertions, 64 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 61341d2f3c0..d3337d9c31d 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -453,6 +453,7 @@ static void hub_quiesce(struct usb_hub *hub)
{
/* stop khubd and related activity */
hub->quiescing = 1;
+ hub->activating = 0;
usb_kill_urb(hub->urb);
if (hub->has_indicators)
cancel_delayed_work(&hub->leds);
@@ -1613,68 +1614,21 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
return 0;
}
- /* suspend interface drivers; if this is a hub, it
- * suspends the child devices
- */
+ /* all interfaces must already be suspended */
if (udev->actconfig) {
int i;
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *intf;
- struct usb_driver *driver;
intf = udev->actconfig->interface[i];
- if (!is_active(intf))
- continue;
- if (!intf->dev.driver)
- continue;
- driver = to_usb_driver(intf->dev.driver);
-
- if (driver->suspend) {
- status = driver->suspend(intf, state);
- if (status == 0)
- mark_quiesced(intf);
- else
- dev_err(&intf->dev,
- "suspend error %d\n",
- status);
- }
-
- /* only drivers with suspend() can ever resume();
- * and after power loss, even they won't.
- * bus_rescan_devices() can rebind drivers later.
- *
- * FIXME the PM core self-deadlocks when unbinding
- * drivers during suspend/resume ... everything grabs
- * dpm_sem (not a spinlock, ugh). we want to unbind,
- * since we know every driver's probe/disconnect works
- * even for drivers that can't suspend.
- */
- if (!driver->suspend || state.event > PM_EVENT_FREEZE) {
-#if 1
- dev_warn(&intf->dev, "resume is unsafe!\n");
-#else
- down_write(&usb_bus_type.rwsem);
- device_release_driver(&intf->dev);
- up_write(&usb_bus_type.rwsem);
-#endif
+ if (is_active(intf)) {
+ dev_dbg(&intf->dev, "nyet suspended\n");
+ return -EBUSY;
}
}
}
- /*
- * FIXME this needs port power off call paths too, to help force
- * USB into the "generic" PM model. At least for devices on
- * ports that aren't using ganged switching (usually root hubs).
- *
- * NOTE: SRP-capable links should adopt more aggressive poweroff
- * policies (when HNP doesn't apply) once we have mechanisms to
- * turn power back on! (Likely not before 2.7...)
- */
- if (state.event > PM_EVENT_FREEZE) {
- dev_warn(&udev->dev, "no poweroff yet, suspending instead\n");
- }
-
/* "global suspend" of the HC-to-USB interface (root hub), or
* "selective suspend" of just one hub-device link.
*/
@@ -1960,26 +1914,20 @@ 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;
-
- /* stop khubd and related activity */
- hub_quiesce(hub);
- /* then suspend every port */
+ /* fail if children aren't already suspended */
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
struct usb_device *udev;
udev = hdev->children [port1-1];
- if (!udev)
- continue;
- down(&udev->serialize);
- status = __usb_suspend_device(udev, port1, msg);
- up(&udev->serialize);
- if (status < 0)
- dev_dbg(&intf->dev, "suspend port %d --> %d\n",
- port1, status);
+ if (udev && udev->state != USB_STATE_SUSPENDED) {
+ dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
+ return -EBUSY;
+ }
}
+ /* stop khubd and related activity */
+ hub_quiesce(hub);
return 0;
}