]> git.karo-electronics.de Git - linux-beck.git/blob - drivers/usb/storage/uas-detect.h
Merge tag 'for-linus-20140716' of git://git.infradead.org/linux-mtd
[linux-beck.git] / drivers / usb / storage / uas-detect.h
1 #include <linux/usb.h>
2 #include <linux/usb/hcd.h>
3 #include "usb.h"
4
5 static int uas_is_interface(struct usb_host_interface *intf)
6 {
7         return (intf->desc.bInterfaceClass == USB_CLASS_MASS_STORAGE &&
8                 intf->desc.bInterfaceSubClass == USB_SC_SCSI &&
9                 intf->desc.bInterfaceProtocol == USB_PR_UAS);
10 }
11
12 static int uas_isnt_supported(struct usb_device *udev)
13 {
14         struct usb_hcd *hcd = bus_to_hcd(udev->bus);
15
16         dev_warn(&udev->dev, "The driver for the USB controller %s does not "
17                         "support scatter-gather which is\n",
18                         hcd->driver->description);
19         dev_warn(&udev->dev, "required by the UAS driver. Please try an"
20                         "alternative USB controller if you wish to use UAS.\n");
21         return -ENODEV;
22 }
23
24 static int uas_find_uas_alt_setting(struct usb_interface *intf)
25 {
26         int i;
27         struct usb_device *udev = interface_to_usbdev(intf);
28         int sg_supported = udev->bus->sg_tablesize != 0;
29
30         for (i = 0; i < intf->num_altsetting; i++) {
31                 struct usb_host_interface *alt = &intf->altsetting[i];
32
33                 if (uas_is_interface(alt)) {
34                         if (!sg_supported)
35                                 return uas_isnt_supported(udev);
36                         return alt->desc.bAlternateSetting;
37                 }
38         }
39
40         return -ENODEV;
41 }
42
43 static int uas_find_endpoints(struct usb_host_interface *alt,
44                               struct usb_host_endpoint *eps[])
45 {
46         struct usb_host_endpoint *endpoint = alt->endpoint;
47         unsigned i, n_endpoints = alt->desc.bNumEndpoints;
48
49         for (i = 0; i < n_endpoints; i++) {
50                 unsigned char *extra = endpoint[i].extra;
51                 int len = endpoint[i].extralen;
52                 while (len >= 3) {
53                         if (extra[1] == USB_DT_PIPE_USAGE) {
54                                 unsigned pipe_id = extra[2];
55                                 if (pipe_id > 0 && pipe_id < 5)
56                                         eps[pipe_id - 1] = &endpoint[i];
57                                 break;
58                         }
59                         len -= extra[0];
60                         extra += extra[0];
61                 }
62         }
63
64         if (!eps[0] || !eps[1] || !eps[2] || !eps[3])
65                 return -ENODEV;
66
67         return 0;
68 }
69
70 static int uas_use_uas_driver(struct usb_interface *intf,
71                               const struct usb_device_id *id)
72 {
73         struct usb_host_endpoint *eps[4] = { };
74         struct usb_device *udev = interface_to_usbdev(intf);
75         struct usb_hcd *hcd = bus_to_hcd(udev->bus);
76         unsigned long flags = id->driver_info;
77         int r, alt;
78
79         usb_stor_adjust_quirks(udev, &flags);
80
81         if (flags & US_FL_IGNORE_UAS)
82                 return 0;
83
84         if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams)
85                 return 0;
86
87         alt = uas_find_uas_alt_setting(intf);
88         if (alt < 0)
89                 return 0;
90
91         r = uas_find_endpoints(&intf->altsetting[alt], eps);
92         if (r < 0)
93                 return 0;
94
95         return 1;
96 }