Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 2bff2cc2f4626dd9e056a1dd37bb2ff505dae69a)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision 2c0564c67df2bfa02f0f8a115f7c2a3028a3b21a)
@@ -248,4 +248,6 @@
 }
 
+static int event_worker(void *arg);
+
 /**
  * Initialize structures kept in allocated memory.
@@ -271,6 +273,21 @@
 		goto err_cmd;
 
-	return EOK;
-
+	fid_t fid = fibril_create(&event_worker, hc);
+	if (!fid)
+		goto err_bus;
+
+	// TODO: completion_reset
+	hc->event_fibril_completion.active = true;
+	fibril_mutex_initialize(&hc->event_fibril_completion.guard);
+	fibril_condvar_initialize(&hc->event_fibril_completion.cv);
+
+	xhci_sw_ring_init(&hc->sw_ring, PAGE_SIZE / sizeof(xhci_trb_t));
+
+	fibril_add_ready(fid);
+
+	return EOK;
+
+err_bus:
+	xhci_bus_fini(&hc->bus);
 err_cmd:
 	xhci_fini_commands(hc);
@@ -537,18 +554,55 @@
 typedef int (*event_handler) (xhci_hc_t *, xhci_trb_t *trb);
 
+/**
+ * These events are handled by separate event handling fibril.
+ */
 static event_handler event_handlers [] = {
-	[XHCI_TRB_TYPE_COMMAND_COMPLETION_EVENT] = &xhci_handle_command_completion,
 	[XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVENT] = &handle_port_status_change_event,
 	[XHCI_TRB_TYPE_TRANSFER_EVENT] = &xhci_handle_transfer_event,
+};
+
+/**
+ * These events are handled directly in the interrupt handler, thus they must
+ * not block waiting for another interrupt.
+ */
+static event_handler event_handlers_fast [] = {
+	[XHCI_TRB_TYPE_COMMAND_COMPLETION_EVENT] = &xhci_handle_command_completion,
 	[XHCI_TRB_TYPE_MFINDEX_WRAP_EVENT] = &xhci_handle_mfindex_wrap_event,
 };
 
-static int hc_handle_event(xhci_hc_t *hc, xhci_trb_t *trb, xhci_interrupter_regs_t *intr)
-{
-	unsigned type = TRB_TYPE(*trb);
-	if (type >= ARRAY_SIZE(event_handlers) || !event_handlers[type])
-		return ENOTSUP;
-
-	return event_handlers[type](hc, trb);
+static int hc_handle_event(xhci_hc_t *hc, xhci_trb_t *trb)
+{
+	const unsigned type = TRB_TYPE(*trb);
+
+	if (type <= ARRAY_SIZE(event_handlers_fast) && event_handlers_fast[type])
+		return event_handlers_fast[type](hc, trb);
+
+	if (type <= ARRAY_SIZE(event_handlers) && event_handlers[type])
+		return xhci_sw_ring_enqueue(&hc->sw_ring, trb);
+
+	return ENOTSUP;
+}
+
+static int event_worker(void *arg)
+{
+	int err;
+	xhci_trb_t trb;
+	xhci_hc_t * const hc = arg;
+	assert(hc);
+
+	while (xhci_sw_ring_dequeue(&hc->sw_ring, &trb) != EINTR) {
+		const unsigned type = TRB_TYPE(trb);
+
+		if ((err = event_handlers[type](hc, &trb)))
+			usb_log_error("Failed to handle event: %s", str_error(err));
+	}
+
+	// TODO: completion_complete
+	fibril_mutex_lock(&hc->event_fibril_completion.guard);
+	hc->event_fibril_completion.active = false;
+	fibril_condvar_wait(&hc->event_fibril_completion.cv, &hc->event_fibril_completion.guard);
+	fibril_mutex_unlock(&hc->event_fibril_completion.guard);
+
+	return EOK;
 }
 
@@ -571,6 +625,6 @@
 
 	while ((err = xhci_event_ring_dequeue(event_ring, &trb)) != ENOENT) {
-		if ((err = hc_handle_event(hc, &trb, intr)) != EOK) {
-			usb_log_error("Failed to handle event: %s", str_error(err));
+		if ((err = hc_handle_event(hc, &trb)) != EOK) {
+			usb_log_error("Failed to handle event in interrupt: %s", str_error(err));
 		}
 
@@ -634,4 +688,13 @@
 void hc_fini(xhci_hc_t *hc)
 {
+	xhci_sw_ring_stop(&hc->sw_ring);
+
+	// TODO: completion_wait
+	fibril_mutex_lock(&hc->event_fibril_completion.guard);
+	while (hc->event_fibril_completion.active)
+		fibril_condvar_wait(&hc->event_fibril_completion.cv, &hc->event_fibril_completion.guard);
+	fibril_mutex_unlock(&hc->event_fibril_completion.guard);
+	xhci_sw_ring_fini(&hc->sw_ring);
+
 	xhci_bus_fini(&hc->bus);
 	xhci_event_ring_fini(&hc->event_ring);
Index: uspace/drv/bus/usb/xhci/hc.h
===================================================================
--- uspace/drv/bus/usb/xhci/hc.h	(revision 2bff2cc2f4626dd9e056a1dd37bb2ff505dae69a)
+++ uspace/drv/bus/usb/xhci/hc.h	(revision 2c0564c67df2bfa02f0f8a115f7c2a3028a3b21a)
@@ -75,4 +75,13 @@
 	xhci_cmd_ring_t cr;
 
+	/* Buffer for events */
+	xhci_sw_ring_t sw_ring;
+
+	struct {
+		fibril_mutex_t guard;
+		fibril_condvar_t cv;
+		bool active;
+	} event_fibril_completion;
+
 	/* Root hub emulation */
 	xhci_rh_t rh;
