diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2006-11-22 16:55:54 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-12-01 17:25:52 -0500 |
commit | ee49fb5dc89d34f1794ac9362fa97c1a640f7ddd (patch) | |
tree | 310d11b695b631c091c9f442d566400ba9a86de9 /drivers/usb | |
parent | d25450c68767481f7c9cc4823a6da8235db40be6 (diff) |
USB: keep count of unsuspended children
This patch (as818b) simplifies autosuspend processing by keeping track
of the number of unsuspended children of each USB hub. This will
permit us to avoid a good deal of unnecessary work all the time; we
will no longer have to create a bunch of workqueue entries to carry
out autosuspend requests, only to have them fail because one of the
hub's children isn't suspended.
The basic idea is simple. There already is a usage counter in the
usb_device structure for preventing autosuspends. The patch just
increments that counter for every unsuspended child. There's only one
tricky part: When a device disconnects we need to remember whether it
was suspended at the time (leave the counter alone) or not (decrement
the counter).
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/driver.c | 34 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 14 |
2 files changed, 37 insertions, 11 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 40c1bf09b2b7..0fa15bd62c48 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -1048,7 +1048,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg) | |||
1048 | 1048 | ||
1049 | /* If the suspend succeeded, propagate it up the tree */ | 1049 | /* If the suspend succeeded, propagate it up the tree */ |
1050 | } else if (parent) | 1050 | } else if (parent) |
1051 | usb_autosuspend_device(parent, 0); | 1051 | usb_autosuspend_device(parent, 1); |
1052 | 1052 | ||
1053 | // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); | 1053 | // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); |
1054 | return status; | 1054 | return status; |
@@ -1096,9 +1096,25 @@ int usb_resume_both(struct usb_device *udev) | |||
1096 | /* Propagate the resume up the tree, if necessary */ | 1096 | /* Propagate the resume up the tree, if necessary */ |
1097 | if (udev->state == USB_STATE_SUSPENDED) { | 1097 | if (udev->state == USB_STATE_SUSPENDED) { |
1098 | if (parent) { | 1098 | if (parent) { |
1099 | usb_pm_lock(parent); | 1099 | status = usb_autoresume_device(parent, 1); |
1100 | parent->auto_pm = 1; | 1100 | if (status == 0) { |
1101 | status = usb_resume_both(parent); | 1101 | status = usb_resume_device(udev); |
1102 | if (status) { | ||
1103 | usb_autosuspend_device(parent, 1); | ||
1104 | |||
1105 | /* It's possible usb_resume_device() | ||
1106 | * failed after the port was | ||
1107 | * unsuspended, causing udev to be | ||
1108 | * logically disconnected. We don't | ||
1109 | * want usb_disconnect() to autosuspend | ||
1110 | * the parent again, so tell it that | ||
1111 | * udev disconnected while still | ||
1112 | * suspended. */ | ||
1113 | if (udev->state == | ||
1114 | USB_STATE_NOTATTACHED) | ||
1115 | udev->discon_suspended = 1; | ||
1116 | } | ||
1117 | } | ||
1102 | } else { | 1118 | } else { |
1103 | 1119 | ||
1104 | /* We can't progagate beyond the USB subsystem, | 1120 | /* We can't progagate beyond the USB subsystem, |
@@ -1107,11 +1123,9 @@ int usb_resume_both(struct usb_device *udev) | |||
1107 | if (udev->dev.parent->power.power_state.event != | 1123 | if (udev->dev.parent->power.power_state.event != |
1108 | PM_EVENT_ON) | 1124 | PM_EVENT_ON) |
1109 | status = -EHOSTUNREACH; | 1125 | status = -EHOSTUNREACH; |
1110 | } | 1126 | else |
1111 | if (status == 0) | 1127 | status = usb_resume_device(udev); |
1112 | status = usb_resume_device(udev); | 1128 | } |
1113 | if (parent) | ||
1114 | usb_pm_unlock(parent); | ||
1115 | } else { | 1129 | } else { |
1116 | 1130 | ||
1117 | /* Needed only for setting udev->dev.power.power_state.event | 1131 | /* Needed only for setting udev->dev.power.power_state.event |
@@ -1119,8 +1133,6 @@ int usb_resume_both(struct usb_device *udev) | |||
1119 | status = usb_resume_device(udev); | 1133 | status = usb_resume_device(udev); |
1120 | } | 1134 | } |
1121 | 1135 | ||
1122 | /* Now the parent won't suspend until we are finished */ | ||
1123 | |||
1124 | if (status == 0 && udev->actconfig) { | 1136 | if (status == 0 && udev->actconfig) { |
1125 | for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { | 1137 | for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { |
1126 | intf = udev->actconfig->interface[i]; | 1138 | intf = udev->actconfig->interface[i]; |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 46df5e60764b..e46d38b18249 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -1039,6 +1039,8 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev) | |||
1039 | if (udev->children[i]) | 1039 | if (udev->children[i]) |
1040 | recursively_mark_NOTATTACHED(udev->children[i]); | 1040 | recursively_mark_NOTATTACHED(udev->children[i]); |
1041 | } | 1041 | } |
1042 | if (udev->state == USB_STATE_SUSPENDED) | ||
1043 | udev->discon_suspended = 1; | ||
1042 | udev->state = USB_STATE_NOTATTACHED; | 1044 | udev->state = USB_STATE_NOTATTACHED; |
1043 | } | 1045 | } |
1044 | 1046 | ||
@@ -1228,6 +1230,14 @@ void usb_disconnect(struct usb_device **pdev) | |||
1228 | *pdev = NULL; | 1230 | *pdev = NULL; |
1229 | spin_unlock_irq(&device_state_lock); | 1231 | spin_unlock_irq(&device_state_lock); |
1230 | 1232 | ||
1233 | /* Decrement the parent's count of unsuspended children */ | ||
1234 | if (udev->parent) { | ||
1235 | usb_pm_lock(udev); | ||
1236 | if (!udev->discon_suspended) | ||
1237 | usb_autosuspend_device(udev->parent, 1); | ||
1238 | usb_pm_unlock(udev); | ||
1239 | } | ||
1240 | |||
1231 | put_device(&udev->dev); | 1241 | put_device(&udev->dev); |
1232 | } | 1242 | } |
1233 | 1243 | ||
@@ -1356,6 +1366,10 @@ static int __usb_new_device(void *void_data) | |||
1356 | goto fail; | 1366 | goto fail; |
1357 | } | 1367 | } |
1358 | 1368 | ||
1369 | /* Increment the parent's count of unsuspended children */ | ||
1370 | if (udev->parent) | ||
1371 | usb_autoresume_device(udev->parent, 1); | ||
1372 | |||
1359 | exit: | 1373 | exit: |
1360 | module_put(THIS_MODULE); | 1374 | module_put(THIS_MODULE); |
1361 | return err; | 1375 | return err; |