Index: HelenOS.config
===================================================================
--- HelenOS.config	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ HelenOS.config	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -70,4 +70,5 @@
 @ "pentium4" Pentium 4
 @ "pentium3" Pentium 3
+@ "i486" i486
 @ "core" Core Solo/Duo
 @ "athlon_xp" Athlon XP
Index: Makefile
===================================================================
--- Makefile	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ Makefile	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -54,5 +54,5 @@
 
 cscope:
-	find kernel boot uspace -regex '^.*\.[chsS]$$' | xargs $(CSCOPE) -b -k -u -f$(CSCOPE).out
+	find abi kernel boot uspace -regex '^.*\.[chsS]$$' | xargs $(CSCOPE) -b -k -u -f$(CSCOPE).out
 
 # Pre-integration build check
Index: abi/include/ipc/event.h
===================================================================
--- abi/include/ipc/event.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ abi/include/ipc/event.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -36,4 +36,5 @@
 #define ABI_IPC_EVENT_H_
 
+/** Global events */
 typedef enum event_type {
 	/** New data available in kernel log */
@@ -46,4 +47,10 @@
 } event_type_t;
 
+/** Per-task events. */
+typedef enum event_task_type {
+	EVENT_TASK_STATE_CHANGE = EVENT_END,
+	EVENT_TASK_END
+} event_task_type_t;
+
 #endif
 
Index: abi/include/ipc/methods.h
===================================================================
--- abi/include/ipc/methods.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ abi/include/ipc/methods.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -171,4 +171,16 @@
 #define IPC_M_DATA_READ  8
 
+/** Authorize change of recipient's state in a third party task.
+ * - ARG1 - user protocol defined data
+ * - ARG2 - user protocol defined data
+ * - ARG3 - user protocol defined data
+ * - ARG5 - sender's phone to the third party task
+ *
+ * on EOK answer, the recipient must set:
+ *
+ * - ARG1 - recipient's phone to the third party task
+ */
+#define IPC_M_STATE_CHANGE_AUTHORIZE	9
+
 /** Debug the recipient.
  * - ARG1 - specifies the debug method (from udebug_method_t)
@@ -176,5 +188,5 @@
  *
  */
-#define IPC_M_DEBUG  9
+#define IPC_M_DEBUG 10
 
 /** Last system IPC method */
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ boot/Makefile.common	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -147,4 +147,5 @@
 	$(USPACE_PATH)/app/ccom/mkext/cc_mkext \
 	$(USPACE_PATH)/app/cpp/cpp \
+	$(USPACE_PATH)/app/devctl/devctl \
 	$(USPACE_PATH)/app/dltest/dltest \
 	$(USPACE_PATH)/app/dltest2/dltest2 \
@@ -154,4 +155,5 @@
 	$(USPACE_PATH)/app/kill/kill \
 	$(USPACE_PATH)/app/killall/killall \
+	$(USPACE_PATH)/app/locinfo/locinfo \
 	$(USPACE_PATH)/app/mkfat/mkfat \
 	$(USPACE_PATH)/app/lsusb/lsusb \
Index: kernel/arch/ia32/Makefile.inc
===================================================================
--- kernel/arch/ia32/Makefile.inc	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/arch/ia32/Makefile.inc	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -64,4 +64,8 @@
 endif
 
+ifeq ($(PROCESSOR),i486)
+	CMN2 = -march=i486
+endif
+
 ifeq ($(PROCESSOR),core)
 	CMN2 = -march=prescott
Index: kernel/arch/ia32/include/asm.h
===================================================================
--- kernel/arch/ia32/include/asm.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/arch/ia32/include/asm.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -311,4 +311,5 @@
 }
 
+#ifndef PROCESSOR_i486
 /** Write to MSR */
 NO_TRACE static inline void write_msr(uint32_t msr, uint64_t value)
@@ -335,4 +336,5 @@
 	return ((uint64_t) dx << 32) | ax;
 }
+#endif
 
 
Index: kernel/arch/ia32/include/atomic.h
===================================================================
--- kernel/arch/ia32/include/atomic.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/arch/ia32/include/atomic.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -121,5 +121,7 @@
 	asm volatile (
 		"0:\n"
+#ifndef PROCESSOR_i486
 		"pause\n"        /* Pentium 4's HT love this instruction */
+#endif
 		"mov %[count], %[tmp]\n"
 		"testl %[tmp], %[tmp]\n"
Index: kernel/arch/ia32/include/cycle.h
===================================================================
--- kernel/arch/ia32/include/cycle.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/arch/ia32/include/cycle.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -40,4 +40,7 @@
 NO_TRACE static inline uint64_t get_cycle(void)
 {
+#ifdef PROCESSOR_i486
+	return 0;
+#else
 	uint64_t v;
 	
@@ -48,4 +51,5 @@
 	
 	return v;
+#endif
 }
 
Index: kernel/arch/ia32/src/asm.S
===================================================================
--- kernel/arch/ia32/src/asm.S	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/arch/ia32/src/asm.S	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -405,5 +405,11 @@
 	xorl %eax, %eax
 	cmpl $(GDT_SELECTOR(KTEXT_DES)), ISTATE_OFFSET_CS(%esp)
+#ifdef PROCESSOR_i486
+	jz 0f
+	movl %eax, %ebp
+0:
+#else
 	cmovnzl %eax, %ebp
+#endif
 
 	movl %ebp, ISTATE_OFFSET_EBP_FRAME(%esp)
Index: kernel/arch/ia32/src/boot/boot.S
===================================================================
--- kernel/arch/ia32/src/boot/boot.S	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/arch/ia32/src/boot/boot.S	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -97,20 +97,4 @@
 	pm_status $status_prot
 	
-	movl $(INTEL_CPUID_LEVEL), %eax
-	cpuid
-	cmp $0x0, %eax  /* any function > 0? */
-	jbe pse_unsupported
-	
-	movl $(INTEL_CPUID_STANDARD), %eax
-	cpuid
-	bt $(INTEL_PSE), %edx
-	jc pse_supported
-	
-	pse_unsupported:
-		
-		pm_error $err_pse
-	
-	pse_supported:
-	
 #include "vesa_prot.inc"
 	
@@ -140,4 +124,71 @@
 		jmp hlt0
 
+/** Calculate unmapped address of the end of the kernel. */
+calc_end_of_kernel:
+	movl $hardcoded_load_address, %edi
+	andl $0x7fffffff, %edi
+	movl (%edi), %esi
+	andl $0x7fffffff, %esi
+	
+	movl $hardcoded_ktext_size, %edi
+	andl $0x7fffffff, %edi
+	addl (%edi), %esi
+	andl $0x7fffffff, %esi
+	
+	movl $hardcoded_kdata_size, %edi
+	andl $0x7fffffff, %edi
+	addl (%edi), %esi
+	andl $0x7fffffff, %esi
+	movl %esi, end_of_kernel
+	ret
+
+/** Find free 2M (+4k for alignment) region where to store page tables */
+find_mem_for_pt:
+	/* Check if multiboot info is present */
+	cmpl $0x2BADB002, grub_eax
+	je check_multiboot_map
+	ret
+check_multiboot_map:
+	/* Copy address of the multiboot info to ebx */
+	movl grub_ebx, %ebx
+	/* Check if memory map flag is present */
+	movl (%ebx), %edx
+	andl $(1 << 6), %edx
+	jnz use_multiboot_map
+	ret
+use_multiboot_map:
+	/* Copy address of the memory map to edx */
+	movl 48(%ebx), %edx
+	movl %edx, %ecx
+	addl 44(%ebx), %ecx
+	/* Find a free region at least 2M in size */
+	check_memmap_loop:
+		/* Is this a free region? */
+		cmp $1, 20(%edx)
+		jnz next_region
+		/* Check size */
+		cmp $0, 16(%edx)
+		jnz next_region
+		cmpl $(2 * 1024 * 1024 + 4 * 1024), 12(%edx)
+		jbe next_region
+		cmp $0, 8(%edx)
+		jz found_region
+	next_region:
+		cmp %ecx, %edx
+		jbe next_region_do
+		ret
+	next_region_do:
+		addl (%edx), %edx
+		addl $4, %edx
+		jmp check_memmap_loop
+	found_region:
+		/* Use end of the found region */
+		mov 4(%edx), %ecx
+		add 12(%edx), %ecx
+		sub $(2 * 1024 * 1024), %ecx
+		mov %ecx, free_area
+	ret
+		
+
 /** Setup mapping for the kernel.
  *
@@ -148,16 +199,54 @@
 .global map_kernel
 map_kernel:
+	/* Paging features */
 	movl %cr4, %ecx
-	orl $(1 << 4), %ecx      /* PSE on */
 	andl $(~(1 << 5)), %ecx  /* PAE off */
 	movl %ecx, %cr4
 	
+	call calc_end_of_kernel
+	call find_mem_for_pt
+	mov end_of_kernel, %esi
+	mov free_area, %ecx
+	cmpl %esi, %ecx
+	jbe use_end_of_kernel
+	mov %ecx, %esi
+	/* Align address down to 4k */
+	andl $(~4095), %esi
+use_end_of_kernel:
+	
+	/* Align address to 4k */
+	addl $4095, %esi
+	andl $(~4095), %esi
+	
+	/* Allocate space for page tables*/	
+	movl %esi, pt_loc
+	movl $ballocs, %edi
+	andl $0x7fffffff, %edi
+	movl %esi, (%edi)
+	addl $4, %edi
+	movl $(2*1024*1024), (%edi)
+	
+	/* Fill page tables */
+	xorl %ecx, %ecx
+	xorl %ebx, %ebx
+	
+	floop_pt:
+		movl $((1 << 1) | (1 << 0)), %eax
+		orl %ebx, %eax
+		movl %eax, (%esi, %ecx, 4)
+		addl $(4 * 1024), %ebx
+		
+		incl %ecx
+		cmpl $(512 * 1024), %ecx
+		jl floop_pt
+	
+	/* Fill page directory */
 	movl $(page_directory + 0), %esi
 	movl $(page_directory + 2048), %edi
 	xorl %ecx, %ecx
-	xorl %ebx, %ebx
+	movl pt_loc, %ebx
 	
 	floop:
-		movl $((1 << 7) | (1 << 1) | (1 << 0)), %eax
+		movl $((1 << 1) | (1 << 0)), %eax
 		orl %ebx, %eax
 		/* Mapping 0x00000000 + %ecx * 4M => 0x00000000 + %ecx * 4M */
@@ -165,5 +254,5 @@
 		/* Mapping 0x80000000 + %ecx * 4M => 0x00000000 + %ecx * 4M */
 		movl %eax, (%edi, %ecx, 4)
-		addl $(4 * 1024 * 1024), %ebx
+		addl $(4 * 1024), %ebx
 		
 		incl %ecx
@@ -523,4 +612,11 @@
 
 grub_ebx:
+	.long 0
+
+pt_loc:
+	.long 0
+end_of_kernel:
+	.long 0
+free_area:
 	.long 0
 
Index: kernel/arch/ia32/src/cpu/cpu.c
===================================================================
--- kernel/arch/ia32/src/cpu/cpu.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/arch/ia32/src/cpu/cpu.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -118,9 +118,11 @@
 		);
 	}
-	
+
+#ifndef PROCESSOR_i486
 	if (CPU->arch.fi.bits.sep) {
 		/* Setup fast SYSENTER/SYSEXIT syscalls */
 		syscall_setup_cpu();
 	}
+#endif
 }
 
Index: kernel/arch/ia32/src/proc/scheduler.c
===================================================================
--- kernel/arch/ia32/src/proc/scheduler.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/arch/ia32/src/proc/scheduler.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -60,8 +60,10 @@
 	uintptr_t kstk = (uintptr_t) &THREAD->kstack[STACK_SIZE];
 	
+#ifndef PROCESSOR_i486
 	if (CPU->arch.fi.bits.sep) {
 		/* Set kernel stack for CP3 -> CPL0 switch via SYSENTER */
 		write_msr(IA32_MSR_SYSENTER_ESP, kstk - sizeof(istate_t));
 	}
+#endif
 	
 	/* Set kernel stack for CPL3 -> CPL0 switch via interrupt */
Index: kernel/arch/ia32/src/syscall.c
===================================================================
--- kernel/arch/ia32/src/syscall.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/arch/ia32/src/syscall.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -39,4 +39,5 @@
 #include <arch/pm.h>
 
+#ifndef PROCESSOR_i486
 /** Enable & setup support for SYSENTER/SYSEXIT */
 void syscall_setup_cpu(void)
@@ -49,4 +50,5 @@
 	write_msr(IA32_MSR_SYSENTER_EIP, (uint32_t) sysenter_handler);
 }
+#endif
 
 /** @}
Index: kernel/generic/include/console/console.h
===================================================================
--- kernel/generic/include/console/console.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/generic/include/console/console.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -63,5 +63,5 @@
 
 extern void klog_init(void);
-extern void klog_update(void);
+extern void klog_update(void *);
 
 extern wchar_t getc(indev_t *indev);
Index: kernel/generic/include/ipc/event.h
===================================================================
--- kernel/generic/include/ipc/event.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/generic/include/ipc/event.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -41,5 +41,7 @@
 #include <ipc/ipc.h>
 
-typedef void (*event_callback_t)(void);
+struct task;
+
+typedef void (*event_callback_t)(void *);
 
 /** Event notification structure. */
@@ -61,6 +63,9 @@
 
 extern void event_init(void);
+extern void event_task_init(struct task *);
 extern void event_cleanup_answerbox(answerbox_t *);
 extern void event_set_unmask_callback(event_type_t, event_callback_t);
+extern void event_task_set_unmask_callback(struct task *, event_task_type_t,
+    event_callback_t);
 
 #define event_notify_0(e, m) \
@@ -77,6 +82,21 @@
 	event_notify((e), (m), (a1), (a2), (a3), (a4), (a5))
 
+#define event_task_notify_0(t, e, m) \
+	event_task_notify((t), (e), (m), 0, 0, 0, 0, 0)
+#define event_task_notify_1(t, e, m, a1) \
+	event_task_notify((t), (e), (m), (a1), 0, 0, 0, 0)
+#define event_task_notify_2(t, e, m, a1, a2) \
+	event_task_notify((t), (e), (m), (a1), (a2), 0, 0, 0)
+#define event_task_notify_3(t, e, m, a1, a2, a3) \
+	event_task_notify((t), (e), (m), (a1), (a2), (a3), 0, 0)
+#define event_task_notify_4(t, e, m, a1, a2, a3, a4) \
+	event_task_notify((t), (e), (m), (a1), (a2), (a3), (a4), 0)
+#define event_task_notify_5(t, e, m, a1, a2, a3, a4, a5) \
+	event_task_notify((t), (e), (m), (a1), (a2), (a3), (a4), (a5))
+
 extern int event_notify(event_type_t, bool, sysarg_t, sysarg_t, sysarg_t,
     sysarg_t, sysarg_t);
+extern int event_task_notify(struct task *, event_task_type_t, bool, sysarg_t, sysarg_t,
+    sysarg_t, sysarg_t, sysarg_t);
 
 extern sysarg_t sys_event_subscribe(sysarg_t, sysarg_t);
Index: kernel/generic/include/ipc/ipc.h
===================================================================
--- kernel/generic/include/ipc/ipc.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/generic/include/ipc/ipc.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -40,4 +40,5 @@
 #include <synch/mutex.h>
 #include <synch/waitq.h>
+#include <typedefs.h>
 
 #define IPC_MAX_PHONES  32
@@ -98,5 +99,5 @@
 	sysarg_t args[IPC_CALL_LEN];
 	/** Task which made or forwarded the call with IPC_FF_ROUTE_FROM_ME. */
-	struct task *task;
+	task_id_t task_id;
 	/** Phone which made or last masqueraded this call. */
 	phone_t *phone;
Index: kernel/generic/include/proc/task.h
===================================================================
--- kernel/generic/include/proc/task.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/generic/include/proc/task.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -38,4 +38,6 @@
 #include <cpu.h>
 #include <ipc/ipc.h>
+#include <ipc/event.h>
+#include <ipc/kbox.h>
 #include <synch/spinlock.h>
 #include <synch/mutex.h>
@@ -53,5 +55,4 @@
 #include <proc/scheduler.h>
 #include <udebug/udebug.h>
-#include <ipc/kbox.h>
 #include <mm/as.h>
 #include <abi/sysinfo.h>
@@ -93,6 +94,6 @@
 	phone_t phones[IPC_MAX_PHONES];
 	stats_ipc_t ipc_info;   /**< IPC statistics */
-	/** List of synchronous answerboxes. */
-	list_t sync_boxes;
+	list_t sync_boxes;      /**< List of synchronous answerboxes. */
+	event_t events[EVENT_TASK_END - EVENT_END];
 	
 #ifdef CONFIG_UDEBUG
Index: kernel/generic/src/console/console.c
===================================================================
--- kernel/generic/src/console/console.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/generic/src/console/console.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -248,5 +248,5 @@
 }
 
-void klog_update(void)
+void klog_update(void *event)
 {
 	if (!atomic_get(&klog_inited))
@@ -327,5 +327,5 @@
 	/* Force notification on newline */
 	if (ch == '\n')
-		klog_update();
+		klog_update(NULL);
 }
 
@@ -358,5 +358,5 @@
 		free(data);
 	} else
-		klog_update();
+		klog_update(NULL);
 	
 	return size;
Index: kernel/generic/src/ipc/event.c
===================================================================
--- kernel/generic/src/ipc/event.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/generic/src/ipc/event.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -40,4 +40,5 @@
 #include <synch/spinlock.h>
 #include <console/console.h>
+#include <proc/task.h>
 #include <memstr.h>
 #include <errno.h>
@@ -47,4 +48,28 @@
 static event_t events[EVENT_END];
 
+static void event_initialize(event_t *event)
+{
+	spinlock_initialize(&event->lock, "event.lock");
+	event->answerbox = NULL;
+	event->counter = 0;
+	event->imethod = 0;
+	event->masked = false;
+	event->unmask_callback = NULL;
+}
+
+static event_t *evno2event(int evno, task_t *t)
+{
+	ASSERT(evno < EVENT_TASK_END);
+
+	event_t *event;
+
+	if (evno < EVENT_END)
+		event = &events[(event_type_t) evno];
+	else
+		event = &t->events[(event_task_type_t) evno - EVENT_END];
+
+	return event;
+}
+
 /** Initialize kernel events.
  *
@@ -52,13 +77,14 @@
 void event_init(void)
 {
-	for (unsigned int i = 0; i < EVENT_END; i++) {
-		spinlock_initialize(&events[i].lock, "event.lock");
-		events[i].answerbox = NULL;
-		events[i].counter = 0;
-		events[i].imethod = 0;
-		events[i].masked = false;
-		events[i].unmask_callback = NULL;
-	}
-}
+	for (unsigned int i = 0; i < EVENT_END; i++)
+		event_initialize(evno2event(i, NULL));
+}
+
+void event_task_init(task_t *task)
+{
+	for (unsigned int i = EVENT_END; i < EVENT_TASK_END; i++)
+		event_initialize(evno2event(i, task));
+}
+
 
 /** Unsubscribe kernel events associated with an answerbox
@@ -83,4 +109,11 @@
 }
 
+static void _event_set_unmask_callback(event_t *event, event_callback_t callback)
+{
+	spinlock_lock(&event->lock);
+	event->unmask_callback = callback;
+	spinlock_unlock(&event->lock);
+}
+
 /** Define a callback function for the event unmask event.
  *
@@ -94,7 +127,59 @@
 	ASSERT(evno < EVENT_END);
 	
-	spinlock_lock(&events[evno].lock);
-	events[evno].unmask_callback = callback;
-	spinlock_unlock(&events[evno].lock);
+	_event_set_unmask_callback(evno2event(evno, NULL), callback);
+}
+
+void event_task_set_unmask_callback(task_t *task, event_task_type_t evno,
+    event_callback_t callback)
+{
+	ASSERT(evno >= (int) EVENT_END);
+	ASSERT(evno < EVENT_TASK_END);
+		
+	_event_set_unmask_callback(evno2event(evno, task), callback);
+}
+
+static int event_enqueue(event_t *event, bool mask, sysarg_t a1, sysarg_t a2,
+    sysarg_t a3, sysarg_t a4, sysarg_t a5)
+{
+	int res;
+
+	spinlock_lock(&event->lock);
+	
+	if (event->answerbox != NULL) {
+		if (!event->masked) {
+			call_t *call = ipc_call_alloc(FRAME_ATOMIC);
+			
+			if (call) {
+				call->flags |= IPC_CALL_NOTIF;
+				call->priv = ++event->counter;
+				
+				IPC_SET_IMETHOD(call->data, event->imethod);
+				IPC_SET_ARG1(call->data, a1);
+				IPC_SET_ARG2(call->data, a2);
+				IPC_SET_ARG3(call->data, a3);
+				IPC_SET_ARG4(call->data, a4);
+				IPC_SET_ARG5(call->data, a5);
+				
+				call->data.task_id = TASK ? TASK->taskid : 0;
+				
+				irq_spinlock_lock(&event->answerbox->irq_lock, true);
+				list_append(&call->link, &event->answerbox->irq_notifs);
+				irq_spinlock_unlock(&event->answerbox->irq_lock, true);
+				
+				waitq_wakeup(&event->answerbox->wq, WAKEUP_FIRST);
+				
+				if (mask)
+					event->masked = true;
+				
+				res = EOK;
+			} else
+				res = ENOMEM;
+		} else
+			res = EBUSY;
+	} else
+		res = ENOENT;
+	
+	spinlock_unlock(&event->lock);
+	return res;
 }
 
@@ -123,43 +208,34 @@
 	ASSERT(evno < EVENT_END);
 	
-	spinlock_lock(&events[evno].lock);
-	
-	int ret;
-	
-	if (events[evno].answerbox != NULL) {
-		if (!events[evno].masked) {
-			call_t *call = ipc_call_alloc(FRAME_ATOMIC);
-			
-			if (call) {
-				call->flags |= IPC_CALL_NOTIF;
-				call->priv = ++events[evno].counter;
-				
-				IPC_SET_IMETHOD(call->data, events[evno].imethod);
-				IPC_SET_ARG1(call->data, a1);
-				IPC_SET_ARG2(call->data, a2);
-				IPC_SET_ARG3(call->data, a3);
-				IPC_SET_ARG4(call->data, a4);
-				IPC_SET_ARG5(call->data, a5);
-				
-				irq_spinlock_lock(&events[evno].answerbox->irq_lock, true);
-				list_append(&call->link, &events[evno].answerbox->irq_notifs);
-				irq_spinlock_unlock(&events[evno].answerbox->irq_lock, true);
-				
-				waitq_wakeup(&events[evno].answerbox->wq, WAKEUP_FIRST);
-				
-				if (mask)
-					events[evno].masked = true;
-				
-				ret = EOK;
-			} else
-				ret = ENOMEM;
-		} else
-			ret = EBUSY;
-	} else
-		ret = ENOENT;
-	
-	spinlock_unlock(&events[evno].lock);
-	
-	return ret;
+	return event_enqueue(evno2event(evno, NULL), mask, a1, a2, a3, a4, a5);
+}
+
+/** Send per-task kernel notification event
+ *
+ * @param task Destination task.
+ * @param evno Event type.
+ * @param mask Mask further notifications after a successful
+ *             sending.
+ * @param a1   First argument.
+ * @param a2   Second argument.
+ * @param a3   Third argument.
+ * @param a4   Fourth argument.
+ * @param a5   Fifth argument.
+ *
+ * @return EOK if notification was successfully sent.
+ * @return ENOMEM if the notification IPC message failed to allocate.
+ * @return EBUSY if the notifications of the given type are
+ *         currently masked.
+ * @return ENOENT if the notifications of the given type are
+ *         currently not subscribed.
+ *
+ */
+int event_task_notify(task_t *task, event_task_type_t evno, bool mask,
+    sysarg_t a1, sysarg_t a2, sysarg_t a3, sysarg_t a4, sysarg_t a5)
+{
+	ASSERT(evno >= (int) EVENT_END);
+	ASSERT(evno < EVENT_TASK_END);
+	
+	return event_enqueue(evno2event(evno, task), mask, a1, a2, a3, a4, a5);
 }
 
@@ -176,23 +252,21 @@
  *
  */
-static int event_subscribe(event_type_t evno, sysarg_t imethod,
+static int event_subscribe(event_t *event, sysarg_t imethod,
     answerbox_t *answerbox)
 {
-	ASSERT(evno < EVENT_END);
-	
-	spinlock_lock(&events[evno].lock);
-	
 	int res;
-	
-	if (events[evno].answerbox == NULL) {
-		events[evno].answerbox = answerbox;
-		events[evno].imethod = imethod;
-		events[evno].counter = 0;
-		events[evno].masked = false;
+
+	spinlock_lock(&event->lock);
+	
+	if (event->answerbox == NULL) {
+		event->answerbox = answerbox;
+		event->imethod = imethod;
+		event->counter = 0;
+		event->masked = false;
 		res = EOK;
 	} else
 		res = EEXISTS;
 	
-	spinlock_unlock(&events[evno].lock);
+	spinlock_unlock(&event->lock);
 	
 	return res;
@@ -204,12 +278,10 @@
  *
  */
-static void event_unmask(event_type_t evno)
-{
-	ASSERT(evno < EVENT_END);
-	
-	spinlock_lock(&events[evno].lock);
-	events[evno].masked = false;
-	event_callback_t callback = events[evno].unmask_callback;
-	spinlock_unlock(&events[evno].lock);
+static void event_unmask(event_t *event)
+{
+	spinlock_lock(&event->lock);
+	event->masked = false;
+	event_callback_t callback = event->unmask_callback;
+	spinlock_unlock(&event->lock);
 	
 	/*
@@ -218,5 +290,5 @@
 	 */
 	if (callback != NULL)
-		callback();
+		callback(event);
 }
 
@@ -235,9 +307,9 @@
 sysarg_t sys_event_subscribe(sysarg_t evno, sysarg_t imethod)
 {
-	if (evno >= EVENT_END)
+	if (evno >= EVENT_TASK_END)
 		return ELIMIT;
 	
-	return (sysarg_t) event_subscribe((event_type_t) evno, (sysarg_t)
-	    imethod, &TASK->answerbox);
+	return (sysarg_t) event_subscribe(evno2event(evno, TASK),
+	    (sysarg_t) imethod, &TASK->answerbox);
 }
 
@@ -257,8 +329,9 @@
 sysarg_t sys_event_unmask(sysarg_t evno)
 {
-	if (evno >= EVENT_END)
+	if (evno >= EVENT_TASK_END)
 		return ELIMIT;
 	
-	event_unmask((event_type_t) evno);
+	event_unmask(evno2event(evno, TASK));
+
 	return EOK;
 }
Index: kernel/generic/src/ipc/ipc.c
===================================================================
--- kernel/generic/src/ipc/ipc.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/generic/src/ipc/ipc.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -294,5 +294,5 @@
 		atomic_inc(&phone->active_calls);
 		call->data.phone = phone;
-		call->data.task = TASK;
+		call->data.task_id = TASK->taskid;
 	}
 	
@@ -406,5 +406,5 @@
 			call->caller_phone = call->data.phone;
 		call->data.phone = newphone;
-		call->data.task = TASK;
+		call->data.task_id = TASK->taskid;
 	}
 	
Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/generic/src/ipc/sysipc.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -44,4 +44,5 @@
 #include <ipc/irq.h>
 #include <ipc/ipcrsc.h>
+#include <ipc/event.h>
 #include <ipc/kbox.h>
 #include <synch/waitq.h>
@@ -53,4 +54,5 @@
 #include <mm/as.h>
 #include <print.h>
+#include <macros.h>
 
 /**
@@ -134,4 +136,5 @@
 	case IPC_M_DATA_WRITE:
 	case IPC_M_DATA_READ:
+	case IPC_M_STATE_CHANGE_AUTHORIZE:
 		return true;
 	default:
@@ -164,4 +167,5 @@
 	case IPC_M_DATA_WRITE:
 	case IPC_M_DATA_READ:
+	case IPC_M_STATE_CHANGE_AUTHORIZE:
 		return true;
 	default:
@@ -249,6 +253,7 @@
 			/* The connection was accepted */
 			phone_connect(phoneid, &answer->sender->answerbox);
-			/* Set 'task hash' as arg4 of response */
-			IPC_SET_ARG4(answer->data, (sysarg_t) TASK);
+			/* Set 'task ID' as arg3 and arg4 of response */
+			IPC_SET_ARG3(answer->data, LOWER32(TASK->taskid));
+			IPC_SET_ARG4(answer->data, UPPER32(TASK->taskid));
 			/* Set 'phone hash' as arg5 of response */
 			IPC_SET_ARG5(answer->data,
@@ -334,4 +339,50 @@
 		free(answer->buffer);
 		answer->buffer = NULL;
+	} else if (IPC_GET_IMETHOD(*olddata) == IPC_M_STATE_CHANGE_AUTHORIZE) {
+		if (!IPC_GET_RETVAL(answer->data)) {
+			/* The recipient authorized the change of state. */
+			phone_t *recipient_phone;
+			task_t *other_task_s;
+			task_t *other_task_r;
+			int rc;
+
+			rc = phone_get(IPC_GET_ARG1(answer->data),
+			    &recipient_phone);
+			if (rc != EOK) {
+				IPC_SET_RETVAL(answer->data, ENOENT);
+				return ENOENT;
+			}
+
+			mutex_lock(&recipient_phone->lock);
+			if (recipient_phone->state != IPC_PHONE_CONNECTED) {
+				mutex_unlock(&recipient_phone->lock);
+				IPC_SET_RETVAL(answer->data, EINVAL);
+				return EINVAL;
+			}
+
+			other_task_r = recipient_phone->callee->task;
+			other_task_s = (task_t *) IPC_GET_ARG5(*olddata);
+
+			/*
+			 * See if both the sender and the recipient meant the
+			 * same third party task.
+			 */
+			if (other_task_r != other_task_s) {
+				IPC_SET_RETVAL(answer->data, EINVAL);
+				rc = EINVAL;
+			} else {
+				rc = event_task_notify_5(other_task_r,
+				    EVENT_TASK_STATE_CHANGE, false,
+				    IPC_GET_ARG1(*olddata),
+				    IPC_GET_ARG2(*olddata),
+				    IPC_GET_ARG3(*olddata),
+				    LOWER32(olddata->task_id),
+				    UPPER32(olddata->task_id));
+				IPC_SET_RETVAL(answer->data, rc);
+			}
+
+			mutex_unlock(&recipient_phone->lock);
+			return rc;
+		}
 	}
 	
@@ -456,4 +507,25 @@
 		}
 		
+		break;
+	}
+	case IPC_M_STATE_CHANGE_AUTHORIZE: {
+		phone_t *sender_phone;
+		task_t *other_task_s;
+
+		if (phone_get(IPC_GET_ARG5(call->data), &sender_phone) != EOK)
+			return ENOENT;
+
+		mutex_lock(&sender_phone->lock);
+		if (sender_phone->state != IPC_PHONE_CONNECTED) {
+			mutex_unlock(&sender_phone->lock);
+			return EINVAL;
+		}
+
+		other_task_s = sender_phone->callee->task;
+
+		mutex_unlock(&sender_phone->lock);
+
+		/* Remember the third party task hash. */
+		IPC_SET_ARG5(call->data, (sysarg_t) other_task_s);
 		break;
 	}
Index: kernel/generic/src/proc/task.c
===================================================================
--- kernel/generic/src/proc/task.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ kernel/generic/src/proc/task.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -50,4 +50,5 @@
 #include <ipc/ipc.h>
 #include <ipc/ipcrsc.h>
+#include <ipc/event.h>
 #include <print.h>
 #include <errno.h>
@@ -57,5 +58,4 @@
 #include <syscall/copy.h>
 #include <macros.h>
-#include <ipc/event.h>
 
 /** Spinlock protecting the tasks_tree AVL tree. */
@@ -201,4 +201,6 @@
 	task->ipc_info.irq_notif_received = 0;
 	task->ipc_info.forwarded = 0;
+
+	event_task_init(task);
 	
 #ifdef CONFIG_UDEBUG
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/Makefile	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -41,4 +41,5 @@
 	app/ccom/mkext \
 	app/cpp \
+	app/devctl \
 	app/edit \
 	app/ext2info \
@@ -48,4 +49,5 @@
 	app/killall \
 	app/klog \
+	app/locinfo \
 	app/lsusb \
 	app/mkfat \
Index: uspace/app/bdsh/exec.c
===================================================================
--- uspace/app/bdsh/exec.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/app/bdsh/exec.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -40,4 +40,5 @@
 #include <str_error.h>
 #include <errno.h>
+#include <vfs/vfs.h>
 
 #include "config.h"
@@ -99,6 +100,6 @@
 	char *tmp;
 	int rc, retval, i;
-	fdi_node_t file_nodes[3];
-	fdi_node_t *file_nodes_p[4];
+	int file_handles[3];
+	int *file_handles_p[4];
 	FILE *files[3];
 
@@ -111,14 +112,14 @@
 	
 	for (i = 0; i < 3 && files[i] != NULL; i++) {
-		if (fnode(files[i], &file_nodes[i]) == EOK) {
-			file_nodes_p[i] = &file_nodes[i];
+		if (fhandle(files[i], &file_handles[i]) == EOK) {
+			file_handles_p[i] = &file_handles[i];
 		}
 		else {
-			file_nodes_p[i] = NULL;
+			file_handles_p[i] = NULL;
 		}
 	}
-	file_nodes_p[i] = NULL;
+	file_handles_p[i] = NULL;
 
-	rc = task_spawnvf(&tid, tmp, (const char **) argv, file_nodes_p);
+	rc = task_spawnvf(&tid, tmp, (const char **) argv, file_handles_p);
 	free(tmp);
 
Index: uspace/app/devctl/Makefile
===================================================================
--- uspace/app/devctl/Makefile	(revision 07b3933873be077318b66f2e001bce5785804448)
+++ uspace/app/devctl/Makefile	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -0,0 +1,35 @@
+#
+# 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.
+#
+
+USPACE_PREFIX = ../..
+BINARY = devctl
+
+SOURCES = \
+	devctl.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/devctl/devctl.c
===================================================================
--- uspace/app/devctl/devctl.c	(revision 07b3933873be077318b66f2e001bce5785804448)
+++ uspace/app/devctl/devctl.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -0,0 +1,111 @@
+/*
+ * 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 devctl
+ * @{
+ */
+/** @file Control device framework (devman server).
+ */
+
+#include <devman.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/typefmt.h>
+
+#define NAME "devctl"
+
+#define MAX_NAME_LENGTH 1024
+
+static int fun_tree_print(devman_handle_t funh, int lvl)
+{
+	char name[MAX_NAME_LENGTH];
+	devman_handle_t devh;
+	devman_handle_t *cfuns;
+	size_t count, i;
+	int rc;
+	int j;
+
+	for (j = 0; j < lvl; j++)
+		printf("    ");
+
+	rc = devman_fun_get_name(funh, name, MAX_NAME_LENGTH);
+	if (rc != EOK) {
+		str_cpy(name, MAX_NAME_LENGTH, "unknown");
+		return ENOMEM;
+	}
+
+	if (name[0] == '\0')
+		str_cpy(name, MAX_NAME_LENGTH, "/");
+
+	printf("%s (%" PRIun ")\n", name, funh);
+
+	rc = devman_fun_get_child(funh, &devh);
+	if (rc == ENOENT)
+		return EOK;
+
+	if (rc != EOK) {
+		printf(NAME ": Failed getting child device for function "
+		    "%s.\n", "xxx");
+		return rc;
+	}
+
+	rc = devman_dev_get_functions(devh, &cfuns, &count);
+	if (rc != EOK) {
+		printf(NAME ": Failed getting list of functions for "
+		    "device %s.\n", "xxx");
+		return rc;
+	}
+
+	for (i = 0; i < count; i++)
+		fun_tree_print(cfuns[i], lvl + 1);
+
+	free(cfuns);
+	return EOK;
+}
+
+int main(int argc, char *argv[])
+{
+	devman_handle_t root_fun;
+	int rc;
+
+	rc = devman_fun_get_handle("/", &root_fun, 0);
+	if (rc != EOK) {
+		printf(NAME ": Error resolving root function.\n");
+		return 1;
+	}
+
+	rc = fun_tree_print(root_fun, 0);
+	if (rc != EOK)
+		return 1;
+
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/app/locinfo/Makefile
===================================================================
--- uspace/app/locinfo/Makefile	(revision 07b3933873be077318b66f2e001bce5785804448)
+++ uspace/app/locinfo/Makefile	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+USPACE_PREFIX = ../..
+EXTRA_CFLAGS = -Iinclude
+BINARY = locinfo
+
+SOURCES = \
+	locinfo.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/locinfo/locinfo.c
===================================================================
--- uspace/app/locinfo/locinfo.c	(revision 07b3933873be077318b66f2e001bce5785804448)
+++ uspace/app/locinfo/locinfo.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -0,0 +1,102 @@
+/*
+ * 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 locinfo
+ * @{
+ */
+/** @file locinfo.c Print information from location service.
+ */
+
+#include <errno.h>
+#include <loc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <sys/types.h>
+#include <sys/typefmt.h>
+
+#define NAME "locinfo"
+
+int main(int argc, char *argv[])
+{
+	category_id_t *cat_ids;
+	size_t cat_cnt;
+	service_id_t *svc_ids;
+	size_t svc_cnt;
+
+	size_t i, j;
+	char *cat_name;
+	char *svc_name;
+	int rc;
+
+	rc = loc_get_categories(&cat_ids, &cat_cnt);
+	if (rc != EOK) {
+		printf(NAME ": Error getting list of categories.\n");
+		return 1;
+	}
+
+	for (i = 0; i < cat_cnt; i++) {
+		rc = loc_category_get_name(cat_ids[i], &cat_name);
+		if (rc != EOK)
+			cat_name = str_dup("<unknown>");
+
+		if (cat_name == NULL) {
+			printf(NAME ": Error allocating memory.\n");
+			return 1;
+		}
+
+		printf("%s (%" PRIun "):\n", cat_name, cat_ids[i]);
+
+		rc = loc_category_get_svcs(cat_ids[i], &svc_ids, &svc_cnt);
+		if (rc != EOK) {
+			printf(NAME ": Failed getting list of services in "
+			    "category %s, skipping.\n", cat_name);
+			free(cat_name);
+			continue;
+		}
+
+		for (j = 0; j < svc_cnt; j++) {
+			rc = loc_service_get_name(svc_ids[j], &svc_name);
+			if (rc != EOK) {
+				printf(NAME ": Unknown service name (SID %"
+				    PRIun ").\n", svc_ids[j]);
+				continue;
+			}
+			printf("\t%s (%" PRIun ")\n", svc_name, svc_ids[j]);
+		}
+
+		free(svc_ids);
+		free(cat_name);
+	}
+
+	free(cat_ids);
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/app/lsusb/main.c
===================================================================
--- uspace/app/lsusb/main.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/app/lsusb/main.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -50,15 +50,12 @@
 
 #define MAX_USB_ADDRESS USB11_ADDRESS_MAX
-#define MAX_FAILED_ATTEMPTS 10
 #define MAX_PATH_LENGTH 1024
 
-static void print_found_hc(size_t class_index, const char *path)
+static void print_found_hc(service_id_t sid, const char *path)
 {
-	// printf(NAME ": host controller %zu is `%s'.\n", class_index, path);
-	printf("Bus %02zu: %s\n", class_index, path);
+	printf("Bus %" PRIun ": %s\n", sid, path);
 }
 static void print_found_dev(usb_address_t addr, const char *path)
 {
-	// printf(NAME ":     device with address %d is `%s'.\n", addr, path);
 	printf("  Device %02d: %s\n", addr, path);
 }
@@ -84,5 +81,5 @@
 		}
 		char path[MAX_PATH_LENGTH];
-		rc = devman_get_device_path(dev_handle, path, MAX_PATH_LENGTH);
+		rc = devman_fun_get_path(dev_handle, path, MAX_PATH_LENGTH);
 		if (rc != EOK) {
 			continue;
@@ -95,23 +92,43 @@
 int main(int argc, char *argv[])
 {
-	size_t class_index = 0;
-	size_t failed_attempts = 0;
+	category_id_t usbhc_cat;
+	service_id_t *svcs;
+	size_t count;
+	size_t i;
+	int rc;
 
-	while (failed_attempts < MAX_FAILED_ATTEMPTS) {
-		class_index++;
+	rc = loc_category_get_id(USB_HC_CATEGORY, &usbhc_cat, 0);
+	if (rc != EOK) {
+		printf(NAME ": Error resolving category '%s'",
+		    USB_HC_CATEGORY);
+		return 1;
+	}
+
+	rc = loc_category_get_svcs(usbhc_cat, &svcs, &count);
+	if (rc != EOK) {
+		printf(NAME ": Error getting list of host controllers.\n");
+		return 1;
+	}
+
+	for (i = 0; i < count; i++) {
 		devman_handle_t hc_handle = 0;
-		int rc = usb_ddf_get_hc_handle_by_class(class_index, &hc_handle);
+		int rc = usb_ddf_get_hc_handle_by_sid(svcs[i], &hc_handle);
 		if (rc != EOK) {
-			failed_attempts++;
+			printf(NAME ": Error resolving handle of HC with SID %"
+			    PRIun ", skipping.\n", svcs[i]);
 			continue;
 		}
 		char path[MAX_PATH_LENGTH];
-		rc = devman_get_device_path(hc_handle, path, MAX_PATH_LENGTH);
+		rc = devman_fun_get_path(hc_handle, path, MAX_PATH_LENGTH);
 		if (rc != EOK) {
+			printf(NAME ": Error resolving path of HC with SID %"
+			    PRIun ", skipping.\n", svcs[i]);
 			continue;
 		}
-		print_found_hc(class_index, path);
+		print_found_hc(svcs[i], path);
 		print_hc_devices(hc_handle);
 	}
+
+	free(svcs);
 
 	return 0;
Index: uspace/app/mkbd/main.c
===================================================================
--- uspace/app/mkbd/main.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/app/mkbd/main.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -240,5 +240,5 @@
 	
 	char path[MAX_PATH_LENGTH];
-	rc = devman_get_device_path(dev_handle, path, MAX_PATH_LENGTH);
+	rc = devman_fun_get_path(dev_handle, path, MAX_PATH_LENGTH);
 	if (rc != EOK) {
 		return ENOMEM;
Index: uspace/app/tester/Makefile
===================================================================
--- uspace/app/tester/Makefile	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/app/tester/Makefile	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -56,6 +56,4 @@
 	mm/malloc3.c \
 	mm/mapping1.c \
-	devs/devman1.c \
-	devs/devman2.c \
 	hw/misc/virtchar1.c \
 	hw/serial/serial1.c \
Index: pace/app/tester/devs/devman1.c
===================================================================
--- uspace/app/tester/devs/devman1.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ 	(revision )
@@ -1,89 +1,0 @@
-/*
- * Copyright (c) 2011 Vojtech Horky
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup tester
- * @brief Test devman service.
- * @{
- */
-/**
- * @file
- */
-
-#include <inttypes.h>
-#include <errno.h>
-#include <str_error.h>
-#include <sys/types.h>
-#include <async.h>
-#include <devman.h>
-#include <str.h>
-#include <vfs/vfs.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "../tester.h"
-
-#define DEVICE_PATH_NORMAL "/virt/null/a"
-#define DEVICE_CLASS "virt-null"
-#define DEVICE_CLASS_NAME "1"
-#define DEVICE_PATH_CLASSES DEVICE_CLASS "/" DEVICE_CLASS_NAME
-
-const char *test_devman1(void)
-{
-	devman_handle_t handle_primary;
-	devman_handle_t handle_class;
-	
-	int rc;
-	
-	TPRINTF("Asking for handle of `%s'...\n", DEVICE_PATH_NORMAL);
-	rc = devman_device_get_handle(DEVICE_PATH_NORMAL, &handle_primary, 0);
-	if (rc != EOK) {
-		TPRINTF(" ...failed: %s.\n", str_error(rc));
-		if (rc == ENOENT) {
-			TPRINTF("Have you compiled the test drivers?\n");
-		}
-		return "Failed getting device handle";
-	}
-
-	TPRINTF("Asking for handle of `%s' by class..\n", DEVICE_PATH_CLASSES);
-	rc = devman_device_get_handle_by_class(DEVICE_CLASS, DEVICE_CLASS_NAME,
-	    &handle_class, 0);
-	if (rc != EOK) {
-		TPRINTF(" ...failed: %s.\n", str_error(rc));
-		return "Failed getting device class handle";
-	}
-
-	TPRINTF("Received handles %" PRIun " and %" PRIun ".\n",
-	    handle_primary, handle_class);
-	if (handle_primary != handle_class) {
-		return "Retrieved different handles for the same device";
-	}
-
-	return NULL;
-}
-
-/** @}
- */
Index: pace/app/tester/devs/devman1.def
===================================================================
--- uspace/app/tester/devs/devman1.def	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ 	(revision )
@@ -1,6 +1,0 @@
-{
-	"devman1",
-	"devman test",
-	&test_devman1,
-	false
-},
Index: pace/app/tester/devs/devman2.c
===================================================================
--- uspace/app/tester/devs/devman2.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ 	(revision )
@@ -1,94 +1,0 @@
-/*
- * Copyright (c) 2011 Vojtech Horky
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup tester
- * @brief Test devman service.
- * @{
- */
-/**
- * @file
- */
-
-#include <inttypes.h>
-#include <errno.h>
-#include <str_error.h>
-#include <sys/types.h>
-#include <async.h>
-#include <devman.h>
-#include <str.h>
-#include <async.h>
-#include <vfs/vfs.h>
-#include <vfs/vfs_sess.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include "../tester.h"
-
-#define DEVICE_CLASS "test3"
-
-const char *test_devman2(void)
-{
-	size_t idx = 1;
-	int rc = EOK;
-	const char *err_msg = NULL;
-	char *path = NULL;
-	while (rc == EOK) {
-		rc = asprintf(&path, "/loc/class/%s\\%zu", DEVICE_CLASS, idx);
-		if (rc < 0) {
-			continue;
-		}
-		int fd = open(path, O_RDONLY);
-		if (fd < 0) {
-			TPRINTF("Failed opening `%s': %s.\n",
-			    path, str_error(fd));
-			rc = fd;
-			err_msg = "Failed opening file";
-			continue;
-		}
-		async_sess_t *sess = fd_session(EXCHANGE_SERIALIZE, fd);
-		close(fd);
-		if (sess == NULL) {
-			TPRINTF("Failed opening phone: %s.\n", str_error(errno));
-			rc = errno;
-			err_msg = "Failed opening file descriptor phone";
-			continue;
-		}
-		async_hangup(sess);
-		TPRINTF("Path `%s' okay.\n", path);
-		free(path);
-		idx++;
-		rc = EOK;
-	}
-	
-	if (path != NULL)
-		free(path);
-	
-	return err_msg;
-}
-
-/** @}
- */
Index: pace/app/tester/devs/devman2.def
===================================================================
--- uspace/app/tester/devs/devman2.def	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ 	(revision )
@@ -1,6 +1,0 @@
-{
-	"devman2",
-	"devman test",
-	&test_devman2,
-	false
-},
Index: uspace/app/tester/hw/misc/virtchar1.c
===================================================================
--- uspace/app/tester/hw/misc/virtchar1.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/app/tester/hw/misc/virtchar1.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -49,5 +49,4 @@
 
 #define DEVICE_PATH_NORMAL "/loc/devices/\\virt\\null\\a"
-#define DEVICE_PATH_CLASSES "/loc/class/virt-null\\1"
 #define BUFFER_SIZE 64
 
@@ -105,9 +104,4 @@
 	}
 
-	res = test_virtchar1_internal(DEVICE_PATH_CLASSES);
-	if (res != NULL) {
-		return res;
-	}
-
 	return NULL;
 }
Index: uspace/app/tester/hw/serial/serial1.c
===================================================================
--- uspace/app/tester/hw/serial/serial1.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/app/tester/hw/serial/serial1.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -72,5 +72,5 @@
 	
 	devman_handle_t handle;
-	int res = devman_device_get_handle("/hw/pci0/00:01.0/com1/a", &handle,
+	int res = devman_fun_get_handle("/hw/pci0/00:01.0/com1/a", &handle,
 	    IPC_FLAG_BLOCKING);
 	if (res != EOK)
Index: uspace/app/tester/tester.c
===================================================================
--- uspace/app/tester/tester.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/app/tester/tester.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -68,6 +68,4 @@
 #include "hw/misc/virtchar1.def"
 #include "libext2/libext2_1.def"
-#include "devs/devman1.def"
-#include "devs/devman2.def"
 	{NULL, NULL, NULL, false}
 };
Index: uspace/app/trace/trace.c
===================================================================
--- uspace/app/trace/trace.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/app/trace/trace.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -50,4 +50,5 @@
 #include <sys/types.h>
 #include <sys/typefmt.h>
+#include <vfs/vfs.h>
 
 #include <libc.h>
@@ -586,21 +587,21 @@
 
 	/* Send default files */
-	fdi_node_t *files[4];
-	fdi_node_t stdin_node;
-	fdi_node_t stdout_node;
-	fdi_node_t stderr_node;
-	
-	if ((stdin != NULL) && (fnode(stdin, &stdin_node) == EOK))
-		files[0] = &stdin_node;
+	int *files[4];
+	int fd_stdin;
+	int fd_stdout;
+	int fd_stderr;
+	
+	if ((stdin != NULL) && (fhandle(stdin, &fd_stdin) == EOK))
+		files[0] = &fd_stdin;
 	else
 		files[0] = NULL;
 	
-	if ((stdout != NULL) && (fnode(stdout, &stdout_node) == EOK))
-		files[1] = &stdout_node;
+	if ((stdout != NULL) && (fhandle(stdout, &fd_stdout) == EOK))
+		files[1] = &fd_stdout;
 	else
 		files[1] = NULL;
 	
-	if ((stderr != NULL) && (fnode(stderr, &stderr_node) == EOK))
-		files[2] = &stderr_node;
+	if ((stderr != NULL) && (fhandle(stderr, &fd_stderr) == EOK))
+		files[2] = &fd_stderr;
 	else
 		files[2] = NULL;
@@ -761,6 +762,4 @@
 	o = oper_new("open", 2, arg_def, V_INT_ERRNO, 0, resp_def);
 	proto_add_oper(p, VFS_IN_OPEN, o);
-	o = oper_new("open_node", 4, arg_def, V_INT_ERRNO, 0, resp_def);
-	proto_add_oper(p, VFS_IN_OPEN_NODE, o);
 	o = oper_new("read", 1, arg_def, V_ERRNO, 1, resp_def);
 	proto_add_oper(p, VFS_IN_READ, o);
Index: uspace/drv/bus/usb/ehci/main.c
===================================================================
--- uspace/drv/bus/usb/ehci/main.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/bus/usb/ehci/main.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -101,5 +101,5 @@
 	    "Failed to bind EHCI function: %s.\n",
 	    str_error(ret));
-	ret = ddf_fun_add_to_class(hc_fun, USB_HC_DDF_CLASS_NAME);
+	ret = ddf_fun_add_to_category(hc_fun, USB_HC_CATEGORY);
 	CHECK_RET_RETURN(ret,
 	    "Failed to add EHCI to HC class: %s.\n",
Index: uspace/drv/bus/usb/ohci/ohci.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/bus/usb/ohci/ohci.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -244,5 +244,5 @@
 	    "Failed to bind OHCI device function: %s.\n", str_error(ret));
 
-	ret = ddf_fun_add_to_class(instance->hc_fun, USB_HC_DDF_CLASS_NAME);
+	ret = ddf_fun_add_to_category(instance->hc_fun, USB_HC_CATEGORY);
 	CHECK_RET_FINI_RETURN(ret,
 	    "Failed to add OHCI to HC class: %s.\n", str_error(ret));
Index: uspace/drv/bus/usb/uhci/uhci.c
===================================================================
--- uspace/drv/bus/usb/uhci/uhci.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/bus/usb/uhci/uhci.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -267,5 +267,5 @@
 	    str_error(ret));
 
-	ret = ddf_fun_add_to_class(instance->hc_fun, USB_HC_DDF_CLASS_NAME);
+	ret = ddf_fun_add_to_category(instance->hc_fun, USB_HC_CATEGORY);
 	CHECK_RET_FINI_RETURN(ret,
 	    "Failed to add UHCI to HC class: %s.\n", str_error(ret));
Index: uspace/drv/bus/usb/usbhid/kbd/kbddev.c
===================================================================
--- uspace/drv/bus/usb/usbhid/kbd/kbddev.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/bus/usb/usbhid/kbd/kbddev.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -102,5 +102,5 @@
 
 const char *HID_KBD_FUN_NAME = "keyboard";
-const char *HID_KBD_CLASS_NAME = "keyboard";
+const char *HID_KBD_CATEGORY_NAME = "keyboard";
 
 static void usb_kbd_set_led(usb_hid_dev_t *hid_dev, usb_kbd_t *kbd_dev);
@@ -551,10 +551,10 @@
 	    HID_KBD_FUN_NAME, fun->handle);
 	
-	usb_log_debug("Adding DDF function to class %s...\n", 
+	usb_log_debug("Adding DDF function to category %s...\n", 
 	    HID_KBD_CLASS_NAME);
-	rc = ddf_fun_add_to_class(fun, HID_KBD_CLASS_NAME);
+	rc = ddf_fun_add_to_category(fun, HID_KBD_CATEGORY_NAME);
 	if (rc != EOK) {
 		usb_log_error(
-		    "Could not add DDF function to class %s: %s.\n",
+		    "Could not add DDF function to category %s: %s.\n",
 		    HID_KBD_CLASS_NAME, str_error(rc));
 		ddf_fun_destroy(fun);
Index: uspace/drv/bus/usb/usbhid/mouse/mousedev.c
===================================================================
--- uspace/drv/bus/usb/usbhid/mouse/mousedev.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/bus/usb/usbhid/mouse/mousedev.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -74,6 +74,6 @@
 const char *HID_MOUSE_FUN_NAME = "mouse";
 const char *HID_MOUSE_WHEEL_FUN_NAME = "mouse-wheel";
-const char *HID_MOUSE_CLASS_NAME = "mouse";
-const char *HID_MOUSE_WHEEL_CLASS_NAME = "keyboard";
+const char *HID_MOUSE_CATEGORY = "mouse";
+const char *HID_MOUSE_WHEEL_CATEGORY = "keyboard";
 
 /** Default idle rate for mouses. */
@@ -345,11 +345,11 @@
 	}
 	
-	usb_log_debug("Adding DDF function to class %s...\n", 
-	    HID_MOUSE_CLASS_NAME);
-	rc = ddf_fun_add_to_class(fun, HID_MOUSE_CLASS_NAME);
+	usb_log_debug("Adding DDF function to category %s...\n", 
+	    HID_MOUSE_CATEGORY);
+	rc = ddf_fun_add_to_category(fun, HID_MOUSE_CATEGORY);
 	if (rc != EOK) {
 		usb_log_error(
-		    "Could not add DDF function to class %s: %s.\n",
-		    HID_MOUSE_CLASS_NAME, str_error(rc));
+		    "Could not add DDF function to category %s: %s.\n",
+		    HID_MOUSE_CATEGORY, str_error(rc));
 		ddf_fun_destroy(fun);
 		return rc;
@@ -383,11 +383,11 @@
 	}
 	
-	usb_log_debug("Adding DDF function to class %s...\n", 
-	    HID_MOUSE_WHEEL_CLASS_NAME);
-	rc = ddf_fun_add_to_class(fun, HID_MOUSE_WHEEL_CLASS_NAME);
+	usb_log_debug("Adding DDF function to category %s...\n", 
+	    HID_MOUSE_WHEEL_CATEGORY);
+	rc = ddf_fun_add_to_category(fun, HID_MOUSE_WHEEL_CATEGORY);
 	if (rc != EOK) {
 		usb_log_error(
-		    "Could not add DDF function to class %s: %s.\n",
-		    HID_MOUSE_WHEEL_CLASS_NAME, str_error(rc));
+		    "Could not add DDF function to category %s: %s.\n",
+		    HID_MOUSE_WHEEL_CATEGORY, str_error(rc));
 		ddf_fun_destroy(fun);
 		return rc;
Index: uspace/drv/bus/usb/usbhid/mouse/mousedev.h
===================================================================
--- uspace/drv/bus/usb/usbhid/mouse/mousedev.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/bus/usb/usbhid/mouse/mousedev.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -59,5 +59,5 @@
 
 const char *HID_MOUSE_FUN_NAME;
-const char *HID_MOUSE_CLASS_NAME;
+const char *HID_MOUSE_CATEGORY;
 
 /*----------------------------------------------------------------------------*/
Index: uspace/drv/bus/usb/usbhid/multimedia/multimedia.c
===================================================================
--- uspace/drv/bus/usb/usbhid/multimedia/multimedia.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/bus/usb/usbhid/multimedia/multimedia.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -205,11 +205,11 @@
 	}
 	
