[PATCH v4 1/4] usb: gadget: add isochronous support to gadget zero

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add two isochronous endpoints to the gadget zero source/sink
function. They are enabled by selecting alternate interface 1, so
by default they are not enabled. Module parameters for setting all
the isoc endpoint characteristics are also provided.

Signed-off-by: Paul Zimmerman <paulz@xxxxxxxxxxxx>
---
 drivers/usb/gadget/f_loopback.c   |    4 +-
 drivers/usb/gadget/f_sourcesink.c |  413 ++++++++++++++++++++++++++++++++-----
 drivers/usb/gadget/g_zero.h       |    5 +-
 drivers/usb/gadget/zero.c         |   19 ++-
 4 files changed, 376 insertions(+), 65 deletions(-)

diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c
index 2c0cd82..7275706 100644
--- a/drivers/usb/gadget/f_loopback.c
+++ b/drivers/usb/gadget/f_loopback.c
@@ -286,7 +286,7 @@ static void disable_loopback(struct f_loopback *loop)
 	struct usb_composite_dev	*cdev;
 
 	cdev = loop->function.config->cdev;
-	disable_endpoints(cdev, loop->in_ep, loop->out_ep);
+	disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL);
 	VDBG(cdev, "%s disabled\n", loop->function.name);
 }
 
@@ -329,7 +329,7 @@ fail0:
 	 * than 'buflen' bytes each.
 	 */
 	for (i = 0; i < qlen && result == 0; i++) {
-		req = alloc_ep_req(ep);
+		req = alloc_ep_req(ep, 0);
 		if (req) {
 			req->complete = loopback_complete;
 			result = usb_ep_queue(ep, req, GFP_ATOMIC);
diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c
index 7aa7ac8..911e6f3 100644
--- a/drivers/usb/gadget/f_sourcesink.c
+++ b/drivers/usb/gadget/f_sourcesink.c
@@ -51,6 +51,8 @@ struct f_sourcesink {
 
 	struct usb_ep		*in_ep;
 	struct usb_ep		*out_ep;
+	struct usb_ep		*iso_in_ep;
+	struct usb_ep		*iso_out_ep;
 };
 
 static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
@@ -59,18 +61,45 @@ static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
 }
 
 static unsigned pattern;
-module_param(pattern, uint, 0);
-MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 ");
+module_param(pattern, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none");
+
+static unsigned isoc_interval = 4;
+module_param(isoc_interval, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_interval, "1 - 16");
+
+static unsigned isoc_maxpacket = 1024;
+module_param(isoc_maxpacket, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
+
+static unsigned isoc_mult;
+module_param(isoc_mult, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)");
+
+static unsigned isoc_maxburst;
+module_param(isoc_maxburst, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
 
 /*-------------------------------------------------------------------------*/
 
-static struct usb_interface_descriptor source_sink_intf = {
-	.bLength =		sizeof source_sink_intf,
+static struct usb_interface_descriptor source_sink_intf_alt0 = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
 	.bDescriptorType =	USB_DT_INTERFACE,
 
+	.bAlternateSetting =	0,
 	.bNumEndpoints =	2,
 	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
-	/* .iInterface = DYNAMIC */
+	/* .iInterface		= DYNAMIC */
+};
+
+static struct usb_interface_descriptor source_sink_intf_alt1 = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	4,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	/* .iInterface		= DYNAMIC */
 };
 
 /* full speed support: */
@@ -91,10 +120,36 @@ static struct usb_endpoint_descriptor fs_sink_desc = {
 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
 };
 
+static struct usb_endpoint_descriptor fs_iso_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	cpu_to_le16(1023),
+	.bInterval =		4,
+};
+
+static struct usb_endpoint_descriptor fs_iso_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	cpu_to_le16(1023),
+	.bInterval =		4,
+};
+
 static struct usb_descriptor_header *fs_source_sink_descs[] = {
-	(struct usb_descriptor_header *) &source_sink_intf,
+	(struct usb_descriptor_header *) &source_sink_intf_alt0,
 	(struct usb_descriptor_header *) &fs_sink_desc,
 	(struct usb_descriptor_header *) &fs_source_desc,
+	(struct usb_descriptor_header *) &source_sink_intf_alt1,
+#define FS_ALT_IFC_1_OFFSET	3
+	(struct usb_descriptor_header *) &fs_sink_desc,
+	(struct usb_descriptor_header *) &fs_source_desc,
+	(struct usb_descriptor_header *) &fs_iso_sink_desc,
+	(struct usb_descriptor_header *) &fs_iso_source_desc,
 	NULL,
 };
 
@@ -116,10 +171,34 @@ static struct usb_endpoint_descriptor hs_sink_desc = {
 	.wMaxPacketSize =	cpu_to_le16(512),
 };
 
+static struct usb_endpoint_descriptor hs_iso_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+	.bInterval =		4,
+};
+
+static struct usb_endpoint_descriptor hs_iso_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+	.bInterval =		4,
+};
+
 static struct usb_descriptor_header *hs_source_sink_descs[] = {
-	(struct usb_descriptor_header *) &source_sink_intf,
+	(struct usb_descriptor_header *) &source_sink_intf_alt0,
+	(struct usb_descriptor_header *) &hs_source_desc,
+	(struct usb_descriptor_header *) &hs_sink_desc,
+	(struct usb_descriptor_header *) &source_sink_intf_alt1,
+#define HS_ALT_IFC_1_OFFSET	3
 	(struct usb_descriptor_header *) &hs_source_desc,
 	(struct usb_descriptor_header *) &hs_sink_desc,
+	(struct usb_descriptor_header *) &hs_iso_source_desc,
+	(struct usb_descriptor_header *) &hs_iso_sink_desc,
 	NULL,
 };
 
