usb: dwc3: gadget: make to increment req->remaining in all cases
[linux-2.6-block.git] / drivers / usb / dwc3 / gadget.c
index 4db97ecae8859ba0bc03c91c4948fc7af205390f..0d75158e43fe4807569db4d10204cee406b097b7 100644 (file)
@@ -1342,6 +1342,68 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
                if (r == req) {
                        /* wait until it is processed */
                        dwc3_stop_active_transfer(dwc, dep->number, true);
+
+                       /*
+                        * If request was already started, this means we had to
+                        * stop the transfer. With that we also need to ignore
+                        * all TRBs used by the request, however TRBs can only
+                        * be modified after completion of END_TRANSFER
+                        * command. So what we do here is that we wait for
+                        * END_TRANSFER completion and only after that, we jump
+                        * over TRBs by clearing HWO and incrementing dequeue
+                        * pointer.
+                        *
+                        * Note that we have 2 possible types of transfers here:
+                        *
+                        * i) Linear buffer request
+                        * ii) SG-list based request
+                        *
+                        * SG-list based requests will have r->num_pending_sgs
+                        * set to a valid number (> 0). Linear requests,
+                        * normally use a single TRB.
+                        *
+                        * For each of these two cases, if r->unaligned flag is
+                        * set, one extra TRB has been used to align transfer
+                        * size to wMaxPacketSize.
+                        *
+                        * All of these cases need to be taken into
+                        * consideration so we don't mess up our TRB ring
+                        * pointers.
+                        */
+                       wait_event_lock_irq(dep->wait_end_transfer,
+                                       !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
+                                       dwc->lock);
+
+                       if (!r->trb)
+                               goto out1;
+
+                       if (r->num_pending_sgs) {
+                               struct dwc3_trb *trb;
+                               int i = 0;
+
+                               for (i = 0; i < r->num_pending_sgs; i++) {
+                                       trb = r->trb + i;
+                                       trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+                                       dwc3_ep_inc_deq(dep);
+                               }
+
+                               if (r->unaligned) {
+                                       trb = r->trb + r->num_pending_sgs + 1;
+                                       trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+                                       dwc3_ep_inc_deq(dep);
+                               }
+                       } else {
+                               struct dwc3_trb *trb = r->trb;
+
+                               trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+                               dwc3_ep_inc_deq(dep);
+
+                               if (r->unaligned) {
+                                       trb = r->trb + 1;
+                                       trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+                                       dwc3_ep_inc_deq(dep);
+                               }
+                       }
                        goto out1;
                }
                dev_err(dwc->dev, "request %p was not queued to %s\n",
@@ -1352,6 +1414,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
 
 out1:
        /* giveback the request */
+       dep->queued_requests--;
        dwc3_gadget_giveback(dep, req, -ECONNRESET);
 
 out0:
@@ -2126,12 +2189,12 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
                return 1;
        }
 
-       if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
-               return 1;
-
        count = trb->size & DWC3_TRB_SIZE_MASK;
        req->remaining += count;
 
+       if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
+               return 1;
+
        if (dep->direction) {
                if (count) {
                        trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
@@ -3228,15 +3291,10 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
 
 int dwc3_gadget_suspend(struct dwc3 *dwc)
 {
-       int ret;
-
        if (!dwc->gadget_driver)
                return 0;
 
-       ret = dwc3_gadget_run_stop(dwc, false, false);
-       if (ret < 0)
-               return ret;
-
+       dwc3_gadget_run_stop(dwc, false, false);
        dwc3_disconnect_gadget(dwc);
        __dwc3_gadget_stop(dwc);