Index: uspace/drv/bus/usb/ehci/ehci_batch.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_batch.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/ehci/ehci_batch.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -61,5 +61,5 @@
 {
 	assert(ehci_batch);
-	dma_buffer_free(&ehci_batch->dma_buffer);
+	dma_buffer_free(&ehci_batch->ehci_dma_buffer);
 	usb_log_debug2("Batch(%p): disposed", ehci_batch);
 	free(ehci_batch);
@@ -116,26 +116,25 @@
 	}
 
+	assert(ehci_batch->td_count > 0);
+
 	const size_t tds_size = ehci_batch->td_count * sizeof(td_t);
 
-	/* Mix setup stage, data and TDs together, we have enough space */
-	if (size + setup_size + tds_size > 0) {
-		if (dma_buffer_alloc(&ehci_batch->dma_buffer, tds_size + setup_size + size)) {
-			usb_log_error("Batch %p: Failed to allocate device "
-			    "buffer", ehci_batch);
-			return ENOMEM;
-		}
-		/* Clean TDs */
-		ehci_batch->tds = ehci_batch->dma_buffer.virt;
-		memset(ehci_batch->tds, 0, tds_size);
-		/* Copy setup data */
-		ehci_batch->setup_buffer = ehci_batch->dma_buffer.virt + tds_size;
-                memcpy(ehci_batch->setup_buffer, ehci_batch->base.setup.buffer, setup_size);
-		/* Copy generic data */
-		ehci_batch->data_buffer = ehci_batch->setup_buffer + setup_size;
-		if (ehci_batch->base.dir != USB_DIRECTION_IN)
-			memcpy(ehci_batch->data_buffer,
-			    ehci_batch->base.buffer,
-			    ehci_batch->base.buffer_size);
-	}
+	/* Mix setup stage and TDs together, we have enough space */
+	if (dma_buffer_alloc(&ehci_batch->ehci_dma_buffer, tds_size + setup_size)) {
+		usb_log_error("Batch %p: Failed to allocate device buffer",
+		    ehci_batch);
+		return ENOMEM;
+	}
+
+	/* Clean TDs */
+	ehci_batch->tds = ehci_batch->ehci_dma_buffer.virt;
+	memset(ehci_batch->tds, 0, tds_size);
+
+	/* Copy setup data */
+	ehci_batch->setup_buffer = ehci_batch->ehci_dma_buffer.virt + tds_size;
+	memcpy(ehci_batch->setup_buffer, ehci_batch->base.setup.buffer, setup_size);
+
+	/* Generic data already prepared*/
+	ehci_batch->data_buffer = ehci_batch->base.dma_buffer.virt;
 
 	if (!batch_setup[ehci_batch->base.ep->transfer_type])
@@ -219,9 +218,4 @@
 	assert(ehci_batch->base.transferred_size <= ehci_batch->base.buffer_size);
 
-	if (ehci_batch->base.dir == USB_DIRECTION_IN)
-		memcpy(ehci_batch->base.buffer,
-		    ehci_batch->data_buffer,
-		    ehci_batch->base.transferred_size);
-
 	/* Clear TD pointers */
 	ehci_batch->qh->next = LINK_POINTER_TERM;
@@ -240,5 +234,6 @@
 {
 	assert(ehci_batch);
-	qh_set_next_td(ehci_batch->qh, dma_buffer_phys(&ehci_batch->dma_buffer, &ehci_batch->tds[0]));
+	qh_set_next_td(ehci_batch->qh,
+	    dma_buffer_phys(&ehci_batch->ehci_dma_buffer, &ehci_batch->tds[0]));
 }
 
@@ -275,10 +270,10 @@
 	/* Setup stage */
 	td_init(&ehci_batch->tds[0],
-	    dma_buffer_phys(&ehci_batch->dma_buffer, &ehci_batch->tds[1]),
-	    dma_buffer_phys(&ehci_batch->dma_buffer, ehci_batch->setup_buffer),
+	    dma_buffer_phys(&ehci_batch->ehci_dma_buffer, &ehci_batch->tds[1]),
+	    dma_buffer_phys(&ehci_batch->ehci_dma_buffer, ehci_batch->setup_buffer),
 	    USB_DIRECTION_BOTH, USB_SETUP_PACKET_SIZE, toggle, false);
 	usb_log_debug2("Batch %p: Created CONTROL SETUP TD(%"PRIxn"): "
 	    "%08x:%08x:%08x", ehci_batch,
-	    dma_buffer_phys(&ehci_batch->dma_buffer, &ehci_batch->tds[0]),
+	    dma_buffer_phys(&ehci_batch->ehci_dma_buffer, &ehci_batch->tds[0]),
 	    ehci_batch->tds[0].status, ehci_batch->tds[0].next,
 	    ehci_batch->tds[0].alternate);
@@ -287,5 +282,6 @@
 	unsigned td_current = 1;
 	size_t remain_size = ehci_batch->base.buffer_size;
-	uintptr_t buffer = dma_buffer_phys(&ehci_batch->dma_buffer, ehci_batch->data_buffer);
+	uintptr_t buffer = dma_buffer_phys(&ehci_batch->base.dma_buffer,
+	    ehci_batch->data_buffer);
 	while (remain_size > 0) {
 		const size_t transfer_size = min(remain_size, EHCI_TD_MAX_TRANSFER);
@@ -293,9 +289,9 @@
 
 		td_init(&ehci_batch->tds[td_current],
-		    dma_buffer_phys(&ehci_batch->dma_buffer, &ehci_batch->tds[td_current + 1]),
+		    dma_buffer_phys(&ehci_batch->ehci_dma_buffer, &ehci_batch->tds[td_current + 1]),
 		    buffer, data_dir, transfer_size, toggle, false);
 		usb_log_debug2("Batch %p: Created CONTROL DATA TD(%"PRIxn"): "
 		    "%08x:%08x:%08x", ehci_batch,
-		    dma_buffer_phys(&ehci_batch->dma_buffer, &ehci_batch->tds[td_current]),
+		    dma_buffer_phys(&ehci_batch->ehci_dma_buffer, &ehci_batch->tds[td_current]),
 		    ehci_batch->tds[td_current].status,
 		    ehci_batch->tds[td_current].next,
@@ -313,5 +309,5 @@
 	usb_log_debug2("Batch %p: Created CONTROL STATUS TD %d(%"PRIxn"): "
 	    "%08x:%08x:%08x", ehci_batch, td_current,
-	    dma_buffer_phys(&ehci_batch->dma_buffer, &ehci_batch->tds[td_current]),
+	    dma_buffer_phys(&ehci_batch->ehci_dma_buffer, &ehci_batch->tds[td_current]),
 	    ehci_batch->tds[td_current].status,
 	    ehci_batch->tds[td_current].next,
@@ -340,5 +336,6 @@
 	size_t td_current = 0;
 	size_t remain_size = ehci_batch->base.buffer_size;
-	uintptr_t buffer = dma_buffer_phys(&ehci_batch->dma_buffer, ehci_batch->data_buffer);
+	uintptr_t buffer = dma_buffer_phys(&ehci_batch->base.dma_buffer,
+	    ehci_batch->data_buffer);
 	while (remain_size > 0) {
 		const size_t transfer_size = remain_size > EHCI_TD_MAX_TRANSFER
@@ -347,10 +344,12 @@
 		const bool last = (remain_size == transfer_size);
 		td_init(&ehci_batch->tds[td_current],
-		    last ? 0 : dma_buffer_phys(&ehci_batch->dma_buffer, &ehci_batch->tds[td_current + 1]),
+		    last ? 0 : dma_buffer_phys(&ehci_batch->ehci_dma_buffer,
+			    &ehci_batch->tds[td_current + 1]),
 		    buffer, ehci_batch->base.dir, transfer_size, -1, last);
 
 		usb_log_debug2("Batch %p: DATA TD(%"PRIxn": %08x:%08x:%08x",
 		    ehci_batch,
-		    dma_buffer_phys(&ehci_batch->dma_buffer, &ehci_batch->tds[td_current]),
+		    dma_buffer_phys(&ehci_batch->ehci_dma_buffer,
+		        &ehci_batch->tds[td_current]),
 		    ehci_batch->tds[td_current].status,
 		    ehci_batch->tds[td_current].next,
Index: uspace/drv/bus/usb/ehci/ehci_batch.h
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_batch.h	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/ehci/ehci_batch.h	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -51,6 +51,6 @@
 	/** Endpoint descriptor of the target endpoint. */
 	qh_t *qh;
-	/** Data buffer, must be accessible by the EHCI hw. */
-	dma_buffer_t dma_buffer;
+	/** Backend for TDs and setup data. */
+	dma_buffer_t ehci_dma_buffer;
 	/** List of TDs needed for the transfer - backed by dma_buffer */
 	td_t *tds;
Index: uspace/drv/bus/usb/ehci/ehci_rh.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_rh.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/ehci/ehci_rh.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -147,5 +147,6 @@
 	batch->error = virthub_base_request(&instance->base, batch->target,
 	    batch->dir, (void*) batch->setup.buffer,
-	    batch->buffer, batch->buffer_size, &batch->transferred_size);
+	    batch->dma_buffer.virt, batch->buffer_size,
+	    &batch->transferred_size);
 	if (batch->error == ENAK) {
 		usb_log_debug("RH(%p): BATCH(%p) adding as unfinished",
@@ -205,5 +206,6 @@
 		batch->error = virthub_base_request(&instance->base, batch->target,
 		    batch->dir, (void*) batch->setup.buffer,
-		    batch->buffer, batch->buffer_size, &batch->transferred_size);
+		    batch->dma_buffer.virt, batch->buffer_size,
+		    &batch->transferred_size);
 		usb_transfer_batch_finish(batch);
 	}
Index: uspace/drv/bus/usb/ohci/ohci_batch.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_batch.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/ohci/ohci_batch.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -113,5 +113,5 @@
 		: 0;
 
-	if (dma_buffer_alloc(&ohci_batch->ohci_dma_buffer, td_size + setup_size + usb_batch->buffer_size)) {
+	if (dma_buffer_alloc(&ohci_batch->ohci_dma_buffer, td_size + setup_size)) {
 		usb_log_error("Failed to allocate OHCI DMA buffer.");
 		return ENOMEM;
@@ -129,7 +129,5 @@
 	memcpy(ohci_batch->setup_buffer, usb_batch->setup.buffer, setup_size);
 
-	ohci_batch->data_buffer = ohci_batch->setup_buffer + setup_size;
-	if (usb_batch->dir == USB_DIRECTION_OUT)
-		memcpy(ohci_batch->data_buffer, usb_batch->buffer, usb_batch->buffer_size);
+	ohci_batch->data_buffer = usb_batch->dma_buffer.virt;
 
 	batch_setup[usb_batch->ep->transfer_type](ohci_batch);
@@ -151,9 +149,10 @@
 	assert(ohci_batch);
 
-	ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ohci_batch->base.ep);
+	usb_transfer_batch_t *usb_batch = &ohci_batch->base;
+	ohci_endpoint_t *ohci_ep = ohci_endpoint_get(usb_batch->ep);
 	assert(ohci_ep);
 
 	usb_log_debug("Batch %p checking %zu td(s) for completion.",
-	    &ohci_batch->base, ohci_batch->td_count);
+	    ohci_batch, ohci_batch->td_count);
 	usb_log_debug2("ED: %08x:%08x:%08x:%08x.",
 	    ohci_ep->ed->status, ohci_ep->ed->td_head,
@@ -167,5 +166,5 @@
 
 	/* Assume all data got through */
-	ohci_batch->base.transferred_size = ohci_batch->base.buffer_size;
+	usb_batch->transferred_size = usb_batch->buffer_size;
 
 	/* Check all TDs */
@@ -176,6 +175,6 @@
 		    ohci_batch->tds[i]->next, ohci_batch->tds[i]->be);
 
-		ohci_batch->base.error = td_error(ohci_batch->tds[i]);
-		if (ohci_batch->base.error == EOK) {
+		usb_batch->error = td_error(ohci_batch->tds[i]);
+		if (usb_batch->error == EOK) {
 			/* If the TD got all its data through, it will report
 			 * 0 bytes remain, the sole exception is INPUT with
@@ -190,9 +189,9 @@
 			 * we leave the very last(unused) TD behind.
 			 */
-			ohci_batch->base.transferred_size
+			usb_batch->transferred_size
 			    -= td_remain_size(ohci_batch->tds[i]);
 		} else {
 			usb_log_debug("Batch %p found error TD(%zu):%08x.",
-			    &ohci_batch->base, i, ohci_batch->tds[i]->status);
+			    ohci_batch, i, ohci_batch->tds[i]->status);
 
 			/* ED should be stopped because of errors */
@@ -213,11 +212,5 @@
 		}
 	}
-	assert(ohci_batch->base.transferred_size <=
-	    ohci_batch->base.buffer_size);
-
-	if (ohci_batch->base.dir == USB_DIRECTION_IN)
-		memcpy(ohci_batch->base.buffer,
-		    ohci_batch->data_buffer,
-		    ohci_batch->base.transferred_size);
+	assert(usb_batch->transferred_size <= usb_batch->buffer_size);
 
 	/* Make sure that we are leaving the right TD behind */
Index: uspace/drv/bus/usb/ohci/ohci_rh.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_rh.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/ohci/ohci_rh.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -182,5 +182,5 @@
 	batch->error = virthub_base_request(&instance->base, batch->target,
 	    batch->dir, &batch->setup.packet,
-	    batch->buffer, batch->buffer_size, &batch->transferred_size);
+	    batch->dma_buffer.virt, batch->buffer_size, &batch->transferred_size);
 	if (batch->error == ENAK) {
 		/* Lock the HC guard */
@@ -233,5 +233,5 @@
 		batch->error = virthub_base_request(&instance->base, batch->target,
 		    batch->dir, &batch->setup.packet,
-		    batch->buffer, batch->buffer_size, &batch->transferred_size);
+		    batch->dma_buffer.virt, batch->buffer_size, &batch->transferred_size);
 		usb_transfer_batch_finish(batch);
 	}
Index: uspace/drv/bus/usb/uhci/uhci_batch.c
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_batch.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/uhci/uhci_batch.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -61,5 +61,5 @@
 {
 	assert(uhci_batch);
-	free32(uhci_batch->device_buffer);
+	dma_buffer_free(&uhci_batch->uhci_dma_buffer);
 	free(uhci_batch);
 }
@@ -110,29 +110,23 @@
 
 	const size_t total_size = (sizeof(td_t) * uhci_batch->td_count)
-	    + sizeof(qh_t) + setup_size + usb_batch->buffer_size;
-	uhci_batch->device_buffer = malloc32(total_size);
-	if (!uhci_batch->device_buffer) {
+	    + sizeof(qh_t) + setup_size;
+
+	if (dma_buffer_alloc(&uhci_batch->uhci_dma_buffer, total_size)) {
 		usb_log_error("Failed to allocate UHCI buffer.");
 		return ENOMEM;
 	}
-	memset(uhci_batch->device_buffer, 0, total_size);
-
-	uhci_batch->tds = uhci_batch->device_buffer;
-	uhci_batch->qh =
-	    (uhci_batch->device_buffer + (sizeof(td_t) * uhci_batch->td_count));
+	memset(uhci_batch->uhci_dma_buffer.virt, 0, total_size);
+
+	uhci_batch->tds = uhci_batch->uhci_dma_buffer.virt;
+	uhci_batch->qh = (qh_t *) &uhci_batch->tds[uhci_batch->td_count];
 
 	qh_init(uhci_batch->qh);
 	qh_set_element_td(uhci_batch->qh, &uhci_batch->tds[0]);
 
-	void *dest =
-	    uhci_batch->device_buffer + (sizeof(td_t) * uhci_batch->td_count)
-	    + sizeof(qh_t);
+	void *setup_buffer = uhci_transfer_batch_setup_buffer(uhci_batch);
+	assert(setup_buffer == (void *) (uhci_batch->qh + 1));
 	/* Copy SETUP packet data to the device buffer */
-	memcpy(dest, usb_batch->setup.buffer, setup_size);
-	dest += setup_size;
-	/* Copy generic data unless they are provided by the device */
-	if (usb_batch->dir != USB_DIRECTION_IN) {
-		memcpy(dest, usb_batch->buffer, usb_batch->buffer_size);
-	}
+	memcpy(setup_buffer, usb_batch->setup.buffer, setup_size);
+
 	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
 	    " memory structures ready.", usb_batch,
@@ -196,10 +190,5 @@
 	}
 
-	if (batch->dir == USB_DIRECTION_IN) {
-		assert(batch->transferred_size <= batch->buffer_size);
-		memcpy(batch->buffer,
-		    uhci_transfer_batch_data_buffer(uhci_batch),
-		    batch->transferred_size);
-	}
+	assert(batch->transferred_size <= batch->buffer_size);
 
 	return true;
Index: uspace/drv/bus/usb/uhci/uhci_batch.h
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_batch.h	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/uhci/uhci_batch.h	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -61,6 +61,8 @@
 	/** Number of TDs used by the transfer */
 	size_t td_count;
-	/** Data buffer, must be accessible by the UHCI hw */
-	void *device_buffer;
+	/* Setup data */
+	char *setup_buffer;
+	/** Backing TDs + setup_buffer */
+	dma_buffer_t uhci_dma_buffer;
 	/** List element */
 	link_t link;
@@ -80,6 +82,5 @@
 {
 	assert(uhci_batch);
-	assert(uhci_batch->device_buffer);
-	return uhci_batch->device_buffer + sizeof(qh_t) +
+	return uhci_batch->uhci_dma_buffer.virt + sizeof(qh_t) +
 	    uhci_batch->td_count * sizeof(td_t);
 }
@@ -93,6 +94,5 @@
 {
 	assert(uhci_batch);
-	return uhci_transfer_batch_setup_buffer(uhci_batch) +
-	    (uhci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL ? USB_SETUP_PACKET_SIZE : 0);
+	return uhci_batch->base.dma_buffer.virt;
 }
 
Index: uspace/drv/bus/usb/uhci/uhci_rh.c
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_rh.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/uhci/uhci_rh.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -107,5 +107,5 @@
 		batch->error = virthub_base_request(&instance->base, batch->target,
 		    batch->dir, (void*) batch->setup.buffer,
-		    batch->buffer, batch->buffer_size, &batch->transferred_size);
+		    batch->dma_buffer.virt, batch->buffer_size, &batch->transferred_size);
 		if (batch->error == ENAK)
 			async_usleep(instance->base.endpoint_descriptor.poll_interval * 1000);
Index: uspace/drv/bus/usb/vhc/transfer.c
===================================================================
--- uspace/drv/bus/usb/vhc/transfer.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/vhc/transfer.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -72,5 +72,5 @@
 			rc = usbvirt_control_read(dev,
 			    batch->setup.buffer, USB_SETUP_PACKET_SIZE,
-			    batch->buffer, batch->buffer_size,
+			    batch->dma_buffer.virt, batch->buffer_size,
 			    actual_data_size);
 		} else {
@@ -78,5 +78,5 @@
 			rc = usbvirt_control_write(dev,
 			    batch->setup.buffer, USB_SETUP_PACKET_SIZE,
-			    batch->buffer, batch->buffer_size);
+			    batch->dma_buffer.virt, batch->buffer_size);
 		}
 	} else {
@@ -84,5 +84,5 @@
 			rc = usbvirt_data_in(dev, batch->ep->transfer_type,
 			    batch->ep->endpoint,
-			    batch->buffer, batch->buffer_size,
+			    batch->dma_buffer.virt, batch->buffer_size,
 			    actual_data_size);
 		} else {
@@ -90,5 +90,5 @@
 			rc = usbvirt_data_out(dev, batch->ep->transfer_type,
 			    batch->ep->endpoint,
-			    batch->buffer, batch->buffer_size);
+			    batch->dma_buffer.virt, batch->buffer_size);
 		}
 	}