-	usb_log_debug("%s function created (jandle: %" PRIun ").\n",
+	usb_log_debug("%s function created (handle: %" PRIun ").\n",
 	    NAME, fun->handle);
 	
-	rc = ddf_fun_add_to_class(fun, "keyboard");
+	rc = ddf_fun_add_to_category(fun, "keyboard");
 	if (rc != EOK) {
 		usb_log_error(
-		    "Could not add DDF function to class 'keyboard': %s.\n",
+		    "Could not add DDF function to category 'keyboard': %s.\n",
 		    str_error(rc));
 		// TODO: Can / should I destroy the DDF function?
Index: uspace/drv/bus/usb/usbhub/usbhub.c
===================================================================
--- uspace/drv/bus/usb/usbhub/usbhub.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/bus/usb/usbhub/usbhub.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -131,5 +131,5 @@
 	opResult = ddf_fun_bind(hub_fun);
 	assert(opResult == EOK);
-	opResult = ddf_fun_add_to_class(hub_fun, "hub");
+	opResult = ddf_fun_add_to_category(hub_fun, "hub");
 	assert(opResult == EOK);
 
Index: uspace/drv/bus/usb/usbmouse/init.c
===================================================================
--- uspace/drv/bus/usb/usbmouse/init.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/bus/usb/usbmouse/init.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -116,5 +116,5 @@
 	
 	/* Add the function to mouse class. */
-	rc = ddf_fun_add_to_class(mouse->mouse_fun, "mouse");
+	rc = ddf_fun_add_to_category(mouse->mouse_fun, "mouse");
 	if (rc != EOK)
 		goto leave;
Index: uspace/drv/bus/usb/vhc/main.c
===================================================================
--- uspace/drv/bus/usb/vhc/main.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/bus/usb/vhc/main.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -104,5 +104,5 @@
 	}
 
-	rc = ddf_fun_add_to_class(hc, USB_HC_DDF_CLASS_NAME);
+	rc = ddf_fun_add_to_category(hc, USB_HC_CATEGORY);
 	if (rc != EOK) {
 		usb_log_fatal("Failed to add function to HC class: %s.\n",
Index: uspace/drv/char/ns8250/ns8250.c
===================================================================
--- uspace/drv/char/ns8250/ns8250.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/char/ns8250/ns8250.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -781,5 +781,5 @@
 	ns->fun = fun;
 	
-	ddf_fun_add_to_class(fun, "serial");
+	ddf_fun_add_to_category(fun, "serial");
 	
 	ddf_msg(LVL_NOTE, "Device %s successfully initialized.",
Index: uspace/drv/test/test1/test1.c
===================================================================
--- uspace/drv/test/test1/test1.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/test/test1/test1.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -143,9 +143,9 @@
 	}
 
-	ddf_fun_add_to_class(fun_a, "virtual");
+	ddf_fun_add_to_category(fun_a, "virtual");
 
 	if (str_cmp(dev->name, "null") == 0) {
 		fun_a->ops = &char_device_ops;
-		ddf_fun_add_to_class(fun_a, "virt-null");
+		ddf_fun_add_to_category(fun_a, "virt-null");
 	} else if (str_cmp(dev->name, "test1") == 0) {
 		(void) register_fun_verbose(dev,
Index: uspace/drv/test/test2/test2.c
===================================================================
--- uspace/drv/test/test2/test2.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/test/test2/test2.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -123,5 +123,5 @@
 	}
 
-	ddf_fun_add_to_class(fun_a, "virtual");
+	ddf_fun_add_to_category(fun_a, "virtual");
 
 	return EOK;
Index: uspace/drv/test/test3/test3.c
===================================================================
--- uspace/drv/test/test3/test3.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/drv/test/test3/test3.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -50,5 +50,5 @@
 };
 
-static int register_fun_and_add_to_class(ddf_dev_t *parent,
+static int register_fun_and_add_to_category(ddf_dev_t *parent,
      const char *base_name, size_t index, const char *class_name)
 {
@@ -77,5 +77,5 @@
 	}
 	
-	ddf_fun_add_to_class(fun, class_name);
+	ddf_fun_add_to_category(fun, class_name);
 
 	ddf_msg(LVL_NOTE, "Registered exposed function `%s'.", fun_name);
@@ -100,5 +100,5 @@
 	size_t i;
 	for (i = 0; i < 20; i++) {
-		rc = register_fun_and_add_to_class(dev,
+		rc = register_fun_and_add_to_category(dev,
 		    "test3_", i, "test3");
 		if (rc != EOK) {
Index: uspace/lib/c/arch/abs32le/include/types.h
===================================================================
--- uspace/lib/c/arch/abs32le/include/types.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/arch/abs32le/include/types.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -53,4 +53,5 @@
 typedef int32_t intptr_t;
 typedef uint32_t uintptr_t;
+typedef int32_t intptr_t;
 typedef uint32_t atomic_count_t;
 typedef int32_t atomic_signed_t;
Index: uspace/lib/c/arch/amd64/include/types.h
===================================================================
--- uspace/lib/c/arch/amd64/include/types.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/arch/amd64/include/types.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -53,4 +53,5 @@
 typedef int64_t intptr_t;
 typedef uint64_t uintptr_t;
+typedef int64_t intptr_t;
 typedef uint64_t atomic_count_t;
 typedef int64_t atomic_signed_t;
Index: uspace/lib/c/arch/arm32/include/types.h
===================================================================
--- uspace/lib/c/arch/arm32/include/types.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/arch/arm32/include/types.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -54,4 +54,5 @@
 typedef int32_t intptr_t;
 typedef uint32_t uintptr_t;
+typedef int32_t intptr_t;
 typedef uint32_t atomic_count_t;
 typedef int32_t atomic_signed_t;
Index: uspace/lib/c/arch/ia32/Makefile.common
===================================================================
--- uspace/lib/c/arch/ia32/Makefile.common	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/arch/ia32/Makefile.common	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -28,5 +28,9 @@
 
 CLANG_ARCH = i386
+ifeq ($(PROCESSOR),i486)
+GCC_CFLAGS += -march=i486 -fno-omit-frame-pointer
+else
 GCC_CFLAGS += -march=pentium -fno-omit-frame-pointer
+endif
 
 ENDIANESS = LE
Index: uspace/lib/c/arch/ia32/Makefile.inc
===================================================================
--- uspace/lib/c/arch/ia32/Makefile.inc	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/arch/ia32/Makefile.inc	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -28,5 +28,5 @@
 
 ARCH_SOURCES = \
-	arch/$(UARCH)/src/entry.s \
+	arch/$(UARCH)/src/entry.S \
 	arch/$(UARCH)/src/entryjmp.s \
 	arch/$(UARCH)/src/thread_entry.s \
Index: uspace/lib/c/arch/ia32/include/types.h
===================================================================
--- uspace/lib/c/arch/ia32/include/types.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/arch/ia32/include/types.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -53,4 +53,5 @@
 typedef int32_t intptr_t;
 typedef uint32_t uintptr_t;
+typedef int32_t intptr_t;
 typedef uint32_t atomic_count_t;
 typedef int32_t atomic_signed_t;
Index: uspace/lib/c/arch/ia32/src/entry.S
===================================================================
--- uspace/lib/c/arch/ia32/src/entry.S	(revision 07b3933873be077318b66f2e001bce5785804448)
+++ uspace/lib/c/arch/ia32/src/entry.S	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -0,0 +1,67 @@
+#
+# Copyright (c) 2005 Martin Decky
+# 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.
+#
+
+INTEL_CPUID_STANDARD = 1
+INTEL_SEP = 11
+
+.section .init, "ax"
+
+.org 0
+
+.globl __entry
+
+## User-space task entry point
+#
+# %edi contains the PCB pointer
+#
+__entry:
+	mov %ss, %ax
+	mov %ax, %ds
+	mov %ax, %es
+	mov %ax, %fs
+	# Do not set %gs, it contains descriptor that can see TLS
+
+#ifndef PROCESSOR_i486	
+	# Detect the mechanism used for making syscalls
+	movl $(INTEL_CPUID_STANDARD), %eax
+	cpuid
+	bt $(INTEL_SEP), %edx
+	jnc 0f
+	leal __syscall_fast_func, %eax
+	movl $__syscall_fast, (%eax)
+0:
+#endif
+	#
+	# Create the first stack frame.
+	#
+	pushl $0
+	movl %esp, %ebp
+	
+	# Pass the PCB pointer to __main as the first argument
+	pushl %edi
+	call __main
Index: pace/lib/c/arch/ia32/src/entry.s
===================================================================
--- uspace/lib/c/arch/ia32/src/entry.s	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ 	(revision )
@@ -1,65 +1,0 @@
-#
-# Copyright (c) 2005 Martin Decky
-# 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.
-#
-
-INTEL_CPUID_STANDARD = 1
-INTEL_SEP = 11
-
-.section .init, "ax"
-
-.org 0
-
-.globl __entry
-
-## User-space task entry point
-#
-# %edi contains the PCB pointer
-#
-__entry:
-	mov %ss, %ax
-	mov %ax, %ds
-	mov %ax, %es
-	mov %ax, %fs
-	# Do not set %gs, it contains descriptor that can see TLS
-	
-	# Detect the mechanism used for making syscalls
-	movl $(INTEL_CPUID_STANDARD), %eax
-	cpuid
-	bt $(INTEL_SEP), %edx
-	jnc 0f
-	leal __syscall_fast_func, %eax
-	movl $__syscall_fast, (%eax)
-0:
-	#
-	# Create the first stack frame.
-	#
-	pushl $0
-	movl %esp, %ebp
-	
-	# Pass the PCB pointer to __main as the first argument
-	pushl %edi
-	call __main
Index: uspace/lib/c/arch/ia64/include/types.h
===================================================================
--- uspace/lib/c/arch/ia64/include/types.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/arch/ia64/include/types.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -63,4 +63,5 @@
 typedef int64_t intptr_t;
 typedef uint64_t uintptr_t;
+typedef int64_t intptr_t;
 typedef uint64_t atomic_count_t;
 typedef int64_t atomic_signed_t;
Index: uspace/lib/c/arch/mips32/include/types.h
===================================================================
--- uspace/lib/c/arch/mips32/include/types.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/arch/mips32/include/types.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -54,4 +54,5 @@
 typedef int32_t intptr_t;
 typedef uint32_t uintptr_t;
+typedef int32_t intptr_t;
 typedef uint32_t atomic_count_t;
 typedef int32_t atomic_signed_t;
Index: uspace/lib/c/arch/mips64/include/types.h
===================================================================
--- uspace/lib/c/arch/mips64/include/types.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/arch/mips64/include/types.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -53,4 +53,5 @@
 
 typedef uint64_t uintptr_t;
+typedef int64_t intptr_t;
 typedef uint64_t atomic_count_t;
 typedef int64_t atomic_signed_t;
Index: uspace/lib/c/arch/ppc32/include/types.h
===================================================================
--- uspace/lib/c/arch/ppc32/include/types.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/arch/ppc32/include/types.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -53,4 +53,5 @@
 typedef int32_t intptr_t;
 typedef uint32_t uintptr_t;
+typedef int32_t intptr_t;
 typedef uint32_t atomic_count_t;
 typedef int32_t atomic_signed_t;
Index: uspace/lib/c/arch/sparc64/include/types.h
===================================================================
--- uspace/lib/c/arch/sparc64/include/types.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/arch/sparc64/include/types.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -53,4 +53,5 @@
 typedef int64_t intptr_t;
 typedef uint64_t uintptr_t;
+typedef int64_t intptr_t;
 typedef uint64_t atomic_count_t;
 typedef int64_t atomic_signed_t;
Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/generic/async.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -112,4 +112,5 @@
 #include <mem.h>
 #include <stdlib.h>
+#include <macros.h>
 #include "private/async.h"
 
@@ -138,5 +139,5 @@
 	link_t link;
 	
-	sysarg_t in_task_hash;
+	task_id_t in_task_id;
 	atomic_t refcnt;
 	void *data;
@@ -150,6 +151,6 @@
 	link_t link;
 	
-	/** Incoming client task hash. */
-	sysarg_t in_task_hash;
+	/** Incoming client task ID. */
+	task_id_t in_task_id;
 	
 	/** Incoming phone hash. */
@@ -203,10 +204,4 @@
 }
 
-void *async_get_client_data(void)
-{
-	assert(fibril_connection);
-	return fibril_connection->client->data;
-}
-
 /** Default fibril function that gets called to handle new connection.
  *
@@ -289,8 +284,10 @@
 {
 	assert(key);
+	assert(keys == 2);
 	assert(item);
 	
 	client_t *client = hash_table_get_instance(item, client_t, link);
-	return (key[0] == client->in_task_hash);
+	return (key[0] == LOWER32(client->in_task_id) &&
+	    (key[1] == UPPER32(client->in_task_id)));
 }
 
@@ -580,4 +577,91 @@
 }
 
+static client_t *async_client_get(task_id_t client_id, bool create)
+{
+	unsigned long key[2] = {
+		LOWER32(client_id),
+		UPPER32(client_id),
+	};
+	client_t *client = NULL;
+
+	futex_down(&async_futex);
+	link_t *lnk = hash_table_find(&client_hash_table, key);
+	if (lnk) {
+		client = hash_table_get_instance(lnk, client_t, link);
+		atomic_inc(&client->refcnt);
+	} else if (create) {
+		client = malloc(sizeof(client_t));
+		if (client) {
+			client->in_task_id = client_id;
+			client->data = async_client_data_create();
+		
+			atomic_set(&client->refcnt, 1);
+			hash_table_insert(&client_hash_table, key, &client->link);
+		}
+	}
+
+	futex_up(&async_futex);
+	return client;
+}
+
+static void async_client_put(client_t *client)
+{
+	bool destroy;
+	unsigned long key[2] = {
+		LOWER32(client->in_task_id),
+		UPPER32(client->in_task_id)
+	};
+	
+	futex_down(&async_futex);
+	
+	if (atomic_predec(&client->refcnt) == 0) {
+		hash_table_remove(&client_hash_table, key, 2);
+		destroy = true;
+	} else
+		destroy = false;
+	
+	futex_up(&async_futex);
+	
+	if (destroy) {
+		if (client->data)
+			async_client_data_destroy(client->data);
+		
+		free(client);
+	}
+}
+
+void *async_get_client_data(void)
+{
+	assert(fibril_connection);
+	return fibril_connection->client->data;
+}
+
+void *async_get_client_data_by_id(task_id_t client_id)
+{
+	client_t *client = async_client_get(client_id, false);
+	if (!client)
+		return NULL;
+	if (!client->data) {
+		async_client_put(client);
+		return NULL;
+	}
+
+	return client->data;
+}
+
+void async_put_client_data_by_id(task_id_t client_id)
+{
+	client_t *client = async_client_get(client_id, false);
+
+	assert(client);
+	assert(client->data);
+
+	/* Drop the reference we got in async_get_client_data_by_hash(). */
+	async_client_put(client);
+
+	/* Drop our own reference we got at the beginning of this function. */
+	async_client_put(client);
+}
+
 /** Wrapper for client connection fibril.
  *
@@ -598,6 +682,4 @@
 	 */
 	fibril_connection = (connection_t *) arg;
-	
-	futex_down(&async_futex);
 	
 	/*
@@ -606,30 +688,11 @@
 	 * hash in a new tracking structure.
 	 */
-	
-	unsigned long key = fibril_connection->in_task_hash;
-	link_t *lnk = hash_table_find(&client_hash_table, &key);
-	
-	client_t *client;
-	
-	if (lnk) {
-		client = hash_table_get_instance(lnk, client_t, link);
-		atomic_inc(&client->refcnt);
-	} else {
-		client = malloc(sizeof(client_t));
-		if (!client) {
-			ipc_answer_0(fibril_connection->callid, ENOMEM);
-			futex_up(&async_futex);
-			return 0;
-		}
-		
-		client->in_task_hash = fibril_connection->in_task_hash;
-		client->data = async_client_data_create();
-		
-		atomic_set(&client->refcnt, 1);
-		hash_table_insert(&client_hash_table, &key, &client->link);
-	}
-	
-	futex_up(&async_futex);
-	
+
+	client_t *client = async_client_get(fibril_connection->in_task_id, true);
+	if (!client) {
+		ipc_answer_0(fibril_connection->callid, ENOMEM);
+		return 0;
+	}
+
 	fibril_connection->client = client;
 	
@@ -643,22 +706,5 @@
 	 * Remove the reference for this client task connection.
 	 */
-	bool destroy;
-	
-	futex_down(&async_futex);
-	
-	if (atomic_predec(&client->refcnt) == 0) {
-		hash_table_remove(&client_hash_table, &key, 1);
-		destroy = true;
-	} else
-		destroy = false;
-	
-	futex_up(&async_futex);
-	
-	if (destroy) {
-		if (client->data)
-			async_client_data_destroy(client->data);
-		
-		free(client);
-	}
+	async_client_put(client);
 	
 	/*
@@ -666,5 +712,5 @@
 	 */
 	futex_down(&async_futex);
-	key = fibril_connection->in_phone_hash;
+	unsigned long key = fibril_connection->in_phone_hash;
 	hash_table_remove(&conn_hash_table, &key, 1);
 	futex_up(&async_futex);
@@ -700,5 +746,5 @@
  * particular fibrils.
  *
- * @param in_task_hash  Identification of the incoming connection.
+ * @param in_task_id    Identification of the incoming connection.
  * @param in_phone_hash Identification of the incoming connection.
  * @param callid        Hash of the opening IPC_M_CONNECT_ME_TO call.
@@ -714,5 +760,5 @@
  *
  */
-fid_t async_new_connection(sysarg_t in_task_hash, sysarg_t in_phone_hash,
+fid_t async_new_connection(task_id_t in_task_id, sysarg_t in_phone_hash,
     ipc_callid_t callid, ipc_call_t *call,
     async_client_conn_t cfibril, void *carg)
@@ -726,5 +772,5 @@
 	}
 	
-	conn->in_task_hash = in_task_hash;
+	conn->in_task_id = in_task_id;
 	conn->in_phone_hash = in_phone_hash;
 	list_initialize(&conn->msg_queue);
@@ -785,5 +831,5 @@
 	case IPC_M_CONNECT_ME_TO:
 		/* Open new connection with fibril, etc. */
-		async_new_connection(call->in_task_hash, IPC_GET_ARG5(*call),
+		async_new_connection(call->in_task_id, IPC_GET_ARG5(*call),
 		    callid, call, client_connection, NULL);
 		return;
@@ -933,5 +979,5 @@
 {
 	if (!hash_table_create(&client_hash_table, CLIENT_HASH_TABLE_BUCKETS,
-	    1, &client_hash_table_ops))
+	    2, &client_hash_table_ops))
 		abort();
 	
@@ -1426,13 +1472,17 @@
 		return ENOENT;
 	
-	sysarg_t task_hash;
+	task_id_t task_id;
+	sysarg_t task_id_lo;
+	sysarg_t task_id_hi;
 	sysarg_t phone_hash;
 	int rc = async_req_3_5(exch, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
-	    NULL, NULL, NULL, &task_hash, &phone_hash);
+	    NULL, NULL, &task_id_lo, &task_id_hi, &phone_hash);
 	if (rc != EOK)
 		return rc;
+
+	task_id = (task_id_t) MERGE_LOUP32(task_id_lo, task_id_hi);
 	
 	if (client_receiver != NULL)
-		async_new_connection(task_hash, phone_hash, 0, NULL,
+		async_new_connection(task_id, phone_hash, 0, NULL,
 		    client_receiver, carg);
 	
@@ -2429,4 +2479,37 @@
 }
 
+int async_state_change_start(async_exch_t *exch, sysarg_t arg1, sysarg_t arg2,
+    sysarg_t arg3, async_exch_t *other_exch)
+{
+	return async_req_5_0(exch, IPC_M_STATE_CHANGE_AUTHORIZE,
+	    arg1, arg2, arg3, 0, other_exch->phone);
+}
+
+bool async_state_change_receive(ipc_callid_t *callid, sysarg_t *arg1,
+    sysarg_t *arg2, sysarg_t *arg3)
+{
+	assert(callid);
+
+	ipc_call_t call;
+	*callid = async_get_call(&call);
+
+	if (IPC_GET_IMETHOD(call) != IPC_M_STATE_CHANGE_AUTHORIZE)
+		return false;
+	
+	if (arg1)
+		*arg1 = IPC_GET_ARG1(call);
+	if (arg2)
+		*arg2 = IPC_GET_ARG2(call);
+	if (arg3)
+		*arg3 = IPC_GET_ARG3(call);
+
+	return true;
+}
+
+int async_state_change_finalize(ipc_callid_t callid, async_exch_t *other_exch)
+{
+	return ipc_answer_1(callid, EOK, other_exch->phone);
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/devman.c
===================================================================
--- uspace/lib/c/generic/devman.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/generic/devman.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2007 Josef Cejka
- * Copyright (c) 2009 Jiri Svoboda
+ * Copyright (c) 2011 Jiri Svoboda
  * Copyright (c) 2010 Lenka Trochtova
  * All rights reserved.
@@ -195,5 +195,5 @@
 	
 	exch = devman_exchange_begin(DEVMAN_DRIVER);
-	async_connect_to_me(exch, 0, 0, 0, NULL, NULL);
+	async_connect_to_me(exch, 0, 0, 0, conn, NULL);
 	devman_exchange_end(exch);
 	
@@ -271,14 +271,14 @@
 }
 
-int devman_add_device_to_class(devman_handle_t devman_handle,
-    const char *class_name)
+int devman_add_device_to_category(devman_handle_t devman_handle,
+    const char *cat_name)
 {
 	async_exch_t *exch = devman_exchange_begin_blocking(DEVMAN_DRIVER);
 	
 	ipc_call_t answer;
-	aid_t req = async_send_1(exch, DEVMAN_ADD_DEVICE_TO_CLASS,
+	aid_t req = async_send_1(exch, DEVMAN_ADD_DEVICE_TO_CATEGORY,
 	    devman_handle, &answer);
-	sysarg_t retval = async_data_write_start(exch, class_name,
-	    str_size(class_name));
+	sysarg_t retval = async_data_write_start(exch, cat_name,
+	    str_size(cat_name));
 	
 	devman_exchange_end(exch);
@@ -308,4 +308,23 @@
 }
 
+/** Remove function from device.
+ *
+ * Request devman to remove function owned by this driver task.
+ * @param funh      Devman handle of the function
+ *
+ * @return EOK on success or negative error code.
+ */
+int devman_remove_function(devman_handle_t funh)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	
+	exch = devman_exchange_begin_blocking(DEVMAN_DRIVER);
+	retval = async_req_1_0(exch, DEVMAN_REMOVE_FUNCTION, (sysarg_t) funh);
+	devman_exchange_end(exch);
+	
+	return (int) retval;
+}
+
 async_sess_t *devman_parent_device_connect(exch_mgmt_t mgmt,
     devman_handle_t handle, unsigned int flags)
@@ -323,5 +342,5 @@
 }
 
-int devman_device_get_handle(const char *pathname, devman_handle_t *handle,
+int devman_fun_get_handle(const char *pathname, devman_handle_t *handle,
     unsigned int flags)
 {
@@ -333,5 +352,5 @@
 		exch = devman_exchange_begin(DEVMAN_CLIENT);
 		if (exch == NULL)
-			return errno;
+			return ENOMEM;
 	}
 	
@@ -364,101 +383,162 @@
 }
 
-int devman_device_get_handle_by_class(const char *classname,
-    const char *devname, devman_handle_t *handle, unsigned int flags)
+static int devman_get_str_internal(sysarg_t method, sysarg_t arg1, char *buf,
+    size_t buf_size)
 {
 	async_exch_t *exch;
-	
-	if (flags & IPC_FLAG_BLOCKING)
-		exch = devman_exchange_begin_blocking(DEVMAN_CLIENT);
-	else {
-		exch = devman_exchange_begin(DEVMAN_CLIENT);
-		if (exch == NULL)
-			return errno;
-	}
+	ipc_call_t dreply;
+	size_t act_size;
+	sysarg_t dretval;
+	
+	exch = devman_exchange_begin_blocking(LOC_PORT_CONSUMER);
 	
 	ipc_call_t answer;
-	aid_t req = async_send_1(exch, DEVMAN_DEVICE_GET_HANDLE_BY_CLASS,
-	    flags, &answer);
-	sysarg_t retval = async_data_write_start(exch, classname,
-	    str_size(classname));
+	aid_t req = async_send_1(exch, method, arg1, &answer);
+	aid_t dreq = async_data_read(exch, buf, buf_size - 1, &dreply);
+	async_wait_for(dreq, &dretval);
+	
+	devman_exchange_end(exch);
+	
+	if (dretval != EOK) {
+		async_wait_for(req, NULL);
+		return dretval;
+	}
+	
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+	
+	if (retval != EOK)
+		return retval;
+	
+	act_size = IPC_GET_ARG2(dreply);
+	assert(act_size <= buf_size - 1);
+	buf[act_size] = '\0';
+	
+	return EOK;
+}
+
+int devman_fun_get_path(devman_handle_t handle, char *buf, size_t buf_size)
+{
+	return devman_get_str_internal(DEVMAN_FUN_GET_PATH, handle, buf,
+	    buf_size);
+}
+
+int devman_fun_get_name(devman_handle_t handle, char *buf, size_t buf_size)
+{
+	return devman_get_str_internal(DEVMAN_FUN_GET_NAME, handle, buf,
+	    buf_size);
+}
+
+static int devman_get_handles_once(sysarg_t method, sysarg_t arg1,
+    devman_handle_t *handle_buf, size_t buf_size, size_t *act_size)
+{
+	async_exch_t *exch = devman_exchange_begin_blocking(DEVMAN_CLIENT);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, method, arg1, &answer);
+	int rc = async_data_read_start(exch, handle_buf, buf_size);
+	
+	devman_exchange_end(exch);
+	
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		return rc;
+	}
+	
+	sysarg_t retval;
+	async_wait_for(req, &retval);
 	
 	if (retval != EOK) {
-		devman_exchange_end(exch);
-		async_wait_for(req, NULL);
-		return retval;
-	}
-	
-	retval = async_data_write_start(exch, devname,
-	    str_size(devname));
-	
-	devman_exchange_end(exch);
-	
-	if (retval != EOK) {
-		async_wait_for(req, NULL);
-		return retval;
-	}
-	
-	async_wait_for(req, &retval);
-	
-	if (retval != EOK) {
-		if (handle != NULL)
-			*handle = (devman_handle_t) -1;
-		
-		return retval;
-	}
-	
-	if (handle != NULL)
-		*handle = (devman_handle_t) IPC_GET_ARG1(answer);
-	
-	return retval;
-}
-
-int devman_get_device_path(devman_handle_t handle, char *path, size_t path_size)
+		return retval;
+	}
+	
+	*act_size = IPC_GET_ARG1(answer);
+	return EOK;
+}
+
+/** Get list of handles.
+ *
+ * Returns an allocated array of handles.
+ *
+ * @param method	IPC method
+ * @param arg1		IPC argument 1
+ * @param data		Place to store pointer to array of handles
+ * @param count		Place to store number of handles
+ * @return 		EOK on success or negative error code
+ */
+static int devman_get_handles_internal(sysarg_t method, sysarg_t arg1,
+    devman_handle_t **data, size_t *count)
+{
+	devman_handle_t *handles;
+	size_t act_size;
+	size_t alloc_size;
+	int rc;
+
+	*data = NULL;
+	act_size = 0;	/* silence warning */
+
+	rc = devman_get_handles_once(method, arg1, NULL, 0,
+	    &act_size);
+	if (rc != EOK)
+		return rc;
+
+	alloc_size = act_size;
+	handles = malloc(alloc_size);
+	if (handles == NULL)
+		return ENOMEM;
+
+	while (true) {
+		rc = devman_get_handles_once(method, arg1, handles, alloc_size,
+		    &act_size);
+		if (rc != EOK)
+			return rc;
+
+		if (act_size <= alloc_size)
+			break;
+
+		alloc_size *= 2;
+		free(handles);
+
+		handles = malloc(alloc_size);
+		if (handles == NULL)
+			return ENOMEM;
+	}
+
+	*count = act_size / sizeof(devman_handle_t);
+	*data = handles;
+	return EOK;
+}
+
+int devman_fun_get_child(devman_handle_t funh, devman_handle_t *devh)
 {
 	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
 	if (exch == NULL)
-		return errno;
-	
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, DEVMAN_DEVICE_GET_DEVICE_PATH,
-	    handle, &answer);
-	
-	ipc_call_t data_request_call;
-	aid_t data_request = async_data_read(exch, path, path_size,
-	    &data_request_call);
-	
-	devman_exchange_end(exch);
-	
-	if (data_request == 0) {
-		async_wait_for(req, NULL);
 		return ENOMEM;
-	}
-	
-	sysarg_t data_request_rc;
-	async_wait_for(data_request, &data_request_rc);
-	
-	sysarg_t opening_request_rc;
-	async_wait_for(req, &opening_request_rc);
-	
-	if (data_request_rc != EOK) {
-		/* Prefer the return code of the opening request. */
-		if (opening_request_rc != EOK)
-			return (int) opening_request_rc;
-		else
-			return (int) data_request_rc;
-	}
-	
-	if (opening_request_rc != EOK)
-		return (int) opening_request_rc;
-	
-	/* To be on the safe-side. */
-	path[path_size - 1] = 0;
-	size_t transferred_size = IPC_GET_ARG2(data_request_call);
-	if (transferred_size >= path_size)
-		return ELIMIT;
-	
-	/* Terminate the string (trailing 0 not send over IPC). */
-	path[transferred_size] = 0;
-	return EOK;
+	
+	sysarg_t retval = async_req_1_1(exch, DEVMAN_FUN_GET_CHILD,
+	    funh, devh);
+	
+	devman_exchange_end(exch);
+	return (int) retval;
+}
+
+int devman_dev_get_functions(devman_handle_t devh, devman_handle_t **funcs,
+    size_t *count)
+{
+	return devman_get_handles_internal(DEVMAN_DEV_GET_FUNCTIONS,
+	    devh, funcs, count);
+}
+
+int devman_fun_sid_to_handle(service_id_t sid, devman_handle_t *handle)
+{
+	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	sysarg_t retval = async_req_1_1(exch, DEVMAN_FUN_SID_TO_HANDLE,
+	    sid, handle);
+	
+	devman_exchange_end(exch);
+	return (int) retval;
 }
 
