Index: uspace/drv/bus/usb/xhci/Makefile
===================================================================
--- uspace/drv/bus/usb/xhci/Makefile	(revision 129b821fb30858c35df3ba6ff80bf8ccc51442de)
+++ uspace/drv/bus/usb/xhci/Makefile	(revision 682c9354d8b5a94c62b34c3784db467a18e7e9ae)
@@ -37,4 +37,5 @@
 	commands.c \
 	debug.c \
+	device.c \
 	endpoint.c \
 	hc.c \
Index: uspace/drv/bus/usb/xhci/bus.c
===================================================================
--- uspace/drv/bus/usb/xhci/bus.c	(revision 129b821fb30858c35df3ba6ff80bf8ccc51442de)
+++ uspace/drv/bus/usb/xhci/bus.c	(revision 682c9354d8b5a94c62b34c3784db467a18e7e9ae)
@@ -30,415 +30,16 @@
  */
 /** @file
- * HC Endpoint management.
+ * xHCI bus interface.
  */
 
-#include <usb/host/ddf_helpers.h>
-#include <usb/host/endpoint.h>
-#include <usb/host/hcd.h>
-#include <usb/host/utility.h>
-#include <usb/classes/classes.h>
-#include <usb/classes/hub.h>
 #include <usb/descriptor.h>
-#include <usb/debug.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <str_error.h>
-#include <macros.h>
-#include <stdbool.h>
 
 #include "hc.h"
-#include "bus.h"
+#include "device.h"
 #include "endpoint.h"
 #include "transfers.h"
 
+#include "bus.h"
 
