Index: .bzrignore
===================================================================
--- .bzrignore	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ .bzrignore	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -239,4 +239,5 @@
 uspace/dist/drv/virt/
 uspace/dist/drv/xtkbd/
+uspace/dist/drv/xhci/
 uspace/dist/inc/_link.ld
 uspace/dist/inc/c/
@@ -292,4 +293,6 @@
 uspace/drv/bus/usb/usbmid/usbmid
 uspace/drv/bus/usb/vhc/vhc
+uspace/drv/bus/usb/xhci/xhci
+uspace/drv/bus/usb/xhci/test-xhci
 uspace/drv/char/i8042/i8042
 uspace/drv/char/ns8250/ns8250
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ boot/Makefile.common	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -237,4 +237,5 @@
 	$(USPACE_PATH)/lib/posix/test-libposix \
 	$(USPACE_PATH)/lib/uri/test-liburi \
+	$(USPACE_PATH)/drv/bus/usb/xhci/test-xhci \
 	$(USPACE_PATH)/app/bdsh/test-bdsh \
 	$(USPACE_PATH)/srv/net/tcp/test-tcp
Index: boot/arch/amd64/Makefile.inc
===================================================================
--- boot/arch/amd64/Makefile.inc	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ boot/arch/amd64/Makefile.inc	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -53,4 +53,5 @@
 	bus/usb/usbmid \
 	bus/usb/vhc \
+	bus/usb/xhci \
 	block/usbmast \
 	hid/usbhid
Index: tools/ew.py
===================================================================
--- tools/ew.py	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ tools/ew.py	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -169,4 +169,9 @@
 	return ' -usb'
 
+def qemu_xhci_options():
+	if is_override('noxhci'):
+		return ''
+	return ' -device nec-usb-xhci,id=xhci'
+
 def qemu_audio_options():
 	if is_override('nosnd'):
@@ -189,4 +194,6 @@
 	if (not 'usb' in cfg.keys()) or cfg['usb']:
 		cmdline += qemu_usb_options()
+	if (not 'xhci' in cfg.keys()) or cfg['xhci']:
+		cmdline += qemu_xhci_options()
 	if (not 'audio' in cfg.keys()) or cfg['audio']:
 		cmdline += qemu_audio_options()
@@ -295,5 +302,5 @@
 def usage():
 	print("%s - emulator wrapper for running HelenOS\n" % os.path.basename(sys.argv[0]))
