Commit 4fe0387a authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

USB: don't send Set-Interface after reset



This patch (as1221) changes the way usbcore reinitializes a device
following a reset or a reset-resume.  Currently we call
usb_set_interface() for every interface in the active configuration;
this is to put the interface into the same altsetting as before the
reset and to make sure that the host's endpoint state matches the
device's endpoint state.

However, sending a Set-Interface request is a waste of time if an
interface was already in altsetting 0 before the reset, since it is
certainly in altsetting 0 afterward.  In addition, many devices can't
handle Set-Interface requests -- they crash when they receive them.

So instead, the patch adds code to check each interface.  If the
interface wasn't in altsetting 0 before the reset, we go head with the
Set-Interface request as before.  But if it was then we skip sending
the Set-Interface request, and we clear out the host-side endpoint
state by calling usb_disable_interface() followed by
usb_enable_interface().

The patch also adds a couple of new comments to explain what's going
on.

Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 77aa2b58
Loading
Loading
Loading
Loading
+16 −7
Original line number Diff line number Diff line
@@ -3394,9 +3394,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
		goto re_enumerate;
  	}

	/* Restore the device's previous configuration */
	if (!udev->actconfig)
		goto done;

	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
			USB_REQ_SET_CONFIGURATION, 0,
			udev->actconfig->desc.bConfigurationValue, 0,
@@ -3409,16 +3409,25 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
  	}
	usb_set_device_state(udev, USB_STATE_CONFIGURED);

	/* Put interfaces back into the same altsettings as before.
	 * Don't bother to send the Set-Interface request for interfaces
	 * that were already in altsetting 0; besides being unnecessary,
	 * many devices can't handle it.  Instead just reset the host-side
	 * endpoint state.
	 */
	for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
		struct usb_interface *intf = udev->actconfig->interface[i];
		struct usb_interface_descriptor *desc;

		/* set_interface resets host side toggle even
		 * for altsetting zero.  the interface may have no driver.
		 */
		desc = &intf->cur_altsetting->desc;
		if (desc->bAlternateSetting == 0) {
			usb_disable_interface(udev, intf, true);
			usb_enable_interface(udev, intf, true);
			ret = 0;
		} else {
			ret = usb_set_interface(udev, desc->bInterfaceNumber,
					desc->bAlternateSetting);
		}
		if (ret < 0) {
			dev_err(&udev->dev, "failed to restore interface %d "
				"altsetting %d (error=%d)\n",