Skip to content

Commit

Permalink
USB: Defer Set-Interface for suspended devices
Browse files Browse the repository at this point in the history
This patch (as1128) fixes one of the problems related to the new PM
infrastructure.  We are not allowed to register new child devices
during the middle of a system sleep transition, but unbinding a USB
driver causes the core to automatically install altsetting 0 and
thereby create new endpoint pseudo-devices.

The patch fixes this problem (and the related problem that installing
altsetting 0 will fail if the device is suspended) by deferring the
Set-Interface call until some later time when it is legal and can
succeed.  Possible later times are: when a new driver is being probed
for the interface, and when the interface is being resumed.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Aug 21, 2008
1 parent 65605ae commit 55151d7
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 3 deletions.
31 changes: 28 additions & 3 deletions drivers/usb/core/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,13 @@ static int usb_probe_interface(struct device *dev)
*/
intf->pm_usage_cnt = !(driver->supports_autosuspend);

/* Carry out a deferred switch to altsetting 0 */
if (intf->needs_altsetting0) {
usb_set_interface(udev, intf->altsetting[0].
desc.bInterfaceNumber, 0);
intf->needs_altsetting0 = 0;
}

error = driver->probe(intf, id);
if (error) {
mark_quiesced(intf);
Expand Down Expand Up @@ -266,8 +273,17 @@ static int usb_unbind_interface(struct device *dev)

driver->disconnect(intf);

/* reset other interface state */
usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0);
/* Reset other interface state.
* We cannot do a Set-Interface if the device is suspended or
* if it is prepared for a system sleep (since installing a new
* altsetting means creating new endpoint device entries).
* When either of these happens, defer the Set-Interface.
*/
if (!error && intf->dev.power.status == DPM_ON)
usb_set_interface(udev, intf->altsetting[0].
desc.bInterfaceNumber, 0);
else
intf->needs_altsetting0 = 1;
usb_set_intfdata(intf, NULL);

intf->condition = USB_INTERFACE_UNBOUND;
Expand Down Expand Up @@ -975,8 +991,17 @@ static int usb_resume_interface(struct usb_device *udev,
goto done;

/* Can't resume it if it doesn't have a driver. */
if (intf->condition == USB_INTERFACE_UNBOUND)
if (intf->condition == USB_INTERFACE_UNBOUND) {

/* Carry out a deferred switch to altsetting 0 */
if (intf->needs_altsetting0 &&
intf->dev.power.status == DPM_ON) {
usb_set_interface(udev, intf->altsetting[0].
desc.bInterfaceNumber, 0);
intf->needs_altsetting0 = 0;
}
goto done;
}

/* Don't resume if the interface is marked for rebinding */
if (intf->needs_binding)
Expand Down
3 changes: 3 additions & 0 deletions include/linux/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ enum usb_interface_condition {
* @sysfs_files_created: sysfs attributes exist
* @needs_remote_wakeup: flag set when the driver requires remote-wakeup
* capability during autosuspend.
* @needs_altsetting0: flag set when a set-interface request for altsetting 0
* has been deferred.
* @needs_binding: flag set when the driver should be re-probed or unbound
* following a reset or suspend operation it doesn't support.
* @dev: driver model's view of this device
Expand Down Expand Up @@ -162,6 +164,7 @@ struct usb_interface {
unsigned is_active:1; /* the interface is not suspended */
unsigned sysfs_files_created:1; /* the sysfs attributes exist */
unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */
unsigned needs_binding:1; /* needs delayed unbind/rebind */

struct device dev; /* interface specific device info */
Expand Down

0 comments on commit 55151d7

Please sign in to comment.