• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/drivers/usb/gadget/
1/*
2 * f_loopback.c - USB peripheral loopback configuration driver
3 *
4 * Copyright (C) 2003-2008 David Brownell
5 * Copyright (C) 2008 by Nokia Corporation
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 */
21
22/* #define VERBOSE_DEBUG */
23
24#include <linux/slab.h>
25#include <linux/kernel.h>
26#include <linux/device.h>
27
28#include "g_zero.h"
29#include "gadget_chips.h"
30
31
32/*
33 * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
34 *
35 * This takes messages of various sizes written OUT to a device, and loops
36 * them back so they can be read IN from it.  It has been used by certain
37 * test applications.  It supports limited testing of data queueing logic.
38 *
39 *
40 * This is currently packaged as a configuration driver, which can't be
41 * combined with other functions to make composite devices.  However, it
42 * can be combined with other independent configurations.
43 */
44struct f_loopback {
45	struct usb_function	function;
46
47	struct usb_ep		*in_ep;
48	struct usb_ep		*out_ep;
49};
50
51static inline struct f_loopback *func_to_loop(struct usb_function *f)
52{
53	return container_of(f, struct f_loopback, function);
54}
55
56static unsigned qlen = 32;
57module_param(qlen, uint, 0);
58MODULE_PARM_DESC(qlenn, "depth of loopback queue");
59
60/*-------------------------------------------------------------------------*/
61
62static struct usb_interface_descriptor loopback_intf = {
63	.bLength =		sizeof loopback_intf,
64	.bDescriptorType =	USB_DT_INTERFACE,
65
66	.bNumEndpoints =	2,
67	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
68	/* .iInterface = DYNAMIC */
69};
70
71/* full speed support: */
72
73static struct usb_endpoint_descriptor fs_loop_source_desc = {
74	.bLength =		USB_DT_ENDPOINT_SIZE,
75	.bDescriptorType =	USB_DT_ENDPOINT,
76
77	.bEndpointAddress =	USB_DIR_IN,
78	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
79};
80
81static struct usb_endpoint_descriptor fs_loop_sink_desc = {
82	.bLength =		USB_DT_ENDPOINT_SIZE,
83	.bDescriptorType =	USB_DT_ENDPOINT,
84
85	.bEndpointAddress =	USB_DIR_OUT,
86	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
87};
88
89static struct usb_descriptor_header *fs_loopback_descs[] = {
90	(struct usb_descriptor_header *) &loopback_intf,
91	(struct usb_descriptor_header *) &fs_loop_sink_desc,
92	(struct usb_descriptor_header *) &fs_loop_source_desc,
93	NULL,
94};
95
96/* high speed support: */
97
98static struct usb_endpoint_descriptor hs_loop_source_desc = {
99	.bLength =		USB_DT_ENDPOINT_SIZE,
100	.bDescriptorType =	USB_DT_ENDPOINT,
101
102	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
103	.wMaxPacketSize =	cpu_to_le16(512),
104};
105
106static struct usb_endpoint_descriptor hs_loop_sink_desc = {
107	.bLength =		USB_DT_ENDPOINT_SIZE,
108	.bDescriptorType =	USB_DT_ENDPOINT,
109
110	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
111	.wMaxPacketSize =	cpu_to_le16(512),
112};
113
114static struct usb_descriptor_header *hs_loopback_descs[] = {
115	(struct usb_descriptor_header *) &loopback_intf,
116	(struct usb_descriptor_header *) &hs_loop_source_desc,
117	(struct usb_descriptor_header *) &hs_loop_sink_desc,
118	NULL,
119};
120
121/* function-specific strings: */
122
123static struct usb_string strings_loopback[] = {
124	[0].s = "loop input to output",
125	{  }			/* end of list */
126};
127
128static struct usb_gadget_strings stringtab_loop = {
129	.language	= 0x0409,	/* en-us */
130	.strings	= strings_loopback,
131};
132
133static struct usb_gadget_strings *loopback_strings[] = {
134	&stringtab_loop,
135	NULL,
136};
137
138/*-------------------------------------------------------------------------*/
139
140static int __init
141loopback_bind(struct usb_configuration *c, struct usb_function *f)
142{
143	struct usb_composite_dev *cdev = c->cdev;
144	struct f_loopback	*loop = func_to_loop(f);
145	int			id;
146
147	/* allocate interface ID(s) */
148	id = usb_interface_id(c, f);
149	if (id < 0)
150		return id;
151	loopback_intf.bInterfaceNumber = id;
152
153	/* allocate endpoints */
154
155	loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
156	if (!loop->in_ep) {
157autoconf_fail:
158		ERROR(cdev, "%s: can't autoconfigure on %s\n",
159			f->name, cdev->gadget->name);
160		return -ENODEV;
161	}
162	loop->in_ep->driver_data = cdev;	/* claim */
163
164	loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
165	if (!loop->out_ep)
166		goto autoconf_fail;
167	loop->out_ep->driver_data = cdev;	/* claim */
168
169	/* support high speed hardware */
170	if (gadget_is_dualspeed(c->cdev->gadget)) {
171		hs_loop_source_desc.bEndpointAddress =
172				fs_loop_source_desc.bEndpointAddress;
173		hs_loop_sink_desc.bEndpointAddress =
174				fs_loop_sink_desc.bEndpointAddress;
175		f->hs_descriptors = hs_loopback_descs;
176	}
177
178	DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
179			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
180			f->name, loop->in_ep->name, loop->out_ep->name);
181	return 0;
182}
183
184static void
185loopback_unbind(struct usb_configuration *c, struct usb_function *f)
186{
187	kfree(func_to_loop(f));
188}
189
190static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
191{
192	struct f_loopback	*loop = ep->driver_data;
193	struct usb_composite_dev *cdev = loop->function.config->cdev;
194	int			status = req->status;
195
196	switch (status) {
197
198	case 0:				/* normal completion? */
199		if (ep == loop->out_ep) {
200			/* loop this OUT packet back IN to the host */
201			req->zero = (req->actual < req->length);
202			req->length = req->actual;
203			status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
204			if (status == 0)
205				return;
206
207			/* "should never get here" */
208			ERROR(cdev, "can't loop %s to %s: %d\n",
209				ep->name, loop->in_ep->name,
210				status);
211		}
212
213		/* queue the buffer for some later OUT packet */
214		req->length = buflen;
215		status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
216		if (status == 0)
217			return;
218
219		/* "should never get here" */
220		/* FALLTHROUGH */
221
222	default:
223		ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
224				status, req->actual, req->length);
225		/* FALLTHROUGH */
226
227	/* NOTE:  since this driver doesn't maintain an explicit record
228	 * of requests it submitted (just maintains qlen count), we
229	 * rely on the hardware driver to clean up on disconnect or
230	 * endpoint disable.
231	 */
232	case -ECONNABORTED:		/* hardware forced ep reset */
233	case -ECONNRESET:		/* request dequeued */
234	case -ESHUTDOWN:		/* disconnect from host */
235		free_ep_req(ep, req);
236		return;
237	}
238}
239
240static void disable_loopback(struct f_loopback *loop)
241{
242	struct usb_composite_dev	*cdev;
243
244	cdev = loop->function.config->cdev;
245	disable_endpoints(cdev, loop->in_ep, loop->out_ep);
246	VDBG(cdev, "%s disabled\n", loop->function.name);
247}
248
249static int
250enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
251{
252	int					result = 0;
253	const struct usb_endpoint_descriptor	*src, *sink;
254	struct usb_ep				*ep;
255	struct usb_request			*req;
256	unsigned				i;
257
258	src = ep_choose(cdev->gadget,
259			&hs_loop_source_desc, &fs_loop_source_desc);
260	sink = ep_choose(cdev->gadget,
261			&hs_loop_sink_desc, &fs_loop_sink_desc);
262
263	/* one endpoint writes data back IN to the host */
264	ep = loop->in_ep;
265	result = usb_ep_enable(ep, src);
266	if (result < 0)
267		return result;
268	ep->driver_data = loop;
269
270	/* one endpoint just reads OUT packets */
271	ep = loop->out_ep;
272	result = usb_ep_enable(ep, sink);
273	if (result < 0) {
274fail0:
275		ep = loop->in_ep;
276		usb_ep_disable(ep);
277		ep->driver_data = NULL;
278		return result;
279	}
280	ep->driver_data = loop;
281
282	/* allocate a bunch of read buffers and queue them all at once.
283	 * we buffer at most 'qlen' transfers; fewer if any need more
284	 * than 'buflen' bytes each.
285	 */
286	for (i = 0; i < qlen && result == 0; i++) {
287		req = alloc_ep_req(ep);
288		if (req) {
289			req->complete = loopback_complete;
290			result = usb_ep_queue(ep, req, GFP_ATOMIC);
291			if (result)
292				ERROR(cdev, "%s queue req --> %d\n",
293						ep->name, result);
294		} else {
295			usb_ep_disable(ep);
296			ep->driver_data = NULL;
297			result = -ENOMEM;
298			goto fail0;
299		}
300	}
301
302	DBG(cdev, "%s enabled\n", loop->function.name);
303	return result;
304}
305
306static int loopback_set_alt(struct usb_function *f,
307		unsigned intf, unsigned alt)
308{
309	struct f_loopback	*loop = func_to_loop(f);
310	struct usb_composite_dev *cdev = f->config->cdev;
311
312	/* we know alt is zero */
313	if (loop->in_ep->driver_data)
314		disable_loopback(loop);
315	return enable_loopback(cdev, loop);
316}
317
318static void loopback_disable(struct usb_function *f)
319{
320	struct f_loopback	*loop = func_to_loop(f);
321
322	disable_loopback(loop);
323}
324
325/*-------------------------------------------------------------------------*/
326
327static int __ref loopback_bind_config(struct usb_configuration *c)
328{
329	struct f_loopback	*loop;
330	int			status;
331
332	loop = kzalloc(sizeof *loop, GFP_KERNEL);
333	if (!loop)
334		return -ENOMEM;
335
336	loop->function.name = "loopback";
337	loop->function.descriptors = fs_loopback_descs;
338	loop->function.bind = loopback_bind;
339	loop->function.unbind = loopback_unbind;
340	loop->function.set_alt = loopback_set_alt;
341	loop->function.disable = loopback_disable;
342
343	status = usb_add_function(c, &loop->function);
344	if (status)
345		kfree(loop);
346	return status;
347}
348
349static  struct usb_configuration loopback_driver = {
350	.label		= "loopback",
351	.strings	= loopback_strings,
352	.bind		= loopback_bind_config,
353	.bConfigurationValue = 2,
354	.bmAttributes	= USB_CONFIG_ATT_SELFPOWER,
355	/* .iConfiguration = DYNAMIC */
356};
357
358/**
359 * loopback_add - add a loopback testing configuration to a device
360 * @cdev: the device to support the loopback configuration
361 */
362int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume)
363{
364	int id;
365
366	/* allocate string ID(s) */
367	id = usb_string_id(cdev);
368	if (id < 0)
369		return id;
370	strings_loopback[0].id = id;
371
372	loopback_intf.iInterface = id;
373	loopback_driver.iConfiguration = id;
374
375	/* support autoresume for remote wakeup testing */
376	if (autoresume)
377		sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
378
379	/* support OTG systems */
380	if (gadget_is_otg(cdev->gadget)) {
381		loopback_driver.descriptors = otg_desc;
382		loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
383	}
384
385	return usb_add_config(cdev, &loopback_driver);
386}
387