diff options
Diffstat (limited to 'drivers/usb/core/driver.c')
-rw-r--r-- | drivers/usb/core/driver.c | 82 |
1 files changed, 59 insertions, 23 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 8c0a7de..abea48d 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) #endif /* CONFIG_USB_SUSPEND */ -static int usb_suspend(struct device *dev, pm_message_t message) +/** + * usb_external_suspend_device - external suspend of a USB device and its interfaces + * @udev: the usb_device to suspend + * @msg: Power Management message describing this state transition + * + * This routine handles external suspend requests: ones not generated + * internally by a USB driver (autosuspend) but rather coming from the user + * (via sysfs) or the PM core (system sleep). The suspend will be carried + * out regardless of @udev's usage counter or those of its interfaces, + * and regardless of whether or not remote wakeup is enabled. Of course, + * interface drivers still have the option of failing the suspend (if + * there are unsuspended children, for example). + * + * The caller must hold @udev's device lock. + */ +int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) { int status; - if (is_usb_device(dev)) { - struct usb_device *udev = to_usb_device(dev); - - usb_pm_lock(udev); - udev->auto_pm = 0; - status = usb_suspend_both(udev, message); - usb_pm_unlock(udev); - } else - status = 0; + usb_pm_lock(udev); + udev->auto_pm = 0; + status = usb_suspend_both(udev, msg); + usb_pm_unlock(udev); return status; } -static int usb_resume(struct device *dev) +/** + * usb_external_resume_device - external resume of a USB device and its interfaces + * @udev: the usb_device to resume + * + * This routine handles external resume requests: ones not generated + * internally by a USB driver (autoresume) but rather coming from the user + * (via sysfs), the PM core (system resume), or the device itself (remote + * wakeup). @udev's usage counter is unaffected. + * + * The caller must hold @udev's device lock. + */ +int usb_external_resume_device(struct usb_device *udev) { int status; - if (is_usb_device(dev)) { - struct usb_device *udev = to_usb_device(dev); - - usb_pm_lock(udev); - udev->auto_pm = 0; - status = usb_resume_both(udev); - usb_pm_unlock(udev); + usb_pm_lock(udev); + udev->auto_pm = 0; + status = usb_resume_both(udev); + usb_pm_unlock(udev); - /* Rebind drivers that had no suspend method? */ - } else - status = 0; + /* Now that the device is awake, we can start trying to autosuspend + * it again. */ + if (status == 0) + usb_try_autosuspend_device(udev); return status; } +static int usb_suspend(struct device *dev, pm_message_t message) +{ + if (!is_usb_device(dev)) /* Ignore PM for interfaces */ + return 0; + return usb_external_suspend_device(to_usb_device(dev), message); +} + +static int usb_resume(struct device *dev) +{ + if (!is_usb_device(dev)) /* Ignore PM for interfaces */ + return 0; + return usb_external_resume_device(to_usb_device(dev)); +} + +#else + +#define usb_suspend NULL +#define usb_resume NULL + #endif /* CONFIG_PM */ struct bus_type usb_bus_type = { .name = "usb", .match = usb_device_match, .uevent = usb_uevent, -#ifdef CONFIG_PM .suspend = usb_suspend, .resume = usb_resume, -#endif }; |