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