usb: dwc3: gadget: handle request->zero

So far, dwc3 has always missed request->zero
handling for every endpoint. Let's implement
that so we can handle cases where transfer must
be finished with a ZLP.

Note that dwc3 is a little special. Even though
we're dealing with a ZLP, we still need a buffer
of wMaxPacketSize bytes; to hide that detail from
every gadget driver, we have a preallocated buffer
of 1024 bytes (biggest bulk size) to use (and
share) among all endpoints.

Reported-by: Ravi B <ravibabu@ti.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
Felipe Balbi
2015-12-02 10:06:45 -06:00
parent 3ff4b5733b
commit 04c03d10e5
2 changed files with 51 additions and 2 deletions

View File

@@ -1159,6 +1159,32 @@ out:
return ret;
}
static void __dwc3_gadget_ep_zlp_complete(struct usb_ep *ep,
struct usb_request *request)
{
dwc3_gadget_ep_free_request(ep, request);
}
static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
struct usb_request *request;
struct usb_ep *ep = &dep->endpoint;
dwc3_trace(trace_dwc3_gadget, "queueing ZLP\n");
request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
if (!request)
return -ENOMEM;
request->length = 0;
request->buf = dwc->zlp_buf;
request->complete = __dwc3_gadget_ep_zlp_complete;
req = to_dwc3_request(request);
return __dwc3_gadget_ep_queue(dep, req);
}
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags)
{
@@ -1172,6 +1198,16 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
spin_lock_irqsave(&dwc->lock, flags);
ret = __dwc3_gadget_ep_queue(dep, req);
/*
* Okay, here's the thing, if gadget driver has requested for a ZLP by
* setting request->zero, instead of doing magic, we will just queue an
* extra usb_request ourselves so that it gets handled the same way as
* any other request.
*/
if (ret == 0 && request->zero && (request->length % ep->maxpacket == 0))
ret = __dwc3_gadget_ep_queue_zlp(dwc, dep);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -2744,6 +2780,12 @@ int dwc3_gadget_init(struct dwc3 *dwc)
goto err3;
}
dwc->zlp_buf = kzalloc(DWC3_ZLP_BUF_SIZE, GFP_KERNEL);
if (!dwc->zlp_buf) {
ret = -ENOMEM;
goto err4;
}
dwc->gadget.ops = &dwc3_gadget_ops;
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->gadget.sg_supported = true;
@@ -2785,16 +2827,19 @@ int dwc3_gadget_init(struct dwc3 *dwc)
ret = dwc3_gadget_init_endpoints(dwc);
if (ret)
goto err4;
goto err5;
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
goto err4;
goto err5;
}
return 0;
err5:
kfree(dwc->zlp_buf);
err4:
dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
@@ -2827,6 +2872,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc->ep0_bounce, dwc->ep0_bounce_addr);
kfree(dwc->setup_buf);
kfree(dwc->zlp_buf);
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
dwc->ep0_trb, dwc->ep0_trb_addr);