@@ -136,6 +215,7 @@ static struct usb_endpoint_descriptor ss_source_desc = {
 struct usb_ss_ep_comp_descriptor ss_source_comp_desc = {
 	.bLength =		USB_DT_SS_EP_COMP_SIZE,
 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
 	.bMaxBurst =		0,
 	.bmAttributes =		0,
 	.wBytesPerInterval =	0,
@@ -152,17 +232,64 @@ static struct usb_endpoint_descriptor ss_sink_desc = {
 struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = {
 	.bLength =		USB_DT_SS_EP_COMP_SIZE,
 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
 	.bMaxBurst =		0,
 	.bmAttributes =		0,
 	.wBytesPerInterval =	0,
 };
 
+static struct usb_endpoint_descriptor ss_iso_source_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+	.bInterval =		4,
+};
+
+struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = {
+	.bLength =		USB_DT_SS_EP_COMP_SIZE,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	.bMaxBurst =		0,
+	.bmAttributes =		0,
+	.wBytesPerInterval =	cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_iso_sink_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bmAttributes =		USB_ENDPOINT_XFER_ISOC,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+	.bInterval =		4,
+};
+
+struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
+	.bLength =		USB_DT_SS_EP_COMP_SIZE,
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	.bMaxBurst =		0,
+	.bmAttributes =		0,
+	.wBytesPerInterval =	cpu_to_le16(1024),
+};
+
 static struct usb_descriptor_header *ss_source_sink_descs[] = {
-	(struct usb_descriptor_header *) &source_sink_intf,
+	(struct usb_descriptor_header *) &source_sink_intf_alt0,
 	(struct usb_descriptor_header *) &ss_source_desc,
 	(struct usb_descriptor_header *) &ss_source_comp_desc,
 	(struct usb_descriptor_header *) &ss_sink_desc,
 	(struct usb_descriptor_header *) &ss_sink_comp_desc,
+	(struct usb_descriptor_header *) &source_sink_intf_alt1,
+#define SS_ALT_IFC_1_OFFSET	5
+	(struct usb_descriptor_header *) &ss_source_desc,
+	(struct usb_descriptor_header *) &ss_source_comp_desc,
+	(struct usb_descriptor_header *) &ss_sink_desc,
+	(struct usb_descriptor_header *) &ss_sink_comp_desc,
+	(struct usb_descriptor_header *) &ss_iso_source_desc,
+	(struct usb_descriptor_header *) &ss_iso_source_comp_desc,
+	(struct usb_descriptor_header *) &ss_iso_sink_desc,
+	(struct usb_descriptor_header *) &ss_iso_sink_comp_desc,
 	NULL,
 };
 
@@ -196,9 +323,10 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
 	id = usb_interface_id(c, f);
 	if (id < 0)
 		return id;
-	source_sink_intf.bInterfaceNumber = id;
+	source_sink_intf_alt0.bInterfaceNumber = id;
+	source_sink_intf_alt1.bInterfaceNumber = id;
 
-	/* allocate endpoints */
+	/* allocate bulk endpoints */
 	ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
 	if (!ss->in_ep) {
 autoconf_fail:
@@ -213,12 +341,74 @@ autoconf_fail:
 		goto autoconf_fail;
 	ss->out_ep->driver_data = cdev;	/* claim */
 
+	/* sanity check the isoc module parameters */
+	if (isoc_interval < 1)
+		isoc_interval = 1;
+	if (isoc_interval > 16)
+		isoc_interval = 16;
+	if (isoc_mult > 2)
+		isoc_mult = 2;
+	if (isoc_maxburst > 15)
+		isoc_maxburst = 15;
+
+	/* fill in the FS isoc descriptors from the module parameters */
+	fs_iso_source_desc.wMaxPacketSize = isoc_maxpacket > 1023 ?
+						1023 : isoc_maxpacket;
+	fs_iso_source_desc.bInterval = isoc_interval;
+	fs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket > 1023 ?
+						1023 : isoc_maxpacket;
+	fs_iso_sink_desc.bInterval = isoc_interval;
+
+	/* allocate iso endpoints */
+	ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc);
+	if (!ss->iso_in_ep)
+		goto no_iso;
+	ss->iso_in_ep->driver_data = cdev;	/* claim */
+
+	ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc);
+	if (ss->iso_out_ep) {
+		ss->iso_out_ep->driver_data = cdev;	/* claim */
+	} else {
+		ss->iso_in_ep->driver_data = NULL;
+		ss->iso_in_ep = NULL;
+no_iso:
+		/*
+		 * We still want to work even if the UDC doesn't have isoc
+		 * endpoints, so null out the alt interface that contains
+		 * them and continue.
+		 */
+		fs_source_sink_descs[FS_ALT_IFC_1_OFFSET] = NULL;
+		hs_source_sink_descs[HS_ALT_IFC_1_OFFSET] = NULL;
+		ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL;
+	}
+
+	if (isoc_maxpacket > 1024)
+		isoc_maxpacket = 1024;
+
 	/* support high speed hardware */
 	if (gadget_is_dualspeed(c->cdev->gadget)) {
 		hs_source_desc.bEndpointAddress =
 				fs_source_desc.bEndpointAddress;
 		hs_sink_desc.bEndpointAddress =
 				fs_sink_desc.bEndpointAddress;
+
+		/*
+		 * Fill in the HS isoc descriptors from the module parameters.
+		 * We assume that the user knows what they are doing and won't
+		 * give parameters that their UDC doesn't support.
+		 */
+		hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
+		hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11;
+		hs_iso_source_desc.bInterval = isoc_interval;
+		hs_iso_source_desc.bEndpointAddress =
+				fs_iso_source_desc.bEndpointAddress;
+
+		hs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket;
+		hs_iso_sink_desc.wMaxPacketSize |= isoc_mult << 11;
+		hs_iso_sink_desc.bInterval = isoc_interval;
+		hs_iso_sink_desc.bEndpointAddress =
+				fs_iso_sink_desc.bEndpointAddress;
+
 		f->hs_descriptors = hs_source_sink_descs;
 	}
 
