diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/core/driver.c | 82 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 25 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 3 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 14 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 4 |
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 | ||
1427 | static 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 | */ | ||
1442 | int 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 | ||
1443 | static 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 | */ | ||
1464 | int 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 | ||
1480 | static 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 | |||
1487 | static 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 | ||
1463 | struct bus_type usb_bus_type = { | 1501 | struct 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 */ | ||
1303 | static 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 | */ |
1310 | void usb_hcd_resume_root_hub (struct usb_hcd *hcd) | 1322 | void 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 | } |
1319 | EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); | 1331 | EXPORT_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 | ||
1987 | void 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 | ||
50 | static int nousb; /* Disable USB when built into kernel image */ | 50 | static int nousb; /* Disable USB when built into kernel image */ |
51 | 51 | ||
52 | struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */ | 52 | /* Workqueue for autosuspend and for remote wakeup of root hubs */ |
53 | struct workqueue_struct *ksuspend_usb_wq; | ||
53 | 54 | ||
54 | #ifdef CONFIG_USB_SUSPEND | 55 | #ifdef CONFIG_USB_SUSPEND |
55 | static int usb_autosuspend_delay = 2; /* Default delay value, | 56 | static 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); | |||
21 | extern int usb_set_configuration(struct usb_device *dev, int configuration); | 21 | extern int usb_set_configuration(struct usb_device *dev, int configuration); |
22 | 22 | ||
23 | extern void usb_kick_khubd(struct usb_device *dev); | 23 | extern void usb_kick_khubd(struct usb_device *dev); |
24 | extern void usb_resume_root_hub(struct usb_device *dev); | ||
25 | extern int usb_match_device(struct usb_device *dev, | 24 | extern 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); | |||
37 | extern void usb_autosuspend_work(struct work_struct *work); | 36 | extern void usb_autosuspend_work(struct work_struct *work); |
38 | extern int usb_port_suspend(struct usb_device *dev); | 37 | extern int usb_port_suspend(struct usb_device *dev); |
39 | extern int usb_port_resume(struct usb_device *dev); | 38 | extern int usb_port_resume(struct usb_device *dev); |
39 | extern int usb_external_suspend_device(struct usb_device *udev, | ||
40 | pm_message_t msg); | ||
41 | extern int usb_external_resume_device(struct usb_device *udev); | ||
40 | 42 | ||
41 | static inline void usb_pm_lock(struct usb_device *udev) | 43 | static inline void usb_pm_lock(struct usb_device *udev) |
42 | { | 44 | { |