diff options
-rw-r--r-- | Documentation/usb/bulk-streams.txt | 78 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 69 | ||||
-rw-r--r-- | drivers/usb/host/xhci-pci.c | 2 | ||||
-rw-r--r-- | include/linux/usb.h | 10 | ||||
-rw-r--r-- | include/linux/usb/hcd.h | 10 |
5 files changed, 169 insertions, 0 deletions
diff --git a/Documentation/usb/bulk-streams.txt b/Documentation/usb/bulk-streams.txt new file mode 100644 index 000000000000..ffc02021863e --- /dev/null +++ b/Documentation/usb/bulk-streams.txt | |||
@@ -0,0 +1,78 @@ | |||
1 | Background | ||
2 | ========== | ||
3 | |||
4 | Bulk endpoint streams were added in the USB 3.0 specification. Streams allow a | ||
5 | device driver to overload a bulk endpoint so that multiple transfers can be | ||
6 | queued at once. | ||
7 | |||
8 | Streams are defined in sections 4.4.6.4 and 8.12.1.4 of the Universal Serial Bus | ||
9 | 3.0 specification at http://www.usb.org/developers/docs/ The USB Attached SCSI | ||
10 | Protocol, which uses streams to queue multiple SCSI commands, can be found on | ||
11 | the T10 website (http://t10.org/). | ||
12 | |||
13 | |||
14 | Device-side implications | ||
15 | ======================== | ||
16 | |||
17 | Once a buffer has been queued to a stream ring, the device is notified (through | ||
18 | an out-of-band mechanism on another endpoint) that data is ready for that stream | ||
19 | ID. The device then tells the host which "stream" it wants to start. The host | ||
20 | can also initiate a transfer on a stream without the device asking, but the | ||
21 | device can refuse that transfer. Devices can switch between streams at any | ||
22 | time. | ||
23 | |||
24 | |||
25 | Driver implications | ||
26 | =================== | ||
27 | |||
28 | int usb_alloc_streams(struct usb_interface *interface, | ||
29 | struct usb_host_endpoint **eps, unsigned int num_eps, | ||
30 | unsigned int num_streams, gfp_t mem_flags); | ||
31 | |||
32 | Device drivers will call this API to request that the host controller driver | ||
33 | allocate memory so the driver can use up to num_streams stream IDs. They must | ||
34 | pass an array of usb_host_endpoints that need to be setup with similar stream | ||
35 | IDs. This is to ensure that a UASP driver will be able to use the same stream | ||
36 | ID for the bulk IN and OUT endpoints used in a Bi-directional command sequence. | ||
37 | |||
38 | The return value is an error condition (if one of the endpoints doesn't support | ||
39 | streams, or the xHCI driver ran out of memory), or the number of streams the | ||
40 | host controller allocated for this endpoint. The xHCI host controller hardware | ||
41 | declares how many stream IDs it can support, and each bulk endpoint on a | ||
42 | SuperSpeed device will say how many stream IDs it can handle. Therefore, | ||
43 | drivers should be able to deal with being allocated less stream IDs than they | ||
44 | requested. | ||
45 | |||
46 | Do NOT call this function if you have URBs enqueued for any of the endpoints | ||
47 | passed in as arguments. Do not call this function to request less than two | ||
48 | streams. | ||
49 | |||
50 | Drivers will only be allowed to call this API once for the same endpoint | ||
51 | without calling usb_free_streams(). This is a simplification for the xHCI host | ||
52 | controller driver, and may change in the future. | ||
53 | |||
54 | |||
55 | Picking new Stream IDs to use | ||
56 | ============================ | ||
57 | |||
58 | Stream ID 0 is reserved, and should not be used to communicate with devices. If | ||
59 | usb_alloc_streams() returns with a value of N, you may use streams 1 though N. | ||
60 | To queue an URB for a specific stream, set the urb->stream_id value. If the | ||
61 | endpoint does not support streams, an error will be returned. | ||
62 | |||
63 | Note that new API to choose the next stream ID will have to be added if the xHCI | ||
64 | driver supports secondary stream IDs. | ||
65 | |||
66 | |||
67 | Clean up | ||
68 | ======== | ||
69 | |||
70 | If a driver wishes to stop using streams to communicate with the device, it | ||
71 | should call | ||
72 | |||
73 | void usb_free_streams(struct usb_interface *interface, | ||
74 | struct usb_host_endpoint **eps, unsigned int num_eps, | ||
75 | gfp_t mem_flags); | ||
76 | |||
77 | All stream IDs will be deallocated when the driver releases the interface, to | ||
78 | ensure that drivers that don't support streams will be able to use the endpoint. | ||
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 6a05e6934455..3aaee2811f01 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c | |||
@@ -1814,6 +1814,75 @@ void usb_hcd_reset_endpoint(struct usb_device *udev, | |||
1814 | } | 1814 | } |
1815 | } | 1815 | } |
1816 | 1816 | ||
1817 | /** | ||
1818 | * usb_alloc_streams - allocate bulk endpoint stream IDs. | ||
1819 | * @interface: alternate setting that includes all endpoints. | ||
1820 | * @eps: array of endpoints that need streams. | ||
1821 | * @num_eps: number of endpoints in the array. | ||
1822 | * @num_streams: number of streams to allocate. | ||
1823 | * @mem_flags: flags hcd should use to allocate memory. | ||
1824 | * | ||
1825 | * Sets up a group of bulk endpoints to have num_streams stream IDs available. | ||
1826 | * Drivers may queue multiple transfers to different stream IDs, which may | ||
1827 | * complete in a different order than they were queued. | ||
1828 | */ | ||
1829 | int usb_alloc_streams(struct usb_interface *interface, | ||
1830 | struct usb_host_endpoint **eps, unsigned int num_eps, | ||
1831 | unsigned int num_streams, gfp_t mem_flags) | ||
1832 | { | ||
1833 | struct usb_hcd *hcd; | ||
1834 | struct usb_device *dev; | ||
1835 | int i; | ||
1836 | |||
1837 | dev = interface_to_usbdev(interface); | ||
1838 | hcd = bus_to_hcd(dev->bus); | ||
1839 | if (!hcd->driver->alloc_streams || !hcd->driver->free_streams) | ||
1840 | return -EINVAL; | ||
1841 | if (dev->speed != USB_SPEED_SUPER) | ||
1842 | return -EINVAL; | ||
1843 | |||
1844 | /* Streams only apply to bulk endpoints. */ | ||
1845 | for (i = 0; i < num_eps; i++) | ||
1846 | if (!usb_endpoint_xfer_bulk(&eps[i]->desc)) | ||
1847 | return -EINVAL; | ||
1848 | |||
1849 | return hcd->driver->alloc_streams(hcd, dev, eps, num_eps, | ||
1850 | num_streams, mem_flags); | ||
1851 | } | ||
1852 | EXPORT_SYMBOL_GPL(usb_alloc_streams); | ||
1853 | |||
1854 | /** | ||
1855 | * usb_free_streams - free bulk endpoint stream IDs. | ||
1856 | * @interface: alternate setting that includes all endpoints. | ||
1857 | * @eps: array of endpoints to remove streams from. | ||
1858 | * @num_eps: number of endpoints in the array. | ||
1859 | * @mem_flags: flags hcd should use to allocate memory. | ||
1860 | * | ||
1861 | * Reverts a group of bulk endpoints back to not using stream IDs. | ||
1862 | * Can fail if we are given bad arguments, or HCD is broken. | ||
1863 | */ | ||
1864 | void usb_free_streams(struct usb_interface *interface, | ||
1865 | struct usb_host_endpoint **eps, unsigned int num_eps, | ||
1866 | gfp_t mem_flags) | ||
1867 | { | ||
1868 | struct usb_hcd *hcd; | ||
1869 | struct usb_device *dev; | ||
1870 | int i; | ||
1871 | |||
1872 | dev = interface_to_usbdev(interface); | ||
1873 | hcd = bus_to_hcd(dev->bus); | ||
1874 | if (dev->speed != USB_SPEED_SUPER) | ||
1875 | return; | ||
1876 | |||
1877 | /* Streams only apply to bulk endpoints. */ | ||
1878 | for (i = 0; i < num_eps; i++) | ||
1879 | if (!usb_endpoint_xfer_bulk(&eps[i]->desc)) | ||
1880 | return; | ||
1881 | |||
1882 | hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags); | ||
1883 | } | ||
1884 | EXPORT_SYMBOL_GPL(usb_free_streams); | ||
1885 | |||
1817 | /* Protect against drivers that try to unlink URBs after the device | 1886 | /* Protect against drivers that try to unlink URBs after the device |
1818 | * is gone, by waiting until all unlinks for @udev are finished. | 1887 | * is gone, by waiting until all unlinks for @udev are finished. |
1819 | * Since we don't currently track URBs by device, simply wait until | 1888 | * Since we don't currently track URBs by device, simply wait until |
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 98a73cd20cc6..d295bbc15eb7 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c | |||
@@ -132,6 +132,8 @@ static const struct hc_driver xhci_pci_hc_driver = { | |||
132 | .urb_dequeue = xhci_urb_dequeue, | 132 | .urb_dequeue = xhci_urb_dequeue, |
133 | .alloc_dev = xhci_alloc_dev, | 133 | .alloc_dev = xhci_alloc_dev, |
134 | .free_dev = xhci_free_dev, | 134 | .free_dev = xhci_free_dev, |
135 | .alloc_streams = xhci_alloc_streams, | ||
136 | .free_streams = xhci_free_streams, | ||
135 | .add_endpoint = xhci_add_endpoint, | 137 | .add_endpoint = xhci_add_endpoint, |
136 | .drop_endpoint = xhci_drop_endpoint, | 138 | .drop_endpoint = xhci_drop_endpoint, |
137 | .endpoint_reset = xhci_endpoint_reset, | 139 | .endpoint_reset = xhci_endpoint_reset, |
diff --git a/include/linux/usb.h b/include/linux/usb.h index 191af498c4f5..1ea25377ca0d 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -570,6 +570,16 @@ static inline void usb_mark_last_busy(struct usb_device *udev) | |||
570 | /* for drivers using iso endpoints */ | 570 | /* for drivers using iso endpoints */ |
571 | extern int usb_get_current_frame_number(struct usb_device *usb_dev); | 571 | extern int usb_get_current_frame_number(struct usb_device *usb_dev); |
572 | 572 | ||
573 | /* Sets up a group of bulk endpoints to support multiple stream IDs. */ | ||
574 | extern int usb_alloc_streams(struct usb_interface *interface, | ||
575 | struct usb_host_endpoint **eps, unsigned int num_eps, | ||
576 | unsigned int num_streams, gfp_t mem_flags); | ||
577 | |||
578 | /* Reverts a group of bulk endpoints back to not using stream IDs. */ | ||
579 | extern void usb_free_streams(struct usb_interface *interface, | ||
580 | struct usb_host_endpoint **eps, unsigned int num_eps, | ||
581 | gfp_t mem_flags); | ||
582 | |||
573 | /* used these for multi-interface device registration */ | 583 | /* used these for multi-interface device registration */ |
574 | extern int usb_driver_claim_interface(struct usb_driver *driver, | 584 | extern int usb_driver_claim_interface(struct usb_driver *driver, |
575 | struct usb_interface *iface, void *priv); | 585 | struct usb_interface *iface, void *priv); |
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index d268415b7a40..aca73a5c3af7 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h | |||
@@ -250,6 +250,16 @@ struct hc_driver { | |||
250 | int (*alloc_dev)(struct usb_hcd *, struct usb_device *); | 250 | int (*alloc_dev)(struct usb_hcd *, struct usb_device *); |
251 | /* Called by usb_disconnect to free HC device structures */ | 251 | /* Called by usb_disconnect to free HC device structures */ |
252 | void (*free_dev)(struct usb_hcd *, struct usb_device *); | 252 | void (*free_dev)(struct usb_hcd *, struct usb_device *); |
253 | /* Change a group of bulk endpoints to support multiple stream IDs */ | ||
254 | int (*alloc_streams)(struct usb_hcd *hcd, struct usb_device *udev, | ||
255 | struct usb_host_endpoint **eps, unsigned int num_eps, | ||
256 | unsigned int num_streams, gfp_t mem_flags); | ||
257 | /* Reverts a group of bulk endpoints back to not using stream IDs. | ||
258 | * Can fail if we run out of memory. | ||
259 | */ | ||
260 | int (*free_streams)(struct usb_hcd *hcd, struct usb_device *udev, | ||
261 | struct usb_host_endpoint **eps, unsigned int num_eps, | ||
262 | gfp_t mem_flags); | ||
253 | 263 | ||
254 | /* Bandwidth computation functions */ | 264 | /* Bandwidth computation functions */ |
255 | /* Note that add_endpoint() can only be called once per endpoint before | 265 | /* Note that add_endpoint() can only be called once per endpoint before |