forked from Minki/linux
14aec58932
On some older XHCIs streams are not supported and the UAS driver will fail at probe time. For those devices storage should try to bind to UAS devices. This patch adds a flag for stream support to HCDs and evaluates it. [Note: Sarah fixed a bug where the USB 2.0 root hub, not USB 3.0 root hub would get marked as being able to support streams.] Signed-off-by: Oliver Neukum <oliver@neukum.org> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Acked-by: Hans de Goede <hdegoede@redhat.com>
97 lines
2.3 KiB
C
97 lines
2.3 KiB
C
#include <linux/usb.h>
|
|
#include <linux/usb/hcd.h>
|
|
#include "usb.h"
|
|
|
|
static int uas_is_interface(struct usb_host_interface *intf)
|
|
{
|
|
return (intf->desc.bInterfaceClass == USB_CLASS_MASS_STORAGE &&
|
|
intf->desc.bInterfaceSubClass == USB_SC_SCSI &&
|
|
intf->desc.bInterfaceProtocol == USB_PR_UAS);
|
|
}
|
|
|
|
static int uas_isnt_supported(struct usb_device *udev)
|
|
{
|
|
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
|
|
|
dev_warn(&udev->dev, "The driver for the USB controller %s does not "
|
|
"support scatter-gather which is\n",
|
|
hcd->driver->description);
|
|
dev_warn(&udev->dev, "required by the UAS driver. Please try an"
|
|
"alternative USB controller if you wish to use UAS.\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
static int uas_find_uas_alt_setting(struct usb_interface *intf)
|
|
{
|
|
int i;
|
|
struct usb_device *udev = interface_to_usbdev(intf);
|
|
int sg_supported = udev->bus->sg_tablesize != 0;
|
|
|
|
for (i = 0; i < intf->num_altsetting; i++) {
|
|
struct usb_host_interface *alt = &intf->altsetting[i];
|
|
|
|
if (uas_is_interface(alt)) {
|
|
if (!sg_supported)
|
|
return uas_isnt_supported(udev);
|
|
return alt->desc.bAlternateSetting;
|
|
}
|
|
}
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
static int uas_find_endpoints(struct usb_host_interface *alt,
|
|
struct usb_host_endpoint *eps[])
|
|
{
|
|
struct usb_host_endpoint *endpoint = alt->endpoint;
|
|
unsigned i, n_endpoints = alt->desc.bNumEndpoints;
|
|
|
|
for (i = 0; i < n_endpoints; i++) {
|
|
unsigned char *extra = endpoint[i].extra;
|
|
int len = endpoint[i].extralen;
|
|
while (len >= 3) {
|
|
if (extra[1] == USB_DT_PIPE_USAGE) {
|
|
unsigned pipe_id = extra[2];
|
|
if (pipe_id > 0 && pipe_id < 5)
|
|
eps[pipe_id - 1] = &endpoint[i];
|
|
break;
|
|
}
|
|
len -= extra[0];
|
|
extra += extra[0];
|
|
}
|
|
}
|
|
|
|
if (!eps[0] || !eps[1] || !eps[2] || !eps[3])
|
|
return -ENODEV;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int uas_use_uas_driver(struct usb_interface *intf,
|
|
const struct usb_device_id *id)
|
|
{
|
|
struct usb_host_endpoint *eps[4] = { };
|
|
struct usb_device *udev = interface_to_usbdev(intf);
|
|
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
|
unsigned long flags = id->driver_info;
|
|
int r, alt;
|
|
|
|
usb_stor_adjust_quirks(udev, &flags);
|
|
|
|
if (flags & US_FL_IGNORE_UAS)
|
|
return 0;
|
|
|
|
if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams)
|
|
return 0;
|
|
|
|
alt = uas_find_uas_alt_setting(intf);
|
|
if (alt < 0)
|
|
return 0;
|
|
|
|
r = uas_find_endpoints(&intf->altsetting[alt], eps);
|
|
if (r < 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|