Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 088f7fec authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

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: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 0c4db6df
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -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
+42 −0
Original line number Diff line number Diff line
@@ -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.
 */
+3 −0
Original line number Diff line number Diff line
@@ -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");
+4 −5
Original line number Diff line number Diff line
@@ -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 */
+7 −16
Original line number Diff line number Diff line
@@ -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);
}
Loading