[PATCH] USB: Fix locking for USB suspend/resume
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 21 Nov 2005 16:58:07 +0000 (11:58 -0500)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 4 Jan 2006 21:48:34 +0000 (13:48 -0800)
The earlier USB locking updates didn't touch the suspend/resume
routines.  They need updating as well, since now the caller holds the
device semaphore.  This patch (as608) makes the necessary changes.  It
also adds a line to store the correct power state when a device is
resumed, something which was unaccountably missing.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/hub.c

index 02601f412f9dd1784bc55451e40d8924c9cd55d9..895ac829b9cf4930fac281c857e11a06ab678a72 100644 (file)
@@ -1648,15 +1648,22 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
 int usb_suspend_device(struct usb_device *udev)
 {
 #ifdef CONFIG_USB_SUSPEND
-       int     port1, status;
+       int     port1;
 
-       port1 = locktree(udev);
-       if (port1 < 0)
-               return port1;
+       if (udev->state == USB_STATE_NOTATTACHED)
+               return -ENODEV;
+       if (!udev->parent)
+               port1 = 0;
+       else {
+               for (port1 = udev->parent->maxchild; port1 > 0; --port1) {
+                       if (udev->parent->children[port1-1] == udev)
+                               break;
+               }
+               if (port1 == 0)
+                       return -ENODEV;
+       }
 
-       status = __usb_suspend_device(udev, port1);
-       usb_unlock_device(udev);
-       return status;
+       return __usb_suspend_device(udev, port1);
 #else
        /* NOTE:  udev->state unchanged, it's not lying ... */
        udev->dev.power.power_state = PMSG_SUSPEND;
@@ -1688,6 +1695,7 @@ static int finish_device_resume(struct usb_device *udev)
        usb_set_device_state(udev, udev->actconfig
                        ? USB_STATE_CONFIGURED
                        : USB_STATE_ADDRESS);
+       udev->dev.power.power_state = PMSG_ON;
 
        /* 10.5.4.5 says be sure devices in the tree are still there.
         * For now let's assume the device didn't go crazy on resume,
@@ -1723,8 +1731,14 @@ static int finish_device_resume(struct usb_device *udev)
                 * may have a child resume event to deal with soon
                 */
                resume = udev->dev.bus->resume;
-               for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++)
-                       (void) resume(&udev->actconfig->interface[i]->dev);
+               for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+                       struct device *dev =
+                                       &udev->actconfig->interface[i]->dev;
+
+                       down(&dev->sem);
+                       (void) resume(dev);
+                       up(&dev->sem);
+               }
                status = 0;
 
        } else if (udev->devnum <= 0) {
@@ -1809,9 +1823,18 @@ int usb_resume_device(struct usb_device *udev)
 {
        int     port1, status;
 
-       port1 = locktree(udev);
-       if (port1 < 0)
-               return port1;
+       if (udev->state == USB_STATE_NOTATTACHED)
+               return -ENODEV;
+       if (!udev->parent)
+               port1 = 0;
+       else {
+               for (port1 = udev->parent->maxchild; port1 > 0; --port1) {
+                       if (udev->parent->children[port1-1] == udev)
+                               break;
+               }
+               if (port1 == 0)
+                       return -ENODEV;
+       }
 
 #ifdef CONFIG_USB_SUSPEND
        /* selective resume of one downstream hub-to-device port */
@@ -1830,11 +1853,12 @@ int usb_resume_device(struct usb_device *udev)
                dev_dbg(&udev->dev, "can't resume, status %d\n",
                        status);
 
-       usb_unlock_device(udev);
-
        /* rebind drivers that had no suspend() */
-       if (status == 0)
+       if (status == 0) {
+               usb_unlock_device(udev);
                bus_rescan_devices(&usb_bus_type);
+               usb_lock_device(udev);
+       }
        return status;
 }