Index: uspace/drv/bus/usb/ehci/ehci_bus.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_bus.c	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/drv/bus/usb/ehci/ehci_bus.c	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -135,45 +135,4 @@
 }
 
-static int ehci_device_online(device_t *device)
-{
-	int err;
-
-	/* Allow creation of new endpoints and transfers. */
-	usb_log_info("Device(%d): Going online.", device->address);
-	fibril_mutex_lock(&device->guard);
-	device->online = true;
-	fibril_mutex_unlock(&device->guard);
-
-	if ((err = ddf_fun_online(device->fun))) {
-		return err;
-	}
-
-	return EOK;
-}
-
-static int ehci_device_offline(device_t *device)
-{
-	int err;
-
-	/* Tear down all drivers working with the device. */
-	if ((err = ddf_fun_offline(device->fun))) {
-		return err;
-	}
-
-	/* At this point, all drivers are assumed to have already terminated
-	 * in a consistent way. The following code just cleans up hanging
-	 * transfers if there are any. */
-
-	/* Block creation of new endpoints and transfers. */
-	usb_log_info("Device(%d): Going offline.", device->address);
-	fibril_mutex_lock(&device->guard);
-	device->online = false;
-	fibril_mutex_unlock(&device->guard);
-
-	/* FIXME: Abort all transfers to all endpoints. */
-
-	return EOK;
-}
-
 static const bus_ops_t ehci_bus_ops = {
 	.parent = &usb2_bus_ops,
@@ -190,6 +149,4 @@
 	.batch_destroy = ehci_destroy_batch,
 	.batch_schedule = ehci_hc_schedule,
-	.device_online = ehci_device_online,
-	.device_offline = ehci_device_offline,
 };
 
Index: uspace/drv/bus/usb/uhci/hc.c
===================================================================
--- uspace/drv/bus/usb/uhci/hc.c	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/drv/bus/usb/uhci/hc.c	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -321,51 +321,16 @@
 }
 
-static int device_online(device_t *device)
-{
-	int err;
-	hc_t *instance = bus_to_hc(device->bus);
-	assert(instance);
-
-	/* Allow creation of new endpoints and transfers. */
-	usb_log_info("Device(%d): Going online.", device->address);
-	fibril_mutex_lock(&device->guard);
-	device->online = true;
-	fibril_mutex_unlock(&device->guard);
-
-	if ((err = ddf_fun_online(device->fun))) {
-		return err;
-	}
-
-	return EOK;
-}
-
-static int device_offline(device_t *device)
-{
-	int err;
-	hc_t *instance = bus_to_hc(device->bus);
-	assert(instance);
-
-	/* Tear down all drivers working with the device. */
-	if ((err = ddf_fun_offline(device->fun))) {
-		return err;
-	}
-
-	/* At this point, all drivers are assumed to have already terminated
-	 * in a consistent way. The following code just cleans up hanging
-	 * transfers if there are any. */
-
-	/* Block creation of new endpoints and transfers. */
-	usb_log_info("Device(%d): Going offline.", device->address);
-	fibril_mutex_lock(&device->guard);
-	device->online = false;
-	fibril_mutex_unlock(&device->guard);
-
-	/* Abort all transfers to all endpoints. */
-	transfer_list_abort_device(&instance->transfers_interrupt, device->address);
-	transfer_list_abort_device(&instance->transfers_control_slow, device->address);
-	transfer_list_abort_device(&instance->transfers_control_full, device->address);
-	transfer_list_abort_device(&instance->transfers_bulk_full, device->address);
-
-	return EOK;
+static void endpoint_unregister(endpoint_t *ep)
+{
+	usb2_bus_ops.endpoint_unregister(ep);
+
+	fibril_mutex_lock(&ep->guard);
+	if (ep->active_batch) {
+		uhci_transfer_batch_t *ub = uhci_transfer_batch_get(ep->active_batch);
+		uhci_transfer_batch_abort(ub);
+
+		assert(ep->active_batch == NULL);
+	}
+	fibril_mutex_unlock(&ep->guard);
 }
 
@@ -379,11 +344,10 @@
 	.status = hc_status,
 
+	.endpoint_unregister = endpoint_unregister,
 	.endpoint_count_bw = bandwidth_count_usb11,
+
 	.batch_create = create_transfer_batch,
 	.batch_schedule = hc_schedule,
 	.batch_destroy = destroy_transfer_batch,
-
-	.device_online = device_online,
-	.device_offline = device_offline,
 };
 
@@ -522,6 +486,6 @@
 static int hc_schedule(usb_transfer_batch_t *batch)
 {
+	assert(batch);
 	hc_t *instance = bus_to_hc(endpoint_get_bus(batch->ep));
-	assert(batch);
 
 	if (batch->target.address == uhci_rh_get_address(&instance->rh))
Index: uspace/drv/bus/usb/uhci/transfer_list.c
===================================================================
--- uspace/drv/bus/usb/uhci/transfer_list.c	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/drv/bus/usb/uhci/transfer_list.c	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -194,32 +194,6 @@
 	while (!list_empty(&instance->batch_list)) {
 		link_t * const current = list_first(&instance->batch_list);
-		uhci_transfer_batch_t *batch =
-		    uhci_transfer_batch_from_link(current);
+		uhci_transfer_batch_t *batch = uhci_transfer_batch_from_link(current);
 		transfer_list_remove_batch(instance, batch);
-		endpoint_abort(batch->base.ep);
-	}
-	fibril_mutex_unlock(&instance->guard);
-}
-
-/** Walk the list and finish all batches of a specified device with EINTR.
- *
- * @param[in] instance List to use.
- * @param[in] address Address of the specified device. Other addresses are skipped.
- */
-void transfer_list_abort_device(transfer_list_t *instance, usb_address_t address)
-{
-	fibril_mutex_lock(&instance->guard);
-	link_t *current = list_first(&instance->batch_list);
-	while (current && current != &instance->batch_list.head && !list_empty(&instance->batch_list)) {
-		link_t * const next = current->next;
-		uhci_transfer_batch_t *batch =
-		    uhci_transfer_batch_from_link(current);
-
-		if (batch->base.target.address == address) {
-			transfer_list_remove_batch(instance, batch);
-			endpoint_abort(batch->base.ep);
-		}
-
-		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 bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/drv/bus/usb/uhci/transfer_list.h	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -63,5 +63,4 @@
 void transfer_list_remove_finished(transfer_list_t *instance, list_t *done);
 void transfer_list_abort_all(transfer_list_t *instance);
-void transfer_list_abort_device(transfer_list_t *instance, usb_address_t address);
 
 #endif
Index: uspace/drv/bus/usb/uhci/uhci_batch.c
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_batch.c	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/drv/bus/usb/uhci/uhci_batch.c	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -64,4 +64,45 @@
 }
 
+/**
+ * Abort a transfer that is currently running.
+ * Call with endpoint guard locked.
+ */
+void uhci_transfer_batch_abort(uhci_transfer_batch_t *batch)
+{
+	assert(batch);
+
+	endpoint_t *ep = batch->base.ep;
+	assert(ep);
+	assert(fibril_mutex_is_locked(&ep->guard));
+	assert(ep->active_batch == &batch->base);
+
+	/*
+	 * TODO: Do some magic here to remove the batch from schedule.
+	 */
+
+	/*
+	 * Wait for 2 frames. If the transfer was being processed,
+	 * it shall be marked as finished already after 1ms.
+	 */
+	endpoint_wait_timeout_locked(ep, 2000);
+	if (ep->active_batch != &batch->base)
+		return;
+
+	/*
+	 * Now, we can be sure the transfer is not scheduled,
+	 * and as such will not be completed. We now own the batch.
+	 */
+	endpoint_deactivate_locked(ep);
+
+	/* Leave the critical section for finishing the batch. */
+	fibril_mutex_unlock(&ep->guard);
+
+	batch->base.error = EINTR;
+	batch->base.transfered_size = 0;
+	usb_transfer_batch_finish(&batch->base);
+
+	fibril_mutex_lock(&ep->guard);
+}
+
 /** Allocate memory and initialize internal data structure.
  *
Index: uspace/drv/bus/usb/uhci/uhci_batch.h
===================================================================
--- uspace/drv/bus/usb/uhci/uhci_batch.h	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/drv/bus/usb/uhci/uhci_batch.h	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -70,5 +70,5 @@
 int uhci_transfer_batch_prepare(uhci_transfer_batch_t *);
 bool uhci_transfer_batch_check_completed(uhci_transfer_batch_t *);
-void uhci_transfer_batch_finish(uhci_transfer_batch_t *);
+void uhci_transfer_batch_abort(uhci_transfer_batch_t *);
 void uhci_transfer_batch_destroy(uhci_transfer_batch_t *);
 
Index: uspace/drv/bus/usb/xhci/bus.c
===================================================================
--- uspace/drv/bus/usb/xhci/bus.c	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/drv/bus/usb/xhci/bus.c	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -209,28 +209,4 @@
 	xhci_device_t *xhci_dev = xhci_device_get(dev);
 
-	/* Block creation of new endpoints and transfers. */
-	usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*xhci_dev));
-	fibril_mutex_lock(&dev->guard);
-	dev->online = false;
-	fibril_mutex_unlock(&dev->guard);
-
-	/* Abort running transfers. */
-	usb_log_debug2("Aborting all active transfers to device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*xhci_dev));
-	for (usb_endpoint_t i = 0; i < USB_ENDPOINT_MAX; ++i) {
-		xhci_endpoint_t *ep = xhci_device_get_endpoint(xhci_dev, i);
-		if (!ep)
-			continue;
-
-		endpoint_abort(&ep->base);
-	}
-
-	/* TODO: Figure out how to handle errors here. So far, they are reported and skipped. */
-
-	/* Make DDF (and all drivers) forget about the device. */
-	if ((err = ddf_fun_unbind(dev->fun))) {
-		usb_log_warning("Failed to unbind DDF function of device " XHCI_DEV_FMT ": %s",
-		    XHCI_DEV_ARGS(*xhci_dev), str_error(err));
-	}
-
 	/* Disable the slot, dropping all endpoints. */
 	const uint32_t slot_id = xhci_dev->slot_id;
@@ -241,16 +217,4 @@
 
 	bus->devices_by_slot[slot_id] = NULL;
-
-	/* Unregister remaining endpoints, freeing memory. */
-	for (usb_endpoint_t i = 0; i < USB_ENDPOINT_MAX; ++i) {
-		if (!dev->endpoints[i])
-			continue;
-
-		bus_endpoint_remove(dev->endpoints[i]);
-	}
-
-	/* Destroy DDF device. */
-	/* XXX: Not a good idea, this method should not destroy devices. */
-	hcd_ddf_fun_destroy(dev);
 }
 
@@ -273,13 +237,4 @@
 	if ((err = hc_configure_device(bus->hc, dev->slot_id))) {
 		usb_log_warning("Failed to configure device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*dev));
-	}
-
-	/* Allow creation of new endpoints and transfers. */
-	usb_log_debug2("Device " XHCI_DEV_FMT " going online.", XHCI_DEV_ARGS(*dev));
-	fibril_mutex_lock(&dev_base->guard);
-	dev_base->online = true;
-	fibril_mutex_unlock(&dev_base->guard);
-
-	if ((err = ddf_fun_online(dev_base->fun))) {
 		return err;
 	}
@@ -294,5 +249,5 @@
  * Bus callback.
  */
-static int device_offline(device_t *dev_base)
+static void device_offline(device_t *dev_base)
 {
 	int err;
@@ -303,24 +258,4 @@
 	xhci_device_t *dev = xhci_device_get(dev_base);
 	assert(dev);
-
-	/* Tear down all drivers working with the device. */
-	if ((err = ddf_fun_offline(dev_base->fun))) {
-		return err;
-	}
-
-	/* Block creation of new endpoints and transfers. */
-	usb_log_debug2("Device " XHCI_DEV_FMT " going offline.", XHCI_DEV_ARGS(*dev));
-	fibril_mutex_lock(&dev_base->guard);
-	dev_base->online = false;
-	fibril_mutex_unlock(&dev_base->guard);
-
-	/* We will need the endpoint array later for DS deallocation. */
-	endpoint_t *endpoints[USB_ENDPOINT_MAX];
-	memcpy(endpoints, dev->base.endpoints, sizeof(endpoints));
-
-	for (usb_endpoint_t i = 1; i < USB_ENDPOINT_MAX; ++i) {
-		/* FIXME: Asserting here that the endpoint is not active. If not, EBUSY? */
-		dev->base.endpoints[i] = NULL;
-	}
 
 	/* Issue one HC command to simultaneously drop all endpoints except zero. */
@@ -329,15 +264,4 @@
 		    XHCI_DEV_ARGS(*dev));
 	}
-
-	/* Tear down TRB ring / PSA. */
-	for (unsigned i = 1; i < ARRAY_SIZE(endpoints); ++i) {
-		if (!endpoints[i])
-			continue;
-
-		/* Bus reference */
-		endpoint_del_ref(endpoints[i]);
-	}
-
-	return EOK;
 }
 
@@ -378,35 +302,9 @@
 
 /**
- * Register an andpoint to the bus. Allocate its transfer ring(s) and inform
- * xHC about it.
+ * Register an andpoint to the xHC.
  *
  * Bus callback.
  */
 static int endpoint_register(endpoint_t *ep_base)
-{
-	int err;
-	xhci_bus_t *bus = bus_to_xhci_bus(endpoint_get_bus(ep_base));
-	xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
-
-	xhci_device_t *dev = xhci_device_get(ep_base->device);
-
-	usb_log_info("Endpoint " XHCI_EP_FMT " registered to XHCI bus.", XHCI_EP_ARGS(*ep));
-
-	xhci_ep_ctx_t ep_ctx;
-	xhci_setup_endpoint_context(ep, &ep_ctx);
-
-	if ((err = hc_add_endpoint(bus->hc, dev->slot_id, xhci_endpoint_index(ep), &ep_ctx)))
-		return err;
-
-	return EOK;
-}
-
-/**
- * Unregister an endpoint. If the device is still available, inform the xHC
- * about it. Destroy resources allocated when registering.
- *
- * Bus callback.
- */
-static void endpoint_unregister(endpoint_t *ep_base)
 {
 	int err;
@@ -415,5 +313,25 @@
 	xhci_device_t *dev = xhci_device_get(ep_base->device);
 
-	usb_log_info("Endpoint " XHCI_EP_FMT " unregistered from XHCI bus.", XHCI_EP_ARGS(*ep));
+	xhci_ep_ctx_t ep_ctx;
+	xhci_setup_endpoint_context(ep, &ep_ctx);
+
+	if ((err = hc_add_endpoint(bus->hc, dev->slot_id, xhci_endpoint_index(ep), &ep_ctx)))
+		return err;
+
+	return EOK;
+}
+
+/**
+ * Unregister an endpoint. If the device is still available, inform the xHC
+ * about it.
+ *
+ * Bus callback.
+ */
+static void endpoint_unregister(endpoint_t *ep_base)
+{
+	int err;
+	xhci_bus_t *bus = bus_to_xhci_bus(endpoint_get_bus(ep_base));
+	xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
+	xhci_device_t *dev = xhci_device_get(ep_base->device);
 
 	/* If device slot is still available, drop the endpoint. */
Index: uspace/lib/usbhost/include/usb/host/bus.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/bus.h	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/lib/usbhost/include/usb/host/bus.h	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -105,5 +105,5 @@
 	void (*device_remove)(device_t *);
 	int (*device_online)(device_t *);			/**< Optional */
-	int (*device_offline)(device_t *);			/**< Optional */
+	void (*device_offline)(device_t *);			/**< Optional */
 	endpoint_t *(*endpoint_create)(device_t *, const usb_endpoint_descriptors_t *);
 
Index: uspace/lib/usbhost/include/usb/host/endpoint.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/endpoint.h	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/lib/usbhost/include/usb/host/endpoint.h	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -44,4 +44,5 @@
 #include <fibril_synch.h>
 #include <stdbool.h>
+#include <sys/time.h>
 #include <usb/usb.h>
 #include <usb/host/bus.h>
@@ -93,17 +94,7 @@
 extern void endpoint_del_ref(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_wait_timeout_locked(endpoint_t *ep, suseconds_t);
 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 *);
 
 /* Calculate bandwidth */
Index: uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/lib/usbhost/include/usb/host/usb_transfer_batch.h	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -112,5 +112,4 @@
 
 /** Batch finalization. */
-void usb_transfer_batch_abort(usb_transfer_batch_t *);
 void usb_transfer_batch_finish(usb_transfer_batch_t *);
 
Index: uspace/lib/usbhost/src/bus.c
===================================================================
--- uspace/lib/usbhost/src/bus.c	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/lib/usbhost/src/bus.c	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -43,4 +43,5 @@
 #include <mem.h>
 #include <stdio.h>
+#include <str_error.h>
 #include <usb/debug.h>
 
@@ -97,4 +98,6 @@
 /**
  * Invoke the device_enumerate bus operation.
+ *
+ * There's no need to synchronize here, because no one knows the device yet.
  */
 int bus_device_enumerate(device_t *dev)
@@ -106,46 +109,189 @@
 		return ENOTSUP;
 
-	return ops->device_enumerate(dev);
-}
-
-/**
- * Invoke the device_remove bus operation.
+	if (dev->online) {
+		fibril_mutex_unlock(&dev->guard);
+		return EINVAL;
+	}
+
+	const int r = ops->device_enumerate(dev);
+	if (!r) {
+		dev->online = true;
+
+		fibril_mutex_lock(&dev->hub->guard);
+		list_append(&dev->link, &dev->hub->devices);
+		fibril_mutex_unlock(&dev->hub->guard);
+	}
+
+	return r;
+}
+
+/**
+ * Clean endpoints and children that could have been left behind after
+ * asking the driver of device to offline/remove a device.
+ *
+ * Note that EP0's lifetime is shared with the device, and as such is not
+ * touched.
+ */
+static void device_clean_ep_children(device_t *dev, const char *op)
+{
+	assert(fibril_mutex_is_locked(&dev->guard));
+
+	/* Unregister endpoints left behind. */
+	for (usb_endpoint_t i = 1; i < USB_ENDPOINT_MAX; ++i) {
+		if (!dev->endpoints[i])
+			continue;
+
+		usb_log_warning("USB device '%s' driver left endpoint %u registered after %s.",
+		    ddf_fun_get_name(dev->fun), i, op);
+
+		endpoint_t * const ep = dev->endpoints[i];
+		endpoint_add_ref(ep);
+		
+		fibril_mutex_unlock(&dev->guard);
+		bus_endpoint_remove(ep);
+		fibril_mutex_lock(&dev->guard);
+
+		assert(dev->endpoints[i] == NULL);
+	}
+
+	/* Remove also orphaned children. */
+	while (!list_empty(&dev->devices)) {
+		device_t * const child = list_get_instance(list_first(&dev->devices), device_t, link);
+
+		usb_log_warning("USB device '%s' driver left device '%s' behind after %s.",
+		    ddf_fun_get_name(dev->fun), ddf_fun_get_name(child->fun), op);
+		/*
+		 * The child node won't disappear, because its parent's driver
+		 * is already dead. And the child will need the guard to remove
+		 * itself from the list.
+		 */
+		fibril_mutex_unlock(&dev->guard);
+		bus_device_remove(child);
+		fibril_mutex_lock(&dev->guard);
+	}
+	assert(list_empty(&dev->devices));
+}
+
+/**
+ * Resolve a USB device that is gone.
  */
 void bus_device_remove(device_t *dev)
 {
 	assert(dev);
+	assert(dev->fun == NULL);
 
 	const bus_ops_t *ops = BUS_OPS_LOOKUP(dev->bus->ops, device_remove);
 	assert(ops);
 
-	return ops->device_remove(dev);
-}
-
-/**
- * Invoke the device_online bus operation.
+	/* First, block new transfers and operations. */
+	fibril_mutex_lock(&dev->guard);
+	dev->online = false;
+
+	/* Unbinding will need guard unlocked. */
+	fibril_mutex_unlock(&dev->guard);
+
+	/* Remove our device from our hub's children. */
+	fibril_mutex_lock(&dev->hub->guard);
+	list_remove(&dev->link);
+	fibril_mutex_unlock(&dev->hub->guard);
+
+	/*
+	 * Unbind the DDF function. That will result in dev_gone called in
+	 * driver, which shall destroy its pipes and remove its children.
+	 */
+	const int err = ddf_fun_unbind(dev->fun);
+	if (err) {
+		usb_log_error("Failed to unbind USB device '%s': %s",
+		    ddf_fun_get_name(dev->fun), str_error(err));
+		return;
+	}
+
+	/* Remove what driver left behind */
+	fibril_mutex_lock(&dev->guard);
+	device_clean_ep_children(dev, "removing");
+
+	/* Tell the HC to release its resources. */
+	ops->device_remove(dev);
+
+	/* Release the EP0 bus reference */
+	endpoint_del_ref(dev->endpoints[0]);
+
+	/* Destroy the function, freeing also the device, unlocking mutex. */
+	ddf_fun_destroy(dev->fun);
+}
+
+/**
+ * The user wants this device back online.
  */
 int bus_device_online(device_t *dev)
 {
+	int err;
 	assert(dev);
 
+	fibril_mutex_lock(&dev->guard);
+	if (dev->online) {
+		fibril_mutex_unlock(&dev->guard);
+		return EINVAL;
+	}
+
+	/* First, tell the HC driver. */
 	const bus_ops_t *ops = BUS_OPS_LOOKUP(dev->bus->ops, device_online);
-	if (!ops)
-		return ENOTSUP;
-
-	return ops->device_online(dev);
-}
-
-/**
- * Invoke the device_offline bus operation.
+	if (ops && (err = ops->device_online(dev))) {
+		usb_log_warning("Host controller refused to make device '%s' online: %s",
+		    ddf_fun_get_name(dev->fun), str_error(err));
+		return err;
+	}
+
+	/* Allow creation of new endpoints and communication with the device. */
+	dev->online = true;
+
+	/* Onlining will need the guard */
+	fibril_mutex_unlock(&dev->guard);
+
+	if ((err = ddf_fun_online(dev->fun))) {
+		usb_log_warning("Failed to take device '%s' online: %s",
+		    ddf_fun_get_name(dev->fun), str_error(err));
+		return err;
+	}
+
+	usb_log_info("USB Device '%s' offlined.", ddf_fun_get_name(dev->fun));
+	return EOK;
+}
+
+/**
+ * The user requested to take this device offline.
  */
 int bus_device_offline(device_t *dev)
 {
+	int err;
 	assert(dev);
 
+	/* Make sure we're the one who offlines this device */
+	if (!dev->online)
+		return ENOENT;
+
+	/*
+	 * XXX: If the device is removed/offlined just now, this can fail on
+	 * assertion. We most probably need some kind of enum status field to
+	 * make the synchronization work.
+	 */
+	
+	/* Tear down all drivers working with the device. */
+	if ((err = ddf_fun_offline(dev->fun))) {
+		return err;
+	}
+
+	fibril_mutex_lock(&dev->guard);
+	dev->online = false;
+	device_clean_ep_children(dev, "offlining");
+
+	/* Tell also the HC driver. */
 	const bus_ops_t *ops = BUS_OPS_LOOKUP(dev->bus->ops, device_offline);
-	if (!ops)
-		return ENOTSUP;
-
-	return ops->device_offline(dev);
+	if (ops)
+		ops->device_offline(dev);
+
+	fibril_mutex_unlock(&dev->guard);
+	usb_log_info("USB Device '%s' offlined.", ddf_fun_get_name(dev->fun));
+	return EOK;
 }
 
@@ -269,7 +415,4 @@
 	fibril_mutex_unlock(&device->guard);
 
-	/* Abort a transfer batch, if there was any */
-	endpoint_abort(ep);
-
 	/* Bus reference */
 	endpoint_del_ref(ep);
Index: uspace/lib/usbhost/src/ddf_helpers.c
===================================================================
--- uspace/lib/usbhost/src/ddf_helpers.c	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/lib/usbhost/src/ddf_helpers.c	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -336,12 +336,7 @@
 	assert(device);
 
-	hc_device_t *hcd = dev_to_hcd(device);
-	assert(hcd);
-	assert(hcd->bus);
+	device_t *victim = NULL;
 
 	fibril_mutex_lock(&hub->guard);
-
-	device_t *victim = NULL;
-
 	list_foreach(hub->devices, link, device_t, it) {
 		if (it->port == port) {
@@ -350,22 +345,15 @@
 		}
 	}
-	if (victim) {
-		assert(victim->fun);
-		assert(victim->port == port);
-		assert(victim->hub == hub);
-		list_remove(&victim->link);
-		fibril_mutex_unlock(&hub->guard);
-		const int ret = ddf_fun_unbind(victim->fun);
-		if (ret == EOK) {
-			bus_device_remove(victim);
-			ddf_fun_destroy(victim->fun);
-		} else {
-			usb_log_warning("Failed to unbind device `%s': %s\n",
-			    ddf_fun_get_name(victim->fun), str_error(ret));
-		}
-		return EOK;
-	}
 	fibril_mutex_unlock(&hub->guard);
-	return ENOENT;
+
+	if (!victim)
+		return ENOENT;
+
+	assert(victim->fun);
+	assert(victim->port == port);
+	assert(victim->hub == hub);
+
+	bus_device_remove(victim);
+	return EOK;
 }
 
@@ -477,8 +465,4 @@
 	}
 
-	fibril_mutex_lock(&hub->guard);
-	list_append(&dev->link, &hub->devices);
-	fibril_mutex_unlock(&hub->guard);
-
 	return EOK;
 
Index: uspace/lib/usbhost/src/endpoint.c
===================================================================
--- uspace/lib/usbhost/src/endpoint.c	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/lib/usbhost/src/endpoint.c	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -124,4 +124,15 @@
 
 /**
+ * Wait until the endpoint have no transfer scheduled.
+ */
+void endpoint_wait_timeout_locked(endpoint_t *ep, suseconds_t timeout)
+{
+	assert(fibril_mutex_is_locked(&ep->guard));
+
+	while (ep->active_batch != NULL)
+		fibril_condvar_wait_timeout(&ep->avail, &ep->guard, timeout);
+}
+
+/**
  * Mark the endpoint as active and block access for further fibrils. If the
  * endpoint is already active, it will block on ep->avail condvar.
@@ -138,8 +149,6 @@
 	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);
+
+	endpoint_wait_timeout_locked(ep, 0);
 	ep->active_batch = batch;
 }
@@ -160,22 +169,4 @@
 	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);
-	usb_transfer_batch_t *batch = ep->active_batch;
-	endpoint_deactivate_locked(ep);
-	fibril_mutex_unlock(&ep->guard);
-
-	if (batch)
-		usb_transfer_batch_abort(batch);
 }
 
Index: uspace/lib/usbhost/src/usb_transfer_batch.c
===================================================================
--- uspace/lib/usbhost/src/usb_transfer_batch.c	(revision bad4a050d11dd44a634a911a7e5a42918d984aab)
+++ uspace/lib/usbhost/src/usb_transfer_batch.c	(revision 0892663adc814c89139511bb2bea33fffe20ff46)
@@ -128,16 +128,4 @@
 
 /**
- * Finish a transfer batch as an aborted one.
- */
-void usb_transfer_batch_abort(usb_transfer_batch_t *batch)
-{
-	assert(batch);
-	assert(batch->ep);
-
-	batch->error = EAGAIN;
-	usb_transfer_batch_finish(batch);
-}
-
-/**
  * @}
  */