-	print("%s [-d] [-h] [-net e1k|rtl8139|ne2k] [-nohdd] [-nokvm] [-nonet] [-nosnd] [-nousb]\n" %
+	print("%s [-d] [-h] [-net e1k|rtl8139|ne2k] [-nohdd] [-nokvm] [-nonet] [-nosnd] [-nousb] [-noxhci]\n" %
 	    os.path.basename(sys.argv[0]))
 	print("-d\tDry run: do not run the emulation, just print the command line.")
@@ -304,4 +311,5 @@
 	print("-nosnd\tDisable sound, if applicable.")
 	print("-nousb\tDisable USB support, if applicable.")
+	print("-noxhci\tDisable XHCI support, if applicable.")
 
 def fail(platform, machine):
@@ -345,4 +353,6 @@
 		elif sys.argv[i] == '-nousb':
 			overrides['nousb'] = True
+		elif sys.argv[i] == '-noxhci':
+			overrides['noxhci'] = True
 		else:
 			usage()
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/Makefile	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -154,4 +154,5 @@
 	drv/bus/usb/usbmid \
 	drv/bus/usb/vhc \
+	drv/bus/usb/xhci \
 	drv/char/i8042 \
 	drv/char/msim-con \
Index: uspace/drv/block/usbmast/main.c
===================================================================
--- uspace/drv/block/usbmast/main.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/block/usbmast/main.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ehci/Makefile	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ehci/ehci_batch.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ehci/ehci_batch.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/ehci/ehci_bus.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/ehci/ehci_bus.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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: pace/drv/bus/usb/ehci/ehci_endpoint.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_endpoint.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ 	(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: pace/drv/bus/usb/ehci/ehci_endpoint.h
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_endpoint.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ 	(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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ehci/ehci_rh.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ehci/endpoint_list.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ehci/hc.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ehci/hc.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ehci/hw_struct/queue_head.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ehci/main.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ehci/res.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ehci/res.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ohci/Makefile	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ohci/endpoint_list.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ohci/hc.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ohci/hc.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ohci/hw_struct/endpoint_descriptor.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ohci/main.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ohci/ohci_batch.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ohci/ohci_batch.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/ohci/ohci_bus.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/ohci/ohci_bus.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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: pace/drv/bus/usb/ohci/ohci_endpoint.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_endpoint.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ 	(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: pace/drv/bus/usb/ohci/ohci_endpoint.h
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_endpoint.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ 	(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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/ohci/ohci_rh.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/uhci/hc.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/uhci/hc.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/uhci/main.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/uhci/transfer_list.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/uhci/uhci_batch.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/uhci/uhci_batch.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/uhci/uhci_rh.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/usbhub/port.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/usbhub/port.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/usbhub/status.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/usbmid/main.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/vhc/main.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/vhc/transfer.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/bus/usb/vhc/vhcd.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/Makefile	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/bus.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/bus.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/commands.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/commands.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/debug.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/debug.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/endpoint.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/endpoint.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/hc.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/hw_struct/README	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/hw_struct/common.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/hw_struct/context.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/hw_struct/regs.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/hw_struct/trb.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/main.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/rh.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/rh.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/scratchpad.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/scratchpad.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/test/reg-ops.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/transfers.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/trb_ring.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/trb_ring.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/drv/bus/usb/xhci/xhci.ma	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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 d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/drv/hid/usbhid/main.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -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;
 }
 
Index: uspace/lib/c/include/bitops.h
===================================================================
--- uspace/lib/c/include/bitops.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/c/include/bitops.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -54,4 +54,8 @@
 #define BIT_RANGE_EXTRACT(type, hi, lo, value) \
     (((value) >> (lo)) & BIT_RRANGE(type, (hi) - (lo) + 1))
+
+/** Insert @a value between bits @a hi .. @a lo. */
+#define BIT_RANGE_INSERT(type, hi, lo, value) \
+    (((value) & BIT_RRANGE(type, (hi) - (lo) + 1)) << (lo))
 
 /** Return position of first non-zero bit from left (i.e. [log_2(arg)]).
Index: uspace/lib/c/include/byteorder.h
===================================================================
--- uspace/lib/c/include/byteorder.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/c/include/byteorder.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -85,4 +85,9 @@
 #define ntohl(n)  uint32_t_be2host((n))
 
+#define uint8_t_be2host(n)  (n)
+#define uint8_t_le2host(n)  (n)
+#define host2uint8_t_be(n)  (n)
+#define host2uint8_t_le(n)  (n)
+
 static inline uint64_t uint64_t_byteorder_swap(uint64_t n)
 {
Index: uspace/lib/c/include/ipc/dev_iface.h
===================================================================
--- uspace/lib/c/include/ipc/dev_iface.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/c/include/ipc/dev_iface.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -56,5 +56,5 @@
 	/** Interface provided by any USB device. */
 	USB_DEV_IFACE,
-	/** Interface provided by USB host controller. */
+	/** Interface provided by USB host controller to USB device. */
 	USBHC_DEV_IFACE,
 	/** Interface provided by USB HID devices. */
Index: uspace/lib/drv/generic/private/remote_usbhc.h
===================================================================
--- uspace/lib/drv/generic/private/remote_usbhc.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/drv/generic/private/remote_usbhc.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2010 Vojtech Horky
+ * Copyright (c) 2017 Ondrej Hlavaty
  * All rights reserved.
  *
Index: uspace/lib/drv/generic/remote_usb.c
===================================================================
--- uspace/lib/drv/generic/remote_usb.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/drv/generic/remote_usb.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -63,12 +63,4 @@
 	IPC_M_USB_GET_MY_INTERFACE,
 	IPC_M_USB_GET_MY_DEVICE_HANDLE,
-	IPC_M_USB_RESERVE_DEFAULT_ADDRESS,
-	IPC_M_USB_RELEASE_DEFAULT_ADDRESS,
-	IPC_M_USB_DEVICE_ENUMERATE,
-	IPC_M_USB_DEVICE_REMOVE,
-	IPC_M_USB_REGISTER_ENDPOINT,
-	IPC_M_USB_UNREGISTER_ENDPOINT,
-	IPC_M_USB_READ,
-	IPC_M_USB_WRITE,
 } usb_iface_funcs_t;
 
@@ -109,192 +101,6 @@
 }
 
-/** Reserve default USB address.
- * @param[in] exch IPC communication exchange
- * @param[in] speed Communication speed of the newly attached device
- * @return Error code.
- */
-int usb_reserve_default_address(async_exch_t *exch, usb_speed_t speed)
-{
-	if (!exch)
-		return EBADMEM;
-	return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_RESERVE_DEFAULT_ADDRESS, speed);
-}
-
-/** Release default USB address.
- *
- * @param[in] exch IPC communication exchange
- *
- * @return Error code.
- *
- */
-int usb_release_default_address(async_exch_t *exch)
-{
-	if (!exch)
-		return EBADMEM;
-	return async_req_1_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_RELEASE_DEFAULT_ADDRESS);
-}
-
-/** Trigger USB device enumeration
- *
- * @param[in]  exch   IPC communication exchange
- * @param[out] handle Identifier of the newly added device (if successful)
- *
- * @return Error code.
- *
- */
-int usb_device_enumerate(async_exch_t *exch, unsigned port)
-{
-	if (!exch)
-		return EBADMEM;
-	const int ret = async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_DEVICE_ENUMERATE, port);
-	return ret;
-}
-
-/** Trigger USB device enumeration
- *
- * @param[in] exch   IPC communication exchange
- * @param[in] handle Identifier of the device
- *
- * @return Error code.
- *
- */
-int usb_device_remove(async_exch_t *exch, unsigned port)
-{
-	if (!exch)
-		return EBADMEM;
-	return async_req_2_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_DEVICE_REMOVE, port);
-}
-
-static_assert(sizeof(sysarg_t) >= 4);
-
-typedef union {
-	uint8_t arr[sizeof(sysarg_t)];
-	sysarg_t arg;
-} pack8_t;
-
-int usb_register_endpoint(async_exch_t *exch, usb_endpoint_t endpoint,
-    usb_transfer_type_t type, usb_direction_t direction,
-    size_t mps, unsigned packets, unsigned interval)
-{
-	if (!exch)
-		return EBADMEM;
-	pack8_t pack;
-	pack.arr[0] = type;
-	pack.arr[1] = direction;
-	pack.arr[2] = interval;
-	pack.arr[3] = packets;
-
-	return async_req_4_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_REGISTER_ENDPOINT, endpoint, pack.arg, mps);
-
-}
-
-int usb_unregister_endpoint(async_exch_t *exch, usb_endpoint_t endpoint,
-    usb_direction_t direction)
-{
-	if (!exch)
-		return EBADMEM;
-	return async_req_3_0(exch, DEV_IFACE_ID(USB_DEV_IFACE),
-	    IPC_M_USB_UNREGISTER_ENDPOINT, endpoint, direction);
-}
-
-int usb_read(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
-    void *data, size_t size, size_t *rec_size)
-{
-	if (!exch)
-		return EBADMEM;
-
-	if (size == 0 && setup == 0)
-		return EOK;
-
-	/* Make call identifying target USB device and type of transfer. */
-	aid_t opening_request = async_send_4(exch,
-	    DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_READ, endpoint,
-	    (setup & UINT32_MAX), (setup >> 32), NULL);
-
-	if (opening_request == 0) {
-		return ENOMEM;
-	}
-
-	/* Retrieve the data. */
-	ipc_call_t data_request_call;
-	aid_t data_request =
-	    async_data_read(exch, data, size, &data_request_call);
-
-	if (data_request == 0) {
-		// FIXME: How to let the other side know that we want to abort?
-		async_forget(opening_request);
-		return ENOMEM;
-	}
-
-	/* Wait for the answer. */
-	sysarg_t data_request_rc;
-	sysarg_t opening_request_rc;
-	async_wait_for(data_request, &data_request_rc);
-	async_wait_for(opening_request, &opening_request_rc);
-
-	if (data_request_rc != EOK) {
-		/* Prefer the return code of the opening request. */
-		if (opening_request_rc != EOK) {
-			return (int) opening_request_rc;
-		} else {
-			return (int) data_request_rc;
-		}
-	}
-	if (opening_request_rc != EOK) {
-		return (int) opening_request_rc;
-	}
-
-	*rec_size = IPC_GET_ARG2(data_request_call);
-	return EOK;
-}
-
-int usb_write(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
-    const void *data, size_t size)
-{
-	if (!exch)
-		return EBADMEM;
-
-	if (size == 0 && setup == 0)
-		return EOK;
-
-	aid_t opening_request = async_send_5(exch,
-	    DEV_IFACE_ID(USB_DEV_IFACE), IPC_M_USB_WRITE, endpoint, size,
-	    (setup & UINT32_MAX), (setup >> 32), NULL);
-
-	if (opening_request == 0) {
-		return ENOMEM;
-	}
-
-	/* Send the data if any. */
-	if (size > 0) {
-		const int ret = async_data_write_start(exch, data, size);
-		if (ret != EOK) {
-			async_forget(opening_request);
-			return ret;
-		}
-	}
-
-	/* Wait for the answer. */
-	sysarg_t opening_request_rc;
-	async_wait_for(opening_request, &opening_request_rc);
-
-	return (int) opening_request_rc;
-}
-
 static void remote_usb_get_my_interface(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
 static void remote_usb_get_my_device_handle(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_reserve_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_release_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_device_enumerate(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_device_remove(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usb_read(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
-static void remote_usb_write(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
 
 /** Remote USB interface operations. */
@@ -302,12 +108,4 @@
 	[IPC_M_USB_GET_MY_INTERFACE] = remote_usb_get_my_interface,
 	[IPC_M_USB_GET_MY_DEVICE_HANDLE] = remote_usb_get_my_device_handle,
-	[IPC_M_USB_RESERVE_DEFAULT_ADDRESS] = remote_usb_reserve_default_address,
-	[IPC_M_USB_RELEASE_DEFAULT_ADDRESS] = remote_usb_release_default_address,
-	[IPC_M_USB_DEVICE_ENUMERATE] = remote_usb_device_enumerate,
-	[IPC_M_USB_DEVICE_REMOVE] = remote_usb_device_remove,
-	[IPC_M_USB_REGISTER_ENDPOINT] = remote_usb_register_endpoint,
-	[IPC_M_USB_UNREGISTER_ENDPOINT] = remote_usb_unregister_endpoint,
-	[IPC_M_USB_READ] = remote_usb_read,
-	[IPC_M_USB_WRITE] = remote_usb_write,
 };
 
@@ -357,266 +155,4 @@
 }
 
-void remote_usb_reserve_default_address(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	const usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (usb_iface->reserve_default_address == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
-	const int ret = usb_iface->reserve_default_address(fun, speed);
-	async_answer_0(callid, ret);
-}
-
-void remote_usb_release_default_address(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	const usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (usb_iface->release_default_address == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const int ret = usb_iface->release_default_address(fun);
-	async_answer_0(callid, ret);
-}
-
-static void remote_usb_device_enumerate(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	const usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (usb_iface->device_enumerate == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const unsigned port = DEV_IPC_GET_ARG1(*call);
-	const int ret = usb_iface->device_enumerate(fun, port);
-	async_answer_0(callid, ret);
-}
-
-static void remote_usb_device_remove(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	const usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (usb_iface->device_remove == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const unsigned port = DEV_IPC_GET_ARG1(*call);
-	const int ret = usb_iface->device_remove(fun, port);
-	async_answer_0(callid, ret);
-}
-
-static void remote_usb_register_endpoint(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (!usb_iface->register_endpoint) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const usb_endpoint_t endpoint = DEV_IPC_GET_ARG1(*call);
-	const pack8_t pack = { .arg = DEV_IPC_GET_ARG2(*call)};
-	const size_t max_packet_size = DEV_IPC_GET_ARG3(*call);
-
-	const usb_transfer_type_t transfer_type = pack.arr[0];
-	const usb_direction_t direction = pack.arr[1];
-	unsigned packets = pack.arr[2];
-	unsigned interval = pack.arr[3];
-
-	const int ret = usb_iface->register_endpoint(fun, endpoint,
-	    transfer_type, direction, max_packet_size, packets, interval);
-
-	async_answer_0(callid, ret);
-}
-
-static void remote_usb_unregister_endpoint(ddf_fun_t *fun, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	usb_iface_t *usb_iface = (usb_iface_t *) iface;
-
-	if (!usb_iface->unregister_endpoint) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	usb_endpoint_t endpoint = (usb_endpoint_t) DEV_IPC_GET_ARG1(*call);
-	usb_direction_t direction = (usb_direction_t) DEV_IPC_GET_ARG2(*call);
-
-	int rc = usb_iface->unregister_endpoint(fun, endpoint, direction);
-
-	async_answer_0(callid, rc);
-}
-
-typedef struct {
-	ipc_callid_t caller;
-	ipc_callid_t data_caller;
-	void *buffer;
-} async_transaction_t;
-
-static void async_transaction_destroy(async_transaction_t *trans)
-{
-	if (trans == NULL) {
-		return;
-	}
-	if (trans->buffer != NULL) {
-		free(trans->buffer);
-	}
-
-	free(trans);
-}
-
-static async_transaction_t *async_transaction_create(ipc_callid_t caller)
-{
-	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
-	if (trans == NULL) {
-		return NULL;
-	}
-
-	trans->caller = caller;
-	trans->data_caller = 0;
-	trans->buffer = NULL;
-
-	return trans;
-}
-
-static void callback_out(int outcome, void *arg)
-{
-	async_transaction_t *trans = arg;
-
-	async_answer_0(trans->caller, outcome);
-
-	async_transaction_destroy(trans);
-}
-
-static void callback_in(int outcome, size_t actual_size, void *arg)
-{
-	async_transaction_t *trans = (async_transaction_t *)arg;
-
-	if (outcome != EOK) {
-		async_answer_0(trans->caller, outcome);
-		if (trans->data_caller) {
-			async_answer_0(trans->data_caller, EINTR);
-		}
-		async_transaction_destroy(trans);
-		return;
-	}
-
-	if (trans->data_caller) {
-		async_data_read_finalize(trans->data_caller,
-		    trans->buffer, actual_size);
-	}
-
-	async_answer_0(trans->caller, EOK);
-
-	async_transaction_destroy(trans);
-}
-
-void remote_usb_read(
-    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
-{
-	assert(fun);
-	assert(iface);
-	assert(call);
-
-	const usb_iface_t *usb_iface = iface;
-
-	if (!usb_iface->read) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
-	const uint64_t setup =
-	    ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
-	    (((uint64_t)DEV_IPC_GET_ARG3(*call)) << 32);
-
-	async_transaction_t *trans = async_transaction_create(callid);
-	if (trans == NULL) {
-		async_answer_0(callid, ENOMEM);
-		return;
-	}
-
-	size_t size = 0;
-	if (!async_data_read_receive(&trans->data_caller, &size)) {
-		async_answer_0(callid, EPARTY);
-		async_transaction_destroy(trans);
-		return;
-	}
-
-	trans->buffer = malloc(size);
-	if (trans->buffer == NULL) {
-		async_answer_0(trans->data_caller, ENOMEM);
-		async_answer_0(callid, ENOMEM);
-		async_transaction_destroy(trans);
-		return;
-	}
-
-	const int rc = usb_iface->read(
-	    fun, ep, setup, trans->buffer, size, callback_in, trans);
-
-	if (rc != EOK) {
-		async_answer_0(trans->data_caller, rc);
-		async_answer_0(callid, rc);
-		async_transaction_destroy(trans);
-	}
-}
-
-void remote_usb_write(
-    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
-{
-	assert(fun);
-	assert(iface);
-	assert(call);
-
-	const usb_iface_t *usb_iface = iface;
-
-	if (!usb_iface->write) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
-	const size_t data_buffer_len = DEV_IPC_GET_ARG2(*call);
-	const uint64_t setup =
-	    ((uint64_t)DEV_IPC_GET_ARG3(*call)) |
-	    (((uint64_t)DEV_IPC_GET_ARG4(*call)) << 32);
-
-	async_transaction_t *trans = async_transaction_create(callid);
-	if (trans == NULL) {
-		async_answer_0(callid, ENOMEM);
-		return;
-	}
-
-	size_t size = 0;
-	if (data_buffer_len > 0) {
-		const int rc = async_data_write_accept(&trans->buffer, false,
-		    1, data_buffer_len, 0, &size);
-
-		if (rc != EOK) {
-			async_answer_0(callid, rc);
-			async_transaction_destroy(trans);
-			return;
-		}
-	}
-
-	const int rc = usb_iface->write(
-	    fun, ep, setup, trans->buffer, size, callback_out, trans);
-
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		async_transaction_destroy(trans);
-	}
-}
 /**
  * @}
Index: uspace/lib/drv/generic/remote_usbhc.c
===================================================================
--- uspace/lib/drv/generic/remote_usbhc.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/drv/generic/remote_usbhc.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2010-2011 Vojtech Horky
+ * Copyright (c) 2010 Vojtech Horky
  * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2017 Ondrej Hlavaty
  * All rights reserved.
  *
@@ -35,68 +36,147 @@
 
 #include <async.h>
+#include <macros.h>
 #include <errno.h>
-#include <assert.h>
-#include <macros.h>
+#include <devman.h>
 
 #include "usbhc_iface.h"
 #include "ddf/driver.h"
 
-#define USB_MAX_PAYLOAD_SIZE 1020
-
-/** IPC methods for communication with HC through DDF interface.
- *
- * Notes for async methods:
- *
- * Methods for sending data to device (OUT transactions)
- * - e.g. IPC_M_USBHC_INTERRUPT_OUT -
- * always use the same semantics:
- * - first, IPC call with given method is made
- *   - argument #1 is target address
- *   - argument #2 is target endpoint
- *   - argument #3 is max packet size of the endpoint
- * - this call is immediately followed by IPC data write (from caller)
- * - the initial call (and the whole transaction) is answer after the
- *   transaction is scheduled by the HC and acknowledged by the device
- *   or immediately after error is detected
- * - the answer carries only the error code
- *
- * Methods for retrieving data from device (IN transactions)
- * - e.g. IPC_M_USBHC_INTERRUPT_IN -
- * also use the same semantics:
- * - first, IPC call with given method is made
- *   - argument #1 is target address
- *   - argument #2 is target endpoint
- * - this call is immediately followed by IPC data read (async version)
- * - the call is not answered until the device returns some data (or until
- *   error occurs)
- *
- * Some special methods (NO-DATA transactions) do not send any data. These
- * might behave as both OUT or IN transactions because communication parts
- * where actual buffers are exchanged are omitted.
- **
- * For all these methods, wrap functions exists. Important rule: functions
- * for IN transactions have (as parameters) buffers where retrieved data
- * will be stored. These buffers must be already allocated and shall not be
- * touch until the transaction is completed
- * (e.g. not before calling usb_wait_for() with appropriate handle).
- * OUT transactions buffers can be freed immediately after call is dispatched
- * (i.e. after return from wrapping function).
- *
- */
+
 typedef enum {
-	/** Get data from device.
-	 * See explanation at usb_iface_funcs_t (IN transaction).
-	 */
-	IPC_M_USBHC_READ,
-
-	/** Send data to device.
-	 * See explanation at usb_iface_funcs_t (OUT transaction).
-	 */
-	IPC_M_USBHC_WRITE,
+	IPC_M_USB_RESERVE_DEFAULT_ADDRESS,
+	IPC_M_USB_RELEASE_DEFAULT_ADDRESS,
+	IPC_M_USB_DEVICE_ENUMERATE,
+	IPC_M_USB_DEVICE_REMOVE,
+	IPC_M_USB_REGISTER_ENDPOINT,
+	IPC_M_USB_UNREGISTER_ENDPOINT,
+	IPC_M_USB_READ,
+	IPC_M_USB_WRITE,
 } usbhc_iface_funcs_t;
 
-int usbhc_read(async_exch_t *exch, usb_address_t address,
-    usb_endpoint_t endpoint, uint64_t setup, void *data, size_t size,
-    size_t *rec_size)
+/** Reserve default USB address.
+ * @param[in] exch IPC communication exchange
+ * @param[in] speed Communication speed of the newly attached device
+ * @return Error code.
+ */
+int usbhc_reserve_default_address(async_exch_t *exch, usb_speed_t speed)
+{
+	if (!exch)
+		return EBADMEM;
+	return async_req_2_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
+	    IPC_M_USB_RESERVE_DEFAULT_ADDRESS, speed);
+}
+
+/** Release default USB address.
+ *
+ * @param[in] exch IPC communication exchange
+ *
+ * @return Error code.
+ *
+ */
+int usbhc_release_default_address(async_exch_t *exch)
+{
+	if (!exch)
+		return EBADMEM;
+	return async_req_1_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
+	    IPC_M_USB_RELEASE_DEFAULT_ADDRESS);
+}
+
+/** Trigger USB device enumeration
+ *
+ * @param[in]  exch   IPC communication exchange
+ * @param[out] handle Identifier of the newly added device (if successful)
+ *
+ * @return Error code.
+ *
+ */
+int usbhc_device_enumerate(async_exch_t *exch, unsigned port)
+{
+	if (!exch)
+		return EBADMEM;
+	const int ret = async_req_2_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
+	    IPC_M_USB_DEVICE_ENUMERATE, port);
+	return ret;
+}
+
+/** Trigger USB device enumeration
+ *
+ * @param[in] exch   IPC communication exchange
+ * @param[in] handle Identifier of the device
+ *
+ * @return Error code.
+ *
+ */
+int usbhc_device_remove(async_exch_t *exch, unsigned port)
+{
+	if (!exch)
+		return EBADMEM;
+	return async_req_2_0(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
+	    IPC_M_USB_DEVICE_REMOVE, port);
+}
+
+int static_assert[sizeof(sysarg_t) >= 4 ? 1 : -1];
+typedef union {
+	uint8_t arr[sizeof(sysarg_t)];
+	sysarg_t arg;
+} pack8_t;
+
+int usbhc_register_endpoint(async_exch_t *exch,
+	usb_endpoint_desc_t *endpoint_desc)
+{
+	if (!exch)
+		return EBADMEM;
+
+	aid_t opening_request = async_send_1(exch,
+	    DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_REGISTER_ENDPOINT, NULL);
+
+	if (opening_request == 0) {
+		return ENOMEM;
+	}
+
+	const int ret = async_data_write_start(exch, (void *) endpoint_desc,
+		sizeof(usb_endpoint_desc_t));
+
+	if (ret != EOK) {
+		async_forget(opening_request);
+		return ret;
+	}
+
+	/* Wait for the answer. */
+	sysarg_t opening_request_rc;
+	async_wait_for(opening_request, &opening_request_rc);
+
+	return (int) opening_request_rc;
+}
+
+int usbhc_unregister_endpoint(async_exch_t *exch,
+	usb_endpoint_desc_t *endpoint_desc)
+{
+	if (!exch)
+		return EBADMEM;
+
+	aid_t opening_request = async_send_1(exch,
+		DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_UNREGISTER_ENDPOINT, NULL);
+
+	if (opening_request == 0) {
+		return ENOMEM;
+	}
+
+	const int ret = async_data_write_start(exch, endpoint_desc,
+		sizeof(usb_endpoint_desc_t));
+	if (ret != EOK) {
+		async_forget(opening_request);
+		return ret;
+	}
+
+	/* Wait for the answer. */
+	sysarg_t opening_request_rc;
+	async_wait_for(opening_request, &opening_request_rc);
+
+	return (int) opening_request_rc;
+}
+
+int usbhc_read(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
+    void *data, size_t size, size_t *rec_size)
 {
 	if (!exch)
@@ -106,11 +186,7 @@
 		return EOK;
 
-	const usb_target_t target =
-	    {{ .address = address, .endpoint = endpoint }};
-
 	/* Make call identifying target USB device and type of transfer. */
 	aid_t opening_request = async_send_4(exch,
-	    DEV_IFACE_ID(USBHC_DEV_IFACE),
-	    IPC_M_USBHC_READ, target.packed,
+	    DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_READ, endpoint,
 	    (setup & UINT32_MAX), (setup >> 32), NULL);
 
@@ -152,6 +228,6 @@
 }
 
-int usbhc_write(async_exch_t *exch, usb_address_t address,
-    usb_endpoint_t endpoint, uint64_t setup, const void *data, size_t size)
+int usbhc_write(async_exch_t *exch, usb_endpoint_t endpoint, uint64_t setup,
+    const void *data, size_t size)
 {
 	if (!exch)
@@ -161,9 +237,6 @@
 		return EOK;
 
-	const usb_target_t target =
-	    {{ .address = address, .endpoint = endpoint }};
-
-	aid_t opening_request = async_send_5(exch, DEV_IFACE_ID(USBHC_DEV_IFACE),
-	    IPC_M_USBHC_WRITE, target.packed, size,
+	aid_t opening_request = async_send_5(exch,
+	    DEV_IFACE_ID(USBHC_DEV_IFACE), IPC_M_USB_WRITE, endpoint, size,
 	    (setup & UINT32_MAX), (setup >> 32), NULL);
 
@@ -188,18 +261,30 @@
 }
 
-static void remote_usbhc_read(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_usbhc_write(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-
-/** Remote USB host controller interface operations. */
-static const remote_iface_func_ptr_t remote_usbhc_iface_ops[] = {
-	[IPC_M_USBHC_READ] = remote_usbhc_read,
-	[IPC_M_USBHC_WRITE] = remote_usbhc_write,
+static void remote_usbhc_reserve_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_release_default_address(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_device_enumerate(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_device_remove(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_register_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_unregister_endpoint(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_read(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
+static void remote_usbhc_write(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call);
+
+/** Remote USB interface operations. */
+static const remote_iface_func_ptr_t remote_usbhc_iface_ops [] = {
+	[IPC_M_USB_RESERVE_DEFAULT_ADDRESS] = remote_usbhc_reserve_default_address,
+	[IPC_M_USB_RELEASE_DEFAULT_ADDRESS] = remote_usbhc_release_default_address,
+	[IPC_M_USB_DEVICE_ENUMERATE] = remote_usbhc_device_enumerate,
+	[IPC_M_USB_DEVICE_REMOVE] = remote_usbhc_device_remove,
+	[IPC_M_USB_REGISTER_ENDPOINT] = remote_usbhc_register_endpoint,
+	[IPC_M_USB_UNREGISTER_ENDPOINT] = remote_usbhc_unregister_endpoint,
+	[IPC_M_USB_READ] = remote_usbhc_read,
+	[IPC_M_USB_WRITE] = remote_usbhc_write,
 };
 
-/** Remote USB host controller interface structure.
+/** Remote USB interface structure.
  */
 const remote_iface_t remote_usbhc_iface = {
 	.method_count = ARRAY_SIZE(remote_usbhc_iface_ops),
-	.methods = remote_usbhc_iface_ops
+	.methods = remote_usbhc_iface_ops,
 };
 
@@ -210,12 +295,136 @@
 } async_transaction_t;
 
+void remote_usbhc_reserve_default_address(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
+
+	if (usbhc_iface->reserve_default_address == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	usb_speed_t speed = DEV_IPC_GET_ARG1(*call);
+	const int ret = usbhc_iface->reserve_default_address(fun, speed);
+	async_answer_0(callid, ret);
+}
+
+void remote_usbhc_release_default_address(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
+
+	if (usbhc_iface->release_default_address == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const int ret = usbhc_iface->release_default_address(fun);
+	async_answer_0(callid, ret);
+}
+
+static void remote_usbhc_device_enumerate(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
+
+	if (usbhc_iface->device_enumerate == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const unsigned port = DEV_IPC_GET_ARG1(*call);
+	const int ret = usbhc_iface->device_enumerate(fun, port);
+	async_answer_0(callid, ret);
+}
+
+static void remote_usbhc_device_remove(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const usbhc_iface_t *usbhc_iface = (usbhc_iface_t *) iface;
+
+	if (usbhc_iface->device_remove == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const unsigned port = DEV_IPC_GET_ARG1(*call);
+	const int ret = usbhc_iface->device_remove(fun, port);
+	async_answer_0(callid, ret);
+}
+
+static void remote_usbhc_register_endpoint(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	assert(fun);
+	assert(iface);
+	assert(call);
+
+	const usbhc_iface_t *usbhc_iface = iface;
+
+	if (!usbhc_iface->register_endpoint) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	void *buffer = NULL;
+	size_t size = 0;
+	int rc = async_data_write_accept(&buffer, false,
+		sizeof(usb_endpoint_desc_t), sizeof(usb_endpoint_desc_t), 0, &size);
+
+	if (rc != EOK) {
+		free(buffer);
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	usb_endpoint_desc_t *endpoint_desc = (usb_endpoint_desc_t *) buffer;
+	rc = usbhc_iface->register_endpoint(fun, endpoint_desc);
+
+	free(buffer);
+	async_answer_0(callid, rc);
+}
+
+static void remote_usbhc_unregister_endpoint(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	assert(fun);
+	assert(iface);
+	assert(call);
+
+	const usbhc_iface_t *usbhc_iface = iface;
+
+	if (!usbhc_iface->unregister_endpoint) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	void *buffer = NULL;
+	size_t size = 0;
+	int rc = async_data_write_accept(&buffer, false,
+		sizeof(usb_endpoint_desc_t), sizeof(usb_endpoint_desc_t), 0, &size);
+
+	if (rc != EOK) {
+		free(buffer);
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	usb_endpoint_desc_t *endpoint_desc = (usb_endpoint_desc_t *) buffer;
+	usbhc_iface->unregister_endpoint(fun, endpoint_desc);
+
+	free(buffer);
+	async_answer_0(callid, rc);
+}
+
 static void async_transaction_destroy(async_transaction_t *trans)
 {
-	if (trans == NULL)
-		return;
-	
-	if (trans->buffer != NULL)
+	if (trans == NULL) {
+		return;
+	}
+	if (trans->buffer != NULL) {
 		free(trans->buffer);
-	
+	}
+
 	free(trans);
 }
@@ -235,34 +444,31 @@
 }
 
-static void callback_out(int outcome, void *arg)
+static int callback_out(void *arg, int error, size_t transfered_size)
 {
 	async_transaction_t *trans = arg;
 
-	async_answer_0(trans->caller, outcome);
+	const int err = async_answer_0(trans->caller, error);
 
 	async_transaction_destroy(trans);
-}
-
-static void callback_in(int outcome, size_t actual_size, void *arg)
-{
-	async_transaction_t *trans = (async_transaction_t *)arg;
-
-	if (outcome != EOK) {
-		async_answer_0(trans->caller, outcome);
-		if (trans->data_caller) {
+
+	return err;
+}
+
+static int callback_in(void *arg, int error, size_t transfered_size)
+{
+	async_transaction_t *trans = arg;
+
+	if (trans->data_caller) {
+		if (error == EOK) {
+			error = async_data_read_finalize(trans->data_caller,
+			    trans->buffer, transfered_size);
+		} else {
 			async_answer_0(trans->data_caller, EINTR);
 		}
-		async_transaction_destroy(trans);
-		return;
-	}
-
-	if (trans->data_caller) {
-		async_data_read_finalize(trans->data_caller,
-		    trans->buffer, actual_size);
-	}
-
-	async_answer_0(trans->caller, EOK);
-
+	}
+
+	const int err = async_answer_0(trans->caller, error);
 	async_transaction_destroy(trans);
+	return err;
 }
 
@@ -274,12 +480,12 @@
 	assert(call);
 
-	const usbhc_iface_t *hc_iface = iface;
-
-	if (!hc_iface->read) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const usb_target_t target = { .packed = DEV_IPC_GET_ARG1(*call) };
+	const usbhc_iface_t *usbhc_iface = iface;
+
+	if (!usbhc_iface->read) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
 	const uint64_t setup =
 	    ((uint64_t)DEV_IPC_GET_ARG2(*call)) |
@@ -295,4 +501,5 @@
 	if (!async_data_read_receive(&trans->data_caller, &size)) {
 		async_answer_0(callid, EPARTY);
+		async_transaction_destroy(trans);
 		return;
 	}
@@ -306,5 +513,10 @@
 	}
 
-	const int rc = hc_iface->read(
+	const usb_target_t target = {{
+		/* .address is initialized by read itself */
+		.endpoint = ep,
+	}};
+
+	const int rc = usbhc_iface->read(
 	    fun, target, setup, trans->buffer, size, callback_in, trans);
 
@@ -323,12 +535,12 @@
 	assert(call);
 
-	const usbhc_iface_t *hc_iface = iface;
-
-	if (!hc_iface->write) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	const usb_target_t target = { .packed = DEV_IPC_GET_ARG1(*call) };
+	const usbhc_iface_t *usbhc_iface = iface;
+
+	if (!usbhc_iface->write) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const usb_endpoint_t ep = DEV_IPC_GET_ARG1(*call);
 	const size_t data_buffer_len = DEV_IPC_GET_ARG2(*call);
 	const uint64_t setup =
@@ -345,6 +557,5 @@
 	if (data_buffer_len > 0) {
 		const int rc = async_data_write_accept(&trans->buffer, false,
-		    1, USB_MAX_PAYLOAD_SIZE,
-		    0, &size);
+		    1, data_buffer_len, 0, &size);
 
 		if (rc != EOK) {
@@ -355,5 +566,10 @@
 	}
 
-	const int rc = hc_iface->write(
+	const usb_target_t target = {{
+		/* .address is initialized by write itself */
+		.endpoint = ep,
+	}};
+
+	const int rc = usbhc_iface->write(
 	    fun, target, setup, trans->buffer, size, callback_out, trans);
 
Index: uspace/lib/drv/include/usb_iface.h
===================================================================
--- uspace/lib/drv/include/usb_iface.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/drv/include/usb_iface.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -32,5 +32,5 @@
  */
 /** @file
- * @brief USB interface definition.
+ * @brief USB device interface definition.
  */
 
@@ -51,4 +51,6 @@
 	/** USB 2.0 high speed (480Mbits/s). */
 	USB_SPEED_HIGH,
+	/** USB 3.0 super speed (5Gbits/s). */
+	USB_SPEED_SUPER,
 	/** Psuedo-speed serving as a boundary. */
 	USB_SPEED_MAX
@@ -64,4 +66,11 @@
  */
 typedef int16_t usb_address_t;
+
+/** USB address for the purposes of Transaction Translation.
+ */
+typedef struct {
+	usb_address_t address;
+	unsigned port;
+} usb_tt_address_t;
 
 /** USB transfer type. */
@@ -91,4 +100,34 @@
 } usb_target_t;
 
+/** Description of usb endpoint.
+ */
+typedef struct {
+	/** Endpoint number. */
+	usb_endpoint_t endpoint_no;
+
+	/** Endpoint transfer type. */
+	usb_transfer_type_t transfer_type;
+
+	/** Endpoint direction. */
+	usb_direction_t direction;
+
+	/** Maximum packet size for the endpoint. */
+	size_t max_packet_size;
+
+	/** Number of packets per frame/uframe.
+	 * Only valid for HS INT and ISO transfers. All others should set to 1*/
+	unsigned packets;
+
+	struct {
+		unsigned polling_interval;
+	} usb2;
+
+	struct {
+		unsigned max_burst;
+		unsigned max_streams;
+	} usb3;
+} usb_endpoint_desc_t;
+
+
 extern usb_dev_session_t *usb_dev_connect(devman_handle_t);
 extern usb_dev_session_t *usb_dev_connect_to_self(ddf_dev_t *);
@@ -98,45 +137,8 @@
 extern int usb_get_my_device_handle(async_exch_t *, devman_handle_t *);
 
-extern int usb_reserve_default_address(async_exch_t *, usb_speed_t);
-extern int usb_release_default_address(async_exch_t *);
-
-extern int usb_device_enumerate(async_exch_t *, unsigned port);
-extern int usb_device_remove(async_exch_t *, unsigned port);
-
-extern int usb_register_endpoint(async_exch_t *, usb_endpoint_t,
-    usb_transfer_type_t, usb_direction_t, size_t, unsigned, unsigned);
-extern int usb_unregister_endpoint(async_exch_t *, usb_endpoint_t,
-    usb_direction_t);
-extern int usb_read(async_exch_t *, usb_endpoint_t, uint64_t, void *, size_t,
-    size_t *);
-extern int usb_write(async_exch_t *, usb_endpoint_t, uint64_t, const void *,
-    size_t);
-
-/** Callback for outgoing transfer. */
-typedef void (*usb_iface_transfer_out_callback_t)(int, void *);
-
-/** Callback for incoming transfer. */
-typedef void (*usb_iface_transfer_in_callback_t)(int, size_t, void *);
-
 /** USB device communication interface. */
 typedef struct {
 	int (*get_my_interface)(ddf_fun_t *, int *);
 	int (*get_my_device_handle)(ddf_fun_t *, devman_handle_t *);
-
-	int (*reserve_default_address)(ddf_fun_t *, usb_speed_t);
-	int (*release_default_address)(ddf_fun_t *);
-
-	int (*device_enumerate)(ddf_fun_t *, unsigned);
-	int (*device_remove)(ddf_fun_t *, unsigned);
-
-	int (*register_endpoint)(ddf_fun_t *, usb_endpoint_t,
-	    usb_transfer_type_t, usb_direction_t, size_t, unsigned, unsigned);
-	int (*unregister_endpoint)(ddf_fun_t *, usb_endpoint_t,
-	    usb_direction_t);
-
-	int (*read)(ddf_fun_t *, usb_endpoint_t, uint64_t, uint8_t *, size_t,
-	    usb_iface_transfer_in_callback_t, void *);
-	int (*write)(ddf_fun_t *, usb_endpoint_t, uint64_t, const uint8_t *,
-	    size_t, usb_iface_transfer_out_callback_t, void *);
 } usb_iface_t;
 
Index: uspace/lib/drv/include/usbhc_iface.h
===================================================================
--- uspace/lib/drv/include/usbhc_iface.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/drv/include/usbhc_iface.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2010 Vojtech Horky
- * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2017 Ondrej Hlavaty
  * All rights reserved.
  *
@@ -32,7 +32,7 @@
  * @{
  */
-
 /** @file
- * @brief USB host controller interface definition.
+ * @brief USB host controler interface definition. This is the interface of
+ * USB host controller function, which can be used by usb device drivers.
  */
 
@@ -41,26 +41,41 @@
 
 #include "ddf/driver.h"
-#include <usb_iface.h>
-#include <stdbool.h>
+#include "usb_iface.h"
+#include <async.h>
 
-extern int usbhc_read(async_exch_t *, usb_address_t, usb_endpoint_t,
-    uint64_t, void *, size_t, size_t *);
-extern int usbhc_write(async_exch_t *, usb_address_t, usb_endpoint_t,
-    uint64_t, const void *, size_t);
+extern int usbhc_reserve_default_address(async_exch_t *, usb_speed_t);
+extern int usbhc_release_default_address(async_exch_t *);
 
-/** Callback for outgoing transfer. */
-typedef void (*usbhc_iface_transfer_out_callback_t)(int, void *);
+extern int usbhc_device_enumerate(async_exch_t *, unsigned port);
+extern int usbhc_device_remove(async_exch_t *, unsigned port);
 
-/** Callback for incoming transfer. */
-typedef void (*usbhc_iface_transfer_in_callback_t)(int, size_t, void *);
+extern int usbhc_register_endpoint(async_exch_t *, usb_endpoint_desc_t *);
+extern int usbhc_unregister_endpoint(async_exch_t *, usb_endpoint_desc_t *);
+extern int usbhc_read(async_exch_t *, usb_endpoint_t, uint64_t, void *, size_t,
+    size_t *);
+extern int usbhc_write(async_exch_t *, usb_endpoint_t, uint64_t, const void *,
+    size_t);
 
-/** USB host controller communication interface. */
+/** Callback for outgoing transfer */
+typedef int (*usbhc_iface_transfer_callback_t)(void *, int, size_t);
+
+/** USB device communication interface. */
 typedef struct {
-	int (*read)(ddf_fun_t *, usb_target_t, uint64_t, uint8_t *, size_t,
-	    usbhc_iface_transfer_in_callback_t, void *);
-	int (*write)(ddf_fun_t *, usb_target_t, uint64_t, const uint8_t *,
-	    size_t, usbhc_iface_transfer_out_callback_t, void *);
+	int (*reserve_default_address)(ddf_fun_t *, usb_speed_t);
+	int (*release_default_address)(ddf_fun_t *);
+
+	int (*device_enumerate)(ddf_fun_t *, unsigned);
+	int (*device_remove)(ddf_fun_t *, unsigned);
+
+	int (*register_endpoint)(ddf_fun_t *, usb_endpoint_desc_t *);
+	int (*unregister_endpoint)(ddf_fun_t *, usb_endpoint_desc_t *);
+
+	int (*read)(ddf_fun_t *, usb_target_t,
+		uint64_t, char *, size_t,
+		usbhc_iface_transfer_callback_t, void *);
+	int (*write)(ddf_fun_t *, usb_target_t,
+		uint64_t, const char *, size_t,
+		usbhc_iface_transfer_callback_t, void *);
 } usbhc_iface_t;
-
 
 #endif
Index: uspace/lib/pcm/src/format.c
===================================================================
--- uspace/lib/pcm/src/format.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/pcm/src/format.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -41,9 +41,4 @@
 
 #include "format.h"
-
-#define uint8_t_le2host(x) (x)
-#define host2uint8_t_le(x) (x)
-#define uint8_t_be2host(x) (x)
-#define host2uint8_t_be(x) (x)
 
 #define int8_t_le2host(x) (x)
Index: uspace/lib/usb/include/usb/descriptor.h
===================================================================
--- uspace/lib/usb/include/usb/descriptor.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usb/include/usb/descriptor.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -49,4 +49,10 @@
 	USB_DESCTYPE_OTHER_SPEED_CONFIGURATION = 7,
 	USB_DESCTYPE_INTERFACE_POWER = 8,
+	/* USB 3.0 types */
+	USB_DESCTYPE_OTG = 9,
+	USB_DESCTYPE_DEBUG = 0xa,
+	USB_DESCTYPE_IFACE_ASSOC = 0xb,
+	USB_DESCTYPE_BOS = 0xf,
+	USB_DESCTYPE_DEVICE_CAP = 0x10,
 	/* Class specific */
 	USB_DESCTYPE_HID = 0x21,
@@ -54,4 +60,5 @@
 	USB_DESCTYPE_HID_PHYSICAL = 0x23,
 	USB_DESCTYPE_HUB = 0x29,
+	USB_DESCTYPE_SSPEED_EP_COMPANION = 0x30
 	/* USB_DESCTYPE_ = */
 } usb_descriptor_type_t;
@@ -217,4 +224,34 @@
 } __attribute__ ((packed)) usb_standard_endpoint_descriptor_t;
 
+/** Superspeed USB endpoint companion descriptor.
+ * See USB 3 specification, section 9.6.7.
+ */
+typedef struct {
+	/** Size of this descriptor in bytes */
+	uint8_t length;
+	/** Descriptor type (USB_DESCTYPE_SSPEED_EP_COMPANION). */
+	uint8_t descriptor_type;
+	/** The maximum number of packets the endpoint can send
+	 * or receive as part of a burst. Valid values are from 0 to 15.
+	 * The endpoint can only burst max_burst + 1 packets at a time.
+	 */
+	uint8_t max_burst;
+	/** Valid only for bulk and isochronous endpoints.
+	 * For bulk endpoints, this field contains the amount of streams
+	 * supported by the endpoint.
+	 * For isochronous endpoints, this field contains either maximum
+	 * number of packets supported within a service interval, or
+	 * whether an isochronous endpoint companion descriptor follows.
+	 */
+	uint8_t attributes;
+#define SS_COMPANION_MAX_STREAMS(attributes) \
+	(attributes & 0x1f)
+	/** The total number of bytes this endpoint will transfer
+	 * every service interval (SI).
+	 * This field is only valid for periodic endpoints.
+	 */
+	uint16_t bytes_per_interval;
+} __attribute__ ((packed)) usb_superspeed_endpoint_companion_descriptor_t;
+
 /** Part of standard USB HID descriptor specifying one class descriptor.
  *
Index: uspace/lib/usb/include/usb/request.h
===================================================================
--- uspace/lib/usb/include/usb/request.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usb/include/usb/request.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -110,6 +110,37 @@
 static_assert(sizeof(usb_device_request_setup_packet_t) == 8);
 
-int usb_request_needs_toggle_reset(
+/** How many toggles need to be reset */
+typedef enum {
+	RESET_NONE,
+	RESET_EP,
+	RESET_ALL
+} toggle_reset_mode_t;
+
+toggle_reset_mode_t usb_request_get_toggle_reset_mode(
     const usb_device_request_setup_packet_t *request);
+
+#define GET_DEVICE_DESC(size) \
+{ \
+	.request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST \
+	    | (USB_REQUEST_TYPE_STANDARD << 5) \
+	    | USB_REQUEST_RECIPIENT_DEVICE, \
+	.request = USB_DEVREQ_GET_DESCRIPTOR, \
+	.value = uint16_host2usb(USB_DESCTYPE_DEVICE << 8), \
+	.index = uint16_host2usb(0), \
+	.length = uint16_host2usb(size), \
+};
+
+#define SET_ADDRESS(address) \
+{ \
+	.request_type = SETUP_REQUEST_TYPE_HOST_TO_DEVICE \
+	    | (USB_REQUEST_TYPE_STANDARD << 5) \
+	    | USB_REQUEST_RECIPIENT_DEVICE, \
+	.request = USB_DEVREQ_SET_ADDRESS, \
+	.value = uint16_host2usb(address), \
+	.index = uint16_host2usb(0), \
+	.length = uint16_host2usb(0), \
+};
+
+#define CTRL_PIPE_MIN_PACKET_SIZE 8
 
 #endif
Index: uspace/lib/usb/src/usb.c
===================================================================
--- uspace/lib/usb/src/usb.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usb/src/usb.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -44,4 +44,5 @@
 	[USB_SPEED_FULL] = "full",
 	[USB_SPEED_HIGH] = "high",
+	[USB_SPEED_SUPER] = "super",
 };
 
@@ -127,5 +128,5 @@
  *
  */
-int usb_request_needs_toggle_reset(
+toggle_reset_mode_t usb_request_get_toggle_reset_mode(
     const usb_device_request_setup_packet_t *request)
 {
@@ -138,5 +139,5 @@
 		if ((request->request_type == 0x2) &&
 		    (request->value == USB_FEATURE_ENDPOINT_HALT))
-			return uint16_usb2host(request->index);
+			return RESET_EP;
 		break;
 	case USB_DEVREQ_SET_CONFIGURATION:
@@ -148,10 +149,11 @@
 		 * interface of an already setup device. */
 		if (!(request->request_type & SETUP_REQUEST_TYPE_DEVICE_TO_HOST))
-			return 0;
+			return RESET_ALL;
 		break;
 	default:
 		break;
 	}
-	return -1;
+
+	return RESET_NONE;
 }
 
Index: uspace/lib/usbdev/include/usb/dev/driver.h
===================================================================
--- uspace/lib/usbdev/include/usb/dev/driver.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbdev/include/usb/dev/driver.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -48,4 +48,8 @@
 	/** Callback when a device was removed from the system. */
 	int (*device_gone)(usb_device_t *);
+	/** Callback asking the driver to online a specific function. */
+	int (*function_online)(ddf_fun_t *);
+	/** Callback asking the driver to offline a specific function. */
+	int (*function_offline)(ddf_fun_t *);
 } usb_driver_ops_t;
 
Index: uspace/lib/usbdev/include/usb/dev/pipes.h
===================================================================
--- uspace/lib/usbdev/include/usb/dev/pipes.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbdev/include/usb/dev/pipes.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -50,25 +50,10 @@
  */
 typedef struct {
-	/** Endpoint number. */
-	usb_endpoint_t endpoint_no;
-
-	/** Endpoint transfer type. */
-	usb_transfer_type_t transfer_type;
-
-	/** Endpoint direction. */
-	usb_direction_t direction;
-
-	/** Maximum packet size for the endpoint. */
-	size_t max_packet_size;
-
-	/** Number of packets per frame/uframe.
-	 * Only valid for HS INT and ISO transfers. All others should set to 1*/
-	unsigned packets;
-
+	/** Endpoint description */
+	usb_endpoint_desc_t desc;
 	/** Whether to automatically reset halt on the endpoint.
 	 * Valid only for control endpoint zero.
 	 */
 	bool auto_reset_halt;
-
 	/** The connection used for sending the data. */
 	usb_dev_session_t *bus_session;
@@ -103,4 +88,6 @@
 	/** Found descriptor fitting the description. */
 	const usb_standard_endpoint_descriptor_t *descriptor;
+	/** Relevant superspeed companion descriptor. */
+	const usb_superspeed_endpoint_companion_descriptor_t *companion_descriptor;
 	/** Interface descriptor the endpoint belongs to. */
 	const usb_standard_interface_descriptor_t *interface;
@@ -110,5 +97,5 @@
 
 int usb_pipe_initialize(usb_pipe_t *, usb_endpoint_t, usb_transfer_type_t,
-    size_t, usb_direction_t, unsigned, usb_dev_session_t *);
+    size_t, usb_direction_t, unsigned, unsigned, unsigned, usb_dev_session_t *);
 int usb_pipe_initialize_default_control(usb_pipe_t *, usb_dev_session_t *);
 
Index: uspace/lib/usbdev/src/devdrv.c
===================================================================
--- uspace/lib/usbdev/src/devdrv.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbdev/src/devdrv.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -56,11 +56,11 @@
 	/** Connection to device on USB bus */
 	usb_dev_session_t *bus_session;
-	
+
 	/** devman handle */
 	devman_handle_t handle;
-	
+
 	/** The default control pipe. */
 	usb_pipe_t ctrl_pipe;
-	
+
 	/** Other endpoint pipes.
 	 *
@@ -69,8 +69,8 @@
 	 */
 	usb_endpoint_mapping_t *pipes;
-	
+
 	/** Number of other endpoint pipes. */
 	size_t pipes_count;
-	
+
 	/** Current interface.
 	 *
@@ -79,14 +79,14 @@
 	 */
 	int interface_no;
-	
+
 	/** Alternative interfaces. */
 	usb_alternate_interfaces_t alternate_interfaces;
-	
+
 	/** Some useful descriptors for USB device. */
 	usb_device_descriptors_t descriptors;
-	
+
 	/** Generic DDF device backing this one. DO NOT TOUCH! */
 	ddf_dev_t *ddf_dev;
-	
+
 	/** Custom driver data.
 	 *
@@ -146,5 +146,5 @@
 		return rc;
 	}
-	
+
 	/* Change current alternative */
 	usb_dev->alternate_interfaces.current = alternate_setting;
@@ -296,5 +296,5 @@
 	assert(usb_dev);
 	assert(usb_dev->pipes || usb_dev->pipes_count == 0);
-	
+
 	/* Destroy the pipes. */
 	for (size_t i = 0; i < usb_dev->pipes_count; ++i) {
@@ -304,5 +304,5 @@
 			usb_pipe_unregister(&usb_dev->pipes[i].pipe);
 	}
-	
+
 	free(usb_dev->pipes);
 	usb_dev->pipes = NULL;
@@ -332,5 +332,5 @@
 	assert(usb_dev);
 	for (unsigned i = 0; i < usb_dev->pipes_count; ++i) {
-		if (usb_dev->pipes[i].pipe.endpoint_no == ep)
+		if (usb_dev->pipes[i].pipe.desc.endpoint_no == ep)
 			return &usb_dev->pipes[i];
 	}
@@ -462,9 +462,9 @@
 	assert(handle);
 	assert(iface_no);
-	
+
 	async_exch_t *exch = async_exchange_begin(sess);
 	if (!exch)
 		return EPARTY;
-	
+
 	int ret = usb_get_my_device_handle(exch, handle);
 	if (ret == EOK) {
@@ -475,5 +475,5 @@
 		}
 	}
-	
+
 	async_exchange_end(exch);
 	return ret;
@@ -502,5 +502,5 @@
 		return ENOMEM;
 	}
-	
+
 	return usb_device_init(usb_dev, ddf_dev, desc, err, h, iface_no);
 }
Index: uspace/lib/usbdev/src/devpoll.c
===================================================================
--- uspace/lib/usbdev/src/devpoll.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbdev/src/devpoll.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -96,5 +96,5 @@
 		    (int) mapping->interface->interface_subclass,
 		    (int) mapping->interface->interface_protocol,
-		    data->request_size, pipe->max_packet_size);
+		    data->request_size, pipe->desc.max_packet_size);
 	}
 
@@ -128,5 +128,5 @@
 			usb_request_clear_endpoint_halt(
 			    usb_device_get_default_pipe(data->dev),
-			    pipe->endpoint_no);
+			    pipe->desc.endpoint_no);
 		}
 
@@ -156,5 +156,5 @@
 
 		/* Take a rest before next request. */
-		
+
 		// FIXME TODO: This is broken, the time is in ms not us.
 		// but first we need to fix drivers to actually stop using this,
@@ -216,9 +216,9 @@
 	if (request_size == 0)
 		return EINVAL;
-	
-	if (!epm || (epm->pipe.transfer_type != USB_TRANSFER_INTERRUPT) ||
-	    (epm->pipe.direction != USB_DIRECTION_IN))
+
+	if (!epm || (epm->pipe.desc.transfer_type != USB_TRANSFER_INTERRUPT) ||
+	    (epm->pipe.desc.direction != USB_DIRECTION_IN))
 		return EINVAL;
-	
+
 
 	polling_data_t *polling_data = malloc(sizeof(polling_data_t));
Index: uspace/lib/usbdev/src/driver.c
===================================================================
--- uspace/lib/usbdev/src/driver.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbdev/src/driver.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -117,8 +117,42 @@
 }
 
+/** Callback when the driver is asked to online a specific function.
+ *
+ * This callback is a wrapper for USB specific version of @c fun_online.
+ *
+ * @param gen_dev Device function structure as prepared by DDF.
+ * @return Error code.
+ */
+static int generic_function_online(ddf_fun_t *fun)
+{
+	assert(driver);
+	assert(driver->ops);
+	if (driver->ops->function_online == NULL)
+		return ENOTSUP;
+	return driver->ops->function_online(fun);
+}
+
+/** Callback when the driver is asked to offline a specific function.
+ *
+ * This callback is a wrapper for USB specific version of @c fun_offline.
+ *
+ * @param gen_dev Device function structure as prepared by DDF.
+ * @return Error code.
+ */
+static int generic_function_offline(ddf_fun_t *fun)
+{
+	assert(driver);
+	assert(driver->ops);
+	if (driver->ops->function_offline == NULL)
+		return ENOTSUP;
+	return driver->ops->function_offline(fun);
+}
+
 static driver_ops_t generic_driver_ops = {
 	.dev_add = generic_device_add,
 	.dev_remove = generic_device_remove,
 	.dev_gone = generic_device_gone,
+	.fun_online = generic_function_online,
+	.fun_offline = generic_function_offline,
 };
 static driver_t generic_driver = {
Index: uspace/lib/usbdev/src/pipes.c
===================================================================
--- uspace/lib/usbdev/src/pipes.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbdev/src/pipes.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -36,5 +36,5 @@
 #include <usb/dev/request.h>
 #include <usb/usb.h>
-#include <usb_iface.h>
+#include <usbhc_iface.h>
 
 #include <assert.h>
@@ -51,5 +51,5 @@
 	assert(pipe != NULL);
 
-	if (!pipe->auto_reset_halt || (pipe->endpoint_no != 0)) {
+	if (!pipe->auto_reset_halt || (pipe->desc.endpoint_no != 0)) {
 		return;
 	}
@@ -88,6 +88,6 @@
 	}
 
-	if ((pipe->direction != USB_DIRECTION_BOTH)
-	    || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
+	if ((pipe->desc.direction != USB_DIRECTION_BOTH)
+	    || (pipe->desc.transfer_type != USB_TRANSFER_CONTROL)) {
 		return EBADF;
 	}
@@ -98,5 +98,5 @@
 	async_exch_t *exch = async_exchange_begin(pipe->bus_session);
 	size_t act_size = 0;
-	const int rc = usb_read(exch, pipe->endpoint_no, setup_packet, buffer,
+	const int rc = usbhc_read(exch, pipe->desc.endpoint_no, setup_packet, buffer,
 	    buffer_size, &act_size);
 	async_exchange_end(exch);
@@ -142,6 +142,6 @@
 	}
 
-	if ((pipe->direction != USB_DIRECTION_BOTH)
-	    || (pipe->transfer_type != USB_TRANSFER_CONTROL)) {
+	if ((pipe->desc.direction != USB_DIRECTION_BOTH)
+	    || (pipe->desc.transfer_type != USB_TRANSFER_CONTROL)) {
 		return EBADF;
 	}
@@ -151,6 +151,6 @@
 
 	async_exch_t *exch = async_exchange_begin(pipe->bus_session);
-	const int rc = usb_write(exch,
-	    pipe->endpoint_no, setup_packet, buffer, buffer_size);
+	const int rc = usbhc_write(exch,
+	    pipe->desc.endpoint_no, setup_packet, buffer, buffer_size);
 	async_exchange_end(exch);
 
@@ -183,15 +183,15 @@
 	}
 
-	if (pipe->direction != USB_DIRECTION_IN) {
-		return EBADF;
-	}
-
-	if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
+	if (pipe->desc.direction != USB_DIRECTION_IN) {
+		return EBADF;
+	}
+
+	if (pipe->desc.transfer_type == USB_TRANSFER_CONTROL) {
 		return EBADF;
 	}
 
 	/* Isochronous transfer are not supported (yet) */
-	if (pipe->transfer_type != USB_TRANSFER_INTERRUPT &&
-	    pipe->transfer_type != USB_TRANSFER_BULK)
+	if (pipe->desc.transfer_type != USB_TRANSFER_INTERRUPT &&
+	    pipe->desc.transfer_type != USB_TRANSFER_BULK)
 	    return ENOTSUP;
 
@@ -199,5 +199,5 @@
 	size_t act_size = 0;
 	const int rc =
-	    usb_read(exch, pipe->endpoint_no, 0, buffer, size, &act_size);
+	    usbhc_read(exch, pipe->desc.endpoint_no, 0, buffer, size, &act_size);
 	async_exchange_end(exch);
 
@@ -224,19 +224,19 @@
 	}
 
-	if (pipe->direction != USB_DIRECTION_OUT) {
-		return EBADF;
-	}
-
-	if (pipe->transfer_type == USB_TRANSFER_CONTROL) {
+	if (pipe->desc.direction != USB_DIRECTION_OUT) {
+		return EBADF;
+	}
+
+	if (pipe->desc.transfer_type == USB_TRANSFER_CONTROL) {
 		return EBADF;
 	}
 
 	/* Isochronous transfer are not supported (yet) */
-	if (pipe->transfer_type != USB_TRANSFER_INTERRUPT &&
-	    pipe->transfer_type != USB_TRANSFER_BULK)
+	if (pipe->desc.transfer_type != USB_TRANSFER_INTERRUPT &&
+	    pipe->desc.transfer_type != USB_TRANSFER_BULK)
 	    return ENOTSUP;
 
 	async_exch_t *exch = async_exchange_begin(pipe->bus_session);
-	const int rc = usb_write(exch, pipe->endpoint_no, 0, buffer, size);
+	const int rc = usbhc_write(exch, pipe->desc.endpoint_no, 0, buffer, size);
 	async_exchange_end(exch);
 	return rc;
@@ -254,13 +254,17 @@
 int usb_pipe_initialize(usb_pipe_t *pipe, usb_endpoint_t endpoint_no,
     usb_transfer_type_t transfer_type, size_t max_packet_size,
-    usb_direction_t direction, unsigned packets, usb_dev_session_t *bus_session)
-{
-	assert(pipe);
-
-	pipe->endpoint_no = endpoint_no;
-	pipe->transfer_type = transfer_type;
-	pipe->packets = packets;
-	pipe->max_packet_size = max_packet_size;
-	pipe->direction = direction;
+    usb_direction_t direction, unsigned packets,
+    unsigned max_burst, unsigned max_streams, usb_dev_session_t *bus_session)
+{
+	// FIXME refactor this function
+	assert(pipe);
+
+	pipe->desc.endpoint_no = endpoint_no;
+	pipe->desc.transfer_type = transfer_type;
+	pipe->desc.packets = packets;
+	pipe->desc.max_packet_size = max_packet_size;
+	pipe->desc.direction = direction;
+	pipe->desc.usb3.max_burst = max_burst;
+	pipe->desc.usb3.max_streams = max_streams;
 	pipe->auto_reset_halt = false;
 	pipe->bus_session = bus_session;
@@ -280,5 +284,5 @@
 
 	const int rc = usb_pipe_initialize(pipe, 0, USB_TRANSFER_CONTROL,
-	    CTRL_PIPE_MIN_PACKET_SIZE, USB_DIRECTION_BOTH, 1, bus_session);
+	    CTRL_PIPE_MIN_PACKET_SIZE, USB_DIRECTION_BOTH, 1, 0, 0, bus_session);
 
 	pipe->auto_reset_halt = true;
@@ -297,10 +301,12 @@
 	assert(pipe);
 	assert(pipe->bus_session);
+
+	pipe->desc.usb2.polling_interval = interval;
 	async_exch_t *exch = async_exchange_begin(pipe->bus_session);
 	if (!exch)
 		return ENOMEM;
-	const int ret = usb_register_endpoint(exch, pipe->endpoint_no,
-	    pipe->transfer_type, pipe->direction, pipe->max_packet_size,
-	    pipe->packets, interval);
+
+	const int ret = usbhc_register_endpoint(exch, &pipe->desc);
+
 	async_exchange_end(exch);
 	return ret;
@@ -319,6 +325,7 @@
 	if (!exch)
 		return ENOMEM;
-	const int ret = usb_unregister_endpoint(exch, pipe->endpoint_no,
-	    pipe->direction);
+
+	const int ret = usbhc_unregister_endpoint(exch, &pipe->desc);
+
 	async_exchange_end(exch);
 	return ret;
Index: uspace/lib/usbdev/src/pipesinit.c
===================================================================
--- uspace/lib/usbdev/src/pipesinit.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbdev/src/pipesinit.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -38,4 +38,5 @@
 #include <usb/dev/request.h>
 #include <usb/usb.h>
+#include <usb/debug.h>
 #include <usb/descriptor.h>
 
@@ -59,4 +60,5 @@
 	NESTING(INTERFACE, HID),
 	NESTING(HID, HID_REPORT),
+	NESTING(ENDPOINT, SSPEED_EP_COMPANION),
 	LAST_NESTING
 };
@@ -70,4 +72,14 @@
 {
 	return descriptor[1] == USB_DESCTYPE_ENDPOINT;
+}
+
+/** Tells whether given descriptor is of superspeed companion type.
+ *
+ * @param descriptor Descriptor in question.
+ * @return Whether the given descriptor is superspeed companion descriptor.
+ */
+static inline bool is_superspeed_companion_descriptor(const uint8_t *descriptor)
+{
+	return descriptor[1] == USB_DESCTYPE_SSPEED_EP_COMPANION;
 }
 
@@ -150,4 +162,5 @@
  * @param interface Interface descriptor under which belongs the @p endpoint.
  * @param endpoint Endpoint descriptor.
+ * @param companion Superspeed companion descriptor.
  * @return Error code.
  */
@@ -156,4 +169,5 @@
     usb_standard_interface_descriptor_t *interface,
     usb_standard_endpoint_descriptor_t *endpoint_desc,
+    usb_superspeed_endpoint_companion_descriptor_t *companion_desc,
     usb_dev_session_t *bus_session)
 {
@@ -194,11 +208,18 @@
 	}
 
+	unsigned max_burst = 0;
+	unsigned max_streams = 0;
+	if(companion_desc) {
+		max_burst = companion_desc->max_burst;
+		max_streams = SS_COMPANION_MAX_STREAMS(companion_desc->attributes);
+	}
+
 	int rc = usb_pipe_initialize(&ep_mapping->pipe,
 	    ep_no, description.transfer_type,
 	    ED_MPS_PACKET_SIZE_GET(
 	        uint16_usb2host(endpoint_desc->max_packet_size)),
-	    description.direction,
-	    ED_MPS_TRANS_OPPORTUNITIES_GET(
-	        uint16_usb2host(endpoint_desc->max_packet_size)), bus_session);
+	    description.direction, ED_MPS_TRANS_OPPORTUNITIES_GET(
+	        uint16_usb2host(endpoint_desc->max_packet_size)),
+	    max_burst, max_streams, bus_session);
 	if (rc != EOK) {
 		return rc;
@@ -207,4 +228,5 @@
 	ep_mapping->present = true;
 	ep_mapping->descriptor = endpoint_desc;
+	ep_mapping->companion_descriptor = companion_desc;
 	ep_mapping->interface = interface;
 
@@ -235,4 +257,12 @@
 	do {
 		if (is_endpoint_descriptor(descriptor)) {
+			/* Check if companion descriptor is present too, it should immediatelly follow. */
+			const uint8_t *companion_desc = usb_dp_get_nested_descriptor(parser,
+				parser_data, descriptor);
+			if (companion_desc && !is_superspeed_companion_descriptor(companion_desc)) {
+				/* Not what we wanted, don't pass it further. */
+				companion_desc = NULL;
+			}
+
 			(void) process_endpoint(mapping, mapping_count,
 			    (usb_standard_interface_descriptor_t *)
@@ -240,4 +270,6 @@
 			    (usb_standard_endpoint_descriptor_t *)
 			        descriptor,
+			    (usb_superspeed_endpoint_companion_descriptor_t *)
+			        companion_desc,
 			    bus_session);
 		}
@@ -288,5 +320,5 @@
 	if (config_descriptor == NULL)
 		return EBADMEM;
-	
+
 	if (config_descriptor_size <
 	    sizeof(usb_standard_configuration_descriptor_t)) {
@@ -343,7 +375,7 @@
 	static_assert(DEV_DESCR_MAX_PACKET_SIZE_OFFSET < CTRL_PIPE_MIN_PACKET_SIZE);
 
-	if ((pipe->direction != USB_DIRECTION_BOTH) ||
-	    (pipe->transfer_type != USB_TRANSFER_CONTROL) ||
-	    (pipe->endpoint_no != 0)) {
+	if ((pipe->desc.direction != USB_DIRECTION_BOTH) ||
+	    (pipe->desc.transfer_type != USB_TRANSFER_CONTROL) ||
+	    (pipe->desc.endpoint_no != 0)) {
 		return EINVAL;
 	}
@@ -369,5 +401,5 @@
 	}
 
-	pipe->max_packet_size
+	pipe->desc.max_packet_size
 	    = dev_descr_start[DEV_DESCR_MAX_PACKET_SIZE_OFFSET];
 
Index: uspace/lib/usbdev/src/request.c
===================================================================
--- uspace/lib/usbdev/src/request.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbdev/src/request.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -844,5 +844,5 @@
 	}
 	return usb_request_clear_endpoint_halt(ctrl_pipe,
-	    target_pipe->endpoint_no);
+	    target_pipe->desc.endpoint_no);
 }
 
@@ -858,5 +858,5 @@
 {
 	uint16_t status_tmp;
-	uint16_t pipe_index = (uint16_t) pipe->endpoint_no;
+	uint16_t pipe_index = (uint16_t) pipe->desc.endpoint_no;
 	int rc = usb_request_get_status(ctrl_pipe,
 	    USB_REQUEST_RECIPIENT_ENDPOINT, uint16_host2usb(pipe_index),
Index: uspace/lib/usbhost/Makefile
===================================================================
--- uspace/lib/usbhost/Makefile	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbhost/Makefile	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -37,5 +37,7 @@
 	src/endpoint.c \
 	src/hcd.c \
-	src/usb_bus.c \
+	src/bus.c \
+	src/usb2_bus.c \
+	src/bandwidth.c \
 	src/usb_transfer_batch.c
 
Index: uspace/lib/usbhost/include/usb/host/bandwidth.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/bandwidth.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/lib/usbhost/include/usb/host/bandwidth.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -0,0 +1,60 @@
+/*
+ * 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 libusbhost
+ * @{
+ */
+/** @file
+ *
+ * Bandwidth calculation functions. Shared among uhci, ohci and ehci drivers.
+ */
+
+#ifndef LIBUSBHOST_HOST_BANDWIDTH_H
+#define LIBUSBHOST_HOST_BANDWIDTH_H
+
+#include <usb/usb.h>
+
+#include <stddef.h>
+
+/** Bytes per second in FULL SPEED */
+#define BANDWIDTH_TOTAL_USB11 (12000000 / 8)
+/** 90% of total bandwidth is available for periodic transfers */
+#define BANDWIDTH_AVAILABLE_USB11 ((BANDWIDTH_TOTAL_USB11 / 10) * 9)
+
+//TODO: Implement
+#define BANDWIDTH_AVAILABLE_USB20  1
+
+typedef struct endpoint endpoint_t;
+
+extern size_t bandwidth_count_usb11(endpoint_t *, size_t);
+
+extern size_t bandwidth_count_usb20(endpoint_t *, size_t);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/usbhost/include/usb/host/bus.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/bus.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/lib/usbhost/include/usb/host/bus.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -0,0 +1,159 @@
+/*
+ * 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
+ * Virtual base for usb bus implementations.
+ *
+ * The purpose of this structure is to keep information about connected devices
+ * and endpoints, manage available bandwidth and the toggle bit flipping.
+ *
+ * The generic implementation is provided for USB 1 and 2 in usb2_bus.c. Some
+ * details in [OUE]HCI are solved through overriding some functions. XHCI does
+ * not need the bookkeeping functionality, because addresses are managed by HC
+ * itself.
+ */
+#ifndef LIBUSBHOST_HOST_BUS_H
+#define LIBUSBHOST_HOST_BUS_H
+
+#include <usb/usb.h>
+#include <usb/request.h>
+
+#include <assert.h>
+#include <fibril_synch.h>
+#include <stdbool.h>
+
+typedef struct hcd hcd_t;
+typedef struct endpoint endpoint_t;
+typedef struct bus bus_t;
+typedef struct ddf_fun ddf_fun_t;
+typedef struct usb_transfer_batch usb_transfer_batch_t;
+
+typedef struct device {
+	/* Device tree keeping */
+	link_t link;
+	list_t devices;
+	fibril_mutex_t guard;
+
+	/* Associated DDF function, if any */
+	ddf_fun_t *fun;
+
+	/* Invalid for the roothub device */
+	unsigned port;
+	struct device *hub;
+
+	/* Transaction translator */
+	usb_tt_address_t tt;
+
+	/* The following are not set by the library */
+	usb_speed_t speed;
+	usb_address_t address;
+
+	/* This structure is meant to be extended by overriding. */
+} device_t;
+
+typedef struct {
+	int (*enumerate_device)(bus_t *, hcd_t *, device_t *);
+	int (*remove_device)(bus_t *, hcd_t *, device_t *);
+
+	int (*online_device)(bus_t *, hcd_t *, device_t *);			/**< Optional */
+	int (*offline_device)(bus_t *, hcd_t *, device_t *);			/**< Optional */
+
+	/* The following operations are protected by a bus guard. */
+	endpoint_t *(*create_endpoint)(bus_t *);
+	int (*register_endpoint)(bus_t *, device_t *, endpoint_t *, const usb_endpoint_desc_t *);
+	int (*unregister_endpoint)(bus_t *, endpoint_t *);
+	endpoint_t *(*find_endpoint)(bus_t *, device_t*, usb_target_t, usb_direction_t);
+	void (*destroy_endpoint)(endpoint_t *);			/**< Optional */
+	bool (*endpoint_get_toggle)(endpoint_t *);		/**< Optional */
+	void (*endpoint_set_toggle)(endpoint_t *, bool);	/**< Optional */
+
+	int (*request_address)(bus_t *, usb_address_t*, bool, usb_speed_t);
+	int (*release_address)(bus_t *, usb_address_t);
+
+	int (*reset_toggle)(bus_t *, usb_target_t, toggle_reset_mode_t);
+
+	size_t (*count_bw) (endpoint_t *, size_t);
+
+	usb_transfer_batch_t *(*create_batch)(bus_t *, endpoint_t *); /**< Optional */
+	void (*destroy_batch)(usb_transfer_batch_t *);	/**< Optional */
+} bus_ops_t;
+
+/** Endpoint management structure */
+typedef struct bus {
+	/* Synchronization of ops */
+	fibril_mutex_t guard;
+
+	size_t device_size;
+
+	/* Do not call directly, ops are synchronized. */
+	bus_ops_t ops;
+
+	/* This structure is meant to be extended by overriding. */
+} bus_t;
+
+void bus_init(bus_t *, size_t);
+int device_init(device_t *);
+
+int device_set_default_name(device_t *);
+
+int bus_enumerate_device(bus_t *, hcd_t *, device_t *);
+int bus_remove_device(bus_t *, hcd_t *, device_t *);
+
+int bus_online_device(bus_t *, hcd_t *, device_t *);
+int bus_offline_device(bus_t *, hcd_t *, device_t *);
+
+int bus_add_endpoint(bus_t *, device_t *, const usb_endpoint_desc_t *, endpoint_t **);
+endpoint_t *bus_find_endpoint(bus_t *, device_t *, usb_target_t, usb_direction_t);
+int bus_remove_endpoint(bus_t *, endpoint_t *);
+
+size_t bus_count_bw(endpoint_t *, size_t);
+
+int bus_request_address(bus_t *, usb_address_t *, bool, usb_speed_t);
+int bus_release_address(bus_t *, usb_address_t);
+
+
+static inline int bus_reserve_default_address(bus_t *bus, usb_speed_t speed) {
+	usb_address_t addr = USB_ADDRESS_DEFAULT;
+
+	const int r = bus_request_address(bus, &addr, true, speed);
+	assert(addr == USB_ADDRESS_DEFAULT);
+	return r;
+}
+
+static inline int bus_release_default_address(bus_t *bus) {
+	return bus_release_address(bus, USB_ADDRESS_DEFAULT);
+}
+
+int bus_reset_toggle(bus_t *, usb_target_t, bool);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/usbhost/include/usb/host/ddf_helpers.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/ddf_helpers.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbhost/include/usb/host/ddf_helpers.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -38,5 +38,5 @@
 
 #include <usb/host/hcd.h>
-#include <usb/host/usb_bus.h>
+#include <usb/host/bus.h>
 #include <usb/usb.h>
 
@@ -45,26 +45,46 @@
 #include <device/hw_res_parsed.h>
 
-typedef int (*driver_init_t)(hcd_t *, const hw_res_list_parsed_t *, bool);
+typedef int (*driver_init_t)(hcd_t *, const hw_res_list_parsed_t *, ddf_dev_t *);
+typedef int (*irq_code_gen_t)(irq_code_t *, hcd_t *, const hw_res_list_parsed_t *);
+typedef int (*claim_t)(hcd_t *, ddf_dev_t *);
+typedef int (*driver_start_t)(hcd_t *, bool irq);
+typedef int (*setup_root_hub_t)(hcd_t *, ddf_dev_t *);
+
+typedef void (*driver_stop_t)(hcd_t *);
 typedef void (*driver_fini_t)(hcd_t *);
-typedef int (*claim_t)(ddf_dev_t *);
-typedef int (*irq_code_gen_t)(irq_code_t *, const hw_res_list_parsed_t *);
 
+/**
+ * All callbacks are optional.
+ */
 typedef struct {
 	hcd_ops_t ops;
-	claim_t claim;
-	usb_speed_t hc_speed;
-	driver_init_t init;
-	driver_fini_t fini;
-	interrupt_handler_t *irq_handler;
-	irq_code_gen_t irq_code_gen;
 	const char *name;
+
+	interrupt_handler_t *irq_handler;  /**< Handler of IRQ. Do have generic implementation. */
+
+	/* Initialization sequence: */
+	driver_init_t init;                /**< Initialize internal structures, memory */
+	claim_t claim;                     /**< Claim device from BIOS */
+	irq_code_gen_t irq_code_gen;       /**< Generate IRQ handling code */
+	driver_start_t start;              /**< Start the HC */
+	setup_root_hub_t setup_root_hub;   /**< Setup the root hub */
+
+	/* Destruction sequence: */
+	driver_stop_t stop;                /**< Stop the HC (counterpart of start) */
+	driver_fini_t fini;                /**< Destroy internal structures (counterpart of init) */
 } ddf_hc_driver_t;
 
 int hcd_ddf_add_hc(ddf_dev_t *device, const ddf_hc_driver_t *driver);
 
-int hcd_ddf_setup_hc(ddf_dev_t *device, usb_speed_t max_speed,
-    size_t bw, bw_count_func_t bw_count);
+int hcd_ddf_setup_hc(ddf_dev_t *device);
 void hcd_ddf_clean_hc(ddf_dev_t *device);
-int hcd_ddf_setup_root_hub(ddf_dev_t *device);
+
+int hcd_setup_virtual_root_hub(hcd_t *, ddf_dev_t *);
+
+device_t *hcd_ddf_device_create(ddf_dev_t *, size_t);
+void hcd_ddf_device_destroy(device_t *);
+int hcd_ddf_device_explore(hcd_t *, device_t *);
+int hcd_ddf_device_online(ddf_fun_t *);
+int hcd_ddf_device_offline(ddf_fun_t *);
 
 hcd_t *dev_to_hcd(ddf_dev_t *dev);
@@ -75,5 +95,5 @@
     const hw_res_list_parsed_t *hw_res,
     interrupt_handler_t handler,
-    int (*gen_irq_code)(irq_code_t *, const hw_res_list_parsed_t *));
+    irq_code_gen_t gen_irq_code);
 void ddf_hcd_gen_irq_handler(ipc_callid_t iid, ipc_call_t *call, ddf_dev_t *dev);
 
Index: uspace/lib/usbhost/include/usb/host/endpoint.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/endpoint.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbhost/include/usb/host/endpoint.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -32,4 +32,8 @@
 /** @file
  *
+ * Endpoint structure is tightly coupled to the bus. The bus controls the
+ * life-cycle of endpoint. In order to keep endpoints lightweight, operations
+ * on endpoints are part of the bus structure.
+ *
  */
 #ifndef LIBUSBHOST_HOST_ENDPOINT_H
@@ -42,13 +46,19 @@
 #include <atomic.h>
 
+typedef struct bus bus_t;
+typedef struct device device_t;
+typedef struct usb_transfer_batch usb_transfer_batch_t;
+
 /** Host controller side endpoint structure. */
 typedef struct endpoint {
-	/** Reference count. */
-	atomic_t refcnt;	
 	/** Part of linked list. */
 	link_t link;
-	/** USB address. */
-	usb_address_t address;
-	/** USB endpoint number. */
+	/** Managing bus */
+	bus_t *bus;
+	/** Reference count. */
+	atomic_t refcnt;
+	/** USB device */
+	device_t *device;
+	/** Enpoint number */
 	usb_endpoint_t endpoint;
 	/** Communication direction. */
@@ -62,47 +72,39 @@
 	/** Additional opportunities per uframe */
 	unsigned packets;
-	/** Necessary bandwidth. */
+	/** Reserved bandwidth. */
 	size_t bandwidth;
 	/** Value of the toggle bit. */
 	unsigned toggle:1;
-	/** True if there is a batch using this scheduled for this endpoint. */
-	volatile bool active;
+	/** The currently active transfer batch. Write using methods, read under guard. */
+	usb_transfer_batch_t *active_batch;
 	/** Protects resources and active status changes. */
 	fibril_mutex_t guard;
 	/** Signals change of active status. */
 	fibril_condvar_t avail;
-	/** High speed TT data */
-	struct {
-		usb_address_t address;
-		unsigned port;
-	} tt;
-	/** Optional device specific data. */
-	struct {
-		/** Device specific data. */
-		void *data;
-		/** Callback to get the value of toggle bit. */
-		int (*toggle_get)(void *);
-		/** Callback to set the value of toggle bit. */
-		void (*toggle_set)(void *, int);
-	} hc_data;
+
+	/* This structure is meant to be extended by overriding. */
 } endpoint_t;
 
-extern endpoint_t *endpoint_create(usb_address_t, usb_endpoint_t,
-    usb_direction_t, usb_transfer_type_t, usb_speed_t, size_t, unsigned int,
-    size_t, usb_address_t, unsigned int);
-extern void endpoint_destroy(endpoint_t *);
+extern void endpoint_init(endpoint_t *, bus_t *);
 
 extern void endpoint_add_ref(endpoint_t *);
 extern void endpoint_del_ref(endpoint_t *);
 
-extern void endpoint_set_hc_data(endpoint_t *, void *, int (*)(void *),
-    void (*)(void *, int));
-extern void endpoint_clear_hc_data(endpoint_t *);
+/* Pay atention to synchronization of batch access wrt to aborting & finishing from another fibril. */
 
-extern void endpoint_use(endpoint_t *);
-extern void endpoint_release(endpoint_t *);
+/* Set currently active batch. The common case is to activate in the same
+ * critical section as scheduling to HW.
+ */
+extern void endpoint_activate_locked(endpoint_t *, usb_transfer_batch_t *);
+
+/* Deactivate the endpoint, allowing others to activate it again. Batch shall
+ * already have an error set. */
+extern void endpoint_deactivate_locked(endpoint_t *);
+
+/* Abort the currenty active batch. */
+void endpoint_abort(endpoint_t *);
 
 extern int endpoint_toggle_get(endpoint_t *);
-extern void endpoint_toggle_set(endpoint_t *, int);
+extern void endpoint_toggle_set(endpoint_t *, bool);
 
 /** list_get_instance wrapper.
@@ -117,4 +119,5 @@
 	return item ? list_get_instance(item, endpoint_t, link) : NULL;
 }
+
 #endif
 
Index: uspace/lib/usbhost/include/usb/host/hcd.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/hcd.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbhost/include/usb/host/hcd.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -38,32 +38,30 @@
 
 #include <usb/host/endpoint.h>
-#include <usb/host/usb_bus.h>
+#include <usb/host/bus.h>
 #include <usb/host/usb_transfer_batch.h>
 #include <usb/usb.h>
 
 #include <assert.h>
-#include <usbhc_iface.h>
+#include <mem.h>
 #include <stddef.h>
 #include <stdint.h>
+#include <usbhc_iface.h>
 
 typedef struct hcd hcd_t;
 
 typedef int (*schedule_hook_t)(hcd_t *, usb_transfer_batch_t *);
-typedef int (*ep_add_hook_t)(hcd_t *, endpoint_t *);
-typedef void (*ep_remove_hook_t)(hcd_t *, endpoint_t *);
 typedef void (*interrupt_hook_t)(hcd_t *, uint32_t);
 typedef int (*status_hook_t)(hcd_t *, uint32_t *);
+typedef int (*address_device_hook_t)(hcd_t *, usb_speed_t, usb_tt_address_t, usb_address_t *);
 
 typedef struct {
 	/** Transfer scheduling, implement in device driver. */
 	schedule_hook_t schedule;
-	/** Hook called upon registering new endpoint. */
-	ep_add_hook_t ep_add_hook;
-	/** Hook called upon removing of an endpoint. */
-	ep_remove_hook_t ep_remove_hook;
 	/** Hook to be called on device interrupt, passes ARG1 */
 	interrupt_hook_t irq_hook;
 	/** Periodic polling hook */
 	status_hook_t status_hook;
+	/** Hook to setup device address */
+	address_device_hook_t address_device;
 } hcd_ops_t;
 
@@ -71,5 +69,5 @@
 struct hcd {
 	/** Endpoint manager. */
-	usb_bus_t bus;
+	bus_t *bus;
 
 	/** Interrupt replacement fibril */
@@ -78,12 +76,13 @@
 	/** Driver implementation */
 	hcd_ops_t ops;
+
 	/** Device specific driver data. */
 	void * driver_data;
 };
 
-extern void hcd_init(hcd_t *, usb_speed_t, size_t, bw_count_func_t);
+extern void hcd_init(hcd_t *);
 
 static inline void hcd_set_implementation(hcd_t *hcd, void *data,
-    const hcd_ops_t *ops)
+    const hcd_ops_t *ops, bus_t *bus)
 {
 	assert(hcd);
@@ -91,4 +90,5 @@
 		hcd->driver_data = data;
 		hcd->ops = *ops;
+		hcd->bus = bus;
 	} else {
 		memset(&hcd->ops, 0, sizeof(hcd->ops));
@@ -104,25 +104,16 @@
 extern usb_address_t hcd_request_address(hcd_t *, usb_speed_t);
 
-extern int hcd_release_address(hcd_t *, usb_address_t);
-
-extern int hcd_reserve_default_address(hcd_t *, usb_speed_t);
-
-static inline int hcd_release_default_address(hcd_t *hcd)
-{
-	return hcd_release_address(hcd, USB_ADDRESS_DEFAULT);
-}
-
 extern int hcd_add_ep(hcd_t *, usb_target_t, usb_direction_t,
-    usb_transfer_type_t, size_t, unsigned int, size_t, usb_address_t,
-    unsigned int);
+    usb_transfer_type_t, size_t, unsigned int, size_t, usb_tt_address_t);
 
 extern int hcd_remove_ep(hcd_t *, usb_target_t, usb_direction_t);
 
-extern int hcd_send_batch(hcd_t *, usb_target_t, usb_direction_t, void *,
-    size_t, uint64_t, usbhc_iface_transfer_in_callback_t,
-    usbhc_iface_transfer_out_callback_t, void *, const char *);
+extern int hcd_send_batch(hcd_t *, device_t *, usb_target_t,
+    usb_direction_t direction, char *, size_t, uint64_t,
+    usbhc_iface_transfer_callback_t, void *, const char *);
 
-extern ssize_t hcd_send_batch_sync(hcd_t *, usb_target_t, usb_direction_t,
-    void *, size_t, uint64_t, const char *);
+extern ssize_t hcd_send_batch_sync(hcd_t *, device_t *, usb_target_t,
+    usb_direction_t direction, char *, size_t, uint64_t,
+    const char *);
 
 #endif
Index: uspace/lib/usbhost/include/usb/host/usb2_bus.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/usb2_bus.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/lib/usbhost/include/usb/host/usb2_bus.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -0,0 +1,73 @@
+/*
+ * 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 libusbhost
+ * @{
+ */
+/** @file
+ *
+ * Bus implementation common for OHCI, UHCI and EHCI.
+ */
+#ifndef LIBUSBHOST_HOST_USB2_BUS_H
+#define LIBUSBHOST_HOST_USB2_BUS_H
+
+#include <usb/usb.h>
+#include <usb/host/bus.h>
+
+#include <adt/list.h>
+#include <stdbool.h>
+
+typedef struct usb2_bus usb2_bus_t;
+typedef struct endpoint endpoint_t;
+
+typedef size_t (*count_bw_func_t)(endpoint_t *, size_t);
+
+/** Endpoint management structure */
+typedef struct usb2_bus {
+	bus_t base;			/**< Inheritance - keep this first */
+
+	/* Device bookkeeping */
+	struct {
+		usb_speed_t speed;      /**< Device speed */
+		bool occupied;          /**< The address is in use. */
+		// TODO: This can be stored in usb2_bus-specific device_t
+		list_t endpoint_list;   /**< Store endpoint_t instances */
+	} devices[USB_ADDRESS_COUNT];
+
+	/** Size of the bandwidth pool */
+	size_t free_bw;
+	/** The last reserved address */
+	usb_address_t last_address;
+} usb2_bus_t;
+
+extern int usb2_bus_init(usb2_bus_t *, size_t, count_bw_func_t);
+
+#endif
+/**
+ * @}
+ */
Index: pace/lib/usbhost/include/usb/host/usb_bus.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/usb_bus.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ 	(revision )
@@ -1,120 +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 libusbhost
- * @{
- */
-/** @file
- * Device keeper structure and functions.
- *
- * Typical USB host controller needs to keep track of various settings for
- * each device that is connected to it.
- * State of toggle bit, device speed etc. etc.
- * This structure shall simplify the management.
- */
-#ifndef LIBUSBHOST_HOST_USB_ENDPOINT_MANAGER_H
-#define LIBUSBHOST_HOST_USB_ENDPOINT_MANAGER_H
-
-#include <usb/host/endpoint.h>
-#include <usb/usb.h>
-
-#include <adt/list.h>
-#include <fibril_synch.h>
-#include <stdbool.h>
-
-
-/** Bytes per second in FULL SPEED */
-#define BANDWIDTH_TOTAL_USB11 (12000000 / 8)
-/** 90% of total bandwidth is available for periodic transfers */
-#define BANDWIDTH_AVAILABLE_USB11 ((BANDWIDTH_TOTAL_USB11 / 10) * 9)
-
-//TODO: Implement
-#define BANDWIDTH_AVAILABLE_USB20  1
-
-typedef size_t (*bw_count_func_t)(usb_speed_t, usb_transfer_type_t, size_t, size_t);
-typedef void (*ep_remove_callback_t)(endpoint_t *, void *);
-typedef int (*ep_add_callback_t)(endpoint_t *, void *);
-
-/** Endpoint management structure */
-typedef struct usb_bus {
-	struct {
-		usb_speed_t speed;      /**< Device speed */
-		bool occupied;          /**< The address is in use. */
-		list_t endpoint_list;   /**< Store endpoint_t instances */
-	} devices[USB_ADDRESS_COUNT];
-	/** Prevents races accessing lists */
-	fibril_mutex_t guard;
-	/** Size of the bandwidth pool */
-	size_t free_bw;
-	/** Use this function to count bw required by EP */
-	bw_count_func_t bw_count;
-	/** Maximum speed allowed. */
-	usb_speed_t max_speed;
-	/** The last reserved address */
-	usb_address_t last_address;
-} usb_bus_t;
-
-
-size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type,
-    size_t size, size_t max_packet_size);
-size_t bandwidth_count_usb20(usb_speed_t speed, usb_transfer_type_t type,
-    size_t size, size_t max_packet_size);
-
-int usb_bus_init(usb_bus_t *instance,
-    size_t available_bandwidth, bw_count_func_t bw_count, usb_speed_t max_speed);
-
-int usb_bus_register_ep(usb_bus_t *instance, endpoint_t *ep, size_t data_size);
-
-int usb_bus_unregister_ep(usb_bus_t *instance, endpoint_t *ep);
-
-endpoint_t * usb_bus_find_ep(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t ep, usb_direction_t direction);
-
-int usb_bus_add_ep(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
-    usb_transfer_type_t type, size_t max_packet_size, unsigned packets,
-    size_t data_size, ep_add_callback_t callback, void *arg,
-    usb_address_t tt_address, unsigned tt_port);
-
-int usb_bus_remove_ep(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
-    ep_remove_callback_t callback, void *arg);
-
-int usb_bus_reset_toggle(usb_bus_t *instance, usb_target_t target, bool all);
-
-int usb_bus_remove_address(usb_bus_t *instance,
-    usb_address_t address, ep_remove_callback_t callback, void *arg);
-
-int usb_bus_request_address(usb_bus_t *instance,
-    usb_address_t *address, bool strict, usb_speed_t speed);
-
-int usb_bus_get_speed(usb_bus_t *instance,
-    usb_address_t address, usb_speed_t *speed);
-#endif
-/**
- * @}
- */
Index: uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -37,46 +37,55 @@
 #define LIBUSBHOST_HOST_USB_TRANSFER_BATCH_H
 
-#include <usb/host/endpoint.h>
 #include <usb/usb.h>
+#include <usb/request.h>
+#include <usbhc_iface.h>
 
-#include <assert.h>
+#include <atomic.h>
 #include <stddef.h>
+#include <errno.h>
 #include <stdint.h>
-#include <usbhc_iface.h>
 
 #define USB_SETUP_PACKET_SIZE 8
 
+typedef struct endpoint endpoint_t;
+typedef struct bus bus_t;
+
 /** Structure stores additional data needed for communication with EP */
 typedef struct usb_transfer_batch {
+	/** Target for communication */
+	usb_target_t target;
+
 	/** Endpoint used for communication */
 	endpoint_t *ep;
-	/** Function called on completion (IN version) */
-	usbhc_iface_transfer_in_callback_t callback_in;
-	/** Function called on completion (OUT version) */
-	usbhc_iface_transfer_out_callback_t callback_out;
-	/** Argument to pass to the completion function */
-	void *arg;
+
+	/** Size reported to be sent */
+	size_t expected_size;
+
+	/** Direction of the transfer */
+	usb_direction_t dir;
+
+	/** Function called on completion */
+	usbhc_iface_transfer_callback_t on_complete;
+	/** Arbitrary data for the handler */
+	void *on_complete_data;
+
+	/** Place to store SETUP data needed by control transfers */
+	union {
+		char buffer [USB_SETUP_PACKET_SIZE];
+		usb_device_request_setup_packet_t packet;
+		uint64_t packed;
+	} setup;
+
+	/** Resetting the Toggle */
+	toggle_reset_mode_t toggle_reset_mode;
+
 	/** Place for data to send/receive */
 	char *buffer;
 	/** Size of memory pointed to by buffer member */
 	size_t buffer_size;
-	/** Place to store SETUP data needed by control transfers */
-	char setup_buffer[USB_SETUP_PACKET_SIZE];
-	/** Used portion of setup_buffer member
-	 *
-	 * SETUP buffer must be 8 bytes for control transfers and is left
-	 * unused for all other transfers. Thus, this field is either 0 or 8.
-	 */
-	size_t setup_size;
+	/** Actually used portion of the buffer */
+	size_t transfered_size;
 
-	/** Actually used portion of the buffer
-	 * This member is never accessed by functions provided in this header,
-	 * with the exception of usb_transfer_batch_finish. For external use.
-	 */
-	size_t transfered_size;
-	/** Indicates success/failure of the communication
-	 * This member is never accessed by functions provided in this header,
-	 * with the exception of usb_transfer_batch_finish. For external use.
-	 */
+	/** Indicates success/failure of the communication */
 	int error;
 } usb_transfer_batch_t;
@@ -89,5 +98,5 @@
  */
 #define USB_TRANSFER_BATCH_ARGS(batch) \
-	(batch).ep->address, (batch).ep->endpoint, \
+	(batch).target.address, (batch).target.endpoint, \
 	usb_str_speed((batch).ep->speed), \
 	usb_str_transfer_type_short((batch).ep->transfer_type), \
@@ -95,56 +104,21 @@
 	(batch).buffer_size, (batch).ep->max_packet_size
 
+/** Wrapper for bus operation. */
+usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *);
 
-usb_transfer_batch_t * usb_transfer_batch_create(
-    endpoint_t *ep,
-    char *buffer,
-    size_t buffer_size,
-    uint64_t setup_buffer,
-    usbhc_iface_transfer_in_callback_t func_in,
-    usbhc_iface_transfer_out_callback_t func_out,
-    void *arg
-);
-void usb_transfer_batch_destroy(const usb_transfer_batch_t *instance);
+/** Batch initializer. */
+void usb_transfer_batch_init(usb_transfer_batch_t *, endpoint_t *);
 
-void usb_transfer_batch_finish_error(const usb_transfer_batch_t *instance,
-    const void* data, size_t size, int error);
+/** Call after status is known, but before releasing endpoint */
+int usb_transfer_batch_reset_toggle(usb_transfer_batch_t *);
 
-/** Finish batch using stored error value and transferred size.
- *
- * @param[in] instance Batch structure to use.
- * @param[in] data Data to copy to the output buffer.
+/** Batch finalization. */
+void usb_transfer_batch_abort(usb_transfer_batch_t *);
+void usb_transfer_batch_finish(usb_transfer_batch_t *);
+
+/** To be called from outside only when the transfer is not going to be finished
+ * (i.o.w. until successfuly scheduling)
  */
-static inline void usb_transfer_batch_finish(
-    const usb_transfer_batch_t *instance, const void* data)
-{
-	assert(instance);
-	usb_transfer_batch_finish_error(
-	    instance, data, instance->transfered_size, instance->error);
-}
-
-/** Determine batch direction based on the callbacks present
- * @param[in] instance Batch structure to use, non-null.
- * @return USB_DIRECTION_IN, or USB_DIRECTION_OUT.
- */
-static inline usb_direction_t usb_transfer_batch_direction(
-    const usb_transfer_batch_t *instance)
-{
-	assert(instance);
-	if (instance->callback_in) {
-		assert(instance->callback_out == NULL);
-		assert(instance->ep == NULL
-		    || instance->ep->transfer_type == USB_TRANSFER_CONTROL
-		    || instance->ep->direction == USB_DIRECTION_IN);
-		return USB_DIRECTION_IN;
-	}
-	if (instance->callback_out) {
-		assert(instance->callback_in == NULL);
-		assert(instance->ep == NULL
-		    || instance->ep->transfer_type == USB_TRANSFER_CONTROL
-		    || instance->ep->direction == USB_DIRECTION_OUT);
-		return USB_DIRECTION_OUT;
-	}
-	assert(false);
-}
+void usb_transfer_batch_destroy(usb_transfer_batch_t *);
 
 #endif
Index: uspace/lib/usbhost/src/bandwidth.c
===================================================================
--- uspace/lib/usbhost/src/bandwidth.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/lib/usbhost/src/bandwidth.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -0,0 +1,115 @@
+/*
+ * 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 libusbhost
+ * @{
+ */
+/** @file
+ *
+ * Bandwidth calculation functions. Shared among uhci, ohci and ehci drivers.
+ */
+
+#include <usb/host/bandwidth.h>
+#include <usb/host/endpoint.h>
+
+#include <assert.h>
+#include <stdlib.h>
+
+/** Calculate bandwidth that needs to be reserved for communication with EP.
+ * Calculation follows USB 1.1 specification.
+ * @param speed Device's speed.
+ * @param type Type of the transfer.
+ * @param size Number of byte to transfer.
+ * @param max_packet_size Maximum bytes in one packet.
+ */
+size_t bandwidth_count_usb11(endpoint_t *ep, size_t size)
+{
+	assert(ep);
+
+	const usb_transfer_type_t type = ep->transfer_type;
+
+	/* We care about bandwidth only for interrupt and isochronous. */
+	if ((type != USB_TRANSFER_INTERRUPT)
+	    && (type != USB_TRANSFER_ISOCHRONOUS)) {
+		return 0;
+	}
+
+	const size_t max_packet_size = ep->max_packet_size;
+
+	const unsigned packet_count =
+	    (size + max_packet_size - 1) / max_packet_size;
+	/* TODO: It may be that ISO and INT transfers use only one packet per
+	 * transaction, but I did not find text in USB spec to confirm this */
+	/* NOTE: All data packets will be considered to be max_packet_size */
+	switch (ep->speed)
+	{
+	case USB_SPEED_LOW:
+		assert(type == USB_TRANSFER_INTERRUPT);
+		/* Protocol overhead 13B
+		 * (3 SYNC bytes, 3 PID bytes, 2 Endpoint + CRC bytes, 2
+		 * CRC bytes, and a 3-byte interpacket delay)
+		 * see USB spec page 45-46. */
+		/* Speed penalty 8: low speed is 8-times slower*/
+		return packet_count * (13 + max_packet_size) * 8;
+	case USB_SPEED_FULL:
+		/* Interrupt transfer overhead see above
+		 * or page 45 of USB spec */
+		if (type == USB_TRANSFER_INTERRUPT)
+			return packet_count * (13 + max_packet_size);
+
+		assert(type == USB_TRANSFER_ISOCHRONOUS);
+		/* Protocol overhead 9B
+		 * (2 SYNC bytes, 2 PID bytes, 2 Endpoint + CRC bytes, 2 CRC
+		 * bytes, and a 1-byte interpacket delay)
+		 * see USB spec page 42 */
+		return packet_count * (9 + max_packet_size);
+	default:
+		return 0;
+	}
+}
+
+/** Calculate bandwidth that needs to be reserved for communication with EP.
+ * Calculation follows USB 2.0 specification.
+ * @param speed Device's speed.
+ * @param type Type of the transfer.
+ * @param size Number of byte to transfer.
+ * @param max_packet_size Maximum bytes in one packet.
+ */
+size_t bandwidth_count_usb20(endpoint_t *ep, size_t size)
+{
+	assert(ep);
+
+	const usb_transfer_type_t type = ep->transfer_type;
+
+	/* We care about bandwidth only for interrupt and isochronous. */
+	if ((type != USB_TRANSFER_INTERRUPT)
+	    && (type != USB_TRANSFER_ISOCHRONOUS)) {
+		return 0;
+	}
+	//TODO Implement
+	return 0;
+}
Index: uspace/lib/usbhost/src/bus.c
===================================================================
--- uspace/lib/usbhost/src/bus.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/lib/usbhost/src/bus.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -0,0 +1,252 @@
+/*
+ * 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
+ *
+ */
+
+#include <usb/host/bus.h>
+#include <usb/host/endpoint.h>
+#include <usb/debug.h>
+#include <ddf/driver.h>
+
+#include <mem.h>
+#include <errno.h>
+#include <stdio.h>
+
+/**
+ * Initializes the bus structure.
+ */
+void bus_init(bus_t *bus, size_t device_size)
+{
+	assert(bus);
+	assert(device_size >= sizeof(device_t));
+	memset(bus, 0, sizeof(bus_t));
+
+	fibril_mutex_initialize(&bus->guard);
+	bus->device_size = device_size;
+}
+
+int device_init(device_t *dev)
+{
+	memset(dev, 0, sizeof(*dev));
+
+	link_initialize(&dev->link);
+	list_initialize(&dev->devices);
+	fibril_mutex_initialize(&dev->guard);
+
+	return EOK;
+}
+
+int device_set_default_name(device_t *dev)
+{
+	assert(dev);
+	assert(dev->fun);
+
+	char buf[10] = { 0 }; /* usbxyz-ss */
+	snprintf(buf, sizeof(buf) - 1, "usb%u-%cs",
+	    dev->address, usb_str_speed(dev->speed)[0]);
+
+	return ddf_fun_set_name(dev->fun, buf);
+}
+
+int bus_enumerate_device(bus_t *bus, hcd_t *hcd, device_t *dev)
+{
+	assert(bus);
+	assert(hcd);
+	assert(dev);
+
+	if (!bus->ops.enumerate_device)
+		return ENOTSUP;
+
+	return bus->ops.enumerate_device(bus, hcd, dev);
+}
+
+int bus_remove_device(bus_t *bus, hcd_t *hcd, device_t *dev)
+{
+	assert(bus);
+	assert(dev);
+
+	if (!bus->ops.remove_device)
+		return ENOTSUP;
+
+	return bus->ops.remove_device(bus, hcd, dev);
+}
+
+int bus_online_device(bus_t *bus, hcd_t *hcd, device_t *dev)
+{
+	assert(bus);
+	assert(hcd);
+	assert(dev);
+
+	if (!bus->ops.online_device)
+		return ENOTSUP;
+
+	return bus->ops.online_device(bus, hcd, dev);
+}
+
+int bus_offline_device(bus_t *bus, hcd_t *hcd, device_t *dev)
+{
+	assert(bus);
+	assert(hcd);
+	assert(dev);
+
+	if (!bus->ops.offline_device)
+		return ENOTSUP;
+
+	return bus->ops.offline_device(bus, hcd, dev);
+}
+
+int bus_add_endpoint(bus_t *bus, device_t *device, const usb_endpoint_desc_t *desc, endpoint_t **out_ep)
+{
+	assert(bus);
+	assert(device);
+
+	fibril_mutex_lock(&bus->guard);
+
+	if (desc->max_packet_size == 0 || desc->packets == 0) {
+		usb_log_warning("Invalid endpoint description (mps %zu, %u packets)", desc->max_packet_size, desc->packets);
+		return EINVAL;
+	}
+
+	int err = ENOMEM;
+	endpoint_t *ep = bus->ops.create_endpoint(bus);
+	if (!ep)
+		goto err;
+
+	/* Bus reference */
+	endpoint_add_ref(ep);
+
+	if ((err = bus->ops.register_endpoint(bus, device, ep, desc)))
+		goto err_ep;
+
+	if (out_ep) {
+		endpoint_add_ref(ep);
+		*out_ep = ep;
+	}
+
+	fibril_mutex_unlock(&bus->guard);
+	return EOK;
+
+err_ep:
+	endpoint_del_ref(ep);
+err:
+	fibril_mutex_unlock(&bus->guard);
+	return err;
+}
+
+/** Searches for an endpoint. Returns a reference.
+ */
+endpoint_t *bus_find_endpoint(bus_t *bus, device_t *device, usb_target_t endpoint, usb_direction_t dir)
+{
+	assert(bus);
+
+	fibril_mutex_lock(&bus->guard);
+	endpoint_t *ep = bus->ops.find_endpoint(bus, device, endpoint, dir);
+	if (ep) {
+		/* Exporting reference */
+		endpoint_add_ref(ep);
+	}
+
+	fibril_mutex_unlock(&bus->guard);
+	return ep;
+}
+
+int bus_remove_endpoint(bus_t *bus, endpoint_t *ep)
+{
+	assert(bus);
+	assert(ep);
+
+	fibril_mutex_lock(&bus->guard);
+	const int r = bus->ops.unregister_endpoint(bus, ep);
+	fibril_mutex_unlock(&bus->guard);
+
+	if (r)
+		return r;
+
+	/* Bus reference */
+	endpoint_del_ref(ep);
+
+	return EOK;
+}
+
+int bus_request_address(bus_t *bus, usb_address_t *hint, bool strict, usb_speed_t speed)
+{
+	assert(bus);
+
+	if (!bus->ops.request_address)
+		return ENOTSUP;
+
+	fibril_mutex_lock(&bus->guard);
+	const int r = bus->ops.request_address(bus, hint, strict, speed);
+	fibril_mutex_unlock(&bus->guard);
+	return r;
+}
+
+int bus_release_address(bus_t *bus, usb_address_t address)
+{
+	assert(bus);
+
+	if (!bus->ops.release_address)
+		return ENOTSUP;
+
+	fibril_mutex_lock(&bus->guard);
+	const int r = bus->ops.release_address(bus, address);
+	fibril_mutex_unlock(&bus->guard);
+	return r;
+}
+
+int bus_reset_toggle(bus_t *bus, usb_target_t target, bool all)
+{
+	assert(bus);
+
+	if (!bus->ops.reset_toggle)
+		return ENOTSUP;
+
+	fibril_mutex_lock(&bus->guard);
+	const int r = bus->ops.reset_toggle(bus, target, all);
+	fibril_mutex_unlock(&bus->guard);
+	return r;
+}
+
+size_t bus_count_bw(endpoint_t *ep, size_t size)
+{
+	assert(ep);
+
+	fibril_mutex_lock(&ep->guard);
+	const size_t bw = ep->bus->ops.count_bw(ep, size);
+	fibril_mutex_unlock(&ep->guard);
+	return bw;
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/usbhost/src/ddf_helpers.c
===================================================================
--- uspace/lib/usbhost/src/ddf_helpers.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbhost/src/ddf_helpers.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -35,4 +35,5 @@
 
 #include <usb/classes/classes.h>
+#include <usb/host/bus.h>
 #include <usb/debug.h>
 #include <usb/descriptor.h>
@@ -49,28 +50,14 @@
 #include <fibril_synch.h>
 #include <macros.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <str_error.h>
 #include <usb_iface.h>
+#include <usbhc_iface.h>
 
 #include "ddf_helpers.h"
-
-#define CTRL_PIPE_MIN_PACKET_SIZE 8
-
-typedef struct usb_dev {
-	link_t link;
-	list_t devices;
-	fibril_mutex_t guard;
-	ddf_fun_t *fun;
-	usb_address_t address;
-	usb_speed_t speed;
-	usb_address_t tt_address;
-	unsigned port;
-} usb_dev_t;
 
 typedef struct hc_dev {
 	ddf_fun_t *ctl_fun;
 	hcd_t hcd;
-	usb_dev_t *root_hub;
 } hc_dev_t;
 
@@ -91,6 +78,6 @@
 
 
-static int hcd_ddf_new_device(ddf_dev_t *device, usb_dev_t *hub, unsigned port);
-static int hcd_ddf_remove_device(ddf_dev_t *device, usb_dev_t *hub, unsigned port);
+static int hcd_ddf_new_device(hcd_t *hcd, ddf_dev_t *hc, device_t *hub_dev, unsigned port);
+static int hcd_ddf_remove_device(ddf_dev_t *device, device_t *hub, unsigned port);
 
 
@@ -99,54 +86,55 @@
 /** Register endpoint interface function.
  * @param fun DDF function.
- * @param address USB address of the device.
- * @param endpoint USB endpoint number to be registered.
- * @param transfer_type Endpoint's transfer type.
- * @param direction USB communication direction the endpoint is capable of.
- * @param max_packet_size Maximu size of packets the endpoint accepts.
- * @param interval Preferred timeout between communication.
+ * @param endpoint_desc Endpoint description.
  * @return Error code.
  */
 static int register_endpoint(
-    ddf_fun_t *fun, usb_endpoint_t endpoint,
-    usb_transfer_type_t transfer_type, usb_direction_t direction,
-    size_t max_packet_size, unsigned packets, unsigned interval)
+	ddf_fun_t *fun, usb_endpoint_desc_t *endpoint_desc)
 {
 	assert(fun);
 	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
-	usb_dev_t *dev = ddf_fun_data_get(fun);
+	device_t *dev = ddf_fun_data_get(fun);
 	assert(hcd);
-	assert(dev);
-	const size_t size = max_packet_size;
-	const usb_target_t target =
-	    {{.address = dev->address, .endpoint = endpoint}};
+	assert(hcd->bus);
+	assert(dev);
 
 	usb_log_debug("Register endpoint %d:%d %s-%s %zuB %ums.\n",
-	    dev->address, endpoint, usb_str_transfer_type(transfer_type),
-	    usb_str_direction(direction), max_packet_size, interval);
-
-	return hcd_add_ep(hcd, target, direction, transfer_type,
-	    max_packet_size, packets, size, dev->tt_address, dev->port);
-}
-
-/** Unregister endpoint interface function.
- * @param fun DDF function.
- * @param address USB address of the endpoint.
- * @param endpoint USB endpoint number.
- * @param direction Communication direction of the enpdoint to unregister.
- * @return Error code.
- */
+		dev->address, endpoint_desc->endpoint_no,
+		usb_str_transfer_type(endpoint_desc->transfer_type),
+		usb_str_direction(endpoint_desc->direction),
+		endpoint_desc->max_packet_size, endpoint_desc->usb2.polling_interval);
+
+	return bus_add_endpoint(hcd->bus, dev, endpoint_desc, NULL);
+}
+
+ /** Unregister endpoint interface function.
+  * @param fun DDF function.
+  * @param endpoint_desc Endpoint description.
+  * @return Error code.
+  */
 static int unregister_endpoint(
-    ddf_fun_t *fun, usb_endpoint_t endpoint, usb_direction_t direction)
+	ddf_fun_t *fun, usb_endpoint_desc_t *endpoint_desc)
 {
 	assert(fun);
 	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
-	usb_dev_t *dev = ddf_fun_data_get(fun);
+	device_t *dev = ddf_fun_data_get(fun);
 	assert(hcd);
-	assert(dev);
-	const usb_target_t target =
-	    {{.address = dev->address, .endpoint = endpoint}};
+	assert(hcd->bus);
+	assert(dev);
+
+	const usb_target_t target = {{
+		.address = dev->address,
+		.endpoint = endpoint_desc->endpoint_no
+	}};
+
 	usb_log_debug("Unregister endpoint %d:%d %s.\n",
-	    dev->address, endpoint, usb_str_direction(direction));
-	return hcd_remove_ep(hcd, target, direction);
+		dev->address, endpoint_desc->endpoint_no,
+		usb_str_direction(endpoint_desc->direction));
+
+	endpoint_t *ep = bus_find_endpoint(hcd->bus, dev, target, endpoint_desc->direction);
+	if (!ep)
+		return ENOENT;
+
+	return bus_remove_endpoint(hcd->bus, ep);
 }
 
