Index: uspace/drv/bus/usb/xhci/bus.c
===================================================================
--- uspace/drv/bus/usb/xhci/bus.c	(revision 944f8fdd006b2359954cee16ede3ed01c2d88b24)
+++ uspace/drv/bus/usb/xhci/bus.c	(revision 2833bb4cbd3b1df768dce926e9942633366f6b10)
@@ -37,4 +37,6 @@
 #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>
@@ -136,4 +138,30 @@
 
 /**
+ * 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.
@@ -184,7 +212,17 @@
 	}
 
-	/* Read the device descriptor, derive the match ids */
-	if ((err = hc_device_explore(dev))) {
-		usb_log_error("Device(%d): Failed to explore device: %s", dev->address, str_error(err));
+	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;
 	}
Index: uspace/drv/bus/usb/xhci/debug.c
===================================================================
--- uspace/drv/bus/usb/xhci/debug.c	(revision 944f8fdd006b2359954cee16ede3ed01c2d88b24)
+++ uspace/drv/bus/usb/xhci/debug.c	(revision 2833bb4cbd3b1df768dce926e9942633366f6b10)
@@ -347,8 +347,9 @@
 	SLOT_DUMP(SPEED);
 	SLOT_DUMP(MTT);
+	SLOT_DUMP(HUB);
 	SLOT_DUMP(CTX_ENTRIES);
 	SLOT_DUMP(MAX_EXIT_LATENCY);
 	SLOT_DUMP(ROOT_HUB_PORT);
-	SLOT_DUMP(NUM_OF_PORTS);
+	SLOT_DUMP(NUM_PORTS);
 	SLOT_DUMP(TT_HUB_SLOT_ID);
 	SLOT_DUMP(TT_PORT_NUM);
Index: uspace/drv/bus/usb/xhci/endpoint.h
===================================================================
--- uspace/drv/bus/usb/xhci/endpoint.h	(revision 944f8fdd006b2359954cee16ede3ed01c2d88b24)
+++ uspace/drv/bus/usb/xhci/endpoint.h	(revision 2833bb4cbd3b1df768dce926e9942633366f6b10)
@@ -123,6 +123,8 @@
 	dma_buffer_t dev_ctx;
 
-	/** Flag indicating whether the device is USB3 (it's USB2 otherwise). */
-	bool usb3;
+	/** 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;
 
Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 944f8fdd006b2359954cee16ede3ed01c2d88b24)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision 2833bb4cbd3b1df768dce926e9942633366f6b10)
@@ -777,4 +777,16 @@
 	XHCI_SLOT_SPEED_SET(*ctx, usb_speed_to_psiv[dev->base.speed]);
 
+	/*
+	 * Note: This function is used even before this flag can be set, to
+	 *       issue the address device command. It is OK, because these
+	 *       flags are not required to be valid for that command.
+	 */
+	if (dev->is_hub) {
+		XHCI_SLOT_HUB_SET(*ctx, 1);
+		XHCI_SLOT_NUM_PORTS_SET(*ctx, dev->num_ports);
+		XHCI_SLOT_TT_THINK_TIME_SET(*ctx, dev->tt_think_time);
+		XHCI_SLOT_MTT_SET(*ctx, 0); // MTT not supported yet
+	}
+
 	/* Setup Transaction Translation. TODO: Test this with HS hub. */
 	if (dev->base.tt.dev != NULL) {
Index: uspace/drv/bus/usb/xhci/hw_struct/context.h
===================================================================
--- uspace/drv/bus/usb/xhci/hw_struct/context.h	(revision 944f8fdd006b2359954cee16ede3ed01c2d88b24)
+++ uspace/drv/bus/usb/xhci/hw_struct/context.h	(revision 2833bb4cbd3b1df768dce926e9942633366f6b10)
@@ -130,4 +130,6 @@
 #define XHCI_SLOT_MTT_SET(ctx, val) \
 	xhci_dword_set_bits(&(ctx).data[0], !!val, 25, 25)
+#define XHCI_SLOT_HUB_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[0], !!val, 25, 25)
 #define XHCI_SLOT_CTX_ENTRIES_SET(ctx, val) \
 	xhci_dword_set_bits(&(ctx).data[0], val, 31, 27)
@@ -135,4 +137,6 @@
 #define XHCI_SLOT_ROOT_HUB_PORT_SET(ctx, val) \
 	xhci_dword_set_bits(&(ctx).data[1], val, 23, 16)
+#define XHCI_SLOT_NUM_PORTS_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[1], val, 31, 24)
 
 #define XHCI_SLOT_TT_HUB_SLOT_ID_SET(ctx, val) \
@@ -140,13 +144,16 @@
 #define XHCI_SLOT_TT_HUB_PORT_SET(ctx, val) \
 	xhci_dword_set_bits(&(ctx).data[2], (val & 0xFF), 15, 8)
+#define XHCI_SLOT_TT_THINK_TIME_SET(ctx, val) \
+	xhci_dword_set_bits(&(ctx).data[2], (val & 0xFF), 17, 16)
 
 #define XHCI_SLOT_ROUTE_STRING(ctx)     XHCI_DWORD_EXTRACT((ctx).data[0], 19,  0)
 #define XHCI_SLOT_SPEED(ctx)            XHCI_DWORD_EXTRACT((ctx).data[0], 23, 20)
 #define XHCI_SLOT_MTT(ctx)              XHCI_DWORD_EXTRACT((ctx).data[0], 25, 25)
+#define XHCI_SLOT_HUB(ctx)              XHCI_DWORD_EXTRACT((ctx).data[0], 26, 26)
 #define XHCI_SLOT_CTX_ENTRIES(ctx)      XHCI_DWORD_EXTRACT((ctx).data[0], 31, 27)
 
 #define XHCI_SLOT_MAX_EXIT_LATENCY(ctx) XHCI_DWORD_EXTRACT((ctx).data[1], 15,  0)
 #define XHCI_SLOT_ROOT_HUB_PORT(ctx)    XHCI_DWORD_EXTRACT((ctx).data[1], 23, 16)
-#define XHCI_SLOT_NUM_OF_PORTS(ctx)     XHCI_DWORD_EXTRACT((ctx).data[1], 31, 24)
+#define XHCI_SLOT_NUM_PORTS(ctx)        XHCI_DWORD_EXTRACT((ctx).data[1], 31, 24)
 
 #define XHCI_SLOT_TT_HUB_SLOT_ID(ctx)   XHCI_DWORD_EXTRACT((ctx).data[2],  7,  0)
Index: uspace/lib/usbhost/include/usb/host/utility.h
===================================================================
--- uspace/lib/usbhost/include/usb/host/utility.h	(revision 944f8fdd006b2359954cee16ede3ed01c2d88b24)
+++ uspace/lib/usbhost/include/usb/host/utility.h	(revision 2833bb4cbd3b1df768dce926e9942633366f6b10)
@@ -41,4 +41,5 @@
 #include <usb/host/usb_transfer_batch.h>
 #include <usb/descriptor.h>
+#include <usb/classes/hub.h>
 #include <usb/request.h>
 
@@ -47,4 +48,5 @@
 int hc_setup_virtual_root_hub(hc_device_t *);
 int hc_get_device_desc(device_t *, usb_standard_device_descriptor_t *);
+int hc_get_hub_desc(device_t *, usb_hub_descriptor_header_t *);
 int hc_device_explore(device_t *);
 
Index: uspace/lib/usbhost/src/utility.c
===================================================================
--- uspace/lib/usbhost/src/utility.c	(revision 944f8fdd006b2359954cee16ede3ed01c2d88b24)
+++ uspace/lib/usbhost/src/utility.c	(revision 2833bb4cbd3b1df768dce926e9942633366f6b10)
@@ -162,4 +162,32 @@
 }
 
+int hc_get_hub_desc(device_t *device, usb_hub_descriptor_header_t *desc)
+{
+	const usb_target_t control_ep = {{
+		.address = device->address,
+		.endpoint = 0,
+	}};
+
+	const usb_device_request_setup_packet_t get_hub_desc = {
+		.request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST
+		    | (USB_REQUEST_TYPE_CLASS << 5)
+		    | USB_REQUEST_RECIPIENT_DEVICE,
+		.request = USB_DEVREQ_GET_DESCRIPTOR, \
+		.value = uint16_host2usb(USB_DESCTYPE_HUB << 8), \
+		.length = sizeof(*desc),
+	};
+
+	usb_log_debug("Device(%d): Requesting hub descriptor.",
+	    device->address);
+	ssize_t got = bus_device_send_batch_sync(device, control_ep, USB_DIRECTION_IN,
+	    (char *) desc, sizeof(*desc), *(uint64_t *)&get_hub_desc,
+	    "get hub descriptor");
+
+	if (got < 0)
+		return got;
+
+	return got == sizeof(*desc) ? EOK : EOVERFLOW;
+}
+
 int hc_device_explore(device_t *device)
 {
