Commit 29fc1aa4 authored by Felipe Balbi's avatar Felipe Balbi Committed by Greg Kroah-Hartman

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: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6c97cfc1
...@@ -1971,8 +1971,9 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, ...@@ -1971,8 +1971,9 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
struct xhci_ep_ctx *ep_ctx; struct xhci_ep_ctx *ep_ctx;
u32 trb_comp_code; u32 trb_comp_code;
u32 remaining, requested; 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)); slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
xdev = xhci->devs[slot_id]; xdev = xhci->devs[slot_id];
ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; 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,14 +1983,11 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
requested = td->urb->transfer_buffer_length; requested = td->urb->transfer_buffer_length;
remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); 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) { switch (trb_comp_code) {
case COMP_SUCCESS: 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", 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; *status = -ESHUTDOWN;
break; break;
} }
...@@ -1999,15 +1997,25 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, ...@@ -1999,15 +1997,25 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
*status = 0; *status = 0;
break; break;
case COMP_STOP_SHORT: case COMP_STOP_SHORT:
if (on_data_stage) if (trb_type == TRB_DATA || trb_type == TRB_NORMAL)
td->urb->actual_length = remaining; td->urb->actual_length = remaining;
else else
xhci_warn(xhci, "WARN: Stopped Short Packet on ctrl setup or status TRB\n"); xhci_warn(xhci, "WARN: Stopped Short Packet on ctrl setup or status TRB\n");
goto finish_td; goto finish_td;
case COMP_STOP: 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; 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: case COMP_STOP_INVAL:
goto finish_td; goto finish_td;
default: default:
...@@ -2019,7 +2027,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, ...@@ -2019,7 +2027,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
/* else fall through */ /* else fall through */
case COMP_STALL: case COMP_STALL:
/* Did we transfer part of the data (middle) phase? */ /* 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; td->urb->actual_length = requested - remaining;
else if (!td->urb_length_set) else if (!td->urb_length_set)
td->urb->actual_length = 0; td->urb->actual_length = 0;
...@@ -2027,14 +2035,15 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, ...@@ -2027,14 +2035,15 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
} }
/* stopped at setup stage, no data transferred */ /* stopped at setup stage, no data transferred */
if (ep_trb == ep_ring->dequeue) if (trb_type == TRB_SETUP)
goto finish_td; goto finish_td;
/* /*
* if on data stage then update the actual_length of the URB and flag it * 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. * 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_length_set = true;
td->urb->actual_length = requested - remaining; td->urb->actual_length = requested - remaining;
xhci_dbg(xhci, "Waiting for status stage event\n"); xhci_dbg(xhci, "Waiting for status stage event\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment