Hi,

This patch adds a small routine that makes it easier to work with
complex configuration descriptors, by declaring null-terminated
vectors of pointers to the component descriptors.  And it also
converts "gadget/ether.c" to use it, showing net simplification
and essentially no size impact.

The other gadget drivers could easily convert to use this, but
clearly they don't _need_ to do so.

I think it's worth merging as-is, but I'm asking for comments on
this in association with the other thread I'm starting about what
sorts of utilities to layer on top of the gadget core APIs.

- Dave

p.s. Applies also to 2.4.25 except for the Makefile.


Documentation/DocBook/gadget.tmpl | 1 drivers/usb/gadget/Makefile | 2 drivers/usb/gadget/config.c | 116 +++++++++++++++++++++++++++++++++++ drivers/usb/gadget/ether.c | 125 +++++++++++++++----------------------- include/linux/usb_gadget.h | 11 +++ 5 files changed, 180 insertions(+), 75 deletions(-)

Adds two new gadget-side utility functions, to support a declarative
style of managing usb configuration descriptors.  The functions fill
buffers from null-terminated vectors of usb descriptors, which are
simple to build or update.

The "ethernet" gadget driver currently has the most interesting config
descriptors.  This uses those functions to replace some complex code with
simpler static declarations; result, it's cleaner.  (And it'll be easier
to add RNDIS configurations later, too.)

Memory savings (or cost, depending on config) was less than 50 bytes;
nothing worth worrying about.



--- 1.7/include/linux/usb_gadget.h      Fri Dec 19 06:46:22 2003
+++ edited/include/linux/usb_gadget.h   Mon Feb 23 23:43:48 2004
@@ -716,6 +716,17 @@
 /* put descriptor for string with that id into buf (buflen >= 256) */
 int usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf);
 
+/*-------------------------------------------------------------------------*/
+
+/* utility to simplify managing config descriptors */
+
+/* write vector of descriptors into buffer */
+int usb_descriptor_fillbuf(void *, unsigned,
+               const struct usb_descriptor_header **);
+
+/* build config descriptor from single descriptor vector */
+int usb_gadget_config_buf(const struct usb_config_descriptor *config,
+       void *buf, unsigned buflen, const struct usb_descriptor_header **desc);
 
 #endif  /* __KERNEL__ */
 
--- 1.13/drivers/usb/gadget/ether.c     Fri Feb  6 11:20:20 2004
+++ edited/drivers/usb/gadget/ether.c   Mon Feb 23 23:43:48 2004
@@ -607,6 +607,25 @@
        .wMaxPacketSize =       __constant_cpu_to_le16 (64),
 };
 
