usb: ci_udc: terminate ep0 INs with a zlp when required

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@nvidia.com>
This commit is contained in:
Stephen Warren 2014-06-10 15:27:39 -06:00 committed by Marek Vasut
parent b7c0051687
commit e0672b3c3a

View File

@ -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);