usb: dwc3: gadget: add remaining sg entries to ring
authorFelipe Balbi <felipe.balbi@linux.intel.com>
Fri, 12 Aug 2016 10:17:27 +0000 (13:17 +0300)
committerFelipe Balbi <felipe.balbi@linux.intel.com>
Thu, 25 Aug 2016 09:12:58 +0000 (12:12 +0300)
Upon transfer completion after a full ring, let's
add more TRBs to our ring in order to complete our
request successfully.

Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
drivers/usb/dwc3/gadget.c

index 90b3d79..c18bf1c 100644 (file)
@@ -888,14 +888,13 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
 static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
                struct dwc3_request *req, unsigned int trbs_left)
 {
-       struct usb_request *request = &req->request;
-       struct scatterlist *sg = request->sg;
+       struct scatterlist *sg = req->sg;
        struct scatterlist *s;
        unsigned int    length;
        dma_addr_t      dma;
        int             i;
 
-       for_each_sg(sg, s, request->num_mapped_sgs, i) {
+       for_each_sg(sg, s, req->num_pending_sgs, i) {
                unsigned chain = true;
 
                length = sg_dma_len(s);
@@ -945,7 +944,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
                return;
 
        list_for_each_entry_safe(req, n, &dep->pending_list, list) {
-               if (req->request.num_mapped_sgs > 0)
+               if (req->num_pending_sgs > 0)
                        dwc3_prepare_one_trb_sg(dep, req, trbs_left--);
                else
                        dwc3_prepare_one_trb_linear(dep, req, trbs_left--);
@@ -1071,6 +1070,9 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
        if (ret)
                return ret;
 
+       req->sg                 = req->request.sg;
+       req->num_pending_sgs    = req->request.num_mapped_sgs;
+
        list_add_tail(&req->list, &dep->pending_list);
 
        if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
@@ -1935,22 +1937,30 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
        int                     ret;
 
        list_for_each_entry_safe(req, n, &dep->started_list, list) {
-
+               unsigned length;
+               unsigned actual;
                int chain;
 
-               chain = req->request.num_mapped_sgs > 0;
+               length = req->request.length;
+               chain = req->num_pending_sgs > 0;
                if (chain) {
-                       struct scatterlist *sg = req->request.sg;
+                       struct scatterlist *sg = req->sg;
                        struct scatterlist *s;
+                       unsigned int pending = req->num_pending_sgs;
                        unsigned int i;
 
-                       for_each_sg(sg, s, req->request.num_mapped_sgs, i) {
+                       for_each_sg(sg, s, pending, i) {
                                trb = &dep->trb_pool[dep->trb_dequeue];
                                count += trb->size & DWC3_TRB_SIZE_MASK;
                                dwc3_ep_inc_deq(dep);
 
+                               req->sg = sg_next(s);
+                               req->num_pending_sgs--;
+
                                ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
                                                event, status, chain);
+                               if (ret)
+                                       break;
                        }
                } else {
                        trb = &dep->trb_pool[dep->trb_dequeue];
@@ -1968,7 +1978,12 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
                 * should receive and we simply bounce the request back to the
                 * gadget driver for further processing.
                 */
-               req->request.actual += req->request.length - count;
+               actual = length - req->request.actual;
+               req->request.actual = actual;
+
+               if (ret && chain && (actual < length) && req->num_pending_sgs)
+                       return __dwc3_gadget_kick_transfer(dep, 0);
+
                dwc3_gadget_giveback(dep, req, status);
 
                if (ret)