Index: uspace/drv/bus/usb/uhci/hc.c
===================================================================
--- uspace/drv/bus/usb/uhci/hc.c	(revision 415c5116c12fd6d1b99d9cb3a5f9ddf5c125b5ea)
+++ uspace/drv/bus/usb/uhci/hc.c	(revision 5dfb70c9d83e87a81d5521cc846a48fe8f84f09b)
@@ -321,4 +321,41 @@
 }
 
+static int device_online(device_t *device)
+{
+	// FIXME: Implement me!
+
+	return ENOTSUP;
+}
+
+static int device_offline(device_t *device)
+{
+	hc_t *instance = bus_to_hc(device->bus);
+	assert(instance);
+
+	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);
+
+	/* 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 int hc_status(bus_t *, uint32_t *);
 static int hc_schedule(usb_transfer_batch_t *);
@@ -334,4 +371,7 @@
 	.batch_schedule = hc_schedule,
 	.batch_destroy = destroy_transfer_batch,
+
+	.device_online = device_online,
+	.device_offline = device_offline,
 };
 
Index: uspace/drv/bus/usb/uhci/transfer_list.c
===================================================================
--- uspace/drv/bus/usb/uhci/transfer_list.c	(revision 415c5116c12fd6d1b99d9cb3a5f9ddf5c125b5ea)
+++ uspace/drv/bus/usb/uhci/transfer_list.c	(revision 5dfb70c9d83e87a81d5521cc846a48fe8f84f09b)
@@ -202,4 +202,28 @@
 }
 
+/** 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);
+}
+
 /** Remove a transfer batch from the list and queue.
  *
Index: uspace/drv/bus/usb/uhci/transfer_list.h
===================================================================
--- uspace/drv/bus/usb/uhci/transfer_list.h	(revision 415c5116c12fd6d1b99d9cb3a5f9ddf5c125b5ea)
+++ uspace/drv/bus/usb/uhci/transfer_list.h	(revision 5dfb70c9d83e87a81d5521cc846a48fe8f84f09b)
@@ -63,4 +63,5 @@
 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/lib/usbhost/src/usb2_bus.c
===================================================================
--- uspace/lib/usbhost/src/usb2_bus.c	(revision 415c5116c12fd6d1b99d9cb3a5f9ddf5c125b5ea)
+++ uspace/lib/usbhost/src/usb2_bus.c	(revision 5dfb70c9d83e87a81d5521cc846a48fe8f84f09b)
@@ -352,45 +352,4 @@
 }
 
-static int usb2_bus_device_online(device_t *device)
-{
-	usb2_bus_t *bus = bus_to_usb2_bus(device->bus);
-	assert(bus);
-
-	// FIXME: Implement me!
-
-	return ENOTSUP;
-}
-
-static int usb2_bus_device_offline(device_t *device)
-{
-	usb2_bus_t *bus = bus_to_usb2_bus(device->bus);
-	assert(bus);
-
-	int err;
-	/* Tear down all drivers working with the device. */
-	if ((err = ddf_fun_offline(device->fun))) {
-		return err;
-	}
-
-	/* 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: This implementation leaves sleeping parts of drivers around.
-	 * With XHCI bus, the HID driver disengages and completely deactivates
-	 * when the DDF function is offlined. In USB2 bus, the driver receives
-	 * dev_remove and disengages "on paper" but later when interrupt message arrives,
-	 * some sleeping code is woken up and crashes the driver.
-	 *
-	 * The XHCI does 2 extra things that might prevent this behavior:
-	 *   (1) deconfigure the device,
-	 *   (2) deallocate all transfer TRB rings
-	 */
-
-	return EOK;
-}
-
 static endpoint_t *usb2_bus_create_ep(device_t *dev, const usb_endpoint_desc_t *desc)
 {
@@ -496,6 +455,4 @@
 	.device_enumerate = usb2_bus_device_enumerate,
 	.device_find_endpoint = usb2_bus_find_ep,
-	.device_online = usb2_bus_device_online,
-	.device_offline = usb2_bus_device_offline,
 	.endpoint_create = usb2_bus_create_ep,
 	.endpoint_register = usb2_bus_register_ep,
