USB: implement usb_enable_autosuspend
This patch (as1326) adds usb_enable_autosuspend() and usb_disable_autosuspend() routines for use by drivers. If a driver knows that its device can handle suspends and resumes correctly, it can enable autosuspend all by itself. This is equivalent to the user writing "auto" to the device's power/level attribute. The implementation differs slightly from what it used to be. Now autosuspend is disabled simply by doing usb_autoresume_device() (to increment the usage counter) and enabled by doing usb_autosuspend_device() (to decrement the usage counter). The set_level() attribute method is updated to use the new routines, and the USB Power-Management documentation is updated. The patch adds a usb_enable_autosuspend() call to the hub driver's probe routine, allowing the special-case code for hubs in quirks.c to be removed. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Родитель
0c4db6df91
Коммит
088f7fec8a
|
@ -229,6 +229,11 @@ necessary operations by hand or add them to a udev script. You can
|
|||
also change the idle-delay time; 2 seconds is not the best choice for
|
||||
every device.
|
||||
|
||||
If a driver knows that its device has proper suspend/resume support,
|
||||
it can enable autosuspend all by itself. For example, the video
|
||||
driver for a laptop's webcam might do this, since these devices are
|
||||
rarely used and so should normally be autosuspended.
|
||||
|
||||
Sometimes it turns out that even when a device does work okay with
|
||||
autosuspend there are still problems. For example, there are
|
||||
experimental patches adding autosuspend support to the usbhid driver,
|
||||
|
@ -384,6 +389,19 @@ autosuspend, there's no delay for an autoresume.
|
|||
Other parts of the driver interface
|
||||
-----------------------------------
|
||||
|
||||
Drivers can enable autosuspend for their devices by calling
|
||||
|
||||
usb_enable_autosuspend(struct usb_device *udev);
|
||||
|
||||
in their probe() routine, if they know that the device is capable of
|
||||
suspending and resuming correctly. This is exactly equivalent to
|
||||
writing "auto" to the device's power/level attribute. Likewise,
|
||||
drivers can disable autosuspend by calling
|
||||
|
||||
usb_disable_autosuspend(struct usb_device *udev);
|
||||
|
||||
This is exactly the same as writing "on" to the power/level attribute.
|
||||
|
||||
Sometimes a driver needs to make sure that remote wakeup is enabled
|
||||
during autosuspend. For example, there's not much point
|
||||
autosuspending a keyboard if the user can't cause the keyboard to do a
|
||||
|
|
|
@ -1415,6 +1415,48 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
|
|||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
/**
|
||||
* usb_enable_autosuspend - allow a USB device to be autosuspended
|
||||
* @udev: the USB device which may be autosuspended
|
||||
*
|
||||
* This routine allows @udev to be autosuspended. An autosuspend won't
|
||||
* take place until the autosuspend_delay has elapsed and all the other
|
||||
* necessary conditions are satisfied.
|
||||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*/
|
||||
int usb_enable_autosuspend(struct usb_device *udev)
|
||||
{
|
||||
if (udev->autosuspend_disabled) {
|
||||
udev->autosuspend_disabled = 0;
|
||||
usb_autosuspend_device(udev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_enable_autosuspend);
|
||||
|
||||
/**
|
||||
* usb_disable_autosuspend - prevent a USB device from being autosuspended
|
||||
* @udev: the USB device which may not be autosuspended
|
||||
*
|
||||
* This routine prevents @udev from being autosuspended and wakes it up
|
||||
* if it is already autosuspended.
|
||||
*
|
||||
* The caller must hold @udev's device lock.
|
||||
*/
|
||||
int usb_disable_autosuspend(struct usb_device *udev)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!udev->autosuspend_disabled) {
|
||||
rc = usb_autoresume_device(udev);
|
||||
if (rc == 0)
|
||||
udev->autosuspend_disabled = 1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_disable_autosuspend);
|
||||
|
||||
/* Internal routine to adjust a device's usage counter and change
|
||||
* its autosuspend state.
|
||||
*/
|
||||
|
|
|
@ -1224,6 +1224,9 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|||
desc = intf->cur_altsetting;
|
||||
hdev = interface_to_usbdev(intf);
|
||||
|
||||
/* Hubs have proper suspend/resume support */
|
||||
usb_enable_autosuspend(hdev);
|
||||
|
||||
if (hdev->level == MAX_TOPO_LEVEL) {
|
||||
dev_err(&intf->dev,
|
||||
"Unsupported bus topology: hub nested too deep\n");
|
||||
|
|
|
@ -103,11 +103,10 @@ void usb_detect_quirks(struct usb_device *udev)
|
|||
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
|
||||
udev->quirks);
|
||||
|
||||
/* By default, disable autosuspend for all non-hubs */
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
if (udev->descriptor.bDeviceClass != USB_CLASS_HUB)
|
||||
udev->autosuspend_disabled = 1;
|
||||
#endif
|
||||
/* By default, disable autosuspend for all devices. The hub driver
|
||||
* will enable it for hubs.
|
||||
*/
|
||||
usb_disable_autosuspend(udev);
|
||||
|
||||
/* For the present, all devices default to USB-PERSIST enabled */
|
||||
#if 0 /* was: #ifdef CONFIG_PM */
|
||||
|
|
|
@ -389,34 +389,25 @@ set_level(struct device *dev, struct device_attribute *attr,
|
|||
struct usb_device *udev = to_usb_device(dev);
|
||||
int len = count;
|
||||
char *cp;
|
||||
int rc = 0;
|
||||
int old_autosuspend_disabled;
|
||||
int rc;
|
||||
|
||||
cp = memchr(buf, '\n', count);
|
||||
if (cp)
|
||||
len = cp - buf;
|
||||
|
||||
usb_lock_device(udev);
|
||||
old_autosuspend_disabled = udev->autosuspend_disabled;
|
||||
|
||||
/* Setting the flags without calling usb_pm_lock is a subject to
|
||||
* races, but who cares...
|
||||
*/
|
||||
if (len == sizeof on_string - 1 &&
|
||||
strncmp(buf, on_string, len) == 0) {
|
||||
udev->autosuspend_disabled = 1;
|
||||
rc = usb_external_resume_device(udev, PMSG_USER_RESUME);
|
||||
strncmp(buf, on_string, len) == 0)
|
||||
rc = usb_disable_autosuspend(udev);
|
||||
|
||||
} else if (len == sizeof auto_string - 1 &&
|
||||
strncmp(buf, auto_string, len) == 0) {
|
||||
udev->autosuspend_disabled = 0;
|
||||
rc = usb_external_resume_device(udev, PMSG_USER_RESUME);
|
||||
else if (len == sizeof auto_string - 1 &&
|
||||
strncmp(buf, auto_string, len) == 0)
|
||||
rc = usb_enable_autosuspend(udev);
|
||||
|
||||
} else
|
||||
else
|
||||
rc = -EINVAL;
|
||||
|
||||
if (rc)
|
||||
udev->autosuspend_disabled = old_autosuspend_disabled;
|
||||
usb_unlock_device(udev);
|
||||
return (rc < 0 ? rc : count);
|
||||
}
|
||||
|
|
|
@ -542,6 +542,9 @@ extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);
|
|||
|
||||
/* USB autosuspend and autoresume */
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
extern int usb_enable_autosuspend(struct usb_device *udev);
|
||||
extern int usb_disable_autosuspend(struct usb_device *udev);
|
||||
|
||||
extern int usb_autopm_get_interface(struct usb_interface *intf);
|
||||
extern void usb_autopm_put_interface(struct usb_interface *intf);
|
||||
extern int usb_autopm_get_interface_async(struct usb_interface *intf);
|
||||
|
@ -565,6 +568,11 @@ static inline void usb_mark_last_busy(struct usb_device *udev)
|
|||
|
||||
#else
|
||||
|
||||
static inline int usb_enable_autosuspend(struct usb_device *udev)
|
||||
{ return 0; }
|
||||
static inline int usb_disable_autosuspend(struct usb_device *udev)
|
||||
{ return 0; }
|
||||
|
||||
static inline int usb_autopm_get_interface(struct usb_interface *intf)
|
||||
{ return 0; }
|
||||
static inline int usb_autopm_get_interface_async(struct usb_interface *intf)
|
||||
|
|
Загрузка…
Ссылка в новой задаче