summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@quicinc.com>2017-07-25 00:35:41 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2017-07-25 00:35:40 -0700
commit43a6d43b545084ba97d898ab6f2a4bf0918ac1f3 (patch)
tree58533990c308ec96d4f35b7e5146801618593d0c /drivers/usb/dwc3
parent48f499115ea4db7929dceb50947806c99da77bc2 (diff)
parent7f2ea4fd0c887de75eef2f9f1df2e779aaf14b49 (diff)
Merge "usb: dwc3: gadget: handle request->zero"
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r--drivers/usb/dwc3/core.h3
-rw-r--r--drivers/usb/dwc3/gadget.c49
2 files changed, 50 insertions, 2 deletions
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 1b4fb562ce4b..927c84ea4921 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -39,6 +39,7 @@
#define DWC3_MSG_MAX 500
/* Global constants */
+#define DWC3_ZLP_BUF_SIZE 1024 /* size of a superspeed bulk */
#define DWC3_EP0_BOUNCE_SIZE 512
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
@@ -748,6 +749,7 @@ struct dwc3_scratchpad_array {
* @ctrl_req: usb control request which is used for ep0
* @ep0_trb: trb which is used for the ctrl_req
* @ep0_bounce: bounce buffer for ep0
+ * @zlp_buf: used when request->zero is set
* @setup_buf: used while precessing STD USB requests
* @ctrl_req_addr: dma address of ctrl_req
* @ep0_trb: dma address of ep0_trb
@@ -853,6 +855,7 @@ struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
struct dwc3_trb *ep0_trb;
void *ep0_bounce;
+ void *zlp_buf;
void *scratchbuf;
u8 *setup_buf;
dma_addr_t ctrl_req_addr;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 658fcca485d8..646b47db88b5 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1340,6 +1340,32 @@ static bool dwc3_gadget_is_suspended(struct dwc3 *dwc)
return false;
}
+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)
{
@@ -1387,6 +1413,15 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
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);
+
out:
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -3554,6 +3589,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;
@@ -3595,12 +3636,12 @@ 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;
}
if (!dwc->is_drd) {
@@ -3612,6 +3653,9 @@ int dwc3_gadget_init(struct dwc3 *dwc)
return 0;
+err5:
+ kfree(dwc->zlp_buf);
+
err4:
dwc3_gadget_free_endpoints(dwc);
dma_free_coherent(dwc->dev, DWC3_EP0_BOUNCE_SIZE,
@@ -3649,6 +3693,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) * 2,
dwc->ep0_trb, dwc->ep0_trb_addr);