Index: uspace/srv/audio/hound/audio_sink.c
===================================================================
--- uspace/srv/audio/hound/audio_sink.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/audio/hound/audio_sink.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -140,6 +140,5 @@
 
 	pcm_format_silence(dest, size, &sink->format);
-	list_foreach(sink->connections, it) {
-		connection_t * conn = connection_from_sink_list(it);
+	list_foreach(sink->connections, sink_link, connection_t, conn) {
 		const int ret = connection_add_source_data(
 		    conn, dest, size, sink->format);
Index: uspace/srv/audio/hound/audio_source.c
===================================================================
--- uspace/srv/audio/hound/audio_source.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/audio/hound/audio_source.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -106,6 +106,5 @@
 		return ENOMEM;
 
-	list_foreach(source->connections, it) {
-		connection_t *conn = connection_from_source_list(it);
+	list_foreach(source->connections, source_link, connection_t, conn) {
 		const int ret = connection_push_data(conn, adata);
 		if (ret != EOK) {
Index: uspace/srv/audio/hound/connection.h
===================================================================
--- uspace/srv/audio/hound/connection.h	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/audio/hound/connection.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -76,14 +76,4 @@
  * @return pointer to a connection structure, NULL on failure.
  */
-static inline connection_t * connection_from_sink_list(link_t *l)
-{
-	return l ? list_get_instance(l, connection_t, sink_link) : NULL;
-}
-
-/**
- * List instance helper.
- * @param l link
- * @return pointer to a connection structure, NULL on failure.
- */
 static inline connection_t * connection_from_hound_list(link_t *l)
 {
Index: uspace/srv/audio/hound/hound.c
===================================================================
--- uspace/srv/audio/hound/hound.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/audio/hound/hound.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -47,20 +47,4 @@
 #include "str_error.h"
 
-#define FIND_BY_NAME(type) \
-do { \
-	assert(list); \
-	assert(name); \
-	list_foreach(*list, it) { \
-		audio_ ## type ## _t *dev = \
-		    audio_ ## type ## _list_instance(it); \
-		if (str_cmp(name, dev->name) == 0) { \
-			log_debug("%s with name '%s' is in the list", \
-			    #type, name); \
-			return dev; \
-		} \
-	} \
-	return NULL; \
-} while (0)
-
 /**
  * Search devices by name.
@@ -68,7 +52,18 @@
  * @return Pointer to the found device, NULL on failure.
  */
-static audio_device_t * find_device_by_name(list_t *list, const char *name)
-{
-	FIND_BY_NAME(device);
+static audio_device_t *find_device_by_name(list_t *list, const char *name)
+{
+	assert(list);
+	assert(name);
+
+	list_foreach(*list, link, audio_device_t, dev) {
+		if (str_cmp(name, dev->name) == 0) {
+			log_debug("device with name '%s' is in the list",
+			    name);
+			return dev;
+		}
+	}
+
+	return NULL;
 }
 
@@ -78,7 +73,18 @@
  * @return Pointer to the found source, NULL on failure.
  */
-static audio_source_t * find_source_by_name(list_t *list, const char *name)
-{
-	FIND_BY_NAME(source);
+static audio_source_t *find_source_by_name(list_t *list, const char *name)
+{
+	assert(list);
+	assert(name);
+
+	list_foreach(*list, link, audio_source_t, dev) {
+		if (str_cmp(name, dev->name) == 0) {
+			log_debug("source with name '%s' is in the list",
+			    name);
+			return dev;
+		}
+	}
+
+	return NULL;
 }
 
@@ -88,7 +94,18 @@
  * @return Pointer to the found sink, NULL on failure.
  */
-static audio_sink_t * find_sink_by_name(list_t *list, const char *name)
-{
-	FIND_BY_NAME(sink);
+static audio_sink_t *find_sink_by_name(list_t *list, const char *name)
+{
+	assert(list);
+	assert(name);
+
+	list_foreach(*list, link, audio_sink_t, dev) {
+		if (str_cmp(name, dev->name) == 0) {
+			log_debug("sink with name '%s' is in the list",
+			    name);
+			return dev;
+		}
+	}
+
+	return NULL;
 }
 
@@ -111,6 +128,6 @@
 		log_warning("Removing sink '%s' while still connected.", sink->name);
 	while (!list_empty(&sink->connections)) {
-		connection_t *conn =
-		    connection_from_sink_list(list_first(&sink->connections));
+		connection_t *conn = list_get_instance(
+		    list_first(&sink->connections), connection_t, sink_link);
 		list_remove(&conn->hound_link);
 		connection_destroy(conn);
@@ -128,13 +145,10 @@
 static void hound_remove_source_internal(hound_t *hound, audio_source_t *source)
 {
-	assert(hound);
-	assert(source);
 	log_verbose("Removing source '%s'.", source->name);
 	if (!list_empty(&source->connections))
 		log_warning("Removing source '%s' while still connected.", source->name);
 	while (!list_empty(&source->connections)) {
-		connection_t *conn =
-		    connection_from_source_list(list_first(&source->connections));
-		assert(conn);
+		connection_t *conn = list_get_instance(
+		    list_first(&source->connections), connection_t, source_link);
 		list_remove(&conn->hound_link);
 		connection_destroy(conn);
@@ -223,6 +237,5 @@
 	fibril_mutex_lock(&hound->list_guard);
 	hound_ctx_t *res = NULL;
-	list_foreach(hound->contexts, it) {
-		hound_ctx_t *ctx = hound_ctx_from_link(it);
+	list_foreach(hound->contexts, link, hound_ctx_t, ctx) {
 		if (hound_ctx_get_id(ctx) == id) {
 			res = ctx;
@@ -251,6 +264,5 @@
 	}
 
-	list_foreach(hound->devices, it) {
-		audio_device_t *dev = audio_device_list_instance(it);
+	list_foreach(hound->devices, link, audio_device_t, dev) {
 		if (dev->id == id) {
 			log_debug("Device with id %zu is already present", id);
Index: uspace/srv/audio/hound/hound_ctx.c
===================================================================
--- uspace/srv/audio/hound/hound_ctx.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/audio/hound/hound_ctx.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -176,14 +176,4 @@
 
 /**
- * List instance helper.
- * @param l link
- * @return pointer to a hound context structure, NULL on failure.
- */
-static inline hound_ctx_stream_t *hound_ctx_stream_from_link(link_t *l)
-{
-	return l ? list_get_instance(l, hound_ctx_stream_t, link) : NULL;
-}
-
-/**
  * New stream append helper.
  * @param ctx hound context.
@@ -417,6 +407,5 @@
 	pcm_format_silence(buffer, size, &source->format);
 	fibril_mutex_lock(&ctx->guard);
-	list_foreach(ctx->streams, it) {
-		hound_ctx_stream_t *stream = hound_ctx_stream_from_link(it);
+	list_foreach(ctx->streams, link, hound_ctx_stream_t, stream) {
 		ssize_t copied = hound_ctx_stream_add_self(
 		    stream, buffer, size, &source->format);
@@ -426,6 +415,5 @@
 	log_verbose("CTX: %p. Pushing audio to %u connections", ctx,
 	    list_count(&source->connections));
-	list_foreach(source->connections, it) {
-		connection_t *conn = connection_from_source_list(it);
+	list_foreach(source->connections, source_link, connection_t, conn) {
 		connection_push_data(conn, adata);
 	}
@@ -444,6 +432,5 @@
 	/* count available data */
 	size_t available_frames = -1;  /* this is ugly.... */
-	list_foreach(sink->connections, it) {
-		connection_t *conn = connection_from_source_list(it);
+	list_foreach(sink->connections, source_link, connection_t, conn) {
 		available_frames = min(available_frames,
 		    audio_pipe_frames(&conn->fifo));
@@ -466,6 +453,5 @@
 	/* mix data */
 	pcm_format_silence(buffer, bsize, &sink->format);
-	list_foreach(sink->connections, it) {
-		connection_t *conn = connection_from_source_list(it);
+	list_foreach(sink->connections, source_link, connection_t, conn) {
 		/* This should not trigger data update on the source */
 		const size_t copied = connection_add_source_data(
@@ -476,6 +462,5 @@
 	}
 	/* push to all streams */
-	list_foreach(ctx->streams, it) {
-		hound_ctx_stream_t *stream = hound_ctx_stream_from_link(it);
+	list_foreach(ctx->streams, link, hound_ctx_stream_t, stream) {
 		const int ret = stream_push_data(stream, adata);
 		if (ret != EOK)
Index: uspace/srv/audio/hound/hound_ctx.h
===================================================================
--- uspace/srv/audio/hound/hound_ctx.h	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/audio/hound/hound_ctx.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -58,14 +58,4 @@
 } hound_ctx_t;
 
-/**
- * List instance helper.
- * @param l link
- * @return pointer to a hound context structure, NULL on failure.
- */
-static inline hound_ctx_t *hound_ctx_from_link(link_t *l)
-{
-	return l ? list_get_instance(l, hound_ctx_t, link) : NULL;
-}
-
 typedef struct hound_ctx_stream hound_ctx_stream_t;
 
Index: uspace/srv/bd/rd/rd.c
===================================================================
--- uspace/srv/bd/rd/rd.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/bd/rd/rd.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -165,5 +165,5 @@
 	    AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE;
 	
-	ret = physmem_map((void *) addr_phys,
+	ret = physmem_map(addr_phys,
 	    ALIGN_UP(rd_size, PAGE_SIZE) >> PAGE_WIDTH, flags, &rd_addr);
 	if (ret != EOK) {
Index: uspace/srv/devman/Makefile
===================================================================
--- uspace/srv/devman/Makefile	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/devman/Makefile	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/client_conn.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/client_conn.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/dev.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/dev.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ 	(revision )
@@ -1,1528 +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, *drv = NULL;
-	int best_score = 0, score = 0;
-	
-	fibril_mutex_lock(&drivers_list->drivers_mutex);
-	
-	list_foreach(drivers_list->drivers, link) {
-		drv = list_get_instance(link, driver_t, drivers);
-		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;
-	driver_t *drv = NULL;
-	
-	fibril_mutex_lock(&drv_list->drivers_mutex);
-	
-	list_foreach(drv_list->drivers, link) {
-		drv = list_get_instance(link, driver_t, drivers);
-		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_not_null(drv->name);
-	free_not_null(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, item) {
-		fun_node_t *fun =
-		    list_get_instance(item, fun_node_t, dev_functions);
-
-		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_not_null(fun->name);
-	free_not_null(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));
-
-	fun_node_t *fun;
-
-	list_foreach(dev->functions, link) {
-		fun = list_get_instance(link, fun_node_t, dev_functions);
-
-		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 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/devman/devman.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/devtree.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/devtree.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/driver.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/driver.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/drv_conn.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/drv_conn.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/fun.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/fun.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/loc.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/loc.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/devman/main.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/main.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/devman/match.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/devman/match.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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.c
===================================================================
--- uspace/srv/devman/util.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/devman/util.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -85,10 +85,4 @@
 }
 
-void free_not_null(const void *ptr)
-{
-	if (ptr != NULL)
-		free(ptr);
-}
-
 void replace_char(char *str, char orig, char repl)
 {
Index: uspace/srv/devman/util.h
===================================================================
--- uspace/srv/devman/util.h	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/devman/util.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/fs/cdfs/cdfs_ops.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -365,8 +365,5 @@
 	
 	/* Check for duplicate entries */
-	list_foreach(parent->cs_list, link) {
-		cdfs_dentry_t *dentry =
-		    list_get_instance(link, cdfs_dentry_t, link);
-		
+	list_foreach(parent->cs_list, link, cdfs_dentry_t, dentry) {
 		if (str_cmp(dentry->name, name) == 0)
 			return EEXIST;
@@ -524,8 +521,5 @@
 	}
 	
-	list_foreach(parent->cs_list, link) {
-		cdfs_dentry_t *dentry =
-		    list_get_instance(link, cdfs_dentry_t, link);
-		
+	list_foreach(parent->cs_list, link, cdfs_dentry_t, dentry) {
 		if (str_cmp(dentry->name, component) == 0) {
 			*fn = get_cached_node(parent->service_id, dentry->index);
@@ -623,4 +617,25 @@
 {
 	return 0;
+}
+
+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;
 }
 
@@ -641,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_idx.c
===================================================================
--- uspace/srv/fs/exfat/exfat_idx.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/fs/exfat/exfat_idx.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -88,11 +88,8 @@
 static unused_t *unused_find(service_id_t service_id, bool lock)
 {
-	unused_t *u;
-
 	if (lock)
 		fibril_mutex_lock(&unused_lock);
 
-	list_foreach(unused_list, l) {
-		u = list_get_instance(l, unused_t, link);
+	list_foreach(unused_list, link, unused_t, u) {
 		if (u->service_id == service_id) 
 			return u;
Index: uspace/srv/fs/exfat/exfat_ops.c
===================================================================
--- uspace/srv/fs/exfat/exfat_ops.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/fs/exfat/exfat_ops.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 *);
 
 /*
@@ -155,5 +158,4 @@
 static int exfat_node_fini_by_service_id(service_id_t service_id)
 {
-	exfat_node_t *nodep;
 	int rc;
 
@@ -166,6 +168,5 @@
 restart:
 	fibril_mutex_lock(&ffn_mutex);
-	list_foreach(ffn_list, lnk) {
-		nodep = list_get_instance(lnk, exfat_node_t, ffn_link);
+	list_foreach(ffn_list, ffn_link, exfat_node_t, nodep) {
 		if (!fibril_mutex_trylock(&nodep->lock)) {
 			fibril_mutex_unlock(&ffn_mutex);
@@ -912,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 */
@@ -930,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 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 */
@@ -194,8 +197,5 @@
 	}
 	
-	list_foreach(instance_list, link) {
-		ext4fs_instance_t *tmp =
-		    list_get_instance(link, ext4fs_instance_t, link);
-		
+	list_foreach(instance_list, link, ext4fs_instance_t, tmp) {
 		if (tmp->service_id == service_id) {
 			*inst = tmp;
@@ -836,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;
 }
 
@@ -857,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_idx.c
===================================================================
--- uspace/srv/fs/fat/fat_idx.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/fs/fat/fat_idx.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -88,11 +88,8 @@
 static unused_t *unused_find(service_id_t service_id, bool lock)
 {
-	unused_t *u;
-
 	if (lock)
 		fibril_mutex_lock(&unused_lock);
 
-	list_foreach(unused_list, l) {
-		u = list_get_instance(l, unused_t, link);
+	list_foreach(unused_list, link, unused_t, u) {
 		if (u->service_id == service_id) 
 			return u;
Index: uspace/srv/fs/fat/fat_ops.c
===================================================================
--- uspace/srv/fs/fat/fat_ops.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/fs/fat/fat_ops.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 *);
 
 /*
@@ -149,5 +152,4 @@
 static int fat_node_fini_by_service_id(service_id_t service_id)
 {
-	fat_node_t *nodep;
 	int rc;
 
@@ -160,6 +162,5 @@
 restart:
 	fibril_mutex_lock(&ffn_mutex);
-	list_foreach(ffn_list, lnk) {
-		nodep = list_get_instance(lnk, fat_node_t, ffn_link);
+	list_foreach(ffn_list, ffn_link, fat_node_t, nodep) {
 		if (!fibril_mutex_trylock(&nodep->lock)) {
 			fibril_mutex_unlock(&ffn_mutex);
@@ -843,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 = {
@@ -860,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 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/fs/mfs/mfs.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/fs/mfs/mfs_balloc.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/fs/mfs/mfs_ops.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/fs/mfs/mfs_rw.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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/tmpfs/tmpfs_ops.c
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs_ops.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/fs/tmpfs/tmpfs_ops.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -259,7 +259,5 @@
 	tmpfs_node_t *parentp = TMPFS_NODE(pfn);
 
-	list_foreach(parentp->cs_list, lnk) {
-		tmpfs_dentry_t *dentryp;
-		dentryp = list_get_instance(lnk, tmpfs_dentry_t, link);
+	list_foreach(parentp->cs_list, link, tmpfs_dentry_t, dentryp) {
 		if (!str_cmp(dentryp->name, component)) {
 			*rfn = FS_NODE(dentryp->node);
@@ -365,7 +363,6 @@
 
 	/* Check for duplicit entries. */
-	list_foreach(parentp->cs_list, lnk) {
-		dentryp = list_get_instance(lnk, tmpfs_dentry_t, link);	
-		if (!str_cmp(dentryp->name, nm))
+	list_foreach(parentp->cs_list, link, tmpfs_dentry_t, dp) {
+		if (!str_cmp(dp->name, nm))
 			return EEXIST;
 	}
@@ -400,8 +397,8 @@
 	if (!parentp)
 		return EBUSY;
-	
-	list_foreach(parentp->cs_list, lnk) {
-		dentryp = list_get_instance(lnk, tmpfs_dentry_t, link);
-		if (!str_cmp(dentryp->name, nm)) {
+
+	list_foreach(parentp->cs_list, link, tmpfs_dentry_t, dp) {
+		if (!str_cmp(dp->name, nm)) {
+			dentryp = dp;
 			childp = dentryp->node;
 			assert(FS_NODE(childp) == cfn);
Index: uspace/srv/fs/udf/udf_ops.c
===================================================================
--- uspace/srv/fs/udf/udf_ops.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/fs/udf/udf_ops.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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/hid/compositor/compositor.c
===================================================================
--- uspace/srv/hid/compositor/compositor.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/hid/compositor/compositor.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -143,4 +143,6 @@
 static LIST_INITIALIZE(viewport_list);
 
+static FIBRIL_MUTEX_INITIALIZE(discovery_mtx);
+
 /** Input server proxy */
 static input_t *input;
@@ -335,6 +337,5 @@
 	}
 
-	list_foreach(viewport_list, link) {
-		viewport_t *vp = list_get_instance(link, viewport_t, link);
+	list_foreach(viewport_list, link, viewport_t, vp) {
 		sysarg_t w_vp, h_vp;
 		surface_get_resolution(vp->surface, &w_vp, &h_vp);
@@ -354,6 +355,5 @@
 	fibril_mutex_lock(&pointer_list_mtx);
 
-	list_foreach(pointer_list, link) {
-		pointer_t *ptr = list_get_instance(link, pointer_t, link);
+	list_foreach(pointer_list, link, pointer_t, ptr) {
 		ptr->pos.x = ptr->pos.x > viewport_bound_rect.x ? ptr->pos.x : viewport_bound_rect.x;
 		ptr->pos.y = ptr->pos.y > viewport_bound_rect.y ? ptr->pos.y : viewport_bound_rect.y;
@@ -374,8 +374,6 @@
 	fibril_mutex_lock(&pointer_list_mtx);
 
-	list_foreach(viewport_list, link) {
-
+	list_foreach(viewport_list, link, viewport_t, vp) {
 		/* Determine what part of the viewport must be updated. */
-		viewport_t *vp = list_get_instance(link, viewport_t, link);
 		sysarg_t x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp;
 		surface_get_resolution(vp->surface, &w_dmg_vp, &h_dmg_vp);
@@ -446,7 +444,5 @@
 			}
 
-			list_foreach(pointer_list, link) {
-
-				pointer_t *ptr = list_get_instance(link, pointer_t, link);
+			list_foreach(pointer_list, link, pointer_t, ptr) {
 				if (ptr->ghost.surface) {
 
@@ -522,9 +518,8 @@
 			}
 
-			list_foreach(pointer_list, link) {
+			list_foreach(pointer_list, link, pointer_t, ptr) {
 
 				/* Determine what part of the pointer intersects with the
 				 * updated area of the current viewport. */
-				pointer_t *ptr = list_get_instance(link, pointer_t, link);
 				sysarg_t x_dmg_ptr, y_dmg_ptr, w_dmg_ptr, h_dmg_ptr;
 				surface_t *sf_ptr = ptr->cursor.states[ptr->state];
@@ -569,6 +564,5 @@
 
 	/* Notify visualizers about updated regions. */
-	list_foreach(viewport_list, link) {
-		viewport_t *vp = list_get_instance(link, viewport_t, link);
+	list_foreach(viewport_list, link, viewport_t, vp) {
 		sysarg_t x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp;
 		surface_get_damaged_region(vp->surface, &x_dmg_vp, &y_dmg_vp, &w_dmg_vp, &h_dmg_vp);
@@ -631,6 +625,5 @@
 
 	fibril_mutex_lock(&pointer_list_mtx);
-	list_foreach(pointer_list, link) {
-		pointer_t *pointer = list_get_instance(link, pointer_t, link);
+	list_foreach(pointer_list, link, pointer_t, pointer) {
 		if (pointer->id == pos_id) {
 			pointer->grab_flags = pointer->pressed ? grab_flags : GF_EMPTY;
@@ -710,15 +703,15 @@
 {
 	fibril_mutex_lock(&window_list_mtx);
-	window_t *window = NULL;
-	list_foreach(window_list, link) {
-		window = list_get_instance(link, window_t, link);
+
+	list_foreach(window_list, link, window_t, window) {
 		if (window == target) {
 			prodcons_produce(&window->queue, &event->link);
-		}
-	}
-	if (!window) {
-		free(event);
-	}
+			fibril_mutex_unlock(&window_list_mtx);
+			return;
+		}
+	}
+
 	fibril_mutex_unlock(&window_list_mtx);
+	free(event);
 }
 
@@ -863,6 +856,5 @@
 	window_t *win = NULL;
 	fibril_mutex_lock(&window_list_mtx);
-	list_foreach(window_list, link) {
-		window_t *cur = list_get_instance(link, window_t, link);
+	list_foreach(window_list, link, window_t, cur) {
 		if (cur->in_dsid == service_id || cur->out_dsid == service_id) {
 			win = cur;
@@ -1007,6 +999,5 @@
 		/* Close all clients and their windows. */
 		fibril_mutex_lock(&window_list_mtx);
-		list_foreach(window_list, link) {
-			window_t *win = list_get_instance(link, window_t, link);
+		list_foreach(window_list, link, window_t, win) {
 			window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
 			if (event) {
@@ -1034,6 +1025,5 @@
 	viewport_t *vp = NULL;
 	fibril_mutex_lock(&viewport_list_mtx);
-	list_foreach(viewport_list, link) {
-		viewport_t *cur = list_get_instance(link, viewport_t, link);
+	list_foreach(viewport_list, link, viewport_t, cur) {
 		if (cur->dsid == (service_id_t) IPC_GET_ARG1(*icall)) {
 			vp = cur;
@@ -1571,6 +1561,6 @@
 
 	/* Determine the window which the mouse click belongs to. */
-	list_foreach(window_list, link) {
-		win = list_get_instance(link, window_t, link);
+	list_foreach(window_list, link, window_t, cw) {
+		win = cw;
 		if (win->surface) {
 			surface_get_resolution(win->surface, &width, &height);
@@ -2126,4 +2116,58 @@
 }
 
+static int discover_viewports(void)
+{
+	/* Create viewports and connect them to visualizers. */
+	category_id_t cat_id;
+	int rc = loc_category_get_id("visualizer", &cat_id, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		printf("%s: Failed to get visualizer category.\n", NAME);
+		return -1;
+	}
+	
+	service_id_t *svcs;
+	size_t svcs_cnt = 0;
+	rc = loc_category_get_svcs(cat_id, &svcs, &svcs_cnt);
+	if (rc != EOK || svcs_cnt == 0) {
+		printf("%s: Failed to get visualizer category services.\n", NAME);
+		return -1;
+	}
+
+	fibril_mutex_lock(&viewport_list_mtx);	
+	for (size_t i = 0; i < svcs_cnt; ++i) {
+		bool exists = false;
+		list_foreach(viewport_list, link, viewport_t, vp) {
+			if (vp->dsid == svcs[i]) {
+				exists = true;
+				break;
+			}
+		}
+		
+		if (exists)
+			continue;
+		
+		char *svc_name;
+		rc = loc_service_get_name(svcs[i], &svc_name);
+		if (rc == EOK) {
+			viewport_t *vp = viewport_create(svc_name);
+			if (vp != NULL) {
+				list_append(&vp->link, &viewport_list);
+			}
+		}
+	}
+	fibril_mutex_unlock(&viewport_list_mtx);
+	
+	/* TODO damage only newly added viewports */
+	comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
+	return EOK;
+}
+
+static void category_change_cb(void)
+{
+	fibril_mutex_lock(&discovery_mtx);
+	discover_viewports();
+	fibril_mutex_unlock(&discovery_mtx);
+}
+
 static int compositor_srv_init(char *input_svc, char *name)
 {
@@ -2177,31 +2221,15 @@
 	}
 
-	/* Create viewports and connect them to visualizers. */
-	category_id_t cat_id;
-	rc = loc_category_get_id("visualizer", &cat_id, IPC_FLAG_BLOCKING);
+	rc = loc_register_cat_change_cb(category_change_cb);
 	if (rc != EOK) {
-		printf("%s: Failed to get visualizer category.\n", NAME);
+		printf("%s: Failed to register category change callback\n", NAME);
 		input_disconnect();
-		return -1;
-	}
-	
-	service_id_t *svcs;
-	size_t svcs_cnt = 0;
-	rc = loc_category_get_svcs(cat_id, &svcs, &svcs_cnt);
-	if (rc != EOK || svcs_cnt == 0) {
-		printf("%s: Failed to get visualizer category services.\n", NAME);
+		return rc;
+	}	
+
+	rc = discover_viewports();
+	if (rc != EOK) {
 		input_disconnect();
-		return -1;
-	}
-	
-	for (size_t i = 0; i < svcs_cnt; ++i) {
-		char *svc_name;
-		rc = loc_service_get_name(svcs[i], &svc_name);
-		if (rc == EOK) {
-			viewport_t *vp = viewport_create(svc_name);
-			if (vp != NULL) {
-				list_append(&vp->link, &viewport_list);
-			}
-		}
+		return rc;
 	}
 	
@@ -2215,4 +2243,5 @@
 	comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
 	
+	
 	return EOK;
 }
Index: uspace/srv/hid/input/input.c
===================================================================
--- uspace/srv/hid/input/input.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/hid/input/input.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -470,8 +470,5 @@
 {
 	/* For each keyboard device */
-	list_foreach(kbd_devs, kdev_link) {
-		kbd_dev_t *kdev = list_get_instance(kdev_link, kbd_dev_t,
-		    kbd_devs);
-		
+	list_foreach(kbd_devs, kbd_devs, kbd_dev_t, kdev) {
 		/* Yield port */
 		if (kdev->port_ops != NULL)
@@ -483,8 +480,5 @@
 {
 	/* For each keyboard device */
-	list_foreach(kbd_devs, kdev_link) {
-		kbd_dev_t *kdev = list_get_instance(kdev_link, kbd_dev_t,
-		    kbd_devs);
-		
+	list_foreach(kbd_devs, kbd_devs, kbd_dev_t, kdev) {
 		/* Reclaim port */
 		if (kdev->port_ops != NULL)
@@ -521,7 +515,5 @@
 		
 		/* Determine whether we already know this device. */
-		list_foreach(kbd_devs, kdev_link) {
-			kbd_dev_t *kdev = list_get_instance(kdev_link,
-			    kbd_dev_t, kbd_devs);
+		list_foreach(kbd_devs, kbd_devs, kbd_dev_t, kdev) {
 			if (kdev->svc_id == svcs[i]) {
 				already_known = true;
@@ -574,7 +566,5 @@
 		
 		/* Determine whether we already know this device. */
-		list_foreach(mouse_devs, mdev_link) {
-			mouse_dev_t *mdev = list_get_instance(mdev_link,
-			    mouse_dev_t, mouse_devs);
+		list_foreach(mouse_devs, mouse_devs, mouse_dev_t, mdev) {
 			if (mdev->svc_id == svcs[i]) {
 				already_known = true;
Index: uspace/srv/hid/input/port/niagara.c
===================================================================
--- uspace/srv/hid/input/port/niagara.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/hid/input/port/niagara.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -97,6 +97,6 @@
 		return -1;
 	
-	int rc = physmem_map((void *) paddr, 1,
-	    AS_AREA_READ | AS_AREA_WRITE, (void *) &input_buffer);
+	int rc = physmem_map(paddr, 1, AS_AREA_READ | AS_AREA_WRITE,
+	    (void *) &input_buffer);
 	if (rc != 0) {
 		printf("Niagara: uspace driver couldn't map physical memory: %d\n",
Index: uspace/srv/hid/output/output.c
===================================================================
--- uspace/srv/hid/output/output.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/hid/output/output.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -82,7 +82,5 @@
 	int ret = EOK;
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		assert(dev->ops.yield);
 		
@@ -99,7 +97,5 @@
 	int ret = EOK;
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		assert(dev->ops.claim);
 		
@@ -117,7 +113,5 @@
 	sysarg_t rows = MAX_ROWS;
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		cols = min(cols, dev->cols);
 		rows = min(rows, dev->rows);
@@ -131,7 +125,5 @@
 	console_caps_t caps = 0;
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		assert(dev->ops.get_caps);
 		
@@ -145,6 +137,5 @@
 {
 	frontbuf_t *frontbuf = NULL;
-	list_foreach(frontbufs, link) {
-		frontbuf_t *cur = list_get_instance(link, frontbuf_t, link);
+	list_foreach(frontbufs, link, frontbuf_t, cur) {
 		if (cur == (frontbuf_t *) handle) {
 			frontbuf = cur;
@@ -216,7 +207,5 @@
 	chargrid_get_cursor(buf, &col, &row);
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		assert(dev->ops.cursor_update);
 		
@@ -237,7 +226,5 @@
 static void srv_set_style(ipc_callid_t iid, ipc_call_t *icall)
 {
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		dev->attrs.type = CHAR_ATTR_STYLE;
 		dev->attrs.val.style =
@@ -250,7 +237,5 @@
 static void srv_set_color(ipc_callid_t iid, ipc_call_t *icall)
 {
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		dev->attrs.type = CHAR_ATTR_INDEX;
 		dev->attrs.val.index.bgcolor =
@@ -267,7 +252,5 @@
 static void srv_set_rgb_color(ipc_callid_t iid, ipc_call_t *icall)
 {
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		dev->attrs.type = CHAR_ATTR_RGB;
 		dev->attrs.val.rgb.bgcolor = IPC_GET_ARG1(*icall);
@@ -325,7 +308,5 @@
 	chargrid_t *buf = (chargrid_t *) frontbuf->data;
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		assert(dev->ops.char_update);
 		
@@ -374,7 +355,5 @@
 	chargrid_t *buf = (chargrid_t *) frontbuf->data;
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		assert(dev->ops.char_update);
 		
Index: uspace/srv/hid/output/port/ega.c
===================================================================
--- uspace/srv/hid/output/port/ega.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/hid/output/port/ega.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -216,5 +216,5 @@
 	ega.size = (ega.cols * ega.rows) << 1;
 	
-	rc = physmem_map((void *) paddr,
+	rc = physmem_map(paddr,
 	    ALIGN_UP(ega.size, PAGE_SIZE) >> PAGE_WIDTH,
 	    AS_AREA_READ | AS_AREA_WRITE, (void *) &ega.addr);
Index: uspace/srv/hid/output/port/kchar.c
===================================================================
--- uspace/srv/hid/output/port/kchar.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/hid/output/port/kchar.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -84,5 +84,5 @@
 		return rc;
 	
-	rc = physmem_map((void *) paddr,
+	rc = physmem_map(paddr,
 	    ALIGN_UP(1, PAGE_SIZE) >> PAGE_WIDTH,
 	    AS_AREA_READ | AS_AREA_WRITE, (void *) &kchar.addr);
Index: uspace/srv/hid/output/port/niagara.c
===================================================================
--- uspace/srv/hid/output/port/niagara.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/hid/output/port/niagara.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -104,6 +104,6 @@
 		return rc;
 	
-	rc = physmem_map((void *) paddr, 1,
-	    AS_AREA_READ | AS_AREA_WRITE, (void *) &niagara.fifo);
+	rc = physmem_map(paddr, 1, AS_AREA_READ | AS_AREA_WRITE,
+	    (void *) &niagara.fifo);
 	if (rc != EOK)
 		return rc;
Index: uspace/srv/hid/remcons/user.c
===================================================================
--- uspace/srv/hid/remcons/user.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/hid/remcons/user.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -128,6 +128,5 @@
 
 	fibril_mutex_lock(&users_guard);
-	list_foreach(users, link) {
-		telnet_user_t *tmp = list_get_instance(link, telnet_user_t, link);
+	list_foreach(users, link, telnet_user_t, tmp) {
 		if (tmp->service_id == id) {
 			user = tmp;
Index: uspace/srv/hid/rfb/Makefile
===================================================================
--- uspace/srv/hid/rfb/Makefile	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/hid/rfb/Makefile	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2013 Martin Sucha
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+LIBS = $(LIBGRAPH_PREFIX)/libgraph.a
+EXTRA_CFLAGS = -I$(LIBGRAPH_PREFIX)
+BINARY = rfb
+
+SOURCES = \
+	main.c \
+	rfb.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/hid/rfb/main.c
===================================================================
--- uspace/srv/hid/rfb/main.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/hid/rfb/main.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <loc.h>
+#include <stdio.h>
+#include <fibril_synch.h>
+#include <abi/ipc/methods.h>
+#include <inttypes.h>
+#include <io/log.h>
+
+#include <abi/fb/visuals.h>
+#include <adt/list.h>
+#include <io/mode.h>
+#include <io/pixelmap.h>
+#include <io/chargrid.h>
+#include <graph.h>
+
+#include "rfb.h"
+
+#define NAME "rfb"
+
+static vslmode_list_element_t pixel_mode;
+static visualizer_t *vis;
+static rfb_t rfb;
+
+static int rfb_claim(visualizer_t *vs)
+{
+	return EOK;
+}
+
+static int rfb_yield(visualizer_t *vs)
+{
+	return EOK;
+}
+
+static int rfb_suspend(visualizer_t *vs)
+{
+	return EOK;
+}
+
+static int rfb_wakeup(visualizer_t *vs)
+{
+	return EOK;
+}
+
+static int rfb_handle_damage_pixels(visualizer_t *vs,
+    sysarg_t x0, sysarg_t y0, sysarg_t width, sysarg_t height,
+    sysarg_t x_offset, sysarg_t y_offset)
+{
+	fibril_mutex_lock(&rfb.lock);
+	
+	if (x0 + width > rfb.width || y0 + height > rfb.height) {
+		fibril_mutex_unlock(&rfb.lock);
+		return EINVAL;
+	}
+	
+	/* TODO update surface_t and use it */
+	if (!rfb.damage_valid) {
+		rfb.damage_rect.x = x0;
+		rfb.damage_rect.y = y0;
+		rfb.damage_rect.width = width;
+		rfb.damage_rect.height = height;
+		rfb.damage_valid = true;
+	}
+	else {
+		if (x0 < rfb.damage_rect.x) {
+			rfb.damage_rect.width += rfb.damage_rect.x - x0;
+			rfb.damage_rect.x = x0;
+		}
+		if (y0 < rfb.damage_rect.y) {
+			rfb.damage_rect.height += rfb.damage_rect.y - y0;
+			rfb.damage_rect.y = y0;
+		}
+		sysarg_t x1 = x0 + width;
+		sysarg_t dx1 = rfb.damage_rect.x + rfb.damage_rect.width;
+		if (x1 > dx1) {
+			rfb.damage_rect.width += x1 - dx1;
+		}
+		sysarg_t y1 = y0 + height;
+		sysarg_t dy1 = rfb.damage_rect.y + rfb.damage_rect.height;
+		if (y1 > dy1) {
+			rfb.damage_rect.height += y1 - dy1;
+		}
+	}
+	
+	pixelmap_t *map = &vs->cells;
+	
+	for (sysarg_t y = y0; y < height + y0; ++y) {
+		for (sysarg_t x = x0; x < width + x0; ++x) {
+			pixel_t pix = pixelmap_get_pixel(map, (x + x_offset) % map->width,
+			    (y + y_offset) % map->height);
+			pixelmap_put_pixel(&rfb.framebuffer, x, y, pix);
+		}
+	}
+	
+	fibril_mutex_unlock(&rfb.lock);
+	return EOK;
+}
+
+static int rfb_change_mode(visualizer_t *vs, vslmode_t new_mode)
+{
+	return EOK;
+}
+
+static visualizer_ops_t rfb_ops = {
+	.claim = rfb_claim,
+	.yield = rfb_yield,
+	.change_mode = rfb_change_mode,
+	.handle_damage = rfb_handle_damage_pixels,
+	.suspend = rfb_suspend,
+	.wakeup = rfb_wakeup
+};
+
+static void syntax_print(void)
+{
+	fprintf(stderr, "Usage: %s <name> <width> <height> [port]\n", NAME);
+}
+
+static void client_connection(ipc_callid_t callid, ipc_call_t *call, void *data)
+{
+	graph_visualizer_connection(vis, callid, call, data);
+}
+
+static int socket_fibril(void *unused)
+{
+	rfb_accept(&rfb);
+	
+	/* Not reached */
+	return EOK;
+}
+
+int main(int argc, char **argv)
+{
+	log_init(NAME);
+
+	if (argc <= 3) {
+		syntax_print();
+		return 1;
+	}
+
+	const char *rfb_name = argv[1];
+	
+	char *endptr;
+	unsigned long width = strtoul(argv[2], &endptr, 0);
+	if (*endptr != 0) {
+		fprintf(stderr, "Invalid width\n");
+		syntax_print();
+		return 1;
+	}
+	
+	unsigned long height = strtoul(argv[3], &endptr, 0);
+	if (*endptr != 0) {
+		fprintf(stderr, "Invalid height\n");
+		syntax_print();
+		return 1;
+	}
+	
+	unsigned long port = 5900;
+	if (argc > 4) {
+		port = strtoul(argv[4], &endptr, 0);
+		if (*endptr != 0) {
+			fprintf(stderr, "Invalid port number\n");
+			syntax_print();
+			return 1;
+		}
+	}
+	
+	rfb_init(&rfb, width, height, rfb_name);
+	
+	vis = malloc(sizeof(visualizer_t));
+	if (vis == NULL) {
+		fprintf(stderr, "Failed allocating visualizer struct\n");
+		return 3;
+	}
+	
+	graph_init_visualizer(vis);
+	
+	pixel_mode.mode.index = 0;
+	pixel_mode.mode.version = 0;
+	pixel_mode.mode.refresh_rate = 0;
+	pixel_mode.mode.screen_aspect.width = rfb.width;
+	pixel_mode.mode.screen_aspect.height = rfb.height;
+	pixel_mode.mode.screen_width = rfb.width;
+	pixel_mode.mode.screen_height = rfb.height;
+	pixel_mode.mode.cell_aspect.width = 1;
+	pixel_mode.mode.cell_aspect.height = 1;
+	pixel_mode.mode.cell_visual.pixel_visual = VISUAL_RGB_8_8_8;
+	
+	link_initialize(&pixel_mode.link);
+	list_append(&pixel_mode.link, &vis->modes);
+	
+	vis->def_mode_idx = 0;
+	
+	vis->ops = rfb_ops;
+	vis->dev_ctx = NULL;
+
+	async_set_client_connection(client_connection);
+
+	int rc = loc_server_register(NAME);
+	if (rc != EOK) {
+		printf("%s: Unable to register server.\n", NAME);
+		return rc;
+	}
+
+	char *service_name;
+	rc = asprintf(&service_name, "rfb/%s", rfb_name);
+	if (rc < 0) {
+		printf(NAME ": Unable to create service name\n");
+		return rc;
+	}
+
+	service_id_t service_id;
+	
+	rc = loc_service_register(service_name, &service_id);
+	if (rc != EOK) {
+		printf(NAME ": Unable to register service %s.\n", service_name);
+		return rc;
+	}
+	
+	free(service_name);
+
+	category_id_t visualizer_category;
+	rc = loc_category_get_id("visualizer", &visualizer_category, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		fprintf(stderr, NAME ": Unable to get visualizer category id.\n");
+		return 1;
+	}
+	
+	rc = loc_service_add_to_cat(service_id, visualizer_category);
+	if (rc != EOK) {
+		fprintf(stderr, NAME ": Unable to add service to visualizer category.\n");
+		return 1;
+	}
+	
+	rc = rfb_listen(&rfb, port);
+	if (rc != EOK) {
+		fprintf(stderr, NAME ": Unable to listen at rfb port\n");
+		return 2;
+	}
+	
+	fid_t fib = fibril_create(socket_fibril, NULL);
+	if (!fib) {
+		fprintf(stderr, NAME ": Unable to create socket fibril.\n");
+		return 2;
+	}
+	
+	fibril_add_ready(fib);
+	
+	printf("%s: Accepting connections\n", NAME);
+	task_retval(0);
+	async_manager();
+
+	/* Not reached */
+	return 0;
+}
Index: uspace/srv/hid/rfb/rfb.c
===================================================================
--- uspace/srv/hid/rfb/rfb.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/hid/rfb/rfb.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -0,0 +1,800 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fibril_synch.h>
+#include <inttypes.h>
+#include <str.h>
+#include <str_error.h>
+#include <byteorder.h>
+#include <macros.h>
+#include <io/log.h>
+
+#include <net/in.h>
+#include <net/inet.h>
+#include <net/socket.h>
+
+#include "rfb.h"
+
+/** Buffer for receiving the request. */
+#define BUFFER_SIZE  1024
+
+static char rbuf[BUFFER_SIZE];
+static size_t rbuf_out;
+static size_t rbuf_in;
+
+
+/** Receive one character (with buffering) */
+static int recv_char(int fd, char *c)
+{
+	if (rbuf_out == rbuf_in) {
+		rbuf_out = 0;
+		rbuf_in = 0;
+		
+		ssize_t rc = recv(fd, rbuf, BUFFER_SIZE, 0);
+		if (rc <= 0)
+			return rc;
+		
+		rbuf_in = rc;
+	}
+	
+	*c = rbuf[rbuf_out++];
+	return EOK;
+}
+
+/** Receive count characters (with buffering) */
+static int recv_chars(int fd, char *c, size_t count)
+{
+	for (size_t i = 0; i < count; i++) {
+		int rc = recv_char(fd, c);
+		if (rc != EOK)
+			return rc;
+		c++;
+	}
+	return EOK;
+}
+
+static int recv_skip_chars(int fd, size_t count)
+{
+	for (size_t i = 0; i < count; i++) {
+		char c;
+		int rc = recv_char(fd, &c);
+		if (rc != EOK)
+			return rc;
+	}
+	return EOK;
+}
+
+static void rfb_pixel_format_to_be(rfb_pixel_format_t *src, rfb_pixel_format_t *dst)
+{
+	dst->r_max = host2uint16_t_be(src->r_max);
+	dst->g_max = host2uint16_t_be(src->g_max);
+	dst->b_max = host2uint16_t_be(src->b_max);
+}
+
+static void rfb_pixel_format_to_host(rfb_pixel_format_t *src, rfb_pixel_format_t *dst)
+{
+	dst->r_max = uint16_t_be2host(src->r_max);
+	dst->g_max = uint16_t_be2host(src->g_max);
+	dst->b_max = uint16_t_be2host(src->b_max);
+}
+
+static void rfb_server_init_to_be(rfb_server_init_t *src, rfb_server_init_t *dst)
+{
+	dst->width = host2uint16_t_be(src->width);
+	dst->height = host2uint16_t_be(src->height);
+	rfb_pixel_format_to_be(&src->pixel_format, &dst->pixel_format);
+	dst->name_length = host2uint32_t_be(src->name_length);
+}
+
+static void rfb_set_encodings_to_host(rfb_set_encodings_t *src, rfb_set_encodings_t *dst)
+{
+	dst->count = uint16_t_be2host(src->count);
+}
+
+static void rfb_framebuffer_update_request_to_host(rfb_framebuffer_update_request_t *src,
+    rfb_framebuffer_update_request_t *dst)
+{
+	dst->x = uint16_t_be2host(src->x);
+	dst->y = uint16_t_be2host(src->x);
+	dst->width = uint16_t_be2host(src->width);
+	dst->height = uint16_t_be2host(src->height);
+}
+
+static void rfb_framebuffer_update_to_be(rfb_framebuffer_update_t *src,
+    rfb_framebuffer_update_t *dst)
+{
+	dst->rect_count = host2uint16_t_be(src->rect_count);
+}
+
+static void rfb_rectangle_to_be(rfb_rectangle_t *src, rfb_rectangle_t *dst)
+{
+	dst->x = host2uint16_t_be(src->x);
+	dst->y = host2uint16_t_be(src->y);
+	dst->width = host2uint16_t_be(src->width);
+	dst->height = host2uint16_t_be(src->height);
+	dst->enctype = host2uint32_t_be(src->enctype);
+}
+
+static void rfb_key_event_to_host(rfb_key_event_t *src, rfb_key_event_t *dst)
+{
+	dst->key = uint32_t_be2host(src->key);
+}
+
+static void rfb_pointer_event_to_host(rfb_pointer_event_t *src, rfb_pointer_event_t *dst)
+{
+	dst->x = uint16_t_be2host(src->x);
+	dst->y = uint16_t_be2host(src->y);
+}
+
+static void rfb_client_cut_text_to_host(rfb_client_cut_text_t *src,
+    rfb_client_cut_text_t *dst)
+{
+	dst->length = uint32_t_be2host(src->length);
+}
+
+int rfb_init(rfb_t *rfb, uint16_t width, uint16_t height, const char *name)
+{
+	memset(rfb, 0, sizeof(rfb_t));
+	fibril_mutex_initialize(&rfb->lock);
+	
+	rfb_pixel_format_t *pf = &rfb->pixel_format;
+	pf->bpp = 32;
+	pf->depth = 24;
+	pf->big_endian = 1;
+	pf->true_color = 1;
+	pf->r_max = 255;
+	pf->g_max = 255;
+	pf->b_max = 255;
+	pf->r_shift = 0;
+	pf->g_shift = 8;
+	pf->b_shift = 16;
+	
+	rfb->name = str_dup(name);
+	rfb->supports_trle = false;
+	
+	return rfb_set_size(rfb, width, height);
+}
+
+int rfb_set_size(rfb_t *rfb, uint16_t width, uint16_t height)
+{
+	size_t new_size = width * height * sizeof(pixel_t);
+	void *pixbuf = malloc(new_size);
+	if (pixbuf == NULL)
+		return ENOMEM;
+
+	free(rfb->framebuffer.data);
+	rfb->framebuffer.data = pixbuf;
+	rfb->framebuffer.width = width;
+	rfb->framebuffer.height = height;
+	rfb->width = width;
+	rfb->height = height;
+	
+	/* Fill with white */
+	memset(rfb->framebuffer.data, 255, new_size);
+	
+	return EOK;
+}
+
+static int recv_message(int conn_sd, char type, void *buf, size_t size)
+{
+	memcpy(buf, &type, 1);
+	return recv_chars(conn_sd, ((char *) buf) + 1, size -1);
+}
+
+static uint32_t rfb_scale_channel(uint8_t val, uint32_t max)
+{
+	return val * max / 255;
+}
+
+static void rfb_encode_index(rfb_t *rfb, uint8_t *buf, pixel_t pixel)
+{
+	int first_free_index = -1;
+	for (size_t i = 0; i < 256; i++) {
+		bool free = ALPHA(rfb->palette[i]) == 0;
+		if (free && first_free_index == -1) {
+			first_free_index = i;
+		}
+		else if (!free && RED(rfb->palette[i]) == RED(pixel) &&
+		    GREEN(rfb->palette[i]) == GREEN(pixel) &&
+		    BLUE(rfb->palette[i]) == BLUE(pixel)) {
+			*buf = i;
+			return;
+		}
+	}
+	
+	if (first_free_index != -1) {
+		rfb->palette[first_free_index] = PIXEL(255, RED(pixel), GREEN(pixel),
+		    BLUE(pixel));
+		rfb->palette_used = max(rfb->palette_used, (unsigned) first_free_index + 1);
+		*buf = first_free_index;
+		return;
+	}
+	
+	/* TODO find nearest color index. We are lazy so return index 0 for now */
+	*buf = 0;
+}
+
+static void rfb_encode_true_color(rfb_pixel_format_t *pf, void *buf,
+    pixel_t pixel)
+{
+	uint32_t pix = 0;
+	pix |= rfb_scale_channel(RED(pixel), pf->r_max) << pf->r_shift;
+	pix |= rfb_scale_channel(GREEN(pixel), pf->g_max) << pf->g_shift;
+	pix |= rfb_scale_channel(BLUE(pixel), pf->b_max) << pf->b_shift;
+	
+	if (pf->bpp == 8) {
+		uint8_t pix8 = pix;
+		memcpy(buf, &pix8, 1);
+	}
+	else if (pf->bpp == 16) {
+		uint16_t pix16 = pix;
+		if (pf->big_endian) {
+			pix16 = host2uint16_t_be(pix16);
+		}
+		else {
+			pix16 = host2uint16_t_le(pix16);
+		}
+		memcpy(buf, &pix16, 2);
+	}
+	else if (pf->bpp == 32) {
+		if (pf->big_endian) {
+			pix = host2uint32_t_be(pix);
+		}
+		else {
+			pix = host2uint32_t_le(pix);
+		}
+		memcpy(buf, &pix, 4);
+	}
+}
+
+static void rfb_encode_pixel(rfb_t *rfb, void *buf, pixel_t pixel)
+{
+	if (rfb->pixel_format.true_color) {
+		rfb_encode_true_color(&rfb->pixel_format, buf, pixel);
+	}
+	else {
+		rfb_encode_index(rfb, buf, pixel);
+	}
+}
+
+static void rfb_set_color_map_entries_to_be(rfb_set_color_map_entries_t *src,
+    rfb_set_color_map_entries_t *dst)
+{
+	dst->first_color = host2uint16_t_be(src->first_color);
+	dst->color_count = host2uint16_t_be(src->color_count);
+}
+
+static void rfb_color_map_entry_to_be(rfb_color_map_entry_t *src,
+    rfb_color_map_entry_t *dst)
+{
+	dst->red = host2uint16_t_be(src->red);
+	dst->green = host2uint16_t_be(src->green);
+	dst->blue = host2uint16_t_be(src->blue);
+}
+
+static void *rfb_send_palette_message(rfb_t *rfb, size_t *psize)
+{
+	size_t size = sizeof(rfb_set_color_map_entries_t) +
+	    rfb->palette_used * sizeof(rfb_color_map_entry_t);
+	
+	void *buf = malloc(size);
+	if (buf == NULL)
+		return NULL;
+	
+	void *pos = buf;
+	
+	rfb_set_color_map_entries_t *scme = pos;
+	scme->message_type = RFB_SMSG_SET_COLOR_MAP_ENTRIES;
+	scme->first_color = 0;
+	scme->color_count = rfb->palette_used;
+	rfb_set_color_map_entries_to_be(scme, scme);
+	pos += sizeof(rfb_set_color_map_entries_t);
+	
+	rfb_color_map_entry_t *entries = pos;
+	for (unsigned i = 0; i < rfb->palette_used; i++) {
+		entries[i].red = 65535 * RED(rfb->palette[i]) / 255;
+		entries[i].green = 65535 * GREEN(rfb->palette[i]) / 255;
+		entries[i].blue = 65535 * BLUE(rfb->palette[i]) / 255;
+		rfb_color_map_entry_to_be(&entries[i], &entries[i]);
+	}
+	
+	*psize = size;
+	return buf;
+}
+
+static size_t rfb_rect_encode_raw(rfb_t *rfb, rfb_rectangle_t *rect, void *buf)
+{
+	size_t pixel_size = rfb->pixel_format.bpp / 8;
+	size_t size = (rect->width * rect->height * pixel_size);
+	
+	if (buf == NULL)
+		return size;
+	
+	for (uint16_t y = 0; y < rect->height; y++) {
+		for (uint16_t x = 0; x < rect->width; x++) {
+			pixel_t pixel = pixelmap_get_pixel(&rfb->framebuffer,
+			    x + rect->x, y + rect->y);
+			rfb_encode_pixel(rfb, buf, pixel);
+			buf += pixel_size;
+		}
+	}
+	
+	return size;
+}
+
+typedef enum {
+	COMP_NONE, COMP_SKIP_START, COMP_SKIP_END
+} cpixel_compress_type_t;
+
+typedef struct {
+	size_t size;
+	cpixel_compress_type_t compress_type;
+} cpixel_ctx_t;
+
+static void cpixel_context_init(cpixel_ctx_t *ctx, rfb_pixel_format_t *pixel_format)
+{
+	ctx->size = pixel_format->bpp / 8;
+	ctx->compress_type = COMP_NONE;
+	
+	if (pixel_format->bpp == 32 && pixel_format->depth <= 24) {
+		uint32_t mask = 0;
+		mask |= pixel_format->r_max << pixel_format->r_shift;
+		mask |= pixel_format->g_max << pixel_format->g_shift;
+		mask |= pixel_format->b_max << pixel_format->b_shift;
+		
+		if (pixel_format->big_endian) {
+			mask = host2uint32_t_be(mask);
+		}
+		else {
+			mask = host2uint32_t_le(mask);
+		}
+		
+		uint8_t *mask_data = (uint8_t *) &mask;
+		if (mask_data[0] == 0) {
+			ctx->compress_type = COMP_SKIP_START;
+			ctx->size = 3;
+		}
+		else if (mask_data[3] == 0) {
+			ctx->compress_type = COMP_SKIP_END;
+			ctx->size = 3;
+		}
+	}
+}
+
+static void cpixel_encode(rfb_t *rfb, cpixel_ctx_t *cpixel, void *buf,
+    pixel_t pixel)
+{
+	uint8_t data[4];
+	rfb_encode_pixel(rfb, data, pixel);
+	
+	switch (cpixel->compress_type) {
+	case COMP_NONE:
+	case COMP_SKIP_END:
+		memcpy(buf, data, cpixel->size);
+		break;
+	case COMP_SKIP_START:
+		memcpy(buf, data + 1, cpixel->size);
+	}
+}
+
+static ssize_t rfb_tile_encode_raw(rfb_t *rfb, cpixel_ctx_t *cpixel,
+    rfb_rectangle_t *tile, void *buf)
+{
+	ssize_t size = tile->width * tile->height * cpixel->size;
+	if (buf == NULL)
+		return size;
+	
+	for (uint16_t y = tile->y; y < tile->y + tile->height; y++) {
+		for (uint16_t x = tile->x; x < tile->x + tile->width; x++) {
+			pixel_t pixel = pixelmap_get_pixel(&rfb->framebuffer, x, y);
+			cpixel_encode(rfb, cpixel, buf, pixel);
+		}
+	}
+	
+	return size;
+}
+
+static ssize_t rfb_tile_encode_solid(rfb_t *rfb, cpixel_ctx_t *cpixel,
+    rfb_rectangle_t *tile, void *buf)
+{
+	/* Check if it is single color */
+	pixel_t the_color = pixelmap_get_pixel(&rfb->framebuffer, tile->x, tile->y);
+	for (uint16_t y = tile->y; y < tile->y + tile->height; y++) {
+		for (uint16_t x = tile->x; x < tile->x + tile->width; x++) {
+			if (pixelmap_get_pixel(&rfb->framebuffer, x, y) != the_color)
+				return -1;
+		}
+	}
+	
+	/* OK, encode it */
+	if (buf)
+		cpixel_encode(rfb, cpixel, buf, the_color);
+	return cpixel->size;
+}
+
+static size_t rfb_rect_encode_trle(rfb_t *rfb, rfb_rectangle_t *rect, void *buf)
+{
+	cpixel_ctx_t cpixel;
+	cpixel_context_init(&cpixel, &rfb->pixel_format);
+	
+	size_t size = 0;
+	for (uint16_t y = 0; y < rect->height; y += 16) {
+		for (uint16_t x = 0; x < rect->width; x += 16) {
+			rfb_rectangle_t tile = {
+				.x = x,
+				.y = y,
+				.width = (x + 16 <= rect->width ? 16 : rect->width - x),
+				.height = (y + 16 <= rect->height ? 16 : rect->height - y)
+			};
+			
+			size += 1;
+			uint8_t *tile_enctype_ptr = buf;
+			if (buf)
+				buf +=  1;
+			
+			uint8_t tile_enctype = RFB_TILE_ENCODING_SOLID;
+			ssize_t tile_size = rfb_tile_encode_solid(rfb, &cpixel, &tile, buf);
+			if (tile_size < 0) {
+				tile_size = rfb_tile_encode_raw(rfb, &cpixel, &tile, buf);
+				tile_enctype = RFB_TILE_ENCODING_RAW;
+			}
+			
+			if (buf) {
+				*tile_enctype_ptr = tile_enctype;
+				buf += tile_size;
+			}
+		}
+	}
+	return size;
+}
+
+static int rfb_send_framebuffer_update(rfb_t *rfb, int conn_sd, bool incremental)
+{
+	fibril_mutex_lock(&rfb->lock);
+	if (!incremental || !rfb->damage_valid) {
+		rfb->damage_rect.x = 0;
+		rfb->damage_rect.y = 0;
+		rfb->damage_rect.width = rfb->width;
+		rfb->damage_rect.height = rfb->height;
+	}
+	
+
+	/* We send only single raw rectangle right now */
+	size_t buf_size = sizeof(rfb_framebuffer_update_t) +
+	    sizeof(rfb_rectangle_t) * 1 +
+	    rfb_rect_encode_raw(rfb, &rfb->damage_rect, NULL)
+	    ;
+	
+	void *buf = malloc(buf_size);
+	if (buf == NULL) {
+		fibril_mutex_unlock(&rfb->lock);
+		return ENOMEM;
+	}
+	memset(buf, 0, buf_size);
+	
+	void *pos = buf;
+	rfb_framebuffer_update_t *fbu = buf;
+	fbu->message_type = RFB_SMSG_FRAMEBUFFER_UPDATE;
+	fbu->rect_count = 1;
+	rfb_framebuffer_update_to_be(fbu, fbu);
+	pos += sizeof(rfb_framebuffer_update_t);
+	
+	rfb_rectangle_t *rect = pos;
+	pos += sizeof(rfb_rectangle_t);
+	
+	*rect = rfb->damage_rect;
+	
+	if (rfb->supports_trle) {
+		rect->enctype = RFB_ENCODING_TRLE;
+		pos += rfb_rect_encode_trle(rfb, rect, pos);
+	}
+	else {
+		rect->enctype = RFB_ENCODING_RAW;
+		pos += rfb_rect_encode_raw(rfb, rect, pos);
+	}
+	rfb_rectangle_to_be(rect, rect);
+	
+	rfb->damage_valid = false;
+	
+	size_t send_palette_size = 0;
+	void *send_palette = NULL;
+	
+	if (!rfb->pixel_format.true_color) {
+		send_palette = rfb_send_palette_message(rfb, &send_palette_size);
+		if (send_palette == NULL) {
+			free(buf);
+			fibril_mutex_unlock(&rfb->lock);
+			return ENOMEM;
+		}
+	}
+	
+	fibril_mutex_unlock(&rfb->lock);
+	
+	if (!rfb->pixel_format.true_color) {
+		int rc = send(conn_sd, send_palette, send_palette_size, 0);
+		if (rc != EOK) {
+			free(buf);
+			return rc;
+		}
+	}
+	
+	int rc = send(conn_sd, buf, buf_size, 0);
+	free(buf);
+	
+	return rc;
+}
+
+static int rfb_set_pixel_format(rfb_t *rfb, rfb_pixel_format_t *pixel_format)
+{
+	rfb->pixel_format = *pixel_format;
+	if (rfb->pixel_format.true_color) {
+		free(rfb->palette);
+		rfb->palette = NULL;
+		rfb->palette_used = 0;
+		log_msg(LOG_DEFAULT, LVL_DEBUG,
+		    "changed pixel format to %d-bit true color (%x<<%d, %x<<%d, %x<<%d)",
+		    pixel_format->depth, pixel_format->r_max, pixel_format->r_shift,
+		    pixel_format->g_max, pixel_format->g_shift, pixel_format->b_max,
+		    pixel_format->b_shift);
+	}
+	else {
+		if (rfb->palette == NULL) {
+			rfb->palette = malloc(sizeof(pixel_t) * 256);
+			if (rfb->palette == NULL)
+				return ENOMEM;
+			memset(rfb->palette, 0, sizeof(pixel_t) * 256);
+			rfb->palette_used = 0;
+		}
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "changed pixel format to %d-bit palette",
+		    pixel_format->depth);
+	}
+	return EOK;
+}
+
+static void rfb_socket_connection(rfb_t *rfb, int conn_sd)
+{
+	/* Version handshake */
+	int rc = send(conn_sd, "RFB 003.008\n", 12, 0);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed sending server version %d", rc);
+		return;
+	}
+	
+	char client_version[12];
+	rc = recv_chars(conn_sd, client_version, 12);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed receiving client version: %d", rc);
+		return;
+	}
+	
+	if (memcmp(client_version, "RFB 003.008\n", 12) != 0) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Client version is not RFB 3.8");
+		return;
+	}
+	
+	/* Security handshake
+	 * 1 security type supported, which is 1 - None
+	 */
+	char sec_types[2];
+	sec_types[0] = 1; /* length */
+	sec_types[1] = RFB_SECURITY_NONE;
+	rc = send(conn_sd, sec_types, 2, 0);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN,
+		    "Failed sending security handshake: %d", rc);
+		return;
+	}
+	
+	char selected_sec_type = 0;
+	rc = recv_char(conn_sd, &selected_sec_type);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed receiving security type: %d", rc);
+		return;
+	}
+	if (selected_sec_type != RFB_SECURITY_NONE) {
+		log_msg(LOG_DEFAULT, LVL_WARN,
+		    "Client selected security type other than none");
+		return;
+	}
+	uint32_t security_result = RFB_SECURITY_HANDSHAKE_OK;
+	rc = send(conn_sd, &security_result, sizeof(uint32_t), 0);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed sending security result: %d", rc);
+		return;
+	}
+	
+	/* Client init */
+	char shared_flag;
+	rc = recv_char(conn_sd, &shared_flag);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed receiving client init: %d", rc);
+		return;
+	}
+	
+	/* Server init */
+	fibril_mutex_lock(&rfb->lock);
+	size_t name_length = str_length(rfb->name);
+	size_t msg_length = sizeof(rfb_server_init_t) + name_length;
+	rfb_server_init_t *server_init = malloc(msg_length);
+	if (server_init == NULL) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Cannot allocate memory for server init");
+		fibril_mutex_unlock(&rfb->lock);
+		return;
+	}
+	server_init->width = rfb->width;
+	server_init->height = rfb->height;
+	server_init->pixel_format = rfb->pixel_format,
+	server_init->name_length = name_length;
+	rfb_server_init_to_be(server_init, server_init);
+	memcpy(server_init->name, rfb->name, name_length);
+	fibril_mutex_unlock(&rfb->lock);
+	rc = send(conn_sd, server_init, msg_length, 0);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed sending server init: %d", rc);
+		return;
+	}
+	
+	while (true) {
+		char message_type = 0;
+		rc = recv_char(conn_sd, &message_type);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_WARN,
+			    "Failed receiving client message type");
+			return;
+		}
+		
+		rfb_set_pixel_format_t spf;
+		rfb_set_encodings_t se;
+		rfb_framebuffer_update_request_t fbur;
+		rfb_key_event_t ke;
+		rfb_pointer_event_t pe;
+		rfb_client_cut_text_t cct;
+		switch (message_type) {
+		case RFB_CMSG_SET_PIXEL_FORMAT:
+			recv_message(conn_sd, message_type, &spf, sizeof(spf));
+			rfb_pixel_format_to_host(&spf.pixel_format, &spf.pixel_format);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2, "Received SetPixelFormat message");
+			fibril_mutex_lock(&rfb->lock);
+			rc = rfb_set_pixel_format(rfb, &spf.pixel_format);
+			fibril_mutex_unlock(&rfb->lock);
+			if (rc != EOK)
+				return;
+			break;
+		case RFB_CMSG_SET_ENCODINGS:
+			recv_message(conn_sd, message_type, &se, sizeof(se));
+			rfb_set_encodings_to_host(&se, &se);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2, "Received SetEncodings message");
+			for (uint16_t i = 0; i < se.count; i++) {
+				int32_t encoding = 0;
+				rc = recv_chars(conn_sd, (char *) &encoding, sizeof(int32_t));
+				if (rc != EOK)
+					return;
+				encoding = uint32_t_be2host(encoding);
+				if (encoding == RFB_ENCODING_TRLE) {
+					log_msg(LOG_DEFAULT, LVL_DEBUG,
+					    "Client supports TRLE encoding");
+					rfb->supports_trle = true;
+				}
+			}
+			break;
+		case RFB_CMSG_FRAMEBUFFER_UPDATE_REQUEST:
+			recv_message(conn_sd, message_type, &fbur, sizeof(fbur));
+			rfb_framebuffer_update_request_to_host(&fbur, &fbur);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2,
+			    "Received FramebufferUpdateRequest message");
+			rfb_send_framebuffer_update(rfb, conn_sd, fbur.incremental);
+			break;
+		case RFB_CMSG_KEY_EVENT:
+			recv_message(conn_sd, message_type, &ke, sizeof(ke));
+			rfb_key_event_to_host(&ke, &ke);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2, "Received KeyEvent message");
+			break;
+		case RFB_CMSG_POINTER_EVENT:
+			recv_message(conn_sd, message_type, &pe, sizeof(pe));
+			rfb_pointer_event_to_host(&pe, &pe);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2, "Received PointerEvent message");
+			break;
+		case RFB_CMSG_CLIENT_CUT_TEXT:
+			recv_message(conn_sd, message_type, &cct, sizeof(cct));
+			rfb_client_cut_text_to_host(&cct, &cct);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2, "Received ClientCutText message");
+			recv_skip_chars(conn_sd, cct.length);
+			break;
+		default:
+			log_msg(LOG_DEFAULT, LVL_WARN,
+			    "Invalid client message type encountered");
+			return;
+		}
+	}
+}
+
+int rfb_listen(rfb_t *rfb, uint16_t port) {
+	struct sockaddr_in addr;
+	
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	
+	int rc = inet_pton(AF_INET, "127.0.0.1", (void *)
+	    &addr.sin_addr.s_addr);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error parsing network address (%s)",
+		    str_error(rc));
+		return rc;
+	}
+
+	int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
+	if (listen_sd < 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating listening socket (%s)",
+		    str_error(listen_sd));
+		return rc;
+	}
+	
+	rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error binding socket (%s)",
+		    str_error(rc));
+		return rc;
+	}
+	
+	rc = listen(listen_sd, 2);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "listen() failed (%s)", str_error(rc));
+		return rc;
+	}
+	
+	rfb->listen_sd = listen_sd;
+	
+	return EOK;
+}
+
+void rfb_accept(rfb_t *rfb)
+{
+	while (true) {
+		struct sockaddr_in raddr;
+		socklen_t raddr_len = sizeof(raddr);
+		int conn_sd = accept(rfb->listen_sd, (struct sockaddr *) &raddr,
+		    &raddr_len);
+		
+		if (conn_sd < 0) {
+			log_msg(LOG_DEFAULT, LVL_WARN, "accept() failed (%s)",
+			    str_error(conn_sd));
+			continue;
+		}
+		
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Connection accepted");
+		
+		rbuf_out = 0;
+		rbuf_in = 0;
+		
+		rfb_socket_connection(rfb, conn_sd);
+		closesocket(conn_sd);
+	}
+}
Index: uspace/srv/hid/rfb/rfb.h
===================================================================
--- uspace/srv/hid/rfb/rfb.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/hid/rfb/rfb.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * 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.
+ */
+
+#ifndef RFB_H__
+#define RFB_H__
+
+#include <io/pixelmap.h>
+#include <fibril_synch.h>
+
+#define RFB_SECURITY_NONE 1
+#define RFB_SECURITY_HANDSHAKE_OK 0
+
+#define RFB_CMSG_SET_PIXEL_FORMAT 0
+#define RFB_CMSG_SET_ENCODINGS 2
+#define RFB_CMSG_FRAMEBUFFER_UPDATE_REQUEST 3
+#define RFB_CMSG_KEY_EVENT 4
+#define RFB_CMSG_POINTER_EVENT 5
+#define RFB_CMSG_CLIENT_CUT_TEXT 6
+
+#define RFB_SMSG_FRAMEBUFFER_UPDATE 0
+#define RFB_SMSG_SET_COLOR_MAP_ENTRIES 1
+#define RFB_SMSG_BELL 2
+#define RFB_SMSG_SERVER_CUT_TEXT 3
+
+#define RFB_ENCODING_RAW 0
+#define RFB_ENCODING_TRLE 15
+
+#define RFB_TILE_ENCODING_RAW 0
+#define RFB_TILE_ENCODING_SOLID 1
+
+typedef struct {
+	uint8_t bpp;
+	uint8_t depth;
+	uint8_t big_endian;
+	uint8_t true_color;
+	uint16_t r_max;
+	uint16_t g_max;
+	uint16_t b_max;
+	uint8_t r_shift;
+	uint8_t g_shift;
+	uint8_t b_shift;
+	uint8_t pad[3];
+} __attribute__((packed)) rfb_pixel_format_t;
+
+typedef struct {
+	uint16_t width;
+	uint16_t height;
+	rfb_pixel_format_t pixel_format;
+	uint32_t name_length;
+	char name[0];
+} __attribute__((packed)) rfb_server_init_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t pad[3];
+	rfb_pixel_format_t pixel_format;
+} __attribute__((packed)) rfb_set_pixel_format_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t pad;
+	uint16_t count;
+} __attribute__((packed)) rfb_set_encodings_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t incremental;
+	uint16_t x;
+	uint16_t y;
+	uint16_t width;
+	uint16_t height;
+} __attribute__((packed)) rfb_framebuffer_update_request_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t down_flag;
+	uint8_t pad[2];
+	uint32_t key;
+} __attribute__((packed)) rfb_key_event_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t button_mask;
+	uint16_t x;
+	uint16_t y;
+} __attribute__((packed)) rfb_pointer_event_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t pad;
+	uint32_t length;
+	char text[0];
+} __attribute__((packed)) rfb_client_cut_text_t;
+
+typedef struct {
+	uint16_t x;
+	uint16_t y;
+	uint16_t width;
+	uint16_t height;
+	int32_t enctype;
+	uint8_t data[0];
+} __attribute__((packed)) rfb_rectangle_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t pad;
+	uint16_t rect_count;
+} __attribute__((packed)) rfb_framebuffer_update_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t pad;
+	uint16_t first_color;
+	uint16_t color_count;
+} __attribute((packed)) rfb_set_color_map_entries_t;
+
+typedef struct {
+	uint16_t red;
+	uint16_t green;
+	uint16_t blue;
+} __attribute__((packed)) rfb_color_map_entry_t;
+
+typedef struct {
+	uint16_t width;
+	uint16_t height;
+	rfb_pixel_format_t pixel_format;
+	const char *name;
+	int listen_sd;
+	pixelmap_t framebuffer;
+	rfb_rectangle_t damage_rect;
+	bool damage_valid;
+	fibril_mutex_t lock;
+	pixel_t *palette;
+	size_t palette_used;
+	bool supports_trle;
+} rfb_t;
+
+
+extern int rfb_init(rfb_t *, uint16_t, uint16_t, const char *);
+extern int rfb_set_size(rfb_t *, uint16_t, uint16_t);
+extern int rfb_listen(rfb_t *, uint16_t);
+extern void rfb_accept(rfb_t *);
+
+#endif
Index: uspace/srv/hw/irc/obio/obio.c
===================================================================
--- uspace/srv/hw/irc/obio/obio.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/hw/irc/obio/obio.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -69,5 +69,5 @@
 #define INO_MASK	0x1f
 
-static void *base_phys;
+static uintptr_t base_phys;
 static volatile uint64_t *base_virt;
 
@@ -123,5 +123,5 @@
 	}
 	
-	base_phys = (void *) paddr;
+	base_phys = (uintptr_t) paddr;
 	
 	int flags = AS_AREA_READ | AS_AREA_WRITE;
@@ -135,5 +135,5 @@
 	}
 	
-	printf("%s: OBIO registers with base at %p\n", NAME, base_phys);
+	printf("%s: OBIO registers with base at %zu\n", NAME, base_phys);
 	
 	async_set_client_connection(obio_connection);
Index: uspace/srv/locsrv/category.c
===================================================================
--- uspace/srv/locsrv/category.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/locsrv/category.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -73,8 +73,5 @@
 
 	size_t pos = 0;
-	list_foreach(cdir->categories, item) {
-		category_t *cat =
-		    list_get_instance(item, category_t, cat_list);
-
+	list_foreach(cdir->categories, cat_list, category_t, cat) {
 		if (pos < buf_cnt)
 			id_buf[pos] = cat->id;
@@ -116,7 +113,5 @@
 
 	/* Verify that category does not contain this service yet. */
-	list_foreach(cat->svc_memb, item) {
-		svc_categ_t *memb = list_get_instance(item, svc_categ_t,
-		    cat_link);
+	list_foreach(cat->svc_memb, cat_link, svc_categ_t, memb) {
 		if (memb->svc == svc) {
 			return EEXIST;
@@ -154,7 +149,5 @@
 	assert(fibril_mutex_is_locked(&cdir->mutex));
 
-	list_foreach(cdir->categories, item) {
-		category_t *cat = list_get_instance(item, category_t,
-		    cat_list);
+	list_foreach(cdir->categories, cat_list, category_t, cat) {
 		if (cat->id == catid)
 			return cat;
@@ -169,7 +162,5 @@
 	assert(fibril_mutex_is_locked(&cdir->mutex));
 
-	list_foreach(cdir->categories, item) {
-		category_t *cat = list_get_instance(item, category_t,
-		    cat_list);
+	list_foreach(cdir->categories, cat_list, category_t, cat) {
 		if (str_cmp(cat->name, name) == 0)
 			return cat;
@@ -197,8 +188,5 @@
 
 	size_t pos = 0;
-	list_foreach(cat->svc_memb, item) {
-		svc_categ_t *memb =
-		    list_get_instance(item, svc_categ_t, cat_link);
-
+	list_foreach(cat->svc_memb, cat_link, svc_categ_t, memb) {
 		if (pos < buf_cnt)
 			id_buf[pos] = memb->svc->id;
Index: uspace/srv/locsrv/locsrv.c
===================================================================
--- uspace/srv/locsrv/locsrv.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/locsrv/locsrv.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -184,7 +184,5 @@
 	assert(fibril_mutex_is_locked(&services_list_mutex));
 	
-	list_foreach(namespaces_list, item) {
-		loc_namespace_t *namespace =
-		    list_get_instance(item, loc_namespace_t, namespaces);
+	list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
 		if (str_cmp(namespace->name, name) == 0)
 			return namespace;
@@ -203,7 +201,5 @@
 	assert(fibril_mutex_is_locked(&services_list_mutex));
 	
-	list_foreach(namespaces_list, item) {
-		loc_namespace_t *namespace =
-		    list_get_instance(item, loc_namespace_t, namespaces);
+	list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
 		if (namespace->id == id)
 			return namespace;
@@ -219,7 +215,5 @@
 	assert(fibril_mutex_is_locked(&services_list_mutex));
 	
-	list_foreach(services_list, item) {
-		loc_service_t *service =
-		    list_get_instance(item, loc_service_t, services);
+	list_foreach(services_list, services, loc_service_t, service) {
 		if ((str_cmp(service->namespace->name, ns_name) == 0)
 		    && (str_cmp(service->name, name) == 0))
@@ -239,7 +233,5 @@
 	assert(fibril_mutex_is_locked(&services_list_mutex));
 	
-	list_foreach(services_list, item) {
-		loc_service_t *service =
-		    list_get_instance(item, loc_service_t, services);
+	list_foreach(services_list, services, loc_service_t, service) {
 		if (service->id == id)
 			return service;
@@ -889,9 +881,5 @@
 	fibril_mutex_lock(&callback_sess_mutex);
 	
-	list_foreach(callback_sess_list, link) {
-		cb_sess_t *cb_sess;
-		
-		cb_sess = list_get_instance(link, cb_sess_t, cb_sess_list);
-		
+	list_foreach(callback_sess_list, cb_sess_list, cb_sess_t, cb_sess) {
 		async_exch_t *exch = async_exchange_begin(cb_sess->sess);
 		async_msg_0(exch, LOC_EVENT_CAT_CHANGE);
@@ -1050,8 +1038,5 @@
 	
 	size_t pos = 0;
-	list_foreach(namespaces_list, item) {
-		loc_namespace_t *namespace =
-		    list_get_instance(item, loc_namespace_t, namespaces);
-		
+	list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
 		desc[pos].id = namespace->id;
 		str_cpy(desc[pos].name, LOC_NAME_MAXLEN, namespace->name);
@@ -1114,8 +1099,5 @@
 	
 	size_t pos = 0;
-	list_foreach(services_list, item) {
-		loc_service_t *service =
-		    list_get_instance(item, loc_service_t, services);
-		
+	list_foreach(services_list, services, loc_service_t, service) {
 		if (service->namespace == namespace) {
 			desc[pos].id = service->id;
Index: uspace/srv/logger/logs.c
===================================================================
--- uspace/srv/logger/logs.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/logger/logs.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -44,6 +44,5 @@
 static logger_log_t *find_log_by_name_and_parent_no_list_lock(const char *name, logger_log_t *parent)
 {
-	list_foreach(log_list, it) {
-		logger_log_t *log = list_get_instance(it, logger_log_t, link);
+	list_foreach(log_list, link, logger_log_t, log) {
 		if ((parent == log->parent) && (str_cmp(log->name, name) == 0))
 			return log;
@@ -148,6 +147,5 @@
 
 	fibril_mutex_lock(&log_list_guard);
-	list_foreach(log_list, it) {
-		logger_log_t *log = list_get_instance(it, logger_log_t, link);
+	list_foreach(log_list, link, logger_log_t, log) {
 		if (str_cmp(log->full_name, name) == 0) {
 			fibril_mutex_lock(&log->guard);
@@ -166,6 +164,5 @@
 
 	fibril_mutex_lock(&log_list_guard);
-	list_foreach(log_list, it) {
-		logger_log_t *log = list_get_instance(it, logger_log_t, link);
+	list_foreach(log_list, link, logger_log_t, log) {
 		if ((sysarg_t) log == id) {
 			fibril_mutex_lock(&log->guard);
Index: uspace/srv/net/dhcp/Makefile
===================================================================
--- uspace/srv/net/dhcp/Makefile	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/net/dhcp/Makefile	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -0,0 +1,35 @@
+#
+# 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.
+#
+
+USPACE_PREFIX = ../../..
+BINARY = dhcp
+
+SOURCES = \
+	dhcp.c 
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/net/dhcp/dhcp.c
===================================================================
--- uspace/srv/net/dhcp/dhcp.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/net/dhcp/dhcp.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -0,0 +1,491 @@
+/*
+ * 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 dhcp
+ * @{
+ */
+/**
+ * @file
+ * @brief DHCP client
+ */
+
+#include <bitops.h>
+#include <inet/addr.h>
+#include <inet/dnsr.h>
+#include <inet/inetcfg.h>
+#include <loc.h>
+#include <net/in.h>
+#include <net/inet.h>
+#include <net/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "dhcp_std.h"
+
+#define NAME "dhcp"
+
+#define MAX_MSG_SIZE 1024
+
+static int transport_fd = -1;
+static addr48_t mac_addr;
+static uint8_t msgbuf[MAX_MSG_SIZE];
+
+typedef struct {
+	/** Message type */
+	enum dhcp_msg_type msg_type;
+	/** Offered address */
+	inet_naddr_t oaddr;
+	/** Server address */
+	inet_addr_t srv_addr;
+	/** Router address */
+	inet_addr_t router;
+	/** DNS server */
+	inet_addr_t dns_server;
+} dhcp_offer_t;
+
+/** Decode subnet mask into subnet prefix length. */
+static int subnet_mask_decode(uint32_t mask, int *bits)
+{
+	int zbits;
+	uint32_t nmask;
+
+	if (mask == 0xffffffff) {
+		*bits = 32;
+		return EOK;
+	}
+
+	zbits = 1 + fnzb32(mask ^ 0xffffffff);
+	nmask = BIT_RRANGE(uint32_t, zbits);
+
+	if ((mask ^ nmask) != 0xffffffff) {
+		/* The mask is not in the form 1**n,0**m */
+		return EINVAL;
+	}
+
+	*bits = 32 - zbits;
+	return EOK;
+}
+
+static uint32_t dhcp_uint32_decode(uint8_t *data)
+{
+	return
+	    ((uint32_t)data[0] << 24) |
+	    ((uint32_t)data[1] << 16) |
+	    ((uint32_t)data[2] << 8) |
+	    ((uint32_t)data[3]);
+}
+
+static int dhcp_send(void *msg, size_t size)
+{
+	struct sockaddr_in addr;
+	int rc;
+
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(dhcp_server_port);
+	addr.sin_addr.s_addr = htonl(addr32_broadcast_all_hosts);
+
+	rc = sendto(transport_fd, msg, size, 0,
+	    (struct sockaddr *)&addr, sizeof(addr));
+	if (rc != EOK) {
+		printf("Sending failed\n");
+		return rc;
+	}
+
+	return EOK;
+}
+
+static int dhcp_send_discover(void)
+{
+	dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
+	uint8_t *opt = msgbuf + sizeof(dhcp_hdr_t);
+
+	memset(msgbuf, 0, MAX_MSG_SIZE);
+	hdr->op = op_bootrequest;
+	hdr->htype = 1; /* AHRD_ETHERNET */
+	hdr->hlen = sizeof(addr48_t);
+	hdr->xid = host2uint32_t_be(42);
+	hdr->flags = flag_broadcast;
+
+	addr48(mac_addr, hdr->chaddr);
+	hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
+
+	opt[0] = opt_msg_type;
+	opt[1] = 1;
+	opt[2] = msg_dhcpdiscover;
+	opt[3] = opt_end;
+
+	return dhcp_send(msgbuf, sizeof(dhcp_hdr_t) + 4);
+}
+
+static int dhcp_recv_msg(void **rmsg, size_t *rsize)
+{
+	struct sockaddr_in src_addr;
+	socklen_t src_addr_size;
+	size_t recv_size;
+	int rc;
+
+	src_addr_size = sizeof(src_addr);
+	rc = recvfrom(transport_fd, msgbuf, MAX_MSG_SIZE, 0,
+	    (struct sockaddr *)&src_addr, &src_addr_size);
+	if (rc < 0) {
+		printf("recvfrom failed (%d)\n", rc);
+		return rc;
+	}
+
+	recv_size = (size_t)rc;
+	*rmsg = msgbuf;
+	*rsize = recv_size;
+
+	return EOK;
+}
+
+static int dhcp_send_request(dhcp_offer_t *offer)
+{
+	dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
+	uint8_t *opt = msgbuf + sizeof(dhcp_hdr_t);
+	size_t i;
+
+	memset(msgbuf, 0, MAX_MSG_SIZE);
+	hdr->op = op_bootrequest;
+	hdr->htype = 1; /* AHRD_ETHERNET */
+	hdr->hlen = 6;
+	hdr->xid = host2uint32_t_be(42);
+	hdr->flags = flag_broadcast;
+	hdr->ciaddr = host2uint32_t_be(offer->oaddr.addr);
+	addr48(mac_addr, hdr->chaddr);
+	hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
+
+	i = 0;
+
+	opt[i++] = opt_msg_type;
+	opt[i++] = 1;
+	opt[i++] = msg_dhcprequest;
+
+	opt[i++] = opt_req_ip_addr;
+	opt[i++] = 4;
+	opt[i++] = offer->oaddr.addr >> 24;
+	opt[i++] = (offer->oaddr.addr >> 16) & 0xff;
+	opt[i++] = (offer->oaddr.addr >> 8) & 0xff;
+	opt[i++] = offer->oaddr.addr & 0xff;
+
+	opt[i++] = opt_server_id;
+	opt[i++] = 4;
+	opt[i++] = offer->srv_addr.addr >> 24;
+	opt[i++] = (offer->srv_addr.addr >> 16) & 0xff;
+	opt[i++] = (offer->srv_addr.addr >> 8) & 0xff;
+	opt[i++] = offer->srv_addr.addr & 0xff;
+
+	opt[i++] = opt_end;
+
+	return dhcp_send(msgbuf, sizeof(dhcp_hdr_t) + i);
+}
+
+static int dhcp_recv_reply(void *msg, size_t size, dhcp_offer_t *offer)
+{
+	dhcp_hdr_t *hdr = (dhcp_hdr_t *)msg;
+	inet_addr_t yiaddr;
+	inet_addr_t siaddr;
+	inet_addr_t giaddr;
+	uint32_t subnet_mask;
+	bool have_subnet_mask = false;
+	bool have_server_id = false;
+	int subnet_bits;
+	char *saddr;
+	uint8_t opt_type, opt_len;
+	uint8_t *msgb;
+	int rc;
+	size_t i;
+
+	printf("Receive reply\n");
+	memset(offer, 0, sizeof(*offer));
+
+	yiaddr.family = AF_INET;
+	yiaddr.addr = uint32_t_be2host(hdr->yiaddr);
+	rc = inet_addr_format(&yiaddr, &saddr);
+	if (rc != EOK)
+		return rc;
+
+	printf("Your IP address: %s\n", saddr);
+	free(saddr);
+
+	siaddr.family = AF_INET;
+	siaddr.addr = uint32_t_be2host(hdr->siaddr);
+	rc = inet_addr_format(&siaddr, &saddr);
+	if (rc != EOK)
+		return rc;
+
+	printf("Next server IP address: %s\n", saddr);
+	free(saddr);
+
+	giaddr.family = AF_INET;
+	giaddr.addr = uint32_t_be2host(hdr->giaddr);
+	rc = inet_addr_format(&giaddr, &saddr);
+	if (rc != EOK)
+		return rc;
+
+	printf("Relay agent IP address: %s\n", saddr);
+	free(saddr);
+
+	offer->oaddr.family = AF_INET;
+	offer->oaddr.addr = yiaddr.addr;
+
+	msgb = (uint8_t *)msg;
+
+	i = sizeof(dhcp_hdr_t);
+	while (i < size) {
+		opt_type = msgb[i++];
+
+		if (opt_type == opt_pad)
+			continue;
+		if (opt_type == opt_end)
+			break;
+
+		if (i >= size)
+			return EINVAL;
+
+		opt_len = msgb[i++];
+
+		if (i + opt_len > size)
+			return EINVAL;
+
+		switch (opt_type) {
+		case opt_subnet_mask:
+			if (opt_len != 4)
+				return EINVAL;
+			subnet_mask = dhcp_uint32_decode(&msgb[i]);
+			rc = subnet_mask_decode(subnet_mask, &subnet_bits);
+			if (rc != EOK)
+				return EINVAL;
+			offer->oaddr.prefix = subnet_bits;
+			have_subnet_mask = true;
+			break;
+		case opt_msg_type:
+			if (opt_len != 1)
+				return EINVAL;
+			offer->msg_type = msgb[i];
+			break;
+		case opt_server_id:
+			if (opt_len != 4)
+				return EINVAL;
+			offer->srv_addr.family = AF_INET;
+			offer->srv_addr.addr = dhcp_uint32_decode(&msgb[i]);
+			have_server_id = true;
+			break;
+		case opt_router:
+			if (opt_len != 4)
+				return EINVAL;
+			offer->router.family = AF_INET;
+			offer->router.addr = dhcp_uint32_decode(&msgb[i]);
+			break;
+		case opt_dns_server:
+			if (opt_len != 4)
+				return EINVAL;
+			offer->dns_server.family = AF_INET;
+			offer->dns_server.addr = dhcp_uint32_decode(&msgb[i]);
+			break;
+		case opt_end:
+			break;
+		default:
+			break;
+		}
+
+		/* Advance to the next option */
+		i = i + opt_len;
+	}
+
+	if (!have_server_id) {
+		printf("Missing server ID option.\n");
+		return rc;
+	}
+
+	if (!have_subnet_mask) {
+		printf("Missing subnet mask option.\n");
+		return rc;
+	}
+
+	rc = inet_naddr_format(&offer->oaddr, &saddr);
+	if (rc != EOK)
+		return rc;
+
+	printf("Offered network address: %s\n", saddr);
+	free(saddr);
+
+	if (offer->router.addr != 0) {
+		rc = inet_addr_format(&offer->router, &saddr);
+		if (rc != EOK)
+			return rc;
+
+		printf("Router address: %s\n", saddr);
+		free(saddr);
+	}
+
+	if (offer->dns_server.addr != 0) {
+		rc = inet_addr_format(&offer->dns_server, &saddr);
+		if (rc != EOK)
+			return rc;
+
+		printf("DNS server: %s\n", saddr);
+		free(saddr);
+	}
+
+	return EOK;
+}
+
+static int dhcp_cfg_create(service_id_t iplink, dhcp_offer_t *offer)
+{
+	int rc;
+	service_id_t addr_id;
+	service_id_t sroute_id;
+	inet_naddr_t defr;
+
+	rc = inetcfg_addr_create_static("dhcp4a", &offer->oaddr, iplink,
+	    &addr_id);
+	if (rc != EOK) {
+		printf("Error creating IP address %s (%d)\n", "dhcp4a", rc);
+		return rc;
+	}
+
+	if (offer->router.addr != 0) {
+		defr.family = AF_INET;
+		defr.addr = 0;
+		defr.prefix = 0;
+
+		rc = inetcfg_sroute_create("dhcpdef", &defr, &offer->router, &sroute_id);
+		if (rc != EOK) {
+			printf("Error creating default route %s (%d).\n", "dhcpdef",
+			    rc);
+			return rc;
+		}
+	}
+
+	if (offer->dns_server.addr != 0) {
+		rc = dnsr_set_srvaddr(&offer->dns_server);
+		if (rc != EOK) {
+			printf("%s: Failed setting nameserver address (%d))\n",
+			    NAME, rc);
+			return rc;
+		}
+	}
+
+	return EOK;
+}
+
+int main(int argc, char *argv[])
+{
+	int fd;
+	struct sockaddr_in laddr;
+	void *msg;
+	service_id_t iplink;
+	size_t msg_size;
+	dhcp_offer_t offer;
+	int rc;
+
+	if (argc < 2) {
+		printf("syntax: %s <ip-link>\n", NAME);
+		return 1;
+	}
+
+	rc = inetcfg_init();
+	if (rc != EOK) {
+		printf("Failed contacting inet configuration service.\n");
+		return 1;
+	}
+
+	rc = loc_service_get_id(argv[1], &iplink, 0);
+	if (rc != EOK) {
+		printf("Failed resolving service '%s'.\n", argv[1]);
+		return 1;
+	}
+
+	/* XXX Determine MAC address automatically */
+	mac_addr[0] = 0xaa;
+	mac_addr[1] = 0xde;
+	mac_addr[2] = 0xad;
+	mac_addr[3] = 0xbe;
+	mac_addr[4] = 0xef;
+	mac_addr[5] = 0xfe;
+
+	laddr.sin_family = AF_INET;
+	laddr.sin_port = htons(dhcp_client_port);
+	laddr.sin_addr.s_addr = INADDR_ANY;
+
+	fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (fd < 0)
+		return 1;
+
+	printf("Bind socket.\n");
+	rc = bind(fd, (struct sockaddr *)&laddr, sizeof(laddr));
+	if (rc != EOK)
+		return 1;
+
+	printf("Set socket options\n");
+	rc = setsockopt(fd, SOL_SOCKET, SO_IPLINK, &iplink, sizeof(iplink));
+	if (rc != EOK)
+		return 1;
+
+	transport_fd = fd;
+
+	printf("Send DHCPDISCOVER\n");
+	rc = dhcp_send_discover();
+	if (rc != EOK)
+		return 1;
+
+	rc = dhcp_recv_msg(&msg, &msg_size);
+	if (rc != EOK)
+		return 1;
+
+	printf("Received %zu bytes\n", msg_size);
+
+	rc = dhcp_recv_reply(msg, msg_size, &offer);
+	if (rc != EOK)
+		return 1;
+
+	rc = dhcp_send_request(&offer);
+	if (rc != EOK)
+		return 1;
+
+	rc = dhcp_recv_msg(&msg, &msg_size);
+	if (rc != EOK)
+		return 1;
+
+	printf("Received %zu bytes\n", msg_size);
+
+	rc = dhcp_recv_reply(msg, msg_size, &offer);
+	if (rc != EOK)
+		return 1;
+
+	rc = dhcp_cfg_create(iplink, &offer);
+	if (rc != EOK)
+		return 1;
+
+	closesocket(fd);
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/srv/net/dhcp/dhcp_std.h
===================================================================
--- uspace/srv/net/dhcp/dhcp_std.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
+++ uspace/srv/net/dhcp/dhcp_std.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -0,0 +1,130 @@
+/*
+ * 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 dhcp
+ * @{
+ */
+/**
+ * @file
+ * @brief DHCP standard definitions
+ */
+
+#ifndef DHCP_STD_H
+#define DHCP_STD_H
+
+#include <stdint.h>
+
+enum {
+	dhcp_server_port = 67,
+	dhcp_client_port = 68
+};
+
+/** Fixed part of DHCP message */
+typedef struct {
+	/** Message op code */
+	uint8_t op;
+	/** Hardware address type */
+	uint8_t htype;
+	/** Hardware address length */
+	uint8_t hlen;
+	/** Hops */
+	uint8_t hops;
+	/** Transaction ID */
+	uint32_t xid;
+	/** Seconds elapsed since client began address acqusition or renewal */
+	uint16_t secs;
+	/** Flags */
+	uint16_t flags;
+	/** Client IP address */
+	uint32_t ciaddr;
+	/** Your (client) IP address */
+	uint32_t yiaddr;
+	/** IP address of next server */
+	uint32_t siaddr;
+	/** Relay agent IP address */
+	uint32_t giaddr;
+	/** Client hardware address */
+	uint8_t chaddr[16];
+	/** Server host name */
+	uint8_t sname[64];
+	/** Boot file name */
+	uint8_t file[128];
+	/** Magic cookie signalling the start of DHCP options */
+	uint32_t opt_magic;
+} dhcp_hdr_t;
+
+/** Values for dhcp_hdr_t.op */
+enum dhcp_op {
+	op_bootrequest = 1,
+	op_bootreply = 2
+};
+
+/** Values for dhcp_hdr_t.flags */
+enum dhcp_flags {
+	flag_broadcast = 0x80
+};
+
+/** Magic cookie signalling the start of DHCP options field */
+enum {
+	dhcp_opt_magic = (99 << 24) | (130 << 16) | (83 << 8) | 99
+};
+
+enum dhcp_option_code {
+	/** Padding */
+	opt_pad = 0,
+	/** Subnet mask */
+	opt_subnet_mask = 1,
+	/** Router IP address */
+	opt_router = 3,
+	/** Domain name server */
+	opt_dns_server = 6,
+	/** Requested IP address */
+	opt_req_ip_addr = 50,
+	/** DHCP message type */
+	opt_msg_type = 53,
+	/** Server identifier */
+	opt_server_id = 54,
+	/** End */
+	opt_end = 255
+};
+
+/** DHCP message type */
+enum dhcp_msg_type {
+	msg_dhcpdiscover = 1,
+	msg_dhcpoffer = 2,
+	msg_dhcprequest = 3,
+	msg_dhcpdecline = 4,
+	msg_dhcpack = 5,
+	msg_dhcpnak = 6,
+	msg_dhcprelease = 7
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/dnsrsrv/dns_msg.c
===================================================================
--- uspace/srv/net/dnsrsrv/dns_msg.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/dnsrsrv/dns_msg.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -510,6 +510,5 @@
 	size = sizeof(dns_header_t);
 
-	list_foreach(msg->question, link) {
-		dns_question_t *q = list_get_instance(link, dns_question_t, msg);
+	list_foreach(msg->question, msg, dns_question_t, q) {
 		rc = dns_question_encode(q, NULL, 0, &q_size);
 		if (rc != EOK)
@@ -526,6 +525,5 @@
 	di = sizeof(dns_header_t);
 
-	list_foreach(msg->question, link) {
-		dns_question_t *q = list_get_instance(link, dns_question_t, msg);
+	list_foreach(msg->question, msg, dns_question_t, q) {
 		rc = dns_question_encode(q, data + di, size - di, &q_size);
 		if (rc != EOK) {
Index: uspace/srv/net/dnsrsrv/dnsrsrv.c
===================================================================
--- uspace/srv/net/dnsrsrv/dnsrsrv.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/dnsrsrv/dnsrsrv.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -60,5 +60,5 @@
 	rc = transport_init();
 	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed initializing tarnsport.");
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed initializing transport.");
 		return EIO;
 	}
Index: uspace/srv/net/dnsrsrv/query.c
===================================================================
--- uspace/srv/net/dnsrsrv/query.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/dnsrsrv/query.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -99,7 +99,5 @@
 	}
 	
-	list_foreach(amsg->answer, link) {
-		dns_rr_t *rr = list_get_instance(link, dns_rr_t, msg);
-		
+	list_foreach(amsg->answer, msg, dns_rr_t, rr) {
 		log_msg(LOG_DEFAULT, LVL_DEBUG, " - '%s' %u/%u, dsize %zu",
 		    rr->name, rr->rtype, rr->rclass, rr->rdata_size);
Index: uspace/srv/net/dnsrsrv/transport.c
===================================================================
--- uspace/srv/net/dnsrsrv/transport.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/dnsrsrv/transport.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -158,7 +158,5 @@
 	assert(fibril_mutex_is_locked(&treq_lock));
 
-	list_foreach(treq_list, link) {
-		trans_req_t *treq = list_get_instance(link, trans_req_t, lreq);
-
+	list_foreach(treq_list, lreq, trans_req_t, treq) {
 		if (treq->req->id == resp->id) {
 			/* Match */
Index: uspace/srv/net/ethip/arp.c
===================================================================
--- uspace/srv/net/ethip/arp.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/ethip/arp.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -98,4 +98,10 @@
     addr48_t mac_addr)
 {
+	/* Broadcast address */
+	if (ip_addr == addr32_broadcast_all_hosts) {
+		addr48(addr48_broadcast, mac_addr);
+		return EOK;
+	}
+
 	int rc = atrans_lookup(ip_addr, mac_addr);
 	if (rc == EOK)
Index: uspace/srv/net/ethip/atrans.c
===================================================================
--- uspace/srv/net/ethip/atrans.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/ethip/atrans.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -51,8 +51,5 @@
 static ethip_atrans_t *atrans_find(addr32_t ip_addr)
 {
-	list_foreach(atrans_list, link) {
-		ethip_atrans_t *atrans = list_get_instance(link,
-		    ethip_atrans_t, atrans_list);
-
+	list_foreach(atrans_list, atrans_list, ethip_atrans_t, atrans) {
 		if (atrans->ip_addr == ip_addr)
 			return atrans;
Index: uspace/srv/net/ethip/ethip_nic.c
===================================================================
--- uspace/srv/net/ethip/ethip_nic.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/ethip/ethip_nic.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -84,7 +84,5 @@
 		already_known = false;
 
-		list_foreach(ethip_nic_list, link) {
-			ethip_nic_t *nic = list_get_instance(link,
-			    ethip_nic_t, link);
+		list_foreach(ethip_nic_list, link, ethip_nic_t, nic) {
 			if (nic->svc_id == svcs[i]) {
 				already_known = true;
@@ -201,4 +199,11 @@
 		log_msg(LOG_DEFAULT, LVL_ERROR, "Error activating NIC '%s'.",
 		    nic->svc_name);
+		goto error;
+	}
+
+	rc = nic_broadcast_set_mode(nic->sess, NIC_BROADCAST_ACCEPTED);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error enabling "
+		    "reception of broadcast frames on '%s'.", nic->svc_name);
 		goto error;
 	}
@@ -313,8 +318,6 @@
 	    (unsigned) iplink_sid);
 
-	list_foreach(ethip_nic_list, link) {
+	list_foreach(ethip_nic_list, link, ethip_nic_t, nic) {
 		log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_find_by_iplink_sid - element");
-		ethip_nic_t *nic = list_get_instance(link, ethip_nic_t, link);
-
 		if (nic->iplink_sid == iplink_sid) {
 			log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_find_by_iplink_sid - found %p", nic);
@@ -350,8 +353,5 @@
 	size_t count = 0;
 	
-	list_foreach(nic->addr_list, link) {
-		ethip_link_addr_t *laddr = list_get_instance(link,
-		    ethip_link_addr_t, link);
-		
+	list_foreach(nic->addr_list, link, ethip_link_addr_t, laddr) {
 		uint16_t af = inet_addr_get(&laddr->addr, NULL, NULL);
 		if (af == AF_INET6)
@@ -371,14 +371,11 @@
 	size_t i = 0;
 	
-	list_foreach(nic->addr_list, link) {
-		assert(i < count);
-		
-		ethip_link_addr_t *laddr = list_get_instance(link,
-		    ethip_link_addr_t, link);
-		
+	list_foreach(nic->addr_list, link, ethip_link_addr_t, laddr) {
 		addr128_t v6;
 		uint16_t af = inet_addr_get(&laddr->addr, NULL, &v6);
 		if (af != AF_INET6)
 			continue;
+		
+		assert(i < count);
 		
 		addr48_t mac;
@@ -444,8 +441,5 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_addr_find()");
 	
-	list_foreach(nic->addr_list, link) {
-		ethip_link_addr_t *laddr = list_get_instance(link,
-		    ethip_link_addr_t, link);
-		
+	list_foreach(nic->addr_list, link, ethip_link_addr_t, laddr) {
 		if (inet_addr_compare(addr, &laddr->addr))
 			return laddr;
Index: uspace/srv/net/inetsrv/addrobj.c
===================================================================
--- uspace/srv/net/inetsrv/addrobj.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/inetsrv/addrobj.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -115,8 +115,5 @@
 	fibril_mutex_lock(&addr_list_lock);
 	
-	list_foreach(addr_list, link) {
-		inet_addrobj_t *naddr = list_get_instance(link,
-		    inet_addrobj_t, addr_list);
-		
+	list_foreach(addr_list, addr_list, inet_addrobj_t, naddr) {
 		switch (find) {
 		case iaf_net:
@@ -158,8 +155,5 @@
 	    name, ilink->svc_name);
 
-	list_foreach(addr_list, link) {
-		inet_addrobj_t *naddr = list_get_instance(link,
-		    inet_addrobj_t, addr_list);
-
+	list_foreach(addr_list, addr_list, inet_addrobj_t, naddr) {
 		if (naddr->ilink == ilink && str_cmp(naddr->name, name) == 0) {
 			log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_addrobj_find_by_name_locked: found %p",
@@ -206,8 +200,5 @@
 	fibril_mutex_lock(&addr_list_lock);
 
-	list_foreach(addr_list, link) {
-		inet_addrobj_t *naddr = list_get_instance(link,
-		    inet_addrobj_t, addr_list);
-
+	list_foreach(addr_list, addr_list, inet_addrobj_t, naddr) {
 		if (naddr->id == id) {
 			fibril_mutex_unlock(&addr_list_lock);
@@ -277,8 +268,5 @@
 
 	i = 0;
-	list_foreach(addr_list, link) {
-		inet_addrobj_t *addr = list_get_instance(link,
-		    inet_addrobj_t, addr_list);
-
+	list_foreach(addr_list, addr_list, inet_addrobj_t, addr) {
 		id_list[i++] = addr->id;
 	}
Index: uspace/srv/net/inetsrv/icmp.c
===================================================================
--- uspace/srv/net/inetsrv/icmp.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/inetsrv/icmp.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -105,4 +105,5 @@
 	reply->checksum = host2uint16_t_be(checksum);
 
+	rdgram.iplink = 0;
 	rdgram.src = dgram->dest;
 	rdgram.dest = dgram->src;
@@ -171,4 +172,5 @@
 	inet_addr_set(sdu->dest, &dgram.dest);
 	
+	dgram.iplink = 0;
 	dgram.tos = ICMP_TOS;
 	dgram.data = rdata;
Index: uspace/srv/net/inetsrv/inet_link.c
===================================================================
--- uspace/srv/net/inetsrv/inet_link.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/inetsrv/inet_link.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -140,7 +140,5 @@
 		already_known = false;
 
-		list_foreach(inet_link_list, ilink_link) {
-			inet_link_t *ilink = list_get_instance(ilink_link,
-			    inet_link_t, link_list);
+		list_foreach(inet_link_list, link_list, inet_link_t, ilink) {
 			if (ilink->svc_id == svcs[i]) {
 				already_known = true;
@@ -237,4 +235,5 @@
 	inet_addrobj_t *addr = NULL;
 	
+	/* XXX FIXME Cannot rely on loopback being the first IP link service!! */
 	if (first_link) {
 		addr = inet_addrobj_new();
@@ -242,13 +241,4 @@
 		inet_naddr(&addr->naddr, 127, 0, 0, 1, 24);
 		first_link = false;
-	} else {
-		/*
-		 * FIXME
-		 * Setting static IPv4 address for testing purposes:
-		 * 10.0.2.15/24
-		 */
-		addr = inet_addrobj_new();
-		
-		inet_naddr(&addr->naddr, 10, 0, 2, 15, 24);
 	}
 	
@@ -492,8 +482,5 @@
 	fibril_mutex_lock(&inet_discovery_lock);
 
-	list_foreach(inet_link_list, elem) {
-		inet_link_t *ilink = list_get_instance(elem, inet_link_t,
-		    link_list);
-
+	list_foreach(inet_link_list, link_list, inet_link_t, ilink) {
 		if (ilink->svc_id == link_id) {
 			fibril_mutex_unlock(&inet_discovery_lock);
Index: uspace/srv/net/inetsrv/inetping.c
===================================================================
--- uspace/srv/net/inetsrv/inetping.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/inetsrv/inetping.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -82,8 +82,5 @@
 	fibril_mutex_lock(&client_list_lock);
 	
-	list_foreach(client_list, link) {
-		inetping_client_t *client = list_get_instance(link,
-		    inetping_client_t, client_list);
-		
+	list_foreach(client_list, client_list, inetping_client_t, client) {
 		if (client->ident == ident) {
 			fibril_mutex_unlock(&client_list_lock);
Index: uspace/srv/net/inetsrv/inetping6.c
===================================================================
--- uspace/srv/net/inetsrv/inetping6.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/inetsrv/inetping6.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -82,8 +82,5 @@
 	fibril_mutex_lock(&client_list_lock);
 	
-	list_foreach(client_list, link) {
-		inetping6_client_t *client = list_get_instance(link,
-		    inetping6_client_t, client_list);
-		
+	list_foreach(client_list, client_list, inetping6_client_t, client) {
 		if (client->ident == ident) {
 			fibril_mutex_unlock(&client_list_lock);
Index: uspace/srv/net/inetsrv/inetsrv.c
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/inetsrv/inetsrv.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -68,4 +68,9 @@
 };
 
+static inet_addr_t broadcast4_all_hosts = {
+	.family = AF_INET,
+	.addr = 0xffffffff
+};
+
 static inet_addr_t multicast_all_nodes = {
 	.family = AF_INET6,
@@ -119,15 +124,4 @@
 	}
 	
-	inet_sroute_t *sroute = inet_sroute_new();
-	if (sroute == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed creating default route (%d).", rc);
-		return ENOMEM;
-	}
-
-	inet_naddr(&sroute->dest, 0, 0, 0, 0, 0);
-	inet_addr(&sroute->router, 10, 0, 2, 2);
-	sroute->name = str_dup("default");
-	inet_sroute_add(sroute);
-
 	rc = inet_link_discovery_start();
 	if (rc != EOK)
@@ -186,5 +180,26 @@
 {
 	inet_dir_t dir;
+	inet_link_t *ilink;
 	int rc;
+
+	if (dgram->iplink != 0) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "dgram directly to iplink %zu",
+		    dgram->iplink);
+		/* Send packet directly to the specified IP link */
+		ilink = inet_link_get_by_id(dgram->iplink);
+		if (ilink == 0)
+			return ENOENT;
+
+		if (dgram->src.family != AF_INET ||
+			dgram->dest.family != AF_INET)
+			return EINVAL;
+
+		return inet_link_send_dgram(ilink, dgram->src.addr,
+		    dgram->dest.addr, dgram, proto, ttl, df);
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dgram to be routed");
+
+	/* Route packet using source/destination addresses */
 
 	rc = inet_find_dir(&dgram->src, &dgram->dest, dgram->tos, &dir);
@@ -214,4 +229,9 @@
 
 	/* Take source address from the address object */
+	if (remote->family == AF_INET && remote->addr == 0xffffffff) {
+		local->family = AF_INET;
+		local->addr = 0;
+		return EOK;
+	}
 	inet_naddr_addr(&dir.aobj->naddr, local);
 	return EOK;
@@ -282,7 +302,8 @@
 	inet_dgram_t dgram;
 	
-	dgram.tos = IPC_GET_ARG1(*icall);
-	
-	uint8_t ttl = IPC_GET_ARG2(*icall);
+	dgram.iplink = IPC_GET_ARG1(*icall);
+	dgram.tos = IPC_GET_ARG2(*icall);
+	
+	uint8_t ttl = IPC_GET_ARG3(*icall);
 	int df = IPC_GET_ARG3(*icall);
 	
@@ -446,8 +467,5 @@
 	fibril_mutex_lock(&client_list_lock);
 
-	list_foreach(client_list, link) {
-		inet_client_t *client = list_get_instance(link, inet_client_t,
-		    client_list);
-
+	list_foreach(client_list, client_list, inet_client_t, client) {
 		if (client->protocol == proto) {
 			fibril_mutex_unlock(&client_list_lock);
@@ -527,5 +545,6 @@
 	if ((addr != NULL) ||
 	    (inet_naddr_compare_mask(&solicited_node_mask, &packet->dest)) ||
-	    (inet_addr_compare(&multicast_all_nodes, &packet->dest))) {
+	    (inet_addr_compare(&multicast_all_nodes, &packet->dest)) || 
+	    (inet_addr_compare(&broadcast4_all_hosts, &packet->dest))) {
 		/* Destined for one of the local addresses */
 
Index: uspace/srv/net/inetsrv/inetsrv.h
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.h	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/inetsrv/inetsrv.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -127,4 +127,5 @@
 
 typedef struct {
+	service_id_t iplink;
 	inet_addr_t src;
 	inet_addr_t dest;
Index: uspace/srv/net/inetsrv/ntrans.c
===================================================================
--- uspace/srv/net/inetsrv/ntrans.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/inetsrv/ntrans.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -56,8 +56,5 @@
 static inet_ntrans_t *ntrans_find(addr128_t ip_addr)
 {
-	list_foreach(ntrans_list, link) {
-		inet_ntrans_t *ntrans = list_get_instance(link,
-		    inet_ntrans_t, ntrans_list);
-
+	list_foreach(ntrans_list, ntrans_list, inet_ntrans_t, ntrans) {
 		if (addr128_compare(ntrans->ip_addr, ip_addr))
 			return ntrans;
Index: uspace/srv/net/inetsrv/reass.c
===================================================================
--- uspace/srv/net/inetsrv/reass.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/inetsrv/reass.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -129,8 +129,5 @@
 	assert(fibril_mutex_is_locked(&reass_dgram_map_lock));
 
-	list_foreach(reass_dgram_map, link) {
-		reass_dgram_t *rdg = list_get_instance(link, reass_dgram_t,
-		    map_link);
-
+	list_foreach(reass_dgram_map, map_link, reass_dgram_t, rdg) {
 		link_t *f1_link = list_first(&rdg->frags);
 		assert(f1_link != NULL);
@@ -289,6 +286,6 @@
 	size_t fragoff_limit;
 	inet_dgram_t dgram;
+	uint8_t proto;
 	reass_frag_t *frag;
-	uint8_t proto;
 
 	/*
@@ -297,9 +294,9 @@
 	 */
 	frag = NULL;
-	list_foreach(rdg->frags, link) {
-		frag = list_get_instance(link, reass_frag_t, dgram_link);
-
-		if (!frag->packet.mf)
+	list_foreach(rdg->frags, dgram_link, reass_frag_t, cfrag) {
+		if (!cfrag->packet.mf) {
+			frag = cfrag;
 			break;
+		}
 	}
 
@@ -330,20 +327,17 @@
 	size_t doffs = 0;
 
-	frag = NULL;
-	list_foreach(rdg->frags, link) {
-		frag = list_get_instance(link, reass_frag_t, dgram_link);
-
+	list_foreach(rdg->frags, dgram_link, reass_frag_t, cfrag) {
 		size_t cb, ce;
 
-		cb = max(doffs, frag->packet.offs);
-		ce = min(dgram_size, frag->packet.offs + frag->packet.size);
+		cb = max(doffs, cfrag->packet.offs);
+		ce = min(dgram_size, cfrag->packet.offs + cfrag->packet.size);
 
 		if (ce > cb) {
 			memcpy(dgram.data + cb,
-			    frag->packet.data + cb - frag->packet.offs,
+			    cfrag->packet.data + cb - cfrag->packet.offs,
 			    ce - cb);
 		}
 
-		if (!frag->packet.mf)
+		if (!cfrag->packet.mf)
 			break;
 	}
Index: uspace/srv/net/inetsrv/sroute.c
===================================================================
--- uspace/srv/net/inetsrv/sroute.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/inetsrv/sroute.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -89,5 +89,5 @@
 }
 
-/** Find address object matching address @a addr.
+/** Find static route object matching address @a addr.
  *
  * @param addr	Address
@@ -102,8 +102,5 @@
 	fibril_mutex_lock(&sroute_list_lock);
 	
-	list_foreach(sroute_list, link) {
-		inet_sroute_t *sroute = list_get_instance(link,
-		    inet_sroute_t, sroute_list);
-		
+	list_foreach(sroute_list, sroute_list, inet_sroute_t, sroute) {
 		uint8_t dest_bits;
 		uint16_t dest_af = inet_naddr_get(&sroute->dest, NULL, NULL,
@@ -147,8 +144,5 @@
 	fibril_mutex_lock(&sroute_list_lock);
 
-	list_foreach(sroute_list, link) {
-		inet_sroute_t *sroute = list_get_instance(link,
-		    inet_sroute_t, sroute_list);
-
+	list_foreach(sroute_list, sroute_list, inet_sroute_t, sroute) {
 		if (str_cmp(sroute->name, name) == 0) {
 			fibril_mutex_unlock(&sroute_list_lock);
@@ -176,8 +170,5 @@
 	fibril_mutex_lock(&sroute_list_lock);
 
-	list_foreach(sroute_list, link) {
-		inet_sroute_t *sroute = list_get_instance(link,
-		    inet_sroute_t, sroute_list);
-
+	list_foreach(sroute_list, sroute_list, inet_sroute_t, sroute) {
 		if (sroute->id == id) {
 			fibril_mutex_unlock(&sroute_list_lock);
@@ -207,8 +198,5 @@
 
 	i = 0;
-	list_foreach(sroute_list, link) {
-		inet_sroute_t *sroute = list_get_instance(link,
-		    inet_sroute_t, sroute_list);
-
+	list_foreach(sroute_list, sroute_list, inet_sroute_t, sroute) {
 		id_list[i++] = sroute->id;
 	}
Index: uspace/srv/net/slip/slip.c
===================================================================
--- uspace/srv/net/slip/slip.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/slip/slip.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -333,5 +333,5 @@
 		    "Failed to connect to service %s (ID=%d)",
 		    svcstr, (int) svcid);
-		return rc;
+		return ENOENT;
 	}
 	slip_iplink.arg = sess_out;
@@ -342,4 +342,5 @@
 		    "Failed to connect to service %s (ID=%d)",
 		    svcstr, (int) svcid);
+		rc = ENOENT;
 		goto fail;
 	}
@@ -365,4 +366,5 @@
 		log_msg(LOG_DEFAULT, LVL_ERROR,
 		    "Failed to create receive fibril.");
+		rc = ENOENT;
 		goto fail;
 	}
Index: uspace/srv/net/tcp/conn.c
===================================================================
--- uspace/srv/net/tcp/conn.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/tcp/conn.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -360,6 +360,5 @@
 	fibril_mutex_lock(&conn_list_lock);
 	
-	list_foreach(conn_list, link) {
-		tcp_conn_t *conn = list_get_instance(link, tcp_conn_t, link);
+	list_foreach(conn_list, link, tcp_conn_t, conn) {
 		tcp_sockpair_t *csp = &conn->ident;
 		
Index: uspace/srv/net/tcp/tcp.c
===================================================================
--- uspace/srv/net/tcp/tcp.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/tcp/tcp.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -141,4 +141,5 @@
 	    pdu->text_size);
 
+	dgram.iplink = 0;
 	dgram.src = pdu->src;
 	dgram.dest = pdu->dest;
Index: uspace/srv/net/udp/assoc.c
===================================================================
--- uspace/srv/net/udp/assoc.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/udp/assoc.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -186,4 +186,18 @@
 }
 
+/** Set IP link in association.
+ *
+ * @param assoc		Association
+ * @param iplink	IP link
+ */
+void udp_assoc_set_iplink(udp_assoc_t *assoc, service_id_t iplink)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_iplink(%p, %zu)",
+	    assoc, iplink);
+	fibril_mutex_lock(&assoc->lock);
+	assoc->ident.iplink = iplink;
+	fibril_mutex_unlock(&assoc->lock);
+}
+
 /** Set foreign socket in association.
  *
@@ -419,6 +433,5 @@
 	fibril_mutex_lock(&assoc_list_lock);
 	
-	list_foreach(assoc_list, link) {
-		udp_assoc_t *assoc = list_get_instance(link, udp_assoc_t, link);
+	list_foreach(assoc_list, link, udp_assoc_t, assoc) {
 		udp_sockpair_t *asp = &assoc->ident;
 		
Index: uspace/srv/net/udp/assoc.h
===================================================================
--- uspace/srv/net/udp/assoc.h	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/udp/assoc.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -36,4 +36,5 @@
 #define ASSOC_H
 
+#include <ipc/loc.h>
 #include <sys/types.h>
 #include "udp_type.h"
@@ -45,4 +46,5 @@
 extern void udp_assoc_addref(udp_assoc_t *);
 extern void udp_assoc_delref(udp_assoc_t *);
+extern void udp_assoc_set_iplink(udp_assoc_t *, service_id_t);
 extern void udp_assoc_set_foreign(udp_assoc_t *, udp_sock_t *);
 extern void udp_assoc_set_local(udp_assoc_t *, udp_sock_t *);
Index: uspace/srv/net/udp/pdu.c
===================================================================
--- uspace/srv/net/udp/pdu.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/udp/pdu.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -215,4 +215,5 @@
 		return ENOMEM;
 
+	npdu->iplink = sp->iplink;
 	npdu->src = sp->local.addr;
 	npdu->dest = sp->foreign.addr;
Index: uspace/srv/net/udp/sock.c
===================================================================
--- uspace/srv/net/udp/sock.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/udp/sock.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2008 Lukas Mejdrech
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
  * All rights reserved.
  *
@@ -349,5 +349,6 @@
 	fibril_mutex_lock(&socket->lock);
 	
-	if (inet_addr_is_any(&socket->assoc->ident.local.addr)) {
+	if (inet_addr_is_any(&socket->assoc->ident.local.addr) &&
+		socket->assoc->ident.iplink == 0) {
 		/* Determine local IP address */
 		inet_addr_t loc_addr;
@@ -665,7 +666,49 @@
 static void udp_sock_setsockopt(udp_client_t *client, ipc_callid_t callid, ipc_call_t call)
 {
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sock_setsockopt()");
-	async_answer_0(callid, ENOTSUP);
-}
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sock_setsockopt)");
+	log_msg(LOG_DEFAULT, LVL_DEBUG, " - async_data_write_accept");
+	
+	void *data = NULL;
+	size_t data_len;
+	int rc = async_data_write_accept(&data, false, 0, 0, 0, &data_len);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, " - failed accepting data");
+		async_answer_0(callid, rc);
+		return;
+	}
+	
+	sysarg_t opt_level = SOL_SOCKET;
+	sysarg_t opt_name = SOCKET_GET_OPT_NAME(call);
+	
+	if (opt_level != SOL_SOCKET || opt_name != SO_IPLINK ||
+	    data_len != sizeof(service_id_t)) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, " - failed opt_level/name/len");
+		log_msg(LOG_DEFAULT, LVL_DEBUG, " - failed opt_level=%d, "
+		    "opt_name=%d, data_len=%zu", (int)opt_level, (int)opt_name,
+		    data_len);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, " - call socket_cores_find");
+	
+	socket_core_t *sock_core = socket_cores_find(&client->sockets,
+	    SOCKET_GET_SOCKET_ID(call));
+	if (sock_core == NULL) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, " - failed getting sock_core");
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	udp_sockdata_t *socket =
+	    (udp_sockdata_t *) sock_core->specific_data;
+	
+	service_id_t iplink = *(service_id_t *)data;
+	udp_uc_set_iplink(socket->assoc, iplink);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, " - success");
+	async_answer_0(callid, EOK);
+}
+
 
 static int udp_sock_recv_fibril(void *arg)
@@ -714,5 +757,4 @@
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sock_recv_fibril() exited loop");
-	fibril_mutex_unlock(&sock->recv_buffer_lock);
 	udp_uc_destroy(sock->assoc);
 
@@ -730,4 +772,6 @@
 	/* Accept the connection */
 	async_answer_0(iid, EOK);
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sock_connection: begin");
 
 	client.sess = async_callback_receive(EXCHANGE_SERIALIZE);
Index: uspace/srv/net/udp/ucall.c
===================================================================
--- uspace/srv/net/udp/ucall.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/udp/ucall.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -55,4 +55,12 @@
 	*assoc = nassoc;
 	return UDP_EOK;
+}
+
+void udp_uc_set_iplink(udp_assoc_t *assoc, service_id_t iplink)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_uc_set_iplink(%p, %zu)",
+	    assoc, iplink);
+
+	udp_assoc_set_iplink(assoc, iplink);
 }
 
Index: uspace/srv/net/udp/ucall.h
===================================================================
--- uspace/srv/net/udp/ucall.h	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/udp/ucall.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -36,8 +36,10 @@
 #define UCALL_H
 
+#include <ipc/loc.h>
 #include <sys/types.h>
 #include "udp_type.h"
 
 extern udp_error_t udp_uc_create(udp_assoc_t **);
+extern void udp_uc_set_iplink(udp_assoc_t *, service_id_t);
 extern udp_error_t udp_uc_set_foreign(udp_assoc_t *, udp_sock_t *);
 extern udp_error_t udp_uc_set_local(udp_assoc_t *, udp_sock_t *);
Index: uspace/srv/net/udp/udp_inet.c
===================================================================
--- uspace/srv/net/udp/udp_inet.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/udp/udp_inet.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -84,4 +84,5 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_transmit_pdu()");
 
+	dgram.iplink = pdu->iplink;
 	dgram.src = pdu->src;
 	dgram.dest = pdu->dest;
Index: uspace/srv/net/udp/udp_type.h
===================================================================
--- uspace/srv/net/udp/udp_type.h	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/net/udp/udp_type.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -38,4 +38,5 @@
 #include <fibril.h>
 #include <fibril_synch.h>
+#include <ipc/loc.h>
 #include <socket_core.h>
 #include <sys/types.h>
@@ -71,4 +72,5 @@
 
 typedef struct {
+	service_id_t iplink;
 	udp_sock_t local;
 	udp_sock_t foreign;
@@ -85,9 +87,10 @@
 /** Encoded PDU */
 typedef struct {
+	/** IP link (optional) */
+	service_id_t iplink;
 	/** Source address */
 	inet_addr_t src;
 	/** Destination address */
 	inet_addr_t dest;
-	
 	/** Encoded PDU data including header */
 	void *data;
@@ -143,4 +146,6 @@
 	/** Connection */
 	udp_assoc_t *assoc;
+	/** User-configured IP link */
+	service_id_t iplink;
 	/** Receiving fibril */
 	fid_t recv_fibril;
Index: uspace/srv/ns/service.c
===================================================================
--- uspace/srv/ns/service.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/ns/service.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -106,7 +106,5 @@
 {
 loop:
-	list_foreach(pending_conn, cur) {
-		pending_conn_t *pr = list_get_instance(cur, pending_conn_t, link);
-		
+	list_foreach(pending_conn, link, pending_conn_t, pr) {
 		ht_link_t *link = hash_table_find(&service_hash_table, &pr->service);
 		if (!link)
@@ -117,5 +115,5 @@
 		    pr->arg3, 0, IPC_FF_NONE);
 		
-		list_remove(cur);
+		list_remove(&pr->link);
 		free(pr);
 		goto loop;
Index: uspace/srv/ns/task.c
===================================================================
--- uspace/srv/ns/task.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/ns/task.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -179,7 +179,5 @@
 	
 loop:
-	list_foreach(pending_wait, cur) {
-		pending_wait_t *pr = list_get_instance(cur, pending_wait_t, link);
-		
+	list_foreach(pending_wait, link, pending_wait_t, pr) {
 		ht_link_t *link = hash_table_find(&task_hash_table, &pr->id);
 		if (!link)
@@ -198,5 +196,5 @@
 		
 		hash_table_remove(&task_hash_table, &pr->id);
-		list_remove(cur);
+		list_remove(&pr->link);
 		free(pr);
 		goto loop;
Index: uspace/srv/taskmon/taskmon.c
===================================================================
--- uspace/srv/taskmon/taskmon.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/taskmon/taskmon.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2010 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,4 +41,6 @@
 #include <task.h>
 #include <event.h>
+#include <ipc/corecfg.h>
+#include <loc.h>
 #include <macros.h>
 #include <errno.h>
@@ -47,8 +49,13 @@
 #define NAME  "taskmon"
 
+static bool write_core_files;
+
+static void corecfg_client_conn(ipc_callid_t , ipc_call_t *, void *);
+
 static void fault_event(ipc_callid_t callid, ipc_call_t *call)
 {
 	const char *fname;
 	char *s_taskid;
+	char *dump_fname;
 	int rc;
 
@@ -69,22 +76,59 @@
 	fname = "/app/taskdump";
 
-#ifdef CONFIG_WRITE_CORE_FILES
-	char *dump_fname;
+	if (write_core_files) {
+		if (asprintf(&dump_fname, "/data/core%" PRIu64, taskid) < 0) {
+			printf("Memory allocation failed.\n");
+			return;
+		}
 
-	if (asprintf(&dump_fname, "/data/core%" PRIu64, taskid) < 0) {
-		printf("Memory allocation failed.\n");
-		return;
+		printf(NAME ": Executing %s -c %s -t %s\n", fname, dump_fname, s_taskid);
+		rc = task_spawnl(NULL, fname, fname, "-c", dump_fname, "-t", s_taskid,
+		    NULL);
+	} else {
+		printf(NAME ": Executing %s -t %s\n", fname, s_taskid);
+		rc = task_spawnl(NULL, fname, fname, "-t", s_taskid, NULL);
 	}
 
-	printf(NAME ": Executing %s -c %s -t %s\n", fname, dump_fname, s_taskid);
-	rc = task_spawnl(NULL, fname, fname, "-c", dump_fname, "-t", s_taskid,
-	    NULL);
-#else
-	printf(NAME ": Executing %s -t %s\n", fname, s_taskid);
-	rc = task_spawnl(NULL, fname, fname, "-t", s_taskid, NULL);
-#endif
 	if (rc != EOK) {
 		printf("%s: Error spawning %s (%s).\n", NAME, fname,
 		    str_error(rc));
+	}
+}
+
+static void corecfg_get_enable_srv(ipc_callid_t iid, ipc_call_t *icall)
+{
+	async_answer_1(iid, EOK, write_core_files);
+}
+
+static void corecfg_set_enable_srv(ipc_callid_t iid, ipc_call_t *icall)
+{
+	write_core_files = IPC_GET_ARG1(*icall);
+	async_answer_0(iid, EOK);
+}
+
+static void corecfg_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	/* Accept the connection */
+	async_answer_0(iid, EOK);
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		sysarg_t method = IPC_GET_IMETHOD(call);
+
+		if (!method) {
+			/* The other side has hung up */
+			async_answer_0(callid, EOK);
+			return;
+		}
+
+		switch (method) {
+		case CORECFG_GET_ENABLE:
+			corecfg_get_enable_srv(callid, &call);
+			break;
+		case CORECFG_SET_ENABLE:
+			corecfg_set_enable_srv(callid, &call);
+			break;
+		}
 	}
 }
@@ -94,6 +138,28 @@
 	printf("%s: Task Monitoring Service\n", NAME);
 	
+#ifdef CONFIG_WRITE_CORE_FILES
+	write_core_files = true;
+#else
+	write_core_files = false;
+#endif
 	if (event_subscribe(EVENT_FAULT, 0) != EOK) {
 		printf("%s: Error registering fault notifications.\n", NAME);
+		return -1;
+	}
+	
+	async_set_client_connection(corecfg_client_conn);
+	
+	int rc = loc_server_register(NAME);
+	if (rc != EOK) {
+		printf("%s: Failed registering server (%d).\n",
+		    NAME, rc);
+		return -1;
+	}
+	
+	service_id_t sid;
+	rc = loc_service_register(SERVICE_NAME_CORECFG, &sid);
+	if (rc != EOK) {
+		printf("%s: Failed registering service (%d).\n",
+		    NAME, rc);
 		return -1;
 	}
Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/vfs/vfs.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/vfs/vfs.h	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -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 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/vfs/vfs_ops.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -548,8 +548,5 @@
 	int found = 0;
 
-	list_foreach(mtab_list, cur) {
-		mtab_ent_t *mtab_ent = list_get_instance(cur, mtab_ent_t,
-		    link);
-
+	list_foreach(mtab_list, link, mtab_ent_t, mtab_ent) {
 		if (str_cmp(mtab_ent->mp, mp) == 0) {
 			list_remove(&mtab_ent->link);
@@ -1371,8 +1368,5 @@
 	async_answer_1(callid, EOK, mtab_size);
 
-	list_foreach(mtab_list, cur) {
-		mtab_ent_t *mtab_ent = list_get_instance(cur, mtab_ent_t,
-		    link);
-
+	list_foreach(mtab_list, link, mtab_ent_t, mtab_ent) {
 		rc = ENOTSUP;
 
@@ -1418,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);
+}
+
 /**
  * @}
Index: uspace/srv/vfs/vfs_register.c
===================================================================
--- uspace/srv/vfs/vfs_register.c	(revision 7f84430205518d0134dd9a2c5d6d22a909150b5f)
+++ uspace/srv/vfs/vfs_register.c	(revision 67fbd5e822922b8167b911f3d04b3e871f887781)
@@ -260,7 +260,5 @@
 	fibril_mutex_lock(&fs_list_lock);
 	
-	list_foreach(fs_list, cur) {
-		fs_info_t *fs = list_get_instance(cur, fs_info_t, fs_link);
-		
+	list_foreach(fs_list, fs_link, fs_info_t, fs) {
 		if (fs->fs_handle == handle) {
 			fibril_mutex_unlock(&fs_list_lock);
@@ -305,6 +303,5 @@
 		fibril_mutex_lock(&fs_list_lock);
 	
-	list_foreach(fs_list, cur) {
-		fs_info_t *fs = list_get_instance(cur, fs_info_t, fs_link);
+	list_foreach(fs_list, fs_link, fs_info_t, fs) {
 		if (str_cmp(fs->vfs_info.name, name) == 0 &&
 		    instance == fs->vfs_info.instance) {
@@ -332,6 +329,5 @@
 	
 	fibril_mutex_lock(&fs_list_lock);
-	list_foreach(fs_list, cur) {
-		fs_info_t *fs = list_get_instance(cur, fs_info_t, fs_link);
+	list_foreach(fs_list, fs_link, fs_info_t, fs) {
 		if (fs->fs_handle == handle) { 
 			info = &fs->vfs_info;
