@@ -1057,8 +1057,10 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
* @trb_length: buffer size of the TRB
* @chain: should this TRB be chained to the next?
* @node: only for isochronous endpoints. First TRB needs different type.
+ *
+ * Return 0 on success or -EAGAIN if there is not enough TRBs.
*/
-static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
+static int dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, unsigned int trb_length,
unsigned chain, unsigned node)
{
@@ -1069,6 +1071,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
unsigned no_interrupt = req->request.no_interrupt;
unsigned is_last = req->request.is_last;
+ if (!dwc3_calc_trbs_left(dep))
+ return -EAGAIN;
+
if (req->request.num_sgs > 0)
dma = sg_dma_address(req->start_sg);
else
@@ -1086,6 +1091,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
__dwc3_prepare_one_trb(dep, trb, dma, trb_length, chain, node,
stream_id, short_not_ok, no_interrupt, is_last);
+
+ return 0;
}
/**
@@ -1094,11 +1101,13 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
* @req: The request to prepare
* @entry_length: The last SG entry size
* @node: Indicates whether this is not the first entry (for isoc only)
+ *
+ * Returns 0 on success or -EAGAIN if there is not enough TRBs.
*/
-static void dwc3_prepare_last_sg(struct dwc3_ep *dep,
- struct dwc3_request *req,
- unsigned int entry_length,
- unsigned int node)
+static int dwc3_prepare_last_sg(struct dwc3_ep *dep,
+ struct dwc3_request *req,
+ unsigned int entry_length,
+ unsigned int node)
{
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
unsigned int rem = req->request.length % maxp;
@@ -1118,6 +1127,9 @@ static void dwc3_prepare_last_sg(struct dwc3_ep *dep,
if (num_extra_trbs > 0)
req->needs_extra_trb = true;
+ if (dwc3_calc_trbs_left(dep) < num_extra_trbs + 1)
+ return -EAGAIN;
+
/* Prepare a normal TRB */
dwc3_prepare_one_trb(dep, req, entry_length, req->needs_extra_trb, node);
@@ -1145,9 +1157,11 @@ static void dwc3_prepare_last_sg(struct dwc3_ep *dep,
req->request.no_interrupt,
req->request.is_last);
}
+
+ return 0;
}
-static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
+static int dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
struct dwc3_request *req)
{
struct scatterlist *sg = req->start_sg;
@@ -1163,6 +1177,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
unsigned int rem = length % maxp;
unsigned int trb_length;
bool last_sg = false;
+ int ret = 0;
trb_length = min_t(unsigned int, length, sg_dma_len(req->start_sg));
@@ -1179,9 +1194,13 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
last_sg = true;
if (last_sg)
- dwc3_prepare_last_sg(dep, req, trb_length, i);
+ ret = dwc3_prepare_last_sg(dep, req, trb_length, i);
else
- dwc3_prepare_one_trb(dep, req, trb_length, 1, i);
+ ret = dwc3_prepare_one_trb(dep, req, trb_length, 1, i);
+
+ /* Ran out of TRBs */
+ if (ret)
+ return ret;
/*
* There can be a situation where all sgs in sglist are not
@@ -1204,16 +1223,14 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
req->num_pending_sgs -= req->request.num_mapped_sgs - req->num_queued_sgs;
break;
}
-
- if (!dwc3_calc_trbs_left(dep))
- break;
}
+ return 0;
}
-static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
+static int dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
struct dwc3_request *req)
{
- dwc3_prepare_last_sg(dep, req, req->request.length, 0);
+ return dwc3_prepare_last_sg(dep, req, req->request.length, 0);
}
/*
@@ -1224,9 +1241,10 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
* transfers. The function returns once there are no more TRBs available or
* it runs out of requests.
*/
-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);
@@ -1241,11 +1259,11 @@ 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 (!dwc3_calc_trbs_left(dep))
- return;
+ if (req->num_pending_sgs > 0) {
+ ret = dwc3_prepare_one_trb_sg(dep, req);
+ if (ret)
+ return ret;
+ }
/*
* Don't prepare beyond a transfer. In DWC_usb32, its transfer
@@ -1253,17 +1271,16 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
* active transfer instead of stopping.
*/
if (dep->stream_capable && req->request.is_last)
- return;
+ return 0;
}
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;
@@ -1271,12 +1288,12 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
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)
+ return ret;
/*
* Don't prepare beyond a transfer. In DWC_usb32, its transfer
@@ -1284,7 +1301,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
* active transfer instead of stopping.
*/
if (dep->stream_capable && req->request.is_last)
- return;
+ return 0;
}
}
@@ -1298,12 +1315,12 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
int ret;
u32 cmd;
- if (!dwc3_calc_trbs_left(dep))
- return 0;
-
starting = !(dep->flags & DWC3_EP_TRANSFER_STARTED);
- dwc3_prepare_trbs(dep);
+ ret = dwc3_prepare_trbs(dep);
+ if (ret)
+ return 0;
+
req = next_request(&dep->started_list);
if (!req) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
When checking for how many TRB remaining, make sure to account for extra TRBs for ZLP or MPS alignment transfers. Since the dwc3_prepare_trb* functions should know if we need the extra TRBs, make those functions return a status code -EAGAIN if there isn't enough TRB. Check against those status when preparing TRB instead. Fixes: c6267a51639b ("usb: dwc3: gadget: align transfers to wMaxPacketSize") Signed-off-by: Thinh Nguyen <thinhn@synopsys.com> --- drivers/usb/dwc3/gadget.c | 79 ++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 31 deletions(-)