Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: Defer Set-Interface for suspended devices

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>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
55151d7d 65605ae8

+31 -3
+28 -3
drivers/usb/core/driver.c
··· 230 230 */ 231 231 intf->pm_usage_cnt = !(driver->supports_autosuspend); 232 232 233 + /* Carry out a deferred switch to altsetting 0 */ 234 + if (intf->needs_altsetting0) { 235 + usb_set_interface(udev, intf->altsetting[0]. 236 + desc.bInterfaceNumber, 0); 237 + intf->needs_altsetting0 = 0; 238 + } 239 + 233 240 error = driver->probe(intf, id); 234 241 if (error) { 235 242 mark_quiesced(intf); ··· 273 266 274 267 driver->disconnect(intf); 275 268 276 - /* reset other interface state */ 277 - usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); 269 + /* Reset other interface state. 270 + * We cannot do a Set-Interface if the device is suspended or 271 + * if it is prepared for a system sleep (since installing a new 272 + * altsetting means creating new endpoint device entries). 273 + * When either of these happens, defer the Set-Interface. 274 + */ 275 + if (!error && intf->dev.power.status == DPM_ON) 276 + usb_set_interface(udev, intf->altsetting[0]. 277 + desc.bInterfaceNumber, 0); 278 + else 279 + intf->needs_altsetting0 = 1; 278 280 usb_set_intfdata(intf, NULL); 279 281 280 282 intf->condition = USB_INTERFACE_UNBOUND; ··· 991 975 goto done; 992 976 993 977 /* Can't resume it if it doesn't have a driver. */ 994 - if (intf->condition == USB_INTERFACE_UNBOUND) 978 + if (intf->condition == USB_INTERFACE_UNBOUND) { 979 + 980 + /* Carry out a deferred switch to altsetting 0 */ 981 + if (intf->needs_altsetting0 && 982 + intf->dev.power.status == DPM_ON) { 983 + usb_set_interface(udev, intf->altsetting[0]. 984 + desc.bInterfaceNumber, 0); 985 + intf->needs_altsetting0 = 0; 986 + } 995 987 goto done; 988 + } 996 989 997 990 /* Don't resume if the interface is marked for rebinding */ 998 991 if (intf->needs_binding)
+3
include/linux/usb.h
··· 110 110 * @sysfs_files_created: sysfs attributes exist 111 111 * @needs_remote_wakeup: flag set when the driver requires remote-wakeup 112 112 * capability during autosuspend. 113 + * @needs_altsetting0: flag set when a set-interface request for altsetting 0 114 + * has been deferred. 113 115 * @needs_binding: flag set when the driver should be re-probed or unbound 114 116 * following a reset or suspend operation it doesn't support. 115 117 * @dev: driver model's view of this device ··· 164 162 unsigned is_active:1; /* the interface is not suspended */ 165 163 unsigned sysfs_files_created:1; /* the sysfs attributes exist */ 166 164 unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ 165 + unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */ 167 166 unsigned needs_binding:1; /* needs delayed unbind/rebind */ 168 167 169 168 struct device dev; /* interface specific device info */