Index: uspace/lib/c/generic/event.c
===================================================================
--- uspace/lib/c/generic/event.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/generic/event.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -54,4 +54,10 @@
 }
 
+int event_task_subscribe(event_task_type_t evno, sysarg_t imethod)
+{
+	return __SYSCALL2(SYS_EVENT_SUBSCRIBE, (sysarg_t) evno,
+	    (sysarg_t) imethod);
+}
+
 /** Unmask event notifications.
  *
@@ -66,4 +72,9 @@
 }
 
+int event_task_unmask(event_task_type_t evno)
+{
+	return __SYSCALL1(SYS_EVENT_UNMASK, (sysarg_t) evno);
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/io/io.c
===================================================================
--- uspace/lib/c/generic/io/io.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/generic/io/io.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -101,8 +101,8 @@
 static LIST_INITIALIZE(files);
 
-void __stdio_init(int filc, fdi_node_t *filv[])
+void __stdio_init(int filc)
 {
 	if (filc > 0) {
-		stdin = fopen_node(filv[0], "r");
+		stdin = fdopen(0, "r");
 	} else {
 		stdin = &stdin_null;
@@ -111,5 +111,5 @@
 	
 	if (filc > 1) {
-		stdout = fopen_node(filv[1], "w");
+		stdout = fdopen(1, "w");
 	} else {
 		stdout = &stdout_klog;
@@ -118,5 +118,5 @@
 	
 	if (filc > 2) {
-		stderr = fopen_node(filv[2], "w");
+		stderr = fdopen(2, "w");
 	} else {
 		stderr = &stderr_klog;
@@ -285,36 +285,4 @@
 }
 
-FILE *fopen_node(fdi_node_t *node, const char *mode)
-{
-	int flags;
-	if (!parse_mode(mode, &flags))
-		return NULL;
-	
-	/* Open file. */
-	FILE *stream = malloc(sizeof(FILE));
-	if (stream == NULL) {
-		errno = ENOMEM;
-		return NULL;
-	}
-	
-	stream->fd = open_node(node, flags);
-	if (stream->fd < 0) {
-		/* errno was set by open_node() */
-		free(stream);
-		return NULL;
-	}
-	
-	stream->error = false;
-	stream->eof = false;
-	stream->klog = false;
-	stream->sess = NULL;
-	stream->need_sync = false;
-	_setvbuf(stream);
-	
-	list_append(&stream->link, &files);
-	
-	return stream;
-}
-
 int fclose(FILE *stream)
 {
@@ -780,8 +748,10 @@
 }
 
-int fnode(FILE *stream, fdi_node_t *node)
-{
-	if (stream->fd >= 0)
-		return fd_node(stream->fd, node);
+int fhandle(FILE *stream, int *handle)
+{
+	if (stream->fd >= 0) {
+		*handle = stream->fd;
+		return EOK;
+	}
 	
 	return ENOENT;
Index: uspace/lib/c/generic/io/printf_core.c
===================================================================
--- uspace/lib/c/generic/io/printf_core.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/generic/io/printf_core.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -206,5 +206,5 @@
 	}
 	
-	return (int) (counter + 1);
+	return (int) (counter);
 }
 
@@ -244,5 +244,5 @@
 	}
 	
-	return (int) (counter + 1);
+	return (int) (counter);
 }
 
Index: uspace/lib/c/generic/libc.c
===================================================================
--- uspace/lib/c/generic/libc.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/generic/libc.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -91,9 +91,9 @@
 		argc = 0;
 		argv = NULL;
-		__stdio_init(0, NULL);
+		__stdio_init(0);
 	} else {
 		argc = __pcb->argc;
 		argv = __pcb->argv;
-		__stdio_init(__pcb->filc, __pcb->filv);
+		__stdio_init(__pcb->filc);
 		(void) chdir(__pcb->cwd);
 	}
Index: uspace/lib/c/generic/loader.c
===================================================================
--- uspace/lib/c/generic/loader.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/generic/loader.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -256,38 +256,28 @@
  *
  */
-int loader_set_files(loader_t *ldr, fdi_node_t *const files[])
-{
-	/*
-	 * Serialize the arguments into a single array. First
-	 * compute size of the buffer needed.
-	 */
-	fdi_node_t *const *ap = files;
-	size_t count = 0;
-	while (*ap != NULL) {
-		count++;
-		ap++;
-	}
-	
-	fdi_node_t *files_buf;
-	files_buf = (fdi_node_t *) malloc(count * sizeof(fdi_node_t));
-	if (files_buf == NULL)
-		return ENOMEM;
-	
-	/* Fill the buffer */
-	size_t i;
-	for (i = 0; i < count; i++)
-		files_buf[i] = *files[i];
-	
+int loader_set_files(loader_t *ldr, int * const files[])
+{
 	/* Send serialized files to the loader */
 	async_exch_t *exch = async_exchange_begin(ldr->sess);
-	
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, LOADER_SET_FILES, &answer);
-	sysarg_t rc = async_data_write_start(exch, (void *) files_buf,
-	    count * sizeof(fdi_node_t));
-	
-	async_exchange_end(exch);
-	free(files_buf);
-	
+	async_exch_t *vfs_exch = vfs_exchange_begin();
+	
+	int i;
+	for (i = 0; files[i]; i++);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, LOADER_SET_FILES, i, &answer);
+
+	sysarg_t rc = EOK;
+	
+	for (i = 0; files[i]; i++) {
+		rc = async_state_change_start(exch, VFS_PASS_HANDLE, *files[i],
+		    0, vfs_exch); 
+		if (rc != EOK)
+			break;
+	}
+	
+	vfs_exchange_end(vfs_exch);
+	async_exchange_end(exch);
+
 	if (rc != EOK) {
 		async_wait_for(req, NULL);
Index: uspace/lib/c/generic/loc.c
===================================================================
--- uspace/lib/c/generic/loc.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/generic/loc.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -45,4 +45,7 @@
 static FIBRIL_MUTEX_INITIALIZE(loc_consumer_mutex);
 
+static FIBRIL_MUTEX_INITIALIZE(loc_callback_mutex);
+static bool loc_callback_created = false;
+
 static async_sess_t *loc_supp_block_sess = NULL;
 static async_sess_t *loc_cons_block_sess = NULL;
@@ -51,4 +54,40 @@
 static async_sess_t *loc_consumer_sess = NULL;
 
+static loc_cat_change_cb_t cat_change_cb = NULL;
+
+static void loc_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	loc_cat_change_cb_t cb_fun;
+	
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call)) {
+			/* TODO: Handle hangup */
+			return;
+		}
+		
+		int retval;
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case LOC_EVENT_CAT_CHANGE:
+			fibril_mutex_lock(&loc_callback_mutex);
+			cb_fun = cat_change_cb;
+			if (cb_fun != NULL) {
+				(*cb_fun)();
+			}
+			fibril_mutex_unlock(&loc_callback_mutex);
+			retval = 0;
+			break;
+		default:
+			retval = ENOTSUP;
+		}
+		
+		async_answer_0(callid, retval);
+	}
+}
+
+
 static void clone_session(fibril_mutex_t *mtx, async_sess_t *src,
     async_sess_t **dst)
@@ -60,4 +99,38 @@
 	
 	fibril_mutex_unlock(mtx);
+}
+
+static int loc_callback_create(void)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	int rc = EOK;
+
+	fibril_mutex_lock(&loc_callback_mutex);
+	
+	if (!loc_callback_created) {
+		exch = loc_exchange_begin_blocking(LOC_PORT_CONSUMER);
+		
+		ipc_call_t answer;
+		aid_t req = async_send_0(exch, LOC_CALLBACK_CREATE, &answer);
+		async_connect_to_me(exch, 0, 0, 0, loc_cb_conn, NULL);
+		loc_exchange_end(exch);
+		
+		async_wait_for(req, &retval);
+		if (rc != EOK)
+			goto done;
+		
+		if (retval != EOK) {
+			rc = retval;
+			goto done;
+		}
+		
+		loc_callback_created = true;
+	}
+	
+	rc = EOK;
+done:
+	fibril_mutex_unlock(&loc_callback_mutex);
+	return rc;
 }
 
@@ -194,7 +267,7 @@
 }
 
-/** Register new device.
- *
- * The @p interface is used when forwarding connection to the driver.
+/** Register new service.
+ *
+ * The @p interface is used when forwarding connection to the server.
  * If not 0, the first argument is the interface and the second argument
  * is the service ID.
@@ -203,11 +276,11 @@
  * the handle (to ensure backward compatibility).
  *
- * @param      fqdn      Fully qualified device name.
- * @param[out] handle    Handle to the created instance of device.
- * @param      interface Interface when forwarding.
- *
- */
-int loc_service_register_with_iface(const char *fqdn,
-    service_id_t *handle, sysarg_t interface)
+ * @param      fqsn      Fully qualified service name
+ * @param[out] sid       Service ID of new service
+ * @param      interface Interface when forwarding
+ *
+ */
+int loc_service_register_with_iface(const char *fqsn,
+    service_id_t *sid, sysarg_t interface)
 {
 	async_exch_t *exch = loc_exchange_begin_blocking(LOC_PORT_SUPPLIER);
@@ -216,5 +289,5 @@
 	aid_t req = async_send_2(exch, LOC_SERVICE_REGISTER, interface, 0,
 	    &answer);
-	sysarg_t retval = async_data_write_start(exch, fqdn, str_size(fqdn));
+	sysarg_t retval = async_data_write_start(exch, fqsn, str_size(fqsn));
 	
 	loc_exchange_end(exch);
@@ -228,25 +301,41 @@
 	
 	if (retval != EOK) {
-		if (handle != NULL)
-			*handle = -1;
-		
-		return retval;
-	}
-	
-	if (handle != NULL)
-		*handle = (service_id_t) IPC_GET_ARG1(answer);
+		if (sid != NULL)
+			*sid = -1;
+		
+		return retval;
+	}
+	
+	if (sid != NULL)
+		*sid = (service_id_t) IPC_GET_ARG1(answer);
 	
 	return retval;
 }
 
-/** Register new device.
- *
- * @param fqdn   Fully qualified device name.
- * @param handle Output: Handle to the created instance of device.
- *
- */
-int loc_service_register(const char *fqdn, service_id_t *handle)
-{
-	return loc_service_register_with_iface(fqdn, handle, 0);
+/** Register new service.
+ *
+ * @param fqsn Fully qualified service name
+ * @param sid  Output: ID of new service
+ *
+ */
+int loc_service_register(const char *fqdn, service_id_t *sid)
+{
+	return loc_service_register_with_iface(fqdn, sid, 0);
+}
+
+/** Unregister service.
+ *
+ * @param sid	Service ID
+ */
+int loc_service_unregister(service_id_t sid)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	
+	exch = loc_exchange_begin_blocking(LOC_PORT_SUPPLIER);
+	retval = async_req_1_0(exch, LOC_SERVICE_UNREGISTER, sid);
+	loc_exchange_end(exch);
+	
+	return (int)retval;
 }
 
@@ -291,14 +380,15 @@
 }
 
-/** Get service name.
- *
- * Provided ID of a service, return its name.
- *
- * @param svc_id	Service ID
+/** Get object name.
+ *
+ * Provided ID of an object, return its name.
+ *
+ * @param method	IPC method
+ * @param id		Object ID
  * @param name		Place to store pointer to new string. Caller should
  *			free it using free().
  * @return		EOK on success or negative error code
  */
-int loc_service_get_name(service_id_t svc_id, char **name)
+static int loc_get_name_internal(sysarg_t method, sysarg_t id, char **name)
 {
 	async_exch_t *exch;
@@ -312,5 +402,5 @@
 	
 	ipc_call_t answer;
-	aid_t req = async_send_1(exch, LOC_SERVICE_GET_NAME, svc_id, &answer);
+	aid_t req = async_send_1(exch, method, id, &answer);
 	aid_t dreq = async_data_read(exch, name_buf, LOC_NAME_MAXLEN,
 	    &dreply);
@@ -341,4 +431,31 @@
 }
 
+/** Get category name.
+ *
+ * Provided ID of a service, return its name.
+ *
+ * @param cat_id	Category ID
+ * @param name		Place to store pointer to new string. Caller should
+ *			free it using free().
+ * @return		EOK on success or negative error code
+ */
+int loc_category_get_name(category_id_t cat_id, char **name)
+{
+	return loc_get_name_internal(LOC_CATEGORY_GET_NAME, cat_id, name);
+}
+
+/** Get service name.
+ *
+ * Provided ID of a service, return its name.
+ *
+ * @param svc_id	Service ID
+ * @param name		Place to store pointer to new string. Caller should
+ *			free it using free().
+ * @return		EOK on success or negative error code
+ */
+int loc_service_get_name(service_id_t svc_id, char **name)
+{
+	return loc_get_name_internal(LOC_SERVICE_GET_NAME, svc_id, name);
+}
 
 int loc_namespace_get_id(const char *name, service_id_t *handle,
@@ -749,2 +866,11 @@
 	    data, count);
 }
+
+int loc_register_cat_change_cb(loc_cat_change_cb_t cb_fun)
+{
+	if (loc_callback_create() != EOK)
+		return EIO;
+
+	cat_change_cb = cb_fun;
+	return EOK;
+}
Index: uspace/lib/c/generic/private/async.h
===================================================================
--- uspace/lib/c/generic/private/async.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/generic/private/async.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -36,4 +36,5 @@
 #define LIBC_PRIVATE_ASYNC_H_
 
+#include <ipc/common.h>
 #include <adt/list.h>
 #include <fibril.h>
Index: uspace/lib/c/generic/private/io.h
===================================================================
--- uspace/lib/c/generic/private/io.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/generic/private/io.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -36,7 +36,5 @@
 #define LIBC_PRIVATE_IO_H_
 
-#include <vfs/vfs.h>
-
-extern void __stdio_init(int filc, fdi_node_t *filv[]);
+extern void __stdio_init(int);
 extern void __stdio_done(void);
 
Index: uspace/lib/c/generic/task.c
===================================================================
--- uspace/lib/c/generic/task.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/generic/task.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -46,4 +46,5 @@
 #include <libc.h>
 #include "private/ns.h"
+#include <vfs/vfs.h>
 
 task_id_t task_get_id(void)
