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;
 }
