Hi,

Felipe Balbi <felipe.ba...@linux.intel.com> writes:
> 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_lenght 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.
>
> Signed-off-by: Felipe Balbi <felipe.ba...@linux.intel.com>
> ---
>  drivers/usb/host/xhci-ring.c | 30 ++++++++++++++++++------------
>  1 file changed, 18 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
> index 76402b44f832..70067b81cc8f 100644
> --- a/drivers/usb/host/xhci-ring.c
> +++ b/drivers/usb/host/xhci-ring.c
> @@ -1939,8 +1939,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;
> @@ -1950,14 +1951,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;
>               }
> @@ -1967,15 +1965,23 @@ 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)
>                       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:

there's a detail to fix here. Data stage can be composed of several
TRBs, in that case we will have one TRB_DATA and N TRB_NORMAL, so we
need to handle that too.

I'll update this patch (and consequently all other patches I've written)
and resend everything as a single series. Before doing that, however,
I'll wait a few more days for comments on any of the patches I've sent.

-- 
balbi

Attachment: signature.asc
Description: PGP signature

Reply via email to