This patch makes "Gadget Zero" autoconfigure itself, getting rid
of most of the controller-specific conditional compilation.
(Whoops, I forgot an sa1100-specific quirk.)

Notable changes were:

 - Use the new config buffer utilities.  This simplifies code
   and also makes it easy to handle the SuperH UDC restriction
   (single configuration).

 - Add a new CONFIG_USB_GADGET_DUALSPEED flag, controlling when
   drivers need to support both full-speed and high-speed configs.

- Use the new endpoint autoconfig calls.

- Assume self-powered operation for now.

The code looks MUCH cleaner IMO, but so far it's only been
sanity tested on a net2280.

- Dave
--- 1.21/drivers/usb/gadget/zero.c      Mon Feb  2 13:22:04 2004
+++ edited/drivers/usb/gadget/zero.c    Fri Mar  5 16:00:10 2004
@@ -89,6 +89,8 @@
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
 
+#include "epautoconf.h"
+
 
 /*-------------------------------------------------------------------------*/
 
@@ -103,134 +105,13 @@
 /*-------------------------------------------------------------------------*/
 
 /*
- * hardware-specific configuration, controlled by which device
- * controller driver was configured.
- *
- * CHIP ... hardware identifier
- * DRIVER_VERSION_NUM ... alerts the host side driver to differences
- * EP_*_NAME ... which endpoints do we use for which purpose?
- * EP_*_NUM ... numbers for them (often limited by hardware)
- * HIGHSPEED ... define if ep0 and descriptors need high speed support
- * MAX_USB_POWER ... define if we use other than 100 mA bus current
- * SELFPOWER ... if we can run on bus power, zero
- * WAKEUP ... if hardware supports remote wakeup AND we will issue the
- *     usb_gadget_wakeup() call to initiate it, USB_CONFIG_ATT_WAKEUP
- *
- * add other defines for other portability issues, like hardware that
- * for some reason doesn't handle full speed bulk maxpacket of 64.
- */
-
-/*
- * DRIVER_VERSION_NUM 0x0000 (?):  Martin Diehl's ezusb an21/fx code
- */
-
-/*
- * NetChip 2280, PCI based.
+ * driver assumes self-powered hardware, and
+ * has no way for users to trigger remote wakeup.
  *
- * This has half a dozen configurable endpoints, four with dedicated
- * DMA channels to manage their FIFOs.  It supports high speed.
- * Those endpoints can be arranged in any desired configuration.
- */
-#if defined(CONFIG_USB_GADGET_NET2280) || defined(CONFIG_USB_GADGET_DUMMY_HCD)
-#define CHIP                   "net2280"
-#define DRIVER_VERSION_NUM     0x0101
-static const char EP_OUT_NAME [] = "ep-a";
-#define EP_OUT_NUM     2
-static const char EP_IN_NAME [] = "ep-b";
-#define EP_IN_NUM      2
-#define HIGHSPEED
-/* specific hardware configs could be bus-powered */
-/* supports remote wakeup, but this driver doesn't */
-#endif
-
-/*
- * PXA-2xx UDC:  widely used in second gen Linux-capable PDAs.
- *
- * This has fifteen fixed-function full speed endpoints, and it
- * can support all USB transfer types.
- *
- * These supports three or four configurations, with fixed numbers.
- * The hardware interprets SET_INTERFACE, net effect is that you
- * can't use altsettings or reset the interfaces independently.
- * So stick to a single interface.
- */
-#ifdef CONFIG_USB_GADGET_PXA2XX
-#define CHIP                   "pxa2xx"
-#define DRIVER_VERSION_NUM     0x0103
-static const char EP_OUT_NAME [] = "ep12out-bulk";
-#define EP_OUT_NUM     12
-static const char EP_IN_NAME [] = "ep11in-bulk";
-#define EP_IN_NUM      11
-/* doesn't support bus-powered operation */
-/* supports remote wakeup, but this driver doesn't */
-#endif
-
-/*
- * SA-1100 UDC:  widely used in first gen Linux-capable PDAs.
- *
- * This has only two fixed function endpoints, which can only
- * be used for bulk (or interrupt) transfers.  (Plus control.)
- *
- * Since it can't flush its TX fifos without disabling the UDC,
- * the current configuration or altsettings can't change except
- * in special situations.  So this is a case of "choose it right
- * during enumeration" ...
- */
-#ifdef CONFIG_USB_GADGET_SA1100
-#define CHIP                   "sa1100"
-#define DRIVER_VERSION_NUM     0x0105
-static const char EP_OUT_NAME [] = "ep1out-bulk";
-#define EP_OUT_NUM     1
-static const char EP_IN_NAME [] = "ep2in-bulk";
-#define EP_IN_NUM      2
-/* doesn't support bus-powered operation */
-/* doesn't support remote wakeup? */
-#endif
-
-/*
- * Toshiba TC86C001 ("Goku-S") UDC
- *
- * This has three semi-configurable full speed bulk/interrupt endpoints.
- */
-#ifdef CONFIG_USB_GADGET_GOKU
-#define CHIP                   "goku"
-#define DRIVER_VERSION_NUM     0x0106
-static const char EP_OUT_NAME [] = "ep1-bulk";
-#define EP_OUT_NUM     1
-static const char EP_IN_NAME [] = "ep2-bulk";
-#define EP_IN_NUM      2
-/* doesn't support remote wakeup */
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-#ifndef EP_OUT_NUM
-#      error Configure some USB peripheral controller driver!
-#endif
-
-/* power usage is config specific.
- * hardware that supports remote wakeup defaults to disabling it.
+ * this version autoconfigures as much as possible.
  */
