Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/Makefile	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -121,4 +121,5 @@
 		drv/usbhid \
 		drv/usbhub \
+		drv/usbmid \
 		drv/vhc
 endif
@@ -136,4 +137,5 @@
 		drv/usbhid \
 		drv/usbhub \
+		drv/usbmid \
 		drv/vhc
 endif
Index: uspace/doc/doxygroups.h
===================================================================
--- uspace/doc/doxygroups.h	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/doc/doxygroups.h	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -220,4 +220,17 @@
 
 	/**
+	 * @defgroup drvusbmid USB multi interface device driver
+	 * @ingroup usb
+	 * @brief USB multi interface device driver
+	 * @details
+	 * This driver serves as a mini hub (or bus) driver for devices
+	 * that have the class defined at interface level (those devices
+	 * usually have several interfaces).
+	 *
+	 * The term multi interface device driver (MID) was borrowed
+	 * Solaris operating system.
+	 */
+
+	/**
 	 * @defgroup drvusbhub USB hub driver
 	 * @ingroup usb
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
Index: uspace/lib/drv/generic/remote_usb.c
===================================================================
--- uspace/lib/drv/generic/remote_usb.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/drv/generic/remote_usb.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -40,4 +40,6 @@
 
 
+static void remote_usb_get_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usb_get_interface(device_t *, void *, ipc_callid_t, ipc_call_t *);
 static void remote_usb_get_hc_handle(device_t *, void *, ipc_callid_t, ipc_call_t *);
 //static void remote_usb(device_t *, void *, ipc_callid_t, ipc_call_t *);
@@ -45,4 +47,6 @@
 /** Remote USB interface operations. */
 static remote_iface_func_ptr_t remote_usb_iface_ops [] = {
+	remote_usb_get_address,
+	remote_usb_get_interface,
 	remote_usb_get_hc_handle
 };
@@ -56,4 +60,46 @@
 };
 
+
+void remote_usb_get_address(device_t *device, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usb_iface_t *usb_iface = (usb_iface_t *) iface;
+
+	if (usb_iface->get_address == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	devman_handle_t handle = DEV_IPC_GET_ARG1(*call);
+
+	usb_address_t address;
+	int rc = usb_iface->get_address(device, handle, &address);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+	} else {
+		async_answer_1(callid, EOK, address);
+	}
+}
+
+void remote_usb_get_interface(device_t *device, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usb_iface_t *usb_iface = (usb_iface_t *) iface;
+
+	if (usb_iface->get_interface == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	devman_handle_t handle = DEV_IPC_GET_ARG1(*call);
+
+	int iface_no;
+	int rc = usb_iface->get_interface(device, handle, &iface_no);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+	} else {
+		async_answer_1(callid, EOK, iface_no);
+	}
+}
 
 void remote_usb_get_hc_handle(device_t *device, void *iface,
Index: uspace/lib/drv/generic/remote_usbhc.c
===================================================================
--- uspace/lib/drv/generic/remote_usbhc.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/drv/generic/remote_usbhc.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -43,7 +43,8 @@
 #define HACK_MAX_PACKET_SIZE_INTERRUPT_IN 4
 
-static void remote_usbhc_get_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
 static void remote_usbhc_interrupt_out(device_t *, void *, ipc_callid_t, ipc_call_t *);
 static void remote_usbhc_interrupt_in(device_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_bulk_out(device_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_bulk_in(device_t *, void *, ipc_callid_t, ipc_call_t *);
 static void remote_usbhc_control_write(device_t *, void *, ipc_callid_t, ipc_call_t *);
 static void remote_usbhc_control_read(device_t *, void *, ipc_callid_t, ipc_call_t *);
@@ -57,6 +58,4 @@
 /** Remote USB host controller interface operations. */
 static remote_iface_func_ptr_t remote_usbhc_iface_ops [] = {
-	remote_usbhc_get_address,
-
 	remote_usbhc_reserve_default_address,
 	remote_usbhc_release_default_address,
@@ -68,4 +67,7 @@
 	remote_usbhc_interrupt_out,
 	remote_usbhc_interrupt_in,
+
+	remote_usbhc_bulk_out,
+	remote_usbhc_bulk_in,
 
 	remote_usbhc_control_write,
@@ -121,25 +123,4 @@
 }
 
-void remote_usbhc_get_address(device_t *device, void *iface,
-    ipc_callid_t callid, ipc_call_t *call)
-{
-	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
-
-	if (!usb_iface->tell_address) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	devman_handle_t handle = DEV_IPC_GET_ARG1(*call);
-
-	usb_address_t address;
-	int rc = usb_iface->tell_address(device, handle, &address);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-	} else {
-		async_answer_1(callid, EOK, address);
-	}
-}
-
 void remote_usbhc_reserve_default_address(device_t *device, void *iface,
     ipc_callid_t callid, ipc_call_t *call)
@@ -389,4 +370,24 @@
 	return remote_usbhc_in_transfer(device, callid, call,
 	    usb_iface->interrupt_in);
+}
+
+void remote_usbhc_bulk_out(device_t *device, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_out_transfer(device, callid, call,
+	    usb_iface->bulk_out);
+}
+
+void remote_usbhc_bulk_in(device_t *device, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_in_transfer(device, callid, call,
+	    usb_iface->bulk_in);
 }
 
Index: uspace/lib/drv/include/usb_iface.h
===================================================================
--- uspace/lib/drv/include/usb_iface.h	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/drv/include/usb_iface.h	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -41,4 +41,24 @@
 #include <usb/usb.h>
 typedef enum {
+	/** Tell USB address assigned to device.
+	 * Parameters:
+	 * - devman handle id
+	 * Answer:
+	 * - EINVAL - unknown handle or handle not managed by this driver
+	 * - ENOTSUP - operation not supported (shall not happen)
+	 * - arbitrary error code if returned by remote implementation
+	 * - EOK - handle found, first parameter contains the USB address
+	 */
+	IPC_M_USB_GET_ADDRESS,
+
+	/** Tell interface number given device can use.
+	 * Parameters
+	 * - devman handle id of the device
+	 * Answer:
+	 * - ENOTSUP - operation not supported (can also mean any interface)
+	 * - EOK - operation okay, first parameter contains interface number
+	 */
+	IPC_M_USB_GET_INTERFACE,
+
 	/** Tell devman handle of device host controller.
 	 * Parameters:
@@ -55,4 +75,6 @@
 /** USB device communication interface. */
 typedef struct {
+	int (*get_address)(device_t *, devman_handle_t, usb_address_t *);
+	int (*get_interface)(device_t *, devman_handle_t, int *);
 	int (*get_hc_handle)(device_t *, devman_handle_t *);
 } usb_iface_t;
Index: uspace/lib/drv/include/usbhc_iface.h
===================================================================
--- uspace/lib/drv/include/usbhc_iface.h	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/drv/include/usbhc_iface.h	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -85,16 +85,4 @@
  */
 typedef enum {
-	/** Tell USB address assigned to device.
-	 * Parameters:
-	 * - devman handle id
-	 * Answer:
-	 * - EINVAL - unknown handle or handle not managed by this driver
-	 * - ENOTSUP - operation not supported by HC (shall not happen)
-	 * - arbitrary error code if returned by remote implementation
-	 * - EOK - handle found, first parameter contains the USB address
-	 */
-	IPC_M_USBHC_GET_ADDRESS,
-
-
 	/** Reserve usage of default address.
 	 * This call informs the host controller that the caller will be
@@ -153,4 +141,14 @@
 	IPC_M_USBHC_INTERRUPT_IN,
 
+	/** Send bulk data to device.
+	 * See explanation at usb_iface_funcs_t (OUT transaction).
+	 */
+	IPC_M_USBHC_BULK_OUT,
+
+	/** Get bulk data from device.
+	 * See explanation at usb_iface_funcs_t (IN transaction).
+	 */
+	IPC_M_USBHC_BULK_IN,
+
 	/** Issue control WRITE transfer.
 	 * See explanation at usb_iface_funcs_t (OUT transaction) for
@@ -196,6 +194,4 @@
 /** USB host controller communication interface. */
 typedef struct {
-	int (*tell_address)(device_t *, devman_handle_t, usb_address_t *);
-
 	int (*reserve_default_address)(device_t *, usb_speed_t);
 	int (*release_default_address)(device_t *);
@@ -207,4 +203,7 @@
 	usbhc_iface_transfer_in_t interrupt_in;
 
+	usbhc_iface_transfer_out_t bulk_out;
+	usbhc_iface_transfer_in_t bulk_in;
+
 	int (*control_write)(device_t *, usb_target_t,
 	    size_t,
Index: uspace/lib/usb/Makefile
===================================================================
--- uspace/lib/usb/Makefile	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/usb/Makefile	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -35,4 +35,5 @@
 	src/addrkeep.c \
 	src/class.c \
+	src/ddfiface.c \
 	src/debug.c \
 	src/dp.c \
Index: uspace/lib/usb/include/usb/ddfiface.h
===================================================================
--- uspace/lib/usb/include/usb/ddfiface.h	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
+++ uspace/lib/usb/include/usb/ddfiface.h	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -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.
+ */
+
+/** @addtogroup libusb
+ * @{
+ */
+/** @file
+ * Implementations of DDF interfaces functions.
+ */
+#ifndef LIBUSB_DDFIFACE_H_
+#define LIBUSB_DDFIFACE_H_
+
+#include <sys/types.h>
+#include <usb/usbdevice.h>
+#include <usb_iface.h>
+
+int usb_iface_get_hc_handle_hub_impl(device_t *, devman_handle_t *);
+int usb_iface_get_address_hub_impl(device_t *, devman_handle_t,
+    usb_address_t *);
+extern usb_iface_t usb_iface_hub_impl;
+
+int usb_iface_get_hc_handle_hub_child_impl(device_t *, devman_handle_t *);
+int usb_iface_get_address_hub_child_impl(device_t *, devman_handle_t,
+    usb_address_t *);
+extern usb_iface_t usb_iface_hub_child_impl;
+
+int usb_iface_get_hc_handle_hc_impl(device_t *, devman_handle_t *);
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/usb/include/usb/dp.h
===================================================================
--- uspace/lib/usb/include/usb/dp.h	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/usb/include/usb/dp.h	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -45,4 +45,6 @@
 } usb_dp_descriptor_nesting_t;
 
+extern usb_dp_descriptor_nesting_t usb_dp_standard_descriptor_nesting[];
+
 typedef struct {
 	usb_dp_descriptor_nesting_t *nesting;
Index: uspace/lib/usb/include/usb/pipes.h
===================================================================
--- uspace/lib/usb/include/usb/pipes.h	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/usb/include/usb/pipes.h	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -107,4 +107,6 @@
 	/** Endpoint description. */
 	const usb_endpoint_description_t *description;
+	/** Interface number the endpoint must belong to (-1 for any). */
+	const int interface_no;
 	/** Found descriptor fitting the description. */
 	usb_standard_endpoint_descriptor_t *descriptor;
@@ -121,4 +123,6 @@
 int usb_device_connection_initialize(usb_device_connection_t *,
     devman_handle_t, usb_address_t);
+
+int usb_device_get_assigned_interface(device_t *);
 
 int usb_endpoint_pipe_initialize(usb_endpoint_pipe_t *,
Index: uspace/lib/usb/include/usb/recognise.h
===================================================================
--- uspace/lib/usb/include/usb/recognise.h	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/usb/include/usb/recognise.h	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -41,4 +41,8 @@
 #include <ipc/devman.h>
 
+int usb_device_create_match_ids_from_interface(
+    const usb_standard_device_descriptor_t *,
+    const usb_standard_interface_descriptor_t *, match_id_list_t *);
+
 int usb_device_create_match_ids(usb_endpoint_pipe_t *, match_id_list_t *);
 
Index: uspace/lib/usb/include/usb/usbdrv.h
===================================================================
--- uspace/lib/usb/include/usb/usbdrv.h	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/usb/include/usb/usbdrv.h	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -103,7 +103,4 @@
 int usb_drv_create_match_ids_from_device_descriptor(match_id_list_t *,
     const usb_standard_device_descriptor_t *);
-int usb_drv_create_match_ids_from_configuration_descriptor(match_id_list_t *,
-    const void *, size_t);
-
 
 #endif
Index: uspace/lib/usb/src/ddfiface.c
===================================================================
--- uspace/lib/usb/src/ddfiface.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
+++ uspace/lib/usb/src/ddfiface.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -0,0 +1,176 @@
+/*
+ * 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 libusb
+ * @{
+ */
+/** @file
+ * Implementations of DDF interfaces functions (actual implementation).
+ */
+#include <ipc/devman.h>
+#include <usb/ddfiface.h>
+#include <errno.h>
+
+/** DDF interface for USB device, implementation for typical hub. */
+usb_iface_t  usb_iface_hub_impl = {
+	.get_hc_handle = usb_iface_get_hc_handle_hub_impl,
+	.get_address = usb_iface_get_address_hub_impl
+};
+
+/** DDF interface for USB device, implementation for child of a typical hub. */
+usb_iface_t  usb_iface_hub_child_impl = {
+	.get_hc_handle = usb_iface_get_hc_handle_hub_child_impl,
+	.get_address = usb_iface_get_address_hub_child_impl
+};
+
+
+/** Get host controller handle, interface implementation for hub driver.
+ *
+ * @param[in] device Device the operation is running on.
+ * @param[out] handle Storage for the host controller handle.
+ * @return Error code.
+ */
+int usb_iface_get_hc_handle_hub_impl(device_t *device, devman_handle_t *handle)
+{
+	assert(device);
+	return usb_hc_find(device->handle, handle);
+}
+
+/** Get host controller handle, interface implementation for child of
+ * a hub driver.
+ *
+ * @param[in] device Device the operation is running on.
+ * @param[out] handle Storage for the host controller handle.
+ * @return Error code.
+ */
+int usb_iface_get_hc_handle_hub_child_impl(device_t *device,
+    devman_handle_t *handle)
+{
+	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_hc_handle) {
+			rc = usb_iface->get_hc_handle(parent, handle);
+		}
+	}
+
+	return rc;
+}
+
+/** Get host controller handle, interface implementation for HC driver.
+ *
+ * @param[in] device Device the operation is running on.
+ * @param[out] handle Storage for the host controller handle.
+ * @return Always EOK.
+ */
+int usb_iface_get_hc_handle_hc_impl(device_t *device, devman_handle_t *handle)
+{
+	assert(device);
+
+	if (handle != NULL) {
+		*handle = device->handle;
+	}
+
+	return EOK;
+}
+
+/** Get USB device address, interface implementation for hub driver.
+ *
+ * @param[in] device Device the operation is running on.
+ * @param[in] handle Devman handle of USB device we want address of.
+ * @param[out] address Storage for USB address of device with handle @p handle.
+ * @return Error code.
+ */
+int usb_iface_get_address_hub_impl(device_t *device, devman_handle_t handle,
+    usb_address_t *address)
+{
+	assert(device);
+	int parent_phone = devman_parent_device_connect(device->handle,
+	    IPC_FLAG_BLOCKING);
+	if (parent_phone < 0) {
+		return parent_phone;
+	}
+
+	sysarg_t addr;
+	int rc = async_req_2_1(parent_phone, DEV_IFACE_ID(USB_DEV_IFACE),
+	    IPC_M_USB_GET_ADDRESS, handle, &addr);
+
+	async_hangup(parent_phone);
+
+	if (rc != EOK) {
+		return rc;
+	}
+
+	if (address != NULL) {
+		*address = (usb_address_t) addr;
+	}
+
+	return EOK;
+}
+
+/** Get USB device address, interface implementation for child of
+ * a hub driver.
+ *
+ * @param[in] device Device the operation is running on.
+ * @param[in] handle Devman handle of USB device we want address of.
+ * @param[out] address Storage for USB address of device with handle @p handle.
+ * @return Error code.
+ */
+int usb_iface_get_address_hub_child_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, handle, address);
+		}
+	}
+
+	return rc;
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/usb/src/dp.c
===================================================================
--- uspace/lib/usb/src/dp.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/usb/src/dp.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -40,4 +40,25 @@
 #include <bool.h>
 #include <usb/dp.h>
+#include <usb/descriptor.h>
+
+#define NESTING(parentname, childname) \
+	{ \
+		.child = USB_DESCTYPE_##childname, \
+		.parent = USB_DESCTYPE_##parentname, \
+	}
+#define LAST_NESTING { -1, -1 }
+
+/** Nesting of standard USB descriptors. */
+usb_dp_descriptor_nesting_t usb_dp_standard_descriptor_nesting[] = {
+	NESTING(CONFIGURATION, INTERFACE),
+	NESTING(INTERFACE, ENDPOINT),
+	NESTING(INTERFACE, HUB),
+	NESTING(INTERFACE, HID),
+	NESTING(HID, HID_REPORT),
+	LAST_NESTING
+};
+
+#undef NESTING
+#undef LAST_NESTING
 
 /** Tells whether pointer points inside descriptor data.
Index: uspace/lib/usb/src/hub.c
===================================================================
--- uspace/lib/usb/src/hub.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/usb/src/hub.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -301,5 +301,4 @@
 }
 
-
 /**
  * @}
Index: uspace/lib/usb/src/pipes.c
===================================================================
--- uspace/lib/usb/src/pipes.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/usb/src/pipes.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -36,4 +36,5 @@
 #include <usb/pipes.h>
 #include <usbhc_iface.h>
+#include <usb_iface.h>
 #include <errno.h>
 #include <assert.h>
@@ -41,5 +42,5 @@
 /** Tell USB address assigned to given device.
  *
- * @param phone Phone to my HC.
+ * @param phone Phone to parent device.
  * @param dev Device in question.
  * @return USB address or error code.
@@ -48,6 +49,6 @@
 {
 	sysarg_t address;
-	int rc = async_req_2_1(phone, DEV_IFACE_ID(USBHC_DEV_IFACE),
-	    IPC_M_USBHC_GET_ADDRESS,
+	int rc = async_req_2_1(phone, DEV_IFACE_ID(USB_DEV_IFACE),
+	    IPC_M_USB_GET_ADDRESS,
 	    dev->handle, &address);
 
@@ -57,4 +58,31 @@
 
 	return (usb_address_t) address;
+}
+
+/** Tell USB interface assigned to given device.
+ *
+ * @param device Device in question.
+ * @return Interface number (negative code means any).
+ */
+int usb_device_get_assigned_interface(device_t *device)
+{
+	int parent_phone = devman_parent_device_connect(device->handle,
+	    IPC_FLAG_BLOCKING);
+	if (parent_phone < 0) {
+		return -1;
+	}
+
+	sysarg_t iface_no;
+	int rc = async_req_2_1(parent_phone, DEV_IFACE_ID(USB_DEV_IFACE),
+	    IPC_M_USB_GET_INTERFACE,
+	    device->handle, &iface_no);
+
+	async_hangup(parent_phone);
+
+	if (rc != EOK) {
+		return -1;
+	}
+
+	return (int) iface_no;
 }
 
@@ -80,10 +108,11 @@
 	}
 
-	int hc_phone = devman_device_connect(hc_handle, 0);
-	if (hc_phone < 0) {
-		return hc_phone;
-	}
-
-	my_address = get_my_address(hc_phone, device);
+	int parent_phone = devman_parent_device_connect(device->handle,
+	    IPC_FLAG_BLOCKING);
+	if (parent_phone < 0) {
+		return parent_phone;
+	}
+
+	my_address = get_my_address(parent_phone, device);
 	if (my_address < 0) {
 		rc = my_address;
@@ -95,5 +124,5 @@
 
 leave:
-	async_hangup(hc_phone);
+	async_hangup(parent_phone);
 	return rc;
 }
Index: uspace/lib/usb/src/pipesinit.c
===================================================================
--- uspace/lib/usb/src/pipesinit.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/usb/src/pipesinit.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -109,4 +109,5 @@
  * @param mapping_count Number of endpoint mappings in @p mapping.
  * @param found_endpoint Description of found endpoint.
+ * @param interface_number Number of currently processed interface.
  * @return Endpoint mapping corresponding to @p found_endpoint.
  * @retval NULL No corresponding endpoint found.
@@ -114,9 +115,15 @@
 static usb_endpoint_mapping_t *find_endpoint_mapping(
     usb_endpoint_mapping_t *mapping, size_t mapping_count,
-    usb_endpoint_description_t *found_endpoint)
+    usb_endpoint_description_t *found_endpoint,
+    int interface_number)
 {
 	while (mapping_count > 0) {
-		if (endpoint_fits_description(mapping->description,
-		    found_endpoint)) {
+		bool interface_number_fits = (mapping->interface_no < 0)
+		    || (mapping->interface_no == interface_number);
+
+		bool endpoint_descriptions_fits = endpoint_fits_description(
+		    mapping->description, found_endpoint);
+
+		if (interface_number_fits && endpoint_descriptions_fits) {
 			return mapping;
 		}
@@ -169,5 +176,5 @@
 	 */
 	usb_endpoint_mapping_t *ep_mapping = find_endpoint_mapping(mapping,
-	    mapping_count, &description);
+	    mapping_count, &description, interface->interface_number);
 	if (ep_mapping == NULL) {
 		return ENOENT;
Index: uspace/lib/usb/src/pipesio.c
===================================================================
--- uspace/lib/usb/src/pipesio.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/usb/src/pipesio.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -71,4 +71,7 @@
 			ipc_method = IPC_M_USBHC_INTERRUPT_IN;
 			break;
+		case USB_TRANSFER_BULK:
+			ipc_method = IPC_M_USBHC_BULK_IN;
+			break;
 		default:
 			return ENOTSUP;
@@ -194,4 +197,7 @@
 		case USB_TRANSFER_INTERRUPT:
 			ipc_method = IPC_M_USBHC_INTERRUPT_OUT;
+			break;
+		case USB_TRANSFER_BULK:
+			ipc_method = IPC_M_USBHC_BULK_OUT;
 			break;
 		default:
Index: uspace/lib/usb/src/recognise.c
===================================================================
--- uspace/lib/usb/src/recognise.c	(revision 62066b4b9236cbb36a30b4ca96c26e641093692c)
+++ uspace/lib/usb/src/recognise.c	(revision ace12560c8fb7ffd6badf7674cbb3f68e1f1cee8)
@@ -34,8 +34,8 @@
  */
 #include <sys/types.h>
-#include <usb_iface.h>
 #include <usb/usbdrv.h>
 #include <usb/pipes.h>
 #include <usb/recognise.h>
+#include <usb/ddfiface.h>
 #include <usb/request.h>
 #include <usb/classes/classes.h>
@@ -46,36 +46,6 @@
 static FIBRIL_MUTEX_INITIALIZE(device_name_index_mutex);
 
-/** Callback for getting host controller handle.
- *
- * @param dev Device in question.
- * @param[out] handle Devman handle of the host controller.
- * @return Error code.
- */
-static int usb_iface_get_hc_handle(device_t *dev, devman_handle_t *handle)
-{
-	assert(dev);
-	assert(dev->parent != NULL);
-
-	device_t *parent = dev->parent;
-
-	if (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_hc_handle) {
-			int rc = usb_iface->get_hc_handle(parent, handle);
-			return rc;
-		}
-	}
-
-	return ENOTSUP;
-}
-
-static usb_iface_t usb_iface = {
-	.get_hc_handle = usb_iface_get_hc_handle
-};
-
 device_ops_t child_ops = {
-	.interfaces[USB_DEV_IFACE] = &usb_iface
+	.interfaces[USB_DEV_IFACE] = &usb_iface_hub_child_impl
 };
 
@@ -142,4 +112,127 @@
 }
 
+#define ADD_MATCHID_OR_RETURN(match_ids, score, format, ...) \
+	do { \
+		int __rc = usb_add_match_id((match_ids), (score), \
+		    format, ##__VA_ARGS__); \
+		if (__rc != EOK) { \
+			return __rc; \
+		} \
+	} while (0)
+
+/** Create device match ids based on its interface.
+ *
+ * @param[in] descriptor Interface descriptor.
+ * @param[out] matches Initialized list of match ids.
+ * @return Error code (the two mentioned are not the only ones).
+ * @retval EINVAL Invalid input parameters (expects non NULL pointers).
+ * @retval ENOENT Interface does not specify class.
+ */
+int usb_device_create_match_ids_from_interface(
+    const usb_standard_device_descriptor_t *desc_device,
+    const usb_standard_interface_descriptor_t *desc_interface,
+    match_id_list_t *matches)
+{
+	if (desc_interface == NULL) {
+		return EINVAL;
+	}
+	if (matches == NULL) {
+		return EINVAL;
+	}
+
+	if (desc_interface->interface_class == USB_CLASS_USE_INTERFACE) {
+		return ENOENT;
+	}
+
+	const char *classname = usb_str_class(desc_interface->interface_class);
+	assert(classname != NULL);
+
+#define IFACE_PROTOCOL_FMT "interface&class=%s&subclass=0x%02x&protocol=0x%02x"
+#define IFACE_PROTOCOL_ARGS classname, desc_interface->interface_subclass, \
+    desc_interface->interface_protocol
+
+#define IFACE_SUBCLASS_FMT "interface&class=%s&subclass=0x%02x"
+#define IFACE_SUBCLASS_ARGS classname, desc_interface->interface_subclass
+
+#define IFACE_CLASS_FMT "interface&class=%s"
+#define IFACE_CLASS_ARGS classname
+
+#define VENDOR_RELEASE_FMT "vendor=0x%04x&product=0x%04x&release=" BCD_FMT
+#define VENDOR_RELEASE_ARGS desc_device->vendor_id, desc_device->product_id, \
+    BCD_ARGS(desc_device->device_version)
+
+#define VENDOR_PRODUCT_FMT "vendor=0x%04x&product=0x%04x"
+#define VENDOR_PRODUCT_ARGS desc_device->vendor_id, desc_device->product_id
+
+#define VENDOR_ONLY_FMT "vendor=0x%04x"
+#define VENDOR_ONLY_ARGS desc_device->vendor_id
+
+	/*
+	 * If the vendor is specified, create match ids with vendor with
+	 * higher score.
+	 * Then the same ones without the vendor part.
+	 */
+	if ((desc_device != NULL) && (desc_device->vendor_id != 0)) {
+		/* First, interface matches with device release number. */
+		ADD_MATCHID_OR_RETURN(matches, 250,
+		    "usb&" VENDOR_RELEASE_FMT "&" IFACE_PROTOCOL_FMT,
+		    VENDOR_RELEASE_ARGS, IFACE_PROTOCOL_ARGS);
+		ADD_MATCHID_OR_RETURN(matches, 240,
+		    "usb&" VENDOR_RELEASE_FMT "&" IFACE_SUBCLASS_FMT,
+		    VENDOR_RELEASE_ARGS, IFACE_SUBCLASS_ARGS);
+		ADD_MATCHID_OR_RETURN(matches, 230,
+		    "usb&" VENDOR_RELEASE_FMT "&" IFACE_CLASS_FMT,
+		    VENDOR_RELEASE_ARGS, IFACE_CLASS_ARGS);
+
+		/* Next, interface matches without release number. */
+		ADD_MATCHID_OR_RETURN(matches, 220,
+		    "usb&" VENDOR_PRODUCT_FMT "&" IFACE_PROTOCOL_FMT,
+		    VENDOR_PRODUCT_ARGS, IFACE_PROTOCOL_ARGS);
+		ADD_MATCHID_OR_RETURN(matches, 210,
+		    "usb&" VENDOR_PRODUCT_FMT "&" IFACE_SUBCLASS_FMT,
+		    VENDOR_PRODUCT_ARGS, IFACE_SUBCLASS_ARGS);
+		ADD_MATCHID_OR_RETURN(matches, 200,
+		    "usb&" VENDOR_PRODUCT_FMT "&" IFACE_CLASS_FMT,
+		    VENDOR_PRODUCT_ARGS, IFACE_CLASS_ARGS);
+
+		/* Finally, interface matches with only vendor. */
+		ADD_MATCHID_OR_RETURN(matches, 190,
+		    "usb&" VENDOR_ONLY_FMT "&" IFACE_PROTOCOL_FMT,
+		    VENDOR_ONLY_ARGS, IFACE_PROTOCOL_ARGS);
+		ADD_MATCHID_OR_RETURN(matches, 180,
+		    "usb&" VENDOR_ONLY_FMT "&" IFACE_SUBCLASS_FMT,
+		    VENDOR_ONLY_ARGS, IFACE_SUBCLASS_ARGS);
+		ADD_MATCHID_OR_RETURN(matches, 170,
+		    "usb&" VENDOR_ONLY_FMT "&" IFACE_CLASS_FMT,
+		    VENDOR_ONLY_ARGS, IFACE_CLASS_ARGS);
+	}
+
+	/* Now, the same but without any vendor specification. */
+	ADD_MATCHID_OR_RETURN(matches, 160,
+	    "usb&" IFACE_PROTOCOL_FMT,
+	    IFACE_PROTOCOL_ARGS);
+	ADD_MATCHID_OR_RETURN(matches, 150,
+	    "usb&" IFACE_SUBCLASS_FMT,
+	    IFACE_SUBCLASS_ARGS);
+	ADD_MATCHID_OR_RETURN(matches, 140,
+	    "usb&" IFACE_CLASS_FMT,
+	    IFACE_CLASS_ARGS);
+
+#undef IFACE_PROTOCOL_FMT
+#undef IFACE_PROTOCOL_ARGS
+#undef IFACE_SUBCLASS_FMT
+#undef IFACE_SUBCLASS_ARGS
+#undef IFACE_CLASS_FMT
+#undef IFACE_CLASS_ARGS
+#undef VENDOR_RELEASE_FMT
+#undef VENDOR_RELEASE_ARGS
+#undef VENDOR_PRODUCT_FMT
+#undef VENDOR_PRODUCT_ARGS
+#undef VENDOR_ONLY_FMT
+#undef VENDOR_ONLY_ARGS
+
+	return EOK;
+}
+
 /** Create DDF match ids from USB device descriptor.
  *
@@ -152,6 +245,4 @@
     const usb_standard_device_descriptor_t *device_descriptor)
 {
-	int rc;
-	
 	/*
 	 * Unless the vendor id is 0, the pair idVendor-idProduct
@@ -160,33 +251,26 @@
 	if (device_descriptor->vendor_id != 0) {
 		/* First, with release number. */
-		rc = usb_add_match_id(matches, 100,
+		ADD_MATCHID_OR_RETURN(matches, 100,
 		    "usb&vendor=0x%04x&product=0x%04x&release=" BCD_FMT,
 		    (int) device_descriptor->vendor_id,
 		    (int) device_descriptor->product_id,
 		    BCD_ARGS(device_descriptor->device_version));
-		if (rc != EOK) {
-			return rc;
-		}
 		
 		/* Next, without release number. */
-		rc = usb_add_match_id(matches, 90,
+		ADD_MATCHID_OR_RETURN(matches, 90,
 		    "usb&vendor=0x%04x&product=0x%04x",
 		    (int) device_descriptor->vendor_id,
 		    (int) device_descriptor->product_id);
-		if (rc != EOK) {
-			return rc;
-		}
 	}	
 
 	/*
 	 * If the device class points to interface we skip adding
-	 * class directly.
+	 * class directly but we add a multi interface device.
 	 */
 	if (device_descriptor->device_class != USB_CLASS_USE_INTERFACE) {
-		rc = usb_add_match_id(matches, 50, "usb&class=%s",
+		ADD_MATCHID_OR_RETURN(matches, 50, "usb&class=%s",
 		    usb_str_class(device_descriptor->device_class));
-		if (rc != EOK) {
-			return rc;
-		}
+	} else {
+		ADD_MATCHID_OR_RETURN(matches, 50, "usb&mid");
 	}
 	
@@ -194,108 +278,4 @@
 }
 
-/** Create DDF match ids from USB configuration descriptor.
- * The configuration descriptor is expected to be in the complete form,
- * i.e. including interface, endpoint etc. descriptors.
- *
- * @param matches List of match ids to extend.
- * @param config_descriptor Configuration descriptor returned by given device.
- * @param total_size Size of the @p config_descriptor.
- * @return Error code.
- */
-int usb_drv_create_match_ids_from_configuration_descriptor(
-    match_id_list_t *matches,
-    const void *config_descriptor, size_t total_size)
-{
-	/*
-	 * Iterate through config descriptor to find the interface
-	 * descriptors.
-	 */
-	size_t position = sizeof(usb_standard_configuration_descriptor_t);
-	while (position + 1 < total_size) {
-		uint8_t *current_descriptor
-		    = ((uint8_t *) config_descriptor) + position;
-		uint8_t cur_descr_len = current_descriptor[0];
-		uint8_t cur_descr_type = current_descriptor[1];
-
-		if (cur_descr_len == 0) {
-			return ENOENT;
-		}
-		
-		position += cur_descr_len;
-		
-		if (cur_descr_type != USB_DESCTYPE_INTERFACE) {
-			continue;
-		}
-		
-		/*
-		 * Finally, we found an interface descriptor.
-		 */
-		usb_standard_interface_descriptor_t *interface
-		    = (usb_standard_interface_descriptor_t *)
-		    current_descriptor;
-		
-		int rc = usb_add_match_id(matches, 50,
-		    "usb&interface&class=%s",
-		    usb_str_class(interface->interface_class));
-		if (rc != EOK) {
-			return rc;
-		}
-	}
-	
-	return EOK;
-}
-
-/** Add match ids based on configuration descriptor.
- *
- * @param pipe Control pipe to the device.
- * @param matches Match ids list to add matches to.
- * @param config_count Number of configurations the device has.
- * @return Error code.
- */
-static int usb_add_config_descriptor_match_ids(usb_endpoint_pipe_t *pipe,
-    match_id_list_t *matches, int config_count)
-{
-	int final_rc = EOK;
-	
-	int config_index;
-	for (config_index = 0; config_index < config_count; config_index++) {
-		int rc;
-		usb_standard_configuration_descriptor_t config_descriptor;
-		rc = usb_request_get_bare_configuration_descriptor(pipe,
-		    config_index, &config_descriptor);
-		if (rc != EOK) {
-			final_rc = rc;
-			continue;
-		}
-
-		size_t full_config_descriptor_size;
-		void *full_config_descriptor
-		    = malloc(config_descriptor.total_length);
-		rc = usb_request_get_full_configuration_descriptor(pipe,
-		    config_index,
-		    full_config_descriptor, config_descriptor.total_length,
-		    &full_config_descriptor_size);
-		if (rc != EOK) {
-			final_rc = rc;
-			continue;
-		}
-		if (full_config_descriptor_size
-		    != config_descriptor.total_length) {
-			final_rc = ERANGE;
-			continue;
-		}
-		
-		rc = usb_drv_create_match_ids_from_configuration_descriptor(
-		    matches,
-		    full_config_descriptor, full_config_descriptor_size);
-		if (rc != EOK) {
-			final_rc = rc;
-			continue;
-		}
-		
-	}
-	
-	return final_rc;
-}
 
 /** Create match ids describing attached device.
@@ -330,20 +310,7 @@
 
 	/*
-	 * Go through all configurations and add matches
-	 * based on interface class.
-	 */
-	rc = usb_add_config_descriptor_match_ids(ctrl_pipe, matches,
-	    device_descriptor.configuration_count);
-	if (rc != EOK) {
-		return rc;
-	}
-
-	/*
 	 * As a fallback, provide the simplest match id possible.
 	 */
-	rc = usb_add_match_id(matches, 1, "usb&fallback");
-	if (rc != EOK) {
-		return rc;
-	}
+	ADD_MATCHID_OR_RETURN(matches, 1, "usb&fallback");
 
 	return EOK;
