Greg:

This is a revised version of an earlier patch; I feel a lot better about 
this one.  Basically it does the same thing as before: allocate interfaces
dynamically to avoid the problems with reusing them.

The difference is that this patch adds a struct kref to the array of
usb_interface_cache's, so the array can persist if needed after the device
has been disconnected.  Each interface takes a reference to it (along with 
the configuration itself), so as long as the interfaces remain pinned in 
memory the altsettings will also remain.

This could use a good deal of testing.  Also I would like to hear
people's comments on it.  However, if nothing breaks and nothing looks
bad, I think this is ready to be applied.

Alan Stern


===== include/linux/usb.h 1.181 vs edited =====
--- 1.181/include/linux/usb.h   Wed Mar 31 17:13:05 2004
+++ edited/include/linux/usb.h  Thu Apr 15 14:43:58 2004
@@ -18,6 +18,7 @@
 #include <linux/fs.h>          /* for struct file_operations */
 #include <linux/completion.h>  /* for struct completion */
 #include <linux/sched.h>       /* for current && schedule_timeout */
+#include <linux/kref.h>                /* for struct kref */
 
 
 static __inline__ void wait_ms(unsigned int ms)
@@ -147,11 +148,42 @@
 #define USB_MAXINTERFACES      32
 
 /**
+ * struct usb_interface_cache - long-term representation of a device interface
+ * @num_altsetting: number of altsettings defined.
+ * @ref: reference counter.
+ * @altsetting: variable-length array of interface structures, one for
+ *     each alternate setting that may be selected.  Each one includes a
+ *     set of endpoint configurations.  They will be in no particular order.
+ *
+ * These structures persist for the lifetime of a usb_device, unlike
+ * struct usb_interface (which persists only as long as its configuration
+ * is installed).  The altsetting arrays can be accessed through these
+ * structures at any time, permitting comparison of configurations and
+ * providing support for the /proc/bus/usb/devices pseudo-file.
+ */
+struct usb_interface_cache {
+       unsigned num_altsetting;        /* number of alternate settings */
+       struct kref ref;                /* reference counter */
+
+       /* variable-length array of alternate settings for this interface,
+        * stored in no particular order */
+       struct usb_host_interface altsetting[0];
+};
+#define        ref_to_usb_interface_cache(r) \
+               container_of(r, struct usb_interface_cache, ref)
+#define        altsetting_to_usb_interface_cache(a) \
+               container_of(a, struct usb_interface_cache, altsetting[0])
+
+/**
  * struct usb_host_config - representation of a device's configuration
  * @desc: the device's configuration descriptor.
- * @interface: array of usb_interface structures, one for each interface
- *     in the configuration.  The number of interfaces is stored in
- *     desc.bNumInterfaces.
+ * @interface: array of pointers to usb_interface structures, one for each
+ *     interface in the configuration.  The number of interfaces is stored
+ *     in desc.bNumInterfaces.  These pointers are valid only while the
+ *     the configuration is active.
+ * @intf_cache: array of pointers to usb_interface_cache structures, one
+ *     for each interface in the configuration.  These structures exist
+ *     for the entire life of the device.
  * @extra: pointer to buffer containing all extra descriptors associated
  *     with this configuration (those preceding the first interface
  *     descriptor).
@@ -184,6 +216,10 @@
        /* the interfaces associated with this configuration,
         * stored in no particular order */
        struct usb_interface *interface[USB_MAXINTERFACES];
+
+       /* Interface information available even when this is not the
+        * active configuration */
+       struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
 
        unsigned char *extra;   /* Extra descriptors */
        int extralen;
===== drivers/usb/core/config.c 1.33 vs edited =====
--- 1.33/drivers/usb/core/config.c      Mon Mar 29 11:17:38 2004
+++ edited/drivers/usb/core/config.c    Thu Apr 15 14:45:41 2004
@@ -96,19 +96,14 @@
        return buffer - buffer0 + i;
 }
 