@@ -102,21 +103,21 @@
 {
 	/* Send default files */
-	fdi_node_t *files[4];
-	fdi_node_t stdin_node;
-	fdi_node_t stdout_node;
-	fdi_node_t stderr_node;
-	
-	if ((stdin != NULL) && (fnode(stdin, &stdin_node) == EOK))
-		files[0] = &stdin_node;
+	int *files[4];
+	int fd_stdin;
+	int fd_stdout;
+	int fd_stderr;
+	
+	if ((stdin != NULL) && (fhandle(stdin, &fd_stdin) == EOK))
+		files[0] = &fd_stdin;
 	else
 		files[0] = NULL;
 	
-	if ((stdout != NULL) && (fnode(stdout, &stdout_node) == EOK))
-		files[1] = &stdout_node;
+	if ((stdout != NULL) && (fhandle(stdout, &fd_stdout) == EOK))
+		files[1] = &fd_stdout;
 	else
 		files[1] = NULL;
 	
-	if ((stderr != NULL) && (fnode(stderr, &stderr_node) == EOK))
-		files[2] = &stderr_node;
+	if ((stderr != NULL) && (fhandle(stderr, &fd_stderr) == EOK))
+		files[2] = &fd_stderr;
 	else
 		files[2] = NULL;
@@ -142,5 +143,5 @@
  */
 int task_spawnvf(task_id_t *id, const char *path, const char *const args[],
-    fdi_node_t *const files[])
+    int *const files[])
 {
 	/* Connect to a program loader. */
Index: uspace/lib/c/generic/vfs/vfs.c
===================================================================
--- uspace/lib/c/generic/vfs/vfs.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/generic/vfs/vfs.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -69,5 +69,5 @@
  *
  */
-static async_exch_t *vfs_exchange_begin(void)
+async_exch_t *vfs_exchange_begin(void)
 {
 	fibril_mutex_lock(&vfs_mutex);
@@ -87,5 +87,5 @@
  *
  */
-static void vfs_exchange_end(async_exch_t *exch)
+void vfs_exchange_end(async_exch_t *exch)
 {
 	async_exchange_end(exch);
@@ -329,23 +329,4 @@
 }
 
-int open_node(fdi_node_t *node, int oflag)
-{
-	async_exch_t *exch = vfs_exchange_begin();
-	
-	ipc_call_t answer;
-	aid_t req = async_send_4(exch, VFS_IN_OPEN_NODE, node->fs_handle,
-	    node->service_id, node->index, oflag, &answer);
-	
-	vfs_exchange_end(exch);
-
-	sysarg_t rc;
-	async_wait_for(req, &rc);
-	
-	if (rc != EOK)
-		return (int) rc;
-	
-	return (int) IPC_GET_ARG1(answer);
-}
-
 int close(int fildes)
 {
@@ -819,18 +800,4 @@
 }
 
-int fd_node(int fildes, fdi_node_t *node)
-{
-	struct stat stat;
-	int rc = fstat(fildes, &stat);
-	
-	if (rc == EOK) {
-		node->fs_handle = stat.fs_handle;
-		node->service_id = stat.service_id;
-		node->index = stat.index;
-	}
-	
-	return rc;
-}
-
 int dup2(int oldfd, int newfd)
 {
@@ -848,4 +815,19 @@
 }
 
+int fd_wait(void)
+{
+	async_exch_t *exch = vfs_exchange_begin();
+	
+	sysarg_t ret;
+	sysarg_t rc = async_req_0_1(exch, VFS_IN_WAIT_HANDLE, &ret);
+	
+	vfs_exchange_end(exch);
+	
+	if (rc == EOK)
+		return (int) ret;
+	
+	return (int) rc;
+}
+
 /** @}
  */
Index: uspace/lib/c/include/async.h
===================================================================
--- uspace/lib/c/include/async.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/include/async.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -176,5 +176,5 @@
 extern int async_wait_timeout(aid_t, sysarg_t *, suseconds_t);
 
-extern fid_t async_new_connection(sysarg_t, sysarg_t, ipc_callid_t,
+extern fid_t async_new_connection(task_id_t, sysarg_t, ipc_callid_t,
     ipc_call_t *, async_client_conn_t, void *);
 
@@ -186,4 +186,6 @@
 extern void async_set_client_data_destructor(async_client_data_dtor_t);
 extern void *async_get_client_data(void);
+extern void *async_get_client_data_by_id(task_id_t);
+extern void async_put_client_data_by_id(task_id_t);
 
 extern void async_set_client_connection(async_client_conn_t);
@@ -477,4 +479,10 @@
 extern async_sess_t *async_callback_receive_start(exch_mgmt_t, ipc_call_t *);
 
+extern int async_state_change_start(async_exch_t *, sysarg_t, sysarg_t,
+    sysarg_t, async_exch_t *);
+extern bool async_state_change_receive(ipc_callid_t *, sysarg_t *, sysarg_t *,
+    sysarg_t *);
+extern int async_state_change_finalize(ipc_callid_t, async_exch_t *);
+
 #endif
 
Index: uspace/lib/c/include/devman.h
===================================================================
--- uspace/lib/c/include/devman.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/include/devman.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -38,4 +38,5 @@
 
 #include <ipc/devman.h>
+#include <ipc/loc.h>
 #include <async.h>
 #include <bool.h>
@@ -48,4 +49,5 @@
 extern int devman_add_function(const char *, fun_type_t, match_id_list_t *,
     devman_handle_t, devman_handle_t *);
+extern int devman_remove_function(devman_handle_t);
 
 extern async_sess_t *devman_device_connect(exch_mgmt_t, devman_handle_t,
@@ -54,11 +56,14 @@
     unsigned int);
 
-extern int devman_device_get_handle(const char *, devman_handle_t *,
+extern int devman_fun_get_handle(const char *, devman_handle_t *,
     unsigned int);
-extern int devman_device_get_handle_by_class(const char *, const char *,
-    devman_handle_t *, unsigned int);
-extern int devman_get_device_path(devman_handle_t, char *, size_t);
+extern int devman_fun_get_child(devman_handle_t, devman_handle_t *);
+extern int devman_dev_get_functions(devman_handle_t, devman_handle_t **,
+    size_t *);
+extern int devman_fun_get_name(devman_handle_t, char *, size_t);
+extern int devman_fun_get_path(devman_handle_t, char *, size_t);
 
-extern int devman_add_device_to_class(devman_handle_t, const char *);
+extern int devman_add_device_to_category(devman_handle_t, const char *);
+extern int devman_fun_sid_to_handle(service_id_t, devman_handle_t *);
 
 #endif
Index: uspace/lib/c/include/event.h
===================================================================
--- uspace/lib/c/include/event.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/include/event.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -37,7 +37,10 @@
 
 #include <abi/ipc/event.h>
+#include <libarch/types.h>
 
 extern int event_subscribe(event_type_t, sysarg_t);
+extern int event_task_subscribe(event_task_type_t, sysarg_t);
 extern int event_unmask(event_type_t);
+extern int event_task_unmask(event_task_type_t);
 
 #endif
Index: uspace/lib/c/include/ipc/common.h
===================================================================
--- uspace/lib/c/include/ipc/common.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/include/ipc/common.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -39,4 +39,5 @@
 #include <abi/ipc/ipc.h>
 #include <atomic.h>
+#include <task.h>
 
 #define IPC_FLAG_BLOCKING  0x01
@@ -44,5 +45,5 @@
 typedef struct {
 	sysarg_t args[IPC_CALL_LEN];
-	sysarg_t in_task_hash;
+	task_id_t in_task_id;
 	sysarg_t in_phone_hash;
 } ipc_call_t;
Index: uspace/lib/c/include/ipc/devman.h
===================================================================
--- uspace/lib/c/include/ipc/devman.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/include/ipc/devman.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -138,6 +138,6 @@
 	DEVMAN_ADD_FUNCTION,
 	DEVMAN_ADD_MATCH_ID,
-	DEVMAN_ADD_DEVICE_TO_CLASS
-
+	DEVMAN_ADD_DEVICE_TO_CATEGORY,
+	DEVMAN_REMOVE_FUNCTION
 } driver_to_devman_t;
 
@@ -149,6 +149,9 @@
 typedef enum {
 	DEVMAN_DEVICE_GET_HANDLE = IPC_FIRST_USER_METHOD,
-	DEVMAN_DEVICE_GET_HANDLE_BY_CLASS,
-	DEVMAN_DEVICE_GET_DEVICE_PATH
+	DEVMAN_DEV_GET_FUNCTIONS,
+	DEVMAN_FUN_GET_CHILD,
+	DEVMAN_FUN_GET_NAME,
+	DEVMAN_FUN_GET_PATH,
+	DEVMAN_FUN_SID_TO_HANDLE
 } client_to_devman_t;
 
Index: uspace/lib/c/include/ipc/loc.h
===================================================================
--- uspace/lib/c/include/ipc/loc.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/include/ipc/loc.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -57,5 +57,7 @@
 	LOC_SERVICE_GET_NAME,
 	LOC_NAMESPACE_GET_ID,
+	LOC_CALLBACK_CREATE,
 	LOC_CATEGORY_GET_ID,
+	LOC_CATEGORY_GET_NAME,
 	LOC_CATEGORY_GET_SVCS,
 	LOC_ID_PROBE,
@@ -68,4 +70,8 @@
 	LOC_GET_SERVICES
 } loc_request_t;
+
+typedef enum {
+	LOC_EVENT_CAT_CHANGE = IPC_FIRST_USER_METHOD
+} loc_event_t;
 
 /** Ports provided by location service.
Index: uspace/lib/c/include/ipc/vfs.h
===================================================================
--- uspace/lib/c/include/ipc/vfs.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/include/ipc/vfs.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -62,5 +62,4 @@
 typedef enum {
 	VFS_IN_OPEN = IPC_FIRST_USER_METHOD,
-	VFS_IN_OPEN_NODE,
 	VFS_IN_READ,
 	VFS_IN_WRITE,
@@ -78,5 +77,6 @@
 	VFS_IN_RENAME,
 	VFS_IN_STAT,
-	VFS_IN_DUP
+	VFS_IN_DUP,
+	VFS_IN_WAIT_HANDLE,
 } vfs_in_request_t;
 
Index: uspace/lib/c/include/loader/loader.h
===================================================================
--- uspace/lib/c/include/loader/loader.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/include/loader/loader.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -38,5 +38,4 @@
 
 #include <task.h>
-#include <vfs/vfs.h>
 
 /** Forward declararion */
@@ -50,5 +49,5 @@
 extern int loader_set_pathname(loader_t *, const char *);
 extern int loader_set_args(loader_t *, const char *const[]);
-extern int loader_set_files(loader_t *, fdi_node_t *const[]);
+extern int loader_set_files(loader_t *, int *const[]);
 extern int loader_load_program(loader_t *);
 extern int loader_run(loader_t *);
Index: uspace/lib/c/include/loader/pcb.h
===================================================================
--- uspace/lib/c/include/loader/pcb.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/include/loader/pcb.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -38,5 +38,4 @@
 
 #include <sys/types.h>
-#include <vfs/vfs.h>
 
 typedef void (*entry_point_t)(void);
@@ -62,7 +61,5 @@
 	
 	/** Number of preset files. */
-	int filc;
-	/** Preset files. */
-	fdi_node_t **filv;
+	unsigned int filc;
 	
 	/*
Index: uspace/lib/c/include/loc.h
===================================================================
--- uspace/lib/c/include/loc.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/include/loc.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -40,4 +40,6 @@
 #include <bool.h>
 
+typedef void (*loc_cat_change_cb_t)(void);
+
 extern async_exch_t *loc_exchange_begin_blocking(loc_interface_t);
 extern async_exch_t *loc_exchange_begin(loc_interface_t);
@@ -48,4 +50,5 @@
 extern int loc_service_register_with_iface(const char *, service_id_t *,
     sysarg_t);
+extern int loc_service_unregister(service_id_t);
 extern int loc_service_add_to_cat(service_id_t, category_id_t);
 
@@ -73,4 +76,5 @@
 extern size_t loc_get_services(service_id_t, loc_sdesc_t **);
 extern int loc_get_categories(category_id_t **, size_t *);
+extern int loc_register_cat_change_cb(loc_cat_change_cb_t);
 
 
Index: uspace/lib/c/include/task.h
===================================================================
--- uspace/lib/c/include/task.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/include/task.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -37,5 +37,4 @@
 
 #include <sys/types.h>
-#include <vfs/vfs.h>
 
 typedef uint64_t task_id_t;
@@ -53,5 +52,5 @@
 extern int task_spawnv(task_id_t *, const char *path, const char *const []);
 extern int task_spawnvf(task_id_t *, const char *path, const char *const [],
-    fdi_node_t *const []);
+    int *const []);
 extern int task_spawnl(task_id_t *, const char *path, ...);
 
Index: uspace/lib/c/include/vfs/vfs.h
===================================================================
--- uspace/lib/c/include/vfs/vfs.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/c/include/vfs/vfs.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -40,16 +40,9 @@
 #include <ipc/loc.h>
 #include <stdio.h>
+#include <async.h>
 
-/** Libc version of the VFS triplet.
- *
- * Unique identification of a file system node
- * within a file system instance.
- *
- */
-typedef struct {
-	fs_handle_t fs_handle;
-	service_id_t service_id;
-	fs_index_t index;
-} fdi_node_t;
+enum vfs_change_state_type {
+	VFS_PASS_HANDLE
+};
 
 extern char *absolutize(const char *, size_t *);
@@ -59,9 +52,10 @@
 extern int unmount(const char *);
 
-extern int open_node(fdi_node_t *, int);
-extern int fd_node(int, fdi_node_t *);
+extern int fhandle(FILE *, int *);
 
-extern FILE *fopen_node(fdi_node_t *, const char *);
-extern int fnode(FILE *, fdi_node_t *);
+extern int fd_wait(void);
+
+extern async_exch_t *vfs_exchange_begin(void);
+extern void vfs_exchange_end(async_exch_t *);
 
 #endif
Index: uspace/lib/drv/generic/driver.c
===================================================================
--- uspace/lib/drv/generic/driver.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/drv/generic/driver.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -428,6 +428,16 @@
 static void driver_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
+	sysarg_t conn_type;
+
+	if (iid == 0) {
+		/* Callback connection from devman */
+		/* XXX Use separate handler for this type of connection */
+		conn_type = DRIVER_DEVMAN;
+	} else {
+		conn_type = IPC_GET_ARG1(*icall);
+	}
+
 	/* Select interface */
-	switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
+	switch (conn_type) {
 	case DRIVER_DEVMAN:
 		/* Handle request from device manager */
@@ -582,4 +592,5 @@
 int ddf_fun_bind(ddf_fun_t *fun)
 {
+	assert(fun->bound == false);
 	assert(fun->name != NULL);
 	
@@ -598,4 +609,29 @@
 }
 
+/** Unbind a function node.
+ *
+ * Unbind the specified function from the system. This effectively makes
+ * the function invisible to the system.
+ *
+ * @param fun		Function to bind
+ * @return		EOK on success or negative error code
+ */
+int ddf_fun_unbind(ddf_fun_t *fun)
+{
+	int res;
+	
+	assert(fun->bound == true);
+	
+	add_to_functions_list(fun);
+	res = devman_remove_function(fun->handle);
+	if (res != EOK)
+		return res;
+
+	remove_from_functions_list(fun);
+	
+	fun->bound = false;
+	return EOK;
+}
+
 /** Add single match ID to inner function.
  *
@@ -635,14 +671,14 @@
 }
 
-/** Add exposed function to class.
+/** Add exposed function to category.
  *
  * Must only be called when the function is bound.
  */
-int ddf_fun_add_to_class(ddf_fun_t *fun, const char *class_name)
+int ddf_fun_add_to_category(ddf_fun_t *fun, const char *cat_name)
 {
 	assert(fun->bound == true);
 	assert(fun->ftype == fun_exposed);
 	
-	return devman_add_device_to_class(fun->handle, class_name);
+	return devman_add_device_to_category(fun->handle, cat_name);
 }
 
Index: uspace/lib/drv/include/ddf/driver.h
===================================================================
--- uspace/lib/drv/include/ddf/driver.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/drv/include/ddf/driver.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -149,7 +149,8 @@
 extern void ddf_fun_destroy(ddf_fun_t *);
 extern int ddf_fun_bind(ddf_fun_t *);
+extern int ddf_fun_unbind(ddf_fun_t *);
 extern int ddf_fun_add_match_id(ddf_fun_t *, const char *, int);
 
-extern int ddf_fun_add_to_class(ddf_fun_t *, const char *);
+extern int ddf_fun_add_to_category(ddf_fun_t *, const char *);
 
 #endif
Index: uspace/lib/posix/ctype.c
===================================================================
--- uspace/lib/posix/ctype.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/ctype.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -37,6 +37,4 @@
 
 #include "ctype.h"
-
-// TODO: propose for inclusion in libc
 
 /**
Index: uspace/lib/posix/errno.h
===================================================================
--- uspace/lib/posix/errno.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/errno.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -57,5 +57,5 @@
  * redefinition for such error codes.
  *
- * FIXME: maybe all HOS error codes should be redefined
+ * XXX: maybe all HOS error codes should be redefined
  *
  * NOTE: This redefinition is slightly POSIX incompatible, since the
Index: uspace/lib/posix/fcntl.c
===================================================================
--- uspace/lib/posix/fcntl.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/fcntl.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -52,39 +52,14 @@
 int posix_fcntl(int fd, int cmd, ...)
 {
-	int rc;
 	int flags;
 
 	switch (cmd) {
 	case F_DUPFD:
-	case F_DUPFD_CLOEXEC: /* FD_CLOEXEC is not supported. */
-		/* VFS does not provide means to express constraints on the new
-		 * file descriptor so the third argument is ignored. */
-
-		/* Retrieve node triplet corresponding to the file descriptor. */
-		/* Empty statement after label. */;
-		fdi_node_t node;
-		rc = fd_node(fd, &node);
-		if (rc != EOK) {
-			errno = -rc;
-			return -1;
-		}
-
-		/* Reopen the node so the fresh file descriptor is generated. */
-		int newfd = open_node(&node, 0);
-		if (newfd < 0) {
-			errno = -newfd;
-			return -1;
-		}
-
-		/* Associate the newly generated descriptor to the file description
-		 * of the old file descriptor. Just reopened node will be automatically
-		 * closed. */
-		rc = dup2(fd, newfd);
-		if (rc != EOK) {
-			errno = -rc;
-			return -1;
-		}
-
-		return newfd;
+	case F_DUPFD_CLOEXEC:
+		/* VFS currently does not provide the functionality to duplicate
+		 * opened file descriptor. */
+		// FIXME: implement this once dup() is available
+		errno = ENOTSUP;
+		return -1;
 	case F_GETFD:
 		/* FD_CLOEXEC is not supported. There are no other flags. */
Index: uspace/lib/posix/fnmatch.c
===================================================================
--- uspace/lib/posix/fnmatch.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/fnmatch.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -66,7 +66,8 @@
 #define COLL_ELM_INVALID -1
 
-/** Get collating element matching a string.
- *
- * @param str
+/**
+ * Get collating element matching a string.
+ *
+ * @param str String representation of the element.
  * @return Matching collating element or COLL_ELM_INVALID.
  */
@@ -79,8 +80,9 @@
 }
 
-/** Get collating element matching a single character.
- *
- * @param c
- * @return
+/**
+ * Get collating element matching a single character.
+ *
+ * @param c Character representation of the element.
+ * @return Matching collating element.
  */
 static coll_elm_t _coll_elm_char(int c)
@@ -89,8 +91,9 @@
 }
 
-/** Match collating element with a beginning of a string.
- *
- * @param elm
- * @param str
+/**
+ * Match collating element with a beginning of a string.
+ *
+ * @param elm Collating element to match.
+ * @param str String which beginning should match the element.
  * @return 0 if the element doesn't match, or the number of characters matched.
  */
@@ -100,6 +103,7 @@
 }
 
-/** Checks whether a string begins with a collating element in the given range.
- *  Ordering depends on the locale (if locales are supported).
+/**
+ * Checks whether a string begins with a collating element in the given range.
+ * Ordering depends on the locale (if locales are supported).
  *
  * @param first First element of the range.
@@ -114,5 +118,6 @@
 }
 
-/** Read a string delimited by [? and ?].
+/**
+ * Read a string delimited by [? and ?].
  *
  * @param pattern Pointer to the string to read from. Its position is moved
@@ -189,9 +194,10 @@
 };
 
-/** Compare function for binary search in the _char_classes array. 
+/**
+ * Compare function for binary search in the _char_classes array.
  * 
- * @param key
- * @param elem
- * @return
+ * @param key Key of the searched element.
+ * @param elem Element of _char_classes array.
+ * @return Ordering indicator (-1 less than, 0 equal, 1 greater than).
  */
 static int _class_compare(const void *key, const void *elem)
@@ -201,5 +207,6 @@
 }
 
-/** Returns whether the given character belongs to the specified character class.
+/**
+ * Returns whether the given character belongs to the specified character class.
  * 
  * @param cname Name of the character class.
@@ -223,6 +230,7 @@
 }
 
-/** Tries to parse an initial part of the pattern as a character class pattern,
- *  and if successful, matches the beginning of the given string against the class.
+/**
+ * Tries to parse an initial part of the pattern as a character class pattern,
+ * and if successful, matches the beginning of the given string against the class.
  * 
  * @param pattern Pointer to the pattern to match. Must begin with a class
@@ -248,6 +256,7 @@
 /************** END CHARACTER CLASSES ****************/
 
-/** Reads the next collating element in the pattern, taking into account
- *  locale (if supported) and flags (see fnmatch()).
+/**
+ * Reads the next collating element in the pattern, taking into account
+ * locale (if supported) and flags (see fnmatch()).
  * 
  * @param pattern Pattern.
@@ -299,6 +308,7 @@
 }
 
-/** Matches the beginning of the given string against a bracket expression
- *  the pattern begins with.
+/**
+ * Matches the beginning of the given string against a bracket expression
+ * the pattern begins with.
  * 
  * @param pattern Pointer to the beginning of a bracket expression in a pattern.
@@ -394,6 +404,7 @@
 }
 
-/** Matches a portion of the pattern containing no asterisks (*) against
- *  the given string.
+/**
+ * Matches a portion of the pattern containing no asterisks (*) against
+ * the given string.
  * 
  * @param pattern Pointer to the unmatched portion of the pattern.
@@ -497,5 +508,6 @@
 }
 
-/** Match string against a pattern.
+/**
+ * Match string against a pattern.
  * 
  * @param pattern Pattern.
@@ -559,5 +571,6 @@
 }
 
-/** Transform the entire string to lowercase.
+/**
+ * Transform the entire string to lowercase.
  * 
  * @param s Input string.
@@ -578,8 +591,9 @@
  * Filename pattern matching.
  *
- * @param pattern
- * @param string
- * @param flags
- * @return
+ * @param pattern Pattern to match the string against.
+ * @param string Matched string.
+ * @param flags Flags altering the matching of special characters
+ *     (mainly for dot and slash).
+ * @return Zero if the string matches the pattern, FNM_NOMATCH otherwise.
  */
 int posix_fnmatch(const char *pattern, const char *string, int flags)
@@ -610,5 +624,5 @@
 }
 
-// FIXME: put the testcases somewhere else
+// FIXME: put the testcases to the app/tester after fnmatch is included into libc
 
 #if 0
Index: uspace/lib/posix/locale.c
===================================================================
--- uspace/lib/posix/locale.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/locale.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -79,5 +79,6 @@
 };
 
-/** Set program locale.
+/**
+ * Set program locale.
  * 
  * @param category What category to set.
@@ -95,5 +96,6 @@
 }
 
-/** Return locale-specific information.
+/**
+ * Return locale-specific information.
  * 
  * @return Information about the current locale.
@@ -105,5 +107,6 @@
 }
 
-/** Duplicate locale object.
+/**
+ * Duplicate locale object.
  * 
  * @param locobj Object to duplicate.
@@ -125,5 +128,6 @@
 }
 
-/** Free locale object.
+/**
+ * Free locale object.
  * 
  * @param locobj Object to free.
@@ -136,5 +140,6 @@
 }
 
-/** Create or modify a locale object.
+/**
+ * Create or modify a locale object.
  * 
  * @param category_mask Mask of categories to be set or modified.
@@ -163,5 +168,6 @@
 }
 
-/** Set locale for the current thread.
+/**
+ * Set locale for the current thread.
  * 
  * @param newloc Locale to use.
Index: uspace/lib/posix/pwd.c
===================================================================
--- uspace/lib/posix/pwd.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/pwd.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -51,8 +51,11 @@
 
 /**
+ * Retrieve next broken-down entry from the user database.
+ *
  * Since HelenOS doesn't have user accounts, this always returns
  * the same made-up entry.
  *
- * @return
+ * @return Next user database entry or NULL if not possible. Since HelenOS
+ *     doesn't have user accounts, this always returns the same made-up entry.
  */
 struct posix_passwd *posix_getpwent(void)
@@ -67,5 +70,5 @@
 
 /**
- * "Rewind the user list".
+ * Rewind the user list.
  */
 void posix_setpwent(void)
@@ -86,5 +89,5 @@
  *
  * @param name Name of the entry.
- * @return
+ * @return Either found entry or NULL if no such entry exists.
  */
 struct posix_passwd *posix_getpwnam(const char *name)
@@ -103,9 +106,10 @@
  *
  * @param name Name of the entry.
- * @param pwd
- * @param buffer
- * @param bufsize
- * @param result
- * @return
+ * @param pwd Original structure.
+ * @param buffer Buffer for the strings referenced from the result structure.
+ * @param bufsize Length of the buffer.
+ * @param result Where to store updated structure.
+ * @return Zero on success (either found or not found, but without an error),
+ *     non-zero error number if error occured.
  */
 int posix_getpwnam_r(const char *name, struct posix_passwd *pwd,
@@ -129,5 +133,5 @@
  *
  * @param uid UID of the entry.
- * @return
+ * @return Either found entry or NULL if no such entry exists.
  */
 struct posix_passwd *posix_getpwuid(posix_uid_t uid)
@@ -144,9 +148,10 @@
  *
  * @param uid UID of the entry.
- * @param pwd
- * @param buffer
- * @param bufsize
- * @param result
- * @return
+ * @param pwd Original structure.
+ * @param buffer Buffer for the strings referenced from the result structure.
+ * @param bufsize Length of the buffer.
+ * @param result Where to store updated structure.
+ * @return Zero on success (either found or not found, but without an error),
+ *     non-zero error number if error occured.
  */
 int posix_getpwuid_r(posix_uid_t uid, struct posix_passwd *pwd,
Index: uspace/lib/posix/signal.c
===================================================================
--- uspace/lib/posix/signal.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/signal.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -136,7 +136,8 @@
 }
 
-/** Just an empty function to get an unique pointer value for comparison.
- *
- * @param signo
+/**
+ * Just an empty function to get an unique pointer value for comparison.
+ *
+ * @param signo Signal number.
  */
 void __posix_hold_signal_handler(int signo)
@@ -145,7 +146,8 @@
 }
 
-/** Empty function to be used as ignoring handler.
- * 
- * @param signo
+/**
+ * Empty function to be used as ignoring handler.
+ * 
+ * @param signo Signal number.
  */
 void __posix_ignore_signal_handler(int signo)
@@ -154,5 +156,6 @@
 }
 
-/** Clear the signal set.
+/**
+ * Clear the signal set.
  * 
  * @param set Pointer to the signal set.
@@ -167,5 +170,6 @@
 }
 
-/** Fill the signal set (i.e. add all signals).
+/**
+ * Fill the signal set (i.e. add all signals).
  * 
  * @param set Pointer to the signal set.
@@ -180,5 +184,6 @@
 }
 
-/** Add a signal to the set.
+/**
+ * Add a signal to the set.
  * 
  * @param set Pointer to the signal set.
@@ -194,5 +199,6 @@
 }
 
-/** Delete a signal from the set.
+/**
+ * Delete a signal from the set.
  * 
  * @param set Pointer to the signal set.
@@ -208,5 +214,6 @@
 }
 
-/** Inclusion test for a signal set.
+/**
+ * Inclusion test for a signal set.
  * 
  * @param set Pointer to the signal set.
@@ -221,7 +228,8 @@
 }
 
-/** Unsafe variant of the sigaction() function.
- *  Doesn't do any checking of its arguments and
- *  does not deal with thread-safety.
+/**
+ * Unsafe variant of the sigaction() function.
+ * Doesn't do any checking of its arguments and
+ * does not deal with thread-safety.
  * 
  * @param sig
@@ -243,5 +251,6 @@
 }
 
-/** Sets a new action for the given signal number.
+/**
+ * Sets a new action for the given signal number.
  * 
  * @param sig Signal number to set action for.
@@ -276,5 +285,6 @@
 }
 
-/** Sets a new handler for the given signal number.
+/**
+ * Sets a new handler for the given signal number.
  * 
  * @param sig Signal number to set handler for.
@@ -304,5 +314,6 @@
 } signal_queue_item;
 
-/** Queue blocked signal.
+/**
+ * Queue blocked signal.
  *
  * @param signo Signal number.
@@ -322,5 +333,6 @@
 
 
-/** Executes an action associated with the given signal.
+/**
+ * Executes an action associated with the given signal.
  *
  * @param signo Signal number.
@@ -366,5 +378,6 @@
 }
 
-/** Raise all unblocked previously queued signals.
+/**
+ * Raise all unblocked previously queued signals.
  */
 static void _dequeue_unblocked_signals()
@@ -390,5 +403,6 @@
 }
 
-/** Raise a signal for the calling process.
+/**
+ * Raise a signal for the calling process.
  * 
  * @param sig Signal number.
@@ -409,5 +423,6 @@
 }
 
-/** Raises a signal for a selected process.
+/**
+ * Raises a signal for a selected process.
  * 
  * @param pid PID of the process for which the signal shall be raised.
@@ -446,6 +461,7 @@
 }
 
-/** Send a signal to a process group. Always fails at the moment because of
- *  lack of this functionality in HelenOS.
+/**
+ * Send a signal to a process group. Always fails at the moment because of
+ * lack of this functionality in HelenOS.
  * 
  * @param pid PID of the process group.
@@ -459,5 +475,6 @@
 }
 
-/** Outputs information about the signal to the standard error stream.
+/**
+ * Outputs information about the signal to the standard error stream.
  * 
  * @param pinfo SigInfo struct to write.
@@ -471,5 +488,6 @@
 }
 
-/** Outputs information about the signal to the standard error stream.
+/**
+ * Outputs information about the signal to the standard error stream.
  * 
  * @param signum Signal number.
@@ -486,5 +504,6 @@
 }
 
-/** Manipulate the signal mask of the calling thread.
+/**
+ * Manipulate the signal mask of the calling thread.
  * 
  * @param how What to do with the mask.
@@ -525,5 +544,6 @@
 }
 
-/** Manipulate the signal mask of the process.
+/**
+ * Manipulate the signal mask of the process.
  * 
  * @param how What to do with the mask.
Index: uspace/lib/posix/stdbool.h
===================================================================
--- uspace/lib/posix/stdbool.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/stdbool.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -36,7 +36,4 @@
 #define POSIX_STDBOOL_H_
 
-// TODO: propose for inclusion in libc and drop bool.h,
-//       it's a very silly incompatibility with standard C
-
 #ifdef LIBC_BOOL_H_
 	#error "You can't include bool.h and stdbool.h at the same time."
Index: uspace/lib/posix/stdint.h
===================================================================
--- uspace/lib/posix/stdint.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/stdint.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -103,12 +103,7 @@
 typedef uint64_t posix_uintmax_t;
 
-// FIXME: should be integrated into build process similarly to uintptr_t
-typedef ssize_t posix_intptr_t;
-
 #ifndef LIBPOSIX_INTERNAL
 	#define intmax_t posix_intmax_t
 	#define uintmax_t posix_uintmax_t
-
-	#define intptr_t posix_intptr_t
 #endif
 
Index: uspace/lib/posix/stdio.c
===================================================================
--- uspace/lib/posix/stdio.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/stdio.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -257,40 +257,16 @@
 	assert(stream != NULL);
 	
-	/* Retrieve the node. */
-	struct stat st;
-	int rc;
-	
 	if (filename == NULL) {
-		rc = fstat(stream->fd, &st);
-	} else {
-		rc = stat(filename, &st);
-		if (-rc == ENOENT) {
-			/* file does not exist, create new file */
-			FILE* tmp = fopen(filename, mode);
-			if (tmp != NULL) {
-				fclose(tmp);
-				/* try again */
-				rc = stat(filename, &st);
-			}
-		}
-	}
-	
-	if (rc != EOK) {
-		fclose(stream);
-		errno = -rc;
-		return NULL;
-	}
-	
-	fdi_node_t node = {
-		.fs_handle = st.fs_handle,
-		.service_id = st.service_id,
-		.index = st.index
-	};
+		/* POSIX allows this to be imlementation-defined. HelenOS currently
+		 * does not support changing the mode. */
+		// FIXME: handle mode change once it is supported
+		return stream;
+	}
 	
 	/* Open a new stream. */
-	FILE* new = fopen_node(&node, mode);
+	FILE* new = fopen(filename, mode);
 	if (new == NULL) {
 		fclose(stream);
-		/* fopen_node() sets errno. */
+		/* errno was set by fopen() */
 		return NULL;
 	}
Index: uspace/lib/posix/stdio.h
===================================================================
--- uspace/lib/posix/stdio.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/stdio.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -140,6 +140,4 @@
 
 	#define freopen posix_freopen
-	#define fmemopen posix_fmemopen
-	#define open_memstream posix_open_memstream
 
 	#define perror posix_perror
Index: uspace/lib/posix/stdio/scanf.c
===================================================================
--- uspace/lib/posix/stdio/scanf.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/stdio/scanf.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -707,5 +707,5 @@
 							break;
 						case LMOD_t: ;
-							// FIXME: What is unsigned counterpart of the ptrdiff_t?
+							// XXX: What is unsigned counterpart of the ptrdiff_t?
 							size_t *pt = va_arg(arg, size_t *);
 							*pt = (size_t) ures;
@@ -1221,5 +1221,5 @@
 }
 
-// FIXME: put the testcases somewhere else
+// FIXME: put the testcases to the app/tester after scanf is included into libc
 
 #if 0
Index: uspace/lib/posix/string.c
===================================================================
--- uspace/lib/posix/string.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/string.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -48,22 +48,4 @@
 
 /**
- * Decides whether s2 is a prefix of s1.
- *
- * @param s1 String in which to look for a prefix.
- * @param s2 Prefix string to look for.
- * @return True if s2 is a prefix of s1, false otherwise.
- */
-static bool begins_with(const char *s1, const char *s2)
-{
-	while (*s1 == *s2 && *s2 != '\0') {
-		s1++;
-		s2++;
-	}
-	
-	/* true if the end was reached */
-	return *s2 == '\0';
-}
-
-/**
  * The same as strpbrk, except it returns pointer to the nul terminator
  * if no occurence is found.
@@ -471,5 +453,5 @@
 
 /**
- * Find a substring.
+ * Find a substring. Uses Knuth-Morris-Pratt algorithm.
  *
  * @param s1 String in which to look for a substring.
@@ -478,22 +460,48 @@
  *     not found.
  */
