diff options
-rw-r--r-- | drivers/usb/core/driver.c | 31 | ||||
-rw-r--r-- | include/linux/usb.h | 3 |
2 files changed, 31 insertions, 3 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index ed1cc8530a93..637b2bea5563 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -230,6 +230,13 @@ static int usb_probe_interface(struct device *dev) | |||
230 | */ | 230 | */ |
231 | intf->pm_usage_cnt = !(driver->supports_autosuspend); | 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 | error = driver->probe(intf, id); | 240 | error = driver->probe(intf, id); |
234 | if (error) { | 241 | if (error) { |
235 | mark_quiesced(intf); | 242 | mark_quiesced(intf); |
@@ -266,8 +273,17 @@ static int usb_unbind_interface(struct device *dev) | |||
266 | 273 | ||
267 | driver->disconnect(intf); | 274 | driver->disconnect(intf); |
268 | 275 | ||
269 | /* reset other interface state */ | 276 | /* Reset other interface state. |
270 | usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); | 277 | * We cannot do a Set-Interface if the device is suspended or |
278 | * if it is prepared for a system sleep (since installing a new | ||
279 | * altsetting means creating new endpoint device entries). | ||
280 | * When either of these happens, defer the Set-Interface. | ||
281 | */ | ||
282 | if (!error && intf->dev.power.status == DPM_ON) | ||
283 | usb_set_interface(udev, intf->altsetting[0]. | ||
284 | desc.bInterfaceNumber, 0); | ||
285 | else | ||
286 | intf->needs_altsetting0 = 1; | ||
271 | usb_set_intfdata(intf, NULL); | 287 | usb_set_intfdata(intf, NULL); |
272 | 288 | ||
273 | intf->condition = USB_INTERFACE_UNBOUND; | 289 | intf->condition = USB_INTERFACE_UNBOUND; |
@@ -975,8 +991,17 @@ static int usb_resume_interface(struct usb_device *udev, | |||
975 | goto done; | 991 | goto done; |
976 | 992 | ||
977 | /* Can't resume it if it doesn't have a driver. */ | 993 | /* Can't resume it if it doesn't have a driver. */ |
978 | if (intf->condition == USB_INTERFACE_UNBOUND) | 994 | if (intf->condition == USB_INTERFACE_UNBOUND) { |
995 | |||
996 | /* Carry out a deferred switch to altsetting 0 */ | ||
997 | if (intf->needs_altsetting0 && | ||
998 | intf->dev.power.status == DPM_ON) { | ||
999 | usb_set_interface(udev, intf->altsetting[0]. | ||
1000 | desc.bInterfaceNumber, 0); | ||
1001 | intf->needs_altsetting0 = 0; | ||
1002 | } | ||
979 | goto done; | 1003 | goto done; |
1004 | } | ||
980 | 1005 | ||
981 | /* Don't resume if the interface is marked for rebinding */ | 1006 | /* Don't resume if the interface is marked for rebinding */ |
982 | if (intf->needs_binding) | 1007 | if (intf->needs_binding) |
diff --git a/include/linux/usb.h b/include/linux/usb.h index 0924cd9c30f6..94ac74aba6b6 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -110,6 +110,8 @@ enum usb_interface_condition { | |||
110 | * @sysfs_files_created: sysfs attributes exist | 110 | * @sysfs_files_created: sysfs attributes exist |
111 | * @needs_remote_wakeup: flag set when the driver requires remote-wakeup | 111 | * @needs_remote_wakeup: flag set when the driver requires remote-wakeup |
112 | * capability during autosuspend. | 112 | * capability during autosuspend. |
113 | * @needs_altsetting0: flag set when a set-interface request for altsetting 0 | ||
114 | * has been deferred. | ||
113 | * @needs_binding: flag set when the driver should be re-probed or unbound | 115 | * @needs_binding: flag set when the driver should be re-probed or unbound |
114 | * following a reset or suspend operation it doesn't support. | 116 | * following a reset or suspend operation it doesn't support. |
115 | * @dev: driver model's view of this device | 117 | * @dev: driver model's view of this device |
@@ -162,6 +164,7 @@ struct usb_interface { | |||
162 | unsigned is_active:1; /* the interface is not suspended */ | 164 | unsigned is_active:1; /* the interface is not suspended */ |
163 | unsigned sysfs_files_created:1; /* the sysfs attributes exist */ | 165 | unsigned sysfs_files_created:1; /* the sysfs attributes exist */ |
164 | unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ | 166 | unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ |
167 | unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */ | ||
165 | unsigned needs_binding:1; /* needs delayed unbind/rebind */ | 168 | unsigned needs_binding:1; /* needs delayed unbind/rebind */ |
166 | 169 | ||
167 | struct device dev; /* interface specific device info */ | 170 | struct device dev; /* interface specific device info */ |