mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 12:17:12 +09:00
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:
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user