Skip to content

Commit

Permalink
usb: cdns3: Fix dequeue implementation.
Browse files Browse the repository at this point in the history
Dequeuing implementation in cdns3_gadget_ep_dequeue gets first request from
deferred_req_list and changed TRB associated with it to LINK TRB.
This approach is incorrect because deferred_req_list contains requests
that have not been placed on hardware RING.  In this case driver should
just giveback this request to gadget driver.

The patch implements new approach that first checks where dequeuing
request is located and only when it's on Transfer Ring then changes TRB
associated with it to LINK TRB.
During processing completed transfers such LINK TRB will be ignored.

Reported-by: Peter Chen <[email protected]>
Signed-off-by: Pawel Laszczak <[email protected]>
Fixes: 7733f6c ("usb: cdns3: Add Cadence USB3 DRD Driver")
Reviewed-by: Peter Chen <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
pawellcdns authored and gregkh committed Oct 15, 2019
1 parent fd47a41 commit f616c3b
Showing 1 changed file with 20 additions and 15 deletions.
35 changes: 20 additions & 15 deletions drivers/usb/cdns3/gadget.c
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,14 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
request = cdns3_next_request(&priv_ep->pending_req_list);
priv_req = to_cdns3_request(request);

trb = priv_ep->trb_pool + priv_ep->dequeue;

/* Request was dequeued and TRB was changed to TRB_LINK. */
if (TRB_FIELD_TO_TYPE(trb->control) == TRB_LINK) {
trace_cdns3_complete_trb(priv_ep, trb);
cdns3_move_deq_to_next_trb(priv_req);
}

/* Re-select endpoint. It could be changed by other CPU during
* handling usb_gadget_giveback_request.
*/
Expand Down Expand Up @@ -2067,6 +2075,7 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
struct usb_request *req, *req_temp;
struct cdns3_request *priv_req;
struct cdns3_trb *link_trb;
u8 req_on_hw_ring = 0;
unsigned long flags;
int ret = 0;

Expand All @@ -2083,8 +2092,10 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,

list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
list) {
if (request == req)
if (request == req) {
req_on_hw_ring = 1;
goto found;
}
}

list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
Expand All @@ -2096,27 +2107,21 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
goto not_found;

found:

if (priv_ep->wa1_trb == priv_req->trb)
cdns3_wa1_restore_cycle_bit(priv_ep);

link_trb = priv_req->trb;
cdns3_move_deq_to_next_trb(priv_req);
cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);

/* Update ring */
request = cdns3_next_request(&priv_ep->deferred_req_list);
if (request) {
priv_req = to_cdns3_request(request);

/* Update ring only if removed request is on pending_req_list list */
if (req_on_hw_ring) {
link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma +
(priv_req->start_trb * TRB_SIZE));
link_trb->control = (link_trb->control & TRB_CYCLE) |
TRB_TYPE(TRB_LINK) | TRB_CHAIN | TRB_TOGGLE;
} else {
priv_ep->flags |= EP_UPDATE_EP_TRBADDR;
TRB_TYPE(TRB_LINK) | TRB_CHAIN;

if (priv_ep->wa1_trb == priv_req->trb)
cdns3_wa1_restore_cycle_bit(priv_ep);
}

cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);

not_found:
spin_unlock_irqrestore(&priv_dev->lock, flags);
return ret;
Expand Down

0 comments on commit f616c3b

Please sign in to comment.