diff options
author | Peter Chen <peter.chen@nxp.com> | 2019-12-13 16:52:06 +0800 |
---|---|---|
committer | Peter Chen <peter.chen@nxp.com> | 2019-12-18 15:12:09 +0800 |
commit | 4ad131b72980f834dbecfd0fe6681d96223c9c11 (patch) | |
tree | d49704d4fa8c7ce22ebc11b447b94bcd5d09ceb7 /drivers/usb/cdns3 | |
parent | 2f1bdd071f81983e7366455ecbfedc088e8ce5f2 (diff) |
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>
Diffstat (limited to 'drivers/usb/cdns3')
-rw-r--r-- | drivers/usb/cdns3/gadget.c | 51 |
1 files changed, 32 insertions, 19 deletions
diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index f34bd7de4fc5..63f52ff12ecc 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -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)) - return; - - trb = priv_ep->trb_pool + priv_ep->dequeue; - trace_cdns3_complete_trb(priv_ep, trb); + 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); - 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_ep_inc_deq(priv_ep); + } - 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); + if (trb_handled) { + cdns3_gadget_giveback(priv_ep, priv_req, 0); + trb_handled = false; + } else { + return; + } } + priv_ep->flags &= ~EP_PENDING_REQUEST; } |