Index: uspace/srv/hw/bus/usb/hcd/virtual/Makefile
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/Makefile	(revision 08af5a623a5800b0ab77eab5b167487adcdfe5ca)
+++ uspace/srv/hw/bus/usb/hcd/virtual/Makefile	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
@@ -28,5 +28,5 @@
 
 USPACE_PREFIX = ../../../../../..
-LIBS = $(LIBUSB_PREFIX)/libusb.a
+LIBS = $(LIBUSB_PREFIX)/libusb.a $(LIBUSBVIRT_PREFIX)/libusbvirt.a
 EXTRA_CFLAGS = -I$(LIB_PREFIX)
 BINARY = vhcd
@@ -37,5 +37,7 @@
 	devices.c \
 	hc.c \
-	hcd.c
+	hcd.c \
+	hub.c \
+	hubops.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/hw/bus/usb/hcd/virtual/conndev.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/conndev.c	(revision 08af5a623a5800b0ab77eab5b167487adcdfe5ca)
+++ uspace/srv/hw/bus/usb/hcd/virtual/conndev.c	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
@@ -40,6 +40,7 @@
 #include "conn.h"
 #include "hc.h"
+#include "hub.h"
 
-/** Handle data from device to function.
+/** Handle data from device to host.
  */
 static void handle_data_from_device(ipc_callid_t iid, ipc_call_t icall,
@@ -50,4 +51,9 @@
 		.endpoint = IPC_GET_ARG2(icall)
 	};
+	
+	if (!hub_can_device_signal(dev)) {
+		ipc_answer_0(iid, EREFUSED);
+		return;
+	}
 	
 	dprintf("data from device %d [%d.%d]", dev->id,
Index: uspace/srv/hw/bus/usb/hcd/virtual/devices.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/devices.c	(revision 08af5a623a5800b0ab77eab5b167487adcdfe5ca)
+++ uspace/srv/hw/bus/usb/hcd/virtual/devices.c	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
@@ -47,4 +47,5 @@
 
 #include "devices.h"
+#include "hub.h"
 
 #define list_foreach(pos, head) \
@@ -61,12 +62,5 @@
 virtdev_connection_t *virtdev_recognise(int id, int phone)
 {
-	virtdev_connection_t * dev = NULL;
-	switch (id) {
-		case USBVIRT_DEV_KEYBOARD_ID:
-			dev = virtdev_add_device(phone);
-			break;
-		default:
-			break;
-	}
+	virtdev_connection_t * dev = virtdev_add_device(phone);
 	
 	/*
@@ -98,4 +92,6 @@
 	list_append(&dev->link, &devices);
 	
+	hub_add_device(dev);
+	
 	return dev;
 }
@@ -105,4 +101,5 @@
 void virtdev_destroy_device(virtdev_connection_t *dev)
 {
+	hub_remove_device(dev);
 	list_remove(&dev->link);
 	free(dev);
@@ -119,4 +116,8 @@
 		virtdev_connection_t *dev
 		    = list_get_instance(pos, virtdev_connection_t, link);
+		
+		if (!hub_can_device_signal(dev)) {
+			continue;
+		}
 		
 		ipc_call_t answer_data;
@@ -143,4 +144,13 @@
 	
 	/*
+	 * Send the data to the virtual hub as well
+	 * (if the address matches).
+	 */
+	if (virthub_dev.address == transaction->target.address) {
+		virthub_dev.receive_data(&virthub_dev, transaction->target.endpoint,
+		    transaction->buffer, transaction->len);
+	}
+	
+	/*
 	 * TODO: maybe screw some transactions to get more
 	 * real-life image.
Index: uspace/srv/hw/bus/usb/hcd/virtual/hcd.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hcd.c	(revision 08af5a623a5800b0ab77eab5b167487adcdfe5ca)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hcd.c	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
@@ -48,4 +48,5 @@
 #include "hc.h"
 #include "devices.h"
+#include "hub.h"
 #include "conn.h"
 
@@ -135,4 +136,6 @@
 	}
 	
+	hub_init();
+	
 	printf("%s: accepting connections.\n", NAME);
 	hc_manager();
Index: uspace/srv/hw/bus/usb/hcd/virtual/hub.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hub.c	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hub.c	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
@@ -0,0 +1,207 @@
+/*
+ * 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.h>
+#include <usbvirt/hub.h>
+#include <usbvirt/device.h>
+#include <errno.h>
+
+#include "vhcd.h"
+#include "hub.h"
+#include "hubintern.h"
+
+hub_port_t hub_ports[HUB_PORT_COUNT];
+
+/** 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_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
+};
+
+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,
+};
+
+usbvirt_device_t virthub_dev = {
+	.ops = &hub_ops,
+	.descriptors = &descriptors,
+};
+
+hub_device_t hub_dev;
+
+void hub_init(void)
+{
+	size_t i;
+	for (i = 0; i < HUB_PORT_COUNT; i++) {
+		hub_dev.ports[i].device = NULL;
+		hub_dev.ports[i].state = HUB_PORT_STATE_NOT_CONFIGURED;
+	}
+	hub_dev.status_change_bitmap = 0;
+	
+	usbvirt_connect_local(&virthub_dev);
+	
+	printf("%s: virtual hub (%d ports) created.\n", NAME, HUB_PORT_COUNT);
+}
+
+size_t hub_add_device(virtdev_connection_t *device)
+{
+	size_t i;
+	for (i = 0; i < HUB_PORT_COUNT; i++) {
+		if (hub_dev.ports[i].device != NULL) {
+			continue;
+		}
+		hub_dev.ports[i].device = device;
+		// TODO - notify the host about change
+		// bad, bad but it will work somehow at least
+		hub_dev.ports[i].state = HUB_PORT_STATE_ENABLED;
+		hub_dev.status_change_bitmap |= (1 << (i+1));
+		return i;
+	}
+	
+	return (size_t)-1;
+}
+
+
+void hub_remove_device(virtdev_connection_t *device)
+{
+	size_t i;
+	for (i = 0; i < HUB_PORT_COUNT; i++) {
+		if (hub_dev.ports[i].device != device) {
+			continue;
+		}
+		hub_dev.ports[i].device = NULL;
+		hub_dev.ports[i].state = HUB_PORT_STATE_DISCONNECTED;
+		hub_dev.status_change_bitmap |= (1 << (i+1));
+		// TODO - notify the host of the removal
+	}
+}
+
+bool hub_can_device_signal(virtdev_connection_t * device)
+{
+	size_t i;
+	for (i = 0; i < HUB_PORT_COUNT; i++) {
+		if (hub_dev.ports[i].device == device) {
+			return hub_dev.ports[i].state == HUB_PORT_STATE_ENABLED;
+		}
+	}
+	
+	return false;
+}
+
+
+/**
+ * @}
+ */
Index: uspace/srv/hw/bus/usb/hcd/virtual/hub.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hub.h	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hub.h	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
@@ -0,0 +1,57 @@
+/*
+ * 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 "devices.h"
+
+#define HUB_PORT_COUNT 6
+
+#define BITS2BYTES(bits) \
+    (bits ? ((((bits)-1)>>3)+1) : 0)
+
+extern usbvirt_device_t virthub_dev;
+
+void hub_init(void);
+size_t hub_add_device(virtdev_connection_t *);
+void hub_remove_device(virtdev_connection_t *);
+bool hub_can_device_signal(virtdev_connection_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/srv/hw/bus/usb/hcd/virtual/hubintern.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hubintern.h	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hubintern.h	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
@@ -0,0 +1,100 @@
+/*
+ * 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
+ */
+#ifndef VHCD_HUBINTERN_H_
+#define VHCD_HUBINTERN_H_
+
+#include "hub.h"
+
+#define HUB_STATUS_CHANGE_PIPE 1
+#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;
+
+typedef enum {
+	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;
+
+typedef struct {
+	virtdev_connection_t *device;
+	hub_port_state_t state;
+} hub_port_t;
+
+typedef struct {
+	hub_port_t ports[HUB_PORT_COUNT];
+	/* FIXME - assuming HUB_PORT_COUNT < 8 */
+	uint8_t status_change_bitmap;
+} hub_device_t;
+
+extern hub_device_t hub_dev;
+
+extern hub_descriptor_t hub_descriptor;
+
+extern usbvirt_device_ops_t hub_ops;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/srv/hw/bus/usb/hcd/virtual/hubops.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hubops.c	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hubops.c	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
@@ -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 USB hub operations.
+ */
+#include <usb/classes.h>
+#include <usb/hub.h>
+#include <usbvirt/hub.h>
+#include <usbvirt/device.h>
+#include <errno.h>
+
+#include "vhcd.h"
+#include "hub.h"
+#include "hubintern.h"
+
+static int on_get_descriptor(struct usbvirt_device *dev,
+    usb_device_request_setup_packet_t *request, uint8_t *data);
+static int on_class_request(struct usbvirt_device *dev,
+    usb_device_request_setup_packet_t *request, uint8_t *data);
+
+static usbvirt_standard_device_request_ops_t standard_request_ops = {
+	.on_get_status = NULL,
+	.on_clear_feature = NULL,
+	.on_set_feature = NULL,
+	.on_set_address = NULL,
+	.on_get_descriptor = on_get_descriptor,
+	.on_set_descriptor = NULL,
+	.on_get_configuration = NULL,
+	.on_set_configuration = NULL,
+	.on_get_interface = NULL,
+	.on_set_interface = NULL,
+	.on_synch_frame = NULL
+};
+
+
+usbvirt_device_ops_t hub_ops = {
+	.standard_request_ops = &standard_request_ops,
+	.on_class_device_request = on_class_request,
+	.on_data = NULL
+};
+
+static int on_get_descriptor(struct usbvirt_device *dev,
+    usb_device_request_setup_packet_t *request, uint8_t *data)
+{
+	if (request->value_high == USB_DESCTYPE_HUB) {
+		int rc = dev->send_data(dev, 0,
+		    &hub_descriptor, hub_descriptor.length);
+		
+		return rc;
+	}
+	/* Let the framework handle all the rest. */
+	return EFORWARD;
+}
+
+static int clear_hub_feature(uint16_t feature)
+{
+	return ENOTSUP;
+}
+
+static int clear_port_feature(uint16_t feature, uint16_t portindex)
+{
+	return ENOTSUP;
+}
+
+static int get_bus_state(uint16_t portindex)
+{
+	return ENOTSUP;
+}
+
+static int get_hub_descriptor(uint8_t descriptor_type,
+    uint8_t descriptor_index, uint16_t length)
+{
+	return ENOTSUP;
+}
+
+static int get_hub_status(void)
+{
+	return ENOTSUP;
+}
+
+static int get_port_status(uint16_t portindex)
+{
+	return ENOTSUP;
+}
+
+
+static int set_hub_feature(uint16_t feature)
+{
+	return ENOTSUP;
+}
+
+static int set_port_feature(uint16_t feature, uint16_t portindex)
+{
+	return ENOTSUP;
+}
+
+
+static int on_class_request(struct usbvirt_device *dev,
+    usb_device_request_setup_packet_t *request, uint8_t *data)
+{	
+	printf("%s: hub class request (%d)\n", NAME, (int) request->request);
+	
+	uint8_t recipient = request->request_type & 31;
+	uint8_t direction = request->request_type >> 7;
+	
+#define _VERIFY(cond) \
+	do { \
+		if (!(cond)) { \
+			printf("%s: WARN: invalid class request (%s not met).\n", \
+			    NAME, #cond); \
+			return EINVAL; \
+		} \
+	} while (0)
+	
+	switch (request->request) {
+		case USB_HUB_REQUEST_CLEAR_FEATURE:
+			_VERIFY(direction == 0);
+			_VERIFY(request->length == 0);
+			if (recipient == 0) {
+				_VERIFY(request->index == 0);
+				return clear_hub_feature(request->value);
+			} else {
+				_VERIFY(recipient == 3);
+				return clear_port_feature(request->value,
+				    request->index);
+			}
+			
+		case USB_HUB_REQUEST_GET_STATE:
+			return get_bus_state(request->index);
+			
+		case USB_HUB_REQUEST_GET_DESCRIPTOR:
+			return get_hub_descriptor(request->value_low,
+			    request->value_high, request->length);
+			
+		case USB_HUB_REQUEST_GET_STATUS:
+			if (recipient == 0) {
+				return get_hub_status();
+			} else {
+				return get_port_status(request->index);
+			}
+			
+		case USB_HUB_REQUEST_SET_FEATURE:
+			if (recipient == 0) {
+				return set_hub_feature(request->value);
+			} else {
+				return set_port_feature(request->value, request->index);
+			}
+			
+		default:
+			break;
+	}
+	
+#undef _VERIFY	
+
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/hw/bus/usb/hcd/virtual/vhcd.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/vhcd.h	(revision 08af5a623a5800b0ab77eab5b167487adcdfe5ca)
+++ uspace/srv/hw/bus/usb/hcd/virtual/vhcd.h	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
@@ -36,4 +36,6 @@
 #define VHCD_VHCD_H_
 
+#include <stdio.h>
+
 #define NAME "hcd-virt"
 #define NAMESPACE "usb"