-
-#ifndef        SELFPOWER
-/* default: say we're self-powered */
-#define SELFPOWER USB_CONFIG_ATT_SELFPOWER
-/* else:
- * - SELFPOWER value must be zero
- * - MAX_USB_POWER may be nonzero.
- */
-#endif
-
-#ifndef        MAX_USB_POWER
-/* any hub supports this steady state bus power consumption */
-#define MAX_USB_POWER  100     /* mA */
-#endif
-
-#ifndef        WAKEUP
-/* default: this driver won't do remote wakeup */
-#define WAKEUP         0
-/* else value must be USB_CONFIG_ATT_WAKEUP */
-#endif
+static const char *EP_IN_NAME;         /* source */
+static const char *EP_OUT_NAME;                /* sink */
 
 /*-------------------------------------------------------------------------*/
 
@@ -291,7 +172,8 @@
  * Normally the "loopback" configuration is second (index 1) so
  * it's not the default.  Here's where to change that order, to
  * work better with hosts (like Linux ... for now!) where config
- * changes are problematic.
+ * changes are problematic.  Or controllers (like superh) that
+ * only support one configuration.
  */
 static int loopdefault = 0;
 
@@ -301,7 +183,7 @@
 
 /* Thanks to NetChip Technologies for donating this product ID.
  *
- * DO NOT REUSE THESE IDs with any other driver!!  Ever!!
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!
  * Instead:  allocate your own, using normal USB-IF procedures.
  */
 #define DRIVER_VENDOR_NUM      0x0525          /* NetChip */
@@ -337,7 +219,6 @@
 
        .idVendor =             __constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
        .idProduct =            __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
-       .bcdDevice =            __constant_cpu_to_le16 (DRIVER_VERSION_NUM),
        .iManufacturer =        STRING_MANUFACTURER,
        .iProduct =             STRING_PRODUCT,
        .iSerialNumber =        STRING_SERIAL,
@@ -353,8 +234,8 @@
        .bNumInterfaces =       1,
        .bConfigurationValue =  CONFIG_SOURCE_SINK,
        .iConfiguration =       STRING_SOURCE_SINK,
-       .bmAttributes =         USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP,
-       .bMaxPower =            (MAX_USB_POWER + 1) / 2,
+       .bmAttributes =         USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+       .bMaxPower =            1,      /* self-powered */
 };
 
 static const struct usb_config_descriptor
@@ -366,8 +247,8 @@
        .bNumInterfaces =       1,
        .bConfigurationValue =  CONFIG_LOOPBACK,
        .iConfiguration =       STRING_LOOPBACK,
-       .bmAttributes =         USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP,
-       .bMaxPower =            (MAX_USB_POWER + 1) / 2,
+       .bmAttributes =         USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+       .bMaxPower =            1,      /* self-powered */
 };
 
 /* one interface in each configuration */
@@ -394,27 +275,39 @@
 
 /* two full speed bulk endpoints; their use is config-dependent */
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 fs_source_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,
 
-       .bEndpointAddress =     EP_IN_NUM | USB_DIR_IN,
+       .bEndpointAddress =     USB_DIR_IN,
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       __constant_cpu_to_le16 (64),
 };
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 fs_sink_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,
 
-       .bEndpointAddress =     EP_OUT_NUM,
+       .bEndpointAddress =     USB_DIR_OUT,
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       __constant_cpu_to_le16 (64),
 };
 
-#ifdef HIGHSPEED
+static const struct usb_descriptor_header *fs_source_sink_function [] = {
+       (struct usb_descriptor_header *) &source_sink_intf,
+       (struct usb_descriptor_header *) &fs_sink_desc,
+       (struct usb_descriptor_header *) &fs_source_desc,
+       0,
+};
+
+static const struct usb_descriptor_header *fs_loopback_function [] = {
+       (struct usb_descriptor_header *) &loopback_intf,
+       (struct usb_descriptor_header *) &fs_sink_desc,
+       (struct usb_descriptor_header *) &fs_source_desc,
+       0,
+};
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
 
 /*
  * usb 2.0 devices need to expose both high speed and full speed
@@ -425,22 +318,22 @@
  * for the config descriptor.
  */
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 hs_source_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,
 
-       .bEndpointAddress =     EP_IN_NUM | USB_DIR_IN,
+       .bEndpointAddress =     USB_DIR_IN,
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
        .wMaxPacketSize =       __constant_cpu_to_le16 (512),
 };
 
-static const struct usb_endpoint_descriptor
+static struct usb_endpoint_descriptor
 hs_sink_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,
 
-       .bEndpointAddress =     EP_OUT_NUM,
+       .bEndpointAddress =     USB_DIR_OUT,
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
        .wMaxPacketSize =       __constant_cpu_to_le16 (512),
 };
@@ -456,6 +349,20 @@
        .bNumConfigurations =   2,
 };
 
+static const struct usb_descriptor_header *hs_source_sink_function [] = {
+       (struct usb_descriptor_header *) &source_sink_intf,
+       (struct usb_descriptor_header *) &hs_source_desc,
+       (struct usb_descriptor_header *) &hs_sink_desc,
+       0,
+};
+
+static const struct usb_descriptor_header *hs_loopback_function [] = {
+       (struct usb_descriptor_header *) &loopback_intf,
+       (struct usb_descriptor_header *) &hs_source_desc,
+       (struct usb_descriptor_header *) &hs_sink_desc,
+       0,
+};
+
 /* maxpacket and other transfer characteristics vary by speed. */
 #define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs))
 
@@ -464,13 +371,14 @@
 /* if there's no high speed support, maxpacket doesn't change. */
 #define ep_desc(g,hs,fs) fs
 
-#endif /* !HIGHSPEED */
+#endif /* !CONFIG_USB_GADGET_DUALSPEED */
 