@@ -108,5 +108,5 @@
 			rc = usbvirt_ipc_send_control_read(sess,
 			    batch->setup.buffer, USB_SETUP_PACKET_SIZE,
-			    batch->buffer, batch->buffer_size,
+			    batch->dma_buffer.virt, batch->buffer_size,
 			    actual_data_size);
 		} else {
@@ -114,5 +114,5 @@
 			rc = usbvirt_ipc_send_control_write(sess,
 			    batch->setup.buffer, USB_SETUP_PACKET_SIZE,
-			    batch->buffer, batch->buffer_size);
+			    batch->dma_buffer.virt, batch->buffer_size);
 		}
 	} else {
@@ -120,5 +120,5 @@
 			rc = usbvirt_ipc_send_data_in(sess, batch->ep->endpoint,
 			    batch->ep->transfer_type,
-			    batch->buffer, batch->buffer_size,
+			    batch->dma_buffer.virt, batch->buffer_size,
 			    actual_data_size);
 		} else {
@@ -126,5 +126,5 @@
 			rc = usbvirt_ipc_send_data_out(sess, batch->ep->endpoint,
 			    batch->ep->transfer_type,
-			    batch->buffer, batch->buffer_size);
+			    batch->dma_buffer.virt, batch->buffer_size);
 		}
 	}
