Index: uspace/drv/bus/usb/xhci/debug.c
===================================================================
--- uspace/drv/bus/usb/xhci/debug.c	(revision 62ba2cbee5c3230302472cecb3778a285cb40cd4)
+++ uspace/drv/bus/usb/xhci/debug.c	(revision cb89430129f9d8d3b93b48217d53165f33b1774f)
@@ -37,4 +37,5 @@
 #include <usb/debug.h>
 
+#include "hw_struct/trb.h"
 #include "debug.h"
 #include "hc.h"
@@ -66,6 +67,9 @@
 	DUMP_REG(cap, XHCI_CAP_VERSION);
 	DUMP_REG(cap, XHCI_CAP_MAX_SLOTS);
+	DUMP_REG(cap, XHCI_CAP_MAX_INTRS);
+	DUMP_REG(cap, XHCI_CAP_MAX_PORTS);
 	DUMP_REG(cap, XHCI_CAP_IST);
 	DUMP_REG(cap, XHCI_CAP_ERST_MAX);
+	usb_log_debug2(PX "%u", "Max Scratchpad bufs", xhci_get_max_spbuf(cap));
 	DUMP_REG(cap, XHCI_CAP_SPR);
 	DUMP_REG(cap, XHCI_CAP_U1EL);
@@ -124,5 +128,4 @@
 	usb_log_debug2("Operational registers:");
 
-	DUMP_REG(hc->op_regs, XHCI_OP_RS);
 	DUMP_REG(hc->op_regs, XHCI_OP_RS);
 	DUMP_REG(hc->op_regs, XHCI_OP_HCRST);
@@ -152,5 +155,22 @@
 	DUMP_REG(hc->op_regs, XHCI_OP_CRCR_LO);
 	DUMP_REG(hc->op_regs, XHCI_OP_CRCR_HI);
-
+	DUMP_REG(hc->op_regs, XHCI_OP_DCBAAP_LO);
+	DUMP_REG(hc->op_regs, XHCI_OP_DCBAAP_HI);
+	DUMP_REG(hc->rt_regs, XHCI_RT_MFINDEX);
+
+	usb_log_debug2("Interrupter 0 state:");
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_IP);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_IE);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_IMI);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_IMC);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_ERSTSZ);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_ERSTBA_LO);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_ERSTBA_HI);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_ERDP_LO);
+	DUMP_REG(&hc->rt_regs->ir[0], XHCI_INTR_ERDP_HI);
+}
+
+void xhci_dump_ports(xhci_hc_t *hc)
+{
 	const size_t num_ports = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PORTS);
 	for (size_t i = 0; i < num_ports; i++) {
@@ -160,4 +180,59 @@
 	}
 }
