[PATCH 3/6] Composite gadget driver upgrade

Ragner Magalhaes <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
Modifications on David Brownell's patch.
Some improvements on David Brownell's patch. It adds some
functions to Composite Framework as well.

Signed-off-by: Felipe Balbi <[email protected]>
Signed-off-by: Ragner Magalhaes <[email protected]>

:100644 100644 495c660... fb23d79... M	drivers/usb/gadget/composite.c
:100644 100644 82fdb13... 49e5d00... M	include/linux/usb/composite.h

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 495c660..fb23d79 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1,5 +1,18 @@
+/*
+ * composite.c -- USB Layer for Composite Gadget Support
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of the License of (at you option) any later version.
+ *
+ * Framework that let the gadget driver to work with multiples
+ * functions together.
+ */
+
+#if 0
 #define DEBUG 1
-// #define VERBOSE
+#define VERBOSE 1
+#endif
 
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -21,10 +34,23 @@
 #include <asm/system.h>
 #include <asm/unaligned.h>
 
-#include <linux/usb_ch9.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
 #include <linux/usb_gadget.h>
 #include <linux/usb/composite.h>
 
+#include "gadget_chips.h"
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_VERSION		"April 24th, 2007"
+#define DRIVER_DESCRIPTOR	"Composite Gadget"
+
+static const char shortname[]	= "composite";
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_composite_dev	*cdev;
 
 #define xprintk(d,level,fmt,args...) \
 	dev_printk(level , &(d)->gadget->dev , fmt , ## args)
@@ -54,10 +80,123 @@
 /*-------------------------------------------------------------------------*/
 
 /* big enough to hold our biggest descriptor */
-#define USB_BUFSIZ	512
+#define USB_BUFSIZ		256
+
+/* An impossibly large value as file_storage */
+#define DELAYED_STATUS		(USB_BUFSIZ + 999)
+
+/* take from zero.c USB OTG Compliance test device */
+#define DRIVER_VENDOR_NUM	0x1a0a
+#define DRIVER_PRODUCT_NUM	0xbadd
+
+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");
+
+/*-------------------------------------------------------------------------*/
+
+/* USB String IDs */
+#define STRING_MANUFACTURER	1
+#define STRING_PRODUCT		2
+#define STRING_SERIAL		3
+#define STRING_CONFIG01 	4
+#define STRING_CONFIG02 	5
+
+#define CONFIG_NUMBER01 	1
+#define CONFIG_NUMBER02 	2
+
+static struct usb_device_descriptor
+device_desc = {
+	.bLength		= sizeof device_desc,
+	.bDescriptorType	= USB_DT_DEVICE,
+
+	.bcdUSB 		= __constant_cpu_to_le16(0x0200),
+	.bDeviceClass		= USB_CLASS_PER_INTERFACE,
+
+	.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,
+};
+
+static struct usb_config_descriptor
+config_desc = {
+	.bLength		= sizeof config_desc,
+	.bDescriptorType	= USB_DT_CONFIG,
+
+	/* compute wTotalLength on the fly */
+	.bConfigurationValue	= CONFIG_NUMBER01,
+	.iConfiguration 	= STRING_CONFIG01,
+	.bmAttributes		= USB_CONFIG_ATT_ONE
+				| USB_CONFIG_ATT_SELFPOWER,
+	.bMaxPower		= 1,	/* self-powered */
+};
+
+static struct usb_otg_descriptor
+otg_descriptor = {
+	.bLength		= sizeof otg_descriptor,
+	.bDescriptorType	= USB_DT_OTG,
+
+	.bmAttributes		= USB_OTG_SRP,
+};
+
+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,
+};
+
+static const struct usb_descriptor_header *shared_function[] = {
+	(struct usb_descriptor_header *)&otg_descriptor,
+	NULL,
+};
+
+static char manufacturer[50];
+static char product_desc[40] = DRIVER_DESCRIPTOR;
+static char serial_number[40];
 
+static char config_cfunc[126];
 
