On Mon, Jan 02, 2012 at 01:35:41PM +0200, Felipe Balbi wrote: > According to USB 3.0 Specification Table 9-22, if > bmAttributes [4:0] are set to zero, it means "no > streams supported", but the way this helper was > defined on Linux, we will *always* have one stream > which might cause several problems. Ok, the code in xhci_calculate_streams_and_bitmask() is convoluted enough that I had to make myself a chart to convince myself there was a bug. :) Bottom line, if xhci_alloc_streams() returns N, the device driver can use streams 1 to N. If the function returns zero (or an error code), that means the driver can't use streams. So we want xhci_alloc_streams() to return zero or an error code if bmAttributes is zero for any endpoint. If xhci_alloc_streams() returns 1, the driver can still try to use stream 1 (although having one stream wouldn't be too useful). driver ask num_streams ep streams max_streams num_streams' xhci_alloc_streams returns 0 1 0 1 1 -EINVAL 1 2 0 1 2 1 2 3 0 1 2 1 3 4 0 1 2 1 0 1 1 2 1 -EINVAL 1 2 1 2 3 2 2 3 1 2 3 2 3 4 1 2 3 2 xhci_calculate_streams_and_bitmask() is supposed to lower num_streams if it finds an endpoint that supports less streams than the driver requested. num_streams in this chart is the value in xhci_alloc_streams() after we add one to it for the reserved stream 0. num_streams' is the value after calling xhci_calculate_streams_and_bitmask(). Because of the bug where USB_SS_MAX_STREAMS() returns 1 for endpoints with bmAttributes set to zero, we're telling the driver it can use stream 1 if it requested more than one stream, and that's not right. But with your patch, the chart will look like this instead: driver ask num_streams ep streams max_streams num_streams' xhci_alloc_streams returns 0 1 0 0 1 -EINVAL 1 2 0 0 1 -EINVAL 2 3 0 0 1 -EINVAL 3 4 0 0 1 -EINVAL 0 1 1 2 1 -EINVAL 1 2 1 2 2 1 2 3 1 2 2 1 3 4 1 2 2 1 Which is what we want. So, yes, your patch is necessary for xHCI, and should be backported to stable kernels. Sorry for the long explanation, it's Monday and my brain is not working unless I write shit down. > For example on DWC3, we would tell the controller > endpoint has streams enabled and yet start transfers > with Stream ID set to 0, which would goof up the host > side. > > While doing that, convert the macro to an inline > function due to the different checks we now need. > > Cc: stable@xxxxxxxxxxxxxxx > Cc: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx> Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx> > Signed-off-by: Felipe Balbi <balbi@xxxxxx> > --- > > this should be applied as far back as 2.6.35 (when the > commit was first introduced), but of course the DWC3 part > will only apply to 3.2-stable. You can safely ignore the > DWC3 part for anything older than 3.2-stable and, hopefully, > the patch should still apply. > > Changes from v2: > > - Fix typo on commit log (s/enable/enabled) > > drivers/usb/dwc3/gadget.c | 3 +-- > drivers/usb/host/xhci.c | 3 +-- > include/linux/usb/ch9.h | 20 +++++++++++++++++++- > 3 files changed, 21 insertions(+), 5 deletions(-) > > diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c > index 12975ac..76036d0 100644 > --- a/drivers/usb/dwc3/gadget.c > +++ b/drivers/usb/dwc3/gadget.c > @@ -234,8 +234,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, > params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN > | DWC3_DEPCFG_XFER_NOT_READY_EN; > > - if (comp_desc && USB_SS_MAX_STREAMS(comp_desc->bmAttributes) > - && usb_endpoint_xfer_bulk(desc)) { > + if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) { > params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE > | DWC3_DEPCFG_STREAM_EVENT_EN; > dep->stream_capable = true; > diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c > index a1afb7c..b0d9ac6 100644 > --- a/drivers/usb/host/xhci.c > +++ b/drivers/usb/host/xhci.c > @@ -2796,8 +2796,7 @@ static int xhci_calculate_streams_and_bitmask(struct xhci_hcd *xhci, > if (ret < 0) > return ret; > > - max_streams = USB_SS_MAX_STREAMS( > - eps[i]->ss_ep_comp.bmAttributes); > + max_streams = usb_ss_max_streams(&eps[i]->ss_ep_comp); > if (max_streams < (*num_streams - 1)) { > xhci_dbg(xhci, "Ep 0x%x only supports %u stream IDs.\n", > eps[i]->desc.bEndpointAddress, > diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h > index d5da6c6..61b2905 100644 > --- a/include/linux/usb/ch9.h > +++ b/include/linux/usb/ch9.h > @@ -605,8 +605,26 @@ struct usb_ss_ep_comp_descriptor { > } __attribute__ ((packed)); > > #define USB_DT_SS_EP_COMP_SIZE 6 > + > /* Bits 4:0 of bmAttributes if this is a bulk endpoint */ > -#define USB_SS_MAX_STREAMS(p) (1 << ((p) & 0x1f)) > +static inline int > +usb_ss_max_streams(const struct usb_ss_ep_comp_descriptor *comp) > +{ > + int max_streams; > + > + if (!comp) > + return 0; > + > + max_streams = comp->bmAttributes & 0x1f; > + > + if (!max_streams) > + return 0; > + > + max_streams = 1 << max_streams; > + > + return max_streams; > +} > + > /* Bits 1:0 of bmAttributes if this is an isoc endpoint */ > #define USB_SS_MULT(p) (1 + ((p) & 0x3)) > > -- > 1.7.8.2 > -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html