Index: .bzrignore
===================================================================
--- .bzrignore	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ .bzrignore	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -49,4 +49,5 @@
 ./uspace/app/killall/killall
 ./uspace/app/klog/klog
+./uspace/app/lsusb/lsusb
 ./uspace/app/mkfat/mkfat
 ./uspace/app/netstart/netstart
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ boot/Makefile.common	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -128,4 +128,5 @@
 	$(USPACE_PATH)/app/killall/killall \
 	$(USPACE_PATH)/app/mkfat/mkfat \
+	$(USPACE_PATH)/app/lsusb/lsusb \
 	$(USPACE_PATH)/app/sbi/sbi \
 	$(USPACE_PATH)/app/redir/redir \
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/Makefile	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -41,4 +41,5 @@
 	app/killall \
 	app/klog \
+	app/lsusb \
 	app/mkfat \
 	app/redir \
Index: uspace/app/lsusb/Makefile
===================================================================
--- uspace/app/lsusb/Makefile	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
+++ uspace/app/lsusb/Makefile	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2011 Vojtech Horky
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+BINARY = lsusb
+
+LIBS = $(LIBUSB_PREFIX)/libusb.a $(LIBDRV_PREFIX)/libdrv.a
+EXTRA_CFLAGS = -I$(LIBUSB_PREFIX)/include -I$(LIBDRV_PREFIX)/include
+
+SOURCES = \
+	main.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/lsusb/main.c
===================================================================
--- uspace/app/lsusb/main.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
+++ uspace/app/lsusb/main.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2010-2011 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup lsusb
+ * @{
+ */
+/**
+ * @file
+ * Listing of USB host controllers.
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <str_error.h>
+#include <bool.h>
+#include <getopt.h>
+#include <devman.h>
+#include <devmap.h>
+#include <usb/host.h>
+
+#define NAME "lsusb"
+
+#define MAX_FAILED_ATTEMPTS 4
+#define MAX_PATH_LENGTH 1024
+
+int main(int argc, char *argv[])
+{
+	size_t class_index = 0;
+	size_t failed_attempts = 0;
+
+	while (failed_attempts < MAX_FAILED_ATTEMPTS) {
+		class_index++;
+		devman_handle_t hc_handle = 0;
+		int rc = usb_ddf_get_hc_handle_by_class(class_index, &hc_handle);
+		if (rc != EOK) {
+			failed_attempts++;
+			continue;
+		}
+		char path[MAX_PATH_LENGTH];
+		rc = devman_get_device_path(hc_handle, path, MAX_PATH_LENGTH);
+		if (rc != EOK) {
+			continue;
+		}
+		printf(NAME ": host controller %zu is `%s'.\n",
+		    class_index, path);
+	}
+
+	return 0;
+}
+
+
+/** @}
+ */
Index: uspace/app/usbinfo/dev.c
===================================================================
--- uspace/app/usbinfo/dev.c	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/app/usbinfo/dev.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -40,6 +40,6 @@
 #include "usbinfo.h"
 
-usbinfo_device_t *prepare_device(devman_handle_t hc_handle,
-    usb_address_t dev_addr)
+usbinfo_device_t *prepare_device(const char *name,
+    devman_handle_t hc_handle, usb_address_t dev_addr)
 {
 	usbinfo_device_t *dev = malloc(sizeof(usbinfo_device_t));
@@ -55,6 +55,6 @@
 	if (rc != EOK) {
 		fprintf(stderr,
-		    NAME ": failed to create connection to the device: %s.\n",
-		    str_error(rc));
+		    NAME ": failed to create connection to device %s: %s.\n",
+		    name, str_error(rc));
 		goto leave;
 	}
@@ -64,6 +64,6 @@
 	if (rc != EOK) {
 		fprintf(stderr,
-		    NAME ": failed to create default control pipe: %s.\n",
-		    str_error(rc));
+		    NAME ": failed to create default control pipe to %s: %s.\n",
+		    name, str_error(rc));
 		goto leave;
 	}
@@ -71,7 +71,13 @@
 	rc = usb_pipe_probe_default_control(&dev->ctrl_pipe);
 	if (rc != EOK) {
-		fprintf(stderr,
-		    NAME ": probing default control pipe failed: %s.\n",
-		    str_error(rc));
+		if (rc == ENOENT) {
+			fprintf(stderr, NAME ": " \
+			    "device %s not present or malfunctioning.\n",
+			    name);
+		} else {
+			fprintf(stderr, NAME ": " \
+			    "probing default control pipe of %s failed: %s.\n",
+			    name, str_error(rc));
+		}
 		goto leave;
 	}
@@ -84,6 +90,6 @@
 	if (rc != EOK) {
 		fprintf(stderr,
-		    NAME ": failed to retrieve device descriptor: %s.\n",
-		    str_error(rc));
+		    NAME ": failed to retrieve device descriptor of %s: %s.\n",
+		    name, str_error(rc));
 		goto leave;
 	}
@@ -93,7 +99,7 @@
 	    &dev->full_configuration_descriptor_size);
 	if (rc != EOK) {
-		fprintf(stderr,
-		    NAME ": failed to retrieve configuration descriptor: %s.\n",
-		    str_error(rc));
+		fprintf(stderr, NAME ": " \
+		    "failed to retrieve configuration descriptor of %s: %s.\n",
+		    name, str_error(rc));
 		goto leave;
 	}
Index: uspace/app/usbinfo/main.c
===================================================================
--- uspace/app/usbinfo/main.c	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/app/usbinfo/main.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -45,5 +45,37 @@
 #include <usb/usbdevice.h>
 #include <usb/pipes.h>
+#include <usb/host.h>
 #include "usbinfo.h"
+
+static bool try_parse_class_and_address(const char *path,
+    devman_handle_t *out_hc_handle, usb_address_t *out_device_address)
+{
+	size_t class_index;
+	size_t address;
+	int rc;
+	char *ptr;
+
+	rc = str_size_t(path, &ptr, 10, false, &class_index);
+	if (rc != EOK) {
+		return false;
+	}
+	if ((*ptr == ':') || (*ptr == '.')) {
+		ptr++;
+	} else {
+		return false;
+	}
+	rc = str_size_t(ptr, NULL, 10, true, &address);
+	if (rc != EOK) {
+		return false;
+	}
+	rc = usb_ddf_get_hc_handle_by_class(class_index, out_hc_handle);
+	if (rc != EOK) {
+		return false;
+	}
+	if (out_device_address != NULL) {
+		*out_device_address = (usb_address_t) address;
+	}
+	return true;
+}
 
 static bool resolve_hc_handle_and_dev_addr(const char *devpath,
@@ -60,4 +92,9 @@
 	if (str_cmp(devpath, "virt") == 0) {
 		devpath = "/virt/usbhc/usb00_a1/usb00_a2";
+	}
+
+	if (try_parse_class_and_address(devpath,
+	    out_hc_handle, out_device_address)) {
+		return true;
 	}
 
@@ -271,5 +308,6 @@
 		}
 
-		usbinfo_device_t *dev = prepare_device(hc_handle, dev_addr);
+		usbinfo_device_t *dev = prepare_device(devpath,
+		    hc_handle, dev_addr);
 		if (dev == NULL) {
 			continue;
Index: uspace/app/usbinfo/usbinfo.h
===================================================================
--- uspace/app/usbinfo/usbinfo.h	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/app/usbinfo/usbinfo.h	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -71,5 +71,5 @@
 }
 
-usbinfo_device_t *prepare_device(devman_handle_t, usb_address_t);
+usbinfo_device_t *prepare_device(const char *, devman_handle_t, usb_address_t);
 void destroy_device(usbinfo_device_t *);
 
Index: uspace/doc/doxygroups.h
===================================================================
--- uspace/doc/doxygroups.h	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/doc/doxygroups.h	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -220,4 +220,12 @@
 
 	/**
+	 * @defgroup lsusb HelenOS version of lsusb command
+	 * @ingroup usb
+	 * @brief Application for listing USB host controllers.
+	 * @details
+	 * List all found host controllers.
+	 */
+
+	/**
 	 * @defgroup drvusbmid USB multi interface device driver
 	 * @ingroup usb
Index: uspace/drv/ehci-hcd/main.c
===================================================================
--- uspace/drv/ehci-hcd/main.c	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/drv/ehci-hcd/main.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -97,8 +97,12 @@
 	}
 	hc_fun->ops = &hc_ops;
+
 	ret = ddf_fun_bind(hc_fun);
-
 	CHECK_RET_RETURN(ret,
 	    "Failed to bind EHCI function: %s.\n",
+	    str_error(ret));
+	ret = ddf_fun_add_to_class(hc_fun, USB_HC_DDF_CLASS_NAME);
+	CHECK_RET_RETURN(ret,
+	    "Failed to add EHCI to HC class: %s.\n",
 	    str_error(ret));
 
Index: uspace/drv/ohci/ohci.c
===================================================================
--- uspace/drv/ohci/ohci.c	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/drv/ohci/ohci.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -230,4 +230,8 @@
 	    ret, str_error(ret));
 
+	ret = ddf_fun_add_to_class(instance->hc_fun, USB_HC_DDF_CLASS_NAME);
+	CHECK_RET_FINI_RETURN(ret,
+	    "Failed to add OHCI to HC class: %s.\n", str_error(ret));
+
 	device->driver_data = instance;
 
Index: uspace/drv/ohci/root_hub.c
===================================================================
--- uspace/drv/ohci/root_hub.c	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/drv/ohci/root_hub.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -45,5 +45,5 @@
 
 /**
- *	standart device descriptor for ohci root hub
+ * standart device descriptor for ohci root hub
  */
 static const usb_standard_device_descriptor_t ohci_rh_device_descriptor = {
@@ -69,5 +69,4 @@
  */
 static const usb_standard_configuration_descriptor_t ohci_rh_conf_descriptor = {
-	/// \TODO some values are default or guessed
 	.attributes = 1 << 7,
 	.configuration_number = 1,
@@ -87,5 +86,4 @@
 	.endpoint_count = 1,
 	.interface_class = USB_CLASS_HUB,
-	/// \TODO is this correct?
 	.interface_number = 1,
 	.interface_protocol = 0,
@@ -107,27 +105,44 @@
 };
 
+/**
+ * bitmask of hub features that are valid to be cleared
+ */
 static const uint32_t hub_clear_feature_valid_mask =
-	(1 << USB_HUB_FEATURE_C_HUB_LOCAL_POWER) |
+    (1 << USB_HUB_FEATURE_C_HUB_LOCAL_POWER) |
 (1 << USB_HUB_FEATURE_C_HUB_OVER_CURRENT);
 
+/**
+ * bitmask of hub features that are cleared by writing 1 (and not 0)
+ */
 static const uint32_t hub_clear_feature_by_writing_one_mask =
-	1 << USB_HUB_FEATURE_C_HUB_LOCAL_POWER;
-
+    1 << USB_HUB_FEATURE_C_HUB_LOCAL_POWER;
+
+/**
+ * bitmask of hub features that are valid to be set
+ */
 static const uint32_t hub_set_feature_valid_mask =
-	(1 << USB_HUB_FEATURE_C_HUB_OVER_CURRENT) |
+    (1 << USB_HUB_FEATURE_C_HUB_OVER_CURRENT) |
 (1 << USB_HUB_FEATURE_C_HUB_LOCAL_POWER);
 
-
+/**
+ * bitmask of hub features that are set by writing 1 and cleared by writing 0
+ */
 static const uint32_t hub_set_feature_direct_mask =
-	(1 << USB_HUB_FEATURE_C_HUB_OVER_CURRENT);
-
+    (1 << USB_HUB_FEATURE_C_HUB_OVER_CURRENT);
+
+/**
+ * bitmask of port features that are valid to be set
+ */
 static const uint32_t port_set_feature_valid_mask =
-	(1 << USB_HUB_FEATURE_PORT_ENABLE) |
+    (1 << USB_HUB_FEATURE_PORT_ENABLE) |
 (1 << USB_HUB_FEATURE_PORT_SUSPEND) |
 (1 << USB_HUB_FEATURE_PORT_RESET) |
 (1 << USB_HUB_FEATURE_PORT_POWER);
 
+/**
+ * bitmask of port features that can be cleared
+ */
 static const uint32_t port_clear_feature_valid_mask =
-	(1 << USB_HUB_FEATURE_PORT_CONNECTION) |
+    (1 << USB_HUB_FEATURE_PORT_CONNECTION) |
 (1 << USB_HUB_FEATURE_PORT_SUSPEND) |
 (1 << USB_HUB_FEATURE_PORT_OVER_CURRENT) |
@@ -141,10 +156,13 @@
 //USB_HUB_FEATURE_PORT_LOW_SPEED
 
+/**
+ * bitmask with port status changes
+ */
 static const uint32_t port_status_change_mask =
-(1<< USB_HUB_FEATURE_C_PORT_CONNECTION) |
-(1<< USB_HUB_FEATURE_C_PORT_ENABLE) |
-(1<< USB_HUB_FEATURE_C_PORT_OVER_CURRENT) |
-(1<< USB_HUB_FEATURE_C_PORT_RESET) |
-(1<< USB_HUB_FEATURE_C_PORT_SUSPEND);
+    (1 << USB_HUB_FEATURE_C_PORT_CONNECTION) |
+(1 << USB_HUB_FEATURE_C_PORT_ENABLE) |
+(1 << USB_HUB_FEATURE_C_PORT_OVER_CURRENT) |
+(1 << USB_HUB_FEATURE_C_PORT_RESET) |
+(1 << USB_HUB_FEATURE_C_PORT_SUSPEND);
 
 
@@ -154,42 +172,42 @@
 
 static int process_get_port_status_request(rh_t *instance, uint16_t port,
-	usb_transfer_batch_t * request);
+    usb_transfer_batch_t * request);
 
 static int process_get_hub_status_request(rh_t *instance,
-	usb_transfer_batch_t * request);
+    usb_transfer_batch_t * request);
 
 static int process_get_status_request(rh_t *instance,
-	usb_transfer_batch_t * request);
+    usb_transfer_batch_t * request);
 
 static void create_interrupt_mask_in_instance(rh_t *instance);
 
 static int process_get_descriptor_request(rh_t *instance,
-	usb_transfer_batch_t *request);
+    usb_transfer_batch_t *request);
 
 static int process_get_configuration_request(rh_t *instance,
-	usb_transfer_batch_t *request);
+    usb_transfer_batch_t *request);
 
 static int process_hub_feature_set_request(rh_t *instance, uint16_t feature);
 
 static int process_hub_feature_clear_request(rh_t *instance,
-	uint16_t feature);
+    uint16_t feature);
 
 static int process_port_feature_set_request(rh_t *instance,
-	uint16_t feature, uint16_t port);
+    uint16_t feature, uint16_t port);
 
 static int process_port_feature_clear_request(rh_t *instance,
-	uint16_t feature, uint16_t port);
+    uint16_t feature, uint16_t port);
 
 static int process_address_set_request(rh_t *instance,
-	uint16_t address);
+    uint16_t address);
 
 static int process_request_with_output(rh_t *instance,
-	usb_transfer_batch_t *request);
+    usb_transfer_batch_t *request);
 
 static int process_request_with_input(rh_t *instance,
-	usb_transfer_batch_t *request);
+    usb_transfer_batch_t *request);
 
 static int process_request_without_data(rh_t *instance,
-	usb_transfer_batch_t *request);
+    usb_transfer_batch_t *request);
 
 static int process_ctrl_request(rh_t *instance, usb_transfer_batch_t *request);
@@ -198,6 +216,4 @@
 
 static bool is_zeros(void * buffer, size_t size);
-
-
 
 /** Root hub initialization
@@ -210,5 +226,5 @@
 	    (instance->registers->rh_desc_a >> RHDA_NDS_SHIFT) & RHDA_NDS_MASK;
 	int opResult = rh_init_descriptors(instance);
-	if(opResult != EOK){
+	if (opResult != EOK) {
 		return opResult;
 	}
@@ -216,11 +232,12 @@
 	instance->registers->rh_desc_a |= RHDA_NPS_FLAG;
 	instance->unfinished_interrupt_transfer = NULL;
-	instance->interrupt_mask_size = (instance->port_count + 8)/8;
+	instance->interrupt_mask_size = (instance->port_count + 8) / 8;
 	instance->interrupt_buffer = malloc(instance->interrupt_mask_size);
-	if(!instance->interrupt_buffer)
+	if (!instance->interrupt_buffer)
 		return ENOMEM;
-	
-
-	usb_log_info("OHCI root hub with %d ports.\n", instance->port_count);
+
+	usb_log_info("OHCI root hub with %d ports initialized.\n",
+	    instance->port_count);
+
 	return EOK;
 }
@@ -245,10 +262,10 @@
 		usb_log_info("Root hub got INTERRUPT packet\n");
 		create_interrupt_mask_in_instance(instance);
-		if(is_zeros(instance->interrupt_buffer,
-		    instance->interrupt_mask_size)){
+		if (is_zeros(instance->interrupt_buffer,
+		    instance->interrupt_mask_size)) {
 			usb_log_debug("no changes..\n");
 			instance->unfinished_interrupt_transfer = request;
 			//will be finished later
-		}else{
+		} else {
 			usb_log_debug("processing changes..\n");
 			process_interrupt_mask_in_instance(instance, request);
@@ -256,4 +273,5 @@
 		opResult = EOK;
 	} else {
+
 		opResult = EINVAL;
 		usb_transfer_batch_finish_error(request, opResult);
@@ -271,5 +289,5 @@
  */
 void rh_interrupt(rh_t *instance) {
-	if(!instance->unfinished_interrupt_transfer){
+	if (!instance->unfinished_interrupt_transfer) {
 		return;
 	}
@@ -292,8 +310,8 @@
 static int create_serialized_hub_descriptor(rh_t *instance) {
 	size_t size = 7 +
-	    ((instance->port_count +7 )/ 8) * 2;
-	size_t var_size = (instance->port_count +7 )/ 8;
+	    ((instance->port_count + 7) / 8) * 2;
+	size_t var_size = (instance->port_count + 7) / 8;
 	uint8_t * result = (uint8_t*) malloc(size);
-	if(!result) return ENOMEM;
+	if (!result) return ENOMEM;
 
 	bzero(result, size);
@@ -305,19 +323,19 @@
 	uint32_t hub_desc_reg = instance->registers->rh_desc_a;
 	result[3] =
-		((hub_desc_reg >> 8) % 2) +
-		(((hub_desc_reg >> 9) % 2) << 1) +
-		(((hub_desc_reg >> 10) % 2) << 2) +
-		(((hub_desc_reg >> 11) % 2) << 3) +
-		(((hub_desc_reg >> 12) % 2) << 4);
+	    ((hub_desc_reg >> 8) % 2) +
+	    (((hub_desc_reg >> 9) % 2) << 1) +
+	    (((hub_desc_reg >> 10) % 2) << 2) +
+	    (((hub_desc_reg >> 11) % 2) << 3) +
+	    (((hub_desc_reg >> 12) % 2) << 4);
 	result[4] = 0;
 	result[5] = /*descriptor->pwr_on_2_good_time*/ 50;
 	result[6] = 50;
 
-	int port;
+	size_t port;
 	for (port = 1; port <= instance->port_count; ++port) {
 		uint8_t is_non_removable =
-			instance->registers->rh_desc_b >> port % 2;
+		    instance->registers->rh_desc_b >> port % 2;
 		result[7 + port / 8] +=
-			is_non_removable << (port % 8);
+		    is_non_removable << (port % 8);
 	}
 	size_t i;
@@ -327,4 +345,5 @@
 	instance->hub_descriptor = result;
 	instance->descriptor_size = size;
+
 	return EOK;
 }
@@ -340,38 +359,39 @@
 static int rh_init_descriptors(rh_t *instance) {
 	memcpy(&instance->descriptors.device, &ohci_rh_device_descriptor,
-		sizeof (ohci_rh_device_descriptor)
-		);
+	    sizeof (ohci_rh_device_descriptor)
+	    );
 	usb_standard_configuration_descriptor_t descriptor;
 	memcpy(&descriptor, &ohci_rh_conf_descriptor,
-		sizeof (ohci_rh_conf_descriptor));
+	    sizeof (ohci_rh_conf_descriptor));
 
 	int opResult = create_serialized_hub_descriptor(instance);
-	if(opResult != EOK){
+	if (opResult != EOK) {
 		return opResult;
 	}
 	descriptor.total_length =
-		sizeof (usb_standard_configuration_descriptor_t) +
-		sizeof (usb_standard_endpoint_descriptor_t) +
-		sizeof (usb_standard_interface_descriptor_t) +
-		instance->descriptor_size;
+	    sizeof (usb_standard_configuration_descriptor_t) +
+	    sizeof (usb_standard_endpoint_descriptor_t) +
+	    sizeof (usb_standard_interface_descriptor_t) +
+	    instance->descriptor_size;
 
 	uint8_t * full_config_descriptor =
-		(uint8_t*) malloc(descriptor.total_length);
-	if(!full_config_descriptor){
+	    (uint8_t*) malloc(descriptor.total_length);
+	if (!full_config_descriptor) {
 		return ENOMEM;
 	}
 	memcpy(full_config_descriptor, &descriptor, sizeof (descriptor));
 	memcpy(full_config_descriptor + sizeof (descriptor),
-		&ohci_rh_iface_descriptor, sizeof (ohci_rh_iface_descriptor));
+	    &ohci_rh_iface_descriptor, sizeof (ohci_rh_iface_descriptor));
 	memcpy(full_config_descriptor + sizeof (descriptor) +
-		sizeof (ohci_rh_iface_descriptor),
-		&ohci_rh_ep_descriptor, sizeof (ohci_rh_ep_descriptor));
+	    sizeof (ohci_rh_iface_descriptor),
+	    &ohci_rh_ep_descriptor, sizeof (ohci_rh_ep_descriptor));
 	memcpy(full_config_descriptor + sizeof (descriptor) +
-		sizeof (ohci_rh_iface_descriptor) +
-		sizeof (ohci_rh_ep_descriptor),
-		instance->hub_descriptor, instance->descriptor_size);
-	
+	    sizeof (ohci_rh_iface_descriptor) +
+	    sizeof (ohci_rh_ep_descriptor),
+	    instance->hub_descriptor, instance->descriptor_size);
+
 	instance->descriptors.configuration = full_config_descriptor;
 	instance->descriptors.configuration_size = descriptor.total_length;
+
 	return EOK;
 }
@@ -389,5 +409,5 @@
  */
 static int process_get_port_status_request(rh_t *instance, uint16_t port,
-	usb_transfer_batch_t * request) {
+    usb_transfer_batch_t * request) {
 	if (port < 1 || port > instance->port_count)
 		return EINVAL;
@@ -398,7 +418,8 @@
 	int i;
 	for (i = 0; i < instance->port_count; ++i) {
+
 		usb_log_debug("port status %d,x%x\n",
-			instance->registers->rh_port_status[i],
-			instance->registers->rh_port_status[i]);
+		    instance->registers->rh_port_status[i],
+		    instance->registers->rh_port_status[i]);
 	}
 #endif
@@ -417,5 +438,5 @@
  */
 static int process_get_hub_status_request(rh_t *instance,
-	usb_transfer_batch_t * request) {
+    usb_transfer_batch_t * request) {
 	uint32_t * uint32_buffer = (uint32_t*) request->data_buffer;
 	request->transfered_size = 4;
@@ -423,4 +444,5 @@
 	uint32_t mask = 1 | (1 << 1) | (1 << 16) | (1 << 17);
 	uint32_buffer[0] = mask & instance->registers->rh_status;
+
 	return EOK;
 }
@@ -437,9 +459,9 @@
  */
 static int process_get_status_request(rh_t *instance,
-	usb_transfer_batch_t * request) {
+    usb_transfer_batch_t * request) {
 	size_t buffer_size = request->buffer_size;
 	usb_device_request_setup_packet_t * request_packet =
-		(usb_device_request_setup_packet_t*)
-		request->setup_buffer;
+	    (usb_device_request_setup_packet_t*)
+	    request->setup_buffer;
 
 	usb_hub_bm_request_type_t request_type = request_packet->request_type;
@@ -453,6 +475,7 @@
 	if (request_type == USB_HUB_REQ_TYPE_GET_PORT_STATUS)
 		return process_get_port_status_request(instance,
-		request_packet->index,
-		request);
+	    request_packet->index,
+	    request);
+
 	return ENOTSUP;
 }
@@ -472,13 +495,14 @@
 	uint8_t * bitmap = (uint8_t*) (instance->interrupt_buffer);
 	uint32_t mask = (1 << (USB_HUB_FEATURE_C_HUB_LOCAL_POWER + 16))
-		| (1 << (USB_HUB_FEATURE_C_HUB_OVER_CURRENT + 16));
+	    | (1 << (USB_HUB_FEATURE_C_HUB_OVER_CURRENT + 16));
 	bzero(bitmap, instance->interrupt_mask_size);
 	if (instance->registers->rh_status & mask) {
 		bitmap[0] = 1;
 	}
-	int port;
+	size_t port;
 	mask = port_status_change_mask;
 	for (port = 1; port <= instance->port_count; ++port) {
 		if (mask & instance->registers->rh_port_status[port - 1]) {
+
 			bitmap[(port) / 8] += 1 << (port % 8);
 		}
@@ -497,7 +521,7 @@
  */
 static int process_get_descriptor_request(rh_t *instance,
-	usb_transfer_batch_t *request) {
+    usb_transfer_batch_t *request) {
 	usb_device_request_setup_packet_t * setup_request =
-		(usb_device_request_setup_packet_t*) request->setup_buffer;
+	    (usb_device_request_setup_packet_t*) request->setup_buffer;
 	size_t size;
 	const void * result_descriptor = NULL;
@@ -543,13 +567,13 @@
 		{
 			usb_log_debug("USB_DESCTYPE_EINVAL %d \n",
-				setup_request->value);
+			    setup_request->value);
 			usb_log_debug("\ttype %d\n\trequest %d\n\tvalue "
-				"%d\n\tindex %d\n\tlen %d\n ",
-				setup_request->request_type,
-				setup_request->request,
-				setup_request_value,
-				setup_request->index,
-				setup_request->length
-				);
+			    "%d\n\tindex %d\n\tlen %d\n ",
+			    setup_request->request_type,
+			    setup_request->request,
+			    setup_request_value,
+			    setup_request->index,
+			    setup_request->length
+			    );
 			return EINVAL;
 		}
@@ -560,4 +584,5 @@
 	request->transfered_size = size;
 	memcpy(request->data_buffer, result_descriptor, size);
+
 	return EOK;
 }
@@ -573,5 +598,5 @@
  */
 static int process_get_configuration_request(rh_t *instance,
-	usb_transfer_batch_t *request) {
+    usb_transfer_batch_t *request) {
 	//set and get configuration requests do not have any meaning, only dummy
 	//values are returned
@@ -580,4 +605,5 @@
 	request->data_buffer[0] = 1;
 	request->transfered_size = 1;
+
 	return EOK;
 }
@@ -592,12 +618,13 @@
  */
 static int process_hub_feature_set_request(rh_t *instance,
-	uint16_t feature) {
+    uint16_t feature) {
 	if (!((1 << feature) & hub_set_feature_valid_mask))
 		return EINVAL;
-	if(feature == USB_HUB_FEATURE_C_HUB_LOCAL_POWER)
+	if (feature == USB_HUB_FEATURE_C_HUB_LOCAL_POWER)
 		feature = USB_HUB_FEATURE_C_HUB_LOCAL_POWER << 16;
 	instance->registers->rh_status =
-		(instance->registers->rh_status | (1 << feature))
-		& (~hub_clear_feature_by_writing_one_mask);
+	    (instance->registers->rh_status | (1 << feature))
+	    & (~hub_clear_feature_by_writing_one_mask);
+
 	return EOK;
 }
@@ -612,5 +639,5 @@
  */
 static int process_hub_feature_clear_request(rh_t *instance,
-	uint16_t feature) {
+    uint16_t feature) {
 	if (!((1 << feature) & hub_clear_feature_valid_mask))
 		return EINVAL;
@@ -618,11 +645,12 @@
 	if ((1 << feature) & hub_set_feature_direct_mask) {
 		instance->registers->rh_status =
-			(instance->registers->rh_status & (~(1 << feature)))
-			& (~hub_clear_feature_by_writing_one_mask);
+		    (instance->registers->rh_status & (~(1 << feature)))
+		    & (~hub_clear_feature_by_writing_one_mask);
 	} else {//the feature is cleared by writing '1'
+
 		instance->registers->rh_status =
-			(instance->registers->rh_status
-			& (~hub_clear_feature_by_writing_one_mask))
-			| (1 << feature);
+		    (instance->registers->rh_status
+		    & (~hub_clear_feature_by_writing_one_mask))
+		    | (1 << feature);
 	}
 	return EOK;
@@ -640,5 +668,5 @@
  */
 static int process_port_feature_set_request(rh_t *instance,
-	uint16_t feature, uint16_t port) {
+    uint16_t feature, uint16_t port) {
 	if (!((1 << feature) & port_set_feature_valid_mask))
 		return EINVAL;
@@ -646,7 +674,8 @@
 		return EINVAL;
 	instance->registers->rh_port_status[port - 1] =
-		(instance->registers->rh_port_status[port - 1] | (1 << feature))
-		& (~port_clear_feature_valid_mask);
+	    (instance->registers->rh_port_status[port - 1] | (1 << feature))
+	    & (~port_clear_feature_valid_mask);
 	/// \TODO any error?
+
 	return EOK;
 }
@@ -663,5 +692,5 @@
  */
 static int process_port_feature_clear_request(rh_t *instance,
-	uint16_t feature, uint16_t port) {
+    uint16_t feature, uint16_t port) {
 	if (!((1 << feature) & port_clear_feature_valid_mask))
 		return EINVAL;
@@ -673,8 +702,9 @@
 		feature = USB_HUB_FEATURE_PORT_OVER_CURRENT;
 	instance->registers->rh_port_status[port - 1] =
-		(instance->registers->rh_port_status[port - 1]
-		& (~port_clear_feature_valid_mask))
-		| (1 << feature);
+	    (instance->registers->rh_port_status[port - 1]
+	    & (~port_clear_feature_valid_mask))
+	    | (1 << feature);
 	/// \TODO any error?
+
 	return EOK;
 }
@@ -689,6 +719,7 @@
  */
 static int process_address_set_request(rh_t *instance,
-	uint16_t address) {
+    uint16_t address) {
 	instance->address = address;
+
 	return EOK;
 }
@@ -705,7 +736,7 @@
  */
 static int process_request_with_output(rh_t *instance,
-	usb_transfer_batch_t *request) {
+    usb_transfer_batch_t *request) {
 	usb_device_request_setup_packet_t * setup_request =
-		(usb_device_request_setup_packet_t*) request->setup_buffer;
+	    (usb_device_request_setup_packet_t*) request->setup_buffer;
 	if (setup_request->request == USB_DEVREQ_GET_STATUS) {
 		usb_log_debug("USB_DEVREQ_GET_STATUS\n");
@@ -718,4 +749,5 @@
 	if (setup_request->request == USB_DEVREQ_GET_CONFIGURATION) {
 		usb_log_debug("USB_DEVREQ_GET_CONFIGURATION\n");
+
 		return process_get_configuration_request(instance, request);
 	}
@@ -734,7 +766,7 @@
  */
 static int process_request_with_input(rh_t *instance,
-	usb_transfer_batch_t *request) {
+    usb_transfer_batch_t *request) {
 	usb_device_request_setup_packet_t * setup_request =
-		(usb_device_request_setup_packet_t*) request->setup_buffer;
+	    (usb_device_request_setup_packet_t*) request->setup_buffer;
 	request->transfered_size = 0;
 	if (setup_request->request == USB_DEVREQ_SET_DESCRIPTOR) {
@@ -744,4 +776,5 @@
 		//set and get configuration requests do not have any meaning,
 		//only dummy values are returned
+
 		return EOK;
 	}
@@ -760,7 +793,7 @@
  */
 static int process_request_without_data(rh_t *instance,
-	usb_transfer_batch_t *request) {
+    usb_transfer_batch_t *request) {
 	usb_device_request_setup_packet_t * setup_request =
-		(usb_device_request_setup_packet_t*) request->setup_buffer;
+	    (usb_device_request_setup_packet_t*) request->setup_buffer;
 	request->transfered_size = 0;
 	if (setup_request->request == USB_DEVREQ_CLEAR_FEATURE) {
@@ -768,14 +801,14 @@
 			usb_log_debug("USB_HUB_REQ_TYPE_SET_HUB_FEATURE\n");
 			return process_hub_feature_clear_request(instance,
-				setup_request->value);
+			    setup_request->value);
 		}
 		if (setup_request->request_type == USB_HUB_REQ_TYPE_SET_PORT_FEATURE) {
 			usb_log_debug("USB_HUB_REQ_TYPE_SET_PORT_FEATURE\n");
 			return process_port_feature_clear_request(instance,
-				setup_request->value,
-				setup_request->index);
+			    setup_request->value,
+			    setup_request->index);
 		}
 		usb_log_debug("USB_HUB_REQ_TYPE_INVALID %d\n",
-			setup_request->request_type);
+		    setup_request->request_type);
 		return EINVAL;
 	}
@@ -784,14 +817,14 @@
 			usb_log_debug("USB_HUB_REQ_TYPE_SET_HUB_FEATURE\n");
 			return process_hub_feature_set_request(instance,
-				setup_request->value);
+			    setup_request->value);
 		}
 		if (setup_request->request_type == USB_HUB_REQ_TYPE_SET_PORT_FEATURE) {
 			usb_log_debug("USB_HUB_REQ_TYPE_SET_PORT_FEATURE\n");
 			return process_port_feature_set_request(instance,
-				setup_request->value,
-				setup_request->index);
+			    setup_request->value,
+			    setup_request->index);
 		}
 		usb_log_debug("USB_HUB_REQ_TYPE_INVALID %d\n",
-			setup_request->request_type);
+		    setup_request->request_type);
 		return EINVAL;
 	}
@@ -799,8 +832,9 @@
 		usb_log_debug("USB_DEVREQ_SET_ADDRESS\n");
 		return process_address_set_request(instance,
-			setup_request->value);
+		    setup_request->value);
 	}
 	usb_log_debug("USB_DEVREQ_SET_ENOTSUP %d\n",
-		setup_request->request_type);
+	    setup_request->request_type);
+
 	return ENOTSUP;
 }
@@ -836,9 +870,9 @@
 	}
 	usb_log_info("CTRL packet: %s.\n",
-		usb_debug_str_buffer(
-		(const uint8_t *) request->setup_buffer, 8, 8));
+	    usb_debug_str_buffer(
+	    (const uint8_t *) request->setup_buffer, 8, 8));
 	usb_device_request_setup_packet_t * setup_request =
-		(usb_device_request_setup_packet_t*)
-		request->setup_buffer;
+	    (usb_device_request_setup_packet_t*)
+	    request->setup_buffer;
 	switch (setup_request->request) {
 		case USB_DEVREQ_GET_STATUS:
@@ -847,5 +881,5 @@
 			usb_log_debug("processing request with output\n");
 			opResult = process_request_with_output(
-				instance, request);
+			    instance, request);
 			break;
 		case USB_DEVREQ_CLEAR_FEATURE:
@@ -853,20 +887,21 @@
 		case USB_DEVREQ_SET_ADDRESS:
 			usb_log_debug("processing request without "
-				"additional data\n");
+			    "additional data\n");
 			opResult = process_request_without_data(
-				instance, request);
+			    instance, request);
 			break;
 		case USB_DEVREQ_SET_DESCRIPTOR:
 		case USB_DEVREQ_SET_CONFIGURATION:
 			usb_log_debug("processing request with "
-				"input\n");
+			    "input\n");
 			opResult = process_request_with_input(
-				instance, request);
+			    instance, request);
+
 			break;
 		default:
 			usb_log_warning("received unsuported request: "
-				"%d\n",
-				setup_request->request
-				);
+			    "%d\n",
+			    setup_request->request
+			    );
 			opResult = ENOTSUP;
 	}
@@ -888,5 +923,5 @@
  * @return
  */
-static int process_interrupt_mask_in_instance(rh_t *instance, usb_transfer_batch_t * request){
+static int process_interrupt_mask_in_instance(rh_t *instance, usb_transfer_batch_t * request) {
 	memcpy(request->data_buffer, instance->interrupt_buffer,
 	    instance->interrupt_mask_size);
@@ -894,4 +929,5 @@
 	instance->unfinished_interrupt_transfer = NULL;
 	usb_transfer_batch_finish_error(request, EOK);
+
 	return EOK;
 }
@@ -907,10 +943,10 @@
  * @return
  */
-static bool is_zeros(void * buffer, size_t size){
-	if(!buffer) return true;
-	if(!size) return true;
+static bool is_zeros(void * buffer, size_t size) {
+	if (!buffer) return true;
+	if (!size) return true;
 	size_t i;
-	for(i=0;i<size;++i){
-		if(((char*)buffer)[i])
+	for (i = 0; i < size; ++i) {
+		if (((char*) buffer)[i])
 			return false;
 	}
Index: uspace/drv/ohci/root_hub.h
===================================================================
--- uspace/drv/ohci/root_hub.h	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/drv/ohci/root_hub.h	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -51,5 +51,5 @@
 	usb_address_t address;
 	/** hub port count */
-	int port_count;
+	size_t port_count;
 	/** hubs descriptors */
 	usb_device_descriptors_t descriptors;
Index: uspace/drv/uhci-hcd/uhci.c
===================================================================
--- uspace/drv/uhci-hcd/uhci.c	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/drv/uhci-hcd/uhci.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -264,4 +264,8 @@
 	    ret, str_error(ret));
 
+	ret = ddf_fun_add_to_class(instance->hc_fun, USB_HC_DDF_CLASS_NAME);
+	CHECK_RET_DEST_FUN_RETURN(ret,
+	    "Failed to add UHCI to HC class: %s.\n", str_error(ret));
+
 	ret = rh_init(&instance->rh, instance->rh_fun,
 	    (uintptr_t)instance->hc.registers + 0x10, 4);
Index: uspace/drv/usbhub/ports.c
===================================================================
--- uspace/drv/usbhub/ports.c	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/drv/usbhub/ports.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -53,4 +53,19 @@
 	size_t port;
 	usb_speed_t speed;
+};
+
+/**
+ * count of port status changes that are not explicitly handled by
+ * any function here and must be cleared by hand
+ */
+static const unsigned int non_handled_changes_count = 2;
+
+/**
+ * port status changes that are not explicitly handled by
+ * any function here and must be cleared by hand
+ */
+static const int non_handled_changes[] =  {
+	USB_HUB_FEATURE_C_PORT_ENABLE,
+	USB_HUB_FEATURE_C_PORT_SUSPEND
 };
 
@@ -131,14 +146,34 @@
 	    &status, USB_HUB_FEATURE_C_PORT_CONNECTION,false);
 	usb_port_status_set_bit(
-	    &status, USB_HUB_FEATURE_PORT_RESET,false);
-	usb_port_status_set_bit(
 	    &status, USB_HUB_FEATURE_C_PORT_RESET,false);
 	usb_port_status_set_bit(
 	    &status, USB_HUB_FEATURE_C_PORT_OVER_CURRENT,false);
-	/// \TODO what about port power change?
-	if (status >> 16) {
-		usb_log_info("there was unsupported change on port %d: %X\n",
-			port, status);
-
+	
+	//clearing not yet handled changes	
+	unsigned int feature_idx;
+	for(feature_idx = 0;feature_idx<non_handled_changes_count;
+	    ++feature_idx){
+		unsigned int bit_idx = non_handled_changes[feature_idx];
+		if(status & (1<<bit_idx)){
+			usb_log_info(
+			    "there was not yet handled change on port %d: %d"
+			    ";clearing it\n",
+			port, bit_idx);
+			int opResult = usb_hub_clear_port_feature(
+			    hub->control_pipe,
+			    port, bit_idx);
+			if (opResult != EOK) {
+				usb_log_warning(
+				    "could not clear port flag %d: %d\n",
+				    bit_idx, opResult
+				    );
+			}
+			usb_port_status_set_bit(
+			    &status, bit_idx,false);
+		}
+	}
+	if(status>>16){
+		usb_log_info("there is still some unhandled change %X\n",
+		    status);
 	}
 }
@@ -222,4 +257,11 @@
 		    "Port %zu reset complete but port not enabled.\n",
 		    (size_t) port);
+	}
+	/* Clear the port reset change. */
+	int rc = usb_hub_clear_port_feature(hub->control_pipe,
+	    port, USB_HUB_FEATURE_C_PORT_RESET);
+	if (rc != EOK) {
+		usb_log_error("Failed to clear port %d reset feature: %s.\n",
+		    port, str_error(rc));
 	}
 }
@@ -319,13 +361,4 @@
 	fibril_mutex_unlock(&my_port->reset_mutex);
 
-	/* Clear the port reset change. */
-	rc = usb_hub_clear_port_feature(hub->control_pipe,
-	    port_no, USB_HUB_FEATURE_C_PORT_RESET);
-	if (rc != EOK) {
-		usb_log_error("Failed to clear port %d reset feature: %s.\n",
-		    port_no, str_error(rc));
-		return rc;
-	}
-
 	if (my_port->reset_okay) {
 		return EOK;
Index: uspace/drv/vhc/main.c
===================================================================
--- uspace/drv/vhc/main.c	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/drv/vhc/main.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -104,5 +104,11 @@
 	}
 
-	ddf_fun_add_to_class(hc, "usbhc");
+	rc = ddf_fun_add_to_class(hc, USB_HC_DDF_CLASS_NAME);
+	if (rc != EOK) {
+		usb_log_fatal("Failed to add function to HC class: %s.\n",
+		    str_error(rc));
+		free(data);
+		return rc;
+	}
 
 	virtual_hub_device_init(hc);
Index: uspace/lib/c/generic/devman.c
===================================================================
--- uspace/lib/c/generic/devman.c	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/lib/c/generic/devman.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -374,4 +374,54 @@
 }
 
+int devman_get_device_path(devman_handle_t handle, char *path, size_t path_size)
+{
+	int phone = devman_get_phone(DEVMAN_CLIENT, 0);
+
+	if (phone < 0)
+		return phone;
+
+	async_serialize_start();
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(phone, DEVMAN_DEVICE_GET_DEVICE_PATH,
+	    handle, &answer);
+
+	ipc_call_t data_request_call;
+	aid_t data_request = async_data_read(phone, path, path_size,
+	    &data_request_call);
+	if (data_request == 0) {
+		async_wait_for(req, NULL);
+		async_serialize_end();
+		return ENOMEM;
+	}
+
+	sysarg_t data_request_rc;
+	sysarg_t opening_request_rc;
+	async_wait_for(data_request, &data_request_rc);
+	async_wait_for(req, &opening_request_rc);
+
+	async_serialize_end();
+
+	if (data_request_rc != EOK) {
+		/* Prefer the return code of the opening request. */
+		if (opening_request_rc != EOK) {
+			return (int) opening_request_rc;
+		} else {
+			return (int) data_request_rc;
+		}
+	}
+	if (opening_request_rc != EOK) {
+		return (int) opening_request_rc;
+	}
+
+	path[path_size - 1] = 0;
+
+	if (IPC_GET_ARG2(data_request_call) >= path_size) {
+		return ELIMIT;
+	}
+
+	return EOK;
+}
+
 
 /** @}
Index: uspace/lib/c/include/devman.h
===================================================================
--- uspace/lib/c/include/devman.h	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/lib/c/include/devman.h	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -55,4 +55,5 @@
 extern int devman_device_get_handle_by_class(const char *, const char *,
     devman_handle_t *, unsigned int);
+extern int devman_get_device_path(devman_handle_t, char *, size_t);
 
 extern int devman_add_device_to_class(devman_handle_t, const char *);
Index: uspace/lib/c/include/ipc/devman.h
===================================================================
--- uspace/lib/c/include/ipc/devman.h	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/lib/c/include/ipc/devman.h	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -149,5 +149,6 @@
 typedef enum {
 	DEVMAN_DEVICE_GET_HANDLE = IPC_FIRST_USER_METHOD,
-	DEVMAN_DEVICE_GET_HANDLE_BY_CLASS
+	DEVMAN_DEVICE_GET_HANDLE_BY_CLASS,
+	DEVMAN_DEVICE_GET_DEVICE_PATH
 } client_to_devman_t;
 
Index: uspace/lib/drv/generic/remote_usbhc.c
===================================================================
--- uspace/lib/drv/generic/remote_usbhc.c	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/lib/drv/generic/remote_usbhc.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -302,4 +302,5 @@
 	async_transaction_t *trans = async_transaction_create(callid);
 	if (trans == NULL) {
+		async_answer_0(data_callid, ENOMEM);
 		async_answer_0(callid, ENOMEM);
 		return;
@@ -314,4 +315,5 @@
 
 	if (rc != EOK) {
+		async_answer_0(data_callid, rc);
 		async_answer_0(callid, rc);
 		async_transaction_destroy(trans);
@@ -460,4 +462,5 @@
 	async_transaction_t *trans = async_transaction_create(callid);
 	if (trans == NULL) {
+		async_answer_0(data_callid, ENOMEM);
 		async_answer_0(callid, ENOMEM);
 		free(setup_packet);
@@ -469,4 +472,5 @@
 	trans->buffer = malloc(data_len);
 	if (trans->buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
 		async_answer_0(callid, ENOMEM);
 		async_transaction_destroy(trans);
@@ -480,4 +484,5 @@
 
 	if (rc != EOK) {
+		async_answer_0(data_callid, rc);
 		async_answer_0(callid, rc);
 		async_transaction_destroy(trans);
Index: uspace/lib/usb/Makefile
===================================================================
--- uspace/lib/usb/Makefile	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/lib/usb/Makefile	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -46,4 +46,5 @@
 	src/hidparser.c \
 	src/hiddescriptor.c \
+	src/host.c \
 	src/hub.c \
 	src/pipepriv.c \
Index: uspace/lib/usb/include/usb/host.h
===================================================================
--- uspace/lib/usb/include/usb/host.h	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
+++ uspace/lib/usb/include/usb/host.h	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libusb
+ * @{
+ */
+/** @file
+ * Host controller common functions.
+ */
+#ifndef LIBUSB_HOST_H_
+#define LIBUSB_HOST_H_
+
+#include <sys/types.h>
+#include <ipc/devman.h>
+
+int usb_ddf_get_hc_handle_by_class(size_t, devman_handle_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/usb/include/usb/usb.h
===================================================================
--- uspace/lib/usb/include/usb/usb.h	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/lib/usb/include/usb/usb.h	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -172,4 +172,7 @@
 } usb_packet_id;
 
+/** Class name for USB host controllers. */
+#define USB_HC_DDF_CLASS_NAME "usbhc"
+
 #endif
 /**
Index: uspace/lib/usb/src/host.c
===================================================================
--- uspace/lib/usb/src/host.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
+++ uspace/lib/usb/src/host.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -0,0 +1,78 @@
+/*
+ * 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
+ * Host controller common functions (implementation).
+ */
+#include <stdio.h>
+#include <str_error.h>
+#include <errno.h>
+#include <assert.h>
+#include <bool.h>
+#include <usb/host.h>
+#include <usb/descriptor.h>
+#include <devman.h>
+
+/** Get host controller handle by its class index.
+ *
+ * @param class_index Class index for the host controller.
+ * @param hc_handle Where to store the HC handle
+ *	(can be NULL for existence test only).
+ * @return Error code.
+ */
+int usb_ddf_get_hc_handle_by_class(size_t class_index,
+    devman_handle_t *hc_handle)
+{
+	char *class_index_str;
+	devman_handle_t hc_handle_tmp;
+	int rc;
+
+	rc = asprintf(&class_index_str, "%zu", class_index);
+	if (rc < 0) {
+		return ENOMEM;
+	}
+	rc = devman_device_get_handle_by_class("usbhc", class_index_str,
+	    &hc_handle_tmp, 0);
+	free(class_index_str);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	if (hc_handle != NULL) {
+		*hc_handle = hc_handle_tmp;
+	}
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision d2bff2f14a4df1b66541e1f560f9eba303514e2d)
+++ uspace/srv/devman/main.c	(revision 76ef94ea25d247bb9e3370cecc09a2f2d05f0979)
@@ -515,4 +515,41 @@
 }
 
+/** Find device path by its handle. */
+static void devman_get_device_path_by_handle(ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*icall);
+
+	fun_node_t *fun = find_fun_node(&device_tree, handle);
+	if (fun == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	ipc_callid_t data_callid;
+	size_t data_len;
+	if (!async_data_read_receive(&data_callid, &data_len)) {
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	void *buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	size_t sent_length = str_size(fun->pathname);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, fun->pathname, sent_length);
+	async_answer_0(iid, EOK);
+
+	free(buffer);
+}
+
 
 /** Function for handling connections from a client to the device manager. */
@@ -536,4 +573,7 @@
 		case DEVMAN_DEVICE_GET_HANDLE_BY_CLASS:
 			devman_function_get_handle_by_class(callid, &call);
+			break;
+		case DEVMAN_DEVICE_GET_DEVICE_PATH:
+			devman_get_device_path_by_handle(callid, &call);
 			break;
 		default:
