]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
usb gadget zero: use composite gadget framework
authorDavid Brownell <dbrownell@users.sourceforge.net>
Fri, 20 Jun 2008 01:18:27 +0000 (18:18 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 21 Jul 2008 22:16:04 +0000 (15:16 -0700)
Update Gadget Zero to use the more modular versions of the loopback
and source/sink configuration drivers which build on the new gadget
framework code.

The core code is a LOT simpler, and it should be much easier now to
understand how the parts fit together.  The conversion is an overall
source shrink in terms of this gadget, since it uses more midlayer
support.  However, it's an overall increase in object size because
there's less sharing between the two configurations (improves code
clarity) and because the midlayer is a bit more functional than this
driver actually needs.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/gadget/Makefile
drivers/usb/gadget/zero.c

index 16211435761f0c44ea66603d2cabe9e83168444c..fe60cca704d963c1c61eacb56640464493d3cf63 100644 (file)
@@ -22,7 +22,9 @@ obj-$(CONFIG_USB_M66592)      += m66592-udc.o
 #
 # USB gadget drivers
 #
-g_zero-objs                    := zero.o usbstring.o config.o epautoconf.o
+C_UTILS =      composite.o usbstring.o config.o epautoconf.o
+
+g_zero-objs                    := zero.o f_sourcesink.o f_loopback.o $(C_UTILS)
 g_ether-objs                   := ether.o usbstring.o config.o epautoconf.o
 g_serial-objs                  := serial.o u_serial.o usbstring.o config.o epautoconf.o
 g_midi-objs                    := gmidi.o usbstring.o config.o epautoconf.o
index fce4924dbbe88b6c49dbea3fe33221e224aece05..aa0bd4f126a1e5f7892e1ce645b187d88e4ea7f1 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * zero.c -- Gadget Zero, for USB development
  *
- * Copyright (C) 2003-2007 David Brownell
- * All rights reserved.
+ * Copyright (C) 2003-2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  *
  * It supports two similar configurations.  One sinks whatever the usb host
  * writes, and in return sources zeroes.  The other loops whatever the host
- * writes back, so the host can read it.  Module options include:
- *
- *   buflen=N          default N=4096, buffer size used
- *   qlen=N            default N=32, how many buffers in the loopback queue
- *   loopdefault       default false, list loopback config first
- *   autoresume=N      default N=0, seconds before triggering remote wakeup
+ * writes back, so the host can read it.
  *
  * Many drivers will only have one configuration, letting them be much
  * simpler if they also don't support high speed operation (like this
  * work with low capability USB controllers without four bulk endpoints.
  */
 
+/*
+ * driver assumes self-powered hardware, and
+ * has no way for users to trigger remote wakeup.
+ */
+
 /* #define VERBOSE_DEBUG */
 
 #include <linux/kernel.h>
 #include <linux/utsname.h>
 #include <linux/device.h>
 
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-
+#include "g_zero.h"
 #include "gadget_chips.h"
 
 
 /*-------------------------------------------------------------------------*/
 
-#define DRIVER_VERSION         "Earth Day 2008"
+#define DRIVER_VERSION         "Cinco de Mayo 2008"
 
-static const char shortname[] = "zero";
 static const char longname[] = "Gadget Zero";
 
-static const char source_sink[] = "source and sink data";
-static const char loopback[] = "loop input to output";
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * driver assumes self-powered hardware, and
- * has no way for users to trigger remote wakeup.
- *
- * this version autoconfigures as much as possible,
- * which is reasonable for most "bulk-only" drivers.
- */
-static const char *EP_IN_NAME;         /* source */
-static const char *EP_OUT_NAME;                /* sink */
-
-/*-------------------------------------------------------------------------*/
-
-/* big enough to hold our biggest descriptor */
-#define USB_BUFSIZ     256
-
-struct zero_dev {
-       spinlock_t              lock;
-       struct usb_gadget       *gadget;
-       struct usb_request      *req;           /* for control responses */
-
-       /* when configured, we have one of two configs:
-        * - source data (in to host) and sink it (out from host)
-        * - or loop it back (out from host back in to host)
-        */
-       u8                      config;
-       struct usb_ep           *in_ep, *out_ep;
-
-       /* autoresume timer */
-       struct timer_list       resume;
-};
-
-#define DBG(d, fmt, args...) \
-       dev_dbg(&(d)->gadget->dev , fmt , ## args)
-#define VDBG(d, fmt, args...) \
-       dev_vdbg(&(d)->gadget->dev , fmt , ## args)
-#define ERROR(d, fmt, args...) \
-       dev_err(&(d)->gadget->dev , fmt , ## args)
-#define WARN(d, fmt, args...) \
-       dev_warn(&(d)->gadget->dev , fmt , ## args)
-#define INFO(d, fmt, args...) \
-       dev_info(&(d)->gadget->dev , fmt , ## args)
-
-/*-------------------------------------------------------------------------*/
-
-static unsigned buflen = 4096;
-static unsigned qlen = 32;
-static unsigned pattern = 0;
-
-module_param(buflen, uint, S_IRUGO);
-module_param(qlen, uint, S_IRUGO);
-module_param(pattern, uint, S_IRUGO|S_IWUSR);
-
-/*
- * if it's nonzero, autoresume says how many seconds to wait
- * before trying to wake up the host after suspend.
- */
-static unsigned autoresume = 0;
-module_param(autoresume, uint, 0);
+unsigned buflen = 4096;
+module_param(buflen, uint, 0);
 
 /*
  * 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 where config changes are problematic.
- * Or controllers (like superh) that only support one config.
+ * work better with hosts where config changes are problematic or
+ * controllers (like original superh) that only support one config.
  */
 static int loopdefault = 0;
 module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
@@ -156,24 +92,6 @@ module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
 
 /*-------------------------------------------------------------------------*/
 
-/*
- * DESCRIPTORS ... most are static, but strings and (full)
- * configuration descriptors are built on demand.
- */
-
-#define STRING_MANUFACTURER            25
-#define STRING_PRODUCT                 42
-#define STRING_SERIAL                  101
-#define STRING_SOURCE_SINK             250
-#define STRING_LOOPBACK                        251
-
-/*
- * This device advertises two configurations; these numbers work
- * on a pxa250 as well as more flexible hardware.
- */
-#define        CONFIG_SOURCE_SINK      3
-#define        CONFIG_LOOPBACK         2
-
 static struct usb_device_descriptor device_desc = {
        .bLength =              sizeof device_desc,
        .bDescriptorType =      USB_DT_DEVICE,
@@ -183,248 +101,64 @@ static struct usb_device_descriptor device_desc = {
 
        .idVendor =             __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
        .idProduct =            __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
-       .iManufacturer =        STRING_MANUFACTURER,
-       .iProduct =             STRING_PRODUCT,
-       .iSerialNumber =        STRING_SERIAL,
        .bNumConfigurations =   2,
 };
 
-static struct usb_config_descriptor source_sink_config = {
-       .bLength =              sizeof source_sink_config,
-       .bDescriptorType =      USB_DT_CONFIG,
-
-       /* compute wTotalLength on the fly */
-       .bNumInterfaces =       1,
-       .bConfigurationValue =  CONFIG_SOURCE_SINK,
-       .iConfiguration =       STRING_SOURCE_SINK,
-       .bmAttributes =         USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
-       .bMaxPower =            1,      /* self-powered */
-};
-
-static struct usb_config_descriptor loopback_config = {
-       .bLength =              sizeof loopback_config,
-       .bDescriptorType =      USB_DT_CONFIG,
-
-       /* compute wTotalLength on the fly */
-       .bNumInterfaces =       1,
-       .bConfigurationValue =  CONFIG_LOOPBACK,
-       .iConfiguration =       STRING_LOOPBACK,
-       .bmAttributes =         USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
-       .bMaxPower =            1,      /* self-powered */
-};
-
+#ifdef CONFIG_USB_OTG
 static struct usb_otg_descriptor otg_descriptor = {
        .bLength =              sizeof otg_descriptor,
        .bDescriptorType =      USB_DT_OTG,
 
-       .bmAttributes =         USB_OTG_SRP,
-};
-
-/* one interface in each configuration */
-
-static const struct usb_interface_descriptor source_sink_intf = {
-       .bLength =              sizeof source_sink_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
-       .iInterface =           STRING_SOURCE_SINK,
-};
-
-static const struct usb_interface_descriptor loopback_intf = {
-       .bLength =              sizeof loopback_intf,
-       .bDescriptorType =      USB_DT_INTERFACE,
-
-       .bNumEndpoints =        2,
-       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
-       .iInterface =           STRING_LOOPBACK,
-};
-
-/* two full speed bulk endpoints; their use is config-dependent */
-
-static struct usb_endpoint_descriptor fs_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_IN,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor fs_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bEndpointAddress =     USB_DIR_OUT,
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-};
-
-static const struct usb_descriptor_header *fs_source_sink_function[] = {
-       (struct usb_descriptor_header *) &otg_descriptor,
-       (struct usb_descriptor_header *) &source_sink_intf,
-       (struct usb_descriptor_header *) &fs_sink_desc,
-       (struct usb_descriptor_header *) &fs_source_desc,
-       NULL,
-};
-
-static const struct usb_descriptor_header *fs_loopback_function[] = {
-       (struct usb_descriptor_header *) &otg_descriptor,
-       (struct usb_descriptor_header *) &loopback_intf,
-       (struct usb_descriptor_header *) &fs_sink_desc,
-       (struct usb_descriptor_header *) &fs_source_desc,
-       NULL,
-};
-
-/*
- * usb 2.0 devices need to expose both high speed and full speed
- * descriptors, unless they only run at full speed.
- *
- * that means alternate endpoint descriptors (bigger packets)
- * and a "device qualifier" ... plus more construction options
- * for the config descriptor.
- */
-
-static struct usb_endpoint_descriptor hs_source_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       __constant_cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor hs_sink_desc = {
-       .bLength =              USB_DT_ENDPOINT_SIZE,
-       .bDescriptorType =      USB_DT_ENDPOINT,
-
-       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
-       .wMaxPacketSize =       __constant_cpu_to_le16(512),
-};
-
-static struct usb_qualifier_descriptor dev_qualifier = {
-       .bLength =              sizeof dev_qualifier,
-       .bDescriptorType =      USB_DT_DEVICE_QUALIFIER,
-
-       .bcdUSB =               __constant_cpu_to_le16(0x0200),
-       .bDeviceClass =         USB_CLASS_VENDOR_SPEC,
-
-       .bNumConfigurations =   2,
+       /* REVISIT SRP-only hardware is possible, although
+        * it would not be called "OTG" ...
+        */
+       .bmAttributes =         USB_OTG_SRP | USB_OTG_HNP,
 };
 
-static const struct usb_descriptor_header *hs_source_sink_function[] = {
+const struct usb_descriptor_header *otg_desc[] = {
        (struct usb_descriptor_header *) &otg_descriptor,
-       (struct usb_descriptor_header *) &source_sink_intf,
-       (struct usb_descriptor_header *) &hs_source_desc,
-       (struct usb_descriptor_header *) &hs_sink_desc,
        NULL,
 };
+#endif
 
-static const struct usb_descriptor_header *hs_loopback_function[] = {
-       (struct usb_descriptor_header *) &otg_descriptor,
-       (struct usb_descriptor_header *) &loopback_intf,
-       (struct usb_descriptor_header *) &hs_source_desc,
-       (struct usb_descriptor_header *) &hs_sink_desc,
-       NULL,
-};
+/* string IDs are assigned dynamically */
 
-/* maxpacket and other transfer characteristics vary by speed. */
-static inline struct usb_endpoint_descriptor *
-ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
-               struct usb_endpoint_descriptor *fs)
-{
-       if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
-               return hs;
-       return fs;
-}
+#define STRING_MANUFACTURER_IDX                0
+#define STRING_PRODUCT_IDX             1
+#define STRING_SERIAL_IDX              2
 
 static char manufacturer[50];
 
 /* default serial number takes at least two packets */
 static char serial[] = "0123456789.0123456789.0123456789";
 
-
-/* static strings, in UTF-8 */
-static struct usb_string strings[] = {
-       { STRING_MANUFACTURER, manufacturer, },
-       { STRING_PRODUCT, longname, },
-       { STRING_SERIAL, serial, },
-       { STRING_LOOPBACK, loopback, },
-       { STRING_SOURCE_SINK, source_sink, },
+static struct usb_string strings_dev[] = {
+       [STRING_MANUFACTURER_IDX].s = manufacturer,
+       [STRING_PRODUCT_IDX].s = longname,
+       [STRING_SERIAL_IDX].s = serial,
        {  }                    /* end of list */
 };
 
-static struct usb_gadget_strings stringtab = {
+static struct usb_gadget_strings stringtab_dev = {
        .language       = 0x0409,       /* en-us */
-       .strings        = strings,
+       .strings        = strings_dev,
 };
 
-/*
- * config descriptors are also handcrafted.  these must agree with code
- * that sets configurations, and with code managing interfaces and their
- * altsettings.  other complexity may come from:
- *
- *  - high speed support, including "other speed config" rules
- *  - multiple configurations
- *  - interfaces with alternate settings
- *  - embedded class or vendor-specific descriptors
- *
- * this handles high speed, and has a second config that could as easily
- * have been an alternate interface setting (on most hardware).
- *
- * NOTE:  to demonstrate (and test) more USB capabilities, this driver
- * should include an altsetting to test interrupt transfers, including
- * high bandwidth modes at high speed.  (Maybe work like Intel's test
- * device?)
- */
-static int config_buf(struct usb_gadget *gadget,
-               u8 *buf, u8 type, unsigned index)
-{
-       int                             is_source_sink;
-       int                             len;
-       const struct usb_descriptor_header **function;
-       int                             hs = 0;
-
-       /* two configurations will always be index 0 and index 1 */
-       if (index > 1)
-               return -EINVAL;
-       is_source_sink = loopdefault ? (index == 1) : (index == 0);
-
-       if (gadget_is_dualspeed(gadget)) {
-               hs = (gadget->speed == USB_SPEED_HIGH);
-               if (type == USB_DT_OTHER_SPEED_CONFIG)
-                       hs = !hs;
-       }
-       if (hs)
-               function = is_source_sink
-                       ? hs_source_sink_function
-                       : hs_loopback_function;
-       else
-               function = is_source_sink
-                       ? fs_source_sink_function
-                       : fs_loopback_function;
-
-       /* for now, don't advertise srp-only devices */
-       if (!gadget_is_otg(gadget))
-               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;
-}
+static struct usb_gadget_strings *dev_strings[] = {
+       &stringtab_dev,
+       NULL,
+};
 
 /*-------------------------------------------------------------------------*/
 
-static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
+struct usb_request *alloc_ep_req(struct usb_ep *ep)
 {
        struct usb_request      *req;
 
        req = usb_ep_alloc_request(ep, GFP_ATOMIC);
        if (req) {
-               req->length = length;
-               req->buf = kmalloc(length, GFP_ATOMIC);
+               req->length = buflen;
+               req->buf = kmalloc(buflen, GFP_ATOMIC);
                if (!req->buf) {
                        usb_ep_free_request(ep, req);
                        req = NULL;
@@ -433,681 +167,73 @@ static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
        return req;
 }
 
-static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+void free_ep_req(struct usb_ep *ep, struct usb_request *req)
 {
        kfree(req->buf);
        usb_ep_free_request(ep, req);
 }
 
-/*-------------------------------------------------------------------------*/
-
-/*
- * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripherals,
- * this just sinks bulk packets OUT to the peripheral and sources them IN
- * to the host, optionally with specific data patterns.
- *
- * In terms of control messaging, this supports all the standard requests
- * plus two that support control-OUT tests.
- *
- * Note that because this doesn't queue more than one request at a time,
- * some other function must be used to test queueing logic.  The network
- * link (g_ether) is probably the best option for that.
- */
-
-/* optionally require specific source/sink data patterns  */
-
-static int
-check_read_data(
-       struct zero_dev         *dev,
-       struct usb_ep           *ep,
-       struct usb_request      *req
-)
+static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
 {
-       unsigned        i;
-       u8              *buf = req->buf;
-
-       for (i = 0; i < req->actual; i++, buf++) {
-               switch (pattern) {
-               /* all-zeroes has no synchronization issues */
-               case 0:
-                       if (*buf == 0)
-                               continue;
-                       break;
-               /* mod63 stays in sync with short-terminated transfers,
-                * or otherwise when host and gadget agree on how large
-                * each usb transfer request should be.  resync is done
-                * with set_interface or set_config.
-                */
-               case 1:
-                       if (*buf == (u8)(i % 63))
-                               continue;
-                       break;
-               }
-               ERROR(dev, "bad OUT byte, buf[%d] = %d\n", i, *buf);
-               usb_ep_set_halt(ep);
-               return -EINVAL;
+       int                     value;
+
+       if (ep->driver_data) {
+               value = usb_ep_disable(ep);
+               if (value < 0)
+                       DBG(cdev, "disable %s --> %d\n",
+                                       ep->name, value);
+               ep->driver_data = NULL;
        }
-       return 0;
 }
 
-static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
+void disable_endpoints(struct usb_composite_dev *cdev,
+               struct usb_ep *in, struct usb_ep *out)
 {
-       unsigned        i;
-       u8              *buf = req->buf;
-
-       switch (pattern) {
-       case 0:
-               memset(req->buf, 0, req->length);
-               break;
-       case 1:
-               for  (i = 0; i < req->length; i++)
-                       *buf++ = (u8) (i % 63);
-               break;
-       }
-}
-
-/* if there is only one request in the queue, there'll always be an
- * irq delay between end of one request and start of the next.
- * that prevents using hardware dma queues.
- */
-static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct zero_dev *dev = ep->driver_data;
-       int             status = req->status;
-
-       switch (status) {
-
-       case 0:                         /* normal completion? */
-               if (ep == dev->out_ep) {
-                       check_read_data(dev, ep, req);
-                       memset(req->buf, 0x55, req->length);
-               } else
-                       reinit_write_data(ep, req);
-               break;
-
-       /* this endpoint is normally active while we're configured */
-       case -ECONNABORTED:             /* hardware forced ep reset */
-       case -ECONNRESET:               /* request dequeued */
-       case -ESHUTDOWN:                /* disconnect from host */
-               VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status,
-                               req->actual, req->length);
-               if (ep == dev->out_ep)
-                       check_read_data(dev, ep, req);
-               free_ep_req(ep, req);
-               return;
-
-       case -EOVERFLOW:                /* buffer overrun on read means that
-                                        * we didn't provide a big enough
-                                        * buffer.
-                                        */
-       default:
-#if 1
-               DBG(dev, "%s complete --> %d, %d/%d\n", ep->name,
-                               status, req->actual, req->length);
-#endif
-       case -EREMOTEIO:                /* short read */
-               break;
-       }
-
-       status = usb_ep_queue(ep, req, GFP_ATOMIC);
-       if (status) {
-               ERROR(dev, "kill %s:  resubmit %d bytes --> %d\n",
-                               ep->name, req->length, status);
-               usb_ep_set_halt(ep);
-               /* FIXME recover later ... somehow */
-       }
-}
-
-static struct usb_request *source_sink_start_ep(struct usb_ep *ep)
-{
-       struct usb_request      *req;
-       int                     status;
-
-       req = alloc_ep_req(ep, buflen);
-       if (!req)
-               return NULL;
-
-       memset(req->buf, 0, req->length);
-       req->complete = source_sink_complete;
-
-       if (strcmp(ep->name, EP_IN_NAME) == 0)
-               reinit_write_data(ep, req);
-       else
-               memset(req->buf, 0x55, req->length);
-
-       status = usb_ep_queue(ep, req, GFP_ATOMIC);
-       if (status) {
-               struct zero_dev *dev = ep->driver_data;
-
-               ERROR(dev, "start %s --> %d\n", ep->name, status);
-               free_ep_req(ep, req);
-               req = NULL;
-       }
-
-       return req;
-}
-
-static int set_source_sink_config(struct zero_dev *dev)
-{
-       int                     result = 0;
-       struct usb_ep           *ep;
-       struct usb_gadget       *gadget = dev->gadget;
-
-       gadget_for_each_ep(ep, gadget) {
-               const struct usb_endpoint_descriptor    *d;
-
-               /* one endpoint writes (sources) zeroes in (to the host) */
-               if (strcmp(ep->name, EP_IN_NAME) == 0) {
-                       d = ep_desc(gadget, &hs_source_desc, &fs_source_desc);
-                       result = usb_ep_enable(ep, d);
-                       if (result == 0) {
-                               ep->driver_data = dev;
-                               if (source_sink_start_ep(ep) != NULL) {
-                                       dev->in_ep = ep;
-                                       continue;
-                               }
-                               usb_ep_disable(ep);
-                               result = -EIO;
-                       }
-
-               /* one endpoint reads (sinks) anything out (from the host) */
-               } else if (strcmp(ep->name, EP_OUT_NAME) == 0) {
-                       d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc);
-                       result = usb_ep_enable(ep, d);
-                       if (result == 0) {
-                               ep->driver_data = dev;
-                               if (source_sink_start_ep(ep) != NULL) {
-                                       dev->out_ep = ep;
-                                       continue;
-                               }
-                               usb_ep_disable(ep);
-                               result = -EIO;
-                       }
-
-               /* ignore any other endpoints */
-               } else
-                       continue;
-
-               /* stop on error */
-               ERROR(dev, "can't start %s, result %d\n", ep->name, result);
-               break;
-       }
-       if (result == 0)
-               DBG(dev, "buflen %d\n", buflen);
-
-       /* caller is responsible for cleanup on error */
-       return result;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       struct zero_dev *dev = ep->driver_data;
-       int             status = req->status;
-
-       switch (status) {
-
-       case 0:                         /* normal completion? */
-               if (ep == dev->out_ep) {
-                       /* loop this OUT packet back IN to the host */
-                       req->zero = (req->actual < req->length);
-                       req->length = req->actual;
-                       status = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC);
-                       if (status == 0)
-                               return;
-
-                       /* "should never get here" */
-                       ERROR(dev, "can't loop %s to %s: %d\n",
-                               ep->name, dev->in_ep->name,
-                               status);
-               }
-
-               /* queue the buffer for some later OUT packet */
-               req->length = buflen;
-               status = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
-               if (status == 0)
-                       return;
-
-               /* "should never get here" */
-               /* FALLTHROUGH */
-
-       default:
-               ERROR(dev, "%s loop complete --> %d, %d/%d\n", ep->name,
-                               status, req->actual, req->length);
-               /* FALLTHROUGH */
-
-       /* NOTE:  since this driver doesn't maintain an explicit record
-        * of requests it submitted (just maintains qlen count), we
-        * rely on the hardware driver to clean up on disconnect or
-        * endpoint disable.
-        */
-       case -ECONNABORTED:             /* hardware forced ep reset */
-       case -ECONNRESET:               /* request dequeued */
-       case -ESHUTDOWN:                /* disconnect from host */
-               free_ep_req(ep, req);
-               return;
-       }
-}
-
-static int set_loopback_config(struct zero_dev *dev)
-{
-       int                     result = 0;
-       struct usb_ep           *ep;
-       struct usb_gadget       *gadget = dev->gadget;
-
-       gadget_for_each_ep(ep, gadget) {
-               const struct usb_endpoint_descriptor    *d;
-
-               /* one endpoint writes data back IN to the host */
-               if (strcmp(ep->name, EP_IN_NAME) == 0) {
-                       d = ep_desc(gadget, &hs_source_desc, &fs_source_desc);
-                       result = usb_ep_enable(ep, d);
-                       if (result == 0) {
-                               ep->driver_data = dev;
-                               dev->in_ep = ep;
-                               continue;
-                       }
-
-               /* one endpoint just reads OUT packets */
-               } else if (strcmp(ep->name, EP_OUT_NAME) == 0) {
-                       d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc);
-                       result = usb_ep_enable(ep, d);
-                       if (result == 0) {
-                               ep->driver_data = dev;
-                               dev->out_ep = ep;
-                               continue;
-                       }
-
-               /* ignore any other endpoints */
-               } else
-                       continue;
-
-               /* stop on error */
-               ERROR(dev, "can't enable %s, result %d\n", ep->name, result);
-               break;
-       }
-
-       /* allocate a bunch of read buffers and queue them all at once.
-        * we buffer at most 'qlen' transfers; fewer if any need more
-        * than 'buflen' bytes each.
-        */
-       if (result == 0) {
-               struct usb_request      *req;
-               unsigned                i;
-
-               ep = dev->out_ep;
-               for (i = 0; i < qlen && result == 0; i++) {
-                       req = alloc_ep_req(ep, buflen);
-                       if (req) {
-                               req->complete = loopback_complete;
-                               result = usb_ep_queue(ep, req, GFP_ATOMIC);
-                               if (result)
-                                       DBG(dev, "%s queue req --> %d\n",
-                                                       ep->name, result);
-                       } else
-                               result = -ENOMEM;
-               }
-       }
-       if (result == 0)
-               DBG(dev, "qlen %d, buflen %d\n", qlen, buflen);
-
-       /* caller is responsible for cleanup on error */
-       return result;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void zero_reset_config(struct zero_dev *dev)
-{
-       if (dev->config == 0)
-               return;
-
-       DBG(dev, "reset config\n");
-
-       /* just disable endpoints, forcing completion of pending i/o.
-        * all our completion handlers free their requests in this case.
-        */
-       if (dev->in_ep) {
-               usb_ep_disable(dev->in_ep);
-               dev->in_ep = NULL;
-       }
-       if (dev->out_ep) {
-               usb_ep_disable(dev->out_ep);
-               dev->out_ep = NULL;
-       }
-       dev->config = 0;
-       del_timer(&dev->resume);
-}
-
-/* change our operational config.  this code must agree with the code
- * that returns config descriptors, and altsetting code.
- *
- * it's also responsible for power management interactions. some
- * configurations might not work with our current power sources.
- *
- * note that some device controller hardware will constrain what this
- * code can do, perhaps by disallowing more than one configuration or
- * by limiting configuration choices (like the pxa2xx).
- */
-static int zero_set_config(struct zero_dev *dev, unsigned number)
-{
-       int                     result = 0;
-       struct usb_gadget       *gadget = dev->gadget;
-
-       if (number == dev->config)
-               return 0;
-
-       if (gadget_is_sa1100(gadget) && dev->config) {
-               /* tx fifo is full, but we can't clear it...*/
-               ERROR(dev, "can't change configurations\n");
-               return -ESPIPE;
-       }
-       zero_reset_config(dev);
-
-       switch (number) {
-       case CONFIG_SOURCE_SINK:
-               result = set_source_sink_config(dev);
-               break;
-       case CONFIG_LOOPBACK:
-               result = set_loopback_config(dev);
-               break;
-       default:
-               result = -EINVAL;
-               /* FALL THROUGH */
-       case 0:
-               return result;
-       }
-
-       if (!result && (!dev->in_ep || !dev->out_ep))
-               result = -ENODEV;
-       if (result)
-               zero_reset_config(dev);
-       else {
-               char *speed;
-
-               switch (gadget->speed) {
-               case USB_SPEED_LOW:     speed = "low"; break;
-               case USB_SPEED_FULL:    speed = "full"; break;
-               case USB_SPEED_HIGH:    speed = "high"; break;
-               default:                speed = "?"; break;
-               }
-
-               dev->config = number;
-               INFO(dev, "%s speed config #%d: %s\n", speed, number,
-                               (number == CONFIG_SOURCE_SINK)
-                                       ? source_sink : loopback);
-       }
-       return result;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void zero_setup_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       if (req->status || req->actual != req->length)
-               DBG((struct zero_dev *) ep->driver_data,
-                               "setup complete --> %d, %d/%d\n",
-                               req->status, req->actual, req->length);
-}
-
-/*
- * The setup() callback implements all the ep0 functionality that's
- * not handled lower down, in hardware or the hardware driver (like
- * device and endpoint feature flags, and their status).  It's all
- * housekeeping for the gadget function we're implementing.  Most of
- * the work is in config-specific setup.
- */
-static int
-zero_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
-{
-       struct zero_dev         *dev = get_gadget_data(gadget);
-       struct usb_request      *req = dev->req;
-       int                     value = -EOPNOTSUPP;
-       u16                     w_index = le16_to_cpu(ctrl->wIndex);
-       u16                     w_value = le16_to_cpu(ctrl->wValue);
-       u16                     w_length = le16_to_cpu(ctrl->wLength);
-
-       /* usually this stores reply data in the pre-allocated ep0 buffer,
-        * but config change events will reconfigure hardware.
-        */
-       req->zero = 0;
-       switch (ctrl->bRequest) {
-
-       case USB_REQ_GET_DESCRIPTOR:
-               if (ctrl->bRequestType != USB_DIR_IN)
-                       goto unknown;
-               switch (w_value >> 8) {
-
-               case USB_DT_DEVICE:
-                       value = min(w_length, (u16) sizeof device_desc);
-                       memcpy(req->buf, &device_desc, value);
-                       break;
-               case USB_DT_DEVICE_QUALIFIER:
-                       if (!gadget_is_dualspeed(gadget))
-                               break;
-                       value = min(w_length, (u16) sizeof dev_qualifier);
-                       memcpy(req->buf, &dev_qualifier, value);
-                       break;
-
-               case USB_DT_OTHER_SPEED_CONFIG:
-                       if (!gadget_is_dualspeed(gadget))
-                               break;
-                       // FALLTHROUGH
-               case USB_DT_CONFIG:
-                       value = config_buf(gadget, req->buf,
-                                       w_value >> 8,
-                                       w_value & 0xff);
-                       if (value >= 0)
-                               value = min(w_length, (u16) value);
-                       break;
-
-               case USB_DT_STRING:
-                       /* wIndex == language code.
-                        * this driver only handles one language, you can
-                        * add string tables for other languages, using
-                        * any UTF-8 characters
-                        */
-                       value = usb_gadget_get_string(&stringtab,
-                                       w_value & 0xff, req->buf);
-                       if (value >= 0)
-                               value = min(w_length, (u16) value);
-                       break;
-               }
-               break;
-
-       /* currently two configs, two speeds */
-       case USB_REQ_SET_CONFIGURATION:
-               if (ctrl->bRequestType != 0)
-                       goto unknown;
-               if (gadget->a_hnp_support)
-                       DBG(dev, "HNP available\n");
-               else if (gadget->a_alt_hnp_support)
-                       DBG(dev, "HNP needs a different root port\n");
-               else
-                       VDBG(dev, "HNP inactive\n");
-               spin_lock(&dev->lock);
-               value = zero_set_config(dev, w_value);
-               spin_unlock(&dev->lock);
-               break;
-       case USB_REQ_GET_CONFIGURATION:
-               if (ctrl->bRequestType != USB_DIR_IN)
-                       goto unknown;
-               *(u8 *)req->buf = dev->config;
-               value = min(w_length, (u16) 1);
-               break;
-
-       /* until we add altsetting support, or other interfaces,
-        * only 0/0 are possible.  pxa2xx only supports 0/0 (poorly)
-        * and already killed pending endpoint I/O.
-        */
-       case USB_REQ_SET_INTERFACE:
-               if (ctrl->bRequestType != USB_RECIP_INTERFACE)
-                       goto unknown;
-               spin_lock(&dev->lock);
-               if (dev->config && w_index == 0 && w_value == 0) {
-                       u8              config = dev->config;
-
-                       /* resets interface configuration, forgets about
-                        * previous transaction state (queued bufs, etc)
-                        * and re-inits endpoint state (toggle etc)
-                        * no response queued, just zero status == success.
-                        * if we had more than one interface we couldn't
-                        * use this "reset the config" shortcut.
-                        */
-                       zero_reset_config(dev);
-                       zero_set_config(dev, config);
-                       value = 0;
-               }
-               spin_unlock(&dev->lock);
-               break;
-       case USB_REQ_GET_INTERFACE:
-               if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
-                       goto unknown;
-               if (!dev->config)
-                       break;
-               if (w_index != 0) {
-                       value = -EDOM;
-                       break;
-               }
-               *(u8 *)req->buf = 0;
-               value = min(w_length, (u16) 1);
-               break;
-
-       /*
-        * These are the same vendor-specific requests supported by
-        * Intel's USB 2.0 compliance test devices.  We exceed that
-        * device spec by allowing multiple-packet requests.
-        */
-       case 0x5b:      /* control WRITE test -- fill the buffer */
-               if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
-                       goto unknown;
-               if (w_value || w_index)
-                       break;
-               /* just read that many bytes into the buffer */
-               if (w_length > USB_BUFSIZ)
-                       break;
-               value = w_length;
-               break;
-       case 0x5c:      /* control READ test -- return the buffer */
-               if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
-                       goto unknown;
-               if (w_value || w_index)
-                       break;
-               /* expect those bytes are still in the buffer; send back */
-               if (w_length > USB_BUFSIZ
-                               || w_length != req->length)
-                       break;
-               value = w_length;
-               break;
-
-       default:
-unknown:
-               VDBG(dev,
-                       "unknown control req%02x.%02x v%04x i%04x l%d\n",
-                       ctrl->bRequestType, ctrl->bRequest,
-                       w_value, w_index, w_length);
-       }
-
-       /* respond with data transfer before status phase? */
-       if (value >= 0) {
-               req->length = value;
-               req->zero = value < w_length;
-               value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
-               if (value < 0) {
-                       DBG(dev, "ep_queue --> %d\n", value);
-                       req->status = 0;
-                       zero_setup_complete(gadget->ep0, req);
-               }
-       }
-
-       /* device either stalls (value < 0) or reports success */
-       return value;
-}
-
-static void zero_disconnect(struct usb_gadget *gadget)
-{
-       struct zero_dev         *dev = get_gadget_data(gadget);
-       unsigned long           flags;
-
-       spin_lock_irqsave(&dev->lock, flags);
-       zero_reset_config(dev);
-
-       /* a more significant application might have some non-usb
-        * activities to quiesce here, saving resources like power
-        * or pushing the notification up a network stack.
-        */
-       spin_unlock_irqrestore(&dev->lock, flags);
-
-       /* next we may get setup() calls to enumerate new connections;
-        * or an unbind() during shutdown (including removing module).
-        */
-}
-
-static void zero_autoresume(unsigned long _dev)
-{
-       struct zero_dev *dev = (struct zero_dev *) _dev;
-       int             status;
-
-       /* normally the host would be woken up for something
-        * more significant than just a timer firing...
-        */
-       if (dev->gadget->speed != USB_SPEED_UNKNOWN) {
-               status = usb_gadget_wakeup(dev->gadget);
-               DBG(dev, "wakeup --> %d\n", status);
-       }
+       disable_ep(cdev, in);
+       disable_ep(cdev, out);
 }
 
 /*-------------------------------------------------------------------------*/
 
-static void zero_unbind(struct usb_gadget *gadget)
+static int __init zero_bind(struct usb_composite_dev *cdev)
 {
-       struct zero_dev         *dev = get_gadget_data(gadget);
-
-       DBG(dev, "unbind\n");
-
-       /* we've already been disconnected ... no i/o is active */
-       if (dev->req) {
-               dev->req->length = USB_BUFSIZ;
-               free_ep_req(gadget->ep0, dev->req);
-       }
-       del_timer_sync(&dev->resume);
-       kfree(dev);
-       set_gadget_data(gadget, NULL);
-}
-
-static int __init zero_bind(struct usb_gadget *gadget)
-{
-       struct zero_dev         *dev;
-       struct usb_ep           *ep;
        int                     gcnum;
+       struct usb_gadget       *gadget = cdev->gadget;
+       int                     id;
 
-       /* FIXME this can't yet work right with SH ... it has only
-        * one configuration, numbered one.
+       /* Allocate string descriptor numbers ... note that string
+        * contents can be overridden by the composite_dev glue.
         */
-       if (gadget_is_sh(gadget))
-               return -ENODEV;
-
-       /* Bulk-only drivers like this one SHOULD be able to
-        * autoconfigure on any sane usb controller driver,
-        * but there may also be important quirks to address.
+       id = usb_string_id(cdev);
+       if (id < 0)
+               return id;
+       strings_dev[STRING_MANUFACTURER_IDX].id = id;
+       device_desc.iManufacturer = id;
+
+       id = usb_string_id(cdev);
+       if (id < 0)
+               return id;
+       strings_dev[STRING_PRODUCT_IDX].id = id;
+       device_desc.iProduct = id;
+
+       id = usb_string_id(cdev);
+       if (id < 0)
+               return id;
+       strings_dev[STRING_SERIAL_IDX].id = id;
+       device_desc.iSerialNumber = id;
+
+       /* Register primary, then secondary configuration.  Note that
+        * SH3 only allows one config...
         */
-       usb_ep_autoconfig_reset(gadget);
-       ep = usb_ep_autoconfig(gadget, &fs_source_desc);
-       if (!ep) {
-autoconf_fail:
-               pr_err("%s: can't autoconfigure on %s\n",
-                       shortname, gadget->name);
-               return -ENODEV;
+       if (loopdefault) {
+               loopback_add(cdev);
+               if (!gadget_is_sh(gadget))
+                       sourcesink_add(cdev);
+       } else {
+               sourcesink_add(cdev);
+               if (!gadget_is_sh(gadget))
+                       loopback_add(cdev);
        }
-       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 */
 
        gcnum = usb_gadget_controller_number(gadget);
        if (gcnum >= 0)
@@ -1115,144 +241,44 @@ autoconf_fail:
        else {
                /* gadget zero is so simple (for now, no altsettings) that
                 * it SHOULD NOT have problems with bulk-capable hardware.
-                * so warn about unrcognized controllers, don't panic.
+                * so just warn about unrcognized controllers -- don't panic.
                 *
                 * things like configuration and altsetting numbering
                 * can need hardware-specific attention though.
                 */
                pr_warning("%s: controller '%s' not recognized\n",
-                       shortname, gadget->name);
+                       longname, gadget->name);
                device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
        }
 
 
-       /* ok, we made sense of the hardware ... */
-       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-       if (!dev)
-               return -ENOMEM;
-       spin_lock_init(&dev->lock);
-       dev->gadget = gadget;
-       set_gadget_data(gadget, dev);
-
-       init_timer(&dev->resume);
-       dev->resume.function = zero_autoresume;
-       dev->resume.data = (unsigned long) dev;
-
-       /* preallocate control response and buffer */
-       dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
-       if (!dev->req)
-               goto enomem;
-       dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
-       if (!dev->req->buf)
-               goto enomem;
-
-       dev->req->complete = zero_setup_complete;
-
-       device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
-
-       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;
-       }
-
-       if (gadget_is_otg(gadget)) {
-               otg_descriptor.bmAttributes |= USB_OTG_HNP,
-               source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
-               loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
-       }
-
-       usb_gadget_set_selfpowered(gadget);
-
-       if (autoresume) {
-               source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
-               loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
-       }
-
-       gadget->ep0->driver_data = dev;
-
-       INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname);
-       INFO(dev, "using %s, OUT %s IN %s\n", gadget->name,
-               EP_OUT_NAME, EP_IN_NAME);
+       INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);
 
        snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
                init_utsname()->sysname, init_utsname()->release,
                gadget->name);
 
        return 0;
-
-enomem:
-       zero_unbind(gadget);
-       return -ENOMEM;
 }
 
-/*-------------------------------------------------------------------------*/
-
-static void zero_suspend(struct usb_gadget *gadget)
-{
-       struct zero_dev         *dev = get_gadget_data(gadget);
-
-       if (gadget->speed == USB_SPEED_UNKNOWN)
-               return;
-
-       if (autoresume) {
-               mod_timer(&dev->resume, jiffies + (HZ * autoresume));
-               DBG(dev, "suspend, wakeup in %d seconds\n", autoresume);
-       } else
-               DBG(dev, "suspend\n");
-}
-
-static void zero_resume(struct usb_gadget *gadget)
-{
-       struct zero_dev         *dev = get_gadget_data(gadget);
-
-       DBG(dev, "resume\n");
-       del_timer(&dev->resume);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static struct usb_gadget_driver zero_driver = {
-#ifdef CONFIG_USB_GADGET_DUALSPEED
-       .speed          = USB_SPEED_HIGH,
-#else
-       .speed          = USB_SPEED_FULL,
-#endif
-       .function       = (char *) longname,
+static struct usb_composite_driver zero_driver = {
+       .name           = "zero",
+       .dev            = &device_desc,
+       .strings        = dev_strings,
        .bind           = zero_bind,
-       .unbind         = __exit_p(zero_unbind),
-
-       .setup          = zero_setup,
-       .disconnect     = zero_disconnect,
-
-       .suspend        = zero_suspend,
-       .resume         = zero_resume,
-
-       .driver         = {
-               .name           = (char *) shortname,
-               .owner          = THIS_MODULE,
-       },
 };
 
 MODULE_AUTHOR("David Brownell");
 MODULE_LICENSE("GPL");
 
-
 static int __init init(void)
 {
-       return usb_gadget_register_driver(&zero_driver);
+       return usb_composite_register(&zero_driver);
 }
 module_init(init);
 
 static void __exit cleanup(void)
 {
-       usb_gadget_unregister_driver(&zero_driver);
+       usb_composite_unregister(&zero_driver);
 }
 module_exit(cleanup);
-