From cb3db5f6a5e08d58c2afed6b2ea2fd244f3e45ea Mon Sep 17 00:00:00 2001 From: William Wu Date: Mon, 23 Mar 2020 16:05:24 +0800 Subject: [PATCH] usb: dwc3: gadget: fix isoc in transfer issue When I test the uvc gadget function, I find that the uvc gadget can only transfer the first frame image and then stop unexpectedly. Without this patch, the isochronous endpoints doesn't wait for a XferNotReady event to start the next frame image, and fail to transfer data. In this case, we will get an endpoint event DWC3_DEPEVT_XFERINPROGRESS and the event status is DEPEVT_STATUS_MISSED_ISOC, and the original code try to issue an EndTransfer command if the started_list is empty. However, the started_list is never empty, so it doesn't issue an EndTransfer command and not XferNotReady event happens. To fix this problem, this patch firstly checks if the event status is DEPEVT_STATUS_MISSED_ISOC and then unmap the isoc request and move the request to the pending list. Then it issue an EndTransfer command if the started_list is empty. After the EndTransfer is completed, we will get a XferNotReady event and it can start the isoc requests again. Change-Id: Ieac3ca9a177bc7599b29ae30bee243e47b3ba7c9 Signed-off-by: William Wu --- drivers/usb/dwc3/gadget.c | 42 +++++++++++++++++++++++++++++---------- drivers/usb/dwc3/gadget.h | 15 ++++++++++++++ 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 6d5083453d17..79733e234b87 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1336,6 +1336,15 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); if (ret < 0) { + /* + * Isochronous endpoints in request needs to + * return directly and retry to transfer next + * time. Otherwise, it will fail to giveback + * the req to the udc gadget driver. + */ + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + usb_endpoint_dir_in(dep->endpoint.desc)) + return ret; /* * FIXME we need to iterate over the list of requests * here and stop, unmap, free and del each of the linked @@ -2553,6 +2562,7 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, const struct dwc3_event_depevt *event, struct dwc3_request *req, int status) { + struct dwc3 *dwc = dep->dwc; int ret; if (req->num_pending_sgs) @@ -2570,9 +2580,25 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, req->request.actual = req->request.length - req->remaining; - if (!dwc3_gadget_ep_request_completed(req) || - req->num_pending_sgs) { - __dwc3_gadget_kick_transfer(dep); + if (!dwc3_gadget_ep_request_completed(req) || req->num_pending_sgs || + event->status & DEPEVT_STATUS_MISSED_ISOC) { + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + /* + * unmap isoc request and move the request + * to the pending list to wait for kicking + * transfer again. + */ + req->remaining = 0; + req->needs_extra_trb = false; + if (req->trb) + usb_gadget_unmap_request_by_dev(dwc->sysdev, + &req->request, + req->direction); + req->trb = NULL; + dwc3_gadget_move_queued_request(req); + } else { + __dwc3_gadget_kick_transfer(dep); + } goto out; } @@ -2609,23 +2635,19 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep, { struct dwc3 *dwc = dep->dwc; unsigned status = 0; - bool stop = false; dwc3_gadget_endpoint_frame_from_event(dep, event); if (event->status & DEPEVT_STATUS_BUSERR) status = -ECONNRESET; - if (event->status & DEPEVT_STATUS_MISSED_ISOC) { + if (event->status & DEPEVT_STATUS_MISSED_ISOC) status = -EXDEV; - if (list_empty(&dep->started_list)) - stop = true; - } - dwc3_gadget_ep_cleanup_completed_requests(dep, event, status); - if (stop) { + if (event->status & DEPEVT_STATUS_MISSED_ISOC && + list_empty(&dep->started_list)) { dwc3_stop_active_transfer(dep, true, true); dep->flags = DWC3_EP_ENABLED; } diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 3ed738e86ea7..9ca5ac361175 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -64,6 +64,21 @@ static inline struct dwc3_request *next_request(struct list_head *list) return list_first_entry_or_null(list, struct dwc3_request, list); } +/** + * dwc3_gadget_move_queued_request - move @req to the pending_list + * @req: the request to be moved + * + * Caller should take care of locking. This function will move @req from its + * current list to the endpoint's pending_list. + */ +static inline void dwc3_gadget_move_queued_request(struct dwc3_request *req) +{ + struct dwc3_ep *dep = req->dep; + + req->status = DWC3_REQUEST_STATUS_QUEUED; + list_move_tail(&req->list, &dep->pending_list); +} + /** * dwc3_gadget_move_started_request - move @req to the started_list * @req: the request to be moved