aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2008-11-12 16:19:49 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-01-07 12:59:53 -0500
commit9ac39f28b5237a629e41ccfc1f73d3a55723045c (patch)
treec161d5b62d11b6e73605a37b2562b90fff689d9e /drivers/usb/core
parentd4f373e57d3916814110968c5ea1155a8d972b5a (diff)
USB: add asynchronous autosuspend/autoresume support
This patch (as1160b) adds support routines for asynchronous autosuspend and autoresume, with accompanying documentation updates. There already are several potential users of this interface, and others are likely to arise as autosuspend support becomes more widespread. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/driver.c86
-rw-r--r--drivers/usb/core/hub.c5
-rw-r--r--drivers/usb/core/usb.c1
-rw-r--r--drivers/usb/core/usb.h1
4 files changed, 91 insertions, 2 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 8c081308b0e..23b3c7e79d4 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1341,6 +1341,19 @@ void usb_autosuspend_work(struct work_struct *work)
1341 usb_autopm_do_device(udev, 0); 1341 usb_autopm_do_device(udev, 0);
1342} 1342}
1343 1343
1344/* usb_autoresume_work - callback routine to autoresume a USB device */
1345void usb_autoresume_work(struct work_struct *work)
1346{
1347 struct usb_device *udev =
1348 container_of(work, struct usb_device, autoresume);
1349
1350 /* Wake it up, let the drivers do their thing, and then put it
1351 * back to sleep.
1352 */
1353 if (usb_autopm_do_device(udev, 1) == 0)
1354 usb_autopm_do_device(udev, -1);
1355}
1356
1344/** 1357/**
1345 * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces 1358 * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
1346 * @udev: the usb_device to autosuspend 1359 * @udev: the usb_device to autosuspend
@@ -1492,6 +1505,45 @@ void usb_autopm_put_interface(struct usb_interface *intf)
1492EXPORT_SYMBOL_GPL(usb_autopm_put_interface); 1505EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
1493 1506
1494/** 1507/**
1508 * usb_autopm_put_interface_async - decrement a USB interface's PM-usage counter
1509 * @intf: the usb_interface whose counter should be decremented
1510 *
1511 * This routine does essentially the same thing as
1512 * usb_autopm_put_interface(): it decrements @intf's usage counter and
1513 * queues a delayed autosuspend request if the counter is <= 0. The
1514 * difference is that it does not acquire the device's pm_mutex;
1515 * callers must handle all synchronization issues themselves.
1516 *
1517 * Typically a driver would call this routine during an URB's completion
1518 * handler, if no more URBs were pending.
1519 *
1520 * This routine can run in atomic context.
1521 */
1522void usb_autopm_put_interface_async(struct usb_interface *intf)
1523{
1524 struct usb_device *udev = interface_to_usbdev(intf);
1525 int status = 0;
1526
1527 if (intf->condition == USB_INTERFACE_UNBOUND) {
1528 status = -ENODEV;
1529 } else {
1530 udev->last_busy = jiffies;
1531 --intf->pm_usage_cnt;
1532 if (udev->autosuspend_disabled || udev->autosuspend_delay < 0)
1533 status = -EPERM;
1534 else if (intf->pm_usage_cnt <= 0 &&
1535 !timer_pending(&udev->autosuspend.timer)) {
1536 queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
1537 round_jiffies_relative(
1538 udev->autosuspend_delay));
1539 }
1540 }
1541 dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
1542 __func__, status, intf->pm_usage_cnt);
1543}
1544EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async);
1545
1546/**
1495 * usb_autopm_get_interface - increment a USB interface's PM-usage counter 1547 * usb_autopm_get_interface - increment a USB interface's PM-usage counter
1496 * @intf: the usb_interface whose counter should be incremented 1548 * @intf: the usb_interface whose counter should be incremented
1497 * 1549 *
@@ -1537,6 +1589,37 @@ int usb_autopm_get_interface(struct usb_interface *intf)
1537EXPORT_SYMBOL_GPL(usb_autopm_get_interface); 1589EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
1538 1590
1539/** 1591/**
1592 * usb_autopm_get_interface_async - increment a USB interface's PM-usage counter
1593 * @intf: the usb_interface whose counter should be incremented
1594 *
1595 * This routine does much the same thing as
1596 * usb_autopm_get_interface(): it increments @intf's usage counter and
1597 * queues an autoresume request if the result is > 0. The differences
1598 * are that it does not acquire the device's pm_mutex (callers must
1599 * handle all synchronization issues themselves), and it does not
1600 * autoresume the device directly (it only queues a request). After a
1601 * successful call, the device will generally not yet be resumed.
1602 *
1603 * This routine can run in atomic context.
1604 */
1605int usb_autopm_get_interface_async(struct usb_interface *intf)
1606{
1607 struct usb_device *udev = interface_to_usbdev(intf);
1608 int status = 0;
1609
1610 if (intf->condition == USB_INTERFACE_UNBOUND)
1611 status = -ENODEV;
1612 else if (udev->autoresume_disabled)
1613 status = -EPERM;
1614 else if (++intf->pm_usage_cnt > 0 && udev->state == USB_STATE_SUSPENDED)
1615 queue_work(ksuspend_usb_wq, &udev->autoresume);
1616 dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
1617 __func__, status, intf->pm_usage_cnt);
1618 return status;
1619}
1620EXPORT_SYMBOL_GPL(usb_autopm_get_interface_async);
1621
1622/**
1540 * usb_autopm_set_interface - set a USB interface's autosuspend state 1623 * usb_autopm_set_interface - set a USB interface's autosuspend state
1541 * @intf: the usb_interface whose state should be set 1624 * @intf: the usb_interface whose state should be set
1542 * 1625 *
@@ -1563,6 +1646,9 @@ EXPORT_SYMBOL_GPL(usb_autopm_set_interface);
1563void usb_autosuspend_work(struct work_struct *work) 1646void usb_autosuspend_work(struct work_struct *work)
1564{} 1647{}
1565 1648
1649void usb_autoresume_work(struct work_struct *work)
1650{}
1651
1566#endif /* CONFIG_USB_SUSPEND */ 1652#endif /* CONFIG_USB_SUSPEND */
1567 1653
1568/** 1654/**
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b19cbfcd51d..95fb3104ba4 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1374,8 +1374,9 @@ static void usb_stop_pm(struct usb_device *udev)
1374 usb_autosuspend_device(udev->parent); 1374 usb_autosuspend_device(udev->parent);
1375 usb_pm_unlock(udev); 1375 usb_pm_unlock(udev);
1376 1376
1377 /* Stop any autosuspend requests already submitted */ 1377 /* Stop any autosuspend or autoresume requests already submitted */
1378 cancel_rearming_delayed_work(&udev->autosuspend); 1378 cancel_delayed_work_sync(&udev->autosuspend);
1379 cancel_work_sync(&udev->autoresume);
1379} 1380}
1380 1381
1381#else 1382#else
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 400fa4cc9a3..44f2fc750b6 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -402,6 +402,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
402#ifdef CONFIG_PM 402#ifdef CONFIG_PM
403 mutex_init(&dev->pm_mutex); 403 mutex_init(&dev->pm_mutex);
404 INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work); 404 INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
405 INIT_WORK(&dev->autoresume, usb_autoresume_work);
405 dev->autosuspend_delay = usb_autosuspend_delay * HZ; 406 dev->autosuspend_delay = usb_autosuspend_delay * HZ;
406 dev->connect_time = jiffies; 407 dev->connect_time = jiffies;
407 dev->active_duration = -jiffies; 408 dev->active_duration = -jiffies;
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 9a1a45ac3ad..b60ebb4de1a 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -45,6 +45,7 @@ extern int usb_suspend(struct device *dev, pm_message_t msg);
45extern int usb_resume(struct device *dev); 45extern int usb_resume(struct device *dev);
46 46
47extern void usb_autosuspend_work(struct work_struct *work); 47extern void usb_autosuspend_work(struct work_struct *work);
48extern void usb_autoresume_work(struct work_struct *work);
48extern int usb_port_suspend(struct usb_device *dev); 49extern int usb_port_suspend(struct usb_device *dev);
49extern int usb_port_resume(struct usb_device *dev); 50extern int usb_port_resume(struct usb_device *dev);
50extern int usb_external_suspend_device(struct usb_device *udev, 51extern int usb_external_suspend_device(struct usb_device *udev,