Index: uspace/drv/bus/usb/xhci/commands.c
===================================================================
--- uspace/drv/bus/usb/xhci/commands.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/xhci/commands.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -260,4 +260,6 @@
 	// Prevent others from starting CR again.
 	cr_set_state(cr, XHCI_CR_STATE_CLOSED);
+
+	XHCI_REG_SET(hc->op_regs, XHCI_OP_CS, 1);
 	fibril_mutex_unlock(&cr->guard);
 }
Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -476,6 +476,5 @@
 		return ETIMEOUT;
 
-	uint64_t dcbaaptr = hc->dcbaa_dma.phys;
-	XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP, dcbaaptr);
+	XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP, hc->dcbaa_dma.phys);
 	XHCI_REG_WR(hc->op_regs, XHCI_OP_MAX_SLOTS_EN, hc->max_slots);
 
Index: uspace/drv/bus/usb/xhci/isoch.c
===================================================================
--- uspace/drv/bus/usb/xhci/isoch.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/xhci/isoch.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -480,8 +480,6 @@
 	xhci_isoch_t * const isoch = ep->isoch;
 
-	if (transfer->batch.buffer_size > ep->base.max_transfer_size) {
-		usb_log_error("Cannot schedule an oversized isochronous transfer.");
-		return ELIMIT;
-	}
+	/* This shall be already checked by endpoint */
+	assert(transfer->batch.buffer_size <= ep->base.max_transfer_size);
 
 	fibril_mutex_lock(&isoch->guard);
@@ -524,5 +522,5 @@
 	/* Prepare the transfer. */
 	it->size = transfer->batch.buffer_size;
-	memcpy(it->data.virt, transfer->batch.buffer, it->size);
+	memcpy(it->data.virt, transfer->batch.dma_buffer.virt, it->size);
 	it->state = ISOCH_FILLED;
 
@@ -573,5 +571,5 @@
 	/* Withdraw results from previous transfer. */
 	if (!it->error) {
-		memcpy(transfer->batch.buffer, it->data.virt, it->size);
+		memcpy(transfer->batch.dma_buffer.virt, it->data.virt, it->size);
 		transfer->batch.transferred_size = it->size;
 		transfer->batch.error = it->error;
Index: uspace/drv/bus/usb/xhci/transfers.c
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -115,6 +115,4 @@
 {
 	xhci_transfer_t *transfer = xhci_transfer_from_batch(batch);
-
-	dma_buffer_free(&transfer->hc_buffer);
 	free(transfer);
 }
@@ -135,6 +133,6 @@
 	size_t i, size_t total, size_t *remaining)
 {
-	const uintptr_t ptr = dma_buffer_phys(&transfer->hc_buffer,
-		transfer->hc_buffer.virt + i * PAGE_SIZE);
+	const uintptr_t ptr = dma_buffer_phys(&transfer->batch.dma_buffer,
+		transfer->batch.dma_buffer.virt + i * PAGE_SIZE);
 
 	trb->parameter = host2xhci(64, ptr);
@@ -418,9 +416,5 @@
 	}
 
-	if (batch->dir == USB_DIRECTION_IN) {
-		assert(batch->buffer);
-		assert(batch->transferred_size <= batch->buffer_size);
-		memcpy(batch->buffer, transfer->hc_buffer.virt, batch->transferred_size);
-	}
+	assert(batch->transferred_size <= batch->buffer_size);
 
 	usb_transfer_batch_finish(batch);
