usb: dwc3: gadget: properly handle miss isoc event

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>
Change-Id: Idf38d9fd4d483854473c18f792d1996fb5fcab4b
This commit is contained in:
William Wu
2022-04-27 18:35:15 +08:00
committed by Tao Huang
parent 2007d5815e
commit c7bcfa4a88
2 changed files with 63 additions and 0 deletions

View File

@@ -3249,6 +3249,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->request.num_mapped_sgs)
@@ -3269,6 +3270,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;
}
dwc3_gadget_giveback(dep, req, status);
out:
@@ -3324,6 +3347,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;
if (!dep->endpoint.desc)
@@ -3334,6 +3358,29 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
goto out;
/*
* 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))

View File

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