USB: make hcd.h public (drivers dependency)
[deliverable/linux.git] / drivers / usb / core / driver.c
index 6a3b5cae3a6e7d3b4b16fe0229caf59f3ad315f1..edff55a325759b80fa6dcde9d9a998f413a0db8c 100644 (file)
@@ -26,8 +26,9 @@
 #include <linux/slab.h>
 #include <linux/usb.h>
 #include <linux/usb/quirks.h>
+#include <linux/usb/hcd.h>
 #include <linux/pm_runtime.h>
-#include "hcd.h"
+
 #include "usb.h"
 
 
@@ -301,7 +302,7 @@ static int usb_probe_interface(struct device *dev)
 
        intf->condition = USB_INTERFACE_BINDING;
 
-       /* Bound interfaces are initially active.  They are
+       /* Probed interfaces are initially active.  They are
         * runtime-PM-enabled only if the driver has autosuspend support.
         * They are sensitive to their children's power states.
         */
@@ -437,11 +438,11 @@ int usb_driver_claim_interface(struct usb_driver *driver,
 
        iface->condition = USB_INTERFACE_BOUND;
 
-       /* Bound interfaces are initially active.  They are
+       /* Claimed interfaces are initially inactive (suspended).  They are
         * runtime-PM-enabled only if the driver has autosuspend support.
         * They are sensitive to their children's power states.
         */
-       pm_runtime_set_active(dev);
+       pm_runtime_set_suspended(dev);
        pm_suspend_ignore_children(dev, false);
        if (driver->supports_autosuspend)
                pm_runtime_enable(dev);
@@ -1170,7 +1171,7 @@ done:
 static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
 {
        int                     status = 0;
-       int                     i = 0;
+       int                     i = 0, n = 0;
        struct usb_interface    *intf;
 
        if (udev->state == USB_STATE_NOTATTACHED ||
@@ -1179,7 +1180,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
 
        /* Suspend all the interfaces and then udev itself */
        if (udev->actconfig) {
-               for (; i < udev->actconfig->desc.bNumInterfaces; i++) {
+               n = udev->actconfig->desc.bNumInterfaces;
+               for (i = n - 1; i >= 0; --i) {
                        intf = udev->actconfig->interface[i];
                        status = usb_suspend_interface(udev, intf, msg);
                        if (status != 0)
@@ -1192,7 +1194,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
        /* If the suspend failed, resume interfaces that did get suspended */
        if (status != 0) {
                msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
-               while (--i >= 0) {
+               while (++i < n) {
                        intf = udev->actconfig->interface[i];
                        usb_resume_interface(udev, intf, msg, 0);
                }
@@ -1263,13 +1265,47 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
        return status;
 }
 
+static void choose_wakeup(struct usb_device *udev, pm_message_t msg)
+{
+       int                     w, i;
+       struct usb_interface    *intf;
+
+       /* Remote wakeup is needed only when we actually go to sleep.
+        * For things like FREEZE and QUIESCE, if the device is already
+        * autosuspended then its current wakeup setting is okay.
+        */
+       if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_QUIESCE) {
+               if (udev->state != USB_STATE_SUSPENDED)
+                       udev->do_remote_wakeup = 0;
+               return;
+       }
+
+       /* If remote wakeup is permitted, see whether any interface drivers
+        * actually want it.
+        */
+       w = 0;
+       if (device_may_wakeup(&udev->dev) && udev->actconfig) {
+               for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+                       intf = udev->actconfig->interface[i];
+                       w |= intf->needs_remote_wakeup;
+               }
+       }
+
+       /* If the device is autosuspended with the wrong wakeup setting,
+        * autoresume now so the setting can be changed.
+        */
+       if (udev->state == USB_STATE_SUSPENDED && w != udev->do_remote_wakeup)
+               pm_runtime_resume(&udev->dev);
+       udev->do_remote_wakeup = w;
+}
+
 /* The device lock is held by the PM core */
 int usb_suspend(struct device *dev, pm_message_t msg)
 {
        struct usb_device       *udev = to_usb_device(dev);
 
        do_unbind_rebind(udev, DO_UNBIND);
-       udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
+       choose_wakeup(udev, msg);
        return usb_suspend_both(udev, msg);
 }
 
This page took 0.035745 seconds and 5 git commands to generate.