| From 8d8dd85e33b4672179e1eb06d3d516d6f81d2c46 Mon Sep 17 00:00:00 2001 |
| From: Felipe Balbi <felipe.balbi@linux.intel.com> |
| Date: Tue, 3 Jan 2017 18:28:53 +0200 |
| Subject: [PATCH 295/299] usb: host: xhci: handle COMP_STOP from SETUP phase |
| too |
| |
| Stop Endpoint command can come at any point and we |
| have no control of that. We should make sure to |
| handle COMP_STOP on SETUP phase as well, otherwise |
| urb->actual_length might be set to negative values |
| in some occasions such as below: |
| |
| urb->length = 4; |
| build_control_transfer_td_for(urb, ep); |
| |
| stop_endpoint(ep); |
| |
| COMP_STOP: |
| [...] |
| urb->actual_length = urb->length - trb->length; |
| |
| trb->length is 8 for SETUP stage (8 control request |
| bytes), so actual_length would be set to -4 in this |
| case. |
| |
| While doing that, also make sure to use TRB_TYPE |
| field of the actual TRB instead of matching pointers |
| to figure out in which stage of the control transfer |
| we got our completion event. |
| |
| Cc: <stable@vger.kernel.org> |
| Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com> |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| (cherry picked from commit 29fc1aa454d0603493b47a8e2410ae6e9ab20258) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| drivers/usb/host/xhci-ring.c | 33 +++++++++++++++++++++------------ |
| 1 file changed, 21 insertions(+), 12 deletions(-) |
| |
| --- a/drivers/usb/host/xhci-ring.c |
| +++ b/drivers/usb/host/xhci-ring.c |
| @@ -1963,8 +1963,9 @@ static int process_ctrl_td(struct xhci_h |
| struct xhci_ep_ctx *ep_ctx; |
| u32 trb_comp_code; |
| u32 remaining, requested; |
| - bool on_data_stage; |
| + u32 trb_type; |
| |
| + trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(ep_trb->generic.field[3])); |
| slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); |
| xdev = xhci->devs[slot_id]; |
| ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; |
| @@ -1974,14 +1975,11 @@ static int process_ctrl_td(struct xhci_h |
| requested = td->urb->transfer_buffer_length; |
| remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); |
| |
| - /* not setup (dequeue), or status stage means we are at data stage */ |
| - on_data_stage = (ep_trb != ep_ring->dequeue && ep_trb != td->last_trb); |
| - |
| switch (trb_comp_code) { |
| case COMP_SUCCESS: |
| - if (ep_trb != td->last_trb) { |
| + if (trb_type != TRB_STATUS) { |
| xhci_warn(xhci, "WARN: Success on ctrl %s TRB without IOC set?\n", |
| - on_data_stage ? "data" : "setup"); |
| + (trb_type == TRB_DATA) ? "data" : "setup"); |
| *status = -ESHUTDOWN; |
| break; |
| } |
| @@ -1991,15 +1989,25 @@ static int process_ctrl_td(struct xhci_h |
| *status = 0; |
| break; |
| case COMP_STOP_SHORT: |
| - if (on_data_stage) |
| + if (trb_type == TRB_DATA || trb_type == TRB_NORMAL) |
| td->urb->actual_length = remaining; |
| else |
| xhci_warn(xhci, "WARN: Stopped Short Packet on ctrl setup or status TRB\n"); |
| goto finish_td; |
| case COMP_STOP: |
| - if (on_data_stage) |
| + switch (trb_type) { |
| + case TRB_SETUP: |
| + td->urb->actual_length = 0; |
| + goto finish_td; |
| + case TRB_DATA: |
| + case TRB_NORMAL: |
| td->urb->actual_length = requested - remaining; |
| - goto finish_td; |
| + goto finish_td; |
| + default: |
| + xhci_warn(xhci, "WARN: unexpected TRB Type %d\n", |
| + trb_type); |
| + goto finish_td; |
| + } |
| case COMP_STOP_INVAL: |
| goto finish_td; |
| default: |
| @@ -2011,7 +2019,7 @@ static int process_ctrl_td(struct xhci_h |
| /* else fall through */ |
| case COMP_STALL: |
| /* Did we transfer part of the data (middle) phase? */ |
| - if (on_data_stage) |
| + if (trb_type == TRB_DATA || trb_type == TRB_NORMAL) |
| td->urb->actual_length = requested - remaining; |
| else if (!td->urb_length_set) |
| td->urb->actual_length = 0; |
| @@ -2019,14 +2027,15 @@ static int process_ctrl_td(struct xhci_h |
| } |
| |
| /* stopped at setup stage, no data transferred */ |
| - if (ep_trb == ep_ring->dequeue) |
| + if (trb_type == TRB_SETUP) |
| goto finish_td; |
| |
| /* |
| * if on data stage then update the actual_length of the URB and flag it |
| * as set, so it won't be overwritten in the event for the last TRB. |
| */ |
| - if (on_data_stage) { |
| + if (trb_type == TRB_DATA || |
| + trb_type == TRB_NORMAL) { |
| td->urb_length_set = true; |
| td->urb->actual_length = requested - remaining; |
| xhci_dbg(xhci, "Waiting for status stage event\n"); |