Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -81,4 +81,5 @@
 	app/vlaunch \
 	app/vterm \
+	app/df \
 	app/wavplay \
 	app/websrv \
Index: uspace/app/devctl/devctl.c
===================================================================
--- uspace/app/devctl/devctl.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/app/devctl/devctl.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -35,4 +35,5 @@
 #include <devman.h>
 #include <errno.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -44,6 +45,28 @@
 #define MAX_NAME_LENGTH 1024
 
-char name[MAX_NAME_LENGTH];
-char drv_name[MAX_NAME_LENGTH];
+static char name[MAX_NAME_LENGTH];
+static char drv_name[MAX_NAME_LENGTH];
+static bool verbose = false;
+
+static const char *drv_state_str(driver_state_t state)
+{
+	const char *sstate;
+
+	switch (state) {
+	case DRIVER_NOT_STARTED:
+		sstate = "not started";
+		break;
+	case DRIVER_STARTING:
+		sstate = "starting";
+		break;
+	case DRIVER_RUNNING:
+		sstate = "running";
+		break;
+	default:
+		sstate = "unknown";
+	}
+
+	return sstate;
+}
 
 static int fun_subtree_print(devman_handle_t funh, int lvl)
@@ -52,4 +75,5 @@
 	devman_handle_t *cfuns;
 	size_t count, i;
+	unsigned int score;
 	int rc;
 	int j;
@@ -74,4 +98,18 @@
 		printf("%s : %s\n", name, drv_name);
 
+	if (verbose) {
+		for (i = 0; true; i++) {
+			rc = devman_fun_get_match_id(funh, i, name, MAX_NAME_LENGTH,
+			    &score);
+			if (rc != EOK)
+				break;
+
+			for (j = 0; j < lvl; j++)
+				printf("    ");
+
+			printf("    %u %s\n", score, name);
+		}
+	}
+
 	rc = devman_fun_get_child(funh, &devh);
 	if (rc == ENOENT)
@@ -159,7 +197,137 @@
 }
 
+static int drv_list(void)
+{
+	devman_handle_t *devs;
+	devman_handle_t *drvs;
+	driver_state_t state;
+	const char *sstate;
+	size_t ndrvs;
+	size_t ndevs;
+	size_t i;
+	int rc;
+
+	rc = devman_get_drivers(&drvs, &ndrvs);
+	if (rc != EOK)
+		return rc;
+
+	for (i = 0; i < ndrvs; i++) {
+		devs = NULL;
+
+		rc = devman_driver_get_name(drvs[i], drv_name, MAX_NAME_LENGTH);
+		if (rc != EOK)
+			goto skip;
+		rc = devman_driver_get_state(drvs[i], &state);
+		if (rc != EOK)
+			goto skip;
+		rc = devman_driver_get_devices(drvs[i], &devs, &ndevs);
+		if (rc != EOK)
+			goto skip;
+
+		sstate = drv_state_str(state);
+
+		printf("%-11s %3zu %s\n", sstate, ndevs, drv_name);
+skip:
+		free(devs);
+	}
+	free(drvs);
+
+	return EOK;
+}
+
+static int drv_show(char *drvname)
+{
+	devman_handle_t *devs;
+	devman_handle_t drvh;
+	devman_handle_t funh;
+	driver_state_t state;
+	const char *sstate;
+	unsigned int score;
+	size_t ndevs;
+	size_t i;
+	int rc;
+
+	rc = devman_driver_get_handle(drvname, &drvh);
+	if (rc != EOK)
+		return rc;
+
+	devs = NULL;
+
+	rc = devman_driver_get_name(drvh, drv_name, MAX_NAME_LENGTH);
+	if (rc != EOK)
+		return rc;
+
+	rc = devman_driver_get_state(drvh, &state);
+	if (rc != EOK)
+		return rc;
+
+	rc = devman_driver_get_devices(drvh, &devs, &ndevs);
+	if (rc != EOK)
+		return rc;
+
+	sstate = drv_state_str(state);
+
+	printf("Driver: %s\n", drv_name);
+	printf("State: %s\n", sstate);
+
+	printf("Attached devices:\n");
+
+	for (i = 0; i < ndevs; i++) {
+		rc = devman_dev_get_parent(devs[i], &funh);
+		if (rc != EOK)
+			goto error;
+
+		rc = devman_fun_get_path(funh, name, MAX_NAME_LENGTH);
+		if (rc != EOK)
+			goto error;
+		printf("\t%s\n", name);
+	}
+
+	printf("Match IDs:\n");
+
+	for (i = 0; true; i++) {
+		rc = devman_driver_get_match_id(drvh, i, name, MAX_NAME_LENGTH,
+		    &score);
+		if (rc != EOK)
+			break;
+
+		printf("\t%u %s\n", score, name);
+	}
+
+error:
+	free(devs);
+
+	return EOK;
+}
+
+static int drv_load(const char *drvname)
+{
+	int rc;
+	devman_handle_t drvh;
+
+	rc = devman_driver_get_handle(drvname, &drvh);
+	if (rc != EOK) {
+		printf("Failed resolving driver '%s' (%d).\n", drvname, rc);
+		return rc;
+	}
+
+	rc = devman_driver_load(drvh);
+	if (rc != EOK) {
+		printf("Failed loading driver '%s' (%d).\n", drvname, rc);
+		return rc;
+	}
+
+	return EOK;
+}
+
 static void print_syntax(void)
 {
-	printf("syntax: devctl [(online|offline) <function>]\n");
+	printf("syntax:\n");
+	printf("\tdevctl\n");
+	printf("\tdevctl online <function>]\n");
+	printf("\tdevctl offline <function>]\n");
+	printf("\tdevctl list-drv\n");
+	printf("\tdevctl show-drv <driver-name>\n");
+	printf("\tdevctl load-drv <driver-name>\n");
 }
 
@@ -168,5 +336,14 @@
 	int rc;
 
-	if (argc == 1) {
+	if (argc == 1 || argv[1][0] == '-') {
+		if (argc > 1) {
+			if (str_cmp(argv[1], "-v") == 0) {
+				verbose = true;
+			} else {
+				printf(NAME ": Invalid argument '%s'\n", argv[1]);
+				print_syntax();
+				return 1;
+			}
+		}
 		rc = fun_tree_print();
 		if (rc != EOK)
@@ -194,4 +371,29 @@
 			return 2;
 		}
+	} else if (str_cmp(argv[1], "list-drv") == 0) {
+		rc = drv_list();
+		if (rc != EOK)
+			return 2;
+	} else if (str_cmp(argv[1], "show-drv") == 0) {
+		if (argc < 3) {
+			printf(NAME ": Argument missing.\n");
+			print_syntax();
+			return 1;
+		}
+
+		rc = drv_show(argv[2]);
+		if (rc != EOK) {
+			return 2;
+		}
+	} else if (str_cmp(argv[1], "load-drv") == 0) {
+		if (argc < 3) {
+			printf(NAME ": Argument missing.\n");
+			print_syntax();
+			return 1;
+		}
+
+		rc = drv_load(argv[2]);
+		if (rc != EOK)
+			return 2;
 	} else {
 		printf(NAME ": Invalid argument '%s'.\n", argv[1]);
Index: uspace/app/df/Makefile
===================================================================
--- uspace/app/df/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/app/df/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2013 Manuele Conti
+# 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 = df
+
+SOURCES = \
+	df.c 
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/df/df.c
===================================================================
--- uspace/app/df/df.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/app/df/df.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2013 Manuele Conti
+ * 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 df
+ * @brief Df utility.
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <sys/statfs.h>
+#include <errno.h>
+#include <adt/list.h>
+#include <vfs/vfs.h>
+
+#define NAME  "df"
+
+#define HEADER_TABLE 	"Filesystem     %4u-blocks           Used      Available Used%% Mounted on"
+#define HEADER_TABLE_HR "Filesystem           Size           Used      Available Used%% Mounted on"
+
+#define PERCENTAGE(x, tot) ((unsigned long long) (100L * (x) / (tot)))
+#define FSBK_TO_BK(x, fsbk, bk) \
+	(((fsbk) != 0 && (fsbk) < (bk)) ? \
+		(unsigned long long) ((x) / ((bk) / (fsbk))) : \
+		(unsigned long long) ((x) * ((fsbk) / (bk))))
+
+static unsigned int unit_size;
+static unsigned int human_readable;
+
+static int size_to_human_readable(char buf[], uint64_t bytes);
+static void print_header(void);
+static void print_statfs(struct statfs *, char *, char *);
+static void print_usage(void);
+
+int main(int argc, char *argv[])
+{
+	int optres, errflg = 0;
+	struct statfs st;
+
+	unit_size = 512;
+	human_readable = 0;
+
+	/******************************************/
+	/*   Parse command line options...        */
+	/******************************************/
+	while ((optres = getopt(argc, argv, ":uhb:")) != -1) {
+		switch(optres) {
+		case 'u':
+			print_usage();
+			return 0;
+
+		case 'h':
+			human_readable = 1;
+			break;
+
+		case 'b':
+			str_uint32_t(optarg, NULL, 0, 0, &unit_size);
+			break;
+ 
+		case ':':
+			fprintf(stderr, "Option -%c requires an operand\n", optopt);
+			errflg++;
+			break;
+
+		case '?':
+			fprintf(stderr, "Unrecognized option: -%c\n", optopt);
+			errflg++;
+			break;
+
+		default:
+			fprintf(stderr, "Unknown error while parsing command line options.");
+			errflg++;
+			break;
+		}
+	}
+
+	if (optind > argc) {
+		fprintf(stderr, "Too many input parameter\n");
+		errflg++;
+	}
+
+	if (errflg) {
+		print_usage();
+		return 1;
+	}
+
+	LIST_INITIALIZE(mtab_list);
+	get_mtab_list(&mtab_list);
+	print_header();
+	list_foreach(mtab_list, link, mtab_ent_t, mtab_ent) {
+		statfs(mtab_ent->mp, &st);
+		print_statfs(&st, mtab_ent->fs_name, mtab_ent->mp);
+	}
+	putchar('\n');
+	return 0;
+}
+
+static int size_to_human_readable(char buf[], uint64_t bytes)
+{
+	const char *units = "BkMGTPEZY";
+	int i = 0;
+	int limit;
+
+	limit = str_length(units);
+	while (bytes >= 1024) {
+		if (i >= limit) 
+			return -1;
+		bytes /= 1024;
+		i++;
+	}
+	snprintf(buf, 6, "%4llu%c", (unsigned long long)bytes, units[i]);
+
+	return 0;
+}
+
+static void print_header(void)
+{
+	if (human_readable)
+		printf(HEADER_TABLE_HR);
+	else
+		printf(HEADER_TABLE, unit_size);
+	putchar('\n');
+}
+
+static void print_statfs(struct statfs *st, char *name, char *mountpoint)
+{
+	printf("%10s", name);
+
+	if (human_readable) {
+		char tmp[1024];
+		size_to_human_readable(tmp, st->f_blocks *  st->f_bsize);
+		printf(" %14s", tmp);                                                                  /* Size       */
+		size_to_human_readable(tmp, (st->f_blocks - st->f_bfree)  *  st->f_bsize);
+		printf(" %14s", tmp);                                                                  /* Used       */
+		size_to_human_readable(tmp, st->f_bfree *  st->f_bsize);
+		printf(" %14s", tmp);                                                                  /* Available  */
+		printf(" %4llu%% %s\n", 
+			(st->f_blocks)?PERCENTAGE(st->f_blocks - st->f_bfree, st->f_blocks):0L,        /* Used%      */
+			mountpoint                                                                     /* Mounted on */
+		);
+	}
+	else
+		printf(" %15llu %14llu %14llu %4llu%% %s\n", 
+			FSBK_TO_BK(st->f_blocks, st->f_bsize, unit_size),                              /* Blocks     */
+			FSBK_TO_BK(st->f_blocks - st->f_bfree, st->f_bsize, unit_size),                /* Used       */
+			FSBK_TO_BK(st->f_bfree, st->f_bsize, unit_size),                               /* Available  */
+			(st->f_blocks)?PERCENTAGE(st->f_blocks - st->f_bfree, st->f_blocks):0L,        /* Used%      */
+			mountpoint                                                                     /* Mounted on */
+		);
+
+}
+
+static void print_usage(void)
+{
+	printf("syntax: %s [-u] [-h] [-b <size>] \n", NAME);
+	printf("  u : Show usage.\n");
+	printf("  h : \"Human-readable\" output.\n");
+	printf("  b : Scale block sizes by selected size.\n");
+	printf("\n");
+}
+
+/** @}
+ */
Index: uspace/app/trace/trace.c
===================================================================
--- uspace/app/trace/trace.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/app/trace/trace.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -724,4 +724,6 @@
 	o = oper_new("stat", 0, arg_def, V_ERRNO, 0, resp_def);
 	proto_add_oper(p, VFS_IN_STAT, o);
+	o = oper_new("statfs", 0, arg_def, V_ERRNO, 0, resp_def);
+	proto_add_oper(p, VFS_IN_STATFS, o);
 
 	proto_register(SERVICE_VFS, p);
Index: uspace/drv/bus/isa/i8237.c
===================================================================
--- uspace/drv/bus/isa/i8237.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/drv/bus/isa/i8237.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -279,4 +279,5 @@
  *
  * @return Error code.
+ *
  */
 static inline int dma_controller_init(dma_controller_t *controller)
Index: uspace/drv/bus/isa/isa.c
===================================================================
--- uspace/drv/bus/isa/isa.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/drv/bus/isa/isa.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -67,4 +67,5 @@
 
 #include <device/hw_res.h>
+#include <device/pio_window.h>
 
 #include "i8237.h"
@@ -79,4 +80,5 @@
 	ddf_dev_t *dev;
 	ddf_fun_t *fctl;
+	pio_window_t pio_win;
 	list_t functions;
 } isa_bus_t;
@@ -405,7 +407,10 @@
 	hw_resource_t *resources = fun->hw_resources.resources;
 
+	isa_bus_t *isa = isa_bus(ddf_fun_get_dev(fun->fnode));
+
 	if (count < ISA_MAX_HW_RES) {
 		resources[count].type = IO_RANGE;
 		resources[count].res.io_range.address = addr;
+		resources[count].res.io_range.address += isa->pio_win.io.base;
 		resources[count].res.io_range.size = len;
 		resources[count].res.io_range.endianness = LITTLE_ENDIAN;
@@ -604,4 +609,7 @@
 static int isa_dev_add(ddf_dev_t *dev)
 {
+	async_sess_t *sess;
+	int rc;
+
 	ddf_msg(LVL_DEBUG, "isa_dev_add, device handle = %d",
 	    (int) ddf_dev_get_handle(dev));
@@ -614,4 +622,18 @@
 	isa->dev = dev;
 	list_initialize(&isa->functions);
+
+	sess = ddf_dev_parent_sess_create(dev, EXCHANGE_SERIALIZE);
+	if (sess == NULL) {
+		ddf_msg(LVL_ERROR, "isa_dev_add failed to connect to the "
+		    "parent driver.");
+		return ENOENT;
+	}
+
+	rc = pio_window_get(sess, &isa->pio_win);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "isa_dev_add failed to get PIO window "
+		    "for the device.");
+		return rc;
+	}	
 
 	/* Make the bus device more visible. Does not do anything. */
Index: uspace/drv/bus/pci/pciintel/pci.c
===================================================================
--- uspace/drv/bus/pci/pciintel/pci.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/drv/bus/pci/pciintel/pci.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -57,4 +57,6 @@
 #include <ops/hw_res.h>
 #include <device/hw_res.h>
+#include <ops/pio_window.h>
+#include <device/pio_window.h>
 #include <ddi.h>
 #include <pci_dev_iface.h>
@@ -141,4 +143,14 @@
 }
 
+static pio_window_t *pciintel_get_pio_window(ddf_fun_t *fnode)
+{
+	pci_fun_t *fun = pci_fun(fnode);
+	
+	if (fun == NULL)
+		return NULL;
+	return &fun->pio_window;
+}
+
+
 static int pci_config_space_write_32(ddf_fun_t *fun, uint32_t address,
     uint32_t data)
@@ -198,4 +210,8 @@
 	.get_resource_list = &pciintel_get_resources,
 	.enable_interrupt = &pciintel_enable_interrupt,
+};
+
+static pio_window_ops_t pciintel_pio_window_ops = {
+	.get_pio_window = &pciintel_get_pio_window
 };
 
@@ -211,4 +227,5 @@
 static ddf_dev_ops_t pci_fun_ops = {
 	.interfaces[HW_RES_DEV_IFACE] = &pciintel_hw_res_ops,
+	.interfaces[PIO_WINDOW_DEV_IFACE] = &pciintel_pio_window_ops,
 	.interfaces[PCI_DEV_IFACE] = &pci_dev_ops
 };
@@ -552,5 +569,7 @@
 {
 	uint8_t irq = pci_conf_read_8(fun, PCI_BRIDGE_INT_LINE);
-	if (irq != 0xff)
+	uint8_t pin = pci_conf_read_8(fun, PCI_BRIDGE_INT_PIN);
+
+	if (pin != 0 && irq != 0xff)
 		pci_add_interrupt(fun, irq);
 }
@@ -615,4 +634,7 @@
 			pci_read_bars(fun);
 			pci_read_interrupt(fun);
+
+			/* Propagate the PIO window to the function. */
+			fun->pio_window = bus->pio_win;
 			
 			ddf_fun_set_ops(fun->fnode, &pci_fun_ops);
@@ -645,4 +667,5 @@
 static int pci_dev_add(ddf_dev_t *dnode)
 {
+	hw_resource_list_t hw_resources;
 	pci_bus_t *bus = NULL;
 	ddf_fun_t *ctl = NULL;
@@ -670,6 +693,11 @@
 		goto fail;
 	}
-	
-	hw_resource_list_t hw_resources;
+
+	rc = pio_window_get(sess, &bus->pio_win);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "pci_dev_add failed to get PIO window "
+		    "for the device.");
+		goto fail;
+	}
 	
 	rc = hw_res_get_resource_list(sess, &hw_resources);
@@ -761,6 +789,4 @@
 {
 	ddf_log_init(NAME);
-	pci_fun_ops.interfaces[HW_RES_DEV_IFACE] = &pciintel_hw_res_ops;
-	pci_fun_ops.interfaces[PCI_DEV_IFACE] = &pci_dev_ops;
 }
 
Index: uspace/drv/bus/pci/pciintel/pci.h
===================================================================
--- uspace/drv/bus/pci/pciintel/pci.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/drv/bus/pci/pciintel/pci.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -40,5 +40,5 @@
 #include "pci_regs.h"
 
-#define PCI_MAX_HW_RES 8
+#define PCI_MAX_HW_RES 10 
 
 typedef struct pciintel_bus {
@@ -49,4 +49,5 @@
 	void *conf_data_port;
 	void *conf_addr_port;
+	pio_window_t pio_win;
 	fibril_mutex_t conf_mutex;
 } pci_bus_t;
@@ -68,4 +69,5 @@
 	hw_resource_list_t hw_resources;
 	hw_resource_t resources[PCI_MAX_HW_RES];
+	pio_window_t pio_window;
 } pci_fun_t;
 
Index: uspace/drv/infrastructure/rootmalta/rootmalta.c
===================================================================
--- uspace/drv/infrastructure/rootmalta/rootmalta.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/drv/infrastructure/rootmalta/rootmalta.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -53,4 +53,6 @@
 #include <ops/hw_res.h>
 #include <device/hw_res.h>
+#include <ops/pio_window.h>
+#include <device/pio_window.h>
 #include <byteorder.h>
 
@@ -66,6 +68,13 @@
 #define GT_PCI_CMD_MBYTESWAP	0x1
 
+#define GT_PCI_MEMBASE	UINT32_C(0x10000000)
+#define GT_PCI_MEMSIZE	UINT32_C(0x08000000)
+
+#define GT_PCI_IOBASE	UINT32_C(0x18000000)
+#define GT_PCI_IOSIZE	UINT32_C(0x00200000)
+
 typedef struct rootmalta_fun {
 	hw_resource_list_t hw_resources;
+	pio_window_t pio_window;
 } rootmalta_fun_t;
 
@@ -107,4 +116,14 @@
 		sizeof(pci_conf_regs) / sizeof(pci_conf_regs[0]),
 		pci_conf_regs
+	},
+	.pio_window = {
+		.mem = {
+			.base = GT_PCI_MEMBASE,
+			.size = GT_PCI_MEMSIZE
+		},
+		.io = {
+			.base = GT_PCI_IOBASE,
+			.size = GT_PCI_IOSIZE
+		}
 	}
 };
@@ -129,4 +148,12 @@
 	
 	return false;
+}
+
+static pio_window_t *rootmalta_get_pio_window(ddf_fun_t *fnode)
+{
+	rootmalta_fun_t *fun = rootmalta_fun(fnode);
+
+	assert(fun != NULL);
+	return &fun->pio_window;
 }
 
@@ -134,4 +161,8 @@
 	.get_resource_list = &rootmalta_get_resources,
 	.enable_interrupt = &rootmalta_enable_interrupt,
+};
+
+static pio_window_ops_t fun_pio_window_ops = {
+	.get_pio_window = &rootmalta_get_pio_window
 };
 
@@ -228,4 +259,5 @@
 	ddf_log_init(NAME);
 	rootmalta_fun_ops.interfaces[HW_RES_DEV_IFACE] = &fun_hw_res_ops;
+	rootmalta_fun_ops.interfaces[PIO_WINDOW_DEV_IFACE] = &fun_pio_window_ops;
 }
 
Index: uspace/drv/infrastructure/rootpc/rootpc.c
===================================================================
--- uspace/drv/infrastructure/rootpc/rootpc.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/drv/infrastructure/rootpc/rootpc.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -51,4 +51,6 @@
 #include <ops/hw_res.h>
 #include <device/hw_res.h>
+#include <ops/pio_window.h>
+#include <device/pio_window.h>
 
 #define NAME "rootpc"
@@ -56,4 +58,5 @@
 typedef struct rootpc_fun {
 	hw_resource_list_t hw_resources;
+	pio_window_t pio_window;
 } rootpc_fun_t;
 
@@ -93,6 +96,16 @@
 static rootpc_fun_t pci_data = {
 	.hw_resources = {
-		sizeof(pci_conf_regs)/sizeof(pci_conf_regs[0]),
+		sizeof(pci_conf_regs) / sizeof(pci_conf_regs[0]),
 		pci_conf_regs
+	},
+	.pio_window = {
+		.mem = {
+			.base = UINT32_C(0),
+			.size = UINT32_C(0xffffffff) /* practical maximum */
+		},
+		.io = {
+			.base = UINT32_C(0),
+			.size = UINT32_C(0x10000)
+		}
 	}
 };
@@ -117,4 +130,12 @@
 	
 	return false;
+}
+
+static pio_window_t *rootpc_get_pio_window(ddf_fun_t *fnode)
+{
+	rootpc_fun_t *fun = rootpc_fun(fnode);
+	
+	assert(fun != NULL);
+	return &fun->pio_window;
 }
 
@@ -122,4 +143,8 @@
 	.get_resource_list = &rootpc_get_resources,
 	.enable_interrupt = &rootpc_enable_interrupt,
+};
+
+static pio_window_ops_t fun_pio_window_ops = {
+	.get_pio_window = &rootpc_get_pio_window
 };
 
@@ -197,4 +222,5 @@
 	ddf_log_init(NAME);
 	rootpc_fun_ops.interfaces[HW_RES_DEV_IFACE] = &fun_hw_res_ops;
+	rootpc_fun_ops.interfaces[PIO_WINDOW_DEV_IFACE] = &fun_pio_window_ops;
 }
 
Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -69,4 +69,5 @@
 	generic/device/hw_res.c \
 	generic/device/hw_res_parsed.c \
+	generic/device/pio_window.c \
 	generic/device/char_dev.c \
 	generic/device/clock_dev.c \
Index: uspace/lib/c/generic/device/hw_res.c
===================================================================
--- uspace/lib/c/generic/device/hw_res.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/generic/device/hw_res.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -44,6 +44,5 @@
 	
 	async_exch_t *exch = async_exchange_begin(sess);
-	if (exch == NULL)
-		return ENOMEM;
+	
 	int rc = async_req_1_1(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
 	    HW_RES_GET_RESOURCE_LIST, &count);
@@ -79,6 +78,5 @@
 {
 	async_exch_t *exch = async_exchange_begin(sess);
-	if (exch == NULL)
-		return false;
+	
 	int rc = async_req_1_0(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
 	    HW_RES_ENABLE_INTERRUPT);
@@ -88,16 +86,17 @@
 }
 
-/**
- * Setup DMA channel to specified place and mode.
- * @param channel DMA Channel 1,2,3 for 8 bit transfers, 5,6,7 for 16 bit.
- * @param pa Physical address of the buffer. Must be < 16MB for 16 bit and < 1MB
- *           for 8 bit transfers.
- * @param size DMA buffer size, limited to 64K.
- * @param mode Mode of the DMA channel:
- *              - Read or Write
- *              - Allow automatic reset
- *              - Use address decrement instead of increment
- *              - Use SINGLE/BLOCK/ON DEMAND transfer mode
+/** Setup DMA channel to specified place and mode.
+ *
+ * @param channel DMA channel.
+ * @param pa      Physical address of the buffer.
+ * @param size    DMA buffer size.
+ * @param mode    Mode of the DMA channel:
+ *                 - Read or Write
+ *                 - Allow automatic reset
+ *                 - Use address decrement instead of increment
+ *                 - Use SINGLE/BLOCK/ON DEMAND transfer mode
+ *
  * @return Error code.
+ *
  */
 int hw_res_dma_channel_setup(async_sess_t *sess,
@@ -105,30 +104,35 @@
 {
 	async_exch_t *exch = async_exchange_begin(sess);
-	if (exch == NULL)
-		return ENOMEM;
+	
 	const uint32_t packed = (channel & 0xffff) | (mode << 16);
 	const int ret = async_req_4_0(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
 	    HW_RES_DMA_CHANNEL_SETUP, packed, pa, size);
+	
 	async_exchange_end(exch);
-
+	
 	return ret;
 }
 
-/**
- * Query remaining bytes in the buffer.
- * @param channel DMA Channel 1,2,3 for 8 bit transfers, 5,6,7 for 16 bit.
- * @return Number of bytes remaining in the buffer(>=0) or error code(<0).
+/** Query remaining bytes in the buffer.
+ *
+ * @param channel DMA channel.
+ *
+ * @return Number of bytes remaining in the buffer if positive.
+ * @return Error code if negative.
+ *
  */
 int hw_res_dma_channel_remain(async_sess_t *sess, unsigned channel)
 {
 	async_exch_t *exch = async_exchange_begin(sess);
-	if (exch == NULL)
-		return ENOMEM;
+	
 	sysarg_t remain;
 	const int ret = async_req_2_1(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
 	    HW_RES_DMA_CHANNEL_REMAIN, channel, &remain);
+	
 	async_exchange_end(exch);
+	
 	if (ret == EOK)
 		return remain;
+	
 	return ret;
 }
Index: uspace/lib/c/generic/device/pio_window.c
===================================================================
--- uspace/lib/c/generic/device/pio_window.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/lib/c/generic/device/pio_window.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2013 Jakub Jermar 
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <device/pio_window.h>
+#include <errno.h>
+#include <async.h>
+
+int pio_window_get(async_sess_t *sess, pio_window_t *pio_win)
+{
+	int rc;
+
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (!exch)
+		return ENOMEM;
+
+	rc = async_req_1_0(exch, DEV_IFACE_ID(PIO_WINDOW_DEV_IFACE),
+	    PIO_WINDOW_GET);
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		return rc;
+	}
+	
+	rc = async_data_read_start(exch, pio_win, sizeof(*pio_win));
+	async_exchange_end(exch);
+	
+	return rc;
+}
+
+/** @}
+ */
+
Index: uspace/lib/c/generic/devman.c
===================================================================
--- uspace/lib/c/generic/devman.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/generic/devman.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2007 Josef Cejka
- * Copyright (c) 2011 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
  * Copyright (c) 2010 Lenka Trochtova
  * All rights reserved.
@@ -401,6 +401,6 @@
 }
 
-static int devman_get_str_internal(sysarg_t method, sysarg_t arg1, char *buf,
-    size_t buf_size)
+static int devman_get_str_internal(sysarg_t method, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t *r1, char *buf, size_t buf_size)
 {
 	async_exch_t *exch;
@@ -412,5 +412,5 @@
 	
 	ipc_call_t answer;
-	aid_t req = async_send_1(exch, method, arg1, &answer);
+	aid_t req = async_send_2(exch, method, arg1, arg2, &answer);
 	aid_t dreq = async_data_read(exch, buf, buf_size - 1, &dreply);
 	async_wait_for(dreq, &dretval);
@@ -430,4 +430,6 @@
 	}
 	
