]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
USB: re-enable interface after driver unbinds
authorAlan Stern <stern@rowland.harvard.edu>
Wed, 31 Dec 2008 16:31:33 +0000 (11:31 -0500)
committerGreg Kroah-Hartman <gregkh@suse.de>
Sun, 25 Jan 2009 00:41:44 +0000 (16:41 -0800)
commit 2caf7fcdb8532045680f06b67b9e63f0c9613aaa upstream.

This patch (as1197) fixes an error introduced recently.  Since a
significant number of devices can't handle Set-Interface requests, we
no longer call usb_set_interface() when a driver unbinds from an
interface, provided the interface is already in altsetting 0.  However
the interface still does get disabled, and the call to
usb_set_interface() was the only thing re-enabling it.  Since the
interface doesn't get re-enabled, further attempts to use it fail.

So the patch adds a call to usb_enable_interface() when a driver
unbinds and the interface is in altsetting 0.  For this to work
right, the interface's endpoints have to be re-enabled but their
toggles have to be left alone.  Therefore an additional argument is
added to usb_enable_endpoint() and usb_enable_interface(), a flag
indicating whether or not the endpoint toggles should be reset.

This is a forward-ported version of a patch which fixes Bugzilla
#12301.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Reported-by: David Roka <roka@dawid.hu>
Reported-by: Erik Ekman <erik@kryo.se>
Tested-by: Erik Ekman <erik@kryo.se>
Tested-by: Alon Bar-Lev <alon.barlev@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/driver.c
drivers/usb/core/hub.c
drivers/usb/core/message.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h

index 8c081308b0e2d8bf2349d6b9e9703de0548213ac..7c3710e421eb59d87d57a005784183d20a2501b5 100644 (file)
@@ -279,9 +279,12 @@ static int usb_unbind_interface(struct device *dev)
         * altsetting means creating new endpoint device entries).
         * When either of these happens, defer the Set-Interface.
         */
-       if (intf->cur_altsetting->desc.bAlternateSetting == 0)
-               ;       /* Already in altsetting 0 so skip Set-Interface */
-       else if (!error && intf->dev.power.status == DPM_ON)
+       if (intf->cur_altsetting->desc.bAlternateSetting == 0) {
+               /* Already in altsetting 0 so skip Set-Interface.
+                * Just re-enable it without affecting the endpoint toggles.
+                */
+               usb_enable_interface(udev, intf, false);
+       } else if (!error && intf->dev.power.status == DPM_ON)
                usb_set_interface(udev, intf->altsetting[0].
                                desc.bInterfaceNumber, 0);
        else
index b19cbfcd51da1bd53b500fd4ab3320c3108d8073..81eea42c4126a3ddd4dc3c780900b1d856c50717 100644 (file)
@@ -2385,7 +2385,7 @@ void usb_ep0_reinit(struct usb_device *udev)
 {
        usb_disable_endpoint(udev, 0 + USB_DIR_IN);
        usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
-       usb_enable_endpoint(udev, &udev->ep0);
+       usb_enable_endpoint(udev, &udev->ep0, true);
 }
 EXPORT_SYMBOL_GPL(usb_ep0_reinit);
 
index 6d1048faf08e1679597af0fadd6a5849d3afd299..bdcfe92fbaa4328490f15106f8e30288aaa88ac3 100644 (file)
@@ -1113,22 +1113,26 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
  * usb_enable_endpoint - Enable an endpoint for USB communications
  * @dev: the device whose interface is being enabled
  * @ep: the endpoint
+ * @reset_toggle: flag to set the endpoint's toggle back to 0
  *
- * Resets the endpoint toggle, and sets dev->ep_{in,out} pointers.
+ * Resets the endpoint toggle if asked, and sets dev->ep_{in,out} pointers.
  * For control endpoints, both the input and output sides are handled.
  */
