Skip to content

Commit

Permalink
usb: dwc3: gadget: Check for number of TRBs prepared
Browse files Browse the repository at this point in the history
By returning the number of TRBs prepared, we know whether to execute
__dwc3_gadget_kick_transfer(). This allows us to check if we ran out of
TRBs when extra TRBs are needed for OUT transfers. It also allows us to
properly handle usb_gadget_map_request_by_dev() error.

Fixes: c6267a5 ("usb: dwc3: gadget: align transfers to wMaxPacketSize")
Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
  • Loading branch information
Thinh Nguyen authored and Felipe Balbi committed Oct 2, 2020
1 parent 13111fc commit 490410b
Showing 1 changed file with 22 additions and 15 deletions.
37 changes: 22 additions & 15 deletions drivers/usb/dwc3/gadget.c
Original file line number Diff line number Diff line change
Expand Up @@ -1257,10 +1257,13 @@ static int dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
* The function goes through the requests list and sets up TRBs for the
* transfers. The function returns once there are no more TRBs available or
* it runs out of requests.
*
* Returns the number of TRBs prepared or negative errno.
*/
static void dwc3_prepare_trbs(struct dwc3_ep *dep)
static int dwc3_prepare_trbs(struct dwc3_ep *dep)
{
struct dwc3_request *req, *n;
int ret = 0;

BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);

Expand All @@ -1275,51 +1278,55 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
* break things.
*/
list_for_each_entry(req, &dep->started_list, list) {
if (req->num_pending_sgs > 0)
dwc3_prepare_one_trb_sg(dep, req);
if (req->num_pending_sgs > 0) {
ret = dwc3_prepare_one_trb_sg(dep, req);
if (!ret)
return ret;
}

if (!dwc3_calc_trbs_left(dep))
return;
return ret;

/*
* Don't prepare beyond a transfer. In DWC_usb32, its transfer
* burst capability may try to read and use TRBs beyond the
* active transfer instead of stopping.
*/
if (dep->stream_capable && req->request.is_last)
return;
return ret;
}

list_for_each_entry_safe(req, n, &dep->pending_list, list) {
struct dwc3 *dwc = dep->dwc;
int ret;

ret = usb_gadget_map_request_by_dev(dwc->sysdev, &req->request,
dep->direction);
if (ret)
return;
return ret;

req->sg = req->request.sg;
req->start_sg = req->sg;
req->num_queued_sgs = 0;
req->num_pending_sgs = req->request.num_mapped_sgs;

if (req->num_pending_sgs > 0)
dwc3_prepare_one_trb_sg(dep, req);
ret = dwc3_prepare_one_trb_sg(dep, req);
else
dwc3_prepare_one_trb_linear(dep, req);
ret = dwc3_prepare_one_trb_linear(dep, req);

if (!dwc3_calc_trbs_left(dep))
return;
if (!ret || !dwc3_calc_trbs_left(dep))
return ret;

/*
* Don't prepare beyond a transfer. In DWC_usb32, its transfer
* burst capability may try to read and use TRBs beyond the
* active transfer instead of stopping.
*/
if (dep->stream_capable && req->request.is_last)
return;
return ret;
}

return ret;
}

static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep);
Expand All @@ -1332,12 +1339,12 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
int ret;
u32 cmd;

if (!dwc3_calc_trbs_left(dep))
return 0;
ret = dwc3_prepare_trbs(dep);
if (ret <= 0)
return ret;

starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED);

dwc3_prepare_trbs(dep);
req = next_request(&dep->started_list);
if (!req) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
Expand Down

0 comments on commit 490410b

Please sign in to comment.