+
+static const char *trb_types [] = {
+	[0] = "<empty>",
+#define TRB(t) [XHCI_TRB_TYPE_##t] = #t
+	TRB(NORMAL),
+	TRB(SETUP_STAGE),
+	TRB(DATA_STAGE),
+	TRB(STATUS_STAGE),
+	TRB(ISOCH),
+	TRB(LINK),
+	TRB(EVENT_DATA),
+	TRB(NO_OP),
+	TRB(ENABLE_SLOT_CMD),
+	TRB(DISABLE_SLOT_CMD),
+	TRB(ADDRESS_DEVICE_CMD),
+	TRB(CONFIGURE_ENDPOINT_CMD),
+	TRB(EVALUATE_CONTEXT_CMD),
+	TRB(RESET_ENDPOINT_CMD),
+	TRB(STOP_ENDPOINT_CMD),
+	TRB(SET_TR_DEQUEUE_POINTER_CMD),
+	TRB(RESET_DEVICE_CMD),
+	TRB(FORCE_EVENT_CMD),
+	TRB(NEGOTIATE_BANDWIDTH_CMD),
+	TRB(SET_LATENCY_TOLERANCE_VALUE_CMD),
+	TRB(GET_PORT_BANDWIDTH_CMD),
+	TRB(FORCE_HEADER_CMD),
+	TRB(NO_OP_CMD),
+	TRB(TRANSFER_EVENT),
+	TRB(COMMAND_COMPLETION_EVENT),
+	TRB(PORT_STATUS_CHANGE_EVENT),
+	TRB(BANDWIDTH_REQUEST_EVENT),
+	TRB(DOORBELL_EVENT),
+	TRB(HOST_CONTROLLER_EVENT),
+	TRB(DEVICE_NOTIFICATION_EVENT),
+	TRB(MFINDEX_WRAP_EVENT),
+#undef TRB
+	[XHCI_TRB_TYPE_MAX] = NULL,
+};
+
+const char *xhci_trb_str_type(unsigned type)
+{
+	static char type_buf [20];
+
+	if (type < XHCI_TRB_TYPE_MAX && trb_types[type] != NULL)
+		return trb_types[type];
+
+	snprintf(type_buf, sizeof(type_buf), "<unknown (%u)>", type);
+	return type_buf;
+}
+
+void xhci_dump_trb(xhci_trb_t *trb)
+{
+	usb_log_debug2("TRB(%p): type %s, cycle %u", trb, xhci_trb_str_type(TRB_TYPE(*trb)), TRB_CYCLE(*trb));
+}
+
 /**
  * @}
Index: uspace/drv/bus/usb/xhci/debug.h
===================================================================
--- uspace/drv/bus/usb/xhci/debug.h	(revision 62ba2cbee5c3230302472cecb3778a285cb40cd4)
+++ uspace/drv/bus/usb/xhci/debug.h	(revision cb89430129f9d8d3b93b48217d53165f33b1774f)
@@ -18,5 +18,4 @@
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
@@ -38,11 +37,16 @@
 #define XHCI_DEBUG_H
 
-#include "hw_struct/regs.h"
+struct xhci_hc;
+struct xhci_cap_regs;
+struct xhci_port_regs;
+struct xhci_trb;
 
-typedef struct xhci_hc xhci_hc_t;
+void xhci_dump_cap_regs(const struct xhci_cap_regs *);
+void xhci_dump_port(struct xhci_port_regs *);
+void xhci_dump_state(struct xhci_hc *);
+void xhci_dump_ports(struct xhci_hc *);
 
-void xhci_dump_cap_regs(xhci_cap_regs_t *);
-void xhci_dump_port(xhci_port_regs_t *);
-void xhci_dump_state(xhci_hc_t *);
+const char *xhci_trb_str_type(unsigned);
+void xhci_dump_trb(struct xhci_trb *trb);
 
 #endif
Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 62ba2cbee5c3230302472cecb3778a285cb40cd4)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision cb89430129f9d8d3b93b48217d53165f33b1774f)
@@ -35,32 +35,185 @@
 
 #include <errno.h>
+#include <str_error.h>
 #include <usb/debug.h>
 #include <usb/host/ddf_helpers.h>
+#include <usb/host/utils/malloc32.h>
 #include "debug.h"
 #include "hc.h"
-
-const ddf_hc_driver_t xhci_ddf_hc_driver = {
-	.hc_speed = USB_SPEED_SUPER,
-	.init = xhci_hc_init,
-	.fini = xhci_hc_fini,
-	.name = "XHCI-PCI",
-	.ops = {
-		.schedule       = xhci_hc_schedule,
-		.irq_hook       = xhci_hc_interrupt,
-		.status_hook    = xhci_hc_status,
+#include "hw_struct/trb.h"
+
+static const irq_cmd_t irq_commands[] = {
+	{
+		.cmd = CMD_PIO_READ_32,
+		.dstarg = 1,
+		.addr = NULL
+	},
+	{
+		.cmd = CMD_AND,
+		.srcarg = 1,
+		.dstarg = 2,
+		.value = 0
+	},
+	{
+		.cmd = CMD_PREDICATE,
+		.srcarg = 2,
+		.value = 2
+	},
+	{
+		.cmd = CMD_PIO_WRITE_A_32,
+		.srcarg = 1,
+		.addr = NULL
+	},
+	{
+		.cmd = CMD_ACCEPT
 	}
 };
 
-int xhci_hc_init(hcd_t *hcd, const hw_res_list_parsed_t *hw_res, bool irq)
+/**
+ * Generates code to accept interrupts. The xHCI is designed primarily for
+ * MSI/MSI-X, but we use PCI Interrupt Pin. In this mode, all the Interrupters
+ * (except 0) are disabled.
+ */
+static int hc_gen_irq_code(irq_code_t *code, const hw_res_list_parsed_t *hw_res)
 {
 	int err;
 
+	assert(code);
+	assert(hw_res);
+
+	if (hw_res->irqs.count != 1 || hw_res->mem_ranges.count != 1) {
+		usb_log_info("Unexpected HW resources to enable interrupts.");
+		return EINVAL;
+	}
+
+	addr_range_t mmio_range = hw_res->mem_ranges.ranges[0];
+
+	if (RNGSZ(mmio_range) < sizeof(xhci_cap_regs_t))
+		return EOVERFLOW;
+
+
+	xhci_cap_regs_t *cap_regs = NULL;
+	if ((err = pio_enable_range(&mmio_range, (void **)&cap_regs)))
+		return EIO;
+
+	code->ranges = malloc(sizeof(irq_pio_range_t));
+	if (code->ranges == NULL)
+		return ENOMEM;
+
+	code->cmds = malloc(sizeof(irq_commands));
+	if (code->cmds == NULL) {
+		free(code->ranges);
+		return ENOMEM;
+	}
+
+	code->rangecount = 1;
+	code->ranges[0] = (irq_pio_range_t) {
+	    .base = RNGABS(mmio_range),
+	    .size = RNGSZ(mmio_range),
+	};
+
+	code->cmdcount = ARRAY_SIZE(irq_commands);
+	memcpy(code->cmds, irq_commands, sizeof(irq_commands));
+
+	void *intr0_iman = RNGABSPTR(mmio_range) + XHCI_REG_RD(cap_regs, XHCI_CAP_RTSOFF) + offsetof(xhci_rt_regs_t, ir[0]);
+	code->cmds[0].addr = intr0_iman;
+	code->cmds[3].addr = intr0_iman;
+	code->cmds[1].value = host2xhci(32, 1);
+
+	return hw_res->irqs.irqs[0];
+}
+
+static int hc_claim(ddf_dev_t *dev)
+{
+	// TODO: implement handoff: section 4.22.1
+	return EOK;
+}
+
+static int hc_reset(xhci_hc_t *hc)
+{
+	/* Stop the HC: set R/S to 0 */
+	XHCI_REG_CLR(hc->op_regs, XHCI_OP_RS, 1);
+
+	/* Wait 16 ms until the HC is halted */
+	async_usleep(16000);
+	assert(XHCI_REG_RD(hc->op_regs, XHCI_OP_HCH));
+
+	/* Reset */
+	XHCI_REG_SET(hc->op_regs, XHCI_OP_HCRST, 1);
+
+	/* Wait until the reset is complete */
+	while (XHCI_REG_RD(hc->op_regs, XHCI_OP_HCRST))
+		async_usleep(1000);
+
+	return EOK;
+}
+
+/**
+ * Initialize the HC: section 4.2
+ */
+static int hc_start(xhci_hc_t *hc, bool irq)
+{
+	int err;
+
+	if ((err = hc_reset(hc)))
+		return err;
+
+	while (XHCI_REG_RD(hc->op_regs, XHCI_OP_CNR))
+		async_usleep(1000);
+
+	uint64_t dcbaaptr = addr_to_phys(hc->event_ring.erst);
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP_LO, LOWER32(dcbaaptr));
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP_HI, UPPER32(dcbaaptr));
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_MAX_SLOTS_EN, 0);
+
+	uint64_t crptr = xhci_trb_ring_get_dequeue_ptr(&hc->command_ring);
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_LO, LOWER32(crptr) >> 6);
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_HI, UPPER32(crptr));
+
+	uint64_t erstptr = addr_to_phys(hc->event_ring.erst);
+	xhci_interrupter_regs_t *intr0 = &hc->rt_regs->ir[0];
+	XHCI_REG_WR(intr0, XHCI_INTR_ERSTSZ, hc->event_ring.segment_count);
+	XHCI_REG_WR(intr0, XHCI_INTR_ERDP_LO, LOWER32(erstptr));
+	XHCI_REG_WR(intr0, XHCI_INTR_ERDP_HI, UPPER32(erstptr));
+	XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_LO, LOWER32(erstptr));
+	XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_HI, UPPER32(erstptr));
+
+	// TODO: Setup scratchpad buffers
+
+	if (irq) {
+		XHCI_REG_SET(intr0, XHCI_INTR_IE, 1);
+		XHCI_REG_SET(hc->op_regs, XHCI_OP_INTE, 1);
+	}
+
+	XHCI_REG_SET(hc->op_regs, XHCI_OP_RS, 1);
+
+	return EOK;
+}
+
+static int hc_init(hcd_t *hcd, const hw_res_list_parsed_t *hw_res, bool irq)
+{
+	int err;
+
 	assert(hcd);
+	assert(hw_res);
 	assert(hcd_get_driver_data(hcd) == NULL);
 
+	/* Initialize the MMIO ranges */
 	if (hw_res->mem_ranges.count != 1) {
-		usb_log_debug("Unexpected MMIO area, bailing out.");
+		usb_log_error("Unexpected MMIO area, bailing out.");
 		return EINVAL;
 	}
+
+	addr_range_t mmio_range = hw_res->mem_ranges.ranges[0];
+
+	usb_log_debug("MMIO area at %p (size %zu), IRQ %d.\n",
+	    RNGABSPTR(mmio_range), RNGSZ(mmio_range), hw_res->irqs.irqs[0]);
+
+	if (RNGSZ(mmio_range) < sizeof(xhci_cap_regs_t))
+		return EOVERFLOW;
+
+	void *base;
+	if ((err = pio_enable_range(&mmio_range, &base)))
+		return err;
 
 	xhci_hc_t *hc = malloc(sizeof(xhci_hc_t));
@@ -68,66 +221,187 @@
 		return ENOMEM;
 
-	addr_range_t mmio_range = hw_res->mem_ranges.ranges[0];
-
-	usb_log_debug("MMIO area at %p (size %zu), IRQ %d.\n",
-	    RNGABSPTR(mmio_range), RNGSZ(mmio_range), hw_res->irqs.irqs[0]);
-
-	if ((err = pio_enable_range(&mmio_range, (void **)&hc->cap_regs)))
+	hc->cap_regs = (xhci_cap_regs_t *)  base;
+	hc->op_regs  = (xhci_op_regs_t *)  (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH));
+	hc->rt_regs  = (xhci_rt_regs_t *)  (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF));
+	hc->db_arry  = (xhci_doorbell_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_DBOFF));
+
+	usb_log_debug2("Initialized MMIO reg areas:");
+	usb_log_debug2("\tCapability regs: %p", hc->cap_regs);
+	usb_log_debug2("\tOperational regs: %p", hc->op_regs);
+	usb_log_debug2("\tRuntime regs: %p", hc->rt_regs);
+	usb_log_debug2("\tDoorbell array base: %p", hc->db_arry);
+
+	xhci_dump_cap_regs(hc->cap_regs);
+
+	hc->ac64 = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_AC64);
+	hc->max_slots = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_SLOTS);
+
+	hc->dcbaa = malloc32((1 + hc->max_slots) * sizeof(xhci_device_ctx_t));
+	if (!hc->dcbaa)
 		goto err_hc;
 