@@ -472,14 +466,4 @@
 	const usb_transfer_type_t type = batch->ep->transfer_type;
 	assert(transfer_handlers[type]);
-
-	if (batch->buffer_size > 0) {
-		if (dma_buffer_alloc(&transfer->hc_buffer, batch->buffer_size))
-			return ENOMEM;
-	}
-
-	if (batch->dir != USB_DIRECTION_IN) {
-		// Sending stuff from host to device, we need to copy the actual data.
-		memcpy(transfer->hc_buffer.virt, batch->buffer, batch->buffer_size);
-	}
 
 	/*
Index: uspace/drv/bus/usb/xhci/transfers.h
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.h	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/drv/bus/usb/xhci/transfers.h	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -50,6 +50,4 @@
 	uint8_t direction;
 
-	dma_buffer_t hc_buffer;
-
 	uintptr_t interrupt_trb_phys;
 } xhci_transfer_t;
Index: uspace/lib/drv/generic/remote_usbhc.c
===================================================================
--- uspace/lib/drv/generic/remote_usbhc.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/lib/drv/generic/remote_usbhc.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -428,4 +428,20 @@
 		return err;
 
+	/*
+	 * As we're going to check the mapping, we must make sure the memory is
+	 * actually mapped. We must do it right now, because the area might be
+	 * read-only or write-only, and we may be unsure later.
+	 */
+	if (flags & AS_AREA_READ) {
+		char foo = 0;
+		volatile const char *buf = trans->buffer;
+		for (size_t i = 0; i < size; i += PAGE_SIZE)
+			foo += buf[i];
+	} else {
+		volatile char *buf = trans->buffer;
+		for (size_t i = 0; i < size; i += PAGE_SIZE)
+			buf[i] = 0xff;
+	}
+
 	return EOK;
 }
Index: uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -41,4 +41,5 @@
 #include <stddef.h>
 #include <stdint.h>
+#include <usb/dma_buffer.h>
 #include <usb/request.h>
 #include <usb/usb.h>
@@ -56,15 +57,9 @@
 	/** Target for communication */
 	usb_target_t target;
+	/** Direction of the transfer */
+	usb_direction_t dir;
 
 	/** Endpoint used for communication */
 	endpoint_t *ep;
-
-	/** Direction of the transfer */
-	usb_direction_t dir;
-
-	/** Function called on completion */
-	usbhc_iface_transfer_callback_t on_complete;
-	/** Arbitrary data for the handler */
-	void *on_complete_data;
 
 	/** Place to store SETUP data needed by control transfers */
@@ -75,13 +70,24 @@
 	} setup;
 
-	/** Place for data to send/receive */
-	char *buffer;
-	/** Size of memory pointed to by buffer member */
+	/** DMA buffer with enforced policy */
+	dma_buffer_t dma_buffer;
+	/** Size of memory buffer */
 	size_t buffer_size;
+
+	/**
+	 * In case the DMA buffer is allocated, the original buffer must to be
+	 * stored to be filled after the IN transaction is finished.
+	 */
+	char *original_buffer;
+
+	/** Indicates success/failure of the communication */
+	errno_t error;
 	/** Actually used portion of the buffer */
 	size_t transferred_size;
 
-	/** Indicates success/failure of the communication */
-	errno_t error;
+	/** Function called on completion */
+	usbhc_iface_transfer_callback_t on_complete;
+	/** Arbitrary data for the handler */
+	void *on_complete_data;
 } usb_transfer_batch_t;
 
@@ -108,4 +114,7 @@
 void usb_transfer_batch_init(usb_transfer_batch_t *, endpoint_t *);
 
+/** Buffer preparation */
+errno_t usb_transfer_batch_prepare_buffer(usb_transfer_batch_t *, char *);
+
 /** Batch finalization. */
 void usb_transfer_batch_finish(usb_transfer_batch_t *);
Index: uspace/lib/usbhost/src/endpoint.c
===================================================================
--- uspace/lib/usbhost/src/endpoint.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/lib/usbhost/src/endpoint.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -74,4 +74,5 @@
 
 	ep->max_transfer_size = ep->max_packet_size * ep->packets_per_uframe;
