summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2005-11-21 16:58:07 (GMT)
committerGreg Kroah-Hartman <gregkh@suse.de>2006-01-04 21:48:34 (GMT)
commit4bf0ba861442d289eebfad8ea9ce365ab04fd582 (patch)
tree0301e45a155712ec03e0226a1e85e74198778f7e
parent7d069b7d80933004282c48edbe62526e4cb0aecc (diff)
downloadlinux-4bf0ba861442d289eebfad8ea9ce365ab04fd582.tar.xz
[PATCH] USB: Fix locking for USB suspend/resume
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>
-rw-r--r--drivers/usb/core/hub.c54
1 files changed, 39 insertions, 15 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 02601f4..895ac82 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -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;
}