-char *posix_strstr(const char *s1, const char *s2)
-{
-	assert(s1 != NULL);
-	assert(s2 != NULL);
-
-	/* special case - needle is an empty string */
-	if (*s2 == '\0') {
-		return (char *) s1;
-	}
-
-	// TODO: use faster algorithm
-	/* check for prefix from every position - quadratic complexity */
-	while (*s1 != '\0') {
-		if (begins_with(s1, s2)) {
-			return (char *) s1;
-		}
+char *posix_strstr(const char *haystack, const char *needle)
+{
+	assert(haystack != NULL);
+	assert(needle != NULL);
+	
+	/* Special case - needle is an empty string. */
+	if (needle[0] == '\0') {
+		return (char *) haystack;
+	}
+	
+	/* Preprocess needle. */
+	size_t nlen = posix_strlen(needle);
+	size_t prefix_table[nlen + 1];
+	
+	{
+		size_t i = 0;
+		ssize_t j = -1;
 		
-		s1++;
+		prefix_table[i] = j;
+		
+		while (i < nlen) {
+			while (j >= 0 && needle[i] != needle[j]) {
+				j = prefix_table[j];
+			}
+			i++; j++;
+			prefix_table[i] = j;
+		}
+	}
+	
+	/* Search needle using the precomputed table. */
+	size_t npos = 0;
+	
+	for (size_t hpos = 0; haystack[hpos] != '\0'; ++hpos) {
+		while (npos != 0 && haystack[hpos] != needle[npos]) {
+			npos = prefix_table[npos];
+		}
+		
+		if (haystack[hpos] == needle[npos]) {
+			npos++;
+			
+			if (npos == nlen) {
+				return (char *) (haystack + hpos - nlen + 1);
+			}
+		}
 	}
 	
Index: uspace/lib/posix/string.h
===================================================================
--- uspace/lib/posix/string.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/string.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -51,13 +51,10 @@
  * void *memmove(void *, const void *, size_t);
  *
- * unimplemented due to missing locales
+ * TODO: not implemented due to missing locale support
  *
  * int      strcoll_l(const char *, const char *, locale_t);
  * char    *strerror_l(int, locale_t);
  * size_t   strxfrm_l(char *restrict, const char *restrict, size_t, locale_t);
- *
  */
-
-// TODO: provide *_l once there is locale.h
 
 #ifndef NULL
@@ -89,5 +86,5 @@
 extern size_t posix_strcspn(const char *s1, const char *s2);
 extern size_t posix_strspn(const char *s1, const char *s2);
-extern char *posix_strstr(const char *s1, const char *s2);
+extern char *posix_strstr(const char *haystack, const char *needle);
 
 /* Collation Functions */
Index: uspace/lib/posix/strings.h
===================================================================
--- uspace/lib/posix/strings.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/strings.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -48,5 +48,5 @@
 #endif
 
-/* TODO: not implemented due to missing locale.h
+/* TODO: not implemented due to missing locale support
  *
  * int strcasecmp_l(const char *, const char *, locale_t);
Index: uspace/lib/posix/time.c
===================================================================
--- uspace/lib/posix/time.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/time.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -51,5 +51,4 @@
 #include "libc/sys/time.h"
 
-// TODO: documentation
 // TODO: test everything in this file
 
@@ -72,5 +71,6 @@
 #define SECS_PER_DAY (SECS_PER_HOUR * HOURS_PER_DAY)
 
-/** Checks whether the year is a leap year.
+/**
+ * Checks whether the year is a leap year.
  *
  * @param year Year since 1900 (e.g. for 1970, the value is 70).
@@ -90,6 +90,7 @@
 }
 
-/** Returns how many days there are in the given month of the given year.
- *  Note that year is only taken into account if month is February.
+/**
+ * Returns how many days there are in the given month of the given year.
+ * Note that year is only taken into account if month is February.
  *
  * @param year Year since 1900 (can be negative).
@@ -113,6 +114,7 @@
 }
 
-/** For specified year, month and day of month, returns which day of that year
- *  it is.
+/**
+ * For specified year, month and day of month, returns which day of that year
+ * it is.
  *
  * For example, given date 2011-01-03, the corresponding expression is:
@@ -134,10 +136,11 @@
 }
 
-/** Integer division that rounds to negative infinity.
- *  Used by some functions in this file.
- *
- * @param op1
- * @param op2
- * @return
+/**
+ * Integer division that rounds to negative infinity.
+ * Used by some functions in this file.
+ *
+ * @param op1 Divident.
+ * @param op2 Divisor.
+ * @return Rounded quotient.
  */
 static time_t _floor_div(time_t op1, time_t op2)
@@ -150,10 +153,11 @@
 }
 
-/** Modulo that rounds to negative infinity.
- *  Used by some functions in this file.
- *
- * @param op1
- * @param op2
- * @return
+/**
+ * Modulo that rounds to negative infinity.
+ * Used by some functions in this file.
+ *
+ * @param op1 Divident.
+ * @param op2 Divisor.
+ * @return Remainder.
  */
 static time_t _floor_mod(time_t op1, time_t op2)
@@ -174,6 +178,7 @@
 }
 
-/** Number of days since the Epoch.
- *  Epoch is 1970-01-01, which is also equal to day 0.
+/**
+ * Number of days since the Epoch.
+ * Epoch is 1970-01-01, which is also equal to day 0.
  *
  * @param year Year (year 1900 = 0, may be negative).
@@ -189,5 +194,6 @@
 }
 
-/** Seconds since the Epoch. see also _days_since_epoch().
+/**
+ * Seconds since the Epoch. see also _days_since_epoch().
  * 
  * @param tm Normalized broken-down time.
@@ -201,5 +207,6 @@
 }
 
-/** Which day of week the specified date is.
+/**
+ * Which day of week the specified date is.
  * 
  * @param year Year (year 1900 = 0).
@@ -214,6 +221,7 @@
 }
 
-/** Normalizes the broken-down time and optionally adds specified amount of
- *  seconds.
+/**
+ * Normalizes the broken-down time and optionally adds specified amount of
+ * seconds.
  * 
  * @param tm Broken-down time to normalize.
@@ -297,6 +305,7 @@
 }
 
-/** Which day the week-based year starts on, relative to the first calendar day.
- *  E.g. if the year starts on December 31st, the return value is -1.
+/**
+ * Which day the week-based year starts on, relative to the first calendar day.
+ * E.g. if the year starts on December 31st, the return value is -1.
  *
  * @param Year since 1900.
@@ -309,5 +318,6 @@
 }
 
-/** Returns week-based year of the specified time.
+/**
+ * Returns week-based year of the specified time.
  *
  * @param tm Normalized broken-down time.
@@ -329,7 +339,8 @@
 }
 
-/** Week number of the year, assuming weeks start on sunday.
- *  The first Sunday of January is the first day of week 1;
- *  days in the new year before this are in week 0.
+/**
+ * Week number of the year, assuming weeks start on sunday.
+ * The first Sunday of January is the first day of week 1;
+ * days in the new year before this are in week 0.
  *
  * @param tm Normalized broken-down time.
@@ -342,9 +353,10 @@
 }
 
-/** Week number of the year, assuming weeks start on monday.
- *  If the week containing January 1st has four or more days in the new year,
- *  then it is considered week 1. Otherwise, it is the last week of the previous
- *  year, and the next week is week 1. Both January 4th and the first Thursday
- *  of January are always in week 1.
+/**
+ * Week number of the year, assuming weeks start on monday.
+ * If the week containing January 1st has four or more days in the new year,
+ * then it is considered week 1. Otherwise, it is the last week of the previous
+ * year, and the next week is week 1. Both January 4th and the first Thursday
+ * of January are always in week 1.
  *
  * @param tm Normalized broken-down time.
@@ -366,7 +378,8 @@
 }
 
-/** Week number of the year, assuming weeks start on monday.
- *  The first Monday of January is the first day of week 1;
- *  days in the new year before this are in week 0. 
+/**
+ * Week number of the year, assuming weeks start on monday.
+ * The first Monday of January is the first day of week 1;
+ * days in the new year before this are in week 0. 
  *
  * @param tm Normalized broken-down time.
@@ -385,6 +398,6 @@
 char *posix_tzname[2];
 
-/** Set timezone conversion information.
- * 
+/**
+ * Set timezone conversion information.
  */
 void posix_tzset(void)
@@ -397,8 +410,9 @@
 }
 
-/** Calculate the difference between two times, in seconds.
- * 
- * @param time1
- * @param time0
+/**
+ * Calculate the difference between two times, in seconds.
+ * 
+ * @param time1 First time.
+ * @param time0 Second time.
  * @return Time in seconds.
  */
@@ -408,10 +422,11 @@
 }
 
-/** This function first normalizes the provided broken-down time
- *  (moves all values to their proper bounds) and then tries to
- *  calculate the appropriate time_t representation.
+/**
+ * This function first normalizes the provided broken-down time
+ * (moves all values to their proper bounds) and then tries to
+ * calculate the appropriate time_t representation.
  *
  * @param tm Broken-down time.
- * @return time_t representation of the time, undefined value on overflow
+ * @return time_t representation of the time, undefined value on overflow.
  */
 time_t posix_mktime(struct posix_tm *tm)
@@ -424,5 +439,6 @@
 }
 
-/** Converts a time value to a broken-down UTC time.
+/**
+ * Converts a time value to a broken-down UTC time.
  *
  * @param timer Time to convert.
@@ -437,5 +453,6 @@
 }
 
-/** Converts a time value to a broken-down UTC time.
+/**
+ * Converts a time value to a broken-down UTC time.
  * 
  * @param timer Time to convert.
@@ -465,5 +482,6 @@
 }
 
-/** Converts a time value to a broken-down local time.
+/**
+ * Converts a time value to a broken-down local time.
  *
  * @param timer Time to convert.
@@ -476,5 +494,6 @@
 }
 
-/** Converts a time value to a broken-down local time.
+/**
+ * Converts a time value to a broken-down local time.
  * 
  * @param timer Time to convert.
@@ -490,6 +509,7 @@
 }
 
-/** Converts broken-down time to a string in format
- *  "Sun Jan 1 00:00:00 1970\n". (Obsolete)
+/**
+ * Converts broken-down time to a string in format
+ * "Sun Jan 1 00:00:00 1970\n". (Obsolete)
  *
  * @param timeptr Broken-down time structure.
@@ -502,6 +522,7 @@
 }
 
-/** Converts broken-down time to a string in format
- *  "Sun Jan 1 00:00:00 1970\n". (Obsolete)
+/**
+ * Converts broken-down time to a string in format
+ * "Sun Jan 1 00:00:00 1970\n". (Obsolete)
  *
  * @param timeptr Broken-down time structure.
@@ -534,5 +555,6 @@
 }
 
-/** Equivalent to asctime(localtime(clock)).
+/**
+ * Equivalent to asctime(localtime(clock)).
  * 
  * @param timer Time to convert.
@@ -548,5 +570,6 @@
 }
 
-/** Reentrant variant of ctime().
+/**
+ * Reentrant variant of ctime().
  * 
  * @param timer Time to convert.
@@ -564,6 +587,7 @@
 }
 
-/** Convert time and date to a string, based on a specified format and
- *  current locale.
+/**
+ * Convert time and date to a string, based on a specified format and
+ * current locale.
  * 
  * @param s Buffer to write string to.
@@ -755,5 +779,6 @@
 }
 
-/** Get clock resolution. Only CLOCK_REALTIME is supported.
+/**
+ * Get clock resolution. Only CLOCK_REALTIME is supported.
  *
  * @param clock_id Clock ID.
@@ -776,9 +801,10 @@
 }
 
-/** Get time. Only CLOCK_REALTIME is supported.
+/**
+ * Get time. Only CLOCK_REALTIME is supported.
  * 
  * @param clock_id ID of the clock to query.
  * @param tp Pointer to the variable where the time is to be written.
- * @return
+ * @return 0 on success, -1 with errno on failure.
  */
 int posix_clock_gettime(posix_clockid_t clock_id, struct posix_timespec *tp)
@@ -800,6 +826,7 @@
 }
 
-/** Set time on a specified clock. As HelenOS doesn't support this yet,
- *  this function always fails.
+/**
+ * Set time on a specified clock. As HelenOS doesn't support this yet,
+ * this function always fails.
  * 
  * @param clock_id ID of the clock to set.
@@ -825,5 +852,6 @@
 }
 
-/** Sleep on a specified clock.
+/**
+ * Sleep on a specified clock.
  * 
  * @param clock_id ID of the clock to sleep on (only CLOCK_REALTIME supported).
@@ -855,5 +883,6 @@
 }
 
-/** Get CPU time used since the process invocation.
+/**
+ * Get CPU time used since the process invocation.
  *
  * @return Consumed CPU cycles by this process or -1 if not available.
Index: uspace/lib/posix/time.h
===================================================================
--- uspace/lib/posix/time.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/posix/time.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -162,11 +162,5 @@
 	#define clock_settime posix_clock_settime
 	#define clock_nanosleep posix_clock_nanosleep
-	
-	#define timer_create posix_timer_create
-	#define timer_delete posix_timer_delete
-	#define timer_getoverrun posix_timer_getoverrun
-	#define timer_gettime posix_timer_gettime
-	#define timer_settime posix_timer_settime
-	
+
 	#define clock posix_clock
 #endif
Index: uspace/lib/usb/include/usb/hc.h
===================================================================
--- uspace/lib/usb/include/usb/hc.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/usb/include/usb/hc.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -38,4 +38,5 @@
 #include <sys/types.h>
 #include <ipc/devman.h>
+#include <ipc/loc.h>
 #include <ddf/driver.h>
 #include <bool.h>
@@ -68,5 +69,5 @@
     devman_handle_t *);
 
-int usb_ddf_get_hc_handle_by_class(size_t, devman_handle_t *);
+int usb_ddf_get_hc_handle_by_sid(service_id_t, devman_handle_t *);
 
 
Index: uspace/lib/usb/include/usb/usb.h
===================================================================
--- uspace/lib/usb/include/usb/usb.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/usb/include/usb/usb.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -174,6 +174,6 @@
 } usb_packet_id;
 
-/** Class name for USB host controllers. */
-#define USB_HC_DDF_CLASS_NAME "usbhc"
+/** Category for USB host controllers. */
+#define USB_HC_CATEGORY "usbhc"
 
 #endif
Index: uspace/lib/usb/src/hc.c
===================================================================
--- uspace/lib/usb/src/hc.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/usb/src/hc.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -201,32 +201,19 @@
 /** Get host controller handle by its class index.
  *
- * @param class_index Class index for the host controller.
+ * @param sid Service ID of the HC function.
  * @param hc_handle Where to store the HC handle
  *	(can be NULL for existence test only).
  * @return Error code.
  */
-int usb_ddf_get_hc_handle_by_class(size_t class_index,
-    devman_handle_t *hc_handle)
-{
-	char *class_index_str;
-	devman_handle_t hc_handle_tmp;
+int usb_ddf_get_hc_handle_by_sid(service_id_t sid, devman_handle_t *hc_handle)
+{
+	devman_handle_t handle;
 	int rc;
-
-	rc = asprintf(&class_index_str, "%zu", class_index);
-	if (rc < 0) {
-		return ENOMEM;
-	}
-	rc = devman_device_get_handle_by_class("usbhc", class_index_str,
-	    &hc_handle_tmp, 0);
-	free(class_index_str);
-	if (rc != EOK) {
-		return rc;
-	}
-
-	if (hc_handle != NULL) {
-		*hc_handle = hc_handle_tmp;
-	}
-
-	return EOK;
+	
+	rc = devman_fun_sid_to_handle(sid, &handle);
+	if (hc_handle != NULL)
+		*hc_handle = handle;
+	
+	return rc;
 }
 
Index: uspace/lib/usb/src/resolve.c
===================================================================
--- uspace/lib/usb/src/resolve.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/usb/src/resolve.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -46,10 +46,10 @@
     devman_handle_t *out_hc_handle, usb_address_t *out_device_address)
 {
-	size_t class_index;
+	uint64_t sid;
 	size_t address;
 	int rc;
 	char *ptr;
 
-	rc = str_size_t(path, &ptr, 10, false, &class_index);
+	rc = str_uint64(path, &ptr, 10, false, &sid);
 	if (rc != EOK) {
 		return false;
@@ -64,5 +64,5 @@
 		return false;
 	}
-	rc = usb_ddf_get_hc_handle_by_class(class_index, out_hc_handle);
+	rc = usb_ddf_get_hc_handle_by_sid(sid, out_hc_handle);
 	if (rc != EOK) {
 		return false;
@@ -152,5 +152,5 @@
 		if (str_length(func_start) > 0) {
 			char tmp_path[MAX_DEVICE_PATH ];
-			rc = devman_get_device_path(dev_handle,
+			rc = devman_fun_get_path(dev_handle,
 			    tmp_path, MAX_DEVICE_PATH);
 			if (rc != EOK) {
@@ -173,5 +173,5 @@
 
 	/* First try to get the device handle. */
-	rc = devman_device_get_handle(path, &dev_handle, 0);
+	rc = devman_fun_get_handle(path, &dev_handle, 0);
 	if (rc != EOK) {
 		free(path);
@@ -184,5 +184,5 @@
 		/* Get device handle first. */
 		devman_handle_t tmp_handle;
-		rc = devman_device_get_handle(path, &tmp_handle, 0);
+		rc = devman_fun_get_handle(path, &tmp_handle, 0);
 		if (rc != EOK) {
 			free(path);
Index: uspace/lib/usbvirt/src/device.c
===================================================================
--- uspace/lib/usbvirt/src/device.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/lib/usbvirt/src/device.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -87,5 +87,5 @@
 	
 	devman_handle_t handle;
-	int rc = devman_device_get_handle(vhc_path, &handle, 0);
+	int rc = devman_fun_get_handle(vhc_path, &handle, 0);
 	if (rc != EOK)
 		return rc;
Index: uspace/srv/devman/devman.c
===================================================================
--- uspace/srv/devman/devman.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/devman/devman.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -73,14 +73,4 @@
 }
 
-static int loc_devices_class_compare(unsigned long key[], hash_count_t keys,
-    link_t *item)
-{
-	dev_class_info_t *class_info
-	    = hash_table_get_instance(item, dev_class_info_t, loc_link);
-	assert(class_info != NULL);
-
-	return (class_info->service_id == (service_id_t) key[0]);
-}
-
 static void devices_remove_callback(link_t *item)
 {
@@ -102,10 +92,4 @@
 	.hash = devices_hash,
 	.compare = loc_functions_compare,
-	.remove_callback = devices_remove_callback
-};
-
-static hash_table_operations_t loc_devices_class_ops = {
-	.hash = devices_hash,
-	.compare = loc_devices_class_compare,
 	.remove_callback = devices_remove_callback
 };
@@ -564,14 +548,4 @@
 
 	fibril_mutex_lock(&driver->driver_mutex);
-	
-	async_exch_t *exch = async_exchange_begin(driver->sess);
-	async_sess_t *sess = async_connect_me_to(EXCHANGE_SERIALIZE, exch,
-	    DRIVER_DEVMAN, 0, 0);
-	async_exchange_end(exch);
-
-	if (!sess) {
-		fibril_mutex_unlock(&driver->driver_mutex);
-		return;
-	}
 
 	/*
@@ -599,5 +573,5 @@
 		fibril_mutex_unlock(&driver->driver_mutex);
 
-		add_device(sess, driver, dev, tree);
+		add_device(driver, dev, tree);
 
 		/*
@@ -619,6 +593,4 @@
 		link = driver->devices.head.next;
 	}
-
-	async_hangup(sess);
 
 	/*
@@ -734,6 +706,5 @@
  * @param node		The device's node in the device tree.
  */
-void add_device(async_sess_t *sess, driver_t *drv, dev_node_t *dev,
-    dev_tree_t *tree)
+void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
 {
 	/*
@@ -752,5 +723,5 @@
 	}
 	
-	async_exch_t *exch = async_exchange_begin(sess);
+	async_exch_t *exch = async_exchange_begin(drv->sess);
 	
 	ipc_call_t answer;
@@ -822,16 +793,7 @@
 	fibril_mutex_unlock(&drv->driver_mutex);
 
-	if (is_running) {
-		/* Notify the driver about the new device. */
-		async_exch_t *exch = async_exchange_begin(drv->sess);
-		async_sess_t *sess = async_connect_me_to(EXCHANGE_SERIALIZE, exch,
-		    DRIVER_DEVMAN, 0, 0);
-		async_exchange_end(exch);
-		
-		if (sess) {
-			add_device(sess, drv, dev, tree);
-			async_hangup(sess);
-		}
-	}
+	/* Notify the driver about the new device. */
+	if (is_running)
+		add_device(drv, dev, tree);
 	
 	return true;
@@ -936,4 +898,35 @@
 }
 
+/** 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 */
 
@@ -950,5 +943,4 @@
 		link_initialize(&res->dev_functions);
 		list_initialize(&res->match_ids.ids);
-		list_initialize(&res->classes);
 		link_initialize(&res->devman_fun);
 		link_initialize(&res->loc_fun);
@@ -1115,4 +1107,24 @@
 	
 	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(tree != NULL);
+	assert(fun != NULL);
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	/* Remove the node from the handle-to-node map. */
+	unsigned long key = fun->handle;
+	hash_table_remove(&tree->devman_functions, &key, 1);
+	
+	/* Remove the node from the list of its parent's children. */
+	if (fun->dev != NULL)
+		list_remove(&fun->dev_functions);
 }
 
@@ -1142,5 +1154,5 @@
 	char *rel_path = path;
 	char *next_path_elem = NULL;
-	bool cont = true;
+	bool cont = (rel_path[1] != '\0');
 	
 	while (cont && fun != NULL) {
@@ -1193,34 +1205,4 @@
 }
 
-/** Find function node by its class name and index. */
-fun_node_t *find_fun_node_by_class(class_list_t *class_list,
-    const char *class_name, const char *dev_name)
-{
-	assert(class_list != NULL);
-	assert(class_name != NULL);
-	assert(dev_name != NULL);
-
-	fibril_rwlock_read_lock(&class_list->rwlock);
-
-	dev_class_t *cl = find_dev_class_no_lock(class_list, class_name);
-	if (cl == NULL) {
-		fibril_rwlock_read_unlock(&class_list->rwlock);
-		return NULL;
-	}
-
-	dev_class_info_t *dev = find_dev_in_class(cl, dev_name);
-	if (dev == NULL) {
-		fibril_rwlock_read_unlock(&class_list->rwlock);
-		return NULL;
-	}
-
-	fun_node_t *fun = dev->fun;
-
-	fibril_rwlock_read_unlock(&class_list->rwlock);
-
-	return fun;
-}
-
-
 /** Find child function node with a specified name.
  *
@@ -1235,187 +1217,4 @@
 	return find_fun_node_in_device(pfun->child, name);
 }
-
-/* Device classes */
-
-/** Create device class.
- *
- * @return	Device class.
- */
-dev_class_t *create_dev_class(void)
-{
-	dev_class_t *cl;
-	
-	cl = (dev_class_t *) malloc(sizeof(dev_class_t));
-	if (cl != NULL) {
-		memset(cl, 0, sizeof(dev_class_t));
-		list_initialize(&cl->devices);
-		fibril_mutex_initialize(&cl->mutex);
-	}
-	
-	return cl;
-}
-
-/** Create device class info.
- *
- * @return		Device class info.
- */
-dev_class_info_t *create_dev_class_info(void)
-{
-	dev_class_info_t *info;
-	
-	info = (dev_class_info_t *) malloc(sizeof(dev_class_info_t));
-	if (info != NULL) {
-		memset(info, 0, sizeof(dev_class_info_t));
-		link_initialize(&info->dev_classes);
-		link_initialize(&info->loc_link);
-		link_initialize(&info->link);
-	}
-	
-	return info;
-}
-
-size_t get_new_class_dev_idx(dev_class_t *cl)
-{
-	size_t dev_idx;
-	
-	fibril_mutex_lock(&cl->mutex);
-	dev_idx = ++cl->curr_dev_idx;
-	fibril_mutex_unlock(&cl->mutex);
-	
-	return dev_idx;
-}
-
-
-/** Create unique device name within the class.
- *
- * @param cl		The class.
- * @param base_dev_name	Contains the base name for the device if it was
- *			specified by the driver when it registered the device by
- *			the class; NULL if driver specified no base name.
- * @return		The unique name for the device within the class.
- */
-char *create_dev_name_for_class(dev_class_t *cl, const char *base_dev_name)
-{
-	char *dev_name;
-	const char *base_name;
-	
-	if (base_dev_name != NULL)
-		base_name = base_dev_name;
-	else
-		base_name = cl->base_dev_name;
-	
-	size_t idx = get_new_class_dev_idx(cl);
-	asprintf(&dev_name, "%s%zu", base_name, idx);
-	
-	return dev_name;
-}
-
-/** Add the device function to the class.
- *
- * The device may be added to multiple classes and a class may contain multiple
- * devices. The class and the device are associated with each other by the
- * dev_class_info_t structure.
- *
- * @param dev		The device.
- * @param class		The class.
- * @param base_dev_name	The base name of the device within the class if
- *			specified by the driver, NULL otherwise.
- * @return		dev_class_info_t structure which associates the device
- *			with the class.
- */
-dev_class_info_t *add_function_to_class(fun_node_t *fun, dev_class_t *cl,
-    const char *base_dev_name)
-{
-	dev_class_info_t *info;
-
-	assert(fun != NULL);
-	assert(cl != NULL);
-
-	info = create_dev_class_info();
-
-	
-	if (info != NULL) {
-		info->dev_class = cl;
-		info->fun = fun;
-		
-		/* Add the device to the class. */
-		fibril_mutex_lock(&cl->mutex);
-		list_append(&info->link, &cl->devices);
-		fibril_mutex_unlock(&cl->mutex);
-		
-		/* Add the class to the device. */
-		list_append(&info->dev_classes, &fun->classes);
-		
-		/* Create unique name for the device within the class. */
-		info->dev_name = create_dev_name_for_class(cl, base_dev_name);
-	}
-	
-	return info;
-}
-
-dev_class_t *get_dev_class(class_list_t *class_list, char *class_name)
-{
-	dev_class_t *cl;
-	
-	fibril_rwlock_write_lock(&class_list->rwlock);
-	cl = find_dev_class_no_lock(class_list, class_name);
-	if (cl == NULL) {
-		cl = create_dev_class();
-		if (cl != NULL) {
-			cl->name = class_name;
-			cl->base_dev_name = "";
-			add_dev_class_no_lock(class_list, cl);
-		}
-	}
-
-	fibril_rwlock_write_unlock(&class_list->rwlock);
-	return cl;
-}
-
-dev_class_t *find_dev_class_no_lock(class_list_t *class_list,
-    const char *class_name)
-{
-	dev_class_t *cl;
-	
-	list_foreach(class_list->classes, link) {
-		cl = list_get_instance(link, dev_class_t, link);
-		if (str_cmp(cl->name, class_name) == 0) {
-			return cl;
-		}
-	}
-	
-	return NULL;
-}
-
-void add_dev_class_no_lock(class_list_t *class_list, dev_class_t *cl)
-{
-	list_append(&cl->link, &class_list->classes);
-}
-
-dev_class_info_t *find_dev_in_class(dev_class_t *dev_class, const char *dev_name)
-{
-	assert(dev_class != NULL);
-	assert(dev_name != NULL);
-
-	list_foreach(dev_class->devices, link) {
-		dev_class_info_t *dev = list_get_instance(link,
-		    dev_class_info_t, link);
-
-		if (str_cmp(dev->dev_name, dev_name) == 0) {
-			return dev;
-		}
-	}
-
-	return NULL;
-}
-
-void init_class_list(class_list_t *class_list)
-{
-	list_initialize(&class_list->classes);
-	fibril_rwlock_initialize(&class_list->rwlock);
-	hash_table_create(&class_list->loc_functions, DEVICE_BUCKETS, 1,
-	    &loc_devices_class_ops);
-}
-
 
 /* loc devices */
@@ -1436,35 +1235,4 @@
 }
 
-fun_node_t *find_loc_class_function(class_list_t *classes,
-    service_id_t service_id)
-{
-	fun_node_t *fun = NULL;
-	dev_class_info_t *cli;
-	link_t *link;
-	unsigned long key = (unsigned long)service_id;
-	
-	fibril_rwlock_read_lock(&classes->rwlock);
-	link = hash_table_find(&classes->loc_functions, &key);
-	if (link != NULL) {
-		cli = hash_table_get_instance(link, dev_class_info_t,
-		    loc_link);
-		fun = cli->fun;
-	}
-	fibril_rwlock_read_unlock(&classes->rwlock);
-	
-	return fun;
-}
-
-void class_add_loc_function(class_list_t *class_list, dev_class_info_t *cli)
-{
-	unsigned long key = (unsigned long) cli->service_id;
-	
-	fibril_rwlock_write_lock(&class_list->rwlock);
-	hash_table_insert(&class_list->loc_functions, &key, &cli->loc_link);
-	fibril_rwlock_write_unlock(&class_list->rwlock);
-
-	assert(find_loc_class_function(class_list, cli->service_id) != NULL);
-}
-
 void tree_add_loc_function(dev_tree_t *tree, fun_node_t *fun)
 {
Index: uspace/srv/devman/devman.h
===================================================================
--- uspace/srv/devman/devman.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/devman/devman.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2011 Jiri Svoboda
  * All rights reserved.
  *
@@ -53,5 +54,4 @@
 #define DEVICE_BUCKETS 256
 
-#define LOC_CLASS_NAMESPACE "class"
 #define LOC_DEVICE_NAMESPACE "devices"
 #define LOC_SEPARATOR '\\'
@@ -155,4 +155,6 @@
 	/** Name of the function, assigned by the device driver */
 	char *name;
+	/** Function type */
+	fun_type_t ftype;
 	
 	/** Full path and name of the device in device hierarchy */
@@ -170,6 +172,4 @@
 	match_id_list_t match_ids;
 	
-	/** List of device classes to which this device function belongs. */
-	list_t classes;
 	/** Service ID if the device function is registered with loc. */
 	service_id_t service_id;
@@ -214,82 +214,4 @@
 } dev_tree_t;
 
-typedef struct dev_class {
-	/** The name of the class. */
-	const char *name;
-	
-	/**
-	 * Pointer to the previous and next class in the list of registered
-	 * classes.
-	 */
-	link_t link;
-	
-	/**
-	 * List of dev_class_info structures - one for each device registered by
-	 * this class.
-	 */
-	list_t devices;
-	
-	/**
-	 * Default base name for the device within the class, might be overrided
-	 * by the driver.
-	 */
-	const char *base_dev_name;
-	
-	/** Unique numerical identifier of the newly added device. */
-	size_t curr_dev_idx;
-	/** Synchronize access to the list of devices in this class. */
-	fibril_mutex_t mutex;
-} dev_class_t;
-
-/**
- * Provides n-to-m mapping between function nodes and classes - each function
- * can register in an arbitrary number of classes and each class can contain
- * an arbitrary number of device functions.
- */
-typedef struct dev_class_info {
-	/** The class. */
-	dev_class_t *dev_class;
-	/** The device. */
-	fun_node_t *fun;
-	
-	/**
-	 * Pointer to the previous and next class info in the list of devices
-	 * registered by the class.
-	 */
-	link_t link;
-	
-	/**
-	 * Pointer to the previous and next class info in the list of classes
-	 * by which the device is registered.
-	 */
-	link_t dev_classes;
-	
-	/** The name of the device function within the class. */
-	char *dev_name;
-	/** Service ID in the class namespace. */
-	service_id_t service_id;
-	
-	/**
-	 * Link to hash table of services registered with location service using
-	 * their class names.
-	 */
-	link_t loc_link;
-} dev_class_info_t;
-
-/** The list of device classes. */
-typedef struct class_list {
-	/** List of classes. */
-	list_t classes;
-	
-	/**
-	 * Hash table of services registered with location service using their
-	 * class name, indexed by service IDs.
-	 */
-	hash_table_t loc_functions;
-	
-	/** Fibril mutex for list of classes. */
-	fibril_rwlock_t rwlock;
-} class_list_t;
-
 /* Match ids and scores */
 
@@ -313,5 +235,5 @@
 extern void add_driver(driver_list_t *, driver_t *);
 extern void attach_driver(dev_node_t *, driver_t *);
-extern void add_device(async_sess_t *, driver_t *, dev_node_t *, dev_tree_t *);
+extern void add_device(driver_t *, dev_node_t *, dev_tree_t *);
 extern bool start_driver(driver_t *);
 
@@ -331,4 +253,6 @@
 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);
@@ -339,5 +263,4 @@
 extern fun_node_t *find_fun_node_by_path(dev_tree_t *, char *);
 extern fun_node_t *find_fun_node_in_device(dev_node_t *, const char *);
-extern fun_node_t *find_fun_node_by_class(class_list_t *, const char *, const char *);
 
 /* Device tree */
@@ -347,20 +270,5 @@
 extern bool insert_dev_node(dev_tree_t *, dev_node_t *, fun_node_t *);
 extern bool insert_fun_node(dev_tree_t *, fun_node_t *, char *, dev_node_t *);
-
-/* Device classes */
-
-extern dev_class_t *create_dev_class(void);
-extern dev_class_info_t *create_dev_class_info(void);
-extern size_t get_new_class_dev_idx(dev_class_t *);
-extern char *create_dev_name_for_class(dev_class_t *, const char *);
-extern dev_class_info_t *add_function_to_class(fun_node_t *, dev_class_t *,
-    const char *);
-
-extern void init_class_list(class_list_t *);
-
-extern dev_class_t *get_dev_class(class_list_t *, char *);
-extern dev_class_t *find_dev_class_no_lock(class_list_t *, const char *);
-extern dev_class_info_t *find_dev_in_class(dev_class_t *, const char *);
-extern void add_dev_class_no_lock(class_list_t *, dev_class_t *);
+extern void remove_fun_node(dev_tree_t *, fun_node_t *);
 
 /* Loc services */
@@ -369,7 +277,5 @@
 
 extern fun_node_t *find_loc_tree_function(dev_tree_t *, service_id_t);
-extern fun_node_t *find_loc_class_function(class_list_t *, service_id_t);
-
-extern void class_add_loc_function(class_list_t *, dev_class_info_t *);
+
 extern void tree_add_loc_function(dev_tree_t *, fun_node_t *);
 
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/devman/main.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -64,5 +64,4 @@
 static driver_list_t drivers_list;
 static dev_tree_t device_tree;
-static class_list_t class_list;
 
 /** Register running driver. */
@@ -279,6 +278,8 @@
 		return;
 	}
-
+	
 	fun_node_t *fun = create_fun_node();
+	fun->ftype = ftype;
+	
 	if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
 		fibril_rwlock_write_unlock(&tree->rwlock);
@@ -333,31 +334,5 @@
 }
 
