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.tikhomirov@xxxxxxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html