David:

This revised patch implements the approach you described.  
usb_set_interface() is changed to work even with devices that have only 
one altsetting, and it gets called after unbinding a driver.  There are 
several other small changes, including a spot in usb_set_configuration() 
where all the interfaces are marked as being in altsetting 0 -- an obvious 
fact that was unaccountably missing.  Take a look and see what you think.

Alan Stern


# This is a BitKeeper generated patch for the following project:
# Project Name: greg k-h's linux 2.5 USB kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#                  ChangeSet    1.1656  -> 1.1657 
#       drivers/usb/core/message.c      1.49    -> 1.50   
#       drivers/usb/core/usb.h  1.1     -> 1.2    
#       drivers/usb/core/usb.c  1.214   -> 1.215  
#       drivers/usb/core/hub.c  1.113   -> 1.114  
#       drivers/usb/core/devio.c        1.77    -> 1.78   
#       drivers/usb/core/hcd.h  1.54    -> 1.55   
#        include/linux/usb.h    1.149   -> 1.150  
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/07/28      [EMAIL PROTECTED]       1.1657
# Rename usb_device_probe() and usb_device_remove() to usb_probe_interface() and 
usb_unbind_interface(),
# and don't export them.
# Remove set_maxpacket().  Replace it with usb_set_configuration().
# Implement usb_disable_endpoint(), usb_disable_interface(), and usb_disable_device().
# Implement usb_enable_endpoint() and usb_enable_interface().
# Enable debugging output in message.c.
# In usb_set_configuration(), set all interfaces to altsetting 0.
# Change the way usb_set_interface() works, and call it after unbinding a driver.
# --------------------------------------------
#
diff -Nru a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
--- a/drivers/usb/core/devio.c  Mon Jul 28 11:38:41 2003
+++ b/drivers/usb/core/devio.c  Mon Jul 28 11:38:41 2003
@@ -47,6 +47,7 @@
 #include <asm/byteorder.h>
 
 #include "hcd.h"       /* for usbcore internals */
+#include "usb.h"
 
 struct async {
        struct list_head asynclist;
@@ -726,7 +727,7 @@
 
                err ("%s - this function is broken", __FUNCTION__);
                if (intf->driver && ps->dev) {
-                       usb_device_probe (&intf->dev);
+                       usb_probe_interface (&intf->dev);
                }
        }
 
@@ -1105,7 +1106,7 @@
                if (driver) {
                        dbg ("disconnect '%s' from dev %d interface %d",
                             driver->name, ps->dev->devnum, ctrl.ifno);
-                       usb_device_remove(&ifp->dev);
+                       usb_unbind_interface(&ifp->dev);
                } else
                        retval = -ENODATA;
                unlock_kernel();
@@ -1114,7 +1115,7 @@
        /* let kernel drivers try to (re)bind to the interface */
        case USBDEVFS_CONNECT:
                lock_kernel();
-               retval = usb_device_probe (&ifp->dev);
+               retval = usb_probe_interface (&ifp->dev);
                unlock_kernel();
                break;
 
diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
--- a/drivers/usb/core/hcd.h    Mon Jul 28 11:38:41 2003
+++ b/drivers/usb/core/hcd.h    Mon Jul 28 11:38:41 2003
@@ -251,7 +251,6 @@
 
 /* exported to hub driver ONLY to support usb_reset_device () */
 extern int usb_get_configuration(struct usb_device *dev);
-extern void usb_set_maxpacket(struct usb_device *dev);
 extern void usb_destroy_configuration(struct usb_device *dev);
 extern int usb_set_address(struct usb_device *dev);
 
diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
--- a/drivers/usb/core/hub.c    Mon Jul 28 11:38:41 2003
+++ b/drivers/usb/core/hub.c    Mon Jul 28 11:38:41 2003
@@ -1324,9 +1324,7 @@
                        return 1;
                }
 
-               dev->actconfig = dev->config;
-               usb_set_maxpacket(dev);
-
+               usb_set_configuration(dev, dev->config[0].desc.bConfigurationValue);
                return 1;
        }
 
diff -Nru a/drivers/usb/core/message.c b/drivers/usb/core/message.c
--- a/drivers/usb/core/message.c        Mon Jul 28 11:38:41 2003
+++ b/drivers/usb/core/message.c        Mon Jul 28 11:38:41 2003
@@ -2,6 +2,14 @@
  * message.c - synchronous message handling
  */
 
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+       #define DEBUG
+#else
+       #undef DEBUG
+#endif
+
 #include <linux/pci.h> /* for scatterlist macros */
 #include <linux/usb.h>
 #include <linux/module.h>
@@ -11,6 +19,7 @@
 #include <asm/byteorder.h>
 
 #include "hcd.h"       /* for usbcore internals */
+#include "usb.h"
 
 struct usb_api_data {
        wait_queue_head_t wqh;
@@ -666,41 +675,6 @@
                HZ * USB_CTRL_GET_TIMEOUT);
 }
 
-
-// hub-only!! ... and only exported for reset/reinit path.
-// otherwise used internally, when setting up a config
-void usb_set_maxpacket(struct usb_device *dev)
-{
-       int i, b;
-
-       /* NOTE:  affects all endpoints _except_ ep0 */
-       for (i=0; i<dev->actconfig->desc.bNumInterfaces; i++) {
-               struct usb_interface *ifp = dev->actconfig->interface + i;
-               struct usb_host_interface *as = ifp->altsetting + ifp->act_altsetting;
-               struct usb_host_endpoint *ep = as->endpoint;
-               int e;
-
-               for (e=0; e<as->desc.bNumEndpoints; e++) {
-                       struct usb_endpoint_descriptor  *d;
-                       d = &ep [e].desc;
-                       b = d->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
-                       if ((d->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
-                               USB_ENDPOINT_XFER_CONTROL) {    /* Control => 
bidirectional */
-                               dev->epmaxpacketout[b] = d->wMaxPacketSize;
-                               dev->epmaxpacketin [b] = d->wMaxPacketSize;
-                               }
-                       else if (usb_endpoint_out(d->bEndpointAddress)) {
-                               if (d->wMaxPacketSize > dev->epmaxpacketout[b])
-                                       dev->epmaxpacketout[b] = d->wMaxPacketSize;
-                       }
-                       else {
-                               if (d->wMaxPacketSize > dev->epmaxpacketin [b])
-                                       dev->epmaxpacketin [b] = d->wMaxPacketSize;
-                       }
-               }
-       }
-}
-
 /**
  * usb_clear_halt - tells device to clear endpoint halt/stall condition
  * @dev: device whose endpoint is halted
@@ -760,6 +734,109 @@
 }
 
 /**
+ * usb_disable_endpoint -- Disable an endpoint by address
+ * @dev: the device whose endpoint is being disabled
+ * @epaddr: the endpoint's address.  Endpoint number for output,
+ *     endpoint number + USB_DIR_IN for input
+ *
+ * Deallocates hcd/hardware state for this endpoint ... and nukes all
+ * pending urbs.
+ */
+void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
+{
+       if (dev && dev->bus && dev->bus->op && dev->bus->op->disable)
+               dev->bus->op->disable(dev, epaddr);
+}
+
+/**
+ * usb_disable_interface -- Disable all endpoints for an interface
+ * @dev: the device whose interface is being disabled
+ * @intf: pointer to the interface descriptor
+ *
+ * Disables all the endpoints for the interface's current altsetting.
+ */
+void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf)
+{
+       struct usb_host_interface *hintf =
+                       &intf->altsetting[intf->act_altsetting];
+       int i;
+
+       for (i = 0; i < hintf->desc.bNumEndpoints; ++i) {
+               usb_disable_endpoint(dev,
+                               hintf->endpoint[i].desc.bEndpointAddress);
+       }
+}
+
+/*
+ * usb_disable_device - Disable all the endpoints for a USB device
+ * @dev: the device whose endpoints are being disabled
+ * @skip_ep0: 0 to disable endpoint 0, 1 to skip it.
+ *
+ * Disables all the device's endpoints, potentially including endpoint 0.
+ * Deallocates hcd/hardware state for the endpoints ... and nukes all
+ * pending urbs.
+ */
+void usb_disable_device(struct usb_device *dev, int skip_ep0)
+{
+       int i;
+
+       dbg("nuking URBs for device %s", dev->dev.bus_id);
+       for (i = skip_ep0; i < 16; ++i) {
+               usb_disable_endpoint(dev, i);
+               usb_disable_endpoint(dev, i + USB_DIR_IN);
+       }
+}
+
+
+/*
+ * usb_enable_endpoint - Enable an endpoint for USB communications
+ * @dev: the device whose interface is being enabled
+ * @epd: pointer to the endpoint descriptor
+ *
+ * Sets the endpoint's maxpacket value, resets the toggle,
+ * and marks it as running.  For control endpoints, both the input
+ * and output sides are handled.
+ */
+void usb_enable_endpoint(struct usb_device *dev,
+               struct usb_endpoint_descriptor *epd)
+{
+       int maxsize = epd->wMaxPacketSize;
+       unsigned int epaddr = epd->bEndpointAddress;
+       unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
+       int is_control = ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+                               USB_ENDPOINT_XFER_CONTROL);
+
+       if (usb_endpoint_out(epaddr) || is_control) {
+               dev->epmaxpacketout[epnum] = maxsize;
+               usb_settoggle(dev, epnum, 1, 0);
+               usb_endpoint_running(dev, epnum, 1);
+       }
+       if (!usb_endpoint_out(epaddr) || is_control) {
+               dev->epmaxpacketin[epnum] = maxsize;
+               usb_settoggle(dev, epnum, 0, 0);
+               usb_endpoint_running(dev, epnum, 0);
+       }
+}
+
+/*
+ * usb_enable_interface - Enable all the endpoints for an interface
+ * @dev: the device whose interface is being enabled
+ * @intf: pointer to the interface descriptor
+ *
+ * Enables all the endpoints for the interface's current altsetting.
+ */
+void usb_enable_interface(struct usb_device *dev,
+               struct usb_interface *intf)
+{
+       struct usb_host_interface *hintf =
+                       &intf->altsetting[intf->act_altsetting];
+       int i;
+
+       for (i = 0; i < hintf->desc.bNumEndpoints; ++i)
+               usb_enable_endpoint(dev, &hintf->endpoint[i].desc);
+}
+
+/**
  * usb_set_interface - Makes a particular alternate setting be current
  * @dev: the device whose interface is being updated
  * @interface: the interface being updated
@@ -795,9 +872,8 @@
 int usb_set_interface(struct usb_device *dev, int interface, int alternate)
 {
        struct usb_interface *iface;
-       struct usb_host_interface *iface_as;
-       int i, ret;
-       void (*disable)(struct usb_device *, int) = dev->bus->op->disable;
+       int ret;
+       int manual = 0;
 
        iface = usb_ifnum_to_if(dev, interface);
        if (!iface) {
@@ -805,22 +881,23 @@
                return -EINVAL;
        }
 
-       /* 9.4.10 says devices don't need this, if the interface
-          only has one alternate setting */
-       if (iface->num_altsetting == 1) {
-               dbg("ignoring set_interface for dev %d, iface %d, alt %d",
-                       dev->devnum, interface, alternate);
-               return 0;
-       }
-
        if (alternate < 0 || alternate >= iface->num_altsetting)
                return -EINVAL;
 
-       if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                                   USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
                                   iface->altsetting[alternate]
                                        .desc.bAlternateSetting,
-                                  interface, NULL, 0, HZ * 5)) < 0)
+                                  interface, NULL, 0, HZ * 5);
+
+       /* 9.4.10 says devices don't need this and are free to STALL the
+        * request if the interface only has one alternate setting.
+        */
+       if (ret == -EPIPE && iface->num_altsetting == 1) {
+               dbg("manual set_interface for dev %d, iface %d, alt %d",
+                       dev->devnum, interface, alternate);
+               manual = 1;
+       } else if (ret < 0)
                return ret;
 
        /* FIXME drivers shouldn't need to replicate/bugfix the logic here
@@ -830,20 +907,32 @@
         */
 
        /* prevent submissions using previous endpoint settings */
-       iface_as = iface->altsetting + iface->act_altsetting;
-       for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {
-               u8      ep = iface_as->endpoint [i].desc.bEndpointAddress;
-               int     out = !(ep & USB_DIR_IN);
-
-               /* clear out hcd state, then usbcore state */
-               if (disable)
-                       disable (dev, ep);
-               ep &= USB_ENDPOINT_NUMBER_MASK;
-               (out ? dev->epmaxpacketout : dev->epmaxpacketin ) [ep] = 0;
-       }
+       usb_disable_interface(dev, iface);
+
        iface->act_altsetting = alternate;
 