-static void usb_free_intf(struct usb_interface *intf)
+static void usb_release_interface_cache(struct kref *ref)
 {
+       struct usb_interface_cache *intfc = ref_to_usb_interface_cache(ref);
        int j;
 
-       if (intf->altsetting) {
-               for (j = 0; j < intf->num_altsetting; j++) {
-                       struct usb_host_interface *alt = &intf->altsetting[j];
-
-                       kfree(alt->endpoint);
-               }
-               kfree(intf->altsetting);
-       }
-       kfree(intf);
+       for (j = 0; j < intfc->num_altsetting; j++)
+               kfree(intfc->altsetting[j].endpoint);
+       kfree(intfc);
 }
 
 static int usb_parse_interface(struct device *ddev, int cfgno,
@@ -117,7 +112,7 @@
        unsigned char *buffer0 = buffer;
        struct usb_interface_descriptor *d;
        int inum, asnum;
-       struct usb_interface *interface;
+       struct usb_interface_cache *intfc;
        struct usb_host_interface *alt;
        int i, n;
        int len, retval;
@@ -137,16 +132,16 @@
        if (inum >= config->desc.bNumInterfaces)
                goto skip_to_next_interface_descriptor;
 
-       interface = config->interface[inum];
+       intfc = config->intf_cache[inum];
        asnum = d->bAlternateSetting;
-       if (asnum >= interface->num_altsetting) {
+       if (asnum >= intfc->num_altsetting) {
                dev_err(ddev, "config %d interface %d has an invalid "
                    "alternate setting number: %d but max is %d\n",
-                   cfgno, inum, asnum, interface->num_altsetting - 1);
+                   cfgno, inum, asnum, intfc->num_altsetting - 1);
                return -EINVAL;
        }
 
-       alt = &interface->altsetting[asnum];
+       alt = &intfc->altsetting[asnum];
        if (alt->desc.bLength) {
                dev_err(ddev, "Duplicate descriptor for config %d "
                    "interface %d altsetting %d\n", cfgno, inum, asnum);
@@ -210,11 +205,12 @@
        int cfgno;
        int nintf, nintf_orig;
        int i, j, n;
-       struct usb_interface *interface;
+       struct usb_interface_cache *intfc;
        unsigned char *buffer2;
        int size2;
        struct usb_descriptor_header *header;
        int len, retval;
+       u8 nalts[USB_MAXINTERFACES];
 
        memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
        if (config->desc.bDescriptorType != USB_DT_CONFIG ||
@@ -237,14 +233,7 @@
                    cfgno, nintf, USB_MAXINTERFACES);
                config->desc.bNumInterfaces = nintf = USB_MAXINTERFACES;
        }
-
-       for (i = 0; i < nintf; ++i) {
-               interface = config->interface[i] =
-                   kmalloc(sizeof(struct usb_interface), GFP_KERNEL);
-               if (!interface)
-                       return -ENOMEM;
-               memset(interface, 0, sizeof(struct usb_interface));
-       }
+       memset(nalts, 0, nintf);
 
        /* Go through the descriptors, checking their length and counting the
         * number of altsettings for each interface */
@@ -277,8 +266,8 @@
                                    cfgno, i, nintf_orig - 1);
                                return -EINVAL;
                        }
-                       if (i < nintf)
-                               ++config->interface[i]->num_altsetting;
+                       if (i < nintf && nalts[i] < 255)
+                               ++nalts[i];
 
                } else if (header->bDescriptorType == USB_DT_DEVICE ||
                            header->bDescriptorType == USB_DT_CONFIG) {
@@ -290,29 +279,29 @@
 
        }       /* for ((buffer2 = buffer, size2 = size); ...) */
 
