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 <william.wu@rock-chips.com>
This commit is contained in:
William Wu
2020-03-23 16:05:24 +08:00
parent d693b4487f
commit cb3db5f6a5
2 changed files with 47 additions and 10 deletions

View File

@@ -1336,6 +1336,15 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
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;
}

View File

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