Index: uspace/drv/bus/usb/xhci/bus.c
===================================================================
--- uspace/drv/bus/usb/xhci/bus.c	(revision 56db65dc41b80ff74d067c9103026ffe34cc8e46)
+++ uspace/drv/bus/usb/xhci/bus.c	(revision 0206d350511d5ad74999ecd3b4f1abcea0463ba8)
@@ -49,13 +49,82 @@
 #include "transfers.h"
 
-/** TODO: Still some copy-pasta left...
- */
+
+/* FIXME Are these really static? Older HCs fetch it from descriptor. */
+/* FIXME Add USB3 options, if applicable. */
+static const usb_endpoint_desc_t ep0_desc = {
+	.endpoint_no = 0,
+	.direction = USB_DIRECTION_BOTH,
+	.transfer_type = USB_TRANSFER_CONTROL,
+	.max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
+	.packets = 1,
+};
+
+static int prepare_endpoint(xhci_endpoint_t *ep, const usb_endpoint_desc_t *desc)
+{
+	/* Extract information from endpoint_desc */
+	ep->base.target = (usb_target_t) {{
+		.address = ep->base.device->address,
+		.endpoint = desc->endpoint_no,
+	}};
+	ep->base.direction = desc->direction;
+	ep->base.transfer_type = desc->transfer_type;
+	ep->base.max_packet_size = desc->max_packet_size;
+	ep->base.packets = desc->packets;
+	ep->max_streams = desc->usb3.max_streams;
+	ep->max_burst = desc->usb3.max_burst;
+	// TODO add this property to usb_endpoint_desc_t and fetch it from ss companion desc
+	ep->mult = 0;
+
+	return xhci_endpoint_alloc_transfer_ds(ep);
+}
+
+static endpoint_t *create_endpoint(bus_t *base);
+
+static int address_device(xhci_hc_t *hc, xhci_device_t *dev)
+{
+	int err;
+
+	/* Enable new slot. */
+	if ((err = hc_enable_slot(hc, &dev->slot_id)) != EOK)
+		return err;
+	usb_log_debug2("Obtained slot ID: %u.\n", dev->slot_id);
+
+	/* Create and configure control endpoint. */
+	endpoint_t *ep0_base = create_endpoint(&hc->bus.base);
+	if (!ep0_base)
+		goto err_slot;
+
+	/* Temporary reference */
+	endpoint_add_ref(ep0_base);
+
+	ep0_base->device = &dev->base;
+	xhci_endpoint_t *ep0 = xhci_endpoint_get(ep0_base);
+
+	if ((err = prepare_endpoint(ep0, &ep0_desc)))
+		goto err_ep;
+
+	/* Address device */
+	if ((err = hc_address_device(hc, dev, ep0)))
+		goto err_prepared_ep;
+
+	/* Register EP0, passing Temporary reference */
+	ep0->base.target.address = dev->base.address;
+	dev->endpoints[0] = ep0;
+
+	return EOK;
+
+err_prepared_ep:
+	xhci_endpoint_free_transfer_ds(ep0);
+err_ep:
+	endpoint_del_ref(ep0_base);
+err_slot:
+	hc_disable_slot(hc, dev->slot_id);
+	return err;
+}
+
 int xhci_bus_enumerate_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
 {
 	int err;
 	xhci_device_t *xhci_dev = xhci_device_get(dev);
-
-	/* TODO: get speed from the default address reservation */
-	dev->speed = USB_SPEED_FULL;
 
 	/* Manage TT */
@@ -72,8 +141,11 @@
 
 	/* Assign an address to the device */
-	if ((err = xhci_rh_address_device(&hc->rh, dev, bus))) {
+	if ((err = address_device(hc, xhci_dev))) {
 		usb_log_error("Failed to setup address of the new device: %s", str_error(err));
 		return err;
 	}
+
+	// TODO: Fetch descriptor of EP0 and reconfigure it accordingly
+	assert(xhci_dev->endpoints[0]);
 
 	assert(bus->devices_by_slot[xhci_dev->slot_id] == NULL);
@@ -93,4 +165,6 @@
 }
 
+static int unregister_endpoint(bus_t *, endpoint_t *);
+
 int xhci_bus_remove_device(xhci_bus_t *bus, xhci_hc_t *hc, device_t *dev)
 {
@@ -98,10 +172,11 @@
 
 	/* Unregister remaining endpoints. */
-	for (size_t i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
+	for (unsigned i = 0; i < ARRAY_SIZE(xhci_dev->endpoints); ++i) {
 		if (!xhci_dev->endpoints[i])
 			continue;
 
-		// FIXME: ignoring return code
-		bus_unregister_endpoint(&bus->base, &xhci_dev->endpoints[i]->base);
+		const int err = unregister_endpoint(&bus->base, &xhci_dev->endpoints[i]->base);
+		if (err)
+			usb_log_warning("Failed to unregister EP (%u:%u): %s", dev->address, i, str_error(err));
 	}
 
@@ -167,26 +242,15 @@
 static int register_endpoint(bus_t *bus_base, endpoint_t *ep, const usb_endpoint_desc_t *desc)
 {
+	int err;
 	xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
 	assert(bus);
 
 	assert(ep->device);
-
-	/* Extract USB2-related information from endpoint_desc */
-	ep->target = (usb_target_t) {{
-		.address = ep->device->address,
-		.endpoint = desc->endpoint_no,
-	}};
-	ep->direction = desc->direction;
-	ep->transfer_type = desc->transfer_type;
-	ep->max_packet_size = desc->max_packet_size;
-	ep->packets = desc->packets;
 
 	xhci_device_t *xhci_dev = xhci_device_get(ep->device);
 	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
 
-	xhci_ep->max_streams = desc->usb3.max_streams;
-	xhci_ep->max_burst = desc->usb3.max_burst;
-	// TODO add this property to usb_endpoint_desc_t and fetch it from ss companion desc
-	xhci_ep->mult = 0;
+	if ((err = prepare_endpoint(xhci_ep, desc)))
+		return err;
 
 	usb_log_info("Endpoint(%d:%d) registered to XHCI bus.", ep->target.address, ep->target.endpoint);
Index: uspace/drv/bus/usb/xhci/endpoint.c
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.c	(revision 56db65dc41b80ff74d067c9103026ffe34cc8e46)
+++ uspace/drv/bus/usb/xhci/endpoint.c	(revision 0206d350511d5ad74999ecd3b4f1abcea0463ba8)
@@ -197,9 +197,8 @@
 static void setup_control_ep_ctx(xhci_endpoint_t *ep, xhci_ep_ctx_t *ctx)
 {
-	// EP0 is configured elsewhere.
-	assert(ep->base.target.endpoint > 0);
-
 	XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
 	XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
+	XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
+	XHCI_EP_MULT_SET(*ctx, ep->mult);
 	XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
 	XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue);
@@ -211,6 +210,5 @@
 	XHCI_EP_TYPE_SET(*ctx, xhci_endpoint_type(ep));
 	XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, ep->base.max_packet_size);
-	XHCI_EP_MAX_BURST_SIZE_SET(*ctx,
-	    xhci_device_get(ep->base.device)->usb3 ? ep->max_burst : 0);
+	XHCI_EP_MAX_BURST_SIZE_SET(*ctx, ep->max_burst);
 	XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
 
@@ -260,4 +258,16 @@
 };
 
+void xhci_setup_endpoint_context(xhci_endpoint_t *ep, xhci_ep_ctx_t *ep_ctx)
+{
+	assert(ep);
+	assert(ep_ctx);
+
+	usb_transfer_type_t tt = ep->base.transfer_type;
+	assert(tt < ARRAY_SIZE(setup_ep_ctx_helpers));
+
+	memset(ep_ctx, 0, sizeof(*ep_ctx));
+	setup_ep_ctx_helpers[tt](ep, ep_ctx);
+}
+
 int xhci_device_add_endpoint(xhci_device_t *dev, xhci_endpoint_t *ep)
 {
@@ -270,5 +280,4 @@
 	}
 
-	int err = ENOMEM;
 	const usb_endpoint_t ep_num = ep->base.target.endpoint;
 
@@ -288,26 +297,9 @@
 	}
 
-	/* Set up TRB ring / PSA. */
-	if ((err = xhci_endpoint_alloc_transfer_ds(ep))) {
-		goto err;
-	}
-
 	/* 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;
-	}
-
-	return EOK;
-
-err_ds:
-	xhci_endpoint_free_transfer_ds(ep);
-err:
-	dev->endpoints[ep_num] = NULL;
-	dev->active_endpoint_count--;
-	return err;
+	xhci_setup_endpoint_context(ep, &ep_ctx);
+
+	return hc_add_endpoint(dev->hc, dev->slot_id, xhci_endpoint_index(ep), &ep_ctx);
 }
 
Index: uspace/drv/bus/usb/xhci/endpoint.h
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.h	(revision 56db65dc41b80ff74d067c9103026ffe34cc8e46)
+++ uspace/drv/bus/usb/xhci/endpoint.h	(revision 0206d350511d5ad74999ecd3b4f1abcea0463ba8)
@@ -119,4 +119,6 @@
 uint8_t xhci_endpoint_index(xhci_endpoint_t *);
 
+void xhci_setup_endpoint_context(xhci_endpoint_t *, xhci_ep_ctx_t *);
+
 int xhci_device_add_endpoint(xhci_device_t *, xhci_endpoint_t *);
 int xhci_device_remove_endpoint(xhci_device_t *, xhci_endpoint_t *);
Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 56db65dc41b80ff74d067c9103026ffe34cc8e46)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision 0206d350511d5ad74999ecd3b4f1abcea0463ba8)
@@ -43,4 +43,6 @@
 #include "rh.h"
 #include "hw_struct/trb.h"
+#include "hw_struct/context.h"
+#include "endpoint.h"
 #include "commands.h"
 #include "transfers.h"
@@ -449,4 +451,5 @@
 {
 	assert(batch);
+	assert(batch->ep);
 
 	usb_log_debug2("Endpoint(%d:%d) started %s transfer of size %lu.",
@@ -632,24 +635,4 @@
 }
 
-int hc_address_device(xhci_hc_t *hc, uint32_t slot_id, xhci_input_ctx_t *ictx)
-{
-	assert(hc);
-
-	int err;
-	xhci_cmd_t cmd;
-	xhci_cmd_init(&cmd);
-
-	cmd.slot_id = slot_id;
-
-	if ((err = xhci_send_address_device_command(hc, &cmd, ictx)) != EOK)
-		return err;
-
-	if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT)) != EOK)
-		return err;
-
-	xhci_cmd_fini(&cmd);
-	return EOK;
-}
-
 static int create_valid_input_ctx(xhci_input_ctx_t **out_ictx)
 {
@@ -674,5 +657,69 @@
 }
 
-int hc_address_rh_device(xhci_hc_t *hc, uint32_t slot_id, uint8_t port, xhci_ep_ctx_t *ep_ctx)
+// TODO: This currently assumes the device is attached to rh directly
+//	-> calculate route string
+int hc_address_device(xhci_hc_t *hc, xhci_device_t *dev, xhci_endpoint_t *ep0)
+{
+	int err = ENOMEM;
+
+	/* Setup and register device context */
+	dev->dev_ctx = malloc32(sizeof(xhci_device_ctx_t));
+	if (!dev->dev_ctx)
+		goto err;
+	memset(dev->dev_ctx, 0, sizeof(xhci_device_ctx_t));
+
+	hc->dcbaa[dev->slot_id] = addr_to_phys(dev->dev_ctx);
+
+	/* Issue configure endpoint command (sec 4.3.5). */
+	xhci_input_ctx_t *ictx;
+	if ((err = create_valid_input_ctx(&ictx))) {
+		goto err_dev_ctx;
+	}
+
+	/* Initialize slot_ctx according to section 4.3.3 point 3. */
+	XHCI_SLOT_ROOT_HUB_PORT_SET(ictx->slot_ctx, dev->base.port); // FIXME: This should be port at RH
+	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); // FIXME: This is apparently valid in limited cases
+
+	/* Copy endpoint 0 context and set A1 flag. */
+	XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 1);
+	xhci_setup_endpoint_context(ep0, &ictx->endpoint_ctx[0]);
+
+	xhci_cmd_t cmd;
+	xhci_cmd_init(&cmd);
+
+	cmd.slot_id = dev->slot_id;
+
+	if ((err = xhci_send_address_device_command(hc, &cmd, ictx)) != EOK)
+		goto err_cmd;
+
+	if ((err = xhci_cmd_wait(&cmd, XHCI_DEFAULT_TIMEOUT)) != EOK)
+		goto err_cmd;
+
+	dev->base.address = XHCI_SLOT_DEVICE_ADDRESS(dev->dev_ctx->slot_ctx);
+	usb_log_debug2("Obtained USB address: %d.\n", dev->base.address);
+
+	/* From now on, the device is officially online, yay! */
+	fibril_mutex_lock(&dev->base.guard);
+	dev->online = true;
+	fibril_mutex_unlock(&dev->base.guard);
+
+	xhci_cmd_fini(&cmd);
+	free32(ictx);
+	return EOK;
+
+err_cmd:
+	xhci_cmd_fini(&cmd);
+	free32(ictx);
+err_dev_ctx:
+	free32(dev->dev_ctx);
+	hc->dcbaa[dev->slot_id] = 0;
+err:
+	return err;
+}
+
+int hc_configure_device(xhci_hc_t *hc, uint32_t slot_id)
 {
 	int err;
@@ -684,18 +731,20 @@
 	}
 
