Merge tag 'usb-3.3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
[linux-2.6.git] / drivers / usb / core / driver.c
index 4fee024..f8e2d6d 100644 (file)
@@ -934,13 +934,8 @@ void usb_rebind_intf(struct usb_interface *intf)
        int rc;
 
        /* Delayed unbind of an existing driver */
-       if (intf->dev.driver) {
-               struct usb_driver *driver =
-                               to_usb_driver(intf->dev.driver);
-
-               dev_dbg(&intf->dev, "forced unbind\n");
-               usb_driver_release_interface(driver, intf);
-       }
+       if (intf->dev.driver)
+               usb_forced_unbind_intf(intf);
 
        /* Try to rebind the interface */
        if (!intf->dev.power.is_prepared) {
@@ -953,15 +948,13 @@ void usb_rebind_intf(struct usb_interface *intf)
 
 #ifdef CONFIG_PM
 
-#define DO_UNBIND      0
-#define DO_REBIND      1
-
-/* Unbind drivers for @udev's interfaces that don't support suspend/resume,
- * or rebind interfaces that have been unbound, according to @action.
+/* Unbind drivers for @udev's interfaces that don't support suspend/resume
+ * There is no check for reset_resume here because it can be determined
+ * only during resume whether reset_resume is needed.
  *
  * The caller must hold @udev's device lock.
  */
-static void do_unbind_rebind(struct usb_device *udev, int action)
+static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
 {
        struct usb_host_config  *config;
        int                     i;
@@ -972,23 +965,53 @@ static void do_unbind_rebind(struct usb_device *udev, int action)
        if (config) {
                for (i = 0; i < config->desc.bNumInterfaces; ++i) {
                        intf = config->interface[i];
-                       switch (action) {
-                       case DO_UNBIND:
-                               if (intf->dev.driver) {
-                                       drv = to_usb_driver(intf->dev.driver);
-                                       if (!drv->suspend || !drv->resume)
-                                               usb_forced_unbind_intf(intf);
-                               }
-                               break;
-                       case DO_REBIND:
-                               if (intf->needs_binding)
-                                       usb_rebind_intf(intf);
-                               break;
+
+                       if (intf->dev.driver) {
+                               drv = to_usb_driver(intf->dev.driver);
+                               if (!drv->suspend || !drv->resume)
+                                       usb_forced_unbind_intf(intf);
                        }
                }
        }
 }
 
+/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
+ * These interfaces have the needs_binding flag set by usb_resume_interface().
+ *
+ * The caller must hold @udev's device lock.
+ */
+static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
+{
+       struct usb_host_config  *config;
+       int                     i;
+       struct usb_interface    *intf;
+
+       config = udev->actconfig;
+       if (config) {
+               for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+                       intf = config->interface[i];
+                       if (intf->dev.driver && intf->needs_binding)
+                               usb_forced_unbind_intf(intf);
+               }
+       }
+}
+
+static void do_rebind_interfaces(struct usb_device *udev)
+{
+       struct usb_host_config  *config;
+       int                     i;
+       struct usb_interface    *intf;
+
+       config = udev->actconfig;
+       if (config) {
+               for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+                       intf = config->interface[i];
+                       if (intf->needs_binding)
+                               usb_rebind_intf(intf);
+               }
+       }
+}
+
 static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
 {
        struct usb_device_driver        *udriver;
@@ -1278,35 +1301,48 @@ int usb_suspend(struct device *dev, pm_message_t msg)
 {
        struct usb_device       *udev = to_usb_device(dev);
 
-       do_unbind_rebind(udev, DO_UNBIND);
+       unbind_no_pm_drivers_interfaces(udev);
+
+       /* From now on we are sure all drivers support suspend/resume
+        * but not necessarily reset_resume()
+        * so we may still need to unbind and rebind upon resume
+        */
        choose_wakeup(udev, msg);
        return usb_suspend_both(udev, msg);
 }
 
 /* The device lock is held by the PM core */
+int usb_resume_complete(struct device *dev)
+{
+       struct usb_device *udev = to_usb_device(dev);
+
+       /* For PM complete calls, all we do is rebind interfaces
+        * whose needs_binding flag is set
+        */
+       if (udev->state != USB_STATE_NOTATTACHED)
+               do_rebind_interfaces(udev);
+       return 0;
+}
+
+/* The device lock is held by the PM core */
 int usb_resume(struct device *dev, pm_message_t msg)
 {
        struct usb_device       *udev = to_usb_device(dev);
        int                     status;
 
-       /* For PM complete calls, all we do is rebind interfaces */
-       if (msg.event == PM_EVENT_ON) {
-               if (udev->state != USB_STATE_NOTATTACHED)
-                       do_unbind_rebind(udev, DO_REBIND);
-               status = 0;
-
-       /* For all other calls, take the device back to full power and
+       /* For all calls, take the device back to full power and
         * tell the PM core in case it was autosuspended previously.
-        * Unbind the interfaces that will need rebinding later.
+        * Unbind the interfaces that will need rebinding later,
+        * because they fail to support reset_resume.
+        * (This can't be done in usb_resume_interface()
+        * above because it doesn't own the right set of locks.)
         */
-       } else {
-               status = usb_resume_both(udev, msg);
-               if (status == 0) {
-                       pm_runtime_disable(dev);
-                       pm_runtime_set_active(dev);
-                       pm_runtime_enable(dev);
-                       do_unbind_rebind(udev, DO_REBIND);
-               }
+       status = usb_resume_both(udev, msg);
+       if (status == 0) {
+               pm_runtime_disable(dev);
+               pm_runtime_set_active(dev);
+               pm_runtime_enable(dev);
+               unbind_no_reset_resume_drivers_interfaces(udev);
        }
 
        /* Avoid PM error messages for devices disconnected while suspended