-       /* Allocate the altsetting arrays */
+       /* Allocate the usb_interface_caches and altsetting arrays */
        for (i = 0; i < nintf; ++i) {
-               interface = config->interface[i];
-               if (interface->num_altsetting > USB_MAXALTSETTING) {
+               j = nalts[i];
+               if (j > USB_MAXALTSETTING) {
                        dev_err(ddev, "too many alternate settings for "
                            "config %d interface %d: %d, "
                            "maximum allowed: %d\n",
-                           cfgno, i, interface->num_altsetting,
-                           USB_MAXALTSETTING);
+                           cfgno, i, j, USB_MAXALTSETTING);
                        return -EINVAL;
                }
-               if (interface->num_altsetting == 0) {
+               if (j == 0) {
                        dev_err(ddev, "config %d has no interface number "
                            "%d\n", cfgno, i);
                        return -EINVAL;
                }
 
-               len = sizeof(*interface->altsetting) *
-                   interface->num_altsetting;
-               interface->altsetting = kmalloc(len, GFP_KERNEL);
-               if (!interface->altsetting)
+               len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j;
+               config->intf_cache[i] = intfc = kmalloc(len, GFP_KERNEL);
+               if (!intfc)
                        return -ENOMEM;
-               memset(interface->altsetting, 0, len);
+               memset(intfc, 0, len);
+               intfc->num_altsetting = j;
+               kref_init(&intfc->ref, usb_release_interface_cache);
        }
 
        /* Skip over any Class Specific or Vendor Specific descriptors;
@@ -340,9 +329,9 @@
 
        /* Check for missing altsettings */
        for (i = 0; i < nintf; ++i) {
-               interface = config->interface[i];
-               for (j = 0; j < interface->num_altsetting; ++j) {
-                       if (!interface->altsetting[j].desc.bLength) {
+               intfc = config->intf_cache[i];
+               for (j = 0; j < intfc->num_altsetting; ++j) {
+                       if (!intfc->altsetting[j].desc.bLength) {
                                dev_err(ddev, "config %d interface %d has no "
                                    "altsetting %d\n", cfgno, i, j);
                                return -EINVAL;
@@ -374,10 +363,8 @@
                struct usb_host_config *cf = &dev->config[c];
 
                for (i = 0; i < cf->desc.bNumInterfaces; i++) {
-                       struct usb_interface *ifp = cf->interface[i];
-
-                       if (ifp)
-                               usb_free_intf(ifp);
+                       if (cf->intf_cache[i])
+                               kref_put(&cf->intf_cache[i]->ref);
                }
        }
        kfree(dev->config);
===== drivers/usb/core/devices.c 1.29 vs edited =====
--- 1.29/drivers/usb/core/devices.c     Tue Mar 30 11:50:40 2004
+++ edited/drivers/usb/core/devices.c   Thu Apr 15 12:21:07 2004
@@ -232,13 +232,21 @@
        return start;
 }
 
-static char *usb_dump_interface_descriptor(char *start, char *end, const struct 
usb_interface *iface, int setno)
+static char *usb_dump_interface_descriptor(char *start, char *end,
+       const struct usb_interface_cache *intfc,
+       const struct usb_interface *iface,
+       int setno)
 {
-       struct usb_interface_descriptor *desc = &iface->altsetting[setno].desc;
+       struct usb_interface_descriptor *desc = &intfc->altsetting[setno].desc;
+       char *driver_name = "";
 
        if (start > end)
                return start;
        down_read(&usb_bus_type.subsys.rwsem);
+       if (iface)
+               driver_name = (iface->dev.driver
+                               ? iface->dev.driver->name
+                               : "(none)");
        start += sprintf(start, format_iface,
                         desc->bInterfaceNumber,
                         desc->bAlternateSetting,
@@ -247,9 +255,7 @@
                         class_decode(desc->bInterfaceClass),
                         desc->bInterfaceSubClass,
                         desc->bInterfaceProtocol,
-                        iface->dev.driver
-                               ? iface->dev.driver->name
-                               : "(none)");
+                        driver_name);
        up_read(&usb_bus_type.subsys.rwsem);
        return start;
 }
@@ -258,13 +264,14 @@
        int speed,
        char *start,
        char *end,
+       const struct usb_interface_cache *intfc,
        const struct usb_interface *iface,
        int setno
 ) {
-       struct usb_host_interface *desc = &iface->altsetting[setno];
+       struct usb_host_interface *desc = &intfc->altsetting[setno];
        int i;
 
-       start = usb_dump_interface_descriptor(start, end, iface, setno);
+       start = usb_dump_interface_descriptor(start, end, intfc, iface, setno);
        for (i = 0; i < desc->desc.bNumEndpoints; i++) {
                if (start > end)
                        return start;
@@ -303,6 +310,7 @@
 )
 {
        int i, j;
+       struct usb_interface_cache *intfc;
        struct usb_interface *interface;
 
        if (start > end)
@@ -311,14 +319,13 @@
                return start + sprintf(start, "(null Cfg. desc.)\n");
        start = usb_dump_config_descriptor(start, end, &config->desc, active);
        for (i = 0; i < config->desc.bNumInterfaces; i++) {
+               intfc = config->intf_cache[i];
                interface = config->interface[i];
-               if (!interface)
-                       break;
-               for (j = 0; j < interface->num_altsetting; j++) {
+               for (j = 0; j < intfc->num_altsetting; j++) {
                        if (start > end)
                                return start;
                        start = usb_dump_interface(speed,
-                                       start, end, interface, j);
+                               start, end, intfc, interface, j);
                }
        }
        return start;
@@ -395,7 +402,7 @@
                return start;
        
        start = usb_dump_device_strings (start, end, dev);
-       
+
        for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
                if (start > end)
                        return start;
===== drivers/usb/core/message.c 1.80 vs edited =====
--- 1.80/drivers/usb/core/message.c     Tue Apr  6 05:52:25 2004
+++ edited/drivers/usb/core/message.c   Thu Apr 15 12:28:25 2004
@@ -796,10 +796,6 @@
        }
 }
 
-static void release_interface(struct device *dev)
-{
-}
-
 /*
  * usb_disable_device - Disable all the endpoints for a USB device
  * @dev: the device whose endpoints are being disabled
@@ -835,6 +831,7 @@
                        dev_dbg (&dev->dev, "unregistering interface %s\n",
                                interface->dev.bus_id);
                        device_unregister (&interface->dev);
+                       dev->actconfig->interface[i] = NULL;
                }
                dev->actconfig = 0;
                if (dev->state == USB_STATE_CONFIGURED)
@@ -1071,6 +1068,16 @@
        return 0;
 }
 
+static void release_interface(struct device *dev)
+{
+       struct usb_interface *intf = to_usb_interface(dev);
+       struct usb_interface_cache *intfc =
+                       altsetting_to_usb_interface_cache(intf->altsetting);
+
+       kref_put(&intfc->ref);
+       kfree(intf);
+}
+
 /*
  * usb_set_configuration - Makes a particular device setting be current
  * @dev: the device whose configuration is being updated
@@ -1109,19 +1116,17 @@
 {
        int i, ret;
        struct usb_host_config *cp = NULL;
-       
+
        /* dev->serialize guards all config changes */
 
-       for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
+       for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
                if (dev->config[i].desc.bConfigurationValue == configuration) {
                        cp = &dev->config[i];
                        break;
                }
        }
-       if ((!cp && configuration != 0)) {
-               ret = -EINVAL;
-               goto out;
-       }
+       if ((!cp && configuration != 0))
+               return -EINVAL;
 
        /* The USB spec says configuration 0 means unconfigured.
         * But if a device includes a configuration numbered 0,
@@ -1139,72 +1144,86 @@
        if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                        USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
                        NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0)
-               goto out;
+               return ret;
 
        dev->actconfig = cp;
-       if (!cp)
+       if (!cp) {
                dev->state = USB_STATE_ADDRESS;
-       else {
-               dev->state = USB_STATE_CONFIGURED;
+               return 0;
+       }
 
-               /* re-initialize hc/hcd/usbcore interface/endpoint state.
-                * this triggers binding of drivers to interfaces; and
-                * maybe probe() calls will choose different altsettings.
-                */
-               for (i = 0; i < cp->desc.bNumInterfaces; ++i) {
-                       struct usb_interface *intf = cp->interface[i];
-                       struct usb_host_interface *alt;
-
-                       alt = usb_altnum_to_altsetting(intf, 0);
-
-                       /* No altsetting 0?  We'll assume the first altsetting.
-                        * We could use a GetInterface call, but if a device is
-                        * so non-compliant that it doesn't have altsetting 0
-                        * then I wouldn't trust its reply anyway.
-                        */
-                       if (!alt)
-                               alt = &intf->altsetting[0];
-
-                       intf->cur_altsetting = alt;
-                       usb_enable_interface(dev, intf);
-                       intf->dev.parent = &dev->dev;
-                       intf->dev.driver = NULL;
-                       intf->dev.bus = &usb_bus_type;
-                       intf->dev.dma_mask = dev->dev.dma_mask;
-                       intf->dev.release = release_interface;
-                       device_initialize (&intf->dev);
-                       sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
-                                dev->bus->busnum, dev->devpath,
-                                configuration,
-                                alt->desc.bInterfaceNumber);
+       dev->state = USB_STATE_CONFIGURED;
+
+       /* re-initialize hc/hcd/usbcore interface/endpoint state.
+        * this triggers binding of drivers to interfaces; and
+        * maybe probe() calls will choose different altsettings.
+        */
+       for (i = 0; i < cp->desc.bNumInterfaces; ++i) {
+               struct usb_interface_cache *intfc;
+               struct usb_interface *intf;
+               struct usb_host_interface *alt;
+
+               cp->interface[i] = intf = kmalloc(sizeof(*intf), GFP_KERNEL);
+               if (!intf) {
+                       dev_err(&dev->dev, "Out of memory");
+                       while (--i >= 0) {
+                               put_device(&cp->interface[i]->dev);
+                               cp->interface[i] = NULL;
+                       }
+                       dev->actconfig = NULL;
+                       return -ENOMEM;
                }
 
-               /* Now that all interfaces are setup, probe() calls
-                * may claim() any interface that's not yet bound.
-                * Many class drivers need that: CDC, audio, video, etc.
+               memset(intf, 0, sizeof(*intf));
+               intfc = cp->intf_cache[i];
+               intf->altsetting = intfc->altsetting;
+               intf->num_altsetting = intfc->num_altsetting;
+               kref_get(&intfc->ref);
+
+               alt = usb_altnum_to_altsetting(intf, 0);
+
+               /* No altsetting 0?  We'll assume the first altsetting.
+                * We could use a GetInterface call, but if a device is
+                * so non-compliant that it doesn't have altsetting 0
+                * then I wouldn't trust its reply anyway.
                 */
-               for (i = 0; i < cp->desc.bNumInterfaces; ++i) {
-                       struct usb_interface *intf = cp->interface[i];
-                       struct usb_interface_descriptor *desc;
-
-                       desc = &intf->altsetting [0].desc;
-                       dev_dbg (&dev->dev,
-                               "adding %s (config #%d, interface %d)\n",
-                               intf->dev.bus_id, configuration,
-                               desc->bInterfaceNumber);
-                       ret = device_add (&intf->dev);
-                       if (ret != 0) {
-                               dev_err(&dev->dev,
-                                       "device_add(%s) --> %d\n",
-                                       intf->dev.bus_id,
-                                       ret);
-                               continue;
-                       }
-                       usb_create_driverfs_intf_files (intf);
+               if (!alt)
+                       alt = &intf->altsetting[0];
+
+               intf->cur_altsetting = alt;
+               usb_enable_interface(dev, intf);
+               intf->dev.parent = &dev->dev;
+               intf->dev.driver = NULL;
+               intf->dev.bus = &usb_bus_type;
+               intf->dev.dma_mask = dev->dev.dma_mask;
+               intf->dev.release = release_interface;
+               device_initialize (&intf->dev);
+               sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
+                        dev->bus->busnum, dev->devpath,
+                        configuration, alt->desc.bInterfaceNumber);
+       }
+
+       /* Now that all interfaces are setup, probe() calls
+        * may claim() any interface that's not yet bound.
+        * Many class drivers need that: CDC, audio, video, etc.
+        */
+       for (i = 0; i < cp->desc.bNumInterfaces; ++i) {
+               struct usb_interface *intf = cp->interface[i];
+               struct usb_interface_descriptor *desc;
+
+               desc = &intf->altsetting [0].desc;
+               dev_dbg (&dev->dev, "adding %s (config #%d, interface %d)\n",
+                       intf->dev.bus_id, configuration,
+                       desc->bInterfaceNumber);
+               ret = device_add (&intf->dev);
+               if (ret != 0) {
+                       dev_err(&dev->dev, "device_add(%s) --> %d\n",
+                               intf->dev.bus_id, ret);
+                       continue;
                }
+               usb_create_driverfs_intf_files (intf);
        }
 
-out:
        return ret;
 }
 
===== drivers/usb/core/usb.c 1.260 vs edited =====
--- 1.260/drivers/usb/core/usb.c        Wed Mar 31 17:13:05 2004
+++ edited/drivers/usb/core/usb.c       Thu Apr 15 12:19:20 2004
@@ -1193,7 +1193,7 @@
                        /* heuristic:  Linux is more likely to have class
                         * drivers, so avoid vendor-specific interfaces.
                         */
-                       desc = &dev->config[i].interface[0]
+                       desc = &dev->config[i].intf_cache[0]
                                        ->altsetting->desc;
                        if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC)
                                continue;




-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials
Free Linux tutorial presented by Daniel Robbins, President and CEO of
GenToo technologies. Learn everything from fundamentals to system
administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to