Index: uspace/drv/uhci-hcd/iface.c
===================================================================
--- uspace/drv/uhci-hcd/iface.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/drv/uhci-hcd/iface.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -42,15 +42,4 @@
 #include "uhci.h"
 
-static int get_address(device_t *dev, devman_handle_t handle,
-    usb_address_t *address)
-{
-	assert(dev);
-	uhci_t *hc = dev_to_uhci(dev);
-	assert(hc);
-	*address = usb_address_keeping_find(&hc->address_manager, handle);
-	if (*address <= 0)
-	  return *address;
-	return EOK;
-}
 /*----------------------------------------------------------------------------*/
 static int reserve_default_address(device_t *dev, usb_speed_t speed)
@@ -168,6 +157,4 @@
 /*----------------------------------------------------------------------------*/
 usbhc_iface_t uhci_iface = {
-	.tell_address = get_address,
-
 	.reserve_default_address = reserve_default_address,
 	.release_default_address = release_default_address,
Index: uspace/drv/uhci-hcd/main.c
===================================================================
--- uspace/drv/uhci-hcd/main.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/drv/uhci-hcd/main.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -34,4 +34,5 @@
 #include <driver.h>
 #include <usb_iface.h>
+#include <usb/ddfiface.h>
 #include <device/hw_res.h>
 
@@ -48,17 +49,29 @@
 
 static int uhci_add_device(device_t *device);
-static int usb_iface_get_hc_handle(device_t *dev, devman_handle_t *handle);
-/*----------------------------------------------------------------------------*/
-static int usb_iface_get_hc_handle(device_t *dev, devman_handle_t *handle)
+
+static int usb_iface_get_address(device_t *dev, devman_handle_t handle,
+    usb_address_t *address)
 {
-	/* This shall be called only for the UHCI itself. */
-	assert(dev->parent == NULL);
+	assert(dev);
+	uhci_t *hc = dev_to_uhci(dev);
+	assert(hc);
 
-	*handle = dev->handle;
+	usb_address_t addr = usb_address_keeping_find(&hc->address_manager,
+	    handle);
+	if (addr < 0) {
+		return addr;
+	}
+
+	if (address != NULL) {
+		*address = addr;
+	}
+
 	return EOK;
 }
-/*----------------------------------------------------------------------------*/
+
+
 static usb_iface_t hc_usb_iface = {
-	.get_hc_handle = usb_iface_get_hc_handle
+	.get_hc_handle = usb_iface_get_hc_handle_hc_impl,
+	.get_address = usb_iface_get_address
 };
 /*----------------------------------------------------------------------------*/
Index: uspace/drv/uhci-rhd/main.c
===================================================================
--- uspace/drv/uhci-rhd/main.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/drv/uhci-rhd/main.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -34,4 +34,5 @@
 #include <driver.h>
 #include <usb_iface.h>
+#include <usb/ddfiface.h>
 
 #include <errno.h>
@@ -56,5 +57,6 @@
 
 static usb_iface_t uhci_rh_usb_iface = {
-	.get_hc_handle = usb_iface_get_hc_handle
+	.get_hc_handle = usb_iface_get_hc_handle,
+	.get_address = usb_iface_get_address_hub_impl
 };
 
Index: uspace/drv/usbhid/main.c
===================================================================
--- uspace/drv/usbhid/main.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/drv/usbhid/main.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -265,8 +265,16 @@
 	for (i = 0; i < count; ++i) {
 		printf("%d ", key_codes[i]);
+	}
+	printf("\n");
+
+	for (i = 0; i < count; ++i) {
 		// TODO: Key press / release
 
 		// TODO: NOT WORKING
 		unsigned int key = usbkbd_parse_scancode(key_codes[i]);
+
+		if (key == 0) {
+			continue;
+		}
 		kbd_push_ev(KEY_PRESS, key);
 	}
@@ -348,5 +356,7 @@
 		{
 			.pipe = &kbd_dev->poll_pipe,
-			.description = &poll_endpoint_description
+			.description = &poll_endpoint_description,
+			.interface_no =
+			    usb_device_get_assigned_interface(kbd_dev->device)
 		}
 	};
Index: uspace/drv/usbhub/usbhub.c
===================================================================
--- uspace/drv/usbhub/usbhub.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/drv/usbhub/usbhub.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -39,4 +39,5 @@
 
 #include <usb_iface.h>
+#include <usb/ddfiface.h>
 #include <usb/usbdrv.h>
 #include <usb/descriptor.h>
@@ -52,15 +53,6 @@
 #include "usb/pipes.h"
 
-static int iface_get_hc_handle(device_t *device, devman_handle_t *handle)
-{
-	return usb_hc_find(device->handle, handle);
-}
-
-static usb_iface_t hub_usb_iface = {
-	.get_hc_handle = iface_get_hc_handle
-};
-
 static device_ops_t hub_device_ops = {
-	.interfaces[USB_DEV_IFACE] = &hub_usb_iface
+	.interfaces[USB_DEV_IFACE] = &usb_iface_hub_impl
 };
 
Index: uspace/drv/usbmid/Makefile
===================================================================
--- uspace/drv/usbmid/Makefile	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
+++ uspace/drv/usbmid/Makefile	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -0,0 +1,40 @@
+#
+# 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 = ../..
+LIBS = $(LIBDRV_PREFIX)/libdrv.a $(LIBUSB_PREFIX)/libusb.a
+EXTRA_CFLAGS += -I$(LIBDRV_PREFIX)/include -I$(LIBUSB_PREFIX)/include
+BINARY = usbmid
+
+SOURCES = \
+	dump.c \
+	explore.c \
+	main.c \
+	usbmid.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/usbmid/dump.c
===================================================================
--- uspace/drv/usbmid/dump.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
+++ uspace/drv/usbmid/dump.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -0,0 +1,120 @@
+/*
+ * 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 drvusbmid
+ * @{
+ */
+/**
+ * @file
+ * Dumping and debugging functions.
+ */
+#include <errno.h>
+#include <str_error.h>
+#include <stdlib.h>
+#include <usb/pipes.h>
+#include <usb/dp.h>
+#include <usb/classes/classes.h>
+#include "usbmid.h"
+
+/** Dump found descriptor.
+ *
+ * @param data Descriptor data.
+ * @param depth Nesting depth.
+ */
+static void dump_tree_descriptor(uint8_t *data, size_t depth)
+{
+	if (data == NULL) {
+		return;
+	}
+	int type = (int) *(data + 1);
+	if (type == USB_DESCTYPE_INTERFACE) {
+		usb_standard_interface_descriptor_t *descriptor
+		    = (usb_standard_interface_descriptor_t *) data;
+		usb_log_info("Found interface: %s (0x%02x/0x%02x/0x%02x).\n",
+		    usb_str_class(descriptor->interface_class),
+		    (int) descriptor->interface_class,
+		    (int) descriptor->interface_subclass,
+		    (int) descriptor->interface_protocol);
+	}
+}
+
+/** Dump tree of descriptors.
+ *
+ * @param parser Descriptor parser.
+ * @param data Descriptor parser data.
+ * @param root Pointer to current root.
+ * @param depth Nesting depth.
+ */
+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);
+}
+
+/** Dump descriptor tree.
+ *
+ * @param parser Descriptor parser.
+ * @param data Descriptor parser data.
+ */
+static void dump_tree(usb_dp_parser_t *parser, usb_dp_parser_data_t *data)
+{
+	uint8_t *ptr = data->data;
+	dump_tree_internal(parser, data, ptr, 0);
+}
+
+/** Dump given descriptors.
+ *
+ * @param descriptors Descriptors buffer (typically full config descriptor).
+ * @param length Size of @p descriptors buffer in bytes.
+ */
+void usbmid_dump_descriptors(uint8_t *descriptors, size_t length)
+{
+	usb_dp_parser_data_t data = {
+		.data = descriptors,
+		.size = length,
+		.arg = NULL
+	};
+
+	usb_dp_parser_t parser = {
+		.nesting = usb_dp_standard_descriptor_nesting
+	};
+
+	dump_tree(&parser, &data);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/usbmid/explore.c
===================================================================
--- uspace/drv/usbmid/explore.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
+++ uspace/drv/usbmid/explore.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -0,0 +1,234 @@
+/*
+ * 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 drvusbmid
+ * @{
+ */
+/**
+ * @file
+ * Exploration of available interfaces in the USB device.
+ */
+#include <errno.h>
+#include <str_error.h>
+#include <stdlib.h>
+#include <usb/classes/classes.h>
+#include <usb/request.h>
+#include <usb/dp.h>
+#include "usbmid.h"
+
+/** Allocate and retrieve full configuration descriptor.
+ *
+ * @param[in] dev USB device.
+ * @param[in] config_index Configuration index.
+ * @param[out] size Pointer where to store size of the allocated buffer.
+ * @return Allocated full configuration descriptor.
+ * @retval NULL Error occured.
+ */
+static void *get_configuration_descriptor(usbmid_device_t *dev,
+    size_t config_index, size_t *size)
+{
+	usb_standard_configuration_descriptor_t config_descriptor;
+	int rc = usb_request_get_bare_configuration_descriptor(&dev->ctrl_pipe,
+	    config_index, &config_descriptor);
+	if (rc != EOK) {
+		usb_log_error("Failed getting configuration descriptor: %s.\n",
+		    str_error(rc));
+		return NULL;
+	}
+
+	void *full_config_descriptor = malloc(config_descriptor.total_length);
+	if (full_config_descriptor == NULL) {
+		usb_log_fatal("Out of memory (wanted: %zuB).\n",
+		    (size_t) config_descriptor.total_length);
+		return NULL;
+	}
+
+	size_t full_config_descriptor_size;
+	rc = usb_request_get_full_configuration_descriptor(&dev->ctrl_pipe,
+	    config_index,
+	    full_config_descriptor, config_descriptor.total_length,
+	    &full_config_descriptor_size);
+	if (rc != EOK) {
+		usb_log_error("Failed getting configuration descriptor: %s.\n",
+		    str_error(rc));
+		free(full_config_descriptor);
+		return NULL;
+	}
+
+	if (full_config_descriptor_size != config_descriptor.total_length) {
+		usb_log_error("Failed getting full configuration descriptor.\n");
+		free(full_config_descriptor);
+		return NULL;
+	}
+
+	if (size != NULL) {
+		*size = full_config_descriptor_size;
+	}
+
+	return full_config_descriptor;
+}
+
+/** Find starting indexes of all interface descriptors in a configuration.
+ *
+ * @param config_descriptor Full configuration descriptor.
+ * @param config_descriptor_size Size of @p config_descriptor in bytes.
+ * @param interface_positions Array where to store indexes of interfaces.
+ * @param interface_count Size of @p interface_positions array.
+ * @return Number of found interfaces.
+ * @retval (size_t)-1 Error occured.
+ */
+static size_t find_interface_descriptors(uint8_t *config_descriptor,
+    size_t config_descriptor_size,
+    size_t *interface_positions, size_t interface_count)
+{
+	if (interface_count == 0) {
+		return (size_t) -1;
+	}
+
+	usb_dp_parser_data_t data = {
+		.data = config_descriptor,
+		.size = config_descriptor_size,
+		.arg = NULL
+	};
+
+	usb_dp_parser_t parser = {
+		.nesting = usb_dp_standard_descriptor_nesting
+	};
+
+	uint8_t *interface = usb_dp_get_nested_descriptor(&parser, &data,
+	    data.data);
+	if (interface == NULL) {
+		return (size_t) -1;
+	}
+	if (interface[1] != USB_DESCTYPE_INTERFACE) {
+		return (size_t) -1;
+	}
+
+	size_t found_interfaces = 0;
+	interface_positions[found_interfaces] = interface - config_descriptor;
+	found_interfaces++;
+
+	while (interface != NULL) {
+		interface = usb_dp_get_sibling_descriptor(&parser, &data,
+		    data.data, interface);
+		if ((interface != NULL)
+		    && (found_interfaces < interface_count)
+		    && (interface[1] == USB_DESCTYPE_INTERFACE)) {
+			interface_positions[found_interfaces]
+			    = interface - config_descriptor;
+			found_interfaces++;
+		}
+	}
+
+	return found_interfaces;
+}
+
+/** Explore MID device.
+ *
+ * We expect that @p dev is initialized and session on control pipe is
+ * started.
+ *
+ * @param dev Device to be explored.
+ * @return Whether to accept this device from devman.
+ */
+bool usbmid_explore_device(usbmid_device_t *dev)
+{
+	usb_standard_device_descriptor_t device_descriptor;
+	int rc = usb_request_get_device_descriptor(&dev->ctrl_pipe,
+	    &device_descriptor);
+	if (rc != EOK) {
+		usb_log_error("Getting device descriptor failed: %s.\n",
+		    str_error(rc));
+		return false;
+	}
+
+	if (device_descriptor.device_class != USB_CLASS_USE_INTERFACE) {
+		usb_log_warning(
+		    "Device class: %d (%s), but expected class 0.\n",
+		    device_descriptor.device_class,
+		    usb_str_class(device_descriptor.device_class));
+		usb_log_error("Not multi interface device, refusing.\n");
+		return false;
+	}
+
+	size_t config_descriptor_size;
+	uint8_t *config_descriptor_raw = get_configuration_descriptor(dev, 0,
+	    &config_descriptor_size);
+	if (config_descriptor_raw == NULL) {
+		return false;
+	}
+
+	usb_standard_configuration_descriptor_t *config_descriptor
+	    = (usb_standard_configuration_descriptor_t *) config_descriptor_raw;
+
+	size_t *interface_descriptors
+	    = malloc(sizeof(size_t) * config_descriptor->interface_count);
+	if (interface_descriptors == NULL) {
+		usb_log_error("Out of memory (wanted %zuB).\n",
+		    sizeof(size_t) * config_descriptor->interface_count);
+		free(config_descriptor_raw);
+		return false;
+	}
+	size_t interface_descriptors_count
+	    = find_interface_descriptors(
+	    config_descriptor_raw, config_descriptor_size,
+	    interface_descriptors, config_descriptor->interface_count);
+
+	if (interface_descriptors_count == (size_t) -1) {
+		usb_log_error("Problem parsing configuration descriptor.\n");
+		free(config_descriptor_raw);
+		free(interface_descriptors);
+		return false;
+	}
+
+	size_t i;
+	for (i = 0; i < interface_descriptors_count; i++) {
+		usb_standard_interface_descriptor_t *interface
+		    = (usb_standard_interface_descriptor_t *)
+		    (config_descriptor_raw + interface_descriptors[i]);
+		usb_log_debug2("Interface descriptor at index %zu (type %d).\n",
+		    interface_descriptors[i], (int) interface->descriptor_type);
+		usb_log_info("Creating child for interface %d (%s).\n",
+		    (int) interface->interface_number,
+		    usb_str_class(interface->interface_class));
+		rc = usbmid_spawn_interface_child(dev, &device_descriptor,
+		    interface);
+		if (rc != EOK) {
+			usb_log_error("Failed to create interface child: %s.\n",
+			    str_error(rc));
+		}
+	}
+
+	free(config_descriptor_raw);
+
+	return true;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/usbmid/main.c
===================================================================
--- uspace/drv/usbmid/main.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
+++ uspace/drv/usbmid/main.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -0,0 +1,107 @@
+/*
+ * 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 drvusbmid
+ * @{
+ */
+/**
+ * @file
+ * Main routines of USB multi interface device driver.
+ */
+#include <errno.h>
+#include <str_error.h>
+#include <usb/debug.h>
+#include <usb/classes/classes.h>
+#include <usb/request.h>
+#include <usb/descriptor.h>
+#include <usb/pipes.h>
+
+#include "usbmid.h"
+
+static int usbmid_add_device(device_t *gen_dev)
+{
+	usbmid_device_t *dev = usbmid_device_create(gen_dev);
+	if (dev == NULL) {
+		return ENOMEM;
+	}
+
+	usb_log_info("Taking care of new MID: addr %d (HC %zu)\n",
+	    dev->wire.address, dev->wire.hc_handle);
+
+	int rc;
+
+	rc = usb_endpoint_pipe_start_session(&dev->ctrl_pipe);
+	if (rc != EOK) {
+		usb_log_error("Failed to start session on control pipe: %s.\n",
+		    str_error(rc));
+		goto error_leave;
+	}
+
+	bool accept = usbmid_explore_device(dev);
+
+	rc = usb_endpoint_pipe_end_session(&dev->ctrl_pipe);
+	if (rc != EOK) {
+		usb_log_warning("Failed to end session on control pipe: %s.\n",
+		    str_error(rc));
+	}
+
+	if (!accept) {
+		rc = ENOTSUP;
+		goto error_leave;
+	}
+
+	gen_dev->driver_data = dev;
+
+	return EOK;
+
+
+error_leave:
+	free(dev);
+	return rc;
+}
+
+static driver_ops_t mid_driver_ops = {
+	.add_device = usbmid_add_device,
+};
+
+static driver_t mid_driver = {
+	.name = NAME,
+	.driver_ops = &mid_driver_ops
+};
+
+int main(int argc, char *argv[])
+{
+	printf(NAME ": USB multi interface device driver.\n");
+
+	usb_log_enable(USB_LOG_LEVEL_DEBUG, NAME);
+	return driver_main(&mid_driver);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/usbmid/usbmid.c
===================================================================
--- uspace/drv/usbmid/usbmid.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
+++ uspace/drv/usbmid/usbmid.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -0,0 +1,242 @@
+/*
+ * 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 drvusbmid
+ * @{
+ */
+/**
+ * @file
+ * Helper functions.
+ */
+#include <errno.h>
+#include <str_error.h>
+#include <stdlib.h>
+#include <usb_iface.h>
+#include <usb/ddfiface.h>
+#include <usb/pipes.h>
+#include <usb/classes/classes.h>
+#include <usb/recognise.h>
+#include "usbmid.h"
+
+/** Callback for DDF USB interface. */
+static int usb_iface_get_address_impl(device_t *device, devman_handle_t handle,
+    usb_address_t *address)
+{
+	assert(device);
+	device_t *parent = device->parent;
+
+	/* Default error, device does not support this operation. */
+	int rc = ENOTSUP;
+
+	if (parent && parent->ops && parent->ops->interfaces[USB_DEV_IFACE]) {
+		usb_iface_t *usb_iface
+		    = (usb_iface_t *) parent->ops->interfaces[USB_DEV_IFACE];
+		assert(usb_iface != NULL);
+
+		if (usb_iface->get_address) {
+			rc = usb_iface->get_address(parent, parent->handle,
+			    address);
+		}
+	}
+
+	return rc;
+}
+
+/** Callback for DDF USB interface. */
+static int usb_iface_get_interface_impl(device_t *device, devman_handle_t handle,
+    int *iface_no)
+{
+	assert(device);
+
+	usbmid_interface_t *iface = device->driver_data;
+	assert(iface);
+
+	if (iface_no != NULL) {
+		*iface_no = iface->interface_no;
+	}
+
+	return EOK;
+}
+
+static usb_iface_t child_usb_iface = {
+	.get_hc_handle = usb_iface_get_hc_handle_hub_child_impl,
+	.get_address = usb_iface_get_address_impl,
+	.get_interface = usb_iface_get_interface_impl
+};
+
+
+static device_ops_t child_device_ops = {
+	.interfaces[USB_DEV_IFACE] = &child_usb_iface
+};
+
+static device_ops_t mid_device_ops = {
+	.interfaces[USB_DEV_IFACE] = &usb_iface_hub_impl
+};
+
+/** Create new USB multi interface device.
+ *
+ * @param dev Backing generic DDF device.
+ * @return New USB MID device.
+ * @retval NULL Error occured.
+ */
+usbmid_device_t *usbmid_device_create(device_t *dev)
+{
+	usbmid_device_t *mid = malloc(sizeof(usbmid_device_t));
+	if (mid == NULL) {
+		usb_log_error("Out of memory (wanted %zu bytes).\n",
+		    sizeof(usbmid_device_t));
+		return NULL;
+	}
+
+	int rc;
+	rc = usb_device_connection_initialize_from_device(&mid->wire, dev);
+	if (rc != EOK) {
+		usb_log_error("Failed to initialize `USB wire': %s.\n",
+		    str_error(rc));
+		free(mid);
+		return NULL;
+	}
+
+	rc = usb_endpoint_pipe_initialize_default_control(&mid->ctrl_pipe,
+	    &mid->wire);
+	if (rc != EOK) {
+		usb_log_error("Failed to initialize control pipe: %s.\n",
+		    str_error(rc));
+		free(mid);
+		return NULL;
+	}
+
+	mid->dev = dev;
+	dev->ops = &mid_device_ops;
+
+	return mid;
+}
+
+/** Create new interface for USB MID device.
+ *
+ * @param dev Backing generic DDF child device (representing interface).
+ * @param iface_no Interface number.
+ * @return New interface.
+ * @retval NULL Error occured.
+ */
+usbmid_interface_t *usbmid_interface_create(device_t *dev, int iface_no)
+{
+	usbmid_interface_t *iface = malloc(sizeof(usbmid_interface_t));
+	if (iface == NULL) {
+		usb_log_error("Out of memory (wanted %zuB).\n",
+		    sizeof(usbmid_interface_t));
+		return NULL;
+	}
+
+	iface->dev = dev;
+	iface->interface_no = iface_no;
+
+	return iface;
+}
+
+
+/** Spawn new child device from one interface.
+ *
+ * @param parent Parent MID device.
+ * @param device_descriptor Device descriptor.
+ * @param interface_descriptor Interface descriptor.
+ * @return Error code.
+ */
+int usbmid_spawn_interface_child(usbmid_device_t *parent,
+    const usb_standard_device_descriptor_t *device_descriptor,
+    const usb_standard_interface_descriptor_t *interface_descriptor)
+{
+	device_t *child = NULL;
+	char *child_name = NULL;
+	usbmid_interface_t *child_as_interface = NULL;
+	int rc;
+
+	/* Create the device. */
+	child = create_device();
+	if (child == NULL) {
+		rc = ENOMEM;
+		goto error_leave;
+	}
+
+	/*
+	 * Name is class name followed by interface number.
+	 * The interface number shall provide uniqueness while the
+	 * class name something humanly understandable.
+	 */
+	rc = asprintf(&child_name, "%s%d",
+	    usb_str_class(interface_descriptor->interface_class),
+	    (int) interface_descriptor->interface_number);
+	if (rc < 0) {
+		goto error_leave;
+	}
+
+	child_as_interface = usbmid_interface_create(child,
+	    (int) interface_descriptor->interface_number);
+	if (child_as_interface == NULL) {
+		rc = ENOMEM;
+		goto error_leave;
+	}
+
+	child->driver_data = child_as_interface;
+	child->parent = parent->dev;
+	child->name = child_name;
+	child->ops = &child_device_ops;
+
+	rc = usb_device_create_match_ids_from_interface(device_descriptor,
+	    interface_descriptor,
+	    &child->match_ids);
+	if (rc != EOK) {
+		goto error_leave;
+	}
+
+	rc = child_device_register(child, parent->dev);
+	if (rc != EOK) {
+		goto error_leave;
+	}
+
+	return EOK;
+
+error_leave:
+	if (child != NULL) {
+		child->name = NULL;
+		/* This takes care of match_id deallocation as well. */
+		delete_device(child);
+	}
+	if (child_name != NULL) {
+		free(child_name);
+	}
+	if (child_as_interface != NULL) {
+		free(child_as_interface);
+	}
+
+	return rc;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/usbmid/usbmid.h
===================================================================
--- uspace/drv/usbmid/usbmid.h	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
+++ uspace/drv/usbmid/usbmid.h	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -0,0 +1,75 @@
+/*
+ * 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 drvusbmid
+ * @{
+ */
+/** @file
+ * Common definitions.
+ */
+
+#ifndef USBMID_H_
+#define USBMID_H_
+
+#include <driver.h>
+#include <usb/usb.h>
+#include <usb/pipes.h>
+#include <usb/debug.h>
+
+#define NAME "usbmid"
+
+typedef struct {
+	/** Device container. */
+	device_t *dev;
+
+	/** Representation of USB wire. */
+	usb_device_connection_t wire;
+	/** Default control pipe. */
+	usb_endpoint_pipe_t ctrl_pipe;
+} usbmid_device_t;
+
+typedef struct {
+	/** Device container. */
+	device_t *dev;
+
+	/** Interface number. */
+	int interface_no;
+} usbmid_interface_t;
+
+usbmid_device_t *usbmid_device_create(device_t *);
+usbmid_interface_t *usbmid_interface_create(device_t *, int);
+bool usbmid_explore_device(usbmid_device_t *);
+int usbmid_spawn_interface_child(usbmid_device_t *,
+    const usb_standard_device_descriptor_t *,
+    const usb_standard_interface_descriptor_t *);
+void usbmid_dump_descriptors(uint8_t *, size_t);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/usbmid/usbmid.ma
===================================================================
--- uspace/drv/usbmid/usbmid.ma	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
+++ uspace/drv/usbmid/usbmid.ma	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -0,0 +1,1 @@
+100 usb&mid
Index: uspace/drv/vhc/conn.h
===================================================================
--- uspace/drv/vhc/conn.h	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/drv/vhc/conn.h	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -38,4 +38,5 @@
 #include <usb/usb.h>
 #include <usbhc_iface.h>
+#include <usb_iface.h>
 #include "vhcd.h"
 #include "devices.h"
@@ -43,5 +44,6 @@
 void connection_handler_host(sysarg_t);
 
-usbhc_iface_t vhc_iface;
+extern usbhc_iface_t vhc_iface;
+extern usb_iface_t vhc_usb_iface;
 
 void address_init(void);
Index: uspace/drv/vhc/connhost.c
===================================================================
--- uspace/drv/vhc/connhost.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/drv/vhc/connhost.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -37,4 +37,5 @@
 #include <usb/usb.h>
 #include <usb/addrkeep.h>
+#include <usb/ddfiface.h>
 
 #include "vhcd.h"
@@ -313,4 +314,15 @@
 static usb_address_keeping_t addresses;
 
+static int tell_address(device_t *dev, devman_handle_t handle,
+    usb_address_t *address)
+{
+	usb_address_t addr = usb_address_keeping_find(&addresses, handle);
+	if (addr < 0) {
+		return addr;
+	}
+
+	*address = addr;
+	return EOK;
+}
 
 static int reserve_default_address(device_t *dev, usb_speed_t ignored)
@@ -350,16 +362,4 @@
 }
 
-static int tell_address(device_t *dev, devman_handle_t handle,
-    usb_address_t *address)
-{
-	usb_address_t addr = usb_address_keeping_find(&addresses, handle);
-	if (addr < 0) {
-		return addr;
-	}
-
-	*address = addr;
-	return EOK;
-}
-
 void address_init(void)
 {
@@ -368,6 +368,4 @@
 
 usbhc_iface_t vhc_iface = {
-	.tell_address = tell_address,
-
 	.reserve_default_address = reserve_default_address,
 	.release_default_address = release_default_address,
@@ -383,4 +381,10 @@
 };
 
+usb_iface_t vhc_usb_iface = {
+	.get_hc_handle = usb_iface_get_hc_handle_hc_impl,
+	.get_address = tell_address
+};
+
+
 /**
  * @}
Index: uspace/drv/vhc/hcd.c
===================================================================
--- uspace/drv/vhc/hcd.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/drv/vhc/hcd.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -45,4 +45,5 @@
 
 #include <usb/usb.h>
+#include <usb/ddfiface.h>
 #include <usb_iface.h>
 #include "vhcd.h"
@@ -52,20 +53,7 @@
 #include "conn.h"
 
-static int usb_iface_get_hc_handle(device_t *dev, devman_handle_t *handle)
-{
-	/* This shall be called only for VHC device. */
-	assert(dev->parent == NULL);
-
-	*handle = dev->handle;
-	return EOK;
-}
-
-static usb_iface_t hc_usb_iface = {
-	.get_hc_handle = usb_iface_get_hc_handle
-};
-
 static device_ops_t vhc_ops = {
 	.interfaces[USBHC_DEV_IFACE] = &vhc_iface,
-	.interfaces[USB_DEV_IFACE] = &hc_usb_iface,
+	.interfaces[USB_DEV_IFACE] = &vhc_usb_iface,
 	.close = on_client_close,
 	.default_handler = default_connection_handler