-	xhci_dump_cap_regs(hc->cap_regs);
-
-	uintptr_t base = (uintptr_t) hc->cap_regs;
-
-	hc->op_regs = (xhci_op_regs_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH));
-	hc->rt_regs = (xhci_rt_regs_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF));
-	hc->db_arry = (xhci_doorbell_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_DBOFF));
-
-	usb_log_debug("Initialized MMIO reg areas:");
-	usb_log_debug("\tCapability regs: %p", hc->cap_regs);
-	usb_log_debug("\tOperational regs: %p", hc->op_regs);
-	usb_log_debug("\tRuntime regs: %p", hc->rt_regs);
-	usb_log_debug("\tDoorbell array base: %p", hc->db_arry);
-
-	xhci_dump_state(hc);
+	if ((err = xhci_trb_ring_init(&hc->command_ring, hc)))
+		goto err_dcbaa;
+
+	if ((err = xhci_event_ring_init(&hc->event_ring, hc)))
+		goto err_cmd_ring;
+
+	// TODO: Allocate scratchpad buffers
 
 	hcd_set_implementation(hcd, hc, &xhci_ddf_hc_driver.ops);
 
-	// TODO: check if everything fits into the mmio_area
-
-	return EOK;
-
+	if ((err = hc_start(hc, irq)))
+		goto err_event_ring;
+
+	return EOK;
+
+err_event_ring:
+	xhci_event_ring_fini(&hc->event_ring);
+err_cmd_ring:
+	xhci_trb_ring_fini(&hc->command_ring);
+err_dcbaa:
+	free32(hc->dcbaa);
 err_hc:
 	free(hc);