-static void loc_register_class_dev(dev_class_info_t *cli)
-{
-	/* Create loc path and name for the service. */
-	char *loc_pathname = NULL;
-
-	asprintf(&loc_pathname, "%s/%s%c%s", LOC_CLASS_NAMESPACE,
-	    cli->dev_class->name, LOC_SEPARATOR, cli->dev_name);
-	if (loc_pathname == NULL)
-		return;
-	
-	/*
-	 * Register the device with location service and remember its
-	 * service ID.
-	 */
-	loc_service_register_with_iface(loc_pathname,
-	    &cli->service_id, DEVMAN_CONNECT_FROM_LOC);
-	
-	/*
-	 * Add device to the hash map of class devices registered with
-	 * location service.
-	 */
-	class_add_loc_function(&class_list, cli);
-	
-	free(loc_pathname);
-}
-
-static void devman_add_function_to_class(ipc_callid_t callid, ipc_call_t *call)
+static void devman_add_function_to_cat(ipc_callid_t callid, ipc_call_t *call)
 {
 	devman_handle_t handle = IPC_GET_ARG1(*call);
@@ -365,7 +340,7 @@
 	int rc;
 	
-	/* Get class name. */
-	char *class_name;
-	rc = async_data_write_accept((void **) &class_name, true,
+	/* Get category name. */
+	char *cat_name;
+	rc = async_data_write_accept((void **) &cat_name, true,
 	    0, 0, 0, 0);
 	if (rc != EOK) {
@@ -380,21 +355,57 @@
 	}
 	
-	dev_class_t *cl = get_dev_class(&class_list, class_name);
-	dev_class_info_t *class_info = add_function_to_class(fun, cl, NULL);
-	
-	/* Register the device's class alias with location service. */
-	loc_register_class_dev(class_info);
-	
-	rc = loc_category_get_id(class_name, &cat_id, IPC_FLAG_BLOCKING);
+	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);
 	} else {
 		log_msg(LVL_ERROR, "Failed adding function `%s' to category "
-		    "`%s'.", fun->pathname, class_name);
-	}
-	
-	log_msg(LVL_NOTE, "Function `%s' added to class `%s' as `%s'.",
-	    fun->pathname, class_name, class_info->dev_name);
-
+		    "`%s'.", fun->pathname, cat_name);
+	}
+	
+	log_msg(LVL_NOTE, "Function `%s' added to category `%s'.",
+	    fun->pathname, cat_name);
+
+	async_answer_0(callid, 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;
+	
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	fun_node_t *fun = find_fun_node_no_lock(&device_tree, fun_handle);
+	if (fun == NULL) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	log_msg(LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
+	
+	if (fun->ftype == fun_inner) {
+		/* Handle possible descendants */
+		/* TODO */
+		log_msg(LVL_WARN, "devman_remove_function(): not handling "
+		    "descendants\n");
+	} else {
+		/* Unregister from location service */
+		rc = loc_service_unregister(fun->service_id);
+		if (rc != EOK) {
+			log_msg(LVL_ERROR, "Failed unregistering tree service.");
+			fibril_rwlock_write_unlock(&tree->rwlock);
+			async_answer_0(callid, EIO);
+			return;
+		}
+	}
+	
+	remove_fun_node(&device_tree, fun);
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	delete_fun_node(fun);
+	
+	log_msg(LVL_DEBUG, "devman_remove_function() succeeded.");
 	async_answer_0(callid, EOK);
 }
@@ -449,6 +460,9 @@
 			devman_add_function(callid, &call);
 			break;
-		case DEVMAN_ADD_DEVICE_TO_CLASS:
-			devman_add_function_to_class(callid, &call);
+		case DEVMAN_ADD_DEVICE_TO_CATEGORY:
+			devman_add_function_to_cat(callid, &call);
+			break;
+		case DEVMAN_REMOVE_FUNCTION:
+			devman_remove_function(callid, &call);
 			break;
 		default:
@@ -483,41 +497,6 @@
 }
 
-/** Find handle for the device instance identified by device class name. */
-static void devman_function_get_handle_by_class(ipc_callid_t iid,
-    ipc_call_t *icall)
-{
-	char *classname;
-	char *devname;
-
-	int rc = async_data_write_accept((void **) &classname, true, 0, 0, 0, 0);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	rc = async_data_write_accept((void **) &devname, true, 0, 0, 0, 0);
-	if (rc != EOK) {
-		free(classname);
-		async_answer_0(iid, rc);
-		return;
-	}
-
-
-	fun_node_t *fun = find_fun_node_by_class(&class_list,
-	    classname, devname);
-
-	free(classname);
-	free(devname);
-
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-
-	async_answer_1(iid, EOK, fun->handle);
-}
-
-/** Find device path by its handle. */
-static void devman_get_device_path_by_handle(ipc_callid_t iid,
-    ipc_call_t *icall)
+/** 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);
@@ -543,4 +522,41 @@
 	}
 
+	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);
+
+	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);
+		return;
+	}
+
+	void *buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
 	size_t sent_length = str_size(fun->pathname);
 	if (sent_length > data_len) {
@@ -554,4 +570,92 @@
 }
 
+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) {
+		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(&device_tree, IPC_GET_ARG1(*icall));
+	if (fun == NULL) {
+		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);
+}
+
+/** 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;
+	}
+
+	async_answer_1(iid, EOK, fun->handle);
+}
 
 /** Function for handling connections from a client to the device manager. */
@@ -572,9 +676,18 @@
 			devman_function_get_handle(callid, &call);
 			break;
-		case DEVMAN_DEVICE_GET_HANDLE_BY_CLASS:
-			devman_function_get_handle_by_class(callid, &call);
-			break;
-		case DEVMAN_DEVICE_GET_DEVICE_PATH:
-			devman_get_device_path_by_handle(callid, &call);
+		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_PATH:
+			devman_fun_get_path(callid, &call);
+			break;
+		case DEVMAN_FUN_SID_TO_HANDLE:
+			devman_fun_sid_to_handle(callid, &call);
 			break;
 		default:
@@ -678,8 +791,8 @@
 
 	fun = find_loc_tree_function(&device_tree, service_id);
-	if (fun == NULL)
-		fun = find_loc_class_function(&class_list, service_id);
 	
 	if (fun == NULL || fun->dev->drv == NULL) {
+		log_msg(LVL_WARN, "devman_connection_loc(): function "
+		    "not found.\n");
 		async_answer_0(iid, ENOENT);
 		return;
@@ -687,9 +800,4 @@
 	
 	dev = fun->dev;
-	
-	if ((dev->state != DEVICE_USABLE) || (!dev->drv->sess)) {
-		async_answer_0(iid, EINVAL);
-		return;
-	}
 	
 	async_exch_t *exch = async_exchange_begin(dev->drv->sess);
@@ -753,6 +861,4 @@
 	}
 
-	init_class_list(&class_list);
-	
 	/*
 	 * !!! devman_connection ... as the device manager is not a real loc
Index: uspace/srv/hid/input/generic/input.c
===================================================================
--- uspace/srv/hid/input/generic/input.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/hid/input/generic/input.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -66,7 +66,4 @@
 #include <abi/ipc/methods.h>
 
-/* In microseconds */
-#define DISCOVERY_POLL_INTERVAL  (10 * 1000 * 1000)
-
 #define NUM_LAYOUTS  3
 
@@ -497,14 +494,7 @@
 }
 
