Index: uspace/drv/bus/usb/uhci/hc.c
===================================================================
--- uspace/drv/bus/usb/uhci/hc.c	(revision deb2e558ccc886ffade58c2d64c4943b78c3e855)
+++ 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 deb2e558ccc886ffade58c2d64c4943b78c3e855)
+++ 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 deb2e558ccc886ffade58c2d64c4943b78c3e855)
+++ 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
