Index: uspace/drv/bus/usb/xhci/bus.c
===================================================================
--- uspace/drv/bus/usb/xhci/bus.c	(revision 2c091a6813c05f6aabf618e99280e40d636e89d1)
+++ uspace/drv/bus/usb/xhci/bus.c	(revision a4e2688242f9b376a6077b18fc9e81977110922c)
@@ -118,5 +118,12 @@
 	xhci_device_t *xhci_dev = xhci_device_get(dev);
 
-	// TODO: Release remaining EPs
+	/* Release remaining endpoints. */
+	for (size_t i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
+		if (!xhci_dev->endpoints[i])
+			continue;
+
+		// FIXME: ignoring return code
+		bus_release_endpoint(&bus->base, &xhci_dev->endpoints[i]->base);
+	}
 
 	hashed_device_t *hashed_dev;
@@ -270,5 +277,5 @@
 	xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
 	assert(bus);
-	
+
 	xhci_endpoint_t *ep;
 	int res = xhci_endpoint_find_by_target(bus, target, &ep);
Index: uspace/drv/bus/usb/xhci/endpoint.c
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.c	(revision 2c091a6813c05f6aabf618e99280e40d636e89d1)
+++ uspace/drv/bus/usb/xhci/endpoint.c	(revision a4e2688242f9b376a6077b18fc9e81977110922c)
@@ -182,4 +182,9 @@
 	assert(ep);
 
+	/* Offline devices don't create new endpoints other than EP0. */
+	if (!dev->online) {
+		return EAGAIN;
+	}
+
 	int err = ENOMEM;
 	const usb_endpoint_t ep_num = ep->base.target.endpoint;
Index: uspace/drv/bus/usb/xhci/endpoint.h
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.h	(revision 2c091a6813c05f6aabf618e99280e40d636e89d1)
+++ uspace/drv/bus/usb/xhci/endpoint.h	(revision a4e2688242f9b376a6077b18fc9e81977110922c)
@@ -94,11 +94,11 @@
 	/** Flag indicating whether the device is USB3 (it's USB2 otherwise). */
 	bool usb3;
+
+	/** True if the device can add new endpoints and schedule transfers. */
+	volatile bool online;
 } xhci_device_t;
 
 int xhci_endpoint_init(xhci_endpoint_t *, xhci_bus_t *);
 void xhci_endpoint_fini(xhci_endpoint_t *);
-
-int xhci_device_init(xhci_device_t *, xhci_bus_t *, usb_address_t);
-void xhci_device_fini(xhci_device_t *);
 
 uint8_t xhci_endpoint_dci(xhci_endpoint_t *);
Index: uspace/drv/bus/usb/xhci/rh.c
===================================================================
--- uspace/drv/bus/usb/xhci/rh.c	(revision 2c091a6813c05f6aabf618e99280e40d636e89d1)
+++ uspace/drv/bus/usb/xhci/rh.c	(revision a4e2688242f9b376a6077b18fc9e81977110922c)
@@ -68,8 +68,6 @@
 	rh->hc = hc;
 	rh->max_ports = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_PORTS);
-	rh->devices = (xhci_device_t **) malloc(rh->max_ports * sizeof(xhci_device_t *));
+	rh->devices = (xhci_device_t **) calloc(rh->max_ports, sizeof(xhci_device_t *));
 	hc->rh.hc_device = device;
-
-	memset(rh->devices, 0, rh->max_ports * sizeof(xhci_device_t *));
 
 	return device_init(&hc->rh.device);
@@ -149,4 +147,9 @@
 	usb_log_debug2("Obtained USB address: %d.\n", dev->address);
 
+	/* From now on, the device is officially online, yay! */
+	fibril_mutex_lock(&dev->guard);
+	xhci_dev->online = true;
+	fibril_mutex_unlock(&dev->guard);
+
 	// XXX: Going around bus, duplicating code
 	ep0_base->device = dev;
@@ -264,15 +267,61 @@
 static int handle_disconnected_device(xhci_rh_t *rh, uint8_t port_id)
 {
+	assert(rh);
+	int err;
+
 	/* Find XHCI device by the port. */
 	xhci_device_t *dev = rh->devices[port_id - 1];
 	if (!dev) {
-		/* Must be extraneous call */
+		/* Must be extraneous call. */
 		return EOK;
 	}
 
-	usb_log_info("Device at port %u has been disconnected.", port_id);
-
-	// TODO: Destroy DDF function using _gone.
-	// TODO: Remove device endpoints on the bus.
+	usb_log_info("Device '%s' at port %u has been disconnected.", ddf_fun_get_name(dev->base.fun), port_id);
+
+	/* Block creation of new endpoints and transfers. */
+	fibril_mutex_lock(&dev->base.guard);
+	dev->online = false;
+	fibril_mutex_unlock(&dev->base.guard);
+
+	fibril_mutex_lock(&rh->device.guard);
+	list_remove(&dev->base.link);
+	fibril_mutex_unlock(&rh->device.guard);
+
+	rh->devices[port_id - 1] = NULL;
+	usb_log_debug2("Aborting all active transfers to '%s'.", ddf_fun_get_name(dev->base.fun));
+
+	/* Abort running transfers. */
+	for (size_t i = 0; i < ARRAY_SIZE(dev->endpoints); ++i) {
+		xhci_endpoint_t *ep = dev->endpoints[i];
+		if (!ep || !ep->base.active)
+			continue;
+
+		if ((err = xhci_transfer_abort(&ep->active_transfer))) {
+			usb_log_warning("Failed to abort active %s transfer to "
+			    " endpoint %d of detached device '%s': %s",
+			    usb_str_transfer_type(ep->base.transfer_type),
+			    ep->base.target.endpoint, ddf_fun_get_name(dev->base.fun),
+			    str_error(err));
+		}
+	}
+
+	/* Make DDF (and all drivers) forget about the device. */
+	if ((err = ddf_fun_unbind(dev->base.fun))) {
+		usb_log_warning("Failed to unbind DDF function of detached device '%s': %s",
+		    ddf_fun_get_name(dev->base.fun), str_error(err));
+	}
+
+	/* FIXME:
+	 * A deadlock happens on the previous line. For some reason, the HID driver
+	 * does not fully release its DDF function (tracked it down to release_endpoint
+ 	 * in usb_remote.c so far).
+	 *
+	 * For that reason as well, the following 3 lines are untested.
+	 */
+
+	xhci_bus_remove_device(&rh->hc->bus, rh->hc, &dev->base);
+	hc_disable_slot(rh->hc, dev->slot_id);
+	hcd_ddf_device_destroy(&dev->base);
+
 	// TODO: Free device context.
 	// TODO: Free TRB rings.
Index: uspace/drv/bus/usb/xhci/transfers.c
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.c	(revision 2c091a6813c05f6aabf618e99280e40d636e89d1)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision a4e2688242f9b376a6077b18fc9e81977110922c)
@@ -248,4 +248,13 @@
 }
 
+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;
+}
+
 int xhci_handle_transfer_event(xhci_hc_t* hc, xhci_trb_t* trb)
 {
@@ -304,4 +313,11 @@
 	assert(xhci_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.target.endpoint) {
+		return EAGAIN;
+	}
+
 	const usb_transfer_type_t type = batch->ep->transfer_type;
 	assert(type >= 0 && type < ARRAY_SIZE(transfer_handlers));
@@ -321,5 +337,5 @@
 		return err;
 
-	const uint8_t slot_id = xhci_ep_to_dev(xhci_ep)->slot_id;
+	const uint8_t slot_id = xhci_dev->slot_id;
 	const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */
 	return hc_ring_doorbell(hc, slot_id, target);
Index: uspace/drv/bus/usb/xhci/transfers.h
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.h	(revision 2c091a6813c05f6aabf618e99280e40d636e89d1)
+++ uspace/drv/bus/usb/xhci/transfers.h	(revision a4e2688242f9b376a6077b18fc9e81977110922c)
@@ -55,7 +55,8 @@
 
 xhci_transfer_t* xhci_transfer_create(endpoint_t *);
-int xhci_transfer_schedule(xhci_hc_t*, usb_transfer_batch_t *);
-int xhci_handle_transfer_event(xhci_hc_t*, xhci_trb_t*);
-void xhci_transfer_destroy(xhci_transfer_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 *);
 
 static inline xhci_transfer_t *xhci_transfer_from_batch(usb_transfer_batch_t *batch)
