usb: dwc3: gadget: Check for number of TRBs prepared
authorThinh Nguyen <Thinh.Nguyen@synopsys.com>
Thu, 24 Sep 2020 08:21:55 +0000 (01:21 -0700)
committerFelipe Balbi <balbi@kernel.org>
Fri, 2 Oct 2020 06:57:44 +0000 (09:57 +0300)
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: c6267a51639b ("usb: dwc3: gadget: align transfers to wMaxPacketSize")
Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
drivers/usb/dwc3/gadget.c

index 673a34b..ec14272 100644 (file)
@@ -1257,10 +1257,13 @@ out:
  * 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);
 
@@ -1275,11 +1278,14 @@ 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
@@ -1287,17 +1293,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 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;
@@ -1305,12 +1310,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 || !dwc3_calc_trbs_left(dep))
+                       return ret;
 
                /*
                 * Don't prepare beyond a transfer. In DWC_usb32, its transfer
@@ -1318,8 +1323,10 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
                 * 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);
@@ -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;