+static char                            manufacturer [40];
 static char                            serial [40];
 
 /* static strings, in iso 8859/1 */
 static struct usb_string               strings [] = {
-       { STRING_MANUFACTURER, UTS_SYSNAME " " UTS_RELEASE " with " CHIP, },
+       { STRING_MANUFACTURER, manufacturer, },
        { STRING_PRODUCT, longname, },
        { STRING_SERIAL, serial, },
        { STRING_LOOPBACK, loopback, },
@@ -505,57 +413,41 @@
 config_buf (enum usb_device_speed speed,
                u8 *buf, u8 type, unsigned index)
 {
-       int             is_source_sink;
-       const unsigned  config_len = USB_DT_CONFIG_SIZE
-                               + USB_DT_INTERFACE_SIZE
-                               + 2 * USB_DT_ENDPOINT_SIZE;
-#ifdef HIGHSPEED
-       int             hs;
+       int                             is_source_sink;
+       int                             len;
+       const struct usb_descriptor_header **function;
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+       int                             hs = (speed == USB_SPEED_HIGH);
 #endif
+
        /* two configurations will always be index 0 and index 1 */
        if (index > 1)
                return -EINVAL;
-       if (config_len > USB_BUFSIZ)
-               return -EDOM;
+       if (gadget_is_sh(g) && index != 0)
+               return -EINVAL;
        is_source_sink = loopdefault ? (index == 1) : (index == 0);
 
-       /* config (or other speed config) */
-       if (is_source_sink)
-               memcpy (buf, &source_sink_config, USB_DT_CONFIG_SIZE);
-       else
-               memcpy (buf, &loopback_config, USB_DT_CONFIG_SIZE);
-       buf [1] = type;
-       ((struct usb_config_descriptor *) buf)->wTotalLength
-               = __constant_cpu_to_le16 (config_len);
-       buf += USB_DT_CONFIG_SIZE;
-
-       /* one interface */
-       if (is_source_sink)
-               memcpy (buf, &source_sink_intf, USB_DT_INTERFACE_SIZE);
-       else
-               memcpy (buf, &loopback_intf, USB_DT_INTERFACE_SIZE);
-       buf += USB_DT_INTERFACE_SIZE;
-
-       /* the endpoints in that interface (at that speed) */
-#ifdef HIGHSPEED
-       hs = (speed == USB_SPEED_HIGH);
+#ifdef CONFIG_USB_GADGET_DUALSPEED
        if (type == USB_DT_OTHER_SPEED_CONFIG)
                hs = !hs;
-       if (hs) {
-               memcpy (buf, &hs_source_desc, USB_DT_ENDPOINT_SIZE);
-               buf += USB_DT_ENDPOINT_SIZE;
-               memcpy (buf, &hs_sink_desc, USB_DT_ENDPOINT_SIZE);
-               buf += USB_DT_ENDPOINT_SIZE;
-       } else
+       if (hs)
+               function = is_source_sink
+                       ? hs_source_sink_function
+                       : hs_loopback_function;
+       else
 #endif
-       {
-               memcpy (buf, &fs_source_desc, USB_DT_ENDPOINT_SIZE);
-               buf += USB_DT_ENDPOINT_SIZE;
-               memcpy (buf, &fs_sink_desc, USB_DT_ENDPOINT_SIZE);
-               buf += USB_DT_ENDPOINT_SIZE;
-       }
-
-       return config_len;
+               function = is_source_sink
+                       ? fs_source_sink_function
+                       : fs_loopback_function;
+
+       len = usb_gadget_config_buf (is_source_sink
+                                       ? &source_sink_config
+                                       : &loopback_config,
+                       buf, USB_BUFSIZ, function);
+       if (len < 0)
+               return len;
+       ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+       return len;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1019,15 +911,19 @@
                        value = min (ctrl->wLength, (u16) sizeof device_desc);
                        memcpy (req->buf, &device_desc, value);
                        break;
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
                case USB_DT_DEVICE_QUALIFIER:
+                       if (!gadget_is_dualspeed (gadget))
+                               break;
                        value = min (ctrl->wLength, (u16) sizeof dev_qualifier);
                        memcpy (req->buf, &dev_qualifier, value);
                        break;
 
                case USB_DT_OTHER_SPEED_CONFIG:
+                       if (!gadget_is_dualspeed (gadget))
+                               break;
                        // FALLTHROUGH
-#endif /* HIGHSPEED */
+#endif /* CONFIG_USB_GADGET_DUALSPEED */
                case USB_DT_CONFIG:
                        value = config_buf (gadget->speed, req->buf,
                                        ctrl->wValue >> 8,
@@ -1191,7 +1087,54 @@
 zero_bind (struct usb_gadget *gadget)
 {
        struct zero_dev         *dev;
+       struct usb_ep           *ep;
 
+       /* Bulk-only drivers like this one SHOULD be able to
+        * autoconfigure on any sane usb controller driver.
+        */
+       usb_ep_autoconfig_reset (gadget);
+       ep = usb_ep_autoconfig (gadget, &fs_source_desc);
+       if (!ep) {
+autoconf_fail:
+               pr_info ("%s: can't autoconfigure on %s\n",
+                       shortname, gadget->name);
+               return -ENODEV;
+       }
+       EP_IN_NAME = ep->name;
+       ep->driver_data = ep;   /* claim */
+       
+       ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
+       if (!ep)
+               goto autoconf_fail;
+       EP_OUT_NAME = ep->name;
+       ep->driver_data = ep;   /* claim */
+
+       if (gadget_is_net2280 (gadget)) {
+               device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201);
+       } else if (gadget_is_pxa (gadget)) {
+               device_desc.bcdDevice = __constant_cpu_to_le16 (0x0203);
+       } else if (gadget_is_sh(gadget)) {
+               device_desc.bcdDevice = __constant_cpu_to_le16 (0x0204);
+               /* SH has only one configuration; see "loopdefault" */
+               device_desc.bNumConfigurations = 1;
+#if 0
+       } else if (gadget_is_sa1100 (gadget)) {
+               device_desc.bcdDevice = __constant_cpu_to_le16 (0x0205);
+#endif
+       } else if (gadget_is_goku (gadget)) {
+               device_desc.bcdDevice = __constant_cpu_to_le16 (0x0206);
+       } else if (gadget_is_mq11xx (gadget)) {
+               device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207);
+       } else if (gadget_is_omap (gadget)) {
+               device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208);
+       } else
+               goto autoconf_fail;
+
+       snprintf (manufacturer, sizeof manufacturer,
+               UTS_SYSNAME " " UTS_RELEASE " with %s",
+               gadget->name);
+
+       /* ok, we made sense of the hardware ... */
        dev = kmalloc (sizeof *dev, SLAB_KERNEL);
        if (!dev)
                return -ENOMEM;
@@ -1212,14 +1155,22 @@
        dev->req->complete = zero_setup_complete;
 
        device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
-#ifdef HIGHSPEED
-       /* assume ep0 uses the same value for both speeds ... */
-       dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+       if (gadget_is_dualspeed (gadget)) {
+               /* assume ep0 uses the same value for both speeds ... */
+               dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
+               /* and that all endpoints are dual-speed */
+               hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
+               hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
+       }
 #endif
 
        gadget->ep0->driver_data = dev;
 
        INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname);