-	/* 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))) {
+	// 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);
@@ -708,5 +757,29 @@
 }
 
-int hc_configure_device(xhci_hc_t *hc, uint32_t slot_id)
+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;
@@ -718,4 +791,7 @@
 	}
 
+	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)
 
@@ -744,29 +820,5 @@
 }
 
-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 hc_drop_endpoint(xhci_hc_t *hc, uint32_t slot_id, uint8_t ep_idx)
 {
 	int err;
@@ -778,6 +830,5 @@
 	}
 
-	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));
+	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)
@@ -807,42 +858,4 @@
 }
 
-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 56db65dc41b80ff74d067c9103026ffe34cc8e46)
+++ uspace/drv/bus/usb/xhci/hc.h	(revision 0206d350511d5ad74999ecd3b4f1abcea0463ba8)
@@ -83,4 +83,7 @@
 } xhci_hc_t;
 
+typedef struct xhci_endpoint xhci_endpoint_t;
+typedef struct xhci_device xhci_device_t;
+
 int hc_init_mmio(xhci_hc_t *, const hw_res_list_parsed_t *);
 int hc_init_memory(xhci_hc_t *, ddf_dev_t *);
@@ -95,6 +98,5 @@
 int hc_enable_slot(xhci_hc_t *, uint32_t *);
 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_address_device(xhci_hc_t *, xhci_device_t *, xhci_endpoint_t *);
 int hc_configure_device(xhci_hc_t *, uint32_t);
 int hc_deconfigure_device(xhci_hc_t *, uint32_t);
Index: uspace/drv/bus/usb/xhci/rh.c
===================================================================
--- uspace/drv/bus/usb/xhci/rh.c	(revision 56db65dc41b80ff74d067c9103026ffe34cc8e46)
+++ uspace/drv/bus/usb/xhci/rh.c	(revision 0206d350511d5ad74999ecd3b4f1abcea0463ba8)
@@ -74,100 +74,15 @@
 }
 
-static void setup_control_ep0_ctx(xhci_ep_ctx_t *ctx, xhci_trb_ring_t *ring,
-		const xhci_port_speed_t *speed)
-{
-	XHCI_EP_TYPE_SET(*ctx, EP_TYPE_CONTROL);
-	// TODO: must be changed with a command after USB descriptor is read
-	// See 4.6.5 in XHCI specification, first note
-	XHCI_EP_MAX_PACKET_SIZE_SET(*ctx, speed->major == 3 ? 512 : 8);
-	XHCI_EP_MAX_BURST_SIZE_SET(*ctx, 0);
-	XHCI_EP_TR_DPTR_SET(*ctx, ring->dequeue);
-	XHCI_EP_DCS_SET(*ctx, 1);
-	XHCI_EP_INTERVAL_SET(*ctx, 0);
-	XHCI_EP_MAX_P_STREAMS_SET(*ctx, 0);
-	XHCI_EP_MULT_SET(*ctx, 0);
-	XHCI_EP_ERROR_COUNT_SET(*ctx, 3);
-}
-
-/* FIXME Are these really static? Older HCs fetch it from descriptor. */
-/* FIXME Add USB3 options, if applicable. */
-static const usb_endpoint_desc_t ep0_desc = {
-	.endpoint_no = 0,
-	.direction = USB_DIRECTION_BOTH,
-	.transfer_type = USB_TRANSFER_CONTROL,
-	.max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
-	.packets = 1,
-};
-
-// TODO: This currently assumes the device is attached to rh directly.
-//       Also, we should consider moving a lot of functionailty to xhci bus
-int xhci_rh_address_device(xhci_rh_t *rh, device_t *dev, xhci_bus_t *bus)
-{
-	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);
-	xhci_dev->hc = rh->hc;
-	xhci_dev->usb3 = speed->major == 3;
-
-	/* 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);
-
-	/* Create and configure control endpoint. */
-	endpoint_t *ep0_base = bus_create_endpoint(&rh->hc->bus.base);
-	if (!ep0_base)
-		return ENOMEM;
-
-	xhci_endpoint_t *ep0 = xhci_endpoint_get(ep0_base);
-
-	if ((err = xhci_endpoint_alloc_transfer_ds(ep0)))
-		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_dev->dev_ctx = malloc32(sizeof(xhci_device_ctx_t));
-	if (!xhci_dev->dev_ctx) {
-		err = ENOMEM;
-		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_rh_device(rh->hc, xhci_dev->slot_id, dev->port, &ep_ctx)))
-		goto err_dctx;
-	dev->address = XHCI_SLOT_DEVICE_ADDRESS(xhci_dev->dev_ctx->slot_ctx);
-	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);
-
-	ep0_base->device = dev;
-
-	bus_register_endpoint(&rh->hc->bus.base, ep0_base, &ep0_desc);
-
-	if (!rh->devices[dev->port - 1]) {
-		/* Only save the device if it's the first one connected to this port. */
-		rh->devices[dev->port - 1] = xhci_dev;
-	}
-
-	return EOK;
-
-err_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);
-	return err;
+static usb_speed_t port_speed_to_usb_speed(const xhci_port_speed_t *port_speed)
+{
+	assert(port_speed->major > 0 && port_speed->major <= USB_SPEED_SUPER);
+
+	switch (port_speed->major) {
+		case 3: return USB_SPEED_SUPER;
+		case 2: return USB_SPEED_HIGH;
+		case 1: return port_speed->minor ? USB_SPEED_FULL : USB_SPEED_LOW;
+	}
+
+	assert(false);
 }
 
