diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2006-10-30 17:06:45 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-12-01 17:23:29 -0500 |
commit | af4f76066d0fcb215ae389b8839d7ae37ce0e28b (patch) | |
tree | 3d3ea0546de3dec1a340752d35831c1cbbd178fa /drivers/usb | |
parent | 0c1ac4f25f894f9df0ffe9b912c165fb6a185a3c (diff) |
USB: autosuspend code consolidation
This patch (as813) gathers together common code for USB interface
autosuspend/autoresume.
It also adds some simple checking at the time an autosuspend request
is made, to see whether the request will fail. This way we don't
add a workqueue entry when it would end up doing nothing.
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 | 112 |
1 files changed, 67 insertions, 45 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 401d76f13419..ca0e40ed2b72 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -940,6 +940,36 @@ done: | |||
940 | return status; | 940 | return status; |
941 | } | 941 | } |
942 | 942 | ||
943 | /* Internal routine to check whether we may autosuspend a device. */ | ||
944 | static int autosuspend_check(struct usb_device *udev) | ||
945 | { | ||
946 | int i; | ||
947 | struct usb_interface *intf; | ||
948 | |||
949 | /* For autosuspend, fail fast if anything is in use. | ||
950 | * Also fail if any interfaces require remote wakeup but it | ||
951 | * isn't available. */ | ||
952 | udev->do_remote_wakeup = device_may_wakeup(&udev->dev); | ||
953 | if (udev->pm_usage_cnt > 0) | ||
954 | return -EBUSY; | ||
955 | if (udev->actconfig) { | ||
956 | for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { | ||
957 | intf = udev->actconfig->interface[i]; | ||
958 | if (!is_active(intf)) | ||
959 | continue; | ||
960 | if (intf->pm_usage_cnt > 0) | ||
961 | return -EBUSY; | ||
962 | if (intf->needs_remote_wakeup && | ||
963 | !udev->do_remote_wakeup) { | ||
964 | dev_dbg(&udev->dev, "remote wakeup needed " | ||
965 | "for autosuspend\n"); | ||
966 | return -EOPNOTSUPP; | ||
967 | } | ||
968 | } | ||
969 | } | ||
970 | return 0; | ||
971 | } | ||
972 | |||
943 | /** | 973 | /** |
944 | * usb_suspend_both - suspend a USB device and its interfaces | 974 | * usb_suspend_both - suspend a USB device and its interfaces |
945 | * @udev: the usb_device to suspend | 975 | * @udev: the usb_device to suspend |
@@ -991,28 +1021,10 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg) | |||
991 | 1021 | ||
992 | udev->do_remote_wakeup = device_may_wakeup(&udev->dev); | 1022 | udev->do_remote_wakeup = device_may_wakeup(&udev->dev); |
993 | 1023 | ||
994 | /* For autosuspend, fail fast if anything is in use. | ||
995 | * Also fail if any interfaces require remote wakeup but it | ||
996 | * isn't available. */ | ||
997 | if (udev->auto_pm) { | 1024 | if (udev->auto_pm) { |
998 | if (udev->pm_usage_cnt > 0) | 1025 | status = autosuspend_check(udev); |
999 | return -EBUSY; | 1026 | if (status < 0) |
1000 | if (udev->actconfig) { | 1027 | return status; |
1001 | for (; i < udev->actconfig->desc.bNumInterfaces; i++) { | ||
1002 | intf = udev->actconfig->interface[i]; | ||
1003 | if (!is_active(intf)) | ||
1004 | continue; | ||
1005 | if (intf->pm_usage_cnt > 0) | ||
1006 | return -EBUSY; | ||
1007 | if (intf->needs_remote_wakeup && | ||
1008 | !udev->do_remote_wakeup) { | ||
1009 | dev_dbg(&udev->dev, | ||
1010 | "remote wakeup needed for autosuspend\n"); | ||
1011 | return -EOPNOTSUPP; | ||
1012 | } | ||
1013 | } | ||
1014 | i = 0; | ||
1015 | } | ||
1016 | } | 1028 | } |
1017 | 1029 | ||
1018 | /* Suspend all the interfaces and then udev itself */ | 1030 | /* Suspend all the interfaces and then udev itself */ |
@@ -1151,7 +1163,7 @@ void usb_autosuspend_device(struct usb_device *udev, int dec_usage_cnt) | |||
1151 | { | 1163 | { |
1152 | usb_pm_lock(udev); | 1164 | usb_pm_lock(udev); |
1153 | udev->pm_usage_cnt -= dec_usage_cnt; | 1165 | udev->pm_usage_cnt -= dec_usage_cnt; |
1154 | if (udev->pm_usage_cnt <= 0) | 1166 | if (autosuspend_check(udev) == 0) |
1155 | queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, | 1167 | queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, |
1156 | USB_AUTOSUSPEND_DELAY); | 1168 | USB_AUTOSUSPEND_DELAY); |
1157 | usb_pm_unlock(udev); | 1169 | usb_pm_unlock(udev); |
@@ -1200,6 +1212,33 @@ int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt) | |||
1200 | return status; | 1212 | return status; |
1201 | } | 1213 | } |
1202 | 1214 | ||
1215 | /* Internal routine to adjust an interface's usage counter and change | ||
1216 | * its device's autosuspend state. | ||
1217 | */ | ||
1218 | static int usb_autopm_do_interface(struct usb_interface *intf, | ||
1219 | int inc_usage_cnt) | ||
1220 | { | ||
1221 | struct usb_device *udev = interface_to_usbdev(intf); | ||
1222 | int status = 0; | ||
1223 | |||
1224 | usb_pm_lock(udev); | ||
1225 | if (intf->condition == USB_INTERFACE_UNBOUND) | ||
1226 | status = -ENODEV; | ||
1227 | else { | ||
1228 | intf->pm_usage_cnt += inc_usage_cnt; | ||
1229 | if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) { | ||
1230 | udev->auto_pm = 1; | ||
1231 | status = usb_resume_both(udev); | ||
1232 | if (status != 0) | ||
1233 | intf->pm_usage_cnt -= inc_usage_cnt; | ||
1234 | } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) | ||
1235 | queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, | ||
1236 | USB_AUTOSUSPEND_DELAY); | ||
1237 | } | ||
1238 | usb_pm_unlock(udev); | ||
1239 | return status; | ||
1240 | } | ||
1241 | |||
1203 | /** | 1242 | /** |
1204 | * usb_autopm_put_interface - decrement a USB interface's PM-usage counter | 1243 | * usb_autopm_put_interface - decrement a USB interface's PM-usage counter |
1205 | * @intf: the usb_interface whose counter should be decremented | 1244 | * @intf: the usb_interface whose counter should be decremented |
@@ -1233,17 +1272,11 @@ int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt) | |||
1233 | */ | 1272 | */ |
1234 | void usb_autopm_put_interface(struct usb_interface *intf) | 1273 | void usb_autopm_put_interface(struct usb_interface *intf) |
1235 | { | 1274 | { |
1236 | struct usb_device *udev = interface_to_usbdev(intf); | 1275 | int status; |
1237 | 1276 | ||
1238 | usb_pm_lock(udev); | 1277 | status = usb_autopm_do_interface(intf, -1); |
1239 | if (intf->condition != USB_INTERFACE_UNBOUND && | 1278 | // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", |
1240 | --intf->pm_usage_cnt <= 0) { | 1279 | // __FUNCTION__, status, intf->pm_usage_cnt); |
1241 | queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, | ||
1242 | USB_AUTOSUSPEND_DELAY); | ||
1243 | } | ||
1244 | usb_pm_unlock(udev); | ||
1245 | // dev_dbg(&intf->dev, "%s: cnt %d\n", | ||
1246 | // __FUNCTION__, intf->pm_usage_cnt); | ||
1247 | } | 1280 | } |
1248 | EXPORT_SYMBOL_GPL(usb_autopm_put_interface); | 1281 | EXPORT_SYMBOL_GPL(usb_autopm_put_interface); |
1249 | 1282 | ||
@@ -1280,20 +1313,9 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface); | |||
1280 | */ | 1313 | */ |
1281 | int usb_autopm_get_interface(struct usb_interface *intf) | 1314 | int usb_autopm_get_interface(struct usb_interface *intf) |
1282 | { | 1315 | { |
1283 | struct usb_device *udev = interface_to_usbdev(intf); | 1316 | int status; |
1284 | int status; | ||
1285 | 1317 | ||
1286 | usb_pm_lock(udev); | 1318 | status = usb_autopm_do_interface(intf, 1); |
1287 | if (intf->condition == USB_INTERFACE_UNBOUND) | ||
1288 | status = -ENODEV; | ||
1289 | else { | ||
1290 | ++intf->pm_usage_cnt; | ||
1291 | udev->auto_pm = 1; | ||
1292 | status = usb_resume_both(udev); | ||
1293 | if (status != 0) | ||
1294 | --intf->pm_usage_cnt; | ||
1295 | } | ||
1296 | usb_pm_unlock(udev); | ||
1297 | // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", | 1319 | // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", |
1298 | // __FUNCTION__, status, intf->pm_usage_cnt); | 1320 | // __FUNCTION__, status, intf->pm_usage_cnt); |
1299 | return status; | 1321 | return status; |