@@ -228,13 +418,39 @@ autoconf_fail:
 				fs_source_desc.bEndpointAddress;
 		ss_sink_desc.bEndpointAddress =
 				fs_sink_desc.bEndpointAddress;
+
+		/*
+		 * Fill in the SS isoc descriptors from the module parameters.
+		 * We assume that the user knows what they are doing and won't
+		 * give parameters that their UDC doesn't support.
+		 */
+		ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
+		ss_iso_source_desc.bInterval = isoc_interval;
+		ss_iso_source_comp_desc.bmAttributes = isoc_mult;
+		ss_iso_source_comp_desc.bMaxBurst = isoc_maxburst;
+		ss_iso_source_comp_desc.wBytesPerInterval =
+			isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
+		ss_iso_source_desc.bEndpointAddress =
+				fs_iso_source_desc.bEndpointAddress;
+
+		ss_iso_sink_desc.wMaxPacketSize = isoc_maxpacket;
+		ss_iso_sink_desc.bInterval = isoc_interval;
+		ss_iso_sink_comp_desc.bmAttributes = isoc_mult;
+		ss_iso_sink_comp_desc.bMaxBurst = isoc_maxburst;
+		ss_iso_sink_comp_desc.wBytesPerInterval =
+			isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
+		ss_iso_sink_desc.bEndpointAddress =
+				fs_iso_sink_desc.bEndpointAddress;
+
 		f->ss_descriptors = ss_source_sink_descs;
 	}
 
