Index: uspace/drv/root/root.c
===================================================================
--- uspace/drv/root/root.c	(revision 41b560841ff4c03028824e18335dc4f0b7cea1bd)
+++ uspace/drv/root/root.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -88,5 +88,6 @@
 
 	int res = child_device_register_wrapper(parent, VIRTUAL_DEVICE_NAME,
-	    VIRTUAL_DEVICE_MATCH_ID, VIRTUAL_DEVICE_MATCH_SCORE);
+	    VIRTUAL_DEVICE_MATCH_ID, VIRTUAL_DEVICE_MATCH_SCORE,
+	    NULL);
 
 	return res;
@@ -136,5 +137,5 @@
 
 	res = child_device_register_wrapper(parent, PLATFORM_DEVICE_NAME,
-	    match_id, PLATFORM_DEVICE_MATCH_SCORE);
+	    match_id, PLATFORM_DEVICE_MATCH_SCORE, NULL);
 
 	return res;
Index: uspace/drv/rootvirt/devices.def
===================================================================
--- uspace/drv/rootvirt/devices.def	(revision 41b560841ff4c03028824e18335dc4f0b7cea1bd)
+++ uspace/drv/rootvirt/devices.def	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -22,2 +22,7 @@
 },
 #endif
+/* Virtual USB host controller. */
+{
+	.name = "usbhc",
+	.match_id = "usb&hc=vhc"
+},
Index: uspace/drv/rootvirt/rootvirt.c
===================================================================
--- uspace/drv/rootvirt/rootvirt.c	(revision 41b560841ff4c03028824e18335dc4f0b7cea1bd)
+++ uspace/drv/rootvirt/rootvirt.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -84,5 +84,5 @@
 
 	int rc = child_device_register_wrapper(parent, virt_dev->name,
-	    virt_dev->match_id, 10);
+	    virt_dev->match_id, 10, NULL);
 
 	if (rc == EOK) {
Index: uspace/drv/test1/test1.c
===================================================================
--- uspace/drv/test1/test1.c	(revision 41b560841ff4c03028824e18335dc4f0b7cea1bd)
+++ uspace/drv/test1/test1.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -62,5 +62,5 @@
 
 	int rc = child_device_register_wrapper(parent, name,
-	    match_id, match_score);
+	    match_id, match_score, NULL);
 
 	if (rc == EOK) {
Index: uspace/drv/test2/test2.c
===================================================================
--- uspace/drv/test2/test2.c	(revision 41b560841ff4c03028824e18335dc4f0b7cea1bd)
+++ uspace/drv/test2/test2.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -64,5 +64,5 @@
 
 	int rc = child_device_register_wrapper(parent, name,
-	    match_id, match_score);
+	    match_id, match_score, NULL);
 
 	if (rc == EOK) {
Index: uspace/drv/uhci/Makefile
===================================================================
--- uspace/drv/uhci/Makefile	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/uhci/Makefile	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+USPACE_PREFIX = ../..
+LIBS = $(LIBDRV_PREFIX)/libdrv.a $(LIBUSB_PREFIX)/libusb.a
+EXTRA_CFLAGS += -I$(LIBDRV_PREFIX)/include -I$(LIBUSB_PREFIX)/include
+BINARY = uhci
+
+SOURCES = \
+	main.c \
+	transfers.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/uhci/main.c
===================================================================
--- uspace/drv/uhci/main.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/uhci/main.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+#include <usb/hcdhubd.h>
+#include <usb_iface.h>
+#include <usb/debug.h>
+#include <errno.h>
+#include <driver.h>
+#include "uhci.h"
+
+static int usb_iface_get_hc_handle(device_t *dev, devman_handle_t *handle)
+{
+	/* This shall be called only for the UHCI itself. */
+	assert(dev->parent == NULL);
+
+	*handle = dev->handle;
+	return EOK;
+}
+
+static usb_iface_t hc_usb_iface = {
+	.get_hc_handle = usb_iface_get_hc_handle
+};
+
+static device_ops_t uhci_ops = {
+	.interfaces[USB_DEV_IFACE] = &hc_usb_iface,
+	.interfaces[USBHC_DEV_IFACE] = &uhci_iface
+};
+
+static int uhci_add_device(device_t *device)
+{
+	usb_dprintf(NAME, 1, "uhci_add_device() called\n");
+	device->ops = &uhci_ops;
+
+	/*
+	 * We need to announce the presence of our root hub.
+	 */
+	usb_dprintf(NAME, 2, "adding root hub\n");
+	usb_hcd_add_root_hub(device);
+
+	return EOK;
+}
+
+static driver_ops_t uhci_driver_ops = {
+	.add_device = uhci_add_device,
+};
+
+static driver_t uhci_driver = {
+	.name = NAME,
+	.driver_ops = &uhci_driver_ops
+};
+
+int main(int argc, char *argv[])
+{
+	/*
+	 * Do some global initializations.
+	 */
+	sleep(5);
+	usb_dprintf_enable(NAME, 5);
+
+	return driver_main(&uhci_driver);
+}
Index: uspace/drv/uhci/transfers.c
===================================================================
--- uspace/drv/uhci/transfers.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/uhci/transfers.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+#include <usb/hcdhubd.h>
+#include <errno.h>
+
+#include "uhci.h"
+
+static int enqueue_transfer_out(device_t *dev,
+    usb_target_t target, usb_transfer_type_t transfer_type,
+    void *buffer, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	printf(NAME ": transfer OUT [%d.%d (%s); %zu]\n",
+	    target.address, target.endpoint,
+	    usb_str_transfer_type(transfer_type),
+	    size);
+
+	return ENOTSUP;
+}
+
+static int enqueue_transfer_setup(device_t *dev,
+    usb_target_t target, usb_transfer_type_t transfer_type,
+    void *buffer, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	printf(NAME ": transfer SETUP [%d.%d (%s); %zu]\n",
+	    target.address, target.endpoint,
+	    usb_str_transfer_type(transfer_type),
+	    size);
+
+	return ENOTSUP;
+}
+
+static int enqueue_transfer_in(device_t *dev,
+    usb_target_t target, usb_transfer_type_t transfer_type,
+    void *buffer, size_t size,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	printf(NAME ": transfer IN [%d.%d (%s); %zu]\n",
+	    target.address, target.endpoint,
+	    usb_str_transfer_type(transfer_type),
+	    size);
+
+	return ENOTSUP;
+}
+
+
+static int get_address(device_t *dev, devman_handle_t handle,
+    usb_address_t *address)
+{
+	return ENOTSUP;
+}
+
+static int interrupt_out(device_t *dev, usb_target_t target,
+    void *data, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return enqueue_transfer_out(dev, target, USB_TRANSFER_INTERRUPT,
+	    data, size,
+	    callback, arg);
+}
+
+static int interrupt_in(device_t *dev, usb_target_t target,
+    void *data, size_t size,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	return enqueue_transfer_in(dev, target, USB_TRANSFER_INTERRUPT,
+	    data, size,
+	    callback, arg);
+}
+
+static int control_write_setup(device_t *dev, usb_target_t target,
+    void *data, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return enqueue_transfer_setup(dev, target, USB_TRANSFER_CONTROL,
+	    data, size,
+	    callback, arg);
+}
+
+static int control_write_data(device_t *dev, usb_target_t target,
+    void *data, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return enqueue_transfer_out(dev, target, USB_TRANSFER_CONTROL,
+	    data, size,
+	    callback, arg);
+}
+
+static int control_write_status(device_t *dev, usb_target_t target,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	return enqueue_transfer_in(dev, target, USB_TRANSFER_CONTROL,
+	    NULL, 0,
+	    callback, arg);
+}
+
+static int control_read_setup(device_t *dev, usb_target_t target,
+    void *data, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return enqueue_transfer_setup(dev, target, USB_TRANSFER_CONTROL,
+	    data, size,
+	    callback, arg);
+}
+
+static int control_read_data(device_t *dev, usb_target_t target,
+    void *data, size_t size,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	return enqueue_transfer_in(dev, target, USB_TRANSFER_CONTROL,
+	    data, size,
+	    callback, arg);
+}
+
+static int control_read_status(device_t *dev, usb_target_t target,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return enqueue_transfer_out(dev, target, USB_TRANSFER_CONTROL,
+	    NULL, 0,
+	    callback, arg);
+}
+
+
+usbhc_iface_t uhci_iface = {
+	.tell_address = get_address,
+	.interrupt_out = interrupt_out,
+	.interrupt_in = interrupt_in,
+	.control_write_setup = control_write_setup,
+	.control_write_data = control_write_data,
+	.control_write_status = control_write_status,
+	.control_read_setup = control_read_setup,
+	.control_read_data = control_read_data,
+	.control_read_status = control_read_status
+};
Index: uspace/drv/uhci/uhci.h
===================================================================
--- uspace/drv/uhci/uhci.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/uhci/uhci.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,47 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief UHCI driver
+ */
+#ifndef DRV_UHCI_UHCI_H
+#define DRV_UHCI_UHCI_H
+
+#include <usbhc_iface.h>
+
+#define NAME "uhci"
+
+usbhc_iface_t uhci_iface;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/uhci/uhci.ma
===================================================================
--- uspace/drv/uhci/uhci.ma	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/uhci/uhci.ma	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,2 @@
+10 pci/ven=8086&dev=7020
+
Index: uspace/drv/usbhub/Makefile
===================================================================
--- uspace/drv/usbhub/Makefile	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbhub/Makefile	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,40 @@
+#
+# 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.
+#
+
+USPACE_PREFIX = ../..
+LIBS = $(LIBDRV_PREFIX)/libdrv.a $(LIBUSB_PREFIX)/libusb.a
+EXTRA_CFLAGS += -I$(LIBDRV_PREFIX)/include -I$(LIBUSB_PREFIX)/include
+BINARY = usbhub
+
+SOURCES = \
+	main.c \
+	utils.c \
+	usbhub.c \
+	usblist.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/usbhub/main.c
===================================================================
--- uspace/drv/usbhub/main.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbhub/main.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#include <driver.h>
+#include <errno.h>
+#include <async.h>
+
+#include <usb/usbdrv.h>
+
+#include "usbhub.h"
+#include "usbhub_private.h"
+
+
+usb_general_list_t usb_hub_list;
+futex_t usb_hub_list_lock;
+
+static driver_ops_t hub_driver_ops = {
+	.add_device = usb_add_hub_device,
+};
+
+static driver_t hub_driver = {
+	.name = "usbhub",
+	.driver_ops = &hub_driver_ops
+};
+
+int usb_hub_control_loop(void * noparam){
+	while(true){
+		usb_hub_check_hub_changes();
+		async_usleep(1000 * 1000);
+	}
+	return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+	usb_dprintf_enable(NAME,1);
+	futex_initialize(&usb_hub_list_lock, 0);
+	usb_lst_init(&usb_hub_list);
+	futex_up(&usb_hub_list_lock);
+	fid_t fid = fibril_create(usb_hub_control_loop, NULL);
+	if (fid == 0) {
+		dprintf(1, "failed to start fibril for HUB devices");
+		//printf("%s: failed to start fibril for HUB devices\n", NAME);
+		return ENOMEM;
+	}
+	fibril_add_ready(fid);
+
+	return driver_main(&hub_driver);
+}
Index: uspace/drv/usbhub/port_status.h
===================================================================
--- uspace/drv/usbhub/port_status.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbhub/port_status.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,303 @@
+/*
+ * 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.
+ */
+
+#ifndef PORT_STATUS_H
+#define	PORT_STATUS_H
+
+#include <bool.h>
+#include <sys/types.h>
+#include <usb/devreq.h>
+#include "usbhub_private.h"
+
+/**
+ * structure holding port status and changes flags.
+ * should not be accessed directly, use supplied getter/setter methods.
+ *
+ * For more information refer to table 11-15 in
+ * "Universal Serial Bus Specification Revision 1.1"
+ *
+ */
+typedef uint32_t usb_port_status_t;
+
+/**
+ * set values in request to be it a port status request
+ * @param request
+ * @param port
+ */
+static inline void usb_hub_set_port_status_request(
+usb_device_request_setup_packet_t * request, uint16_t port
+){
+	request->index = port;
+	request->request_type = USB_HUB_REQ_TYPE_GET_PORT_STATUS;
+	request->request = USB_HUB_REQUEST_GET_STATUS;
+	request->value = 0;
+	request->length = 4;
+}
+
+
+/**
+ * create request for usb hub port status
+ * @param port
+ * @return
+ */
+static inline usb_device_request_setup_packet_t *
+usb_hub_create_port_status_request(uint16_t port){
+	usb_device_request_setup_packet_t * result =
+		usb_new(usb_device_request_setup_packet_t);
+	usb_hub_set_port_status_request(result,port);
+	return result;
+}
+
+
+/**
+ * set the device request to be a port enable request
+ * @param request
+ * @param port
+ */
+static inline void usb_hub_set_enable_port_request(
+usb_device_request_setup_packet_t * request, uint16_t port
+){
+	request->index = port;
+	request->request_type = USB_HUB_REQ_TYPE_SET_PORT_FEATURE;
+	request->request = USB_HUB_REQUEST_SET_FEATURE;
+	request->value = USB_HUB_FEATURE_C_PORT_ENABLE;
+	request->length = 0;
+}
+
+/**
+ * enable specified port
+ * @param port
+ * @return
+ */
+static inline usb_device_request_setup_packet_t *
+usb_hub_create_enable_port_request(uint16_t port){
+	usb_device_request_setup_packet_t * result =
+		usb_new(usb_device_request_setup_packet_t);
+	usb_hub_set_enable_port_request(result,port);
+	return result;
+}
+
+/**
+ * set the device request to be a port disable request
+ * @param request
+ * @param port
+ */
+static inline void usb_hub_set_disable_port_request(
+usb_device_request_setup_packet_t * request, uint16_t port
+){
+	request->index = port;
+	request->request_type = USB_HUB_REQ_TYPE_SET_PORT_FEATURE;
+	request->request = USB_HUB_REQUEST_SET_FEATURE;
+	request->value = USB_HUB_FEATURE_C_PORT_SUSPEND;
+	request->length = 0;
+}
+
+/**
+ * disable specified port
+ * @param port
+ * @return
+ */
+static inline usb_device_request_setup_packet_t *
+usb_hub_create_disable_port_request(uint16_t port){
+	usb_device_request_setup_packet_t * result =
+		usb_new(usb_device_request_setup_packet_t);
+	usb_hub_set_disable_port_request(result,port);
+	return result;
+}
+
+/**
+ * set the device request to be a port disable request
+ * @param request
+ * @param port
+ */
+static inline void usb_hub_set_reset_port_request(
+usb_device_request_setup_packet_t * request, uint16_t port
+){
+	request->index = port;
+	request->request_type = USB_HUB_REQ_TYPE_SET_PORT_FEATURE;
+	request->request = USB_HUB_REQUEST_SET_FEATURE;
+	request->value = USB_HUB_FEATURE_PORT_RESET;
+	request->length = 0;
+}
+
+/**
+ * disable specified port
+ * @param port
+ * @return
+ */
+static inline usb_device_request_setup_packet_t *
+usb_hub_create_reset_port_request(uint16_t port){
+	usb_device_request_setup_packet_t * result =
+		usb_new(usb_device_request_setup_packet_t);
+	usb_hub_set_reset_port_request(result,port);
+	return result;
+}
+
+/**
+ * set the device request to be a port disable request
+ * @param request
+ * @param port
+ */
+static inline void usb_hub_set_power_port_request(
+usb_device_request_setup_packet_t * request, uint16_t port
+){
+	request->index = port;
+	request->request_type = USB_HUB_REQ_TYPE_SET_PORT_FEATURE;
+	request->request = USB_HUB_REQUEST_SET_FEATURE;
+	request->value = USB_HUB_FEATURE_PORT_POWER;
+	request->length = 0;
+}
+
+/** get i`th bit of port status */
+static inline bool usb_port_get_bit(usb_port_status_t * status, int idx)
+{
+	return (((*status)>>(idx))%2);
+}
+
+/** set i`th bit of port status */
+static inline void usb_port_set_bit(
+	usb_port_status_t * status, int idx, bool value)
+{
+	(*status) = value?
+		               ((*status)|(1<<(idx))):
+		               ((*status)&(~(1<<(idx))));
+}
+
+//device connnected on port
+static inline bool usb_port_dev_connected(usb_port_status_t * status){
+	return usb_port_get_bit(status,0);
+}
+
+static inline void usb_port_set_dev_connected(usb_port_status_t * status,bool connected){
+	usb_port_set_bit(status,0,connected);
+}
+
+//port enabled
+static inline bool usb_port_enabled(usb_port_status_t * status){
+	return usb_port_get_bit(status,1);
+}
+
+static inline void usb_port_set_enabled(usb_port_status_t * status,bool enabled){
+	usb_port_set_bit(status,1,enabled);
+}
+
+//port suspended
+static inline bool usb_port_suspended(usb_port_status_t * status){
+	return usb_port_get_bit(status,2);
+}
+
+static inline void usb_port_set_suspended(usb_port_status_t * status,bool suspended){
+	usb_port_set_bit(status,2,suspended);
+}
+
+//over currect
+static inline bool usb_port_over_current(usb_port_status_t * status){
+	return usb_port_get_bit(status,3);
+}
+
+static inline void usb_port_set_over_current(usb_port_status_t * status,bool value){
+	usb_port_set_bit(status,3,value);
+}
+
+//port reset
+static inline bool usb_port_reset(usb_port_status_t * status){
+	return usb_port_get_bit(status,4);
+}
+
+static inline void usb_port_set_reset(usb_port_status_t * status,bool value){
+	usb_port_set_bit(status,4,value);
+}
+
+//powered
+static inline bool usb_port_powered(usb_port_status_t * status){
+	return usb_port_get_bit(status,8);
+}
+
+static inline void usb_port_set_powered(usb_port_status_t * status,bool powered){
+	usb_port_set_bit(status,8,powered);
+}
+
+//low speed device attached
+static inline bool usb_port_low_speed(usb_port_status_t * status){
+	return usb_port_get_bit(status,9);
+}
+
+static inline void usb_port_set_low_speed(usb_port_status_t * status,bool low_speed){
+	usb_port_set_bit(status,9,low_speed);
+}
+
+
+//connect change
+static inline bool usb_port_connect_change(usb_port_status_t * status){
+	return usb_port_get_bit(status,16);
+}
+
+static inline void usb_port_set_connect_change(usb_port_status_t * status,bool change){
+	usb_port_set_bit(status,16,change);
+}
+
+//port enable change
+static inline bool usb_port_enabled_change(usb_port_status_t * status){
+	return usb_port_get_bit(status,17);
+}
+
+static inline void usb_port_set_enabled_change(usb_port_status_t * status,bool change){
+	usb_port_set_bit(status,17,change);
+}
+
+//suspend change
+static inline bool usb_port_suspend_change(usb_port_status_t * status){
+	return usb_port_get_bit(status,18);
+}
+
+static inline void usb_port_set_suspend_change(usb_port_status_t * status,bool change){
+	usb_port_set_bit(status,18,change);
+}
+
+//over current change
+static inline bool usb_port_overcurrent_change(usb_port_status_t * status){
+	return usb_port_get_bit(status,19);
+}
+
+static inline void usb_port_set_overcurrent_change(usb_port_status_t * status,bool change){
+	usb_port_set_bit(status,19,change);
+}
+
+//reset change
+static inline bool usb_port_reset_completed(usb_port_status_t * status){
+	return usb_port_get_bit(status,20);
+}
+
+static inline void usb_port_set_reset_completed(usb_port_status_t * status,bool completed){
+	usb_port_set_bit(status,20,completed);
+}
+
+
+
+#endif	/* PORT_STATUS_H */
+
Index: uspace/drv/usbhub/usbhub.c
===================================================================
--- uspace/drv/usbhub/usbhub.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbhub/usbhub.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,520 @@
+/*
+ * 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 usb hub driver
+ * @{
+ */
+/** @file
+ * @brief usb hub main functionality
+ */
+
+#include <driver.h>
+#include <bool.h>
+#include <errno.h>
+
+#include <usb_iface.h>
+#include <usb/usbdrv.h>
+#include <usb/descriptor.h>
+#include <usb/devreq.h>
+#include <usb/classes/hub.h>
+
+#include "usbhub.h"
+#include "usbhub_private.h"
+#include "port_status.h"
+
+static usb_iface_t hub_usb_iface = {
+	.get_hc_handle = usb_drv_find_hc
+};
+
+static device_ops_t hub_device_ops = {
+	.interfaces[USB_DEV_IFACE] = &hub_usb_iface
+};
+
+//*********************************************
+//
+//  hub driver code, initialization
+//
+//*********************************************
+
+usb_hub_info_t * usb_create_hub_info(device_t * device, int hc) {
+	usb_hub_info_t* result = usb_new(usb_hub_info_t);
+	//result->device = device;
+	result->port_count = -1;
+	/// \TODO is this correct? is the device stored?
+	result->device = device;
+
+
+	//printf("[usb_hub] phone to hc = %d\n", hc);
+	if (hc < 0) {
+		return result;
+	}
+	//get some hub info
+	usb_address_t addr = usb_drv_get_my_address(hc, device);
+	dprintf(1,"[usb_hub] address of newly created hub = %d", addr);
+	/*if(addr<0){
+		//return result;
+
+	}*/
+
+	result->usb_device = usb_new(usb_hcd_attached_device_info_t);
+	result->usb_device->address = addr;
+
+	// get hub descriptor
+	usb_target_t target;
+	target.address = addr;
+	target.endpoint = 0;
+	usb_device_request_setup_packet_t request;
+	//printf("[usb_hub] creating descriptor request\n");
+	usb_hub_set_descriptor_request(&request);
+
+	//printf("[usb_hub] creating serialized descriptor\n");
+	void * serialized_descriptor = malloc(USB_HUB_MAX_DESCRIPTOR_SIZE);
+	usb_hub_descriptor_t * descriptor;
+	size_t received_size;
+	int opResult;
+	//printf("[usb_hub] starting control transaction\n");
+	opResult = usb_drv_sync_control_read(
+			hc, target, &request, serialized_descriptor,
+			USB_HUB_MAX_DESCRIPTOR_SIZE, &received_size);
+	if (opResult != EOK) {
+		dprintf(1,"[usb_hub] failed when receiving hub descriptor, badcode = %d",opResult);
+		free(serialized_descriptor);
+		return result;
+	}
+	//printf("[usb_hub] deserializing descriptor\n");
+	descriptor = usb_deserialize_hub_desriptor(serialized_descriptor);
+	if(descriptor==NULL){
+		dprintf(1,"[usb_hub] could not deserialize descriptor ");
+		result->port_count = 1;///\TODO this code is only for debug!!!
+		return result;
+	}
+	//printf("[usb_hub] setting port count to %d\n",descriptor->ports_count);
+	result->port_count = descriptor->ports_count;
+	result->attached_devs = (usb_hub_attached_device_t*)
+	    malloc((result->port_count+1) * sizeof(usb_hub_attached_device_t));
+	int i;
+	for(i=0;i<result->port_count+1;++i){
+		result->attached_devs[i].devman_handle=0;
+		result->attached_devs[i].address=0;
+	}
+	//printf("[usb_hub] freeing data\n");
+	free(serialized_descriptor);
+	free(descriptor->devices_removable);
+	free(descriptor);
+
+	//finish
+
+	dprintf(1,"[usb_hub] hub info created");
+
+	return result;
+}
+
+int usb_add_hub_device(device_t *dev) {
+	dprintf(1, "add_hub_device(handle=%d)", (int) dev->handle);
+	dprintf(1,"[usb_hub] hub device");
+
+	/*
+	 * We are some (probably deeply nested) hub.
+	 * Thus, assign our own operations and explore already
+	 * connected devices.
+	 */
+	dev->ops = &hub_device_ops;
+
+	//create the hub structure
+	//get hc connection
+	int hc = usb_drv_hc_connect_auto(dev, 0);
+	if (hc < 0) {
+		return hc;
+	}
+
+	usb_hub_info_t * hub_info = usb_create_hub_info(dev, hc);
+	int port;
+	int opResult;
+	usb_device_request_setup_packet_t request;
+	usb_target_t target;
+	target.address = hub_info->usb_device->address;
+	target.endpoint = 0;
+
+	//get configuration descriptor
+	// this is not fully correct - there are more configurations
+	// and all should be checked
+	usb_standard_device_descriptor_t std_descriptor;
+	opResult = usb_drv_req_get_device_descriptor(hc, target.address,
+    &std_descriptor);
+	if(opResult!=EOK){
+		dprintf(1,"[usb_hub] could not get device descriptor, %d",opResult);
+		return opResult;
+	}
+	dprintf(1,"[usb_hub] hub has %d configurations",std_descriptor.configuration_count);
+	if(std_descriptor.configuration_count<1){
+		dprintf(1,"[usb_hub] THERE ARE NO CONFIGURATIONS AVAILABLE");
+	}
+	/// \TODO check other configurations
+	usb_standard_configuration_descriptor_t config_descriptor;
+	opResult = usb_drv_req_get_bare_configuration_descriptor(hc,
+        target.address, 0,
+        &config_descriptor);
+	if(opResult!=EOK){
+		dprintf(1,"[usb_hub] could not get configuration descriptor, %d",opResult);
+		return opResult;
+	}
+	//set configuration
+	request.request_type = 0;
+	request.request = USB_DEVREQ_SET_CONFIGURATION;
+	request.index=0;
+	request.length=0;
+	request.value_high=0;
+	request.value_low = config_descriptor.configuration_number;
+	opResult = usb_drv_sync_control_write(hc, target, &request, NULL, 0);
+	if (opResult != EOK) {
+		dprintf(1,"[usb_hub]something went wrong when setting hub`s configuration, %d", opResult);
+	}
+
+	for (port = 1; port < hub_info->port_count+1; ++port) {
+		usb_hub_set_power_port_request(&request, port);
+		opResult = usb_drv_sync_control_write(hc, target, &request, NULL, 0);
+		dprintf(1,"[usb_hub] powering port %d",port);
+		if (opResult != EOK) {
+			dprintf(1,"[usb_hub]something went wrong when setting hub`s %dth port", port);
+		}
+	}
+	//ports powered, hub seems to be enabled
+
+	ipc_hangup(hc);
+
+	//add the hub to list
+	futex_down(&usb_hub_list_lock);
+	usb_lst_append(&usb_hub_list, hub_info);
+	futex_up(&usb_hub_list_lock);
+
+	dprintf(1,"[usb_hub] hub info added to list");
+	//(void)hub_info;
+	usb_hub_check_hub_changes();
+
+	
+
+	dprintf(1,"[usb_hub] hub dev added");
+	dprintf(1,"\taddress %d, has %d ports ",
+			hub_info->usb_device->address,
+			hub_info->port_count);
+	dprintf(1,"\tused configuration %d",config_descriptor.configuration_number);
+
+	return EOK;
+	//return ENOTSUP;
+}
+
+
+
+//*********************************************
+//
+//  hub driver code, main loop
+//
+//*********************************************
+
+/**
+ * convenience function for releasing default address and writing debug info
+ * (these few lines are used too often to be written again and again)
+ * @param hc
+ * @return
+ */
+inline static int usb_hub_release_default_address(int hc){
+	int opResult;
+	dprintf(1,"[usb_hub] releasing default address");
+	opResult = usb_drv_release_default_address(hc);
+	if (opResult != EOK) {
+		dprintf(1,"[usb_hub] failed to release default address");
+	}
+	return opResult;
+}
+
+/**
+ * reset the port with new device and reserve the default address
+ * @param hc
+ * @param port
+ * @param target
+ */
+static void usb_hub_init_add_device(int hc, uint16_t port, usb_target_t target) {
+	usb_device_request_setup_packet_t request;
+	int opResult;
+	dprintf(1,"[usb_hub] some connection changed");
+	//get default address
+	opResult = usb_drv_reserve_default_address(hc);
+	if (opResult != EOK) {
+		dprintf(1,"[usb_hub] cannot assign default address, it is probably used");
+		return;
+	}
+	//reset port
+	usb_hub_set_reset_port_request(&request, port);
+	opResult = usb_drv_sync_control_write(
+			hc, target,
+			&request,
+			NULL, 0
+			);
+	if (opResult != EOK) {
+		dprintf(1,"[usb_hub] something went wrong when reseting a port");
+		usb_hub_release_default_address(hc);
+	}
+}
+
+/**
+ * finalize adding new device after port reset
+ * @param hc
+ * @param port
+ * @param target
+ */
+static void usb_hub_finalize_add_device( usb_hub_info_t * hub,
+		int hc, uint16_t port, usb_target_t target) {
+
+	int opResult;
+	dprintf(1,"[usb_hub] finalizing add device");
+	opResult = usb_hub_clear_port_feature(hc, target.address,
+	    port, USB_HUB_FEATURE_C_PORT_RESET);
+	if (opResult != EOK) {
+		dprintf(1,"[usb_hub] failed to clear port reset feature");
+		usb_hub_release_default_address(hc);
+		return;
+	}
+
+	/* Request address at from host controller. */
+	usb_address_t new_device_address = usb_drv_request_address(hc);
+	if (new_device_address < 0) {
+		dprintf(1,"[usb_hub] failed to get free USB address");
+		opResult = new_device_address;
+		usb_hub_release_default_address(hc);
+		return;
+	}
+	dprintf(1,"[usb_hub] setting new address");
+	opResult = usb_drv_req_set_address(hc, USB_ADDRESS_DEFAULT,
+	    new_device_address);
+
+	if (opResult != EOK) {
+		dprintf(1,"[usb_hub] could not set address for new device");
+		usb_hub_release_default_address(hc);
+		return;
+	}
+
+
+	opResult = usb_hub_release_default_address(hc);
+	if(opResult!=EOK){
+		return;
+	}
+
+	devman_handle_t child_handle;
+	opResult = usb_drv_register_child_in_devman(hc, hub->device,
+            new_device_address, &child_handle);
+	if (opResult != EOK) {
+		dprintf(1,"[usb_hub] could not start driver for new device");
+		return;
+	}
+	hub->attached_devs[port].devman_handle = child_handle;
+	hub->attached_devs[port].address = new_device_address;
+
+	opResult = usb_drv_bind_address(hc, new_device_address, child_handle);
+	if (opResult != EOK) {
+		dprintf(1,"[usb_hub] could not assign address of device in hcd");
+		return;
+	}
+	dprintf(1,"[usb_hub] new device address %d, handle %zu",
+	    new_device_address, child_handle);
+
+}
+
+/**
+ * unregister device address in hc
+ * @param hc
+ * @param port
+ * @param target
+ */
+static void usb_hub_removed_device(
+    usb_hub_info_t * hub, int hc, uint16_t port, usb_target_t target) {
+	//usb_device_request_setup_packet_t request;
+	int opResult;
+	
+	/// \TODO remove device
+
+	hub->attached_devs[port].devman_handle=0;
+	//close address
+	if(hub->attached_devs[port].address!=0){
+		opResult = usb_drv_release_address(hc,hub->attached_devs[port].address);
+		if(opResult != EOK) {
+			dprintf(1,
+					"[usb_hub] could not release address of removed device: %d"
+					,opResult);
+		}
+		hub->attached_devs[port].address = 0;
+	}else{
+		dprintf(1,
+				"[usb_hub] this is strange, disconnected device had no address");
+		//device was disconnected before it`s port was reset - return default address
+		usb_drv_release_default_address(hc);
+	}
+}
+
+/**
+ * process interrupts on given hub port
+ * @param hc
+ * @param port
+ * @param target
+ */
+static void usb_hub_process_interrupt(usb_hub_info_t * hub, int hc,
+        uint16_t port, usb_address_t address) {
+	dprintf(1,"[usb_hub] interrupt at port %d", port);
+	//determine type of change
+	usb_target_t target;
+	target.address=address;
+	target.endpoint=0;
+	usb_port_status_t status;
+	size_t rcvd_size;
+	usb_device_request_setup_packet_t request;
+	int opResult;
+	usb_hub_set_port_status_request(&request, port);
+	//endpoint 0
+
+	opResult = usb_drv_sync_control_read(
+			hc, target,
+			&request,
+			&status, 4, &rcvd_size
+			);
+	if (opResult != EOK) {
+		dprintf(1,"[usb_hub] ERROR: could not get port status");
+		return;
+	}
+	if (rcvd_size != sizeof (usb_port_status_t)) {
+		dprintf(1,"[usb_hub] ERROR: received status has incorrect size");
+		return;
+	}
+	//something connected/disconnected
+	if (usb_port_connect_change(&status)) {
+		opResult = usb_hub_clear_port_feature(hc, target.address,
+		    port, USB_HUB_FEATURE_C_PORT_CONNECTION);
+		// TODO: check opResult
+		if (usb_port_dev_connected(&status)) {
+			dprintf(1,"[usb_hub] some connection changed");
+			usb_hub_init_add_device(hc, port, target);
+		} else {
+			usb_hub_removed_device(hub, hc, port, target);
+		}
+	}
+	//port reset
+	if (usb_port_reset_completed(&status)) {
+		dprintf(1,"[usb_hub] port reset complete");
+		if (usb_port_enabled(&status)) {
+			usb_hub_finalize_add_device(hub, hc, port, target);
+		} else {
+			dprintf(1,"[usb_hub] ERROR: port reset, but port still not enabled");
+		}
+	}
+
+	usb_port_set_connect_change(&status, false);
+	usb_port_set_reset(&status, false);
+	usb_port_set_reset_completed(&status, false);
+	usb_port_set_dev_connected(&status, false);
+	if (status) {
+		dprintf(1,"[usb_hub]there was some unsupported change on port %d",port);
+	}
+	/// \TODO handle other changes
+	/// \TODO debug log for various situations
+
+}
+
+/* Check changes on all known hubs.
+ */
+void usb_hub_check_hub_changes(void) {
+	/*
+	 * Iterate through all hubs.
+	 */
+	usb_general_list_t * lst_item;
+	futex_down(&usb_hub_list_lock);
+	for (lst_item = usb_hub_list.next;
+			lst_item != &usb_hub_list;
+			lst_item = lst_item->next) {
+		futex_up(&usb_hub_list_lock);
+		usb_hub_info_t * hub_info = ((usb_hub_info_t*)lst_item->data);
+		/*
+		 * Check status change pipe of this hub.
+		 */
+
+		usb_target_t target;
+		target.address = hub_info->usb_device->address;
+		target.endpoint = 1;/// \TODO get from endpoint descriptor
+		dprintf(1,"[usb_hub] checking changes for hub at addr %d",
+		    target.address);
+
+		size_t port_count = hub_info->port_count;
+
+		/*
+		 * Connect to respective HC.
+		 */
+		int hc = usb_drv_hc_connect_auto(hub_info->device, 0);
+		if (hc < 0) {
+			continue;
+		}
+
+		/// FIXME: count properly
+		size_t byte_length = ((port_count+1) / 8) + 1;
+
+		void *change_bitmap = malloc(byte_length);
+		size_t actual_size;
+		usb_handle_t handle;
+
+		/*
+		 * Send the request.
+		 */
+		int opResult = usb_drv_async_interrupt_in(hc, target,
+				change_bitmap, byte_length, &actual_size,
+				&handle);
+
+		usb_drv_async_wait_for(handle);
+
+		if (opResult != EOK) {
+			dprintf(1,"[usb_hub] something went wrong while getting status of hub");
+			continue;
+		}
+		unsigned int port;
+		for (port = 1; port < port_count+1; ++port) {
+			bool interrupt =
+					(((uint8_t*) change_bitmap)[port / 8] >> (port % 8)) % 2;
+			if (interrupt) {
+				usb_hub_process_interrupt(
+				        hub_info, hc, port, hub_info->usb_device->address);
+			}
+		}
+		free(change_bitmap);
+
+		ipc_hangup(hc);
+		futex_down(&usb_hub_list_lock);
+	}
+	futex_up(&usb_hub_list_lock);
+}
+
+
+
+
+
+/**
+ * @}
+ */
Index: uspace/drv/usbhub/usbhub.h
===================================================================
--- uspace/drv/usbhub/usbhub.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbhub/usbhub.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,87 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Hub driver.
+ */
+#ifndef DRV_USBHUB_USBHUB_H
+#define DRV_USBHUB_USBHUB_H
+
+#define NAME "usbhub"
+
+#include "usb/hcdhubd.h"
+
+/** basic information about device attached to hub */
+typedef struct{
+	usb_address_t address;
+	devman_handle_t devman_handle;
+}usb_hub_attached_device_t;
+
+/** Information about attached hub. */
+typedef struct {
+	/** Number of ports. */
+	int port_count;
+	/** attached device handles */
+	usb_hub_attached_device_t * attached_devs;
+	/** General usb device info. */
+	usb_hcd_attached_device_info_t * usb_device;
+	/** General device info*/
+	device_t * device;
+
+} usb_hub_info_t;
+
+/**
+ * function running the hub-controlling loop.
+ * @param noparam fundtion does not need any parameters
+ */
+int usb_hub_control_loop(void * noparam);
+
+/** Callback when new hub device is detected.
+ *
+ * @param dev New device.
+ * @return Error code.
+ */
+int usb_add_hub_device(device_t *dev);
+
+/**
+ * check changes on all registered hubs
+ */
+void usb_hub_check_hub_changes(void);
+
+
+//int usb_add_hub_device(device_t *);
+
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/usbhub/usbhub.ma
===================================================================
--- uspace/drv/usbhub/usbhub.ma	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbhub/usbhub.ma	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,2 @@
+10 usb&hub
+10 usb&class=hub
Index: uspace/drv/usbhub/usbhub_private.h
===================================================================
--- uspace/drv/usbhub/usbhub_private.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbhub/usbhub_private.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,163 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Hub driver.
+ */
+
+#ifndef USBHUB_PRIVATE_H
+#define	USBHUB_PRIVATE_H
+
+#include "usbhub.h"
+#include "usblist.h"
+
+#include <adt/list.h>
+#include <bool.h>
+#include <driver.h>
+#include <futex.h>
+
+#include <usb/usb.h>
+#include <usb/usbdrv.h>
+#include <usb/classes/hub.h>
+#include <usb/devreq.h>
+#include <usb/debug.h>
+
+//************
+//
+// convenience define for malloc
+//
+//************
+#define usb_new(type) (type*)malloc(sizeof(type))
+
+
+//************
+//
+// convenience debug printf
+//
+//************
+#define dprintf(level, format, ...) \
+	usb_dprintf(NAME, (level), format "\n", ##__VA_ARGS__)
+
+/**
+ * create hub structure instance
+ *
+ * Set the address and port count information most importantly.
+ *
+ * @param device
+ * @param hc host controller phone
+ * @return
+ */
+usb_hub_info_t * usb_create_hub_info(device_t * device, int hc);
+
+/** list of hubs maanged by this driver */
+extern usb_general_list_t usb_hub_list;
+
+/** lock for hub list*/
+extern futex_t usb_hub_list_lock;
+
+
+/**
+ * perform complete control read transaction
+ *
+ * manages all three steps of transaction: setup, read and finalize
+ * @param phone
+ * @param target
+ * @param request request for data
+ * @param rcvd_buffer received data
+ * @param rcvd_size
+ * @param actual_size actual size of received data
+ * @return error code
+ */
+int usb_drv_sync_control_read(
+    int phone, usb_target_t target,
+    usb_device_request_setup_packet_t * request,
+    void * rcvd_buffer, size_t rcvd_size, size_t * actual_size
+);
+
+/**
+ * perform complete control write transaction
+ *
+ * manages all three steps of transaction: setup, write and finalize
+ * @param phone
+ * @param target
+ * @param request request to send data
+ * @param sent_buffer
+ * @param sent_size
+ * @return error code
+ */
+int usb_drv_sync_control_write(
+    int phone, usb_target_t target,
+    usb_device_request_setup_packet_t * request,
+    void * sent_buffer, size_t sent_size
+);
+
+/**
+ * set the device request to be a get hub descriptor request.
+ * @warning the size is allways set to USB_HUB_MAX_DESCRIPTOR_SIZE
+ * @param request
+ * @param addr
+ */
+static inline void usb_hub_set_descriptor_request(
+usb_device_request_setup_packet_t * request
+){
+	request->index = 0;
+	request->request_type = USB_HUB_REQ_TYPE_GET_DESCRIPTOR;
+	request->request = USB_HUB_REQUEST_GET_DESCRIPTOR;
+	request->value_high = USB_DESCTYPE_HUB;
+	request->value_low = 0;
+	request->length = USB_HUB_MAX_DESCRIPTOR_SIZE;
+}
+
+static inline int usb_hub_clear_port_feature(int hc, usb_address_t address,
+    int port_index,
+    usb_hub_class_feature_t feature) {
+	usb_target_t target = {
+		.address = address,
+		.endpoint = 0
+	};
+	usb_device_request_setup_packet_t clear_request = {
+		.request_type = USB_HUB_REQ_TYPE_CLEAR_PORT_FEATURE,
+		.request = USB_DEVREQ_CLEAR_FEATURE,
+		.length = 0,
+		.index = port_index
+	};
+	clear_request.value = feature;
+	return usb_drv_psync_control_write(hc, target, &clear_request,
+	    sizeof(clear_request), NULL, 0);
+}
+
+
+
+#endif	/* USBHUB_PRIVATE_H */
+
+/**
+ * @}
+ */
Index: uspace/drv/usbhub/usblist.c
===================================================================
--- uspace/drv/usbhub/usblist.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbhub/usblist.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,80 @@
+/*
+ * 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 usb hub driver
+ * @{
+ */
+/** @file
+ * @brief usblist implementation
+ */
+#include <sys/types.h>
+
+#include "usbhub_private.h"
+
+
+usb_general_list_t * usb_lst_create(void) {
+	usb_general_list_t* result = usb_new(usb_general_list_t);
+	usb_lst_init(result);
+	return result;
+}
+
+void usb_lst_init(usb_general_list_t * lst) {
+	lst->prev = lst;
+	lst->next = lst;
+	lst->data = NULL;
+}
+
+void usb_lst_prepend(usb_general_list_t* item, void* data) {
+	usb_general_list_t* appended = usb_new(usb_general_list_t);
+	appended->data = data;
+	appended->next = item;
+	appended->prev = item->prev;
+	item->prev->next = appended;
+	item->prev = appended;
+}
+
+void usb_lst_append(usb_general_list_t* item, void* data) {
+	usb_general_list_t* appended = usb_new(usb_general_list_t);
+	appended->data = data;
+	appended->next = item->next;
+	appended->prev = item;
+	item->next->prev = appended;
+	item->next = appended;
+}
+
+void usb_lst_remove(usb_general_list_t* item) {
+	item->next->prev = item->prev;
+	item->prev->next = item->next;
+}
+
+
+
+/**
+ * @}
+ */
+
+
Index: uspace/drv/usbhub/usblist.h
===================================================================
--- uspace/drv/usbhub/usblist.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbhub/usblist.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+
+#ifndef USBLIST_H
+#define	USBLIST_H
+/** @addtogroup usb hub driver
+ * @{
+ */
+/** @file
+ * @brief HC driver and hub driver.
+ *
+ * My private list implementation; I did not like the original helenos list.
+ * This one does not depend on the structure of stored data and has
+ * much simpler and more straight-forward semantics.
+ */
+
+/**
+ * general list structure
+ */
+typedef struct usb_general_list{
+	void * data;
+	struct usb_general_list * prev, * next;
+} usb_general_list_t;
+
+/** create head of usb general list */
+usb_general_list_t * usb_lst_create(void);
+
+/** initialize head of usb general list */
+void usb_lst_init(usb_general_list_t * lst);
+
+
+/** is the list empty? */
+static inline bool usb_lst_empty(usb_general_list_t * lst){
+	return lst?(lst->next==lst):true;
+}
+
+/** append data behind item */
+void usb_lst_append(usb_general_list_t * lst, void * data);
+
+/** prepend data beore item */
+void usb_lst_prepend(usb_general_list_t * lst, void * data);
+
+/** remove list item from list */
+void usb_lst_remove(usb_general_list_t * item);
+
+/** get data o specified type from list item */
+#define usb_lst_get_data(item, type)  (type *) (item->data)
+
+/** get usb_hub_info_t data from list item */
+static inline usb_hub_info_t * usb_hub_lst_get_data(usb_general_list_t * item) {
+	return usb_lst_get_data(item,usb_hub_info_t);
+}
+
+
+/**
+ * @}
+ */
+
+
+
+#endif	/* USBLIST_H */
+
Index: uspace/drv/usbhub/utils.c
===================================================================
--- uspace/drv/usbhub/utils.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbhub/utils.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,207 @@
+/*
+ * 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 various utilities
+ */
+#include <driver.h>
+#include <bool.h>
+#include <errno.h>
+
+#include <usbhc_iface.h>
+#include <usb/usbdrv.h>
+#include <usb/descriptor.h>
+#include <usb/devreq.h>
+#include <usb/classes/hub.h>
+
+#include "usbhub.h"
+#include "usbhub_private.h"
+#include "port_status.h"
+
+
+size_t USB_HUB_MAX_DESCRIPTOR_SIZE = 71;
+
+//*********************************************
+//
+//  various utils
+//
+//*********************************************
+
+//hub descriptor 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) {
+		dprintf(1,"[usb_hub] wrong descriptor %x\n",sdescriptor[1]);
+		return NULL;
+	}
+
+	usb_hub_descriptor_t * result = usb_new(usb_hub_descriptor_t);
+	
+
+	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);
+	//printf("[usb_hub] getting removable devices data \n");
+	size_t i;
+	for (i = 0; i < var_size; ++i) {
+		result->devices_removable[i] = sdescriptor[7 + i];
+	}
+	return result;
+}
+
+//control transactions
+
+int usb_drv_sync_control_read(
+		int phone, usb_target_t target,
+		usb_device_request_setup_packet_t * request,
+		void * rcvd_buffer, size_t rcvd_size, size_t * actual_size
+		) {
+	usb_handle_t handle;
+	int opResult;
+	//setup
+	opResult = usb_drv_async_control_read_setup(phone, target,
+			request, sizeof (usb_device_request_setup_packet_t),
+			&handle);
+	if (opResult != EOK) {
+		return opResult;
+	}
+	opResult = usb_drv_async_wait_for(handle);
+	if (opResult != EOK) {
+		return opResult;
+	}
+	//read
+	opResult = usb_drv_async_control_read_data(phone, target,
+			rcvd_buffer, rcvd_size, actual_size,
+			&handle);
+	if (opResult != EOK) {
+		return opResult;
+	}
+	opResult = usb_drv_async_wait_for(handle);
+	if (opResult != EOK) {
+		return opResult;
+	}
+	//finalize
+	opResult = usb_drv_async_control_read_status(phone, target,
+			&handle);
+	if (opResult != EOK) {
+		return opResult;
+	}
+	opResult = usb_drv_async_wait_for(handle);
+	if (opResult != EOK) {
+		return opResult;
+	}
+	return EOK;
+}
+
+int usb_drv_sync_control_write(
+		int phone, usb_target_t target,
+		usb_device_request_setup_packet_t * request,
+		void * sent_buffer, size_t sent_size
+		) {
+	usb_handle_t handle;
+	int opResult;
+	//setup
+	opResult = usb_drv_async_control_write_setup(phone, target,
+			request, sizeof (usb_device_request_setup_packet_t),
+			&handle);
+	if (opResult != EOK) {
+		return opResult;
+	}
+	opResult = usb_drv_async_wait_for(handle);
+	if (opResult != EOK) {
+		return opResult;
+	}
+	//write
+	opResult = usb_drv_async_control_write_data(phone, target,
+			sent_buffer, sent_size,
+			&handle);
+	if (opResult != EOK) {
+		return opResult;
+	}
+	opResult = usb_drv_async_wait_for(handle);
+	if (opResult != EOK) {
+		return opResult;
+	}
+	//finalize
+	opResult = usb_drv_async_control_write_status(phone, target,
+			&handle);
+	if (opResult != EOK) {
+		return opResult;
+	}
+	opResult = usb_drv_async_wait_for(handle);
+	if (opResult != EOK) {
+		return opResult;
+	}
+	return EOK;
+}
+
+
+
+
+
+/**
+ * @}
+ */
Index: uspace/drv/usbkbd/Makefile
===================================================================
--- uspace/drv/usbkbd/Makefile	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbkbd/Makefile	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,37 @@
+#
+# 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.
+#
+
+USPACE_PREFIX = ../..
+LIBS = $(LIBDRV_PREFIX)/libdrv.a $(LIBUSB_PREFIX)/libusb.a
+EXTRA_CFLAGS += -I$(LIBDRV_PREFIX)/include -I$(LIBUSB_PREFIX)/include
+BINARY = usbkbd
+
+SOURCES = \
+	main.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/usbkbd/main.c
===================================================================
--- uspace/drv/usbkbd/main.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbkbd/main.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,357 @@
+/*
+ * 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.
+ */
+#include <usb/usbdrv.h>
+#include <driver.h>
+#include <ipc/driver.h>
+#include <ipc/kbd.h>
+#include <io/keycode.h>
+#include <io/console.h>
+#include <errno.h>
+#include <fibril.h>
+#include <usb/classes/hid.h>
+#include <usb/classes/hidparser.h>
+#include <usb/devreq.h>
+#include <usb/descriptor.h>
+
+#define BUFFER_SIZE 32
+#define NAME "usbkbd"
+
+#define GUESSED_POLL_ENDPOINT 1
+
+static void default_connection_handler(device_t *, ipc_callid_t, ipc_call_t *);
+static device_ops_t keyboard_ops = {
+	.default_handler = default_connection_handler
+};
+
+static int console_callback_phone = -1;
+
+/** Default handler for IPC methods not handled by DDF.
+ *
+ * @param dev Device handling the call.
+ * @param icallid Call id.
+ * @param icall Call data.
+ */
+void default_connection_handler(device_t *dev,
+    ipc_callid_t icallid, ipc_call_t *icall)
+{
+	sysarg_t method = IPC_GET_IMETHOD(*icall);
+
+	if (method == IPC_M_CONNECT_TO_ME) {
+		int callback = IPC_GET_ARG5(*icall);
+
+		if (console_callback_phone != -1) {
+			ipc_answer_0(icallid, ELIMIT);
+			return;
+		}
+
+		console_callback_phone = callback;
+		ipc_answer_0(icallid, EOK);
+		return;
+	}
+
+	ipc_answer_0(icallid, EINVAL);
+}
+
+#if 0
+static void send_key(int key, int type, wchar_t c) {
+	async_msg_4(console_callback_phone, KBD_EVENT, type, key,
+	    KM_NUM_LOCK, c);
+}
+#endif
+
+/*
+ * Callbacks for parser
+ */
+static void usbkbd_process_keycodes(const uint16_t *key_codes, size_t count,
+                                    void *arg)
+{
+
+}
+
+/*
+ * Kbd functions
+ */
+static int usbkbd_parse_descriptors(usb_hid_dev_kbd_t *kbd_dev,
+                                    const uint8_t *data, size_t size)
+{
+//	const uint8_t *pos = data;
+	
+//	// get the configuration descriptor (should be first)
+//	if (*pos != sizeof(usb_standard_configuration_descriptor_t)
+//	    || *(pos + 1) != USB_DESCTYPE_CONFIGURATION) {
+//		fprintf(stderr, "Wrong format of configuration descriptor");
+//		return EINVAL;
+//	}
+	
+//	usb_standard_configuration_descriptor_t config_descriptor;
+//	memcpy(&config_descriptor, pos, 
+//	    sizeof(usb_standard_configuration_descriptor_t));
+//	pos += sizeof(usb_standard_configuration_descriptor_t);
+	
+//	// parse other descriptors
+//	while (pos - data < size) {
+//		//uint8_t desc_size = *pos;
+//		uint8_t desc_type = *(pos + 1);
+//		switch (desc_type) {
+//		case USB_DESCTYPE_INTERFACE:
+//			break;
+//		case USB_DESCTYPE_ENDPOINT:
+//			break;
+//		case USB_DESCTYPE_HID:
+//			break;
+//		case USB_DESCTYPE_HID_REPORT:
+//			break;
+//		case USB_DESCTYPE_HID_PHYSICAL:
+//			break;
+//		}
+//	}
+	
+	return EOK;
+}
+
+static int usbkbd_get_descriptors(usb_hid_dev_kbd_t *kbd_dev)
+{
+	// get the first configuration descriptor (TODO: or some other??)
+	usb_standard_configuration_descriptor_t config_desc;
+	
+	int rc = usb_drv_req_get_bare_configuration_descriptor(
+	    kbd_dev->device->parent_phone, kbd_dev->address, 0, &config_desc);
+	
+	if (rc != EOK) {
+		return rc;
+	}
+	
+	// prepare space for all underlying descriptors
+	uint8_t *descriptors = (uint8_t *)malloc(config_desc.total_length);
+	if (descriptors == NULL) {
+		return ENOMEM;
+	}
+	
+	size_t transferred = 0;
+	// get full configuration descriptor
+	rc = usb_drv_req_get_full_configuration_descriptor(
+	    kbd_dev->device->parent_phone, kbd_dev->address, 0, descriptors,
+	    config_desc.total_length, &transferred);
+	
+	if (rc != EOK) {
+		return rc;
+	}
+	if (transferred != config_desc.total_length) {
+		return ELIMIT;
+	}
+	
+	rc = usbkbd_parse_descriptors(kbd_dev, descriptors, transferred);
+	free(descriptors);
+	
+	return rc;
+}
+
+static usb_hid_dev_kbd_t *usbkbd_init_device(device_t *dev)
+{
+	usb_hid_dev_kbd_t *kbd_dev = (usb_hid_dev_kbd_t *)malloc(
+			sizeof(usb_hid_dev_kbd_t));
+
+	if (kbd_dev == NULL) {
+		fprintf(stderr, NAME ": No memory!\n");
+		return NULL;
+	}
+
+	kbd_dev->device = dev;
+
+	// get phone to my HC and save it as my parent's phone
+	// TODO: maybe not a good idea if DDF will use parent_phone
+	kbd_dev->device->parent_phone = usb_drv_hc_connect_auto(dev, 0);
+
+	kbd_dev->address = usb_drv_get_my_address(dev->parent_phone,
+	    dev);
+
+	// doesn't matter now that we have no address
+//	if (kbd_dev->address < 0) {
+//		fprintf(stderr, NAME ": No device address!\n");
+//		free(kbd_dev);
+//		return NULL;
+//	}
+
+	// default endpoint
+	kbd_dev->poll_endpoint = GUESSED_POLL_ENDPOINT;
+	
+	/*
+	 * will need all descriptors:
+	 * 1) choose one configuration from configuration descriptors 
+	 *    (set it to the device)
+	 * 2) set endpoints from endpoint descriptors
+	 */
+
+	// TODO: get descriptors
+	usbkbd_get_descriptors(kbd_dev);
+	// TODO: parse descriptors and save endpoints
+
+	return kbd_dev;
+}
+
+static void usbkbd_process_interrupt_in(usb_hid_dev_kbd_t *kbd_dev,
+                                        uint8_t *buffer, size_t actual_size)
+{
+	/*
+	 * here, the parser will be called, probably with some callbacks
+	 * now only take last 6 bytes and process, i.e. send to kbd
+	 */
+
+	usb_hid_report_in_callbacks_t *callbacks =
+	    (usb_hid_report_in_callbacks_t *)malloc(
+		sizeof(usb_hid_report_in_callbacks_t));
+	callbacks->keyboard = usbkbd_process_keycodes;
+
+	usb_hid_parse_report(kbd_dev->parser, buffer, actual_size, callbacks, 
+	    NULL);
+}
+
+static void usbkbd_poll_keyboard(usb_hid_dev_kbd_t *kbd_dev)
+{
+	int rc;
+	usb_handle_t handle;
+	uint8_t buffer[BUFFER_SIZE];
+	size_t actual_size;
+	//usb_endpoint_t poll_endpoint = 1;
+
+//	usb_address_t my_address = usb_drv_get_my_address(dev->parent_phone,
+//	    dev);
+//	if (my_address < 0) {
+//		return;
+//	}
+
+	usb_target_t poll_target = {
+		.address = kbd_dev->address,
+		.endpoint = kbd_dev->poll_endpoint
+	};
+
+	while (true) {
+		async_usleep(1000 * 1000);
+		rc = usb_drv_async_interrupt_in(kbd_dev->device->parent_phone,
+		    poll_target, buffer, BUFFER_SIZE, &actual_size, &handle);
+
+		if (rc != EOK) {
+			continue;
+		}
+
+		rc = usb_drv_async_wait_for(handle);
+		if (rc != EOK) {
+			continue;
+		}
+
+		/*
+		 * If the keyboard answered with NAK, it returned no data.
+		 * This implies that no change happened since last query.
+		 */
+		if (actual_size == 0) {
+			continue;
+		}
+
+		/*
+		 * TODO: Process pressed keys.
+		 */
+		usbkbd_process_interrupt_in(kbd_dev, buffer, actual_size);
+	}
+
+	// not reached
+	assert(0);
+}
+
+static int usbkbd_fibril_device(void *arg)
+{
+	printf("!!! USB device fibril\n");
+
+	if (arg == NULL) {
+		printf("No device!\n");
+		return -1;
+	}
+
+	device_t *dev = (device_t *)arg;
+
+	// initialize device (get and process descriptors, get address, etc.)
+	usb_hid_dev_kbd_t *kbd_dev = usbkbd_init_device(dev);
+
+	usbkbd_poll_keyboard(kbd_dev);
+
+	return EOK;
+}
+
+static int usbkbd_add_device(device_t *dev)
+{
+	/* For now, fail immediately. */
+	//return ENOTSUP;
+
+	/*
+	 * When everything is okay, connect to "our" HC.
+	 *
+	 * Not supported yet, skip..
+	 */
+//	int phone = usb_drv_hc_connect_auto(dev, 0);
+//	if (phone < 0) {
+//		/*
+//		 * Connecting to HC failed, roll-back and announce
+//		 * failure.
+//		 */
+//		return phone;
+//	}
+
+//	dev->parent_phone = phone;
+
+	/*
+	 * Create new fibril for handling this keyboard
+	 */
+	fid_t fid = fibril_create(usbkbd_fibril_device, dev);
+	if (fid == 0) {
+		printf("%s: failed to start fibril for HID device\n", NAME);
+		return ENOMEM;
+	}
+	fibril_add_ready(fid);
+
+	dev->ops = &keyboard_ops;
+
+	add_device_to_class(dev, "keyboard");
+
+	/*
+	 * Hurrah, device is initialized.
+	 */
+	return EOK;
+}
+
+static driver_ops_t kbd_driver_ops = {
+	.add_device = usbkbd_add_device,
+};
+
+static driver_t kbd_driver = {
+	.name = NAME,
+	.driver_ops = &kbd_driver_ops
+};
+
+int main(int argc, char *argv[])
+{
+	return driver_main(&kbd_driver);
+}
Index: uspace/drv/usbkbd/usbkbd.ma
===================================================================
--- uspace/drv/usbkbd/usbkbd.ma	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/usbkbd/usbkbd.ma	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,4 @@
+10 usb&class=hid
+10 usb&class=HID
+10 usb&interface&class=HID
+10 usb&hid
Index: uspace/drv/vhc/Makefile
===================================================================
--- uspace/drv/vhc/Makefile	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/Makefile	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,52 @@
+#
+# 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.
+#
+
+USPACE_PREFIX = ../..
+LIBS = \
+	$(LIBUSB_PREFIX)/libusb.a \
+	$(LIBUSBVIRT_PREFIX)/libusbvirt.a \
+	$(LIBDRV_PREFIX)/libdrv.a
+EXTRA_CFLAGS += \
+	-I$(LIBUSBVIRT_PREFIX)/include \
+	-I$(LIBUSB_PREFIX)/include \
+	-I$(LIBDRV_PREFIX)/include
+BINARY = vhc
+
+SOURCES = \
+	hub/hub.c \
+	hub/virthub.c \
+	hub/virthubops.c \
+	conndev.c \
+	connhost.c \
+	debug.c \
+	devices.c \
+	hc.c \
+	hcd.c \
+	hub.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/vhc/conn.h
===================================================================
--- uspace/drv/vhc/conn.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/conn.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,58 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Connection handling of incoming calls.
+ */
+#ifndef VHCD_CONN_H_
+#define VHCD_CONN_H_
+
+#include <usb/usb.h>
+#include <usb/hcdhubd.h>
+#include <usbhc_iface.h>
+#include "vhcd.h"
+#include "devices.h"
+
+void connection_handler_host(sysarg_t);
+
+usb_hcd_transfer_ops_t vhc_transfer_ops;
+usbhc_iface_t vhc_iface;
+
+void address_init(void);
+
+
+void default_connection_handler(device_t *, ipc_callid_t, ipc_call_t *);
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/vhc/conndev.c
===================================================================
--- uspace/drv/vhc/conndev.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/conndev.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,115 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Connection handling of calls from virtual device (implementation).
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <usbvirt/hub.h>
+
+#include "conn.h"
+#include "hc.h"
+#include "hub.h"
+
+#define DEVICE_NAME_MAXLENGTH 32
+
+static int get_device_name(int phone, char *buffer, size_t len)
+{
+	ipc_call_t answer_data;
+	sysarg_t answer_rc;
+	aid_t req;
+	int rc;
+	
+	req = async_send_0(phone,
+	    IPC_M_USBVIRT_GET_NAME,
+	    &answer_data);
+	
+	rc = async_data_read_start(phone, buffer, len);
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		return EINVAL;
+	}
+	
+	async_wait_for(req, &answer_rc);
+	rc = (int)answer_rc;
+	
+	if (IPC_GET_ARG1(answer_data) < len) {
+		len = IPC_GET_ARG1(answer_data);
+	} else {
+		len--;
+	}
+	buffer[len] = 0;
+	
+	return rc;
+}
+
+/** Default handler for IPC methods not handled by DDF.
+ *
+ * @param dev Device handling the call.
+ * @param icallid Call id.
+ * @param icall Call data.
+ */
+void default_connection_handler(device_t *dev,
+    ipc_callid_t icallid, ipc_call_t *icall)
+{
+	sysarg_t method = IPC_GET_IMETHOD(*icall);
+
+	if (method == IPC_M_CONNECT_TO_ME) {
+		int callback = IPC_GET_ARG5(*icall);
+		virtdev_connection_t *dev
+		    = virtdev_add_device(callback);
+		if (!dev) {
+			ipc_answer_0(icallid, EEXISTS);
+			ipc_hangup(callback);
+			return;
+		}
+		ipc_answer_0(icallid, EOK);
+
+		char devname[DEVICE_NAME_MAXLENGTH + 1];
+		int rc = get_device_name(callback, devname, DEVICE_NAME_MAXLENGTH);
+
+		dprintf(0, "virtual device connected (name: %s)",
+		    rc == EOK ? devname : "<unknown>");
+
+		/* FIXME: destroy the device when the client disconnects. */
+
+		return;
+	}
+
+	ipc_answer_0(icallid, EINVAL);
+}
+
+
+/**
+ * @}
+ */
Index: uspace/drv/vhc/connhost.c
===================================================================
--- uspace/drv/vhc/connhost.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/connhost.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,299 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Connection handling of calls from host (implementation).
+ */
+#include <assert.h>
+#include <errno.h>
+#include <usb/usb.h>
+#include <usb/hcd.h>
+
+#include "vhcd.h"
+#include "conn.h"
+#include "hc.h"
+
+typedef struct {
+	usb_direction_t direction;
+	usbhc_iface_transfer_out_callback_t out_callback;
+	usbhc_iface_transfer_in_callback_t in_callback;
+	device_t *dev;
+	void *arg;
+} transfer_info_t;
+
+static void universal_callback(void *buffer, size_t size,
+    usb_transaction_outcome_t outcome, void *arg)
+{
+	transfer_info_t *transfer = (transfer_info_t *) arg;
+
+	switch (transfer->direction) {
+		case USB_DIRECTION_IN:
+			transfer->in_callback(transfer->dev,
+			    outcome, size,
+			    transfer->arg);
+			break;
+		case USB_DIRECTION_OUT:
+			transfer->out_callback(transfer->dev,
+			    outcome,
+			    transfer->arg);
+			break;
+		default:
+			assert(false && "unreachable");
+			break;
+	}
+
+	free(transfer);
+}
+
+static transfer_info_t *create_transfer_info(device_t *dev,
+    usb_direction_t direction, void *arg)
+{
+	transfer_info_t *transfer = malloc(sizeof(transfer_info_t));
+
+	transfer->direction = direction;
+	transfer->in_callback = NULL;
+	transfer->out_callback = NULL;
+	transfer->arg = arg;
+	transfer->dev = dev;
+
+	return transfer;
+}
+
+static int enqueue_transfer_out(device_t *dev,
+    usb_target_t target, usb_transfer_type_t transfer_type,
+    void *buffer, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	dprintf(3, "transfer OUT [%d.%d (%s); %zu]",
+	    target.address, target.endpoint,
+	    usb_str_transfer_type(transfer_type),
+	    size);
+
+	transfer_info_t *transfer
+	    = create_transfer_info(dev, USB_DIRECTION_OUT, arg);
+	transfer->out_callback = callback;
+
+	hc_add_transaction_to_device(false, target, transfer_type, buffer, size,
+	    universal_callback, transfer);
+
+	return EOK;
+}
+
+static int enqueue_transfer_setup(device_t *dev,
+    usb_target_t target, usb_transfer_type_t transfer_type,
+    void *buffer, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	dprintf(3, "transfer SETUP [%d.%d (%s); %zu]",
+	    target.address, target.endpoint,
+	    usb_str_transfer_type(transfer_type),
+	    size);
+
+	transfer_info_t *transfer
+	    = create_transfer_info(dev, USB_DIRECTION_OUT, arg);
+	transfer->out_callback = callback;
+
+	hc_add_transaction_to_device(true, target, transfer_type, buffer, size,
+	    universal_callback, transfer);
+
+	return EOK;
+}
+
+static int enqueue_transfer_in(device_t *dev,
+    usb_target_t target, usb_transfer_type_t transfer_type,
+    void *buffer, size_t size,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	dprintf(3, "transfer IN [%d.%d (%s); %zu]",
+	    target.address, target.endpoint,
+	    usb_str_transfer_type(transfer_type),
+	    size);
+
+	transfer_info_t *transfer
+	    = create_transfer_info(dev, USB_DIRECTION_IN, arg);
+	transfer->in_callback = callback;
+
+	hc_add_transaction_from_device(target, transfer_type, buffer, size,
+	    universal_callback, transfer);
+
+	return EOK;
+}
+
+
+static int interrupt_out(device_t *dev, usb_target_t target,
+    void *data, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return enqueue_transfer_out(dev, target, USB_TRANSFER_INTERRUPT,
+	    data, size,
+	    callback, arg);
+}
+
+static int interrupt_in(device_t *dev, usb_target_t target,
+    void *data, size_t size,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	return enqueue_transfer_in(dev, target, USB_TRANSFER_INTERRUPT,
+	    data, size,
+	    callback, arg);
+}
+
+static int control_write_setup(device_t *dev, usb_target_t target,
+    void *data, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return enqueue_transfer_setup(dev, target, USB_TRANSFER_CONTROL,
+	    data, size,
+	    callback, arg);
+}
+
+static int control_write_data(device_t *dev, usb_target_t target,
+    void *data, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return enqueue_transfer_out(dev, target, USB_TRANSFER_CONTROL,
+	    data, size,
+	    callback, arg);
+}
+
+static int control_write_status(device_t *dev, usb_target_t target,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	return enqueue_transfer_in(dev, target, USB_TRANSFER_CONTROL,
+	    NULL, 0,
+	    callback, arg);
+}
+
+static int control_read_setup(device_t *dev, usb_target_t target,
+    void *data, size_t size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return enqueue_transfer_setup(dev, target, USB_TRANSFER_CONTROL,
+	    data, size,
+	    callback, arg);
+}
+
+static int control_read_data(device_t *dev, usb_target_t target,
+    void *data, size_t size,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	return enqueue_transfer_in(dev, target, USB_TRANSFER_CONTROL,
+	    data, size,
+	    callback, arg);
+}
+
+static int control_read_status(device_t *dev, usb_target_t target,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	return enqueue_transfer_out(dev, target, USB_TRANSFER_CONTROL,
+	    NULL, 0,
+	    callback, arg);
+}
+
+static usb_address_keeping_t addresses;
+
+
+static int reserve_default_address(device_t *dev)
+{
+	usb_address_keeping_reserve_default(&addresses);
+	return EOK;
+}
+
+static int release_default_address(device_t *dev)
+{
+	usb_address_keeping_release_default(&addresses);
+	return EOK;
+}
+
+static int request_address(device_t *dev, usb_address_t *address)
+{
+	usb_address_t addr = usb_address_keeping_request(&addresses);
+	if (addr < 0) {
+		return (int)addr;
+	}
+
+	*address = addr;
+	return EOK;
+}
+
+static int release_address(device_t *dev, usb_address_t address)
+{
+	return usb_address_keeping_release(&addresses, address);
+}
+
+static int bind_address(device_t *dev, usb_address_t address,
+    devman_handle_t handle)
+{
+	usb_address_keeping_devman_bind(&addresses, address, handle);
+	return EOK;
+}
+
+static int tell_address(device_t *dev, devman_handle_t handle,
+    usb_address_t *address)
+{
+	usb_address_t addr = usb_address_keeping_find(&addresses, handle);
+	if (addr < 0) {
+		return addr;
+	}
+
+	*address = addr;
+	return EOK;
+}
+
+void address_init(void)
+{
+	usb_address_keeping_init(&addresses, 50);
+}
+
+usbhc_iface_t vhc_iface = {
+	.tell_address = tell_address,
+
+	.reserve_default_address = reserve_default_address,
+	.release_default_address = release_default_address,
+	.request_address = request_address,
+	.bind_address = bind_address,
+	.release_address = release_address,
+
+	.interrupt_out = interrupt_out,
+	.interrupt_in = interrupt_in,
+
+	.control_write_setup = control_write_setup,
+	.control_write_data = control_write_data,
+	.control_write_status = control_write_status,
+
+	.control_read_setup = control_read_setup,
+	.control_read_data = control_read_data,
+	.control_read_status = control_read_status
+};
+
+/**
+ * @}
+ */
Index: uspace/drv/vhc/debug.c
===================================================================
--- uspace/drv/vhc/debug.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/debug.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,55 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Debugging support.
+ */
+#include <stdio.h>
+#include <ipc/ipc.h>
+#include <usb/debug.h>
+
+#include "vhcd.h"
+
+
+/** Debug print informing of invalid call.
+ */
+void dprintf_inval_call(int level, ipc_call_t call, sysarg_t phone_hash)
+{
+	dprintf(level, "phone%#x: invalid call [%u (%u, %u, %u, %u, %u)]",
+	    phone_hash,
+	    IPC_GET_IMETHOD(call),
+	    IPC_GET_ARG1(call), IPC_GET_ARG2(call), IPC_GET_ARG3(call),
+	    IPC_GET_ARG4(call), IPC_GET_ARG5(call));
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/vhc/devices.c
===================================================================
--- uspace/drv/vhc/devices.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/devices.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,190 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Virtual device management (implementation).
+ */
+
+#include <ipc/ipc.h>
+#include <adt/list.h>
+#include <bool.h>
+#include <async.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+
+#include <usbvirt/hub.h>
+
+#include "devices.h"
+#include "hub.h"
+#include "hub/virthub.h"
+#include "vhcd.h"
+
+#define list_foreach(pos, head) \
+	for (pos = (head)->next; pos != (head); \
+        	pos = pos->next)
+
+static LIST_INITIALIZE(devices);
+
+/** Create virtual device.
+ *
+ * @param address USB address.
+ * @param phone Callback phone.
+ * @return New device.
+ * @retval NULL Out of memory or address already occupied.
+ */
+virtdev_connection_t *virtdev_add_device(int phone)
+{
+	virtdev_connection_t *dev = (virtdev_connection_t *)
+	    malloc(sizeof(virtdev_connection_t));
+	dev->phone = phone;
+	list_append(&dev->link, &devices);
+	
+	virthub_connect_device(&virtual_hub_device, dev);
+	
+	return dev;
+}
+
+/** Destroy virtual device.
+ */
+void virtdev_destroy_device(virtdev_connection_t *dev)
+{
+	virthub_disconnect_device(&virtual_hub_device, dev);
+	list_remove(&dev->link);
+	free(dev);
+}
+
+/** Send data to all connected devices.
+ *
+ * @param transaction Transaction to be sent over the bus.
+ */
+usb_transaction_outcome_t virtdev_send_to_all(transaction_t *transaction)
+{
+	link_t *pos;
+	list_foreach(pos, &devices) {
+		virtdev_connection_t *dev
+		    = list_get_instance(pos, virtdev_connection_t, link);
+		
+		if (!virthub_is_device_enabled(&virtual_hub_device, dev)) {
+			continue;
+		}
+		
+		ipc_call_t answer_data;
+		sysarg_t answer_rc;
+		aid_t req;
+		int rc = EOK;
+		int method = IPC_M_USBVIRT_TRANSACTION_SETUP;
+		
+		switch (transaction->type) {
+			case USBVIRT_TRANSACTION_SETUP:
+				method = IPC_M_USBVIRT_TRANSACTION_SETUP;
+				break;
+			case USBVIRT_TRANSACTION_IN:
+				method = IPC_M_USBVIRT_TRANSACTION_IN;
+				break;
+			case USBVIRT_TRANSACTION_OUT:
+				method = IPC_M_USBVIRT_TRANSACTION_OUT;
+				break;
+		}
+		
+		req = async_send_3(dev->phone,
+		    method,
+		    transaction->target.address,
+		    transaction->target.endpoint,
+		    transaction->len,
+		    &answer_data);
+		
+		if (transaction->len > 0) {
+			if (transaction->type == USBVIRT_TRANSACTION_IN) {
+				rc = async_data_read_start(dev->phone,
+				    transaction->buffer, transaction->len);
+			} else {
+				rc = async_data_write_start(dev->phone,
+				    transaction->buffer, transaction->len);
+			}
+		}
+		
+		if (rc != EOK) {
+			async_wait_for(req, NULL);
+		} else {
+			async_wait_for(req, &answer_rc);
+			rc = (int)answer_rc;
+		}
+	}
+	
+	/*
+	 * Send the data to the virtual hub as well
+	 * (if the address matches).
+	 */
+	if (virtual_hub_device.address == transaction->target.address) {
+		size_t tmp;
+		dprintf(1, "sending `%s' transaction to hub",
+		    usbvirt_str_transaction_type(transaction->type));
+		switch (transaction->type) {
+			case USBVIRT_TRANSACTION_SETUP:
+				virtual_hub_device.transaction_setup(
+				    &virtual_hub_device,
+				    transaction->target.endpoint,
+				    transaction->buffer, transaction->len);
+				break;
+				
+			case USBVIRT_TRANSACTION_IN:
+				virtual_hub_device.transaction_in(
+				    &virtual_hub_device,
+				    transaction->target.endpoint,
+				    transaction->buffer, transaction->len,
+				    &tmp);
+				if (tmp < transaction->len) {
+					transaction->len = tmp;
+				}
+				break;
+				
+			case USBVIRT_TRANSACTION_OUT:
+				virtual_hub_device.transaction_out(
+				    &virtual_hub_device,
+				    transaction->target.endpoint,
+				    transaction->buffer, transaction->len);
+				break;
+		}
+		dprintf(4, "transaction on hub processed...");
+	}
+	
+	/*
+	 * TODO: maybe screw some transactions to get more
+	 * real-life image.
+	 */
+	return USB_OUTCOME_OK;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/vhc/devices.h
===================================================================
--- uspace/drv/vhc/devices.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/devices.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,59 @@
+/*
+ * 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 usb
+ * @{
+ */ 
+/** @file
+ * @brief Virtual device management.
+ */
+#ifndef VHCD_DEVICES_H_
+#define VHCD_DEVICES_H_
+
+#include <adt/list.h>
+#include <usb/usb.h>
+
+#include "hc.h"
+
+/** Connected virtual device. */
+typedef struct {
+	/** Phone used when sending data to device. */
+	int phone;
+	/** Linked-list handle. */
+	link_t link;
+} virtdev_connection_t;
+
+virtdev_connection_t *virtdev_add_device(int);
+virtdev_connection_t *virtdev_get_mine(void);
+void virtdev_destroy_device(virtdev_connection_t *);
+usb_transaction_outcome_t virtdev_send_to_all(transaction_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/vhc/hc.c
===================================================================
--- uspace/drv/vhc/hc.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/hc.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,209 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Virtual HC (implementation).
+ */
+
+#include <ipc/ipc.h>
+#include <adt/list.h>
+#include <bool.h>
+#include <async.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+
+#include <usbvirt/hub.h>
+
+#include "vhcd.h"
+#include "hc.h"
+#include "devices.h"
+#include "hub.h"
+
+#define USLEEP_BASE (0 * 5 * 1000)
+
+#define USLEEP_VAR 50
+
+#define SHORTENING_VAR 15
+
+#define PROB_OUTCOME_BABBLE 5
+#define PROB_OUTCOME_CRCERROR 7
+
+#define PROB_TEST(var, new_value, prob, number) \
+	do { \
+		if (((number) % (prob)) == 0) { \
+			var = (new_value); \
+		} \
+	} while (0)
+
+static link_t transaction_list;
+
+#define TRANSACTION_FORMAT "T[%d.%d %s/%s (%d)]"
+#define TRANSACTION_PRINTF(t) \
+	(t).target.address, (t).target.endpoint, \
+	usb_str_transfer_type((t).transfer_type), \
+	usbvirt_str_transaction_type((t).type), \
+	(int)(t).len
+
+#define transaction_get_instance(lnk) \
+	list_get_instance(lnk, transaction_t, link)
+
+#define HUB_STATUS_MAX_LEN (HUB_PORT_COUNT + 64)
+
+static inline unsigned int pseudo_random(unsigned int *seed)
+{
+	*seed = ((*seed) * 873511) % 22348977 + 7;
+	return ((*seed) >> 8);
+}
+
+/** Call transaction callback.
+ * Calling this callback informs the backend that transaction was processed.
+ */
+static void process_transaction_with_outcome(transaction_t * transaction,
+    usb_transaction_outcome_t outcome)
+{
+	dprintf(3, "transaction " TRANSACTION_FORMAT " done, outcome: %s",
+	    TRANSACTION_PRINTF(*transaction),
+	    usb_str_transaction_outcome(outcome));
+	
+	transaction->callback(transaction->buffer, transaction->len, outcome,
+	    transaction->callback_arg);
+}
+
+/** Host controller manager main function.
+ */
+static int hc_manager_fibril(void *arg)
+{
+	list_initialize(&transaction_list);
+	
+	static unsigned int seed = 4573;
+	
+	printf("%s: transaction processor ready.\n", NAME);
+	
+	while (true) {
+		async_usleep(USLEEP_BASE + (pseudo_random(&seed) % USLEEP_VAR));
+		
+		if (list_empty(&transaction_list)) {
+			continue;
+		}
+		
+		char ports[HUB_STATUS_MAX_LEN + 1];
+		virthub_get_status(&virtual_hub_device, ports, HUB_STATUS_MAX_LEN);
+		
+		link_t *first_transaction_link = transaction_list.next;
+		transaction_t *transaction
+		    = transaction_get_instance(first_transaction_link);
+		list_remove(first_transaction_link);
+		
+
+		dprintf(0, "about to process " TRANSACTION_FORMAT " [%s]",
+		    TRANSACTION_PRINTF(*transaction), ports);
+
+		dprintf(3, "processing transaction " TRANSACTION_FORMAT "",
+		    TRANSACTION_PRINTF(*transaction));
+		
+		usb_transaction_outcome_t outcome;
+		outcome = virtdev_send_to_all(transaction);
+		
+		process_transaction_with_outcome(transaction, outcome);
+
+		free(transaction);
+	}
+
+	assert(false && "unreachable");
+	return EOK;
+}
+
+void hc_manager(void)
+{
+	fid_t fid = fibril_create(hc_manager_fibril, NULL);
+	if (fid == 0) {
+		printf(NAME ": failed to start HC manager fibril\n");
+		return;
+	}
+	fibril_add_ready(fid);
+}
+
+/** Create new transaction
+ */
+static transaction_t *transaction_create(usbvirt_transaction_type_t type,
+    usb_target_t target, usb_transfer_type_t transfer_type,
+    void * buffer, size_t len,
+    hc_transaction_done_callback_t callback, void * arg)
+{
+	transaction_t * transaction = malloc(sizeof(transaction_t));
+	
+	list_initialize(&transaction->link);
+	transaction->type = type;
+	transaction->transfer_type = transfer_type;
+	transaction->target = target;
+	transaction->buffer = buffer;
+	transaction->len = len;
+	transaction->callback = callback;
+	transaction->callback_arg = arg;
+	
+	dprintf(3, "creating transaction " TRANSACTION_FORMAT,
+	    TRANSACTION_PRINTF(*transaction));
+	
+	return transaction;
+}
+
+/** Add transaction directioned towards the device.
+ */
+void hc_add_transaction_to_device(bool setup, usb_target_t target,
+    usb_transfer_type_t transfer_type,
+    void * buffer, size_t len,
+    hc_transaction_done_callback_t callback, void * arg)
+{
+	transaction_t *transaction = transaction_create(
+	    setup ? USBVIRT_TRANSACTION_SETUP : USBVIRT_TRANSACTION_OUT,
+	    target, transfer_type,
+	    buffer, len, callback, arg);
+	list_append(&transaction->link, &transaction_list);
+}
+
+/** Add transaction directioned from the device.
+ */
+void hc_add_transaction_from_device(usb_target_t target,
+    usb_transfer_type_t transfer_type,
+    void * buffer, size_t len,
+    hc_transaction_done_callback_t callback, void * arg)
+{
+	transaction_t *transaction = transaction_create(USBVIRT_TRANSACTION_IN,
+	    target, transfer_type,
+	    buffer, len, callback, arg);
+	list_append(&transaction->link, &transaction_list);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/vhc/hc.h
===================================================================
--- uspace/drv/vhc/hc.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/hc.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,89 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Virtual HC.
+ */
+#ifndef VHCD_HC_H_
+#define VHCD_HC_H_
+
+#include <usb/usb.h>
+#include <usbvirt/hub.h>
+
+/** Callback after transaction is sent to USB.
+ *
+ * @param buffer Transaction data buffer.
+ * @param size Transaction data size.
+ * @param outcome Transaction outcome.
+ * @param arg Custom argument.
+ */
+typedef void (*hc_transaction_done_callback_t)(void *buffer, size_t size,
+    usb_transaction_outcome_t outcome, void *arg);
+
+/** Pending transaction details. */
+typedef struct {
+	/** Linked-list link. */
+	link_t link;
+	/** Transaction type. */
+	usbvirt_transaction_type_t type;
+	/** Transfer type. */
+	usb_transfer_type_t transfer_type;
+	/** Device address. */
+	usb_target_t target;
+	/** Direction of the transaction. */
+	usb_direction_t direction;
+	/** Transaction data buffer. */
+	void * buffer;
+	/** Transaction data length. */
+	size_t len;
+	/** Callback after transaction is done. */
+	hc_transaction_done_callback_t callback;
+	/** Argument to the callback. */
+	void * callback_arg;
+} transaction_t;
+
+void hc_manager(void);
+
+void hc_add_transaction_to_device(bool setup,
+    usb_target_t target, usb_transfer_type_t transfer_type,
+    void * buffer, size_t len,
+    hc_transaction_done_callback_t callback, void * arg);
+
+void hc_add_transaction_from_device(
+    usb_target_t target, usb_transfer_type_t transfer_type,
+    void * buffer, size_t len,
+    hc_transaction_done_callback_t callback, void * arg);
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/vhc/hcd.c
===================================================================
--- uspace/drv/vhc/hcd.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/hcd.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,141 @@
+/*
+ * 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 usb
+ * @{
+ */ 
+/** @file
+ * @brief Virtual host controller driver.
+ */
+
+#include <devmap.h>
+#include <ipc/ipc.h>
+#include <async.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sysinfo.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+#include <driver.h>
+
+#include <usb/usb.h>
+#include <usb_iface.h>
+#include "vhcd.h"
+#include "hc.h"
+#include "devices.h"
+#include "hub.h"
+#include "conn.h"
+
+static int usb_iface_get_hc_handle(device_t *dev, devman_handle_t *handle)
+{
+	/* This shall be called only for VHC device. */
+	assert(dev->parent == NULL);
+
+	*handle = dev->handle;
+	return EOK;
+}
+
+static usb_iface_t hc_usb_iface = {
+	.get_hc_handle = usb_iface_get_hc_handle
+};
+
+static device_ops_t vhc_ops = {
+	.interfaces[USBHC_DEV_IFACE] = &vhc_iface,
+	.interfaces[USB_DEV_IFACE] = &hc_usb_iface,
+	.default_handler = default_connection_handler
+};
+
+static int vhc_count = 0;
+static int vhc_add_device(device_t *dev)
+{
+	/*
+	 * Currently, we know how to simulate only single HC.
+	 */
+	if (vhc_count > 0) {
+		return ELIMIT;
+	}
+
+	vhc_count++;
+
+	dev->ops = &vhc_ops;
+
+	devman_add_device_to_class(dev->handle, "usbhc");
+
+	/*
+	 * Initialize our hub and announce its presence.
+	 */
+	virtual_hub_device_init(dev);
+
+	printf("%s: virtual USB host controller ready.\n", NAME);
+
+	return EOK;
+}
+
+static driver_ops_t vhc_driver_ops = {
+	.add_device = vhc_add_device,
+};
+
+static driver_t vhc_driver = {
+	.name = NAME,
+	.driver_ops = &vhc_driver_ops
+};
+
+
+int main(int argc, char * argv[])
+{	
+	/*
+	 * Temporary workaround. Wait a little bit to be the last driver
+	 * in devman output.
+	 */
+	sleep(5);
+
+	usb_dprintf_enable(NAME, 0);
+
+	printf(NAME ": virtual USB host controller driver.\n");
+
+	/*
+	 * Initialize address management.
+	 */
+	address_init();
+
+	/*
+	 * Run the transfer scheduler.
+	 */
+	hc_manager();
+
+	/*
+	 * We are also a driver within devman framework.
+	 */
+	return driver_main(&vhc_driver);
+}
+
+
+/**
+ * @}
+ */
Index: uspace/drv/vhc/hub.c
===================================================================
--- uspace/drv/vhc/hub.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/hub.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,104 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Virtual USB hub.
+ */
+#include <usb/classes/classes.h>
+#include <usbvirt/hub.h>
+#include <usbvirt/device.h>
+#include <errno.h>
+#include <str_error.h>
+#include <stdlib.h>
+#include <driver.h>
+#include <usb/usbdrv.h>
+
+#include "hub.h"
+#include "hub/virthub.h"
+#include "vhcd.h"
+
+usbvirt_device_t virtual_hub_device;
+
+static int hub_register_in_devman_fibril(void *arg);
+
+void virtual_hub_device_init(device_t *hc_dev)
+{
+	virthub_init(&virtual_hub_device);
+
+	/*
+	 * We need to register the root hub.
+	 * This must be done in separate fibril because the device
+	 * we are connecting to are ourselves and we cannot connect
+	 * before leaving the add_device() function.
+	 */
+	fid_t root_hub_registration
+	    = fibril_create(hub_register_in_devman_fibril, hc_dev);
+	if (root_hub_registration == 0) {
+		printf(NAME ": failed to register root hub\n");
+		return;
+	}
+
+	fibril_add_ready(root_hub_registration);
+}
+
+/** Register root hub in devman.
+ *
+ * @param arg Host controller device (type <code>device_t *</code>).
+ * @return Error code.
+ */
+int hub_register_in_devman_fibril(void *arg)
+{
+	device_t *hc_dev = (device_t *) arg;
+
+	int hc = usb_drv_hc_connect(hc_dev, hc_dev->handle, IPC_FLAG_BLOCKING);
+	if (hc < 0) {
+		printf(NAME ": failed to register root hub\n");
+		return hc;
+	}
+
+	usb_drv_reserve_default_address(hc);
+
+	usb_address_t hub_address = usb_drv_request_address(hc);
+	usb_drv_req_set_address(hc, USB_ADDRESS_DEFAULT, hub_address);
+
+	usb_drv_release_default_address(hc);
+
+	devman_handle_t hub_handle;
+	usb_drv_register_child_in_devman(hc, hc_dev, hub_address, &hub_handle);
+	usb_drv_bind_address(hc, hub_address, hub_handle);
+
+	return EOK;
+}
+	
+
+/**
+ * @}
+ */
Index: uspace/drv/vhc/hub.h
===================================================================
--- uspace/drv/vhc/hub.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/hub.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,52 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Virtual USB hub.
+ */
+#ifndef VHCD_HUB_H_
+#define VHCD_HUB_H_
+
+#include <usbvirt/device.h>
+#include <driver.h>
+
+#include "devices.h"
+#include "hub/hub.h"
+#include "hub/virthub.h"
+
+extern usbvirt_device_t virtual_hub_device;
+
+void virtual_hub_device_init(device_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/vhc/hub/hub.c
===================================================================
--- uspace/drv/vhc/hub/hub.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/hub/hub.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,489 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Representation of an USB hub (implementation).
+ */
+#include <usb/classes/classes.h>
+#include <usbvirt/hub.h>
+#include <usbvirt/device.h>
+#include <errno.h>
+#include <str_error.h>
+#include <stdlib.h>
+#include <driver.h>
+#include <usb/usbdrv.h>
+
+#include "hub.h"
+
+
+/** Produce a byte from bit values.
+ */
+#define MAKE_BYTE(b0, b1, b2, b3, b4, b5, b6, b7) \
+	(( \
+		((b0) << 0) \
+		| ((b1) << 1) \
+		| ((b2) << 2) \
+		| ((b3) << 3) \
+		| ((b4) << 4) \
+		| ((b5) << 5) \
+		| ((b6) << 6) \
+		| ((b7) << 7) \
+	))
+
+/* Static functions. */
+static hub_port_t *get_hub_port(hub_t *, size_t);
+static void set_port_status_change(hub_port_t *, hub_status_change_t);
+static void clear_port_status_change(hub_port_t *, uint16_t);
+static int set_port_state_delayed_fibril(void *);
+static void set_port_state_delayed(hub_t *, size_t, suseconds_t,
+    hub_port_state_t, hub_port_state_t);
+
+/** Convert hub port state to a char. */
+char hub_port_state_to_char(hub_port_state_t state) {
+	switch (state) {
+		case HUB_PORT_STATE_NOT_CONFIGURED:
+			return '-';
+		case HUB_PORT_STATE_POWERED_OFF:
+			return 'O';
+		case HUB_PORT_STATE_DISCONNECTED:
+			return 'X';
+		case HUB_PORT_STATE_DISABLED:
+			return 'D';
+		case HUB_PORT_STATE_RESETTING:
+			return 'R';
+		case HUB_PORT_STATE_ENABLED:
+			return 'E';
+		case HUB_PORT_STATE_SUSPENDED:
+			return 'S';
+		case HUB_PORT_STATE_RESUMING:
+			return 'F';
+		default:
+			return '?';
+	}
+}
+
+/** Initialize single hub port.
+ *
+ * @param port Port to be initialized.
+ * @param index Port index (one based).
+ */
+static void hub_init_port(hub_port_t *port, size_t index)
+{
+	port->connected_device = NULL;
+	port->index = index;
+	port->state = HUB_PORT_STATE_NOT_CONFIGURED;
+	port->status_change = 0;
+}
+
+/** Initialize the hub.
+ *
+ * @param hub Hub to be initialized.
+ */
+void hub_init(hub_t *hub)
+{
+	size_t i;
+	for (i = 0; i < HUB_PORT_COUNT; i++) {
+		hub_init_port(&hub->ports[i], i + 1);
+	}
+	hub->custom_data = NULL;
+	fibril_mutex_initialize(&hub->guard);
+}
+
+/** Connect a device to the hub.
+ *
+ * @param hub Hub to connect device to.
+ * @param device Device to be connected.
+ * @return Index of port the device was connected to.
+ * @retval -1 No free port available.
+ */
+size_t hub_connect_device(hub_t *hub, void *device)
+{
+	size_t i;
+	for (i = 0; i < HUB_PORT_COUNT; i++) {
+		hub_port_t *port = &hub->ports[i];
+
+		if (port->connected_device != NULL) {
+			continue;
+		}
+
+		port->connected_device = device;
+
+		/*
+		 * TODO:
+		 * If the hub was configured, we can normally
+		 * announce the plug-in.
+		 * Otherwise, we will wait until hub is configured
+		 * and announce changes in single burst.
+		 */
+		//if (port->state == HUB_PORT_STATE_DISCONNECTED) {
+			port->state = HUB_PORT_STATE_DISABLED;
+			set_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
+		//}
+
+		return i;
+	}
+
+	return (size_t) -1;
+}
+
+/** Find port device is connected to.
+ *
+ * @param hub Hub in question.
+ * @param device Device in question.
+ * @return Port index (zero based).
+ * @retval -1 Device is not connected.
+ */
+size_t hub_find_device(hub_t *hub, void *device)
+{
+	size_t i;
+	for (i = 0; i < HUB_PORT_COUNT; i++) {
+		hub_port_t *port = &hub->ports[i];
+
+		if (port->connected_device == device) {
+			return i;
+		}
+	}
+
+	return 0;
+}
+
+/** Acquire exclusive access to the hub.
+ *
+ * @param hub Hub in question.
+ */
+void hub_acquire(hub_t *hub)
+{
+	fibril_mutex_lock(&hub->guard);
+}
+
+/** Give up exclusive access to the hub.
+ *
+ * @param hub Hub in question.
+ */
+void hub_release(hub_t *hub)
+{
+	fibril_mutex_unlock(&hub->guard);
+}
+
+/** Change port state.
+ *
+ * @param hub Hub the port belongs to.
+ * @param port_index Port index (zero based).
+ * @param state New port state.
+ */
+void hub_set_port_state(hub_t *hub, size_t port_index, hub_port_state_t state)
+{
+	hub_port_t *port = get_hub_port(hub, port_index);
+	if (port == NULL) {
+		return;
+	}
+
+	switch (state) {
+		case HUB_PORT_STATE_POWERED_OFF:
+			clear_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
+			clear_port_status_change(port, HUB_STATUS_C_PORT_ENABLE);
+			clear_port_status_change(port, HUB_STATUS_C_PORT_RESET);
+			break;
+		case HUB_PORT_STATE_RESUMING:
+			set_port_state_delayed(hub, port_index,
+			    10, state, HUB_PORT_STATE_ENABLED);
+			break;
+		case HUB_PORT_STATE_RESETTING:
+			set_port_state_delayed(hub, port_index,
+			    10, state, HUB_PORT_STATE_ENABLED);
+			break;
+		case HUB_PORT_STATE_ENABLED:
+			if (port->state == HUB_PORT_STATE_RESETTING) {
+				set_port_status_change(port, HUB_STATUS_C_PORT_RESET);
+			}
+			break;
+		default:
+			break;
+	}
+
+	port->state = state;
+}
+
+/** Change state of all ports.
+ *
+ * @param hub Hub in question.
+ * @param state New state for all ports.
+ */
+void hub_set_port_state_all(hub_t *hub, hub_port_state_t state)
+{
+	size_t i;
+	for (i = 0; i < HUB_PORT_COUNT; i++) {
+		hub_set_port_state(hub, i, state);
+	}
+}
+
+/** Get port state.
+ *
+ * @param hub Hub the port belongs to.
+ * @param port_index Port index (zero based).
+ * @return Port state.
+ */
+hub_port_state_t hub_get_port_state(hub_t *hub, size_t port_index)
+{
+	hub_port_t *port = get_hub_port(hub, port_index);
+	if (port == NULL) {
+		return HUB_PORT_STATE_UNKNOWN;
+	}
+
+	return port->state;
+}
+
+/** Clear port status change bit.
+ *
+ * @param hub Hub the port belongs to.
+ * @param port_index Port index (zero based).
+ * @param change Change to be cleared.
+ */
+void hub_clear_port_status_change(hub_t *hub, size_t port_index,
+    hub_status_change_t change)
+{
+	hub_port_t *port = get_hub_port(hub, port_index);
+	if (port == NULL) {
+		return;
+	}
+
+	clear_port_status_change(port, change);
+}
+
+/** Get port status change bits.
+ *
+ * @param hub Hub the port belongs to.
+ * @param port_index Port index (zero based).
+ * @return Port status change bitmap in standard format.
+ */
+uint16_t hub_get_port_status_change(hub_t *hub, size_t port_index)
+{
+	hub_port_t *port = get_hub_port(hub, port_index);
+	if (port == NULL) {
+		return 0;
+	}
+
+	return port->status_change;
+}
+
+/** Get port status bits.
+ *
+ * @param hub Hub the port belongs to.
+ * @param port_index Port index (zero based).
+ * @return Port status bitmap in standard format.
+ */
+uint32_t hub_get_port_status(hub_t *hub, size_t port_index)
+{
+	hub_port_t *port = get_hub_port(hub, port_index);
+	if (port == NULL) {
+		return 0;
+	}
+
+	uint32_t status;
+	status = MAKE_BYTE(
+	    /* Current connect status. */
+	    port->connected_device == NULL ? 0 : 1,
+	    /* Port enabled/disabled. */
+	    port->state == HUB_PORT_STATE_ENABLED ? 1 : 0,
+	    /* Suspend. */
+	    (port->state == HUB_PORT_STATE_SUSPENDED)
+		|| (port->state == HUB_PORT_STATE_RESUMING) ? 1 : 0,
+	    /* Over-current. */
+	    0,
+	    /* Reset. */
+	    port->state == HUB_PORT_STATE_RESETTING ? 1 : 0,
+	    /* Reserved. */
+	    0, 0, 0)
+
+	    | (MAKE_BYTE(
+	    /* Port power. */
+	    port->state == HUB_PORT_STATE_POWERED_OFF ? 0 : 1,
+	    /* Full-speed device. */
+	    0,
+	    /* Reserved. */
+	    0, 0, 0, 0, 0, 0
+	    )) << 8;
+
+	status |= (port->status_change << 16);
+
+	return status;
+}
+
+/** Create hub status change bitmap.
+ *
+ * @warning This function assumes that the whole bitmap fits into 8 bits.
+ *
+ * @param hub Hub in question.
+ * @return Hub status change bitmap.
+ */
+uint8_t hub_get_status_change_bitmap(hub_t *hub)
+{
+	uint8_t change_map = 0;
+
+	size_t i;
+	for (i = 0; i < HUB_PORT_COUNT; i++) {
+		hub_port_t *port = &hub->ports[i];
+
+		if (port->status_change != 0) {
+			change_map |= (1 << port->index);
+		}
+	}
+
+	return change_map;
+}
+
+
+/*
+ *
+ * STATIC (HELPER) FUNCTIONS
+ *
+ */
+
+/** Find a port in a hub.
+ *
+ * @param hub Hub in question.
+ * @param port Port index (zero based).
+ * @return Port structure.
+ * @retval NULL Invalid port index.
+ */
+static hub_port_t *get_hub_port(hub_t *hub, size_t port)
+{
+	if (port >= HUB_PORT_COUNT) {
+		return NULL;
+	}
+
+	return &hub->ports[port];
+}
+
+/** Adds a port status change to a port.
+ *
+ * @param port The port with status change.
+ * @param change Change to be added to the status.
+ */
+static void set_port_status_change(hub_port_t *port,
+    hub_status_change_t change)
+{
+	assert(port != NULL);
+	port->status_change |= change;
+}
+
+/** Clears a port status change on a port.
+ *
+ * @param port The port with status change.
+ * @param change Change to be removed from the status.
+ */
+static void clear_port_status_change(hub_port_t *port,
+    uint16_t change)
+{
+	assert(port != NULL);
+	port->status_change &= (~change);
+}
+
+/** Structure for automatic (delayed) port state change. */
+struct delay_port_state_change {
+	/** Delay in microseconds. */
+	suseconds_t delay;
+	/** Old state of the port. */
+	hub_port_state_t old_state;
+	/** New state of the port. */
+	hub_port_state_t new_state;
+	/** Port index (zero based). */
+	size_t port;
+	/** Hub. */
+	hub_t *hub;
+};
+
+/** Fibril responsible for delayed port state change.
+ *
+ * @param arg Pointer to delay_port_state_change.
+ * @return Always EOK.
+ */
+static int set_port_state_delayed_fibril(void *arg)
+{
+	struct delay_port_state_change *change
+	    = (struct delay_port_state_change *) arg;
+
+	async_usleep(change->delay);
+
+	hub_acquire(change->hub);
+
+	hub_port_t *port = get_hub_port(change->hub, change->port);
+	assert(port != NULL);
+
+	if (port->state == change->old_state) {
+		hub_set_port_state(change->hub, change->port,
+		    change->new_state);
+	}
+
+	hub_release(change->hub);
+
+	free(change);
+
+	return EOK;
+}
+
+/** Change port state with a delay.
+ *
+ * @warning If the port state changes during the waiting phase, the state
+ * is not changed.
+ *
+ * @param hub Hub in question.
+ * @param port_index Port index (zero based).
+ * @param delay_time_ms Delay time in miliseconds.
+ * @param old_state Old (current) state of the port.
+ * @param new_state New state of the port.
+ */
+static void set_port_state_delayed(hub_t *hub, size_t port_index,
+    suseconds_t delay_time_ms,
+    hub_port_state_t old_state, hub_port_state_t new_state)
+{
+	struct delay_port_state_change *change
+	    = malloc(sizeof(struct delay_port_state_change));
+
+	change->hub = hub;
+	change->port = port_index;
+	change->delay = delay_time_ms * 1000;
+	change->old_state = old_state;
+	change->new_state = new_state;
+	fid_t fibril = fibril_create(set_port_state_delayed_fibril, change);
+	if (fibril == 0) {
+		printf("Failed to create fibril\n");
+		free(change);
+		return;
+	}
+	fibril_add_ready(fibril);
+}
+
+
+
+/**
+ * @}
+ */
Index: uspace/drv/vhc/hub/hub.h
===================================================================
--- uspace/drv/vhc/hub/hub.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/hub/hub.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,111 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Representation of an USB hub.
+ */
+#ifndef VHC_HUB_HUB_H_
+#define VHC_HUB_HUB_H_
+
+#include <fibril_synch.h>
+
+#define HUB_PORT_COUNT 2
+#define BITS2BYTES(bits) (bits ? ((((bits)-1)>>3)+1) : 0)
+
+/** Hub port internal state.
+ * Some states (e.g. port over current) are not covered as they are not
+ * simulated at all.
+ */
+typedef enum {
+	HUB_PORT_STATE_UNKNOWN,
+	HUB_PORT_STATE_NOT_CONFIGURED,
+	HUB_PORT_STATE_POWERED_OFF,
+	HUB_PORT_STATE_DISCONNECTED,
+	HUB_PORT_STATE_DISABLED,
+	HUB_PORT_STATE_RESETTING,
+	HUB_PORT_STATE_ENABLED,
+	HUB_PORT_STATE_SUSPENDED,
+	HUB_PORT_STATE_RESUMING,
+	/* HUB_PORT_STATE_, */
+} hub_port_state_t;
+
+char hub_port_state_to_char(hub_port_state_t);
+
+/** Hub status change mask bits. */
+typedef enum {
+	HUB_STATUS_C_PORT_CONNECTION = (1 << 0),
+	HUB_STATUS_C_PORT_ENABLE = (1 << 1),
+	HUB_STATUS_C_PORT_SUSPEND = (1 << 2),
+	HUB_STATUS_C_PORT_OVER_CURRENT = (1 << 3),
+	HUB_STATUS_C_PORT_RESET = (1 << 4),
+	/* HUB_STATUS_C_ = (1 << ), */
+} hub_status_change_t;
+
+/** Hub port information. */
+typedef struct {
+	/** Custom pointer to connected device. */
+	void *connected_device;
+	/** Port index (one based). */
+	size_t index;
+	/** Port state. */
+	hub_port_state_t state;
+	/** Status change bitmap. */
+	uint16_t status_change;
+} hub_port_t;
+
+/** Hub device type. */
+typedef struct {
+	/** Hub ports. */
+	hub_port_t ports[HUB_PORT_COUNT];
+	/** Custom hub data. */
+	void *custom_data;
+	/** Access guard to the whole hub. */
+	fibril_mutex_t guard;
+} hub_t;
+
+void hub_init(hub_t *);
+size_t hub_connect_device(hub_t *, void *);
+size_t hub_find_device(hub_t *, void *);
+void hub_acquire(hub_t *);
+void hub_release(hub_t *);
+void hub_set_port_state(hub_t *, size_t, hub_port_state_t);
+void hub_set_port_state_all(hub_t *, hub_port_state_t);
+hub_port_state_t hub_get_port_state(hub_t *, size_t);
+void hub_clear_port_status_change(hub_t *, size_t, hub_status_change_t);
+uint16_t hub_get_port_status_change(hub_t *, size_t);
+uint32_t hub_get_port_status(hub_t *, size_t);
+uint8_t hub_get_status_change_bitmap(hub_t *);
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/vhc/hub/virthub.c
===================================================================
--- uspace/drv/vhc/hub/virthub.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/hub/virthub.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,265 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief
+ */
+#include <usb/classes/classes.h>
+#include <usbvirt/hub.h>
+#include <usbvirt/device.h>
+#include <assert.h>
+#include <errno.h>
+#include <str_error.h>
+#include <stdlib.h>
+#include <driver.h>
+#include <usb/usbdrv.h>
+
+#include "virthub.h"
+#include "hub.h"
+
+
+/** Standard device descriptor. */
+usb_standard_device_descriptor_t std_device_descriptor = {
+	.length = sizeof(usb_standard_device_descriptor_t),
+	.descriptor_type = USB_DESCTYPE_DEVICE,
+	.usb_spec_version = 0x110,
+	.device_class = USB_CLASS_HUB,
+	.device_subclass = 0,
+	.device_protocol = 0,
+	.max_packet_size = 64,
+	.configuration_count = 1
+};
+
+/** Standard interface descriptor. */
+usb_standard_interface_descriptor_t std_interface_descriptor = {
+	.length = sizeof(usb_standard_interface_descriptor_t),
+	.descriptor_type = USB_DESCTYPE_INTERFACE,
+	.interface_number = 0,
+	.alternate_setting = 0,
+	.endpoint_count = 1,
+	.interface_class = USB_CLASS_HUB,
+	.interface_subclass = 0,
+	.interface_protocol = 0,
+	.str_interface = 0
+};
+
+/** Hub descriptor. */
+hub_descriptor_t hub_descriptor = {
+	.length = sizeof(hub_descriptor_t),
+	.type = USB_DESCTYPE_HUB,
+	.port_count = HUB_PORT_COUNT,
+	.characteristics = 0, 
+	.power_on_warm_up = 50, /* Huh? */
+	.max_current = 100, /* Huh again. */
+	.removable_device = { 0 },
+	.port_power = { 0xFF }
+};
+
+/** Endpoint descriptor. */
+usb_standard_endpoint_descriptor_t endpoint_descriptor = {
+	.length = sizeof(usb_standard_endpoint_descriptor_t),
+	.descriptor_type = USB_DESCTYPE_ENDPOINT,
+	.endpoint_address = HUB_STATUS_CHANGE_PIPE | 128,
+	.attributes = USB_TRANSFER_INTERRUPT,
+	.max_packet_size = 8,
+	.poll_interval = 0xFF
+};
+
+/** Standard configuration descriptor. */
+usb_standard_configuration_descriptor_t std_configuration_descriptor = {
+	.length = sizeof(usb_standard_configuration_descriptor_t),
+	.descriptor_type = USB_DESCTYPE_CONFIGURATION,
+	.total_length = 
+		sizeof(usb_standard_configuration_descriptor_t)
+		+ sizeof(std_interface_descriptor)
+		+ sizeof(hub_descriptor)
+		+ sizeof(endpoint_descriptor)
+		,
+	.interface_count = 1,
+	.configuration_number = HUB_CONFIGURATION_ID,
+	.str_configuration = 0,
+	.attributes = 128, /* denotes bus-powered device */
+	.max_power = 50
+};
+
+/** All hub configuration descriptors. */
+static usbvirt_device_configuration_extras_t extra_descriptors[] = {
+	{
+		.data = (uint8_t *) &std_interface_descriptor,
+		.length = sizeof(std_interface_descriptor)
+	},
+	{
+		.data = (uint8_t *) &hub_descriptor,
+		.length = sizeof(hub_descriptor)
+	},
+	{
+		.data = (uint8_t *) &endpoint_descriptor,
+		.length = sizeof(endpoint_descriptor)
+	}
+};
+
+/** Hub configuration. */
+usbvirt_device_configuration_t configuration = {
+	.descriptor = &std_configuration_descriptor,
+	.extra = extra_descriptors,
+	.extra_count = sizeof(extra_descriptors)/sizeof(extra_descriptors[0])
+};
+
+/** Hub standard descriptors. */
+usbvirt_descriptors_t descriptors = {
+	.device = &std_device_descriptor,
+	.configuration = &configuration,
+	.configuration_count = 1,
+};
+
+/** Initializes virtual hub device.
+ *
+ * @param dev Virtual USB device backend.
+ * @return Error code.
+ */
+int virthub_init(usbvirt_device_t *dev)
+{
+	if (dev == NULL) {
+		return EBADMEM;
+	}
+	dev->ops = &hub_ops;
+	dev->descriptors = &descriptors;
+	dev->lib_debug_level = 0;
+	dev->lib_debug_enabled_tags = USBVIRT_DEBUGTAG_ALL;
+
+	hub_t *hub = malloc(sizeof(hub_t));
+	if (hub == NULL) {
+		return ENOMEM;
+	}
+
+	hub_init(hub);
+	dev->device_data = hub;
+
+	usbvirt_connect_local(dev);
+
+	return EOK;
+}
+
+/** Connect a device to a virtual hub.
+ *
+ * @param dev Virtual device representing the hub.
+ * @param conn Device to be connected.
+ * @return Port device was connected to.
+ */
+int virthub_connect_device(usbvirt_device_t *dev, virtdev_connection_t *conn)
+{
+	assert(dev != NULL);
+	assert(conn != NULL);
+
+	hub_t *hub = (hub_t *) dev->device_data;
+
+	hub_acquire(hub);
+	size_t port = hub_connect_device(hub, conn);
+	hub_release(hub);
+
+	return port;
+}
+
+/** Disconnect a device from a virtual hub.
+ *
+ * @param dev Virtual device representing the hub.
+ * @param conn Device to be disconnected.
+ * @return Error code.
+ */
+int virthub_disconnect_device(usbvirt_device_t *dev, virtdev_connection_t *conn)
+{
+	assert(dev != NULL);
+	assert(conn != NULL);
+
+	hub_t *hub = (hub_t *) dev->device_data;
+
+	hub_acquire(hub);
+	/* TODO: implement. */
+	hub_release(hub);
+
+	return ENOTSUP;
+}
+
+/** Whether trafic is propagated to given device.
+ *
+ * @param dev Virtual device representing the hub.
+ * @param conn Connected device.
+ * @return Whether port is signalling to the device.
+ */
+bool virthub_is_device_enabled(usbvirt_device_t *dev, virtdev_connection_t *conn)
+{
+	assert(dev != NULL);
+	assert(conn != NULL);
+
+	hub_t *hub = (hub_t *) dev->device_data;
+
+	hub_acquire(hub);
+
+	hub_port_state_t state = HUB_PORT_STATE_UNKNOWN;
+	size_t port = hub_find_device(hub, conn);
+	if (port != (size_t) -1) {
+		state = hub_get_port_state(hub, port);
+	}
+	hub_release(hub);
+
+	return state == HUB_PORT_STATE_ENABLED;
+}
+
+/** Format status of a virtual hub.
+ *
+ * @param dev Virtual device representing the hub.
+ * @param[out] status Hub status information.
+ * @param[in] len Size of the @p status buffer.
+ */
+void virthub_get_status(usbvirt_device_t *dev, char *status, size_t len)
+{
+	assert(dev != NULL);
+	if (len == 0) {
+		return;
+	}
+
+	hub_t *hub = (hub_t *) dev->device_data;
+
+	char port_status[HUB_PORT_COUNT + 1];
+
+	size_t i;
+	for (i = 0; i < HUB_PORT_COUNT; i++) {
+		port_status[i] = hub_port_state_to_char(
+		    hub_get_port_state(hub, i));
+	}
+	port_status[HUB_PORT_COUNT] = 0;
+
+	snprintf(status, len, "vhub:%s", port_status);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/vhc/hub/virthub.h
===================================================================
--- uspace/drv/vhc/hub/virthub.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/hub/virthub.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,85 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief USB hub as a virtual USB device.
+ */
+#ifndef VHC_HUB_VIRTHUB_H_
+#define VHC_HUB_VIRTHUB_H_
+
+#include <usbvirt/device.h>
+#include "../devices.h"
+#include "hub.h"
+
+/** Endpoint number for status change pipe. */
+#define HUB_STATUS_CHANGE_PIPE 1
+/** Configuration value for hub configuration. */
+#define HUB_CONFIGURATION_ID 1
+
+
+/** Hub descriptor.
+ */
+typedef struct {
+	/** Size of this descriptor in bytes. */
+	uint8_t length;
+	/** Descriptor type (USB_DESCTYPE_HUB). */
+	uint8_t type;
+	/** Number of downstream ports. */
+	uint8_t port_count;
+	/** Hub characteristics. */
+	uint16_t characteristics;
+	/** Time from power-on to stabilized current.
+	 * Expressed in 2ms unit.
+	 */
+	uint8_t power_on_warm_up;
+	/** Maximum current (in mA). */
+	uint8_t max_current;
+	/** Whether device at given port is removable. */
+	uint8_t removable_device[BITS2BYTES(HUB_PORT_COUNT+1)];
+	/** Port power control.
+	 * This is USB1.0 compatibility field, all bits must be 1.
+	 */
+	uint8_t port_power[BITS2BYTES(HUB_PORT_COUNT+1)];
+} __attribute__ ((packed)) hub_descriptor_t;
+
+extern usbvirt_device_ops_t hub_ops;
+extern hub_descriptor_t hub_descriptor;
+
+int virthub_init(usbvirt_device_t *);
+int virthub_connect_device(usbvirt_device_t *, virtdev_connection_t *);
+int virthub_disconnect_device(usbvirt_device_t *, virtdev_connection_t *);
+bool virthub_is_device_enabled(usbvirt_device_t *, virtdev_connection_t *);
+void virthub_get_status(usbvirt_device_t *, char *, size_t);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/vhc/hub/virthubops.c
===================================================================
--- uspace/drv/vhc/hub/virthubops.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/hub/virthubops.c	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,433 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Virtual USB hub operations.
+ */
+#include <errno.h>
+#include <usb/classes/hub.h>
+#include "virthub.h"
+#include "hub.h"
+
+/** Callback when device changes states. */
+static void on_state_change(struct usbvirt_device *dev,
+    usbvirt_device_state_t old_state, usbvirt_device_state_t new_state)
+{
+	hub_t *hub = (hub_t *)dev->device_data;
+
+	hub_acquire(hub);
+
+	switch (new_state) {
+		case USBVIRT_STATE_CONFIGURED:
+			hub_set_port_state_all(hub, HUB_PORT_STATE_POWERED_OFF);
+			break;
+		case USBVIRT_STATE_ADDRESS:
+			hub_set_port_state_all(hub, HUB_PORT_STATE_NOT_CONFIGURED);
+			break;
+		default:
+			break;
+	}
+
+	hub_release(hub);
+}
+
+/** Callback for data request. */
+static int req_on_data(struct usbvirt_device *dev,
+    usb_endpoint_t endpoint,
+    void *buffer, size_t size, size_t *actual_size)
+{
+	if (endpoint != HUB_STATUS_CHANGE_PIPE) {
+		return EINVAL;
+	}
+	
+	hub_t *hub = (hub_t *)dev->device_data;
+
+	hub_acquire(hub);
+
+	uint8_t change_map = hub_get_status_change_bitmap(hub);
+		
+	uint8_t *b = (uint8_t *) buffer;
+	if (size > 0) {
+		*b = change_map;
+		*actual_size = 1;
+	}
+	
+	hub_release(hub);
+
+	return EOK;
+}
+
+/** Handle ClearHubFeature request.
+ *
+ * @param dev Virtual device representing the hub.
+ * @param request The SETUP packet of the control request.
+ * @param data Extra data (when DATA stage present).
+ * @return Error code.
+ */
+static int req_clear_hub_feature(usbvirt_device_t *dev,
+    usb_device_request_setup_packet_t *request,
+    uint8_t *data)
+{
+	return ENOTSUP;
+}
+
+/** Handle ClearPortFeature request.
+ *
+ * @param dev Virtual device representing the hub.
+ * @param request The SETUP packet of the control request.
+ * @param data Extra data (when DATA stage present).
+ * @return Error code.
+ */
+static int req_clear_port_feature(usbvirt_device_t *dev,
+    usb_device_request_setup_packet_t *request,
+    uint8_t *data)
+{
+	int rc;
+	size_t port = request->index - 1;
+	usb_hub_class_feature_t feature = request->value;
+	hub_t *hub = (hub_t *) dev->device_data;
+
+	hub_acquire(hub);
+
+	hub_port_state_t port_state = hub_get_port_state(hub, port);
+
+	switch (feature) {
+		case USB_HUB_FEATURE_PORT_ENABLE:
+			if ((port_state != HUB_PORT_STATE_NOT_CONFIGURED)
+			    && (port_state != HUB_PORT_STATE_POWERED_OFF)) {
+				hub_set_port_state(hub, port, HUB_PORT_STATE_DISABLED);
+			}
+			rc = EOK;
+			break;
+
+		case USB_HUB_FEATURE_PORT_SUSPEND:
+			if (port_state != HUB_PORT_STATE_SUSPENDED) {
+				rc = EOK;
+				break;
+			}
+			hub_set_port_state(hub, port, HUB_PORT_STATE_RESUMING);
+			rc = EOK;
+			break;
+
+		case USB_HUB_FEATURE_PORT_POWER:
+			if (port_state != HUB_PORT_STATE_NOT_CONFIGURED) {
+				hub_set_port_state(hub, port, HUB_PORT_STATE_POWERED_OFF);
+			}
+			rc = EOK;
+			break;
+
+		case USB_HUB_FEATURE_C_PORT_CONNECTION:
+			hub_clear_port_status_change(hub, port, HUB_STATUS_C_PORT_CONNECTION);
+			rc = EOK;
+			break;
+
+		case USB_HUB_FEATURE_C_PORT_ENABLE:
+			hub_clear_port_status_change(hub, port, HUB_STATUS_C_PORT_ENABLE);
+			rc = EOK;
+			break;
+
+		case USB_HUB_FEATURE_C_PORT_SUSPEND:
+			hub_clear_port_status_change(hub, port, HUB_STATUS_C_PORT_SUSPEND);
+			rc = EOK;
+			break;
+
+		case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
+			hub_clear_port_status_change(hub, port, HUB_STATUS_C_PORT_OVER_CURRENT);
+			rc = EOK;
+			break;
+
+		case USB_HUB_FEATURE_C_PORT_RESET:
+			hub_clear_port_status_change(hub, port, HUB_STATUS_C_PORT_RESET);
+			rc = EOK;
+			break;
+
+		default:
+			rc = ENOTSUP;
+			break;
+	}
+
+	hub_release(hub);
+
+	return rc;
+}
+
+/** Handle GetBusState request.
+ *
+ * @param dev Virtual device representing the hub.
+ * @param request The SETUP packet of the control request.
+ * @param data Extra data (when DATA stage present).
+ * @return Error code.
+ */
+static int req_get_bus_state(usbvirt_device_t *dev,
+    usb_device_request_setup_packet_t *request,
+    uint8_t *data)
+{
+	return ENOTSUP;
+}
+
+/** Handle GetDescriptor request.
+ *
+ * @param dev Virtual device representing the hub.
+ * @param request The SETUP packet of the control request.
+ * @param data Extra data (when DATA stage present).
+ * @return Error code.
+ */
+static int req_get_descriptor(usbvirt_device_t *dev,
+    usb_device_request_setup_packet_t *request,
+    uint8_t *data)
+{
+	if (request->value_high == USB_DESCTYPE_HUB) {
+		int rc = dev->control_transfer_reply(dev, 0,
+		    &hub_descriptor, hub_descriptor.length);
+
+		return rc;
+	}
+	/* Let the framework handle all the rest. */
+	return EFORWARD;
+}
+
+/** Handle GetHubStatus request.
+ *
+ * @param dev Virtual device representing the hub.
+ * @param request The SETUP packet of the control request.
+ * @param data Extra data (when DATA stage present).
+ * @return Error code.
+ */
+static int req_get_hub_status(usbvirt_device_t *dev,
+    usb_device_request_setup_packet_t *request,
+    uint8_t *data)
+{
+	uint32_t hub_status = 0;
+
+	return dev->control_transfer_reply(dev, 0,
+	    &hub_status, sizeof(hub_status));
+}
+
+/** Handle GetPortStatus request.
+ *
+ * @param dev Virtual device representing the hub.
+ * @param request The SETUP packet of the control request.
+ * @param data Extra data (when DATA stage present).
+ * @return Error code.
+ */
+static int req_get_port_status(usbvirt_device_t *dev,
+    usb_device_request_setup_packet_t *request,
+    uint8_t *data)
+{
+	hub_t *hub = (hub_t *) dev->device_data;
+
+	hub_acquire(hub);
+
+	uint32_t status = hub_get_port_status(hub, request->index - 1);
+
+	hub_release(hub);
+
+	return dev->control_transfer_reply(dev, 0, &status, 4);
+}
+
+/** Handle SetHubFeature request.
+ *
+ * @param dev Virtual device representing the hub.
+ * @param request The SETUP packet of the control request.
+ * @param data Extra data (when DATA stage present).
+ * @return Error code.
+ */
+static int req_set_hub_feature(usbvirt_device_t *dev,
+    usb_device_request_setup_packet_t *request,
+    uint8_t *data)
+{
+	return ENOTSUP;
+}
+
+/** Handle SetPortFeature request.
+ *
+ * @param dev Virtual device representing the hub.
+ * @param request The SETUP packet of the control request.
+ * @param data Extra data (when DATA stage present).
+ * @return Error code.
+ */
+static int req_set_port_feature(usbvirt_device_t *dev,
+    usb_device_request_setup_packet_t *request,
+    uint8_t *data)
+{
+	int rc;
+	size_t port = request->index - 1;
+	usb_hub_class_feature_t feature = request->value;
+	hub_t *hub = (hub_t *) dev->device_data;
+
+	hub_acquire(hub);
+
+	hub_port_state_t port_state = hub_get_port_state(hub, port);
+
+	switch (feature) {
+		case USB_HUB_FEATURE_PORT_RESET:
+			if (port_state != HUB_PORT_STATE_POWERED_OFF) {
+				hub_set_port_state(hub, port, HUB_PORT_STATE_RESETTING);
+			}
+			rc = EOK;
+			break;
+
+		case USB_HUB_FEATURE_PORT_SUSPEND:
+			if (port_state == HUB_PORT_STATE_ENABLED) {
+				hub_set_port_state(hub, port, HUB_PORT_STATE_SUSPENDED);
+			}
+			rc = EOK;
+			break;
+
+		case USB_HUB_FEATURE_PORT_POWER:
+			if (port_state == HUB_PORT_STATE_POWERED_OFF) {
+				hub_set_port_state(hub, port, HUB_PORT_STATE_DISCONNECTED);
+			}
+			rc = EOK;
+			break;
+
+		default:
+			break;
+	}
+
+	hub_release(hub);
+
+	return rc;
+}
+
+
+/** IN class request. */
+#define CLASS_REQ_IN(recipient) \
+	USBVIRT_MAKE_CONTROL_REQUEST_TYPE(USB_DIRECTION_IN, \
+	USBVIRT_REQUEST_TYPE_CLASS, recipient)
+/** OUT class request. */
+#define CLASS_REQ_OUT(recipient) \
+	USBVIRT_MAKE_CONTROL_REQUEST_TYPE(USB_DIRECTION_OUT, \
+	USBVIRT_REQUEST_TYPE_CLASS, recipient)
+
+/** Recipient: other. */
+#define REC_OTHER USBVIRT_REQUEST_RECIPIENT_OTHER
+/** Recipient: device. */
+#define REC_DEVICE USBVIRT_REQUEST_RECIPIENT_DEVICE
+/** Direction: in. */
+#define DIR_IN USB_DIRECTION_IN
+/** Direction: out. */
+#define DIR_OUT USB_DIRECTION_OUT
+
+/** Create a class request.
+ *
+ * @param direction Request direction.
+ * @param recipient Request recipient.
+ * @param req Request code.
+ */
+#define CLASS_REQ(direction, recipient, req) \
+	.request_type = USBVIRT_MAKE_CONTROL_REQUEST_TYPE(direction, \
+	    USBVIRT_REQUEST_TYPE_CLASS, recipient), \
+	.request = req
+
+/** Create a standard request.
+ *
+ * @param direction Request direction.
+ * @param recipient Request recipient.
+ * @param req Request code.
+ */
+#define STD_REQ(direction, recipient, req) \
+	.request_type = USBVIRT_MAKE_CONTROL_REQUEST_TYPE(direction, \
+	    USBVIRT_REQUEST_TYPE_STANDARD, recipient), \
+	.request = req
+
+/** Hub operations on control endpoint zero. */
+static usbvirt_control_transfer_handler_t endpoint_zero_handlers[] = {
+	{
+		STD_REQ(DIR_IN, REC_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
+		.name = "GetDescriptor",
+		.callback = req_get_descriptor
+	},
+	{
+		CLASS_REQ(DIR_IN, REC_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
+		.name = "GetDescriptor",
+		.callback = req_get_descriptor
+	},
+	{
+		CLASS_REQ(DIR_IN, REC_OTHER, USB_HUB_REQUEST_GET_STATUS),
+		.name = "GetPortStatus",
+		.callback = req_get_port_status
+	},
+	{
+		CLASS_REQ(DIR_OUT, REC_DEVICE, USB_HUB_REQUEST_CLEAR_FEATURE),
+		.name = "ClearHubFeature",
+		.callback = req_clear_hub_feature
+	},
+	{
+		CLASS_REQ(DIR_OUT, REC_OTHER, USB_HUB_REQUEST_CLEAR_FEATURE),
+		.name = "ClearPortFeature",
+		.callback = req_clear_port_feature
+	},
+	{
+		CLASS_REQ(DIR_IN, REC_OTHER, USB_HUB_REQUEST_GET_STATE),
+		.name = "GetBusState",
+		.callback = req_get_bus_state
+	},
+	{
+		CLASS_REQ(DIR_IN, REC_DEVICE, USB_HUB_REQUEST_GET_DESCRIPTOR),
+		.name = "GetHubDescriptor",
+		.callback = req_get_descriptor
+	},
+	{
+		CLASS_REQ(DIR_IN, REC_DEVICE, USB_HUB_REQUEST_GET_STATUS),
+		.name = "GetHubStatus",
+		.callback = req_get_hub_status
+	},
+	{
+		CLASS_REQ(DIR_IN, REC_OTHER, USB_HUB_REQUEST_GET_STATUS),
+		.name = "GetPortStatus",
+		.callback = req_get_port_status
+	},
+	{
+		CLASS_REQ(DIR_OUT, REC_DEVICE, USB_HUB_REQUEST_SET_FEATURE),
+		.name = "SetHubFeature",
+		.callback = req_set_hub_feature
+	},
+	{
+		CLASS_REQ(DIR_OUT, REC_OTHER, USB_HUB_REQUEST_SET_FEATURE),
+		.name = "SetPortFeature",
+		.callback = req_set_port_feature
+	},
+	USBVIRT_CONTROL_TRANSFER_HANDLER_LAST
+};
+
+
+/** Hub operations. */
+usbvirt_device_ops_t hub_ops = {
+	.control_transfer_handlers = endpoint_zero_handlers,
+	.on_data = NULL,
+	.on_data_request = req_on_data,
+	.on_state_change = on_state_change,
+};
+
+/**
+ * @}
+ */
Index: uspace/drv/vhc/vhc.ma
===================================================================
--- uspace/drv/vhc/vhc.ma	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/vhc.ma	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,2 @@
+10 usb&hc=vhc
+
Index: uspace/drv/vhc/vhcd.h
===================================================================
--- uspace/drv/vhc/vhcd.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
+++ uspace/drv/vhc/vhcd.h	(revision 1ffa73b8c67cd8a07425142d61d6f82e76f3c497)
@@ -0,0 +1,54 @@
+/*
+ * 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 usb
+ * @{
+ */
+/** @file
+ * @brief Virtual USB host controller common definitions.
+ */
+#ifndef VHCD_VHCD_H_
+#define VHCD_VHCD_H_
+
+#include <usb/debug.h>
+
+#define NAME "vhc"
+#define NAME_DEV "hcd-virt-dev"
+#define NAMESPACE "usb"
+
+#define DEVMAP_PATH_HC NAMESPACE "/" NAME
+#define DEVMAP_PATH_DEV NAMESPACE "/" NAME_DEV
+
+#define dprintf(level, format, ...) \
+	usb_dprintf(NAME, (level), format "\n", ##__VA_ARGS__)
+void dprintf_inval_call(int, ipc_call_t, sysarg_t);
+
+#endif
+/**
+ * @}
+ */