-void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
+void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep,
+               bool reset_toggle)
 {
        int epnum = usb_endpoint_num(&ep->desc);
        int is_out = usb_endpoint_dir_out(&ep->desc);
        int is_control = usb_endpoint_xfer_control(&ep->desc);
 
        if (is_out || is_control) {
-               usb_settoggle(dev, epnum, 1, 0);
+               if (reset_toggle)
+                       usb_settoggle(dev, epnum, 1, 0);
                dev->ep_out[epnum] = ep;
        }
        if (!is_out || is_control) {
-               usb_settoggle(dev, epnum, 0, 0);
+               if (reset_toggle)
+                       usb_settoggle(dev, epnum, 0, 0);
                dev->ep_in[epnum] = ep;
        }
        ep->enabled = 1;
@@ -1138,17 +1142,18 @@ void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
  * usb_enable_interface - Enable all the endpoints for an interface
  * @dev: the device whose interface is being enabled
  * @intf: pointer to the interface descriptor
+ * @reset_toggles: flag to set the endpoints' toggles back to 0
  *
  * Enables all the endpoints for the interface's current altsetting.
  */
-static void usb_enable_interface(struct usb_device *dev,
-                                struct usb_interface *intf)
+void usb_enable_interface(struct usb_device *dev,
+               struct usb_interface *intf, bool reset_toggles)
 {
        struct usb_host_interface *alt = intf->cur_altsetting;
        int i;
 
        for (i = 0; i < alt->desc.bNumEndpoints; ++i)
-               usb_enable_endpoint(dev, &alt->endpoint[i]);
+               usb_enable_endpoint(dev, &alt->endpoint[i], reset_toggles);
 }
 
 /**
@@ -1271,7 +1276,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
         * during the SETUP stage - hence EP0 toggles are "don't care" here.
         * (Likewise, EP0 never "halts" on well designed devices.)
         */
-       usb_enable_interface(dev, iface);
+       usb_enable_interface(dev, iface, true);
        if (device_is_registered(&iface->dev))
                usb_create_sysfs_intf_files(iface);
 
@@ -1346,7 +1351,7 @@ int usb_reset_configuration(struct usb_device *dev)
                        alt = &intf->altsetting[0];
 
                intf->cur_altsetting = alt;
-               usb_enable_interface(dev, intf);
+               usb_enable_interface(dev, intf, true);
                if (device_is_registered(&intf->dev))
                        usb_create_sysfs_intf_files(intf);
        }
@@ -1604,7 +1609,7 @@ free_interfaces:
                        alt = &intf->altsetting[0];
 
                intf->cur_altsetting = alt;
-               usb_enable_interface(dev, intf);
+               usb_enable_interface(dev, intf, true);
                intf->dev.parent = &dev->dev;
                intf->dev.driver = NULL;
                intf->dev.bus = &usb_bus_type;
index be1fa0723f2c662a058040e8878e7ffb5c12c3df..956bf1ecf37e86e5134b448b6cb6d93d7bbb33ca 100644 (file)
@@ -362,7 +362,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
        dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
        dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
        /* ep0 maxpacket comes later, from device descriptor */
-       usb_enable_endpoint(dev, &dev->ep0);
+       usb_enable_endpoint(dev, &dev->ep0, true);
        dev->can_submit = 1;
 
        /* Save readable and stable topology id, distinguishing devices
index 9a1a45ac3add0ba9ab582d713a6341ee6df311c7..1d450e993e76766baa7893d244c308f30c6a5a0f 100644 (file)
@@ -10,7 +10,9 @@ extern int usb_create_ep_files(struct device *parent,
 extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
 
 extern void usb_enable_endpoint(struct usb_device *dev,
-               struct usb_host_endpoint *ep);
+               struct usb_host_endpoint *ep, bool reset_toggle);
+extern void usb_enable_interface(struct usb_device *dev,
+               struct usb_interface *intf, bool reset_toggles);
 extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr);
 extern void usb_disable_interface(struct usb_device *dev,
                struct usb_interface *intf);