-	DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+	DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
 	    (gadget_is_superspeed(c->cdev->gadget) ? "super" :
 	     (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
-			f->name, ss->in_ep->name, ss->out_ep->name);
+			f->name, ss->in_ep->name, ss->out_ep->name,
+			ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
+			ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
 	return 0;
 }
 
@@ -251,6 +467,9 @@ static int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
 	u8			*buf = req->buf;
 	struct usb_composite_dev *cdev = ss->function.config->cdev;
 
+	if (pattern == 2)
+		return 0;
+
 	for (i = 0; i < req->actual; i++, buf++) {
 		switch (pattern) {
 
@@ -265,7 +484,7 @@ static int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
 		 * each usb transfer request should be.  Resync is done
 		 * with set_interface or set_config.  (We *WANT* it to
 		 * get quickly out of sync if controllers or their drivers
-		 * stutter for any reason, including buffer duplcation...)
+		 * stutter for any reason, including buffer duplication...)
 		 */
 		case 1:
 			if (*buf == (u8)(i % 63))
@@ -292,21 +511,30 @@ static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
 		for  (i = 0; i < req->length; i++)
 			*buf++ = (u8) (i % 63);
 		break;
+	case 2:
+		break;
 	}
 }
 
 static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
 {
-	struct f_sourcesink	*ss = ep->driver_data;
-	struct usb_composite_dev *cdev = ss->function.config->cdev;
-	int			status = req->status;
+	struct usb_composite_dev	*cdev;
+	struct f_sourcesink		*ss = ep->driver_data;
+	int				status = req->status;
+
+	/* driver_data will be null if ep has been disabled */
+	if (!ss)
+		return;
+
+	cdev = ss->function.config->cdev;
 
 	switch (status) {
 
 	case 0:				/* normal completion? */
 		if (ep == ss->out_ep) {
 			check_read_data(ss, req);
-			memset(req->buf, 0x55, req->length);
+			if (pattern != 2)
+				memset(req->buf, 0x55, req->length);
 		} else
 			reinit_write_data(ep, req);
 		break;
@@ -344,32 +572,57 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
 	}
 }
 
-static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in)
+static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
+		bool is_iso, int speed)
 {
 	struct usb_ep		*ep;
 	struct usb_request	*req;
-	int			status;
+	int			i, size, status;
+
+	for (i = 0; i < 8; i++) {
+		if (is_iso) {
+			switch (speed) {
+			case USB_SPEED_SUPER:
+				size = isoc_maxpacket * (isoc_mult + 1) *
+						(isoc_maxburst + 1);
+				break;
+			case USB_SPEED_HIGH:
+				size = isoc_maxpacket * (isoc_mult + 1);
+				break;
+			default:
+				size = isoc_maxpacket > 1023 ?
+						1023 : isoc_maxpacket;
+				break;
+			}
+			ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
+			req = alloc_ep_req(ep, size);
+		} else {
+			ep = is_in ? ss->in_ep : ss->out_ep;
+			req = alloc_ep_req(ep, 0);
+		}
 
-	ep = is_in ? ss->in_ep : ss->out_ep;
-	req = alloc_ep_req(ep);
-	if (!req)
-		return -ENOMEM;
+		if (!req)
+			return -ENOMEM;
+
+		req->complete = source_sink_complete;
+		if (is_in)
+			reinit_write_data(ep, req);
+		else if (pattern != 2)
+			memset(req->buf, 0x55, req->length);
 
-	req->complete = source_sink_complete;
-	if (is_in)
-		reinit_write_data(ep, req);
-	else
-		memset(req->buf, 0x55, req->length);
+		status = usb_ep_queue(ep, req, GFP_ATOMIC);
+		if (status) {
+			struct usb_composite_dev	*cdev;
 
-	status = usb_ep_queue(ep, req, GFP_ATOMIC);
-	if (status) {
-		struct usb_composite_dev	*cdev;
+			cdev = ss->function.config->cdev;
+			ERROR(cdev, "start %s%s %s --> %d\n",
+			      is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
+			      ep->name, status);
+			free_ep_req(ep, req);
+		}
 
-		cdev = ss->function.config->cdev;
-		ERROR(cdev, "start %s %s --> %d\n",
-				is_in ? "IN" : "OUT",
-				ep->name, status);
-		free_ep_req(ep, req);
+		if (!is_iso)
+			break;
 	}
 
 	return status;
@@ -380,17 +633,20 @@ static void disable_source_sink(struct f_sourcesink *ss)
 	struct usb_composite_dev	*cdev;
 
 	cdev = ss->function.config->cdev;
-	disable_endpoints(cdev, ss->in_ep, ss->out_ep);
+	disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep,
+			ss->iso_out_ep);
 	VDBG(cdev, "%s disabled\n", ss->function.name);
 }
 
 static int
-enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss)
+enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss,
+		int alt)
 {
 	int					result = 0;
+	int					speed = cdev->gadget->speed;
 	struct usb_ep				*ep;
 
-	/* one endpoint writes (sources) zeroes IN (to the host) */
+	/* one bulk endpoint writes (sources) zeroes IN (to the host) */
 	ep = ss->in_ep;
 	result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
 	if (result)
@@ -400,7 +656,7 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss)
 		return result;
 	ep->driver_data = ss;
 
-	result = source_sink_start_ep(ss, true);
+	result = source_sink_start_ep(ss, true, false, speed);
 	if (result < 0) {
 fail:
 		ep = ss->in_ep;
@@ -409,7 +665,7 @@ fail:
 		return result;
 	}
 
-	/* one endpoint reads (sinks) anything OUT (from the host) */
+	/* one bulk endpoint reads (sinks) anything OUT (from the host) */
 	ep = ss->out_ep;
 	result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
 	if (result)
@@ -419,27 +675,73 @@ fail:
 		goto fail;
 	ep->driver_data = ss;
 
-	result = source_sink_start_ep(ss, false);
+	result = source_sink_start_ep(ss, false, false, speed);
 	if (result < 0) {
+fail2:
+		ep = ss->out_ep;
 		usb_ep_disable(ep);
 		ep->driver_data = NULL;
 		goto fail;
 	}
 
-	DBG(cdev, "%s enabled\n", ss->function.name);
+	if (alt == 0)
+		goto out;
+
+	/* one iso endpoint writes (sources) zeroes IN (to the host) */
+	ep = ss->iso_in_ep;
+	if (ep) {
+		result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
+		if (result)
+			goto fail2;
+		result = usb_ep_enable(ep);
+		if (result < 0)
+			goto fail2;
+		ep->driver_data = ss;
+
+		result = source_sink_start_ep(ss, true, true, speed);
+		if (result < 0) {
+fail3:
+			ep = ss->iso_in_ep;
+			if (ep) {
+				usb_ep_disable(ep);
+				ep->driver_data = NULL;
+			}
+			goto fail2;
+		}
+	}
+
+	/* one iso endpoint reads (sinks) anything OUT (from the host) */
+	ep = ss->iso_out_ep;
+	if (ep) {
+		result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
+		if (result)
+			goto fail3;
+		result = usb_ep_enable(ep);
+		if (result < 0)
+			goto fail3;
+		ep->driver_data = ss;
+
+		result = source_sink_start_ep(ss, false, true, speed);
+		if (result < 0) {
+			usb_ep_disable(ep);
+			ep->driver_data = NULL;
+			goto fail3;
+		}
+	}
+out:
+	DBG(cdev, "%s enabled, alt intf %d\n", ss->function.name, alt);
 	return result;
 }
 
 static int sourcesink_set_alt(struct usb_function *f,
 		unsigned intf, unsigned alt)
 {
-	struct f_sourcesink	*ss = func_to_ss(f);
-	struct usb_composite_dev *cdev = f->config->cdev;
+	struct f_sourcesink		*ss = func_to_ss(f);
+	struct usb_composite_dev	*cdev = f->config->cdev;
 
-	/* we know alt is zero */
 	if (ss->in_ep->driver_data)
 		disable_source_sink(ss);
-	return enable_source_sink(cdev, ss);
+	return enable_source_sink(cdev, ss, alt);
 }
 
 static void sourcesink_disable(struct usb_function *f)