@@ -188,6 +103,12 @@
 	}
 
+	const xhci_port_speed_t *port_speed = xhci_rh_get_port_speed(rh, port_id);
+	xhci_device_t *xhci_dev = xhci_device_get(dev);
+	xhci_dev->hc = rh->hc;
+	xhci_dev->usb3 = port_speed->major == 3;
+
 	dev->hub = &rh->device;
 	dev->port = port_id;
+	dev->speed = port_speed_to_usb_speed(port_speed);
 
 	if ((err = xhci_bus_enumerate_device(bus, rh->hc, dev))) {
@@ -207,4 +128,8 @@
 	fibril_mutex_lock(&rh->device.guard);
 	list_append(&dev->link, &rh->device.devices);
+	if (!rh->devices[port_id - 1]) {
+		/* Only save the device if it's the first one connected to this port. */
+		rh->devices[port_id - 1] = xhci_dev;
+	}
 	fibril_mutex_unlock(&rh->device.guard);
 
@@ -286,4 +211,5 @@
 			continue;
 
+		/* FIXME: This is racy. */
 		if ((err = xhci_transfer_abort(&ep->active_transfer))) {
 			usb_log_warning("Failed to abort active %s transfer to "
@@ -305,5 +231,5 @@
 
 	/* Unregister EP0. */
-	if ((err = bus_unregister_endpoint(&rh->hc->bus.base, &dev->endpoints[0]->base))) {
+	if ((err = bus_remove_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));
Index: uspace/lib/usbhost/include/usb/host/bus.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/bus.h	(revision 56db65dc41b80ff74d067c9103026ffe34cc8e46)
+++ uspace/lib/usbhost/include/usb/host/bus.h	(revision 0206d350511d5ad74999ecd3b4f1abcea0463ba8)
@@ -117,7 +117,4 @@
 int device_init(device_t *);
 
-extern int bus_add_ep(bus_t *, device_t *, const usb_endpoint_desc_t *);
-extern int bus_remove_ep(bus_t *, device_t *, usb_target_t, usb_direction_t);
-
 int device_set_default_name(device_t *);
 
@@ -125,8 +122,7 @@
 int bus_remove_device(bus_t *, hcd_t *, device_t *);
 
-endpoint_t *bus_create_endpoint(bus_t *);
-int bus_register_endpoint(bus_t *, endpoint_t *, const usb_endpoint_desc_t *);
-int bus_unregister_endpoint(bus_t *, endpoint_t *);
+int bus_add_endpoint(bus_t *, device_t *, const usb_endpoint_desc_t *, endpoint_t **);
 endpoint_t *bus_find_endpoint(bus_t *, device_t *, usb_target_t, usb_direction_t);
+int bus_remove_endpoint(bus_t *, endpoint_t *);
 
 size_t bus_count_bw(endpoint_t *, size_t);
Index: uspace/lib/usbhost/src/bus.c
===================================================================
--- uspace/lib/usbhost/src/bus.c	(revision 56db65dc41b80ff74d067c9103026ffe34cc8e46)
+++ uspace/lib/usbhost/src/bus.c	(revision 0206d350511d5ad74999ecd3b4f1abcea0463ba8)
@@ -66,33 +66,4 @@
 }
 
-int bus_add_ep(bus_t *bus, device_t *device, const usb_endpoint_desc_t *desc)
-{
-	assert(bus);
-	assert(device);
-
-	/* Temporary reference */
-	endpoint_t *ep = bus_create_endpoint(bus);
-	if (!ep)
-		return ENOMEM;
-
-	ep->device = device;
-	const int err = bus_register_endpoint(bus, ep, desc);
-
-	/* drop Temporary reference */
-	endpoint_del_ref(ep);
-
-	return err;
-}
-
-int bus_remove_ep(bus_t *bus, device_t *dev, usb_target_t target, usb_direction_t dir)
-{
-	assert(bus);
-	endpoint_t *ep = bus_find_endpoint(bus, dev, target, dir);
-	if (!ep)
-		return ENOENT;
-
-	return bus_unregister_endpoint(bus, ep);
-}
-
 int device_set_default_name(device_t *dev)
 {
@@ -136,50 +107,37 @@
 }
 
-endpoint_t *bus_create_endpoint(bus_t *bus)
-{
-	assert(bus);
-
-	fibril_mutex_lock(&bus->guard);
+int bus_add_endpoint(bus_t *bus, device_t *device, const usb_endpoint_desc_t *desc, endpoint_t **out_ep)
+{
+	int err = ENOMEM;
+
+	assert(bus);
+	assert(device);
+
+	fibril_mutex_lock(&bus->guard);
+
 	endpoint_t *ep = bus->ops.create_endpoint(bus);
-	if (ep) {
-		/* Exporting reference */
-		endpoint_add_ref(ep);
-	}
-	fibril_mutex_unlock(&bus->guard);
-
-	return ep;
-}
-
-int bus_register_endpoint(bus_t *bus, endpoint_t *ep, const usb_endpoint_desc_t *desc)
-{
-	assert(bus);
-	assert(ep);
+	if (!ep)
+		goto err;
 
 	/* Bus reference */
 	endpoint_add_ref(ep);
 
-	fibril_mutex_lock(&bus->guard);
-	const int r = bus->ops.register_endpoint(bus, ep, desc);
-	fibril_mutex_unlock(&bus->guard);
-
-	return r;
-}
-
-int bus_unregister_endpoint(bus_t *bus, endpoint_t *ep)
-{
-	assert(bus);
-	assert(ep);
-
-	fibril_mutex_lock(&bus->guard);
-	const int r = bus->ops.unregister_endpoint(bus, ep);
-	fibril_mutex_unlock(&bus->guard);
-
-	if (r)
-		return r;
-
-	/* Bus reference */
+	ep->device = device;
+	if ((err = bus->ops.register_endpoint(bus, ep, desc)))
+		goto err_ep;
+
+	if (out_ep) {
+		endpoint_add_ref(ep);
+		*out_ep = ep;
+	}
+
+	fibril_mutex_unlock(&bus->guard);
+	return EOK;
+
+err_ep:
 	endpoint_del_ref(ep);
-
-	return EOK;
+err:
+	fibril_mutex_unlock(&bus->guard);
+	return err;
 }
 
@@ -201,4 +159,22 @@
 }
 
+int bus_remove_endpoint(bus_t *bus, endpoint_t *ep)
+{
+	assert(bus);
+	assert(ep);
+
+	fibril_mutex_lock(&bus->guard);
+	const int r = bus->ops.unregister_endpoint(bus, ep);
+	fibril_mutex_unlock(&bus->guard);
+
+	if (r)
+		return r;
+
+	/* Bus reference */
+	endpoint_del_ref(ep);
+
+	return EOK;
+}
+
 int bus_request_address(bus_t *bus, usb_address_t *hint, bool strict, usb_speed_t speed)
 {
Index: uspace/lib/usbhost/src/ddf_helpers.c
===================================================================
--- uspace/lib/usbhost/src/ddf_helpers.c	(revision 56db65dc41b80ff74d067c9103026ffe34cc8e46)
+++ uspace/lib/usbhost/src/ddf_helpers.c	(revision 0206d350511d5ad74999ecd3b4f1abcea0463ba8)
@@ -104,5 +104,5 @@
 		endpoint_desc->max_packet_size, endpoint_desc->usb2.polling_interval);
 
-	return bus_add_ep(hcd->bus, dev, endpoint_desc);
+	return bus_add_endpoint(hcd->bus, dev, endpoint_desc, NULL);
 }
 
@@ -130,5 +130,10 @@
 		dev->address, endpoint_desc->endpoint_no,
 		usb_str_direction(endpoint_desc->direction));
-	return bus_remove_ep(hcd->bus, dev, target, endpoint_desc->direction);
+
+	endpoint_t *ep = bus_find_endpoint(hcd->bus, dev, target, endpoint_desc->direction);
+	if (!ep)
+		return ENOENT;
+
+	return bus_remove_endpoint(hcd->bus, ep);
 }
 
