On Wed, 2014-02-05 at 22:08 +0100, Hans de Goede wrote: > Hi Oliver, > > On 02/05/2014 09:13 PM, oliver@xxxxxxxxxx wrote: > > From: Oliver Neukum <oneukum@xxxxxxx> > > > > uas_probe() calls usb_alloc_streams(). That can fail on XHCI > > with -ENOSYS if the controller doesn't support streams. In that > > case devices should be handed over to storage. Thus the driver > > needs to return -ENODEV so that the driver core will give other > > drivers a chance. > > I'm afraid that that won't work, it won't change the result of > uas_use_uas_driver() so usb-storage still will fail to bind. > > I'm not sure which version you're looking at, but the new > re-enabled uas code as it will be going upstream for 3.15 is here: > http://git.linuxtv.org/hgoede/gspca.git/shortlog/refs/heads/usb-next-for-sarah > > For the above specifically see: > http://git.linuxtv.org/hgoede/gspca.git/commitdiff/dda9e7db4e293defeff4956018183e06749528dc > > So to fix this we would need to export if an hcd supports streams > with some hcd flag / property and then as_use_uas_driver() can > check this flag (in combination with connection speed). Hi, something like this? Regards Oliver
>From c04c3cf3a15162bae22e8dcc74dbf9449e822b56 Mon Sep 17 00:00:00 2001 From: Oliver Neukum <oneukum@xxxxxxx> Date: Fri, 7 Feb 2014 10:57:44 +0100 Subject: [PATCH] USB:query streams support This adds a method to query HCDs for their support for streams and uses this in the storage driver to take devices which UAS rejects due to a lack of streams support. This allows UAS devices to work as storage devices with old XHCI chips. Signed-off-by: Oliver Neukum <oliver@xxxxxxxxxx> --- drivers/usb/core/hcd.c | 14 ++++++++++++++ drivers/usb/host/xhci-pci.c | 1 + drivers/usb/host/xhci-plat.c | 1 + drivers/usb/host/xhci.c | 7 +++++++ drivers/usb/host/xhci.h | 1 + drivers/usb/storage/uas-detect.h | 13 +++++++++++++ include/linux/usb.h | 4 ++++ include/linux/usb/hcd.h | 2 ++ 8 files changed, 43 insertions(+) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9c4e292..efc16f0 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2027,6 +2027,20 @@ void usb_hcd_reset_endpoint(struct usb_device *udev, usb_settoggle(udev, epnum, !is_out, 0); } } +/** + * usb_query_stream_support - query whether a HC supports streams + * @hcd: HC to query + * + * Return: 1 for support; 0 for no support + */ +int usb_query_stream_support(struct usb_hcd *hcd) +{ + if (!hcd->driver->query_streams) + return 0; + else + return hcd->driver->query_streams(hcd); +} +EXPORT_SYMBOL_GPL(usb_query_stream_support); /** * usb_alloc_streams - allocate bulk endpoint stream IDs. diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 04f986d..f9f770d 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -334,6 +334,7 @@ static const struct hc_driver xhci_pci_hc_driver = { .alloc_dev = xhci_alloc_dev, .free_dev = xhci_free_dev, .alloc_streams = xhci_alloc_streams, + .query_streams = xhci_query_streams, .free_streams = xhci_free_streams, .add_endpoint = xhci_add_endpoint, .drop_endpoint = xhci_drop_endpoint, diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 8abda5c..3d96517 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -62,6 +62,7 @@ static const struct hc_driver xhci_plat_xhci_driver = { .alloc_dev = xhci_alloc_dev, .free_dev = xhci_free_dev, .alloc_streams = xhci_alloc_streams, + .query_streams = xhci_query_streams, .free_streams = xhci_free_streams, .add_endpoint = xhci_add_endpoint, .drop_endpoint = xhci_drop_endpoint, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 1f15830..f77b29e 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3267,6 +3267,13 @@ cleanup: return -ENOMEM; } +int xhci_query_streams(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + return HCC_MAX_PSA(xhci->hcc_params) >= 4; +} + /* Transition the endpoint from using streams to being a "normal" endpoint * without streams. * diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 08d669c..8c449e7 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1707,6 +1707,7 @@ void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci, struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, unsigned int num_stream_ctxs, unsigned int num_streams, gfp_t flags); +int xhci_query_streams(struct usb_hcd *hcd); void xhci_free_stream_info(struct xhci_hcd *xhci, struct xhci_stream_info *stream_info); void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci, diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index b8a02e1..73a4478 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -9,6 +9,13 @@ static int uas_is_interface(struct usb_host_interface *intf) intf->desc.bInterfaceProtocol == USB_PR_UAS); } +static int uas_find_streams(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + return usb_query_stream_support(hcd) ? 0 : -ENODEV; +} + static int uas_isnt_supported(struct usb_device *udev) { struct usb_hcd *hcd = bus_to_hcd(udev->bus); @@ -80,6 +87,12 @@ static int uas_use_uas_driver(struct usb_interface *intf, if (flags & US_FL_IGNORE_UAS) return 0; + if (udev->speed >= USB_SPEED_SUPER) { + r = uas_find_streams(udev); + if (r < 0) + return 0; + } + alt = uas_find_uas_alt_setting(intf); if (alt < 0) return 0; diff --git a/include/linux/usb.h b/include/linux/usb.h index e644923..f435113 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -25,6 +25,7 @@ struct usb_device; struct usb_driver; struct wusb_dev; +struct usb_hcd; /*-------------------------------------------------------------------------*/ @@ -707,6 +708,9 @@ extern int usb_alloc_streams(struct usb_interface *interface, struct usb_host_endpoint **eps, unsigned int num_eps, unsigned int num_streams, gfp_t mem_flags); +/* Can a HC support streams for a given device */ +extern int usb_query_stream_support(struct usb_hcd *hcd); + /* Reverts a group of bulk endpoints back to not using stream IDs. */ extern int usb_free_streams(struct usb_interface *interface, struct usb_host_endpoint **eps, unsigned int num_eps, diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index efe8d8a..6a12152 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -310,6 +310,8 @@ struct hc_driver { int (*alloc_dev)(struct usb_hcd *, struct usb_device *); /* Called by usb_disconnect to free HC device structures */ void (*free_dev)(struct usb_hcd *, struct usb_device *); + /* query for stream support */ + int (*query_streams) (struct usb_hcd *hcd); /* Change a group of bulk endpoints to support multiple stream IDs */ int (*alloc_streams)(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, -- 1.8.4