[patch 1/2] updated composite gadget infrastructure

David Brownell <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
Add <linux/usb/composite.h> interfaces for composite gadget drivers:

  - struct usb_function ... grouping one or more interfaces into
    a function that will often be managed as one unit;

  - struct usb_composite_driver ... grouping one or more such
    functions into a configuration driver.

  - struct usb_composite_dev ... a (possibly composite) configuration,
    managed by the composite driver.

There's an implementation library behind the "composite_driver" which
exposes the "composite_dev".  How to use it (the short version):  add
each usb_function to the list in the composite_driver, then register
that composite driver.

Changes since the last veresion include merging some of the updates
from INDT, notably having standard module parameters for all the
device descriptor strings.  Also bugfixes (e.g. in ID allocation),
and actually working with a partially converted Gadget Zero.


ISSUE:  as implied by the text above, I'm thinking "composite_driver"
should be renamed as "configuration_driver", with new abstractions
for gluing multiple configurations into one device.  But I wanted to
send this version around, while the previous names were still used,
since they are what have thus far been discussed.

That issue partially reflects the fact that the USB spec has a formal
term "composite device", reflecting aspects of one configuration.
But we also need to be able to talk about devices which may only be
composite in one of their several configurations.


# NYET signed-off, it's still work-in-progress

---
 drivers/usb/gadget/composite.c |  764 +++++++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/usbstring.c |    2 
 include/linux/usb/composite.h  |  170 +++++++++
 include/linux/usb_gadget.h     |   82 ++--
 4 files changed, 985 insertions(+), 33 deletions(-)