-/** Periodically check for new input devices.
- *
- * Looks under /loc/class/keyboard and /loc/class/mouse.
- *
- * @param arg Ignored
- *
- */
-static int dev_discovery_fibril(void *arg)
-{
-	category_id_t keyboard_cat, mouse_cat;
+static int dev_check_new_kbdevs(void)
+{
+	category_id_t keyboard_cat;
 	service_id_t *svcs;
 	size_t count, i;
@@ -518,4 +508,51 @@
 	}
 	
+	/*
+	 * Check for new keyboard devices
+	 */
+	rc = loc_category_get_svcs(keyboard_cat, &svcs, &count);
+	if (rc != EOK) {
+		printf("%s: Failed getting list of keyboard devices.\n",
+		    NAME);
+		return EIO;
+	}
+
+	for (i = 0; i < count; i++) {
+		already_known = false;
+		
+		/* 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);
+			if (kdev->svc_id == svcs[i]) {
+				already_known = true;
+				break;
+			}
+		}
+		
+		if (!already_known) {
+			kbd_dev_t *kdev;
+			if (kbd_add_kbdev(svcs[i], &kdev) == EOK) {
+				printf("%s: Connected keyboard device '%s'\n",
+				    NAME, kdev->svc_name);
+			}
+		}
+	}
+	
+	free(svcs);
+	
+	/* XXX Handle device removal */
+	
+	return EOK;
+}
+
+static int dev_check_new_mousedevs(void)
+{
+	category_id_t mouse_cat;
+	service_id_t *svcs;
+	size_t count, i;
+	bool already_known;
+	int rc;
+	
 	rc = loc_category_get_id("mouse", &mouse_cat, IPC_FLAG_BLOCKING);
 	if (rc != EOK) {
@@ -524,90 +561,76 @@
 	}
 	
-	while (true) {
-		async_usleep(DISCOVERY_POLL_INTERVAL);
-		
-		/*
-		 * Check for new keyboard devices
-		 */
-		rc = loc_category_get_svcs(keyboard_cat, &svcs, &count);
-		if (rc != EOK) {
-			printf("%s: Failed getting list of keyboard devices.\n",
-			    NAME);
-			continue;
-		}
-
-		for (i = 0; i < count; i++) {
-			already_known = false;
-			
-			/* 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);
-				if (kdev->svc_id == svcs[i]) {
-					already_known = true;
-					break;
-				}
-			}
-
-			if (!already_known) {
-				kbd_dev_t *kdev;
-				if (kbd_add_kbdev(svcs[i], &kdev) == EOK) {
-					printf("%s: Connected keyboard device '%s'\n",
-					    NAME, kdev->svc_name);
-				}
+	/*
+	 * Check for new mouse devices
+	 */
+	rc = loc_category_get_svcs(mouse_cat, &svcs, &count);
+	if (rc != EOK) {
+		printf("%s: Failed getting list of mouse devices.\n",
+		    NAME);
+		return EIO;
+	}
+	
+	for (i = 0; i < count; i++) {
+		already_known = false;
+		
+		/* 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);
+			if (mdev->svc_id == svcs[i]) {
+				already_known = true;
+				break;
 			}
 		}
 		
-		/* XXX Handle device removal */
-		
-		/*
-		 * Check for new mouse devices
-		 */
-		rc = loc_category_get_svcs(mouse_cat, &svcs, &count);
-		if (rc != EOK) {
-			printf("%s: Failed getting list of mouse devices.\n",
-			    NAME);
-			continue;
-		}
-
-		for (i = 0; i < count; i++) {
-			already_known = false;
-			
-			/* 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);
-				if (mdev->svc_id == svcs[i]) {
-					already_known = true;
-					break;
-				}
-			}
-
-			if (!already_known) {
-				mouse_dev_t *mdev;
-				if (mouse_add_mousedev(svcs[i], &mdev) == EOK) {
-					printf("%s: Connected mouse device '%s'\n",
-					    NAME, mdev->svc_name);
-				}
+		if (!already_known) {
+			mouse_dev_t *mdev;
+			if (mouse_add_mousedev(svcs[i], &mdev) == EOK) {
+				printf("%s: Connected mouse device '%s'\n",
+				    NAME, mdev->svc_name);
 			}
 		}
-		
-		/* XXX Handle device removal */
-	}
+	}
+	
+	free(svcs);
+	
+	/* XXX Handle device removal */
 	
 	return EOK;
 }
 
-/** Start a fibril for discovering new devices. */
-static void input_start_dev_discovery(void)
-{
-	fid_t fid = fibril_create(dev_discovery_fibril, NULL);
-	if (!fid) {
-		printf("%s: Failed to create device discovery fibril.\n",
-		    NAME);
-		return;
-	}
-	
-	fibril_add_ready(fid);
+static int dev_check_new(void)
+{
+	int rc;
+	
+	rc = dev_check_new_kbdevs();
+	if (rc != EOK)
+		return rc;
+	
+	rc = dev_check_new_mousedevs();
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+static void cat_change_cb(void)
+{
+	dev_check_new();
+}
+
+/** Start listening for new devices. */
+static int input_start_dev_discovery(void)
+{
+	int rc;
+
+	rc = loc_register_cat_change_cb(cat_change_cb);
+	if (rc != EOK) {
+		printf("%s: Failed registering callback for device discovery. "
+		    "(%d)\n", NAME, rc);
+		return rc;
+	}
+
+	return dev_check_new();
 }
 
Index: uspace/srv/hid/input/port/adb_mouse.c
===================================================================
--- uspace/srv/hid/input/port/adb_mouse.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/hid/input/port/adb_mouse.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -42,4 +42,5 @@
 #include <errno.h>
 #include <loc.h>
+#include <stdio.h>
 
 static mouse_dev_t *mouse_dev;
Index: uspace/srv/hw/irc/apic/apic.c
===================================================================
--- uspace/srv/hw/irc/apic/apic.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/hw/irc/apic/apic.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -45,4 +45,5 @@
 #include <errno.h>
 #include <async.h>
+#include <stdio.h>
 
 #define NAME  "apic"
Index: uspace/srv/loader/main.c
===================================================================
--- uspace/srv/loader/main.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/loader/main.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -61,4 +61,5 @@
 #include <elf/elf.h>
 #include <elf/elf_load.h>
+#include <vfs/vfs.h>
 
 #ifdef CONFIG_RTLD
@@ -89,9 +90,5 @@
 
 /** Number of preset files */
-static int filc = 0;
-/** Preset files vector */
-static fdi_node_t **filv = NULL;
-/** Buffer holding all preset files */
-static fdi_node_t *fil_buf = NULL;
+static unsigned int filc = 0;
 
 static elf_info_t prog_info;
@@ -239,45 +236,23 @@
 static void ldr_set_files(ipc_callid_t rid, ipc_call_t *request)
 {
-	fdi_node_t *buf;
-	size_t buf_size;
-	int rc = async_data_write_accept((void **) &buf, false, 0, 0,
-	    sizeof(fdi_node_t), &buf_size);
-	
-	if (rc == EOK) {
-		int count = buf_size / sizeof(fdi_node_t);
-		
-		/*
-		 * Allocate new filv
-		 */
-		fdi_node_t **_filv = (fdi_node_t **) calloc(count + 1, sizeof(fdi_node_t *));
-		if (_filv == NULL) {
-			free(buf);
-			async_answer_0(rid, ENOMEM);
-			return;
+	size_t count = IPC_GET_ARG1(*request);
+
+	async_exch_t *vfs_exch = vfs_exchange_begin();
+
+	for (filc = 0; filc < count; filc++) {
+		ipc_callid_t callid;
+		int fd;
+
+		if (!async_state_change_receive(&callid, NULL, NULL, NULL)) {
+			async_answer_0(callid, EINVAL);
+			break;
 		}
-		
-		/*
-		 * Fill the new filv with argument pointers
-		 */
-		int i;
-		for (i = 0; i < count; i++)
-			_filv[i] = &buf[i];
-		
-		_filv[count] = NULL;
-		
-		/*
-		 * Copy temporary data to global variables
-		 */
-		if (fil_buf != NULL)
-			free(fil_buf);
-		
-		if (filv != NULL)
-			free(filv);
-		
-		filc = count;
-		fil_buf = buf;
-		filv = _filv;
-	}
-	
+		async_state_change_finalize(callid, vfs_exch);
+		fd = fd_wait();
+		assert(fd == (int) filc);
+	}
+
+	vfs_exchange_end(vfs_exch);
+
 	async_answer_0(rid, EOK);
 }
@@ -308,5 +283,4 @@
 	
 	pcb.filc = filc;
-	pcb.filv = filv;
 	
 	if (prog_info.interp == NULL) {
Index: uspace/srv/loc/category.c
===================================================================
--- uspace/srv/loc/category.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/loc/category.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -93,5 +93,5 @@
 	cat->id = loc_create_id();
 	link_initialize(&cat->cat_list);
-	list_initialize(&cat->services);
+	list_initialize(&cat->svc_memb);
 }
 
@@ -113,17 +113,38 @@
 {
 	assert(fibril_mutex_is_locked(&cat->mutex));
+	assert(fibril_mutex_is_locked(&services_list_mutex));
 
 	/* Verify that category does not contain this service yet. */
-	list_foreach(cat->services, item) {
-
-		loc_service_t *csvc = list_get_instance(item, loc_service_t,
-		    cat_services);
-		if (csvc == svc) {
+	list_foreach(cat->svc_memb, item) {
+		svc_categ_t *memb = list_get_instance(item, svc_categ_t,
+		    cat_link);
+		if (memb->svc == svc) {
 			return EEXIST;
 		}
 	}
 
-	list_append(&svc->cat_services, &cat->services);
+	svc_categ_t *nmemb = malloc(sizeof(svc_categ_t));
+	if (nmemb == NULL)
+		return ENOMEM;
+
+	nmemb->svc = svc;
+	nmemb->cat = cat;
+
+	list_append(&nmemb->cat_link, &cat->svc_memb);
+	list_append(&nmemb->svc_link, &svc->cat_memb);
+
 	return EOK;
+}
+
+/** Remove service from category. */
+void category_remove_service(svc_categ_t *memb)
+{
+	assert(fibril_mutex_is_locked(&memb->cat->mutex));
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+
+	list_remove(&memb->cat_link);
+	list_remove(&memb->svc_link);
+
+	free(memb);
 }
 
@@ -169,5 +190,5 @@
 	buf_cnt = buf_size / sizeof(service_id_t);
 
-	act_cnt = list_count(&cat->services);
+	act_cnt = list_count(&cat->svc_memb);
 	*act_size = act_cnt * sizeof(service_id_t);
 
@@ -176,10 +197,10 @@
 
 	size_t pos = 0;
-	list_foreach(cat->services, item) {
-		loc_service_t *svc =
-		    list_get_instance(item, loc_service_t, cat_services);
+	list_foreach(cat->svc_memb, item) {
+		svc_categ_t *memb =
+		    list_get_instance(item, svc_categ_t, cat_link);
 
 		if (pos < buf_cnt)
-			id_buf[pos] = svc->id;
+			id_buf[pos] = memb->svc->id;
 		pos++;
 	}
Index: uspace/srv/loc/category.h
===================================================================
--- uspace/srv/loc/category.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/loc/category.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -55,6 +55,6 @@
 	link_t cat_list;
 
-	/** List of services in this category (loc_service_t) */
-	list_t services;
+	/** List of service memberships in this category (svc_categ_t) */
+	list_t svc_memb;
 } category_t;
 
@@ -67,4 +67,17 @@
 } categ_dir_t;
 
+/** Service in category membership. */
+typedef struct {
+	/** Link to category_t.svc_memb list */
+	link_t cat_link;
+	/** Link to loc_service_t.cat_memb list */
+	link_t svc_link;
+	
+	/** Category */
+	category_t *cat;
+	/** Service */
+	loc_service_t *svc;
+} svc_categ_t;
+
 extern void categ_dir_init(categ_dir_t *);
 extern void categ_dir_add_cat(categ_dir_t *, category_t *);
@@ -73,4 +86,5 @@
 extern category_t *category_new(const char *);
 extern int category_add_service(category_t *, loc_service_t *);
+extern void category_remove_service(svc_categ_t *);
 extern category_t *category_get(categ_dir_t *, catid_t);
 extern category_t *category_find_by_name(categ_dir_t *, const char *);
Index: uspace/srv/loc/loc.c
===================================================================
--- uspace/srv/loc/loc.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/loc/loc.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -67,5 +67,5 @@
  **/
 
-static FIBRIL_MUTEX_INITIALIZE(services_list_mutex);
+FIBRIL_MUTEX_INITIALIZE(services_list_mutex);
 static FIBRIL_CONDVAR_INITIALIZE(services_list_cv);
 static FIBRIL_MUTEX_INITIALIZE(servers_list_mutex);
@@ -84,4 +84,7 @@
 /** Service directory ogranized by categories (yellow pages) */
 static categ_dir_t cdir;
+
+static FIBRIL_MUTEX_INITIALIZE(callback_sess_mutex);
+static async_sess_t *callback_sess = NULL;
 
 service_id_t loc_create_id(void)
@@ -308,8 +311,19 @@
 {
 	assert(fibril_mutex_is_locked(&services_list_mutex));
-
+	assert(fibril_mutex_is_locked(&cdir.mutex));
+	
 	loc_namespace_delref(service->namespace);
 	list_remove(&(service->services));
 	list_remove(&(service->server_services));
+	
+	/* Remove service from all categories. */
+	while (!list_empty(&service->cat_memb)) {
+		link_t *link = list_first(&service->cat_memb);
+		svc_categ_t *memb = list_get_instance(link, svc_categ_t,
+		    svc_link);
+		fibril_mutex_lock(&memb->cat->mutex);
+		category_remove_service(memb);
+		fibril_mutex_unlock(&memb->cat->mutex);
+	}
 	
 	free(service->name);
@@ -412,4 +426,5 @@
 	fibril_mutex_lock(&services_list_mutex);
 	fibril_mutex_lock(&server->services_mutex);
+	fibril_mutex_lock(&cdir.mutex);
 	
 	while (!list_empty(&server->services)) {
@@ -420,4 +435,5 @@
 	}
 	
+	fibril_mutex_unlock(&cdir.mutex);
 	fibril_mutex_unlock(&server->services_mutex);
 	fibril_mutex_unlock(&services_list_mutex);
@@ -489,4 +505,5 @@
 	link_initialize(&service->services);
 	link_initialize(&service->server_services);
+	list_initialize(&service->cat_memb);
 	
 	/* Check that service is not already registered */
@@ -526,9 +543,61 @@
  *
  */
-static int loc_service_unregister(ipc_callid_t iid, ipc_call_t *icall, 
+static void loc_service_unregister(ipc_callid_t iid, ipc_call_t *icall, 
     loc_server_t *server)
 {
-	/* TODO */
-	return EOK;
+	loc_service_t *svc;
+	
+	fibril_mutex_lock(&services_list_mutex);
+	svc = loc_service_find_id(IPC_GET_ARG1(*icall));
+	if (svc == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+	loc_service_unregister_core(svc);
+	fibril_mutex_unlock(&cdir.mutex);
+	fibril_mutex_unlock(&services_list_mutex);
+	async_answer_0(iid, EOK);
+}
+
+static void loc_category_get_name(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	category_t *cat;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+	
+	cat = category_get(&cdir, IPC_GET_ARG1(*icall));
+	if (cat == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	act_size = str_size(cat->name);
+	if (act_size > size) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, EOVERFLOW);
+		async_answer_0(iid, EOVERFLOW);
+		return;
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, cat->name,
+	    min(size, act_size));
+	
+	fibril_mutex_unlock(&cdir.mutex);
+	
+	async_answer_0(iid, retval);
 }
 
@@ -571,5 +640,4 @@
 	async_answer_0(iid, retval);
 }
-
 
 /** Connect client to the service.
@@ -728,4 +796,44 @@
  *
  */
+static void loc_callback_create(ipc_callid_t iid, ipc_call_t *icall)
+{
+	async_sess_t *cb_sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (cb_sess == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	fibril_mutex_lock(&callback_sess_mutex);
+	if (callback_sess != NULL) {
+		fibril_mutex_unlock(&callback_sess_mutex);
+		async_answer_0(iid, EEXIST);
+		return;
+	}
+	
+	callback_sess = cb_sess;
+	fibril_mutex_unlock(&callback_sess_mutex);
+	
+	async_answer_0(iid, EOK);
+}
+
+void loc_category_change_event(void)
+{
+	fibril_mutex_lock(&callback_sess_mutex);
+
+	if (callback_sess != NULL) {
+		async_exch_t *exch = async_exchange_begin(callback_sess);
+		async_msg_0(exch, LOC_EVENT_CAT_CHANGE);
+		async_exchange_end(exch);
+	}
+
+	fibril_mutex_unlock(&callback_sess_mutex);
+}
+
+/** Find ID for category specified by name.
+ *
+ * On success, answer will contain EOK int retval and service ID in arg1.
+ * On failure, error code will be sent in retval.
+ *
+ */
 static void loc_category_get_id(ipc_callid_t iid, ipc_call_t *icall)
 {
@@ -1095,5 +1203,7 @@
 	
 	fibril_mutex_lock(&services_list_mutex);
+	fibril_mutex_lock(&cdir.mutex);
 	loc_service_unregister_core(null_services[i]);
+	fibril_mutex_unlock(&cdir.mutex);
 	fibril_mutex_unlock(&services_list_mutex);
 	
@@ -1129,4 +1239,6 @@
 
 	async_answer_0(iid, retval);
+
+	loc_category_change_event();
 }
 
@@ -1156,4 +1268,10 @@
 
 	cat = category_new("serial");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("usbhc");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("virtual");
 	categ_dir_add_cat(&cdir, cat);
 
@@ -1244,6 +1362,12 @@
 			loc_namespace_get_id(callid, &call);
 			break;
+		case LOC_CALLBACK_CREATE:
+			loc_callback_create(callid, &call);
+			break;
 		case LOC_CATEGORY_GET_ID:
 			loc_category_get_id(callid, &call);
+			break;
+		case LOC_CATEGORY_GET_NAME:
+			loc_category_get_name(callid, &call);
 			break;
 		case LOC_CATEGORY_GET_SVCS:
Index: uspace/srv/loc/loc.h
===================================================================
--- uspace/srv/loc/loc.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/loc/loc.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -36,4 +36,5 @@
 #define LOC_H_
 
+#include <ipc/loc.h>
 #include <async.h>
 #include <fibril_synch.h>
@@ -89,4 +90,6 @@
 	/** Link to list of services in category (category_t.services) */
 	link_t cat_services;
+	/** List of category memberships (svc_categ_t) */
+	list_t cat_memb;
 	/** Unique service identifier */
 	service_id_t id;
@@ -101,5 +104,8 @@
 } loc_service_t;
 
+extern fibril_mutex_t services_list_mutex;
+
 extern service_id_t loc_create_id(void);
+extern void loc_category_change_event(void);
 
 #endif
Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/vfs/vfs.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -36,5 +36,8 @@
  */
 
+#include <vfs/vfs.h>
 #include <ipc/services.h>
+#include <abi/ipc/event.h>
+#include <event.h>
 #include <ns.h>
 #include <async.h>
@@ -45,7 +48,12 @@
 #include <as.h>
 #include <atomic.h>
+#include <macros.h>
 #include "vfs.h"
 
 #define NAME  "vfs"
+
+enum {
+	VFS_TASK_STATE_CHANGE
+};
 
 static void vfs_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
@@ -80,7 +88,4 @@
 			vfs_open(callid, &call);
 			break;
-		case VFS_IN_OPEN_NODE:
-			vfs_open_node(callid, &call);
-			break;
 		case VFS_IN_CLOSE:
 			vfs_close(callid, &call);
@@ -118,4 +123,8 @@
 		case VFS_IN_DUP:
 			vfs_dup(callid, &call);
+			break;
+		case VFS_IN_WAIT_HANDLE:
+			vfs_wait_handle(callid, &call);
+			break;
 		default:
 			async_answer_0(callid, ENOTSUP);
@@ -128,4 +137,19 @@
 	 * connection fibril terminates.
 	 */
+}
+
+static void notification_received(ipc_callid_t callid, ipc_call_t *call)
+{
+	switch (IPC_GET_IMETHOD(*call)) {
+	case VFS_TASK_STATE_CHANGE:
+		if (IPC_GET_ARG1(*call) == VFS_PASS_HANDLE)
+			vfs_pass_handle(
+			    (task_id_t) MERGE_LOUP32(IPC_GET_ARG4(*call),
+			    IPC_GET_ARG5(*call)), call->in_task_id,
+			    (int) IPC_GET_ARG2(*call));
+		break;
+	default:
+		break;
+	}
 }
 
@@ -170,4 +194,10 @@
 
 	/*
+	 * Set notification handler and subscribe to notifications.
+	 */
+	async_set_interrupt_received(notification_received);
+	event_task_subscribe(EVENT_TASK_STATE_CHANGE, VFS_TASK_STATE_CHANGE);
+
+	/*
 	 * Register at the naming service.
 	 */
Index: uspace/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/vfs/vfs.h	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -41,4 +41,5 @@
 #include <bool.h>
 #include <ipc/vfs.h>
+#include <task.h>
 
 #ifndef dprintf
@@ -175,5 +176,4 @@
 extern int vfs_lookup_internal(char *, int, vfs_lookup_res_t *,
     vfs_pair_t *, ...);
-extern int vfs_open_node_internal(vfs_lookup_res_t *);
 
 extern bool vfs_nodes_init(void);
@@ -189,4 +189,7 @@
 extern void vfs_client_data_destroy(void *);
 
+extern void vfs_pass_handle(task_id_t, task_id_t, int);
+extern int vfs_wait_handle_internal(void);
+
 extern vfs_file_t *vfs_file_get(int);
 extern void vfs_file_put(vfs_file_t *);
@@ -197,4 +200,5 @@
 extern void vfs_node_addref(vfs_node_t *);
 extern void vfs_node_delref(vfs_node_t *);
+extern int vfs_open_node_remote(vfs_node_t *);
 
 extern void vfs_register(ipc_callid_t, ipc_call_t *);
@@ -202,5 +206,4 @@
 extern void vfs_unmount(ipc_callid_t, ipc_call_t *);
 extern void vfs_open(ipc_callid_t, ipc_call_t *);
-extern void vfs_open_node(ipc_callid_t, ipc_call_t *);
 extern void vfs_sync(ipc_callid_t, ipc_call_t *);
 extern void vfs_dup(ipc_callid_t, ipc_call_t *);
@@ -215,4 +218,5 @@
 extern void vfs_unlink(ipc_callid_t, ipc_call_t *);
 extern void vfs_rename(ipc_callid_t, ipc_call_t *);
+extern void vfs_wait_handle(ipc_callid_t, ipc_call_t *);
 
 #endif
Index: uspace/srv/vfs/vfs_file.c
===================================================================
--- uspace/srv/vfs/vfs_file.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/vfs/vfs_file.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -43,4 +43,6 @@
 #include <fibril.h>
 #include <fibril_synch.h>
+#include <adt/list.h>
+#include <task.h>
 #include "vfs.h"
 
@@ -50,38 +52,57 @@
 typedef struct {
 	fibril_mutex_t lock;
+	fibril_condvar_t cv;
+	list_t passed_handles;
 	vfs_file_t **files;
 } vfs_client_data_t;
 
+typedef struct {
+	link_t link;
+	int handle;
+} vfs_boxed_handle_t;
+
+static int _vfs_fd_free(vfs_client_data_t *, int);
+
 /** Initialize the table of open files. */
-static bool vfs_files_init(void)
-{
-	fibril_mutex_lock(&VFS_DATA->lock);
-	if (!FILES) {
-		FILES = malloc(MAX_OPEN_FILES * sizeof(vfs_file_t *));
-		if (!FILES) {
-			fibril_mutex_unlock(&VFS_DATA->lock);
+static bool vfs_files_init(vfs_client_data_t *vfs_data)
+{
+	fibril_mutex_lock(&vfs_data->lock);
+	if (!vfs_data->files) {
+		vfs_data->files = malloc(MAX_OPEN_FILES * sizeof(vfs_file_t *));
+		if (!vfs_data->files) {
+			fibril_mutex_unlock(&vfs_data->lock);
 			return false;
 		}
-		memset(FILES, 0, MAX_OPEN_FILES * sizeof(vfs_file_t *));
-	}
-	fibril_mutex_unlock(&VFS_DATA->lock);
+		memset(vfs_data->files, 0, MAX_OPEN_FILES * sizeof(vfs_file_t *));
+	}
+	fibril_mutex_unlock(&vfs_data->lock);
 	return true;
 }
 
 /** Cleanup the table of open files. */
-static void vfs_files_done(void)
+static void vfs_files_done(vfs_client_data_t *vfs_data)
 {
 	int i;
 
-	if (!FILES)
+	if (!vfs_data->files)
 		return;
 
 	for (i = 0; i < MAX_OPEN_FILES; i++) {
-		if (FILES[i]) {
-			(void) vfs_fd_free(i);
-		}
-	}
-	
-	free(FILES);
+		if (vfs_data->files[i])
+			(void) _vfs_fd_free(vfs_data, i);
+	}
+	
+	free(vfs_data->files);
+
+	while (!list_empty(&vfs_data->passed_handles)) {
+		link_t *lnk;
+		vfs_boxed_handle_t *bh;
+		
+		lnk = list_first(&vfs_data->passed_handles);
+		list_remove(lnk);
+
+		bh = list_get_instance(lnk, vfs_boxed_handle_t, link);
+		free(bh);
+	}
 }
 
@@ -93,4 +114,6 @@
 	if (vfs_data) {
 		fibril_mutex_initialize(&vfs_data->lock);
+		fibril_condvar_initialize(&vfs_data->cv);
+		list_initialize(&vfs_data->passed_handles);
 		vfs_data->files = NULL;
 	}
@@ -103,5 +126,5 @@
 	vfs_client_data_t *vfs_data = (vfs_client_data_t *) data;
 
-	vfs_files_done();
+	vfs_files_done(vfs_data);
 	free(vfs_data);
 }
@@ -131,7 +154,7 @@
  *			incremented.
  */
-static void vfs_file_addref(vfs_file_t *file)
-{
-	assert(fibril_mutex_is_locked(&VFS_DATA->lock));
+static void vfs_file_addref(vfs_client_data_t *vfs_data, vfs_file_t *file)
+{
+	assert(fibril_mutex_is_locked(&vfs_data->lock));
 
 	file->refcnt++;
@@ -143,9 +166,9 @@
  *			decremented.
  */
-static int vfs_file_delref(vfs_file_t *file)
+static int vfs_file_delref(vfs_client_data_t *vfs_data, vfs_file_t *file)
 {
 	int rc = EOK;
 
-	assert(fibril_mutex_is_locked(&VFS_DATA->lock));
+	assert(fibril_mutex_is_locked(&vfs_data->lock));
 
 	if (file->refcnt-- == 1) {
@@ -162,16 +185,7 @@
 }
 
-
-/** Allocate a file descriptor.
- *
- * @param desc If true, look for an available file descriptor
- *             in a descending order.
- *
- * @return First available file descriptor or a negative error
- *         code.
- */
-int vfs_fd_alloc(bool desc)
-{
-	if (!vfs_files_init())
+static int _vfs_fd_alloc(vfs_client_data_t *vfs_data, bool desc)
+{
+	if (!vfs_files_init(vfs_data))
 		return ENOMEM;
 	
@@ -182,17 +196,17 @@
 		i = 0;
 	
-	fibril_mutex_lock(&VFS_DATA->lock);
+	fibril_mutex_lock(&vfs_data->lock);
 	while (true) {
-		if (!FILES[i]) {
-			FILES[i] = (vfs_file_t *) malloc(sizeof(vfs_file_t));
-			if (!FILES[i]) {
-				fibril_mutex_unlock(&VFS_DATA->lock);
+		if (!vfs_data->files[i]) {
+			vfs_data->files[i] = (vfs_file_t *) malloc(sizeof(vfs_file_t));
+			if (!vfs_data->files[i]) {
+				fibril_mutex_unlock(&vfs_data->lock);
 				return ENOMEM;
 			}
 			
-			memset(FILES[i], 0, sizeof(vfs_file_t));
-			fibril_mutex_initialize(&FILES[i]->lock);
-			vfs_file_addref(FILES[i]);
-			fibril_mutex_unlock(&VFS_DATA->lock);
+			memset(vfs_data->files[i], 0, sizeof(vfs_file_t));
+			fibril_mutex_initialize(&vfs_data->files[i]->lock);
+			vfs_file_addref(vfs_data, vfs_data->files[i]);
+			fibril_mutex_unlock(&vfs_data->lock);
 			return (int) i;
 		}
@@ -210,7 +224,40 @@
 		}
 	}
-	fibril_mutex_unlock(&VFS_DATA->lock);
+	fibril_mutex_unlock(&vfs_data->lock);
 	
 	return EMFILE;
+}
+
+/** Allocate a file descriptor.
+ *
+ * @param desc If true, look for an available file descriptor
+ *             in a descending order.
+ *
+ * @return First available file descriptor or a negative error
+ *         code.
+ */
+int vfs_fd_alloc(bool desc)
+{
+	return _vfs_fd_alloc(VFS_DATA, desc);
+}
+
+static int _vfs_fd_free(vfs_client_data_t *vfs_data, int fd)
+{
+	int rc;
+
+	if (!vfs_files_init(vfs_data))
+		return ENOMEM;
+
+	fibril_mutex_lock(&vfs_data->lock);	
+	if ((fd < 0) || (fd >= MAX_OPEN_FILES) || !vfs_data->files[fd]) {
+		fibril_mutex_unlock(&vfs_data->lock);
+		return EBADF;
+	}
+	
+	rc = vfs_file_delref(vfs_data, vfs_data->files[fd]);
+	vfs_data->files[fd] = NULL;
+	fibril_mutex_unlock(&vfs_data->lock);
+	
+	return rc;
 }
 
@@ -224,20 +271,5 @@
 int vfs_fd_free(int fd)
 {
-	int rc;
-
-	if (!vfs_files_init())
-		return ENOMEM;
-
-	fibril_mutex_lock(&VFS_DATA->lock);	
-	if ((fd < 0) || (fd >= MAX_OPEN_FILES) || (FILES[fd] == NULL)) {
-		fibril_mutex_unlock(&VFS_DATA->lock);
-		return EBADF;
-	}
-	
-	rc = vfs_file_delref(FILES[fd]);
-	FILES[fd] = NULL;
-	fibril_mutex_unlock(&VFS_DATA->lock);
-	
-	return rc;
+	return _vfs_fd_free(VFS_DATA, fd);
 }
 
@@ -253,5 +285,5 @@
 int vfs_fd_assign(vfs_file_t *file, int fd)
 {
-	if (!vfs_files_init())
+	if (!vfs_files_init(VFS_DATA))
 		return ENOMEM;
 
@@ -263,5 +295,5 @@
 	
 	FILES[fd] = file;
-	vfs_file_addref(FILES[fd]);
+	vfs_file_addref(VFS_DATA, FILES[fd]);
 	fibril_mutex_unlock(&VFS_DATA->lock);
 	
@@ -269,29 +301,41 @@
 }
 
-/** Find VFS file structure for a given file descriptor.
- *
- * @param fd		File descriptor.
- *
- * @return		VFS file structure corresponding to fd.
- */
-vfs_file_t *vfs_file_get(int fd)
-{
-	if (!vfs_files_init())
+static vfs_file_t *_vfs_file_get(vfs_client_data_t *vfs_data, int fd)
+{
+	if (!vfs_files_init(vfs_data))
 		return NULL;
 	
-	fibril_mutex_lock(&VFS_DATA->lock);
+	fibril_mutex_lock(&vfs_data->lock);
 	if ((fd >= 0) && (fd < MAX_OPEN_FILES)) {
-		vfs_file_t *file = FILES[fd];
+		vfs_file_t *file = vfs_data->files[fd];
 		if (file != NULL) {
-			vfs_file_addref(file);
-			fibril_mutex_unlock(&VFS_DATA->lock);
+			vfs_file_addref(vfs_data, file);
+			fibril_mutex_unlock(&vfs_data->lock);
 			return file;
 		}
 	}
-	fibril_mutex_unlock(&VFS_DATA->lock);
+	fibril_mutex_unlock(&vfs_data->lock);
 	
 	return NULL;
 }
 
+/** Find VFS file structure for a given file descriptor.
+ *
+ * @param fd		File descriptor.
+ *
+ * @return		VFS file structure corresponding to fd.
+ */
+vfs_file_t *vfs_file_get(int fd)
+{
+	return _vfs_file_get(VFS_DATA, fd);
+}
+
+static void _vfs_file_put(vfs_client_data_t *vfs_data, vfs_file_t *file)
+{
+	fibril_mutex_lock(&vfs_data->lock);
+	vfs_file_delref(vfs_data, file);
+	fibril_mutex_unlock(&vfs_data->lock);
+}
+
 /** Stop using a file structure.
  *
@@ -300,7 +344,90 @@
 void vfs_file_put(vfs_file_t *file)
 {
-	fibril_mutex_lock(&VFS_DATA->lock);
-	vfs_file_delref(file);
-	fibril_mutex_unlock(&VFS_DATA->lock);
+	_vfs_file_put(VFS_DATA, file);
+}
+
+void vfs_pass_handle(task_id_t donor_id, task_id_t acceptor_id, int donor_fd)
+{
+	vfs_client_data_t *donor_data = NULL;
+	vfs_client_data_t *acceptor_data = NULL;
+	vfs_file_t *donor_file = NULL;
+	vfs_file_t *acceptor_file = NULL;
+	vfs_boxed_handle_t *bh;
+	int acceptor_fd;
+
+	acceptor_data = async_get_client_data_by_id(acceptor_id);
+	if (!acceptor_data)
+		return;
+
+	bh = malloc(sizeof(vfs_boxed_handle_t));
+	assert(bh);
+
+	link_initialize(&bh->link);
+	bh->handle = -1;
+
+	donor_data = async_get_client_data_by_id(donor_id);
+	if (!donor_data)
+		goto out;
+
+	donor_file = _vfs_file_get(donor_data, donor_fd);
+	if (!donor_file)
+		goto out;
+
+	acceptor_fd = _vfs_fd_alloc(acceptor_data, false);
+	if (acceptor_fd < 0)
+		goto out;
+
+	bh->handle = acceptor_fd;
+
+	/*
+	 * Add a new reference to the underlying VFS node.
+	 */
+	vfs_node_addref(donor_file->node);
+	(void) vfs_open_node_remote(donor_file->node);
+
+	acceptor_file = _vfs_file_get(acceptor_data, acceptor_fd);
+	assert(acceptor_file);
+
+	/*
+	 * Inherit attributes from the donor.
+	 */
+	acceptor_file->node = donor_file->node;
+	acceptor_file->append = donor_file->append;
+	acceptor_file->pos = donor_file->pos;
+
+out:
+	fibril_mutex_lock(&acceptor_data->lock);
+	list_append(&bh->link, &acceptor_data->passed_handles);
+	fibril_condvar_broadcast(&acceptor_data->cv);
+	fibril_mutex_unlock(&acceptor_data->lock);
+
+	if (donor_data)
+		async_put_client_data_by_id(donor_id);
+	if (acceptor_data)
+		async_put_client_data_by_id(acceptor_id);
+	if (donor_file)
+		_vfs_file_put(donor_data, donor_file);
+	if (acceptor_file)
+		_vfs_file_put(acceptor_data, acceptor_file);
+
+}
+
+int vfs_wait_handle_internal(void)
+{
+	vfs_client_data_t *vfs_data = VFS_DATA;	
+	int fd;
+	
+	fibril_mutex_lock(&vfs_data->lock);
+	while (list_empty(&vfs_data->passed_handles))
+		fibril_condvar_wait(&vfs_data->cv, &vfs_data->lock);
+	link_t *lnk = list_first(&vfs_data->passed_handles);
+	list_remove(lnk);
+	fibril_mutex_unlock(&vfs_data->lock);
+
+	vfs_boxed_handle_t *bh = list_get_instance(lnk, vfs_boxed_handle_t, link);
+	fd = bh->handle;
+	free(bh);
+
+	return fd;
 }
 
Index: uspace/srv/vfs/vfs_lookup.c
===================================================================
--- uspace/srv/vfs/vfs_lookup.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/vfs/vfs_lookup.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -201,37 +201,4 @@
 }
 
-/** Perform a node open operation.
- *
- * @return EOK on success or an error code from errno.h.
- *
- */
-int vfs_open_node_internal(vfs_lookup_res_t *result)
-{
-	async_exch_t *exch = vfs_exchange_grab(result->triplet.fs_handle);
-	
-	ipc_call_t answer;
-	aid_t req = async_send_2(exch, VFS_OUT_OPEN_NODE,
-	    (sysarg_t) result->triplet.service_id,
-	    (sysarg_t) result->triplet.index, &answer);
-	
-	sysarg_t rc;
-	async_wait_for(req, &rc);
-	vfs_exchange_release(exch);
-	
-	if (rc == EOK) {
-		result->size =
-		    MERGE_LOUP32(IPC_GET_ARG1(answer), IPC_GET_ARG2(answer));
-		result->lnkcnt = (unsigned int) IPC_GET_ARG3(answer);
-		if (IPC_GET_ARG4(answer) & L_FILE)
-			result->type = VFS_NODE_FILE;
-		else if (IPC_GET_ARG4(answer) & L_DIRECTORY)
-			result->type = VFS_NODE_DIRECTORY;
-		else
-			result->type = VFS_NODE_UNKNOWN;
-	}
-	
-	return rc;
-}
-
 /**
  * @}
Index: uspace/srv/vfs/vfs_node.c
===================================================================
--- uspace/srv/vfs/vfs_node.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/vfs/vfs_node.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -293,4 +293,26 @@
 }
 
+
+/** Perform a remote node open operation.
+ *
+ * @return EOK on success or an error code from errno.h.
+ *
+ */
+int vfs_open_node_remote(vfs_node_t *node)
+{
+	async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
+	
+	ipc_call_t answer;
+	aid_t req = async_send_2(exch, VFS_OUT_OPEN_NODE,
+	    (sysarg_t) node->service_id, (sysarg_t) node->index, &answer);
+	
+	vfs_exchange_release(exch);
+
+	sysarg_t rc;
+	async_wait_for(req, &rc);
+	
+	return rc;
+}
+
 /**
  * @}
Index: uspace/srv/vfs/vfs_ops.c
===================================================================
--- uspace/srv/vfs/vfs_ops.c	(revision 0cf27ee8c50ec7e07748f2e04672e911b9e30b77)
+++ uspace/srv/vfs/vfs_ops.c	(revision 07b3933873be077318b66f2e001bce5785804448)
@@ -618,77 +618,4 @@
 }
 
-void vfs_open_node(ipc_callid_t rid, ipc_call_t *request)
-{
-	// FIXME: check for sanity of the supplied fs, dev and index
-	
-	/*
-	 * The interface is open_node(fs, dev, index, oflag).
-	 */
-	vfs_lookup_res_t lr;
-	
-	lr.triplet.fs_handle = IPC_GET_ARG1(*request);
-	lr.triplet.service_id = IPC_GET_ARG2(*request);
-	lr.triplet.index = IPC_GET_ARG3(*request);
-	int oflag = IPC_GET_ARG4(*request);
-	
-	fibril_rwlock_read_lock(&namespace_rwlock);
-	
-	int rc = vfs_open_node_internal(&lr);
-	if (rc != EOK) {
-		fibril_rwlock_read_unlock(&namespace_rwlock);
-		async_answer_0(rid, rc);
-		return;
-	}
-	
-	vfs_node_t *node = vfs_node_get(&lr);
-	fibril_rwlock_read_unlock(&namespace_rwlock);
-	
-	/* Truncate the file if requested and if necessary. */
-	if (oflag & O_TRUNC) {
-		fibril_rwlock_write_lock(&node->contents_rwlock);
-		if (node->size) {
-			rc = vfs_truncate_internal(node->fs_handle,
-			    node->service_id, node->index, 0);
-			if (rc) {
-				fibril_rwlock_write_unlock(&node->contents_rwlock);
-				vfs_node_put(node);
-				async_answer_0(rid, rc);
-				return;
-			}
-			node->size = 0;
-		}
-		fibril_rwlock_write_unlock(&node->contents_rwlock);
-	}
-	
-	/*
-	 * Get ourselves a file descriptor and the corresponding vfs_file_t
-	 * structure.
-	 */
-	int fd = vfs_fd_alloc((oflag & O_DESC) != 0);
-	if (fd < 0) {
-		vfs_node_put(node);
-		async_answer_0(rid, fd);
-		return;
-	}
-	vfs_file_t *file = vfs_file_get(fd);
-	file->node = node;
-	if (oflag & O_APPEND)
-		file->append = true;
-	
-	/*
-	 * The following increase in reference count is for the fact that the
-	 * file is being opened and that a file structure is pointing to it.
-	 * It is necessary so that the file will not disappear when
-	 * vfs_node_put() is called. The reference will be dropped by the
-	 * respective VFS_IN_CLOSE.
-	 */
-	vfs_node_addref(node);
-	vfs_node_put(node);
-	vfs_file_put(file);
-	
-	/* Success! Return the new file descriptor to the client. */
-	async_answer_1(rid, EOK, fd);
-}
-
 void vfs_sync(ipc_callid_t rid, ipc_call_t *request)
 {
@@ -1349,4 +1276,10 @@
 }
 
+void vfs_wait_handle(ipc_callid_t rid, ipc_call_t *request)
+{
+	int fd = vfs_wait_handle_internal();
+	async_answer_1(rid, EOK, fd);
+}
+
 /**
  * @}
