On Wed, 2014-02-05 at 22:08 +0100, Hans de Goede wrote:
> Hi Oliver,
>
> On 02/05/2014 09:13 PM, [email protected] wrote:
> > From: Oliver Neukum <[email protected]>
> >
> > 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 <[email protected]>
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 <[email protected]>
---
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