Index: uspace/drv/bus/usb/xhci/commands.c
===================================================================
--- uspace/drv/bus/usb/xhci/commands.c	(revision 6ef407b4020edc3b5f4d30ee4103e0c56013e650)
+++ uspace/drv/bus/usb/xhci/commands.c	(revision 889146e9b7a3599240dc6e7f9960962f5c52db8c)
@@ -64,11 +64,25 @@
 /* Control functions */
 
+static xhci_cmd_ring_t *get_cmd_ring(xhci_hc_t *hc)
+{
+	assert(hc);
+	return &hc->cr;
+}
+
 int xhci_init_commands(xhci_hc_t *hc)
 {
-	assert(hc);
-
-	list_initialize(&hc->commands);
-
-	fibril_mutex_initialize(&hc->commands_mtx);
+	xhci_cmd_ring_t *cr = get_cmd_ring(hc);
+	int err;
+
+	if ((err = xhci_trb_ring_init(&cr->trb_ring)))
+		return err;
+
+	fibril_mutex_initialize(&cr->guard);
+	fibril_condvar_initialize(&cr->state_cv);
+	fibril_condvar_initialize(&cr->stopped_cv);
+
+	list_initialize(&cr->cmd_list);
+
+	cr->state = XHCI_CR_STATE_OPEN;
 
 	return EOK;
@@ -77,5 +91,5 @@
 void xhci_fini_commands(xhci_hc_t *hc)
 {
-	// Note: Untested.
+	xhci_stop_command_ring(hc);
 	assert(hc);
 }
@@ -91,5 +105,4 @@
 
 	cmd->_header.cmd = type;
-	cmd->_header.timeout = XHCI_DEFAULT_TIMEOUT;
 }
 
@@ -106,9 +119,11 @@
 }
 
-static inline xhci_cmd_t *get_command(xhci_hc_t *hc, uint64_t phys)
-{
-	fibril_mutex_lock(&hc->commands_mtx);
-
-	link_t *cmd_link = list_first(&hc->commands);
+/** Call with guard locked. */
+static inline xhci_cmd_t *find_command(xhci_hc_t *hc, uint64_t phys)
+{
+	xhci_cmd_ring_t *cr = get_cmd_ring(hc);
+	assert(fibril_mutex_is_locked(&cr->guard));
+
+	link_t *cmd_link = list_first(&cr->cmd_list);
 
 	while (cmd_link != NULL) {
@@ -118,33 +133,36 @@
 			break;
 
-		cmd_link = list_next(cmd_link, &hc->commands);
-	}
-
-	if (cmd_link != NULL) {
-		list_remove(cmd_link);
-		fibril_mutex_unlock(&hc->commands_mtx);
-
-		return list_get_instance(cmd_link, xhci_cmd_t, _header.link);
-	}
-
-	fibril_mutex_unlock(&hc->commands_mtx);
-	return NULL;
+		cmd_link = list_next(cmd_link, &cr->cmd_list);
+	}
+
+	return cmd_link ? list_get_instance(cmd_link, xhci_cmd_t, _header.link)
+		: NULL;
 }
 
 static inline int enqueue_command(xhci_hc_t *hc, xhci_cmd_t *cmd, unsigned doorbell, unsigned target)
 {
-	assert(hc);
-	assert(cmd);
-
-	fibril_mutex_lock(&hc->commands_mtx);
-	list_append(&cmd->_header.link, &hc->commands);
-	fibril_mutex_unlock(&hc->commands_mtx);
-
-	xhci_trb_ring_enqueue(&hc->command_ring, &cmd->_header.trb, &cmd->_header.trb_phys);
-	hc_ring_doorbell(hc, doorbell, target);
-
-	usb_log_debug2("HC(%p): Sent command:", hc);
+	xhci_cmd_ring_t *cr = get_cmd_ring(hc);
+	assert(cmd);
+
+	fibril_mutex_lock(&cr->guard);
+
+	while (cr->state == XHCI_CR_STATE_CHANGING)
+		fibril_condvar_wait(&cr->state_cv, &cr->guard);
+
+	if (cr->state != XHCI_CR_STATE_OPEN) {
+		fibril_mutex_unlock(&cr->guard);
+		return ENAK;
+	}
+
+	usb_log_debug2("HC(%p): Sending command:", hc);
 	xhci_dump_trb(&cmd->_header.trb);
 
+	list_append(&cmd->_header.link, &cr->cmd_list);
+
+	xhci_trb_ring_enqueue(&cr->trb_ring, &cmd->_header.trb, &cmd->_header.trb_phys);
+	hc_ring_doorbell(hc, 0, 0);
+
+	fibril_mutex_unlock(&cr->guard);
+
 	return EOK;
 }
@@ -152,30 +170,25 @@
 void xhci_stop_command_ring(xhci_hc_t *hc)
 {
-	assert(hc);
+	xhci_cmd_ring_t *cr = get_cmd_ring(hc);
+
+	fibril_mutex_lock(&cr->guard);
+
+	// Prevent others from starting CR again.
+	cr->state = XHCI_CR_STATE_CLOSED;
+	fibril_condvar_broadcast(&cr->state_cv);
 
 	XHCI_REG_SET(hc->op_regs, XHCI_OP_CS, 1);
-
-	/**
-	 * Note: There is a bug in qemu that checks CS only when CRCR_HI
-	 *       is written, this (and the read/write in abort) ensures
-	 *       the command rings stops.
-	 */
-	XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_HI, XHCI_REG_RD(hc->op_regs, XHCI_OP_CRCR_HI));
-}
-
-void xhci_abort_command_ring(xhci_hc_t *hc)
-{
-	assert(hc);
-
+	XHCI_REG_SET(hc->op_regs, XHCI_OP_CRCR_HI, 0); // Some systems (incl. QEMU) require 64-bit write
+
+	while (XHCI_REG_RD(hc->op_regs, XHCI_OP_CRR))
+		fibril_condvar_wait(&cr->stopped_cv, &cr->guard);
+
+	fibril_mutex_unlock(&cr->guard);
+}
+
+static void abort_command_ring(xhci_hc_t *hc)
+{
 	XHCI_REG_WR(hc->op_regs, XHCI_OP_CA, 1);
-	XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_HI, XHCI_REG_RD(hc->op_regs, XHCI_OP_CRCR_HI));
-}
-
-void xhci_start_command_ring(xhci_hc_t *hc)
-{
-	assert(hc);
-
-	XHCI_REG_WR(hc->op_regs, XHCI_OP_CRR, 1);
-	hc_ring_doorbell(hc, 0, 0);
+	XHCI_REG_SET(hc->op_regs, XHCI_OP_CRCR_HI, 0); // Some systems (incl. QEMU) require 64-bit write
 }
 
@@ -233,20 +246,32 @@
 int xhci_handle_command_completion(xhci_hc_t *hc, xhci_trb_t *trb)
 {
-	// TODO: Update dequeue ptrs.
-	assert(hc);
+	xhci_cmd_ring_t *cr = get_cmd_ring(hc);
 	assert(trb);
 
 	usb_log_debug2("HC(%p) Command completed.", hc);
 
-	int code;
-	uint64_t phys;
-	xhci_cmd_t *command;
-
-	code = TRB_GET_CODE(*trb);
-	phys = TRB_GET_PHYS(*trb);;
-	command = get_command(hc, phys);
+	fibril_mutex_lock(&cr->guard);
+
+	int code = TRB_GET_CODE(*trb);
+	const uint64_t phys = TRB_GET_PHYS(*trb);
+
+	xhci_trb_ring_update_dequeue(&cr->trb_ring, phys);
+
+	if (code == XHCI_TRBC_COMMAND_RING_STOPPED) {
+		/* This can either mean that the ring is being stopped, or
+		 * a command was aborted. In either way, wake threads waiting
+		 * on stopped_cv.
+		 *
+		 * Note that we need to hold mutex, because we must be sure the
+		 * requesting thread is waiting inside the CV.
+		 */
+		fibril_condvar_broadcast(&cr->stopped_cv);
+		fibril_mutex_unlock(&cr->guard);
+		return EOK;
+	}
+
+	xhci_cmd_t *command = find_command(hc, phys);
 	if (command == NULL) {
-		// TODO: STOP & ABORT may not have command structs in the list!
-		usb_log_warning("No command struct for this completion event found.");
+		usb_log_error("No command struct for this completion event found.");
 
 		if (code != XHCI_TRBC_SUCCESS)
@@ -255,4 +280,6 @@
 		return EOK;
 	}
+
+	list_remove(&command->_header.link);
 
 	/* Semantics of NO_OP_CMD is that success is marked as a TRB error. */
@@ -272,15 +299,9 @@
 	switch (TRB_TYPE(command->_header.trb)) {
 	case XHCI_TRB_TYPE_NO_OP_CMD:
-		break;
 	case XHCI_TRB_TYPE_ENABLE_SLOT_CMD:
-		break;
 	case XHCI_TRB_TYPE_DISABLE_SLOT_CMD:
-		break;
 	case XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD:
-		break;
 	case XHCI_TRB_TYPE_CONFIGURE_ENDPOINT_CMD:
-		break;
 	case XHCI_TRB_TYPE_EVALUATE_CONTEXT_CMD:
-		break;
 	case XHCI_TRB_TYPE_RESET_ENDPOINT_CMD:
 		break;
@@ -294,8 +315,8 @@
 	default:
 		usb_log_debug2("Unsupported command trb: %s", xhci_trb_str_type(TRB_TYPE(command->_header.trb)));
-
-		command->_header.completed = true;
 		return ENAK;
 	}
+
+	fibril_mutex_unlock(&cr->guard);
 
 	fibril_mutex_lock(&command->_header.completed_mtx);
@@ -533,5 +554,54 @@
 };
 
-static int wait_for_cmd_completion(xhci_cmd_t *cmd)
+static int try_abort_current_command(xhci_hc_t *hc)
+{
+	xhci_cmd_ring_t *cr = get_cmd_ring(hc);
+
+	fibril_mutex_lock(&cr->guard);
+
+	if (cr->state != XHCI_CR_STATE_OPEN) {
+		// The CR is either stopped, or different fibril is already
+		// restarting it.
+		fibril_mutex_unlock(&cr->guard);
+		return EOK;
+	}
+
+	usb_log_error("HC(%p): Timeout while waiting for command: aborting current command.", hc);
+
+	cr->state = XHCI_CR_STATE_CHANGING;
+	fibril_condvar_broadcast(&cr->state_cv);
+
+	abort_command_ring(hc);
+
+	fibril_condvar_wait_timeout(&cr->stopped_cv, &cr->guard, XHCI_CR_ABORT_TIMEOUT);
+
+	if (XHCI_REG_RD(hc->op_regs, XHCI_OP_CRR)) {
+		/* 4.6.1.2, implementation note
+		 * Assume there are larger problems with HC and
+		 * reset it.
+		 */
+		usb_log_error("HC(%p): Command didn't abort.", hc);
+
+		cr->state = XHCI_CR_STATE_CLOSED;
+		fibril_condvar_broadcast(&cr->state_cv);
+
+		// TODO: Reset HC completely.
+		// Don't forget to somehow complete all commands with error.
+
+		fibril_mutex_unlock(&cr->guard);
+		return ENAK;
+	}
+
+	usb_log_error("HC(%p): Command ring stopped. Starting again.", hc);
+	hc_ring_doorbell(hc, 0, 0);
+
+	cr->state = XHCI_CR_STATE_OPEN;
+	fibril_condvar_broadcast(&cr->state_cv);
+
+	fibril_mutex_unlock(&cr->guard);
+	return EOK;
+}
+
+static int wait_for_cmd_completion(xhci_hc_t *hc, xhci_cmd_t *cmd)
 {
 	int rv = EOK;
@@ -539,10 +609,18 @@
 	fibril_mutex_lock(&cmd->_header.completed_mtx);
 	while (!cmd->_header.completed) {
-		usb_log_debug2("Waiting for event completion: going to sleep.");
-		rv = fibril_condvar_wait_timeout(&cmd->_header.completed_cv, &cmd->_header.completed_mtx, cmd->_header.timeout);
-
-		usb_log_debug2("Waiting for event completion: woken: %s", str_error(rv));
-		if (rv == ETIMEOUT) {
-			break;
+
+		rv = fibril_condvar_wait_timeout(&cmd->_header.completed_cv, &cmd->_header.completed_mtx, XHCI_COMMAND_TIMEOUT);
+
+		/* The waiting timed out. Current command (not necessarily
+		 * ours) is probably blocked.
+		 */
+		if (!cmd->_header.completed && rv == ETIMEOUT) {
+			fibril_mutex_unlock(&cmd->_header.completed_mtx);
+
+			rv = try_abort_current_command(hc);
+			if (rv)
+				return rv;
+
+			fibril_mutex_lock(&cmd->_header.completed_mtx);
 		}
 	}
@@ -572,6 +650,6 @@
 	}
 
-	if ((err = wait_for_cmd_completion(cmd))) {
-		/* Timeout expired or command failed. */
+	if ((err = wait_for_cmd_completion(hc, cmd))) {
+		/* Command failed. */
 		return err;
 	}
Index: uspace/drv/bus/usb/xhci/commands.h
===================================================================
--- uspace/drv/bus/usb/xhci/commands.h	(revision 6ef407b4020edc3b5f4d30ee4103e0c56013e650)
+++ uspace/drv/bus/usb/xhci/commands.h	(revision 889146e9b7a3599240dc6e7f9960962f5c52db8c)
@@ -42,7 +42,8 @@
 #include <usb/host/dma_buffer.h>
 #include "hw_struct/trb.h"
+#include "trb_ring.h"
 
-#define XHCI_DEFAULT_TIMEOUT       1000000
-#define XHCI_BLOCK_INDEFINITELY    0
+#define XHCI_COMMAND_TIMEOUT       10000
+#define XHCI_CR_ABORT_TIMEOUT       5000
 
 typedef struct xhci_hc xhci_hc_t;
@@ -68,4 +69,23 @@
 } xhci_cmd_type_t;
 
+typedef enum {
+	XHCI_CR_STATE_CLOSED,		/**< Commands are rejected with ENAK. */
+	XHCI_CR_STATE_OPEN,		/**< Commands are enqueued normally. */
+	XHCI_CR_STATE_CHANGING,		/**< Commands wait until state changes. */
+} xhci_cr_state;
+
+typedef struct xhci_command_ring {
+	xhci_trb_ring_t trb_ring;
+
+	fibril_mutex_t guard;		/**< Guard access to this structure. */
+	list_t cmd_list;
+
+	xhci_cr_state state;		/**< Whether commands are allowed to be
+					     added. */
+	fibril_condvar_t state_cv;	/**< For waiting on CR state change. */
+
+	fibril_condvar_t stopped_cv;	/**< For waiting on CR stopped event. */
+} xhci_cmd_ring_t;
+
 typedef struct xhci_command {
 	/** Internal fields used for bookkeeping. Need not worry about these. */
@@ -74,5 +94,4 @@
 
 		xhci_cmd_type_t cmd;
-		suseconds_t timeout;
 
 		xhci_trb_t trb;
@@ -130,8 +149,4 @@
 	fibril_condvar_initialize(&cmd._header.completed_cv);
 
-	if (!cmd._header.timeout) {
-		cmd._header.timeout = XHCI_DEFAULT_TIMEOUT;
-	}
-
 	/* Issue the command */
 	const int err = xhci_cmd_sync(hc, &cmd);
Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 6ef407b4020edc3b5f4d30ee4103e0c56013e650)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision 889146e9b7a3599240dc6e7f9960962f5c52db8c)
@@ -44,5 +44,4 @@
 #include "hw_struct/context.h"
 #include "endpoint.h"
-#include "commands.h"
 #include "transfers.h"
 #include "trb_ring.h"
@@ -204,9 +203,6 @@
 	hc->dcbaa = hc->dcbaa_dma.virt;
 
-	if ((err = xhci_trb_ring_init(&hc->command_ring)))
+	if ((err = xhci_event_ring_init(&hc->event_ring)))
 		goto err_dcbaa;
-
-	if ((err = xhci_event_ring_init(&hc->event_ring)))
-		goto err_cmd_ring;
 
 	if ((err = xhci_scratchpad_alloc(hc)))
@@ -233,6 +229,4 @@
 err_event_ring:
 	xhci_event_ring_fini(&hc->event_ring);
-err_cmd_ring:
-	xhci_trb_ring_fini(&hc->command_ring);
 err_dcbaa:
 	hc->dcbaa = NULL;
@@ -399,4 +393,5 @@
 		return err;
 
+	// FIXME: Waiting forever.
 	while (XHCI_REG_RD(hc->op_regs, XHCI_OP_CNR))
 		async_usleep(1000);
@@ -407,5 +402,5 @@
 	XHCI_REG_WR(hc->op_regs, XHCI_OP_MAX_SLOTS_EN, 0);
 
-	uint64_t crptr = xhci_trb_ring_get_dequeue_ptr(&hc->command_ring);
+	uint64_t crptr = xhci_trb_ring_get_dequeue_ptr(&hc->cr.trb_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));
@@ -578,5 +573,4 @@
 {
 	xhci_bus_fini(&hc->bus);
-	xhci_trb_ring_fini(&hc->command_ring);
 	xhci_event_ring_fini(&hc->event_ring);
 	hc_dcbaa_fini(hc);
Index: uspace/drv/bus/usb/xhci/hc.h
===================================================================
--- uspace/drv/bus/usb/xhci/hc.h	(revision 6ef407b4020edc3b5f4d30ee4103e0c56013e650)
+++ uspace/drv/bus/usb/xhci/hc.h	(revision 889146e9b7a3599240dc6e7f9960962f5c52db8c)
@@ -45,5 +45,8 @@
 
 #include "rh.h"
+#include "commands.h"
 #include "bus.h"
+
+typedef struct xhci_command xhci_cmd_t;
 
 typedef struct xhci_hc {
@@ -61,5 +64,4 @@
 
 	/* Structures in allocated memory */
-	xhci_trb_ring_t command_ring;
 	xhci_event_ring_t event_ring;
 	uint64_t *dcbaa;
@@ -67,4 +69,7 @@
 	dma_buffer_t scratchpad_array;
 	dma_buffer_t *scratchpad_buffers;
+
+	/* Command ring management */
+	xhci_cmd_ring_t cr;
 
 	/* Root hub emulation */
@@ -81,8 +86,4 @@
 	xhci_port_speed_t speeds [16];
 	uint8_t speed_to_psiv [USB_SPEED_MAX];
-
-	/* Command list */
-	list_t commands;
-	fibril_mutex_t commands_mtx;
 
 	/* TODO: Hack. Figure out a better way. */
Index: uspace/drv/bus/usb/xhci/trb_ring.h
===================================================================
--- uspace/drv/bus/usb/xhci/trb_ring.h	(revision 6ef407b4020edc3b5f4d30ee4103e0c56013e650)
+++ uspace/drv/bus/usb/xhci/trb_ring.h	(revision 889146e9b7a3599240dc6e7f9960962f5c52db8c)
@@ -91,5 +91,5 @@
  * pointer inside the ring. Otherwise, the ring will soon show up as full.
  */
-void xhci_trb_ring_update_dequeue(xhci_trb_ring_t *, uintptr_t);
+static inline void xhci_trb_ring_update_dequeue(xhci_trb_ring_t *ring, uintptr_t phys) {}
 uintptr_t xhci_trb_ring_get_dequeue_ptr(xhci_trb_ring_t *);
 
