Index: uspace/app/init/init.c
===================================================================
--- uspace/app/init/init.c	(revision 7551706b843aa33e309ce55169520d8c0aa5b8ce)
+++ uspace/app/init/init.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -312,4 +312,5 @@
 	getterm("term/vc5", "/app/bdsh", false);
 	getterm("term/vc6", "/app/klog", false);
+	getterm("term/vc7", "/srv/devman", false);
 	
 	return 0;
Index: uspace/app/tester/Makefile
===================================================================
--- uspace/app/tester/Makefile	(revision 7551706b843aa33e309ce55169520d8c0aa5b8ce)
+++ uspace/app/tester/Makefile	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -31,6 +31,10 @@
 BINARY = tester
 
+LIBS += $(LIBUSB_PREFIX)/libusb.a
+EXTRA_CFLAGS += -I$(LIBUSB_PREFIX)/include
+
 SOURCES = \
 	tester.c \
+	adt/usbaddrkeep.c \
 	thread/thread1.c \
 	print/print1.c \
Index: uspace/app/tester/adt/usbaddrkeep.c
===================================================================
--- uspace/app/tester/adt/usbaddrkeep.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/tester/adt/usbaddrkeep.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,66 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <usb/hcd.h>
+#include <errno.h>
+#include "../tester.h"
+
+#define MAX_ADDRESS 5
+
+
+const char *test_usbaddrkeep(void)
+{
+	int rc;
+	usb_address_keeping_t addresses;
+
+	TPRINTF("Initializing addresses keeping structure...\n");
+	usb_address_keeping_init(&addresses, MAX_ADDRESS);
+	
+	TPRINTF("Requesting address...\n");
+	usb_address_t addr = usb_address_keeping_request(&addresses);
+	TPRINTF("Address assigned: %d\n", (int) addr);
+	if (addr != 1) {
+		return "have not received expected address 1";
+	}
+
+	TPRINTF("Releasing not assigned address...\n");
+	rc = usb_address_keeping_release(&addresses, 2);
+	if (rc != ENOENT) {
+		return "have not received expected ENOENT";
+	}
+
+	TPRINTF("Releasing acquired address...\n");
+	rc = usb_address_keeping_release(&addresses, addr);
+	if (rc != EOK) {
+		return "have not received expected EOK";
+	}
+
+	return NULL;
+}
Index: uspace/app/tester/adt/usbaddrkeep.def
===================================================================
--- uspace/app/tester/adt/usbaddrkeep.def	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/tester/adt/usbaddrkeep.def	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,6 @@
+{
+	"usbaddrkeep",
+	"USB address keeping structure",
+	&test_usbaddrkeep,
+	true
+},
Index: uspace/app/tester/tester.c
===================================================================
--- uspace/app/tester/tester.c	(revision 7551706b843aa33e309ce55169520d8c0aa5b8ce)
+++ uspace/app/tester/tester.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -65,4 +65,5 @@
 #include "mm/malloc1.def"
 #include "hw/serial/serial1.def"
+#include "adt/usbaddrkeep.def"
 #include "hw/misc/virtchar1.def"
 	{NULL, NULL, NULL, false}
Index: uspace/app/tester/tester.h
===================================================================
--- uspace/app/tester/tester.h	(revision 7551706b843aa33e309ce55169520d8c0aa5b8ce)
+++ uspace/app/tester/tester.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -82,4 +82,5 @@
 extern const char *test_malloc1(void);
 extern const char *test_serial1(void);
+extern const char *test_usbaddrkeep(void);
 extern const char *test_virtchar1(void);
 
Index: uspace/app/usbinfo/Makefile
===================================================================
--- uspace/app/usbinfo/Makefile	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/usbinfo/Makefile	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,39 @@
+#
+# 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 = ../..
+BINARY = usbinfo
+
+LIBS = $(LIBUSB_PREFIX)/libusb.a $(LIBDRV_PREFIX)/libdrv.a
+EXTRA_CFLAGS = -I$(LIBUSB_PREFIX)/include -I$(LIBDRV_PREFIX)/include
+
+SOURCES = \
+	dump.c \
+	main.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/usbinfo/dump.c
===================================================================
--- uspace/app/usbinfo/dump.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/usbinfo/dump.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,129 @@
+/*
+ * 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 querying.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <str_error.h>
+#include <bool.h>
+
+#include <usb/usb.h>
+#include <usb/descriptor.h>
+
+#include "usbinfo.h"
+
+#define INDENT "  "
+#define BYTES_PER_LINE 12
+
+#define BCD_INT(a) (((unsigned int)(a)) / 256)
+#define BCD_FRAC(a) (((unsigned int)(a)) % 256)
+
+#define BCD_FMT "%x.%x"
+#define BCD_ARGS(a) BCD_INT((a)), BCD_FRAC((a))
+
+void dump_buffer(const char *msg, const uint8_t *buffer, size_t length)
+{
+	printf("%s\n", msg);
+
+	size_t i;
+	for (i = 0; i < length; i++) {
+		printf("  0x%02X", buffer[i]);
+		if (((i > 0) && (((i+1) % BYTES_PER_LINE) == 0))
+		    || (i + 1 == length)) {
+			printf("\n");
+		}
+	}
+}
+
+void dump_match_ids(match_id_list_t *matches)
+{
+	printf("Match ids:\n");
+	link_t *link;
+	for (link = matches->ids.next;
+	    link != &matches->ids;
+	    link = link->next) {
+		match_id_t *match = list_get_instance(link, match_id_t, link);
+
+		printf(INDENT "%d %s\n", match->score, match->id);
+	}
+}
+
+void dump_standard_device_descriptor(usb_standard_device_descriptor_t *d)
+{
+	printf("Standard device descriptor:\n");
+
+	printf(INDENT "bLength = %d\n", d->length);
+	printf(INDENT "bDescriptorType = 0x%02x\n", d->descriptor_type);
+	printf(INDENT "bcdUSB = %d (" BCD_FMT ")\n", d->usb_spec_version,
+	    BCD_ARGS(d->usb_spec_version));
+	printf(INDENT "bDeviceClass = 0x%02x\n", d->device_class);
+	printf(INDENT "bDeviceSubClass = 0x%02x\n", d->device_subclass);
+	printf(INDENT "bDeviceProtocol = 0x%02x\n", d->device_protocol);
+	printf(INDENT "bMaxPacketSize0 = %d\n", d->max_packet_size);
+	printf(INDENT "idVendor = %d\n", d->vendor_id);
+	printf(INDENT "idProduct = %d\n", d->product_id);
+	printf(INDENT "bcdDevice = %d\n", d->device_version);
+	printf(INDENT "iManufacturer = %d\n", d->str_manufacturer);
+	printf(INDENT "iProduct = %d\n", d->str_product);
+	printf(INDENT "iSerialNumber = %d\n", d->str_serial_number);
+	printf(INDENT "bNumConfigurations = %d\n", d->configuration_count);
+}
+
+void dump_standard_configuration_descriptor(
+    int index, usb_standard_configuration_descriptor_t *d)
+{
+	bool self_powered = d->attributes & 64;
+	bool remote_wakeup = d->attributes & 32;
+	
+	printf("Standard configuration descriptor #%d\n", index);
+	printf(INDENT "bLength = %d\n", d->length);
+	printf(INDENT "bDescriptorType = 0x%02x\n", d->descriptor_type);
+	printf(INDENT "wTotalLength = %d\n", d->total_length);
+	printf(INDENT "bNumInterfaces = %d\n", d->interface_count);
+	printf(INDENT "bConfigurationValue = %d\n", d->configuration_number);
+	printf(INDENT "iConfiguration = %d\n", d->str_configuration);
+	printf(INDENT "bmAttributes = %d [%s%s%s]\n", d->attributes,
+	    self_powered ? "self-powered" : "",
+	    (self_powered & remote_wakeup) ? ", " : "",
+	    remote_wakeup ? "remote-wakeup" : "");
+	printf(INDENT "MaxPower = %d (%dmA)\n", d->max_power,
+	    2 * d->max_power);
+	// printf(INDENT " = %d\n", d->);
+}
+
+
+/** @}
+ */
Index: uspace/app/usbinfo/main.c
===================================================================
--- uspace/app/usbinfo/main.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/usbinfo/main.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,188 @@
+/*
+ * 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 querying.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <str_error.h>
+#include <bool.h>
+#include <devman.h>
+#include <usb/usbdrv.h>
+#include "usbinfo.h"
+
+#define DEFAULT_HOST_CONTROLLER_PATH "/virt/usbhc"
+
+static void print_usage(char *app_name)
+{
+	printf(NAME ": query USB devices for descriptors\n\n");
+	printf("Usage: %s /path/to/hc usb-address\n where\n", app_name);
+	printf("   /path/to/hc   Devman path to USB host controller " \
+	    "(use `-' for\n");
+	printf("                   default HC at `%s').\n",
+	    DEFAULT_HOST_CONTROLLER_PATH);
+	printf("   usb-address   USB address of device to be queried\n");
+	printf("\n");
+}
+
+static int connect_to_hc(const char *path)
+{
+	int rc;
+	devman_handle_t handle;
+
+	rc = devman_device_get_handle(path, &handle, 0);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	int phone = devman_device_connect(handle, 0);
+
+	return phone;
+}
+
+int main(int argc, char *argv[])
+{
+	if (argc != 3) {
+		print_usage(argv[0]);
+		return EINVAL;
+	}
+
+	char *hc_path = argv[1];
+	long int address_long = strtol(argv[2], NULL, 0);
+
+	/*
+	 * Connect to given host controller driver.
+	 */
+	if (str_cmp(hc_path, "-") == 0) {
+		hc_path = (char *) DEFAULT_HOST_CONTROLLER_PATH;
+	}
+	int hc_phone = connect_to_hc(hc_path);
+	if (hc_phone < 0) {
+		fprintf(stderr,
+		    NAME ": unable to connect to HC at `%s': %s.\n",
+		    hc_path, str_error(hc_phone));
+		return hc_phone;
+	}
+
+	/*
+	 * Verify address is okay.
+	 */
+	usb_address_t address = (usb_address_t) address_long;
+	if ((address < 0) || (address >= USB11_ADDRESS_MAX)) {
+		fprintf(stderr, NAME ": USB address out of range.\n");
+		return ERANGE;
+	}
+
+	/*
+	 * Now, learn information about the device.
+	 */
+	int rc;
+
+	/*
+	 * Dump information about possible match ids.
+	 */
+	match_id_list_t match_id_list;
+	init_match_ids(&match_id_list);
+	rc = usb_drv_create_device_match_ids(hc_phone, &match_id_list, address);
+	if (rc != EOK) {
+		fprintf(stderr,
+		    NAME ": failed to fetch match ids of the device: %s.\n",
+		    str_error(rc));
+		return rc;
+	}
+	dump_match_ids(&match_id_list);
+
+	/*
+	 * Get device descriptor and dump it.
+	 */
+	usb_standard_device_descriptor_t device_descriptor;
+	usb_dprintf(NAME, 1,
+	    "usb_drv_req_get_device_descriptor(%d, %d, %p)\n",
+	    hc_phone, (int) address, &device_descriptor);
+
+	rc = usb_drv_req_get_device_descriptor(hc_phone, address,
+	    &device_descriptor);
+	if (rc != EOK) {
+		fprintf(stderr,
+		    NAME ": failed to fetch standard device descriptor: %s.\n",
+		    str_error(rc));
+		return rc;
+	}
+	dump_standard_device_descriptor(&device_descriptor);
+
+	/*
+	 * Get first configuration descriptor and dump it.
+	 */
+	usb_standard_configuration_descriptor_t config_descriptor;
+	int config_index = 0;
+	usb_dprintf(NAME, 1,
+	    "usb_drv_req_get_bare_configuration_descriptor(%d, %d, %d, %p)\n",
+	    hc_phone, (int) address, config_index, &config_descriptor);
+
+	rc = usb_drv_req_get_bare_configuration_descriptor(hc_phone, address,
+	    config_index, &config_descriptor );
+	if (rc != EOK) {
+		fprintf(stderr,
+		    NAME ": failed to fetch standard configuration descriptor: %s.\n",
+		    str_error(rc));
+		return rc;
+	}
+	dump_standard_configuration_descriptor(config_index,
+	    &config_descriptor);
+
+	void *full_config_descriptor = malloc(config_descriptor.total_length);
+	usb_dprintf(NAME, 1,
+	    "usb_drv_req_get_full_configuration_descriptor(%d, %d, %d, %p, %zu)\n",
+	    hc_phone, (int) address, config_index,
+	    full_config_descriptor, config_descriptor.total_length);
+
+	rc = usb_drv_req_get_full_configuration_descriptor(hc_phone, address,
+	    config_index,
+	    full_config_descriptor, config_descriptor.total_length, NULL);
+	if (rc != EOK) {
+		fprintf(stderr,
+		    NAME ": failed to fetch full configuration descriptor: %s.\n",
+		    str_error(rc));
+		return rc;
+	}
+	dump_buffer("Full configuration descriptor:",
+	    full_config_descriptor, config_descriptor.total_length);
+
+	return EOK;
+}
+
+
+/** @}
+ */
Index: uspace/app/usbinfo/usbinfo.h
===================================================================
--- uspace/app/usbinfo/usbinfo.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/usbinfo/usbinfo.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -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 Common header for usbinfo application.
+ */
+#ifndef USBINFO_USBINFO_H_
+#define USBINFO_USBINFO_H_
+
+#include <usb/usb.h>
+#include <usb/descriptor.h>
+#include <usb/debug.h>
+#include <ipc/devman.h>
+
+
+#define NAME "usbinfo"
+
+void dump_buffer(const char *, const uint8_t *, size_t);
+void dump_match_ids(match_id_list_t *matches);
+void dump_standard_device_descriptor(usb_standard_device_descriptor_t *);
+void dump_standard_configuration_descriptor(int, 
+    usb_standard_configuration_descriptor_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/app/virtusbkbd/Makefile
===================================================================
--- uspace/app/virtusbkbd/Makefile	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/virtusbkbd/Makefile	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,43 @@
+#
+# 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 = ../..
+# acronym for virtual usb keyboard
+# (it is really annoying to write long names)
+BINARY = vuk
+
+LIBS = $(LIBUSB_PREFIX)/libusb.a $(LIBUSBVIRT_PREFIX)/libusbvirt.a
+EXTRA_CFLAGS = -I$(LIBUSB_PREFIX)/include -I$(LIBUSBVIRT_PREFIX)/include -I$(LIBDRV_PREFIX)/include
+
+SOURCES = \
+	kbdconfig.c \
+	keys.c \
+	stdreq.c \
+	virtusbkbd.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/virtusbkbd/descriptor.h
===================================================================
--- uspace/app/virtusbkbd/descriptor.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/virtusbkbd/descriptor.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,51 @@
+/*
+ * 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 keyboard descriptors.
+ */
+#ifndef VUK_DESCRIPTOR_H_
+#define VUK_DESCRIPTOR_H_
+
+typedef struct {
+	uint8_t length;
+	uint8_t type;
+	uint16_t hid_spec_release;
+	uint8_t country_code;
+	uint8_t descriptor_count;
+	uint8_t descriptor1_type;
+	uint16_t descriptor1_length;
+} __attribute__ ((packed)) hid_descriptor_t;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/app/virtusbkbd/items.h
===================================================================
--- uspace/app/virtusbkbd/items.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/virtusbkbd/items.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,113 @@
+/*
+ * 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 HID Item related functions.
+ */
+#ifndef VUK_ITEM_H_
+#define VUK_ITEM_H_
+
+#include <sys/types.h>
+
+typedef uint8_t report_descriptor_data_t[];
+
+/* Item types. */
+#define ITEM_MAIN 0
+#define ITEM_GLOBAL 1
+#define ITEM_LOCAL 2
+
+
+
+/* Item tags. */
+
+/* Main item tags. */
+#define TAG_INPUT 8
+#define TAG_OUTPUT 9
+#define TAG_FEATURE 11
+#define TAG_COLLECTION 10
+#define TAG_END_COLLECTION 12
+
+/* Global item tags. */
+#define TAG_USAGE_PAGE 0
+#define TAG_LOGICAL_MINIMUM 1
+#define TAG_LOGICAL_MAXIMUM 2
+#define TAG_REPORT_SIZE 7
+#define TAG_REPORT_COUNT 9
+
+/* Local item tags. */
+#define TAG_USAGE 0
+#define TAG_USAGE_MINIMUM 1
+#define TAG_USAGE_MAXIMUM 2
+
+
+/* Bits for Input, Output and Feature items. */
+#define _IOF(value, shift) ((value) << (shift))
+#define IOF_DATA _IOF(0, 0)
+#define IOF_CONSTANT _IOF(1, 0)
+#define IOF_ARRAY _IOF(0, 1)
+#define IOF_VARIABLE _IOF(1, 1)
+#define IOF_ABSOLUTE _IOF(0, 2)
+#define IOF_RELATIVE _IOF(1, 2)
+/* ... */
+
+/* Collection types. */
+#define COLLECTION_PHYSICAL 0x00
+#define COLLECTION_APPLICATION 0x01
+
+
+/** Creates item prefix.
+ * @param size Item size.
+ * @param type Item type.
+ * @param tag Item tag.
+ */
+#define BUILD_ITEM_PREFIX(size, type, tag) \
+	((size) | ((type) << 2) | ((tag) << 4))
+
+/** Create no-data item.
+ * @param type Item type.
+ * @param tag Item tag.
+ */
+#define ITEM_CREATE0(type, tag) \
+	BUILD_ITEM_PREFIX(0, type, tag)
+
+/** Create item with 1-byte data.
+ * @param type Item type.
+ * @param tag Item tag.
+ * @param data Item data (single byte).
+ */
+#define ITEM_CREATE1(type, tag, data) \
+	BUILD_ITEM_PREFIX(1, type, tag), data
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/app/virtusbkbd/kbdconfig.c
===================================================================
--- uspace/app/virtusbkbd/kbdconfig.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/virtusbkbd/kbdconfig.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,155 @@
+/*
+ * 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 Keyboard configuration.
+ */
+#include "kbdconfig.h"
+#include "keys.h"
+#include <usb/usb.h>
+#include <usb/classes/hid.h>
+#include <usb/classes/hidut.h>
+#include <usb/classes/classes.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_USE_INTERFACE,
+	.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_HID,
+	.interface_subclass = 0,
+	.interface_protocol = USB_HID_PROTOCOL_KEYBOARD,
+	.str_interface = 0
+};
+
+/** USB keyboard report descriptor.
+ * Copied from USB HID 1.11 (section E.6).
+ */
+report_descriptor_data_t report_descriptor = {
+	STD_USAGE_PAGE(USB_HIDUT_PAGE_GENERIC_DESKTOP),
+	USAGE1(USB_HIDUT_USAGE_GENERIC_DESKTOP_KEYBOARD),
+	START_COLLECTION(COLLECTION_APPLICATION),
+		STD_USAGE_PAGE(USB_HIDUT_PAGE_KEYBOARD),
+		USAGE_MINIMUM1(224),
+		USAGE_MAXIMUM1(231),
+		LOGICAL_MINIMUM1(0),
+		LOGICAL_MAXIMUM1(1),
+		REPORT_SIZE1(1),
+		REPORT_COUNT1(8),
+		/* Modifiers */
+		INPUT(IOF_DATA | IOF_VARIABLE | IOF_ABSOLUTE),
+		REPORT_COUNT1(1),
+		REPORT_SIZE1(8),
+		/* Reserved */
+		INPUT(IOF_CONSTANT),
+		REPORT_COUNT1(5),
+		REPORT_SIZE1(1),
+		STD_USAGE_PAGE(USB_HIDUT_PAGE_LED),
+		USAGE_MINIMUM1(1),
+		USAGE_MAXIMUM1(5),
+		/* LED states */
+		OUTPUT(IOF_DATA | IOF_VARIABLE | IOF_ABSOLUTE),
+		REPORT_COUNT1(1),
+		REPORT_SIZE1(3),
+		/* LED states padding */
+		OUTPUT(IOF_CONSTANT),
+		REPORT_COUNT1(KB_MAX_KEYS_AT_ONCE),
+		REPORT_SIZE1(8),
+		LOGICAL_MINIMUM1(0),
+		LOGICAL_MAXIMUM1(101),
+		STD_USAGE_PAGE(USB_HIDUT_PAGE_KEYBOARD),
+		USAGE_MINIMUM1(0),
+		USAGE_MAXIMUM1(101),
+		/* Key array */
+		INPUT(IOF_DATA | IOF_ARRAY),
+	END_COLLECTION()
+};
+size_t report_descriptor_size = sizeof(report_descriptor);
+
+/** HID descriptor. */
+hid_descriptor_t hid_descriptor = {
+	.length = sizeof(hid_descriptor_t),
+	.type = 0x21, // HID descriptor
+	.hid_spec_release = 0x101,
+	.country_code = 0,
+	.descriptor_count = 1,
+	.descriptor1_type = 0x22, // Report descriptor
+	.descriptor1_length = sizeof(report_descriptor)
+};
+
+/** Endpoint descriptor. */
+usb_standard_endpoint_descriptor_t endpoint_descriptor = {
+	.length = sizeof(usb_standard_endpoint_descriptor_t),
+	.descriptor_type = USB_DESCTYPE_ENDPOINT,
+	.endpoint_address = 1 | 128,
+	.attributes = USB_TRANSFER_INTERRUPT,
+	.max_packet_size = 8,
+	.poll_interval = 10
+};
+
+/** 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(hid_descriptor)
+		+ sizeof(endpoint_descriptor)
+		,
+	.interface_count = 1,
+	.configuration_number = 1,
+	.str_configuration = 0,
+	.attributes = 128, /* denotes bus-powered device */
+	.max_power = 50
+};
+
+
+
+
+
+/** @}
+ */
Index: uspace/app/virtusbkbd/kbdconfig.h
===================================================================
--- uspace/app/virtusbkbd/kbdconfig.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/virtusbkbd/kbdconfig.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,60 @@
+/*
+ * 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 keyboard configuration.
+ */
+#ifndef VUK_KBDCONFIG_H_
+#define VUK_KBDCONFIG_H_
+
+#include <usb/descriptor.h>
+#include "report.h"
+#include "descriptor.h"
+
+extern usb_standard_device_descriptor_t std_device_descriptor;
+
+extern usb_standard_configuration_descriptor_t std_configuration_descriptor;
+
+extern usb_standard_interface_descriptor_t std_interface_descriptor;
+
+extern usb_standard_endpoint_descriptor_t endpoint_descriptor;
+
+
+extern hid_descriptor_t hid_descriptor;
+
+extern report_descriptor_data_t report_descriptor;
+extern size_t report_descriptor_size;
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/app/virtusbkbd/keys.c
===================================================================
--- uspace/app/virtusbkbd/keys.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/virtusbkbd/keys.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,123 @@
+/*
+ * 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 Keyboard keys related structures.
+ */
+#include "keys.h"
+
+/** Initializes keyboard status. */
+void kb_init(kb_status_t *status)
+{
+	status->modifiers = 0;
+	size_t i;
+	for (i = 0; i < KB_MAX_KEYS_AT_ONCE; i++) {
+		status->pressed_keys[i] = 0;
+	}
+}
+
+/** Change pressed modifiers. */
+void kb_change_modifier(kb_status_t *status, kb_key_action_t action,
+    kb_modifier_t modifier)
+{
+	if (action == KB_KEY_DOWN) {
+		status->modifiers = status->modifiers | modifier;
+	} else {
+		status->modifiers = status->modifiers & (~modifier);
+	}
+}
+
+/** Find index of given key in key code array.
+ * @retval (size_t)-1 Key not found.
+ */
+static size_t find_key_index(kb_key_code_t *keys, size_t size, kb_key_code_t key)
+{
+	size_t i;
+	for (i = 0; i < size; i++) {
+		if (keys[i] == key) {
+			return i;
+		}
+	}
+	return (size_t)-1;
+}
+
+/** Change pressed key. */
+void kb_change_key(kb_status_t *status, kb_key_action_t action,
+    kb_key_code_t key_code)
+{
+	size_t pos = find_key_index(status->pressed_keys, KB_MAX_KEYS_AT_ONCE,
+	    key_code);
+	if (action == KB_KEY_DOWN) {
+		if (pos != (size_t)-1) {
+			return;
+		}
+		/*
+		 * Find first free item in the array.
+		 */
+		size_t i;
+		for (i = 0; i < KB_MAX_KEYS_AT_ONCE; i++) {
+			if (status->pressed_keys[i] == 0) {
+				status->pressed_keys[i] = key_code;
+				return;
+			}
+		}
+		// TODO - handle buffer overflow
+	} else {
+		if (pos == (size_t)-1) {
+			return;
+		}
+		status->pressed_keys[pos] = 0;
+	}	
+}
+
+/** Process list of events. */
+void kb_process_events(kb_status_t *status,
+    kb_event_t *events, size_t count,
+    kb_on_status_change on_change)
+{
+	while (count-- > 0) {
+		if (events->normal_key) {
+			kb_change_key(status, events->action,
+			    events->key_change);
+		} else {
+			kb_change_modifier(status, events->action,
+			    events->modifier_change);
+		}
+		if (on_change) {
+			on_change(status);
+		}
+		events++;
+	}
+}
+
+/** @}
+ */
Index: uspace/app/virtusbkbd/keys.h
===================================================================
--- uspace/app/virtusbkbd/keys.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/virtusbkbd/keys.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,121 @@
+/*
+ * 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 Keyboard keys related structures.
+ */
+#ifndef VUK_KEYS_H_
+#define VUK_KEYS_H_
+
+#include <sys/types.h>
+#include <bool.h>
+
+/** Maximum number of keys that can be pressed simultaneously. */
+#define KB_MAX_KEYS_AT_ONCE 4
+
+/** Key code type. */
+typedef uint8_t kb_key_code_t;
+
+#define USB_HIDUT_KBD_KEY(name, usage_id, l, lc, l1, l2) \
+	KB_KEY_##name = usage_id,
+/** USB key code. */
+typedef enum {
+	#include <usb/classes/hidutkbd.h>
+} key_code_t;
+
+/** Modifier type. */
+typedef uint8_t kb_modifier_t;
+#define _KB_MOD(shift) (1 << (shift))
+#define KB_MOD_LEFT_CTRL _KB_MOD(0)
+#define KB_MOD_LEFT_SHIFT _KB_MOD(1)
+#define KB_MOD_LEFT_ALT _KB_MOD(2)
+#define KB_MOD_LEFT_GUI _KB_MOD(3)
+#define KB_MOD_RIGHT_CTRL _KB_MOD(4)
+#define KB_MOD_RIGHT_SHIFT _KB_MOD(5)
+#define KB_MOD_RIGHT_ALT _KB_MOD(6)
+#define KB_MOD_RIGHT_GUI _KB_MOD(7)
+
+/** Base key action. */
+typedef enum {
+	KB_KEY_DOWN,
+	KB_KEY_UP
+} kb_key_action_t;
+
+/** Keyboard status. */
+typedef struct {
+	/** Bitmap of pressed modifiers. */
+	kb_modifier_t modifiers;
+	/** Array of currently pressed keys. */
+	kb_key_code_t pressed_keys[KB_MAX_KEYS_AT_ONCE];
+} kb_status_t;
+
+/** Callback type for status change. */
+typedef void (*kb_on_status_change)(kb_status_t *);
+
+
+void kb_init(kb_status_t *);
+void kb_change_modifier(kb_status_t *, kb_key_action_t, kb_modifier_t);
+void kb_change_key(kb_status_t *, kb_key_action_t, kb_key_code_t);
+
+/** Keyboard event.
+ * Use macros M_DOWN, M_UP, K_DOWN, K_UP, K_PRESS to generate list
+ * of them.
+ */
+typedef struct {
+	/** Key action. */
+	kb_key_action_t action;
+	/** Switch whether action is about modifier or normal key. */
+	bool normal_key;
+	/** Modifier change. */
+	kb_modifier_t modifier_change;
+	/** Normal key change. */
+	kb_key_code_t key_change;
+} kb_event_t;
+
+#define M_DOWN(mod) \
+	{ KB_KEY_DOWN, false, (mod), 0 }
+#define M_UP(mod) \
+	{ KB_KEY_UP, false, (mod), 0 }
+#define K_DOWN(key) \
+	{ KB_KEY_DOWN, true, 0, (key) }
+#define K_UP(key) \
+	{ KB_KEY_UP, true, 0, (key) }
+#define K_PRESS(key) \
+	K_DOWN(key), K_UP(key)
+
+
+void kb_process_events(kb_status_t *, kb_event_t *, size_t, kb_on_status_change);
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/app/virtusbkbd/report.h
===================================================================
--- uspace/app/virtusbkbd/report.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/virtusbkbd/report.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -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 HID Report Descriptor.
+ */
+#ifndef VUK_REPORT_H_
+#define VUK_REPORT_H_
+
+#include "items.h"
+
+/** Use standard Usage Page. */
+#define STD_USAGE_PAGE(page) \
+	ITEM_CREATE1(ITEM_GLOBAL, TAG_USAGE_PAGE, page)
+
+/** Usage with one byte usage ID. */
+#define USAGE1(usage_id) \
+	ITEM_CREATE1(ITEM_LOCAL, TAG_USAGE, usage_id)
+
+/** Start a collection. */
+#define START_COLLECTION(collection) \
+	ITEM_CREATE1(ITEM_MAIN, TAG_COLLECTION, collection)
+
+/** End a collection. */
+#define END_COLLECTION() \
+	ITEM_CREATE0(ITEM_MAIN, TAG_END_COLLECTION)
+
+
+#define USAGE_MINIMUM1(value) \
+	ITEM_CREATE1(ITEM_LOCAL, TAG_USAGE_MINIMUM, value)
+	
+#define USAGE_MAXIMUM1(value) \
+	ITEM_CREATE1(ITEM_LOCAL, TAG_USAGE_MAXIMUM, value)
+	
+#define LOGICAL_MINIMUM1(value) \
+	ITEM_CREATE1(ITEM_GLOBAL, TAG_LOGICAL_MINIMUM, value)
+
+#define LOGICAL_MAXIMUM1(value) \
+	ITEM_CREATE1(ITEM_GLOBAL, TAG_LOGICAL_MAXIMUM, value)
+
+#define REPORT_SIZE1(size) \
+	ITEM_CREATE1(ITEM_GLOBAL, TAG_REPORT_SIZE, size)
+
+#define REPORT_COUNT1(count) \
+	ITEM_CREATE1(ITEM_GLOBAL, TAG_REPORT_COUNT, count)
+	
+#define INPUT(modifiers) \
+	ITEM_CREATE1(ITEM_MAIN, TAG_INPUT, modifiers)
+
+#define OUTPUT(modifiers) \
+	ITEM_CREATE1(ITEM_MAIN, TAG_OUTPUT, modifiers)
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/app/virtusbkbd/stdreq.c
===================================================================
--- uspace/app/virtusbkbd/stdreq.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/virtusbkbd/stdreq.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,62 @@
+/*
+ * 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 Keyboard configuration.
+ */
+#include <errno.h>
+#include <usb/descriptor.h>
+#include "stdreq.h"
+#include "kbdconfig.h"
+
+int stdreq_on_get_descriptor(struct usbvirt_device *dev,
+    usb_device_request_setup_packet_t *request, uint8_t *data)
+{
+	if (request->value_high == USB_DESCTYPE_HID_REPORT) {
+		/*
+		 * For simplicity, always return the same
+		 * report descriptor.
+		 */
+		int rc = dev->control_transfer_reply(dev, 0,
+		    report_descriptor, report_descriptor_size);
+		
+		return rc;
+	}
+	
+	/* Let the framework handle all the rest. */
+	return EFORWARD;
+}
+
+
+
+/** @}
+ */
Index: uspace/app/virtusbkbd/stdreq.h
===================================================================
--- uspace/app/virtusbkbd/stdreq.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/virtusbkbd/stdreq.h	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,46 @@
+/*
+ * 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 Standard device request handlers.
+ */
+#ifndef VUK_STDREQ_H_
+#define VUK_STDREQ_H_
+
+#include <usbvirt/device.h>
+
+int stdreq_on_get_descriptor(usbvirt_device_t *,
+    usb_device_request_setup_packet_t *, uint8_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/app/virtusbkbd/virtusbkbd.c
===================================================================
--- uspace/app/virtusbkbd/virtusbkbd.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
+++ uspace/app/virtusbkbd/virtusbkbd.c	(revision 8dd039aa2d3be181c5034b982b31181c20a58a41)
@@ -0,0 +1,287 @@
+/*
+ * 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 keyboard.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <vfs/vfs.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <str_error.h>
+#include <bool.h>
+#include <async.h>
+
+#include <usb/usb.h>
+#include <usb/descriptor.h>
+#include <usb/classes/hid.h>
+#include <usbvirt/device.h>
+#include <usbvirt/hub.h>
+
+#include "kbdconfig.h"
+#include "keys.h"
+#include "stdreq.h"
+
+/** Pause between individual key-presses in seconds. */
+#define KEY_PRESS_DELAY 2
+#define NAME "virt-usb-kbd"
+
+
+#define __QUOTEME(x) #x
+#define _QUOTEME(x) __QUOTEME(x)
+
+#define VERBOSE_EXEC(cmd, fmt, ...) \
+	(printf("%s: %s" fmt "\n", NAME, _QUOTEME(cmd), __VA_ARGS__), cmd(__VA_ARGS__))
+
+kb_status_t status;
+
+static int on_incoming_data(struct usbvirt_device *dev,
+    usb_endpoint_t endpoint, void *buffer, size_t size)
+{
+	printf("%s: ignoring incomming data to endpoint %d\n", NAME, endpoint);
+	
+	return EOK;
+}
+
+
+/** Compares current and last status of pressed keys.
+ *
+ * @warning Has side-efect - changes status_last field.
+ *
+ * @param status_now Status now.
+ * @param status_last Last status.
+ * @param len Size of status.
+ * @return Whether they are the same.
+ */
+static bool keypress_check_with_last_request(uint8_t *status_now,
+    uint8_t *status_last, size_t len)
+{
+	bool same = true;
+	size_t i;
+	for (i = 0; i < len; i++) {
+		if (status_now[i] != status_last[i]) {
+			status_last[i] = status_now[i];
+			same = false;
+		}
+	}
+	return same;
+}
+
+static int on_request_for_data(struct usbvirt_device *dev,
+    usb_endpoint_t endpoint, void *buffer, size_t size, size_t *actual_size)
+{
+	static uint8_t last_data[2 + KB_MAX_KEYS_AT_ONCE];
+
+	if (size < 2 + KB_MAX_KEYS_AT_ONCE) {
+		return EINVAL;
+	}
+	
+	*actual_size = 2 + KB_MAX_KEYS_AT_ONCE;
+	
+	uint8_t data[2 + KB_MAX_KEYS_AT_ONCE];
+	data[0] = status.modifiers;
+	data[1] = 0;
+	
+	size_t i;
+	for (i = 0; i < KB_MAX_KEYS_AT_ONCE; i++) {
+		data[2 + i] = status.pressed_keys[i];
+	}
+	
+	if (keypress_check_with_last_request(data, last_data,
+	    2 + KB_MAX_KEYS_AT_ONCE)) {
+		*actual_size = 0;
+		return EOK;
+	}
+
+	memcpy(buffer, &data, *actual_size);
+	
+	return EOK;
+}
+
+static usbvirt_control_transfer_handler_t endpoint_zero_handlers[] = {
+	{
+		.request_type = USBVIRT_MAKE_CONTROL_REQUEST_TYPE(
+		    USB_DIRECTION_IN,
+		    USBVIRT_REQUEST_TYPE_STANDARD,
+		    USBVIRT_REQUEST_RECIPIENT_DEVICE),
+		.request = USB_DEVREQ_GET_DESCRIPTOR,
+		.name = "GetDescriptor",
+		.callback = stdreq_on_get_descriptor
+	},
+	{
+		.request_type = USBVIRT_MAKE_CONTROL_REQUEST_TYPE(
+		    USB_DIRECTION_IN,
+		    USBVIRT_REQUEST_TYPE_CLASS,
+		    USBVIRT_REQUEST_RECIPIENT_DEVICE),
+		.request = USB_DEVREQ_GET_DESCRIPTOR,
+		.name = "GetDescriptor",
+		.callback = stdreq_on_get_descriptor
+	},
+	USBVIRT_CONTROL_TRANSFER_HANDLER_LAST
+};
+
+/** Keyboard callbacks.
+ * We abuse the fact that static variables are zero-filled.
+ */
+static usbvirt_device_ops_t keyboard_ops = {
+	.control_transfer_handlers = endpoint_zero_handlers,
+	.on_data = on_incoming_data,
+	.on_data_request = on_request_for_data
+};
+
+usbvirt_device_configuration_extras_t extra_descriptors[] = {
+	{
+		.data = (uint8_t *) &std_interface_descriptor,
+		.length = sizeof(std_interface_descriptor)
+	},
+	{
+		.data = (uint8_t *) &hid_descriptor,
+		.length = sizeof(hid_descriptor)
+	},
+	{
+		.data = (uint8_t *) &endpoint_descriptor,
+		.length = sizeof(endpoint_descriptor)
+	}
+};
+
+/** Keyboard configuration. */
+usbvirt_device_configuration_t configuration = {
+	.descriptor = &std_configuration_descriptor,
+	.extra = extra_descriptors,
+	.extra_count = sizeof(extra_descriptors)/sizeof(extra_descriptors[0])
+};
+
+/** Keyboard standard descriptors. */
+usbvirt_descriptors_t descriptors = {
+	.device = &std_device_descriptor,
+	.configuration = &configuration,
+	.configuration_count = 1,
+};
+
+/** Keyboard device.
+ * Rest of the items will be initialized later.
+ */
+static usbvirt_device_t keyboard_dev = {
+	.ops = &keyboard_ops,
+	.descriptors = &descriptors,
+	.lib_debug_level = 3,
+	.lib_debug_enabled_tags = USBVIRT_DEBUGTAG_ALL,
+	.name = "keyboard"
+};
+
+
+static void fibril_sleep(size_t sec)
+{
+	while (sec-- > 0) {
+		async_usleep(1000*1000);
+	}
+}
+
+
+/** Callback when keyboard status changed.
+ *
+ * @param status Current keyboard status.
+ */
+static void on_keyboard_change(kb_status_t *status)
+{
+	printf("%s: Current keyboard status: %02hhx", NAME, status->modifiers);
+	size_t i;
+	for (i = 0; i < KB_MAX_KEYS_AT_ONCE; i++) {
+		printf(" 0x%02X", (int)status->pressed_keys[i]);
+	}
+	printf("\n");
+	
+	fibril_sleep(KEY_PRESS_DELAY);
+}
+
+/** Simulated keyboard events. */
+static kb_event_t keyboard_events[] = {
+	/* Switch to VT6 (Alt+F6) */
+	M_DOWN(KB_MOD_LEFT_ALT),
+	K_PRESS(KB_KEY_F6),
+	M_UP(KB_MOD_LEFT_ALT),
+	/* Type the word 'Hello' */
+	M_DOWN(KB_MOD_LEFT_SHIFT),
+	K_PRESS(KB_KEY_H),
+	M_UP(KB_MOD_LEFT_SHIFT),
+	K_PRESS(KB_KEY_E),
+	K_PRESS(KB_KEY_L),
+	K_PRESS(KB_KEY_L),
+	K_PRESS(KB_KEY_O)
+};
+static size_t keyboard_events_count =
+    sizeof(keyboard_events)/sizeof(keyboard_events[0]);
+
+
+
+int main(int argc, char * argv[])
+{
+	printf("Dump of report descriptor (%zu bytes):\n", report_descriptor_size);
+	size_t i;
+	for (i = 0; i < report_descriptor_size; i++) {
+		printf("  0x%02X", report_descriptor[i]);
+		if (((i > 0) && (((i+1) % 10) == 0))
+		    || (i + 1 == report_descriptor_size)) {
+			printf("\n");
+		}
+	}
+	
+	kb_init(&status);
+	
+	
+	int rc = usbvirt_connect(&keyboard_dev);
+	if (rc != EOK) {
+		printf("%s: Unable to start communication with VHCD (%s).\n",
+		    NAME, str_error(rc));
+		return rc;
+	}
+	
+	printf("%s: Simulating keyboard events...\n", NAME);
+	fibril_sleep(10);
+	while (1) {
+		kb_process_events(&status, keyboard_events, keyboard_events_count,
+			on_keyboard_change);
+	}
+	
+	printf("%s: Terminating...\n", NAME);
+	
+	usbvirt_disconnect(&keyboard_dev);
+	
+	return 0;
+}
+
+
+/** @}
+ */
