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:
parent
3ff4b5733b
commit
04c03d10e5
@ -37,6 +37,7 @@
|
|||||||
#define DWC3_MSG_MAX 500
|
#define DWC3_MSG_MAX 500
|
||||||
|
|
||||||
/* Global constants */
|
/* Global constants */
|
||||||
|
#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
|
||||||
#define DWC3_EP0_BOUNCE_SIZE 512
|
#define DWC3_EP0_BOUNCE_SIZE 512
|
||||||
#define DWC3_ENDPOINTS_NUM 32
|
#define DWC3_ENDPOINTS_NUM 32
|
||||||
#define DWC3_XHCI_RESOURCES_NUM 2
|
#define DWC3_XHCI_RESOURCES_NUM 2
|
||||||
@ -647,6 +648,7 @@ struct dwc3_scratchpad_array {
|
|||||||
* @ctrl_req: usb control request which is used for ep0
|
* @ctrl_req: usb control request which is used for ep0
|
||||||
* @ep0_trb: trb which is used for the ctrl_req
|
* @ep0_trb: trb which is used for the ctrl_req
|
||||||
* @ep0_bounce: bounce buffer for ep0
|
* @ep0_bounce: bounce buffer for ep0
|
||||||
|
* @zlp_buf: used when request->zero is set
|
||||||
* @setup_buf: used while precessing STD USB requests
|
* @setup_buf: used while precessing STD USB requests
|
||||||
* @ctrl_req_addr: dma address of ctrl_req
|
* @ctrl_req_addr: dma address of ctrl_req
|
||||||
* @ep0_trb: dma address of ep0_trb
|
* @ep0_trb: dma address of ep0_trb
|
||||||
@ -734,6 +736,7 @@ struct dwc3 {
|
|||||||
struct usb_ctrlrequest *ctrl_req;
|
struct usb_ctrlrequest *ctrl_req;
|
||||||
struct dwc3_trb *ep0_trb;
|
struct dwc3_trb *ep0_trb;
|
||||||
void *ep0_bounce;
|
void *ep0_bounce;
|
||||||
|
void *zlp_buf;
|
||||||
void *scratchbuf;
|
void *scratchbuf;
|
||||||
u8 *setup_buf;
|
u8 *setup_buf;
|
||||||
dma_addr_t ctrl_req_addr;
|
dma_addr_t ctrl_req_addr;
|
||||||
|
@ -1159,6 +1159,32 @@ out:
|
|||||||
return ret;
|
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,
|
static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
|
||||||
gfp_t gfp_flags)
|
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);
|
spin_lock_irqsave(&dwc->lock, flags);
|
||||||
ret = __dwc3_gadget_ep_queue(dep, req);
|
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);
|
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -2744,6 +2780,12 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||||||
goto err3;
|
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.ops = &dwc3_gadget_ops;
|
||||||
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||||
dwc->gadget.sg_supported = true;
|
dwc->gadget.sg_supported = true;
|
||||||
@ -2785,16 +2827,19 @@ int dwc3_gadget_init(struct dwc3 *dwc)
|
|||||||
|
|
||||||
ret = dwc3_gadget_init_endpoints(dwc);
|
ret = dwc3_gadget_init_endpoints(dwc);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err4;
|
goto err5;
|
||||||
|
|
||||||
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
|
ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dwc->dev, "failed to register udc\n");
|
dev_err(dwc->dev, "failed to register udc\n");
|
||||||
goto err4;
|
goto err5;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err5:
|
||||||
|
kfree(dwc->zlp_buf);
|
||||||
|
|
||||||
err4:
|
err4:
|
||||||
dwc3_gadget_free_endpoints(dwc);
|
dwc3_gadget_free_endpoints(dwc);
|
||||||
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
|
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);
|
dwc->ep0_bounce, dwc->ep0_bounce_addr);
|
||||||
|
|
||||||
kfree(dwc->setup_buf);
|
kfree(dwc->setup_buf);
|
||||||
|
kfree(dwc->zlp_buf);
|
||||||
|
|
||||||
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
|
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
|
||||||
dwc->ep0_trb, dwc->ep0_trb_addr);
|
dwc->ep0_trb, dwc->ep0_trb_addr);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user