+	hcd_set_implementation(hcd, NULL, NULL);
 	return err;
 }
 
-int xhci_hc_status(hcd_t *hcd, uint32_t *status)
-{
-	usb_log_info("status");
-	return ENOTSUP;
-}
-
-int xhci_hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
-{
-	usb_log_info("schedule");
-	return ENOTSUP;
-}
-
-void xhci_hc_interrupt(hcd_t *hcd, uint32_t status)
-{
-	usb_log_info("Interrupted!");
-}
-
-void xhci_hc_fini(hcd_t *hcd)
-{
-	assert(hcd);
+static int hc_status(hcd_t *hcd, uint32_t *status)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+	assert(status);
+
+	*status = 0;
+	if (hc->op_regs) {
+		*status = XHCI_REG_RD(hc->op_regs, XHCI_OP_STATUS);
+		XHCI_REG_WR(hc->op_regs, XHCI_OP_STATUS, *status & XHCI_STATUS_ACK_MASK);
+	}
+	usb_log_debug2("HC(%p): Read status: %x", hc, *status);
+	return EOK;
+}
+
+static int ring_doorbell(xhci_hc_t *hc, unsigned doorbell, unsigned target)
+{
+	uint32_t v = host2xhci(32, target & BIT_RRANGE(uint32_t, 7));
+	pio_write_32(&hc->db_arry[doorbell], v);
+	return EOK;
+}
+
+static int send_no_op_command(xhci_hc_t *hc)
+{
+	xhci_trb_t trb;
+	memset(&trb, 0, sizeof(trb));
+
+	trb.control = host2xhci(32, XHCI_TRB_TYPE_NO_OP_CMD << 10);
+
+	xhci_trb_ring_enqueue(&hc->command_ring, &trb);
+	ring_doorbell(hc, 0, 0);
+
+	xhci_dump_trb(&trb);
+	usb_log_debug2("HC(%p): Sent TRB", hc);
+	return EOK;
+}
+
+static int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	xhci_dump_state(hc);
+	send_no_op_command(hc);
+	async_usleep(1000);
+	xhci_dump_state(hc);
+
+	xhci_dump_trb(hc->event_ring.dequeue_trb);
+	return EOK;
+}
+
+static void hc_run_event_ring(xhci_hc_t *hc, xhci_event_ring_t *event_ring, xhci_interrupter_regs_t *intr)
+{
+	int err;
+	xhci_trb_t trb;
+
+	err = xhci_event_ring_dequeue(event_ring, &trb);;
+
+	switch (err) {
+		case EOK:
+			usb_log_debug2("Dequeued from event ring.");
+			xhci_dump_trb(&trb);
+			break;
+
+		case ENOENT:
+			usb_log_debug2("Event ring finished.");
+			break;
+
+		default:
+			usb_log_warning("Error while accessing event ring: %s", str_error(err));
+	}
+
+	/* Update the ERDP to make room inthe ring */
+	uint64_t erstptr = addr_to_phys(hc->event_ring.erst);
+	XHCI_REG_WR(intr, XHCI_INTR_ERDP_LO, LOWER32(erstptr));
+	XHCI_REG_WR(intr, XHCI_INTR_ERDP_HI, UPPER32(erstptr));
+}
+
+static void hc_interrupt(hcd_t *hcd, uint32_t status)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
+	if (status & XHCI_REG_MASK(XHCI_OP_HSE)) {
+		usb_log_error("Host controller error occured. Bad things gonna happen...");
+	}
+
+	if (status & XHCI_REG_MASK(XHCI_OP_EINT)) {
+		usb_log_debug2("Event interrupt.");
+
+		xhci_interrupter_regs_t *intr0 = &hc->rt_regs->ir[0];
+
+		if (XHCI_REG_RD(intr0, XHCI_INTR_IP)) {
+			XHCI_REG_SET(intr0, XHCI_INTR_IP, 1);
+			hc_run_event_ring(hc, &hc->event_ring, intr0);
+		}
+	}
+
+	if (status & XHCI_REG_MASK(XHCI_OP_PCD)) {
+		usb_log_error("Port change detected. Not implemented yet!");
+	}
+	
+	if (status & XHCI_REG_MASK(XHCI_OP_SRE)) {
+		usb_log_error("Save/Restore error occured. WTF, S/R mechanism not implemented!");
+	}
+}
+
+static void hc_fini(hcd_t *hcd)
+{
+	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	assert(hc);
+
 	usb_log_info("Finishing");
 
-	xhci_hc_t *hc = hcd_get_driver_data(hcd);
+	xhci_trb_ring_fini(&hc->command_ring);
+	xhci_event_ring_fini(&hc->event_ring);
+
 	free(hc);
 	hcd_set_implementation(hcd, NULL, NULL);
 }
 
