Index: uspace/drv/bus/usb/xhci/Makefile
===================================================================
--- uspace/drv/bus/usb/xhci/Makefile	(revision 7d1dd2b55c7a5309651626ded2ab97671e17687a)
+++ uspace/drv/bus/usb/xhci/Makefile	(revision 47e9494c5eb0d77e3f73d832708572c5ada9398e)
@@ -43,4 +43,5 @@
 	rh.c \
 	scratchpad.c \
+	streams.c \
 	transfers.c \
 	trb_ring.c
Index: uspace/drv/bus/usb/xhci/endpoint.c
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.c	(revision 7d1dd2b55c7a5309651626ded2ab97671e17687a)
+++ uspace/drv/bus/usb/xhci/endpoint.c	(revision 47e9494c5eb0d77e3f73d832708572c5ada9398e)
@@ -44,4 +44,5 @@
 #include "commands.h"
 #include "endpoint.h"
+#include "streams.h"
 
 static int alloc_transfer_ds(xhci_endpoint_t *);
@@ -131,5 +132,5 @@
  * @return EP_TYPE_[CONTROL|ISOCH|BULK|INTERRUPT]_[IN|OUT]
  */
-static int xhci_endpoint_type(xhci_endpoint_t *ep)
+int xhci_endpoint_type(xhci_endpoint_t *ep)
 {
 	const bool in = ep->base.direction == USB_DIRECTION_IN;
@@ -153,117 +154,4 @@
 
 	return EP_TYPE_INVALID;
-}
-
-/**
- * Test whether an XHCI endpoint uses streams.
- * @param[in] xhci_ep XHCI endpoint to query.
- *
- * @return True if the endpoint uses streams.
- */
-static bool endpoint_using_streams(xhci_endpoint_t *xhci_ep)
-{
-	return xhci_ep->primary_stream_ctx_array != NULL;
-}
-
-// static bool primary_stream_ctx_has_secondary_array(xhci_stream_ctx_t *primary_ctx) {
-// 	/* Section 6.2.4.1, SCT values */
-// 	return XHCI_STREAM_SCT(*primary_ctx) >= 2;
-// }
-//
-// static size_t secondary_stream_ctx_array_size(xhci_stream_ctx_t *primary_ctx) {
-// 	if (XHCI_STREAM_SCT(*primary_ctx) < 2) return 0;
-// 	return 2 << XHCI_STREAM_SCT(*primary_ctx);
-// }
-
-/** Initialize primary streams of XHCI bulk endpoint.
- * @param[in] hc Host controller of the endpoint.
- * @param[in] xhci_epi XHCI bulk endpoint to use.
- * @param[in] count Number of primary streams to initialize.
- */
-static void initialize_primary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep, unsigned count) {
-	for (size_t index = 0; index < count; ++index) {
-		xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[index];
-		xhci_trb_ring_t *ring = &xhci_ep->primary_stream_rings[index];
-
-		/* Init and register TRB ring for every primary stream */
-		xhci_trb_ring_init(ring); // FIXME: Not checking error code?
-		XHCI_STREAM_DEQ_PTR_SET(*ctx, ring->dequeue);
-
-		/* Set to linear stream array */
-		XHCI_STREAM_SCT_SET(*ctx, 1);
-	}
-}
-
-/** Configure XHCI bulk endpoint's stream context.
- * @param[in] xhci_ep Associated XHCI bulk endpoint.
- * @param[in] ctx Endpoint context to configure.
- * @param[in] pstreams The value of MaxPStreams.
- */
-static void setup_stream_context(xhci_endpoint_t *xhci_ep, xhci_ep_ctx_t *ctx, unsigned pstreams) {
-	XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(xhci_ep));
-	XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, xhci_ep->base.max_packet_size);
-	XHCI_EP_MAX_BURST_SIZE_SET(*ctx, xhci_ep->max_burst - 1);
-	XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
-
-	XHCI_EP_MAX_P_STREAMS_SET(*ctx, pstreams);
-	XHCI_EP_TR_DPTR_SET(*ctx, xhci_ep->primary_stream_ctx_dma.phys);
-	// TODO: set HID?
-	XHCI_EP_LSA_SET(*ctx, 1);
-}
-
-/** TODO document this
- */
-int xhci_endpoint_request_streams(xhci_hc_t *hc, xhci_device_t *dev, xhci_endpoint_t *xhci_ep, unsigned count) {
-	if (xhci_ep->base.transfer_type != USB_TRANSFER_BULK
-		|| dev->base.speed != USB_SPEED_SUPER) {
-		usb_log_error("Streams are only supported by superspeed bulk endpoints.");
-		return EINVAL;
-	}
-
-	if (xhci_ep->max_streams == 1) {
-		usb_log_error("Streams are not supported by endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
-		return EINVAL;
-	}
-
-	uint8_t max_psa_size = 2 << XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PSA_SIZE);
-	if (count > max_psa_size) {
-		// FIXME: We don't support secondary stream arrays yet, so we just give up for this
-		return ENOTSUP;
-	}
-
-	if (count > xhci_ep->max_streams) {
-		usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
-			XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
-		return EINVAL;
-	}
-
-	if (count <= 1024) {
-		usb_log_debug2("Allocating primary stream context array of size %u for endpoint " XHCI_EP_FMT,
-			count, XHCI_EP_ARGS(*xhci_ep));
-		if ((dma_buffer_alloc(&xhci_ep->primary_stream_ctx_dma, count * sizeof(xhci_stream_ctx_t))))
-			return ENOMEM;
-		xhci_ep->primary_stream_ctx_array = xhci_ep->primary_stream_ctx_dma.virt;
-
-		xhci_ep->primary_stream_rings = calloc(count, sizeof(xhci_trb_ring_t));
-		if (!xhci_ep->primary_stream_rings) {
-			dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
-			return ENOMEM;
-		}
-
-		// FIXME: count should be rounded to nearest power of 2 for xHC, workaround for now
-		count = 1024;
-		// FIXME: pstreams are "log2(count) - 1"
-		const size_t pstreams = 9;
-		xhci_ep->primary_stream_ctx_array_size = count;
-
-		memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
-		initialize_primary_streams(hc, xhci_ep, count);
-
-		xhci_ep_ctx_t ep_ctx;
-		setup_stream_context(xhci_ep, &ep_ctx, pstreams);
-		return hc_add_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep), &ep_ctx);
-	}
-	// FIXME: Complex stuff not supported yet
-	return ENOTSUP;
 }
 
@@ -300,23 +188,8 @@
 static void free_transfer_ds(xhci_endpoint_t *xhci_ep)
 {
-	if (endpoint_using_streams(xhci_ep)) {
-		usb_log_debug2("Freeing primary stream context array of endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
-
-		// maybe check if LSA, then skip?
-		// for (size_t index = 0; index < primary_stream_ctx_array_size(xhci_ep); ++index) {
-		// 	xhci_stream_ctx_t *primary_ctx = xhci_ep->primary_stream_ctx_array + index;
-		// 	if (primary_stream_ctx_has_secondary_array(primary_ctx)) {
-		// 		// uintptr_t phys = XHCI_STREAM_DEQ_PTR(*primary_ctx);
-		// 		/* size_t size = */ secondary_stream_ctx_array_size(primary_ctx);
-		// 		// TODO: somehow map the address to virtual and free the secondary array
-		// 	}
-		// }
-		for (size_t index = 0; index < xhci_ep->primary_stream_ctx_array_size; ++index) {
-			// FIXME: Get the trb ring associated with stream [index] and fini it
-		}
-		dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
+	if (xhci_ep->primary_stream_data_size) {
+		xhci_stream_free_ds(xhci_ep);
 	} else {
 		usb_log_debug2("Freeing main transfer ring of endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
-
 		xhci_trb_ring_fini(&xhci_ep->ring);
 	}
Index: uspace/drv/bus/usb/xhci/endpoint.h
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.h	(revision 7d1dd2b55c7a5309651626ded2ab97671e17687a)
+++ uspace/drv/bus/usb/xhci/endpoint.h	(revision 47e9494c5eb0d77e3f73d832708572c5ada9398e)
@@ -51,4 +51,5 @@
 typedef struct xhci_device xhci_device_t;
 typedef struct xhci_endpoint xhci_endpoint_t;
+typedef struct xhci_stream_data xhci_stream_data_t;
 typedef struct xhci_bus xhci_bus_t;
 
@@ -71,13 +72,13 @@
 	xhci_trb_ring_t ring;
 
-	/** Primary stream context array (or NULL if endpoint doesn't use streams). */
+	/** Primary stream context data array (or NULL if endpoint doesn't use streams). */
+	xhci_stream_data_t *primary_stream_data_array;
+
+	/** Primary stream context array - allocated for xHC hardware. */
 	xhci_stream_ctx_t *primary_stream_ctx_array;
 	dma_buffer_t primary_stream_ctx_dma;
 
-	/** Primary stream ring array (or NULL if endpoint doesn't use streams). */
-	xhci_trb_ring_t *primary_stream_rings;
-
-	/** Size of the allocated primary stream context array (and ring array). */
-	uint16_t primary_stream_ctx_array_size;
+	/** Size of the allocated primary stream data array (and context array). */
+	uint16_t primary_stream_data_size;
 
 	/* Maximum number of primary streams (0 - 2^16). */
@@ -129,8 +130,8 @@
 #define XHCI_DEV_ARGS(dev)		 ddf_fun_get_name((dev).base.fun), (dev).slot_id
 
+int xhci_endpoint_type(xhci_endpoint_t *ep);
+
 int xhci_endpoint_init(xhci_endpoint_t *, device_t *, const usb_endpoint_descriptors_t *);
 void xhci_endpoint_fini(xhci_endpoint_t *);
-
-int xhci_endpoint_request_streams(xhci_hc_t *, xhci_device_t *, xhci_endpoint_t *, unsigned);
 
 uint8_t xhci_endpoint_dci(xhci_endpoint_t *);
Index: uspace/drv/bus/usb/xhci/hw_struct/context.h
===================================================================
--- uspace/drv/bus/usb/xhci/hw_struct/context.h	(revision 7d1dd2b55c7a5309651626ded2ab97671e17687a)
+++ uspace/drv/bus/usb/xhci/hw_struct/context.h	(revision 47e9494c5eb0d77e3f73d832708572c5ada9398e)
@@ -167,5 +167,5 @@
 #define XHCI_STREAM_DCS(ctx)       XHCI_QWORD_EXTRACT((ctx).data[0],  0, 0)
 #define XHCI_STREAM_SCT(ctx)       XHCI_QWORD_EXTRACT((ctx).data[0],  3, 1)
-#define XHCI_STREAM_DEQ_PTR(ctx)   XHCI_QWORD_EXTRACT((ctx).data[0], 63, 4)
+#define XHCI_STREAM_DEQ_PTR(ctx)   (XHCI_QWORD_EXTRACT((ctx).data[0], 63, 4) << 4)
 #define XHCI_STREAM_EDTLA(ctx)     XHCI_QWORD_EXTRACT((ctx).data[1], 24, 0)
 
Index: uspace/drv/bus/usb/xhci/hw_struct/trb.h
===================================================================
--- uspace/drv/bus/usb/xhci/hw_struct/trb.h	(revision 7d1dd2b55c7a5309651626ded2ab97671e17687a)
+++ uspace/drv/bus/usb/xhci/hw_struct/trb.h	(revision 47e9494c5eb0d77e3f73d832708572c5ada9398e)
@@ -102,4 +102,5 @@
 #define TRB_LINK_TC(trb)        XHCI_DWORD_EXTRACT((trb).control, 1, 1)
 #define TRB_IOC(trb)            XHCI_DWORD_EXTRACT((trb).control, 5, 5)
+#define TRB_EVENT_DATA(trb)		XHCI_DWORD_EXTRACT((trb).control, 2, 2)
 
 #define TRB_TRANSFER_LENGTH(trb)	XHCI_DWORD_EXTRACT((trb).status, 23, 0)
Index: uspace/drv/bus/usb/xhci/streams.c
===================================================================
--- uspace/drv/bus/usb/xhci/streams.c	(revision 47e9494c5eb0d77e3f73d832708572c5ada9398e)
+++ uspace/drv/bus/usb/xhci/streams.c	(revision 47e9494c5eb0d77e3f73d832708572c5ada9398e)
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2017 Michal Staruch
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup drvusbxhci
+ * @{
+ */
+/** @file
+ * @brief Structures and functions for Superspeed bulk streams.
+ */
+
+#include "endpoint.h"
+#include "hc.h"
+#include "hw_struct/regs.h"
+#include "streams.h"
+
+xhci_stream_data_t *xhci_get_stream_ctx_data(xhci_endpoint_t *ep, uint32_t stream_id)
+{
+	if (stream_id == 0 || stream_id >= 65534) {
+		return NULL;
+	}
+
+	/* See 4.12.2.1 for the calculation of the IDs and dividing the stream_id */
+	uint32_t primary_stream_id = (uint32_t) (stream_id & (ep->primary_stream_data_size - 1));
+	uint32_t secondary_stream_id = (uint32_t) ((stream_id / ep->primary_stream_data_size) & 0xFF);
+
+	if (primary_stream_id >= ep->primary_stream_data_size) {
+		return NULL;
+	}
+
+	xhci_stream_data_t *primary_data = &ep->primary_stream_data_array[primary_stream_id];
+	if (secondary_stream_id != 0 && !primary_data->secondary_size) {
+		return NULL;
+	}
+
+	if (!primary_data->secondary_size) {
+		return primary_data;
+	}
+
+	xhci_stream_data_t *secondary_data = primary_data->secondary_data;
+	if (secondary_stream_id >= primary_data->secondary_size) {
+		return NULL;
+	}
+
+	return &secondary_data[secondary_stream_id];
+}
+
+void xhci_stream_free_ds(xhci_endpoint_t *xhci_ep)
+{
+	usb_log_debug2("Freeing stream rings and context arrays of endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
+
+	for (size_t index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
+		xhci_stream_data_t *primary_data = xhci_ep->primary_stream_data_array + index;
+		if (primary_data->secondary_size > 0) {
+			for (size_t index2 = 0; index2 < primary_data->secondary_size; ++index2) {
+				xhci_stream_data_t *secondary_data = primary_data->secondary_data + index2;
+				xhci_trb_ring_fini(&secondary_data->ring);
+			}
+			dma_buffer_free(&primary_data->secondary_stream_ctx_dma);
+		}
+		else {
+			xhci_trb_ring_fini(&primary_data->ring);
+		}
+	}
+	dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
+}
+
+/** Initialize secondary streams of XHCI bulk endpoint.
+ * @param[in] hc Host controller of the endpoint.
+ * @param[in] xhci_epi XHCI bulk endpoint to use.
+ * @param[in] index Index to primary stream array
+ */
+static int initialize_primary_stream(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep, unsigned index) {
+	xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[index];
+	xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[index];
+	memset(data, 0, sizeof(xhci_stream_data_t));
+
+	int err = EOK;
+
+	/* Init and register TRB ring for every primary stream */
+	if ((err = xhci_trb_ring_init(&data->ring))) {
+		return err;
+	}
+	XHCI_STREAM_DEQ_PTR_SET(*ctx, data->ring.dequeue);
+
+	/* Set to linear stream array */
+	XHCI_STREAM_SCT_SET(*ctx, 1);
+
+	return EOK;
+}
+
+/** Initialize primary streams of XHCI bulk endpoint.
+ * @param[in] hc Host controller of the endpoint.
+ * @param[in] xhci_epi XHCI bulk endpoint to use.
+ */
+static int initialize_primary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep)
+{
+	int err = EOK;
+	for (size_t index = 0; index < xhci_ep->primary_stream_data_size; ++index) {
+		err = initialize_primary_stream(hc, xhci_ep, index);
+		if (err) {
+			return err;
+		}
+	}
+
+	// TODO: deinitialize if we got stuck in the middle
+
+	return EOK;
+}
+
+/** Initialize secondary streams of XHCI bulk endpoint.
+ * @param[in] hc Host controller of the endpoint.
+ * @param[in] xhci_epi XHCI bulk endpoint to use.
+ * @param[in] index Index to primary stream array
+ * @param[in] count Number of secondary streams to initialize.
+ */
+static int initialize_secondary_streams(xhci_hc_t *hc, xhci_endpoint_t *xhci_ep, unsigned index, unsigned count)
+{
+	if (count == 0) {
+		return initialize_primary_stream(hc, xhci_ep, index);
+	}
+
+	if ((count & (count - 1)) != 0 || count < 8 || count > 256) {
+		usb_log_error("The secondary stream array size must be a power of 2 between 8 and 256.");
+		return EINVAL;
+	}
+
+	xhci_stream_ctx_t *ctx = &xhci_ep->primary_stream_ctx_array[index];
+	xhci_stream_data_t *data = &xhci_ep->primary_stream_data_array[index];
+	memset(data, 0, sizeof(xhci_stream_data_t));
+
+	data->secondary_size = count;
+	data->secondary_data = calloc(count, sizeof(xhci_stream_data_t));
+	if (!data->secondary_size) {
+		return ENOMEM;
+	}
+
+	if ((dma_buffer_alloc(&data->secondary_stream_ctx_dma, count * sizeof(xhci_stream_ctx_t)))) {
+		return ENOMEM;
+	}
+	data->secondary_stream_ctx_array = data->secondary_stream_ctx_dma.virt;
+
+	XHCI_STREAM_DEQ_PTR_SET(*ctx, data->secondary_stream_ctx_dma.phys);
+	XHCI_STREAM_SCT_SET(*ctx, fnzb32(count) + 1);
+
+	int err = EOK;
+
+	for (size_t i = 0; i < count; ++i) {
+		xhci_stream_ctx_t *secondary_ctx = &data->secondary_stream_ctx_array[i];
+		xhci_stream_data_t *secondary_data = &data->secondary_data[i];
+		/* Init and register TRB ring for every secondary stream */
+		if ((err = xhci_trb_ring_init(&secondary_data->ring))) {
+			return err;
+		}
+
+		XHCI_STREAM_DEQ_PTR_SET(*secondary_ctx, secondary_data->ring.dequeue);
+
+		/* Set to linear stream array */
+		XHCI_STREAM_SCT_SET(*secondary_ctx, 0);
+	}
+
+	// TODO: deinitialize if we got stuck in the middle
+
+	return EOK;
+}
+
+/** Configure XHCI bulk endpoint's stream context.
+ * @param[in] xhci_ep Associated XHCI bulk endpoint.
+ * @param[in] ctx Endpoint context to configure.
+ * @param[in] pstreams The value of MaxPStreams.
+ */
+static void setup_stream_context(xhci_endpoint_t *xhci_ep, xhci_ep_ctx_t *ctx, unsigned pstreams, unsigned lsa)
+{
+	XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(xhci_ep));
+	XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, xhci_ep->base.max_packet_size);
+	XHCI_EP_MAX_BURST_SIZE_SET(*ctx, xhci_ep->max_burst - 1);
+	XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
+
+	XHCI_EP_MAX_P_STREAMS_SET(*ctx, pstreams);
+	XHCI_EP_TR_DPTR_SET(*ctx, xhci_ep->primary_stream_ctx_dma.phys);
+	// TODO: set HID?
+	XHCI_EP_LSA_SET(*ctx, lsa);
+}
+
+static int verify_stream_conditions(xhci_hc_t *hc, xhci_device_t *dev,
+	xhci_endpoint_t *xhci_ep, unsigned count)
+{
+	if (xhci_ep->base.transfer_type != USB_TRANSFER_BULK
+		|| dev->base.speed != USB_SPEED_SUPER) {
+		usb_log_error("Streams are only supported by superspeed bulk endpoints.");
+		return EINVAL;
+	}
+
+	if (xhci_ep->max_streams == 1) {
+		usb_log_error("Streams are not supported by endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
+		return EINVAL;
+	}
+
+	uint8_t max_psa_size = 2 << XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PSA_SIZE);
+	if (count > max_psa_size) {
+		usb_log_error("Host controller only supports %u primary streams.", max_psa_size);
+		return EINVAL;
+	}
+
+	if (count > xhci_ep->max_streams) {
+		usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
+			XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
+		return EINVAL;
+	}
+
+	if ((count & (count - 1)) != 0) {
+		usb_log_error("The amount of primary streams must be a power of 2.");
+		return EINVAL;
+	}
+
+	return EOK;
+}
+
+static int initialize_primary_structures(xhci_endpoint_t *xhci_ep, unsigned count)
+{
+	usb_log_debug2("Allocating primary stream context array of size %u for endpoint " XHCI_EP_FMT,
+		count, XHCI_EP_ARGS(*xhci_ep));
+
+	if ((dma_buffer_alloc(&xhci_ep->primary_stream_ctx_dma, count * sizeof(xhci_stream_ctx_t)))) {
+		return ENOMEM;
+	}
+
+	xhci_ep->primary_stream_ctx_array = xhci_ep->primary_stream_ctx_dma.virt;
+	xhci_ep->primary_stream_data_array = calloc(count, sizeof(xhci_stream_data_t));
+	if (!xhci_ep->primary_stream_data_array) {
+		dma_buffer_free(&xhci_ep->primary_stream_ctx_dma);
+		return ENOMEM;
+	}
+
+	xhci_ep->primary_stream_data_size = count;
+
+	return EOK;
+}
+
+/** Initialize primary streams
+ */
+int xhci_endpoint_request_primary_streams(xhci_hc_t *hc, xhci_device_t *dev,
+	xhci_endpoint_t *xhci_ep, unsigned count)
+{
+	int err = verify_stream_conditions(hc, dev, xhci_ep, count);
+	if (err) {
+		return err;
+	}
+
+	err = initialize_primary_structures(xhci_ep, count);
+	if (err) {
+		return err;
+	}
+
+	memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
+	initialize_primary_streams(hc, xhci_ep);
+
+	xhci_ep_ctx_t ep_ctx;
+	const size_t pstreams = fnzb32(count) - 1;
+	setup_stream_context(xhci_ep, &ep_ctx, pstreams, 1);
+
+	// FIXME: do we add endpoint? do we need to destroy previous configuration?
+	return hc_add_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep), &ep_ctx);
+}
+
+/** Initialize secondary streams
+ * sizes - the size of each secondary stream context (an array)
+ * count - the amount of primary stream contexts
+ */
+int xhci_endpoint_request_secondary_streams(xhci_hc_t *hc, xhci_device_t *dev,
+	xhci_endpoint_t *xhci_ep, unsigned *sizes, unsigned count)
+{
+	int err = verify_stream_conditions(hc, dev, xhci_ep, count);
+	if (err) {
+		return err;
+	}
+
+	// TODO: determine if count * secondary streams <= max_streams
+	if (count > xhci_ep->max_streams) {
+		usb_log_error("Endpoint " XHCI_EP_FMT " supports only %" PRIu32 " streams.",
+			XHCI_EP_ARGS(*xhci_ep), xhci_ep->max_streams);
+		return EINVAL;
+	}
+
+	err = initialize_primary_structures(xhci_ep, count);
+	if (err) {
+		return err;
+	}
+
+	memset(xhci_ep->primary_stream_ctx_array, 0, count * sizeof(xhci_stream_ctx_t));
+	for (size_t index = 0; index < count; ++index) {
+		initialize_secondary_streams(hc, xhci_ep, index, *(sizes + index));
+	}
+
+	xhci_ep_ctx_t ep_ctx;
+	const size_t pstreams = fnzb32(count) - 1;
+	setup_stream_context(xhci_ep, &ep_ctx, pstreams, 0);
+
+	// FIXME: do we add endpoint? do we need to destroy previous configuration?
+	return hc_add_endpoint(hc, dev->slot_id, xhci_endpoint_index(xhci_ep), &ep_ctx);
+}
Index: uspace/drv/bus/usb/xhci/streams.h
===================================================================
--- uspace/drv/bus/usb/xhci/streams.h	(revision 47e9494c5eb0d77e3f73d832708572c5ada9398e)
+++ uspace/drv/bus/usb/xhci/streams.h	(revision 47e9494c5eb0d77e3f73d832708572c5ada9398e)
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2017 Michal Staruch
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup drvusbxhci
+ * @{
+ */
+/** @file
+ * @brief Structures and functions for Superspeed bulk streams.
+ */
+
+#ifndef XHCI_STREAMS_H
+#define XHCI_STREAMS_H
+
+#include "endpoint.h"
+#include "hw_struct/context.h"
+#include "trb_ring.h"
+
+typedef struct xhci_endpoint xhci_endpoint_t;
+typedef struct xhci_stream_data xhci_stream_data_t;
+
+typedef struct xhci_stream_data {
+	/** The TRB ring for the context, if valid */
+	xhci_trb_ring_t ring;
+
+	/** Pointer to the array of secondary stream context data for primary data. */
+	xhci_stream_data_t *secondary_data;
+
+	/** The size of secondary stream context data array */
+	uint32_t secondary_size;
+
+	/** Secondary stream context array - allocated for xHC hardware.
+	 * Required for later dealocation of secondary structure.
+	 */
+	xhci_stream_ctx_t *secondary_stream_ctx_array;
+	dma_buffer_t secondary_stream_ctx_dma;
+} xhci_stream_data_t;
+
+xhci_stream_data_t *xhci_get_stream_ctx_data(xhci_endpoint_t *ep, uint32_t stream_id);
+void xhci_stream_free_ds(xhci_endpoint_t *xhci_ep);
+
+int xhci_endpoint_request_primary_streams(xhci_hc_t *hc, xhci_device_t *dev,
+	xhci_endpoint_t *xhci_ep, unsigned count);
+int xhci_endpoint_request_secondary_streams(xhci_hc_t *hc, xhci_device_t *dev,
+	xhci_endpoint_t *xhci_ep, unsigned *sizes, unsigned count);
+
+#endif
Index: uspace/drv/bus/usb/xhci/transfers.c
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.c	(revision 7d1dd2b55c7a5309651626ded2ab97671e17687a)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision 47e9494c5eb0d77e3f73d832708572c5ada9398e)
@@ -39,4 +39,5 @@
 #include "hc.h"
 #include "hw_struct/trb.h"
+#include "streams.h"
 #include "transfers.h"
 #include "trb_ring.h"
@@ -121,5 +122,15 @@
 static xhci_trb_ring_t *get_ring(xhci_hc_t *hc, xhci_transfer_t *transfer)
 {
-	return &xhci_endpoint_get(transfer->batch.ep)->ring;
+	xhci_endpoint_t *ep = xhci_endpoint_get(transfer->batch.ep);
+	if (ep->primary_stream_data_size == 0) return &ep->ring;
+	uint32_t stream_id = transfer->batch.target.stream;
+
+	xhci_stream_data_t *stream_data = xhci_get_stream_ctx_data(ep, stream_id);
+	if (stream_data == NULL) {
+		usb_log_warning("No transfer ring was found for stream %u.", stream_id);
+		return NULL;
+	}
+
+	return &stream_data->ring;
 }
 
@@ -202,15 +213,38 @@
 	// data size (sent for OUT, or buffer size)
 	TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_size);
-	// FIXME: TD size 4.11.2.4
-	TRB_CTRL_SET_TD_SIZE(trb, 1);
-
-	// we want an interrupt after this td is done
-	TRB_CTRL_SET_IOC(trb, 1);
-
-	TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
-
-	xhci_trb_ring_t* ring = get_ring(hc, transfer);
-
-	return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
+
+	/* The stream-enabled endpoints need to chain ED trb */
+	xhci_endpoint_t *ep = xhci_endpoint_get(transfer->batch.ep);
+	if (!ep->primary_stream_data_size) {
+		// FIXME: TD size 4.11.2.4
+		TRB_CTRL_SET_TD_SIZE(trb, 1);
+
+		// we want an interrupt after this td is done
+		TRB_CTRL_SET_IOC(trb, 1);
+		TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
+
+		xhci_trb_ring_t* ring = get_ring(hc, transfer);
+		return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
+	}
+	else {
+		TRB_CTRL_SET_TD_SIZE(trb, 2);
+		TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
+		TRB_CTRL_SET_CHAIN(trb, 1);
+		TRB_CTRL_SET_ENT(trb, 1);
+
+		xhci_trb_ring_t* ring = get_ring(hc, transfer);
+		int err = xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
+
+		if (err) {
+			return err;
+		}
+
+		xhci_trb_clean(&trb);
+		trb.parameter = host2xhci(64, (uintptr_t) transfer);
+		TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_EVENT_DATA);
+		TRB_CTRL_SET_IOC(trb, 1);
+
+		return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
+	}
 }
 
@@ -267,18 +301,40 @@
 	xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
 
-	xhci_trb_ring_update_dequeue(&ep->ring, addr);
-
-	if (ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) {
-		isoch_handle_transfer_event(hc, ep, trb);
-		endpoint_del_ref(&ep->base);
-		return EOK;
-	}
-
-	fibril_mutex_lock(&ep->base.guard);
-	usb_transfer_batch_t *batch = ep->base.active_batch;
-	if (!batch) {
+	usb_transfer_batch_t *batch;
+	xhci_transfer_t *transfer;
+
+	if (TRB_EVENT_DATA(*trb)) {
+		assert(ep->base.transfer_type != USB_TRANSFER_ISOCHRONOUS);
+		/* We are received transfer pointer instead - work with that */
+		transfer = (xhci_transfer_t *) addr;
+		xhci_trb_ring_t * ring = get_ring(hc, transfer);
+		xhci_trb_ring_update_dequeue(ring, transfer->interrupt_trb_phys);
+		batch = &transfer->batch;
+
+		fibril_mutex_lock(&ep->base.guard);
+		endpoint_deactivate_locked(&ep->base);
 		fibril_mutex_unlock(&ep->base.guard);
-		endpoint_del_ref(&ep->base);
-		return ENOENT;
+	}
+	else {
+		xhci_trb_ring_update_dequeue(&ep->ring, addr);
+
+		if (ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) {
+			isoch_handle_transfer_event(hc, ep, trb);
+			endpoint_del_ref(&ep->base);
+			return EOK;
+		}
+
+		fibril_mutex_lock(&ep->base.guard);
+		batch = ep->base.active_batch;
+		if (!batch) {
+			fibril_mutex_unlock(&ep->base.guard);
+			endpoint_del_ref(&ep->base);
+			return ENOENT;
+		}
+
+		transfer = xhci_transfer_from_batch(batch);
+
+		endpoint_deactivate_locked(&ep->base);
+		fibril_mutex_unlock(&ep->base.guard);
 	}
 
@@ -296,9 +352,4 @@
 	}
 
-	endpoint_deactivate_locked(&ep->base);
-	fibril_mutex_unlock(&ep->base.guard);
-
-	xhci_transfer_t *transfer = xhci_transfer_from_batch(batch);
-
 	if (batch->dir == USB_DIRECTION_IN) {
 		assert(batch->buffer);
@@ -370,5 +421,6 @@
 
 	const uint8_t slot_id = xhci_dev->slot_id;
-	const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
+	/* EP Doorbells start at 1 */
+	const uint8_t target = (xhci_endpoint_index(xhci_ep) + 1) | (batch->target.stream << 16);
 	hc_ring_doorbell(hc, slot_id, target);
 	return EOK;
Index: uspace/lib/drv/include/usb_iface.h
===================================================================
--- uspace/lib/drv/include/usb_iface.h	(revision 7d1dd2b55c7a5309651626ded2ab97671e17687a)
+++ uspace/lib/drv/include/usb_iface.h	(revision 47e9494c5eb0d77e3f73d832708572c5ada9398e)
@@ -89,6 +89,7 @@
 		usb_address_t address;
 		usb_endpoint_t endpoint;
+		uint32_t stream;
 	} __attribute__((packed));
-	uint32_t packed;
+	uint64_t packed;
 } usb_target_t;
 