@@ -155,11 +143,12 @@
 	assert(fun);
 	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
-	usb_dev_t *dev = ddf_fun_data_get(fun);
+	device_t *dev = ddf_fun_data_get(fun);
 	assert(hcd);
+	assert(hcd->bus);
 	assert(dev);
 
 	usb_log_debug("Device %d requested default address at %s speed\n",
 	    dev->address, usb_str_speed(speed));
-	return hcd_reserve_default_address(hcd, speed);
+	return bus_reserve_default_address(hcd->bus, speed);
 }
 
@@ -168,10 +157,11 @@
 	assert(fun);
 	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
-	usb_dev_t *dev = ddf_fun_data_get(fun);
+	device_t *dev = ddf_fun_data_get(fun);
 	assert(hcd);
+	assert(hcd->bus);
 	assert(dev);
 
 	usb_log_debug("Device %d released default address\n", dev->address);
-	return hcd_release_default_address(hcd);
+	return bus_release_default_address(hcd->bus);
 }
 
@@ -179,18 +169,21 @@
 {
 	assert(fun);
+	ddf_dev_t *hc = ddf_fun_get_dev(fun);
+	assert(hc);
+	hcd_t *hcd = dev_to_hcd(hc);
+	assert(hcd);
+	device_t *hub = ddf_fun_data_get(fun);
+	assert(hub);
+
+	usb_log_debug("Hub %d reported a new USB device on port: %u\n",
+	    hub->address, port);
+	return hcd_ddf_new_device(hcd, hc, hub, port);
+}
+
+static int device_remove(ddf_fun_t *fun, unsigned port)
+{
+	assert(fun);
 	ddf_dev_t *ddf_dev = ddf_fun_get_dev(fun);
-	usb_dev_t *dev = ddf_fun_data_get(fun);
-	assert(ddf_dev);
-	assert(dev);
-	usb_log_debug("Hub %d reported a new USB device on port: %u\n",
-	    dev->address, port);
-	return hcd_ddf_new_device(ddf_dev, dev, port);
-}
-
-static int device_remove(ddf_fun_t *fun, unsigned port)
-{
-	assert(fun);
-	ddf_dev_t *ddf_dev = ddf_fun_get_dev(fun);
-	usb_dev_t *dev = ddf_fun_data_get(fun);
+	device_t *dev = ddf_fun_data_get(fun);
 	assert(ddf_dev);
 	assert(dev);
@@ -224,18 +217,18 @@
  * @return Error code.
  */
-static int dev_read(ddf_fun_t *fun, usb_endpoint_t endpoint,
-    uint64_t setup_data, uint8_t *data, size_t size,
-    usbhc_iface_transfer_in_callback_t callback, void *arg)
-{
-	assert(fun);
-	usb_dev_t *usb_dev = ddf_fun_data_get(fun);
-	assert(usb_dev);
-	const usb_target_t target = {{
-	    .address =  usb_dev->address,
-	    .endpoint = endpoint,
-	}};
-	return hcd_send_batch(dev_to_hcd(ddf_fun_get_dev(fun)), target,
-	    USB_DIRECTION_IN, data, size, setup_data, callback, NULL, arg,
-	    "READ");
+static int dev_read(ddf_fun_t *fun, usb_target_t target,
+    uint64_t setup_data, char *data, size_t size,
+    usbhc_iface_transfer_callback_t callback, void *arg)
+{
+	assert(fun);
+	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
+	device_t *dev = ddf_fun_data_get(fun);
+	assert(dev);
+
+	target.address = dev->address;
+
+	return hcd_send_batch(hcd, dev, target, USB_DIRECTION_IN,
+	    data, size, setup_data,
+	    callback, arg, "READ");
 }
 
@@ -250,17 +243,17 @@
  * @return Error code.
  */
-static int dev_write(ddf_fun_t *fun, usb_endpoint_t endpoint,
-    uint64_t setup_data, const uint8_t *data, size_t size,
-    usbhc_iface_transfer_out_callback_t callback, void *arg)
-{
-	assert(fun);
-	usb_dev_t *usb_dev = ddf_fun_data_get(fun);
-	assert(usb_dev);
-	const usb_target_t target = {{
-	    .address =  usb_dev->address,
-	    .endpoint = endpoint,
-	}};
-	return hcd_send_batch(dev_to_hcd(ddf_fun_get_dev(fun)),
-	    target, USB_DIRECTION_OUT, (uint8_t*)data, size, setup_data, NULL,
+static int dev_write(ddf_fun_t *fun, usb_target_t target,
+    uint64_t setup_data, const char *data, size_t size,
+    usbhc_iface_transfer_callback_t callback, void *arg)
+{
+	assert(fun);
+	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
+	device_t *dev = ddf_fun_data_get(fun);
+	assert(dev);
+
+	target.address = dev->address;
+
+	return hcd_send_batch(hcd, dev, target, USB_DIRECTION_OUT,
+	    (char *) data, size, setup_data,
 	    callback, arg, "WRITE");
 }
@@ -269,5 +262,8 @@
 static usb_iface_t usb_iface = {
 	.get_my_device_handle = get_my_device_handle,
-
+};
+
+/** USB host controller interface */
+static usbhc_iface_t usbhc_iface = {
 	.reserve_default_address = reserve_default_address,
 	.release_default_address = release_default_address,
@@ -286,86 +282,9 @@
 static ddf_dev_ops_t usb_ops = {
 	.interfaces[USB_DEV_IFACE] = &usb_iface,
+	.interfaces[USBHC_DEV_IFACE] = &usbhc_iface,
 };
 
 
 /* DDF HELPERS */
-
-#define GET_DEVICE_DESC(size) \
-{ \
-	.request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST \
-	    | (USB_REQUEST_TYPE_STANDARD << 5) \
-	    | USB_REQUEST_RECIPIENT_DEVICE, \
-	.request = USB_DEVREQ_GET_DESCRIPTOR, \
-	.value = uint16_host2usb(USB_DESCTYPE_DEVICE << 8), \
-	.index = uint16_host2usb(0), \
-	.length = uint16_host2usb(size), \
-};
-
-#define SET_ADDRESS(address) \
-{ \
-	.request_type = SETUP_REQUEST_TYPE_HOST_TO_DEVICE \
-	    | (USB_REQUEST_TYPE_STANDARD << 5) \
-	    | USB_REQUEST_RECIPIENT_DEVICE, \
-	.request = USB_DEVREQ_SET_ADDRESS, \
-	.value = uint16_host2usb(address), \
-	.index = uint16_host2usb(0), \
-	.length = uint16_host2usb(0), \
-};
-
-static int hcd_ddf_add_device(ddf_dev_t *parent, usb_dev_t *hub_dev,
-    unsigned port, usb_address_t address, usb_speed_t speed, const char *name,
-    const match_id_list_t *mids)
-{
-	assert(parent);
-
-	char default_name[10] = { 0 }; /* usbxyz-ss */
-	if (!name) {
-		snprintf(default_name, sizeof(default_name) - 1,
-		    "usb%u-%cs", address, usb_str_speed(speed)[0]);
-		name = default_name;
-	}
-
-	ddf_fun_t *fun = ddf_fun_create(parent, fun_inner, name);
-	if (!fun)
-		return ENOMEM;
-	usb_dev_t *info = ddf_fun_data_alloc(fun, sizeof(usb_dev_t));
-	if (!info) {
-		ddf_fun_destroy(fun);
-		return ENOMEM;
-	}
-	info->address = address;
-	info->speed = speed;
-	info->fun = fun;
-	info->port = port;
-	info->tt_address = hub_dev ? hub_dev->tt_address : -1;
-	link_initialize(&info->link);
-	list_initialize(&info->devices);
-	fibril_mutex_initialize(&info->guard);
-
-	if (hub_dev && hub_dev->speed == USB_SPEED_HIGH && usb_speed_is_11(speed))
-		info->tt_address = hub_dev->address;
-
-	ddf_fun_set_ops(fun, &usb_ops);
-	list_foreach(mids->ids, link, const match_id_t, mid) {
-		ddf_fun_add_match_id(fun, mid->id, mid->score);
-	}
-
-	int ret = ddf_fun_bind(fun);
-	if (ret != EOK) {
-		ddf_fun_destroy(fun);
-		return ret;
-	}
-
-	if (hub_dev) {
-		fibril_mutex_lock(&hub_dev->guard);
-		list_append(&info->link, &hub_dev->devices);
-		fibril_mutex_unlock(&hub_dev->guard);
-	} else {
-		hc_dev_t *hc_dev = dev_to_hc_dev(parent);
-		assert(hc_dev->root_hub == NULL);
-		hc_dev->root_hub = info;
-	}
-	return EOK;
-}
 
 #define ADD_MATCHID_OR_RETURN(list, sc, str, ...) \
@@ -394,5 +313,5 @@
 	assert(l);
 	assert(d);
-	
+
 	if (d->vendor_id != 0) {
 		/* First, with release number. */
@@ -401,5 +320,5 @@
 		    d->vendor_id, d->product_id, (d->device_version >> 8),
 		    (d->device_version & 0xff));
-	
+
 		/* Next, without release number. */
 		ADD_MATCHID_OR_RETURN(l, 90, "usb&vendor=%#04x&product=%#04x",
@@ -415,8 +334,7 @@
 
 	return EOK;
-
-}
-
-static int hcd_ddf_remove_device(ddf_dev_t *device, usb_dev_t *hub,
+}
+
+static int hcd_ddf_remove_device(ddf_dev_t *device, device_t *hub,
     unsigned port)
 {
@@ -425,4 +343,5 @@
 	hcd_t *hcd = dev_to_hcd(device);
 	assert(hcd);
+	assert(hcd->bus);
 
 	hc_dev_t *hc_dev = dev_to_hc_dev(device);
@@ -431,7 +350,7 @@
 	fibril_mutex_lock(&hub->guard);
 
-	usb_dev_t *victim = NULL;
-
-	list_foreach(hub->devices, link, usb_dev_t, it) {
+	device_t *victim = NULL;
+
+	list_foreach(hub->devices, link, device_t, it) {
 		if (it->port == port) {
 			victim = it;
@@ -440,5 +359,7 @@
 	}
 	if (victim) {
+		assert(victim->fun);
 		assert(victim->port == port);
+		assert(victim->hub == hub);
 		list_remove(&victim->link);
 		fibril_mutex_unlock(&hub->guard);
@@ -446,6 +367,7 @@
 		if (ret == EOK) {
 			usb_address_t address = victim->address;
+			bus_remove_device(hcd->bus, hcd, victim);
 			ddf_fun_destroy(victim->fun);
-			hcd_release_address(hcd, address);
+			bus_release_address(hcd->bus, address);
 		} else {
 			usb_log_warning("Failed to unbind device `%s': %s\n",
@@ -458,111 +380,44 @@
 }
 
-static int hcd_ddf_new_device(ddf_dev_t *device, usb_dev_t *hub, unsigned port)
-{
-	assert(device);
-
-	hcd_t *hcd = dev_to_hcd(device);
-	assert(hcd);
-
-	usb_speed_t speed = USB_SPEED_MAX;
-
-	/* This checks whether the default address is reserved and gets speed */
-	int ret = usb_bus_get_speed(&hcd->bus, USB_ADDRESS_DEFAULT, &speed);
-	if (ret != EOK) {
-		usb_log_error("Failed to verify speed: %s.", str_error(ret));
-		return ret;
-	}
-
-	usb_log_debug("Found new %s speed USB device.", usb_str_speed(speed));
-
-	static const usb_target_t default_target = {{
-		.address = USB_ADDRESS_DEFAULT,
+device_t *hcd_ddf_device_create(ddf_dev_t *hc, size_t device_size)
+{
+	/* Create DDF function for the new device */
+	ddf_fun_t *fun = ddf_fun_create(hc, fun_inner, NULL);
+	if (!fun)
+		return NULL;
+
+	ddf_fun_set_ops(fun, &usb_ops);
+
+	/* Create USB device node for the new device */
+	device_t *dev = ddf_fun_data_alloc(fun, device_size);
+	if (!dev) {
+		ddf_fun_destroy(fun);
+		return NULL;
+	}
+
+	device_init(dev);
+	dev->fun = fun;
+	return dev;
+}
+
+void hcd_ddf_device_destroy(device_t *dev)
+{
+	assert(dev);
+	assert(dev->fun);
+	ddf_fun_destroy(dev->fun);
+}
+
+int hcd_ddf_device_explore(hcd_t *hcd, device_t *device)
+{
+	int err;
+	match_id_list_t mids;
+	usb_standard_device_descriptor_t desc = { 0 };
+
+	init_match_ids(&mids);
+
+	const usb_target_t control_ep = {{
+		.address = device->address,
 		.endpoint = 0,
 	}};
-
-	const usb_address_t address = hcd_request_address(hcd, speed);
-	if (address < 0) {
-		usb_log_error("Failed to reserve new address: %s.",
-		    str_error(address));
-		return address;
-	}
-
-	usb_log_debug("Reserved new address: %d\n", address);
-
-	const usb_target_t target = {{
-		.address = address,
-		.endpoint = 0,
-	}};
-
-	const usb_address_t tt_address = hub ? hub->tt_address : -1;
-
-	/* Add default pipe on default address */
-	usb_log_debug("Device(%d): Adding default target(0:0)\n", address);
-	ret = hcd_add_ep(hcd,
-	    default_target, USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL,
-	    CTRL_PIPE_MIN_PACKET_SIZE, CTRL_PIPE_MIN_PACKET_SIZE, 1,
-	    tt_address, port);
-	if (ret != EOK) {
-		usb_log_error("Device(%d): Failed to add default target: %s.",
-		    address, str_error(ret));
-		hcd_release_address(hcd, address);
-		return ret;
-	}
-
-	/* Get max packet size for default pipe */
-	usb_standard_device_descriptor_t desc = { 0 };
-	const usb_device_request_setup_packet_t get_device_desc_8 =
-	    GET_DEVICE_DESC(CTRL_PIPE_MIN_PACKET_SIZE);
-
-	// TODO CALLBACKS
-	usb_log_debug("Device(%d): Requesting first 8B of device descriptor.",
-	    address);
-	ssize_t got = hcd_send_batch_sync(hcd, default_target, USB_DIRECTION_IN,
-	    &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8,
-	    "read first 8 bytes of dev descriptor");
-
-	if (got != CTRL_PIPE_MIN_PACKET_SIZE) {
-		ret = got < 0 ? got : EOVERFLOW;
-		usb_log_error("Device(%d): Failed to get 8B of dev descr: %s.",
-		    address, str_error(ret));
-		hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
-		hcd_release_address(hcd, address);
-		return ret;
-	}
-
-	/* Register EP on the new address */
-	usb_log_debug("Device(%d): Registering control EP.", address);
-	ret = hcd_add_ep(hcd, target, USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL,
-	    ED_MPS_PACKET_SIZE_GET(uint16_usb2host(desc.max_packet_size)),
-	    ED_MPS_TRANS_OPPORTUNITIES_GET(uint16_usb2host(desc.max_packet_size)),
-	    ED_MPS_PACKET_SIZE_GET(uint16_usb2host(desc.max_packet_size)),
-	    tt_address, port);
-	if (ret != EOK) {
-		usb_log_error("Device(%d): Failed to register EP0: %s",
-		    address, str_error(ret));
-		hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
-		hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
-		hcd_release_address(hcd, address);
-		return ret;
-	}
-
-	/* Set new address */
-	const usb_device_request_setup_packet_t set_address =
-	    SET_ADDRESS(target.address);
-
-	usb_log_debug("Device(%d): Setting USB address.", address);
-	got = hcd_send_batch_sync(hcd, default_target, USB_DIRECTION_OUT,
-	    NULL, 0, *(uint64_t *)&set_address, "set address");
-
-	usb_log_debug("Device(%d): Removing default (0:0) EP.", address);
-	hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
-
-	if (got != 0) {
-		usb_log_error("Device(%d): Failed to set new address: %s.",
-		    address, str_error(got));
-		hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
-		hcd_release_address(hcd, address);
-		return got;
-	}
 
 	/* Get std device descriptor */
@@ -571,42 +426,102 @@
 
 	usb_log_debug("Device(%d): Requesting full device descriptor.",
-	    address);
-	got = hcd_send_batch_sync(hcd, target, USB_DIRECTION_IN,
-	    &desc, sizeof(desc), *(uint64_t *)&get_device_desc,
+	    device->address);
+	ssize_t got = hcd_send_batch_sync(hcd, device, control_ep, USB_DIRECTION_IN,
+	    (char *) &desc, sizeof(desc), *(uint64_t *)&get_device_desc,
 	    "read device descriptor");
-	if (ret != EOK) {
+	if (got < 0) {
+		err = got < 0 ? got : EOVERFLOW;
 		usb_log_error("Device(%d): Failed to set get dev descriptor: %s",
-		    address, str_error(ret));
-		hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
-		hcd_release_address(hcd, target.address);
-		return ret;
+		    device->address, str_error(err));
+		goto out;
 	}
 
 	/* Create match ids from the device descriptor */
-	match_id_list_t mids;
-	init_match_ids(&mids);
-
-	usb_log_debug("Device(%d): Creating match IDs.", address);
-	ret = create_match_ids(&mids, &desc);
-	if (ret != EOK) {
-		usb_log_error("Device(%d): Failed to create match ids: %s",
-		    address, str_error(ret));
-		hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
-		hcd_release_address(hcd, target.address);
-		return ret;
-	}
-
-	/* Register device */
-	usb_log_debug("Device(%d): Registering DDF device.", address);
-	ret = hcd_ddf_add_device(device, hub, port, address, speed, NULL, &mids);
+	usb_log_debug("Device(%d): Creating match IDs.", device->address);
+	if ((err = create_match_ids(&mids, &desc))) {
+		usb_log_error("Device(%d): Failed to create match ids: %s", device->address, str_error(err));
+		goto out;
+	}
+
+	list_foreach(mids.ids, link, const match_id_t, mid) {
+		ddf_fun_add_match_id(device->fun, mid->id, mid->score);
+	}
+
+out:
 	clean_match_ids(&mids);
-	if (ret != EOK) {
-		usb_log_error("Device(%d): Failed to register: %s.",
-		    address, str_error(ret));
-		hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
-		hcd_release_address(hcd, target.address);
-	}
-
-	return ret;
+	return err;
+}
+
+int hcd_ddf_device_online(ddf_fun_t *fun)
+{
+	assert(fun);
+
+	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
+	device_t *dev = ddf_fun_data_get(fun);
+	assert(dev);
+	assert(hcd->bus);
+
+	usb_log_info("Device(%d): Requested to be brought online.", dev->address);
+
+	return bus_online_device(hcd->bus, hcd, dev);
+}
+
+int hcd_ddf_device_offline(ddf_fun_t *fun)
+{
+	assert(fun);
+
+	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
+	device_t *dev = ddf_fun_data_get(fun);
+	assert(dev);
+	assert(hcd->bus);
+
+	usb_log_info("Device(%d): Requested to be taken offline.", dev->address);
+
+	return bus_offline_device(hcd->bus, hcd, dev);
+}
+
+static int hcd_ddf_new_device(hcd_t *hcd, ddf_dev_t *hc, device_t *hub, unsigned port)
+{
+	int err;
+	assert(hcd);
+	assert(hcd->bus);
+	assert(hub);
+	assert(hc);
+
+	device_t *dev = hcd_ddf_device_create(hc, hcd->bus->device_size);
+	if (!dev) {
+		usb_log_error("Failed to create USB device function.");
+		return ENOMEM;
+	}
+
+	dev->hub = hub;
+	dev->port = port;
+
+	if ((err = bus_enumerate_device(hcd->bus, hcd, dev))) {
+		usb_log_error("Failed to initialize USB dev memory structures.");
+		return err;
+	}
+
+	/* If the driver didn't name the dev when enumerating,
+	 * do it in some generic way.
+	 */
+	if (!ddf_fun_get_name(dev->fun)) {
+		device_set_default_name(dev);
+	}
+
+	if ((err = ddf_fun_bind(dev->fun))) {
+		usb_log_error("Device(%d): Failed to register: %s.", dev->address, str_error(err));
+		goto err_usb_dev;
+	}
+
+	fibril_mutex_lock(&hub->guard);
+	list_append(&dev->link, &hub->devices);
+	fibril_mutex_unlock(&hub->guard);
+
+	return EOK;
+
+err_usb_dev:
+	hcd_ddf_device_destroy(dev);
+	return err;
 }
 
@@ -616,14 +531,44 @@
  * @return Error code
  */
-int hcd_ddf_setup_root_hub(ddf_dev_t *device)
-{
-	assert(device);
-	hcd_t *hcd = dev_to_hcd(device);
+int hcd_setup_virtual_root_hub(hcd_t *hcd, ddf_dev_t *hc)
+{
+	int err;
+
+	assert(hc);
 	assert(hcd);
-
-	hcd_reserve_default_address(hcd, hcd->bus.max_speed);
-	const int ret = hcd_ddf_new_device(device, NULL, 0);
-	hcd_release_default_address(hcd);
-	return ret;
+	assert(hcd->bus);
+
+	if ((err = bus_reserve_default_address(hcd->bus, USB_SPEED_MAX))) {
+		usb_log_error("Failed to reserve default address for roothub setup: %s", str_error(err));
+		return err;
+	}
+
+	device_t *dev = hcd_ddf_device_create(hc, hcd->bus->device_size);
+	if (!dev) {
+		usb_log_error("Failed to create function for the root hub.");
+		goto err_default_address;
+	}
+
+	ddf_fun_set_name(dev->fun, "roothub");
+
+	/* Assign an address to the device */
+	if ((err = bus_enumerate_device(hcd->bus, hcd, dev))) {
+		usb_log_error("Failed to enumerate roothub device: %s", str_error(err));
+		goto err_usb_dev;
+	}
+
+	if ((err = ddf_fun_bind(dev->fun))) {
+		usb_log_error("Failed to register roothub: %s.", str_error(err));
+		goto err_usb_dev;
+	}
+
+	bus_release_default_address(hcd->bus);
+	return EOK;
+
+err_usb_dev:
+	hcd_ddf_device_destroy(dev);
+err_default_address:
+	bus_release_default_address(hcd->bus);
+	return err;
 }
 
@@ -638,6 +583,5 @@
  * This function does all the ddf work for hc driver.
  */
-int hcd_ddf_setup_hc(ddf_dev_t *device, usb_speed_t max_speed,
-    size_t bw, bw_count_func_t bw_count)
+int hcd_ddf_setup_hc(ddf_dev_t *device)
 {
 	assert(device);
@@ -648,6 +592,5 @@
 		return ENOMEM;
 	}
-	instance->root_hub = NULL;
-	hcd_init(&instance->hcd, max_speed, bw, bw_count);
+	hcd_init(&instance->hcd);
 
 	int ret = ENOMEM;
@@ -748,8 +691,10 @@
     const hw_res_list_parsed_t *hw_res,
     interrupt_handler_t handler,
-    int (*gen_irq_code)(irq_code_t *, const hw_res_list_parsed_t *hw_res))
-{
-
+    irq_code_gen_t gen_irq_code)
+{
 	assert(device);
+
+	hcd_t *hcd = dev_to_hcd(device);
+
 	if (!handler || !gen_irq_code)
 		return ENOTSUP;
@@ -757,5 +702,5 @@
 	irq_code_t irq_code = {0};
 
-	const int irq = gen_irq_code(&irq_code, hw_res);
+	const int irq = gen_irq_code(&irq_code, hcd, hw_res);
 	if (irq < 0) {
 		usb_log_error("Failed to generate IRQ code: %s.\n",
@@ -777,5 +722,5 @@
 	int ret = hcd_ddf_enable_interrupt(device, irq);
 	if (ret != EOK) {
-		usb_log_error("Failed to register interrupt handler: %s.\n",
+		usb_log_error("Failed to enable interrupts: %s.\n",
 		    str_error(ret));
 		unregister_interrupt_handler(device, irq_cap);
@@ -844,18 +789,6 @@
 {
 	assert(driver);
-	static const struct { size_t bw; bw_count_func_t bw_count; }bw[] = {
-	    [USB_SPEED_FULL] = { .bw = BANDWIDTH_AVAILABLE_USB11,
-	                         .bw_count = bandwidth_count_usb11 },
-	    [USB_SPEED_HIGH] = { .bw = BANDWIDTH_AVAILABLE_USB11,
-	                         .bw_count = bandwidth_count_usb11 },
-	};
 
 	int ret = EOK;
-	const usb_speed_t speed = driver->hc_speed;
-	if (speed >= ARRAY_SIZE(bw) || bw[speed].bw == 0) {
-		usb_log_error("Driver `%s' reported unsupported speed: %s",
-		    driver->name, usb_str_speed(speed));
-		return ENOTSUP;
-	}
 
 	hw_res_list_parsed_t hw_res;
@@ -868,11 +801,20 @@
 	}
 
-	ret = hcd_ddf_setup_hc(device, speed, bw[speed].bw, bw[speed].bw_count);
+	ret = hcd_ddf_setup_hc(device);
 	if (ret != EOK) {
 		usb_log_error("Failed to setup generic HCD.\n");
-		hw_res_list_parsed_clean(&hw_res);
-		return ret;
-	}
-
+		goto err_hw_res;
+	}
+
+	hcd_t *hcd = dev_to_hcd(device);
+
+	if (driver->init)
+		ret = driver->init(hcd, &hw_res, device);
+	if (ret != EOK) {
+		usb_log_error("Failed to init HCD.\n");
+		goto err_hcd;
+	}
+
+	/* Setup interrupts  */
 	interrupt_handler_t *irq_handler =
 	    driver->irq_handler ? driver->irq_handler : ddf_hcd_gen_irq_handler;
@@ -884,20 +826,19 @@
 	}
 
+	/* Claim the device from BIOS */
 	if (driver->claim)
-		ret = driver->claim(device);
+		ret = driver->claim(hcd, device);
 	if (ret != EOK) {
-		usb_log_error("Failed to claim `%s' for driver `%s'",
-		    ddf_dev_get_name(device), driver->name);
-		return ret;
-	}
-
-
-	/* Init hw driver */
-	hcd_t *hcd = dev_to_hcd(device);
-	ret = driver->init(hcd, &hw_res, irqs_enabled);
-	hw_res_list_parsed_clean(&hw_res);
+		usb_log_error("Failed to claim `%s' for driver `%s': %s",
+		    ddf_dev_get_name(device), driver->name, str_error(ret));
+		goto err_irq;
+	}
+
+	/* Start hw driver */
+	if (driver->start)
+		ret = driver->start(hcd, irqs_enabled);
 	if (ret != EOK) {
-		usb_log_error("Failed to init HCD: %s.\n", str_error(ret));
-		goto irq_unregister;
+		usb_log_error("Failed to start HCD: %s.\n", str_error(ret));
+		goto err_irq;
 	}
 
@@ -908,5 +849,5 @@
 			usb_log_error("Failed to create polling fibril\n");
 			ret = ENOMEM;
-			goto irq_unregister;
+			goto err_started;
 		}
 		fibril_add_ready(hcd->polling_fibril);
@@ -919,14 +860,10 @@
 	 * needs to be ready at this time.
 	 */
-	ret = hcd_ddf_setup_root_hub(device);
+	if (driver->setup_root_hub)
+		ret = driver->setup_root_hub(hcd, device);
 	if (ret != EOK) {
 		usb_log_error("Failed to setup HC root hub: %s.\n",
 		    str_error(ret));
-		driver->fini(dev_to_hcd(device));
-irq_unregister:
-		/* Unregistering non-existent should be ok */
-		unregister_interrupt_handler(device, irq_cap);
-		hcd_ddf_clean_hc(device);
-		return ret;
+		goto err_polling;
 	}
 
@@ -934,5 +871,22 @@
 	    driver->name, ddf_dev_get_name(device));
 	return EOK;
-}
+
+err_polling:
+	// TODO: Stop the polling fibril (refactor the interrupt_polling func)
+	//
+err_started:
+	if (driver->stop)
+		driver->stop(hcd);
+err_irq:
+	unregister_interrupt_handler(device, irq_cap);
+	if (driver->fini)
+		driver->fini(hcd);
+err_hcd:
+	hcd_ddf_clean_hc(device);
+err_hw_res:
+	hw_res_list_parsed_clean(&hw_res);
+	return ret;
+}
+
 /**
  * @}
Index: uspace/lib/usbhost/src/endpoint.c
===================================================================
--- uspace/lib/usbhost/src/endpoint.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbhost/src/endpoint.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
  * All rights reserved.
  *
@@ -35,157 +36,121 @@
 
 #include <usb/host/endpoint.h>
+#include <usb/host/usb_transfer_batch.h>
+#include <usb/host/bus.h>
 
 #include <assert.h>
+#include <atomic.h>
+#include <mem.h>
 #include <stdlib.h>
-#include <atomic.h>
 
-/** Allocate ad initialize endpoint_t structure.
- * @param address USB address.
- * @param endpoint USB endpoint number.
- * @param direction Communication direction.
- * @param type USB transfer type.
- * @param speed Communication speed.
- * @param max_packet_size Maximum size of data packets.
- * @param bw Required bandwidth.
- * @return Pointer to initialized endpoint_t structure, NULL on failure.
+/** Initialize provided endpoint structure.
  */
-endpoint_t * endpoint_create(usb_address_t address, usb_endpoint_t endpoint,
-    usb_direction_t direction, usb_transfer_type_t type, usb_speed_t speed,
-    size_t max_packet_size, unsigned packets, size_t bw,
-    usb_address_t tt_address, unsigned tt_p)
+void endpoint_init(endpoint_t *ep, bus_t *bus)
 {
-	endpoint_t *instance = malloc(sizeof(endpoint_t));
-	if (instance) {
-		atomic_set(&instance->refcnt, 0);
-		instance->address = address;
-		instance->endpoint = endpoint;
-		instance->direction = direction;
-		instance->transfer_type = type;
-		instance->speed = speed;
-		instance->max_packet_size = max_packet_size;
-		instance->packets = packets;
-		instance->bandwidth = bw;
-		instance->toggle = 0;
-		instance->active = false;
-		instance->tt.address = tt_address;
-		instance->tt.port = tt_p;
-		instance->hc_data.data = NULL;
-		instance->hc_data.toggle_get = NULL;
-		instance->hc_data.toggle_set = NULL;
-		link_initialize(&instance->link);
-		fibril_mutex_initialize(&instance->guard);
-		fibril_condvar_initialize(&instance->avail);
-	}
-	return instance;
+	memset(ep, 0, sizeof(endpoint_t));
+
+	ep->bus = bus;
+	atomic_set(&ep->refcnt, 0);
+	link_initialize(&ep->link);
+	fibril_mutex_initialize(&ep->guard);
+	fibril_condvar_initialize(&ep->avail);
 }
 
-/** Properly dispose of endpoint_t structure.
- * @param instance endpoint_t structure.
- */
-void endpoint_destroy(endpoint_t *instance)
+void endpoint_add_ref(endpoint_t *ep)
 {
-	assert(instance);
-	assert(!instance->active);
-	assert(instance->hc_data.data == NULL);
-	free(instance);
+	atomic_inc(&ep->refcnt);
 }
 
-void endpoint_add_ref(endpoint_t *instance)
+void endpoint_del_ref(endpoint_t *ep)
 {
-	atomic_inc(&instance->refcnt);
-}
+	if (atomic_predec(&ep->refcnt) == 0) {
+		if (ep->bus->ops.destroy_endpoint) {
+			ep->bus->ops.destroy_endpoint(ep);
+		}
+		else {
+			assert(ep->active_batch == NULL);
 
-void endpoint_del_ref(endpoint_t *instance)
-{
-	if (atomic_predec(&instance->refcnt) == 0)
-		endpoint_destroy(instance);
-}
-
-/** Set device specific data and hooks.
- * @param instance endpoint_t structure.
- * @param data device specific data.
- * @param toggle_get Hook to call when retrieving value of toggle bit.
- * @param toggle_set Hook to call when setting the value of toggle bit.
- */
-void endpoint_set_hc_data(endpoint_t *instance,
-    void *data, int (*toggle_get)(void *), void (*toggle_set)(void *, int))
-{
-	assert(instance);
-	fibril_mutex_lock(&instance->guard);
-	instance->hc_data.data = data;
-	instance->hc_data.toggle_get = toggle_get;
-	instance->hc_data.toggle_set = toggle_set;
-	fibril_mutex_unlock(&instance->guard);
-}
-
-/** Clear device specific data and hooks.
- * @param instance endpoint_t structure.
- * @note This function does not free memory pointed to by data pointer.
- */
-void endpoint_clear_hc_data(endpoint_t *instance)
-{
-	assert(instance);
-	endpoint_set_hc_data(instance, NULL, NULL, NULL);
+			/* Assume mostly the eps will be allocated by malloc. */
+			free(ep);
+		}
+	}
 }
 
 /** Mark the endpoint as active and block access for further fibrils.
- * @param instance endpoint_t structure.
+ * @param ep endpoint_t structure.
  */
-void endpoint_use(endpoint_t *instance)
+void endpoint_activate_locked(endpoint_t *ep, usb_transfer_batch_t *batch)
 {
-	assert(instance);
-	/* Add reference for active endpoint. */
-	endpoint_add_ref(instance);
-	fibril_mutex_lock(&instance->guard);
-	while (instance->active)
-		fibril_condvar_wait(&instance->avail, &instance->guard);
-	instance->active = true;
-	fibril_mutex_unlock(&instance->guard);
+	assert(ep);
+	assert(batch);
+	assert(batch->ep == ep);
+	assert(fibril_mutex_is_locked(&ep->guard));
+
+	while (ep->active_batch != NULL)
+		fibril_condvar_wait(&ep->avail, &ep->guard);
+	ep->active_batch = batch;
 }
 
 /** Mark the endpoint as inactive and allow access for further fibrils.
- * @param instance endpoint_t structure.
+ * @param ep endpoint_t structure.
  */
-void endpoint_release(endpoint_t *instance)
+void endpoint_deactivate_locked(endpoint_t *ep)
 {
-	assert(instance);
-	fibril_mutex_lock(&instance->guard);
-	instance->active = false;
-	fibril_mutex_unlock(&instance->guard);
-	fibril_condvar_signal(&instance->avail);
-	/* Drop reference for active endpoint. */
-	endpoint_del_ref(instance);
+	assert(ep);
+	assert(fibril_mutex_is_locked(&ep->guard));
+
+	if (ep->active_batch && ep->active_batch->error == EOK)
+		usb_transfer_batch_reset_toggle(ep->active_batch);
+
+	ep->active_batch = NULL;
+	fibril_condvar_signal(&ep->avail);
 }
 
-/** Get the value of toggle bit.
- * @param instance endpoint_t structure.
- * @note Will use provided hook.
+/** Abort an active batch on endpoint, if any.
+ *
+ * @param[in] ep endpoint_t structure.
  */
-int endpoint_toggle_get(endpoint_t *instance)
+void endpoint_abort(endpoint_t *ep)
 {
-	assert(instance);
-	fibril_mutex_lock(&instance->guard);
-	if (instance->hc_data.toggle_get)
-		instance->toggle =
-		    instance->hc_data.toggle_get(instance->hc_data.data);
-	const int ret = instance->toggle;
-	fibril_mutex_unlock(&instance->guard);
-	return ret;
+	assert(ep);
+
+	fibril_mutex_lock(&ep->guard);
+	usb_transfer_batch_t *batch = ep->active_batch;
+	endpoint_deactivate_locked(ep);
+	fibril_mutex_unlock(&ep->guard);
+
+	if (batch)
+		usb_transfer_batch_abort(batch);
 }
 
-/** Set the value of toggle bit.
- * @param instance endpoint_t structure.
- * @note Will use provided hook.
+/** Get the value of toggle bit. Either uses the toggle_get op, or just returns
+ * the value of the toggle.
+ * @param ep endpoint_t structure.
  */
-void endpoint_toggle_set(endpoint_t *instance, int toggle)
+int endpoint_toggle_get(endpoint_t *ep)
 {
-	assert(instance);
-	assert(toggle == 0 || toggle == 1);
-	fibril_mutex_lock(&instance->guard);
-	instance->toggle = toggle;
-	if (instance->hc_data.toggle_set)
-		instance->hc_data.toggle_set(instance->hc_data.data, toggle);
-	fibril_mutex_unlock(&instance->guard);
+	assert(ep);
+
+	return ep->bus->ops.endpoint_get_toggle
+	    ? ep->bus->ops.endpoint_get_toggle(ep)
+	    : ep->toggle;
 }
+
+/** Set the value of toggle bit. Either uses the toggle_set op, or just sets
+ * the toggle inside.
+ * @param ep endpoint_t structure.
+ */
+void endpoint_toggle_set(endpoint_t *ep, bool toggle)
+{
+	assert(ep);
+
+	if (ep->bus->ops.endpoint_set_toggle) {
+		ep->bus->ops.endpoint_set_toggle(ep, toggle);
+	}
+	else {
+		ep->toggle = toggle;
+	}
+}
+
 
 /**
Index: uspace/lib/usbhost/src/hcd.c
===================================================================
--- uspace/lib/usbhost/src/hcd.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbhost/src/hcd.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -41,46 +41,7 @@
 #include <errno.h>
 #include <usb_iface.h>
+#include <str_error.h>
 
 #include "hcd.h"
-
-/** Calls ep_add_hook upon endpoint registration.
- * @param ep Endpoint to be registered.
- * @param arg hcd_t in disguise.
- * @return Error code.
- */
-static int register_helper(endpoint_t *ep, void *arg)
-{
-	hcd_t *hcd = arg;
-	assert(ep);
-	assert(hcd);
-	if (hcd->ops.ep_add_hook)
-		return hcd->ops.ep_add_hook(hcd, ep);
-	return EOK;
-}
-
-/** Calls ep_remove_hook upon endpoint removal.
- * @param ep Endpoint to be unregistered.
- * @param arg hcd_t in disguise.
- */
-static void unregister_helper(endpoint_t *ep, void *arg)
-{
-	hcd_t *hcd = arg;
-	assert(ep);
-	assert(hcd);
-	if (hcd->ops.ep_remove_hook)
-		hcd->ops.ep_remove_hook(hcd, ep);
-}
-
-/** Calls ep_remove_hook upon endpoint removal. Prints warning.
- *  * @param ep Endpoint to be unregistered.
- *   * @param arg hcd_t in disguise.
- *    */
-static void unregister_helper_warn(endpoint_t *ep, void *arg)
-{
-        assert(ep);
-        usb_log_warning("Endpoint %d:%d %s was left behind, removing.\n",
-            ep->address, ep->endpoint, usb_str_direction(ep->direction));
-	unregister_helper(ep, arg);
-}
 
 
@@ -93,11 +54,8 @@
  * @param bw_count Bandwidth compute function, passed to endpoint manager.
  */
-void hcd_init(hcd_t *hcd, usb_speed_t max_speed, size_t bandwidth,
-    bw_count_func_t bw_count)
-{
+void hcd_init(hcd_t *hcd) {
 	assert(hcd);
-	usb_bus_init(&hcd->bus, bandwidth, bw_count, max_speed);
 
-	hcd_set_implementation(hcd, NULL, NULL);
+	hcd_set_implementation(hcd, NULL, NULL, NULL);
 }
 
@@ -106,6 +64,5 @@
 	assert(hcd);
 	usb_address_t address = 0;
-	const int ret = usb_bus_request_address(
-	    &hcd->bus, &address, false, speed);
+	const int ret = bus_request_address(hcd->bus, &address, false, speed);
 	if (ret != EOK)
 		return ret;
@@ -113,60 +70,6 @@
 }
 
-int hcd_release_address(hcd_t *hcd, usb_address_t address)
-{
-	assert(hcd);
-	return usb_bus_remove_address(&hcd->bus, address,
-	    unregister_helper_warn, hcd);
-}
-
-int hcd_reserve_default_address(hcd_t *hcd, usb_speed_t speed)
-{
-	assert(hcd);
-	usb_address_t address = 0;
-	return usb_bus_request_address(&hcd->bus, &address, true, speed);
-}
-
-int hcd_add_ep(hcd_t *hcd, usb_target_t target, usb_direction_t dir,
-    usb_transfer_type_t type, size_t max_packet_size, unsigned packets,
-    size_t size, usb_address_t tt_address, unsigned tt_port)
-{
-	assert(hcd);
-	return usb_bus_add_ep(&hcd->bus, target.address,
-	    target.endpoint, dir, type, max_packet_size, packets, size,
-	    register_helper, hcd, tt_address, tt_port);
-}
-
-int hcd_remove_ep(hcd_t *hcd, usb_target_t target, usb_direction_t dir)
-{
-	assert(hcd);
-	return usb_bus_remove_ep(&hcd->bus, target.address,
-	    target.endpoint, dir, unregister_helper, hcd);
-}
-
-
-typedef struct {
-	void *original_data;
-	usbhc_iface_transfer_out_callback_t original_callback;
-	usb_target_t target;
-	hcd_t *hcd;
-} toggle_t;
-
-static void toggle_reset_callback(int retval, void *arg)
-{
-	assert(arg);
-	toggle_t *toggle = arg;
-	if (retval == EOK) {
-		usb_log_debug2("Reseting toggle on %d:%d.\n",
-		    toggle->target.address, toggle->target.endpoint);
-		usb_bus_reset_toggle(&toggle->hcd->bus,
-		    toggle->target, toggle->target.endpoint == 0);
-	}
-
-	toggle->original_callback(retval, toggle->original_data);
-}
-
 /** Prepare generic usb_transfer_batch and schedule it.
  * @param hcd Host controller driver.
- * @param fun DDF fun
  * @param target address and endpoint number.
  * @param setup_data Data to use in setup stage (Control communication type)
@@ -177,32 +80,11 @@
  * @return Error code.
  */
-int hcd_send_batch(
-    hcd_t *hcd, usb_target_t target, usb_direction_t direction,
-    void *data, size_t size, uint64_t setup_data,
-    usbhc_iface_transfer_in_callback_t in,
-    usbhc_iface_transfer_out_callback_t out, void *arg, const char* name)
+int hcd_send_batch(hcd_t *hcd, device_t *device, usb_target_t target,
+    usb_direction_t direction, char *data, size_t size, uint64_t setup_data,
+    usbhc_iface_transfer_callback_t on_complete, void *arg, const char *name)
 {
 	assert(hcd);
+	assert(device->address == target.address);
 
-	endpoint_t *ep = usb_bus_find_ep(&hcd->bus,
-	    target.address, target.endpoint, direction);
-	if (ep == NULL) {
-		usb_log_error("Endpoint(%d:%d) not registered for %s.\n",
-		    target.address, target.endpoint, name);
-		return ENOENT;
-	}
-
-	usb_log_debug2("%s %d:%d %zu(%zu).\n",
-	    name, target.address, target.endpoint, size, ep->max_packet_size);
-
-	const size_t bw = bandwidth_count_usb11(
-	    ep->speed, ep->transfer_type, size, ep->max_packet_size);
-	/* Check if we have enough bandwidth reserved */
-	if (ep->bandwidth < bw) {
-		usb_log_error("Endpoint(%d:%d) %s needs %zu bw "
-		    "but only %zu is reserved.\n",
-		    ep->address, ep->endpoint, name, bw, ep->bandwidth);
-		return ENOSPC;
-	}
 	if (!hcd->ops.schedule) {
 		usb_log_error("HCD does not implement scheduler.\n");
@@ -210,26 +92,26 @@
 	}
 
-	/* Check for commands that reset toggle bit */
-	if (ep->transfer_type == USB_TRANSFER_CONTROL) {
-		const int reset_toggle = usb_request_needs_toggle_reset(
-		    (usb_device_request_setup_packet_t *) &setup_data);
-		if (reset_toggle >= 0) {
-			assert(out);
-			toggle_t *toggle = malloc(sizeof(toggle_t));
-			if (!toggle)
-				return ENOMEM;
-			toggle->target.address = target.address;
-			toggle->target.endpoint = reset_toggle;
-			toggle->original_callback = out;
-			toggle->original_data = arg;
-			toggle->hcd = hcd;
-
-			arg = toggle;
-			out = toggle_reset_callback;
-		}
+	endpoint_t *ep = bus_find_endpoint(hcd->bus, device, target, direction);
+	if (ep == NULL) {
+		usb_log_error("Endpoint(%d:%d) not registered for %s.\n",
+		    device->address, target.endpoint, name);
+		return ENOENT;
 	}
 
-	usb_transfer_batch_t *batch = usb_transfer_batch_create(
-	    ep, data, size, setup_data, in, out, arg);
+	// TODO cut here aka provide helper to call with instance of endpoint_t in hand
+
+	usb_log_debug2("%s %d:%d %zu(%zu).\n",
+	    name, target.address, target.endpoint, size, ep->max_packet_size);
+
+	const size_t bw = bus_count_bw(ep, size);
+	/* Check if we have enough bandwidth reserved */
+	if (ep->bandwidth < bw) {
+		usb_log_error("Endpoint(%d:%d) %s needs %zu bw "
+		    "but only %zu is reserved.\n",
+		    device->address, ep->endpoint, name, bw, ep->bandwidth);
+		return ENOSPC;
+	}
+
+	usb_transfer_batch_t *batch = usb_transfer_batch_create(ep);
 	if (!batch) {
 		usb_log_error("Failed to create transfer batch.\n");
@@ -237,7 +119,22 @@
 	}
 
+	batch->target = target;
+	batch->buffer = data;
+	batch->buffer_size = size;
+	batch->setup.packed = setup_data;
+	batch->dir = direction;
+	batch->on_complete = on_complete;
+	batch->on_complete_data = arg;
+
+	/* Check for commands that reset toggle bit */
+	if (ep->transfer_type == USB_TRANSFER_CONTROL)
+		batch->toggle_reset_mode
+			= usb_request_get_toggle_reset_mode(&batch->setup.packet);
+
 	const int ret = hcd->ops.schedule(hcd, batch);
-	if (ret != EOK)
+	if (ret != EOK) {
+		usb_log_warning("Batch %p failed to schedule: %s", batch, str_error(ret));
 		usb_transfer_batch_destroy(batch);
+	}
 
 	/* Drop our own reference to ep. */
@@ -248,47 +145,48 @@
 
 typedef struct {
-	volatile unsigned done;
-	int ret;
-	size_t size;
+	fibril_mutex_t done_mtx;
+	fibril_condvar_t done_cv;
+	unsigned done;
+
+	size_t transfered_size;
+	int error;
 } sync_data_t;
 
-static void transfer_in_cb(int ret, size_t size, void* data)
+static int sync_transfer_complete(void *arg, int error, size_t transfered_size)
 {
-	sync_data_t *d = data;
+	sync_data_t *d = arg;
 	assert(d);
-	d->ret = ret;
+	d->transfered_size = transfered_size;
+	d->error = error;
+	fibril_mutex_lock(&d->done_mtx);
 	d->done = 1;
-	d->size = size;
+	fibril_condvar_broadcast(&d->done_cv);
+	fibril_mutex_unlock(&d->done_mtx);
+	return EOK;
 }
 
-static void transfer_out_cb(int ret, void* data)
-{
-	sync_data_t *d = data;
-	assert(data);
-	d->ret = ret;
-	d->done = 1;
-}
-
-/** this is really ugly version of sync usb communication */
-ssize_t hcd_send_batch_sync(
-    hcd_t *hcd, usb_target_t target, usb_direction_t dir,
-    void *data, size_t size, uint64_t setup_data, const char* name)
+ssize_t hcd_send_batch_sync(hcd_t *hcd, device_t *device, usb_target_t target,
+    usb_direction_t direction, char *data, size_t size, uint64_t setup_data,
+    const char *name)
 {
 	assert(hcd);
-	sync_data_t sd = { .done = 0, .ret = EBUSY, .size = size };
+	sync_data_t sd = { .done = 0 };
+	fibril_mutex_initialize(&sd.done_mtx);
+	fibril_condvar_initialize(&sd.done_cv);
 
-	const int ret = hcd_send_batch(hcd, target, dir, data, size, setup_data,
-	    dir == USB_DIRECTION_IN ? transfer_in_cb : NULL,
-	    dir == USB_DIRECTION_OUT ? transfer_out_cb : NULL, &sd, name);
+	const int ret = hcd_send_batch(hcd, device, target, direction,
+	    data, size, setup_data,
+	    sync_transfer_complete, &sd, name);
 	if (ret != EOK)
 		return ret;
 
-	while (!sd.done) {
-		async_usleep(1000);
-	}
+	fibril_mutex_lock(&sd.done_mtx);
+	while (!sd.done)
+		fibril_condvar_wait(&sd.done_cv, &sd.done_mtx);
+	fibril_mutex_unlock(&sd.done_mtx);
 
-	if (sd.ret == EOK)
-		return sd.size;
-	return sd.ret;
+	return (sd.error == EOK)
+		? (ssize_t) sd.transfered_size
+		: (ssize_t) sd.error;
 }
 
Index: uspace/lib/usbhost/src/usb2_bus.c
===================================================================
--- uspace/lib/usbhost/src/usb2_bus.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
+++ uspace/lib/usbhost/src/usb2_bus.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -0,0 +1,505 @@
+/*
+ * 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 libusbhost
+ * @{
+ */
+/** @file
+ * HC Endpoint management.
+ */
+
+#include <usb/host/usb2_bus.h>
+#include <usb/host/endpoint.h>
+#include <usb/host/ddf_helpers.h>
+#include <usb/debug.h>
+#include <usb/request.h>
+#include <usb/descriptor.h>
+#include <usb/usb.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <macros.h>
+#include <str_error.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+/** Ops receive generic bus_t pointer. */
+static inline usb2_bus_t *bus_to_usb2_bus(bus_t *bus_base)
+{
+	assert(bus_base);
+	return (usb2_bus_t *) bus_base;
+}
+
+/** Get list that holds endpoints for given address.
+ * @param bus usb2_bus structure, non-null.
+ * @param addr USB address, must be >= 0.
+ * @return Pointer to the appropriate list.
+ */
+static list_t * get_list(usb2_bus_t *bus, usb_address_t addr)
+{
+	assert(bus);
+	assert(addr >= 0);
+	return &bus->devices[addr % ARRAY_SIZE(bus->devices)].endpoint_list;
+}
+
+/** Get speed assigned to USB address.
+ *
+ * @param[in] bus Device manager structure to use.
+ * @param[in] address Address the caller wants to find.
+ * @param[out] speed Assigned speed.
+ * @return Error code.
+ */
+static int usb2_bus_get_speed(bus_t *bus_base, usb_address_t address, usb_speed_t *speed)
+{
+	usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
+
+	if (!usb_address_is_valid(address)) {
+		return EINVAL;
+	}
+
+	const int ret = bus->devices[address].occupied ? EOK : ENOENT;
+	if (speed && bus->devices[address].occupied) {
+		*speed = bus->devices[address].speed;
+	}
+
+	return ret;
+}
+
+static const usb_endpoint_desc_t usb2_default_control_ep = {
+	.endpoint_no = 0,
+	.transfer_type = USB_TRANSFER_CONTROL,
+	.direction = USB_DIRECTION_BOTH,
+	.max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
+	.packets = 1,
+};
+
+
+static const usb_target_t usb2_default_target = {{
+	.address = USB_ADDRESS_DEFAULT,
+	.endpoint = 0,
+}};
+
+static int usb2_bus_address_device(bus_t *bus, hcd_t *hcd, device_t *dev)
+{
+	int err;
+
+	/* The default address is currently reserved for this device */
+	dev->address = USB_ADDRESS_DEFAULT;
+
+	/** Reserve address early, we want pretty log messages */
+	usb_address_t address;
+	if ((err = bus_request_address(bus, &address, false, dev->speed))) {
+		usb_log_error("Failed to reserve new address: %s.",
+		    str_error(err));
+		return err;
+	}
+	usb_log_debug("Device(%d): Reserved new address.", address);
+
+	/* Add default pipe on default address */
+	usb_log_debug("Device(%d): Adding default target (0:0)", address);
+
+	endpoint_t *default_ep;
+	err = bus_add_endpoint(bus, dev, &usb2_default_control_ep, &default_ep);
+	if (err != EOK) {
+		usb_log_error("Device(%d): Failed to add default target: %s.",
+		    address, str_error(err));
+		goto err_address;
+	}
+
+	/* Get max packet size for default pipe */
+	usb_standard_device_descriptor_t desc = { 0 };
+	const usb_device_request_setup_packet_t get_device_desc_8 =
+	    GET_DEVICE_DESC(CTRL_PIPE_MIN_PACKET_SIZE);
+
+	usb_log_debug("Device(%d): Requesting first 8B of device descriptor.",
+	    address);
+	ssize_t got = hcd_send_batch_sync(hcd, dev, usb2_default_target, USB_DIRECTION_IN,
+	    (char *) &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8,
+	    "read first 8 bytes of dev descriptor");
+
+	if (got != CTRL_PIPE_MIN_PACKET_SIZE) {
+		err = got < 0 ? got : EOVERFLOW;
+		usb_log_error("Device(%d): Failed to get 8B of dev descr: %s.",
+		    address, str_error(err));
+		goto err_default_control_ep;
+	}
+
+	/* Set new address */
+	const usb_device_request_setup_packet_t set_address = SET_ADDRESS(address);
+
+	usb_log_debug("Device(%d): Setting USB address.", address);
+	err = hcd_send_batch_sync(hcd, dev, usb2_default_target, USB_DIRECTION_OUT,
+	    NULL, 0, *(uint64_t *)&set_address, "set address");
+	if (err != 0) {
+		usb_log_error("Device(%d): Failed to set new address: %s.",
+		    address, str_error(got));
+		goto err_default_control_ep;
+	}
+
+	/* We need to remove ep before we change the address */
+	if ((err = bus_remove_endpoint(bus, default_ep))) {
+		usb_log_error("Device(%d): Failed to unregister default target: %s", address, str_error(err));
+		goto err_address;
+	}
+	endpoint_del_ref(default_ep);
+
+	dev->address = address;
+
+	const usb_endpoint_desc_t control_ep = {
+		.endpoint_no = 0,
+		.transfer_type = USB_TRANSFER_CONTROL,
+		.direction = USB_DIRECTION_BOTH,
+		.max_packet_size = ED_MPS_PACKET_SIZE_GET(uint16_usb2host(desc.max_packet_size)),
+		.packets = ED_MPS_TRANS_OPPORTUNITIES_GET(uint16_usb2host(desc.max_packet_size)),
+	};
+
+	/* Register EP on the new address */
+	usb_log_debug("Device(%d): Registering control EP.", address);
+	err = bus_add_endpoint(bus, dev, &control_ep, NULL);
+	if (err != EOK) {
+		usb_log_error("Device(%d): Failed to register EP0: %s",
+		    address, str_error(err));
+		goto err_address;
+	}
+
+	return EOK;
+
+err_default_control_ep:
+	bus_remove_endpoint(bus, default_ep);
+	endpoint_del_ref(default_ep);
+err_address:
+	bus_release_address(bus, address);
+	return err;
+}
+
+/** Enumerate a new USB device
+ */
+static int usb2_bus_enumerate_device(bus_t *bus, hcd_t *hcd, device_t *dev)
+{
+	int err;
+
+	/* The speed of the new device was reported by the hub when reserving
+	 * default address.
+	 */
+	if ((err = usb2_bus_get_speed(bus, USB_ADDRESS_DEFAULT, &dev->speed))) {
+		usb_log_error("Failed to verify speed: %s.", str_error(err));
+		return err;
+	}
+	usb_log_debug("Found new %s speed USB device.", usb_str_speed(dev->speed));
+
+	if (dev->hub) {
+		/* 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;
+		}
+	}
+	else {
+		dev->tt = (usb_tt_address_t) {
+			.address = -1,
+			.port = 0,
+		};
+	}
+
+	/* Assign an address to the device */
+	if ((err = usb2_bus_address_device(bus, hcd, dev))) {
+		usb_log_error("Failed to setup address of the new device: %s", str_error(err));
+		return err;
+	}
+
+	/* Read the device descriptor, derive the match ids */
+	if ((err = hcd_ddf_device_explore(hcd, dev))) {
+		usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
+		bus_release_address(bus, dev->address);
+		return err;
+	}
+
+	return EOK;
+}
+
+/** Get a free USB address
+ *
+ * @param[in] bus Device manager structure to use.
+ * @return Free address, or error code.
+ */
+static int usb_bus_get_free_address(usb2_bus_t *bus, usb_address_t *addr)
+{
+	usb_address_t new_address = bus->last_address;
+	do {
+		new_address = (new_address + 1) % USB_ADDRESS_COUNT;
+		if (new_address == USB_ADDRESS_DEFAULT)
+			new_address = 1;
+		if (new_address == bus->last_address)
+			return ENOSPC;
+	} while (bus->devices[new_address].occupied);
+
+	assert(new_address != USB_ADDRESS_DEFAULT);
+	bus->last_address = new_address;
+
+	*addr = new_address;
+	return EOK;
+}
+
+/** Find endpoint.
+ * @param bus usb_bus structure, non-null.
+ * @param target Endpoint address.
+ * @param direction Communication direction.
+ * @return Pointer to endpoint_t structure representing given communication
+ * target, NULL if there is no such endpoint registered.
+ * @note Assumes that the internal mutex is locked.
+ */
+static endpoint_t *usb2_bus_find_ep(bus_t *bus_base, device_t *device, usb_target_t target, usb_direction_t direction)
+{
+	usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
+
+	assert(device->address == target.address);
+
+	list_foreach(*get_list(bus, target.address), link, endpoint_t, ep) {
+		if (((direction == ep->direction)
+		       || (ep->direction == USB_DIRECTION_BOTH)
+		       || (direction == USB_DIRECTION_BOTH))
+		    && (target.endpoint == ep->endpoint))
+			return ep;
+	}
+	return NULL;
+}
+
+static endpoint_t *usb2_bus_create_ep(bus_t *bus)
+{
+	endpoint_t *ep = malloc(sizeof(endpoint_t));
+	if (!ep)
+		return NULL;
+
+	endpoint_init(ep, bus);
+	return ep;
+}
+
+static usb_target_t usb2_ep_to_target(endpoint_t *ep)
+{
+	assert(ep);
+	assert(ep->device);
+
+	return (usb_target_t) {{
+		.address = ep->device->address,
+		.endpoint = ep->endpoint,
+	}};
+}
+
+/** Register an endpoint to the bus. Reserves bandwidth.
+ * @param bus usb_bus structure, non-null.
+ * @param endpoint USB endpoint number.
+ */
+static int usb2_bus_register_ep(bus_t *bus_base, device_t *device, endpoint_t *ep, const usb_endpoint_desc_t *desc)
+{
+	usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
+	assert(ep);
+
+	ep->device = device;
+
+	/* Extract USB2-related information from endpoint_desc */
+	ep->endpoint = desc->endpoint_no;
+	ep->direction = desc->direction;
+	ep->transfer_type = desc->transfer_type;
+	ep->max_packet_size = desc->max_packet_size;
+	ep->packets = desc->packets;
+
+	ep->bandwidth = bus_base->ops.count_bw(ep, desc->max_packet_size);
+
+	/* Check for existence */
+	if (usb2_bus_find_ep(bus_base, ep->device, usb2_ep_to_target(ep), ep->direction))
+		return EEXIST;
+
+	/* Check for available bandwidth */
+	if (ep->bandwidth > bus->free_bw)
+		return ENOSPC;
+
+	list_append(&ep->link, get_list(bus, ep->device->address));
+	bus->free_bw -= ep->bandwidth;
+
+	return EOK;
+}
+
+
+/** Release bandwidth reserved by the given endpoint.
+ */
+static int usb2_bus_unregister_ep(bus_t *bus_base, endpoint_t *ep)
+{
+	usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
+	assert(ep);
+
+	list_remove(&ep->link);
+	ep->device = NULL;
+
+	bus->free_bw += ep->bandwidth;
+
+	return EOK;
+}
+
+static int usb2_bus_reset_toggle(bus_t *bus_base, usb_target_t target, toggle_reset_mode_t mode)
+{
+	usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
+
+	if (!usb_target_is_valid(target))
+		return EINVAL;
+
+	if (mode == RESET_NONE)
+		return EOK;
+
+	int ret = ENOENT;
+
+	list_foreach(*get_list(bus, target.address), link, endpoint_t, ep) {
+		assert(ep->device->address == target.address);
+
+		if (mode == RESET_ALL || ep->endpoint == target.endpoint) {
+			endpoint_toggle_set(ep, 0);
+			ret = EOK;
+		}
+	}
+	return ret;
+}
+
+/** Unregister and destroy all endpoints using given address.
+ * @param bus usb_bus structure, non-null.
+ * @param address USB address.
+ * @param endpoint USB endpoint number.
+ * @param direction Communication direction.
+ * @return Error code.
+ */
+static int usb2_bus_release_address(bus_t *bus_base, usb_address_t address)
+{
+	usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
+
+	if (!usb_address_is_valid(address))
+		return EINVAL;
+
+	const int ret = bus->devices[address].occupied ? EOK : ENOENT;
+	bus->devices[address].occupied = false;
+
+	list_t *list = get_list(bus, address);
+	for (link_t *link = list_first(list); link != NULL; ) {
+		endpoint_t *ep = list_get_instance(link, endpoint_t, link);
+		link = list_next(link, list);
+
+		assert(ep->device->address == address);
+		list_remove(&ep->link);
+
+		usb_log_warning("Endpoint %d:%d %s was left behind, removing.\n",
+		    address, ep->endpoint, usb_str_direction(ep->direction));
+
+		/* Drop bus reference */
+		endpoint_del_ref(ep);
+	}
+
+	return ret;
+}
+
+/** Request USB address.
+ * @param bus usb_device_manager
+ * @param addr Pointer to requested address value, place to store new address
+ * @parma strict Fail if the requested address is not available.
+ * @return Error code.
+ * @note Default address is only available in strict mode.
+ */
+static int usb2_bus_request_address(bus_t *bus_base, usb_address_t *addr, bool strict, usb_speed_t speed)
+{
+	int err;
+
+	usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
+	assert(addr);
+
+	if (!usb_address_is_valid(*addr))
+		return EINVAL;
+
+	/* Only grant default address to strict requests */
+	if ((*addr == USB_ADDRESS_DEFAULT) && !strict) {
+		if ((err = usb_bus_get_free_address(bus, addr)))
+			return err;
+	}
+	else if (bus->devices[*addr].occupied) {
+		if (strict) {
+			return ENOENT;
+		}
+		if ((err = usb_bus_get_free_address(bus, addr)))
+			return err;
+	}
+
+	assert(usb_address_is_valid(*addr));
+	assert(bus->devices[*addr].occupied == false);
+	assert(*addr != USB_ADDRESS_DEFAULT || strict);
+
+	bus->devices[*addr].occupied = true;
+	bus->devices[*addr].speed = speed;
+
+	return EOK;
+}
+
+static const bus_ops_t usb2_bus_ops = {
+	.enumerate_device = usb2_bus_enumerate_device,
+	.create_endpoint = usb2_bus_create_ep,
+	.find_endpoint = usb2_bus_find_ep,
+	.unregister_endpoint = usb2_bus_unregister_ep,
+	.register_endpoint = usb2_bus_register_ep,
+	.request_address = usb2_bus_request_address,
+	.release_address = usb2_bus_release_address,
+	.reset_toggle = usb2_bus_reset_toggle,
+};
+
+/** Initialize to default state.
+ *
+ * @param bus usb_bus structure, non-null.
+ * @param available_bandwidth Size of the bandwidth pool.
+ * @param bw_count function to use to calculate endpoint bw requirements.
+ * @return Error code.
+ */
+int usb2_bus_init(usb2_bus_t *bus, size_t available_bandwidth, count_bw_func_t count_bw)
+{
+	assert(bus);
+
+	bus_init(&bus->base, sizeof(device_t));
+
+	bus->base.ops = usb2_bus_ops;
+	bus->base.ops.count_bw = count_bw;
+
+	bus->free_bw = available_bandwidth;
+	bus->last_address = 0;
+	for (unsigned i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
+		list_initialize(&bus->devices[i].endpoint_list);
+		bus->devices[i].speed = USB_SPEED_MAX;
+		bus->devices[i].occupied = false;
+	}
+	return EOK;
+}
+/**
+ * @}
+ */
Index: pace/lib/usbhost/src/usb_bus.c
===================================================================
--- uspace/lib/usbhost/src/usb_bus.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ 	(revision )
@@ -1,548 +1,0 @@
-/*
- * Copyright (c) 2011 Jan Vesely
- * All rights eps.
- *
- * 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/usb_bus.h>
-#include <usb/debug.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <macros.h>
-#include <stdbool.h>
-
-
-/** Endpoint compare helper function.
- *
- * USB_DIRECTION_BOTH matches both IN and OUT.
- * @param ep Endpoint to compare, non-null.
- * @param address Tested address.
- * @param endpoint Tested endpoint number.
- * @param direction Tested direction.
- * @return True if ep can be used to communicate with given device,
- * false otherwise.
- */
-static inline bool ep_match(const endpoint_t *ep,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
-{
-	assert(ep);
-	return
-	    ((direction == ep->direction)
-	        || (ep->direction == USB_DIRECTION_BOTH)
-	        || (direction == USB_DIRECTION_BOTH))
-	    && (endpoint == ep->endpoint)
-	    && (address == ep->address);
-}
-
-/** Get list that holds endpoints for given address.
- * @param instance usb_bus structure, non-null.
- * @param addr USB address, must be >= 0.
- * @return Pointer to the appropriate list.
- */
-static list_t * get_list(usb_bus_t *instance, usb_address_t addr)
-{
-	assert(instance);
-	assert(addr >= 0);
-	return &instance->devices[addr % ARRAY_SIZE(instance->devices)].endpoint_list;
-}
-
-/** Internal search function, works on locked structure.
- * @param instance usb_bus structure, non-null.
- * @param address USB address, must be valid.
- * @param endpoint USB endpoint number.
- * @param direction Communication direction.
- * @return Pointer to endpoint_t structure representing given communication
- * target, NULL if there is no such endpoint registered.
- * @note Assumes that the internal mutex is locked.
- */
-static endpoint_t * find_locked(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
-{
-	assert(instance);
-	assert(fibril_mutex_is_locked(&instance->guard));
-	if (address < 0)
-		return NULL;
-	list_foreach(*get_list(instance, address), link, endpoint_t, ep) {
-		if (ep_match(ep, address, endpoint, direction))
-			return ep;
-	}
-	return NULL;
-}
-
-/** Get a free USB address
- *
- * @param[in] instance Device manager structure to use.
- * @return Free address, or error code.
- */
-static usb_address_t usb_bus_get_free_address(usb_bus_t *instance)
-{
-
-	usb_address_t new_address = instance->last_address;
-	do {
-		new_address = (new_address + 1) % USB_ADDRESS_COUNT;
-		if (new_address == USB_ADDRESS_DEFAULT)
-			new_address = 1;
-		if (new_address == instance->last_address)
-			return ENOSPC;
-	} while (instance->devices[new_address].occupied);
-
-	assert(new_address != USB_ADDRESS_DEFAULT);
-	instance->last_address = new_address;
-
-	return new_address;
-}
-
-/** Calculate bandwidth that needs to be reserved for communication with EP.
- * Calculation follows USB 1.1 specification.
- * @param speed Device's speed.
- * @param type Type of the transfer.
- * @param size Number of byte to transfer.
- * @param max_packet_size Maximum bytes in one packet.
- */
-size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type,
-    size_t size, size_t max_packet_size)
-{
-	/* We care about bandwidth only for interrupt and isochronous. */
-	if ((type != USB_TRANSFER_INTERRUPT)
-	    && (type != USB_TRANSFER_ISOCHRONOUS)) {
-		return 0;
-	}
-
-	const unsigned packet_count =
-	    (size + max_packet_size - 1) / max_packet_size;
-	/* TODO: It may be that ISO and INT transfers use only one packet per
-	 * transaction, but I did not find text in USB spec to confirm this */
-	/* NOTE: All data packets will be considered to be max_packet_size */
-	switch (speed)
-	{
-	case USB_SPEED_LOW:
-		assert(type == USB_TRANSFER_INTERRUPT);
-		/* Protocol overhead 13B
-		 * (3 SYNC bytes, 3 PID bytes, 2 Endpoint + CRC bytes, 2
-		 * CRC bytes, and a 3-byte interpacket delay)
-		 * see USB spec page 45-46. */
-		/* Speed penalty 8: low speed is 8-times slower*/
-		return packet_count * (13 + max_packet_size) * 8;
-	case USB_SPEED_FULL:
-		/* Interrupt transfer overhead see above
-		 * or page 45 of USB spec */
-		if (type == USB_TRANSFER_INTERRUPT)
-			return packet_count * (13 + max_packet_size);
-
-		assert(type == USB_TRANSFER_ISOCHRONOUS);
-		/* Protocol overhead 9B
-		 * (2 SYNC bytes, 2 PID bytes, 2 Endpoint + CRC bytes, 2 CRC
-		 * bytes, and a 1-byte interpacket delay)
-		 * see USB spec page 42 */
-		return packet_count * (9 + max_packet_size);
-	default:
-		return 0;
-	}
-}
-
-/** Calculate bandwidth that needs to be reserved for communication with EP.
- * Calculation follows USB 2.0 specification.
- * @param speed Device's speed.
- * @param type Type of the transfer.
- * @param size Number of byte to transfer.
- * @param max_packet_size Maximum bytes in one packet.
- */
-size_t bandwidth_count_usb20(usb_speed_t speed, usb_transfer_type_t type,
-    size_t size, size_t max_packet_size)
-{
-	/* We care about bandwidth only for interrupt and isochronous. */
-	if ((type != USB_TRANSFER_INTERRUPT)
-	    && (type != USB_TRANSFER_ISOCHRONOUS)) {
-		return 0;
-	}
-	//TODO Implement
-	return 0;
-}
-
-/** Initialize to default state.
- * You need to provide valid bw_count function if you plan to use
- * add_endpoint/remove_endpoint pair.
- *
- * @param instance usb_bus structure, non-null.
- * @param available_bandwidth Size of the bandwidth pool.
- * @param bw_count function to use to calculate endpoint bw requirements.
- * @return Error code.
- */
-int usb_bus_init(usb_bus_t *instance,
-    size_t available_bandwidth, bw_count_func_t bw_count, usb_speed_t max_speed)
-{
-	assert(instance);
-	fibril_mutex_initialize(&instance->guard);
-	instance->free_bw = available_bandwidth;
-	instance->bw_count = bw_count;
-	instance->last_address = 0;
-	instance->max_speed = max_speed;
-	for (unsigned i = 0; i < ARRAY_SIZE(instance->devices); ++i) {
-		list_initialize(&instance->devices[i].endpoint_list);
-		instance->devices[i].speed = USB_SPEED_MAX;
-		instance->devices[i].occupied = false;
-	}
-	return EOK;
-}
-
-/** Register endpoint structure.
- * Checks for duplicates.
- * @param instance usb_bus, non-null.
- * @param ep endpoint_t to register.
- * @param data_size Size of data to transfer.
- * @return Error code.
- */
-int usb_bus_register_ep(usb_bus_t *instance, endpoint_t *ep, size_t data_size)
-{
-	assert(instance);
-	if (ep == NULL || ep->address < 0)
-		return EINVAL;
-
-	fibril_mutex_lock(&instance->guard);
-	/* Check for available bandwidth */
-	if (ep->bandwidth > instance->free_bw) {
-		fibril_mutex_unlock(&instance->guard);
-		return ENOSPC;
-	}
-
-	/* Check for existence */
-	const endpoint_t *endpoint =
-	    find_locked(instance, ep->address, ep->endpoint, ep->direction);
-	if (endpoint != NULL) {
-		fibril_mutex_unlock(&instance->guard);
-		return EEXIST;
-	}
-	/* Add endpoint list's reference to ep. */
-	endpoint_add_ref(ep);
-	list_append(&ep->link, get_list(instance, ep->address));
-
-	instance->free_bw -= ep->bandwidth;
-	usb_log_debug("Registered EP(%d:%d:%s:%s)\n", ep->address, ep->endpoint,
-	    usb_str_transfer_type_short(ep->transfer_type),
-	    usb_str_direction(ep->direction));
-	fibril_mutex_unlock(&instance->guard);
-	return EOK;
-}
-
-/** Unregister endpoint structure.
- * Checks for duplicates.
- * @param instance usb_bus, non-null.
- * @param ep endpoint_t to unregister.
- * @return Error code.
- */
-int usb_bus_unregister_ep(usb_bus_t *instance, endpoint_t *ep)
-{
-	assert(instance);
-	if (ep == NULL || ep->address < 0)
-		return EINVAL;
-
-	fibril_mutex_lock(&instance->guard);
-	if (!list_member(&ep->link, get_list(instance, ep->address))) {
-		fibril_mutex_unlock(&instance->guard);
-		return ENOENT;
-	}
-	list_remove(&ep->link);
-	instance->free_bw += ep->bandwidth;
-	usb_log_debug("Unregistered EP(%d:%d:%s:%s)\n", ep->address,
-	    ep->endpoint, usb_str_transfer_type_short(ep->transfer_type),
-	    usb_str_direction(ep->direction));
-	/* Drop endpoint list's reference to ep. */
-	endpoint_del_ref(ep);
-	fibril_mutex_unlock(&instance->guard);
-	return EOK;
-}
-
-/** Find endpoint_t representing the given communication route.
- * @param instance usb_bus, non-null.
- * @param address
- */
-endpoint_t * usb_bus_find_ep(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
-{
-	assert(instance);
-
-	fibril_mutex_lock(&instance->guard);
-	endpoint_t *ep = find_locked(instance, address, endpoint, direction);
-	if (ep) {
-		/* We are exporting ep to the outside world, add reference. */
-		endpoint_add_ref(ep);
-	}
-	fibril_mutex_unlock(&instance->guard);
-	return ep;
-}
-
-/** Create and register new endpoint_t structure.
- * @param instance usb_bus structure, non-null.
- * @param address USB address.
- * @param endpoint USB endpoint number.
- * @param direction Communication direction.
- * @param type USB transfer type.
- * @param speed USB Communication speed.
- * @param max_packet_size Maximum size of data packets.
- * @param data_size Expected communication size.
- * @param callback function to call just after registering.
- * @param arg Argument to pass to the callback function.
- * @return Error code.
- */
-int usb_bus_add_ep(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
-    usb_transfer_type_t type, size_t max_packet_size, unsigned packets,
-    size_t data_size, ep_add_callback_t callback, void *arg,
-    usb_address_t tt_address, unsigned tt_port)
-{
-	assert(instance);
-	if (instance->bw_count == NULL)
-		return ENOTSUP;
-	if (!usb_address_is_valid(address))
-		return EINVAL;
-
-
-	fibril_mutex_lock(&instance->guard);
-	/* Check for speed and address */
-	if (!instance->devices[address].occupied) {
-		fibril_mutex_unlock(&instance->guard);
-		return ENOENT;
-	}
-
-	/* Check for existence */
-	endpoint_t *ep = find_locked(instance, address, endpoint, direction);
-	if (ep != NULL) {
-		fibril_mutex_unlock(&instance->guard);
-		return EEXIST;
-	}
-
-	const usb_speed_t speed = instance->devices[address].speed;
-	const size_t bw =
-	    instance->bw_count(speed, type, data_size, max_packet_size);
-
-	/* Check for available bandwidth */
-	if (bw > instance->free_bw) {
-		fibril_mutex_unlock(&instance->guard);
-		return ENOSPC;
-	}
-
-	ep = endpoint_create(address, endpoint, direction, type, speed,
-	    max_packet_size, packets, bw, tt_address, tt_port);
-	if (!ep) {
-		fibril_mutex_unlock(&instance->guard);
-		return ENOMEM;
-	}
-
-	/* Add our reference to ep. */
-	endpoint_add_ref(ep);
-
-	if (callback) {
-		const int ret = callback(ep, arg);
-		if (ret != EOK) {
-			fibril_mutex_unlock(&instance->guard);
-			endpoint_del_ref(ep);
-			return ret;
-		}
-	}
-	
-	/* Add endpoint list's reference to ep. */
-	endpoint_add_ref(ep);
-	list_append(&ep->link, get_list(instance, ep->address));
-
-	instance->free_bw -= ep->bandwidth;
-	fibril_mutex_unlock(&instance->guard);
-
-	/* Drop our reference to ep. */
-	endpoint_del_ref(ep);
-
-	return EOK;
-}
-
-/** Unregister and destroy endpoint_t structure representing given route.
- * @param instance usb_bus structure, non-null.
- * @param address USB address.
- * @param endpoint USB endpoint number.
- * @param direction Communication direction.
- * @param callback Function to call after unregister, before destruction.
- * @arg Argument to pass to the callback function.
- * @return Error code.
- */
-int usb_bus_remove_ep(usb_bus_t *instance,
-    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
-    ep_remove_callback_t callback, void *arg)
-{
-	assert(instance);
-	fibril_mutex_lock(&instance->guard);
-	endpoint_t *ep = find_locked(instance, address, endpoint, direction);
-	if (ep != NULL) {
-		list_remove(&ep->link);
-		instance->free_bw += ep->bandwidth;
-	}
-	fibril_mutex_unlock(&instance->guard);
-	if (ep == NULL)
-		return ENOENT;
-
-	if (callback) {
-		callback(ep, arg);
-	}
-	/* Drop endpoint list's reference to ep. */
-	endpoint_del_ref(ep);
-	return EOK;
-}
-
-int usb_bus_reset_toggle(usb_bus_t *instance, usb_target_t target, bool all)
-{
-	assert(instance);
-	if (!usb_target_is_valid(target))
-		return EINVAL;
-
-	int ret = ENOENT;
-
-	fibril_mutex_lock(&instance->guard);
-	list_foreach(*get_list(instance, target.address), link, endpoint_t, ep) {
-		if ((ep->address == target.address)
-		    && (all || ep->endpoint == target.endpoint)) {
-			endpoint_toggle_set(ep, 0);
-			ret = EOK;
-		}
-	}
-	fibril_mutex_unlock(&instance->guard);
-	return ret;
-}
-
-/** Unregister and destroy all endpoints using given address.
- * @param instance usb_bus structure, non-null.
- * @param address USB address.
- * @param endpoint USB endpoint number.
- * @param direction Communication direction.
- * @param callback Function to call after unregister, before destruction.
- * @arg Argument to pass to the callback function.
- * @return Error code.
- */
-int usb_bus_remove_address(usb_bus_t *instance,
-    usb_address_t address, ep_remove_callback_t callback, void *arg)
-{
-	assert(instance);
-	if (!usb_address_is_valid(address))
-		return EINVAL;
-
-	fibril_mutex_lock(&instance->guard);
-
-	const int ret = instance->devices[address].occupied ? EOK : ENOENT;
-	instance->devices[address].occupied = false;
-
-	list_t *list = get_list(instance, address);
-	for (link_t *link = list_first(list); link != NULL; ) {
-		endpoint_t *ep = list_get_instance(link, endpoint_t, link);
-		link = list_next(link, list);
-		if (ep->address == address) {
-			list_remove(&ep->link);
-			if (callback)
-				callback(ep, arg);
-			/* Drop endpoint list's reference to ep. */
-			endpoint_del_ref(ep);
-		}
-	}
-	fibril_mutex_unlock(&instance->guard);
-	return ret;
-}
-
-/** Request USB address.
- * @param instance usb_device_manager
- * @param address Pointer to requested address value, place to store new address
- * @parma strict Fail if the requested address is not available.
- * @return Error code.
- * @note Default address is only available in strict mode.
- */
-int usb_bus_request_address(usb_bus_t *instance,
-    usb_address_t *address, bool strict, usb_speed_t speed)
-{
-	assert(instance);
-	assert(address);
-	if (speed > instance->max_speed)
-		return ENOTSUP;
-
-	if (!usb_address_is_valid(*address))
-		return EINVAL;
-
-	usb_address_t addr = *address;
-
-	fibril_mutex_lock(&instance->guard);
-	/* Only grant default address to strict requests */
-	if ((addr == USB_ADDRESS_DEFAULT) && !strict) {
-		addr = usb_bus_get_free_address(instance);
-	}
-
-	if (instance->devices[addr].occupied) {
-		if (strict) {
-			fibril_mutex_unlock(&instance->guard);
-			return ENOENT;
-		}
-		addr = usb_bus_get_free_address(instance);
-	}
-	if (usb_address_is_valid(addr)) {
-		assert(instance->devices[addr].occupied == false);
-		assert(addr != USB_ADDRESS_DEFAULT || strict);
-
-		instance->devices[addr].occupied = true;
-		instance->devices[addr].speed = speed;
-		*address = addr;
-		addr = 0;
-	}
-
-	fibril_mutex_unlock(&instance->guard);
-	return addr;
-}
-
-/** Get speed assigned to USB address.
- *
- * @param[in] instance Device manager structure to use.
- * @param[in] address Address the caller wants to find.
- * @param[out] speed Assigned speed.
- * @return Error code.
- */
-int usb_bus_get_speed(usb_bus_t *instance, usb_address_t address,
-    usb_speed_t *speed)
-{
-	assert(instance);
-	if (!usb_address_is_valid(address)) {
-		return EINVAL;
-	}
-
-	fibril_mutex_lock(&instance->guard);
-
-	const int ret = instance->devices[address].occupied ? EOK : ENOENT;
-	if (speed && instance->devices[address].occupied) {
-		*speed = instance->devices[address].speed;
-	}
-
-	fibril_mutex_unlock(&instance->guard);
-	return ret;
-}
-/**
- * @}
- */
Index: uspace/lib/usbhost/src/usb_transfer_batch.c
===================================================================
--- uspace/lib/usbhost/src/usb_transfer_batch.c	(revision d2d142afa611111d7d295ad56a4f3bba24d607f2)
+++ uspace/lib/usbhost/src/usb_transfer_batch.c	(revision f9d0a86c86defda4d9f30fbb4f7217a75048bf8b)
@@ -34,104 +34,116 @@
 
 #include <usb/host/usb_transfer_batch.h>
+#include <usb/host/endpoint.h>
+#include <usb/host/bus.h>
 #include <usb/debug.h>
 
 #include <assert.h>
+#include <stdlib.h>
 #include <errno.h>
-#include <macros.h>
-#include <mem.h>
-#include <stdlib.h>
-#include <usbhc_iface.h>
+#include <str_error.h>
 
-/** Allocate and initialize usb_transfer_batch structure.
- * @param ep endpoint used by the transfer batch.
- * @param buffer data to send/recieve.
- * @param buffer_size Size of data buffer.
- * @param setup_buffer Data to send in SETUP stage of control transfer.
- * @param func_in callback on IN transfer completion.
- * @param func_out callback on OUT transfer completion.
- * @param fun DDF function (passed to callback function).
- * @param arg Argument to pass to the callback function.
- * @param private_data driver specific per batch data.
- * @param private_data_dtor Function to properly destroy private_data.
- * @return Pointer to valid usb_transfer_batch_t structure, NULL on failure.
+
+/** Create a batch on given endpoint.
  */
-usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *ep, char *buffer,
-    size_t buffer_size,
-    uint64_t setup_buffer,
-    usbhc_iface_transfer_in_callback_t func_in,
-    usbhc_iface_transfer_out_callback_t func_out,
-    void *arg)
+usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *ep)
 {
-	if (func_in == NULL && func_out == NULL)
-		return NULL;
-	if (func_in != NULL && func_out != NULL)
-		return NULL;
+	assert(ep);
+	assert(ep->bus);
 
-	usb_transfer_batch_t *instance = malloc(sizeof(usb_transfer_batch_t));
-	if (instance) {
-		instance->ep = ep;
-		instance->callback_in = func_in;
-		instance->callback_out = func_out;
-		instance->arg = arg;
-		instance->buffer = buffer;
-		instance->buffer_size = buffer_size;
-		instance->setup_size = 0;
-		instance->transfered_size = 0;
-		instance->error = EOK;
-		if (ep && ep->transfer_type == USB_TRANSFER_CONTROL) {
-			memcpy(instance->setup_buffer, &setup_buffer,
-			    USB_SETUP_PACKET_SIZE);
-			instance->setup_size = USB_SETUP_PACKET_SIZE;
-		}
-		if (instance->ep)
-			endpoint_use(instance->ep);
-	}
-	return instance;
+	usb_transfer_batch_t *batch;
+	if (ep->bus->ops.create_batch)
+		batch = ep->bus->ops.create_batch(ep->bus, ep);
+	else
+		batch = malloc(sizeof(usb_transfer_batch_t));
+
+	return batch;
 }
 
-/** Correctly dispose all used data structures.
- *
- * @param[in] instance Batch structure to use.
+/** Initialize given batch structure.
  */
-void usb_transfer_batch_destroy(const usb_transfer_batch_t *instance)
+void usb_transfer_batch_init(usb_transfer_batch_t *batch, endpoint_t *ep)
 {
-	if (!instance)
-		return;
-	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " disposing.\n",
-	    instance, USB_TRANSFER_BATCH_ARGS(*instance));
-	if (instance->ep) {
-		endpoint_release(instance->ep);
-	}
-	free(instance);
+	memset(batch, 0, sizeof(*batch));
+	batch->ep = ep;
 }
 
-/** Prepare data and call the right callback.
+/** Resolve resetting toggle.
  *
- * @param[in] instance Batch structure to use.
- * @param[in] data Data to copy to the output buffer.
- * @param[in] size Size of @p data.
- * @param[in] error Error value to use.
+ * @param[in] batch Batch structure to use.
  */
-void usb_transfer_batch_finish_error(const usb_transfer_batch_t *instance,
-    const void *data, size_t size, int error)
+int usb_transfer_batch_reset_toggle(usb_transfer_batch_t *batch)
 {
-	assert(instance);
+	assert(batch);
+
+	if (batch->error != EOK || batch->toggle_reset_mode == RESET_NONE)
+		return EOK;
+
+	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " resets %s",
+	    batch, USB_TRANSFER_BATCH_ARGS(*batch),
+	    batch->toggle_reset_mode == RESET_ALL ? "all EPs toggle" : "EP toggle");
+
+	return bus_reset_toggle(batch->ep->bus, batch->target, batch->toggle_reset_mode);
+}
+
+/** Destroy the batch.
+ *
+ * @param[in] batch Batch structure to use.
+ */
+void usb_transfer_batch_destroy(usb_transfer_batch_t *batch)
+{
+	assert(batch);
+	assert(batch->ep);
+	assert(batch->ep->bus);
+
+	bus_t *bus = batch->ep->bus;
+	if (bus->ops.destroy_batch) {
+		usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " destroying.\n",
+		    batch, USB_TRANSFER_BATCH_ARGS(*batch));
+		bus->ops.destroy_batch(batch);
+	}
+	else {
+		usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " disposing.\n",
+		    batch, USB_TRANSFER_BATCH_ARGS(*batch));
+		free(batch);
+	}
+}
+
+/** Finish a transfer batch: call handler, destroy batch, release endpoint.
+ *
+ * Call only after the batch have been scheduled && completed!
+ *
+ * @param[in] batch Batch structure to use.
+ */
+void usb_transfer_batch_finish(usb_transfer_batch_t *batch)
+{
+	assert(batch);
+	assert(batch->ep);
+
 	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " finishing.\n",
-	    instance, USB_TRANSFER_BATCH_ARGS(*instance));
+	    batch, USB_TRANSFER_BATCH_ARGS(*batch));
 
-	/* NOTE: Only one of these pointers should be set. */
-        if (instance->callback_out) {
-		instance->callback_out(error, instance->arg);
+	if (batch->on_complete) {
+		const int err = batch->on_complete(batch->on_complete_data, batch->error, batch->transfered_size);
+		if (err)
+			usb_log_warning("batch %p failed to complete: %s",
+			    batch, str_error(err));
 	}
 
-        if (instance->callback_in) {
-		/* We care about the data and there are some to copy */
-		const size_t safe_size = min(size, instance->buffer_size);
-		if (data) {
-	                memcpy(instance->buffer, data, safe_size);
-		}
-		instance->callback_in(error, safe_size, instance->arg);
-	}
+	usb_transfer_batch_destroy(batch);
 }
+
+/** Finish a transfer batch as an aborted one.
+ *
+ * @param[in] batch Batch structure to use.
+ */
+void usb_transfer_batch_abort(usb_transfer_batch_t *batch)
+{
+	assert(batch);
+	assert(batch->ep);
+
+	batch->error = EAGAIN;
+	usb_transfer_batch_finish(batch);
+}
+
 /**
  * @}