+const ddf_hc_driver_t xhci_ddf_hc_driver = {
+	.hc_speed = USB_SPEED_SUPER,
+	.irq_code_gen = hc_gen_irq_code,
+	.claim = hc_claim,
+	.init = hc_init,
+	.fini = hc_fini,
+	.name = "XHCI-PCI",
+	.ops = {
+		.schedule       = hc_schedule,
+		.irq_hook       = hc_interrupt,
+		.status_hook    = hc_status,
+	}
+};
+
+
 
 /**
Index: uspace/drv/bus/usb/xhci/hc.h
===================================================================
--- uspace/drv/bus/usb/xhci/hc.h	(revision 62ba2cbee5c3230302472cecb3778a285cb40cd4)
+++ uspace/drv/bus/usb/xhci/hc.h	(revision cb89430129f9d8d3b93b48217d53165f33b1774f)
@@ -36,4 +36,6 @@
 #include <usb/host/ddf_helpers.h>
 #include "hw_struct/regs.h"
+#include "hw_struct/context.h"
+#include "trb_ring.h"
 
 typedef struct xhci_hc {
@@ -42,14 +44,16 @@
 	xhci_rt_regs_t *rt_regs;
 	xhci_doorbell_t *db_arry;
+
+	xhci_trb_ring_t command_ring;
+	xhci_event_ring_t event_ring;
+
+	xhci_device_ctx_t *dcbaa;
+
+	unsigned max_slots;
+	bool ac64;
+
 } xhci_hc_t;
 
 extern const ddf_hc_driver_t xhci_ddf_hc_driver;
-
-int xhci_hc_init(hcd_t *, const hw_res_list_parsed_t *, bool irq);
-int xhci_hc_gen_irq_code(irq_code_t *, const hw_res_list_parsed_t *);
-int xhci_hc_status(hcd_t *, uint32_t *);
-int xhci_hc_schedule(hcd_t *, usb_transfer_batch_t *);
-void xhci_hc_interrupt(hcd_t *, uint32_t);
-void xhci_hc_fini(hcd_t *);
 
 
Index: uspace/drv/bus/usb/xhci/hw_struct/context.h
===================================================================
--- uspace/drv/bus/usb/xhci/hw_struct/context.h	(revision 62ba2cbee5c3230302472cecb3778a285cb40cd4)
+++ uspace/drv/bus/usb/xhci/hw_struct/context.h	(revision cb89430129f9d8d3b93b48217d53165f33b1774f)
@@ -43,5 +43,5 @@
 
 #include <stdint.h>
-#include <common.h>
+#include "common.h"
 
 /**
@@ -67,5 +67,5 @@
 #define XHCI_EP_TR_DPTR(ctx)            XHCI_DWORD_EXTRACT((ctx).data[2], 63,  4)
 
-} ep_ctx_t __attribute__((packed));
+} __attribute__((packed)) xhci_ep_ctx_t;
 
 /**
@@ -93,5 +93,5 @@
 #define XHCI_SLOT_SLOT_STATE(ctx)       XHCI_DWORD_EXTRACT((ctx).data[3], 31, 27)
 
-} xhci_slot_ctx_t __attribute__((packed));
+} __attribute__((packed)) xhci_slot_ctx_t;
 
 /**
@@ -101,5 +101,5 @@
 	xhci_slot_ctx_t slot_ctx;
 	xhci_ep_ctx_t endpoint_ctx [31];
-} xhci_device_ctx_t;
+} __attribute__((packed)) xhci_device_ctx_t;
 
 /**
@@ -108,5 +108,5 @@
 typedef struct xhci_stream_ctx {
 	uint64_t data [2];
-} xhci_stream_ctx_t __attribute__((packed));
+} __attribute__((packed)) xhci_stream_ctx_t;
 
 #endif
Index: uspace/drv/bus/usb/xhci/hw_struct/regs.h
===================================================================
--- uspace/drv/bus/usb/xhci/hw_struct/regs.h	(revision 62ba2cbee5c3230302472cecb3778a285cb40cd4)
+++ uspace/drv/bus/usb/xhci/hw_struct/regs.h	(revision cb89430129f9d8d3b93b48217d53165f33b1774f)
@@ -43,11 +43,5 @@
 #include "common.h"
 
-/*
- */
-
 #define XHCI_PIO_CHANGE_UDELAY 5
-
-#define host2xhci(size, val) host2uint##size##_t_le((val))
-#define xhci2host(size, val) uint##size##_t_le2host((val))
 
 /*
@@ -63,4 +57,6 @@
 #define XHCI_REG_SET(reg_set, reg_spec, value) XHCI_REG_SET_INNER(reg_set, value, reg_spec)
 #define XHCI_REG_CLR(reg_set, reg_spec, value) XHCI_REG_CLR_INNER(reg_set, value, reg_spec)
+#define XHCI_REG_MASK(reg_spec)                XHCI_REG_MASK_INNER(reg_spec)
+#define XHCI_REG_SHIFT(reg_spec)               XHCI_REG_SHIFT_INNER(reg_spec)
 
 /*
@@ -78,4 +74,10 @@
 #define XHCI_REG_CLR_INNER(reg_set, value, field, size, type, ...) \
 	XHCI_REG_CLR_##type(&(reg_set)->field, value, size, ##__VA_ARGS__)
+
+#define XHCI_REG_MASK_INNER(field, size, type, ...) \
+	XHCI_REG_MASK_##type(size, ##__VA_ARGS__)
+
+#define XHCI_REG_SHIFT_INNER(field, size, type, ...) \
+	XHCI_REG_SHIFT_##type(size, ##__VA_ARGS__)
 
 /*
@@ -86,4 +88,6 @@
 #define XHCI_REG_SET_FIELD(ptr, value, size) pio_set_##size((ptr), host2xhci(size, value), XHCI_PIO_CHANGE_UDELAY);
 #define XHCI_REG_CLR_FIELD(ptr, value, size) pio_clear_##size((ptr), host2xhci(size, value), XHCI_PIO_CHANGE_UDELAY);
+#define XHCI_REG_MASK_FIELD(size)            (~((uint##size##_t) 0))
+#define XHCI_REG_SHIFT_FIELD(size)           (0)
 
 /*
@@ -94,4 +98,6 @@
 #define XHCI_REG_SET_FLAG(ptr, value, size, offset) XHCI_REG_SET_RANGE((ptr), (value), size, (offset), (offset))
 #define XHCI_REG_CLR_FLAG(ptr, value, size, offset) XHCI_REG_CLR_RANGE((ptr), (value), size, (offset), (offset))
+#define XHCI_REG_MASK_FLAG(size, offset)            BIT_V(uint##size##_t, offset)
+#define XHCI_REG_SHIFT_FLAG(size, offset)           (offset)
 
 /*
@@ -113,4 +119,7 @@
 	pio_clear_##size((ptr), host2xhci(size, BIT_RANGE_INSERT(uint##size##_t, (hi), (lo), (value))), \
 		XHCI_PIO_CHANGE_UDELAY);
+
+#define XHCI_REG_MASK_RANGE(size, hi, lo)  BIT_RANGE(uint##size##_t, hi, lo)
+#define XHCI_REG_SHIFT_RANGE(size, hi, lo) (lo)
 
 /** HC capability registers: section 5.3 */
