aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--Documentation/usb/power-management.txt18
-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
-rw-r--r--include/linux/usb.h8
6 files changed, 82 insertions, 21 deletions
diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt
index 3bf6818c8cf5..e3fa189c257a 100644
--- a/Documentation/usb/power-management.txt
+++ b/Documentation/usb/power-management.txt
@@ -229,6 +229,11 @@ necessary operations by hand or add them to a udev script. You can
229also change the idle-delay time; 2 seconds is not the best choice for 229also change the idle-delay time; 2 seconds is not the best choice for
230every device. 230every device.
231 231
232If a driver knows that its device has proper suspend/resume support,
233it can enable autosuspend all by itself. For example, the video
234driver for a laptop's webcam might do this, since these devices are
235rarely used and so should normally be autosuspended.
236
232Sometimes it turns out that even when a device does work okay with 237Sometimes it turns out that even when a device does work okay with
233autosuspend there are still problems. For example, there are 238autosuspend there are still problems. For example, there are
234experimental patches adding autosuspend support to the usbhid driver, 239experimental patches adding autosuspend support to the usbhid driver,
@@ -384,6 +389,19 @@ autosuspend, there's no delay for an autoresume.
384 Other parts of the driver interface 389 Other parts of the driver interface
385 ----------------------------------- 390 -----------------------------------
386 391
392Drivers can enable autosuspend for their devices by calling
393
394 usb_enable_autosuspend(struct usb_device *udev);
395
396in their probe() routine, if they know that the device is capable of
397suspending and resuming correctly. This is exactly equivalent to
398writing "auto" to the device's power/level attribute. Likewise,
399drivers can disable autosuspend by calling
400
401 usb_disable_autosuspend(struct usb_device *udev);
402
403This is exactly the same as writing "on" to the power/level attribute.
404
387Sometimes a driver needs to make sure that remote wakeup is enabled 405Sometimes a driver needs to make sure that remote wakeup is enabled
388during autosuspend. For example, there's not much point 406during autosuspend. For example, there's not much point
389autosuspending a keyboard if the user can't cause the keyboard to do a 407autosuspending a keyboard if the user can't cause the keyboard to do a
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}
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 332eaea61021..e6419ac89ea2 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -542,6 +542,9 @@ extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);
542 542
543/* USB autosuspend and autoresume */ 543/* USB autosuspend and autoresume */
544#ifdef CONFIG_USB_SUSPEND 544#ifdef CONFIG_USB_SUSPEND
545extern int usb_enable_autosuspend(struct usb_device *udev);
546extern int usb_disable_autosuspend(struct usb_device *udev);
547
545extern int usb_autopm_get_interface(struct usb_interface *intf); 548extern int usb_autopm_get_interface(struct usb_interface *intf);
546extern void usb_autopm_put_interface(struct usb_interface *intf); 549extern void usb_autopm_put_interface(struct usb_interface *intf);
547extern int usb_autopm_get_interface_async(struct usb_interface *intf); 550extern int usb_autopm_get_interface_async(struct usb_interface *intf);
@@ -565,6 +568,11 @@ static inline void usb_mark_last_busy(struct usb_device *udev)
565 568
566#else 569#else
567 570
571static inline int usb_enable_autosuspend(struct usb_device *udev)
572{ return 0; }
573static inline int usb_disable_autosuspend(struct usb_device *udev)
574{ return 0; }
575
568static inline int usb_autopm_get_interface(struct usb_interface *intf) 576static inline int usb_autopm_get_interface(struct usb_interface *intf)
569{ return 0; } 577{ return 0; }
570static inline int usb_autopm_get_interface_async(struct usb_interface *intf) 578static inline int usb_autopm_get_interface_async(struct usb_interface *intf)