David:

Please take a look at this proposed patch and see what you think.  It 
removes the recursion from the usbcore suspend/resume routines.  There's 
just a couple of small exceptions; the big one is that resuming a device 
will cause all its interfaces to be resumed as well, and the small one is 
that before suspending a device all the interfaces are checked to make 
sure they are already suspended.

In addition, the code for handling interfaces has been moved from hub.c 
into the generic suspend/resume routines near the end of usb.c.  However I 
didn't bother to change the part in usb_resume_device where the single 
interface of a root hub gets resumed along with the root hub; that code 
still calls hub_resume directly.

All this should work perfectly well with system sleep transitions, as the 
PM core does the recursion for us.  Runtime power management will be a 
little more difficult; for testing purposes it will be handy to have some 
scripts available.

Alan Stern



Index: usb-2.6/drivers/usb/core/hub.c
===================================================================
--- usb-2.6.orig/drivers/usb/core/hub.c
+++ usb-2.6/drivers/usb/core/hub.c
@@ -452,6 +452,7 @@ static void hub_quiesce(struct usb_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);
@@ -1553,50 +1554,23 @@ static int __usb_suspend_device (struct 
                return 0;
        }
 
-       /* suspend interface drivers; if this is a hub, it
-        * suspends the child devices
+       /* check to be sure the interfaces really are suspended;
+        * the caller should have suspended them already
+        *
+        * FIXME: Should this check all the child devices?
         */
        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 (state <= intf->dev.power.power_state)
-                               continue;
-                       if (!intf->dev.driver)
-                               continue;
-                       driver = to_usb_driver(intf->dev.driver);
-
-                       if (driver->suspend) {
-                               status = driver->suspend(intf, state);
-                               if (intf->dev.power.power_state != state
-                                               || status)
-                                       dev_err(&intf->dev,
-                                               "suspend %d fail, code %d\n",
-                                               state, 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 > PM_SUSPEND_MEM) {
-#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 (intf->dev.power.power_state == PMSG_ON) {
+                               dev_err(&udev->dev, "suspend %d failed, "
+                                               "interface %s still awake\n",
+                                               state, intf->dev.bus_id);
+                               return -EBUSY;
                        }
                }
        }
@@ -1719,33 +1693,13 @@ static int finish_port_resume(struct usb
                        }
                }
 
-               /* resume interface drivers; if this is a hub, it
-                * resumes the child devices
-                */
+               /* resume interface drivers */
+               /* FIXME: Is this single-level recursion needed? */
                for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
                        struct usb_interface    *intf;
-                       struct usb_driver       *driver;
 
                        intf = udev->actconfig->interface[i];
-                       if (intf->dev.power.power_state == PMSG_SUSPEND)
-                               continue;
-                       if (!intf->dev.driver) {
-                               /* FIXME maybe force to alt 0 */
-                               continue;
-                       }
-                       driver = to_usb_driver(intf->dev.driver);
-
-                       /* bus_rescan_devices() may rebind drivers */
-                       if (!driver->resume)
-                               continue;
-
-                       /* can we do better than just logging errors? */
-                       status = driver->resume(intf);
-                       if (intf->dev.power.power_state != PMSG_ON
-                                       || status)
-                               dev_dbg(&intf->dev,
-                                       "resume fail, state %d code %d\n",
-                                       intf->dev.power.power_state, status);
+                       intf->dev.bus->resume(&intf->dev);
                }
                status = 0;
 
