Commit 571dc79d authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

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: default avatarAlan Stern <stern@rowland.harvard.edu>
Debugged-and-tested-by: default avatarDominik Brodowski <linux@dominikbrodowski.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 0e5f231b
...@@ -301,7 +301,7 @@ static int usb_probe_interface(struct device *dev) ...@@ -301,7 +301,7 @@ static int usb_probe_interface(struct device *dev)
intf->condition = USB_INTERFACE_BINDING; 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. * runtime-PM-enabled only if the driver has autosuspend support.
* They are sensitive to their children's power states. * They are sensitive to their children's power states.
*/ */
...@@ -437,11 +437,11 @@ int usb_driver_claim_interface(struct usb_driver *driver, ...@@ -437,11 +437,11 @@ int usb_driver_claim_interface(struct usb_driver *driver,
iface->condition = USB_INTERFACE_BOUND; 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. * runtime-PM-enabled only if the driver has autosuspend support.
* They are sensitive to their children's power states. * 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); pm_suspend_ignore_children(dev, false);
if (driver->supports_autosuspend) if (driver->supports_autosuspend)
pm_runtime_enable(dev); pm_runtime_enable(dev);
...@@ -1170,7 +1170,7 @@ done: ...@@ -1170,7 +1170,7 @@ done:
static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
{ {
int status = 0; int status = 0;
int i = 0; int i = 0, n = 0;
struct usb_interface *intf; struct usb_interface *intf;
if (udev->state == USB_STATE_NOTATTACHED || if (udev->state == USB_STATE_NOTATTACHED ||
...@@ -1179,7 +1179,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) ...@@ -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 */ /* Suspend all the interfaces and then udev itself */
if (udev->actconfig) { 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]; intf = udev->actconfig->interface[i];
status = usb_suspend_interface(udev, intf, msg); status = usb_suspend_interface(udev, intf, msg);
if (status != 0) if (status != 0)
...@@ -1192,7 +1193,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) ...@@ -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 the suspend failed, resume interfaces that did get suspended */
if (status != 0) { if (status != 0) {
msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME); msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
while (--i >= 0) { while (++i < n) {
intf = udev->actconfig->interface[i]; intf = udev->actconfig->interface[i];
usb_resume_interface(udev, intf, msg, 0); usb_resume_interface(udev, intf, msg, 0);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment