aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-03-13 16:37:30 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-04-27 16:28:35 -0400
commit6b157c9bf3bace6eeb4a973da63923ef24995cce (patch)
tree7069b603d4a3d4723e8b99a23f97139b100547db /drivers
parent378465396e74aa6ef36271cecaea49eb742025a8 (diff)
USB: separate autosuspend from external suspend
This patch (as866) adds new entry points for external USB device suspend and resume requests, as opposed to internally-generated autosuspend or autoresume. It also changes the existing remote-wakeup code paths to use the new routines, since remote wakeup is not the same as autoresume. As part of the change, it turns out to be necessary to do remote wakeup of root hubs from a workqueue. We had been using khubd, but it does autoresume rather than an external resume. Using the ksuspend_usb_wq workqueue for this purpose seemed a logical choice. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/core/driver.c82
-rw-r--r--drivers/usb/core/hcd.c25
-rw-r--r--drivers/usb/core/hcd.h3
-rw-r--r--drivers/usb/core/hub.c14
-rw-r--r--drivers/usb/core/usb.c3
-rw-r--r--drivers/usb/core/usb.h4
6 files changed, 90 insertions, 41 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 8c0a7de61228..abea48de8766 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1424,48 +1424,84 @@ void usb_autosuspend_work(struct work_struct *work)
1424 1424
1425#endif /* CONFIG_USB_SUSPEND */ 1425#endif /* CONFIG_USB_SUSPEND */
1426 1426
1427static int usb_suspend(struct device *dev, pm_message_t message) 1427/**
1428 * usb_external_suspend_device - external suspend of a USB device and its interfaces
1429 * @udev: the usb_device to suspend
1430 * @msg: Power Management message describing this state transition
1431 *
1432 * This routine handles external suspend requests: ones not generated
1433 * internally by a USB driver (autosuspend) but rather coming from the user
1434 * (via sysfs) or the PM core (system sleep). The suspend will be carried
1435 * out regardless of @udev's usage counter or those of its interfaces,
1436 * and regardless of whether or not remote wakeup is enabled. Of course,
1437 * interface drivers still have the option of failing the suspend (if
1438 * there are unsuspended children, for example).
1439 *
1440 * The caller must hold @udev's device lock.
1441 */
1442int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
1428{ 1443{
1429 int status; 1444 int status;
1430 1445
1431 if (is_usb_device(dev)) { 1446 usb_pm_lock(udev);
1432 struct usb_device *udev = to_usb_device(dev); 1447 udev->auto_pm = 0;
1433 1448 status = usb_suspend_both(udev, msg);
1434 usb_pm_lock(udev); 1449 usb_pm_unlock(udev);
1435 udev->auto_pm = 0;
1436 status = usb_suspend_both(udev, message);
1437 usb_pm_unlock(udev);
1438 } else
1439 status = 0;
1440 return status; 1450 return status;
1441} 1451}
1442 1452
1443static int usb_resume(struct device *dev) 1453/**
1454 * usb_external_resume_device - external resume of a USB device and its interfaces
1455 * @udev: the usb_device to resume
1456 *
1457 * This routine handles external resume requests: ones not generated
1458 * internally by a USB driver (autoresume) but rather coming from the user
1459 * (via sysfs), the PM core (system resume), or the device itself (remote
1460 * wakeup). @udev's usage counter is unaffected.
1461 *
1462 * The caller must hold @udev's device lock.
1463 */
1464int usb_external_resume_device(struct usb_device *udev)
1444{ 1465{
1445 int status; 1466 int status;
1446 1467
1447 if (is_usb_device(dev)) { 1468 usb_pm_lock(udev);
1448 struct usb_device *udev = to_usb_device(dev); 1469 udev->auto_pm = 0;
1449 1470 status = usb_resume_both(udev);
1450 usb_pm_lock(udev); 1471 usb_pm_unlock(udev);
1451 udev->auto_pm = 0;
1452 status = usb_resume_both(udev);
1453 usb_pm_unlock(udev);
1454 1472
1455 /* Rebind drivers that had no suspend method? */ 1473 /* Now that the device is awake, we can start trying to autosuspend
1456 } else 1474 * it again. */
1457 status = 0; 1475 if (status == 0)
1476 usb_try_autosuspend_device(udev);
1458 return status; 1477 return status;
1459} 1478}
1460 1479
1480static int usb_suspend(struct device *dev, pm_message_t message)
1481{
1482 if (!is_usb_device(dev)) /* Ignore PM for interfaces */
1483 return 0;
1484 return usb_external_suspend_device(to_usb_device(dev), message);
1485}
1486
1487static int usb_resume(struct device *dev)
1488{
1489 if (!is_usb_device(dev)) /* Ignore PM for interfaces */
1490 return 0;
1491 return usb_external_resume_device(to_usb_device(dev));
1492}
1493
1494#else
1495
1496#define usb_suspend NULL
1497#define usb_resume NULL
1498
1461#endif /* CONFIG_PM */ 1499#endif /* CONFIG_PM */
1462 1500
1463struct bus_type usb_bus_type = { 1501struct bus_type usb_bus_type = {
1464 .name = "usb", 1502 .name = "usb",
1465 .match = usb_device_match, 1503 .match = usb_device_match,
1466 .uevent = usb_uevent, 1504 .uevent = usb_uevent,
1467#ifdef CONFIG_PM
1468 .suspend = usb_suspend, 1505 .suspend = usb_suspend,
1469 .resume = usb_resume, 1506 .resume = usb_resume,
1470#endif
1471}; 1507};
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index af7aed11398b..8bc3ce6d9666 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -37,6 +37,7 @@
37#include <asm/irq.h> 37#include <asm/irq.h>
38#include <asm/byteorder.h> 38#include <asm/byteorder.h>
39#include <linux/platform_device.h> 39#include <linux/platform_device.h>
40#include <linux/workqueue.h>
40 41
41#include <linux/usb.h> 42#include <linux/usb.h>
42 43
@@ -1298,14 +1299,25 @@ int hcd_bus_resume (struct usb_bus *bus)
1298 return status; 1299 return status;
1299} 1300}
1300 1301
1302/* Workqueue routine for root-hub remote wakeup */
1303static void hcd_resume_work(struct work_struct *work)
1304{
1305 struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);
1306 struct usb_device *udev = hcd->self.root_hub;
1307
1308 usb_lock_device(udev);
1309 usb_external_resume_device(udev);
1310 usb_unlock_device(udev);
1311}
1312
1301/** 1313/**
1302 * usb_hcd_resume_root_hub - called by HCD to resume its root hub 1314 * usb_hcd_resume_root_hub - called by HCD to resume its root hub
1303 * @hcd: host controller for this root hub 1315 * @hcd: host controller for this root hub
1304 * 1316 *
1305 * The USB host controller calls this function when its root hub is 1317 * The USB host controller calls this function when its root hub is
1306 * suspended (with the remote wakeup feature enabled) and a remote 1318 * suspended (with the remote wakeup feature enabled) and a remote
1307 * wakeup request is received. It queues a request for khubd to 1319 * wakeup request is received. The routine submits a workqueue request
1308 * resume the root hub (that is, manage its downstream ports again). 1320 * to resume the root hub (that is, manage its downstream ports again).
1309 */ 1321 */
1310void usb_hcd_resume_root_hub (struct usb_hcd *hcd) 1322void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
1311{ 1323{
@@ -1313,7 +1325,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
1313 1325
1314 spin_lock_irqsave (&hcd_root_hub_lock, flags); 1326 spin_lock_irqsave (&hcd_root_hub_lock, flags);
1315 if (hcd->rh_registered) 1327 if (hcd->rh_registered)
1316 usb_resume_root_hub (hcd->self.root_hub); 1328 queue_work(ksuspend_usb_wq, &hcd->wakeup_work);
1317 spin_unlock_irqrestore (&hcd_root_hub_lock, flags); 1329 spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
1318} 1330}
1319EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); 1331EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
@@ -1502,6 +1514,9 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
1502 init_timer(&hcd->rh_timer); 1514 init_timer(&hcd->rh_timer);
1503 hcd->rh_timer.function = rh_timer_func; 1515 hcd->rh_timer.function = rh_timer_func;
1504 hcd->rh_timer.data = (unsigned long) hcd; 1516 hcd->rh_timer.data = (unsigned long) hcd;
1517#ifdef CONFIG_PM
1518 INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
1519#endif
1505 1520
1506 hcd->driver = driver; 1521 hcd->driver = driver;
1507 hcd->product_desc = (driver->product_desc) ? driver->product_desc : 1522 hcd->product_desc = (driver->product_desc) ? driver->product_desc :
@@ -1668,6 +1683,10 @@ void usb_remove_hcd(struct usb_hcd *hcd)
1668 hcd->rh_registered = 0; 1683 hcd->rh_registered = 0;
1669 spin_unlock_irq (&hcd_root_hub_lock); 1684 spin_unlock_irq (&hcd_root_hub_lock);
1670 1685
1686#ifdef CONFIG_PM
1687 flush_workqueue(ksuspend_usb_wq);
1688#endif
1689
1671 mutex_lock(&usb_bus_list_lock); 1690 mutex_lock(&usb_bus_list_lock);
1672 usb_disconnect(&hcd->self.root_hub); 1691 usb_disconnect(&hcd->self.root_hub);
1673 mutex_unlock(&usb_bus_list_lock); 1692 mutex_unlock(&usb_bus_list_lock);
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 2a269ca20517..ef50fa494e47 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -68,6 +68,9 @@ struct usb_hcd {
68 68
69 struct timer_list rh_timer; /* drives root-hub polling */ 69 struct timer_list rh_timer; /* drives root-hub polling */
70 struct urb *status_urb; /* the current status urb */ 70 struct urb *status_urb; /* the current status urb */
71#ifdef CONFIG_PM
72 struct work_struct wakeup_work; /* for remote wakeup */
73#endif
71 74
72 /* 75 /*
73 * hardware info/state 76 * hardware info/state
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 7a6028599d62..19abe81babd5 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1855,12 +1855,7 @@ static int remote_wakeup(struct usb_device *udev)
1855 usb_lock_device(udev); 1855 usb_lock_device(udev);
1856 if (udev->state == USB_STATE_SUSPENDED) { 1856 if (udev->state == USB_STATE_SUSPENDED) {
1857 dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); 1857 dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
1858 status = usb_autoresume_device(udev); 1858 status = usb_external_resume_device(udev);
1859
1860 /* Give the interface drivers a chance to do something,
1861 * then autosuspend the device again. */
1862 if (status == 0)
1863 usb_autosuspend_device(udev);
1864 } 1859 }
1865 usb_unlock_device(udev); 1860 usb_unlock_device(udev);
1866 return status; 1861 return status;
@@ -1984,13 +1979,6 @@ static inline int remote_wakeup(struct usb_device *udev)
1984#define hub_resume NULL 1979#define hub_resume NULL
1985#endif 1980#endif
1986 1981
1987void usb_resume_root_hub(struct usb_device *hdev)
1988{
1989 struct usb_hub *hub = hdev_to_hub(hdev);
1990
1991 kick_khubd(hub);
1992}
1993
1994 1982
1995/* USB 2.0 spec, 7.1.7.3 / fig 7-29: 1983/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
1996 * 1984 *
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 82338f497860..138252e0a1cf 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -49,7 +49,8 @@ const char *usbcore_name = "usbcore";
49 49
50static int nousb; /* Disable USB when built into kernel image */ 50static int nousb; /* Disable USB when built into kernel image */
51 51
52struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */ 52/* Workqueue for autosuspend and for remote wakeup of root hubs */
53struct workqueue_struct *ksuspend_usb_wq;
53 54
54#ifdef CONFIG_USB_SUSPEND 55#ifdef CONFIG_USB_SUSPEND
55static int usb_autosuspend_delay = 2; /* Default delay value, 56static int usb_autosuspend_delay = 2; /* Default delay value,
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index b98bc0d381c0..c94379e55f2d 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -21,7 +21,6 @@ extern char *usb_cache_string(struct usb_device *udev, int index);
21extern int usb_set_configuration(struct usb_device *dev, int configuration); 21extern int usb_set_configuration(struct usb_device *dev, int configuration);
22 22
23extern void usb_kick_khubd(struct usb_device *dev); 23extern void usb_kick_khubd(struct usb_device *dev);
24extern void usb_resume_root_hub(struct usb_device *dev);
25extern int usb_match_device(struct usb_device *dev, 24extern int usb_match_device(struct usb_device *dev,
26 const struct usb_device_id *id); 25 const struct usb_device_id *id);
27 26
@@ -37,6 +36,9 @@ extern void usb_host_cleanup(void);
37extern void usb_autosuspend_work(struct work_struct *work); 36extern void usb_autosuspend_work(struct work_struct *work);
38extern int usb_port_suspend(struct usb_device *dev); 37extern int usb_port_suspend(struct usb_device *dev);
39extern int usb_port_resume(struct usb_device *dev); 38extern int usb_port_resume(struct usb_device *dev);
39extern int usb_external_suspend_device(struct usb_device *udev,
40 pm_message_t msg);
41extern int usb_external_resume_device(struct usb_device *udev);
40 42
41static inline void usb_pm_lock(struct usb_device *udev) 43static inline void usb_pm_lock(struct usb_device *udev)
42{ 44{