From 4fe0387afa893b44c5a21b9bfbb0888354ef4656 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 26 Feb 2009 10:21:02 -0500 Subject: 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: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'drivers/usb/core') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f17d9ebc44a..81eb3e6b659 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3393,10 +3393,10 @@ static int usb_reset_and_verify_device(struct usb_device *udev) udev->descriptor = descriptor; /* for disconnect() calls */ 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; - ret = usb_set_interface(udev, desc->bInterfaceNumber, - desc->bAlternateSetting); + 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", -- cgit v1.2.3