Index: uspace/drv/bus/usb/uhci/hc.c
===================================================================
--- uspace/drv/bus/usb/uhci/hc.c	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/drv/bus/usb/uhci/hc.c	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -177,5 +177,5 @@
 			uhci_transfer_batch_t *batch =
 			    uhci_transfer_batch_from_link(current);
-			uhci_transfer_batch_finish(batch);
+			usb_transfer_batch_finish(&batch->base);
 		}
 	}
Index: uspace/drv/bus/usb/uhci/transfer_list.c
===================================================================
--- uspace/drv/bus/usb/uhci/transfer_list.c	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/drv/bus/usb/uhci/transfer_list.c	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -115,4 +115,12 @@
 	assert(instance);
 	assert(uhci_batch);
+
+	endpoint_t *ep = uhci_batch->base.ep;
+
+	/* First, wait until the endpoint is free to use */
+	fibril_mutex_lock(&ep->guard);
+	endpoint_activate_locked(ep, &uhci_batch->base);
+	fibril_mutex_unlock(&ep->guard);
+
 	usb_log_debug2("Batch %p adding to queue %s.\n",
 	    uhci_batch, instance->name);
@@ -189,5 +197,5 @@
 		    uhci_transfer_batch_from_link(current);
 		transfer_list_remove_batch(instance, batch);
-		uhci_transfer_batch_abort(batch);
+		endpoint_abort(batch->base.ep);
 	}
 	fibril_mutex_unlock(&instance->guard);
Index: uspace/drv/bus/usb/uhci/uhci_batch.c
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_batch.c	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/drv/bus/usb/uhci/uhci_batch.c	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -64,13 +64,4 @@
 }
 