-       /* 9.1.1.5: reset toggles for all endpoints affected by this iface-as
+       /* If the interface only has one altsetting and the device didn't
+        * accept the request, we attempt to carry out the equivalent action
+        * by manually clearing the HALT feature for each endpoint in the
+        * new altsetting.
+        */
+       if (manual) {
+               struct usb_host_interface *iface_as =
+                               &iface->altsetting[alternate];
+               int i;
+
+               for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {
+                       unsigned int epaddr =
+                               iface_as->endpoint[i].desc.bEndpointAddress;
+                       unsigned int pipe =
+       __create_pipe(dev, USB_ENDPOINT_NUMBER_MASK & epaddr)
+       | (usb_endpoint_out(epaddr) ? USB_DIR_OUT : USB_DIR_IN);
+
+                       usb_clear_halt(dev, pipe);
+               }
+       }
+
+       /* 9.1.1.5: reset toggles for all endpoints in the new altsetting
         *
         * Note:
         * Despite EP0 is always present in all interfaces/AS, the list of
@@ -854,18 +943,7 @@
         * during the SETUP stage - hence EP0 toggles are "don't care" here.
         * (Likewise, EP0 never "halts" on well designed devices.)
         */
-
-       iface_as = &iface->altsetting[alternate];
-       for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {
-               u8      ep = iface_as->endpoint[i].desc.bEndpointAddress;
-               int     out = !(ep & USB_DIR_IN);
-
-               ep &= USB_ENDPOINT_NUMBER_MASK;
-               usb_settoggle (dev, ep, out, 0);
-               (out ? dev->epmaxpacketout : dev->epmaxpacketin) [ep]
-                       = iface_as->endpoint [i].desc.wMaxPacketSize;
-               usb_endpoint_running (dev, ep, out);
-       }
+       usb_enable_interface(dev, iface);
 
        return 0;
 }
@@ -904,7 +982,6 @@
 {
        int i, ret;
        struct usb_host_config *cp = NULL;
-       void (*disable)(struct usb_device *, int) = dev->bus->op->disable;
        
        for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
                if (dev->config[i].desc.bConfigurationValue == configuration) {
@@ -918,12 +995,8 @@
        }
 
        /* if it's already configured, clear out old state first. */
-       if (dev->state != USB_STATE_ADDRESS && disable) {
-               for (i = 1 /* skip ep0 */; i < 15; i++) {
-                       disable (dev, i);
-                       disable (dev, USB_DIR_IN | i);
-               }
-       }
+       if (dev->state != USB_STATE_ADDRESS)
+               usb_disable_device (dev, 1);    // Skip ep0
        dev->toggle[0] = dev->toggle[1] = 0;
        dev->halted[0] = dev->halted[1] = 0;
        dev->state = USB_STATE_ADDRESS;
@@ -936,8 +1009,13 @@
                dev->state = USB_STATE_CONFIGURED;
        dev->actconfig = cp;
 
-       /* reset more hc/hcd endpoint state */
-       usb_set_maxpacket(dev);
+       /* reset more hc/hcd interface/endpoint state */
+       for (i = 0; i < cp->desc.bNumInterfaces; ++i) {
+               struct usb_interface *intf = &cp->interface[i];
+
+               intf->act_altsetting = 0;
+               usb_enable_interface(dev, intf);
+       }
 
        return 0;
 }
diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c    Mon Jul 28 11:38:41 2003
+++ b/drivers/usb/core/usb.c    Mon Jul 28 11:38:41 2003
@@ -80,25 +80,8 @@
 
 static int usb_generic_driver_data;
 
-/* deallocate hcd/hardware state ... and nuke all pending urbs */
-static void nuke_urbs(struct usb_device *dev)
-{
-       void (*disable)(struct usb_device *, int);
-       int i;
-
-       if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->disable)
-               return;
-       dbg("nuking urbs assigned to %s", dev->dev.bus_id);
-
-       disable = dev->bus->op->disable;
-       for (i = 0; i < 15; i++) {
-               disable(dev, i);
-               disable(dev, USB_DIR_IN | i);
-       }
-}
-
 /* needs to be called with BKL held */
-int usb_device_probe(struct device *dev)
+int usb_probe_interface(struct device *dev)
 {
        struct usb_interface * intf = to_usb_interface(dev);
        struct usb_driver * driver = to_usb_driver(dev->driver);
@@ -123,25 +106,21 @@
        return error;
 }
 
