patch "usb: host: xhci: handle COMP_STOP from SETUP phase too" added to usb-linus
Subsystems (3)
This is a note to let you know that I've just added the patch titled
usb: host: xhci: handle COMP_STOP from SETUP phase too
to my usb git tree which can be found at
git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git
in the usb-linus branch.
The patch will show up in the next release of the linux-next tree (usually sometime within the next 24 hours during the week.)
The patch will hopefully also be merged in Linus's tree for the next -rc kernel release.
If you have any questions about this process, please let me know.
Quoted 1 line
From
29fc1aa454d0603493b47a8e2410ae6e9ab20258Mon Sep 17 00:00:00 2001
From: Felipe Balbi <[email protected]>
Date: Tue, 3 Jan 2017 18:28:53 +0200
Subject: 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.
--- drivers/usb/host/xhci-ring.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-)
drivers/usb/host/xhci-ring.c +21 -12
@@ -1971,8 +1971,9 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,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 ;
@@ -1982,14 +1983,11 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, 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 ; }
@@ -1999,15 +1997,25 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, *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 :
@@ -2019,7 +2027,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,/* 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 ;
@@ -2027,14 +2035,15 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, }/* 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" );
Signature · 1 line
2.11.0