@@ -3454,6 +3454,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 request_status;
int ret;
@@ -3475,6 +3476,28 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
req->needs_extra_trb = false;
}
+ /*
+ * If MISS ISOC happens, we need to move the req from started_list
+ * to cancelled_list, then unmap the req and clear the HWO of trb.
+ * Later in the dwc3_gadget_endpoint_trbs_complete(), it will move
+ * the req from the cancelled_list to the pending_list, and restart
+ * the req for isoc transfer.
+ */
+ if (status == -EXDEV && usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ req->remaining = 0;
+ req->needs_extra_trb = false;
+ dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED);
+ if (req->trb) {
+ usb_gadget_unmap_request_by_dev(dwc->sysdev,
+ &req->request,
+ req->direction);
+ req->trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ req->trb = NULL;
+ }
+ ret = 0;
+ goto out;
+ }
+
/*
* The event status only reflects the status of the TRB with IOC set.
* For the requests that don't set interrupt on completion, the driver
@@ -3564,6 +3587,7 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event, int status)
{
struct dwc3 *dwc = dep->dwc;
+ struct dwc3_request *req, *tmp;
bool no_started_trb = true;
dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
@@ -3574,6 +3598,29 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
if (!dep->endpoint.desc)
return no_started_trb;
+ /*
+ * If MISS ISOC happens, we need to do the following three steps
+ * to restart the reqs in the cancelled_list and pending_list
+ * in order.
+ * Step1. Move all the reqs from pending_list to the tail of
+ * cancelled_list.
+ * Step2. Move all the reqs from cancelled_list to the tail
+ * of pending_list.
+ * Step3. Stop and restart an isoc transfer.
+ */
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && status == -EXDEV &&
+ !list_empty(&dep->cancelled_list) &&
+ !list_empty(&dep->pending_list)) {
+ list_for_each_entry_safe(req, tmp, &dep->pending_list, list)
+ dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED);
+ }
+
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && status == -EXDEV &&
+ !list_empty(&dep->cancelled_list)) {
+ list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list)
+ dwc3_gadget_move_queued_request(req);
+ }
+
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
list_empty(&dep->started_list) &&
(list_empty(&dep->pending_list) || status == -EXDEV))
@@ -104,6 +104,22 @@ static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req,
list_move_tail(&req->list, &dep->cancelled_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);
+}
+
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status);
If miss isoc event happens, the current code just set the req status to -EXDEV and giveback the req to the usb gadget driver, and then stop the active transfer with the cmd DWC3_DEPCMD_ENDTRANSFER and wait for a XferNotReady event to restart a transfer again. However, for isoc ep in transfer, it cause to lost the isoc data of the req. This patch moves the miss isoc req to pending_list in order to restart transfer immediately instead of give back the req to the usb gadget driver. Signed-off-by: William Wu <william.wu@rock-chips.com> --- drivers/usb/dwc3/gadget.c | 47 +++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc3/gadget.h | 16 +++++++++++++ 2 files changed, 63 insertions(+)