@@ -536,7 +838,7 @@ unknown:
 		req->length = value;
 		value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);
 		if (value < 0)
-			ERROR(c->cdev, "source/sinkc response, err %d\n",
+			ERROR(c->cdev, "source/sink response, err %d\n",
 					value);
 	}
 
@@ -545,12 +847,12 @@ unknown:
 }
 
 static struct usb_configuration sourcesink_driver = {
-	.label		= "source/sink",
-	.strings	= sourcesink_strings,
-	.setup		= sourcesink_setup,
-	.bConfigurationValue = 3,
-	.bmAttributes	= USB_CONFIG_ATT_SELFPOWER,
-	/* .iConfiguration = DYNAMIC */
+	.label			= "source/sink",
+	.strings		= sourcesink_strings,
+	.setup			= sourcesink_setup,
+	.bConfigurationValue	= 3,
+	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
+	/* .iConfiguration	= DYNAMIC */
 };
 
 /**
@@ -567,7 +869,8 @@ int __init sourcesink_add(struct usb_composite_dev *cdev, bool autoresume)
 		return id;
 	strings_sourcesink[0].id = id;
 
-	source_sink_intf.iInterface = id;
+	source_sink_intf_alt0.iInterface = id;
+	source_sink_intf_alt1.iInterface = id;
 	sourcesink_driver.iConfiguration = id;
 
 	/* support autoresume for remote wakeup testing */
diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h
index e84b3c4..71ca193 100644
--- a/drivers/usb/gadget/g_zero.h
+++ b/drivers/usb/gadget/g_zero.h
@@ -13,10 +13,11 @@ extern unsigned buflen;
 extern const struct usb_descriptor_header *otg_desc[];
 
 /* common utilities */
-struct usb_request *alloc_ep_req(struct usb_ep *ep);
+struct usb_request *alloc_ep_req(struct usb_ep *ep, int len);
 void free_ep_req(struct usb_ep *ep, struct usb_request *req);
 void disable_endpoints(struct usb_composite_dev *cdev,
-		struct usb_ep *in, struct usb_ep *out);
+		struct usb_ep *in, struct usb_ep *out,
+		struct usb_ep *iso_in, struct usb_ep *iso_out);
 
 /* configuration-specific linkup */
 int sourcesink_add(struct usb_composite_dev *cdev, bool autoresume);
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 31d3483..12ad516 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -72,7 +72,7 @@
 
 static const char longname[] = "Gadget Zero";
 
-unsigned buflen = 4096;
+unsigned buflen = 4096;		/* only used for bulk endpoints */
 module_param(buflen, uint, 0);
 
 /*
@@ -170,14 +170,17 @@ static struct usb_gadget_strings *dev_strings[] = {
 
 /*-------------------------------------------------------------------------*/
 
-struct usb_request *alloc_ep_req(struct usb_ep *ep)
+struct usb_request *alloc_ep_req(struct usb_ep *ep, int len)
 {
 	struct usb_request	*req;
 
 	req = usb_ep_alloc_request(ep, GFP_ATOMIC);
 	if (req) {
-		req->length = buflen;
-		req->buf = kmalloc(buflen, GFP_ATOMIC);
+		if (len)
+			req->length = len;
+		else
+			req->length = buflen;
+		req->buf = kmalloc(req->length, GFP_ATOMIC);
 		if (!req->buf) {
 			usb_ep_free_request(ep, req);
 			req = NULL;
@@ -206,10 +209,15 @@ static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
 }
 
 void disable_endpoints(struct usb_composite_dev *cdev,
-		struct usb_ep *in, struct usb_ep *out)
+		struct usb_ep *in, struct usb_ep *out,
+		struct usb_ep *iso_in, struct usb_ep *iso_out)
 {
 	disable_ep(cdev, in);
 	disable_ep(cdev, out);
+	if (iso_in)
+		disable_ep(cdev, iso_in);
+	if (iso_out)
+		disable_ep(cdev, iso_out);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -311,7 +319,6 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
 		device_desc.bcdDevice = cpu_to_le16(0x9999);
 	}
 
-
 	INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);
 
 	snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux