Index: uspace/drv/bus/usb/ehci/ehci_bus.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_bus.c	(revision 8033f891a5db64bf70d7b58c3ae97e1b72e52042)
+++ uspace/drv/bus/usb/ehci/ehci_bus.c	(revision 4db49344094408c5a4e6654a2a2813509a0c351b)
@@ -107,5 +107,5 @@
 	qh_init(ehci_ep->qh, ep);
 	hc_enqueue_endpoint(bus->hc, ep);
-
+	endpoint_set_online(ep, &bus->hc->guard);
 	return EOK;
 }
@@ -121,26 +121,17 @@
 	usb2_bus_ops.endpoint_unregister(ep);
 	hc_dequeue_endpoint(hc, ep);
+	/*
+	 * Now we can be sure the active transfer will not be completed,
+	 * as it's out of the schedule, and HC acknowledged it.
+	 */
 
 	ehci_endpoint_t *ehci_ep = ehci_endpoint_get(ep);
 
-	/*
-	 * Now we can be sure the active transfer will not be completed. But first,
-	 * make sure that the handling fibril won't use its link in pending list.
-	 */
 	fibril_mutex_lock(&hc->guard);
-	if (link_in_use(&ehci_ep->pending_link))
-		/* pending list reference */
-		endpoint_del_ref(ep);
+	endpoint_set_offline_locked(ep);
 	list_remove(&ehci_ep->pending_link);
-	fibril_mutex_unlock(&hc->guard);
-
-	/*
-	 * Finally, the endpoint shall not be used anywhere else. Finish the
-	 * pending batch.
-	 */
-	fibril_mutex_lock(&ep->guard);
 	usb_transfer_batch_t * const batch = ep->active_batch;
 	endpoint_deactivate_locked(ep);
-	fibril_mutex_unlock(&ep->guard);
+	fibril_mutex_unlock(&hc->guard);
 
 	if (batch) {
Index: uspace/drv/bus/usb/ehci/hc.c
===================================================================
--- uspace/drv/bus/usb/ehci/hc.c	(revision 8033f891a5db64bf70d7b58c3ae97e1b72e52042)
+++ uspace/drv/bus/usb/ehci/hc.c	(revision 4db49344094408c5a4e6654a2a2813509a0c351b)
@@ -302,18 +302,15 @@
 	endpoint_t * const ep = batch->ep;
 	ehci_endpoint_t * const ehci_ep = ehci_endpoint_get(ep);
-
-	/* creating local reference */
-	endpoint_add_ref(ep);
-
-	fibril_mutex_lock(&ep->guard);
-	endpoint_activate_locked(ep, batch);
-
 	ehci_transfer_batch_t *ehci_batch = ehci_transfer_batch_get(batch);
-	const int err = ehci_transfer_batch_prepare(ehci_batch);
-	if (err) {
-		endpoint_deactivate_locked(ep);
-		fibril_mutex_unlock(&ep->guard);
-		/* dropping local reference */
-		endpoint_del_ref(ep);
+
+	int err;
+
+	if ((err = ehci_transfer_batch_prepare(ehci_batch)))
+		return err;
+
+	fibril_mutex_lock(&hc->guard);
+
+	if ((err = endpoint_activate_locked(ep, batch))) {
+		fibril_mutex_unlock(&hc->guard);
 		return err;
 	}
@@ -321,14 +318,10 @@
 	usb_log_debug("HC(%p): Committing BATCH(%p)", hc, batch);
 	ehci_transfer_batch_commit(ehci_batch);
-	fibril_mutex_unlock(&ep->guard);
 
 	/* Enqueue endpoint to the checked list */
-	fibril_mutex_lock(&hc->guard);
 	usb_log_debug2("HC(%p): Appending BATCH(%p)", hc, batch);
-
-	/* local reference -> pending list reference */
 	list_append(&ehci_ep->pending_link, &hc->pending_endpoints);
+
 	fibril_mutex_unlock(&hc->guard);
-
 	return EOK;
 }
@@ -368,5 +361,4 @@
 				= list_get_instance(current, ehci_endpoint_t, pending_link);
 
-			fibril_mutex_lock(&ep->base.guard);
 			ehci_transfer_batch_t *batch
 				= ehci_transfer_batch_get(ep->base.active_batch);
@@ -376,9 +368,7 @@
 				endpoint_deactivate_locked(&ep->base);
 				list_remove(current);
-				endpoint_del_ref(&ep->base);
 				hc_reset_toggles(&batch->base, &ehci_ep_toggle_reset);
 				usb_transfer_batch_finish(&batch->base);
 			}
-			fibril_mutex_unlock(&ep->base.guard);
 		}
 		fibril_mutex_unlock(&hc->guard);
Index: uspace/drv/bus/usb/ohci/hc.c
===================================================================
--- uspace/drv/bus/usb/ohci/hc.c	(revision 8033f891a5db64bf70d7b58c3ae97e1b72e52042)
+++ uspace/drv/bus/usb/ohci/hc.c	(revision 4db49344094408c5a4e6654a2a2813509a0c351b)
@@ -297,22 +297,22 @@
 		return ohci_rh_schedule(&hc->rh, batch);
 	}
-	ohci_transfer_batch_t *ohci_batch = ohci_transfer_batch_get(batch);
-	if (!ohci_batch)
-		return ENOMEM;
-
-	const int err = ohci_transfer_batch_prepare(ohci_batch);
-	if (err)
-		return err;
 
 	endpoint_t *ep = batch->ep;
 	ohci_endpoint_t * const ohci_ep = ohci_endpoint_get(ep);
-
-	/* creating local reference */
-	endpoint_add_ref(ep);
-
-	fibril_mutex_lock(&ep->guard);
-	endpoint_activate_locked(ep, batch);
+	ohci_transfer_batch_t *ohci_batch = ohci_transfer_batch_get(batch);
+
+	int err;
+	if ((err = ohci_transfer_batch_prepare(ohci_batch)))
+		return err;
+
+	fibril_mutex_lock(&hc->guard);
+	if ((err = endpoint_activate_locked(ep, batch))) {
+		fibril_mutex_unlock(&hc->guard);
+		return err;
+	}
+
 	ohci_transfer_batch_commit(ohci_batch);
-	fibril_mutex_unlock(&ep->guard);
+	list_append(&ohci_ep->pending_link, &hc->pending_endpoints);
+	fibril_mutex_unlock(&hc->guard);
 
 	/* Control and bulk schedules need a kick to start working */
@@ -328,8 +328,4 @@
 		break;
 	}
-
-	fibril_mutex_lock(&hc->guard);
-	list_append(&ohci_ep->pending_link, &hc->pending_endpoints);
-	fibril_mutex_unlock(&hc->guard);
 
 	return EOK;
@@ -369,5 +365,4 @@
 				= list_get_instance(current, ohci_endpoint_t, pending_link);
 
-			fibril_mutex_lock(&ep->base.guard);
 			ohci_transfer_batch_t *batch
 				= ohci_transfer_batch_get(ep->base.active_batch);
@@ -377,9 +372,7 @@
 				endpoint_deactivate_locked(&ep->base);
 				list_remove(current);
-				endpoint_del_ref(&ep->base);
 				hc_reset_toggles(&batch->base, &ohci_ep_toggle_reset);
 				usb_transfer_batch_finish(&batch->base);
 			}
-			fibril_mutex_unlock(&ep->base.guard);
 		}
 		fibril_mutex_unlock(&hc->guard);
Index: uspace/drv/bus/usb/ohci/ohci_bus.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_bus.c	(revision 8033f891a5db64bf70d7b58c3ae97e1b72e52042)
+++ uspace/drv/bus/usb/ohci/ohci_bus.c	(revision 4db49344094408c5a4e6654a2a2813509a0c351b)
@@ -115,4 +115,5 @@
 	ed_init(ohci_ep->ed, ep, ohci_ep->td);
 	hc_enqueue_endpoint(bus->hc, ep);
+	endpoint_set_online(ep, &bus->hc->guard);
 
 	return EOK;
@@ -128,25 +129,17 @@
 	hc_dequeue_endpoint(bus->hc, ep);
 
-	ohci_endpoint_t * const ohci_ep = ohci_endpoint_get(ep);
+	/*
+	 * Now we can be sure the active transfer will not be completed,
+	 * as it's out of the schedule, and HC acknowledged it.
+	 */
 
-	/*
-	 * Now we can be sure the active transfer will not be completed. But first,
-	 * make sure that the handling fibril won't use its link in pending list.
-	 */
+	ohci_endpoint_t *ohci_ep = ohci_endpoint_get(ep);
+
 	fibril_mutex_lock(&hc->guard);
-	if (link_in_use(&ohci_ep->pending_link))
-		/* pending list reference */
-		endpoint_del_ref(ep);
+	endpoint_set_offline_locked(ep);
 	list_remove(&ohci_ep->pending_link);
-	fibril_mutex_unlock(&hc->guard);
-
-	/*
-	 * Finally, the endpoint shall not be used anywhere else. Finish the
-	 * pending batch.
-	 */
-	fibril_mutex_lock(&ep->guard);
 	usb_transfer_batch_t * const batch = ep->active_batch;
 	endpoint_deactivate_locked(ep);
-	fibril_mutex_unlock(&ep->guard);
+	fibril_mutex_unlock(&hc->guard);
 
 	if (batch) {
Index: uspace/drv/bus/usb/uhci/hc.c
===================================================================
--- uspace/drv/bus/usb/uhci/hc.c	(revision 8033f891a5db64bf70d7b58c3ae97e1b72e52042)
+++ uspace/drv/bus/usb/uhci/hc.c	(revision 4db49344094408c5a4e6654a2a2813509a0c351b)
@@ -97,5 +97,5 @@
 
 static void hc_init_hw(const hc_t *instance);
-static int hc_init_mem_structures(hc_t *instance, hc_device_t *);
+static int hc_init_mem_structures(hc_t *instance);
 static int hc_init_transfer_lists(hc_t *instance);
 
@@ -164,21 +164,10 @@
 	/* Lower 2 bits are transaction error and transaction complete */
 	if (status & (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)) {
-		LIST_INITIALIZE(done);
-		transfer_list_remove_finished(
-		    &instance->transfers_interrupt, &done);
-		transfer_list_remove_finished(
-		    &instance->transfers_control_slow, &done);
-		transfer_list_remove_finished(
-		    &instance->transfers_control_full, &done);
-		transfer_list_remove_finished(
-		    &instance->transfers_bulk_full, &done);
-
-		list_foreach_safe(done, current, next) {
-			list_remove(current);
-			uhci_transfer_batch_t *batch =
-			    uhci_transfer_batch_from_link(current);
-			usb_transfer_batch_finish(&batch->base);
-		}
-	}
+		transfer_list_check_finished(&instance->transfers_interrupt);
+		transfer_list_check_finished(&instance->transfers_control_slow);
+		transfer_list_check_finished(&instance->transfers_control_full);
+		transfer_list_check_finished(&instance->transfers_bulk_full);
+	}
+
 	/* Resume interrupts are not supported */
 	if (status & UHCI_STATUS_RESUME) {
@@ -239,5 +228,5 @@
 	    hw_res->io_ranges.ranges[0].size);
 
-	ret = hc_init_mem_structures(instance, hcd);
+	ret = hc_init_mem_structures(instance);
 	if (ret != EOK) {
 		usb_log_error("Failed to init UHCI memory structures: %s.",
@@ -328,10 +317,30 @@
 }
 
+static int endpoint_register(endpoint_t *ep)
+{
+	hc_t * const hc = bus_to_hc(endpoint_get_bus(ep));
+
+	const int err = usb2_bus_ops.endpoint_register(ep);
+	if (err)
+		return err;
+
+	transfer_list_t *list = hc->transfers[ep->device->speed][ep->transfer_type];
+	if (!list)
+		/*
+		 * We don't support this combination (e.g. isochronous). Do not
+		 * fail early, because that would block any device with these
+		 * endpoints from connecting. Instead, make sure these transfers
+		 * are denied soon enough with ENOTSUP not to fail on asserts.
+		 */
+		return EOK;
+
+	endpoint_set_online(ep, &list->guard);
+	return EOK;
+}
+
 static void endpoint_unregister(endpoint_t *ep)
 {
 	hc_t * const hc = bus_to_hc(endpoint_get_bus(ep));
 	usb2_bus_ops.endpoint_unregister(ep);
-
-	uhci_transfer_batch_t *batch = NULL;
 
 	// Check for the roothub, as it does not schedule into lists
@@ -344,5 +353,4 @@
 
 	transfer_list_t *list = hc->transfers[ep->device->speed][ep->transfer_type];
-
 	if (!list)
 		/*
@@ -352,23 +360,40 @@
 		return;
 
-	// To avoid ABBA deadlock, we need to take the list first
 	fibril_mutex_lock(&list->guard);
-	fibril_mutex_lock(&ep->guard);
-	if (ep->active_batch) {
-		batch = uhci_transfer_batch_get(ep->active_batch);
-		endpoint_deactivate_locked(ep);
-		transfer_list_remove_batch(list, batch);
-	}
-	fibril_mutex_unlock(&ep->guard);
+
+	endpoint_set_offline_locked(ep);
+	/* From now on, no other transfer will be scheduled. */
+
+	if (!ep->active_batch) {
+		fibril_mutex_unlock(&list->guard);
+		return;
+	}
+
+	/* First, offer the batch a short chance to be finished. */
+	endpoint_wait_timeout_locked(ep, 10000);
+
+	if (!ep->active_batch) {
+		fibril_mutex_unlock(&list->guard);
+		return;
+	}
+
+	uhci_transfer_batch_t * const batch =
+		uhci_transfer_batch_get(ep->active_batch);
+
+	/* Remove the batch from the schedule to stop it from being finished. */
+	endpoint_deactivate_locked(ep);
+	transfer_list_remove_batch(list, batch);
+
 	fibril_mutex_unlock(&list->guard);
 
-	if (batch) {
-		// The HW could have been looking at the batch.
-		// Better wait two frames before we release the buffers.
-		async_usleep(2000);
-		batch->base.error = EINTR;
-		batch->base.transferred_size = 0;
-		usb_transfer_batch_finish(&batch->base);
-	}
+	/*
+	 * We removed the batch from software schedule only, it's still possible
+	 * that HC has it in its caches. Better wait a while before we release
+	 * the buffers.
+	 */
+	async_usleep(20000);
+	batch->base.error = EINTR;
+	batch->base.transferred_size = 0;
+	usb_transfer_batch_finish(&batch->base);
 }
 
@@ -382,4 +407,5 @@
 	.status = hc_status,
 
+	.endpoint_register = endpoint_register,
 	.endpoint_unregister = endpoint_unregister,
 	.endpoint_count_bw = bandwidth_count_usb11,
@@ -400,5 +426,5 @@
  *  - frame list page (needs to be one UHCI hw accessible 4K page)
  */
-int hc_init_mem_structures(hc_t *instance, hc_device_t *hcd)
+int hc_init_mem_structures(hc_t *instance)
 {
 	assert(instance);
@@ -425,4 +451,5 @@
 		return ENOMEM;
 	}
+	list_initialize(&instance->pending_endpoints);
 	usb_log_debug("Initialized transfer lists.");
 
@@ -514,11 +541,10 @@
 }
 
-/** Schedule batch for execution.
+/**
+ * Schedule batch for execution.
  *
  * @param[in] instance UHCI structure to use.
  * @param[in] batch Transfer batch to schedule.
  * @return Error code
- *
- * Checks for bandwidth availability and appends the batch to the proper queue.
  */
 static int hc_schedule(usb_transfer_batch_t *batch)
@@ -531,20 +557,15 @@
 		return uhci_rh_schedule(&hc->rh, batch);
 
-
-	const int err = uhci_transfer_batch_prepare(uhci_batch);
-	if (err)
+	transfer_list_t * const list =
+	    hc->transfers[ep->device->speed][ep->transfer_type];
+
+	if (!list)
+		return ENOTSUP;
+
+	int err;
+	if ((err = uhci_transfer_batch_prepare(uhci_batch)))
 		return err;
 
-	transfer_list_t *list = hc->transfers[ep->device->speed][ep->transfer_type];
-	assert(list);
-	transfer_list_add_batch(list, uhci_batch);
-
-	return EOK;
-}
-
-int hc_unschedule_batch(usb_transfer_batch_t *batch)
-{
-
-	return EOK;
+	return transfer_list_add_batch(list, uhci_batch);
 }
 
Index: uspace/drv/bus/usb/uhci/hc.h
===================================================================
--- uspace/drv/bus/usb/uhci/hc.h	(revision 8033f891a5db64bf70d7b58c3ae97e1b72e52042)
+++ uspace/drv/bus/usb/uhci/hc.h	(revision 4db49344094408c5a4e6654a2a2813509a0c351b)
@@ -123,4 +123,12 @@
 	transfer_list_t *transfers[2][4];
 
+	/**
+	 * Guard for the pending list. Can be locked under EP guard, but not
+	 * vice versa.
+	 */
+	fibril_mutex_t guard;
+	/** List of endpoints with a transfer scheduled */
+	list_t pending_endpoints;
+
 	/** Number of hw failures detected. */
 	unsigned hw_failures;
Index: uspace/drv/bus/usb/uhci/transfer_list.c
===================================================================
--- uspace/drv/bus/usb/uhci/transfer_list.c	(revision 8033f891a5db64bf70d7b58c3ae97e1b72e52042)
+++ uspace/drv/bus/usb/uhci/transfer_list.c	(revision 4db49344094408c5a4e6654a2a2813509a0c351b)
@@ -102,12 +102,14 @@
 }
 
-/** Add transfer batch to the list and queue.
- *
- * @param[in] instance List to use.
- * @param[in] batch Transfer batch to submit.
+/**
+ * Add transfer batch to the list and queue.
  *
  * The batch is added to the end of the list and queue.
- */
-void transfer_list_add_batch(
+ *
+ * @param[in] instance List to use.
+ * @param[in] batch Transfer batch to submit. After return, the batch must
+ *                  not be used further.
+ */
+int transfer_list_add_batch(
     transfer_list_t *instance, uhci_transfer_batch_t *uhci_batch)
 {
@@ -117,13 +119,14 @@
 	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);
+	fibril_mutex_lock(&instance->guard);
+
+	const int err = endpoint_activate_locked(ep, &uhci_batch->base);
+	if (err) {
+		fibril_mutex_unlock(&instance->guard);
+		return err;
+	}
 
 	usb_log_debug2("Batch %p adding to queue %s.",
 	    uhci_batch, instance->name);
-
-	fibril_mutex_lock(&instance->guard);
 
 	/* Assume there is nothing scheduled */
@@ -155,4 +158,5 @@
 	    USB_TRANSFER_BATCH_ARGS(uhci_batch->base), instance->name);
 	fibril_mutex_unlock(&instance->guard);
+	return EOK;
 }
 
@@ -171,28 +175,19 @@
  * @param[in] done list to fill
  */
-void transfer_list_remove_finished(transfer_list_t *instance, list_t *done)
-{
-	assert(instance);
-	assert(done);
+void transfer_list_check_finished(transfer_list_t *instance)
+{
+	assert(instance);
 
 	fibril_mutex_lock(&instance->guard);
-	link_t *current = list_first(&instance->batch_list);
-	while (current && current != &instance->batch_list.head) {
-		link_t * const next = current->next;
-		uhci_transfer_batch_t *batch =
-		    uhci_transfer_batch_from_link(current);
+	list_foreach_safe(instance->batch_list, current, next) {
+		uhci_transfer_batch_t *batch = uhci_transfer_batch_from_link(current);
 
 		if (uhci_transfer_batch_check_completed(batch)) {
-			/* Remove from schedule, save for processing */
-			fibril_mutex_lock(&batch->base.ep->guard);
 			assert(batch->base.ep->active_batch == &batch->base);
+			endpoint_deactivate_locked(batch->base.ep);
 			hc_reset_toggles(&batch->base, &uhci_reset_toggle);
-			endpoint_deactivate_locked(batch->base.ep);
 			transfer_list_remove_batch(instance, batch);
-			fibril_mutex_unlock(&batch->base.ep->guard);
-
-			list_append(current, done);
+			usb_transfer_batch_finish(&batch->base);
 		}
-		current = next;
 	}
 	fibril_mutex_unlock(&instance->guard);
Index: uspace/drv/bus/usb/uhci/transfer_list.h
===================================================================
--- uspace/drv/bus/usb/uhci/transfer_list.h	(revision 8033f891a5db64bf70d7b58c3ae97e1b72e52042)
+++ uspace/drv/bus/usb/uhci/transfer_list.h	(revision 4db49344094408c5a4e6654a2a2813509a0c351b)
@@ -59,7 +59,7 @@
 int transfer_list_init(transfer_list_t *, const char *);
 void transfer_list_set_next(transfer_list_t *, transfer_list_t *);
-void transfer_list_add_batch(transfer_list_t *, uhci_transfer_batch_t *);
+int transfer_list_add_batch(transfer_list_t *, uhci_transfer_batch_t *);
 void transfer_list_remove_batch(transfer_list_t *, uhci_transfer_batch_t *);
-void transfer_list_remove_finished(transfer_list_t *, list_t *);
+void transfer_list_check_finished(transfer_list_t *);
 void transfer_list_abort_all(transfer_list_t *);
 
Index: uspace/drv/bus/usb/xhci/device.c
===================================================================
--- uspace/drv/bus/usb/xhci/device.c	(revision 8033f891a5db64bf70d7b58c3ae97e1b72e52042)
+++ uspace/drv/bus/usb/xhci/device.c	(revision 4db49344094408c5a4e6654a2a2813509a0c351b)
@@ -81,12 +81,7 @@
 	usb_log_debug("Obtained slot ID: %u.", dev->slot_id);
 
-	/* Create and configure control endpoint. */
-	endpoint_t *ep0_base = xhci_endpoint_create(&dev->base, &ep0_initial_desc);
-	if (!ep0_base)
+	endpoint_t *ep0_base;
+	if ((err = bus_endpoint_add(&dev->base, &ep0_initial_desc, &ep0_base)))
 		goto err_slot;
-
-	/* Bus reference */
-	endpoint_add_ref(ep0_base);
-	dev->base.endpoints[0] = ep0_base;
 
 	usb_log_debug("Looking up new device initial MPS: %s",
Index: uspace/drv/bus/usb/xhci/endpoint.c
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.c	(revision 8033f891a5db64bf70d7b58c3ae97e1b72e52042)
+++ uspace/drv/bus/usb/xhci/endpoint.c	(revision 4db49344094408c5a4e6654a2a2813509a0c351b)
@@ -68,4 +68,6 @@
 	endpoint_init(ep, dev, desc);
 
+	fibril_mutex_initialize(&xhci_ep->guard);
+
 	xhci_ep->max_burst = desc->companion.max_burst + 1;
 
@@ -177,7 +179,8 @@
 	xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
 
-	if ((err = hc_add_endpoint(ep)))
+	if (ep_base->endpoint != 0 && (err = hc_add_endpoint(ep)))
 		return err;
 
+	endpoint_set_online(ep_base, &ep->guard);
 	return EOK;
 }
@@ -186,36 +189,44 @@
  * Abort a transfer on an endpoint.
  */
-static int endpoint_abort(endpoint_t *ep)
+static void endpoint_abort(endpoint_t *ep)
 {
 	xhci_device_t *dev = xhci_device_get(ep->device);
 	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
 
-	usb_transfer_batch_t *batch = NULL;
-	fibril_mutex_lock(&ep->guard);
-	if (ep->active_batch) {
-		if (dev->slot_id) {
-			const int err = hc_stop_endpoint(xhci_ep);
-			if (err) {
-				usb_log_warning("Failed to stop endpoint %u of device "
-				    XHCI_DEV_FMT ": %s", ep->endpoint, XHCI_DEV_ARGS(*dev),
-				    str_error(err));
-			}
-
-			endpoint_wait_timeout_locked(ep, 2000);
-		}
-
-		batch = ep->active_batch;
-		if (batch) {
-			endpoint_deactivate_locked(ep);
-		}
-	}
-	fibril_mutex_unlock(&ep->guard);
-
-	if (batch) {
-		batch->error = EINTR;
-		batch->transferred_size = 0;
-		usb_transfer_batch_finish(batch);
-	}
-	return EOK;
+	/* This function can only abort endpoints without streams. */
+	assert(xhci_ep->primary_stream_data_array == NULL);
+
+	fibril_mutex_lock(&xhci_ep->guard);
+
+	endpoint_set_offline_locked(ep);
+
+	if (!ep->active_batch) {
+		fibril_mutex_unlock(&xhci_ep->guard);
+		return;
+	}
+
+	/* First, offer the batch a short chance to be finished. */
+	endpoint_wait_timeout_locked(ep, 10000);
+
+	if (!ep->active_batch) {
+		fibril_mutex_unlock(&xhci_ep->guard);
+		return;
+	}
+
+	usb_transfer_batch_t * const batch = ep->active_batch;
+
+	const int err = hc_stop_endpoint(xhci_ep);
+	if (err) {
+		usb_log_error("Failed to stop endpoint %u of device "
+		    XHCI_DEV_FMT ": %s", ep->endpoint, XHCI_DEV_ARGS(*dev),
+		    str_error(err));
+	}
+
+	fibril_mutex_unlock(&xhci_ep->guard);
+
+	batch->error = EINTR;
+	batch->transferred_size = 0;
+	usb_transfer_batch_finish(batch);
+	return;
 }
 
@@ -235,5 +246,5 @@
 
 	/* If device slot is still available, drop the endpoint. */
-	if (dev->slot_id) {
+	if (ep_base->endpoint != 0 && dev->slot_id) {
 
 		if ((err = hc_drop_endpoint(ep))) {
Index: uspace/drv/bus/usb/xhci/endpoint.h
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.h	(revision 8033f891a5db64bf70d7b58c3ae97e1b72e52042)
+++ uspace/drv/bus/usb/xhci/endpoint.h	(revision 4db49344094408c5a4e6654a2a2813509a0c351b)
@@ -70,4 +70,7 @@
 	endpoint_t base;	/**< Inheritance. Keep this first. */
 
+	/** Guarding scheduling of this endpoint. */
+	fibril_mutex_t guard;
+
 	/** Main transfer ring (unused if streams are enabled) */
 	xhci_trb_ring_t ring;
Index: uspace/drv/bus/usb/xhci/transfers.c
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.c	(revision 8033f891a5db64bf70d7b58c3ae97e1b72e52042)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision 4db49344094408c5a4e6654a2a2813509a0c351b)
@@ -300,5 +300,7 @@
 
 	if (TRB_EVENT_DATA(*trb)) {
-		assert(ep->base.transfer_type != USB_TRANSFER_ISOCHRONOUS);
+		/* We schedule those only when streams are involved */
+		assert(ep->primary_stream_ctx_array != NULL);
+
 		/* We are received transfer pointer instead - work with that */
 		transfer = (xhci_transfer_t *) addr;
@@ -306,8 +308,4 @@
 		    transfer->interrupt_trb_phys);
 		batch = &transfer->batch;
-
-		fibril_mutex_lock(&ep->base.guard);
-		endpoint_deactivate_locked(&ep->base);
-		fibril_mutex_unlock(&ep->base.guard);
 	}
 	else {
@@ -321,8 +319,10 @@
 		}
 
-		fibril_mutex_lock(&ep->base.guard);
+		fibril_mutex_lock(&ep->guard);
 		batch = ep->base.active_batch;
+		endpoint_deactivate_locked(&ep->base);
+		fibril_mutex_unlock(&ep->guard);
+
 		if (!batch) {
-			fibril_mutex_unlock(&ep->base.guard);
 			/* Dropping temporary reference */
 			endpoint_del_ref(&ep->base);
@@ -331,7 +331,4 @@
 
 		transfer = xhci_transfer_from_batch(batch);
-
-		endpoint_deactivate_locked(&ep->base);
-		fibril_mutex_unlock(&ep->base.guard);
 	}
 
@@ -482,16 +479,20 @@
 
 
-	fibril_mutex_lock(&ep->guard);
-	endpoint_activate_locked(ep, batch);
-	const int err = transfer_handlers[batch->ep->transfer_type](hc, transfer);
-
-	if (err) {
+	int err;
+	fibril_mutex_lock(&xhci_ep->guard);
+
+	if ((err = endpoint_activate_locked(ep, batch))) {
+		fibril_mutex_unlock(&xhci_ep->guard);
+		return err;
+	}
+
+	if ((err = transfer_handlers[batch->ep->transfer_type](hc, transfer))) {
 		endpoint_deactivate_locked(ep);
-		fibril_mutex_unlock(&ep->guard);
+		fibril_mutex_unlock(&xhci_ep->guard);
 		return err;
 	}
 
 	hc_ring_ep_doorbell(xhci_ep, batch->target.stream);
-	fibril_mutex_unlock(&ep->guard);
+	fibril_mutex_unlock(&xhci_ep->guard);
 	return EOK;
 }
