Index: uspace/drv/bus/usb/xhci/bus.c
===================================================================
--- uspace/drv/bus/usb/xhci/bus.c	(revision 366e9b6e9dc1eec138a5ceadc18883937ba67764)
+++ uspace/drv/bus/usb/xhci/bus.c	(revision c10daa82dbe942a6c5d2311e2cf11aac4ce52e01)
@@ -50,7 +50,7 @@
 	ht_link_t link;
 
-	/** Endpoint */
-	xhci_endpoint_t *endpoint;
-} hashed_endpoint_t;
+	/** Device */
+	xhci_device_t *device;
+} hashed_device_t;
 
 /** Ops receive generic bus_t pointer. */
@@ -85,42 +85,112 @@
 }
 
-static int endpoint_find_by_target(xhci_bus_t *bus, usb_target_t target, hashed_endpoint_t **ep)
-{
-	ht_link_t *link = hash_table_find(&bus->endpoints, &target.packed);
+static int hashed_device_find_by_address(xhci_bus_t *bus, usb_address_t address, hashed_device_t **dev)
+{
+	ht_link_t *link = hash_table_find(&bus->devices, &address);
 	if (link == NULL)
 		return ENOENT;
 
-	*ep = hash_table_get_inst(link, hashed_endpoint_t, link);
-	return EOK;
-}
-
-static int register_endpoint(bus_t *bus_base, endpoint_t *ep)
-{
-	xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
-	assert(bus);
-
-	hashed_endpoint_t *hashed_ep =
-	    (hashed_endpoint_t *) malloc(sizeof(hashed_endpoint_t));
-	if (!hashed_ep)
-		return ENOMEM;
-
-	hashed_ep->endpoint = xhci_endpoint_get(ep);
-	hash_table_insert(&bus->endpoints, &hashed_ep->link);
-
-	return EOK;
-}
-
-static int release_endpoint(bus_t *bus_base, endpoint_t *ep)
-{
-	xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
-	assert(bus);
-
-	hashed_endpoint_t *hashed_ep;
-	int res = endpoint_find_by_target(bus, ep->target, &hashed_ep);
+	*dev = hash_table_get_inst(link, hashed_device_t, link);
+	return EOK;
+}
+
+static int xhci_endpoint_find_by_target(xhci_bus_t *bus, usb_target_t target, xhci_endpoint_t **ep)
+{
+	hashed_device_t *dev;
+	int res = hashed_device_find_by_address(bus, target.address, &dev);
 	if (res != EOK)
 		return res;
 
-	hash_table_remove(&bus->endpoints, &ep->target.packed);
-	free(hashed_ep);
+	xhci_endpoint_t *ret_ep = xhci_device_get_endpoint(dev->device, target.endpoint);
+	if (!ret_ep)
+		return ENOENT;
+
+	*ep = ret_ep;
+	return EOK;
+}
+
+static int hashed_device_create(xhci_bus_t *bus, hashed_device_t **hashed_dev)
+{
+	int res;
+	xhci_device_t *dev = (xhci_device_t *) malloc(sizeof(xhci_device_t));
+	if (!dev) {
+		res = ENOMEM;
+		goto err_xhci_dev_alloc;
+	}
+
+	res = xhci_device_init(dev, bus);
+	if (res != EOK) {
+		goto err_xhci_dev_init;
+	}
+
+	// TODO: Set device data.
+
+	hashed_device_t *ret_dev = (hashed_device_t *) malloc(sizeof(hashed_device_t));
+	if (!ret_dev) {
+		res = ENOMEM;
+		goto err_hashed_dev_alloc;
+	}
+
+	ret_dev->device = dev;
+
+	hash_table_insert(&bus->devices, &ret_dev->link);
+	*hashed_dev = ret_dev;
+	return EOK;
+
+err_hashed_dev_alloc:
+err_xhci_dev_init:
+	free(dev);
+err_xhci_dev_alloc:
+	return res;
+}
+
+static int hashed_device_remove(xhci_bus_t *bus, hashed_device_t *hashed_dev)
+{
+	hash_table_remove(&bus->devices, &hashed_dev->device->address);
+	xhci_device_fini(hashed_dev->device);
+	free(hashed_dev->device);
+	free(hashed_dev);
+
+	return EOK;
+}
+
+static int register_endpoint(bus_t *bus_base, endpoint_t *ep)
+{
+	xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
+	assert(bus);
+
+	hashed_device_t *hashed_dev;
+	int res = hashed_device_find_by_address(bus, ep->target.address, &hashed_dev);
+	if (res != EOK && res != ENOENT)
+		return res;
+
+	if (res == ENOENT) {
+		res = hashed_device_create(bus, &hashed_dev);
+
+		if (res != EOK)
+			return res;
+	}
+
+	return xhci_device_add_endpoint(hashed_dev->device, xhci_endpoint_get(ep));
+}
+
+static int release_endpoint(bus_t *bus_base, endpoint_t *ep)
+{
+	xhci_bus_t *bus = bus_to_xhci_bus(bus_base);
+	assert(bus);
+
+	hashed_device_t *hashed_dev;
+	int res = hashed_device_find_by_address(bus, ep->target.address, &hashed_dev);
+	if (res != EOK)
+		return res;
+
+	xhci_device_remove_endpoint(hashed_dev->device, xhci_endpoint_get(ep));
+
+	if (hashed_dev->device->active_endpoint_count == 0) {
+		res = hashed_device_remove(bus, hashed_dev);
+
+		if (res != EOK)
+			return res;
+	}
 
 	return EOK;
@@ -132,10 +202,10 @@
 	assert(bus);
 
-	hashed_endpoint_t *hashed_ep;
-	int res = endpoint_find_by_target(bus, target, &hashed_ep);
+	xhci_endpoint_t *ep;
+	int res = xhci_endpoint_find_by_target(bus, target, &ep);
 	if (res != EOK)
 		return NULL;
 
-	return &hashed_ep->endpoint->base;
+	return &ep->base;
 }
 
@@ -205,26 +275,26 @@
 };
 
-static size_t endpoint_ht_hash(const ht_link_t *item)
-{
-	hashed_endpoint_t *ep = hash_table_get_inst(item, hashed_endpoint_t, link);
-	return (size_t) hash_mix32(ep->endpoint->base.target.packed);
-}
-
-static size_t endpoint_ht_key_hash(void *key)
-{
-	return (size_t) hash_mix32(*(uint32_t *)key);
-}
-
-static bool endpoint_ht_key_equal(void *key, const ht_link_t *item)
-{
-	hashed_endpoint_t *ep = hash_table_get_inst(item, hashed_endpoint_t, link);
-	return ep->endpoint->base.target.packed == *(uint32_t *) key;
-}
-
-/** Operations for the endpoint hash table. */
-static hash_table_ops_t endpoint_ht_ops = {
-	.hash = endpoint_ht_hash,
-	.key_hash = endpoint_ht_key_hash,
-	.key_equal = endpoint_ht_key_equal,
+static size_t device_ht_hash(const ht_link_t *item)
+{
+	hashed_device_t *dev = hash_table_get_inst(item, hashed_device_t, link);
+	return (size_t) hash_mix(dev->device->address);
+}
+
+static size_t device_ht_key_hash(void *key)
+{
+	return (size_t) hash_mix(*(usb_address_t *)key);
+}
+
+static bool device_ht_key_equal(void *key, const ht_link_t *item)
+{
+	hashed_device_t *dev = hash_table_get_inst(item, hashed_device_t, link);
+	return dev->device->address == *(usb_address_t *) key;
+}
+
+/** Operations for the device hash table. */
+static hash_table_ops_t device_ht_ops = {
+	.hash = device_ht_hash,
+	.key_hash = device_ht_key_hash,
+	.key_equal = device_ht_key_equal,
 	.equal = NULL,
 	.remove_callback = NULL
@@ -237,5 +307,5 @@
 	bus_init(&bus->base);
 
-	if (!hash_table_create(&bus->endpoints, 0, 0, &endpoint_ht_ops)) {
+	if (!hash_table_create(&bus->devices, 0, 0, &device_ht_ops)) {
 		// FIXME: Dealloc base!
 		return ENOMEM;
@@ -248,5 +318,7 @@
 void xhci_bus_fini(xhci_bus_t *bus)
 {
-	hash_table_destroy(&bus->endpoints);
+	// FIXME: Make sure no devices are in the hash table.
+
+	hash_table_destroy(&bus->devices);
 }
 /**
Index: uspace/drv/bus/usb/xhci/bus.h
===================================================================
--- uspace/drv/bus/usb/xhci/bus.h	(revision 366e9b6e9dc1eec138a5ceadc18883937ba67764)
+++ uspace/drv/bus/usb/xhci/bus.h	(revision c10daa82dbe942a6c5d2311e2cf11aac4ce52e01)
@@ -52,5 +52,5 @@
 	 */
 
-	hash_table_t endpoints;
+	hash_table_t devices;
 } xhci_bus_t;
 
Index: uspace/drv/bus/usb/xhci/endpoint.c
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.c	(revision 366e9b6e9dc1eec138a5ceadc18883937ba67764)
+++ uspace/drv/bus/usb/xhci/endpoint.c	(revision c10daa82dbe942a6c5d2311e2cf11aac4ce52e01)
@@ -50,6 +50,5 @@
 
 	endpoint_init(ep, bus);
-
-	/* FIXME: Set xhci_ep->slot_id */
+	xhci_ep->device = NULL;
 
 	usb_log_debug("XHCI Endpoint %d:%d initialized.", ep->target.address, ep->target.endpoint);
@@ -69,4 +68,45 @@
 }
 
+int xhci_device_init(xhci_device_t *dev, xhci_bus_t *bus)
+{
+	memset(&dev->endpoints, 0, sizeof(dev->endpoints));
+	dev->active_endpoint_count = 0;
+	return EOK;
+}
+
+void xhci_device_fini(xhci_device_t *dev)
+{
+	// TODO: Check that all endpoints are dead.
+}
+
+int xhci_device_add_endpoint(xhci_device_t *dev, xhci_endpoint_t *ep)
+{
+	assert(dev->address == ep->base.target.address);
+	assert(!dev->endpoints[ep->base.target.endpoint]);
+	assert(!ep->device);
+
+	ep->device = dev;
+	dev->endpoints[ep->base.target.endpoint] = ep;
+	++dev->active_endpoint_count;
+	return EOK;
+}
+
+int xhci_device_remove_endpoint(xhci_device_t *dev, xhci_endpoint_t *ep)
+{
+	assert(dev->address == ep->base.target.address);
+	assert(dev->endpoints[ep->base.target.endpoint]);
+	assert(dev == ep->device);
+
+	ep->device = NULL;
+	dev->endpoints[ep->base.target.endpoint] = NULL;
+	--dev->active_endpoint_count;
+	return EOK;
+}
+
+xhci_endpoint_t * xhci_device_get_endpoint(xhci_device_t *dev, usb_endpoint_t ep)
+{
+	return dev->endpoints[ep];
+}
+
 /**
  * @}
Index: uspace/drv/bus/usb/xhci/endpoint.h
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.h	(revision 366e9b6e9dc1eec138a5ceadc18883937ba67764)
+++ uspace/drv/bus/usb/xhci/endpoint.h	(revision c10daa82dbe942a6c5d2311e2cf11aac4ce52e01)
@@ -43,6 +43,9 @@
 #include <usb/host/hcd.h>
 
+typedef struct xhci_device xhci_device_t;
 typedef struct xhci_endpoint xhci_endpoint_t;
 typedef struct xhci_bus xhci_bus_t;
+
+#define XHCI_DEVICE_MAX_ENDPOINTS 32
 
 enum {
@@ -61,9 +64,25 @@
 	endpoint_t base;	/**< Inheritance. Keep this first. */
 
+	xhci_device_t *device;
+} xhci_endpoint_t;
+
+typedef struct xhci_device {
+	usb_address_t address;
+
 	uint32_t slot_id;
-} xhci_endpoint_t;
+
+	xhci_endpoint_t *endpoints[XHCI_DEVICE_MAX_ENDPOINTS];
+	uint8_t active_endpoint_count;
+} 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 *);
+void xhci_device_fini(xhci_device_t *);
+
+int xhci_device_add_endpoint(xhci_device_t *, xhci_endpoint_t *);
+int xhci_device_remove_endpoint(xhci_device_t *, xhci_endpoint_t *);
+xhci_endpoint_t * xhci_device_get_endpoint(xhci_device_t *, usb_endpoint_t);
 
 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 366e9b6e9dc1eec138a5ceadc18883937ba67764)
+++ uspace/drv/bus/usb/xhci/transfers.c	(revision c10daa82dbe942a6c5d2311e2cf11aac4ce52e01)
@@ -150,5 +150,5 @@
 	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
 
-	uint8_t slot_id = xhci_ep->slot_id;
+	uint8_t slot_id = xhci_ep->device->slot_id;
 	xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[0];
 
@@ -234,5 +234,5 @@
 
 	xhci_endpoint_t *xhci_ep = xhci_endpoint_get(batch->ep);
-	uint8_t slot_id = xhci_ep->slot_id;
+	uint8_t slot_id = xhci_ep->device->slot_id;
 	xhci_trb_ring_t* ring = hc->dcbaa_virt[slot_id].trs[batch->ep->target.endpoint];
 