Index: uspace/lib/usbhost/src/usb2_bus.c
===================================================================
--- uspace/lib/usbhost/src/usb2_bus.c	(revision 56db65dc41b80ff74d067c9103026ffe34cc8e46)
+++ uspace/lib/usbhost/src/usb2_bus.c	(revision 0206d350511d5ad74999ecd3b4f1abcea0463ba8)
@@ -120,5 +120,7 @@
 	/* Add default pipe on default address */
 	usb_log_debug("Device(%d): Adding default target (0:0)", address);
-	err = bus_add_ep(bus, dev, &usb2_default_control_ep);
+
+	endpoint_t *default_ep;
+	err = bus_add_endpoint(bus, dev, &usb2_default_control_ep, &default_ep);
 	if (err != EOK) {
 		usb_log_error("Device(%d): Failed to add default target: %s.",
@@ -169,5 +171,5 @@
 	/* Register EP on the new address */
 	usb_log_debug("Device(%d): Registering control EP.", address);
-	err = bus_add_ep(bus, dev, &control_ep);
+	err = bus_add_endpoint(bus, dev, &control_ep, NULL);
 	if (err != EOK) {
 		usb_log_error("Device(%d): Failed to register EP0: %s",
@@ -176,9 +178,16 @@
 	}
 
-	bus_remove_ep(bus, dev, usb2_default_target, USB_DIRECTION_BOTH);
+	err = bus_remove_endpoint(bus, default_ep);
+	assert(err == EOK);
+	endpoint_del_ref(default_ep);
+
+	err = bus_release_address(bus, address);
+	assert(err == EOK);
+
 	return EOK;
 
 err_default_control_ep:
-	bus_remove_ep(bus, dev, usb2_default_target, USB_DIRECTION_BOTH);
+	bus_remove_endpoint(bus, default_ep);
+	endpoint_del_ref(default_ep);
 err_address:
 	bus_release_address(bus, address);
