[PATCH 5/5] USB: Support for allocating USB 3.0 streams.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Bulk endpoint streams were added in the USB 3.0 specification.  Streams
allow a device driver to overload a bulk endpoint so that multiple
transfers can be queued at once.

The device then decides which transfer it wants to work on first, and can
queue part of a transfer before it switches to a new stream.  All this
switching is invisible to the device driver, which just gets a completion
for the URB.  Drivers that use streams must be able to handle URBs
completing in a different order than they were submitted to the endpoint.

This requires adding new API to set up xHCI data structures to support
multiple queues ("stream rings") per endpoint.  Drivers will allocate a
number of stream IDs before enqueueing URBs to the bulk endpoints of the
device, and free the stream IDs in their disconnect function.  See
Documentation/usb/bulk-streams.txt for details.

The new mass storage device class, USB Attached SCSI Protocol (UASP), uses
these streams API.

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
---
 drivers/usb/core/driver.c   |   54 +++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/core/hcd.h      |   10 ++++++++
 drivers/usb/host/xhci-pci.c |    2 +
 include/linux/usb.h         |   10 ++++++++
 4 files changed, 76 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index f3c2338..cc599da 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -497,6 +497,60 @@ void usb_driver_release_interface(struct usb_driver *driver,
 }
 EXPORT_SYMBOL_GPL(usb_driver_release_interface);
 
+/* Sets up a group of bulk endpoints to have num_streams stream IDs available.
+ * Drivers may queue multiple transfers to different stream IDs, which may
+ * complete in a different order than they were queued.
+ */
+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)
+{
+	struct usb_hcd *hcd;
+	struct usb_device *dev;
+	int i;
+
+	dev = interface_to_usbdev(interface);
+	hcd = bus_to_hcd(dev->bus);
+	if (!hcd->driver->alloc_streams || !hcd->driver->free_streams)
+		return -EINVAL;
+	if (dev->speed != USB_SPEED_SUPER)
+		return -EINVAL;
+
+	/* Streams only apply to bulk endpoints. */
+	for (i = 0; i < num_eps; i++)
+		if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
+			return -EINVAL;
+
+	return hcd->driver->alloc_streams(hcd, dev, eps, num_eps,
+			num_streams, mem_flags);
+}
+EXPORT_SYMBOL_GPL(usb_alloc_streams);
+
+/* Reverts a group of bulk endpoints back to not using stream IDs.
+ * Can fail if we are given bad arguments.
+ */
+void usb_free_streams(struct usb_interface *interface,
+		struct usb_host_endpoint **eps, unsigned int num_eps,
+		gfp_t mem_flags)
+{
+	struct usb_hcd *hcd;
+	struct usb_device *dev;
+	int i;
+
+	dev = interface_to_usbdev(interface);
+	hcd = bus_to_hcd(dev->bus);
+	if (dev->speed != USB_SPEED_SUPER)
+		return;
+
+	/* Streams only apply to bulk endpoints. */
+	for (i = 0; i < num_eps; i++)
+		if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
+			return;
+
+	hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
+}
+EXPORT_SYMBOL_GPL(usb_free_streams);
+
 /* returns 0 if no match, 1 if match */
 int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
 {
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index a3cdb09..d8fca36 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -250,6 +250,16 @@ 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 *);
+	/* 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,
+		unsigned int num_streams, gfp_t mem_flags);
+	/* Reverts a group of bulk endpoints back to not using stream IDs.
+	 * Can fail if we run out of memory.
+	 */
+	int	(*free_streams)(struct usb_hcd *hcd, struct usb_device *udev,
+		struct usb_host_endpoint **eps, unsigned int num_eps,
+		gfp_t mem_flags);
 
 	/* Bandwidth computation functions */
 	/* Note that add_endpoint() can only be called once per endpoint before
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 417d37a..d4db3d9 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 = {
 	.urb_dequeue =		xhci_urb_dequeue,
 	.alloc_dev =		xhci_alloc_dev,
 	.free_dev =		xhci_free_dev,
+	.alloc_streams =	xhci_alloc_streams,
+	.free_streams =		xhci_free_streams,
 	.add_endpoint =		xhci_add_endpoint,
 	.drop_endpoint =	xhci_drop_endpoint,
 	.endpoint_reset =	xhci_endpoint_reset,
diff --git a/include/linux/usb.h b/include/linux/usb.h
index b6a1fb6..406a47d 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -572,6 +572,16 @@ static inline void usb_mark_last_busy(struct usb_device *udev)
 /* for drivers using iso endpoints */
 extern int usb_get_current_frame_number(struct usb_device *usb_dev);
 
+/* Sets up a group of bulk endpoints to support multiple stream IDs. */
+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);
+
+/* Reverts a group of bulk endpoints back to not using stream IDs. */
+extern void usb_free_streams(struct usb_interface *interface,
+		struct usb_host_endpoint **eps, unsigned int num_eps,
+		gfp_t mem_flags);
+
 /* used these for multi-interface device registration */
 extern int usb_driver_claim_interface(struct usb_driver *driver,
 			struct usb_interface *iface, void *priv);
-- 
1.6.3.3

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux