Index: uspace/drv/bus/usb/xhci/bus.c
===================================================================
--- uspace/drv/bus/usb/xhci/bus.c	(revision 1252e8110bb3ea42f4ef3d85040d83785bb4d42b)
+++ uspace/drv/bus/usb/xhci/bus.c	(revision d1d7a9224fd43f2b1810083927b68b44d7e95e5f)
@@ -187,5 +187,5 @@
 	ret_dev->device = dev;
 
-	usb_log_debug("Device(%d) registered to XHCI bus.", dev->address);
+	usb_log_info("Device(%d) registered to XHCI bus.", dev->address);
 
 	hash_table_insert(&bus->devices, &ret_dev->link);
@@ -202,5 +202,5 @@
 static int hashed_device_remove(xhci_bus_t *bus, hashed_device_t *hashed_dev)
 {
-	usb_log_debug("Device(%d) released from XHCI bus.", hashed_dev->device->address);
+	usb_log_info("Device(%d) released from XHCI bus.", hashed_dev->device->address);
 
 	hash_table_remove(&bus->devices, &hashed_dev->device->address);
@@ -229,5 +229,5 @@
 	}
 
-	usb_log_debug("Endpoint(%d:%d) registered to XHCI bus.", ep->target.address, ep->target.endpoint);
+	usb_log_info("Endpoint(%d:%d) registered to XHCI bus.", ep->target.address, ep->target.endpoint);
 
 	return xhci_device_add_endpoint(hashed_dev->device, xhci_endpoint_get(ep));
@@ -239,5 +239,5 @@
 	assert(bus);
 
-	usb_log_debug("Endpoint(%d:%d) released from XHCI bus.", ep->target.address, ep->target.endpoint);
+	usb_log_info("Endpoint(%d:%d) released from XHCI bus.", ep->target.address, ep->target.endpoint);
 
 	hashed_device_t *hashed_dev;
Index: uspace/drv/bus/usb/xhci/commands.c
===================================================================
--- uspace/drv/bus/usb/xhci/commands.c	(revision 1252e8110bb3ea42f4ef3d85040d83785bb4d42b)
+++ uspace/drv/bus/usb/xhci/commands.c	(revision d1d7a9224fd43f2b1810083927b68b44d7e95e5f)
@@ -443,5 +443,5 @@
 	assert(trb);
 
-	usb_log_debug("HC(%p) Command completed.", hc);
+	usb_log_debug2("HC(%p) Command completed.", hc);
 
 	int code;
@@ -454,5 +454,5 @@
 	if (command == NULL) {
 		// TODO: STOP & ABORT may not have command structs in the list!
-		usb_log_debug("No command struct for this completion event found.");
+		usb_log_warning("No command struct for this completion event found.");
 
 		if (code != XHCI_TRBC_SUCCESS)
Index: uspace/drv/bus/usb/xhci/transfers.c
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.c	(revision 1252e8110bb3ea42f4ef3d85040d83785bb4d42b)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision d1d7a9224fd43f2b1810083927b68b44d7e95e5f)
@@ -155,58 +155,55 @@
 	}
 
-	xhci_trb_t trb_setup;
-	xhci_trb_clean(&trb_setup);
-
-	TRB_CTRL_SET_SETUP_WVALUE(trb_setup, setup->value);
-	TRB_CTRL_SET_SETUP_WLENGTH(trb_setup, setup->length);
-	TRB_CTRL_SET_SETUP_WINDEX(trb_setup, setup->index);
-	TRB_CTRL_SET_SETUP_BREQ(trb_setup, setup->request);
-	TRB_CTRL_SET_SETUP_BMREQTYPE(trb_setup, setup->request_type);
+        xhci_trb_t trbs[3];
+        int trbs_used = 0;
+
+	xhci_trb_t *trb_setup = trbs + trbs_used++;
+	xhci_trb_clean(trb_setup);
+
+	TRB_CTRL_SET_SETUP_WVALUE(*trb_setup, setup->value);
+	TRB_CTRL_SET_SETUP_WLENGTH(*trb_setup, setup->length);
+	TRB_CTRL_SET_SETUP_WINDEX(*trb_setup, setup->index);
+	TRB_CTRL_SET_SETUP_BREQ(*trb_setup, setup->request);
+	TRB_CTRL_SET_SETUP_BMREQTYPE(*trb_setup, setup->request_type);
 
 	/* Size of the setup packet is always 8 */
-	TRB_CTRL_SET_XFER_LEN(trb_setup, 8);
-	// if we want an interrupt after this td is done, use
-	// TRB_CTRL_SET_IOC(trb_setup, 1);
+	TRB_CTRL_SET_XFER_LEN(*trb_setup, 8);
 
 	/* Immediate data */
-	TRB_CTRL_SET_IDT(trb_setup, 1);
-	TRB_CTRL_SET_TRB_TYPE(trb_setup, XHCI_TRB_TYPE_SETUP_STAGE);
-	TRB_CTRL_SET_TRT(trb_setup, get_transfer_type(&trb_setup, setup->request_type, setup->length));
+	TRB_CTRL_SET_IDT(*trb_setup, 1);
+	TRB_CTRL_SET_TRB_TYPE(*trb_setup, XHCI_TRB_TYPE_SETUP_STAGE);
+	TRB_CTRL_SET_TRT(*trb_setup, get_transfer_type(trb_setup, setup->request_type, setup->length));
 
 	/* Data stage */
-	xhci_trb_t trb_data;
-	xhci_trb_clean(&trb_data);
-
+	xhci_trb_t *trb_data = NULL;
 	if (setup->length > 0) {
-		trb_data.parameter = addr_to_phys(transfer->hc_buffer);
+                trb_data = trbs + trbs_used++;
+                xhci_trb_clean(trb_data);
+
+		trb_data->parameter = addr_to_phys(transfer->hc_buffer);
 
 		// data size (sent for OUT, or buffer size)
-		TRB_CTRL_SET_XFER_LEN(trb_data, batch->buffer_size);
+		TRB_CTRL_SET_XFER_LEN(*trb_data, batch->buffer_size);
 		// FIXME: TD size 4.11.2.4
-		TRB_CTRL_SET_TD_SIZE(trb_data, 1);
-
-		// if we want an interrupt after this td is done, use
-		// TRB_CTRL_SET_IOC(trb_data, 1);
+		TRB_CTRL_SET_TD_SIZE(*trb_data, 1);
 
 		// Some more fields here, no idea what they mean
-		TRB_CTRL_SET_TRB_TYPE(trb_data, XHCI_TRB_TYPE_DATA_STAGE);
+		TRB_CTRL_SET_TRB_TYPE(*trb_data, XHCI_TRB_TYPE_DATA_STAGE);
 
 		transfer->direction = REQUEST_TYPE_IS_DEVICE_TO_HOST(setup->request_type)
 					? STAGE_IN : STAGE_OUT;
-		TRB_CTRL_SET_DIR(trb_data, transfer->direction);
+		TRB_CTRL_SET_DIR(*trb_data, transfer->direction);
 	}
 
 	/* Status stage */
-	xhci_trb_t trb_status;
-	xhci_trb_clean(&trb_status);
+	xhci_trb_t *trb_status = trbs + trbs_used++;
+	xhci_trb_clean(trb_status);
 
 	// FIXME: Evaluate next TRB? 4.12.3
-	// TRB_CTRL_SET_ENT(trb_status, 1);
-
-	// if we want an interrupt after this td is done, use
-	TRB_CTRL_SET_IOC(trb_status, 1);
-
-	TRB_CTRL_SET_TRB_TYPE(trb_status, XHCI_TRB_TYPE_STATUS_STAGE);
-	TRB_CTRL_SET_DIR(trb_status, get_status_direction_flag(&trb_setup, setup->request_type, setup->length));
+	// TRB_CTRL_SET_ENT(*trb_status, 1);
+
+	TRB_CTRL_SET_IOC(*trb_status, 1);
+	TRB_CTRL_SET_TRB_TYPE(*trb_status, XHCI_TRB_TYPE_STATUS_STAGE);
+	TRB_CTRL_SET_DIR(*trb_status, get_status_direction_flag(trb_setup, setup->request_type, setup->length));
 
         xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
@@ -214,10 +211,7 @@
         xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[batch->ep->target.endpoint];
 
-	uintptr_t dummy = 0;
-	xhci_trb_ring_enqueue(ring, &trb_setup, &dummy);
-	if (setup->length > 0) {
-		xhci_trb_ring_enqueue(ring, &trb_data, &dummy);
-	}
-	xhci_trb_ring_enqueue(ring, &trb_status, &transfer->interrupt_trb_phys);
+        int err = xhci_trb_ring_enqueue_multiple(ring, trbs, trbs_used, &transfer->interrupt_trb_phys);
+        if (err != EOK)
+                return err;
 
 	list_append(&transfer->link, &hc->transfers);
@@ -226,6 +220,8 @@
 	if (configure_endpoint_needed(setup)) {
 		// TODO: figure out the best time to issue this command
-		// FIXME: ignoring return code
-		xhci_device_configure(xhci_ep->device, hc);
+                // FIXME: on fail, we need to "cancel" in-flight TRBs and remove transfer from the list
+		err = xhci_device_configure(xhci_ep->device, hc);
+                if (err != EOK)
+                        return err;
 	}
 
Index: uspace/drv/bus/usb/xhci/trb_ring.c
===================================================================
--- uspace/drv/bus/usb/xhci/trb_ring.c	(revision 1252e8110bb3ea42f4ef3d85040d83785bb4d42b)
+++ uspace/drv/bus/usb/xhci/trb_ring.c	(revision d1d7a9224fd43f2b1810083927b68b44d7e95e5f)
@@ -116,5 +116,5 @@
 	fibril_mutex_initialize(&ring->guard);
 
-	usb_log_debug("Initialized new TRB ring.");
+	usb_log_debug2("Initialized new TRB ring.");
 
 	return EOK;
@@ -156,20 +156,23 @@
 
 /**
- * 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.
+ * Enqueue TDs composed of TRBs.
+ *
+ * This will copy specified number of TRBs chained together into the ring. The
+ * cycle flag in TRBs may be changed.
+ *
+ * The copied 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
- * @param phys returns address of the first TRB enqueued
+ * @param first_trb the first TRB
+ * @param trbs number of TRBS to enqueue
+ * @param phys returns address of the last TRB enqueued
  * @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, uintptr_t *phys)
-{
+int xhci_trb_ring_enqueue_multiple(xhci_trb_ring_t *ring, xhci_trb_t *first_trb,
+	size_t trbs, uintptr_t *phys)
+{
+	assert(trbs > 0);
 	fibril_mutex_lock(&ring->guard);
 
@@ -183,6 +186,6 @@
 	 * be full anytime during the transaction.
 	 */
-	xhci_trb_t *trb = td;
-	do {
+	xhci_trb_t *trb = first_trb;
+	for (size_t i = 0; i < trbs; ++i, ++trb) {
 		ring->enqueue_trb++;
 
@@ -192,16 +195,17 @@
 		if (trb_ring_enqueue_phys(ring) == ring->dequeue)
 			goto err_again;
-	} while (xhci_trb_is_chained(trb++));
+	}
 
 	ring->enqueue_segment = saved_enqueue_segment;
 	ring->enqueue_trb = saved_enqueue_trb;
-	if (phys)
-		*phys = trb_ring_enqueue_phys(ring);
 
 	/*
 	 * Now, copy the TRBs without further checking.
 	 */
-	trb = td;
-	do {
+	trb = first_trb;
+	for (size_t i = 0; i < trbs; ++i, ++trb) {
+		if (phys && i == trbs - 1)
+			*phys = trb_ring_enqueue_phys(ring);
+
 		xhci_trb_set_cycle(trb, ring->pcs);
 		xhci_trb_copy(ring->enqueue_trb, trb);
@@ -221,5 +225,5 @@
 			trb_ring_resolve_link(ring);
 		}
-	} while (xhci_trb_is_chained(trb++));
+	}
 
 	fibril_mutex_unlock(&ring->guard);
@@ -234,4 +238,12 @@
 
 /**
+ * Enqueue TD composed of a single TRB. See: `xhci_trb_ring_enqueue_multiple`
+ */
+int xhci_trb_ring_enqueue(xhci_trb_ring_t *ring, xhci_trb_t *td, uintptr_t *phys)
+{
+	return xhci_trb_ring_enqueue_multiple(ring, td, 1, phys);
+}
+
+/**
  * Initializes an event ring.
  * Even when it fails, the structure needs to be finalized.
Index: uspace/drv/bus/usb/xhci/trb_ring.h
===================================================================
--- uspace/drv/bus/usb/xhci/trb_ring.h	(revision 1252e8110bb3ea42f4ef3d85040d83785bb4d42b)
+++ uspace/drv/bus/usb/xhci/trb_ring.h	(revision d1d7a9224fd43f2b1810083927b68b44d7e95e5f)
@@ -76,4 +76,5 @@
 int xhci_trb_ring_fini(xhci_trb_ring_t *);
 int xhci_trb_ring_enqueue(xhci_trb_ring_t *, xhci_trb_t *, uintptr_t *);
+int xhci_trb_ring_enqueue_multiple(xhci_trb_ring_t *, xhci_trb_t *, size_t, uintptr_t *);
 
 /**
