Index: uspace/drv/bus/usb/xhci/commands.c
===================================================================
--- uspace/drv/bus/usb/xhci/commands.c	(revision 327f14759b992c17c88f75a6ddd0e2aa077e1957)
+++ uspace/drv/bus/usb/xhci/commands.c	(revision b7244940340ca14345eb61f436e6e36c4e2c0a21)
@@ -46,4 +46,5 @@
 #define TRB_SET_TCS(trb, tcs)   (trb).control |= host2xhci(32, ((tcs &0x1) << 9))
 #define TRB_SET_TYPE(trb, type) (trb).control |= host2xhci(32, (type) << 10)
+#define TRB_SET_DC(trb, dc)     (trb).control |= host2xhci(32, (dc) << 9)
 #define TRB_SET_EP(trb, ep)     (trb).control |= host2xhci(32, ((ep) & 0x5) << 16)
 #define TRB_SET_STREAM(trb, st) (trb).control |= host2xhci(32, ((st) & 0xFFFF) << 16)
@@ -323,13 +324,18 @@
 	assert(hc);
 	assert(cmd);
-	assert(ictx);
-
-	xhci_trb_clean(&cmd->trb);
-
-	uint64_t phys_addr = (uint64_t) addr_to_phys(ictx);
-	TRB_SET_ICTX(cmd->trb, phys_addr);
+
+	xhci_trb_clean(&cmd->trb);
+
+	if (!cmd->deconfigure) {
+		/* If the DC flag is on, input context is not evaluated. */
+		assert(ictx);
+
+		uint64_t phys_addr = (uint64_t) addr_to_phys(ictx);
+		TRB_SET_ICTX(cmd->trb, phys_addr);
+	}
 
 	TRB_SET_TYPE(cmd->trb, XHCI_TRB_TYPE_CONFIGURE_ENDPOINT_CMD);
 	TRB_SET_SLOT(cmd->trb, cmd->slot_id);
+	TRB_SET_DC(cmd->trb, cmd->deconfigure);
 
 	return enqueue_command(hc, cmd, 0, 0);
Index: uspace/drv/bus/usb/xhci/commands.h
===================================================================
--- uspace/drv/bus/usb/xhci/commands.h	(revision 327f14759b992c17c88f75a6ddd0e2aa077e1957)
+++ uspace/drv/bus/usb/xhci/commands.h	(revision b7244940340ca14345eb61f436e6e36c4e2c0a21)
@@ -58,4 +58,5 @@
 	uint32_t slot_id;
 	uint32_t status;
+	bool deconfigure;
 
 	bool completed;
Index: uspace/drv/bus/usb/xhci/endpoint.c
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.c	(revision 327f14759b992c17c88f75a6ddd0e2aa077e1957)
+++ uspace/drv/bus/usb/xhci/endpoint.c	(revision b7244940340ca14345eb61f436e6e36c4e2c0a21)
@@ -242,26 +242,4 @@
 };
 
-static int create_valid_input_ctx(xhci_input_ctx_t **out_ictx)
-{
-	xhci_input_ctx_t *ictx = malloc32(sizeof(xhci_input_ctx_t));
-	if (!ictx) {
-		return ENOMEM;
-	}
-
-	memset(ictx, 0, sizeof(xhci_input_ctx_t));
-
-	// Quoting sec. 4.6.6: A1, D0, D1 are down, A0 is up.
-	XHCI_INPUT_CTRL_CTX_ADD_CLEAR(ictx->ctrl_ctx, 1);
-	XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 0);
-	XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 1);
-	XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 0);
-
-	if (out_ictx) {
-		*out_ictx = ictx;
-	}
-
-	return EOK;
-}
-
 int xhci_device_add_endpoint(xhci_device_t *dev, xhci_endpoint_t *ep)
 {
@@ -300,34 +278,15 @@
 	}
 
-	/* Issue configure endpoint command (sec 4.3.5). */
-	xhci_input_ctx_t *ictx;
-	if ((err = create_valid_input_ctx(&ictx))) {
+	/* Add endpoint. */
+	xhci_ep_ctx_t ep_ctx;
+	memset(&ep_ctx, 0, sizeof(xhci_ep_ctx_t));
+	setup_ep_ctx_helpers[ep->base.transfer_type](ep, &ep_ctx);
+
+	if ((err = hc_add_endpoint(dev->hc, dev->slot_id, xhci_endpoint_index(ep), &ep_ctx))) {
 		goto err_ds;
 	}
 
-	const unsigned ep_idx = xhci_endpoint_index(ep);
-	XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, ep_idx + 1); /* Preceded by slot ctx */
-	setup_ep_ctx_helpers[ep->base.transfer_type](ep, &ictx->endpoint_ctx[ep_idx]);
-
-	xhci_cmd_t cmd;
-	xhci_cmd_init(&cmd);
-
-	cmd.slot_id = dev->slot_id;
-
-	if ((err = xhci_send_configure_endpoint_command(dev->hc, &cmd, ictx))) {
-		goto err_ictx;
-	}
-
-	if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT))) {
-		goto err_ictx;
-	}
-
-	xhci_cmd_fini(&cmd);
-
-	free32(ictx);
-	return EOK;
-
-err_ictx:
-	free32(ictx);
+	return EOK;
+
 err_ds:
 	xhci_endpoint_free_transfer_ds(ep);
@@ -356,27 +315,8 @@
 	}
 
-	/* Issue configure endpoint command to drop this endpoint. */
-	xhci_input_ctx_t *ictx;
-	if ((err = create_valid_input_ctx(&ictx))) {
+	/* Drop the endpoint. */
+	if ((err = hc_drop_endpoint(dev->hc, dev->slot_id, xhci_endpoint_index(ep)))) {
 		goto err;
 	}
-
-	const unsigned ep_idx = xhci_endpoint_index(ep);
-	XHCI_INPUT_CTRL_CTX_DROP_SET(ictx->ctrl_ctx, ep_idx + 1); /* Preceded by slot ctx */
-
-	xhci_cmd_t cmd;
-	xhci_cmd_init(&cmd);
-
-	cmd.slot_id = dev->slot_id;
-
-	if ((err = xhci_send_configure_endpoint_command(dev->hc, &cmd, ictx))) {
-		goto err_ictx;
-	}
-
-	if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT))) {
-		goto err_ictx;
-	}
-
-	xhci_cmd_fini(&cmd);
 
 	/* Tear down TRB ring / PSA. */
@@ -387,9 +327,6 @@
 	*/
 
-	free32(ictx);
-	return EOK;
-
-err_ictx:
-	free32(ictx);
+	return EOK;
+
 err:
 	dev->endpoints[ep_num] = ep;
@@ -403,40 +340,4 @@
 }
 
-int xhci_device_configure(xhci_device_t *dev, xhci_hc_t *hc)
-{
-	int err;
-
-	/* Issue configure endpoint command (sec 4.3.5). */
-	xhci_input_ctx_t *ictx;
-	if ((err = create_valid_input_ctx(&ictx))) {
-		goto err;
-	}
-
-	// TODO: Set slot context and other flags. (probably forgot a lot of 'em)
-
-	xhci_cmd_t cmd;
-	xhci_cmd_init(&cmd);
-
-	cmd.slot_id = dev->slot_id;
-
-	if ((err = xhci_send_configure_endpoint_command(hc, &cmd, ictx))) {
-		goto err_cmd;
-	}
-
-	if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT))) {
-		goto err_cmd;
-	}
-
-	xhci_cmd_fini(&cmd);
-
-	free32(ictx);
-	return EOK;
-
-err_cmd:
-	free32(ictx);
-err:
-	return err;
-}
-
 /**
  * @}
Index: uspace/drv/bus/usb/xhci/endpoint.h
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.h	(revision 327f14759b992c17c88f75a6ddd0e2aa077e1957)
+++ uspace/drv/bus/usb/xhci/endpoint.h	(revision b7244940340ca14345eb61f436e6e36c4e2c0a21)
@@ -122,5 +122,4 @@
 int xhci_device_remove_endpoint(xhci_device_t *, xhci_endpoint_t *);
 xhci_endpoint_t * xhci_device_get_endpoint(xhci_device_t *, usb_endpoint_t);
-int xhci_device_configure(xhci_device_t *, xhci_hc_t *);
 
 static inline xhci_device_t * xhci_device_get(device_t *dev)
Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 327f14759b992c17c88f75a6ddd0e2aa077e1957)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision b7244940340ca14345eb61f436e6e36c4e2c0a21)
@@ -652,4 +652,197 @@
 }
 
+static int create_valid_input_ctx(xhci_input_ctx_t **out_ictx)
+{
+	xhci_input_ctx_t *ictx = malloc32(sizeof(xhci_input_ctx_t));
+	if (!ictx) {
+		return ENOMEM;
+	}
+
+	memset(ictx, 0, sizeof(xhci_input_ctx_t));
+
+	// Quoting sec. 4.6.6: A1, D0, D1 are down, A0 is up.
+	XHCI_INPUT_CTRL_CTX_ADD_CLEAR(ictx->ctrl_ctx, 1);
+	XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 0);
+	XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 1);
+	XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 0);
+
+	if (out_ictx) {
+		*out_ictx = ictx;
+	}
+
+	return EOK;
+}
+
+int hc_address_rh_device(xhci_hc_t *hc, uint32_t slot_id, uint8_t port, xhci_ep_ctx_t *ep_ctx)
+{
+	int err;
+
+	/* Issue configure endpoint command (sec 4.3.5). */
+	xhci_input_ctx_t *ictx;
+	if ((err = create_valid_input_ctx(&ictx))) {
+		goto err;
+	}
+
+	/* Initialize slot_ctx according to section 4.3.3 point 3. */
+	XHCI_SLOT_ROOT_HUB_PORT_SET(ictx->slot_ctx, port);
+	XHCI_SLOT_CTX_ENTRIES_SET(ictx->slot_ctx, 1);
+
+	/* Attaching to root hub port, root string equals to 0. */
+	XHCI_SLOT_ROUTE_STRING_SET(ictx->slot_ctx, 0);
+
+	/* Copy endpoint 0 context and set A1 flag. */
+	memcpy(&ictx->endpoint_ctx[0], ep_ctx, sizeof(xhci_ep_ctx_t));
+	XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 1);
+
+	if ((err = hc_address_device(hc, slot_id, ictx))) {
+		goto err_cmd;
+	}
+
+	free32(ictx);
+	return EOK;
+
+err_cmd:
+	free32(ictx);
+err:
+	return err;
+}
+
+int hc_configure_device(xhci_hc_t *hc, uint32_t slot_id)
+{
+	int err;
+
+	/* Issue configure endpoint command (sec 4.3.5). */
+	xhci_input_ctx_t *ictx;
+	if ((err = create_valid_input_ctx(&ictx))) {
+		goto err;
+	}
+
+	// TODO: Set slot context and other flags. (probably forgot a lot of 'em)
+
+	xhci_cmd_t cmd;
+	xhci_cmd_init(&cmd);
+
+	cmd.slot_id = slot_id;
+
+	if ((err = xhci_send_configure_endpoint_command(hc, &cmd, ictx))) {
+		goto err_cmd;
+	}
+
+	if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT))) {
+		goto err_cmd;
+	}
+
+	xhci_cmd_fini(&cmd);
+
+	free32(ictx);
+	return EOK;
+
+err_cmd:
+	free32(ictx);
+err:
+	return err;
+}
+
+int hc_deconfigure_device(xhci_hc_t *hc, uint32_t slot_id)
+{
+	int err;
+
+	/* Issue configure endpoint command (sec 4.3.5) with the DC flag. */
+	xhci_cmd_t cmd;
+	xhci_cmd_init(&cmd);
+
+	cmd.slot_id = slot_id;
+	cmd.deconfigure = true;
+
+	if ((err = xhci_send_configure_endpoint_command(hc, &cmd, NULL))) {
+		return err;
+	}
+
+	if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT))) {
+		return err;
+	}
+
+	xhci_cmd_fini(&cmd);
+
+	return EOK;
+}
+
+int hc_add_endpoint(xhci_hc_t *hc, uint32_t slot_id, uint8_t ep_idx, xhci_ep_ctx_t *ep_ctx)
+{
+	int err;
+
+	/* Issue configure endpoint command (sec 4.3.5). */
+	xhci_input_ctx_t *ictx;
+	if ((err = create_valid_input_ctx(&ictx))) {
+		goto err;
+	}
+
+	XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, ep_idx + 1); /* Preceded by slot ctx */
+	memcpy(&ictx->endpoint_ctx[ep_idx], ep_ctx, sizeof(xhci_ep_ctx_t));
+
+	// TODO: Set slot context and other flags. (probably forgot a lot of 'em)
+
+	xhci_cmd_t cmd;
+	xhci_cmd_init(&cmd);
+
+	cmd.slot_id = slot_id;
+
+	if ((err = xhci_send_configure_endpoint_command(hc, &cmd, ictx))) {
+		goto err_cmd;
+	}
+
+	if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT))) {
+		goto err_cmd;
+	}
+
+	xhci_cmd_fini(&cmd);
+
+	free32(ictx);
+	return EOK;
+
+err_cmd:
+	free32(ictx);
+err:
+	return err;
+}
+
+int hc_drop_endpoint(xhci_hc_t *hc, uint32_t slot_id, uint8_t ep_idx)
+{
+	int err;
+
+	/* Issue configure endpoint command (sec 4.3.5). */
+	xhci_input_ctx_t *ictx;
+	if ((err = create_valid_input_ctx(&ictx))) {
+		goto err;
+	}
+
+	XHCI_INPUT_CTRL_CTX_DROP_SET(ictx->ctrl_ctx, ep_idx + 1); /* Preceded by slot ctx */
+
+	// TODO: Set slot context and other flags. (probably forgot a lot of 'em)
+
+	xhci_cmd_t cmd;
+	xhci_cmd_init(&cmd);
+
+	cmd.slot_id = slot_id;
+
+	if ((err = xhci_send_configure_endpoint_command(hc, &cmd, ictx))) {
+		goto err_cmd;
+	}
+
+	if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT))) {
+		goto err_cmd;
+	}
+
+	xhci_cmd_fini(&cmd);
+
+	free32(ictx);
+	return EOK;
+
+err_cmd:
+	free32(ictx);
+err:
+	return err;
+}
+
 /**
  * @}
Index: uspace/drv/bus/usb/xhci/hc.h
===================================================================
--- uspace/drv/bus/usb/xhci/hc.h	(revision 327f14759b992c17c88f75a6ddd0e2aa077e1957)
+++ uspace/drv/bus/usb/xhci/hc.h	(revision b7244940340ca14345eb61f436e6e36c4e2c0a21)
@@ -96,4 +96,9 @@
 int hc_disable_slot(xhci_hc_t *, uint32_t);
 int hc_address_device(xhci_hc_t *, uint32_t, xhci_input_ctx_t *);
+int hc_address_rh_device(xhci_hc_t *, uint32_t, uint8_t, xhci_ep_ctx_t *);
+int hc_configure_device(xhci_hc_t *, uint32_t);
+int hc_deconfigure_device(xhci_hc_t *, uint32_t);
+int hc_add_endpoint(xhci_hc_t *, uint32_t, uint8_t, xhci_ep_ctx_t *);
+int hc_drop_endpoint(xhci_hc_t *, uint32_t, uint8_t);
 
 #endif
Index: uspace/drv/bus/usb/xhci/rh.c
===================================================================
--- uspace/drv/bus/usb/xhci/rh.c	(revision 327f14759b992c17c88f75a6ddd0e2aa077e1957)
+++ uspace/drv/bus/usb/xhci/rh.c	(revision b7244940340ca14345eb61f436e6e36c4e2c0a21)
@@ -95,63 +95,46 @@
 {
 	int err;
+
+	const xhci_port_speed_t *speed = xhci_rh_get_port_speed(rh, dev->port);
 	xhci_device_t *xhci_dev = xhci_device_get(dev);
-
-	/* FIXME: Certainly not generic solution. */
-	const uint32_t route_str = 0;
-
 	xhci_dev->hc = rh->hc;
-
-	const xhci_port_speed_t *speed = xhci_rh_get_port_speed(rh, dev->port);
 	xhci_dev->usb3 = speed->major == 3;
 
-	/* Enable new slot */
+	/* Enable new slot. */
 	if ((err = hc_enable_slot(rh->hc, &xhci_dev->slot_id)) != EOK)
 		return err;
 	usb_log_debug2("Obtained slot ID: %u.\n", xhci_dev->slot_id);
 
-	/* Setup input context */
-	xhci_input_ctx_t *ictx = malloc32(sizeof(xhci_input_ctx_t));
-	if (!ictx)
-		return ENOMEM;
-	memset(ictx, 0, sizeof(xhci_input_ctx_t));
-
-	XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 0);
-	XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 1);
-
-	/* Initialize slot_ctx according to section 4.3.3 point 3. */
-	/* Attaching to root hub port, root string equals to 0. */
-	XHCI_SLOT_ROOT_HUB_PORT_SET(ictx->slot_ctx, dev->port);
-	XHCI_SLOT_CTX_ENTRIES_SET(ictx->slot_ctx, 1);
-	XHCI_SLOT_ROUTE_STRING_SET(ictx->slot_ctx, route_str);
-
+	/* Create and configure control endpoint. */
 	endpoint_t *ep0_base = bus_create_endpoint(&rh->hc->bus.base);
 	if (!ep0_base)
-		goto err_ictx;
+		return ENOMEM;
+
 	xhci_endpoint_t *ep0 = xhci_endpoint_get(ep0_base);
-
-	/* Control endpoints don't use streams. */
 	/* FIXME: Sync this with xhci_device_add_endpoint. */
 	ep0->max_streams = 0;
 	ep0->max_burst = 0;
 	ep0->mult = 0;
+
 	if ((err = xhci_endpoint_alloc_transfer_ds(ep0)))
-		goto err_ictx;
-
-	setup_control_ep0_ctx(&ictx->endpoint_ctx[0], &ep0->ring, speed);
+		goto err_ep;
+
+	xhci_ep_ctx_t ep_ctx;
+	memset(&ep_ctx, 0, sizeof(xhci_ep_ctx_t));
+	setup_control_ep0_ctx(&ep_ctx, &ep0->ring, speed);
 
 	/* Setup and register device context */
-	xhci_device_ctx_t *dctx = malloc32(sizeof(xhci_device_ctx_t));
-	if (!dctx) {
+	xhci_dev->dev_ctx = malloc32(sizeof(xhci_device_ctx_t));
+	if (!xhci_dev->dev_ctx) {
 		err = ENOMEM;
-		goto err_ep;
-	}
-	xhci_dev->dev_ctx = dctx;
-	rh->hc->dcbaa[xhci_dev->slot_id] = addr_to_phys(dctx);
-	memset(dctx, 0, sizeof(xhci_device_ctx_t));
+		goto err_ds;
+	}
+	rh->hc->dcbaa[xhci_dev->slot_id] = addr_to_phys(xhci_dev->dev_ctx);
+	memset(xhci_dev->dev_ctx, 0, sizeof(xhci_device_ctx_t));
 
 	/* Address device */
-	if ((err = hc_address_device(rh->hc, xhci_dev->slot_id, ictx)) != EOK)
+	if ((err = hc_address_rh_device(rh->hc, xhci_dev->slot_id, dev->port, &ep_ctx)))
 		goto err_dctx;
-	dev->address = XHCI_SLOT_DEVICE_ADDRESS(dctx->slot_ctx);
+	dev->address = XHCI_SLOT_DEVICE_ADDRESS(xhci_dev->dev_ctx->slot_ctx);
 	usb_log_debug2("Obtained USB address: %d.\n", dev->address);
 
@@ -178,15 +161,14 @@
 	}
 
-	free32(ictx);
 	return EOK;
 
 err_dctx:
-	free32(dctx);
+	free32(xhci_dev->dev_ctx);
 	rh->hc->dcbaa[xhci_dev->slot_id] = 0;
+err_ds:
+	xhci_endpoint_free_transfer_ds(ep0);
 err_ep:
 	xhci_endpoint_fini(ep0);
 	free(ep0);
-err_ictx:
-	free32(ictx);
 	return err;
 }
@@ -316,4 +298,5 @@
 
 	/* TODO: Figure out how to handle errors here. So far, they are reported and skipped. */
+	/* TODO: Move parts of the code below to xhci_bus_remove_device() */
 
 	/* Make DDF (and all drivers) forget about the device. */
@@ -323,6 +306,18 @@
 	}
 
-	// TODO: Remove EP0.
-	// TODO: Deconfigure device.
+	/* Unregister EP0. */
+	if ((err = bus_unregister_endpoint(&rh->hc->bus.base, &dev->endpoints[0]->base))) {
+		usb_log_warning("Failed to unregister configuration endpoint of device '%s' from XHCI bus: %s",
+		    ddf_fun_get_name(dev->base.fun), str_error(err));
+	}
+
+	/* Deconfigure device. */
+	if ((err = hc_deconfigure_device(rh->hc, dev->slot_id))) {
+		usb_log_warning("Failed to deconfigure detached device '%s': %s",
+		    ddf_fun_get_name(dev->base.fun), str_error(err));
+	}
+
+	/* TODO: Free EP0 structures. */
+	/* TODO: Destroy EP0 by removing its last reference. */
 
 	/* Remove device from XHCI bus. */
Index: uspace/drv/bus/usb/xhci/transfers.c
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.c	(revision 327f14759b992c17c88f75a6ddd0e2aa077e1957)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision b7244940340ca14345eb61f436e6e36c4e2c0a21)
@@ -191,5 +191,5 @@
 	// Issue a Configure Endpoint command, if needed.
 	if (configure_endpoint_needed(setup)) {
-		const int err = xhci_device_configure(xhci_ep_to_dev(xhci_ep), hc);
+		const int err = hc_configure_device(hc, xhci_ep_to_dev(xhci_ep)->slot_id);
 		if (err)
 			return err;