@@ -215,4 +224,9 @@
 #define XHCI_CAP_CIC          hccparams2, 32,  FLAG,  5
 
+static inline unsigned xhci_get_max_spbuf(xhci_cap_regs_t *cap_regs) {
+	return XHCI_REG_RD(cap_regs, XHCI_CAP_MAX_SPBUF_HI) << 5
+		| XHCI_REG_RD(cap_regs, XHCI_CAP_MAX_SPBUF_LO);
+}
+
 /**
  * XHCI Port Register Set: section 5.4, table 32
@@ -395,4 +409,15 @@
 #define XHCI_OP_CRCR_LO        crcr_lo, 32, RANGE, 31, 6
 #define XHCI_OP_CRCR_HI        crcr_lo, 32, FIELD
+#define XHCI_OP_DCBAAP_LO    dcbaap_lo, 32, FIELD
+#define XHCI_OP_DCBAAP_HI    dcbaap_lo, 32, FIELD
+#define XHCI_OP_MAX_SLOTS_EN    config, 32, RANGE, 7, 0
+#define XHCI_OP_U3E             config, 32,  FLAG, 8
+#define XHCI_OP_CIE             config, 32,  FLAG, 9
+
+/* Aggregating field to read & write whole status at once */
+#define XHCI_OP_STATUS          usbsts, 32, RANGE, 12, 0
+
+/* RW1C fields in usbsts */
+#define XHCI_STATUS_ACK_MASK     0x41C
 
 /**
@@ -444,7 +469,7 @@
 	ioport32_t mfindex;
 
-	PADD32 [5];
-
-	xhci_interrupter_regs_t ir[1024];
+	PADD32 [7];
+
+	xhci_interrupter_regs_t ir [];
 } xhci_rt_regs_t;
 
@@ -452,7 +477,7 @@
 
 /**
- * XHCI Doorbel Registers: section 5.6
- *
- * These registers are write-only, thus convenience macros are useless.
+ * XHCI Doorbell Registers: section 5.6
+ *
+ * These registers are to be written as a whole field.
  */
 typedef ioport32_t xhci_doorbell_t;
Index: uspace/drv/bus/usb/xhci/hw_struct/trb.h
===================================================================
--- uspace/drv/bus/usb/xhci/hw_struct/trb.h	(revision 62ba2cbee5c3230302472cecb3778a285cb40cd4)
+++ uspace/drv/bus/usb/xhci/hw_struct/trb.h	(revision cb89430129f9d8d3b93b48217d53165f33b1774f)
@@ -84,4 +84,6 @@
 	XHCI_TRB_TYPE_DEVICE_NOTIFICATION_EVENT,
 	XHCI_TRB_TYPE_MFINDEX_WRAP_EVENT,
+
+	XHCI_TRB_TYPE_MAX
 };
 
@@ -93,7 +95,8 @@
 	xhci_dword_t status;
 	xhci_dword_t control;
-} xhci_trb_t;
+} __attribute__((packed)) xhci_trb_t;
 
 #define TRB_TYPE(trb)           XHCI_DWORD_EXTRACT((trb).control, 15, 10)
+#define TRB_CYCLE(trb)          XHCI_DWORD_EXTRACT((trb).control, 0, 0)
 #define TRB_LINK_TC(trb)        XHCI_DWORD_EXTRACT((trb).control, 1, 1)
 
@@ -114,5 +117,5 @@
 static inline void xhci_trb_set_cycle(xhci_trb_t *trb, bool cycle)
 {
-	xhci_dword_set_bits(&trb->control, cycle, 1, 1);
+	xhci_dword_set_bits(&trb->control, cycle, 0, 0);
 }
 
@@ -137,12 +140,18 @@
 }
 
-
 /**
  * Event Ring Segment Table: section 6.5
  */
 typedef struct xhci_erst_entry {
-	xhci_qword_t rs_base_ptr;       // sans bits 0-6
-	xhci_dword_t size;              // only low 16 bits, the rest is reserved
+	xhci_qword_t rs_base_ptr;       /* 64B aligned */
+	xhci_dword_t size;              /* only low 16 bits, the rest is RsvdZ */
+	xhci_dword_t _reserved;
 } xhci_erst_entry_t;
 
+static inline void xhci_fill_erst_entry(xhci_erst_entry_t *entry, uintptr_t phys, int segments)
+{
+	xhci_qword_set(&entry->rs_base_ptr, phys);
+	xhci_dword_set_bits(&entry->size, segments, 16, 0);
+}
+
 #endif
Index: uspace/drv/bus/usb/xhci/trb_ring.c
===================================================================
--- uspace/drv/bus/usb/xhci/trb_ring.c	(revision 62ba2cbee5c3230302472cecb3778a285cb40cd4)
+++ uspace/drv/bus/usb/xhci/trb_ring.c	(revision cb89430129f9d8d3b93b48217d53165f33b1774f)
@@ -32,4 +32,7 @@
 #include <as.h>
 #include <align.h>
