[PATCH 6/6] Composite Ether gadget support

Ragner Magalhaes <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
This patch adds some features that allow the ether gadget
to work as a usb_function module.

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

:100644 100644 1dd8b57... dd0867f... M	drivers/usb/gadget/ether.c

diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 1dd8b57..dd0867f 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -49,6 +49,7 @@
 #include <linux/usb/ch9.h>
 #include <linux/usb/cdc.h>
 #include <linux/usb_gadget.h>
+#include <linux/usb/composite.h>
 
 #include <linux/random.h>
 #include <linux/netdevice.h>
@@ -118,6 +119,7 @@ static const char driver_desc [] = DRIVER_DESC;
 struct eth_dev {
 	spinlock_t		lock;
 	struct usb_gadget	*gadget;
+	struct usb_function	*f;
 	struct usb_request	*req;		/* for control responses */
 	struct usb_request	*stat_req;	/* for cdc & rndis status */
 
@@ -145,6 +147,8 @@ struct eth_dev {
 	u8			host_mac [ETH_ALEN];
 };
 
+static struct usb_function eth_driver;
+
 /* This version autoconfigures as much as possible at run-time.
  *
  * It also ASSUMES a self-powered device, without remote wakeup,
@@ -417,14 +421,15 @@ static inline int BITRATE(struct usb_gadget *g)
 
 #define STRING_MANUFACTURER		1
 #define STRING_PRODUCT			2
-#define STRING_ETHADDR			3
-#define STRING_DATA			4
-#define STRING_CONTROL			5
+#define STRING_SERIALNUMBER		3
+#define STRING_CDC			4
+#define STRING_RNDIS			5
+
 #define STRING_RNDIS_CONTROL		6
-#define STRING_CDC			7
+#define STRING_DATA			7
 #define STRING_SUBSET			8
-#define STRING_RNDIS			9
-#define STRING_SERIALNUMBER		10
+#define STRING_CONTROL			9
+#define STRING_ETHADDR			10
 
 /* holds our biggest descriptor (or RNDIS response) */
 #define USB_BUFSIZ	256
@@ -982,7 +987,7 @@ static struct usb_gadget_strings	stringtab = {
  * complications: class descriptors, and an altsetting.
  */
 static int
-config_buf (enum usb_device_speed speed,
+config_buf (struct eth_dev *dev,
 	u8 *buf, u8 type,
 	unsigned index, int is_otg)
 {
@@ -990,7 +995,7 @@ config_buf (enum usb_device_speed speed,
 	const struct usb_config_descriptor	*config;
 	const struct usb_descriptor_header	**function;
 #ifdef CONFIG_USB_GADGET_DUALSPEED
-	int				hs = (speed == USB_SPEED_HIGH);
+	int	hs = (dev->gadget->speed == USB_SPEED_HIGH);
 
 	if (type == USB_DT_OTHER_SPEED_CONFIG)
 		hs = !hs;
@@ -1017,9 +1022,16 @@ config_buf (enum usb_device_speed speed,
 	}
 
 	/* for now, don't advertise srp-only devices */
-	if (!is_otg)
+	if (!is_otg || is_composite())
 		function++;
 
+	if (is_composite()) {
+		dev->f->config[index] = config;
+		buf += dev->req->length;
+		len = USB_BUFSIZ - dev->req->length;
+		return usb_descriptor_fillbuf(buf, len, function);
+	}
+
 	len = usb_gadget_config_buf (config, buf, USB_BUFSIZ, function);
 	if (len < 0)
 		return len;
@@ -1323,14 +1335,6 @@ static void issue_start_status (struct eth_dev *dev)
 
 /*-------------------------------------------------------------------------*/
 
-static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req)
-{
-	if (req->status || req->actual != req->length)
-		DEBUG ((struct eth_dev *) ep->driver_data,
-				"setup complete --> %d, %d/%d\n",
-				req->status, req->actual, req->length);
-}
-
 #ifdef CONFIG_USB_ETH_RNDIS
 
 static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req)
@@ -1370,7 +1374,7 @@ static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req)
 static int
 eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 {
-	struct eth_dev		*dev = get_gadget_data (gadget);
+	struct eth_dev		*dev = get_usb_function_data (gadget);
 	struct usb_request	*req = dev->req;
 	int			value = -EOPNOTSUPP;
 	u16			wIndex = le16_to_cpu(ctrl->wIndex);
@@ -1380,7 +1384,6 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 	/* descriptors just go into the pre-allocated ep0 buffer,
 	 * while config change events may enable network traffic.
 	 */
-	req->complete = eth_setup_complete;
 	switch (ctrl->bRequest) {
 
 	case USB_REQ_GET_DESCRIPTOR:
@@ -1406,7 +1409,7 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 			// FALLTHROUGH
 #endif /* CONFIG_USB_GADGET_DUALSPEED */
 		case USB_DT_CONFIG:
-			value = config_buf (gadget->speed, req->buf,
+			value = config_buf (dev, req->buf,
 					wValue >> 8,
 					wValue & 0xff,
 					gadget->is_otg);
@@ -1422,7 +1425,6 @@ eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 			break;
 		}
 		break;
-
 	case USB_REQ_SET_CONFIGURATION:
 		if (ctrl->bRequestType != 0)
 			break;
@@ -1521,9 +1523,10 @@ done_set_intf:
 
 		/* for CDC, iff carrier is on, data interface is active. */
 		if (rndis_active(dev) || wIndex != 1)
-			*(u8 *)req->buf = 0;
+			*(u8 *)req->buf = INTRF_FUNC2COMPOSITE(dev->f,0);
 		else
-			*(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0;
+			*(u8 *)req->buf = INTRF_FUNC2COMPOSITE(dev->f,
+					netif_carrier_ok (dev->net) ? 1 : 0);
 		value = min (wLength, (u16) 1);
 		break;
 
@@ -1599,19 +1602,6 @@ done_set_intf:
 			wValue, wIndex, wLength);
 	}
 
-	/* respond with data transfer before status phase? */
-	if (value >= 0) {
-		req->length = value;
-		req->zero = value < wLength
-				&& (value % gadget->ep0->maxpacket) == 0;
-		value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
-		if (value < 0) {
-			DEBUG (dev, "ep_queue --> %d\n", value);
-			req->status = 0;
-			eth_setup_complete (gadget->ep0, req);
-		}
-	}
-
 	/* host either stalls (value < 0) or reports success */
 	return value;
 }
@@ -1619,7 +1609,7 @@ done_set_intf:
 static void
 eth_disconnect (struct usb_gadget *gadget)
 {
-	struct eth_dev		*dev = get_gadget_data (gadget);
+	struct eth_dev		*dev = get_usb_function_data (gadget);
 	unsigned long		flags;
 
 	spin_lock_irqsave (&dev->lock, flags);
@@ -2218,17 +2208,19 @@ eth_req_free (struct usb_ep *ep, struct usb_request *req)
 static void /* __init_or_exit */
 eth_unbind (struct usb_gadget *gadget)
 {
-	struct eth_dev		*dev = get_gadget_data (gadget);
+	struct eth_dev		*dev = get_usb_function_data (gadget);
 
 	DEBUG (dev, "unbind\n");
+
+	if (is_composite())
+		eth_reset_config (dev);
+
 	rndis_deregister (dev->rndis_config);
 	rndis_exit ();
 
 	/* we've already been disconnected ... no i/o is active */
-	if (dev->req) {
-		eth_req_free (gadget->ep0, dev->req);
+	if (dev->req)
 		dev->req = NULL;
-	}
 	if (dev->stat_req) {
 		eth_req_free (dev->status_ep, dev->stat_req);
 		dev->stat_req = NULL;
@@ -2239,7 +2231,6 @@ eth_unbind (struct usb_gadget *gadget)
 
 	/* assuming we used keventd, it must quiesce too */
 	flush_scheduled_work ();
-	set_gadget_data (gadget, NULL);
 }
 
 static u8 __devinit nibble (unsigned char c)
@@ -2377,7 +2368,6 @@ eth_bind (struct usb_gadget *gadget)
 	}
 
 	/* all we really need is bulk IN/OUT */
-	usb_ep_autoconfig_reset (gadget);
 	in_ep = usb_ep_autoconfig (gadget, &fs_source_desc);
 	if (!in_ep) {
 autoconf_fail:
@@ -2521,10 +2511,7 @@ autoconf_fail:
 	SET_ETHTOOL_OPS(net, &ops);
 
 	/* preallocate control message data and buffer */
-	dev->req = eth_req_alloc (gadget->ep0, USB_BUFSIZ, GFP_KERNEL);
-	if (!dev->req)
-		goto fail;
-	dev->req->complete = eth_setup_complete;
+	dev->req = get_usb_gadget_request (gadget);
 
 	/* ... and maybe likewise for status transfer */
 #if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
@@ -2532,7 +2519,6 @@ autoconf_fail:
 		dev->stat_req = eth_req_alloc (dev->status_ep,
 					STATUS_BYTECOUNT, GFP_KERNEL);
 		if (!dev->stat_req) {
-			eth_req_free (gadget->ep0, dev->req);
 			goto fail;
 		}
 		dev->stat_req->context = NULL;
@@ -2541,8 +2527,10 @@ autoconf_fail:
 
 	/* finish hookup to lower layer ... */
 	dev->gadget = gadget;
-	set_gadget_data (gadget, dev);
-	gadget->ep0->driver_data = dev;
+	dev->f = get_usb_function(gadget);
+	set_usb_function_data (dev->f, dev);
+
+	dev->f->num_conf = rndis ? 2 : 1;
 
 	/* two kinds of host-initiated state changes:
 	 *  - iff DATA transfer is active, carrier is "on"
@@ -2615,7 +2603,7 @@ fail:
 static void
 eth_suspend (struct usb_gadget *gadget)
 {
-	struct eth_dev		*dev = get_gadget_data (gadget);
+	struct eth_dev		*dev = get_usb_function_data (gadget);
 
 	DEBUG (dev, "suspend\n");
 	dev->suspended = 1;
@@ -2624,7 +2612,7 @@ eth_suspend (struct usb_gadget *gadget)
 static void
 eth_resume (struct usb_gadget *gadget)
 {
-	struct eth_dev		*dev = get_gadget_data (gadget);
+	struct eth_dev		*dev = get_usb_function_data (gadget);
 
 	DEBUG (dev, "resume\n");
 	dev->suspended = 0;
@@ -2632,10 +2620,10 @@ eth_resume (struct usb_gadget *gadget)
 
 /*-------------------------------------------------------------------------*/
 
-static struct usb_gadget_driver eth_driver = {
-	.speed		= DEVSPEED,
+static struct usb_function eth_driver = {
+	.name		= (char *) shortname,
+	.strings	= &stringtab,
 
-	.function	= (char *) driver_desc,
 	.bind		= eth_bind,
 	.unbind		= eth_unbind,
 
@@ -2644,11 +2632,6 @@ static struct usb_gadget_driver eth_driver = {
 
 	.suspend	= eth_suspend,
 	.resume		= eth_resume,
-
-	.driver	= {
-		.name		= (char *) shortname,
-		.owner		= THIS_MODULE,
-	},
 };
 
 MODULE_DESCRIPTION (DRIVER_DESC);
@@ -2658,13 +2641,13 @@ MODULE_LICENSE ("GPL");
 
 static int __init init (void)
 {
-	return usb_gadget_register_driver (&eth_driver);
+	return usb_func_register(&eth_driver);
 }
 module_init (init);
 
 static void __exit cleanup (void)
 {
-	usb_gadget_unregister_driver (&eth_driver);
+	usb_func_unregister(&eth_driver);
 }
 module_exit (cleanup);
 
-- 
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.