|
@@ -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:
|