Index: kernel/generic/include/ipc/ipc.h
===================================================================
--- kernel/generic/include/ipc/ipc.h	(revision bbc28becd7d06ac1598f9fe6c3a639eb10017af9)
+++ kernel/generic/include/ipc/ipc.h	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -45,5 +45,5 @@
 /** Maximum active async calls per thread */
 #ifdef CONFIG_DEBUG
-	#define IPC_MAX_ASYNC_CALLS  4
+	#define IPC_MAX_ASYNC_CALLS  16
 #else
 	#define IPC_MAX_ASYNC_CALLS  4000
Index: uspace/drv/uhci/main.c
===================================================================
--- uspace/drv/uhci/main.c	(revision bbc28becd7d06ac1598f9fe6c3a639eb10017af9)
+++ uspace/drv/uhci/main.c	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -77,8 +77,6 @@
 	/*
 	 * We need to announce the presence of our root hub.
-	 * Commented out until the problem which causes the whole task to
-	 * block is solved.
 	 */
-	//usb_hcd_add_root_hub(device);
+	usb_hcd_add_root_hub(device);
 
 	return EOK;
Index: uspace/drv/usbkbd/usbkbd.ma
===================================================================
--- uspace/drv/usbkbd/usbkbd.ma	(revision bbc28becd7d06ac1598f9fe6c3a639eb10017af9)
+++ uspace/drv/usbkbd/usbkbd.ma	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -1,1 +1,2 @@
 10 usb&class=hid
+10 usb&hid
Index: uspace/lib/drv/generic/remote_usbhc.c
===================================================================
--- uspace/lib/drv/generic/remote_usbhc.c	(revision bbc28becd7d06ac1598f9fe6c3a639eb10017af9)
+++ uspace/lib/drv/generic/remote_usbhc.c	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -42,14 +42,28 @@
 #define USB_MAX_PAYLOAD_SIZE 1020
 
+static void remote_usbhc_get_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
 static void remote_usbhc_get_buffer(device_t *, void *, ipc_callid_t, ipc_call_t *);
 static void remote_usbhc_interrupt_out(device_t *, void *, ipc_callid_t, ipc_call_t *);
 static void remote_usbhc_interrupt_in(device_t *, void *, ipc_callid_t, ipc_call_t *);
-//static void remote_usb(device_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_control_write_setup(device_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_control_write_data(device_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_control_write_status(device_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_control_read_setup(device_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_control_read_data(device_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_control_read_status(device_t *, void *, ipc_callid_t, ipc_call_t *);
+//static void remote_usbhc(device_t *, void *, ipc_callid_t, ipc_call_t *);
 
 /** Remote USB interface operations. */
 static remote_iface_func_ptr_t remote_usbhc_iface_ops [] = {
-	&remote_usbhc_get_buffer,
-	&remote_usbhc_interrupt_out,
-	&remote_usbhc_interrupt_in
+	remote_usbhc_get_address,
+	remote_usbhc_get_buffer,
+	remote_usbhc_interrupt_out,
+	remote_usbhc_interrupt_in,
+	remote_usbhc_control_write_setup,
+	remote_usbhc_control_write_data,
+	remote_usbhc_control_write_status,
+	remote_usbhc_control_read_setup,
+	remote_usbhc_control_read_data,
+	remote_usbhc_control_read_status
 };
 
@@ -68,4 +82,24 @@
 } async_transaction_t;
 
+void remote_usbhc_get_address(device_t *device, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+
+	if (!usb_iface->tell_address) {
+		ipc_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	devman_handle_t handle = IPC_GET_ARG1(*call);
+
+	usb_address_t address;
+	int rc = usb_iface->tell_address(device, handle, &address);
+	if (rc != EOK) {
+		ipc_answer_0(callid, rc);
+	} else {
+		ipc_answer_1(callid, EOK, address);
+	}
+}
 
 void remote_usbhc_get_buffer(device_t *device, void *iface,
@@ -125,8 +159,19 @@
 }
 
-void remote_usbhc_interrupt_out(device_t *device, void *iface,
-	    ipc_callid_t callid, ipc_call_t *call)
-{
-	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+/** Process an outgoing transfer (both OUT and SETUP).
+ *
+ * @param device Target device.
+ * @param callid Initiating caller.
+ * @param call Initiating call.
+ * @param transfer_func Transfer function (might be NULL).
+ */
+static void remote_usbhc_out_transfer(device_t *device,
+    ipc_callid_t callid, ipc_call_t *call,
+    usbhc_iface_transfer_out_t transfer_func)
+{
+	if (!transfer_func) {
+		ipc_answer_0(callid, ENOTSUP);
+		return;
+	}
 
 	size_t expected_len = IPC_GET_ARG3(*call);
@@ -149,27 +194,36 @@
 	}
 
-	if (!usb_iface->interrupt_out) {
-		ipc_answer_0(callid, ENOTSUP);
-		return;
-	}
-
 	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
 	trans->caller = callid;
-	trans->buffer = NULL;
-	trans->size = 0;
-
-	int rc = usb_iface->interrupt_out(device, target, buffer, len,
+	trans->buffer = buffer;
+	trans->size = len;
+
+	int rc = transfer_func(device, target, buffer, len,
 	    callback_out, trans);
 
 	if (rc != EOK) {
 		ipc_answer_0(callid, rc);
+		if (buffer != NULL) {
+			free(buffer);
+		}
 		free(trans);
 	}
 }
 
-void remote_usbhc_interrupt_in(device_t *device, void *iface,
-	    ipc_callid_t callid, ipc_call_t *call)
-{
-	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+/** Process an incoming transfer.
+ *
+ * @param device Target device.
+ * @param callid Initiating caller.
+ * @param call Initiating call.
+ * @param transfer_func Transfer function (might be NULL).
+ */
+static void remote_usbhc_in_transfer(device_t *device,
+    ipc_callid_t callid, ipc_call_t *call,
+    usbhc_iface_transfer_in_t transfer_func)
+{
+	if (!transfer_func) {
+		ipc_answer_0(callid, ENOTSUP);
+		return;
+	}
 
 	size_t len = IPC_GET_ARG3(*call);
@@ -179,9 +233,4 @@
 	};
 
-	if (!usb_iface->interrupt_in) {
-		ipc_answer_0(callid, ENOTSUP);
-		return;
-	}
-
 	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
 	trans->caller = callid;
@@ -189,5 +238,5 @@
 	trans->size = len;
 
-	int rc = usb_iface->interrupt_in(device, target, trans->buffer, len,
+	int rc = transfer_func(device, target, trans->buffer, len,
 	    callback_in, trans);
 
@@ -199,4 +248,153 @@
 }
 
+/** Process status part of control transfer.
+ *
+ * @param device Target device.
+ * @param callid Initiating caller.
+ * @param call Initiating call.
+ * @param direction Transfer direction (read ~ in, write ~ out).
+ * @param transfer_in_func Transfer function for control read (might be NULL).
+ * @param transfer_out_func Transfer function for control write (might be NULL).
+ */
+static void remote_usbhc_status_transfer(device_t *device,
+    ipc_callid_t callid, ipc_call_t *call,
+    usb_direction_t direction,
+    int (*transfer_in_func)(device_t *, usb_target_t,
+        usbhc_iface_transfer_in_callback_t, void *),
+    int (*transfer_out_func)(device_t *, usb_target_t,
+        usbhc_iface_transfer_out_callback_t, void *))
+{
+	switch (direction) {
+		case USB_DIRECTION_IN:
+			if (!transfer_in_func) {
+				ipc_answer_0(callid, ENOTSUP);
+				return;
+			}
+			break;
+		case USB_DIRECTION_OUT:
+			if (!transfer_out_func) {
+				ipc_answer_0(callid, ENOTSUP);
+				return;
+			}
+			break;
+		default:
+			assert(false && "unreachable code");
+			break;
+	}
+
+	usb_target_t target = {
+		.address = IPC_GET_ARG1(*call),
+		.endpoint = IPC_GET_ARG2(*call)
+	};
+
+	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
+	trans->caller = callid;
+	trans->buffer = NULL;
+	trans->size = 0;
+
+	int rc;
+	switch (direction) {
+		case USB_DIRECTION_IN:
+			rc = transfer_in_func(device, target,
+			    callback_in, trans);
+			break;
+		case USB_DIRECTION_OUT:
+			rc = transfer_out_func(device, target,
+			    callback_out, trans);
+			break;
+		default:
+			assert(false && "unreachable code");
+			break;
+	}
+
+	if (rc != EOK) {
+		ipc_answer_0(callid, rc);
+		free(trans);
+	}
+	return;
+}
+
+
+void remote_usbhc_interrupt_out(device_t *device, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_out_transfer(device, callid, call,
+	    usb_iface->interrupt_out);
+}
+
+void remote_usbhc_interrupt_in(device_t *device, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_in_transfer(device, callid, call,
+	    usb_iface->interrupt_in);
+}
+
+void remote_usbhc_control_write_setup(device_t *device, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_out_transfer(device, callid, call,
+	    usb_iface->control_write_setup);
+}
+
+void remote_usbhc_control_write_data(device_t *device, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_out_transfer(device, callid, call,
+	    usb_iface->control_write_data);
+}
+
+void remote_usbhc_control_write_status(device_t *device, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_status_transfer(device, callid, call,
+	    USB_DIRECTION_IN, usb_iface->control_write_status, NULL);
+}
+
+void remote_usbhc_control_read_setup(device_t *device, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_out_transfer(device, callid, call,
+	    usb_iface->control_read_setup);
+}
+
+void remote_usbhc_control_read_data(device_t *device, void *iface,
+	    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_in_transfer(device, callid, call,
+	    usb_iface->control_read_data);
+}
+
+void remote_usbhc_control_read_status(device_t *device, void *iface,
+	    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_status_transfer(device, callid, call,
+	    USB_DIRECTION_OUT, NULL, usb_iface->control_read_status);
+}
+
+
 
 /**
Index: uspace/lib/drv/include/usbhc_iface.h
===================================================================
--- uspace/lib/drv/include/usbhc_iface.h	(revision bbc28becd7d06ac1598f9fe6c3a639eb10017af9)
+++ uspace/lib/drv/include/usbhc_iface.h	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -92,4 +92,15 @@
  */
 typedef enum {
+	/** Tell USB address assigned to device.
+	 * Parameters:
+	 * - devman handle id
+	 * Answer:
+	 * - EINVAL - unknown handle or handle not managed by this driver
+	 * - ENOTSUP - operation not supported by HC (shall not happen)
+	 * - arbitrary error code if returned by remote implementation
+	 * - EOK - handle found, first parameter contains the USB address
+	 */
+	IPC_M_USBHC_GET_ADDRESS,
+
 	/** Asks for data buffer.
 	 * See explanation at usb_iface_funcs_t.
@@ -155,12 +166,34 @@
     usb_transaction_outcome_t, size_t, void *);
 
+
+/** Out transfer processing function prototype. */
+typedef int (*usbhc_iface_transfer_out_t)(device_t *, usb_target_t,
+    void *, size_t,
+    usbhc_iface_transfer_out_callback_t, void *);
+
+/** Setup transfer processing function prototype. */
+typedef usbhc_iface_transfer_out_t usbhc_iface_transfer_setup_t;
+
+/** In transfer processing function prototype. */
+typedef int (*usbhc_iface_transfer_in_t)(device_t *, usb_target_t,
+    void *, size_t,
+    usbhc_iface_transfer_in_callback_t, void *);
+
 /** USB devices communication interface. */
 typedef struct {
-	int (*interrupt_out)(device_t *, usb_target_t,
-	    void *, size_t,
+	int (*tell_address)(device_t *, devman_handle_t, usb_address_t *);
+
+	usbhc_iface_transfer_out_t interrupt_out;
+	usbhc_iface_transfer_in_t interrupt_in;
+
+	usbhc_iface_transfer_setup_t control_write_setup;
+	usbhc_iface_transfer_out_t control_write_data;
+	int (*control_write_status)(device_t *, usb_target_t,
+	    usbhc_iface_transfer_in_callback_t, void *);
+
+	usbhc_iface_transfer_setup_t control_read_setup;
+	usbhc_iface_transfer_in_t control_read_data;
+	int (*control_read_status)(device_t *, usb_target_t,
 	    usbhc_iface_transfer_out_callback_t, void *);
-	int (*interrupt_in)(device_t *, usb_target_t,
-	    void *, size_t,
-	    usbhc_iface_transfer_in_callback_t, void *);
 } usbhc_iface_t;
 
Index: uspace/lib/usb/Makefile
===================================================================
--- uspace/lib/usb/Makefile	(revision bbc28becd7d06ac1598f9fe6c3a639eb10017af9)
+++ uspace/lib/usb/Makefile	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -34,5 +34,8 @@
 SOURCES = \
 	src/hcdhubd.c \
+	src/hcdrv.c \
+	src/hubdrv.c \
 	src/localdrv.c \
+	src/remotedrv.c \
 	src/usb.c \
 	src/usbdrv.c
Index: uspace/lib/usb/include/usb/hcdhubd.h
===================================================================
--- uspace/lib/usb/include/usb/hcdhubd.h	(revision bbc28becd7d06ac1598f9fe6c3a639eb10017af9)
+++ uspace/lib/usb/include/usb/hcdhubd.h	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -37,4 +37,5 @@
 
 #include <adt/list.h>
+#include <bool.h>
 #include <driver.h>
 #include <usb/usb.h>
@@ -175,4 +176,5 @@
 int usb_hc_async_wait_for(usb_handle_t);
 
+int usb_hc_add_child_device(device_t *, const char *, const char *, bool);
 
 #endif
Index: uspace/lib/usb/include/usb/usbdrv.h
===================================================================
--- uspace/lib/usb/include/usb/usbdrv.h	(revision bbc28becd7d06ac1598f9fe6c3a639eb10017af9)
+++ uspace/lib/usb/include/usb/usbdrv.h	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -48,4 +48,18 @@
     void *, size_t, size_t *, usb_handle_t *);
 
+int usb_drv_async_control_write_setup(int, usb_target_t,
+    void *, size_t, usb_handle_t *);
+int usb_drv_async_control_write_data(int, usb_target_t,
+    void *, size_t, usb_handle_t *);
+int usb_drv_async_control_write_status(int, usb_target_t,
+    usb_handle_t *);
+
+int usb_drv_async_control_read_setup(int, usb_target_t,
+    void *, size_t, usb_handle_t *);
+int usb_drv_async_control_read_data(int, usb_target_t,
+    void *, size_t, size_t *, usb_handle_t *);
+int usb_drv_async_control_read_status(int, usb_target_t,
+    usb_handle_t *);
+
 int usb_drv_async_wait_for(usb_handle_t);
 
Index: uspace/lib/usb/src/hcdhubd.c
===================================================================
--- uspace/lib/usb/src/hcdhubd.c	(revision bbc28becd7d06ac1598f9fe6c3a639eb10017af9)
+++ uspace/lib/usb/src/hcdhubd.c	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -31,5 +31,5 @@
  */
 /** @file
- * @brief HC driver and hub driver (implementation).
+ * @brief Common stuff for both HC driver and hub driver.
  */
 #include <usb/hcdhubd.h>
@@ -40,124 +40,15 @@
 #include <bool.h>
 #include <errno.h>
+#include <str_error.h>
 #include <usb/classes/hub.h>
 
-#define USB_HUB_DEVICE_NAME "usbhub"
-
-#define USB_KBD_DEVICE_NAME "hid"
-
-
-
-
-/** List of handled host controllers. */
-static LIST_INITIALIZE(hc_list);
-
-/** Our HC driver. */
-static usb_hc_driver_t *hc_driver = NULL;
-
-static usbhc_iface_t usb_interface = {
-	.interrupt_out = NULL,
-	.interrupt_in = NULL
-};
-
-static device_ops_t usb_device_ops = {
-	.interfaces[USBHC_DEV_IFACE] = &usb_interface
-};
-
-size_t USB_HUB_MAX_DESCRIPTOR_SIZE = 71;
-
-uint8_t USB_HUB_DESCRIPTOR_TYPE = 0x29;
-
-//*********************************************
-//
-//  various utils
-//
-//*********************************************
-
-void * usb_serialize_hub_descriptor(usb_hub_descriptor_t * descriptor) {
-	//base size
-	size_t size = 7;
-	//variable size according to port count
-	size_t var_size = descriptor->ports_count / 8 + ((descriptor->ports_count % 8 > 0) ? 1 : 0);
-	size += 2 * var_size;
-	uint8_t * result = (uint8_t*) malloc(size);
-	//size
-	result[0] = size;
-	//descriptor type
-	result[1] = USB_DESCTYPE_HUB;
-	result[2] = descriptor->ports_count;
-	/// @fixme handling of endianness??
-	result[3] = descriptor->hub_characteristics / 256;
-	result[4] = descriptor->hub_characteristics % 256;
-	result[5] = descriptor->pwr_on_2_good_time;
-	result[6] = descriptor->current_requirement;
-
-	size_t i;
-	for (i = 0; i < var_size; ++i) {
-		result[7 + i] = descriptor->devices_removable[i];
-	}
-	for (i = 0; i < var_size; ++i) {
-		result[7 + var_size + i] = 255;
-	}
-	return result;
-}
-
-usb_hub_descriptor_t * usb_deserialize_hub_desriptor(void * serialized_descriptor) {
-	uint8_t * sdescriptor = (uint8_t*) serialized_descriptor;
-	if (sdescriptor[1] != USB_DESCTYPE_HUB) return NULL;
-	usb_hub_descriptor_t * result = (usb_hub_descriptor_t*) malloc(sizeof (usb_hub_descriptor_t));
-	//uint8_t size = sdescriptor[0];
-	result->ports_count = sdescriptor[2];
-	/// @fixme handling of endianness??
-	result->hub_characteristics = sdescriptor[4] + 256 * sdescriptor[3];
-	result->pwr_on_2_good_time = sdescriptor[5];
-	result->current_requirement = sdescriptor[6];
-	size_t var_size = result->ports_count / 8 + ((result->ports_count % 8 > 0) ? 1 : 0);
-	result->devices_removable = (uint8_t*) malloc(var_size);
-
-	size_t i;
-	for (i = 0; i < var_size; ++i) {
-		result->devices_removable[i] = sdescriptor[7 + i];
-	}
-	return result;
-}
-
-
-//*********************************************
-//
-//  hub driver code
-//
-//*********************************************
-
-static void set_hub_address(usb_hc_device_t *hc, usb_address_t address);
-
-usb_hcd_hub_info_t * usb_create_hub_info(device_t * device) {
-	usb_hcd_hub_info_t* result = (usb_hcd_hub_info_t*) malloc(sizeof (usb_hcd_hub_info_t));
-	//get parent device
-	/// @TODO this code is not correct
-	device_t * my_hcd = device;
-	while (my_hcd->parent)
-		my_hcd = my_hcd->parent;
-	//dev->
-	printf("%s: owner hcd found: %s\n", hc_driver->name, my_hcd->name);
-	//we add the hub into the first hc
-	//link_t *link_hc = hc_list.next;
-	//usb_hc_device_t *hc = list_get_instance(link_hc,
-	//		usb_hc_device_t, link);
-	//must get generic device info
-
-
-	return result;
-}
+#include "hcdhubd_private.h"
 
 /** Callback when new device is detected and must be handled by this driver.
  *
  * @param dev New device.
- * @return Error code.hub added, hurrah!\n"
+ * @return Error code.
  */
 static int add_device(device_t *dev) {
-	/*
-	 * FIXME: use some magic to determine whether hub or another HC
-	 * was connected.
-	 */
 	bool is_hc = str_cmp(dev->name, USB_HUB_DEVICE_NAME) != 0;
 	printf("%s: add_device(name=\"%s\")\n", hc_driver->name, dev->name);
@@ -167,205 +58,12 @@
 		 * We are the HC itself.
 		 */
-		usb_hc_device_t *hc_dev = malloc(sizeof (usb_hc_device_t));
-		list_initialize(&hc_dev->link);
-		hc_dev->transfer_ops = NULL;
-
-		hc_dev->generic = dev;
-		dev->ops = &usb_device_ops;
-		hc_dev->generic->driver_data = hc_dev;
-
-		int rc = hc_driver->add_hc(hc_dev);
-		if (rc != EOK) {
-			free(hc_dev);
-			return rc;
-		}
-
+		return usb_add_hc_device(dev);
+	} else {
 		/*
-		 * FIXME: The following line causes devman to hang.
-		 * Will investigate later why.
-		 */
-		// add_device_to_class(dev, "usbhc");
-
-		list_append(&hc_dev->link, &hc_list);
-
-		//add keyboard
-		/// @TODO this is not correct code
-		
-		/*
-		 * Announce presence of child device.
-		 */
-		device_t *kbd = NULL;
-		match_id_t *match_id = NULL;
-
-		kbd = create_device();
-		if (kbd == NULL) {
-			printf("ERROR: enomem\n");
-		}
-		kbd->name = USB_KBD_DEVICE_NAME;
-
-		match_id = create_match_id();
-		if (match_id == NULL) {
-			printf("ERROR: enomem\n");
-		}
-
-		char *id;
-		rc = asprintf(&id, USB_KBD_DEVICE_NAME);
-		if (rc <= 0) {
-			printf("ERROR: enomem\n");
-			return rc;
-		}
-
-		match_id->id = id;
-		match_id->score = 30;
-
-		add_match_id(&kbd->match_ids, match_id);
-
-		rc = child_device_register(kbd, dev);
-		if (rc != EOK) {
-			printf("ERROR: cannot register kbd\n");
-			return rc;
-		}
-
-		printf("%s: registered root hub\n", dev->name);
-		return EOK;
-
-
-
-	} else {
-		usb_hc_device_t *hc = list_get_instance(hc_list.next, usb_hc_device_t, link);
-		set_hub_address(hc, 5);
-
-		/*
-		 * We are some (probably deeply nested) hub.
+		 * We are some (maybe deeply nested) hub.
 		 * Thus, assign our own operations and explore already
 		 * connected devices.
 		 */
-		//insert hub into list
-		//find owner hcd
-		device_t * my_hcd = dev;
-		while (my_hcd->parent)
-			my_hcd = my_hcd->parent;
-		//dev->
-		printf("%s: owner hcd found: %s\n", hc_driver->name, my_hcd->name);
-		my_hcd = dev;
-		while (my_hcd->parent)
-			my_hcd = my_hcd->parent;
-		//dev->
-
-		printf("%s: owner hcd found: %s\n", hc_driver->name, my_hcd->name);
-		
-		//create the hub structure
-		usb_hcd_hub_info_t * hub_info = usb_create_hub_info(dev);
-
-
-		//append into the list
-		//we add the hub into the first hc
-		list_append(&hub_info->link, &hc->hubs);
-
-
-
-		return EOK;
-		//return ENOTSUP;
-	}
-}
-
-/** Sample usage of usb_hc_async functions.
- * This function sets hub address using standard SET_ADDRESS request.
- *
- * @warning This function shall be removed once you are familiar with
- * the usb_hc_ API.
- *
- * @param hc Host controller the hub belongs to.
- * @param address New hub address.
- */
-static void set_hub_address(usb_hc_device_t *hc, usb_address_t address) {
-	printf("%s: setting hub address to %d\n", hc->generic->name, address);
-	usb_target_t target = {0, 0};
-	usb_handle_t handle;
-	int rc;
-
-	usb_device_request_setup_packet_t setup_packet = {
-		.request_type = 0,
-		.request = USB_DEVREQ_SET_ADDRESS,
-		.index = 0,
-		.length = 0,
-	};
-	setup_packet.value = address;
-
-	rc = usb_hc_async_control_write_setup(hc, target,
-			&setup_packet, sizeof (setup_packet), &handle);
-	if (rc != EOK) {
-		return;
-	}
-
-	rc = usb_hc_async_wait_for(handle);
-	if (rc != EOK) {
-		return;
-	}
-
-	rc = usb_hc_async_control_write_status(hc, target, &handle);
-	if (rc != EOK) {
-		return;
-	}
-
-	rc = usb_hc_async_wait_for(handle);
-	if (rc != EOK) {
-		return;
-	}
-
-	printf("%s: hub address changed\n", hc->generic->name);
-}
-
-/** Check changes on all known hubs.
- */
-static void check_hub_changes(void) {
-	/*
-	 * Iterate through all HCs.
-	 */
-	link_t *link_hc;
-	for (link_hc = hc_list.next;
-			link_hc != &hc_list;
-			link_hc = link_hc->next) {
-		usb_hc_device_t *hc = list_get_instance(link_hc,
-				usb_hc_device_t, link);
-		/*
-		 * Iterate through all their hubs.
-		 */
-		link_t *link_hub;
-		for (link_hub = hc->hubs.next;
-				link_hub != &hc->hubs;
-				link_hub = link_hub->next) {
-			usb_hcd_hub_info_t *hub = list_get_instance(link_hub,
-					usb_hcd_hub_info_t, link);
-
-			/*
-			 * Check status change pipe of this hub.
-			 */
-			usb_target_t target = {
-				.address = hub->device->address,
-				.endpoint = 1
-			};
-
-			// FIXME: count properly
-			size_t byte_length = (hub->port_count / 8) + 1;
-
-			void *change_bitmap = malloc(byte_length);
-			size_t actual_size;
-			usb_handle_t handle;
-
-			/*
-			 * Send the request.
-			 * FIXME: check returned value for possible errors
-			 */
-			usb_hc_async_interrupt_in(hc, target,
-					change_bitmap, byte_length, &actual_size,
-					&handle);
-
-			usb_hc_async_wait_for(handle);
-
-			/*
-			 * TODO: handle the changes.
-			 */
-		}
+		return usb_add_hub_device(dev);
 	}
 }
@@ -391,11 +89,4 @@
 	hc_driver = hc;
 	hc_driver_generic.name = hc->name;
-
-	/*
-	 * Launch here fibril that will periodically check all
-	 * attached hubs for status change.
-	 * WARN: This call will effectively do nothing.
-	 */
-	check_hub_changes();
 
 	/*
@@ -414,19 +105,44 @@
  * @return Error code.
  */
-int usb_hcd_add_root_hub(usb_hc_device_t *dev) {
+int usb_hcd_add_root_hub(usb_hc_device_t *dev)
+{
+	char *id;
+	int rc = asprintf(&id, "usb&hc=%s&hub", hc_driver->name);
+	if (rc <= 0) {
+		return rc;
+	}
+
+	rc = usb_hc_add_child_device(dev->generic, USB_HUB_DEVICE_NAME, id, true);
+	if (rc != EOK) {
+		free(id);
+	}
+
+	return rc;
+}
+
+/** Info about child device. */
+struct child_device_info {
+	device_t *parent;
+	const char *name;
+	const char *match_id;
+};
+
+/** Adds a child device fibril worker. */
+static int fibril_add_child_device(void *arg)
+{
+	struct child_device_info *child_info
+	    = (struct child_device_info *) arg;
 	int rc;
 
-	/*
-	 * Announce presence of child device.
-	 */
-	device_t *hub = NULL;
+	async_usleep(1000);
+
+	device_t *child = create_device();
 	match_id_t *match_id = NULL;
 
-	hub = create_device();
-	if (hub == NULL) {
+	if (child == NULL) {
 		rc = ENOMEM;
 		goto failure;
 	}
-	hub->name = USB_HUB_DEVICE_NAME;
+	child->name = child_info->name;
 
 	match_id = create_match_id();
@@ -435,33 +151,90 @@
 		goto failure;
 	}
-
-	char *id;
-	rc = asprintf(&id, "usb&hc=%s&hub", dev->generic->name);
-	if (rc <= 0) {
-		rc = ENOMEM;
-		goto failure;
-	}
-
-	match_id->id = id;
-	match_id->score = 30;
-
-	add_match_id(&hub->match_ids, match_id);
-
-	rc = child_device_register(hub, dev->generic);
+	match_id->id = child_info->match_id;
+	match_id->score = 10;
+	add_match_id(&child->match_ids, match_id);
+
+	printf("%s: adding child device `%s' with match \"%s\"\n",
+	    hc_driver->name, child->name, match_id->id);
+	rc = child_device_register(child, child_info->parent);
+	printf("%s: child device `%s' registration: %s\n",
+	    hc_driver->name, child->name, str_error(rc));
+
 	if (rc != EOK) {
 		goto failure;
 	}
 
-	printf("%s: registered root hub\n", dev->generic->name);
+	goto leave;
+
+failure:
+	if (child != NULL) {
+		child->name = NULL;
+		delete_device(child);
+	}
+
+	if (match_id != NULL) {
+		match_id->id = NULL;
+		delete_match_id(match_id);
+	}
+
+leave:
+	free(arg);
 	return EOK;
-
-failure:
-	if (hub != NULL) {
-		hub->name = NULL;
-		delete_device(hub);
-	}
-	delete_match_id(match_id);
-
-	return rc;
+}
+
+/** Adds a child.
+ * Due to deadlock in devman when parent registers child that oughts to be
+ * driven by the same task, the child adding is done in separate fibril.
+ * Not optimal, but it works.
+ * Update: not under all circumstances the new fibril is successful either.
+ * Thus the last parameter to let the caller choose.
+ *
+ * @param parent Parent device.
+ * @param name Device name.
+ * @param match_id Match id.
+ * @param create_fibril Whether to run the addition in new fibril.
+ * @return Error code.
+ */
+int usb_hc_add_child_device(device_t *parent, const char *name,
+    const char *match_id, bool create_fibril)
+{
+	printf("%s: about to add child device `%s' (%s)\n", hc_driver->name,
+	    name, match_id);
+
+	/*
+	 * Seems that creating fibril which postpones the action
+	 * is the best solution.
+	 */
+	create_fibril = true;
+
+	struct child_device_info *child_info
+	    = malloc(sizeof(struct child_device_info));
+
+	child_info->parent = parent;
+	child_info->name = name;
+	child_info->match_id = match_id;
+
+	if (create_fibril) {
+		fid_t fibril = fibril_create(fibril_add_child_device, child_info);
+		if (!fibril) {
+			return ENOMEM;
+		}
+		fibril_add_ready(fibril);
+	} else {
+		fibril_add_child_device(child_info);
+	}
+
+	return EOK;
+}
+
+/** Tell USB address of given device.
+ *
+ * @param handle Devman handle of the device.
+ * @return USB device address or error code.
+ */
+usb_address_t usb_get_address_by_handle(devman_handle_t handle)
+{
+	/* TODO: search list of attached devices. */
+	return ENOENT;
 }
 
Index: uspace/lib/usb/src/hcdhubd_private.h
===================================================================
--- uspace/lib/usb/src/hcdhubd_private.h	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
+++ uspace/lib/usb/src/hcdhubd_private.h	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010 Vojtech Horky
+ * 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 libusb usb
+ * @{
+ */
+/** @file
+ * @brief Common definitions for both HC driver and hub driver.
+ */
+#ifndef LIBUSB_HCDHUBD_PRIVATE_H_
+#define LIBUSB_HCDHUBD_PRIVATE_H_
+
+#define USB_HUB_DEVICE_NAME "usbhub"
+#define USB_KBD_DEVICE_NAME "hid"
+
+extern link_t hc_list;
+extern usb_hc_driver_t *hc_driver;
+
+extern usbhc_iface_t usbhc_interface;
+
+usb_address_t usb_get_address_by_handle(devman_handle_t);
+int usb_add_hc_device(device_t *);
+int usb_add_hub_device(device_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/usb/src/hcdrv.c
===================================================================
--- uspace/lib/usb/src/hcdrv.c	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
+++ uspace/lib/usb/src/hcdrv.c	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2010 Vojtech Horky
+ * 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 libusb usb
+ * @{
+ */
+/** @file
+ * @brief HC driver.
+ */
+#include <usb/hcdhubd.h>
+#include <usb/devreq.h>
+#include <usbhc_iface.h>
+#include <usb/descriptor.h>
+#include <driver.h>
+#include <bool.h>
+#include <errno.h>
+#include <usb/classes/hub.h>
+
+#include "hcdhubd_private.h"
+
+/** List of handled host controllers. */
+LIST_INITIALIZE(hc_list);
+
+/** Our HC driver. */
+usb_hc_driver_t *hc_driver = NULL;
+
+static device_ops_t usb_device_ops = {
+	.interfaces[USBHC_DEV_IFACE] = &usbhc_interface
+};
+
+static usb_hc_device_t *usb_hc_device_create(device_t *dev) {
+	usb_hc_device_t *hc_dev = malloc(sizeof (usb_hc_device_t));
+
+	list_initialize(&hc_dev->link);
+	list_initialize(&hc_dev->hubs);
+	list_initialize(&hc_dev->attached_devices);
+	hc_dev->transfer_ops = NULL;
+
+	hc_dev->generic = dev;
+	dev->ops = &usb_device_ops;
+	hc_dev->generic->driver_data = hc_dev;
+
+	return hc_dev;
+}
+
+int usb_add_hc_device(device_t *dev)
+{
+	usb_hc_device_t *hc_dev = usb_hc_device_create(dev);
+
+	int rc = hc_driver->add_hc(hc_dev);
+	if (rc != EOK) {
+		free(hc_dev);
+		return rc;
+	}
+
+	/*
+	 * FIXME: The following line causes devman to hang.
+	 * Will investigate later why.
+	 */
+	// add_device_to_class(dev, "usbhc");
+
+	list_append(&hc_dev->link, &hc_list);
+
+	/*
+	 * FIXME: the following is a workaround to force loading of USB
+	 * keyboard driver.
+	 * Will be removed as soon as the hub driver is completed and
+	 * can detect connected devices.
+	 */
+	printf("%s: trying to add USB HID child device...\n", hc_driver->name);
+	rc = usb_hc_add_child_device(dev, USB_KBD_DEVICE_NAME, "usb&hid", false);
+	if (rc != EOK) {
+		printf("%s: adding USB HID child failed...\n", hc_driver->name);
+	}
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/usb/src/hubdrv.c
===================================================================
--- uspace/lib/usb/src/hubdrv.c	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
+++ uspace/lib/usb/src/hubdrv.c	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2010 Matus Dekanek
+ * 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 libusb usb
+ * @{
+ */
+/** @file
+ * @brief Hub driver.
+ */
+#include <usb/hcdhubd.h>
+#include <usb/devreq.h>
+#include <usbhc_iface.h>
+#include <usb/descriptor.h>
+#include <driver.h>
+#include <bool.h>
+#include <errno.h>
+#include <usb/classes/hub.h>
+#include "hcdhubd_private.h"
+
+static void check_hub_changes(void);
+
+size_t USB_HUB_MAX_DESCRIPTOR_SIZE = 71;
+
+//*********************************************
+//
+//  various utils
+//
+//*********************************************
+
+void * usb_serialize_hub_descriptor(usb_hub_descriptor_t * descriptor) {
+	//base size
+	size_t size = 7;
+	//variable size according to port count
+	size_t var_size = descriptor->ports_count / 8 + ((descriptor->ports_count % 8 > 0) ? 1 : 0);
+	size += 2 * var_size;
+	uint8_t * result = (uint8_t*) malloc(size);
+	//size
+	result[0] = size;
+	//descriptor type
+	result[1] = USB_DESCTYPE_HUB;
+	result[2] = descriptor->ports_count;
+	/// @fixme handling of endianness??
+	result[3] = descriptor->hub_characteristics / 256;
+	result[4] = descriptor->hub_characteristics % 256;
+	result[5] = descriptor->pwr_on_2_good_time;
+	result[6] = descriptor->current_requirement;
+
+	size_t i;
+	for (i = 0; i < var_size; ++i) {
+		result[7 + i] = descriptor->devices_removable[i];
+	}
+	for (i = 0; i < var_size; ++i) {
+		result[7 + var_size + i] = 255;
+	}
+	return result;
+}
+
+usb_hub_descriptor_t * usb_deserialize_hub_desriptor(void * serialized_descriptor) {
+	uint8_t * sdescriptor = (uint8_t*) serialized_descriptor;
+	if (sdescriptor[1] != USB_DESCTYPE_HUB) return NULL;
+	usb_hub_descriptor_t * result = (usb_hub_descriptor_t*) malloc(sizeof (usb_hub_descriptor_t));
+	//uint8_t size = sdescriptor[0];
+	result->ports_count = sdescriptor[2];
+	/// @fixme handling of endianness??
+	result->hub_characteristics = sdescriptor[4] + 256 * sdescriptor[3];
+	result->pwr_on_2_good_time = sdescriptor[5];
+	result->current_requirement = sdescriptor[6];
+	size_t var_size = result->ports_count / 8 + ((result->ports_count % 8 > 0) ? 1 : 0);
+	result->devices_removable = (uint8_t*) malloc(var_size);
+
+	size_t i;
+	for (i = 0; i < var_size; ++i) {
+		result->devices_removable[i] = sdescriptor[7 + i];
+	}
+	return result;
+}
+
+
+//*********************************************
+//
+//  hub driver code
+//
+//*********************************************
+
+static void set_hub_address(usb_hc_device_t *hc, usb_address_t address);
+
+usb_hcd_hub_info_t * usb_create_hub_info(device_t * device) {
+	usb_hcd_hub_info_t* result = (usb_hcd_hub_info_t*) malloc(sizeof (usb_hcd_hub_info_t));
+	//get parent device
+	/// @TODO this code is not correct
+	device_t * my_hcd = device;
+	while (my_hcd->parent)
+		my_hcd = my_hcd->parent;
+	//dev->
+	printf("%s: owner hcd found: %s\n", hc_driver->name, my_hcd->name);
+	//we add the hub into the first hc
+	//link_t *link_hc = hc_list.next;
+	//usb_hc_device_t *hc = list_get_instance(link_hc,
+	//		usb_hc_device_t, link);
+	//must get generic device info
+
+
+	return result;
+}
+
+/** Callback when new hub device is detected.
+ *
+ * @param dev New device.
+ * @return Error code.
+ */
+int usb_add_hub_device(device_t *dev) {
+	usb_hc_device_t *hc = list_get_instance(hc_list.next, usb_hc_device_t, link);
+	set_hub_address(hc, 5);
+
+	check_hub_changes();
+
+	/*
+	 * We are some (probably deeply nested) hub.
+	 * Thus, assign our own operations and explore already
+	 * connected devices.
+	 */
+	//insert hub into list
+	//find owner hcd
+	device_t * my_hcd = dev;
+	while (my_hcd->parent)
+		my_hcd = my_hcd->parent;
+	//dev->
+	printf("%s: owner hcd found: %s\n", hc_driver->name, my_hcd->name);
+	my_hcd = dev;
+	while (my_hcd->parent)
+		my_hcd = my_hcd->parent;
+	//dev->
+
+	printf("%s: owner hcd found: %s\n", hc_driver->name, my_hcd->name);
+
+	//create the hub structure
+	usb_hcd_hub_info_t * hub_info = usb_create_hub_info(dev);
+
+
+	//append into the list
+	//we add the hub into the first hc
+	list_append(&hub_info->link, &hc->hubs);
+
+
+
+	return EOK;
+	//return ENOTSUP;
+}
+
+/** Sample usage of usb_hc_async functions.
+ * This function sets hub address using standard SET_ADDRESS request.
+ *
+ * @warning This function shall be removed once you are familiar with
+ * the usb_hc_ API.
+ *
+ * @param hc Host controller the hub belongs to.
+ * @param address New hub address.
+ */
+static void set_hub_address(usb_hc_device_t *hc, usb_address_t address) {
+	printf("%s: setting hub address to %d\n", hc->generic->name, address);
+	usb_target_t target = {0, 0};
+	usb_handle_t handle;
+	int rc;
+
+	usb_device_request_setup_packet_t setup_packet = {
+		.request_type = 0,
+		.request = USB_DEVREQ_SET_ADDRESS,
+		.index = 0,
+		.length = 0,
+	};
+	setup_packet.value = address;
+
+	rc = usb_hc_async_control_write_setup(hc, target,
+			&setup_packet, sizeof (setup_packet), &handle);
+	if (rc != EOK) {
+		return;
+	}
+
+	rc = usb_hc_async_wait_for(handle);
+	if (rc != EOK) {
+		return;
+	}
+
+	rc = usb_hc_async_control_write_status(hc, target, &handle);
+	if (rc != EOK) {
+		return;
+	}
+
+	rc = usb_hc_async_wait_for(handle);
+	if (rc != EOK) {
+		return;
+	}
+
+	printf("%s: hub address changed\n", hc->generic->name);
+}
+
+/** Check changes on all known hubs.
+ */
+static void check_hub_changes(void) {
+	/*
+	 * Iterate through all HCs.
+	 */
+	link_t *link_hc;
+	for (link_hc = hc_list.next;
+			link_hc != &hc_list;
+			link_hc = link_hc->next) {
+		usb_hc_device_t *hc = list_get_instance(link_hc,
+				usb_hc_device_t, link);
+		/*
+		 * Iterate through all their hubs.
+		 */
+		link_t *link_hub;
+		for (link_hub = hc->hubs.next;
+				link_hub != &hc->hubs;
+				link_hub = link_hub->next) {
+			usb_hcd_hub_info_t *hub = list_get_instance(link_hub,
+					usb_hcd_hub_info_t, link);
+
+			/*
+			 * Check status change pipe of this hub.
+			 */
+			usb_target_t target = {
+				.address = hub->device->address,
+				.endpoint = 1
+			};
+
+			// FIXME: count properly
+			size_t byte_length = (hub->port_count / 8) + 1;
+
+			void *change_bitmap = malloc(byte_length);
+			size_t actual_size;
+			usb_handle_t handle;
+
+			/*
+			 * Send the request.
+			 * FIXME: check returned value for possible errors
+			 */
+			usb_hc_async_interrupt_in(hc, target,
+					change_bitmap, byte_length, &actual_size,
+					&handle);
+
+			usb_hc_async_wait_for(handle);
+
+			/*
+			 * TODO: handle the changes.
+			 */
+		}
+	}
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/usb/src/remotedrv.c
===================================================================
--- uspace/lib/usb/src/remotedrv.c	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
+++ uspace/lib/usb/src/remotedrv.c	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) 2010 Vojtech Horky
+ * 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 libusb usb
+ * @{
+ */
+/** @file
+ * @brief Driver communication for remote drivers (interface implementation).
+ */
+#include <usb/hcdhubd.h>
+#include <usbhc_iface.h>
+#include <driver.h>
+#include <bool.h>
+#include <errno.h>
+
+#include "hcdhubd_private.h"
+
+static int remote_get_address(device_t *, devman_handle_t, usb_address_t *);
+
+static int remote_interrupt_out(device_t *, usb_target_t, void *, size_t,
+    usbhc_iface_transfer_out_callback_t, void *);
+static int remote_interrupt_in(device_t *, usb_target_t, void *, size_t,
+    usbhc_iface_transfer_in_callback_t, void *);
+
+static int remote_control_write_setup(device_t *, usb_target_t,
+    void *, size_t,
+    usbhc_iface_transfer_out_callback_t, void *);
+static int remote_control_write_data(device_t *, usb_target_t,
+    void *, size_t,
+    usbhc_iface_transfer_out_callback_t, void *);
+static int remote_control_write_status(device_t *, usb_target_t,
+    usbhc_iface_transfer_in_callback_t, void *);
+
+static int remote_control_read_setup(device_t *, usb_target_t,
+    void *, size_t,
+    usbhc_iface_transfer_out_callback_t, void *);
+static int remote_control_read_data(device_t *, usb_target_t,
+    void *, size_t,
+    usbhc_iface_transfer_in_callback_t, void *);
+static int remote_control_read_status(device_t *, usb_target_t,
+    usbhc_iface_transfer_out_callback_t, void *);
+
+/** Implementation of USB HC interface. */
+usbhc_iface_t usbhc_interface = {
+	.tell_address = remote_get_address,
+	.interrupt_out = remote_interrupt_out,
+	.interrupt_in = remote_interrupt_in,
+	.control_write_setup = remote_control_write_setup,
+	.control_write_data = remote_control_write_data,
+	.control_write_status = remote_control_write_status,
+	.control_read_setup = remote_control_read_setup,
+	.control_read_data = remote_control_read_data,
+	.control_read_status = remote_control_read_status
+};
+
+/** Get USB address for remote USBHC interface.
+ *
+ * @param dev Device asked for the information.
+ * @param handle Devman handle of the USB device.
+ * @param address Storage for obtained address.
+ * @return Error code.
+ */
+int remote_get_address(device_t *dev, devman_handle_t handle,
+    usb_address_t *address)
+{
+	usb_address_t addr = usb_get_address_by_handle(handle);
+	if (addr < 0) {
+		return addr;
+	}
+
+	*address = addr;
+
+	return EOK;
+}
+
+/** Information about pending transaction on HC. */
+typedef struct {
+	/** Target device. */
+	usb_hcd_attached_device_info_t *device;
+	/** Target endpoint. */
+	usb_hc_endpoint_info_t *endpoint;
+
+	/** Callbacks. */
+	union {
+		/** Callback for outgoing transfers. */
+		usbhc_iface_transfer_out_callback_t out_callback;
+		/** Callback for incoming transfers. */
+		usbhc_iface_transfer_in_callback_t in_callback;
+	};
+
+	/** Custom argument for the callback. */
+	void *arg;
+} transfer_info_t;
+
+/** Create new transfer info.
+ *
+ * @param device Attached device.
+ * @param endpoint Endpoint.
+ * @param custom_arg Custom argument.
+ * @return Transfer info with pre-filled values.
+ */
+static transfer_info_t *transfer_info_create(
+    usb_hcd_attached_device_info_t *device, usb_hc_endpoint_info_t *endpoint,
+    void *custom_arg)
+{
+	transfer_info_t *transfer = malloc(sizeof(transfer_info_t));
+
+	transfer->device = device;
+	transfer->endpoint = endpoint;
+	transfer->arg = custom_arg;
+	transfer->out_callback = NULL;
+	transfer->in_callback = NULL;
+
+	return transfer;
+}
+
+/** Destroy transfer info.
+ *
+ * @param transfer Transfer to be destroyed.
+ */
+static void transfer_info_destroy(transfer_info_t *transfer)
+{
+	free(transfer->device);
+	free(transfer->endpoint);
+	free(transfer);
+}
+
+/** Create info about attached device.
+ *
+ * @param address Device address.
+ * @return Device info structure.
+ */
+static usb_hcd_attached_device_info_t *create_attached_device_info(
+    usb_address_t address)
+{
+	usb_hcd_attached_device_info_t *dev
+	    = malloc(sizeof(usb_hcd_attached_device_info_t));
+
+	dev->address = address;
+	dev->endpoint_count = 0;
+	dev->endpoints = NULL;
+	list_initialize(&dev->link);
+
+	return dev;
+}
+
+/** Create info about device endpoint.
+ *
+ * @param endpoint Endpoint number.
+ * @param direction Endpoint data direction.
+ * @param transfer_type Transfer type of the endpoint.
+ * @return Endpoint info structure.
+ */
+static usb_hc_endpoint_info_t *create_endpoint_info(usb_endpoint_t endpoint,
+    usb_direction_t direction, usb_transfer_type_t transfer_type)
+{
+	usb_hc_endpoint_info_t *ep = malloc(sizeof(usb_hc_endpoint_info_t));
+	ep->data_toggle = 0;
+	ep->direction = direction;
+	ep->transfer_type = transfer_type;
+	ep->endpoint = endpoint;
+
+	return ep;
+}
+
+
+
+/** Callback for OUT transfers.
+ * This callback is called by implementation of HC operations.
+ *
+ * @param hc Host controller that processed the transfer.
+ * @param outcome Transfer outcome.
+ * @param arg Custom argument.
+ */
+static void remote_out_callback(usb_hc_device_t *hc,
+    usb_transaction_outcome_t outcome, void *arg)
+{
+	transfer_info_t *transfer = (transfer_info_t *) arg;
+	transfer->out_callback(hc->generic, outcome, transfer->arg);
+
+	transfer_info_destroy(transfer);
+}
+
+/** Start an OUT transfer.
+ *
+ * @param dev Device that shall process the transfer.
+ * @param target Target device for the data.
+ * @param transfer_type Transfer type.
+ * @param data Data buffer.
+ * @param size Size of data buffer.
+ * @param callback Callback after transfer is complete.
+ * @param arg Custom argument to the callback.
+ * @return Error code.
+ */
+static int remote_out_transfer(device_t *dev, usb_target_t target,
+    usb_transfer_type_t transfer_type, void *data, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	usb_hc_device_t *hc = (usb_hc_device_t *) dev->driver_data;
+
+	if ((hc->transfer_ops == NULL)
+	    || (hc->transfer_ops->transfer_out == NULL)) {
+		return ENOTSUP;
+	}
+
+	transfer_info_t *transfer = transfer_info_create(
+	    create_attached_device_info(target.address),
+	    create_endpoint_info(target.endpoint,
+		USB_DIRECTION_OUT, transfer_type),
+	    arg);
+	transfer->out_callback = callback;
+
+	int rc = hc->transfer_ops->transfer_out(hc,
+	    transfer->device, transfer->endpoint,
+	    data, size,
+	    remote_out_callback, transfer);
+
+	if (rc != EOK) {
+		transfer_info_destroy(transfer);
+		return rc;
+	}
+
+	return EOK;
+}
+
+/** Start a SETUP transfer.
+ *
+ * @param dev Device that shall process the transfer.
+ * @param target Target device for the data.
+ * @param transfer_type Transfer type.
+ * @param data Data buffer.
+ * @param size Size of data buffer.
+ * @param callback Callback after transfer is complete.
+ * @param arg Custom argument to the callback.
+ * @return Error code.
+ */
+static int remote_setup_transfer(device_t *dev, usb_target_t target,
+    usb_transfer_type_t transfer_type, void *data, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	usb_hc_device_t *hc = (usb_hc_device_t *) dev->driver_data;
+
+	if ((hc->transfer_ops == NULL)
+	    || (hc->transfer_ops->transfer_setup == NULL)) {
+		return ENOTSUP;
+	}
+
+	transfer_info_t *transfer = transfer_info_create(
+	    create_attached_device_info(target.address),
+	    create_endpoint_info(target.endpoint,
+		USB_DIRECTION_OUT, transfer_type),
+	    arg);
+	transfer->out_callback = callback;
+
+	int rc = hc->transfer_ops->transfer_setup(hc,
+	    transfer->device, transfer->endpoint,
+	    data, size,
+	    remote_out_callback, transfer);
+
+	if (rc != EOK) {
+		transfer_info_destroy(transfer);
+		return rc;
+	}
+
+	return EOK;
+}
+
+/** Callback for IN transfers.
+ * This callback is called by implementation of HC operations.
+ *
+ * @param hc Host controller that processed the transfer.
+ * @param outcome Transfer outcome.
+ * @param actual_size Size of actually received data.
+ * @param arg Custom argument.
+ */
+static void remote_in_callback(usb_hc_device_t *hc,
+    usb_transaction_outcome_t outcome, size_t actual_size, void *arg)
+{
+	transfer_info_t *transfer = (transfer_info_t *) arg;
+	transfer->in_callback(hc->generic, outcome, actual_size, transfer->arg);
+
+	transfer_info_destroy(transfer);
+}
+
+/** Start an IN transfer.
+ *
+ * @param dev Device that shall process the transfer.
+ * @param target Target device for the data.
+ * @param transfer_type Transfer type.
+ * @param data Data buffer.
+ * @param size Size of data buffer.
+ * @param callback Callback after transfer is complete.
+ * @param arg Custom argument to the callback.
+ * @return Error code.
+ */
+static int remote_in_transfer(device_t *dev, usb_target_t target,
+    usb_transfer_type_t transfer_type, void *data, size_t size,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	usb_hc_device_t *hc = (usb_hc_device_t *) dev->driver_data;
+
+	if ((hc->transfer_ops == NULL)
+	    || (hc->transfer_ops->transfer_in == NULL)) {
+		return ENOTSUP;
+	}
+
+	transfer_info_t *transfer = transfer_info_create(
+	    create_attached_device_info(target.address),
+	    create_endpoint_info(target.endpoint,
+		USB_DIRECTION_OUT, transfer_type),
+	    arg);
+	transfer->in_callback = callback;
+
+	int rc = hc->transfer_ops->transfer_in(hc,
+	    transfer->device, transfer->endpoint,
+	    data, size,
+	    remote_in_callback, transfer);
+
+	if (rc != EOK) {
+		transfer_info_destroy(transfer);
+		return rc;
+	}
+
+	return EOK;
+}
+
+/** Start outgoing interrupt transfer (USBHC remote interface).
+ *
+ * @param dev Host controller device processing the transfer.
+ * @param target Target USB device.
+ * @param buffer Data buffer.
+ * @param size Data buffer size.
+ * @param callback Callback after the transfer is completed.
+ * @param arg Custom argument to the callback.
+ * @return Error code.
+ */
+int remote_interrupt_out(device_t *dev, usb_target_t target,
+    void *buffer, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return remote_out_transfer(dev, target, USB_TRANSFER_INTERRUPT,
+	    buffer, size, callback, arg);
+}
+
+/** Start incoming interrupt transfer (USBHC remote interface).
+ *
+ * @param dev Host controller device processing the transfer.
+ * @param target Target USB device.
+ * @param buffer Data buffer.
+ * @param size Data buffer size.
+ * @param callback Callback after the transfer is completed.
+ * @param arg Custom argument to the callback.
+ * @return Error code.
+ */
+int remote_interrupt_in(device_t *dev, usb_target_t target,
+    void *buffer, size_t size,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	return remote_in_transfer(dev, target, USB_TRANSFER_INTERRUPT,
+	    buffer, size, callback, arg);
+}
+
+
+int remote_control_write_setup(device_t *device, usb_target_t target,
+    void *buffer, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return remote_setup_transfer(device, target, USB_TRANSFER_CONTROL,
+	    buffer, size, callback, arg);
+}
+
+int remote_control_write_data(device_t *device, usb_target_t target,
+    void *buffer, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return remote_out_transfer(device, target, USB_TRANSFER_CONTROL,
+	    buffer, size, callback, arg);
+}
+
+int remote_control_write_status(device_t *device, usb_target_t target,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	return remote_in_transfer(device, target, USB_TRANSFER_CONTROL,
+	    NULL, 0, callback, arg);
+}
+
+int remote_control_read_setup(device_t *device, usb_target_t target,
+    void *buffer, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return remote_setup_transfer(device, target, USB_TRANSFER_CONTROL,
+	    buffer, size, callback, arg);
+}
+
+int remote_control_read_data(device_t *dev, usb_target_t target,
+    void *buffer, size_t size,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	return remote_in_transfer(dev, target, USB_TRANSFER_CONTROL,
+	    buffer, size, callback, arg);
+}
+
+int remote_control_read_status(device_t *device, usb_target_t target,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return remote_out_transfer(device, target, USB_TRANSFER_CONTROL,
+	    NULL, 0, callback, arg);
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/usb/src/usbdrv.c
===================================================================
--- uspace/lib/usb/src/usbdrv.c	(revision bbc28becd7d06ac1598f9fe6c3a639eb10017af9)
+++ uspace/lib/usb/src/usbdrv.c	(revision 70c852118889bcf58e9f4542b5879ca3051632d5)
@@ -64,5 +64,19 @@
 	 * Call parent hub to obtain device handle of respective HC.
 	 */
-	return ENOTSUP;
+
+	/*
+	 * FIXME: currently we connect always to virtual host controller.
+	 */
+	int rc;
+	devman_handle_t handle;
+
+	rc = devman_device_get_handle("/vhc", &handle, 0);
+	if (rc != EOK) {
+		return rc;
+	}
+	
+	int phone = devman_device_connect(handle, 0);
+
+	return phone;
 }
 
@@ -75,5 +89,13 @@
 usb_address_t usb_drv_get_my_address(int phone, device_t *dev)
 {
-	return ENOTSUP;
+	ipcarg_t address;
+	int rc = async_req_1_1(phone, IPC_M_USBHC_GET_ADDRESS,
+	    dev->handle, &address);
+
+	if (rc != EOK) {
+		return rc;
+	}
+
+	return (usb_address_t) address;
 }
 
@@ -323,4 +345,74 @@
 }
 
+/** Start control write transfer. */
+int usb_drv_async_control_write_setup(int phone, usb_target_t target,
+    void *buffer, size_t size,
+    usb_handle_t *handle)
+{
+	return async_send_buffer(phone,
+	    IPC_M_USBHC_CONTROL_WRITE_SETUP,
+	    target,
+	    buffer, size,
+	    handle);
+}
+
+/** Send data during control write transfer. */
+int usb_drv_async_control_write_data(int phone, usb_target_t target,
+    void *buffer, size_t size,
+    usb_handle_t *handle)
+{
+	return async_send_buffer(phone,
+	    IPC_M_USBHC_CONTROL_WRITE_DATA,
+	    target,
+	    buffer, size,
+	    handle);
+}
+
+/** Finalize control write transfer. */
+int usb_drv_async_control_write_status(int phone, usb_target_t target,
+    usb_handle_t *handle)
+{
+	return async_recv_buffer(phone,
+	    IPC_M_USBHC_CONTROL_WRITE_STATUS,
+	    target,
+	    NULL, 0, NULL,
+	    handle);
+}
+
+/** Start control read transfer. */
+int usb_drv_async_control_read_setup(int phone, usb_target_t target,
+    void *buffer, size_t size,
+    usb_handle_t *handle)
+{
+	return async_send_buffer(phone,
+	    IPC_M_USBHC_CONTROL_READ_SETUP,
+	    target,
+	    buffer, size,
+	    handle);
+}
+
+/** Read data during control read transfer. */
+int usb_drv_async_control_read_data(int phone, usb_target_t target,
+    void *buffer, size_t size, size_t *actual_size,
+    usb_handle_t *handle)
+{
+	return async_recv_buffer(phone,
+	    IPC_M_USBHC_CONTROL_READ_DATA,
+	    target,
+	    buffer, size, actual_size,
+	    handle);
+}
+
+/** Finalize control read transfer. */
+int usb_drv_async_control_read_status(int phone, usb_target_t target,
+    usb_handle_t *handle)
+{
+	return async_send_buffer(phone,
+	    IPC_M_USBHC_CONTROL_READ_STATUS,
+	    target,
+	    NULL, 0,
+	    handle);
+}
+
 /**
  * @}