@@ -1897,24 +1851,20 @@ static int hub_suspend(struct usb_interf
        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 */
+       /* warn if some port isn't 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, state);
-               up(&udev->serialize);
-               if (status < 0)
-                       dev_dbg(&intf->dev, "suspend port %d --> %d\n",
-                               port1, status);
+               if (udev->dev.power.power_state == PMSG_ON)
+                       dev_dbg(&intf->dev, "port %d isn't suspended\n",
+                               port1);
        }
 
        intf->dev.power.power_state = state;
@@ -1923,47 +1873,10 @@ static int hub_suspend(struct usb_interf
 
 static int hub_resume(struct usb_interface *intf)
 {
-       struct usb_device       *hdev = interface_to_usbdev(intf);
        struct usb_hub          *hub = usb_get_intfdata (intf);
-       unsigned                port1;
-       int                     status;
 
-       if (intf->dev.power.power_state == PM_SUSPEND_ON)
+       if (intf->dev.power.power_state == PMSG_ON)
                return 0;
-
-       for (port1 = 1; port1 <= hdev->maxchild; port1++) {
-               struct usb_device       *udev;
-               u16                     portstat, portchange;
-
-               udev = hdev->children [port1-1];
-               status = hub_port_status(hub, port1, &portstat, &portchange);
-               if (status == 0) {
-                       if (portchange & USB_PORT_STAT_C_SUSPEND) {
-                               clear_port_feature(hdev, port1,
-                                       USB_PORT_FEAT_C_SUSPEND);
-                               portchange &= ~USB_PORT_STAT_C_SUSPEND;
-                       }
-
-                       /* let khubd handle disconnects etc */
-                       if (portchange)
-                               continue;
-               }
-
-               if (!udev || status < 0)
-                       continue;
-               down (&udev->serialize);
-               if (portstat & USB_PORT_STAT_SUSPEND)
-                       status = hub_port_resume(hub, port1, udev);
-               else {
-                       status = finish_port_resume(udev);
-                       if (status < 0) {
-                               dev_dbg(&intf->dev, "resume port %d --> %d\n",
-                                       port1, status);
-                               hub_port_logical_disconnect(hub, port1);
-                       }
-               }
-               up(&udev->serialize);
-       }
        intf->dev.power.power_state = PMSG_ON;
 
        hub->resume_root_hub = 0;
Index: usb-2.6/drivers/usb/core/usb.c
===================================================================
--- usb-2.6.orig/drivers/usb/core/usb.c
+++ usb-2.6/drivers/usb/core/usb.c
@@ -1379,23 +1379,49 @@ static int usb_generic_suspend(struct de
 {
        struct usb_interface *intf;
        struct usb_driver *driver;
+       int status;
 
+       /* there's only one USB suspend state */
+       if (dev->power.power_state != PMSG_ON)
+               return 0;
+
+       /* devices suspend through their hub */
        if (dev->driver == &usb_generic_driver)
                return usb_suspend_device (to_usb_device(dev), message);
 
-       if ((dev->driver == NULL) ||
-           (dev->driver_data == &usb_generic_driver_data))
+       if (dev->driver == NULL) {
+               dev->power.power_state = message;
                return 0;
+       }
 
        intf = to_usb_interface(dev);
        driver = to_usb_driver(dev->driver);
-
-       /* there's only one USB suspend state */
-       if (intf->dev.power.power_state)
-               return 0;
-
-       if (driver->suspend)
-               return driver->suspend(intf, message);
+       if (driver->suspend) {
+               status = driver->suspend(intf, message);
+               if (intf->dev.power.power_state != message || status)
+                       dev_err(&intf->dev, "suspend %d fail, code %d\n",
+                                       message, status);
+       } else {
+               dev->power.power_state = message;
+
+               /* 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 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
+       }
        return 0;
 }
 
@@ -1403,20 +1429,40 @@ static int usb_generic_resume(struct dev
 {
        struct usb_interface *intf;
        struct usb_driver *driver;
+       int status;
+
+       if (dev->power.power_state == PMSG_ON)
+               return 0;
 
        /* devices resume through their hub */
        if (dev->driver == &usb_generic_driver)
                return usb_resume_device (to_usb_device(dev));
 
-       if ((dev->driver == NULL) ||
-           (dev->driver_data == &usb_generic_driver_data))
+       intf = to_usb_interface(dev);
+       if (dev->driver == NULL) {
+               dev->power.power_state = PMSG_ON;
+
+               /* Force to altsetting 0 for the next driver */
+               if (intf->cur_altsetting->desc.bAlternateSetting != 0)
+                       usb_set_interface(interface_to_usbdev(intf),
+                               intf->cur_altsetting->desc.bInterfaceNumber,
+                               0);
+
+               /* bus_rescan_devices() may rebind drivers */
                return 0;
+       }
 
-       intf = to_usb_interface(dev);
        driver = to_usb_driver(dev->driver);
+       if (driver->resume) {
+
+               /* can we do better than just logging errors? */
+               status = driver->resume(intf);
+               if (intf->dev.power.power_state != PMSG_ON || status)
+                       dev_dbg(&intf->dev, "resume fail, state %d code %d\n",
+                                       intf->dev.power.power_state, status);
+       } else
+               dev->power.power_state = PMSG_ON;
 
-       if (driver->resume)
-               return driver->resume(intf);
        return 0;
 }
 



-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to