Index: uspace/app/init/init.c
===================================================================
--- uspace/app/init/init.c	(revision 5d1b3aa2ae833977640704ed7cf0ed0ad531bb41)
+++ uspace/app/init/init.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -272,7 +272,4 @@
 	mount_tmpfs();
 	
-#ifdef CONFIG_START_DEVMAN
-	spawn("/srv/devman");
-#endif
 	spawn("/srv/apic");
 	spawn("/srv/i8259");
@@ -316,5 +313,15 @@
 	getterm("term/vc5", "/app/bdsh", false);
 	getterm("term/vc6", "/app/klog", false);
-	
+
+#ifdef CONFIG_START_DEVMAN
+
+#ifdef CONFIG_DEVMAN_EARLY_LAUNCH
+	spawn("/srv/devman");
+#else
+	getterm("term/vc7", "/srv/devman", false);
+#endif
+
+#endif
+
 	return 0;
 }
Index: uspace/app/lsusb/Makefile
===================================================================
--- uspace/app/lsusb/Makefile	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/lsusb/Makefile	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 2011 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 = lsusb
+
+LIBS = \
+	$(LIBUSBDEV_PREFIX)/libusbdev.a \
+	$(LIBUSB_PREFIX)/libusb.a \
+	$(LIBDRV_PREFIX)/libdrv.a
+EXTRA_CFLAGS = \
+	-I$(LIBUSB_PREFIX)/include \
+	-I$(LIBUSBDEV_PREFIX)/include \
+	-I$(LIBDRV_PREFIX)/include
+
+SOURCES = \
+	main.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/lsusb/main.c
===================================================================
--- uspace/app/lsusb/main.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/lsusb/main.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2010-2011 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 lsusb
+ * @{
+ */
+/**
+ * @file
+ * Listing of USB host controllers.
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <str_error.h>
+#include <bool.h>
+#include <getopt.h>
+#include <devman.h>
+#include <devmap.h>
+#include <usb/dev/hub.h>
+#include <usb/hc.h>
+
+#define NAME "lsusb"
+
+#define MAX_USB_ADDRESS USB11_ADDRESS_MAX
+#define MAX_FAILED_ATTEMPTS 10
+#define MAX_PATH_LENGTH 1024
+
+static void print_found_hc(size_t class_index, const char *path)
+{
+	// printf(NAME ": host controller %zu is `%s'.\n", class_index, path);
+	printf("Bus %02zu: %s\n", class_index, path);
+}
+static void print_found_dev(usb_address_t addr, const char *path)
+{
+	// printf(NAME ":     device with address %d is `%s'.\n", addr, path);
+	printf("  Device %02d: %s\n", addr, path);
+}
+
+static void print_hc_devices(devman_handle_t hc_handle)
+{
+	int rc;
+	usb_hc_connection_t conn;
+
+	usb_hc_connection_initialize(&conn, hc_handle);
+	rc = usb_hc_connection_open(&conn);
+	if (rc != EOK) {
+		printf(NAME ": failed to connect to HC: %s.\n",
+		    str_error(rc));
+		return;
+	}
+	usb_address_t addr;
+	for (addr = 1; addr < MAX_USB_ADDRESS; addr++) {
+		devman_handle_t dev_handle;
+		rc = usb_hc_get_handle_by_address(&conn, addr, &dev_handle);
+		if (rc != EOK) {
+			continue;
+		}
+		char path[MAX_PATH_LENGTH];
+		rc = devman_get_device_path(dev_handle, path, MAX_PATH_LENGTH);
+		if (rc != EOK) {
+			continue;
+		}
+		print_found_dev(addr, path);
+	}
+	usb_hc_connection_close(&conn);
+}
+
+int main(int argc, char *argv[])
+{
+	size_t class_index = 0;
+	size_t failed_attempts = 0;
+
+	while (failed_attempts < MAX_FAILED_ATTEMPTS) {
+		class_index++;
+		devman_handle_t hc_handle = 0;
+		int rc = usb_ddf_get_hc_handle_by_class(class_index, &hc_handle);
+		if (rc != EOK) {
+			failed_attempts++;
+			continue;
+		}
+		char path[MAX_PATH_LENGTH];
+		rc = devman_get_device_path(hc_handle, path, MAX_PATH_LENGTH);
+		if (rc != EOK) {
+			continue;
+		}
+		print_found_hc(class_index, path);
+		print_hc_devices(hc_handle);
+	}
+
+	return 0;
+}
+
+
+/** @}
+ */
Index: uspace/app/mkbd/Makefile
===================================================================
--- uspace/app/mkbd/Makefile	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/mkbd/Makefile	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,46 @@
+#
+# Copyright (c) 2011 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 = mkbd
+
+LIBS = \
+	$(LIBUSBHID_PREFIX)/libusbhid.a \
+	$(LIBUSBDEV_PREFIX)/libusbdev.a \
+	$(LIBUSB_PREFIX)/libusb.a \
+	$(LIBDRV_PREFIX)/libdrv.a 
+EXTRA_CFLAGS = \
+	-I$(LIBUSB_PREFIX)/include \
+	-I$(LIBUSBDEV_PREFIX)/include \
+	-I$(LIBDRV_PREFIX)/include \
+	-I$(LIBUSBHID_PREFIX)/include 
+
+SOURCES = \
+	main.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/mkbd/main.c
===================================================================
--- uspace/app/mkbd/main.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/mkbd/main.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2011 Lubos Slovak
+ * 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 mkbd
+ * @{
+ */
+/**
+ * @file
+ * Sample application using the data from multimedia keys on keyboard
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <str_error.h>
+#include <bool.h>
+#include <getopt.h>
+#include <devman.h>
+#include <devmap.h>
+#include <usb/dev/hub.h>
+//#include <usb/host.h>
+//#include <usb/driver.h>
+#include <usb/hid/iface.h>
+#include <usb/dev/pipes.h>
+#include <async.h>
+#include <usb/hid/usages/core.h>
+#include <usb/hid/hidparser.h>
+#include <usb/hid/hiddescriptor.h>
+#include <usb/hid/usages/consumer.h>
+#include <assert.h>
+
+#define NAME "mkbd"
+
+static int dev_phone = -1;
+
+static int initialize_report_parser(int dev_phone, usb_hid_report_t **report)
+{
+	*report = (usb_hid_report_t *)malloc(sizeof(usb_hid_report_t));
+	if (*report == NULL) {
+		return ENOMEM;
+	}
+	
+	int rc = usb_hid_report_init(*report);
+	if (rc != EOK) {
+		usb_hid_free_report(*report);
+		*report = NULL;
+		//printf("usb_hid_report_init() failed.\n");
+		return rc;
+	}
+	
+	// get the report descriptor length from the device
+	size_t report_desc_size;
+	rc = usbhid_dev_get_report_descriptor_length(
+	    dev_phone, &report_desc_size);
+	if (rc != EOK) {
+		usb_hid_free_report(*report);
+		*report = NULL;
+		//printf("usbhid_dev_get_report_descriptor_length() failed.\n");
+		return rc;
+	}
+	
+	if (report_desc_size == 0) {
+		usb_hid_free_report(*report);
+		*report = NULL;
+		//printf("usbhid_dev_get_report_descriptor_length() returned 0.\n");
+		return EINVAL;	// TODO: other error code?
+	}
+	
+	uint8_t *desc = (uint8_t *)malloc(report_desc_size);
+	if (desc == NULL) {
+		usb_hid_free_report(*report);
+		*report = NULL;
+		return ENOMEM;
+	}
+	
+	// get the report descriptor from the device
+	size_t actual_size;
+	rc = usbhid_dev_get_report_descriptor(dev_phone, desc, report_desc_size,
+	    &actual_size);
+	if (rc != EOK) {
+		usb_hid_free_report(*report);
+		*report = NULL;
+		free(desc);
+		//printf("usbhid_dev_get_report_descriptor() failed.\n");
+		return rc;
+	}
+	
+	if (actual_size != report_desc_size) {
+		usb_hid_free_report(*report);
+		*report = NULL;
+		free(desc);
+//		printf("usbhid_dev_get_report_descriptor() returned wrong size:"
+//		    " %zu, expected: %zu.\n", actual_size, report_desc_size);
+		return EINVAL;	// TODO: other error code?
+	}
+	
+	// initialize the report parser
+	
+	rc = usb_hid_parse_report_descriptor(*report, desc, report_desc_size);
+	free(desc);
+	
+	if (rc != EOK) {
+		free(desc);
+//		printf("usb_hid_parse_report_descriptor() failed.\n");
+		return rc;
+	}
+	
+	return EOK;
+}
+
+static void print_key(uint8_t *buffer, size_t size, usb_hid_report_t *report)
+{
+	assert(buffer != NULL);
+	assert(report != NULL);
+	
+//	printf("Calling usb_hid_parse_report() with size %zu and "
+//	    "buffer: \n", size);
+//	for (size_t i = 0; i < size; ++i) {
+//		printf(" %X ", buffer[i]);
+//	}
+//	printf("\n");
+	
+	uint8_t report_id;
+	int rc = usb_hid_parse_report(report, buffer, size, &report_id);
+	if (rc != EOK) {
+//		printf("Error parsing report: %s\n", str_error(rc));
+		return;
+	}
+	
+	usb_hid_report_path_t *path = usb_hid_report_path();
+	if (path == NULL) {
+		return;
+	}
+	
+	usb_hid_report_path_append_item(path, USB_HIDUT_PAGE_CONSUMER, 0);
+	
+	usb_hid_report_path_set_report_id(path, report_id);
+
+	usb_hid_report_field_t *field = usb_hid_report_get_sibling(
+	    report, NULL, path, USB_HID_PATH_COMPARE_END 
+	    | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY, 
+	    USB_HID_REPORT_TYPE_INPUT);
+	
+//	printf("Field: %p\n", field);
+	
+	while (field != NULL) {
+//		printf("Field usage: %u, field value: %d\n", field->usage, 
+//		    field->value);
+		if (field->value != 0) {
+			const char *key_str = 
+			    usbhid_multimedia_usage_to_str(field->usage);
+			printf("Pressed key: %s\n", key_str);
+		}
+		
+		field = usb_hid_report_get_sibling(
+		    report, field, path, USB_HID_PATH_COMPARE_END
+		    | USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY, 
+		    USB_HID_REPORT_TYPE_INPUT);
+//		printf("Next field: %p\n", field);
+	}
+	
+	usb_hid_report_path_free(path);
+}
+
+#define MAX_PATH_LENGTH 1024
+
+static void print_usage(char *app_name)
+{
+#define _INDENT "      "
+
+       printf(NAME ": Print out what multimedia keys were pressed.\n\n");
+       printf("Usage: %s device\n", app_name);
+       printf(_INDENT "The device is a devman path to the device.\n");
+
+#undef _OPTION
+#undef _INDENT
+}
+
+int main(int argc, char *argv[])
+{
+	int act_event = -1;
+	
+	if (argc <= 1) {
+		print_usage(argv[0]);
+		return -1;
+	}
+	
+	char *devpath = argv[1];
+	
+	devman_handle_t dev_handle = 0;
+	
+	int rc = usb_resolve_device_handle(devpath, NULL, NULL, &dev_handle);
+	if (rc != EOK) {
+		printf("Device not found or not of USB kind: %s.\n",
+		    str_error(rc));
+		return rc;
+	}
+	
+	rc = devman_device_connect(dev_handle, 0);
+	if (rc < 0) {
+		printf(NAME ": failed to connect to the device (handle %"
+		       PRIun "): %s.\n", dev_handle, str_error(rc));
+		return rc;
+	}
+	
+	dev_phone = rc;
+//	printf("Got phone to the device: %d\n", dev_phone);
+	
+	char path[MAX_PATH_LENGTH];
+	rc = devman_get_device_path(dev_handle, path, MAX_PATH_LENGTH);
+	if (rc != EOK) {
+		return ENOMEM;
+	}
+	
+	printf("Device path: %s\n", path);
+	
+	
+	usb_hid_report_t *report = NULL;
+	rc = initialize_report_parser(dev_phone, &report);
+	if (rc != EOK) {
+		printf("Failed to initialize report parser: %s\n",
+		    str_error(rc));
+		return rc;
+	}
+	
+	assert(report != NULL);
+	
+	size_t size;
+	rc = usbhid_dev_get_event_length(dev_phone, &size);
+	if (rc != EOK) {
+		printf("Failed to get event length: %s.\n", str_error(rc));
+		return rc;
+	}
+	
+//	printf("Event length: %zu\n", size);
+	uint8_t *event = (uint8_t *)malloc(size);
+	if (event == NULL) {
+		// hangup phone?
+		return ENOMEM;
+	}
+	
+//	printf("Event length: %zu\n", size);
+	
+	size_t actual_size;
+	int event_nr;
+	
+	while (1) {
+		// get event from the driver
+//		printf("Getting event from the driver.\n");
+		
+		/** @todo Try blocking call. */
+		rc = usbhid_dev_get_event(dev_phone, event, size, &actual_size, 
+		    &event_nr, 0);
+		if (rc != EOK) {
+			// hangup phone?
+			printf("Error in getting event from the HID driver:"
+			    "%s.\n", str_error(rc));
+			break;
+		}
+		
+//		printf("Got buffer: %p, size: %zu, max size: %zu\n", event, 
+//		    actual_size, size);
+		
+//		printf("Event number: %d, my actual event: %d\n", event_nr, 
+//		    act_event);
+		if (event_nr > act_event) {
+			print_key(event, size, report);
+			act_event = event_nr;
+		}
+		
+		async_usleep(10000);
+	}
+	
+	return 0;
+}
+
+
+/** @}
+ */
Index: uspace/app/tester/Makefile
===================================================================
--- uspace/app/tester/Makefile	(revision 5d1b3aa2ae833977640704ed7cf0ed0ad531bb41)
+++ uspace/app/tester/Makefile	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -51,4 +51,5 @@
 	mm/malloc1.c \
 	mm/malloc2.c \
+	mm/mapping1.c \
 	mm/malloc3.c \
 	devs/devman1.c \
Index: uspace/app/tester/mm/mapping1.c
===================================================================
--- uspace/app/tester/mm/mapping1.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/tester/mm/mapping1.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2011 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 <unistd.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <as.h>
+#include <errno.h>
+#include "../tester.h"
+
+#define BUFFER1_PAGES 4
+#define BUFFER2_PAGES 2
+
+static void *create_as_area(size_t size)
+{
+	void *result = as_get_mappable_page(size);
+	TPRINTF("Creating AS area...\n");
+	if (as_area_create(result, size,
+	    AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE) != result) {
+		return NULL;
+	}
+	return result;
+}
+
+static void touch_area(void *area, size_t size)
+{
+	TPRINTF("Touching (faulting-in) AS area...\n");
+	
+	char *ptr = (char *)area;
+	
+	while (size > 0) {
+		*ptr = 0;
+		size--;
+		ptr++;
+	}
+}
+
+#define VERIFY_MAPPING(area, page_count, expected_rc) \
+    verify_mapping((area), (page_count), (expected_rc), #expected_rc)
+
+static bool verify_mapping(void *area, int page_count, int expected_rc,
+    const char *expected_rc_str)
+{
+	TPRINTF("Verifying mapping (expected: %s).\n", expected_rc_str);
+	int i;
+	for (i = 0; i < page_count; i++) {
+		void *page_start = ((char *)area) + PAGE_SIZE * i;
+		int rc = as_get_physical_mapping(page_start, NULL);
+		if (rc != expected_rc) {
+			TPRINTF("as_get_physical_mapping() = %d != %d\n",
+			    rc, expected_rc);
+			return false;
+		}
+	}
+	return true;
+}
+
+const char *test_mapping1(void)
+{
+	int rc;
+	
+	size_t buffer1_len = BUFFER1_PAGES * PAGE_SIZE;
+	size_t buffer2_len = BUFFER2_PAGES * PAGE_SIZE;
+	void *buffer1 = create_as_area(buffer1_len);
+	void *buffer2 = create_as_area(buffer2_len);
+	if (!buffer1 || !buffer2) {
+		return "Cannot allocate memory";
+	}
+	
+	touch_area(buffer1, buffer1_len);
+	touch_area(buffer2, buffer2_len);
+	
+	/* Now verify that mapping to physical frames exist. */
+	if (!VERIFY_MAPPING(buffer1, BUFFER1_PAGES, EOK)) {
+		return "Failed to find mapping (buffer1)";
+	}
+	if (!VERIFY_MAPPING(buffer2, BUFFER2_PAGES, EOK)) {
+		return "Failed to find mapping (buffer2)";
+	}
+	
+	/* Let's destroy the buffer1 area and access it again. */
+	rc = as_area_destroy(buffer1);
+	if (rc != EOK) {
+		return "Failed to destroy AS area";
+	}
+	if (!VERIFY_MAPPING(buffer1, BUFFER1_PAGES, ENOENT)) {
+		return "Mapping of destroyed area still exists";
+	}
+	
+	/* clean-up */
+	rc = as_area_destroy(buffer2);
+	if (rc != EOK) {
+		return "Failed to destroy AS area";
+	}
+	
+	return NULL;
+}
Index: uspace/app/tester/mm/mapping1.def
===================================================================
--- uspace/app/tester/mm/mapping1.def	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/tester/mm/mapping1.def	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,6 @@
+{
+	"mapping1",
+	"Page mapping test",
+	&test_mapping1,
+	true
+},
Index: uspace/app/tester/tester.c
===================================================================
--- uspace/app/tester/tester.c	(revision 5d1b3aa2ae833977640704ed7cf0ed0ad531bb41)
+++ uspace/app/tester/tester.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -64,4 +64,5 @@
 #include "mm/malloc2.def"
 #include "mm/malloc3.def"
+#include "mm/mapping1.def"
 #include "hw/serial/serial1.def"
 #include "hw/misc/virtchar1.def"
Index: uspace/app/tester/tester.h
===================================================================
--- uspace/app/tester/tester.h	(revision 5d1b3aa2ae833977640704ed7cf0ed0ad531bb41)
+++ uspace/app/tester/tester.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -97,4 +97,5 @@
 extern const char *test_malloc2(void);
 extern const char *test_malloc3(void);
+extern const char *test_mapping1(void);
 extern const char *test_serial1(void);
 extern const char *test_virtchar1(void);
Index: uspace/app/usbinfo/Makefile
===================================================================
--- uspace/app/usbinfo/Makefile	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/usbinfo/Makefile	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,48 @@
+#
+# 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 = \
+	$(LIBUSBDEV_PREFIX)/libusbdev.a \
+	$(LIBUSB_PREFIX)/libusb.a \
+	$(LIBDRV_PREFIX)/libdrv.a
+EXTRA_CFLAGS = \
+	-I$(LIBUSB_PREFIX)/include \
+	-I$(LIBUSBDEV_PREFIX)/include \
+	-I$(LIBDRV_PREFIX)/include
+
+SOURCES = \
+	desctree.c \
+	dev.c \
+	dump.c \
+	info.c \
+	main.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/usbinfo/desctree.c
===================================================================
--- uspace/app/usbinfo/desctree.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/usbinfo/desctree.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -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 usbinfo
+ * @{
+ */
+/**
+ * @file
+ * Descriptor tree dump.
+ */
+
+#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 <usb/debug.h>
+#include <usb/classes/classes.h>
+
+#include "usbinfo.h"
+#include <usb/dev/dp.h>
+
+static void browse_descriptor_tree_internal(usb_dp_parser_t *parser,
+    usb_dp_parser_data_t *data, uint8_t *root, size_t depth,
+    dump_descriptor_in_tree_t callback, void *arg)
+{
+	if (root == NULL) {
+		return;
+	}
+	callback(root, depth, arg);
+	uint8_t *child = usb_dp_get_nested_descriptor(parser, data, root);
+	do {
+		browse_descriptor_tree_internal(parser, data, child, depth + 1,
+		    callback, arg);
+		child = usb_dp_get_sibling_descriptor(parser, data,
+		    root, child);
+	} while (child != NULL);
+}
+
+void browse_descriptor_tree(uint8_t *descriptors, size_t descriptors_size,
+    usb_dp_descriptor_nesting_t *descriptor_nesting,
+    dump_descriptor_in_tree_t callback, size_t initial_depth, void *arg)
+{
+	usb_dp_parser_data_t data = {
+		.data = descriptors,
+		.size = descriptors_size,
+		.arg = NULL
+	};
+
+	usb_dp_parser_t parser = {
+		.nesting = descriptor_nesting
+	};
+
+	browse_descriptor_tree_internal(&parser, &data, descriptors,
+	    initial_depth, callback, arg);
+}
+
+/** @}
+ */
Index: uspace/app/usbinfo/dev.c
===================================================================
--- uspace/app/usbinfo/dev.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/usbinfo/dev.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2011 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 usbinfo
+ * @{
+ */
+/**
+ * @file
+ * Representation of queried device.
+ */
+#include <usb/dev/pipes.h>
+#include <errno.h>
+#include <str_error.h>
+#include <usb/dev/request.h>
+#include "usbinfo.h"
+
+usbinfo_device_t *prepare_device(const char *name,
+    devman_handle_t hc_handle, usb_address_t dev_addr)
+{
+	usbinfo_device_t *dev = malloc(sizeof(usbinfo_device_t));
+	if (dev == NULL) {
+		fprintf(stderr, NAME ": memory allocation failed.\n");
+		return NULL;
+	}
+
+	int rc;
+	bool transfer_started = false;
+
+	rc = usb_device_connection_initialize(&dev->wire, hc_handle, dev_addr);
+	if (rc != EOK) {
+		fprintf(stderr,
+		    NAME ": failed to create connection to device %s: %s.\n",
+		    name, str_error(rc));
+		goto leave;
+	}
+
+	rc = usb_pipe_initialize_default_control(&dev->ctrl_pipe,
+	    &dev->wire);
+	if (rc != EOK) {
+		fprintf(stderr,
+		    NAME ": failed to create default control pipe to %s: %s.\n",
+		    name, str_error(rc));
+		goto leave;
+	}
+
+	rc = usb_pipe_probe_default_control(&dev->ctrl_pipe);
+	if (rc != EOK) {
+		if (rc == ENOENT) {
+			fprintf(stderr, NAME ": " \
+			    "device %s not present or malfunctioning.\n",
+			    name);
+		} else {
+			fprintf(stderr, NAME ": " \
+			    "probing default control pipe of %s failed: %s.\n",
+			    name, str_error(rc));
+		}
+		goto leave;
+	}
+
+	usb_pipe_start_long_transfer(&dev->ctrl_pipe);
+	transfer_started = true;
+
+	rc = usb_request_get_device_descriptor(&dev->ctrl_pipe,
+	    &dev->device_descriptor);
+	if (rc != EOK) {
+		fprintf(stderr,
+		    NAME ": failed to retrieve device descriptor of %s: %s.\n",
+		    name, str_error(rc));
+		goto leave;
+	}
+
+	rc = usb_request_get_full_configuration_descriptor_alloc(
+	    &dev->ctrl_pipe, 0, (void **)&dev->full_configuration_descriptor,
+	    &dev->full_configuration_descriptor_size);
+	if (rc != EOK) {
+		fprintf(stderr, NAME ": " \
+		    "failed to retrieve configuration descriptor of %s: %s.\n",
+		    name, str_error(rc));
+		goto leave;
+	}
+
+	return dev;
+
+
+leave:
+	if (transfer_started) {
+		usb_pipe_end_long_transfer(&dev->ctrl_pipe);
+	}
+
+	free(dev);
+
+	return NULL;
+}
+
+void destroy_device(usbinfo_device_t *dev)
+{
+	usb_pipe_end_long_transfer(&dev->ctrl_pipe);
+	free(dev);
+}
+
+/** @}
+ */
Index: uspace/app/usbinfo/dump.c
===================================================================
--- uspace/app/usbinfo/dump.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/usbinfo/dump.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,194 @@
+/*
+ * 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 usbinfo
+ * @{
+ */
+/**
+ * @file
+ * 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 <usb/debug.h>
+#include <usb/classes/classes.h>
+
+#include "usbinfo.h"
+#include <usb/dev/dp.h>
+
+#define INDENT "  "
+#define BYTES_PER_LINE 12
+
+
+const char *get_indent(size_t level)
+{
+	static const char *indents[] = {
+		INDENT,
+		INDENT INDENT,
+		INDENT INDENT INDENT,
+		INDENT INDENT INDENT INDENT,
+		INDENT INDENT INDENT INDENT INDENT
+	};
+	static size_t indents_count = sizeof(indents)/sizeof(indents[0]);
+	if (level >= indents_count) {
+		return indents[indents_count - 1];
+	}
+	return indents[level];
+}
+
+void dump_buffer(const char *msg, size_t indent,
+    const uint8_t *buffer, size_t length)
+{
+	if (msg != NULL) {
+		printf("%s\n", msg);
+	}
+
+	size_t i;
+	if (length > 0) {
+		printf("%s", get_indent(indent));
+	}
+	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");
+			if (i + 1 < length) {
+				printf("%s", get_indent(indent));
+			}
+		} else {
+			printf("  ");
+		}
+	}
+}
+
+void dump_usb_descriptor(uint8_t *descriptor, size_t size)
+{
+	printf("Device descriptor:\n");
+	usb_dump_standard_descriptor(stdout, get_indent(0), "\n",
+	    descriptor, size);
+}
+
+void dump_match_ids(match_id_list_t *matches, const char *line_prefix)
+{
+	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("%s%3d %s\n", line_prefix, match->score, match->id);
+	}
+}
+
+static void dump_tree_descriptor(uint8_t *descriptor, size_t depth)
+{
+	if (descriptor == NULL) {
+		return;
+	}
+	int type = (int) *(descriptor + 1);
+	const char *name = "unknown";
+	switch (type) {
+#define _TYPE(descriptor_type) \
+		case USB_DESCTYPE_##descriptor_type: name = #descriptor_type; break
+		_TYPE(DEVICE);
+		_TYPE(CONFIGURATION);
+		_TYPE(STRING);
+		_TYPE(INTERFACE);
+		_TYPE(ENDPOINT);
+		_TYPE(HID);
+		_TYPE(HID_REPORT);
+		_TYPE(HID_PHYSICAL);
+		_TYPE(HUB);
+#undef _TYPE
+	}
+	printf("%s%s (0x%02X):\n", get_indent(depth), name, type);
+	usb_dump_standard_descriptor(stdout, get_indent(depth), "\n",
+	    descriptor, descriptor[0]);
+}
+
+static void dump_tree_internal(usb_dp_parser_t *parser, usb_dp_parser_data_t *data,
+    uint8_t *root, size_t depth)
+{
+	if (root == NULL) {
+		return;
+	}
+	dump_tree_descriptor(root, depth);
+	uint8_t *child = usb_dp_get_nested_descriptor(parser, data, root);
+	do {
+		dump_tree_internal(parser, data, child, depth + 1);
+		child = usb_dp_get_sibling_descriptor(parser, data, root, child);
+	} while (child != NULL);
+}
+
+static void dump_tree(usb_dp_parser_t *parser, usb_dp_parser_data_t *data)
+{
+	uint8_t *ptr = data->data;
+	printf("Descriptor tree:\n");
+	dump_tree_internal(parser, data, ptr, 0);
+}
+
+#define NESTING(parentname, childname) \
+	{ \
+		.child = USB_DESCTYPE_##childname, \
+		.parent = USB_DESCTYPE_##parentname, \
+	}
+#define LAST_NESTING { -1, -1 }
+
+static usb_dp_descriptor_nesting_t descriptor_nesting[] = {
+	NESTING(CONFIGURATION, INTERFACE),
+	NESTING(INTERFACE, ENDPOINT),
+	NESTING(INTERFACE, HUB),
+	NESTING(INTERFACE, HID),
+	NESTING(HID, HID_REPORT),
+	LAST_NESTING
+};
+
+static usb_dp_parser_t parser = {
+	.nesting = descriptor_nesting
+};
+
+void dump_descriptor_tree(uint8_t *descriptors, size_t length)
+{
+	usb_dp_parser_data_t data = {
+		.data = descriptors,
+		.size = length,
+		.arg = NULL
+	};
+
+	dump_tree(&parser, &data);
+}
+
+/** @}
+ */
Index: uspace/app/usbinfo/info.c
===================================================================
--- uspace/app/usbinfo/info.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/usbinfo/info.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2011 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 usbinfo
+ * @{
+ */
+/**
+ * @file
+ * Dumping of generic device properties.
+ */
+#include <stdio.h>
+#include <str_error.h>
+#include <errno.h>
+#include <usb/dev/pipes.h>
+#include <usb/dev/recognise.h>
+#include <usb/dev/request.h>
+#include <usb/classes/classes.h>
+#include <usb/classes/hub.h>
+#include "usbinfo.h"
+
+void dump_short_device_identification(usbinfo_device_t *dev)
+{
+	printf("%sDevice 0x%04x by vendor 0x%04x\n", get_indent(0),
+	    (int) dev->device_descriptor.product_id,
+	    (int) dev->device_descriptor.vendor_id);
+}
+
+static void dump_match_ids_from_interface(uint8_t *descriptor, size_t depth,
+    void *arg)
+{
+	if (depth != 1) {
+		return;
+	}
+	size_t descr_size = descriptor[0];
+	if (descr_size < sizeof(usb_standard_interface_descriptor_t)) {
+		return;
+	}
+	int descr_type = descriptor[1];
+	if (descr_type != USB_DESCTYPE_INTERFACE) {
+		return;
+	}
+
+	usbinfo_device_t *dev = (usbinfo_device_t *) arg;
+
+	usb_standard_interface_descriptor_t *iface
+	    = (usb_standard_interface_descriptor_t *) descriptor;
+
+	printf("%sInterface #%d match ids (%s, 0x%02x, 0x%02x)\n",
+	    get_indent(0),
+	    (int) iface->interface_number,
+	    usb_str_class(iface->interface_class),
+	    (int) iface->interface_subclass,
+	    (int) iface->interface_protocol);
+
+	match_id_list_t matches;
+	init_match_ids(&matches);
+	usb_device_create_match_ids_from_interface(&dev->device_descriptor,
+	    iface, &matches);
+	dump_match_ids(&matches, get_indent(1));
+	clean_match_ids(&matches);
+}
+
+void dump_device_match_ids(usbinfo_device_t *dev)
+{
+	match_id_list_t matches;
+	init_match_ids(&matches);
+	usb_device_create_match_ids_from_device_descriptor(
+	    &dev->device_descriptor, &matches);
+	printf("%sDevice match ids (0x%04x by 0x%04x, %s)\n", get_indent(0),
+	    (int) dev->device_descriptor.product_id,
+	    (int) dev->device_descriptor.vendor_id,
+	    usb_str_class(dev->device_descriptor.device_class));
+	dump_match_ids(&matches, get_indent(1));
+	clean_match_ids(&matches);
+
+	usb_dp_walk_simple(dev->full_configuration_descriptor,
+	    dev->full_configuration_descriptor_size,
+	    usb_dp_standard_descriptor_nesting,
+	    dump_match_ids_from_interface,
+	    dev);
+}
+
+static void dump_descriptor_tree_brief_device(const char *prefix,
+    usb_standard_device_descriptor_t *descriptor)
+{
+	printf("%sDevice (0x%04x by 0x%04x, %s, %zu configurations)\n", prefix,
+	    (int) descriptor->product_id,
+	    (int) descriptor->vendor_id,
+	    usb_str_class(descriptor->device_class),
+	    (size_t) descriptor->configuration_count);
+}
+
+static void dump_descriptor_tree_brief_configuration(const char *prefix,
+    usb_standard_configuration_descriptor_t *descriptor)
+{
+	printf("%sConfiguration #%d (%zu interfaces, total %zuB)\n", prefix,
+	    (int) descriptor->configuration_number,
+	    (size_t) descriptor->interface_count,
+	    (size_t) descriptor->total_length);
+}
+
+static void dump_descriptor_tree_brief_interface(const char *prefix,
+    usb_standard_interface_descriptor_t *descriptor)
+{
+	printf("%sInterface #%d (%s, 0x%02x, 0x%02x), alternate %d\n", prefix,
+	    (int) descriptor->interface_number,
+	    usb_str_class(descriptor->interface_class),
+	    (int) descriptor->interface_subclass,
+	    (int) descriptor->interface_protocol,
+	    (int) descriptor->alternate_setting);
+}
+
+static void dump_descriptor_tree_brief_endpoint(const char *prefix,
+    usb_standard_endpoint_descriptor_t *descriptor)
+{
+	usb_endpoint_t endpoint_no = descriptor->endpoint_address & 0xF;
+	usb_transfer_type_t transfer = descriptor->attributes & 0x3;
+	usb_direction_t direction = descriptor->endpoint_address & 0x80
+	    ? USB_DIRECTION_IN : USB_DIRECTION_OUT;
+	printf("%sEndpoint #%d (%s %s, %zu)\n", prefix,
+	    endpoint_no, usb_str_transfer_type(transfer),
+	    direction == USB_DIRECTION_IN ? "in" : "out",
+	    (size_t) descriptor->max_packet_size);
+}
+
+static void dump_descriptor_tree_brief_hid(const char *prefix,
+    usb_standard_hid_descriptor_t *descriptor)
+{
+	printf("%sHID (country %d, %d descriptors)\n", prefix,
+	    (int) descriptor->country_code,
+	    (int) descriptor->class_desc_count);
+}
+
+static void dump_descriptor_tree_brief_hub(const char *prefix,
+    usb_hub_descriptor_header_t *descriptor)
+{
+	printf("%shub (%d ports)\n", prefix,
+	    (int) descriptor->port_count);
+}
+
+
+static void dump_descriptor_tree_callback(uint8_t *descriptor,
+    size_t depth, void *arg)
+{
+	const char *indent = get_indent(depth + 1);
+
+	int descr_type = -1;
+	size_t descr_size = descriptor[0];
+	if (descr_size > 0) {
+		descr_type = descriptor[1];
+	}
+
+	switch (descr_type) {
+
+#define _BRANCH(type_enum, descriptor_type, callback) \
+	case type_enum: \
+		if (descr_size >= sizeof(descriptor_type)) { \
+			callback(indent, (descriptor_type *) descriptor); \
+			if (arg != NULL) { \
+				usb_dump_standard_descriptor(stdout, \
+				    get_indent(depth +2), "\n", \
+				    descriptor, descr_size); \
+			} \
+		} else { \
+			descr_type = -1; \
+		} \
+		break;
+
+		_BRANCH(USB_DESCTYPE_DEVICE,
+		    usb_standard_device_descriptor_t,
+		    dump_descriptor_tree_brief_device);
+		_BRANCH(USB_DESCTYPE_CONFIGURATION,
+		    usb_standard_configuration_descriptor_t,
+		    dump_descriptor_tree_brief_configuration);
+		_BRANCH(USB_DESCTYPE_INTERFACE,
+		    usb_standard_interface_descriptor_t,
+		    dump_descriptor_tree_brief_interface);
+		_BRANCH(USB_DESCTYPE_ENDPOINT,
+		    usb_standard_endpoint_descriptor_t,
+		    dump_descriptor_tree_brief_endpoint);
+		_BRANCH(USB_DESCTYPE_HID,
+		    usb_standard_hid_descriptor_t,
+		    dump_descriptor_tree_brief_hid);
+		/*
+		 * Probably useless, hub descriptor shall not be part of
+		 * configuration descriptor.
+		 */
+		_BRANCH(USB_DESCTYPE_HUB,
+		    usb_hub_descriptor_header_t,
+		    dump_descriptor_tree_brief_hub);
+
+		default:
+			break;
+	}
+
+	if (descr_type == -1) {
+		printf("%sInvalid descriptor.\n", indent);
+	}
+}
+
+void dump_descriptor_tree_brief(usbinfo_device_t *dev)
+{
+	dump_descriptor_tree_callback((uint8_t *)&dev->device_descriptor,
+	    (size_t) -1, NULL);
+	usb_dp_walk_simple(dev->full_configuration_descriptor,
+	    dev->full_configuration_descriptor_size,
+	    usb_dp_standard_descriptor_nesting,
+	    dump_descriptor_tree_callback,
+	    NULL);
+}
+
+void dump_descriptor_tree_full(usbinfo_device_t *dev)
+{
+	dump_descriptor_tree_callback((uint8_t *)&dev->device_descriptor,
+	    (size_t) -1, dev);
+	usb_dp_walk_simple(dev->full_configuration_descriptor,
+	    dev->full_configuration_descriptor_size,
+	    usb_dp_standard_descriptor_nesting,
+	    dump_descriptor_tree_callback,
+	    dev);
+}
+
+static void find_string_indexes_callback(uint8_t *descriptor,
+    size_t depth, void *arg)
+{
+	size_t descriptor_length = descriptor[0];
+	if (descriptor_length <= 1) {
+		return;
+	}
+
+	/*printf("Found string in %s->%s: %zu\n",
+	    #descr_struct, #descr_item, __str_index); */
+#define SET_STRING_INDEX(descr, mask, descr_type, descr_struct, descr_item) \
+	do { \
+		if ((descr)[1] == (descr_type)) { \
+			descr_struct *__type_descr = (descr_struct *) (descr); \
+			size_t __str_index = __type_descr->descr_item; \
+			if ((__str_index > 0) && (__str_index < 64)) { \
+				mask = (mask) | (1 << __str_index); \
+			} \
+		} \
+	} while (0)
+
+	uint64_t *mask = arg;
+
+#define SET_STR(descr_type, descr_struct, descr_item) \
+	SET_STRING_INDEX(descriptor, (*mask), descr_type, descr_struct, descr_item)
+
+	SET_STR(USB_DESCTYPE_DEVICE, usb_standard_device_descriptor_t,
+	    str_manufacturer);
+	SET_STR(USB_DESCTYPE_DEVICE, usb_standard_device_descriptor_t,
+	    str_product);
+	SET_STR(USB_DESCTYPE_DEVICE, usb_standard_device_descriptor_t,
+	    str_serial_number);
+	SET_STR(USB_DESCTYPE_CONFIGURATION, usb_standard_configuration_descriptor_t,
+	    str_configuration);
+	SET_STR(USB_DESCTYPE_INTERFACE, usb_standard_interface_descriptor_t,
+	    str_interface);
+}
+
+
+void dump_strings(usbinfo_device_t *dev)
+{
+	/* Get supported languages. */
+	l18_win_locales_t *langs;
+	size_t langs_count;
+	int rc = usb_request_get_supported_languages(&dev->ctrl_pipe,
+	    &langs, &langs_count);
+	if (rc != EOK) {
+		fprintf(stderr,
+		    NAME ": failed to get list of supported languages: %s.\n",
+		    str_error(rc));
+		return;
+	}
+
+	printf("%sString languages (%zu):", get_indent(0), langs_count);
+	size_t i;
+	for (i = 0; i < langs_count; i++) {
+		printf(" 0x%04x", (int) langs[i]);
+	}
+	printf(".\n");
+
+	/* Find used indexes. Device with more than 64 strings are very rare.
+	 */
+	uint64_t str_mask = 0;
+	find_string_indexes_callback((uint8_t *)&dev->device_descriptor, 0,
+	    &str_mask);
+	usb_dp_walk_simple(dev->full_configuration_descriptor,
+	    dev->full_configuration_descriptor_size,
+	    usb_dp_standard_descriptor_nesting,
+	    find_string_indexes_callback,
+	    &str_mask);
+
+	/* Get all strings and dump them. */
+	for (i = 0; i < langs_count; i++) {
+		l18_win_locales_t lang = langs[i];
+
+		printf("%sStrings in %s:\n", get_indent(0),
+		    str_l18_win_locale(lang));
+
+		size_t idx;
+		for (idx = 1; idx < 64; idx++) {
+			if ((str_mask & ((uint64_t)1 << idx)) == 0) {
+				continue;
+			}
+			char *string = NULL;
+			rc = usb_request_get_string(&dev->ctrl_pipe, idx, lang,
+			    &string);
+			if ((rc != EOK) && (rc != EEMPTY)) {
+				printf("%sWarn: failed to retrieve string #%zu: %s.\n",
+				    get_indent(1), idx, str_error(rc));
+				continue;
+			}
+			printf("%sString #%zu: \"%s\"\n", get_indent(1),
+			    idx, rc == EOK ? string : "");
+			if (string != NULL) {
+				free(string);
+			}
+		}
+	}
+}
+
+
+void dump_status(usbinfo_device_t *dev)
+{
+	int rc;
+	uint16_t device_status = 0;
+	uint16_t ctrl_pipe_status = 0;
+
+	/* Device status first. */
+	rc = usb_request_get_status(&dev->ctrl_pipe,
+	    USB_REQUEST_RECIPIENT_DEVICE, 0,
+	    &device_status);
+	if (rc != EOK) {
+		printf("%sFailed to get device status: %s.\n",
+		    get_indent(0), str_error(rc));
+		goto try_ctrl_pipe_status;
+	}
+
+	printf("%sDevice status 0x%04x: power=%s, remote-wakeup=%s.\n",
+	    get_indent(0),
+	    device_status,
+	    device_status & USB_DEVICE_STATUS_SELF_POWERED ? "self" : "bus",
+	    device_status & USB_DEVICE_STATUS_REMOTE_WAKEUP ? "yes" : "no");
+
+	/* Interface is not interesting, skipping ;-). */
+
+	/* Control endpoint zero. */
+try_ctrl_pipe_status:
+	rc = usb_request_get_status(&dev->ctrl_pipe,
+	    USB_REQUEST_RECIPIENT_ENDPOINT, 0,
+	    &ctrl_pipe_status);
+	if (rc != EOK) {
+		printf("%sFailed to get control endpoint status: %s.\n",
+		    get_indent(0), str_error(rc));
+		goto leave;
+	}
+
+	printf("%sControl endpoint zero status %04X: halted=%s.\n",
+	    get_indent(0),
+	    ctrl_pipe_status,
+	    ctrl_pipe_status & USB_ENDPOINT_STATUS_HALTED ? "yes" : "no");
+
+leave:
+	return;
+}
+
+/** @}
+ */
Index: uspace/app/usbinfo/main.c
===================================================================
--- uspace/app/usbinfo/main.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/usbinfo/main.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2010-2011 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 usbinfo
+ * @{
+ */
+/**
+ * @file
+ * USB querying.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <str_error.h>
+#include <bool.h>
+#include <getopt.h>
+#include <devman.h>
+#include <devmap.h>
+#include <usb/hc.h>
+#include <usb/dev/pipes.h>
+#include "usbinfo.h"
+
+static void print_usage(char *app_name)
+{
+#define _INDENT "      "
+#define _OPTION(opt, description) \
+	printf(_INDENT opt "\n" _INDENT _INDENT description "\n")
+
+	printf(NAME ": query USB devices for descriptors\n\n");
+	printf("Usage: %s [options] device [device [device [ ... ]]]\n",
+	    app_name);
+	printf(_INDENT "The device is a devman path to the device.\n");
+
+	_OPTION("-h --help", "Print this help and exit.");
+	_OPTION("-i --identification", "Brief device identification.");
+	_OPTION("-m --match-ids", "Print match ids generated for the device.");
+	_OPTION("-t --descriptor-tree", "Print descriptor tree.");
+	_OPTION("-T --descriptor-tree-full", "Print detailed descriptor tree");
+	_OPTION("-s --strings", "Try to print all string descriptors.");
+	_OPTION("-S --status", "Get status of the device.");
+
+	printf("\n");
+	printf("If no option is specified, `-i' is considered default.\n");
+	printf("\n");
+
+#undef _OPTION
+#undef _INDENT
+}
+
+static struct option long_options[] = {
+	{"help", no_argument, NULL, 'h'},
+	{"identification", no_argument, NULL, 'i'},
+	{"match-ids", no_argument, NULL, 'm'},
+	{"descriptor-tree", no_argument, NULL, 't'},
+	{"descriptor-tree-full", no_argument, NULL, 'T'},
+	{"strings", no_argument, NULL, 's'},
+	{"status", no_argument, NULL, 'S'},
+	{0, 0, NULL, 0}
+};
+static const char *short_options = "himtTsS";
+
+static usbinfo_action_t actions[] = {
+	{
+		.opt = 'i',
+		.action = dump_short_device_identification,
+		.active = false
+	},
+	{
+		.opt = 'm',
+		.action = dump_device_match_ids,
+		.active = false
+	},
+	{
+		.opt = 't',
+		.action = dump_descriptor_tree_brief,
+		.active = false
+	},
+	{
+		.opt = 'T',
+		.action = dump_descriptor_tree_full,
+		.active = false
+	},
+	{
+		.opt = 's',
+		.action = dump_strings,
+		.active = false
+	},
+	{
+		.opt = 'S',
+		.action = dump_status,
+		.active = false
+	},
+	{
+		.opt = 0
+	}
+};
+
+int main(int argc, char *argv[])
+{
+	if (argc <= 1) {
+		print_usage(argv[0]);
+		return -1;
+	}
+
+	/*
+	 * Process command-line options. They determine what shall be
+	 * done with the device.
+	 */
+	int opt;
+	do {
+		opt = getopt_long(argc, argv,
+		    short_options, long_options, NULL);
+		switch (opt) {
+			case -1:
+				break;
+			case '?':
+				print_usage(argv[0]);
+				return 1;
+			case 'h':
+				print_usage(argv[0]);
+				return 0;
+			default: {
+				int idx = 0;
+				while (actions[idx].opt != 0) {
+					if (actions[idx].opt == opt) {
+						actions[idx].active = true;
+						break;
+					}
+					idx++;
+				}
+				break;
+			}
+		}
+	} while (opt > 0);
+
+	/* Set the default action. */
+	int idx = 0;
+	bool something_active = false;
+	while (actions[idx].opt != 0) {
+		if (actions[idx].active) {
+			something_active = true;
+			break;
+		}
+		idx++;
+	}
+	if (!something_active) {
+		actions[0].active = true;
+	}
+
+	/*
+	 * Go through all devices given on the command line and run the
+	 * specified actions.
+	 */
+	int i;
+	for (i = optind; i < argc; i++) {
+		char *devpath = argv[i];
+
+		/* The initialization is here only to make compiler happy. */
+		devman_handle_t hc_handle = 0;
+		usb_address_t dev_addr = 0;
+		int rc = usb_resolve_device_handle(devpath,
+		    &hc_handle, &dev_addr, NULL);
+		if (rc != EOK) {
+			fprintf(stderr, NAME ": device `%s' not found "
+			    "or not of USB kind, skipping.\n",
+			    devpath);
+			continue;
+		}
+
+		usbinfo_device_t *dev = prepare_device(devpath,
+		    hc_handle, dev_addr);
+		if (dev == NULL) {
+			continue;
+		}
+
+		/* Run actions the user specified. */
+		printf("%s\n", devpath);
+
+		int action = 0;
+		while (actions[action].opt != 0) {
+			if (actions[action].active) {
+				actions[action].action(dev);
+			}
+			action++;
+		}
+
+		/* Destroy the control pipe (close the session etc.). */
+		destroy_device(dev);
+	}
+
+	return 0;
+}
+
+
+/** @}
+ */
Index: uspace/app/usbinfo/usbinfo.h
===================================================================
--- uspace/app/usbinfo/usbinfo.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/usbinfo/usbinfo.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,92 @@
+/*
+ * 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 usbinfo
+ * @{
+ */
+/** @file
+ * Common header for usbinfo application.
+ */
+#ifndef USBINFO_USBINFO_H_
+#define USBINFO_USBINFO_H_
+
+#include <usb/usb.h>
+#include <usb/descriptor.h>
+#include <usb/dev/pipes.h>
+#include <usb/debug.h>
+#include <usb/dev/dp.h>
+#include <ipc/devman.h>
+
+typedef struct {
+	usb_pipe_t ctrl_pipe;
+	usb_device_connection_t wire;
+	usb_standard_device_descriptor_t device_descriptor;
+	uint8_t *full_configuration_descriptor;
+	size_t full_configuration_descriptor_size;
+} usbinfo_device_t;
+
+typedef struct {
+	int opt;
+	void (*action)(usbinfo_device_t *dev);
+	bool active;
+} usbinfo_action_t;
+
+
+#define NAME "usbinfo"
+
+void dump_buffer(const char *, size_t, const uint8_t *, size_t);
+const char *get_indent(size_t);
+void dump_match_ids(match_id_list_t *, const char *);
+void dump_usb_descriptor(uint8_t *, size_t);
+void dump_descriptor_tree(uint8_t *, size_t);
+
+static inline void internal_error(int err)
+{
+	fprintf(stderr, NAME ": internal error (%s).\n", str_error(err));
+}
+
+usbinfo_device_t *prepare_device(const char *, devman_handle_t, usb_address_t);
+void destroy_device(usbinfo_device_t *);
+
+typedef void (*dump_descriptor_in_tree_t)(uint8_t *, size_t, void *);
+void browse_descriptor_tree(uint8_t *, size_t, usb_dp_descriptor_nesting_t *,
+    dump_descriptor_in_tree_t, size_t, void *);
+
+
+void dump_short_device_identification(usbinfo_device_t *);
+void dump_device_match_ids(usbinfo_device_t *);
+void dump_descriptor_tree_brief(usbinfo_device_t *);
+void dump_descriptor_tree_full(usbinfo_device_t *);
+void dump_strings(usbinfo_device_t *);
+void dump_status(usbinfo_device_t *);
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/app/vuhid/Makefile
===================================================================
--- uspace/app/vuhid/Makefile	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/vuhid/Makefile	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,58 @@
+#
+# Copyright (c) 2011 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 human input device
+# (it is really annoying to write long names)
+BINARY = vuh
+
+LIBS = \
+	$(LIBUSBVIRT_PREFIX)/libusbvirt.a \
+	$(LIBUSBHID_PREFIX)/libusbhid.a \
+	$(LIBUSBDEV_PREFIX)/libusbdev.a \
+	$(LIBUSB_PREFIX)/libusb.a
+EXTRA_CFLAGS = \
+	-I$(LIBUSB_PREFIX)/include \
+	-I$(LIBUSBDEV_PREFIX)/include \
+	-I$(LIBUSBHID_PREFIX)/include \
+	-I$(LIBUSBVIRT_PREFIX)/include \
+	-I$(LIBDRV_PREFIX)/include
+
+
+SOURCES_INTERFACES = \
+	hids/bootkbd.c
+
+SOURCES = \
+	main.c \
+	device.c \
+	ifaces.c \
+	stdreq.c \
+	$(SOURCES_INTERFACES)
+	
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/vuhid/device.c
===================================================================
--- uspace/app/vuhid/device.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/vuhid/device.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2011 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 usbvirthid
+ * @{
+ */
+/**
+ * @file
+ * Virtual USB HID device.
+ */
+#include "virthid.h"
+#include <errno.h>
+#include <stdio.h>
+#include <str.h>
+#include <assert.h>
+#include <usb/classes/classes.h>
+
+static int on_data_from_device(usbvirt_device_t *dev,
+    usb_endpoint_t ep, usb_transfer_type_t tr_type,
+    void *data, size_t data_size, size_t *actual_size)
+{
+	vuhid_data_t *vuhid = dev->device_data;
+	vuhid_interface_t *iface = vuhid->in_endpoints_mapping[ep];
+	if (iface == NULL) {
+		return ESTALL;
+	}
+
+	if (iface->on_data_in == NULL) {
+		return EBADCHECKSUM;
+	}
+
+	return iface->on_data_in(iface, data, data_size, actual_size);
+}
+
+static int on_data_to_device(usbvirt_device_t *dev,
+    usb_endpoint_t ep, usb_transfer_type_t tr_type,
+    void *data, size_t data_size)
+{
+	vuhid_data_t *vuhid = dev->device_data;
+	vuhid_interface_t *iface = vuhid->out_endpoints_mapping[ep];
+	if (iface == NULL) {
+		return ESTALL;
+	}
+
+	if (iface->on_data_out == NULL) {
+		return EBADCHECKSUM;
+	}
+
+	return iface->on_data_out(iface, data, data_size);
+}
+
+static int interface_life_fibril(void *arg)
+{
+	vuhid_interface_t *iface = arg;
+	vuhid_data_t *hid_data = iface->vuhid_data;
+
+	if (iface->live != NULL) {
+		iface->live(iface);
+	}
+
+	fibril_mutex_lock(&hid_data->iface_count_mutex);
+	hid_data->iface_died_count++;
+	fibril_condvar_broadcast(&hid_data->iface_count_cv);
+	fibril_mutex_unlock(&hid_data->iface_count_mutex);
+
+	return EOK;
+}
+
+
+static vuhid_interface_t *find_interface_by_id(vuhid_interface_t **ifaces,
+    const char *id)
+{
+	if ((ifaces == NULL) || (id == NULL)) {
+		return NULL;
+	}
+	while (*ifaces != NULL) {
+		if (str_cmp((*ifaces)->id, id) == 0) {
+			return *ifaces;
+		}
+		ifaces++;
+	}
+
+	return NULL;
+}
+
+int add_interface_by_id(vuhid_interface_t **interfaces, const char *id,
+    usbvirt_device_t *dev)
+{
+	vuhid_interface_t *iface = find_interface_by_id(interfaces, id);
+	if (iface == NULL) {
+		return ENOENT;
+	}
+
+	if ((iface->in_data_size == 0) && (iface->out_data_size == 0)) {
+		return EEMPTY;
+	}
+
+	/* Already used interface. */
+	if (iface->vuhid_data != NULL) {
+		return EEXISTS;
+	}
+
+	vuhid_data_t *hid_data = dev->device_data;
+
+	/* Check that we have not run out of available endpoints. */
+	if ((iface->in_data_size > 0)
+	    && (hid_data->in_endpoint_first_free >= VUHID_ENDPOINT_MAX)) {
+		return ELIMIT;
+	}
+	if ((iface->out_data_size > 0)
+	    && (hid_data->out_endpoint_first_free >= VUHID_ENDPOINT_MAX)) {
+		return ELIMIT;
+	}
+
+	if (dev->descriptors->configuration->descriptor->interface_count >=
+	    VUHID_INTERFACE_MAX) {
+		return ELIMIT;
+	}
+
+	/*
+	 * How may descriptors we would add?
+	 */
+	/* Start with interface descriptor. */
+	size_t new_descriptor_count = 1;
+	/* Having in/out data size positive means we would need endpoint. */
+	if (iface->in_data_size > 0) {
+		new_descriptor_count++;
+	}
+	if (iface->out_data_size > 0) {
+		new_descriptor_count++;
+	}
+	/* Having report descriptor means adding the HID descriptor. */
+	if (iface->report_descriptor != NULL) {
+		new_descriptor_count++;
+	}
+
+	/* From now on, in case of errors, we goto to error_leave */
+	int rc;
+
+	/*
+	 * Prepare new descriptors.
+	 */
+	size_t descr_count = 0;
+	size_t total_descr_size = 0;
+	usb_standard_interface_descriptor_t *descr_iface = NULL;
+	hid_descriptor_t *descr_hid = NULL;
+	usb_standard_endpoint_descriptor_t *descr_ep_in = NULL;
+	usb_standard_endpoint_descriptor_t *descr_ep_out = NULL;
+
+	size_t ep_count = 0;
+	if (iface->in_data_size > 0) {
+		ep_count++;
+	}
+	if (iface->out_data_size > 0) {
+		ep_count++;
+	}
+	assert(ep_count > 0);
+
+	/* Interface would be needed always. */
+	descr_iface = malloc(sizeof(usb_standard_interface_descriptor_t));
+	if (descr_iface == NULL) {
+		rc = ENOMEM;
+		goto error_leave;
+	}
+	descr_count++;
+	total_descr_size += sizeof(usb_standard_interface_descriptor_t);
+	descr_iface->length = sizeof(usb_standard_interface_descriptor_t);
+	descr_iface->descriptor_type = USB_DESCTYPE_INTERFACE;
+	descr_iface->interface_number
+	    = dev->descriptors->configuration->descriptor->interface_count;
+	descr_iface->alternate_setting = 0;
+	descr_iface->endpoint_count = ep_count;
+	descr_iface->interface_class = USB_CLASS_HID;
+	descr_iface->interface_subclass = iface->usb_subclass;
+	descr_iface->interface_protocol = iface->usb_protocol;
+	descr_iface->str_interface = 0;
+
+	/* Create the HID descriptor. */
+	if (iface->report_descriptor != NULL) {
+		descr_hid = malloc(sizeof(hid_descriptor_t));
+		if (descr_hid == NULL) {
+			rc = ENOMEM;
+			goto error_leave;
+		}
+		descr_count++;
+		total_descr_size += sizeof(hid_descriptor_t);
+		descr_hid->length = sizeof(hid_descriptor_t);
+		descr_hid->type = USB_DESCTYPE_HID;
+		descr_hid->hid_spec_release = 0x101;
+		descr_hid->country_code = 0;
+		descr_hid->descriptor_count = 1;
+		descr_hid->descriptor1_type = USB_DESCTYPE_HID_REPORT;
+		descr_hid->descriptor1_length = iface->report_descriptor_size;
+	}
+
+	/* Prepare the endpoint descriptors. */
+	if (iface->in_data_size > 0) {
+		descr_ep_in = malloc(sizeof(usb_standard_endpoint_descriptor_t));
+		if (descr_ep_in == NULL) {
+			rc = ENOMEM;
+			goto error_leave;
+		}
+		descr_count++;
+		total_descr_size += sizeof(usb_standard_endpoint_descriptor_t);
+		descr_ep_in->length = sizeof(usb_standard_endpoint_descriptor_t);
+		descr_ep_in->descriptor_type = USB_DESCTYPE_ENDPOINT;
+		descr_ep_in->endpoint_address
+		    = 0x80 | hid_data->in_endpoint_first_free;
+		descr_ep_in->attributes = 3;
+		descr_ep_in->max_packet_size = iface->in_data_size;
+		descr_ep_in->poll_interval = 10;
+	}
+	if (iface->out_data_size > 0) {
+		descr_ep_out = malloc(sizeof(usb_standard_endpoint_descriptor_t));
+		if (descr_ep_out == NULL) {
+			rc = ENOMEM;
+			goto error_leave;
+		}
+		descr_count++;
+		total_descr_size += sizeof(usb_standard_endpoint_descriptor_t);
+		descr_ep_out->length = sizeof(usb_standard_endpoint_descriptor_t);
+		descr_ep_out->descriptor_type = USB_DESCTYPE_ENDPOINT;
+		descr_ep_out->endpoint_address
+		    = 0x00 | hid_data->out_endpoint_first_free;
+		descr_ep_out->attributes = 3;
+		descr_ep_out->max_packet_size = iface->out_data_size;
+		descr_ep_out->poll_interval = 10;
+	}
+
+	/* Extend existing extra descriptors with these ones. */
+	usbvirt_device_configuration_extras_t *extra_descriptors
+	    = dev->descriptors->configuration->extra;
+	extra_descriptors = realloc(extra_descriptors,
+	    sizeof(usbvirt_device_configuration_extras_t)
+	    * (dev->descriptors->configuration->extra_count + descr_count));
+	if (extra_descriptors == NULL) {
+		rc = ENOMEM;
+		goto error_leave;
+	}
+
+	/* Launch the "life" fibril. */
+	iface->vuhid_data = hid_data;
+	fid_t life_fibril = fibril_create(interface_life_fibril, iface);
+	if (life_fibril == 0) {
+		rc = ENOMEM;
+		goto error_leave;
+	}
+
+	/* Allocation is okay, we can (actually have to now) overwrite the
+	 * original pointer.
+	 */
+	dev->descriptors->configuration->extra = extra_descriptors;
+	extra_descriptors += dev->descriptors->configuration->extra_count;
+
+	/* Initialize them. */
+#define _APPEND_DESCRIPTOR(descriptor) \
+	do { \
+		if ((descriptor) != NULL) { \
+			extra_descriptors->data = (uint8_t *) (descriptor); \
+			extra_descriptors->length = (descriptor)->length; \
+			extra_descriptors++; \
+		} \
+	} while (0)
+
+	_APPEND_DESCRIPTOR(descr_iface);
+	_APPEND_DESCRIPTOR(descr_hid);
+	_APPEND_DESCRIPTOR(descr_ep_in);
+	_APPEND_DESCRIPTOR(descr_ep_out);
+#undef _APPEND_DESCRIPTOR
+	dev->descriptors->configuration->extra_count += descr_count;
+
+	/*
+	 * Final changes.
+	 * Increase the necessary counters, make endpoint mapping.
+	 *
+	 */
+	if (iface->in_data_size > 0) {
+		hid_data->in_endpoints_mapping[hid_data->in_endpoint_first_free]
+		    = iface;
+		dev->ops->data_in[hid_data->in_endpoint_first_free]
+		    = on_data_from_device;
+		hid_data->in_endpoint_first_free++;
+	}
+	if (iface->out_data_size > 0) {
+		hid_data->out_endpoints_mapping[hid_data->out_endpoint_first_free]
+		    = iface;
+		dev->ops->data_out[hid_data->out_endpoint_first_free]
+		    = on_data_to_device;
+		hid_data->out_endpoint_first_free++;
+	}
+
+	hid_data->interface_mapping[
+	    dev->descriptors->configuration->descriptor->interface_count]
+	    = iface;
+
+	dev->descriptors->configuration->descriptor->interface_count++;
+	dev->descriptors->configuration->descriptor->total_length
+	    += total_descr_size;
+
+	hid_data->iface_count++;
+	fibril_add_ready(life_fibril);
+
+	return EOK;
+
+error_leave:
+	if (descr_iface != NULL) {
+		free(descr_iface);
+	}
+	if (descr_hid != NULL) {
+		free(descr_hid);
+	}
+	if (descr_ep_in != NULL) {
+		free(descr_ep_in);
+	}
+	if (descr_ep_out != NULL) {
+		free(descr_ep_out);
+	}
+
+	return rc;
+}
+
+void wait_for_interfaces_death(usbvirt_device_t *dev)
+{
+	vuhid_data_t *hid_data = dev->device_data;
+
+	fibril_mutex_lock(&hid_data->iface_count_mutex);
+	while (hid_data->iface_died_count < hid_data->iface_count) {
+		fibril_condvar_wait(&hid_data->iface_count_cv,
+		    &hid_data->iface_count_mutex);
+	}
+	fibril_mutex_unlock(&hid_data->iface_count_mutex);
+}
+
+/** @}
+ */
Index: uspace/app/vuhid/hids/bootkbd.c
===================================================================
--- uspace/app/vuhid/hids/bootkbd.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/vuhid/hids/bootkbd.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2011 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 usbvirthid
+ * @{
+ */
+/** @file
+ *
+ */
+#include "../virthid.h"
+#include <errno.h>
+#include <usb/debug.h>
+#include <usb/hid/hid.h>
+#include <usb/hid/usages/core.h>
+
+#include "../report.h"
+
+uint8_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(6),
+		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()
+};
+
+#define INPUT_SIZE 8
+
+static uint8_t in_data[] = {
+	     0, 0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	     0, 0, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, // Caps Lock
+	     0, 0, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, // Num Lock
+	     0, 0, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, // Caps Lock
+	1 << 2, 0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	1 << 2, 0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00,
+	1 << 2, 0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	     0, 0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static size_t in_data_count = sizeof(in_data)/INPUT_SIZE;
+// FIXME - locking
+static size_t in_data_position = 0;
+
+static int on_data_in(vuhid_interface_t *iface,
+    void *buffer, size_t buffer_size, size_t *act_buffer_size)
+{
+	static size_t last_pos = (size_t) -1;
+	size_t pos = in_data_position;
+	if (pos >= in_data_count) {
+		return EBADCHECKSUM;
+	}
+
+	if (last_pos == pos) {
+		return ENAK;
+	}
+
+	if (buffer_size > INPUT_SIZE) {
+		buffer_size = INPUT_SIZE;
+	}
+
+	if (act_buffer_size != NULL) {
+		*act_buffer_size = buffer_size;
+	}
+
+	memcpy(buffer, in_data + pos * INPUT_SIZE, buffer_size);
+	last_pos = pos;
+
+	return EOK;
+}
+
+static int on_data_out(vuhid_interface_t *iface,
+    void *buffer, size_t buffer_size)
+{
+	if (buffer_size == 0) {
+		return EEMPTY;
+	}
+	uint8_t leds = ((uint8_t *) buffer)[0];
+#define _GET_LED(index, signature) \
+	(((leds) & (1 << index)) ? (signature) : '-')
+	usb_log_info("%s: LEDs = %c%c%c%c%c\n",
+	    iface->name,
+	    _GET_LED(0, '0'), _GET_LED(1, 'A'), _GET_LED(2, 's'),
+	    _GET_LED(3, 'c'), _GET_LED(4, 'k'));
+#undef _GET_LED
+	return EOK;
+}
+
+
+static void live(vuhid_interface_t *iface)
+{
+	async_usleep(1000 * 1000 * 5);
+	usb_log_debug("Boot keyboard comes to life...\n");
+	while (in_data_position < in_data_count) {
+		async_usleep(1000 * 500);
+		in_data_position++;
+	}
+	usb_log_debug("Boot keyboard died.\n");
+}
+
+
+vuhid_interface_t vuhid_interface_bootkbd = {
+	.id = "boot",
+	.name = "boot keyboard",
+	.usb_subclass = USB_HID_SUBCLASS_BOOT,
+	.usb_protocol = USB_HID_PROTOCOL_KEYBOARD,
+
+	.report_descriptor = report_descriptor,
+	.report_descriptor_size = sizeof(report_descriptor),
+
+	.in_data_size = INPUT_SIZE,
+	.on_data_in = on_data_in,
+
+	.out_data_size = 1,
+	.on_data_out = on_data_out,
+
+	.live = live,
+
+	.vuhid_data = NULL
+};
+
+/**
+ * @}
+ */
Index: uspace/app/vuhid/ifaces.c
===================================================================
--- uspace/app/vuhid/ifaces.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/vuhid/ifaces.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2011 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 usbvirthid
+ * @{
+ */
+/**
+ * @file
+ * List of known interfaces.
+ */
+#include <stdlib.h>
+#include "ifaces.h"
+
+extern vuhid_interface_t vuhid_interface_bootkbd;
+
+vuhid_interface_t *available_hid_interfaces[] = {
+	&vuhid_interface_bootkbd,
+	NULL
+};
+
+
+/** @}
+ */
Index: uspace/app/vuhid/ifaces.h
===================================================================
--- uspace/app/vuhid/ifaces.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/vuhid/ifaces.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 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 usbvirthid
+ * @{
+ */
+/** @file
+ *
+ */
+#ifndef VUHID_IFACES_H_
+#define VUHID_IFACES_H_
+
+#include "virthid.h"
+
+extern vuhid_interface_t *available_hid_interfaces[];
+
+#endif
+/**
+ * @}
+ */
Index: uspace/app/vuhid/items.h
===================================================================
--- uspace/app/vuhid/items.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/vuhid/items.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -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 usbvirtkbd
+ * @{
+ */
+/** @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/vuhid/main.c
===================================================================
--- uspace/app/vuhid/main.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/vuhid/main.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2011 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 usbvirthid
+ * @{
+ */
+/**
+ * @file
+ * Virtual USB HID device.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <str_error.h>
+
+#include <usb/usb.h>
+#include <usb/descriptor.h>
+#include <usb/debug.h>
+#include <usb/classes/classes.h>
+#include <usb/hid/hid.h>
+#include <usbvirt/device.h>
+
+#include "virthid.h"
+#include "ifaces.h"
+#include "stdreq.h"
+
+static usbvirt_control_request_handler_t endpoint_zero_handlers[] = {
+	{
+		.req_direction = USB_DIRECTION_IN,
+		.req_type = USB_REQUEST_TYPE_STANDARD,
+		.req_recipient = USB_REQUEST_RECIPIENT_INTERFACE,
+		.request = USB_DEVREQ_GET_DESCRIPTOR,
+		.name = "Get_Descriptor",
+		.callback = req_get_descriptor
+	},
+	{
+		.req_direction = USB_DIRECTION_OUT,
+		.req_recipient = USB_REQUEST_RECIPIENT_INTERFACE,
+		.req_type = USB_REQUEST_TYPE_CLASS,
+		.request = USB_HIDREQ_SET_PROTOCOL,
+		.name = "Set_Protocol",
+		.callback = req_set_protocol
+	},
+	{
+		.req_direction = USB_DIRECTION_OUT,
+		.req_recipient = USB_REQUEST_RECIPIENT_INTERFACE,
+		.req_type = USB_REQUEST_TYPE_CLASS,
+		.request = USB_HIDREQ_SET_REPORT,
+		.name = "Set_Report",
+		.callback = req_set_report
+	},
+	{
+		.callback = NULL
+	}
+};
+
+/** Keyboard callbacks.
+ * We abuse the fact that static variables are zero-filled.
+ */
+static usbvirt_device_ops_t hid_ops = {
+	.control = endpoint_zero_handlers,
+};
+
+/** Standard configuration descriptor. */
+static usb_standard_configuration_descriptor_t std_configuration_descriptor = {
+	.length = sizeof(usb_standard_configuration_descriptor_t),
+	.descriptor_type = USB_DESCTYPE_CONFIGURATION,
+	/* Will be patched at runtime. */
+	.total_length = sizeof(usb_standard_configuration_descriptor_t),
+	.interface_count = 0,
+	.configuration_number = 1,
+	.str_configuration = 0,
+	.attributes = 128, /* denotes bus-powered device */
+	.max_power = 50
+};
+
+/** Standard device descriptor. */
+static 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
+};
+
+/** HID configuration. */
+usbvirt_device_configuration_t configuration = {
+	.descriptor = &std_configuration_descriptor,
+	.extra = NULL,
+	.extra_count = 0
+};
+
+/** HID standard descriptors. */
+usbvirt_descriptors_t descriptors = {
+	.device = &std_device_descriptor,
+	.configuration = &configuration,
+	.configuration_count = 1,
+};
+
+static vuhid_data_t vuhid_data = {
+	.in_endpoints_mapping = { NULL },
+	.in_endpoint_first_free = 1,
+	.out_endpoints_mapping = { NULL },
+	.out_endpoint_first_free = 1,
+
+	.iface_count = 0,
+	.iface_died_count = 0
+	// mutex and CV must be initialized elsewhere
+};
+
+
+/** Keyboard device.
+ * Rest of the items will be initialized later.
+ */
+static usbvirt_device_t hid_dev = {
+	.ops = &hid_ops,
+	.descriptors = &descriptors,
+	.name = "HID",
+	.device_data = &vuhid_data
+};
+
+
+int main(int argc, char * argv[])
+{
+	int rc;
+
+	usb_log_enable(USB_LOG_LEVEL_DEBUG2, "vusbhid");
+
+	fibril_mutex_initialize(&vuhid_data.iface_count_mutex);
+	fibril_condvar_initialize(&vuhid_data.iface_count_cv);
+
+	/* Determine which interfaces to initialize. */
+	int i;
+	for (i = 1; i < argc; i++) {
+		rc = add_interface_by_id(available_hid_interfaces, argv[i],
+		    &hid_dev);
+		if (rc != EOK) {
+			fprintf(stderr, "Failed to add device `%s': %s.\n",
+			    argv[i], str_error(rc));
+		} else {
+			printf("Added device `%s'.\n", argv[i]);
+		}
+	}
+
+	for (i = 0; i < (int) hid_dev.descriptors->configuration->extra_count; i++) {
+		usb_log_debug("Found extra descriptor: %s.\n",
+		    usb_debug_str_buffer(
+		        hid_dev.descriptors->configuration->extra[i].data,
+		        hid_dev.descriptors->configuration->extra[i].length,
+		        0));
+	}
+
+	rc = usbvirt_device_plug(&hid_dev, "/virt/usbhc/hc");
+	if (rc != EOK) {
+		printf("Unable to start communication with VHCD: %s.\n",
+		    str_error(rc));
+		return rc;
+	}
+	
+	printf("Connected to VHCD...\n");
+
+	wait_for_interfaces_death(&hid_dev);
+	
+	printf("Terminating...\n");
+	
+	usbvirt_device_unplug(&hid_dev);
+
+	return 0;
+}
+
+
+/** @}
+ */
Index: uspace/app/vuhid/report.h
===================================================================
--- uspace/app/vuhid/report.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/vuhid/report.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -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 usbvirtkbd
+ * @{
+ */
+/** @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/vuhid/stdreq.c
===================================================================
--- uspace/app/vuhid/stdreq.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/vuhid/stdreq.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2011 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 usbvirthid
+ * @{
+ */
+/**
+ * @file
+ *
+ */
+#include <errno.h>
+#include <usb/debug.h>
+#include <usb/descriptor.h>
+#include "stdreq.h"
+#include "virthid.h"
+
+#define VUHID_DATA(vuhid, device) \
+	vuhid_data_t *vuhid = device->device_data
+
+int req_get_descriptor(usbvirt_device_t *device,
+    const usb_device_request_setup_packet_t *setup_packet,
+    uint8_t *data, size_t *act_size)
+{
+	VUHID_DATA(vuhid, device);
+
+	if (setup_packet->value_high == USB_DESCTYPE_HID_REPORT) {
+		vuhid_interface_t *iface
+		    = vuhid->interface_mapping[setup_packet->index];
+		if (iface == NULL) {
+			return EFORWARD;
+		}
+		if (iface->report_descriptor != NULL) {
+			usbvirt_control_reply_helper(setup_packet,
+			    data, act_size,
+			    iface->report_descriptor,
+			    iface->report_descriptor_size);
+			return EOK;
+		} else {
+			return ENOENT;
+		}
+	}
+	
+	/* Let the framework handle all the rest. */
+	return EFORWARD;
+}
+
+int req_set_protocol(usbvirt_device_t *device,
+    const usb_device_request_setup_packet_t *setup_packet,
+    uint8_t *data, size_t *act_size)
+{
+	VUHID_DATA(vuhid, device);
+
+	size_t iface_index = setup_packet->index;
+	int protocol = setup_packet->value;
+
+	// FIXME - check ranges
+	vuhid_interface_t *iface = vuhid->interface_mapping[iface_index];
+	if (iface == NULL) {
+		return ENOENT;
+	}
+	iface->set_protocol = protocol;
+
+	return EOK;
+}
+
+int req_set_report(usbvirt_device_t *device,
+    const usb_device_request_setup_packet_t *setup_packet,
+    uint8_t *data, size_t *act_size)
+{
+	VUHID_DATA(vuhid, device);
+
+	size_t iface_index = setup_packet->index;
+
+	// FIXME - check ranges
+	vuhid_interface_t *iface = vuhid->interface_mapping[iface_index];
+	if (iface == NULL) {
+		return ENOENT;
+	}
+
+	size_t data_length = setup_packet->length;
+	if (iface->on_data_out == NULL) {
+		return ENOTSUP;
+	}
+
+	/* SET_REPORT is translated to data out */
+	int rc = iface->on_data_out(iface, data, data_length);
+
+	return rc;
+}
+
+
+
+/** @}
+ */
Index: uspace/app/vuhid/stdreq.h
===================================================================
--- uspace/app/vuhid/stdreq.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/vuhid/stdreq.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2011 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 usbvirthid
+ * @{
+ */
+/** @file
+ * Device request handlers.
+ */
+#ifndef VUHID_STDREQ_H_
+#define VUHID_STDREQ_H_
+
+#include <usbvirt/device.h>
+
+int req_get_descriptor(usbvirt_device_t *device,
+    const usb_device_request_setup_packet_t *setup_packet,
+    uint8_t *data, size_t *act_size);
+
+int req_set_protocol(usbvirt_device_t *device,
+    const usb_device_request_setup_packet_t *setup_packet,
+    uint8_t *data, size_t *act_size);
+
+int req_set_report(usbvirt_device_t *device,
+    const usb_device_request_setup_packet_t *setup_packet,
+    uint8_t *data, size_t *act_size);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/app/vuhid/virthid.h
===================================================================
--- uspace/app/vuhid/virthid.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
+++ uspace/app/vuhid/virthid.h	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2011 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 usbvirthid
+ * @{
+ */
+/** @file
+ *
+ */
+#ifndef VUHID_VIRTHID_H_
+#define VUHID_VIRTHID_H_
+
+#include <usb/usb.h>
+#include <usbvirt/device.h>
+#include <fibril_synch.h>
+
+#define VUHID_ENDPOINT_MAX USB11_ENDPOINT_MAX
+#define VUHID_INTERFACE_MAX 8
+
+typedef struct vuhid_interface vuhid_interface_t;
+
+typedef struct {
+	vuhid_interface_t *in_endpoints_mapping[VUHID_ENDPOINT_MAX];
+	size_t in_endpoint_first_free;
+	vuhid_interface_t *out_endpoints_mapping[VUHID_ENDPOINT_MAX];
+	size_t out_endpoint_first_free;
+	vuhid_interface_t *interface_mapping[VUHID_INTERFACE_MAX];
+
+	fibril_mutex_t iface_count_mutex;
+	fibril_condvar_t iface_count_cv;
+	size_t iface_count;
+	size_t iface_died_count;
+} vuhid_data_t;
+
+struct vuhid_interface {
+	const char *name;
+	const char *id;
+	int usb_subclass;
+	int usb_protocol;
+
+	uint8_t *report_descriptor;
+	size_t report_descriptor_size;
+
+	size_t in_data_size;
+	size_t out_data_size;
+
+	int (*on_data_in)(vuhid_interface_t *, void *, size_t, size_t *);
+	int (*on_data_out)(vuhid_interface_t *, void *, size_t);
+	void (*live)(vuhid_interface_t *);
+
+	int set_protocol;
+
+	void *interface_data;
+
+	vuhid_data_t *vuhid_data;
+};
+
+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;
+
+int add_interface_by_id(vuhid_interface_t **, const char *, usbvirt_device_t *);
+void wait_for_interfaces_death(usbvirt_device_t *);
+
+#endif
+/**
+ * @}
+ */
