summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc2
diff options
context:
space:
mode:
authorMian Yousaf Kaukab <yousaf.kaukab@intel.com>2015-01-09 15:38:59 +0300
committerFelipe Balbi <balbi@ti.com>2015-01-13 00:34:08 +0300
commitf71b5e2533debe564f21c068aaabc42c6293d18b (patch)
tree9424399d8b8c67f8394bc00c59f4828e167d569f /drivers/usb/dwc2
parentfe0b94abcdf6d80bd575e91baf1e0f85a4978d49 (diff)
downloadlinux-f71b5e2533debe564f21c068aaabc42c6293d18b.tar.xz
usb: dwc2: gadget: fix zero length packet transfers
According to programming guide, zero length packet should be programmed on its own and should not be counted in DIEPTSIZ.PktCnt with other packets. For ep0, this is the zlp for DATA IN stage (if required) and not for the STATUS stage. Tested-by: Robert Baldyga <r.baldyga@samsung.com> Acked-by: Paul Zimmerman <paulz@synopsys.com> Signed-off-by: Gregory Herrero <gregory.herrero@intel.com> Signed-off-by: Mian Yousaf Kaukab <yousaf.kaukab@intel.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/dwc2')
-rw-r--r--drivers/usb/dwc2/gadget.c51
1 files changed, 22 insertions, 29 deletions
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index c3977a8d70ac..992a2eb0dd81 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -598,14 +598,15 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
else
epsize = 0;
- if (index != 0 && ureq->zero) {
- /*
- * test for the packets being exactly right for the
- * transfer
- */
-
- if (length == (packets * hs_ep->ep.maxpacket))
- packets++;
+ /*
+ * zero length packet should be programmed on its own and should not
+ * be counted in DIEPTSIZ.PktCnt with other packets.
+ */
+ if (dir_in && ureq->zero && !continuing) {
+ /* Test if zlp is actually required. */
+ if ((ureq->length >= hs_ep->ep.maxpacket) &&
+ !(ureq->length % hs_ep->ep.maxpacket))
+ hs_ep->sent_zlp = 1;
}
epsize |= DXEPTSIZ_PKTCNT(packets);
@@ -857,7 +858,11 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
req->buf = hsotg->ep0_buff;
req->length = length;
- req->zero = 1; /* always do zero-length final transfer */
+ /*
+ * zero flag is for sending zlp in DATA IN stage. It has no impact on
+ * STATUS stage.
+ */
+ req->zero = 0;
req->complete = s3c_hsotg_complete_oursetup;
if (length)
@@ -1744,32 +1749,20 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
dev_dbg(hsotg->dev, "req->length:%d req->actual:%d req->zero:%d\n",
hs_req->req.length, hs_req->req.actual, hs_req->req.zero);
- /*
- * Check if dealing with Maximum Packet Size(MPS) IN transfer at EP0
- * When sent data is a multiple MPS size (e.g. 64B ,128B ,192B
- * ,256B ... ), after last MPS sized packet send IN ZLP packet to
- * inform the host that no more data is available.
- * The state of req.zero member is checked to be sure that the value to
- * send is smaller than wValue expected from host.
- * Check req.length to NOT send another ZLP when the current one is
- * under completion (the one for which this completion has been called).
- */
- if (hs_req->req.length && hs_ep->index == 0 && hs_req->req.zero &&
- hs_req->req.length == hs_req->req.actual &&
- !(hs_req->req.length % hs_ep->ep.maxpacket)) {
-
- dev_dbg(hsotg->dev, "ep0 zlp IN packet sent\n");
- s3c_hsotg_program_zlp(hsotg, hs_ep);
-
- return;
- }
-
if (!size_left && hs_req->req.actual < hs_req->req.length) {
dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
return;
}
+ /* Zlp for all endpoints, for ep0 only in DATA IN stage */
+ if (hs_ep->sent_zlp) {
+ s3c_hsotg_program_zlp(hsotg, hs_ep);
+ hs_ep->sent_zlp = 0;
+ /* transfer will be completed on next complete interrupt */
+ return;
+ }
+
if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_DATA_IN) {
/* Move to STATUS OUT */
s3c_hsotg_ep0_zlp(hsotg, false);