-void uhci_transfer_batch_finish(uhci_transfer_batch_t *batch)
-{
-	if (batch->base.dir == USB_DIRECTION_IN) {
-		assert(batch->base.transfered_size <= batch->base.buffer_size);
-		memcpy(batch->base.buffer, uhci_transfer_batch_data_buffer(batch), batch->base.transfered_size);
-	}
-	usb_transfer_batch_finish(&batch->base);
-}
-
 /** Allocate memory and initialize internal data structure.
  *
@@ -113,5 +104,5 @@
 	}
 
-	const size_t setup_size = (uhci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
+	const size_t setup_size = (usb_batch->ep->transfer_type == USB_TRANSFER_CONTROL)
 		? USB_SETUP_PACKET_SIZE
 		: 0;
@@ -165,10 +156,11 @@
 {
 	assert(uhci_batch);
+	usb_transfer_batch_t *batch = &uhci_batch->base;
 
 	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT
 	    " checking %zu transfer(s) for completion.\n",
-	    uhci_batch, USB_TRANSFER_BATCH_ARGS(uhci_batch->base),
+	    uhci_batch, USB_TRANSFER_BATCH_ARGS(*batch),
 	    uhci_batch->td_count);
-	uhci_batch->base.transfered_size = 0;
+	batch->transfered_size = 0;
 
 	for (size_t i = 0;i < uhci_batch->td_count; ++i) {
@@ -177,7 +169,7 @@
 		}
 
-		uhci_batch->base.error = td_status(&uhci_batch->tds[i]);
-		if (uhci_batch->base.error != EOK) {
-			assert(uhci_batch->base.ep != NULL);
+		batch->error = td_status(&uhci_batch->tds[i]);
+		if (batch->error != EOK) {
+			assert(batch->ep != NULL);
 
 			usb_log_debug("Batch %p found error TD(%zu->%p):%"
@@ -186,5 +178,5 @@
 			td_print_status(&uhci_batch->tds[i]);
 
-			endpoint_toggle_set(uhci_batch->base.ep,
+			endpoint_toggle_set(batch->ep,
 			    td_toggle(&uhci_batch->tds[i]));
 			if (i > 0)
@@ -193,5 +185,5 @@
 		}
 
-		uhci_batch->base.transfered_size
+		batch->transfered_size
 		    += td_act_size(&uhci_batch->tds[i]);
 		if (td_is_short(&uhci_batch->tds[i]))
@@ -199,6 +191,19 @@
 	}
 substract_ret:
-	if (uhci_batch->base.ep->transfer_type == USB_TRANSFER_CONTROL)
-		uhci_batch->base.transfered_size -= USB_SETUP_PACKET_SIZE;
+	if (batch->ep->transfer_type == USB_TRANSFER_CONTROL)
+		batch->transfered_size -= USB_SETUP_PACKET_SIZE;
+
+	fibril_mutex_lock(&batch->ep->guard);
+	usb_transfer_batch_reset_toggle(batch);
+	endpoint_deactivate_locked(batch->ep);
+	fibril_mutex_unlock(&batch->ep->guard);
+
+	if (batch->dir == USB_DIRECTION_IN) {
+		assert(batch->transfered_size <= batch->buffer_size);
+		memcpy(batch->buffer,
+		    uhci_transfer_batch_data_buffer(uhci_batch),
+		    batch->transfered_size);
+	}
+
 	return true;
 }
Index: uspace/drv/bus/usb/uhci/uhci_batch.h
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_batch.h	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/drv/bus/usb/uhci/uhci_batch.h	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -98,17 +98,4 @@
 }
 
-/** Aborts the batch.
- * Sets error to EINTR and size off transferd data to 0, before finishing the
- * batch.
- * @param uhci_batch Batch to abort.
- */
-static inline void uhci_transfer_batch_abort(uhci_transfer_batch_t *uhci_batch)
-{
-	assert(uhci_batch);
-	uhci_batch->base.error = EINTR;
-	uhci_batch->base.transfered_size = 0;
-	usb_transfer_batch_finish(&uhci_batch->base);
-}
-
 /** Linked list conversion wrapper.
  * @param l Linked list link.
Index: uspace/drv/bus/usb/xhci/bus.c
===================================================================
--- uspace/drv/bus/usb/xhci/bus.c	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/drv/bus/usb/xhci/bus.c	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -198,13 +198,8 @@
 	for (size_t i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
 		xhci_endpoint_t *ep = xhci_dev->endpoints[i];
-		if (!ep || !ep->base.active)
+		if (!ep)
 			continue;
 
-		/* FIXME: This is racy. */
-		if ((err = xhci_transfer_abort(&ep->active_transfer))) {
-			usb_log_warning("Failed to abort active transfer to "
-			    " endpoint " XHCI_EP_FMT ": %s", XHCI_EP_ARGS(*ep),
-			    str_error(err));
-		}
+		endpoint_abort(&ep->base);
 	}
 
@@ -454,5 +449,5 @@
 }
 
-static int reset_toggle(bus_t *bus_base, usb_target_t target, bool all)
+static int reset_toggle(bus_t *bus_base, usb_target_t target, toggle_reset_mode_t mode)
 {
 	// TODO: Implement me!
Index: uspace/drv/bus/usb/xhci/endpoint.h
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.h	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/drv/bus/usb/xhci/endpoint.h	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -68,9 +68,4 @@
 	/** Main transfer ring (unused if streams are enabled) */
 	xhci_trb_ring_t ring;
-
-	/** There shall be only one transfer active on an endpoint. The
-	 * synchronization is performed using the active flag in base
-	 * endpoint_t */
-	xhci_transfer_t active_transfer;
 
 	/** Primary stream context array (or NULL if endpoint doesn't use streams). */
Index: uspace/drv/bus/usb/xhci/transfers.c
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.c	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -101,16 +101,9 @@
 xhci_transfer_t* xhci_transfer_create(endpoint_t* ep)
 {
-	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
-	xhci_transfer_t *transfer = &xhci_ep->active_transfer;
-
-	/* Do not access the transfer yet, it may be still in use. */
+	xhci_transfer_t *transfer = calloc(1, sizeof(xhci_transfer_t));
+	if (!transfer)
+		return NULL;
 
 	usb_transfer_batch_init(&transfer->batch, ep);
-	assert(ep->active);
-
-	/* Clean just our data. */
-	memset(((void *) transfer) + sizeof(usb_transfer_batch_t), 0,
-	    sizeof(xhci_transfer_t) - sizeof(usb_transfer_batch_t));
-
 	return transfer;
 }
@@ -246,13 +239,4 @@
 	usb_log_error("Isochronous transfers are not yet implemented!");
 	return ENOTSUP;
-}
-
-int xhci_transfer_abort(xhci_transfer_t *transfer)
-{
-	usb_transfer_batch_t *batch = &transfer->batch;
-	batch->error = EAGAIN;
-	batch->transfered_size = 0;
-	usb_transfer_batch_finish(batch);
-	return EOK;
 }
 
@@ -277,13 +261,21 @@
 	}
 
-	xhci_transfer_t *transfer = &ep->active_transfer;
-
-	/** FIXME: This is racy. Do we care? */
+	/* FIXME: This is racy. Do we care? */
 	ep->ring.dequeue = addr;
 
-	usb_transfer_batch_t *batch = &transfer->batch;
+	fibril_mutex_lock(&ep->base.guard);
+	usb_transfer_batch_t *batch = ep->base.active_batch;
+	if (!batch) {
+		fibril_mutex_unlock(&ep->base.guard);
+		return ENOENT;
+	}
 
 	batch->error = (TRB_COMPLETION_CODE(*trb) == XHCI_TRBC_SUCCESS) ? EOK : ENAK;
 	batch->transfered_size = batch->buffer_size - TRB_TRANSFER_LENGTH(*trb);
+	usb_transfer_batch_reset_toggle(batch);
+	endpoint_deactivate_locked(&ep->base);
+	fibril_mutex_unlock(&ep->base.guard);
+
+	xhci_transfer_t *transfer = xhci_transfer_from_batch(batch);
 
 	if (batch->dir == USB_DIRECTION_IN) {
@@ -309,13 +301,12 @@
 {
 	assert(hc);
+	endpoint_t *ep = batch->ep;
 
 	xhci_transfer_t *transfer = xhci_transfer_from_batch(batch);
-	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
-	assert(xhci_ep);
-
+	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
 	xhci_device_t *xhci_dev = xhci_ep_to_dev(xhci_ep);
 
 	/* Offline devices don't schedule transfers other than on EP0. */
-	if (!xhci_dev->online && xhci_ep->base.endpoint) {
+	if (!xhci_dev->online && ep->endpoint > 0) {
 		return EAGAIN;
 	}
@@ -334,6 +325,7 @@
 	if (batch->buffer_size > 0) {
 		transfer->hc_buffer = malloc32(batch->buffer_size);
-	}
-
+		if (!transfer->hc_buffer)
+			return ENOMEM;
+	}
 
 	if (batch->dir != USB_DIRECTION_IN) {
@@ -342,7 +334,17 @@
 	}
 
+	fibril_mutex_lock(&ep->guard);
+	endpoint_activate_locked(ep, batch);
 	const int err = transfer_handlers[batch->ep->transfer_type](hc, transfer);
-	if (err)
+
+	if (err) {
+		endpoint_deactivate_locked(ep);
+		fibril_mutex_unlock(&ep->guard);
 		return err;
+	}
+
+	/* After the critical section, the transfer can already be finished or aborted. */
+	transfer = NULL; batch = NULL;
+	fibril_mutex_unlock(&ep->guard);
 
 	const uint8_t slot_id = xhci_dev->slot_id;
Index: uspace/drv/bus/usb/xhci/transfers.h
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.h	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/drv/bus/usb/xhci/transfers.h	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -58,5 +58,4 @@
 xhci_transfer_t* xhci_transfer_create(endpoint_t *);
 int xhci_transfer_schedule(xhci_hc_t *, usb_transfer_batch_t *);
-int xhci_transfer_abort(xhci_transfer_t *);
 int xhci_handle_transfer_event(xhci_hc_t *, xhci_trb_t *);
 void xhci_transfer_destroy(xhci_transfer_t *);
Index: uspace/lib/usb/include/usb/request.h
===================================================================
--- uspace/lib/usb/include/usb/request.h	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/lib/usb/include/usb/request.h	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -109,5 +109,5 @@
 int assert[(sizeof(usb_device_request_setup_packet_t) == 8) ? 1: -1];
 
-/** How much toggles needs to be reset */
+/** How many toggles need to be reset */
 typedef enum {
 	RESET_NONE,
Index: uspace/lib/usbhost/include/usb/host/bus.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/bus.h	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/lib/usbhost/include/usb/host/bus.h	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -44,4 +44,5 @@
 
 #include <usb/usb.h>
+#include <usb/request.h>
 
 #include <assert.h>
@@ -97,5 +98,5 @@
 	int (*release_address)(bus_t *, usb_address_t);
 
-	int (*reset_toggle)(bus_t *, usb_target_t, bool);
+	int (*reset_toggle)(bus_t *, usb_target_t, toggle_reset_mode_t);
 
 	size_t (*count_bw) (endpoint_t *, size_t);
Index: uspace/lib/usbhost/include/usb/host/endpoint.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/endpoint.h	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/lib/usbhost/include/usb/host/endpoint.h	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -52,10 +52,10 @@
 /** Host controller side endpoint structure. */
 typedef struct endpoint {
+	/** Part of linked list. */
+	link_t link;
 	/** Managing bus */
 	bus_t *bus;
 	/** Reference count. */
 	atomic_t refcnt;
-	/** Part of linked list. */
-	link_t link;
 	/** USB device */
 	device_t *device;
@@ -76,6 +76,6 @@
 	/** Value of the toggle bit. */
 	unsigned toggle:1;
-	/** True if there is a batch using this scheduled for this endpoint. */
-	bool active;
+	/** The currently active transfer batch. Write using methods, read under guard. */
+	usb_transfer_batch_t *active_batch;
 	/** Protects resources and active status changes. */
 	fibril_mutex_t guard;
@@ -91,9 +91,20 @@
 extern void endpoint_del_ref(endpoint_t *);
 
-extern void endpoint_use(endpoint_t *);
-extern void endpoint_release(endpoint_t *);
+/* Pay atention to synchronization of batch access wrt to aborting & finishing from another fibril. */
+
+/* Set currently active batch. The common case is to activate in the same
+ * critical section as scheduling to HW.
+ */
+extern void endpoint_activate_locked(endpoint_t *, usb_transfer_batch_t *);
+
+/* Deactivate the endpoint, allowing others to activate it again. Batch shall
+ * already have an error set. */
+extern void endpoint_deactivate_locked(endpoint_t *);
+
+/* Abort the currenty active batch. */
+void endpoint_abort(endpoint_t *);
 
 extern int endpoint_toggle_get(endpoint_t *);
-extern void endpoint_toggle_set(endpoint_t *, unsigned);
+extern void endpoint_toggle_set(endpoint_t *, bool);
 
 /** list_get_instance wrapper.
Index: uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -40,5 +40,7 @@
 #include <usb/request.h>
 
+#include <atomic.h>
 #include <stddef.h>
+#include <errno.h>
 #include <stdint.h>
 #include <usbhc_iface.h>
@@ -86,7 +88,7 @@
 	/** Size of memory pointed to by buffer member */
 	size_t buffer_size;
-
 	/** Actually used portion of the buffer */
 	size_t transfered_size;
+
 	/** Indicates success/failure of the communication */
 	int error;
@@ -106,8 +108,20 @@
 	(batch).buffer_size, (batch).ep->max_packet_size
 
+/** Wrapper for bus operation. */
+usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *);
+
+/** Batch initializer. */
 void usb_transfer_batch_init(usb_transfer_batch_t *, endpoint_t *);
+
+/** Call after status is known, but before releasing endpoint */
+int usb_transfer_batch_reset_toggle(usb_transfer_batch_t *);
+
+/** Batch finalization. */
+void usb_transfer_batch_abort(usb_transfer_batch_t *);
 void usb_transfer_batch_finish(usb_transfer_batch_t *);
 
-usb_transfer_batch_t *usb_transfer_batch_create(endpoint_t *);
+/** To be called from outside only when the transfer is not going to be finished
+ * (i.o.w. until successfuly scheduling)
+ */
 void usb_transfer_batch_destroy(usb_transfer_batch_t *);
 
Index: uspace/lib/usbhost/src/endpoint.c
===================================================================
--- uspace/lib/usbhost/src/endpoint.c	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/lib/usbhost/src/endpoint.c	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -36,4 +36,5 @@
 
 #include <usb/host/endpoint.h>
+#include <usb/host/usb_transfer_batch.h>
 #include <usb/host/bus.h>
 
@@ -68,5 +69,5 @@
 		}
 		else {
-			assert(!ep->active);
+			assert(ep->active_batch == NULL);
 
 			/* Assume mostly the eps will be allocated by malloc. */
@@ -79,14 +80,14 @@
  * @param ep endpoint_t structure.
  */
-void endpoint_use(endpoint_t *ep)
+void endpoint_activate_locked(endpoint_t *ep, usb_transfer_batch_t *batch)
 {
 	assert(ep);
-	/* Add reference for active endpoint. */
-	endpoint_add_ref(ep);
-	fibril_mutex_lock(&ep->guard);
-	while (ep->active)
+	assert(batch);
+	assert(batch->ep == ep);
+	assert(fibril_mutex_is_locked(&ep->guard));
+
+	while (ep->active_batch != NULL)
 		fibril_condvar_wait(&ep->avail, &ep->guard);
-	ep->active = true;
-	fibril_mutex_unlock(&ep->guard);
+	ep->active_batch = batch;
 }
 
@@ -94,13 +95,31 @@
  * @param ep endpoint_t structure.
  */
-void endpoint_release(endpoint_t *ep)
+void endpoint_deactivate_locked(endpoint_t *ep)
 {
 	assert(ep);
+	assert(fibril_mutex_is_locked(&ep->guard));
+
+	if (ep->active_batch && ep->active_batch->error == EOK)
+		usb_transfer_batch_reset_toggle(ep->active_batch);
+
+	ep->active_batch = NULL;
+	fibril_condvar_signal(&ep->avail);
+}
+
+/** Abort an active batch on endpoint, if any.
+ *
+ * @param[in] ep endpoint_t structure.
+ */
+void endpoint_abort(endpoint_t *ep)
+{
+	assert(ep);
+
 	fibril_mutex_lock(&ep->guard);
-	ep->active = false;
+	usb_transfer_batch_t *batch = ep->active_batch;
+	endpoint_deactivate_locked(ep);
 	fibril_mutex_unlock(&ep->guard);
-	fibril_condvar_signal(&ep->avail);
-	/* Drop reference for active endpoint. */
-	endpoint_del_ref(ep);
+
+	if (batch)
+		usb_transfer_batch_abort(batch);
 }
 
@@ -112,10 +131,8 @@
 {
 	assert(ep);
-	fibril_mutex_lock(&ep->guard);
-	const int ret = ep->bus->ops.endpoint_get_toggle
+
+	return ep->bus->ops.endpoint_get_toggle
 	    ? ep->bus->ops.endpoint_get_toggle(ep)
 	    : ep->toggle;
-	fibril_mutex_unlock(&ep->guard);
-	return ret;
 }
 
@@ -124,9 +141,8 @@
  * @param ep endpoint_t structure.
  */
-void endpoint_toggle_set(endpoint_t *ep, unsigned toggle)
+void endpoint_toggle_set(endpoint_t *ep, bool toggle)
 {
 	assert(ep);
-	assert(toggle == 0 || toggle == 1);
-	fibril_mutex_lock(&ep->guard);
+
 	if (ep->bus->ops.endpoint_set_toggle) {
 		ep->bus->ops.endpoint_set_toggle(ep, toggle);
@@ -135,5 +151,4 @@
 		ep->toggle = toggle;
 	}
-	fibril_mutex_unlock(&ep->guard);
 }
 
Index: uspace/lib/usbhost/src/usb2_bus.c
===================================================================
--- uspace/lib/usbhost/src/usb2_bus.c	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/lib/usbhost/src/usb2_bus.c	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -367,5 +367,5 @@
 }
 
-static int usb2_bus_reset_toggle(bus_t *bus_base, usb_target_t target, bool all)
+static int usb2_bus_reset_toggle(bus_t *bus_base, usb_target_t target, toggle_reset_mode_t mode)
 {
 	usb2_bus_t *bus = bus_to_usb2_bus(bus_base);
@@ -374,4 +374,7 @@
 		return EINVAL;
 
+	if (mode == RESET_NONE)
+		return EOK;
+
 	int ret = ENOENT;
 
@@ -379,5 +382,5 @@
 		assert(ep->device->address == target.address);
 
-		if (all || ep->endpoint == target.endpoint) {
+		if (mode == RESET_ALL || ep->endpoint == target.endpoint) {
 			endpoint_toggle_set(ep, 0);
 			ret = EOK;
Index: uspace/lib/usbhost/src/usb_transfer_batch.c
===================================================================
--- uspace/lib/usbhost/src/usb_transfer_batch.c	(revision a312d8f527427930eb111c09105cfcf076485261)
+++ uspace/lib/usbhost/src/usb_transfer_batch.c	(revision 17873ac7d4019500d5125f0b78948a7b91d478ec)
@@ -63,32 +63,24 @@
 void usb_transfer_batch_init(usb_transfer_batch_t *batch, endpoint_t *ep)
 {
-	endpoint_use(ep);
-
 	memset(batch, 0, sizeof(*batch));
 	batch->ep = ep;
 }
 
-/** Call the handler of the batch.
+/** Resolve resetting toggle.
  *
  * @param[in] batch Batch structure to use.
  */
-static int batch_complete(usb_transfer_batch_t *batch)
+int usb_transfer_batch_reset_toggle(usb_transfer_batch_t *batch)
 {
 	assert(batch);
 
-	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)
+		return EOK;
 
-	if (batch->error == EOK && batch->toggle_reset_mode != RESET_NONE) {
-		usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " resets %s",
-		    batch, USB_TRANSFER_BATCH_ARGS(*batch),
-		    batch->toggle_reset_mode == RESET_ALL ? "all EPs toggle" : "EP toggle");
-		bus_reset_toggle(batch->ep->bus, 
-		    batch->target, batch->toggle_reset_mode == RESET_ALL);
-	}
+	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " resets %s",
+	    batch, USB_TRANSFER_BATCH_ARGS(*batch),
+	    batch->toggle_reset_mode == RESET_ALL ? "all EPs toggle" : "EP toggle");
 
-	return batch->on_complete
-		? batch->on_complete(batch)
-		: EOK;
+	return bus_reset_toggle(batch->ep->bus, batch->target, batch->toggle_reset_mode);
 }
 
@@ -114,6 +106,4 @@
 		free(batch);
 	}
-
-	endpoint_release(batch->ep);
 }
 
@@ -126,9 +116,31 @@
 void usb_transfer_batch_finish(usb_transfer_batch_t *batch)
 {
-	const int err = batch_complete(batch);
-	if (err)
-		usb_log_warning("batch %p failed to complete: %s", batch, str_error(err));
+	assert(batch);
+	assert(batch->ep);
+
+	usb_log_debug2("Batch %p " USB_TRANSFER_BATCH_FMT " finishing.\n",
+	    batch, USB_TRANSFER_BATCH_ARGS(*batch));
+
+	if (batch->on_complete) {
+		const int err = batch->on_complete(batch);
+		if (err)
+			usb_log_warning("batch %p failed to complete: %s",
+			    batch, str_error(err));
+	}
 
 	usb_transfer_batch_destroy(batch);
+}
+
+/** Finish a transfer batch as an aborted one.
+ *
+ * @param[in] batch Batch structure to use.
+ */
+void usb_transfer_batch_abort(usb_transfer_batch_t *batch)
+{
+	assert(batch);
+	assert(batch->ep);
+
+	batch->error = EAGAIN;
+	usb_transfer_batch_finish(batch);
 }
 
