In accordance with specification, when sent data length is
an exact multiple of wMaxPacketSize for the pipe and less
than requested by host, the function shall return a zero-length
packet (ZLP) to indicate the end of the Data stage to a USB host.

Signed-off-by: Anton Tikhomirov <av.tikhomi...@samsung.com>
---
 drivers/usb/dwc3/core.h   |    2 ++
 drivers/usb/dwc3/ep0.c    |   27 ++++++++++++++++++++-------
 drivers/usb/dwc3/gadget.c |    3 +++
 3 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index f8af8d4..fefbf2d 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -619,6 +619,7 @@ struct dwc3_scratchpad_array {
  * @three_stage_setup: set if we perform a three phase setup
  * @ep0_bounced: true when we used bounce buffer
  * @ep0_expect_in: true when we expect a DATA IN transfer
+ * @ep0_zlp_sent: true when ZLP was sent
  * @start_config_issued: true when StartConfig command has been issued
  * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
  * @needs_fifo_resize: not all users might want fifo resizing, flag it
@@ -700,6 +701,7 @@ struct dwc3 {
        unsigned                three_stage_setup:1;
        unsigned                ep0_bounced:1;
        unsigned                ep0_expect_in:1;
+       unsigned                ep0_zlp_sent:1;
        unsigned                start_config_issued:1;
        unsigned                setup_packet_pending:1;
        unsigned                delayed_status:1;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 21a3520..cf72561 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -782,6 +782,9 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
                return;
        }
 
+       if (dwc->ep0_zlp_sent)
+               goto finish_zlp;
+
        length = trb->size & DWC3_TRB_SIZE_MASK;
 
        if (dwc->ep0_bounced) {
@@ -802,14 +805,24 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
                /* for some reason we did not get everything out */
 
                dwc3_ep0_stall_and_restart(dwc);
-       } else {
-               /*
-                * handle the case where we have to send a zero packet. This
-                * seems to be case when req.length > maxpacket. Could it be?
-                */
-               if (r)
-                       dwc3_gadget_giveback(ep0, r, 0);
+               return;
        }
+
+       /* handle the case where we have to send a zero packet */
+       if ((epnum & 1) && ur->zero &&
+           (ur->length % ep0->endpoint.maxpacket == 0)) {
+               int ret;
+
+               ret = dwc3_ep0_start_trans(dwc, epnum, dwc->ctrl_req_addr, 0,
+                               DWC3_TRBCTL_CONTROL_DATA);
+               WARN_ON(ret < 0);
+               dwc->ep0_zlp_sent = 1;
+               return;
+       }
+
+finish_zlp:
+       if (r)
+               dwc3_gadget_giveback(ep0, r, 0);
 }
 
 static void dwc3_ep0_complete_status(struct dwc3 *dwc,
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 02e44fc..59ecd1e 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -251,6 +251,9 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct 
dwc3_request *req,
                usb_gadget_unmap_request(&dwc->gadget, &req->request,
                                req->direction);
 
+       if (dwc->ep0_zlp_sent && dep->number == 0)
+               dwc->ep0_zlp_sent = 0;
+
        dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
                        req, dep->name, req->request.actual,
                        req->request.length, status);
-- 
1.7.9.5


--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to