[U-Boot] [PATCH] usb: ci_udc: terminate ep0 INs with a zlp when required

Stephen Warren swarren at wwwdotorg.org
Tue Jun 10 23:27:39 CEST 2014


From: Stephen Warren <swarren at nvidia.com>

Sometimes, a zero-length packet is required at the end of an IN
transaction so that the host knows the device is done sending data.
Enhance ci_udc to send a zlp when necessary. See the comments for
more details.

Signed-off-by: Stephen Warren <swarren at nvidia.com>
---
This depends on all the other ci_udc patches I've sent previously
at least for diff context if nothing else.
---
 drivers/usb/gadget/ci_udc.c | 38 +++++++++++++++++++++++++++++++++++---
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c
index 435a2720e96f..b18bee43ad89 100644
--- a/drivers/usb/gadget/ci_udc.c
+++ b/drivers/usb/gadget/ci_udc.c
@@ -369,18 +369,50 @@ static void ci_ep_submit_next_request(struct ci_ep *ci_ep)
 	ci_req = list_first_entry(&ci_ep->queue, struct ci_req, queue);
 	len = ci_req->req.length;
 
-	item->next = TERMINATE;
-	item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE;
+	item->info = INFO_BYTES(len) | INFO_ACTIVE;
 	item->page0 = (uint32_t)ci_req->hw_buf;
 	item->page1 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x1000;
 	item->page2 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x2000;
 	item->page3 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x3000;
 	item->page4 = ((uint32_t)ci_req->hw_buf & 0xfffff000) + 0x4000;
-	ci_flush_qtd(num);
 
 	head->next = (unsigned) item;
 	head->info = 0;
 
+	/*
+	 * When sending the data for an IN transaction, the attached host
+	 * knows that all data for the IN is sent when one of the following
+	 * occurs:
+	 * a) A zero-length packet is transmitted.
+	 * b) A packet with length that isn't an exact multiple of the ep's
+	 *    maxpacket is transmitted.
+	 * c) Enough data is sent to exactly fill the host's maximum expected
+	 *    IN transaction size.
+	 *
+	 * One of these conditions MUST apply at the end of an IN transaction,
+	 * or the transaction will not be considered complete by the host. If
+	 * none of (a)..(c) already applies, then we must force (a) to apply
+	 * by explicitly sending an extra zero-length packet.
+	 */
+	/*  IN    !a     !b                              !c */
+	if (in && len && !(len % ci_ep->ep.maxpacket) && ci_req->req.zero) {
+		/*
+		 * Each endpoint has 2 items allocated, even though typically
+		 * only 1 is used at a time since either an IN or an OUT but
+		 * not both is queued. For an IN transaction, item currently
+		 * points at the second of these items, so we know that we
+		 * can use (item - 1) to transmit the extra zero-length packet
+		 */
+		item->next = (unsigned)(item - 1);
+		item--;
+		item->info = INFO_ACTIVE;
+	}
+
+	item->next = TERMINATE;
+	item->info |= INFO_IOC;
+
+	ci_flush_qtd(num);
+
 	DBG("ept%d %s queue len %x, req %p, buffer %p\n",
 	    num, in ? "in" : "out", len, ci_req, ci_req->hw_buf);
 	ci_flush_qh(num);
-- 
1.8.1.5



More information about the U-Boot mailing list