aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/function
diff options
context:
space:
mode:
authorFelipe Balbi <balbi@ti.com>2014-10-13 16:30:52 -0400
committerFelipe Balbi <balbi@ti.com>2014-10-23 10:55:42 -0400
commite0857ce58e8658657f5f12fe25272b93cfeb16aa (patch)
treebfc7fd6444aecd682879f427c7474e7527078a5f /drivers/usb/gadget/function
parentc0d31b3c3d9a025b8d5a57c35671e60c5f388bf7 (diff)
usb: gadget: loopback: don't queue requests to bogus endpoints
A request allocated from e.g. ep1out cannot be queued to any other endpoint. This bug has been in f_loopback at least since mid-2011 and since nobody has really screamed about it, we're not backporting to any stable kernels. Debugged with MUSB since that's the only controller which complains about this case. Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/gadget/function')
-rw-r--r--drivers/usb/gadget/function/f_loopback.c87
1 files changed, 42 insertions, 45 deletions
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index bf04389137e6..298b46112b1a 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -253,22 +253,13 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
253 253
254 case 0: /* normal completion? */ 254 case 0: /* normal completion? */
255 if (ep == loop->out_ep) { 255 if (ep == loop->out_ep) {
256 /* loop this OUT packet back IN to the host */
257 req->zero = (req->actual < req->length); 256 req->zero = (req->actual < req->length);
258 req->length = req->actual; 257 req->length = req->actual;
259 status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
260 if (status == 0)
261 return;
262
263 /* "should never get here" */
264 ERROR(cdev, "can't loop %s to %s: %d\n",
265 ep->name, loop->in_ep->name,
266 status);
267 } 258 }
268 259
269 /* queue the buffer for some later OUT packet */ 260 /* queue the buffer for some later OUT packet */
270 req->length = buflen; 261 req->length = buflen;
271 status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC); 262 status = usb_ep_queue(ep, req, GFP_ATOMIC);
272 if (status == 0) 263 if (status == 0)
273 return; 264 return;
274 265
@@ -308,60 +299,66 @@ static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
308 return alloc_ep_req(ep, len, buflen); 299 return alloc_ep_req(ep, len, buflen);
309} 300}
310 301
311static int 302static int enable_endpoint(struct usb_composite_dev *cdev, struct f_loopback *loop,
312enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) 303 struct usb_ep *ep)
313{ 304{
314 int result = 0;
315 struct usb_ep *ep;
316 struct usb_request *req; 305 struct usb_request *req;
317 unsigned i; 306 unsigned i;
307 int result;
318 308
319 /* one endpoint writes data back IN to the host */ 309 /*
320 ep = loop->in_ep; 310 * one endpoint writes data back IN to the host while another endpoint
311 * just reads OUT packets
312 */
321 result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); 313 result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
322 if (result) 314 if (result)
323 return result; 315 goto fail0;
324 result = usb_ep_enable(ep); 316 result = usb_ep_enable(ep);
325 if (result < 0) 317 if (result < 0)
326 return result;
327 ep->driver_data = loop;
328
329 /* one endpoint just reads OUT packets */
330 ep = loop->out_ep;
331 result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
332 if (result)
333 goto fail0; 318 goto fail0;
334
335 result = usb_ep_enable(ep);
336 if (result < 0) {
337fail0:
338 ep = loop->in_ep;
339 usb_ep_disable(ep);
340 ep->driver_data = NULL;
341 return result;
342 }
343 ep->driver_data = loop; 319 ep->driver_data = loop;
344 320
345 /* allocate a bunch of read buffers and queue them all at once. 321 /*
322 * allocate a bunch of read buffers and queue them all at once.
346 * we buffer at most 'qlen' transfers; fewer if any need more 323 * we buffer at most 'qlen' transfers; fewer if any need more
347 * than 'buflen' bytes each. 324 * than 'buflen' bytes each.
348 */ 325 */
349 for (i = 0; i < qlen && result == 0; i++) { 326 for (i = 0; i < qlen && result == 0; i++) {
350 req = lb_alloc_ep_req(ep, 0); 327 req = lb_alloc_ep_req(ep, 0);
351 if (req) { 328 if (!req)
352 req->complete = loopback_complete; 329 goto fail1;
353 result = usb_ep_queue(ep, req, GFP_ATOMIC); 330
354 if (result) 331 req->complete = loopback_complete;
355 ERROR(cdev, "%s queue req --> %d\n", 332 result = usb_ep_queue(ep, req, GFP_ATOMIC);
356 ep->name, result); 333 if (result) {
357 } else { 334 ERROR(cdev, "%s queue req --> %d\n",
358 usb_ep_disable(ep); 335 ep->name, result);
359 ep->driver_data = NULL; 336 goto fail1;
360 result = -ENOMEM;
361 goto fail0;
362 } 337 }
363 } 338 }
364 339
340 return 0;
341
342fail1:
343 usb_ep_disable(ep);
344
345fail0:
346 return result;
347}
348
349static int
350enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
351{
352 int result = 0;
353
354 result = enable_endpoint(cdev, loop, loop->in_ep);
355 if (result)
356 return result;
357
358 result = enable_endpoint(cdev, loop, loop->out_ep);
359 if (result)
360 return result;
361
365 DBG(cdev, "%s enabled\n", loop->function.name); 362 DBG(cdev, "%s enabled\n", loop->function.name);
366 return result; 363 return result;
367} 364}