summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelipe Balbi <felipe.balbi@linux.intel.com>2017-02-17 11:12:44 +0200
committerLeonard Crestez <leonard.crestez@nxp.com>2018-08-24 12:41:33 +0300
commitb72c95137c2608ac00762eeda4fcd04a32ad3d83 (patch)
treeb0db98215a3c58f521ffbc2bc08da1164bdab26c
parentb877768fee78ba3eae4b9a01fe6299e8d34726de (diff)
usb: dwc3: gadget: properly increment dequeue pointer on ep_dequeue
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 have to do here is wait for END_TRANSFER completion and only after that 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. All of these cases need to be taken into consideration so we don't mess up our TRB ring pointers. Tested-by: Janusz Dziedzic <januszx.dziedzic@intel.com> Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com> (cherry picked from commit cf3113d893d4427b166ec8695460efa7aa660923)
-rw-r--r--drivers/usb/dwc3/gadget.c47
1 files changed, 47 insertions, 0 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f70cb93a36a2..6e64d3a5f019 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1289,6 +1289,52 @@ 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.
+ *
+ * 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);
+ }
+ } else {
+ struct dwc3_trb *trb = r->trb;
+
+ trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+ dwc3_ep_inc_deq(dep);
+ }
goto out1;
}
dev_err(dwc->dev, "request %pK was not queued to %s\n",
@@ -1299,6 +1345,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: