aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelipe Balbi <felipe.balbi@linux.intel.com>2017-01-03 11:28:53 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-03 11:37:32 -0500
commit29fc1aa454d0603493b47a8e2410ae6e9ab20258 (patch)
treeb3043688b5e5ac95ea3e9b9398b9726f3be4565a
parent6c97cfc1a097b1e0786c836e92b7a72b4d031e25 (diff)
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>
-rw-r--r--drivers/usb/host/xhci-ring.c33
1 files changed, 21 insertions, 12 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 7a14e9ad664d..25f522b09dd9 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1971,8 +1971,9 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
1971 struct xhci_ep_ctx *ep_ctx; 1971 struct xhci_ep_ctx *ep_ctx;
1972 u32 trb_comp_code; 1972 u32 trb_comp_code;
1973 u32 remaining, requested; 1973 u32 remaining, requested;
1974 bool on_data_stage; 1974 u32 trb_type;
1975 1975
1976 trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(ep_trb->generic.field[3]));
1976 slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); 1977 slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
1977 xdev = xhci->devs[slot_id]; 1978 xdev = xhci->devs[slot_id];
1978 ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; 1979 ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
@@ -1982,14 +1983,11 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
1982 requested = td->urb->transfer_buffer_length; 1983 requested = td->urb->transfer_buffer_length;
1983 remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); 1984 remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
1984 1985
1985 /* not setup (dequeue), or status stage means we are at data stage */
1986 on_data_stage = (ep_trb != ep_ring->dequeue && ep_trb != td->last_trb);
1987
1988 switch (trb_comp_code) { 1986 switch (trb_comp_code) {
1989 case COMP_SUCCESS: 1987 case COMP_SUCCESS:
1990 if (ep_trb != td->last_trb) { 1988 if (trb_type != TRB_STATUS) {
1991 xhci_warn(xhci, "WARN: Success on ctrl %s TRB without IOC set?\n", 1989 xhci_warn(xhci, "WARN: Success on ctrl %s TRB without IOC set?\n",
1992 on_data_stage ? "data" : "setup"); 1990 (trb_type == TRB_DATA) ? "data" : "setup");
1993 *status = -ESHUTDOWN; 1991 *status = -ESHUTDOWN;
1994 break; 1992 break;
1995 } 1993 }
@@ -1999,15 +1997,25 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
1999 *status = 0; 1997 *status = 0;
2000 break; 1998 break;
2001 case COMP_STOP_SHORT: 1999 case COMP_STOP_SHORT:
2002 if (on_data_stage) 2000 if (trb_type == TRB_DATA || trb_type == TRB_NORMAL)
2003 td->urb->actual_length = remaining; 2001 td->urb->actual_length = remaining;
2004 else 2002 else
2005 xhci_warn(xhci, "WARN: Stopped Short Packet on ctrl setup or status TRB\n"); 2003 xhci_warn(xhci, "WARN: Stopped Short Packet on ctrl setup or status TRB\n");
2006 goto finish_td; 2004 goto finish_td;
2007 case COMP_STOP: 2005 case COMP_STOP:
2008 if (on_data_stage) 2006 switch (trb_type) {
2007 case TRB_SETUP:
2008 td->urb->actual_length = 0;
2009 goto finish_td;
2010 case TRB_DATA:
2011 case TRB_NORMAL:
2009 td->urb->actual_length = requested - remaining; 2012 td->urb->actual_length = requested - remaining;
2010 goto finish_td; 2013 goto finish_td;
2014 default:
2015 xhci_warn(xhci, "WARN: unexpected TRB Type %d\n",
2016 trb_type);
2017 goto finish_td;
2018 }
2011 case COMP_STOP_INVAL: 2019 case COMP_STOP_INVAL:
2012 goto finish_td; 2020 goto finish_td;
2013 default: 2021 default:
@@ -2019,7 +2027,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
2019 /* else fall through */ 2027 /* else fall through */
2020 case COMP_STALL: 2028 case COMP_STALL:
2021 /* Did we transfer part of the data (middle) phase? */ 2029 /* Did we transfer part of the data (middle) phase? */
2022 if (on_data_stage) 2030 if (trb_type == TRB_DATA || trb_type == TRB_NORMAL)
2023 td->urb->actual_length = requested - remaining; 2031 td->urb->actual_length = requested - remaining;
2024 else if (!td->urb_length_set) 2032 else if (!td->urb_length_set)
2025 td->urb->actual_length = 0; 2033 td->urb->actual_length = 0;
@@ -2027,14 +2035,15 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
2027 } 2035 }
2028 2036
2029 /* stopped at setup stage, no data transferred */ 2037 /* stopped at setup stage, no data transferred */
2030 if (ep_trb == ep_ring->dequeue) 2038 if (trb_type == TRB_SETUP)
2031 goto finish_td; 2039 goto finish_td;
2032 2040
2033 /* 2041 /*
2034 * if on data stage then update the actual_length of the URB and flag it 2042 * if on data stage then update the actual_length of the URB and flag it
2035 * as set, so it won't be overwritten in the event for the last TRB. 2043 * as set, so it won't be overwritten in the event for the last TRB.
2036 */ 2044 */
2037 if (on_data_stage) { 2045 if (trb_type == TRB_DATA ||
2046 trb_type == TRB_NORMAL) {
2038 td->urb_length_set = true; 2047 td->urb_length_set = true;
2039 td->urb->actual_length = requested - remaining; 2048 td->urb->actual_length = requested - remaining;
2040 xhci_dbg(xhci, "Waiting for status stage event\n"); 2049 xhci_dbg(xhci, "Waiting for status stage event\n");