summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/dwc3/core.h2
-rw-r--r--drivers/usb/dwc3/gadget.c23
2 files changed, 22 insertions, 3 deletions
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index b6ca898a65d0..ab93d22b6965 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -834,6 +834,7 @@ struct dwc3_hwparams {
* @sg: pointer to first incomplete sg
* @start_sg: pointer to the sg which should be queued next
* @num_pending_sgs: counter to pending sgs
+ * @num_queued_sgs: counter to the number of sgs which already got queued
* @remaining: amount of data remaining
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
@@ -852,6 +853,7 @@ struct dwc3_request {
struct scatterlist *start_sg;
unsigned num_pending_sgs;
+ unsigned int num_queued_sgs;
unsigned remaining;
u8 epnum;
struct dwc3_trb *trb;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 56ca1666b35f..43cbc363c72f 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1067,7 +1067,10 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
struct scatterlist *s;
int i;
- for_each_sg(sg, s, req->num_pending_sgs, i) {
+ unsigned int remaining = req->request.num_mapped_sgs
+ - req->num_queued_sgs;
+
+ for_each_sg(sg, s, remaining, i) {
unsigned int length = req->request.length;
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
unsigned int rem = length % maxp;
@@ -1106,6 +1109,8 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
if (chain)
req->start_sg = sg_next(s);
+ req->num_queued_sgs++;
+
if (!dwc3_calc_trbs_left(dep))
break;
}
@@ -1197,6 +1202,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
req->sg = req->request.sg;
req->start_sg = req->sg;
+ req->num_queued_sgs = 0;
req->num_pending_sgs = req->request.num_mapped_sgs;
if (req->num_pending_sgs > 0)
@@ -2380,8 +2386,19 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
req->request.actual = length - req->remaining;
- if ((req->request.actual < length) && req->num_pending_sgs)
- return __dwc3_gadget_kick_transfer(dep);
+ if (req->request.actual < length || req->num_pending_sgs) {
+ /*
+ * There could be a scenario where the whole req can't
+ * be mapped into available TRB's. In that case, we need
+ * to kick transfer again if (req->num_pending_sgs > 0)
+ */
+ if (req->num_pending_sgs) {
+ dev_WARN_ONCE(dwc->dev,
+ (req->request.actual == length),
+ "There are some pending sg's that needs to be queued again\n");
+ return __dwc3_gadget_kick_transfer(dep);
+ }
+ }
dwc3_gadget_giveback(dep, req, status);