aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
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{