+	if (r1 != NULL)
+		*r1 = IPC_GET_ARG1(answer);
 	act_size = IPC_GET_ARG2(dreply);
 	assert(act_size <= buf_size - 1);
@@ -439,18 +441,33 @@
 int devman_fun_get_path(devman_handle_t handle, char *buf, size_t buf_size)
 {
-	return devman_get_str_internal(DEVMAN_FUN_GET_PATH, handle, buf,
-	    buf_size);
+	return devman_get_str_internal(DEVMAN_FUN_GET_PATH, handle, 0, NULL,
+	    buf, buf_size);
+}
+
+int devman_fun_get_match_id(devman_handle_t handle, size_t index, char *buf,
+    size_t buf_size, unsigned int *rscore)
+{
+	int rc;
+	sysarg_t score = 0;
+
+	rc = devman_get_str_internal(DEVMAN_FUN_GET_MATCH_ID, handle, index,
+	    &score, buf, buf_size);
+	if (rc != EOK)
+		return rc;
+
+	*rscore = score;
+	return rc;
 }
 
 int devman_fun_get_name(devman_handle_t handle, char *buf, size_t buf_size)
 {
-	return devman_get_str_internal(DEVMAN_FUN_GET_NAME, handle, buf,
-	    buf_size);
+	return devman_get_str_internal(DEVMAN_FUN_GET_NAME, handle, 0, NULL,
+	    buf, buf_size);
 }
 
 int devman_fun_get_driver_name(devman_handle_t handle, char *buf, size_t buf_size)
 {
-	return devman_get_str_internal(DEVMAN_FUN_GET_DRIVER_NAME, handle, buf,
-	    buf_size);
+	return devman_get_str_internal(DEVMAN_FUN_GET_DRIVER_NAME, handle, 0,
+	    NULL, buf, buf_size);
 }
 
@@ -579,4 +596,17 @@
 }
 
+int devman_dev_get_parent(devman_handle_t devh, devman_handle_t *funh)
+{
+	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	sysarg_t retval = async_req_1_1(exch, DEVMAN_DEV_GET_PARENT,
+	    devh, funh);
+	
+	devman_exchange_end(exch);
+	return (int) retval;
+}
+
 int devman_fun_sid_to_handle(service_id_t sid, devman_handle_t *handle)
 {
@@ -592,4 +622,103 @@
 }
 
+int devman_get_drivers(devman_handle_t **drvs,
+    size_t *count)
+{
+	return devman_get_handles_internal(DEVMAN_GET_DRIVERS, 0, drvs, count);
+}
+
+int devman_driver_get_devices(devman_handle_t drvh, devman_handle_t **devs,
+    size_t *count)
+{
+	return devman_get_handles_internal(DEVMAN_DRIVER_GET_DEVICES,
+	    drvh, devs, count);
+}
+
+int devman_driver_get_handle(const char *drvname, devman_handle_t *handle)
+{
+	async_exch_t *exch;
+
+	exch = devman_exchange_begin(DEVMAN_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, DEVMAN_DRIVER_GET_HANDLE, &answer);
+	sysarg_t retval = async_data_write_start(exch, drvname,
+	    str_size(drvname));
+	
+	devman_exchange_end(exch);
+	
+	if (retval != EOK) {
+		async_forget(req);
+		return retval;
+	}
+	
+	async_wait_for(req, &retval);
+	
+	if (retval != EOK) {
+		if (handle != NULL)
+			*handle = (devman_handle_t) -1;
+		
+		return retval;
+	}
+	
+	if (handle != NULL)
+		*handle = (devman_handle_t) IPC_GET_ARG1(answer);
+	
+	return retval;
+}
+
+int devman_driver_get_match_id(devman_handle_t handle, size_t index, char *buf,
+    size_t buf_size, unsigned int *rscore)
+{
+	int rc;
+	sysarg_t score = 0;
+
+	rc = devman_get_str_internal(DEVMAN_DRIVER_GET_MATCH_ID, handle, index,
+	    &score, buf, buf_size);
+	if (rc != EOK)
+		return rc;
+
+	*rscore = score;
+	return rc;
+}
+
+int devman_driver_get_name(devman_handle_t handle, char *buf, size_t buf_size)
+{
+	return devman_get_str_internal(DEVMAN_DRIVER_GET_NAME, handle, 0, NULL,
+	    buf, buf_size);
+}
+
+int devman_driver_get_state(devman_handle_t drvh, driver_state_t *rstate)
+{
+	sysarg_t state;
+	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	int rc = async_req_1_1(exch, DEVMAN_DRIVER_GET_STATE, drvh,
+	    &state);
+	
+	devman_exchange_end(exch);
+	if (rc != EOK)
+		return rc;
+
+	*rstate = state;
+	return rc;
+}
+
+int devman_driver_load(devman_handle_t drvh)
+{
+	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	int rc = async_req_1_0(exch, DEVMAN_DRIVER_LOAD, drvh);
+	
+	devman_exchange_end(exch);
+	return rc;
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/vfs/vfs.c
===================================================================
--- uspace/lib/c/generic/vfs/vfs.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/generic/vfs/vfs.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -43,4 +43,5 @@
 #include <stdio.h>
 #include <sys/stat.h>
+#include <sys/statfs.h>
 #include <sys/types.h>
 #include <ipc/services.h>
@@ -892,4 +893,43 @@
 }
 
+int statfs(const char *path, struct statfs *statfs)
+{
+	sysarg_t rc;
+	sysarg_t rc_orig;
+	aid_t req;
+	size_t pa_size;
+	
+	char *pa = absolutize(path, &pa_size);
+	if (!pa)
+		return ENOMEM;
+	async_exch_t *exch = vfs_exchange_begin();
+	
+	req = async_send_0(exch, VFS_IN_STATFS, NULL);
+	rc = async_data_write_start(exch, pa, pa_size);
+	if (rc != EOK) {
+		vfs_exchange_end(exch);
+		free(pa);
+		async_wait_for(req, &rc_orig);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	rc = async_data_read_start(exch, (void *) statfs, sizeof(struct statfs));
+	if (rc != EOK) {
+		vfs_exchange_end(exch);
+		free(pa);
+		async_wait_for(req, &rc_orig);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	vfs_exchange_end(exch);
+	free(pa);
+	async_wait_for(req, &rc);
+	return rc;
+}
+
 /** @}
  */
Index: uspace/lib/c/include/adt/list.h
===================================================================
--- uspace/lib/c/include/adt/list.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/include/adt/list.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -68,5 +68,5 @@
 
 #define list_foreach(list, member, itype, iterator) \
-	for (itype *iterator = NULL; iterator == NULL; iterator =(itype *)1) \
+	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) 1) \
 	    for (link_t *_link = (list).head.next; \
 	    iterator = list_get_instance(_link, itype, member), \
@@ -74,5 +74,5 @@
 
 /** Unlike list_foreach(), allows removing items while traversing a list.
- * 
+ *
  * @code
  * list_t mylist;
@@ -249,4 +249,5 @@
  *
  * @return Next item or NULL if @a link is the last item.
+ *
  */
 static inline link_t *list_next(link_t *link, const list_t *list)
@@ -261,4 +262,5 @@
  *
  * @return Previous item or NULL if @a link is the first item.
+ *
  */
 static inline link_t *list_prev(link_t *link, const list_t *list)
Index: uspace/lib/c/include/device/pio_window.h
===================================================================
--- uspace/lib/c/include/device/pio_window.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/lib/c/include/device/pio_window.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013 Jakub Jermar 
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_DEVICE_PIO_WINDOW_H_
+#define LIBC_DEVICE_PIO_WINDOW_H_
+
+#include <ipc/dev_iface.h>
+#include <async.h>
+
+/** PIO_WINDOW provider interface */
+typedef enum {
+	PIO_WINDOW_GET = 0,
+} pio_window_method_t;
+
+typedef struct {
+	struct {
+		uintptr_t base;
+		size_t size;
+	} mem, io;
+} pio_window_t;
+
+extern int pio_window_get(async_sess_t *, pio_window_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/devman.h
===================================================================
--- uspace/lib/c/include/devman.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/include/devman.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -61,6 +61,9 @@
     unsigned int);
 extern int devman_fun_get_child(devman_handle_t, devman_handle_t *);
+extern int devman_dev_get_parent(devman_handle_t, devman_handle_t *);
 extern int devman_dev_get_functions(devman_handle_t, devman_handle_t **,
     size_t *);
+extern int devman_fun_get_match_id(devman_handle_t, size_t, char *, size_t,
+    unsigned int *);
 extern int devman_fun_get_name(devman_handle_t, char *, size_t);
 extern int devman_fun_get_driver_name(devman_handle_t, char *, size_t);
@@ -71,4 +74,13 @@
 extern int devman_add_device_to_category(devman_handle_t, const char *);
 extern int devman_fun_sid_to_handle(service_id_t, devman_handle_t *);
+extern int devman_get_drivers(devman_handle_t **, size_t *);
+extern int devman_driver_get_devices(devman_handle_t, devman_handle_t **,
+    size_t *);
+extern int devman_driver_get_handle(const char *, devman_handle_t *);
+extern int devman_driver_get_match_id(devman_handle_t, size_t, char *, size_t,
+    unsigned int *);
+extern int devman_driver_get_name(devman_handle_t, char *, size_t);
+extern int devman_driver_get_state(devman_handle_t, driver_state_t *);
+extern int devman_driver_load(devman_handle_t);
 
 #endif
Index: uspace/lib/c/include/ipc/dev_iface.h
===================================================================
--- uspace/lib/c/include/ipc/dev_iface.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/include/ipc/dev_iface.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -36,4 +36,5 @@
 typedef enum {
 	HW_RES_DEV_IFACE = 0,
+	PIO_WINDOW_DEV_IFACE,
 
 	/** Character device interface */
Index: uspace/lib/c/include/ipc/devman.h
===================================================================
--- uspace/lib/c/include/ipc/devman.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/include/ipc/devman.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -42,4 +42,18 @@
 
 typedef sysarg_t devman_handle_t;
+
+typedef enum {
+	/** Driver has not been started. */
+	DRIVER_NOT_STARTED = 0,
+	
+	/**
+	 * Driver has been started, but has not registered as running and ready
+	 * to receive requests.
+	 */
+	DRIVER_STARTING,
+	
+	/** Driver is running and prepared to serve incomming requests. */
+	DRIVER_RUNNING
+} driver_state_t;
 
 typedef enum {
@@ -155,5 +169,7 @@
 	DEVMAN_DEVICE_GET_HANDLE = IPC_FIRST_USER_METHOD,
 	DEVMAN_DEV_GET_FUNCTIONS,
+	DEVMAN_DEV_GET_PARENT,
 	DEVMAN_FUN_GET_CHILD,
+	DEVMAN_FUN_GET_MATCH_ID,
 	DEVMAN_FUN_GET_NAME,
 	DEVMAN_FUN_GET_DRIVER_NAME,
@@ -161,5 +177,12 @@
 	DEVMAN_FUN_OFFLINE,
 	DEVMAN_FUN_GET_PATH,
-	DEVMAN_FUN_SID_TO_HANDLE
+	DEVMAN_FUN_SID_TO_HANDLE,
+	DEVMAN_GET_DRIVERS,
+	DEVMAN_DRIVER_GET_DEVICES,
+	DEVMAN_DRIVER_GET_HANDLE,
+	DEVMAN_DRIVER_GET_MATCH_ID,
+	DEVMAN_DRIVER_GET_NAME,
+	DEVMAN_DRIVER_GET_STATE,
+	DEVMAN_DRIVER_LOAD
 } client_to_devman_t;
 
Index: uspace/lib/c/include/ipc/vfs.h
===================================================================
--- uspace/lib/c/include/ipc/vfs.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/include/ipc/vfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -82,4 +82,5 @@
 	VFS_IN_WAIT_HANDLE,
 	VFS_IN_MTAB_GET,
+	VFS_IN_STATFS
 } vfs_in_request_t;
 
@@ -98,4 +99,5 @@
 	VFS_OUT_LOOKUP,
 	VFS_OUT_DESTROY,
+	VFS_OUT_STATFS,
 	VFS_OUT_LAST
 } vfs_out_request_t;
Index: uspace/lib/c/include/sys/statfs.h
===================================================================
--- uspace/lib/c/include/sys/statfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/lib/c/include/sys/statfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013 Manuele Conti
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_SYS_STATFS_H_
+#define LIBC_SYS_STATFS_H_
+
+#include <sys/types.h>
+
+struct statfs { 
+	uint32_t    f_type;     /* type of file system  */
+	uint32_t    f_bsize;    /* fundamental file system block size */
+	uint64_t    f_blocks;   /* total data blocks in file system */
+	uint64_t    f_bfree;    /* free blocks in fs */
+};
+
+extern int statfs(const char *, struct statfs *);
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/vfs/vfs.h
===================================================================
--- uspace/lib/c/include/vfs/vfs.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/include/vfs/vfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -44,7 +44,9 @@
 #include "vfs_mtab.h"
 
+
 enum vfs_change_state_type {
 	VFS_PASS_HANDLE
 };
+
 
 extern char *absolutize(const char *, size_t *);
@@ -61,5 +63,4 @@
 extern async_exch_t *vfs_exchange_begin(void);
 extern void vfs_exchange_end(async_exch_t *);
-
 #endif
 
Index: uspace/lib/drv/Makefile
===================================================================
--- uspace/lib/drv/Makefile	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/drv/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -41,4 +41,5 @@
 	generic/remote_audio_pcm.c \
 	generic/remote_hw_res.c \
+	generic/remote_pio_window.c \
 	generic/remote_char_dev.c \
 	generic/remote_graph_dev.c \
Index: uspace/lib/drv/generic/dev_iface.c
===================================================================
--- uspace/lib/drv/generic/dev_iface.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/drv/generic/dev_iface.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -40,4 +40,5 @@
 #include "dev_iface.h"
 #include "remote_hw_res.h"
+#include "remote_pio_window.h"
 #include "remote_char_dev.h"
 #include "remote_clock_dev.h"
@@ -58,4 +59,5 @@
 		[AUDIO_PCM_BUFFER_IFACE] = &remote_audio_pcm_iface,
 		[HW_RES_DEV_IFACE] = &remote_hw_res_iface,
+		[PIO_WINDOW_DEV_IFACE] = &remote_pio_window_iface,
 		[CHAR_DEV_IFACE] = &remote_char_dev_iface,
 		[GRAPH_DEV_IFACE] = &remote_graph_dev_iface,
Index: uspace/lib/drv/generic/remote_pio_window.c
===================================================================
--- uspace/lib/drv/generic/remote_pio_window.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/lib/drv/generic/remote_pio_window.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013 Jakub Jermar
+ * 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 libdrv
+ * @{
+ */
+/** @file
+ */
+
+#include <async.h>
+#include <errno.h>
+
+#include "ops/pio_window.h"
+#include "ddf/driver.h"
+
+static void remote_pio_window_get(ddf_fun_t *, void *, ipc_callid_t,
+    ipc_call_t *);
+
+static remote_iface_func_ptr_t remote_pio_window_iface_ops [] = {
+	[PIO_WINDOW_GET] = &remote_pio_window_get
+};
+
+remote_iface_t remote_pio_window_iface = {
+	.method_count = sizeof(remote_pio_window_iface_ops) /
+	    sizeof(remote_iface_func_ptr_t),
+	.methods = remote_pio_window_iface_ops
+};
+
+static void remote_pio_window_get(ddf_fun_t *fun, void *ops,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	pio_window_ops_t *pio_win_ops = (pio_window_ops_t *) ops;
+	size_t len;
+
+	if (!pio_win_ops->get_pio_window) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	
+	pio_window_t *pio_window = pio_win_ops->get_pio_window(fun);
+	if (!pio_window) {
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	async_answer_0(callid, EOK);
+
+	if (!async_data_read_receive(&callid, &len)) {
+		/* Protocol error - the recipient is not accepting data */
+		return;
+	}
+
+	async_data_read_finalize(callid, pio_window, len);
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/drv/include/ops/pio_window.h
===================================================================
--- uspace/lib/drv/include/ops/pio_window.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/lib/drv/include/ops/pio_window.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013 Jakub Jermar 
+ * 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 libdrv
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDRV_OPS_PIO_WINDOW_H_
+#define LIBDRV_OPS_PIO_WINDOW_H_
+
+#include <device/pio_window.h>
+#include <sys/types.h>
+#include "../ddf/driver.h"
+
+typedef struct {
+	pio_window_t *(* get_pio_window)(ddf_fun_t *);
+} pio_window_ops_t;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/drv/include/remote_pio_window.h
===================================================================
--- uspace/lib/drv/include/remote_pio_window.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/lib/drv/include/remote_pio_window.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013 Jakub Jermar 
+ * 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 libdrv
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDRV_REMOTE_PIO_WINDOW_H_
+#define LIBDRV_REMOTE_PIO_WINDOW_H_
+
+extern remote_iface_t remote_pio_window_iface;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/fs/libfs.c
===================================================================
--- uspace/lib/fs/libfs.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/fs/libfs.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -45,4 +45,5 @@
 #include <mem.h>
 #include <sys/stat.h>
+#include <sys/statfs.h>
 #include <stdlib.h>
 
@@ -74,4 +75,5 @@
 static void libfs_open_node(libfs_ops_t *, fs_handle_t, ipc_callid_t,
     ipc_call_t *);
+static void libfs_statfs(libfs_ops_t *, fs_handle_t, ipc_callid_t, ipc_call_t *);
 
 static void vfs_out_mounted(ipc_callid_t rid, ipc_call_t *req)
@@ -219,4 +221,8 @@
 }
 
+static void vfs_out_statfs(ipc_callid_t rid, ipc_call_t *req)
+{
+	libfs_statfs(libfs_ops, reg.fs_handle, rid, req);
+}
 static void vfs_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
@@ -276,4 +282,7 @@
 		case VFS_OUT_SYNC:
 			vfs_out_sync(callid, &call);
+			break;
+		case VFS_OUT_STATFS:
+			vfs_out_statfs(callid, &call);
 			break;
 		default:
@@ -825,8 +834,59 @@
 	
 	ops->node_put(fn);
-	
+
+
 	async_data_read_finalize(callid, &stat, sizeof(struct stat));
 	async_answer_0(rid, EOK);
 }
+
+void libfs_statfs(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid,
+    ipc_call_t *request)
+{
+	service_id_t service_id = (service_id_t) IPC_GET_ARG1(*request);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
+	
+	fs_node_t *fn;
+	int rc = ops->node_get(&fn, service_id, index);
+	on_error(rc, answer_and_return(rid, rc));
+	
+	ipc_callid_t callid;
+	size_t size;
+	if ((!async_data_read_receive(&callid, &size)) ||
+	    (size != sizeof(struct statfs))) {
+		ops->node_put(fn);
+		async_answer_0(callid, EINVAL);
+		async_answer_0(rid, EINVAL);
+		return;
+	}
+	
+	struct statfs statfs;
+	memset(&statfs, 0, sizeof(struct statfs));
+
+	if (NULL != ops->size_block) {	
+		rc = ops->size_block(service_id, &statfs.f_bsize);
+		if (rc != EOK) goto error;
+	}
+
+	if (NULL != ops->total_block_count) {
+		rc = ops->total_block_count(service_id, &statfs.f_blocks);
+		if (rc != EOK) goto error;
+	}
+
+	if (NULL != ops->free_block_count) {
+		rc = ops->free_block_count(service_id, &statfs.f_bfree);
+		if (rc != EOK) goto error;
+	}
+
+	ops->node_put(fn);
+	async_data_read_finalize(callid, &statfs, sizeof(struct statfs));
+	async_answer_0(rid, EOK);
+	return;
+
+error:
+	ops->node_put(fn);
+	async_answer_0(callid, EINVAL);
+	async_answer_0(rid, EINVAL);
+}
+
 
 /** Open VFS triplet.
Index: uspace/lib/fs/libfs.h
===================================================================
--- uspace/lib/fs/libfs.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/fs/libfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -93,4 +93,7 @@
 	bool (* is_file)(fs_node_t *);
 	service_id_t (* service_get)(fs_node_t *);
+	int (* size_block)(service_id_t, uint32_t *);
+	int (* total_block_count)(service_id_t, uint64_t *);
+	int (* free_block_count)(service_id_t, uint64_t *);
 } libfs_ops_t;
 
Index: uspace/lib/usbhid/src/hidparser.c
===================================================================
--- uspace/lib/usbhid/src/hidparser.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/usbhid/src/hidparser.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -132,5 +132,5 @@
 	usb_hid_report_description_t *report_des;
 	usb_hid_report_type_t type = USB_HID_REPORT_TYPE_INPUT;
-
+	
 	if (report == NULL) {
 		return EINVAL;
@@ -155,10 +155,10 @@
 
 		if (USB_HID_ITEM_FLAG_CONSTANT(item->item_flags) == 0) {
-
+			
 			if (USB_HID_ITEM_FLAG_VARIABLE(item->item_flags) == 0) {
 				/* array */
 				item->value = 
 					usb_hid_translate_data(item, data);
-
+				
 				item->usage = USB_HID_EXTENDED_USAGE(
 				    item->usages[item->value -
@@ -185,5 +185,5 @@
 		}
 	}
-
+	
 	return EOK;
 }
@@ -293,7 +293,8 @@
 
 	list_foreach(report->reports, reports_link,
-	    usb_hid_report_description_t, report_des) {
-		if ((report_des->report_id == report_id) &&
-		    (report_des->type == USB_HID_REPORT_TYPE_OUTPUT)) {
+	    usb_hid_report_description_t, rdes) {
+		if ((rdes->report_id == report_id) &&
+		    (rdes->type == USB_HID_REPORT_TYPE_OUTPUT)) {
+			report_des = rdes;
 			break;
 		}
@@ -340,5 +341,5 @@
 	int length;
 	int32_t tmp_value;
-
+	
 	if (report == NULL) {
 		return EINVAL;
@@ -352,5 +353,5 @@
 	report_des = usb_hid_report_find_description(report, report_id, 
 	    USB_HID_REPORT_TYPE_OUTPUT);
-
+	
 	if (report_des == NULL) {
 		return EINVAL;
@@ -364,5 +365,5 @@
 		offset = report_des->bit_length - report_item->offset - 1;
 		length = report_item->size;
-
+		
 		usb_log_debug("\ttranslated value: %x\n", value);
 
@@ -375,5 +376,5 @@
 			value = value << shift;
 			value = value & (((1 << length) - 1) << shift);
-
+			
 			uint8_t mask = 0;
 			mask = 0xff - (((1 << length) - 1) << shift);
@@ -391,5 +392,5 @@
 
 					tmp_value = tmp_value << (offset % 8);
-
+					
 					mask = ~(((1 << (8 - (offset % 8))) - 1)
 					    << (offset % 8));
@@ -398,5 +399,5 @@
 					    tmp_value;
 				} else if (i == ((offset + length - 1) / 8)) {
-
+					
 					value = value >> (length - 
 					    ((offset + length) % 8));
@@ -404,5 +405,5 @@
 					value = value & ((1 << (length - 
 					    ((offset + length) % 8))) - 1);
-
+					
 					mask = (1 << (length - 
 					    ((offset + length) % 8))) - 1;
@@ -418,5 +419,5 @@
 		report_item->value = 0;
 	}
-
+	
 	return EOK;
 }
Index: uspace/lib/usbhid/src/hidpath.c
===================================================================
--- uspace/lib/usbhid/src/hidpath.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/usbhid/src/hidpath.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -237,14 +237,11 @@
 		    usb_hid_report_usage_path_t, report_item) {
 
-			if(USB_HID_SAME_USAGE_PAGE(report_item->usage_page,
-				path_item->usage_page)){
-
-				if(only_page == 0){
-					if(USB_HID_SAME_USAGE(
-						report_item->usage,
-						path_item->usage)) {
-
+			if (USB_HID_SAME_USAGE_PAGE(report_item->usage_page,
+				path_item->usage_page)) {
+				
+				if (only_page == 0) {
+					if (USB_HID_SAME_USAGE(report_item->usage,
+					    path_item->usage))
 						return EOK;
-					}
 				}
 				else {
@@ -268,19 +265,18 @@
 		report_link = report_path->items.head.next;
 		path_link = path->items.head.next;
-
-		while((report_link != &report_path->items.head) && 
-		      (path_link != &path->items.head)) {
-
+		
+		while ((report_link != &report_path->items.head) &&
+		    (path_link != &path->items.head)) {
+			
 			report_item = list_get_instance(report_link, 
-				usb_hid_report_usage_path_t, rpath_items_link);
-
+			    usb_hid_report_usage_path_t, rpath_items_link);
+			
 			path_item = list_get_instance(path_link,
        				usb_hid_report_usage_path_t, rpath_items_link);
 
-			if(!USB_HID_SAME_USAGE_PAGE(report_item->usage_page, 
-				path_item->usage_page) || ((only_page == 0) && 
+			if (!USB_HID_SAME_USAGE_PAGE(report_item->usage_page, 
+			    path_item->usage_page) || ((only_page == 0) && 
 			    !USB_HID_SAME_USAGE(report_item->usage, 
-				path_item->usage))) {
-
+			    path_item->usage))) {
 				return 1;
 			} else {
@@ -290,14 +286,12 @@
 		}
 
-		if((((flags & USB_HID_PATH_COMPARE_BEGIN) != 0) && 
-			(path_link == &path->items.head)) || 
-		   ((report_link == &report_path->items.head) && 
-			(path_link == &path->items.head))) {
-
+		if ((((flags & USB_HID_PATH_COMPARE_BEGIN) != 0) && 
+		    (path_link == &path->items.head)) || 
+		    ((report_link == &report_path->items.head) && 
+		    (path_link == &path->items.head))) {
 			return EOK;
-		}
-		else {
+		} else {
 			return 1;
-		}						
+		}
 		break;
 
Index: uspace/srv/devman/Makefile
===================================================================
--- uspace/srv/devman/Makefile	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/devman/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -33,6 +33,12 @@
 
 SOURCES = \
+	client_conn.c \
+	dev.c \
+	devtree.c \
+	drv_conn.c \
+	driver.c \
+	loc.c \
+	fun.c \
 	main.c \
-	devman.c \
 	match.c \
 	util.c
Index: uspace/srv/devman/client_conn.c
===================================================================
--- uspace/srv/devman/client_conn.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/client_conn.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,803 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * 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.
+ */
+
+/**
+ * @defgroup devman Device manager.
+ * @brief HelenOS device manager.
+ * @{
+ */
+
+/** @file
+ */
+
+#include <inttypes.h>
+#include <assert.h>
+#include <ns.h>
+#include <async.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+#include <stdbool.h>
+#include <fibril_synch.h>
+#include <stdlib.h>
+#include <str.h>
+#include <ctype.h>
+#include <ipc/devman.h>
+
+#include "client_conn.h"
+#include "dev.h"
+#include "devman.h"
+#include "driver.h"
+#include "fun.h"
+#include "loc.h"
+#include "main.h"
+
+/** Find handle for the device instance identified by the device's path in the
+ * device tree. */
+static void devman_function_get_handle(ipc_callid_t iid, ipc_call_t *icall)
+{
+	char *pathname;
+	devman_handle_t handle;
+	
+	int rc = async_data_write_accept((void **) &pathname, true, 0, 0, 0, 0);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fun_node_t *fun = find_fun_node_by_path(&device_tree, pathname);
+	
+	free(pathname);
+
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	handle = fun->handle;
+
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+
+	/* Delete reference created above by find_fun_node_by_path() */
+	fun_del_ref(fun);
+
+	async_answer_1(iid, EOK, handle);
+}
+
+/** Get device match ID. */
+static void devman_fun_get_match_id(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*icall);
+	size_t index = IPC_GET_ARG2(*icall);
+	void *buffer = NULL;
+
+	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);
+		fun_del_ref(fun);
+		return;
+	}
+
+	buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		fun_del_ref(fun);
+		return;
+	}
+
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+
+	/* Check function state */
+	if (fun->state == FUN_REMOVED)
+		goto error;
+
+	link_t *link = list_nth(&fun->match_ids.ids, index);
+	if (link == NULL)
+		goto error;
+
+	match_id_t *mid = list_get_instance(link, match_id_t, link);
+
+	size_t sent_length = str_size(mid->id);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, mid->id, sent_length);
+	async_answer_1(iid, EOK, mid->score);
+
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	fun_del_ref(fun);
+	free(buffer);
+
+	return;
+error:
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	free(buffer);
+
+	async_answer_0(data_callid, ENOENT);
+	async_answer_0(iid, ENOENT);
+	fun_del_ref(fun);
+}
+
+/** Get device name. */
+static void devman_fun_get_name(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);
+		fun_del_ref(fun);
+		return;
+	}
+
+	void *buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		fun_del_ref(fun);
+		return;
+	}
+
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		free(buffer);
+
+		async_answer_0(data_callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		fun_del_ref(fun);
+		return;
+	}
+
+	size_t sent_length = str_size(fun->name);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, fun->name, sent_length);
+	async_answer_0(iid, EOK);
+
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	fun_del_ref(fun);
+	free(buffer);
+}
+
+/** Get function driver name. */
+static void devman_fun_get_driver_name(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);
+		fun_del_ref(fun);
+		return;
+	}
+
+	void *buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		fun_del_ref(fun);
+		return;
+	}
+
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		free(buffer);
+
+		async_answer_0(data_callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		fun_del_ref(fun);
+		return;
+	}
+
+	/* Check whether function has a driver */
+	if (fun->child == NULL || fun->child->drv == NULL) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		free(buffer);
+
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		fun_del_ref(fun);
+		return;
+	}
+
+	size_t sent_length = str_size(fun->child->drv->name);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, fun->child->drv->name,
+	    sent_length);
+	async_answer_0(iid, EOK);
+
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	fun_del_ref(fun);
+	free(buffer);
+}
+
+/** Get device path. */
+static void devman_fun_get_path(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);
+		fun_del_ref(fun);
+		return;
+	}
+
+	void *buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		fun_del_ref(fun);
+		return;
+	}
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		free(buffer);
+
+		async_answer_0(data_callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		fun_del_ref(fun);
+		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);
+
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	fun_del_ref(fun);
+	free(buffer);
+}
+
+/** Get handle for parent function of a device. */
+static void devman_dev_get_parent(ipc_callid_t iid, ipc_call_t *icall)
+{
+	dev_node_t *dev;
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	
+	dev = find_dev_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
+	if (dev == NULL || dev->state == DEVICE_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	if (dev->pfun == NULL) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, dev->pfun->handle);
+	
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+}
+
+static void devman_dev_get_functions(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	
+	dev_node_t *dev = find_dev_node_no_lock(&device_tree,
+	    IPC_GET_ARG1(*icall));
+	if (dev == NULL || dev->state == DEVICE_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
+	if (hdl_buf == NULL) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	rc = dev_get_functions(&device_tree, dev, hdl_buf, size, &act_size);
+	if (rc != EOK) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	
+	sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
+	free(hdl_buf);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+/** Get handle for child device of a function. */
+static void devman_fun_get_child(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fun_node_t *fun;
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	
+	fun = find_fun_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
+	if (fun == NULL || fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	if (fun->child == NULL) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, fun->child->handle);
+	
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+}
+
+/** Online function.
+ *
+ * Send a request to online a function to the responsible driver.
+ * The driver may offline other functions if necessary (i.e. if the state
+ * of this function is linked to state of another function somehow).
+ */
+static void devman_fun_online(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fun_node_t *fun;
+	int rc;
+
+	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	rc = driver_fun_online(&device_tree, fun);
+	fun_del_ref(fun);
+	
+	async_answer_0(iid, (sysarg_t) rc);
+}
+
+/** Offline function.
+ *
+ * Send a request to offline a function to the responsible driver. As
+ * a result the subtree rooted at that function should be cleanly
+ * detatched. The driver may offline other functions if necessary
+ * (i.e. if the state of this function is linked to state of another
+ * function somehow).
+ */
+static void devman_fun_offline(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fun_node_t *fun;
+	int rc;
+
+	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	rc = driver_fun_offline(&device_tree, fun);
+	fun_del_ref(fun);
+	
+	async_answer_0(iid, (sysarg_t) rc);
+}
+
+/** Find handle for the function instance identified by its service ID. */
+static void devman_fun_sid_to_handle(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fun_node_t *fun;
+
+	fun = find_loc_tree_function(&device_tree, IPC_GET_ARG1(*icall));
+	
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+
+	async_answer_1(iid, EOK, fun->handle);
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	fun_del_ref(fun);
+}
+
+/** Get list of all registered drivers. */
+static void devman_get_drivers(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
+	if (hdl_buf == NULL) {
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	rc = driver_get_list(&drivers_list, hdl_buf, size, &act_size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
+	free(hdl_buf);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+static void devman_driver_get_devices(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	driver_t *drv = driver_find(&drivers_list, IPC_GET_ARG1(*icall));
+	if (drv == NULL) {
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
+	if (hdl_buf == NULL) {
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	rc = driver_get_devices(drv, hdl_buf, size, &act_size);
+	if (rc != EOK) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
+	free(hdl_buf);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+
+/** Find driver by name. */
+static void devman_driver_get_handle(ipc_callid_t iid, ipc_call_t *icall)
+{
+	char *drvname;
+	
+	int rc = async_data_write_accept((void **) &drvname, true, 0, 0, 0, 0);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	driver_t *driver = driver_find_by_name(&drivers_list, drvname);
+	
+	free(drvname);
+	
+	if (driver == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, driver->handle);
+}
+
+/** Get driver match ID. */
+static void devman_driver_get_match_id(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*icall);
+	size_t index = IPC_GET_ARG2(*icall);
+
+	driver_t *drv = driver_find(&drivers_list, handle);
+	if (drv == 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;
+	}
+
+	fibril_mutex_lock(&drv->driver_mutex);
+	link_t *link = list_nth(&drv->match_ids.ids, index);
+	if (link == NULL) {
+		fibril_mutex_unlock(&drv->driver_mutex);
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	match_id_t *mid = list_get_instance(link, match_id_t, link);
+
+	size_t sent_length = str_size(mid->id);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, mid->id, sent_length);
+	async_answer_1(iid, EOK, mid->score);
+
+	fibril_mutex_unlock(&drv->driver_mutex);
+
+	free(buffer);
+}
+
+/** Get driver name. */
+static void devman_driver_get_name(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*icall);
+
+	driver_t *drv = driver_find(&drivers_list, handle);
+	if (drv == 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;
+	}
+
+	fibril_mutex_lock(&drv->driver_mutex);
+
+	size_t sent_length = str_size(drv->name);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, drv->name, sent_length);
+	async_answer_0(iid, EOK);
+
+	fibril_mutex_unlock(&drv->driver_mutex);
+
+	free(buffer);
+}
+
+/** Get driver state. */
+static void devman_driver_get_state(ipc_callid_t iid, ipc_call_t *icall)
+{
+	driver_t *drv;
+	
+	drv = driver_find(&drivers_list, IPC_GET_ARG1(*icall));
+	if (drv == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, (sysarg_t) drv->state);
+}
+
+/** Forcibly load a driver. */
+static void devman_driver_load(ipc_callid_t iid, ipc_call_t *icall)
+{
+	driver_t *drv;
+	int rc;
+	
+	drv = driver_find(&drivers_list, IPC_GET_ARG1(*icall));
+	if (drv == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fibril_mutex_lock(&drv->driver_mutex);
+	rc = start_driver(drv) ? EOK : EIO;
+	fibril_mutex_unlock(&drv->driver_mutex);
+
+	async_answer_0(iid, rc);
+}
+
+/** Function for handling connections from a client to the device manager. */
+void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
+{
+	/* Accept connection. */
+	async_answer_0(iid, EOK);
+	
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call))
+			break;
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case DEVMAN_DEVICE_GET_HANDLE:
+			devman_function_get_handle(callid, &call);
+			break;
+		case DEVMAN_DEV_GET_PARENT:
+			devman_dev_get_parent(callid, &call);
+			break;
+		case DEVMAN_DEV_GET_FUNCTIONS:
+			devman_dev_get_functions(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_CHILD:
+			devman_fun_get_child(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_MATCH_ID:
+			devman_fun_get_match_id(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_NAME:
+			devman_fun_get_name(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_DRIVER_NAME:
+			devman_fun_get_driver_name(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_PATH:
+			devman_fun_get_path(callid, &call);
+			break;
+		case DEVMAN_FUN_ONLINE:
+			devman_fun_online(callid, &call);
+			break;
+		case DEVMAN_FUN_OFFLINE:
+			devman_fun_offline(callid, &call);
+			break;
+		case DEVMAN_FUN_SID_TO_HANDLE:
+			devman_fun_sid_to_handle(callid, &call);
+			break;
+		case DEVMAN_GET_DRIVERS:
+			devman_get_drivers(callid, &call);
+			break;
+		case DEVMAN_DRIVER_GET_DEVICES:
+			devman_driver_get_devices(callid, &call);
+			break;
+		case DEVMAN_DRIVER_GET_HANDLE:
+			devman_driver_get_handle(callid, &call);
+			break;
+		case DEVMAN_DRIVER_GET_MATCH_ID:
+			devman_driver_get_match_id(callid, &call);
+			break;
+		case DEVMAN_DRIVER_GET_NAME:
+			devman_driver_get_name(callid, &call);
+			break;
+		case DEVMAN_DRIVER_GET_STATE:
+			devman_driver_get_state(callid, &call);
+			break;
+		case DEVMAN_DRIVER_LOAD:
+			devman_driver_load(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOENT);
+		}
+	}
+}
+
+
+/** @}
+ */
Index: uspace/srv/devman/client_conn.h
===================================================================
--- uspace/srv/devman/client_conn.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/client_conn.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * 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 devman
+ * @{
+ */
+
+#ifndef CLIENT_CONN_H_
+#define CLIENT_CONN_H_
+
+#include "devman.h"
+
+extern void devman_connection_client(ipc_callid_t, ipc_call_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/dev.c
===================================================================
--- uspace/srv/devman/dev.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/dev.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * 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 devman
+ * @{
+ */
+
+#include <errno.h>
+
+#include "dev.h"
+#include "devman.h"
+
+/** Create a new device node.
+ *
+ * @return		A device node structure.
+ */
+dev_node_t *create_dev_node(void)
+{
+	dev_node_t *dev;
+	
+	dev = calloc(1, sizeof(dev_node_t));
+	if (dev == NULL)
+		return NULL;
+	
+	atomic_set(&dev->refcnt, 0);
+	list_initialize(&dev->functions);
+	link_initialize(&dev->driver_devices);
+	
+	return dev;
+}
+
+/** Delete a device node.
+ *
+ * @param node		The device node structure.
+ */
+void delete_dev_node(dev_node_t *dev)
+{
+	assert(list_empty(&dev->functions));
+	assert(dev->pfun == NULL);
+	assert(dev->drv == NULL);
+	
+	free(dev);
+}
+
+/** Increase device node reference count.
+ *
+ * @param dev	Device node
+ */
+void dev_add_ref(dev_node_t *dev)
+{
+	atomic_inc(&dev->refcnt);
+}
+
+/** Decrease device node reference count.
+ *
+ * When the count drops to zero the device node is freed.
+ *
+ * @param dev	Device node
+ */
+void dev_del_ref(dev_node_t *dev)
+{
+	if (atomic_predec(&dev->refcnt) == 0)
+		delete_dev_node(dev);
+}
+
+/** Find the device node structure of the device witch has the specified handle.
+ *
+ * @param tree		The device tree where we look for the device node.
+ * @param handle	The handle of the device.
+ * @return		The device node.
+ */
+dev_node_t *find_dev_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
+{
+	assert(fibril_rwlock_is_locked(&tree->rwlock));
+	
+	ht_link_t *link = hash_table_find(&tree->devman_devices, &handle);
+	if (link == NULL)
+		return NULL;
+	
+	return hash_table_get_inst(link, dev_node_t, devman_dev);
+}
+
+/** Find the device node structure of the device witch has the specified handle.
+ *
+ * @param tree		The device tree where we look for the device node.
+ * @param handle	The handle of the device.
+ * @return		The device node.
+ */
+dev_node_t *find_dev_node(dev_tree_t *tree, devman_handle_t handle)
+{
+	dev_node_t *dev = NULL;
+	
+	fibril_rwlock_read_lock(&tree->rwlock);
+	dev = find_dev_node_no_lock(tree, handle);
+	if (dev != NULL)
+		dev_add_ref(dev);
+	
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	return dev;
+}
+
+/** Get list of device functions. */
+int dev_get_functions(dev_tree_t *tree, dev_node_t *dev,
+    devman_handle_t *hdl_buf, size_t buf_size, size_t *act_size)
+{
+	size_t act_cnt;
+	size_t buf_cnt;
+
+	assert(fibril_rwlock_is_locked(&tree->rwlock));
+
+	buf_cnt = buf_size / sizeof(devman_handle_t);
+
+	act_cnt = list_count(&dev->functions);
+	*act_size = act_cnt * sizeof(devman_handle_t);
+
+	if (buf_size % sizeof(devman_handle_t) != 0)
+		return EINVAL;
+
+	size_t pos = 0;
+	list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
+		if (pos < buf_cnt) {
+			hdl_buf[pos] = fun->handle;
+		}
+
+		pos++;
+	}
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/devman/dev.h
===================================================================
--- uspace/srv/devman/dev.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/dev.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * 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 devman
+ * @{
+ */
+
+#ifndef DEV_H_
+#define DEV_H_
+
+#include "devman.h"
+
+extern dev_node_t *create_dev_node(void);
+extern void delete_dev_node(dev_node_t *node);
+extern void dev_add_ref(dev_node_t *);
+extern void dev_del_ref(dev_node_t *);
+
+extern dev_node_t *find_dev_node_no_lock(dev_tree_t *tree,
+    devman_handle_t handle);
+extern dev_node_t *find_dev_node(dev_tree_t *tree, devman_handle_t handle);
+extern int dev_get_functions(dev_tree_t *tree, dev_node_t *, devman_handle_t *,
+    size_t, size_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/devman.c
===================================================================
--- uspace/srv/devman/devman.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ 	(revision )
@@ -1,1518 +1,0 @@
-/*
- * Copyright (c) 2010 Lenka Trochtova
- * 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 devman
- * @{
- */
-/** @file Device Manager
- *
- * Locking order:
- *   (1) driver_t.driver_mutex
- *   (2) dev_tree_t.rwlock
- *
- * Synchronization:
- *    - device_tree.rwlock protects:
- *        - tree root, complete tree topology
- *        - complete contents of device and function nodes
- *    - dev_node_t.refcnt, fun_node_t.refcnt prevent nodes from
- *      being deallocated
- *    - find_xxx() functions increase reference count of returned object
- *    - find_xxx_no_lock() do not increase reference count
- *
- * TODO
- *    - Track all steady and transient device/function states
- *    - Check states, wait for steady state on certain operations
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <io/log.h>
-#include <ipc/driver.h>
-#include <ipc/devman.h>
-#include <loc.h>
-#include <str_error.h>
-#include <stdio.h>
-
-#include "devman.h"
-
-static fun_node_t *find_node_child(dev_tree_t *, fun_node_t *, const char *);
-
-/* hash table operations */
-
-static inline size_t handle_key_hash(void *key)
-{
-	devman_handle_t handle = *(devman_handle_t*)key;
-	return handle;
-}
-
-static size_t devman_devices_hash(const ht_link_t *item)
-{
-	dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
-	return handle_key_hash(&dev->handle);
-}
-
-static size_t devman_functions_hash(const ht_link_t *item)
-{
-	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
-	return handle_key_hash(&fun->handle);
-}
-
-static bool devman_devices_key_equal(void *key, const ht_link_t *item)
-{
-	devman_handle_t handle = *(devman_handle_t*)key;
-	dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
-	return dev->handle == handle;
-}
-
-static bool devman_functions_key_equal(void *key, const ht_link_t *item)
-{
-	devman_handle_t handle = *(devman_handle_t*)key;
-	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
-	return fun->handle == handle;
-}
-
-static inline size_t service_id_key_hash(void *key)
-{
-	service_id_t service_id = *(service_id_t*)key;
-	return service_id;
-}
-
-static size_t loc_functions_hash(const ht_link_t *item)
-{
-	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
-	return service_id_key_hash(&fun->service_id);
-}
-
-static bool loc_functions_key_equal(void *key, const ht_link_t *item)
-{
-	service_id_t service_id = *(service_id_t*)key;
-	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
-	return fun->service_id == service_id;
-}
-
-
-static hash_table_ops_t devman_devices_ops = {
-	.hash = devman_devices_hash,
-	.key_hash = handle_key_hash,
-	.key_equal = devman_devices_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-static hash_table_ops_t devman_functions_ops = {
-	.hash = devman_functions_hash,
-	.key_hash = handle_key_hash,
-	.key_equal = devman_functions_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-static hash_table_ops_t loc_devices_ops = {
-	.hash = loc_functions_hash,
-	.key_hash = service_id_key_hash,
-	.key_equal = loc_functions_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-/**
- * Initialize the list of device driver's.
- *
- * @param drv_list the list of device driver's.
- *
- */
-void init_driver_list(driver_list_t *drv_list)
-{
-	assert(drv_list != NULL);
-	
-	list_initialize(&drv_list->drivers);
-	fibril_mutex_initialize(&drv_list->drivers_mutex);
-}
-
-/** Allocate and initialize a new driver structure.
- *
- * @return	Driver structure.
- */
-driver_t *create_driver(void)
-{
-	driver_t *res = malloc(sizeof(driver_t));
-	if (res != NULL)
-		init_driver(res);
-	return res;
-}
-
-/** Add a driver to the list of drivers.
- *
- * @param drivers_list	List of drivers.
- * @param drv		Driver structure.
- */
-void add_driver(driver_list_t *drivers_list, driver_t *drv)
-{
-	fibril_mutex_lock(&drivers_list->drivers_mutex);
-	list_prepend(&drv->drivers, &drivers_list->drivers);
-	fibril_mutex_unlock(&drivers_list->drivers_mutex);
-
-	log_msg(LOG_DEFAULT, LVL_NOTE, "Driver `%s' was added to the list of available "
-	    "drivers.", drv->name);
-}
-
-/** Read match id at the specified position of a string and set the position in
- * the string to the first character following the id.
- *
- * @param buf		The position in the input string.
- * @return		The match id.
- */
-char *read_match_id(char **buf)
-{
-	char *res = NULL;
-	size_t len = get_nonspace_len(*buf);
-	
-	if (len > 0) {
-		res = malloc(len + 1);
-		if (res != NULL) {
-			str_ncpy(res, len + 1, *buf, len);
-			*buf += len;
-		}
-	}
-	
-	return res;
-}
-
-/**
- * Read match ids and associated match scores from a string.
- *
- * Each match score in the string is followed by its match id.
- * The match ids and match scores are separated by whitespaces.
- * Neither match ids nor match scores can contain whitespaces.
- *
- * @param buf		The string from which the match ids are read.
- * @param ids		The list of match ids into which the match ids and
- *			scores are added.
- * @return		True if at least one match id and associated match score
- *			was successfully read, false otherwise.
- */
-bool parse_match_ids(char *buf, match_id_list_t *ids)
-{
-	int score = 0;
-	char *id = NULL;
-	int ids_read = 0;
-	
-	while (true) {
-		/* skip spaces */
-		if (!skip_spaces(&buf))
-			break;
-		
-		/* read score */
-		score = strtoul(buf, &buf, 10);
-		
-		/* skip spaces */
-		if (!skip_spaces(&buf))
-			break;
-		
-		/* read id */
-		id = read_match_id(&buf);
-		if (NULL == id)
-			break;
-		
-		/* create new match_id structure */
-		match_id_t *mid = create_match_id();
-		mid->id = id;
-		mid->score = score;
-		
-		/* add it to the list */
-		add_match_id(ids, mid);
-		
-		ids_read++;
-	}
-	
-	return ids_read > 0;
-}
-
-/**
- * Read match ids and associated match scores from a file.
- *
- * Each match score in the file is followed by its match id.
- * The match ids and match scores are separated by whitespaces.
- * Neither match ids nor match scores can contain whitespaces.
- *
- * @param buf		The path to the file from which the match ids are read.
- * @param ids		The list of match ids into which the match ids and
- *			scores are added.
- * @return		True if at least one match id and associated match score
- *			was successfully read, false otherwise.
- */
-bool read_match_ids(const char *conf_path, match_id_list_t *ids)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "read_match_ids(conf_path=\"%s\")", conf_path);
-	
-	bool suc = false;
-	char *buf = NULL;
-	bool opened = false;
-	int fd;
-	size_t len = 0;
-	
-	fd = open(conf_path, O_RDONLY);
-	if (fd < 0) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to open `%s' for reading: %s.",
-		    conf_path, str_error(fd));
-		goto cleanup;
-	}
-	opened = true;
-	
-	len = lseek(fd, 0, SEEK_END);
-	lseek(fd, 0, SEEK_SET);
-	if (len == 0) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Configuration file '%s' is empty.",
-		    conf_path);
-		goto cleanup;
-	}
-	
-	buf = malloc(len + 1);
-	if (buf == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Memory allocation failed when parsing file "
-		    "'%s'.", conf_path);
-		goto cleanup;
-	}
-	
-	ssize_t read_bytes = read_all(fd, buf, len);
-	if (read_bytes <= 0) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to read file '%s' (%zd).", conf_path,
-		    read_bytes);
-		goto cleanup;
-	}
-	buf[read_bytes] = 0;
-	
-	suc = parse_match_ids(buf, ids);
-	
-cleanup:
-	free(buf);
-	
-	if (opened)
-		close(fd);
-	
-	return suc;
-}
-
-/**
- * Get information about a driver.
- *
- * Each driver has its own directory in the base directory.
- * The name of the driver's directory is the same as the name of the driver.
- * The driver's directory contains driver's binary (named as the driver without
- * extension) and the configuration file with match ids for device-to-driver
- *  matching (named as the driver with a special extension).
- *
- * This function searches for the driver's directory and containing
- * configuration files. If all the files needed are found, they are parsed and
- * the information about the driver is stored in the driver's structure.
- *
- * @param base_path	The base directory, in which we look for driver's
- *			subdirectory.
- * @param name		The name of the driver.
- * @param drv		The driver structure to fill information in.
- *
- * @return		True on success, false otherwise.
- */
-bool get_driver_info(const char *base_path, const char *name, driver_t *drv)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "get_driver_info(base_path=\"%s\", name=\"%s\")",
-	    base_path, name);
-	
-	assert(base_path != NULL && name != NULL && drv != NULL);
-	
-	bool suc = false;
-	char *match_path = NULL;
-	size_t name_size = 0;
-	
-	/* Read the list of match ids from the driver's configuration file. */
-	match_path = get_abs_path(base_path, name, MATCH_EXT);
-	if (match_path == NULL)
-		goto cleanup;
-	
-	if (!read_match_ids(match_path, &drv->match_ids))
-		goto cleanup;
-	
-	/* Allocate and fill driver's name. */
-	name_size = str_size(name) + 1;
-	drv->name = malloc(name_size);
-	if (drv->name == NULL)
-		goto cleanup;
-	str_cpy(drv->name, name_size, name);
-	
-	/* Initialize path with driver's binary. */
-	drv->binary_path = get_abs_path(base_path, name, "");
-	if (drv->binary_path == NULL)
-		goto cleanup;
-	
-	/* Check whether the driver's binary exists. */
-	struct stat s;
-	if (stat(drv->binary_path, &s) == ENOENT) { /* FIXME!! */
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Driver not found at path `%s'.",
-		    drv->binary_path);
-		goto cleanup;
-	}
-	
-	suc = true;
-	
-cleanup:
-	if (!suc) {
-		free(drv->binary_path);
-		free(drv->name);
-		/* Set the driver structure to the default state. */
-		init_driver(drv);
-	}
-	
-	free(match_path);
-	
-	return suc;
-}
-
-/** Lookup drivers in the directory.
- *
- * @param drivers_list	The list of available drivers.
- * @param dir_path	The path to the directory where we search for drivers.
- * @return		Number of drivers which were found.
- */
-int lookup_available_drivers(driver_list_t *drivers_list, const char *dir_path)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "lookup_available_drivers(dir=\"%s\")", dir_path);
-	
-	int drv_cnt = 0;
-	DIR *dir = NULL;
-	struct dirent *diren;
-
-	dir = opendir(dir_path);
-	
-	if (dir != NULL) {
-		driver_t *drv = create_driver();
-		while ((diren = readdir(dir))) {
-			if (get_driver_info(dir_path, diren->d_name, drv)) {
-				add_driver(drivers_list, drv);
-				drv_cnt++;
-				drv = create_driver();
-			}
-		}
-		delete_driver(drv);
-		closedir(dir);
-	}
-	
-	return drv_cnt;
-}
-
-/** Create root device and function node in the device tree.
- *
- * @param tree	The device tree.
- * @return	True on success, false otherwise.
- */
-bool create_root_nodes(dev_tree_t *tree)
-{
-	fun_node_t *fun;
-	dev_node_t *dev;
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "create_root_nodes()");
-	
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	/*
-	 * Create root function. This is a pseudo function to which
-	 * the root device node is attached. It allows us to match
-	 * the root device driver in a standard manner, i.e. against
-	 * the parent function.
-	 */
-	
-	fun = create_fun_node();
-	if (fun == NULL) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		return false;
-	}
-	
-	fun_add_ref(fun);
-	insert_fun_node(tree, fun, str_dup(""), NULL);
-	
-	match_id_t *id = create_match_id();
-	id->id = str_dup("root");
-	id->score = 100;
-	add_match_id(&fun->match_ids, id);
-	tree->root_node = fun;
-	
-	/*
-	 * Create root device node.
-	 */
-	dev = create_dev_node();
-	if (dev == NULL) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		return false;
-	}
-	
-	dev_add_ref(dev);
-	insert_dev_node(tree, dev, fun);
-	
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	
-	return dev != NULL;
-}
-
-/** Lookup the best matching driver for the specified device in the list of
- * drivers.
- *
- * A match between a device and a driver is found if one of the driver's match
- * ids match one of the device's match ids. The score of the match is the
- * product of the driver's and device's score associated with the matching id.
- * The best matching driver for a device is the driver with the highest score
- * of the match between the device and the driver.
- *
- * @param drivers_list	The list of drivers, where we look for the driver
- *			suitable for handling the device.
- * @param node		The device node structure of the device.
- * @return		The best matching driver or NULL if no matching driver
- *			is found.
- */
-driver_t *find_best_match_driver(driver_list_t *drivers_list, dev_node_t *node)
-{
-	driver_t *best_drv = NULL;
-	int best_score = 0, score = 0;
-	
-	fibril_mutex_lock(&drivers_list->drivers_mutex);
-	
-	list_foreach(drivers_list->drivers, drivers, driver_t, drv) {
-		score = get_match_score(drv, node);
-		if (score > best_score) {
-			best_score = score;
-			best_drv = drv;
-		}
-	}
-	
-	fibril_mutex_unlock(&drivers_list->drivers_mutex);
-	
-	return best_drv;
-}
-
-/** Assign a driver to a device.
- *
- * @param tree		Device tree
- * @param node		The device's node in the device tree.
- * @param drv		The driver.
- */
-void attach_driver(dev_tree_t *tree, dev_node_t *dev, driver_t *drv)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "attach_driver(dev=\"%s\",drv=\"%s\")",
-	    dev->pfun->pathname, drv->name);
-	
-	fibril_mutex_lock(&drv->driver_mutex);
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	dev->drv = drv;
-	list_append(&dev->driver_devices, &drv->devices);
-	
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	fibril_mutex_unlock(&drv->driver_mutex);
-}
-
-/** Detach driver from device.
- *
- * @param tree		Device tree
- * @param node		The device's node in the device tree.
- * @param drv		The driver.
- */
-void detach_driver(dev_tree_t *tree, dev_node_t *dev)
-{
-	driver_t *drv = dev->drv;
-	
-	assert(drv != NULL);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "detach_driver(dev=\"%s\",drv=\"%s\")",
-	    dev->pfun->pathname, drv->name);
-	
-	fibril_mutex_lock(&drv->driver_mutex);
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	dev->drv = NULL;
-	list_remove(&dev->driver_devices);
-	
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	fibril_mutex_unlock(&drv->driver_mutex);
-}
-
-/** Start a driver
- *
- * @param drv		The driver's structure.
- * @return		True if the driver's task is successfully spawned, false
- *			otherwise.
- */
-bool start_driver(driver_t *drv)
-{
-	int rc;
-
-	assert(fibril_mutex_is_locked(&drv->driver_mutex));
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "start_driver(drv=\"%s\")", drv->name);
-	
-	rc = task_spawnl(NULL, drv->binary_path, drv->binary_path, NULL);
-	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Spawning driver `%s' (%s) failed: %s.",
-		    drv->name, drv->binary_path, str_error(rc));
-		return false;
-	}
-	
-	drv->state = DRIVER_STARTING;
-	return true;
-}
-
-/** Find device driver in the list of device drivers.
- *
- * @param drv_list	The list of device drivers.
- * @param drv_name	The name of the device driver which is searched.
- * @return		The device driver of the specified name, if it is in the
- *			list, NULL otherwise.
- */
-driver_t *find_driver(driver_list_t *drv_list, const char *drv_name)
-{
-	driver_t *res = NULL;
-	
-	fibril_mutex_lock(&drv_list->drivers_mutex);
-	
-	list_foreach(drv_list->drivers, drivers, driver_t, drv) {
-		if (str_cmp(drv->name, drv_name) == 0) {
-			res = drv;
-			break;
-		}
-	}
-	
-	fibril_mutex_unlock(&drv_list->drivers_mutex);
-	
-	return res;
-}
-
-/** Notify driver about the devices to which it was assigned.
- *
- * @param driver	The driver to which the devices are passed.
- */
-static void pass_devices_to_driver(driver_t *driver, dev_tree_t *tree)
-{
-	dev_node_t *dev;
-	link_t *link;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver(driver=\"%s\")",
-	    driver->name);
-
-	fibril_mutex_lock(&driver->driver_mutex);
-
-	/*
-	 * Go through devices list as long as there is some device
-	 * that has not been passed to the driver.
-	 */
-	link = driver->devices.head.next;
-	while (link != &driver->devices.head) {
-		dev = list_get_instance(link, dev_node_t, driver_devices);
-		fibril_rwlock_write_lock(&tree->rwlock);
-		
-		if (dev->passed_to_driver) {
-			fibril_rwlock_write_unlock(&tree->rwlock);
-			link = link->next;
-			continue;
-		}
-
-		log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver: dev->refcnt=%d\n",
-		    (int)atomic_get(&dev->refcnt));
-		dev_add_ref(dev);
-
-		/*
-		 * Unlock to avoid deadlock when adding device
-		 * handled by itself.
-		 */
-		fibril_mutex_unlock(&driver->driver_mutex);
-		fibril_rwlock_write_unlock(&tree->rwlock);
-
-		add_device(driver, dev, tree);
-
-		dev_del_ref(dev);
-
-		/*
-		 * Lock again as we will work with driver's
-		 * structure.
-		 */
-		fibril_mutex_lock(&driver->driver_mutex);
-
-		/*
-		 * Restart the cycle to go through all devices again.
-		 */
-		link = driver->devices.head.next;
-	}
-
-	/*
-	 * Once we passed all devices to the driver, we need to mark the
-	 * driver as running.
-	 * It is vital to do it here and inside critical section.
-	 *
-	 * If we would change the state earlier, other devices added to
-	 * the driver would be added to the device list and started
-	 * immediately and possibly started here as well.
-	 */
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' enters running state.", driver->name);
-	driver->state = DRIVER_RUNNING;
-
-	fibril_mutex_unlock(&driver->driver_mutex);
-}
-
-/** Finish the initialization of a driver after it has succesfully started
- * and after it has registered itself by the device manager.
- *
- * Pass devices formerly matched to the driver to the driver and remember the
- * driver is running and fully functional now.
- *
- * @param driver	The driver which registered itself as running by the
- *			device manager.
- */
-void initialize_running_driver(driver_t *driver, dev_tree_t *tree)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "initialize_running_driver(driver=\"%s\")",
-	    driver->name);
-	
-	/*
-	 * Pass devices which have been already assigned to the driver to the
-	 * driver.
-	 */
-	pass_devices_to_driver(driver, tree);
-}
-
-/** Initialize device driver structure.
- *
- * @param drv		The device driver structure.
- */
-void init_driver(driver_t *drv)
-{
-	assert(drv != NULL);
-
-	memset(drv, 0, sizeof(driver_t));
-	list_initialize(&drv->match_ids.ids);
-	list_initialize(&drv->devices);
-	fibril_mutex_initialize(&drv->driver_mutex);
-	drv->sess = NULL;
-}
-
-/** Device driver structure clean-up.
- *
- * @param drv		The device driver structure.
- */
-void clean_driver(driver_t *drv)
-{
-	assert(drv != NULL);
-
-	free(drv->name);
-	free(drv->binary_path);
-
-	clean_match_ids(&drv->match_ids);
-
-	init_driver(drv);
-}
-
-/** Delete device driver structure.
- *
- * @param drv		The device driver structure.
- */
-void delete_driver(driver_t *drv)
-{
-	assert(drv != NULL);
-	
-	clean_driver(drv);
-	free(drv);
-}
-
-/** Create loc path and name for the function. */
-void loc_register_tree_function(fun_node_t *fun, dev_tree_t *tree)
-{
-	char *loc_pathname = NULL;
-	char *loc_name = NULL;
-	
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-	
-	asprintf(&loc_name, "%s", fun->pathname);
-	if (loc_name == NULL)
-		return;
-	
-	replace_char(loc_name, '/', LOC_SEPARATOR);
-	
-	asprintf(&loc_pathname, "%s/%s", LOC_DEVICE_NAMESPACE,
-	    loc_name);
-	if (loc_pathname == NULL) {
-		free(loc_name);
-		return;
-	}
-	
-	loc_service_register_with_iface(loc_pathname,
-	    &fun->service_id, DEVMAN_CONNECT_FROM_LOC);
-	
-	tree_add_loc_function(tree, fun);
-	
-	free(loc_name);
-	free(loc_pathname);
-}
-
-/** Pass a device to running driver.
- *
- * @param drv		The driver's structure.
- * @param node		The device's node in the device tree.
- */
-void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
-{
-	/*
-	 * We do not expect to have driver's mutex locked as we do not
-	 * access any structures that would affect driver_t.
-	 */
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
-	    drv->name, dev->pfun->name);
-	
-	/* Send the device to the driver. */
-	devman_handle_t parent_handle;
-	if (dev->pfun) {
-		parent_handle = dev->pfun->handle;
-	} else {
-		parent_handle = 0;
-	}
-	
-	async_exch_t *exch = async_exchange_begin(drv->sess);
-	
-	ipc_call_t answer;
-	aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
-	    parent_handle, &answer);
-	
-	/* Send the device name to the driver. */
-	sysarg_t rc = async_data_write_start(exch, dev->pfun->name,
-	    str_size(dev->pfun->name) + 1);
-	
-	async_exchange_end(exch);
-	
-	if (rc != EOK) {
-		/* TODO handle error */
-	}
-
-	/* Wait for answer from the driver. */
-	async_wait_for(req, &rc);
-
-	switch(rc) {
-	case EOK:
-		dev->state = DEVICE_USABLE;
-		break;
-	case ENOENT:
-		dev->state = DEVICE_NOT_PRESENT;
-		break;
-	default:
-		dev->state = DEVICE_INVALID;
-		break;
-	}
-	
-	dev->passed_to_driver = true;
-
-	return;
-}
-
-/** Find suitable driver for a device and assign the driver to it.
- *
- * @param node		The device node of the device in the device tree.
- * @param drivers_list	The list of available drivers.
- * @return		True if the suitable driver is found and
- *			successfully assigned to the device, false otherwise.
- */
-bool assign_driver(dev_node_t *dev, driver_list_t *drivers_list,
-    dev_tree_t *tree)
-{
-	assert(dev != NULL);
-	assert(drivers_list != NULL);
-	assert(tree != NULL);
-	
-	/*
-	 * Find the driver which is the most suitable for handling this device.
-	 */
-	driver_t *drv = find_best_match_driver(drivers_list, dev);
-	if (drv == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "No driver found for device `%s'.",
-		    dev->pfun->pathname);
-		return false;
-	}
-	
-	/* Attach the driver to the device. */
-	attach_driver(tree, dev, drv);
-	
-	fibril_mutex_lock(&drv->driver_mutex);
-	if (drv->state == DRIVER_NOT_STARTED) {
-		/* Start the driver. */
-		start_driver(drv);
-	}
-	bool is_running = drv->state == DRIVER_RUNNING;
-	fibril_mutex_unlock(&drv->driver_mutex);
-
-	/* Notify the driver about the new device. */
-	if (is_running)
-		add_device(drv, dev, tree);
-	
-	fibril_mutex_lock(&drv->driver_mutex);
-	fibril_mutex_unlock(&drv->driver_mutex);
-
-	fibril_rwlock_write_lock(&tree->rwlock);
-	if (dev->pfun != NULL) {
-		dev->pfun->state = FUN_ON_LINE;
-	}
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	return true;
-}
-
-int driver_dev_remove(dev_tree_t *tree, dev_node_t *dev)
-{
-	async_exch_t *exch;
-	sysarg_t retval;
-	driver_t *drv;
-	devman_handle_t handle;
-	
-	assert(dev != NULL);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_remove(%p)", dev);
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	drv = dev->drv;
-	handle = dev->handle;
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	exch = async_exchange_begin(drv->sess);
-	retval = async_req_1_0(exch, DRIVER_DEV_REMOVE, handle);
-	async_exchange_end(exch);
-	
-	return retval;
-}
-
-int driver_dev_gone(dev_tree_t *tree, dev_node_t *dev)
-{
-	async_exch_t *exch;
-	sysarg_t retval;
-	driver_t *drv;
-	devman_handle_t handle;
-	
-	assert(dev != NULL);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_gone(%p)", dev);
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	drv = dev->drv;
-	handle = dev->handle;
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	exch = async_exchange_begin(drv->sess);
-	retval = async_req_1_0(exch, DRIVER_DEV_GONE, handle);
-	async_exchange_end(exch);
-	
-	return retval;
-}
-
-int driver_fun_online(dev_tree_t *tree, fun_node_t *fun)
-{
-	async_exch_t *exch;
-	sysarg_t retval;
-	driver_t *drv;
-	devman_handle_t handle;
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_online(%p)", fun);
-
-	fibril_rwlock_read_lock(&tree->rwlock);
-	
-	if (fun->dev == NULL) {
-		/* XXX root function? */
-		fibril_rwlock_read_unlock(&tree->rwlock);
-		return EINVAL;
-	}
-	
-	drv = fun->dev->drv;
-	handle = fun->handle;
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	exch = async_exchange_begin(drv->sess);
-	retval = async_req_1_0(exch, DRIVER_FUN_ONLINE, handle);
-	loc_exchange_end(exch);
-	
-	return retval;
-}
-
-int driver_fun_offline(dev_tree_t *tree, fun_node_t *fun)
-{
-	async_exch_t *exch;
-	sysarg_t retval;
-	driver_t *drv;
-	devman_handle_t handle;
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_offline(%p)", fun);
-
-	fibril_rwlock_read_lock(&tree->rwlock);
-	if (fun->dev == NULL) {
-		/* XXX root function? */
-		fibril_rwlock_read_unlock(&tree->rwlock);
-		return EINVAL;
-	}
-	
-	drv = fun->dev->drv;
-	handle = fun->handle;
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	exch = async_exchange_begin(drv->sess);
-	retval = async_req_1_0(exch, DRIVER_FUN_OFFLINE, handle);
-	loc_exchange_end(exch);
-	
-	return retval;
-
-}
-
-/** Initialize the device tree.
- *
- * Create root device node of the tree and assign driver to it.
- *
- * @param tree		The device tree.
- * @param drivers_list	the list of available drivers.
- * @return		True on success, false otherwise.
- */
-bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "init_device_tree()");
-	
-	tree->current_handle = 0;
-	
-	hash_table_create(&tree->devman_devices, 0, 0, &devman_devices_ops);
-	hash_table_create(&tree->devman_functions, 0, 0, &devman_functions_ops);
-	hash_table_create(&tree->loc_functions, 0, 0, &loc_devices_ops);
-	
-	fibril_rwlock_initialize(&tree->rwlock);
-	
-	/* Create root function and root device and add them to the device tree. */
-	if (!create_root_nodes(tree))
-		return false;
-    
-	/* Find suitable driver and start it. */
-	dev_node_t *rdev = tree->root_node->child;
-	dev_add_ref(rdev);
-	int rc = assign_driver(rdev, drivers_list, tree);
-	dev_del_ref(rdev);
-	
-	return rc;
-}
-
-/* Device nodes */
-
-/** Create a new device node.
- *
- * @return		A device node structure.
- */
-dev_node_t *create_dev_node(void)
-{
-	dev_node_t *dev;
-	
-	dev = calloc(1, sizeof(dev_node_t));
-	if (dev == NULL)
-		return NULL;
-	
-	atomic_set(&dev->refcnt, 0);
-	list_initialize(&dev->functions);
-	link_initialize(&dev->driver_devices);
-	
-	return dev;
-}
-
-/** Delete a device node.
- *
- * @param node		The device node structure.
- */
-void delete_dev_node(dev_node_t *dev)
-{
-	assert(list_empty(&dev->functions));
-	assert(dev->pfun == NULL);
-	assert(dev->drv == NULL);
-	
-	free(dev);
-}
-
-/** Increase device node reference count.
- *
- * @param dev	Device node
- */
-void dev_add_ref(dev_node_t *dev)
-{
-	atomic_inc(&dev->refcnt);
-}
-
-/** Decrease device node reference count.
- *
- * When the count drops to zero the device node is freed.
- *
- * @param dev	Device node
- */
-void dev_del_ref(dev_node_t *dev)
-{
-	if (atomic_predec(&dev->refcnt) == 0)
-		delete_dev_node(dev);
-}
-
-/** Find the device node structure of the device witch has the specified handle.
- *
- * @param tree		The device tree where we look for the device node.
- * @param handle	The handle of the device.
- * @return		The device node.
- */
-dev_node_t *find_dev_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
-{
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-	
-	ht_link_t *link = hash_table_find(&tree->devman_devices, &handle);
-	if (link == NULL)
-		return NULL;
-	
-	return hash_table_get_inst(link, dev_node_t, devman_dev);
-}
-
-/** Find the device node structure of the device witch has the specified handle.
- *
- * @param tree		The device tree where we look for the device node.
- * @param handle	The handle of the device.
- * @return		The device node.
- */
-dev_node_t *find_dev_node(dev_tree_t *tree, devman_handle_t handle)
-{
-	dev_node_t *dev = NULL;
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	dev = find_dev_node_no_lock(tree, handle);
-	if (dev != NULL)
-		dev_add_ref(dev);
-	
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	return dev;
-}
-
-/** Get list of device functions. */
-int dev_get_functions(dev_tree_t *tree, dev_node_t *dev,
-    devman_handle_t *hdl_buf, size_t buf_size, size_t *act_size)
-{
-	size_t act_cnt;
-	size_t buf_cnt;
-
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-
-	buf_cnt = buf_size / sizeof(devman_handle_t);
-
-	act_cnt = list_count(&dev->functions);
-	*act_size = act_cnt * sizeof(devman_handle_t);
-
-	if (buf_size % sizeof(devman_handle_t) != 0)
-		return EINVAL;
-
-	size_t pos = 0;
-	list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
-		if (pos < buf_cnt) {
-			hdl_buf[pos] = fun->handle;
-		}
-
-		pos++;
-	}
-
-	return EOK;
-}
-
-
-/* Function nodes */
-
-/** Create a new function node.
- *
- * @return		A function node structure.
- */
-fun_node_t *create_fun_node(void)
-{
-	fun_node_t *fun;
-
-	fun = calloc(1, sizeof(fun_node_t));
-	if (fun == NULL)
-		return NULL;
-	
-	fun->state = FUN_INIT;
-	atomic_set(&fun->refcnt, 0);
-	fibril_mutex_initialize(&fun->busy_lock);
-	link_initialize(&fun->dev_functions);
-	list_initialize(&fun->match_ids.ids);
-	
-	return fun;
-}
-
-/** Delete a function node.
- *
- * @param fun		The device node structure.
- */
-void delete_fun_node(fun_node_t *fun)
-{
-	assert(fun->dev == NULL);
-	assert(fun->child == NULL);
-	
-	clean_match_ids(&fun->match_ids);
-	free(fun->name);
-	free(fun->pathname);
-	free(fun);
-}
-
-/** Increase function node reference count.
- *
- * @param fun	Function node
- */
-void fun_add_ref(fun_node_t *fun)
-{
-	atomic_inc(&fun->refcnt);
-}
-
-/** Decrease function node reference count.
- *
- * When the count drops to zero the function node is freed.
- *
- * @param fun	Function node
- */
-void fun_del_ref(fun_node_t *fun)
-{
-	if (atomic_predec(&fun->refcnt) == 0)
-		delete_fun_node(fun);
-}
-
-/** Make function busy for reconfiguration operations. */
-void fun_busy_lock(fun_node_t *fun)
-{
-	fibril_mutex_lock(&fun->busy_lock);
-}
-
-/** Mark end of reconfiguration operation. */
-void fun_busy_unlock(fun_node_t *fun)
-{
-	fibril_mutex_unlock(&fun->busy_lock);
-}
-
-/** Find the function node with the specified handle.
- *
- * @param tree		The device tree where we look for the device node.
- * @param handle	The handle of the function.
- * @return		The function node.
- */
-fun_node_t *find_fun_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
-{
-	fun_node_t *fun;
-	
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-	
-	ht_link_t *link = hash_table_find(&tree->devman_functions, &handle);
-	if (link == NULL)
-		return NULL;
-	
-	fun = hash_table_get_inst(link, fun_node_t, devman_fun);
-	
-	return fun;
-}
-
-/** Find the function node with the specified handle.
- *
- * @param tree		The device tree where we look for the device node.
- * @param handle	The handle of the function.
- * @return		The function node.
- */
-fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle)
-{
-	fun_node_t *fun = NULL;
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	
-	fun = find_fun_node_no_lock(tree, handle);
-	if (fun != NULL)
-		fun_add_ref(fun);
-	
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	return fun;
-}
-
-/** Create and set device's full path in device tree.
- *
- * @param tree		Device tree
- * @param node		The device's device node.
- * @param parent	The parent device node.
- * @return		True on success, false otherwise (insufficient
- *			resources etc.).
- */
-static bool set_fun_path(dev_tree_t *tree, fun_node_t *fun, fun_node_t *parent)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	assert(fun->name != NULL);
-	
-	size_t pathsize = (str_size(fun->name) + 1);
-	if (parent != NULL)
-		pathsize += str_size(parent->pathname) + 1;
-	
-	fun->pathname = (char *) malloc(pathsize);
-	if (fun->pathname == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate device path.");
-		return false;
-	}
-	
-	if (parent != NULL) {
-		str_cpy(fun->pathname, pathsize, parent->pathname);
-		str_append(fun->pathname, pathsize, "/");
-		str_append(fun->pathname, pathsize, fun->name);
-	} else {
-		str_cpy(fun->pathname, pathsize, fun->name);
-	}
-	
-	return true;
-}
-
-/** Insert new device into device tree.
- *
- * @param tree		The device tree.
- * @param dev		The newly added device node.
- * @param pfun		The parent function node.
- *
- * @return		True on success, false otherwise (insufficient resources
- *			etc.).
- */
-bool insert_dev_node(dev_tree_t *tree, dev_node_t *dev, fun_node_t *pfun)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
-	    dev, pfun, pfun->pathname);
-
-	/* Add the node to the handle-to-node map. */
-	dev->handle = ++tree->current_handle;
-	hash_table_insert(&tree->devman_devices, &dev->devman_dev);
-
-	/* Add the node to the list of its parent's children. */
-	dev->pfun = pfun;
-	pfun->child = dev;
-	
-	return true;
-}
-
-/** Remove device from device tree.
- *
- * @param tree		Device tree
- * @param dev		Device node
- */
-void remove_dev_node(dev_tree_t *tree, dev_node_t *dev)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
-	
-	/* Remove node from the handle-to-node map. */
-	hash_table_remove(&tree->devman_devices, &dev->handle);
-	
-	/* Unlink from parent function. */
-	dev->pfun->child = NULL;
-	dev->pfun = NULL;
-	
-	dev->state = DEVICE_REMOVED;
-}
-
-
-/** Insert new function into device tree.
- *
- * @param tree		The device tree.
- * @param fun		The newly added function node. 
- * @param fun_name	The name of the newly added function.
- * @param dev		Owning device node.
- *
- * @return		True on success, false otherwise (insufficient resources
- *			etc.).
- */
-bool insert_fun_node(dev_tree_t *tree, fun_node_t *fun, char *fun_name,
-    dev_node_t *dev)
-{
-	fun_node_t *pfun;
-	
-	assert(fun_name != NULL);
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	/*
-	 * The root function is a special case, it does not belong to any
-	 * device so for the root function dev == NULL.
-	 */
-	pfun = (dev != NULL) ? dev->pfun : NULL;
-	
-	fun->name = fun_name;
-	if (!set_fun_path(tree, fun, pfun)) {
-		return false;
-	}
-	
-	/* Add the node to the handle-to-node map. */
-	fun->handle = ++tree->current_handle;
-	hash_table_insert(&tree->devman_functions, &fun->devman_fun);
-
-	/* Add the node to the list of its parent's children. */
-	fun->dev = dev;
-	if (dev != NULL)
-		list_append(&fun->dev_functions, &dev->functions);
-	
-	return true;
-}
-
-/** Remove function from device tree.
- *
- * @param tree		Device tree
- * @param node		Function node to remove
- */
-void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	/* Remove the node from the handle-to-node map. */
-	hash_table_remove(&tree->devman_functions, &fun->handle);
-	
-	/* Remove the node from the list of its parent's children. */
-	if (fun->dev != NULL)
-		list_remove(&fun->dev_functions);
-	
-	fun->dev = NULL;
-	fun->state = FUN_REMOVED;
-}
-
-/** Find function node with a specified path in the device tree.
- * 
- * @param path		The path of the function node in the device tree.
- * @param tree		The device tree.
- * @return		The function node if it is present in the tree, NULL
- *			otherwise.
- */
-fun_node_t *find_fun_node_by_path(dev_tree_t *tree, char *path)
-{
-	assert(path != NULL);
-
-	bool is_absolute = path[0] == '/';
-	if (!is_absolute) {
-		return NULL;
-	}
-
-	fibril_rwlock_read_lock(&tree->rwlock);
-	
-	fun_node_t *fun = tree->root_node;
-	fun_add_ref(fun);
-	/*
-	 * Relative path to the function from its parent (but with '/' at the
-	 * beginning)
-	 */
-	char *rel_path = path;
-	char *next_path_elem = NULL;
-	bool cont = (rel_path[1] != '\0');
-	
-	while (cont && fun != NULL) {
-		next_path_elem  = get_path_elem_end(rel_path + 1);
-		if (next_path_elem[0] == '/') {
-			cont = true;
-			next_path_elem[0] = 0;
-		} else {
-			cont = false;
-		}
-		
-		fun_node_t *cfun = find_node_child(tree, fun, rel_path + 1);
-		fun_del_ref(fun);
-		fun = cfun;
-		
-		if (cont) {
-			/* Restore the original path. */
-			next_path_elem[0] = '/';
-		}
-		rel_path = next_path_elem;
-	}
-	
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	return fun;
-}
-
-/** Find function with a specified name belonging to given device.
- *
- * Device tree rwlock should be held at least for reading.
- *
- * @param tree Device tree
- * @param dev Device the function belongs to.
- * @param name Function name (not path).
- * @return Function node.
- * @retval NULL No function with given name.
- */
-fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *dev,
-    const char *name)
-{
-	assert(name != NULL);
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-
-	list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
-		if (str_cmp(name, fun->name) == 0) {
-			fun_add_ref(fun);
-			return fun;
-		}
-	}
-
-	return NULL;
-}
-
-/** Find child function node with a specified name.
- *
- * Device tree rwlock should be held at least for reading.
- *
- * @param tree		Device tree
- * @param parent	The parent function node.
- * @param name		The name of the child function.
- * @return		The child function node.
- */
-static fun_node_t *find_node_child(dev_tree_t *tree, fun_node_t *pfun,
-    const char *name)
-{
-	return find_fun_node_in_device(tree, pfun->child, name);
-}
-
-/* loc devices */
-
-fun_node_t *find_loc_tree_function(dev_tree_t *tree, service_id_t service_id)
-{
-	fun_node_t *fun = NULL;
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	ht_link_t *link = hash_table_find(&tree->loc_functions, &service_id);
-	if (link != NULL) {
-		fun = hash_table_get_inst(link, fun_node_t, loc_fun);
-		fun_add_ref(fun);
-	}
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	return fun;
-}
-
-void tree_add_loc_function(dev_tree_t *tree, fun_node_t *fun)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	hash_table_insert(&tree->loc_functions, &fun->loc_fun);
-}
-
-/** @}
- */
Index: uspace/srv/devman/devman.h
===================================================================
--- uspace/srv/devman/devman.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/devman/devman.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -35,8 +35,5 @@
 #define DEVMAN_H_
 
-#include <assert.h>
 #include <stdbool.h>
-#include <dirent.h>
-#include <str.h>
 #include <adt/list.h>
 #include <adt/hash_table.h>
@@ -51,6 +48,4 @@
 #define NAME "devman"
 
-#define MATCH_EXT ".ma"
-
 #define LOC_DEVICE_NAMESPACE "devices"
 #define LOC_SEPARATOR '\\'
@@ -67,22 +62,10 @@
 } client_t;
 
-typedef enum {
-	/** Driver has not been started. */
-	DRIVER_NOT_STARTED = 0,
-	
-	/**
-	 * Driver has been started, but has not registered as running and ready
-	 * to receive requests.
-	 */
-	DRIVER_STARTING,
-	
-	/** Driver is running and prepared to serve incomming requests. */
-	DRIVER_RUNNING
-} driver_state_t;
-
 /** Representation of device driver. */
 typedef struct driver {
 	/** Pointers to previous and next drivers in a linked list. */
 	link_t drivers;
+	/** Handle */
+	devman_handle_t handle;
 	
 	/**
@@ -90,5 +73,5 @@
 	 * and prepared to receive requests.
 	 */
-	int state;
+	driver_state_t state;
 	
 	/** Session asociated with this driver. */
@@ -115,4 +98,6 @@
 	/** Fibril mutex for list of drivers. */
 	fibril_mutex_t drivers_mutex;
+	/** Next free handle */
+	devman_handle_t next_handle;
 } driver_list_t;
 
@@ -238,84 +223,4 @@
 } dev_tree_t;
 
-/* Match ids and scores */
-
-extern int get_match_score(driver_t *, dev_node_t *);
-
-extern bool parse_match_ids(char *, match_id_list_t *);
-extern bool read_match_ids(const char *, match_id_list_t *);
-extern char *read_match_id(char **);
-extern char *read_id(const char **);
-
-/* Drivers */
-
-extern void init_driver_list(driver_list_t *);
-extern driver_t *create_driver(void);
-extern bool get_driver_info(const char *, const char *, driver_t *);
-extern int lookup_available_drivers(driver_list_t *, const char *);
-
-extern driver_t *find_best_match_driver(driver_list_t *, dev_node_t *);
-extern bool assign_driver(dev_node_t *, driver_list_t *, dev_tree_t *);
-
-extern void add_driver(driver_list_t *, driver_t *);
-extern void attach_driver(dev_tree_t *, dev_node_t *, driver_t *);
-extern void detach_driver(dev_tree_t *, dev_node_t *);
-extern void add_device(driver_t *, dev_node_t *, dev_tree_t *);
-extern bool start_driver(driver_t *);
-extern int driver_dev_remove(dev_tree_t *, dev_node_t *);
-extern int driver_dev_gone(dev_tree_t *, dev_node_t *);
-extern int driver_fun_online(dev_tree_t *, fun_node_t *);
-extern int driver_fun_offline(dev_tree_t *, fun_node_t *);
-
-extern driver_t *find_driver(driver_list_t *, const char *);
-extern void initialize_running_driver(driver_t *, dev_tree_t *);
-
-extern void init_driver(driver_t *);
-extern void clean_driver(driver_t *);
-extern void delete_driver(driver_t *);
-
-/* Device nodes */
-
-extern dev_node_t *create_dev_node(void);
-extern void delete_dev_node(dev_node_t *node);
-extern void dev_add_ref(dev_node_t *);
-extern void dev_del_ref(dev_node_t *);
-
-extern dev_node_t *find_dev_node_no_lock(dev_tree_t *tree,
-    devman_handle_t handle);
-extern dev_node_t *find_dev_node(dev_tree_t *tree, devman_handle_t handle);
-extern dev_node_t *find_dev_function(dev_node_t *, const char *);
-extern int dev_get_functions(dev_tree_t *tree, dev_node_t *, devman_handle_t *,
-    size_t, size_t *);
-
-extern fun_node_t *create_fun_node(void);
-extern void delete_fun_node(fun_node_t *);
-extern void fun_add_ref(fun_node_t *);
-extern void fun_del_ref(fun_node_t *);
-extern void fun_busy_lock(fun_node_t *);
-extern void fun_busy_unlock(fun_node_t *);
-extern fun_node_t *find_fun_node_no_lock(dev_tree_t *tree,
-    devman_handle_t handle);
-extern fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle);
-extern fun_node_t *find_fun_node_by_path(dev_tree_t *, char *);
-extern fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *,
-    const char *);
-
-/* Device tree */
-
-extern bool init_device_tree(dev_tree_t *, driver_list_t *);
-extern bool create_root_nodes(dev_tree_t *);
-extern bool insert_dev_node(dev_tree_t *, dev_node_t *, fun_node_t *);
-extern void remove_dev_node(dev_tree_t *, dev_node_t *);
-extern bool insert_fun_node(dev_tree_t *, fun_node_t *, char *, dev_node_t *);
-extern void remove_fun_node(dev_tree_t *, fun_node_t *);
-
-/* Loc services */
-
-extern void loc_register_tree_function(fun_node_t *, dev_tree_t *);
-
-extern fun_node_t *find_loc_tree_function(dev_tree_t *, service_id_t);
-
-extern void tree_add_loc_function(dev_tree_t *, fun_node_t *);
-
 #endif
 
Index: uspace/srv/devman/devtree.c
===================================================================
--- uspace/srv/devman/devtree.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/devtree.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * 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 devman
+ * @{
+ */
+
+#include <errno.h>
+#include <io/log.h>
+
+#include "dev.h"
+#include "devtree.h"
+#include "devman.h"
+#include "driver.h"
+#include "fun.h"
+
+/* hash table operations */
+
+static inline size_t handle_key_hash(void *key)
+{
+	devman_handle_t handle = *(devman_handle_t*)key;
+	return handle;
+}
+
+static size_t devman_devices_hash(const ht_link_t *item)
+{
+	dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
+	return handle_key_hash(&dev->handle);
+}
+
+static size_t devman_functions_hash(const ht_link_t *item)
+{
+	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
+	return handle_key_hash(&fun->handle);
+}
+
+static bool devman_devices_key_equal(void *key, const ht_link_t *item)
+{
+	devman_handle_t handle = *(devman_handle_t*)key;
+	dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
+	return dev->handle == handle;
+}
+
+static bool devman_functions_key_equal(void *key, const ht_link_t *item)
+{
+	devman_handle_t handle = *(devman_handle_t*)key;
+	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
+	return fun->handle == handle;
+}
+
+static inline size_t service_id_key_hash(void *key)
+{
+	service_id_t service_id = *(service_id_t*)key;
+	return service_id;
+}
+
+static size_t loc_functions_hash(const ht_link_t *item)
+{
+	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
+	return service_id_key_hash(&fun->service_id);
+}
+
+static bool loc_functions_key_equal(void *key, const ht_link_t *item)
+{
+	service_id_t service_id = *(service_id_t*)key;
+	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
+	return fun->service_id == service_id;
+}
+
+
+static hash_table_ops_t devman_devices_ops = {
+	.hash = devman_devices_hash,
+	.key_hash = handle_key_hash,
+	.key_equal = devman_devices_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+static hash_table_ops_t devman_functions_ops = {
+	.hash = devman_functions_hash,
+	.key_hash = handle_key_hash,
+	.key_equal = devman_functions_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+static hash_table_ops_t loc_devices_ops = {
+	.hash = loc_functions_hash,
+	.key_hash = service_id_key_hash,
+	.key_equal = loc_functions_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+/** Create root device and function node in the device tree.
+ *
+ * @param tree	The device tree.
+ * @return	True on success, false otherwise.
+ */
+bool create_root_nodes(dev_tree_t *tree)
+{
+	fun_node_t *fun;
+	dev_node_t *dev;
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "create_root_nodes()");
+	
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	/*
+	 * Create root function. This is a pseudo function to which
+	 * the root device node is attached. It allows us to match
+	 * the root device driver in a standard manner, i.e. against
+	 * the parent function.
+	 */
+	
+	fun = create_fun_node();
+	if (fun == NULL) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		return false;
+	}
+	
+	fun_add_ref(fun);
+	insert_fun_node(tree, fun, str_dup(""), NULL);
+	
+	match_id_t *id = create_match_id();
+	id->id = str_dup("root");
+	id->score = 100;
+	add_match_id(&fun->match_ids, id);
+	tree->root_node = fun;
+	
+	/*
+	 * Create root device node.
+	 */
+	dev = create_dev_node();
+	if (dev == NULL) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		return false;
+	}
+	
+	dev_add_ref(dev);
+	insert_dev_node(tree, dev, fun);
+	
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	
+	return dev != NULL;
+}
+
+/** Initialize the device tree.
+ *
+ * Create root device node of the tree and assign driver to it.
+ *
+ * @param tree		The device tree.
+ * @param drivers_list	the list of available drivers.
+ * @return		True on success, false otherwise.
+ */
+bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "init_device_tree()");
+	
+	tree->current_handle = 0;
+	
+	hash_table_create(&tree->devman_devices, 0, 0, &devman_devices_ops);
+	hash_table_create(&tree->devman_functions, 0, 0, &devman_functions_ops);
+	hash_table_create(&tree->loc_functions, 0, 0, &loc_devices_ops);
+	
+	fibril_rwlock_initialize(&tree->rwlock);
+	
+	/* Create root function and root device and add them to the device tree. */
+	if (!create_root_nodes(tree))
+		return false;
+    
+	/* Find suitable driver and start it. */
+	dev_node_t *rdev = tree->root_node->child;
+	dev_add_ref(rdev);
+	int rc = assign_driver(rdev, drivers_list, tree);
+	dev_del_ref(rdev);
+	
+	return rc;
+}
+
+/** Insert new device into device tree.
+ *
+ * @param tree		The device tree.
+ * @param dev		The newly added device node.
+ * @param pfun		The parent function node.
+ *
+ * @return		True on success, false otherwise (insufficient resources
+ *			etc.).
+ */
+bool insert_dev_node(dev_tree_t *tree, dev_node_t *dev, fun_node_t *pfun)
+{
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
+	    dev, pfun, pfun->pathname);
+
+	/* Add the node to the handle-to-node map. */
+	dev->handle = ++tree->current_handle;
+	hash_table_insert(&tree->devman_devices, &dev->devman_dev);
+
+	/* Add the node to the list of its parent's children. */
+	dev->pfun = pfun;
+	pfun->child = dev;
+	
+	return true;
+}
+
+/** Remove device from device tree.
+ *
+ * @param tree		Device tree
+ * @param dev		Device node
+ */
+void remove_dev_node(dev_tree_t *tree, dev_node_t *dev)
+{
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
+	
+	/* Remove node from the handle-to-node map. */
+	hash_table_remove(&tree->devman_devices, &dev->handle);
+	
+	/* Unlink from parent function. */
+	dev->pfun->child = NULL;
+	dev->pfun = NULL;
+	
+	dev->state = DEVICE_REMOVED;
+}
+
+
+/** Insert new function into device tree.
+ *
+ * @param tree		The device tree.
+ * @param fun		The newly added function node. 
+ * @param fun_name	The name of the newly added function.
+ * @param dev		Owning device node.
+ *
+ * @return		True on success, false otherwise (insufficient resources
+ *			etc.).
+ */
+bool insert_fun_node(dev_tree_t *tree, fun_node_t *fun, char *fun_name,
+    dev_node_t *dev)
+{
+	fun_node_t *pfun;
+	
+	assert(fun_name != NULL);
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	/*
+	 * The root function is a special case, it does not belong to any
+	 * device so for the root function dev == NULL.
+	 */
+	pfun = (dev != NULL) ? dev->pfun : NULL;
+	
+	fun->name = fun_name;
+	if (!set_fun_path(tree, fun, pfun)) {
+		return false;
+	}
+	
+	/* Add the node to the handle-to-node map. */
+	fun->handle = ++tree->current_handle;
+	hash_table_insert(&tree->devman_functions, &fun->devman_fun);
+
+	/* Add the node to the list of its parent's children. */
+	fun->dev = dev;
+	if (dev != NULL)
+		list_append(&fun->dev_functions, &dev->functions);
+	
+	return true;
+}
+
+/** Remove function from device tree.
+ *
+ * @param tree		Device tree
+ * @param node		Function node to remove
+ */
+void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
+{
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	/* Remove the node from the handle-to-node map. */
+	hash_table_remove(&tree->devman_functions, &fun->handle);
+	
+	/* Remove the node from the list of its parent's children. */
+	if (fun->dev != NULL)
+		list_remove(&fun->dev_functions);
+	
+	fun->dev = NULL;
+	fun->state = FUN_REMOVED;
+}
+
+/** @}
+ */
Index: uspace/srv/devman/devtree.h
===================================================================
--- uspace/srv/devman/devtree.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/devtree.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * 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 devman
+ * @{
+ */
+
+#ifndef DEVTREE_H_
+#define DEVTREE_H_
+
+#include <stdbool.h>
+
+#include "devman.h"
+
+extern bool init_device_tree(dev_tree_t *, driver_list_t *);
+extern bool create_root_nodes(dev_tree_t *);
+extern bool insert_dev_node(dev_tree_t *, dev_node_t *, fun_node_t *);
+extern void remove_dev_node(dev_tree_t *, dev_node_t *);
+extern bool insert_fun_node(dev_tree_t *, fun_node_t *, char *, dev_node_t *);
+extern void remove_fun_node(dev_tree_t *, fun_node_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/driver.c
===================================================================
--- uspace/srv/devman/driver.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/driver.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,764 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * 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 devman
+ * @{
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <io/log.h>
+#include <ipc/driver.h>
+#include <loc.h>
+#include <str_error.h>
+#include <stdio.h>
+
+#include "dev.h"
+#include "devman.h"
+#include "driver.h"
+#include "match.h"
+
+/**
+ * Initialize the list of device driver's.
+ *
+ * @param drv_list the list of device driver's.
+ *
+ */
+void init_driver_list(driver_list_t *drv_list)
+{
+	assert(drv_list != NULL);
+	
+	list_initialize(&drv_list->drivers);
+	fibril_mutex_initialize(&drv_list->drivers_mutex);
+	drv_list->next_handle = 1;
+}
+
+/** Allocate and initialize a new driver structure.
+ *
+ * @return	Driver structure.
+ */
+driver_t *create_driver(void)
+{
+	driver_t *res = malloc(sizeof(driver_t));
+	if (res != NULL)
+		init_driver(res);
+	return res;
+}
+
+/** Add a driver to the list of drivers.
+ *
+ * @param drivers_list	List of drivers.
+ * @param drv		Driver structure.
+ */
+void add_driver(driver_list_t *drivers_list, driver_t *drv)
+{
+	fibril_mutex_lock(&drivers_list->drivers_mutex);
+	list_append(&drv->drivers, &drivers_list->drivers);
+	drv->handle = drivers_list->next_handle++;
+	fibril_mutex_unlock(&drivers_list->drivers_mutex);
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' was added to the list of available "
+	    "drivers.", drv->name);
+}
+
+/**
+ * Get information about a driver.
+ *
+ * Each driver has its own directory in the base directory.
+ * The name of the driver's directory is the same as the name of the driver.
+ * The driver's directory contains driver's binary (named as the driver without
+ * extension) and the configuration file with match ids for device-to-driver
+ *  matching (named as the driver with a special extension).
+ *
+ * This function searches for the driver's directory and containing
+ * configuration files. If all the files needed are found, they are parsed and
+ * the information about the driver is stored in the driver's structure.
+ *
+ * @param base_path	The base directory, in which we look for driver's
+ *			subdirectory.
+ * @param name		The name of the driver.
+ * @param drv		The driver structure to fill information in.
+ *
+ * @return		True on success, false otherwise.
+ */
+bool get_driver_info(const char *base_path, const char *name, driver_t *drv)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "get_driver_info(base_path=\"%s\", name=\"%s\")",
+	    base_path, name);
+	
+	assert(base_path != NULL && name != NULL && drv != NULL);
+	
+	bool suc = false;
+	char *match_path = NULL;
+	size_t name_size = 0;
+	
+	/* Read the list of match ids from the driver's configuration file. */
+	match_path = get_abs_path(base_path, name, MATCH_EXT);
+	if (match_path == NULL)
+		goto cleanup;
+	
+	if (!read_match_ids(match_path, &drv->match_ids))
+		goto cleanup;
+	
+	/* Allocate and fill driver's name. */
+	name_size = str_size(name) + 1;
+	drv->name = malloc(name_size);
+	if (drv->name == NULL)
+		goto cleanup;
+	str_cpy(drv->name, name_size, name);
+	
+	/* Initialize path with driver's binary. */
+	drv->binary_path = get_abs_path(base_path, name, "");
+	if (drv->binary_path == NULL)
+		goto cleanup;
+	
+	/* Check whether the driver's binary exists. */
+	struct stat s;
+	if (stat(drv->binary_path, &s) == ENOENT) { /* FIXME!! */
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Driver not found at path `%s'.",
+		    drv->binary_path);
+		goto cleanup;
+	}
+	
+	suc = true;
+	
+cleanup:
+	if (!suc) {
+		free(drv->binary_path);
+		free(drv->name);
+		/* Set the driver structure to the default state. */
+		init_driver(drv);
+	}
+	
+	free(match_path);
+	
+	return suc;
+}
+
+/** Lookup drivers in the directory.
+ *
+ * @param drivers_list	The list of available drivers.
+ * @param dir_path	The path to the directory where we search for drivers.
+ * @return		Number of drivers which were found.
+ */
+int lookup_available_drivers(driver_list_t *drivers_list, const char *dir_path)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "lookup_available_drivers(dir=\"%s\")", dir_path);
+	
+	int drv_cnt = 0;
+	DIR *dir = NULL;
+	struct dirent *diren;
+
+	dir = opendir(dir_path);
+	
+	if (dir != NULL) {
+		driver_t *drv = create_driver();
+		while ((diren = readdir(dir))) {
+			if (get_driver_info(dir_path, diren->d_name, drv)) {
+				add_driver(drivers_list, drv);
+				drv_cnt++;
+				drv = create_driver();
+			}
+		}
+		delete_driver(drv);
+		closedir(dir);
+	}
+	
+	return drv_cnt;
+}
+
+/** Lookup the best matching driver for the specified device in the list of
+ * drivers.
+ *
+ * A match between a device and a driver is found if one of the driver's match
+ * ids match one of the device's match ids. The score of the match is the
+ * product of the driver's and device's score associated with the matching id.
+ * The best matching driver for a device is the driver with the highest score
+ * of the match between the device and the driver.
+ *
+ * @param drivers_list	The list of drivers, where we look for the driver
+ *			suitable for handling the device.
+ * @param node		The device node structure of the device.
+ * @return		The best matching driver or NULL if no matching driver
+ *			is found.
+ */
+driver_t *find_best_match_driver(driver_list_t *drivers_list, dev_node_t *node)
+{
+	driver_t *best_drv = NULL;
+	int best_score = 0, score = 0;
+	
+	fibril_mutex_lock(&drivers_list->drivers_mutex);
+	
+	list_foreach(drivers_list->drivers, drivers, driver_t, drv) {
+		score = get_match_score(drv, node);
+		if (score > best_score) {
+			best_score = score;
+			best_drv = drv;
+		}
+	}
+	
+	fibril_mutex_unlock(&drivers_list->drivers_mutex);
+	
+	return best_drv;
+}
+
+/** Assign a driver to a device.
+ *
+ * @param tree		Device tree
+ * @param node		The device's node in the device tree.
+ * @param drv		The driver.
+ */
+void attach_driver(dev_tree_t *tree, dev_node_t *dev, driver_t *drv)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "attach_driver(dev=\"%s\",drv=\"%s\")",
+	    dev->pfun->pathname, drv->name);
+	
+	fibril_mutex_lock(&drv->driver_mutex);
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	dev->drv = drv;
+	list_append(&dev->driver_devices, &drv->devices);
+	
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	fibril_mutex_unlock(&drv->driver_mutex);
+}
+
+/** Detach driver from device.
+ *
+ * @param tree		Device tree
+ * @param node		The device's node in the device tree.
+ * @param drv		The driver.
+ */
+void detach_driver(dev_tree_t *tree, dev_node_t *dev)
+{
+	driver_t *drv = dev->drv;
+	
+	assert(drv != NULL);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "detach_driver(dev=\"%s\",drv=\"%s\")",
+	    dev->pfun->pathname, drv->name);
+	
+	fibril_mutex_lock(&drv->driver_mutex);
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	dev->drv = NULL;
+	list_remove(&dev->driver_devices);
+	
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	fibril_mutex_unlock(&drv->driver_mutex);
+}
+
+/** Start a driver
+ *
+ * @param drv		The driver's structure.
+ * @return		True if the driver's task is successfully spawned, false
+ *			otherwise.
+ */
+bool start_driver(driver_t *drv)
+{
+	int rc;
+
+	assert(fibril_mutex_is_locked(&drv->driver_mutex));
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "start_driver(drv=\"%s\")", drv->name);
+	
+	rc = task_spawnl(NULL, drv->binary_path, drv->binary_path, NULL);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Spawning driver `%s' (%s) failed: %s.",
+		    drv->name, drv->binary_path, str_error(rc));
+		return false;
+	}
+	
+	drv->state = DRIVER_STARTING;
+	return true;
+}
+
+/** Find device driver by handle.
+ *
+ * @param drv_list	The list of device drivers
+ * @param handle	Driver handle
+ * @return		The device driver, if it is in the list,
+ *			NULL otherwise.
+ */
+driver_t *driver_find(driver_list_t *drv_list, devman_handle_t handle)
+{
+	driver_t *res = NULL;
+	
+	fibril_mutex_lock(&drv_list->drivers_mutex);
+	
+	list_foreach(drv_list->drivers, drivers, driver_t, drv) {
+		if (drv->handle == handle) {
+			res = drv;
+			break;
+		}
+	}
+	
+	fibril_mutex_unlock(&drv_list->drivers_mutex);
+	
+	return res;
+}
+
+
+/** Find device driver by name.
+ *
+ * @param drv_list	The list of device drivers.
+ * @param drv_name	The name of the device driver which is searched.
+ * @return		The device driver of the specified name, if it is in the
+ *			list, NULL otherwise.
+ */
+driver_t *driver_find_by_name(driver_list_t *drv_list, const char *drv_name)
+{
+	driver_t *res = NULL;
+	
+	fibril_mutex_lock(&drv_list->drivers_mutex);
+	
+	list_foreach(drv_list->drivers, drivers, driver_t, drv) {
+		if (str_cmp(drv->name, drv_name) == 0) {
+			res = drv;
+			break;
+		}
+	}
+	
+	fibril_mutex_unlock(&drv_list->drivers_mutex);
+	
+	return res;
+}
+
+/** Notify driver about the devices to which it was assigned.
+ *
+ * @param driver	The driver to which the devices are passed.
+ */
+static void pass_devices_to_driver(driver_t *driver, dev_tree_t *tree)
+{
+	dev_node_t *dev;
+	link_t *link;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver(driver=\"%s\")",
+	    driver->name);
+
+	fibril_mutex_lock(&driver->driver_mutex);
+
+	/*
+	 * Go through devices list as long as there is some device
+	 * that has not been passed to the driver.
+	 */
+	link = driver->devices.head.next;
+	while (link != &driver->devices.head) {
+		dev = list_get_instance(link, dev_node_t, driver_devices);
+		fibril_rwlock_write_lock(&tree->rwlock);
+		
+		if (dev->passed_to_driver) {
+			fibril_rwlock_write_unlock(&tree->rwlock);
+			link = link->next;
+			continue;
+		}
+
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver: dev->refcnt=%d\n",
+		    (int)atomic_get(&dev->refcnt));
+		dev_add_ref(dev);
+
+		/*
+		 * Unlock to avoid deadlock when adding device
+		 * handled by itself.
+		 */
+		fibril_mutex_unlock(&driver->driver_mutex);
+		fibril_rwlock_write_unlock(&tree->rwlock);
+
+		add_device(driver, dev, tree);
+
+		dev_del_ref(dev);
+
+		/*
+		 * Lock again as we will work with driver's
+		 * structure.
+		 */
+		fibril_mutex_lock(&driver->driver_mutex);
+
+		/*
+		 * Restart the cycle to go through all devices again.
+		 */
+		link = driver->devices.head.next;
+	}
+
+	/*
+	 * Once we passed all devices to the driver, we need to mark the
+	 * driver as running.
+	 * It is vital to do it here and inside critical section.
+	 *
+	 * If we would change the state earlier, other devices added to
+	 * the driver would be added to the device list and started
+	 * immediately and possibly started here as well.
+	 */
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' enters running state.", driver->name);
+	driver->state = DRIVER_RUNNING;
+
+	fibril_mutex_unlock(&driver->driver_mutex);
+}
+
+/** Finish the initialization of a driver after it has succesfully started
+ * and after it has registered itself by the device manager.
+ *
+ * Pass devices formerly matched to the driver to the driver and remember the
+ * driver is running and fully functional now.
+ *
+ * @param driver	The driver which registered itself as running by the
+ *			device manager.
+ */
+void initialize_running_driver(driver_t *driver, dev_tree_t *tree)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "initialize_running_driver(driver=\"%s\")",
+	    driver->name);
+	
+	/*
+	 * Pass devices which have been already assigned to the driver to the
+	 * driver.
+	 */
+	pass_devices_to_driver(driver, tree);
+}
+
+/** Initialize device driver structure.
+ *
+ * @param drv		The device driver structure.
+ */
+void init_driver(driver_t *drv)
+{
+	assert(drv != NULL);
+
+	memset(drv, 0, sizeof(driver_t));
+	list_initialize(&drv->match_ids.ids);
+	list_initialize(&drv->devices);
+	fibril_mutex_initialize(&drv->driver_mutex);
+	drv->sess = NULL;
+}
+
+/** Device driver structure clean-up.
+ *
+ * @param drv		The device driver structure.
+ */
+void clean_driver(driver_t *drv)
+{
+	assert(drv != NULL);
+
+	free(drv->name);
+	free(drv->binary_path);
+
+	clean_match_ids(&drv->match_ids);
+
+	init_driver(drv);
+}
+
+/** Delete device driver structure.
+ *
+ * @param drv		The device driver structure.
+ */
+void delete_driver(driver_t *drv)
+{
+	assert(drv != NULL);
+	
+	clean_driver(drv);
+	free(drv);
+}
+
+/** Find suitable driver for a device and assign the driver to it.
+ *
+ * @param node		The device node of the device in the device tree.
+ * @param drivers_list	The list of available drivers.
+ * @return		True if the suitable driver is found and
+ *			successfully assigned to the device, false otherwise.
+ */
+bool assign_driver(dev_node_t *dev, driver_list_t *drivers_list,
+    dev_tree_t *tree)
+{
+	assert(dev != NULL);
+	assert(drivers_list != NULL);
+	assert(tree != NULL);
+	
+	/*
+	 * Find the driver which is the most suitable for handling this device.
+	 */
+	driver_t *drv = find_best_match_driver(drivers_list, dev);
+	if (drv == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "No driver found for device `%s'.",
+		    dev->pfun->pathname);
+		return false;
+	}
+	
+	/* Attach the driver to the device. */
+	attach_driver(tree, dev, drv);
+	
+	fibril_mutex_lock(&drv->driver_mutex);
+	if (drv->state == DRIVER_NOT_STARTED) {
+		/* Start the driver. */
+		start_driver(drv);
+	}
+	bool is_running = drv->state == DRIVER_RUNNING;
+	fibril_mutex_unlock(&drv->driver_mutex);
+
+	/* Notify the driver about the new device. */
+	if (is_running)
+		add_device(drv, dev, tree);
+	
+	fibril_mutex_lock(&drv->driver_mutex);
+	fibril_mutex_unlock(&drv->driver_mutex);
+
+	fibril_rwlock_write_lock(&tree->rwlock);
+	if (dev->pfun != NULL) {
+		dev->pfun->state = FUN_ON_LINE;
+	}
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	return true;
+}
+
+/** Pass a device to running driver.
+ *
+ * @param drv		The driver's structure.
+ * @param node		The device's node in the device tree.
+ */
+void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
+{
+	/*
+	 * We do not expect to have driver's mutex locked as we do not
+	 * access any structures that would affect driver_t.
+	 */
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
+	    drv->name, dev->pfun->name);
+	
+	/* Send the device to the driver. */
+	devman_handle_t parent_handle;
+	if (dev->pfun) {
+		parent_handle = dev->pfun->handle;
+	} else {
+		parent_handle = 0;
+	}
+	
+	async_exch_t *exch = async_exchange_begin(drv->sess);
+	
+	ipc_call_t answer;
+	aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
+	    parent_handle, &answer);
+	
+	/* Send the device name to the driver. */
+	sysarg_t rc = async_data_write_start(exch, dev->pfun->name,
+	    str_size(dev->pfun->name) + 1);
+	
+	async_exchange_end(exch);
+	
+	if (rc != EOK) {
+		/* TODO handle error */
+	}
+
+	/* Wait for answer from the driver. */
+	async_wait_for(req, &rc);
+
+	switch(rc) {
+	case EOK:
+		dev->state = DEVICE_USABLE;
+		break;
+	case ENOENT:
+		dev->state = DEVICE_NOT_PRESENT;
+		break;
+	default:
+		dev->state = DEVICE_INVALID;
+		break;
+	}
+	
+	dev->passed_to_driver = true;
+
+	return;
+}
+
+int driver_dev_remove(dev_tree_t *tree, dev_node_t *dev)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	driver_t *drv;
+	devman_handle_t handle;
+	
+	assert(dev != NULL);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_remove(%p)", dev);
+	
+	fibril_rwlock_read_lock(&tree->rwlock);
+	drv = dev->drv;
+	handle = dev->handle;
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	exch = async_exchange_begin(drv->sess);
+	retval = async_req_1_0(exch, DRIVER_DEV_REMOVE, handle);
+	async_exchange_end(exch);
+	
+	return retval;
+}
+
+int driver_dev_gone(dev_tree_t *tree, dev_node_t *dev)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	driver_t *drv;
+	devman_handle_t handle;
+	
+	assert(dev != NULL);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_gone(%p)", dev);
+	
+	fibril_rwlock_read_lock(&tree->rwlock);
+	drv = dev->drv;
+	handle = dev->handle;
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	exch = async_exchange_begin(drv->sess);
+	retval = async_req_1_0(exch, DRIVER_DEV_GONE, handle);
+	async_exchange_end(exch);
+	
+	return retval;
+}
+
+int driver_fun_online(dev_tree_t *tree, fun_node_t *fun)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	driver_t *drv;
+	devman_handle_t handle;
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_online(%p)", fun);
+
+	fibril_rwlock_read_lock(&tree->rwlock);
+	
+	if (fun->dev == NULL) {
+		/* XXX root function? */
+		fibril_rwlock_read_unlock(&tree->rwlock);
+		return EINVAL;
+	}
+	
+	drv = fun->dev->drv;
+	handle = fun->handle;
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	exch = async_exchange_begin(drv->sess);
+	retval = async_req_1_0(exch, DRIVER_FUN_ONLINE, handle);
+	loc_exchange_end(exch);
+	
+	return retval;
+}
+
+int driver_fun_offline(dev_tree_t *tree, fun_node_t *fun)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	driver_t *drv;
+	devman_handle_t handle;
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_offline(%p)", fun);
+
+	fibril_rwlock_read_lock(&tree->rwlock);
+	if (fun->dev == NULL) {
+		/* XXX root function? */
+		fibril_rwlock_read_unlock(&tree->rwlock);
+		return EINVAL;
+	}
+	
+	drv = fun->dev->drv;
+	handle = fun->handle;
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	exch = async_exchange_begin(drv->sess);
+	retval = async_req_1_0(exch, DRIVER_FUN_OFFLINE, handle);
+	loc_exchange_end(exch);
+	
+	return retval;
+
+}
+
+/** Get list of registered drivers. */
+int driver_get_list(driver_list_t *driver_list, devman_handle_t *hdl_buf,
+    size_t buf_size, size_t *act_size)
+{
+	size_t act_cnt;
+	size_t buf_cnt;
+
+	fibril_mutex_lock(&driver_list->drivers_mutex);
+
+	buf_cnt = buf_size / sizeof(devman_handle_t);
+
+	act_cnt = list_count(&driver_list->drivers);
+	*act_size = act_cnt * sizeof(devman_handle_t);
+
+	if (buf_size % sizeof(devman_handle_t) != 0) {
+		fibril_mutex_unlock(&driver_list->drivers_mutex);
+		return EINVAL;
+	}
+
+	size_t pos = 0;
+	list_foreach(driver_list->drivers, drivers, driver_t, drv) {
+		if (pos < buf_cnt) {
+			hdl_buf[pos] = drv->handle;
+		}
+
+		pos++;
+	}
+
+	fibril_mutex_unlock(&driver_list->drivers_mutex);
+	return EOK;
+}
+
+/** Get list of device functions. */
+int driver_get_devices(driver_t *driver, devman_handle_t *hdl_buf,
+    size_t buf_size, size_t *act_size)
+{
+	size_t act_cnt;
+	size_t buf_cnt;
+
+	fibril_mutex_lock(&driver->driver_mutex);
+
+	buf_cnt = buf_size / sizeof(devman_handle_t);
+
+	act_cnt = list_count(&driver->devices);
+	*act_size = act_cnt * sizeof(devman_handle_t);
+
+	if (buf_size % sizeof(devman_handle_t) != 0) {
+		fibril_mutex_unlock(&driver->driver_mutex);
+		return EINVAL;
+	}
+
+	size_t pos = 0;
+	list_foreach(driver->devices, driver_devices, dev_node_t, dev) {
+		if (pos < buf_cnt) {
+			hdl_buf[pos] = dev->handle;
+		}
+
+		pos++;
+	}
+
+	fibril_mutex_unlock(&driver->driver_mutex);
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/devman/driver.h
===================================================================
--- uspace/srv/devman/driver.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/driver.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * 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 devman
+ * @{
+ */
+
+#ifndef DRIVER_H_
+#define DRIVER_H_
+
+#include <stdbool.h>
+#include "devman.h"
+
+extern void init_driver_list(driver_list_t *);
+extern driver_t *create_driver(void);
+extern bool get_driver_info(const char *, const char *, driver_t *);
+extern int lookup_available_drivers(driver_list_t *, const char *);
+
+extern driver_t *find_best_match_driver(driver_list_t *, dev_node_t *);
+extern bool assign_driver(dev_node_t *, driver_list_t *, dev_tree_t *);
+
+extern void add_driver(driver_list_t *, driver_t *);
+extern void attach_driver(dev_tree_t *, dev_node_t *, driver_t *);
+extern void detach_driver(dev_tree_t *, dev_node_t *);
+extern bool start_driver(driver_t *);
+extern void add_device(driver_t *, dev_node_t *, dev_tree_t *);
+extern int driver_dev_remove(dev_tree_t *, dev_node_t *);
+extern int driver_dev_gone(dev_tree_t *, dev_node_t *);
+extern int driver_fun_online(dev_tree_t *, fun_node_t *);
+extern int driver_fun_offline(dev_tree_t *, fun_node_t *);
+
+extern driver_t *driver_find(driver_list_t *, devman_handle_t);
+extern driver_t *driver_find_by_name(driver_list_t *, const char *);
+extern void initialize_running_driver(driver_t *, dev_tree_t *);
+
+extern void init_driver(driver_t *);
+extern void clean_driver(driver_t *);
+extern void delete_driver(driver_t *);
+extern int driver_get_list(driver_list_t *, devman_handle_t *, size_t, size_t *);
+extern int driver_get_devices(driver_t *, devman_handle_t *, size_t, size_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/drv_conn.c
===================================================================
--- uspace/srv/devman/drv_conn.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/drv_conn.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,655 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * 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.
+ */
+
+/**
+ * @defgroup devman Device manager.
+ * @brief HelenOS device manager.
+ * @{
+ */
+
+/** @file
+ */
+
+#include <assert.h>
+#include <ipc/services.h>
+#include <ns.h>
+#include <async.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+#include <stdbool.h>
+#include <fibril_synch.h>
+#include <stdlib.h>
+#include <str.h>
+#include <io/log.h>
+#include <ipc/devman.h>
+#include <ipc/driver.h>
+#include <loc.h>
+
+#include "client_conn.h"
+#include "dev.h"
+#include "devman.h"
+#include "devtree.h"
+#include "driver.h"
+#include "drv_conn.h"
+#include "fun.h"
+#include "loc.h"
+#include "main.h"
+
+static int init_running_drv(void *drv);
+
+/** Register running driver. */
+static driver_t *devman_driver_register(ipc_callid_t callid, ipc_call_t *call)
+{
+	driver_t *driver = NULL;
+	char *drv_name = NULL;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_driver_register");
+	
+	/* Get driver name. */
+	int rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return NULL;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s' driver is trying to register.",
+	    drv_name);
+	
+	/* Find driver structure. */
+	driver = driver_find_by_name(&drivers_list, drv_name);
+	if (driver == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "No driver named `%s' was found.", drv_name);
+		free(drv_name);
+		drv_name = NULL;
+		async_answer_0(callid, ENOENT);
+		return NULL;
+	}
+	
+	free(drv_name);
+	drv_name = NULL;
+	
+	fibril_mutex_lock(&driver->driver_mutex);
+	
+	if (driver->sess) {
+		/* We already have a connection to the driver. */
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Driver '%s' already started.\n",
+		    driver->name);
+		fibril_mutex_unlock(&driver->driver_mutex);
+		async_answer_0(callid, EEXISTS);
+		return NULL;
+	}
+	
+	switch (driver->state) {
+	case DRIVER_NOT_STARTED:
+		/* Somebody started the driver manually. */
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Driver '%s' started manually.\n",
+		    driver->name);
+		driver->state = DRIVER_STARTING;
+		break;
+	case DRIVER_STARTING:
+		/* The expected case */
+		break;
+	case DRIVER_RUNNING:
+		/* Should not happen since we do not have a connected session */
+		assert(false);
+	}
+	
+	/* Create connection to the driver. */
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Creating connection to the `%s' driver.",
+	    driver->name);
+	driver->sess = async_callback_receive(EXCHANGE_PARALLEL);
+	if (!driver->sess) {
+		fibril_mutex_unlock(&driver->driver_mutex);
+		async_answer_0(callid, ENOTSUP);
+		return NULL;
+	}
+	/* FIXME: Work around problem with callback sessions */
+	async_sess_args_set(driver->sess, DRIVER_DEVMAN, 0, 0);
+	
+	log_msg(LOG_DEFAULT, LVL_NOTE,
+	    "The `%s' driver was successfully registered as running.",
+	    driver->name);
+	
+	/*
+	 * Initialize the driver as running (e.g. pass assigned devices to it)
+	 * in a separate fibril; the separate fibril is used to enable the
+	 * driver to use devman service during the driver's initialization.
+	 */
+	fid_t fid = fibril_create(init_running_drv, driver);
+	if (fid == 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create initialization fibril " \
+		    "for driver `%s'.", driver->name);
+		fibril_mutex_unlock(&driver->driver_mutex);
+		async_answer_0(callid, ENOMEM);
+		return NULL;
+	}
+	
+	fibril_add_ready(fid);
+	fibril_mutex_unlock(&driver->driver_mutex);
+	
+	async_answer_0(callid, EOK);
+	return driver;
+}
+
+/** Receive device match ID from the device's parent driver and add it to the
+ * list of devices match ids.
+ *
+ * @param match_ids	The list of the device's match ids.
+ * @return		Zero on success, negative error code otherwise.
+ */
+static int devman_receive_match_id(match_id_list_t *match_ids)
+{
+	match_id_t *match_id = create_match_id();
+	ipc_callid_t callid;
+	ipc_call_t call;
+	int rc = 0;
+	
+	callid = async_get_call(&call);
+	if (DEVMAN_ADD_MATCH_ID != IPC_GET_IMETHOD(call)) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, 
+		    "Invalid protocol when trying to receive match id.");
+		async_answer_0(callid, EINVAL); 
+		delete_match_id(match_id);
+		return EINVAL;
+	}
+	
+	if (match_id == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate match id.");
+		async_answer_0(callid, ENOMEM);
+		return ENOMEM;
+	}
+	
+	async_answer_0(callid, EOK);
+	
+	match_id->score = IPC_GET_ARG1(call);
+	
+	char *match_id_str;
+	rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
+	match_id->id = match_id_str;
+	if (rc != EOK) {
+		delete_match_id(match_id);
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to receive match id string: %s.",
+		    str_error(rc));
+		return rc;
+	}
+	
+	list_append(&match_id->link, &match_ids->ids);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Received match id `%s', score %d.",
+	    match_id->id, match_id->score);
+	return rc;
+}
+
+/** Receive device match IDs from the device's parent driver and add them to the
+ * list of devices match ids.
+ *
+ * @param match_count	The number of device's match ids to be received.
+ * @param match_ids	The list of the device's match ids.
+ * @return		Zero on success, negative error code otherwise.
+ */
+static int devman_receive_match_ids(sysarg_t match_count,
+    match_id_list_t *match_ids)
+{
+	int ret = EOK;
+	size_t i;
+	
+	for (i = 0; i < match_count; i++) {
+		if (EOK != (ret = devman_receive_match_id(match_ids)))
+			return ret;
+	}
+	return ret;
+}
+
+/** Handle function registration.
+ *
+ * Child devices are registered by their parent's device driver.
+ */
+static void devman_add_function(ipc_callid_t callid, ipc_call_t *call)
+{
+	fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call);
+	devman_handle_t dev_handle = IPC_GET_ARG2(*call);
+	sysarg_t match_count = IPC_GET_ARG3(*call);
+	dev_tree_t *tree = &device_tree;
+	
+	dev_node_t *pdev = find_dev_node(&device_tree, dev_handle);
+	if (pdev == NULL) {
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	if (ftype != fun_inner && ftype != fun_exposed) {
+		/* Unknown function type */
+		log_msg(LOG_DEFAULT, LVL_ERROR, 
+		    "Unknown function type %d provided by driver.",
+		    (int) ftype);
+
+		dev_del_ref(pdev);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	char *fun_name = NULL;
+	int rc = async_data_write_accept((void **)&fun_name, true, 0, 0, 0, 0);
+	if (rc != EOK) {
+		dev_del_ref(pdev);
+		async_answer_0(callid, rc);
+		return;
+	}
+	
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	/* Check device state */
+	if (pdev->state == DEVICE_REMOVED) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		dev_del_ref(pdev);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	/* Check that function with same name is not there already. */
+	fun_node_t *tfun = find_fun_node_in_device(tree, pdev, fun_name);
+	if (tfun) {
+		fun_del_ref(tfun);	/* drop the new unwanted reference */
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		dev_del_ref(pdev);
+		async_answer_0(callid, EEXISTS);
+		printf(NAME ": Warning, driver tried to register `%s' twice.\n",
+		    fun_name);
+		free(fun_name);
+		return;
+	}
+	
+	fun_node_t *fun = create_fun_node();
+	/* One reference for creation, one for us */
+	fun_add_ref(fun);
+	fun_add_ref(fun);
+	fun->ftype = ftype;
+	
+	/*
+	 * We can lock the function here even when holding the tree because
+	 * we know it cannot be held by anyone else yet.
+	 */
+	fun_busy_lock(fun);
+	
+	if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		dev_del_ref(pdev);
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		delete_fun_node(fun);
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+	
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	dev_del_ref(pdev);
+	
+	devman_receive_match_ids(match_count, &fun->match_ids);
+	
+	rc = fun_online(fun);
+	if (rc != EOK) {
+		/* XXX Set some failed state? */
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		async_answer_0(callid, rc);
+		return;
+	}
+	
+	fun_busy_unlock(fun);
+	fun_del_ref(fun);
+	
+	/* Return device handle to parent's driver. */
+	async_answer_1(callid, EOK, fun->handle);
+}
+
+static void devman_add_function_to_cat(ipc_callid_t callid, ipc_call_t *call)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*call);
+	category_id_t cat_id;
+	int rc;
+	
+	/* Get category name. */
+	char *cat_name;
+	rc = async_data_write_accept((void **) &cat_name, true,
+	    0, 0, 0, 0);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+	
+	fun_node_t *fun = find_fun_node(&device_tree, handle);
+	if (fun == NULL) {
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
+	if (rc == EOK) {
+		loc_service_add_to_cat(fun->service_id, cat_id);
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Function `%s' added to category `%s'.",
+		    fun->pathname, cat_name);
+	} else {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding function `%s' to category "
+		    "`%s'.", fun->pathname, cat_name);
+	}
+	
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	fun_del_ref(fun);
+	
+	async_answer_0(callid, rc);
+}
+
+/** Online function by driver request.
+ *
+ */
+static void devman_drv_fun_online(ipc_callid_t iid, ipc_call_t *icall,
+    driver_t *drv)
+{
+	fun_node_t *fun;
+	int rc;
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_drv_fun_online()");
+	
+	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fun_busy_lock(fun);
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	if (fun->dev == NULL || fun->dev->drv != drv) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	
+	rc = fun_offline(fun);
+	if (rc != EOK) {
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		async_answer_0(iid, (sysarg_t) rc);
+		return;
+	}
+	
+	fun_busy_unlock(fun);
+	fun_del_ref(fun);
+	
+	async_answer_0(iid, (sysarg_t) EOK);
+}
+
+
+/** Offline function by driver request.
+ *
+ */
+static void devman_drv_fun_offline(ipc_callid_t iid, ipc_call_t *icall,
+    driver_t *drv)
+{
+	fun_node_t *fun;
+	int rc;
+
+	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fun_busy_lock(fun);
+	
+	fibril_rwlock_write_lock(&device_tree.rwlock);
+	if (fun->dev == NULL || fun->dev->drv != drv) {
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	fibril_rwlock_write_unlock(&device_tree.rwlock);
+	
+	rc = fun_offline(fun);
+	if (rc != EOK) {
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		async_answer_0(iid, (sysarg_t) rc);
+		return;
+	}
+	
+	fun_busy_unlock(fun);
+	fun_del_ref(fun);
+	async_answer_0(iid, (sysarg_t) EOK);
+}
+
+/** Remove function. */
+static void devman_remove_function(ipc_callid_t callid, ipc_call_t *call)
+{
+	devman_handle_t fun_handle = IPC_GET_ARG1(*call);
+	dev_tree_t *tree = &device_tree;
+	int rc;
+	
+	fun_node_t *fun = find_fun_node(&device_tree, fun_handle);
+	if (fun == NULL) {
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	fun_busy_lock(fun);
+	
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
+	
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	if (fun->ftype == fun_inner) {
+		/* This is a surprise removal. Handle possible descendants */
+		if (fun->child != NULL) {
+			dev_node_t *dev = fun->child;
+			device_state_t dev_state;
+			int gone_rc;
+			
+			dev_add_ref(dev);
+			dev_state = dev->state;
+			
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
+			
+			/* If device is owned by driver, inform driver it is gone. */
+			if (dev_state == DEVICE_USABLE)
+				gone_rc = driver_dev_gone(&device_tree, dev);
+			else
+				gone_rc = EOK;
+			
+			fibril_rwlock_read_lock(&device_tree.rwlock);
+			
+			/* Verify that driver succeeded and removed all functions */
+			if (gone_rc != EOK || !list_empty(&dev->functions)) {
+				log_msg(LOG_DEFAULT, LVL_ERROR, "Driver did not remove "
+				    "functions for device that is gone. "
+				    "Device node is now defunct.");
+				
+				/*
+				 * Not much we can do but mark the device
+				 * node as having invalid state. This
+				 * is a driver bug.
+				 */
+				dev->state = DEVICE_INVALID;
+				fibril_rwlock_read_unlock(&device_tree.rwlock);
+				dev_del_ref(dev);
+				if (gone_rc == EOK)
+					gone_rc = ENOTSUP;
+				fun_busy_unlock(fun);
+				fun_del_ref(fun);
+				async_answer_0(callid, gone_rc);
+				return;
+			}
+			
+			driver_t *driver = dev->drv;
+			fibril_rwlock_read_unlock(&device_tree.rwlock);
+			
+			if (driver)
+				detach_driver(&device_tree, dev);
+			
+			fibril_rwlock_write_lock(&device_tree.rwlock);
+			remove_dev_node(&device_tree, dev);
+			
+			/* Delete ref created when node was inserted */
+			dev_del_ref(dev);
+			/* Delete ref created by dev_add_ref(dev) above */
+			dev_del_ref(dev);
+		}
+	} else {
+		if (fun->service_id != 0) {
+			/* Unregister from location service */
+			rc = loc_service_unregister(fun->service_id);
+			if (rc != EOK) {
+				log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree "
+				    "service.");
+				fibril_rwlock_write_unlock(&tree->rwlock);
+				fun_busy_unlock(fun);
+				fun_del_ref(fun);
+				async_answer_0(callid, EIO);
+				return;
+			}
+		}
+	}
+	
+	remove_fun_node(&device_tree, fun);
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	fun_busy_unlock(fun);
+	
+	/* Delete ref added when inserting function into tree */
+	fun_del_ref(fun);
+	/* Delete ref added above when looking up function */
+	fun_del_ref(fun);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function() succeeded.");
+	async_answer_0(callid, EOK);
+}
+
+/** Initialize driver which has registered itself as running and ready.
+ *
+ * The initialization is done in a separate fibril to avoid deadlocks (if the
+ * driver needed to be served by devman during the driver's initialization).
+ */
+static int init_running_drv(void *drv)
+{
+	driver_t *driver = (driver_t *) drv;
+	
+	initialize_running_driver(driver, &device_tree);
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s` driver was successfully initialized.",
+	    driver->name);
+	return 0;
+}
+
+/** Function for handling connections from a driver to the device manager. */
+void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
+{
+	client_t *client;
+	driver_t *driver = NULL;
+	
+	/* Accept the connection. */
+	async_answer_0(iid, EOK);
+	
+	client = async_get_client_data();
+	if (client == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate client data.");
+		return;
+	}
+	
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call))
+			break;
+		
+		if (IPC_GET_IMETHOD(call) != DEVMAN_DRIVER_REGISTER) {
+			fibril_mutex_lock(&client->mutex);
+			driver = client->driver;
+			fibril_mutex_unlock(&client->mutex);
+			if (driver == NULL) {
+				/* First call must be to DEVMAN_DRIVER_REGISTER */
+				async_answer_0(callid, ENOTSUP);
+				continue;
+			}
+		}
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case DEVMAN_DRIVER_REGISTER:
+			fibril_mutex_lock(&client->mutex);
+			if (client->driver != NULL) {
+				fibril_mutex_unlock(&client->mutex);
+				async_answer_0(callid, EINVAL);
+				continue;
+			}
+			client->driver = devman_driver_register(callid, &call);
+			fibril_mutex_unlock(&client->mutex);
+			break;
+		case DEVMAN_ADD_FUNCTION:
+			devman_add_function(callid, &call);
+			break;
+		case DEVMAN_ADD_DEVICE_TO_CATEGORY:
+			devman_add_function_to_cat(callid, &call);
+			break;
+		case DEVMAN_DRV_FUN_ONLINE:
+			devman_drv_fun_online(callid, &call, driver);
+			break;
+		case DEVMAN_DRV_FUN_OFFLINE:
+			devman_drv_fun_offline(callid, &call, driver);
+			break;
+		case DEVMAN_REMOVE_FUNCTION:
+			devman_remove_function(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, EINVAL);
+			break;
+		}
+	}
+}
+
+/** @}
+ */
Index: uspace/srv/devman/drv_conn.h
===================================================================
--- uspace/srv/devman/drv_conn.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/drv_conn.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * 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 devman
+ * @{
+ */
+
+#ifndef DRV_CONN_H_
+#define DRV_CONN_H_
+
+#include <async.h>
+#include "devman.h"
+
+extern void devman_connection_driver(ipc_callid_t, ipc_call_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/fun.c
===================================================================
--- uspace/srv/devman/fun.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/fun.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * 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 devman
+ * @{
+ */
+
+#include <errno.h>
+#include <io/log.h>
+#include <loc.h>
+
+#include "dev.h"
+#include "devman.h"
+#include "devtree.h"
+#include "driver.h"
+#include "fun.h"
+#include "main.h"
+#include "loc.h"
+
+static fun_node_t *find_node_child(dev_tree_t *, fun_node_t *, const char *);
+
+/* Function nodes */
+
+/** Create a new function node.
+ *
+ * @return		A function node structure.
+ */
+fun_node_t *create_fun_node(void)
+{
+	fun_node_t *fun;
+
+	fun = calloc(1, sizeof(fun_node_t));
+	if (fun == NULL)
+		return NULL;
+	
+	fun->state = FUN_INIT;
+	atomic_set(&fun->refcnt, 0);
+	fibril_mutex_initialize(&fun->busy_lock);
+	link_initialize(&fun->dev_functions);
+	list_initialize(&fun->match_ids.ids);
+	
+	return fun;
+}
+
+/** Delete a function node.
+ *
+ * @param fun		The device node structure.
+ */
+void delete_fun_node(fun_node_t *fun)
+{
+	assert(fun->dev == NULL);
+	assert(fun->child == NULL);
+	
+	clean_match_ids(&fun->match_ids);
+	free(fun->name);
+	free(fun->pathname);
+	free(fun);
+}
+
+/** Increase function node reference count.
+ *
+ * @param fun	Function node
+ */
+void fun_add_ref(fun_node_t *fun)
+{
+	atomic_inc(&fun->refcnt);
+}
+
+/** Decrease function node reference count.
+ *
+ * When the count drops to zero the function node is freed.
+ *
+ * @param fun	Function node
+ */
+void fun_del_ref(fun_node_t *fun)
+{
+	if (atomic_predec(&fun->refcnt) == 0)
+		delete_fun_node(fun);
+}
+
+/** Make function busy for reconfiguration operations. */
+void fun_busy_lock(fun_node_t *fun)
+{
+	fibril_mutex_lock(&fun->busy_lock);
+}
+
+/** Mark end of reconfiguration operation. */
+void fun_busy_unlock(fun_node_t *fun)
+{
+	fibril_mutex_unlock(&fun->busy_lock);
+}
+
+/** Find the function node with the specified handle.
+ *
+ * @param tree		The device tree where we look for the device node.
+ * @param handle	The handle of the function.
+ * @return		The function node.
+ */
+fun_node_t *find_fun_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
+{
+	fun_node_t *fun;
+	
+	assert(fibril_rwlock_is_locked(&tree->rwlock));
+	
+	ht_link_t *link = hash_table_find(&tree->devman_functions, &handle);
+	if (link == NULL)
+		return NULL;
+	
+	fun = hash_table_get_inst(link, fun_node_t, devman_fun);
+	
+	return fun;
+}
+
+/** Find the function node with the specified handle.
+ *
+ * @param tree		The device tree where we look for the device node.
+ * @param handle	The handle of the function.
+ * @return		The function node.
+ */
+fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle)
+{
+	fun_node_t *fun = NULL;
+	
+	fibril_rwlock_read_lock(&tree->rwlock);
+	
+	fun = find_fun_node_no_lock(tree, handle);
+	if (fun != NULL)
+		fun_add_ref(fun);
+	
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	return fun;
+}
+
+/** Create and set device's full path in device tree.
+ *
+ * @param tree		Device tree
+ * @param node		The device's device node.
+ * @param parent	The parent device node.
+ * @return		True on success, false otherwise (insufficient
+ *			resources etc.).
+ */
+bool set_fun_path(dev_tree_t *tree, fun_node_t *fun, fun_node_t *parent)
+{
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	assert(fun->name != NULL);
+	
+	size_t pathsize = (str_size(fun->name) + 1);
+	if (parent != NULL)
+		pathsize += str_size(parent->pathname) + 1;
+	
+	fun->pathname = (char *) malloc(pathsize);
+	if (fun->pathname == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate device path.");
+		return false;
+	}
+	
+	if (parent != NULL) {
+		str_cpy(fun->pathname, pathsize, parent->pathname);
+		str_append(fun->pathname, pathsize, "/");
+		str_append(fun->pathname, pathsize, fun->name);
+	} else {
+		str_cpy(fun->pathname, pathsize, fun->name);
+	}
+	
+	return true;
+}
+
+/** Find function node with a specified path in the device tree.
+ * 
+ * @param path		The path of the function node in the device tree.
+ * @param tree		The device tree.
+ * @return		The function node if it is present in the tree, NULL
+ *			otherwise.
+ */
+fun_node_t *find_fun_node_by_path(dev_tree_t *tree, char *path)
+{
+	assert(path != NULL);
+
+	bool is_absolute = path[0] == '/';
+	if (!is_absolute) {
+		return NULL;
+	}
+
+	fibril_rwlock_read_lock(&tree->rwlock);
+	
+	fun_node_t *fun = tree->root_node;
+	fun_add_ref(fun);
+	/*
+	 * Relative path to the function from its parent (but with '/' at the
+	 * beginning)
+	 */
+	char *rel_path = path;
+	char *next_path_elem = NULL;
+	bool cont = (rel_path[1] != '\0');
+	
+	while (cont && fun != NULL) {
+		next_path_elem  = get_path_elem_end(rel_path + 1);
+		if (next_path_elem[0] == '/') {
+			cont = true;
+			next_path_elem[0] = 0;
+		} else {
+			cont = false;
+		}
+		
+		fun_node_t *cfun = find_node_child(tree, fun, rel_path + 1);
+		fun_del_ref(fun);
+		fun = cfun;
+		
+		if (cont) {
+			/* Restore the original path. */
+			next_path_elem[0] = '/';
+		}
+		rel_path = next_path_elem;
+	}
+	
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	return fun;
+}
+
+/** Find function with a specified name belonging to given device.
+ *
+ * Device tree rwlock should be held at least for reading.
+ *
+ * @param tree Device tree
+ * @param dev Device the function belongs to.
+ * @param name Function name (not path).
+ * @return Function node.
+ * @retval NULL No function with given name.
+ */
+fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *dev,
+    const char *name)
+{
+	assert(name != NULL);
+	assert(fibril_rwlock_is_locked(&tree->rwlock));
+
+	list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
+		if (str_cmp(name, fun->name) == 0) {
+			fun_add_ref(fun);
+			return fun;
+		}
+	}
+
+	return NULL;
+}
+
+/** Find child function node with a specified name.
+ *
+ * Device tree rwlock should be held at least for reading.
+ *
+ * @param tree		Device tree
+ * @param parent	The parent function node.
+ * @param name		The name of the child function.
+ * @return		The child function node.
+ */
+static fun_node_t *find_node_child(dev_tree_t *tree, fun_node_t *pfun,
+    const char *name)
+{
+	return find_fun_node_in_device(tree, pfun->child, name);
+}
+
+static int assign_driver_fibril(void *arg)
+{
+	dev_node_t *dev_node = (dev_node_t *) arg;
+	assign_driver(dev_node, &drivers_list, &device_tree);
+
+	/* Delete one reference we got from the caller. */
+	dev_del_ref(dev_node);
+	return EOK;
+}
+
+int fun_online(fun_node_t *fun)
+{
+	dev_node_t *dev;
+	
+	fibril_rwlock_write_lock(&device_tree.rwlock);
+	
+	if (fun->state == FUN_ON_LINE) {
+		fibril_rwlock_write_unlock(&device_tree.rwlock);
+		log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already on line.",
+		    fun->pathname);
+		return EOK;
+	}
+	
+	if (fun->ftype == fun_inner) {
+		dev = create_dev_node();
+		if (dev == NULL) {
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
+			return ENOMEM;
+		}
+		
+		insert_dev_node(&device_tree, dev, fun);
+		dev_add_ref(dev);
+	}
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
+	
+	if (fun->ftype == fun_inner) {
+		dev = fun->child;
+		assert(dev != NULL);
+		
+		/* Give one reference over to assign_driver_fibril(). */
+		dev_add_ref(dev);
+		
+		/*
+		 * Try to find a suitable driver and assign it to the device.  We do
+		 * not want to block the current fibril that is used for processing
+		 * incoming calls: we will launch a separate fibril to handle the
+		 * driver assigning. That is because assign_driver can actually include
+		 * task spawning which could take some time.
+		 */
+		fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
+		if (assign_fibril == 0) {
+			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for "
+			    "assigning driver.");
+			/* XXX Cleanup */
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
+			return ENOMEM;
+		}
+		fibril_add_ready(assign_fibril);
+	} else
+		loc_register_tree_function(fun, &device_tree);
+	
+	fibril_rwlock_write_unlock(&device_tree.rwlock);
+	
+	return EOK;
+}
+
+int fun_offline(fun_node_t *fun)
+{
+	int rc;
+	
+	fibril_rwlock_write_lock(&device_tree.rwlock);
+	
+	if (fun->state == FUN_OFF_LINE) {
+		fibril_rwlock_write_unlock(&device_tree.rwlock);
+		log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.",
+		    fun->pathname);
+		return EOK;
+	}
+	
+	if (fun->ftype == fun_inner) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
+		    fun->pathname);
+		
+		if (fun->child != NULL) {
+			dev_node_t *dev = fun->child;
+			device_state_t dev_state;
+			
+			dev_add_ref(dev);
+			dev_state = dev->state;
+			
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
+
+			/* If device is owned by driver, ask driver to give it up. */
+			if (dev_state == DEVICE_USABLE) {
+				rc = driver_dev_remove(&device_tree, dev);
+				if (rc != EOK) {
+					dev_del_ref(dev);
+					return ENOTSUP;
+				}
+			}
+			
+			/* Verify that driver removed all functions */
+			fibril_rwlock_read_lock(&device_tree.rwlock);
+			if (!list_empty(&dev->functions)) {
+				fibril_rwlock_read_unlock(&device_tree.rwlock);
+				dev_del_ref(dev);
+				return EIO;
+			}
+			
+			driver_t *driver = dev->drv;
+			fibril_rwlock_read_unlock(&device_tree.rwlock);
+			
+			if (driver)
+				detach_driver(&device_tree, dev);
+			
+			fibril_rwlock_write_lock(&device_tree.rwlock);
+			remove_dev_node(&device_tree, dev);
+			
+			/* Delete ref created when node was inserted */
+			dev_del_ref(dev);
+			/* Delete ref created by dev_add_ref(dev) above */
+			dev_del_ref(dev);
+		}
+	} else {
+		/* Unregister from location service */
+		rc = loc_service_unregister(fun->service_id);
+		if (rc != EOK) {
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
+			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service.");
+			return EIO;
+		}
+		
+		fun->service_id = 0;
+	}
+	
+	fun->state = FUN_OFF_LINE;
+	fibril_rwlock_write_unlock(&device_tree.rwlock);
+	
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/devman/fun.h
===================================================================
--- uspace/srv/devman/fun.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/fun.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * 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 devman
+ * @{
+ */
+
+#ifndef FUN_H_
+#define FUN_H_
+
+#include "devman.h"
+
+extern fun_node_t *create_fun_node(void);
+extern void delete_fun_node(fun_node_t *);
+extern void fun_add_ref(fun_node_t *);
+extern void fun_del_ref(fun_node_t *);
+extern void fun_busy_lock(fun_node_t *);
+extern void fun_busy_unlock(fun_node_t *);
+extern fun_node_t *find_fun_node_no_lock(dev_tree_t *, devman_handle_t);
+extern fun_node_t *find_fun_node(dev_tree_t *, devman_handle_t);
+extern fun_node_t *find_fun_node_by_path(dev_tree_t *, char *);
+extern fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *,
+    const char *);
+extern bool set_fun_path(dev_tree_t *, fun_node_t *, fun_node_t *);
+extern int fun_online(fun_node_t *);
+extern int fun_offline(fun_node_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/loc.c
===================================================================
--- uspace/srv/devman/loc.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/loc.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * 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 devman
+ * @{
+ */
+
+#include <loc.h>
+#include <stdio.h>
+
+#include "devman.h"
+#include "fun.h"
+#include "loc.h"
+
+/** Create loc path and name for the function. */
+void loc_register_tree_function(fun_node_t *fun, dev_tree_t *tree)
+{
+	char *loc_pathname = NULL;
+	char *loc_name = NULL;
+	
+	assert(fibril_rwlock_is_locked(&tree->rwlock));
+	
+	asprintf(&loc_name, "%s", fun->pathname);
+	if (loc_name == NULL)
+		return;
+	
+	replace_char(loc_name, '/', LOC_SEPARATOR);
+	
+	asprintf(&loc_pathname, "%s/%s", LOC_DEVICE_NAMESPACE,
+	    loc_name);
+	if (loc_pathname == NULL) {
+		free(loc_name);
+		return;
+	}
+	
+	loc_service_register_with_iface(loc_pathname,
+	    &fun->service_id, DEVMAN_CONNECT_FROM_LOC);
+	
+	tree_add_loc_function(tree, fun);
+	
+	free(loc_name);
+	free(loc_pathname);
+}
+
+fun_node_t *find_loc_tree_function(dev_tree_t *tree, service_id_t service_id)
+{
+	fun_node_t *fun = NULL;
+	
+	fibril_rwlock_read_lock(&tree->rwlock);
+	ht_link_t *link = hash_table_find(&tree->loc_functions, &service_id);
+	if (link != NULL) {
+		fun = hash_table_get_inst(link, fun_node_t, loc_fun);
+		fun_add_ref(fun);
+	}
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	return fun;
+}
+
+void tree_add_loc_function(dev_tree_t *tree, fun_node_t *fun)
+{
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	hash_table_insert(&tree->loc_functions, &fun->loc_fun);
+}
+
+/** @}
+ */
Index: uspace/srv/devman/loc.h
===================================================================
--- uspace/srv/devman/loc.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/loc.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2011 Jiri Svoboda
+ * 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 devman
+ * @{
+ */
+
+#ifndef LOC_H_
+#define LOC_H_
+
+#include <ipc/loc.h>
+#include "devman.h"
+
+extern void loc_register_tree_function(fun_node_t *, dev_tree_t *);
+extern fun_node_t *find_loc_tree_function(dev_tree_t *, service_id_t);
+extern void tree_add_loc_function(dev_tree_t *, fun_node_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/devman/main.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -36,5 +36,4 @@
  */
 
-#include <inttypes.h>
 #include <assert.h>
 #include <ipc/services.h>
@@ -43,1163 +42,27 @@
 #include <stdio.h>
 #include <errno.h>
-#include <str_error.h>
 #include <stdbool.h>
 #include <fibril_synch.h>
 #include <stdlib.h>
 #include <str.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <sys/stat.h>
 #include <ctype.h>
 #include <io/log.h>
 #include <ipc/devman.h>
 #include <ipc/driver.h>
-#include <thread.h>
 #include <loc.h>
 
+#include "client_conn.h"
+#include "dev.h"
 #include "devman.h"
+#include "devtree.h"
+#include "drv_conn.h"
+#include "driver.h"
+#include "fun.h"
+#include "loc.h"
 
 #define DRIVER_DEFAULT_STORE  "/drv"
 
-static driver_list_t drivers_list;
-static dev_tree_t device_tree;
-
-static int init_running_drv(void *drv);
-
-/** Register running driver. */
-static driver_t *devman_driver_register(ipc_callid_t callid, ipc_call_t *call)
-{
-	driver_t *driver = NULL;
-	char *drv_name = NULL;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_driver_register");
-	
-	/* Get driver name. */
-	int rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return NULL;
-	}
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s' driver is trying to register.",
-	    drv_name);
-	
-	/* Find driver structure. */
-	driver = find_driver(&drivers_list, drv_name);
-	if (driver == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "No driver named `%s' was found.", drv_name);
-		free(drv_name);
-		drv_name = NULL;
-		async_answer_0(callid, ENOENT);
-		return NULL;
-	}
-	
-	free(drv_name);
-	drv_name = NULL;
-	
-	fibril_mutex_lock(&driver->driver_mutex);
-	
-	if (driver->sess) {
-		/* We already have a connection to the driver. */
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Driver '%s' already started.\n",
-		    driver->name);
-		fibril_mutex_unlock(&driver->driver_mutex);
-		async_answer_0(callid, EEXISTS);
-		return NULL;
-	}
-	
-	switch (driver->state) {
-	case DRIVER_NOT_STARTED:
-		/* Somebody started the driver manually. */
-		log_msg(LOG_DEFAULT, LVL_NOTE, "Driver '%s' started manually.\n",
-		    driver->name);
-		driver->state = DRIVER_STARTING;
-		break;
-	case DRIVER_STARTING:
-		/* The expected case */
-		break;
-	case DRIVER_RUNNING:
-		/* Should not happen since we do not have a connected session */
-		assert(false);
-	}
-	
-	/* Create connection to the driver. */
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "Creating connection to the `%s' driver.",
-	    driver->name);
-	driver->sess = async_callback_receive(EXCHANGE_PARALLEL);
-	if (!driver->sess) {
-		fibril_mutex_unlock(&driver->driver_mutex);
-		async_answer_0(callid, ENOTSUP);
-		return NULL;
-	}
-	/* FIXME: Work around problem with callback sessions */
-	async_sess_args_set(driver->sess, DRIVER_DEVMAN, 0, 0);
-	
-	log_msg(LOG_DEFAULT, LVL_NOTE,
-	    "The `%s' driver was successfully registered as running.",
-	    driver->name);
-	
-	/*
-	 * Initialize the driver as running (e.g. pass assigned devices to it)
-	 * in a separate fibril; the separate fibril is used to enable the
-	 * driver to use devman service during the driver's initialization.
-	 */
-	fid_t fid = fibril_create(init_running_drv, driver);
-	if (fid == 0) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create initialization fibril " \
-		    "for driver `%s'.", driver->name);
-		fibril_mutex_unlock(&driver->driver_mutex);
-		async_answer_0(callid, ENOMEM);
-		return NULL;
-	}
-	
-	fibril_add_ready(fid);
-	fibril_mutex_unlock(&driver->driver_mutex);
-	
-	async_answer_0(callid, EOK);
-	return driver;
-}
-
-/** Receive device match ID from the device's parent driver and add it to the
- * list of devices match ids.
- *
- * @param match_ids	The list of the device's match ids.
- * @return		Zero on success, negative error code otherwise.
- */
-static int devman_receive_match_id(match_id_list_t *match_ids)
-{
-	match_id_t *match_id = create_match_id();
-	ipc_callid_t callid;
-	ipc_call_t call;
-	int rc = 0;
-	
-	callid = async_get_call(&call);
-	if (DEVMAN_ADD_MATCH_ID != IPC_GET_IMETHOD(call)) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, 
-		    "Invalid protocol when trying to receive match id.");
-		async_answer_0(callid, EINVAL); 
-		delete_match_id(match_id);
-		return EINVAL;
-	}
-	
-	if (match_id == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate match id.");
-		async_answer_0(callid, ENOMEM);
-		return ENOMEM;
-	}
-	
-	async_answer_0(callid, EOK);
-	
-	match_id->score = IPC_GET_ARG1(call);
-	
-	char *match_id_str;
-	rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
-	match_id->id = match_id_str;
-	if (rc != EOK) {
-		delete_match_id(match_id);
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to receive match id string: %s.",
-		    str_error(rc));
-		return rc;
-	}
-	
-	list_append(&match_id->link, &match_ids->ids);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "Received match id `%s', score %d.",
-	    match_id->id, match_id->score);
-	return rc;
-}
-
-/** Receive device match IDs from the device's parent driver and add them to the
- * list of devices match ids.
- *
- * @param match_count	The number of device's match ids to be received.
- * @param match_ids	The list of the device's match ids.
- * @return		Zero on success, negative error code otherwise.
- */
-static int devman_receive_match_ids(sysarg_t match_count,
-    match_id_list_t *match_ids)
-{
-	int ret = EOK;
-	size_t i;
-	
-	for (i = 0; i < match_count; i++) {
-		if (EOK != (ret = devman_receive_match_id(match_ids)))
-			return ret;
-	}
-	return ret;
-}
-
-static int assign_driver_fibril(void *arg)
-{
-	dev_node_t *dev_node = (dev_node_t *) arg;
-	assign_driver(dev_node, &drivers_list, &device_tree);
-
-	/* Delete one reference we got from the caller. */
-	dev_del_ref(dev_node);
-	return EOK;
-}
-
-static int online_function(fun_node_t *fun)
-{
-	dev_node_t *dev;
-	
-	fibril_rwlock_write_lock(&device_tree.rwlock);
-	
-	if (fun->state == FUN_ON_LINE) {
-		fibril_rwlock_write_unlock(&device_tree.rwlock);
-		log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already on line.",
-		    fun->pathname);
-		return EOK;
-	}
-	
-	if (fun->ftype == fun_inner) {
-		dev = create_dev_node();
-		if (dev == NULL) {
-			fibril_rwlock_write_unlock(&device_tree.rwlock);
-			return ENOMEM;
-		}
-		
-		insert_dev_node(&device_tree, dev, fun);
-		dev_add_ref(dev);
-	}
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
-	
-	if (fun->ftype == fun_inner) {
-		dev = fun->child;
-		assert(dev != NULL);
-		
-		/* Give one reference over to assign_driver_fibril(). */
-		dev_add_ref(dev);
-		
-		/*
-		 * Try to find a suitable driver and assign it to the device.  We do
-		 * not want to block the current fibril that is used for processing
-		 * incoming calls: we will launch a separate fibril to handle the
-		 * driver assigning. That is because assign_driver can actually include
-		 * task spawning which could take some time.
-		 */
-		fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
-		if (assign_fibril == 0) {
-			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for "
-			    "assigning driver.");
-			/* XXX Cleanup */
-			fibril_rwlock_write_unlock(&device_tree.rwlock);
-			return ENOMEM;
-		}
-		fibril_add_ready(assign_fibril);
-	} else
-		loc_register_tree_function(fun, &device_tree);
-	
-	fibril_rwlock_write_unlock(&device_tree.rwlock);
-	
-	return EOK;
-}
-
-static int offline_function(fun_node_t *fun)
-{
-	int rc;
-	
-	fibril_rwlock_write_lock(&device_tree.rwlock);
-	
-	if (fun->state == FUN_OFF_LINE) {
-		fibril_rwlock_write_unlock(&device_tree.rwlock);
-		log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.",
-		    fun->pathname);
-		return EOK;
-	}
-	
-	if (fun->ftype == fun_inner) {
-		log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
-		    fun->pathname);
-		
-		if (fun->child != NULL) {
-			dev_node_t *dev = fun->child;
-			device_state_t dev_state;
-			
-			dev_add_ref(dev);
-			dev_state = dev->state;
-			
-			fibril_rwlock_write_unlock(&device_tree.rwlock);
-
-			/* If device is owned by driver, ask driver to give it up. */
-			if (dev_state == DEVICE_USABLE) {
-				rc = driver_dev_remove(&device_tree, dev);
-				if (rc != EOK) {
-					dev_del_ref(dev);
-					return ENOTSUP;
-				}
-			}
-			
-			/* Verify that driver removed all functions */
-			fibril_rwlock_read_lock(&device_tree.rwlock);
-			if (!list_empty(&dev->functions)) {
-				fibril_rwlock_read_unlock(&device_tree.rwlock);
-				dev_del_ref(dev);
-				return EIO;
-			}
-			
-			driver_t *driver = dev->drv;
-			fibril_rwlock_read_unlock(&device_tree.rwlock);
-			
-			if (driver)
-				detach_driver(&device_tree, dev);
-			
-			fibril_rwlock_write_lock(&device_tree.rwlock);
-			remove_dev_node(&device_tree, dev);
-			
-			/* Delete ref created when node was inserted */
-			dev_del_ref(dev);
-			/* Delete ref created by dev_add_ref(dev) above */
-			dev_del_ref(dev);
-		}
-	} else {
-		/* Unregister from location service */
-		rc = loc_service_unregister(fun->service_id);
-		if (rc != EOK) {
-			fibril_rwlock_write_unlock(&device_tree.rwlock);
-			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service.");
-			return EIO;
-		}
-		
-		fun->service_id = 0;
-	}
-	
-	fun->state = FUN_OFF_LINE;
-	fibril_rwlock_write_unlock(&device_tree.rwlock);
-	
-	return EOK;
-}
-
-/** Handle function registration.
- *
- * Child devices are registered by their parent's device driver.
- */
-static void devman_add_function(ipc_callid_t callid, ipc_call_t *call)
-{
-	fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call);
-	devman_handle_t dev_handle = IPC_GET_ARG2(*call);
-	sysarg_t match_count = IPC_GET_ARG3(*call);
-	dev_tree_t *tree = &device_tree;
-	
-	dev_node_t *pdev = find_dev_node(&device_tree, dev_handle);
-	if (pdev == NULL) {
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	if (ftype != fun_inner && ftype != fun_exposed) {
-		/* Unknown function type */
-		log_msg(LOG_DEFAULT, LVL_ERROR, 
-		    "Unknown function type %d provided by driver.",
-		    (int) ftype);
-
-		dev_del_ref(pdev);
-		async_answer_0(callid, EINVAL);
-		return;
-	}
-	
-	char *fun_name = NULL;
-	int rc = async_data_write_accept((void **)&fun_name, true, 0, 0, 0, 0);
-	if (rc != EOK) {
-		dev_del_ref(pdev);
-		async_answer_0(callid, rc);
-		return;
-	}
-	
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	/* Check device state */
-	if (pdev->state == DEVICE_REMOVED) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		dev_del_ref(pdev);
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	/* Check that function with same name is not there already. */
-	fun_node_t *tfun = find_fun_node_in_device(tree, pdev, fun_name);
-	if (tfun) {
-		fun_del_ref(tfun);	/* drop the new unwanted reference */
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		dev_del_ref(pdev);
-		async_answer_0(callid, EEXISTS);
-		printf(NAME ": Warning, driver tried to register `%s' twice.\n",
-		    fun_name);
-		free(fun_name);
-		return;
-	}
-	
-	fun_node_t *fun = create_fun_node();
-	/* One reference for creation, one for us */
-	fun_add_ref(fun);
-	fun_add_ref(fun);
-	fun->ftype = ftype;
-	
-	/*
-	 * We can lock the function here even when holding the tree because
-	 * we know it cannot be held by anyone else yet.
-	 */
-	fun_busy_lock(fun);
-	
-	if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		dev_del_ref(pdev);
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		delete_fun_node(fun);
-		async_answer_0(callid, ENOMEM);
-		return;
-	}
-	
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	dev_del_ref(pdev);
-	
-	devman_receive_match_ids(match_count, &fun->match_ids);
-	
-	rc = online_function(fun);
-	if (rc != EOK) {
-		/* XXX Set some failed state? */
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		async_answer_0(callid, rc);
-		return;
-	}
-	
-	fun_busy_unlock(fun);
-	fun_del_ref(fun);
-	
-	/* Return device handle to parent's driver. */
-	async_answer_1(callid, EOK, fun->handle);
-}
-
-static void devman_add_function_to_cat(ipc_callid_t callid, ipc_call_t *call)
-{
-	devman_handle_t handle = IPC_GET_ARG1(*call);
-	category_id_t cat_id;
-	int rc;
-	
-	/* Get category name. */
-	char *cat_name;
-	rc = async_data_write_accept((void **) &cat_name, true,
-	    0, 0, 0, 0);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return;
-	}
-	
-	fun_node_t *fun = find_fun_node(&device_tree, handle);
-	if (fun == NULL) {
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-	
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
-	if (rc == EOK) {
-		loc_service_add_to_cat(fun->service_id, cat_id);
-		log_msg(LOG_DEFAULT, LVL_NOTE, "Function `%s' added to category `%s'.",
-		    fun->pathname, cat_name);
-	} else {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding function `%s' to category "
-		    "`%s'.", fun->pathname, cat_name);
-	}
-	
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	fun_del_ref(fun);
-	
-	async_answer_0(callid, rc);
-}
-
-/** Online function by driver request.
- *
- */
-static void devman_drv_fun_online(ipc_callid_t iid, ipc_call_t *icall,
-    driver_t *drv)
-{
-	fun_node_t *fun;
-	int rc;
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_drv_fun_online()");
-	
-	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	fun_busy_lock(fun);
-	
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-	if (fun->dev == NULL || fun->dev->drv != drv) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	
-	rc = online_function(fun);
-	if (rc != EOK) {
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		async_answer_0(iid, (sysarg_t) rc);
-		return;
-	}
-	
-	fun_busy_unlock(fun);
-	fun_del_ref(fun);
-	
-	async_answer_0(iid, (sysarg_t) EOK);
-}
-
-
-/** Offline function by driver request.
- *
- */
-static void devman_drv_fun_offline(ipc_callid_t iid, ipc_call_t *icall,
-    driver_t *drv)
-{
-	fun_node_t *fun;
-	int rc;
-
-	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	fun_busy_lock(fun);
-	
-	fibril_rwlock_write_lock(&device_tree.rwlock);
-	if (fun->dev == NULL || fun->dev->drv != drv) {
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	fibril_rwlock_write_unlock(&device_tree.rwlock);
-	
-	rc = offline_function(fun);
-	if (rc != EOK) {
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		async_answer_0(iid, (sysarg_t) rc);
-		return;
-	}
-	
-	fun_busy_unlock(fun);
-	fun_del_ref(fun);
-	async_answer_0(iid, (sysarg_t) EOK);
-}
-
-/** Remove function. */
-static void devman_remove_function(ipc_callid_t callid, ipc_call_t *call)
-{
-	devman_handle_t fun_handle = IPC_GET_ARG1(*call);
-	dev_tree_t *tree = &device_tree;
-	int rc;
-	
-	fun_node_t *fun = find_fun_node(&device_tree, fun_handle);
-	if (fun == NULL) {
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	fun_busy_lock(fun);
-	
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
-	
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	if (fun->ftype == fun_inner) {
-		/* This is a surprise removal. Handle possible descendants */
-		if (fun->child != NULL) {
-			dev_node_t *dev = fun->child;
-			device_state_t dev_state;
-			int gone_rc;
-			
-			dev_add_ref(dev);
-			dev_state = dev->state;
-			
-			fibril_rwlock_write_unlock(&device_tree.rwlock);
-			
-			/* If device is owned by driver, inform driver it is gone. */
-			if (dev_state == DEVICE_USABLE)
-				gone_rc = driver_dev_gone(&device_tree, dev);
-			else
-				gone_rc = EOK;
-			
-			fibril_rwlock_read_lock(&device_tree.rwlock);
-			
-			/* Verify that driver succeeded and removed all functions */
-			if (gone_rc != EOK || !list_empty(&dev->functions)) {
-				log_msg(LOG_DEFAULT, LVL_ERROR, "Driver did not remove "
-				    "functions for device that is gone. "
-				    "Device node is now defunct.");
-				
-				/*
-				 * Not much we can do but mark the device
-				 * node as having invalid state. This
-				 * is a driver bug.
-				 */
-				dev->state = DEVICE_INVALID;
-				fibril_rwlock_read_unlock(&device_tree.rwlock);
-				dev_del_ref(dev);
-				if (gone_rc == EOK)
-					gone_rc = ENOTSUP;
-				fun_busy_unlock(fun);
-				fun_del_ref(fun);
-				async_answer_0(callid, gone_rc);
-				return;
-			}
-			
-			driver_t *driver = dev->drv;
-			fibril_rwlock_read_unlock(&device_tree.rwlock);
-			
-			if (driver)
-				detach_driver(&device_tree, dev);
-			
-			fibril_rwlock_write_lock(&device_tree.rwlock);
-			remove_dev_node(&device_tree, dev);
-			
-			/* Delete ref created when node was inserted */
-			dev_del_ref(dev);
-			/* Delete ref created by dev_add_ref(dev) above */
-			dev_del_ref(dev);
-		}
-	} else {
-		if (fun->service_id != 0) {
-			/* Unregister from location service */
-			rc = loc_service_unregister(fun->service_id);
-			if (rc != EOK) {
-				log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree "
-				    "service.");
-				fibril_rwlock_write_unlock(&tree->rwlock);
-				fun_busy_unlock(fun);
-				fun_del_ref(fun);
-				async_answer_0(callid, EIO);
-				return;
-			}
-		}
-	}
-	
-	remove_fun_node(&device_tree, fun);
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	fun_busy_unlock(fun);
-	
-	/* Delete ref added when inserting function into tree */
-	fun_del_ref(fun);
-	/* Delete ref added above when looking up function */
-	fun_del_ref(fun);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function() succeeded.");
-	async_answer_0(callid, EOK);
-}
-
-/** Initialize driver which has registered itself as running and ready.
- *
- * The initialization is done in a separate fibril to avoid deadlocks (if the
- * driver needed to be served by devman during the driver's initialization).
- */
-static int init_running_drv(void *drv)
-{
-	driver_t *driver = (driver_t *) drv;
-	
-	initialize_running_driver(driver, &device_tree);
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s` driver was successfully initialized.",
-	    driver->name);
-	return 0;
-}
-
-/** Function for handling connections from a driver to the device manager. */
-static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
-{
-	client_t *client;
-	driver_t *driver = NULL;
-	
-	/* Accept the connection. */
-	async_answer_0(iid, EOK);
-	
-	client = async_get_client_data();
-	if (client == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate client data.");
-		return;
-	}
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			break;
-		
-		if (IPC_GET_IMETHOD(call) != DEVMAN_DRIVER_REGISTER) {
-			fibril_mutex_lock(&client->mutex);
-			driver = client->driver;
-			fibril_mutex_unlock(&client->mutex);
-			if (driver == NULL) {
-				/* First call must be to DEVMAN_DRIVER_REGISTER */
-				async_answer_0(callid, ENOTSUP);
-				continue;
-			}
-		}
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case DEVMAN_DRIVER_REGISTER:
-			fibril_mutex_lock(&client->mutex);
-			if (client->driver != NULL) {
-				fibril_mutex_unlock(&client->mutex);
-				async_answer_0(callid, EINVAL);
-				continue;
-			}
-			client->driver = devman_driver_register(callid, &call);
-			fibril_mutex_unlock(&client->mutex);
-			break;
-		case DEVMAN_ADD_FUNCTION:
-			devman_add_function(callid, &call);
-			break;
-		case DEVMAN_ADD_DEVICE_TO_CATEGORY:
-			devman_add_function_to_cat(callid, &call);
-			break;
-		case DEVMAN_DRV_FUN_ONLINE:
-			devman_drv_fun_online(callid, &call, driver);
-			break;
-		case DEVMAN_DRV_FUN_OFFLINE:
-			devman_drv_fun_offline(callid, &call, driver);
-			break;
-		case DEVMAN_REMOVE_FUNCTION:
-			devman_remove_function(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, EINVAL);
-			break;
-		}
-	}
-}
-
-/** Find handle for the device instance identified by the device's path in the
- * device tree. */
-static void devman_function_get_handle(ipc_callid_t iid, ipc_call_t *icall)
-{
-	char *pathname;
-	devman_handle_t handle;
-	
-	int rc = async_data_write_accept((void **) &pathname, true, 0, 0, 0, 0);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	fun_node_t *fun = find_fun_node_by_path(&device_tree, pathname);
-	
-	free(pathname);
-
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	handle = fun->handle;
-
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-
-	/* Delete reference created above by find_fun_node_by_path() */
-	fun_del_ref(fun);
-
-	async_answer_1(iid, EOK, handle);
-}
-
-/** Get device name. */
-static void devman_fun_get_name(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);
-		fun_del_ref(fun);
-		return;
-	}
-
-	void *buffer = malloc(data_len);
-	if (buffer == NULL) {
-		async_answer_0(data_callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		fun_del_ref(fun);
-		return;
-	}
-
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		free(buffer);
-
-		async_answer_0(data_callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		fun_del_ref(fun);
-		return;
-	}
-
-	size_t sent_length = str_size(fun->name);
-	if (sent_length > data_len) {
-		sent_length = data_len;
-	}
-
-	async_data_read_finalize(data_callid, fun->name, sent_length);
-	async_answer_0(iid, EOK);
-
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	fun_del_ref(fun);
-	free(buffer);
-}
-
-/** Get function driver name. */
-static void devman_fun_get_driver_name(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);
-		fun_del_ref(fun);
-		return;
-	}
-
-	void *buffer = malloc(data_len);
-	if (buffer == NULL) {
-		async_answer_0(data_callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		fun_del_ref(fun);
-		return;
-	}
-
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		free(buffer);
-
-		async_answer_0(data_callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		fun_del_ref(fun);
-		return;
-	}
-
-	/* Check whether function has a driver */
-	if (fun->child == NULL || fun->child->drv == NULL) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		free(buffer);
-
-		async_answer_0(data_callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		fun_del_ref(fun);
-		return;
-	}
-
-	size_t sent_length = str_size(fun->child->drv->name);
-	if (sent_length > data_len) {
-		sent_length = data_len;
-	}
-
-	async_data_read_finalize(data_callid, fun->child->drv->name,
-	    sent_length);
-	async_answer_0(iid, EOK);
-
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	fun_del_ref(fun);
-	free(buffer);
-}
-
-/** Get device path. */
-static void devman_fun_get_path(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);
-		fun_del_ref(fun);
-		return;
-	}
-
-	void *buffer = malloc(data_len);
-	if (buffer == NULL) {
-		async_answer_0(data_callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		fun_del_ref(fun);
-		return;
-	}
-	
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-	
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		free(buffer);
-
-		async_answer_0(data_callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		fun_del_ref(fun);
-		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);
-
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	fun_del_ref(fun);
-	free(buffer);
-}
-
-static void devman_dev_get_functions(ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	size_t act_size;
-	int rc;
-	
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-	
-	dev_node_t *dev = find_dev_node_no_lock(&device_tree,
-	    IPC_GET_ARG1(*icall));
-	if (dev == NULL || dev->state == DEVICE_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
-	if (hdl_buf == NULL) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	rc = dev_get_functions(&device_tree, dev, hdl_buf, size, &act_size);
-	if (rc != EOK) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(callid, rc);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	
-	sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
-	free(hdl_buf);
-	
-	async_answer_1(iid, retval, act_size);
-}
-
-
-/** Get handle for child device of a function. */
-static void devman_fun_get_child(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fun_node_t *fun;
-	
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-	
-	fun = find_fun_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
-	if (fun == NULL || fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	if (fun->child == NULL) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	async_answer_1(iid, EOK, fun->child->handle);
-	
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-}
-
-/** Online function.
- *
- * Send a request to online a function to the responsible driver.
- * The driver may offline other functions if necessary (i.e. if the state
- * of this function is linked to state of another function somehow).
- */
-static void devman_fun_online(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fun_node_t *fun;
-	int rc;
-
-	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	rc = driver_fun_online(&device_tree, fun);
-	fun_del_ref(fun);
-	
-	async_answer_0(iid, (sysarg_t) rc);
-}
-
-/** Offline function.
- *
- * Send a request to offline a function to the responsible driver. As
- * a result the subtree rooted at that function should be cleanly
- * detatched. The driver may offline other functions if necessary
- * (i.e. if the state of this function is linked to state of another
- * function somehow).
- */
-static void devman_fun_offline(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fun_node_t *fun;
-	int rc;
-
-	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	rc = driver_fun_offline(&device_tree, fun);
-	fun_del_ref(fun);
-	
-	async_answer_0(iid, (sysarg_t) rc);
-}
-
-/** Find handle for the function instance identified by its service ID. */
-static void devman_fun_sid_to_handle(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fun_node_t *fun;
-
-	fun = find_loc_tree_function(&device_tree, IPC_GET_ARG1(*icall));
-	
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-
-	async_answer_1(iid, EOK, fun->handle);
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	fun_del_ref(fun);
-}
-
-/** Function for handling connections from a client to the device manager. */
-static void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
-{
-	/* Accept connection. */
-	async_answer_0(iid, EOK);
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			break;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case DEVMAN_DEVICE_GET_HANDLE:
-			devman_function_get_handle(callid, &call);
-			break;
-		case DEVMAN_DEV_GET_FUNCTIONS:
-			devman_dev_get_functions(callid, &call);
-			break;
-		case DEVMAN_FUN_GET_CHILD:
-			devman_fun_get_child(callid, &call);
-			break;
-		case DEVMAN_FUN_GET_NAME:
-			devman_fun_get_name(callid, &call);
-			break;
-		case DEVMAN_FUN_GET_DRIVER_NAME:
-			devman_fun_get_driver_name(callid, &call);
-			break;
-		case DEVMAN_FUN_GET_PATH:
-			devman_fun_get_path(callid, &call);
-			break;
-		case DEVMAN_FUN_ONLINE:
-			devman_fun_online(callid, &call);
-			break;
-		case DEVMAN_FUN_OFFLINE:
-			devman_fun_offline(callid, &call);
-			break;
-		case DEVMAN_FUN_SID_TO_HANDLE:
-			devman_fun_sid_to_handle(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, ENOENT);
-		}
-	}
-}
+driver_list_t drivers_list;
+dev_tree_t device_tree;
 
 static void devman_forward(ipc_callid_t iid, ipc_call_t *icall,
Index: uspace/srv/devman/main.h
===================================================================
--- uspace/srv/devman/main.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/main.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * 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 devman
+ * @{
+ */
+
+#ifndef MAIN_H_
+#define MAIN_H_
+
+#include "devman.h"
+
+extern driver_list_t drivers_list;
+extern dev_tree_t device_tree;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/match.c
===================================================================
--- uspace/srv/devman/match.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/devman/match.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -31,7 +31,13 @@
  */
 
+#include <fcntl.h>
+#include <io/log.h>
 #include <str.h>
+#include <str_error.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include "devman.h"
+#include "match.h"
 
 /** Compute compound score of driver and device.
@@ -93,4 +99,141 @@
 }
 
+/** Read match id at the specified position of a string and set the position in
+ * the string to the first character following the id.
+ *
+ * @param buf		The position in the input string.
+ * @return		The match id.
+ */
+char *read_match_id(char **buf)
+{
+	char *res = NULL;
+	size_t len = get_nonspace_len(*buf);
+	
+	if (len > 0) {
+		res = malloc(len + 1);
+		if (res != NULL) {
+			str_ncpy(res, len + 1, *buf, len);
+			*buf += len;
+		}
+	}
+	
+	return res;
+}
+
+/**
+ * Read match ids and associated match scores from a string.
+ *
+ * Each match score in the string is followed by its match id.
+ * The match ids and match scores are separated by whitespaces.
+ * Neither match ids nor match scores can contain whitespaces.
+ *
+ * @param buf		The string from which the match ids are read.
+ * @param ids		The list of match ids into which the match ids and
+ *			scores are added.
+ * @return		True if at least one match id and associated match score
+ *			was successfully read, false otherwise.
+ */
+bool parse_match_ids(char *buf, match_id_list_t *ids)
+{
+	int score = 0;
+	char *id = NULL;
+	int ids_read = 0;
+	
+	while (true) {
+		/* skip spaces */
+		if (!skip_spaces(&buf))
+			break;
+		
+		/* read score */
+		score = strtoul(buf, &buf, 10);
+		
+		/* skip spaces */
+		if (!skip_spaces(&buf))
+			break;
+		
+		/* read id */
+		id = read_match_id(&buf);
+		if (NULL == id)
+			break;
+		
+		/* create new match_id structure */
+		match_id_t *mid = create_match_id();
+		mid->id = id;
+		mid->score = score;
+		
+		/* add it to the list */
+		add_match_id(ids, mid);
+		
+		ids_read++;
+	}
+	
+	return ids_read > 0;
+}
+
+/**
+ * Read match ids and associated match scores from a file.
+ *
+ * Each match score in the file is followed by its match id.
+ * The match ids and match scores are separated by whitespaces.
+ * Neither match ids nor match scores can contain whitespaces.
+ *
+ * @param buf		The path to the file from which the match ids are read.
+ * @param ids		The list of match ids into which the match ids and
+ *			scores are added.
+ * @return		True if at least one match id and associated match score
+ *			was successfully read, false otherwise.
+ */
+bool read_match_ids(const char *conf_path, match_id_list_t *ids)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "read_match_ids(conf_path=\"%s\")", conf_path);
+	
+	bool suc = false;
+	char *buf = NULL;
+	bool opened = false;
+	int fd;
+	size_t len = 0;
+	
+	fd = open(conf_path, O_RDONLY);
+	if (fd < 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to open `%s' for reading: %s.",
+		    conf_path, str_error(fd));
+		goto cleanup;
+	}
+	opened = true;
+	
+	len = lseek(fd, 0, SEEK_END);
+	lseek(fd, 0, SEEK_SET);
+	if (len == 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Configuration file '%s' is empty.",
+		    conf_path);
+		goto cleanup;
+	}
+	
+	buf = malloc(len + 1);
+	if (buf == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Memory allocation failed when parsing file "
+		    "'%s'.", conf_path);
+		goto cleanup;
+	}
+	
+	ssize_t read_bytes = read_all(fd, buf, len);
+	if (read_bytes <= 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to read file '%s' (%zd).", conf_path,
+		    read_bytes);
+		goto cleanup;
+	}
+	buf[read_bytes] = 0;
+	
+	suc = parse_match_ids(buf, ids);
+	
+cleanup:
+	free(buf);
+	
+	if (opened)
+		close(fd);
+	
+	return suc;
+}
+
 /** @}
  */
Index: uspace/srv/devman/match.h
===================================================================
--- uspace/srv/devman/match.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/match.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * 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 devman
+ * @{
+ */
+
+#ifndef MATCH_H_
+#define MATCH_H_
+
+#include <stdbool.h>
+
+#include "devman.h"
+
+#define MATCH_EXT ".ma"
+
+extern int get_match_score(driver_t *, dev_node_t *);
+extern bool parse_match_ids(char *, match_id_list_t *);
+extern bool read_match_ids(const char *, match_id_list_t *);
+extern char *read_match_id(char **);
+extern char *read_id(const char **);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/util.h
===================================================================
--- uspace/srv/devman/util.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/devman/util.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -43,5 +43,4 @@
 extern bool skip_spaces(char **);
 extern size_t get_nonspace_len(const char *);
-extern void free_not_null(const void *);
 extern void replace_char(char *, char, char);
 
Index: uspace/srv/fs/cdfs/cdfs_ops.c
===================================================================
--- uspace/srv/fs/cdfs/cdfs_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/cdfs/cdfs_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -619,4 +619,25 @@
 }
 
+static int cdfs_size_block(service_id_t service_id, uint32_t *size)
+{
+	*size = BLOCK_SIZE;
+
+	return EOK; 
+}
+
+static int cdfs_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	*count = 0;
+	
+	return EOK;
+}
+
+static int cdfs_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	*count = 0;
+	
+	return EOK;
+}
+
 libfs_ops_t cdfs_libfs_ops = {
 	.root_get = cdfs_root_get,
@@ -635,5 +656,8 @@
 	.is_directory = cdfs_is_directory,
 	.is_file = cdfs_is_file,
-	.service_get = cdfs_service_get
+	.service_get = cdfs_service_get,
+	.size_block = cdfs_size_block,
+	.total_block_count = cdfs_total_block_count,
+	.free_block_count = cdfs_free_block_count
 };
 
Index: uspace/srv/fs/exfat/exfat_ops.c
===================================================================
--- uspace/srv/fs/exfat/exfat_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/exfat/exfat_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -89,4 +89,7 @@
 static bool exfat_is_file(fs_node_t *node);
 static service_id_t exfat_service_get(fs_node_t *node);
+static int exfat_size_block(service_id_t, uint32_t *);
+static int exfat_total_block_count(service_id_t, uint64_t *);
+static int exfat_free_block_count(service_id_t, uint64_t *);
 
 /*
@@ -910,4 +913,81 @@
 }
 
+int exfat_size_block(service_id_t service_id, uint32_t *size)
+{
+	exfat_bs_t *bs;
+	bs = block_bb_get(service_id);
+	*size = BPC(bs);
+
+	return EOK;
+}
+
+int exfat_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	exfat_bs_t *bs;
+	bs = block_bb_get(service_id);
+	*count = DATA_CNT(bs);
+	
+	return EOK;
+}
+
+int exfat_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	fs_node_t *node;
+	exfat_node_t *bmap_node;
+	exfat_bs_t *bs;
+	uint64_t free_block_count = 0;
+	uint64_t block_count;
+	unsigned sector;
+	int rc;
+
+	rc = exfat_total_block_count(service_id, &block_count);
+	if (rc != EOK)
+		goto exit;
+
+	bs = block_bb_get(service_id);
+	node = NULL;
+	rc = exfat_bitmap_get(&node, service_id);
+	if (rc != EOK)
+		goto exit;
+
+	bmap_node = (exfat_node_t *) node->data;
+
+	unsigned const bmap_sectors = ROUND_UP(bmap_node->size, BPS(bs)) /
+	    BPS(bs);
+
+	for (sector = 0; sector < bmap_sectors; ++sector) {
+
+		block_t *block;
+		uint8_t *bitmap;
+		unsigned bit;
+
+		rc = exfat_block_get(&block, bs, bmap_node, sector,
+		    BLOCK_FLAGS_NONE);
+		if (rc != EOK) {
+			free_block_count = 0;
+			goto exit;
+		}
+
+		bitmap = (uint8_t *) block->data;
+
+		for (bit = 0; bit < BPS(bs) * 8 && block_count > 0;
+		    ++bit, --block_count) {
+			if (!(bitmap[bit / 8] & (1 << (bit % 8))))
+				++free_block_count;
+		}
+
+		block_put(block);
+
+		if (block_count == 0) {
+			/* Reached the end of the bitmap */
+			goto exit;
+		}
+	}
+
+exit:
+	exfat_node_put(node);
+	*count = free_block_count;
+	return rc;
+}
 
 /** libfs operations */
@@ -928,5 +1008,8 @@
 	.is_directory = exfat_is_directory,
 	.is_file = exfat_is_file,
-	.service_get = exfat_service_get
+	.service_get = exfat_service_get,
+	.size_block = exfat_size_block,
+	.total_block_count = exfat_total_block_count,
+	.free_block_count = exfat_free_block_count
 };
 
Index: uspace/srv/fs/ext4fs/ext4fs_ops.c
===================================================================
--- uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -101,4 +101,7 @@
 static bool ext4fs_is_file(fs_node_t *node);
 static service_id_t ext4fs_service_get(fs_node_t *node);
+static int ext4fs_size_block(service_id_t, uint32_t *);
+static int ext4fs_total_block_count(service_id_t, uint64_t *);
+static int ext4fs_free_block_count(service_id_t, uint64_t *);
 
 /* Static variables */
@@ -833,4 +836,51 @@
 	ext4fs_node_t *enode = EXT4FS_NODE(fn);
 	return enode->instance->service_id;
+}
+
+int ext4fs_size_block(service_id_t service_id, uint32_t *size)
+{
+	ext4fs_instance_t *inst;
+	int rc = ext4fs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == inst)
+		return ENOENT;
+
+	ext4_superblock_t *sb = inst->filesystem->superblock;
+	*size = ext4_superblock_get_block_size(sb);
+
+	return EOK;
+}
+
+int ext4fs_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	ext4fs_instance_t *inst;
+	int rc = ext4fs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == inst)
+		return ENOENT;
+
+	ext4_superblock_t *sb = inst->filesystem->superblock;
+	*count = ext4_superblock_get_blocks_count(sb);
+
+	return EOK;
+}
+
+int ext4fs_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	ext4fs_instance_t *inst;
+	int rc = ext4fs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+	if (NULL == inst)
+		return ENOENT;
+
+	ext4_superblock_t *sb = inst->filesystem->superblock;
+	*count = ext4_superblock_get_free_blocks_count(sb);
+
+	return EOK;
 }
 
@@ -854,5 +904,8 @@
 	.is_directory = ext4fs_is_directory,
 	.is_file = ext4fs_is_file,
-	.service_get = ext4fs_service_get
+	.service_get = ext4fs_service_get,
+	.size_block = ext4fs_size_block,
+	.total_block_count = ext4fs_total_block_count,
+	.free_block_count = ext4fs_free_block_count
 };
 
Index: uspace/srv/fs/fat/fat_ops.c
===================================================================
--- uspace/srv/fs/fat/fat_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/fat/fat_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -91,4 +91,7 @@
 static bool fat_is_file(fs_node_t *node);
 static service_id_t fat_service_get(fs_node_t *node);
+static int fat_size_block(service_id_t, uint32_t *);
+static int fat_total_block_count(service_id_t, uint64_t *);
+static int fat_free_block_count(service_id_t, uint64_t *);
 
 /*
@@ -841,4 +844,48 @@
 }
 
+int fat_size_block(service_id_t service_id, uint32_t *size)
+{
+	fat_bs_t *bs;
+
+	bs = block_bb_get(service_id);
+	*size = BPC(bs);
+
+	return EOK;
+}
+
+int fat_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	fat_bs_t *bs;
+	
+	bs = block_bb_get(service_id);
+	*count = (SPC(bs)) ? TS(bs) / SPC(bs) : 0;
+
+	return EOK;
+}
+
+int fat_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	fat_bs_t *bs;
+	fat_cluster_t e0;
+	uint64_t block_count;
+	int rc;
+	uint32_t cluster_no, clusters;
+
+	block_count = 0;
+	bs = block_bb_get(service_id);
+	clusters = (SPC(bs)) ? TS(bs) / SPC(bs) : 0;
+	for (cluster_no = 0; cluster_no < clusters; cluster_no++) {
+		rc = fat_get_cluster(bs, service_id, FAT1, cluster_no, &e0);
+		if (rc != EOK)
+			return EIO;
+
+		if (e0 == FAT_CLST_RES0)
+			block_count++;
+	}
+	*count = block_count;
+	
+	return EOK;
+}
+
 /** libfs operations */
 libfs_ops_t fat_libfs_ops = {
@@ -858,5 +905,8 @@
 	.is_directory = fat_is_directory,
 	.is_file = fat_is_file,
-	.service_get = fat_service_get
+	.service_get = fat_service_get,
+	.size_block = fat_size_block,
+	.total_block_count = fat_total_block_count,
+	.free_block_count = fat_free_block_count
 };
 
Index: uspace/srv/fs/mfs/mfs.h
===================================================================
--- uspace/srv/fs/mfs/mfs.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/mfs/mfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -58,4 +58,14 @@
 #endif
 
+#define MFS_BMAP_START_BLOCK(sbi, bid) \
+    ((bid) == BMAP_ZONE ? 2 + (sbi)->ibmap_blocks : 2)
+
+#define MFS_BMAP_SIZE_BITS(sbi, bid) \
+    ((bid) == BMAP_ZONE ? (sbi)->nzones - (sbi)->firstdatazone - 1 : \
+    (sbi)->ninodes - 1)
+
+#define MFS_BMAP_SIZE_BLOCKS(sbi, bid) \
+    ((bid) == BMAP_ZONE ? (sbi)->zbmap_blocks : (sbi)->ibmap_blocks)
+
 typedef uint32_t bitchunk_t;
 
@@ -201,4 +211,11 @@
 mfs_free_zone(struct mfs_instance *inst, uint32_t zone);
 
+extern int
+mfs_count_free_zones(struct mfs_instance *inst, uint32_t *zones);
+
+extern int
+mfs_count_free_inodes(struct mfs_instance *inst, uint32_t *inodes);
+
+
 /* mfs_utils.c */
 extern uint16_t
Index: uspace/srv/fs/mfs/mfs_balloc.c
===================================================================
--- uspace/srv/fs/mfs/mfs_balloc.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/mfs/mfs_balloc.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -44,4 +44,8 @@
 mfs_alloc_bit(struct mfs_instance *inst, uint32_t *idx, bmap_id_t bid);
 
+static int
+mfs_count_free_bits(struct mfs_instance *inst, bmap_id_t bid, uint32_t *free);
+
+
 /**Allocate a new inode.
  *
@@ -102,4 +106,97 @@
 
 	return mfs_free_bit(inst, zone, BMAP_ZONE);
+}
+
+/** Count the number of free zones
+ *
+ * @param inst          Pointer to the instance structure.
+ * @param zones         Pointer to the memory location where the result
+ *                      will be stored.
+ *
+ * @return              EOK on success or a negative error code.
+ */
+int
+mfs_count_free_zones(struct mfs_instance *inst, uint32_t *zones)
+{
+	return mfs_count_free_bits(inst, BMAP_ZONE, zones);
+}
+
+/** Count the number of free inodes
+ *
+ * @param inst          Pointer to the instance structure.
+ * @param zones         Pointer to the memory location where the result
+ *                      will be stored.
+ *
+ * @return              EOK on success or a negative error code.
+ */
+
+int
+mfs_count_free_inodes(struct mfs_instance *inst, uint32_t *inodes)
+{
+	return mfs_count_free_bits(inst, BMAP_INODE, inodes);
+}
+
+/** Count the number of free bits in a bitmap
+ *
+ * @param inst          Pointer to the instance structure.
+ * @param bid           Type of the bitmap (inode or zone).
+ * @param free          Pointer to the memory location where the result
+ *                      will be stores.
+ *
+ * @return              EOK on success or a negative error code.
+ */
+static int
+mfs_count_free_bits(struct mfs_instance *inst, bmap_id_t bid, uint32_t *free)
+{
+	int r;
+	unsigned start_block;
+	unsigned long nblocks;
+	unsigned long nbits;
+	unsigned long block;
+	unsigned long free_bits = 0;
+	bitchunk_t chunk;
+	size_t const bitchunk_bits = sizeof(bitchunk_t) * 8;
+	block_t *b;
+	struct mfs_sb_info *sbi = inst->sbi;
+
+	start_block = MFS_BMAP_START_BLOCK(sbi, bid);
+	nblocks = MFS_BMAP_SIZE_BLOCKS(sbi, bid);
+	nbits = MFS_BMAP_SIZE_BITS(sbi, bid);
+
+	for (block = 0; block < nblocks; ++block) {
+		r = block_get(&b, inst->service_id, block + start_block,
+		    BLOCK_FLAGS_NONE);
+		if (r != EOK)
+			return r;
+
+		size_t i;
+		bitchunk_t *data = (bitchunk_t *) b->data;
+
+		/* Read the bitmap block, chunk per chunk,
+		 * counting the zero bits.
+		 */
+		for (i = 0; i < sbi->block_size / sizeof(bitchunk_t); ++i) {
+			chunk = conv32(sbi->native, data[i]);
+
+			size_t bit;
+			for (bit = 0; bit < bitchunk_bits && nbits > 0;
+			    ++bit, --nbits) {
+				if (!(chunk & (1 << bit)))
+					free_bits++;
+			}
+
+			if (nbits == 0)
+				break;
+		}
+
+		r = block_put(b);
+		if (r != EOK)
+			return r;
+	}
+
+	*free = free_bits;
+	assert(nbits == 0);
+
+	return EOK;
 }
 
@@ -124,7 +221,8 @@
 	sbi = inst->sbi;
 
+	start_block = MFS_BMAP_START_BLOCK(sbi, bid);
+
 	if (bid == BMAP_ZONE) {
 		search = &sbi->zsearch;
-		start_block = 2 + sbi->ibmap_blocks;
 		if (idx > sbi->nzones) {
 			printf(NAME ": Error! Trying to free beyond the "
@@ -135,5 +233,4 @@
 		/* bid == BMAP_INODE */
 		search = &sbi->isearch;
-		start_block = 2;
 		if (idx > sbi->ninodes) {
 			printf(NAME ": Error! Trying to free beyond the "
@@ -192,15 +289,13 @@
 	sbi = inst->sbi;
 
+	start_block = MFS_BMAP_START_BLOCK(sbi, bid);
+	limit = MFS_BMAP_SIZE_BITS(sbi, bid);
+	nblocks = MFS_BMAP_SIZE_BLOCKS(sbi, bid);
+
 	if (bid == BMAP_ZONE) {
 		search = &sbi->zsearch;
-		start_block = 2 + sbi->ibmap_blocks;
-		nblocks = sbi->zbmap_blocks;
-		limit = sbi->nzones - sbi->firstdatazone - 1;
 	} else {
 		/* bid == BMAP_INODE */
 		search = &sbi->isearch;
-		start_block = 2;
-		nblocks = sbi->ibmap_blocks;
-		limit = sbi->ninodes - 1;
 	}
 	bits_per_block = sbi->block_size * 8;
Index: uspace/srv/fs/mfs/mfs_ops.c
===================================================================
--- uspace/srv/fs/mfs/mfs_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/mfs/mfs_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -64,4 +64,7 @@
 static int mfs_check_sanity(struct mfs_sb_info *sbi);
 static bool is_power_of_two(uint32_t n);
+static int mfs_size_block(service_id_t service_id, uint32_t *size);
+static int mfs_total_block_count(service_id_t service_id, uint64_t *count);
+static int mfs_free_block_count(service_id_t service_id, uint64_t *count);
 
 static hash_table_t open_nodes;
@@ -84,5 +87,8 @@
 	.destroy = mfs_destroy_node,
 	.has_children = mfs_has_children,
-	.lnkcnt_get = mfs_lnkcnt_get
+	.lnkcnt_get = mfs_lnkcnt_get,
+	.size_block = mfs_size_block,
+	.total_block_count = mfs_total_block_count,
+	.free_block_count = mfs_free_block_count
 };
 
@@ -1129,4 +1135,59 @@
 }
 
+static int
+mfs_size_block(service_id_t service_id, uint32_t *size)
+{
+	struct mfs_instance *inst;
+	int rc;
+
+	rc = mfs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == inst)
+		return ENOENT;
+	
+	*size = inst->sbi->block_size;
+
+	return EOK;
+}
+
+static int
+mfs_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	struct mfs_instance *inst;
+	int rc;
+	
+	rc = mfs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == inst)
+		return ENOENT;
+	
+	*count = (uint64_t) MFS_BMAP_SIZE_BITS(inst->sbi, BMAP_ZONE);
+
+	return EOK;
+}
+
+static int
+mfs_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	uint32_t block_free;
+	
+	struct mfs_instance *inst;
+	int rc = mfs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == inst)
+		return ENOENT;
+
+	mfs_count_free_zones(inst, &block_free);
+	*count = block_free;
+
+	return EOK;
+}
+
 vfs_out_ops_t mfs_ops = {
 	.mounted = mfs_mounted,
Index: uspace/srv/fs/mfs/mfs_rw.c
===================================================================
--- uspace/srv/fs/mfs/mfs_rw.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/mfs/mfs_rw.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -104,7 +104,8 @@
     bool write_mode, uint32_t w_block)
 {
-	int r, nr_direct;
+	int nr_direct;
 	int ptrs_per_block;
-	uint32_t *ind_zone, *ind2_zone;
+	uint32_t *ind_zone = NULL, *ind2_zone = NULL;
+	int r = EOK;
 
 	struct mfs_ino_info *ino_i = mnode->ino_i;
@@ -130,5 +131,5 @@
 			ino_i->dirty = true;
 		}
-		return EOK;
+		goto out;
 	}
 
@@ -142,5 +143,5 @@
 				r = alloc_zone_and_clear(inst, &zone);
 				if (r != EOK)
-					return r;
+					goto out;
 
 				ino_i->i_izone[0] = zone;
@@ -149,5 +150,5 @@
 				/* Sparse block */
 				*b = 0;
-				return EOK;
+				goto out;
 			}
 		}
@@ -155,5 +156,5 @@
 		r = read_ind_zone(inst, ino_i->i_izone[0], &ind_zone);
 		if (r != EOK)
-			return r;
+			goto out;
 
 		*b = ind_zone[rblock];
@@ -163,5 +164,5 @@
 		}
 
-		goto out_free_ind1;
+		goto out;
 	}
 
@@ -176,5 +177,5 @@
 			r = alloc_zone_and_clear(inst, &zone);
 			if (r != EOK)
-				return r;
+				goto out;
 
 			ino_i->i_izone[1] = zone;
@@ -183,5 +184,5 @@
 			/* Sparse block */
 			*b = 0;
-			return EOK;
+			goto out;
 		}
 	}
@@ -189,5 +190,5 @@
 	r = read_ind_zone(inst, ino_i->i_izone[1], &ind_zone);
 	if (r != EOK)
-		return r;
+		goto out;
 
 	/*
@@ -203,5 +204,5 @@
 			r = alloc_zone_and_clear(inst, &zone);
 			if (r != EOK)
-				goto out_free_ind1;
+				goto out;
 
 			ind_zone[ind2_off] = zone;
@@ -209,7 +210,6 @@
 		} else {
 			/* Sparse block */
-			r = EOK;
 			*b = 0;
-			goto out_free_ind1;
+			goto out;
 		}
 	}
@@ -217,5 +217,5 @@
 	r = read_ind_zone(inst, ind_zone[ind2_off], &ind2_zone);
 	if (r != EOK)
-		goto out_free_ind1;
+		goto out;
 
 	*b = ind2_zone[rblock - (ind2_off * ptrs_per_block)];
@@ -225,8 +225,6 @@
 	}
 
-	r = EOK;
-
+out:
 	free(ind2_zone);
-out_free_ind1:
 	free(ind_zone);
 	return r;
Index: uspace/srv/fs/udf/udf_ops.c
===================================================================
--- uspace/srv/fs/udf/udf_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/udf/udf_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -249,4 +249,33 @@
 }
 
+static int udf_size_block(service_id_t service_id, uint32_t *size)
+{
+	udf_instance_t *instance;
+	int rc = fs_instance_get(service_id, (void **) &instance);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == instance)
+		return ENOENT;
+	
+	*size = instance->volumes[DEFAULT_VOL].logical_block_size;
+	
+	return EOK;
+}
+
+static int udf_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	*count = 0;
+	
+	return EOK;
+}
+
+static int udf_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	*count = 0;
+	
+	return EOK;
+}
+
 libfs_ops_t udf_libfs_ops = {
 	.root_get = udf_root_get,
@@ -265,5 +294,8 @@
 	.is_directory = udf_is_directory,
 	.is_file = udf_is_file,
-	.service_get = udf_service_get
+	.service_get = udf_service_get,
+	.size_block = udf_size_block,
+	.total_block_count = udf_total_block_count,
+	.free_block_count = udf_free_block_count
 };
 
Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/vfs/vfs.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -130,4 +130,7 @@
 			vfs_get_mtab(callid, &call);
 			break;
+		case VFS_IN_STATFS:
+			vfs_statfs(callid, &call);
+			break;
 		default:
 			async_answer_0(callid, ENOTSUP);
Index: uspace/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/vfs/vfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -222,4 +222,5 @@
 extern void vfs_wait_handle(ipc_callid_t, ipc_call_t *);
 extern void vfs_get_mtab(ipc_callid_t, ipc_call_t *);
+extern void vfs_statfs(ipc_callid_t, ipc_call_t *);
 
 #endif
Index: uspace/srv/vfs/vfs_ops.c
===================================================================
--- uspace/srv/vfs/vfs_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/vfs/vfs_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -1412,4 +1412,58 @@
 }
 
+void vfs_statfs(ipc_callid_t rid, ipc_call_t *request)
+{
+	char *path;
+	int rc = async_data_write_accept((void **) &path, true, 0, 0, 0, NULL);
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
+		return;
+	}
+	
+	ipc_callid_t callid;
+	if (!async_data_read_receive(&callid, NULL)) {
+		free(path);
+		async_answer_0(callid, EINVAL);
+		async_answer_0(rid, EINVAL);
+		return;
+	}
+
+	vfs_lookup_res_t lr;
+	fibril_rwlock_read_lock(&namespace_rwlock);
+	rc = vfs_lookup_internal(path, L_NONE, &lr, NULL);
+	free(path);
+	if (rc != EOK) {
+		fibril_rwlock_read_unlock(&namespace_rwlock);
+		async_answer_0(callid, rc);
+		async_answer_0(rid, rc);
+		return;
+	}
+	vfs_node_t *node = vfs_node_get(&lr);
+	if (!node) {
+		fibril_rwlock_read_unlock(&namespace_rwlock);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(rid, ENOMEM);
+		return;
+	}
+
+	fibril_rwlock_read_unlock(&namespace_rwlock);
+
+	async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
+	
+	aid_t msg;
+	msg = async_send_3(exch, VFS_OUT_STATFS, node->service_id,
+	    node->index, false, NULL);
+	async_forward_fast(callid, exch, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
+	
+	vfs_exchange_release(exch);
+	
+	sysarg_t rv;
+	async_wait_for(msg, &rv);
+
+	async_answer_0(rid, rv);
+
+	vfs_node_put(node);
+}
+
 /**
  * @}