--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ at91/drivers/usb/gadget/composite.c	2007-08-02 11:32:00.000000000 -0700
@@ -0,0 +1,763 @@
+/*
+ * composite.c - infrastructure for Composite USB Gadgets
+ *
+ * Copyright (C) 2006-2007 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
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#include <linux/usb/composite.h>
+
+
+/*
+ * The code in this file is utility code, used to build a gadget driver
+ * from one or more "function" drivers and a "usb_composite_driver" to
+ * glue them together along with the relevant device-wide data.
+ */
+
+#define DBG(comp, fmt, args...) \
+	dev_dbg(&(comp)->gadget->dev , fmt , ## args)
+#define VDBG(comp, fmt, args...) \
+	dev_vdbg(&(comp)->gadget->dev , fmt , ## args)
+#define ERROR(comp, fmt, args...) \
+	dev_err(&(comp)->gadget->dev , fmt , ## args)
+#define WARN(comp, fmt, args...) \
+	dev_warn(&(comp)->gadget->dev , fmt , ## args)
+#define INFO(comp, fmt, args...) \
+	dev_info(&(comp)->gadget->dev , fmt , ## args)
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ	512
+
+static struct usb_composite_driver	*composite;
+static struct usb_composite_dev		*cdev;
+
+/* Some systems will need runtime overrides for the  product identifers
+ * published in the device descriptor, either numbers or strings or both.
+ * String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+
+static ushort idVendor;
+module_param(idVendor, ushort, S_IRUGO);
+MODULE_PARM_DESC(idVendor, "USB Vendor ID");
+
+static ushort idProduct;
+module_param(idProduct, ushort, S_IRUGO);
+MODULE_PARM_DESC(idProduct, "USB Product ID");
+
+static ushort bcdDevice;
+module_param(bcdDevice, ushort, S_IRUGO);
+MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
+
+static char *iManufacturer;
+module_param(iManufacturer, charp, S_IRUGO);
+MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
+
+static char *iProduct;
+module_param(iProduct, charp, S_IRUGO);
+MODULE_PARM_DESC(iProduct, "USB Product string");
+
+static char *iSerialNumber;
+module_param(iSerialNumber, charp, S_IRUGO);
+MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
+
+/*-------------------------------------------------------------------------*/
+
+/* To simplify, we expect to have only ONE real configuration, working the
+ * same no matter what speed it connects with.  A given function may expose
+ * multiple interfaces.  Each interface includes its descriptor, plus optional
+ * class and endpoint descriptors (as usual).
+ *
+ * Note that the configuration numbers are *NOT* related to how many configs
+ * a device has ... some hardware places restrictions on config numbers.
+ * So having a single configuration, number 3, would be fine.
+ *
+ * REVISIT we do need to lift this restriction, at least for RNDIS.
+ * For PXA compat, stick to config numbers 1, 2, and 3; for SH, just 1.
+ */
+
+#define	CONFIG_NUMBER	1
+
+static int
+config_buf(void *buf, u8 type)
+{
+	struct usb_config_descriptor	*c = buf;
+	void				*next = buf + USB_DT_CONFIG_SIZE;
+	int				len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
+	int				hs;
+	struct usb_function		*f;
+
+	if (gadget_is_dualspeed(cdev->gadget)) {
+		hs = (cdev->gadget->speed == USB_SPEED_HIGH);
+		if (type == USB_DT_OTHER_SPEED_CONFIG)
+			hs = !hs;
+	} else
+		hs = 0;
+
+	/* write a config descriptor */
+	*c = cdev->config;
+	c->bLength = USB_DT_CONFIG_SIZE;
+	c->bDescriptorType = type;
+	c->bConfigurationValue = CONFIG_NUMBER;
+
+	c->iConfiguration = cdev->iConfiguration;
+	c->bmAttributes = USB_CONFIG_ATT_ONE | cdev->bmAttributes;
+	c->bMaxPower = cdev->bMaxPower;
+
+	/* REVISIT some configurations might need other descriptors,
+	 * independent of the interfaces they implement ... notably
+	 * OTG descriptors.
+	 */
+
+	/* add each function's descriptors */
+	list_for_each_entry(f, &cdev->functions, function) {
+		int			status;
+
+		status = usb_descriptor_fillbuf(next, len,
+				hs ? f->hs_descriptors : f->descriptors);
+		if (status < 0)
+			return status;
+		len -= status;
+		next += status;
+	}
+
+	len = next - buf;
+	c->wTotalLength = cpu_to_le16(len);
+	return len;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void composite_reset_config(struct usb_ctrlrequest *req)
+{
+	struct usb_function		*f;
+	int				result;
+
+	DBG(cdev, "reset config\n");
+
+	req->bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+	req->bRequest = USB_REQ_SET_CONFIGURATION;
+
+	list_for_each_entry(f, &cdev->functions, function) {
+		result = f->setup(cdev, req);
+		if (result < 0)
+			DBG(cdev, "reset function %s --> %d\n",
+					f->name, result);
+	}
+	cdev->config.bConfigurationValue = 0;
+}
+
+static int
+composite_set_config(const struct usb_ctrlrequest *ctrl, unsigned number)
+{
+	int			result = 0;
+	int			tmp;
+	struct usb_gadget	*gadget = cdev->gadget;
+	struct usb_ctrlrequest	req;
+
+	memset(&req, 0, sizeof req);
+
+	/* for now function drivers should assume SET_CONFIGURATION means
+	 * reset/deconfigure, with SET_INTERFACE to each interface used
+	 * to activate some altsetting in "the single" configuration.
+	 */
+	composite_reset_config(&req);
+
+	switch (number) {
+	default:
+		result = -EINVAL;
+		req.wValue = cpu_to_le16(0);
+		/* FALLTHROUGH */
+	case 0:
+		for (tmp = 0; tmp < MAX_COMPOSITE_INTERFACES; tmp++) {
+			struct usb_function	*f = cdev->interface[tmp];
+
+			if (!f)
+				continue;
+			f->setup(cdev, ctrl);
+		}
+
+		usb_gadget_vbus_draw(gadget, gadget_is_otg(gadget) ? 8 : 100);
+		break;
+	case CONFIG_NUMBER:
+		req.bRequestType = USB_DIR_OUT
+				| USB_TYPE_STANDARD
+				| USB_RECIP_INTERFACE;
+		req.bRequest = USB_REQ_SET_INTERFACE;
+
+		for (tmp = 0; tmp < MAX_COMPOSITE_INTERFACES; tmp++) {
+			struct usb_function	*f = cdev->interface[tmp];
+
+			if (!f)
+				continue;
+			req.wIndex = cpu_to_le16(tmp);
+			result = f->setup(cdev, &req);
+			if (result < 0) {
+				DBG(cdev, "interface %d/%s alt 0--> %d\n",
+						tmp, f->name, result);
+				(void) composite_set_config(ctrl, 0);
+				return result;
+			}
+		}
+
+		cdev->config.bConfigurationValue = number;
+		usb_gadget_vbus_draw(gadget, 2 * cdev->config.bMaxPower);
+		break;
+	}
+
+	INFO(cdev, "%s speed config #%d\n",
+		({ 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;
+		} ; speed; }), number);
+
+	return result;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+composite_collect_langs(struct usb_gadget_strings **sp, __le16 *buf)
+{
+	const struct usb_gadget_strings	*s;
+	u16				language;
+	__le16				*tmp;
+
+	while (*sp) {
+		s = *sp;
+		language = cpu_to_le16(s->language);
+		for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
+			if (*tmp == language)
+				goto repeat;
+		}
+		*tmp++ = language;
+repeat:
+		sp++;
+	}
+}
+
+static int composite_check_string(
+	struct usb_gadget_strings	**sp,
+	void				*buf,
+	u16				language,
+	int				id
+)
+{
+	struct usb_gadget_strings	*s;
+	int				value;
+
+	while (*sp) {
+		s = *sp++;
+		if (s->language != language)
+			continue;
+		value = usb_gadget_get_string(s, id, buf);
+		if (value > 0)
+			return value;
+	}
+	return -EINVAL;
+}
+
+static int composite_lookup_string(void *buf, u16 language, int id)
+{
+	struct usb_function		*f;
+	int				len;
+
+	/* 0 == report all available language codes */
+	if (id == 0) {
+		struct usb_string_descriptor	*s = buf;
+		struct usb_gadget_strings	**sp;
+
+		memset(s, 0, 256);
+		s->bDescriptorType = USB_DT_STRING;
+
+		sp = composite->strings;
+		if (sp)
+			composite_collect_langs(sp, s->wData);
+
+		list_for_each_entry(f, &cdev->functions, function) {
+			sp = f->strings;
+			if (sp)
+				composite_collect_langs(sp, s->wData);
+		}
+
+		for (len = 0; s->wData[len] && len <= 126; len++)
+			continue;
+		if (!len)
+			return -EINVAL;
+
+		s->bLength = 2 * (len + 1);
+		return s->bLength;
+	}
+
+	/* otherwise, look up and return a specified string */
+	if (composite->strings) {
+		len = composite_check_string(composite->strings,
+				buf, language, id);
+		if (len > 0)
+			return len;
+	}
+	list_for_each_entry(f, &cdev->functions, function) {
+		if (!f->strings)
+			continue;
+		len = composite_check_string(f->strings, buf, language, id);
+		if (len > 0)
+			return len;
+	}
+	return -EINVAL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->status || req->actual != req->length)
+		DBG((struct usb_composite_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
+composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
+	struct usb_request		*req = cdev->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);
+
+	/* partial re-init of the response message; the function or the
+	 * gadget might need to intercept e.g. a control-OUT completion.
+	 */
+	req->zero = 0;
+	req->complete = composite_setup_complete;
+	gadget->ep0->driver_data = cdev;
+
+	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 cdev->dev);
+			memcpy(req->buf, &cdev->dev, value);
+			break;
+		case USB_DT_DEVICE_QUALIFIER:
+			if (!gadget_is_dualspeed(gadget))
+				break;
+			value = min(w_length, (u16) sizeof cdev->qual);
+			memcpy(req->buf, &cdev->qual, value);
+			break;
+		case USB_DT_OTHER_SPEED_CONFIG:
+			if (!gadget_is_dualspeed(gadget))
+				break;
+			/* FALLTHROUGH */
+		case USB_DT_CONFIG:
+			/* one config ... so it must always be index 0 */
+			if (w_value & 0xff)
+				break;
+
+			value = config_buf(req->buf, w_value >> 8);
+			if (value >= 0)
+				value = min(w_length, (u16) value);
+			break;
+		case USB_DT_STRING:
+			value = composite_lookup_string(req->buf, w_index,
+						w_value & 0xff);
+			if (value >= 0)
+				value = min(w_length, (u16) value);
+			break;
+		}
+		break;
+
+	/* currently one config, two speeds */
+	case USB_REQ_SET_CONFIGURATION:
+		if (ctrl->bRequestType != 0)
+			goto unknown;
+		if (gadget->a_hnp_support)
+			DBG(cdev, "HNP available\n");
+		else if (gadget->a_alt_hnp_support)
+			DBG(cdev, "HNP needs a different root port\n");
+		else
+			VDBG(cdev, "HNP inactive\n");
+		spin_lock(&cdev->lock);
+		value = composite_set_config(ctrl, w_value);
+		spin_unlock(&cdev->lock);
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+		if (ctrl->bRequestType != USB_DIR_IN)
+			goto unknown;
+		*(u8 *)req->buf = cdev->config.bConfigurationValue;
+		value = min(w_length, (u16) 1);
+		break;
+
+	/* function drivers must handle get/set altsetting */
+	case USB_REQ_SET_INTERFACE:
+		if (ctrl->bRequestType != USB_RECIP_INTERFACE)
+			goto unknown;
+		if (!cdev->config.bConfigurationValue
+				|| w_index >= MAX_COMPOSITE_INTERFACES
+				|| !cdev->interface[w_index])
+			break;
+		spin_lock(&cdev->lock);
+		value = cdev->interface[w_index]->setup(cdev, ctrl);
+		spin_unlock(&cdev->lock);
+		break;
+	case USB_REQ_GET_INTERFACE:
+		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
+			goto unknown;
+		if (!cdev->config.bConfigurationValue
+				|| w_index >= MAX_COMPOSITE_INTERFACES
+				|| !cdev->interface[w_index])
+			break;
+		spin_lock(&cdev->lock);
+		/* function must set cdev->req->buf[0] */
+		value = cdev->interface[w_index]->setup(cdev, ctrl);
+		spin_unlock(&cdev->lock);
+		value = min(w_length, (u16) 1);
+		break;
+	default:
+unknown:
+		VDBG(cdev,
+			"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(cdev, "ep_queue --> %d\n", value);
+			req->status = 0;
+			composite_setup_complete(gadget->ep0, req);
+		}
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static void
+composite_disconnect(struct usb_gadget *gadget)
+{
+	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
+	unsigned long			flags;
+	struct usb_ctrlrequest	req;
+
+	DBG(cdev, "disconnect\n");
+
+	memset(&req, 0, sizeof req);
+
+	spin_lock_irqsave(&cdev->lock, flags);
+	composite_reset_config(&req);
+	spin_unlock_irqrestore(&cdev->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void /* __init_or_exit */
+composite_unbind(struct usb_gadget *gadget)
+{
+	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
+	struct usb_function		*f;
+
+	DBG(cdev, "unbind\n");
+
+	list_for_each_entry(f, &cdev->functions, function) {
+		if (f->unbind)
+			f->unbind(cdev, f);
+		if (f == cdev->current_bind)
+			break;
+	}
+	if (composite->unbind)
+		composite->unbind(cdev);
+	if (cdev->req) {
+		kfree(cdev->req->buf);
+		usb_ep_free_request(gadget->ep0, cdev->req);
+	}
+	kfree(cdev);
+	set_gadget_data(gadget, NULL);
+	composite = NULL;
+}
+
+static void
+string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
+{
+	struct usb_string *str = tab->strings;
+
+	for (str = tab->strings; str->s; str++) {
+		if (str->id == id) {
+			str->s = s;
+			return;
+		}
+	}
+}
+
+static void
+string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
+{
+	while (*tab) {
+		string_override_one(*tab, id, s);
+		tab++;
+	}
+}
+
+static int __init
+composite_bind(struct usb_gadget *gadget)
+{
+	struct usb_function		*f;
+	int				status = -ENOMEM;
+
+	if (cdev)
+		return -EBUSY;
+
+	cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
+	if (!cdev)
+		return status;
+
+	spin_lock_init(&cdev->lock);
+	cdev->gadget = gadget;
+	set_gadget_data(gadget, cdev);
+	INIT_LIST_HEAD(&cdev->functions);
+
+	/* preallocate control response and buffer */
+	cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+	if (!cdev->req)
+		goto fail;
+	cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
+	if (!cdev->req->buf)
+		goto fail;
+	cdev->req->complete = composite_setup_complete;
+	gadget->ep0->driver_data = cdev;
+
+	cdev->bufsiz = USB_BUFSIZ;
+	cdev->driver = composite;
+
+	usb_gadget_set_selfpowered(gadget);
+
+	/* interface and string IDs start at zero */
+	usb_ep_autoconfig_reset(gadget);
+
+	/* composite gadget needs to assign strings for whole device (like
+	 * serial number), register function drivers, potentially update
+	 * power state and consumption, etc
+	 */
+	cdev->current_bind = gadget;
+	status = composite->bind(cdev);
+	if (status < 0)
+		goto fail;
+	cdev->current_bind = NULL;
+
+	cdev->dev = *composite->dev;
+	cdev->dev.bMaxPacketSize0 = gadget->ep0->maxpacket;
+
+	/* standardized runtime overrides for device ID data */
+	if (idVendor)
+		cdev->dev.idVendor = idVendor;
+	if (idProduct)
+		cdev->dev.idProduct = idProduct;
+	if (bcdDevice)
+		cdev->dev.bcdDevice = bcdDevice;
+
+	if (cdev->dev.iManufacturer && iManufacturer)
+		string_override(composite->strings,
+			cdev->dev.iManufacturer, iManufacturer);
+	if (cdev->dev.iProduct && iProduct)
+		string_override(composite->strings,
+			cdev->dev.iProduct, iProduct);
+	if (cdev->dev.iSerialNumber && iSerialNumber)
+		string_override(composite->strings,
+			cdev->dev.iSerialNumber, iSerialNumber);
+
+	/* function binding involves updating interface, class, and
+	 * endpoint descriptors:
+	 *  - allocating any string IDs needed (interface, class)
+	 *  - allocating interface IDs (ditto)
+	 *  - allocating endpoints (endpoint, class)
+	 *  - allocating non-USB resources (buffers etc)
+	 *
+	 * NOTE:  we assume that if there are multiple controllers, they
+	 * are all the same type (e.g. net2280 in pci slots) so functions
+	 * never need different descriptors.
+	 */
+	list_for_each_entry(f, &cdev->functions, function) {
+		if (!f->bind || !f->setup) {
+			status = -EINVAL;
+			goto fail;
+		}
+
+		cdev->current_bind = f;
+		status = f->bind(cdev, f);
+		if (status < 0)
+			goto fail;
+
+		if ((gadget_is_dualspeed(gadget) && !f->hs_descriptors)
+				|| !f->descriptors) {
+			status = -ENXIO;
+			goto fail;
+		}
+	}
+	cdev->current_bind = NULL;
+	cdev->config.bNumInterfaces = cdev->next_interface_id;
+
+	/* REVISIT eventually we want e.g. RNDIS and non-RNDIS configs,
+	 * at each available device speed ...
+	 */
+	cdev->dev.bNumConfigurations = 1;
+
+	if (gadget_is_dualspeed(gadget)) {
+		cdev->qual.bLength = sizeof cdev->qual;
+		cdev->qual.bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+
+		cdev->qual.bcdUSB = cdev->dev.bcdUSB;
+		cdev->qual.bDeviceClass = cdev->dev.bDeviceClass;
+		cdev->qual.bDeviceProtocol = cdev->dev.bDeviceProtocol;
+
+		/* assume ep0 uses the same value for both speeds ... */
+		cdev->qual.bMaxPacketSize0 = cdev->dev.bMaxPacketSize0;
+
+		cdev->qual.bNumConfigurations = cdev->dev.bNumConfigurations;
+	}
+
+	INFO(cdev, "%s ready\n", composite->name);
+	return 0;
+
+fail:
+	composite_unbind(gadget);
+	return status;
+}
+
+/*
+ * usb_composite_string_id() is called from bind() callbacks to allocate
+ * a new string ID.  The function (or composite gadget) driver will then
+ * store that ID in the appropriate descriptors and string table.
+ */
+int usb_composite_string_id(struct usb_composite_dev *cdev)
+{
+	if (cdev->current_bind && cdev->next_string_id < 254) {
+		/* string id 0 is reserved */
+		cdev->next_string_id++;
+		return cdev->next_string_id;
+	}
+	return -ENODEV;
+}
+
+/*
+ * usb_composite_interface_id() is called from bind() callbacks to
+ * allocate a new interface ID.  The function (or composite gadget)
+ * driver will then store that ID in interface, association, cdc union,
+ * and other appropriate descriptors.
+ */
+int usb_composite_interface_id(struct usb_composite_dev *cdev)
+{
+	if (cdev->next_interface_id < MAX_COMPOSITE_INTERFACES
+			&& cdev->current_bind) {
+		cdev->interface[cdev->next_interface_id] = cdev->current_bind;
+		return cdev->next_interface_id++;
+	}
+	return -ENODEV;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+composite_suspend(struct usb_gadget *gadget)
+{
+	struct usb_function		*f;
+
+	DBG(cdev, "suspend\n");
+	/* revisit -- iterate cdev->interface? */
+	list_for_each_entry(f, &cdev->functions, function) {
+		if (!f->suspend)
+			continue;
+		f->suspend(cdev);
+	}
+}
+
+static void
+composite_resume(struct usb_gadget *gadget)
+{
+	struct usb_function		*f;
+
+	DBG(cdev, "resume\n");
+	/* revisit -- iterate cdev->interface? */
+	list_for_each_entry(f, &cdev->functions, function) {
+		if (!f->resume)
+			continue;
+		f->resume(cdev);
+	}
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver composite_driver = {
+	.speed		= USB_SPEED_HIGH,
+
+	.bind		= composite_bind,
+	.unbind		= __exit_p(composite_unbind),
+
+	.setup		= composite_setup,
+	.disconnect	= composite_disconnect,
+
+	.suspend	= composite_suspend,
+	.resume		= composite_resume,
+
+	.driver	= {
+		.owner		= THIS_MODULE,
+	},
+};
+
+int usb_composite_register(struct usb_composite_driver *d)
+{
+	if (!d || !d->dev || !d->bind || composite)
+		return -EINVAL;
+
+	if (!d->name)
+		d->name = "composite";
+	composite_driver.function =  (char *) d->name;
+	composite_driver.driver.name = d->name;
+	composite = d;
+
+	return usb_gadget_register_driver(&composite_driver);
+}
+
+void usb_composite_unregister(struct usb_composite_driver *d)
+{
+	if (composite != d)
+		return;
+	usb_gadget_unregister_driver(&composite_driver);
+}
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ at91/include/linux/usb/composite.h	2007-08-02 11:33:26.000000000 -0700
@@ -0,0 +1,174 @@
+/* <linux/usb/composite.h> */
+
+#ifndef	__LINUX_USB_COMPOSITE_H
+#define	__LINUX_USB_COMPOSITE_H
+
+#ifdef __KERNEL__
+
+#include <linux/usb/ch9.h>
+#include <linux/usb_gadget.h>
+
+
+/* Support for composite configs, built from distinct function drivers.
+ * Example:  one offering both network and mass storage functionality.
+ */
+
+struct usb_composite_dev;
+struct usb_composite_driver;
+
+/**
+ * struct usb_function - describes one function of a composite configuration
+ * @name: for diagnostics, identifies the function
+ * @strings: tables of strings, keyed by identifiers assigned during bind()
+ *	and language IDs provided in control requests
+ * @descriptors: table of low/full speed descriptors, using interface and
+ *	string identifiers assigned during bind()
+ * @hs_descriptors: table of high speed descriptors, using interface and
+ *	string identifiers assigned during bind(); or null
+ * @function: each function is on the usb_composite.function list when the
+ *	composite gadget is initialized, and then places itself in the right
+ *	position(s) in usb_composite.functions[] during bind()
+ * @bind: before the gadget can register, all of its functions bind() to the
+ *	available resources including identifiers for strings, interfaces,
+ *	and endpoints
+ * @unbind: before the gadget can unregister, all of its functions unbind()
+ *	the resources they have allocated.
+ * @setup: used for SET_INTERFACE, SET_CONFIGURATION, and interface-specific
+ *	control requests
+ * @disconnect: used to notify functions when the host has disconnected
+ * @suspend: notifies functions when the host stops sending USB traffic
+ * @disconnect: notifies functions when the host reactivates the USB link
+ *
+ * A single USB function uses one or more interfaces, and supports dual speed
+ * operation on appropriate hardware.
+ *
+ * Only one instance of a function may be associated with a given composite
+ * configuration.  Accordingly, data which a function driver needs in order
+ * to implement any callback can reasonably be static to that driver's module,
+ * or part of a structure within which the function struct has been embedded.
+ */
+struct usb_function {
+	const char				*name;
+	struct usb_gadget_strings		**strings;
+	const struct usb_descriptor_header	**descriptors;
+	const struct usb_descriptor_header	**hs_descriptors;
+
+	struct list_head			function;
+
+	/* REVISIT want multi-config at *same* speed too ...
+	 * e.g. one has RNDIS, another has CDC-Ethernet
+	 * not just high speed versions of each.
+	 */
+
+	int			(*bind)(struct usb_composite_dev *,
+					struct usb_function *);
+	void			(*unbind)(struct usb_composite_dev *,
+					struct usb_function *);
+	int			(*setup)(struct usb_composite_dev *,
+					const struct usb_ctrlrequest *);
+	void			(*disconnect)(struct usb_composite_dev *);
+	void			(*suspend)(struct usb_composite_dev *);
+	void			(*resume)(struct usb_composite_dev *);
+};
+
+/**
+ * struct usb_composite_driver -- groups functions into one gadget config
+ * @name: for diagnostics, identifies the driver
+ * @dev: descriptor for the device
+ * @strings: tables of strings, keyed by identifiers assigned during bind()
+ *	and language IDs provided in control requests
+ * @bind: Used to build the list of functions, and to allocate for resources
+ *	shared across the whole device such as string IDs.  This can update
+ *	device and configuration descriptors before functions bind().
+ * @unbind: optionally reverses bind()
+ * @setup: optionally used to delegate class and vendor control requests
+ *
+ * As with any gadget driver, there may be only one per system.  Data that a
+ * composite gadget driver needs to implement any callback can reasonably be
+ * static to that driver's module, or part of a structure within which the
+ * driver struct has been embedded.
+ *
+ * FIXME device descriptor shouldn't be part of a per-config structure...
+ */
+struct usb_composite_driver {
+	const char				*name;
+	const struct usb_device_descriptor	*dev;
+	struct usb_gadget_strings		**strings;
+
+	/* REVISIT want a general "add more descriptors for config N"
+	 * hook; OTG should fall out naturally
+	 */
+
+	int			(*bind)(struct usb_composite_dev *);
+	int			(*unbind)(struct usb_composite_dev *);
+	int			(*setup)(struct usb_composite_dev *,
+					const struct usb_ctrlrequest *);
+
+	/* REVISIT disconnect(), suspend(), resume() too ?? */
+};
+
+extern int usb_composite_register(struct usb_composite_driver *);
+extern void usb_composite_unregister(struct usb_composite_driver *);
+
+
+#define	MAX_COMPOSITE_INTERFACES		8	/* max 16 */
+
+/**
+ * struct usb_composite_device - represents one composite usb gadget
+ * @gadget: read-only, abstracts the gadget's usb peripheral controller
+ * @req: used for control responses; buffer is pre-allocated
+ * @bufsiz: size of buffer pre-allocated in @req
+ * @dev: device descriptor
+ * @functions: each function on this list after driver->bind() returns
+ *	will be bound and initialized
+ * @config: configuration descriptor; if the device is not configured,
+ *	its bConfigurationValue is zero and other fields are ignored
+ *
+ * REVISIT:  this represents a composite configuration, and should
+ * be renamed accordingly...
+ */
+struct usb_composite_dev {
+	/* lock protects writes to all fields of this structure */
+	spinlock_t			lock;
+
+	struct usb_gadget		*gadget;
+	struct usb_request		*req;
+	unsigned			bufsiz;
+	struct usb_device_descriptor	dev;
+	struct usb_config_descriptor	config;
+
+	/* REVISIT need per-function state hook ... maybe a
+	 * (void*) returned from bind() and passed to callbacks?
+	 * arguably, interface descriptors are part of that...
+	 */
+
+	struct list_head		functions;
+	struct usb_composite_driver	*driver;
+
+	/* fields in the config descriptor */
+	u8				iConfiguration;
+	u8				bmAttributes;
+	u8				bMaxPower;
+
+	/* INTERNALS -- not for function drivers */
+	u8				next_string_id;
+	u8				next_interface_id;
+	void				*current_bind;
+	struct usb_function		*interface[MAX_COMPOSITE_INTERFACES];
+
+	struct usb_qualifier_descriptor	qual;
+};
+
+/* IDs may be assigned ONLY during function driver bind() */
+extern int usb_composite_string_id(struct usb_composite_dev *c);
+extern int usb_composite_interface_id(struct usb_composite_dev *c);
+
+static inline void
+usb_composite_add_function(struct usb_composite_dev *c, struct usb_function *f)
+{
+	list_add_tail(&f->function, &c->functions);
+}
+
+#endif  /* __KERNEL__ */
+
+#endif	/* __LINUX_USB_COMPOSITE_H */

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.