aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2010-01-08 12:56:54 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2010-03-02 17:54:10 -0500
commit088f7fec8a0e683db72fd8826c5d3ab914e197b1 (patch)
tree971ef100d0db9727e4403865b67c72de62e8247a /drivers/usb
parent0c4db6df915bc470f0cd32fe48287fa6eb6adfb4 (diff)
USB: implement usb_enable_autosuspend
This patch (as1326) adds usb_enable_autosuspend() and usb_disable_autosuspend() routines for use by drivers. If a driver knows that its device can handle suspends and resumes correctly, it can enable autosuspend all by itself. This is equivalent to the user writing "auto" to the device's power/level attribute. The implementation differs slightly from what it used to be. Now autosuspend is disabled simply by doing usb_autoresume_device() (to increment the usage counter) and enabled by doing usb_autosuspend_device() (to decrement the usage counter). The set_level() attribute method is updated to use the new routines, and the USB Power-Management documentation is updated. The patch adds a usb_enable_autosuspend() call to the hub driver's probe routine, allowing the special-case code for hubs in quirks.c to be removed. 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.c42
-rw-r--r--drivers/usb/core/hub.c3
-rw-r--r--drivers/usb/core/quirks.c9
-rw-r--r--drivers/usb/core/sysfs.c23
4 files changed, 56 insertions, 21 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 2b39583040d0..057eeab06004 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1415,6 +1415,48 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
1415 1415
1416#ifdef CONFIG_USB_SUSPEND 1416#ifdef CONFIG_USB_SUSPEND
1417 1417
1418/**
1419 * usb_enable_autosuspend - allow a USB device to be autosuspended
1420 * @udev: the USB device which may be autosuspended
1421 *
1422 * This routine allows @udev to be autosuspended. An autosuspend won't
1423 * take place until the autosuspend_delay has elapsed and all the other
1424 * necessary conditions are satisfied.
1425 *
1426 * The caller must hold @udev's device lock.
1427 */
1428int usb_enable_autosuspend(struct usb_device *udev)
1429{
1430 if (udev->autosuspend_disabled) {
1431 udev->autosuspend_disabled = 0;
1432 usb_autosuspend_device(udev);
1433 }
1434 return 0;
1435}
1436EXPORT_SYMBOL_GPL(usb_enable_autosuspend);
1437
1438/**
1439 * usb_disable_autosuspend - prevent a USB device from being autosuspended
1440 * @udev: the USB device which may not be autosuspended
1441 *
1442 * This routine prevents @udev from being autosuspended and wakes it up
1443 * if it is already autosuspended.
1444 *
1445 * The caller must hold @udev's device lock.
1446 */
1447int usb_disable_autosuspend(struct usb_device *udev)
1448{
1449 int rc = 0;
1450
1451 if (!udev->autosuspend_disabled) {
1452 rc = usb_autoresume_device(udev);
1453 if (rc == 0)
1454 udev->autosuspend_disabled = 1;
1455 }
1456 return rc;
1457}
1458EXPORT_SYMBOL_GPL(usb_disable_autosuspend);
1459
1418/* Internal routine to adjust a device's usage counter and change 1460/* Internal routine to adjust a device's usage counter and change
1419 * its autosuspend state. 1461 * its autosuspend state.
1420 */ 1462 */
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index bfa6123bbdb5..746f26f222ab 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1224,6 +1224,9 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
1224 desc = intf->cur_altsetting; 1224 desc = intf->cur_altsetting;
1225 hdev = interface_to_usbdev(intf); 1225 hdev = interface_to_usbdev(intf);
1226 1226
1227 /* Hubs have proper suspend/resume support */
1228 usb_enable_autosuspend(hdev);
1229
1227 if (hdev->level == MAX_TOPO_LEVEL) { 1230 if (hdev->level == MAX_TOPO_LEVEL) {
1228 dev_err(&intf->dev, 1231 dev_err(&intf->dev,
1229 "Unsupported bus topology: hub nested too deep\n"); 1232 "Unsupported bus topology: hub nested too deep\n");
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 0b689224394b..4314f259524b 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -103,11 +103,10 @@ void usb_detect_quirks(struct usb_device *udev)
103 dev_dbg(&udev->dev, "USB quirks for this device: %x\n", 103 dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
104 udev->quirks); 104 udev->quirks);
105 105
106 /* By default, disable autosuspend for all non-hubs */ 106 /* By default, disable autosuspend for all devices. The hub driver
107#ifdef CONFIG_USB_SUSPEND 107 * will enable it for hubs.
108 if (udev->descriptor.bDeviceClass != USB_CLASS_HUB) 108 */
109 udev->autosuspend_disabled = 1; 109 usb_disable_autosuspend(udev);
110#endif
111 110
112 /* For the present, all devices default to USB-PERSIST enabled */ 111 /* For the present, all devices default to USB-PERSIST enabled */
113#if 0 /* was: #ifdef CONFIG_PM */ 112#if 0 /* was: #ifdef CONFIG_PM */
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 5a1a0e2b6474..313e241f5ccc 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -389,34 +389,25 @@ set_level(struct device *dev, struct device_attribute *attr,
389 struct usb_device *udev = to_usb_device(dev); 389 struct usb_device *udev = to_usb_device(dev);
390 int len = count; 390 int len = count;
391 char *cp; 391 char *cp;
392 int rc = 0; 392 int rc;
393 int old_autosuspend_disabled;
394 393
395 cp = memchr(buf, '\n', count); 394 cp = memchr(buf, '\n', count);
396 if (cp) 395 if (cp)
397 len = cp - buf; 396 len = cp - buf;
398 397
399 usb_lock_device(udev); 398 usb_lock_device(udev);
400 old_autosuspend_disabled = udev->autosuspend_disabled;
401 399
402 /* Setting the flags without calling usb_pm_lock is a subject to
403 * races, but who cares...
404 */
405 if (len == sizeof on_string - 1 && 400 if (len == sizeof on_string - 1 &&
406 strncmp(buf, on_string, len) == 0) { 401 strncmp(buf, on_string, len) == 0)
407 udev->autosuspend_disabled = 1; 402 rc = usb_disable_autosuspend(udev);
408 rc = usb_external_resume_device(udev, PMSG_USER_RESUME);
409 403
410 } else if (len == sizeof auto_string - 1 && 404 else if (len == sizeof auto_string - 1 &&
411 strncmp(buf, auto_string, len) == 0) { 405 strncmp(buf, auto_string, len) == 0)
412 udev->autosuspend_disabled = 0; 406 rc = usb_enable_autosuspend(udev);
413 rc = usb_external_resume_device(udev, PMSG_USER_RESUME);
414 407
415 } else 408 else
416 rc = -EINVAL; 409 rc = -EINVAL;
417 410
418 if (rc)
419 udev->autosuspend_disabled = old_autosuspend_disabled;
420 usb_unlock_device(udev); 411 usb_unlock_device(udev);
421 return (rc < 0 ? rc : count); 412 return (rc < 0 ? rc : count);
422} 413}