Index: .bzrignore
===================================================================
--- .bzrignore	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ .bzrignore	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -5,4 +5,5 @@
 Makefile.common
 Makefile.config
+cscope.out
 autotool/
 common.h
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ boot/Makefile.common	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -207,5 +207,6 @@
 	$(USPACE_PATH)/app/websrv/websrv \
 	$(USPACE_PATH)/app/date/date \
-	$(USPACE_PATH)/app/vdemo/vdemo
+	$(USPACE_PATH)/app/vdemo/vdemo \
+	$(USPACE_PATH)/app/df/df
 
 ifeq ($(CONFIG_PCC),y)
Index: kernel/Makefile
===================================================================
--- kernel/Makefile	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -220,5 +220,4 @@
 	generic/src/mm/km.c \
 	generic/src/mm/reserve.c \
-	generic/src/mm/buddy.c \
 	generic/src/mm/frame.c \
 	generic/src/mm/page.c \
Index: kernel/arch/abs32le/include/arch/mm/page.h
===================================================================
--- kernel/arch/abs32le/include/arch/mm/page.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/abs32le/include/arch/mm/page.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -57,8 +57,8 @@
 
 /* Page table sizes for each level. */
-#define PTL0_SIZE_ARCH  ONE_FRAME
-#define PTL1_SIZE_ARCH  0
-#define PTL2_SIZE_ARCH  0
-#define PTL3_SIZE_ARCH  ONE_FRAME
+#define PTL0_FRAMES_ARCH  1
+#define PTL1_FRAMES_ARCH  1
+#define PTL2_FRAMES_ARCH  1
+#define PTL3_FRAMES_ARCH  1
 
 /* Macros calculating indices for each level. */
Index: kernel/arch/amd64/include/arch/mm/page.h
===================================================================
--- kernel/arch/amd64/include/arch/mm/page.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/amd64/include/arch/mm/page.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -61,8 +61,8 @@
 
 /* Page table sizes for each level. */
-#define PTL0_SIZE_ARCH  ONE_FRAME
-#define PTL1_SIZE_ARCH  ONE_FRAME
-#define PTL2_SIZE_ARCH  ONE_FRAME
-#define PTL3_SIZE_ARCH  ONE_FRAME
+#define PTL0_FRAMES_ARCH  1
+#define PTL1_FRAMES_ARCH  1
+#define PTL2_FRAMES_ARCH  1
+#define PTL3_FRAMES_ARCH  1
 
 /* Macros calculating indices into page tables in each level. */
Index: kernel/arch/amd64/src/ddi/ddi.c
===================================================================
--- kernel/arch/amd64/src/ddi/ddi.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/amd64/src/ddi/ddi.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -68,13 +68,13 @@
 		 */
 		
-		void *store = malloc(bitmap_size(elements, 0), FRAME_ATOMIC);
+		void *store = malloc(bitmap_size(elements), FRAME_ATOMIC);
 		if (!store)
 			return ENOMEM;
 		
 		bitmap_t oldiomap;
-		bitmap_initialize(&oldiomap, task->arch.iomap.elements, 0,
+		bitmap_initialize(&oldiomap, task->arch.iomap.elements,
 		    task->arch.iomap.bits);
 		
-		bitmap_initialize(&task->arch.iomap, elements, 0, store);
+		bitmap_initialize(&task->arch.iomap, elements, store);
 		
 		/*
@@ -129,5 +129,5 @@
 		
 		bitmap_t iomap;
-		bitmap_initialize(&iomap, TSS_IOMAP_SIZE * 8, 0,
+		bitmap_initialize(&iomap, TSS_IOMAP_SIZE * 8,
 		    CPU->arch.tss->iomap);
 		bitmap_copy(&iomap, &TASK->arch.iomap, elements);
@@ -157,5 +157,5 @@
 	
 	descriptor_t *gdt_p = (descriptor_t *) cpugdtr.base;
-	size_t size = bitmap_size(elements, 0);
+	size_t size = bitmap_size(elements);
 	gdt_tss_setlimit(&gdt_p[TSS_DES], TSS_BASIC_SIZE + size);
 	gdtr_load(&cpugdtr);
Index: kernel/arch/amd64/src/proc/task.c
===================================================================
--- kernel/arch/amd64/src/proc/task.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/amd64/src/proc/task.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -46,5 +46,5 @@
 {
 	task->arch.iomapver = 0;
-	bitmap_initialize(&task->arch.iomap, 0, 0, NULL);
+	bitmap_initialize(&task->arch.iomap, 0, NULL);
 }
 
Index: kernel/arch/arm32/include/arch/mm/page.h
===================================================================
--- kernel/arch/arm32/include/arch/mm/page.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/arm32/include/arch/mm/page.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -73,8 +73,8 @@
 
 /* Page table sizes for each level. */
-#define PTL0_SIZE_ARCH          FOUR_FRAMES
-#define PTL1_SIZE_ARCH          0
-#define PTL2_SIZE_ARCH          0
-#define PTL3_SIZE_ARCH          ONE_FRAME
+#define PTL0_FRAMES_ARCH  4
+#define PTL1_FRAMES_ARCH  1
+#define PTL2_FRAMES_ARCH  1
+#define PTL3_FRAMES_ARCH  1
 
 /* Macros calculating indices into page tables for each level. */
Index: kernel/arch/arm32/src/mm/frame.c
===================================================================
--- kernel/arch/arm32/src/mm/frame.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/arm32/src/mm/frame.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -88,7 +88,6 @@
 void boot_page_table_free(void)
 {
-	unsigned int i;
-	for (i = 0; i < BOOT_PAGE_TABLE_SIZE_IN_FRAMES; i++)
-		frame_free(i * FRAME_SIZE + BOOT_PAGE_TABLE_ADDRESS);
+	frame_free(BOOT_PAGE_TABLE_ADDRESS,
+	    BOOT_PAGE_TABLE_SIZE_IN_FRAMES);
 }
 
Index: kernel/arch/arm32/src/mm/page.c
===================================================================
--- kernel/arch/arm32/src/mm/page.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/arm32/src/mm/page.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -69,5 +69,5 @@
 #ifdef HIGH_EXCEPTION_VECTORS
 	/* Create mapping for exception table at high offset */
-	uintptr_t ev_frame = frame_alloc(ONE_FRAME, FRAME_NONE, 0);
+	uintptr_t ev_frame = frame_alloc(1, FRAME_NONE, 0);
 	page_mapping_insert(AS_KERNEL, EXC_BASE_ADDRESS, ev_frame, flags);
 #else
Index: kernel/arch/arm32/src/ras.c
===================================================================
--- kernel/arch/arm32/src/ras.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/arm32/src/ras.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -51,8 +51,8 @@
 void ras_init(void)
 {
-	uintptr_t frame = frame_alloc(ONE_FRAME,
-	    FRAME_ATOMIC | FRAME_HIGHMEM, 0);
+	uintptr_t frame =
+	    frame_alloc(1, FRAME_ATOMIC | FRAME_HIGHMEM, 0);
 	if (!frame)
-		frame = frame_alloc(ONE_FRAME, FRAME_LOWMEM, 0);
+		frame = frame_alloc(1, FRAME_LOWMEM, 0);
 	
 	ras_page = (uintptr_t *) km_map(frame,
Index: kernel/arch/ia32/include/arch/mm/page.h
===================================================================
--- kernel/arch/ia32/include/arch/mm/page.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/ia32/include/arch/mm/page.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -66,8 +66,8 @@
 
 /* Page table sizes for each level. */
-#define PTL0_SIZE_ARCH  ONE_FRAME
-#define PTL1_SIZE_ARCH  0
-#define PTL2_SIZE_ARCH  0
-#define PTL3_SIZE_ARCH  ONE_FRAME
+#define PTL0_FRAMES_ARCH  1
+#define PTL1_FRAMES_ARCH  1
+#define PTL2_FRAMES_ARCH  1
+#define PTL3_FRAMES_ARCH  1
 
 /* Macros calculating indices for each level. */
Index: kernel/arch/ia32/src/ddi/ddi.c
===================================================================
--- kernel/arch/ia32/src/ddi/ddi.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/ia32/src/ddi/ddi.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -68,13 +68,13 @@
 		 */
 		
-		void *store = malloc(bitmap_size(elements, 0), FRAME_ATOMIC);
+		void *store = malloc(bitmap_size(elements), FRAME_ATOMIC);
 		if (!store)
 			return ENOMEM;
 		
 		bitmap_t oldiomap;
-		bitmap_initialize(&oldiomap, task->arch.iomap.elements, 0,
+		bitmap_initialize(&oldiomap, task->arch.iomap.elements,
 		    task->arch.iomap.bits);
 		
-		bitmap_initialize(&task->arch.iomap, elements, 0, store);
+		bitmap_initialize(&task->arch.iomap, elements, store);
 		
 		/*
@@ -129,5 +129,5 @@
 		
 		bitmap_t iomap;
-		bitmap_initialize(&iomap, TSS_IOMAP_SIZE * 8, 0,
+		bitmap_initialize(&iomap, TSS_IOMAP_SIZE * 8,
 		    CPU->arch.tss->iomap);
 		bitmap_copy(&iomap, &TASK->arch.iomap, elements);
@@ -157,5 +157,5 @@
 	
 	descriptor_t *gdt_p = (descriptor_t *) cpugdtr.base;
-	size_t size = bitmap_size(elements, 0);
+	size_t size = bitmap_size(elements);
 	gdt_setlimit(&gdt_p[TSS_DES], TSS_BASIC_SIZE + size);
 	gdtr_load(&cpugdtr);
Index: kernel/arch/ia32/src/proc/task.c
===================================================================
--- kernel/arch/ia32/src/proc/task.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/ia32/src/proc/task.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -46,5 +46,5 @@
 {
 	task->arch.iomapver = 0;
-	bitmap_initialize(&task->arch.iomap, 0, 0, NULL);
+	bitmap_initialize(&task->arch.iomap, 0, NULL);
 }
 
Index: kernel/arch/ia64/src/ddi/ddi.c
===================================================================
--- kernel/arch/ia64/src/ddi/ddi.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/ia64/src/ddi/ddi.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2006 Jakub Jermar
- * Copyright (c) 2008 Jakub vana
+ * Copyright (c) 2008 Jakub Vana
  * All rights reserved.
  *
@@ -60,9 +60,9 @@
 			return ENOMEM;
 		
-		void *store = malloc(bitmap_size(IO_MEMMAP_PAGES, 0), 0);
+		void *store = malloc(bitmap_size(IO_MEMMAP_PAGES), 0);
 		if (store == NULL)
 			return ENOMEM;
 		
-		bitmap_initialize(task->arch.iomap, IO_MEMMAP_PAGES, 0, store);
+		bitmap_initialize(task->arch.iomap, IO_MEMMAP_PAGES, store);
 		bitmap_clear_range(task->arch.iomap, 0, IO_MEMMAP_PAGES);
 	}
Index: kernel/arch/ia64/src/mm/vhpt.c
===================================================================
--- kernel/arch/ia64/src/mm/vhpt.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/ia64/src/mm/vhpt.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -42,9 +42,10 @@
 uintptr_t vhpt_set_up(void)
 {
-	vhpt_base = (vhpt_entry_t *) PA2KA(frame_alloc(VHPT_WIDTH - FRAME_WIDTH,
-	    FRAME_ATOMIC, 0));
-	if (!vhpt_base)
+	uintptr_t vhpt_frame =
+	    frame_alloc(SIZE2FRAMES(VHPT_SIZE), FRAME_ATOMIC, 0);
+	if (!vhpt_frame)
 		panic("Kernel configured with VHPT but no memory for table.");
 	
+	vhpt_base = (vhpt_entry_t *) PA2KA(vhpt_frame);
 	vhpt_invalidate_all();
 	return (uintptr_t) vhpt_base;
@@ -83,5 +84,5 @@
 void vhpt_invalidate_all()
 {
-	memsetb(vhpt_base, 1 << VHPT_WIDTH, 0);
+	memsetb(vhpt_base, VHPT_SIZE, 0);
 }
 
Index: kernel/arch/mips32/include/arch/mm/page.h
===================================================================
--- kernel/arch/mips32/include/arch/mm/page.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/mips32/include/arch/mm/page.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -78,8 +78,8 @@
 
 /* Macros describing size of page tables in each level. */
-#define PTL0_SIZE_ARCH		ONE_FRAME
-#define PTL1_SIZE_ARCH		0
-#define PTL2_SIZE_ARCH		0
-#define PTL3_SIZE_ARCH		ONE_FRAME
+#define PTL0_FRAMES_ARCH  1
+#define PTL1_FRAMES_ARCH  1
+#define PTL2_FRAMES_ARCH  1
+#define PTL3_FRAMES_ARCH  1
 
 /* Macros calculating entry indices for each level. */
@@ -196,5 +196,4 @@
 	p->p = 1;
 }
-	
 
 extern void page_arch_init(void);
Index: kernel/arch/mips32/src/mach/malta/malta.c
===================================================================
--- kernel/arch/mips32/src/mach/malta/malta.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/mips32/src/mach/malta/malta.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -103,4 +103,5 @@
 void malta_input_init(void)
 {
+	(void) stdin_wire();
 }
 
Index: kernel/arch/mips32/src/mm/tlb.c
===================================================================
--- kernel/arch/mips32/src/mm/tlb.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/mips32/src/mm/tlb.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -48,14 +48,16 @@
 #include <symtab.h>
 
-#define PFN_SHIFT	12
-#define VPN_SHIFT	12
-#define ADDR2VPN(a)	((a) >> VPN_SHIFT)
-#define ADDR2VPN2(a)	(ADDR2VPN((a)) >> 1)
-#define VPN2ADDR(vpn)	((vpn) << VPN_SHIFT)
-#define VPN22ADDR(vpn2)	(VPN2ADDR(vpn2) << 1)
-#define PFN2ADDR(pfn)	((pfn) << PFN_SHIFT)
-
-#define BANK_SELECT_BIT(a)	(((a) >> PAGE_WIDTH) & 1) 
-	
+#define PFN_SHIFT  12
+#define VPN_SHIFT  12
+
+#define ADDR2HI_VPN(a)   ((a) >> VPN_SHIFT)
+#define ADDR2HI_VPN2(a)  (ADDR2HI_VPN((a)) >> 1)
+
+#define HI_VPN2ADDR(vpn)    ((vpn) << VPN_SHIFT)
+#define HI_VPN22ADDR(vpn2)  (HI_VPN2ADDR(vpn2) << 1)
+
+#define LO_PFN2ADDR(pfn)  ((pfn) << PFN_SHIFT)
+
+#define BANK_SELECT_BIT(a)  (((a) >> PAGE_WIDTH) & 1)
 
 /** Initialize TLB.
@@ -266,5 +268,5 @@
 {
 	hi->value = 0;
-	hi->vpn2 = ADDR2VPN2(ALIGN_DOWN(addr, PAGE_SIZE));
+	hi->vpn2 = ADDR2HI_VPN2(ALIGN_DOWN(addr, PAGE_SIZE));
 	hi->asid = asid;
 }
@@ -295,8 +297,8 @@
 		
 		printf("%-4u %-6u %0#10x %-#6x  %1u%1u%1u%1u  %0#10x\n",
-		    i, hi.asid, VPN22ADDR(hi.vpn2), mask.mask,
-		    lo0.g, lo0.v, lo0.d, lo0.c, PFN2ADDR(lo0.pfn));
+		    i, hi.asid, HI_VPN22ADDR(hi.vpn2), mask.mask,
+		    lo0.g, lo0.v, lo0.d, lo0.c, LO_PFN2ADDR(lo0.pfn));
 		printf("                               %1u%1u%1u%1u  %0#10x\n",
-		    lo1.g, lo1.v, lo1.d, lo1.c, PFN2ADDR(lo1.pfn));
+		    lo1.g, lo1.v, lo1.d, lo1.c, LO_PFN2ADDR(lo1.pfn));
 	}
 	
Index: kernel/arch/ppc32/include/arch/mm/page.h
===================================================================
--- kernel/arch/ppc32/include/arch/mm/page.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/ppc32/include/arch/mm/page.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -70,8 +70,8 @@
 
 /* Page table sizes for each level. */
-#define PTL0_SIZE_ARCH  ONE_FRAME
-#define PTL1_SIZE_ARCH  0
-#define PTL2_SIZE_ARCH  0
-#define PTL3_SIZE_ARCH  ONE_FRAME
+#define PTL0_FRAMES_ARCH  1
+#define PTL1_FRAMES_ARCH  1
+#define PTL2_FRAMES_ARCH  1
+#define PTL3_FRAMES_ARCH  1
 
 /* Macros calculating indices into page tables on each level. */
Index: kernel/arch/sparc64/src/mm/sun4u/as.c
===================================================================
--- kernel/arch/sparc64/src/mm/sun4u/as.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/sparc64/src/mm/sun4u/as.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -63,39 +63,30 @@
 {
 #ifdef CONFIG_TSB
-	/*
-	 * The order must be calculated with respect to the emulated
-	 * 16K page size.
-	 *
-	 */
-	uint8_t order = fnzb32(((ITSB_ENTRY_COUNT + DTSB_ENTRY_COUNT) *
-	    sizeof(tsb_entry_t)) >> FRAME_WIDTH);
-	
-	uintptr_t tsb = PA2KA(frame_alloc(order, flags, 0));
-	if (!tsb)
+	uintptr_t tsb_phys =
+	    frame_alloc(SIZE2FRAMES((ITSB_ENTRY_COUNT + DTSB_ENTRY_COUNT) *
+	    sizeof(tsb_entry_t)), flags, 0);
+	if (!tsb_phys)
 		return -1;
 	
-	as->arch.itsb = (tsb_entry_t *) tsb;
-	as->arch.dtsb = (tsb_entry_t *) (tsb + ITSB_ENTRY_COUNT *
+	tsb_entry_t *tsb = (tsb_entry_t *) PA2KA(tsb_phys);
+	
+	as->arch.itsb = tsb;
+	as->arch.dtsb = tsb + ITSB_ENTRY_COUNT;
+	
+	memsetb(as->arch.itsb, (ITSB_ENTRY_COUNT + DTSB_ENTRY_COUNT) *
+	    sizeof(tsb_entry_t), 0);
+#endif
+	
+	return 0;
+}
+
+int as_destructor_arch(as_t *as)
+{
+#ifdef CONFIG_TSB
+	size_t frames = SIZE2FRAMES((ITSB_ENTRY_COUNT + DTSB_ENTRY_COUNT) *
 	    sizeof(tsb_entry_t));
-	
-	memsetb(as->arch.itsb,
-	    (ITSB_ENTRY_COUNT + DTSB_ENTRY_COUNT) * sizeof(tsb_entry_t), 0);
-#endif
-	
-	return 0;
-}
-
-int as_destructor_arch(as_t *as)
-{
-#ifdef CONFIG_TSB
-	/*
-	 * The count must be calculated with respect to the emualted 16K page
-	 * size.
-	 */
-	size_t cnt = ((ITSB_ENTRY_COUNT + DTSB_ENTRY_COUNT) *
-	    sizeof(tsb_entry_t)) >> FRAME_WIDTH;
-	frame_free(KA2PA((uintptr_t) as->arch.itsb));
-	
-	return cnt;
+	frame_free(KA2PA((uintptr_t) as->arch.itsb), frames);
+	
+	return frames;
 #else
 	return 0;
Index: kernel/arch/sparc64/src/mm/sun4v/as.c
===================================================================
--- kernel/arch/sparc64/src/mm/sun4v/as.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/arch/sparc64/src/mm/sun4v/as.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -66,8 +66,7 @@
 {
 #ifdef CONFIG_TSB
-	uint8_t order = fnzb32(
-		(TSB_ENTRY_COUNT * sizeof(tsb_entry_t)) >> FRAME_WIDTH);
-	
-	uintptr_t tsb = frame_alloc(order, flags, 0);
+	uintptr_t tsb =
+	    frame_alloc(SIZE2FRAMES(TSB_ENTRY_COUNT * sizeof(tsb_entry_t)),
+	    flags, 0);
 	if (!tsb)
 		return -1;
@@ -91,8 +90,8 @@
 {
 #ifdef CONFIG_TSB
-	size_t cnt = (TSB_ENTRY_COUNT * sizeof(tsb_entry_t)) >> FRAME_WIDTH;
-	frame_free((uintptr_t) as->arch.tsb_description.tsb_base);
+	size_t frames = SIZE2FRAMES(TSB_ENTRY_COUNT * sizeof(tsb_entry_t));
+	frame_free(as->arch.tsb_description.tsb_base, frames);
 	
-	return cnt;
+	return frames;
 #else
 	return 0;
Index: kernel/genarch/include/genarch/mm/page_pt.h
===================================================================
--- kernel/genarch/include/genarch/mm/page_pt.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/genarch/include/genarch/mm/page_pt.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -58,9 +58,15 @@
 #define PTL3_ENTRIES  PTL3_ENTRIES_ARCH
 
-/* Table sizes in each level */
-#define PTL0_SIZE  PTL0_SIZE_ARCH
-#define PTL1_SIZE  PTL1_SIZE_ARCH
-#define PTL2_SIZE  PTL2_SIZE_ARCH
-#define PTL3_SIZE  PTL3_SIZE_ARCH
+/* Table sizes in each level (in frames) */
+#define PTL0_FRAMES  PTL0_FRAMES_ARCH
+#define PTL1_FRAMES  PTL1_FRAMES_ARCH
+#define PTL2_FRAMES  PTL2_FRAMES_ARCH
+#define PTL3_FRAMES  PTL3_FRAMES_ARCH
+
+/* Table sizes in each level (in bytes) */
+#define PTL0_SIZE  FRAMES2SIZE(PTL0_FRAMES)
+#define PTL1_SIZE  FRAMES2SIZE(PTL1_FRAMES)
+#define PTL2_SIZE  FRAMES2SIZE(PTL2_FRAMES)
+#define PTL3_SIZE  FRAMES2SIZE(PTL3_FRAMES)
 
 /*
Index: kernel/genarch/src/mm/as_pt.c
===================================================================
--- kernel/genarch/src/mm/as_pt.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/genarch/src/mm/as_pt.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -73,10 +73,9 @@
 pte_t *ptl0_create(unsigned int flags)
 {
-	pte_t *dst_ptl0 = (pte_t *) PA2KA(frame_alloc(PTL0_SIZE,
-	    FRAME_LOWMEM, 0));
-	size_t table_size = FRAME_SIZE << PTL0_SIZE;
+	pte_t *dst_ptl0 = (pte_t *)
+	    PA2KA(frame_alloc(PTL0_FRAMES, FRAME_LOWMEM, PTL0_SIZE - 1));
 	
 	if (flags & FLAG_AS_KERNEL)
-		memsetb(dst_ptl0, table_size, 0);
+		memsetb(dst_ptl0, PTL0_SIZE, 0);
 	else {
 		/*
@@ -94,7 +93,7 @@
 		    &dst_ptl0[PTL0_INDEX(KERNEL_ADDRESS_SPACE_START)];
 		
-		memsetb(dst_ptl0, table_size, 0);
+		memsetb(dst_ptl0, PTL0_SIZE, 0);
 		memcpy((void *) dst, (void *) src,
-		    table_size - (src - (uintptr_t) src_ptl0));
+		    PTL0_SIZE - (src - (uintptr_t) src_ptl0));
 		
 		mutex_unlock(&AS_KERNEL->lock);
@@ -113,5 +112,5 @@
 void ptl0_destroy(pte_t *page_table)
 {
-	frame_free((uintptr_t) page_table);
+	frame_free((uintptr_t) page_table, PTL0_FRAMES);
 }
 
Index: kernel/genarch/src/mm/page_pt.c
===================================================================
--- kernel/genarch/src/mm/page_pt.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/genarch/src/mm/page_pt.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -82,7 +82,7 @@
 	
 	if (GET_PTL1_FLAGS(ptl0, PTL0_INDEX(page)) & PAGE_NOT_PRESENT) {
-		pte_t *newpt = (pte_t *) PA2KA(frame_alloc(PTL1_SIZE,
-		    FRAME_LOWMEM, 0));
-		memsetb(newpt, FRAME_SIZE << PTL1_SIZE, 0);
+		pte_t *newpt = (pte_t *)
+		    PA2KA(frame_alloc(PTL1_FRAMES, FRAME_LOWMEM, PTL1_SIZE - 1));
+		memsetb(newpt, PTL1_SIZE, 0);
 		SET_PTL1_ADDRESS(ptl0, PTL0_INDEX(page), KA2PA(newpt));
 		SET_PTL1_FLAGS(ptl0, PTL0_INDEX(page),
@@ -101,7 +101,7 @@
 	
 	if (GET_PTL2_FLAGS(ptl1, PTL1_INDEX(page)) & PAGE_NOT_PRESENT) {
-		pte_t *newpt = (pte_t *) PA2KA(frame_alloc(PTL2_SIZE,
-		    FRAME_LOWMEM, 0));
-		memsetb(newpt, FRAME_SIZE << PTL2_SIZE, 0);
+		pte_t *newpt = (pte_t *)
+		    PA2KA(frame_alloc(PTL2_FRAMES, FRAME_LOWMEM, PTL2_SIZE - 1));
+		memsetb(newpt, PTL2_SIZE, 0);
 		SET_PTL2_ADDRESS(ptl1, PTL1_INDEX(page), KA2PA(newpt));
 		SET_PTL2_FLAGS(ptl1, PTL1_INDEX(page),
@@ -118,7 +118,7 @@
 	
 	if (GET_PTL3_FLAGS(ptl2, PTL2_INDEX(page)) & PAGE_NOT_PRESENT) {
-		pte_t *newpt = (pte_t *) PA2KA(frame_alloc(PTL3_SIZE,
-		    FRAME_LOWMEM, 0));
-		memsetb(newpt, FRAME_SIZE << PTL3_SIZE, 0);
+		pte_t *newpt = (pte_t *)
+		    PA2KA(frame_alloc(PTL3_FRAMES, FRAME_LOWMEM, PTL2_SIZE - 1));
+		memsetb(newpt, PTL2_SIZE, 0);
 		SET_PTL3_ADDRESS(ptl2, PTL2_INDEX(page), KA2PA(newpt));
 		SET_PTL3_FLAGS(ptl2, PTL2_INDEX(page),
@@ -219,5 +219,5 @@
 		memsetb(&ptl0[PTL0_INDEX(page)], sizeof(pte_t), 0);
 #endif
-		frame_free(KA2PA((uintptr_t) ptl3));
+		frame_free(KA2PA((uintptr_t) ptl3), PTL3_FRAMES);
 	} else {
 		/*
@@ -253,5 +253,5 @@
 		memsetb(&ptl0[PTL0_INDEX(page)], sizeof(pte_t), 0);
 #endif
-		frame_free(KA2PA((uintptr_t) ptl2));
+		frame_free(KA2PA((uintptr_t) ptl2), PTL2_FRAMES);
 	} else {
 		/*
@@ -284,5 +284,5 @@
 
 		memsetb(&ptl0[PTL0_INDEX(page)], sizeof(pte_t), 0);
-		frame_free(KA2PA((uintptr_t) ptl1));
+		frame_free(KA2PA((uintptr_t) ptl1), PTL1_FRAMES);
 	}
 #endif /* PTL1_ENTRIES != 0 */
@@ -371,12 +371,12 @@
 	uintptr_t ptl0 = PA2KA((uintptr_t) AS_KERNEL->genarch.page_table);
 	uintptr_t ptl0_step = ptl0_step_get();
-	size_t order;
+	size_t frames;
 	
 #if (PTL1_ENTRIES != 0)
-	order = PTL1_SIZE;
+	frames = PTL1_FRAMES;
 #elif (PTL2_ENTRIES != 0)
-	order = PTL2_SIZE;
+	frames = PTL2_FRAMES;
 #else
-	order = PTL3_SIZE;
+	frames = PTL3_FRAMES;
 #endif
 	
@@ -384,6 +384,6 @@
 	    addr - 1 < base + size - 1;
 	    addr += ptl0_step) {
-		uintptr_t l1 = PA2KA(frame_alloc(order, FRAME_LOWMEM, 0));
-		memsetb((void *) l1, FRAME_SIZE << order, 0);
+		uintptr_t l1 = PA2KA(frame_alloc(frames, FRAME_LOWMEM, 0));
+		memsetb((void *) l1, FRAMES2SIZE(frames), 0);
 		SET_PTL1_ADDRESS(ptl0, PTL0_INDEX(addr), KA2PA(l1));
 		SET_PTL1_FLAGS(ptl0, PTL0_INDEX(addr),
Index: kernel/generic/include/adt/bitmap.h
===================================================================
--- kernel/generic/include/adt/bitmap.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/include/adt/bitmap.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -44,7 +44,5 @@
 	size_t elements;
 	uint8_t *bits;
-	
-	size_t block_size;
-	uint8_t *blocks;
+	size_t next_fit;
 } bitmap_t;
 
@@ -52,24 +50,15 @@
     unsigned int value)
 {
-	if (element < bitmap->elements) {
-		/*
-		 * The 2nd level bitmap is conservative.
-		 * Make sure we update it properly.
-		 */
-		
-		if (value) {
-			bitmap->bits[element / BITMAP_ELEMENT] |=
-			    (1 << (element & BITMAP_REMAINER));
-		} else {
-			bitmap->bits[element / BITMAP_ELEMENT] &=
-			    ~(1 << (element & BITMAP_REMAINER));
-			
-			if (bitmap->block_size > 0) {
-				size_t block = element / bitmap->block_size;
-				
-				bitmap->blocks[block / BITMAP_ELEMENT] &=
-				    ~(1 << (block & BITMAP_REMAINER));
-			}
-		}
+	if (element >= bitmap->elements)
+		return;
+	
+	size_t byte = element / BITMAP_ELEMENT;
+	uint8_t mask = 1 << (element & BITMAP_REMAINER);
+	
+	if (value) {
+		bitmap->bits[byte] |= mask;
+	} else {
+		bitmap->bits[byte] &= ~mask;
+		bitmap->next_fit = byte;
 	}
 }
@@ -80,17 +69,17 @@
 		return 0;
 	
-	return !!((bitmap->bits)[element / BITMAP_ELEMENT] &
-	    (1 << (element & BITMAP_REMAINER)));
+	size_t byte = element / BITMAP_ELEMENT;
+	uint8_t mask = 1 << (element & BITMAP_REMAINER);
+	
+	return !!((bitmap->bits)[byte] & mask);
 }
 
-extern size_t bitmap_size(size_t, size_t);
-extern void bitmap_initialize(bitmap_t *, size_t, size_t, void *);
+extern size_t bitmap_size(size_t);
+extern void bitmap_initialize(bitmap_t *, size_t, void *);
 
 extern void bitmap_set_range(bitmap_t *, size_t, size_t);
 extern void bitmap_clear_range(bitmap_t *, size_t, size_t);
 
-extern int bitmap_find_range(bitmap_t *, size_t, size_t, size_t);
 extern int bitmap_allocate_range(bitmap_t *, size_t, size_t, size_t, size_t *);
-extern void bitmap_free_range(bitmap_t *, size_t, size_t);
 extern void bitmap_copy(bitmap_t *, bitmap_t *, size_t);
 
Index: kernel/generic/include/adt/list.h
===================================================================
--- kernel/generic/include/adt/list.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/include/adt/list.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -68,5 +68,5 @@
 
 #define list_foreach(list, member, itype, iterator) \
-	for (itype *iterator = NULL; iterator == NULL; iterator =(itype *)1) \
+	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) 1) \
 	    for (link_t *_link = (list).head.next; \
 	    iterator = list_get_instance(_link, itype, member), \
@@ -186,4 +186,5 @@
  * @return Head item of the list.
  * @return NULL if the list is empty.
+ *
  */
 static inline link_t *list_first(const list_t *list)
@@ -198,4 +199,5 @@
  * @return Head item of the list.
  * @return NULL if the list is empty.
+ *
  */
 static inline link_t *list_last(list_t *list)
Index: kernel/generic/include/config.h
===================================================================
--- kernel/generic/include/config.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/include/config.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -37,11 +37,8 @@
 
 #include <arch/mm/page.h>
+#include <macros.h>
 
-#define ONE_FRAME    0
-#define TWO_FRAMES   1
-#define FOUR_FRAMES  2
-
-#define STACK_FRAMES  TWO_FRAMES
-#define STACK_SIZE    ((1 << STACK_FRAMES) << PAGE_WIDTH)
+#define STACK_FRAMES  2
+#define STACK_SIZE    FRAMES2SIZE(STACK_FRAMES)
 
 #define STACK_SIZE_USER  (1 * 1024 * 1024)
@@ -97,5 +94,5 @@
 	/** Size of initial stack. */
 	size_t stack_size;
-
+	
 	bool identity_configured;
 	/** Base address of the kernel identity mapped memory. */
@@ -103,7 +100,7 @@
 	/** Size of the kernel identity mapped memory. */
 	size_t identity_size;
-
-	bool non_identity_configured;   
-
+	
+	bool non_identity_configured;
+	
 	/** End of physical memory. */
 	uint64_t physmem_end;
Index: kernel/generic/include/macros.h
===================================================================
--- kernel/generic/include/macros.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/include/macros.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -117,4 +117,11 @@
 	overlaps(KA2PA((x)), (szx), KA2PA((y)), (szy))
 
+#define PFN2ADDR(frame)  ((frame) << FRAME_WIDTH)
+#define ADDR2PFN(addr)   ((addr) >> FRAME_WIDTH)
+
+#define FRAMES2SIZE(frames)  ((frames) << FRAME_WIDTH)
+#define SIZE2FRAMES(size) \
+	(((size) == 0) ? 0 : ((((size) - 1) >> FRAME_WIDTH) + 1))
+
 #define KiB2SIZE(kb)  ((kb) << 10)
 #define MiB2SIZE(mb)  ((mb) << 20)
Index: rnel/generic/include/mm/buddy.h
===================================================================
--- kernel/generic/include/mm/buddy.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ 	(revision )
@@ -1,91 +1,0 @@
-/*
- * Copyright (c) 2005 Jakub Jermar
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup genericmm
- * @{
- */
-/** @file
- */
-
-#ifndef KERN_BUDDY_H_
-#define KERN_BUDDY_H_
-
-#include <typedefs.h>
-#include <adt/list.h>
-
-#define BUDDY_SYSTEM_INNER_BLOCK	0xff
-
-struct buddy_system;
-
-/** Buddy system operations to be implemented by each implementation. */
-typedef struct {
-	/**
-	 * Return pointer to left-side or right-side buddy for block passed as
-	 * argument.
-	 */
-	link_t *(* find_buddy)(struct buddy_system *, link_t *);
-	/**
-	 * Bisect the block passed as argument and return pointer to the new
-	 * right-side buddy.
-	 */
-	link_t *(* bisect)(struct buddy_system *, link_t *);
-	/** Coalesce two buddies into a bigger block. */
-	link_t *(* coalesce)(struct buddy_system *, link_t *, link_t *);
-	/** Set order of block passed as argument. */
-	void (*set_order)(struct buddy_system *, link_t *, uint8_t);
-	/** Return order of block passed as argument. */
-	uint8_t (*get_order)(struct buddy_system *, link_t *);
-	/** Mark block as busy. */
-	void (*mark_busy)(struct buddy_system *, link_t *);
-	/** Mark block as available. */
-	void (*mark_available)(struct buddy_system *, link_t *);
-	/** Find parent of block that has given order  */
-	link_t *(* find_block)(struct buddy_system *, link_t *, uint8_t);
-} buddy_system_operations_t;
-
-typedef struct buddy_system {
-	/** Maximal order of block which can be stored by buddy system. */
-	uint8_t max_order;
-	list_t *order;
-	buddy_system_operations_t *op;
-	/** Pointer to be used by the implementation. */
-	void *data;
-} buddy_system_t;
-
-extern void buddy_system_create(buddy_system_t *, uint8_t,
-    buddy_system_operations_t *, void *);
-extern link_t *buddy_system_alloc(buddy_system_t *, uint8_t);
-extern bool buddy_system_can_alloc(buddy_system_t *, uint8_t);
-extern void buddy_system_free(buddy_system_t *, link_t *);
-extern size_t buddy_conf_size(size_t);
-extern link_t *buddy_system_alloc_block(buddy_system_t *, link_t *);
-
-#endif
-
-/** @}
- */
Index: kernel/generic/include/mm/frame.h
===================================================================
--- kernel/generic/include/mm/frame.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/include/mm/frame.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -39,6 +39,6 @@
 #include <typedefs.h>
 #include <trace.h>
+#include <adt/bitmap.h>
 #include <adt/list.h>
-#include <mm/buddy.h>
 #include <synch/spinlock.h>
 #include <arch/mm/page.h>
@@ -90,24 +90,29 @@
 
 typedef struct {
-	size_t refcount;      /**< Tracking of shared frames */
-	link_t buddy_link;    /**< Link to the next free block inside
-                                   one order */
-	void *parent;         /**< If allocated by slab, this points there */
-	uint8_t buddy_order;  /**< Buddy system block order */
+	size_t refcount;  /**< Tracking of shared frames */
+	void *parent;     /**< If allocated by slab, this points there */
 } frame_t;
 
 typedef struct {
-	pfn_t base;                    /**< Frame_no of the first frame
-                                            in the frames array */
-	size_t count;                  /**< Size of zone */
-	size_t free_count;             /**< Number of free frame_t
-                                            structures */
-	size_t busy_count;             /**< Number of busy frame_t
-                                            structures */
-	zone_flags_t flags;            /**< Type of the zone */
+	/** Frame_no of the first frame in the frames array */
+	pfn_t base;
 	
-	frame_t *frames;               /**< Array of frame_t structures
-                                            in this zone */
-	buddy_system_t *buddy_system;  /**< Buddy system for the zone */
+	/** Size of zone */
+	size_t count;
+	
+	/** Number of free frame_t structures */
+	size_t free_count;
+	
+	/** Number of busy frame_t structures */
+	size_t busy_count;
+	
+	/** Type of the zone */
+	zone_flags_t flags;
+	
+	/** Frame bitmap */
+	bitmap_t bitmap;
+	
+	/** Array of frame_t structures in this zone */
+	frame_t *frames;
 } zone_t;
 
@@ -124,46 +129,12 @@
 extern zones_t zones;
 
-NO_TRACE static inline uintptr_t PFN2ADDR(pfn_t frame)
-{
-	return (uintptr_t) (frame << FRAME_WIDTH);
-}
-
-NO_TRACE static inline pfn_t ADDR2PFN(uintptr_t addr)
-{
-	return (pfn_t) (addr >> FRAME_WIDTH);
-}
-
-NO_TRACE static inline size_t SIZE2FRAMES(size_t size)
-{
-	if (size == 0)
-		return 0;
-	
-	return (size_t) ((size - 1) >> FRAME_WIDTH) + 1;
-}
-
-NO_TRACE static inline size_t FRAMES2SIZE(size_t frames)
-{
-	return (size_t) (frames << FRAME_WIDTH);
-}
-
-#define IS_BUDDY_ORDER_OK(index, order) \
-    ((~(((sysarg_t) -1) << (order)) & (index)) == 0)
-#define IS_BUDDY_LEFT_BLOCK(zone, frame) \
-    (((frame_index((zone), (frame)) >> (frame)->buddy_order) & 0x1) == 0)
-#define IS_BUDDY_RIGHT_BLOCK(zone, frame) \
-    (((frame_index((zone), (frame)) >> (frame)->buddy_order) & 0x1) == 1)
-#define IS_BUDDY_LEFT_BLOCK_ABS(zone, frame) \
-    (((frame_index_abs((zone), (frame)) >> (frame)->buddy_order) & 0x1) == 0)
-#define IS_BUDDY_RIGHT_BLOCK_ABS(zone, frame) \
-    (((frame_index_abs((zone), (frame)) >> (frame)->buddy_order) & 0x1) == 1)
-
 extern void frame_init(void);
 extern bool frame_adjust_zone_bounds(bool, uintptr_t *, size_t *);
-extern uintptr_t frame_alloc_generic(uint8_t, frame_flags_t, uintptr_t, size_t *);
-extern uintptr_t frame_alloc(uint8_t, frame_flags_t, uintptr_t);
-extern uintptr_t frame_alloc_noreserve(uint8_t, frame_flags_t, uintptr_t);
-extern void frame_free_generic(uintptr_t, frame_flags_t);
-extern void frame_free(uintptr_t);
-extern void frame_free_noreserve(uintptr_t);
+extern uintptr_t frame_alloc_generic(size_t, frame_flags_t, uintptr_t, size_t *);
+extern uintptr_t frame_alloc(size_t, frame_flags_t, uintptr_t);
+extern uintptr_t frame_alloc_noreserve(size_t, frame_flags_t, uintptr_t);
+extern void frame_free_generic(uintptr_t, size_t, frame_flags_t);
+extern void frame_free(uintptr_t, size_t);
+extern void frame_free_noreserve(uintptr_t, size_t);
 extern void frame_reference_add(pfn_t);
 extern size_t frame_total_free_get(void);
Index: kernel/generic/include/mm/slab.h
===================================================================
--- kernel/generic/include/mm/slab.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/include/mm/slab.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -55,5 +55,5 @@
 /** Maximum wasted space we allow for cache */
 #define SLAB_MAX_BADNESS(cache) \
-	(((unsigned int) PAGE_SIZE << (cache)->order) >> 2)
+	(FRAMES2SIZE((cache)->frames) >> 2)
 
 /* slab_reclaim constants */
@@ -101,5 +101,5 @@
 	
 	/* Computed values */
-	uint8_t order;   /**< Order of frames to be allocated */
+	size_t frames;   /**< Number of frames to be allocated */
 	size_t objects;  /**< Number of objects that fit in */
 	
Index: kernel/generic/src/adt/bitmap.c
===================================================================
--- kernel/generic/src/adt/bitmap.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/src/adt/bitmap.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -37,12 +37,4 @@
  * setting and clearing ranges of bits and for finding ranges
  * of unset bits.
- *
- * The bitmap ADT can optionally implement a two-level hierarchy
- * for faster range searches. The second level bitmap (of blocks)
- * is not precise, but conservative. This means that if the block
- * bit is set, it guarantees that all bits in the block are set.
- * But if the block bit is unset, nothing can be said about the
- * bits in the block.
- *
  */
 
@@ -56,4 +48,22 @@
 #define ALL_ZEROES  0x00
 
+/** Unchecked version of bitmap_get()
+ *
+ * This version of bitmap_get() does not do any boundary checks.
+ *
+ * @param bitmap  Bitmap to access.
+ * @param element Element to access.
+ *
+ * @return Bit value of the element in the bitmap.
+ *
+ */
+static unsigned int bitmap_get_fast(bitmap_t *bitmap, size_t element)
+{
+	size_t byte = element / BITMAP_ELEMENT;
+	uint8_t mask = 1 << (element & BITMAP_REMAINER);
+	
+	return !!((bitmap->bits)[byte] & mask);
+}
+
 /** Get bitmap size
  *
@@ -61,11 +71,9 @@
  *
  * @param elements   Number bits stored in bitmap.
- * @param block_size Block size of the 2nd level bitmap.
- *                   If set to zero, no 2nd level is used.
  *
  * @return Size (in bytes) required for the bitmap.
  *
  */
-size_t bitmap_size(size_t elements, size_t block_size)
+size_t bitmap_size(size_t elements)
 {
 	size_t size = elements / BITMAP_ELEMENT;
@@ -74,11 +82,4 @@
 		size++;
 	
-	if (block_size > 0) {
-		size += elements / block_size;
-		
-		if ((elements % block_size) != 0)
-			size++;
-	}
-	
 	return size;
 }
@@ -90,6 +91,4 @@
  * @param bitmap     Bitmap structure.
  * @param elements   Number of bits stored in bitmap.
- * @param block_size Block size of the 2nd level bitmap.
- *                   If set to zero, no 2nd level is used.
  * @param data       Address of the memory used to hold the map.
  *                   The optional 2nd level bitmap follows the 1st
@@ -97,60 +96,9 @@
  *
  */
-void bitmap_initialize(bitmap_t *bitmap, size_t elements, size_t block_size,
-    void *data)
+void bitmap_initialize(bitmap_t *bitmap, size_t elements, void *data)
 {
 	bitmap->elements = elements;
 	bitmap->bits = (uint8_t *) data;
-	
-	if (block_size > 0) {
-		bitmap->block_size = block_size;
-		bitmap->blocks = bitmap->bits +
-		    bitmap_size(elements, 0);
-	} else {
-		bitmap->block_size = 0;
-		bitmap->blocks = NULL;
-	}
-}
-
-static void bitmap_set_range_internal(uint8_t *bits, size_t start, size_t count)
-{
-	if (count == 0)
-		return;
-	
-	size_t aligned_start = ALIGN_UP(start, BITMAP_ELEMENT);
-	
-	/* Leading unaligned bits */
-	size_t lub = min(aligned_start - start, count);
-	
-	/* Aligned middle bits */
-	size_t amb = (count > lub) ? (count - lub) : 0;
-	
-	/* Trailing aligned bits */
-	size_t tab = amb % BITMAP_ELEMENT;
-	
-	if (start + count < aligned_start) {
-		/* Set bits in the middle of byte. */
-		bits[start / BITMAP_ELEMENT] |=
-		    ((1 << lub) - 1) << (start & BITMAP_REMAINER);
-		return;
-	}
-	
-	if (lub) {
-		/* Make sure to set any leading unaligned bits. */
-		bits[start / BITMAP_ELEMENT] |=
-		    ~((1 << (BITMAP_ELEMENT - lub)) - 1);
-	}
-	
-	size_t i;
-	
-	for (i = 0; i < amb / BITMAP_ELEMENT; i++) {
-		/* The middle bits can be set byte by byte. */
-		bits[aligned_start / BITMAP_ELEMENT + i] = ALL_ONES;
-	}
-	
-	if (tab) {
-		/* Make sure to set any trailing aligned bits. */
-		bits[aligned_start / BITMAP_ELEMENT + i] |= (1 << tab) - 1;
-	}
+	bitmap->next_fit = 0;
 }
 
@@ -166,28 +114,8 @@
 	ASSERT(start + count <= bitmap->elements);
 	
-	bitmap_set_range_internal(bitmap->bits, start, count);
-	
-	if (bitmap->block_size > 0) {
-		size_t aligned_start = ALIGN_UP(start, bitmap->block_size);
-		
-		/* Leading unaligned bits */
-		size_t lub = min(aligned_start - start, count);
-		
-		/* Aligned middle bits */
-		size_t amb = (count > lub) ? (count - lub) : 0;
-		
-		size_t aligned_size = amb / bitmap->block_size;
-		
-		bitmap_set_range_internal(bitmap->blocks, aligned_start,
-		    aligned_size);
-	}
-}
-
-static void bitmap_clear_range_internal(uint8_t *bits, size_t start,
-    size_t count)
-{
 	if (count == 0)
 		return;
 	
+	size_t start_byte = start / BITMAP_ELEMENT;
 	size_t aligned_start = ALIGN_UP(start, BITMAP_ELEMENT);
 	
@@ -202,14 +130,14 @@
 	
 	if (start + count < aligned_start) {
-		/* Set bits in the middle of byte */
-		bits[start / BITMAP_ELEMENT] &=
-		    ~(((1 << lub) - 1) << (start & BITMAP_REMAINER));
+		/* Set bits in the middle of byte. */
+		bitmap->bits[start_byte] |=
+		    ((1 << lub) - 1) << (start & BITMAP_REMAINER);
 		return;
 	}
 	
 	if (lub) {
-		/* Make sure to clear any leading unaligned bits. */
-		bits[start / BITMAP_ELEMENT] &=
-		    (1 << (BITMAP_ELEMENT - lub)) - 1;
+		/* Make sure to set any leading unaligned bits. */
+		bitmap->bits[start_byte] |=
+		    ~((1 << (BITMAP_ELEMENT - lub)) - 1);
 	}
 	
@@ -217,11 +145,13 @@
 	
 	for (i = 0; i < amb / BITMAP_ELEMENT; i++) {
-		/* The middle bits can be cleared byte by byte. */
-		bits[aligned_start / BITMAP_ELEMENT + i] = ALL_ZEROES;
+		/* The middle bits can be set byte by byte. */
+		bitmap->bits[aligned_start / BITMAP_ELEMENT + i] =
+		    ALL_ONES;
 	}
 	
 	if (tab) {
-		/* Make sure to clear any trailing aligned bits. */
-		bits[aligned_start / BITMAP_ELEMENT + i] &= ~((1 << tab) - 1);
+		/* Make sure to set any trailing aligned bits. */
+		bitmap->bits[aligned_start / BITMAP_ELEMENT + i] |=
+		    (1 << tab) - 1;
 	}
 }
@@ -238,19 +168,47 @@
 	ASSERT(start + count <= bitmap->elements);
 	
-	bitmap_clear_range_internal(bitmap->bits, start, count);
-	
-	if (bitmap->block_size > 0) {
-		size_t aligned_start = start / bitmap->block_size;
-		
-		size_t aligned_end = (start + count) / bitmap->block_size;
-		
-		if (((start + count) % bitmap->block_size) != 0)
-			aligned_end++;
-		
-		size_t aligned_size = aligned_end - aligned_start;
-		
-		bitmap_clear_range_internal(bitmap->blocks, aligned_start,
-		    aligned_size);
-	}
+	if (count == 0)
+		return;
+	
+	size_t start_byte = start / BITMAP_ELEMENT;
+	size_t aligned_start = ALIGN_UP(start, BITMAP_ELEMENT);
+	
+	/* Leading unaligned bits */
+	size_t lub = min(aligned_start - start, count);
+	
+	/* Aligned middle bits */
+	size_t amb = (count > lub) ? (count - lub) : 0;
+	
+	/* Trailing aligned bits */
+	size_t tab = amb % BITMAP_ELEMENT;
+	
+	if (start + count < aligned_start) {
+		/* Set bits in the middle of byte */
+		bitmap->bits[start_byte] &=
+		    ~(((1 << lub) - 1) << (start & BITMAP_REMAINER));
+		return;
+	}
+	
+	if (lub) {
+		/* Make sure to clear any leading unaligned bits. */
+		bitmap->bits[start_byte] &=
+		    (1 << (BITMAP_ELEMENT - lub)) - 1;
+	}
+	
+	size_t i;
+	
+	for (i = 0; i < amb / BITMAP_ELEMENT; i++) {
+		/* The middle bits can be cleared byte by byte. */
+		bitmap->bits[aligned_start / BITMAP_ELEMENT + i] =
+		    ALL_ZEROES;
+	}
+	
+	if (tab) {
+		/* Make sure to clear any trailing aligned bits. */
+		bitmap->bits[aligned_start / BITMAP_ELEMENT + i] &=
+		    ~((1 << tab) - 1);
+	}
+	
+	bitmap->next_fit = start_byte;
 }
 
@@ -280,4 +238,88 @@
 }
 
+static int constraint_satisfy(size_t index, size_t base, size_t constraint)
+{
+	return (((base + index) & constraint) == 0);
+}
+
+/** Find a continuous zero bit range
+ *
+ * Find a continuous zero bit range in the bitmap. The address
+ * computed as the sum of the index of the first zero bit and
+ * the base argument needs to be compliant with the constraint
+ * (those bits that are set in the constraint cannot be set in
+ * the address).
+ *
+ * If the index argument is non-NULL, the continuous zero range
+ * is set and the index of the first bit is stored to index.
+ * Otherwise the bitmap stays untouched.
+ *
+ * @param bitmap     Bitmap structure.
+ * @param count      Number of continuous zero bits to find.
+ * @param base       Address of the first bit in the bitmap.
+ * @param constraint Constraint for the address of the first zero bit.
+ * @param index      Place to store the index of the first zero
+ *                   bit. Can be NULL (in which case the bitmap
+ *                   is not modified).
+ *
+ * @return Non-zero if a continuous range of zero bits satisfying
+ *         the constraint has been found.
+ * @return Zero otherwise.
+ *
+ */
+int bitmap_allocate_range(bitmap_t *bitmap, size_t count, size_t base,
+    size_t constraint, size_t *index)
+{
+	if (count == 0)
+		return false;
+	
+	size_t size = bitmap_size(bitmap->elements);
+	
+	for (size_t pos = 0; pos < size; pos++) {
+		size_t byte = (bitmap->next_fit + pos) % size;
+		
+		/* Skip if the current byte has all bits set */
+		if (bitmap->bits[byte] == ALL_ONES)
+			continue;
+		
+		size_t byte_bit = byte * BITMAP_ELEMENT;
+		
+		for (size_t bit = 0; bit < BITMAP_ELEMENT; bit++) {
+			size_t i = byte_bit + bit;
+			
+			if (i >= bitmap->elements)
+				break;
+			
+			if (!constraint_satisfy(i, base, constraint))
+				continue;
+			
+			if (!bitmap_get_fast(bitmap, i)) {
+				size_t continuous = 1;
+				
+				for (size_t j = 1; j < count; j++) {
+					if ((i + j < bitmap->elements) &&
+					    (!bitmap_get_fast(bitmap, i + j)))
+						continuous++;
+					else
+						break;
+				}
+				
+				if (continuous == count) {
+					if (index != NULL) {
+						bitmap_set_range(bitmap, i, count);
+						bitmap->next_fit = i / BITMAP_ELEMENT;
+						*index = i;
+					}
+					
+					return true;
+				} else
+					i += continuous;
+			}
+		}
+	}
+	
+	return false;
+}
+
 /** @}
  */
Index: kernel/generic/src/cpu/cpu.c
===================================================================
--- kernel/generic/src/cpu/cpu.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/src/cpu/cpu.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -73,12 +73,15 @@
 		size_t i;
 		for (i = 0; i < config.cpu_count; i++) {
-			cpus[i].stack = (uint8_t *) PA2KA(frame_alloc(STACK_FRAMES,
-			    FRAME_LOWMEM | FRAME_ATOMIC, 0));
+			uintptr_t stack_phys = frame_alloc(STACK_FRAMES,
+			    FRAME_LOWMEM | FRAME_ATOMIC, STACK_SIZE - 1);
+			if (!stack_phys)
+				panic("Cannot allocate CPU stack.");
+			
+			cpus[i].stack = (uint8_t *) PA2KA(stack_phys);
 			cpus[i].id = i;
 			
 			irq_spinlock_initialize(&cpus[i].lock, "cpus[].lock");
 			
-			unsigned int j;
-			for (j = 0; j < RQ_COUNT; j++) {
+			for (unsigned int j = 0; j < RQ_COUNT; j++) {
 				irq_spinlock_initialize(&cpus[i].rq[j].lock, "cpus[].rq[].lock");
 				list_initialize(&cpus[i].rq[j].rq);
Index: kernel/generic/src/ddi/ddi.c
===================================================================
--- kernel/generic/src/ddi/ddi.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/src/ddi/ddi.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -322,19 +322,12 @@
 }
 
-NO_TRACE static int dmamem_map_anonymous(size_t size, unsigned int map_flags,
-    unsigned int flags, uintptr_t *phys, uintptr_t *virt, uintptr_t bound)
+NO_TRACE static int dmamem_map_anonymous(size_t size, uintptr_t constraint,
+    unsigned int map_flags, unsigned int flags, uintptr_t *phys,
+    uintptr_t *virt, uintptr_t bound)
 {
 	ASSERT(TASK);
 	
 	size_t frames = SIZE2FRAMES(size);
-	uint8_t order;
-	
-	/* We need the 2^order >= frames */
-	if (frames == 1)
-		order = 0;
-	else
-		order = fnzb(frames - 1) + 1;
-	
-	*phys = frame_alloc_noreserve(order, 0, 0);
+	*phys = frame_alloc_noreserve(frames, 0, constraint);
 	if (*phys == 0)
 		return ENOMEM;
@@ -346,5 +339,5 @@
 	if (!as_area_create(TASK->as, map_flags, size,
 	    AS_AREA_ATTR_NONE, &phys_backend, &backend_data, virt, bound)) {
-		frame_free_noreserve(*phys);
+		frame_free_noreserve(*phys, frames);
 		return ENOMEM;
 	}
@@ -390,7 +383,13 @@
 		 */
 		
+		uintptr_t constraint;
+		int rc = copy_from_uspace(&constraint, phys_ptr,
+		    sizeof(constraint));
+		if (rc != EOK)
+			return rc;
+		
 		uintptr_t phys;
 		uintptr_t virt = (uintptr_t) -1;
-		int rc = dmamem_map_anonymous(size, map_flags, flags,
+		rc = dmamem_map_anonymous(size, constraint, map_flags, flags,
 		    &phys, &virt, bound);
 		if (rc != EOK)
Index: kernel/generic/src/mm/as.c
===================================================================
--- kernel/generic/src/mm/as.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/src/mm/as.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -907,5 +907,5 @@
 			
 			for (i = 0; i < node->keys; i++)
-				frame_free((uintptr_t) node->value[i]);
+				frame_free((uintptr_t) node->value[i], 1);
 		}
 		
Index: kernel/generic/src/mm/backend_anon.c
===================================================================
--- kernel/generic/src/mm/backend_anon.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/src/mm/backend_anon.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -294,5 +294,5 @@
 		 * the normal unreserving frame_free().
 		 */
-		frame_free(frame);
+		frame_free(frame, 1);
 	} else {
 		/*
@@ -301,5 +301,5 @@
 		 * manipulate the reserve or it would be given back twice.
 		 */
-		frame_free_noreserve(frame);
+		frame_free_noreserve(frame, 1);
 	}
 }
Index: kernel/generic/src/mm/backend_elf.c
===================================================================
--- kernel/generic/src/mm/backend_elf.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/src/mm/backend_elf.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -429,5 +429,5 @@
 			 * data.
 			 */
-			frame_free_noreserve(frame);
+			frame_free_noreserve(frame, 1);
 		}
 	} else {
@@ -437,5 +437,5 @@
 		 * anonymous). In any case, a frame needs to be freed.
 		 */
-		frame_free_noreserve(frame);
+		frame_free_noreserve(frame, 1);
 	}
 }
Index: rnel/generic/src/mm/buddy.c
===================================================================
--- kernel/generic/src/mm/buddy.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ 	(revision )
@@ -1,284 +1,0 @@
-/*
- * Copyright (c) 2005 Jakub Jermar
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup genericmm
- * @{
- */
-
-/**
- * @file
- * @brief	Buddy allocator framework.
- *
- * This file contains buddy system allocator framework.
- * Specialized functions are needed for this abstract framework to be useful.
- */
-
-#include <mm/buddy.h>
-#include <mm/frame.h>
-#include <typedefs.h>
-#include <debug.h>
-#include <print.h>
-#include <macros.h>
-
-/** Return size needed for the buddy configuration data. */
-size_t buddy_conf_size(size_t max_order)
-{
-	return sizeof(buddy_system_t) + (max_order + 1) * sizeof(link_t);
-}
-
-
-/** Create buddy system.
- *
- * Allocate memory for and initialize new buddy system.
- *
- * @param b		Preallocated buddy system control data.
- * @param max_order	The biggest allocable size will be 2^max_order.
- * @param op		Operations for new buddy system.
- * @param data		Pointer to be used by implementation.
- *
- * @return		New buddy system.
- */
-void
-buddy_system_create(buddy_system_t *b, uint8_t max_order,
-    buddy_system_operations_t *op, void *data)
-{
-	int i;
-
-	ASSERT(max_order < BUDDY_SYSTEM_INNER_BLOCK);
-
-	ASSERT(op->find_buddy);
-	ASSERT(op->set_order);
-	ASSERT(op->get_order);
-	ASSERT(op->bisect);
-	ASSERT(op->coalesce);
-	ASSERT(op->mark_busy);
-
-	/*
-	 * Use memory after our own structure.
-	 */
-	b->order = (list_t *) (&b[1]);
-	
-	for (i = 0; i <= max_order; i++)
-		list_initialize(&b->order[i]);
-
-	b->max_order = max_order;
-	b->op = op;
-	b->data = data;
-}
-
-/** Check if buddy system can allocate block.
- *
- * @param b		Buddy system pointer.
- * @param i		Size of the block (2^i).
- *
- * @return		True if block can be allocated.
- */
-bool buddy_system_can_alloc(buddy_system_t *b, uint8_t i)
-{
-	uint8_t k;
-	
-	/*
-	 * If requested block is greater then maximal block
-	 * we know immediatly that we cannot satisfy the request.
-	 */
-	if (i > b->max_order)
-		return false;
-
-	/*
-	 * Check if any bigger or equal order has free elements
-	 */
-	for (k = i; k <= b->max_order; k++) {
-		if (!list_empty(&b->order[k])) {
-			return true;
-		}
-	}
-	
-	return false;
-}
-
-/** Allocate PARTICULAR block from buddy system.
- *
- * @return		Block of data or NULL if no such block was found.
- */
-link_t *buddy_system_alloc_block(buddy_system_t *b, link_t *block)
-{
-	link_t *left,*right, *tmp;
-	uint8_t order;
-
-	left = b->op->find_block(b, block, BUDDY_SYSTEM_INNER_BLOCK);
-	ASSERT(left);
-	list_remove(left);
-	while (1) {
-		if (!b->op->get_order(b, left)) {
-			b->op->mark_busy(b, left);
-			return left;
-		}
-		
-		order = b->op->get_order(b, left);
-
-		right = b->op->bisect(b, left);
-		b->op->set_order(b, left, order - 1);
-		b->op->set_order(b, right, order - 1);
-
-		tmp = b->op->find_block(b, block, BUDDY_SYSTEM_INNER_BLOCK);
-
-		if (tmp == right) {
-			right = left;
-			left = tmp;
-		} 
-		ASSERT(tmp == left);
-		b->op->mark_busy(b, left);
-		buddy_system_free(b, right);
-		b->op->mark_available(b, left);
-	}
-}
-
-/** Allocate block from buddy system.
- *
- * @param b		Buddy system pointer.
- * @param i		Returned block will be 2^i big.
- *
- * @return		Block of data represented by link_t.
- */
-link_t *buddy_system_alloc(buddy_system_t *b, uint8_t i)
-{
-	link_t *res, *hlp;
-
-	ASSERT(i <= b->max_order);
-
-	/*
-	 * If the list of order i is not empty,
-	 * the request can be immediatelly satisfied.
-	 */
-	res = list_first(&b->order[i]);
-	if (res != NULL) {
-		list_remove(res);
-		b->op->mark_busy(b, res);
-		return res;
-	}
-	/*
-	 * If order i is already the maximal order,
-	 * the request cannot be satisfied.
-	 */
-	if (i == b->max_order)
-		return NULL;
-
-	/*
-	 * Try to recursively satisfy the request from higher order lists.
-	 */	
-	hlp = buddy_system_alloc(b, i + 1);
-	
-	/*
-	 * The request could not be satisfied
-	 * from higher order lists.
-	 */
-	if (!hlp)
-		return NULL;
-		
-	res = hlp;
-	
-	/*
-	 * Bisect the block and set order of both of its parts to i.
-	 */
-	hlp = b->op->bisect(b, res);
-	b->op->set_order(b, res, i);
-	b->op->set_order(b, hlp, i);
-	
-	/*
-	 * Return the other half to buddy system. Mark the first part
-	 * full, so that it won't coalesce again.
-	 */
-	b->op->mark_busy(b, res);
-	buddy_system_free(b, hlp);
-	
-	return res;
-}
-
-/** Return block to buddy system.
- *
- * @param b		Buddy system pointer.
- * @param block		Block to return.
- */
-void buddy_system_free(buddy_system_t *b, link_t *block)
-{
-	link_t *buddy, *hlp;
-	uint8_t i;
-
-	/*
-	 * Determine block's order.
-	 */
-	i = b->op->get_order(b, block);
-
-	ASSERT(i <= b->max_order);
-
-	if (i != b->max_order) {
-		/*
-		 * See if there is any buddy in the list of order i.
-		 */
-		buddy = b->op->find_buddy(b, block);
-		if (buddy) {
-
-			ASSERT(b->op->get_order(b, buddy) == i);
-			/*
-			 * Remove buddy from the list of order i.
-			 */
-			list_remove(buddy);
-		
-			/*
-			 * Invalidate order of both block and buddy.
-			 */
-			b->op->set_order(b, block, BUDDY_SYSTEM_INNER_BLOCK);
-			b->op->set_order(b, buddy, BUDDY_SYSTEM_INNER_BLOCK);
-		
-			/*
-			 * Coalesce block and buddy into one block.
-			 */
-			hlp = b->op->coalesce(b, block, buddy);
-
-			/*
-			 * Set order of the coalesced block to i + 1.
-			 */
-			b->op->set_order(b, hlp, i + 1);
-
-			/*
-			 * Recursively add the coalesced block to the list of
-			 * order i + 1.
-			 */
-			buddy_system_free(b, hlp);
-			return;
-		}
-	}
-
-	/*
-	 * Insert block into the list of order i.
-	 */
-	list_append(block, &b->order[i]);
-}
-
-/** @}
- */
Index: kernel/generic/src/mm/frame.c
===================================================================
--- kernel/generic/src/mm/frame.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/src/mm/frame.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -38,7 +38,6 @@
  *
  * This file contains the physical frame allocator and memory zone management.
- * The frame allocator is built on top of the buddy allocator.
- *
- * @see buddy.c
+ * The frame allocator is built on top of the two-level bitmap structure.
+ *
  */
 
@@ -92,9 +91,4 @@
 }
 
-NO_TRACE static inline size_t make_frame_index(zone_t *zone, frame_t *frame)
-{
-	return (frame - zone->frames);
-}
-
 /** Initialize frame structure.
  *
@@ -104,6 +98,6 @@
 NO_TRACE static void frame_initialize(frame_t *frame)
 {
-	frame->refcount = 1;
-	frame->buddy_order = 0;
+	frame->refcount = 0;
+	frame->parent = NULL;
 }
 
@@ -161,11 +155,6 @@
 	
 	/* Move other zones up */
-	size_t j;
-	for (j = zones.count; j > i; j--) {
+	for (size_t j = zones.count; j > i; j--)
 		zones.info[j] = zones.info[j - 1];
-		if (zones.info[j].buddy_system != NULL)
-			zones.info[j].buddy_system->data =
-			    (void *) &zones.info[j];
-	}
 	
 	zones.count++;
@@ -237,23 +226,31 @@
 }
 
-/** @return True if zone can allocate specified order */
-NO_TRACE static bool zone_can_alloc(zone_t *zone, uint8_t order)
-{
+/** @return True if zone can allocate specified number of frames */
+NO_TRACE static bool zone_can_alloc(zone_t *zone, size_t count,
+    pfn_t constraint)
+{
+	/*
+	 * The function bitmap_allocate_range() does not modify
+	 * the bitmap if the last argument is NULL.
+	 */
 	return ((zone->flags & ZONE_AVAILABLE) &&
-	    buddy_system_can_alloc(zone->buddy_system, order));
-}
-
-/** Find a zone that can allocate order frames.
+	    bitmap_allocate_range(&zone->bitmap, count, zone->base,
+	    constraint, NULL));
+}
+
+/** Find a zone that can allocate specified number of frames
  *
  * Assume interrupts are disabled and zones lock is
  * locked.
  *
- * @param order Size (2^order) of free space we are trying to find.
- * @param flags Required flags of the target zone.
- * @param hind  Preferred zone.
- *
- */
-NO_TRACE static size_t find_free_zone(uint8_t order, zone_flags_t flags,
-    size_t hint)
+ * @param count      Number of free frames we are trying to find.
+ * @param flags      Required flags of the target zone.
+ * @param constraint Indication of bits that cannot be set in the
+ *                   physical frame number of the first allocated frame.
+ * @param hind       Preferred zone.
+ *
+ */
+NO_TRACE static size_t find_free_zone(size_t count, zone_flags_t flags,
+    pfn_t constraint, size_t hint)
 {
 	if (hint >= zones.count)
@@ -267,7 +264,7 @@
 		if (ZONE_FLAGS_MATCH(zones.info[i].flags, flags)) {
 			/*
-			 * Check if the zone has 2^order frames area available.
+			 * Check if the zone can satisfy the allocation request.
 			 */
-			if (zone_can_alloc(&zones.info[i], order))
+			if (zone_can_alloc(&zones.info[i], count, constraint))
 				return i;
 		}
@@ -282,169 +279,16 @@
 }
 
-/**************************/
-/* Buddy system functions */
-/**************************/
-
-/** Buddy system find_block implementation.
- *
- * Find block that is parent of current list.
- * That means go to lower addresses, until such block is found
- *
- * @param order Order of parent must be different then this
- *              parameter!!
- *
- */
-NO_TRACE static link_t *zone_buddy_find_block(buddy_system_t *buddy,
-    link_t *child, uint8_t order)
-{
-	frame_t *frame = list_get_instance(child, frame_t, buddy_link);
-	zone_t *zone = (zone_t *) buddy->data;
-	
-	size_t index = frame_index(zone, frame);
-	do {
-		if (zone->frames[index].buddy_order != order)
-			return &zone->frames[index].buddy_link;
-	} while (index-- > 0);
-	
-	return NULL;
-}
-
-/** Buddy system find_buddy implementation.
- *
- * @param buddy Buddy system.
- * @param block Block for which buddy should be found.
- *
- * @return Buddy for given block if found.
- *
- */
-NO_TRACE static link_t *zone_buddy_find_buddy(buddy_system_t *buddy,
-    link_t *block)
-{
-	frame_t *frame = list_get_instance(block, frame_t, buddy_link);
-	zone_t *zone = (zone_t *) buddy->data;
-	ASSERT(IS_BUDDY_ORDER_OK(frame_index_abs(zone, frame),
-	    frame->buddy_order));
-	
-	bool is_left = IS_BUDDY_LEFT_BLOCK_ABS(zone, frame);
-	
-	size_t index;
-	if (is_left) {
-		index = (frame_index(zone, frame)) +
-		    (1 << frame->buddy_order);
-	} else {  /* is_right */
-		index = (frame_index(zone, frame)) -
-		    (1 << frame->buddy_order);
-	}
-	
-	if (frame_index_valid(zone, index)) {
-		if ((zone->frames[index].buddy_order == frame->buddy_order) &&
-		    (zone->frames[index].refcount == 0)) {
-			return &zone->frames[index].buddy_link;
-		}
-	}
-	
-	return NULL;
-}
-
-/** Buddy system bisect implementation.
- *
- * @param buddy Buddy system.
- * @param block Block to bisect.
- *
- * @return Right block.
- *
- */
-NO_TRACE static link_t *zone_buddy_bisect(buddy_system_t *buddy, link_t *block)
-{
-	frame_t *frame_l = list_get_instance(block, frame_t, buddy_link);
-	frame_t *frame_r = (frame_l + (1 << (frame_l->buddy_order - 1)));
-	
-	return &frame_r->buddy_link;
-}
-
-/** Buddy system coalesce implementation.
- *
- * @param buddy   Buddy system.
- * @param block_1 First block.
- * @param block_2 First block's buddy.
- *
- * @return Coalesced block (actually block that represents lower
- *         address).
- *
- */
-NO_TRACE static link_t *zone_buddy_coalesce(buddy_system_t *buddy,
-    link_t *block_1, link_t *block_2)
-{
-	frame_t *frame1 = list_get_instance(block_1, frame_t, buddy_link);
-	frame_t *frame2 = list_get_instance(block_2, frame_t, buddy_link);
-	
-	return ((frame1 < frame2) ? block_1 : block_2);
-}
-
-/** Buddy system set_order implementation.
- *
- * @param buddy Buddy system.
- * @param block Buddy system block.
- * @param order Order to set.
- *
- */
-NO_TRACE static void zone_buddy_set_order(buddy_system_t *buddy, link_t *block,
-    uint8_t order)
-{
-	list_get_instance(block, frame_t, buddy_link)->buddy_order = order;
-}
-
-/** Buddy system get_order implementation.
- *
- * @param buddy Buddy system.
- * @param block Buddy system block.
- *
- * @return Order of block.
- *
- */
-NO_TRACE static uint8_t zone_buddy_get_order(buddy_system_t *buddy,
-    link_t *block)
-{
-	return list_get_instance(block, frame_t, buddy_link)->buddy_order;
-}
-
-/** Buddy system mark_busy implementation.
- *
- * @param buddy Buddy system.
- * @param block Buddy system block.
- *
- */
-NO_TRACE static void zone_buddy_mark_busy(buddy_system_t *buddy, link_t *block)
-{
-	list_get_instance(block, frame_t, buddy_link)->refcount = 1;
-}
-
-/** Buddy system mark_available implementation.
- *
- * @param buddy Buddy system.
- * @param block Buddy system block.
- *
- */
-NO_TRACE static void zone_buddy_mark_available(buddy_system_t *buddy,
-    link_t *block)
-{
-	list_get_instance(block, frame_t, buddy_link)->refcount = 0;
-}
-
-static buddy_system_operations_t zone_buddy_system_operations = {
-	.find_buddy = zone_buddy_find_buddy,
-	.bisect = zone_buddy_bisect,
-	.coalesce = zone_buddy_coalesce,
-	.set_order = zone_buddy_set_order,
-	.get_order = zone_buddy_get_order,
-	.mark_busy = zone_buddy_mark_busy,
-	.mark_available = zone_buddy_mark_available,
-	.find_block = zone_buddy_find_block
-};
-
 /******************/
 /* Zone functions */
 /******************/
 
+/** Return frame from zone. */
+NO_TRACE static frame_t *zone_get_frame(zone_t *zone, size_t index)
+{
+	ASSERT(index < zone->count);
+	
+	return &zone->frames[index];
+}
+
 /** Allocate frame in particular zone.
  *
@@ -452,28 +296,37 @@
  * Panics if allocation is impossible.
  *
- * @param zone  Zone to allocate from.
- * @param order Allocate exactly 2^order frames.
+ * @param zone       Zone to allocate from.
+ * @param count      Number of frames to allocate
+ * @param constraint Indication of bits that cannot be set in the
+ *                   physical frame number of the first allocated frame.
  *
  * @return Frame index in zone.
  *
  */
-NO_TRACE static pfn_t zone_frame_alloc(zone_t *zone, uint8_t order)
+NO_TRACE static size_t zone_frame_alloc(zone_t *zone, size_t count,
+    pfn_t constraint)
 {
 	ASSERT(zone->flags & ZONE_AVAILABLE);
 	
-	/* Allocate frames from zone buddy system */
-	link_t *link = buddy_system_alloc(zone->buddy_system, order);
-	
-	ASSERT(link);
+	/* Allocate frames from zone */
+	size_t index;
+	int avail = bitmap_allocate_range(&zone->bitmap, count, zone->base,
+	    constraint, &index);
+	
+	ASSERT(avail);
+	
+	/* Update frame reference count */
+	for (size_t i = 0; i < count; i++) {
+		frame_t *frame = zone_get_frame(zone, index + i);
+		
+		ASSERT(frame->refcount == 0);
+		frame->refcount = 1;
+	}
 	
 	/* Update zone information. */
-	zone->free_count -= (1 << order);
-	zone->busy_count += (1 << order);
-	
-	/* Frame will be actually a first frame of the block. */
-	frame_t *frame = list_get_instance(link, frame_t, buddy_link);
-	
-	/* Get frame address */
-	return make_frame_index(zone, frame);
+	zone->free_count -= count;
+	zone->busy_count += count;
+	
+	return index;
 }
 
@@ -482,54 +335,44 @@
  * Assume zone is locked and is available for deallocation.
  *
- * @param zone      Pointer to zone from which the frame is to be freed.
- * @param frame_idx Frame index relative to zone.
- *
- * @return          Number of freed frames.
- *
- */
-NO_TRACE static size_t zone_frame_free(zone_t *zone, size_t frame_idx)
+ * @param zone  Pointer to zone from which the frame is to be freed.
+ * @param index Frame index relative to zone.
+ *
+ * @return Number of freed frames.
+ *
+ */
+NO_TRACE static size_t zone_frame_free(zone_t *zone, size_t index)
 {
 	ASSERT(zone->flags & ZONE_AVAILABLE);
 	
-	frame_t *frame = &zone->frames[frame_idx];
-	size_t size = 0;
-	
-	ASSERT(frame->refcount);
+	frame_t *frame = zone_get_frame(zone, index);
+	
+	ASSERT(frame->refcount > 0);
 	
 	if (!--frame->refcount) {
-		size = 1 << frame->buddy_order;
-		buddy_system_free(zone->buddy_system, &frame->buddy_link);		
+		bitmap_set(&zone->bitmap, index, 0);
+		
 		/* Update zone information. */
-		zone->free_count += size;
-		zone->busy_count -= size;
-	}
-	
-	return size;
-}
-
-/** Return frame from zone. */
-NO_TRACE static frame_t *zone_get_frame(zone_t *zone, size_t frame_idx)
-{
-	ASSERT(frame_idx < zone->count);
-	return &zone->frames[frame_idx];
+		zone->free_count++;
+		zone->busy_count--;
+		
+		return 1;
+	}
+	
+	return 0;
 }
 
 /** Mark frame in zone unavailable to allocation. */
-NO_TRACE static void zone_mark_unavailable(zone_t *zone, size_t frame_idx)
+NO_TRACE static void zone_mark_unavailable(zone_t *zone, size_t index)
 {
 	if (!(zone->flags & ZONE_AVAILABLE))
 		return;
-//	ASSERT(zone->flags & ZONE_AVAILABLE);
-	
-	frame_t *frame = zone_get_frame(zone, frame_idx);
-	if (frame->refcount)
+	
+	frame_t *frame = zone_get_frame(zone, index);
+	if (frame->refcount > 0)
 		return;
 	
-	link_t *link __attribute__ ((unused));
-	
-	link = buddy_system_alloc_block(zone->buddy_system,
-	    &frame->buddy_link);
-	
-	ASSERT(link);
+	frame->refcount = 1;
+	bitmap_set_range(&zone->bitmap, index, 1);
+	
 	zone->free_count--;
 	reserve_force_alloc(1);
@@ -538,16 +381,15 @@
 /** Merge two zones.
  *
- * Expect buddy to point to space at least zone_conf_size large.
  * Assume z1 & z2 are locked and compatible and zones lock is
  * locked.
  *
- * @param z1     First zone to merge.
- * @param z2     Second zone to merge.
- * @param old_z1 Original date of the first zone.
- * @param buddy  Merged zone buddy.
+ * @param z1       First zone to merge.
+ * @param z2       Second zone to merge.
+ * @param old_z1   Original data of the first zone.
+ * @param confdata Merged zone configuration data.
  *
  */
 NO_TRACE static void zone_merge_internal(size_t z1, size_t z2, zone_t *old_z1,
-    buddy_system_t *buddy)
+    void *confdata)
 {
 	ASSERT(zones.info[z1].flags & ZONE_AVAILABLE);
@@ -564,69 +406,26 @@
 	zones.info[z1].free_count += zones.info[z2].free_count;
 	zones.info[z1].busy_count += zones.info[z2].busy_count;
-	zones.info[z1].buddy_system = buddy;
-	
-	uint8_t order = fnzb(zones.info[z1].count);
-	buddy_system_create(zones.info[z1].buddy_system, order,
-	    &zone_buddy_system_operations, (void *) &zones.info[z1]);
-	
-	zones.info[z1].frames =
-	    (frame_t *) ((uint8_t *) zones.info[z1].buddy_system
-	    + buddy_conf_size(order));
-	
-	/* This marks all frames busy */
-	size_t i;
-	for (i = 0; i < zones.info[z1].count; i++)
-		frame_initialize(&zones.info[z1].frames[i]);
-	
-	/* Copy frames from both zones to preserve full frame orders,
-	 * parents etc. Set all free frames with refcount = 0 to 1, because
-	 * we add all free frames to buddy allocator later again, clearing
-	 * order to 0. Don't set busy frames with refcount = 0, as they
-	 * will not be reallocated during merge and it would make later
-	 * problems with allocation/free.
+	
+	bitmap_initialize(&zones.info[z1].bitmap, zones.info[z1].count,
+	    confdata + (sizeof(frame_t) * zones.info[z1].count));
+	bitmap_clear_range(&zones.info[z1].bitmap, 0, zones.info[z1].count);
+	
+	zones.info[z1].frames = (frame_t *) confdata;
+	
+	/*
+	 * Copy frames and bits from both zones to preserve parents, etc.
 	 */
-	for (i = 0; i < old_z1->count; i++)
+	
+	for (size_t i = 0; i < old_z1->count; i++) {
+		bitmap_set(&zones.info[z1].bitmap, i,
+		    bitmap_get(&old_z1->bitmap, i));
 		zones.info[z1].frames[i] = old_z1->frames[i];
-	
-	for (i = 0; i < zones.info[z2].count; i++)
-		zones.info[z1].frames[base_diff + i]
-		    = zones.info[z2].frames[i];
-	
-	i = 0;
-	while (i < zones.info[z1].count) {
-		if (zones.info[z1].frames[i].refcount) {
-			/* Skip busy frames */
-			i += 1 << zones.info[z1].frames[i].buddy_order;
-		} else {
-			/* Free frames, set refcount = 1
-			 * (all free frames have refcount == 0, we need not
-			 * to check the order)
-			 */
-			zones.info[z1].frames[i].refcount = 1;
-			zones.info[z1].frames[i].buddy_order = 0;
-			i++;
-		}
-	}
-	
-	/* Add free blocks from the original zone z1 */
-	while (zone_can_alloc(old_z1, 0)) {
-		/* Allocate from the original zone */
-		pfn_t frame_idx = zone_frame_alloc(old_z1, 0);
-		
-		/* Free the frame from the merged zone */
-		frame_t *frame = &zones.info[z1].frames[frame_idx];
-		frame->refcount = 0;
-		buddy_system_free(zones.info[z1].buddy_system, &frame->buddy_link);
-	}
-	
-	/* Add free blocks from the original zone z2 */
-	while (zone_can_alloc(&zones.info[z2], 0)) {
-		/* Allocate from the original zone */
-		pfn_t frame_idx = zone_frame_alloc(&zones.info[z2], 0);
-		
-		/* Free the frame from the merged zone */
-		frame_t *frame = &zones.info[z1].frames[base_diff + frame_idx];
-		frame->refcount = 0;
-		buddy_system_free(zones.info[z1].buddy_system, &frame->buddy_link);
+	}
+	
+	for (size_t i = 0; i < zones.info[z2].count; i++) {
+		bitmap_set(&zones.info[z1].bitmap, base_diff + i,
+		    bitmap_get(&zones.info[z2].bitmap, i));
+		zones.info[z1].frames[base_diff + i] =
+		    zones.info[z2].frames[i];
 	}
 }
@@ -651,55 +450,11 @@
 	size_t cframes = SIZE2FRAMES(zone_conf_size(count));
 	
-	if ((pfn < zones.info[znum].base)
-	    || (pfn >= zones.info[znum].base + zones.info[znum].count))
+	if ((pfn < zones.info[znum].base) ||
+	    (pfn >= zones.info[znum].base + zones.info[znum].count))
 		return;
 	
-	frame_t *frame __attribute__ ((unused));
-
-	frame = &zones.info[znum].frames[pfn - zones.info[znum].base];
-	ASSERT(!frame->buddy_order);
-	
-	size_t i;
-	for (i = 0; i < cframes; i++) {
-		zones.info[znum].busy_count++;
+	for (size_t i = 0; i < cframes; i++)
 		(void) zone_frame_free(&zones.info[znum],
 		    pfn - zones.info[znum].base + i);
-	}
-}
-
-/** Reduce allocated block to count of order 0 frames.
- *
- * The allocated block needs 2^order frames. Reduce all frames
- * in the block to order 0 and free the unneeded frames. This means that
- * when freeing the previously allocated block starting with frame_idx,
- * you have to free every frame.
- *
- * @param znum      Zone.
- * @param frame_idx Index the first frame of the block.
- * @param count     Allocated frames in block.
- *
- */
-NO_TRACE static void zone_reduce_region(size_t znum, pfn_t frame_idx,
-    size_t count)
-{
-	ASSERT(zones.info[znum].flags & ZONE_AVAILABLE);
-	ASSERT(frame_idx + count < zones.info[znum].count);
-	
-	uint8_t order = zones.info[znum].frames[frame_idx].buddy_order;
-	ASSERT((size_t) (1 << order) >= count);
-	
-	/* Reduce all blocks to order 0 */
-	size_t i;
-	for (i = 0; i < (size_t) (1 << order); i++) {
-		frame_t *frame = &zones.info[znum].frames[i + frame_idx];
-		frame->buddy_order = 0;
-		if (!frame->refcount)
-			frame->refcount = 1;
-		ASSERT(frame->refcount == 1);
-	}
-	
-	/* Free unneeded frames */
-	for (i = count; i < (size_t) (1 << order); i++)
-		(void) zone_frame_free(&zones.info[znum], i + frame_idx);
 }
 
@@ -721,5 +476,6 @@
 	bool ret = true;
 	
-	/* We can join only 2 zones with none existing inbetween,
+	/*
+	 * We can join only 2 zones with none existing inbetween,
 	 * the zones have to be available and with the same
 	 * set of flags
@@ -735,16 +491,12 @@
 	    + zones.info[z2].count));
 	
-	uint8_t order;
-	if (cframes == 1)
-		order = 0;
-	else
-		order = fnzb(cframes - 1) + 1;
-	
 	/* Allocate merged zone data inside one of the zones */
 	pfn_t pfn;
-	if (zone_can_alloc(&zones.info[z1], order)) {
-		pfn = zones.info[z1].base + zone_frame_alloc(&zones.info[z1], order);
-	} else if (zone_can_alloc(&zones.info[z2], order)) {
-		pfn = zones.info[z2].base + zone_frame_alloc(&zones.info[z2], order);
+	if (zone_can_alloc(&zones.info[z1], cframes, 0)) {
+		pfn = zones.info[z1].base +
+		    zone_frame_alloc(&zones.info[z1], cframes, 0);
+	} else if (zone_can_alloc(&zones.info[z2], cframes, 0)) {
+		pfn = zones.info[z2].base +
+		    zone_frame_alloc(&zones.info[z2], cframes, 0);
 	} else {
 		ret = false;
@@ -754,12 +506,7 @@
 	/* Preserve original data from z1 */
 	zone_t old_z1 = zones.info[z1];
-	old_z1.buddy_system->data = (void *) &old_z1;
 	
 	/* Do zone merging */
-	buddy_system_t *buddy = (buddy_system_t *) PA2KA(PFN2ADDR(pfn));
-	zone_merge_internal(z1, z2, &old_z1, buddy);
-	
-	/* Free unneeded config frames */
-	zone_reduce_region(z1, pfn - zones.info[z1].base, cframes);
+	zone_merge_internal(z1, z2, &old_z1, (void *) PA2KA(PFN2ADDR(pfn)));
 	
 	/* Subtract zone information from busy frames */
@@ -774,11 +521,6 @@
 	
 	/* Move zones down */
-	size_t i;
-	for (i = z2 + 1; i < zones.count; i++) {
+	for (size_t i = z2 + 1; i < zones.count; i++)
 		zones.info[i - 1] = zones.info[i];
-		if (zones.info[i - 1].buddy_system != NULL)
-			zones.info[i - 1].buddy_system->data =
-			    (void *) &zones.info[i - 1];
-	}
 	
 	zones.count--;
@@ -799,7 +541,8 @@
 void zone_merge_all(void)
 {
-	size_t i = 0;
+	size_t i = 1;
+	
 	while (i < zones.count) {
-		if (!zone_merge(i, i + 1))
+		if (!zone_merge(i - 1, i))
 			i++;
 	}
@@ -808,15 +551,15 @@
 /** Create new frame zone.
  *
- * @param zone  Zone to construct.
- * @param buddy Address of buddy system configuration information.
- * @param start Physical address of the first frame within the zone.
- * @param count Count of frames in zone.
- * @param flags Zone flags.
+ * @param zone     Zone to construct.
+ * @param start    Physical address of the first frame within the zone.
+ * @param count    Count of frames in zone.
+ * @param flags    Zone flags.
+ * @param confdata Configuration data of the zone.
  *
  * @return Initialized zone.
  *
  */
-NO_TRACE static void zone_construct(zone_t *zone, buddy_system_t *buddy,
-    pfn_t start, size_t count, zone_flags_t flags)
+NO_TRACE static void zone_construct(zone_t *zone, pfn_t start, size_t count,
+    zone_flags_t flags, void *confdata)
 {
 	zone->base = start;
@@ -825,31 +568,27 @@
 	zone->free_count = count;
 	zone->busy_count = 0;
-	zone->buddy_system = buddy;
 	
 	if (flags & ZONE_AVAILABLE) {
 		/*
-		 * Compute order for buddy system and initialize
+		 * Initialize frame bitmap (located after the array of
+		 * frame_t structures in the configuration space).
 		 */
-		uint8_t order = fnzb(count);
-		buddy_system_create(zone->buddy_system, order,
-		    &zone_buddy_system_operations, (void *) zone);
-		
-		/* Allocate frames _after_ the confframe */
-		
-		/* Check sizes */
-		zone->frames = (frame_t *) ((uint8_t *) zone->buddy_system +
-		    buddy_conf_size(order));
-		
-		size_t i;
-		for (i = 0; i < count; i++)
+		
+		bitmap_initialize(&zone->bitmap, count, confdata +
+		    (sizeof(frame_t) * count));
+		bitmap_clear_range(&zone->bitmap, 0, count);
+		
+		/*
+		 * Initialize the array of frame_t structures.
+		 */
+		
+		zone->frames = (frame_t *) confdata;
+		
+		for (size_t i = 0; i < count; i++)
 			frame_initialize(&zone->frames[i]);
-		
-		/* Stuffing frames */
-		for (i = 0; i < count; i++) {
-			zone->frames[i].refcount = 0;
-			buddy_system_free(zone->buddy_system, &zone->frames[i].buddy_link);
-		}
-	} else
+	} else {
+		bitmap_initialize(&zone->bitmap, 0, NULL);
 		zone->frames = NULL;
+	}
 }
 
@@ -863,5 +602,5 @@
 size_t zone_conf_size(size_t count)
 {
-	return (count * sizeof(frame_t) + buddy_conf_size(fnzb(count)));
+	return (count * sizeof(frame_t) + bitmap_size(count));
 }
 
@@ -869,9 +608,8 @@
 pfn_t zone_external_conf_alloc(size_t count)
 {
-	size_t size = zone_conf_size(count);
-	size_t order = ispwr2(size) ? fnzb(size) : (fnzb(size) + 1);
-
-	return ADDR2PFN((uintptr_t) frame_alloc(order - FRAME_WIDTH,
-	    FRAME_LOWMEM | FRAME_ATOMIC, 0));
+	size_t frames = SIZE2FRAMES(zone_conf_size(count));
+	
+	return ADDR2PFN((uintptr_t)
+	    frame_alloc(frames, FRAME_LOWMEM | FRAME_ATOMIC, 0));
 }
 
@@ -881,5 +619,5 @@
  * @param count     Size of zone in frames.
  * @param confframe Where configuration frames are supposed to be.
- *                  Automatically checks, that we will not disturb the
+ *                  Automatically checks that we will not disturb the
  *                  kernel and possibly init. If confframe is given
  *                  _outside_ this zone, it is expected, that the area is
@@ -898,17 +636,20 @@
 	
 	if (flags & ZONE_AVAILABLE) {  /* Create available zone */
-		/* Theoretically we could have NULL here, practically make sure
+		/*
+		 * Theoretically we could have NULL here, practically make sure
 		 * nobody tries to do that. If some platform requires, remove
 		 * the assert
 		 */
 		ASSERT(confframe != ADDR2PFN((uintptr_t ) NULL));
-
+		
 		/* Update the known end of physical memory. */
 		config.physmem_end = max(config.physmem_end, PFN2ADDR(start + count));
 		
-		/* If confframe is supposed to be inside our zone, then make sure
+		/*
+		 * If confframe is supposed to be inside our zone, then make sure
 		 * it does not span kernel & init
 		 */
 		size_t confcount = SIZE2FRAMES(zone_conf_size(count));
+		
 		if ((confframe >= start) && (confframe < start + count)) {
 			for (; confframe < start + count; confframe++) {
@@ -923,6 +664,5 @@
 				
 				bool overlap = false;
-				size_t i;
-				for (i = 0; i < init.cnt; i++)
+				for (size_t i = 0; i < init.cnt; i++) {
 					if (overlaps(addr, PFN2ADDR(confcount),
 					    init.tasks[i].paddr,
@@ -931,4 +671,6 @@
 						break;
 					}
+				}
+				
 				if (overlap)
 					continue;
@@ -937,9 +679,6 @@
 			}
 			
-			if (confframe >= start + count) {
-				flags &= ~ZONE_AVAILABLE;
-				goto nonavail;
-//				panic("Cannot find configuration data for zone.");
-			}
+			if (confframe >= start + count)
+				panic("Cannot find configuration data for zone.");
 		}
 		
@@ -950,11 +689,10 @@
 		}
 		
-		buddy_system_t *buddy = (buddy_system_t *) PA2KA(PFN2ADDR(confframe));
-		zone_construct(&zones.info[znum], buddy, start, count, flags);
+		void *confdata = (void *) PA2KA(PFN2ADDR(confframe));
+		zone_construct(&zones.info[znum], start, count, flags, confdata);
 		
 		/* If confdata in zone, mark as unavailable */
 		if ((confframe >= start) && (confframe < start + count)) {
-			size_t i;
-			for (i = confframe; i < confframe + confcount; i++)
+			for (size_t i = confframe; i < confframe + confcount; i++)
 				zone_mark_unavailable(&zones.info[znum],
 				    i - zones.info[znum].base);
@@ -965,6 +703,5 @@
 		return znum;
 	}
-nonavail:
-	(void)0; // label trick
+	
 	/* Non-available zone */
 	size_t znum = zones_insert_zone(start, count, flags);
@@ -973,5 +710,6 @@
 		return (size_t) -1;
 	}
-	zone_construct(&zones.info[znum], NULL, start, count, flags);
+	
+	zone_construct(&zones.info[znum], start, count, flags, NULL);
 	
 	irq_spinlock_unlock(&zones.lock, true);
@@ -1015,25 +753,29 @@
 }
 
-/** Allocate power-of-two frames of physical memory.
- *
- * @param order Allocate exactly 2^order frames.
- * @param flags Flags for host zone selection and address processing.
- * @param pzone Preferred zone.
+/** Allocate frames of physical memory.
+ *
+ * @param count      Number of continuous frames to allocate.
+ * @param flags      Flags for host zone selection and address processing.
+ * @param constraint Indication of physical address bits that cannot be
+ *                   set in the address of the first allocated frame.
+ * @param pzone      Preferred zone.
  *
  * @return Physical address of the allocated frame.
  *
  */
-uintptr_t frame_alloc_generic(uint8_t order, frame_flags_t flags,
+uintptr_t frame_alloc_generic(size_t count, frame_flags_t flags,
     uintptr_t constraint, size_t *pzone)
 {
-	size_t size = ((size_t) 1) << order;
+	ASSERT(count > 0);
+	
 	size_t hint = pzone ? (*pzone) : 0;
+	pfn_t frame_constraint = ADDR2PFN(constraint);
 	
 	/*
 	 * If not told otherwise, we must first reserve the memory.
 	 */
-	if (!(flags & FRAME_NO_RESERVE)) 
-		reserve_force_alloc(size);
-
+	if (!(flags & FRAME_NO_RESERVE))
+		reserve_force_alloc(count);
+	
 loop:
 	irq_spinlock_lock(&zones.lock, true);
@@ -1042,9 +784,11 @@
 	 * First, find suitable frame zone.
 	 */
-	size_t znum = find_free_zone(order,
-	    FRAME_TO_ZONE_FLAGS(flags), hint);
-	
-	/* If no memory, reclaim some slab memory,
-	   if it does not help, reclaim all */
+	size_t znum = find_free_zone(count, FRAME_TO_ZONE_FLAGS(flags),
+	    frame_constraint, hint);
+	
+	/*
+	 * If no memory, reclaim some slab memory,
+	 * if it does not help, reclaim all.
+	 */
 	if ((znum == (size_t) -1) && (!(flags & FRAME_NO_RECLAIM))) {
 		irq_spinlock_unlock(&zones.lock, true);
@@ -1053,6 +797,6 @@
 		
 		if (freed > 0)
-			znum = find_free_zone(order,
-			    FRAME_TO_ZONE_FLAGS(flags), hint);
+			znum = find_free_zone(count, FRAME_TO_ZONE_FLAGS(flags),
+			    frame_constraint, hint);
 		
 		if (znum == (size_t) -1) {
@@ -1062,6 +806,6 @@
 			
 			if (freed > 0)
-				znum = find_free_zone(order,
-				    FRAME_TO_ZONE_FLAGS(flags), hint);
+				znum = find_free_zone(count, FRAME_TO_ZONE_FLAGS(flags),
+				    frame_constraint, hint);
 		}
 	}
@@ -1070,6 +814,8 @@
 		if (flags & FRAME_ATOMIC) {
 			irq_spinlock_unlock(&zones.lock, true);
+			
 			if (!(flags & FRAME_NO_RESERVE))
-				reserve_free(size);
+				reserve_free(count);
+			
 			return 0;
 		}
@@ -1082,5 +828,6 @@
 		
 		if (!THREAD)
-			panic("Cannot wait for memory to become available.");
+			panic("Cannot wait for %zu frames to become available "
+			    "(%zu available).", count, avail);
 		
 		/*
@@ -1089,11 +836,11 @@
 		
 #ifdef CONFIG_DEBUG
-		printf("Thread %" PRIu64 " waiting for %zu frames, "
-		    "%zu available.\n", THREAD->tid, size, avail);
+		printf("Thread %" PRIu64 " waiting for %zu frames "
+		    "(%zu available).\n", THREAD->tid, count, avail);
 #endif
 		
 		/*
-		 * Since the mem_avail_mtx is an active mutex, we need to disable interrupts
-		 * to prevent deadlock with TLB shootdown.
+		 * Since the mem_avail_mtx is an active mutex, we need to
+		 * disable interrupts to prevent deadlock with TLB shootdown.
 		 */
 		ipl_t ipl = interrupts_disable();
@@ -1101,7 +848,8 @@
 		
 		if (mem_avail_req > 0)
-			mem_avail_req = min(mem_avail_req, size);
+			mem_avail_req = min(mem_avail_req, count);
 		else
-			mem_avail_req = size;
+			mem_avail_req = count;
+		
 		size_t gen = mem_avail_gen;
 		
@@ -1119,6 +867,6 @@
 	}
 	
-	pfn_t pfn = zone_frame_alloc(&zones.info[znum], order)
-	    + zones.info[znum].base;
+	pfn_t pfn = zone_frame_alloc(&zones.info[znum], count,
+	    frame_constraint) + zones.info[znum].base;
 	
 	irq_spinlock_unlock(&zones.lock, true);
@@ -1130,41 +878,45 @@
 }
 
-uintptr_t frame_alloc(uint8_t order, frame_flags_t flags, uintptr_t constraint)
-{
-	return frame_alloc_generic(order, flags, constraint, NULL);
-}
-
-uintptr_t frame_alloc_noreserve(uint8_t order, frame_flags_t flags,
+uintptr_t frame_alloc(size_t count, frame_flags_t flags, uintptr_t constraint)
+{
+	return frame_alloc_generic(count, flags, constraint, NULL);
+}
+
+uintptr_t frame_alloc_noreserve(size_t count, frame_flags_t flags,
     uintptr_t constraint)
 {
-	return frame_alloc_generic(order, flags | FRAME_NO_RESERVE, constraint,
+	return frame_alloc_generic(count, flags | FRAME_NO_RESERVE, constraint,
 	    NULL);
 }
 
-/** Free a frame.
- *
- * Find respective frame structure for supplied physical frame address.
- * Decrement frame reference count. If it drops to zero, move the frame
- * structure to free list.
- *
- * @param frame Physical Address of of the frame to be freed.
+/** Free frames of physical memory.
+ *
+ * Find respective frame structures for supplied physical frames.
+ * Decrement each frame reference count. If it drops to zero, mark
+ * the frames as available.
+ *
+ * @param start Physical Address of the first frame to be freed.
+ * @param count Number of frames to free.
  * @param flags Flags to control memory reservation.
  *
  */
-void frame_free_generic(uintptr_t frame, frame_flags_t flags)
-{
-	size_t size;
+void frame_free_generic(uintptr_t start, size_t count, frame_flags_t flags)
+{
+	size_t freed = 0;
 	
 	irq_spinlock_lock(&zones.lock, true);
 	
-	/*
-	 * First, find host frame zone for addr.
-	 */
-	pfn_t pfn = ADDR2PFN(frame);
-	size_t znum = find_zone(pfn, 1, 0);
-
-	ASSERT(znum != (size_t) -1);
-	
-	size = zone_frame_free(&zones.info[znum], pfn - zones.info[znum].base);
+	for (size_t i = 0; i < count; i++) {
+		/*
+		 * First, find host frame zone for addr.
+		 */
+		pfn_t pfn = ADDR2PFN(start) + i;
+		size_t znum = find_zone(pfn, 1, 0);
+		
+		ASSERT(znum != (size_t) -1);
+		
+		freed += zone_frame_free(&zones.info[znum],
+		    pfn - zones.info[znum].base);
+	}
 	
 	irq_spinlock_unlock(&zones.lock, true);
@@ -1172,15 +924,14 @@
 	/*
 	 * Signal that some memory has been freed.
+	 * Since the mem_avail_mtx is an active mutex,
+	 * we need to disable interruptsto prevent deadlock
+	 * with TLB shootdown.
 	 */
-
-	
-	/*
-	 * Since the mem_avail_mtx is an active mutex, we need to disable interrupts
-	 * to prevent deadlock with TLB shootdown.
-	 */
+	
 	ipl_t ipl = interrupts_disable();
 	mutex_lock(&mem_avail_mtx);
+	
 	if (mem_avail_req > 0)
-		mem_avail_req -= min(mem_avail_req, size);
+		mem_avail_req -= min(mem_avail_req, freed);
 	
 	if (mem_avail_req == 0) {
@@ -1188,19 +939,20 @@
 		condvar_broadcast(&mem_avail_cv);
 	}
+	
 	mutex_unlock(&mem_avail_mtx);
 	interrupts_restore(ipl);
 	
 	if (!(flags & FRAME_NO_RESERVE))
-		reserve_free(size);
-}
-
-void frame_free(uintptr_t frame)
-{
-	frame_free_generic(frame, 0);
-}
-
-void frame_free_noreserve(uintptr_t frame)
-{
-	frame_free_generic(frame, FRAME_NO_RESERVE);
+		reserve_free(freed);
+}
+
+void frame_free(uintptr_t frame, size_t count)
+{
+	frame_free_generic(frame, count, 0);
+}
+
+void frame_free_noreserve(uintptr_t frame, size_t count)
+{
+	frame_free_generic(frame, count, FRAME_NO_RESERVE);
 }
 
@@ -1236,7 +988,7 @@
 	irq_spinlock_lock(&zones.lock, true);
 	
-	size_t i;
-	for (i = 0; i < count; i++) {
+	for (size_t i = 0; i < count; i++) {
 		size_t znum = find_zone(start + i, 1, 0);
+		
 		if (znum == (size_t) -1)  /* PFN not found */
 			continue;
@@ -1263,4 +1015,5 @@
 	/* Tell the architecture to create some memory */
 	frame_low_arch_init();
+	
 	if (config.cpu_active == 1) {
 		frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)),
@@ -1269,10 +1022,7 @@
 		    SIZE2FRAMES(config.stack_size));
 		
-		size_t i;
-		for (i = 0; i < init.cnt; i++) {
-			pfn_t pfn = ADDR2PFN(init.tasks[i].paddr);
-			frame_mark_unavailable(pfn,
+		for (size_t i = 0; i < init.cnt; i++)
+			frame_mark_unavailable(ADDR2PFN(init.tasks[i].paddr),
 			    SIZE2FRAMES(init.tasks[i].size));
-		}
 		
 		if (ballocs.size)
@@ -1280,9 +1030,11 @@
 			    SIZE2FRAMES(ballocs.size));
 		
-		/* Black list first frame, as allocating NULL would
+		/*
+		 * Blacklist first frame, as allocating NULL would
 		 * fail in some places
 		 */
 		frame_mark_unavailable(0, 1);
 	}
+	
 	frame_high_arch_init();
 }
@@ -1290,21 +1042,24 @@
 /** Adjust bounds of physical memory region according to low/high memory split.
  *
- * @param low[in]	If true, the adjustment is performed to make the region
- *			fit in the low memory. Otherwise the adjustment is
- *			performed to make the region fit in the high memory.
- * @param basep[inout]	Pointer to a variable which contains the region's base
- *			address and which may receive the adjusted base address.
- * @param sizep[inout]	Pointer to a variable which contains the region's size
- *			and which may receive the adjusted size.
- * @retun		True if the region still exists even after the
- *			adjustment, false otherwise.
+ * @param low[in]      If true, the adjustment is performed to make the region
+ *                     fit in the low memory. Otherwise the adjustment is
+ *                     performed to make the region fit in the high memory.
+ * @param basep[inout] Pointer to a variable which contains the region's base
+ *                     address and which may receive the adjusted base address.
+ * @param sizep[inout] Pointer to a variable which contains the region's size
+ *                     and which may receive the adjusted size.
+ *
+ * @return True if the region still exists even after the adjustment.
+ * @return False otherwise.
+ *
  */
 bool frame_adjust_zone_bounds(bool low, uintptr_t *basep, size_t *sizep)
 {
 	uintptr_t limit = KA2PA(config.identity_base) + config.identity_size;
-
+	
 	if (low) {
 		if (*basep > limit)
 			return false;
+		
 		if (*basep + *sizep > limit)
 			*sizep = limit - *basep;
@@ -1312,4 +1067,5 @@
 		if (*basep + *sizep <= limit)
 			return false;
+		
 		if (*basep <= limit) {
 			*sizep -= limit - *basep;
@@ -1317,4 +1073,5 @@
 		}
 	}
+	
 	return true;
 }
@@ -1328,6 +1085,6 @@
 	
 	uint64_t total = 0;
-	size_t i;
-	for (i = 0; i < zones.count; i++)
+	
+	for (size_t i = 0; i < zones.count; i++)
 		total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
 	
@@ -1352,6 +1109,5 @@
 	*free = 0;
 	
-	size_t i;
-	for (i = 0; i < zones.count; i++) {
+	for (size_t i = 0; i < zones.count; i++) {
 		*total += (uint64_t) FRAMES2SIZE(zones.info[i].count);
 		
@@ -1390,6 +1146,5 @@
 	 */
 	
-	size_t i;
-	for (i = 0;; i++) {
+	for (size_t i = 0;; i++) {
 		irq_spinlock_lock(&zones.lock, true);
 		
@@ -1444,6 +1199,5 @@
 	size_t znum = (size_t) -1;
 	
-	size_t i;
-	for (i = 0; i < zones.count; i++) {
+	for (size_t i = 0; i < zones.count; i++) {
 		if ((i == num) || (PFN2ADDR(zones.info[i].base) == num)) {
 			znum = i;
@@ -1458,9 +1212,9 @@
 	}
 	
-	uintptr_t base = PFN2ADDR(zones.info[i].base);
-	zone_flags_t flags = zones.info[i].flags;
-	size_t count = zones.info[i].count;
-	size_t free_count = zones.info[i].free_count;
-	size_t busy_count = zones.info[i].busy_count;
+	uintptr_t base = PFN2ADDR(zones.info[znum].base);
+	zone_flags_t flags = zones.info[znum].flags;
+	size_t count = zones.info[znum].count;
+	size_t free_count = zones.info[znum].free_count;
+	size_t busy_count = zones.info[znum].busy_count;
 	
 	irq_spinlock_unlock(&zones.lock, true);
Index: kernel/generic/src/mm/km.c
===================================================================
--- kernel/generic/src/mm/km.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/src/mm/km.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -247,6 +247,6 @@
 	 */
 	uintptr_t page;
-	uintptr_t frame = frame_alloc(ONE_FRAME,
-	    FRAME_HIGHMEM | FRAME_ATOMIC | flags, 0); 
+	uintptr_t frame =
+	    frame_alloc(1, FRAME_HIGHMEM | FRAME_ATOMIC | flags, 0);
 	if (frame) {
 		page = km_map(frame, PAGE_SIZE,
@@ -256,5 +256,5 @@
 		ASSERT(page);
 	} else {
-		frame = frame_alloc(ONE_FRAME, FRAME_LOWMEM | flags, 0);
+		frame = frame_alloc(1, FRAME_LOWMEM | flags, 0);
 		if (!frame)
 			return (uintptr_t) NULL;
Index: kernel/generic/src/mm/slab.c
===================================================================
--- kernel/generic/src/mm/slab.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/src/mm/slab.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -182,8 +182,10 @@
 	size_t zone = 0;
 	
-	void *data = (void *)
-	    PA2KA(frame_alloc_generic(cache->order, flags, 0, &zone));
-	if (!data)
+	uintptr_t data_phys =
+	    frame_alloc_generic(cache->frames, flags, 0, &zone);
+	if (!data_phys)
 		return NULL;
+	
+	void *data = (void *) PA2KA(data_phys);
 	
 	slab_t *slab;
@@ -193,9 +195,9 @@
 		slab = slab_alloc(slab_extern_cache, flags);
 		if (!slab) {
-			frame_free(KA2PA(data));
+			frame_free(KA2PA(data), cache->frames);
 			return NULL;
 		}
 	} else {
-		fsize = (PAGE_SIZE << cache->order);
+		fsize = FRAMES2SIZE(cache->frames);
 		slab = data + fsize - sizeof(*slab);
 	}
@@ -203,5 +205,5 @@
 	/* Fill in slab structures */
 	size_t i;
-	for (i = 0; i < ((size_t) 1 << cache->order); i++)
+	for (i = 0; i < cache->frames; i++)
 		frame_set_parent(ADDR2PFN(KA2PA(data)) + i, slab, zone);
 	
@@ -225,5 +227,5 @@
 NO_TRACE static size_t slab_space_free(slab_cache_t *cache, slab_t *slab)
 {
-	frame_free(KA2PA(slab->start));
+	frame_free(KA2PA(slab->start), slab->cache->frames);
 	if (!(cache->flags & SLAB_CACHE_SLINSIDE))
 		slab_free(slab_extern_cache, slab);
@@ -231,5 +233,5 @@
 	atomic_dec(&cache->allocated_slabs);
 	
-	return (1 << cache->order);
+	return cache->frames;
 }
 
@@ -558,8 +560,8 @@
 {
 	if (cache->flags & SLAB_CACHE_SLINSIDE)
-		return ((PAGE_SIZE << cache->order)
-		    - sizeof(slab_t)) / cache->size;
+		return (FRAMES2SIZE(cache->frames) - sizeof(slab_t)) /
+		    cache->size;
 	else
-		return (PAGE_SIZE << cache->order) / cache->size;
+		return FRAMES2SIZE(cache->frames) / cache->size;
 }
 
@@ -570,5 +572,5 @@
 {
 	size_t objects = comp_objects(cache);
-	size_t ssize = PAGE_SIZE << cache->order;
+	size_t ssize = FRAMES2SIZE(cache->frames);
 	
 	if (cache->flags & SLAB_CACHE_SLINSIDE)
@@ -634,15 +636,9 @@
 		cache->flags |= SLAB_CACHE_SLINSIDE;
 	
-	/* Minimum slab order */
-	size_t pages = SIZE2FRAMES(cache->size);
-	
-	/* We need the 2^order >= pages */
-	if (pages == 1)
-		cache->order = 0;
-	else
-		cache->order = fnzb(pages - 1) + 1;
+	/* Minimum slab frames */
+	cache->frames = SIZE2FRAMES(cache->size);
 	
 	while (badness(cache) > SLAB_MAX_BADNESS(cache))
-		cache->order += 1;
+		cache->frames <<= 1;
 	
 	cache->objects = comp_objects(cache);
@@ -870,5 +866,5 @@
 		
 		const char *name = cache->name;
-		uint8_t order = cache->order;
+		size_t frames = cache->frames;
 		size_t size = cache->size;
 		size_t objects = cache->objects;
@@ -880,6 +876,6 @@
 		irq_spinlock_unlock(&slab_cache_lock, true);
 		
-		printf("%-18s %8zu %8u %8zu %8ld %8ld %8ld %-5s\n",
-		    name, size, (1 << order), objects, allocated_slabs,
+		printf("%-18s %8zu %8zu %8zu %8ld %8ld %8ld %-5s\n",
+		    name, size, frames, objects, allocated_slabs,
 		    cached_objs, allocated_objs,
 		    flags & SLAB_CACHE_SLINSIDE ? "in" : "out");
Index: kernel/generic/src/proc/thread.c
===================================================================
--- kernel/generic/src/proc/thread.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/src/proc/thread.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -192,7 +192,7 @@
 	kmflags &= ~FRAME_HIGHMEM;
 	
-	thread->kstack = (uint8_t *)
-	    PA2KA(frame_alloc(STACK_FRAMES, kmflags, 0));
-	if (!thread->kstack) {
+	uintptr_t stack_phys =
+	    frame_alloc(STACK_FRAMES, kmflags, STACK_SIZE - 1);
+	if (!stack_phys) {
 #ifdef CONFIG_FPU
 		if (thread->saved_fpu_context)
@@ -202,4 +202,6 @@
 	}
 	
+	thread->kstack = (uint8_t *) PA2KA(stack_phys);
+	
 #ifdef CONFIG_UDEBUG
 	mutex_initialize(&thread->udebug.lock, MUTEX_PASSIVE);
@@ -217,5 +219,5 @@
 	thr_destructor_arch(thread);
 	
-	frame_free(KA2PA(thread->kstack));
+	frame_free(KA2PA(thread->kstack), STACK_FRAMES);
 	
 #ifdef CONFIG_FPU
Index: kernel/generic/src/time/clock.c
===================================================================
--- kernel/generic/src/time/clock.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/generic/src/time/clock.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -81,5 +81,5 @@
 void clock_counter_init(void)
 {
-	uintptr_t faddr = frame_alloc(ONE_FRAME, FRAME_ATOMIC, 0);
+	uintptr_t faddr = frame_alloc(1, FRAME_ATOMIC, 0);
 	if (faddr == 0)
 		panic("Cannot allocate page for clock.");
Index: kernel/test/mm/falloc1.c
===================================================================
--- kernel/test/mm/falloc1.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/test/mm/falloc1.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -37,5 +37,5 @@
 #include <align.h>
 
-#define MAX_FRAMES  1024U
+#define MAX_FRAMES  1024
 #define MAX_ORDER   8
 #define TEST_RUNS   2
@@ -51,24 +51,19 @@
 		return "Unable to allocate frames";
 	
-	unsigned int results[MAX_ORDER + 1];
+	unsigned int results[MAX_FRAMES + 1];
+	
 	for (unsigned int run = 0; run < TEST_RUNS; run++) {
-		for (unsigned int order = 0; order <= MAX_ORDER; order++) {
-			TPRINTF("Allocating %u frames blocks ... ", 1 << order);
+		for (size_t count = 1; count <= MAX_FRAMES; count++) {
+			size_t bytes = FRAMES2SIZE(count);
+			
+			TPRINTF("Allocating %zu frames blocks (%zu bytes) ... ",
+			    count, bytes);
 			
 			unsigned int allocated = 0;
-			for (unsigned int i = 0; i < (MAX_FRAMES >> order); i++) {
-				frames[allocated] =
-				    PA2KA(frame_alloc(order, FRAME_ATOMIC, 0));
-				
-				if (ALIGN_UP(frames[allocated], FRAME_SIZE << order) !=
-				    frames[allocated]) {
-					TPRINTF("Block at address %p (size %u) is not aligned\n",
-					    (void *) frames[allocated], (FRAME_SIZE << order) >> 10);
-					return "Test failed";
-				}
-				
-				if (frames[allocated])
+			for (unsigned int i = 0; i < (MAX_FRAMES / count); i++) {
+				frames[allocated] = frame_alloc(count, FRAME_ATOMIC, 0);
+				if (frames[allocated]) {
 					allocated++;
-				else {
+				} else {
 					TPRINTF("done. ");
 					break;
@@ -78,14 +73,14 @@
 			TPRINTF("%d blocks allocated.\n", allocated);
 			
-			if (run) {
-				if (results[order] != allocated)
+			if (run > 0) {
+				if (results[count] != allocated)
 					return "Possible frame leak";
 			} else
-				results[order] = allocated;
+				results[count] = allocated;
 			
 			TPRINTF("Deallocating ... ");
 			
 			for (unsigned int i = 0; i < allocated; i++)
-				frame_free(KA2PA(frames[i]));
+				frame_free(frames[i], count);
 			
 			TPRINTF("done.\n");
Index: kernel/test/mm/falloc2.c
===================================================================
--- kernel/test/mm/falloc2.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/test/mm/falloc2.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -40,6 +40,5 @@
 #include <arch.h>
 
-#define MAX_FRAMES  256U
-#define MAX_ORDER   8
+#define MAX_FRAMES  256
 
 #define THREAD_RUNS  1
@@ -66,15 +65,16 @@
 	
 	for (unsigned int run = 0; run < THREAD_RUNS; run++) {
-		for (unsigned int order = 0; order <= MAX_ORDER; order++) {
+		for (size_t count = 1; count <= MAX_FRAMES; count++) {
+			size_t bytes = FRAMES2SIZE(count);
+			
 			TPRINTF("Thread #%" PRIu64 " (cpu%u): "
-			    "Allocating %u frames blocks ... \n", THREAD->tid,
-			    CPU->id, 1 << order);
+			    "Allocating %zu frames blocks (%zu bytes) ... \n", THREAD->tid,
+			    CPU->id, count, bytes);
 			
 			unsigned int allocated = 0;
-			for (unsigned int i = 0; i < (MAX_FRAMES >> order); i++) {
-				frames[allocated] =
-				    PA2KA(frame_alloc(order, FRAME_ATOMIC, 0));
+			for (unsigned int i = 0; i < (MAX_FRAMES / count); i++) {
+				frames[allocated] = frame_alloc(count, FRAME_ATOMIC, 0);
 				if (frames[allocated]) {
-					memsetb((void *) frames[allocated], FRAME_SIZE << order, val);
+					memsetb((void *) PA2KA(frames[allocated]), bytes, val);
 					allocated++;
 				} else
@@ -89,10 +89,9 @@
 			
 			for (unsigned int i = 0; i < allocated; i++) {
-				for (size_t k = 0; k <= (((size_t) FRAME_SIZE << order) - 1);
-				    k++) {
-					if (((uint8_t *) frames[i])[k] != val) {
+				for (size_t k = 0; k < bytes; k++) {
+					if (((uint8_t *) PA2KA(frames[i]))[k] != val) {
 						TPRINTF("Thread #%" PRIu64 " (cpu%u): "
 						    "Unexpected data (%c) in block %zu offset %zu\n",
-						    THREAD->tid, CPU->id, ((char *) frames[i])[k],
+						    THREAD->tid, CPU->id, ((char *) PA2KA(frames[i]))[k],
 						    frames[i], k);
 						atomic_inc(&thread_fail);
@@ -100,5 +99,5 @@
 					}
 				}
-				frame_free(KA2PA(frames[i]));
+				frame_free(frames[i], count);
 			}
 			
Index: kernel/test/mm/mapping1.c
===================================================================
--- kernel/test/mm/mapping1.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ kernel/test/mm/mapping1.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -41,14 +41,12 @@
 const char *test_mapping1(void)
 {
-	uintptr_t page0, page1;
-	uint32_t v;
+	uintptr_t frame = frame_alloc(1, FRAME_NONE, 0);
 	
-	uintptr_t frame = frame_alloc(ONE_FRAME, FRAME_NONE, 0);
-	
-	page0 = km_map(frame, FRAME_SIZE,
+	uintptr_t page0 = km_map(frame, FRAME_SIZE,
 	    PAGE_READ | PAGE_WRITE | PAGE_CACHEABLE);
 	TPRINTF("Virtual address %p mapped to physical address %p.\n",
 	    (void *) page0, (void *) frame);
-	page1 = km_map(frame, FRAME_SIZE,
+	
+	uintptr_t page1 = km_map(frame, FRAME_SIZE,
 	    PAGE_READ | PAGE_WRITE | PAGE_CACHEABLE);
 	TPRINTF("Virtual address %p mapped to physical address %p.\n",
@@ -62,10 +60,10 @@
 		TPRINTF("Reading magic using the second virtual address.\n");
 		
-		v = *((uint32_t *) page1);
+		uint32_t v = *((uint32_t *) page1);
 		
 		if (v != TEST_MAGIC) {
 			km_unmap(page0, PAGE_SIZE);
 			km_unmap(page1, PAGE_SIZE);
-			frame_free(frame);
+			frame_free(frame, 1);
 			return "Criss-cross read does not match the value written.";
 		}
@@ -82,5 +80,5 @@
 			km_unmap(page0, PAGE_SIZE);
 			km_unmap(page1, PAGE_SIZE);
-			frame_free(frame);
+			frame_free(frame, 1);
 			return "Criss-cross read does not match the value written.";
 		}
@@ -89,5 +87,5 @@
 	km_unmap(page0, PAGE_SIZE);
 	km_unmap(page1, PAGE_SIZE);
-	frame_free(frame);
+	frame_free(frame, 1);
 	
 	return NULL;
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -81,4 +81,5 @@
 	app/vlaunch \
 	app/vterm \
+	app/df \
 	app/wavplay \
 	app/websrv \
Index: uspace/app/devctl/devctl.c
===================================================================
--- uspace/app/devctl/devctl.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/app/devctl/devctl.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -35,4 +35,5 @@
 #include <devman.h>
 #include <errno.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -44,6 +45,28 @@
 #define MAX_NAME_LENGTH 1024
 
-char name[MAX_NAME_LENGTH];
-char drv_name[MAX_NAME_LENGTH];
+static char name[MAX_NAME_LENGTH];
+static char drv_name[MAX_NAME_LENGTH];
+static bool verbose = false;
+
+static const char *drv_state_str(driver_state_t state)
+{
+	const char *sstate;
+
+	switch (state) {
+	case DRIVER_NOT_STARTED:
+		sstate = "not started";
+		break;
+	case DRIVER_STARTING:
+		sstate = "starting";
+		break;
+	case DRIVER_RUNNING:
+		sstate = "running";
+		break;
+	default:
+		sstate = "unknown";
+	}
+
+	return sstate;
+}
 
 static int fun_subtree_print(devman_handle_t funh, int lvl)
@@ -52,4 +75,5 @@
 	devman_handle_t *cfuns;
 	size_t count, i;
+	unsigned int score;
 	int rc;
 	int j;
@@ -74,4 +98,18 @@
 		printf("%s : %s\n", name, drv_name);
 
+	if (verbose) {
+		for (i = 0; true; i++) {
+			rc = devman_fun_get_match_id(funh, i, name, MAX_NAME_LENGTH,
+			    &score);
+			if (rc != EOK)
+				break;
+
+			for (j = 0; j < lvl; j++)
+				printf("    ");
+
+			printf("    %u %s\n", score, name);
+		}
+	}
+
 	rc = devman_fun_get_child(funh, &devh);
 	if (rc == ENOENT)
@@ -159,7 +197,137 @@
 }
 
+static int drv_list(void)
+{
+	devman_handle_t *devs;
+	devman_handle_t *drvs;
+	driver_state_t state;
+	const char *sstate;
+	size_t ndrvs;
+	size_t ndevs;
+	size_t i;
+	int rc;
+
+	rc = devman_get_drivers(&drvs, &ndrvs);
+	if (rc != EOK)
+		return rc;
+
+	for (i = 0; i < ndrvs; i++) {
+		devs = NULL;
+
+		rc = devman_driver_get_name(drvs[i], drv_name, MAX_NAME_LENGTH);
+		if (rc != EOK)
+			goto skip;
+		rc = devman_driver_get_state(drvs[i], &state);
+		if (rc != EOK)
+			goto skip;
+		rc = devman_driver_get_devices(drvs[i], &devs, &ndevs);
+		if (rc != EOK)
+			goto skip;
+
+		sstate = drv_state_str(state);
+
+		printf("%-11s %3zu %s\n", sstate, ndevs, drv_name);
+skip:
+		free(devs);
+	}
+	free(drvs);
+
+	return EOK;
+}
+
+static int drv_show(char *drvname)
+{
+	devman_handle_t *devs;
+	devman_handle_t drvh;
+	devman_handle_t funh;
+	driver_state_t state;
+	const char *sstate;
+	unsigned int score;
+	size_t ndevs;
+	size_t i;
+	int rc;
+
+	rc = devman_driver_get_handle(drvname, &drvh);
+	if (rc != EOK)
+		return rc;
+
+	devs = NULL;
+
+	rc = devman_driver_get_name(drvh, drv_name, MAX_NAME_LENGTH);
+	if (rc != EOK)
+		return rc;
+
+	rc = devman_driver_get_state(drvh, &state);
+	if (rc != EOK)
+		return rc;
+
+	rc = devman_driver_get_devices(drvh, &devs, &ndevs);
+	if (rc != EOK)
+		return rc;
+
+	sstate = drv_state_str(state);
+
+	printf("Driver: %s\n", drv_name);
+	printf("State: %s\n", sstate);
+
+	printf("Attached devices:\n");
+
+	for (i = 0; i < ndevs; i++) {
+		rc = devman_dev_get_parent(devs[i], &funh);
+		if (rc != EOK)
+			goto error;
+
+		rc = devman_fun_get_path(funh, name, MAX_NAME_LENGTH);
+		if (rc != EOK)
+			goto error;
+		printf("\t%s\n", name);
+	}
+
+	printf("Match IDs:\n");
+
+	for (i = 0; true; i++) {
+		rc = devman_driver_get_match_id(drvh, i, name, MAX_NAME_LENGTH,
+		    &score);
+		if (rc != EOK)
+			break;
+
+		printf("\t%u %s\n", score, name);
+	}
+
+error:
+	free(devs);
+
+	return EOK;
+}
+
+static int drv_load(const char *drvname)
+{
+	int rc;
+	devman_handle_t drvh;
+
+	rc = devman_driver_get_handle(drvname, &drvh);
+	if (rc != EOK) {
+		printf("Failed resolving driver '%s' (%d).\n", drvname, rc);
+		return rc;
+	}
+
+	rc = devman_driver_load(drvh);
+	if (rc != EOK) {
+		printf("Failed loading driver '%s' (%d).\n", drvname, rc);
+		return rc;
+	}
+
+	return EOK;
+}
+
 static void print_syntax(void)
 {
-	printf("syntax: devctl [(online|offline) <function>]\n");
+	printf("syntax:\n");
+	printf("\tdevctl\n");
+	printf("\tdevctl online <function>]\n");
+	printf("\tdevctl offline <function>]\n");
+	printf("\tdevctl list-drv\n");
+	printf("\tdevctl show-drv <driver-name>\n");
+	printf("\tdevctl load-drv <driver-name>\n");
 }
 
@@ -168,5 +336,14 @@
 	int rc;
 
-	if (argc == 1) {
+	if (argc == 1 || argv[1][0] == '-') {
+		if (argc > 1) {
+			if (str_cmp(argv[1], "-v") == 0) {
+				verbose = true;
+			} else {
+				printf(NAME ": Invalid argument '%s'\n", argv[1]);
+				print_syntax();
+				return 1;
+			}
+		}
 		rc = fun_tree_print();
 		if (rc != EOK)
@@ -194,4 +371,29 @@
 			return 2;
 		}
+	} else if (str_cmp(argv[1], "list-drv") == 0) {
+		rc = drv_list();
+		if (rc != EOK)
+			return 2;
+	} else if (str_cmp(argv[1], "show-drv") == 0) {
+		if (argc < 3) {
+			printf(NAME ": Argument missing.\n");
+			print_syntax();
+			return 1;
+		}
+
+		rc = drv_show(argv[2]);
+		if (rc != EOK) {
+			return 2;
+		}
+	} else if (str_cmp(argv[1], "load-drv") == 0) {
+		if (argc < 3) {
+			printf(NAME ": Argument missing.\n");
+			print_syntax();
+			return 1;
+		}
+
+		rc = drv_load(argv[2]);
+		if (rc != EOK)
+			return 2;
 	} else {
 		printf(NAME ": Invalid argument '%s'.\n", argv[1]);
Index: uspace/app/df/Makefile
===================================================================
--- uspace/app/df/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/app/df/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2013 Manuele Conti
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+BINARY = df
+
+SOURCES = \
+	df.c 
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/df/df.c
===================================================================
--- uspace/app/df/df.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/app/df/df.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2013 Manuele Conti
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup df
+ * @brief Df utility.
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <sys/statfs.h>
+#include <errno.h>
+#include <adt/list.h>
+#include <vfs/vfs.h>
+
+#define NAME  "df"
+
+#define HEADER_TABLE 	"Filesystem     %4u-blocks           Used      Available Used%% Mounted on"
+#define HEADER_TABLE_HR "Filesystem           Size           Used      Available Used%% Mounted on"
+
+#define PERCENTAGE(x, tot) ((unsigned long long) (100L * (x) / (tot)))
+#define FSBK_TO_BK(x, fsbk, bk) \
+	(((fsbk) != 0 && (fsbk) < (bk)) ? \
+		(unsigned long long) ((x) / ((bk) / (fsbk))) : \
+		(unsigned long long) ((x) * ((fsbk) / (bk))))
+
+static unsigned int unit_size;
+static unsigned int human_readable;
+
+static int size_to_human_readable(char buf[], uint64_t bytes);
+static void print_header(void);
+static void print_statfs(struct statfs *, char *, char *);
+static void print_usage(void);
+
+int main(int argc, char *argv[])
+{
+	int optres, errflg = 0;
+	struct statfs st;
+
+	unit_size = 512;
+	human_readable = 0;
+
+	/******************************************/
+	/*   Parse command line options...        */
+	/******************************************/
+	while ((optres = getopt(argc, argv, ":uhb:")) != -1) {
+		switch(optres) {
+		case 'u':
+			print_usage();
+			return 0;
+
+		case 'h':
+			human_readable = 1;
+			break;
+
+		case 'b':
+			str_uint32_t(optarg, NULL, 0, 0, &unit_size);
+			break;
+ 
+		case ':':
+			fprintf(stderr, "Option -%c requires an operand\n", optopt);
+			errflg++;
+			break;
+
+		case '?':
+			fprintf(stderr, "Unrecognized option: -%c\n", optopt);
+			errflg++;
+			break;
+
+		default:
+			fprintf(stderr, "Unknown error while parsing command line options.");
+			errflg++;
+			break;
+		}
+	}
+
+	if (optind > argc) {
+		fprintf(stderr, "Too many input parameter\n");
+		errflg++;
+	}
+
+	if (errflg) {
+		print_usage();
+		return 1;
+	}
+
+	LIST_INITIALIZE(mtab_list);
+	get_mtab_list(&mtab_list);
+	print_header();
+	list_foreach(mtab_list, link, mtab_ent_t, mtab_ent) {
+		statfs(mtab_ent->mp, &st);
+		print_statfs(&st, mtab_ent->fs_name, mtab_ent->mp);
+	}
+	putchar('\n');
+	return 0;
+}
+
+static int size_to_human_readable(char buf[], uint64_t bytes)
+{
+	const char *units = "BkMGTPEZY";
+	int i = 0;
+	int limit;
+
+	limit = str_length(units);
+	while (bytes >= 1024) {
+		if (i >= limit) 
+			return -1;
+		bytes /= 1024;
+		i++;
+	}
+	snprintf(buf, 6, "%4llu%c", (unsigned long long)bytes, units[i]);
+
+	return 0;
+}
+
+static void print_header(void)
+{
+	if (human_readable)
+		printf(HEADER_TABLE_HR);
+	else
+		printf(HEADER_TABLE, unit_size);
+	putchar('\n');
+}
+
+static void print_statfs(struct statfs *st, char *name, char *mountpoint)
+{
+	printf("%10s", name);
+
+	if (human_readable) {
+		char tmp[1024];
+		size_to_human_readable(tmp, st->f_blocks *  st->f_bsize);
+		printf(" %14s", tmp);                                                                  /* Size       */
+		size_to_human_readable(tmp, (st->f_blocks - st->f_bfree)  *  st->f_bsize);
+		printf(" %14s", tmp);                                                                  /* Used       */
+		size_to_human_readable(tmp, st->f_bfree *  st->f_bsize);
+		printf(" %14s", tmp);                                                                  /* Available  */
+		printf(" %4llu%% %s\n", 
+			(st->f_blocks)?PERCENTAGE(st->f_blocks - st->f_bfree, st->f_blocks):0L,        /* Used%      */
+			mountpoint                                                                     /* Mounted on */
+		);
+	}
+	else
+		printf(" %15llu %14llu %14llu %4llu%% %s\n", 
+			FSBK_TO_BK(st->f_blocks, st->f_bsize, unit_size),                              /* Blocks     */
+			FSBK_TO_BK(st->f_blocks - st->f_bfree, st->f_bsize, unit_size),                /* Used       */
+			FSBK_TO_BK(st->f_bfree, st->f_bsize, unit_size),                               /* Available  */
+			(st->f_blocks)?PERCENTAGE(st->f_blocks - st->f_bfree, st->f_blocks):0L,        /* Used%      */
+			mountpoint                                                                     /* Mounted on */
+		);
+
+}
+
+static void print_usage(void)
+{
+	printf("syntax: %s [-u] [-h] [-b <size>] \n", NAME);
+	printf("  u : Show usage.\n");
+	printf("  h : \"Human-readable\" output.\n");
+	printf("  b : Scale block sizes by selected size.\n");
+	printf("\n");
+}
+
+/** @}
+ */
Index: uspace/app/trace/trace.c
===================================================================
--- uspace/app/trace/trace.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/app/trace/trace.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -724,4 +724,6 @@
 	o = oper_new("stat", 0, arg_def, V_ERRNO, 0, resp_def);
 	proto_add_oper(p, VFS_IN_STAT, o);
+	o = oper_new("statfs", 0, arg_def, V_ERRNO, 0, resp_def);
+	proto_add_oper(p, VFS_IN_STATFS, o);
 
 	proto_register(SERVICE_VFS, p);
Index: uspace/drv/bus/isa/i8237.c
===================================================================
--- uspace/drv/bus/isa/i8237.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/drv/bus/isa/i8237.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -279,4 +279,5 @@
  *
  * @return Error code.
+ *
  */
 static inline int dma_controller_init(dma_controller_t *controller)
Index: uspace/drv/bus/isa/isa.c
===================================================================
--- uspace/drv/bus/isa/isa.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/drv/bus/isa/isa.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -67,4 +67,5 @@
 
 #include <device/hw_res.h>
+#include <device/pio_window.h>
 
 #include "i8237.h"
@@ -79,4 +80,5 @@
 	ddf_dev_t *dev;
 	ddf_fun_t *fctl;
+	pio_window_t pio_win;
 	list_t functions;
 } isa_bus_t;
@@ -405,7 +407,10 @@
 	hw_resource_t *resources = fun->hw_resources.resources;
 
+	isa_bus_t *isa = isa_bus(ddf_fun_get_dev(fun->fnode));
+
 	if (count < ISA_MAX_HW_RES) {
 		resources[count].type = IO_RANGE;
 		resources[count].res.io_range.address = addr;
+		resources[count].res.io_range.address += isa->pio_win.io.base;
 		resources[count].res.io_range.size = len;
 		resources[count].res.io_range.endianness = LITTLE_ENDIAN;
@@ -604,4 +609,7 @@
 static int isa_dev_add(ddf_dev_t *dev)
 {
+	async_sess_t *sess;
+	int rc;
+
 	ddf_msg(LVL_DEBUG, "isa_dev_add, device handle = %d",
 	    (int) ddf_dev_get_handle(dev));
@@ -614,4 +622,18 @@
 	isa->dev = dev;
 	list_initialize(&isa->functions);
+
+	sess = ddf_dev_parent_sess_create(dev, EXCHANGE_SERIALIZE);
+	if (sess == NULL) {
+		ddf_msg(LVL_ERROR, "isa_dev_add failed to connect to the "
+		    "parent driver.");
+		return ENOENT;
+	}
+
+	rc = pio_window_get(sess, &isa->pio_win);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "isa_dev_add failed to get PIO window "
+		    "for the device.");
+		return rc;
+	}	
 
 	/* Make the bus device more visible. Does not do anything. */
Index: uspace/drv/bus/pci/pciintel/pci.c
===================================================================
--- uspace/drv/bus/pci/pciintel/pci.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/drv/bus/pci/pciintel/pci.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -57,4 +57,6 @@
 #include <ops/hw_res.h>
 #include <device/hw_res.h>
+#include <ops/pio_window.h>
+#include <device/pio_window.h>
 #include <ddi.h>
 #include <pci_dev_iface.h>
@@ -141,4 +143,14 @@
 }
 
+static pio_window_t *pciintel_get_pio_window(ddf_fun_t *fnode)
+{
+	pci_fun_t *fun = pci_fun(fnode);
+	
+	if (fun == NULL)
+		return NULL;
+	return &fun->pio_window;
+}
+
+
 static int pci_config_space_write_32(ddf_fun_t *fun, uint32_t address,
     uint32_t data)
@@ -198,4 +210,8 @@
 	.get_resource_list = &pciintel_get_resources,
 	.enable_interrupt = &pciintel_enable_interrupt,
+};
+
+static pio_window_ops_t pciintel_pio_window_ops = {
+	.get_pio_window = &pciintel_get_pio_window
 };
 
@@ -211,4 +227,5 @@
 static ddf_dev_ops_t pci_fun_ops = {
 	.interfaces[HW_RES_DEV_IFACE] = &pciintel_hw_res_ops,
+	.interfaces[PIO_WINDOW_DEV_IFACE] = &pciintel_pio_window_ops,
 	.interfaces[PCI_DEV_IFACE] = &pci_dev_ops
 };
@@ -552,5 +569,7 @@
 {
 	uint8_t irq = pci_conf_read_8(fun, PCI_BRIDGE_INT_LINE);
-	if (irq != 0xff)
+	uint8_t pin = pci_conf_read_8(fun, PCI_BRIDGE_INT_PIN);
+
+	if (pin != 0 && irq != 0xff)
 		pci_add_interrupt(fun, irq);
 }
@@ -615,4 +634,7 @@
 			pci_read_bars(fun);
 			pci_read_interrupt(fun);
+
+			/* Propagate the PIO window to the function. */
+			fun->pio_window = bus->pio_win;
 			
 			ddf_fun_set_ops(fun->fnode, &pci_fun_ops);
@@ -645,4 +667,5 @@
 static int pci_dev_add(ddf_dev_t *dnode)
 {
+	hw_resource_list_t hw_resources;
 	pci_bus_t *bus = NULL;
 	ddf_fun_t *ctl = NULL;
@@ -670,6 +693,11 @@
 		goto fail;
 	}
-	
-	hw_resource_list_t hw_resources;
+
+	rc = pio_window_get(sess, &bus->pio_win);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "pci_dev_add failed to get PIO window "
+		    "for the device.");
+		goto fail;
+	}
 	
 	rc = hw_res_get_resource_list(sess, &hw_resources);
@@ -761,6 +789,4 @@
 {
 	ddf_log_init(NAME);
-	pci_fun_ops.interfaces[HW_RES_DEV_IFACE] = &pciintel_hw_res_ops;
-	pci_fun_ops.interfaces[PCI_DEV_IFACE] = &pci_dev_ops;
 }
 
Index: uspace/drv/bus/pci/pciintel/pci.h
===================================================================
--- uspace/drv/bus/pci/pciintel/pci.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/drv/bus/pci/pciintel/pci.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -40,5 +40,5 @@
 #include "pci_regs.h"
 
-#define PCI_MAX_HW_RES 8
+#define PCI_MAX_HW_RES 10 
 
 typedef struct pciintel_bus {
@@ -49,4 +49,5 @@
 	void *conf_data_port;
 	void *conf_addr_port;
+	pio_window_t pio_win;
 	fibril_mutex_t conf_mutex;
 } pci_bus_t;
@@ -68,4 +69,5 @@
 	hw_resource_list_t hw_resources;
 	hw_resource_t resources[PCI_MAX_HW_RES];
+	pio_window_t pio_window;
 } pci_fun_t;
 
Index: uspace/drv/infrastructure/rootmalta/rootmalta.c
===================================================================
--- uspace/drv/infrastructure/rootmalta/rootmalta.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/drv/infrastructure/rootmalta/rootmalta.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -53,4 +53,6 @@
 #include <ops/hw_res.h>
 #include <device/hw_res.h>
+#include <ops/pio_window.h>
+#include <device/pio_window.h>
 #include <byteorder.h>
 
@@ -66,6 +68,13 @@
 #define GT_PCI_CMD_MBYTESWAP	0x1
 
+#define GT_PCI_MEMBASE	UINT32_C(0x10000000)
+#define GT_PCI_MEMSIZE	UINT32_C(0x08000000)
+
+#define GT_PCI_IOBASE	UINT32_C(0x18000000)
+#define GT_PCI_IOSIZE	UINT32_C(0x00200000)
+
 typedef struct rootmalta_fun {
 	hw_resource_list_t hw_resources;
+	pio_window_t pio_window;
 } rootmalta_fun_t;
 
@@ -107,4 +116,14 @@
 		sizeof(pci_conf_regs) / sizeof(pci_conf_regs[0]),
 		pci_conf_regs
+	},
+	.pio_window = {
+		.mem = {
+			.base = GT_PCI_MEMBASE,
+			.size = GT_PCI_MEMSIZE
+		},
+		.io = {
+			.base = GT_PCI_IOBASE,
+			.size = GT_PCI_IOSIZE
+		}
 	}
 };
@@ -129,4 +148,12 @@
 	
 	return false;
+}
+
+static pio_window_t *rootmalta_get_pio_window(ddf_fun_t *fnode)
+{
+	rootmalta_fun_t *fun = rootmalta_fun(fnode);
+
+	assert(fun != NULL);
+	return &fun->pio_window;
 }
 
@@ -134,4 +161,8 @@
 	.get_resource_list = &rootmalta_get_resources,
 	.enable_interrupt = &rootmalta_enable_interrupt,
+};
+
+static pio_window_ops_t fun_pio_window_ops = {
+	.get_pio_window = &rootmalta_get_pio_window
 };
 
@@ -228,4 +259,5 @@
 	ddf_log_init(NAME);
 	rootmalta_fun_ops.interfaces[HW_RES_DEV_IFACE] = &fun_hw_res_ops;
+	rootmalta_fun_ops.interfaces[PIO_WINDOW_DEV_IFACE] = &fun_pio_window_ops;
 }
 
Index: uspace/drv/infrastructure/rootpc/rootpc.c
===================================================================
--- uspace/drv/infrastructure/rootpc/rootpc.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/drv/infrastructure/rootpc/rootpc.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -51,4 +51,6 @@
 #include <ops/hw_res.h>
 #include <device/hw_res.h>
+#include <ops/pio_window.h>
+#include <device/pio_window.h>
 
 #define NAME "rootpc"
@@ -56,4 +58,5 @@
 typedef struct rootpc_fun {
 	hw_resource_list_t hw_resources;
+	pio_window_t pio_window;
 } rootpc_fun_t;
 
@@ -93,6 +96,16 @@
 static rootpc_fun_t pci_data = {
 	.hw_resources = {
-		sizeof(pci_conf_regs)/sizeof(pci_conf_regs[0]),
+		sizeof(pci_conf_regs) / sizeof(pci_conf_regs[0]),
 		pci_conf_regs
+	},
+	.pio_window = {
+		.mem = {
+			.base = UINT32_C(0),
+			.size = UINT32_C(0xffffffff) /* practical maximum */
+		},
+		.io = {
+			.base = UINT32_C(0),
+			.size = UINT32_C(0x10000)
+		}
 	}
 };
@@ -117,4 +130,12 @@
 	
 	return false;
+}
+
+static pio_window_t *rootpc_get_pio_window(ddf_fun_t *fnode)
+{
+	rootpc_fun_t *fun = rootpc_fun(fnode);
+	
+	assert(fun != NULL);
+	return &fun->pio_window;
 }
 
@@ -122,4 +143,8 @@
 	.get_resource_list = &rootpc_get_resources,
 	.enable_interrupt = &rootpc_enable_interrupt,
+};
+
+static pio_window_ops_t fun_pio_window_ops = {
+	.get_pio_window = &rootpc_get_pio_window
 };
 
@@ -197,4 +222,5 @@
 	ddf_log_init(NAME);
 	rootpc_fun_ops.interfaces[HW_RES_DEV_IFACE] = &fun_hw_res_ops;
+	rootpc_fun_ops.interfaces[PIO_WINDOW_DEV_IFACE] = &fun_pio_window_ops;
 }
 
Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -69,4 +69,5 @@
 	generic/device/hw_res.c \
 	generic/device/hw_res_parsed.c \
+	generic/device/pio_window.c \
 	generic/device/char_dev.c \
 	generic/device/clock_dev.c \
Index: uspace/lib/c/generic/device/hw_res.c
===================================================================
--- uspace/lib/c/generic/device/hw_res.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/generic/device/hw_res.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -44,6 +44,5 @@
 	
 	async_exch_t *exch = async_exchange_begin(sess);
-	if (exch == NULL)
-		return ENOMEM;
+	
 	int rc = async_req_1_1(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
 	    HW_RES_GET_RESOURCE_LIST, &count);
@@ -79,6 +78,5 @@
 {
 	async_exch_t *exch = async_exchange_begin(sess);
-	if (exch == NULL)
-		return false;
+	
 	int rc = async_req_1_0(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
 	    HW_RES_ENABLE_INTERRUPT);
@@ -88,16 +86,17 @@
 }
 
-/**
- * Setup DMA channel to specified place and mode.
- * @param channel DMA Channel 1,2,3 for 8 bit transfers, 5,6,7 for 16 bit.
- * @param pa Physical address of the buffer. Must be < 16MB for 16 bit and < 1MB
- *           for 8 bit transfers.
- * @param size DMA buffer size, limited to 64K.
- * @param mode Mode of the DMA channel:
- *              - Read or Write
- *              - Allow automatic reset
- *              - Use address decrement instead of increment
- *              - Use SINGLE/BLOCK/ON DEMAND transfer mode
+/** Setup DMA channel to specified place and mode.
+ *
+ * @param channel DMA channel.
+ * @param pa      Physical address of the buffer.
+ * @param size    DMA buffer size.
+ * @param mode    Mode of the DMA channel:
+ *                 - Read or Write
+ *                 - Allow automatic reset
+ *                 - Use address decrement instead of increment
+ *                 - Use SINGLE/BLOCK/ON DEMAND transfer mode
+ *
  * @return Error code.
+ *
  */
 int hw_res_dma_channel_setup(async_sess_t *sess,
@@ -105,30 +104,35 @@
 {
 	async_exch_t *exch = async_exchange_begin(sess);
-	if (exch == NULL)
-		return ENOMEM;
+	
 	const uint32_t packed = (channel & 0xffff) | (mode << 16);
 	const int ret = async_req_4_0(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
 	    HW_RES_DMA_CHANNEL_SETUP, packed, pa, size);
+	
 	async_exchange_end(exch);
-
+	
 	return ret;
 }
 
-/**
- * Query remaining bytes in the buffer.
- * @param channel DMA Channel 1,2,3 for 8 bit transfers, 5,6,7 for 16 bit.
- * @return Number of bytes remaining in the buffer(>=0) or error code(<0).
+/** Query remaining bytes in the buffer.
+ *
+ * @param channel DMA channel.
+ *
+ * @return Number of bytes remaining in the buffer if positive.
+ * @return Error code if negative.
+ *
  */
 int hw_res_dma_channel_remain(async_sess_t *sess, unsigned channel)
 {
 	async_exch_t *exch = async_exchange_begin(sess);
-	if (exch == NULL)
-		return ENOMEM;
+	
 	sysarg_t remain;
 	const int ret = async_req_2_1(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
 	    HW_RES_DMA_CHANNEL_REMAIN, channel, &remain);
+	
 	async_exchange_end(exch);
+	
 	if (ret == EOK)
 		return remain;
+	
 	return ret;
 }
Index: uspace/lib/c/generic/device/pio_window.c
===================================================================
--- uspace/lib/c/generic/device/pio_window.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/lib/c/generic/device/pio_window.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2013 Jakub Jermar 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+ /** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#include <device/pio_window.h>
+#include <errno.h>
+#include <async.h>
+
+int pio_window_get(async_sess_t *sess, pio_window_t *pio_win)
+{
+	int rc;
+
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (!exch)
+		return ENOMEM;
+
+	rc = async_req_1_0(exch, DEV_IFACE_ID(PIO_WINDOW_DEV_IFACE),
+	    PIO_WINDOW_GET);
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		return rc;
+	}
+	
+	rc = async_data_read_start(exch, pio_win, sizeof(*pio_win));
+	async_exchange_end(exch);
+	
+	return rc;
+}
+
+/** @}
+ */
+
Index: uspace/lib/c/generic/devman.c
===================================================================
--- uspace/lib/c/generic/devman.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/generic/devman.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2007 Josef Cejka
- * Copyright (c) 2011 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
  * Copyright (c) 2010 Lenka Trochtova
  * All rights reserved.
@@ -401,6 +401,6 @@
 }
 
-static int devman_get_str_internal(sysarg_t method, sysarg_t arg1, char *buf,
-    size_t buf_size)
+static int devman_get_str_internal(sysarg_t method, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t *r1, char *buf, size_t buf_size)
 {
 	async_exch_t *exch;
@@ -412,5 +412,5 @@
 	
 	ipc_call_t answer;
-	aid_t req = async_send_1(exch, method, arg1, &answer);
+	aid_t req = async_send_2(exch, method, arg1, arg2, &answer);
 	aid_t dreq = async_data_read(exch, buf, buf_size - 1, &dreply);
 	async_wait_for(dreq, &dretval);
@@ -430,4 +430,6 @@
 	}
 	
+	if (r1 != NULL)
+		*r1 = IPC_GET_ARG1(answer);
 	act_size = IPC_GET_ARG2(dreply);
 	assert(act_size <= buf_size - 1);
@@ -439,18 +441,33 @@
 int devman_fun_get_path(devman_handle_t handle, char *buf, size_t buf_size)
 {
-	return devman_get_str_internal(DEVMAN_FUN_GET_PATH, handle, buf,
-	    buf_size);
+	return devman_get_str_internal(DEVMAN_FUN_GET_PATH, handle, 0, NULL,
+	    buf, buf_size);
+}
+
+int devman_fun_get_match_id(devman_handle_t handle, size_t index, char *buf,
+    size_t buf_size, unsigned int *rscore)
+{
+	int rc;
+	sysarg_t score = 0;
+
+	rc = devman_get_str_internal(DEVMAN_FUN_GET_MATCH_ID, handle, index,
+	    &score, buf, buf_size);
+	if (rc != EOK)
+		return rc;
+
+	*rscore = score;
+	return rc;
 }
 
 int devman_fun_get_name(devman_handle_t handle, char *buf, size_t buf_size)
 {
-	return devman_get_str_internal(DEVMAN_FUN_GET_NAME, handle, buf,
-	    buf_size);
+	return devman_get_str_internal(DEVMAN_FUN_GET_NAME, handle, 0, NULL,
+	    buf, buf_size);
 }
 
 int devman_fun_get_driver_name(devman_handle_t handle, char *buf, size_t buf_size)
 {
-	return devman_get_str_internal(DEVMAN_FUN_GET_DRIVER_NAME, handle, buf,
-	    buf_size);
+	return devman_get_str_internal(DEVMAN_FUN_GET_DRIVER_NAME, handle, 0,
+	    NULL, buf, buf_size);
 }
 
@@ -579,4 +596,17 @@
 }
 
+int devman_dev_get_parent(devman_handle_t devh, devman_handle_t *funh)
+{
+	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	sysarg_t retval = async_req_1_1(exch, DEVMAN_DEV_GET_PARENT,
+	    devh, funh);
+	
+	devman_exchange_end(exch);
+	return (int) retval;
+}
+
 int devman_fun_sid_to_handle(service_id_t sid, devman_handle_t *handle)
 {
@@ -592,4 +622,103 @@
 }
 
+int devman_get_drivers(devman_handle_t **drvs,
+    size_t *count)
+{
+	return devman_get_handles_internal(DEVMAN_GET_DRIVERS, 0, drvs, count);
+}
+
+int devman_driver_get_devices(devman_handle_t drvh, devman_handle_t **devs,
+    size_t *count)
+{
+	return devman_get_handles_internal(DEVMAN_DRIVER_GET_DEVICES,
+	    drvh, devs, count);
+}
+
+int devman_driver_get_handle(const char *drvname, devman_handle_t *handle)
+{
+	async_exch_t *exch;
+
+	exch = devman_exchange_begin(DEVMAN_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, DEVMAN_DRIVER_GET_HANDLE, &answer);
+	sysarg_t retval = async_data_write_start(exch, drvname,
+	    str_size(drvname));
+	
+	devman_exchange_end(exch);
+	
+	if (retval != EOK) {
+		async_forget(req);
+		return retval;
+	}
+	
+	async_wait_for(req, &retval);
+	
+	if (retval != EOK) {
+		if (handle != NULL)
+			*handle = (devman_handle_t) -1;
+		
+		return retval;
+	}
+	
+	if (handle != NULL)
+		*handle = (devman_handle_t) IPC_GET_ARG1(answer);
+	
+	return retval;
+}
+
+int devman_driver_get_match_id(devman_handle_t handle, size_t index, char *buf,
+    size_t buf_size, unsigned int *rscore)
+{
+	int rc;
+	sysarg_t score = 0;
+
+	rc = devman_get_str_internal(DEVMAN_DRIVER_GET_MATCH_ID, handle, index,
+	    &score, buf, buf_size);
+	if (rc != EOK)
+		return rc;
+
+	*rscore = score;
+	return rc;
+}
+
+int devman_driver_get_name(devman_handle_t handle, char *buf, size_t buf_size)
+{
+	return devman_get_str_internal(DEVMAN_DRIVER_GET_NAME, handle, 0, NULL,
+	    buf, buf_size);
+}
+
+int devman_driver_get_state(devman_handle_t drvh, driver_state_t *rstate)
+{
+	sysarg_t state;
+	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	int rc = async_req_1_1(exch, DEVMAN_DRIVER_GET_STATE, drvh,
+	    &state);
+	
+	devman_exchange_end(exch);
+	if (rc != EOK)
+		return rc;
+
+	*rstate = state;
+	return rc;
+}
+
+int devman_driver_load(devman_handle_t drvh)
+{
+	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	int rc = async_req_1_0(exch, DEVMAN_DRIVER_LOAD, drvh);
+	
+	devman_exchange_end(exch);
+	return rc;
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/vfs/vfs.c
===================================================================
--- uspace/lib/c/generic/vfs/vfs.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/generic/vfs/vfs.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -43,4 +43,5 @@
 #include <stdio.h>
 #include <sys/stat.h>
+#include <sys/statfs.h>
 #include <sys/types.h>
 #include <ipc/services.h>
@@ -892,4 +893,43 @@
 }
 
+int statfs(const char *path, struct statfs *statfs)
+{
+	sysarg_t rc;
+	sysarg_t rc_orig;
+	aid_t req;
+	size_t pa_size;
+	
+	char *pa = absolutize(path, &pa_size);
+	if (!pa)
+		return ENOMEM;
+	async_exch_t *exch = vfs_exchange_begin();
+	
+	req = async_send_0(exch, VFS_IN_STATFS, NULL);
+	rc = async_data_write_start(exch, pa, pa_size);
+	if (rc != EOK) {
+		vfs_exchange_end(exch);
+		free(pa);
+		async_wait_for(req, &rc_orig);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	rc = async_data_read_start(exch, (void *) statfs, sizeof(struct statfs));
+	if (rc != EOK) {
+		vfs_exchange_end(exch);
+		free(pa);
+		async_wait_for(req, &rc_orig);
+		if (rc_orig == EOK)
+			return (int) rc;
+		else
+			return (int) rc_orig;
+	}
+	vfs_exchange_end(exch);
+	free(pa);
+	async_wait_for(req, &rc);
+	return rc;
+}
+
 /** @}
  */
Index: uspace/lib/c/include/adt/list.h
===================================================================
--- uspace/lib/c/include/adt/list.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/include/adt/list.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -68,5 +68,5 @@
 
 #define list_foreach(list, member, itype, iterator) \
-	for (itype *iterator = NULL; iterator == NULL; iterator =(itype *)1) \
+	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) 1) \
 	    for (link_t *_link = (list).head.next; \
 	    iterator = list_get_instance(_link, itype, member), \
@@ -74,5 +74,5 @@
 
 /** Unlike list_foreach(), allows removing items while traversing a list.
- * 
+ *
  * @code
  * list_t mylist;
@@ -249,4 +249,5 @@
  *
  * @return Next item or NULL if @a link is the last item.
+ *
  */
 static inline link_t *list_next(link_t *link, const list_t *list)
@@ -261,4 +262,5 @@
  *
  * @return Previous item or NULL if @a link is the first item.
+ *
  */
 static inline link_t *list_prev(link_t *link, const list_t *list)
Index: uspace/lib/c/include/device/pio_window.h
===================================================================
--- uspace/lib/c/include/device/pio_window.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/lib/c/include/device/pio_window.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2013 Jakub Jermar 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_DEVICE_PIO_WINDOW_H_
+#define LIBC_DEVICE_PIO_WINDOW_H_
+
+#include <ipc/dev_iface.h>
+#include <async.h>
+
+/** PIO_WINDOW provider interface */
+typedef enum {
+	PIO_WINDOW_GET = 0,
+} pio_window_method_t;
+
+typedef struct {
+	struct {
+		uintptr_t base;
+		size_t size;
+	} mem, io;
+} pio_window_t;
+
+extern int pio_window_get(async_sess_t *, pio_window_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/devman.h
===================================================================
--- uspace/lib/c/include/devman.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/include/devman.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -61,6 +61,9 @@
     unsigned int);
 extern int devman_fun_get_child(devman_handle_t, devman_handle_t *);
+extern int devman_dev_get_parent(devman_handle_t, devman_handle_t *);
 extern int devman_dev_get_functions(devman_handle_t, devman_handle_t **,
     size_t *);
+extern int devman_fun_get_match_id(devman_handle_t, size_t, char *, size_t,
+    unsigned int *);
 extern int devman_fun_get_name(devman_handle_t, char *, size_t);
 extern int devman_fun_get_driver_name(devman_handle_t, char *, size_t);
@@ -71,4 +74,13 @@
 extern int devman_add_device_to_category(devman_handle_t, const char *);
 extern int devman_fun_sid_to_handle(service_id_t, devman_handle_t *);
+extern int devman_get_drivers(devman_handle_t **, size_t *);
+extern int devman_driver_get_devices(devman_handle_t, devman_handle_t **,
+    size_t *);
+extern int devman_driver_get_handle(const char *, devman_handle_t *);
+extern int devman_driver_get_match_id(devman_handle_t, size_t, char *, size_t,
+    unsigned int *);
+extern int devman_driver_get_name(devman_handle_t, char *, size_t);
+extern int devman_driver_get_state(devman_handle_t, driver_state_t *);
+extern int devman_driver_load(devman_handle_t);
 
 #endif
Index: uspace/lib/c/include/ipc/dev_iface.h
===================================================================
--- uspace/lib/c/include/ipc/dev_iface.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/include/ipc/dev_iface.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -36,4 +36,5 @@
 typedef enum {
 	HW_RES_DEV_IFACE = 0,
+	PIO_WINDOW_DEV_IFACE,
 
 	/** Character device interface */
Index: uspace/lib/c/include/ipc/devman.h
===================================================================
--- uspace/lib/c/include/ipc/devman.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/include/ipc/devman.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -42,4 +42,18 @@
 
 typedef sysarg_t devman_handle_t;
+
+typedef enum {
+	/** Driver has not been started. */
+	DRIVER_NOT_STARTED = 0,
+	
+	/**
+	 * Driver has been started, but has not registered as running and ready
+	 * to receive requests.
+	 */
+	DRIVER_STARTING,
+	
+	/** Driver is running and prepared to serve incomming requests. */
+	DRIVER_RUNNING
+} driver_state_t;
 
 typedef enum {
@@ -155,5 +169,7 @@
 	DEVMAN_DEVICE_GET_HANDLE = IPC_FIRST_USER_METHOD,
 	DEVMAN_DEV_GET_FUNCTIONS,
+	DEVMAN_DEV_GET_PARENT,
 	DEVMAN_FUN_GET_CHILD,
+	DEVMAN_FUN_GET_MATCH_ID,
 	DEVMAN_FUN_GET_NAME,
 	DEVMAN_FUN_GET_DRIVER_NAME,
@@ -161,5 +177,12 @@
 	DEVMAN_FUN_OFFLINE,
 	DEVMAN_FUN_GET_PATH,
-	DEVMAN_FUN_SID_TO_HANDLE
+	DEVMAN_FUN_SID_TO_HANDLE,
+	DEVMAN_GET_DRIVERS,
+	DEVMAN_DRIVER_GET_DEVICES,
+	DEVMAN_DRIVER_GET_HANDLE,
+	DEVMAN_DRIVER_GET_MATCH_ID,
+	DEVMAN_DRIVER_GET_NAME,
+	DEVMAN_DRIVER_GET_STATE,
+	DEVMAN_DRIVER_LOAD
 } client_to_devman_t;
 
Index: uspace/lib/c/include/ipc/vfs.h
===================================================================
--- uspace/lib/c/include/ipc/vfs.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/include/ipc/vfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -82,4 +82,5 @@
 	VFS_IN_WAIT_HANDLE,
 	VFS_IN_MTAB_GET,
+	VFS_IN_STATFS
 } vfs_in_request_t;
 
@@ -98,4 +99,5 @@
 	VFS_OUT_LOOKUP,
 	VFS_OUT_DESTROY,
+	VFS_OUT_STATFS,
 	VFS_OUT_LAST
 } vfs_out_request_t;
Index: uspace/lib/c/include/sys/statfs.h
===================================================================
--- uspace/lib/c/include/sys/statfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/lib/c/include/sys/statfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013 Manuele Conti
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_SYS_STATFS_H_
+#define LIBC_SYS_STATFS_H_
+
+#include <sys/types.h>
+
+struct statfs { 
+	uint32_t    f_type;     /* type of file system  */
+	uint32_t    f_bsize;    /* fundamental file system block size */
+	uint64_t    f_blocks;   /* total data blocks in file system */
+	uint64_t    f_bfree;    /* free blocks in fs */
+};
+
+extern int statfs(const char *, struct statfs *);
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/vfs/vfs.h
===================================================================
--- uspace/lib/c/include/vfs/vfs.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/c/include/vfs/vfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -44,7 +44,9 @@
 #include "vfs_mtab.h"
 
+
 enum vfs_change_state_type {
 	VFS_PASS_HANDLE
 };
+
 
 extern char *absolutize(const char *, size_t *);
@@ -61,5 +63,4 @@
 extern async_exch_t *vfs_exchange_begin(void);
 extern void vfs_exchange_end(async_exch_t *);
-
 #endif
 
Index: uspace/lib/drv/Makefile
===================================================================
--- uspace/lib/drv/Makefile	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/drv/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -41,4 +41,5 @@
 	generic/remote_audio_pcm.c \
 	generic/remote_hw_res.c \
+	generic/remote_pio_window.c \
 	generic/remote_char_dev.c \
 	generic/remote_graph_dev.c \
Index: uspace/lib/drv/generic/dev_iface.c
===================================================================
--- uspace/lib/drv/generic/dev_iface.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/drv/generic/dev_iface.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -40,4 +40,5 @@
 #include "dev_iface.h"
 #include "remote_hw_res.h"
+#include "remote_pio_window.h"
 #include "remote_char_dev.h"
 #include "remote_clock_dev.h"
@@ -58,4 +59,5 @@
 		[AUDIO_PCM_BUFFER_IFACE] = &remote_audio_pcm_iface,
 		[HW_RES_DEV_IFACE] = &remote_hw_res_iface,
+		[PIO_WINDOW_DEV_IFACE] = &remote_pio_window_iface,
 		[CHAR_DEV_IFACE] = &remote_char_dev_iface,
 		[GRAPH_DEV_IFACE] = &remote_graph_dev_iface,
Index: uspace/lib/drv/generic/remote_pio_window.c
===================================================================
--- uspace/lib/drv/generic/remote_pio_window.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/lib/drv/generic/remote_pio_window.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libdrv
+ * @{
+ */
+/** @file
+ */
+
+#include <async.h>
+#include <errno.h>
+
+#include "ops/pio_window.h"
+#include "ddf/driver.h"
+
+static void remote_pio_window_get(ddf_fun_t *, void *, ipc_callid_t,
+    ipc_call_t *);
+
+static remote_iface_func_ptr_t remote_pio_window_iface_ops [] = {
+	[PIO_WINDOW_GET] = &remote_pio_window_get
+};
+
+remote_iface_t remote_pio_window_iface = {
+	.method_count = sizeof(remote_pio_window_iface_ops) /
+	    sizeof(remote_iface_func_ptr_t),
+	.methods = remote_pio_window_iface_ops
+};
+
+static void remote_pio_window_get(ddf_fun_t *fun, void *ops,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	pio_window_ops_t *pio_win_ops = (pio_window_ops_t *) ops;
+	size_t len;
+
+	if (!pio_win_ops->get_pio_window) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	
+	pio_window_t *pio_window = pio_win_ops->get_pio_window(fun);
+	if (!pio_window) {
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	async_answer_0(callid, EOK);
+
+	if (!async_data_read_receive(&callid, &len)) {
+		/* Protocol error - the recipient is not accepting data */
+		return;
+	}
+
+	async_data_read_finalize(callid, pio_window, len);
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/drv/include/ops/pio_window.h
===================================================================
--- uspace/lib/drv/include/ops/pio_window.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/lib/drv/include/ops/pio_window.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013 Jakub Jermar 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libdrv
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDRV_OPS_PIO_WINDOW_H_
+#define LIBDRV_OPS_PIO_WINDOW_H_
+
+#include <device/pio_window.h>
+#include <sys/types.h>
+#include "../ddf/driver.h"
+
+typedef struct {
+	pio_window_t *(* get_pio_window)(ddf_fun_t *);
+} pio_window_ops_t;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/drv/include/remote_pio_window.h
===================================================================
--- uspace/lib/drv/include/remote_pio_window.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/lib/drv/include/remote_pio_window.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013 Jakub Jermar 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libdrv
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDRV_REMOTE_PIO_WINDOW_H_
+#define LIBDRV_REMOTE_PIO_WINDOW_H_
+
+extern remote_iface_t remote_pio_window_iface;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/fs/libfs.c
===================================================================
--- uspace/lib/fs/libfs.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/fs/libfs.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -45,4 +45,5 @@
 #include <mem.h>
 #include <sys/stat.h>
+#include <sys/statfs.h>
 #include <stdlib.h>
 
@@ -74,4 +75,5 @@
 static void libfs_open_node(libfs_ops_t *, fs_handle_t, ipc_callid_t,
     ipc_call_t *);
+static void libfs_statfs(libfs_ops_t *, fs_handle_t, ipc_callid_t, ipc_call_t *);
 
 static void vfs_out_mounted(ipc_callid_t rid, ipc_call_t *req)
@@ -219,4 +221,8 @@
 }
 
+static void vfs_out_statfs(ipc_callid_t rid, ipc_call_t *req)
+{
+	libfs_statfs(libfs_ops, reg.fs_handle, rid, req);
+}
 static void vfs_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
@@ -276,4 +282,7 @@
 		case VFS_OUT_SYNC:
 			vfs_out_sync(callid, &call);
+			break;
+		case VFS_OUT_STATFS:
+			vfs_out_statfs(callid, &call);
 			break;
 		default:
@@ -825,8 +834,59 @@
 	
 	ops->node_put(fn);
-	
+
+
 	async_data_read_finalize(callid, &stat, sizeof(struct stat));
 	async_answer_0(rid, EOK);
 }
+
+void libfs_statfs(libfs_ops_t *ops, fs_handle_t fs_handle, ipc_callid_t rid,
+    ipc_call_t *request)
+{
+	service_id_t service_id = (service_id_t) IPC_GET_ARG1(*request);
+	fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
+	
+	fs_node_t *fn;
+	int rc = ops->node_get(&fn, service_id, index);
+	on_error(rc, answer_and_return(rid, rc));
+	
+	ipc_callid_t callid;
+	size_t size;
+	if ((!async_data_read_receive(&callid, &size)) ||
+	    (size != sizeof(struct statfs))) {
+		ops->node_put(fn);
+		async_answer_0(callid, EINVAL);
+		async_answer_0(rid, EINVAL);
+		return;
+	}
+	
+	struct statfs statfs;
+	memset(&statfs, 0, sizeof(struct statfs));
+
+	if (NULL != ops->size_block) {	
+		rc = ops->size_block(service_id, &statfs.f_bsize);
+		if (rc != EOK) goto error;
+	}
+
+	if (NULL != ops->total_block_count) {
+		rc = ops->total_block_count(service_id, &statfs.f_blocks);
+		if (rc != EOK) goto error;
+	}
+
+	if (NULL != ops->free_block_count) {
+		rc = ops->free_block_count(service_id, &statfs.f_bfree);
+		if (rc != EOK) goto error;
+	}
+
+	ops->node_put(fn);
+	async_data_read_finalize(callid, &statfs, sizeof(struct statfs));
+	async_answer_0(rid, EOK);
+	return;
+
+error:
+	ops->node_put(fn);
+	async_answer_0(callid, EINVAL);
+	async_answer_0(rid, EINVAL);
+}
+
 
 /** Open VFS triplet.
Index: uspace/lib/fs/libfs.h
===================================================================
--- uspace/lib/fs/libfs.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/fs/libfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -93,4 +93,7 @@
 	bool (* is_file)(fs_node_t *);
 	service_id_t (* service_get)(fs_node_t *);
+	int (* size_block)(service_id_t, uint32_t *);
+	int (* total_block_count)(service_id_t, uint64_t *);
+	int (* free_block_count)(service_id_t, uint64_t *);
 } libfs_ops_t;
 
Index: uspace/lib/usbhid/src/hidparser.c
===================================================================
--- uspace/lib/usbhid/src/hidparser.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/usbhid/src/hidparser.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -132,5 +132,5 @@
 	usb_hid_report_description_t *report_des;
 	usb_hid_report_type_t type = USB_HID_REPORT_TYPE_INPUT;
-
+	
 	if (report == NULL) {
 		return EINVAL;
@@ -155,10 +155,10 @@
 
 		if (USB_HID_ITEM_FLAG_CONSTANT(item->item_flags) == 0) {
-
+			
 			if (USB_HID_ITEM_FLAG_VARIABLE(item->item_flags) == 0) {
 				/* array */
 				item->value = 
 					usb_hid_translate_data(item, data);
-
+				
 				item->usage = USB_HID_EXTENDED_USAGE(
 				    item->usages[item->value -
@@ -185,5 +185,5 @@
 		}
 	}
-
+	
 	return EOK;
 }
@@ -293,7 +293,8 @@
 
 	list_foreach(report->reports, reports_link,
-	    usb_hid_report_description_t, report_des) {
-		if ((report_des->report_id == report_id) &&
-		    (report_des->type == USB_HID_REPORT_TYPE_OUTPUT)) {
+	    usb_hid_report_description_t, rdes) {
+		if ((rdes->report_id == report_id) &&
+		    (rdes->type == USB_HID_REPORT_TYPE_OUTPUT)) {
+			report_des = rdes;
 			break;
 		}
@@ -340,5 +341,5 @@
 	int length;
 	int32_t tmp_value;
-
+	
 	if (report == NULL) {
 		return EINVAL;
@@ -352,5 +353,5 @@
 	report_des = usb_hid_report_find_description(report, report_id, 
 	    USB_HID_REPORT_TYPE_OUTPUT);
-
+	
 	if (report_des == NULL) {
 		return EINVAL;
@@ -364,5 +365,5 @@
 		offset = report_des->bit_length - report_item->offset - 1;
 		length = report_item->size;
-
+		
 		usb_log_debug("\ttranslated value: %x\n", value);
 
@@ -375,5 +376,5 @@
 			value = value << shift;
 			value = value & (((1 << length) - 1) << shift);
-
+			
 			uint8_t mask = 0;
 			mask = 0xff - (((1 << length) - 1) << shift);
@@ -391,5 +392,5 @@
 
 					tmp_value = tmp_value << (offset % 8);
-
+					
 					mask = ~(((1 << (8 - (offset % 8))) - 1)
 					    << (offset % 8));
@@ -398,5 +399,5 @@
 					    tmp_value;
 				} else if (i == ((offset + length - 1) / 8)) {
-
+					
 					value = value >> (length - 
 					    ((offset + length) % 8));
@@ -404,5 +405,5 @@
 					value = value & ((1 << (length - 
 					    ((offset + length) % 8))) - 1);
-
+					
 					mask = (1 << (length - 
 					    ((offset + length) % 8))) - 1;
@@ -418,5 +419,5 @@
 		report_item->value = 0;
 	}
-
+	
 	return EOK;
 }
Index: uspace/lib/usbhid/src/hidpath.c
===================================================================
--- uspace/lib/usbhid/src/hidpath.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/lib/usbhid/src/hidpath.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -237,14 +237,11 @@
 		    usb_hid_report_usage_path_t, report_item) {
 
-			if(USB_HID_SAME_USAGE_PAGE(report_item->usage_page,
-				path_item->usage_page)){
-
-				if(only_page == 0){
-					if(USB_HID_SAME_USAGE(
-						report_item->usage,
-						path_item->usage)) {
-
+			if (USB_HID_SAME_USAGE_PAGE(report_item->usage_page,
+				path_item->usage_page)) {
+				
+				if (only_page == 0) {
+					if (USB_HID_SAME_USAGE(report_item->usage,
+					    path_item->usage))
 						return EOK;
-					}
 				}
 				else {
@@ -268,19 +265,18 @@
 		report_link = report_path->items.head.next;
 		path_link = path->items.head.next;
-
-		while((report_link != &report_path->items.head) && 
-		      (path_link != &path->items.head)) {
-
+		
+		while ((report_link != &report_path->items.head) &&
+		    (path_link != &path->items.head)) {
+			
 			report_item = list_get_instance(report_link, 
-				usb_hid_report_usage_path_t, rpath_items_link);
-
+			    usb_hid_report_usage_path_t, rpath_items_link);
+			
 			path_item = list_get_instance(path_link,
        				usb_hid_report_usage_path_t, rpath_items_link);
 
-			if(!USB_HID_SAME_USAGE_PAGE(report_item->usage_page, 
-				path_item->usage_page) || ((only_page == 0) && 
+			if (!USB_HID_SAME_USAGE_PAGE(report_item->usage_page, 
+			    path_item->usage_page) || ((only_page == 0) && 
 			    !USB_HID_SAME_USAGE(report_item->usage, 
-				path_item->usage))) {
-
+			    path_item->usage))) {
 				return 1;
 			} else {
@@ -290,14 +286,12 @@
 		}
 
-		if((((flags & USB_HID_PATH_COMPARE_BEGIN) != 0) && 
-			(path_link == &path->items.head)) || 
-		   ((report_link == &report_path->items.head) && 
-			(path_link == &path->items.head))) {
-
+		if ((((flags & USB_HID_PATH_COMPARE_BEGIN) != 0) && 
+		    (path_link == &path->items.head)) || 
+		    ((report_link == &report_path->items.head) && 
+		    (path_link == &path->items.head))) {
 			return EOK;
-		}
-		else {
+		} else {
 			return 1;
-		}						
+		}
 		break;
 
Index: uspace/srv/devman/Makefile
===================================================================
--- uspace/srv/devman/Makefile	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/devman/Makefile	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -33,6 +33,12 @@
 
 SOURCES = \
+	client_conn.c \
+	dev.c \
+	devtree.c \
+	drv_conn.c \
+	driver.c \
+	loc.c \
+	fun.c \
 	main.c \
-	devman.c \
 	match.c \
 	util.c
Index: uspace/srv/devman/client_conn.c
===================================================================
--- uspace/srv/devman/client_conn.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/client_conn.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,803 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @defgroup devman Device manager.
+ * @brief HelenOS device manager.
+ * @{
+ */
+
+/** @file
+ */
+
+#include <inttypes.h>
+#include <assert.h>
+#include <ns.h>
+#include <async.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+#include <stdbool.h>
+#include <fibril_synch.h>
+#include <stdlib.h>
+#include <str.h>
+#include <ctype.h>
+#include <ipc/devman.h>
+
+#include "client_conn.h"
+#include "dev.h"
+#include "devman.h"
+#include "driver.h"
+#include "fun.h"
+#include "loc.h"
+#include "main.h"
+
+/** Find handle for the device instance identified by the device's path in the
+ * device tree. */
+static void devman_function_get_handle(ipc_callid_t iid, ipc_call_t *icall)
+{
+	char *pathname;
+	devman_handle_t handle;
+	
+	int rc = async_data_write_accept((void **) &pathname, true, 0, 0, 0, 0);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fun_node_t *fun = find_fun_node_by_path(&device_tree, pathname);
+	
+	free(pathname);
+
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	handle = fun->handle;
+
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+
+	/* Delete reference created above by find_fun_node_by_path() */
+	fun_del_ref(fun);
+
+	async_answer_1(iid, EOK, handle);
+}
+
+/** Get device match ID. */
+static void devman_fun_get_match_id(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*icall);
+	size_t index = IPC_GET_ARG2(*icall);
+	void *buffer = NULL;
+
+	fun_node_t *fun = find_fun_node(&device_tree, handle);
+	if (fun == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	ipc_callid_t data_callid;
+	size_t data_len;
+	if (!async_data_read_receive(&data_callid, &data_len)) {
+		async_answer_0(iid, EINVAL);
+		fun_del_ref(fun);
+		return;
+	}
+
+	buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		fun_del_ref(fun);
+		return;
+	}
+
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+
+	/* Check function state */
+	if (fun->state == FUN_REMOVED)
+		goto error;
+
+	link_t *link = list_nth(&fun->match_ids.ids, index);
+	if (link == NULL)
+		goto error;
+
+	match_id_t *mid = list_get_instance(link, match_id_t, link);
+
+	size_t sent_length = str_size(mid->id);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, mid->id, sent_length);
+	async_answer_1(iid, EOK, mid->score);
+
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	fun_del_ref(fun);
+	free(buffer);
+
+	return;
+error:
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	free(buffer);
+
+	async_answer_0(data_callid, ENOENT);
+	async_answer_0(iid, ENOENT);
+	fun_del_ref(fun);
+}
+
+/** Get device name. */
+static void devman_fun_get_name(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*icall);
+
+	fun_node_t *fun = find_fun_node(&device_tree, handle);
+	if (fun == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	ipc_callid_t data_callid;
+	size_t data_len;
+	if (!async_data_read_receive(&data_callid, &data_len)) {
+		async_answer_0(iid, EINVAL);
+		fun_del_ref(fun);
+		return;
+	}
+
+	void *buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		fun_del_ref(fun);
+		return;
+	}
+
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		free(buffer);
+
+		async_answer_0(data_callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		fun_del_ref(fun);
+		return;
+	}
+
+	size_t sent_length = str_size(fun->name);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, fun->name, sent_length);
+	async_answer_0(iid, EOK);
+
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	fun_del_ref(fun);
+	free(buffer);
+}
+
+/** Get function driver name. */
+static void devman_fun_get_driver_name(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*icall);
+
+	fun_node_t *fun = find_fun_node(&device_tree, handle);
+	if (fun == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	ipc_callid_t data_callid;
+	size_t data_len;
+	if (!async_data_read_receive(&data_callid, &data_len)) {
+		async_answer_0(iid, EINVAL);
+		fun_del_ref(fun);
+		return;
+	}
+
+	void *buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		fun_del_ref(fun);
+		return;
+	}
+
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		free(buffer);
+
+		async_answer_0(data_callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		fun_del_ref(fun);
+		return;
+	}
+
+	/* Check whether function has a driver */
+	if (fun->child == NULL || fun->child->drv == NULL) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		free(buffer);
+
+		async_answer_0(data_callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		fun_del_ref(fun);
+		return;
+	}
+
+	size_t sent_length = str_size(fun->child->drv->name);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, fun->child->drv->name,
+	    sent_length);
+	async_answer_0(iid, EOK);
+
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	fun_del_ref(fun);
+	free(buffer);
+}
+
+/** Get device path. */
+static void devman_fun_get_path(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*icall);
+
+	fun_node_t *fun = find_fun_node(&device_tree, handle);
+	if (fun == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	ipc_callid_t data_callid;
+	size_t data_len;
+	if (!async_data_read_receive(&data_callid, &data_len)) {
+		async_answer_0(iid, EINVAL);
+		fun_del_ref(fun);
+		return;
+	}
+
+	void *buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		fun_del_ref(fun);
+		return;
+	}
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		free(buffer);
+
+		async_answer_0(data_callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		fun_del_ref(fun);
+		return;
+	}
+	
+	size_t sent_length = str_size(fun->pathname);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, fun->pathname, sent_length);
+	async_answer_0(iid, EOK);
+
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	fun_del_ref(fun);
+	free(buffer);
+}
+
+/** Get handle for parent function of a device. */
+static void devman_dev_get_parent(ipc_callid_t iid, ipc_call_t *icall)
+{
+	dev_node_t *dev;
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	
+	dev = find_dev_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
+	if (dev == NULL || dev->state == DEVICE_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	if (dev->pfun == NULL) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, dev->pfun->handle);
+	
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+}
+
+static void devman_dev_get_functions(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	
+	dev_node_t *dev = find_dev_node_no_lock(&device_tree,
+	    IPC_GET_ARG1(*icall));
+	if (dev == NULL || dev->state == DEVICE_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
+	if (hdl_buf == NULL) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	rc = dev_get_functions(&device_tree, dev, hdl_buf, size, &act_size);
+	if (rc != EOK) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	
+	sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
+	free(hdl_buf);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+/** Get handle for child device of a function. */
+static void devman_fun_get_child(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fun_node_t *fun;
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	
+	fun = find_fun_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
+	if (fun == NULL || fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	if (fun->child == NULL) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, fun->child->handle);
+	
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+}
+
+/** Online function.
+ *
+ * Send a request to online a function to the responsible driver.
+ * The driver may offline other functions if necessary (i.e. if the state
+ * of this function is linked to state of another function somehow).
+ */
+static void devman_fun_online(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fun_node_t *fun;
+	int rc;
+
+	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	rc = driver_fun_online(&device_tree, fun);
+	fun_del_ref(fun);
+	
+	async_answer_0(iid, (sysarg_t) rc);
+}
+
+/** Offline function.
+ *
+ * Send a request to offline a function to the responsible driver. As
+ * a result the subtree rooted at that function should be cleanly
+ * detatched. The driver may offline other functions if necessary
+ * (i.e. if the state of this function is linked to state of another
+ * function somehow).
+ */
+static void devman_fun_offline(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fun_node_t *fun;
+	int rc;
+
+	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	rc = driver_fun_offline(&device_tree, fun);
+	fun_del_ref(fun);
+	
+	async_answer_0(iid, (sysarg_t) rc);
+}
+
+/** Find handle for the function instance identified by its service ID. */
+static void devman_fun_sid_to_handle(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fun_node_t *fun;
+
+	fun = find_loc_tree_function(&device_tree, IPC_GET_ARG1(*icall));
+	
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+
+	async_answer_1(iid, EOK, fun->handle);
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	fun_del_ref(fun);
+}
+
+/** Get list of all registered drivers. */
+static void devman_get_drivers(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
+	if (hdl_buf == NULL) {
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	rc = driver_get_list(&drivers_list, hdl_buf, size, &act_size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
+	free(hdl_buf);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+static void devman_driver_get_devices(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	driver_t *drv = driver_find(&drivers_list, IPC_GET_ARG1(*icall));
+	if (drv == NULL) {
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
+	if (hdl_buf == NULL) {
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	rc = driver_get_devices(drv, hdl_buf, size, &act_size);
+	if (rc != EOK) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
+	free(hdl_buf);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+
+/** Find driver by name. */
+static void devman_driver_get_handle(ipc_callid_t iid, ipc_call_t *icall)
+{
+	char *drvname;
+	
+	int rc = async_data_write_accept((void **) &drvname, true, 0, 0, 0, 0);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	driver_t *driver = driver_find_by_name(&drivers_list, drvname);
+	
+	free(drvname);
+	
+	if (driver == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, driver->handle);
+}
+
+/** Get driver match ID. */
+static void devman_driver_get_match_id(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*icall);
+	size_t index = IPC_GET_ARG2(*icall);
+
+	driver_t *drv = driver_find(&drivers_list, handle);
+	if (drv == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	ipc_callid_t data_callid;
+	size_t data_len;
+	if (!async_data_read_receive(&data_callid, &data_len)) {
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	void *buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	fibril_mutex_lock(&drv->driver_mutex);
+	link_t *link = list_nth(&drv->match_ids.ids, index);
+	if (link == NULL) {
+		fibril_mutex_unlock(&drv->driver_mutex);
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	match_id_t *mid = list_get_instance(link, match_id_t, link);
+
+	size_t sent_length = str_size(mid->id);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, mid->id, sent_length);
+	async_answer_1(iid, EOK, mid->score);
+
+	fibril_mutex_unlock(&drv->driver_mutex);
+
+	free(buffer);
+}
+
+/** Get driver name. */
+static void devman_driver_get_name(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*icall);
+
+	driver_t *drv = driver_find(&drivers_list, handle);
+	if (drv == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	ipc_callid_t data_callid;
+	size_t data_len;
+	if (!async_data_read_receive(&data_callid, &data_len)) {
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	void *buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	fibril_mutex_lock(&drv->driver_mutex);
+
+	size_t sent_length = str_size(drv->name);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, drv->name, sent_length);
+	async_answer_0(iid, EOK);
+
+	fibril_mutex_unlock(&drv->driver_mutex);
+
+	free(buffer);
+}
+
+/** Get driver state. */
+static void devman_driver_get_state(ipc_callid_t iid, ipc_call_t *icall)
+{
+	driver_t *drv;
+	
+	drv = driver_find(&drivers_list, IPC_GET_ARG1(*icall));
+	if (drv == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, (sysarg_t) drv->state);
+}
+
+/** Forcibly load a driver. */
+static void devman_driver_load(ipc_callid_t iid, ipc_call_t *icall)
+{
+	driver_t *drv;
+	int rc;
+	
+	drv = driver_find(&drivers_list, IPC_GET_ARG1(*icall));
+	if (drv == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fibril_mutex_lock(&drv->driver_mutex);
+	rc = start_driver(drv) ? EOK : EIO;
+	fibril_mutex_unlock(&drv->driver_mutex);
+
+	async_answer_0(iid, rc);
+}
+
+/** Function for handling connections from a client to the device manager. */
+void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
+{
+	/* Accept connection. */
+	async_answer_0(iid, EOK);
+	
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call))
+			break;
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case DEVMAN_DEVICE_GET_HANDLE:
+			devman_function_get_handle(callid, &call);
+			break;
+		case DEVMAN_DEV_GET_PARENT:
+			devman_dev_get_parent(callid, &call);
+			break;
+		case DEVMAN_DEV_GET_FUNCTIONS:
+			devman_dev_get_functions(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_CHILD:
+			devman_fun_get_child(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_MATCH_ID:
+			devman_fun_get_match_id(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_NAME:
+			devman_fun_get_name(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_DRIVER_NAME:
+			devman_fun_get_driver_name(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_PATH:
+			devman_fun_get_path(callid, &call);
+			break;
+		case DEVMAN_FUN_ONLINE:
+			devman_fun_online(callid, &call);
+			break;
+		case DEVMAN_FUN_OFFLINE:
+			devman_fun_offline(callid, &call);
+			break;
+		case DEVMAN_FUN_SID_TO_HANDLE:
+			devman_fun_sid_to_handle(callid, &call);
+			break;
+		case DEVMAN_GET_DRIVERS:
+			devman_get_drivers(callid, &call);
+			break;
+		case DEVMAN_DRIVER_GET_DEVICES:
+			devman_driver_get_devices(callid, &call);
+			break;
+		case DEVMAN_DRIVER_GET_HANDLE:
+			devman_driver_get_handle(callid, &call);
+			break;
+		case DEVMAN_DRIVER_GET_MATCH_ID:
+			devman_driver_get_match_id(callid, &call);
+			break;
+		case DEVMAN_DRIVER_GET_NAME:
+			devman_driver_get_name(callid, &call);
+			break;
+		case DEVMAN_DRIVER_GET_STATE:
+			devman_driver_get_state(callid, &call);
+			break;
+		case DEVMAN_DRIVER_LOAD:
+			devman_driver_load(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOENT);
+		}
+	}
+}
+
+
+/** @}
+ */
Index: uspace/srv/devman/client_conn.h
===================================================================
--- uspace/srv/devman/client_conn.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/client_conn.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#ifndef CLIENT_CONN_H_
+#define CLIENT_CONN_H_
+
+#include "devman.h"
+
+extern void devman_connection_client(ipc_callid_t, ipc_call_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/dev.c
===================================================================
--- uspace/srv/devman/dev.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/dev.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#include <errno.h>
+
+#include "dev.h"
+#include "devman.h"
+
+/** Create a new device node.
+ *
+ * @return		A device node structure.
+ */
+dev_node_t *create_dev_node(void)
+{
+	dev_node_t *dev;
+	
+	dev = calloc(1, sizeof(dev_node_t));
+	if (dev == NULL)
+		return NULL;
+	
+	atomic_set(&dev->refcnt, 0);
+	list_initialize(&dev->functions);
+	link_initialize(&dev->driver_devices);
+	
+	return dev;
+}
+
+/** Delete a device node.
+ *
+ * @param node		The device node structure.
+ */
+void delete_dev_node(dev_node_t *dev)
+{
+	assert(list_empty(&dev->functions));
+	assert(dev->pfun == NULL);
+	assert(dev->drv == NULL);
+	
+	free(dev);
+}
+
+/** Increase device node reference count.
+ *
+ * @param dev	Device node
+ */
+void dev_add_ref(dev_node_t *dev)
+{
+	atomic_inc(&dev->refcnt);
+}
+
+/** Decrease device node reference count.
+ *
+ * When the count drops to zero the device node is freed.
+ *
+ * @param dev	Device node
+ */
+void dev_del_ref(dev_node_t *dev)
+{
+	if (atomic_predec(&dev->refcnt) == 0)
+		delete_dev_node(dev);
+}
+
+/** Find the device node structure of the device witch has the specified handle.
+ *
+ * @param tree		The device tree where we look for the device node.
+ * @param handle	The handle of the device.
+ * @return		The device node.
+ */
+dev_node_t *find_dev_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
+{
+	assert(fibril_rwlock_is_locked(&tree->rwlock));
+	
+	ht_link_t *link = hash_table_find(&tree->devman_devices, &handle);
+	if (link == NULL)
+		return NULL;
+	
+	return hash_table_get_inst(link, dev_node_t, devman_dev);
+}
+
+/** Find the device node structure of the device witch has the specified handle.
+ *
+ * @param tree		The device tree where we look for the device node.
+ * @param handle	The handle of the device.
+ * @return		The device node.
+ */
+dev_node_t *find_dev_node(dev_tree_t *tree, devman_handle_t handle)
+{
+	dev_node_t *dev = NULL;
+	
+	fibril_rwlock_read_lock(&tree->rwlock);
+	dev = find_dev_node_no_lock(tree, handle);
+	if (dev != NULL)
+		dev_add_ref(dev);
+	
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	return dev;
+}
+
+/** Get list of device functions. */
+int dev_get_functions(dev_tree_t *tree, dev_node_t *dev,
+    devman_handle_t *hdl_buf, size_t buf_size, size_t *act_size)
+{
+	size_t act_cnt;
+	size_t buf_cnt;
+
+	assert(fibril_rwlock_is_locked(&tree->rwlock));
+
+	buf_cnt = buf_size / sizeof(devman_handle_t);
+
+	act_cnt = list_count(&dev->functions);
+	*act_size = act_cnt * sizeof(devman_handle_t);
+
+	if (buf_size % sizeof(devman_handle_t) != 0)
+		return EINVAL;
+
+	size_t pos = 0;
+	list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
+		if (pos < buf_cnt) {
+			hdl_buf[pos] = fun->handle;
+		}
+
+		pos++;
+	}
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/devman/dev.h
===================================================================
--- uspace/srv/devman/dev.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/dev.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#ifndef DEV_H_
+#define DEV_H_
+
+#include "devman.h"
+
+extern dev_node_t *create_dev_node(void);
+extern void delete_dev_node(dev_node_t *node);
+extern void dev_add_ref(dev_node_t *);
+extern void dev_del_ref(dev_node_t *);
+
+extern dev_node_t *find_dev_node_no_lock(dev_tree_t *tree,
+    devman_handle_t handle);
+extern dev_node_t *find_dev_node(dev_tree_t *tree, devman_handle_t handle);
+extern int dev_get_functions(dev_tree_t *tree, dev_node_t *, devman_handle_t *,
+    size_t, size_t *);
+
+#endif
+
+/** @}
+ */
Index: pace/srv/devman/devman.c
===================================================================
--- uspace/srv/devman/devman.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ 	(revision )
@@ -1,1518 +1,0 @@
-/*
- * Copyright (c) 2010 Lenka Trochtova
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup devman
- * @{
- */
-/** @file Device Manager
- *
- * Locking order:
- *   (1) driver_t.driver_mutex
- *   (2) dev_tree_t.rwlock
- *
- * Synchronization:
- *    - device_tree.rwlock protects:
- *        - tree root, complete tree topology
- *        - complete contents of device and function nodes
- *    - dev_node_t.refcnt, fun_node_t.refcnt prevent nodes from
- *      being deallocated
- *    - find_xxx() functions increase reference count of returned object
- *    - find_xxx_no_lock() do not increase reference count
- *
- * TODO
- *    - Track all steady and transient device/function states
- *    - Check states, wait for steady state on certain operations
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <io/log.h>
-#include <ipc/driver.h>
-#include <ipc/devman.h>
-#include <loc.h>
-#include <str_error.h>
-#include <stdio.h>
-
-#include "devman.h"
-
-static fun_node_t *find_node_child(dev_tree_t *, fun_node_t *, const char *);
-
-/* hash table operations */
-
-static inline size_t handle_key_hash(void *key)
-{
-	devman_handle_t handle = *(devman_handle_t*)key;
-	return handle;
-}
-
-static size_t devman_devices_hash(const ht_link_t *item)
-{
-	dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
-	return handle_key_hash(&dev->handle);
-}
-
-static size_t devman_functions_hash(const ht_link_t *item)
-{
-	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
-	return handle_key_hash(&fun->handle);
-}
-
-static bool devman_devices_key_equal(void *key, const ht_link_t *item)
-{
-	devman_handle_t handle = *(devman_handle_t*)key;
-	dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
-	return dev->handle == handle;
-}
-
-static bool devman_functions_key_equal(void *key, const ht_link_t *item)
-{
-	devman_handle_t handle = *(devman_handle_t*)key;
-	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
-	return fun->handle == handle;
-}
-
-static inline size_t service_id_key_hash(void *key)
-{
-	service_id_t service_id = *(service_id_t*)key;
-	return service_id;
-}
-
-static size_t loc_functions_hash(const ht_link_t *item)
-{
-	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
-	return service_id_key_hash(&fun->service_id);
-}
-
-static bool loc_functions_key_equal(void *key, const ht_link_t *item)
-{
-	service_id_t service_id = *(service_id_t*)key;
-	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
-	return fun->service_id == service_id;
-}
-
-
-static hash_table_ops_t devman_devices_ops = {
-	.hash = devman_devices_hash,
-	.key_hash = handle_key_hash,
-	.key_equal = devman_devices_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-static hash_table_ops_t devman_functions_ops = {
-	.hash = devman_functions_hash,
-	.key_hash = handle_key_hash,
-	.key_equal = devman_functions_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-static hash_table_ops_t loc_devices_ops = {
-	.hash = loc_functions_hash,
-	.key_hash = service_id_key_hash,
-	.key_equal = loc_functions_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-/**
- * Initialize the list of device driver's.
- *
- * @param drv_list the list of device driver's.
- *
- */
-void init_driver_list(driver_list_t *drv_list)
-{
-	assert(drv_list != NULL);
-	
-	list_initialize(&drv_list->drivers);
-	fibril_mutex_initialize(&drv_list->drivers_mutex);
-}
-
-/** Allocate and initialize a new driver structure.
- *
- * @return	Driver structure.
- */
-driver_t *create_driver(void)
-{
-	driver_t *res = malloc(sizeof(driver_t));
-	if (res != NULL)
-		init_driver(res);
-	return res;
-}
-
-/** Add a driver to the list of drivers.
- *
- * @param drivers_list	List of drivers.
- * @param drv		Driver structure.
- */
-void add_driver(driver_list_t *drivers_list, driver_t *drv)
-{
-	fibril_mutex_lock(&drivers_list->drivers_mutex);
-	list_prepend(&drv->drivers, &drivers_list->drivers);
-	fibril_mutex_unlock(&drivers_list->drivers_mutex);
-
-	log_msg(LOG_DEFAULT, LVL_NOTE, "Driver `%s' was added to the list of available "
-	    "drivers.", drv->name);
-}
-
-/** Read match id at the specified position of a string and set the position in
- * the string to the first character following the id.
- *
- * @param buf		The position in the input string.
- * @return		The match id.
- */
-char *read_match_id(char **buf)
-{
-	char *res = NULL;
-	size_t len = get_nonspace_len(*buf);
-	
-	if (len > 0) {
-		res = malloc(len + 1);
-		if (res != NULL) {
-			str_ncpy(res, len + 1, *buf, len);
-			*buf += len;
-		}
-	}
-	
-	return res;
-}
-
-/**
- * Read match ids and associated match scores from a string.
- *
- * Each match score in the string is followed by its match id.
- * The match ids and match scores are separated by whitespaces.
- * Neither match ids nor match scores can contain whitespaces.
- *
- * @param buf		The string from which the match ids are read.
- * @param ids		The list of match ids into which the match ids and
- *			scores are added.
- * @return		True if at least one match id and associated match score
- *			was successfully read, false otherwise.
- */
-bool parse_match_ids(char *buf, match_id_list_t *ids)
-{
-	int score = 0;
-	char *id = NULL;
-	int ids_read = 0;
-	
-	while (true) {
-		/* skip spaces */
-		if (!skip_spaces(&buf))
-			break;
-		
-		/* read score */
-		score = strtoul(buf, &buf, 10);
-		
-		/* skip spaces */
-		if (!skip_spaces(&buf))
-			break;
-		
-		/* read id */
-		id = read_match_id(&buf);
-		if (NULL == id)
-			break;
-		
-		/* create new match_id structure */
-		match_id_t *mid = create_match_id();
-		mid->id = id;
-		mid->score = score;
-		
-		/* add it to the list */
-		add_match_id(ids, mid);
-		
-		ids_read++;
-	}
-	
-	return ids_read > 0;
-}
-
-/**
- * Read match ids and associated match scores from a file.
- *
- * Each match score in the file is followed by its match id.
- * The match ids and match scores are separated by whitespaces.
- * Neither match ids nor match scores can contain whitespaces.
- *
- * @param buf		The path to the file from which the match ids are read.
- * @param ids		The list of match ids into which the match ids and
- *			scores are added.
- * @return		True if at least one match id and associated match score
- *			was successfully read, false otherwise.
- */
-bool read_match_ids(const char *conf_path, match_id_list_t *ids)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "read_match_ids(conf_path=\"%s\")", conf_path);
-	
-	bool suc = false;
-	char *buf = NULL;
-	bool opened = false;
-	int fd;
-	size_t len = 0;
-	
-	fd = open(conf_path, O_RDONLY);
-	if (fd < 0) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to open `%s' for reading: %s.",
-		    conf_path, str_error(fd));
-		goto cleanup;
-	}
-	opened = true;
-	
-	len = lseek(fd, 0, SEEK_END);
-	lseek(fd, 0, SEEK_SET);
-	if (len == 0) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Configuration file '%s' is empty.",
-		    conf_path);
-		goto cleanup;
-	}
-	
-	buf = malloc(len + 1);
-	if (buf == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Memory allocation failed when parsing file "
-		    "'%s'.", conf_path);
-		goto cleanup;
-	}
-	
-	ssize_t read_bytes = read_all(fd, buf, len);
-	if (read_bytes <= 0) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to read file '%s' (%zd).", conf_path,
-		    read_bytes);
-		goto cleanup;
-	}
-	buf[read_bytes] = 0;
-	
-	suc = parse_match_ids(buf, ids);
-	
-cleanup:
-	free(buf);
-	
-	if (opened)
-		close(fd);
-	
-	return suc;
-}
-
-/**
- * Get information about a driver.
- *
- * Each driver has its own directory in the base directory.
- * The name of the driver's directory is the same as the name of the driver.
- * The driver's directory contains driver's binary (named as the driver without
- * extension) and the configuration file with match ids for device-to-driver
- *  matching (named as the driver with a special extension).
- *
- * This function searches for the driver's directory and containing
- * configuration files. If all the files needed are found, they are parsed and
- * the information about the driver is stored in the driver's structure.
- *
- * @param base_path	The base directory, in which we look for driver's
- *			subdirectory.
- * @param name		The name of the driver.
- * @param drv		The driver structure to fill information in.
- *
- * @return		True on success, false otherwise.
- */
-bool get_driver_info(const char *base_path, const char *name, driver_t *drv)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "get_driver_info(base_path=\"%s\", name=\"%s\")",
-	    base_path, name);
-	
-	assert(base_path != NULL && name != NULL && drv != NULL);
-	
-	bool suc = false;
-	char *match_path = NULL;
-	size_t name_size = 0;
-	
-	/* Read the list of match ids from the driver's configuration file. */
-	match_path = get_abs_path(base_path, name, MATCH_EXT);
-	if (match_path == NULL)
-		goto cleanup;
-	
-	if (!read_match_ids(match_path, &drv->match_ids))
-		goto cleanup;
-	
-	/* Allocate and fill driver's name. */
-	name_size = str_size(name) + 1;
-	drv->name = malloc(name_size);
-	if (drv->name == NULL)
-		goto cleanup;
-	str_cpy(drv->name, name_size, name);
-	
-	/* Initialize path with driver's binary. */
-	drv->binary_path = get_abs_path(base_path, name, "");
-	if (drv->binary_path == NULL)
-		goto cleanup;
-	
-	/* Check whether the driver's binary exists. */
-	struct stat s;
-	if (stat(drv->binary_path, &s) == ENOENT) { /* FIXME!! */
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Driver not found at path `%s'.",
-		    drv->binary_path);
-		goto cleanup;
-	}
-	
-	suc = true;
-	
-cleanup:
-	if (!suc) {
-		free(drv->binary_path);
-		free(drv->name);
-		/* Set the driver structure to the default state. */
-		init_driver(drv);
-	}
-	
-	free(match_path);
-	
-	return suc;
-}
-
-/** Lookup drivers in the directory.
- *
- * @param drivers_list	The list of available drivers.
- * @param dir_path	The path to the directory where we search for drivers.
- * @return		Number of drivers which were found.
- */
-int lookup_available_drivers(driver_list_t *drivers_list, const char *dir_path)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "lookup_available_drivers(dir=\"%s\")", dir_path);
-	
-	int drv_cnt = 0;
-	DIR *dir = NULL;
-	struct dirent *diren;
-
-	dir = opendir(dir_path);
-	
-	if (dir != NULL) {
-		driver_t *drv = create_driver();
-		while ((diren = readdir(dir))) {
-			if (get_driver_info(dir_path, diren->d_name, drv)) {
-				add_driver(drivers_list, drv);
-				drv_cnt++;
-				drv = create_driver();
-			}
-		}
-		delete_driver(drv);
-		closedir(dir);
-	}
-	
-	return drv_cnt;
-}
-
-/** Create root device and function node in the device tree.
- *
- * @param tree	The device tree.
- * @return	True on success, false otherwise.
- */
-bool create_root_nodes(dev_tree_t *tree)
-{
-	fun_node_t *fun;
-	dev_node_t *dev;
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "create_root_nodes()");
-	
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	/*
-	 * Create root function. This is a pseudo function to which
-	 * the root device node is attached. It allows us to match
-	 * the root device driver in a standard manner, i.e. against
-	 * the parent function.
-	 */
-	
-	fun = create_fun_node();
-	if (fun == NULL) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		return false;
-	}
-	
-	fun_add_ref(fun);
-	insert_fun_node(tree, fun, str_dup(""), NULL);
-	
-	match_id_t *id = create_match_id();
-	id->id = str_dup("root");
-	id->score = 100;
-	add_match_id(&fun->match_ids, id);
-	tree->root_node = fun;
-	
-	/*
-	 * Create root device node.
-	 */
-	dev = create_dev_node();
-	if (dev == NULL) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		return false;
-	}
-	
-	dev_add_ref(dev);
-	insert_dev_node(tree, dev, fun);
-	
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	
-	return dev != NULL;
-}
-
-/** Lookup the best matching driver for the specified device in the list of
- * drivers.
- *
- * A match between a device and a driver is found if one of the driver's match
- * ids match one of the device's match ids. The score of the match is the
- * product of the driver's and device's score associated with the matching id.
- * The best matching driver for a device is the driver with the highest score
- * of the match between the device and the driver.
- *
- * @param drivers_list	The list of drivers, where we look for the driver
- *			suitable for handling the device.
- * @param node		The device node structure of the device.
- * @return		The best matching driver or NULL if no matching driver
- *			is found.
- */
-driver_t *find_best_match_driver(driver_list_t *drivers_list, dev_node_t *node)
-{
-	driver_t *best_drv = NULL;
-	int best_score = 0, score = 0;
-	
-	fibril_mutex_lock(&drivers_list->drivers_mutex);
-	
-	list_foreach(drivers_list->drivers, drivers, driver_t, drv) {
-		score = get_match_score(drv, node);
-		if (score > best_score) {
-			best_score = score;
-			best_drv = drv;
-		}
-	}
-	
-	fibril_mutex_unlock(&drivers_list->drivers_mutex);
-	
-	return best_drv;
-}
-
-/** Assign a driver to a device.
- *
- * @param tree		Device tree
- * @param node		The device's node in the device tree.
- * @param drv		The driver.
- */
-void attach_driver(dev_tree_t *tree, dev_node_t *dev, driver_t *drv)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "attach_driver(dev=\"%s\",drv=\"%s\")",
-	    dev->pfun->pathname, drv->name);
-	
-	fibril_mutex_lock(&drv->driver_mutex);
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	dev->drv = drv;
-	list_append(&dev->driver_devices, &drv->devices);
-	
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	fibril_mutex_unlock(&drv->driver_mutex);
-}
-
-/** Detach driver from device.
- *
- * @param tree		Device tree
- * @param node		The device's node in the device tree.
- * @param drv		The driver.
- */
-void detach_driver(dev_tree_t *tree, dev_node_t *dev)
-{
-	driver_t *drv = dev->drv;
-	
-	assert(drv != NULL);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "detach_driver(dev=\"%s\",drv=\"%s\")",
-	    dev->pfun->pathname, drv->name);
-	
-	fibril_mutex_lock(&drv->driver_mutex);
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	dev->drv = NULL;
-	list_remove(&dev->driver_devices);
-	
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	fibril_mutex_unlock(&drv->driver_mutex);
-}
-
-/** Start a driver
- *
- * @param drv		The driver's structure.
- * @return		True if the driver's task is successfully spawned, false
- *			otherwise.
- */
-bool start_driver(driver_t *drv)
-{
-	int rc;
-
-	assert(fibril_mutex_is_locked(&drv->driver_mutex));
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "start_driver(drv=\"%s\")", drv->name);
-	
-	rc = task_spawnl(NULL, drv->binary_path, drv->binary_path, NULL);
-	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Spawning driver `%s' (%s) failed: %s.",
-		    drv->name, drv->binary_path, str_error(rc));
-		return false;
-	}
-	
-	drv->state = DRIVER_STARTING;
-	return true;
-}
-
-/** Find device driver in the list of device drivers.
- *
- * @param drv_list	The list of device drivers.
- * @param drv_name	The name of the device driver which is searched.
- * @return		The device driver of the specified name, if it is in the
- *			list, NULL otherwise.
- */
-driver_t *find_driver(driver_list_t *drv_list, const char *drv_name)
-{
-	driver_t *res = NULL;
-	
-	fibril_mutex_lock(&drv_list->drivers_mutex);
-	
-	list_foreach(drv_list->drivers, drivers, driver_t, drv) {
-		if (str_cmp(drv->name, drv_name) == 0) {
-			res = drv;
-			break;
-		}
-	}
-	
-	fibril_mutex_unlock(&drv_list->drivers_mutex);
-	
-	return res;
-}
-
-/** Notify driver about the devices to which it was assigned.
- *
- * @param driver	The driver to which the devices are passed.
- */
-static void pass_devices_to_driver(driver_t *driver, dev_tree_t *tree)
-{
-	dev_node_t *dev;
-	link_t *link;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver(driver=\"%s\")",
-	    driver->name);
-
-	fibril_mutex_lock(&driver->driver_mutex);
-
-	/*
-	 * Go through devices list as long as there is some device
-	 * that has not been passed to the driver.
-	 */
-	link = driver->devices.head.next;
-	while (link != &driver->devices.head) {
-		dev = list_get_instance(link, dev_node_t, driver_devices);
-		fibril_rwlock_write_lock(&tree->rwlock);
-		
-		if (dev->passed_to_driver) {
-			fibril_rwlock_write_unlock(&tree->rwlock);
-			link = link->next;
-			continue;
-		}
-
-		log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver: dev->refcnt=%d\n",
-		    (int)atomic_get(&dev->refcnt));
-		dev_add_ref(dev);
-
-		/*
-		 * Unlock to avoid deadlock when adding device
-		 * handled by itself.
-		 */
-		fibril_mutex_unlock(&driver->driver_mutex);
-		fibril_rwlock_write_unlock(&tree->rwlock);
-
-		add_device(driver, dev, tree);
-
-		dev_del_ref(dev);
-
-		/*
-		 * Lock again as we will work with driver's
-		 * structure.
-		 */
-		fibril_mutex_lock(&driver->driver_mutex);
-
-		/*
-		 * Restart the cycle to go through all devices again.
-		 */
-		link = driver->devices.head.next;
-	}
-
-	/*
-	 * Once we passed all devices to the driver, we need to mark the
-	 * driver as running.
-	 * It is vital to do it here and inside critical section.
-	 *
-	 * If we would change the state earlier, other devices added to
-	 * the driver would be added to the device list and started
-	 * immediately and possibly started here as well.
-	 */
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' enters running state.", driver->name);
-	driver->state = DRIVER_RUNNING;
-
-	fibril_mutex_unlock(&driver->driver_mutex);
-}
-
-/** Finish the initialization of a driver after it has succesfully started
- * and after it has registered itself by the device manager.
- *
- * Pass devices formerly matched to the driver to the driver and remember the
- * driver is running and fully functional now.
- *
- * @param driver	The driver which registered itself as running by the
- *			device manager.
- */
-void initialize_running_driver(driver_t *driver, dev_tree_t *tree)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "initialize_running_driver(driver=\"%s\")",
-	    driver->name);
-	
-	/*
-	 * Pass devices which have been already assigned to the driver to the
-	 * driver.
-	 */
-	pass_devices_to_driver(driver, tree);
-}
-
-/** Initialize device driver structure.
- *
- * @param drv		The device driver structure.
- */
-void init_driver(driver_t *drv)
-{
-	assert(drv != NULL);
-
-	memset(drv, 0, sizeof(driver_t));
-	list_initialize(&drv->match_ids.ids);
-	list_initialize(&drv->devices);
-	fibril_mutex_initialize(&drv->driver_mutex);
-	drv->sess = NULL;
-}
-
-/** Device driver structure clean-up.
- *
- * @param drv		The device driver structure.
- */
-void clean_driver(driver_t *drv)
-{
-	assert(drv != NULL);
-
-	free(drv->name);
-	free(drv->binary_path);
-
-	clean_match_ids(&drv->match_ids);
-
-	init_driver(drv);
-}
-
-/** Delete device driver structure.
- *
- * @param drv		The device driver structure.
- */
-void delete_driver(driver_t *drv)
-{
-	assert(drv != NULL);
-	
-	clean_driver(drv);
-	free(drv);
-}
-
-/** Create loc path and name for the function. */
-void loc_register_tree_function(fun_node_t *fun, dev_tree_t *tree)
-{
-	char *loc_pathname = NULL;
-	char *loc_name = NULL;
-	
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-	
-	asprintf(&loc_name, "%s", fun->pathname);
-	if (loc_name == NULL)
-		return;
-	
-	replace_char(loc_name, '/', LOC_SEPARATOR);
-	
-	asprintf(&loc_pathname, "%s/%s", LOC_DEVICE_NAMESPACE,
-	    loc_name);
-	if (loc_pathname == NULL) {
-		free(loc_name);
-		return;
-	}
-	
-	loc_service_register_with_iface(loc_pathname,
-	    &fun->service_id, DEVMAN_CONNECT_FROM_LOC);
-	
-	tree_add_loc_function(tree, fun);
-	
-	free(loc_name);
-	free(loc_pathname);
-}
-
-/** Pass a device to running driver.
- *
- * @param drv		The driver's structure.
- * @param node		The device's node in the device tree.
- */
-void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
-{
-	/*
-	 * We do not expect to have driver's mutex locked as we do not
-	 * access any structures that would affect driver_t.
-	 */
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
-	    drv->name, dev->pfun->name);
-	
-	/* Send the device to the driver. */
-	devman_handle_t parent_handle;
-	if (dev->pfun) {
-		parent_handle = dev->pfun->handle;
-	} else {
-		parent_handle = 0;
-	}
-	
-	async_exch_t *exch = async_exchange_begin(drv->sess);
-	
-	ipc_call_t answer;
-	aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
-	    parent_handle, &answer);
-	
-	/* Send the device name to the driver. */
-	sysarg_t rc = async_data_write_start(exch, dev->pfun->name,
-	    str_size(dev->pfun->name) + 1);
-	
-	async_exchange_end(exch);
-	
-	if (rc != EOK) {
-		/* TODO handle error */
-	}
-
-	/* Wait for answer from the driver. */
-	async_wait_for(req, &rc);
-
-	switch(rc) {
-	case EOK:
-		dev->state = DEVICE_USABLE;
-		break;
-	case ENOENT:
-		dev->state = DEVICE_NOT_PRESENT;
-		break;
-	default:
-		dev->state = DEVICE_INVALID;
-		break;
-	}
-	
-	dev->passed_to_driver = true;
-
-	return;
-}
-
-/** Find suitable driver for a device and assign the driver to it.
- *
- * @param node		The device node of the device in the device tree.
- * @param drivers_list	The list of available drivers.
- * @return		True if the suitable driver is found and
- *			successfully assigned to the device, false otherwise.
- */
-bool assign_driver(dev_node_t *dev, driver_list_t *drivers_list,
-    dev_tree_t *tree)
-{
-	assert(dev != NULL);
-	assert(drivers_list != NULL);
-	assert(tree != NULL);
-	
-	/*
-	 * Find the driver which is the most suitable for handling this device.
-	 */
-	driver_t *drv = find_best_match_driver(drivers_list, dev);
-	if (drv == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "No driver found for device `%s'.",
-		    dev->pfun->pathname);
-		return false;
-	}
-	
-	/* Attach the driver to the device. */
-	attach_driver(tree, dev, drv);
-	
-	fibril_mutex_lock(&drv->driver_mutex);
-	if (drv->state == DRIVER_NOT_STARTED) {
-		/* Start the driver. */
-		start_driver(drv);
-	}
-	bool is_running = drv->state == DRIVER_RUNNING;
-	fibril_mutex_unlock(&drv->driver_mutex);
-
-	/* Notify the driver about the new device. */
-	if (is_running)
-		add_device(drv, dev, tree);
-	
-	fibril_mutex_lock(&drv->driver_mutex);
-	fibril_mutex_unlock(&drv->driver_mutex);
-
-	fibril_rwlock_write_lock(&tree->rwlock);
-	if (dev->pfun != NULL) {
-		dev->pfun->state = FUN_ON_LINE;
-	}
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	return true;
-}
-
-int driver_dev_remove(dev_tree_t *tree, dev_node_t *dev)
-{
-	async_exch_t *exch;
-	sysarg_t retval;
-	driver_t *drv;
-	devman_handle_t handle;
-	
-	assert(dev != NULL);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_remove(%p)", dev);
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	drv = dev->drv;
-	handle = dev->handle;
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	exch = async_exchange_begin(drv->sess);
-	retval = async_req_1_0(exch, DRIVER_DEV_REMOVE, handle);
-	async_exchange_end(exch);
-	
-	return retval;
-}
-
-int driver_dev_gone(dev_tree_t *tree, dev_node_t *dev)
-{
-	async_exch_t *exch;
-	sysarg_t retval;
-	driver_t *drv;
-	devman_handle_t handle;
-	
-	assert(dev != NULL);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_gone(%p)", dev);
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	drv = dev->drv;
-	handle = dev->handle;
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	exch = async_exchange_begin(drv->sess);
-	retval = async_req_1_0(exch, DRIVER_DEV_GONE, handle);
-	async_exchange_end(exch);
-	
-	return retval;
-}
-
-int driver_fun_online(dev_tree_t *tree, fun_node_t *fun)
-{
-	async_exch_t *exch;
-	sysarg_t retval;
-	driver_t *drv;
-	devman_handle_t handle;
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_online(%p)", fun);
-
-	fibril_rwlock_read_lock(&tree->rwlock);
-	
-	if (fun->dev == NULL) {
-		/* XXX root function? */
-		fibril_rwlock_read_unlock(&tree->rwlock);
-		return EINVAL;
-	}
-	
-	drv = fun->dev->drv;
-	handle = fun->handle;
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	exch = async_exchange_begin(drv->sess);
-	retval = async_req_1_0(exch, DRIVER_FUN_ONLINE, handle);
-	loc_exchange_end(exch);
-	
-	return retval;
-}
-
-int driver_fun_offline(dev_tree_t *tree, fun_node_t *fun)
-{
-	async_exch_t *exch;
-	sysarg_t retval;
-	driver_t *drv;
-	devman_handle_t handle;
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_offline(%p)", fun);
-
-	fibril_rwlock_read_lock(&tree->rwlock);
-	if (fun->dev == NULL) {
-		/* XXX root function? */
-		fibril_rwlock_read_unlock(&tree->rwlock);
-		return EINVAL;
-	}
-	
-	drv = fun->dev->drv;
-	handle = fun->handle;
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	exch = async_exchange_begin(drv->sess);
-	retval = async_req_1_0(exch, DRIVER_FUN_OFFLINE, handle);
-	loc_exchange_end(exch);
-	
-	return retval;
-
-}
-
-/** Initialize the device tree.
- *
- * Create root device node of the tree and assign driver to it.
- *
- * @param tree		The device tree.
- * @param drivers_list	the list of available drivers.
- * @return		True on success, false otherwise.
- */
-bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "init_device_tree()");
-	
-	tree->current_handle = 0;
-	
-	hash_table_create(&tree->devman_devices, 0, 0, &devman_devices_ops);
-	hash_table_create(&tree->devman_functions, 0, 0, &devman_functions_ops);
-	hash_table_create(&tree->loc_functions, 0, 0, &loc_devices_ops);
-	
-	fibril_rwlock_initialize(&tree->rwlock);
-	
-	/* Create root function and root device and add them to the device tree. */
-	if (!create_root_nodes(tree))
-		return false;
-    
-	/* Find suitable driver and start it. */
-	dev_node_t *rdev = tree->root_node->child;
-	dev_add_ref(rdev);
-	int rc = assign_driver(rdev, drivers_list, tree);
-	dev_del_ref(rdev);
-	
-	return rc;
-}
-
-/* Device nodes */
-
-/** Create a new device node.
- *
- * @return		A device node structure.
- */
-dev_node_t *create_dev_node(void)
-{
-	dev_node_t *dev;
-	
-	dev = calloc(1, sizeof(dev_node_t));
-	if (dev == NULL)
-		return NULL;
-	
-	atomic_set(&dev->refcnt, 0);
-	list_initialize(&dev->functions);
-	link_initialize(&dev->driver_devices);
-	
-	return dev;
-}
-
-/** Delete a device node.
- *
- * @param node		The device node structure.
- */
-void delete_dev_node(dev_node_t *dev)
-{
-	assert(list_empty(&dev->functions));
-	assert(dev->pfun == NULL);
-	assert(dev->drv == NULL);
-	
-	free(dev);
-}
-
-/** Increase device node reference count.
- *
- * @param dev	Device node
- */
-void dev_add_ref(dev_node_t *dev)
-{
-	atomic_inc(&dev->refcnt);
-}
-
-/** Decrease device node reference count.
- *
- * When the count drops to zero the device node is freed.
- *
- * @param dev	Device node
- */
-void dev_del_ref(dev_node_t *dev)
-{
-	if (atomic_predec(&dev->refcnt) == 0)
-		delete_dev_node(dev);
-}
-
-/** Find the device node structure of the device witch has the specified handle.
- *
- * @param tree		The device tree where we look for the device node.
- * @param handle	The handle of the device.
- * @return		The device node.
- */
-dev_node_t *find_dev_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
-{
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-	
-	ht_link_t *link = hash_table_find(&tree->devman_devices, &handle);
-	if (link == NULL)
-		return NULL;
-	
-	return hash_table_get_inst(link, dev_node_t, devman_dev);
-}
-
-/** Find the device node structure of the device witch has the specified handle.
- *
- * @param tree		The device tree where we look for the device node.
- * @param handle	The handle of the device.
- * @return		The device node.
- */
-dev_node_t *find_dev_node(dev_tree_t *tree, devman_handle_t handle)
-{
-	dev_node_t *dev = NULL;
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	dev = find_dev_node_no_lock(tree, handle);
-	if (dev != NULL)
-		dev_add_ref(dev);
-	
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	return dev;
-}
-
-/** Get list of device functions. */
-int dev_get_functions(dev_tree_t *tree, dev_node_t *dev,
-    devman_handle_t *hdl_buf, size_t buf_size, size_t *act_size)
-{
-	size_t act_cnt;
-	size_t buf_cnt;
-
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-
-	buf_cnt = buf_size / sizeof(devman_handle_t);
-
-	act_cnt = list_count(&dev->functions);
-	*act_size = act_cnt * sizeof(devman_handle_t);
-
-	if (buf_size % sizeof(devman_handle_t) != 0)
-		return EINVAL;
-
-	size_t pos = 0;
-	list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
-		if (pos < buf_cnt) {
-			hdl_buf[pos] = fun->handle;
-		}
-
-		pos++;
-	}
-
-	return EOK;
-}
-
-
-/* Function nodes */
-
-/** Create a new function node.
- *
- * @return		A function node structure.
- */
-fun_node_t *create_fun_node(void)
-{
-	fun_node_t *fun;
-
-	fun = calloc(1, sizeof(fun_node_t));
-	if (fun == NULL)
-		return NULL;
-	
-	fun->state = FUN_INIT;
-	atomic_set(&fun->refcnt, 0);
-	fibril_mutex_initialize(&fun->busy_lock);
-	link_initialize(&fun->dev_functions);
-	list_initialize(&fun->match_ids.ids);
-	
-	return fun;
-}
-
-/** Delete a function node.
- *
- * @param fun		The device node structure.
- */
-void delete_fun_node(fun_node_t *fun)
-{
-	assert(fun->dev == NULL);
-	assert(fun->child == NULL);
-	
-	clean_match_ids(&fun->match_ids);
-	free(fun->name);
-	free(fun->pathname);
-	free(fun);
-}
-
-/** Increase function node reference count.
- *
- * @param fun	Function node
- */
-void fun_add_ref(fun_node_t *fun)
-{
-	atomic_inc(&fun->refcnt);
-}
-
-/** Decrease function node reference count.
- *
- * When the count drops to zero the function node is freed.
- *
- * @param fun	Function node
- */
-void fun_del_ref(fun_node_t *fun)
-{
-	if (atomic_predec(&fun->refcnt) == 0)
-		delete_fun_node(fun);
-}
-
-/** Make function busy for reconfiguration operations. */
-void fun_busy_lock(fun_node_t *fun)
-{
-	fibril_mutex_lock(&fun->busy_lock);
-}
-
-/** Mark end of reconfiguration operation. */
-void fun_busy_unlock(fun_node_t *fun)
-{
-	fibril_mutex_unlock(&fun->busy_lock);
-}
-
-/** Find the function node with the specified handle.
- *
- * @param tree		The device tree where we look for the device node.
- * @param handle	The handle of the function.
- * @return		The function node.
- */
-fun_node_t *find_fun_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
-{
-	fun_node_t *fun;
-	
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-	
-	ht_link_t *link = hash_table_find(&tree->devman_functions, &handle);
-	if (link == NULL)
-		return NULL;
-	
-	fun = hash_table_get_inst(link, fun_node_t, devman_fun);
-	
-	return fun;
-}
-
-/** Find the function node with the specified handle.
- *
- * @param tree		The device tree where we look for the device node.
- * @param handle	The handle of the function.
- * @return		The function node.
- */
-fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle)
-{
-	fun_node_t *fun = NULL;
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	
-	fun = find_fun_node_no_lock(tree, handle);
-	if (fun != NULL)
-		fun_add_ref(fun);
-	
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	return fun;
-}
-
-/** Create and set device's full path in device tree.
- *
- * @param tree		Device tree
- * @param node		The device's device node.
- * @param parent	The parent device node.
- * @return		True on success, false otherwise (insufficient
- *			resources etc.).
- */
-static bool set_fun_path(dev_tree_t *tree, fun_node_t *fun, fun_node_t *parent)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	assert(fun->name != NULL);
-	
-	size_t pathsize = (str_size(fun->name) + 1);
-	if (parent != NULL)
-		pathsize += str_size(parent->pathname) + 1;
-	
-	fun->pathname = (char *) malloc(pathsize);
-	if (fun->pathname == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate device path.");
-		return false;
-	}
-	
-	if (parent != NULL) {
-		str_cpy(fun->pathname, pathsize, parent->pathname);
-		str_append(fun->pathname, pathsize, "/");
-		str_append(fun->pathname, pathsize, fun->name);
-	} else {
-		str_cpy(fun->pathname, pathsize, fun->name);
-	}
-	
-	return true;
-}
-
-/** Insert new device into device tree.
- *
- * @param tree		The device tree.
- * @param dev		The newly added device node.
- * @param pfun		The parent function node.
- *
- * @return		True on success, false otherwise (insufficient resources
- *			etc.).
- */
-bool insert_dev_node(dev_tree_t *tree, dev_node_t *dev, fun_node_t *pfun)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
-	    dev, pfun, pfun->pathname);
-
-	/* Add the node to the handle-to-node map. */
-	dev->handle = ++tree->current_handle;
-	hash_table_insert(&tree->devman_devices, &dev->devman_dev);
-
-	/* Add the node to the list of its parent's children. */
-	dev->pfun = pfun;
-	pfun->child = dev;
-	
-	return true;
-}
-
-/** Remove device from device tree.
- *
- * @param tree		Device tree
- * @param dev		Device node
- */
-void remove_dev_node(dev_tree_t *tree, dev_node_t *dev)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
-	
-	/* Remove node from the handle-to-node map. */
-	hash_table_remove(&tree->devman_devices, &dev->handle);
-	
-	/* Unlink from parent function. */
-	dev->pfun->child = NULL;
-	dev->pfun = NULL;
-	
-	dev->state = DEVICE_REMOVED;
-}
-
-
-/** Insert new function into device tree.
- *
- * @param tree		The device tree.
- * @param fun		The newly added function node. 
- * @param fun_name	The name of the newly added function.
- * @param dev		Owning device node.
- *
- * @return		True on success, false otherwise (insufficient resources
- *			etc.).
- */
-bool insert_fun_node(dev_tree_t *tree, fun_node_t *fun, char *fun_name,
-    dev_node_t *dev)
-{
-	fun_node_t *pfun;
-	
-	assert(fun_name != NULL);
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	/*
-	 * The root function is a special case, it does not belong to any
-	 * device so for the root function dev == NULL.
-	 */
-	pfun = (dev != NULL) ? dev->pfun : NULL;
-	
-	fun->name = fun_name;
-	if (!set_fun_path(tree, fun, pfun)) {
-		return false;
-	}
-	
-	/* Add the node to the handle-to-node map. */
-	fun->handle = ++tree->current_handle;
-	hash_table_insert(&tree->devman_functions, &fun->devman_fun);
-
-	/* Add the node to the list of its parent's children. */
-	fun->dev = dev;
-	if (dev != NULL)
-		list_append(&fun->dev_functions, &dev->functions);
-	
-	return true;
-}
-
-/** Remove function from device tree.
- *
- * @param tree		Device tree
- * @param node		Function node to remove
- */
-void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	/* Remove the node from the handle-to-node map. */
-	hash_table_remove(&tree->devman_functions, &fun->handle);
-	
-	/* Remove the node from the list of its parent's children. */
-	if (fun->dev != NULL)
-		list_remove(&fun->dev_functions);
-	
-	fun->dev = NULL;
-	fun->state = FUN_REMOVED;
-}
-
-/** Find function node with a specified path in the device tree.
- * 
- * @param path		The path of the function node in the device tree.
- * @param tree		The device tree.
- * @return		The function node if it is present in the tree, NULL
- *			otherwise.
- */
-fun_node_t *find_fun_node_by_path(dev_tree_t *tree, char *path)
-{
-	assert(path != NULL);
-
-	bool is_absolute = path[0] == '/';
-	if (!is_absolute) {
-		return NULL;
-	}
-
-	fibril_rwlock_read_lock(&tree->rwlock);
-	
-	fun_node_t *fun = tree->root_node;
-	fun_add_ref(fun);
-	/*
-	 * Relative path to the function from its parent (but with '/' at the
-	 * beginning)
-	 */
-	char *rel_path = path;
-	char *next_path_elem = NULL;
-	bool cont = (rel_path[1] != '\0');
-	
-	while (cont && fun != NULL) {
-		next_path_elem  = get_path_elem_end(rel_path + 1);
-		if (next_path_elem[0] == '/') {
-			cont = true;
-			next_path_elem[0] = 0;
-		} else {
-			cont = false;
-		}
-		
-		fun_node_t *cfun = find_node_child(tree, fun, rel_path + 1);
-		fun_del_ref(fun);
-		fun = cfun;
-		
-		if (cont) {
-			/* Restore the original path. */
-			next_path_elem[0] = '/';
-		}
-		rel_path = next_path_elem;
-	}
-	
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	return fun;
-}
-
-/** Find function with a specified name belonging to given device.
- *
- * Device tree rwlock should be held at least for reading.
- *
- * @param tree Device tree
- * @param dev Device the function belongs to.
- * @param name Function name (not path).
- * @return Function node.
- * @retval NULL No function with given name.
- */
-fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *dev,
-    const char *name)
-{
-	assert(name != NULL);
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-
-	list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
-		if (str_cmp(name, fun->name) == 0) {
-			fun_add_ref(fun);
-			return fun;
-		}
-	}
-
-	return NULL;
-}
-
-/** Find child function node with a specified name.
- *
- * Device tree rwlock should be held at least for reading.
- *
- * @param tree		Device tree
- * @param parent	The parent function node.
- * @param name		The name of the child function.
- * @return		The child function node.
- */
-static fun_node_t *find_node_child(dev_tree_t *tree, fun_node_t *pfun,
-    const char *name)
-{
-	return find_fun_node_in_device(tree, pfun->child, name);
-}
-
-/* loc devices */
-
-fun_node_t *find_loc_tree_function(dev_tree_t *tree, service_id_t service_id)
-{
-	fun_node_t *fun = NULL;
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	ht_link_t *link = hash_table_find(&tree->loc_functions, &service_id);
-	if (link != NULL) {
-		fun = hash_table_get_inst(link, fun_node_t, loc_fun);
-		fun_add_ref(fun);
-	}
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	return fun;
-}
-
-void tree_add_loc_function(dev_tree_t *tree, fun_node_t *fun)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	hash_table_insert(&tree->loc_functions, &fun->loc_fun);
-}
-
-/** @}
- */
Index: uspace/srv/devman/devman.h
===================================================================
--- uspace/srv/devman/devman.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/devman/devman.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -35,8 +35,5 @@
 #define DEVMAN_H_
 
-#include <assert.h>
 #include <stdbool.h>
-#include <dirent.h>
-#include <str.h>
 #include <adt/list.h>
 #include <adt/hash_table.h>
@@ -51,6 +48,4 @@
 #define NAME "devman"
 
-#define MATCH_EXT ".ma"
-
 #define LOC_DEVICE_NAMESPACE "devices"
 #define LOC_SEPARATOR '\\'
@@ -67,22 +62,10 @@
 } client_t;
 
-typedef enum {
-	/** Driver has not been started. */
-	DRIVER_NOT_STARTED = 0,
-	
-	/**
-	 * Driver has been started, but has not registered as running and ready
-	 * to receive requests.
-	 */
-	DRIVER_STARTING,
-	
-	/** Driver is running and prepared to serve incomming requests. */
-	DRIVER_RUNNING
-} driver_state_t;
-
 /** Representation of device driver. */
 typedef struct driver {
 	/** Pointers to previous and next drivers in a linked list. */
 	link_t drivers;
+	/** Handle */
+	devman_handle_t handle;
 	
 	/**
@@ -90,5 +73,5 @@
 	 * and prepared to receive requests.
 	 */
-	int state;
+	driver_state_t state;
 	
 	/** Session asociated with this driver. */
@@ -115,4 +98,6 @@
 	/** Fibril mutex for list of drivers. */
 	fibril_mutex_t drivers_mutex;
+	/** Next free handle */
+	devman_handle_t next_handle;
 } driver_list_t;
 
@@ -238,84 +223,4 @@
 } dev_tree_t;
 
-/* Match ids and scores */
-
-extern int get_match_score(driver_t *, dev_node_t *);
-
-extern bool parse_match_ids(char *, match_id_list_t *);
-extern bool read_match_ids(const char *, match_id_list_t *);
-extern char *read_match_id(char **);
-extern char *read_id(const char **);
-
-/* Drivers */
-
-extern void init_driver_list(driver_list_t *);
-extern driver_t *create_driver(void);
-extern bool get_driver_info(const char *, const char *, driver_t *);
-extern int lookup_available_drivers(driver_list_t *, const char *);
-
-extern driver_t *find_best_match_driver(driver_list_t *, dev_node_t *);
-extern bool assign_driver(dev_node_t *, driver_list_t *, dev_tree_t *);
-
-extern void add_driver(driver_list_t *, driver_t *);
-extern void attach_driver(dev_tree_t *, dev_node_t *, driver_t *);
-extern void detach_driver(dev_tree_t *, dev_node_t *);
-extern void add_device(driver_t *, dev_node_t *, dev_tree_t *);
-extern bool start_driver(driver_t *);
-extern int driver_dev_remove(dev_tree_t *, dev_node_t *);
-extern int driver_dev_gone(dev_tree_t *, dev_node_t *);
-extern int driver_fun_online(dev_tree_t *, fun_node_t *);
-extern int driver_fun_offline(dev_tree_t *, fun_node_t *);
-
-extern driver_t *find_driver(driver_list_t *, const char *);
-extern void initialize_running_driver(driver_t *, dev_tree_t *);
-
-extern void init_driver(driver_t *);
-extern void clean_driver(driver_t *);
-extern void delete_driver(driver_t *);
-
-/* Device nodes */
-
-extern dev_node_t *create_dev_node(void);
-extern void delete_dev_node(dev_node_t *node);
-extern void dev_add_ref(dev_node_t *);
-extern void dev_del_ref(dev_node_t *);
-
-extern dev_node_t *find_dev_node_no_lock(dev_tree_t *tree,
-    devman_handle_t handle);
-extern dev_node_t *find_dev_node(dev_tree_t *tree, devman_handle_t handle);
-extern dev_node_t *find_dev_function(dev_node_t *, const char *);
-extern int dev_get_functions(dev_tree_t *tree, dev_node_t *, devman_handle_t *,
-    size_t, size_t *);
-
-extern fun_node_t *create_fun_node(void);
-extern void delete_fun_node(fun_node_t *);
-extern void fun_add_ref(fun_node_t *);
-extern void fun_del_ref(fun_node_t *);
-extern void fun_busy_lock(fun_node_t *);
-extern void fun_busy_unlock(fun_node_t *);
-extern fun_node_t *find_fun_node_no_lock(dev_tree_t *tree,
-    devman_handle_t handle);
-extern fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle);
-extern fun_node_t *find_fun_node_by_path(dev_tree_t *, char *);
-extern fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *,
-    const char *);
-
-/* Device tree */
-
-extern bool init_device_tree(dev_tree_t *, driver_list_t *);
-extern bool create_root_nodes(dev_tree_t *);
-extern bool insert_dev_node(dev_tree_t *, dev_node_t *, fun_node_t *);
-extern void remove_dev_node(dev_tree_t *, dev_node_t *);
-extern bool insert_fun_node(dev_tree_t *, fun_node_t *, char *, dev_node_t *);
-extern void remove_fun_node(dev_tree_t *, fun_node_t *);
-
-/* Loc services */
-
-extern void loc_register_tree_function(fun_node_t *, dev_tree_t *);
-
-extern fun_node_t *find_loc_tree_function(dev_tree_t *, service_id_t);
-
-extern void tree_add_loc_function(dev_tree_t *, fun_node_t *);
-
 #endif
 
Index: uspace/srv/devman/devtree.c
===================================================================
--- uspace/srv/devman/devtree.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/devtree.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#include <errno.h>
+#include <io/log.h>
+
+#include "dev.h"
+#include "devtree.h"
+#include "devman.h"
+#include "driver.h"
+#include "fun.h"
+
+/* hash table operations */
+
+static inline size_t handle_key_hash(void *key)
+{
+	devman_handle_t handle = *(devman_handle_t*)key;
+	return handle;
+}
+
+static size_t devman_devices_hash(const ht_link_t *item)
+{
+	dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
+	return handle_key_hash(&dev->handle);
+}
+
+static size_t devman_functions_hash(const ht_link_t *item)
+{
+	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
+	return handle_key_hash(&fun->handle);
+}
+
+static bool devman_devices_key_equal(void *key, const ht_link_t *item)
+{
+	devman_handle_t handle = *(devman_handle_t*)key;
+	dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
+	return dev->handle == handle;
+}
+
+static bool devman_functions_key_equal(void *key, const ht_link_t *item)
+{
+	devman_handle_t handle = *(devman_handle_t*)key;
+	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
+	return fun->handle == handle;
+}
+
+static inline size_t service_id_key_hash(void *key)
+{
+	service_id_t service_id = *(service_id_t*)key;
+	return service_id;
+}
+
+static size_t loc_functions_hash(const ht_link_t *item)
+{
+	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
+	return service_id_key_hash(&fun->service_id);
+}
+
+static bool loc_functions_key_equal(void *key, const ht_link_t *item)
+{
+	service_id_t service_id = *(service_id_t*)key;
+	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
+	return fun->service_id == service_id;
+}
+
+
+static hash_table_ops_t devman_devices_ops = {
+	.hash = devman_devices_hash,
+	.key_hash = handle_key_hash,
+	.key_equal = devman_devices_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+static hash_table_ops_t devman_functions_ops = {
+	.hash = devman_functions_hash,
+	.key_hash = handle_key_hash,
+	.key_equal = devman_functions_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+static hash_table_ops_t loc_devices_ops = {
+	.hash = loc_functions_hash,
+	.key_hash = service_id_key_hash,
+	.key_equal = loc_functions_key_equal,
+	.equal = NULL,
+	.remove_callback = NULL
+};
+
+/** Create root device and function node in the device tree.
+ *
+ * @param tree	The device tree.
+ * @return	True on success, false otherwise.
+ */
+bool create_root_nodes(dev_tree_t *tree)
+{
+	fun_node_t *fun;
+	dev_node_t *dev;
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "create_root_nodes()");
+	
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	/*
+	 * Create root function. This is a pseudo function to which
+	 * the root device node is attached. It allows us to match
+	 * the root device driver in a standard manner, i.e. against
+	 * the parent function.
+	 */
+	
+	fun = create_fun_node();
+	if (fun == NULL) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		return false;
+	}
+	
+	fun_add_ref(fun);
+	insert_fun_node(tree, fun, str_dup(""), NULL);
+	
+	match_id_t *id = create_match_id();
+	id->id = str_dup("root");
+	id->score = 100;
+	add_match_id(&fun->match_ids, id);
+	tree->root_node = fun;
+	
+	/*
+	 * Create root device node.
+	 */
+	dev = create_dev_node();
+	if (dev == NULL) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		return false;
+	}
+	
+	dev_add_ref(dev);
+	insert_dev_node(tree, dev, fun);
+	
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	
+	return dev != NULL;
+}
+
+/** Initialize the device tree.
+ *
+ * Create root device node of the tree and assign driver to it.
+ *
+ * @param tree		The device tree.
+ * @param drivers_list	the list of available drivers.
+ * @return		True on success, false otherwise.
+ */
+bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "init_device_tree()");
+	
+	tree->current_handle = 0;
+	
+	hash_table_create(&tree->devman_devices, 0, 0, &devman_devices_ops);
+	hash_table_create(&tree->devman_functions, 0, 0, &devman_functions_ops);
+	hash_table_create(&tree->loc_functions, 0, 0, &loc_devices_ops);
+	
+	fibril_rwlock_initialize(&tree->rwlock);
+	
+	/* Create root function and root device and add them to the device tree. */
+	if (!create_root_nodes(tree))
+		return false;
+    
+	/* Find suitable driver and start it. */
+	dev_node_t *rdev = tree->root_node->child;
+	dev_add_ref(rdev);
+	int rc = assign_driver(rdev, drivers_list, tree);
+	dev_del_ref(rdev);
+	
+	return rc;
+}
+
+/** Insert new device into device tree.
+ *
+ * @param tree		The device tree.
+ * @param dev		The newly added device node.
+ * @param pfun		The parent function node.
+ *
+ * @return		True on success, false otherwise (insufficient resources
+ *			etc.).
+ */
+bool insert_dev_node(dev_tree_t *tree, dev_node_t *dev, fun_node_t *pfun)
+{
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
+	    dev, pfun, pfun->pathname);
+
+	/* Add the node to the handle-to-node map. */
+	dev->handle = ++tree->current_handle;
+	hash_table_insert(&tree->devman_devices, &dev->devman_dev);
+
+	/* Add the node to the list of its parent's children. */
+	dev->pfun = pfun;
+	pfun->child = dev;
+	
+	return true;
+}
+
+/** Remove device from device tree.
+ *
+ * @param tree		Device tree
+ * @param dev		Device node
+ */
+void remove_dev_node(dev_tree_t *tree, dev_node_t *dev)
+{
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
+	
+	/* Remove node from the handle-to-node map. */
+	hash_table_remove(&tree->devman_devices, &dev->handle);
+	
+	/* Unlink from parent function. */
+	dev->pfun->child = NULL;
+	dev->pfun = NULL;
+	
+	dev->state = DEVICE_REMOVED;
+}
+
+
+/** Insert new function into device tree.
+ *
+ * @param tree		The device tree.
+ * @param fun		The newly added function node. 
+ * @param fun_name	The name of the newly added function.
+ * @param dev		Owning device node.
+ *
+ * @return		True on success, false otherwise (insufficient resources
+ *			etc.).
+ */
+bool insert_fun_node(dev_tree_t *tree, fun_node_t *fun, char *fun_name,
+    dev_node_t *dev)
+{
+	fun_node_t *pfun;
+	
+	assert(fun_name != NULL);
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	/*
+	 * The root function is a special case, it does not belong to any
+	 * device so for the root function dev == NULL.
+	 */
+	pfun = (dev != NULL) ? dev->pfun : NULL;
+	
+	fun->name = fun_name;
+	if (!set_fun_path(tree, fun, pfun)) {
+		return false;
+	}
+	
+	/* Add the node to the handle-to-node map. */
+	fun->handle = ++tree->current_handle;
+	hash_table_insert(&tree->devman_functions, &fun->devman_fun);
+
+	/* Add the node to the list of its parent's children. */
+	fun->dev = dev;
+	if (dev != NULL)
+		list_append(&fun->dev_functions, &dev->functions);
+	
+	return true;
+}
+
+/** Remove function from device tree.
+ *
+ * @param tree		Device tree
+ * @param node		Function node to remove
+ */
+void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
+{
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	/* Remove the node from the handle-to-node map. */
+	hash_table_remove(&tree->devman_functions, &fun->handle);
+	
+	/* Remove the node from the list of its parent's children. */
+	if (fun->dev != NULL)
+		list_remove(&fun->dev_functions);
+	
+	fun->dev = NULL;
+	fun->state = FUN_REMOVED;
+}
+
+/** @}
+ */
Index: uspace/srv/devman/devtree.h
===================================================================
--- uspace/srv/devman/devtree.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/devtree.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#ifndef DEVTREE_H_
+#define DEVTREE_H_
+
+#include <stdbool.h>
+
+#include "devman.h"
+
+extern bool init_device_tree(dev_tree_t *, driver_list_t *);
+extern bool create_root_nodes(dev_tree_t *);
+extern bool insert_dev_node(dev_tree_t *, dev_node_t *, fun_node_t *);
+extern void remove_dev_node(dev_tree_t *, dev_node_t *);
+extern bool insert_fun_node(dev_tree_t *, fun_node_t *, char *, dev_node_t *);
+extern void remove_fun_node(dev_tree_t *, fun_node_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/driver.c
===================================================================
--- uspace/srv/devman/driver.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/driver.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,764 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <io/log.h>
+#include <ipc/driver.h>
+#include <loc.h>
+#include <str_error.h>
+#include <stdio.h>
+
+#include "dev.h"
+#include "devman.h"
+#include "driver.h"
+#include "match.h"
+
+/**
+ * Initialize the list of device driver's.
+ *
+ * @param drv_list the list of device driver's.
+ *
+ */
+void init_driver_list(driver_list_t *drv_list)
+{
+	assert(drv_list != NULL);
+	
+	list_initialize(&drv_list->drivers);
+	fibril_mutex_initialize(&drv_list->drivers_mutex);
+	drv_list->next_handle = 1;
+}
+
+/** Allocate and initialize a new driver structure.
+ *
+ * @return	Driver structure.
+ */
+driver_t *create_driver(void)
+{
+	driver_t *res = malloc(sizeof(driver_t));
+	if (res != NULL)
+		init_driver(res);
+	return res;
+}
+
+/** Add a driver to the list of drivers.
+ *
+ * @param drivers_list	List of drivers.
+ * @param drv		Driver structure.
+ */
+void add_driver(driver_list_t *drivers_list, driver_t *drv)
+{
+	fibril_mutex_lock(&drivers_list->drivers_mutex);
+	list_append(&drv->drivers, &drivers_list->drivers);
+	drv->handle = drivers_list->next_handle++;
+	fibril_mutex_unlock(&drivers_list->drivers_mutex);
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' was added to the list of available "
+	    "drivers.", drv->name);
+}
+
+/**
+ * Get information about a driver.
+ *
+ * Each driver has its own directory in the base directory.
+ * The name of the driver's directory is the same as the name of the driver.
+ * The driver's directory contains driver's binary (named as the driver without
+ * extension) and the configuration file with match ids for device-to-driver
+ *  matching (named as the driver with a special extension).
+ *
+ * This function searches for the driver's directory and containing
+ * configuration files. If all the files needed are found, they are parsed and
+ * the information about the driver is stored in the driver's structure.
+ *
+ * @param base_path	The base directory, in which we look for driver's
+ *			subdirectory.
+ * @param name		The name of the driver.
+ * @param drv		The driver structure to fill information in.
+ *
+ * @return		True on success, false otherwise.
+ */
+bool get_driver_info(const char *base_path, const char *name, driver_t *drv)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "get_driver_info(base_path=\"%s\", name=\"%s\")",
+	    base_path, name);
+	
+	assert(base_path != NULL && name != NULL && drv != NULL);
+	
+	bool suc = false;
+	char *match_path = NULL;
+	size_t name_size = 0;
+	
+	/* Read the list of match ids from the driver's configuration file. */
+	match_path = get_abs_path(base_path, name, MATCH_EXT);
+	if (match_path == NULL)
+		goto cleanup;
+	
+	if (!read_match_ids(match_path, &drv->match_ids))
+		goto cleanup;
+	
+	/* Allocate and fill driver's name. */
+	name_size = str_size(name) + 1;
+	drv->name = malloc(name_size);
+	if (drv->name == NULL)
+		goto cleanup;
+	str_cpy(drv->name, name_size, name);
+	
+	/* Initialize path with driver's binary. */
+	drv->binary_path = get_abs_path(base_path, name, "");
+	if (drv->binary_path == NULL)
+		goto cleanup;
+	
+	/* Check whether the driver's binary exists. */
+	struct stat s;
+	if (stat(drv->binary_path, &s) == ENOENT) { /* FIXME!! */
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Driver not found at path `%s'.",
+		    drv->binary_path);
+		goto cleanup;
+	}
+	
+	suc = true;
+	
+cleanup:
+	if (!suc) {
+		free(drv->binary_path);
+		free(drv->name);
+		/* Set the driver structure to the default state. */
+		init_driver(drv);
+	}
+	
+	free(match_path);
+	
+	return suc;
+}
+
+/** Lookup drivers in the directory.
+ *
+ * @param drivers_list	The list of available drivers.
+ * @param dir_path	The path to the directory where we search for drivers.
+ * @return		Number of drivers which were found.
+ */
+int lookup_available_drivers(driver_list_t *drivers_list, const char *dir_path)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "lookup_available_drivers(dir=\"%s\")", dir_path);
+	
+	int drv_cnt = 0;
+	DIR *dir = NULL;
+	struct dirent *diren;
+
+	dir = opendir(dir_path);
+	
+	if (dir != NULL) {
+		driver_t *drv = create_driver();
+		while ((diren = readdir(dir))) {
+			if (get_driver_info(dir_path, diren->d_name, drv)) {
+				add_driver(drivers_list, drv);
+				drv_cnt++;
+				drv = create_driver();
+			}
+		}
+		delete_driver(drv);
+		closedir(dir);
+	}
+	
+	return drv_cnt;
+}
+
+/** Lookup the best matching driver for the specified device in the list of
+ * drivers.
+ *
+ * A match between a device and a driver is found if one of the driver's match
+ * ids match one of the device's match ids. The score of the match is the
+ * product of the driver's and device's score associated with the matching id.
+ * The best matching driver for a device is the driver with the highest score
+ * of the match between the device and the driver.
+ *
+ * @param drivers_list	The list of drivers, where we look for the driver
+ *			suitable for handling the device.
+ * @param node		The device node structure of the device.
+ * @return		The best matching driver or NULL if no matching driver
+ *			is found.
+ */
+driver_t *find_best_match_driver(driver_list_t *drivers_list, dev_node_t *node)
+{
+	driver_t *best_drv = NULL;
+	int best_score = 0, score = 0;
+	
+	fibril_mutex_lock(&drivers_list->drivers_mutex);
+	
+	list_foreach(drivers_list->drivers, drivers, driver_t, drv) {
+		score = get_match_score(drv, node);
+		if (score > best_score) {
+			best_score = score;
+			best_drv = drv;
+		}
+	}
+	
+	fibril_mutex_unlock(&drivers_list->drivers_mutex);
+	
+	return best_drv;
+}
+
+/** Assign a driver to a device.
+ *
+ * @param tree		Device tree
+ * @param node		The device's node in the device tree.
+ * @param drv		The driver.
+ */
+void attach_driver(dev_tree_t *tree, dev_node_t *dev, driver_t *drv)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "attach_driver(dev=\"%s\",drv=\"%s\")",
+	    dev->pfun->pathname, drv->name);
+	
+	fibril_mutex_lock(&drv->driver_mutex);
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	dev->drv = drv;
+	list_append(&dev->driver_devices, &drv->devices);
+	
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	fibril_mutex_unlock(&drv->driver_mutex);
+}
+
+/** Detach driver from device.
+ *
+ * @param tree		Device tree
+ * @param node		The device's node in the device tree.
+ * @param drv		The driver.
+ */
+void detach_driver(dev_tree_t *tree, dev_node_t *dev)
+{
+	driver_t *drv = dev->drv;
+	
+	assert(drv != NULL);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "detach_driver(dev=\"%s\",drv=\"%s\")",
+	    dev->pfun->pathname, drv->name);
+	
+	fibril_mutex_lock(&drv->driver_mutex);
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	dev->drv = NULL;
+	list_remove(&dev->driver_devices);
+	
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	fibril_mutex_unlock(&drv->driver_mutex);
+}
+
+/** Start a driver
+ *
+ * @param drv		The driver's structure.
+ * @return		True if the driver's task is successfully spawned, false
+ *			otherwise.
+ */
+bool start_driver(driver_t *drv)
+{
+	int rc;
+
+	assert(fibril_mutex_is_locked(&drv->driver_mutex));
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "start_driver(drv=\"%s\")", drv->name);
+	
+	rc = task_spawnl(NULL, drv->binary_path, drv->binary_path, NULL);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Spawning driver `%s' (%s) failed: %s.",
+		    drv->name, drv->binary_path, str_error(rc));
+		return false;
+	}
+	
+	drv->state = DRIVER_STARTING;
+	return true;
+}
+
+/** Find device driver by handle.
+ *
+ * @param drv_list	The list of device drivers
+ * @param handle	Driver handle
+ * @return		The device driver, if it is in the list,
+ *			NULL otherwise.
+ */
+driver_t *driver_find(driver_list_t *drv_list, devman_handle_t handle)
+{
+	driver_t *res = NULL;
+	
+	fibril_mutex_lock(&drv_list->drivers_mutex);
+	
+	list_foreach(drv_list->drivers, drivers, driver_t, drv) {
+		if (drv->handle == handle) {
+			res = drv;
+			break;
+		}
+	}
+	
+	fibril_mutex_unlock(&drv_list->drivers_mutex);
+	
+	return res;
+}
+
+
+/** Find device driver by name.
+ *
+ * @param drv_list	The list of device drivers.
+ * @param drv_name	The name of the device driver which is searched.
+ * @return		The device driver of the specified name, if it is in the
+ *			list, NULL otherwise.
+ */
+driver_t *driver_find_by_name(driver_list_t *drv_list, const char *drv_name)
+{
+	driver_t *res = NULL;
+	
+	fibril_mutex_lock(&drv_list->drivers_mutex);
+	
+	list_foreach(drv_list->drivers, drivers, driver_t, drv) {
+		if (str_cmp(drv->name, drv_name) == 0) {
+			res = drv;
+			break;
+		}
+	}
+	
+	fibril_mutex_unlock(&drv_list->drivers_mutex);
+	
+	return res;
+}
+
+/** Notify driver about the devices to which it was assigned.
+ *
+ * @param driver	The driver to which the devices are passed.
+ */
+static void pass_devices_to_driver(driver_t *driver, dev_tree_t *tree)
+{
+	dev_node_t *dev;
+	link_t *link;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver(driver=\"%s\")",
+	    driver->name);
+
+	fibril_mutex_lock(&driver->driver_mutex);
+
+	/*
+	 * Go through devices list as long as there is some device
+	 * that has not been passed to the driver.
+	 */
+	link = driver->devices.head.next;
+	while (link != &driver->devices.head) {
+		dev = list_get_instance(link, dev_node_t, driver_devices);
+		fibril_rwlock_write_lock(&tree->rwlock);
+		
+		if (dev->passed_to_driver) {
+			fibril_rwlock_write_unlock(&tree->rwlock);
+			link = link->next;
+			continue;
+		}
+
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver: dev->refcnt=%d\n",
+		    (int)atomic_get(&dev->refcnt));
+		dev_add_ref(dev);
+
+		/*
+		 * Unlock to avoid deadlock when adding device
+		 * handled by itself.
+		 */
+		fibril_mutex_unlock(&driver->driver_mutex);
+		fibril_rwlock_write_unlock(&tree->rwlock);
+
+		add_device(driver, dev, tree);
+
+		dev_del_ref(dev);
+
+		/*
+		 * Lock again as we will work with driver's
+		 * structure.
+		 */
+		fibril_mutex_lock(&driver->driver_mutex);
+
+		/*
+		 * Restart the cycle to go through all devices again.
+		 */
+		link = driver->devices.head.next;
+	}
+
+	/*
+	 * Once we passed all devices to the driver, we need to mark the
+	 * driver as running.
+	 * It is vital to do it here and inside critical section.
+	 *
+	 * If we would change the state earlier, other devices added to
+	 * the driver would be added to the device list and started
+	 * immediately and possibly started here as well.
+	 */
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' enters running state.", driver->name);
+	driver->state = DRIVER_RUNNING;
+
+	fibril_mutex_unlock(&driver->driver_mutex);
+}
+
+/** Finish the initialization of a driver after it has succesfully started
+ * and after it has registered itself by the device manager.
+ *
+ * Pass devices formerly matched to the driver to the driver and remember the
+ * driver is running and fully functional now.
+ *
+ * @param driver	The driver which registered itself as running by the
+ *			device manager.
+ */
+void initialize_running_driver(driver_t *driver, dev_tree_t *tree)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "initialize_running_driver(driver=\"%s\")",
+	    driver->name);
+	
+	/*
+	 * Pass devices which have been already assigned to the driver to the
+	 * driver.
+	 */
+	pass_devices_to_driver(driver, tree);
+}
+
+/** Initialize device driver structure.
+ *
+ * @param drv		The device driver structure.
+ */
+void init_driver(driver_t *drv)
+{
+	assert(drv != NULL);
+
+	memset(drv, 0, sizeof(driver_t));
+	list_initialize(&drv->match_ids.ids);
+	list_initialize(&drv->devices);
+	fibril_mutex_initialize(&drv->driver_mutex);
+	drv->sess = NULL;
+}
+
+/** Device driver structure clean-up.
+ *
+ * @param drv		The device driver structure.
+ */
+void clean_driver(driver_t *drv)
+{
+	assert(drv != NULL);
+
+	free(drv->name);
+	free(drv->binary_path);
+
+	clean_match_ids(&drv->match_ids);
+
+	init_driver(drv);
+}
+
+/** Delete device driver structure.
+ *
+ * @param drv		The device driver structure.
+ */
+void delete_driver(driver_t *drv)
+{
+	assert(drv != NULL);
+	
+	clean_driver(drv);
+	free(drv);
+}
+
+/** Find suitable driver for a device and assign the driver to it.
+ *
+ * @param node		The device node of the device in the device tree.
+ * @param drivers_list	The list of available drivers.
+ * @return		True if the suitable driver is found and
+ *			successfully assigned to the device, false otherwise.
+ */
+bool assign_driver(dev_node_t *dev, driver_list_t *drivers_list,
+    dev_tree_t *tree)
+{
+	assert(dev != NULL);
+	assert(drivers_list != NULL);
+	assert(tree != NULL);
+	
+	/*
+	 * Find the driver which is the most suitable for handling this device.
+	 */
+	driver_t *drv = find_best_match_driver(drivers_list, dev);
+	if (drv == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "No driver found for device `%s'.",
+		    dev->pfun->pathname);
+		return false;
+	}
+	
+	/* Attach the driver to the device. */
+	attach_driver(tree, dev, drv);
+	
+	fibril_mutex_lock(&drv->driver_mutex);
+	if (drv->state == DRIVER_NOT_STARTED) {
+		/* Start the driver. */
+		start_driver(drv);
+	}
+	bool is_running = drv->state == DRIVER_RUNNING;
+	fibril_mutex_unlock(&drv->driver_mutex);
+
+	/* Notify the driver about the new device. */
+	if (is_running)
+		add_device(drv, dev, tree);
+	
+	fibril_mutex_lock(&drv->driver_mutex);
+	fibril_mutex_unlock(&drv->driver_mutex);
+
+	fibril_rwlock_write_lock(&tree->rwlock);
+	if (dev->pfun != NULL) {
+		dev->pfun->state = FUN_ON_LINE;
+	}
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	return true;
+}
+
+/** Pass a device to running driver.
+ *
+ * @param drv		The driver's structure.
+ * @param node		The device's node in the device tree.
+ */
+void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
+{
+	/*
+	 * We do not expect to have driver's mutex locked as we do not
+	 * access any structures that would affect driver_t.
+	 */
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
+	    drv->name, dev->pfun->name);
+	
+	/* Send the device to the driver. */
+	devman_handle_t parent_handle;
+	if (dev->pfun) {
+		parent_handle = dev->pfun->handle;
+	} else {
+		parent_handle = 0;
+	}
+	
+	async_exch_t *exch = async_exchange_begin(drv->sess);
+	
+	ipc_call_t answer;
+	aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
+	    parent_handle, &answer);
+	
+	/* Send the device name to the driver. */
+	sysarg_t rc = async_data_write_start(exch, dev->pfun->name,
+	    str_size(dev->pfun->name) + 1);
+	
+	async_exchange_end(exch);
+	
+	if (rc != EOK) {
+		/* TODO handle error */
+	}
+
+	/* Wait for answer from the driver. */
+	async_wait_for(req, &rc);
+
+	switch(rc) {
+	case EOK:
+		dev->state = DEVICE_USABLE;
+		break;
+	case ENOENT:
+		dev->state = DEVICE_NOT_PRESENT;
+		break;
+	default:
+		dev->state = DEVICE_INVALID;
+		break;
+	}
+	
+	dev->passed_to_driver = true;
+
+	return;
+}
+
+int driver_dev_remove(dev_tree_t *tree, dev_node_t *dev)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	driver_t *drv;
+	devman_handle_t handle;
+	
+	assert(dev != NULL);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_remove(%p)", dev);
+	
+	fibril_rwlock_read_lock(&tree->rwlock);
+	drv = dev->drv;
+	handle = dev->handle;
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	exch = async_exchange_begin(drv->sess);
+	retval = async_req_1_0(exch, DRIVER_DEV_REMOVE, handle);
+	async_exchange_end(exch);
+	
+	return retval;
+}
+
+int driver_dev_gone(dev_tree_t *tree, dev_node_t *dev)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	driver_t *drv;
+	devman_handle_t handle;
+	
+	assert(dev != NULL);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_gone(%p)", dev);
+	
+	fibril_rwlock_read_lock(&tree->rwlock);
+	drv = dev->drv;
+	handle = dev->handle;
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	exch = async_exchange_begin(drv->sess);
+	retval = async_req_1_0(exch, DRIVER_DEV_GONE, handle);
+	async_exchange_end(exch);
+	
+	return retval;
+}
+
+int driver_fun_online(dev_tree_t *tree, fun_node_t *fun)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	driver_t *drv;
+	devman_handle_t handle;
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_online(%p)", fun);
+
+	fibril_rwlock_read_lock(&tree->rwlock);
+	
+	if (fun->dev == NULL) {
+		/* XXX root function? */
+		fibril_rwlock_read_unlock(&tree->rwlock);
+		return EINVAL;
+	}
+	
+	drv = fun->dev->drv;
+	handle = fun->handle;
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	exch = async_exchange_begin(drv->sess);
+	retval = async_req_1_0(exch, DRIVER_FUN_ONLINE, handle);
+	loc_exchange_end(exch);
+	
+	return retval;
+}
+
+int driver_fun_offline(dev_tree_t *tree, fun_node_t *fun)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	driver_t *drv;
+	devman_handle_t handle;
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_offline(%p)", fun);
+
+	fibril_rwlock_read_lock(&tree->rwlock);
+	if (fun->dev == NULL) {
+		/* XXX root function? */
+		fibril_rwlock_read_unlock(&tree->rwlock);
+		return EINVAL;
+	}
+	
+	drv = fun->dev->drv;
+	handle = fun->handle;
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	exch = async_exchange_begin(drv->sess);
+	retval = async_req_1_0(exch, DRIVER_FUN_OFFLINE, handle);
+	loc_exchange_end(exch);
+	
+	return retval;
+
+}
+
+/** Get list of registered drivers. */
+int driver_get_list(driver_list_t *driver_list, devman_handle_t *hdl_buf,
+    size_t buf_size, size_t *act_size)
+{
+	size_t act_cnt;
+	size_t buf_cnt;
+
+	fibril_mutex_lock(&driver_list->drivers_mutex);
+
+	buf_cnt = buf_size / sizeof(devman_handle_t);
+
+	act_cnt = list_count(&driver_list->drivers);
+	*act_size = act_cnt * sizeof(devman_handle_t);
+
+	if (buf_size % sizeof(devman_handle_t) != 0) {
+		fibril_mutex_unlock(&driver_list->drivers_mutex);
+		return EINVAL;
+	}
+
+	size_t pos = 0;
+	list_foreach(driver_list->drivers, drivers, driver_t, drv) {
+		if (pos < buf_cnt) {
+			hdl_buf[pos] = drv->handle;
+		}
+
+		pos++;
+	}
+
+	fibril_mutex_unlock(&driver_list->drivers_mutex);
+	return EOK;
+}
+
+/** Get list of device functions. */
+int driver_get_devices(driver_t *driver, devman_handle_t *hdl_buf,
+    size_t buf_size, size_t *act_size)
+{
+	size_t act_cnt;
+	size_t buf_cnt;
+
+	fibril_mutex_lock(&driver->driver_mutex);
+
+	buf_cnt = buf_size / sizeof(devman_handle_t);
+
+	act_cnt = list_count(&driver->devices);
+	*act_size = act_cnt * sizeof(devman_handle_t);
+
+	if (buf_size % sizeof(devman_handle_t) != 0) {
+		fibril_mutex_unlock(&driver->driver_mutex);
+		return EINVAL;
+	}
+
+	size_t pos = 0;
+	list_foreach(driver->devices, driver_devices, dev_node_t, dev) {
+		if (pos < buf_cnt) {
+			hdl_buf[pos] = dev->handle;
+		}
+
+		pos++;
+	}
+
+	fibril_mutex_unlock(&driver->driver_mutex);
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/devman/driver.h
===================================================================
--- uspace/srv/devman/driver.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/driver.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#ifndef DRIVER_H_
+#define DRIVER_H_
+
+#include <stdbool.h>
+#include "devman.h"
+
+extern void init_driver_list(driver_list_t *);
+extern driver_t *create_driver(void);
+extern bool get_driver_info(const char *, const char *, driver_t *);
+extern int lookup_available_drivers(driver_list_t *, const char *);
+
+extern driver_t *find_best_match_driver(driver_list_t *, dev_node_t *);
+extern bool assign_driver(dev_node_t *, driver_list_t *, dev_tree_t *);
+
+extern void add_driver(driver_list_t *, driver_t *);
+extern void attach_driver(dev_tree_t *, dev_node_t *, driver_t *);
+extern void detach_driver(dev_tree_t *, dev_node_t *);
+extern bool start_driver(driver_t *);
+extern void add_device(driver_t *, dev_node_t *, dev_tree_t *);
+extern int driver_dev_remove(dev_tree_t *, dev_node_t *);
+extern int driver_dev_gone(dev_tree_t *, dev_node_t *);
+extern int driver_fun_online(dev_tree_t *, fun_node_t *);
+extern int driver_fun_offline(dev_tree_t *, fun_node_t *);
+
+extern driver_t *driver_find(driver_list_t *, devman_handle_t);
+extern driver_t *driver_find_by_name(driver_list_t *, const char *);
+extern void initialize_running_driver(driver_t *, dev_tree_t *);
+
+extern void init_driver(driver_t *);
+extern void clean_driver(driver_t *);
+extern void delete_driver(driver_t *);
+extern int driver_get_list(driver_list_t *, devman_handle_t *, size_t, size_t *);
+extern int driver_get_devices(driver_t *, devman_handle_t *, size_t, size_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/drv_conn.c
===================================================================
--- uspace/srv/devman/drv_conn.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/drv_conn.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,655 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @defgroup devman Device manager.
+ * @brief HelenOS device manager.
+ * @{
+ */
+
+/** @file
+ */
+
+#include <assert.h>
+#include <ipc/services.h>
+#include <ns.h>
+#include <async.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+#include <stdbool.h>
+#include <fibril_synch.h>
+#include <stdlib.h>
+#include <str.h>
+#include <io/log.h>
+#include <ipc/devman.h>
+#include <ipc/driver.h>
+#include <loc.h>
+
+#include "client_conn.h"
+#include "dev.h"
+#include "devman.h"
+#include "devtree.h"
+#include "driver.h"
+#include "drv_conn.h"
+#include "fun.h"
+#include "loc.h"
+#include "main.h"
+
+static int init_running_drv(void *drv);
+
+/** Register running driver. */
+static driver_t *devman_driver_register(ipc_callid_t callid, ipc_call_t *call)
+{
+	driver_t *driver = NULL;
+	char *drv_name = NULL;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_driver_register");
+	
+	/* Get driver name. */
+	int rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return NULL;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s' driver is trying to register.",
+	    drv_name);
+	
+	/* Find driver structure. */
+	driver = driver_find_by_name(&drivers_list, drv_name);
+	if (driver == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "No driver named `%s' was found.", drv_name);
+		free(drv_name);
+		drv_name = NULL;
+		async_answer_0(callid, ENOENT);
+		return NULL;
+	}
+	
+	free(drv_name);
+	drv_name = NULL;
+	
+	fibril_mutex_lock(&driver->driver_mutex);
+	
+	if (driver->sess) {
+		/* We already have a connection to the driver. */
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Driver '%s' already started.\n",
+		    driver->name);
+		fibril_mutex_unlock(&driver->driver_mutex);
+		async_answer_0(callid, EEXISTS);
+		return NULL;
+	}
+	
+	switch (driver->state) {
+	case DRIVER_NOT_STARTED:
+		/* Somebody started the driver manually. */
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Driver '%s' started manually.\n",
+		    driver->name);
+		driver->state = DRIVER_STARTING;
+		break;
+	case DRIVER_STARTING:
+		/* The expected case */
+		break;
+	case DRIVER_RUNNING:
+		/* Should not happen since we do not have a connected session */
+		assert(false);
+	}
+	
+	/* Create connection to the driver. */
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Creating connection to the `%s' driver.",
+	    driver->name);
+	driver->sess = async_callback_receive(EXCHANGE_PARALLEL);
+	if (!driver->sess) {
+		fibril_mutex_unlock(&driver->driver_mutex);
+		async_answer_0(callid, ENOTSUP);
+		return NULL;
+	}
+	/* FIXME: Work around problem with callback sessions */
+	async_sess_args_set(driver->sess, DRIVER_DEVMAN, 0, 0);
+	
+	log_msg(LOG_DEFAULT, LVL_NOTE,
+	    "The `%s' driver was successfully registered as running.",
+	    driver->name);
+	
+	/*
+	 * Initialize the driver as running (e.g. pass assigned devices to it)
+	 * in a separate fibril; the separate fibril is used to enable the
+	 * driver to use devman service during the driver's initialization.
+	 */
+	fid_t fid = fibril_create(init_running_drv, driver);
+	if (fid == 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create initialization fibril " \
+		    "for driver `%s'.", driver->name);
+		fibril_mutex_unlock(&driver->driver_mutex);
+		async_answer_0(callid, ENOMEM);
+		return NULL;
+	}
+	
+	fibril_add_ready(fid);
+	fibril_mutex_unlock(&driver->driver_mutex);
+	
+	async_answer_0(callid, EOK);
+	return driver;
+}
+
+/** Receive device match ID from the device's parent driver and add it to the
+ * list of devices match ids.
+ *
+ * @param match_ids	The list of the device's match ids.
+ * @return		Zero on success, negative error code otherwise.
+ */
+static int devman_receive_match_id(match_id_list_t *match_ids)
+{
+	match_id_t *match_id = create_match_id();
+	ipc_callid_t callid;
+	ipc_call_t call;
+	int rc = 0;
+	
+	callid = async_get_call(&call);
+	if (DEVMAN_ADD_MATCH_ID != IPC_GET_IMETHOD(call)) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, 
+		    "Invalid protocol when trying to receive match id.");
+		async_answer_0(callid, EINVAL); 
+		delete_match_id(match_id);
+		return EINVAL;
+	}
+	
+	if (match_id == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate match id.");
+		async_answer_0(callid, ENOMEM);
+		return ENOMEM;
+	}
+	
+	async_answer_0(callid, EOK);
+	
+	match_id->score = IPC_GET_ARG1(call);
+	
+	char *match_id_str;
+	rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
+	match_id->id = match_id_str;
+	if (rc != EOK) {
+		delete_match_id(match_id);
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to receive match id string: %s.",
+		    str_error(rc));
+		return rc;
+	}
+	
+	list_append(&match_id->link, &match_ids->ids);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Received match id `%s', score %d.",
+	    match_id->id, match_id->score);
+	return rc;
+}
+
+/** Receive device match IDs from the device's parent driver and add them to the
+ * list of devices match ids.
+ *
+ * @param match_count	The number of device's match ids to be received.
+ * @param match_ids	The list of the device's match ids.
+ * @return		Zero on success, negative error code otherwise.
+ */
+static int devman_receive_match_ids(sysarg_t match_count,
+    match_id_list_t *match_ids)
+{
+	int ret = EOK;
+	size_t i;
+	
+	for (i = 0; i < match_count; i++) {
+		if (EOK != (ret = devman_receive_match_id(match_ids)))
+			return ret;
+	}
+	return ret;
+}
+
+/** Handle function registration.
+ *
+ * Child devices are registered by their parent's device driver.
+ */
+static void devman_add_function(ipc_callid_t callid, ipc_call_t *call)
+{
+	fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call);
+	devman_handle_t dev_handle = IPC_GET_ARG2(*call);
+	sysarg_t match_count = IPC_GET_ARG3(*call);
+	dev_tree_t *tree = &device_tree;
+	
+	dev_node_t *pdev = find_dev_node(&device_tree, dev_handle);
+	if (pdev == NULL) {
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	if (ftype != fun_inner && ftype != fun_exposed) {
+		/* Unknown function type */
+		log_msg(LOG_DEFAULT, LVL_ERROR, 
+		    "Unknown function type %d provided by driver.",
+		    (int) ftype);
+
+		dev_del_ref(pdev);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	char *fun_name = NULL;
+	int rc = async_data_write_accept((void **)&fun_name, true, 0, 0, 0, 0);
+	if (rc != EOK) {
+		dev_del_ref(pdev);
+		async_answer_0(callid, rc);
+		return;
+	}
+	
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	/* Check device state */
+	if (pdev->state == DEVICE_REMOVED) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		dev_del_ref(pdev);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	/* Check that function with same name is not there already. */
+	fun_node_t *tfun = find_fun_node_in_device(tree, pdev, fun_name);
+	if (tfun) {
+		fun_del_ref(tfun);	/* drop the new unwanted reference */
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		dev_del_ref(pdev);
+		async_answer_0(callid, EEXISTS);
+		printf(NAME ": Warning, driver tried to register `%s' twice.\n",
+		    fun_name);
+		free(fun_name);
+		return;
+	}
+	
+	fun_node_t *fun = create_fun_node();
+	/* One reference for creation, one for us */
+	fun_add_ref(fun);
+	fun_add_ref(fun);
+	fun->ftype = ftype;
+	
+	/*
+	 * We can lock the function here even when holding the tree because
+	 * we know it cannot be held by anyone else yet.
+	 */
+	fun_busy_lock(fun);
+	
+	if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		dev_del_ref(pdev);
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		delete_fun_node(fun);
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+	
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	dev_del_ref(pdev);
+	
+	devman_receive_match_ids(match_count, &fun->match_ids);
+	
+	rc = fun_online(fun);
+	if (rc != EOK) {
+		/* XXX Set some failed state? */
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		async_answer_0(callid, rc);
+		return;
+	}
+	
+	fun_busy_unlock(fun);
+	fun_del_ref(fun);
+	
+	/* Return device handle to parent's driver. */
+	async_answer_1(callid, EOK, fun->handle);
+}
+
+static void devman_add_function_to_cat(ipc_callid_t callid, ipc_call_t *call)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*call);
+	category_id_t cat_id;
+	int rc;
+	
+	/* Get category name. */
+	char *cat_name;
+	rc = async_data_write_accept((void **) &cat_name, true,
+	    0, 0, 0, 0);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+	
+	fun_node_t *fun = find_fun_node(&device_tree, handle);
+	if (fun == NULL) {
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
+	if (rc == EOK) {
+		loc_service_add_to_cat(fun->service_id, cat_id);
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Function `%s' added to category `%s'.",
+		    fun->pathname, cat_name);
+	} else {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding function `%s' to category "
+		    "`%s'.", fun->pathname, cat_name);
+	}
+	
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	fun_del_ref(fun);
+	
+	async_answer_0(callid, rc);
+}
+
+/** Online function by driver request.
+ *
+ */
+static void devman_drv_fun_online(ipc_callid_t iid, ipc_call_t *icall,
+    driver_t *drv)
+{
+	fun_node_t *fun;
+	int rc;
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_drv_fun_online()");
+	
+	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fun_busy_lock(fun);
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	if (fun->dev == NULL || fun->dev->drv != drv) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	
+	rc = fun_offline(fun);
+	if (rc != EOK) {
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		async_answer_0(iid, (sysarg_t) rc);
+		return;
+	}
+	
+	fun_busy_unlock(fun);
+	fun_del_ref(fun);
+	
+	async_answer_0(iid, (sysarg_t) EOK);
+}
+
+
+/** Offline function by driver request.
+ *
+ */
+static void devman_drv_fun_offline(ipc_callid_t iid, ipc_call_t *icall,
+    driver_t *drv)
+{
+	fun_node_t *fun;
+	int rc;
+
+	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fun_busy_lock(fun);
+	
+	fibril_rwlock_write_lock(&device_tree.rwlock);
+	if (fun->dev == NULL || fun->dev->drv != drv) {
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	fibril_rwlock_write_unlock(&device_tree.rwlock);
+	
+	rc = fun_offline(fun);
+	if (rc != EOK) {
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		async_answer_0(iid, (sysarg_t) rc);
+		return;
+	}
+	
+	fun_busy_unlock(fun);
+	fun_del_ref(fun);
+	async_answer_0(iid, (sysarg_t) EOK);
+}
+
+/** Remove function. */
+static void devman_remove_function(ipc_callid_t callid, ipc_call_t *call)
+{
+	devman_handle_t fun_handle = IPC_GET_ARG1(*call);
+	dev_tree_t *tree = &device_tree;
+	int rc;
+	
+	fun_node_t *fun = find_fun_node(&device_tree, fun_handle);
+	if (fun == NULL) {
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	fun_busy_lock(fun);
+	
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
+	
+	/* Check function state */
+	if (fun->state == FUN_REMOVED) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		fun_busy_unlock(fun);
+		fun_del_ref(fun);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	if (fun->ftype == fun_inner) {
+		/* This is a surprise removal. Handle possible descendants */
+		if (fun->child != NULL) {
+			dev_node_t *dev = fun->child;
+			device_state_t dev_state;
+			int gone_rc;
+			
+			dev_add_ref(dev);
+			dev_state = dev->state;
+			
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
+			
+			/* If device is owned by driver, inform driver it is gone. */
+			if (dev_state == DEVICE_USABLE)
+				gone_rc = driver_dev_gone(&device_tree, dev);
+			else
+				gone_rc = EOK;
+			
+			fibril_rwlock_read_lock(&device_tree.rwlock);
+			
+			/* Verify that driver succeeded and removed all functions */
+			if (gone_rc != EOK || !list_empty(&dev->functions)) {
+				log_msg(LOG_DEFAULT, LVL_ERROR, "Driver did not remove "
+				    "functions for device that is gone. "
+				    "Device node is now defunct.");
+				
+				/*
+				 * Not much we can do but mark the device
+				 * node as having invalid state. This
+				 * is a driver bug.
+				 */
+				dev->state = DEVICE_INVALID;
+				fibril_rwlock_read_unlock(&device_tree.rwlock);
+				dev_del_ref(dev);
+				if (gone_rc == EOK)
+					gone_rc = ENOTSUP;
+				fun_busy_unlock(fun);
+				fun_del_ref(fun);
+				async_answer_0(callid, gone_rc);
+				return;
+			}
+			
+			driver_t *driver = dev->drv;
+			fibril_rwlock_read_unlock(&device_tree.rwlock);
+			
+			if (driver)
+				detach_driver(&device_tree, dev);
+			
+			fibril_rwlock_write_lock(&device_tree.rwlock);
+			remove_dev_node(&device_tree, dev);
+			
+			/* Delete ref created when node was inserted */
+			dev_del_ref(dev);
+			/* Delete ref created by dev_add_ref(dev) above */
+			dev_del_ref(dev);
+		}
+	} else {
+		if (fun->service_id != 0) {
+			/* Unregister from location service */
+			rc = loc_service_unregister(fun->service_id);
+			if (rc != EOK) {
+				log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree "
+				    "service.");
+				fibril_rwlock_write_unlock(&tree->rwlock);
+				fun_busy_unlock(fun);
+				fun_del_ref(fun);
+				async_answer_0(callid, EIO);
+				return;
+			}
+		}
+	}
+	
+	remove_fun_node(&device_tree, fun);
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	fun_busy_unlock(fun);
+	
+	/* Delete ref added when inserting function into tree */
+	fun_del_ref(fun);
+	/* Delete ref added above when looking up function */
+	fun_del_ref(fun);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function() succeeded.");
+	async_answer_0(callid, EOK);
+}
+
+/** Initialize driver which has registered itself as running and ready.
+ *
+ * The initialization is done in a separate fibril to avoid deadlocks (if the
+ * driver needed to be served by devman during the driver's initialization).
+ */
+static int init_running_drv(void *drv)
+{
+	driver_t *driver = (driver_t *) drv;
+	
+	initialize_running_driver(driver, &device_tree);
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s` driver was successfully initialized.",
+	    driver->name);
+	return 0;
+}
+
+/** Function for handling connections from a driver to the device manager. */
+void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
+{
+	client_t *client;
+	driver_t *driver = NULL;
+	
+	/* Accept the connection. */
+	async_answer_0(iid, EOK);
+	
+	client = async_get_client_data();
+	if (client == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate client data.");
+		return;
+	}
+	
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call))
+			break;
+		
+		if (IPC_GET_IMETHOD(call) != DEVMAN_DRIVER_REGISTER) {
+			fibril_mutex_lock(&client->mutex);
+			driver = client->driver;
+			fibril_mutex_unlock(&client->mutex);
+			if (driver == NULL) {
+				/* First call must be to DEVMAN_DRIVER_REGISTER */
+				async_answer_0(callid, ENOTSUP);
+				continue;
+			}
+		}
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case DEVMAN_DRIVER_REGISTER:
+			fibril_mutex_lock(&client->mutex);
+			if (client->driver != NULL) {
+				fibril_mutex_unlock(&client->mutex);
+				async_answer_0(callid, EINVAL);
+				continue;
+			}
+			client->driver = devman_driver_register(callid, &call);
+			fibril_mutex_unlock(&client->mutex);
+			break;
+		case DEVMAN_ADD_FUNCTION:
+			devman_add_function(callid, &call);
+			break;
+		case DEVMAN_ADD_DEVICE_TO_CATEGORY:
+			devman_add_function_to_cat(callid, &call);
+			break;
+		case DEVMAN_DRV_FUN_ONLINE:
+			devman_drv_fun_online(callid, &call, driver);
+			break;
+		case DEVMAN_DRV_FUN_OFFLINE:
+			devman_drv_fun_offline(callid, &call, driver);
+			break;
+		case DEVMAN_REMOVE_FUNCTION:
+			devman_remove_function(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, EINVAL);
+			break;
+		}
+	}
+}
+
+/** @}
+ */
Index: uspace/srv/devman/drv_conn.h
===================================================================
--- uspace/srv/devman/drv_conn.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/drv_conn.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#ifndef DRV_CONN_H_
+#define DRV_CONN_H_
+
+#include <async.h>
+#include "devman.h"
+
+extern void devman_connection_driver(ipc_callid_t, ipc_call_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/fun.c
===================================================================
--- uspace/srv/devman/fun.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/fun.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#include <errno.h>
+#include <io/log.h>
+#include <loc.h>
+
+#include "dev.h"
+#include "devman.h"
+#include "devtree.h"
+#include "driver.h"
+#include "fun.h"
+#include "main.h"
+#include "loc.h"
+
+static fun_node_t *find_node_child(dev_tree_t *, fun_node_t *, const char *);
+
+/* Function nodes */
+
+/** Create a new function node.
+ *
+ * @return		A function node structure.
+ */
+fun_node_t *create_fun_node(void)
+{
+	fun_node_t *fun;
+
+	fun = calloc(1, sizeof(fun_node_t));
+	if (fun == NULL)
+		return NULL;
+	
+	fun->state = FUN_INIT;
+	atomic_set(&fun->refcnt, 0);
+	fibril_mutex_initialize(&fun->busy_lock);
+	link_initialize(&fun->dev_functions);
+	list_initialize(&fun->match_ids.ids);
+	
+	return fun;
+}
+
+/** Delete a function node.
+ *
+ * @param fun		The device node structure.
+ */
+void delete_fun_node(fun_node_t *fun)
+{
+	assert(fun->dev == NULL);
+	assert(fun->child == NULL);
+	
+	clean_match_ids(&fun->match_ids);
+	free(fun->name);
+	free(fun->pathname);
+	free(fun);
+}
+
+/** Increase function node reference count.
+ *
+ * @param fun	Function node
+ */
+void fun_add_ref(fun_node_t *fun)
+{
+	atomic_inc(&fun->refcnt);
+}
+
+/** Decrease function node reference count.
+ *
+ * When the count drops to zero the function node is freed.
+ *
+ * @param fun	Function node
+ */
+void fun_del_ref(fun_node_t *fun)
+{
+	if (atomic_predec(&fun->refcnt) == 0)
+		delete_fun_node(fun);
+}
+
+/** Make function busy for reconfiguration operations. */
+void fun_busy_lock(fun_node_t *fun)
+{
+	fibril_mutex_lock(&fun->busy_lock);
+}
+
+/** Mark end of reconfiguration operation. */
+void fun_busy_unlock(fun_node_t *fun)
+{
+	fibril_mutex_unlock(&fun->busy_lock);
+}
+
+/** Find the function node with the specified handle.
+ *
+ * @param tree		The device tree where we look for the device node.
+ * @param handle	The handle of the function.
+ * @return		The function node.
+ */
+fun_node_t *find_fun_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
+{
+	fun_node_t *fun;
+	
+	assert(fibril_rwlock_is_locked(&tree->rwlock));
+	
+	ht_link_t *link = hash_table_find(&tree->devman_functions, &handle);
+	if (link == NULL)
+		return NULL;
+	
+	fun = hash_table_get_inst(link, fun_node_t, devman_fun);
+	
+	return fun;
+}
+
+/** Find the function node with the specified handle.
+ *
+ * @param tree		The device tree where we look for the device node.
+ * @param handle	The handle of the function.
+ * @return		The function node.
+ */
+fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle)
+{
+	fun_node_t *fun = NULL;
+	
+	fibril_rwlock_read_lock(&tree->rwlock);
+	
+	fun = find_fun_node_no_lock(tree, handle);
+	if (fun != NULL)
+		fun_add_ref(fun);
+	
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	return fun;
+}
+
+/** Create and set device's full path in device tree.
+ *
+ * @param tree		Device tree
+ * @param node		The device's device node.
+ * @param parent	The parent device node.
+ * @return		True on success, false otherwise (insufficient
+ *			resources etc.).
+ */
+bool set_fun_path(dev_tree_t *tree, fun_node_t *fun, fun_node_t *parent)
+{
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	assert(fun->name != NULL);
+	
+	size_t pathsize = (str_size(fun->name) + 1);
+	if (parent != NULL)
+		pathsize += str_size(parent->pathname) + 1;
+	
+	fun->pathname = (char *) malloc(pathsize);
+	if (fun->pathname == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate device path.");
+		return false;
+	}
+	
+	if (parent != NULL) {
+		str_cpy(fun->pathname, pathsize, parent->pathname);
+		str_append(fun->pathname, pathsize, "/");
+		str_append(fun->pathname, pathsize, fun->name);
+	} else {
+		str_cpy(fun->pathname, pathsize, fun->name);
+	}
+	
+	return true;
+}
+
+/** Find function node with a specified path in the device tree.
+ * 
+ * @param path		The path of the function node in the device tree.
+ * @param tree		The device tree.
+ * @return		The function node if it is present in the tree, NULL
+ *			otherwise.
+ */
+fun_node_t *find_fun_node_by_path(dev_tree_t *tree, char *path)
+{
+	assert(path != NULL);
+
+	bool is_absolute = path[0] == '/';
+	if (!is_absolute) {
+		return NULL;
+	}
+
+	fibril_rwlock_read_lock(&tree->rwlock);
+	
+	fun_node_t *fun = tree->root_node;
+	fun_add_ref(fun);
+	/*
+	 * Relative path to the function from its parent (but with '/' at the
+	 * beginning)
+	 */
+	char *rel_path = path;
+	char *next_path_elem = NULL;
+	bool cont = (rel_path[1] != '\0');
+	
+	while (cont && fun != NULL) {
+		next_path_elem  = get_path_elem_end(rel_path + 1);
+		if (next_path_elem[0] == '/') {
+			cont = true;
+			next_path_elem[0] = 0;
+		} else {
+			cont = false;
+		}
+		
+		fun_node_t *cfun = find_node_child(tree, fun, rel_path + 1);
+		fun_del_ref(fun);
+		fun = cfun;
+		
+		if (cont) {
+			/* Restore the original path. */
+			next_path_elem[0] = '/';
+		}
+		rel_path = next_path_elem;
+	}
+	
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	return fun;
+}
+
+/** Find function with a specified name belonging to given device.
+ *
+ * Device tree rwlock should be held at least for reading.
+ *
+ * @param tree Device tree
+ * @param dev Device the function belongs to.
+ * @param name Function name (not path).
+ * @return Function node.
+ * @retval NULL No function with given name.
+ */
+fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *dev,
+    const char *name)
+{
+	assert(name != NULL);
+	assert(fibril_rwlock_is_locked(&tree->rwlock));
+
+	list_foreach(dev->functions, dev_functions, fun_node_t, fun) {
+		if (str_cmp(name, fun->name) == 0) {
+			fun_add_ref(fun);
+			return fun;
+		}
+	}
+
+	return NULL;
+}
+
+/** Find child function node with a specified name.
+ *
+ * Device tree rwlock should be held at least for reading.
+ *
+ * @param tree		Device tree
+ * @param parent	The parent function node.
+ * @param name		The name of the child function.
+ * @return		The child function node.
+ */
+static fun_node_t *find_node_child(dev_tree_t *tree, fun_node_t *pfun,
+    const char *name)
+{
+	return find_fun_node_in_device(tree, pfun->child, name);
+}
+
+static int assign_driver_fibril(void *arg)
+{
+	dev_node_t *dev_node = (dev_node_t *) arg;
+	assign_driver(dev_node, &drivers_list, &device_tree);
+
+	/* Delete one reference we got from the caller. */
+	dev_del_ref(dev_node);
+	return EOK;
+}
+
+int fun_online(fun_node_t *fun)
+{
+	dev_node_t *dev;
+	
+	fibril_rwlock_write_lock(&device_tree.rwlock);
+	
+	if (fun->state == FUN_ON_LINE) {
+		fibril_rwlock_write_unlock(&device_tree.rwlock);
+		log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already on line.",
+		    fun->pathname);
+		return EOK;
+	}
+	
+	if (fun->ftype == fun_inner) {
+		dev = create_dev_node();
+		if (dev == NULL) {
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
+			return ENOMEM;
+		}
+		
+		insert_dev_node(&device_tree, dev, fun);
+		dev_add_ref(dev);
+	}
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
+	
+	if (fun->ftype == fun_inner) {
+		dev = fun->child;
+		assert(dev != NULL);
+		
+		/* Give one reference over to assign_driver_fibril(). */
+		dev_add_ref(dev);
+		
+		/*
+		 * Try to find a suitable driver and assign it to the device.  We do
+		 * not want to block the current fibril that is used for processing
+		 * incoming calls: we will launch a separate fibril to handle the
+		 * driver assigning. That is because assign_driver can actually include
+		 * task spawning which could take some time.
+		 */
+		fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
+		if (assign_fibril == 0) {
+			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for "
+			    "assigning driver.");
+			/* XXX Cleanup */
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
+			return ENOMEM;
+		}
+		fibril_add_ready(assign_fibril);
+	} else
+		loc_register_tree_function(fun, &device_tree);
+	
+	fibril_rwlock_write_unlock(&device_tree.rwlock);
+	
+	return EOK;
+}
+
+int fun_offline(fun_node_t *fun)
+{
+	int rc;
+	
+	fibril_rwlock_write_lock(&device_tree.rwlock);
+	
+	if (fun->state == FUN_OFF_LINE) {
+		fibril_rwlock_write_unlock(&device_tree.rwlock);
+		log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.",
+		    fun->pathname);
+		return EOK;
+	}
+	
+	if (fun->ftype == fun_inner) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
+		    fun->pathname);
+		
+		if (fun->child != NULL) {
+			dev_node_t *dev = fun->child;
+			device_state_t dev_state;
+			
+			dev_add_ref(dev);
+			dev_state = dev->state;
+			
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
+
+			/* If device is owned by driver, ask driver to give it up. */
+			if (dev_state == DEVICE_USABLE) {
+				rc = driver_dev_remove(&device_tree, dev);
+				if (rc != EOK) {
+					dev_del_ref(dev);
+					return ENOTSUP;
+				}
+			}
+			
+			/* Verify that driver removed all functions */
+			fibril_rwlock_read_lock(&device_tree.rwlock);
+			if (!list_empty(&dev->functions)) {
+				fibril_rwlock_read_unlock(&device_tree.rwlock);
+				dev_del_ref(dev);
+				return EIO;
+			}
+			
+			driver_t *driver = dev->drv;
+			fibril_rwlock_read_unlock(&device_tree.rwlock);
+			
+			if (driver)
+				detach_driver(&device_tree, dev);
+			
+			fibril_rwlock_write_lock(&device_tree.rwlock);
+			remove_dev_node(&device_tree, dev);
+			
+			/* Delete ref created when node was inserted */
+			dev_del_ref(dev);
+			/* Delete ref created by dev_add_ref(dev) above */
+			dev_del_ref(dev);
+		}
+	} else {
+		/* Unregister from location service */
+		rc = loc_service_unregister(fun->service_id);
+		if (rc != EOK) {
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
+			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service.");
+			return EIO;
+		}
+		
+		fun->service_id = 0;
+	}
+	
+	fun->state = FUN_OFF_LINE;
+	fibril_rwlock_write_unlock(&device_tree.rwlock);
+	
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/devman/fun.h
===================================================================
--- uspace/srv/devman/fun.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/fun.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#ifndef FUN_H_
+#define FUN_H_
+
+#include "devman.h"
+
+extern fun_node_t *create_fun_node(void);
+extern void delete_fun_node(fun_node_t *);
+extern void fun_add_ref(fun_node_t *);
+extern void fun_del_ref(fun_node_t *);
+extern void fun_busy_lock(fun_node_t *);
+extern void fun_busy_unlock(fun_node_t *);
+extern fun_node_t *find_fun_node_no_lock(dev_tree_t *, devman_handle_t);
+extern fun_node_t *find_fun_node(dev_tree_t *, devman_handle_t);
+extern fun_node_t *find_fun_node_by_path(dev_tree_t *, char *);
+extern fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *,
+    const char *);
+extern bool set_fun_path(dev_tree_t *, fun_node_t *, fun_node_t *);
+extern int fun_online(fun_node_t *);
+extern int fun_offline(fun_node_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/loc.c
===================================================================
--- uspace/srv/devman/loc.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/loc.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#include <loc.h>
+#include <stdio.h>
+
+#include "devman.h"
+#include "fun.h"
+#include "loc.h"
+
+/** Create loc path and name for the function. */
+void loc_register_tree_function(fun_node_t *fun, dev_tree_t *tree)
+{
+	char *loc_pathname = NULL;
+	char *loc_name = NULL;
+	
+	assert(fibril_rwlock_is_locked(&tree->rwlock));
+	
+	asprintf(&loc_name, "%s", fun->pathname);
+	if (loc_name == NULL)
+		return;
+	
+	replace_char(loc_name, '/', LOC_SEPARATOR);
+	
+	asprintf(&loc_pathname, "%s/%s", LOC_DEVICE_NAMESPACE,
+	    loc_name);
+	if (loc_pathname == NULL) {
+		free(loc_name);
+		return;
+	}
+	
+	loc_service_register_with_iface(loc_pathname,
+	    &fun->service_id, DEVMAN_CONNECT_FROM_LOC);
+	
+	tree_add_loc_function(tree, fun);
+	
+	free(loc_name);
+	free(loc_pathname);
+}
+
+fun_node_t *find_loc_tree_function(dev_tree_t *tree, service_id_t service_id)
+{
+	fun_node_t *fun = NULL;
+	
+	fibril_rwlock_read_lock(&tree->rwlock);
+	ht_link_t *link = hash_table_find(&tree->loc_functions, &service_id);
+	if (link != NULL) {
+		fun = hash_table_get_inst(link, fun_node_t, loc_fun);
+		fun_add_ref(fun);
+	}
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	return fun;
+}
+
+void tree_add_loc_function(dev_tree_t *tree, fun_node_t *fun)
+{
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	hash_table_insert(&tree->loc_functions, &fun->loc_fun);
+}
+
+/** @}
+ */
Index: uspace/srv/devman/loc.h
===================================================================
--- uspace/srv/devman/loc.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/loc.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2011 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#ifndef LOC_H_
+#define LOC_H_
+
+#include <ipc/loc.h>
+#include "devman.h"
+
+extern void loc_register_tree_function(fun_node_t *, dev_tree_t *);
+extern fun_node_t *find_loc_tree_function(dev_tree_t *, service_id_t);
+extern void tree_add_loc_function(dev_tree_t *, fun_node_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/devman/main.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -36,5 +36,4 @@
  */
 
-#include <inttypes.h>
 #include <assert.h>
 #include <ipc/services.h>
@@ -43,1163 +42,27 @@
 #include <stdio.h>
 #include <errno.h>
-#include <str_error.h>
 #include <stdbool.h>
 #include <fibril_synch.h>
 #include <stdlib.h>
 #include <str.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <sys/stat.h>
 #include <ctype.h>
 #include <io/log.h>
 #include <ipc/devman.h>
 #include <ipc/driver.h>
-#include <thread.h>
 #include <loc.h>
 
+#include "client_conn.h"
+#include "dev.h"
 #include "devman.h"
+#include "devtree.h"
+#include "drv_conn.h"
+#include "driver.h"
+#include "fun.h"
+#include "loc.h"
 
 #define DRIVER_DEFAULT_STORE  "/drv"
 
-static driver_list_t drivers_list;
-static dev_tree_t device_tree;
-
-static int init_running_drv(void *drv);
-
-/** Register running driver. */
-static driver_t *devman_driver_register(ipc_callid_t callid, ipc_call_t *call)
-{
-	driver_t *driver = NULL;
-	char *drv_name = NULL;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_driver_register");
-	
-	/* Get driver name. */
-	int rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return NULL;
-	}
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s' driver is trying to register.",
-	    drv_name);
-	
-	/* Find driver structure. */
-	driver = find_driver(&drivers_list, drv_name);
-	if (driver == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "No driver named `%s' was found.", drv_name);
-		free(drv_name);
-		drv_name = NULL;
-		async_answer_0(callid, ENOENT);
-		return NULL;
-	}
-	
-	free(drv_name);
-	drv_name = NULL;
-	
-	fibril_mutex_lock(&driver->driver_mutex);
-	
-	if (driver->sess) {
-		/* We already have a connection to the driver. */
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Driver '%s' already started.\n",
-		    driver->name);
-		fibril_mutex_unlock(&driver->driver_mutex);
-		async_answer_0(callid, EEXISTS);
-		return NULL;
-	}
-	
-	switch (driver->state) {
-	case DRIVER_NOT_STARTED:
-		/* Somebody started the driver manually. */
-		log_msg(LOG_DEFAULT, LVL_NOTE, "Driver '%s' started manually.\n",
-		    driver->name);
-		driver->state = DRIVER_STARTING;
-		break;
-	case DRIVER_STARTING:
-		/* The expected case */
-		break;
-	case DRIVER_RUNNING:
-		/* Should not happen since we do not have a connected session */
-		assert(false);
-	}
-	
-	/* Create connection to the driver. */
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "Creating connection to the `%s' driver.",
-	    driver->name);
-	driver->sess = async_callback_receive(EXCHANGE_PARALLEL);
-	if (!driver->sess) {
-		fibril_mutex_unlock(&driver->driver_mutex);
-		async_answer_0(callid, ENOTSUP);
-		return NULL;
-	}
-	/* FIXME: Work around problem with callback sessions */
-	async_sess_args_set(driver->sess, DRIVER_DEVMAN, 0, 0);
-	
-	log_msg(LOG_DEFAULT, LVL_NOTE,
-	    "The `%s' driver was successfully registered as running.",
-	    driver->name);
-	
-	/*
-	 * Initialize the driver as running (e.g. pass assigned devices to it)
-	 * in a separate fibril; the separate fibril is used to enable the
-	 * driver to use devman service during the driver's initialization.
-	 */
-	fid_t fid = fibril_create(init_running_drv, driver);
-	if (fid == 0) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create initialization fibril " \
-		    "for driver `%s'.", driver->name);
-		fibril_mutex_unlock(&driver->driver_mutex);
-		async_answer_0(callid, ENOMEM);
-		return NULL;
-	}
-	
-	fibril_add_ready(fid);
-	fibril_mutex_unlock(&driver->driver_mutex);
-	
-	async_answer_0(callid, EOK);
-	return driver;
-}
-
-/** Receive device match ID from the device's parent driver and add it to the
- * list of devices match ids.
- *
- * @param match_ids	The list of the device's match ids.
- * @return		Zero on success, negative error code otherwise.
- */
-static int devman_receive_match_id(match_id_list_t *match_ids)
-{
-	match_id_t *match_id = create_match_id();
-	ipc_callid_t callid;
-	ipc_call_t call;
-	int rc = 0;
-	
-	callid = async_get_call(&call);
-	if (DEVMAN_ADD_MATCH_ID != IPC_GET_IMETHOD(call)) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, 
-		    "Invalid protocol when trying to receive match id.");
-		async_answer_0(callid, EINVAL); 
-		delete_match_id(match_id);
-		return EINVAL;
-	}
-	
-	if (match_id == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate match id.");
-		async_answer_0(callid, ENOMEM);
-		return ENOMEM;
-	}
-	
-	async_answer_0(callid, EOK);
-	
-	match_id->score = IPC_GET_ARG1(call);
-	
-	char *match_id_str;
-	rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
-	match_id->id = match_id_str;
-	if (rc != EOK) {
-		delete_match_id(match_id);
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to receive match id string: %s.",
-		    str_error(rc));
-		return rc;
-	}
-	
-	list_append(&match_id->link, &match_ids->ids);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "Received match id `%s', score %d.",
-	    match_id->id, match_id->score);
-	return rc;
-}
-
-/** Receive device match IDs from the device's parent driver and add them to the
- * list of devices match ids.
- *
- * @param match_count	The number of device's match ids to be received.
- * @param match_ids	The list of the device's match ids.
- * @return		Zero on success, negative error code otherwise.
- */
-static int devman_receive_match_ids(sysarg_t match_count,
-    match_id_list_t *match_ids)
-{
-	int ret = EOK;
-	size_t i;
-	
-	for (i = 0; i < match_count; i++) {
-		if (EOK != (ret = devman_receive_match_id(match_ids)))
-			return ret;
-	}
-	return ret;
-}
-
-static int assign_driver_fibril(void *arg)
-{
-	dev_node_t *dev_node = (dev_node_t *) arg;
-	assign_driver(dev_node, &drivers_list, &device_tree);
-
-	/* Delete one reference we got from the caller. */
-	dev_del_ref(dev_node);
-	return EOK;
-}
-
-static int online_function(fun_node_t *fun)
-{
-	dev_node_t *dev;
-	
-	fibril_rwlock_write_lock(&device_tree.rwlock);
-	
-	if (fun->state == FUN_ON_LINE) {
-		fibril_rwlock_write_unlock(&device_tree.rwlock);
-		log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already on line.",
-		    fun->pathname);
-		return EOK;
-	}
-	
-	if (fun->ftype == fun_inner) {
-		dev = create_dev_node();
-		if (dev == NULL) {
-			fibril_rwlock_write_unlock(&device_tree.rwlock);
-			return ENOMEM;
-		}
-		
-		insert_dev_node(&device_tree, dev, fun);
-		dev_add_ref(dev);
-	}
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
-	
-	if (fun->ftype == fun_inner) {
-		dev = fun->child;
-		assert(dev != NULL);
-		
-		/* Give one reference over to assign_driver_fibril(). */
-		dev_add_ref(dev);
-		
-		/*
-		 * Try to find a suitable driver and assign it to the device.  We do
-		 * not want to block the current fibril that is used for processing
-		 * incoming calls: we will launch a separate fibril to handle the
-		 * driver assigning. That is because assign_driver can actually include
-		 * task spawning which could take some time.
-		 */
-		fid_t assign_fibril = fibril_create(assign_driver_fibril, dev);
-		if (assign_fibril == 0) {
-			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create fibril for "
-			    "assigning driver.");
-			/* XXX Cleanup */
-			fibril_rwlock_write_unlock(&device_tree.rwlock);
-			return ENOMEM;
-		}
-		fibril_add_ready(assign_fibril);
-	} else
-		loc_register_tree_function(fun, &device_tree);
-	
-	fibril_rwlock_write_unlock(&device_tree.rwlock);
-	
-	return EOK;
-}
-
-static int offline_function(fun_node_t *fun)
-{
-	int rc;
-	
-	fibril_rwlock_write_lock(&device_tree.rwlock);
-	
-	if (fun->state == FUN_OFF_LINE) {
-		fibril_rwlock_write_unlock(&device_tree.rwlock);
-		log_msg(LOG_DEFAULT, LVL_WARN, "Function %s is already off line.",
-		    fun->pathname);
-		return EOK;
-	}
-	
-	if (fun->ftype == fun_inner) {
-		log_msg(LOG_DEFAULT, LVL_DEBUG, "Offlining inner function %s.",
-		    fun->pathname);
-		
-		if (fun->child != NULL) {
-			dev_node_t *dev = fun->child;
-			device_state_t dev_state;
-			
-			dev_add_ref(dev);
-			dev_state = dev->state;
-			
-			fibril_rwlock_write_unlock(&device_tree.rwlock);
-
-			/* If device is owned by driver, ask driver to give it up. */
-			if (dev_state == DEVICE_USABLE) {
-				rc = driver_dev_remove(&device_tree, dev);
-				if (rc != EOK) {
-					dev_del_ref(dev);
-					return ENOTSUP;
-				}
-			}
-			
-			/* Verify that driver removed all functions */
-			fibril_rwlock_read_lock(&device_tree.rwlock);
-			if (!list_empty(&dev->functions)) {
-				fibril_rwlock_read_unlock(&device_tree.rwlock);
-				dev_del_ref(dev);
-				return EIO;
-			}
-			
-			driver_t *driver = dev->drv;
-			fibril_rwlock_read_unlock(&device_tree.rwlock);
-			
-			if (driver)
-				detach_driver(&device_tree, dev);
-			
-			fibril_rwlock_write_lock(&device_tree.rwlock);
-			remove_dev_node(&device_tree, dev);
-			
-			/* Delete ref created when node was inserted */
-			dev_del_ref(dev);
-			/* Delete ref created by dev_add_ref(dev) above */
-			dev_del_ref(dev);
-		}
-	} else {
-		/* Unregister from location service */
-		rc = loc_service_unregister(fun->service_id);
-		if (rc != EOK) {
-			fibril_rwlock_write_unlock(&device_tree.rwlock);
-			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree service.");
-			return EIO;
-		}
-		
-		fun->service_id = 0;
-	}
-	
-	fun->state = FUN_OFF_LINE;
-	fibril_rwlock_write_unlock(&device_tree.rwlock);
-	
-	return EOK;
-}
-
-/** Handle function registration.
- *
- * Child devices are registered by their parent's device driver.
- */
-static void devman_add_function(ipc_callid_t callid, ipc_call_t *call)
-{
-	fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call);
-	devman_handle_t dev_handle = IPC_GET_ARG2(*call);
-	sysarg_t match_count = IPC_GET_ARG3(*call);
-	dev_tree_t *tree = &device_tree;
-	
-	dev_node_t *pdev = find_dev_node(&device_tree, dev_handle);
-	if (pdev == NULL) {
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	if (ftype != fun_inner && ftype != fun_exposed) {
-		/* Unknown function type */
-		log_msg(LOG_DEFAULT, LVL_ERROR, 
-		    "Unknown function type %d provided by driver.",
-		    (int) ftype);
-
-		dev_del_ref(pdev);
-		async_answer_0(callid, EINVAL);
-		return;
-	}
-	
-	char *fun_name = NULL;
-	int rc = async_data_write_accept((void **)&fun_name, true, 0, 0, 0, 0);
-	if (rc != EOK) {
-		dev_del_ref(pdev);
-		async_answer_0(callid, rc);
-		return;
-	}
-	
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	/* Check device state */
-	if (pdev->state == DEVICE_REMOVED) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		dev_del_ref(pdev);
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	/* Check that function with same name is not there already. */
-	fun_node_t *tfun = find_fun_node_in_device(tree, pdev, fun_name);
-	if (tfun) {
-		fun_del_ref(tfun);	/* drop the new unwanted reference */
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		dev_del_ref(pdev);
-		async_answer_0(callid, EEXISTS);
-		printf(NAME ": Warning, driver tried to register `%s' twice.\n",
-		    fun_name);
-		free(fun_name);
-		return;
-	}
-	
-	fun_node_t *fun = create_fun_node();
-	/* One reference for creation, one for us */
-	fun_add_ref(fun);
-	fun_add_ref(fun);
-	fun->ftype = ftype;
-	
-	/*
-	 * We can lock the function here even when holding the tree because
-	 * we know it cannot be held by anyone else yet.
-	 */
-	fun_busy_lock(fun);
-	
-	if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		dev_del_ref(pdev);
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		delete_fun_node(fun);
-		async_answer_0(callid, ENOMEM);
-		return;
-	}
-	
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	dev_del_ref(pdev);
-	
-	devman_receive_match_ids(match_count, &fun->match_ids);
-	
-	rc = online_function(fun);
-	if (rc != EOK) {
-		/* XXX Set some failed state? */
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		async_answer_0(callid, rc);
-		return;
-	}
-	
-	fun_busy_unlock(fun);
-	fun_del_ref(fun);
-	
-	/* Return device handle to parent's driver. */
-	async_answer_1(callid, EOK, fun->handle);
-}
-
-static void devman_add_function_to_cat(ipc_callid_t callid, ipc_call_t *call)
-{
-	devman_handle_t handle = IPC_GET_ARG1(*call);
-	category_id_t cat_id;
-	int rc;
-	
-	/* Get category name. */
-	char *cat_name;
-	rc = async_data_write_accept((void **) &cat_name, true,
-	    0, 0, 0, 0);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return;
-	}
-	
-	fun_node_t *fun = find_fun_node(&device_tree, handle);
-	if (fun == NULL) {
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-	
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
-	if (rc == EOK) {
-		loc_service_add_to_cat(fun->service_id, cat_id);
-		log_msg(LOG_DEFAULT, LVL_NOTE, "Function `%s' added to category `%s'.",
-		    fun->pathname, cat_name);
-	} else {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding function `%s' to category "
-		    "`%s'.", fun->pathname, cat_name);
-	}
-	
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	fun_del_ref(fun);
-	
-	async_answer_0(callid, rc);
-}
-
-/** Online function by driver request.
- *
- */
-static void devman_drv_fun_online(ipc_callid_t iid, ipc_call_t *icall,
-    driver_t *drv)
-{
-	fun_node_t *fun;
-	int rc;
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_drv_fun_online()");
-	
-	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	fun_busy_lock(fun);
-	
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-	if (fun->dev == NULL || fun->dev->drv != drv) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	
-	rc = online_function(fun);
-	if (rc != EOK) {
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		async_answer_0(iid, (sysarg_t) rc);
-		return;
-	}
-	
-	fun_busy_unlock(fun);
-	fun_del_ref(fun);
-	
-	async_answer_0(iid, (sysarg_t) EOK);
-}
-
-
-/** Offline function by driver request.
- *
- */
-static void devman_drv_fun_offline(ipc_callid_t iid, ipc_call_t *icall,
-    driver_t *drv)
-{
-	fun_node_t *fun;
-	int rc;
-
-	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	fun_busy_lock(fun);
-	
-	fibril_rwlock_write_lock(&device_tree.rwlock);
-	if (fun->dev == NULL || fun->dev->drv != drv) {
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	fibril_rwlock_write_unlock(&device_tree.rwlock);
-	
-	rc = offline_function(fun);
-	if (rc != EOK) {
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		async_answer_0(iid, (sysarg_t) rc);
-		return;
-	}
-	
-	fun_busy_unlock(fun);
-	fun_del_ref(fun);
-	async_answer_0(iid, (sysarg_t) EOK);
-}
-
-/** Remove function. */
-static void devman_remove_function(ipc_callid_t callid, ipc_call_t *call)
-{
-	devman_handle_t fun_handle = IPC_GET_ARG1(*call);
-	dev_tree_t *tree = &device_tree;
-	int rc;
-	
-	fun_node_t *fun = find_fun_node(&device_tree, fun_handle);
-	if (fun == NULL) {
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	fun_busy_lock(fun);
-	
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
-	
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		fun_busy_unlock(fun);
-		fun_del_ref(fun);
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	if (fun->ftype == fun_inner) {
-		/* This is a surprise removal. Handle possible descendants */
-		if (fun->child != NULL) {
-			dev_node_t *dev = fun->child;
-			device_state_t dev_state;
-			int gone_rc;
-			
-			dev_add_ref(dev);
-			dev_state = dev->state;
-			
-			fibril_rwlock_write_unlock(&device_tree.rwlock);
-			
-			/* If device is owned by driver, inform driver it is gone. */
-			if (dev_state == DEVICE_USABLE)
-				gone_rc = driver_dev_gone(&device_tree, dev);
-			else
-				gone_rc = EOK;
-			
-			fibril_rwlock_read_lock(&device_tree.rwlock);
-			
-			/* Verify that driver succeeded and removed all functions */
-			if (gone_rc != EOK || !list_empty(&dev->functions)) {
-				log_msg(LOG_DEFAULT, LVL_ERROR, "Driver did not remove "
-				    "functions for device that is gone. "
-				    "Device node is now defunct.");
-				
-				/*
-				 * Not much we can do but mark the device
-				 * node as having invalid state. This
-				 * is a driver bug.
-				 */
-				dev->state = DEVICE_INVALID;
-				fibril_rwlock_read_unlock(&device_tree.rwlock);
-				dev_del_ref(dev);
-				if (gone_rc == EOK)
-					gone_rc = ENOTSUP;
-				fun_busy_unlock(fun);
-				fun_del_ref(fun);
-				async_answer_0(callid, gone_rc);
-				return;
-			}
-			
-			driver_t *driver = dev->drv;
-			fibril_rwlock_read_unlock(&device_tree.rwlock);
-			
-			if (driver)
-				detach_driver(&device_tree, dev);
-			
-			fibril_rwlock_write_lock(&device_tree.rwlock);
-			remove_dev_node(&device_tree, dev);
-			
-			/* Delete ref created when node was inserted */
-			dev_del_ref(dev);
-			/* Delete ref created by dev_add_ref(dev) above */
-			dev_del_ref(dev);
-		}
-	} else {
-		if (fun->service_id != 0) {
-			/* Unregister from location service */
-			rc = loc_service_unregister(fun->service_id);
-			if (rc != EOK) {
-				log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree "
-				    "service.");
-				fibril_rwlock_write_unlock(&tree->rwlock);
-				fun_busy_unlock(fun);
-				fun_del_ref(fun);
-				async_answer_0(callid, EIO);
-				return;
-			}
-		}
-	}
-	
-	remove_fun_node(&device_tree, fun);
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	fun_busy_unlock(fun);
-	
-	/* Delete ref added when inserting function into tree */
-	fun_del_ref(fun);
-	/* Delete ref added above when looking up function */
-	fun_del_ref(fun);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function() succeeded.");
-	async_answer_0(callid, EOK);
-}
-
-/** Initialize driver which has registered itself as running and ready.
- *
- * The initialization is done in a separate fibril to avoid deadlocks (if the
- * driver needed to be served by devman during the driver's initialization).
- */
-static int init_running_drv(void *drv)
-{
-	driver_t *driver = (driver_t *) drv;
-	
-	initialize_running_driver(driver, &device_tree);
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s` driver was successfully initialized.",
-	    driver->name);
-	return 0;
-}
-
-/** Function for handling connections from a driver to the device manager. */
-static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
-{
-	client_t *client;
-	driver_t *driver = NULL;
-	
-	/* Accept the connection. */
-	async_answer_0(iid, EOK);
-	
-	client = async_get_client_data();
-	if (client == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate client data.");
-		return;
-	}
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			break;
-		
-		if (IPC_GET_IMETHOD(call) != DEVMAN_DRIVER_REGISTER) {
-			fibril_mutex_lock(&client->mutex);
-			driver = client->driver;
-			fibril_mutex_unlock(&client->mutex);
-			if (driver == NULL) {
-				/* First call must be to DEVMAN_DRIVER_REGISTER */
-				async_answer_0(callid, ENOTSUP);
-				continue;
-			}
-		}
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case DEVMAN_DRIVER_REGISTER:
-			fibril_mutex_lock(&client->mutex);
-			if (client->driver != NULL) {
-				fibril_mutex_unlock(&client->mutex);
-				async_answer_0(callid, EINVAL);
-				continue;
-			}
-			client->driver = devman_driver_register(callid, &call);
-			fibril_mutex_unlock(&client->mutex);
-			break;
-		case DEVMAN_ADD_FUNCTION:
-			devman_add_function(callid, &call);
-			break;
-		case DEVMAN_ADD_DEVICE_TO_CATEGORY:
-			devman_add_function_to_cat(callid, &call);
-			break;
-		case DEVMAN_DRV_FUN_ONLINE:
-			devman_drv_fun_online(callid, &call, driver);
-			break;
-		case DEVMAN_DRV_FUN_OFFLINE:
-			devman_drv_fun_offline(callid, &call, driver);
-			break;
-		case DEVMAN_REMOVE_FUNCTION:
-			devman_remove_function(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, EINVAL);
-			break;
-		}
-	}
-}
-
-/** Find handle for the device instance identified by the device's path in the
- * device tree. */
-static void devman_function_get_handle(ipc_callid_t iid, ipc_call_t *icall)
-{
-	char *pathname;
-	devman_handle_t handle;
-	
-	int rc = async_data_write_accept((void **) &pathname, true, 0, 0, 0, 0);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	fun_node_t *fun = find_fun_node_by_path(&device_tree, pathname);
-	
-	free(pathname);
-
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	handle = fun->handle;
-
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-
-	/* Delete reference created above by find_fun_node_by_path() */
-	fun_del_ref(fun);
-
-	async_answer_1(iid, EOK, handle);
-}
-
-/** Get device name. */
-static void devman_fun_get_name(ipc_callid_t iid, ipc_call_t *icall)
-{
-	devman_handle_t handle = IPC_GET_ARG1(*icall);
-
-	fun_node_t *fun = find_fun_node(&device_tree, handle);
-	if (fun == NULL) {
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-
-	ipc_callid_t data_callid;
-	size_t data_len;
-	if (!async_data_read_receive(&data_callid, &data_len)) {
-		async_answer_0(iid, EINVAL);
-		fun_del_ref(fun);
-		return;
-	}
-
-	void *buffer = malloc(data_len);
-	if (buffer == NULL) {
-		async_answer_0(data_callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		fun_del_ref(fun);
-		return;
-	}
-
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		free(buffer);
-
-		async_answer_0(data_callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		fun_del_ref(fun);
-		return;
-	}
-
-	size_t sent_length = str_size(fun->name);
-	if (sent_length > data_len) {
-		sent_length = data_len;
-	}
-
-	async_data_read_finalize(data_callid, fun->name, sent_length);
-	async_answer_0(iid, EOK);
-
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	fun_del_ref(fun);
-	free(buffer);
-}
-
-/** Get function driver name. */
-static void devman_fun_get_driver_name(ipc_callid_t iid, ipc_call_t *icall)
-{
-	devman_handle_t handle = IPC_GET_ARG1(*icall);
-
-	fun_node_t *fun = find_fun_node(&device_tree, handle);
-	if (fun == NULL) {
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-
-	ipc_callid_t data_callid;
-	size_t data_len;
-	if (!async_data_read_receive(&data_callid, &data_len)) {
-		async_answer_0(iid, EINVAL);
-		fun_del_ref(fun);
-		return;
-	}
-
-	void *buffer = malloc(data_len);
-	if (buffer == NULL) {
-		async_answer_0(data_callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		fun_del_ref(fun);
-		return;
-	}
-
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		free(buffer);
-
-		async_answer_0(data_callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		fun_del_ref(fun);
-		return;
-	}
-
-	/* Check whether function has a driver */
-	if (fun->child == NULL || fun->child->drv == NULL) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		free(buffer);
-
-		async_answer_0(data_callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		fun_del_ref(fun);
-		return;
-	}
-
-	size_t sent_length = str_size(fun->child->drv->name);
-	if (sent_length > data_len) {
-		sent_length = data_len;
-	}
-
-	async_data_read_finalize(data_callid, fun->child->drv->name,
-	    sent_length);
-	async_answer_0(iid, EOK);
-
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	fun_del_ref(fun);
-	free(buffer);
-}
-
-/** Get device path. */
-static void devman_fun_get_path(ipc_callid_t iid, ipc_call_t *icall)
-{
-	devman_handle_t handle = IPC_GET_ARG1(*icall);
-
-	fun_node_t *fun = find_fun_node(&device_tree, handle);
-	if (fun == NULL) {
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-
-	ipc_callid_t data_callid;
-	size_t data_len;
-	if (!async_data_read_receive(&data_callid, &data_len)) {
-		async_answer_0(iid, EINVAL);
-		fun_del_ref(fun);
-		return;
-	}
-
-	void *buffer = malloc(data_len);
-	if (buffer == NULL) {
-		async_answer_0(data_callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		fun_del_ref(fun);
-		return;
-	}
-	
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-	
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		free(buffer);
-
-		async_answer_0(data_callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		fun_del_ref(fun);
-		return;
-	}
-	
-	size_t sent_length = str_size(fun->pathname);
-	if (sent_length > data_len) {
-		sent_length = data_len;
-	}
-
-	async_data_read_finalize(data_callid, fun->pathname, sent_length);
-	async_answer_0(iid, EOK);
-
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	fun_del_ref(fun);
-	free(buffer);
-}
-
-static void devman_dev_get_functions(ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	size_t act_size;
-	int rc;
-	
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-	
-	dev_node_t *dev = find_dev_node_no_lock(&device_tree,
-	    IPC_GET_ARG1(*icall));
-	if (dev == NULL || dev->state == DEVICE_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(callid, ENOENT);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
-	if (hdl_buf == NULL) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
-	rc = dev_get_functions(&device_tree, dev, hdl_buf, size, &act_size);
-	if (rc != EOK) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(callid, rc);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	
-	sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
-	free(hdl_buf);
-	
-	async_answer_1(iid, retval, act_size);
-}
-
-
-/** Get handle for child device of a function. */
-static void devman_fun_get_child(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fun_node_t *fun;
-	
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-	
-	fun = find_fun_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
-	if (fun == NULL || fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	if (fun->child == NULL) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	async_answer_1(iid, EOK, fun->child->handle);
-	
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-}
-
-/** Online function.
- *
- * Send a request to online a function to the responsible driver.
- * The driver may offline other functions if necessary (i.e. if the state
- * of this function is linked to state of another function somehow).
- */
-static void devman_fun_online(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fun_node_t *fun;
-	int rc;
-
-	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	rc = driver_fun_online(&device_tree, fun);
-	fun_del_ref(fun);
-	
-	async_answer_0(iid, (sysarg_t) rc);
-}
-
-/** Offline function.
- *
- * Send a request to offline a function to the responsible driver. As
- * a result the subtree rooted at that function should be cleanly
- * detatched. The driver may offline other functions if necessary
- * (i.e. if the state of this function is linked to state of another
- * function somehow).
- */
-static void devman_fun_offline(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fun_node_t *fun;
-	int rc;
-
-	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-	
-	rc = driver_fun_offline(&device_tree, fun);
-	fun_del_ref(fun);
-	
-	async_answer_0(iid, (sysarg_t) rc);
-}
-
-/** Find handle for the function instance identified by its service ID. */
-static void devman_fun_sid_to_handle(ipc_callid_t iid, ipc_call_t *icall)
-{
-	fun_node_t *fun;
-
-	fun = find_loc_tree_function(&device_tree, IPC_GET_ARG1(*icall));
-	
-	if (fun == NULL) {
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-
-	fibril_rwlock_read_lock(&device_tree.rwlock);
-
-	/* Check function state */
-	if (fun->state == FUN_REMOVED) {
-		fibril_rwlock_read_unlock(&device_tree.rwlock);
-		async_answer_0(iid, ENOENT);
-		return;
-	}
-
-	async_answer_1(iid, EOK, fun->handle);
-	fibril_rwlock_read_unlock(&device_tree.rwlock);
-	fun_del_ref(fun);
-}
-
-/** Function for handling connections from a client to the device manager. */
-static void devman_connection_client(ipc_callid_t iid, ipc_call_t *icall)
-{
-	/* Accept connection. */
-	async_answer_0(iid, EOK);
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			break;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case DEVMAN_DEVICE_GET_HANDLE:
-			devman_function_get_handle(callid, &call);
-			break;
-		case DEVMAN_DEV_GET_FUNCTIONS:
-			devman_dev_get_functions(callid, &call);
-			break;
-		case DEVMAN_FUN_GET_CHILD:
-			devman_fun_get_child(callid, &call);
-			break;
-		case DEVMAN_FUN_GET_NAME:
-			devman_fun_get_name(callid, &call);
-			break;
-		case DEVMAN_FUN_GET_DRIVER_NAME:
-			devman_fun_get_driver_name(callid, &call);
-			break;
-		case DEVMAN_FUN_GET_PATH:
-			devman_fun_get_path(callid, &call);
-			break;
-		case DEVMAN_FUN_ONLINE:
-			devman_fun_online(callid, &call);
-			break;
-		case DEVMAN_FUN_OFFLINE:
-			devman_fun_offline(callid, &call);
-			break;
-		case DEVMAN_FUN_SID_TO_HANDLE:
-			devman_fun_sid_to_handle(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, ENOENT);
-		}
-	}
-}
+driver_list_t drivers_list;
+dev_tree_t device_tree;
 
 static void devman_forward(ipc_callid_t iid, ipc_call_t *icall,
Index: uspace/srv/devman/main.h
===================================================================
--- uspace/srv/devman/main.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/main.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#ifndef MAIN_H_
+#define MAIN_H_
+
+#include "devman.h"
+
+extern driver_list_t drivers_list;
+extern dev_tree_t device_tree;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/match.c
===================================================================
--- uspace/srv/devman/match.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/devman/match.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -31,7 +31,13 @@
  */
 
+#include <fcntl.h>
+#include <io/log.h>
 #include <str.h>
+#include <str_error.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include "devman.h"
+#include "match.h"
 
 /** Compute compound score of driver and device.
@@ -93,4 +99,141 @@
 }
 
+/** Read match id at the specified position of a string and set the position in
+ * the string to the first character following the id.
+ *
+ * @param buf		The position in the input string.
+ * @return		The match id.
+ */
+char *read_match_id(char **buf)
+{
+	char *res = NULL;
+	size_t len = get_nonspace_len(*buf);
+	
+	if (len > 0) {
+		res = malloc(len + 1);
+		if (res != NULL) {
+			str_ncpy(res, len + 1, *buf, len);
+			*buf += len;
+		}
+	}
+	
+	return res;
+}
+
+/**
+ * Read match ids and associated match scores from a string.
+ *
+ * Each match score in the string is followed by its match id.
+ * The match ids and match scores are separated by whitespaces.
+ * Neither match ids nor match scores can contain whitespaces.
+ *
+ * @param buf		The string from which the match ids are read.
+ * @param ids		The list of match ids into which the match ids and
+ *			scores are added.
+ * @return		True if at least one match id and associated match score
+ *			was successfully read, false otherwise.
+ */
+bool parse_match_ids(char *buf, match_id_list_t *ids)
+{
+	int score = 0;
+	char *id = NULL;
+	int ids_read = 0;
+	
+	while (true) {
+		/* skip spaces */
+		if (!skip_spaces(&buf))
+			break;
+		
+		/* read score */
+		score = strtoul(buf, &buf, 10);
+		
+		/* skip spaces */
+		if (!skip_spaces(&buf))
+			break;
+		
+		/* read id */
+		id = read_match_id(&buf);
+		if (NULL == id)
+			break;
+		
+		/* create new match_id structure */
+		match_id_t *mid = create_match_id();
+		mid->id = id;
+		mid->score = score;
+		
+		/* add it to the list */
+		add_match_id(ids, mid);
+		
+		ids_read++;
+	}
+	
+	return ids_read > 0;
+}
+
+/**
+ * Read match ids and associated match scores from a file.
+ *
+ * Each match score in the file is followed by its match id.
+ * The match ids and match scores are separated by whitespaces.
+ * Neither match ids nor match scores can contain whitespaces.
+ *
+ * @param buf		The path to the file from which the match ids are read.
+ * @param ids		The list of match ids into which the match ids and
+ *			scores are added.
+ * @return		True if at least one match id and associated match score
+ *			was successfully read, false otherwise.
+ */
+bool read_match_ids(const char *conf_path, match_id_list_t *ids)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "read_match_ids(conf_path=\"%s\")", conf_path);
+	
+	bool suc = false;
+	char *buf = NULL;
+	bool opened = false;
+	int fd;
+	size_t len = 0;
+	
+	fd = open(conf_path, O_RDONLY);
+	if (fd < 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to open `%s' for reading: %s.",
+		    conf_path, str_error(fd));
+		goto cleanup;
+	}
+	opened = true;
+	
+	len = lseek(fd, 0, SEEK_END);
+	lseek(fd, 0, SEEK_SET);
+	if (len == 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Configuration file '%s' is empty.",
+		    conf_path);
+		goto cleanup;
+	}
+	
+	buf = malloc(len + 1);
+	if (buf == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Memory allocation failed when parsing file "
+		    "'%s'.", conf_path);
+		goto cleanup;
+	}
+	
+	ssize_t read_bytes = read_all(fd, buf, len);
+	if (read_bytes <= 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to read file '%s' (%zd).", conf_path,
+		    read_bytes);
+		goto cleanup;
+	}
+	buf[read_bytes] = 0;
+	
+	suc = parse_match_ids(buf, ids);
+	
+cleanup:
+	free(buf);
+	
+	if (opened)
+		close(fd);
+	
+	return suc;
+}
+
 /** @}
  */
Index: uspace/srv/devman/match.h
===================================================================
--- uspace/srv/devman/match.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
+++ uspace/srv/devman/match.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#ifndef MATCH_H_
+#define MATCH_H_
+
+#include <stdbool.h>
+
+#include "devman.h"
+
+#define MATCH_EXT ".ma"
+
+extern int get_match_score(driver_t *, dev_node_t *);
+extern bool parse_match_ids(char *, match_id_list_t *);
+extern bool read_match_ids(const char *, match_id_list_t *);
+extern char *read_match_id(char **);
+extern char *read_id(const char **);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/util.h
===================================================================
--- uspace/srv/devman/util.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/devman/util.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -43,5 +43,4 @@
 extern bool skip_spaces(char **);
 extern size_t get_nonspace_len(const char *);
-extern void free_not_null(const void *);
 extern void replace_char(char *, char, char);
 
Index: uspace/srv/fs/cdfs/cdfs_ops.c
===================================================================
--- uspace/srv/fs/cdfs/cdfs_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/cdfs/cdfs_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -619,4 +619,25 @@
 }
 
+static int cdfs_size_block(service_id_t service_id, uint32_t *size)
+{
+	*size = BLOCK_SIZE;
+
+	return EOK; 
+}
+
+static int cdfs_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	*count = 0;
+	
+	return EOK;
+}
+
+static int cdfs_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	*count = 0;
+	
+	return EOK;
+}
+
 libfs_ops_t cdfs_libfs_ops = {
 	.root_get = cdfs_root_get,
@@ -635,5 +656,8 @@
 	.is_directory = cdfs_is_directory,
 	.is_file = cdfs_is_file,
-	.service_get = cdfs_service_get
+	.service_get = cdfs_service_get,
+	.size_block = cdfs_size_block,
+	.total_block_count = cdfs_total_block_count,
+	.free_block_count = cdfs_free_block_count
 };
 
Index: uspace/srv/fs/exfat/exfat_ops.c
===================================================================
--- uspace/srv/fs/exfat/exfat_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/exfat/exfat_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -89,4 +89,7 @@
 static bool exfat_is_file(fs_node_t *node);
 static service_id_t exfat_service_get(fs_node_t *node);
+static int exfat_size_block(service_id_t, uint32_t *);
+static int exfat_total_block_count(service_id_t, uint64_t *);
+static int exfat_free_block_count(service_id_t, uint64_t *);
 
 /*
@@ -910,4 +913,81 @@
 }
 
+int exfat_size_block(service_id_t service_id, uint32_t *size)
+{
+	exfat_bs_t *bs;
+	bs = block_bb_get(service_id);
+	*size = BPC(bs);
+
+	return EOK;
+}
+
+int exfat_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	exfat_bs_t *bs;
+	bs = block_bb_get(service_id);
+	*count = DATA_CNT(bs);
+	
+	return EOK;
+}
+
+int exfat_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	fs_node_t *node;
+	exfat_node_t *bmap_node;
+	exfat_bs_t *bs;
+	uint64_t free_block_count = 0;
+	uint64_t block_count;
+	unsigned sector;
+	int rc;
+
+	rc = exfat_total_block_count(service_id, &block_count);
+	if (rc != EOK)
+		goto exit;
+
+	bs = block_bb_get(service_id);
+	node = NULL;
+	rc = exfat_bitmap_get(&node, service_id);
+	if (rc != EOK)
+		goto exit;
+
+	bmap_node = (exfat_node_t *) node->data;
+
+	unsigned const bmap_sectors = ROUND_UP(bmap_node->size, BPS(bs)) /
+	    BPS(bs);
+
+	for (sector = 0; sector < bmap_sectors; ++sector) {
+
+		block_t *block;
+		uint8_t *bitmap;
+		unsigned bit;
+
+		rc = exfat_block_get(&block, bs, bmap_node, sector,
+		    BLOCK_FLAGS_NONE);
+		if (rc != EOK) {
+			free_block_count = 0;
+			goto exit;
+		}
+
+		bitmap = (uint8_t *) block->data;
+
+		for (bit = 0; bit < BPS(bs) * 8 && block_count > 0;
+		    ++bit, --block_count) {
+			if (!(bitmap[bit / 8] & (1 << (bit % 8))))
+				++free_block_count;
+		}
+
+		block_put(block);
+
+		if (block_count == 0) {
+			/* Reached the end of the bitmap */
+			goto exit;
+		}
+	}
+
+exit:
+	exfat_node_put(node);
+	*count = free_block_count;
+	return rc;
+}
 
 /** libfs operations */
@@ -928,5 +1008,8 @@
 	.is_directory = exfat_is_directory,
 	.is_file = exfat_is_file,
-	.service_get = exfat_service_get
+	.service_get = exfat_service_get,
+	.size_block = exfat_size_block,
+	.total_block_count = exfat_total_block_count,
+	.free_block_count = exfat_free_block_count
 };
 
Index: uspace/srv/fs/ext4fs/ext4fs_ops.c
===================================================================
--- uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -101,4 +101,7 @@
 static bool ext4fs_is_file(fs_node_t *node);
 static service_id_t ext4fs_service_get(fs_node_t *node);
+static int ext4fs_size_block(service_id_t, uint32_t *);
+static int ext4fs_total_block_count(service_id_t, uint64_t *);
+static int ext4fs_free_block_count(service_id_t, uint64_t *);
 
 /* Static variables */
@@ -833,4 +836,51 @@
 	ext4fs_node_t *enode = EXT4FS_NODE(fn);
 	return enode->instance->service_id;
+}
+
+int ext4fs_size_block(service_id_t service_id, uint32_t *size)
+{
+	ext4fs_instance_t *inst;
+	int rc = ext4fs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == inst)
+		return ENOENT;
+
+	ext4_superblock_t *sb = inst->filesystem->superblock;
+	*size = ext4_superblock_get_block_size(sb);
+
+	return EOK;
+}
+
+int ext4fs_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	ext4fs_instance_t *inst;
+	int rc = ext4fs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == inst)
+		return ENOENT;
+
+	ext4_superblock_t *sb = inst->filesystem->superblock;
+	*count = ext4_superblock_get_blocks_count(sb);
+
+	return EOK;
+}
+
+int ext4fs_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	ext4fs_instance_t *inst;
+	int rc = ext4fs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+	if (NULL == inst)
+		return ENOENT;
+
+	ext4_superblock_t *sb = inst->filesystem->superblock;
+	*count = ext4_superblock_get_free_blocks_count(sb);
+
+	return EOK;
 }
 
@@ -854,5 +904,8 @@
 	.is_directory = ext4fs_is_directory,
 	.is_file = ext4fs_is_file,
-	.service_get = ext4fs_service_get
+	.service_get = ext4fs_service_get,
+	.size_block = ext4fs_size_block,
+	.total_block_count = ext4fs_total_block_count,
+	.free_block_count = ext4fs_free_block_count
 };
 
Index: uspace/srv/fs/fat/fat_ops.c
===================================================================
--- uspace/srv/fs/fat/fat_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/fat/fat_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -91,4 +91,7 @@
 static bool fat_is_file(fs_node_t *node);
 static service_id_t fat_service_get(fs_node_t *node);
+static int fat_size_block(service_id_t, uint32_t *);
+static int fat_total_block_count(service_id_t, uint64_t *);
+static int fat_free_block_count(service_id_t, uint64_t *);
 
 /*
@@ -841,4 +844,48 @@
 }
 
+int fat_size_block(service_id_t service_id, uint32_t *size)
+{
+	fat_bs_t *bs;
+
+	bs = block_bb_get(service_id);
+	*size = BPC(bs);
+
+	return EOK;
+}
+
+int fat_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	fat_bs_t *bs;
+	
+	bs = block_bb_get(service_id);
+	*count = (SPC(bs)) ? TS(bs) / SPC(bs) : 0;
+
+	return EOK;
+}
+
+int fat_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	fat_bs_t *bs;
+	fat_cluster_t e0;
+	uint64_t block_count;
+	int rc;
+	uint32_t cluster_no, clusters;
+
+	block_count = 0;
+	bs = block_bb_get(service_id);
+	clusters = (SPC(bs)) ? TS(bs) / SPC(bs) : 0;
+	for (cluster_no = 0; cluster_no < clusters; cluster_no++) {
+		rc = fat_get_cluster(bs, service_id, FAT1, cluster_no, &e0);
+		if (rc != EOK)
+			return EIO;
+
+		if (e0 == FAT_CLST_RES0)
+			block_count++;
+	}
+	*count = block_count;
+	
+	return EOK;
+}
+
 /** libfs operations */
 libfs_ops_t fat_libfs_ops = {
@@ -858,5 +905,8 @@
 	.is_directory = fat_is_directory,
 	.is_file = fat_is_file,
-	.service_get = fat_service_get
+	.service_get = fat_service_get,
+	.size_block = fat_size_block,
+	.total_block_count = fat_total_block_count,
+	.free_block_count = fat_free_block_count
 };
 
Index: uspace/srv/fs/mfs/mfs.h
===================================================================
--- uspace/srv/fs/mfs/mfs.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/mfs/mfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -58,4 +58,14 @@
 #endif
 
+#define MFS_BMAP_START_BLOCK(sbi, bid) \
+    ((bid) == BMAP_ZONE ? 2 + (sbi)->ibmap_blocks : 2)
+
+#define MFS_BMAP_SIZE_BITS(sbi, bid) \
+    ((bid) == BMAP_ZONE ? (sbi)->nzones - (sbi)->firstdatazone - 1 : \
+    (sbi)->ninodes - 1)
+
+#define MFS_BMAP_SIZE_BLOCKS(sbi, bid) \
+    ((bid) == BMAP_ZONE ? (sbi)->zbmap_blocks : (sbi)->ibmap_blocks)
+
 typedef uint32_t bitchunk_t;
 
@@ -201,4 +211,11 @@
 mfs_free_zone(struct mfs_instance *inst, uint32_t zone);
 
+extern int
+mfs_count_free_zones(struct mfs_instance *inst, uint32_t *zones);
+
+extern int
+mfs_count_free_inodes(struct mfs_instance *inst, uint32_t *inodes);
+
+
 /* mfs_utils.c */
 extern uint16_t
Index: uspace/srv/fs/mfs/mfs_balloc.c
===================================================================
--- uspace/srv/fs/mfs/mfs_balloc.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/mfs/mfs_balloc.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -44,4 +44,8 @@
 mfs_alloc_bit(struct mfs_instance *inst, uint32_t *idx, bmap_id_t bid);
 
+static int
+mfs_count_free_bits(struct mfs_instance *inst, bmap_id_t bid, uint32_t *free);
+
+
 /**Allocate a new inode.
  *
@@ -102,4 +106,97 @@
 
 	return mfs_free_bit(inst, zone, BMAP_ZONE);
+}
+
+/** Count the number of free zones
+ *
+ * @param inst          Pointer to the instance structure.
+ * @param zones         Pointer to the memory location where the result
+ *                      will be stored.
+ *
+ * @return              EOK on success or a negative error code.
+ */
+int
+mfs_count_free_zones(struct mfs_instance *inst, uint32_t *zones)
+{
+	return mfs_count_free_bits(inst, BMAP_ZONE, zones);
+}
+
+/** Count the number of free inodes
+ *
+ * @param inst          Pointer to the instance structure.
+ * @param zones         Pointer to the memory location where the result
+ *                      will be stored.
+ *
+ * @return              EOK on success or a negative error code.
+ */
+
+int
+mfs_count_free_inodes(struct mfs_instance *inst, uint32_t *inodes)
+{
+	return mfs_count_free_bits(inst, BMAP_INODE, inodes);
+}
+
+/** Count the number of free bits in a bitmap
+ *
+ * @param inst          Pointer to the instance structure.
+ * @param bid           Type of the bitmap (inode or zone).
+ * @param free          Pointer to the memory location where the result
+ *                      will be stores.
+ *
+ * @return              EOK on success or a negative error code.
+ */
+static int
+mfs_count_free_bits(struct mfs_instance *inst, bmap_id_t bid, uint32_t *free)
+{
+	int r;
+	unsigned start_block;
+	unsigned long nblocks;
+	unsigned long nbits;
+	unsigned long block;
+	unsigned long free_bits = 0;
+	bitchunk_t chunk;
+	size_t const bitchunk_bits = sizeof(bitchunk_t) * 8;
+	block_t *b;
+	struct mfs_sb_info *sbi = inst->sbi;
+
+	start_block = MFS_BMAP_START_BLOCK(sbi, bid);
+	nblocks = MFS_BMAP_SIZE_BLOCKS(sbi, bid);
+	nbits = MFS_BMAP_SIZE_BITS(sbi, bid);
+
+	for (block = 0; block < nblocks; ++block) {
+		r = block_get(&b, inst->service_id, block + start_block,
+		    BLOCK_FLAGS_NONE);
+		if (r != EOK)
+			return r;
+
+		size_t i;
+		bitchunk_t *data = (bitchunk_t *) b->data;
+
+		/* Read the bitmap block, chunk per chunk,
+		 * counting the zero bits.
+		 */
+		for (i = 0; i < sbi->block_size / sizeof(bitchunk_t); ++i) {
+			chunk = conv32(sbi->native, data[i]);
+
+			size_t bit;
+			for (bit = 0; bit < bitchunk_bits && nbits > 0;
+			    ++bit, --nbits) {
+				if (!(chunk & (1 << bit)))
+					free_bits++;
+			}
+
+			if (nbits == 0)
+				break;
+		}
+
+		r = block_put(b);
+		if (r != EOK)
+			return r;
+	}
+
+	*free = free_bits;
+	assert(nbits == 0);
+
+	return EOK;
 }
 
@@ -124,7 +221,8 @@
 	sbi = inst->sbi;
 
+	start_block = MFS_BMAP_START_BLOCK(sbi, bid);
+
 	if (bid == BMAP_ZONE) {
 		search = &sbi->zsearch;
-		start_block = 2 + sbi->ibmap_blocks;
 		if (idx > sbi->nzones) {
 			printf(NAME ": Error! Trying to free beyond the "
@@ -135,5 +233,4 @@
 		/* bid == BMAP_INODE */
 		search = &sbi->isearch;
-		start_block = 2;
 		if (idx > sbi->ninodes) {
 			printf(NAME ": Error! Trying to free beyond the "
@@ -192,15 +289,13 @@
 	sbi = inst->sbi;
 
+	start_block = MFS_BMAP_START_BLOCK(sbi, bid);
+	limit = MFS_BMAP_SIZE_BITS(sbi, bid);
+	nblocks = MFS_BMAP_SIZE_BLOCKS(sbi, bid);
+
 	if (bid == BMAP_ZONE) {
 		search = &sbi->zsearch;
-		start_block = 2 + sbi->ibmap_blocks;
-		nblocks = sbi->zbmap_blocks;
-		limit = sbi->nzones - sbi->firstdatazone - 1;
 	} else {
 		/* bid == BMAP_INODE */
 		search = &sbi->isearch;
-		start_block = 2;
-		nblocks = sbi->ibmap_blocks;
-		limit = sbi->ninodes - 1;
 	}
 	bits_per_block = sbi->block_size * 8;
Index: uspace/srv/fs/mfs/mfs_ops.c
===================================================================
--- uspace/srv/fs/mfs/mfs_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/mfs/mfs_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -64,4 +64,7 @@
 static int mfs_check_sanity(struct mfs_sb_info *sbi);
 static bool is_power_of_two(uint32_t n);
+static int mfs_size_block(service_id_t service_id, uint32_t *size);
+static int mfs_total_block_count(service_id_t service_id, uint64_t *count);
+static int mfs_free_block_count(service_id_t service_id, uint64_t *count);
 
 static hash_table_t open_nodes;
@@ -84,5 +87,8 @@
 	.destroy = mfs_destroy_node,
 	.has_children = mfs_has_children,
-	.lnkcnt_get = mfs_lnkcnt_get
+	.lnkcnt_get = mfs_lnkcnt_get,
+	.size_block = mfs_size_block,
+	.total_block_count = mfs_total_block_count,
+	.free_block_count = mfs_free_block_count
 };
 
@@ -1129,4 +1135,59 @@
 }
 
+static int
+mfs_size_block(service_id_t service_id, uint32_t *size)
+{
+	struct mfs_instance *inst;
+	int rc;
+
+	rc = mfs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == inst)
+		return ENOENT;
+	
+	*size = inst->sbi->block_size;
+
+	return EOK;
+}
+
+static int
+mfs_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	struct mfs_instance *inst;
+	int rc;
+	
+	rc = mfs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == inst)
+		return ENOENT;
+	
+	*count = (uint64_t) MFS_BMAP_SIZE_BITS(inst->sbi, BMAP_ZONE);
+
+	return EOK;
+}
+
+static int
+mfs_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	uint32_t block_free;
+	
+	struct mfs_instance *inst;
+	int rc = mfs_instance_get(service_id, &inst);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == inst)
+		return ENOENT;
+
+	mfs_count_free_zones(inst, &block_free);
+	*count = block_free;
+
+	return EOK;
+}
+
 vfs_out_ops_t mfs_ops = {
 	.mounted = mfs_mounted,
Index: uspace/srv/fs/mfs/mfs_rw.c
===================================================================
--- uspace/srv/fs/mfs/mfs_rw.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/mfs/mfs_rw.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -104,7 +104,8 @@
     bool write_mode, uint32_t w_block)
 {
-	int r, nr_direct;
+	int nr_direct;
 	int ptrs_per_block;
-	uint32_t *ind_zone, *ind2_zone;
+	uint32_t *ind_zone = NULL, *ind2_zone = NULL;
+	int r = EOK;
 
 	struct mfs_ino_info *ino_i = mnode->ino_i;
@@ -130,5 +131,5 @@
 			ino_i->dirty = true;
 		}
-		return EOK;
+		goto out;
 	}
 
@@ -142,5 +143,5 @@
 				r = alloc_zone_and_clear(inst, &zone);
 				if (r != EOK)
-					return r;
+					goto out;
 
 				ino_i->i_izone[0] = zone;
@@ -149,5 +150,5 @@
 				/* Sparse block */
 				*b = 0;
-				return EOK;
+				goto out;
 			}
 		}
@@ -155,5 +156,5 @@
 		r = read_ind_zone(inst, ino_i->i_izone[0], &ind_zone);
 		if (r != EOK)
-			return r;
+			goto out;
 
 		*b = ind_zone[rblock];
@@ -163,5 +164,5 @@
 		}
 
-		goto out_free_ind1;
+		goto out;
 	}
 
@@ -176,5 +177,5 @@
 			r = alloc_zone_and_clear(inst, &zone);
 			if (r != EOK)
-				return r;
+				goto out;
 
 			ino_i->i_izone[1] = zone;
@@ -183,5 +184,5 @@
 			/* Sparse block */
 			*b = 0;
-			return EOK;
+			goto out;
 		}
 	}
@@ -189,5 +190,5 @@
 	r = read_ind_zone(inst, ino_i->i_izone[1], &ind_zone);
 	if (r != EOK)
-		return r;
+		goto out;
 
 	/*
@@ -203,5 +204,5 @@
 			r = alloc_zone_and_clear(inst, &zone);
 			if (r != EOK)
-				goto out_free_ind1;
+				goto out;
 
 			ind_zone[ind2_off] = zone;
@@ -209,7 +210,6 @@
 		} else {
 			/* Sparse block */
-			r = EOK;
 			*b = 0;
-			goto out_free_ind1;
+			goto out;
 		}
 	}
@@ -217,5 +217,5 @@
 	r = read_ind_zone(inst, ind_zone[ind2_off], &ind2_zone);
 	if (r != EOK)
-		goto out_free_ind1;
+		goto out;
 
 	*b = ind2_zone[rblock - (ind2_off * ptrs_per_block)];
@@ -225,8 +225,6 @@
 	}
 
-	r = EOK;
-
+out:
 	free(ind2_zone);
-out_free_ind1:
 	free(ind_zone);
 	return r;
Index: uspace/srv/fs/udf/udf_ops.c
===================================================================
--- uspace/srv/fs/udf/udf_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/fs/udf/udf_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -249,4 +249,33 @@
 }
 
+static int udf_size_block(service_id_t service_id, uint32_t *size)
+{
+	udf_instance_t *instance;
+	int rc = fs_instance_get(service_id, (void **) &instance);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == instance)
+		return ENOENT;
+	
+	*size = instance->volumes[DEFAULT_VOL].logical_block_size;
+	
+	return EOK;
+}
+
+static int udf_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	*count = 0;
+	
+	return EOK;
+}
+
+static int udf_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	*count = 0;
+	
+	return EOK;
+}
+
 libfs_ops_t udf_libfs_ops = {
 	.root_get = udf_root_get,
@@ -265,5 +294,8 @@
 	.is_directory = udf_is_directory,
 	.is_file = udf_is_file,
-	.service_get = udf_service_get
+	.service_get = udf_service_get,
+	.size_block = udf_size_block,
+	.total_block_count = udf_total_block_count,
+	.free_block_count = udf_free_block_count
 };
 
Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/vfs/vfs.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -130,4 +130,7 @@
 			vfs_get_mtab(callid, &call);
 			break;
+		case VFS_IN_STATFS:
+			vfs_statfs(callid, &call);
+			break;
 		default:
 			async_answer_0(callid, ENOTSUP);
Index: uspace/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/vfs/vfs.h	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -222,4 +222,5 @@
 extern void vfs_wait_handle(ipc_callid_t, ipc_call_t *);
 extern void vfs_get_mtab(ipc_callid_t, ipc_call_t *);
+extern void vfs_statfs(ipc_callid_t, ipc_call_t *);
 
 #endif
Index: uspace/srv/vfs/vfs_ops.c
===================================================================
--- uspace/srv/vfs/vfs_ops.c	(revision f9f45e7ade54ca0d1a7465c54d5baeb53951c48b)
+++ uspace/srv/vfs/vfs_ops.c	(revision 3731d316ba8734ec66b1e1175e1b2d89faf45e7a)
@@ -1412,4 +1412,58 @@
 }
 
+void vfs_statfs(ipc_callid_t rid, ipc_call_t *request)
+{
+	char *path;
+	int rc = async_data_write_accept((void **) &path, true, 0, 0, 0, NULL);
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
+		return;
+	}
+	
+	ipc_callid_t callid;
+	if (!async_data_read_receive(&callid, NULL)) {
+		free(path);
+		async_answer_0(callid, EINVAL);
+		async_answer_0(rid, EINVAL);
+		return;
+	}
+
+	vfs_lookup_res_t lr;
+	fibril_rwlock_read_lock(&namespace_rwlock);
+	rc = vfs_lookup_internal(path, L_NONE, &lr, NULL);
+	free(path);
+	if (rc != EOK) {
+		fibril_rwlock_read_unlock(&namespace_rwlock);
+		async_answer_0(callid, rc);
+		async_answer_0(rid, rc);
+		return;
+	}
+	vfs_node_t *node = vfs_node_get(&lr);
+	if (!node) {
+		fibril_rwlock_read_unlock(&namespace_rwlock);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(rid, ENOMEM);
+		return;
+	}
+
+	fibril_rwlock_read_unlock(&namespace_rwlock);
+
+	async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
+	
+	aid_t msg;
+	msg = async_send_3(exch, VFS_OUT_STATFS, node->service_id,
+	    node->index, false, NULL);
+	async_forward_fast(callid, exch, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
+	
+	vfs_exchange_release(exch);
+	
+	sysarg_t rv;
+	async_wait_for(msg, &rv);
+
+	async_answer_0(rid, rv);
+
+	vfs_node_put(node);
+}
+
 /**
  * @}