-int usb_device_remove(struct device *dev)
+int usb_unbind_interface(struct device *dev)
 {
-       struct usb_interface *intf;
-       struct usb_driver *driver;
-
-       intf = list_entry(dev,struct usb_interface,dev);
-       driver = to_usb_driver(dev->driver);
+       struct usb_interface *intf = to_usb_interface(dev);
+       struct usb_driver *driver = to_usb_driver(dev->driver);
 
        down(&driver->serialize);
 
-       /* release all urbs for this device */
-       nuke_urbs(interface_to_usbdev(intf));
+       /* release all urbs for this interface */
+       usb_disable_interface(interface_to_usbdev(intf), intf);
 
        if (intf->driver && intf->driver->disconnect)
                intf->driver->disconnect(intf);
 
-       /* if driver->disconnect didn't release the interface */
-       if (intf->driver)
-               usb_driver_release_interface(driver, intf);
+       /* force a release and re-initialize the interface */
+       usb_driver_release_interface(driver, intf);
 
        up(&driver->serialize);
 
@@ -170,8 +149,8 @@
 
        new_driver->driver.name = (char *)new_driver->name;
        new_driver->driver.bus = &usb_bus_type;
-       new_driver->driver.probe = usb_device_probe;
-       new_driver->driver.remove = usb_device_remove;
+       new_driver->driver.probe = usb_probe_interface;
+       new_driver->driver.remove = usb_unbind_interface;
 
        init_MUTEX(&new_driver->serialize);
 
@@ -325,10 +304,12 @@
  * usb_driver_release_interface - unbind a driver from an interface
  * @driver: the driver to be unbound
  * @iface: the interface from which it will be unbound
+ *
+ * In addition to unbinding the driver, this re-initializes the interface
+ * by selecting altsetting 0, the default alternate setting.
  * 
- * This should be used by drivers to release their claimed interfaces.
- * It is normally called in their disconnect() methods, and only for
- * drivers that bound to more than one interface in their probe().
+ * This can be used by drivers to release an interface without waiting
+ * for their disconnect() methods to be called.
  *
  * When the USB subsystem disconnect()s a driver from some interface,
  * it automatically invokes this method for that interface.  That
@@ -338,11 +319,14 @@
 void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface 
*iface)
 {
        /* this should never happen, don't release something that's not ours */
-       if (!iface || iface->driver != driver)
+       if (iface->driver && iface->driver != driver)
                return;
 
        iface->driver = NULL;
        usb_set_intfdata(iface, NULL);
+       usb_set_interface(interface_to_usbdev(iface),
+                       iface->altsetting[0].desc.bInterfaceNumber,
+                       0);
 }
 
 /**
@@ -917,7 +901,7 @@
        }
 
        /* deallocate hcd/hardware state ... and nuke all pending urbs */
-       nuke_urbs(dev);
+       usb_disable_device(dev, 0);
 
        /* disconnect() drivers from interfaces (a key side effect) */
        dev_dbg (&dev->dev, "unregistering interfaces\n");
@@ -1586,9 +1570,6 @@
 EXPORT_SYMBOL(usb_register);
 EXPORT_SYMBOL(usb_deregister);
 EXPORT_SYMBOL(usb_disabled);
-
-EXPORT_SYMBOL(usb_device_probe);
-EXPORT_SYMBOL(usb_device_remove);
 
 EXPORT_SYMBOL(usb_alloc_dev);
 EXPORT_SYMBOL(usb_put_dev);
diff -Nru a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
--- a/drivers/usb/core/usb.h    Mon Jul 28 11:38:41 2003
+++ b/drivers/usb/core/usb.h    Mon Jul 28 11:38:41 2003
@@ -2,4 +2,15 @@
 
 extern void usb_create_driverfs_dev_files (struct usb_device *dev);
 extern void usb_create_driverfs_intf_files (struct usb_interface *intf);
+extern int usb_probe_interface (struct device *dev);
+extern int usb_unbind_interface (struct device *dev);
 
+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);
+extern void usb_disable_device (struct usb_device *dev, int skip_ep0);
+
+extern void usb_enable_endpoint (struct usb_device *dev,
+               struct usb_endpoint_descriptor *epd);
+extern void usb_enable_interface (struct usb_device *dev,
+               struct usb_interface *intf);
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h       Mon Jul 28 11:38:41 2003
+++ b/include/linux/usb.h       Mon Jul 28 11:38:41 2003
@@ -484,8 +484,6 @@
 extern void usb_deregister_dev(struct usb_interface *intf,
                               struct usb_class_driver *class_driver);
 
-extern int usb_device_probe(struct device *dev);
-extern int usb_device_remove(struct device *dev);
 extern int usb_disabled(void);
 
 /* -------------------------------------------------------------------------- */



-------------------------------------------------------
This SF.Net email sponsored by: Free pre-built ASP.NET sites including
Data Reports, E-commerce, Portals, and Forums are available now.
Download today and enter to win an XBOX or Visual Studio .NET.
http://aspnet.click-url.com/go/psa00100003ave/direct;at.aspnet_072303_01/01
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to