1*00a2430fSAndrzej Pietrasiewicz /*
2*00a2430fSAndrzej Pietrasiewicz  * f_loopback.c - USB peripheral loopback configuration driver
3*00a2430fSAndrzej Pietrasiewicz  *
4*00a2430fSAndrzej Pietrasiewicz  * Copyright (C) 2003-2008 David Brownell
5*00a2430fSAndrzej Pietrasiewicz  * Copyright (C) 2008 by Nokia Corporation
6*00a2430fSAndrzej Pietrasiewicz  *
7*00a2430fSAndrzej Pietrasiewicz  * This program is free software; you can redistribute it and/or modify
8*00a2430fSAndrzej Pietrasiewicz  * it under the terms of the GNU General Public License as published by
9*00a2430fSAndrzej Pietrasiewicz  * the Free Software Foundation; either version 2 of the License, or
10*00a2430fSAndrzej Pietrasiewicz  * (at your option) any later version.
11*00a2430fSAndrzej Pietrasiewicz  */
12*00a2430fSAndrzej Pietrasiewicz 
13*00a2430fSAndrzej Pietrasiewicz /* #define VERBOSE_DEBUG */
14*00a2430fSAndrzej Pietrasiewicz 
15*00a2430fSAndrzej Pietrasiewicz #include <linux/slab.h>
16*00a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h>
17*00a2430fSAndrzej Pietrasiewicz #include <linux/device.h>
18*00a2430fSAndrzej Pietrasiewicz #include <linux/module.h>
19*00a2430fSAndrzej Pietrasiewicz #include <linux/err.h>
20*00a2430fSAndrzej Pietrasiewicz #include <linux/usb/composite.h>
21*00a2430fSAndrzej Pietrasiewicz 
22*00a2430fSAndrzej Pietrasiewicz #include "g_zero.h"
23*00a2430fSAndrzej Pietrasiewicz #include "u_f.h"
24*00a2430fSAndrzej Pietrasiewicz 
25*00a2430fSAndrzej Pietrasiewicz /*
26*00a2430fSAndrzej Pietrasiewicz  * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
27*00a2430fSAndrzej Pietrasiewicz  *
28*00a2430fSAndrzej Pietrasiewicz  * This takes messages of various sizes written OUT to a device, and loops
29*00a2430fSAndrzej Pietrasiewicz  * them back so they can be read IN from it.  It has been used by certain
30*00a2430fSAndrzej Pietrasiewicz  * test applications.  It supports limited testing of data queueing logic.
31*00a2430fSAndrzej Pietrasiewicz  *
32*00a2430fSAndrzej Pietrasiewicz  *
33*00a2430fSAndrzej Pietrasiewicz  * This is currently packaged as a configuration driver, which can't be
34*00a2430fSAndrzej Pietrasiewicz  * combined with other functions to make composite devices.  However, it
35*00a2430fSAndrzej Pietrasiewicz  * can be combined with other independent configurations.
36*00a2430fSAndrzej Pietrasiewicz  */
37*00a2430fSAndrzej Pietrasiewicz struct f_loopback {
38*00a2430fSAndrzej Pietrasiewicz 	struct usb_function	function;
39*00a2430fSAndrzej Pietrasiewicz 
40*00a2430fSAndrzej Pietrasiewicz 	struct usb_ep		*in_ep;
41*00a2430fSAndrzej Pietrasiewicz 	struct usb_ep		*out_ep;
42*00a2430fSAndrzej Pietrasiewicz };
43*00a2430fSAndrzej Pietrasiewicz 
44*00a2430fSAndrzej Pietrasiewicz static inline struct f_loopback *func_to_loop(struct usb_function *f)
45*00a2430fSAndrzej Pietrasiewicz {
46*00a2430fSAndrzej Pietrasiewicz 	return container_of(f, struct f_loopback, function);
47*00a2430fSAndrzej Pietrasiewicz }
48*00a2430fSAndrzej Pietrasiewicz 
49*00a2430fSAndrzej Pietrasiewicz static unsigned qlen;
50*00a2430fSAndrzej Pietrasiewicz static unsigned buflen;
51*00a2430fSAndrzej Pietrasiewicz 
52*00a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
53*00a2430fSAndrzej Pietrasiewicz 
54*00a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor loopback_intf = {
55*00a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof loopback_intf,
56*00a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
57*00a2430fSAndrzej Pietrasiewicz 
58*00a2430fSAndrzej Pietrasiewicz 	.bNumEndpoints =	2,
59*00a2430fSAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
60*00a2430fSAndrzej Pietrasiewicz 	/* .iInterface = DYNAMIC */
61*00a2430fSAndrzej Pietrasiewicz };
62*00a2430fSAndrzej Pietrasiewicz 
63*00a2430fSAndrzej Pietrasiewicz /* full speed support: */
64*00a2430fSAndrzej Pietrasiewicz 
65*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_loop_source_desc = {
66*00a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
67*00a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
68*00a2430fSAndrzej Pietrasiewicz 
69*00a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
70*00a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
71*00a2430fSAndrzej Pietrasiewicz };
72*00a2430fSAndrzej Pietrasiewicz 
73*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor fs_loop_sink_desc = {
74*00a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
75*00a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
76*00a2430fSAndrzej Pietrasiewicz 
77*00a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
78*00a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
79*00a2430fSAndrzej Pietrasiewicz };
80*00a2430fSAndrzej Pietrasiewicz 
81*00a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *fs_loopback_descs[] = {
82*00a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &loopback_intf,
83*00a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fs_loop_sink_desc,
84*00a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fs_loop_source_desc,
85*00a2430fSAndrzej Pietrasiewicz 	NULL,
86*00a2430fSAndrzej Pietrasiewicz };
87*00a2430fSAndrzej Pietrasiewicz 
88*00a2430fSAndrzej Pietrasiewicz /* high speed support: */
89*00a2430fSAndrzej Pietrasiewicz 
90*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_loop_source_desc = {
91*00a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
92*00a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
93*00a2430fSAndrzej Pietrasiewicz 
94*00a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
95*00a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
96*00a2430fSAndrzej Pietrasiewicz };
97*00a2430fSAndrzej Pietrasiewicz 
98*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hs_loop_sink_desc = {
99*00a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
100*00a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
101*00a2430fSAndrzej Pietrasiewicz 
102*00a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
103*00a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
104*00a2430fSAndrzej Pietrasiewicz };
105*00a2430fSAndrzej Pietrasiewicz 
106*00a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *hs_loopback_descs[] = {
107*00a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &loopback_intf,
108*00a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &hs_loop_source_desc,
109*00a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &hs_loop_sink_desc,
110*00a2430fSAndrzej Pietrasiewicz 	NULL,
111*00a2430fSAndrzej Pietrasiewicz };
112*00a2430fSAndrzej Pietrasiewicz 
113*00a2430fSAndrzej Pietrasiewicz /* super speed support: */
114*00a2430fSAndrzej Pietrasiewicz 
115*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor ss_loop_source_desc = {
116*00a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
117*00a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
118*00a2430fSAndrzej Pietrasiewicz 
119*00a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
120*00a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(1024),
121*00a2430fSAndrzej Pietrasiewicz };
122*00a2430fSAndrzej Pietrasiewicz 
123*00a2430fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = {
124*00a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_SS_EP_COMP_SIZE,
125*00a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
126*00a2430fSAndrzej Pietrasiewicz 	.bMaxBurst =		0,
127*00a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		0,
128*00a2430fSAndrzej Pietrasiewicz 	.wBytesPerInterval =	0,
129*00a2430fSAndrzej Pietrasiewicz };
130*00a2430fSAndrzej Pietrasiewicz 
131*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor ss_loop_sink_desc = {
132*00a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
133*00a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
134*00a2430fSAndrzej Pietrasiewicz 
135*00a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
136*00a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(1024),
137*00a2430fSAndrzej Pietrasiewicz };
138*00a2430fSAndrzej Pietrasiewicz 
139*00a2430fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = {
140*00a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_SS_EP_COMP_SIZE,
141*00a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
142*00a2430fSAndrzej Pietrasiewicz 	.bMaxBurst =		0,
143*00a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		0,
144*00a2430fSAndrzej Pietrasiewicz 	.wBytesPerInterval =	0,
145*00a2430fSAndrzej Pietrasiewicz };
146*00a2430fSAndrzej Pietrasiewicz 
147*00a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *ss_loopback_descs[] = {
148*00a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &loopback_intf,
149*00a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ss_loop_source_desc,
150*00a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ss_loop_source_comp_desc,
151*00a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ss_loop_sink_desc,
152*00a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &ss_loop_sink_comp_desc,
153*00a2430fSAndrzej Pietrasiewicz 	NULL,
154*00a2430fSAndrzej Pietrasiewicz };
155*00a2430fSAndrzej Pietrasiewicz 
156*00a2430fSAndrzej Pietrasiewicz /* function-specific strings: */
157*00a2430fSAndrzej Pietrasiewicz 
158*00a2430fSAndrzej Pietrasiewicz static struct usb_string strings_loopback[] = {
159*00a2430fSAndrzej Pietrasiewicz 	[0].s = "loop input to output",
160*00a2430fSAndrzej Pietrasiewicz 	{  }			/* end of list */
161*00a2430fSAndrzej Pietrasiewicz };
162*00a2430fSAndrzej Pietrasiewicz 
163*00a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings stringtab_loop = {
164*00a2430fSAndrzej Pietrasiewicz 	.language	= 0x0409,	/* en-us */
165*00a2430fSAndrzej Pietrasiewicz 	.strings	= strings_loopback,
166*00a2430fSAndrzej Pietrasiewicz };
167*00a2430fSAndrzej Pietrasiewicz 
168*00a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings *loopback_strings[] = {
169*00a2430fSAndrzej Pietrasiewicz 	&stringtab_loop,
170*00a2430fSAndrzej Pietrasiewicz 	NULL,
171*00a2430fSAndrzej Pietrasiewicz };
172*00a2430fSAndrzej Pietrasiewicz 
173*00a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
174*00a2430fSAndrzej Pietrasiewicz 
175*00a2430fSAndrzej Pietrasiewicz static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
176*00a2430fSAndrzej Pietrasiewicz {
177*00a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = c->cdev;
178*00a2430fSAndrzej Pietrasiewicz 	struct f_loopback	*loop = func_to_loop(f);
179*00a2430fSAndrzej Pietrasiewicz 	int			id;
180*00a2430fSAndrzej Pietrasiewicz 	int ret;
181*00a2430fSAndrzej Pietrasiewicz 
182*00a2430fSAndrzej Pietrasiewicz 	/* allocate interface ID(s) */
183*00a2430fSAndrzej Pietrasiewicz 	id = usb_interface_id(c, f);
184*00a2430fSAndrzej Pietrasiewicz 	if (id < 0)
185*00a2430fSAndrzej Pietrasiewicz 		return id;
186*00a2430fSAndrzej Pietrasiewicz 	loopback_intf.bInterfaceNumber = id;
187*00a2430fSAndrzej Pietrasiewicz 
188*00a2430fSAndrzej Pietrasiewicz 	id = usb_string_id(cdev);
189*00a2430fSAndrzej Pietrasiewicz 	if (id < 0)
190*00a2430fSAndrzej Pietrasiewicz 		return id;
191*00a2430fSAndrzej Pietrasiewicz 	strings_loopback[0].id = id;
192*00a2430fSAndrzej Pietrasiewicz 	loopback_intf.iInterface = id;
193*00a2430fSAndrzej Pietrasiewicz 
194*00a2430fSAndrzej Pietrasiewicz 	/* allocate endpoints */
195*00a2430fSAndrzej Pietrasiewicz 
196*00a2430fSAndrzej Pietrasiewicz 	loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
197*00a2430fSAndrzej Pietrasiewicz 	if (!loop->in_ep) {
198*00a2430fSAndrzej Pietrasiewicz autoconf_fail:
199*00a2430fSAndrzej Pietrasiewicz 		ERROR(cdev, "%s: can't autoconfigure on %s\n",
200*00a2430fSAndrzej Pietrasiewicz 			f->name, cdev->gadget->name);
201*00a2430fSAndrzej Pietrasiewicz 		return -ENODEV;
202*00a2430fSAndrzej Pietrasiewicz 	}
203*00a2430fSAndrzej Pietrasiewicz 	loop->in_ep->driver_data = cdev;	/* claim */
204*00a2430fSAndrzej Pietrasiewicz 
205*00a2430fSAndrzej Pietrasiewicz 	loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
206*00a2430fSAndrzej Pietrasiewicz 	if (!loop->out_ep)
207*00a2430fSAndrzej Pietrasiewicz 		goto autoconf_fail;
208*00a2430fSAndrzej Pietrasiewicz 	loop->out_ep->driver_data = cdev;	/* claim */
209*00a2430fSAndrzej Pietrasiewicz 
210*00a2430fSAndrzej Pietrasiewicz 	/* support high speed hardware */
211*00a2430fSAndrzej Pietrasiewicz 	hs_loop_source_desc.bEndpointAddress =
212*00a2430fSAndrzej Pietrasiewicz 		fs_loop_source_desc.bEndpointAddress;
213*00a2430fSAndrzej Pietrasiewicz 	hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
214*00a2430fSAndrzej Pietrasiewicz 
215*00a2430fSAndrzej Pietrasiewicz 	/* support super speed hardware */
216*00a2430fSAndrzej Pietrasiewicz 	ss_loop_source_desc.bEndpointAddress =
217*00a2430fSAndrzej Pietrasiewicz 		fs_loop_source_desc.bEndpointAddress;
218*00a2430fSAndrzej Pietrasiewicz 	ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
219*00a2430fSAndrzej Pietrasiewicz 
220*00a2430fSAndrzej Pietrasiewicz 	ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs,
221*00a2430fSAndrzej Pietrasiewicz 			ss_loopback_descs);
222*00a2430fSAndrzej Pietrasiewicz 	if (ret)
223*00a2430fSAndrzej Pietrasiewicz 		return ret;
224*00a2430fSAndrzej Pietrasiewicz 
225*00a2430fSAndrzej Pietrasiewicz 	DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
226*00a2430fSAndrzej Pietrasiewicz 	    (gadget_is_superspeed(c->cdev->gadget) ? "super" :
227*00a2430fSAndrzej Pietrasiewicz 	     (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
228*00a2430fSAndrzej Pietrasiewicz 			f->name, loop->in_ep->name, loop->out_ep->name);
229*00a2430fSAndrzej Pietrasiewicz 	return 0;
230*00a2430fSAndrzej Pietrasiewicz }
231*00a2430fSAndrzej Pietrasiewicz 
232*00a2430fSAndrzej Pietrasiewicz static void lb_free_func(struct usb_function *f)
233*00a2430fSAndrzej Pietrasiewicz {
234*00a2430fSAndrzej Pietrasiewicz 	struct f_lb_opts *opts;
235*00a2430fSAndrzej Pietrasiewicz 
236*00a2430fSAndrzej Pietrasiewicz 	opts = container_of(f->fi, struct f_lb_opts, func_inst);
237*00a2430fSAndrzej Pietrasiewicz 
238*00a2430fSAndrzej Pietrasiewicz 	mutex_lock(&opts->lock);
239*00a2430fSAndrzej Pietrasiewicz 	opts->refcnt--;
240*00a2430fSAndrzej Pietrasiewicz 	mutex_unlock(&opts->lock);
241*00a2430fSAndrzej Pietrasiewicz 
242*00a2430fSAndrzej Pietrasiewicz 	usb_free_all_descriptors(f);
243*00a2430fSAndrzej Pietrasiewicz 	kfree(func_to_loop(f));
244*00a2430fSAndrzej Pietrasiewicz }
245*00a2430fSAndrzej Pietrasiewicz 
246*00a2430fSAndrzej Pietrasiewicz static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
247*00a2430fSAndrzej Pietrasiewicz {
248*00a2430fSAndrzej Pietrasiewicz 	struct f_loopback	*loop = ep->driver_data;
249*00a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = loop->function.config->cdev;
250*00a2430fSAndrzej Pietrasiewicz 	int			status = req->status;
251*00a2430fSAndrzej Pietrasiewicz 
252*00a2430fSAndrzej Pietrasiewicz 	switch (status) {
253*00a2430fSAndrzej Pietrasiewicz 
254*00a2430fSAndrzej Pietrasiewicz 	case 0:				/* normal completion? */
255*00a2430fSAndrzej Pietrasiewicz 		if (ep == loop->out_ep) {
256*00a2430fSAndrzej Pietrasiewicz 			/* loop this OUT packet back IN to the host */
257*00a2430fSAndrzej Pietrasiewicz 			req->zero = (req->actual < req->length);
258*00a2430fSAndrzej Pietrasiewicz 			req->length = req->actual;
259*00a2430fSAndrzej Pietrasiewicz 			status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
260*00a2430fSAndrzej Pietrasiewicz 			if (status == 0)
261*00a2430fSAndrzej Pietrasiewicz 				return;
262*00a2430fSAndrzej Pietrasiewicz 
263*00a2430fSAndrzej Pietrasiewicz 			/* "should never get here" */
264*00a2430fSAndrzej Pietrasiewicz 			ERROR(cdev, "can't loop %s to %s: %d\n",
265*00a2430fSAndrzej Pietrasiewicz 				ep->name, loop->in_ep->name,
266*00a2430fSAndrzej Pietrasiewicz 				status);
267*00a2430fSAndrzej Pietrasiewicz 		}
268*00a2430fSAndrzej Pietrasiewicz 
269*00a2430fSAndrzej Pietrasiewicz 		/* queue the buffer for some later OUT packet */
270*00a2430fSAndrzej Pietrasiewicz 		req->length = buflen;
271*00a2430fSAndrzej Pietrasiewicz 		status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
272*00a2430fSAndrzej Pietrasiewicz 		if (status == 0)
273*00a2430fSAndrzej Pietrasiewicz 			return;
274*00a2430fSAndrzej Pietrasiewicz 
275*00a2430fSAndrzej Pietrasiewicz 		/* "should never get here" */
276*00a2430fSAndrzej Pietrasiewicz 		/* FALLTHROUGH */
277*00a2430fSAndrzej Pietrasiewicz 
278*00a2430fSAndrzej Pietrasiewicz 	default:
279*00a2430fSAndrzej Pietrasiewicz 		ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
280*00a2430fSAndrzej Pietrasiewicz 				status, req->actual, req->length);
281*00a2430fSAndrzej Pietrasiewicz 		/* FALLTHROUGH */
282*00a2430fSAndrzej Pietrasiewicz 
283*00a2430fSAndrzej Pietrasiewicz 	/* NOTE:  since this driver doesn't maintain an explicit record
284*00a2430fSAndrzej Pietrasiewicz 	 * of requests it submitted (just maintains qlen count), we
285*00a2430fSAndrzej Pietrasiewicz 	 * rely on the hardware driver to clean up on disconnect or
286*00a2430fSAndrzej Pietrasiewicz 	 * endpoint disable.
287*00a2430fSAndrzej Pietrasiewicz 	 */
288*00a2430fSAndrzej Pietrasiewicz 	case -ECONNABORTED:		/* hardware forced ep reset */
289*00a2430fSAndrzej Pietrasiewicz 	case -ECONNRESET:		/* request dequeued */
290*00a2430fSAndrzej Pietrasiewicz 	case -ESHUTDOWN:		/* disconnect from host */
291*00a2430fSAndrzej Pietrasiewicz 		free_ep_req(ep, req);
292*00a2430fSAndrzej Pietrasiewicz 		return;
293*00a2430fSAndrzej Pietrasiewicz 	}
294*00a2430fSAndrzej Pietrasiewicz }
295*00a2430fSAndrzej Pietrasiewicz 
296*00a2430fSAndrzej Pietrasiewicz static void disable_loopback(struct f_loopback *loop)
297*00a2430fSAndrzej Pietrasiewicz {
298*00a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev	*cdev;
299*00a2430fSAndrzej Pietrasiewicz 
300*00a2430fSAndrzej Pietrasiewicz 	cdev = loop->function.config->cdev;
301*00a2430fSAndrzej Pietrasiewicz 	disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL);
302*00a2430fSAndrzej Pietrasiewicz 	VDBG(cdev, "%s disabled\n", loop->function.name);
303*00a2430fSAndrzej Pietrasiewicz }
304*00a2430fSAndrzej Pietrasiewicz 
305*00a2430fSAndrzej Pietrasiewicz static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
306*00a2430fSAndrzej Pietrasiewicz {
307*00a2430fSAndrzej Pietrasiewicz 	return alloc_ep_req(ep, len, buflen);
308*00a2430fSAndrzej Pietrasiewicz }
309*00a2430fSAndrzej Pietrasiewicz 
310*00a2430fSAndrzej Pietrasiewicz static int
311*00a2430fSAndrzej Pietrasiewicz enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
312*00a2430fSAndrzej Pietrasiewicz {
313*00a2430fSAndrzej Pietrasiewicz 	int					result = 0;
314*00a2430fSAndrzej Pietrasiewicz 	struct usb_ep				*ep;
315*00a2430fSAndrzej Pietrasiewicz 	struct usb_request			*req;
316*00a2430fSAndrzej Pietrasiewicz 	unsigned				i;
317*00a2430fSAndrzej Pietrasiewicz 
318*00a2430fSAndrzej Pietrasiewicz 	/* one endpoint writes data back IN to the host */
319*00a2430fSAndrzej Pietrasiewicz 	ep = loop->in_ep;
320*00a2430fSAndrzej Pietrasiewicz 	result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
321*00a2430fSAndrzej Pietrasiewicz 	if (result)
322*00a2430fSAndrzej Pietrasiewicz 		return result;
323*00a2430fSAndrzej Pietrasiewicz 	result = usb_ep_enable(ep);
324*00a2430fSAndrzej Pietrasiewicz 	if (result < 0)
325*00a2430fSAndrzej Pietrasiewicz 		return result;
326*00a2430fSAndrzej Pietrasiewicz 	ep->driver_data = loop;
327*00a2430fSAndrzej Pietrasiewicz 
328*00a2430fSAndrzej Pietrasiewicz 	/* one endpoint just reads OUT packets */
329*00a2430fSAndrzej Pietrasiewicz 	ep = loop->out_ep;
330*00a2430fSAndrzej Pietrasiewicz 	result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
331*00a2430fSAndrzej Pietrasiewicz 	if (result)
332*00a2430fSAndrzej Pietrasiewicz 		goto fail0;
333*00a2430fSAndrzej Pietrasiewicz 
334*00a2430fSAndrzej Pietrasiewicz 	result = usb_ep_enable(ep);
335*00a2430fSAndrzej Pietrasiewicz 	if (result < 0) {
336*00a2430fSAndrzej Pietrasiewicz fail0:
337*00a2430fSAndrzej Pietrasiewicz 		ep = loop->in_ep;
338*00a2430fSAndrzej Pietrasiewicz 		usb_ep_disable(ep);
339*00a2430fSAndrzej Pietrasiewicz 		ep->driver_data = NULL;
340*00a2430fSAndrzej Pietrasiewicz 		return result;
341*00a2430fSAndrzej Pietrasiewicz 	}
342*00a2430fSAndrzej Pietrasiewicz 	ep->driver_data = loop;
343*00a2430fSAndrzej Pietrasiewicz 
344*00a2430fSAndrzej Pietrasiewicz 	/* allocate a bunch of read buffers and queue them all at once.
345*00a2430fSAndrzej Pietrasiewicz 	 * we buffer at most 'qlen' transfers; fewer if any need more
346*00a2430fSAndrzej Pietrasiewicz 	 * than 'buflen' bytes each.
347*00a2430fSAndrzej Pietrasiewicz 	 */
348*00a2430fSAndrzej Pietrasiewicz 	for (i = 0; i < qlen && result == 0; i++) {
349*00a2430fSAndrzej Pietrasiewicz 		req = lb_alloc_ep_req(ep, 0);
350*00a2430fSAndrzej Pietrasiewicz 		if (req) {
351*00a2430fSAndrzej Pietrasiewicz 			req->complete = loopback_complete;
352*00a2430fSAndrzej Pietrasiewicz 			result = usb_ep_queue(ep, req, GFP_ATOMIC);
353*00a2430fSAndrzej Pietrasiewicz 			if (result)
354*00a2430fSAndrzej Pietrasiewicz 				ERROR(cdev, "%s queue req --> %d\n",
355*00a2430fSAndrzej Pietrasiewicz 						ep->name, result);
356*00a2430fSAndrzej Pietrasiewicz 		} else {
357*00a2430fSAndrzej Pietrasiewicz 			usb_ep_disable(ep);
358*00a2430fSAndrzej Pietrasiewicz 			ep->driver_data = NULL;
359*00a2430fSAndrzej Pietrasiewicz 			result = -ENOMEM;
360*00a2430fSAndrzej Pietrasiewicz 			goto fail0;
361*00a2430fSAndrzej Pietrasiewicz 		}
362*00a2430fSAndrzej Pietrasiewicz 	}
363*00a2430fSAndrzej Pietrasiewicz 
364*00a2430fSAndrzej Pietrasiewicz 	DBG(cdev, "%s enabled\n", loop->function.name);
365*00a2430fSAndrzej Pietrasiewicz 	return result;
366*00a2430fSAndrzej Pietrasiewicz }
367*00a2430fSAndrzej Pietrasiewicz 
368*00a2430fSAndrzej Pietrasiewicz static int loopback_set_alt(struct usb_function *f,
369*00a2430fSAndrzej Pietrasiewicz 		unsigned intf, unsigned alt)
370*00a2430fSAndrzej Pietrasiewicz {
371*00a2430fSAndrzej Pietrasiewicz 	struct f_loopback	*loop = func_to_loop(f);
372*00a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
373*00a2430fSAndrzej Pietrasiewicz 
374*00a2430fSAndrzej Pietrasiewicz 	/* we know alt is zero */
375*00a2430fSAndrzej Pietrasiewicz 	if (loop->in_ep->driver_data)
376*00a2430fSAndrzej Pietrasiewicz 		disable_loopback(loop);
377*00a2430fSAndrzej Pietrasiewicz 	return enable_loopback(cdev, loop);
378*00a2430fSAndrzej Pietrasiewicz }
379*00a2430fSAndrzej Pietrasiewicz 
380*00a2430fSAndrzej Pietrasiewicz static void loopback_disable(struct usb_function *f)
381*00a2430fSAndrzej Pietrasiewicz {
382*00a2430fSAndrzej Pietrasiewicz 	struct f_loopback	*loop = func_to_loop(f);
383*00a2430fSAndrzej Pietrasiewicz 
384*00a2430fSAndrzej Pietrasiewicz 	disable_loopback(loop);
385*00a2430fSAndrzej Pietrasiewicz }
386*00a2430fSAndrzej Pietrasiewicz 
387*00a2430fSAndrzej Pietrasiewicz static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
388*00a2430fSAndrzej Pietrasiewicz {
389*00a2430fSAndrzej Pietrasiewicz 	struct f_loopback	*loop;
390*00a2430fSAndrzej Pietrasiewicz 	struct f_lb_opts	*lb_opts;
391*00a2430fSAndrzej Pietrasiewicz 
392*00a2430fSAndrzej Pietrasiewicz 	loop = kzalloc(sizeof *loop, GFP_KERNEL);
393*00a2430fSAndrzej Pietrasiewicz 	if (!loop)
394*00a2430fSAndrzej Pietrasiewicz 		return ERR_PTR(-ENOMEM);
395*00a2430fSAndrzej Pietrasiewicz 
396*00a2430fSAndrzej Pietrasiewicz 	lb_opts = container_of(fi, struct f_lb_opts, func_inst);
397*00a2430fSAndrzej Pietrasiewicz 
398*00a2430fSAndrzej Pietrasiewicz 	mutex_lock(&lb_opts->lock);
399*00a2430fSAndrzej Pietrasiewicz 	lb_opts->refcnt++;
400*00a2430fSAndrzej Pietrasiewicz 	mutex_unlock(&lb_opts->lock);
401*00a2430fSAndrzej Pietrasiewicz 
402*00a2430fSAndrzej Pietrasiewicz 	buflen = lb_opts->bulk_buflen;
403*00a2430fSAndrzej Pietrasiewicz 	qlen = lb_opts->qlen;
404*00a2430fSAndrzej Pietrasiewicz 	if (!qlen)
405*00a2430fSAndrzej Pietrasiewicz 		qlen = 32;
406*00a2430fSAndrzej Pietrasiewicz 
407*00a2430fSAndrzej Pietrasiewicz 	loop->function.name = "loopback";
408*00a2430fSAndrzej Pietrasiewicz 	loop->function.bind = loopback_bind;
409*00a2430fSAndrzej Pietrasiewicz 	loop->function.set_alt = loopback_set_alt;
410*00a2430fSAndrzej Pietrasiewicz 	loop->function.disable = loopback_disable;
411*00a2430fSAndrzej Pietrasiewicz 	loop->function.strings = loopback_strings;
412*00a2430fSAndrzej Pietrasiewicz 
413*00a2430fSAndrzej Pietrasiewicz 	loop->function.free_func = lb_free_func;
414*00a2430fSAndrzej Pietrasiewicz 
415*00a2430fSAndrzej Pietrasiewicz 	return &loop->function;
416*00a2430fSAndrzej Pietrasiewicz }
417*00a2430fSAndrzej Pietrasiewicz 
418*00a2430fSAndrzej Pietrasiewicz static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item)
419*00a2430fSAndrzej Pietrasiewicz {
420*00a2430fSAndrzej Pietrasiewicz 	return container_of(to_config_group(item), struct f_lb_opts,
421*00a2430fSAndrzej Pietrasiewicz 			    func_inst.group);
422*00a2430fSAndrzej Pietrasiewicz }
423*00a2430fSAndrzej Pietrasiewicz 
424*00a2430fSAndrzej Pietrasiewicz CONFIGFS_ATTR_STRUCT(f_lb_opts);
425*00a2430fSAndrzej Pietrasiewicz CONFIGFS_ATTR_OPS(f_lb_opts);
426*00a2430fSAndrzej Pietrasiewicz 
427*00a2430fSAndrzej Pietrasiewicz static void lb_attr_release(struct config_item *item)
428*00a2430fSAndrzej Pietrasiewicz {
429*00a2430fSAndrzej Pietrasiewicz 	struct f_lb_opts *lb_opts = to_f_lb_opts(item);
430*00a2430fSAndrzej Pietrasiewicz 
431*00a2430fSAndrzej Pietrasiewicz 	usb_put_function_instance(&lb_opts->func_inst);
432*00a2430fSAndrzej Pietrasiewicz }
433*00a2430fSAndrzej Pietrasiewicz 
434*00a2430fSAndrzej Pietrasiewicz static struct configfs_item_operations lb_item_ops = {
435*00a2430fSAndrzej Pietrasiewicz 	.release		= lb_attr_release,
436*00a2430fSAndrzej Pietrasiewicz 	.show_attribute		= f_lb_opts_attr_show,
437*00a2430fSAndrzej Pietrasiewicz 	.store_attribute	= f_lb_opts_attr_store,
438*00a2430fSAndrzej Pietrasiewicz };
439*00a2430fSAndrzej Pietrasiewicz 
440*00a2430fSAndrzej Pietrasiewicz static ssize_t f_lb_opts_qlen_show(struct f_lb_opts *opts, char *page)
441*00a2430fSAndrzej Pietrasiewicz {
442*00a2430fSAndrzej Pietrasiewicz 	int result;
443*00a2430fSAndrzej Pietrasiewicz 
444*00a2430fSAndrzej Pietrasiewicz 	mutex_lock(&opts->lock);
445*00a2430fSAndrzej Pietrasiewicz 	result = sprintf(page, "%d", opts->qlen);
446*00a2430fSAndrzej Pietrasiewicz 	mutex_unlock(&opts->lock);
447*00a2430fSAndrzej Pietrasiewicz 
448*00a2430fSAndrzej Pietrasiewicz 	return result;
449*00a2430fSAndrzej Pietrasiewicz }
450*00a2430fSAndrzej Pietrasiewicz 
451*00a2430fSAndrzej Pietrasiewicz static ssize_t f_lb_opts_qlen_store(struct f_lb_opts *opts,
452*00a2430fSAndrzej Pietrasiewicz 				    const char *page, size_t len)
453*00a2430fSAndrzej Pietrasiewicz {
454*00a2430fSAndrzej Pietrasiewicz 	int ret;
455*00a2430fSAndrzej Pietrasiewicz 	u32 num;
456*00a2430fSAndrzej Pietrasiewicz 
457*00a2430fSAndrzej Pietrasiewicz 	mutex_lock(&opts->lock);
458*00a2430fSAndrzej Pietrasiewicz 	if (opts->refcnt) {
459*00a2430fSAndrzej Pietrasiewicz 		ret = -EBUSY;
460*00a2430fSAndrzej Pietrasiewicz 		goto end;
461*00a2430fSAndrzej Pietrasiewicz 	}
462*00a2430fSAndrzej Pietrasiewicz 
463*00a2430fSAndrzej Pietrasiewicz 	ret = kstrtou32(page, 0, &num);
464*00a2430fSAndrzej Pietrasiewicz 	if (ret)
465*00a2430fSAndrzej Pietrasiewicz 		goto end;
466*00a2430fSAndrzej Pietrasiewicz 
467*00a2430fSAndrzej Pietrasiewicz 	opts->qlen = num;
468*00a2430fSAndrzej Pietrasiewicz 	ret = len;
469*00a2430fSAndrzej Pietrasiewicz end:
470*00a2430fSAndrzej Pietrasiewicz 	mutex_unlock(&opts->lock);
471*00a2430fSAndrzej Pietrasiewicz 	return ret;
472*00a2430fSAndrzej Pietrasiewicz }
473*00a2430fSAndrzej Pietrasiewicz 
474*00a2430fSAndrzej Pietrasiewicz static struct f_lb_opts_attribute f_lb_opts_qlen =
475*00a2430fSAndrzej Pietrasiewicz 	__CONFIGFS_ATTR(qlen, S_IRUGO | S_IWUSR,
476*00a2430fSAndrzej Pietrasiewicz 			f_lb_opts_qlen_show,
477*00a2430fSAndrzej Pietrasiewicz 			f_lb_opts_qlen_store);
478*00a2430fSAndrzej Pietrasiewicz 
479*00a2430fSAndrzej Pietrasiewicz static ssize_t f_lb_opts_bulk_buflen_show(struct f_lb_opts *opts, char *page)
480*00a2430fSAndrzej Pietrasiewicz {
481*00a2430fSAndrzej Pietrasiewicz 	int result;
482*00a2430fSAndrzej Pietrasiewicz 
483*00a2430fSAndrzej Pietrasiewicz 	mutex_lock(&opts->lock);
484*00a2430fSAndrzej Pietrasiewicz 	result = sprintf(page, "%d", opts->bulk_buflen);
485*00a2430fSAndrzej Pietrasiewicz 	mutex_unlock(&opts->lock);
486*00a2430fSAndrzej Pietrasiewicz 
487*00a2430fSAndrzej Pietrasiewicz 	return result;
488*00a2430fSAndrzej Pietrasiewicz }
489*00a2430fSAndrzej Pietrasiewicz 
490*00a2430fSAndrzej Pietrasiewicz static ssize_t f_lb_opts_bulk_buflen_store(struct f_lb_opts *opts,
491*00a2430fSAndrzej Pietrasiewicz 				    const char *page, size_t len)
492*00a2430fSAndrzej Pietrasiewicz {
493*00a2430fSAndrzej Pietrasiewicz 	int ret;
494*00a2430fSAndrzej Pietrasiewicz 	u32 num;
495*00a2430fSAndrzej Pietrasiewicz 
496*00a2430fSAndrzej Pietrasiewicz 	mutex_lock(&opts->lock);
497*00a2430fSAndrzej Pietrasiewicz 	if (opts->refcnt) {
498*00a2430fSAndrzej Pietrasiewicz 		ret = -EBUSY;
499*00a2430fSAndrzej Pietrasiewicz 		goto end;
500*00a2430fSAndrzej Pietrasiewicz 	}
501*00a2430fSAndrzej Pietrasiewicz 
502*00a2430fSAndrzej Pietrasiewicz 	ret = kstrtou32(page, 0, &num);
503*00a2430fSAndrzej Pietrasiewicz 	if (ret)
504*00a2430fSAndrzej Pietrasiewicz 		goto end;
505*00a2430fSAndrzej Pietrasiewicz 
506*00a2430fSAndrzej Pietrasiewicz 	opts->bulk_buflen = num;
507*00a2430fSAndrzej Pietrasiewicz 	ret = len;
508*00a2430fSAndrzej Pietrasiewicz end:
509*00a2430fSAndrzej Pietrasiewicz 	mutex_unlock(&opts->lock);
510*00a2430fSAndrzej Pietrasiewicz 	return ret;
511*00a2430fSAndrzej Pietrasiewicz }
512*00a2430fSAndrzej Pietrasiewicz 
513*00a2430fSAndrzej Pietrasiewicz static struct f_lb_opts_attribute f_lb_opts_bulk_buflen =
514*00a2430fSAndrzej Pietrasiewicz 	__CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR,
515*00a2430fSAndrzej Pietrasiewicz 			f_lb_opts_bulk_buflen_show,
516*00a2430fSAndrzej Pietrasiewicz 			f_lb_opts_bulk_buflen_store);
517*00a2430fSAndrzej Pietrasiewicz 
518*00a2430fSAndrzej Pietrasiewicz static struct configfs_attribute *lb_attrs[] = {
519*00a2430fSAndrzej Pietrasiewicz 	&f_lb_opts_qlen.attr,
520*00a2430fSAndrzej Pietrasiewicz 	&f_lb_opts_bulk_buflen.attr,
521*00a2430fSAndrzej Pietrasiewicz 	NULL,
522*00a2430fSAndrzej Pietrasiewicz };
523*00a2430fSAndrzej Pietrasiewicz 
524*00a2430fSAndrzej Pietrasiewicz static struct config_item_type lb_func_type = {
525*00a2430fSAndrzej Pietrasiewicz 	.ct_item_ops    = &lb_item_ops,
526*00a2430fSAndrzej Pietrasiewicz 	.ct_attrs	= lb_attrs,
527*00a2430fSAndrzej Pietrasiewicz 	.ct_owner       = THIS_MODULE,
528*00a2430fSAndrzej Pietrasiewicz };
529*00a2430fSAndrzej Pietrasiewicz 
530*00a2430fSAndrzej Pietrasiewicz static void lb_free_instance(struct usb_function_instance *fi)
531*00a2430fSAndrzej Pietrasiewicz {
532*00a2430fSAndrzej Pietrasiewicz 	struct f_lb_opts *lb_opts;
533*00a2430fSAndrzej Pietrasiewicz 
534*00a2430fSAndrzej Pietrasiewicz 	lb_opts = container_of(fi, struct f_lb_opts, func_inst);
535*00a2430fSAndrzej Pietrasiewicz 	kfree(lb_opts);
536*00a2430fSAndrzej Pietrasiewicz }
537*00a2430fSAndrzej Pietrasiewicz 
538*00a2430fSAndrzej Pietrasiewicz static struct usb_function_instance *loopback_alloc_instance(void)
539*00a2430fSAndrzej Pietrasiewicz {
540*00a2430fSAndrzej Pietrasiewicz 	struct f_lb_opts *lb_opts;
541*00a2430fSAndrzej Pietrasiewicz 
542*00a2430fSAndrzej Pietrasiewicz 	lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL);
543*00a2430fSAndrzej Pietrasiewicz 	if (!lb_opts)
544*00a2430fSAndrzej Pietrasiewicz 		return ERR_PTR(-ENOMEM);
545*00a2430fSAndrzej Pietrasiewicz 	mutex_init(&lb_opts->lock);
546*00a2430fSAndrzej Pietrasiewicz 	lb_opts->func_inst.free_func_inst = lb_free_instance;
547*00a2430fSAndrzej Pietrasiewicz 	lb_opts->bulk_buflen = GZERO_BULK_BUFLEN;
548*00a2430fSAndrzej Pietrasiewicz 	lb_opts->qlen = GZERO_QLEN;
549*00a2430fSAndrzej Pietrasiewicz 
550*00a2430fSAndrzej Pietrasiewicz 	config_group_init_type_name(&lb_opts->func_inst.group, "",
551*00a2430fSAndrzej Pietrasiewicz 				    &lb_func_type);
552*00a2430fSAndrzej Pietrasiewicz 
553*00a2430fSAndrzej Pietrasiewicz 	return  &lb_opts->func_inst;
554*00a2430fSAndrzej Pietrasiewicz }
555*00a2430fSAndrzej Pietrasiewicz DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
556*00a2430fSAndrzej Pietrasiewicz 
557*00a2430fSAndrzej Pietrasiewicz int __init lb_modinit(void)
558*00a2430fSAndrzej Pietrasiewicz {
559*00a2430fSAndrzej Pietrasiewicz 	int ret;
560*00a2430fSAndrzej Pietrasiewicz 
561*00a2430fSAndrzej Pietrasiewicz 	ret = usb_function_register(&Loopbackusb_func);
562*00a2430fSAndrzej Pietrasiewicz 	if (ret)
563*00a2430fSAndrzej Pietrasiewicz 		return ret;
564*00a2430fSAndrzej Pietrasiewicz 	return ret;
565*00a2430fSAndrzej Pietrasiewicz }
566*00a2430fSAndrzej Pietrasiewicz void __exit lb_modexit(void)
567*00a2430fSAndrzej Pietrasiewicz {
568*00a2430fSAndrzej Pietrasiewicz 	usb_function_unregister(&Loopbackusb_func);
569*00a2430fSAndrzej Pietrasiewicz }
570*00a2430fSAndrzej Pietrasiewicz 
571*00a2430fSAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
572