Index: uspace/drv/block/usbmast/main.c
===================================================================
--- uspace/drv/block/usbmast/main.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/block/usbmast/main.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -172,7 +172,7 @@
 	    usb_device_get_name(dev));
 	usb_log_debug("Bulk in endpoint: %d [%zuB].\n",
-	    epm_in->pipe.endpoint_no, epm_in->pipe.max_packet_size);
+	    epm_in->pipe.desc.endpoint_no, epm_in->pipe.desc.max_packet_size);
 	usb_log_debug("Bulk out endpoint: %d [%zuB].\n",
-	    epm_out->pipe.endpoint_no, epm_out->pipe.max_packet_size);
+	    epm_out->pipe.desc.endpoint_no, epm_out->pipe.desc.max_packet_size);
 
 	usb_log_debug("Get LUN count...\n");
Index: uspace/drv/bus/usb/ehci/Makefile
===================================================================
--- uspace/drv/bus/usb/ehci/Makefile	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ehci/Makefile	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -35,5 +35,5 @@
 SOURCES = \
 	ehci_batch.c \
-	ehci_endpoint.c \
+	ehci_bus.c \
 	ehci_rh.c \
 	endpoint_list.c \
Index: uspace/drv/bus/usb/ehci/ehci_batch.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_batch.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ehci/ehci_batch.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -45,5 +45,5 @@
 
 #include "ehci_batch.h"
-#include "ehci_endpoint.h"
+#include "ehci_bus.h"
 
 /* The buffer pointer list in the qTD is long enough to support a maximum
@@ -53,5 +53,5 @@
 #define EHCI_TD_MAX_TRANSFER   (16 * 1024)
 
-static void (*const batch_setup[])(ehci_transfer_batch_t*, usb_direction_t);
+static void (*const batch_setup[])(ehci_transfer_batch_t*);
 
 /** Safely destructs ehci_transfer_batch_t structure
@@ -59,8 +59,7 @@
  * @param[in] ehci_batch Instance to destroy.
  */
-static void ehci_transfer_batch_dispose(ehci_transfer_batch_t *ehci_batch)
-{
-	if (!ehci_batch)
-		return;
+void ehci_transfer_batch_destroy(ehci_transfer_batch_t *ehci_batch)
+{
+	assert(ehci_batch);
 	if (ehci_batch->tds) {
 		for (size_t i = 0; i < ehci_batch->td_count; ++i) {
@@ -69,5 +68,4 @@
 		free(ehci_batch->tds);
 	}
-	usb_transfer_batch_destroy(ehci_batch->usb_batch);
 	free32(ehci_batch->device_buffer);
 	free(ehci_batch);
@@ -75,17 +73,4 @@
 }
 
-/** Finishes usb_transfer_batch and destroys the structure.
- *
- * @param[in] uhci_batch Instance to finish and destroy.
- */
-void ehci_transfer_batch_finish_dispose(ehci_transfer_batch_t *ehci_batch)
-{
-	assert(ehci_batch);
-	assert(ehci_batch->usb_batch);
-	usb_transfer_batch_finish(ehci_batch->usb_batch,
-	    ehci_batch->device_buffer + ehci_batch->usb_batch->setup_size);
-	ehci_transfer_batch_dispose(ehci_batch);
-}
-
 /** Allocate memory and initialize internal data structure.
  *
@@ -94,26 +79,64 @@
  * NULL otherwise.
  *
+ */
+ehci_transfer_batch_t * ehci_transfer_batch_create(endpoint_t *ep)
+{
+	assert(ep);
+
+	ehci_transfer_batch_t *ehci_batch = calloc(1, sizeof(ehci_transfer_batch_t));
+	if (!ehci_batch) {
+		usb_log_error("Failed to allocate EHCI batch data.");
+		return NULL;
+	}
+
+	usb_transfer_batch_init(&ehci_batch->base, ep);
+	link_initialize(&ehci_batch->link);
+
+	return ehci_batch;
+}
+
+/** Prepares a batch to be sent.
+ *
  * Determines the number of needed transfer descriptors (TDs).
  * Prepares a transport buffer (that is accessible by the hardware).
  * Initializes parameters needed for the transfer and callback.
  */
-ehci_transfer_batch_t * ehci_transfer_batch_get(usb_transfer_batch_t *usb_batch)
-{
-	assert(usb_batch);
-
-	ehci_transfer_batch_t *ehci_batch =
-	    calloc(1, sizeof(ehci_transfer_batch_t));
-	if (!ehci_batch) {
-		usb_log_error("Batch %p: Failed to allocate EHCI batch data.",
-		    usb_batch);
-		goto dispose;
-	}
-	link_initialize(&ehci_batch->link);
-	ehci_batch->td_count =
-	    (usb_batch->buffer_size + EHCI_TD_MAX_TRANSFER - 1)
-	    / EHCI_TD_MAX_TRANSFER;
+int ehci_transfer_batch_prepare(ehci_transfer_batch_t *ehci_batch)
+{
+	assert(ehci_batch);
+
+	const size_t setup_size = (ehci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
+		? USB_SETUP_PACKET_SIZE
+		: 0;
+
+	const size_t size = ehci_batch->base.buffer_size;
+
+	/* Mix setup stage and data together, we have enough space */
+        if (size + setup_size > 0) {
+		/* Use one buffer for setup and data stage */
+		ehci_batch->device_buffer = malloc32(size + setup_size);
+		if (!ehci_batch->device_buffer) {
+			usb_log_error("Batch %p: Failed to allocate device "
+			    "buffer", ehci_batch);
+			return ENOMEM;
+		}
+		/* Copy setup data */
+                memcpy(ehci_batch->device_buffer, ehci_batch->base.setup.buffer,
+		    setup_size);
+		/* Copy generic data */
+		if (ehci_batch->base.dir != USB_DIRECTION_IN)
+			memcpy(ehci_batch->device_buffer + setup_size,
+			    ehci_batch->base.buffer, ehci_batch->base.buffer_size);
+        }
+
+	/* Add TD left over by the previous transfer */
+	ehci_batch->qh = ehci_endpoint_get(ehci_batch->base.ep)->qh;
+
+	/* Determine number of TDs needed */
+	ehci_batch->td_count = (size + EHCI_TD_MAX_TRANSFER - 1)
+		/ EHCI_TD_MAX_TRANSFER;
 
 	/* Control transfer need Setup and Status stage */
-	if (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
+	if (ehci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL) {
 		ehci_batch->td_count += 2;
 	}
@@ -122,10 +145,7 @@
 	if (!ehci_batch->tds) {
 		usb_log_error("Batch %p: Failed to allocate EHCI transfer "
-		    "descriptors.", usb_batch);
-		goto dispose;
-	}
-
-	/* Add TD left over by the previous transfer */
-	ehci_batch->qh = ehci_endpoint_get(usb_batch->ep)->qh;
+		    "descriptors.", ehci_batch);
+		return ENOMEM;
+	}
 
 	for (unsigned i = 0; i < ehci_batch->td_count; ++i) {
@@ -133,44 +153,18 @@
 		if (!ehci_batch->tds[i]) {
 			usb_log_error("Batch %p: Failed to allocate TD %d.",
-			    usb_batch, i);
-			goto dispose;
+			    ehci_batch, i);
+			return ENOMEM;
 		}
 		memset(ehci_batch->tds[i], 0, sizeof(td_t));
 	}
 
-
-	/* Mix setup stage and data together, we have enough space */
-        if (usb_batch->setup_size + usb_batch->buffer_size > 0) {
-		/* Use one buffer for setup and data stage */
-		ehci_batch->device_buffer =
-		    malloc32(usb_batch->setup_size + usb_batch->buffer_size);
-		if (!ehci_batch->device_buffer) {
-			usb_log_error("Batch %p: Failed to allocate device "
-			    "buffer", usb_batch);
-			goto dispose;
-		}
-		/* Copy setup data */
-                memcpy(ehci_batch->device_buffer, usb_batch->setup_buffer,
-		    usb_batch->setup_size);
-		/* Copy generic data */
-		if (usb_batch->ep->direction != USB_DIRECTION_IN)
-			memcpy(
-			    ehci_batch->device_buffer + usb_batch->setup_size,
-			    usb_batch->buffer, usb_batch->buffer_size);
-        }
-	ehci_batch->usb_batch = usb_batch;
-
-	const usb_direction_t dir = usb_transfer_batch_direction(usb_batch);
-	assert(batch_setup[usb_batch->ep->transfer_type]);
-	batch_setup[usb_batch->ep->transfer_type](ehci_batch, dir);
+	assert(batch_setup[ehci_batch->base.ep->transfer_type]);
+	batch_setup[ehci_batch->base.ep->transfer_type](ehci_batch);
 
 	usb_log_debug("Batch %p %s " USB_TRANSFER_BATCH_FMT " initialized.\n",
-	    usb_batch, usb_str_direction(dir),
-	    USB_TRANSFER_BATCH_ARGS(*usb_batch));
-
-	return ehci_batch;
-dispose:
-	ehci_transfer_batch_dispose(ehci_batch);
-	return NULL;
+	    ehci_batch, usb_str_direction(ehci_batch->base.dir),
+	    USB_TRANSFER_BATCH_ARGS(ehci_batch->base));
+
+	return EOK;
 }
 
@@ -184,14 +178,13 @@
  * completes with the last TD.
  */
-bool ehci_transfer_batch_is_complete(const ehci_transfer_batch_t *ehci_batch)
-{
-	assert(ehci_batch);
-	assert(ehci_batch->usb_batch);
+bool ehci_transfer_batch_check_completed(ehci_transfer_batch_t *ehci_batch)
+{
+	assert(ehci_batch);
 
 	usb_log_debug("Batch %p: checking %zu td(s) for completion.\n",
-	    ehci_batch->usb_batch, ehci_batch->td_count);
+	    ehci_batch, ehci_batch->td_count);
 
 	usb_log_debug2("Batch %p: QH: %08x:%08x:%08x:%08x:%08x:%08x.\n",
-	    ehci_batch->usb_batch,
+	    ehci_batch,
 	    ehci_batch->qh->ep_char, ehci_batch->qh->ep_cap,
 	    ehci_batch->qh->status, ehci_batch->qh->current,
@@ -206,6 +199,5 @@
 
 	/* Assume all data got through */
-	ehci_batch->usb_batch->transfered_size =
-	    ehci_batch->usb_batch->buffer_size;
+	ehci_batch->base.transfered_size = ehci_batch->base.buffer_size;
 
 	/* Check all TDs */
@@ -213,10 +205,10 @@
 		assert(ehci_batch->tds[i] != NULL);
 		usb_log_debug("Batch %p: TD %zu: %08x:%08x:%08x.",
-		    ehci_batch->usb_batch, i,
+		    ehci_batch, i,
 		    ehci_batch->tds[i]->status, ehci_batch->tds[i]->next,
 		    ehci_batch->tds[i]->alternate);
 
-		ehci_batch->usb_batch->error = td_error(ehci_batch->tds[i]);
-		if (ehci_batch->usb_batch->error == EOK) {
+		ehci_batch->base.error = td_error(ehci_batch->tds[i]);
+		if (ehci_batch->base.error == EOK) {
 			/* If the TD got all its data through, it will report
 			 * 0 bytes remain, the sole exception is INPUT with
@@ -231,11 +223,11 @@
 			 * we leave the very last(unused) TD behind.
 			 */
-			ehci_batch->usb_batch->transfered_size
+			ehci_batch->base.transfered_size
 			    -= td_remain_size(ehci_batch->tds[i]);
 		} else {
 			usb_log_debug("Batch %p found error TD(%zu):%08x (%d).",
-			    ehci_batch->usb_batch, i,
+			    ehci_batch, i,
 			    ehci_batch->tds[i]->status,
-			    ehci_batch->usb_batch->error);
+			    ehci_batch->base.error);
 			/* Clear possible ED HALT */
 			qh_clear_halt(ehci_batch->qh);
@@ -244,11 +236,14 @@
 	}
 
-	assert(ehci_batch->usb_batch->transfered_size <=
-	    ehci_batch->usb_batch->buffer_size);
+	assert(ehci_batch->base.transfered_size <= ehci_batch->base.buffer_size);
+
+	if (ehci_batch->base.dir == USB_DIRECTION_IN)
+		memcpy(ehci_batch->base.buffer, ehci_batch->device_buffer, ehci_batch->base.transfered_size);
+
 	/* Clear TD pointers */
 	ehci_batch->qh->next = LINK_POINTER_TERM;
 	ehci_batch->qh->current = LINK_POINTER_TERM;
-	usb_log_debug("Batch %p complete: %s", ehci_batch->usb_batch,
-	    str_error(ehci_batch->usb_batch->error));
+	usb_log_debug("Batch %p complete: %s", ehci_batch,
+	    str_error(ehci_batch->base.error));
 
 	return true;
@@ -268,18 +263,18 @@
  *
  * @param[in] ehci_batch Batch structure to use.
- * @param[in] dir Communication direction
  *
  * Setup stage with toggle 0 and direction BOTH(SETUP_PID)
- * Data stage with alternating toggle and direction supplied by parameter.
- * Status stage with toggle 1 and direction supplied by parameter.
- */
-static void batch_control(ehci_transfer_batch_t *ehci_batch, usb_direction_t dir)
-{
-	assert(ehci_batch);
-	assert(ehci_batch->usb_batch);
+ * Data stage with alternating toggle and direction
+ * Status stage with toggle 1 and direction
+ */
+static void batch_control(ehci_transfer_batch_t *ehci_batch)
+{
+	assert(ehci_batch);
+
+	usb_direction_t dir = ehci_batch->base.dir;
 	assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
 
 	usb_log_debug2("Batch %p: Control QH(%"PRIxn"): "
-	    "%08x:%08x:%08x:%08x:%08x:%08x", ehci_batch->usb_batch,
+	    "%08x:%08x:%08x:%08x:%08x:%08x", ehci_batch,
 	    addr_to_phys(ehci_batch->qh),
 	    ehci_batch->qh->ep_char, ehci_batch->qh->ep_cap,
@@ -298,15 +293,15 @@
 	/* Setup stage */
 	td_init(ehci_batch->tds[0], ehci_batch->tds[1], USB_DIRECTION_BOTH,
-	    buffer, ehci_batch->usb_batch->setup_size, toggle, false);
+	    buffer, USB_SETUP_PACKET_SIZE, toggle, false);
 	usb_log_debug2("Batch %p: Created CONTROL SETUP TD(%"PRIxn"): "
-	    "%08x:%08x:%08x", ehci_batch->usb_batch,
+	    "%08x:%08x:%08x", ehci_batch,
 	    addr_to_phys(ehci_batch->tds[0]),
 	    ehci_batch->tds[0]->status, ehci_batch->tds[0]->next,
 	    ehci_batch->tds[0]->alternate);
-	buffer += ehci_batch->usb_batch->setup_size;
+	buffer += USB_SETUP_PACKET_SIZE;
 
 	/* Data stage */
 	size_t td_current = 1;
-	size_t remain_size = ehci_batch->usb_batch->buffer_size;
+	size_t remain_size = ehci_batch->base.buffer_size;
 	while (remain_size > 0) {
 		const size_t transfer_size =
@@ -318,5 +313,5 @@
 		    transfer_size, toggle, false);
 		usb_log_debug2("Batch %p: Created CONTROL DATA TD(%"PRIxn"): "
-		    "%08x:%08x:%08x", ehci_batch->usb_batch,
+		    "%08x:%08x:%08x", ehci_batch,
 		    addr_to_phys(ehci_batch->tds[td_current]),
 		    ehci_batch->tds[td_current]->status,
@@ -334,5 +329,5 @@
 	td_init(ehci_batch->tds[td_current], NULL, status_dir, NULL, 0, 1, true);
 	usb_log_debug2("Batch %p: Created CONTROL STATUS TD(%"PRIxn"): "
-	    "%08x:%08x:%08x", ehci_batch->usb_batch,
+	    "%08x:%08x:%08x", ehci_batch,
 	    addr_to_phys(ehci_batch->tds[td_current]),
 	    ehci_batch->tds[td_current]->status,
@@ -349,12 +344,10 @@
  * EHCI hw in ED.
  */
-static void batch_data(ehci_transfer_batch_t *ehci_batch, usb_direction_t dir)
-{
-	assert(ehci_batch);
-	assert(ehci_batch->usb_batch);
-	assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
+static void batch_data(ehci_transfer_batch_t *ehci_batch)
+{
+	assert(ehci_batch);
 
 	usb_log_debug2("Batch %p: Data QH(%"PRIxn"): "
-	    "%08x:%08x:%08x:%08x:%08x:%08x", ehci_batch->usb_batch,
+	    "%08x:%08x:%08x:%08x:%08x:%08x", ehci_batch,
 	    addr_to_phys(ehci_batch->qh),
 	    ehci_batch->qh->ep_char, ehci_batch->qh->ep_cap,
@@ -363,5 +356,5 @@
 
 	size_t td_current = 0;
-	size_t remain_size = ehci_batch->usb_batch->buffer_size;
+	size_t remain_size = ehci_batch->base.buffer_size;
 	char *buffer = ehci_batch->device_buffer;
 	while (remain_size > 0) {
@@ -373,8 +366,8 @@
 		    ehci_batch->tds[td_current],
 		    last ? NULL : ehci_batch->tds[td_current + 1],
-		    dir, buffer, transfer_size, -1, last);
+		    ehci_batch->base.dir, buffer, transfer_size, -1, last);
 
 		usb_log_debug2("Batch %p: DATA TD(%"PRIxn": %08x:%08x:%08x",
-		    ehci_batch->usb_batch,
+		    ehci_batch,
 		    addr_to_phys(ehci_batch->tds[td_current]),
 		    ehci_batch->tds[td_current]->status,
@@ -390,5 +383,5 @@
 
 /** Transfer setup table. */
-static void (*const batch_setup[])(ehci_transfer_batch_t*, usb_direction_t) =
+static void (*const batch_setup[])(ehci_transfer_batch_t*) =
 {
 	[USB_TRANSFER_CONTROL] = batch_control,
Index: uspace/drv/bus/usb/ehci/ehci_batch.h
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_batch.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ehci/ehci_batch.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -45,4 +45,5 @@
 /** EHCI specific data required for USB transfer */
 typedef struct ehci_transfer_batch {
+	usb_transfer_batch_t base;
 	/** Link */
 	link_t link;
@@ -59,8 +60,9 @@
 } ehci_transfer_batch_t;
 
-ehci_transfer_batch_t * ehci_transfer_batch_get(usb_transfer_batch_t *batch);
-bool ehci_transfer_batch_is_complete(const ehci_transfer_batch_t *batch);
+ehci_transfer_batch_t * ehci_transfer_batch_create(endpoint_t *ep);
+int ehci_transfer_batch_prepare(ehci_transfer_batch_t *batch);
 void ehci_transfer_batch_commit(const ehci_transfer_batch_t *batch);
-void ehci_transfer_batch_finish_dispose(ehci_transfer_batch_t *batch);
+bool ehci_transfer_batch_check_completed(ehci_transfer_batch_t *batch);
+void ehci_transfer_batch_destroy(ehci_transfer_batch_t *batch);
 
 static inline ehci_transfer_batch_t *ehci_transfer_batch_from_link(link_t *l)
@@ -69,4 +71,12 @@
 	return list_get_instance(l, ehci_transfer_batch_t, link);
 }
+
+static inline ehci_transfer_batch_t * ehci_transfer_batch_get(usb_transfer_batch_t *usb_batch)
+{
+	assert(usb_batch);
+
+	return (ehci_transfer_batch_t *) usb_batch;
+}
+
 #endif
 /**
Index: uspace/drv/bus/usb/ehci/ehci_bus.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_bus.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/ehci/ehci_bus.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvusbehci
+ * @{
+ */
+/** @file
+ * @brief EHCI driver
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <usb/host/utils/malloc32.h>
+#include <usb/host/bandwidth.h>
+#include <usb/debug.h>
+
+#include "ehci_bus.h"
+#include "ehci_batch.h"
+#include "hc.h"
+
+/** Callback to set toggle on ED.
+ *
+ * @param[in] hcd_ep hcd endpoint structure
+ * @param[in] toggle new value of toggle bit
+ */
+static void ehci_ep_toggle_set(endpoint_t *ep, bool toggle)
+{
+	ehci_endpoint_t *instance = ehci_endpoint_get(ep);
+	assert(instance);
+	assert(instance->qh);
+	ep->toggle = toggle;
+	if (qh_toggle_from_td(instance->qh))
+		usb_log_warning("EP(%p): Setting toggle bit for transfer "
+		    "directed EP", instance);
+	qh_toggle_set(instance->qh, toggle);
+}
+
+/** Callback to get value of toggle bit.
+ *
+ * @param[in] hcd_ep hcd endpoint structure
+ * @return Current value of toggle bit.
+ */
+static bool ehci_ep_toggle_get(endpoint_t *ep)
+{
+	ehci_endpoint_t *instance = ehci_endpoint_get(ep);
+	assert(instance);
+	assert(instance->qh);
+
+	if (qh_toggle_from_td(instance->qh))
+		usb_log_warning("EP(%p): Reading useless toggle bit", instance);
+	return qh_toggle_get(instance->qh);
+}
+
+/** Creates new hcd endpoint representation.
+ */
+static endpoint_t *ehci_endpoint_create(bus_t *bus)
+{
+	assert(bus);
+
+	ehci_endpoint_t *ehci_ep = malloc(sizeof(ehci_endpoint_t));
+	if (ehci_ep == NULL)
+		return NULL;
+
+	endpoint_init(&ehci_ep->base, bus);
+
+	ehci_ep->qh = malloc32(sizeof(qh_t));
+	if (ehci_ep->qh == NULL) {
+		free(ehci_ep);
+		return NULL;
+	}
+
+	link_initialize(&ehci_ep->link);
+	return &ehci_ep->base;
+}
+
+/** Disposes hcd endpoint structure
+ *
+ * @param[in] hcd driver using this instance.
+ * @param[in] ep endpoint structure.
+ */
+static void ehci_endpoint_destroy(endpoint_t *ep)
+{
+	assert(ep);
+	ehci_endpoint_t *instance = ehci_endpoint_get(ep);
+
+	free32(instance->qh);
+	free(instance);
+}
+
+
+static int ehci_register_ep(bus_t *bus_base, device_t *dev, endpoint_t *ep, const usb_endpoint_desc_t *desc)
+{
+	ehci_bus_t *bus = (ehci_bus_t *) bus_base;
+	ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ep);
+
+	// TODO utilize desc->usb2
+
+	const int err = bus->parent_ops.register_endpoint(bus_base, dev, ep, desc);
+	if (err)
+		return err;
+
+	qh_init(ehci_ep->qh, ep);
+	hc_enqueue_endpoint(bus->hc, ep);
+
+	return EOK;
+}
+
+static int ehci_unregister_ep(bus_t *bus_base, endpoint_t *ep)
+{
+	ehci_bus_t *bus = (ehci_bus_t *) bus_base;
+	assert(bus);
+	assert(ep);
+
+	const int err = bus->parent_ops.unregister_endpoint(bus_base, ep);
+	if (err)
+		return err;
+
+	hc_dequeue_endpoint(bus->hc, ep);
+	return EOK;
+}
+
+static usb_transfer_batch_t *ehci_bus_create_batch(bus_t *bus, endpoint_t *ep)
+{
+	ehci_transfer_batch_t *batch = ehci_transfer_batch_create(ep);
+	return &batch->base;
+}
+
+static void ehci_bus_destroy_batch(usb_transfer_batch_t *batch)
+{
+	ehci_transfer_batch_destroy(ehci_transfer_batch_get(batch));
+}
+
+int ehci_bus_init(ehci_bus_t *bus, hc_t *hc)
+{
+	assert(hc);
+	assert(bus);
+
+	// FIXME: Implement the USB2 bw counting.
+	usb2_bus_init(&bus->base, BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);
+
+	bus_ops_t *ops = &bus->base.base.ops;
+	bus->parent_ops = *ops;
+	ops->create_endpoint = ehci_endpoint_create;
+	ops->destroy_endpoint = ehci_endpoint_destroy;
+	ops->endpoint_set_toggle = ehci_ep_toggle_set;
+	ops->endpoint_get_toggle = ehci_ep_toggle_get;
+
+	ops->register_endpoint = ehci_register_ep;
+	ops->unregister_endpoint = ehci_unregister_ep;
+
+	ops->create_batch = ehci_bus_create_batch;
+	ops->destroy_batch = ehci_bus_destroy_batch;
+
+	bus->hc = hc;
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/ehci/ehci_bus.h
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_bus.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/ehci/ehci_bus.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
+ * 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 drvusbehci
+ * @{
+ */
+/** @file
+ * @brief EHCI driver
+ */
+#ifndef DRV_EHCI_HCD_BUS_H
+#define DRV_EHCI_HCD_BUS_H
+
+#include <assert.h>
+#include <adt/list.h>
+#include <usb/host/usb2_bus.h>
+#include <usb/host/endpoint.h>
+
+#include "hw_struct/queue_head.h"
+
+/** Connector structure linking ED to to prepared TD. */
+typedef struct ehci_endpoint {
+	/* Inheritance */
+	endpoint_t base;
+
+	/** EHCI endpoint descriptor */
+	qh_t *qh;
+	/** Linked list used by driver software */
+	link_t link;
+} ehci_endpoint_t;
+
+typedef struct hc hc_t;
+
+typedef struct {
+	usb2_bus_t base;
+	hc_t *hc;
+
+	/* Stored original ops from base, they are called in our handlers */
+	bus_ops_t parent_ops;
+} ehci_bus_t;
+
+int ehci_bus_init(ehci_bus_t *, hc_t *);
+
+/** Get and convert assigned ehci_endpoint_t structure
+ * @param[in] ep USBD endpoint structure.
+ * @return Pointer to assigned hcd endpoint structure
+ */
+static inline ehci_endpoint_t * ehci_endpoint_get(const endpoint_t *ep)
+{
+	assert(ep);
+	return (ehci_endpoint_t *) ep;
+}
+
+static inline ehci_endpoint_t * ehci_endpoint_list_instance(link_t *l)
+{
+	return list_get_instance(l, ehci_endpoint_t, link);
+}
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/ehci/ehci_endpoint.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_endpoint.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ 	(revision )
@@ -1,126 +1,0 @@
-/*
- * Copyright (c) 2014 Jan Vesely
- * 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 drvusbehci
- * @{
- */
-/** @file
- * @brief EHCI driver
- */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <usb/debug.h>
-#include <usb/host/utils/malloc32.h>
-
-#include "ehci_endpoint.h"
-#include "hc.h"
-
-/** Callback to set toggle on ED.
- *
- * @param[in] hcd_ep hcd endpoint structure
- * @param[in] toggle new value of toggle bit
- */
-static void ehci_ep_toggle_set(void *ehci_ep, int toggle)
-{
-	ehci_endpoint_t *instance = ehci_ep;
-	assert(instance);
-	assert(instance->qh);
-	if (qh_toggle_from_td(instance->qh))
-		usb_log_warning("EP(%p): Setting toggle bit for transfer "
-		    "directed EP", instance);
-	qh_toggle_set(instance->qh, toggle);
-}
-
-/** Callback to get value of toggle bit.
- *
- * @param[in] hcd_ep hcd endpoint structure
- * @return Current value of toggle bit.
- */
-static int ehci_ep_toggle_get(void *ehci_ep)
-{
-	ehci_endpoint_t *instance = ehci_ep;
-	assert(instance);
-	assert(instance->qh);
-	if (qh_toggle_from_td(instance->qh))
-		usb_log_warning("EP(%p): Reading useless toggle bit", instance);
-	return qh_toggle_get(instance->qh);
-}
-
-/** Creates new hcd endpoint representation.
- *
- * @param[in] ep USBD endpoint structure
- * @return Error code.
- */
-int ehci_endpoint_init(hcd_t *hcd, endpoint_t *ep)
-{
-	assert(ep);
-	hc_t *hc = hcd_get_driver_data(hcd);
-
-	ehci_endpoint_t *ehci_ep = malloc(sizeof(ehci_endpoint_t));
-	if (ehci_ep == NULL)
-		return ENOMEM;
-
-	ehci_ep->qh = malloc32(sizeof(qh_t));
-	if (ehci_ep->qh == NULL) {
-		free(ehci_ep);
-		return ENOMEM;
-	}
-
-	usb_log_debug2("EP(%p): Creating for %p", ehci_ep, ep);
-	link_initialize(&ehci_ep->link);
-	qh_init(ehci_ep->qh, ep);
-	endpoint_set_hc_data(
-	    ep, ehci_ep, ehci_ep_toggle_get, ehci_ep_toggle_set);
-	hc_enqueue_endpoint(hc, ep);
-	return EOK;
-}
-
-/** Disposes hcd endpoint structure
- *
- * @param[in] hcd driver using this instance.
- * @param[in] ep endpoint structure.
- */
-void ehci_endpoint_fini(hcd_t *hcd, endpoint_t *ep)
-{
-	assert(hcd);
-	assert(ep);
-	hc_t *hc = hcd_get_driver_data(hcd);
-
-	ehci_endpoint_t *instance = ehci_endpoint_get(ep);
-	hc_dequeue_endpoint(hc, ep);
-	endpoint_clear_hc_data(ep);
-	usb_log_debug2("EP(%p): Destroying for %p", instance, ep);
-	if (instance) {
-		free32(instance->qh);
-		free(instance);
-	}
-}
-/**
- * @}
- */
-
Index: uspace/drv/bus/usb/ehci/ehci_endpoint.h
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_endpoint.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ 	(revision )
@@ -1,75 +1,0 @@
-/*
- * Copyright (c) 2011 Jan Vesely
- * 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 drvusbehci
- * @{
- */
-/** @file
- * @brief EHCI driver
- */
-#ifndef DRV_EHCI_HCD_ENDPOINT_H
-#define DRV_EHCI_HCD_ENDPOINT_H
-
-#include <assert.h>
-#include <adt/list.h>
-#include <usb/host/endpoint.h>
-#include <usb/host/hcd.h>
-
-#include "hw_struct/queue_head.h"
-#include "hw_struct/transfer_descriptor.h"
-
-/** Connector structure linking ED to to prepared TD. */
-typedef struct ehci_endpoint {
-	/** EHCI endpoint descriptor */
-	qh_t *qh;
-	/** Linked list used by driver software */
-	link_t link;
-} ehci_endpoint_t;
-
-int ehci_endpoint_init(hcd_t *hcd, endpoint_t *ep);
-void ehci_endpoint_fini(hcd_t *hcd, endpoint_t *ep);
-
-/** Get and convert assigned ehci_endpoint_t structure
- * @param[in] ep USBD endpoint structure.
- * @return Pointer to assigned hcd endpoint structure
- */
-static inline ehci_endpoint_t * ehci_endpoint_get(const endpoint_t *ep)
-{
-	assert(ep);
-	return ep->hc_data.data;
-}
-
-static inline ehci_endpoint_t * ehci_endpoint_list_instance(link_t *l)
-{
-	return list_get_instance(l, ehci_endpoint_t, link);
-}
-
-#endif
-/**
- * @}
- */
-
Index: uspace/drv/bus/usb/ehci/ehci_rh.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_rh.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ehci/ehci_rh.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -144,10 +144,6 @@
 	assert(instance);
 	assert(batch);
-	const usb_target_t target = {{
-		.address = batch->ep->address,
-		.endpoint = batch->ep->endpoint,
-	}};
-	batch->error = virthub_base_request(&instance->base, target,
-	    usb_transfer_batch_direction(batch), (void*)batch->setup_buffer,
+	batch->error = virthub_base_request(&instance->base, batch->target,
+	    batch->dir, (void*) batch->setup.buffer,
 	    batch->buffer, batch->buffer_size, &batch->transfered_size);
 	if (batch->error == ENAK) {
@@ -160,8 +156,7 @@
 		instance->unfinished_interrupt_transfer = batch;
 	} else {
-		usb_transfer_batch_finish(batch, NULL);
-		usb_transfer_batch_destroy(batch);
 		usb_log_debug("RH(%p): BATCH(%p) virtual request complete: %s",
 		    instance, batch, str_error(batch->error));
+		usb_transfer_batch_finish(batch);
 	}
 	return EOK;
@@ -183,14 +178,8 @@
 	    instance, batch);
 	if (batch) {
-		const usb_target_t target = {{
-			.address = batch->ep->address,
-			.endpoint = batch->ep->endpoint,
-		}};
-		batch->error = virthub_base_request(&instance->base, target,
-		    usb_transfer_batch_direction(batch),
-		    (void*)batch->setup_buffer,
+		batch->error = virthub_base_request(&instance->base, batch->target,
+		    batch->dir, (void*) batch->setup.buffer,
 		    batch->buffer, batch->buffer_size, &batch->transfered_size);
-		usb_transfer_batch_finish(batch, NULL);
-		usb_transfer_batch_destroy(batch);
+		usb_transfer_batch_finish(batch);
 	}
 	return EOK;
Index: uspace/drv/bus/usb/ehci/endpoint_list.h
===================================================================
--- uspace/drv/bus/usb/ehci/endpoint_list.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ehci/endpoint_list.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -40,5 +40,5 @@
 #include <usb/host/utils/malloc32.h>
 
-#include "ehci_endpoint.h"
+#include "ehci_bus.h"
 #include "hw_struct/queue_head.h"
 
Index: uspace/drv/bus/usb/ehci/hc.c
===================================================================
--- uspace/drv/bus/usb/ehci/hc.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ehci/hc.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -89,5 +89,4 @@
 };
 
-static void hc_start(hc_t *instance);
 static int hc_init_memory(hc_t *instance);
 
@@ -98,8 +97,10 @@
  * @return Error code.
  */
-int ehci_hc_gen_irq_code(irq_code_t *code, const hw_res_list_parsed_t *hw_res)
+int ehci_hc_gen_irq_code(irq_code_t *code, hcd_t *hcd, const hw_res_list_parsed_t *hw_res)
 {
 	assert(code);
 	assert(hw_res);
+
+	hc_t *instance = hcd_get_driver_data(hcd);
 
 	if (hw_res->irqs.count != 1 || hw_res->mem_ranges.count != 1)
@@ -128,15 +129,7 @@
 
 	memcpy(code->cmds, ehci_irq_commands, sizeof(ehci_irq_commands));
-	ehci_caps_regs_t *caps = NULL;
-
-	int ret = pio_enable_range(&regs, (void**)&caps);
-	if (ret != EOK) {
-		free(code->ranges);
-		free(code->cmds);
-		return ret;
-	}
 
 	ehci_regs_t *registers =
-	    (ehci_regs_t *)(RNGABSPTR(regs) + EHCI_RD8(caps->caplength));
+		(ehci_regs_t *)(RNGABSPTR(regs) + EHCI_RD8(instance->caps->caplength));
 	code->cmds[0].addr = (void *) &registers->usbsts;
 	code->cmds[3].addr = (void *) &registers->usbsts;
@@ -156,5 +149,5 @@
  * @return Error code
  */
-int hc_init(hc_t *instance, const hw_res_list_parsed_t *hw_res, bool interrupts)
+int hc_init(hc_t *instance, const hw_res_list_parsed_t *hw_res)
 {
 	assert(instance);
@@ -172,4 +165,5 @@
 		return ret;
 	}
+
 	usb_log_info("HC(%p): Device registers at %"PRIx64" (%zuB) accessible.",
 	    instance, hw_res->mem_ranges.ranges[0].address.absolute,
@@ -195,7 +189,6 @@
 	ehci_rh_init(
 	    &instance->rh, instance->caps, instance->registers, "ehci rh");
-	usb_log_debug("HC(%p): Starting HW.", instance);
-	hc_start(instance);
-
+
+	ehci_bus_init(&instance->bus, instance);
 	return EOK;
 }
@@ -222,5 +215,5 @@
 	ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ep);
 	usb_log_debug("HC(%p) enqueue EP(%d:%d:%s:%s)\n", instance,
-	    ep->address, ep->endpoint,
+	    ep->device->address, ep->endpoint,
 	    usb_str_transfer_type_short(ep->transfer_type),
 	    usb_str_direction(ep->direction));
@@ -246,5 +239,5 @@
 	ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ep);
 	usb_log_debug("HC(%p) dequeue EP(%d:%d:%s:%s)\n", instance,
-	    ep->address, ep->endpoint,
+	    ep->device->address, ep->endpoint,
 	    usb_str_transfer_type_short(ep->transfer_type),
 	    usb_str_direction(ep->direction));
@@ -298,12 +291,15 @@
 
 	/* Check for root hub communication */
-	if (batch->ep->address == ehci_rh_get_address(&instance->rh)) {
+	if (batch->target.address == ehci_rh_get_address(&instance->rh)) {
 		usb_log_debug("HC(%p): Scheduling BATCH(%p) for RH(%p)",
 		    instance, batch, &instance->rh);
 		return ehci_rh_schedule(&instance->rh, batch);
 	}
+
 	ehci_transfer_batch_t *ehci_batch = ehci_transfer_batch_get(batch);
-	if (!ehci_batch)
-		return ENOMEM;
+
+	const int err = ehci_transfer_batch_prepare(ehci_batch);
+	if (err)
+		return err;
 
 	fibril_mutex_lock(&instance->guard);
@@ -350,7 +346,7 @@
 			    ehci_transfer_batch_from_link(current);
 
-			if (ehci_transfer_batch_is_complete(batch)) {
+			if (ehci_transfer_batch_check_completed(batch)) {
 				list_remove(current);
-				ehci_transfer_batch_finish_dispose(batch);
+				usb_transfer_batch_finish(&batch->base);
 			}
 		}
@@ -368,7 +364,9 @@
  * @param[in] instance EHCI hc driver structure.
  */
-void hc_start(hc_t *instance)
-{
-	assert(instance);
+int hc_start(hc_t *instance, bool interrupts)
+{
+	assert(instance);
+	usb_log_debug("HC(%p): Starting HW.", instance);
+
 	/* Turn off the HC if it's running, Reseting a running device is
 	 * undefined */
@@ -435,4 +433,6 @@
 	EHCI_WR(instance->registers->usbsts, EHCI_RD(instance->registers->usbsts));
 	EHCI_WR(instance->registers->usbintr, EHCI_USED_INTERRUPTS);
+
+	return EOK;
 }
 
Index: uspace/drv/bus/usb/ehci/hc.h
===================================================================
--- uspace/drv/bus/usb/ehci/hc.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ehci/hc.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -80,7 +80,11 @@
 	/** USB hub emulation structure */
 	ehci_rh_t rh;
+
+	/** USB bookkeeping */
+	ehci_bus_t bus;
 } hc_t;
 
-int hc_init(hc_t *instance, const hw_res_list_parsed_t *hw_res, bool interrupts);
+int hc_init(hc_t *instance, const hw_res_list_parsed_t *hw_res);
+int hc_start(hc_t *instance, bool interrupts);
 void hc_fini(hc_t *instance);
 
@@ -88,5 +92,5 @@
 void hc_dequeue_endpoint(hc_t *instance, const endpoint_t *ep);
 
-int ehci_hc_gen_irq_code(irq_code_t *code, const hw_res_list_parsed_t *hw_res);
+int ehci_hc_gen_irq_code(irq_code_t *code, hcd_t *hcd, const hw_res_list_parsed_t *hw_res);
 
 void ehci_hc_interrupt(hcd_t *hcd, uint32_t status);
Index: uspace/drv/bus/usb/ehci/hw_struct/queue_head.c
===================================================================
--- uspace/drv/bus/usb/ehci/hw_struct/queue_head.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ehci/hw_struct/queue_head.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -37,4 +37,5 @@
 #include <mem.h>
 #include <macros.h>
+#include <usb/host/bus.h>
 
 #include "mem_access.h"
@@ -65,5 +66,5 @@
 	assert(ep->speed < ARRAY_SIZE(speed));
 	EHCI_MEM32_WR(instance->ep_char,
-	    QH_EP_CHAR_ADDR_SET(ep->address) |
+	    QH_EP_CHAR_ADDR_SET(ep->device->address) |
 	    QH_EP_CHAR_EP_SET(ep->endpoint) |
 	    speed[ep->speed] |
@@ -81,6 +82,6 @@
 	if (ep->speed != USB_SPEED_HIGH) {
 		ep_cap |=
-		    QH_EP_CAP_TT_PORT_SET(ep->tt.port) |
-		    QH_EP_CAP_TT_ADDR_SET(ep->tt.address);
+		    QH_EP_CAP_TT_PORT_SET(ep->device->tt.port) |
+		    QH_EP_CAP_TT_ADDR_SET(ep->device->tt.address);
 	}
 	if (ep->transfer_type == USB_TRANSFER_INTERRUPT) {
Index: uspace/drv/bus/usb/ehci/main.c
===================================================================
--- uspace/drv/bus/usb/ehci/main.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ehci/main.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -48,22 +48,22 @@
 #include "res.h"
 #include "hc.h"
-#include "ehci_endpoint.h"
 
 #define NAME "ehci"
 
-static int ehci_driver_init(hcd_t *, const hw_res_list_parsed_t *, bool);
+static int ehci_driver_init(hcd_t *, const hw_res_list_parsed_t *, ddf_dev_t *);
+static int ehci_driver_claim(hcd_t *, ddf_dev_t *);
+static int ehci_driver_start(hcd_t *, bool);
 static void ehci_driver_fini(hcd_t *);
 
 static const ddf_hc_driver_t ehci_hc_driver = {
-	.claim = disable_legacy,
-	.hc_speed = USB_SPEED_HIGH,
+	.name = "EHCI-PCI",
+	.init = ehci_driver_init,
 	.irq_code_gen = ehci_hc_gen_irq_code,
-	.init = ehci_driver_init,
+	.claim = ehci_driver_claim,
+	.start = ehci_driver_start,
+	.setup_root_hub = hcd_setup_virtual_root_hub,
 	.fini = ehci_driver_fini,
-	.name = "EHCI-PCI",
 	.ops = {
 		.schedule       = ehci_hc_schedule,
-		.ep_add_hook    = ehci_endpoint_init,
-		.ep_remove_hook = ehci_endpoint_fini,
 		.irq_hook       = ehci_hc_interrupt,
 		.status_hook    = ehci_hc_status,
@@ -72,6 +72,5 @@
 
 
-static int ehci_driver_init(hcd_t *hcd, const hw_res_list_parsed_t *res,
-    bool irq)
+static int ehci_driver_init(hcd_t *hcd, const hw_res_list_parsed_t *res, ddf_dev_t *device)
 {
 	assert(hcd);
@@ -82,11 +81,26 @@
 		return ENOMEM;
 
-	const int ret = hc_init(instance, res, irq);
+	const int ret = hc_init(instance, res);
 	if (ret == EOK) {
-		hcd_set_implementation(hcd, instance, &ehci_hc_driver.ops);
+		hcd_set_implementation(hcd, instance, &ehci_hc_driver.ops, &instance->bus.base.base);
 	} else {
 		free(instance);
 	}
 	return ret;
+}
+
+static int ehci_driver_claim(hcd_t *hcd, ddf_dev_t *dev)
+{
+	hc_t *instance = hcd_get_driver_data(hcd);
+	assert(instance);
+
+	return disable_legacy(instance, dev);
+}
+
+static int ehci_driver_start(hcd_t *hcd, bool irq) {
+	hc_t *instance = hcd_get_driver_data(hcd);
+	assert(instance);
+
+	return hc_start(instance, irq);
 }
 
@@ -99,5 +113,5 @@
 
 	free(hc);
-	hcd_set_implementation(hcd, NULL, NULL);
+	hcd_set_implementation(hcd, NULL, NULL, NULL);
 }
 
@@ -116,7 +130,19 @@
 }
 
+static int ehci_fun_online(ddf_fun_t *fun)
+{
+	return hcd_ddf_device_online(fun);
+}
+
+static int ehci_fun_offline(ddf_fun_t *fun)
+{
+	return hcd_ddf_device_offline(fun);
+}
+
 
 static const driver_ops_t ehci_driver_ops = {
 	.dev_add = ehci_dev_add,
+	.fun_online = ehci_fun_online,
+	.fun_offline = ehci_fun_offline
 };
 
Index: uspace/drv/bus/usb/ehci/res.c
===================================================================
--- uspace/drv/bus/usb/ehci/res.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ehci/res.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -172,5 +172,5 @@
 }
 
-int disable_legacy(ddf_dev_t *device)
+int disable_legacy(hc_t *hc, ddf_dev_t *device)
 {
 	assert(device);
@@ -182,34 +182,6 @@
 	usb_log_debug("Disabling EHCI legacy support.\n");
 
-	hw_res_list_parsed_t res;
-	hw_res_list_parsed_init(&res);
-	int ret = hw_res_get_list_parsed(parent_sess, &res, 0);
-	if (ret != EOK) {
-		usb_log_error("Failed to get resource list: %s\n",
-		    str_error(ret));
-		goto clean;
-	}
-
-	if (res.mem_ranges.count < 1) {
-		usb_log_error("Incorrect mem range count: %zu",
-		    res.mem_ranges.count);
-		ret = EINVAL;
-		goto clean;
-	}
-
-	/* Map EHCI registers */
-	void *regs = NULL;
-	ret = pio_enable_range(&res.mem_ranges.ranges[0], &regs);
-	if (ret != EOK) {
-		usb_log_error("Failed to map registers %p: %s.\n",
-		    RNGABSPTR(res.mem_ranges.ranges[0]), str_error(ret));
-		goto clean;
-	}
-
-	usb_log_debug("Registers mapped at: %p.\n", regs);
-
-	ehci_caps_regs_t *ehci_caps = regs;
-
-	const uint32_t hcc_params = EHCI_RD(ehci_caps->hccparams);
+
+	const uint32_t hcc_params = EHCI_RD(hc->caps->hccparams);
 	usb_log_debug2("Value of hcc params register: %x.\n", hcc_params);
 
@@ -220,5 +192,5 @@
 	usb_log_debug2("Value of EECP: %x.\n", eecp);
 
-	ret = disable_extended_caps(parent_sess, eecp);
+	int ret = disable_extended_caps(parent_sess, eecp);
 	if (ret != EOK) {
 		usb_log_error("Failed to disable extended capabilities: %s.\n",
@@ -227,6 +199,5 @@
 	}
 clean:
-	//TODO unmap registers
-	hw_res_list_parsed_clean(&res);
+	async_hangup(parent_sess);
 	return ret;
 }
Index: uspace/drv/bus/usb/ehci/res.h
===================================================================
--- uspace/drv/bus/usb/ehci/res.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ehci/res.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -39,5 +39,7 @@
 #include <device/hw_res_parsed.h>
 
-extern int disable_legacy(ddf_dev_t *);
+#include "hc.h"
+
+extern int disable_legacy(hc_t *, ddf_dev_t *);
 
 #endif
Index: uspace/drv/bus/usb/ohci/Makefile
===================================================================
--- uspace/drv/bus/usb/ohci/Makefile	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ohci/Makefile	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -38,5 +38,5 @@
 	main.c \
 	ohci_batch.c \
-	ohci_endpoint.c \
+	ohci_bus.c \
 	ohci_rh.c \
 	hw_struct/endpoint_descriptor.c \
Index: uspace/drv/bus/usb/ohci/endpoint_list.h
===================================================================
--- uspace/drv/bus/usb/ohci/endpoint_list.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ohci/endpoint_list.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -41,5 +41,5 @@
 #include <usb/host/utils/malloc32.h>
 
-#include "ohci_endpoint.h"
+#include "ohci_bus.h"
 #include "hw_struct/endpoint_descriptor.h"
 
Index: uspace/drv/bus/usb/ohci/hc.c
===================================================================
--- uspace/drv/bus/usb/ohci/hc.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ohci/hc.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -47,5 +47,5 @@
 #include <usb/usb.h>
 
-#include "ohci_endpoint.h"
+#include "ohci_bus.h"
 #include "ohci_batch.h"
 
@@ -89,6 +89,4 @@
 };
 
-static void hc_gain_control(hc_t *instance);
-static void hc_start(hc_t *instance);
 static int hc_init_transfer_lists(hc_t *instance);
 static int hc_init_memory(hc_t *instance);
@@ -103,5 +101,5 @@
  * @return Error code.
  */
-int ohci_hc_gen_irq_code(irq_code_t *code, const hw_res_list_parsed_t *hw_res)
+int ohci_hc_gen_irq_code(irq_code_t *code, hcd_t *hcd, const hw_res_list_parsed_t *hw_res)
 {
 	assert(code);
@@ -151,5 +149,5 @@
  * @return Error code
  */
-int hc_init(hc_t *instance, const hw_res_list_parsed_t *hw_res, bool interrupts)
+int hc_init(hc_t *instance, const hw_res_list_parsed_t *hw_res)
 {
 	assert(instance);
@@ -172,5 +170,4 @@
 	list_initialize(&instance->pending_batches);
 	fibril_mutex_initialize(&instance->guard);
-	instance->hw_interrupts = interrupts;
 
 	ret = hc_init_memory(instance);
@@ -181,9 +178,4 @@
 		return ret;
 	}
-
-	hc_gain_control(instance);
-
-	ohci_rh_init(&instance->rh, instance->registers, "ohci rh");
-	hc_start(instance);
 
 	return EOK;
@@ -295,5 +287,5 @@
 
 	/* Check for root hub communication */
-	if (batch->ep->address == ohci_rh_get_address(&instance->rh)) {
+	if (batch->target.address == ohci_rh_get_address(&instance->rh)) {
 		usb_log_debug("OHCI root hub request.\n");
 		return ohci_rh_schedule(&instance->rh, batch);
@@ -302,4 +294,8 @@
 	if (!ohci_batch)
 		return ENOMEM;
+
+	const int err = ohci_transfer_batch_prepare(ohci_batch);
+	if (err)
+		return err;
 
 	fibril_mutex_lock(&instance->guard);
@@ -354,7 +350,7 @@
 			    ohci_transfer_batch_from_link(current);
 
-			if (ohci_transfer_batch_is_complete(batch)) {
+			if (ohci_transfer_batch_check_completed(batch)) {
 				list_remove(current);
-				ohci_transfer_batch_finish_dispose(batch);
+				usb_transfer_batch_finish(&batch->base);
 			}
 
@@ -443,4 +439,6 @@
 void hc_start(hc_t *instance)
 {
+	ohci_rh_init(&instance->rh, instance->registers, "ohci rh");
+
 	/* OHCI guide page 42 */
 	assert(instance);
Index: uspace/drv/bus/usb/ohci/hc.h
===================================================================
--- uspace/drv/bus/usb/ohci/hc.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ohci/hc.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -52,4 +52,5 @@
 #include "ohci_regs.h"
 #include "ohci_rh.h"
+#include "ohci_bus.h"
 #include "endpoint_list.h"
 #include "hw_struct/hcca.h"
@@ -59,4 +60,5 @@
 	/** Memory mapped I/O registers area */
 	ohci_regs_t *registers;
+	
 	/** Host controller communication area structure */
 	hcca_t *hcca;
@@ -64,4 +66,5 @@
 	/** Transfer schedules */
 	endpoint_list_t lists[4];
+
 	/** List of active transfers */
 	list_t pending_batches;
@@ -78,7 +81,12 @@
 	/** USB hub emulation structure */
 	ohci_rh_t rh;
+
+	/** USB bookkeeping */
+	ohci_bus_t bus;
 } hc_t;
 
-extern int hc_init(hc_t *, const hw_res_list_parsed_t *, bool);
+extern int hc_init(hc_t *, const hw_res_list_parsed_t *);
+extern void hc_gain_control(hc_t *instance);
+extern void hc_start(hc_t *instance);
 extern void hc_fini(hc_t *);
 
@@ -86,5 +94,5 @@
 extern void hc_dequeue_endpoint(hc_t *, const endpoint_t *);
 
-int ohci_hc_gen_irq_code(irq_code_t *code, const hw_res_list_parsed_t *hw_res);
+int ohci_hc_gen_irq_code(irq_code_t *code, hcd_t *hcd, const hw_res_list_parsed_t *hw_res);
 
 extern void ohci_hc_interrupt(hcd_t *, uint32_t);
Index: uspace/drv/bus/usb/ohci/hw_struct/endpoint_descriptor.c
===================================================================
--- uspace/drv/bus/usb/ohci/hw_struct/endpoint_descriptor.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ohci/hw_struct/endpoint_descriptor.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -40,4 +40,6 @@
 #include <usb/usb.h>
 #include <usb/host/utils/malloc32.h>
+#include <usb/host/endpoint.h>
+#include <usb/host/bus.h>
 
 #include "mem_access.h"
@@ -79,5 +81,5 @@
 	/* Status: address, endpoint nr, direction mask and max packet size. */
 	OHCI_MEM32_WR(instance->status,
-	    ((ep->address & ED_STATUS_FA_MASK) << ED_STATUS_FA_SHIFT)
+	    ((ep->device->address & ED_STATUS_FA_MASK) << ED_STATUS_FA_SHIFT)
 	    | ((ep->endpoint & ED_STATUS_EN_MASK) << ED_STATUS_EN_SHIFT)
 	    | ((dir[ep->direction] & ED_STATUS_D_MASK) << ED_STATUS_D_SHIFT)
Index: uspace/drv/bus/usb/ohci/main.c
===================================================================
--- uspace/drv/bus/usb/ohci/main.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ohci/main.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -45,19 +45,22 @@
 
 #include "hc.h"
+#include "ohci_bus.h"
 
 #define NAME "ohci"
-static int ohci_driver_init(hcd_t *, const hw_res_list_parsed_t *, bool);
+static int ohci_driver_init(hcd_t *, const hw_res_list_parsed_t *, ddf_dev_t *);
+static int ohci_driver_start(hcd_t *, bool);
+static int ohci_driver_claim(hcd_t *, ddf_dev_t *);
 static void ohci_driver_fini(hcd_t *);
 
 static const ddf_hc_driver_t ohci_hc_driver = {
-        .hc_speed = USB_SPEED_FULL,
         .irq_code_gen = ohci_hc_gen_irq_code,
         .init = ohci_driver_init,
+        .claim = ohci_driver_claim,
+        .start = ohci_driver_start,
+	.setup_root_hub = hcd_setup_virtual_root_hub,
         .fini = ohci_driver_fini,
         .name = "OHCI",
 	.ops = {
                 .schedule       = ohci_hc_schedule,
-                .ep_add_hook    = ohci_endpoint_init,
-                .ep_remove_hook = ohci_endpoint_fini,
                 .irq_hook       = ohci_hc_interrupt,
                 .status_hook    = ohci_hc_status,
@@ -66,6 +69,8 @@
 
 
-static int ohci_driver_init(hcd_t *hcd, const hw_res_list_parsed_t *res, bool irq)
+static int ohci_driver_init(hcd_t *hcd, const hw_res_list_parsed_t *res, ddf_dev_t *device)
 {
+	int err;
+
 	assert(hcd);
 	assert(hcd_get_driver_data(hcd) == NULL);
@@ -75,11 +80,37 @@
 		return ENOMEM;
 
-	const int ret = hc_init(instance, res, irq);
-	if (ret == EOK) {
-		hcd_set_implementation(hcd, instance, &ohci_hc_driver.ops);
-	} else {
-		free(instance);
-	}
-	return ret;
+	if ((err = hc_init(instance, res)) != EOK)
+		goto err;
+
+	if ((err = ohci_bus_init(&instance->bus, instance)))
+		goto err;
+
+	hcd_set_implementation(hcd, instance, &ohci_hc_driver.ops, &instance->bus.base.base);
+
+	return EOK;
+
+err:
+	free(instance);
+	return err;
+}
+
+static int ohci_driver_claim(hcd_t *hcd, ddf_dev_t *dev)
+{
+	hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	hc_gain_control(hc);
+
+	return EOK;
+}
+
+static int ohci_driver_start(hcd_t *hcd, bool interrupts)
+{
+	hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	hc->hw_interrupts = interrupts;
+	hc_start(hc);
+	return EOK;
 }
 
@@ -91,5 +122,5 @@
 		hc_fini(hc);
 
-	hcd_set_implementation(hcd, NULL, NULL);
+	hcd_set_implementation(hcd, NULL, NULL, NULL);
 	free(hc);
 }
@@ -107,6 +138,19 @@
 }
 
+static int ohci_fun_online(ddf_fun_t *fun)
+{
+	return hcd_ddf_device_online(fun);
+}
+
+static int ohci_fun_offline(ddf_fun_t *fun)
+{
+	return hcd_ddf_device_offline(fun);
+}
+
+
 static const driver_ops_t ohci_driver_ops = {
 	.dev_add = ohci_dev_add,
+	.fun_online = ohci_fun_online,
+	.fun_offline = ohci_fun_offline
 };
 
Index: uspace/drv/bus/usb/ohci/ohci_batch.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_batch.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ohci/ohci_batch.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -45,7 +45,7 @@
 
 #include "ohci_batch.h"
-#include "ohci_endpoint.h"
-
-static void (*const batch_setup[])(ohci_transfer_batch_t*, usb_direction_t);
+#include "ohci_bus.h"
+
+static void (*const batch_setup[])(ohci_transfer_batch_t*);
 
 /** Safely destructs ohci_transfer_batch_t structure
@@ -53,8 +53,7 @@
  * @param[in] ohci_batch Instance to destroy.
  */
-static void ohci_transfer_batch_dispose(ohci_transfer_batch_t *ohci_batch)
-{
-	if (!ohci_batch)
-		return;
+void ohci_transfer_batch_destroy(ohci_transfer_batch_t *ohci_batch)
+{
+	assert(ohci_batch);
 	if (ohci_batch->tds) {
 		const ohci_endpoint_t *ohci_ep =
@@ -67,20 +66,6 @@
 		free(ohci_batch->tds);
 	}
-	usb_transfer_batch_destroy(ohci_batch->usb_batch);
 	free32(ohci_batch->device_buffer);
 	free(ohci_batch);
-}
-
-/** Finishes usb_transfer_batch and destroys the structure.
- *
- * @param[in] uhci_batch Instance to finish and destroy.
- */
-void ohci_transfer_batch_finish_dispose(ohci_transfer_batch_t *ohci_batch)
-{
-	assert(ohci_batch);
-	assert(ohci_batch->usb_batch);
-	usb_transfer_batch_finish(ohci_batch->usb_batch,
-	    ohci_batch->device_buffer + ohci_batch->usb_batch->setup_size);
-	ohci_transfer_batch_dispose(ohci_batch);
 }
 
@@ -90,12 +75,8 @@
  * @return Valid pointer if all structures were successfully created,
  * NULL otherwise.
- *
- * Determines the number of needed transfer descriptors (TDs).
- * Prepares a transport buffer (that is accessible by the hardware).
- * Initializes parameters needed for the transfer and callback.
- */
-ohci_transfer_batch_t * ohci_transfer_batch_get(usb_transfer_batch_t *usb_batch)
-{
-	assert(usb_batch);
+ */
+ohci_transfer_batch_t * ohci_transfer_batch_create(endpoint_t *ep)
+{
+	assert(ep);
 
 	ohci_transfer_batch_t *ohci_batch =
@@ -103,9 +84,30 @@
 	if (!ohci_batch) {
 		usb_log_error("Failed to allocate OHCI batch data.");
-		goto dispose;
-	}
+		return NULL;
+	}
+
+	usb_transfer_batch_init(&ohci_batch->base, ep);
 	link_initialize(&ohci_batch->link);
-	ohci_batch->td_count =
-	    (usb_batch->buffer_size + OHCI_TD_MAX_TRANSFER - 1)
+
+	return ohci_batch;
+}
+
+/** Prepares a batch to be sent.
+ *
+ * Determines the number of needed transfer descriptors (TDs).
+ * Prepares a transport buffer (that is accessible by the hardware).
+ * Initializes parameters needed for the transfer and callback.
+ */
+int ohci_transfer_batch_prepare(ohci_transfer_batch_t *ohci_batch)
+{
+	assert(ohci_batch);
+
+	const size_t setup_size = (ohci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
+		? USB_SETUP_PACKET_SIZE
+		: 0;
+
+	usb_transfer_batch_t *usb_batch = &ohci_batch->base;
+
+	ohci_batch->td_count = (usb_batch->buffer_size + OHCI_TD_MAX_TRANSFER - 1)
 	    / OHCI_TD_MAX_TRANSFER;
 	/* Control transfer need Setup and Status stage */
@@ -118,5 +120,5 @@
 	if (!ohci_batch->tds) {
 		usb_log_error("Failed to allocate OHCI transfer descriptors.");
-		goto dispose;
+		return ENOMEM;
 	}
 
@@ -129,8 +131,7 @@
 		if (!ohci_batch->tds[i]) {
 			usb_log_error("Failed to allocate TD %d.", i);
-			goto dispose;
+			return ENOMEM;
 		}
 	}
-
 
 	/* NOTE: OHCI is capable of handling buffer that crosses page boundaries
@@ -138,31 +139,25 @@
 	 * than two pages (the first page is computed using start pointer, the
 	 * other using the end pointer) */
-        if (usb_batch->setup_size + usb_batch->buffer_size > 0) {
+        if (setup_size + usb_batch->buffer_size > 0) {
 		/* Use one buffer for setup and data stage */
 		ohci_batch->device_buffer =
-		    malloc32(usb_batch->setup_size + usb_batch->buffer_size);
+		    malloc32(setup_size + usb_batch->buffer_size);
 		if (!ohci_batch->device_buffer) {
 			usb_log_error("Failed to allocate device buffer");
-			goto dispose;
+			return ENOMEM;
 		}
 		/* Copy setup data */
-                memcpy(ohci_batch->device_buffer, usb_batch->setup_buffer,
-		    usb_batch->setup_size);
+                memcpy(ohci_batch->device_buffer, usb_batch->setup.buffer, setup_size);
 		/* Copy generic data */
 		if (usb_batch->ep->direction != USB_DIRECTION_IN)
 			memcpy(
-			    ohci_batch->device_buffer + usb_batch->setup_size,
+			    ohci_batch->device_buffer + setup_size,
 			    usb_batch->buffer, usb_batch->buffer_size);
         }
-	ohci_batch->usb_batch = usb_batch;
-
-	const usb_direction_t dir = usb_transfer_batch_direction(usb_batch);
+
 	assert(batch_setup[usb_batch->ep->transfer_type]);
-	batch_setup[usb_batch->ep->transfer_type](ohci_batch, dir);
-
-	return ohci_batch;
-dispose:
-	ohci_transfer_batch_dispose(ohci_batch);
-	return NULL;
+	batch_setup[usb_batch->ep->transfer_type](ohci_batch);
+
+	return EOK;
 }
 
@@ -176,8 +171,7 @@
  * completes with the last TD.
  */
-bool ohci_transfer_batch_is_complete(const ohci_transfer_batch_t *ohci_batch)
-{
-	assert(ohci_batch);
-	assert(ohci_batch->usb_batch);
+bool ohci_transfer_batch_check_completed(ohci_transfer_batch_t *ohci_batch)
+{
+	assert(ohci_batch);
 
 	usb_log_debug("Batch %p checking %zu td(s) for completion.\n",
@@ -194,6 +188,5 @@
 
 	/* Assume all data got through */
-	ohci_batch->usb_batch->transfered_size =
-	    ohci_batch->usb_batch->buffer_size;
+	ohci_batch->base.transfered_size = ohci_batch->base.buffer_size;
 
 	/* Assume we will leave the last(unused) TD behind */
@@ -207,6 +200,6 @@
 		    ohci_batch->tds[i]->next, ohci_batch->tds[i]->be);
 
-		ohci_batch->usb_batch->error = td_error(ohci_batch->tds[i]);
-		if (ohci_batch->usb_batch->error == EOK) {
+		ohci_batch->base.error = td_error(ohci_batch->tds[i]);
+		if (ohci_batch->base.error == EOK) {
 			/* If the TD got all its data through, it will report
 			 * 0 bytes remain, the sole exception is INPUT with
@@ -221,5 +214,5 @@
 			 * we leave the very last(unused) TD behind.
 			 */
-			ohci_batch->usb_batch->transfered_size
+			ohci_batch->base.transfered_size
 			    -= td_remain_size(ohci_batch->tds[i]);
 		} else {
@@ -252,9 +245,12 @@
 		}
 	}
-	assert(ohci_batch->usb_batch->transfered_size <=
-	    ohci_batch->usb_batch->buffer_size);
+	assert(ohci_batch->base.transfered_size <=
+	    ohci_batch->base.buffer_size);
+
+	if (ohci_batch->base.dir == USB_DIRECTION_IN)
+		memcpy(ohci_batch->base.buffer, ohci_batch->device_buffer, ohci_batch->base.transfered_size);
 
 	/* Store the remaining TD */
-	ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ohci_batch->usb_batch->ep);
+	ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ohci_batch->base.ep);
 	assert(ohci_ep);
 	ohci_ep->td = ohci_batch->tds[leave_td];
@@ -286,9 +282,11 @@
  * Status stage with toggle 1 and direction supplied by parameter.
  */
-static void batch_control(ohci_transfer_batch_t *ohci_batch, usb_direction_t dir)
-{
-	assert(ohci_batch);
-	assert(ohci_batch->usb_batch);
+static void batch_control(ohci_transfer_batch_t *ohci_batch)
+{
+	assert(ohci_batch);
+
+	usb_direction_t dir = ohci_batch->base.dir;
 	assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
+
 	usb_log_debug("Using ED(%p): %08x:%08x:%08x:%08x.\n", ohci_batch->ed,
 	    ohci_batch->ed->status, ohci_batch->ed->td_tail,
@@ -307,9 +305,9 @@
 	td_init(
 	    ohci_batch->tds[0], ohci_batch->tds[1], USB_DIRECTION_BOTH,
-	    buffer, ohci_batch->usb_batch->setup_size, toggle);
+	    buffer, USB_SETUP_PACKET_SIZE, toggle);
 	usb_log_debug("Created CONTROL SETUP TD: %08x:%08x:%08x:%08x.\n",
 	    ohci_batch->tds[0]->status, ohci_batch->tds[0]->cbp,
 	    ohci_batch->tds[0]->next, ohci_batch->tds[0]->be);
-	buffer += ohci_batch->usb_batch->setup_size;
+	buffer += USB_SETUP_PACKET_SIZE;
 
 	/* Data stage */
@@ -361,8 +359,9 @@
  * OHCI hw in ED.
  */
-static void batch_data(ohci_transfer_batch_t *ohci_batch, usb_direction_t dir)
-{
-	assert(ohci_batch);
-	assert(ohci_batch->usb_batch);
+static void batch_data(ohci_transfer_batch_t *ohci_batch)
+{
+	assert(ohci_batch);
+
+	usb_direction_t dir = ohci_batch->base.dir;
 	assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
 	usb_log_debug("Using ED(%p): %08x:%08x:%08x:%08x.\n", ohci_batch->ed,
@@ -401,5 +400,5 @@
 
 /** Transfer setup table. */
-static void (*const batch_setup[])(ohci_transfer_batch_t*, usb_direction_t) =
+static void (*const batch_setup[])(ohci_transfer_batch_t*) =
 {
 	[USB_TRANSFER_CONTROL] = batch_control,
Index: uspace/drv/bus/usb/ohci/ohci_batch.h
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_batch.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ohci/ohci_batch.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -45,4 +45,6 @@
 /** OHCI specific data required for USB transfer */
 typedef struct ohci_transfer_batch {
+	usb_transfer_batch_t base;
+
 	/** Link */
 	link_t link;
@@ -59,8 +61,9 @@
 } ohci_transfer_batch_t;
 
-ohci_transfer_batch_t * ohci_transfer_batch_get(usb_transfer_batch_t *batch);
-bool ohci_transfer_batch_is_complete(const ohci_transfer_batch_t *batch);
+ohci_transfer_batch_t * ohci_transfer_batch_create(endpoint_t *batch);
+int ohci_transfer_batch_prepare(ohci_transfer_batch_t *ohci_batch);
 void ohci_transfer_batch_commit(const ohci_transfer_batch_t *batch);
-void ohci_transfer_batch_finish_dispose(ohci_transfer_batch_t *batch);
+bool ohci_transfer_batch_check_completed(ohci_transfer_batch_t *batch);
+void ohci_transfer_batch_destroy(ohci_transfer_batch_t *ohci_batch);
 
 static inline ohci_transfer_batch_t *ohci_transfer_batch_from_link(link_t *l)
@@ -69,4 +72,12 @@
 	return list_get_instance(l, ohci_transfer_batch_t, link);
 }
+
+static inline ohci_transfer_batch_t * ohci_transfer_batch_get(usb_transfer_batch_t *usb_batch)
+{
+	assert(usb_batch);
+
+	return (ohci_transfer_batch_t *) usb_batch;
+}
+
 #endif
 /**
Index: uspace/drv/bus/usb/ohci/ohci_bus.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_bus.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/ohci/ohci_bus.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvusbohci
+ * @{
+ */
+/** @file
+ * @brief OHCI driver
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <usb/host/utils/malloc32.h>
+#include <usb/host/bandwidth.h>
+
+#include "ohci_bus.h"
+#include "ohci_batch.h"
+#include "hc.h"
+
+/** Callback to set toggle on ED.
+ *
+ * @param[in] hcd_ep hcd endpoint structure
+ * @param[in] toggle new value of toggle bit
+ */
+static void ohci_ep_toggle_set(endpoint_t *ep, bool toggle)
+{
+	ohci_endpoint_t *instance = ohci_endpoint_get(ep);
+	assert(instance);
+	assert(instance->ed);
+	ep->toggle = toggle;
+	ed_toggle_set(instance->ed, toggle);
+}
+
+/** Callback to get value of toggle bit.
+ *
+ * @param[in] hcd_ep hcd endpoint structure
+ * @return Current value of toggle bit.
+ */
+static bool ohci_ep_toggle_get(endpoint_t *ep)
+{
+	ohci_endpoint_t *instance = ohci_endpoint_get(ep);
+	assert(instance);
+	assert(instance->ed);
+	return ed_toggle_get(instance->ed);
+}
+
+/** Creates new hcd endpoint representation.
+ */
+static endpoint_t *ohci_endpoint_create(bus_t *bus)
+{
+	assert(bus);
+
+	ohci_endpoint_t *ohci_ep = malloc(sizeof(ohci_endpoint_t));
+	if (ohci_ep == NULL)
+		return NULL;
+
+	endpoint_init(&ohci_ep->base, bus);
+
+	ohci_ep->ed = malloc32(sizeof(ed_t));
+	if (ohci_ep->ed == NULL) {
+		free(ohci_ep);
+		return NULL;
+	}
+
+	ohci_ep->td = malloc32(sizeof(td_t));
+	if (ohci_ep->td == NULL) {
+		free32(ohci_ep->ed);
+		free(ohci_ep);
+		return NULL;
+	}
+
+	link_initialize(&ohci_ep->link);
+	return &ohci_ep->base;
+}
+
+/** Disposes hcd endpoint structure
+ *
+ * @param[in] hcd driver using this instance.
+ * @param[in] ep endpoint structure.
+ */
+static void ohci_endpoint_destroy(endpoint_t *ep)
+{
+	assert(ep);
+	ohci_endpoint_t *instance = ohci_endpoint_get(ep);
+
+	free32(instance->ed);
+	free32(instance->td);
+	free(instance);
+}
+
+
+static int ohci_register_ep(bus_t *bus_base, device_t *dev, endpoint_t *ep, const usb_endpoint_desc_t *desc)
+{
+	ohci_bus_t *bus = (ohci_bus_t *) bus_base;
+	ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ep);
+
+	const int err = bus->parent_ops.register_endpoint(bus_base, dev, ep, desc);
+	if (err)
+		return err;
+
+	ed_init(ohci_ep->ed, ep, ohci_ep->td);
+	hc_enqueue_endpoint(bus->hc, ep);
+
+	return EOK;
+}
+
+static int ohci_unregister_ep(bus_t *bus_base, endpoint_t *ep)
+{
+	ohci_bus_t *bus = (ohci_bus_t *) bus_base;
+	assert(bus);
+	assert(ep);
+
+	const int err = bus->parent_ops.unregister_endpoint(bus_base, ep);
+	if (err)
+		return err;
+
+	hc_dequeue_endpoint(bus->hc, ep);
+	return EOK;
+}
+
+static usb_transfer_batch_t *ohci_bus_create_batch(bus_t *bus, endpoint_t *ep)
+{
+	ohci_transfer_batch_t *batch = ohci_transfer_batch_create(ep);
+	return &batch->base;
+}
+
+static void ohci_bus_destroy_batch(usb_transfer_batch_t *batch)
+{
+	ohci_transfer_batch_destroy(ohci_transfer_batch_get(batch));
+}
+
+int ohci_bus_init(ohci_bus_t *bus, hc_t *hc)
+{
+	assert(hc);
+	assert(bus);
+
+	usb2_bus_init(&bus->base, BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);
+
+	bus_ops_t *ops = &bus->base.base.ops;
+	bus->parent_ops = *ops;
+	ops->create_endpoint = ohci_endpoint_create;
+	ops->destroy_endpoint = ohci_endpoint_destroy;
+	ops->endpoint_set_toggle = ohci_ep_toggle_set;
+	ops->endpoint_get_toggle = ohci_ep_toggle_get;
+
+	ops->register_endpoint = ohci_register_ep;
+	ops->unregister_endpoint = ohci_unregister_ep;
+
+	ops->create_batch = ohci_bus_create_batch;
+	ops->destroy_batch = ohci_bus_destroy_batch;
+
+	bus->hc = hc;
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/ohci/ohci_bus.h
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_bus.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/ohci/ohci_bus.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
+ * 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 drvusbohci
+ * @{
+ */
+/** @file
+ * @brief OHCI driver
+ */
+#ifndef DRV_OHCI_HCD_BUS_H
+#define DRV_OHCI_HCD_BUS_H
+
+#include <assert.h>
+#include <adt/list.h>
+#include <usb/host/usb2_bus.h>
+
+#include "hw_struct/endpoint_descriptor.h"
+#include "hw_struct/transfer_descriptor.h"
+
+/** Connector structure linking ED to to prepared TD. */
+typedef struct ohci_endpoint {
+	endpoint_t base;
+
+	/** OHCI endpoint descriptor */
+	ed_t *ed;
+	/** Currently enqueued transfer descriptor */
+	td_t *td;
+	/** Linked list used by driver software */
+	link_t link;
+} ohci_endpoint_t;
+
+typedef struct hc hc_t;
+
+typedef struct {
+	usb2_bus_t base;
+	hc_t *hc;
+
+	/* Stored original ops from base, they are called in our handlers */
+	bus_ops_t parent_ops;
+} ohci_bus_t;
+
+int ohci_bus_init(ohci_bus_t *, hc_t *);
+
+/** Get and convert assigned ohci_endpoint_t structure
+ * @param[in] ep USBD endpoint structure.
+ * @return Pointer to assigned hcd endpoint structure
+ */
+static inline ohci_endpoint_t * ohci_endpoint_get(const endpoint_t *ep)
+{
+	assert(ep);
+	return (ohci_endpoint_t *) ep;
+}
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/ohci/ohci_endpoint.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_endpoint.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ 	(revision )
@@ -1,123 +1,0 @@
-/*
- * Copyright (c) 2011 Jan Vesely
- * 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 drvusbohci
- * @{
- */
-/** @file
- * @brief OHCI driver
- */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <usb/host/utils/malloc32.h>
-
-#include "ohci_endpoint.h"
-#include "hc.h"
-
-/** Callback to set toggle on ED.
- *
- * @param[in] hcd_ep hcd endpoint structure
- * @param[in] toggle new value of toggle bit
- */
-static void ohci_ep_toggle_set(void *ohci_ep, int toggle)
-{
-	ohci_endpoint_t *instance = ohci_ep;
-	assert(instance);
-	assert(instance->ed);
-	ed_toggle_set(instance->ed, toggle);
-}
-
-/** Callback to get value of toggle bit.
- *
- * @param[in] hcd_ep hcd endpoint structure
- * @return Current value of toggle bit.
- */
-static int ohci_ep_toggle_get(void *ohci_ep)
-{
-	ohci_endpoint_t *instance = ohci_ep;
-	assert(instance);
-	assert(instance->ed);
-	return ed_toggle_get(instance->ed);
-}
-
-/** Creates new hcd endpoint representation.
- *
- * @param[in] ep USBD endpoint structure
- * @return Error code.
- */
-int ohci_endpoint_init(hcd_t *hcd, endpoint_t *ep)
-{
-	assert(ep);
-	ohci_endpoint_t *ohci_ep = malloc(sizeof(ohci_endpoint_t));
-	if (ohci_ep == NULL)
-		return ENOMEM;
-
-	ohci_ep->ed = malloc32(sizeof(ed_t));
-	if (ohci_ep->ed == NULL) {
-		free(ohci_ep);
-		return ENOMEM;
-	}
-
-	ohci_ep->td = malloc32(sizeof(td_t));
-	if (ohci_ep->td == NULL) {
-		free32(ohci_ep->ed);
-		free(ohci_ep);
-		return ENOMEM;
-	}
-
-	link_initialize(&ohci_ep->link);
-	ed_init(ohci_ep->ed, ep, ohci_ep->td);
-	endpoint_set_hc_data(
-	    ep, ohci_ep, ohci_ep_toggle_get, ohci_ep_toggle_set);
-	hc_enqueue_endpoint(hcd_get_driver_data(hcd), ep);
-	return EOK;
-}
-
-/** Disposes hcd endpoint structure
- *
- * @param[in] hcd driver using this instance.
- * @param[in] ep endpoint structure.
- */
-void ohci_endpoint_fini(hcd_t *hcd, endpoint_t *ep)
-{
-	assert(hcd);
-	assert(ep);
-	ohci_endpoint_t *instance = ohci_endpoint_get(ep);
-	hc_dequeue_endpoint(hcd_get_driver_data(hcd), ep);
-	endpoint_clear_hc_data(ep);
-	if (instance) {
-		free32(instance->ed);
-		free32(instance->td);
-		free(instance);
-	}
-}
-
-/**
- * @}
- */
Index: uspace/drv/bus/usb/ohci/ohci_endpoint.h
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_endpoint.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ 	(revision )
@@ -1,71 +1,0 @@
-/*
- * Copyright (c) 2011 Jan Vesely
- * 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 drvusbohci
- * @{
- */
-/** @file
- * @brief OHCI driver
- */
-#ifndef DRV_OHCI_HCD_ENDPOINT_H
-#define DRV_OHCI_HCD_ENDPOINT_H
-
-#include <assert.h>
-#include <adt/list.h>
-#include <usb/host/endpoint.h>
-#include <usb/host/hcd.h>
-
-#include "hw_struct/endpoint_descriptor.h"
-#include "hw_struct/transfer_descriptor.h"
-
-/** Connector structure linking ED to to prepared TD. */
-typedef struct ohci_endpoint {
-	/** OHCI endpoint descriptor */
-	ed_t *ed;
-	/** Currently enqueued transfer descriptor */
-	td_t *td;
-	/** Linked list used by driver software */
-	link_t link;
-} ohci_endpoint_t;
-
-int ohci_endpoint_init(hcd_t *hcd, endpoint_t *ep);
-void ohci_endpoint_fini(hcd_t *hcd, endpoint_t *ep);
-
-/** Get and convert assigned ohci_endpoint_t structure
- * @param[in] ep USBD endpoint structure.
- * @return Pointer to assigned hcd endpoint structure
- */
-static inline ohci_endpoint_t * ohci_endpoint_get(const endpoint_t *ep)
-{
-	assert(ep);
-	return ep->hc_data.data;
-}
-
-#endif
-/**
- * @}
- */
Index: uspace/drv/bus/usb/ohci/ohci_rh.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_rh.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/ohci/ohci_rh.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -178,10 +178,6 @@
 	assert(instance);
 	assert(batch);
-	const usb_target_t target = {{
-		.address = batch->ep->address,
-		.endpoint = batch->ep->endpoint,
-	}};
-	batch->error = virthub_base_request(&instance->base, target,
-	    usb_transfer_batch_direction(batch), (void*)batch->setup_buffer,
+	batch->error = virthub_base_request(&instance->base, batch->target,
+	    batch->dir, &batch->setup.packet,
 	    batch->buffer, batch->buffer_size, &batch->transfered_size);
 	if (batch->error == ENAK) {
@@ -192,6 +188,5 @@
 		instance->unfinished_interrupt_transfer = batch;
 	} else {
-		usb_transfer_batch_finish(batch, NULL);
-		usb_transfer_batch_destroy(batch);
+		usb_transfer_batch_finish(batch);
 	}
 	return EOK;
@@ -211,14 +206,8 @@
 	instance->unfinished_interrupt_transfer = NULL;
 	if (batch) {
-		const usb_target_t target = {{
-			.address = batch->ep->address,
-			.endpoint = batch->ep->endpoint,
-		}};
-		batch->error = virthub_base_request(&instance->base, target,
-		    usb_transfer_batch_direction(batch),
-		    (void*)batch->setup_buffer,
+		batch->error = virthub_base_request(&instance->base, batch->target,
+		    batch->dir, &batch->setup.packet,
 		    batch->buffer, batch->buffer_size, &batch->transfered_size);
-		usb_transfer_batch_finish(batch, NULL);
-		usb_transfer_batch_destroy(batch);
+		usb_transfer_batch_finish(batch);
 	}
 	return EOK;
Index: uspace/drv/bus/usb/uhci/hc.c
===================================================================
--- uspace/drv/bus/usb/uhci/hc.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/uhci/hc.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -50,4 +50,5 @@
 #include <usb/usb.h>
 #include <usb/host/utils/malloc32.h>
+#include <usb/host/bandwidth.h>
 
 #include "uhci_batch.h"
@@ -106,5 +107,5 @@
  * @return Error code.
  */
-int uhci_hc_gen_irq_code(irq_code_t *code, const hw_res_list_parsed_t *hw_res)
+int uhci_hc_gen_irq_code(irq_code_t *code, hcd_t *hcd, const hw_res_list_parsed_t *hw_res)
 {
 	assert(code);
@@ -176,5 +177,5 @@
 			uhci_transfer_batch_t *batch =
 			    uhci_transfer_batch_from_link(current);
-			uhci_transfer_batch_finish_dispose(batch);
+			usb_transfer_batch_finish(&batch->base);
 		}
 	}
@@ -214,5 +215,5 @@
  * interrupt fibrils.
  */
-int hc_init(hc_t *instance, const hw_res_list_parsed_t *hw_res, bool interrupts)
+int hc_init(hc_t *instance, const hw_res_list_parsed_t *hw_res)
 {
 	assert(instance);
@@ -222,5 +223,4 @@
 	    return EINVAL;
 
-	instance->hw_interrupts = interrupts;
 	instance->hw_failures = 0;
 
@@ -246,10 +246,13 @@
 	}
 
+	return EOK;
+}
+
+void hc_start(hc_t *instance)
+{
 	hc_init_hw(instance);
 	(void)hc_debug_checker;
 
 	uhci_rh_init(&instance->rh, instance->registers->ports, "uhci");
-
-	return EOK;
 }
 
@@ -306,4 +309,15 @@
 }
 
+static usb_transfer_batch_t *create_transfer_batch(bus_t *bus, endpoint_t *ep)
+{
+	uhci_transfer_batch_t *batch = uhci_transfer_batch_create(ep);
+	return &batch->base;
+}
+
+static void destroy_transfer_batch(usb_transfer_batch_t *batch)
+{
+	uhci_transfer_batch_destroy(uhci_transfer_batch_get(batch));
+}
+
 /** Initialize UHCI hc memory structures.
  *
@@ -318,5 +332,12 @@
 int hc_init_mem_structures(hc_t *instance)
 {
-	assert(instance);
+	int err;
+	assert(instance);
+
+	if ((err = usb2_bus_init(&instance->bus, BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11)))
+		return err;
+
+	instance->bus.base.ops.create_batch = create_transfer_batch;
+	instance->bus.base.ops.destroy_batch = destroy_transfer_batch;
 
 	/* Init USB frame list page */
@@ -440,12 +461,16 @@
 	assert(batch);
 
-	if (batch->ep->address == uhci_rh_get_address(&instance->rh))
+	if (batch->target.address == uhci_rh_get_address(&instance->rh))
 		return uhci_rh_schedule(&instance->rh, batch);
 
-	uhci_transfer_batch_t *uhci_batch = uhci_transfer_batch_get(batch);
+	uhci_transfer_batch_t *uhci_batch = (uhci_transfer_batch_t *) batch;
 	if (!uhci_batch) {
 		usb_log_error("Failed to create UHCI transfer structures.\n");
 		return ENOMEM;
 	}
+
+	const int err = uhci_transfer_batch_prepare(uhci_batch);
+	if (err)
+		return err;
 
 	transfer_list_t *list =
Index: uspace/drv/bus/usb/uhci/hc.h
===================================================================
--- uspace/drv/bus/usb/uhci/hc.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/uhci/hc.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -43,4 +43,5 @@
 #include <ddi.h>
 #include <usb/host/hcd.h>
+#include <usb/host/usb2_bus.h>
 #include <usb/host/usb_transfer_batch.h>
 
@@ -100,4 +101,5 @@
 typedef struct hc {
 	uhci_rh_t rh;
+	usb2_bus_t bus;
 	/** Addresses of I/O registers */
 	uhci_regs_t *registers;
@@ -124,8 +126,9 @@
 } hc_t;
 
-extern int hc_init(hc_t *, const hw_res_list_parsed_t *, bool);
+extern int hc_init(hc_t *, const hw_res_list_parsed_t *);
+extern void hc_start(hc_t *);
 extern void hc_fini(hc_t *);
 
-extern int uhci_hc_gen_irq_code(irq_code_t *, const hw_res_list_parsed_t *);
+extern int uhci_hc_gen_irq_code(irq_code_t *, hcd_t *,const hw_res_list_parsed_t *);
 
 extern void uhci_hc_interrupt(hcd_t *, uint32_t);
Index: uspace/drv/bus/usb/uhci/main.c
===================================================================
--- uspace/drv/bus/usb/uhci/main.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/uhci/main.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -49,13 +49,15 @@
 #define NAME "uhci"
 
-static int uhci_driver_init(hcd_t *, const hw_res_list_parsed_t *, bool);
+static int uhci_driver_init(hcd_t *, const hw_res_list_parsed_t *, ddf_dev_t *);
+static int uhci_driver_start(hcd_t *, bool);
 static void uhci_driver_fini(hcd_t *);
-static int disable_legacy(ddf_dev_t *);
+static int disable_legacy(hcd_t *, ddf_dev_t *);
 
 static const ddf_hc_driver_t uhci_hc_driver = {
         .claim = disable_legacy,
-        .hc_speed = USB_SPEED_FULL,
         .irq_code_gen = uhci_hc_gen_irq_code,
         .init = uhci_driver_init,
+        .start = uhci_driver_start,
+	.setup_root_hub = hcd_setup_virtual_root_hub,
         .fini = uhci_driver_fini,
         .name = "UHCI",
@@ -67,6 +69,8 @@
 };
 
-static int uhci_driver_init(hcd_t *hcd, const hw_res_list_parsed_t *res, bool irq)
+static int uhci_driver_init(hcd_t *hcd, const hw_res_list_parsed_t *res, ddf_dev_t *device)
 {
+	int err;
+
 	assert(hcd);
 	assert(hcd_get_driver_data(hcd) == NULL);
@@ -76,11 +80,24 @@
 		return ENOMEM;
 
-	const int ret = hc_init(instance, res, irq);
-	if (ret == EOK) {
-		hcd_set_implementation(hcd, instance, &uhci_hc_driver.ops);
-	} else {
-		free(instance);
-	}
-	return ret;
+	if ((err = hc_init(instance, res)) != EOK)
+		goto err;
+
+	hcd_set_implementation(hcd, instance, &uhci_hc_driver.ops, &instance->bus.base);
+
+	return EOK;
+
+err:
+	free(instance);
+	return err;
+}
+
+static int uhci_driver_start(hcd_t *hcd, bool interrupts)
+{
+	assert(hcd);
+	hc_t *hc = hcd_get_driver_data(hcd);
+
+	hc->hw_interrupts = interrupts;
+	hc_start(hc);
+	return EOK;
 }
 
@@ -92,5 +109,5 @@
 		hc_fini(hc);
 
-	hcd_set_implementation(hcd, NULL, NULL);
+	hcd_set_implementation(hcd, NULL, NULL, NULL);
 	free(hc);
 }
@@ -101,5 +118,5 @@
  * @return Error code.
  */
-static int disable_legacy(ddf_dev_t *device)
+static int disable_legacy(hcd_t *hcd, ddf_dev_t *device)
 {
 	assert(device);
@@ -126,6 +143,18 @@
 }
 
+static int uhci_fun_online(ddf_fun_t *fun)
+{
+	return hcd_ddf_device_online(fun);
+}
+
+static int uhci_fun_offline(ddf_fun_t *fun)
+{
+	return hcd_ddf_device_offline(fun);
+}
+
 static const driver_ops_t uhci_driver_ops = {
 	.dev_add = uhci_dev_add,
+	.fun_online = uhci_fun_online,
+	.fun_offline = uhci_fun_offline
 };
 
@@ -148,5 +177,5 @@
 	printf(NAME ": HelenOS UHCI driver.\n");
 	log_init(NAME);
-	logctl_set_log_level(NAME, LVL_NOTE);
+	logctl_set_log_level(NAME, LVL_DEBUG2);
 	return ddf_driver_main(&uhci_driver);
 }
Index: uspace/drv/bus/usb/uhci/transfer_list.c
===================================================================
--- uspace/drv/bus/usb/uhci/transfer_list.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/uhci/transfer_list.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -115,6 +115,14 @@
 	assert(instance);
 	assert(uhci_batch);
+
+	endpoint_t *ep = uhci_batch->base.ep;
+
+	/* First, wait until the endpoint is free to use */
+	fibril_mutex_lock(&ep->guard);
+	endpoint_activate_locked(ep, &uhci_batch->base);
+	fibril_mutex_unlock(&ep->guard);
+
 	usb_log_debug2("Batch %p adding to queue %s.\n",
-	    uhci_batch->usb_batch, instance->name);
+	    uhci_batch, instance->name);
 
 	fibril_mutex_lock(&instance->guard);
@@ -145,6 +153,6 @@
 
 	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
-	    " scheduled in queue %s.\n", uhci_batch->usb_batch,
-	    USB_TRANSFER_BATCH_ARGS(*uhci_batch->usb_batch), instance->name);
+	    " scheduled in queue %s.\n", uhci_batch,
+	    USB_TRANSFER_BATCH_ARGS(uhci_batch->base), instance->name);
 	fibril_mutex_unlock(&instance->guard);
 }
@@ -167,5 +175,5 @@
 		    uhci_transfer_batch_from_link(current);
 
-		if (uhci_transfer_batch_is_complete(batch)) {
+		if (uhci_transfer_batch_check_completed(batch)) {
 			/* Save for processing */
 			transfer_list_remove_batch(instance, batch);
@@ -189,5 +197,5 @@
 		    uhci_transfer_batch_from_link(current);
 		transfer_list_remove_batch(instance, batch);
-		uhci_transfer_batch_abort(batch);
+		endpoint_abort(batch->base.ep);
 	}
 	fibril_mutex_unlock(&instance->guard);
@@ -211,5 +219,5 @@
 
 	usb_log_debug2("Batch %p removing from queue %s.\n",
-	    uhci_batch->usb_batch, instance->name);
+	    uhci_batch, instance->name);
 
 	/* Assume I'm the first */
@@ -233,6 +241,6 @@
 	list_remove(&uhci_batch->link);
 	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " removed (%s) "
-	    "from %s, next: %x.\n", uhci_batch->usb_batch,
-	    USB_TRANSFER_BATCH_ARGS(*uhci_batch->usb_batch),
+	    "from %s, next: %x.\n", uhci_batch,
+	    USB_TRANSFER_BATCH_ARGS(uhci_batch->base),
 	    qpos, instance->name, uhci_batch->qh->next);
 }
Index: uspace/drv/bus/usb/uhci/uhci_batch.c
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_batch.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/uhci/uhci_batch.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -50,33 +50,17 @@
 #define DEFAULT_ERROR_COUNT 3
 
-/** Safely destructs uhci_transfer_batch_t structure.
+/** Transfer batch setup table. */
+static void (*const batch_setup[])(uhci_transfer_batch_t*);
+
+/** Destroys uhci_transfer_batch_t structure.
  *
  * @param[in] uhci_batch Instance to destroy.
  */
-static void uhci_transfer_batch_dispose(uhci_transfer_batch_t *uhci_batch)
-{
-	if (uhci_batch) {
-		usb_transfer_batch_destroy(uhci_batch->usb_batch);
-		free32(uhci_batch->device_buffer);
-		free(uhci_batch);
-	}
-}
-
-/** Finishes usb_transfer_batch and destroys the structure.
- *
- * @param[in] uhci_batch Instance to finish and destroy.
- */
-void uhci_transfer_batch_finish_dispose(uhci_transfer_batch_t *uhci_batch)
+void uhci_transfer_batch_destroy(uhci_transfer_batch_t *uhci_batch)
 {
 	assert(uhci_batch);
-	assert(uhci_batch->usb_batch);
-	assert(!link_in_use(&uhci_batch->link));
-	usb_transfer_batch_finish(uhci_batch->usb_batch,
-	    uhci_transfer_batch_data_buffer(uhci_batch));
-	uhci_transfer_batch_dispose(uhci_batch);
-}
-
-/** Transfer batch setup table. */
-static void (*const batch_setup[])(uhci_transfer_batch_t*, usb_direction_t);
+	free32(uhci_batch->device_buffer);
+	free(uhci_batch);
+}
 
 /** Allocate memory and initialize internal data structure.
@@ -85,4 +69,21 @@
  * @return Valid pointer if all structures were successfully created,
  * NULL otherwise.
+ */
+uhci_transfer_batch_t * uhci_transfer_batch_create(endpoint_t *ep)
+{
+	uhci_transfer_batch_t *uhci_batch =
+	    calloc(1, sizeof(uhci_transfer_batch_t));
+	if (!uhci_batch) {
+		usb_log_error("Failed to allocate UHCI batch.\n");
+		return NULL;
+	}
+
+	usb_transfer_batch_init(&uhci_batch->base, ep);
+
+	link_initialize(&uhci_batch->link);
+	return uhci_batch;
+}
+
+/* Prepares batch for commiting.
  *
  * Determines the number of needed transfer descriptors (TDs).
@@ -90,31 +91,28 @@
  * Initializes parameters needed for the transfer and callback.
  */
-uhci_transfer_batch_t * uhci_transfer_batch_get(usb_transfer_batch_t *usb_batch)
+int uhci_transfer_batch_prepare(uhci_transfer_batch_t *uhci_batch)
 {
 	static_assert((sizeof(td_t) % 16) == 0);
-#define CHECK_NULL_DISPOSE_RETURN(ptr, message...) \
-	if (ptr == NULL) { \
-		usb_log_error(message); \
-		uhci_transfer_batch_dispose(uhci_batch); \
-		return NULL; \
-	} else (void)0
-
-	uhci_transfer_batch_t *uhci_batch =
-	    calloc(1, sizeof(uhci_transfer_batch_t));
-	CHECK_NULL_DISPOSE_RETURN(uhci_batch,
-	    "Failed to allocate UHCI batch.\n");
-	link_initialize(&uhci_batch->link);
-	uhci_batch->td_count =
-	    (usb_batch->buffer_size + usb_batch->ep->max_packet_size - 1)
-	    / usb_batch->ep->max_packet_size;
+
+	usb_transfer_batch_t *usb_batch = &uhci_batch->base;
+
+	uhci_batch->td_count = (usb_batch->buffer_size + usb_batch->ep->max_packet_size - 1)
+		/ usb_batch->ep->max_packet_size;
+
 	if (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
 		uhci_batch->td_count += 2;
 	}
 
+	const size_t setup_size = (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL)
+		? USB_SETUP_PACKET_SIZE
+		: 0;
+
 	const size_t total_size = (sizeof(td_t) * uhci_batch->td_count)
-	    + sizeof(qh_t) + usb_batch->setup_size + usb_batch->buffer_size;
+	    + sizeof(qh_t) + setup_size + usb_batch->buffer_size;
 	uhci_batch->device_buffer = malloc32(total_size);
-	CHECK_NULL_DISPOSE_RETURN(uhci_batch->device_buffer,
-	    "Failed to allocate UHCI buffer.\n");
+	if (!uhci_batch->device_buffer) {
+		usb_log_error("Failed to allocate UHCI buffer.\n");
+		return ENOMEM;
+	}
 	memset(uhci_batch->device_buffer, 0, total_size);
 
@@ -130,21 +128,18 @@
 	    + sizeof(qh_t);
 	/* Copy SETUP packet data to the device buffer */
-	memcpy(dest, usb_batch->setup_buffer, usb_batch->setup_size);
-	dest += usb_batch->setup_size;
+	memcpy(dest, usb_batch->setup.buffer, setup_size);
+	dest += setup_size;
 	/* Copy generic data unless they are provided by the device */
-	if (usb_batch->ep->direction != USB_DIRECTION_IN) {
+	if (usb_batch->dir != USB_DIRECTION_IN) {
 		memcpy(dest, usb_batch->buffer, usb_batch->buffer_size);
 	}
-	uhci_batch->usb_batch = usb_batch;
 	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
 	    " memory structures ready.\n", usb_batch,
 	    USB_TRANSFER_BATCH_ARGS(*usb_batch));
 
-	const usb_direction_t dir = usb_transfer_batch_direction(usb_batch);
-
 	assert(batch_setup[usb_batch->ep->transfer_type]);
-	batch_setup[usb_batch->ep->transfer_type](uhci_batch, dir);
-
-	return uhci_batch;
+	batch_setup[usb_batch->ep->transfer_type](uhci_batch);
+
+	return EOK;
 }
 
@@ -158,15 +153,14 @@
  * is reached.
  */
-bool uhci_transfer_batch_is_complete(const uhci_transfer_batch_t *uhci_batch)
+bool uhci_transfer_batch_check_completed(uhci_transfer_batch_t *uhci_batch)
 {
 	assert(uhci_batch);
-	assert(uhci_batch->usb_batch);
+	usb_transfer_batch_t *batch = &uhci_batch->base;
 
 	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
 	    " checking %zu transfer(s) for completion.\n",
-	    uhci_batch->usb_batch,
-	    USB_TRANSFER_BATCH_ARGS(*uhci_batch->usb_batch),
+	    uhci_batch, USB_TRANSFER_BATCH_ARGS(*batch),
 	    uhci_batch->td_count);
-	uhci_batch->usb_batch->transfered_size = 0;
+	batch->transfered_size = 0;
 
 	for (size_t i = 0;i < uhci_batch->td_count; ++i) {
@@ -175,14 +169,14 @@
 		}
 
-		uhci_batch->usb_batch->error = td_status(&uhci_batch->tds[i]);
-		if (uhci_batch->usb_batch->error != EOK) {
-			assert(uhci_batch->usb_batch->ep != NULL);
+		batch->error = td_status(&uhci_batch->tds[i]);
+		if (batch->error != EOK) {
+			assert(batch->ep != NULL);
 
 			usb_log_debug("Batch %p found error TD(%zu->%p):%"
-			    PRIx32 ".\n", uhci_batch->usb_batch, i,
+			    PRIx32 ".\n", uhci_batch, i,
 			    &uhci_batch->tds[i], uhci_batch->tds[i].status);
 			td_print_status(&uhci_batch->tds[i]);
 
-			endpoint_toggle_set(uhci_batch->usb_batch->ep,
+			endpoint_toggle_set(batch->ep,
 			    td_toggle(&uhci_batch->tds[i]));
 			if (i > 0)
@@ -191,5 +185,5 @@
 		}
 
-		uhci_batch->usb_batch->transfered_size
+		batch->transfered_size
 		    += td_act_size(&uhci_batch->tds[i]);
 		if (td_is_short(&uhci_batch->tds[i]))
@@ -197,6 +191,19 @@
 	}
 substract_ret:
-	uhci_batch->usb_batch->transfered_size
-	    -= uhci_batch->usb_batch->setup_size;
+	if (batch->ep->transfer_type == USB_TRANSFER_CONTROL)
+		batch->transfered_size -= USB_SETUP_PACKET_SIZE;
+
+	fibril_mutex_lock(&batch->ep->guard);
+	usb_transfer_batch_reset_toggle(batch);
+	endpoint_deactivate_locked(batch->ep);
+	fibril_mutex_unlock(&batch->ep->guard);
+
+	if (batch->dir == USB_DIRECTION_IN) {
+		assert(batch->transfered_size <= batch->buffer_size);
+		memcpy(batch->buffer,
+		    uhci_transfer_batch_data_buffer(uhci_batch),
+		    batch->transfered_size);
+	}
+
 	return true;
 }
@@ -216,9 +223,9 @@
  * The last transfer is marked with IOC flag.
  */
-static void batch_data(uhci_transfer_batch_t *uhci_batch, usb_direction_t dir)
+static void batch_data(uhci_transfer_batch_t *uhci_batch)
 {
 	assert(uhci_batch);
-	assert(uhci_batch->usb_batch);
-	assert(uhci_batch->usb_batch->ep);
+
+	usb_direction_t dir = uhci_batch->base.dir;
 	assert(dir == USB_DIRECTION_OUT || dir == USB_DIRECTION_IN);
 
@@ -226,15 +233,12 @@
 	const usb_packet_id pid = direction_pids[dir];
 	const bool low_speed =
-	    uhci_batch->usb_batch->ep->speed == USB_SPEED_LOW;
-	const size_t mps = uhci_batch->usb_batch->ep->max_packet_size;
-	const usb_target_t target = {{
-	    uhci_batch->usb_batch->ep->address,
-	    uhci_batch->usb_batch->ep->endpoint }};
-
-	int toggle = endpoint_toggle_get(uhci_batch->usb_batch->ep);
+	    uhci_batch->base.ep->speed == USB_SPEED_LOW;
+	const size_t mps = uhci_batch->base.ep->max_packet_size;
+
+	int toggle = endpoint_toggle_get(uhci_batch->base.ep);
 	assert(toggle == 0 || toggle == 1);
 
 	size_t td = 0;
-	size_t remain_size = uhci_batch->usb_batch->buffer_size;
+	size_t remain_size = uhci_batch->base.buffer_size;
 	char *buffer = uhci_transfer_batch_data_buffer(uhci_batch);
 
@@ -248,5 +252,5 @@
 		td_init(
 		    &uhci_batch->tds[td], DEFAULT_ERROR_COUNT, packet_size,
-		    toggle, false, low_speed, target, pid, buffer, next_td);
+		    toggle, false, low_speed, uhci_batch->base.target, pid, buffer, next_td);
 
 		++td;
@@ -256,11 +260,11 @@
 	}
 	td_set_ioc(&uhci_batch->tds[td - 1]);
-	endpoint_toggle_set(uhci_batch->usb_batch->ep, toggle);
+	endpoint_toggle_set(uhci_batch->base.ep, toggle);
 	usb_log_debug2(
 	    "Batch %p %s %s " USB_TRANSFER_BATCH_FMT " initialized.\n", \
-	    uhci_batch->usb_batch,
-	    usb_str_transfer_type(uhci_batch->usb_batch->ep->transfer_type),
-	    usb_str_direction(uhci_batch->usb_batch->ep->direction),
-	    USB_TRANSFER_BATCH_ARGS(*uhci_batch->usb_batch));
+	    uhci_batch,
+	    usb_str_transfer_type(uhci_batch->base.ep->transfer_type),
+	    usb_str_direction(uhci_batch->base.ep->direction),
+	    USB_TRANSFER_BATCH_ARGS(uhci_batch->base));
 }
 
@@ -276,9 +280,9 @@
  * The last transfer is marked with IOC.
  */
-static void batch_control(uhci_transfer_batch_t *uhci_batch, usb_direction_t dir)
+static void batch_control(uhci_transfer_batch_t *uhci_batch)
 {
 	assert(uhci_batch);
-	assert(uhci_batch->usb_batch);
-	assert(uhci_batch->usb_batch->ep);
+
+	usb_direction_t dir = uhci_batch->base.dir;
 	assert(dir == USB_DIRECTION_OUT || dir == USB_DIRECTION_IN);
 	assert(uhci_batch->td_count >= 2);
@@ -291,14 +295,12 @@
 	const usb_packet_id status_stage_pid = status_stage_pids[dir];
 	const bool low_speed =
-	    uhci_batch->usb_batch->ep->speed == USB_SPEED_LOW;
-	const size_t mps = uhci_batch->usb_batch->ep->max_packet_size;
-	const usb_target_t target = {{
-	    uhci_batch->usb_batch->ep->address,
-	    uhci_batch->usb_batch->ep->endpoint }};
+	    uhci_batch->base.ep->speed == USB_SPEED_LOW;
+	const size_t mps = uhci_batch->base.ep->max_packet_size;
+	const usb_target_t target = uhci_batch->base.target;
 
 	/* setup stage */
 	td_init(
 	    &uhci_batch->tds[0], DEFAULT_ERROR_COUNT,
-	    uhci_batch->usb_batch->setup_size, 0, false,
+	    USB_SETUP_PACKET_SIZE, 0, false,
 	    low_speed, target, USB_PID_SETUP,
 	    uhci_transfer_batch_setup_buffer(uhci_batch), &uhci_batch->tds[1]);
@@ -307,5 +309,5 @@
 	size_t td = 1;
 	unsigned toggle = 1;
-	size_t remain_size = uhci_batch->usb_batch->buffer_size;
+	size_t remain_size = uhci_batch->base.buffer_size;
 	char *buffer = uhci_transfer_batch_data_buffer(uhci_batch);
 
@@ -337,5 +339,5 @@
 }
 
-static void (*const batch_setup[])(uhci_transfer_batch_t*, usb_direction_t) =
+static void (*const batch_setup[])(uhci_transfer_batch_t*) =
 {
 	[USB_TRANSFER_CONTROL] = batch_control,
Index: uspace/drv/bus/usb/uhci/uhci_batch.h
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_batch.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/uhci/uhci_batch.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -43,4 +43,5 @@
 #include <stddef.h>
 #include <usb/host/usb_transfer_batch.h>
+#include <usb/host/endpoint.h>
 
 #include "hw_struct/queue_head.h"
@@ -49,4 +50,6 @@
 /** UHCI specific data required for USB transfer */
 typedef struct uhci_transfer_batch {
+	usb_transfer_batch_t base;
+
 	/** Queue head
 	 * This QH is used to maintain UHCI schedule structure and the element
@@ -60,13 +63,13 @@
 	/** Data buffer, must be accessible by the UHCI hw */
 	void *device_buffer;
-	/** Generic transfer data */
-	usb_transfer_batch_t *usb_batch;
 	/** List element */
 	link_t link;
 } uhci_transfer_batch_t;
 
-uhci_transfer_batch_t * uhci_transfer_batch_get(usb_transfer_batch_t *batch);
-void uhci_transfer_batch_finish_dispose(uhci_transfer_batch_t *uhci_batch);
-bool uhci_transfer_batch_is_complete(const uhci_transfer_batch_t *uhci_batch);
+uhci_transfer_batch_t * uhci_transfer_batch_create(endpoint_t *);
+int uhci_transfer_batch_prepare(uhci_transfer_batch_t *);
+bool uhci_transfer_batch_check_completed(uhci_transfer_batch_t *);
+void uhci_transfer_batch_finish(uhci_transfer_batch_t *);
+void uhci_transfer_batch_destroy(uhci_transfer_batch_t *);
 
 /** Get offset to setup buffer accessible to the HC hw.
@@ -91,21 +94,6 @@
 {
 	assert(uhci_batch);
-	assert(uhci_batch->usb_batch);
 	return uhci_transfer_batch_setup_buffer(uhci_batch) +
-	    uhci_batch->usb_batch->setup_size;
-}
-
-/** Aborts the batch.
- * Sets error to EINTR and size off transferd data to 0, before finishing the
- * batch.
- * @param uhci_batch Batch to abort.
- */
-static inline void uhci_transfer_batch_abort(uhci_transfer_batch_t *uhci_batch)
-{
-	assert(uhci_batch);
-	assert(uhci_batch->usb_batch);
-	uhci_batch->usb_batch->error = EINTR;
-	uhci_batch->usb_batch->transfered_size = 0;
-	uhci_transfer_batch_finish_dispose(uhci_batch);
+	    (uhci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL ? USB_SETUP_PACKET_SIZE : 0);
 }
 
@@ -120,4 +108,10 @@
 }
 
+static inline uhci_transfer_batch_t *uhci_transfer_batch_get(usb_transfer_batch_t *b)
+{
+	assert(b);
+	return (uhci_transfer_batch_t *) b;
+}
+
 #endif
 
Index: uspace/drv/bus/usb/uhci/uhci_rh.c
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_rh.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/uhci/uhci_rh.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -39,4 +39,5 @@
 #include <usb/classes/hub.h>
 #include <usb/request.h>
+#include <usb/host/endpoint.h>
 #include <usb/usb.h>
 
@@ -103,11 +104,7 @@
 	assert(batch);
 
-	const usb_target_t target = {{
-		.address = batch->ep->address,
-		.endpoint = batch->ep->endpoint
-	}};
 	do {
-		batch->error = virthub_base_request(&instance->base, target,
-		    usb_transfer_batch_direction(batch), (void*)batch->setup_buffer,
+		batch->error = virthub_base_request(&instance->base, batch->target,
+		    batch->dir, (void*) batch->setup.buffer,
 		    batch->buffer, batch->buffer_size, &batch->transfered_size);
 		if (batch->error == ENAK)
@@ -116,6 +113,5 @@
 		//ENAK is technically an error condition
 	} while (batch->error == ENAK);
-	usb_transfer_batch_finish(batch, NULL);
-	usb_transfer_batch_destroy(batch);
+	usb_transfer_batch_finish(batch);
 	return EOK;
 }
Index: uspace/drv/bus/usb/usbhub/port.c
===================================================================
--- uspace/drv/bus/usb/usbhub/port.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/usbhub/port.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -40,4 +40,5 @@
 #include <inttypes.h>
 #include <fibril_synch.h>
+#include <usbhc_iface.h>
 
 #include <usb/debug.h>
@@ -267,5 +268,5 @@
 	if (!exch)
 		return ENOMEM;
-	const int rc = usb_device_remove(exch, port->port_number);
+	const int rc = usbhc_device_remove(exch, port->port_number);
 	usb_device_bus_exchange_end(exch);
 	if (rc == EOK)
@@ -405,5 +406,5 @@
 
 	/* Reserve default address */
-	while ((ret = usb_reserve_default_address(exch, speed)) == ENOENT) {
+	while ((ret = usbhc_reserve_default_address(exch, speed)) == ENOENT) {
 		async_usleep(1000000);
 	}
@@ -421,5 +422,5 @@
 		usb_log_error("(%p-%u): Failed to reset port.", hub,
 		    port->port_number);
-		if (usb_release_default_address(exch) != EOK)
+		if (usbhc_release_default_address(exch) != EOK)
 			usb_log_warning("(%p-%u): Failed to release default "
 			    "address.", hub, port->port_number);
@@ -430,5 +431,5 @@
 	    port->port_number);
 
-	ret = usb_device_enumerate(exch, port->port_number);
+	ret = usbhc_device_enumerate(exch, port->port_number);
 	if (ret != EOK) {
 		usb_log_error("(%p-%u): Failed to enumerate device: %s", hub,
@@ -440,5 +441,5 @@
 			    port->port_number, str_error(ret));
 		} else {
-			const int ret = usb_release_default_address(exch);
+			const int ret = usbhc_release_default_address(exch);
 			if (ret != EOK)
 				usb_log_warning("(%p-%u): Failed to release "
@@ -450,5 +451,5 @@
 		    port->port_number);
 		port->device_attached = true;
-		if (usb_release_default_address(exch) != EOK)
+		if (usbhc_release_default_address(exch) != EOK)
 			usb_log_warning("(%p-%u): Failed to release default "
 			    "address", hub, port->port_number);
Index: uspace/drv/bus/usb/usbhub/port.h
===================================================================
--- uspace/drv/bus/usb/usbhub/port.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/usbhub/port.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -40,5 +40,4 @@
 #include <usb/dev/driver.h>
 #include <usb/classes/hub.h>
-#include <usb_iface.h>
 
 typedef struct usb_hub_dev usb_hub_dev_t;
Index: uspace/drv/bus/usb/usbhub/status.h
===================================================================
--- uspace/drv/bus/usb/usbhub/status.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/usbhub/status.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -110,4 +110,5 @@
 	if ((status & USB_HUB_PORT_STATUS_HIGH_SPEED) != 0)
 		return USB_SPEED_HIGH;
+	/* TODO: add super speed */
 	return USB_SPEED_FULL;
 }
Index: uspace/drv/bus/usb/usbmid/main.c
===================================================================
--- uspace/drv/bus/usb/usbmid/main.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/usbmid/main.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -147,4 +147,16 @@
 }
 
+static int usbmid_function_online(ddf_fun_t *fun)
+{
+	/* TODO: What if this is the control function? */
+	return ddf_fun_online(fun);
+}
+
+static int usbmid_function_offline(ddf_fun_t *fun)
+{
+	/* TODO: What if this is the control function? */
+	return ddf_fun_offline(fun);
+}
+
 /** USB MID driver ops. */
 static const usb_driver_ops_t mid_driver_ops = {
@@ -152,4 +164,6 @@
 	.device_rem = usbmid_device_remove,
 	.device_gone = usbmid_device_gone,
+	.function_online = usbmid_function_online,
+	.function_offline = usbmid_function_offline
 };
 
Index: uspace/drv/bus/usb/vhc/main.c
===================================================================
--- uspace/drv/bus/usb/vhc/main.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/vhc/main.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -89,6 +89,5 @@
 
 	/* Initialize generic structures */
-	ret = hcd_ddf_setup_hc(dev, USB_SPEED_FULL,
-	    BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);
+	ret = hcd_ddf_setup_hc(dev);
 	if (ret != EOK) {
 		usb_log_error("Failed to init HCD structures: %s.\n",
@@ -98,5 +97,5 @@
 	}
 
-	hcd_set_implementation(dev_to_hcd(dev), data, &vhc_hc_ops);
+	hcd_set_implementation(dev_to_hcd(dev), data, &vhc_hc_ops, &data->bus.base);
 
 	/* Add virtual hub device */
@@ -112,5 +111,5 @@
 	 * needs to be ready at this time.
 	 */
-	ret = hcd_ddf_setup_root_hub(dev);
+	ret = hcd_setup_virtual_root_hub(dev_to_hcd(dev), dev);
 	if (ret != EOK) {
 		usb_log_error("Failed to init VHC root hub: %s\n",
Index: uspace/drv/bus/usb/vhc/transfer.c
===================================================================
--- uspace/drv/bus/usb/vhc/transfer.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/vhc/transfer.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -31,4 +31,5 @@
 #include <usb/debug.h>
 #include <usbvirt/device.h>
+#include <usb/host/bandwidth.h>
 #include <usbvirt/ipc.h>
 #include "vhcd.h"
@@ -37,5 +38,5 @@
 static bool is_set_address_transfer(vhc_transfer_t *transfer)
 {
-	if (transfer->batch->ep->endpoint != 0) {
+	if (transfer->batch->target.endpoint != 0) {
 		return false;
 	}
@@ -43,9 +44,9 @@
 		return false;
 	}
-	if (usb_transfer_batch_direction(transfer->batch) != USB_DIRECTION_OUT) {
-		return false;
-	}
-	const usb_device_request_setup_packet_t *setup =
-	    (void*)transfer->batch->setup_buffer;
+	if (transfer->batch->dir != USB_DIRECTION_OUT) {
+		return false;
+	}
+	const usb_device_request_setup_packet_t *setup
+		= &transfer->batch->setup.packet;
 	if (setup->request_type != 0) {
 		return false;
@@ -62,11 +63,11 @@
 {
 	int rc;
-	
-	const usb_direction_t dir = usb_transfer_batch_direction(batch);
+
+	const usb_direction_t dir = batch->dir;
 
 	if (batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
 		if (dir == USB_DIRECTION_IN) {
 			rc = usbvirt_control_read(dev,
-			    batch->setup_buffer, batch->setup_size,
+			    batch->setup.buffer, USB_SETUP_PACKET_SIZE,
 			    batch->buffer, batch->buffer_size,
 			    actual_data_size);
@@ -74,5 +75,5 @@
 			assert(dir == USB_DIRECTION_OUT);
 			rc = usbvirt_control_write(dev,
-			    batch->setup_buffer, batch->setup_size,
+			    batch->setup.buffer, USB_SETUP_PACKET_SIZE,
 			    batch->buffer, batch->buffer_size);
 		}
@@ -99,10 +100,10 @@
 	int rc;
 
-	const usb_direction_t dir = usb_transfer_batch_direction(batch);
+	const usb_direction_t dir = batch->dir;
 
 	if (batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
 		if (dir == USB_DIRECTION_IN) {
 			rc = usbvirt_ipc_send_control_read(sess,
-			    batch->setup_buffer, batch->setup_size,
+			    batch->setup.buffer, USB_SETUP_PACKET_SIZE,
 			    batch->buffer, batch->buffer_size,
 			    actual_data_size);
@@ -110,5 +111,5 @@
 			assert(dir == USB_DIRECTION_OUT);
 			rc = usbvirt_ipc_send_control_write(sess,
-			    batch->setup_buffer, batch->setup_size,
+			    batch->setup.buffer, USB_SETUP_PACKET_SIZE,
 			    batch->buffer, batch->buffer_size);
 		}
@@ -148,7 +149,7 @@
 	assert(transfer);
 	assert(transfer->batch);
-	usb_transfer_batch_finish_error(transfer->batch, NULL,
-	    data_transfer_size, outcome);
-	usb_transfer_batch_destroy(transfer->batch);
+	transfer->batch->error = outcome;
+	transfer->batch->transfered_size = data_transfer_size;
+	usb_transfer_batch_finish(transfer->batch);
 	free(transfer);
 }
@@ -159,4 +160,5 @@
 	list_initialize(&instance->devices);
 	fibril_mutex_initialize(&instance->guard);
+	usb2_bus_init(&instance->bus, BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11);
 	instance->magic = 0xDEADBEEF;
 	return virthub_init(&instance->hub, "root hub");
@@ -182,5 +184,5 @@
 	list_foreach(vhc->devices, link, vhc_virtdev_t, dev) {
 		fibril_mutex_lock(&dev->guard);
-		if (dev->address == transfer->batch->ep->address) {
+		if (dev->address == transfer->batch->target.address) {
 			if (!targets) {
 				list_append(&transfer->link, &dev->transfer_queue);
@@ -234,5 +236,5 @@
 			if (is_set_address_transfer(transfer)) {
 				usb_device_request_setup_packet_t *setup =
-				    (void*) transfer->batch->setup_buffer;
+				    (void*) transfer->batch->setup.buffer;
 				dev->address = setup->value;
 				usb_log_debug2("Address changed to %d\n",
Index: uspace/drv/bus/usb/vhc/vhcd.h
===================================================================
--- uspace/drv/bus/usb/vhc/vhcd.h	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/bus/usb/vhc/vhcd.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -38,8 +38,8 @@
 
 #include <usbvirt/device.h>
-#include <usbhc_iface.h>
 #include <async.h>
 
 #include <usb/host/hcd.h>
+#include <usb/host/usb2_bus.h>
 
 #define NAME "vhc"
@@ -60,4 +60,5 @@
 	fibril_mutex_t guard;
 	usbvirt_device_t hub;
+	usb2_bus_t bus;
 } vhc_data_t;
 
Index: uspace/drv/bus/usb/xhci/Makefile
===================================================================
--- uspace/drv/bus/usb/xhci/Makefile	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/Makefile	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,50 @@
+#
+# Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
+# 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.
+#
+
+USPACE_PREFIX = ../../../..
+
+LIBS = usbhost usbvirt usb drv
+
+BINARY = xhci
+
+SOURCES = \
+	bus.c \
+	commands.c \
+	debug.c \
+	endpoint.c \
+	hc.c \
+	main.c \
+	rh.c \
+	scratchpad.c \
+	transfers.c \
+	trb_ring.c
+
+TEST_SOURCES = \
+	test/reg-ops.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/bus/usb/xhci/bus.c
===================================================================
--- uspace/drv/bus/usb/xhci/bus.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/bus.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,569 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
+ * 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 libusbhost
+ * @{
+ */
+/** @file
+ * HC Endpoint management.
+ */
+
+#include <usb/host/utils/malloc32.h>
+#include <usb/host/ddf_helpers.h>
+#include <usb/host/endpoint.h>
+#include <usb/host/hcd.h>
+#include <usb/debug.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <str_error.h>
+#include <macros.h>
+#include <stdbool.h>
+
+#include "hc.h"
+#include "bus.h"
+#include "endpoint.h"
+#include "transfers.h"
+
+
+/* FIXME Are these really static? Older HCs fetch it from descriptor. */
+/* FIXME Add USB3 options, if applicable. */
+static const usb_endpoint_desc_t ep0_desc = {
+	.endpoint_no = 0,
+	.direction = USB_DIRECTION_BOTH,
+	.transfer_type = USB_TRANSFER_CONTROL,
+	.max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
+	.packets = 1,
+};
+
+static int prepare_endpoint(xhci_endpoint_t *ep, const usb_endpoint_desc_t *desc)
+{
+	/* Extract information from endpoint_desc */
+	ep->base.endpoint = desc->endpoint_no;
+	ep->base.direction = desc->direction;
+	ep->base.transfer_type = desc->transfer_type;
+	ep->base.max_packet_size = desc->max_packet_size;
+	ep->base.packets = desc->packets;
+	ep->max_streams = desc->usb3.max_streams;
+	ep->max_burst = desc->usb3.max_burst;
+	// TODO add this property to usb_endpoint_desc_t and fetch it from ss companion desc
+	ep->mult = 0;
+
+	return xhci_endpoint_alloc_transfer_ds(ep);
+}
+
+static endpoint_t *create_endpoint(bus_t *base);
+
+static int address_device(xhci_hc_t *hc, xhci_device_t *dev)
+{
+	int err;
+
+	/* Enable new slot. */
+	if ((err = hc_enable_slot(hc, &dev->slot_id)) != EOK)
+		return err;
+	usb_log_debug2("Obtained slot ID: %u.\n", dev->slot_id);
+
+	/* Create and configure control endpoint. */
+	endpoint_t *ep0_base = create_endpoint(&hc->bus.base);
+	if (!ep0_base)
+		goto err_slot;
+
+	/* Temporary reference */
+	endpoint_add_ref(ep0_base);
+
+	xhci_endpoint_t *ep0 = xhci_endpoint_get(ep0_base);
+
+	if ((err = prepare_endpoint(ep0, &ep0_desc)))
+		goto err_ep;
+
+	/* Register EP0 */
+	if ((err = xhci_device_add_endpoint(dev, ep0)))
+		goto err_prepared;
+
+	/* Address device */
+	if ((err = hc_address_device(hc, dev, ep0)))
+		goto err_added;
+
+	/* Temporary reference */
+	endpoint_del_ref(ep0_base);
+	return EOK;
+
+err_added:
+	xhci_device_remove_endpoint(ep0);
+err_prepared:
+	xhci_endpoint_free_transfer_ds(ep0);
+err_ep:
+	/* Temporary reference */
+	endpoint_del_ref(ep0_base);
+err_slot:
+	hc_disable_slot(hc, dev);
+	return err;
+}
+
+int xhci_bus_enumerate_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
+{
+	int err;
+	xhci_device_t *xhci_dev = xhci_device_get(dev);
+
+	/* Manage TT */
+	if (dev->hub->speed == USB_SPEED_HIGH && usb_speed_is_11(dev->speed)) {
+		/* For LS devices under HS hub */
+		/* TODO: How about SS hubs? */
+		dev->tt.address = dev->hub->address;
+		dev->tt.port = dev->port;
+	}
+	else {
+		/* Inherit hub's TT */
+		dev->tt = dev->hub->tt;
+	}
+
+	/* Calculate route string */
+	xhci_device_t *xhci_hub = xhci_device_get(dev->hub);
+	xhci_dev->tier = xhci_hub->tier + 1;
+	xhci_dev->route_str = xhci_hub->route_str;
+
+	/* Roothub port is not part of the route string */
+	if (xhci_dev->tier >= 2) {
+		const unsigned offset = 4 * (xhci_dev->tier - 2);
+		xhci_dev->route_str |= (dev->port & 0xf) << offset;
+		xhci_dev->rh_port = xhci_hub->rh_port;
+	}
+
+	fibril_mutex_lock(&bus->base.guard);
+	/* Assign an address to the device */
+	if ((err = address_device(hc, xhci_dev))) {
+		usb_log_error("Failed to setup address of the new device: %s", str_error(err));
+		return err;
+	}
+
+	// TODO: Fetch descriptor of EP0 and reconfigure it accordingly
+	assert(xhci_dev->endpoints[0]);
+
+	assert(bus->devices_by_slot[xhci_dev->slot_id] == NULL);
+	bus->devices_by_slot[xhci_dev->slot_id] = xhci_dev;
+	fibril_mutex_unlock(&bus->base.guard);
+
+	/* Read the device descriptor, derive the match ids */
+	if ((err = hcd_ddf_device_explore(hc->hcd, dev))) {
+		usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
+		goto err_address;
+	}
+
+	return EOK;
+
+err_address:
+	bus_release_address(&bus->base, dev->address);
+	return err;
+}
+
+static int unregister_endpoint(bus_t *, endpoint_t *);
+
+int xhci_bus_remove_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
+{
+	int err;
+	xhci_device_t *xhci_dev = xhci_device_get(dev);
+
+	/* Block creation of new endpoints and transfers. */
+	usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*xhci_dev));
+	fibril_mutex_lock(&dev->guard);
+	xhci_dev->online = false;
+	fibril_mutex_unlock(&dev->guard);
+
+	/* Abort running transfers. */
+	usb_log_debug2("Aborting all active transfers to device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*xhci_dev));
+	for (size_t i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
+		xhci_endpoint_t *ep = xhci_dev->endpoints[i];
+		if (!ep)
+			continue;
+
+		endpoint_abort(&ep->base);
+	}
+
+	/* TODO: Figure out how to handle errors here. So far, they are reported and skipped. */
+
+	/* Make DDF (and all drivers) forget about the device. */
+	if ((err = ddf_fun_unbind(dev->fun))) {
+		usb_log_warning("Failed to unbind DDF function of device " XHCI_DEV_FMT ": %s",
+		    XHCI_DEV_ARGS(*xhci_dev), str_error(err));
+	}
+
+	/* Disable the slot, dropping all endpoints. */
+	const uint32_t slot_id = xhci_dev->slot_id;
+	if ((err = hc_disable_slot(hc, xhci_dev))) {
+		usb_log_warning("Failed to disable slot of device " XHCI_DEV_FMT ": %s",
+		    XHCI_DEV_ARGS(*xhci_dev), str_error(err));
+	}
+
+	bus->devices_by_slot[slot_id] = NULL;
+
+	/* Unregister remaining endpoints, freeing memory. */
+	for (unsigned i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
+		if (!xhci_dev->endpoints[i])
+			continue;
+
+		if ((err = unregister_endpoint(&bus->base, &xhci_dev->endpoints[i]->base))) {
+			usb_log_warning("Failed to unregister endpoint " XHCI_EP_FMT ": %s",
+			    XHCI_EP_ARGS(*xhci_dev->endpoints[i]), str_error(err));
+		}
+	}
+
+	/* Destroy DDF device. */
+	/* XXX: Not a good idea, this method should not destroy devices. */
+	hcd_ddf_device_destroy(dev);
+
+	return EOK;
+}
+
+/** Ops receive generic bus_t pointer. */
+static inline xhci_bus_t *bus_to_xhci_bus(bus_t *bus_base)
+{
+	assert(bus_base);
+	return (xhci_bus_t *) bus_base;
+}
+
+static int enumerate_device(bus_t *bus_base, hcd_t *hcd, device_t *dev)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
+	assert(bus);
+
+	return xhci_bus_enumerate_device(bus, hc, dev);
+}
+
+static int remove_device(bus_t *bus_base, hcd_t *hcd, device_t *dev)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
+	assert(bus);
+
+	return xhci_bus_remove_device(bus, hc, dev);
+}
+
+static int online_device(bus_t *bus_base, hcd_t *hcd, device_t *dev_base)
+{
+	int err;
+
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
+	assert(bus);
+
+	xhci_device_t *dev = xhci_device_get(dev_base);
+	assert(dev);
+
+	/* Transition the device from the Addressed to the Configured state. */
+	if ((err = hc_configure_device(hc, dev->slot_id))) {
+		usb_log_warning("Failed to configure device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*dev));
+	}
+
+	/* Block creation of new endpoints and transfers. */
+	usb_log_debug2("Device " XHCI_DEV_FMT " going online.", XHCI_DEV_ARGS(*dev));
+	fibril_mutex_lock(&dev_base->guard);
+	dev->online = true;
+	fibril_mutex_unlock(&dev_base->guard);
+
+	if ((err = ddf_fun_online(dev_base->fun))) {
+		return err;
+	}
+
+	return EOK;
+}
+
+static int offline_device(bus_t *bus_base, hcd_t *hcd, device_t *dev_base)
+{
+	int err;
+
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
+	assert(bus);
+
+	xhci_device_t *dev = xhci_device_get(dev_base);
+	assert(dev);
+
+	/* Tear down all drivers working with the device. */
+	if ((err = ddf_fun_offline(dev_base->fun))) {
+		return err;
+	}
+
+	/* Block creation of new endpoints and transfers. */
+	usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*dev));
+	fibril_mutex_lock(&dev_base->guard);
+	dev->online = false;
+	fibril_mutex_unlock(&dev_base->guard);
+
+	/* We will need the endpoint array later for DS deallocation. */
+	xhci_endpoint_t *endpoints[ARRAY_SIZE(dev->endpoints)];
+	memcpy(endpoints, dev->endpoints, sizeof(dev->endpoints));
+
+	/* Remove all endpoints except zero. */
+	for (unsigned i = 1; i < ARRAY_SIZE(endpoints); ++i) {
+		if (!endpoints[i])
+			continue;
+
+		/* FIXME: Asserting here that the endpoint is not active. If not, EBUSY? */
+
+		xhci_device_remove_endpoint(endpoints[i]);
+	}
+
+	/* Issue one HC command to simultaneously drop all endpoints except zero. */
+	if ((err = hc_deconfigure_device(hc, dev->slot_id))) {
+		usb_log_warning("Failed to deconfigure device " XHCI_DEV_FMT ".",
+		    XHCI_DEV_ARGS(*dev));
+	}
+
+	/* Tear down TRB ring / PSA. */
+	for (unsigned i = 1; i < ARRAY_SIZE(endpoints); ++i) {
+		if (!endpoints[i])
+			continue;
+
+		xhci_endpoint_free_transfer_ds(endpoints[i]);
+	}
+
+	/* FIXME: What happens to unregistered endpoints now? Destroy them? */
+
+	return EOK;
+}
+
+static endpoint_t *create_endpoint(bus_t *base)
+{
+	xhci_bus_t *bus = bus_to_xhci_bus(base);
+
+	xhci_endpoint_t *ep = calloc(1, sizeof(xhci_endpoint_t));
+	if (!ep)
+		return NULL;
+
+	if (xhci_endpoint_init(ep, bus)) {
+		free(ep);
+		return NULL;
+	}
+
+	return &ep->base;
+}
+
+static void destroy_endpoint(endpoint_t *ep)
+{
+	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
+
+	xhci_endpoint_fini(xhci_ep);
+	free(xhci_ep);
+}
+
+static int register_endpoint(bus_t *bus_base, device_t *device, endpoint_t *ep_base, const usb_endpoint_desc_t *desc)
+{
+	int err;
+	xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
+	xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
+
+	xhci_device_t *dev = xhci_device_get(device);
+
+	if ((err = prepare_endpoint(ep, desc)))
+		return err;
+
+	if ((err = xhci_device_add_endpoint(dev, ep)))
+		goto err_prepared;
+
+	usb_log_info("Endpoint " XHCI_EP_FMT " registered to XHCI bus.", XHCI_EP_ARGS(*ep));
+
+	xhci_ep_ctx_t ep_ctx;
+	xhci_setup_endpoint_context(ep, &ep_ctx);
+
+	if ((err = hc_add_endpoint(bus->hc, dev->slot_id, xhci_endpoint_index(ep), &ep_ctx)))
+		goto err_added;
+
+	return EOK;
+
+err_added:
+	xhci_device_remove_endpoint(ep);
+err_prepared:
+	xhci_endpoint_free_transfer_ds(ep);
+	return err;
+}
+
+static int unregister_endpoint(bus_t *bus_base, endpoint_t *ep_base)
+{
+	int err;
+	xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
+	xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
+	xhci_device_t *dev = xhci_device_get(ep_base->device);
+
+	usb_log_info("Endpoint " XHCI_EP_FMT " unregistered from XHCI bus.", XHCI_EP_ARGS(*ep));
+
+	xhci_device_remove_endpoint(ep);
+
+	/* If device slot is still available, drop the endpoint. */
+	if (dev->slot_id) {
+		if ((err = hc_drop_endpoint(bus->hc, dev->slot_id, xhci_endpoint_index(ep)))) {
+			usb_log_error("Failed to drop endpoint " XHCI_EP_FMT ": %s", XHCI_EP_ARGS(*ep), str_error(err));
+		}
+	} else {
+		usb_log_debug("Not going to drop endpoint " XHCI_EP_FMT " because"
+		    " the slot has already been disabled.", XHCI_EP_ARGS(*ep));
+	}
+
+	/* Tear down TRB ring / PSA. */
+	xhci_endpoint_free_transfer_ds(ep);
+
+	return EOK;
+}
+
+static endpoint_t* find_endpoint(bus_t *bus_base, device_t *dev_base, usb_target_t target, usb_direction_t direction)
+{
+	xhci_device_t *dev = xhci_device_get(dev_base);
+
+	xhci_endpoint_t *ep = xhci_device_get_endpoint(dev, target.endpoint);
+	if (!ep)
+		return NULL;
+
+	return &ep->base;
+}
+
+static int reset_toggle(bus_t *bus_base, usb_target_t target, toggle_reset_mode_t mode)
+{
+	// TODO: Implement me!
+	return ENOTSUP;
+}
+
+static size_t count_bw(endpoint_t *ep, size_t size)
+{
+	// TODO: Implement me!
+	return 0;
+}
+
+/* Endpoint ops, optional (have generic fallback) */
+static bool endpoint_get_toggle(endpoint_t *ep)
+{
+	// TODO: Implement me!
+	return ENOTSUP;
+}
+
+static void endpoint_set_toggle(endpoint_t *ep, bool toggle)
+{
+	// TODO: Implement me!
+}
+
+static int request_address(bus_t *bus_base, usb_address_t *addr, bool strict, usb_speed_t speed)
+{
+	assert(addr);
+
+	if (*addr != USB_ADDRESS_DEFAULT)
+		/* xHCI does not allow software to assign addresses. */
+		return ENOTSUP;
+
+	assert(strict);
+
+	xhci_bus_t *xhci_bus = bus_to_xhci_bus(bus_base);
+
+	if (xhci_bus->default_address_speed != USB_SPEED_MAX)
+		/* Already allocated */
+		return ENOENT;
+
+	xhci_bus->default_address_speed = speed;
+	return EOK;
+}
+
+static int release_address(bus_t *bus_base, usb_address_t addr)
+{
+	if (addr != USB_ADDRESS_DEFAULT)
+		return ENOTSUP;
+
+	xhci_bus_t *xhci_bus = bus_to_xhci_bus(bus_base);
+
+	xhci_bus->default_address_speed = USB_SPEED_MAX;
+	return EOK;
+}
+
+static usb_transfer_batch_t *create_batch(bus_t *bus, endpoint_t *ep)
+{
+	xhci_transfer_t *transfer = xhci_transfer_create(ep);
+	return &transfer->batch;
+}
+
+static void destroy_batch(usb_transfer_batch_t *batch)
+{
+	xhci_transfer_destroy(xhci_transfer_from_batch(batch));
+}
+
+static const bus_ops_t xhci_bus_ops = {
+#define BIND_OP(op) .op = op,
+	BIND_OP(enumerate_device)
+	BIND_OP(remove_device)
+
+	BIND_OP(online_device)
+	BIND_OP(offline_device)
+
+	BIND_OP(create_endpoint)
+	BIND_OP(destroy_endpoint)
+
+	BIND_OP(register_endpoint)
+	BIND_OP(unregister_endpoint)
+	BIND_OP(find_endpoint)
+
+	BIND_OP(request_address)
+	BIND_OP(release_address)
+	BIND_OP(reset_toggle)
+
+	BIND_OP(count_bw)
+
+	BIND_OP(endpoint_get_toggle)
+	BIND_OP(endpoint_set_toggle)
+
+	BIND_OP(create_batch)
+	BIND_OP(destroy_batch)
+#undef BIND_OP
+};
+
+int xhci_bus_init(xhci_bus_t *bus, xhci_hc_t *hc)
+{
+	assert(bus);
+
+	bus_init(&bus->base, sizeof(xhci_device_t));
+
+	bus->devices_by_slot = calloc(hc->max_slots, sizeof(xhci_device_t *));
+	if (!bus->devices_by_slot)
+		return ENOMEM;
+
+	bus->hc = hc;
+	bus->base.ops = xhci_bus_ops;
+	bus->default_address_speed = USB_SPEED_MAX;
+	return EOK;
+}
+
+void xhci_bus_fini(xhci_bus_t *bus)
+{
+
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/bus.h
===================================================================
--- uspace/drv/bus/usb/xhci/bus.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/bus.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
+ * 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 libusbhost
+ * @{
+ */
+/** @file
+ *
+ * An implementation of bus keeper for xHCI. The (physical) HC itself takes
+ * care about addressing devices, so this implementation is actually simpler
+ * than those of [OUE]HCI.
+ */
+#ifndef XHCI_BUS_H
+#define XHCI_BUS_H
+
+#include <usb/usb.h>
+#include <usb/host/bus.h>
+
+typedef struct xhci_hc xhci_hc_t;
+typedef struct xhci_device xhci_device_t;
+
+/** Endpoint management structure */
+typedef struct xhci_bus {
+	bus_t base;		/**< Inheritance. Keep this first. */
+
+	xhci_hc_t *hc;				/**< Pointer to managing HC (to issue commands) */
+
+	xhci_device_t **devices_by_slot;	/**< Devices by Slot ID */
+
+	usb_speed_t default_address_speed;	/**< Used to get speed from usb hubs */
+} xhci_bus_t;
+
+int xhci_bus_init(xhci_bus_t *, xhci_hc_t *);
+void xhci_bus_fini(xhci_bus_t *);
+
+int xhci_bus_enumerate_device(xhci_bus_t *, xhci_hc_t *, device_t *);
+int xhci_bus_remove_device(xhci_bus_t *, xhci_hc_t *, device_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/commands.c
===================================================================
--- uspace/drv/bus/usb/xhci/commands.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/commands.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,644 @@
+/*
+ * Copyright (c) 2017 Jaroslav Jindrak
+ * 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 Command sending functions.
+ */
+
+#include <errno.h>
+#include <str_error.h>
+#include <usb/debug.h>
+#include <usb/host/utils/malloc32.h>
+#include "commands.h"
+#include "debug.h"
+#include "hc.h"
+#include "hw_struct/context.h"
+#include "hw_struct/trb.h"
+
+#define TRB_SET_TCS(trb, tcs)   (trb).control |= host2xhci(32, ((tcs &0x1) << 9))
+#define TRB_SET_TYPE(trb, type) (trb).control |= host2xhci(32, (type) << 10)
+#define TRB_SET_DC(trb, dc)     (trb).control |= host2xhci(32, (dc) << 9)
+#define TRB_SET_EP(trb, ep)     (trb).control |= host2xhci(32, ((ep) & 0x5) << 16)
+#define TRB_SET_STREAM(trb, st) (trb).control |= host2xhci(32, ((st) & 0xFFFF) << 16)
+#define TRB_SET_SUSP(trb, susp) (trb).control |= host2xhci(32, ((susp) & 0x1) << 23)
+#define TRB_SET_SLOT(trb, slot) (trb).control |= host2xhci(32, (slot) << 24)
+#define TRB_SET_DEV_SPEED(trb, speed)	(trb).control |= host2xhci(32, (speed & 0xF) << 16)
+
+/**
+ * TODO: Not sure about SCT and DCS (see section 6.4.3.9).
+ */
+#define TRB_SET_DEQUEUE_PTR(trb, dptr) (trb).parameter |= host2xhci(64, (dptr))
+#define TRB_SET_ICTX(trb, phys) (trb).parameter |= host2xhci(64, phys_addr & (~0xF))
+
+#define TRB_GET_CODE(trb) XHCI_DWORD_EXTRACT((trb).status, 31, 24)
+#define TRB_GET_SLOT(trb) XHCI_DWORD_EXTRACT((trb).control, 31, 24)
+#define TRB_GET_PHYS(trb) (XHCI_QWORD_EXTRACT((trb).parameter, 63, 4) << 4)
+
+/* Control functions */
+
+int xhci_init_commands(xhci_hc_t *hc)
+{
+	assert(hc);
+
+	list_initialize(&hc->commands);
+
+	fibril_mutex_initialize(&hc->commands_mtx);
+
+	return EOK;
+}
+
+void xhci_fini_commands(xhci_hc_t *hc)
+{
+	// Note: Untested.
+	assert(hc);
+}
+
+void xhci_cmd_init(xhci_cmd_t *cmd, xhci_cmd_type_t type)
+{
+	memset(cmd, 0, sizeof(*cmd));
+
+	link_initialize(&cmd->_header.link);
+
+	fibril_mutex_initialize(&cmd->_header.completed_mtx);
+	fibril_condvar_initialize(&cmd->_header.completed_cv);
+
+	cmd->_header.cmd = type;
+	cmd->_header.timeout = XHCI_DEFAULT_TIMEOUT;
+}
+
+void xhci_cmd_fini(xhci_cmd_t *cmd)
+{
+	list_remove(&cmd->_header.link);
+
+	if (cmd->input_ctx) {
+		free32(cmd->input_ctx);
+	};
+
+	if (cmd->bandwidth_ctx) {
+		free32(cmd->bandwidth_ctx);
+	}
+
+	if (cmd->_header.async) {
+		free(cmd);
+	}
+}
+
+static inline xhci_cmd_t *get_command(xhci_hc_t *hc, uint64_t phys)
+{
+	fibril_mutex_lock(&hc->commands_mtx);
+
+	link_t *cmd_link = list_first(&hc->commands);
+
+	while (cmd_link != NULL) {
+		xhci_cmd_t *cmd = list_get_instance(cmd_link, xhci_cmd_t, _header.link);
+
+		if (cmd->_header.trb_phys == phys)
+			break;
+
+		cmd_link = list_next(cmd_link, &hc->commands);
+	}
+
+	if (cmd_link != NULL) {
+		list_remove(cmd_link);
+		fibril_mutex_unlock(&hc->commands_mtx);
+
+		return list_get_instance(cmd_link, xhci_cmd_t, _header.link);
+	}
+
+	fibril_mutex_unlock(&hc->commands_mtx);
+	return NULL;
+}
+
+static inline int enqueue_command(xhci_hc_t *hc, xhci_cmd_t *cmd, unsigned doorbell, unsigned target)
+{
+	assert(hc);
+	assert(cmd);
+
+	fibril_mutex_lock(&hc->commands_mtx);
+	list_append(&cmd->_header.link, &hc->commands);
+	fibril_mutex_unlock(&hc->commands_mtx);
+
+	xhci_trb_ring_enqueue(&hc->command_ring, &cmd->_header.trb, &cmd->_header.trb_phys);
+	hc_ring_doorbell(hc, doorbell, target);
+
+	usb_log_debug2("HC(%p): Sent command:", hc);
+	xhci_dump_trb(&cmd->_header.trb);
+
+	return EOK;
+}
+
+void xhci_stop_command_ring(xhci_hc_t *hc)
+{
+	assert(hc);
+
+	XHCI_REG_SET(hc->op_regs, XHCI_OP_CS, 1);
+
+	/**
+	 * Note: There is a bug in qemu that checks CS only when CRCR_HI
+	 *       is written, this (and the read/write in abort) ensures
+	 *       the command rings stops.
+	 */
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_HI, XHCI_REG_RD(hc->op_regs, XHCI_OP_CRCR_HI));
+}
+
+void xhci_abort_command_ring(xhci_hc_t *hc)
+{
+	assert(hc);
+
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_CA, 1);
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_HI, XHCI_REG_RD(hc->op_regs, XHCI_OP_CRCR_HI));
+}
+
+void xhci_start_command_ring(xhci_hc_t *hc)
+{
+	assert(hc);
+
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_CRR, 1);
+	hc_ring_doorbell(hc, 0, 0);
+}
+
+static const char *trb_codes [] = {
+#define TRBC(t) [XHCI_TRBC_##t] = #t
+	TRBC(INVALID),
+	TRBC(SUCCESS),
+	TRBC(DATA_BUFFER_ERROR),
+	TRBC(BABBLE_DETECTED_ERROR),
+	TRBC(USB_TRANSACTION_ERROR),
+	TRBC(TRB_ERROR),
+	TRBC(STALL_ERROR),
+	TRBC(RESOURCE_ERROR),
+	TRBC(BANDWIDTH_ERROR),
+	TRBC(NO_SLOTS_ERROR),
+	TRBC(INVALID_STREAM_ERROR),
+	TRBC(SLOT_NOT_ENABLED_ERROR),
+	TRBC(EP_NOT_ENABLED_ERROR),
+	TRBC(SHORT_PACKET),
+	TRBC(RING_UNDERRUN),
+	TRBC(RING_OVERRUN),
+	TRBC(VF_EVENT_RING_FULL),
+	TRBC(PARAMETER_ERROR),
+	TRBC(BANDWIDTH_OVERRUN_ERROR),
+	TRBC(CONTEXT_STATE_ERROR),
+	TRBC(NO_PING_RESPONSE_ERROR),
+	TRBC(EVENT_RING_FULL_ERROR),
+	TRBC(INCOMPATIBLE_DEVICE_ERROR),
+	TRBC(MISSED_SERVICE_ERROR),
+	TRBC(COMMAND_RING_STOPPED),
+	TRBC(COMMAND_ABORTED),
+	TRBC(STOPPED),
+	TRBC(STOPPED_LENGTH_INVALID),
+	TRBC(STOPPED_SHORT_PACKET),
+	TRBC(MAX_EXIT_LATENCY_TOO_LARGE_ERROR),
+	[30] = "<reserved>",
+	TRBC(ISOCH_BUFFER_OVERRUN),
+	TRBC(EVENT_LOST_ERROR),
+	TRBC(UNDEFINED_ERROR),
+	TRBC(INVALID_STREAM_ID_ERROR),
+	TRBC(SECONDARY_BANDWIDTH_ERROR),
+	TRBC(SPLIT_TRANSACTION_ERROR),
+	[XHCI_TRBC_MAX] = NULL
+#undef TRBC
+};
+
+static void report_error(int code)
+{
+	if (code < XHCI_TRBC_MAX && trb_codes[code] != NULL)
+		usb_log_error("Command resulted in error: %s.", trb_codes[code]);
+	else
+		usb_log_error("Command resulted in reserved or vendor specific error.");
+}
+
+int xhci_handle_command_completion(xhci_hc_t *hc, xhci_trb_t *trb)
+{
+	// TODO: Update dequeue ptrs.
+	assert(hc);
+	assert(trb);
+
+	usb_log_debug2("HC(%p) Command completed.", hc);
+
+	int code;
+	uint64_t phys;
+	xhci_cmd_t *command;
+
+	code = TRB_GET_CODE(*trb);
+	phys = TRB_GET_PHYS(*trb);;
+	command = get_command(hc, phys);
+	if (command == NULL) {
+		// TODO: STOP & ABORT may not have command structs in the list!
+		usb_log_warning("No command struct for this completion event found.");
+
+		if (code != XHCI_TRBC_SUCCESS)
+			report_error(code);
+
+		return EOK;
+	}
+
+	/* Semantics of NO_OP_CMD is that success is marked as a TRB error. */
+	if (command->_header.cmd == XHCI_CMD_NO_OP && code == XHCI_TRBC_TRB_ERROR)
+		code = XHCI_TRBC_SUCCESS;
+
+	command->status = code;
+	command->slot_id = TRB_GET_SLOT(*trb);
+
+	usb_log_debug2("Completed command trb: %s", xhci_trb_str_type(TRB_TYPE(command->_header.trb)));
+
+	if (code != XHCI_TRBC_SUCCESS) {
+		report_error(code);
+		xhci_dump_trb(&command->_header.trb);
+	}
+
+	switch (TRB_TYPE(command->_header.trb)) {
+	case XHCI_TRB_TYPE_NO_OP_CMD:
+		break;
+	case XHCI_TRB_TYPE_ENABLE_SLOT_CMD:
+		break;
+	case XHCI_TRB_TYPE_DISABLE_SLOT_CMD:
+		break;
+	case XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD:
+		break;
+	case XHCI_TRB_TYPE_CONFIGURE_ENDPOINT_CMD:
+		break;
+	case XHCI_TRB_TYPE_EVALUATE_CONTEXT_CMD:
+		break;
+	case XHCI_TRB_TYPE_RESET_ENDPOINT_CMD:
+		break;
+	case XHCI_TRB_TYPE_STOP_ENDPOINT_CMD:
+		// Note: If the endpoint was in the middle of a transfer, then the xHC
+		//       will add a Transfer TRB before the Event TRB, research that and
+		//       handle it appropriately!
+		break;
+	case XHCI_TRB_TYPE_RESET_DEVICE_CMD:
+		break;
+	default:
+		usb_log_debug2("Unsupported command trb: %s", xhci_trb_str_type(TRB_TYPE(command->_header.trb)));
+
+		command->_header.completed = true;
+		return ENAK;
+	}
+
+	fibril_mutex_lock(&command->_header.completed_mtx);
+	command->_header.completed = true;
+	fibril_condvar_broadcast(&command->_header.completed_cv);
+	fibril_mutex_unlock(&command->_header.completed_mtx);
+
+	if (command->_header.async) {
+		/* Free the command and other DS upon completion. */
+		xhci_cmd_fini(command);
+	}
+
+	return EOK;
+}
+
+/* Command-issuing functions */
+
+static int no_op_cmd(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	assert(hc);
+
+	xhci_trb_clean(&cmd->_header.trb);
+
+	TRB_SET_TYPE(cmd->_header.trb, XHCI_TRB_TYPE_NO_OP_CMD);
+
+	return enqueue_command(hc, cmd, 0, 0);
+}
+
+static int enable_slot_cmd(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	assert(hc);
+
+	xhci_trb_clean(&cmd->_header.trb);
+
+	TRB_SET_TYPE(cmd->_header.trb, XHCI_TRB_TYPE_ENABLE_SLOT_CMD);
+	cmd->_header.trb.control |= host2xhci(32, XHCI_REG_RD(hc->xecp, XHCI_EC_SP_SLOT_TYPE) << 16);
+
+	return enqueue_command(hc, cmd, 0, 0);
+}
+
+static int disable_slot_cmd(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	assert(hc);
+	assert(cmd);
+
+	xhci_trb_clean(&cmd->_header.trb);
+
+	TRB_SET_TYPE(cmd->_header.trb, XHCI_TRB_TYPE_DISABLE_SLOT_CMD);
+	TRB_SET_SLOT(cmd->_header.trb, cmd->slot_id);
+
+	return enqueue_command(hc, cmd, 0, 0);
+}
+
+static int address_device_cmd(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	assert(hc);
+	assert(cmd);
+	assert(cmd->input_ctx);
+
+	/**
+	 * TODO: Requirements for this command:
+	 *           dcbaa[slot_id] is properly sized and initialized
+	 *           ictx has valids slot context and endpoint 0, all
+	 *           other should be ignored at this point (see section 4.6.5).
+	 */
+
+	xhci_trb_clean(&cmd->_header.trb);
+
+	uint64_t phys_addr = (uint64_t) addr_to_phys(cmd->input_ctx);
+	TRB_SET_ICTX(cmd->_header.trb, phys_addr);
+
+	/**
+	 * Note: According to section 6.4.3.4, we can set the 9th bit
+	 *       of the control field of the trb (BSR) to 1 and then the xHC
+	 *       will not issue the SET_ADDRESS request to the USB device.
+	 *       This can be used to provide compatibility with legacy USB devices
+	 *       that require their device descriptor to be read before such request.
+	 */
+	TRB_SET_TYPE(cmd->_header.trb, XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD);
+	TRB_SET_SLOT(cmd->_header.trb, cmd->slot_id);
+
+	return enqueue_command(hc, cmd, 0, 0);
+}
+
+static int configure_endpoint_cmd(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	assert(hc);
+	assert(cmd);
+
+	xhci_trb_clean(&cmd->_header.trb);
+
+	if (!cmd->deconfigure) {
+		/* If the DC flag is on, input context is not evaluated. */
+		assert(cmd->input_ctx);
+
+		uint64_t phys_addr = (uint64_t) addr_to_phys(cmd->input_ctx);
+		TRB_SET_ICTX(cmd->_header.trb, phys_addr);
+	}
+
+	TRB_SET_TYPE(cmd->_header.trb, XHCI_TRB_TYPE_CONFIGURE_ENDPOINT_CMD);
+	TRB_SET_SLOT(cmd->_header.trb, cmd->slot_id);
+	TRB_SET_DC(cmd->_header.trb, cmd->deconfigure);
+
+	return enqueue_command(hc, cmd, 0, 0);
+}
+
+static int evaluate_context_cmd(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	assert(hc);
+	assert(cmd);
+	assert(cmd->input_ctx);
+
+	/**
+	 * Note: All Drop Context flags of the input context shall be 0,
+	 *       all Add Context flags shall be initialize to indicate IDs
+	 *       of the contexts affected by the command.
+	 *       Refer to sections 6.2.2.3 and 6.3.3.3 for further info.
+	 */
+	xhci_trb_clean(&cmd->_header.trb);
+
+	uint64_t phys_addr = (uint64_t) addr_to_phys(cmd->input_ctx);
+	TRB_SET_ICTX(cmd->_header.trb, phys_addr);
+
+	TRB_SET_TYPE(cmd->_header.trb, XHCI_TRB_TYPE_EVALUATE_CONTEXT_CMD);
+	TRB_SET_SLOT(cmd->_header.trb, cmd->slot_id);
+
+	return enqueue_command(hc, cmd, 0, 0);
+}
+
+static int reset_endpoint_cmd(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	assert(hc);
+	assert(cmd);
+
+	/**
+	 * Note: TCS can have values 0 or 1. If it is set to 0, see sectuon 4.5.8 for
+	 *       information about this flag.
+	 */
+	xhci_trb_clean(&cmd->_header.trb);
+
+	TRB_SET_TYPE(cmd->_header.trb, XHCI_TRB_TYPE_RESET_ENDPOINT_CMD);
+	TRB_SET_TCS(cmd->_header.trb, cmd->tcs);
+	TRB_SET_EP(cmd->_header.trb, cmd->endpoint_id);
+	TRB_SET_SLOT(cmd->_header.trb, cmd->slot_id);
+
+	return enqueue_command(hc, cmd, 0, 0);
+}
+
+static int stop_endpoint_cmd(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	assert(hc);
+	assert(cmd);
+
+	xhci_trb_clean(&cmd->_header.trb);
+
+	TRB_SET_TYPE(cmd->_header.trb, XHCI_TRB_TYPE_STOP_ENDPOINT_CMD);
+	TRB_SET_EP(cmd->_header.trb, cmd->endpoint_id);
+	TRB_SET_SUSP(cmd->_header.trb, cmd->susp);
+	TRB_SET_SLOT(cmd->_header.trb, cmd->slot_id);
+
+	return enqueue_command(hc, cmd, 0, 0);
+}
+
+static int set_tr_dequeue_pointer_cmd(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	assert(hc);
+	assert(cmd);
+
+	xhci_trb_clean(&cmd->_header.trb);
+
+	TRB_SET_TYPE(cmd->_header.trb, XHCI_TRB_TYPE_SET_TR_DEQUEUE_POINTER_CMD);
+	TRB_SET_EP(cmd->_header.trb, cmd->endpoint_id);
+	TRB_SET_STREAM(cmd->_header.trb, cmd->stream_id);
+	TRB_SET_SLOT(cmd->_header.trb, cmd->slot_id);
+	TRB_SET_DEQUEUE_PTR(cmd->_header.trb, cmd->dequeue_ptr);
+
+	/**
+	 * TODO: Set DCS (see section 4.6.10).
+	 */
+
+	return enqueue_command(hc, cmd, 0, 0);
+}
+
+static int reset_device_cmd(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	assert(hc);
+	assert(cmd);
+
+	xhci_trb_clean(&cmd->_header.trb);
+
+	TRB_SET_TYPE(cmd->_header.trb, XHCI_TRB_TYPE_RESET_DEVICE_CMD);
+	TRB_SET_SLOT(cmd->_header.trb, cmd->slot_id);
+
+	return enqueue_command(hc, cmd, 0, 0);
+}
+
+static int get_port_bandwidth_cmd(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	assert(hc);
+	assert(cmd);
+
+	xhci_trb_clean(&cmd->_header.trb);
+
+	uint64_t phys_addr = (uint64_t) addr_to_phys(cmd->bandwidth_ctx);
+	TRB_SET_ICTX(cmd->_header.trb, phys_addr);
+
+	TRB_SET_TYPE(cmd->_header.trb, XHCI_TRB_TYPE_GET_PORT_BANDWIDTH_CMD);
+	TRB_SET_SLOT(cmd->_header.trb, cmd->slot_id);
+	TRB_SET_DEV_SPEED(cmd->_header.trb, cmd->device_speed);
+
+	return enqueue_command(hc, cmd, 0, 0);
+}
+
+/* The table of command-issuing functions. */
+
+typedef int (*cmd_handler) (xhci_hc_t *hc, xhci_cmd_t *cmd);
+
+static cmd_handler cmd_handlers [] = {
+	[XHCI_CMD_ENABLE_SLOT] = enable_slot_cmd,
+	[XHCI_CMD_DISABLE_SLOT] = disable_slot_cmd,
+	[XHCI_CMD_ADDRESS_DEVICE] = address_device_cmd,
+	[XHCI_CMD_CONFIGURE_ENDPOINT] = configure_endpoint_cmd,
+	[XHCI_CMD_EVALUATE_CONTEXT] = evaluate_context_cmd,
+	[XHCI_CMD_RESET_ENDPOINT] = reset_endpoint_cmd,
+	[XHCI_CMD_STOP_ENDPOINT] = stop_endpoint_cmd,
+	[XHCI_CMD_SET_TR_DEQUEUE_POINTER] = set_tr_dequeue_pointer_cmd,
+	[XHCI_CMD_RESET_DEVICE] = reset_device_cmd,
+	// TODO: Force event (optional normative, for VMM, section 4.6.12).
+	[XHCI_CMD_FORCE_EVENT] = NULL,
+	// TODO: Negotiate bandwidth (optional normative, section 4.6.13).
+	[XHCI_CMD_NEGOTIATE_BANDWIDTH] = NULL,
+	// TODO: Set latency tolerance value (optional normative, section 4.6.14).
+	[XHCI_CMD_SET_LATENCY_TOLERANCE_VALUE] = NULL,
+	// TODO: Get port bandwidth (mandatory, but needs root hub implementation, section 4.6.15).
+	[XHCI_CMD_GET_PORT_BANDWIDTH] = get_port_bandwidth_cmd,
+	// TODO: Force header (mandatory, but needs root hub implementation, section 4.6.16).
+	[XHCI_CMD_FORCE_HEADER] = NULL,
+	[XHCI_CMD_NO_OP] = no_op_cmd
+};
+
+static int wait_for_cmd_completion(xhci_cmd_t *cmd)
+{
+	int rv = EOK;
+
+	fibril_mutex_lock(&cmd->_header.completed_mtx);
+	while (!cmd->_header.completed) {
+		usb_log_debug2("Waiting for event completion: going to sleep.");
+		rv = fibril_condvar_wait_timeout(&cmd->_header.completed_cv, &cmd->_header.completed_mtx, cmd->_header.timeout);
+
+		usb_log_debug2("Waiting for event completion: woken: %s", str_error(rv));
+		if (rv == ETIMEOUT) {
+			break;
+		}
+	}
+	fibril_mutex_unlock(&cmd->_header.completed_mtx);
+
+	return rv;
+}
+
+/** Issue command and block the current fibril until it is completed or timeout
+ *  expires. Nothing is deallocated. Caller should always execute `xhci_cmd_fini`.
+ */
+int xhci_cmd_sync(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	assert(hc);
+	assert(cmd);
+
+	int err;
+
+	if (!cmd_handlers[cmd->_header.cmd]) {
+		/* Handler not implemented. */
+		return ENOTSUP;
+	}
+
+	if ((err = cmd_handlers[cmd->_header.cmd](hc, cmd))) {
+		/* Command could not be issued. */
+		return err;
+	}
+
+	if ((err = wait_for_cmd_completion(cmd))) {
+		/* Timeout expired or command failed. */
+		return err;
+	}
+
+	return cmd->status == XHCI_TRBC_SUCCESS ? EOK : EINVAL;
+}
+
+/** Does the same thing as `xhci_cmd_sync` and executes `xhci_cmd_fini`. This
+ *  is a useful shorthand for issuing commands without out parameters.
+ */
+int xhci_cmd_sync_fini(xhci_hc_t *hc, xhci_cmd_t *cmd)
+{
+	const int err = xhci_cmd_sync(hc, cmd);
+	xhci_cmd_fini(cmd);
+
+	return err;
+}
+
+/** Does the same thing as `xhci_cmd_sync_fini` without blocking the current
+ *  fibril. The command is copied to stack memory and `fini` is called upon its completion.
+ */
+int xhci_cmd_async_fini(xhci_hc_t *hc, xhci_cmd_t *stack_cmd)
+{
+	assert(hc);
+	assert(stack_cmd);
+
+	/* Save the command for later. */
+	xhci_cmd_t *heap_cmd = (xhci_cmd_t *) malloc(sizeof(xhci_cmd_t));
+	if (!heap_cmd) {
+		return ENOMEM;
+	}
+
+	/* TODO: Is this good for the mutex and the condvar? */
+	memcpy(heap_cmd, stack_cmd, sizeof(xhci_cmd_t));
+	heap_cmd->_header.async = true;
+
+	/* Issue the command. */
+	int err;
+
+	if (!cmd_handlers[heap_cmd->_header.cmd]) {
+		/* Handler not implemented. */
+		err = ENOTSUP;
+		goto err_heap_cmd;
+	}
+
+	if ((err = cmd_handlers[heap_cmd->_header.cmd](hc, heap_cmd))) {
+		/* Command could not be issued. */
+		goto err_heap_cmd;
+	}
+
+	return EOK;
+
+err_heap_cmd:
+	free(heap_cmd);
+	return err;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/commands.h
===================================================================
--- uspace/drv/bus/usb/xhci/commands.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/commands.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2017 Jaroslav Jindrak
+ * 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
+ * Utility functions used to place TRBs onto the command ring.
+ */
+
+#ifndef XHCI_COMMANDS_H
+#define XHCI_COMMANDS_H
+
+#include <adt/list.h>
+#include <stdbool.h>
+#include <fibril_synch.h>
+#include "hw_struct/trb.h"
+
+#define XHCI_DEFAULT_TIMEOUT       1000000
+#define XHCI_BLOCK_INDEFINITELY    0
+
+typedef struct xhci_hc xhci_hc_t;
+typedef struct xhci_input_ctx xhci_input_ctx_t;
+typedef struct xhci_port_bandwidth_ctx xhci_port_bandwidth_ctx_t;
+
+typedef enum xhci_cmd_type {
+	XHCI_CMD_ENABLE_SLOT,
+	XHCI_CMD_DISABLE_SLOT,
+	XHCI_CMD_ADDRESS_DEVICE,
+	XHCI_CMD_CONFIGURE_ENDPOINT,
+	XHCI_CMD_EVALUATE_CONTEXT,
+	XHCI_CMD_RESET_ENDPOINT,
+	XHCI_CMD_STOP_ENDPOINT,
+	XHCI_CMD_SET_TR_DEQUEUE_POINTER,
+	XHCI_CMD_RESET_DEVICE,
+	XHCI_CMD_FORCE_EVENT,
+	XHCI_CMD_NEGOTIATE_BANDWIDTH,
+	XHCI_CMD_SET_LATENCY_TOLERANCE_VALUE,
+	XHCI_CMD_GET_PORT_BANDWIDTH,
+	XHCI_CMD_FORCE_HEADER,
+	XHCI_CMD_NO_OP,
+} xhci_cmd_type_t;
+
+typedef struct xhci_command {
+	/** Internal fields used for bookkeeping. Need not worry about these. */
+	struct {
+		link_t link;
+
+		xhci_cmd_type_t cmd;
+		suseconds_t timeout;
+
+		xhci_trb_t trb;
+		uintptr_t trb_phys;
+
+		bool async;
+		bool completed;
+
+		/* Will broadcast after command completes. */
+		fibril_mutex_t completed_mtx;
+		fibril_condvar_t completed_cv;
+	} _header;
+
+	/** Below are arguments of all commands mixed together.
+	 *  Be sure to know which command accepts what arguments. */
+
+	uint32_t slot_id;
+	uint32_t endpoint_id;
+	uint16_t stream_id;
+
+	xhci_input_ctx_t *input_ctx;
+	xhci_port_bandwidth_ctx_t *bandwidth_ctx;
+	uintptr_t dequeue_ptr;
+
+	uint8_t tcs;
+	uint8_t susp;
+	uint8_t device_speed;
+	uint32_t status;
+	bool deconfigure;
+} xhci_cmd_t;
+
+/* Command handling control */
+int xhci_init_commands(xhci_hc_t *);
+void xhci_fini_commands(xhci_hc_t *);
+
+void xhci_stop_command_ring(xhci_hc_t *);
+void xhci_abort_command_ring(xhci_hc_t *);
+void xhci_start_command_ring(xhci_hc_t *);
+
+int xhci_handle_command_completion(xhci_hc_t *, xhci_trb_t *);
+
+/* Command lifecycle */
+void xhci_cmd_init(xhci_cmd_t *, xhci_cmd_type_t);
+void xhci_cmd_fini(xhci_cmd_t *);
+
+/* Issuing commands */
+int xhci_cmd_sync(xhci_hc_t *, xhci_cmd_t *);
+int xhci_cmd_sync_fini(xhci_hc_t *, xhci_cmd_t *);
+int xhci_cmd_async_fini(xhci_hc_t *, xhci_cmd_t *);
+
+static inline int xhci_cmd_sync_inline_wrapper(xhci_hc_t *hc, xhci_cmd_t cmd)
+{
+	/* Poor man's xhci_cmd_init (everything else is zeroed) */
+	link_initialize(&cmd._header.link);
+	fibril_mutex_initialize(&cmd._header.completed_mtx);
+	fibril_condvar_initialize(&cmd._header.completed_cv);
+
+	if (!cmd._header.timeout) {
+		cmd._header.timeout = XHCI_DEFAULT_TIMEOUT;
+	}
+
+	/* Issue the command */
+	const int err = xhci_cmd_sync(hc, &cmd);
+	xhci_cmd_fini(&cmd);
+
+	return err;
+}
+
+/** The inline macro expects:
+ *    - hc      - HC to schedule command on (xhci_hc_t *).
+ *    - command - Member of `xhci_cmd_type_t` without the "XHCI_CMD_" prefix.
+ *    - VA_ARGS - (optional) Command arguments in struct initialization notation.
+ *
+ *  The return code and semantics matches those of `xhci_cmd_sync_fini`.
+ *
+ *  Example:
+ *    int err = xhci_cmd_sync_inline(hc, DISABLE_SLOT, .slot_id = 42);
+ */
+
+#define xhci_cmd_sync_inline(hc, command, ...) \
+	xhci_cmd_sync_inline_wrapper(hc, \
+	(xhci_cmd_t) { ._header.cmd = XHCI_CMD_##command, ##__VA_ARGS__ })
+
+#endif
+
+
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/debug.c
===================================================================
--- uspace/drv/bus/usb/xhci/debug.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/debug.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty
+ * 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
+ * Memory-mapped register structures of the xHC.
+ */
+
+#include <inttypes.h>
+#include <byteorder.h>
+#include <usb/debug.h>
+
+#include "hw_struct/trb.h"
+#include "debug.h"
+#include "hc.h"
+
+#define PX "\t%-21s = "
+
+#define DUMP_REG_FIELD(ptr, title, size, ...) \
+	usb_log_debug2(PX "%" PRIu##size, title, XHCI_REG_RD_FIELD(ptr, size, ##__VA_ARGS__))
+
+#define DUMP_REG_RANGE(ptr, title, size, ...) \
+	usb_log_debug2(PX "%" PRIu##size, title, XHCI_REG_RD_RANGE(ptr, size, ##__VA_ARGS__))
+
+#define DUMP_REG_FLAG(ptr, title, size, ...) \
+	usb_log_debug2(PX "%s", title, XHCI_REG_RD_FLAG(ptr, size, ##__VA_ARGS__) ? "true" : "false")
+
+#define DUMP_REG_INNER(set, title, field, size, type, ...) \
+	DUMP_REG_##type(&(set)->field, title, size, ##__VA_ARGS__)
+
+#define DUMP_REG(set, c) DUMP_REG_INNER(set, #c, c)
+
+/**
+ * Dumps all capability registers.
+ */
+void xhci_dump_cap_regs(const xhci_cap_regs_t *cap)
+{
+	usb_log_debug2("Capabilities:");
+
+	DUMP_REG(cap, XHCI_CAP_LENGTH);
+	DUMP_REG(cap, XHCI_CAP_VERSION);
+	DUMP_REG(cap, XHCI_CAP_MAX_SLOTS);
+	DUMP_REG(cap, XHCI_CAP_MAX_INTRS);
+	DUMP_REG(cap, XHCI_CAP_MAX_PORTS);
+	DUMP_REG(cap, XHCI_CAP_IST);
+	DUMP_REG(cap, XHCI_CAP_ERST_MAX);
+	usb_log_debug2(PX "%u", "Max Scratchpad bufs", xhci_get_max_spbuf(cap));
+	DUMP_REG(cap, XHCI_CAP_SPR);
+	DUMP_REG(cap, XHCI_CAP_U1EL);
+	DUMP_REG(cap, XHCI_CAP_U2EL);
+	DUMP_REG(cap, XHCI_CAP_AC64);
+	DUMP_REG(cap, XHCI_CAP_BNC);
+	DUMP_REG(cap, XHCI_CAP_CSZ);
+	DUMP_REG(cap, XHCI_CAP_PPC);
+	DUMP_REG(cap, XHCI_CAP_PIND);
+	DUMP_REG(cap, XHCI_CAP_C);
+	DUMP_REG(cap, XHCI_CAP_LTC);
+	DUMP_REG(cap, XHCI_CAP_NSS);
+	DUMP_REG(cap, XHCI_CAP_PAE);
+	DUMP_REG(cap, XHCI_CAP_SPC);
+	DUMP_REG(cap, XHCI_CAP_SEC);
+	DUMP_REG(cap, XHCI_CAP_CFC);
+	DUMP_REG(cap, XHCI_CAP_MAX_PSA_SIZE);
+	DUMP_REG(cap, XHCI_CAP_XECP);
+	DUMP_REG(cap, XHCI_CAP_DBOFF);
+	DUMP_REG(cap, XHCI_CAP_RTSOFF);
+	DUMP_REG(cap, XHCI_CAP_U3C);
+	DUMP_REG(cap, XHCI_CAP_CMC);
+	DUMP_REG(cap, XHCI_CAP_FSC);
+	DUMP_REG(cap, XHCI_CAP_CTC);
+	DUMP_REG(cap, XHCI_CAP_LEC);
+	DUMP_REG(cap, XHCI_CAP_CIC);
+}
+
+void xhci_dump_port(const xhci_port_regs_t *port)
+{
+	DUMP_REG(port, XHCI_PORT_CCS);
+	DUMP_REG(port, XHCI_PORT_PED);
+	DUMP_REG(port, XHCI_PORT_OCA);
+	DUMP_REG(port, XHCI_PORT_PR);
+	DUMP_REG(port, XHCI_PORT_PLS);
+	DUMP_REG(port, XHCI_PORT_PP);
+	DUMP_REG(port, XHCI_PORT_PIC);
+	DUMP_REG(port, XHCI_PORT_LWS);
+	DUMP_REG(port, XHCI_PORT_CSC);
+	DUMP_REG(port, XHCI_PORT_PEC);
+	DUMP_REG(port, XHCI_PORT_WRC);
+	DUMP_REG(port, XHCI_PORT_OCC);
+	DUMP_REG(port, XHCI_PORT_PRC);
+	DUMP_REG(port, XHCI_PORT_PLC);
+	DUMP_REG(port, XHCI_PORT_CEC);
+	DUMP_REG(port, XHCI_PORT_CAS);
+	DUMP_REG(port, XHCI_PORT_WCE);
+	DUMP_REG(port, XHCI_PORT_WDE);
+	DUMP_REG(port, XHCI_PORT_WOE);
+	DUMP_REG(port, XHCI_PORT_DR);
+	DUMP_REG(port, XHCI_PORT_WPR);
+}
+
+void xhci_dump_state(const xhci_hc_t *hc)
+{
+	usb_log_debug2("Operational registers:");
+
+	DUMP_REG(hc->op_regs, XHCI_OP_RS);
+	DUMP_REG(hc->op_regs, XHCI_OP_HCRST);
+	DUMP_REG(hc->op_regs, XHCI_OP_INTE);
+	DUMP_REG(hc->op_regs, XHCI_OP_HSEE);
+	DUMP_REG(hc->op_regs, XHCI_OP_LHCRST);
+	DUMP_REG(hc->op_regs, XHCI_OP_CSS);
+	DUMP_REG(hc->op_regs, XHCI_OP_CRS);
+	DUMP_REG(hc->op_regs, XHCI_OP_EWE);
+	DUMP_REG(hc->op_regs, XHCI_OP_EU3S);
+	DUMP_REG(hc->op_regs, XHCI_OP_CME);
+	DUMP_REG(hc->op_regs, XHCI_OP_HCH);
+	DUMP_REG(hc->op_regs, XHCI_OP_HSE);
+	DUMP_REG(hc->op_regs, XHCI_OP_EINT);
+	DUMP_REG(hc->op_regs, XHCI_OP_PCD);
+	DUMP_REG(hc->op_regs, XHCI_OP_SSS);
+	DUMP_REG(hc->op_regs, XHCI_OP_RSS);
+	DUMP_REG(hc->op_regs, XHCI_OP_SRE);
+	DUMP_REG(hc->op_regs, XHCI_OP_CNR);
+	DUMP_REG(hc->op_regs, XHCI_OP_HCE);
+	DUMP_REG(hc->op_regs, XHCI_OP_PAGESIZE);
+	DUMP_REG(hc->op_regs, XHCI_OP_NOTIFICATION);
+	DUMP_REG(hc->op_regs, XHCI_OP_RCS);
+	DUMP_REG(hc->op_regs, XHCI_OP_CS);
+	DUMP_REG(hc->op_regs, XHCI_OP_CA);
+	DUMP_REG(hc->op_regs, XHCI_OP_CRR);
+	DUMP_REG(hc->op_regs, XHCI_OP_CRCR_LO);
+	DUMP_REG(hc->op_regs, XHCI_OP_CRCR_HI);
+	DUMP_REG(hc->op_regs, XHCI_OP_DCBAAP_LO);
+	DUMP_REG(hc->op_regs, XHCI_OP_DCBAAP_HI);
+	DUMP_REG(hc->rt_regs, XHCI_RT_MFINDEX);
+
+	usb_log_debug2("Interrupter 0 state:");
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_IP);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_IE);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_IMI);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_IMC);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_ERSTSZ);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_ERSTBA_LO);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_ERSTBA_HI);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_ERDP_LO);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_ERDP_HI);
+}
+
+void xhci_dump_ports(const xhci_hc_t *hc)
+{
+	const size_t num_ports = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PORTS);
+	for (size_t i = 0; i < num_ports; i++) {
+		usb_log_debug2("Port %zu state:", i);
+
+		xhci_dump_port(&hc->op_regs->portrs[i]);
+	}
+}
+
+static const char *trb_types [] = {
+	[0] = "<empty>",
+#define TRB(t) [XHCI_TRB_TYPE_##t] = #t
+	TRB(NORMAL),
+	TRB(SETUP_STAGE),
+	TRB(DATA_STAGE),
+	TRB(STATUS_STAGE),
+	TRB(ISOCH),
+	TRB(LINK),
+	TRB(EVENT_DATA),
+	TRB(NO_OP),
+	TRB(ENABLE_SLOT_CMD),
+	TRB(DISABLE_SLOT_CMD),
+	TRB(ADDRESS_DEVICE_CMD),
+	TRB(CONFIGURE_ENDPOINT_CMD),
+	TRB(EVALUATE_CONTEXT_CMD),
+	TRB(RESET_ENDPOINT_CMD),
+	TRB(STOP_ENDPOINT_CMD),
+	TRB(SET_TR_DEQUEUE_POINTER_CMD),
+	TRB(RESET_DEVICE_CMD),
+	TRB(FORCE_EVENT_CMD),
+	TRB(NEGOTIATE_BANDWIDTH_CMD),
+	TRB(SET_LATENCY_TOLERANCE_VALUE_CMD),
+	TRB(GET_PORT_BANDWIDTH_CMD),
+	TRB(FORCE_HEADER_CMD),
+	TRB(NO_OP_CMD),
+	TRB(TRANSFER_EVENT),
+	TRB(COMMAND_COMPLETION_EVENT),
+	TRB(PORT_STATUS_CHANGE_EVENT),
+	TRB(BANDWIDTH_REQUEST_EVENT),
+	TRB(DOORBELL_EVENT),
+	TRB(HOST_CONTROLLER_EVENT),
+	TRB(DEVICE_NOTIFICATION_EVENT),
+	TRB(MFINDEX_WRAP_EVENT),
+#undef TRB
+	[XHCI_TRB_TYPE_MAX] = NULL,
+};
+
+const char *xhci_trb_str_type(unsigned type)
+{
+	static char type_buf [20];
+
+	if (type < XHCI_TRB_TYPE_MAX && trb_types[type] != NULL)
+		return trb_types[type];
+
+	snprintf(type_buf, sizeof(type_buf), "<unknown (%u)>", type);
+	return type_buf;
+}
+
+void xhci_dump_trb(const xhci_trb_t *trb)
+{
+	usb_log_debug2("TRB(%p): type %s, cycle %u", trb, xhci_trb_str_type(TRB_TYPE(*trb)), TRB_CYCLE(*trb));
+}
+
+static const char *ec_ids [] = {
+	[0] = "<empty>",
+#define EC(t) [XHCI_EC_##t] = #t
+	EC(USB_LEGACY),
+	EC(SUPPORTED_PROTOCOL),
+	EC(EXTENDED_POWER_MANAGEMENT),
+	EC(IOV),
+	EC(MSI),
+	EC(LOCALMEM),
+	EC(DEBUG),
+	EC(MSIX),
+#undef EC
+	[XHCI_EC_MAX] = NULL
+};
+
+const char *xhci_ec_str_id(unsigned id)
+{
+	static char buf [20];
+
+	if (id < XHCI_EC_MAX && ec_ids[id] != NULL)
+		return ec_ids[id];
+
+	snprintf(buf, sizeof(buf), "<unknown (%u)>", id);
+	return buf;
+}
+
+static void xhci_dump_psi(const xhci_psi_t *psi)
+{
+	static const char speed_exp [] = " KMG";
+	static const char *psi_types [] = { "", " rsvd", " RX", " TX" };
+	
+	usb_log_debug("Speed %u%s: %5u %cb/s, %s",
+	    XHCI_REG_RD(psi, XHCI_PSI_PSIV),
+	    psi_types[XHCI_REG_RD(psi, XHCI_PSI_PLT)],
+	    XHCI_REG_RD(psi, XHCI_PSI_PSIM),
+	    speed_exp[XHCI_REG_RD(psi, XHCI_PSI_PSIE)],
+	    XHCI_REG_RD(psi, XHCI_PSI_PFD) ? "full-duplex" : "");
+}
+
+void xhci_dump_extcap(const xhci_extcap_t *ec)
+{
+	xhci_sp_name_t name;
+	unsigned ports_from, ports_to;
+
+	unsigned id = XHCI_REG_RD(ec, XHCI_EC_CAP_ID);
+	usb_log_debug("Extended capability %s", xhci_ec_str_id(id));
+
+	switch (id) {
+		case XHCI_EC_SUPPORTED_PROTOCOL:
+			name.packed = host2uint32_t_le(XHCI_REG_RD(ec, XHCI_EC_SP_NAME));
+			ports_from = XHCI_REG_RD(ec, XHCI_EC_SP_CP_OFF);
+			ports_to = ports_from + XHCI_REG_RD(ec, XHCI_EC_SP_CP_COUNT) - 1;
+			usb_log_debug("\tProtocol %.4s%u.%u, ports %u-%u", name.str,
+			    XHCI_REG_RD(ec, XHCI_EC_SP_MAJOR),
+			    XHCI_REG_RD(ec, XHCI_EC_SP_MINOR),
+			    ports_from, ports_to);
+
+			unsigned psic = XHCI_REG_RD(ec, XHCI_EC_SP_PSIC);
+			for (unsigned i = 0; i < psic; i++)
+				xhci_dump_psi(xhci_extcap_psi(ec, i));
+			break;
+	}
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/debug.h
===================================================================
--- uspace/drv/bus/usb/xhci/debug.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/debug.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty
+ * 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.
+ * 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
+ *
+ * Utility functions for debugging and logging purposes.
+ */
+
+#ifndef XHCI_DEBUG_H
+#define XHCI_DEBUG_H
+
+/**
+ * As the debug header is likely to be included in every file, avoid including
+ * all headers of xhci to support "include what you use".
+ */
+struct xhci_hc;
+struct xhci_cap_regs;
+struct xhci_port_regs;
+struct xhci_trb;
+struct xhci_extcap;
+
+void xhci_dump_cap_regs(const struct xhci_cap_regs *);
+void xhci_dump_port(const struct xhci_port_regs *);
+void xhci_dump_state(const struct xhci_hc *);
+void xhci_dump_ports(const struct xhci_hc *);
+
+const char *xhci_trb_str_type(unsigned);
+void xhci_dump_trb(const struct xhci_trb *trb);
+
+const char *xhci_ec_str_id(unsigned);
+void xhci_dump_extcap(const struct xhci_extcap *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/endpoint.c
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/endpoint.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2017 Petr Manek
+ * 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 The host controller endpoint management.
+ */
+
+#include <usb/host/utils/malloc32.h>
+#include <usb/host/endpoint.h>
+#include <usb/descriptor.h>
+
+#include <errno.h>
+#include <macros.h>
+
+#include "hc.h"
+#include "bus.h"
+#include "commands.h"
+#include "endpoint.h"
+
+int xhci_endpoint_init(xhci_endpoint_t *xhci_ep, xhci_bus_t *xhci_bus)
+{
+	assert(xhci_ep);
+	assert(xhci_bus);
+
+	bus_t *bus = &xhci_bus->base;
+	endpoint_t *ep = &xhci_ep->base;
+
+	endpoint_init(ep, bus);
+
+	return EOK;
+}
+
+void xhci_endpoint_fini(xhci_endpoint_t *xhci_ep)
+{
+	assert(xhci_ep);
+
+	// TODO: Something missed?
+}
+
+static int xhci_endpoint_type(xhci_endpoint_t *ep)
+{
+	const bool in = ep->base.direction == USB_DIRECTION_IN;
+
+	switch (ep->base.transfer_type) {
+	case USB_TRANSFER_CONTROL:
+		return EP_TYPE_CONTROL;
+
+	case USB_TRANSFER_ISOCHRONOUS:
+		return in ? EP_TYPE_ISOCH_IN
+			  : EP_TYPE_ISOCH_OUT;
+
+	case USB_TRANSFER_BULK:
+		return in ? EP_TYPE_BULK_IN
+			  : EP_TYPE_BULK_OUT;
+
+	case USB_TRANSFER_INTERRUPT:
+		return in ? EP_TYPE_INTERRUPT_IN
+			  : EP_TYPE_INTERRUPT_OUT;
+	}
+
+	return EP_TYPE_INVALID;
+}
+
+static bool endpoint_using_streams(xhci_endpoint_t *xhci_ep)
+{
+	return xhci_ep->primary_stream_ctx_array != NULL;
+}
+
+static size_t primary_stream_ctx_array_max_size(xhci_endpoint_t *xhci_ep)
+{
+	if (!xhci_ep->max_streams)
+		return 0;
+
+	/* Section 6.2.3, Table 61 */
+	return 1 << (xhci_ep->max_streams + 1);
+}
+
+// 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);
+// }
+
+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);
+		XHCI_STREAM_DEQ_PTR_SET(*ctx, ring->dequeue);
+
+		/* Set to linear stream array */
+		XHCI_STREAM_SCT_SET(*ctx, 1);
+	}
+}
+
+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);
+	XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
+
+	XHCI_EP_MAX_P_STREAMS_SET(*ctx, pstreams);
+	XHCI_EP_TR_DPTR_SET(*ctx, addr_to_phys(xhci_ep->primary_stream_ctx_array));
+	// TODO: set HID?
+	XHCI_EP_LSA_SET(*ctx, 1);
+}
+
+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
+		|| xhci_ep->base.speed != USB_SPEED_SUPER) {
+		usb_log_error("Streams are only supported by superspeed bulk endpoints.");
+		return EINVAL;
+	}
+
+	if (!primary_stream_ctx_array_max_size(xhci_ep)) {
+		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 > (unsigned) (1 << xhci_ep->max_streams)) {
+		usb_log_error("Endpoint " XHCI_EP_FMT " supports only %u streams.",
+			XHCI_EP_ARGS(*xhci_ep), (1 << 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));
+		xhci_ep->primary_stream_ctx_array = malloc32(count * sizeof(xhci_stream_ctx_t));
+		if (!xhci_ep->primary_stream_ctx_array) {
+			return ENOMEM;
+		}
+
+		xhci_ep->primary_stream_rings = calloc(count, sizeof(xhci_trb_ring_t));
+		if (!xhci_ep->primary_stream_rings) {
+			free32(xhci_ep->primary_stream_ctx_array);
+			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;
+}
+
+int xhci_endpoint_alloc_transfer_ds(xhci_endpoint_t *xhci_ep)
+{
+	/* Can't use XHCI_EP_FMT because the endpoint may not have device. */
+	usb_log_debug2("Allocating main transfer ring for endpoint " XHCI_EP_FMT, XHCI_EP_ARGS(*xhci_ep));
+
+	xhci_ep->primary_stream_ctx_array = NULL;
+
+	int err;
+	if ((err = xhci_trb_ring_init(&xhci_ep->ring))) {
+		return err;
+	}
+
+	return EOK;
+}
+
+void xhci_endpoint_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
+		}
+		free32(xhci_ep->primary_stream_ctx_array);
+	} 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);
+	}
+}
+
+/** See section 4.5.1 of the xHCI spec.
+ */
+uint8_t xhci_endpoint_dci(xhci_endpoint_t *ep)
+{
+	return (2 * ep->base.endpoint) +
+		(ep->base.transfer_type == USB_TRANSFER_CONTROL
+		 || ep->base.direction == USB_DIRECTION_IN);
+}
+
+/** Return an index to the endpoint array. The indices are assigned as follows:
+ * 0	EP0 BOTH
+ * 1	EP1 OUT
+ * 2	EP1 IN
+ *
+ * For control endpoints >0, the IN endpoint index is used.
+ *
+ * The index returned must be usually offset by a number of contexts preceding
+ * the endpoint contexts themselves.
+ */
+uint8_t xhci_endpoint_index(xhci_endpoint_t *ep)
+{
+	return xhci_endpoint_dci(ep) - 1;
+}
+
+static void setup_control_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
+{
+	XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
+	XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
+	XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
+	XHCI_EP_MULT_SET(*ctx, ep->mult);
+	XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
+	XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
+	XHCI_EP_DCS_SET(*ctx, 1);
+}
+
+static void setup_bulk_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
+{
+	XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
+	XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
+	XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
+	XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
+
+	XHCI_EP_MAX_P_STREAMS_SET(*ctx, 0);
+	XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
+	XHCI_EP_DCS_SET(*ctx, 1);
+}
+
+static void setup_isoch_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
+{
+	XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
+	XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
+	XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
+	XHCI_EP_MULT_SET(*ctx, ep->mult);
+	XHCI_EP_ERROR_COUNT_SET(*ctx, 0);
+	XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
+	XHCI_EP_DCS_SET(*ctx, 1);
+	// TODO: max ESIT payload
+}
+
+static void setup_interrupt_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
+{
+	XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
+	XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size & 0x07FF);
+	XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
+	XHCI_EP_MULT_SET(*ctx, 0);
+	XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
+	XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
+	XHCI_EP_DCS_SET(*ctx, 1);
+	// TODO: max ESIT payload
+}
+
+typedef void (*setup_ep_ctx_helper)(xhci_endpoint_t *, xhci_ep_ctx_t *);
+
+static const setup_ep_ctx_helper setup_ep_ctx_helpers[] = {
+	[USB_TRANSFER_CONTROL] = setup_control_ep_ctx,
+	[USB_TRANSFER_ISOCHRONOUS] = setup_isoch_ep_ctx,
+	[USB_TRANSFER_BULK] = setup_bulk_ep_ctx,
+	[USB_TRANSFER_INTERRUPT] = setup_interrupt_ep_ctx,
+};
+
+void xhci_setup_endpoint_context(xhci_endpoint_t *ep, xhci_ep_ctx_t *ep_ctx)
+{
+	assert(ep);
+	assert(ep_ctx);
+
+	usb_transfer_type_t tt = ep->base.transfer_type;
+	assert(tt < ARRAY_SIZE(setup_ep_ctx_helpers));
+
+	memset(ep_ctx, 0, sizeof(*ep_ctx));
+	setup_ep_ctx_helpers[tt](ep, ep_ctx);
+}
+
+int xhci_device_add_endpoint(xhci_device_t *dev, xhci_endpoint_t *ep)
+{
+	assert(dev);
+	assert(ep);
+
+	/* Offline devices don't create new endpoints other than EP0. */
+	if (!dev->online && ep->base.endpoint > 0) {
+		return EAGAIN;
+	}
+
+	const usb_endpoint_t ep_num = ep->base.endpoint;
+
+	if (dev->endpoints[ep_num])
+		return EEXIST;
+
+	/* Device reference */
+	endpoint_add_ref(&ep->base);
+	ep->base.device = &dev->base;
+	dev->endpoints[ep_num] = ep;
+
+	return EOK;
+}
+
+void xhci_device_remove_endpoint(xhci_endpoint_t *ep)
+{
+	assert(ep);
+	xhci_device_t *dev = xhci_device_get(ep->base.device);
+
+	assert(dev->endpoints[ep->base.endpoint]);
+	dev->endpoints[ep->base.endpoint] = NULL;
+	ep->base.device = NULL;
+
+	endpoint_del_ref(&ep->base);
+}
+
+xhci_endpoint_t *xhci_device_get_endpoint(xhci_device_t *dev, usb_endpoint_t ep)
+{
+	return dev->endpoints[ep];
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/endpoint.h
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/endpoint.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2017 Petr Manek
+ * 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 The host controller endpoint management.
+ */
+
+#ifndef XHCI_ENDPOINT_H
+#define XHCI_ENDPOINT_H
+
+#include <assert.h>
+
+#include <usb/debug.h>
+#include <usb/host/endpoint.h>
+#include <usb/host/hcd.h>
+#include <ddf/driver.h>
+
+#include "trb_ring.h"
+
+#include "transfers.h"
+
+typedef struct xhci_device xhci_device_t;
+typedef struct xhci_endpoint xhci_endpoint_t;
+typedef struct xhci_bus xhci_bus_t;
+
+enum {
+	EP_TYPE_INVALID = 0,
+	EP_TYPE_ISOCH_OUT = 1,
+	EP_TYPE_BULK_OUT = 2,
+	EP_TYPE_INTERRUPT_OUT = 3,
+	EP_TYPE_CONTROL = 4,
+	EP_TYPE_ISOCH_IN = 5,
+	EP_TYPE_BULK_IN = 6,
+	EP_TYPE_INTERRUPT_IN = 7
+};
+
+/** Connector structure linking endpoint context to the endpoint. */
+typedef struct xhci_endpoint {
+	endpoint_t base;	/**< Inheritance. Keep this first. */
+
+	/** Main transfer ring (unused if streams are enabled) */
+	xhci_trb_ring_t ring;
+
+	/** Primary stream context array (or NULL if endpoint doesn't use streams). */
+	xhci_stream_ctx_t *primary_stream_ctx_array;
+
+	/** 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;
+
+	/** 2-log of maximum number of primary streams (0-16). Not to be used directly. */
+	uint8_t max_streams;
+
+	/** Maximum number of consecutive USB transactions (0-15) that should be executed per scheduling opportunity */
+	uint8_t max_burst;
+
+	/** Maximum number of bursts within an interval that this endpoint supports */
+	uint8_t mult;
+} xhci_endpoint_t;
+
+#define XHCI_EP_FMT  "(%d:%d %s)"
+/* FIXME: "Device -1" messes up log messages, figure out a better way. */
+#define XHCI_EP_ARGS(ep)		\
+	((ep).base.device ? (ep).base.device->address : -1),	\
+	((ep).base.endpoint),		\
+	(usb_str_transfer_type((ep).base.transfer_type))
+
+typedef struct xhci_device {
+	device_t base;		/**< Inheritance. Keep this first. */
+
+	/** Slot ID assigned to the device by xHC. */
+	uint32_t slot_id;
+
+	/** Corresponding port on RH */
+	uint8_t rh_port;
+
+	/** USB Tier of the device */
+	uint8_t tier;
+
+	/** Route string */
+	uint32_t route_str;
+
+	/** Place to store virtual address for allocated context */
+	xhci_device_ctx_t *dev_ctx;
+
+	/** All endpoints of the device. Dropped ones are NULL */
+	xhci_endpoint_t *endpoints[XHCI_EP_COUNT];
+
+	/** Flag indicating whether the device is USB3 (it's USB2 otherwise). */
+	bool usb3;
+
+	/** True if the device can add new endpoints and schedule transfers. */
+	volatile bool online;
+} xhci_device_t;
+
+#define XHCI_DEV_FMT  "(%s, slot %d)"
+#define XHCI_DEV_ARGS(dev)		 ddf_fun_get_name((dev).base.fun), (dev).slot_id
+
+int xhci_endpoint_init(xhci_endpoint_t *, xhci_bus_t *);
+void xhci_endpoint_fini(xhci_endpoint_t *);
+int xhci_endpoint_alloc_transfer_ds(xhci_endpoint_t *);
+void xhci_endpoint_free_transfer_ds(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 *);
+uint8_t xhci_endpoint_index(xhci_endpoint_t *);
+
+void xhci_setup_endpoint_context(xhci_endpoint_t *, xhci_ep_ctx_t *);
+
+int xhci_device_add_endpoint(xhci_device_t *, xhci_endpoint_t *);
+void xhci_device_remove_endpoint(xhci_endpoint_t *);
+xhci_endpoint_t * xhci_device_get_endpoint(xhci_device_t *, usb_endpoint_t);
+
+static inline xhci_device_t * xhci_device_get(device_t *dev)
+{
+	assert(dev);
+	return (xhci_device_t *) dev;
+}
+
+static inline xhci_endpoint_t * xhci_endpoint_get(endpoint_t *ep)
+{
+	assert(ep);
+	return (xhci_endpoint_t *) ep;
+}
+
+static inline xhci_device_t * xhci_ep_to_dev(xhci_endpoint_t *ep)
+{
+	assert(ep);
+	return xhci_device_get(ep->base.device);
+}
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,778 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty
+ * 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 The host controller data bookkeeping.
+ */
+
+#include <errno.h>
+#include <str_error.h>
+#include <usb/debug.h>
+#include <usb/host/endpoint.h>
+#include <usb/host/utils/malloc32.h>
+#include "debug.h"
+#include "hc.h"
+#include "rh.h"
+#include "hw_struct/trb.h"
+#include "hw_struct/context.h"
+#include "endpoint.h"
+#include "commands.h"
+#include "transfers.h"
+#include "trb_ring.h"
+
+/**
+ * Default USB Speed ID mapping: Table 157
+ */
+#define PSI_TO_BPS(psie, psim) (((uint64_t) psim) << (10 * psie))
+#define PORT_SPEED(usb, mjr, psie, psim) { \
+	.name = "USB ", \
+	.major = mjr, \
+	.minor = 0, \
+	.usb_speed = USB_SPEED_##usb, \
+	.rx_bps = PSI_TO_BPS(psie, psim), \
+	.tx_bps = PSI_TO_BPS(psie, psim) \
+}
+static const xhci_port_speed_t ps_default_full  = PORT_SPEED(FULL, 2, 2, 12);
+static const xhci_port_speed_t ps_default_low   = PORT_SPEED(LOW, 2, 1, 1500);
+static const xhci_port_speed_t ps_default_high  = PORT_SPEED(HIGH, 2, 2, 480);
+static const xhci_port_speed_t ps_default_super = PORT_SPEED(SUPER, 3, 3, 5);
+
+/**
+ * Walk the list of extended capabilities.
+ */
+static int hc_parse_ec(xhci_hc_t *hc)
+{
+	unsigned psic, major, minor;
+	xhci_sp_name_t name;
+
+	xhci_port_speed_t *speeds = hc->speeds;
+
+	for (xhci_extcap_t *ec = hc->xecp; ec; ec = xhci_extcap_next(ec)) {
+		xhci_dump_extcap(ec);
+		switch (XHCI_REG_RD(ec, XHCI_EC_CAP_ID)) {
+		case XHCI_EC_USB_LEGACY:
+			assert(hc->legsup == NULL);
+			hc->legsup = (xhci_legsup_t *) ec;
+			break;
+		case XHCI_EC_SUPPORTED_PROTOCOL:
+			psic = XHCI_REG_RD(ec, XHCI_EC_SP_PSIC);
+			major = XHCI_REG_RD(ec, XHCI_EC_SP_MAJOR);
+			minor = XHCI_REG_RD(ec, XHCI_EC_SP_MINOR);
+			name.packed = host2uint32_t_le(XHCI_REG_RD(ec, XHCI_EC_SP_NAME));
+
+			if (name.packed != xhci_name_usb.packed) {
+				/**
+				 * The detection of such protocol would work,
+				 * but the rest of the implementation is made
+				 * for the USB protocol only.
+				 */
+				usb_log_error("Unknown protocol %.4s.", name.str);
+				return ENOTSUP;
+			}
+
+			// "Implied" speed
+			if (psic == 0) {
+				assert(minor == 0);
+
+				if (major == 2) {
+					speeds[1] = ps_default_full;
+					speeds[2] = ps_default_low;
+					speeds[3] = ps_default_high;
+
+					hc->speed_to_psiv[USB_SPEED_FULL] = 1;
+					hc->speed_to_psiv[USB_SPEED_LOW] = 2;
+					hc->speed_to_psiv[USB_SPEED_HIGH] = 3;
+				} else if (major == 3) {
+					speeds[4] = ps_default_super;
+					hc->speed_to_psiv[USB_SPEED_SUPER] = 4;
+				} else {
+					return EINVAL;
+				}
+
+				usb_log_debug2("Implied speed of USB %u.0 set up.", major);
+			} else {
+				for (unsigned i = 0; i < psic; i++) {
+					xhci_psi_t *psi = xhci_extcap_psi(ec, i);
+					unsigned sim = XHCI_REG_RD(psi, XHCI_PSI_PSIM);
+					unsigned psiv = XHCI_REG_RD(psi, XHCI_PSI_PSIV);
+					unsigned psie = XHCI_REG_RD(psi, XHCI_PSI_PSIE);
+					unsigned psim = XHCI_REG_RD(psi, XHCI_PSI_PSIM);
+
+					speeds[psiv].major = major;
+					speeds[psiv].minor = minor;
+					str_ncpy(speeds[psiv].name, 4, name.str, 4);
+					speeds[psiv].usb_speed = USB_SPEED_MAX;
+
+					uint64_t bps = PSI_TO_BPS(psie, psim);
+
+					if (sim == XHCI_PSI_PLT_SYMM || sim == XHCI_PSI_PLT_RX)
+						speeds[psiv].rx_bps = bps;
+					if (sim == XHCI_PSI_PLT_SYMM || sim == XHCI_PSI_PLT_TX) {
+						speeds[psiv].tx_bps = bps;
+						usb_log_debug2("Speed %u set up for bps %" PRIu64 " / %" PRIu64 ".", psiv, speeds[psiv].rx_bps, speeds[psiv].tx_bps);
+					}
+				}
+			}
+		}
+	}
+	return EOK;
+}
+
+int hc_init_mmio(xhci_hc_t *hc, const hw_res_list_parsed_t *hw_res)
+{
+	int err;
+
+	if (hw_res->mem_ranges.count != 1) {
+		usb_log_error("Unexpected MMIO area, bailing out.");
+		return EINVAL;
+	}
+
+	hc->mmio_range = hw_res->mem_ranges.ranges[0];
+
+	usb_log_debug("MMIO area at %p (size %zu), IRQ %d.\n",
+	    RNGABSPTR(hc->mmio_range), RNGSZ(hc->mmio_range), hw_res->irqs.irqs[0]);
+
+	if (RNGSZ(hc->mmio_range) < sizeof(xhci_cap_regs_t))
+		return EOVERFLOW;
+
+	void *base;
+	if ((err = pio_enable_range(&hc->mmio_range, &base)))
+		return err;
+
+	hc->reg_base = base;
+	hc->cap_regs = (xhci_cap_regs_t *)  base;
+	hc->op_regs  = (xhci_op_regs_t *)  (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH));
+	hc->rt_regs  = (xhci_rt_regs_t *)  (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF));
+	hc->db_arry  = (xhci_doorbell_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_DBOFF));
+
+	uintptr_t xec_offset = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_XECP) * sizeof(xhci_dword_t);
+	if (xec_offset > 0)
+		hc->xecp = (xhci_extcap_t *) (base + xec_offset);
+
+	usb_log_debug2("Initialized MMIO reg areas:");
+	usb_log_debug2("\tCapability regs: %p", hc->cap_regs);
+	usb_log_debug2("\tOperational regs: %p", hc->op_regs);
+	usb_log_debug2("\tRuntime regs: %p", hc->rt_regs);
+	usb_log_debug2("\tDoorbell array base: %p", hc->db_arry);
+
+	xhci_dump_cap_regs(hc->cap_regs);
+
+	hc->ac64 = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_AC64);
+	hc->max_slots = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_SLOTS);
+
+	if ((err = hc_parse_ec(hc))) {
+		pio_disable(hc->reg_base, RNGSZ(hc->mmio_range));
+		return err;
+	}
+
+	return EOK;
+}
+
+int hc_init_memory(xhci_hc_t *hc, ddf_dev_t *device)
+{
+	int err;
+
+	hc->dcbaa = malloc32((1 + hc->max_slots) * sizeof(uint64_t));
+	if (!hc->dcbaa)
+		return ENOMEM;
+
+	if ((err = xhci_trb_ring_init(&hc->command_ring)))
+		goto err_dcbaa;
+
+	if ((err = xhci_event_ring_init(&hc->event_ring)))
+		goto err_cmd_ring;
+
+	if ((err = xhci_scratchpad_alloc(hc)))
+		goto err_event_ring;
+
+	if ((err = xhci_init_commands(hc)))
+		goto err_scratch;
+
+	if ((err = xhci_rh_init(&hc->rh, hc, device)))
+		goto err_cmd;
+
+	if ((err = xhci_bus_init(&hc->bus, hc)))
+		goto err_rh;
+
+
+	return EOK;
+
+err_rh:
+	xhci_rh_fini(&hc->rh);
+err_cmd:
+	xhci_fini_commands(hc);
+err_scratch:
+	xhci_scratchpad_free(hc);
+err_event_ring:
+	xhci_event_ring_fini(&hc->event_ring);
+err_cmd_ring:
+	xhci_trb_ring_fini(&hc->command_ring);
+err_dcbaa:
+	free32(hc->dcbaa);
+	return err;
+}
+
+/*
+ * Pseudocode:
+ *	ip = read(intr[0].iman)
+ *	if (ip) {
+ *		status = read(usbsts)
+ *		assert status
+ *		assert ip
+ *		accept (passing status)
+ *	}
+ *	decline
+ */
+static const irq_cmd_t irq_commands[] = {
+	{
+		.cmd = CMD_PIO_READ_32,
+		.dstarg = 3,
+		.addr = NULL	/* intr[0].iman */
+	},
+	{
+		.cmd = CMD_AND,
+		.srcarg = 3,
+		.dstarg = 4,
+		.value = 0	/* host2xhci(32, 1) */
+	},
+	{
+		.cmd = CMD_PREDICATE,
+		.srcarg = 4,
+		.value = 5
+	},
+	{
+		.cmd = CMD_PIO_READ_32,
+		.dstarg = 1,
+		.addr = NULL	/* usbsts */
+	},
+	{
+		.cmd = CMD_AND,
+		.srcarg = 1,
+		.dstarg = 2,
+		.value = 0	/* host2xhci(32, XHCI_STATUS_ACK_MASK) */
+	},
+	{
+		.cmd = CMD_PIO_WRITE_A_32,
+		.srcarg = 2,
+		.addr = NULL	/* usbsts */
+	},
+	{
+		.cmd = CMD_PIO_WRITE_A_32,
+		.srcarg = 3,
+		.addr = NULL	/* intr[0].iman */
+	},
+	{
+		.cmd = CMD_ACCEPT
+	},
+	{
+		.cmd = CMD_DECLINE
+	}
+};
+
+
+/**
+ * Generates code to accept interrupts. The xHCI is designed primarily for
+ * MSI/MSI-X, but we use PCI Interrupt Pin. In this mode, all the Interrupters
+ * (except 0) are disabled.
+ */
+int hc_irq_code_gen(irq_code_t *code, xhci_hc_t *hc, const hw_res_list_parsed_t *hw_res)
+{
+	assert(code);
+	assert(hw_res);
+
+	if (hw_res->irqs.count != 1) {
+		usb_log_info("Unexpected HW resources to enable interrupts.");
+		return EINVAL;
+	}
+
+	code->ranges = malloc(sizeof(irq_pio_range_t));
+	if (code->ranges == NULL)
+		return ENOMEM;
+
+	code->cmds = malloc(sizeof(irq_commands));
+	if (code->cmds == NULL) {
+		free(code->ranges);
+		return ENOMEM;
+	}
+
+	code->rangecount = 1;
+	code->ranges[0] = (irq_pio_range_t) {
+	    .base = RNGABS(hc->mmio_range),
+	    .size = RNGSZ(hc->mmio_range),
+	};
+
+	code->cmdcount = ARRAY_SIZE(irq_commands);
+	memcpy(code->cmds, irq_commands, sizeof(irq_commands));
+
+	void *intr0_iman = RNGABSPTR(hc->mmio_range) + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF) + offsetof(xhci_rt_regs_t, ir[0]);
+	void *usbsts = RNGABSPTR(hc->mmio_range) + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH) + offsetof(xhci_op_regs_t, usbsts);
+	code->cmds[0].addr = intr0_iman;
+	code->cmds[1].value = host2xhci(32, 1);
+	code->cmds[3].addr = usbsts;
+	code->cmds[4].value = host2xhci(32, XHCI_STATUS_ACK_MASK);
+	code->cmds[5].addr = usbsts;
+	code->cmds[6].addr = intr0_iman;
+
+	return hw_res->irqs.irqs[0];
+}
+
+int hc_claim(xhci_hc_t *hc, ddf_dev_t *dev)
+{
+	/* No legacy support capability, the controller is solely for us */
+	if (!hc->legsup)
+		return EOK;
+
+	/* Section 4.22.1 */
+	/* TODO: Test this with USB3-aware BIOS */
+	usb_log_debug2("LEGSUP: bios: %x, os: %x", hc->legsup->sem_bios, hc->legsup->sem_os);
+	XHCI_REG_WR(hc->legsup, XHCI_LEGSUP_SEM_OS, 1);
+	for (int i = 0; i <= (XHCI_LEGSUP_BIOS_TIMEOUT_US / XHCI_LEGSUP_POLLING_DELAY_1MS); i++) {
+		usb_log_debug2("LEGSUP: elapsed: %i ms, bios: %x, os: %x", i,
+			XHCI_REG_RD(hc->legsup, XHCI_LEGSUP_SEM_BIOS),
+			XHCI_REG_RD(hc->legsup, XHCI_LEGSUP_SEM_OS));
+		if (XHCI_REG_RD(hc->legsup, XHCI_LEGSUP_SEM_BIOS) == 0) {
+			assert(XHCI_REG_RD(hc->legsup, XHCI_LEGSUP_SEM_OS) == 1);
+			return EOK;
+		}
+		async_usleep(XHCI_LEGSUP_POLLING_DELAY_1MS);
+	}
+	usb_log_error("BIOS did not release XHCI legacy hold!\n");
+
+	return ENOTSUP;
+}
+
+static int hc_reset(xhci_hc_t *hc)
+{
+	/* Stop the HC: set R/S to 0 */
+	XHCI_REG_CLR(hc->op_regs, XHCI_OP_RS, 1);
+
+	/* Wait 16 ms until the HC is halted */
+	async_usleep(16000);
+	assert(XHCI_REG_RD(hc->op_regs, XHCI_OP_HCH));
+
+	/* Reset */
+	XHCI_REG_SET(hc->op_regs, XHCI_OP_HCRST, 1);
+
+	/* Wait until the reset is complete */
+	while (XHCI_REG_RD(hc->op_regs, XHCI_OP_HCRST))
+		async_usleep(1000);
+
+	return EOK;
+}
+
+/**
+ * Initialize the HC: section 4.2
+ */
+int hc_start(xhci_hc_t *hc, bool irq)
+{
+	int err;
+
+	if ((err = hc_reset(hc)))
+		return err;
+
+	while (XHCI_REG_RD(hc->op_regs, XHCI_OP_CNR))
+		async_usleep(1000);
+
+	uint64_t dcbaaptr = addr_to_phys(hc->dcbaa);
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP_LO, LOWER32(dcbaaptr));
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP_HI, UPPER32(dcbaaptr));
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_MAX_SLOTS_EN, 0);
+
+	uint64_t crptr = xhci_trb_ring_get_dequeue_ptr(&hc->command_ring);
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_LO, LOWER32(crptr) >> 6);
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_HI, UPPER32(crptr));
+
+	uint64_t erstptr = addr_to_phys(hc->event_ring.erst);
+	uint64_t erdp = hc->event_ring.dequeue_ptr;
+	xhci_interrupter_regs_t *intr0 = &hc->rt_regs->ir[0];
+	XHCI_REG_WR(intr0, XHCI_INTR_ERSTSZ, hc->event_ring.segment_count);
+	XHCI_REG_WR(intr0, XHCI_INTR_ERDP_LO, LOWER32(erdp));
+	XHCI_REG_WR(intr0, XHCI_INTR_ERDP_HI, UPPER32(erdp));
+	XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_LO, LOWER32(erstptr));
+	XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_HI, UPPER32(erstptr));
+
+	if (irq) {
+		XHCI_REG_SET(intr0, XHCI_INTR_IE, 1);
+		XHCI_REG_SET(hc->op_regs, XHCI_OP_INTE, 1);
+	}
+
+	XHCI_REG_SET(hc->op_regs, XHCI_OP_RS, 1);
+
+	/* The reset changed status of all ports, and SW originated reason does
+	 * not cause an interrupt.
+	 */
+	xhci_rh_handle_port_change(&hc->rh);
+
+	return EOK;
+}
+
+/**
+ * Used only when polling. Shall supplement the irq_commands.
+ */
+int hc_status(xhci_hc_t *hc, uint32_t *status)
+{
+	int ip = XHCI_REG_RD(hc->rt_regs->ir, XHCI_INTR_IP);
+	if (ip) {
+		*status = XHCI_REG_RD(hc->op_regs, XHCI_OP_STATUS);
+		XHCI_REG_WR(hc->op_regs, XHCI_OP_STATUS, *status & XHCI_STATUS_ACK_MASK);
+		XHCI_REG_WR(hc->rt_regs->ir, XHCI_INTR_IP, 1);
+
+		/* interrupt handler expects status from irq_commands, which is
+		 * in xhci order. */
+		*status = host2xhci(32, *status);
+	}
+
+	usb_log_debug2("HC(%p): Polled status: %x", hc, *status);
+	return EOK;
+}
+
+int hc_schedule(xhci_hc_t *hc, usb_transfer_batch_t *batch)
+{
+	assert(batch);
+	assert(batch->ep);
+
+	if (!batch->target.address) {
+		usb_log_error("Attempted to schedule transfer to address 0.");
+		return EINVAL;
+	}
+
+	return xhci_transfer_schedule(hc, batch);
+}
+
+typedef int (*event_handler) (xhci_hc_t *, xhci_trb_t *trb);
+
+static event_handler event_handlers [] = {
+	[XHCI_TRB_TYPE_COMMAND_COMPLETION_EVENT] = &xhci_handle_command_completion,
+	[XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVENT] = &xhci_rh_handle_port_status_change_event,
+	[XHCI_TRB_TYPE_TRANSFER_EVENT] = &xhci_handle_transfer_event,
+};
+
+static int hc_handle_event(xhci_hc_t *hc, xhci_trb_t *trb, xhci_interrupter_regs_t *intr)
+{
+	unsigned type = TRB_TYPE(*trb);
+	if (type >= ARRAY_SIZE(event_handlers) || !event_handlers[type])
+		return ENOTSUP;
+
+	return event_handlers[type](hc, trb);
+}
+
+static void hc_run_event_ring(xhci_hc_t *hc, xhci_event_ring_t *event_ring, xhci_interrupter_regs_t *intr)
+{
+	int err;
+	ssize_t size = 16;
+	xhci_trb_t *queue = malloc(sizeof(xhci_trb_t) * size);
+	if (!queue) {
+		usb_log_error("Not enough memory to run the event ring.");
+		return;
+	}
+
+	xhci_trb_t *head = queue;
+
+	while ((err = xhci_event_ring_dequeue(event_ring, head)) != ENOENT) {
+		if (err != EOK) {
+			usb_log_warning("Error while accessing event ring: %s", str_error(err));
+			break;
+		}
+
+		usb_log_debug2("Dequeued trb from event ring: %s", xhci_trb_str_type(TRB_TYPE(*head)));
+		head++;
+
+		/* Expand the array if needed. */
+		if (head - queue >= size) {
+			size *= 2;
+			xhci_trb_t *new_queue = realloc(queue, size);
+			if (new_queue == NULL)
+				break; /* Will process only those TRBs we have memory for. */
+
+			head = new_queue + (head - queue);
+		}
+	}
+
+	/* Update the ERDP to make room in the ring. */
+	usb_log_debug2("Copying from ring finished, updating ERDP.");
+	uint64_t erdp = hc->event_ring.dequeue_ptr;
+	XHCI_REG_WR(intr, XHCI_INTR_ERDP_LO, LOWER32(erdp));
+	XHCI_REG_WR(intr, XHCI_INTR_ERDP_HI, UPPER32(erdp));
+	XHCI_REG_SET(intr, XHCI_INTR_ERDP_EHB, 1);
+
+	/* Handle all of the collected events if possible. */
+	if (head == queue)
+		usb_log_warning("No events to be handled!");
+
+	for (xhci_trb_t *tail = queue; tail != head; tail++) {
+		if ((err = hc_handle_event(hc, tail, intr)) != EOK) {
+			usb_log_error("Failed to handle event: %s", str_error(err));
+		}
+	}
+
+	free(queue);
+	usb_log_debug2("Event ring run finished.");
+}
+
+void hc_interrupt(xhci_hc_t *hc, uint32_t status)
+{
+	status = xhci2host(32, status);
+
+	if (status & XHCI_REG_MASK(XHCI_OP_PCD)) {
+		usb_log_debug2("Root hub interrupt.");
+		xhci_rh_handle_port_change(&hc->rh);
+		status &= ~XHCI_REG_MASK(XHCI_OP_PCD);
+	}
+
+	if (status & XHCI_REG_MASK(XHCI_OP_HSE)) {
+		usb_log_error("Host controller error occured. Bad things gonna happen...");
+		status &= ~XHCI_REG_MASK(XHCI_OP_HSE);
+	}
+
+	if (status & XHCI_REG_MASK(XHCI_OP_EINT)) {
+		usb_log_debug2("Event interrupt, running the event ring.");
+		hc_run_event_ring(hc, &hc->event_ring, &hc->rt_regs->ir[0]);
+		status &= ~XHCI_REG_MASK(XHCI_OP_EINT);
+	}
+
+	if (status & XHCI_REG_MASK(XHCI_OP_SRE)) {
+		usb_log_error("Save/Restore error occured. WTF, S/R mechanism not implemented!");
+		status &= ~XHCI_REG_MASK(XHCI_OP_SRE);
+	}
+
+	if (status) {
+		usb_log_error("Non-zero status after interrupt handling (%08x) - missing something?", status);
+	}
+}
+
+static void hc_dcbaa_fini(xhci_hc_t *hc)
+{
+	xhci_scratchpad_free(hc);
+	free32(hc->dcbaa);
+}
+
+void hc_fini(xhci_hc_t *hc)
+{
+	xhci_bus_fini(&hc->bus);
+	xhci_trb_ring_fini(&hc->command_ring);
+	xhci_event_ring_fini(&hc->event_ring);
+	hc_dcbaa_fini(hc);
+	xhci_fini_commands(hc);
+	xhci_rh_fini(&hc->rh);
+	pio_disable(hc->reg_base, RNGSZ(hc->mmio_range));
+	usb_log_info("HC(%p): Finalized.", hc);
+}
+
+int hc_ring_doorbell(xhci_hc_t *hc, unsigned doorbell, unsigned target)
+{
+	assert(hc);
+	uint32_t v = host2xhci(32, target & BIT_RRANGE(uint32_t, 7));
+	pio_write_32(&hc->db_arry[doorbell], v);
+	usb_log_debug2("Ringing doorbell %d (target: %d)", doorbell, target);
+	return EOK;
+}
+
+int hc_enable_slot(xhci_hc_t *hc, uint32_t *slot_id)
+{
+	assert(hc);
+
+	int err;
+	xhci_cmd_t cmd;
+	xhci_cmd_init(&cmd, XHCI_CMD_ENABLE_SLOT);
+
+	if ((err = xhci_cmd_sync(hc, &cmd))) {
+		goto end;
+	}
+
+	if (slot_id) {
+		*slot_id = cmd.slot_id;
+	}
+
+end:
+	xhci_cmd_fini(&cmd);
+	return err;
+}
+
+int hc_disable_slot(xhci_hc_t *hc, xhci_device_t *dev)
+{
+	int err;
+	assert(hc);
+
+	if ((err = xhci_cmd_sync_inline(hc, DISABLE_SLOT, .slot_id = dev->slot_id))) {
+		return err;
+	}
+
+	/* Free the device context. */
+	hc->dcbaa[dev->slot_id] = 0;
+	if (dev->dev_ctx) {
+		free32(dev->dev_ctx);
+		dev->dev_ctx = NULL;
+	}
+
+	/* Mark the slot as invalid. */
+	dev->slot_id = 0;
+
+	return EOK;
+}
+
+static int create_valid_input_ctx(xhci_input_ctx_t **out_ictx)
+{
+	xhci_input_ctx_t *ictx = malloc32(sizeof(xhci_input_ctx_t));
+	if (!ictx) {
+		return ENOMEM;
+	}
+
+	memset(ictx, 0, sizeof(xhci_input_ctx_t));
+
+	// Quoting sec. 4.6.6: A1, D0, D1 are down, A0 is up.
+	XHCI_INPUT_CTRL_CTX_ADD_CLEAR(ictx->ctrl_ctx, 1);
+	XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 0);
+	XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 1);
+	XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 0);
+
+	if (out_ictx) {
+		*out_ictx = ictx;
+	}
+
+	return EOK;
+}
+
+// TODO: This currently assumes the device is attached to rh directly
+//	-> calculate route string
+int hc_address_device(xhci_hc_t *hc, xhci_device_t *dev, xhci_endpoint_t *ep0)
+{
+	int err = ENOMEM;
+
+	/* Although we have the precise PSIV value on devices of tier 1,
+	 * we have to rely on reverse mapping on others. */
+	if (!hc->speed_to_psiv[dev->base.speed]) {
+		usb_log_error("Device reported an USB speed that cannot be mapped to HC port speed.");
+		return EINVAL;
+	}
+
+	/* Setup and register device context */
+	dev->dev_ctx = malloc32(sizeof(xhci_device_ctx_t));
+	if (!dev->dev_ctx)
+		goto err;
+	memset(dev->dev_ctx, 0, sizeof(xhci_device_ctx_t));
+
+	hc->dcbaa[dev->slot_id] = addr_to_phys(dev->dev_ctx);
+
+	/* Issue configure endpoint command (sec 4.3.5). */
+	xhci_input_ctx_t *ictx;
+	if ((err = create_valid_input_ctx(&ictx))) {
+		goto err_dev_ctx;
+	}
+
+	/* Initialize slot_ctx according to section 4.3.3 point 3. */
+	XHCI_SLOT_ROOT_HUB_PORT_SET(ictx->slot_ctx, dev->rh_port);
+	XHCI_SLOT_CTX_ENTRIES_SET(ictx->slot_ctx, 1);
+	XHCI_SLOT_ROUTE_STRING_SET(ictx->slot_ctx, dev->route_str);
+	XHCI_SLOT_SPEED_SET(ictx->slot_ctx, hc->speed_to_psiv[dev->base.speed]);
+
+	/* In a very specific case, we have to set also these. But before that,
+	 * we need to refactor how TT is handled in libusbhost. */
+	XHCI_SLOT_TT_HUB_SLOT_ID_SET(ictx->slot_ctx, 0);
+	XHCI_SLOT_TT_HUB_PORT_SET(ictx->slot_ctx, 0);
+	XHCI_SLOT_MTT_SET(ictx->slot_ctx, 0);
+
+	/* Copy endpoint 0 context and set A1 flag. */
+	XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 1);
+	xhci_setup_endpoint_context(ep0, &ictx->endpoint_ctx[0]);
+
+	/* Issue Address Device command. */
+	if ((err = xhci_cmd_sync_inline(hc, ADDRESS_DEVICE, .slot_id = dev->slot_id, .input_ctx = ictx))) {
+		goto err_dev_ctx;
+	}
+
+	dev->base.address = XHCI_SLOT_DEVICE_ADDRESS(dev->dev_ctx->slot_ctx);
+	usb_log_debug2("Obtained USB address: %d.\n", dev->base.address);
+
+	/* From now on, the device is officially online, yay! */
+	fibril_mutex_lock(&dev->base.guard);
+	dev->online = true;
+	fibril_mutex_unlock(&dev->base.guard);
+
+	return EOK;
+
+err_dev_ctx:
+	free32(dev->dev_ctx);
+	hc->dcbaa[dev->slot_id] = 0;
+err:
+	return err;
+}
+
+int hc_configure_device(xhci_hc_t *hc, uint32_t slot_id)
+{
+	/* Issue configure endpoint command (sec 4.3.5). */
+	xhci_input_ctx_t *ictx;
+	const int err = create_valid_input_ctx(&ictx);
+	if (err)
+		return err;
+
+	// TODO: Set slot context and other flags. (probably forgot a lot of 'em)
+
+	return xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT, .slot_id = slot_id, .input_ctx = ictx);
+}
+
+int hc_deconfigure_device(xhci_hc_t *hc, uint32_t slot_id)
+{
+	/* Issue configure endpoint command (sec 4.3.5) with the DC flag. */
+	return xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT, .slot_id = slot_id, .deconfigure = true);
+}
+
+int hc_add_endpoint(xhci_hc_t *hc, uint32_t slot_id, uint8_t ep_idx, xhci_ep_ctx_t *ep_ctx)
+{
+	/* Issue configure endpoint command (sec 4.3.5). */
+	xhci_input_ctx_t *ictx;
+	const int err = create_valid_input_ctx(&ictx);
+	if (err)
+		return err;
+
+	XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, ep_idx + 1); /* Preceded by slot ctx */
+	memcpy(&ictx->endpoint_ctx[ep_idx], ep_ctx, sizeof(xhci_ep_ctx_t));
+	// TODO: Set slot context and other flags. (probably forgot a lot of 'em)
+
+	return xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT, .slot_id = slot_id, .input_ctx = ictx);
+}
+
+int hc_drop_endpoint(xhci_hc_t *hc, uint32_t slot_id, uint8_t ep_idx)
+{
+	/* Issue configure endpoint command (sec 4.3.5). */
+	xhci_input_ctx_t *ictx;
+	const int err = create_valid_input_ctx(&ictx);
+	if (err)
+		return err;
+
+	XHCI_INPUT_CTRL_CTX_DROP_SET(ictx->ctrl_ctx, ep_idx + 1); /* Preceded by slot ctx */
+	// TODO: Set slot context and other flags. (probably forgot a lot of 'em)
+
+	return xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT, .slot_id = slot_id, .input_ctx = ictx);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/hc.h
===================================================================
--- uspace/drv/bus/usb/xhci/hc.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/hc.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty
+ * 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 The host controller data bookkeeping.
+ */
+
+#ifndef XHCI_HC_H
+#define XHCI_HC_H
+
+#include <fibril_synch.h>
+#include <usb/host/usb_transfer_batch.h>
+#include "hw_struct/regs.h"
+#include "hw_struct/context.h"
+#include "scratchpad.h"
+#include "trb_ring.h"
+
+#include "rh.h"
+#include "bus.h"
+
+typedef struct xhci_hc {
+	/* MMIO range */
+	addr_range_t mmio_range;
+
+	/* Mapped register sets */
+	void *reg_base;
+	xhci_cap_regs_t *cap_regs;
+	xhci_op_regs_t *op_regs;
+	xhci_rt_regs_t *rt_regs;
+	xhci_doorbell_t *db_arry;
+	xhci_extcap_t *xecp;		/**< First extended capability */
+	xhci_legsup_t *legsup;		/**< Legacy support capability */
+
+	/* Structures in allocated memory */
+	xhci_trb_ring_t command_ring;
+	xhci_event_ring_t event_ring;
+	uint64_t *dcbaa;
+	xhci_scratchpad_t *scratchpad;
+
+	/* Root hub emulation */
+	xhci_rh_t rh;
+
+	/* Bus bookkeeping */
+	xhci_bus_t bus;
+
+	/* Cached capabilities */
+	unsigned max_slots;
+	bool ac64;
+
+	/** Port speed mapping */
+	xhci_port_speed_t speeds [16];
+	uint8_t speed_to_psiv [USB_SPEED_MAX];
+
+	/* Command list */
+	list_t commands;
+	fibril_mutex_t commands_mtx;
+
+	/* TODO: Hack. Figure out a better way. */
+	hcd_t *hcd;
+} xhci_hc_t;
+
+typedef struct xhci_endpoint xhci_endpoint_t;
+typedef struct xhci_device xhci_device_t;
+
+int hc_init_mmio(xhci_hc_t *, const hw_res_list_parsed_t *);
+int hc_init_memory(xhci_hc_t *, ddf_dev_t *);
+int hc_claim(xhci_hc_t *, ddf_dev_t *);
+int hc_irq_code_gen(irq_code_t *, xhci_hc_t *, const hw_res_list_parsed_t *);
+int hc_start(xhci_hc_t *, bool);
+int hc_schedule(xhci_hc_t *hc, usb_transfer_batch_t *batch);
+int hc_status(xhci_hc_t *, uint32_t *);
+void hc_interrupt(xhci_hc_t *, uint32_t);
+void hc_fini(xhci_hc_t *);
+int hc_ring_doorbell(xhci_hc_t *, unsigned, unsigned);
+int hc_enable_slot(xhci_hc_t *, uint32_t *);
+int hc_disable_slot(xhci_hc_t *, xhci_device_t *);
+int hc_address_device(xhci_hc_t *, xhci_device_t *, xhci_endpoint_t *);
+int hc_configure_device(xhci_hc_t *, uint32_t);
+int hc_deconfigure_device(xhci_hc_t *, uint32_t);
+int hc_add_endpoint(xhci_hc_t *, uint32_t, uint8_t, xhci_ep_ctx_t *);
+int hc_drop_endpoint(xhci_hc_t *, uint32_t, uint8_t);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/hw_struct/README
===================================================================
--- uspace/drv/bus/usb/xhci/hw_struct/README	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/hw_struct/README	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,3 @@
+This folder contains hardware communication structures and constants, as defined
+in chapter 6 of the xHCI specification. It shall not be modified, as it reflects
+the order hardware expects it.
Index: uspace/drv/bus/usb/xhci/hw_struct/common.h
===================================================================
--- uspace/drv/bus/usb/xhci/hw_struct/common.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/hw_struct/common.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty
+ * 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 Common macros for HW structures.
+ *
+ * There are a lot of data structures that are defined on bit-basis.
+ * Therefore, we provide macros to define getters and setters.
+ */
+
+#ifndef XHCI_COMMON_H
+#define XHCI_COMMON_H
+
+#include <assert.h>
+#include <bitops.h>
+#include <ddi.h>
+#include <byteorder.h>
+
+#define host2xhci(size, val) host2uint##size##_t_le((val))
+#define xhci2host(size, val) uint##size##_t_le2host((val))
+
+/**
+ * 4 bytes, little-endian.
+ */
+typedef ioport32_t xhci_dword_t __attribute__((aligned(4)));
+
+/**
+ * 8 bytes, little-endian.
+ */
+typedef volatile uint64_t xhci_qword_t __attribute__((aligned(8)));
+
+#define XHCI_DWORD_EXTRACT(field, hi, lo) (BIT_RANGE_EXTRACT(uint32_t, hi, lo, xhci2host(32, field)))
+#define XHCI_QWORD_EXTRACT(field, hi, lo) (BIT_RANGE_EXTRACT(uint64_t, hi, lo, xhci2host(64, field)))
+
+/**
+ * Common base for setters on xhci_dword_t storage.
+ *
+ * Not thread-safe, proper synchronization over this dword must be assured.
+ */
+static inline void xhci_dword_set_bits(xhci_dword_t *storage, uint32_t value, unsigned hi, unsigned lo)
+{
+	const uint32_t mask = host2xhci(32, BIT_RANGE(uint32_t, hi, lo));
+	const uint32_t set = host2xhci(32, value << lo);
+	*storage = (*storage & ~mask) | set;
+}
+
+/**
+ * Setter for whole qword.
+ */
+static inline void xhci_qword_set(xhci_qword_t *storage, uint64_t value)
+{
+	*storage = host2xhci(64, value);
+}
+
+static inline void xhci_qword_set_bits(xhci_qword_t *storage, uint64_t value, unsigned hi, unsigned lo)
+{
+	const uint64_t mask = host2xhci(64, BIT_RANGE(uint64_t, hi, lo));
+	const uint64_t set = host2xhci(64, value << lo);
+	*storage = (*storage & ~mask) | set;
+}
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/hw_struct/context.h
===================================================================
--- uspace/drv/bus/usb/xhci/hw_struct/context.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/hw_struct/context.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
+ * 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
+ * Context data structures of the xHC.
+ *
+ * Most of them are to be initialized to zero and passed ownership to the HC,
+ * so they are mostly read-only.
+ *
+ * Feel free to write a setter when in need.
+ */
+
+#ifndef XHCI_CONTEXT_H
+#define XHCI_CONTEXT_H
+
+#include <stdint.h>
+#include "common.h"
+
+/**
+ * Endpoint context: section 6.2.3
+ */
+typedef struct xhci_endpoint_ctx {
+	xhci_dword_t data[2];
+	xhci_qword_t data2;
+	xhci_dword_t data3;
+	xhci_dword_t reserved[3];
+
+#define XHCI_EP_COUNT 31
+
+#define XHCI_EP_TYPE_ISOCH_OUT		1
+#define XHCI_EP_TYPE_BULK_OUT		2
+#define XHCI_EP_TYPE_INTERRUPT_OUT	3
+#define XHCI_EP_TYPE_CONTROL		4
+#define XHCI_EP_TYPE_ISOCH_IN		5
+#define XHCI_EP_TYPE_BULK_IN		6
+#define XHCI_EP_TYPE_INTERRUPT_IN	7
+
+#define XHCI_EP_TYPE_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[1], val, 5, 3)
+#define XHCI_EP_MAX_PACKET_SIZE_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[1], val, 31, 16)
+#define XHCI_EP_MAX_BURST_SIZE_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[1], val, 15, 8)
+#define XHCI_EP_TR_DPTR_SET(ctx, val) \
+	xhci_qword_set_bits(&(ctx).data2, (val >> 4), 63, 4)
+#define XHCI_EP_DCS_SET(ctx, val) \
+	xhci_qword_set_bits(&(ctx).data2, val, 0, 0)
+#define XHCI_EP_MAX_ESIT_PAYLOAD_LO_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data3, val, 31, 16)
+#define XHCI_EP_INTERVAL_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[0], val, 23, 16)
+#define XHCI_EP_MAX_P_STREAMS_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[0], val, 14, 10)
+#define XHCI_EP_LSA_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[0], val, 15, 15)
+#define XHCI_EP_MULT_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[0], val, 9, 8)
+#define XHCI_EP_ERROR_COUNT_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[1], val, 2, 1)
+
+#define XHCI_EP_STATE(ctx)              XHCI_DWORD_EXTRACT((ctx).data[0],  2,  0)
+#define XHCI_EP_MULT(ctx)               XHCI_DWORD_EXTRACT((ctx).data[0],  9,  8)
+#define XHCI_EP_MAX_P_STREAMS(ctx)      XHCI_DWORD_EXTRACT((ctx).data[0], 14, 10)
+#define XHCI_EP_LSA(ctx)                XHCI_DWORD_EXTRACT((ctx).data[0], 15, 15)
+#define XHCI_EP_INTERVAL(ctx)           XHCI_DWORD_EXTRACT((ctx).data[0], 23, 16)
+
+#define XHCI_EP_ERROR_COUNT(ctx)        XHCI_DWORD_EXTRACT((ctx).data[1],  2,  1)
+#define XHCI_EP_TYPE(ctx)               XHCI_DWORD_EXTRACT((ctx).data[1],  5,  3)
+#define XHCI_EP_HID(ctx)                XHCI_DWORD_EXTRACT((ctx).data[1],  7,  7)
+#define XHCI_EP_MAX_BURST_SIZE(ctx)     XHCI_DWORD_EXTRACT((ctx).data[1], 15,  8)
+#define XHCI_EP_MAX_PACKET_SIZE(ctx)    XHCI_DWORD_EXTRACT((ctx).data[1], 31, 16)
+
+#define XHCI_EP_DCS(ctx)                XHCI_QWORD_EXTRACT((ctx).data2,  0,  0)
+#define XHCI_EP_TR_DPTR(ctx)            XHCI_QWORD_EXTRACT((ctx).data2, 63,  4)
+
+#define XHCI_EP_MAX_ESIT_PAYLOAD_LO(ctx) XHCI_DWORD_EXTRACT((ctx).data3, 31,  16)
+
+} __attribute__((packed)) xhci_ep_ctx_t;
+
+/**
+ * Slot context: section 6.2.2
+ */
+typedef struct xhci_slot_ctx {
+	xhci_dword_t data [4];
+	xhci_dword_t reserved [4];
+
+#define XHCI_SLOT_ROUTE_STRING_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[0], (val & 0xFFFFF), 19, 0)
+#define XHCI_SLOT_SPEED_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[0], (val & 0xF), 23, 20)
+#define XHCI_SLOT_MTT_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[0], !!val, 25, 25)
+#define XHCI_SLOT_CTX_ENTRIES_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[0], val, 31, 27)
+
+#define XHCI_SLOT_ROOT_HUB_PORT_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[1], val, 23, 16)
+
+#define XHCI_SLOT_TT_HUB_SLOT_ID_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[2], (val & 0xFF), 7, 0)
+#define XHCI_SLOT_TT_HUB_PORT_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[2], (val & 0xFF), 15, 8)
+
+#define XHCI_SLOT_ROUTE_STRING(ctx)     XHCI_DWORD_EXTRACT((ctx).data[0], 19,  0)
+#define XHCI_SLOT_SPEED(ctx)            XHCI_DWORD_EXTRACT((ctx).data[0], 23, 20)
+#define XHCI_SLOT_MTT(ctx)              XHCI_DWORD_EXTRACT((ctx).data[0], 25, 25)
+#define XHCI_SLOT_CTX_ENTRIES(ctx)      XHCI_DWORD_EXTRACT((ctx).data[0], 31, 27)
+
+#define XHCI_SLOT_MAX_EXIT_LATENCY(ctx) XHCI_DWORD_EXTRACT((ctx).data[1], 15,  0)
+#define XHCI_SLOT_ROOT_HUB_PORT(ctx)    XHCI_DWORD_EXTRACT((ctx).data[1], 23, 16)
+#define XHCI_SLOT_NUM_OF_PORTS(ctx)     XHCI_DWORD_EXTRACT((ctx).data[1], 31, 24)
+
+#define XHCI_SLOT_TT_HUB_SLOT_ID(ctx)   XHCI_DWORD_EXTRACT((ctx).data[2],  7,  0)
+#define XHCI_SLOT_TT_PORT_NUM(ctx)      XHCI_DWORD_EXTRACT((ctx).data[2], 15,  8)
+#define XHCI_SLOT_TT_THINK_TIME(ctx)    XHCI_DWORD_EXTRACT((ctx).data[2], 17, 16)
+#define XHCI_SLOT_INTERRUPTER(ctx)      XHCI_DWORD_EXTRACT((ctx).data[2], 31, 22)
+
+#define XHCI_SLOT_DEVICE_ADDRESS(ctx)   XHCI_DWORD_EXTRACT((ctx).data[3],  7,  0)
+#define XHCI_SLOT_SLOT_STATE(ctx)       XHCI_DWORD_EXTRACT((ctx).data[3], 31, 27)
+
+} __attribute__((packed)) xhci_slot_ctx_t;
+
+/**
+ * Device context: section 6.2.1
+ */
+typedef struct xhci_device_ctx {
+	xhci_slot_ctx_t slot_ctx;
+	xhci_ep_ctx_t endpoint_ctx[XHCI_EP_COUNT];
+} __attribute__((packed)) xhci_device_ctx_t;
+
+/**
+ * Stream context: section 6.2.4 {
+ */
+typedef struct xhci_stream_ctx {
+	uint64_t data [2];
+#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_EDTLA(ctx)     XHCI_QWORD_EXTRACT((ctx).data[1], 24, 0)
+
+#define XHCI_STREAM_SCT_SET(ctx, val) \
+	xhci_qword_set_bits(&(ctx).data[0], val, 3, 1)
+#define XHCI_STREAM_DEQ_PTR_SET(ctx, val) \
+	xhci_qword_set_bits(&(ctx).data[0], (val >> 4), 63, 4)
+} __attribute__((packed)) xhci_stream_ctx_t;
+
+/**
+ * Input control context: section 6.2.5.1
+ * Note: According to section 6.2.5.1 figure 78,
+ *       the context size register value in hccparams1
+ *       dictates whether input control context shall have
+ *       32 or 64 bytes, but in any case only dwords 0, 1 and 7
+ *       are used, the rest are reserved.
+ */
+typedef struct xhci_input_ctrl_ctx {
+	uint32_t data [8];
+#define XHCI_INPUT_CTRL_CTX_DROP(ctx, idx) \
+	XHCI_DWORD_EXTRACT((ctx).data[0], (idx), (idx))
+
+#define XHCI_INPUT_CTRL_CTX_DROP_SET(ctx, idx) (ctx).data[0] |= (1 << (idx))
+#define XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ctx, idx) (ctx).data[0] &= ~(1 << (idx))
+
+#define XHCI_INPUT_CTRL_CTX_ADD(ctx, idx) \
+	XHCI_DWORD_EXTRACT((ctx).data[1], (idx), (idx))
+
+#define XHCI_INPUT_CTRL_CTX_ADD_SET(ctx, idx) (ctx).data[1] |= (1 << (idx))
+#define XHCI_INPUT_CTRL_CTX_ADD_CLEAR(ctx, idx) (ctx).data[1] &= ~(1 << (idx))
+
+#define XHCI_INPUT_CTRL_CTX_CONFIG_VALUE(ctx)   XHCI_DWORD_EXTRACT((ctx).data[7],  7,  0)
+#define XHCI_INPUT_CTRL_CTX_IFACE_NUMBER(ctx)   XHCI_DWORD_EXTRACT((ctx).data[7], 15,  8)
+#define XHCI_INPUT_CTRL_CTX_ALTER_SETTING(ctx)  XHCI_DWORD_EXTRACT((ctx).data[7], 23, 16)
+} __attribute__((packed)) xhci_input_ctrl_ctx_t;
+
+/**
+ * Input context: section 6.2.5
+ */
+typedef struct xhci_input_ctx {
+	xhci_input_ctrl_ctx_t ctrl_ctx;
+	xhci_slot_ctx_t slot_ctx;
+	xhci_ep_ctx_t endpoint_ctx[XHCI_EP_COUNT];
+} __attribute__((packed)) xhci_input_ctx_t;
+
+/**
+ * Port bandwidth context: section 6.2.6
+ * The number of ports depends on the amount of ports available to the hub.
+ */
+typedef struct xhci_port_bandwidth_ctx {
+	uint8_t reserved;
+	uint8_t ports [];
+} __attribute__((packed)) xhci_port_bandwidth_ctx_t;
+
+#endif
Index: uspace/drv/bus/usb/xhci/hw_struct/regs.h
===================================================================
--- uspace/drv/bus/usb/xhci/hw_struct/regs.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/hw_struct/regs.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,593 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty
+ * 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
+ * Memory-mapped register structures of the xHC.
+ *
+ * The main pr
+ */
+
+#ifndef XHCI_REGS_H
+#define XHCI_REGS_H
+
+#include <macros.h>
+#include <ddi.h>
+#include "common.h"
+
+#define XHCI_PIO_CHANGE_UDELAY 5
+
+/*
+ * These four are the main macros to be used.
+ * Semantics is usual - READ reads value, WRITE changes value, SET sets
+ * selected bits, CLEAR clears selected bits to 0.
+ *
+ * The key thing here is the order of macro expansion, expanding the reg_spec
+ * argument as more arguments (comma delimited) for the inner macro.
+ */
+#define XHCI_REG_RD(reg_set, reg_spec)         XHCI_REG_RD_INNER(reg_set, reg_spec)
+#define XHCI_REG_WR(reg_set, reg_spec, value)  XHCI_REG_WR_INNER(reg_set, value, reg_spec)
+#define XHCI_REG_SET(reg_set, reg_spec, value) XHCI_REG_SET_INNER(reg_set, value, reg_spec)
+#define XHCI_REG_CLR(reg_set, reg_spec, value) XHCI_REG_CLR_INNER(reg_set, value, reg_spec)
+#define XHCI_REG_MASK(reg_spec)                XHCI_REG_MASK_INNER(reg_spec)
+#define XHCI_REG_SHIFT(reg_spec)               XHCI_REG_SHIFT_INNER(reg_spec)
+
+/*
+ * These take a pointer to the field, and selects the type-specific macro.
+ */
+#define XHCI_REG_RD_INNER(reg_set, field, size, type, ...) \
+	XHCI_REG_RD_##type(&((reg_set)->field), size, ##__VA_ARGS__)
+
+#define XHCI_REG_WR_INNER(reg_set, value, field, size, type, ...) \
+	XHCI_REG_WR_##type(&(reg_set)->field, value, size, ##__VA_ARGS__)
+
+#define XHCI_REG_SET_INNER(reg_set, value, field, size, type, ...) \
+	XHCI_REG_SET_##type(&(reg_set)->field, value, size, ##__VA_ARGS__)
+
+#define XHCI_REG_CLR_INNER(reg_set, value, field, size, type, ...) \
+	XHCI_REG_CLR_##type(&(reg_set)->field, value, size, ##__VA_ARGS__)
+
+#define XHCI_REG_MASK_INNER(field, size, type, ...) \
+	XHCI_REG_MASK_##type(size, ##__VA_ARGS__)
+
+#define XHCI_REG_SHIFT_INNER(field, size, type, ...) \
+	XHCI_REG_SHIFT_##type(size, ##__VA_ARGS__)
+
+/*
+ * Field handling is the easiest. Just do it with whole field.
+ */
+#define XHCI_REG_RD_FIELD(ptr, size)         xhci2host(size, pio_read_##size((ptr)))
+#define XHCI_REG_WR_FIELD(ptr, value, size)  pio_write_##size((ptr), host2xhci(size, value))
+#define XHCI_REG_SET_FIELD(ptr, value, size) pio_set_##size((ptr), host2xhci(size, value), XHCI_PIO_CHANGE_UDELAY);
+#define XHCI_REG_CLR_FIELD(ptr, value, size) pio_clear_##size((ptr), host2xhci(size, value), XHCI_PIO_CHANGE_UDELAY);
+#define XHCI_REG_MASK_FIELD(size)            (~((uint##size##_t) 0))
+#define XHCI_REG_SHIFT_FIELD(size)           (0)
+
+/*
+ * Flags are just trivial case of ranges.
+ */
+#define XHCI_REG_RD_FLAG(ptr, size, offset)         XHCI_REG_RD_RANGE((ptr), size, (offset), (offset))
+#define XHCI_REG_WR_FLAG(ptr, value, size, offset)  XHCI_REG_WR_RANGE((ptr), (value), size, (offset), (offset))
+#define XHCI_REG_SET_FLAG(ptr, value, size, offset) XHCI_REG_SET_RANGE((ptr), (value), size, (offset), (offset))
+#define XHCI_REG_CLR_FLAG(ptr, value, size, offset) XHCI_REG_CLR_RANGE((ptr), (value), size, (offset), (offset))
+#define XHCI_REG_MASK_FLAG(size, offset)            BIT_V(uint##size##_t, offset)
+#define XHCI_REG_SHIFT_FLAG(size, offset)           (offset)
+
+/*
+ * Ranges are the most difficult. We need to play around with bitmasks.
+ */
+#define XHCI_REG_RD_RANGE(ptr, size, hi, lo) \
+	BIT_RANGE_EXTRACT(uint##size##_t, (hi), (lo), XHCI_REG_RD_FIELD((ptr), size))
+
+#define XHCI_REG_WR_RANGE(ptr, value, size, hi, lo) \
+	pio_change_##size((ptr), host2xhci(size, BIT_RANGE_INSERT(uint##size##_t, (hi), (lo), (value))), \
+		host2xhci(size, BIT_RANGE(uint##size##_t, (hi), (lo))), \
+		XHCI_PIO_CHANGE_UDELAY);
+
+#define XHCI_REG_SET_RANGE(ptr, value, size, hi, lo) \
+	pio_set_##size((ptr), host2xhci(size, BIT_RANGE_INSERT(uint##size##_t, (hi), (lo), (value))), \
+		XHCI_PIO_CHANGE_UDELAY);
+
+#define XHCI_REG_CLR_RANGE(ptr, value, size, hi, lo) \
+	pio_clear_##size((ptr), host2xhci(size, BIT_RANGE_INSERT(uint##size##_t, (hi), (lo), (value))), \
+		XHCI_PIO_CHANGE_UDELAY);
+
+#define XHCI_REG_MASK_RANGE(size, hi, lo)  BIT_RANGE(uint##size##_t, hi, lo)
+#define XHCI_REG_SHIFT_RANGE(size, hi, lo) (lo)
+
+/** HC capability registers: section 5.3 */
+typedef const struct xhci_cap_regs {
+
+	/* Size of this structure, offset for the operation registers */
+	const ioport8_t caplength;
+
+	const PADD8;
+
+	/* BCD of specification version */
+	const ioport16_t hciversion;
+
+	/*
+	 *  7:0  - MaxSlots
+	 * 18:8  - MaxIntrs
+	 * 31:24 - MaxPorts
+	 */
+	const ioport32_t hcsparams1;
+
+	/*
+	 *  3:0  - IST
+	 *  7:4  - ERST Max
+	 * 25:21 - Max Scratchpad Bufs Hi
+	 *    26 - SPR
+	 * 31:27 - Max Scratchpad Bufs Lo
+	 */
+	const ioport32_t hcsparams2;
+
+	/*
+	 *  7:0  - U1 Device Exit Latency
+	 * 31:16 - U2 Device Exit Latency
+	 */
+	const ioport32_t hcsparams3;
+
+	/*
+	 *          11  10   9   8   7   6 5    4   3   2   1    0
+	 * 11:0  - CFC SEC SPC PAE NSS LTC C PIND PPC CSZ BNC AC64
+	 * 15:12 - MaxPSASize
+	 * 31:16 - xECP
+	 */
+	const ioport32_t hccparams1;
+
+	/*
+	 * 31:2 - Doorbell Array Offset
+	 */
+	const ioport32_t dboff;
+
+	/*
+	 * 31:5 - Runtime Register Space Offset
+	 */
+	const ioport32_t rtsoff;
+
+	/*
+	 *                 5   4   3   2   1   0
+	 * 5:0  - Flags: CIC LEC CTC FSC CMC U3C
+	 */
+	const ioport32_t hccparams2;
+
+	// the rest to operational registers is reserved
+} xhci_cap_regs_t;
+
+/*
+ * The register specifiers are to be used as the reg_spec argument.
+ *
+ * The values are field, bitsize, type, (type specific args)
+ * When the type is RANGE: hi, lo
+ */
+#define XHCI_CAP_LENGTH        caplength,  8, FIELD
+#define XHCI_CAP_VERSION      hciversion, 16, FIELD
+#define XHCI_CAP_MAX_SLOTS    hcsparams1, 32, RANGE,  7,  0
+#define XHCI_CAP_MAX_INTRS    hcsparams1, 32, RANGE, 18,  8
+#define XHCI_CAP_MAX_PORTS    hcsparams1, 32, RANGE, 31, 24
+#define XHCI_CAP_IST          hcsparams2, 32, RANGE,  3,  0
+#define XHCI_CAP_ERST_MAX     hcsparams2, 32, RANGE,  7,  4
+#define XHCI_CAP_MAX_SPBUF_LO hcsparams2, 32, RANGE, 25,  4
+#define XHCI_CAP_SPR          hcsparams2, 32,  FLAG, 26
+#define XHCI_CAP_MAX_SPBUF_HI hcsparams2, 32, RANGE, 31, 27
+#define XHCI_CAP_U1EL         hcsparams3, 32, RANGE,  7,  0
+#define XHCI_CAP_U2EL         hcsparams3, 32, RANGE, 31, 16
+#define XHCI_CAP_AC64         hccparams1, 32,  FLAG,  0
+#define XHCI_CAP_BNC          hccparams1, 32,  FLAG,  1
+#define XHCI_CAP_CSZ          hccparams1, 32,  FLAG,  2
+#define XHCI_CAP_PPC          hccparams1, 32,  FLAG,  3
+#define XHCI_CAP_PIND         hccparams1, 32,  FLAG,  4
+#define XHCI_CAP_C            hccparams1, 32,  FLAG,  5
+#define XHCI_CAP_LTC          hccparams1, 32,  FLAG,  6
+#define XHCI_CAP_NSS          hccparams1, 32,  FLAG,  7
+#define XHCI_CAP_PAE          hccparams1, 32,  FLAG,  8
+#define XHCI_CAP_SPC          hccparams1, 32,  FLAG,  9
+#define XHCI_CAP_SEC          hccparams1, 32,  FLAG, 10
+#define XHCI_CAP_CFC          hccparams1, 32,  FLAG, 11
+#define XHCI_CAP_MAX_PSA_SIZE hccparams1, 32, RANGE, 15, 12
+#define XHCI_CAP_XECP         hccparams1, 32, RANGE, 31, 16
+#define XHCI_CAP_DBOFF             dboff, 32, FIELD
+#define XHCI_CAP_RTSOFF           rtsoff, 32, FIELD
+#define XHCI_CAP_U3C          hccparams2, 32,  FLAG,  0
+#define XHCI_CAP_CMC          hccparams2, 32,  FLAG,  1
+#define XHCI_CAP_FSC          hccparams2, 32,  FLAG,  2
+#define XHCI_CAP_CTC          hccparams2, 32,  FLAG,  3
+#define XHCI_CAP_LEC          hccparams2, 32,  FLAG,  4
+#define XHCI_CAP_CIC          hccparams2, 32,  FLAG,  5
+
+static inline unsigned xhci_get_max_spbuf(xhci_cap_regs_t *cap_regs) {
+	return XHCI_REG_RD(cap_regs, XHCI_CAP_MAX_SPBUF_HI) << 5
+		| XHCI_REG_RD(cap_regs, XHCI_CAP_MAX_SPBUF_LO);
+}
+
+/**
+ * XHCI Port Register Set: section 5.4, table 32
+ */
+typedef struct xhci_port_regs {
+	/*
+	 *          4   3 2   1   0
+	 *  4:0  - PR OCA Z PED CCS
+	 *  8:5  - PLS
+	 *    9  - PP
+	 * 13:10 - Port Speed
+	 * 15:14 - PIC
+	 *          27  26  25  24  23  22  21  20  19  18  17  16
+	 * 27:16 - WOE WDE WCE CAS CEC PLC PRC OCC WRC PEC CSC LWS
+	 *    30 - DR
+	 *    31 - WPR
+	 */
+	ioport32_t portsc;
+
+	/*
+	 * Contents of this fields depends on the protocol supported by the port.
+	 * USB3:
+	 *      7:0  - U1 Timeout
+	 *     15:8  - U2 Timeout
+	 *        16 - Force Link PM Accept
+	 * USB2:
+	 *      2:0  - L1S
+	 *        3  - RWE
+	 *      7:4  - BESL
+	 *     15:8  - L1 Device Slot
+	 *        16 - HLE
+	 *     31:28 - Test Mode
+	 */
+	ioport32_t portpmsc;
+
+	/*
+	 * This field is valid only for USB3 ports.
+	 * 15:0  - Link Error Count
+	 * 19:16 - RLC
+	 * 23:20 - TLC
+	 */
+	ioport32_t portli;
+
+	/*
+	 * This field is valid only for USB2 ports.
+	 *  1:0  - HIRDM
+	 *  9:2  - L1 Timeout
+	 * 13:10 - BESLD
+	 */
+	ioport32_t porthlpmc;
+} xhci_port_regs_t;
+
+#define XHCI_PORT_CCS           portsc, 32,  FLAG,  0
+#define XHCI_PORT_PED           portsc, 32,  FLAG,  1
+#define XHCI_PORT_OCA           portsc, 32,  FLAG,  3
+#define XHCI_PORT_PR            portsc, 32,  FLAG,  4
+#define XHCI_PORT_PLS           portsc, 32, RANGE,  8,  5
+#define XHCI_PORT_PP            portsc, 32,  FLAG,  9
+#define XHCI_PORT_PS            portsc, 32, RANGE, 13, 10
+#define XHCI_PORT_PIC           portsc, 32, RANGE, 15, 14
+#define XHCI_PORT_LWS           portsc, 32,  FLAG, 16
+#define XHCI_PORT_CSC           portsc, 32,  FLAG, 17
+#define XHCI_PORT_PEC           portsc, 32,  FLAG, 18
+#define XHCI_PORT_WRC           portsc, 32,  FLAG, 19
+#define XHCI_PORT_OCC           portsc, 32,  FLAG, 20
+#define XHCI_PORT_PRC           portsc, 32,  FLAG, 21
+#define XHCI_PORT_PLC           portsc, 32,  FLAG, 22
+#define XHCI_PORT_CEC           portsc, 32,  FLAG, 23
+#define XHCI_PORT_CAS           portsc, 32,  FLAG, 24
+#define XHCI_PORT_WCE           portsc, 32,  FLAG, 25
+#define XHCI_PORT_WDE           portsc, 32,  FLAG, 26
+#define XHCI_PORT_WOE           portsc, 32,  FLAG, 27
+#define XHCI_PORT_DR            portsc, 32,  FLAG, 28
+#define XHCI_PORT_WPR           portsc, 32,  FLAG, 29
+
+#define XHCI_PORT_USB3_U1TO   portpmsc, 32, RANGE,  7,  0
+#define XHCI_PORT_USB3_U2TO   portpmsc, 32, RANGE, 15,  8
+#define XHCI_PORT_USB3_FLPMA  portpmsc, 32,  FLAG, 16
+#define XHCI_PORT_USB3_LEC      portli, 32, RANGE, 15,  0
+#define XHCI_PORT_USB3_RLC      portli, 32, RANGE, 19, 16
+#define XHCI_PORT_USB3_TLC      portli, 32, RANGE, 23, 20
+
+#define XHCI_PORT_USB2_L1S    portpmsc, 32, RANGE,  2,  0
+#define XHCI_PORT_USB2_RWE    portpmsc, 32,  FLAG,  3
+#define XHCI_PORT_USB2_BESL   portpmsc, 32, RANGE,  7,  4
+#define XHCI_PORT_USB2_L1DS   portpmsc, 32, RANGE, 15,  8
+#define XHCI_PORT_USB2_HLE    portpmsc, 32,  FLAG, 16
+#define XHCI_PORT_USB2_TM     portpmsc, 32, RANGE, 31, 28
+#define XHCI_PORT_USB2_HIRDM porthlmpc, 32, RANGE,  1,  0
+#define XHCI_PORT_USB2_L1TO  porthlmpc, 32, RANGE,  9,  2
+#define XHCI_PORT_USB2_BESLD porthlmpc, 32, RANGE, 13, 10
+
+/**
+ * XHCI Operational Registers: section 5.4
+ */
+typedef struct xhci_op_regs {
+
+	/*
+	 *            3    2     1   0
+	 *  3:0  - HSEE INTE HCRST R/S
+	 *
+	 *           11  10   9   8      7
+	 * 11:7  - EU3S EWE CRS CSS LHCRST
+	 *    13 - CME
+	 */
+	ioport32_t usbcmd;
+
+	/*
+	 *          4    3   2 1   0
+	 *  4:0 - PCD EINT HSE _ HCH
+	 *
+	 *        12  11  10   9   8
+	 * 12:8 - HCE CNR SRE RSS SSS
+	 */
+	ioport32_t usbsts;
+
+	/*
+	 * Bitmask of page sizes supported: 128M .. 4K
+	 */
+	ioport32_t pagesize;
+
+	PADD32[2];
+
+	/*
+	 * 15:0 - Notification enable
+	 */
+	ioport32_t dnctrl;
+
+	/*          3  2  1   0
+	 *  3:0 - CRR CA CS RCS
+	 * 64:6 - Command Ring Pointer
+	 */
+	ioport32_t crcr_lo;
+	ioport32_t crcr_hi;
+
+	PADD32[4];
+
+	ioport32_t dcbaap_lo;
+	ioport32_t dcbaap_hi;
+
+	/*
+	 * 7:0 - MaxSlotsEn
+	 *   8 - U3E
+	 *   9 - CIE
+	 */
+	ioport32_t config;
+
+	/* Offset of portrs from op_regs addr is 0x400. */
+	PADD32[241];
+
+	/*
+	 * Individual ports register sets
+	 */
+	xhci_port_regs_t portrs[256];
+} xhci_op_regs_t;
+
+#define XHCI_OP_RS              usbcmd, 32,  FLAG,  0
+#define XHCI_OP_HCRST           usbcmd, 32,  FLAG,  1
+#define XHCI_OP_INTE            usbcmd, 32,  FLAG,  2
+#define XHCI_OP_HSEE            usbcmd, 32,  FLAG,  3
+#define XHCI_OP_LHCRST          usbcmd, 32,  FLAG,  7
+#define XHCI_OP_CSS             usbcmd, 32,  FLAG,  8
+#define XHCI_OP_CRS             usbcmd, 32,  FLAG,  9
+#define XHCI_OP_EWE             usbcmd, 32,  FLAG, 10
+#define XHCI_OP_EU3S            usbcmd, 32,  FLAG, 11
+#define XHCI_OP_CME             usbcmd, 32,  FLAG, 13
+#define XHCI_OP_HCH             usbsts, 32,  FLAG,  0
+#define XHCI_OP_HSE             usbsts, 32,  FLAG,  2
+#define XHCI_OP_EINT            usbsts, 32,  FLAG,  3
+#define XHCI_OP_PCD             usbsts, 32,  FLAG,  4
+#define XHCI_OP_SSS             usbsts, 32,  FLAG,  8
+#define XHCI_OP_RSS             usbsts, 32,  FLAG,  9
+#define XHCI_OP_SRE             usbsts, 32,  FLAG, 10
+#define XHCI_OP_CNR             usbsts, 32,  FLAG, 11
+#define XHCI_OP_HCE             usbsts, 32,  FLAG, 12
+#define XHCI_OP_PAGESIZE      pagesize, 32, FIELD
+#define XHCI_OP_NOTIFICATION    dnctrl, 32, RANGE, 15, 0
+#define XHCI_OP_RCS            crcr_lo, 32,  FLAG, 0
+#define XHCI_OP_CS             crcr_lo, 32,  FLAG, 1
+#define XHCI_OP_CA             crcr_lo, 32,  FLAG, 2
+#define XHCI_OP_CRR            crcr_lo, 32,  FLAG, 3
+#define XHCI_OP_CRCR_LO        crcr_lo, 32, RANGE, 31, 6
+#define XHCI_OP_CRCR_HI        crcr_hi, 32, FIELD
+#define XHCI_OP_DCBAAP_LO    dcbaap_lo, 32, FIELD
+#define XHCI_OP_DCBAAP_HI    dcbaap_hi, 32, FIELD
+#define XHCI_OP_MAX_SLOTS_EN    config, 32, RANGE, 7, 0
+#define XHCI_OP_U3E             config, 32,  FLAG, 8
+#define XHCI_OP_CIE             config, 32,  FLAG, 9
+
+/* Aggregating field to read & write whole status at once */
+#define XHCI_OP_STATUS          usbsts, 32, RANGE, 12, 0
+
+/* RW1C fields in usbsts */
+#define XHCI_STATUS_ACK_MASK     0x41C
+
+/**
+ * Interrupter Register Set: section 5.5.2
+ */
+typedef struct xhci_interrupter_regs {
+	/*
+	 * 0 - Interrupt Pending
+	 * 1 - Interrupt Enable
+	 */
+	ioport32_t iman;
+
+	/*
+	 * 15:0  - Interrupt Moderation Interval
+	 * 31:16 - Interrupt Moderation Counter
+	 */
+	ioport32_t imod;
+
+	ioport32_t erstsz;
+
+	PADD32;
+
+	ioport32_t erstba_lo;
+	ioport32_t erstba_hi;
+
+	/*
+	 *  2:0 - Dequeue ERST Segment Index
+	 *    3 - Event Handler Busy
+	 * 63:4 - Event Ring Dequeue Pointer
+	 */
+	ioport32_t erdp_lo;
+	ioport32_t erdp_hi;
+} xhci_interrupter_regs_t;
+
+#define XHCI_INTR_IP              iman, 32,  FLAG,  0
+#define XHCI_INTR_IE              iman, 32,  FLAG,  1
+#define XHCI_INTR_IMI             imod, 32, RANGE, 15, 0
+#define XHCI_INTR_IMC             imod, 32, RANGE, 31, 16
+#define XHCI_INTR_ERSTSZ        erstsz, 32, FIELD
+#define XHCI_INTR_ERSTBA_LO  erstba_lo, 32, FIELD
+#define XHCI_INTR_ERSTBA_HI  erstba_hi, 32, FIELD
+#define XHCI_INTR_ERDP_ESI     erdp_lo, 32, RANGE,  2, 0
+#define XHCI_INTR_ERDP_EHB     erdp_lo, 32,  FLAG,  3
+// TODO: ERDP_LO is supposed to be RANGE 31, 4 (section 5.5.2.3.3).
+#define XHCI_INTR_ERDP_LO      erdp_lo, 32, FIELD
+#define XHCI_INTR_ERDP_HI      erdp_hi, 32, FIELD
+
+/**
+ * XHCI Runtime registers: section 5.5
+ */
+typedef struct xhci_rt_regs {
+	ioport32_t mfindex;
+
+	PADD32 [7];
+
+	xhci_interrupter_regs_t ir [];
+} xhci_rt_regs_t;
+
+#define XHCI_RT_MFINDEX        mfindex, 32, FIELD
+
+/**
+ * XHCI Doorbell Registers: section 5.6
+ *
+ * These registers are to be written as a whole field.
+ */
+typedef ioport32_t xhci_doorbell_t;
+
+enum xhci_plt {
+	XHCI_PSI_PLT_SYMM,
+	XHCI_PSI_PLT_RSVD,
+	XHCI_PSI_PLT_RX,
+	XHCI_PSI_PLT_TX
+};
+
+/**
+ * Protocol speed ID: section 7.2.1
+ */
+typedef struct xhci_psi {
+	xhci_dword_t psi;
+} xhci_psi_t;
+
+#define XHCI_PSI_PSIV    psi, 32, RANGE,  3,  0
+#define XHCI_PSI_PSIE    psi, 32, RANGE,  5,  4
+#define XHCI_PSI_PLT     psi, 32, RANGE,  7,  6
+#define XHCI_PSI_PFD     psi, 32,  FLAG,  8
+#define XHCI_PSI_PSIM    psi, 32, RANGE, 31, 16
+
+enum xhci_extcap_type {
+	XHCI_EC_RESERVED = 0,
+	XHCI_EC_USB_LEGACY,
+	XHCI_EC_SUPPORTED_PROTOCOL,
+	XHCI_EC_EXTENDED_POWER_MANAGEMENT,
+	XHCI_EC_IOV,
+	XHCI_EC_MSI,
+	XHCI_EC_LOCALMEM,
+	XHCI_EC_DEBUG = 10,
+	XHCI_EC_MSIX = 17,
+	XHCI_EC_MAX = 255
+};
+
+/**
+ * xHCI Extended Capability: section 7
+ */
+typedef struct xhci_extcap {
+	xhci_dword_t header;
+	xhci_dword_t cap_specific[];
+} xhci_extcap_t;
+
+#define XHCI_EC_CAP_ID                           header, 32, RANGE,  7,  0
+#define XHCI_EC_SIZE                             header, 32, RANGE, 15,  8
+
+/* Supported protocol */
+#define XHCI_EC_SP_MINOR                         header, 32, RANGE, 23, 16
+#define XHCI_EC_SP_MAJOR                         header, 32, RANGE, 31, 24
+#define XHCI_EC_SP_NAME                 cap_specific[0], 32, FIELD
+#define XHCI_EC_SP_CP_OFF               cap_specific[1], 32, RANGE,  7,  0
+#define XHCI_EC_SP_CP_COUNT             cap_specific[1], 32, RANGE, 15,  8
+#define XHCI_EC_SP_PSIC                 cap_specific[1], 32, RANGE, 31, 28
+#define XHCI_EC_SP_SLOT_TYPE            cap_specific[2], 32, RANGE,  4,  0
+
+typedef union {
+	char str [4];
+	uint32_t packed;
+} xhci_sp_name_t;
+
+static const xhci_sp_name_t xhci_name_usb = {
+	.str = "USB "
+};
+
+static inline xhci_extcap_t *xhci_extcap_next(const xhci_extcap_t *cur)
+{
+	unsigned dword_offset = XHCI_REG_RD(cur, XHCI_EC_SIZE);
+	if (!dword_offset)
+		return NULL;
+	return (xhci_extcap_t *) (((xhci_dword_t *) cur) + dword_offset);
+}
+
+static inline xhci_psi_t *xhci_extcap_psi(const xhci_extcap_t *ec, unsigned psid)
+{
+	assert(XHCI_REG_RD(ec, XHCI_EC_CAP_ID) == XHCI_EC_SUPPORTED_PROTOCOL);
+	assert(XHCI_REG_RD(ec, XHCI_EC_SP_PSIC) > psid);
+
+	unsigned dword_offset = 4 + psid;
+	return (xhci_psi_t *) (((xhci_dword_t *) ec) + dword_offset);
+}
+
+/**
+ * USB Legacy Support: section 7.1
+ *
+ * Legacy support have an exception from dword-access, because it needs to be
+ * byte-accessed.
+ */
+typedef struct xhci_extcap_legsup {
+	ioport8_t cap_id;
+	ioport8_t size;			/**< Next Capability Pointer */
+	ioport8_t sem_bios;
+	ioport8_t sem_os;
+
+	xhci_dword_t usblegctlsts;	/**< USB Legacy Support Control/Status - RW for BIOS, RO for OS */
+} xhci_legsup_t;
+
+#define XHCI_LEGSUP_SEM_BIOS	sem_bios, 8, FLAG, 0
+#define XHCI_LEGSUP_SEM_OS	sem_os, 8, FLAG, 0
+
+#define XHCI_LEGSUP_POLLING_DELAY_1MS	1000
+#define XHCI_LEGSUP_BIOS_TIMEOUT_US	1000000 /* 4.22.1 BIOS may take up to 1 second to release the device */
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/hw_struct/trb.h
===================================================================
--- uspace/drv/bus/usb/xhci/hw_struct/trb.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/hw_struct/trb.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty
+ * 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
+ * TRB-related structures of the xHC.
+ *
+ * This file contains all the types of TRB and the TRB ring handling.
+ */
+
+#ifndef XHCI_TRB_H
+#define XHCI_TRB_H
+
+#include "common.h"
+#include <libarch/barrier.h>
+
+/**
+ * TRB types: section 6.4.6, table 139
+ */
+enum xhci_trb_type {
+	XHCI_TRB_TYPE_RESERVED = 0,
+
+// Transfer ring:
+	XHCI_TRB_TYPE_NORMAL,
+	XHCI_TRB_TYPE_SETUP_STAGE,
+	XHCI_TRB_TYPE_DATA_STAGE,
+	XHCI_TRB_TYPE_STATUS_STAGE,
+	XHCI_TRB_TYPE_ISOCH,
+	XHCI_TRB_TYPE_LINK,
+	XHCI_TRB_TYPE_EVENT_DATA,
+	XHCI_TRB_TYPE_NO_OP,
+
+// Command ring:
+	XHCI_TRB_TYPE_ENABLE_SLOT_CMD,
+	XHCI_TRB_TYPE_DISABLE_SLOT_CMD,
+	XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD,
+	XHCI_TRB_TYPE_CONFIGURE_ENDPOINT_CMD,
+	XHCI_TRB_TYPE_EVALUATE_CONTEXT_CMD,
+	XHCI_TRB_TYPE_RESET_ENDPOINT_CMD,
+	XHCI_TRB_TYPE_STOP_ENDPOINT_CMD,
+	XHCI_TRB_TYPE_SET_TR_DEQUEUE_POINTER_CMD,
+	XHCI_TRB_TYPE_RESET_DEVICE_CMD,
+	XHCI_TRB_TYPE_FORCE_EVENT_CMD,
+	XHCI_TRB_TYPE_NEGOTIATE_BANDWIDTH_CMD,
+	XHCI_TRB_TYPE_SET_LATENCY_TOLERANCE_VALUE_CMD,
+	XHCI_TRB_TYPE_GET_PORT_BANDWIDTH_CMD,
+	XHCI_TRB_TYPE_FORCE_HEADER_CMD,
+	XHCI_TRB_TYPE_NO_OP_CMD,
+// Reserved: 24-31
+
+// Event ring:
+	XHCI_TRB_TYPE_TRANSFER_EVENT = 32,
+	XHCI_TRB_TYPE_COMMAND_COMPLETION_EVENT,
+	XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVENT,
+	XHCI_TRB_TYPE_BANDWIDTH_REQUEST_EVENT,
+	XHCI_TRB_TYPE_DOORBELL_EVENT,
+	XHCI_TRB_TYPE_HOST_CONTROLLER_EVENT,
+	XHCI_TRB_TYPE_DEVICE_NOTIFICATION_EVENT,
+	XHCI_TRB_TYPE_MFINDEX_WRAP_EVENT,
+
+	XHCI_TRB_TYPE_MAX
+};
+
+/**
+ * TRB template: section 4.11.1
+ */
+typedef struct xhci_trb {
+	xhci_qword_t parameter;
+	xhci_dword_t status;
+	xhci_dword_t control;
+} __attribute__((packed)) xhci_trb_t;
+
+#define TRB_TYPE(trb)           XHCI_DWORD_EXTRACT((trb).control, 15, 10)
+#define TRB_CYCLE(trb)          XHCI_DWORD_EXTRACT((trb).control, 0, 0)
+#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_TRANSFER_LENGTH(trb)	XHCI_DWORD_EXTRACT((trb).status, 23, 0)
+#define TRB_COMPLETION_CODE(trb)	XHCI_DWORD_EXTRACT((trb).status, 31, 24)
+
+#define TRB_LINK_SET_TC(trb, val) \
+	xhci_dword_set_bits(&(trb).control, val, 1, 1)
+#define TRB_SET_CYCLE(trb, val) \
+	xhci_dword_set_bits(&(trb).control, val, 0, 0)
+
+#define TRB_CTRL_SET_SETUP_WLENGTH(trb, val) \
+	xhci_qword_set_bits(&(trb).parameter, val, 63, 48)
+#define TRB_CTRL_SET_SETUP_WINDEX(trb, val) \
+	xhci_qword_set_bits(&(trb).parameter, val, 47, 32)
+#define TRB_CTRL_SET_SETUP_WVALUE(trb, val) \
+	xhci_qword_set_bits(&(trb).parameter, val, 31, 16)
+#define TRB_CTRL_SET_SETUP_BREQ(trb, val) \
+	xhci_qword_set_bits(&(trb).parameter, val, 15, 8)
+#define TRB_CTRL_SET_SETUP_BMREQTYPE(trb, val) \
+	xhci_qword_set_bits(&(trb).parameter, val, 7, 0)
+
+#define TRB_CTRL_SET_TD_SIZE(trb, val) \
+	xhci_dword_set_bits(&(trb).status, val, 21, 17)
+#define TRB_CTRL_SET_XFER_LEN(trb, val) \
+	xhci_dword_set_bits(&(trb).status, val, 16, 0)
+
+#define TRB_CTRL_SET_ENT(trb, val) \
+	xhci_dword_set_bits(&(trb).control, val, 1, 1)
+#define TRB_CTRL_SET_ISP(trb, val) \
+	xhci_dword_set_bits(&(trb).control, val, 2, 2)
+#define TRB_CTRL_SET_NS(trb, val) \
+	xhci_dword_set_bits(&(trb).control, val, 3, 3)
+#define TRB_CTRL_SET_CHAIN(trb, val) \
+	xhci_dword_set_bits(&(trb).control, val, 4, 4)
+#define TRB_CTRL_SET_IOC(trb, val) \
+	xhci_dword_set_bits(&(trb).control, val, 5, 5)
+#define TRB_CTRL_SET_IDT(trb, val) \
+	xhci_dword_set_bits(&(trb).control, val, 6, 6)
+
+#define TRB_CTRL_SET_TRB_TYPE(trb, val) \
+	xhci_dword_set_bits(&(trb).control, val, 15, 10)
+#define TRB_CTRL_SET_DIR(trb, val) \
+	xhci_dword_set_bits(&(trb).control, val, 16, 16)
+#define TRB_CTRL_SET_TRT(trb, val) \
+	xhci_dword_set_bits(&(trb).control, val, 17, 16)
+
+/**
+ * The Chain bit is valid only in specific TRB types.
+ */
+static inline bool xhci_trb_is_chained(xhci_trb_t *trb) {
+	const int type = TRB_TYPE(*trb);
+	const bool chain_bit = XHCI_DWORD_EXTRACT(trb->control, 4, 4);
+
+	return chain_bit &&
+	    (type == XHCI_TRB_TYPE_NORMAL
+	    || type == XHCI_TRB_TYPE_DATA_STAGE
+	    || type == XHCI_TRB_TYPE_STATUS_STAGE
+	    || type == XHCI_TRB_TYPE_ISOCH);
+}
+
+static inline void xhci_trb_link_fill(xhci_trb_t *trb, uintptr_t next_phys)
+{
+	// TRBs require 16-byte alignment
+	assert((next_phys & 0xf) == 0);
+
+	xhci_dword_set_bits(&trb->control, XHCI_TRB_TYPE_LINK, 15, 10);
+	xhci_qword_set(&trb->parameter, next_phys);
+}
+
+static inline void xhci_trb_copy_to_pio(xhci_trb_t *dst, xhci_trb_t *src)
+{
+	/*
+	 * As we do not know, whether our architecture is capable of copying 16
+	 * bytes atomically, let's copy the fields one by one.
+	 */
+	dst->parameter = src->parameter;
+	dst->status = src->status;
+
+	write_barrier();
+
+	dst->control = src->control;
+}
+
+static inline void xhci_trb_clean(xhci_trb_t *trb)
+{
+	memset(trb, 0, sizeof(*trb));
+}
+
+/**
+ * Event Ring Segment Table: section 6.5
+ */
+typedef struct xhci_erst_entry {
+	xhci_qword_t rs_base_ptr;       /* 64B aligned */
+	xhci_dword_t size;              /* only low 16 bits, the rest is RsvdZ */
+	xhci_dword_t _reserved;
+} xhci_erst_entry_t;
+
+static inline void xhci_fill_erst_entry(xhci_erst_entry_t *entry, uintptr_t phys, int segments)
+{
+	xhci_qword_set(&entry->rs_base_ptr, phys);
+	xhci_dword_set_bits(&entry->size, segments, 16, 0);
+}
+
+enum xhci_trb_completion_code {
+	XHCI_TRBC_INVALID = 0,
+	XHCI_TRBC_SUCCESS,
+	XHCI_TRBC_DATA_BUFFER_ERROR,
+	XHCI_TRBC_BABBLE_DETECTED_ERROR,
+	XHCI_TRBC_USB_TRANSACTION_ERROR,
+	XHCI_TRBC_TRB_ERROR,
+	XHCI_TRBC_STALL_ERROR,
+	XHCI_TRBC_RESOURCE_ERROR,
+	XHCI_TRBC_BANDWIDTH_ERROR,
+	XHCI_TRBC_NO_SLOTS_ERROR,
+	XHCI_TRBC_INVALID_STREAM_ERROR,
+	XHCI_TRBC_SLOT_NOT_ENABLED_ERROR,
+	XHCI_TRBC_EP_NOT_ENABLED_ERROR,
+	XHCI_TRBC_SHORT_PACKET,
+	XHCI_TRBC_RING_UNDERRUN,
+	XHCI_TRBC_RING_OVERRUN,
+	XHCI_TRBC_VF_EVENT_RING_FULL,
+	XHCI_TRBC_PARAMETER_ERROR,
+	XHCI_TRBC_BANDWIDTH_OVERRUN_ERROR,
+	XHCI_TRBC_CONTEXT_STATE_ERROR,
+	XHCI_TRBC_NO_PING_RESPONSE_ERROR,
+	XHCI_TRBC_EVENT_RING_FULL_ERROR,
+	XHCI_TRBC_INCOMPATIBLE_DEVICE_ERROR,
+	XHCI_TRBC_MISSED_SERVICE_ERROR,
+	XHCI_TRBC_COMMAND_RING_STOPPED,
+	XHCI_TRBC_COMMAND_ABORTED,
+	XHCI_TRBC_STOPPED,
+	XHCI_TRBC_STOPPED_LENGTH_INVALID,
+	XHCI_TRBC_STOPPED_SHORT_PACKET,
+	XHCI_TRBC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR,
+	/* 30 reserved */
+	XHCI_TRBC_ISOCH_BUFFER_OVERRUN = 31,
+	XHCI_TRBC_EVENT_LOST_ERROR,
+	XHCI_TRBC_UNDEFINED_ERROR,
+	XHCI_TRBC_INVALID_STREAM_ID_ERROR,
+	XHCI_TRBC_SECONDARY_BANDWIDTH_ERROR,
+	XHCI_TRBC_SPLIT_TRANSACTION_ERROR,
+	XHCI_TRBC_MAX
+	/**
+	 *  37 - 191 reserved
+	 * 192 - 223 vendor defined error
+	 * 224 - 255 vendor defined info
+	 */
+};
+
+#endif
Index: uspace/drv/bus/usb/xhci/main.c
===================================================================
--- uspace/drv/bus/usb/xhci/main.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/main.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
+ * 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
+ * Main routines of XHCI driver.
+ */
+
+#include <ddf/driver.h>
+#include <errno.h>
+#include <io/log.h>
+#include <io/logctl.h>
+#include <usb/debug.h>
+#include <usb/host/ddf_helpers.h>
+
+#include "hc.h"
+#include "rh.h"
+#include "endpoint.h"
+
+#define NAME "xhci"
+
+static int hc_driver_init(hcd_t *, const hw_res_list_parsed_t *, ddf_dev_t *);
+static int hcd_irq_code_gen(irq_code_t *, hcd_t *, const hw_res_list_parsed_t *);
+static int hcd_claim(hcd_t *, ddf_dev_t *);
+static int hcd_start(hcd_t *, bool);
+static int hcd_status(hcd_t *, uint32_t *);
+static void hcd_interrupt(hcd_t *, uint32_t);
+static int hcd_schedule(hcd_t *, usb_transfer_batch_t *);
+static void hc_driver_fini(hcd_t *);
+
+static const ddf_hc_driver_t xhci_ddf_hc_driver = {
+	.name = "XHCI-PCI",
+	.init = hc_driver_init,
+	.irq_code_gen = hcd_irq_code_gen,
+	.claim = hcd_claim,
+	.start = hcd_start,
+	.setup_root_hub = NULL,
+	.fini = hc_driver_fini,
+	.ops = {
+		.schedule       = hcd_schedule,
+		.irq_hook       = hcd_interrupt,
+		.status_hook    = hcd_status,
+	}
+};
+
+static int hc_driver_init(hcd_t *hcd, const hw_res_list_parsed_t *hw_res, ddf_dev_t *device)
+{
+	int err;
+
+	xhci_hc_t *hc = malloc(sizeof(xhci_hc_t));
+	if (!hc)
+		return ENOMEM;
+
+	if ((err = hc_init_mmio(hc, hw_res)))
+		goto err;
+
+	if ((err = hc_init_memory(hc, device)))
+		goto err;
+
+	hcd_set_implementation(hcd, hc, &xhci_ddf_hc_driver.ops, &hc->bus.base);
+	hc->hcd = hcd;
+
+	return EOK;
+err:
+	free(hc);
+	return err;
+}
+
+static int hcd_irq_code_gen(irq_code_t *code, hcd_t *hcd, const hw_res_list_parsed_t *hw_res)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	return hc_irq_code_gen(code, hc, hw_res);
+}
+
+static int hcd_claim(hcd_t *hcd, ddf_dev_t *dev)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	return hc_claim(hc, dev);
+}
+
+static int hcd_start(hcd_t *hcd, bool irq)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	return hc_start(hc, irq);
+}
+
+static int hcd_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	return hc_schedule(hc, batch);
+}
+
+static int hcd_status(hcd_t *hcd, uint32_t *status)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+	assert(status);
+
+	return hc_status(hc, status);
+}
+
+static void hcd_interrupt(hcd_t *hcd, uint32_t status)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	hc_interrupt(hc, status);
+}
+
+static void hc_driver_fini(hcd_t *hcd)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	hc_fini(hc);
+
+	free(hc);
+}
+
+/** Initializes a new ddf driver instance of XHCI hcd.
+ *
+ * @param[in] device DDF instance of the device to initialize.
+ * @return Error code.
+ */
+static int xhci_dev_add(ddf_dev_t *device)
+{
+	usb_log_info("Adding device %s", ddf_dev_get_name(device));
+	return hcd_ddf_add_hc(device, &xhci_ddf_hc_driver);
+}
+
+static int xhci_fun_online(ddf_fun_t *fun)
+{
+	return hcd_ddf_device_online(fun);
+}
+
+static int xhci_fun_offline(ddf_fun_t *fun)
+{
+	return hcd_ddf_device_offline(fun);
+}
+
+
+static const driver_ops_t xhci_driver_ops = {
+	.dev_add = xhci_dev_add,
+	.fun_online = xhci_fun_online,
+	.fun_offline = xhci_fun_offline
+};
+
+static const driver_t xhci_driver = {
+	.name = NAME,
+	.driver_ops = &xhci_driver_ops
+};
+
+
+/** Initializes global driver structures (NONE).
+ *
+ * @param[in] argc Nmber of arguments in argv vector (ignored).
+ * @param[in] argv Cmdline argument vector (ignored).
+ * @return Error code.
+ *
+ * Driver debug level is set here.
+ */
+int main(int argc, char *argv[])
+{
+	log_init(NAME);
+	logctl_set_log_level(NAME, LVL_DEBUG2);
+	return ddf_driver_main(&xhci_driver);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/rh.c
===================================================================
--- uspace/drv/bus/usb/xhci/rh.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/rh.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,367 @@
+/*
+ * 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 The roothub structures abstraction.
+ */
+
+#include <errno.h>
+#include <str_error.h>
+#include <usb/request.h>
+#include <usb/debug.h>
+#include <usb/host/utils/malloc32.h>
+#include <usb/host/bus.h>
+#include <usb/host/ddf_helpers.h>
+#include <usb/host/hcd.h>
+
+#include "debug.h"
+#include "commands.h"
+#include "endpoint.h"
+#include "hc.h"
+#include "hw_struct/trb.h"
+#include "rh.h"
+#include "transfers.h"
+
+/* This mask only lists registers, which imply port change. */
+static const uint32_t port_change_mask =
+	XHCI_REG_MASK(XHCI_PORT_CSC) |
+	XHCI_REG_MASK(XHCI_PORT_PEC) |
+	XHCI_REG_MASK(XHCI_PORT_WRC) |
+	XHCI_REG_MASK(XHCI_PORT_OCC) |
+	XHCI_REG_MASK(XHCI_PORT_PRC) |
+	XHCI_REG_MASK(XHCI_PORT_PLC) |
+	XHCI_REG_MASK(XHCI_PORT_CEC);
+
+int xhci_rh_init(xhci_rh_t *rh, xhci_hc_t *hc, ddf_dev_t *device)
+{
+	assert(rh);
+	assert(hc);
+
+	rh->hc = hc;
+	rh->max_ports = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PORTS);
+	rh->devices_by_port = (xhci_device_t **) calloc(rh->max_ports, sizeof(xhci_device_t *));
+	rh->hc_device = device;
+
+	const int err = device_init(&rh->device.base);
+	if (err)
+		return err;
+
+	/* Initialize route string */
+	rh->device.route_str = 0;
+	rh->device.tier = 0;
+
+	return EOK;
+}
+
+/** Create a device node for device directly connected to RH.
+ */
+static int rh_setup_device(xhci_rh_t *rh, uint8_t port_id)
+{
+	int err;
+	assert(rh);
+	assert(rh->hc_device);
+
+	assert(rh->devices_by_port[port_id - 1] == NULL);
+
+	xhci_bus_t *bus = &rh->hc->bus;
+
+	device_t *dev = hcd_ddf_device_create(rh->hc_device, bus->base.device_size);
+	if (!dev) {
+		usb_log_error("Failed to create USB device function.");
+		return ENOMEM;
+	}
+
+	const xhci_port_speed_t *port_speed = xhci_rh_get_port_speed(rh, port_id);
+	xhci_device_t *xhci_dev = xhci_device_get(dev);
+	xhci_dev->usb3 = port_speed->major == 3;
+	xhci_dev->rh_port = port_id;
+
+	dev->hub = &rh->device.base;
+	dev->port = port_id;
+	dev->speed = port_speed->usb_speed;
+
+	if ((err = xhci_bus_enumerate_device(bus, rh->hc, dev))) {
+		usb_log_error("Failed to enumerate USB device: %s", str_error(err));
+		return err;
+	}
+
+	if (!ddf_fun_get_name(dev->fun)) {
+		device_set_default_name(dev);
+	}
+
+	if ((err = ddf_fun_bind(dev->fun))) {
+		usb_log_error("Failed to register device " XHCI_DEV_FMT " DDF function: %s.",
+		    XHCI_DEV_ARGS(*xhci_dev), str_error(err));
+		goto err_usb_dev;
+	}
+
+	fibril_mutex_lock(&rh->device.base.guard);
+	list_append(&dev->link, &rh->device.base.devices);
+	rh->devices_by_port[port_id - 1] = xhci_dev;
+	fibril_mutex_unlock(&rh->device.base.guard);
+
+	return EOK;
+
+err_usb_dev:
+	hcd_ddf_device_destroy(dev);
+	return err;
+}
+
+static int handle_connected_device(xhci_rh_t *rh, uint8_t port_id)
+{
+	xhci_port_regs_t *regs = &rh->hc->op_regs->portrs[port_id - 1];
+
+	uint8_t link_state = XHCI_REG_RD(regs, XHCI_PORT_PLS);
+	const xhci_port_speed_t *speed = xhci_rh_get_port_speed(rh, port_id);
+
+	usb_log_info("Detected new %.4s%u.%u device on port %u.", speed->name, speed->major, speed->minor, port_id);
+
+	if (speed->major == 3) {
+		if (link_state == 0) {
+			/* USB3 is automatically advanced to enabled. */
+			return rh_setup_device(rh, port_id);
+		}
+		else if (link_state == 5) {
+			/* USB 3 failed to enable. */
+			usb_log_error("USB 3 port couldn't be enabled.");
+			return EAGAIN;
+		}
+		else {
+			usb_log_error("USB 3 port is in invalid state %u.", link_state);
+			return EINVAL;
+		}
+	}
+	else {
+		usb_log_debug("USB 2 device attached, issuing reset.");
+		xhci_rh_reset_port(rh, port_id);
+		/*
+			FIXME: we need to wait for the event triggered by the reset
+			and then alloc_dev()... can't it be done directly instead of
+			going around?
+		*/
+		return EOK;
+	}
+}
+
+/** Deal with a detached device.
+ */
+static int handle_disconnected_device(xhci_rh_t *rh, uint8_t port_id)
+{
+	assert(rh);
+	int err;
+
+	/* Find XHCI device by the port. */
+	xhci_device_t *dev = rh->devices_by_port[port_id - 1];
+	if (!dev) {
+		/* Must be extraneous call. */
+		return EOK;
+	}
+
+	usb_log_info("Device " XHCI_DEV_FMT " at port %u has been disconnected.",
+	    XHCI_DEV_ARGS(*dev), port_id);
+
+	/* Mark the device as detached. */
+	fibril_mutex_lock(&rh->device.base.guard);
+	list_remove(&dev->base.link);
+	rh->devices_by_port[port_id - 1] = NULL;
+	fibril_mutex_unlock(&rh->device.base.guard);
+
+	/* Remove device from XHCI bus. */
+	if ((err = xhci_bus_remove_device(&rh->hc->bus, rh->hc, &dev->base))) {
+		usb_log_warning("Failed to remove device " XHCI_DEV_FMT " from XHCI bus: %s",
+		    XHCI_DEV_ARGS(*dev), str_error(err));
+	}
+
+	return EOK;
+}
+
+/** Handle an incoming Port Change Detected Event.
+ */
+int xhci_rh_handle_port_status_change_event(xhci_hc_t *hc, xhci_trb_t *trb)
+{
+	uint8_t port_id = XHCI_QWORD_EXTRACT(trb->parameter, 31, 24);
+	usb_log_debug("Port status change event detected for port %u.", port_id);
+
+	/**
+	 * We can't be sure that the port change this event announces is the
+	 * only port change that happened (see section 4.19.2 of the xHCI
+	 * specification). Therefore, we just check all ports for changes.
+	 */
+	xhci_rh_handle_port_change(&hc->rh);
+
+	return EOK;
+}
+
+void xhci_rh_handle_port_change(xhci_rh_t *rh)
+{
+	for (uint8_t i = 1; i <= rh->max_ports; ++i) {
+		xhci_port_regs_t *regs = &rh->hc->op_regs->portrs[i - 1];
+
+		uint32_t events = XHCI_REG_RD_FIELD(&regs->portsc, 32);
+		XHCI_REG_WR_FIELD(&regs->portsc, events, 32);
+
+		events &= port_change_mask;
+
+		if (events & XHCI_REG_MASK(XHCI_PORT_CSC)) {
+			usb_log_info("Connected state changed on port %u.", i);
+			events &= ~XHCI_REG_MASK(XHCI_PORT_CSC);
+
+			bool connected = XHCI_REG_RD(regs, XHCI_PORT_CCS);
+			if (connected) {
+				handle_connected_device(rh, i);
+			} else {
+				handle_disconnected_device(rh, i);
+			}
+		}
+
+		if (events & XHCI_REG_MASK(XHCI_PORT_PEC)) {
+			usb_log_info("Port enabled changed on port %u.", i);
+			events &= ~XHCI_REG_MASK(XHCI_PORT_PEC);
+		}
+
+		if (events & XHCI_REG_MASK(XHCI_PORT_WRC)) {
+			usb_log_info("Warm port reset on port %u completed.", i);
+			events &= ~XHCI_REG_MASK(XHCI_PORT_WRC);
+		}
+
+		if (events & XHCI_REG_MASK(XHCI_PORT_OCC)) {
+			usb_log_info("Over-current change on port %u.", i);
+			events &= ~XHCI_REG_MASK(XHCI_PORT_OCC);
+		}
+
+		if (events & XHCI_REG_MASK(XHCI_PORT_PRC)) {
+			usb_log_info("Port reset on port %u completed.", i);
+			events &= ~XHCI_REG_MASK(XHCI_PORT_PRC);
+
+			const xhci_port_speed_t *speed = xhci_rh_get_port_speed(rh, i);
+			if (speed->major != 3) {
+				/* FIXME: We probably don't want to do this
+				 * every time USB2 port is reset. This is a
+				 * temporary workaround. */
+				rh_setup_device(rh, i);
+			}
+		}
+
+		if (events & XHCI_REG_MASK(XHCI_PORT_PLC)) {
+			usb_log_info("Port link state changed on port %u.", i);
+			events &= ~XHCI_REG_MASK(XHCI_PORT_PLC);
+		}
+
+		if (events & XHCI_REG_MASK(XHCI_PORT_CEC)) {
+			usb_log_info("Port %u failed to configure link.", i);
+			events &= ~XHCI_REG_MASK(XHCI_PORT_CEC);
+		}
+
+		if (events) {
+			usb_log_warning("Port change (0x%08x) ignored on port %u.", events, i);
+		}
+	}
+
+	/**
+	 * Theory:
+	 *
+	 * Although more events could have happened while processing, the PCD
+	 * bit in USBSTS will be set on every change. Because the PCD is
+	 * cleared even before the interrupt is cleared, it is safe to assume
+	 * that this handler will be called again.
+	 *
+	 * But because we could have handled the event in previous run of this
+	 * handler, it is not an error when no event is detected.
+	 *
+	 * Reality:
+	 *
+	 * The PCD bit is never set. TODO Check why the interrupt never carries
+	 * the PCD flag. Possibly repeat the checking until we're sure the
+	 * PSCEG is 0 - check section 4.19.2 of the xHCI spec.
+	 */
+}
+
+static inline int get_hub_available_bandwidth(xhci_hc_t *hc, xhci_device_t* dev, uint8_t speed, xhci_port_bandwidth_ctx_t *ctx) {
+	// TODO: find a correct place for this function + API
+	// We need speed, because a root hub device has both USB 2 and USB 3 speeds
+	// and the command can query only one of them
+	// ctx is an out parameter as of now
+	assert(dev);
+	assert(ctx);
+
+	xhci_port_bandwidth_ctx_t *in_ctx = malloc32(sizeof(xhci_port_bandwidth_ctx_t));
+	if (!in_ctx) {
+		return ENOMEM;
+	}
+
+	xhci_cmd_t cmd;
+	xhci_cmd_init(&cmd, XHCI_CMD_GET_PORT_BANDWIDTH);
+
+	cmd.bandwidth_ctx = in_ctx;
+	cmd.device_speed = speed;
+
+	int err;
+	if ((err = xhci_cmd_sync(hc, &cmd))) {
+		goto end;
+	}
+
+	memcpy(ctx, in_ctx, sizeof(xhci_port_bandwidth_ctx_t));
+
+end:
+	xhci_cmd_fini(&cmd);
+	return EOK;
+}
+
+const xhci_port_speed_t *xhci_rh_get_port_speed(xhci_rh_t *rh, uint8_t port)
+{
+	xhci_port_regs_t *port_regs = &rh->hc->op_regs->portrs[port - 1];
+
+	unsigned psiv = XHCI_REG_RD(port_regs, XHCI_PORT_PS);
+	return &rh->hc->speeds[psiv];
+}
+
+int xhci_rh_reset_port(xhci_rh_t* rh, uint8_t port)
+{
+	usb_log_debug2("Resetting port %u.", port);
+	xhci_port_regs_t *regs = &rh->hc->op_regs->portrs[port-1];
+	XHCI_REG_SET(regs, XHCI_PORT_PR, 1);
+
+	return EOK;
+}
+
+int xhci_rh_fini(xhci_rh_t *rh)
+{
+	/* TODO: Implement me! */
+	usb_log_debug2("Called xhci_rh_fini().");
+
+	free(rh->devices_by_port);
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/rh.h
===================================================================
--- uspace/drv/bus/usb/xhci/rh.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/rh.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,96 @@
+/*
+ * 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 The roothub structures abstraction.
+ */
+
+#ifndef XHCI_RH_H
+#define XHCI_RH_H
+
+#include <usb/host/usb_transfer_batch.h>
+#include <usb/host/bus.h>
+
+#include "hw_struct/regs.h"
+#include "endpoint.h"
+
+typedef struct xhci_hc xhci_hc_t;
+typedef struct ddf_dev ddf_dev_t;
+
+/**
+ * xHCI lets the controller define speeds of ports it controls.
+ */
+typedef struct xhci_port_speed {
+	char name [4];
+	uint8_t major, minor;
+	uint64_t rx_bps, tx_bps;
+	usb_speed_t usb_speed;
+} xhci_port_speed_t;
+
+typedef struct hcd_roothub hcd_roothub_t;
+typedef struct xhci_bus xhci_bus_t;
+
+/* XHCI root hub instance */
+typedef struct {
+	/** Host controller */
+	xhci_hc_t *hc;
+
+	/* Root for the device tree */
+	xhci_device_t device;
+
+	/* We need this to attach children to */
+	ddf_dev_t *hc_device;
+
+	/** Interrupt transfer waiting for an actual interrupt to occur */
+	usb_transfer_batch_t *unfinished_interrupt_transfer;
+
+	/* Number of hub ports. */
+	uint8_t max_ports;
+
+	/* Device pointers connected to RH ports or NULL. (size is `max_ports`) */
+	xhci_device_t **devices_by_port;
+} xhci_rh_t;
+
+int xhci_rh_init(xhci_rh_t *, xhci_hc_t *, ddf_dev_t *);
+int xhci_rh_fini(xhci_rh_t *);
+const xhci_port_speed_t *xhci_rh_get_port_speed(xhci_rh_t *, uint8_t);
+int xhci_rh_reset_port(xhci_rh_t *, uint8_t);
+
+int xhci_rh_handle_port_status_change_event(xhci_hc_t *, xhci_trb_t *);
+void xhci_rh_handle_port_change(xhci_rh_t *);
+
+int xhci_rh_address_device(xhci_rh_t *rh, device_t *dev, xhci_bus_t *bus);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/scratchpad.c
===================================================================
--- uspace/drv/bus/usb/xhci/scratchpad.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/scratchpad.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2017 Jaroslav Jindrak
+ * 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
+ * Scratchpad buffer array bookkeeping.
+ */
+
+#include <errno.h>
+#include <usb/debug.h>
+#include <usb/host/utils/malloc32.h>
+#include "hc.h"
+#include "hw_struct/regs.h"
+#include "scratchpad.h"
+
+static inline unsigned xhci_scratchpad_count(xhci_hc_t *hc)
+{
+	unsigned lo, hi;
+
+	lo = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_SPBUF_LO);
+	hi = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_SPBUF_HI);
+
+	return (hi << 5) | lo;
+}
+
+int xhci_scratchpad_alloc(xhci_hc_t *hc)
+{
+	unsigned num_bufs, allocated;
+	xhci_scratchpad_t *bufs;
+
+	num_bufs = xhci_scratchpad_count(hc);
+
+	if (!num_bufs)
+		return EOK;
+
+	bufs = malloc32(sizeof(xhci_scratchpad_t));
+	if (!bufs)
+		return ENOMEM;
+
+	allocated = 0;
+
+	uint64_t *phys_array = malloc32(num_bufs * sizeof(uint64_t));
+	if (phys_array == NULL)
+		goto err_phys_array;
+
+	uint64_t *virt_array = malloc32(num_bufs * sizeof(uint64_t));
+	if (virt_array == NULL)
+		goto err_virt_array;
+
+	for (unsigned i = 0; i < num_bufs; ++i) {
+		void *buf = malloc32(PAGE_SIZE);
+		phys_array[i] = host2xhci(64, (uint64_t) addr_to_phys(buf));
+		virt_array[i] = (uint64_t) buf;
+
+		if (buf != NULL)
+			++allocated;
+		else
+			goto err_page_alloc;
+
+		memset(buf, 0, PAGE_SIZE);
+	}
+
+	bufs->phys_ptr = host2xhci(64, (uint64_t) addr_to_phys(phys_array));
+	bufs->virt_ptr = (uint64_t) virt_array;
+	bufs->phys_bck = (uint64_t) phys_array;
+
+	hc->dcbaa[0] = bufs->phys_ptr;
+	hc->scratchpad = bufs;
+
+	usb_log_debug2("Allocated %d scratchpad buffers.", num_bufs);
+
+	return EOK;
+
+err_page_alloc:
+	for (unsigned i = 0; i < allocated; ++i)
+		free32((void *) virt_array[i]);
+	free32(virt_array);
+
+err_virt_array:
+	free32(phys_array);
+
+err_phys_array:
+	free32(bufs);
+
+	return ENOMEM;
+}
+
+void xhci_scratchpad_free(xhci_hc_t *hc)
+{
+	unsigned num_bufs;
+	xhci_scratchpad_t *scratchpad;
+	uint64_t *virt_array;
+
+	num_bufs = xhci_scratchpad_count(hc);
+	if (!num_bufs)
+		return;
+
+	scratchpad =  hc->scratchpad;
+	virt_array = (uint64_t *) scratchpad->virt_ptr;
+
+	for (unsigned i = 0; i < num_bufs; ++i)
+		free32((void *) virt_array[i]);
+	free32((void *) scratchpad->virt_ptr);
+	free32((void *) scratchpad->phys_bck);
+
+	hc->dcbaa[0] = 0;
+
+	return;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/scratchpad.h
===================================================================
--- uspace/drv/bus/usb/xhci/scratchpad.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/scratchpad.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017 Jaroslav Jindrak
+ * 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
+ * Scratchpad buffers are PAGE_SIZE sized page boundary aligned buffers
+ * that are free to use by the xHC.
+ *
+ * This file provides means of allocation and deallocation of these
+ * buffers.
+ */
+
+#ifndef XHCI_SCRATCHPAD_H
+#define XHCI_SCRATCHPAD_H
+
+typedef struct xhci_hc xhci_hc_t;
+
+typedef struct xhci_scratchpad {
+	/* Pointers to scratchpad buffers used by the xHC. */
+	uint64_t phys_ptr;
+	/* Pointers to scratchpad buffers used for deallocation. */
+	uint64_t virt_ptr;
+	/* Pointers to the scratchpad array used for deallocation. */
+	uint64_t phys_bck;
+} xhci_scratchpad_t;
+
+int xhci_scratchpad_alloc(xhci_hc_t *);
+void xhci_scratchpad_free(xhci_hc_t *);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/test/reg-ops.c
===================================================================
--- uspace/drv/bus/usb/xhci/test/reg-ops.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/test/reg-ops.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,181 @@
+#include <pcut/pcut.h>
+#include "../hw_struct/regs.h"
+
+PCUT_INIT
+
+static struct {
+	uint32_t field32;
+	uint16_t field16;
+	uint8_t field8;
+} regs[1];
+
+#define REG_8_FLAG    field8,  8,  FLAG,  3
+#define REG_8_RANGE   field8,  8, RANGE,  6, 2
+#define REG_8_FIELD   field8,  8, FIELD
+#define REG_16_FLAG  field16, 16,  FLAG,  8
+#define REG_16_RANGE field16, 16, RANGE, 11, 4
+#define REG_16_FIELD field16, 16, FIELD
+#define REG_32_FLAG  field32, 32,  FLAG, 16
+#define REG_32_RANGE field32, 32, RANGE, 23, 8
+#define REG_32_FIELD field32, 32, FIELD
+
+#define RESET memset(regs, 0, sizeof(regs[0]))
+#define EQ(exp, act) PCUT_ASSERT_INT_EQUALS((exp), (act))
+
+PCUT_TEST(ops_8_field) {
+	RESET;
+	EQ(0, XHCI_REG_RD(regs, REG_8_FIELD));
+
+	XHCI_REG_WR(regs, REG_8_FIELD, 0x55);
+	EQ(0x55, XHCI_REG_RD(regs, REG_8_FIELD));
+	EQ(0x55, regs->field8);
+
+	RESET;
+	XHCI_REG_SET(regs, REG_8_FIELD, 0x55);
+	EQ(0x55, XHCI_REG_RD(regs, REG_8_FIELD));
+	EQ(0x55, regs->field8);
+
+	XHCI_REG_CLR(regs, REG_8_FIELD, 0x5);
+	EQ(0x50, XHCI_REG_RD(regs, REG_8_FIELD));
+	EQ(0x50, regs->field8);
+}
+
+PCUT_TEST(ops_8_range) {
+	RESET;
+	EQ(0, XHCI_REG_RD(regs, REG_8_RANGE));
+
+	XHCI_REG_WR(regs, REG_8_RANGE, 0x55);
+	EQ(0x15, XHCI_REG_RD(regs, REG_8_RANGE));
+	EQ(0x54, regs->field8);
+
+	XHCI_REG_SET(regs, REG_8_RANGE, 0x2);
+	EQ(0x17, XHCI_REG_RD(regs, REG_8_RANGE));
+	EQ(0x5c, regs->field8);
+
+	XHCI_REG_CLR(regs, REG_8_RANGE, 0x2);
+	EQ(0x15, XHCI_REG_RD(regs, REG_8_RANGE));
+	EQ(0x54, regs->field8);
+}
+
+PCUT_TEST(ops_8_flag) {
+	RESET;
+	EQ(0, XHCI_REG_RD(regs, REG_8_FLAG));
+
+	XHCI_REG_WR(regs, REG_8_FLAG, 1);
+	EQ(1, XHCI_REG_RD(regs, REG_8_FLAG));
+	EQ(8, regs->field8);
+
+	RESET;
+	XHCI_REG_SET(regs, REG_8_FLAG, 1);
+	EQ(1, XHCI_REG_RD(regs, REG_8_FLAG));
+	EQ(8, regs->field8);
+
+	XHCI_REG_CLR(regs, REG_8_FLAG, 1);
+	EQ(0, XHCI_REG_RD(regs, REG_8_FLAG));
+	EQ(0, regs->field8);
+}
+
+PCUT_TEST(ops_16_field) {
+	RESET;
+	EQ(0, XHCI_REG_RD(regs, REG_16_FIELD));
+
+	XHCI_REG_WR(regs, REG_16_FIELD, 0x5555);
+	EQ(0x5555, XHCI_REG_RD(regs, REG_16_FIELD));
+	EQ(0x5555, xhci2host(16, regs->field16));
+
+	XHCI_REG_SET(regs, REG_16_FIELD, 0x00aa);
+	EQ(0x55ff, XHCI_REG_RD(regs, REG_16_FIELD));
+	EQ(0x55ff, xhci2host(16, regs->field16));
+
+	XHCI_REG_CLR(regs, REG_16_FIELD, 0x055a);
+	EQ(0x50a5, XHCI_REG_RD(regs, REG_16_FIELD));
+	EQ(0x50a5, xhci2host(16, regs->field16));
+}
+
+PCUT_TEST(ops_16_range) {
+	RESET;
+	EQ(0, XHCI_REG_RD(regs, REG_16_RANGE));
+
+	XHCI_REG_WR(regs, REG_16_RANGE, 0x5a5a);
+	EQ(0x5a, XHCI_REG_RD(regs, REG_16_RANGE));
+	EQ(0x05a0, xhci2host(16, regs->field16));
+
+	XHCI_REG_SET(regs, REG_16_RANGE, 0xa5);
+	EQ(0xff, XHCI_REG_RD(regs, REG_16_RANGE));
+	EQ(0x0ff0, xhci2host(16, regs->field16));
+
+	XHCI_REG_CLR(regs, REG_16_RANGE, 0x5a);
+	EQ(0xa5, XHCI_REG_RD(regs, REG_16_RANGE));
+	EQ(0x0a50, xhci2host(16, regs->field16));
+}
+
+PCUT_TEST(ops_16_flag) {
+	RESET;
+	EQ(0, XHCI_REG_RD(regs, REG_16_FLAG));
+
+	XHCI_REG_WR(regs, REG_16_FLAG, 1);
+	EQ(1, XHCI_REG_RD(regs, REG_16_FLAG));
+	EQ(0x100, xhci2host(16, regs->field16));
+
+	RESET;
+	XHCI_REG_SET(regs, REG_16_FLAG, 1);
+	EQ(1, XHCI_REG_RD(regs, REG_16_FLAG));
+	EQ(0x100, xhci2host(16, regs->field16));
+
+	XHCI_REG_CLR(regs, REG_16_FLAG, 1);
+	EQ(0, XHCI_REG_RD(regs, REG_16_FLAG));
+	EQ(0, xhci2host(16, regs->field16));
+}
+
+PCUT_TEST(ops_32_field) {
+	RESET;
+	EQ(0, XHCI_REG_RD(regs, REG_32_FIELD));
+
+	XHCI_REG_WR(regs, REG_32_FIELD, 0xffaa5500);
+	EQ(0xffaa5500, XHCI_REG_RD(regs, REG_32_FIELD));
+	EQ(0xffaa5500, xhci2host(32, regs->field32));
+
+	XHCI_REG_SET(regs, REG_32_FIELD, 0x0055aa00);
+	EQ(0xffffff00, XHCI_REG_RD(regs, REG_32_FIELD));
+	EQ(0xffffff00, xhci2host(32, regs->field32));
+
+	XHCI_REG_CLR(regs, REG_32_FIELD, 0x00aa55ff);
+	EQ(0xff55aa00, XHCI_REG_RD(regs, REG_32_FIELD));
+	EQ(0xff55aa00, xhci2host(32, regs->field32));
+}
+
+PCUT_TEST(ops_32_range) {
+	RESET;
+	EQ(0, XHCI_REG_RD(regs, REG_32_RANGE));
+
+	XHCI_REG_WR(regs, REG_32_RANGE, 0xff5a0);
+	EQ(0xf5a0, XHCI_REG_RD(regs, REG_32_RANGE));
+	EQ(0x00f5a000, xhci2host(32, regs->field32));
+
+	XHCI_REG_SET(regs, REG_32_RANGE, 0xffa50);
+	EQ(0xfff0, XHCI_REG_RD(regs, REG_32_RANGE));
+	EQ(0x00fff000, xhci2host(32, regs->field32));
+
+	XHCI_REG_CLR(regs, REG_32_RANGE, 0xf05af);
+	EQ(0xfa50, XHCI_REG_RD(regs, REG_32_RANGE));
+	EQ(0x00fa5000, xhci2host(32, regs->field32));
+}
+
+PCUT_TEST(ops_32_flag) {
+	RESET;
+	EQ(0, XHCI_REG_RD(regs, REG_32_FLAG));
+
+	XHCI_REG_WR(regs, REG_32_FLAG, 1);
+	EQ(1, XHCI_REG_RD(regs, REG_32_FLAG));
+	EQ(0x10000, xhci2host(32, regs->field32));
+
+	RESET;
+	XHCI_REG_SET(regs, REG_32_FLAG, 1);
+	EQ(1, XHCI_REG_RD(regs, REG_32_FLAG));
+	EQ(0x10000, xhci2host(32, regs->field32));
+
+	XHCI_REG_CLR(regs, REG_32_FLAG, 1);
+	EQ(0, XHCI_REG_RD(regs, REG_32_FLAG));
+	EQ(0, xhci2host(32, regs->field32));
+}
+PCUT_MAIN();
Index: uspace/drv/bus/usb/xhci/transfers.c
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,353 @@
+/*
+ * 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 The host controller transfer ring management
+ */
+
+#include <usb/host/utils/malloc32.h>
+#include <usb/debug.h>
+#include <usb/request.h>
+#include "endpoint.h"
+#include "hc.h"
+#include "hw_struct/trb.h"
+#include "transfers.h"
+#include "trb_ring.h"
+
+typedef enum {
+    STAGE_OUT,
+    STAGE_IN,
+} stage_dir_flag_t;
+
+#define REQUEST_TYPE_DTD (0x80)
+#define REQUEST_TYPE_IS_DEVICE_TO_HOST(rq) ((rq) & REQUEST_TYPE_DTD)
+
+
+/** Get direction flag of data stage.
+ *  See Table 7 of xHCI specification.
+ */
+static inline stage_dir_flag_t get_status_direction_flag(xhci_trb_t* trb,
+	uint8_t bmRequestType, uint16_t wLength)
+{
+	/* See Table 7 of xHCI specification */
+	return REQUEST_TYPE_IS_DEVICE_TO_HOST(bmRequestType) && (wLength > 0)
+		? STAGE_OUT
+		: STAGE_IN;
+}
+
+typedef enum {
+    DATA_STAGE_NO = 0,
+    DATA_STAGE_OUT = 2,
+    DATA_STAGE_IN = 3,
+} data_stage_type_t;
+
+/** Get transfer type flag.
+ *  See Table 8 of xHCI specification.
+ */
+static inline data_stage_type_t get_transfer_type(xhci_trb_t* trb, uint8_t
+	bmRequestType, uint16_t wLength)
+{
+	if (wLength == 0)
+		return DATA_STAGE_NO;
+
+	/* See Table 7 of xHCI specification */
+	return REQUEST_TYPE_IS_DEVICE_TO_HOST(bmRequestType)
+		? DATA_STAGE_IN
+		: DATA_STAGE_NO;
+}
+
+static inline bool configure_endpoint_needed(usb_device_request_setup_packet_t *setup)
+{
+	usb_request_type_t request_type = SETUP_REQUEST_TYPE_GET_TYPE(setup->request_type);
+
+	return request_type == USB_REQUEST_TYPE_STANDARD &&
+		(setup->request == USB_DEVREQ_SET_CONFIGURATION
+		|| setup->request == USB_DEVREQ_SET_INTERFACE);
+}
+
+/**
+ * There can currently be only one active transfer, because
+ * usb_transfer_batch_init locks the endpoint by endpoint_use.
+ * Therefore, we store the only active transfer per endpoint there.
+ */
+xhci_transfer_t* xhci_transfer_create(endpoint_t* ep)
+{
+	xhci_transfer_t *transfer = calloc(1, sizeof(xhci_transfer_t));
+	if (!transfer)
+		return NULL;
+
+	usb_transfer_batch_init(&transfer->batch, ep);
+	return transfer;
+}
+
+void xhci_transfer_destroy(xhci_transfer_t* transfer)
+{
+	assert(transfer);
+
+	if (transfer->hc_buffer)
+		free32(transfer->hc_buffer);
+}
+
+static xhci_trb_ring_t *get_ring(xhci_hc_t *hc, xhci_transfer_t *transfer)
+{
+	return &xhci_endpoint_get(transfer->batch.ep)->ring;
+}
+
+static int schedule_control(xhci_hc_t* hc, xhci_transfer_t* transfer)
+{
+	usb_transfer_batch_t *batch = &transfer->batch;
+	xhci_trb_ring_t *ring = get_ring(hc, transfer);
+	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(transfer->batch.ep);
+
+	usb_device_request_setup_packet_t* setup = &batch->setup.packet;
+
+	xhci_trb_t trbs[3];
+	int trbs_used = 0;
+
+	xhci_trb_t *trb_setup = trbs + trbs_used++;
+	xhci_trb_clean(trb_setup);
+
+	TRB_CTRL_SET_SETUP_WVALUE(*trb_setup, setup->value);
+	TRB_CTRL_SET_SETUP_WLENGTH(*trb_setup, setup->length);
+	TRB_CTRL_SET_SETUP_WINDEX(*trb_setup, setup->index);
+	TRB_CTRL_SET_SETUP_BREQ(*trb_setup, setup->request);
+	TRB_CTRL_SET_SETUP_BMREQTYPE(*trb_setup, setup->request_type);
+
+	/* Size of the setup packet is always 8 */
+	TRB_CTRL_SET_XFER_LEN(*trb_setup, 8);
+
+	/* Immediate data */
+	TRB_CTRL_SET_IDT(*trb_setup, 1);
+	TRB_CTRL_SET_TRB_TYPE(*trb_setup, XHCI_TRB_TYPE_SETUP_STAGE);
+	TRB_CTRL_SET_TRT(*trb_setup, get_transfer_type(trb_setup, setup->request_type, setup->length));
+
+	/* Data stage */
+	xhci_trb_t *trb_data = NULL;
+	if (setup->length > 0) {
+		trb_data = trbs + trbs_used++;
+		xhci_trb_clean(trb_data);
+
+		trb_data->parameter = addr_to_phys(transfer->hc_buffer);
+
+		// data size (sent for OUT, or buffer size)
+		TRB_CTRL_SET_XFER_LEN(*trb_data, batch->buffer_size);
+		// FIXME: TD size 4.11.2.4
+		TRB_CTRL_SET_TD_SIZE(*trb_data, 1);
+
+		// Some more fields here, no idea what they mean
+		TRB_CTRL_SET_TRB_TYPE(*trb_data, XHCI_TRB_TYPE_DATA_STAGE);
+
+		int stage_dir = REQUEST_TYPE_IS_DEVICE_TO_HOST(setup->request_type)
+					? STAGE_IN : STAGE_OUT;
+		TRB_CTRL_SET_DIR(*trb_data, stage_dir);
+	}
+
+	/* Status stage */
+	xhci_trb_t *trb_status = trbs + trbs_used++;
+	xhci_trb_clean(trb_status);
+
+	// FIXME: Evaluate next TRB? 4.12.3
+	// TRB_CTRL_SET_ENT(*trb_status, 1);
+
+	TRB_CTRL_SET_IOC(*trb_status, 1);
+	TRB_CTRL_SET_TRB_TYPE(*trb_status, XHCI_TRB_TYPE_STATUS_STAGE);
+	TRB_CTRL_SET_DIR(*trb_status, get_status_direction_flag(trb_setup, setup->request_type, setup->length));
+
+	// Issue a Configure Endpoint command, if needed.
+	if (configure_endpoint_needed(setup)) {
+		const int err = hc_configure_device(hc, xhci_ep_to_dev(xhci_ep)->slot_id);
+		if (err)
+			return err;
+	}
+
+	return xhci_trb_ring_enqueue_multiple(ring, trbs, trbs_used, &transfer->interrupt_trb_phys);
+}
+
+static int schedule_bulk(xhci_hc_t* hc, xhci_transfer_t *transfer)
+{
+	xhci_trb_t trb;
+	xhci_trb_clean(&trb);
+	trb.parameter = addr_to_phys(transfer->hc_buffer);
+
+	// 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);
+}
+
+static int schedule_interrupt(xhci_hc_t* hc, xhci_transfer_t* transfer)
+{
+	xhci_trb_t trb;
+	xhci_trb_clean(&trb);
+	trb.parameter = addr_to_phys(transfer->hc_buffer);
+
+	// 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);
+}
+
+static int schedule_isochronous(xhci_hc_t* hc, xhci_transfer_t* transfer)
+{
+	/* TODO: Implement me. */
+	usb_log_error("Isochronous transfers are not yet implemented!");
+	return ENOTSUP;
+}
+
+int xhci_handle_transfer_event(xhci_hc_t* hc, xhci_trb_t* trb)
+{
+	uintptr_t addr = trb->parameter;
+	const unsigned slot_id = XHCI_DWORD_EXTRACT(trb->control, 31, 24);
+	const unsigned ep_dci = XHCI_DWORD_EXTRACT(trb->control, 20, 16);
+
+	xhci_device_t *dev = hc->bus.devices_by_slot[slot_id];
+	if (!dev) {
+		usb_log_error("Transfer event on disabled slot %u", slot_id);
+		return ENOENT;
+	}
+
+	const usb_endpoint_t ep_num = ep_dci / 2;
+	xhci_endpoint_t *ep = xhci_device_get_endpoint(dev, ep_num);
+	if (!ep) {
+		usb_log_error("Transfer event on dropped endpoint %u of device "
+		    XHCI_DEV_FMT, ep_num, XHCI_DEV_ARGS(*dev));
+		return ENOENT;
+	}
+
+	/* FIXME: This is racy. Do we care? */
+	ep->ring.dequeue = addr;
+
+	fibril_mutex_lock(&ep->base.guard);
+	usb_transfer_batch_t *batch = ep->base.active_batch;
+	if (!batch) {
+		fibril_mutex_unlock(&ep->base.guard);
+		return ENOENT;
+	}
+
+	batch->error = (TRB_COMPLETION_CODE(*trb) == XHCI_TRBC_SUCCESS) ? EOK : ENAK;
+	batch->transfered_size = batch->buffer_size - TRB_TRANSFER_LENGTH(*trb);
+	usb_transfer_batch_reset_toggle(batch);
+	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);
+		assert(batch->transfered_size <= batch->buffer_size);
+		memcpy(batch->buffer, transfer->hc_buffer, batch->transfered_size);
+	}
+
+	usb_transfer_batch_finish(batch);
+	return EOK;
+}
+
+typedef int (*transfer_handler)(xhci_hc_t *, xhci_transfer_t *);
+
+static const transfer_handler transfer_handlers[] = {
+	[USB_TRANSFER_CONTROL] = schedule_control,
+	[USB_TRANSFER_ISOCHRONOUS] = schedule_isochronous,
+	[USB_TRANSFER_BULK] = schedule_bulk,
+	[USB_TRANSFER_INTERRUPT] = schedule_interrupt,
+};
+
+int xhci_transfer_schedule(xhci_hc_t *hc, usb_transfer_batch_t *batch)
+{
+	assert(hc);
+	endpoint_t *ep = batch->ep;
+
+	xhci_transfer_t *transfer = xhci_transfer_from_batch(batch);
+	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
+	xhci_device_t *xhci_dev = xhci_ep_to_dev(xhci_ep);
+
+	/* Offline devices don't schedule transfers other than on EP0. */
+	if (!xhci_dev->online && ep->endpoint > 0) {
+		return EAGAIN;
+	}
+
+	// FIXME: find a better way to check if the ring is not initialized
+	if (!xhci_ep->ring.segment_count) {
+		usb_log_error("Ring not initialized for endpoint " XHCI_EP_FMT,
+		    XHCI_EP_ARGS(*xhci_ep));
+		return EINVAL;
+	}
+
+	const usb_transfer_type_t type = batch->ep->transfer_type;
+	assert(type >= 0 && type < ARRAY_SIZE(transfer_handlers));
+	assert(transfer_handlers[type]);
+
+	if (batch->buffer_size > 0) {
+		transfer->hc_buffer = malloc32(batch->buffer_size);
+		if (!transfer->hc_buffer)
+			return ENOMEM;
+	}
+
+	if (batch->dir != USB_DIRECTION_IN) {
+		// Sending stuff from host to device, we need to copy the actual data.
+		memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
+	}
+
+	fibril_mutex_lock(&ep->guard);
+	endpoint_activate_locked(ep, batch);
+	const int err = transfer_handlers[batch->ep->transfer_type](hc, transfer);
+
+	if (err) {
+		endpoint_deactivate_locked(ep);
+		fibril_mutex_unlock(&ep->guard);
+		return err;
+	}
+
+	/* After the critical section, the transfer can already be finished or aborted. */
+	transfer = NULL; batch = NULL;
+	fibril_mutex_unlock(&ep->guard);
+
+	const uint8_t slot_id = xhci_dev->slot_id;
+	const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
+	return hc_ring_doorbell(hc, slot_id, target);
+}
Index: uspace/drv/bus/usb/xhci/transfers.h
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/transfers.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,72 @@
+/*
+ * 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 The host controller transfer ring management
+ */
+
+#ifndef XHCI_TRANSFERS_H
+#define XHCI_TRANSFERS_H
+
+#include <usb/host/usb_transfer_batch.h>
+
+#include "hw_struct/context.h"
+#include "trb_ring.h"
+
+typedef struct xhci_hc xhci_hc_t;
+
+typedef struct {
+	usb_transfer_batch_t batch;
+	link_t link;
+
+	uint8_t direction;
+
+	void* hc_buffer;                    /* Virtual address of the buffer start. */
+	uintptr_t hc_buffer_phys;
+
+	uintptr_t interrupt_trb_phys;
+} xhci_transfer_t;
+
+xhci_transfer_t* xhci_transfer_create(endpoint_t *);
+int xhci_transfer_schedule(xhci_hc_t *, usb_transfer_batch_t *);
+int xhci_handle_transfer_event(xhci_hc_t *, xhci_trb_t *);
+void xhci_transfer_destroy(xhci_transfer_t *);
+
+static inline xhci_transfer_t *xhci_transfer_from_batch(usb_transfer_batch_t *batch)
+{
+	assert(batch);
+	return (xhci_transfer_t *) batch;
+}
+
+/**
+ * @}
+ */
+#endif
Index: uspace/drv/bus/usb/xhci/trb_ring.c
===================================================================
--- uspace/drv/bus/usb/xhci/trb_ring.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/trb_ring.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty
+ * 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.
+ */
+
+#include <errno.h>
+#include <assert.h>
+#include <ddi.h>
+#include <as.h>
+#include <align.h>
+#include <usb/debug.h>
+#include <usb/host/utils/malloc32.h>
+#include "hw_struct/trb.h"
+#include "trb_ring.h"
+
+#define SEGMENT_HEADER_SIZE (sizeof(link_t) + sizeof(uintptr_t))
+
+/**
+ * Number of TRBs in a segment (with our header).
+ */
+#define SEGMENT_TRB_COUNT ((PAGE_SIZE - SEGMENT_HEADER_SIZE) / sizeof(xhci_trb_t))
+
+struct trb_segment {
+	xhci_trb_t trb_storage [SEGMENT_TRB_COUNT];
+
+	link_t segments_link;
+	uintptr_t phys;
+} __attribute__((aligned(PAGE_SIZE)));
+
+
+static inline xhci_trb_t *segment_begin(trb_segment_t *segment)
+{
+	return segment->trb_storage;
+}
+
+static inline xhci_trb_t *segment_end(trb_segment_t *segment)
+{
+	return segment_begin(segment) + SEGMENT_TRB_COUNT;
+}
+
+/**
+ * Allocate and initialize new segment.
+ *
+ * TODO: When the HC supports 64-bit addressing, there's no need to restrict
+ * to DMAMEM_4GiB.
+ */
+static int trb_segment_allocate(trb_segment_t **segment)
+{
+	uintptr_t phys;
+	int err;
+
+	*segment = AS_AREA_ANY;
+	err = dmamem_map_anonymous(PAGE_SIZE,
+	    DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &phys,
+	    (void *) segment);
+
+	if (err == EOK) {
+		memset(*segment, 0, PAGE_SIZE);
+		(*segment)->phys = phys;
+
+		usb_log_debug2("Allocated new ring segment.");
+	}
+
+	return err;
+}
+
+/**
+ * Initializes the ring with one segment.
+ * Event when it fails, the structure needs to be finalized.
+ */
+int xhci_trb_ring_init(xhci_trb_ring_t *ring)
+{
+	struct trb_segment *segment;
+	int err;
+
+	list_initialize(&ring->segments);
+
+	if ((err = trb_segment_allocate(&segment)) != EOK)
+		return err;
+
+	list_append(&segment->segments_link, &ring->segments);
+	ring->segment_count = 1;
+
+	xhci_trb_t *last = segment_end(segment) - 1;
+	xhci_trb_link_fill(last, segment->phys);
+	TRB_LINK_SET_TC(*last, true);
+
+	ring->enqueue_segment = segment;
+	ring->enqueue_trb = segment_begin(segment);
+	ring->dequeue = segment->phys;
+	ring->pcs = 1;
+
+	fibril_mutex_initialize(&ring->guard);
+
+	usb_log_debug2("Initialized new TRB ring.");
+
+	return EOK;
+}
+
+void xhci_trb_ring_fini(xhci_trb_ring_t *ring)
+{
+	assert(ring);
+
+	list_foreach_safe(ring->segments, cur, next) {
+		trb_segment_t *segment = list_get_instance(cur, trb_segment_t, segments_link);
+		dmamem_unmap_anonymous(segment);
+	}
+}
+
+/**
+ * When the enqueue pointer targets a Link TRB, resolve it.
+ *
+ * Relies on segments being in the segment list in linked order.
+ *
+ * According to section 4.9.2.2, figure 16, the link TRBs cannot be chained, so
+ * it shall not be called in cycle, nor have an inner cycle.
+ */
+static void trb_ring_resolve_link(xhci_trb_ring_t *ring)
+{
+	link_t *next_segment = list_next(&ring->enqueue_segment->segments_link, &ring->segments);
+	if (!next_segment)
+		next_segment = list_first(&ring->segments);
+
+	ring->enqueue_segment = list_get_instance(next_segment, trb_segment_t, segments_link);
+	ring->enqueue_trb = segment_begin(ring->enqueue_segment);
+}
+
+static uintptr_t trb_ring_enqueue_phys(xhci_trb_ring_t *ring)
+{
+	uintptr_t trb_id = ring->enqueue_trb - segment_begin(ring->enqueue_segment);
+	return ring->enqueue_segment->phys + trb_id * sizeof(xhci_trb_t);
+}
+
+static bool trb_generates_interrupt(xhci_trb_t *trb)
+{
+	return TRB_TYPE(*trb) >= XHCI_TRB_TYPE_ENABLE_SLOT_CMD
+		|| TRB_IOC(*trb);
+}
+
+/**
+ * Enqueue TDs composed of TRBs.
+ *
+ * This will copy specified number of TRBs chained together into the ring. The
+ * cycle flag in TRBs may be changed.
+ *
+ * The copied TRBs must be contiguous in memory, and must not contain Link TRBs.
+ *
+ * We cannot avoid the copying, because the TRB in ring should be updated atomically.
+ *
+ * @param first_trb the first TRB
+ * @param trbs number of TRBS to enqueue
+ * @param phys returns address of the last TRB enqueued
+ * @return EOK on success,
+ *         EAGAIN when the ring is too full to fit all TRBs (temporary)
+ */
+int xhci_trb_ring_enqueue_multiple(xhci_trb_ring_t *ring, xhci_trb_t *first_trb,
+	size_t trbs, uintptr_t *phys)
+{
+	assert(trbs > 0);
+	fibril_mutex_lock(&ring->guard);
+
+	xhci_trb_t * const saved_enqueue_trb = ring->enqueue_trb;
+	trb_segment_t * const saved_enqueue_segment = ring->enqueue_segment;
+	if (phys)
+		*phys = (uintptr_t)NULL;
+
+	/*
+	 * First, dry run and advance the enqueue pointer to see if the ring would
+	 * be full anytime during the transaction.
+	 */
+	xhci_trb_t *trb = first_trb;
+	for (size_t i = 0; i < trbs; ++i, ++trb) {
+		if (trb_generates_interrupt(trb)) {
+			if (*phys)
+				return ENOTSUP;
+			*phys = trb_ring_enqueue_phys(ring);
+		}
+
+		ring->enqueue_trb++;
+
+		if (TRB_TYPE(*ring->enqueue_trb) == XHCI_TRB_TYPE_LINK)
+			trb_ring_resolve_link(ring);
+
+		if (trb_ring_enqueue_phys(ring) == ring->dequeue)
+			goto err_again;
+	}
+
+	ring->enqueue_segment = saved_enqueue_segment;
+	ring->enqueue_trb = saved_enqueue_trb;
+
+	/*
+	 * Now, copy the TRBs without further checking.
+	 */
+	trb = first_trb;
+	for (size_t i = 0; i < trbs; ++i, ++trb) {
+		TRB_SET_CYCLE(*trb, ring->pcs);
+		xhci_trb_copy_to_pio(ring->enqueue_trb, trb);
+
+		usb_log_debug2("TRB ring(%p): Enqueued TRB %p", ring, trb);
+		ring->enqueue_trb++;
+
+		if (TRB_TYPE(*ring->enqueue_trb) == XHCI_TRB_TYPE_LINK) {
+			TRB_SET_CYCLE(*ring->enqueue_trb, ring->pcs);
+
+			if (TRB_LINK_TC(*ring->enqueue_trb)) {
+				ring->pcs = !ring->pcs;
+				usb_log_debug2("TRB ring(%p): PCS toggled", ring);
+			}
+
+			trb_ring_resolve_link(ring);
+		}
+	}
+
+	fibril_mutex_unlock(&ring->guard);
+	return EOK;
+
+err_again:
+	ring->enqueue_segment = saved_enqueue_segment;
+	ring->enqueue_trb = saved_enqueue_trb;
+	fibril_mutex_unlock(&ring->guard);
+	return EAGAIN;
+}
+
+/**
+ * Enqueue TD composed of a single TRB. See: `xhci_trb_ring_enqueue_multiple`
+ */
+int xhci_trb_ring_enqueue(xhci_trb_ring_t *ring, xhci_trb_t *td, uintptr_t *phys)
+{
+	return xhci_trb_ring_enqueue_multiple(ring, td, 1, phys);
+}
+
+/**
+ * Initializes an event ring.
+ * Even when it fails, the structure needs to be finalized.
+ */
+int xhci_event_ring_init(xhci_event_ring_t *ring)
+{
+	struct trb_segment *segment;
+	int err;
+
+	list_initialize(&ring->segments);
+
+	if ((err = trb_segment_allocate(&segment)) != EOK)
+		return err;
+
+	list_append(&segment->segments_link, &ring->segments);
+	ring->segment_count = 1;
+
+	ring->dequeue_segment = segment;
+	ring->dequeue_trb = segment_begin(segment);
+	ring->dequeue_ptr = segment->phys;
+
+	ring->erst = malloc32(PAGE_SIZE);
+	if (ring->erst == NULL)
+		return ENOMEM;
+	memset(ring->erst, 0, PAGE_SIZE);
+
+	xhci_fill_erst_entry(&ring->erst[0], segment->phys, SEGMENT_TRB_COUNT);
+
+	ring->ccs = 1;
+
+	fibril_mutex_initialize(&ring->guard);
+
+	usb_log_debug("Initialized event ring.");
+
+	return EOK;
+}
+
+void xhci_event_ring_fini(xhci_event_ring_t *ring)
+{
+	list_foreach_safe(ring->segments, cur, next) {
+		trb_segment_t *segment = list_get_instance(cur, trb_segment_t, segments_link);
+		dmamem_unmap_anonymous(segment);
+	}
+
+	if (ring->erst)
+		free32(ring->erst);
+}
+
+static uintptr_t event_ring_dequeue_phys(xhci_event_ring_t *ring)
+{
+	uintptr_t trb_id = ring->dequeue_trb - segment_begin(ring->dequeue_segment);
+	return ring->dequeue_segment->phys + trb_id * sizeof(xhci_trb_t);
+}
+
+/**
+ * Fill the event with next valid event from the ring.
+ *
+ * @param event pointer to event to be overwritten
+ * @return EOK on success,
+ *         ENOENT when the ring is empty
+ */
+int xhci_event_ring_dequeue(xhci_event_ring_t *ring, xhci_trb_t *event)
+{
+	fibril_mutex_lock(&ring->guard);
+
+	/**
+	 * The ERDP reported to the HC is a half-phase off the one we need to
+	 * maintain. Therefore, we keep it extra.
+	 */
+	ring->dequeue_ptr = event_ring_dequeue_phys(ring);
+
+	if (TRB_CYCLE(*ring->dequeue_trb) != ring->ccs) {
+		fibril_mutex_unlock(&ring->guard);
+		return ENOENT; /* The ring is empty. */
+	}
+
+	memcpy(event, ring->dequeue_trb, sizeof(xhci_trb_t));
+
+	ring->dequeue_trb++;
+	const unsigned index = ring->dequeue_trb - segment_begin(ring->dequeue_segment);
+
+	/* Wrapping around segment boundary */
+	if (index >= SEGMENT_TRB_COUNT) {
+		link_t *next_segment = list_next(&ring->dequeue_segment->segments_link, &ring->segments);
+
+		/* Wrapping around table boundary */
+		if (!next_segment) {
+			next_segment = list_first(&ring->segments);
+			ring->ccs = !ring->ccs;
+		}
+
+		ring->dequeue_segment = list_get_instance(next_segment, trb_segment_t, segments_link);
+		ring->dequeue_trb = segment_begin(ring->dequeue_segment);
+	}
+
+	fibril_mutex_unlock(&ring->guard);
+	return EOK;
+}
Index: uspace/drv/bus/usb/xhci/trb_ring.h
===================================================================
--- uspace/drv/bus/usb/xhci/trb_ring.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/trb_ring.h	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty
+ * 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
+ * TRB Ring is a data structure for communication between HC and software.
+ *
+ * Despite this description, it is not used as an hardware structure - all but
+ * the Event ring is used as buffer of the TRBs itself, linked by Link TRB to
+ * form a (possibly multi-segment) circular buffer.
+ *
+ * This data structure abstracts this behavior.
+ */
+
+#ifndef XHCI_TRB_RING_H
+#define XHCI_TRB_RING_H
+
+#include <adt/list.h>
+#include <fibril_synch.h>
+#include <libarch/config.h>
+
+typedef struct trb_segment trb_segment_t;
+typedef struct xhci_hc xhci_hc_t;
+typedef struct xhci_trb xhci_trb_t;
+typedef struct xhci_erst_entry xhci_erst_entry_t;
+
+/**
+ * A TRB ring of which the software is a producer - command / transfer.
+ */
+typedef struct xhci_trb_ring {
+	list_t segments;                /* List of assigned segments */
+	int segment_count;              /* Number of segments assigned */
+
+	/*
+	 * As the link TRBs connect physical addresses, we need to keep track of
+	 * active segment in virtual memory. The enqueue ptr should always belong
+	 * to the enqueue segment.
+	 */
+	trb_segment_t *enqueue_segment;
+	xhci_trb_t *enqueue_trb;
+
+	uintptr_t dequeue;              /* Last reported position of the dequeue pointer */
+	bool pcs;                       /* Producer Cycle State: section 4.9.2 */
+
+	fibril_mutex_t guard;
+} xhci_trb_ring_t;
+
+int xhci_trb_ring_init(xhci_trb_ring_t *);
+void xhci_trb_ring_fini(xhci_trb_ring_t *);
+int xhci_trb_ring_enqueue(xhci_trb_ring_t *, xhci_trb_t *, uintptr_t *);
+int xhci_trb_ring_enqueue_multiple(xhci_trb_ring_t *, xhci_trb_t *, size_t, uintptr_t *);
+
+/**
+ * Get the initial value to fill into CRCR.
+ */
+static inline uintptr_t xhci_trb_ring_get_dequeue_ptr(xhci_trb_ring_t *ring)
+{
+	return ring->dequeue;
+}
+
+/**
+ * When an event is received by the upper layer, it needs to update the dequeue
+ * pointer inside the ring. Otherwise, the ring will soon show up as full.
+ */
+void xhci_trb_ring_update_dequeue(xhci_trb_ring_t *, uintptr_t);
+uintptr_t xhci_trb_ring_get_dequeue_ptr(xhci_trb_ring_t *);
+
+/**
+ * A TRB ring of which the software is a consumer (event rings).
+ */
+typedef struct xhci_event_ring {
+	list_t segments;                /* List of assigned segments */
+	int segment_count;              /* Number of segments assigned */
+
+	trb_segment_t *dequeue_segment; /* Current segment of the dequeue ptr */
+	xhci_trb_t *dequeue_trb;        /* Next TRB to be processed */
+	uintptr_t dequeue_ptr;          /* Physical address of the ERDP to be reported to the HC */
+
+	xhci_erst_entry_t *erst;        /* ERST given to the HC */
+
+	bool ccs;                       /* Consumer Cycle State: section 4.9.2 */
+
+	fibril_mutex_t guard;
+} xhci_event_ring_t;
+
+int xhci_event_ring_init(xhci_event_ring_t *);
+void xhci_event_ring_fini(xhci_event_ring_t *);
+int xhci_event_ring_dequeue(xhci_event_ring_t *, xhci_trb_t *);
+
+#endif
Index: uspace/drv/bus/usb/xhci/xhci.ma
===================================================================
--- uspace/drv/bus/usb/xhci/xhci.ma	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
+++ uspace/drv/bus/usb/xhci/xhci.ma	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -0,0 +1,2 @@
+20 usb/host=xhci
+10 pci/class=0c&subclass=03&progif=30
Index: uspace/drv/hid/usbhid/main.c
===================================================================
--- uspace/drv/hid/usbhid/main.c	(revision 53b9f2c3825e0a362ac71ad50183265aa5d20115)
+++ uspace/drv/hid/usbhid/main.c	(revision 6cad7765736ec29f90dd2a80f7df7d0dd4857b9d)
@@ -95,5 +95,5 @@
 	   usb_hid_polling_callback,
 	   /* How much data to request. */
-	   hid_dev->poll_pipe_mapping->pipe.max_packet_size,
+	   hid_dev->poll_pipe_mapping->pipe.desc.max_packet_size,
 	   /* Delay */
 	   -1,
@@ -124,7 +124,13 @@
 static int usb_hid_device_rem(usb_device_t *dev)
 {
-	// TODO: Stop device polling
-	// TODO: Call deinit (stops autorepeat too)
-	return ENOTSUP;
+	assert(dev);
+	usb_hid_dev_t *hid_dev = usb_device_data_get(dev);
+	assert(hid_dev);
+
+	/* TODO: Stop device polling prior to deinit. Now it fails on endpoint error. */
+
+	usb_hid_deinit(hid_dev);
+	usb_log_debug2("%s destruction complete.\n", usb_device_get_name(dev));
+	return EOK;
 }
 