-/** Initial descriptor used for control endpoint 0 before more configuration is retrieved. */
-static const usb_endpoint_descriptors_t ep0_initial_desc = {
-	.endpoint.max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
-};
-
-static endpoint_t *endpoint_create(device_t *, const usb_endpoint_descriptors_t *);
-
-/**
- * Assign address and control endpoint to a new XHCI device. Once this function
- * successfully returns, the device is online.
- *
- * @param[in] bus XHCI bus, in which the address is assigned.
- * @param[in] dev New device to address and configure./e
- * @return Error code.
- */
-static int address_device(xhci_device_t *dev)
-{
-	int err;
-
-	/* Enable new slot. */
-	if ((err = hc_enable_slot(dev)) != EOK)
-		return err;
-	usb_log_debug2("Obtained slot ID: %u.", dev->slot_id);
-
-	/* Create and configure control endpoint. */
-	endpoint_t *ep0_base = endpoint_create(&dev->base, &ep0_initial_desc);
-	if (!ep0_base)
-		goto err_slot;
-
-	/* Bus reference */
-	endpoint_add_ref(ep0_base);
-	dev->base.endpoints[0] = ep0_base;
-
-	xhci_endpoint_t *ep0 = xhci_endpoint_get(ep0_base);
-
-	/* Address device */
-	if ((err = hc_address_device(dev, ep0)))
-		goto err_added;
-
-	return EOK;
-
-err_added:
-	/* Bus reference */
-	endpoint_del_ref(ep0_base);
-	dev->base.endpoints[0] = NULL;
-err_slot:
-	hc_disable_slot(dev);
-	return err;
-}
-
-/**
- * Retrieve and set maximum packet size for endpoint zero of a XHCI device.
- *
- * @param[in] hc Host controller, which manages the device.
- * @param[in] dev Device with operational endpoint zero.
- * @return Error code.
- */
-static int setup_ep0_packet_size(xhci_hc_t *hc, xhci_device_t *dev)
-{
-	int err;
-
-	uint16_t max_packet_size;
-	if ((err = hc_get_ep0_max_packet_size(&max_packet_size, (bus_t *) &hc->bus, &dev->base)))
-		return err;
-
-	xhci_endpoint_t *ep0 = xhci_endpoint_get(dev->base.endpoints[0]);
-	assert(ep0);
-
-	if (ep0->base.max_packet_size == max_packet_size)
-		return EOK;
-
-	ep0->base.max_packet_size = max_packet_size;
-	ep0->base.max_transfer_size = max_packet_size * ep0->base.packets_per_uframe;
-
-	xhci_ep_ctx_t ep_ctx;
-	xhci_setup_endpoint_context(ep0, &ep_ctx);
-
-	if ((err = hc_update_endpoint(dev, 0, &ep_ctx)))
-		return err;
-
-	return EOK;
-}
-
-/**
- * Check whether the device is a hub and if so, fill its characterstics.
- *
- * If this fails, it does not necessarily mean the device is unusable.
- * Just the TT will not work correctly.
- */
-static int setup_hub(xhci_device_t *dev, usb_standard_device_descriptor_t *desc)
-{
-	if (desc->device_class != USB_CLASS_HUB)
-		return EOK;
-
-	usb_hub_descriptor_header_t hub_desc = { 0 };
-	const int err = hc_get_hub_desc(&dev->base, &hub_desc);
-	if (err)
-		return err;
-
-	dev->is_hub = 1;
-	dev->num_ports = hub_desc.port_count;
-	dev->tt_think_time = 8 +
-		8  * !!(hub_desc.characteristics & HUB_CHAR_TT_THINK_8) +
-		16 * !!(hub_desc.characteristics & HUB_CHAR_TT_THINK_16);
-
-	usb_log_debug2("Device(%u): recognised USB hub with %u ports", dev->base.address, dev->num_ports);
-	return EOK;
-}
-
-/**
- * Respond to a new device on the XHCI bus. Address it, negotiate packet size
- * and retrieve USB descriptors.
- *
- * @param[in] bus XHCI bus, where the new device emerged.
- * @param[in] dev XHCI device, which has appeared on the bus.
- *
- * @return Error code.
- */
-static int device_enumerate(device_t *dev)
-{
-	int err;
-	xhci_bus_t *bus = bus_to_xhci_bus(dev->bus);
-	xhci_device_t *xhci_dev = xhci_device_get(dev);
-
-	/* Calculate route string */
-	xhci_device_t *xhci_hub = xhci_device_get(dev->hub);
-	xhci_dev->tier = xhci_hub->tier + 1;
-	xhci_dev->route_str = xhci_hub->route_str;
-
-	/* Roothub port is not part of the route string */
-	if (xhci_dev->tier >= 2) {
-		const unsigned offset = 4 * (xhci_dev->tier - 2);
-		xhci_dev->route_str |= (dev->port & 0xf) << offset;
-		xhci_dev->rh_port = xhci_hub->rh_port;
-	}
-
-	int retries = 3;
-	do {
-		/* Assign an address to the device */
-		err = address_device(xhci_dev);
-	} while (err == ESTALL && --retries > 0);
-
-	if (err) {
-		usb_log_error("Failed to setup address of the new device: %s", str_error(err));
-		return err;
-	}
-
-	/* Setup EP0 might already need to issue a transfer. */
-	fibril_mutex_lock(&bus->base.guard);
-	assert(bus->devices_by_slot[xhci_dev->slot_id] == NULL);
-	bus->devices_by_slot[xhci_dev->slot_id] = xhci_dev;
-	fibril_mutex_unlock(&bus->base.guard);
-
-	if ((err = setup_ep0_packet_size(bus->hc, xhci_dev))) {
-		usb_log_error("Failed to setup control endpoint of the new device: %s", str_error(err));
-		goto err_address;
-	}
-
-	usb_standard_device_descriptor_t desc = { 0 };
-
-	if ((err = hc_get_device_desc(dev, &desc))) {
-		usb_log_error("Device(%d): failed to get devices descriptor: %s", dev->address, str_error(err));
-		goto err_address;
-	}
-
-	if ((err = setup_hub(xhci_dev, &desc)))
-		usb_log_warning("Device(%d): failed to setup hub characteristics: %s. "
-		    " Continuing anyway.", dev->address, str_error(err));
-
-	if ((err = hcd_ddf_setup_match_ids(dev, &desc))) {
-		usb_log_error("Device(%d): failed to setup match IDs: %s", dev->address, str_error(err));
-		goto err_address;
-	}
-
-	return EOK;
-
-err_address:
-	// TODO: deaddress device
-	return err;
-}
-
-/**
- * Remove device from XHCI bus. Transition it to the offline state, abort all
- * ongoing transfers and unregister all of its endpoints.
- *
- * Bus callback.
- *
- * @param[in] bus XHCI bus, from which the device is removed.
- * @param[in] dev XHCI device, which is removed from the bus.
- * @return Error code.
- */
-static void device_gone(device_t *dev)
-{
-	int err;
-	xhci_bus_t *bus = bus_to_xhci_bus(dev->bus);
-	xhci_device_t *xhci_dev = xhci_device_get(dev);
-
-	/* Disable the slot, dropping all endpoints. */
-	const uint32_t slot_id = xhci_dev->slot_id;
-	if ((err = hc_disable_slot(xhci_dev))) {
-		usb_log_warning("Failed to disable slot of device " XHCI_DEV_FMT ": %s",
-		    XHCI_DEV_ARGS(*xhci_dev), str_error(err));
-	}
-
-	bus->devices_by_slot[slot_id] = NULL;
-}
-
-/**
- * Reverts things device_offline did, getting the device back up.
- *
- * Bus callback.
- */
-static int device_online(device_t *dev_base)
-{
-	int err;
-
-	xhci_bus_t *bus = bus_to_xhci_bus(dev_base->bus);
-	assert(bus);
-
-	xhci_device_t *dev = xhci_device_get(dev_base);
-	assert(dev);
-
-	/* Transition the device from the Addressed to the Configured state. */
-	if ((err = hc_configure_device(dev))) {
-		usb_log_warning("Failed to configure device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*dev));
-		return err;
-	}
-
-	return EOK;
-}
-
-/**
- * Make given device offline. Offline the DDF function, tear down all
- * endpoints, issue Deconfigure Device command to xHC.
- *
- * Bus callback.
- */
-static void device_offline(device_t *dev_base)
-{
-	int err;
-
-	xhci_bus_t *bus = bus_to_xhci_bus(dev_base->bus);
-	assert(bus);
-
-	xhci_device_t *dev = xhci_device_get(dev_base);
-	assert(dev);
-
-	/* Issue one HC command to simultaneously drop all endpoints except zero. */
-	if ((err = hc_deconfigure_device(dev))) {
-		usb_log_warning("Failed to deconfigure device " XHCI_DEV_FMT ".",
-		    XHCI_DEV_ARGS(*dev));
-	}
-}
-
-/**
- * Create a new xHCI endpoint structure.
- *
- * Bus callback.
- */
-static endpoint_t *endpoint_create(device_t *dev, const usb_endpoint_descriptors_t *desc)
-{
-	const usb_transfer_type_t type = USB_ED_GET_TRANSFER_TYPE(desc->endpoint);
-
-	xhci_endpoint_t *ep = calloc(1, sizeof(xhci_endpoint_t)
-		+ (type == USB_TRANSFER_ISOCHRONOUS) * sizeof(*ep->isoch));
-	if (!ep)
-		return NULL;
-
-	if (xhci_endpoint_init(ep, dev, desc)) {
-		free(ep);
-		return NULL;
-	}
-
-	return &ep->base;
-}
-
-/**
- * Destroy given xHCI endpoint structure.
- *
- * Bus callback.
- */
-static void endpoint_destroy(endpoint_t *ep)
-{
-	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
-
-	xhci_endpoint_fini(xhci_ep);
-	free(xhci_ep);
-}
-
-/**
- * Register an andpoint to the xHC.
- *
- * Bus callback.
- */
-static int endpoint_register(endpoint_t *ep_base)
-{
-	int err;
-	xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
-	xhci_device_t *dev = xhci_device_get(ep_base->device);
-
-	xhci_ep_ctx_t ep_ctx;
-	xhci_setup_endpoint_context(ep, &ep_ctx);
-
-	if ((err = hc_add_endpoint(dev, xhci_endpoint_index(ep), &ep_ctx)))
-		return err;
-
-	return EOK;
-}
-
-/**
- * Abort a transfer on an endpoint.
- */
-static int endpoint_abort(endpoint_t *ep)
-{
-	xhci_device_t *dev = xhci_device_get(ep->device);
-
-	usb_transfer_batch_t *batch = NULL;
-	fibril_mutex_lock(&ep->guard);
-	if (ep->active_batch) {
-		if (dev->slot_id) {
-			const int err = hc_stop_endpoint(dev, xhci_endpoint_dci(xhci_endpoint_get(ep)));
-			if (err) {
-				usb_log_warning("Failed to stop endpoint %u of device " XHCI_DEV_FMT ": %s",
-				    ep->endpoint, XHCI_DEV_ARGS(*dev), str_error(err));
-			}
-
-			endpoint_wait_timeout_locked(ep, 2000);
-		}
-
-		batch = ep->active_batch;
-		if (batch) {
-			endpoint_deactivate_locked(ep);
-		}
-	}
-	fibril_mutex_unlock(&ep->guard);
-
-	if (batch) {
-		batch->error = EINTR;
-		batch->transfered_size = 0;
-		usb_transfer_batch_finish(batch);
-	}
-	return EOK;
-}
-
-/**
- * Unregister an endpoint. If the device is still available, inform the xHC
- * about it.
- *
- * Bus callback.
- */
-static void endpoint_unregister(endpoint_t *ep_base)
-{
-	int err;
-	xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
-	xhci_device_t *dev = xhci_device_get(ep_base->device);
-
-	endpoint_abort(ep_base);
-
-	/* If device slot is still available, drop the endpoint. */
-	if (dev->slot_id) {
-
-		if ((err = hc_drop_endpoint(dev, xhci_endpoint_index(ep)))) {
-			usb_log_error("Failed to drop endpoint " XHCI_EP_FMT ": %s", XHCI_EP_ARGS(*ep), str_error(err));
-		}
-	} else {
-		usb_log_debug("Not going to drop endpoint " XHCI_EP_FMT " because"
-		    " the slot has already been disabled.", XHCI_EP_ARGS(*ep));
-	}
-}
-
-/**
- * Schedule a batch for xHC.
- *
- * Bus callback.
- */
-static int batch_schedule(usb_transfer_batch_t *batch)
-{
-	assert(batch);
-	xhci_hc_t *hc = bus_to_hc(endpoint_get_bus(batch->ep));
-
-	if (!batch->target.address) {
-		usb_log_error("Attempted to schedule transfer to address 0.");
-		return EINVAL;
-	}
-
-	return xhci_transfer_schedule(hc, batch);
-}
 
 static const bus_ops_t xhci_bus_ops = {
@@ -446,15 +47,15 @@
 	.status = hc_status,
 
-	.device_enumerate = device_enumerate,
-	.device_gone = device_gone,
-	.device_online = device_online,
-	.device_offline = device_offline,
+	.device_enumerate = xhci_device_enumerate,
+	.device_gone = xhci_device_gone,
+	.device_online = xhci_device_online,
+	.device_offline = xhci_device_offline,
 
-	.endpoint_create = endpoint_create,
-	.endpoint_destroy = endpoint_destroy,
-	.endpoint_register = endpoint_register,
-	.endpoint_unregister = endpoint_unregister,
+	.endpoint_create = xhci_endpoint_create,
+	.endpoint_destroy = xhci_endpoint_destroy,
+	.endpoint_register = xhci_endpoint_register,
+	.endpoint_unregister = xhci_endpoint_unregister,
 
-	.batch_schedule = batch_schedule,
+	.batch_schedule = xhci_transfer_schedule,
 	.batch_create = xhci_transfer_create,
 	.batch_destroy = xhci_transfer_destroy,
Index: uspace/drv/bus/usb/xhci/bus.h
===================================================================
--- uspace/drv/bus/usb/xhci/bus.h	(revision 129b821fb30858c35df3ba6ff80bf8ccc51442de)
+++ uspace/drv/bus/usb/xhci/bus.h	(revision 682c9354d8b5a94c62b34c3784db467a18e7e9ae)
@@ -38,5 +38,4 @@
 #define XHCI_BUS_H
 
-#include <usb/usb.h>
 #include <usb/host/bus.h>
 
@@ -56,7 +55,4 @@
 void xhci_bus_fini(xhci_bus_t *);
 
-int xhci_bus_enumerate_device(xhci_bus_t *, device_t *);
-int xhci_bus_remove_device(xhci_bus_t *, device_t *);
-
 static inline xhci_bus_t *bus_to_xhci_bus(bus_t *bus_base)
 {
Index: uspace/drv/bus/usb/xhci/device.c
===================================================================
--- uspace/drv/bus/usb/xhci/device.c	(revision 682c9354d8b5a94c62b34c3784db467a18e7e9ae)
+++ uspace/drv/bus/usb/xhci/device.c	(revision 682c9354d8b5a94c62b34c3784db467a18e7e9ae)
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**  @addtogroup libusbhost
+ * @{
+ */
+/** @file
+ * HC Endpoint management.
+ */
+
+#include <usb/host/utility.h>
+#include <usb/host/ddf_helpers.h>
+#include <usb/host/endpoint.h>
+#include <usb/host/hcd.h>
+#include <usb/host/utility.h>
+#include <usb/classes/classes.h>
+#include <usb/classes/hub.h>
+#include <usb/descriptor.h>
+#include <usb/debug.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <str_error.h>
+#include <macros.h>
+#include <stdbool.h>
+
+#include "hc.h"
+#include "bus.h"
+#include "endpoint.h"
+
+#include "device.h"
+
+/** Initial descriptor used for control endpoint 0 before more configuration is retrieved. */
+static const usb_endpoint_descriptors_t ep0_initial_desc = {
+	.endpoint.max_packet_size = CTRL_PIPE_MIN_PACKET_SIZE,
+};
+
+/**
+ * Assign address and control endpoint to a new XHCI device. Once this function
+ * successfully returns, the device is online.
+ *
+ * @param[in] bus XHCI bus, in which the address is assigned.
+ * @param[in] dev New device to address and configure./e
+ * @return Error code.
+ */
+static int address_device(xhci_device_t *dev)
+{
+	int err;
+
+	/* Enable new slot. */
+	if ((err = hc_enable_slot(dev)) != EOK)
+		return err;
+	usb_log_debug2("Obtained slot ID: %u.", dev->slot_id);
+
+	/* Create and configure control endpoint. */
+	endpoint_t *ep0_base = xhci_endpoint_create(&dev->base, &ep0_initial_desc);
+	if (!ep0_base)
+		goto err_slot;
+
+	/* Bus reference */
+	endpoint_add_ref(ep0_base);
+	dev->base.endpoints[0] = ep0_base;
+
+	xhci_endpoint_t *ep0 = xhci_endpoint_get(ep0_base);
+
+	/* Address device */
+	if ((err = hc_address_device(dev, ep0)))
+		goto err_added;
+
+	return EOK;
+
+err_added:
+	/* Bus reference */
+	endpoint_del_ref(ep0_base);
+	dev->base.endpoints[0] = NULL;
+err_slot:
+	hc_disable_slot(dev);
+	return err;
+}
+
+/**
+ * Retrieve and set maximum packet size for endpoint zero of a XHCI device.
+ *
+ * @param[in] hc Host controller, which manages the device.
+ * @param[in] dev Device with operational endpoint zero.
+ * @return Error code.
+ */
+static int setup_ep0_packet_size(xhci_hc_t *hc, xhci_device_t *dev)
+{
+	int err;
+
+	uint16_t max_packet_size;
+	if ((err = hc_get_ep0_max_packet_size(&max_packet_size, (bus_t *) &hc->bus, &dev->base)))
+		return err;
+
+	xhci_endpoint_t *ep0 = xhci_endpoint_get(dev->base.endpoints[0]);
+	assert(ep0);
+
+	if (ep0->base.max_packet_size == max_packet_size)
+		return EOK;
+
+	ep0->base.max_packet_size = max_packet_size;
+	ep0->base.max_transfer_size = max_packet_size * ep0->base.packets_per_uframe;
+
+	xhci_ep_ctx_t ep_ctx;
+	xhci_setup_endpoint_context(ep0, &ep_ctx);
+
+	if ((err = hc_update_endpoint(dev, 0, &ep_ctx)))
+		return err;
+
+	return EOK;
+}
+
+/**
+ * Check whether the device is a hub and if so, fill its characterstics.
+ *
+ * If this fails, it does not necessarily mean the device is unusable.
+ * Just the TT will not work correctly.
+ */
+static int setup_hub(xhci_device_t *dev, usb_standard_device_descriptor_t *desc)
+{
+	if (desc->device_class != USB_CLASS_HUB)
+		return EOK;
+
+	usb_hub_descriptor_header_t hub_desc = { 0 };
+	const int err = hc_get_hub_desc(&dev->base, &hub_desc);
+	if (err)
+		return err;
+
+	dev->is_hub = 1;
+	dev->num_ports = hub_desc.port_count;
+	dev->tt_think_time = 8 +
+		8  * !!(hub_desc.characteristics & HUB_CHAR_TT_THINK_8) +
+		16 * !!(hub_desc.characteristics & HUB_CHAR_TT_THINK_16);
+
+	usb_log_debug2("Device(%u): recognised USB hub with %u ports", dev->base.address, dev->num_ports);
+	return EOK;
+}
+
+/**
+ * Respond to a new device on the XHCI bus. Address it, negotiate packet size
+ * and retrieve USB descriptors.
+ *
+ * @param[in] bus XHCI bus, where the new device emerged.
+ * @param[in] dev XHCI device, which has appeared on the bus.
+ *
+ * @return Error code.
+ */
+int xhci_device_enumerate(device_t *dev)
+{
+	int err;
+	xhci_bus_t *bus = bus_to_xhci_bus(dev->bus);
+	xhci_device_t *xhci_dev = xhci_device_get(dev);
+
+	/* Calculate route string */
+	xhci_device_t *xhci_hub = xhci_device_get(dev->hub);
+	xhci_dev->tier = xhci_hub->tier + 1;
+	xhci_dev->route_str = xhci_hub->route_str;
+
+	/* Roothub port is not part of the route string */
+	if (xhci_dev->tier >= 2) {
+		const unsigned offset = 4 * (xhci_dev->tier - 2);
+		xhci_dev->route_str |= (dev->port & 0xf) << offset;
+		xhci_dev->rh_port = xhci_hub->rh_port;
+	}
+
+	int retries = 3;
+	do {
+		/* Assign an address to the device */
+		err = address_device(xhci_dev);
+	} while (err == ESTALL && --retries > 0);
+
+	if (err) {
+		usb_log_error("Failed to setup address of the new device: %s", str_error(err));
+		return err;
+	}
+
+	/* Setup EP0 might already need to issue a transfer. */
+	fibril_mutex_lock(&bus->base.guard);
+	assert(bus->devices_by_slot[xhci_dev->slot_id] == NULL);
+	bus->devices_by_slot[xhci_dev->slot_id] = xhci_dev;
+	fibril_mutex_unlock(&bus->base.guard);
+
+	if ((err = setup_ep0_packet_size(bus->hc, xhci_dev))) {
+		usb_log_error("Failed to setup control endpoint of the new device: %s", str_error(err));
+		goto err_address;
+	}
+
+	usb_standard_device_descriptor_t desc = { 0 };
+
+	if ((err = hc_get_device_desc(dev, &desc))) {
+		usb_log_error("Device(%d): failed to get devices descriptor: %s", dev->address, str_error(err));
+		goto err_address;
+	}
+
+	if ((err = setup_hub(xhci_dev, &desc)))
+		usb_log_warning("Device(%d): failed to setup hub characteristics: %s. "
+		    " Continuing anyway.", dev->address, str_error(err));
+
+	if ((err = hcd_ddf_setup_match_ids(dev, &desc))) {
+		usb_log_error("Device(%d): failed to setup match IDs: %s", dev->address, str_error(err));
+		goto err_address;
+	}
+
+	return EOK;
+
+err_address:
+	return err;
+}
+
+/**
+ * Remove device from XHCI bus. Transition it to the offline state, abort all
+ * ongoing transfers and unregister all of its endpoints.
+ *
+ * Bus callback.
+ *
+ * @param[in] bus XHCI bus, from which the device is removed.
+ * @param[in] dev XHCI device, which is removed from the bus.
+ * @return Error code.
+ */
+void xhci_device_gone(device_t *dev)
+{
+	int err;
+	xhci_bus_t *bus = bus_to_xhci_bus(dev->bus);
+	xhci_device_t *xhci_dev = xhci_device_get(dev);
+
+	/* Disable the slot, dropping all endpoints. */
+	const uint32_t slot_id = xhci_dev->slot_id;
+	if ((err = hc_disable_slot(xhci_dev))) {
+		usb_log_warning("Failed to disable slot of device " XHCI_DEV_FMT ": %s",
+		    XHCI_DEV_ARGS(*xhci_dev), str_error(err));
+	}
+
+	bus->devices_by_slot[slot_id] = NULL;
+}
+
+/**
+ * Reverts things device_offline did, getting the device back up.
+ *
+ * Bus callback.
+ */
+int xhci_device_online(device_t *dev_base)
+{
+	int err;
+
+	xhci_bus_t *bus = bus_to_xhci_bus(dev_base->bus);
+	assert(bus);
+
+	xhci_device_t *dev = xhci_device_get(dev_base);
+	assert(dev);
+
+	/* Transition the device from the Addressed to the Configured state. */
+	if ((err = hc_configure_device(dev))) {
+		usb_log_warning("Failed to configure device " XHCI_DEV_FMT ".", XHCI_DEV_ARGS(*dev));
+		return err;
+	}
+
+	return EOK;
+}
+
+/**
+ * Make given device offline. Offline the DDF function, tear down all
+ * endpoints, issue Deconfigure Device command to xHC.
+ *
+ * Bus callback.
+ */
+void xhci_device_offline(device_t *dev_base)
+{
+	int err;
+
+	xhci_bus_t *bus = bus_to_xhci_bus(dev_base->bus);
+	assert(bus);
+
+	xhci_device_t *dev = xhci_device_get(dev_base);
+	assert(dev);
+
+	/* Issue one HC command to simultaneously drop all endpoints except zero. */
+	if ((err = hc_deconfigure_device(dev))) {
+		usb_log_warning("Failed to deconfigure device " XHCI_DEV_FMT ".",
+		    XHCI_DEV_ARGS(*dev));
+	}
+}
+
+
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/device.h
===================================================================
--- uspace/drv/bus/usb/xhci/device.h	(revision 682c9354d8b5a94c62b34c3784db467a18e7e9ae)
+++ uspace/drv/bus/usb/xhci/device.h	(revision 682c9354d8b5a94c62b34c3784db467a18e7e9ae)
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2017 Ondrej Hlavaty <aearsis@eideo.cz>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/** @addtogroup libusbhost
+ * @{
+ */
+/** @file
+ *
+ */
+#ifndef XHCI_DEVICE_H
+#define XHCI_DEVICE_H
+
+#include <usb/host/bus.h>
+#include <usb/host/dma_buffer.h>
+
+typedef struct xhci_device {
+	device_t base;		/**< Inheritance. Keep this first. */
+
+	/** Slot ID assigned to the device by xHC. */
+	uint32_t slot_id;
+
+	/** Corresponding port on RH */
+	uint8_t rh_port;
+
+	/** USB Tier of the device */
+	uint8_t tier;
+
+	/** Route string */
+	uint32_t route_str;
+
+	/** Place to store the allocated context */
+	dma_buffer_t dev_ctx;
+
+	/** Hub specific information. Valid only if the device is_hub. */
+	bool is_hub;
+	uint8_t num_ports;
+	uint8_t tt_think_time;
+} xhci_device_t;
+
+#define XHCI_DEV_FMT  "(%s, slot %d)"
+#define XHCI_DEV_ARGS(dev)		 ddf_fun_get_name((dev).base.fun), (dev).slot_id
+
+/* Bus callbacks */
+int xhci_device_enumerate(device_t *);
+void xhci_device_offline(device_t *);
+int xhci_device_online(device_t *);
+void xhci_device_gone(device_t *);
+
+static inline xhci_device_t * xhci_device_get(device_t *dev)
+{
+	assert(dev);
+	return (xhci_device_t *) dev;
+}
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/bus/usb/xhci/endpoint.c
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.c	(revision 129b821fb30858c35df3ba6ff80bf8ccc51442de)
+++ uspace/drv/bus/usb/xhci/endpoint.c	(revision 682c9354d8b5a94c62b34c3784db467a18e7e9ae)
@@ -39,8 +39,10 @@
 #include <errno.h>
 #include <macros.h>
+#include <str_error.h>
 
 #include "hc.h"
 #include "bus.h"
 #include "commands.h"
+#include "device.h"
 #include "endpoint.h"
 #include "streams.h"
@@ -56,5 +58,5 @@
  * @return Error code.
  */
-int xhci_endpoint_init(xhci_endpoint_t *xhci_ep, device_t *dev, const usb_endpoint_descriptors_t *desc)
+static int xhci_endpoint_init(xhci_endpoint_t *xhci_ep, device_t *dev, const usb_endpoint_descriptors_t *desc)
 {
 	int rc;
@@ -113,8 +115,30 @@
 
 /**
+ * Create a new xHCI endpoint structure.
+ *
+ * Bus callback.
+ */
+endpoint_t *xhci_endpoint_create(device_t *dev, const usb_endpoint_descriptors_t *desc)
+{
+	const usb_transfer_type_t type = USB_ED_GET_TRANSFER_TYPE(desc->endpoint);
+
+	xhci_endpoint_t *ep = calloc(1, sizeof(xhci_endpoint_t)
+		+ (type == USB_TRANSFER_ISOCHRONOUS) * sizeof(*ep->isoch));
+	if (!ep)
+		return NULL;
+
+	if (xhci_endpoint_init(ep, dev, desc)) {
+		free(ep);
+		return NULL;
+	}
+
+	return &ep->base;
+}
+
+/**
  * Finalize XHCI endpoint.
  * @param[in] xhci_ep XHCI endpoint to finalize.
  */
-void xhci_endpoint_fini(xhci_endpoint_t *xhci_ep)
+static void xhci_endpoint_fini(xhci_endpoint_t *xhci_ep)
 {
 	assert(xhci_ep);
@@ -123,4 +147,99 @@
 
 	// TODO: Something missed?
+}
+
+/**
+ * Destroy given xHCI endpoint structure.
+ *
+ * Bus callback.
+ */
+void xhci_endpoint_destroy(endpoint_t *ep)
+{
+	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
+
+	xhci_endpoint_fini(xhci_ep);
+	free(xhci_ep);
+}
+
+
+/**
+ * Register an andpoint to the xHC.
+ *
+ * Bus callback.
+ */
+int xhci_endpoint_register(endpoint_t *ep_base)
+{
+	int err;
+	xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
+	xhci_device_t *dev = xhci_device_get(ep_base->device);
+
+	xhci_ep_ctx_t ep_ctx;
+	xhci_setup_endpoint_context(ep, &ep_ctx);
+
+	if ((err = hc_add_endpoint(dev, xhci_endpoint_index(ep), &ep_ctx)))
+		return err;
+
+	return EOK;
+}
+
+/**
+ * Abort a transfer on an endpoint.
+ */
+static int endpoint_abort(endpoint_t *ep)
+{
+	xhci_device_t *dev = xhci_device_get(ep->device);
+
+	usb_transfer_batch_t *batch = NULL;
+	fibril_mutex_lock(&ep->guard);
+	if (ep->active_batch) {
+		if (dev->slot_id) {
+			const int err = hc_stop_endpoint(dev, xhci_endpoint_dci(xhci_endpoint_get(ep)));
+			if (err) {
+				usb_log_warning("Failed to stop endpoint %u of device " XHCI_DEV_FMT ": %s",
+				    ep->endpoint, XHCI_DEV_ARGS(*dev), str_error(err));
+			}
+
+			endpoint_wait_timeout_locked(ep, 2000);
+		}
+
+		batch = ep->active_batch;
+		if (batch) {
+			endpoint_deactivate_locked(ep);
+		}
+	}
+	fibril_mutex_unlock(&ep->guard);
+
+	if (batch) {
+		batch->error = EINTR;
+		batch->transfered_size = 0;
+		usb_transfer_batch_finish(batch);
+	}
+	return EOK;
+}
+
+/**
+ * Unregister an endpoint. If the device is still available, inform the xHC
+ * about it.
+ *
+ * Bus callback.
+ */
+void xhci_endpoint_unregister(endpoint_t *ep_base)
+{
+	int err;
+	xhci_endpoint_t *ep = xhci_endpoint_get(ep_base);
+	xhci_device_t *dev = xhci_device_get(ep_base->device);
+
+	endpoint_abort(ep_base);
+
+	/* If device slot is still available, drop the endpoint. */
+	if (dev->slot_id) {
+
+		if ((err = hc_drop_endpoint(dev, xhci_endpoint_index(ep)))) {
+			usb_log_error("Failed to drop endpoint " XHCI_EP_FMT ": %s", XHCI_EP_ARGS(*ep), str_error(err));
+		}
+	} else {
+		usb_log_debug("Not going to drop endpoint " XHCI_EP_FMT " because"
+		    " the slot has already been disabled.", XHCI_EP_ARGS(*ep));
+	}
 }
 
Index: uspace/drv/bus/usb/xhci/endpoint.h
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.h	(revision 129b821fb30858c35df3ba6ff80bf8ccc51442de)
+++ uspace/drv/bus/usb/xhci/endpoint.h	(revision 682c9354d8b5a94c62b34c3784db467a18e7e9ae)
@@ -45,4 +45,5 @@
 #include <ddf/driver.h>
 
+#include "device.h"
 #include "isoch.h"
 #include "transfers.h"
@@ -105,35 +106,10 @@
 	(usb_str_transfer_type((ep).base.transfer_type))
 
-typedef struct xhci_device {
-	device_t base;		/**< Inheritance. Keep this first. */
-
-	/** Slot ID assigned to the device by xHC. */
-	uint32_t slot_id;
-
-	/** Corresponding port on RH */
-	uint8_t rh_port;
-
-	/** USB Tier of the device */
-	uint8_t tier;
-
-	/** Route string */
-	uint32_t route_str;
-
-	/** Place to store the allocated context */
-	dma_buffer_t dev_ctx;
-
-	/** Hub specific information. Valid only if the device is_hub. */
-	bool is_hub;
-	uint8_t num_ports;
-	uint8_t tt_think_time;
-} xhci_device_t;
-
-#define XHCI_DEV_FMT  "(%s, slot %d)"
-#define XHCI_DEV_ARGS(dev)		 ddf_fun_get_name((dev).base.fun), (dev).slot_id
-
 int xhci_endpoint_type(xhci_endpoint_t *ep);
 
-int xhci_endpoint_init(xhci_endpoint_t *, device_t *, const usb_endpoint_descriptors_t *);
-void xhci_endpoint_fini(xhci_endpoint_t *);
+endpoint_t *xhci_endpoint_create(device_t *, const usb_endpoint_descriptors_t *);
+int xhci_endpoint_register(endpoint_t *);
+void xhci_endpoint_unregister(endpoint_t *);
+void xhci_endpoint_destroy(endpoint_t *);
 
 void xhci_endpoint_free_transfer_ds(xhci_endpoint_t *xhci_ep);
@@ -144,10 +120,4 @@
 void xhci_setup_endpoint_context(xhci_endpoint_t *, xhci_ep_ctx_t *);
 int xhci_endpoint_clear_halt(xhci_endpoint_t *, unsigned);
-
-static inline xhci_device_t * xhci_device_get(device_t *dev)
-{
-	assert(dev);
-	return (xhci_device_t *) dev;
-}
 
 static inline xhci_endpoint_t * xhci_endpoint_get(endpoint_t *ep)
Index: uspace/drv/bus/usb/xhci/transfers.c
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.c	(revision 129b821fb30858c35df3ba6ff80bf8ccc51442de)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision 682c9354d8b5a94c62b34c3784db467a18e7e9ae)
@@ -416,12 +416,22 @@
 };
 
-int xhci_transfer_schedule(xhci_hc_t *hc, usb_transfer_batch_t *batch)
-{
-	assert(hc);
+/**
+ * Schedule a batch for xHC.
+ *
+ * Bus callback.
+ */
+int xhci_transfer_schedule(usb_transfer_batch_t *batch)
+{
 	endpoint_t *ep = batch->ep;
 
+	xhci_hc_t *hc = bus_to_hc(endpoint_get_bus(batch->ep));
 	xhci_transfer_t *transfer = xhci_transfer_from_batch(batch);
 	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(ep);
 	xhci_device_t *xhci_dev = xhci_ep_to_dev(xhci_ep);
+
+	if (!batch->target.address) {
+		usb_log_error("Attempted to schedule transfer to address 0.");
+		return EINVAL;
+	}
 
 	// FIXME: find a better way to check if the ring is not initialized
Index: uspace/drv/bus/usb/xhci/transfers.h
===================================================================
--- uspace/drv/bus/usb/xhci/transfers.h	(revision 129b821fb30858c35df3ba6ff80bf8ccc51442de)
+++ uspace/drv/bus/usb/xhci/transfers.h	(revision 682c9354d8b5a94c62b34c3784db467a18e7e9ae)
@@ -56,5 +56,6 @@
 
 usb_transfer_batch_t* xhci_transfer_create(endpoint_t *);
-int xhci_transfer_schedule(xhci_hc_t *, usb_transfer_batch_t *);
+int xhci_transfer_schedule(usb_transfer_batch_t *);
+
 int xhci_handle_transfer_event(xhci_hc_t *, xhci_trb_t *);
 void xhci_transfer_destroy(usb_transfer_batch_t *);