+       INFO (dev, "using %s, %s and %s\n", gadget->name,
+               EP_IN_NAME, EP_OUT_NAME);
        return 0;
 
 enomem:
@@ -1230,7 +1181,7 @@
 /*-------------------------------------------------------------------------*/
 
 static struct usb_gadget_driver zero_driver = {
-#ifdef HIGHSPEED
+#ifdef CONFIG_USB_GADGET_DUALSPEED
        .speed          = USB_SPEED_HIGH,
 #else
        .speed          = USB_SPEED_FULL,
--- 1.31/drivers/usb/gadget/Kconfig     Mon Feb  2 15:28:59 2004
+++ edited/drivers/usb/gadget/Kconfig   Fri Mar  5 16:00:10 2004
@@ -43,6 +43,7 @@
 config USB_GADGET_NET2280
        boolean "NetChip 2280"
        depends on PCI
+       select USB_GADGET_DUALSPEED
        help
           NetChip 2280 is a PCI based USB peripheral controller which
           supports both full and high speed USB 2.0 data transfers.  
@@ -153,6 +154,13 @@
 
 endchoice
 
+config USB_GADGET_DUALSPEED
+       bool
+       depends on USB_GADGET
+       default n
+       help
+         Forced to true when auto-configuring gadget drivers need
+         to handle dual-speed controllers.
 
 #
 # USB Gadget Drivers
--- 1.10/drivers/usb/gadget/Makefile    Sat Feb 28 17:23:05 2004
+++ edited/drivers/usb/gadget/Makefile  Fri Mar  5 16:00:10 2004
@@ -9,7 +9,7 @@
 #
 # USB gadget drivers
 #
-g_zero-objs                    := zero.o usbstring.o
+g_zero-objs                    := zero.o usbstring.o config.o epautoconf.o
 g_ether-objs                   := ether.o usbstring.o config.o
 g_serial-objs                  := serial.o usbstring.o
 gadgetfs-objs                  := inode.o usbstring.o

Reply via email to