USB: put claimed interfaces in the "suspended" state

This patch (as1370) fixes a bug in the USB runtime power management
code.  When a driver claims an interface, it doesn't expect to need to
call usb_autopm_get_interface() or usb_autopm_put_interface() for
runtime PM to work.  Runtime PM can be controlled by the driver's
primary interface; the additional interfaces it claims shouldn't
interfere.  As things stand, the claimed interfaces will prevent the
device from autosuspending.

To fix this problem, the patch sets interfaces to the suspended state
when they are claimed.

Also, although in theory this shouldn't matter, the patch changes the
suspend code so that interfaces are suspended in reverse order from
detection and resuming.  This is how the PM core works, and we ought
to use the same approach.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Debugged-and-tested-by: Dominik Brodowski <linux@dominikbrodowski.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Alan Stern 2010-04-09 16:03:43 -04:00 committed by Greg Kroah-Hartman
parent 0e5f231bc1
commit 571dc79d62

View File

@ -301,7 +301,7 @@ static int usb_probe_interface(struct device *dev)
intf->condition = USB_INTERFACE_BINDING;
/* Bound interfaces are initially active. They are
/* Probed interfaces are initially active. They are
* runtime-PM-enabled only if the driver has autosuspend support.
* They are sensitive to their children's power states.
*/
@ -437,11 +437,11 @@ int usb_driver_claim_interface(struct usb_driver *driver,
iface->condition = USB_INTERFACE_BOUND;
/* Bound interfaces are initially active. They are
/* Claimed interfaces are initially inactive (suspended). They are
* runtime-PM-enabled only if the driver has autosuspend support.
* They are sensitive to their children's power states.
*/
pm_runtime_set_active(dev);
pm_runtime_set_suspended(dev);
pm_suspend_ignore_children(dev, false);
if (driver->supports_autosuspend)
pm_runtime_enable(dev);
@ -1170,7 +1170,7 @@ done:
static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
{
int status = 0;
int i = 0;
int i = 0, n = 0;
struct usb_interface *intf;
if (udev->state == USB_STATE_NOTATTACHED ||
@ -1179,7 +1179,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
/* Suspend all the interfaces and then udev itself */
if (udev->actconfig) {
for (; i < udev->actconfig->desc.bNumInterfaces; i++) {
n = udev->actconfig->desc.bNumInterfaces;
for (i = n - 1; i >= 0; --i) {
intf = udev->actconfig->interface[i];
status = usb_suspend_interface(udev, intf, msg);
if (status != 0)
@ -1192,7 +1193,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
/* If the suspend failed, resume interfaces that did get suspended */
if (status != 0) {
msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
while (--i >= 0) {
while (++i < n) {
intf = udev->actconfig->interface[i];
usb_resume_interface(udev, intf, msg, 0);
}