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