diff options
Diffstat (limited to 'drivers/usb/dwc3/gadget.c')
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 97 |
1 files changed, 84 insertions, 13 deletions
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4db97ecae885..79e7a3480d51 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c | |||
@@ -171,6 +171,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, | |||
171 | int status) | 171 | int status) |
172 | { | 172 | { |
173 | struct dwc3 *dwc = dep->dwc; | 173 | struct dwc3 *dwc = dep->dwc; |
174 | unsigned int unmap_after_complete = false; | ||
174 | 175 | ||
175 | req->started = false; | 176 | req->started = false; |
176 | list_del(&req->list); | 177 | list_del(&req->list); |
@@ -180,11 +181,19 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, | |||
180 | if (req->request.status == -EINPROGRESS) | 181 | if (req->request.status == -EINPROGRESS) |
181 | req->request.status = status; | 182 | req->request.status = status; |
182 | 183 | ||
183 | if (dwc->ep0_bounced && dep->number <= 1) | 184 | /* |
185 | * NOTICE we don't want to unmap before calling ->complete() if we're | ||
186 | * dealing with a bounced ep0 request. If we unmap it here, we would end | ||
187 | * up overwritting the contents of req->buf and this could confuse the | ||
188 | * gadget driver. | ||
189 | */ | ||
190 | if (dwc->ep0_bounced && dep->number <= 1) { | ||
184 | dwc->ep0_bounced = false; | 191 | dwc->ep0_bounced = false; |
185 | 192 | unmap_after_complete = true; | |
186 | usb_gadget_unmap_request_by_dev(dwc->sysdev, | 193 | } else { |
187 | &req->request, req->direction); | 194 | usb_gadget_unmap_request_by_dev(dwc->sysdev, |
195 | &req->request, req->direction); | ||
196 | } | ||
188 | 197 | ||
189 | trace_dwc3_gadget_giveback(req); | 198 | trace_dwc3_gadget_giveback(req); |
190 | 199 | ||
@@ -192,6 +201,10 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, | |||
192 | usb_gadget_giveback_request(&dep->endpoint, &req->request); | 201 | usb_gadget_giveback_request(&dep->endpoint, &req->request); |
193 | spin_lock(&dwc->lock); | 202 | spin_lock(&dwc->lock); |
194 | 203 | ||
204 | if (unmap_after_complete) | ||
205 | usb_gadget_unmap_request_by_dev(dwc->sysdev, | ||
206 | &req->request, req->direction); | ||
207 | |||
195 | if (dep->number > 1) | 208 | if (dep->number > 1) |
196 | pm_runtime_put(dwc->dev); | 209 | pm_runtime_put(dwc->dev); |
197 | } | 210 | } |
@@ -1342,6 +1355,68 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, | |||
1342 | if (r == req) { | 1355 | if (r == req) { |
1343 | /* wait until it is processed */ | 1356 | /* wait until it is processed */ |
1344 | dwc3_stop_active_transfer(dwc, dep->number, true); | 1357 | dwc3_stop_active_transfer(dwc, dep->number, true); |
1358 | |||
1359 | /* | ||
1360 | * If request was already started, this means we had to | ||
1361 | * stop the transfer. With that we also need to ignore | ||
1362 | * all TRBs used by the request, however TRBs can only | ||
1363 | * be modified after completion of END_TRANSFER | ||
1364 | * command. So what we do here is that we wait for | ||
1365 | * END_TRANSFER completion and only after that, we jump | ||
1366 | * over TRBs by clearing HWO and incrementing dequeue | ||
1367 | * pointer. | ||
1368 | * | ||
1369 | * Note that we have 2 possible types of transfers here: | ||
1370 | * | ||
1371 | * i) Linear buffer request | ||
1372 | * ii) SG-list based request | ||
1373 | * | ||
1374 | * SG-list based requests will have r->num_pending_sgs | ||
1375 | * set to a valid number (> 0). Linear requests, | ||
1376 | * normally use a single TRB. | ||
1377 | * | ||
1378 | * For each of these two cases, if r->unaligned flag is | ||
1379 | * set, one extra TRB has been used to align transfer | ||
1380 | * size to wMaxPacketSize. | ||
1381 | * | ||
1382 | * All of these cases need to be taken into | ||
1383 | * consideration so we don't mess up our TRB ring | ||
1384 | * pointers. | ||
1385 | */ | ||
1386 | wait_event_lock_irq(dep->wait_end_transfer, | ||
1387 | !(dep->flags & DWC3_EP_END_TRANSFER_PENDING), | ||
1388 | dwc->lock); | ||
1389 | |||
1390 | if (!r->trb) | ||
1391 | goto out1; | ||
1392 | |||
1393 | if (r->num_pending_sgs) { | ||
1394 | struct dwc3_trb *trb; | ||
1395 | int i = 0; | ||
1396 | |||
1397 | for (i = 0; i < r->num_pending_sgs; i++) { | ||
1398 | trb = r->trb + i; | ||
1399 | trb->ctrl &= ~DWC3_TRB_CTRL_HWO; | ||
1400 | dwc3_ep_inc_deq(dep); | ||
1401 | } | ||
1402 | |||
1403 | if (r->unaligned) { | ||
1404 | trb = r->trb + r->num_pending_sgs + 1; | ||
1405 | trb->ctrl &= ~DWC3_TRB_CTRL_HWO; | ||
1406 | dwc3_ep_inc_deq(dep); | ||
1407 | } | ||
1408 | } else { | ||
1409 | struct dwc3_trb *trb = r->trb; | ||
1410 | |||
1411 | trb->ctrl &= ~DWC3_TRB_CTRL_HWO; | ||
1412 | dwc3_ep_inc_deq(dep); | ||
1413 | |||
1414 | if (r->unaligned) { | ||
1415 | trb = r->trb + 1; | ||
1416 | trb->ctrl &= ~DWC3_TRB_CTRL_HWO; | ||
1417 | dwc3_ep_inc_deq(dep); | ||
1418 | } | ||
1419 | } | ||
1345 | goto out1; | 1420 | goto out1; |
1346 | } | 1421 | } |
1347 | dev_err(dwc->dev, "request %p was not queued to %s\n", | 1422 | dev_err(dwc->dev, "request %p was not queued to %s\n", |
@@ -1352,6 +1427,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, | |||
1352 | 1427 | ||
1353 | out1: | 1428 | out1: |
1354 | /* giveback the request */ | 1429 | /* giveback the request */ |
1430 | dep->queued_requests--; | ||
1355 | dwc3_gadget_giveback(dep, req, -ECONNRESET); | 1431 | dwc3_gadget_giveback(dep, req, -ECONNRESET); |
1356 | 1432 | ||
1357 | out0: | 1433 | out0: |
@@ -2126,12 +2202,12 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, | |||
2126 | return 1; | 2202 | return 1; |
2127 | } | 2203 | } |
2128 | 2204 | ||
2129 | if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) | ||
2130 | return 1; | ||
2131 | |||
2132 | count = trb->size & DWC3_TRB_SIZE_MASK; | 2205 | count = trb->size & DWC3_TRB_SIZE_MASK; |
2133 | req->remaining += count; | 2206 | req->remaining += count; |
2134 | 2207 | ||
2208 | if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) | ||
2209 | return 1; | ||
2210 | |||
2135 | if (dep->direction) { | 2211 | if (dep->direction) { |
2136 | if (count) { | 2212 | if (count) { |
2137 | trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size); | 2213 | trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size); |
@@ -3228,15 +3304,10 @@ void dwc3_gadget_exit(struct dwc3 *dwc) | |||
3228 | 3304 | ||
3229 | int dwc3_gadget_suspend(struct dwc3 *dwc) | 3305 | int dwc3_gadget_suspend(struct dwc3 *dwc) |
3230 | { | 3306 | { |
3231 | int ret; | ||
3232 | |||
3233 | if (!dwc->gadget_driver) | 3307 | if (!dwc->gadget_driver) |
3234 | return 0; | 3308 | return 0; |
3235 | 3309 | ||
3236 | ret = dwc3_gadget_run_stop(dwc, false, false); | 3310 | dwc3_gadget_run_stop(dwc, false, false); |
3237 | if (ret < 0) | ||
3238 | return ret; | ||
3239 | |||
3240 | dwc3_disconnect_gadget(dwc); | 3311 | dwc3_disconnect_gadget(dwc); |
3241 | __dwc3_gadget_stop(dwc); | 3312 | __dwc3_gadget_stop(dwc); |
3242 | 3313 | ||