Index: uspace/drv/bus/usb/ehci/ehci_batch.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_batch.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/ehci/ehci_batch.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -53,5 +53,5 @@
 #define EHCI_TD_MAX_TRANSFER   (16 * 1024)
 
-static void (*const batch_setup[])(ehci_transfer_batch_t*, usb_direction_t);
+static void (*const batch_setup[])(ehci_transfer_batch_t*);
 
 /** Safely destructs ehci_transfer_batch_t structure
@@ -59,8 +59,7 @@
  * @param[in] ehci_batch Instance to destroy.
  */
-static void ehci_transfer_batch_dispose(ehci_transfer_batch_t *ehci_batch)
-{
-	if (!ehci_batch)
-		return;
+void ehci_transfer_batch_destroy(ehci_transfer_batch_t *ehci_batch)
+{
+	assert(ehci_batch);
 	if (ehci_batch->tds) {
 		for (size_t i = 0; i < ehci_batch->td_count; ++i) {
@@ -69,5 +68,4 @@
 		free(ehci_batch->tds);
 	}
-	usb_transfer_batch_destroy(ehci_batch->usb_batch);
 	free32(ehci_batch->device_buffer);
 	free(ehci_batch);
@@ -75,17 +73,4 @@
 }
 
-/** Finishes usb_transfer_batch and destroys the structure.
- *
- * @param[in] uhci_batch Instance to finish and destroy.
- */
-void ehci_transfer_batch_finish_dispose(ehci_transfer_batch_t *ehci_batch)
-{
-	assert(ehci_batch);
-	assert(ehci_batch->usb_batch);
-	usb_transfer_batch_finish(ehci_batch->usb_batch,
-	    ehci_batch->device_buffer + ehci_batch->usb_batch->setup_size);
-	ehci_transfer_batch_dispose(ehci_batch);
-}
-
 /** Allocate memory and initialize internal data structure.
  *
@@ -94,26 +79,64 @@
  * NULL otherwise.
  *
+ */
+ehci_transfer_batch_t * ehci_transfer_batch_create(endpoint_t *ep)
+{
+	assert(ep);
+
+	ehci_transfer_batch_t *ehci_batch = calloc(1, sizeof(ehci_transfer_batch_t));
+	if (!ehci_batch) {
+		usb_log_error("Failed to allocate EHCI batch data.");
+		return NULL;
+	}
+
+	usb_transfer_batch_init(&ehci_batch->base, ep);
+	link_initialize(&ehci_batch->link);
+
+	return ehci_batch;
+}
+
+/** Prepares a batch to be sent.
+ *
  * Determines the number of needed transfer descriptors (TDs).
  * Prepares a transport buffer (that is accessible by the hardware).
  * Initializes parameters needed for the transfer and callback.
  */
-ehci_transfer_batch_t * ehci_transfer_batch_get(usb_transfer_batch_t *usb_batch)
-{
-	assert(usb_batch);
-
-	ehci_transfer_batch_t *ehci_batch =
-	    calloc(1, sizeof(ehci_transfer_batch_t));
-	if (!ehci_batch) {
-		usb_log_error("Batch %p: Failed to allocate EHCI batch data.",
-		    usb_batch);
-		goto dispose;
-	}
-	link_initialize(&ehci_batch->link);
-	ehci_batch->td_count =
-	    (usb_batch->buffer_size + EHCI_TD_MAX_TRANSFER - 1)
-	    / EHCI_TD_MAX_TRANSFER;
+int ehci_transfer_batch_prepare(ehci_transfer_batch_t *ehci_batch)
+{
+	assert(ehci_batch);
+
+	const size_t setup_size = (ehci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
+		? USB_SETUP_PACKET_SIZE
+		: 0;
+
+	const size_t size = ehci_batch->base.buffer_size;
+
+	/* Mix setup stage and data together, we have enough space */
+        if (size + setup_size > 0) {
+		/* Use one buffer for setup and data stage */
+		ehci_batch->device_buffer = malloc32(size + setup_size);
+		if (!ehci_batch->device_buffer) {
+			usb_log_error("Batch %p: Failed to allocate device "
+			    "buffer", ehci_batch);
+			return ENOMEM;
+		}
+		/* Copy setup data */
+                memcpy(ehci_batch->device_buffer, ehci_batch->base.setup.buffer,
+		    setup_size);
+		/* Copy generic data */
+		if (ehci_batch->base.dir != USB_DIRECTION_IN)
+			memcpy(ehci_batch->device_buffer + setup_size,
+			    ehci_batch->base.buffer, ehci_batch->base.buffer_size);
+        }
+
+	/* Add TD left over by the previous transfer */
+	ehci_batch->qh = ehci_endpoint_get(ehci_batch->base.ep)->qh;
+
+	/* Determine number of TDs needed */
+	ehci_batch->td_count = (size + EHCI_TD_MAX_TRANSFER - 1)
+		/ EHCI_TD_MAX_TRANSFER;
 
 	/* Control transfer need Setup and Status stage */
-	if (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
+	if (ehci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL) {
 		ehci_batch->td_count += 2;
 	}
@@ -122,10 +145,7 @@
 	if (!ehci_batch->tds) {
 		usb_log_error("Batch %p: Failed to allocate EHCI transfer "
-		    "descriptors.", usb_batch);
-		goto dispose;
-	}
-
-	/* Add TD left over by the previous transfer */
-	ehci_batch->qh = ehci_endpoint_get(usb_batch->ep)->qh;
+		    "descriptors.", ehci_batch);
+		return ENOMEM;
+	}
 
 	for (unsigned i = 0; i < ehci_batch->td_count; ++i) {
@@ -133,44 +153,18 @@
 		if (!ehci_batch->tds[i]) {
 			usb_log_error("Batch %p: Failed to allocate TD %d.",
-			    usb_batch, i);
-			goto dispose;
+			    ehci_batch, i);
+			return ENOMEM;
 		}
 		memset(ehci_batch->tds[i], 0, sizeof(td_t));
 	}
 
-
-	/* Mix setup stage and data together, we have enough space */
-        if (usb_batch->setup_size + usb_batch->buffer_size > 0) {
-		/* Use one buffer for setup and data stage */
-		ehci_batch->device_buffer =
-		    malloc32(usb_batch->setup_size + usb_batch->buffer_size);
-		if (!ehci_batch->device_buffer) {
-			usb_log_error("Batch %p: Failed to allocate device "
-			    "buffer", usb_batch);
-			goto dispose;
-		}
-		/* Copy setup data */
-                memcpy(ehci_batch->device_buffer, usb_batch->setup_buffer,
-		    usb_batch->setup_size);
-		/* Copy generic data */
-		if (usb_batch->ep->direction != USB_DIRECTION_IN)
-			memcpy(
-			    ehci_batch->device_buffer + usb_batch->setup_size,
-			    usb_batch->buffer, usb_batch->buffer_size);
-        }
-	ehci_batch->usb_batch = usb_batch;
-
-	const usb_direction_t dir = usb_transfer_batch_direction(usb_batch);
-	assert(batch_setup[usb_batch->ep->transfer_type]);
-	batch_setup[usb_batch->ep->transfer_type](ehci_batch, dir);
+	assert(batch_setup[ehci_batch->base.ep->transfer_type]);
+	batch_setup[ehci_batch->base.ep->transfer_type](ehci_batch);
 
 	usb_log_debug("Batch %p %s " USB_TRANSFER_BATCH_FMT " initialized.\n",
-	    usb_batch, usb_str_direction(dir),
-	    USB_TRANSFER_BATCH_ARGS(*usb_batch));
-
-	return ehci_batch;
-dispose:
-	ehci_transfer_batch_dispose(ehci_batch);
-	return NULL;
+	    ehci_batch, usb_str_direction(ehci_batch->base.dir),
+	    USB_TRANSFER_BATCH_ARGS(ehci_batch->base));
+
+	return EOK;
 }
 
@@ -184,14 +178,13 @@
  * completes with the last TD.
  */
-bool ehci_transfer_batch_is_complete(const ehci_transfer_batch_t *ehci_batch)
-{
-	assert(ehci_batch);
-	assert(ehci_batch->usb_batch);
+bool ehci_transfer_batch_check_completed(ehci_transfer_batch_t *ehci_batch)
+{
+	assert(ehci_batch);
 
 	usb_log_debug("Batch %p: checking %zu td(s) for completion.\n",
-	    ehci_batch->usb_batch, ehci_batch->td_count);
+	    ehci_batch, ehci_batch->td_count);
 
 	usb_log_debug2("Batch %p: QH: %08x:%08x:%08x:%08x:%08x:%08x.\n",
-	    ehci_batch->usb_batch,
+	    ehci_batch,
 	    ehci_batch->qh->ep_char, ehci_batch->qh->ep_cap,
 	    ehci_batch->qh->status, ehci_batch->qh->current,
@@ -206,6 +199,5 @@
 
 	/* Assume all data got through */
-	ehci_batch->usb_batch->transfered_size =
-	    ehci_batch->usb_batch->buffer_size;
+	ehci_batch->base.transfered_size = ehci_batch->base.buffer_size;
 
 	/* Check all TDs */
@@ -213,10 +205,10 @@
 		assert(ehci_batch->tds[i] != NULL);
 		usb_log_debug("Batch %p: TD %zu: %08x:%08x:%08x.",
-		    ehci_batch->usb_batch, i,
+		    ehci_batch, i,
 		    ehci_batch->tds[i]->status, ehci_batch->tds[i]->next,
 		    ehci_batch->tds[i]->alternate);
 
-		ehci_batch->usb_batch->error = td_error(ehci_batch->tds[i]);
-		if (ehci_batch->usb_batch->error == EOK) {
+		ehci_batch->base.error = td_error(ehci_batch->tds[i]);
+		if (ehci_batch->base.error == EOK) {
 			/* If the TD got all its data through, it will report
 			 * 0 bytes remain, the sole exception is INPUT with
@@ -231,11 +223,11 @@
 			 * we leave the very last(unused) TD behind.
 			 */
-			ehci_batch->usb_batch->transfered_size
+			ehci_batch->base.transfered_size
 			    -= td_remain_size(ehci_batch->tds[i]);
 		} else {
 			usb_log_debug("Batch %p found error TD(%zu):%08x (%d).",
-			    ehci_batch->usb_batch, i,
+			    ehci_batch, i,
 			    ehci_batch->tds[i]->status,
-			    ehci_batch->usb_batch->error);
+			    ehci_batch->base.error);
 			/* Clear possible ED HALT */
 			qh_clear_halt(ehci_batch->qh);
@@ -244,11 +236,14 @@
 	}
 
-	assert(ehci_batch->usb_batch->transfered_size <=
-	    ehci_batch->usb_batch->buffer_size);
+	assert(ehci_batch->base.transfered_size <= ehci_batch->base.buffer_size);
+
+	if (ehci_batch->base.dir == USB_DIRECTION_IN)
+		memcpy(ehci_batch->base.buffer, ehci_batch->device_buffer, ehci_batch->base.transfered_size);
+
 	/* Clear TD pointers */
 	ehci_batch->qh->next = LINK_POINTER_TERM;
 	ehci_batch->qh->current = LINK_POINTER_TERM;
-	usb_log_debug("Batch %p complete: %s", ehci_batch->usb_batch,
-	    str_error(ehci_batch->usb_batch->error));
+	usb_log_debug("Batch %p complete: %s", ehci_batch,
+	    str_error(ehci_batch->base.error));
 
 	return true;
@@ -268,18 +263,18 @@
  *
  * @param[in] ehci_batch Batch structure to use.
- * @param[in] dir Communication direction
  *
  * Setup stage with toggle 0 and direction BOTH(SETUP_PID)
- * Data stage with alternating toggle and direction supplied by parameter.
- * Status stage with toggle 1 and direction supplied by parameter.
- */
-static void batch_control(ehci_transfer_batch_t *ehci_batch, usb_direction_t dir)
-{
-	assert(ehci_batch);
-	assert(ehci_batch->usb_batch);
+ * Data stage with alternating toggle and direction
+ * Status stage with toggle 1 and direction
+ */
+static void batch_control(ehci_transfer_batch_t *ehci_batch)
+{
+	assert(ehci_batch);
+
+	usb_direction_t dir = ehci_batch->base.dir;
 	assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
 
 	usb_log_debug2("Batch %p: Control QH(%"PRIxn"): "
-	    "%08x:%08x:%08x:%08x:%08x:%08x", ehci_batch->usb_batch,
+	    "%08x:%08x:%08x:%08x:%08x:%08x", ehci_batch,
 	    addr_to_phys(ehci_batch->qh),
 	    ehci_batch->qh->ep_char, ehci_batch->qh->ep_cap,
@@ -298,15 +293,15 @@
 	/* Setup stage */
 	td_init(ehci_batch->tds[0], ehci_batch->tds[1], USB_DIRECTION_BOTH,
-	    buffer, ehci_batch->usb_batch->setup_size, toggle, false);
+	    buffer, USB_SETUP_PACKET_SIZE, toggle, false);
 	usb_log_debug2("Batch %p: Created CONTROL SETUP TD(%"PRIxn"): "
-	    "%08x:%08x:%08x", ehci_batch->usb_batch,
+	    "%08x:%08x:%08x", ehci_batch,
 	    addr_to_phys(ehci_batch->tds[0]),
 	    ehci_batch->tds[0]->status, ehci_batch->tds[0]->next,
 	    ehci_batch->tds[0]->alternate);
-	buffer += ehci_batch->usb_batch->setup_size;
+	buffer += USB_SETUP_PACKET_SIZE;
 
 	/* Data stage */
 	size_t td_current = 1;
-	size_t remain_size = ehci_batch->usb_batch->buffer_size;
+	size_t remain_size = ehci_batch->base.buffer_size;
 	while (remain_size > 0) {
 		const size_t transfer_size =
@@ -318,5 +313,5 @@
 		    transfer_size, toggle, false);
 		usb_log_debug2("Batch %p: Created CONTROL DATA TD(%"PRIxn"): "
-		    "%08x:%08x:%08x", ehci_batch->usb_batch,
+		    "%08x:%08x:%08x", ehci_batch,
 		    addr_to_phys(ehci_batch->tds[td_current]),
 		    ehci_batch->tds[td_current]->status,
@@ -334,5 +329,5 @@
 	td_init(ehci_batch->tds[td_current], NULL, status_dir, NULL, 0, 1, true);
 	usb_log_debug2("Batch %p: Created CONTROL STATUS TD(%"PRIxn"): "
-	    "%08x:%08x:%08x", ehci_batch->usb_batch,
+	    "%08x:%08x:%08x", ehci_batch,
 	    addr_to_phys(ehci_batch->tds[td_current]),
 	    ehci_batch->tds[td_current]->status,
@@ -349,12 +344,10 @@
  * EHCI hw in ED.
  */
-static void batch_data(ehci_transfer_batch_t *ehci_batch, usb_direction_t dir)
-{
-	assert(ehci_batch);
-	assert(ehci_batch->usb_batch);
-	assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
+static void batch_data(ehci_transfer_batch_t *ehci_batch)
+{
+	assert(ehci_batch);
 
 	usb_log_debug2("Batch %p: Data QH(%"PRIxn"): "
-	    "%08x:%08x:%08x:%08x:%08x:%08x", ehci_batch->usb_batch,
+	    "%08x:%08x:%08x:%08x:%08x:%08x", ehci_batch,
 	    addr_to_phys(ehci_batch->qh),
 	    ehci_batch->qh->ep_char, ehci_batch->qh->ep_cap,
@@ -363,5 +356,5 @@
 
 	size_t td_current = 0;
-	size_t remain_size = ehci_batch->usb_batch->buffer_size;
+	size_t remain_size = ehci_batch->base.buffer_size;
 	char *buffer = ehci_batch->device_buffer;
 	while (remain_size > 0) {
@@ -373,8 +366,8 @@
 		    ehci_batch->tds[td_current],
 		    last ? NULL : ehci_batch->tds[td_current + 1],
-		    dir, buffer, transfer_size, -1, last);
+		    ehci_batch->base.dir, buffer, transfer_size, -1, last);
 
 		usb_log_debug2("Batch %p: DATA TD(%"PRIxn": %08x:%08x:%08x",
-		    ehci_batch->usb_batch,
+		    ehci_batch,
 		    addr_to_phys(ehci_batch->tds[td_current]),
 		    ehci_batch->tds[td_current]->status,
@@ -390,5 +383,5 @@
 
 /** Transfer setup table. */
-static void (*const batch_setup[])(ehci_transfer_batch_t*, usb_direction_t) =
+static void (*const batch_setup[])(ehci_transfer_batch_t*) =
 {
 	[USB_TRANSFER_CONTROL] = batch_control,
Index: uspace/drv/bus/usb/ehci/ehci_batch.h
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_batch.h	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/ehci/ehci_batch.h	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -45,4 +45,5 @@
 /** EHCI specific data required for USB transfer */
 typedef struct ehci_transfer_batch {
+	usb_transfer_batch_t base;
 	/** Link */
 	link_t link;
@@ -59,8 +60,9 @@
 } ehci_transfer_batch_t;
 
-ehci_transfer_batch_t * ehci_transfer_batch_get(usb_transfer_batch_t *batch);
-bool ehci_transfer_batch_is_complete(const ehci_transfer_batch_t *batch);
+ehci_transfer_batch_t * ehci_transfer_batch_create(endpoint_t *ep);
+int ehci_transfer_batch_prepare(ehci_transfer_batch_t *batch);
 void ehci_transfer_batch_commit(const ehci_transfer_batch_t *batch);
-void ehci_transfer_batch_finish_dispose(ehci_transfer_batch_t *batch);
+bool ehci_transfer_batch_check_completed(ehci_transfer_batch_t *batch);
+void ehci_transfer_batch_destroy(ehci_transfer_batch_t *batch);
 
 static inline ehci_transfer_batch_t *ehci_transfer_batch_from_link(link_t *l)
@@ -69,4 +71,12 @@
 	return list_get_instance(l, ehci_transfer_batch_t, link);
 }
+
+static inline ehci_transfer_batch_t * ehci_transfer_batch_get(usb_transfer_batch_t *usb_batch)
+{
+	assert(usb_batch);
+
+	return (ehci_transfer_batch_t *) usb_batch;
+}
+
 #endif
 /**
Index: uspace/drv/bus/usb/ehci/ehci_bus.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_bus.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/ehci/ehci_bus.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -41,4 +41,5 @@
 
 #include "ehci_bus.h"
+#include "ehci_batch.h"
 #include "hc.h"
 
@@ -140,5 +141,15 @@
 	hc_dequeue_endpoint(bus->hc, ep);
 	return EOK;
+}
 
+static usb_transfer_batch_t *ehci_bus_create_batch(bus_t *bus, endpoint_t *ep)
+{
+	ehci_transfer_batch_t *batch = ehci_transfer_batch_create(ep);
+	return &batch->base;
+}
+
+static void ehci_bus_destroy_batch(usb_transfer_batch_t *batch)
+{
+	ehci_transfer_batch_destroy(ehci_transfer_batch_get(batch));
 }
 
@@ -161,4 +172,7 @@
 	ops->release_endpoint = ehci_release_ep;
 
+	ops->create_batch = ehci_bus_create_batch;
+	ops->destroy_batch = ehci_bus_destroy_batch;
+
 	bus->hc = hc;
 
Index: uspace/drv/bus/usb/ehci/ehci_rh.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_rh.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/ehci/ehci_rh.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -146,5 +146,5 @@
 	const usb_target_t target = batch->ep->target;
 	batch->error = virthub_base_request(&instance->base, target,
-	    usb_transfer_batch_direction(batch), (void*)batch->setup_buffer,
+	    batch->dir, (void*) batch->setup.buffer,
 	    batch->buffer, batch->buffer_size, &batch->transfered_size);
 	if (batch->error == ENAK) {
@@ -157,8 +157,7 @@
 		instance->unfinished_interrupt_transfer = batch;
 	} else {
-		usb_transfer_batch_finish(batch, NULL);
-		usb_transfer_batch_destroy(batch);
 		usb_log_debug("RH(%p): BATCH(%p) virtual request complete: %s",
 		    instance, batch, str_error(batch->error));
+		usb_transfer_batch_finish(batch);
 	}
 	return EOK;
@@ -182,9 +181,7 @@
 		const usb_target_t target = batch->ep->target;
 		batch->error = virthub_base_request(&instance->base, target,
-		    usb_transfer_batch_direction(batch),
-		    (void*)batch->setup_buffer,
+		    batch->dir, (void*) batch->setup.buffer,
 		    batch->buffer, batch->buffer_size, &batch->transfered_size);
-		usb_transfer_batch_finish(batch, NULL);
-		usb_transfer_batch_destroy(batch);
+		usb_transfer_batch_finish(batch);
 	}
 	return EOK;
Index: uspace/drv/bus/usb/ehci/hc.c
===================================================================
--- uspace/drv/bus/usb/ehci/hc.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/ehci/hc.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -296,7 +296,10 @@
 		return ehci_rh_schedule(&instance->rh, batch);
 	}
+
 	ehci_transfer_batch_t *ehci_batch = ehci_transfer_batch_get(batch);
-	if (!ehci_batch)
-		return ENOMEM;
+
+	const int err = ehci_transfer_batch_prepare(ehci_batch);
+	if (err)
+		return err;
 
 	fibril_mutex_lock(&instance->guard);
@@ -343,7 +346,7 @@
 			    ehci_transfer_batch_from_link(current);
 
-			if (ehci_transfer_batch_is_complete(batch)) {
+			if (ehci_transfer_batch_check_completed(batch)) {
 				list_remove(current);
-				ehci_transfer_batch_finish_dispose(batch);
+				usb_transfer_batch_finish(&batch->base);
 			}
 		}
Index: uspace/drv/bus/usb/ohci/hc.c
===================================================================
--- uspace/drv/bus/usb/ohci/hc.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/ohci/hc.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -295,4 +295,8 @@
 		return ENOMEM;
 
+	const int err = ohci_transfer_batch_prepare(ohci_batch);
+	if (err)
+		return err;
+
 	fibril_mutex_lock(&instance->guard);
 	list_append(&ohci_batch->link, &instance->pending_batches);
@@ -346,7 +350,7 @@
 			    ohci_transfer_batch_from_link(current);
 
-			if (ohci_transfer_batch_is_complete(batch)) {
+			if (ohci_transfer_batch_check_completed(batch)) {
 				list_remove(current);
-				ohci_transfer_batch_finish_dispose(batch);
+				usb_transfer_batch_finish(&batch->base);
 			}
 
Index: uspace/drv/bus/usb/ohci/ohci_batch.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_batch.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/ohci/ohci_batch.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -47,5 +47,5 @@
 #include "ohci_bus.h"
 
-static void (*const batch_setup[])(ohci_transfer_batch_t*, usb_direction_t);
+static void (*const batch_setup[])(ohci_transfer_batch_t*);
 
 /** Safely destructs ohci_transfer_batch_t structure
@@ -53,8 +53,7 @@
  * @param[in] ohci_batch Instance to destroy.
  */
-static void ohci_transfer_batch_dispose(ohci_transfer_batch_t *ohci_batch)
-{
-	if (!ohci_batch)
-		return;
+void ohci_transfer_batch_destroy(ohci_transfer_batch_t *ohci_batch)
+{
+	assert(ohci_batch);
 	if (ohci_batch->tds) {
 		const ohci_endpoint_t *ohci_ep =
@@ -67,20 +66,6 @@
 		free(ohci_batch->tds);
 	}
-	usb_transfer_batch_destroy(ohci_batch->usb_batch);
 	free32(ohci_batch->device_buffer);
 	free(ohci_batch);
-}
-
-/** Finishes usb_transfer_batch and destroys the structure.
- *
- * @param[in] uhci_batch Instance to finish and destroy.
- */
-void ohci_transfer_batch_finish_dispose(ohci_transfer_batch_t *ohci_batch)
-{
-	assert(ohci_batch);
-	assert(ohci_batch->usb_batch);
-	usb_transfer_batch_finish(ohci_batch->usb_batch,
-	    ohci_batch->device_buffer + ohci_batch->usb_batch->setup_size);
-	ohci_transfer_batch_dispose(ohci_batch);
 }
 
@@ -90,12 +75,8 @@
  * @return Valid pointer if all structures were successfully created,
  * NULL otherwise.
- *
- * Determines the number of needed transfer descriptors (TDs).
- * Prepares a transport buffer (that is accessible by the hardware).
- * Initializes parameters needed for the transfer and callback.
- */
-ohci_transfer_batch_t * ohci_transfer_batch_get(usb_transfer_batch_t *usb_batch)
-{
-	assert(usb_batch);
+ */
+ohci_transfer_batch_t * ohci_transfer_batch_create(endpoint_t *ep)
+{
+	assert(ep);
 
 	ohci_transfer_batch_t *ohci_batch =
@@ -103,9 +84,30 @@
 	if (!ohci_batch) {
 		usb_log_error("Failed to allocate OHCI batch data.");
-		goto dispose;
-	}
+		return NULL;
+	}
+
+	usb_transfer_batch_init(&ohci_batch->base, ep);
 	link_initialize(&ohci_batch->link);
-	ohci_batch->td_count =
-	    (usb_batch->buffer_size + OHCI_TD_MAX_TRANSFER - 1)
+
+	return ohci_batch;
+}
+
+/** Prepares a batch to be sent.
+ *
+ * Determines the number of needed transfer descriptors (TDs).
+ * Prepares a transport buffer (that is accessible by the hardware).
+ * Initializes parameters needed for the transfer and callback.
+ */
+int ohci_transfer_batch_prepare(ohci_transfer_batch_t *ohci_batch)
+{
+	assert(ohci_batch);
+
+	const size_t setup_size = (ohci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
+		? USB_SETUP_PACKET_SIZE
+		: 0;
+
+	usb_transfer_batch_t *usb_batch = &ohci_batch->base;
+
+	ohci_batch->td_count = (usb_batch->buffer_size + OHCI_TD_MAX_TRANSFER - 1)
 	    / OHCI_TD_MAX_TRANSFER;
 	/* Control transfer need Setup and Status stage */
@@ -118,5 +120,5 @@
 	if (!ohci_batch->tds) {
 		usb_log_error("Failed to allocate OHCI transfer descriptors.");
-		goto dispose;
+		return ENOMEM;
 	}
 
@@ -129,8 +131,7 @@
 		if (!ohci_batch->tds[i]) {
 			usb_log_error("Failed to allocate TD %d.", i);
-			goto dispose;
+			return ENOMEM;
 		}
 	}
-
 
 	/* NOTE: OHCI is capable of handling buffer that crosses page boundaries
@@ -138,31 +139,25 @@
 	 * than two pages (the first page is computed using start pointer, the
 	 * other using the end pointer) */
-        if (usb_batch->setup_size + usb_batch->buffer_size > 0) {
+        if (setup_size + usb_batch->buffer_size > 0) {
 		/* Use one buffer for setup and data stage */
 		ohci_batch->device_buffer =
-		    malloc32(usb_batch->setup_size + usb_batch->buffer_size);
+		    malloc32(setup_size + usb_batch->buffer_size);
 		if (!ohci_batch->device_buffer) {
 			usb_log_error("Failed to allocate device buffer");
-			goto dispose;
+			return ENOMEM;
 		}
 		/* Copy setup data */
-                memcpy(ohci_batch->device_buffer, usb_batch->setup_buffer,
-		    usb_batch->setup_size);
+                memcpy(ohci_batch->device_buffer, usb_batch->setup.buffer, setup_size);
 		/* Copy generic data */
 		if (usb_batch->ep->direction != USB_DIRECTION_IN)
 			memcpy(
-			    ohci_batch->device_buffer + usb_batch->setup_size,
+			    ohci_batch->device_buffer + setup_size,
 			    usb_batch->buffer, usb_batch->buffer_size);
         }
-	ohci_batch->usb_batch = usb_batch;
-
-	const usb_direction_t dir = usb_transfer_batch_direction(usb_batch);
+
 	assert(batch_setup[usb_batch->ep->transfer_type]);
-	batch_setup[usb_batch->ep->transfer_type](ohci_batch, dir);
-
-	return ohci_batch;
-dispose:
-	ohci_transfer_batch_dispose(ohci_batch);
-	return NULL;
+	batch_setup[usb_batch->ep->transfer_type](ohci_batch);
+
+	return EOK;
 }
 
@@ -176,8 +171,7 @@
  * completes with the last TD.
  */
-bool ohci_transfer_batch_is_complete(const ohci_transfer_batch_t *ohci_batch)
-{
-	assert(ohci_batch);
-	assert(ohci_batch->usb_batch);
+bool ohci_transfer_batch_check_completed(ohci_transfer_batch_t *ohci_batch)
+{
+	assert(ohci_batch);
 
 	usb_log_debug("Batch %p checking %zu td(s) for completion.\n",
@@ -194,6 +188,5 @@
 
 	/* Assume all data got through */
-	ohci_batch->usb_batch->transfered_size =
-	    ohci_batch->usb_batch->buffer_size;
+	ohci_batch->base.transfered_size = ohci_batch->base.buffer_size;
 
 	/* Assume we will leave the last(unused) TD behind */
@@ -207,6 +200,6 @@
 		    ohci_batch->tds[i]->next, ohci_batch->tds[i]->be);
 
-		ohci_batch->usb_batch->error = td_error(ohci_batch->tds[i]);
-		if (ohci_batch->usb_batch->error == EOK) {
+		ohci_batch->base.error = td_error(ohci_batch->tds[i]);
+		if (ohci_batch->base.error == EOK) {
 			/* If the TD got all its data through, it will report
 			 * 0 bytes remain, the sole exception is INPUT with
@@ -221,5 +214,5 @@
 			 * we leave the very last(unused) TD behind.
 			 */
-			ohci_batch->usb_batch->transfered_size
+			ohci_batch->base.transfered_size
 			    -= td_remain_size(ohci_batch->tds[i]);
 		} else {
@@ -252,9 +245,12 @@
 		}
 	}
-	assert(ohci_batch->usb_batch->transfered_size <=
-	    ohci_batch->usb_batch->buffer_size);
+	assert(ohci_batch->base.transfered_size <=
+	    ohci_batch->base.buffer_size);
+
+	if (ohci_batch->base.dir == USB_DIRECTION_IN)
+		memcpy(ohci_batch->base.buffer, ohci_batch->device_buffer, ohci_batch->base.transfered_size);
 
 	/* Store the remaining TD */
-	ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ohci_batch->usb_batch->ep);
+	ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ohci_batch->base.ep);
 	assert(ohci_ep);
 	ohci_ep->td = ohci_batch->tds[leave_td];
@@ -286,9 +282,11 @@
  * Status stage with toggle 1 and direction supplied by parameter.
  */
-static void batch_control(ohci_transfer_batch_t *ohci_batch, usb_direction_t dir)
-{
-	assert(ohci_batch);
-	assert(ohci_batch->usb_batch);
+static void batch_control(ohci_transfer_batch_t *ohci_batch)
+{
+	assert(ohci_batch);
+
+	usb_direction_t dir = ohci_batch->base.dir;
 	assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
+
 	usb_log_debug("Using ED(%p): %08x:%08x:%08x:%08x.\n", ohci_batch->ed,
 	    ohci_batch->ed->status, ohci_batch->ed->td_tail,
@@ -307,9 +305,9 @@
 	td_init(
 	    ohci_batch->tds[0], ohci_batch->tds[1], USB_DIRECTION_BOTH,
-	    buffer, ohci_batch->usb_batch->setup_size, toggle);
+	    buffer, USB_SETUP_PACKET_SIZE, toggle);
 	usb_log_debug("Created CONTROL SETUP TD: %08x:%08x:%08x:%08x.\n",
 	    ohci_batch->tds[0]->status, ohci_batch->tds[0]->cbp,
 	    ohci_batch->tds[0]->next, ohci_batch->tds[0]->be);
-	buffer += ohci_batch->usb_batch->setup_size;
+	buffer += USB_SETUP_PACKET_SIZE;
 
 	/* Data stage */
@@ -361,8 +359,9 @@
  * OHCI hw in ED.
  */
-static void batch_data(ohci_transfer_batch_t *ohci_batch, usb_direction_t dir)
-{
-	assert(ohci_batch);
-	assert(ohci_batch->usb_batch);
+static void batch_data(ohci_transfer_batch_t *ohci_batch)
+{
+	assert(ohci_batch);
+
+	usb_direction_t dir = ohci_batch->base.dir;
 	assert(dir == USB_DIRECTION_IN || dir == USB_DIRECTION_OUT);
 	usb_log_debug("Using ED(%p): %08x:%08x:%08x:%08x.\n", ohci_batch->ed,
@@ -401,5 +400,5 @@
 
 /** Transfer setup table. */
-static void (*const batch_setup[])(ohci_transfer_batch_t*, usb_direction_t) =
+static void (*const batch_setup[])(ohci_transfer_batch_t*) =
 {
 	[USB_TRANSFER_CONTROL] = batch_control,
Index: uspace/drv/bus/usb/ohci/ohci_batch.h
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_batch.h	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/ohci/ohci_batch.h	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -45,4 +45,6 @@
 /** OHCI specific data required for USB transfer */
 typedef struct ohci_transfer_batch {
+	usb_transfer_batch_t base;
+
 	/** Link */
 	link_t link;
@@ -59,8 +61,9 @@
 } ohci_transfer_batch_t;
 
-ohci_transfer_batch_t * ohci_transfer_batch_get(usb_transfer_batch_t *batch);
-bool ohci_transfer_batch_is_complete(const ohci_transfer_batch_t *batch);
+ohci_transfer_batch_t * ohci_transfer_batch_create(endpoint_t *batch);
+int ohci_transfer_batch_prepare(ohci_transfer_batch_t *ohci_batch);
 void ohci_transfer_batch_commit(const ohci_transfer_batch_t *batch);
-void ohci_transfer_batch_finish_dispose(ohci_transfer_batch_t *batch);
+bool ohci_transfer_batch_check_completed(ohci_transfer_batch_t *batch);
+void ohci_transfer_batch_destroy(ohci_transfer_batch_t *ohci_batch);
 
 static inline ohci_transfer_batch_t *ohci_transfer_batch_from_link(link_t *l)
@@ -69,4 +72,12 @@
 	return list_get_instance(l, ohci_transfer_batch_t, link);
 }
+
+static inline ohci_transfer_batch_t * ohci_transfer_batch_get(usb_transfer_batch_t *usb_batch)
+{
+	assert(usb_batch);
+
+	return (ohci_transfer_batch_t *) usb_batch;
+}
+
 #endif
 /**
Index: uspace/drv/bus/usb/ohci/ohci_bus.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_bus.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/ohci/ohci_bus.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -40,4 +40,5 @@
 
 #include "ohci_bus.h"
+#include "ohci_batch.h"
 #include "hc.h"
 
@@ -141,5 +142,15 @@
 	hc_dequeue_endpoint(bus->hc, ep);
 	return EOK;
+}
 
+static usb_transfer_batch_t *ohci_bus_create_batch(bus_t *bus, endpoint_t *ep)
+{
+	ohci_transfer_batch_t *batch = ohci_transfer_batch_create(ep);
+	return &batch->base;
+}
+
+static void ohci_bus_destroy_batch(usb_transfer_batch_t *batch)
+{
+	ohci_transfer_batch_destroy(ohci_transfer_batch_get(batch));
 }
 
@@ -161,4 +172,7 @@
 	ops->release_endpoint = ohci_release_ep;
 
+	ops->create_batch = ohci_bus_create_batch;
+	ops->destroy_batch = ohci_bus_destroy_batch;
+
 	bus->hc = hc;
 
Index: uspace/drv/bus/usb/ohci/ohci_rh.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_rh.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/ohci/ohci_rh.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -180,5 +180,5 @@
 	const usb_target_t target = batch->ep->target;
 	batch->error = virthub_base_request(&instance->base, target,
-	    usb_transfer_batch_direction(batch), (void*)batch->setup_buffer,
+	    batch->dir, &batch->setup.packet,
 	    batch->buffer, batch->buffer_size, &batch->transfered_size);
 	if (batch->error == ENAK) {
@@ -189,6 +189,5 @@
 		instance->unfinished_interrupt_transfer = batch;
 	} else {
-		usb_transfer_batch_finish(batch, NULL);
-		usb_transfer_batch_destroy(batch);
+		usb_transfer_batch_finish(batch);
 	}
 	return EOK;
@@ -210,9 +209,7 @@
 		const usb_target_t target = batch->ep->target;
 		batch->error = virthub_base_request(&instance->base, target,
-		    usb_transfer_batch_direction(batch),
-		    (void*)batch->setup_buffer,
+		    batch->dir, &batch->setup.packet,
 		    batch->buffer, batch->buffer_size, &batch->transfered_size);
-		usb_transfer_batch_finish(batch, NULL);
-		usb_transfer_batch_destroy(batch);
+		usb_transfer_batch_finish(batch);
 	}
 	return EOK;
Index: uspace/drv/bus/usb/uhci/hc.c
===================================================================
--- uspace/drv/bus/usb/uhci/hc.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/uhci/hc.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -177,5 +177,5 @@
 			uhci_transfer_batch_t *batch =
 			    uhci_transfer_batch_from_link(current);
-			uhci_transfer_batch_finish_dispose(batch);
+			usb_transfer_batch_finish(&batch->base);
 		}
 	}
@@ -309,4 +309,15 @@
 }
 
+static usb_transfer_batch_t *create_transfer_batch(bus_t *bus, endpoint_t *ep)
+{
+	uhci_transfer_batch_t *batch = uhci_transfer_batch_create(ep);
+	return &batch->base;
+}
+
+static void destroy_transfer_batch(usb_transfer_batch_t *batch)
+{
+	uhci_transfer_batch_destroy(uhci_transfer_batch_get(batch));
+}
+
 /** Initialize UHCI hc memory structures.
  *
@@ -326,4 +337,7 @@
 	if ((err = usb2_bus_init(&instance->bus, BANDWIDTH_AVAILABLE_USB11, bandwidth_count_usb11)))
 		return err;
+
+	instance->bus.base.ops.create_batch = create_transfer_batch;
+	instance->bus.base.ops.destroy_batch = destroy_transfer_batch;
 
 	/* Init USB frame list page */
@@ -450,9 +464,13 @@
 		return uhci_rh_schedule(&instance->rh, batch);
 
-	uhci_transfer_batch_t *uhci_batch = uhci_transfer_batch_get(batch);
+	uhci_transfer_batch_t *uhci_batch = (uhci_transfer_batch_t *) batch;
 	if (!uhci_batch) {
 		usb_log_error("Failed to create UHCI transfer structures.\n");
 		return ENOMEM;
 	}
+
+	const int err = uhci_transfer_batch_prepare(uhci_batch);
+	if (err)
+		return err;
 
 	transfer_list_t *list =
Index: uspace/drv/bus/usb/uhci/transfer_list.c
===================================================================
--- uspace/drv/bus/usb/uhci/transfer_list.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/uhci/transfer_list.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -167,5 +167,5 @@
 		    uhci_transfer_batch_from_link(current);
 
-		if (uhci_transfer_batch_is_complete(batch)) {
+		if (uhci_transfer_batch_check_completed(batch)) {
 			/* Save for processing */
 			transfer_list_remove_batch(instance, batch);
Index: uspace/drv/bus/usb/uhci/uhci_batch.c
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_batch.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/uhci/uhci_batch.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -50,33 +50,17 @@
 #define DEFAULT_ERROR_COUNT 3
 
-/** Safely destructs uhci_transfer_batch_t structure.
+/** Transfer batch setup table. */
+static void (*const batch_setup[])(uhci_transfer_batch_t*);
+
+/** Destroys uhci_transfer_batch_t structure.
  *
  * @param[in] uhci_batch Instance to destroy.
  */
-static void uhci_transfer_batch_dispose(uhci_transfer_batch_t *uhci_batch)
-{
-	if (uhci_batch) {
-		usb_transfer_batch_destroy(uhci_batch->usb_batch);
-		free32(uhci_batch->device_buffer);
-		free(uhci_batch);
-	}
-}
-
-/** Finishes usb_transfer_batch and destroys the structure.
- *
- * @param[in] uhci_batch Instance to finish and destroy.
- */
-void uhci_transfer_batch_finish_dispose(uhci_transfer_batch_t *uhci_batch)
+void uhci_transfer_batch_destroy(uhci_transfer_batch_t *uhci_batch)
 {
 	assert(uhci_batch);
-	assert(uhci_batch->usb_batch);
-	assert(!link_in_use(&uhci_batch->link));
-	usb_transfer_batch_finish(uhci_batch->usb_batch,
-	    uhci_transfer_batch_data_buffer(uhci_batch));
-	uhci_transfer_batch_dispose(uhci_batch);
-}
-
-/** Transfer batch setup table. */
-static void (*const batch_setup[])(uhci_transfer_batch_t*, usb_direction_t);
+	free32(uhci_batch->device_buffer);
+	free(uhci_batch);
+}
 
 /** Allocate memory and initialize internal data structure.
@@ -85,4 +69,21 @@
  * @return Valid pointer if all structures were successfully created,
  * NULL otherwise.
+ */
+uhci_transfer_batch_t * uhci_transfer_batch_create(endpoint_t *ep)
+{
+	uhci_transfer_batch_t *uhci_batch =
+	    calloc(1, sizeof(uhci_transfer_batch_t));
+	if (!uhci_batch) {
+		usb_log_error("Failed to allocate UHCI batch.\n");
+		return NULL;
+	}
+
+	usb_transfer_batch_init(&uhci_batch->base, ep);
+
+	link_initialize(&uhci_batch->link);
+	return uhci_batch;
+}
+
+/* Prepares batch for commiting.
  *
  * Determines the number of needed transfer descriptors (TDs).
@@ -90,31 +91,28 @@
  * Initializes parameters needed for the transfer and callback.
  */
-uhci_transfer_batch_t * uhci_transfer_batch_get(usb_transfer_batch_t *usb_batch)
+int uhci_transfer_batch_prepare(uhci_transfer_batch_t *uhci_batch)
 {
 	static_assert((sizeof(td_t) % 16) == 0);
-#define CHECK_NULL_DISPOSE_RETURN(ptr, message...) \
-	if (ptr == NULL) { \
-		usb_log_error(message); \
-		uhci_transfer_batch_dispose(uhci_batch); \
-		return NULL; \
-	} else (void)0
-
-	uhci_transfer_batch_t *uhci_batch =
-	    calloc(1, sizeof(uhci_transfer_batch_t));
-	CHECK_NULL_DISPOSE_RETURN(uhci_batch,
-	    "Failed to allocate UHCI batch.\n");
-	link_initialize(&uhci_batch->link);
-	uhci_batch->td_count =
-	    (usb_batch->buffer_size + usb_batch->ep->max_packet_size - 1)
-	    / usb_batch->ep->max_packet_size;
+
+	usb_transfer_batch_t *usb_batch = &uhci_batch->base;
+
+	uhci_batch->td_count = (usb_batch->buffer_size + usb_batch->ep->max_packet_size - 1)
+		/ usb_batch->ep->max_packet_size;
+
 	if (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
 		uhci_batch->td_count += 2;
 	}
 
+	const size_t setup_size = (uhci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
+		? USB_SETUP_PACKET_SIZE
+		: 0;
+
 	const size_t total_size = (sizeof(td_t) * uhci_batch->td_count)
-	    + sizeof(qh_t) + usb_batch->setup_size + usb_batch->buffer_size;
+	    + sizeof(qh_t) + setup_size + usb_batch->buffer_size;
 	uhci_batch->device_buffer = malloc32(total_size);
-	CHECK_NULL_DISPOSE_RETURN(uhci_batch->device_buffer,
-	    "Failed to allocate UHCI buffer.\n");
+	if (!uhci_batch->device_buffer) {
+		usb_log_error("Failed to allocate UHCI buffer.\n");
+		return ENOMEM;
+	}
 	memset(uhci_batch->device_buffer, 0, total_size);
 
@@ -130,21 +128,18 @@
 	    + sizeof(qh_t);
 	/* Copy SETUP packet data to the device buffer */
-	memcpy(dest, usb_batch->setup_buffer, usb_batch->setup_size);
-	dest += usb_batch->setup_size;
+	memcpy(dest, usb_batch->setup.buffer, setup_size);
+	dest += setup_size;
 	/* Copy generic data unless they are provided by the device */
 	if (usb_batch->ep->direction != USB_DIRECTION_IN) {
 		memcpy(dest, usb_batch->buffer, usb_batch->buffer_size);
 	}
-	uhci_batch->usb_batch = usb_batch;
 	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
 	    " memory structures ready.\n", usb_batch,
 	    USB_TRANSFER_BATCH_ARGS(*usb_batch));
 
-	const usb_direction_t dir = usb_transfer_batch_direction(usb_batch);
-
 	assert(batch_setup[usb_batch->ep->transfer_type]);
-	batch_setup[usb_batch->ep->transfer_type](uhci_batch, dir);
-
-	return uhci_batch;
+	batch_setup[usb_batch->ep->transfer_type](uhci_batch);
+
+	return EOK;
 }
 
@@ -158,8 +153,7 @@
  * is reached.
  */
-bool uhci_transfer_batch_is_complete(const uhci_transfer_batch_t *uhci_batch)
+bool uhci_transfer_batch_check_completed(uhci_transfer_batch_t *uhci_batch)
 {
 	assert(uhci_batch);
-	assert(uhci_batch->usb_batch);
 
 	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
@@ -168,5 +162,5 @@
 	    USB_TRANSFER_BATCH_ARGS(*uhci_batch->usb_batch),
 	    uhci_batch->td_count);
-	uhci_batch->usb_batch->transfered_size = 0;
+	uhci_batch->base.transfered_size = 0;
 
 	for (size_t i = 0;i < uhci_batch->td_count; ++i) {
@@ -175,7 +169,7 @@
 		}
 
-		uhci_batch->usb_batch->error = td_status(&uhci_batch->tds[i]);
-		if (uhci_batch->usb_batch->error != EOK) {
-			assert(uhci_batch->usb_batch->ep != NULL);
+		uhci_batch->base.error = td_status(&uhci_batch->tds[i]);
+		if (uhci_batch->base.error != EOK) {
+			assert(uhci_batch->base.ep != NULL);
 
 			usb_log_debug("Batch %p found error TD(%zu->%p):%"
@@ -184,5 +178,5 @@
 			td_print_status(&uhci_batch->tds[i]);
 
-			endpoint_toggle_set(uhci_batch->usb_batch->ep,
+			endpoint_toggle_set(uhci_batch->base.ep,
 			    td_toggle(&uhci_batch->tds[i]));
 			if (i > 0)
@@ -191,5 +185,5 @@
 		}
 
-		uhci_batch->usb_batch->transfered_size
+		uhci_batch->base.transfered_size
 		    += td_act_size(&uhci_batch->tds[i]);
 		if (td_is_short(&uhci_batch->tds[i]))
@@ -197,6 +191,6 @@
 	}
 substract_ret:
-	uhci_batch->usb_batch->transfered_size
-	    -= uhci_batch->usb_batch->setup_size;
+	if (uhci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
+		uhci_batch->base.transfered_size -= USB_SETUP_PACKET_SIZE;
 	return true;
 }
@@ -216,9 +210,9 @@
  * The last transfer is marked with IOC flag.
  */
-static void batch_data(uhci_transfer_batch_t *uhci_batch, usb_direction_t dir)
+static void batch_data(uhci_transfer_batch_t *uhci_batch)
 {
 	assert(uhci_batch);
-	assert(uhci_batch->usb_batch);
-	assert(uhci_batch->usb_batch->ep);
+
+	usb_direction_t dir = uhci_batch->base.dir;
 	assert(dir == USB_DIRECTION_OUT || dir == USB_DIRECTION_IN);
 
@@ -226,13 +220,13 @@
 	const usb_packet_id pid = direction_pids[dir];
 	const bool low_speed =
-	    uhci_batch->usb_batch->ep->speed == USB_SPEED_LOW;
-	const size_t mps = uhci_batch->usb_batch->ep->max_packet_size;
-	const usb_target_t target = uhci_batch->usb_batch->ep->target;
-
-	int toggle = endpoint_toggle_get(uhci_batch->usb_batch->ep);
+	    uhci_batch->base.ep->speed == USB_SPEED_LOW;
+	const size_t mps = uhci_batch->base.ep->max_packet_size;
+	const usb_target_t target = uhci_batch->base.ep->target;
+
+	int toggle = endpoint_toggle_get(uhci_batch->base.ep);
 	assert(toggle == 0 || toggle == 1);
 
 	size_t td = 0;
-	size_t remain_size = uhci_batch->usb_batch->buffer_size;
+	size_t remain_size = uhci_batch->base.buffer_size;
 	char *buffer = uhci_transfer_batch_data_buffer(uhci_batch);
 
@@ -254,10 +248,10 @@
 	}
 	td_set_ioc(&uhci_batch->tds[td - 1]);
-	endpoint_toggle_set(uhci_batch->usb_batch->ep, toggle);
+	endpoint_toggle_set(uhci_batch->base.ep, toggle);
 	usb_log_debug2(
 	    "Batch %p %s %s " USB_TRANSFER_BATCH_FMT " initialized.\n", \
 	    uhci_batch->usb_batch,
-	    usb_str_transfer_type(uhci_batch->usb_batch->ep->transfer_type),
-	    usb_str_direction(uhci_batch->usb_batch->ep->direction),
+	    usb_str_transfer_type(uhci_batch->base.ep->transfer_type),
+	    usb_str_direction(uhci_batch->base.ep->direction),
 	    USB_TRANSFER_BATCH_ARGS(*uhci_batch->usb_batch));
 }
@@ -274,9 +268,9 @@
  * The last transfer is marked with IOC.
  */
-static void batch_control(uhci_transfer_batch_t *uhci_batch, usb_direction_t dir)
+static void batch_control(uhci_transfer_batch_t *uhci_batch)
 {
 	assert(uhci_batch);
-	assert(uhci_batch->usb_batch);
-	assert(uhci_batch->usb_batch->ep);
+
+	usb_direction_t dir = uhci_batch->base.dir;
 	assert(dir == USB_DIRECTION_OUT || dir == USB_DIRECTION_IN);
 	assert(uhci_batch->td_count >= 2);
@@ -289,12 +283,12 @@
 	const usb_packet_id status_stage_pid = status_stage_pids[dir];
 	const bool low_speed =
-	    uhci_batch->usb_batch->ep->speed == USB_SPEED_LOW;
-	const size_t mps = uhci_batch->usb_batch->ep->max_packet_size;
-	const usb_target_t target = uhci_batch->usb_batch->ep->target;
+	    uhci_batch->base.ep->speed == USB_SPEED_LOW;
+	const size_t mps = uhci_batch->base.ep->max_packet_size;
+	const usb_target_t target = uhci_batch->base.ep->target;
 
 	/* setup stage */
 	td_init(
 	    &uhci_batch->tds[0], DEFAULT_ERROR_COUNT,
-	    uhci_batch->usb_batch->setup_size, 0, false,
+	    USB_SETUP_PACKET_SIZE, 0, false,
 	    low_speed, target, USB_PID_SETUP,
 	    uhci_transfer_batch_setup_buffer(uhci_batch), &uhci_batch->tds[1]);
@@ -303,5 +297,5 @@
 	size_t td = 1;
 	unsigned toggle = 1;
-	size_t remain_size = uhci_batch->usb_batch->buffer_size;
+	size_t remain_size = uhci_batch->base.buffer_size;
 	char *buffer = uhci_transfer_batch_data_buffer(uhci_batch);
 
@@ -333,5 +327,5 @@
 }
 
-static void (*const batch_setup[])(uhci_transfer_batch_t*, usb_direction_t) =
+static void (*const batch_setup[])(uhci_transfer_batch_t*) =
 {
 	[USB_TRANSFER_CONTROL] = batch_control,
Index: uspace/drv/bus/usb/uhci/uhci_batch.h
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_batch.h	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/uhci/uhci_batch.h	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -43,4 +43,5 @@
 #include <stddef.h>
 #include <usb/host/usb_transfer_batch.h>
+#include <usb/host/endpoint.h>
 
 #include "hw_struct/queue_head.h"
@@ -49,4 +50,6 @@
 /** UHCI specific data required for USB transfer */
 typedef struct uhci_transfer_batch {
+	usb_transfer_batch_t base;
+
 	/** Queue head
 	 * This QH is used to maintain UHCI schedule structure and the element
@@ -66,7 +69,8 @@
 } uhci_transfer_batch_t;
 
-uhci_transfer_batch_t * uhci_transfer_batch_get(usb_transfer_batch_t *batch);
-void uhci_transfer_batch_finish_dispose(uhci_transfer_batch_t *uhci_batch);
-bool uhci_transfer_batch_is_complete(const uhci_transfer_batch_t *uhci_batch);
+uhci_transfer_batch_t * uhci_transfer_batch_create(endpoint_t *ep);
+int uhci_transfer_batch_prepare(uhci_transfer_batch_t *uhci_batch);
+bool uhci_transfer_batch_check_completed(uhci_transfer_batch_t *uhci_batch);
+void uhci_transfer_batch_destroy(uhci_transfer_batch_t *uhci_batch);
 
 /** Get offset to setup buffer accessible to the HC hw.
@@ -93,5 +97,5 @@
 	assert(uhci_batch->usb_batch);
 	return uhci_transfer_batch_setup_buffer(uhci_batch) +
-	    uhci_batch->usb_batch->setup_size;
+	    (uhci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL ? USB_SETUP_PACKET_SIZE : 0);
 }
 
@@ -107,5 +111,5 @@
 	uhci_batch->usb_batch->error = EINTR;
 	uhci_batch->usb_batch->transfered_size = 0;
-	uhci_transfer_batch_finish_dispose(uhci_batch);
+	usb_transfer_batch_finish(&uhci_batch->base);
 }
 
@@ -120,4 +124,10 @@
 }
 
+static inline uhci_transfer_batch_t *uhci_transfer_batch_get(usb_transfer_batch_t *b)
+{
+	assert(b);
+	return (uhci_transfer_batch_t *) b;
+}
+
 #endif
 
Index: uspace/drv/bus/usb/uhci/uhci_rh.c
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_rh.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/uhci/uhci_rh.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -39,4 +39,5 @@
 #include <usb/classes/hub.h>
 #include <usb/request.h>
+#include <usb/host/endpoint.h>
 #include <usb/usb.h>
 
@@ -106,5 +107,5 @@
 	do {
 		batch->error = virthub_base_request(&instance->base, target,
-		    usb_transfer_batch_direction(batch), (void*)batch->setup_buffer,
+		    batch->dir, (void*) batch->setup.buffer,
 		    batch->buffer, batch->buffer_size, &batch->transfered_size);
 		if (batch->error == ENAK)
@@ -113,6 +114,5 @@
 		//ENAK is technically an error condition
 	} while (batch->error == ENAK);
-	usb_transfer_batch_finish(batch, NULL);
-	usb_transfer_batch_destroy(batch);
+	usb_transfer_batch_finish(batch);
 	return EOK;
 }
Index: uspace/drv/bus/usb/vhc/transfer.c
===================================================================
--- uspace/drv/bus/usb/vhc/transfer.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/vhc/transfer.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -44,9 +44,9 @@
 		return false;
 	}
-	if (usb_transfer_batch_direction(transfer->batch) != USB_DIRECTION_OUT) {
-		return false;
-	}
-	const usb_device_request_setup_packet_t *setup =
-	    (void*)transfer->batch->setup_buffer;
+	if (transfer->batch->dir != USB_DIRECTION_OUT) {
+		return false;
+	}
+	const usb_device_request_setup_packet_t *setup
+		= &transfer->batch->setup.packet;
 	if (setup->request_type != 0) {
 		return false;
@@ -63,11 +63,11 @@
 {
 	int rc;
-	
-	const usb_direction_t dir = usb_transfer_batch_direction(batch);
+
+	const usb_direction_t dir = batch->dir;
 
 	if (batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
 		if (dir == USB_DIRECTION_IN) {
 			rc = usbvirt_control_read(dev,
-			    batch->setup_buffer, batch->setup_size,
+			    batch->setup.buffer, USB_SETUP_PACKET_SIZE,
 			    batch->buffer, batch->buffer_size,
 			    actual_data_size);
@@ -75,5 +75,5 @@
 			assert(dir == USB_DIRECTION_OUT);
 			rc = usbvirt_control_write(dev,
-			    batch->setup_buffer, batch->setup_size,
+			    batch->setup.buffer, USB_SETUP_PACKET_SIZE,
 			    batch->buffer, batch->buffer_size);
 		}
@@ -100,10 +100,10 @@
 	int rc;
 
-	const usb_direction_t dir = usb_transfer_batch_direction(batch);
+	const usb_direction_t dir = batch->dir;
 
 	if (batch->ep->transfer_type == USB_TRANSFER_CONTROL) {
 		if (dir == USB_DIRECTION_IN) {
 			rc = usbvirt_ipc_send_control_read(sess,
-			    batch->setup_buffer, batch->setup_size,
+			    batch->setup.buffer, USB_SETUP_PACKET_SIZE,
 			    batch->buffer, batch->buffer_size,
 			    actual_data_size);
@@ -111,5 +111,5 @@
 			assert(dir == USB_DIRECTION_OUT);
 			rc = usbvirt_ipc_send_control_write(sess,
-			    batch->setup_buffer, batch->setup_size,
+			    batch->setup.buffer, USB_SETUP_PACKET_SIZE,
 			    batch->buffer, batch->buffer_size);
 		}
@@ -149,7 +149,7 @@
 	assert(transfer);
 	assert(transfer->batch);
-	usb_transfer_batch_finish_error(transfer->batch, NULL,
-	    data_transfer_size, outcome);
-	usb_transfer_batch_destroy(transfer->batch);
+	transfer->batch->error = outcome;
+	transfer->batch->transfered_size = data_transfer_size;
+	usb_transfer_batch_finish(transfer->batch);
 	free(transfer);
 }
@@ -236,5 +236,5 @@
 			if (is_set_address_transfer(transfer)) {
 				usb_device_request_setup_packet_t *setup =
-				    (void*) transfer->batch->setup_buffer;
+				    (void*) transfer->batch->setup.buffer;
 				dev->address = setup->value;
 				usb_log_debug2("Address changed to %d\n",
Index: uspace/drv/bus/usb/xhci/bus.c
===================================================================
--- uspace/drv/bus/usb/xhci/bus.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/xhci/bus.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -48,4 +48,5 @@
 #include "bus.h"
 #include "endpoint.h"
+#include "transfers.h"
 
 /** Element of the hash table. */
@@ -310,4 +311,15 @@
 }
 
+static usb_transfer_batch_t *create_batch(bus_t *bus, endpoint_t *ep)
+{
+	xhci_transfer_t *transfer = xhci_transfer_create(ep);
+	return &transfer->batch;
+}
+
+static void destroy_batch(usb_transfer_batch_t *batch)
+{
+	xhci_transfer_destroy(xhci_transfer_from_batch(batch));
+}
+
 static const bus_ops_t xhci_bus_ops = {
 	.enumerate_device = enumerate_device,
@@ -329,4 +341,7 @@
 	.endpoint_get_toggle = endpoint_get_toggle,
 	.endpoint_set_toggle = endpoint_set_toggle,
+
+	.create_batch = create_batch,
+	.destroy_batch = destroy_batch,
 };
 
Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -37,4 +37,5 @@
 #include <str_error.h>
 #include <usb/debug.h>
+#include <usb/host/endpoint.h>
 #include <usb/host/utils/malloc32.h>
 #include "debug.h"
@@ -472,16 +473,5 @@
 	}
 
-	switch (batch->ep->transfer_type) {
-	case USB_TRANSFER_CONTROL:
-		return xhci_schedule_control_transfer(hc, batch);
-	case USB_TRANSFER_ISOCHRONOUS:
-		return xhci_schedule_isochronous_transfer(hc, batch);
-	case USB_TRANSFER_BULK:
-		return xhci_schedule_bulk_transfer(hc, batch);
-	case USB_TRANSFER_INTERRUPT:
-		return xhci_schedule_interrupt_transfer(hc, batch);
-	}
-
-	return EOK;
+	return xhci_transfer_schedule(hc, batch);
 }
 
Index: uspace/drv/bus/usb/xhci/main.c
===================================================================
--- uspace/drv/bus/usb/xhci/main.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/xhci/main.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -186,5 +186,5 @@
 {
 	log_init(NAME);
-	logctl_set_log_level(NAME, LVL_DEBUG);
+	logctl_set_log_level(NAME, LVL_DEBUG2);
 	return ddf_driver_main(&xhci_driver);
 }
Index: uspace/drv/bus/usb/xhci/transfers.c
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -108,53 +108,46 @@
 }
 
-xhci_transfer_t* xhci_transfer_alloc(usb_transfer_batch_t* batch) {
-	xhci_transfer_t* transfer = malloc(sizeof(xhci_transfer_t));
+xhci_transfer_t* xhci_transfer_create(endpoint_t* ep) {
+	xhci_transfer_t* transfer = calloc(1, sizeof(xhci_transfer_t));
 	if (!transfer)
 		return NULL;
 
-	memset(transfer, 0, sizeof(xhci_transfer_t));
-	transfer->batch = batch;
+	usb_transfer_batch_init(&transfer->batch, ep);
+
 	link_initialize(&transfer->link);
-	transfer->hc_buffer = batch->buffer_size > 0 ? malloc32(batch->buffer_size) : NULL;
 
 	return transfer;
 }
 
-void xhci_transfer_fini(xhci_transfer_t* transfer) {
-	if (transfer) {
-		if (transfer->batch->buffer_size > 0)
-			free32(transfer->hc_buffer);
-
-		usb_transfer_batch_destroy(transfer->batch);
-
-		free(transfer);
-	}
-}
-
-int xhci_schedule_control_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch)
-{
-	if (!batch->setup_size) {
-		usb_log_error("Missing setup packet for the control transfer.");
-		return EINVAL;
-	}
-	if (batch->ep->transfer_type != USB_TRANSFER_CONTROL) {
-		/* This method only works for control transfers. */
-		usb_log_error("Attempted to schedule a control transfer to non control endpoint.");
-		return EINVAL;
-	}
-
-	usb_device_request_setup_packet_t* setup =
-		(usb_device_request_setup_packet_t*) batch->setup_buffer;
-
-	/* For the TRB formats, see xHCI specification 6.4.1.2 */
-	xhci_transfer_t *transfer = xhci_transfer_alloc(batch);
-
-	if (!transfer->direction) {
-		// Sending stuff from host to device, we need to copy the actual data.
-		memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
-	}
-
-        xhci_trb_t trbs[3];
-        int trbs_used = 0;
+void xhci_transfer_destroy(xhci_transfer_t* transfer)
+{
+	assert(transfer);
+
+	if (transfer->hc_buffer)
+		free32(transfer->hc_buffer);
+
+	free(transfer);
+}
+
+static xhci_trb_ring_t *get_ring(xhci_hc_t *hc, xhci_transfer_t *transfer)
+{
+	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(transfer->batch.ep);
+	uint8_t slot_id = xhci_ep->device->slot_id;
+
+	xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[transfer->batch.ep->target.endpoint];
+	assert(ring);
+	return ring;
+}
+
+static int schedule_control(xhci_hc_t* hc, xhci_transfer_t* transfer)
+{
+	usb_transfer_batch_t *batch = &transfer->batch;
+	xhci_trb_ring_t *ring = get_ring(hc, transfer);
+	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(transfer->batch.ep);
+
+	usb_device_request_setup_packet_t* setup = &batch->setup.packet;
+
+	xhci_trb_t trbs[3];
+	int trbs_used = 0;
 
 	xhci_trb_t *trb_setup = trbs + trbs_used++;
@@ -178,6 +171,6 @@
 	xhci_trb_t *trb_data = NULL;
 	if (setup->length > 0) {
-                trb_data = trbs + trbs_used++;
-                xhci_trb_clean(trb_data);
+		trb_data = trbs + trbs_used++;
+		xhci_trb_clean(trb_data);
 
 		trb_data->parameter = addr_to_phys(transfer->hc_buffer);
@@ -191,7 +184,7 @@
 		TRB_CTRL_SET_TRB_TYPE(*trb_data, XHCI_TRB_TYPE_DATA_STAGE);
 
-		transfer->direction = REQUEST_TYPE_IS_DEVICE_TO_HOST(setup->request_type)
+		int stage_dir = 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, stage_dir);
 	}
 
@@ -207,44 +200,16 @@
 	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);
-        uint8_t slot_id = xhci_ep->device->slot_id;
-        xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[batch->ep->target.endpoint];
-
-        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);
-
 	// Issue a Configure Endpoint command, if needed.
 	if (configure_endpoint_needed(setup)) {
-		// TODO: figure out the best time to issue this command
-                // 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;
-	}
-
-	const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbels start at 1 */
-	return hc_ring_doorbell(hc, slot_id, target);
-}
-
-int xhci_schedule_bulk_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch)
-{
-	if (batch->setup_size) {
-		usb_log_warning("Setup packet present for a bulk transfer. Ignored.");
-	}
-	if (batch->ep->transfer_type != USB_TRANSFER_BULK) {
-		/* This method only works for bulk transfers. */
-		usb_log_error("Attempted to schedule a bulk transfer to non bulk endpoint.");
-		return EINVAL;
-	}
-
-	xhci_transfer_t *transfer = xhci_transfer_alloc(batch);
-	if (!transfer->direction) {
-		// Sending stuff from host to device, we need to copy the actual data.
-		memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
-	}
-
+		const int err = xhci_device_configure(xhci_ep->device, hc);
+		if (err)
+			return err;
+	}
+
+	return xhci_trb_ring_enqueue_multiple(ring, trbs, trbs_used, &transfer->interrupt_trb_phys);
+}
+
+static int schedule_bulk(xhci_hc_t* hc, xhci_transfer_t *transfer)
+{
 	xhci_trb_t trb;
 	xhci_trb_clean(&trb);
@@ -252,5 +217,5 @@
 
 	// data size (sent for OUT, or buffer size)
-	TRB_CTRL_SET_XFER_LEN(trb, batch->buffer_size);
+	TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_size);
 	// FIXME: TD size 4.11.2.4
 	TRB_CTRL_SET_TD_SIZE(trb, 1);
@@ -261,33 +226,13 @@
 	TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
 
-	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
+	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(transfer->batch.ep);
 	uint8_t slot_id = xhci_ep->device->slot_id;
-	xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[batch->ep->target.endpoint];
-
-	xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
-	list_append(&transfer->link, &hc->transfers);
-
-	// TODO: target = endpoint | stream_id << 16
-	const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
-	return hc_ring_doorbell(hc, slot_id, target);
-}
-
-int xhci_schedule_interrupt_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch)
-{
-	if (batch->setup_size) {
-		usb_log_warning("Setup packet present for a interrupt transfer. Ignored.");
-	}
-	if (batch->ep->transfer_type != USB_TRANSFER_INTERRUPT) {
-		/* This method only works for interrupt transfers. */
-		usb_log_error("Attempted to schedule a interrupt transfer to non interrupt endpoint.");
-		return EINVAL;
-	}
-
-	xhci_transfer_t *transfer = xhci_transfer_alloc(batch);
-	if (!transfer->direction) {
-		// Sending stuff from host to device, we need to copy the actual data.
-		memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
-	}
-
+	xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[transfer->batch.ep->target.endpoint];
+
+	return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
+}
+
+static int schedule_interrupt(xhci_hc_t* hc, xhci_transfer_t* transfer)
+{
 	xhci_trb_t trb;
 	xhci_trb_clean(&trb);
@@ -295,5 +240,5 @@
 
 	// data size (sent for OUT, or buffer size)
-	TRB_CTRL_SET_XFER_LEN(trb, batch->buffer_size);
+	TRB_CTRL_SET_XFER_LEN(trb, transfer->batch.buffer_size);
 	// FIXME: TD size 4.11.2.4
 	TRB_CTRL_SET_TD_SIZE(trb, 1);
@@ -304,29 +249,16 @@
 	TRB_CTRL_SET_TRB_TYPE(trb, XHCI_TRB_TYPE_NORMAL);
 
-	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
+	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(transfer->batch.ep);
 	uint8_t slot_id = xhci_ep->device->slot_id;
-	xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[batch->ep->target.endpoint];
-
-	xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
-	list_append(&transfer->link, &hc->transfers);
-
-	const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
-	return hc_ring_doorbell(hc, slot_id, target);
-}
-
-int xhci_schedule_isochronous_transfer(xhci_hc_t* hc, usb_transfer_batch_t* batch)
-{
-	if (batch->setup_size) {
-		usb_log_warning("Setup packet present for a isochronous transfer. Ignored.");
-	}
-	if (batch->ep->transfer_type != USB_TRANSFER_ISOCHRONOUS) {
-		/* This method only works for isochronous transfers. */
-		usb_log_error("Attempted to schedule a isochronous transfer to non isochronous endpoint.");
-		return EINVAL;
-	}
-
-        /* TODO: Implement me. */
-        usb_log_error("Isochronous transfers are not yet implemented!");
-        return ENOTSUP;
+	xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[transfer->batch.ep->target.endpoint];
+
+	return xhci_trb_ring_enqueue(ring, &trb, &transfer->interrupt_trb_phys);
+}
+
+static int schedule_isochronous(xhci_hc_t* hc, xhci_transfer_t* transfer)
+{
+	/* TODO: Implement me. */
+	usb_log_error("Isochronous transfers are not yet implemented!");
+	return ENOTSUP;
 }
 
@@ -352,10 +284,59 @@
 
 	list_remove(transfer_link);
-	usb_transfer_batch_t *batch = transfer->batch;
-
-	const int err = (TRB_COMPLETION_CODE(*trb) == XHCI_TRBC_SUCCESS) ? EOK : ENAK;
-	const size_t size = batch->buffer_size - TRB_TRANSFER_LENGTH(*trb);
-	usb_transfer_batch_finish_error(batch, transfer->hc_buffer, size, err);
-	xhci_transfer_fini(transfer);
+	usb_transfer_batch_t *batch = &transfer->batch;
+
+	batch->error = (TRB_COMPLETION_CODE(*trb) == XHCI_TRBC_SUCCESS) ? EOK : ENAK;
+	batch->transfered_size = batch->buffer_size - TRB_TRANSFER_LENGTH(*trb);
+
+	if (batch->dir == USB_DIRECTION_IN) {
+		assert(batch->buffer);
+		assert(batch->transfered_size <= batch->buffer_size);
+		memcpy(batch->buffer, transfer->hc_buffer, batch->transfered_size);
+	}
+
+	usb_transfer_batch_finish(batch);
 	return EOK;
 }
+
+typedef int (*transfer_handler)(xhci_hc_t *, xhci_transfer_t *);
+
+static const transfer_handler transfer_handlers[] = {
+	[USB_TRANSFER_CONTROL] = schedule_control,
+	[USB_TRANSFER_ISOCHRONOUS] = schedule_isochronous,
+	[USB_TRANSFER_BULK] = schedule_bulk,
+	[USB_TRANSFER_INTERRUPT] = schedule_interrupt,
+};
+
+int xhci_transfer_schedule(xhci_hc_t *hc, usb_transfer_batch_t *batch)
+{
+	assert(hc);
+
+	xhci_transfer_t *transfer = xhci_transfer_from_batch(batch);
+	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
+	uint8_t slot_id = xhci_ep->device->slot_id;
+
+	assert(xhci_ep);
+	assert(slot_id);
+
+	const usb_transfer_type_t type = batch->ep->transfer_type;
+	assert(type >= 0 && type < ARRAY_SIZE(transfer_handlers));
+	assert(transfer_handlers[type]);
+
+	if (batch->buffer_size > 0) {
+		transfer->hc_buffer = malloc32(batch->buffer_size);
+	}
+
+	if (batch->dir != USB_DIRECTION_IN) {
+		// Sending stuff from host to device, we need to copy the actual data.
+		memcpy(transfer->hc_buffer, batch->buffer, batch->buffer_size);
+	}
+
+	const int err = transfer_handlers[batch->ep->transfer_type](hc, transfer);
+	if (err)
+		return err;
+
+	list_append(&transfer->link, &hc->transfers);
+
+	const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
+	return hc_ring_doorbell(hc, slot_id, target);
+}
Index: uspace/drv/bus/usb/xhci/transfers.h
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.h	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/drv/bus/usb/xhci/transfers.h	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -34,16 +34,22 @@
  */
 
+#ifndef XHCI_TRANSFERS_H
+#define XHCI_TRANSFERS_H
+
+#include <usb/host/usb_transfer_batch.h>
+
 #include "hc.h"
 #include "trb_ring.h"
 
 typedef struct {
+	usb_transfer_batch_t batch;
 	link_t link;
 
-	uintptr_t interrupt_trb_phys;
 	uint8_t direction;
 
-	usb_transfer_batch_t* batch;
+	void* hc_buffer;                    /* Virtual address of the buffer start. */
+	uintptr_t hc_buffer_phys;
 
-	void* hc_buffer;                    /* Virtual address of the buffer start. */
+	uintptr_t interrupt_trb_phys;
 } xhci_transfer_t;
 
@@ -51,11 +57,17 @@
 void xhci_fini_transfers(xhci_hc_t*);
 
-xhci_transfer_t* xhci_transfer_alloc(usb_transfer_batch_t*);
-void xhci_transfer_fini(xhci_transfer_t*);
+xhci_transfer_t* xhci_transfer_create(endpoint_t *);
+int xhci_transfer_schedule(xhci_hc_t*, usb_transfer_batch_t *);
+int xhci_handle_transfer_event(xhci_hc_t*, xhci_trb_t*);
+void xhci_transfer_destroy(xhci_transfer_t*);
 
-int xhci_handle_transfer_event(xhci_hc_t*, xhci_trb_t*);
+static inline xhci_transfer_t *xhci_transfer_from_batch(usb_transfer_batch_t *batch)
+{
+	assert(batch);
+	return (xhci_transfer_t *) batch;
+}
 
-int xhci_schedule_control_transfer(xhci_hc_t*, usb_transfer_batch_t*);
-int xhci_schedule_bulk_transfer(xhci_hc_t*, usb_transfer_batch_t*);
-int xhci_schedule_interrupt_transfer(xhci_hc_t*, usb_transfer_batch_t*);
-int xhci_schedule_isochronous_transfer(xhci_hc_t* , usb_transfer_batch_t* );
+/**
+ * @}
+ */
+#endif
Index: uspace/lib/usb/include/usb/request.h
===================================================================
--- uspace/lib/usb/include/usb/request.h	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/lib/usb/include/usb/request.h	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -109,5 +109,12 @@
 int assert[(sizeof(usb_device_request_setup_packet_t) == 8) ? 1: -1];
 
-int usb_request_needs_toggle_reset(
+/** How much toggles needs to be reset */
+typedef enum {
+	RESET_NONE,
+	RESET_EP,
+	RESET_ALL
+} toggle_reset_mode_t;
+
+toggle_reset_mode_t usb_request_get_toggle_reset_mode(
     const usb_device_request_setup_packet_t *request);
 
Index: uspace/lib/usb/src/usb.c
===================================================================
--- uspace/lib/usb/src/usb.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/lib/usb/src/usb.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -128,5 +128,5 @@
  *
  */
-int usb_request_needs_toggle_reset(
+toggle_reset_mode_t usb_request_get_toggle_reset_mode(
     const usb_device_request_setup_packet_t *request)
 {
@@ -139,5 +139,5 @@
 		if ((request->request_type == 0x2) &&
 		    (request->value == USB_FEATURE_ENDPOINT_HALT))
-			return uint16_usb2host(request->index);
+			return RESET_EP;
 		break;
 	case USB_DEVREQ_SET_CONFIGURATION:
@@ -149,10 +149,11 @@
 		 * interface of an already setup device. */
 		if (!(request->request_type & SETUP_REQUEST_TYPE_DEVICE_TO_HOST))
-			return 0;
+			return RESET_ALL;
 		break;
 	default:
 		break;
 	}
-	return -1;
+
+	return RESET_NONE;
 }
 
Index: uspace/lib/usbhost/include/usb/host/bus.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/bus.h	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/lib/usbhost/include/usb/host/bus.h	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -33,5 +33,5 @@
  *
  * The purpose of this structure is to keep information about connected devices
- * and enpoints, manage available bandwidth and the toggle bit flipping.
+ * and endpoints, manage available bandwidth and the toggle bit flipping.
  *
  * The generic implementation is provided for USB 1 and 2 in usb2_bus.c. Some
@@ -53,4 +53,5 @@
 typedef struct bus bus_t;
 typedef struct ddf_fun ddf_fun_t;
+typedef struct usb_transfer_batch usb_transfer_batch_t;
 
 typedef struct device {
@@ -85,4 +86,7 @@
 	int (*release_endpoint)(bus_t *, endpoint_t *);
 	endpoint_t *(*find_endpoint)(bus_t *, usb_target_t, usb_direction_t);
+	void (*destroy_endpoint)(endpoint_t *);			/**< Optional */
+	bool (*endpoint_get_toggle)(endpoint_t *);		/**< Optional */
+	void (*endpoint_set_toggle)(endpoint_t *, bool);	/**< Optional */
 
 	int (*request_address)(bus_t *, usb_address_t*, bool, usb_speed_t);
@@ -93,8 +97,6 @@
 	size_t (*count_bw) (endpoint_t *, size_t);
 
-	/* Endpoint ops, optional (have generic fallback) */
-	void (*destroy_endpoint)(endpoint_t *);
-	bool (*endpoint_get_toggle)(endpoint_t *);
-	void (*endpoint_set_toggle)(endpoint_t *, bool);
+	usb_transfer_batch_t *(*create_batch)(bus_t *, endpoint_t *); /**< Optional */
+	void (*destroy_batch)(usb_transfer_batch_t *);	/**< Optional */
 } bus_ops_t;
 
@@ -135,4 +137,5 @@
 int bus_release_address(bus_t *, usb_address_t);
 
+
 static inline int bus_reserve_default_address(bus_t *bus, usb_speed_t speed) {
 	usb_address_t addr = USB_ADDRESS_DEFAULT;
Index: uspace/lib/usbhost/include/usb/host/endpoint.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/endpoint.h	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/lib/usbhost/include/usb/host/endpoint.h	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -48,4 +48,5 @@
 typedef struct bus bus_t;
 typedef struct device device_t;
+typedef struct usb_transfer_batch usb_transfer_batch_t;
 
 /** Host controller side endpoint structure. */
Index: uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -37,8 +37,7 @@
 #define LIBUSBHOST_HOST_USB_TRANSFER_BATCH_H
 
-#include <usb/host/endpoint.h>
 #include <usb/usb.h>
+#include <usb/request.h>
 
-#include <assert.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -47,36 +46,44 @@
 #define USB_SETUP_PACKET_SIZE 8
 
+typedef struct endpoint endpoint_t;
+typedef struct bus bus_t;
+typedef struct usb_transfer_batch usb_transfer_batch_t;
+
+/** Callback to be called on transfer. */
+typedef int (*usb_transfer_batch_callback_t)(usb_transfer_batch_t *);
+
 /** Structure stores additional data needed for communication with EP */
 typedef struct usb_transfer_batch {
 	/** Endpoint used for communication */
 	endpoint_t *ep;
-	/** Function called on completion (IN version) */
-	usbhc_iface_transfer_in_callback_t callback_in;
-	/** Function called on completion (OUT version) */
-	usbhc_iface_transfer_out_callback_t callback_out;
-	/** Argument to pass to the completion function */
-	void *arg;
+	/** Size reported to be sent */
+	size_t expected_size;
+
+	/** Direction of the transfer */
+	usb_direction_t dir;
+
+	/** Function called on completion */
+	usb_transfer_batch_callback_t on_complete;
+	/** Arbitrary data for the handler */
+	void *on_complete_data;
+
+	/** Place to store SETUP data needed by control transfers */
+	union {
+		char buffer [USB_SETUP_PACKET_SIZE];
+		usb_device_request_setup_packet_t packet;
+		uint64_t packed;
+	} setup;
+
+	/** Resetting the Toggle */
+	toggle_reset_mode_t toggle_reset_mode;
+
 	/** Place for data to send/receive */
 	char *buffer;
 	/** Size of memory pointed to by buffer member */
 	size_t buffer_size;
-	/** Place to store SETUP data needed by control transfers */
-	char setup_buffer[USB_SETUP_PACKET_SIZE];
-	/** Used portion of setup_buffer member
-	 *
-	 * SETUP buffer must be 8 bytes for control transfers and is left
-	 * unused for all other transfers. Thus, this field is either 0 or 8.
-	 */
-	size_t setup_size;
 
-	/** Actually used portion of the buffer
-	 * This member is never accessed by functions provided in this header,
-	 * with the exception of usb_transfer_batch_finish. For external use.
-	 */
+	/** Actually used portion of the buffer */
 	size_t transfered_size;
-	/** Indicates success/failure of the communication
-	 * This member is never accessed by functions provided in this header,
-	 * with the exception of usb_transfer_batch_finish. For external use.
-	 */
+	/** Indicates success/failure of the communication */
 	int error;
 } usb_transfer_batch_t;
@@ -95,56 +102,15 @@
 	(batch).buffer_size, (batch).ep->max_packet_size
 
+void usb_transfer_batch_init(usb_transfer_batch_t *, endpoint_t *);
+void usb_transfer_batch_finish(usb_transfer_batch_t *);
 
-usb_transfer_batch_t * usb_transfer_batch_create(
-    endpoint_t *ep,
-    char *buffer,
-    size_t buffer_size,
-    uint64_t setup_buffer,
-    usbhc_iface_transfer_in_callback_t func_in,
-    usbhc_iface_transfer_out_callback_t func_out,
-    void *arg
-);
-void usb_transfer_batch_destroy(const usb_transfer_batch_t *instance);
+usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *);
+void usb_transfer_batch_destroy(usb_transfer_batch_t *);
 
-void usb_transfer_batch_finish_error(const usb_transfer_batch_t *instance,
-    const void* data, size_t size, int error);
-
-/** Finish batch using stored error value and transferred size.
- *
- * @param[in] instance Batch structure to use.
- * @param[in] data Data to copy to the output buffer.
+/** Provided to ease the transition. Wraps old-style handlers into a new one.
  */
-static inline void usb_transfer_batch_finish(
-    const usb_transfer_batch_t *instance, const void* data)
-{
-	assert(instance);
-	usb_transfer_batch_finish_error(
-	    instance, data, instance->transfered_size, instance->error);
-}
-
-/** Determine batch direction based on the callbacks present
- * @param[in] instance Batch structure to use, non-null.
- * @return USB_DIRECTION_IN, or USB_DIRECTION_OUT.
- */
-static inline usb_direction_t usb_transfer_batch_direction(
-    const usb_transfer_batch_t *instance)
-{
-	assert(instance);
-	if (instance->callback_in) {
-		assert(instance->callback_out == NULL);
-		assert(instance->ep == NULL
-		    || instance->ep->transfer_type == USB_TRANSFER_CONTROL
-		    || instance->ep->direction == USB_DIRECTION_IN);
-		return USB_DIRECTION_IN;
-	}
-	if (instance->callback_out) {
-		assert(instance->callback_in == NULL);
-		assert(instance->ep == NULL
-		    || instance->ep->transfer_type == USB_TRANSFER_CONTROL
-		    || instance->ep->direction == USB_DIRECTION_OUT);
-		return USB_DIRECTION_OUT;
-	}
-	assert(false);
-}
+extern void usb_transfer_batch_set_old_handlers(usb_transfer_batch_t *,
+	usbhc_iface_transfer_in_callback_t,
+	usbhc_iface_transfer_out_callback_t, void *);
 
 #endif
Index: uspace/lib/usbhost/src/hcd.c
===================================================================
--- uspace/lib/usbhost/src/hcd.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/lib/usbhost/src/hcd.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -69,28 +69,6 @@
 }
 
-typedef struct {
-	void *original_data;
-	usbhc_iface_transfer_out_callback_t original_callback;
-	usb_target_t target;
-	hcd_t *hcd;
-} toggle_t;
-
-static void toggle_reset_callback(int retval, void *arg)
-{
-	assert(arg);
-	toggle_t *toggle = arg;
-	if (retval == EOK) {
-		usb_log_debug2("Reseting toggle on %d:%d.\n",
-		    toggle->target.address, toggle->target.endpoint);
-		bus_reset_toggle(toggle->hcd->bus,
-		    toggle->target, toggle->target.endpoint == 0);
-	}
-
-	toggle->original_callback(retval, toggle->original_data);
-}
-
 /** Prepare generic usb_transfer_batch and schedule it.
  * @param hcd Host controller driver.
- * @param fun DDF fun
  * @param target address and endpoint number.
  * @param setup_data Data to use in setup stage (Control communication type)
@@ -101,9 +79,8 @@
  * @return Error code.
  */
-int hcd_send_batch(
-    hcd_t *hcd, usb_target_t target, usb_direction_t direction,
-    void *data, size_t size, uint64_t setup_data,
-    usbhc_iface_transfer_in_callback_t in,
-    usbhc_iface_transfer_out_callback_t out, void *arg, const char* name)
+int hcd_send_batch(hcd_t *hcd, usb_target_t target, usb_direction_t direction,
+	void *data, size_t size, uint64_t setup_data,
+	usbhc_iface_transfer_in_callback_t in, usbhc_iface_transfer_out_callback_t out,
+	void *arg, const char *name)
 {
 	assert(hcd);
@@ -132,30 +109,21 @@
 	}
 
-	/* Check for commands that reset toggle bit */
-	if (ep->transfer_type == USB_TRANSFER_CONTROL) {
-		const int reset_toggle = usb_request_needs_toggle_reset(
-		    (usb_device_request_setup_packet_t *) &setup_data);
-		if (reset_toggle >= 0) {
-			assert(out);
-			toggle_t *toggle = malloc(sizeof(toggle_t));
-			if (!toggle)
-				return ENOMEM;
-			toggle->target.address = target.address;
-			toggle->target.endpoint = reset_toggle;
-			toggle->original_callback = out;
-			toggle->original_data = arg;
-			toggle->hcd = hcd;
-
-			arg = toggle;
-			out = toggle_reset_callback;
-		}
-	}
-
-	usb_transfer_batch_t *batch = usb_transfer_batch_create(
-	    ep, data, size, setup_data, in, out, arg);
+	usb_transfer_batch_t *batch = usb_transfer_batch_create(ep);
 	if (!batch) {
 		usb_log_error("Failed to create transfer batch.\n");
 		return ENOMEM;
 	}
+
+	batch->buffer = data;
+	batch->buffer_size = size;
+	batch->setup.packed = setup_data;
+	batch->dir = direction;
+
+	/* Check for commands that reset toggle bit */
+	if (ep->transfer_type == USB_TRANSFER_CONTROL)
+		batch->toggle_reset_mode
+			= usb_request_get_toggle_reset_mode(&batch->setup.packet);
+
+	usb_transfer_batch_set_old_handlers(batch, in, out, arg);
 
 	const int ret = hcd->ops.schedule(hcd, batch);
Index: uspace/lib/usbhost/src/usb_transfer_batch.c
===================================================================
--- uspace/lib/usbhost/src/usb_transfer_batch.c	(revision 74b852bcea4149baee916fef8e27effeeb124476)
+++ uspace/lib/usbhost/src/usb_transfer_batch.c	(revision 5fd9c308725be28e98aaeb91d9f3367e470de4f1)
@@ -34,103 +34,135 @@
 
 #include <usb/host/usb_transfer_batch.h>
+#include <usb/host/endpoint.h>
+#include <usb/host/bus.h>
 #include <usb/debug.h>
 
 #include <assert.h>
 #include <errno.h>
-#include <macros.h>
-#include <mem.h>
-#include <stdlib.h>
-#include <usbhc_iface.h>
 
-/** Allocate and initialize usb_transfer_batch structure.
- * @param ep endpoint used by the transfer batch.
- * @param buffer data to send/recieve.
- * @param buffer_size Size of data buffer.
- * @param setup_buffer Data to send in SETUP stage of control transfer.
- * @param func_in callback on IN transfer completion.
- * @param func_out callback on OUT transfer completion.
- * @param fun DDF function (passed to callback function).
- * @param arg Argument to pass to the callback function.
- * @param private_data driver specific per batch data.
- * @param private_data_dtor Function to properly destroy private_data.
- * @return Pointer to valid usb_transfer_batch_t structure, NULL on failure.
+
+/** Create a batch on given endpoint.
  */
-usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *ep, char *buffer,
-    size_t buffer_size,
-    uint64_t setup_buffer,
-    usbhc_iface_transfer_in_callback_t func_in,
-    usbhc_iface_transfer_out_callback_t func_out,
-    void *arg)
+usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *ep)
 {
-	if (func_in == NULL && func_out == NULL)
-		return NULL;
-	if (func_in != NULL && func_out != NULL)
-		return NULL;
+	assert(ep);
+	assert(ep->bus);
 
-	usb_transfer_batch_t *instance = malloc(sizeof(usb_transfer_batch_t));
-	if (instance) {
-		instance->ep = ep;
-		instance->callback_in = func_in;
-		instance->callback_out = func_out;
-		instance->arg = arg;
-		instance->buffer = buffer;
-		instance->buffer_size = buffer_size;
-		instance->setup_size = 0;
-		instance->transfered_size = 0;
-		instance->error = EOK;
-		if (ep && ep->transfer_type == USB_TRANSFER_CONTROL) {
-			memcpy(instance->setup_buffer, &setup_buffer,
-			    USB_SETUP_PACKET_SIZE);
-			instance->setup_size = USB_SETUP_PACKET_SIZE;
-		}
-		if (instance->ep)
-			endpoint_use(instance->ep);
-	}
-	return instance;
+	usb_transfer_batch_t *batch;
+	if (ep->bus->ops.create_batch)
+		batch = ep->bus->ops.create_batch(ep->bus, ep);
+	else
+		batch = malloc(sizeof(usb_transfer_batch_t));
+
+	return batch;
 }
 
-/** Correctly dispose all used data structures.
- *
- * @param[in] instance Batch structure to use.
+/** Initialize given batch structure.
  */
-void usb_transfer_batch_destroy(const usb_transfer_batch_t *instance)
+void usb_transfer_batch_init(usb_transfer_batch_t *batch, endpoint_t *ep)
 {
-	if (!instance)
-		return;
-	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " disposing.\n",
-	    instance, USB_TRANSFER_BATCH_ARGS(*instance));
-	if (instance->ep) {
-		endpoint_release(instance->ep);
-	}
-	free(instance);
+	memset(batch, 0, sizeof(*batch));
+
+	batch->ep = ep;
+
+	endpoint_use(ep);
 }
 
-/** Prepare data and call the right callback.
+/** Call the handler of the batch.
  *
- * @param[in] instance Batch structure to use.
- * @param[in] data Data to copy to the output buffer.
- * @param[in] size Size of @p data.
- * @param[in] error Error value to use.
+ * @param[in] batch Batch structure to use.
  */
-void usb_transfer_batch_finish_error(const usb_transfer_batch_t *instance,
-    const void *data, size_t size, int error)
+static int batch_complete(usb_transfer_batch_t *batch)
 {
-	assert(instance);
-	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " finishing.\n",
-	    instance, USB_TRANSFER_BATCH_ARGS(*instance));
+	assert(batch);
 
-	/* NOTE: Only one of these pointers should be set. */
-        if (instance->callback_out) {
-		instance->callback_out(error, instance->arg);
+	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " completed.\n",
+	    batch, USB_TRANSFER_BATCH_ARGS(*batch));
+
+	if (batch->error == EOK && batch->toggle_reset_mode != RESET_NONE) {
+		usb_log_debug2("Reseting %s due to transaction of %d:%d.\n",
+		    batch->toggle_reset_mode == RESET_ALL ? "all EPs toggle" : "EP toggle",
+		    batch->ep->target.address, batch->ep->target.endpoint);
+		bus_reset_toggle(batch->ep->bus,
+		    batch->ep->target, batch->toggle_reset_mode == RESET_ALL);
 	}
 
-        if (instance->callback_in) {
-		/* We care about the data and there are some to copy */
-		const size_t safe_size = min(size, instance->buffer_size);
-		if (data) {
-	                memcpy(instance->buffer, data, safe_size);
-		}
-		instance->callback_in(error, safe_size, instance->arg);
-	}
+	return batch->on_complete
+		? batch->on_complete(batch)
+		: EOK;
+}
+
+/** Destroy the batch.
+ *
+ * @param[in] batch Batch structure to use.
+ */
+void usb_transfer_batch_destroy(usb_transfer_batch_t *batch)
+{
+	assert(batch);
+	assert(batch->ep);
+	assert(batch->ep->bus);
+
+	usb_log_debug2("batch %p " USB_TRANSFER_BATCH_FMT " disposing.\n",
+	    batch, USB_TRANSFER_BATCH_ARGS(*batch));
+
+	bus_t *bus = batch->ep->bus;
+	if (bus->ops.destroy_batch)
+		bus->ops.destroy_batch(batch);
+	else
+		free(batch);
+
+	endpoint_release(batch->ep);
+}
+
+/** Finish a transfer batch: call handler, destroy batch, release endpoint.
+ *
+ * Call only after the batch have been scheduled && completed!
+ *
+ * @param[in] batch Batch structure to use.
+ */
+void usb_transfer_batch_finish(usb_transfer_batch_t *batch)
+{
+	if (!batch_complete(batch))
+		usb_log_warning("failed to complete batch %p!", batch);
+
+	usb_transfer_batch_destroy(batch);
+}
+
+
+struct old_handler_wrapper_data {
+	usbhc_iface_transfer_in_callback_t in_callback;
+	usbhc_iface_transfer_out_callback_t out_callback;
+	void *arg;
+};
+
+static int old_handler_wrapper(usb_transfer_batch_t *batch)
+{
+	struct old_handler_wrapper_data *data = batch->on_complete_data;
+
+	assert(data);
+
+	if (data->out_callback)
+		data->out_callback(batch->error, data->arg);
+
+	if (data->in_callback)
+		data->in_callback(batch->error, batch->transfered_size, data->arg);
+
+	free(data);
+	return EOK;
+}
+
+void usb_transfer_batch_set_old_handlers(usb_transfer_batch_t *batch,
+	usbhc_iface_transfer_in_callback_t in_callback,
+	usbhc_iface_transfer_out_callback_t out_callback,
+	void *arg)
+{
+	struct old_handler_wrapper_data *data = malloc(sizeof(*data));
+
+	data->in_callback = in_callback;
+	data->out_callback = out_callback;
+	data->arg = arg;
+
+	batch->on_complete = old_handler_wrapper;
+	batch->on_complete_data = data;
 }
 /**
