1
0
Fork 0

LF-468-1 usb: cdns3: gadget: handle trb correctly at sg case

At current transfer complete handler, it doesn't consider
sg (scatter buffer list) case, and only handles single trb
request. In fact, we need to handle every trbs in request,
and giveback the request after all trbs are handled.

Signed-off-by: Peter Chen <peter.chen@nxp.com>
5.4-rM2-2.2.x-imx-squashed
Peter Chen 2019-12-13 16:52:06 +08:00
parent 2f1bdd071f
commit 4ad131b729
No known key found for this signature in database
GPG Key ID: 4859298150D671BB
1 changed files with 33 additions and 20 deletions

View File

@ -826,14 +826,28 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
struct cdns3_request *priv_req)
{
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
struct cdns3_trb *trb = priv_req->trb;
struct cdns3_trb *trb;
int current_index = 0;
int handled = 0;
current_index = (readl(&priv_dev->regs->ep_traddr) -
priv_ep->trb_pool_dma) / TRB_SIZE;
trb = &priv_ep->trb_pool[priv_req->start_trb];
/* current trb doesn't belong to this request */
if ((priv_req->start_trb < priv_req->end_trb) &&
(priv_ep->dequeue > priv_req->end_trb))
goto finish;
if ((priv_req->start_trb > priv_req->end_trb) &&
(priv_ep->dequeue > priv_req->end_trb) &&
(priv_ep->dequeue < priv_req->start_trb))
goto finish;
if ((priv_req->start_trb == priv_req->end_trb) &&
(priv_ep->dequeue != priv_req->end_trb))
goto finish;
trb = &priv_ep->trb_pool[priv_ep->dequeue];
if ((trb->control & TRB_CYCLE) != priv_ep->ccs)
goto finish;
@ -843,12 +857,8 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
!priv_ep->dequeue)
goto finish;
if (priv_req->end_trb >= priv_ep->dequeue &&
priv_req->end_trb < current_index)
handled = 1;
handled = 1;
} else if (priv_ep->dequeue > current_index) {
if (priv_req->end_trb < current_index ||
priv_req->end_trb >= priv_ep->dequeue)
handled = 1;
}
@ -864,6 +874,7 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
struct cdns3_request *priv_req;
struct usb_request *request;
struct cdns3_trb *trb;
bool trb_handled = false;
while (!list_empty(&priv_ep->pending_req_list)) {
request = cdns3_next_request(&priv_ep->pending_req_list);
@ -874,21 +885,23 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
*/
cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
if (!cdns3_request_handled(priv_ep, priv_req))
while (cdns3_request_handled(priv_ep, priv_req)) {
trb_handled = true;
trb = priv_ep->trb_pool + priv_ep->dequeue;
trace_cdns3_complete_trb(priv_ep, trb);
request->actual += TRB_LEN(le32_to_cpu(trb->length));
cdns3_ep_inc_deq(priv_ep);
}
if (trb_handled) {
cdns3_gadget_giveback(priv_ep, priv_req, 0);
trb_handled = false;
} else {
return;
trb = priv_ep->trb_pool + priv_ep->dequeue;
trace_cdns3_complete_trb(priv_ep, trb);
if (trb != priv_req->trb)
dev_warn(priv_dev->dev,
"request_trb=0x%p, queue_trb=0x%p\n",
priv_req->trb, trb);
request->actual = TRB_LEN(le32_to_cpu(trb->length));
cdns3_move_deq_to_next_trb(priv_req);
cdns3_gadget_giveback(priv_ep, priv_req, 0);
}
}
priv_ep->flags &= ~EP_PENDING_REQUEST;
}