Index: uspace/drv/bus/usb/ehci/ehci_bus.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_bus.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/ehci/ehci_bus.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -162,4 +162,6 @@
 	.parent = &usb2_bus_ops,
 
+	.interrupt = ehci_hc_interrupt,
+	.status = ehci_hc_status,
 	.endpoint_destroy = ehci_endpoint_destroy,
 	.endpoint_create = ehci_endpoint_create,
@@ -171,7 +173,8 @@
 	.batch_create = ehci_create_batch,
 	.batch_destroy = ehci_destroy_batch,
+	.batch_schedule = ehci_hc_schedule,
 };
 
-int ehci_bus_init(ehci_bus_t *bus, hcd_t *hcd, hc_t *hc)
+int ehci_bus_init(ehci_bus_t *bus, hc_t *hc)
 {
 	assert(hc);
@@ -181,5 +184,5 @@
 	bus_t *bus_base = (bus_t *) bus;
 
-	usb2_bus_init(usb2_bus, hcd, BANDWIDTH_AVAILABLE_USB11);
+	usb2_bus_init(usb2_bus, BANDWIDTH_AVAILABLE_USB11);
 	bus_base->ops = &ehci_bus_ops;
 
Index: uspace/drv/bus/usb/ehci/ehci_bus.h
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_bus.h	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/ehci/ehci_bus.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -63,9 +63,9 @@
 void ehci_bus_prepare_ops(void);
 
-int ehci_bus_init(ehci_bus_t *, hcd_t *, hc_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
+ * @return Pointer to assigned ehci endpoint structure
  */
 static inline ehci_endpoint_t * ehci_endpoint_get(const endpoint_t *ep)
Index: uspace/drv/bus/usb/ehci/hc.c
===================================================================
--- uspace/drv/bus/usb/ehci/hc.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/ehci/hc.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -97,10 +97,9 @@
  * @return Error code.
  */
-int ehci_hc_gen_irq_code(irq_code_t *code, hcd_t *hcd, const hw_res_list_parsed_t *hw_res)
+int hc_gen_irq_code(irq_code_t *code, hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
 {
 	assert(code);
 	assert(hw_res);
-
-	hc_t *instance = hcd_get_driver_data(hcd);
+	hc_t *instance = hcd_to_hc(hcd);
 
 	if (hw_res->irqs.count != 1 || hw_res->mem_ranges.count != 1)
@@ -149,7 +148,7 @@
  * @return Error code
  */
-int hc_init(hc_t *instance, hcd_t *hcd, const hw_res_list_parsed_t *hw_res)
-{
-	assert(instance);
+int hc_add(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
+{
+	hc_t *instance = hcd_to_hc(hcd);
 	assert(hw_res);
 	if (hw_res->mem_ranges.count != 1 ||
@@ -190,5 +189,6 @@
 	    &instance->rh, instance->caps, instance->registers, "ehci rh");
 
-	ehci_bus_init(&instance->bus, hcd, instance);
+	ehci_bus_init(&instance->bus, instance);
+	hc_device_setup(hcd, (bus_t *) &instance->bus);
 	return EOK;
 }
@@ -198,7 +198,8 @@
  * @param[in] instance Host controller structure to use.
  */
-void hc_fini(hc_t *instance)
+int hc_gone(hc_device_t *instance)
 {
 	assert(instance);
+	return EOK;
 	//TODO: stop the hw
 #if 0
@@ -263,16 +264,19 @@
 }
 
-int ehci_hc_status(hcd_t *hcd, uint32_t *status)
-{
-	assert(hcd);
-	hc_t *instance = hcd_get_driver_data(hcd);
-	assert(instance);
+int ehci_hc_status(bus_t *bus_base, uint32_t *status)
+{
+	assert(bus_base);
 	assert(status);
+
+	ehci_bus_t *bus = (ehci_bus_t *) bus_base;
+	hc_t *hc = bus->hc;
+	assert(hc);
+
 	*status = 0;
-	if (instance->registers) {
-		*status = EHCI_RD(instance->registers->usbsts);
-		EHCI_WR(instance->registers->usbsts, *status);
-	}
-	usb_log_debug2("HC(%p): Read status: %x", instance, *status);
+	if (hc->registers) {
+		*status = EHCI_RD(hc->registers->usbsts);
+		EHCI_WR(hc->registers->usbsts, *status);
+	}
+	usb_log_debug2("HC(%p): Read status: %x", hc, *status);
 	return EOK;
 }
@@ -284,15 +288,17 @@
  * @return Error code.
  */
-int ehci_hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
-{
-	assert(hcd);
-	hc_t *instance = hcd_get_driver_data(hcd);
-	assert(instance);
+int ehci_hc_schedule(usb_transfer_batch_t *batch)
+{
+	assert(batch);
+
+	ehci_bus_t *bus = (ehci_bus_t *) endpoint_get_bus(batch->ep);
+	hc_t *hc = bus->hc;
+	assert(hc);
 
 	/* Check for root hub communication */
-	if (batch->target.address == ehci_rh_get_address(&instance->rh)) {
+	if (batch->target.address == ehci_rh_get_address(&hc->rh)) {
 		usb_log_debug("HC(%p): Scheduling BATCH(%p) for RH(%p)",
-		    instance, batch, &instance->rh);
-		return ehci_rh_schedule(&instance->rh, batch);
+		    hc, batch, &hc->rh);
+		return ehci_rh_schedule(&hc->rh, batch);
 	}
 
@@ -303,11 +309,11 @@
 		return err;
 
-	fibril_mutex_lock(&instance->guard);
-	usb_log_debug2("HC(%p): Appending BATCH(%p)", instance, batch);
-	list_append(&ehci_batch->link, &instance->pending_batches);
-	usb_log_debug("HC(%p): Committing BATCH(%p)", instance, batch);
+	fibril_mutex_lock(&hc->guard);
+	usb_log_debug2("HC(%p): Appending BATCH(%p)", hc, batch);
+	list_append(&ehci_batch->link, &hc->pending_batches);
+	usb_log_debug("HC(%p): Committing BATCH(%p)", hc, batch);
 	ehci_transfer_batch_commit(ehci_batch);
 
-	fibril_mutex_unlock(&instance->guard);
+	fibril_mutex_unlock(&hc->guard);
 	return EOK;
 }
@@ -318,29 +324,30 @@
  * @param[in] status Value of the status register at the time of interrupt.
  */
-void ehci_hc_interrupt(hcd_t *hcd, uint32_t status)
-{
-	assert(hcd);
-	hc_t *instance = hcd_get_driver_data(hcd);
-	status = EHCI_RD(status);
-	assert(instance);
-
-	usb_log_debug2("HC(%p): Interrupt: %"PRIx32, instance, status);
+void ehci_hc_interrupt(bus_t *bus_base, uint32_t status)
+{
+	assert(bus_base);
+
+	ehci_bus_t *bus = (ehci_bus_t *) bus_base;
+	hc_t *hc = bus->hc;
+	assert(hc);
+
+	usb_log_debug2("HC(%p): Interrupt: %"PRIx32, hc, status);
 	if (status & USB_STS_PORT_CHANGE_FLAG) {
-		ehci_rh_interrupt(&instance->rh);
+		ehci_rh_interrupt(&hc->rh);
 	}
 
 	if (status & USB_STS_IRQ_ASYNC_ADVANCE_FLAG) {
-		fibril_mutex_lock(&instance->guard);
-		usb_log_debug2("HC(%p): Signaling doorbell", instance);
-		fibril_condvar_broadcast(&instance->async_doorbell);
-		fibril_mutex_unlock(&instance->guard);
+		fibril_mutex_lock(&hc->guard);
+		usb_log_debug2("HC(%p): Signaling doorbell", hc);
+		fibril_condvar_broadcast(&hc->async_doorbell);
+		fibril_mutex_unlock(&hc->guard);
 	}
 
 	if (status & (USB_STS_IRQ_FLAG | USB_STS_ERR_IRQ_FLAG)) {
-		fibril_mutex_lock(&instance->guard);
-
-		usb_log_debug2("HC(%p): Scanning %lu pending batches", instance,
-			list_count(&instance->pending_batches));
-		list_foreach_safe(instance->pending_batches, current, next) {
+		fibril_mutex_lock(&hc->guard);
+
+		usb_log_debug2("HC(%p): Scanning %lu pending batches", hc,
+			list_count(&hc->pending_batches));
+		list_foreach_safe(hc->pending_batches, current, next) {
 			ehci_transfer_batch_t *batch =
 			    ehci_transfer_batch_from_link(current);
@@ -351,9 +358,9 @@
 			}
 		}
-		fibril_mutex_unlock(&instance->guard);
+		fibril_mutex_unlock(&hc->guard);
 	}
 
 	if (status & USB_STS_HOST_ERROR_FLAG) {
-		usb_log_fatal("HCD(%p): HOST SYSTEM ERROR!", instance);
+		usb_log_fatal("HCD(%p): HOST SYSTEM ERROR!", hc);
 		//TODO do something here
 	}
@@ -364,7 +371,7 @@
  * @param[in] instance EHCI hc driver structure.
  */
-int hc_start(hc_t *instance, bool interrupts)
-{
-	assert(instance);
+int hc_start(hc_device_t *hcd)
+{
+	hc_t *instance = hcd_to_hc(hcd);
 	usb_log_debug("HC(%p): Starting HW.", instance);
 
Index: uspace/drv/bus/usb/ehci/hc.h
===================================================================
--- uspace/drv/bus/usb/ehci/hc.h	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/ehci/hc.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -55,4 +55,7 @@
 /** Main EHCI driver structure */
 typedef struct hc {
+	/* Common device header */
+	hc_device_t base;
+
 	/** Memory mapped CAPS register area */
 	ehci_caps_regs_t *caps;
@@ -85,16 +88,24 @@
 } hc_t;
 
-int hc_init(hc_t *instance, hcd_t *hcd, const hw_res_list_parsed_t *hw_res);
-int hc_start(hc_t *instance, bool interrupts);
-void hc_fini(hc_t *instance);
+static inline hc_t *hcd_to_hc(hc_device_t *hcd)
+{
+	assert(hcd);
+	return (hc_t *) hcd;
+}
 
-void hc_enqueue_endpoint(hc_t *instance, const endpoint_t *ep);
-void hc_dequeue_endpoint(hc_t *instance, const endpoint_t *ep);
+void hc_enqueue_endpoint(hc_t *, const endpoint_t *);
+void hc_dequeue_endpoint(hc_t *, const endpoint_t *);
 
-int ehci_hc_gen_irq_code(irq_code_t *code, hcd_t *hcd, const hw_res_list_parsed_t *hw_res);
+/* Boottime operations */
+int hc_add(hc_device_t *, const hw_res_list_parsed_t *);
+int hc_start(hc_device_t *);
+int hc_gen_irq_code(irq_code_t *, hc_device_t *, const hw_res_list_parsed_t *);
+int hc_gone(hc_device_t *);
 
-void ehci_hc_interrupt(hcd_t *hcd, uint32_t status);
-int ehci_hc_status(hcd_t *hcd, uint32_t *status);
-int ehci_hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch);
+/** Runtime operations */
+void ehci_hc_interrupt(bus_t *, uint32_t);
+int ehci_hc_status(bus_t *, uint32_t *);
+int ehci_hc_schedule(usb_transfer_batch_t *);
+
 #endif
 /**
Index: uspace/drv/bus/usb/ehci/main.c
===================================================================
--- uspace/drv/bus/usb/ehci/main.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/ehci/main.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -35,13 +35,6 @@
  */
 
-#include <ddf/driver.h>
-#include <ddf/interrupt.h>
-#include <device/hw_res.h>
-#include <errno.h>
-#include <str_error.h>
 #include <io/logctl.h>
-
-#include <usb_iface.h>
-#include <usb/debug.h>
+#include <usb/host/hcd.h>
 #include <usb/host/ddf_helpers.h>
 
@@ -51,105 +44,15 @@
 #define NAME "ehci"
 
-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 hc_driver_t ehci_driver = {
+	.name = NAME,
+	.hc_device_size = sizeof(hc_t),
 
-static const ddf_hc_driver_t ehci_hc_driver = {
-	.name = "EHCI-PCI",
-	.init = ehci_driver_init,
-	.irq_code_gen = ehci_hc_gen_irq_code,
-	.claim = ehci_driver_claim,
-	.start = ehci_driver_start,
+	.hc_add = hc_add,
+	.irq_code_gen = hc_gen_irq_code,
+	.claim = disable_legacy,
+	.start = hc_start,
 	.setup_root_hub = hcd_setup_virtual_root_hub,
-	.fini = ehci_driver_fini,
-	.ops = {
-		.schedule       = ehci_hc_schedule,
-		.irq_hook       = ehci_hc_interrupt,
-		.status_hook    = ehci_hc_status,
-	}
+	.hc_gone = hc_gone,
 };
-
-
-static int ehci_driver_init(hcd_t *hcd, const hw_res_list_parsed_t *res, ddf_dev_t *device)
-{
-	assert(hcd);
-	assert(hcd_get_driver_data(hcd) == NULL);
-
-	hc_t *instance = malloc(sizeof(hc_t));
-	if (!instance)
-		return ENOMEM;
-
-	const int ret = hc_init(instance, hcd, res);
-	if (ret == EOK) {
-		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);
-}
-
-static void ehci_driver_fini(hcd_t *hcd)
-{
-	assert(hcd);
-	hc_t *hc = hcd_get_driver_data(hcd);
-	if (hc)
-		hc_fini(hc);
-
-	free(hc);
-	hcd_set_implementation(hcd, NULL, NULL, NULL);
-}
-
-/** Initializes a new ddf driver instance of EHCI hcd.
- *
- * @param[in] device DDF instance of the device to initialize.
- * @return Error code.
- */
-static int ehci_dev_add(ddf_dev_t *device)
-{
-	usb_log_debug("ehci_dev_add() called\n");
-	assert(device);
-
-	return hcd_ddf_add_hc(device, &ehci_hc_driver);
-
-}
-
-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
-};
-
-static const driver_t ehci_driver = {
-	.name = NAME,
-	.driver_ops = &ehci_driver_ops
-};
-
 
 /** Initializes global driver structures (NONE).
@@ -165,5 +68,5 @@
 	log_init(NAME);
 	logctl_set_log_level(NAME, LVL_NOTE);
-	return ddf_driver_main(&ehci_driver);
+	return hc_driver_main(&ehci_driver);
 }
 
Index: uspace/drv/bus/usb/ehci/res.c
===================================================================
--- uspace/drv/bus/usb/ehci/res.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/ehci/res.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -45,4 +45,5 @@
 #include <pci_dev_iface.h>
 
+#include "hc.h"
 #include "res.h"
 #include "ehci_regs.h"
@@ -172,9 +173,9 @@
 }
 
-int disable_legacy(hc_t *hc, ddf_dev_t *device)
+int disable_legacy(hc_device_t *hcd)
 {
-	assert(device);
-
-	async_sess_t *parent_sess = ddf_dev_parent_sess_get(device);
+	hc_t *hc = hcd_to_hc(hcd);
+
+	async_sess_t *parent_sess = ddf_dev_parent_sess_get(hcd->ddf_dev);
 	if (parent_sess == NULL)
 		return ENOMEM;
Index: uspace/drv/bus/usb/ehci/res.h
===================================================================
--- uspace/drv/bus/usb/ehci/res.h	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/ehci/res.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -36,10 +36,7 @@
 #define DRV_EHCI_PCI_H
 
-#include <ddf/driver.h>
-#include <device/hw_res_parsed.h>
+typedef struct hc_device hc_device_t;
 
-#include "hc.h"
-
-extern int disable_legacy(hc_t *, ddf_dev_t *);
+extern int disable_legacy(hc_device_t *);
 
 #endif
Index: uspace/drv/bus/usb/ohci/hc.c
===================================================================
--- uspace/drv/bus/usb/ohci/hc.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/ohci/hc.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -101,5 +101,5 @@
  * @return Error code.
  */
-int ohci_hc_gen_irq_code(irq_code_t *code, hcd_t *hcd, const hw_res_list_parsed_t *hw_res)
+int hc_gen_irq_code(irq_code_t *code, hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
 {
 	assert(code);
@@ -149,7 +149,7 @@
  * @return Error code
  */
-int hc_init(hc_t *instance, const hw_res_list_parsed_t *hw_res)
-{
-	assert(instance);
+int hc_add(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
+{
+	hc_t *instance = hcd_to_hc(hcd);
 	assert(hw_res);
 	if (hw_res->mem_ranges.count != 1 ||
@@ -186,9 +186,10 @@
  * @param[in] instance Host controller structure to use.
  */
-void hc_fini(hc_t *instance)
+int hc_gone(hc_device_t *instance)
 {
 	assert(instance);
 	/* TODO: implement*/
-};
+	return ENOTSUP;
+}
 
 void hc_enqueue_endpoint(hc_t *instance, const endpoint_t *ep)
@@ -260,14 +261,16 @@
 }
 
-int ohci_hc_status(hcd_t *hcd, uint32_t *status)
-{
-	assert(hcd);
+int ohci_hc_status(bus_t *bus_base, uint32_t *status)
+{
+	assert(bus_base);
 	assert(status);
-	hc_t *instance = hcd_get_driver_data(hcd);
-	assert(instance);
-
-	if (instance->registers){
-		*status = OHCI_RD(instance->registers->interrupt_status);
-		OHCI_WR(instance->registers->interrupt_status, *status);
+
+	ohci_bus_t *bus = (ohci_bus_t *) bus_base;
+	hc_t *hc = bus->hc;
+	assert(hc);
+
+	if (hc->registers){
+		*status = OHCI_RD(hc->registers->interrupt_status);
+		OHCI_WR(hc->registers->interrupt_status, *status);
 	}
 	return EOK;
@@ -280,14 +283,16 @@
  * @return Error code.
  */
-int ohci_hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
-{
-	assert(hcd);
-	hc_t *instance = hcd_get_driver_data(hcd);
-	assert(instance);
+int ohci_hc_schedule(usb_transfer_batch_t *batch)
+{
+	assert(batch);
+
+	ohci_bus_t *bus = (ohci_bus_t *) endpoint_get_bus(batch->ep);
+	hc_t *hc = bus->hc;
+	assert(hc);
 
 	/* Check for root hub communication */
-	if (batch->target.address == ohci_rh_get_address(&instance->rh)) {
+	if (batch->target.address == ohci_rh_get_address(&hc->rh)) {
 		usb_log_debug("OHCI root hub request.\n");
-		return ohci_rh_schedule(&instance->rh, batch);
+		return ohci_rh_schedule(&hc->rh, batch);
 	}
 	ohci_transfer_batch_t *ohci_batch = ohci_transfer_batch_get(batch);
@@ -299,6 +304,6 @@
 		return err;
 
-	fibril_mutex_lock(&instance->guard);
-	list_append(&ohci_batch->link, &instance->pending_batches);
+	fibril_mutex_lock(&hc->guard);
+	list_append(&ohci_batch->link, &hc->pending_batches);
 	ohci_transfer_batch_commit(ohci_batch);
 
@@ -307,13 +312,13 @@
 	{
 	case USB_TRANSFER_CONTROL:
-		OHCI_SET(instance->registers->command_status, CS_CLF);
+		OHCI_SET(hc->registers->command_status, CS_CLF);
 		break;
 	case USB_TRANSFER_BULK:
-		OHCI_SET(instance->registers->command_status, CS_BLF);
+		OHCI_SET(hc->registers->command_status, CS_BLF);
 		break;
 	default:
 		break;
 	}
-	fibril_mutex_unlock(&instance->guard);
+	fibril_mutex_unlock(&hc->guard);
 	return EOK;
 }
@@ -324,26 +329,30 @@
  * @param[in] status Value of the status register at the time of interrupt.
  */
-void ohci_hc_interrupt(hcd_t *hcd, uint32_t status)
-{
-	assert(hcd);
-	hc_t *instance = hcd_get_driver_data(hcd);
+void ohci_hc_interrupt(bus_t *bus_base, uint32_t status)
+{
+	assert(bus_base);
+
+	ohci_bus_t *bus = (ohci_bus_t *) bus_base;
+	hc_t *hc = bus->hc;
+	assert(hc);
+
 	status = OHCI_RD(status);
-	assert(instance);
+	assert(hc);
 	if ((status & ~I_SF) == 0) /* ignore sof status */
 		return;
-	usb_log_debug2("OHCI(%p) interrupt: %x.\n", instance, status);
+	usb_log_debug2("OHCI(%p) interrupt: %x.\n", hc, status);
 	if (status & I_RHSC)
-		ohci_rh_interrupt(&instance->rh);
+		ohci_rh_interrupt(&hc->rh);
 
 	if (status & I_WDH) {
-		fibril_mutex_lock(&instance->guard);
-		usb_log_debug2("HCCA: %p-%#" PRIx32 " (%p).\n", instance->hcca,
-		    OHCI_RD(instance->registers->hcca),
-		    (void *) addr_to_phys(instance->hcca));
+		fibril_mutex_lock(&hc->guard);
+		usb_log_debug2("HCCA: %p-%#" PRIx32 " (%p).\n", hc->hcca,
+		    OHCI_RD(hc->registers->hcca),
+		    (void *) addr_to_phys(hc->hcca));
 		usb_log_debug2("Periodic current: %#" PRIx32 ".\n",
-		    OHCI_RD(instance->registers->periodic_current));
-
-		link_t *current = list_first(&instance->pending_batches);
-		while (current && current != &instance->pending_batches.head) {
+		    OHCI_RD(hc->registers->periodic_current));
+
+		link_t *current = list_first(&hc->pending_batches);
+		while (current && current != &hc->pending_batches.head) {
 			link_t *next = current->next;
 			ohci_transfer_batch_t *batch =
@@ -357,10 +366,10 @@
 			current = next;
 		}
-		fibril_mutex_unlock(&instance->guard);
+		fibril_mutex_unlock(&hc->guard);
 	}
 
 	if (status & I_UE) {
 		usb_log_fatal("Error like no other!\n");
-		hc_start(instance);
+		hc_start(&hc->base);
 	}
 
@@ -374,7 +383,7 @@
  * @param[in] instance OHCI hc driver structure.
  */
-void hc_gain_control(hc_t *instance)
-{
-	assert(instance);
+int hc_gain_control(hc_device_t *hcd)
+{
+	hc_t *instance = hcd_to_hc(hcd);
 
 	usb_log_debug("Requesting OHCI control.\n");
@@ -409,5 +418,5 @@
 		C_HCFS_SET(instance->registers->control, C_HCFS_RESET);
 		async_usleep(50000);
-		return;
+		return EOK;
 	}
 
@@ -418,5 +427,5 @@
 		if (hc_status == C_HCFS_OPERATIONAL) {
 			usb_log_info("BIOS driver: HC operational.\n");
-			return;
+			return EOK;
 		}
 		/* HC is suspended assert resume for 20ms */
@@ -424,5 +433,5 @@
 		async_usleep(20000);
 		usb_log_info("BIOS driver: HC resumed.\n");
-		return;
+		return EOK;
 	}
 
@@ -431,4 +440,5 @@
 	usb_log_debug("Host controller found in reset state.\n");
 	async_usleep(50000);
+	return EOK;
 }
 
@@ -437,6 +447,7 @@
  * @param[in] instance OHCI hc driver structure.
  */
-void hc_start(hc_t *instance)
-{
+int hc_start(hc_device_t *hcd)
+{
+	hc_t *instance = hcd_to_hc(hcd);
 	ohci_rh_init(&instance->rh, instance->registers, "ohci rh");
 
@@ -489,5 +500,5 @@
 
 	/* Enable interrupts */
-	if (instance->hw_interrupts) {
+	if (instance->base.irq_cap >= 0) {
 		OHCI_WR(instance->registers->interrupt_enable,
 		    OHCI_USED_INTERRUPTS);
@@ -508,4 +519,6 @@
 	usb_log_debug("OHCI HC up and running (ctl_reg=0x%x).\n",
 	    OHCI_RD(instance->registers->control));
+
+	return EOK;
 }
 
@@ -555,5 +568,5 @@
 	memset(&instance->rh, 0, sizeof(instance->rh));
 	/* Init queues */
-	const int ret = hc_init_transfer_lists(instance);
+	int ret = hc_init_transfer_lists(instance);
 	if (ret != EOK) {
 		return ret;
@@ -574,4 +587,12 @@
 	    instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa);
 
+	if ((ret = ohci_bus_init(&instance->bus, instance))) {
+		usb_log_error("HC(%p): Failed to setup bus : %s",
+		    instance, str_error(ret));
+		return ret;
+	}
+
+	hc_device_setup(&instance->base, (bus_t *) &instance->bus);
+
 	return EOK;
 }
Index: uspace/drv/bus/usb/ohci/hc.h
===================================================================
--- uspace/drv/bus/usb/ohci/hc.h	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/ohci/hc.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -58,4 +58,7 @@
 /** Main OHCI driver structure */
 typedef struct hc {
+	/** Common hcd header */
+	hc_device_t base;
+
 	/** Memory mapped I/O registers area */
 	ohci_regs_t *registers;
@@ -70,12 +73,6 @@
 	list_t pending_batches;
 
-	/** Fibril for periodic checks if interrupts can't be used */
-	fid_t interrupt_emulator;
-
 	/** Guards schedule and endpoint manipulation */
 	fibril_mutex_t guard;
-
-	/** interrupts available */
-	bool hw_interrupts;
 
 	/** USB hub emulation structure */
@@ -86,17 +83,22 @@
 } hc_t;
 
-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 *);
+static inline hc_t * hcd_to_hc(hc_device_t *hcd)
+{
+	assert(hcd);
+	return (hc_t *) hcd;
+}
+
+extern int hc_add(hc_device_t *, const hw_res_list_parsed_t *);
+extern int hc_gen_irq_code(irq_code_t *, hc_device_t *, const hw_res_list_parsed_t *);
+extern int hc_gain_control(hc_device_t *);
+extern int hc_start(hc_device_t *);
+extern int hc_gone(hc_device_t *);
 
 extern void hc_enqueue_endpoint(hc_t *, const endpoint_t *);
 extern void hc_dequeue_endpoint(hc_t *, const endpoint_t *);
 
-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);
-extern int ohci_hc_status(hcd_t *, uint32_t *);
-extern int ohci_hc_schedule(hcd_t *, usb_transfer_batch_t *);
+extern int ohci_hc_schedule(usb_transfer_batch_t *);
+extern int ohci_hc_status(bus_t *, uint32_t *);
+extern void ohci_hc_interrupt(bus_t *, uint32_t);
 
 #endif
Index: uspace/drv/bus/usb/ohci/main.c
===================================================================
--- uspace/drv/bus/usb/ohci/main.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/ohci/main.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -48,114 +48,15 @@
 
 #define NAME "ohci"
-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 = {
-        .irq_code_gen = ohci_hc_gen_irq_code,
-        .init = ohci_driver_init,
-        .claim = ohci_driver_claim,
-        .start = ohci_driver_start,
+static const hc_driver_t ohci_driver = {
+	.name = NAME,
+	.hc_device_size = sizeof(hc_t),
+
+	.hc_add = hc_add,
+	.irq_code_gen = hc_gen_irq_code,
+	.claim = hc_gain_control,
+	.start = hc_start,
 	.setup_root_hub = hcd_setup_virtual_root_hub,
-        .fini = ohci_driver_fini,
-        .name = "OHCI",
-	.ops = {
-                .schedule       = ohci_hc_schedule,
-                .irq_hook       = ohci_hc_interrupt,
-                .status_hook    = ohci_hc_status,
-	},
-};
-
-
-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);
-
-	hc_t *instance = malloc(sizeof(hc_t));
-	if (!instance)
-		return ENOMEM;
-
-	if ((err = hc_init(instance, res)) != EOK)
-		goto err;
-
-	if ((err = ohci_bus_init(&instance->bus, hcd, 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;
-}
-
-static void ohci_driver_fini(hcd_t *hcd)
-{
-	assert(hcd);
-	hc_t *hc = hcd_get_driver_data(hcd);
-	if (hc)
-		hc_fini(hc);
-
-	hcd_set_implementation(hcd, NULL, NULL, NULL);
-	free(hc);
-}
-
-/** Initializes a new ddf driver instance of OHCI hcd.
- *
- * @param[in] device DDF instance of the device to initialize.
- * @return Error code.
- */
-static int ohci_dev_add(ddf_dev_t *device)
-{
-	usb_log_debug("ohci_dev_add() called\n");
-	assert(device);
-	return hcd_ddf_add_hc(device, &ohci_hc_driver);
-}
-
-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
-};
-
-static const driver_t ohci_driver = {
-	.name = NAME,
-	.driver_ops = &ohci_driver_ops
+	.hc_gone = hc_gone,
 };
 
@@ -171,5 +72,5 @@
 {
 	log_init(NAME);
-	return ddf_driver_main(&ohci_driver);
+	return hc_driver_main(&ohci_driver);
 }
 
Index: uspace/drv/bus/usb/ohci/ohci_bus.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_bus.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/ohci/ohci_bus.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -158,4 +158,7 @@
 	.parent = &usb2_bus_ops,
 
+	.interrupt = ohci_hc_interrupt,
+	.status = ohci_hc_status,
+
 	.endpoint_destroy = ohci_endpoint_destroy,
 	.endpoint_create = ohci_endpoint_create,
@@ -167,17 +170,17 @@
 	.batch_create = ohci_create_batch,
 	.batch_destroy = ohci_destroy_batch,
+	.batch_schedule = ohci_hc_schedule,
 };
 
 
-int ohci_bus_init(ohci_bus_t *bus, hcd_t *hcd, hc_t *hc)
+int ohci_bus_init(ohci_bus_t *bus, hc_t *hc)
 {
 	assert(hc);
 	assert(bus);
 
-
 	usb2_bus_t *usb2_bus = (usb2_bus_t *) bus;
 	bus_t *bus_base = (bus_t *) bus;
 
-	usb2_bus_init(usb2_bus, hcd, BANDWIDTH_AVAILABLE_USB11);
+	usb2_bus_init(usb2_bus, BANDWIDTH_AVAILABLE_USB11);
 	bus_base->ops = &ohci_bus_ops;
 
Index: uspace/drv/bus/usb/ohci/ohci_bus.h
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_bus.h	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/ohci/ohci_bus.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -62,5 +62,5 @@
 } ohci_bus_t;
 
-int ohci_bus_init(ohci_bus_t *, hcd_t *, hc_t *);
+int ohci_bus_init(ohci_bus_t *, hc_t *);
 
 /** Get and convert assigned ohci_endpoint_t structure
Index: uspace/drv/bus/usb/uhci/hc.c
===================================================================
--- uspace/drv/bus/usb/uhci/hc.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/uhci/hc.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -95,5 +95,5 @@
 
 static void hc_init_hw(const hc_t *instance);
-static int hc_init_mem_structures(hc_t *instance, hcd_t *);
+static int hc_init_mem_structures(hc_t *instance, hc_device_t *);
 static int hc_init_transfer_lists(hc_t *instance);
 
@@ -107,5 +107,5 @@
  * @return Error code.
  */
-int uhci_hc_gen_irq_code(irq_code_t *code, hcd_t *hcd, const hw_res_list_parsed_t *hw_res)
+int hc_gen_irq_code(irq_code_t *code, hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
 {
 	assert(code);
@@ -156,9 +156,8 @@
  * - resume from suspend state (not implemented)
  */
-void uhci_hc_interrupt(hcd_t *hcd, uint32_t status)
-{
-	assert(hcd);
-	hc_t *instance = hcd_get_driver_data(hcd);
-	assert(instance);
+static void hc_interrupt(bus_t *bus, uint32_t status)
+{
+	hc_t *instance = bus_to_hc(bus);
+
 	/* Lower 2 bits are transaction error and transaction complete */
 	if (status & (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)) {
@@ -199,5 +198,5 @@
 		} else {
 			usb_log_fatal("Too many UHCI hardware failures!.\n");
-			hc_fini(instance);
+			hc_gone(&instance->base);
 		}
 	}
@@ -215,7 +214,7 @@
  * interrupt fibrils.
  */
-int hc_init(hc_t *instance, hcd_t *hcd, const hw_res_list_parsed_t *hw_res)
-{
-	assert(instance);
+int hc_add(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
+{
+	hc_t *instance = hcd_to_hc(hcd);
 	assert(hw_res);
 	if (hw_res->io_ranges.count != 1 ||
@@ -249,10 +248,11 @@
 }
 
-void hc_start(hc_t *instance)
-{
+int hc_start(hc_device_t *hcd)
+{
+	hc_t *instance = hcd_to_hc(hcd);
 	hc_init_hw(instance);
 	(void)hc_debug_checker;
 
-	uhci_rh_init(&instance->rh, instance->registers->ports, "uhci");
+	return uhci_rh_init(&instance->rh, instance->registers->ports, "uhci");
 }
 
@@ -261,8 +261,9 @@
  * @param[in] instance Host controller structure to use.
  */
-void hc_fini(hc_t *instance)
+int hc_gone(hc_device_t *instance)
 {
 	assert(instance);
 	//TODO Implement
+	return ENOTSUP;
 }
 
@@ -294,5 +295,5 @@
 	pio_write_32(&registers->flbaseadd, pa);
 
-	if (instance->hw_interrupts) {
+	if (instance->base.irq_cap >= 0) {
 		/* Enable all interrupts, but resume interrupt */
 		pio_write_16(&instance->registers->usbintr,
@@ -320,9 +321,16 @@
 }
 
+static int hc_status(bus_t *, uint32_t *);
+static int hc_schedule(usb_transfer_batch_t *);
+
 static const bus_ops_t uhci_bus_ops = {
 	.parent = &usb2_bus_ops,
 
+	.interrupt = hc_interrupt,
+	.status = hc_status,
+
 	.endpoint_count_bw = bandwidth_count_usb11,
 	.batch_create = create_transfer_batch,
+	.batch_schedule = hc_schedule,
 	.batch_destroy = destroy_transfer_batch,
 };
@@ -338,14 +346,16 @@
  *  - frame list page (needs to be one UHCI hw accessible 4K page)
  */
-int hc_init_mem_structures(hc_t *instance, hcd_t *hcd)
+int hc_init_mem_structures(hc_t *instance, hc_device_t *hcd)
 {
 	int err;
 	assert(instance);
 
-	if ((err = usb2_bus_init(&instance->bus, hcd, BANDWIDTH_AVAILABLE_USB11)))
+	if ((err = usb2_bus_init(&instance->bus, BANDWIDTH_AVAILABLE_USB11)))
 		return err;
 
 	bus_t *bus = (bus_t *) &instance->bus;
 	bus->ops = &uhci_bus_ops;
+
+	hc_device_setup(&instance->base, bus);
 
 	/* Init USB frame list page */
@@ -438,10 +448,8 @@
 }
 
-int uhci_hc_status(hcd_t *hcd, uint32_t *status)
-{
-	assert(hcd);
+static int hc_status(bus_t *bus, uint32_t *status)
+{
+	hc_t *instance = bus_to_hc(bus);
 	assert(status);
-	hc_t *instance = hcd_get_driver_data(hcd);
-	assert(instance);
 
 	*status = 0;
@@ -462,9 +470,7 @@
  * Checks for bandwidth availability and appends the batch to the proper queue.
  */
-int uhci_hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
-{
-	assert(hcd);
-	hc_t *instance = hcd_get_driver_data(hcd);
-	assert(instance);
+static int hc_schedule(usb_transfer_batch_t *batch)
+{
+	hc_t *instance = bus_to_hc(endpoint_get_bus(batch->ep));
 	assert(batch);
 
Index: uspace/drv/bus/usb/uhci/hc.h
===================================================================
--- uspace/drv/bus/usb/uhci/hc.h	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/uhci/hc.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -100,4 +100,7 @@
 /** Main UHCI driver structure */
 typedef struct hc {
+	/* Common hc_device header */
+	hc_device_t base;
+
 	uhci_rh_t rh;
 	usb2_bus_t bus;
@@ -119,6 +122,4 @@
 	/** Pointer table to the above lists, helps during scheduling */
 	transfer_list_t *transfers[2][4];
-	/** Indicator of hw interrupts availability */
-	bool hw_interrupts;
 
 	/** Number of hw failures detected. */
@@ -126,13 +127,20 @@
 } hc_t;
 
-extern int hc_init(hc_t *, hcd_t *, const hw_res_list_parsed_t *);
-extern void hc_start(hc_t *);
-extern void hc_fini(hc_t *);
+static inline hc_t *hcd_to_hc(hc_device_t *hcd)
+{
+	assert(hcd);
+	return (hc_t *) hcd;
+}
 
-extern int uhci_hc_gen_irq_code(irq_code_t *, hcd_t *,const hw_res_list_parsed_t *);
+static inline hc_t *bus_to_hc(bus_t *bus)
+{
+	assert(bus);
+	return member_to_inst(bus, hc_t, bus);
+}
 
-extern void uhci_hc_interrupt(hcd_t *, uint32_t);
-extern int uhci_hc_status(hcd_t *, uint32_t *);
-extern int uhci_hc_schedule(hcd_t *, usb_transfer_batch_t *);
+extern int hc_add(hc_device_t *, const hw_res_list_parsed_t *);
+extern int hc_gen_irq_code(irq_code_t *, hc_device_t *, const hw_res_list_parsed_t *);
+extern int hc_start(hc_device_t *);
+extern int hc_gone(hc_device_t *);
 
 #endif
Index: uspace/drv/bus/usb/uhci/main.c
===================================================================
--- uspace/drv/bus/usb/uhci/main.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/uhci/main.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -49,67 +49,16 @@
 #define NAME "uhci"
 
-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(hcd_t *, ddf_dev_t *);
+static int disable_legacy(hc_device_t *);
 
-static const ddf_hc_driver_t uhci_hc_driver = {
-        .claim = disable_legacy,
-        .irq_code_gen = uhci_hc_gen_irq_code,
-        .init = uhci_driver_init,
-        .start = uhci_driver_start,
+static const hc_driver_t uhci_driver = {
+	.name = NAME,
+	.hc_device_size = sizeof(hc_t),
+	.claim = disable_legacy,
+	.irq_code_gen = hc_gen_irq_code,
+	.hc_add = hc_add,
+	.start = hc_start,
 	.setup_root_hub = hcd_setup_virtual_root_hub,
-        .fini = uhci_driver_fini,
-        .name = "UHCI",
-	.ops = {
-		.schedule    = uhci_hc_schedule,
-		.irq_hook    = uhci_hc_interrupt,
-		.status_hook = uhci_hc_status,
-	},
+	.hc_gone = hc_gone,
 };
-
-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);
-
-	hc_t *instance = malloc(sizeof(hc_t));
-	if (!instance)
-		return ENOMEM;
-
-	if ((err = hc_init(instance, hcd, 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;
-}
-
-static void uhci_driver_fini(hcd_t *hcd)
-{
-	assert(hcd);
-	hc_t *hc = hcd_get_driver_data(hcd);
-	if (hc)
-		hc_fini(hc);
-
-	hcd_set_implementation(hcd, NULL, NULL, NULL);
-	free(hc);
-}
 
 /** Call the PCI driver with a request to clear legacy support register
@@ -118,9 +67,9 @@
  * @return Error code.
  */
-static int disable_legacy(hcd_t *hcd, ddf_dev_t *device)
+static int disable_legacy(hc_device_t *hcd)
 {
-	assert(device);
+	assert(hcd);
 
-	async_sess_t *parent_sess = ddf_dev_parent_sess_get(device);
+	async_sess_t *parent_sess = ddf_dev_parent_sess_get(hcd->ddf_dev);
 	if (parent_sess == NULL)
 		return ENOMEM;
@@ -130,38 +79,4 @@
 	return pci_config_space_write_16(parent_sess, 0xc0, 0xaf00);
 }
-
-/** Initialize a new ddf driver instance for uhci hc and hub.
- *
- * @param[in] device DDF instance of the device to initialize.
- * @return Error code.
- */
-static int uhci_dev_add(ddf_dev_t *device)
-{
-	usb_log_debug2("uhci_dev_add() called\n");
-	assert(device);
-	return hcd_ddf_add_hc(device, &uhci_hc_driver);
-}
-
-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
-};
-
-static const driver_t uhci_driver = {
-	.name = NAME,
-	.driver_ops = &uhci_driver_ops
-};
-
 
 /** Initialize global driver structures (NONE).
@@ -178,5 +93,5 @@
 	log_init(NAME);
 	logctl_set_log_level(NAME, LVL_DEBUG2);
-	return ddf_driver_main(&uhci_driver);
+	return hc_driver_main(&uhci_driver);
 }
 /**
Index: uspace/drv/bus/usb/vhc/main.c
===================================================================
--- uspace/drv/bus/usb/vhc/main.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/vhc/main.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -69,36 +69,31 @@
 		return ret;
 	}
-	vhc_init(vhc, dev_to_hcd(dev));
 	return EOK;
 }
 
-hcd_ops_t vhc_hc_ops = {
-	.schedule = vhc_schedule,
-};
-
 static int vhc_dev_add(ddf_dev_t *dev)
 {
+	/* Initialize generic structures */
+	int ret = hcd_ddf_setup_hc(dev, sizeof(vhc_data_t));
+	if (ret != EOK) {
+		usb_log_error("Failed to init HCD structures: %s.\n",
+		   str_error(ret));
+		return ret;
+	}
+	vhc_data_t *vhc = ddf_dev_data_get(dev);
+	vhc_init(vhc);
+
+	hc_device_setup(&vhc->base, (bus_t *) &vhc->bus);
+
 	/* Initialize virtual structure */
 	ddf_fun_t *ctl_fun = NULL;
-	int ret = vhc_control_node(dev, &ctl_fun);
+	ret = vhc_control_node(dev, &ctl_fun);
 	if (ret != EOK) {
 		usb_log_error("Failed to setup control node.\n");
 		return ret;
 	}
-	vhc_data_t *data = ddf_fun_data_get(ctl_fun);
-
-	/* Initialize generic structures */
-	ret = hcd_ddf_setup_hc(dev);
-	if (ret != EOK) {
-		usb_log_error("Failed to init HCD structures: %s.\n",
-		   str_error(ret));
-		ddf_fun_destroy(ctl_fun);
-		return ret;
-	}
-
-	hcd_set_implementation(dev_to_hcd(dev), data, &vhc_hc_ops, &data->bus.base);
 
 	/* Add virtual hub device */
-	ret = vhc_virtdev_plug_hub(data, &data->hub, NULL, 0);
+	ret = vhc_virtdev_plug_hub(vhc, &vhc->hub, NULL, 0);
 	if (ret != EOK) {
 		usb_log_error("Failed to plug root hub: %s.\n", str_error(ret));
@@ -111,5 +106,5 @@
 	 * needs to be ready at this time.
 	 */
-	ret = hcd_setup_virtual_root_hub(dev_to_hcd(dev), dev);
+	ret = hcd_setup_virtual_root_hub(&vhc->base);
 	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 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/vhc/transfer.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -40,15 +40,15 @@
 static bool is_set_address_transfer(vhc_transfer_t *transfer)
 {
-	if (transfer->batch->target.endpoint != 0) {
-		return false;
-	}
-	if (transfer->batch->ep->transfer_type != USB_TRANSFER_CONTROL) {
-		return false;
-	}
-	if (transfer->batch->dir != USB_DIRECTION_OUT) {
+	if (transfer->batch.target.endpoint != 0) {
+		return false;
+	}
+	if (transfer->batch.ep->transfer_type != USB_TRANSFER_CONTROL) {
+		return false;
+	}
+	if (transfer->batch.dir != USB_DIRECTION_OUT) {
 		return false;
 	}
 	const usb_device_request_setup_packet_t *setup
-		= &transfer->batch->setup.packet;
+		= &transfer->batch.setup.packet;
 	if (setup->request_type != 0) {
 		return false;
@@ -150,39 +150,42 @@
 	assert(outcome != ENAK);
 	assert(transfer);
-	assert(transfer->batch);
-	transfer->batch->error = outcome;
-	transfer->batch->transfered_size = data_transfer_size;
-	usb_transfer_batch_finish(transfer->batch);
+	transfer->batch.error = outcome;
+	transfer->batch.transfered_size = data_transfer_size;
+	usb_transfer_batch_finish(&transfer->batch);
 	free(transfer);
+}
+
+static usb_transfer_batch_t *batch_create(endpoint_t *ep)
+{
+	vhc_transfer_t *transfer = calloc(1, sizeof(vhc_transfer_t));
+	usb_transfer_batch_init(&transfer->batch, ep);
+	link_initialize(&transfer->link);
+	return &transfer->batch;
 }
 
 static const bus_ops_t vhc_bus_ops = {
 	.parent = &usb2_bus_ops,
+
 	.endpoint_count_bw = bandwidth_count_usb11,
+	.batch_create = batch_create,
+	.batch_schedule = vhc_schedule,
 };
 
-int vhc_init(vhc_data_t *instance, hcd_t *hcd)
+int vhc_init(vhc_data_t *instance)
 {
 	assert(instance);
 	list_initialize(&instance->devices);
 	fibril_mutex_initialize(&instance->guard);
-	usb2_bus_init(&instance->bus, hcd, BANDWIDTH_AVAILABLE_USB11);
+	usb2_bus_init(&instance->bus, BANDWIDTH_AVAILABLE_USB11);
 	instance->bus.base.ops = &vhc_bus_ops;
-	instance->magic = 0xDEADBEEF;
 	return virthub_init(&instance->hub, "root hub");
 }
 
-int vhc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
-{
-	assert(hcd);
+int vhc_schedule(usb_transfer_batch_t *batch)
+{
 	assert(batch);
-	vhc_data_t *vhc = hcd_get_driver_data(hcd);
+	vhc_transfer_t *transfer = (vhc_transfer_t *) batch;
+	vhc_data_t *vhc = bus_to_vhc(endpoint_get_bus(batch->ep));
 	assert(vhc);
-
-	vhc_transfer_t *transfer = malloc(sizeof(vhc_transfer_t));
-	if (!transfer)
-		return ENOMEM;
-	link_initialize(&transfer->link);
-	transfer->batch = batch;
 
 	fibril_mutex_lock(&vhc->guard);
@@ -192,5 +195,5 @@
 	list_foreach(vhc->devices, link, vhc_virtdev_t, dev) {
 		fibril_mutex_lock(&dev->guard);
-		if (dev->address == transfer->batch->target.address) {
+		if (dev->address == transfer->batch.target.address) {
 			if (!targets) {
 				list_append(&transfer->link, &dev->transfer_queue);
@@ -227,8 +230,8 @@
 		size_t data_transfer_size = 0;
 		if (dev->dev_sess) {
-			rc = process_transfer_remote(transfer->batch,
+			rc = process_transfer_remote(&transfer->batch,
 			    dev->dev_sess, &data_transfer_size);
 		} else if (dev->dev_local != NULL) {
-			rc = process_transfer_local(transfer->batch,
+			rc = process_transfer_local(&transfer->batch,
 			    dev->dev_local, &data_transfer_size);
 		} else {
@@ -244,5 +247,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 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/vhc/vhcd.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -39,7 +39,9 @@
 #include <usbvirt/device.h>
 #include <async.h>
+#include <macros.h>
 
 #include <usb/host/hcd.h>
 #include <usb/host/usb2_bus.h>
+#include <usb/host/usb_transfer_batch.h>
 
 #define NAME "vhc"
@@ -56,15 +58,29 @@
 
 typedef struct {
-	uint32_t magic;
+	hc_device_t base;
+
+	usb2_bus_t bus;
+	ddf_fun_t *virtual_fun;
 	list_t devices;
 	fibril_mutex_t guard;
 	usbvirt_device_t hub;
-	usb2_bus_t bus;
 } vhc_data_t;
 
 typedef struct {
+	usb_transfer_batch_t batch;
 	link_t link;
-	usb_transfer_batch_t *batch;
 } vhc_transfer_t;
+
+static inline vhc_data_t *hcd_to_vhc(hc_device_t *hcd)
+{
+	assert(hcd);
+	return (vhc_data_t *) hcd;
+}
+
+static inline vhc_data_t *bus_to_vhc(bus_t *bus)
+{
+	assert(bus);
+	return member_to_inst(bus, vhc_data_t, bus);
+}
 
 void on_client_close(ddf_fun_t *fun);
@@ -77,6 +93,6 @@
 void vhc_virtdev_unplug(vhc_data_t *, uintptr_t);
 
-int vhc_init(vhc_data_t *instance, hcd_t *);
-int vhc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch);
+int vhc_init(vhc_data_t *);
+int vhc_schedule(usb_transfer_batch_t *);
 int vhc_transfer_queue_processor(void *arg);
 
Index: uspace/drv/bus/usb/xhci/bus.c
===================================================================
--- uspace/drv/bus/usb/xhci/bus.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/xhci/bus.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -111,5 +111,5 @@
 
 	uint16_t max_packet_size;
-	if ((err = hcd_get_ep0_max_packet_size(&max_packet_size, hc->hcd, &dev->base)))
+	if ((err = hcd_get_ep0_max_packet_size(&max_packet_size, (bus_t *) &hc->bus, &dev->base)))
 		return err;
 
@@ -168,5 +168,5 @@
 
 	/* Read the device descriptor, derive the match ids */
-	if ((err = hcd_ddf_device_explore(dev))) {
+	if ((err = hcd_device_explore(dev))) {
 		usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
 		goto err_address;
@@ -233,5 +233,5 @@
 	/* Destroy DDF device. */
 	/* XXX: Not a good idea, this method should not destroy devices. */
-	hcd_ddf_device_destroy(dev);
+	hcd_ddf_fun_destroy(dev);
 
 	return EOK;
@@ -501,4 +501,8 @@
 	BIND_OP(batch_destroy)
 #undef BIND_OP
+
+	.interrupt = hc_interrupt,
+	.status = hc_status,
+	.batch_schedule = hc_schedule,
 };
 
@@ -507,5 +511,5 @@
 	assert(bus);
 
-	bus_init(&bus->base, hc->hcd, sizeof(xhci_device_t));
+	bus_init(&bus->base, sizeof(xhci_device_t));
 
 	bus->devices_by_slot = calloc(hc->max_slots, sizeof(xhci_device_t *));
Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -432,6 +432,7 @@
  * Used only when polling. Shall supplement the irq_commands.
  */
-int hc_status(xhci_hc_t *hc, uint32_t *status)
-{
+int hc_status(bus_t *bus, uint32_t *status)
+{
+	xhci_hc_t *hc = bus_to_hc(bus);
 	int ip = XHCI_REG_RD(hc->rt_regs->ir, XHCI_INTR_IP);
 	if (ip) {
@@ -449,8 +450,8 @@
 }
 
-int hc_schedule(xhci_hc_t *hc, usb_transfer_batch_t *batch)
+int hc_schedule(usb_transfer_batch_t *batch)
 {
 	assert(batch);
-	assert(batch->ep);
+	xhci_hc_t *hc = bus_to_hc(endpoint_get_bus(batch->ep));
 
 	if (!batch->target.address) {
@@ -532,6 +533,7 @@
 }
 
-void hc_interrupt(xhci_hc_t *hc, uint32_t status)
-{
+void hc_interrupt(bus_t *bus, uint32_t status)
+{
+	xhci_hc_t *hc = bus_to_hc(bus);
 	status = xhci2host(32, status);
 
Index: uspace/drv/bus/usb/xhci/hc.h
===================================================================
--- uspace/drv/bus/usb/xhci/hc.h	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/xhci/hc.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -51,4 +51,7 @@
 
 typedef struct xhci_hc {
+	/** Common HC device header */
+	hc_device_t base;
+
 	/* MMIO range */
 	addr_range_t mmio_range;
@@ -86,8 +89,11 @@
 	xhci_port_speed_t speeds [16];
 	uint8_t speed_to_psiv [USB_SPEED_MAX];
+} xhci_hc_t;
 
-	/* TODO: Hack. Figure out a better way. */
-	hcd_t *hcd;
-} xhci_hc_t;
+static inline xhci_hc_t *bus_to_hc(bus_t *bus)
+{
+	assert(bus);
+	return member_to_inst(bus, xhci_hc_t, bus);
+}
 
 typedef struct xhci_endpoint xhci_endpoint_t;
@@ -99,7 +105,4 @@
 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);
@@ -113,4 +116,8 @@
 int hc_update_endpoint(xhci_hc_t *, uint32_t, uint8_t, xhci_ep_ctx_t *);
 
+int hc_schedule(usb_transfer_batch_t *batch);
+int hc_status(bus_t *, uint32_t *);
+void hc_interrupt(bus_t *, uint32_t);
+
 #endif
 
Index: uspace/drv/bus/usb/xhci/main.c
===================================================================
--- uspace/drv/bus/usb/xhci/main.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/xhci/main.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -47,144 +47,60 @@
 #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 inline xhci_hc_t *hcd_to_hc(hc_device_t *hcd)
+{
+	assert(hcd);
+	return (xhci_hc_t *) hcd;
+}
 
-static const ddf_hc_driver_t xhci_ddf_hc_driver = {
-	.name = "XHCI-PCI",
-	.init = hc_driver_init,
+static int hcd_hc_add(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
+{
+	int err;
+	xhci_hc_t *hc = hcd_to_hc(hcd);
+	hc_device_setup(hcd, (bus_t *) &hc->bus);
+
+	if ((err = hc_init_mmio(hc, hw_res)))
+		return err;
+
+	if ((err = hc_init_memory(hc, hcd->ddf_dev)))
+		return err;
+
+	return EOK;
+}
+
+static int hcd_irq_code_gen(irq_code_t *code, hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
+{
+	xhci_hc_t *hc = hcd_to_hc(hcd);
+	return hc_irq_code_gen(code, hc, hw_res);
+}
+
+static int hcd_claim(hc_device_t *hcd)
+{
+	xhci_hc_t *hc = hcd_to_hc(hcd);
+	return hc_claim(hc, hcd->ddf_dev);
+}
+
+static int hcd_start(hc_device_t *hcd)
+{
+	xhci_hc_t *hc = hcd_to_hc(hcd);
+	return hc_start(hc, hcd->irq_cap >= 0);
+}
+
+static int hcd_hc_gone(hc_device_t *hcd)
+{
+	xhci_hc_t *hc = hcd_to_hc(hcd);
+	hc_fini(hc);
+	return EOK;
+}
+
+static const hc_driver_t xhci_driver = {
+	.name = NAME,
+	.hc_device_size = sizeof(xhci_hc_t),
+
+	.hc_add = hcd_hc_add,
 	.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,
-	}
+	.hc_gone = hcd_hc_gone,
 };
-
-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;
-
-	hc->hcd = hcd;
-
-	if ((err = hc_init_memory(hc, device)))
-		goto err;
-
-	hcd_set_implementation(hcd, hc, &xhci_ddf_hc_driver.ops, &hc->bus.base);
-
-	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).
@@ -200,5 +116,5 @@
 	log_init(NAME);
 	logctl_set_log_level(NAME, LVL_DEBUG2);
-	return ddf_driver_main(&xhci_driver);
+	return hc_driver_main(&xhci_driver);
 }
 
Index: uspace/drv/bus/usb/xhci/rh.c
===================================================================
--- uspace/drv/bus/usb/xhci/rh.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/drv/bus/usb/xhci/rh.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -94,5 +94,5 @@
 	xhci_bus_t *bus = &rh->hc->bus;
 
-	device_t *dev = hcd_ddf_device_create(rh->hc_device, &bus->base);
+	device_t *dev = hcd_ddf_fun_create(&rh->hc->base);
 	if (!dev) {
 		usb_log_error("Failed to create USB device function.");
@@ -132,5 +132,5 @@
 
 err_usb_dev:
-	hcd_ddf_device_destroy(dev);
+	hcd_ddf_fun_destroy(dev);
 	return err;
 }
Index: uspace/lib/usbhost/include/usb/host/bus.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/bus.h	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/lib/usbhost/include/usb/host/bus.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -43,11 +43,11 @@
 #define LIBUSBHOST_HOST_BUS_H
 
-#include <usb/usb.h>
-#include <usb/request.h>
-#include <usb/host/hcd.h>
-
 #include <assert.h>
 #include <fibril_synch.h>
 #include <stdbool.h>
+#include <usb/host/hcd.h>
+#include <usb/request.h>
+#include <usb/usb.h>
+#include <usbhc_iface.h>
 
 typedef struct hcd hcd_t;
@@ -94,4 +94,6 @@
 
 	/* Global operations on the bus */
+	void (*interrupt)(bus_t *, uint32_t);
+	int (*status)(bus_t *, uint32_t *);
 	int (*reserve_default_address)(bus_t *, usb_speed_t);
 	int (*release_default_address)(bus_t *);
@@ -116,5 +118,6 @@
 
 	/* Operations on batch */
-	void (*batch_destroy)(usb_transfer_batch_t *);	/**< Optional */
+	void (*batch_destroy)(usb_transfer_batch_t *);		/**< Optional */
+	int (*batch_schedule)(usb_transfer_batch_t *);
 };
 
@@ -129,7 +132,5 @@
 	fibril_mutex_t guard;
 
-	/* TODO: get rid of this one. */
-	hcd_t *hcd;
-
+	/* Size of the device_t extended structure */
 	size_t device_size;
 
@@ -140,5 +141,5 @@
 } bus_t;
 
-void bus_init(bus_t *, hcd_t *, size_t);
+void bus_init(bus_t *, size_t);
 int bus_device_init(device_t *, bus_t *);
 
@@ -150,4 +151,12 @@
 int bus_device_online(device_t *);
 int bus_device_offline(device_t *);
+
+int bus_device_send_batch(device_t *, usb_target_t,
+    usb_direction_t direction, char *, size_t, uint64_t,
+    usbhc_iface_transfer_callback_t, void *, const char *);
+
+ssize_t bus_device_send_batch_sync(device_t *, usb_target_t,
+    usb_direction_t direction, char *, size_t, uint64_t,
+    const char *);
 
 int bus_endpoint_add(device_t *, const usb_endpoint_desc_t *, endpoint_t **);
Index: uspace/lib/usbhost/include/usb/host/ddf_helpers.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/ddf_helpers.h	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/lib/usbhost/include/usb/host/ddf_helpers.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -39,61 +39,22 @@
 #include <ddf/driver.h>
 #include <ddf/interrupt.h>
-#include <device/hw_res_parsed.h>
 #include <usb/usb.h>
 
 #include <usb/host/hcd.h>
 
-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 *);
+int hcd_ddf_setup_hc(ddf_dev_t *, size_t);
+void hcd_ddf_clean_hc(hc_device_t *);
 
-typedef void (*driver_stop_t)(hcd_t *);
-typedef void (*driver_fini_t)(hcd_t *);
+int hcd_setup_virtual_root_hub(hc_device_t *);
 
-/**
- * All callbacks are optional.
- */
-typedef struct {
-	hcd_ops_t ops;
-	const char *name;
+device_t *hcd_ddf_fun_create(hc_device_t *);
+void hcd_ddf_fun_destroy(device_t *);
 
-	interrupt_handler_t *irq_handler;  /**< Handler of IRQ. Do have generic implementation. */
+int hcd_device_explore(device_t *);
 
-	/* 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 */
+int hcd_ddf_enable_interrupt(hc_device_t *hcd, int);
+int hcd_ddf_get_registers(hc_device_t *hcd, hw_res_list_parsed_t *hw_res);
 
-	/* 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);
-void hcd_ddf_clean_hc(ddf_dev_t *device);
-
-int hcd_setup_virtual_root_hub(hcd_t *, ddf_dev_t *);
-
-device_t *hcd_ddf_device_create(ddf_dev_t *, bus_t *);
-void hcd_ddf_device_destroy(device_t *);
-int hcd_ddf_device_explore(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);
-
-int hcd_ddf_enable_interrupt(ddf_dev_t *device, int);
-int hcd_ddf_get_registers(ddf_dev_t *device, hw_res_list_parsed_t *hw_res);
-int hcd_ddf_setup_interrupts(ddf_dev_t *device,
-    const hw_res_list_parsed_t *hw_res,
-    interrupt_handler_t handler,
-    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);
+void hcd_ddf_gen_irq_handler(ipc_callid_t iid, ipc_call_t *call, ddf_dev_t *dev);
 
 #endif
Index: uspace/lib/usbhost/include/usb/host/endpoint.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/endpoint.h	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/lib/usbhost/include/usb/host/endpoint.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -109,4 +109,8 @@
 ssize_t endpoint_count_bw(endpoint_t *, size_t);
 
+int endpoint_send_batch(endpoint_t *, usb_target_t, usb_direction_t,
+    char *, size_t, uint64_t, usbhc_iface_transfer_callback_t, void *,
+    const char *);
+
 static inline bus_t *endpoint_get_bus(endpoint_t *ep)
 {
Index: uspace/lib/usbhost/include/usb/host/hcd.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/hcd.h	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/lib/usbhost/include/usb/host/hcd.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -37,75 +37,82 @@
 #define LIBUSBHOST_HOST_HCD_H
 
-#include <assert.h>
-#include <mem.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <usb/usb.h>
-#include <usbhc_iface.h>
+#include <ddf/driver.h>
+#include <usb/request.h>
 
-typedef struct hcd hcd_t;
+typedef struct hw_resource_list_parsed hw_res_list_parsed_t;
 typedef struct bus bus_t;
 typedef struct device device_t;
-typedef struct usb_transfer_batch usb_transfer_batch_t;
 
-typedef int (*schedule_hook_t)(hcd_t *, usb_transfer_batch_t *);
-typedef void (*interrupt_hook_t)(hcd_t *, uint32_t);
-typedef int (*status_hook_t)(hcd_t *, uint32_t *);
+/* Treat this header as read-only in driver code.
+ * It could be opaque, but why to complicate matters.
+ */
+typedef struct hc_device {
+	/* Bus instance */
+	bus_t *bus;
 
-typedef struct {
-	/** Transfer scheduling, implement in device driver. */
-	schedule_hook_t schedule;
-	/** Hook to be called on device interrupt, passes ARG1 */
-	interrupt_hook_t irq_hook;
-	/** Periodic polling hook */
-	status_hook_t status_hook;
-} hcd_ops_t;
+	/* Managed DDF device */
+	ddf_dev_t *ddf_dev;
 
-/** Generic host controller driver structure. */
-struct hcd {
-	/** Endpoint manager. */
-	bus_t *bus;
+	/* Control function */
+	ddf_fun_t *ctl_fun;
+
+	/* Result of enabling HW IRQs */
+	int irq_cap;
 
 	/** Interrupt replacement fibril */
 	fid_t polling_fibril;
 
-	/** Driver implementation */
-	hcd_ops_t ops;
+	/* This structure is meant to be extended by driver code. */
+} hc_device_t;
 
-	/** Device specific driver data. */
-	void * driver_data;
-};
+typedef struct hc_driver {
+	const char *name;
 
-extern void hcd_init(hcd_t *);
+	/** Size of the device data to be allocated, and passed as the
+	 * hc_device_t. */
+	size_t hc_device_size;
 
-static inline void hcd_set_implementation(hcd_t *hcd, void *data,
-    const hcd_ops_t *ops, bus_t *bus)
-{
-	assert(hcd);
-	if (ops) {
-		hcd->driver_data = data;
-		hcd->ops = *ops;
-		hcd->bus = bus;
-	} else {
-		memset(&hcd->ops, 0, sizeof(hcd->ops));
-	}
+	/** Initialize device structures. */
+	int (*hc_add)(hc_device_t *, const hw_res_list_parsed_t *);
+
+	/** Generate IRQ code to handle interrupts. */
+	int (*irq_code_gen)(irq_code_t *, hc_device_t *, const hw_res_list_parsed_t *);
+
+	/** Claim device from BIOS. */
+	int (*claim)(hc_device_t *);
+
+	/** Start the host controller. */
+	int (*start)(hc_device_t *);
+
+	/** Setup the virtual roothub. */
+	int (*setup_root_hub)(hc_device_t *);
+
+	/** Stop the host controller (after start has been called) */
+	int (*stop)(hc_device_t *);
+
+	/** HC was asked to be removed (after hc_add has been called) */
+	int (*hc_remove)(hc_device_t *);
+
+	/** HC is gone. */
+	int (*hc_gone)(hc_device_t *);
+} hc_driver_t;
+
+/* Drivers should call this before leaving hc_add */
+static inline void hc_device_setup(hc_device_t *hcd, bus_t *bus) {
+	hcd->bus = bus;
 }
 
-static inline void * hcd_get_driver_data(hcd_t *hcd)
+static inline hc_device_t *dev_to_hcd(ddf_dev_t *dev)
 {
-	assert(hcd);
-	return hcd->driver_data;
+	return ddf_dev_data_get(dev);
 }
 
-extern int hcd_get_ep0_max_packet_size(uint16_t *, hcd_t *, device_t *);
+int hc_driver_main(const hc_driver_t *);
+
+/* TODO: These are a kind of utility functions, they should probably go
+ * somewhere else.
+ */
+extern int hcd_get_ep0_max_packet_size(uint16_t *, bus_t *, device_t *);
 extern void hcd_setup_device_tt(device_t *);
-
-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 *, device_t *, usb_target_t,
-    usb_direction_t direction, char *, size_t, uint64_t,
-    const char *);
 
 /** How many toggles need to be reset */
@@ -116,4 +123,7 @@
 } toggle_reset_mode_t;
 
+extern toggle_reset_mode_t hcd_get_request_toggle_reset_mode(
+    const usb_device_request_setup_packet_t *request);
+
 #endif
 
Index: uspace/lib/usbhost/include/usb/host/usb2_bus.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/usb2_bus.h	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/lib/usbhost/include/usb/host/usb2_bus.h	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -66,5 +66,5 @@
 extern const bus_ops_t usb2_bus_ops;
 
-extern int usb2_bus_init(usb2_bus_t *, hcd_t *, size_t);
+extern int usb2_bus_init(usb2_bus_t *, size_t);
 
 #endif
Index: uspace/lib/usbhost/src/bus.c
===================================================================
--- uspace/lib/usbhost/src/bus.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/lib/usbhost/src/bus.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -46,13 +46,11 @@
  * Initializes the bus structure.
  */
-void bus_init(bus_t *bus, hcd_t *hcd, size_t device_size)
-{
-	assert(bus);
-	assert(hcd);
+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->hcd = hcd;
 	bus->device_size = device_size;
 }
@@ -61,5 +59,4 @@
 {
 	assert(bus);
-	assert(bus->hcd);
 
 	memset(dev, 0, sizeof(*dev));
@@ -258,4 +255,88 @@
 }
 
+/** Prepare generic usb_transfer_batch and schedule it.
+ * @param device Device for which to send the batch
+ * @param target address and endpoint number.
+ * @param setup_data Data to use in setup stage (Control communication type)
+ * @param in Callback for device to host communication.
+ * @param out Callback for host to device communication.
+ * @param arg Callback parameter.
+ * @param name Communication identifier (for nicer output).
+ * @return Error code.
+ */
+int bus_device_send_batch(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(device->address == target.address);
+
+	/* Temporary reference */
+	endpoint_t *ep = bus_find_endpoint(device, target, direction);
+	if (ep == NULL) {
+		usb_log_error("Endpoint(%d:%d) not registered for %s.\n",
+		    device->address, target.endpoint, name);
+		return ENOENT;
+	}
+
+	assert(ep->device == device);
+
+	const int err = endpoint_send_batch(ep, target, direction, data, size, setup_data,
+	    on_complete, arg, name);
+
+	/* Temporary reference */
+	endpoint_del_ref(ep);
+
+	return err;
+}
+
+typedef struct {
+	fibril_mutex_t done_mtx;
+	fibril_condvar_t done_cv;
+	unsigned done;
+
+	size_t transfered_size;
+	int error;
+} sync_data_t;
+
+static int sync_transfer_complete(void *arg, int error, size_t transfered_size)
+{
+	sync_data_t *d = arg;
+	assert(d);
+	d->transfered_size = transfered_size;
+	d->error = error;
+	fibril_mutex_lock(&d->done_mtx);
+	d->done = 1;
+	fibril_condvar_broadcast(&d->done_cv);
+	fibril_mutex_unlock(&d->done_mtx);
+	return EOK;
+}
+
+ssize_t bus_device_send_batch_sync(device_t *device, usb_target_t target,
+    usb_direction_t direction, char *data, size_t size, uint64_t setup_data,
+    const char *name)
+{
+	sync_data_t sd = { .done = 0 };
+	fibril_mutex_initialize(&sd.done_mtx);
+	fibril_condvar_initialize(&sd.done_cv);
+
+	const int ret = bus_device_send_batch(device, target, direction,
+	    data, size, setup_data,
+	    sync_transfer_complete, &sd, name);
+	if (ret != EOK)
+		return ret;
+
+	fibril_mutex_lock(&sd.done_mtx);
+	while (!sd.done) {
+		fibril_condvar_wait_timeout(&sd.done_cv, &sd.done_mtx, 3000000);
+		if (!sd.done)
+			usb_log_debug2("Still waiting...");
+	}
+	fibril_mutex_unlock(&sd.done_mtx);
+
+	return (sd.error == EOK)
+		? (ssize_t) sd.transfered_size
+		: (ssize_t) sd.error;
+}
+
 /**
  * @}
Index: uspace/lib/usbhost/src/ddf_helpers.c
===================================================================
--- uspace/lib/usbhost/src/ddf_helpers.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/lib/usbhost/src/ddf_helpers.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -53,26 +53,6 @@
 #include "ddf_helpers.h"
 
-typedef struct hc_dev {
-	ddf_fun_t *ctl_fun;
-	hcd_t hcd;
-} hc_dev_t;
-
-static hc_dev_t *dev_to_hc_dev(ddf_dev_t *dev)
-{
-	return ddf_dev_data_get(dev);
-}
-
-hcd_t *dev_to_hcd(ddf_dev_t *dev)
-{
-	hc_dev_t *hc_dev = dev_to_hc_dev(dev);
-	if (!hc_dev) {
-		usb_log_error("Invalid HCD device.\n");
-		return NULL;
-	}
-	return &hc_dev->hcd;
-}
-
-
-static int hcd_ddf_new_device(hcd_t *hcd, ddf_dev_t *hc, device_t *hub_dev, unsigned port);
+
+static int hcd_ddf_new_device(hc_device_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);
 
@@ -89,5 +69,5 @@
 {
 	assert(fun);
-	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
+	hc_device_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
 	device_t *dev = ddf_fun_data_get(fun);
 	assert(hcd);
@@ -113,5 +93,5 @@
 {
 	assert(fun);
-	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
+	hc_device_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
 	device_t *dev = ddf_fun_data_get(fun);
 	assert(hcd);
@@ -138,5 +118,5 @@
 {
 	assert(fun);
-	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
+	hc_device_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
 	device_t *dev = ddf_fun_data_get(fun);
 	assert(hcd);
@@ -152,5 +132,5 @@
 {
 	assert(fun);
-	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
+	hc_device_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
 	device_t *dev = ddf_fun_data_get(fun);
 	assert(hcd);
@@ -167,5 +147,5 @@
 	ddf_dev_t *hc = ddf_fun_get_dev(fun);
 	assert(hc);
-	hcd_t *hcd = dev_to_hcd(hc);
+	hc_device_t *hcd = dev_to_hcd(hc);
 	assert(hcd);
 	device_t *hub = ddf_fun_data_get(fun);
@@ -218,5 +198,4 @@
 {
 	assert(fun);
-	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
 	device_t *dev = ddf_fun_data_get(fun);
 	assert(dev);
@@ -224,5 +203,5 @@
 	target.address = dev->address;
 
-	return hcd_send_batch(hcd, dev, target, USB_DIRECTION_IN,
+	return bus_device_send_batch(dev, target, USB_DIRECTION_IN,
 	    data, size, setup_data,
 	    callback, arg, "READ");
@@ -244,5 +223,4 @@
 {
 	assert(fun);
-	hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
 	device_t *dev = ddf_fun_data_get(fun);
 	assert(dev);
@@ -250,5 +228,5 @@
 	target.address = dev->address;
 
-	return hcd_send_batch(hcd, dev, target, USB_DIRECTION_OUT,
+	return bus_device_send_batch(dev, target, USB_DIRECTION_OUT,
 	    (char *) data, size, setup_data,
 	    callback, arg, "WRITE");
@@ -337,10 +315,7 @@
 	assert(device);
 
-	hcd_t *hcd = dev_to_hcd(device);
+	hc_device_t *hcd = dev_to_hcd(device);
 	assert(hcd);
 	assert(hcd->bus);
-
-	hc_dev_t *hc_dev = dev_to_hc_dev(device);
-	assert(hc_dev);
 
 	fibril_mutex_lock(&hub->guard);
@@ -374,8 +349,8 @@
 }
 
-device_t *hcd_ddf_device_create(ddf_dev_t *hc, bus_t *bus)
+device_t *hcd_ddf_fun_create(hc_device_t *hc)
 {
 	/* Create DDF function for the new device */
-	ddf_fun_t *fun = ddf_fun_create(hc, fun_inner, NULL);
+	ddf_fun_t *fun = ddf_fun_create(hc->ddf_dev, fun_inner, NULL);
 	if (!fun)
 		return NULL;
@@ -384,5 +359,5 @@
 
 	/* Create USB device node for the new device */
-	device_t *dev = ddf_fun_data_alloc(fun, bus->device_size);
+	device_t *dev = ddf_fun_data_alloc(fun, hc->bus->device_size);
 	if (!dev) {
 		ddf_fun_destroy(fun);
@@ -390,10 +365,10 @@
 	}
 
-	bus_device_init(dev, bus);
+	bus_device_init(dev, hc->bus);
 	dev->fun = fun;
 	return dev;
 }
 
-void hcd_ddf_device_destroy(device_t *dev)
+void hcd_ddf_fun_destroy(device_t *dev)
 {
 	assert(dev);
@@ -402,5 +377,5 @@
 }
 
-int hcd_ddf_device_explore(device_t *device)
+int hcd_device_explore(device_t *device)
 {
 	int err;
@@ -421,5 +396,5 @@
 	usb_log_debug("Device(%d): Requesting full device descriptor.",
 	    device->address);
-	ssize_t got = hcd_send_batch_sync(device->bus->hcd, device, control_ep, USB_DIRECTION_IN,
+	ssize_t got = bus_device_send_batch_sync(device, control_ep, USB_DIRECTION_IN,
 	    (char *) &desc, sizeof(desc), *(uint64_t *)&get_device_desc,
 	    "read device descriptor");
@@ -447,33 +422,5 @@
 }
 
-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_device_online(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_device_offline(dev);
-}
-
-static int hcd_ddf_new_device(hcd_t *hcd, ddf_dev_t *hc, device_t *hub, unsigned port)
+static int hcd_ddf_new_device(hc_device_t *hcd, ddf_dev_t *hc, device_t *hub, unsigned port)
 {
 	int err;
@@ -483,5 +430,5 @@
 	assert(hc);
 
-	device_t *dev = hcd_ddf_device_create(hc, hcd->bus);
+	device_t *dev = hcd_ddf_fun_create(hcd);
 	if (!dev) {
 		usb_log_error("Failed to create USB device function.");
@@ -516,5 +463,5 @@
 
 err_usb_dev:
-	hcd_ddf_device_destroy(dev);
+	hcd_ddf_fun_destroy(dev);
 	return err;
 }
@@ -525,11 +472,9 @@
  * @return Error code
  */
-int hcd_setup_virtual_root_hub(hcd_t *hcd, ddf_dev_t *hc)
+int hcd_setup_virtual_root_hub(hc_device_t *hcd)
 {
 	int err;
 
-	assert(hc);
-	assert(hcd);
-	assert(hcd->bus);
+	assert(hcd);
 
 	if ((err = bus_reserve_default_address(hcd->bus, USB_SPEED_MAX))) {
@@ -538,5 +483,5 @@
 	}
 
-	device_t *dev = hcd_ddf_device_create(hc, hcd->bus);
+	device_t *dev = hcd_ddf_fun_create(hcd);
 	if (!dev) {
 		usb_log_error("Failed to create function for the root hub.");
@@ -561,5 +506,5 @@
 
 err_usb_dev:
-	hcd_ddf_device_destroy(dev);
+	hcd_ddf_fun_destroy(dev);
 err_default_address:
 	bus_release_default_address(hcd->bus);
@@ -577,14 +522,14 @@
  * This function does all the ddf work for hc driver.
  */
-int hcd_ddf_setup_hc(ddf_dev_t *device)
+int hcd_ddf_setup_hc(ddf_dev_t *device, size_t size)
 {
 	assert(device);
 
-	hc_dev_t *instance = ddf_dev_data_alloc(device, sizeof(hc_dev_t));
+	hc_device_t *instance = ddf_dev_data_alloc(device, size);
 	if (instance == NULL) {
 		usb_log_error("Failed to allocate HCD ddf structure.\n");
 		return ENOMEM;
 	}
-	hcd_init(&instance->hcd);
+	instance->ddf_dev = device;
 
 	int ret = ENOMEM;
@@ -618,15 +563,10 @@
 }
 
-void hcd_ddf_clean_hc(ddf_dev_t *device)
-{
-	assert(device);
-	hc_dev_t *hc = dev_to_hc_dev(device);
-	assert(hc);
-	const int ret = ddf_fun_unbind(hc->ctl_fun);
-	if (ret == EOK)
-		ddf_fun_destroy(hc->ctl_fun);
-}
-
-//TODO: Cache parent session in HCD
+void hcd_ddf_clean_hc(hc_device_t *hcd)
+{
+	if (ddf_fun_unbind(hcd->ctl_fun) == EOK)
+		ddf_fun_destroy(hcd->ctl_fun);
+}
+
 /** Call the parent driver with a request to enable interrupt
  *
@@ -635,7 +575,7 @@
  * @return Error code.
  */
-int hcd_ddf_enable_interrupt(ddf_dev_t *device, int inum)
-{
-	async_sess_t *parent_sess = ddf_dev_parent_sess_get(device);
+int hcd_ddf_enable_interrupt(hc_device_t *hcd, int inum)
+{
+	async_sess_t *parent_sess = ddf_dev_parent_sess_get(hcd->ddf_dev);
 	if (parent_sess == NULL)
 		return EIO;
@@ -644,8 +584,7 @@
 }
 
-//TODO: Cache parent session in HCD
-int hcd_ddf_get_registers(ddf_dev_t *device, hw_res_list_parsed_t *hw_res)
-{
-	async_sess_t *parent_sess = ddf_dev_parent_sess_get(device);
+int hcd_ddf_get_registers(hc_device_t *hcd, hw_res_list_parsed_t *hw_res)
+{
+	async_sess_t *parent_sess = ddf_dev_parent_sess_get(hcd->ddf_dev);
 	if (parent_sess == NULL)
 		return EIO;
@@ -658,229 +597,4 @@
 }
 
-// TODO: move this someplace else
-static inline void irq_code_clean(irq_code_t *code)
-{
-	if (code) {
-		free(code->ranges);
-		free(code->cmds);
-		code->ranges = NULL;
-		code->cmds = NULL;
-		code->rangecount = 0;
-		code->cmdcount = 0;
-	}
-}
-
-/** Register interrupt handler
- *
- * @param[in] device Host controller DDF device
- * @param[in] regs Register range
- * @param[in] irq Interrupt number
- * @paran[in] handler Interrupt handler
- * @param[in] gen_irq_code IRQ code generator.
- *
- * @return IRQ capability handle on success.
- * @return Negative error code.
- */
-int hcd_ddf_setup_interrupts(ddf_dev_t *device,
-    const hw_res_list_parsed_t *hw_res,
-    interrupt_handler_t handler,
-    irq_code_gen_t gen_irq_code)
-{
-	assert(device);
-
-	hcd_t *hcd = dev_to_hcd(device);
-
-	if (!handler || !gen_irq_code)
-		return ENOTSUP;
-
-	irq_code_t irq_code = {0};
-
-	const int irq = gen_irq_code(&irq_code, hcd, hw_res);
-	if (irq < 0) {
-		usb_log_error("Failed to generate IRQ code: %s.\n",
-		    str_error(irq));
-		return irq;
-	}
-
-	/* Register handler to avoid interrupt lockup */
-	const int irq_cap = register_interrupt_handler(device, irq, handler,
-	    &irq_code);
-	irq_code_clean(&irq_code);
-	if (irq_cap < 0) {
-		usb_log_error("Failed to register interrupt handler: %s.\n",
-		    str_error(irq_cap));
-		return irq_cap;
-	}
-
-	/* Enable interrupts */
-	int ret = hcd_ddf_enable_interrupt(device, irq);
-	if (ret != EOK) {
-		usb_log_error("Failed to enable interrupts: %s.\n",
-		    str_error(ret));
-		unregister_interrupt_handler(device, irq_cap);
-		return ret;
-	}
-	return irq_cap;
-}
-
-/** IRQ handling callback, forward status from call to diver structure.
- *
- * @param[in] dev DDF instance of the device to use.
- * @param[in] iid (Unused).
- * @param[in] call Pointer to the call from kernel.
- */
-void ddf_hcd_gen_irq_handler(ipc_callid_t iid, ipc_call_t *call, ddf_dev_t *dev)
-{
-	assert(dev);
-	hcd_t *hcd = dev_to_hcd(dev);
-	if (!hcd || !hcd->ops.irq_hook) {
-		usb_log_error("Interrupt on not yet initialized device.\n");
-		return;
-	}
-	const uint32_t status = IPC_GET_ARG1(*call);
-	hcd->ops.irq_hook(hcd, status);
-}
-
-static int interrupt_polling(void *arg)
-{
-	hcd_t *hcd = arg;
-	assert(hcd);
-	if (!hcd->ops.status_hook || !hcd->ops.irq_hook)
-		return ENOTSUP;
-	uint32_t status = 0;
-	while (hcd->ops.status_hook(hcd, &status) == EOK) {
-		hcd->ops.irq_hook(hcd, status);
-		status = 0;
-		/* We should wait 1 frame - 1ms here, but this polling is a
-		 * lame crutch anyway so don't hog the system. 10ms is still
-		 * good enough for emergency mode */
-		async_usleep(10000);
-	}
-	return EOK;
-}
-
-/** Initialize hc and rh DDF structures and their respective drivers.
- *
- * @param device DDF instance of the device to use
- * @param speed Maximum supported speed
- * @param bw Available bandwidth (arbitrary units)
- * @param bw_count Bandwidth computing function
- * @param irq_handler IRQ handling function
- * @param gen_irq_code Function to generate IRQ pseudocode
- *                     (it needs to return used irq number)
- * @param driver_init Function to initialize HC driver
- * @param driver_fini Function to cleanup HC driver
- * @return Error code
- *
- * This function does all the preparatory work for hc and rh drivers:
- *  - gets device's hw resources
- *  - attempts to enable interrupts
- *  - registers interrupt handler
- *  - calls driver specific initialization
- *  - registers root hub
- */
-int hcd_ddf_add_hc(ddf_dev_t *device, const ddf_hc_driver_t *driver)
-{
-	assert(driver);
-
-	int ret = EOK;
-
-	hw_res_list_parsed_t hw_res;
-	ret = hcd_ddf_get_registers(device, &hw_res);
-	if (ret != EOK) {
-		usb_log_error("Failed to get register memory addresses "
-		    "for `%s': %s.\n", ddf_dev_get_name(device),
-		    str_error(ret));
-		return ret;
-	}
-
-	ret = hcd_ddf_setup_hc(device);
-	if (ret != EOK) {
-		usb_log_error("Failed to setup generic HCD.\n");
-		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;
-	const int irq_cap = hcd_ddf_setup_interrupts(device, &hw_res,
-	    irq_handler, driver->irq_code_gen);
-	bool irqs_enabled = !(irq_cap < 0);
-	if (irqs_enabled) {
-		usb_log_debug("Hw interrupts enabled.\n");
-	}
-
-	/* Claim the device from BIOS */
-	if (driver->claim)
-		ret = driver->claim(hcd, device);
-	if (ret != EOK) {
-		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 start HCD: %s.\n", str_error(ret));
-		goto err_irq;
-	}
-
-	/* Need working irq replacement to setup root hub */
-	if (!irqs_enabled && hcd->ops.status_hook) {
-		hcd->polling_fibril = fibril_create(interrupt_polling, hcd);
-		if (hcd->polling_fibril == 0) {
-			usb_log_error("Failed to create polling fibril\n");
-			ret = ENOMEM;
-			goto err_started;
-		}
-		fibril_add_ready(hcd->polling_fibril);
-		usb_log_warning("Failed to enable interrupts: %s."
-		    " Falling back to polling.\n", str_error(irq_cap));
-	}
-
-	/*
-	 * Creating root hub registers a new USB device so HC
-	 * needs to be ready at this time.
-	 */
-	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));
-		goto err_polling;
-	}
-
-	usb_log_info("Controlling new `%s' device `%s'.\n",
-	    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 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/lib/usbhost/src/endpoint.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -39,4 +39,7 @@
 #include <mem.h>
 #include <stdlib.h>
+#include <str_error.h>
+#include <usb/debug.h>
+#include <usb/host/hcd.h>
 
 #include "usb_transfer_batch.h"
@@ -187,4 +190,65 @@
 }
 
+/** Prepare generic usb_transfer_batch and schedule it.
+ * @param ep Endpoint for which the batch shall be created.
+ * @param target address and endpoint number.
+ * @param setup_data Data to use in setup stage (Control communication type)
+ * @param in Callback for device to host communication.
+ * @param out Callback for host to device communication.
+ * @param arg Callback parameter.
+ * @param name Communication identifier (for nicer output).
+ * @return Error code.
+ */
+int endpoint_send_batch(endpoint_t *ep, 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)
+{
+	usb_log_debug2("%s %d:%d %zu(%zu).\n",
+	    name, target.address, target.endpoint, size, ep->max_packet_size);
+
+	bus_t *bus = endpoint_get_bus(ep);
+	const bus_ops_t *ops = BUS_OPS_LOOKUP(bus->ops, batch_schedule);
+	if (!ops) {
+		usb_log_error("HCD does not implement scheduler.\n");
+		return ENOTSUP;
+	}
+
+	const size_t bw = endpoint_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",
+		    ep->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");
+		return ENOMEM;
+	}
+
+	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
+			= hcd_get_request_toggle_reset_mode(&batch->setup.packet);
+
+	const int ret = ops->batch_schedule(batch);
+	if (ret != EOK) {
+		usb_log_warning("Batch %p failed to schedule: %s", batch, str_error(ret));
+		usb_transfer_batch_destroy(batch);
+	}
+
+	return ret;
+}
+
 /**
  * @}
Index: uspace/lib/usbhost/src/hcd.c
===================================================================
--- uspace/lib/usbhost/src/hcd.c	(revision 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/lib/usbhost/src/hcd.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -36,4 +36,5 @@
 #include <assert.h>
 #include <async.h>
+#include <ddf/interrupt.h>
 #include <errno.h>
 #include <macros.h>
@@ -45,4 +46,5 @@
 
 #include "bus.h"
+#include "ddf_helpers.h"
 #include "endpoint.h"
 #include "usb_transfer_batch.h"
@@ -50,17 +52,307 @@
 #include "hcd.h"
 
-
-/** Initialize hcd_t structure.
- * Initializes device and endpoint managers. Sets data and hook pointer to NULL.
- *
- * @param hcd hcd_t structure to initialize, non-null.
- * @param max_speed Maximum supported USB speed (full, high).
- * @param bandwidth Available bandwidth, passed to endpoint manager.
- * @param bw_count Bandwidth compute function, passed to endpoint manager.
- */
-void hcd_init(hcd_t *hcd) {
+int hc_dev_add(ddf_dev_t *);
+int hc_dev_remove(ddf_dev_t *);
+int hc_dev_gone(ddf_dev_t *);
+int hc_fun_online(ddf_fun_t *);
+int hc_fun_offline(ddf_fun_t *);
+
+static driver_ops_t hc_driver_ops = {
+	.dev_add = hc_dev_add,
+	.dev_remove = hc_dev_remove,
+	.dev_gone = hc_dev_gone,
+	.fun_online = hc_fun_offline,
+	.fun_offline = hc_fun_offline,
+};
+
+static const hc_driver_t *hc_driver;
+
+int hc_driver_main(const hc_driver_t *driver)
+{
+	driver_t ddf_driver = {
+		.name = driver->name,
+		.driver_ops = &hc_driver_ops,
+	};
+
+	/* Remember ops to call. */
+	hc_driver = driver;
+
+	return ddf_driver_main(&ddf_driver);
+}
+
+/** IRQ handling callback, forward status from call to diver structure.
+ *
+ * @param[in] dev DDF instance of the device to use.
+ * @param[in] iid (Unused).
+ * @param[in] call Pointer to the call from kernel.
+ */
+static void irq_handler(ipc_callid_t iid, ipc_call_t *call, ddf_dev_t *dev)
+{
+	assert(dev);
+	hc_device_t *hcd = dev_to_hcd(dev);
+
+	const bus_ops_t *ops = BUS_OPS_LOOKUP(hcd->bus->ops, interrupt);
+	assert(ops);
+
+	const uint32_t status = IPC_GET_ARG1(*call);
+	ops->interrupt(hcd->bus, status);
+}
+
+/** Worker for the HW interrupt replacement fibril.
+ */
+static int interrupt_polling(void *arg)
+{
+	hc_device_t *hcd = arg;
 	assert(hcd);
-
-	hcd_set_implementation(hcd, NULL, NULL, NULL);
+	bus_t *bus = hcd->bus;
+
+	const bus_ops_t *interrupt_ops = BUS_OPS_LOOKUP(bus->ops, interrupt);
+	const bus_ops_t *status_ops = BUS_OPS_LOOKUP(bus->ops, status);
+	if (!interrupt_ops || !status_ops)
+		return ENOTSUP;
+
+	uint32_t status = 0;
+	while (status_ops->status(bus, &status) == EOK) {
+		interrupt_ops->interrupt(bus, status);
+		status = 0;
+		/* We should wait 1 frame - 1ms here, but this polling is a
+		 * lame crutch anyway so don't hog the system. 10ms is still
+		 * good enough for emergency mode */
+		async_usleep(10000);
+	}
+	return EOK;
+}
+
+static inline void irq_code_clean(irq_code_t *code)
+{
+	if (code) {
+		free(code->ranges);
+		free(code->cmds);
+		code->ranges = NULL;
+		code->cmds = NULL;
+		code->rangecount = 0;
+		code->cmdcount = 0;
+	}
+}
+
+/** Register interrupt handler
+ *
+ * @param[in] device Host controller DDF device
+ * @param[in] regs Register range
+ * @param[in] irq Interrupt number
+ * @paran[in] handler Interrupt handler
+ * @param[in] gen_irq_code IRQ code generator.
+ *
+ * @return IRQ capability handle on success.
+ * @return Negative error code.
+ */
+static int hcd_ddf_setup_interrupts(hc_device_t *hcd, const hw_res_list_parsed_t *hw_res)
+{
+	assert(hcd);
+	irq_code_t irq_code = {0};
+
+	if (!hc_driver->irq_code_gen)
+		return ENOTSUP;
+
+	const int irq = hc_driver->irq_code_gen(&irq_code, hcd, hw_res);
+	if (irq < 0) {
+		usb_log_error("Failed to generate IRQ code: %s.\n",
+		    str_error(irq));
+		return irq;
+	}
+
+	/* Register handler to avoid interrupt lockup */
+	const int irq_cap = register_interrupt_handler(hcd->ddf_dev, irq, irq_handler, &irq_code);
+	irq_code_clean(&irq_code);
+	if (irq_cap < 0) {
+		usb_log_error("Failed to register interrupt handler: %s.\n",
+		    str_error(irq_cap));
+		return irq_cap;
+	}
+
+	/* Enable interrupts */
+	int ret = hcd_ddf_enable_interrupt(hcd, irq);
+	if (ret != EOK) {
+		usb_log_error("Failed to enable interrupts: %s.\n",
+		    str_error(ret));
+		unregister_interrupt_handler(hcd->ddf_dev, irq_cap);
+		return ret;
+	}
+	return irq_cap;
+}
+
+/** Initialize HC in memory of the driver.
+ *
+ * @param device DDF instance of the device to use
+ * @return Error code
+ *
+ * This function does all the preparatory work for hc and rh drivers:
+ *  - gets device's hw resources
+ *  - attempts to enable interrupts
+ *  - registers interrupt handler
+ *  - calls driver specific initialization
+ *  - registers root hub
+ */
+int hc_dev_add(ddf_dev_t *device)
+{
+	int ret = EOK;
+	assert(device);
+
+	if (!hc_driver->hc_add) {
+		usb_log_error("Driver '%s' does not support adding devices.", hc_driver->name);
+		return ENOTSUP;
+	}
+
+	ret = hcd_ddf_setup_hc(device, hc_driver->hc_device_size);
+	if (ret != EOK) {
+		usb_log_error("Failed to setup HC device.\n");
+		return ret;
+	}
+
+	hc_device_t *hcd = dev_to_hcd(device);
+
+	hw_res_list_parsed_t hw_res;
+	ret = hcd_ddf_get_registers(hcd, &hw_res);
+	if (ret != EOK) {
+		usb_log_error("Failed to get register memory addresses "
+		    "for `%s': %s.\n", ddf_dev_get_name(device),
+		    str_error(ret));
+		goto err_hcd;
+	}
+
+	ret = hc_driver->hc_add(hcd, &hw_res);
+	if (ret != EOK) {
+		usb_log_error("Failed to init HCD.\n");
+		goto err_hw_res;
+	}
+
+	assert(hcd->bus);
+
+	/* Setup interrupts  */
+	hcd->irq_cap = hcd_ddf_setup_interrupts(hcd, &hw_res);
+	if (hcd->irq_cap >= 0) {
+		usb_log_debug("Hw interrupts enabled.\n");
+	}
+
+	/* Claim the device from BIOS */
+	if (hc_driver->claim)
+		ret = hc_driver->claim(hcd);
+	if (ret != EOK) {
+		usb_log_error("Failed to claim `%s' for `%s': %s",
+		    ddf_dev_get_name(device), hc_driver->name, str_error(ret));
+		goto err_irq;
+	}
+
+	/* Start hw */
+	if (hc_driver->start)
+		ret = hc_driver->start(hcd);
+	if (ret != EOK) {
+		usb_log_error("Failed to start HCD: %s.\n", str_error(ret));
+		goto err_irq;
+	}
+
+	const bus_ops_t *ops = BUS_OPS_LOOKUP(hcd->bus->ops, status);
+
+	/* Need working irq replacement to setup root hub */
+	if (hcd->irq_cap < 0 && ops) {
+		hcd->polling_fibril = fibril_create(interrupt_polling, hcd->bus);
+		if (!hcd->polling_fibril) {
+			usb_log_error("Failed to create polling fibril\n");
+			ret = ENOMEM;
+			goto err_started;
+		}
+		fibril_add_ready(hcd->polling_fibril);
+		usb_log_warning("Failed to enable interrupts: %s."
+		    " Falling back to polling.\n", str_error(hcd->irq_cap));
+	}
+
+	/*
+	 * Creating root hub registers a new USB device so HC
+	 * needs to be ready at this time.
+	 */
+	if (hc_driver->setup_root_hub)
+		ret = hc_driver->setup_root_hub(hcd);
+	if (ret != EOK) {
+		usb_log_error("Failed to setup HC root hub: %s.\n",
+		    str_error(ret));
+		goto err_polling;
+	}
+
+	usb_log_info("Controlling new `%s' device `%s'.\n",
+	   hc_driver->name, ddf_dev_get_name(device));
+	return EOK;
+
+err_polling:
+	// TODO: Stop the polling fibril (refactor the interrupt_polling func)
+	//
+err_started:
+	if (hc_driver->stop)
+		hc_driver->stop(hcd);
+err_irq:
+	unregister_interrupt_handler(device, hcd->irq_cap);
+	if (hc_driver->hc_remove)
+		hc_driver->hc_remove(hcd);
+err_hw_res:
+	hw_res_list_parsed_clean(&hw_res);
+err_hcd:
+	hcd_ddf_clean_hc(hcd);
+	return ret;
+}
+
+int hc_dev_remove(ddf_dev_t *dev)
+{
+	int err;
+	hc_device_t *hcd = dev_to_hcd(dev);
+
+	if (hc_driver->stop)
+		if ((err = hc_driver->stop(hcd)))
+			return err;
+
+	unregister_interrupt_handler(dev, hcd->irq_cap);
+
+	if (hc_driver->hc_remove)
+		if ((err = hc_driver->hc_remove(hcd)))
+			return err;
+
+	hcd_ddf_clean_hc(hcd);
+
+	// TODO probably not complete
+
+	return EOK;
+}
+
+int hc_dev_gone(ddf_dev_t *dev)
+{
+	int err = ENOTSUP;
+	hc_device_t *hcd = dev_to_hcd(dev);
+
+	if (hc_driver->hc_gone)
+		err = hc_driver->hc_gone(hcd);
+
+	hcd_ddf_clean_hc(hcd);
+
+	return err;
+}
+
+int hc_fun_online(ddf_fun_t *fun)
+{
+	assert(fun);
+
+	device_t *dev = ddf_fun_data_get(fun);
+	assert(dev);
+
+	usb_log_info("Device(%d): Requested to be brought online.", dev->address);
+	return bus_device_online(dev);
+}
+
+int hc_fun_offline(ddf_fun_t *fun)
+{
+	assert(fun);
+
+	device_t *dev = ddf_fun_data_get(fun);
+	assert(dev);
+
+	usb_log_info("Device(%d): Requested to be taken offline.", dev->address);
+	return bus_device_offline(dev);
 }
 
@@ -72,5 +364,5 @@
  * @return Max packet size for EP 0
  */
-int hcd_get_ep0_max_packet_size(uint16_t *mps, hcd_t *hcd, device_t *dev)
+int hcd_get_ep0_max_packet_size(uint16_t *mps, bus_t *bus, device_t *dev)
 {
 	assert(mps);
@@ -97,5 +389,5 @@
 
 	usb_log_debug("Requesting first 8B of device descriptor to determine MPS.");
-	ssize_t got = hcd_send_batch_sync(hcd, dev, control_ep, USB_DIRECTION_IN,
+	ssize_t got = bus_device_send_batch_sync(dev, control_ep, USB_DIRECTION_IN,
 	    (char *) &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8,
 	    "read first 8 bytes of dev descriptor");
@@ -156,5 +448,5 @@
  *
  */
-static toggle_reset_mode_t hcd_get_request_toggle_reset_mode(
+toggle_reset_mode_t hcd_get_request_toggle_reset_mode(
     const usb_device_request_setup_packet_t *request)
 {
@@ -186,126 +478,4 @@
 }
 
-/** Prepare generic usb_transfer_batch and schedule it.
- * @param hcd Host controller driver.
- * @param target address and endpoint number.
- * @param setup_data Data to use in setup stage (Control communication type)
- * @param in Callback for device to host communication.
- * @param out Callback for host to device communication.
- * @param arg Callback parameter.
- * @param name Communication identifier (for nicer output).
- * @return Error code.
- */
-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);
-
-	if (!hcd->ops.schedule) {
-		usb_log_error("HCD does not implement scheduler.\n");
-		return ENOTSUP;
-	}
-
-	endpoint_t *ep = bus_find_endpoint(device, target, direction);
-	if (ep == NULL) {
-		usb_log_error("Endpoint(%d:%d) not registered for %s.\n",
-		    device->address, target.endpoint, name);
-		return ENOENT;
-	}
-
-	// TODO cut here aka provide helper to call with instance of endpoint_t in hand
-	assert(ep->device == device);
-
-	usb_log_debug2("%s %d:%d %zu(%zu).\n",
-	    name, target.address, target.endpoint, size, ep->max_packet_size);
-
-	const size_t bw = endpoint_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");
-		return ENOMEM;
-	}
-
-	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
-			= hcd_get_request_toggle_reset_mode(&batch->setup.packet);
-
-	const int ret = hcd->ops.schedule(hcd, batch);
-	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. */
-	endpoint_del_ref(ep);
-
-	return ret;
-}
-
-typedef struct {
-	fibril_mutex_t done_mtx;
-	fibril_condvar_t done_cv;
-	unsigned done;
-
-	size_t transfered_size;
-	int error;
-} sync_data_t;
-
-static int sync_transfer_complete(void *arg, int error, size_t transfered_size)
-{
-	sync_data_t *d = arg;
-	assert(d);
-	d->transfered_size = transfered_size;
-	d->error = error;
-	fibril_mutex_lock(&d->done_mtx);
-	d->done = 1;
-	fibril_condvar_broadcast(&d->done_cv);
-	fibril_mutex_unlock(&d->done_mtx);
-	return EOK;
-}
-
-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 };
-	fibril_mutex_initialize(&sd.done_mtx);
-	fibril_condvar_initialize(&sd.done_cv);
-
-	const int ret = hcd_send_batch(hcd, device, target, direction,
-	    data, size, setup_data,
-	    sync_transfer_complete, &sd, name);
-	if (ret != EOK)
-		return ret;
-
-	fibril_mutex_lock(&sd.done_mtx);
-	while (!sd.done)
-		fibril_condvar_wait(&sd.done_cv, &sd.done_mtx);
-	fibril_mutex_unlock(&sd.done_mtx);
-
-	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 1ea0bbf904b247218cfae78b7d3a61582f2a159a)
+++ uspace/lib/usbhost/src/usb2_bus.c	(revision 32fb6bcec95f3a7fbb1b2fdb6528f0e04726a6b6)
@@ -205,5 +205,4 @@
 
 	usb2_bus_t *bus = (usb2_bus_t *) dev->bus;
-	hcd_t *hcd = (hcd_t *) bus->base.hcd;
 
 	/* The default address is currently reserved for this device */
@@ -231,5 +230,5 @@
 
 	uint16_t max_packet_size;
-	if ((err = hcd_get_ep0_max_packet_size(&max_packet_size, hcd, dev)))
+	if ((err = hcd_get_ep0_max_packet_size(&max_packet_size, &bus->base, dev)))
 		goto err_address;
 
@@ -238,5 +237,5 @@
 
 	usb_log_debug("Device(%d): Setting USB address.", address);
-	err = hcd_send_batch_sync(hcd, dev, usb2_default_target, USB_DIRECTION_OUT,
+	err = bus_device_send_batch_sync(dev, usb2_default_target, USB_DIRECTION_OUT,
 	    NULL, 0, *(uint64_t *)&set_address, "set address");
 	if (err != 0) {
@@ -315,5 +314,5 @@
 
 	/* Read the device descriptor, derive the match ids */
-	if ((err = hcd_ddf_device_explore(dev))) {
+	if ((err = hcd_device_explore(dev))) {
 		usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
 		release_address(bus, dev->address);
@@ -463,9 +462,9 @@
  * @return Error code.
  */
-int usb2_bus_init(usb2_bus_t *bus, hcd_t *hcd, size_t available_bandwidth)
+int usb2_bus_init(usb2_bus_t *bus, size_t available_bandwidth)
 {
 	assert(bus);
 
-	bus_init(&bus->base, hcd, sizeof(device_t));
+	bus_init(&bus->base, sizeof(device_t));
 	bus->base.ops = &usb2_bus_ops;
 