+	ep->transfer_buffer_policy = DMA_POLICY_STRICT;
 }
 
@@ -215,5 +216,5 @@
  * @param name Communication identifier (for nicer output).
  */
-int endpoint_send_batch(endpoint_t *ep, usb_target_t target,
+errno_t endpoint_send_batch(endpoint_t *ep, usb_target_t target,
     usb_direction_t direction, char *data, size_t size, uint64_t setup_data,
     usbhc_iface_transfer_callback_t on_complete, void *arg, const char *name)
@@ -258,8 +259,15 @@
 
 	batch->target = target;
-	batch->buffer = data;
-	batch->buffer_size = size;
 	batch->setup.packed = setup_data;
 	batch->dir = direction;
+	batch->buffer_size = size;
+
+	errno_t err;
+	if ((err = usb_transfer_batch_prepare_buffer(batch, data))) {
+		usb_log_warning("Failed to prepare buffer for batch: %s", str_error(err));
+		usb_transfer_batch_destroy(batch);
+		return err;
+	}
+
 	batch->on_complete = on_complete;
 	batch->on_complete_data = arg;
Index: uspace/lib/usbhost/src/usb_transfer_batch.c
===================================================================
--- uspace/lib/usbhost/src/usb_transfer_batch.c	(revision 65c059faef414c2c2e8542095e9f65eca6e83f08)
+++ uspace/lib/usbhost/src/usb_transfer_batch.c	(revision c21e6a5172262a245f7e805a5cb9885ee65024b4)
@@ -104,4 +104,43 @@
 
 /**
+ * Prepare a DMA buffer according to endpoint policy.
+ *
+ * If the buffer is suitable to be used directly, it is. Otherwise, a bounce
+ * buffer is created.
+ */
+errno_t usb_transfer_batch_prepare_buffer(
+    usb_transfer_batch_t *batch, char *buf)
+{
+	const dma_policy_t policy = batch->ep->transfer_buffer_policy;
+
+	/* Empty transfers do not need a buffer */
+	if (batch->buffer_size == 0)
+		return EOK;
+
+	if (dma_buffer_check_policy(buf, batch->buffer_size, policy)) {
+		/* Mark this case with invalid address */
+		batch->original_buffer = NULL;
+
+		/* Fill the buffer with virtual address and lock it for DMA */
+		return dma_buffer_lock(&batch->dma_buffer, buf, batch->buffer_size);
+	}
+	else {
+		usb_log_debug("Batch(%p): Buffer cannot be used directly, "
+		    "falling back to bounce buffer!", batch);
+		const errno_t err = dma_buffer_alloc_policy(&batch->dma_buffer,
+		    batch->buffer_size, policy);
+
+		/* Copy the data out */
+		if (!err && batch->dir == USB_DIRECTION_OUT)
+			memcpy(batch->dma_buffer.virt, buf, batch->buffer_size);
+
+		/* Store the buffer to store the data back before finishing */
+		batch->original_buffer = buf;
+
+		return err;
+	}
+}
+
+/**
  * Finish a transfer batch: call handler, destroy batch, release endpoint.
  *
@@ -116,8 +155,25 @@
 	    batch, USB_TRANSFER_BATCH_ARGS(*batch));
 
+	if (batch->error == EOK && batch->buffer_size > 0) {
+		if (batch->original_buffer == NULL) {
+			/* Unlock the buffer for DMA */
+			dma_buffer_unlock(&batch->dma_buffer,
+			    batch->buffer_size);
+		}
+		else {
+			/* We we're forced to use bounce buffer, copy it back */
+			if (batch->dir == USB_DIRECTION_IN)
+				memcpy(batch->original_buffer,
+				    batch->dma_buffer.virt,
+				    batch->transferred_size);
+
+			dma_buffer_free(&batch->dma_buffer);
+		}
+	}
+
 	if (batch->on_complete) {
 		const int err = batch->on_complete(batch->on_complete_data, batch->error, batch->transferred_size);
 		if (err)
-			usb_log_warning("batch %p failed to complete: %s",
+			usb_log_warning("Batch %p failed to complete: %s",
 			    batch, str_error(err));
 	}