-static struct usb_composite_driver	*composite;
+/* static strings, in UTF-8 */
+static struct usb_string strings[] = {
+	{STRING_MANUFACTURER, manufacturer,},
+	{STRING_PRODUCT, product_desc,},
+	{STRING_SERIAL, serial_number,},
+	{STRING_CONFIG01, config_cfunc,},
+	{STRING_CONFIG02, config_cfunc,},
+	{}			/* end of list */
+};
+
+static struct usb_gadget_strings stringtab = {
+	.language = 0x0409,	/* en-us */
+	.strings = strings,
+};
+
+/*-------------------------------------------------------------------------*/
 
 static inline int is_dualspeed(struct usb_gadget *g)
 {
@@ -79,185 +218,382 @@ static inline int is_otg(struct usb_gadget *g)
 
 /*-------------------------------------------------------------------------*/
 
-/* 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).
- */
+static void
+free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->buf)
+		kfree (req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+static int
+set_interface_number(struct usb_composite_dev *cdev,
+	struct usb_interface_descriptor *intf)
+{
+	if (intf->iInterface)
+		intf->iInterface = STRID_FUNC2COMPOSITE (cdev->func,
+						intf->iInterface);
+	intf->bInterfaceNumber = INTRF_FUNC2COMPOSITE (cdev->func,
+						intf->bInterfaceNumber);
+	cdev->interface[intf->bInterfaceNumber] = cdev->func;
+	if (!intf->bAlternateSetting)
+		return 1;
+	return 0;
+}
+
+static void
+set_cs_interface_number(struct usb_composite_dev *cdev,
+	struct usb_cdc_header_desc *cdc_desc)
+{
+	u8				*i;
+	switch (cdc_desc->bDescriptorSubType) {
+	case USB_CDC_UNION_TYPE:
+		i = &((struct usb_cdc_union_desc *)
+					cdc_desc)->bMasterInterface0;
+		*i = INTRF_FUNC2COMPOSITE (cdev->func, *i);
+
+		i = &((struct usb_cdc_union_desc *)
+					cdc_desc)->bSlaveInterface0;
+		*i = INTRF_FUNC2COMPOSITE (cdev->func, *i);
+		break;
+	case USB_CDC_CALL_MANAGEMENT_TYPE:
+		i = &((struct usb_cdc_call_mgmt_descriptor *)
+						cdc_desc)->bDataInterface;
+		*i = INTRF_FUNC2COMPOSITE (cdev->func, *i);
+		break;
+	case USB_CDC_ETHERNET_TYPE:
+		i = &((struct usb_cdc_ether_desc *) cdc_desc)->iMACAddress;
+		if (*i > 0)
+			*i = STRID_FUNC2COMPOSITE (cdev->func, *i);
+		break;
+	}
+}
 
-#define	CONFIG_NUMBER	1
+int usb_composite_interface_id(struct usb_composite_dev *cdev,
+void *buf, int size)
+{
+	int				status = 0;
+	int				i = 0;
+	struct usb_descriptor_header * src = buf;
+
+	if (cdev->next_interface_id >= MAX_COMPOSITE_INTERFACES)
+		return -EBUSY;
+
+	if (!src)
+		return -EINVAL;
+
+	while (i < size) {
+		switch (src->bDescriptorType) {
+		case USB_DT_INTERFACE:
+			status += set_interface_number(cdev,
+				(struct usb_interface_descriptor *) src);
+			break;
+		case USB_DT_CS_INTERFACE:
+			set_cs_interface_number(cdev,
+				(struct usb_cdc_header_desc *) src);
+			break;
+		}
+		i += src->bLength;
+		buf += src->bLength;
+		src = buf;
+	}
+
+	return status;
+}
+
+static inline struct usb_string *
+get_func_strings(struct usb_function *f)
+{
+	return f->strings->strings;
+}
+
+int usb_composite_string_id(struct usb_composite_dev *cdev)
+{
+	struct usb_string		*src = get_func_strings(cdev->func);
+	int				status = 0;
+
+	if (cdev->next_string_id >= 255)
+		return -EBUSY;
+
+	for (; src && src->s ; src++) {
+		status ++;
+	}
+	return status;
+}
+
+/*-------------------------------------------------------------------------*/
 
 static int
-config_buf(struct usb_composite_dev *cdev, void *buf, u8 type)
+config_buf(struct usb_composite_dev *cdev, void *buf, u8 type, unsigned index)
 {
 	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;
+	struct usb_gadget		*gadget = cdev->gadget;
+	struct usb_ctrlrequest		req;
+	int				status;
+	int				i;
+	const struct usb_descriptor_header **function;
 
-	if (is_dualspeed(cdev->gadget)) {
-		hs = (cdev->gadget->speed == USB_SPEED_HIGH);
+	if (index >= device_desc.bNumConfigurations)
+		return -EINVAL;
+
+	if (is_dualspeed(gadget)) {
+		hs = (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 = config_desc;
 	c->bLength = USB_DT_CONFIG_SIZE;
 	c->bDescriptorType = type;
-	c->bConfigurationValue = CONFIG_NUMBER;
 
-	/* REVISIT some configurations might need other descriptors,
-	 * independent of the interfaces they implement ... notably
-	 * OTG descriptors.
-	 */
+	if (index == 0) {
+		c->iConfiguration = STRING_CONFIG01;
+		c->bConfigurationValue = CONFIG_NUMBER01;
+	} else {
+		c->iConfiguration = STRING_CONFIG02;
+		c->bConfigurationValue = CONFIG_NUMBER02;
+	}
 
-	/* add each function's descriptors */
-	list_for_each_entry (f, &composite->functions, function) {
-		int			status;
+	function = shared_function;
+	if (!is_otg(gadget))
+		function++;
 
-		status = usb_descriptor_fillbuf(next, len,
-				hs ? f->hs_descriptors : f->descriptors);
+	/* shared function descriptors */
+	if (function) {
+		status = usb_descriptor_fillbuf(next, len, function);
 		if (status < 0)
 			return status;
 		len -= status;
 		next += status;
 	}
 
-	len = next - buf;
-	c->wTotalLength = cpu_to_le16(len);
-	return len;
-}
+	/* add each function's descriptors */
+	c->bNumInterfaces =0;
+	cdev->next_interface_id = 0;
+	cdev->next_string_id = 0;
 
-/*-------------------------------------------------------------------------*/
+	if (!cdev->func)
+		goto result;
 
-static void composite_reset_config(
-	struct usb_composite_dev	*cdev,
-	struct usb_ctrlrequest		*req
-) {
-	struct usb_function		*f;
-	int				result;
+	memset(&req, 0, sizeof req);
+	req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+	req.bRequest = USB_REQ_GET_DESCRIPTOR;
+	req.wLength = USB_BUFSIZ;
+
+	index = (index < cdev->func->num_conf) ? index : 0;
+
+	list_for_each_entry (f, &cdev->functions, function) {
+
+		req.wValue = type << 8;
+		req.wValue |= index;
 
-	DBG(cdev, "reset config\n");
+		f->interface_shift = cdev->next_interface_id;
+		f->stringid_shift = cdev->next_string_id;
+
+		cdev->req->length = next - buf;
+		cdev->func = f;
+		status = f->setup(gadget, &req);
+
+		if (status < 0) {
+			DBG(cdev, " %s - invalid descriptor header.\n", f->name);
+			return status;
+		}
 
-	req->bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
-	req->bRequest = USB_REQ_SET_CONFIGURATION;
+		i = usb_composite_interface_id(cdev, next, status);
+		if (i < 0)
+			return i;
+		cdev->next_interface_id += i;
 
-	list_for_each_entry (f, &composite->functions, function) {
-		result = f->setup(cdev, req);
-		if (result < 0)
-			DBG(cdev, "reset function %s --> %d\n",
-					f->name, result);
+		i = usb_composite_string_id(cdev);
+		if (i < 0)
+			return i;
+		cdev->next_string_id += i;
+
+		/* exchange the configuration of the fist function only
+		 * in the buf of the two composite's configurations.
+		 */
+		index = 0;
+
+		len -= status;
+		next += status;
 	}
-	cdev->config.bConfigurationValue = 0;
+result:
+
+	len = next - buf;
+	c->wTotalLength = cpu_to_le16(len);
+	c->bNumInterfaces = cdev->next_interface_id;
+
+	return len;
 }
 
 static int
 composite_set_config(struct usb_composite_dev *cdev, unsigned number)
 {
-	int			result = 0, tmp;
-	struct usb_gadget	*gadget = cdev->gadget;
-	struct usb_ctrlrequest	req;
+	int				result = 0;
+	struct usb_gadget		*gadget = cdev->gadget;
+	struct usb_ctrlrequest		req;
+	struct usb_function		*f;
+	int				i;
 
 	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(cdev, &req);
+	req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+	req.bRequest = USB_REQ_SET_CONFIGURATION;
+	req.wValue = number;
+
+	/* reset config */
+	cdev->config = 0;
 
 	switch (number) {
 	default:
 		result = -EINVAL;
 		/* FALLTHROUGH */
 	case 0:
-		usb_gadget_vbus_draw(gadget, is_otg(gadget) ? 8 : 100);
+		list_for_each_entry (f, &cdev->functions, function) {
+			cdev->func = f;
+			result = f->setup(gadget, &req);
+			if (result < 0)
+				break;
+		}
 		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(cdev, 0);
-				return result;
-			}
+	case CONFIG_NUMBER01:
+		i = 0;
+		list_for_each_entry (f, &cdev->functions, function) {
+			req.wValue = f->config[i]->bConfigurationValue;
+			cdev->func = f;
+			result = f->setup(gadget, &req);
+			if (result < 0)
+				break;
 		}
+		break;
 
-		cdev->config.bConfigurationValue = number;
-		usb_gadget_vbus_draw(gadget, 2 * cdev->config.bMaxPower);
+	case CONFIG_NUMBER02:
+		if (!cdev->func)
+			break;
+		i = (1 < cdev->func->num_conf) ? 1 : 0;
+		list_for_each_entry (f, &cdev->functions, function) {
+			req.wValue = f->config[i]->bConfigurationValue;
+			cdev->func = f;
+			result = f->setup(gadget, &req);
+			if (result < 0)
+				break;
+			i = 0;
+		}
 		break;
 	}
 
-	INFO(cdev, "%s speed config #%d\n",
-		({ char *speed;
+	if (result < 0)
+		usb_gadget_vbus_draw(gadget, is_otg(gadget) ? 8 : 100);
+	else {
+		char *speed;
+		unsigned power;
+
+		power = 2 * config_desc.bMaxPower;
+		usb_gadget_vbus_draw(gadget, power);
+
 		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);
+		case USB_SPEED_LOW:
+			speed = "low"; break;
+		case USB_SPEED_FULL:
+			speed = "full"; break;
+		case USB_SPEED_HIGH:
+			speed = "high"; break;
+		default:
+			speed = "?"; break;
+		}
 
+		cdev->config = number;
+		sprintf(config_cfunc,"Composite gadget");
+		INFO (cdev, "%s speed config #%d: %d mA, %s\n",
+				speed, number, power, config_cfunc);
+		result = 0;
+	}
 	return result;
 }
 
 /*-------------------------------------------------------------------------*/
 
+static struct usb_function *
+get_function_head(struct usb_composite_dev *c)
+{
+	if (list_empty(&c->functions))
+		return NULL;
+	return	list_entry(c->functions.next, struct usb_function, function);
+}
+
+void usb_func_ep_reset (struct usb_gadget *g, struct usb_function *f)
+{
+	struct usb_ep	*ep;
+
+	list_for_each_entry (ep, &g->ep_list, ep_list) {
+		if (ep->driver_data == f->driver_data) {
+			usb_ep_disable (ep);
+			ep->driver_data = NULL;
+		}
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const char *get_string (const struct usb_gadget_strings *table, int id)
+{
+	struct usb_string	*s;
+
+	for (s = table->strings; s && s->s; s++)
+		if (s->id == id)
+			break;
+	return s->s;
+}
+
 static void
-composite_collect_langs(const struct usb_gadget_strings **sp, __le16 *buf)
+composite_collect_langs(struct usb_gadget_strings *sp, __le16 * buf)
 {
-	const struct usb_gadget_strings	*s;
+	struct usb_gadget_strings	*s;
 	u16				language;
 	__le16				*tmp;
 
-	while (*sp) {
-		s = *sp;
-		language = cpu_to_le16(s->language);
+	if (sp) {
+		s = sp;
+		language = cpu_to_le16 (s->language);
 		for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
 			if (*tmp == language)
-				goto repeat;
+				return;
 		}
 		*tmp++ = language;
-repeat:
-		sp++;
 	}
 }
 
 static int composite_check_string(
-	const struct usb_gadget_strings	**sp,
+	struct usb_gadget_strings	*sp,
 	void				*buf,
 	u16				language,
 	int				id
 )
 {
-	const struct usb_gadget_strings	*s;
+	struct usb_gadget_strings	*s;
 	int				value;
 
-	while (*sp) {
-		s = *sp++;
+	if (sp) {
+		s = sp;
 		if (s->language != language)
-			continue;
-		value = usb_gadget_get_string(s, id, buf);
+			return -EINVAL;
+		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)
+static int composite_lookup_string(struct usb_composite_dev *cdev,
+		void *buf, u16 language, int id)
 {
 	struct usb_function		*f;
 	int				len;
@@ -265,16 +601,16 @@ static int composite_lookup_string(void *buf, u16 language, int id)
 	/* 0 == report all available language codes */
 	if (id == 0) {
 		struct usb_string_descriptor	*s = buf;
-		const struct usb_gadget_strings	**sp;
+		struct usb_gadget_strings	*sp;
 
-		memset(s, 0, 256);
+		memset(s, 0, USB_BUFSIZ);
 		s->bDescriptorType = USB_DT_STRING;
 
-		sp = composite->strings;
+		sp = &stringtab;
 		if (sp)
 			composite_collect_langs(sp, s->wData);
 
-		list_for_each_entry (f, &composite->functions, function) {
+		list_for_each_entry (f, &cdev->functions, function) {
 			sp = f->strings;
 			if (sp)
 				composite_collect_langs(sp, s->wData);
@@ -287,19 +623,48 @@ static int composite_lookup_string(void *buf, u16 language, int id)
 
 		s->bLength = 2 * (len + 1);
 		return s->bLength;
+	} else if (cdev->func && ((id == STRING_CONFIG01)
+				|| (id == STRING_CONFIG02))) {
+		const char	*s;
+		int		l = 0;
+		int		i;
+		int		bl = 126;
+
+		if (id == STRING_CONFIG01 || cdev->func->num_conf < 2)
+			i = 0;
+		else
+			i = 1;
+
+		list_for_each_entry (f, &cdev->functions, function) {
+			s = get_string(f->strings,
+					f->config[i]->iConfiguration);
+			if (!s)
+				continue;
+
+			bl -= strlen(s);
+			if (bl <= 0)
+				break;
+			if (l > 0) {
+				config_cfunc[l] = ',';
+				config_cfunc[l + 1] = ' ';
+				l += 2;
+			}
+			strcpy(config_cfunc + l, s);
+			l = strlen(config_cfunc);
+			i = 0;
+		}
 	}
 
 	/* 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, &composite->functions, function) {
+	len = composite_check_string(&stringtab, 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);
+		len = composite_check_string(f->strings, buf, language,
+						STRID_COMPOSITE2FUNC (f,id));
 		if (len > 0)
 			return len;
 	}
@@ -308,14 +673,43 @@ static int composite_lookup_string(void *buf, u16 language, int id)
 
 /*-------------------------------------------------------------------------*/
 
-static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
+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",
+				"setup complete --> %d, %u/%u\n",
 				req->status, req->actual, req->length);
+
 }
 
+static int
+func_setup(struct usb_composite_dev *cdev,
+				const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_ctrlrequest		fctrl;
+	int				value = -EOPNOTSUPP;
+	u16				w_index = le16_to_cpu(ctrl->wIndex);
+
+	if (w_index >= MAX_COMPOSITE_INTERFACES
+			|| !cdev->interface[w_index])
+		goto out;
+
+	memcpy (&fctrl, ctrl, sizeof fctrl);
+
+	cdev->func = cdev->interface[w_index];
+	fctrl.wIndex = cpu_to_le16 (INTRF_COMPOSITE2FUNC(cdev->func, w_index));
+	fctrl.wValue = ctrl->wValue;
+	fctrl.wLength = ctrl->wLength;
+
+	spin_lock(&cdev->lock);
+	value = cdev->func->setup(cdev->gadget, &fctrl);
+	spin_unlock(&cdev->lock);
+out:
+	return value;
+}
+
+
 /*
  * The setup() callback implements all the ep0 functionality that's
  * not handled lower down, in hardware or the hardware driver(like
@@ -324,16 +718,14 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
  * the work is in config-specific setup.
  */
 static int
-composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+setup_standard(struct usb_composite_dev	*cdev, 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);
 
-	req->zero = 0;
 	switch (ctrl->bRequest) {
 
 	case USB_REQ_GET_DESCRIPTOR:
@@ -342,46 +734,51 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 		switch (w_value >> 8) {
 
 		case USB_DT_DEVICE:
-			value = min(w_length, (u16) sizeof cdev->dev);
-			memcpy(req->buf, &cdev->dev, value);
+			if (cdev->func)
+				device_desc.bNumConfigurations =
+						cdev->func->num_conf;
+			else
+				device_desc.bNumConfigurations = 1;
+
+			value = min(w_length, (u16) sizeof device_desc);
+			memcpy(req->buf, &device_desc, value);
 			break;
+#ifdef CONFIG_USB_GADGET_DUALSPEED
 		case USB_DT_DEVICE_QUALIFIER:
-			if (!is_dualspeed(gadget))
+			if (!is_dualspeed (cdev->gadget))
 				break;
-			value = min(w_length, (u16) sizeof cdev->qual);
-			memcpy(req->buf, &cdev->qual, value);
+			value = min(w_length, (u16) sizeof dev_qualifier);
+			memcpy(req->buf, &dev_qualifier, value);
 			break;
 
 		case USB_DT_OTHER_SPEED_CONFIG:
-			if (!is_dualspeed(gadget))
+			if (!is_dualspeed (cdev->gadget))
 				break;
 			// FALLTHROUGH
-
+#endif				/* CONFIG_USB_GADGET_DUALSPEED */
 		case USB_DT_CONFIG:
-			/* one config ... so it must always be index 0 */
-			if (w_value & 0xff)
-				break;
+			value = config_buf(cdev, req->buf, w_value >> 8,
+							w_value & 0xff);
 
-			value = config_buf(cdev, 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);
+			value = composite_lookup_string(cdev, 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)
+		if (cdev->gadget->a_hnp_support)
 			DBG(cdev, "HNP available\n");
-		else if (gadget->a_alt_hnp_support)
+		else if (cdev->gadget->a_alt_hnp_support)
 			DBG(cdev, "HNP needs a different root port\n");
 		else
 			VDBG(cdev, "HNP inactive\n");
@@ -392,7 +789,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 	case USB_REQ_GET_CONFIGURATION:
 		if (ctrl->bRequestType != USB_DIR_IN)
 			goto unknown;
-		*(u8 *)req->buf = cdev->config.bConfigurationValue;
+		*(u8 *)req->buf = cdev->config;
 		value = min(w_length, (u16) 1);
 		break;
 
@@ -400,37 +797,54 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 	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);
+		value = func_setup(cdev, ctrl);
 		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);
+		value = func_setup(cdev, ctrl);
 		break;
+
 	default:
+		value = func_setup(cdev, ctrl);
+		if (value != -EOPNOTSUPP)
+			break;
 unknown:
-		VDBG(dev,
-			"unknown control req%02x.%02x v%04x i%04x l%d\n",
+		VDBG(cdev,
+			"unknown standard control "
+			"req%02x.%02x v%04x i%04x l%d\n",
 			ctrl->bRequestType, ctrl->bRequest,
 			w_value, w_index, w_length);
 	}
+	return value;
+}
+
+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);
+
+	req->complete = composite_setup_complete;
+	req->zero = 0;
+	switch (ctrl->bRequestType & USB_TYPE_MASK) {
+
+	case USB_TYPE_STANDARD:
+		cdev->func = get_function_head(cdev);
+		value = setup_standard(cdev, ctrl);
+		break;
+
+	default:
+		value = func_setup(cdev, ctrl);
+	}
 
 	/* respond with data transfer before status phase? */
-	if (value >= 0) {
+	if (value >= 0 && value != DELAYED_STATUS) {
 		req->length = value;
 		req->zero = value < w_length;
 		value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
@@ -439,6 +853,12 @@ unknown:
 			req->status = 0;
 			composite_setup_complete(gadget->ep0, req);
 		}
+	} else {
+		DBG(cdev, "setup req%02x.%02x v%04x i%04x l%d --> %d\n",
+				ctrl->bRequestType, ctrl->bRequest,
+				w_value, w_index, w_length, value);
+		/* to eliminate compile warnings */
+		w_index = w_value = 0;
 	}
 
 	/* device either stalls (value < 0) or reports success */
@@ -448,17 +868,16 @@ unknown:
 static void
 composite_disconnect(struct usb_gadget *gadget)
 {
-	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
-	unsigned long			flags;
-	struct usb_ctrlrequest	req;
+	struct usb_composite_dev	*cdev = get_gadget_data (gadget);
+	struct usb_function		*f;
 
 	DBG(cdev, "disconnect\n");
 
-	memset(&req, 0, sizeof req);
-
-	spin_lock_irqsave(&cdev->lock, flags);
-	composite_reset_config(cdev, &req);
-	spin_unlock_irqrestore(&cdev->lock, flags);
+	list_for_each_entry (f, &cdev->functions, function) {
+		/*current func */
+		cdev->func = f;
+		f->disconnect (gadget);
+	}
 }
 
 /*-------------------------------------------------------------------------*/
@@ -466,37 +885,41 @@ composite_disconnect(struct usb_gadget *gadget)
 static void /* __init_or_exit */
 composite_unbind(struct usb_gadget *gadget)
 {
-	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
-	struct usb_function		*f;
+	struct usb_composite_dev	*cdev = get_gadget_data (gadget);
 
 	DBG(cdev, "unbind\n");
 
-	list_for_each_entry (f, &cdev->driver->functions, function) {
-		if (f->unbind)
-			f->unbind(cdev);
-		if (f == cdev->current_bind)
-			break;
-	}
-	if (composite->unbind)
-		composite->unbind(cdev);
+	/* we've already been disconnected ... no i/o is active */
 	if (cdev->req) {
-		kfree(cdev->req->buf);
-		usb_ep_free_request(gadget->ep0, cdev->req);
+		cdev->req->length = USB_BUFSIZ;
+		free_ep_req(gadget->ep0, cdev->req);
+		cdev->req = NULL;
 	}
 	kfree(cdev);
+
 	set_gadget_data(gadget, NULL);
 }
 
-static int __init
+static int __devinit
 composite_bind(struct usb_gadget *gadget)
 {
-	struct usb_composite_dev	*cdev;
-	struct usb_function		*f;
 	int				status = -ENOMEM;
+	int				gcnum;
+	int				i;
+
+	gcnum = usb_gadget_controller_number(gadget);
+	if (gcnum >= 0)
+		device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
+	else {
+		printk(KERN_WARNING "%s: controller '%s' not recognized\n",
+		       shortname, gadget->name);
+		device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+	}
 
-	cdev = kzalloc(sizeof *cdev, SLAB_KERNEL);
+	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
 	if (!cdev)
 		return status;
+	INIT_LIST_HEAD(&cdev->functions);
 
 	spin_lock_init(&cdev->lock);
 	cdev->gadget = gadget;
@@ -506,136 +929,109 @@ composite_bind(struct usb_gadget *gadget)
 	cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
 	if (!cdev->req)
 		goto fail;
-	cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
+	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->driver = composite;
-	cdev->dev = *composite->dev;
-	cdev->dev.bMaxPacketSize0 = gadget->ep0->maxpacket;
-
-	usb_gadget_set_selfpowered(gadget);
-
-	/* mostly to assign strings for whole device, like serial number,
-	 * but potentially also to update power state and consumption etc
-	 */
-	if (composite->bind) {
-		status = composite->bind(cdev);
-		if (status < 0)
-			goto fail;
-	}
-
-	/* 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.
-	 */
 	usb_ep_autoconfig_reset(gadget);
-	list_for_each_entry (f, &cdev->driver->functions, function) {
-		cdev->current_bind = f;
-		status = f->bind(cdev);
-		if (status < 0)
-			goto fail;
-
-		if ((is_dualspeed(gadget) && !f->hs_descriptors)
-				|| !f->descriptors) {
-			status = -ENXIO;
+	/* support optional vendor/distro customization */
+	if (idVendor) {
+		if (!idProduct) {
+			dev_err (&gadget->dev, "idVendor needs idProduct!\n");
+			status = -EINVAL;
 			goto fail;
 		}
+		device_desc.idVendor = cpu_to_le16(idVendor);
+		device_desc.idProduct = cpu_to_le16(idProduct);
+		if (bcdDevice)
+			device_desc.bcdDevice = cpu_to_le16(bcdDevice);
+	}
+	if (iManufacturer)
+		strlcpy (manufacturer, iManufacturer, sizeof manufacturer);
+	else
+		snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
+				init_utsname()->sysname,
+				init_utsname()->release,
+				gadget->name);
+	if (iProduct)
+		strlcpy (product_desc, iProduct, sizeof product_desc);
+	if (iSerialNumber)
+		strlcpy(serial_number, iSerialNumber, sizeof serial_number);
+	else for (i = 0; i < strlen(DRIVER_VERSION)
+			&& i < sizeof(serial_number); i ++) {
+		unsigned char		c = DRIVER_VERSION[i];
+		if (!c)
+			break;
+		sprintf(&serial_number[i], "%x", c);
 	}
-	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 (is_dualspeed(gadget)) {
-		cdev->qual.bLength = sizeof cdev->qual;
-		cdev->qual.bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+	device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
 
-		cdev->qual.bcdUSB = cdev->dev.bcdUSB;
-		cdev->qual.bDeviceClass = cdev->dev.bDeviceClass;
-		cdev->qual.bDeviceProtocol = cdev->dev.bDeviceProtocol;
+	if (is_otg(gadget)) {
+		otg_descriptor.bmAttributes |= USB_OTG_HNP,
+		config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+		config_desc.bMaxPower = 4;
+	}
 
+	if (is_dualspeed(gadget)) {
 		/* assume ep0 uses the same value for both speeds ... */
-		cdev->qual.bMaxPacketSize0 = cdev->dev.bMaxPacketSize0;
-
-		cdev->qual.bNumConfigurations = cdev->dev.bNumConfigurations;
+		dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
+		dev_qualifier.bcdUSB = device_desc.bcdUSB;
+		dev_qualifier.bNumConfigurations =
+			device_desc.bNumConfigurations;
 	}
 
-	INFO(cdev, "%s ready\n", composite->name);
 	return 0;
-
 fail:
 	composite_unbind(gadget);
 	return status;
 }
 
-int usb_composite_string_id(struct usb_composite_dev *cdev)
-{
-	if (cdev->current_bind && cdev->next_string_id < 255)
-		return cdev->next_string_id++;
-	return -ENODEV;
-}
-
-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_composite_dev	*cdev = get_gadget_data(gadget);
+	struct usb_composite_dev	*cdev = get_gadget_data (gadget);
 	struct usb_function		*f;
 
 	DBG(cdev, "suspend\n");
-	/* revisit -- iterate cdev->interface? */
-	list_for_each_entry (f, &cdev->driver->functions, function) {
-		if (!f->suspend)
-			continue;
-		f->suspend(cdev);
+
+	list_for_each_entry (f, &cdev->functions, function) {
+		/*current func */
+		cdev->func = f;
+		f->suspend (gadget);
 	}
 }
 
 static void
 composite_resume(struct usb_gadget *gadget)
 {
-	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
+	struct usb_composite_dev	*cdev = get_gadget_data (gadget);
 	struct usb_function		*f;
 
 	DBG(cdev, "resume\n");
-	/* revisit -- iterate cdev->interface? */
-	list_for_each_entry (f, &cdev->driver->functions, function) {
-		if (!f->resume)
-			continue;
-		f->resume(cdev);
+
+	list_for_each_entry (f, &cdev->functions, function) {
+		/*current func */
+		cdev->func = f;
+		f->resume (gadget);
 	}
 }
 
-
 /*-------------------------------------------------------------------------*/
 
-static struct usb_gadget_driver composite_driver = {
+static struct usb_gadget_driver composite_drv = {
+#ifdef CONFIG_USB_GADGET_DUALSPEED
 	.speed		= USB_SPEED_HIGH,
+#else
+	.speed		= USB_SPEED_FULL,
+#endif
+	.function	= (char *) product_desc,
 
 	.bind		= composite_bind,
 	.unbind		= __exit_p(composite_unbind),
@@ -647,33 +1043,60 @@ static struct usb_gadget_driver composite_driver = {
 	.resume		= composite_resume,
 
 	.driver	= {
-		.owner		= THIS_MODULE,
+		.name	= (char *) shortname,
+		.owner	= THIS_MODULE,
 	},
 };
 
-int usb_composite_register(struct usb_composite_driver *d)
+
+int usb_func_register(struct usb_function *f)
 {
-	struct usb_function	*f;
+	int				status = -EINVAL;
 
-	if (!d || !d->dev || composite)
+	if (!f || !f->bind)
 		return -EINVAL;
 
-	list_for_each_entry (f, &d->functions, function) {
-		if (!f->bind || !f->setup)
-			return -EINVAL;
+	/* current func */
+	cdev->func = f;
+	status = f->bind (cdev->gadget);
+	if (status < 0)
+		goto fail;
 
-		/* REVISIT erm, hs_descriptors may be unknown here? */
-		if (!f->hs_descriptors) {
-			composite_driver.speed = USB_SPEED_FULL;
-			break;
-		}
-	}
+	list_add_tail(&f->function, &cdev->functions);
+
+	return 0;
+fail:
+	return status;
+}
+EXPORT_SYMBOL(usb_func_register);
+
+void usb_func_unregister(struct usb_function *f)
+{
+	if (!f)
+		return;
 
-	if (!d->name)
-		d->name = "composite";
-	composite_driver.function =  (char *) d->name;
-	composite_driver.driver.name = d->name;
-	composite = d;
+	/* current func */
+	cdev->func = f;
+	if (f->unbind)
+		f->unbind(cdev->gadget);
 
-	return usb_gadget_register_driver(&composite_driver);
+	usb_func_ep_reset (cdev->gadget, f);
+	list_del(&f->function);
 }
+EXPORT_SYMBOL(usb_func_unregister);
+
+MODULE_AUTHOR("David Brownell, Felipe Balbi, Ragner Magalhaes");
+MODULE_LICENSE("GPL");
+
+static int __init init (void)
+{
+	return usb_gadget_register_driver (&composite_drv);
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+	usb_gadget_unregister_driver (&composite_drv);
+}
+module_exit (cleanup);
+
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 82fdb13..49e5d00 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -5,6 +5,32 @@
 
 #ifdef __KERNEL__
 
+#if	defined(CONFIG_USB_COMPOSITE) || defined(CONFIG_USB_COMPOSITE_MODULE)
+#define USB_COMPOSITE
+#define is_composite()	1
+#else
+#define is_composite()	0
+#endif
+
+#define func_printk(level,f,fmt,args...)	\
+printk(level "%s: " fmt , (f)->name , ## args)
+
+#define INTRF_FUNC2COMPOSITE(f,i)		\
+i + (f)->interface_shift
+
+#define INTRF_COMPOSITE2FUNC(f,i)		\
+i - (f)->interface_shift
+
+#define STRID_FUNC2COMPOSITE(f,i)		\
+i + (f)->stringid_shift
+
+#define STRID_COMPOSITE2FUNC(f,i)		\
+i - (f)->stringid_shift
+
+
+#define MAX_CONFIGS		2
+
+
 /* Support for composite gadgets, built from distinct function drivers.
  * Example:  one offering both network and mass storage functionality.
  */
@@ -17,13 +43,14 @@ struct usb_composite_driver;
  * @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
+ * @configs: function's configs supported, max two configs.
  * @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()
+ * @driver_data: function's driver data.
+ * @num_conf: number of configurations in the function.
+ * @interface_shift: function's interface shift in the composite driver.
+ * @stringid_shift: function's string id shift in the composite driver.
  * @bind: before the gadget can register, all of its functions bind() to the
  *	available resources including identifiers for strings, interfaces,
  *	and endpoints
@@ -40,97 +67,104 @@ struct usb_composite_driver;
  */
 struct usb_function {
 	const char				*name;
-	const struct usb_gadget_strings		**strings;
-	const struct usb_descriptor_header	**descriptors;
-	const struct usb_descriptor_header	**hs_descriptors;
+	struct usb_gadget_strings		*strings;
+	const struct usb_config_descriptor	*config[MAX_CONFIGS];
 
 	struct list_head			function;
+	/* function's driver */
+	void			*driver_data;
 
-	/* REVISIT want multi-config at *same* speed too ...
-	 * e.g. one has RNDIS, another has CDC-Ethernet
-	 * not just high speed versions of each.
-	 */
+	u8			num_conf;
+	u8			interface_shift;
+	u8			stringid_shift;
 
-	int			(*bind)(struct usb_composite_dev *);
-	void			(*unbind)(struct usb_composite_dev *);
-	int			(*setup)(struct usb_composite_dev *,
+	int			(*bind)(struct usb_gadget *);
+	void			(*unbind)(struct usb_gadget *);
+	int			(*setup)(struct usb_gadget *,
 					const struct usb_ctrlrequest *);
-	void			(*disconnect)(struct usb_composite_dev *);
-	void			(*suspend)(struct usb_composite_dev *);
-	void			(*resume)(struct usb_composite_dev *);
+	void			(*disconnect)(struct usb_gadget *);
+	void			(*suspend)(struct usb_gadget *);
+	void			(*resume)(struct usb_gadget *);
 };
 
-/**
- * struct usb_composite_driver -- groups functions into one gadget driver
- * @name: for diagnostics, identifies the gadget driver
- * @dev: descriptor for the device
- * @strings: tables of strings, keyed by identifiers assigned during bind()
- *	and language IDs provided in control requests
- * @functions: each function on this list when usb_composite_init() is called
- *	will be bound and initialized
- * @bind: optionally used for whole-device resources such as string IDs,
- *	updating the device and configuration descriptors before functions
- *	bind(); and updating whole-device policies.
- * @unbind: optionally reverses bind()
- * @setup: optionally used to delegate class and vendor control requests
- */
-struct usb_composite_driver {
-	const char				*name;
-	const struct usb_device_descriptor	*dev;
-	const struct usb_gadget_strings		**strings;
-
-	/* REVISIT want a general "add more descriptors for config N"
-	 * hook; OTG would fall out naturally
-	 */
-
-	struct list_head	functions;
+extern int usb_func_register(struct usb_function *);
+extern void usb_func_unregister(struct usb_function *);
 
-	int			(*bind)(struct usb_composite_dev *);
-	int			(*unbind)(struct usb_composite_dev *);
-	int			(*setup)(struct usb_composite_dev *,
-					const struct usb_ctrlrequest *);
+/* struct usb_gadget_dev - represents one usb gadget
+ * @gadget: read-only, abstracts the gadget's usb peripheral controller
+ * @req: used for control responses; buffer is pre-allocated
+ * @config: selected config
+ * @func: function of the gadget device
+ */
+struct usb_gadget_dev {
+	spinlock_t			lock;
+	struct usb_gadget		*gadget;
+	struct usb_request		*req;
+	u8				config;
 
-	/* REVISIT disconnect(), suspend(), resume() too ?? */
+	struct usb_function	*func;
 };
 
-extern int usb_composite_register(struct usb_composite_driver *);
-
-
 #define	MAX_COMPOSITE_INTERFACES		8	/* max 16 */
 
 /**
- * struct usb_composite_device - represents one composite usb gadget
+ * struct usb_composite_dev - represents one composite usb gadget
  * @gadget: read-only, abstracts the gadget's usb peripheral controller
  * @req: used for control responses; buffer is pre-allocated
- * @dev: device descriptor
- * @config: configuration descriptor; if the device is not configured,
- *	its bConfigurationValue is zero and other fields are ignored
+ * @config: selected config
+ * @func: current function
  */
 struct usb_composite_dev {
 	spinlock_t			lock;
 	struct usb_gadget		*gadget;
 	struct usb_request		*req;
-	struct usb_device_descriptor	dev;
-	struct usb_config_descriptor	config;
+	u8				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 usb_function	*func;
+	struct list_head	functions;
 
-	/* INTERNALS -- not for function drivers */
 	u8			next_string_id;
 	u8			next_interface_id;
-	struct usb_function	*current_bind;
 	struct usb_function	*interface[MAX_COMPOSITE_INTERFACES];
-
-	struct usb_composite_driver	*driver;
-	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 struct usb_gadget_dev *
+get_usb_gadget_dev (struct usb_gadget *g)
+{
+	return get_gadget_data (g);
+}
+
+static inline struct usb_request *
+get_usb_gadget_request (struct usb_gadget *g)
+{
+	return get_usb_gadget_dev(g)->req;
+}
+
+static inline void
+set_usb_function (struct usb_gadget *g, struct usb_function *f)
+{
+	get_usb_gadget_dev (g)->func = f;
+}
+
+static inline struct usb_function *
+get_usb_function (struct usb_gadget *g)
+{
+	return get_usb_gadget_dev (g)->func;
+}
+
+static inline void
+set_usb_function_data (struct usb_function *f, void *d)
+{
+	f->driver_data = d;
+}
+
+static inline void *
+get_usb_function_data (struct usb_gadget *g)
+{
+	return get_usb_function (g)->driver_data;
+}
 
 #endif  /* __KERNEL__ */
 
-- 
1.5.0.4.501.gb08b




-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
[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.