Skip to content

Commit 91c42b0

Browse files
kopasiakFelipe Balbi
authored and
Felipe Balbi
committed
usb: gadget: loopback: Fix looping back logic implementation
Since commit e0857ce ("usb: gadget: loopback: don't queue requests to bogus endpoints") Loopback function is not realy working as that commit removed all looping back logic. After that commit ep-out works like /dev/null and ep-in works like /dev/zero. This commit fix this issue by allocating set of out requests and set of in requests but each out req shares buffer with one in req: out_req->buf ---> buf <--- in_req.buf out_req->context <---> in_req.context The completion routine simply enqueue the suitable req in an oposite direction. Cc: <[email protected]> # 3.18+ Fixes: e0857ce ("usb: gadget: loopback: don't queue requests to bogus endpoints") Signed-off-by: Krzysztof Opasiak <[email protected]> Signed-off-by: Felipe Balbi <[email protected]>
1 parent 3e9d992 commit 91c42b0

File tree

1 file changed

+92
-39
lines changed

1 file changed

+92
-39
lines changed

drivers/usb/gadget/function/f_loopback.c

Lines changed: 92 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -243,22 +243,38 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
243243
int status = req->status;
244244

245245
switch (status) {
246-
247246
case 0: /* normal completion? */
248247
if (ep == loop->out_ep) {
249-
req->zero = (req->actual < req->length);
250-
req->length = req->actual;
248+
/*
249+
* We received some data from the host so let's
250+
* queue it so host can read the from our in ep
251+
*/
252+
struct usb_request *in_req = req->context;
253+
254+
in_req->zero = (req->actual < req->length);
255+
in_req->length = req->actual;
256+
ep = loop->in_ep;
257+
req = in_req;
258+
} else {
259+
/*
260+
* We have just looped back a bunch of data
261+
* to host. Now let's wait for some more data.
262+
*/
263+
req = req->context;
264+
ep = loop->out_ep;
251265
}
252266

253-
/* queue the buffer for some later OUT packet */
254-
req->length = loop->buflen;
267+
/* queue the buffer back to host or for next bunch of data */
255268
status = usb_ep_queue(ep, req, GFP_ATOMIC);
256-
if (status == 0)
269+
if (status == 0) {
257270
return;
271+
} else {
272+
ERROR(cdev, "Unable to loop back buffer to %s: %d\n",
273+
ep->name, status);
274+
goto free_req;
275+
}
258276

259277
/* "should never get here" */
260-
/* FALLTHROUGH */
261-
262278
default:
263279
ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
264280
status, req->actual, req->length);
@@ -272,6 +288,10 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
272288
case -ECONNABORTED: /* hardware forced ep reset */
273289
case -ECONNRESET: /* request dequeued */
274290
case -ESHUTDOWN: /* disconnect from host */
291+
free_req:
292+
usb_ep_free_request(ep == loop->in_ep ?
293+
loop->out_ep : loop->in_ep,
294+
req->context);
275295
free_ep_req(ep, req);
276296
return;
277297
}
@@ -293,50 +313,72 @@ static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
293313
return alloc_ep_req(ep, len, loop->buflen);
294314
}
295315

296-
static int enable_endpoint(struct usb_composite_dev *cdev, struct f_loopback *loop,
297-
struct usb_ep *ep)
316+
static int alloc_requests(struct usb_composite_dev *cdev,
317+
struct f_loopback *loop)
298318
{
299-
struct usb_request *req;
300-
unsigned i;
301-
int result;
302-
303-
/*
304-
* one endpoint writes data back IN to the host while another endpoint
305-
* just reads OUT packets
306-
*/
307-
result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
308-
if (result)
309-
goto fail0;
310-
result = usb_ep_enable(ep);
311-
if (result < 0)
312-
goto fail0;
313-
ep->driver_data = loop;
319+
struct usb_request *in_req, *out_req;
320+
int i;
321+
int result = 0;
314322

315323
/*
316324
* allocate a bunch of read buffers and queue them all at once.
317-
* we buffer at most 'qlen' transfers; fewer if any need more
318-
* than 'buflen' bytes each.
325+
* we buffer at most 'qlen' transfers; We allocate buffers only
326+
* for out transfer and reuse them in IN transfers to implement
327+
* our loopback functionality
319328
*/
320329
for (i = 0; i < loop->qlen && result == 0; i++) {
321-
req = lb_alloc_ep_req(ep, 0);
322-
if (!req)
323-
goto fail1;
330+
result = -ENOMEM;
331+
332+
in_req = usb_ep_alloc_request(loop->in_ep, GFP_KERNEL);
333+
if (!in_req)
334+
goto fail;
335+
336+
out_req = lb_alloc_ep_req(loop->out_ep, 0);
337+
if (!out_req)
338+
goto fail_in;
339+
340+
in_req->complete = loopback_complete;
341+
out_req->complete = loopback_complete;
342+
343+
in_req->buf = out_req->buf;
344+
/* length will be set in complete routine */
345+
in_req->context = out_req;
346+
out_req->context = in_req;
324347

325-
req->complete = loopback_complete;
326-
result = usb_ep_queue(ep, req, GFP_ATOMIC);
348+
result = usb_ep_queue(loop->out_ep, out_req, GFP_ATOMIC);
327349
if (result) {
328350
ERROR(cdev, "%s queue req --> %d\n",
329-
ep->name, result);
330-
goto fail1;
351+
loop->out_ep->name, result);
352+
goto fail_out;
331353
}
332354
}
333355

334356
return 0;
335357

336-
fail1:
337-
usb_ep_disable(ep);
358+
fail_out:
359+
free_ep_req(loop->out_ep, out_req);
360+
fail_in:
361+
usb_ep_free_request(loop->in_ep, in_req);
362+
fail:
363+
return result;
364+
}
365+
366+
static int enable_endpoint(struct usb_composite_dev *cdev,
367+
struct f_loopback *loop, struct usb_ep *ep)
368+
{
369+
int result;
370+
371+
result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
372+
if (result)
373+
goto out;
338374

339-
fail0:
375+
result = usb_ep_enable(ep);
376+
if (result < 0)
377+
goto out;
378+
ep->driver_data = loop;
379+
result = 0;
380+
381+
out:
340382
return result;
341383
}
342384

@@ -347,13 +389,24 @@ enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
347389

348390
result = enable_endpoint(cdev, loop, loop->in_ep);
349391
if (result)
350-
return result;
392+
goto out;
351393

352394
result = enable_endpoint(cdev, loop, loop->out_ep);
353395
if (result)
354-
return result;
396+
goto disable_in;
397+
398+
result = alloc_requests(cdev, loop);
399+
if (result)
400+
goto disable_out;
355401

356402
DBG(cdev, "%s enabled\n", loop->function.name);
403+
return 0;
404+
405+
disable_out:
406+
usb_ep_disable(loop->out_ep);
407+
disable_in:
408+
usb_ep_disable(loop->in_ep);
409+
out:
357410
return result;
358411
}
359412

0 commit comments

Comments
 (0)