+#include <usb/debug.h>
+#include <usb/host/utils/malloc32.h>
+#include "hw_struct/trb.h"
 #include "trb_ring.h"
 
@@ -42,5 +45,5 @@
 
 struct trb_segment {
-	xhci_trb_t trb_storage [SEGMENT_TRB_COUNT] __attribute__((packed));
+	xhci_trb_t trb_storage [SEGMENT_TRB_COUNT];
 
 	link_t segments_link;
@@ -59,4 +62,10 @@
 }
 
+/**
+ * Allocate and initialize new segment.
+ *
+ * TODO: When the HC supports 64-bit addressing, there's no need to restrict
+ * to DMAMEM_4GiB.
+ */
 static int trb_segment_allocate(trb_segment_t **segment)
 {
@@ -72,4 +81,6 @@
 		memset(*segment, 0, PAGE_SIZE);
 		(*segment)->phys = phys;
+
+		usb_log_debug2("Allocated new ring segment.");
 	}
 
@@ -77,14 +88,20 @@
 }
 
-int trb_ring_init(xhci_trb_ring_t *ring)
+/**
+ * Initializes the ring with one segment.
+ * Event when it fails, the structure needs to be finalized.
+ */
+int xhci_trb_ring_init(xhci_trb_ring_t *ring, xhci_hc_t *hc)
 {
 	struct trb_segment *segment;
 	int err;
 
+	list_initialize(&ring->segments);
+
 	if ((err = trb_segment_allocate(&segment)) != EOK)
 		return err;
 
-	list_initialize(&ring->segments);
 	list_append(&segment->segments_link, &ring->segments);
+	ring->segment_count = 1;
 
 	xhci_trb_t *last = segment_end(segment) - 1;
@@ -97,9 +114,13 @@
 	ring->pcs = 1;
 
-	return EOK;
-}
-
-int trb_ring_fini(xhci_trb_ring_t *ring)
-{
+	usb_log_debug("Initialized new TRB ring.");
+
+	return EOK;
+}
+
+int xhci_trb_ring_fini(xhci_trb_ring_t *ring)
+{
+	list_foreach(ring->segments, segments_link, trb_segment_t, segment)
+		dmamem_unmap_anonymous(segment);
 	return EOK;
 }
@@ -123,5 +144,5 @@
 }
 
-static uintptr_t xhci_trb_ring_enqueue_phys(xhci_trb_ring_t *ring)
+static uintptr_t trb_ring_enqueue_phys(xhci_trb_ring_t *ring)
 {
 	uintptr_t trb_id = ring->enqueue_trb - segment_begin(ring->enqueue_segment);
@@ -129,5 +150,19 @@
 }
 
-int trb_ring_enqueue(xhci_trb_ring_t *ring, xhci_trb_t *td)
+/**
+ * Enqueue a TD composed of TRBs.
+ *
+ * This will copy all TRBs chained together into the ring. The cycle flag in
+ * TRBs may be changed.
+ *
+ * The chained TRBs must be contiguous in memory, and must not contain Link TRBs.
+ *
+ * We cannot avoid the copying, because the TRB in ring should be updated atomically.
+ *
+ * @param td the first TRB of TD
+ * @return EOK on success,
+ *         EAGAIN when the ring is too full to fit all TRBs (temporary)
+ */
+int xhci_trb_ring_enqueue(xhci_trb_ring_t *ring, xhci_trb_t *td)
 {
 	xhci_trb_t * const saved_enqueue_trb = ring->enqueue_trb;
@@ -145,5 +180,5 @@
 			trb_ring_resolve_link(ring);
 
-		if (xhci_trb_ring_enqueue_phys(ring) == ring->dequeue)
+		if (trb_ring_enqueue_phys(ring) == ring->dequeue)
 			goto err_again;
 	} while (xhci_trb_is_chained(trb++));
@@ -160,4 +195,5 @@
 		xhci_trb_copy(ring->enqueue_trb, trb);
 
+		usb_log_debug2("TRB ring(%p): Enqueued TRB %p", ring, trb);
 		ring->enqueue_trb++;
 
@@ -166,6 +202,8 @@
 			xhci_trb_set_cycle(ring->enqueue_trb, ring->pcs);
 
-			if (TRB_LINK_TC(*ring->enqueue_trb))
+			if (TRB_LINK_TC(*ring->enqueue_trb)) {
 				ring->pcs = !ring->pcs;
+				usb_log_debug2("TRB ring(%p): PCS toggled", ring);
+			}
 
 			trb_ring_resolve_link(ring);
@@ -180,2 +218,94 @@
 	return EAGAIN;
 }
+
+/**
+ * Initializes an event ring.
+ * Even when it fails, the structure needs to be finalized.
+ */
+int xhci_event_ring_init(xhci_event_ring_t *ring, xhci_hc_t *hc)
+{
+	struct trb_segment *segment;
+	int err;
+
+	list_initialize(&ring->segments);
+
+	if ((err = trb_segment_allocate(&segment)) != EOK)
+		return err;
+
+	list_append(&segment->segments_link, &ring->segments);
+	ring->segment_count = 1;
+
+	ring->dequeue_segment = segment;
+	ring->dequeue_trb = segment_begin(segment);
+	ring->dequeue_ptr = segment->phys;
+
+	ring->erst = malloc32(PAGE_SIZE);
+	if (ring->erst == NULL)
+		return ENOMEM;
+
+	xhci_fill_erst_entry(&ring->erst[0], segment->phys, SEGMENT_TRB_COUNT);
+
+	ring->ccs = 1;
+
+	usb_log_debug("Initialized event ring.");
+
+	return EOK;
+}
+
+int xhci_event_ring_fini(xhci_event_ring_t *ring)
+{
+	list_foreach(ring->segments, segments_link, trb_segment_t, segment)
+		dmamem_unmap_anonymous(segment);
+
+	if (ring->erst)
+		free32(ring->erst);
+
+	return EOK;
+}
+
+static uintptr_t event_ring_dequeue_phys(xhci_event_ring_t *ring)
+{
+	uintptr_t trb_id = ring->dequeue_trb - segment_begin(ring->dequeue_segment);
+	return ring->dequeue_segment->phys + trb_id * sizeof(xhci_trb_t);
+}
+
+/**
+ * Fill the event with next valid event from the ring.
+ *
+ * @param event pointer to event to be overwritten
+ * @return EOK on success,
+ *         ENOENT when the ring is empty
+ */
+int xhci_event_ring_dequeue(xhci_event_ring_t *ring, xhci_trb_t *event)
+{
+	/**
+	 * The ERDP reported to the HC is a half-phase off the one we need to
+	 * maintain. Therefore, we keep it extra.
+	 */
+	ring->dequeue_ptr = event_ring_dequeue_phys(ring);
+
+	if (TRB_CYCLE(*ring->dequeue_trb) != ring->ccs)
+		return ENOENT; /* The ring is empty. */
+
+	memcpy(event, ring->dequeue_trb, sizeof(xhci_trb_t));
+
+	ring->dequeue_trb++;
+	const unsigned index = ring->dequeue_trb - segment_begin(ring->dequeue_segment);
+
+	/* Wrapping around segment boundary */
+	if (index >= SEGMENT_TRB_COUNT) {
+		link_t *next_segment = list_next(&ring->dequeue_segment->segments_link, &ring->segments);
+
+		/* Wrapping around table boundary */
+		if (!next_segment) {
+			next_segment = list_first(&ring->segments);
+			ring->ccs = !ring->ccs;
+		}
+
+		ring->dequeue_segment = list_get_instance(next_segment, trb_segment_t, segments_link);
+		ring->dequeue_trb = segment_begin(ring->dequeue_segment);
+	}
+	
+
+	return EOK;
+}
Index: uspace/drv/bus/usb/xhci/trb_ring.h
===================================================================
--- uspace/drv/bus/usb/xhci/trb_ring.h	(revision 62ba2cbee5c3230302472cecb3778a285cb40cd4)
+++ uspace/drv/bus/usb/xhci/trb_ring.h	(revision cb89430129f9d8d3b93b48217d53165f33b1774f)
@@ -45,7 +45,9 @@
 #include <adt/list.h>
 #include <libarch/config.h>
-#include "hw_struct/trb.h"
 
 typedef struct trb_segment trb_segment_t;
+typedef struct xhci_hc xhci_hc_t;
+typedef struct xhci_trb xhci_trb_t;
+typedef struct xhci_erst_entry xhci_erst_entry_t;
 
 /**
@@ -54,4 +56,5 @@
 typedef struct xhci_trb_ring {
 	list_t segments;                /* List of assigned segments */
+	int segment_count;              /* Number of segments assigned */
 
 	/*
@@ -67,26 +70,15 @@
 } xhci_trb_ring_t;
 
-/**
- * Initializes the ring.
- * Allocates one page as the first segment.
- */
-int trb_ring_init(xhci_trb_ring_t *ring);
-int trb_ring_fini(xhci_trb_ring_t *ring);
+int xhci_trb_ring_init(xhci_trb_ring_t *, xhci_hc_t *);
+int xhci_trb_ring_fini(xhci_trb_ring_t *);
+int xhci_trb_ring_enqueue(xhci_trb_ring_t *, xhci_trb_t *);
 
 /**
- * Enqueue a TD composed of TRBs.
- *
- * This will copy all TRBs chained together into the ring. The cycle flag in
- * TRBs may be changed.
- *
- * The chained TRBs must be contiguous in memory, and must not contain Link TRBs.
- *
- * We cannot avoid the copying, because the TRB in ring should be updated atomically.
- *
- * @param td the first TRB of TD
- * @return EOK on success,
- *         EAGAIN when the ring is too full to fit all TRBs (temporary)
+ * Get the initial value to fill into CRCR.
  */
-int trb_ring_enqueue(xhci_trb_ring_t *ring, xhci_trb_t *td);
+static inline uintptr_t xhci_trb_ring_get_dequeue_ptr(xhci_trb_ring_t *ring)
+{
+	return ring->dequeue;
+}
 
 /**
@@ -94,6 +86,26 @@
  * pointer inside the ring. Otherwise, the ring will soon show up as full.
  */
-void trb_ring_update_dequeue(xhci_trb_ring_t *ring, uintptr_t dequeue);
+void xhci_trb_ring_update_dequeue(xhci_trb_ring_t *, uintptr_t);
+uintptr_t xhci_trb_ring_get_dequeue_ptr(xhci_trb_ring_t *);
 
+/**
+ * A TRB ring of which the software is a consumer (event rings).
+ */
+typedef struct xhci_event_ring {
+	list_t segments;                /* List of assigned segments */
+	int segment_count;              /* Number of segments assigned */
+
+	trb_segment_t *dequeue_segment; /* Current segment of the dequeue ptr */
+	xhci_trb_t *dequeue_trb;        /* Next TRB to be processed */
+	uintptr_t dequeue_ptr;          /* Physical address of the ERDP to be reported to the HC */
+
+	xhci_erst_entry_t *erst;        /* ERST given to the HC */
+
+	bool ccs;                       /* Consumer Cycle State: section 4.9.2 */
+} xhci_event_ring_t;
+
+int xhci_event_ring_init(xhci_event_ring_t *, xhci_hc_t *);
+int xhci_event_ring_fini(xhci_event_ring_t *);
+int xhci_event_ring_dequeue(xhci_event_ring_t *, xhci_trb_t *);
 
 #endif