+static const struct usb_descriptor_header *fs_function [] = {
+#ifdef DEV_CONFIG_CDC
+       /* "cdc" mode descriptors */
+       (struct usb_descriptor_header *) &control_intf,
+       (struct usb_descriptor_header *) &header_desc,
+       (struct usb_descriptor_header *) &union_desc,
+       (struct usb_descriptor_header *) &ether_desc,
+#ifdef EP_STATUS_NUM
+       (struct usb_descriptor_header *) &fs_status_desc,
+#endif
+       (struct usb_descriptor_header *) &data_nop_intf,
+#endif /* DEV_CONFIG_CDC */
+       /* minimalist core */
+       (struct usb_descriptor_header *) &data_intf,
+       (struct usb_descriptor_header *) &fs_source_desc,
+       (struct usb_descriptor_header *) &fs_sink_desc,
+       0,
+};
+
 #ifdef HIGHSPEED
 
 /*
@@ -660,6 +679,25 @@
        .bNumConfigurations =   1,
 };
 
+static const struct usb_descriptor_header *hs_function [] = {
+#ifdef DEV_CONFIG_CDC
+       /* "cdc" mode descriptors */
+       (struct usb_descriptor_header *) &control_intf,
+       (struct usb_descriptor_header *) &header_desc,
+       (struct usb_descriptor_header *) &union_desc,
+       (struct usb_descriptor_header *) &ether_desc,
+#ifdef EP_STATUS_NUM
+       (struct usb_descriptor_header *) &hs_status_desc,
+#endif
+       (struct usb_descriptor_header *) &data_nop_intf,
+#endif /* DEV_CONFIG_CDC */
+       /* minimalist core */
+       (struct usb_descriptor_header *) &data_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))
@@ -704,86 +742,25 @@
 static int
 config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index)
 {
-       const unsigned  config_len = USB_DT_CONFIG_SIZE
-#ifdef DEV_CONFIG_CDC
-                               + 2 * USB_DT_INTERFACE_SIZE
-                               + sizeof header_desc
-                               + sizeof union_desc
-                               + sizeof ether_desc
-#ifdef EP_STATUS_NUM
-                               + USB_DT_ENDPOINT_SIZE
-#endif
-#endif /* DEV_CONFIG_CDC */
-                               + USB_DT_INTERFACE_SIZE
-                               + 2 * USB_DT_ENDPOINT_SIZE;
-
+       int                             len;
+       const struct usb_descriptor_header **function = fs_function;
 #ifdef HIGHSPEED
-       int             hs;
-#endif
-       /* a single configuration must always be index 0 */
-       if (index > 0)
-               return -EINVAL;
-       if (config_len > USB_BUFSIZ)
-               return -EDOM;
+       int                             hs = (speed == USB_SPEED_HIGH);
 
-       /* config (or other speed config) */
-       memcpy (buf, &eth_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;
-#ifdef HIGHSPEED
-       hs = (speed == USB_SPEED_HIGH);
        if (type == USB_DT_OTHER_SPEED_CONFIG)
                hs = !hs;
-#endif
-
-#ifdef DEV_CONFIG_CDC
-       /* control interface, class descriptors, optional status endpoint */
-       memcpy (buf, &control_intf, USB_DT_INTERFACE_SIZE);
-       buf += USB_DT_INTERFACE_SIZE;
-
-       memcpy (buf, &header_desc, sizeof header_desc);
-       buf += sizeof header_desc;
-       memcpy (buf, &union_desc, sizeof union_desc);
-       buf += sizeof union_desc;
-       memcpy (buf, &ether_desc, sizeof ether_desc);
-       buf += sizeof ether_desc;
-
-#ifdef EP_STATUS_NUM
-#ifdef HIGHSPEED
        if (hs)
-               memcpy (buf, &hs_status_desc, USB_DT_ENDPOINT_SIZE);
-       else
-#endif /* HIGHSPEED */
-               memcpy (buf, &fs_status_desc, USB_DT_ENDPOINT_SIZE);
-       buf += USB_DT_ENDPOINT_SIZE;
-#endif /* EP_STATUS_NUM */
-
-       /* default data altsetting has no endpoints */
-       memcpy (buf, &data_nop_intf, USB_DT_INTERFACE_SIZE);
-       buf += USB_DT_INTERFACE_SIZE;
-#endif /* DEV_CONFIG_CDC */
-
-       /* the "real" data interface has two endpoints */
-       memcpy (buf, &data_intf, USB_DT_INTERFACE_SIZE);
-       buf += USB_DT_INTERFACE_SIZE;
-#ifdef HIGHSPEED
-       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
-#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;
-       }
+               function = hs_function;
+#endif
 
-       return config_len;
+       /* a single configuration must always be index 0 */
+       if (index > 0)
+               return -EINVAL;
+       len = usb_gadget_config_buf (&eth_config, buf, USB_BUFSIZ, function);
+       if (len < 0)
+               return len;
+       ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+       return len;
 }
 
 /*-------------------------------------------------------------------------*/
--- 1.4/drivers/usb/gadget/Makefile     Wed Jan 21 04:56:53 2004
+++ edited/drivers/usb/gadget/Makefile  Mon Feb 23 23:43:48 2004
@@ -9,7 +9,7 @@
 # USB gadget drivers
 #
 g_zero-objs                    := zero.o usbstring.o
-g_ether-objs                   := ether.o usbstring.o
+g_ether-objs                   := ether.o usbstring.o config.o
 g_serial-objs                  := serial.o usbstring.o
 gadgetfs-objs                  := inode.o usbstring.o
 g_file_storage-objs            := file_storage.o usbstring.o
--- 1.3/Documentation/DocBook/gadget.tmpl       Mon Sep 15 03:43:20 2003
+++ edited/Documentation/DocBook/gadget.tmpl    Tue Feb 24 08:18:48 2004
@@ -454,6 +454,7 @@
 </para>
 
 !Edrivers/usb/gadget/usbstring.c
+!Edrivers/usb/gadget/config.c
 </sect1>
 
 </chapter>
--- devnull/drivers/usb/gadget/config.c 1969-12-31 16:00:00.000000000 -0800
+++ ./drivers/usb/gadget/config.c       2004-02-23 22:51:28.880225936 -0800
@@ -0,0 +1,116 @@
+/*
+ * usb/gadget/config.c -- simplify building config descriptors
+ *
+ * Copyright (C) 2003 David Brownell
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/device.h>
+
+#include <linux/usb_ch9.h>
+
+
+/**
+ * usb_descriptor_fillbuf - fill buffer with descriptors
+ * @buf: Buffer to be filled
+ * @buflen: Size of buf
+ * @src: Array of descriptor pointers, terminated by null pointer.
+ *
+ * Copies descriptors into the buffer, returning the length or a
+ * negative error code if they can't all be copied.  Useful when
+ * assembling descriptors for an associated set of interfaces used
+ * as part of configuring a composite device; or in other cases where
+ * sets of descriptors need to be marshaled.
+ */
+int
+usb_descriptor_fillbuf(void *buf, unsigned buflen,
+               const struct usb_descriptor_header **src)
+{
+       u8      *dest = buf;
+
+       if (!src)
+               return -EINVAL;
+
+       /* fill buffer from src[] until null descriptor ptr */
+       for (; 0 != *src; src++) {
+               unsigned                len = (*src)->bLength;
+
+               if (len > buflen);
+                       return -EINVAL;
+               memcpy(dest, *src, len);
+               buflen -= len;
+               dest += len;
+       }
+       return dest - (u8 *)buf;
+}
+
+
+/**
+ * usb_gadget_config_buf - builts a complete configuration descriptor
+ * @config: Header for the descriptor, including characteristics such
+ *     as power requirements and number of interfaces.
+ * @desc: Null-terminated vector of pointers to the descriptors (interface,
+ *     endpoint, etc) defining all functions in this device configuration.
+ * @buf: Buffer for the resulting configuration descriptor.
+ * @length: Length of buffer.  If this is not big enough to hold the
+ *     entire configuration descriptor, an error code will be returned.
+ *
+ * This copies descriptors into the response buffer, building a descriptor
+ * for that configuration.  It returns the buffer length or a negative
+ * status code.  The config.wTotalLength field is set to match the length
+ * of the result, but other descriptor fields (including power usage and
+ * interface count) must be set by the caller.
+ *
+ * Gadget drivers could use this when constructing a config descriptor
+ * in response to USB_REQ_GET_DESCRIPTOR.  They will need to patch the
+ * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
+ */
+int usb_gadget_config_buf(
+       const struct usb_config_descriptor      *config,
+       void                                    *buf,
+       unsigned                                length,
+       const struct usb_descriptor_header      **desc
+)
+{
+       struct usb_config_descriptor            *cp = buf;
+       int                                     len;
+
+       /* config descriptor first */
+       if (length < USB_DT_CONFIG_SIZE || !desc)
+               return -EINVAL;
+       *cp = *config; 
+
+       /* then interface/endpoint/class/vendor/... */
+       len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
+                       length - USB_DT_CONFIG_SIZE, desc);
+       if (len < 0)
+               return len;
+       len += USB_DT_CONFIG_SIZE;
+       if (len > 0xffff)
+               return -EINVAL;
+
+       /* patch up the config descriptor */
+       cp->bLength = USB_DT_CONFIG_SIZE;
+       cp->bDescriptorType = USB_DT_CONFIG;
+       cp->wTotalLength = cpu_to_le16(len);
+       cp->bmAttributes |= USB_CONFIG_ATT_ONE;
+       return len;
+}
+

Reply via email to