Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 629255aedc4238596ec15d37677104ce5eb4fb45)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision 047fbc82a9a6f1939379d03723a2abc9fdf13295)
@@ -549,12 +549,4 @@
 }
 
-static int handle_port_status_change_event(xhci_hc_t *hc, xhci_trb_t *trb)
-{
-	uint8_t port_id = XHCI_QWORD_EXTRACT(trb->parameter, 31, 24);
-	usb_log_debug("Port status change event detected for port %u.", port_id);
-	xhci_rh_handle_port_change(&hc->rh, port_id);
-	return EOK;
-}
-
 typedef int (*event_handler) (xhci_hc_t *, xhci_trb_t *trb);
 
@@ -563,5 +555,4 @@
  */
 static event_handler event_handlers [] = {
-	[XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVENT] = &handle_port_status_change_event,
 	[XHCI_TRB_TYPE_TRANSFER_EVENT] = &xhci_handle_transfer_event,
 };
@@ -586,4 +577,7 @@
 		return xhci_sw_ring_enqueue(&hc->sw_ring, trb);
 
+	if (type == XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVENT)
+		return xhci_sw_ring_enqueue(&hc->rh.event_ring, trb);
+
 	return ENOTSUP;
 }
@@ -606,6 +600,5 @@
 	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_condvar_broadcast(&hc->event_fibril_completion.cv);
 	fibril_mutex_unlock(&hc->event_fibril_completion.guard);
 
Index: uspace/drv/bus/usb/xhci/rh.c
===================================================================
--- uspace/drv/bus/usb/xhci/rh.c	(revision 629255aedc4238596ec15d37677104ce5eb4fb45)
+++ uspace/drv/bus/usb/xhci/rh.c	(revision 047fbc82a9a6f1939379d03723a2abc9fdf13295)
@@ -74,4 +74,6 @@
 } rh_port_t;
 
+static int rh_worker(void *);
+
 /**
  * Initialize the roothub subsystem.
@@ -94,4 +96,10 @@
 	}
 
+	fid_t fid = fibril_create(&rh_worker, rh);
+	if (!fid) {
+		free(rh->ports);
+		return err;
+	}
+
 	for (unsigned i = 0; i < rh->max_ports; i++) {
 		usb_port_init(&rh->ports[i].base);
@@ -103,4 +111,12 @@
 	rh->device.route_str = 0;
 
+	xhci_sw_ring_init(&rh->event_ring, rh->max_ports);
+
+	hc->event_fibril_completion.active = true;
+	fibril_mutex_initialize(&hc->event_fibril_completion.guard);
+	fibril_condvar_initialize(&hc->event_fibril_completion.cv);
+
+	fibril_add_ready(fid);
+
 	return EOK;
 }
@@ -114,4 +130,14 @@
 	for (unsigned i = 0; i < rh->max_ports; i++)
 		usb_port_fini(&rh->ports[i].base);
+
+	xhci_sw_ring_stop(&rh->event_ring);
+
+	// TODO: completion_wait
+	fibril_mutex_lock(&rh->event_fibril_completion.guard);
+	while (rh->event_fibril_completion.active)
+		fibril_condvar_wait(&rh->event_fibril_completion.cv,
+		    &rh->event_fibril_completion.guard);
+	fibril_mutex_unlock(&rh->event_fibril_completion.guard);
+	xhci_sw_ring_fini(&rh->event_ring);
 	return EOK;
 }
@@ -219,5 +245,5 @@
  * Handle all changes on specified port.
  */
-void xhci_rh_handle_port_change(xhci_rh_t *rh, uint8_t port_id)
+static void handle_port_change(xhci_rh_t *rh, uint8_t port_id)
 {
 	rh_port_t * const port = &rh->ports[port_id - 1];
@@ -280,5 +306,5 @@
 	 */
 	for (uint8_t i = 0; i < rh->max_ports; ++i) {
-		xhci_rh_handle_port_change(rh, i + 1);
+		handle_port_change(rh, i + 1);
 
 		rh_port_t * const port = &rh->ports[i];
@@ -295,4 +321,24 @@
 }
 
+static int rh_worker(void *arg)
+{
+	xhci_rh_t * const rh = arg;
+
+	xhci_trb_t trb;
+	while (xhci_sw_ring_dequeue(&rh->event_ring, &trb) == EOK) {
+		uint8_t port_id = XHCI_QWORD_EXTRACT(trb.parameter, 31, 24);
+		usb_log_debug("Port status change event detected for port %u.", port_id);
+		handle_port_change(rh, port_id);
+	}
+
+	// TODO: completion_complete
+	fibril_mutex_lock(&rh->event_fibril_completion.guard);
+	rh->event_fibril_completion.active = false;
+	fibril_condvar_broadcast(&rh->event_fibril_completion.cv);
+	fibril_mutex_unlock(&rh->event_fibril_completion.guard);
+
+	return EOK;
+}
+
 /**
  * @}
Index: uspace/drv/bus/usb/xhci/rh.h
===================================================================
--- uspace/drv/bus/usb/xhci/rh.h	(revision 629255aedc4238596ec15d37677104ce5eb4fb45)
+++ uspace/drv/bus/usb/xhci/rh.h	(revision 047fbc82a9a6f1939379d03723a2abc9fdf13295)
@@ -73,4 +73,13 @@
 	/* Array of port structures. (size is `max_ports`) */
 	rh_port_t *ports;
+
+	/* Event ring for roothub */
+	xhci_sw_ring_t event_ring;
+
+	struct {
+		fibril_mutex_t guard;
+		fibril_condvar_t cv;
+		bool active;
+	} event_fibril_completion;
 } xhci_rh_t;
 
@@ -78,5 +87,4 @@
 extern int xhci_rh_fini(xhci_rh_t *);
 
-extern void xhci_rh_handle_port_change(xhci_rh_t *, uint8_t);
 extern void xhci_rh_set_ports_protocol(xhci_rh_t *, unsigned, unsigned, unsigned);
 extern void xhci_rh_startup(xhci_rh_t *);
