Index: .bzrignore
===================================================================
--- .bzrignore	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ .bzrignore	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,280 @@
+*.disasm
+*.map
+*.prev
+Makefile.depend
+Makefile.common
+Makefile.config
+cscope.out
+autotool/
+common.h
+config.h
+hdisk.img
+image.iso
+boot/distroot/
+boot/initrd.img
+kernel/kernel.bin
+kernel/kernel.dump
+kernel/kernel.raw
+kernel/arch/amd64/_link.ld
+kernel/arch/amd64/include/arch/common.h
+kernel/generic/src/debug/real_map.bin
+uspace/app/bdsh/bdsh
+uspace/app/bithenge/bithenge
+uspace/app/blkdump/blkdump
+uspace/app/bnchmark/bnchmark
+uspace/app/date/date
+uspace/app/devctl/devctl
+uspace/app/dnscfg/dnscfg
+uspace/app/dnsres/dnsres
+uspace/app/edit/edit
+uspace/app/getterm/getterm
+uspace/app/inet/inet
+uspace/app/init/init
+uspace/app/kill/kill
+uspace/app/killall/killall
+uspace/app/klog/klog
+uspace/app/loc/loc
+uspace/app/logset/logset
+uspace/app/mixerctl/mixerctl
+uspace/app/mkbd/mkbd
+uspace/app/mkexfat/mkexfat
+uspace/app/mkfat/mkfat
+uspace/app/mkmfs/mkmfs
+uspace/app/netecho/netecho
+uspace/app/nettest1/nettest1
+uspace/app/nettest2/nettest2
+uspace/app/nettest3/nettest3
+uspace/app/nterm/nterm
+uspace/app/ping/ping
+uspace/app/ping6/ping6
+uspace/app/redir/redir
+uspace/app/sbi/sbi
+uspace/app/sportdmp/sportdmp
+uspace/app/stats/stats
+uspace/app/sysinfo/sysinfo
+uspace/app/taskdump/taskdump
+uspace/app/tester/tester
+uspace/app/testread/testread
+uspace/app/testwrit/testwrit
+uspace/app/tetris/tetris
+uspace/app/top/top
+uspace/app/trace/trace
+uspace/app/usbinfo/usbinfo
+uspace/app/vdemo/vdemo
+uspace/app/vlaunch/images.c
+uspace/app/vlaunch/images.h
+uspace/app/vlaunch/vlaunch
+uspace/app/vterm/vterm
+uspace/app/vuhid/vuh
+uspace/app/wavplay/wavplay
+uspace/app/websrv/websrv
+uspace/dist/app/bdsh
+uspace/dist/app/bithenge
+uspace/dist/app/blkdump
+uspace/dist/app/bnchmark
+uspace/dist/app/date
+uspace/dist/app/devctl
+uspace/dist/app/dnscfg
+uspace/dist/app/dnsres
+uspace/dist/app/edit
+uspace/dist/app/getterm
+uspace/dist/app/inet
+uspace/dist/app/kill
+uspace/dist/app/killall
+uspace/dist/app/klog
+uspace/dist/app/loc
+uspace/dist/app/logset
+uspace/dist/app/mixerctl
+uspace/dist/app/mkbd
+uspace/dist/app/mkexfat
+uspace/dist/app/mkfat
+uspace/dist/app/mkmfs
+uspace/dist/app/netecho
+uspace/dist/app/nettest1
+uspace/dist/app/nettest2
+uspace/dist/app/nettest3
+uspace/dist/app/nterm
+uspace/dist/app/ping
+uspace/dist/app/ping6
+uspace/dist/app/redir
+uspace/dist/app/sbi
+uspace/dist/app/sportdmp
+uspace/dist/app/stats
+uspace/dist/app/sysinfo
+uspace/dist/app/taskdump
+uspace/dist/app/tester
+uspace/dist/app/testread
+uspace/dist/app/testwrit
+uspace/dist/app/tetris
+uspace/dist/app/top
+uspace/dist/app/trace
+uspace/dist/app/usbinfo
+uspace/dist/app/vdemo
+uspace/dist/app/vlaunch
+uspace/dist/app/vterm
+uspace/dist/app/vuh
+uspace/dist/app/wavplay
+uspace/dist/app/websrv
+uspace/dist/drv/ahci/
+uspace/dist/drv/ata_bd/
+uspace/dist/drv/cmos-rtc/
+uspace/dist/drv/e1k/
+uspace/dist/drv/ehci/
+uspace/dist/drv/i8042/
+uspace/dist/drv/isa/
+uspace/dist/drv/kfb/
+uspace/dist/drv/ne2k/
+uspace/dist/drv/ns8250/
+uspace/dist/drv/ohci/
+uspace/dist/drv/pciintel/
+uspace/dist/drv/ps2mouse/
+uspace/dist/drv/root/
+uspace/dist/drv/rootpc/
+uspace/dist/drv/rootvirt/
+uspace/dist/drv/rtl8139/
+uspace/dist/drv/sb16/
+uspace/dist/drv/test1/
+uspace/dist/drv/test2/
+uspace/dist/drv/test3/
+uspace/dist/drv/uhci/
+uspace/dist/drv/uhcirh/
+uspace/dist/drv/usbflbk/
+uspace/dist/drv/usbhid/
+uspace/dist/drv/usbhub/
+uspace/dist/drv/usbmast/
+uspace/dist/drv/usbmid/
+uspace/dist/drv/vhc/
+uspace/dist/drv/xtkbd/
+uspace/dist/srv/apic
+uspace/dist/srv/cdfs
+uspace/dist/srv/clipboard
+uspace/dist/srv/compositor
+uspace/dist/srv/console
+uspace/dist/srv/devman
+uspace/dist/srv/dnsrsrv
+uspace/dist/srv/ethip
+uspace/dist/srv/exfat
+uspace/dist/srv/ext4fs
+uspace/dist/srv/fat
+uspace/dist/srv/file_bd
+uspace/dist/srv/g_part
+uspace/dist/srv/hound
+uspace/dist/srv/i8259
+uspace/dist/srv/inetsrv
+uspace/dist/srv/input
+uspace/dist/srv/isdv4_tablet
+uspace/dist/srv/locfs
+uspace/dist/srv/loopip
+uspace/dist/srv/mbr_part
+uspace/dist/srv/mfs
+uspace/dist/srv/output
+uspace/dist/srv/remcons
+uspace/dist/srv/sata_bd
+uspace/dist/srv/slip
+uspace/dist/srv/taskmon
+uspace/dist/srv/tcp
+uspace/dist/srv/tmpfs
+uspace/dist/srv/udf
+uspace/dist/srv/udp
+uspace/drv/audio/sb16/sb16
+uspace/drv/block/ahci/ahci
+uspace/drv/block/ata_bd/ata_bd
+uspace/drv/bus/isa/isa
+uspace/drv/bus/pci/pciintel/pciintel
+uspace/drv/bus/usb/ehci/ehci
+uspace/drv/bus/usb/ohci/ohci
+uspace/drv/bus/usb/uhci/uhci
+uspace/drv/bus/usb/uhcirh/uhcirh
+uspace/drv/bus/usb/usbflbk/usbflbk
+uspace/drv/bus/usb/usbhid/usbhid
+uspace/drv/bus/usb/usbhub/usbhub
+uspace/drv/bus/usb/usbmast/usbmast
+uspace/drv/bus/usb/usbmid/usbmid
+uspace/drv/bus/usb/vhc/vhc
+uspace/drv/char/i8042/i8042
+uspace/drv/char/ns8250/ns8250
+uspace/drv/char/ps2mouse/ps2mouse
+uspace/drv/char/xtkbd/xtkbd
+uspace/drv/fb/kfb/kfb
+uspace/drv/infrastructure/root/root
+uspace/drv/infrastructure/rootpc/rootpc
+uspace/drv/infrastructure/rootvirt/rootvirt
+uspace/drv/nic/e1k/e1k
+uspace/drv/nic/ne2k/ne2k
+uspace/drv/nic/rtl8139/rtl8139
+uspace/drv/test/test1/test1
+uspace/drv/test/test2/test2
+uspace/drv/test/test3/test3
+uspace/drv/time/cmos-rtc/cmos-rtc
+uspace/lib/c/arch/amd64/_link-dlexe.ld
+uspace/lib/c/arch/amd64/_link-loader.ld
+uspace/lib/c/arch/amd64/_link-shlib.ld
+uspace/lib/c/arch/amd64/_link.ld
+uspace/lib/c/arch/amd64/include/libarch/common.h
+uspace/lib/posix/collisions.list
+uspace/lib/posix/redefs-hide-libc.xargs
+uspace/lib/posix/redefs-show-posix.xargs
+uspace/lib/posix/include/libc
+uspace/srv/audio/hound/hound
+uspace/srv/bd/file_bd/file_bd
+uspace/srv/bd/part/guid_part/g_part
+uspace/srv/bd/part/mbr_part/mbr_part
+uspace/srv/bd/rd/rd
+uspace/srv/bd/sata_bd/sata_bd
+uspace/srv/clipboard/clipboard
+uspace/srv/devman/devman
+uspace/srv/fs/cdfs/cdfs
+uspace/srv/fs/exfat/exfat
+uspace/srv/fs/ext4fs/ext4fs
+uspace/srv/fs/fat/fat
+uspace/srv/fs/locfs/locfs
+uspace/srv/fs/mfs/mfs
+uspace/srv/fs/tmpfs/tmpfs
+uspace/srv/fs/udf/udf
+uspace/srv/hid/compositor/compositor
+uspace/srv/hid/compositor/images.c
+uspace/srv/hid/compositor/images.h
+uspace/srv/hid/console/console
+uspace/srv/hid/input/input
+uspace/srv/hid/isdv4_tablet/isdv4_tablet
+uspace/srv/hid/output/output
+uspace/srv/hid/remcons/remcons
+uspace/srv/hid/s3c24xx_ts/s3c24xx_ts
+uspace/srv/hw/char/s3c24xx_uart/s3c24ser
+uspace/srv/hw/irc/apic/apic
+uspace/srv/hw/irc/i8259/i8259
+uspace/srv/loader/loader
+uspace/srv/locsrv/locsrv
+uspace/srv/logger/logger
+uspace/srv/net/dnsrsrv/dnsrsrv
+uspace/srv/net/ethip/ethip
+uspace/srv/net/inetsrv/inetsrv
+uspace/srv/net/loopip/loopip
+uspace/srv/net/slip/slip
+uspace/srv/net/tcp/tcp
+uspace/srv/net/udp/udp
+uspace/srv/ns/ns
+uspace/srv/taskmon/taskmon
+uspace/srv/vfs/vfs
+kernel/arch/ia32/_link.ld
+uspace/lib/c/arch/ia32/_link-dlexe.ld
+uspace/lib/c/arch/ia32/_link-loader.ld
+uspace/lib/c/arch/ia32/_link-shlib.ld
+uspace/lib/c/arch/ia32/_link.ld
+kernel/arch/arm32/_link.ld
+uspace/app/corecfg/corecfg
+uspace/dist/app/corecfg
+uspace/dist/srv/rfb
+uspace/srv/hid/rfb/rfb
+uspace/dist/srv/dhcp
+uspace/srv/net/dhcp/dhcp
+uspace/dist/srv/nconfsrv
+uspace/srv/net/nconfsrv/nconfsrv
+uspace/app/netspeed/netspeed
+uspace/dist/app/netspeed
+uspace/app/df/df
+uspace/dist/app/df
+uspace/app/download/download
+uspace/dist/app/download
+uspace/app/viewer/viewer
+uspace/dist/app/viewer
Index: abi/include/abi/ipc/ipc.h
===================================================================
--- abi/include/abi/ipc/ipc.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ abi/include/abi/ipc/ipc.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -59,4 +59,10 @@
 /** Interrupt notification */
 #define IPC_CALL_NOTIF  (1 << 3)
+
+/**
+ * Maximum buffer size allowed for IPC_M_DATA_WRITE and
+ * IPC_M_DATA_READ requests.
+ */
+#define DATA_XFER_LIMIT  (64 * 1024)
 
 
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ boot/Makefile.common	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -112,8 +112,11 @@
 	$(USPACE_PATH)/srv/hid/remcons/remcons \
 	$(USPACE_PATH)/srv/hid/isdv4_tablet/isdv4_tablet \
+	$(USPACE_PATH)/srv/hid/rfb/rfb \
+	$(USPACE_PATH)/srv/net/dhcp/dhcp \
 	$(USPACE_PATH)/srv/net/dnsrsrv/dnsrsrv \
 	$(USPACE_PATH)/srv/net/ethip/ethip \
 	$(USPACE_PATH)/srv/net/inetsrv/inetsrv \
 	$(USPACE_PATH)/srv/net/loopip/loopip \
+	$(USPACE_PATH)/srv/net/nconfsrv/nconfsrv \
 	$(USPACE_PATH)/srv/net/slip/slip \
 	$(USPACE_PATH)/srv/net/tcp/tcp \
@@ -121,6 +124,8 @@
 	$(USPACE_PATH)/srv/taskmon/taskmon
 
-RD_DRVS = \
-	infrastructure/root \
+RD_DRVS_ESSENTIAL = \
+	infrastructure/root
+
+RD_DRVS_NON_ESSENTIAL = \
 	infrastructure/rootvirt \
 	fb/kfb \
@@ -163,4 +168,5 @@
 	$(USPACE_PATH)/app/blkdump/blkdump \
 	$(USPACE_PATH)/app/bnchmark/bnchmark \
+	$(USPACE_PATH)/app/corecfg/corecfg \
 	$(USPACE_PATH)/app/devctl/devctl \
 	$(USPACE_PATH)/app/dltest/dltest \
@@ -169,4 +175,5 @@
 	$(USPACE_PATH)/app/dnscfg/dnscfg \
 	$(USPACE_PATH)/app/dnsres/dnsres \
+	$(USPACE_PATH)/app/download/download \
 	$(USPACE_PATH)/app/edit/edit \
 	$(USPACE_PATH)/app/inet/inet \
@@ -174,4 +181,5 @@
 	$(USPACE_PATH)/app/killall/killall \
 	$(USPACE_PATH)/app/loc/loc \
+	$(USPACE_PATH)/app/mixerctl/mixerctl \
 	$(USPACE_PATH)/app/logset/logset \
 	$(USPACE_PATH)/app/mkfat/mkfat \
@@ -191,7 +199,7 @@
 	$(USPACE_PATH)/app/nettest3/nettest3 \
 	$(USPACE_PATH)/app/netecho/netecho \
+	$(USPACE_PATH)/app/netspeed/netspeed \
 	$(USPACE_PATH)/app/nterm/nterm \
 	$(USPACE_PATH)/app/ping/ping \
-	$(USPACE_PATH)/app/ping6/ping6 \
 	$(USPACE_PATH)/app/stats/stats \
 	$(USPACE_PATH)/app/sysinfo/sysinfo \
@@ -202,5 +210,7 @@
 	$(USPACE_PATH)/app/websrv/websrv \
 	$(USPACE_PATH)/app/date/date \
-	$(USPACE_PATH)/app/vdemo/vdemo
+	$(USPACE_PATH)/app/vdemo/vdemo \
+	$(USPACE_PATH)/app/viewer/viewer \
+	$(USPACE_PATH)/app/df/df
 
 ifeq ($(CONFIG_PCC),y)
@@ -237,7 +247,9 @@
 RD_SRVS = $(RD_SRVS_ESSENTIAL)
 RD_APPS = $(RD_APPS_ESSENTIAL)
+RD_DRVS = $(RD_DRVS_ESSENTIAL)
 else
 RD_SRVS = $(RD_SRVS_ESSENTIAL) $(RD_SRVS_NON_ESSENTIAL)
 RD_APPS = $(RD_APPS_ESSENTIAL) $(RD_APPS_NON_ESSENTIAL)
+RD_DRVS = $(RD_DRVS_ESSENTIAL) $(RD_DRVS_NON_ESSENTIAL)
 endif
 
Index: boot/arch/amd64/Makefile.inc
===================================================================
--- boot/arch/amd64/Makefile.inc	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ boot/arch/amd64/Makefile.inc	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -28,16 +28,22 @@
 
 RD_SRVS_ESSENTIAL += \
+	$(USPACE_PATH)/srv/audio/hound/hound \
+	$(USPACE_PATH)/srv/devman/devman \
 	$(USPACE_PATH)/srv/hw/irc/apic/apic \
 	$(USPACE_PATH)/srv/hw/irc/i8259/i8259
 
-RD_DRVS += \
+
+RD_DRVS_ESSENTIAL += \
 	infrastructure/rootpc \
 	block/ata_bd \
 	bus/pci/pciintel \
 	bus/isa \
+	audio/sb16 \
 	char/i8042 \
+	char/ps2mouse \
+	char/xtkbd
+
+RD_DRVS_NON_ESSENTIAL += \
 	char/ns8250 \
-	char/ps2mouse \
-	char/xtkbd \
 	time/cmos-rtc \
 	bus/usb/ehci\
@@ -55,4 +61,9 @@
 	bus/isa
 
+RD_APPS_ESSENTIAL += \
+	$(USPACE_PATH)/app/edit/edit \
+	$(USPACE_PATH)/app/mixerctl/mixerctl \
+	$(USPACE_PATH)/app/wavplay/wavplay \
+	
 BOOT_OUTPUT = $(ROOT_PATH)/image.iso
 PREBUILD = $(INITRD).img
Index: boot/arch/arm32/Makefile.inc
===================================================================
--- boot/arch/arm32/Makefile.inc	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ boot/arch/arm32/Makefile.inc	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -57,5 +57,5 @@
 endif
 
-RD_DRVS += \
+RD_DRVS_ESSENTIAL += \
 	infrastructure/rootamdm37x \
 	fb/amdm37x_dispc \
Index: boot/arch/arm32/include/cp15.h
===================================================================
--- boot/arch/arm32/include/cp15.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ boot/arch/arm32/include/cp15.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,1 @@
+../../../../kernel/arch/arm32/include/arch/cp15.h
Index: boot/arch/arm32/src/asm.S
===================================================================
--- boot/arch/arm32/src/asm.S	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ boot/arch/arm32/src/asm.S	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -56,5 +56,4 @@
 jump_to_kernel:
 	#
-	# TODO
 	# Make sure that the I-cache, D-cache and memory are mutually coherent
 	# before passing control to the copied code.
@@ -68,10 +67,19 @@
 #define CP15_C1_BP		11
 #define CP15_C1_DC		2
-	# Disable I-cache and D-cache before the kernel is started.
+
+
+#ifndef PROCESSOR_ARCH_armv7_a
 	mrc	p15, 0, r4, c1, c0, 0
+	
+	# D-cache before the kernel is started.
 	bic	r4, r4, #(1 << CP15_C1_DC)
+
+	# Disable I-cache and Branche predictors.
 	bic	r4, r4, #(1 << CP15_C1_IC)
 	bic	r4, r4, #(1 << CP15_C1_BP)
+	
 	mcr	p15, 0, r4, c1, c0, 0
+#endif
+
 
 	
@@ -81,8 +89,10 @@
 #else
 	#cp15 dsb, r4 is ignored (should be zero)
+	mov r4, #0
 	mcr p15, 0, r4, c7, c10, 4
 #endif
 	
 	# Clean ICache and BPredictors, r4 ignored (SBZ)
+	mov r4, #0
 	mcr p15, 0, r4, c7, c5, 0
 	nop
Index: boot/arch/arm32/src/main.c
===================================================================
--- boot/arch/arm32/src/main.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ boot/arch/arm32/src/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -53,25 +53,9 @@
 extern void *bdata_end;
 
-
-static inline void invalidate_icache(void)
-{
-	/* ICIALLU Invalidate entire ICache */
-	asm volatile ("mov r0, #0\n" "mcr p15, 0, r0, c7, c5, 0\n" ::: "r0" );
-}
-
-static inline void invalidate_dcache(void *address, size_t size)
-{
-	const uintptr_t addr = (uintptr_t)address;
-	/* DCIMVAC - invalidate by address to the point of coherence */
-	for (uintptr_t a = addr; a < addr + size; a += 4) {
-		asm volatile ("mcr p15, 0, %[a], c7, c6, 1\n" :: [a]"r"(a) : );
-	}
-}
-
 static inline void clean_dcache_poc(void *address, size_t size)
 {
 	const uintptr_t addr = (uintptr_t)address;
-	/* DCCMVAC - clean by address to the point of coherence */
 	for (uintptr_t a = addr; a < addr + size; a += 4) {
+		/* DCCMVAC - clean by address to the point of coherence */
 		asm volatile ("mcr p15, 0, %[a], c7, c10, 1\n" :: [a]"r"(a) : );
 	}
@@ -82,11 +66,4 @@
 void bootstrap(void)
 {
-	/* Make sure  we run in memory code when caches are enabled,
-	 * make sure we read memory data too. This part is ARMv7 specific as
-	 * ARMv7 no longer invalidates caches on restart.
-	 * See chapter B2.2.2 of ARM Architecture Reference Manual p. B2-1263*/
-	invalidate_icache();
-	invalidate_dcache(&bdata_start, &bdata_end - &bdata_start);
-
 	/* Enable MMU and caches */
 	mmu_start();
@@ -105,5 +82,4 @@
 		    components[i].start, components[i].name, components[i].inflated,
 		    components[i].size);
-		invalidate_dcache(components[i].start, components[i].size);
 	}
 	
@@ -148,4 +124,5 @@
 			halt();
 		}
+		/* Make sure data are in the memory, ICache will need them */
 		clean_dcache_poc(dest[i - 1], components[i - 1].inflated);
 	}
Index: boot/arch/arm32/src/mm.c
===================================================================
--- boot/arch/arm32/src/mm.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ boot/arch/arm32/src/mm.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,4 +37,50 @@
 #include <arch/asm.h>
 #include <arch/mm.h>
+#include <arch/cp15.h>
+
+#ifdef PROCESSOR_ARCH_armv7_a
+static unsigned log2(unsigned val)
+{
+	unsigned log = 0;
+	while (val >> log++);
+	return log - 2;
+}
+
+static void dcache_invalidate_level(unsigned level)
+{
+	CSSELR_write(level << 1);
+	const uint32_t ccsidr = CCSIDR_read();
+	const unsigned sets = CCSIDR_SETS(ccsidr);
+	const unsigned ways = CCSIDR_WAYS(ccsidr);
+	const unsigned line_log = CCSIDR_LINESIZE_LOG(ccsidr);
+	const unsigned set_shift = line_log;
+	const unsigned way_shift = 32 - log2(ways);
+
+	for (unsigned k = 0; k < ways; ++k)
+		for (unsigned j = 0; j < sets; ++j) {
+			const uint32_t val = (level << 1) |
+			    (j << set_shift) | (k << way_shift);
+			DCISW_write(val);
+		}
+}
+
+/** invalidate all dcaches -- armv7 */
+static void cache_invalidate(void)
+{
+	const uint32_t cinfo = CLIDR_read();
+	for (unsigned i = 0; i < 7; ++i) {
+		switch (CLIDR_CACHE(i, cinfo))
+		{
+		case CLIDR_DCACHE_ONLY:
+		case CLIDR_SEP_CACHE:
+		case CLIDR_UNI_CACHE:
+			dcache_invalidate_level(i);
+		}
+	}
+	asm volatile ( "dsb\n" );
+	ICIALLU_write(0);
+	asm volatile ( "isb\n" );
+}
+#endif
 
 /** Disable the MMU */
@@ -60,21 +106,16 @@
 static inline int section_cacheable(pfn_t section)
 {
+	const unsigned long address = section << PTE_SECTION_SHIFT;
 #ifdef MACHINE_gta02
-	unsigned long address = section << PTE_SECTION_SHIFT;
-
-	if (address >= GTA02_IOMEM_START && address < GTA02_IOMEM_END)
-		return 0;
-	else
+	if (address < GTA02_IOMEM_START || address >= GTA02_IOMEM_END)
 		return 1;
 #elif defined MACHINE_beagleboardxm
-	const unsigned long address = section << PTE_SECTION_SHIFT;
 	if (address >= BBXM_RAM_START && address < BBXM_RAM_END)
 		return 1;
 #elif defined MACHINE_beaglebone
-	const unsigned long address = section << PTE_SECTION_SHIFT;
 	if (address >= AM335x_RAM_START && address < AM335x_RAM_END)
 		return 1;
 #endif
-	return 0;
+	return address * 0;
 }
 
@@ -95,11 +136,23 @@
 {
 	pte->descriptor_type = PTE_DESCRIPTOR_SECTION;
-	pte->bufferable = 1;
-	pte->cacheable = section_cacheable(frame);
 	pte->xn = 0;
 	pte->domain = 0;
 	pte->should_be_zero_1 = 0;
 	pte->access_permission_0 = PTE_AP_USER_NO_KERNEL_RW;
+#ifdef PROCESSOR_ARCH_armv7_a
+	/*
+	 * Keeps this setting in sync with memory type attributes in:
+	 * init_boot_pt (boot/arch/arm32/src/mm.c)
+	 * set_pt_level1_flags (kernel/arch/arm32/include/arch/mm/page_armv6.h)
+	 * set_ptl0_addr (kernel/arch/arm32/include/arch/mm/page.h)
+	 */
+	pte->tex = section_cacheable(frame) ? 5 : 0;
+	pte->cacheable = section_cacheable(frame) ? 0 : 0;
+	pte->bufferable = section_cacheable(frame) ? 1 : 0;
+#else
+	pte->bufferable = 1;
+	pte->cacheable = section_cacheable(frame);
 	pte->tex = 0;
+#endif
 	pte->access_permission_1 = 0;
 	pte->shareable = 0;
@@ -113,14 +166,26 @@
 static void init_boot_pt(void)
 {
-	const pfn_t split_page = PTL0_ENTRIES;
-	/* Create 1:1 virtual-physical mapping (in lower 2 GB). */
-	pfn_t page;
-	for (page = 0; page < split_page; page++)
+	/*
+	 * Create 1:1 virtual-physical mapping.
+	 * Physical memory on BBxM a BBone starts at 2GB
+	 * boundary, icp has a memory mirror at 2GB.
+	 * (ARM Integrator Core Module User guide ch. 6.3,  p. 6-7)
+	 * gta02 somehow works (probably due to limited address size),
+	 * s3c2442b manual ch. 5, p.5-1:
+	 * "Address space: 128Mbytes per bank (total 1GB/8 banks)"
+	 */
+	for (pfn_t page = 0; page < PTL0_ENTRIES; ++page)
 		init_ptl0_section(&boot_pt[page], page);
-	
-	asm volatile (
-		"mcr p15, 0, %[pt], c2, c0, 0\n"
-		:: [pt] "r" (boot_pt)
-	);
+
+	/*
+	 * Tell MMU page might be cached. Keeps this setting in sync
+	 * with memory type attributes in:
+	 * init_ptl0_section (boot/arch/arm32/src/mm.c)
+	 * set_pt_level1_flags (kernel/arch/arm32/include/arch/mm/page_armv6.h)
+	 * set_ptl0_addr (kernel/arch/arm32/include/arch/mm/page.h)
+	 */
+	uint32_t val = (uint32_t)boot_pt & TTBR_ADDR_MASK;
+	val |= TTBR_RGN_WBWA_CACHE | TTBR_C_FLAG;
+	TTBR0_write(val);
 }
 
@@ -141,6 +206,12 @@
 		 * we disable caches before jumping to kernel
 		 * so this is safe for all archs.
+		 * Enable VMSAv6 the bit (23) is only writable on ARMv6.
+		 * (and QEMU)
 		 */
+#ifdef PROCESSOR_ARCH_armv6
+		"ldr r1, =0x00801805\n"
+#else
 		"ldr r1, =0x00001805\n"
+#endif
 		
 		"orr r0, r0, r1\n"
@@ -160,4 +231,11 @@
 void mmu_start() {
 	disable_paging();
+#ifdef PROCESSOR_ARCH_armv7_a
+	/* Make sure we run in memory code when caches are enabled,
+	 * make sure we read memory data too. This part is ARMv7 specific as
+	 * ARMv7 no longer invalidates caches on restart.
+	 * See chapter B2.2.2 of ARM Architecture Reference Manual p. B2-1263*/
+	cache_invalidate();
+#endif
 	init_boot_pt();
 	enable_paging();
Index: boot/arch/mips32/Makefile.inc
===================================================================
--- boot/arch/mips32/Makefile.inc	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ boot/arch/mips32/Makefile.inc	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -50,4 +50,17 @@
 endif
 
+ifeq ($(MACHINE), $(filter $(MACHINE),bmalta lmalta))
+	RD_DRVS_ESSENTIAL += \
+		infrastructure/rootmalta \
+		block/ata_bd \
+		bus/pci/pciintel \
+		bus/isa \
+		char/i8042 \
+		char/ps2mouse \
+		char/xtkbd
+
+	RD_DRV_CFG += \
+		bus/isa
+endif
 
 SOURCES = \
Index: boot/arch/ppc32/Makefile.inc
===================================================================
--- boot/arch/ppc32/Makefile.inc	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ boot/arch/ppc32/Makefile.inc	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -42,5 +42,5 @@
 	$(USPACE_PATH)/srv/hw/bus/cuda_adb/cuda_adb
 
-RD_DRVS += \
+RD_DRVS_ESSENTIAL += \
 	infrastructure/rootmac \
 	bus/pci/pciintel \
Index: boot/arch/sparc64/src/asm.S
===================================================================
--- boot/arch/sparc64/src/asm.S	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ boot/arch/sparc64/src/asm.S	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -30,8 +30,10 @@
 #include <arch/arch.h>
 
+#if defined(PROCESSOR_us) || defined(PROCESSOR_us3)
 #define ICACHE_SIZE       8192
 #define ICACHE_LINE_SIZE  32
 #define ICACHE_SET_BIT    (1 << 13)
 #define ASI_ICACHE_TAG    0x67
+#endif	/* PROCESSOR_us || PROCESSOR_us3 */
 
 .register %g2, #scratch
@@ -134,4 +136,5 @@
 # Flush I-cache
 icache_flush:
+#if defined(PROCESSOR_us) || defined(PROCESSOR_us3)
 	set ((ICACHE_SIZE - ICACHE_LINE_SIZE) | ICACHE_SET_BIT), %g1
 	stxa %g0, [%g1] ASI_ICACHE_TAG
@@ -149,4 +152,9 @@
 	
 	nop
+#else
+	// TODO: sun4v
+	retl
+	nop
+#endif	/* PROCESSOR_us || PROCESSOR_us3 */
 
 .global ofw
Index: contrib/conf/ia32-qe.sh
===================================================================
--- contrib/conf/ia32-qe.sh	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ contrib/conf/ia32-qe.sh	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -20,3 +20,3 @@
 fi
 
-"${QEMU_BINARY}" "$@" -m 32 -hda "$DISK_IMG" -cdrom image.iso -boot d
+"${QEMU_BINARY}" "$@" -m 64 -hda "$DISK_IMG" -cdrom image.iso -boot d -soundhw sb16
Index: defaults/sparc64/niagara/Makefile.config
===================================================================
--- defaults/sparc64/niagara/Makefile.config	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ defaults/sparc64/niagara/Makefile.config	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -10,2 +10,5 @@
 # Framebuffer support
 CONFIG_FB = n
+
+# Barebone build with essential binaries only
+CONFIG_BAREBONE = y
Index: kernel/Makefile
===================================================================
--- kernel/Makefile	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -226,5 +226,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/frame.h
===================================================================
--- kernel/arch/abs32le/include/arch/mm/frame.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/abs32le/include/arch/mm/frame.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,4 +39,6 @@
 #define FRAME_SIZE   (1 << FRAME_WIDTH)
 
+#define FRAME_LOWPRIO  0
+
 #include <typedefs.h>
 
Index: kernel/arch/abs32le/include/arch/mm/page.h
===================================================================
--- kernel/arch/abs32le/include/arch/mm/page.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/abs32le/include/arch/mm/page.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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/frame.h
===================================================================
--- kernel/arch/amd64/include/arch/mm/frame.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/amd64/include/arch/mm/frame.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,4 +39,6 @@
 #define FRAME_SIZE   (1 << FRAME_WIDTH)
 
+#define FRAME_LOWPRIO  0x1000
+
 #ifndef __ASM__
 
Index: kernel/arch/amd64/include/arch/mm/page.h
===================================================================
--- kernel/arch/amd64/include/arch/mm/page.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/amd64/include/arch/mm/page.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/amd64/src/ddi/ddi.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -42,4 +42,5 @@
 #include <errno.h>
 #include <arch/cpu.h>
+#include <cpu.h>
 #include <arch.h>
 #include <align.h>
@@ -58,27 +59,28 @@
 int ddi_iospace_enable_arch(task_t *task, uintptr_t ioaddr, size_t size)
 {
-	size_t bits = ioaddr + size;
-	if (bits > IO_PORTS)
+	size_t elements = ioaddr + size;
+	if (elements > IO_PORTS)
 		return ENOENT;
 	
-	if (task->arch.iomap.bits < bits) {
+	if (task->arch.iomap.elements < elements) {
 		/*
 		 * The I/O permission bitmap is too small and needs to be grown.
 		 */
 		
-		uint8_t *newmap = (uint8_t *) malloc(BITS2BYTES(bits), FRAME_ATOMIC);
-		if (!newmap)
+		void *store = malloc(bitmap_size(elements), FRAME_ATOMIC);
+		if (!store)
 			return ENOMEM;
 		
 		bitmap_t oldiomap;
-		bitmap_initialize(&oldiomap, task->arch.iomap.map,
+		bitmap_initialize(&oldiomap, task->arch.iomap.elements,
 		    task->arch.iomap.bits);
-		bitmap_initialize(&task->arch.iomap, newmap, bits);
+		
+		bitmap_initialize(&task->arch.iomap, elements, store);
 		
 		/*
 		 * Mark the new range inaccessible.
 		 */
-		bitmap_set_range(&task->arch.iomap, oldiomap.bits,
-		    bits - oldiomap.bits);
+		bitmap_set_range(&task->arch.iomap, oldiomap.elements,
+		    elements - oldiomap.elements);
 		
 		/*
@@ -88,6 +90,7 @@
 		if (oldiomap.bits) {
 			bitmap_copy(&task->arch.iomap, &oldiomap,
-			    oldiomap.bits);
-			free(oldiomap.map);
+			    oldiomap.elements);
+			
+			free(oldiomap.bits);
 		}
 	}
@@ -96,5 +99,5 @@
 	 * Enable the range and we are done.
 	 */
-	bitmap_clear_range(&task->arch.iomap, (size_t) ioaddr, (size_t) size);
+	bitmap_clear_range(&task->arch.iomap, (size_t) ioaddr, size);
 	
 	/*
@@ -118,13 +121,15 @@
 	/* First, copy the I/O Permission Bitmap. */
 	irq_spinlock_lock(&TASK->lock, false);
+	
 	size_t ver = TASK->arch.iomapver;
-	size_t bits = TASK->arch.iomap.bits;
-	if (bits) {
-		ASSERT(TASK->arch.iomap.map);
+	size_t elements = TASK->arch.iomap.elements;
+	
+	if (elements > 0) {
+		ASSERT(TASK->arch.iomap.bits);
 		
 		bitmap_t iomap;
-		bitmap_initialize(&iomap, CPU->arch.tss->iomap,
-		    TSS_IOMAP_SIZE * 8);
-		bitmap_copy(&iomap, &TASK->arch.iomap, bits);
+		bitmap_initialize(&iomap, TSS_IOMAP_SIZE * 8,
+		    CPU->arch.tss->iomap);
+		bitmap_copy(&iomap, &TASK->arch.iomap, elements);
 		
 		/*
@@ -132,16 +137,19 @@
 		 * I/O access.
 		 */
-		bitmap_set_range(&iomap, bits, ALIGN_UP(bits, 8) - bits);
+		bitmap_set_range(&iomap, elements,
+		    ALIGN_UP(elements, 8) - elements);
+		
 		/*
 		 * It is safe to set the trailing eight bits because of the
 		 * extra convenience byte in TSS_IOMAP_SIZE.
 		 */
-		bitmap_set_range(&iomap, ALIGN_UP(bits, 8), 8);
+		bitmap_set_range(&iomap, ALIGN_UP(elements, 8), 8);
 	}
+	
 	irq_spinlock_unlock(&TASK->lock, false);
 	
 	/*
 	 * Second, adjust TSS segment limit.
-	 * Take the extra ending byte will all bits set into account. 
+	 * Take the extra ending byte with all bits set into account.
 	 */
 	ptr_16_64_t cpugdtr;
@@ -149,5 +157,6 @@
 	
 	descriptor_t *gdt_p = (descriptor_t *) cpugdtr.base;
-	gdt_tss_setlimit(&gdt_p[TSS_DES], TSS_BASIC_SIZE + BITS2BYTES(bits));
+	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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/amd64/src/proc/task.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -34,6 +34,7 @@
 
 #include <proc/task.h>
+#include <typedefs.h>
+#include <adt/bitmap.h>
 #include <mm/slab.h>
-#include <typedefs.h>
 
 /** Perform amd64 specific task initialization.
@@ -45,5 +46,5 @@
 {
 	task->arch.iomapver = 0;
-	bitmap_initialize(&task->arch.iomap, NULL, 0);
+	bitmap_initialize(&task->arch.iomap, 0, NULL);
 }
 
@@ -55,6 +56,6 @@
 void task_destroy_arch(task_t *task)
 {
-	if (task->arch.iomap.map)
-		free(task->arch.iomap.map);
+	if (task->arch.iomap.bits != NULL)
+		free(task->arch.iomap.bits);
 }
 
Index: kernel/arch/arm32/include/arch/asm.h
===================================================================
--- kernel/arch/arm32/include/arch/asm.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/include/arch/asm.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,4 +38,5 @@
 
 #include <typedefs.h>
+#include <arch/cp15.h>
 #include <arch/stack.h>
 #include <config.h>
@@ -51,7 +52,7 @@
  * chapter 2.3.8 p.2-22 (52 in the PDF)
  *
- * @note Although mcr p15, 0, R0, c7, c0, 4 is defined in ARM Architecture
- * reference manual for armv4/5 CP15 implementation is mandatory only for
- * armv6+.
+ * @note Although CP15WFI (mcr p15, 0, R0, c7, c0, 4) is defined in ARM
+ * Architecture reference manual for armv4/5, CP15 implementation is mandatory
+ * only for armv6+.
  */
 NO_TRACE static inline void cpu_sleep(void)
@@ -60,5 +61,5 @@
 	asm volatile ( "wfe" );
 #elif defined(PROCESSOR_ARCH_armv6) | defined(PROCESSOR_arm926ej_s) | defined(PROCESSOR_arm920t)
-	asm volatile ( "mcr p15, 0, R0, c7, c0, 4" );
+	WFI_write(0);
 #endif
 }
Index: kernel/arch/arm32/include/arch/cp15.h
===================================================================
--- kernel/arch/arm32/include/arch/cp15.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/include/arch/cp15.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -171,4 +171,11 @@
 	CCSIDR_LINESIZE_MASK = 0x7,
 	CCSIDR_LINESIZE_SHIFT = 0,
+#define CCSIDR_SETS(val) \
+	(((val >> CCSIDR_NUMSETS_SHIFT) & CCSIDR_NUMSETS_MASK) + 1)
+#define CCSIDR_WAYS(val) \
+	(((val >> CCSIDR_ASSOC_SHIFT) & CCSIDR_ASSOC_MASK) + 1)
+/* The register value is log(linesize_in_words) - 2 */
+#define CCSIDR_LINESIZE_LOG(val) \
+	(((val >> CCSIDR_LINESIZE_SHIFT) & CCSIDR_LINESIZE_MASK) + 2 + 2)
 };
 CONTROL_REG_GEN_READ(CCSIDR, c0, 1, c0, 0);
@@ -187,5 +194,6 @@
 	CLIDR_UNI_CACHE = 0x4,
 	CLIDR_CACHE_MASK = 0x7,
-#define CLIDR_CACHE(level, val)   ((val >> (level - 1) * 3) & CLIDR_CACHE_MASK)
+/** levels counted from 0 */
+#define CLIDR_CACHE(level, val)   ((val >> (level * 3)) & CLIDR_CACHE_MASK)
 };
 CONTROL_REG_GEN_READ(CLIDR, c0, 1, c0, 1);
@@ -293,4 +301,15 @@
 
 /* Memory protection and control registers */
+enum {
+	TTBR_ADDR_MASK = 0xffffff80,
+	TTBR_NOS_FLAG = 1 << 5,
+	TTBR_RGN_MASK = 0x3 << 3,
+	TTBR_RGN_NO_CACHE = 0x0 << 3,
+	TTBR_RGN_WBWA_CACHE = 0x1 << 3,
+	TTBR_RGN_WT_CACHE = 0x2 << 3,
+	TTBR_RGN_WB_CACHE = 0x3 << 3,
+	TTBR_S_FLAG = 1 << 1,
+	TTBR_C_FLAG = 1 << 0,
+};
 CONTROL_REG_GEN_READ(TTBR0, c2, 0, c0, 0);
 CONTROL_REG_GEN_WRITE(TTBR0, c2, 0, c0, 0);
@@ -363,5 +382,5 @@
 
 CONTROL_REG_GEN_WRITE(DCIMVAC, c7, 0, c6, 1);
-CONTROL_REG_GEN_WRITE(DCIMSW, c7, 0, c6, 2);
+CONTROL_REG_GEN_WRITE(DCISW, c7, 0, c6, 2);
 
 CONTROL_REG_GEN_WRITE(ATS1CPR, c7, 0, c8, 0);
@@ -369,8 +388,8 @@
 CONTROL_REG_GEN_WRITE(ATS1CUR, c7, 0, c8, 2);
 CONTROL_REG_GEN_WRITE(ATS1CUW, c7, 0, c8, 3);
-CONTROL_REG_GEN_WRITE(ATS1NSOPR, c7, 0, c8, 4);
-CONTROL_REG_GEN_WRITE(ATS1NSOPW, c7, 0, c8, 5);
-CONTROL_REG_GEN_WRITE(ATS1NSOUR, c7, 0, c8, 6);
-CONTROL_REG_GEN_WRITE(ATS1NSOUW, c7, 0, c8, 7);
+CONTROL_REG_GEN_WRITE(ATS12NSOPR, c7, 0, c8, 4);
+CONTROL_REG_GEN_WRITE(ATS12NSOPW, c7, 0, c8, 5);
+CONTROL_REG_GEN_WRITE(ATS12NSOUR, c7, 0, c8, 6);
+CONTROL_REG_GEN_WRITE(ATS12NSOUW, c7, 0, c8, 7);
 
 
Index: kernel/arch/arm32/include/arch/mm/frame.h
===================================================================
--- kernel/arch/arm32/include/arch/mm/frame.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/include/arch/mm/frame.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,4 +39,6 @@
 #define FRAME_WIDTH  12  /* 4KB frames */
 #define FRAME_SIZE   (1 << FRAME_WIDTH)
+
+#define FRAME_LOWPRIO  0
 
 #ifndef __ASM__
Index: kernel/arch/arm32/include/arch/mm/page.h
===================================================================
--- kernel/arch/arm32/include/arch/mm/page.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/include/arch/mm/page.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -41,4 +41,5 @@
 #include <arch/exception.h>
 #include <arch/barrier.h>
+#include <arch/cp15.h>
 #include <trace.h>
 
@@ -72,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. */
@@ -95,11 +96,11 @@
 /* Set PTE address accessors for each level. */
 #define SET_PTL0_ADDRESS_ARCH(ptl0) \
-        (set_ptl0_addr((pte_t *) (ptl0)))
+	set_ptl0_addr((pte_t *) (ptl0))
 #define SET_PTL1_ADDRESS_ARCH(ptl0, i, a) \
-        (((pte_t *) (ptl0))[(i)].l0.coarse_table_addr = (a) >> 10)
+	set_ptl1_addr((pte_t*) (ptl0), i, a)
 #define SET_PTL2_ADDRESS_ARCH(ptl1, i, a)
 #define SET_PTL3_ADDRESS_ARCH(ptl2, i, a)
 #define SET_FRAME_ADDRESS_ARCH(ptl3, i, a) \
-        (((pte_t *) (ptl3))[(i)].l1.frame_base_addr = (a) >> 12)
+	set_ptl3_addr((pte_t*) (ptl3), i, a)
 
 /* Get PTE flags accessors for each level. */
@@ -129,4 +130,7 @@
 	set_pt_level1_present((pte_t *) (ptl3), (size_t) (i))
 
+
+#define pt_coherence(page) pt_coherence_m(page, 1)
+
 #if defined(PROCESSOR_ARCH_armv6) | defined(PROCESSOR_ARCH_armv7_a)
 #include "page_armv6.h"
@@ -137,4 +141,33 @@
 #endif
 
+/** Sets the address of level 0 page table.
+ *
+ * @param pt Pointer to the page table to set.
+ *
+ * Page tables are always in cacheable memory.
+ * Make sure the memory type is correct, and in sync with:
+ * init_boot_pt (boot/arch/arm32/src/mm.c)
+ * init_ptl0_section (boot/arch/arm32/src/mm.c)
+ * set_pt_level1_flags (kernel/arch/arm32/include/arch/mm/page_armv6.h)
+ */
+NO_TRACE static inline void set_ptl0_addr(pte_t *pt)
+{
+	uint32_t val = (uint32_t)pt & TTBR_ADDR_MASK;
+	val |= TTBR_RGN_WBWA_CACHE | TTBR_C_FLAG;
+	TTBR0_write(val);
+}
+
+NO_TRACE static inline void set_ptl1_addr(pte_t *pt, size_t i, uintptr_t address)
+{
+	pt[i].l0.coarse_table_addr = address >> 10;
+	pt_coherence(&pt[i].l0);
+}
+
+NO_TRACE static inline void set_ptl3_addr(pte_t *pt, size_t i, uintptr_t address)
+{
+	pt[i].l1.frame_base_addr = address >> 12;
+	pt_coherence(&pt[i].l1);
+}
+
 #endif
 
Index: kernel/arch/arm32/include/arch/mm/page_armv4.h
===================================================================
--- kernel/arch/arm32/include/arch/mm/page_armv4.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/include/arch/mm/page_armv4.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -120,18 +120,10 @@
 #define PTE_DESCRIPTOR_SMALL_PAGE	2
 
-
-/** Sets the address of level 0 page table.
- *
- * @param pt Pointer to the page table to set.
- *
- */
-NO_TRACE static inline void set_ptl0_addr(pte_t *pt)
-{
-	asm volatile (
-		"mcr p15, 0, %[pt], c2, c0, 0\n"
-		:: [pt] "r" (pt)
-	);
-}
-
+#define pt_coherence_m(pt, count) \
+do { \
+	for (unsigned i = 0; i < count; ++i) \
+		DCCMVAU_write((uintptr_t)(pt + i)); \
+	read_barrier(); \
+} while (0)
 
 /** Returns level 0 page table entry flags.
@@ -223,5 +215,5 @@
 	
 	/* default access permission */
-	p->access_permission_0 = p->access_permission_1 = 
+	p->access_permission_0 = p->access_permission_1 =
 	    p->access_permission_2 = p->access_permission_3 =
 	    PTE_AP_USER_NO_KERNEL_RW;
@@ -229,12 +221,12 @@
 	if (flags & PAGE_USER)  {
 		if (flags & PAGE_READ) {
-			p->access_permission_0 = p->access_permission_1 = 
-			    p->access_permission_2 = p->access_permission_3 = 
+			p->access_permission_0 = p->access_permission_1 =
+			    p->access_permission_2 = p->access_permission_3 =
 			    PTE_AP_USER_RO_KERNEL_RW;
 		}
 		if (flags & PAGE_WRITE) {
-			p->access_permission_0 = p->access_permission_1 = 
-			    p->access_permission_2 = p->access_permission_3 = 
-			    PTE_AP_USER_RW_KERNEL_RW; 
+			p->access_permission_0 = p->access_permission_1 =
+			    p->access_permission_2 = p->access_permission_3 =
+			    PTE_AP_USER_RW_KERNEL_RW;
 		}
 	}
Index: kernel/arch/arm32/include/arch/mm/page_armv6.h
===================================================================
--- kernel/arch/arm32/include/arch/mm/page_armv6.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/include/arch/mm/page_armv6.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -40,4 +40,5 @@
 #error "Do not include arch specific page.h directly use generic page.h instead"
 #endif
+
 
 /* Macros for querying the last-level PTE entries. */
@@ -125,16 +126,37 @@
 #define PTE_DESCRIPTOR_SMALL_PAGE_NX	3
 
-/** Sets the address of level 0 page table.
- *
- * @param pt Pointer to the page table to set.
- *
- */
-NO_TRACE static inline void set_ptl0_addr(pte_t *pt)
-{
-	asm volatile (
-		"mcr p15, 0, %[pt], c2, c0, 0\n"
-		:: [pt] "r" (pt)
-	);
-}
+
+/**
+ * For an ARMv7 implementation that does not include the Large Physical Address Extension,
+ * and in implementations of architecture versions before ARMv7, if the translation tables
+ * are held in Write-Back Cacheable memory, the caches must be cleaned to the point of
+ * unification after writing to the translation tables and before the DSB instruction. This
+ * ensures that the updated translation table are visible to a hardware translation table walk.
+ *
+ * Therefore, an example instruction sequence for writing a translation table entry,
+ * covering changes to the instruction
+ * or data mappings in a uniprocessor system is:
+ * STR rx, [Translation table entry]
+ * ; write new entry to the translation table
+ * Clean cache line [Translation table entry] : This operation is not required with the
+ * ; Multiprocessing Extensions.
+ * DSB
+ * ; ensures visibility of the data cleaned from the D Cache
+ * Invalidate TLB entry by MVA (and ASID if non-global) [page address]
+ * Invalidate BTC
+ * DSB
+ * ; ensure completion of the Invalidate TLB operation
+ * ISB
+ * ; ensure table changes visible to instruction fetch
+ *
+ * ARM Architecture reference chp. B3.10.1 p. B3-1375
+ * @note: see TTRB0/1 for pt memory type
+ */
+#define pt_coherence_m(pt, count) \
+do { \
+	for (unsigned i = 0; i < count; ++i) \
+		DCCMVAU_write((uintptr_t)(pt + i)); \
+	read_barrier(); \
+} while (0)
 
 
@@ -206,4 +228,5 @@
 		p->ns = 0;
 	}
+	pt_coherence(p);
 }
 
@@ -232,15 +255,30 @@
 			p->descriptor_type = PTE_DESCRIPTOR_SMALL_PAGE_NX;
 	}
-	
-	/* tex=0 buf=1 and cache=1 => normal memory
-	 * tex=0 buf=1 and cache=0 => shareable device mmio
-	 */
-	p->cacheable = (flags & PAGE_CACHEABLE);
-	p->bufferable = 1;
-	p->tex = 0;
+
+	if (flags & PAGE_CACHEABLE) {
+		/*
+		 * Write-through, no write-allocate memory, see ch. B3.8.2
+		 * (p. B3-1358) of ARM Architecture reference manual.
+		 * Make sure the memory type is correct, and in sync with:
+		 * init_boot_pt (boot/arch/arm32/src/mm.c)
+		 * init_ptl0_section (boot/arch/arm32/src/mm.c)
+		 * set_ptl0_addr (kernel/arch/arm32/include/arch/mm/page.h)
+		 */
+		p->tex = 5;
+		p->cacheable = 0;
+		p->bufferable = 1;
+	} else {
+		/*
+		 * Shareable device memory, see ch. B3.8.2 (p. B3-1358) of
+		 * ARM Architecture reference manual.
+		 */
+		p->tex = 0;
+		p->cacheable = 0;
+		p->bufferable = 1;
+	}
 	
 	/* Shareable is ignored for devices (non-cacheable),
-	 * turn it on for normal memory. */
-	p->shareable = 1;
+	 * turn it off for normal memory. */
+	p->shareable = 0;
 	
 	p->non_global = !(flags & PAGE_GLOBAL);
@@ -256,4 +294,5 @@
 			p->access_permission_1 = PTE_AP1_RO;
 	}
+	pt_coherence(p);
 }
 
@@ -264,6 +303,6 @@
 	p->should_be_zero_0 = 0;
 	p->should_be_zero_1 = 0;
-	write_barrier();
 	p->descriptor_type = PTE_DESCRIPTOR_COARSE_TABLE;
+	pt_coherence(p);
 }
 
@@ -273,4 +312,5 @@
 
 	p->descriptor_type = PTE_DESCRIPTOR_SMALL_PAGE;
+	pt_coherence(p);
 }
 
Index: kernel/arch/arm32/src/cpu/cpu.c
===================================================================
--- kernel/arch/arm32/src/cpu/cpu.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/src/cpu/cpu.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -157,5 +157,5 @@
 #endif
 #ifdef PROCESSOR_ARCH_armv7_a
-	 /* ICache coherency is elaborate on in barrier.h.
+	 /* ICache coherency is elaborated on in barrier.h.
 	  * VIPT and PIPT caches need maintenance only on code modify,
 	  * so it should be safe for general use.
@@ -166,4 +166,7 @@
 		control_reg |=
 		    SCTLR_INST_CACHE_EN_FLAG | SCTLR_BRANCH_PREDICT_EN_FLAG;
+	} else {
+		control_reg &=
+		    ~(SCTLR_INST_CACHE_EN_FLAG | SCTLR_BRANCH_PREDICT_EN_FLAG);
 	}
 #endif
@@ -204,7 +207,6 @@
 #ifdef PROCESSOR_ARCH_armv7_a
 	CSSELR_write((level & CCSELR_LEVEL_MASK) << CCSELR_LEVEL_SHIFT);
-	const unsigned ls_log = 2 +
-	    ((CCSIDR_read() >> CCSIDR_LINESIZE_SHIFT) & CCSIDR_LINESIZE_MASK);
-	return ls_log + 2; //return log2(bytes)
+	const uint32_t ccsidr = CCSIDR_read();
+	return CCSIDR_LINESIZE_LOG(ccsidr);
 #endif
 	return 0;
@@ -217,7 +219,6 @@
 #ifdef PROCESSOR_ARCH_armv7_a
 	CSSELR_write((level & CCSELR_LEVEL_MASK) << CCSELR_LEVEL_SHIFT);
-	const unsigned ways = 1 +
-	    ((CCSIDR_read() >> CCSIDR_ASSOC_SHIFT) & CCSIDR_ASSOC_MASK);
-	return ways;
+	const uint32_t ccsidr = CCSIDR_read();
+	return CCSIDR_WAYS(ccsidr);
 #endif
 	return 0;
@@ -229,7 +230,6 @@
 #ifdef PROCESSOR_ARCH_armv7_a
 	CSSELR_write((level & CCSELR_LEVEL_MASK) << CCSELR_LEVEL_SHIFT);
-	const unsigned sets = 1 +
-	    ((CCSIDR_read() >> CCSIDR_NUMSETS_SHIFT) & CCSIDR_NUMSETS_MASK);
-	return sets;
+	const uint32_t ccsidr = CCSIDR_read();
+	return CCSIDR_SETS(ccsidr);
 #endif
 	return 0;
@@ -241,5 +241,5 @@
 #ifdef PROCESSOR_ARCH_armv7_a
 	const uint32_t val = CLIDR_read();
-	for (unsigned i = 1; i <= 7; ++i) {
+	for (unsigned i = 0; i < 8; ++i) {
 		const unsigned ctype = CLIDR_CACHE(i, val);
 		switch (ctype) {
@@ -280,5 +280,5 @@
 		const unsigned ways = dcache_ways(i);
 		const unsigned sets = dcache_sets(i);
-		const unsigned way_shift =  31 - log2(ways);
+		const unsigned way_shift = 32 - log2(ways);
 		const unsigned set_shift = dcache_linesize_log(i);
 		dcache_clean_manual(i, false, ways, sets, way_shift, set_shift);
@@ -293,5 +293,5 @@
 		const unsigned ways = dcache_ways(i);
 		const unsigned sets = dcache_sets(i);
-		const unsigned way_shift =  31 - log2(ways);
+		const unsigned way_shift = 32 - log2(ways);
 		const unsigned set_shift = dcache_linesize_log(i);
 		dcache_clean_manual(i, true, ways, sets, way_shift, set_shift);
Index: kernel/arch/arm32/src/mach/beagleboardxm/beagleboardxm.c
===================================================================
--- kernel/arch/arm32/src/mach/beagleboardxm/beagleboardxm.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/src/mach/beagleboardxm/beagleboardxm.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -60,5 +60,5 @@
 
 static struct beagleboard {
-	amdm37x_irc_regs_t *irc_addr;
+	omap_irc_regs_t *irc_addr;
 	omap_uart_t uart;
 	amdm37x_gpt_t timer;
@@ -85,9 +85,10 @@
 static void bb_timer_irq_handler(irq_t *irq)
 {
+	amdm37x_gpt_irq_ack(&beagleboard.timer);
+
         /*
          * We are holding a lock which prevents preemption.
          * Release the lock, call clock() and reacquire the lock again.
          */
-	amdm37x_gpt_irq_ack(&beagleboard.timer);
 	spinlock_unlock(&irq->lock);
 	clock();
@@ -102,5 +103,5 @@
 	    PAGE_NOT_CACHEABLE);
 	ASSERT(beagleboard.irc_addr);
-	amdm37x_irc_init(beagleboard.irc_addr);
+	omap_irc_init(beagleboard.irc_addr);
 
 	/* Initialize timer. Use timer1, because it is in WKUP power domain
@@ -122,5 +123,5 @@
 
 	/* Enable timer interrupt */
-	amdm37x_irc_enable(beagleboard.irc_addr, AMDM37x_GPT1_IRQ);
+	omap_irc_enable(beagleboard.irc_addr, AMDM37x_GPT1_IRQ);
 
 	/* Start timer here */
@@ -146,6 +147,5 @@
 static void bbxm_irq_exception(unsigned int exc_no, istate_t *istate)
 {
-	const unsigned inum = amdm37x_irc_inum_get(beagleboard.irc_addr);
-	amdm37x_irc_irq_ack(beagleboard.irc_addr);
+	const unsigned inum = omap_irc_inum_get(beagleboard.irc_addr);
 
 	irq_t *irq = irq_dispatch_and_lock(inum);
@@ -159,4 +159,7 @@
 		    CPU->id, inum);
 	}
+	/** amdm37x manual ch. 12.5.2 (p. 2428) places irc ack at the end
+	 * of ISR. DO this to avoid strange behavior. */
+	omap_irc_irq_ack(beagleboard.irc_addr);
 }
 
@@ -185,5 +188,5 @@
 		indev_t *srln = srln_wire(srln_instance, sink);
 		omap_uart_input_wire(&beagleboard.uart, srln);
-		amdm37x_irc_enable(beagleboard.irc_addr, AMDM37x_UART3_IRQ);
+		omap_irc_enable(beagleboard.irc_addr, AMDM37x_UART3_IRQ);
 	}
 #endif
Index: kernel/arch/arm32/src/mach/beaglebone/beaglebone.c
===================================================================
--- kernel/arch/arm32/src/mach/beaglebone/beaglebone.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/src/mach/beaglebone/beaglebone.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -63,5 +63,5 @@
 
 static struct beaglebone {
-	am335x_irc_regs_t *irc_addr;
+	omap_irc_regs_t *irc_addr;
 	am335x_cm_per_regs_t *cm_per_addr;
 	am335x_cm_dpll_regs_t *cm_dpll_addr;
@@ -104,5 +104,5 @@
 
 	/* Initialize the interrupt controller */
-	am335x_irc_init(bbone.irc_addr);
+	omap_irc_init(bbone.irc_addr);
 }
 
@@ -153,5 +153,5 @@
 	}
 	/* Enable the interrupt */
-	am335x_irc_enable(bbone.irc_addr, AM335x_DMTIMER2_IRQ);
+	omap_irc_enable(bbone.irc_addr, AM335x_DMTIMER2_IRQ);
 	/* Start the timer */
 	am335x_timer_start(&bbone.timer);
@@ -176,5 +176,5 @@
 static void bbone_irq_exception(unsigned int exc_no, istate_t *istate)
 {
-	const unsigned inum = am335x_irc_inum_get(bbone.irc_addr);
+	const unsigned inum = omap_irc_inum_get(bbone.irc_addr);
 
 	irq_t *irq = irq_dispatch_and_lock(inum);
@@ -187,5 +187,5 @@
 	}
 
-	am335x_irc_irq_ack(bbone.irc_addr);
+	omap_irc_irq_ack(bbone.irc_addr);
 }
 
@@ -214,5 +214,5 @@
 		indev_t *srln = srln_wire(srln_instance, sink);
 		omap_uart_input_wire(&bbone.uart, srln);
-		am335x_irc_enable(bbone.irc_addr, AM335x_UART0_IRQ);
+		omap_irc_enable(bbone.irc_addr, AM335x_UART0_IRQ);
 	}
 #endif
Index: kernel/arch/arm32/src/mm/frame.c
===================================================================
--- kernel/arch/arm32/src/mm/frame.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/src/mm/frame.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/src/mm/page.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -69,5 +69,5 @@
 #ifdef HIGH_EXCEPTION_VECTORS
 	/* Create mapping for exception table at high offset */
-	uintptr_t ev_frame = (uintptr_t) frame_alloc(ONE_FRAME, FRAME_NONE);
+	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/mm/tlb.c
===================================================================
--- kernel/arch/arm32/src/mm/tlb.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/src/mm/tlb.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,6 +37,8 @@
 #include <arch/mm/asid.h>
 #include <arch/asm.h>
+#include <arch/cp15.h>
 #include <typedefs.h>
 #include <arch/mm/page.h>
+#include <arch/cache.h>
 
 /** Invalidate all entries in TLB.
@@ -46,9 +48,17 @@
 void tlb_invalidate_all(void)
 {
-	asm volatile (
-		"eor r1, r1\n"
-		"mcr p15, 0, r1, c8, c7, 0\n"
-		::: "r1"
-	);
+	TLBIALL_write(0);
+	/*
+	 * "A TLB maintenance operation is only guaranteed to be complete after
+	 * the execution of a DSB instruction."
+	 * "An ISB instruction, or a return from an exception, causes the
+	 * effect of all completed TLB maintenance operations that appear in
+	 * program order before the ISB or return from exception to be visible
+	 * to all subsequent instructions, including the instruction fetches
+	 * for those instructions."
+	 * ARM Architecture reference Manual ch. B3.10.1 p. B3-1374 B3-1375
+	 */
+	read_barrier();
+	inst_barrier();
 }
 
@@ -60,4 +70,5 @@
 {
 	tlb_invalidate_all();
+	// TODO: why not TLBIASID_write(asid) ?
 }
 
@@ -65,11 +76,21 @@
  *
  * @param page Virtual adress of the page
- */ 
+ */
 static inline void invalidate_page(uintptr_t page)
 {
-	asm volatile (
-		"mcr p15, 0, %[page], c8, c7, 1\n"
-		:: [page] "r" (page)
-	);
+	//TODO: What about TLBIMVAA?
+	TLBIMVA_write(page);
+	/*
+	 * "A TLB maintenance operation is only guaranteed to be complete after
+	 * the execution of a DSB instruction."
+	 * "An ISB instruction, or a return from an exception, causes the
+	 * effect of all completed TLB maintenance operations that appear in
+	 * program order before the ISB or return from exception to be visible
+	 * to all subsequent instructions, including the instruction fetches
+	 * for those instructions."
+	 * ARM Architecture reference Manual ch. B3.10.1 p. B3-1374 B3-1375
+	 */
+	read_barrier();
+	inst_barrier();
 }
 
@@ -83,7 +104,5 @@
 void tlb_invalidate_pages(asid_t asid __attribute__((unused)), uintptr_t page, size_t cnt)
 {
-	unsigned int i;
-
-	for (i = 0; i < cnt; i++)
+	for (unsigned i = 0; i < cnt; i++)
 		invalidate_page(page + i * PAGE_SIZE);
 }
Index: kernel/arch/arm32/src/ras.c
===================================================================
--- kernel/arch/arm32/src/ras.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/arm32/src/ras.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -51,13 +51,12 @@
 void ras_init(void)
 {
-	uintptr_t frame;
-
-	frame = (uintptr_t) frame_alloc(ONE_FRAME,
-	    FRAME_ATOMIC | FRAME_HIGHMEM);
+	uintptr_t frame =
+	    frame_alloc(1, FRAME_ATOMIC | FRAME_HIGHMEM, 0);
 	if (!frame)
-		frame = (uintptr_t) frame_alloc(ONE_FRAME, FRAME_LOWMEM);
+		frame = frame_alloc(1, FRAME_LOWMEM, 0);
+	
 	ras_page = (uintptr_t *) km_map(frame,
 	    PAGE_SIZE, PAGE_READ | PAGE_WRITE | PAGE_USER | PAGE_CACHEABLE);
-
+	
 	memsetb(ras_page, PAGE_SIZE, 0); 
 	ras_page[RAS_START] = 0;
Index: kernel/arch/ia32/include/arch/mm/frame.h
===================================================================
--- kernel/arch/ia32/include/arch/mm/frame.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/ia32/include/arch/mm/frame.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,4 +39,6 @@
 #define FRAME_SIZE   (1 << FRAME_WIDTH)
 
+#define FRAME_LOWPRIO  0x1000
+
 #ifndef __ASM__
 
Index: kernel/arch/ia32/include/arch/mm/page.h
===================================================================
--- kernel/arch/ia32/include/arch/mm/page.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/ia32/include/arch/mm/page.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/ia32/src/ddi/ddi.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -59,27 +59,28 @@
 int ddi_iospace_enable_arch(task_t *task, uintptr_t ioaddr, size_t size)
 {
-	size_t bits = ioaddr + size;
-	if (bits > IO_PORTS)
+	size_t elements = ioaddr + size;
+	if (elements > IO_PORTS)
 		return ENOENT;
 	
-	if (task->arch.iomap.bits < bits) {
+	if (task->arch.iomap.elements < elements) {
 		/*
 		 * The I/O permission bitmap is too small and needs to be grown.
 		 */
 		
-		uint8_t *newmap = (uint8_t *) malloc(BITS2BYTES(bits), FRAME_ATOMIC);
-		if (!newmap)
+		void *store = malloc(bitmap_size(elements), FRAME_ATOMIC);
+		if (!store)
 			return ENOMEM;
 		
 		bitmap_t oldiomap;
-		bitmap_initialize(&oldiomap, task->arch.iomap.map,
+		bitmap_initialize(&oldiomap, task->arch.iomap.elements,
 		    task->arch.iomap.bits);
-		bitmap_initialize(&task->arch.iomap, newmap, bits);
+		
+		bitmap_initialize(&task->arch.iomap, elements, store);
 		
 		/*
 		 * Mark the new range inaccessible.
 		 */
-		bitmap_set_range(&task->arch.iomap, oldiomap.bits,
-		    bits - oldiomap.bits);
+		bitmap_set_range(&task->arch.iomap, oldiomap.elements,
+		    elements - oldiomap.elements);
 		
 		/*
@@ -89,6 +90,7 @@
 		if (oldiomap.bits) {
 			bitmap_copy(&task->arch.iomap, &oldiomap,
-			    oldiomap.bits);
-			free(oldiomap.map);
+			    oldiomap.elements);
+			
+			free(oldiomap.bits);
 		}
 	}
@@ -97,5 +99,5 @@
 	 * Enable the range and we are done.
 	 */
-	bitmap_clear_range(&task->arch.iomap, (size_t) ioaddr, (size_t) size);
+	bitmap_clear_range(&task->arch.iomap, (size_t) ioaddr, size);
 	
 	/*
@@ -119,13 +121,15 @@
 	/* First, copy the I/O Permission Bitmap. */
 	irq_spinlock_lock(&TASK->lock, false);
+	
 	size_t ver = TASK->arch.iomapver;
-	size_t bits = TASK->arch.iomap.bits;
-	if (bits) {
-		ASSERT(TASK->arch.iomap.map);
+	size_t elements = TASK->arch.iomap.elements;
+	
+	if (elements > 0) {
+		ASSERT(TASK->arch.iomap.bits);
 		
 		bitmap_t iomap;
-		bitmap_initialize(&iomap, CPU->arch.tss->iomap,
-		    TSS_IOMAP_SIZE * 8);
-		bitmap_copy(&iomap, &TASK->arch.iomap, bits);
+		bitmap_initialize(&iomap, TSS_IOMAP_SIZE * 8,
+		    CPU->arch.tss->iomap);
+		bitmap_copy(&iomap, &TASK->arch.iomap, elements);
 		
 		/*
@@ -133,11 +137,14 @@
 		 * I/O access.
 		 */
-		bitmap_set_range(&iomap, bits, ALIGN_UP(bits, 8) - bits);
+		bitmap_set_range(&iomap, elements,
+		    ALIGN_UP(elements, 8) - elements);
+		
 		/*
 		 * It is safe to set the trailing eight bits because of the
 		 * extra convenience byte in TSS_IOMAP_SIZE.
 		 */
-		bitmap_set_range(&iomap, ALIGN_UP(bits, 8), 8);
+		bitmap_set_range(&iomap, ALIGN_UP(elements, 8), 8);
 	}
+	
 	irq_spinlock_unlock(&TASK->lock, false);
 	
@@ -150,5 +157,6 @@
 	
 	descriptor_t *gdt_p = (descriptor_t *) cpugdtr.base;
-	gdt_setlimit(&gdt_p[TSS_DES], TSS_BASIC_SIZE + BITS2BYTES(bits));
+	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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/ia32/src/proc/task.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -40,20 +40,22 @@
 /** Perform ia32 specific task initialization.
  *
- * @param t Task to be initialized.
+ * @param task Task to be initialized.
+ *
  */
-void task_create_arch(task_t *t)
+void task_create_arch(task_t *task)
 {
-	t->arch.iomapver = 0;
-	bitmap_initialize(&t->arch.iomap, NULL, 0);
+	task->arch.iomapver = 0;
+	bitmap_initialize(&task->arch.iomap, 0, NULL);
 }
 
 /** Perform ia32 specific task destruction.
  *
- * @param t Task to be initialized.
+ * @param task Task to be initialized.
+ *
  */
-void task_destroy_arch(task_t *t)
+void task_destroy_arch(task_t *task)
 {
-	if (t->arch.iomap.map)
-		free(t->arch.iomap.map);
+	if (task->arch.iomap.bits != NULL)
+		free(task->arch.iomap.bits);
 }
 
Index: kernel/arch/ia64/include/arch/mm/frame.h
===================================================================
--- kernel/arch/ia64/include/arch/mm/frame.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/ia64/include/arch/mm/frame.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,4 +39,6 @@
 #define FRAME_SIZE   (1 << FRAME_WIDTH)
 
+#define FRAME_LOWPRIO  0
+
 #ifndef __ASM__
 
Index: kernel/arch/ia64/src/ddi/ddi.c
===================================================================
--- kernel/arch/ia64/src/ddi/ddi.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/ia64/src/ddi/ddi.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2006 Jakub Jermar
- * Copyright (c) 2008 Jakub vana
+ * Copyright (c) 2008 Jakub Vana
  * All rights reserved.
  *
@@ -56,11 +56,13 @@
 {
 	if (!task->arch.iomap) {
-		uint8_t *map;
-
 		task->arch.iomap = malloc(sizeof(bitmap_t), 0);
-		map = malloc(BITS2BYTES(IO_MEMMAP_PAGES), 0);
-		if(!map)
+		if (task->arch.iomap == NULL)
 			return ENOMEM;
-		bitmap_initialize(task->arch.iomap, map, IO_MEMMAP_PAGES);
+		
+		void *store = malloc(bitmap_size(IO_MEMMAP_PAGES), 0);
+		if (store == NULL)
+			return ENOMEM;
+		
+		bitmap_initialize(task->arch.iomap, IO_MEMMAP_PAGES, store);
 		bitmap_clear_range(task->arch.iomap, 0, IO_MEMMAP_PAGES);
 	}
@@ -69,5 +71,5 @@
 	size = ALIGN_UP(size + ioaddr - 4 * iopage, PORTS_PER_PAGE);
 	bitmap_set_range(task->arch.iomap, iopage, size / 4);
-
+	
 	return 0;
 }
Index: kernel/arch/ia64/src/mm/vhpt.c
===================================================================
--- kernel/arch/ia64/src/mm/vhpt.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/ia64/src/mm/vhpt.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -42,8 +42,10 @@
 uintptr_t vhpt_set_up(void)
 {
-	vhpt_base = frame_alloc(VHPT_WIDTH - FRAME_WIDTH,
-	    FRAME_KA | FRAME_ATOMIC);
-	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;
@@ -82,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/asm.h
===================================================================
--- kernel/arch/mips32/include/arch/asm.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/mips32/include/arch/asm.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -42,12 +42,5 @@
 NO_TRACE static inline void cpu_sleep(void)
 {
-	/*
-	 * Unfortunatelly most of the simulators do not support
-	 *
-	 * asm volatile (
-	 *     "wait"
-	 * );
-	 *
-	 */
+	asm volatile ("wait");
 }
 
Index: kernel/arch/mips32/include/arch/mm/frame.h
===================================================================
--- kernel/arch/mips32/include/arch/mm/frame.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/mips32/include/arch/mm/frame.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,4 +39,6 @@
 #define FRAME_SIZE   (1 << FRAME_WIDTH)
 
+#define FRAME_LOWPRIO  0
+
 #ifndef __ASM__
 
Index: kernel/arch/mips32/include/arch/mm/page.h
===================================================================
--- kernel/arch/mips32/include/arch/mm/page.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/mips32/include/arch/mm/page.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -27,5 +27,5 @@
  */
 
-/** @addtogroup mips32mm	
+/** @addtogroup mips32mm
  * @{
  */
@@ -70,27 +70,27 @@
  * - PTL3 has 4096 entries (12 bits)
  */
- 
+
 /* Macros describing number of entries in each level. */
-#define PTL0_ENTRIES_ARCH	64
-#define PTL1_ENTRIES_ARCH	0
-#define PTL2_ENTRIES_ARCH	0
-#define PTL3_ENTRIES_ARCH	4096
+#define PTL0_ENTRIES_ARCH  64
+#define PTL1_ENTRIES_ARCH  0
+#define PTL2_ENTRIES_ARCH  0
+#define PTL3_ENTRIES_ARCH  4096
 
 /* 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. */
-#define PTL0_INDEX_ARCH(vaddr)	((vaddr) >> 26) 
-#define PTL1_INDEX_ARCH(vaddr)	0
-#define PTL2_INDEX_ARCH(vaddr)	0
-#define PTL3_INDEX_ARCH(vaddr)	(((vaddr) >> 14) & 0xfff)
+#define PTL0_INDEX_ARCH(vaddr)  ((vaddr) >> 26)
+#define PTL1_INDEX_ARCH(vaddr)  0
+#define PTL2_INDEX_ARCH(vaddr)  0
+#define PTL3_INDEX_ARCH(vaddr)  (((vaddr) >> 14) & 0xfff)
 
 /* Set accessor for PTL0 address. */
 #define SET_PTL0_ADDRESS_ARCH(ptl0)
 
-/* Get PTE address accessors for each level. */ 
+/* Get PTE address accessors for each level. */
 #define GET_PTL1_ADDRESS_ARCH(ptl0, i) \
 	(((pte_t *) (ptl0))[(i)].pfn << 12)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/mips32/src/mach/malta/malta.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/mips32/src/mm/tlb.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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/mips64/include/arch/mm/frame.h
===================================================================
--- kernel/arch/mips64/include/arch/mm/frame.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/mips64/include/arch/mm/frame.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,4 +39,6 @@
 #define FRAME_SIZE   (1 << FRAME_WIDTH)
 
+#define FRAME_LOWPRIO  0
+
 #ifndef __ASM__
 
Index: kernel/arch/ppc32/include/arch/mm/frame.h
===================================================================
--- kernel/arch/ppc32/include/arch/mm/frame.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/ppc32/include/arch/mm/frame.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,4 +39,6 @@
 #define FRAME_SIZE   (1 << FRAME_WIDTH)
 
+#define FRAME_LOWPRIO  0
+
 #ifndef __ASM__
 
Index: kernel/arch/ppc32/include/arch/mm/page.h
===================================================================
--- kernel/arch/ppc32/include/arch/mm/page.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/ppc32/include/arch/mm/page.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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/include/arch/mm/frame.h
===================================================================
--- kernel/arch/sparc64/include/arch/mm/frame.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/sparc64/include/arch/mm/frame.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -46,4 +46,16 @@
 #endif
 
+#ifndef __ASM__
+
+#include <typedefs.h>
+
+extern uintptr_t end_of_identity;
+
+extern void frame_low_arch_init(void);
+extern void frame_high_arch_init(void);
+#define physmem_print()
+
+#endif
+
 #endif
 
Index: kernel/arch/sparc64/include/arch/mm/sun4u/frame.h
===================================================================
--- kernel/arch/sparc64/include/arch/mm/sun4u/frame.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/sparc64/include/arch/mm/sun4u/frame.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -27,5 +27,5 @@
  */
 
-/** @addtogroup sparc64mm	
+/** @addtogroup sparc64mm
  * @{
  */
@@ -41,6 +41,6 @@
  * Therefore, the kernel uses 8K only internally on the TLB and TSB levels.
  */
-#define MMU_FRAME_WIDTH		13	/* 8K */
-#define MMU_FRAME_SIZE		(1 << MMU_FRAME_WIDTH)
+#define MMU_FRAME_WIDTH  13  /* 8K */
+#define MMU_FRAME_SIZE   (1 << MMU_FRAME_WIDTH)
 
 /*
@@ -49,6 +49,8 @@
  * each 16K page with a pair of adjacent 8K pages.
  */
-#define FRAME_WIDTH		14	/* 16K */
-#define FRAME_SIZE		(1 << FRAME_WIDTH)
+#define FRAME_WIDTH  14  /* 16K */
+#define FRAME_SIZE   (1 << FRAME_WIDTH)
+
+#define FRAME_LOWPRIO  0
 
 #ifndef __ASM__
@@ -72,10 +74,4 @@
 typedef union frame_address frame_address_t;
 
-extern uintptr_t end_of_identity;
-
-extern void frame_low_arch_init(void);
-extern void frame_high_arch_init(void);
-#define physmem_print()
-
 #endif
 
Index: kernel/arch/sparc64/include/arch/mm/sun4v/frame.h
===================================================================
--- kernel/arch/sparc64/include/arch/mm/sun4v/frame.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/sparc64/include/arch/mm/sun4v/frame.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -27,5 +27,5 @@
  */
 
-/** @addtogroup sparc64mm	
+/** @addtogroup sparc64mm
  * @{
  */
@@ -36,19 +36,11 @@
 #define KERN_sparc64_sun4v_FRAME_H_
 
-#define MMU_FRAME_WIDTH		13	/* 8K */
-#define MMU_FRAME_SIZE		(1 << MMU_FRAME_WIDTH)
+#define MMU_FRAME_WIDTH  13  /* 8K */
+#define MMU_FRAME_SIZE   (1 << MMU_FRAME_WIDTH)
 
-#define FRAME_WIDTH		13
-#define FRAME_SIZE		(1 << FRAME_WIDTH)
+#define FRAME_WIDTH  13
+#define FRAME_SIZE   (1 << FRAME_WIDTH)
 
-#ifndef __ASM__
-
-#include <typedefs.h>
-
-extern void frame_low_arch_init(void);
-extern void frame_high_arch_init(void);
-#define physmem_print()
-
-#endif
+#define FRAME_LOWPRIO  0
 
 #endif
Index: kernel/arch/sparc64/include/arch/trap/sun4v/mmu.h
===================================================================
--- kernel/arch/sparc64/include/arch/trap/sun4v/mmu.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/sparc64/include/arch/trap/sun4v/mmu.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -102,4 +102,11 @@
 	nop
 
+	/* exclude pages beyond the end of memory from the identity mapping */
+	sethi %hi(end_of_identity), %g4
+	ldx [%g4 + %lo(end_of_identity)], %g4
+	cmp %g1, %g4
+	bgeu %xcc, 0f
+	nop
+
 	/*
 	 * Installing the identity does not fit into 32 instructions, call
Index: kernel/arch/sparc64/src/mm/sun4u/as.c
===================================================================
--- kernel/arch/sparc64/src/mm/sun4u/as.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/sparc64/src/mm/sun4u/as.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -63,40 +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 = (uintptr_t) frame_alloc(order, flags | FRAME_KA);
-	
-	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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/sparc64/src/mm/sun4v/as.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -66,9 +66,7 @@
 {
 #ifdef CONFIG_TSB
-	uint8_t order = fnzb32(
-		(TSB_ENTRY_COUNT * sizeof(tsb_entry_t)) >> FRAME_WIDTH);
-	
-	uintptr_t tsb = (uintptr_t) frame_alloc(order, flags);
-	
+	uintptr_t tsb =
+	    frame_alloc(SIZE2FRAMES(TSB_ENTRY_COUNT * sizeof(tsb_entry_t)),
+	    flags, 0);
 	if (!tsb)
 		return -1;
@@ -92,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/arch/sparc64/src/mm/sun4v/frame.c
===================================================================
--- kernel/arch/sparc64/src/mm/sun4v/frame.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/sparc64/src/mm/sun4v/frame.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -101,4 +101,7 @@
 	 */
 	frame_mark_unavailable(ADDR2PFN(KA2PA(PFN2ADDR(0))), 1);
+
+	/* PA2KA will work only on low-memory. */
+	end_of_identity = PA2KA(config.physmem_end - FRAME_SIZE) + PAGE_SIZE;
 }
 
Index: kernel/arch/sparc64/src/mm/sun4v/tlb.c
===================================================================
--- kernel/arch/sparc64/src/mm/sun4v/tlb.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/sparc64/src/mm/sun4v/tlb.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -251,4 +251,5 @@
 	uintptr_t va = DMISS_ADDRESS(page_and_ctx);
 	uint16_t ctx = DMISS_CONTEXT(page_and_ctx);
+	as_t *as = AS;
 
 	if (ctx == ASID_KERNEL) {
@@ -256,9 +257,13 @@
 			/* NULL access in kernel */
 			panic("NULL pointer dereference.");
+		} else if (va >= end_of_identity) {
+			/* Kernel non-identity */
+			as = AS_KERNEL;
+		} else {
+			panic("Unexpected kernel page fault.");
 		}
-		panic("Unexpected kernel page fault.");
-	}
-
-	t = page_mapping_find(AS, va, true);
+	}
+
+	t = page_mapping_find(as, va, true);
 	if (t) {
 		/*
@@ -295,6 +300,10 @@
 	uintptr_t va = DMISS_ADDRESS(page_and_ctx);
 	uint16_t ctx = DMISS_CONTEXT(page_and_ctx);
-
-	t = page_mapping_find(AS, va, true);
+	as_t *as = AS;
+
+	if (ctx == ASID_KERNEL)
+		as = AS_KERNEL;
+
+	t = page_mapping_find(as, va, true);
 	if (t && PTE_WRITABLE(t)) {
 		/*
Index: kernel/arch/sparc64/src/sun4v/start.S
===================================================================
--- kernel/arch/sparc64/src/sun4v/start.S	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/arch/sparc64/src/sun4v/start.S	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -345,4 +345,13 @@
 	.quad 0
 
+/*
+ * This variable is used by the fast_data_access_MMU_miss trap handler.
+ * In runtime, it is modified to contain the address of the end of physical
+ * memory.
+ */
+.global end_of_identity
+end_of_identity:
+	.quad -1
+
 .global kernel_8k_tlb_data_template
 kernel_8k_tlb_data_template:
Index: kernel/doc/mm
===================================================================
--- kernel/doc/mm	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/doc/mm	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -71,5 +71,5 @@
 
 'malloc' function accepts flags as a second argument. The flags are directly
-passed to the underlying frame_alloc function. 
+passed to the underlying frame_alloc function.
 
 1) If the flags parameter contains FRAME_ATOMIC, the allocator will not sleep.
Index: kernel/genarch/include/genarch/drivers/am335x/irc.h
===================================================================
--- kernel/genarch/include/genarch/drivers/am335x/irc.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/genarch/include/genarch/drivers/am335x/irc.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,214 +38,14 @@
 #define KERN_AM335x_IRQC_H_
 
-#define AM335x_IRC_BASE_ADDRESS 0x48200000
-#define AM335x_IRC_SIZE         4096
+#define AM335x_IRC_BASE_ADDRESS     0x48200000
+#define AM335x_IRC_SIZE             4096
 
-#define AM335x_IRC_IRQ_COUNT    128
+#define AM335x_IRC_IRQ_COUNT        128
+#define AM335x_IRC_IRQ_GROUPS_COUNT 4
 
-#include <typedefs.h>
+#define OMAP_IRC_IRQ_COUNT        AM335x_IRC_IRQ_COUNT
+#define OMAP_IRC_IRQ_GROUPS_COUNT AM335x_IRC_IRQ_GROUPS_COUNT
 
-typedef struct {
-	const ioport32_t revision;
-#define AM335x_IRC_REV_MASK 0xFF
-
-	const uint8_t padd0[12];
-
-	/* This register controls the various parameters
-	 * of the OCP interface.
-	 */
-	ioport32_t sysconfig;
-#define AM335x_IRC_SYSCONFIG_AUTOIDLE_FLAG   (1 << 0)
-#define AM335x_IRC_SYSCONFIG_SOFTRESET_FLAG  (1 << 1)
-
-	/* This register provides status information about the module */
-	const ioport32_t sysstatus;
-#define AM335x_IRC_SYSSTATUS_RESET_DONE_FLAG (1 << 0)
-
-	const uint8_t padd1[40];
-
-	/* This register supplies the currently active IRQ interrupt number */
-	ioport32_t sir_irq;
-#define AM335x_IRC_SIR_IRQ_ACTIVEIRQ_MASK       0x7F
-#define AM335x_IRC_SIR_IRQ_SPURIOUSIRQFLAG_MASK 0xFFFFFFF8
-
-	/* This register supplies the currently active FIQ interrupt number */
-	const ioport32_t sir_fiq;
-#define AM335x_IRC_FIQ_IRQ_ACTIVEFIQ_MASK       0x7F
-#define AM335x_IRC_FIQ_IRQ_SPURIOUSFIQFLAG_MASK 0xFFFFFFF8
-
-	/* This register contains the new interrupt agreement bits */
-	ioport32_t control;
-#define AM335x_IRC_CONTROL_NEWIRQAGR_FLAG       (1 << 0)
-#define AM335x_IRC_CONTROL_NEWFIQAGR_FLAG       (1 << 1)
-
-	/* This register controls protection of the other registers.
-	 * This register can only be accessed in priviledged mode, regardless
-	 * of the current value of the protection bit.
-	 */
-	ioport32_t protection;
-#define AM335x_IRC_PROTECTION_FLAG              (1 << 0)
-
-	/* This register controls the clock auto-idle for the functional
-	 * clock and the input synchronizers.
-	 */
-	ioport32_t idle;
-#define AM335x_IRC_IDLE_FUNCIDLE_FLAG           (1 << 0)
-#define AM335x_IRC_IDLE_TURBO_FLAG              (1 << 1)
-
-	const uint8_t padd2[12];
-
-	/* This register supplies the currently active IRQ priority level */
-	const ioport32_t irq_priority;
-#define AM335x_IRC_IRQ_PRIORITY_IRQPRIORITY_MASK     0x7F
-#define AM335x_IRC_IRQ_PRIORITY_SPURIOUSIRQFLAG_MASK 0xFFFFFFF8
-
-	/* This register supplies the currently active FIQ priority level */
-	const ioport32_t fiq_priority;
-#define AM335x_IRC_FIQ_PRIORITY_FIQPRIORITY_MASK     0x7F
-#define AM335x_IRC_FIQ_PRIORITY_SPURIOUSIRQFLAG_MASK 0xFFFFFFF8
-
-	/* This register sets the priority threshold */
-	ioport32_t threshold;
-#define AM335x_IRC_THRESHOLD_PRIORITYTHRESHOLD_MASK     0xFF
-#define AM335x_IRC_THRESHOLD_PRIORITYTHRESHOLD_ENABLED  0x00
-#define AM335x_IRC_THRESHOLD_PRIORITYTHRESHOLD_DISABLED 0xFF
-
-	const uint8_t padd[20];
-
-	struct {
-		/* Raw interrupt input status before masking */
-		const ioport32_t itr;
-
-		/* Interrupt mask */
-		ioport32_t mir;
-
-		/* This register is used to clear the interrupt mask bits,
-		 * Write 1 clears the mask bit to 0.
-		 */
-		ioport32_t mir_clear;
-
-		/* This register is used to set the interrupt mask bits,
-		 * Write 1 sets the mask bit to 1.
-		 */
-		ioport32_t mir_set;
-
-		/* This register is used to set the software interrupt bits,
-		 * it is also used to read the current active software
-		 * interrupts.
-		 * Write 1 sets the software interrups bits to 1.
-		 */
-		ioport32_t isr_set;
-
-		/* This register is used to clear the software interrups bits.
-		 * Write 1 clears the software interrupt bits to 0.
-		 */
-		ioport32_t isr_clear;
-
-		/* This register contains the IRQ status after masking. */
-		const ioport32_t pending_irq;
-
-		/* This register contains the FIQ status after masking. */
-		const ioport32_t pending_fiq;
-	} interrupts[4];
-
-	/* These registers contain the priority for the interrups and
-	 * the FIQ/IRQ steering.
-	 */
-	ioport32_t ilr[AM335x_IRC_IRQ_COUNT];
-/* 0 = Interrupt routed to IRQ, 1 = interrupt routed to FIQ */
-#define AM335x_IRC_ILR_FIQNIRQ_FLAG    (1 << 0)
-#define AM335x_IRC_ILR_PRIORITY_MASK   0x3F
-#define AM335x_IRC_ILR_PRIORITY_SHIFT  2
-
-} am335x_irc_regs_t;
-
-static inline void am335x_irc_init(am335x_irc_regs_t *regs)
-{
-	int i;
-
-	/* Initialization sequence */
-
-	/* 1 - Program the SYSCONFIG register: if necessary, enable the
-	 *     autogating by setting the AUTOIDLE bit.
-	 */
-	regs->sysconfig &= ~AM335x_IRC_SYSCONFIG_AUTOIDLE_FLAG;
-
-	/* 2 - Program the IDLE register: if necessary, disable functional
-	 *     clock autogating or enable synchronizer autogating by setting
-	 *     the FUNCIDLE bit or the TURBO bit accordingly.
-	 */
-	regs->idle &= ~AM335x_IRC_IDLE_FUNCIDLE_FLAG;
-	regs->idle &= ~AM335x_IRC_IDLE_TURBO_FLAG;
-
-	/* 3 - Program ILRm register for each interrupt line: Assign a
-	 *     priority level and set the FIQNIRQ bit for an FIQ interrupt
-	 *     (by default, interrupts are mapped to IRQ and
-	 *     priority is 0 (highest).
-	 */
-
-	for (i = 0; i < AM335x_IRC_IRQ_COUNT; ++i)
-		regs->ilr[i] = 0;
-
-	/* 4 - Program the MIRn register: Enable interrupts (by default,
-	 *     all interrupt lines are masked).
-	 */
-	for (i = 0; i < 4; ++i)
-		regs->interrupts[i].mir_set = 0xFFFFFFFF;
-}
-
-/** Get the currently active IRQ interrupt number
- *
- * @param regs     Pointer to the irc memory mapped registers
- *
- * @return         The active IRQ interrupt number
- */
-static inline unsigned am335x_irc_inum_get(am335x_irc_regs_t *regs)
-{
-	return regs->sir_irq & AM335x_IRC_SIR_IRQ_ACTIVEIRQ_MASK;
-}
-
-/** Reset IRQ output and enable new IRQ generation
- *
- * @param regs    Pointer to the irc memory mapped registers
- */
-static inline void am335x_irc_irq_ack(am335x_irc_regs_t *regs)
-{
-	regs->control = AM335x_IRC_CONTROL_NEWIRQAGR_FLAG;
-}
-
-/** Reset FIQ output and enable new FIQ generation
- *
- * @param regs    Pointer to the irc memory mapped registers
- */
-static inline void am335x_irc_fiq_ack(am335x_irc_regs_t *regs)
-{
-	regs->control = AM335x_IRC_CONTROL_NEWFIQAGR_FLAG;
-}
-
-/** Clear an interrupt mask bit
- *
- * @param regs    Pointer to the irc memory mapped registers
- * @param inum    The interrupt to be enabled
- */
-static inline void am335x_irc_enable(am335x_irc_regs_t *regs, unsigned inum)
-{
-	ASSERT(inum < AM335x_IRC_IRQ_COUNT);
-	const unsigned set = inum / 32;
-	const unsigned pos = inum % 32;
-	regs->interrupts[set].mir_clear = (1 << pos);
-}
-
-/** Set an interrupt mask bit
- *
- * @param regs    Pointer to the irc memory mapped registers
- * @param inum    The interrupt to be disabled
- */
-static inline void am335x_irc_disable(am335x_irc_regs_t *regs, unsigned inum)
-{
-	ASSERT(inum < AM335x_IRC_IRQ_COUNT);
-	const unsigned set = inum / 32;
-	const unsigned pos = inum % 32;
-	regs->interrupts[set].mir_set = (1 << pos);
-}
+#include <genarch/drivers/omap/irc.h>
 
 #endif
Index: kernel/genarch/include/genarch/drivers/amdm37x/gpt.h
===================================================================
--- kernel/genarch/include/genarch/drivers/amdm37x/gpt.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/genarch/include/genarch/drivers/amdm37x/gpt.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,4 +39,5 @@
 #include <typedefs.h>
 #include <mm/km.h>
+#include <time/clock.h>
 
 /* AMDM37x TRM p. 2740 */
@@ -128,8 +129,13 @@
 #define AMDM37x_GPT_TCLR_CE_FLAG  (1 << 6)
 #define AMDM37x_GPT_TCLR_SCPWM  (1 << 7)
-#define AMDM37x_GPT_TCLR_TCM_MASK  (0x3)
-#define AMDM37x_GPT_TCLR_TCM_SHIFT  (8)
-#define AMDM37x_GPT_TCLR_TRG_MASK  (0x3)
-#define AMDM37x_GPT_TCLR_TRG_SHIFT (10)
+#define AMDM37x_GPT_TCLR_TCM_MASK  (0x3 << 8)
+#define AMDM37x_GPT_TCLR_TCM_NO_CAPTURE   (0x0 << 8)
+#define AMDM37x_GPT_TCLR_TCM_RAISE_CAPTURE   (0x1 << 8)
+#define AMDM37x_GPT_TCLR_TCM_FALL_CAPTURE   (0x2 << 8)
+#define AMDM37x_GPT_TCLR_TCM_BOTH_CAPTURE   (0x3 << 8)
+#define AMDM37x_GPT_TCLR_TRG_MASK  (0x3 << 10)
+#define AMDM37x_GPT_TCLR_TRG_NO  (0x0 << 10)
+#define AMDM37x_GPT_TCLR_TRG_OVERFLOW  (0x1 << 10)
+#define AMDM37x_GPT_TCLR_TRG_OVERMATCH  (0x2 << 10)
 #define AMDM37x_GPT_TCLR_PT_FLAG  (1 << 12)
 #define AMDM37x_GPT_TCLR_CAPT_MODE_FLAG  (1 << 13)
@@ -209,6 +215,11 @@
 	timer->regs = (void*) km_map(ioregs, iosize, PAGE_NOT_CACHEABLE);
 
+	/* Reset the timer */
+	timer->regs->tiocp_cfg |= AMDM37x_GPT_TIOCP_CFG_SOFTRESET_FLAG;
+
+	while (!(timer->regs->tistat & AMDM37x_GPT_TISTAT_RESET_DONE_FLAG));
+
 	/* Set autoreload */
-	timer->regs->tclr = AMDM37x_GPT_TCLR_AR_FLAG;
+	timer->regs->tclr |= AMDM37x_GPT_TCLR_AR_FLAG;
 
 	timer->special_available = (
@@ -216,13 +227,16 @@
 	    (ioregs == AMDM37x_GPT2_BASE_ADDRESS) ||
 	    (ioregs == AMDM37x_GPT10_BASE_ADDRESS));
+	/* Select reload value */
 	timer->regs->tldr = 0xffffffff - (32768 / hz) + 1;
+	/* Set current counter value */
 	timer->regs->tccr = 0xffffffff - (32768 / hz) + 1;
+
 	if (timer->special_available) {
-		/* Set values for according to formula (manual p. 2733) */
+		/* Set values according to formula (manual p. 2733) */
 		/* Use temporary variables for easier debugging */
 		const uint32_t tpir =
 		    ((32768 / hz + 1) * 1000000) - (32768000L * (1000 / hz));
 		const uint32_t tnir =
-		    ((32768 / hz) * 1000000) - (32768000 * (1000 / hz));
+		    ((32768 / hz) * 1000000) - (32768000L * (1000 / hz));
 		timer->regs->tpir = tpir;
 		timer->regs->tnir = tnir;
@@ -241,10 +255,12 @@
 }
 
-static inline void amdm37x_gpt_irq_ack(amdm37x_gpt_t* timer)
+static inline bool amdm37x_gpt_irq_ack(amdm37x_gpt_t* timer)
 {
 	ASSERT(timer);
 	ASSERT(timer->regs);
 	/* Clear all pending interrupts */
-	timer->regs->tisr = timer->regs->tisr;
+	const uint32_t tisr = timer->regs->tisr;
+	timer->regs->tisr = tisr;
+	return tisr != 0;
 }
 
Index: kernel/genarch/include/genarch/drivers/amdm37x/irc.h
===================================================================
--- kernel/genarch/include/genarch/drivers/amdm37x/irc.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/genarch/include/genarch/drivers/amdm37x/irc.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,165 +38,14 @@
 
 /* AMDM37x TRM p. 1079 */
-#define AMDM37x_IRC_BASE_ADDRESS 0x48200000
-#define AMDM37x_IRC_SIZE 4096
+#define AMDM37x_IRC_BASE_ADDRESS     0x48200000
+#define AMDM37x_IRC_SIZE             4096
 
-#define AMDM37x_IRC_IRQ_COUNT 96
+#define AMDM37x_IRC_IRQ_COUNT        96
+#define AMDM37x_IRC_IRQ_GROUPS_COUNT 3
 
-#include <typedefs.h>
+#define OMAP_IRC_IRQ_COUNT        AMDM37x_IRC_IRQ_COUNT
+#define OMAP_IRC_IRQ_GROUPS_COUNT AMDM37x_IRC_IRQ_GROUPS_COUNT
 
-typedef struct {
-	const ioport32_t revision; /**< Revision */
-#define AMDM37x_IRC_REV_MASK (0xff)
-
-	uint8_t padd0_[12];
-
-	ioport32_t sysconfig; /**< SYS config */
-#define AMDM37x_IRC_SYSCONFIG_AUTOIDLE_FLAG (1 << 0)
-#define AMDM37x_IRC_SYSCONFIG_SOFTRESET_FLAG (1 << 1)
-
-	const ioport32_t sysstatus; /**< SYS status */
-#define AMDM37x_IRC_SYSSTATUS_RESET_DONE_FLAG (1 << 0)
-
-	uint8_t padd1_[40];
-
-	const ioport32_t sir_irq;   /**< Currently active irq number */
-#define AMDM37x_IRC_SIR_IRQ_ACTIVEIRQ_MASK (0x7f)
-#define AMDM37x_IRC_SIR_IRQ_SPURIOUSIRQFLAG_MASK (0xfffffff8)
-
-	const ioport32_t sir_fiq;
-#define AMDM37x_IRC_SIR_FIQ_ACTIVEIRQ_MASK (0x7f)
-#define AMDM37x_IRC_SIR_FIQ_SPURIOUSIRQFLAG_MASK (0xfffffff8)
-
-	ioport32_t control;   /**< New interrupt agreement. */
-#define AMDM37x_IRC_CONTROL_NEWIRQAGR_FLAG (1 << 0)
-#define AMDM37x_IRC_CONTROL_NEWFIQAGR_FLAG (1 << 1)
-
-	ioport32_t protection;  /**< Protect other registers. */
-#define AMDM37x_IRC_PROTECTION_PROETCTION_FLAG (1 << 0)
-
-	ioport32_t idle;   /**< Idle and autogating */
-#define AMDM37x_IRC_IDLE_FUNCIDLE_FLAG (1 << 0)
-#define AMDM37x_IRC_IDLE_TURBO_FLAG (1 << 1)
-
-	uint8_t padd2_[12];
-
-	ioport32_t irq_priority; /**< Active IRQ priority */
-#define AMDM37x_IRC_IRQ_PRIORITY_IRQPRIORITY_MASK (0x7f)
-#define AMDM37x_IRC_IRQ_PRIORITY_SPURIOUSIRQFLAG_MASK (0xfffffff8)
-
-	ioport32_t fiq_priority; /**< Active FIQ priority */
-#define AMDM37x_IRC_FIQ_PRIORITY_FIQPRIORITY_MASK (0x7f)
-#define AMDM37x_IRC_FIQ_PRIORITY_SPURIOUSFIQFLAG_MASK (0xfffffff8)
-
-	ioport32_t threshold; /**< Priority threshold */
-#define AMDM37x_IRC_THRESHOLD_PRIORITYTHRESHOLD_MASK (0xff)
-#define AMDM37x_IRC_THRESHOLD_PRIORITYTHRESHOLD_ENABLED (0x00)
-#define AMDM37x_IRC_THRESHOLD_PRIORITYTHRESHOLD_DISABLED (0xff)
-
-	uint8_t padd3__[20];
-
-	struct {
-		const ioport32_t itr;   /**< Interrupt input status before masking */
-		ioport32_t mir;   /**< Interrupt mask */
-		ioport32_t mir_clear; /**< Clear mir mask bits */
-		ioport32_t mir_set;   /**< Set mir mask bits */
-		ioport32_t isr_set;   /**< Set software interrupt bits */
-		ioport32_t isr_clear; /**< Clear software interrupt bits */
-		const ioport32_t pending_irq; /**< IRQ status after masking */
-		const ioport32_t pending_fiq; /**< FIQ status after masking */
-	} interrupts[3];
-
-	uint8_t padd4_[32];
-
-	ioport32_t ilr[96];   /**< FIQ/IRQ steering */
-#define AMDM37x_IRC_ILR_FIQNIRQ (1 << 0)
-#define AMDM37x_IRC_ILR_PRIORITY_MASK (0x3f)
-#define AMDM37x_IRC_ILR_PRIORITY_SHIFT (2)
-
-} amdm37x_irc_regs_t;
-
-static inline void amdm37x_irc_dump(amdm37x_irc_regs_t *regs)
-{
-#define DUMP_REG(name) \
-	printf("%s %p(%x).\n", #name, &regs->name, regs->name);
-
-	DUMP_REG(revision);
-	DUMP_REG(sysconfig);
-	DUMP_REG(sysstatus);
-	DUMP_REG(sir_irq);
-	DUMP_REG(sir_fiq);
-	DUMP_REG(control);
-	DUMP_REG(protection);
-	DUMP_REG(idle);
-	DUMP_REG(irq_priority);
-	DUMP_REG(fiq_priority);
-	DUMP_REG(threshold);
-
-	for (int i = 0; i < 3; ++i) {
-		DUMP_REG(interrupts[i].itr);
-		DUMP_REG(interrupts[i].mir);
-		DUMP_REG(interrupts[i].isr_set);
-		DUMP_REG(interrupts[i].pending_irq);
-		DUMP_REG(interrupts[i].pending_fiq);
-	}
-	for (int i = 0; i < AMDM37x_IRC_IRQ_COUNT; ++i) {
-		DUMP_REG(ilr[i]);
-	}
-
-#undef DUMP_REG
-}
-
-static inline void amdm37x_irc_init(amdm37x_irc_regs_t *regs)
-{
-	/* AMDM37x TRM sec 12.5.1 p. 2425 */
-	/* Program system config register */
-	//TODO enable this when you know the meaning
-	//regs->sysconfig |= AMDM37x_IRC_SYSCONFIG_AUTOIDLE_FLAG;
-
-	/* Program idle register */
-	//TODO enable this when you know the meaning
-	//regs->sysconfig |= AMDM37x_IRC_IDLE_TURBO_FLAG;
-
-	/* Program ilr[m] assign priority, decide fiq */
-	for (unsigned i = 0; i < AMDM37x_IRC_IRQ_COUNT; ++i) {
-		regs->ilr[i] = 0; /* highest prio(default) route to irq */
-	}
-
-	/* Disable all interrupts */
-	regs->interrupts[0].mir_set = 0xffffffff;
-	regs->interrupts[1].mir_set = 0xffffffff;
-	regs->interrupts[2].mir_set = 0xffffffff;
-}
-
-static inline unsigned amdm37x_irc_inum_get(amdm37x_irc_regs_t *regs)
-{
-	return regs->sir_irq & AMDM37x_IRC_SIR_IRQ_ACTIVEIRQ_MASK;
-}
-
-static inline void amdm37x_irc_irq_ack(amdm37x_irc_regs_t *regs)
-{
-	regs->control = AMDM37x_IRC_CONTROL_NEWIRQAGR_FLAG;
-}
-
-static inline void amdm37x_irc_fiq_ack(amdm37x_irc_regs_t *regs)
-{
-	regs->control = AMDM37x_IRC_CONTROL_NEWFIQAGR_FLAG;
-}
-
-static inline void amdm37x_irc_enable(amdm37x_irc_regs_t *regs, unsigned inum)
-{
-	ASSERT(inum < AMDM37x_IRC_IRQ_COUNT);
-	const unsigned set = inum / 32;
-	const unsigned pos = inum % 32;
-	regs->interrupts[set].mir_clear = (1 << pos);
-}
-
-static inline void amdm37x_irc_disable(amdm37x_irc_regs_t *regs, unsigned inum)
-{
-	ASSERT(inum < AMDM37x_IRC_IRQ_COUNT);
-	const unsigned set = inum / 32;
-	const unsigned pos = inum % 32;
-	regs->interrupts[set].mir_set = (1 << pos);
-}
+#include <genarch/drivers/omap/irc.h>
 
 #endif
Index: kernel/genarch/include/genarch/drivers/omap/irc.h
===================================================================
--- kernel/genarch/include/genarch/drivers/omap/irc.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ kernel/genarch/include/genarch/drivers/omap/irc.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2012 Maurizio Lombardi
+ *
+ * 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 genarch
+ * @{
+ */
+
+/**
+ * @file
+ * @brief Texas Instruments OMAP on-chip interrupt controller driver.
+ */
+
+#ifndef KERN_OMAP_IRQC_H_
+#define KERN_OMAP_IRQC_H_
+
+#include <typedefs.h>
+
+#define OMAP_IRC_IRQ_GROUPS_PAD (4 - OMAP_IRC_IRQ_GROUPS_COUNT)
+
+typedef struct {
+	const ioport32_t revision;
+#define OMAP_IRC_REV_MASK 0xFF
+
+	const uint8_t padd0[12];
+
+	/* This register controls the various parameters
+	 * of the OCP interface.
+	 */
+	ioport32_t sysconfig;
+#define OMAP_IRC_SYSCONFIG_AUTOIDLE_FLAG   (1 << 0)
+#define OMAP_IRC_SYSCONFIG_SOFTRESET_FLAG  (1 << 1)
+
+	/* This register provides status information about the module */
+	const ioport32_t sysstatus;
+#define OMAP_IRC_SYSSTATUS_RESET_DONE_FLAG (1 << 0)
+
+	const uint8_t padd1[40];
+
+	/* This register supplies the currently active IRQ interrupt number */
+	ioport32_t sir_irq;
+#define OMAP_IRC_SIR_IRQ_ACTIVEIRQ_MASK       0x7F
+#define OMAP_IRC_SIR_IRQ_SPURIOUSIRQFLAG_MASK 0xFFFFFFF8
+
+	/* This register supplies the currently active FIQ interrupt number */
+	const ioport32_t sir_fiq;
+#define OMAP_IRC_FIQ_IRQ_ACTIVEFIQ_MASK       0x7F
+#define OMAP_IRC_FIQ_IRQ_SPURIOUSFIQFLAG_MASK 0xFFFFFFF8
+
+	/* This register contains the new interrupt agreement bits */
+	ioport32_t control;
+#define OMAP_IRC_CONTROL_NEWIRQAGR_FLAG       (1 << 0)
+#define OMAP_IRC_CONTROL_NEWFIQAGR_FLAG       (1 << 1)
+
+	/* This register controls protection of the other registers.
+	 * This register can only be accessed in priviledged mode, regardless
+	 * of the current value of the protection bit.
+	 */
+	ioport32_t protection;
+#define OMAP_IRC_PROTECTION_FLAG              (1 << 0)
+
+	/* This register controls the clock auto-idle for the functional
+	 * clock and the input synchronizers.
+	 */
+	ioport32_t idle;
+#define OMAP_IRC_IDLE_FUNCIDLE_FLAG           (1 << 0)
+#define OMAP_IRC_IDLE_TURBO_FLAG              (1 << 1)
+
+	const uint8_t padd2[12];
+
+	/* This register supplies the currently active IRQ priority level */
+	const ioport32_t irq_priority;
+#define OMAP_IRC_IRQ_PRIORITY_IRQPRIORITY_MASK     0x7F
+#define OMAP_IRC_IRQ_PRIORITY_SPURIOUSIRQFLAG_MASK 0xFFFFFFF8
+
+	/* This register supplies the currently active FIQ priority level */
+	const ioport32_t fiq_priority;
+#define OMAP_IRC_FIQ_PRIORITY_FIQPRIORITY_MASK     0x7F
+#define OMAP_IRC_FIQ_PRIORITY_SPURIOUSIRQFLAG_MASK 0xFFFFFFF8
+
+	/* This register sets the priority threshold */
+	ioport32_t threshold;
+#define OMAP_IRC_THRESHOLD_PRIORITYTHRESHOLD_MASK     0xFF
+#define OMAP_IRC_THRESHOLD_PRIORITYTHRESHOLD_ENABLED  0x00
+#define OMAP_IRC_THRESHOLD_PRIORITYTHRESHOLD_DISABLED 0xFF
+
+	const uint8_t padd3[20];
+
+	struct {
+		/* Raw interrupt input status before masking */
+		const ioport32_t itr;
+
+		/* Interrupt mask */
+		ioport32_t mir;
+
+		/* This register is used to clear the interrupt mask bits,
+		 * Write 1 clears the mask bit to 0.
+		 */
+		ioport32_t mir_clear;
+
+		/* This register is used to set the interrupt mask bits,
+		 * Write 1 sets the mask bit to 1.
+		 */
+		ioport32_t mir_set;
+
+		/* This register is used to set the software interrupt bits,
+		 * it is also used to read the current active software
+		 * interrupts.
+		 * Write 1 sets the software interrups bits to 1.
+		 */
+		ioport32_t isr_set;
+
+		/* This register is used to clear the software interrups bits.
+		 * Write 1 clears the software interrupt bits to 0.
+		 */
+		ioport32_t isr_clear;
+
+		/* This register contains the IRQ status after masking. */
+		const ioport32_t pending_irq;
+
+		/* This register contains the FIQ status after masking. */
+		const ioport32_t pending_fiq;
+	} interrupts[OMAP_IRC_IRQ_GROUPS_COUNT];
+
+	const uint32_t padd4[8 * OMAP_IRC_IRQ_GROUPS_PAD];
+
+	/* These registers contain the priority for the interrups and
+	 * the FIQ/IRQ steering.
+	 */
+	ioport32_t ilr[OMAP_IRC_IRQ_COUNT];
+/* 0 = Interrupt routed to IRQ, 1 = interrupt routed to FIQ */
+#define OMAP_IRC_ILR_FIQNIRQ_FLAG    (1 << 0)
+#define OMAP_IRC_ILR_PRIORITY_MASK   0x3F
+#define OMAP_IRC_ILR_PRIORITY_SHIFT  2
+
+} omap_irc_regs_t;
+
+static inline void omap_irc_init(omap_irc_regs_t *regs)
+{
+	int i;
+
+	/* Initialization sequence */
+
+	/* 1 - Program the SYSCONFIG register: if necessary, enable the
+	 *     autogating by setting the AUTOIDLE bit.
+	 */
+	regs->sysconfig &= ~OMAP_IRC_SYSCONFIG_AUTOIDLE_FLAG;
+
+	/* 2 - Program the IDLE register: if necessary, disable functional
+	 *     clock autogating or enable synchronizer autogating by setting
+	 *     the FUNCIDLE bit or the TURBO bit accordingly.
+	 */
+	regs->idle &= ~OMAP_IRC_IDLE_FUNCIDLE_FLAG;
+	regs->idle &= ~OMAP_IRC_IDLE_TURBO_FLAG;
+
+	/* 3 - Program ILRm register for each interrupt line: Assign a
+	 *     priority level and set the FIQNIRQ bit for an FIQ interrupt
+	 *     (by default, interrupts are mapped to IRQ and
+	 *     priority is 0 (highest).
+	 */
+
+	for (i = 0; i < OMAP_IRC_IRQ_COUNT; ++i)
+		regs->ilr[i] = 0;
+
+	/* 4 - Program the MIRn register: Enable interrupts (by default,
+	 *     all interrupt lines are masked).
+	 */
+	for (i = 0; i < OMAP_IRC_IRQ_GROUPS_COUNT; ++i)
+		regs->interrupts[i].mir_set = 0xFFFFFFFF;
+}
+
+/** Get the currently active IRQ interrupt number
+ *
+ * @param regs     Pointer to the irc memory mapped registers
+ *
+ * @return         The active IRQ interrupt number
+ */
+static inline unsigned omap_irc_inum_get(omap_irc_regs_t *regs)
+{
+	return regs->sir_irq & OMAP_IRC_SIR_IRQ_ACTIVEIRQ_MASK;
+}
+
+/** Reset IRQ output and enable new IRQ generation
+ *
+ * @param regs    Pointer to the irc memory mapped registers
+ */
+static inline void omap_irc_irq_ack(omap_irc_regs_t *regs)
+{
+	regs->control = OMAP_IRC_CONTROL_NEWIRQAGR_FLAG;
+}
+
+/** Reset FIQ output and enable new FIQ generation
+ *
+ * @param regs    Pointer to the irc memory mapped registers
+ */
+static inline void omap_irc_fiq_ack(omap_irc_regs_t *regs)
+{
+	regs->control = OMAP_IRC_CONTROL_NEWFIQAGR_FLAG;
+}
+
+/** Clear an interrupt mask bit
+ *
+ * @param regs    Pointer to the irc memory mapped registers
+ * @param inum    The interrupt to be enabled
+ */
+static inline void omap_irc_enable(omap_irc_regs_t *regs, unsigned inum)
+{
+	ASSERT(inum < OMAP_IRC_IRQ_COUNT);
+	const unsigned set = inum / 32;
+	const unsigned pos = inum % 32;
+	regs->interrupts[set].mir_clear = (1 << pos);
+}
+
+/** Set an interrupt mask bit
+ *
+ * @param regs    Pointer to the irc memory mapped registers
+ * @param inum    The interrupt to be disabled
+ */
+static inline void omap_irc_disable(omap_irc_regs_t *regs, unsigned inum)
+{
+	ASSERT(inum < OMAP_IRC_IRQ_COUNT);
+	const unsigned set = inum / 32;
+	const unsigned pos = inum % 32;
+	regs->interrupts[set].mir_set = (1 << pos);
+}
+
+static inline void omap_irc_dump(omap_irc_regs_t *regs)
+{
+#define DUMP_REG(name) \
+	printf("%s %p(%x).\n", #name, &regs->name, regs->name);
+
+	DUMP_REG(revision);
+	DUMP_REG(sysconfig);
+	DUMP_REG(sysstatus);
+	DUMP_REG(sir_irq);
+	DUMP_REG(sir_fiq);
+	DUMP_REG(control);
+	DUMP_REG(protection);
+	DUMP_REG(idle);
+	DUMP_REG(irq_priority);
+	DUMP_REG(fiq_priority);
+	DUMP_REG(threshold);
+
+	for (int i = 0; i < OMAP_IRC_IRQ_GROUPS_COUNT; ++i) {
+		DUMP_REG(interrupts[i].itr);
+		DUMP_REG(interrupts[i].mir);
+		DUMP_REG(interrupts[i].isr_set);
+		DUMP_REG(interrupts[i].pending_irq);
+		DUMP_REG(interrupts[i].pending_fiq);
+	}
+	for (int i = 0; i < OMAP_IRC_IRQ_COUNT; ++i) {
+		DUMP_REG(ilr[i]);
+	}
+
+#undef DUMP_REG
+}
+
+#endif
+
+/**
+ * @}
+ */
Index: kernel/genarch/include/genarch/mm/page_pt.h
===================================================================
--- kernel/genarch/include/genarch/mm/page_pt.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/genarch/include/genarch/mm/page_pt.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/genarch/src/mm/as_pt.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -73,14 +73,12 @@
 pte_t *ptl0_create(unsigned int flags)
 {
-	pte_t *dst_ptl0 = (pte_t *) frame_alloc(PTL0_SIZE,
-	    FRAME_LOWMEM | FRAME_KA);
-	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 {
 		/*
 		 * Copy the kernel address space portion to new PTL0.
-		 *
 		 */
 		
@@ -95,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);
@@ -114,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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/genarch/src/mm/page_pt.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -82,7 +82,7 @@
 	
 	if (GET_PTL1_FLAGS(ptl0, PTL0_INDEX(page)) & PAGE_NOT_PRESENT) {
-		pte_t *newpt = (pte_t *) frame_alloc(PTL1_SIZE,
-		    FRAME_LOWMEM | FRAME_KA);
-		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 *) frame_alloc(PTL2_SIZE,
-		    FRAME_LOWMEM | FRAME_KA);
-		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),
@@ -112,5 +112,5 @@
 		 */
 		write_barrier();
-		SET_PTL2_PRESENT(ptl1, PTL1_INDEX(page));	
+		SET_PTL2_PRESENT(ptl1, PTL1_INDEX(page));
 	}
 	
@@ -118,7 +118,7 @@
 	
 	if (GET_PTL3_FLAGS(ptl2, PTL2_INDEX(page)) & PAGE_NOT_PRESENT) {
-		pte_t *newpt = (pte_t *) frame_alloc(PTL3_SIZE,
-		    FRAME_LOWMEM | FRAME_KA);
-		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),
@@ -180,5 +180,9 @@
 	 * Destroy the mapping.
 	 * Setting to PAGE_NOT_PRESENT is not sufficient.
-	 */
+	 * But we need SET_FRAME for possible PT coherence maintenance.
+	 * At least on ARM.
+	 */
+	//TODO: Fix this inconsistency
+	SET_FRAME_FLAGS(ptl3, PTL3_INDEX(page), PAGE_NOT_PRESENT);
 	memsetb(&ptl3[PTL3_INDEX(page)], sizeof(pte_t), 0);
 	
@@ -215,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 {
 		/*
@@ -249,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 {
 		/*
@@ -280,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 */
@@ -359,28 +363,27 @@
  * @param size Size in bytes defining the range of PTL0 entries that will be
  *             altered by this function.
+ *
  */
 void pt_mapping_make_global(uintptr_t base, size_t size)
 {
+	ASSERT(size > 0);
+	
 	uintptr_t ptl0 = PA2KA((uintptr_t) AS_KERNEL->genarch.page_table);
 	uintptr_t ptl0_step = ptl0_step_get();
-	size_t order;
-	uintptr_t addr;
-
+	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;
-#endif
-
-	ASSERT(size > 0);
-
-	for (addr = ALIGN_DOWN(base, ptl0_step); addr - 1 < base + size - 1;
+	frames = PTL3_FRAMES;
+#endif
+	
+	for (uintptr_t addr = ALIGN_DOWN(base, ptl0_step);
+	    addr - 1 < base + size - 1;
 	    addr += ptl0_step) {
-		uintptr_t l1;
-
-		l1 = (uintptr_t) frame_alloc(order, FRAME_KA | FRAME_LOWMEM);
-		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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/include/adt/bitmap.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,24 +38,50 @@
 #include <typedefs.h>
 
-#define BITS2BYTES(bits)	(bits ? ((((bits)-1)>>3)+1) : 0)
+#define BITMAP_ELEMENT   8
+#define BITMAP_REMAINER  7
 
 typedef struct {
-	uint8_t *map;
-	size_t bits;
+	size_t elements;
+	uint8_t *bits;
+	size_t next_fit;
 } bitmap_t;
 
-extern void bitmap_initialize(bitmap_t *bitmap, uint8_t *map, size_t bits);
-extern void bitmap_set_range(bitmap_t *bitmap, size_t start, size_t bits);
-extern void bitmap_clear_range(bitmap_t *bitmap, size_t start, size_t bits);
-extern void bitmap_copy(bitmap_t *dst, bitmap_t *src, size_t bits);
+static inline void bitmap_set(bitmap_t *bitmap, size_t element,
+    unsigned int value)
+{
+	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;
+	}
+}
 
-static inline int bitmap_get(bitmap_t *bitmap, size_t bit)
+static inline unsigned int bitmap_get(bitmap_t *bitmap, size_t element)
 {
-	if(bit >= bitmap->bits)
+	if (element >= bitmap->elements)
 		return 0;
 	
-	return !! ((bitmap->map)[bit/8] & (1 << (bit & 7)));
+	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);
+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_allocate_range(bitmap_t *, size_t, size_t, size_t, size_t,
+    size_t *);
+extern void bitmap_copy(bitmap_t *, bitmap_t *, size_t);
 
 #endif
Index: kernel/generic/include/adt/list.h
===================================================================
--- kernel/generic/include/adt/list.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/include/adt/list.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2001-2004 Jakub Jermar
- * Copyright (c) 2011 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
  * All rights reserved.
  *
@@ -65,9 +65,11 @@
 
 #define list_get_instance(link, type, member) \
-	((type *) (((void *)(link)) - ((void *) &(((type *) NULL)->member))))
-
-#define list_foreach(list, iterator) \
-	for (link_t *iterator = (list).head.next; \
-	    iterator != &(list).head; iterator = iterator->next)
+	((type *) (((void *)(link)) - list_link_to_void(&(((type *) NULL)->member))))
+
+#define list_foreach(list, member, itype, iterator) \
+	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) 1) \
+	    for (link_t *_link = (list).head.next; \
+	    iterator = list_get_instance(_link, itype, member), \
+	    _link != &(list).head; _link = _link->next)
 
 #define assert_link_not_used(link) \
@@ -204,4 +206,28 @@
 }
 
+/** Get next item in list.
+ *
+ * @param link Current item link
+ * @param list List containing @a link
+ *
+ * @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)
+{
+	return (link->next == &list->head) ? NULL : link->next;
+}
+
+/** Get previous item in list.
+ *
+ * @param link Current item link
+ * @param list List containing @a link
+ *
+ * @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)
+{
+	return (link->prev == &list->head) ? NULL : link->prev;
+}
+
 /** Split or concatenate headless doubly-linked circular list
  *
@@ -270,13 +296,25 @@
 {
 	unsigned int cnt = 0;
-	
-	list_foreach(*list, link) {
+	link_t *link;
+	
+	link = list_first(list);
+	while (link != NULL) {
 		if (cnt == n)
 			return link;
 		
 		cnt++;
+		link = list_next(link, list);
 	}
 	
 	return NULL;
+}
+
+/** Verify that argument type is a pointer to link_t (at compile time).
+ *
+ * This can be used to check argument type in a macro.
+ */
+static inline const void *list_link_to_void(const link_t *link)
+{
+	return link;
 }
 
Index: kernel/generic/include/config.h
===================================================================
--- kernel/generic/include/config.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/include/config.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,23 +37,14 @@
 
 #include <arch/mm/page.h>
+#include <macros.h>
 
-#define ONE_FRAME    0
-#define TWO_FRAMES   1
-#define FOUR_FRAMES  2
+#define STACK_FRAMES  2
+#define STACK_SIZE    FRAMES2SIZE(STACK_FRAMES)
 
-#define STACK_FRAMES  TWO_FRAMES
-#define STACK_SIZE    ((1 << STACK_FRAMES) << PAGE_WIDTH)
-
-#define STACK_SIZE_USER	(1 * 1024 * 1024)
+#define STACK_SIZE_USER  (1 * 1024 * 1024)
 
 #define CONFIG_INIT_TASKS        32
 #define CONFIG_TASK_NAME_BUFLEN  32
 #define CONFIG_TASK_ARGUMENTS_BUFLEN 64
-
-/**
- * Maximum buffer size allowed for IPC_M_DATA_WRITE and IPC_M_DATA_READ
- * requests.
- */
-#define DATA_XFER_LIMIT  (64 * 1024)
 
 #ifndef __ASM__
@@ -97,5 +88,5 @@
 	/** Size of initial stack. */
 	size_t stack_size;
-
+	
 	bool identity_configured;
 	/** Base address of the kernel identity mapped memory. */
@@ -103,7 +94,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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/include/macros.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ 	(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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/include/mm/frame.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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>
@@ -50,40 +50,38 @@
 typedef uint8_t frame_flags_t;
 
-#define FRAME_NONE        0x0
-/** Convert the frame address to kernel VA. */
-#define FRAME_KA          0x1
+#define FRAME_NONE        0x00
 /** Do not panic and do not sleep on failure. */
-#define FRAME_ATOMIC      0x2
+#define FRAME_ATOMIC      0x01
 /** Do not start reclaiming when no free memory. */
-#define FRAME_NO_RECLAIM  0x4
+#define FRAME_NO_RECLAIM  0x02
 /** Do not reserve / unreserve memory. */
-#define FRAME_NO_RESERVE  0x8
+#define FRAME_NO_RESERVE  0x04
 /** Allocate a frame which can be identity-mapped. */
-#define FRAME_LOWMEM	  0x10
+#define FRAME_LOWMEM      0x08
 /** Allocate a frame which cannot be identity-mapped. */
-#define FRAME_HIGHMEM	  0x20
+#define FRAME_HIGHMEM     0x10
 
 typedef uint8_t zone_flags_t;
 
-#define ZONE_NONE	0x0
+#define ZONE_NONE       0x00
 /** Available zone (free for allocation) */
-#define ZONE_AVAILABLE  0x1
+#define ZONE_AVAILABLE  0x01
 /** Zone is reserved (not available for allocation) */
-#define ZONE_RESERVED   0x2
+#define ZONE_RESERVED   0x02
 /** Zone is used by firmware (not available for allocation) */
-#define ZONE_FIRMWARE   0x4
+#define ZONE_FIRMWARE   0x04
 /** Zone contains memory that can be identity-mapped */
-#define ZONE_LOWMEM	0x8
+#define ZONE_LOWMEM     0x08
 /** Zone contains memory that cannot be identity-mapped */
-#define ZONE_HIGHMEM	0x10
+#define ZONE_HIGHMEM    0x10
 
 /** Mask of zone bits that must be matched exactly. */
-#define ZONE_EF_MASK	0x7
+#define ZONE_EF_MASK  0x07
 
-#define FRAME_TO_ZONE_FLAGS(ff)	\
+#define FRAME_TO_ZONE_FLAGS(ff) \
 	((((ff) & FRAME_LOWMEM) ? ZONE_LOWMEM : \
 	    (((ff) & FRAME_HIGHMEM) ? ZONE_HIGHMEM : \
 	    ZONE_LOWMEM /* | ZONE_HIGHMEM */)) | \
-	    ZONE_AVAILABLE) 
+	    ZONE_AVAILABLE)
 
 #define ZONE_FLAGS_MATCH(zf, f) \
@@ -92,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;
 
@@ -126,45 +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)
-		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 void *frame_alloc_generic(uint8_t, frame_flags_t, size_t *);
-extern void *frame_alloc(uint8_t, frame_flags_t);
-extern void *frame_alloc_noreserve(uint8_t, frame_flags_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 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/page.h
===================================================================
--- kernel/generic/include/mm/page.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/include/mm/page.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -65,6 +65,6 @@
 extern void page_table_destroy(pte_t *);
 
-extern int page_find_mapping(uintptr_t, void **);
-extern sysarg_t sys_page_find_mapping(uintptr_t, void *);
+extern int page_find_mapping(uintptr_t, uintptr_t *);
+extern sysarg_t sys_page_find_mapping(uintptr_t, uintptr_t *);
 
 #endif
Index: kernel/generic/include/mm/slab.h
===================================================================
--- kernel/generic/include/mm/slab.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/include/mm/slab.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 */
@@ -90,4 +90,5 @@
 	
 	/* Configuration */
+	
 	/** Size of slab position - align_up(sizeof(obj)) */
 	size_t size;
@@ -100,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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/adt/bitmap.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -35,5 +35,6 @@
  *
  * This file implements bitmap ADT and provides functions for
- * setting and clearing ranges of bits.
+ * setting and clearing ranges of bits and for finding ranges
+ * of unset bits.
  */
 
@@ -44,6 +45,43 @@
 #include <macros.h>
 
-#define ALL_ONES 	0xff
-#define ALL_ZEROES	0x00
+#define ALL_ONES    0xff
+#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
+ *
+ * Return the size (in bytes) required for the bitmap.
+ *
+ * @param elements   Number bits stored in bitmap.
+ *
+ * @return Size (in bytes) required for the bitmap.
+ *
+ */
+size_t bitmap_size(size_t elements)
+{
+	size_t size = elements / BITMAP_ELEMENT;
+	
+	if ((elements % BITMAP_ELEMENT) != 0)
+		size++;
+	
+	return size;
+}
 
 /** Initialize bitmap.
@@ -51,41 +89,48 @@
  * No portion of the bitmap is set or cleared by this function.
  *
- * @param bitmap	Bitmap structure.
- * @param map		Address of the memory used to hold the map.
- * @param bits		Number of bits stored in bitmap.
- */
-void bitmap_initialize(bitmap_t *bitmap, uint8_t *map, size_t bits)
-{
-	bitmap->map = map;
-	bitmap->bits = bits;
+ * @param bitmap     Bitmap structure.
+ * @param elements   Number of bits stored in bitmap.
+ * @param data       Address of the memory used to hold the map.
+ *                   The optional 2nd level bitmap follows the 1st
+ *                   level bitmap.
+ *
+ */
+void bitmap_initialize(bitmap_t *bitmap, size_t elements, void *data)
+{
+	bitmap->elements = elements;
+	bitmap->bits = (uint8_t *) data;
+	bitmap->next_fit = 0;
 }
 
 /** Set range of bits.
  *
- * @param bitmap	Bitmap structure.
- * @param start		Starting bit.
- * @param bits		Number of bits to set.
- */
-void bitmap_set_range(bitmap_t *bitmap, size_t start, size_t bits)
-{
-	size_t i = 0;
-	size_t aligned_start;
-	size_t lub;	/* leading unaligned bits */
-	size_t amb;	/* aligned middle bits */
-	size_t tab;	/* trailing aligned bits */
-	
-	ASSERT(start + bits <= bitmap->bits);
-	
-	aligned_start = ALIGN_UP(start, 8);
-	lub = min(aligned_start - start, bits);
-	amb = bits > lub ? bits - lub : 0;
-	tab = amb % 8;
-	
-	if (!bits)
+ * @param bitmap Bitmap structure.
+ * @param start  Starting bit.
+ * @param count  Number of bits to set.
+ *
+ */
+void bitmap_set_range(bitmap_t *bitmap, size_t start, size_t count)
+{
+	ASSERT(start + count <= bitmap->elements);
+	
+	if (count == 0)
 		return;
-
-	if (start + bits < aligned_start) {
+	
+	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->map[start / 8] |= ((1 << lub) - 1) << (start & 7);
+		bitmap->bits[start_byte] |=
+		    ((1 << lub) - 1) << (start & BITMAP_REMAINER);
 		return;
 	}
@@ -93,82 +138,199 @@
 	if (lub) {
 		/* Make sure to set any leading unaligned bits. */
-		bitmap->map[start / 8] |= ~((1 << (8 - lub)) - 1);
-	}
-	for (i = 0; i < amb / 8; i++) {
+		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 set byte by byte. */
-		bitmap->map[aligned_start / 8 + i] = ALL_ONES;
-	}
+		bitmap->bits[aligned_start / BITMAP_ELEMENT + i] =
+		    ALL_ONES;
+	}
+	
 	if (tab) {
 		/* Make sure to set any trailing aligned bits. */
-		bitmap->map[aligned_start / 8 + i] |= (1 << tab) - 1;
-	}
-	
+		bitmap->bits[aligned_start / BITMAP_ELEMENT + i] |=
+		    (1 << tab) - 1;
+	}
 }
 
 /** Clear range of bits.
  *
- * @param bitmap	Bitmap structure.
- * @param start		Starting bit.
- * @param bits		Number of bits to clear.
- */
-void bitmap_clear_range(bitmap_t *bitmap, size_t start, size_t bits)
-{
-	size_t i = 0;
-	size_t aligned_start;
-	size_t lub;	/* leading unaligned bits */
-	size_t amb;	/* aligned middle bits */
-	size_t tab;	/* trailing aligned bits */
-	
-	ASSERT(start + bits <= bitmap->bits);
-	
-	aligned_start = ALIGN_UP(start, 8);
-	lub = min(aligned_start - start, bits);
-	amb = bits > lub ? bits - lub : 0;
-	tab = amb % 8;
-
-	if (!bits)
+ * @param bitmap Bitmap structure.
+ * @param start  Starting bit.
+ * @param count  Number of bits to clear.
+ *
+ */
+void bitmap_clear_range(bitmap_t *bitmap, size_t start, size_t count)
+{
+	ASSERT(start + count <= bitmap->elements);
+	
+	if (count == 0)
 		return;
-
-	if (start + bits < aligned_start) {
+	
+	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->map[start / 8] &= ~(((1 << lub) - 1) << (start & 7));
+		bitmap->bits[start_byte] &=
+		    ~(((1 << lub) - 1) << (start & BITMAP_REMAINER));
 		return;
 	}
-
+	
 	if (lub) {
 		/* Make sure to clear any leading unaligned bits. */
-		bitmap->map[start / 8] &= (1 << (8 - lub)) - 1;
-	}
-	for (i = 0; i < amb / 8; i++) {
+		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->map[aligned_start / 8 + i] = ALL_ZEROES;
-	}
+		bitmap->bits[aligned_start / BITMAP_ELEMENT + i] =
+		    ALL_ZEROES;
+	}
+	
 	if (tab) {
 		/* Make sure to clear any trailing aligned bits. */
-		bitmap->map[aligned_start / 8 + i] &= ~((1 << tab) - 1);
-	}
-
+		bitmap->bits[aligned_start / BITMAP_ELEMENT + i] &=
+		    ~((1 << tab) - 1);
+	}
+	
+	bitmap->next_fit = start_byte;
 }
 
 /** Copy portion of one bitmap into another bitmap.
  *
- * @param dst		Destination bitmap.
- * @param src		Source bitmap.
- * @param bits		Number of bits to copy.
- */
-void bitmap_copy(bitmap_t *dst, bitmap_t *src, size_t bits)
-{
+ * @param dst   Destination bitmap.
+ * @param src   Source bitmap.
+ * @param count Number of bits to copy.
+ *
+ */
+void bitmap_copy(bitmap_t *dst, bitmap_t *src, size_t count)
+{
+	ASSERT(count <= dst->elements);
+	ASSERT(count <= src->elements);
+	
 	size_t i;
 	
-	ASSERT(bits <= dst->bits);
-	ASSERT(bits <= src->bits);
-	
-	for (i = 0; i < bits / 8; i++)
-		dst->map[i] = src->map[i];
-	
-	if (bits % 8) {
-		bitmap_clear_range(dst, i * 8, bits % 8);
-		dst->map[i] |= src->map[i] & ((1 << (bits % 8)) - 1);
-	}
+	for (i = 0; i < count / BITMAP_ELEMENT; i++)
+		dst->bits[i] = src->bits[i];
+	
+	if (count % BITMAP_ELEMENT) {
+		bitmap_clear_range(dst, i * BITMAP_ELEMENT,
+		    count % BITMAP_ELEMENT);
+		dst->bits[i] |= src->bits[i] &
+		    ((1 << (count % BITMAP_ELEMENT)) - 1);
+	}
+}
+
+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 prefered   Prefered address to start searching from.
+ * @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 prefered, size_t constraint, size_t *index)
+{
+	if (count == 0)
+		return false;
+	
+	size_t size = bitmap_size(bitmap->elements);
+	size_t next_fit = bitmap->next_fit;
+	
+	/*
+	 * Adjust the next-fit value according to the address
+	 * the caller prefers to start the search at.
+	 */
+	if ((prefered > base) && (prefered < base + bitmap->elements)) {
+		size_t prefered_fit = (prefered - base) / BITMAP_ELEMENT;
+		
+		if (prefered_fit > next_fit)
+			next_fit = prefered_fit;
+	}
+	
+	for (size_t pos = 0; pos < size; pos++) {
+		size_t byte = (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/adt/btree.c
===================================================================
--- kernel/generic/src/adt/btree.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/adt/btree.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1031,9 +1031,5 @@
 	
 	printf("Printing list of leaves:\n");
-	list_foreach(t->leaf_list, cur) {
-		btree_node_t *node;
-		
-		node = list_get_instance(cur, btree_node_t, leaf_link);
-		
+	list_foreach(t->leaf_list, leaf_link, btree_node_t, node) {
 		ASSERT(node);
 		
Index: kernel/generic/src/adt/hash_table.c
===================================================================
--- kernel/generic/src/adt/hash_table.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/adt/hash_table.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -117,5 +117,6 @@
 	ASSERT(chain < h->entries);
 	
-	list_foreach(h->entry[chain], cur) {
+	link_t *cur = list_first(&h->entry[chain]);
+	while (cur != NULL) {
 		if (h->op->compare(key, h->max_keys, cur)) {
 			/*
@@ -124,4 +125,5 @@
 			return cur;
 		}
+		cur = list_next(cur, &h->entry[chain]);
 	}
 	
Index: kernel/generic/src/adt/list.c
===================================================================
--- kernel/generic/src/adt/list.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/adt/list.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -101,6 +101,8 @@
 	unsigned int count = 0;
 	
-	list_foreach(*list, link) {
+	link_t *link = list_first(list);
+	while (link != NULL) {
 		count++;
+		link = list_next(link, list);
 	}
 	
Index: kernel/generic/src/console/cmd.c
===================================================================
--- kernel/generic/src/console/cmd.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/console/cmd.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -656,8 +656,5 @@
 	
 	size_t len = 0;
-	list_foreach(cmd_list, cur) {
-		cmd_info_t *hlp;
-		hlp = list_get_instance(cur, cmd_info_t, link);
-		
+	list_foreach(cmd_list, link, cmd_info_t, hlp) {
 		spinlock_lock(&hlp->lock);
 		if (str_length(hlp->name) > len)
@@ -672,8 +669,5 @@
 	}
 	
-	list_foreach(cmd_list, cur) {
-		cmd_info_t *hlp;
-		hlp = list_get_instance(cur, cmd_info_t, link);
-		
+	list_foreach(cmd_list, link, cmd_info_t, hlp) {
 		spinlock_lock(&hlp->lock);
 		printf("%-*s %s\n", _len, hlp->name, hlp->description);
@@ -912,8 +906,5 @@
 	spinlock_lock(&cmd_lock);
 	
-	list_foreach(cmd_list, cur) {
-		cmd_info_t *hlp;
-		
-		hlp = list_get_instance(cur, cmd_info_t, link);
+	list_foreach(cmd_list, link, cmd_info_t, hlp) {
 		spinlock_lock(&hlp->lock);
 		
Index: kernel/generic/src/console/console.c
===================================================================
--- kernel/generic/src/console/console.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/console/console.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -125,6 +125,5 @@
 static void stdout_write(outdev_t *dev, wchar_t ch)
 {
-	list_foreach(dev->list, cur) {
-		outdev_t *sink = list_get_instance(cur, outdev_t, link);
+	list_foreach(dev->list, link, outdev_t, sink) {
 		if ((sink) && (sink->op->write))
 			sink->op->write(sink, ch);
@@ -134,6 +133,5 @@
 static void stdout_redraw(outdev_t *dev)
 {
-	list_foreach(dev->list, cur) {
-		outdev_t *sink = list_get_instance(cur, outdev_t, link);
+	list_foreach(dev->list, link, outdev_t, sink) {
 		if ((sink) && (sink->op->redraw))
 			sink->op->redraw(sink);
Index: kernel/generic/src/console/kconsole.c
===================================================================
--- kernel/generic/src/console/kconsole.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/console/kconsole.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -53,5 +53,4 @@
 #include <func.h>
 #include <str.h>
-#include <macros.h>
 #include <sysinfo/sysinfo.h>
 #include <ddi/device.h>
@@ -119,7 +118,5 @@
 	 * Make sure the command is not already listed.
 	 */
-	list_foreach(cmd_list, cur) {
-		cmd_info_t *hlp = list_get_instance(cur, cmd_info_t, link);
-		
+	list_foreach(cmd_list, link, cmd_info_t, hlp) {
 		if (hlp == cmd) {
 			/* The command is already there. */
@@ -613,6 +610,5 @@
 	cmd_info_t *cmd = NULL;
 	
-	list_foreach(cmd_list, cur) {
-		cmd_info_t *hlp = list_get_instance(cur, cmd_info_t, link);
+	list_foreach(cmd_list, link, cmd_info_t, hlp) {
 		spinlock_lock(&hlp->lock);
 		
Index: kernel/generic/src/cpu/cpu.c
===================================================================
--- kernel/generic/src/cpu/cpu.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/cpu/cpu.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -73,12 +73,15 @@
 		size_t i;
 		for (i = 0; i < config.cpu_count; i++) {
-			cpus[i].stack = (uint8_t *) frame_alloc(STACK_FRAMES,
-			    FRAME_LOWMEM | FRAME_KA | FRAME_ATOMIC);
+			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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/ddi/ddi.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -314,5 +314,5 @@
 
 NO_TRACE static int dmamem_map(uintptr_t virt, size_t size, unsigned int map_flags,
-    unsigned int flags, void **phys)
+    unsigned int flags, uintptr_t *phys)
 {
 	ASSERT(TASK);
@@ -322,29 +322,22 @@
 }
 
-NO_TRACE static int dmamem_map_anonymous(size_t size, unsigned int map_flags,
-    unsigned int flags, void **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 pages = SIZE2FRAMES(size);
-	uint8_t order;
-	
-	/* We need the 2^order >= pages */
-	if (pages == 1)
-		order = 0;
-	else
-		order = fnzb(pages - 1) + 1;
-	
-	*phys = frame_alloc_noreserve(order, 0);
-	if (*phys == NULL)
+	size_t frames = SIZE2FRAMES(size);
+	*phys = frame_alloc(frames, FRAME_NO_RESERVE, constraint);
+	if (*phys == 0)
 		return ENOMEM;
 	
 	mem_backend_data_t backend_data;
-	backend_data.base = (uintptr_t) *phys;
-	backend_data.frames = pages;
+	backend_data.base = *phys;
+	backend_data.frames = frames;
 	
 	if (!as_area_create(TASK->as, map_flags, size,
 	    AS_AREA_ATTR_NONE, &phys_backend, &backend_data, virt, bound)) {
-		frame_free_noreserve((uintptr_t) *phys);
+		frame_free_noreserve(*phys, frames);
 		return ENOMEM;
 	}
@@ -373,5 +366,5 @@
 		 */
 		
-		void *phys;
+		uintptr_t phys;
 		int rc = dmamem_map((uintptr_t) virt_ptr, size, map_flags,
 		    flags, &phys);
@@ -390,7 +383,13 @@
 		 */
 		
-		void *phys;
+		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/debug/panic.c
===================================================================
--- kernel/generic/src/debug/panic.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/debug/panic.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -96,7 +96,13 @@
 	printf("THE=%p: ", THE);
 	if (THE != NULL) {
-		printf("pe=%" PRIun " thr=%p task=%p cpu=%p as=%p"
+		printf("pd=%" PRIun " thread=%p task=%p cpu=%p as=%p"
 		    " magic=%#" PRIx32 "\n", THE->preemption_disabled,
 		    THE->thread, THE->task, THE->cpu, THE->as, THE->magic);
+		
+		if (THE->thread != NULL)
+			printf("thread=\"%s\"\n", THE->thread->name);
+		
+		if (THE->task != NULL)
+			printf("task=\"%s\"\n", THE->task->name);
 	} else
 		printf("invalid\n");
Index: kernel/generic/src/debug/stacktrace.c
===================================================================
--- kernel/generic/src/debug/stacktrace.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/debug/stacktrace.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,5 +39,5 @@
 #include <print.h>
 
-#define STACK_FRAMES_MAX	20
+#define STACK_FRAMES_MAX  20
 
 void stack_trace_ctx(stack_trace_ops_t *ops, stack_trace_context_t *ctx)
@@ -49,6 +49,6 @@
 	uintptr_t pc;
 	
-	while (cnt++ < STACK_FRAMES_MAX &&
-	    ops->stack_trace_context_validate(ctx)) {
+	while ((cnt++ < STACK_FRAMES_MAX) &&
+	    (ops->stack_trace_context_validate(ctx))) {
 		if (ops->symbol_resolve &&
 		    ops->symbol_resolve(ctx->pc, &symbol, &offset)) {
Index: kernel/generic/src/ipc/ipc.c
===================================================================
--- kernel/generic/src/ipc/ipc.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/ipc/ipc.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -774,7 +774,5 @@
 static void ipc_print_call_list(list_t *list)
 {
-	list_foreach(*list, cur) {
-		call_t *call = list_get_instance(cur, call_t, ab_link);
-		
+	list_foreach(*list, ab_link, call_t, call) {
 #ifdef __32_BITS__
 		printf("%10p ", call);
Index: kernel/generic/src/ipc/ipcrsc.c
===================================================================
--- kernel/generic/src/ipc/ipcrsc.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/ipc/ipcrsc.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -151,6 +151,5 @@
 	irq_spinlock_lock(&TASK->answerbox.lock, true);
 	
-	list_foreach(TASK->answerbox.dispatched_calls, lst) {
-		call_t *call = list_get_instance(lst, call_t, ab_link);
+	list_foreach(TASK->answerbox.dispatched_calls, ab_link, call_t, call) {
 		if ((sysarg_t) call == callid) {
 			result = call;
Index: kernel/generic/src/lib/ra.c
===================================================================
--- kernel/generic/src/lib/ra.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/lib/ra.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -391,7 +391,5 @@
 
 	irq_spinlock_lock(&arena->lock, true);
-	list_foreach(arena->spans, cur) {
-		ra_span_t *span = list_get_instance(cur, ra_span_t, span_link);
-
+	list_foreach(arena->spans, span_link, ra_span_t, span) {
 		base = ra_span_alloc(span, size, alignment);
 		if (base)
@@ -407,7 +405,5 @@
 {
 	irq_spinlock_lock(&arena->lock, true);
-	list_foreach(arena->spans, cur) {
-		ra_span_t *span = list_get_instance(cur, ra_span_t, span_link);
-
+	list_foreach(arena->spans, span_link, ra_span_t, span) {
 		if (iswithin(span->base, span->size, base, size)) {
 			ra_span_free(span, base, size);
Index: kernel/generic/src/mm/as.c
===================================================================
--- kernel/generic/src/mm/as.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/mm/as.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -488,7 +488,5 @@
 	
 	/* Eventually check the addresses behind each area */
-	list_foreach(as->as_area_btree.leaf_list, cur) {
-		btree_node_t *node =
-		    list_get_instance(cur, btree_node_t, leaf_link);
+	list_foreach(as->as_area_btree.leaf_list, leaf_link, btree_node_t, node) {
 		
 		for (btree_key_t i = 0; i < node->keys; i++) {
@@ -904,11 +902,10 @@
 		 * reference from all frames found there.
 		 */
-		list_foreach(sh_info->pagemap.leaf_list, cur) {
-			btree_node_t *node
-			    = list_get_instance(cur, btree_node_t, leaf_link);
+		list_foreach(sh_info->pagemap.leaf_list, leaf_link,
+		    btree_node_t, node) {
 			btree_key_t i;
 			
 			for (i = 0; i < node->keys; i++)
-				frame_free((uintptr_t) node->value[i]);
+				frame_free((uintptr_t) node->value[i], 1);
 		}
 		
@@ -956,9 +953,8 @@
 	 * Visit only the pages mapped by used_space B+tree.
 	 */
-	list_foreach(area->used_space.leaf_list, cur) {
-		btree_node_t *node;
+	list_foreach(area->used_space.leaf_list, leaf_link, btree_node_t,
+	    node) {
 		btree_key_t i;
 		
-		node = list_get_instance(cur, btree_node_t, leaf_link);
 		for (i = 0; i < node->keys; i++) {
 			uintptr_t ptr = node->key[i];
@@ -1238,7 +1234,6 @@
 	size_t used_pages = 0;
 	
-	list_foreach(area->used_space.leaf_list, cur) {
-		btree_node_t *node
-		    = list_get_instance(cur, btree_node_t, leaf_link);
+	list_foreach(area->used_space.leaf_list, leaf_link, btree_node_t,
+	    node) {
 		btree_key_t i;
 		
@@ -1264,7 +1259,6 @@
 	size_t frame_idx = 0;
 	
-	list_foreach(area->used_space.leaf_list, cur) {
-		btree_node_t *node = list_get_instance(cur, btree_node_t,
-		    leaf_link);
+	list_foreach(area->used_space.leaf_list, leaf_link, btree_node_t,
+	    node) {
 		btree_key_t i;
 		
@@ -1316,7 +1310,6 @@
 	frame_idx = 0;
 	
-	list_foreach(area->used_space.leaf_list, cur) {
-		btree_node_t *node
-		    = list_get_instance(cur, btree_node_t, leaf_link);
+	list_foreach(area->used_space.leaf_list, leaf_link, btree_node_t,
+	    node) {
 		btree_key_t i;
 		
@@ -2182,7 +2175,6 @@
 	size_t area_cnt = 0;
 	
-	list_foreach(as->as_area_btree.leaf_list, cur) {
-		btree_node_t *node =
-		    list_get_instance(cur, btree_node_t, leaf_link);
+	list_foreach(as->as_area_btree.leaf_list, leaf_link, btree_node_t,
+	    node) {
 		area_cnt += node->keys;
 	}
@@ -2195,7 +2187,6 @@
 	size_t area_idx = 0;
 	
-	list_foreach(as->as_area_btree.leaf_list, cur) {
-		btree_node_t *node =
-		    list_get_instance(cur, btree_node_t, leaf_link);
+	list_foreach(as->as_area_btree.leaf_list, leaf_link, btree_node_t,
+	    node) {
 		btree_key_t i;
 		
@@ -2231,7 +2222,6 @@
 	
 	/* Print out info about address space areas */
-	list_foreach(as->as_area_btree.leaf_list, cur) {
-		btree_node_t *node
-		    = list_get_instance(cur, btree_node_t, leaf_link);
+	list_foreach(as->as_area_btree.leaf_list, leaf_link, btree_node_t,
+	    node) {
 		btree_key_t i;
 		
Index: kernel/generic/src/mm/backend_anon.c
===================================================================
--- kernel/generic/src/mm/backend_anon.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/mm/backend_anon.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -118,9 +118,8 @@
 	 */
 	mutex_lock(&area->sh_info->lock);
-	list_foreach(area->used_space.leaf_list, cur) {
-		btree_node_t *node;
+	list_foreach(area->used_space.leaf_list, leaf_link, btree_node_t,
+	    node) {
 		unsigned int i;
 		
-		node = list_get_instance(cur, btree_node_t, leaf_link);
 		for (i = 0; i < node->keys; i++) {
 			uintptr_t base = node->key[i];
@@ -295,5 +294,5 @@
 		 * the normal unreserving frame_free().
 		 */
-		frame_free(frame);
+		frame_free(frame, 1);
 	} else {
 		/*
@@ -302,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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/mm/backend_elf.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ 	(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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/mm/frame.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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,209 +226,134 @@
 }
 
-/** @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,
+	    FRAME_LOWPRIO, constraint, NULL));
+}
+
+/** Find a zone that can allocate specified number of frames
+ *
+ * This function searches among all zones. Assume interrupts are
+ * disabled and zones lock is locked.
+ *
+ * @param count      Number of free frames we are trying to find.
+ * @param flags      Required flags of the zone.
+ * @param constraint Indication of bits that cannot be set in the
+ *                   physical frame number of the first allocated frame.
+ * @param hint       Preferred zone.
+ *
+ * @return Zone that can allocate specified number of frames.
+ * @return -1 if no zone can satisfy the request.
+ *
+ */
+NO_TRACE static size_t find_free_zone_all(size_t count, zone_flags_t flags,
+    pfn_t constraint, size_t hint)
+{
+	for (size_t pos = 0; pos < zones.count; pos++) {
+		size_t i = (pos + hint) % zones.count;
+		
+		/* Check whether the zone meets the search criteria. */
+		if (!ZONE_FLAGS_MATCH(zones.info[i].flags, flags))
+			continue;
+		
+		/* Check if the zone can satisfy the allocation request. */
+		if (zone_can_alloc(&zones.info[i], count, constraint))
+			return i;
+	}
+	
+	return (size_t) -1;
+}
+
+/** Check if frame range  priority memory
+ *
+ * @param pfn   Starting frame.
+ * @param count Number of frames.
+ *
+ * @return True if the range contains only priority memory.
+ *
+ */
+NO_TRACE static bool is_high_priority(pfn_t base, size_t count)
+{
+	return (base + count <= FRAME_LOWPRIO);
+}
+
+/** Find a zone that can allocate specified number of frames
+ *
+ * This function ignores zones that contain only high-priority
+ * memory. Assume interrupts are disabled and zones lock is locked.
+ *
+ * @param count      Number of free frames we are trying to find.
+ * @param flags      Required flags of the zone.
+ * @param constraint Indication of bits that cannot be set in the
+ *                   physical frame number of the first allocated frame.
+ * @param hint       Preferred zone.
+ *
+ * @return Zone that can allocate specified number of frames.
+ * @return -1 if no low-priority zone can satisfy the request.
+ *
+ */
+NO_TRACE static size_t find_free_zone_lowprio(size_t count, zone_flags_t flags,
+    pfn_t constraint, size_t hint)
+{	
+	for (size_t pos = 0; pos < zones.count; pos++) {
+		size_t i = (pos + hint) % zones.count;
+		
+		/* Skip zones containing only high-priority memory. */
+		if (is_high_priority(zones.info[i].base, zones.info[i].count))
+			continue;
+		
+		/* Check whether the zone meets the search criteria. */
+		if (!ZONE_FLAGS_MATCH(zones.info[i].flags, flags))
+			continue;
+		
+		/* Check if the zone can satisfy the allocation request. */
+		if (zone_can_alloc(&zones.info[i], count, constraint))
+			return i;
+	}
+	
+	return (size_t) -1;
+}
+
+/** 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 hint       Preferred zone.
+ *
+ * @return Zone that can allocate specified number of frames.
+ * @return -1 if no zone can satisfy the request.
+ *
+ */
+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)
 		hint = 0;
 	
-	size_t i = hint;
-	do {
-		/*
-		 * Check whether the zone meets the search criteria.
-		 */
-		if (ZONE_FLAGS_MATCH(zones.info[i].flags, flags)) {
-			/*
-			 * Check if the zone has 2^order frames area available.
-			 */
-			if (zone_can_alloc(&zones.info[i], order))
-				return i;
-		}
-		
-		i++;
-		if (i >= zones.count)
-			i = 0;
-		
-	} while (i != hint);
-	
-	return (size_t) -1;
-}
-
-/**************************/
-/* 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
-};
+	/*
+	 * Prefer zones with low-priority memory over
+	 * zones with high-priority memory.
+	 */
+	
+	size_t znum = find_free_zone_lowprio(count, flags, constraint, hint);
+	if (znum != (size_t) -1)
+		return znum;
+	
+	/* Take all zones into account */
+	return find_free_zone_all(count, flags, constraint, hint);
+}
 
 /******************/
@@ -447,4 +361,12 @@
 /******************/
 
+/** 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 +374,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,
+	    FRAME_LOWPRIO, 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,52 +413,43 @@
  * 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)
 {
 	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);
@@ -536,16 +458,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);
@@ -562,69 +483,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];
 	}
 }
@@ -649,55 +527,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);
 }
 
@@ -719,5 +553,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
@@ -733,16 +568,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;
@@ -752,12 +583,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 */
@@ -772,11 +598,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--;
@@ -797,7 +618,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++;
 	}
@@ -806,15 +628,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;
@@ -823,31 +645,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;
+	}
 }
 
@@ -861,5 +679,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));
 }
 
@@ -867,9 +685,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));
+	size_t frames = SIZE2FRAMES(zone_conf_size(count));
+	
+	return ADDR2PFN((uintptr_t)
+	    frame_alloc(frames, FRAME_LOWMEM | FRAME_ATOMIC, 0));
 }
 
@@ -879,5 +696,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
@@ -896,17 +713,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++) {
@@ -921,6 +741,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,
@@ -929,4 +748,6 @@
 						break;
 					}
+				}
+				
 				if (overlap)
 					continue;
@@ -945,11 +766,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);
@@ -967,5 +787,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);
@@ -1009,24 +830,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.
  *
  */
-void *frame_alloc_generic(uint8_t order, frame_flags_t flags, size_t *pzone)
-{
-	size_t size = ((size_t) 1) << order;
+uintptr_t frame_alloc_generic(size_t count, frame_flags_t flags,
+    uintptr_t constraint, size_t *pzone)
+{
+	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);
@@ -1035,9 +861,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);
@@ -1046,6 +874,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) {
@@ -1055,6 +883,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);
 		}
 	}
@@ -1063,7 +891,9 @@
 		if (flags & FRAME_ATOMIC) {
 			irq_spinlock_unlock(&zones.lock, true);
+			
 			if (!(flags & FRAME_NO_RESERVE))
-				reserve_free(size);
-			return NULL;
+				reserve_free(count);
+			
+			return 0;
 		}
 		
@@ -1075,5 +905,6 @@
 		
 		if (!THREAD)
-			panic("Cannot wait for memory to become available.");
+			panic("Cannot wait for %zu frames to become available "
+			    "(%zu available).", count, avail);
 		
 		/*
@@ -1082,11 +913,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();
@@ -1094,7 +925,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;
 		
@@ -1112,6 +944,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);
@@ -1120,45 +952,41 @@
 		*pzone = znum;
 	
-	if (flags & FRAME_KA)
-		return (void *) PA2KA(PFN2ADDR(pfn));
-	
-	return (void *) PFN2ADDR(pfn);
-}
-
-void *frame_alloc(uint8_t order, frame_flags_t flags)
-{
-	return frame_alloc_generic(order, flags, NULL);
-}
-
-void *frame_alloc_noreserve(uint8_t order, frame_flags_t flags)
-{
-	return frame_alloc_generic(order, flags | FRAME_NO_RESERVE, 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.
+	return PFN2ADDR(pfn);
+}
+
+uintptr_t frame_alloc(size_t count, frame_flags_t flags, uintptr_t constraint)
+{
+	return frame_alloc_generic(count, flags, constraint, NULL);
+}
+
+/** 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);
@@ -1166,15 +994,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) {
@@ -1182,19 +1009,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);
 }
 
@@ -1230,7 +1058,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;
@@ -1257,4 +1085,5 @@
 	/* Tell the architecture to create some memory */
 	frame_low_arch_init();
+	
 	if (config.cpu_active == 1) {
 		frame_mark_unavailable(ADDR2PFN(KA2PA(config.base)),
@@ -1263,10 +1092,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)
@@ -1274,9 +1100,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();
 }
@@ -1284,21 +1112,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;
@@ -1306,4 +1137,5 @@
 		if (*basep + *sizep <= limit)
 			return false;
+		
 		if (*basep <= limit) {
 			*sizep -= limit - *basep;
@@ -1311,4 +1143,5 @@
 		}
 	}
+	
 	return true;
 }
@@ -1322,6 +1155,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);
 	
@@ -1346,6 +1179,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);
 		
@@ -1375,5 +1207,5 @@
 	/*
 	 * Because printing may require allocation of memory, we may not hold
-	 * the frame allocator locks when printing zone statistics.  Therefore,
+	 * the frame allocator locks when printing zone statistics. Therefore,
 	 * we simply gather the statistics under the protection of the locks and
 	 * print the statistics when the locks have been released.
@@ -1384,6 +1216,9 @@
 	 */
 	
-	size_t i;
-	for (i = 0;; i++) {
+	size_t free_lowmem = 0;
+	size_t free_highmem = 0;
+	size_t free_highprio = 0;
+	
+	for (size_t i = 0;; i++) {
 		irq_spinlock_lock(&zones.lock, true);
 		
@@ -1393,5 +1228,6 @@
 		}
 		
-		uintptr_t base = PFN2ADDR(zones.info[i].base);
+		pfn_t fbase = zones.info[i].base;
+		uintptr_t base = PFN2ADDR(fbase);
 		size_t count = zones.info[i].count;
 		zone_flags_t flags = zones.info[i].flags;
@@ -1399,7 +1235,36 @@
 		size_t busy_count = zones.info[i].busy_count;
 		
+		bool available = ((flags & ZONE_AVAILABLE) != 0);
+		bool lowmem = ((flags & ZONE_LOWMEM) != 0);
+		bool highmem = ((flags & ZONE_HIGHMEM) != 0);
+		bool highprio = is_high_priority(fbase, count);
+		
+		if (available) {
+			if (lowmem)
+				free_lowmem += free_count;
+			
+			if (highmem)
+				free_highmem += free_count;
+			
+			if (highprio) {
+				free_highprio += free_count;
+			} else {
+				/*
+				 * Walk all frames of the zone and examine
+				 * all high priority memory to get accurate
+				 * statistics.
+				 */
+				
+				for (size_t index = 0; index < count; index++) {
+					if (is_high_priority(fbase + index, 0)) {
+						if (!bitmap_get(&zones.info[i].bitmap, index))
+							free_highprio++;
+					} else
+						break;
+				}
+			}
+		}
+		
 		irq_spinlock_unlock(&zones.lock, true);
-		
-		bool available = ((flags & ZONE_AVAILABLE) != 0);
 		
 		printf("%-4zu", i);
@@ -1426,4 +1291,24 @@
 		printf("\n");
 	}
+	
+	printf("\n");
+	
+	uint64_t size;
+	const char *size_suffix;
+	
+	bin_order_suffix(FRAMES2SIZE(free_lowmem), &size, &size_suffix,
+	    false);
+	printf("Available low memory:    %zu frames (%" PRIu64 " %s)\n",
+	    free_lowmem, size, size_suffix);
+	
+	bin_order_suffix(FRAMES2SIZE(free_highmem), &size, &size_suffix,
+	    false);
+	printf("Available high memory:   %zu frames (%" PRIu64 " %s)\n",
+	    free_highmem, size, size_suffix);
+	
+	bin_order_suffix(FRAMES2SIZE(free_highprio), &size, &size_suffix,
+	    false);
+	printf("Available high priority: %zu frames (%" PRIu64 " %s)\n",
+	    free_highprio, size, size_suffix);
 }
 
@@ -1438,6 +1323,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;
@@ -1452,23 +1336,58 @@
 	}
 	
-	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;
+	size_t free_lowmem = 0;
+	size_t free_highmem = 0;
+	size_t free_highprio = 0;
+	
+	pfn_t fbase = zones.info[znum].base;
+	uintptr_t base = PFN2ADDR(fbase);
+	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;
+	
+	bool available = ((flags & ZONE_AVAILABLE) != 0);
+	bool lowmem = ((flags & ZONE_LOWMEM) != 0);
+	bool highmem = ((flags & ZONE_HIGHMEM) != 0);
+	bool highprio = is_high_priority(fbase, count);
+	
+	if (available) {
+		if (lowmem)
+			free_lowmem = free_count;
+		
+		if (highmem)
+			free_highmem = free_count;
+		
+		if (highprio) {
+			free_highprio = free_count;
+		} else {
+			/*
+			 * Walk all frames of the zone and examine
+			 * all high priority memory to get accurate
+			 * statistics.
+			 */
+			
+			for (size_t index = 0; index < count; index++) {
+				if (is_high_priority(fbase + index, 0)) {
+					if (!bitmap_get(&zones.info[znum].bitmap, index))
+						free_highprio++;
+				} else
+					break;
+			}
+		}
+	}
 	
 	irq_spinlock_unlock(&zones.lock, true);
-	
-	bool available = ((flags & ZONE_AVAILABLE) != 0);
 	
 	uint64_t size;
 	const char *size_suffix;
+	
 	bin_order_suffix(FRAMES2SIZE(count), &size, &size_suffix, false);
 	
-	printf("Zone number:       %zu\n", znum);
-	printf("Zone base address: %p\n", (void *) base);
-	printf("Zone size:         %zu frames (%" PRIu64 " %s)\n", count,
+	printf("Zone number:             %zu\n", znum);
+	printf("Zone base address:       %p\n", (void *) base);
+	printf("Zone size:               %zu frames (%" PRIu64 " %s)\n", count,
 	    size, size_suffix);
-	printf("Zone flags:        %c%c%c%c%c\n",
+	printf("Zone flags:              %c%c%c%c%c\n",
 	    available ? 'A' : '-',
 	    (flags & ZONE_RESERVED) ? 'R' : '-',
@@ -1480,10 +1399,26 @@
 		bin_order_suffix(FRAMES2SIZE(busy_count), &size, &size_suffix,
 		    false);
-		printf("Allocated space:   %zu frames (%" PRIu64 " %s)\n",
+		printf("Allocated space:         %zu frames (%" PRIu64 " %s)\n",
 		    busy_count, size, size_suffix);
+		
 		bin_order_suffix(FRAMES2SIZE(free_count), &size, &size_suffix,
 		    false);
-		printf("Available space:   %zu frames (%" PRIu64 " %s)\n",
+		printf("Available space:         %zu frames (%" PRIu64 " %s)\n",
 		    free_count, size, size_suffix);
+		
+		bin_order_suffix(FRAMES2SIZE(free_lowmem), &size, &size_suffix,
+		    false);
+		printf("Available low memory:    %zu frames (%" PRIu64 " %s)\n",
+		    free_lowmem, size, size_suffix);
+		
+		bin_order_suffix(FRAMES2SIZE(free_highmem), &size, &size_suffix,
+		    false);
+		printf("Available high memory:   %zu frames (%" PRIu64 " %s)\n",
+		    free_highmem, size, size_suffix);
+		
+		bin_order_suffix(FRAMES2SIZE(free_highprio), &size, &size_suffix,
+		    false);
+		printf("Available high priority: %zu frames (%" PRIu64 " %s)\n",
+		    free_highprio, size, size_suffix);
 	}
 }
Index: kernel/generic/src/mm/km.c
===================================================================
--- kernel/generic/src/mm/km.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/mm/km.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -239,30 +239,30 @@
 uintptr_t km_temporary_page_get(uintptr_t *framep, frame_flags_t flags)
 {
-	uintptr_t frame;
-	uintptr_t page;
-
 	ASSERT(THREAD);
 	ASSERT(framep);
 	ASSERT(!(flags & ~(FRAME_NO_RESERVE | FRAME_ATOMIC)));
-
+	
 	/*
 	 * Allocate a frame, preferably from high memory.
 	 */
-	frame = (uintptr_t) frame_alloc(ONE_FRAME,
-	    FRAME_HIGHMEM | FRAME_ATOMIC | flags); 
+	uintptr_t page;
+	uintptr_t frame =
+	    frame_alloc(1, FRAME_HIGHMEM | FRAME_ATOMIC | flags, 0);
 	if (frame) {
 		page = km_map(frame, PAGE_SIZE,
 		    PAGE_READ | PAGE_WRITE | PAGE_CACHEABLE);
-		ASSERT(page);	// FIXME
+		
+		// FIXME
+		ASSERT(page);
 	} else {
-		frame = (uintptr_t) frame_alloc(ONE_FRAME,
-		    FRAME_LOWMEM | flags);
+		frame = frame_alloc(1, FRAME_LOWMEM | flags, 0);
 		if (!frame)
 			return (uintptr_t) NULL;
+		
 		page = PA2KA(frame);
 	}
-
+	
 	*framep = frame;
-	return page;	
+	return page;
 }
 
Index: kernel/generic/src/mm/page.c
===================================================================
--- kernel/generic/src/mm/page.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/mm/page.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -169,5 +169,5 @@
 }
 
-int page_find_mapping(uintptr_t virt, void **phys)
+int page_find_mapping(uintptr_t virt, uintptr_t *phys)
 {
 	page_table_lock(AS, true);
@@ -179,5 +179,5 @@
 	}
 	
-	*phys = (void *) PTE_GET_FRAME(pte) +
+	*phys = PTE_GET_FRAME(pte) +
 	    (virt - ALIGN_DOWN(virt, PAGE_SIZE));
 	
@@ -193,7 +193,7 @@
  *
  */
-sysarg_t sys_page_find_mapping(uintptr_t virt, void *phys_ptr)
-{
-	void *phys;
+sysarg_t sys_page_find_mapping(uintptr_t virt, uintptr_t *phys_ptr)
+{
+	uintptr_t phys;
 	int rc = page_find_mapping(virt, &phys);
 	if (rc != EOK)
Index: kernel/generic/src/mm/slab.c
===================================================================
--- kernel/generic/src/mm/slab.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/mm/slab.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -182,8 +182,10 @@
 	size_t zone = 0;
 	
-	void *data = frame_alloc_generic(cache->order, FRAME_KA | flags, &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);
@@ -810,6 +806,5 @@
 	
 	size_t frames = 0;
-	list_foreach(slab_cache_list, cur) {
-		slab_cache_t *cache = list_get_instance(cur, slab_cache_t, link);
+	list_foreach(slab_cache_list, link, slab_cache_t, cache) {
 		frames += _slab_reclaim(cache, flags);
 	}
@@ -871,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;
@@ -881,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");
@@ -936,6 +931,5 @@
 	irq_spinlock_lock(&slab_cache_lock, false);
 	
-	list_foreach(slab_cache_list, cur) {
-		slab_cache_t *slab = list_get_instance(cur, slab_cache_t, link);
+	list_foreach(slab_cache_list, link, slab_cache_t, slab) {
 		if ((slab->flags & SLAB_CACHE_MAGDEFERRED) !=
 		    SLAB_CACHE_MAGDEFERRED)
Index: kernel/generic/src/proc/scheduler.c
===================================================================
--- kernel/generic/src/proc/scheduler.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/proc/scheduler.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -739,7 +739,6 @@
 			
 			printf("\trq[%u]: ", i);
-			list_foreach(cpus[cpu].rq[i].rq, cur) {
-				thread_t *thread = list_get_instance(cur,
-				    thread_t, rq_link);
+			list_foreach(cpus[cpu].rq[i].rq, rq_link, thread_t,
+			    thread) {
 				printf("%" PRIu64 "(%s) ", thread->tid,
 				    thread_states[thread->state]);
Index: kernel/generic/src/proc/task.c
===================================================================
--- kernel/generic/src/proc/task.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/proc/task.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -452,7 +452,5 @@
 	
 	/* Current values of threads */
-	list_foreach(task->threads, cur) {
-		thread_t *thread = list_get_instance(cur, thread_t, th_link);
-		
+	list_foreach(task->threads, th_link, thread_t, thread) {
 		irq_spinlock_lock(&thread->lock, false);
 		
@@ -484,6 +482,5 @@
 	 */
 	
-	list_foreach(task->threads, cur) {
-		thread_t *thread = list_get_instance(cur, thread_t, th_link);
+	list_foreach(task->threads, th_link, thread_t, thread) {
 		bool sleeping = false;
 		
Index: kernel/generic/src/proc/thread.c
===================================================================
--- kernel/generic/src/proc/thread.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/proc/thread.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -192,6 +192,7 @@
 	kmflags &= ~FRAME_HIGHMEM;
 	
-	thread->kstack = (uint8_t *) frame_alloc(STACK_FRAMES, FRAME_KA | kmflags);
-	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)
@@ -201,4 +202,6 @@
 	}
 	
+	thread->kstack = (uint8_t *) PA2KA(stack_phys);
+	
 #ifdef CONFIG_UDEBUG
 	mutex_initialize(&thread->udebug.lock, MUTEX_PASSIVE);
@@ -216,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/synch/futex.c
===================================================================
--- kernel/generic/src/synch/futex.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/synch/futex.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -274,9 +274,7 @@
 	mutex_lock(&TASK->futexes_lock);
 
-	list_foreach(TASK->futexes.leaf_list, cur) {
-		btree_node_t *node;
+	list_foreach(TASK->futexes.leaf_list, leaf_link, btree_node_t, node) {
 		unsigned int i;
 		
-		node = list_get_instance(cur, btree_node_t, leaf_link);
 		for (i = 0; i < node->keys; i++) {
 			futex_t *ftx;
Index: kernel/generic/src/sysinfo/stats.c
===================================================================
--- kernel/generic/src/sysinfo/stats.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/sysinfo/stats.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -175,8 +175,6 @@
 	
 	/* Walk the B+ tree and count pages */
-	list_foreach(as->as_area_btree.leaf_list, cur) {
-		btree_node_t *node =
-		    list_get_instance(cur, btree_node_t, leaf_link);
-		
+	list_foreach(as->as_area_btree.leaf_list, leaf_link, btree_node_t,
+	    node) {
 		unsigned int i;
 		for (i = 0; i < node->keys; i++) {
@@ -218,8 +216,5 @@
 	
 	/* Walk the B+ tree and count pages */
-	list_foreach(as->as_area_btree.leaf_list, cur) {
-		btree_node_t *node =
-		    list_get_instance(cur, btree_node_t, leaf_link);
-		
+	list_foreach(as->as_area_btree.leaf_list, leaf_link, btree_node_t, node) {
 		unsigned int i;
 		for (i = 0; i < node->keys; i++) {
Index: kernel/generic/src/time/clock.c
===================================================================
--- kernel/generic/src/time/clock.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/time/clock.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -81,6 +81,6 @@
 void clock_counter_init(void)
 {
-	void *faddr = frame_alloc(ONE_FRAME, FRAME_ATOMIC);
-	if (!faddr)
+	uintptr_t faddr = frame_alloc(1, FRAME_ATOMIC, 0);
+	if (faddr == 0)
 		panic("Cannot allocate page for clock.");
 	
@@ -91,5 +91,5 @@
 	uptime->useconds = 0;
 	
-	clock_parea.pbase = (uintptr_t) faddr;
+	clock_parea.pbase = faddr;
 	clock_parea.frames = 1;
 	clock_parea.unpriv = true;
Index: kernel/generic/src/udebug/udebug.c
===================================================================
--- kernel/generic/src/udebug/udebug.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/udebug/udebug.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -406,7 +406,5 @@
 	
 	/* Finish debugging of all userspace threads */
-	list_foreach(task->threads, cur) {
-		thread_t *thread = list_get_instance(cur, thread_t, th_link);
-		
+	list_foreach(task->threads, th_link, thread_t, thread) {
 		mutex_lock(&thread->udebug.lock);
 		
Index: kernel/generic/src/udebug/udebug_ops.c
===================================================================
--- kernel/generic/src/udebug/udebug_ops.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/generic/src/udebug/udebug_ops.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -196,7 +196,5 @@
 	/* Set udebug.active on all of the task's userspace threads. */
 	
-	list_foreach(TASK->threads, cur) {
-		thread_t *thread = list_get_instance(cur, thread_t, th_link);
-		
+	list_foreach(TASK->threads, th_link, thread_t, thread) {
 		mutex_lock(&thread->udebug.lock);
 		if (thread->uspace) {
@@ -389,7 +387,5 @@
 	
 	/* FIXME: make sure the thread isn't past debug shutdown... */
-	list_foreach(TASK->threads, cur) {
-		thread_t *thread = list_get_instance(cur, thread_t, th_link);
-		
+	list_foreach(TASK->threads, th_link, thread_t, thread) {
 		irq_spinlock_lock(&thread->lock, false);
 		bool uspace = thread->uspace;
Index: kernel/test/mm/falloc1.c
===================================================================
--- kernel/test/mm/falloc1.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/test/mm/falloc1.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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] = (uintptr_t)
-				    frame_alloc(order, FRAME_ATOMIC | FRAME_KA);
-				
-				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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/test/mm/falloc2.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -40,6 +40,5 @@
 #include <arch.h>
 
-#define MAX_FRAMES  256U
-#define MAX_ORDER   8
+#define MAX_FRAMES  256
 
 #define THREAD_RUNS  1
@@ -53,6 +52,6 @@
 	uint8_t val = THREAD->tid % THREADS;
 	
-	void **frames = (void **)
-	    malloc(MAX_FRAMES * sizeof(void *), FRAME_ATOMIC);
+	uintptr_t *frames = (uintptr_t *)
+	    malloc(MAX_FRAMES * sizeof(uintptr_t), FRAME_ATOMIC);
 	if (frames == NULL) {
 		TPRINTF("Thread #%" PRIu64 " (cpu%u): "
@@ -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] =
-				    frame_alloc(order, FRAME_ATOMIC | FRAME_KA);
+			for (unsigned int i = 0; i < (MAX_FRAMES / count); i++) {
+				frames[allocated] = frame_alloc(count, FRAME_ATOMIC, 0);
 				if (frames[allocated]) {
-					memsetb(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 %p offset %zu\n",
-						    THREAD->tid, CPU->id, ((char *) frames[i])[k],
+						    "Unexpected data (%c) in block %zu offset %zu\n",
+						    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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ kernel/test/mm/mapping1.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -41,55 +41,51 @@
 const char *test_mapping1(void)
 {
-	uintptr_t page0, page1;
-	uintptr_t frame;
-	uint32_t v;
-	int i;
+	uintptr_t frame = frame_alloc(1, FRAME_NONE, 0);
 	
-	frame = (uintptr_t) frame_alloc(ONE_FRAME, FRAME_NONE);
-
-	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",
 	    (void *) page1, (void *) frame);
 	
-	for (i = 0; i < 2; i++) {
+	for (unsigned int i = 0; i < 2; i++) {
 		TPRINTF("Writing magic using the first virtual address.\n");
-
+		
 		*((uint32_t *) page0) = TEST_MAGIC;
-
+		
 		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.";
 		}
-
+		
 		TPRINTF("Writing zero using the second virtual address.\n");
-	
+		
 		*((uint32_t *) page1) = 0;
-
+		
 		TPRINTF("Reading zero using the first virtual address.\n");
-	
+		
 		v = *((uint32_t *) page0);
-	
+		
 		if (v != 0) {
 			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.";
 		}
 	}
-
+	
 	km_unmap(page0, PAGE_SIZE);
 	km_unmap(page1, PAGE_SIZE);
-	frame_free(frame);
+	frame_free(frame, 1);
 	
 	return NULL;
Index: tools/config.py
===================================================================
--- tools/config.py	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ tools/config.py	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -363,6 +363,7 @@
 def create_output(mkname, mcname, config, rules):
 	"Create output configuration"
-	
-	timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
+
+	timestamp_unix = int(time.time())
+	timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp_unix))
 	
 	sys.stderr.write("Fetching current revision identifier ... ")
@@ -423,4 +424,8 @@
 		outmc.write('#define REVISION %s\n' % revision)
 		defs += ' "-DREVISION=%s"' % revision
+	
+	outmk.write('TIMESTAMP_UNIX = %d\n' % timestamp_unix)
+	outmc.write('#define TIMESTAMP_UNIX %d\n' % timestamp_unix)
+	defs += ' "-DTIMESTAMP_UNIX=%d"\n' % timestamp_unix
 	
 	outmk.write('TIMESTAMP = %s\n' % timestamp)
Index: tools/toolchain.sh
===================================================================
--- tools/toolchain.sh	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ tools/toolchain.sh	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -36,5 +36,5 @@
 	GCC_GMP_VERSION_NUM(__GNU_MP_VERSION, __GNU_MP_VERSION_MINOR, __GNU_MP_VERSION_PATCHLEVEL)
 
-#if GCC_GMP_VERSION < GCC_GMP_VERSION_NUM(4,3,2)
+#if GCC_GMP_VERSION < GCC_GMP_VERSION_NUM(4, 3, 2)
 	choke me
 #endif
@@ -58,5 +58,5 @@
 GCC_VERSION="4.8.1"
 GCC_PATCHES="toolchain-gcc-4.8.1-targets.patch toolchain-gcc-4.8.1-headers.patch"
-GDB_VERSION="7.6"
+GDB_VERSION="7.6.1"
 GDB_PATCHES="toolchain-gdb-7.6.patch"
 
@@ -273,4 +273,31 @@
 }
 
+check_dirs() {
+	OUTSIDE="$1"
+	BASE="$2"
+	ORIGINAL="`pwd`"
+	
+	cd "${OUTSIDE}"
+	check_error $? "Unable to change directory to ${OUTSIDE}."
+	ABS_OUTSIDE="`pwd`"
+	
+	cd "${BASE}"
+	check_error $? "Unable to change directory to ${BASE}."
+	ABS_BASE="`pwd`"
+	
+	cd "${ORIGINAL}"
+	check_error $? "Unable to change directory to ${ORIGINAL}."
+	
+	BASE_LEN="${#ABS_BASE}"
+	OUTSIDE_TRIM="${ABS_OUTSIDE:0:${BASE_LEN}}"
+	
+	if [ "${OUTSIDE_TRIM}" == "${ABS_BASE}" ] ; then
+		echo
+		echo "CROSS_PREFIX cannot reside within the working directory."
+		
+		exit 5
+	fi
+}
+
 unpack_tarball() {
 	FILE="$1"
@@ -307,5 +334,5 @@
 	download_fetch "${BINUTILS_SOURCE}" "${BINUTILS}" "33adb18c3048d057ac58d07a3f1adb38"
 	download_fetch "${GCC_SOURCE}" "${GCC}" "3b2386c114cd74185aa3754b58a79304"
-	download_fetch "${GDB_SOURCE}" "${GDB}" "fda57170e4d11cdde74259ca575412a8"
+	download_fetch "${GDB_SOURCE}" "${GDB}" "fbc4dab4181e6e9937075b43a4ce2732"
 }
 
@@ -398,4 +425,6 @@
 	$REAL_INSTALL && create_dir "${PREFIX}" "destination directory"
 	create_dir "${OBJDIR}" "GCC object directory"
+	
+	check_dirs "${PREFIX}" "${WORKDIR}"
 	
 	echo ">>> Unpacking tarballs"
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,7 +38,9 @@
 	app/blkdump \
 	app/bnchmark \
+	app/corecfg \
 	app/devctl \
 	app/dnscfg \
 	app/dnsres \
+	app/download \
 	app/edit \
 	app/getterm \
@@ -50,4 +52,5 @@
 	app/loc \
 	app/logset \
+	app/mixerctl \
 	app/mkfat \
 	app/mkexfat \
@@ -68,16 +71,20 @@
 	app/vuhid \
 	app/netecho \
+	app/netspeed \
 	app/nettest1 \
 	app/nettest2 \
 	app/nettest3 \
 	app/ping \
-	app/ping6 \
 	app/sysinfo \
 	app/mkbd \
 	app/date \
-	app/websrv \
 	app/vdemo \
+	app/viewer \
 	app/vlaunch \
 	app/vterm \
+	app/df \
+	app/wavplay \
+	app/websrv \
+	srv/audio/hound \
 	srv/clipboard \
 	srv/locsrv \
@@ -85,8 +92,10 @@
 	srv/devman \
 	srv/loader \
+	srv/net/dhcp \
 	srv/net/dnsrsrv \
 	srv/net/ethip \
 	srv/net/inetsrv \
 	srv/net/loopip \
+	srv/net/nconfsrv \
 	srv/net/slip \
 	srv/net/tcp \
@@ -116,4 +125,6 @@
 	srv/hid/remcons \
 	srv/hw/char/s3c24xx_uart \
+	srv/hid/rfb \
+	drv/audio/sb16 \
 	drv/infrastructure/root \
 	drv/infrastructure/rootvirt \
@@ -184,4 +195,11 @@
 endif
 
+ifeq ($(UARCH), $(filter $(UARCH),mips32 mips32eb))
+	DIRS += \
+		drv/infrastructure/rootmalta \
+		drv/bus/pci/pciintel \
+		drv/bus/isa
+endif
+
 ifeq ($(UARCH),ppc32)
 	DIRS += \
@@ -217,4 +235,6 @@
 	lib/graph \
 	lib/gui \
+	lib/hound \
+	lib/http \
 	lib/softrend \
 	lib/draw \
@@ -222,4 +242,5 @@
 	lib/nic \
 	lib/ext4 \
+	lib/uri \
 	lib/usb \
 	lib/usbhost \
@@ -227,4 +248,5 @@
 	lib/usbhid \
 	lib/usbvirt \
+	lib/pcm \
 	lib/bithenge \
 	lib/posix
Index: uspace/Makefile.common
===================================================================
--- uspace/Makefile.common	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/Makefile.common	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -131,4 +131,6 @@
 
 LIBDRV_PREFIX = $(LIB_PREFIX)/drv
+LIBHOUND_PREFIX = $(LIB_PREFIX)/hound
+LIBPCM_PREFIX = $(LIB_PREFIX)/pcm
 LIBNET_PREFIX = $(LIB_PREFIX)/net
 LIBNIC_PREFIX = $(LIB_PREFIX)/nic
@@ -138,4 +140,7 @@
 
 LIBBITHENGE_PREFIX = $(LIB_PREFIX)/bithenge
+
+LIBHTTP_PREFIX = $(LIB_PREFIX)/http
+LIBURI_PREFIX = $(LIB_PREFIX)/uri
 
 ifeq ($(STATIC_NEEDED),y)
Index: uspace/app/bdsh/cmds/modules/mount/mount.c
===================================================================
--- uspace/app/bdsh/cmds/modules/mount/mount.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/bdsh/cmds/modules/mount/mount.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -74,8 +74,5 @@
 	get_mtab_list(&mtab_list);
 
-	list_foreach(mtab_list, cur) {
-		mtab_ent_t *mtab_ent = list_get_instance(cur, mtab_ent_t,
-		    link);
-
+	list_foreach(mtab_list, link, mtab_ent_t, mtab_ent) {
 		if (old_ent)
 			free(old_ent);
Index: uspace/app/corecfg/Makefile
===================================================================
--- uspace/app/corecfg/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/corecfg/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2013 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+BINARY = corecfg
+
+SOURCES = \
+	corecfg.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/corecfg/corecfg.c
===================================================================
--- uspace/app/corecfg/corecfg.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/corecfg/corecfg.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,91 @@
+/*
+ * 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 corecfg
+ * @{
+ */
+/** @file Core file configuration utility.
+ */
+
+#include <corecfg.h>
+#include <errno.h>
+#include <stdio.h>
+
+#define NAME "dnscfg"
+
+static void print_syntax(void)
+{
+	printf("Syntax:\n");
+	printf("\t%s [get]\n", NAME);
+	printf("\t%s enable\n", NAME);
+	printf("\t%s disable\n", NAME);
+}
+
+static int corecfg_print(void)
+{
+	bool enable;
+	int rc;
+
+	rc = corecfg_get_enable(&enable);
+	if (rc != EOK) {
+		printf("Failed getting core file setting.\n");
+		return 1;
+	}
+
+	printf("Core files: %s.\n", enable ? "enabled" : "disabled");
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	int rc;
+
+	rc = corecfg_init();
+	if (rc != EOK) {
+		printf("Failed contacting corecfg service.\n");
+		return 1;
+	}
+
+	if ((argc < 2) || (str_cmp(argv[1], "get") == 0))
+		return corecfg_print();
+	else if (str_cmp(argv[1], "enable") == 0)
+		return corecfg_set_enable(true);
+	else if (str_cmp(argv[1], "disable") == 0)
+		return corecfg_set_enable(false);
+	else {
+		printf("%s: Unknown command '%s'.\n", NAME, argv[1]);
+		print_syntax();
+		return 1;
+	}
+
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/app/devctl/devctl.c
===================================================================
--- uspace/app/devctl/devctl.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/devctl/devctl.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/df/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/df/df.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,205 @@
+/*
+ * 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) (tot ? (100ULL * (x) / (tot)) : 0)
+#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 void 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 parameters\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 void size_to_human_readable(char *buf, uint64_t bytes)
+{
+	const char *units = "BkMGTPEZY";
+	int i = 0;
+
+	while (bytes >= 1024) {
+		bytes /= 1024;
+		i++;
+	}
+
+	snprintf(buf, 6, "%4llu%c", (unsigned long long) bytes, units[i]);
+}
+
+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)
+{
+	uint64_t const used_blocks = st->f_blocks - st->f_bfree;
+	unsigned const perc_used = PERCENTAGE(used_blocks, st->f_blocks);
+
+	printf("%10s", name);
+
+	if (human_readable) {
+		char tmp[1024];
+
+		/* Print size */
+		size_to_human_readable(tmp, st->f_blocks *  st->f_bsize);
+		printf(" %14s", tmp);
+
+		/* Number of used blocks */
+		size_to_human_readable(tmp, used_blocks  *  st->f_bsize);
+		printf(" %14s", tmp);
+
+		/* Number of available blocks */
+		size_to_human_readable(tmp, st->f_bfree *  st->f_bsize);
+		printf(" %14s", tmp);
+
+		/* Percentage of used blocks */
+		printf(" %4u%%", perc_used);
+
+		/* Mount point */
+		printf(" %s\n", mountpoint);
+	} else {
+		/* Blocks / Used blocks / Available blocks / Used% / Mounted on */
+		printf(" %15llu %14llu %14llu %4u%% %s\n", 
+		    FSBK_TO_BK(st->f_blocks, st->f_bsize, unit_size),
+		    FSBK_TO_BK(used_blocks, st->f_bsize, unit_size),
+		    FSBK_TO_BK(st->f_bfree, st->f_bsize, unit_size),
+		    perc_used,
+		    mountpoint);
+	}
+
+}
+
+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/dnsres/dnsres.c
===================================================================
--- uspace/app/dnsres/dnsres.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/dnsres/dnsres.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -36,5 +36,4 @@
 #include <inet/addr.h>
 #include <inet/dnsr.h>
-#include <net/socket_codes.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -54,5 +53,5 @@
 	}
 	
-	uint16_t af;
+	uint16_t ver;
 	char *hname;
 	
@@ -63,5 +62,5 @@
 		}
 		
-		af = AF_INET;
+		ver = ip_v4;
 		hname = argv[2];
 	} else if (str_cmp(argv[1], "-6") == 0) {
@@ -71,13 +70,13 @@
 		}
 		
-		af = AF_INET6;
+		ver = ip_v6;
 		hname = argv[2];
 	} else {
-		af = 0;
+		ver = ip_any;
 		hname = argv[1];
 	}
 	
 	dnsr_hostinfo_t *hinfo;
-	int rc = dnsr_name2host(hname, &hinfo, af);
+	int rc = dnsr_name2host(hname, &hinfo, ver);
 	if (rc != EOK) {
 		printf("%s: Error resolving '%s'.\n", NAME, hname);
Index: uspace/app/download/Makefile
===================================================================
--- uspace/app/download/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/download/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2013 Martin Sucha
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+LIBS = $(LIBHTTP_PREFIX)/libhttp.a $(LIBURI_PREFIX)/liburi.a
+EXTRA_CFLAGS = -I$(LIBHTTP_PREFIX)/include -I$(LIBURI_PREFIX)
+DEFS = -DRELEASE=$(RELEASE)
+BINARY = download
+
+SOURCES = \
+	main.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/download/main.c
===================================================================
--- uspace/app/download/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/download/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup download
+ * @{
+ */
+
+/** @file
+ * Download a file from a HTTP server
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <str_error.h>
+#include <task.h>
+#include <macros.h>
+
+#include <net/in.h>
+#include <net/inet.h>
+#include <net/socket.h>
+
+#include <http/http.h>
+#include <uri.h>
+
+#define NAME "download"
+#ifdef TIMESTAMP_UNIX
+#define VERSION STRING(RELEASE) "-" STRING(TIMESTAMP_UNIX)
+#else
+#define VERSION STRING(RELEASE)
+#endif
+#define USER_AGENT "HelenOS-" NAME "/" VERSION
+
+static void syntax_print(void)
+{
+	fprintf(stderr, "Usage: download <url>\n");
+	fprintf(stderr, "  This will write the data to stdout, so you may want\n");
+	fprintf(stderr, "  to redirect the output, e.g.\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "    download http://helenos.org/ | to helenos.html\n\n");
+}
+
+int main(int argc, char *argv[])
+{
+	if (argc != 2) {
+		syntax_print();
+		return 2;
+	}
+	
+	uri_t *uri = uri_parse(argv[1]);
+	if (uri == NULL) {
+		fprintf(stderr, "Failed parsing URI\n");
+		return 2;
+	}
+	
+	if (!uri_validate(uri)) {
+		fprintf(stderr, "The URI is invalid\n");
+		return 2;
+	}
+	
+	/* TODO uri_normalize(uri) */
+	
+	if (str_cmp(uri->scheme, "http") != 0) {
+		fprintf(stderr, "Only http scheme is supported at the moment\n");
+		return 2;
+	}
+	
+	if (uri->host == NULL) {
+		fprintf(stderr, "host not set\n");
+		return 2;
+	}
+	
+	uint16_t port = 80;
+	if (uri->port != NULL) {
+		int rc = str_uint16_t(uri->port, NULL, 10, true, &port);
+		if (rc != EOK) {
+			fprintf(stderr, "Invalid port number: %s\n", uri->port);
+			return 2;
+		}
+	}
+	
+	const char *path = uri->path;
+	if (path == NULL || *path == 0)
+		path = "/";
+	char *server_path = NULL;
+	if (uri->query == NULL) {
+		server_path = str_dup(path);
+		if (server_path == NULL) {
+			fprintf(stderr, "Failed allocating path\n");
+			uri_destroy(uri);
+			return 3;
+		}
+	}
+	else {
+		int rc = asprintf(&server_path, "%s?%s", path, uri->query);
+		if (rc < 0) {
+			fprintf(stderr, "Failed allocating path\n");
+			uri_destroy(uri);
+			return 3;
+		}
+	}
+	
+	http_request_t *req = http_request_create("GET", server_path);
+	free(server_path);
+	if (req == NULL) {
+		fprintf(stderr, "Failed creating request\n");
+		uri_destroy(uri);
+		return 3;
+	}
+	
+	int rc = http_headers_append(&req->headers, "Host", uri->host);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed setting Host header: %s\n", str_error(rc));
+		uri_destroy(uri);
+		return rc;
+	}
+	
+	rc = http_headers_append(&req->headers, "User-Agent", USER_AGENT);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed creating User-Agent header: %s\n", str_error(rc));
+		uri_destroy(uri);
+		return rc;
+	}
+	
+	http_t *http = http_create(uri->host, port);
+	if (http == NULL) {
+		uri_destroy(uri);
+		fprintf(stderr, "Failed creating HTTP object\n");
+		return 3;
+	}
+	
+	rc = http_connect(http);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed connecting: %s\n", str_error(rc));
+		uri_destroy(uri);
+		return rc;
+	}
+	
+	rc = http_send_request(http, req);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed sending request: %s\n", str_error(rc));
+		uri_destroy(uri);
+		return rc;
+	}
+	
+	http_response_t *response = NULL;
+	rc = http_receive_response(&http->recv_buffer, &response, 16 * 1024,
+	    100);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed receiving response: %s\n", str_error(rc));
+		uri_destroy(uri);
+		return rc;
+	}
+	
+	if (response->status != 200) {
+		fprintf(stderr, "Server returned status %d %s\n", response->status,
+		    response->message);
+	}
+	else {
+		size_t buf_size = 4096;
+		void *buf = malloc(buf_size);
+		if (buf == NULL) {
+			fprintf(stderr, "Failed allocating buffer\n)");
+			uri_destroy(uri);
+			return ENOMEM;
+		}
+		
+		int body_size;
+		while ((body_size = recv_buffer(&http->recv_buffer, buf, buf_size)) > 0) {
+			fwrite(buf, 1, body_size, stdout);
+		}
+		
+		if (body_size != 0) {
+			fprintf(stderr, "Failed receiving body: %s", str_error(body_size));
+		}
+	}
+	
+	uri_destroy(uri);
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/app/edit/sheet.c
===================================================================
--- uspace/app/edit/sheet.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/edit/sheet.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -105,5 +105,4 @@
 	char *ipp;
 	size_t sz;
-	tag_t *tag;
 	char *newp;
 
@@ -128,7 +127,5 @@
 	/* Adjust tags. */
 
-	list_foreach(sh->tags, link) {
-		tag = list_get_instance(link, tag_t, link);
-
+	list_foreach(sh->tags, link, tag_t, tag) {
 		if (tag->b_off > pos->b_off)
 			tag->b_off += sz;
@@ -154,5 +151,4 @@
 	char *spp;
 	size_t sz;
-	tag_t *tag;
 	char *newp;
 	size_t shrink_size;
@@ -165,7 +161,5 @@
 
 	/* Adjust tags. */
-	list_foreach(sh->tags, link) {
-		tag = list_get_instance(link, tag_t, link);
-
+	list_foreach(sh->tags, link, tag_t, tag) {
 		if (tag->b_off >= epos->b_off)
 			tag->b_off -= sz;
Index: uspace/app/getterm/getterm.c
===================================================================
--- uspace/app/getterm/getterm.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/getterm/getterm.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -112,10 +112,4 @@
 	reopen(&stderr, 2, term, O_WRONLY, "w");
 	
-	/*
-	 * FIXME: fdopen() should actually detect that we are opening a console
-	 * and it should set line-buffering mode automatically.
-	 */
-	setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
-	
 	if (stdin == NULL)
 		return -2;
@@ -126,4 +120,10 @@
 	if (stderr == NULL)
 		return -4;
+	
+	/*
+	 * FIXME: fdopen() should actually detect that we are opening a console
+	 * and it should set line-buffering mode automatically.
+	 */
+	setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
 	
 	version_print(term);
Index: uspace/app/inet/inet.c
===================================================================
--- uspace/app/inet/inet.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/inet/inet.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -312,4 +312,50 @@
 }
 
+static int link_list(void)
+{
+	sysarg_t *link_list;
+	inet_link_info_t linfo;
+
+	size_t count;
+	size_t i;
+	int rc;
+
+	rc = inetcfg_get_link_list(&link_list, &count);
+	if (rc != EOK) {
+		printf(NAME ": Failed getting link list.\n");
+		return rc;
+	}
+
+	printf("IP links:\n");
+	if (count > 0)
+		printf("    [Link-layer Address] [Link-Name] [Def-MTU]\n");
+
+	for (i = 0; i < count; i++) {
+		rc = inetcfg_link_get(link_list[i], &linfo);
+		if (rc != EOK) {
+			printf("Failed getting properties of link %zu.\n",
+			    (size_t)link_list[i]);
+			continue;
+		}
+
+		printf("    %02x:%02x:%02x:%02x:%02x:%02x %s %zu\n",
+		    linfo.mac_addr[0], linfo.mac_addr[1],
+		    linfo.mac_addr[2], linfo.mac_addr[3],
+		    linfo.mac_addr[4], linfo.mac_addr[5],
+		    linfo.name, linfo.def_mtu);
+
+		free(linfo.name);
+
+		linfo.name = NULL;
+	}
+
+	if (count == 0)
+		printf("    None\n");
+
+	free(link_list);
+
+	return EOK;
+}
+
 static int sroute_list(void)
 {
@@ -404,4 +450,7 @@
 		if (rc != EOK)
 			return 1;
+		rc = link_list();
+		if (rc != EOK)
+			return 1;
 		return 0;
 	}
Index: uspace/app/init/init.c
===================================================================
--- uspace/app/init/init.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/init/init.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -360,4 +360,6 @@
 	srv_start("/srv/udp");
 	srv_start("/srv/dnsrsrv");
+	srv_start("/srv/dhcp");
+	srv_start("/srv/nconfsrv");
 	
 	srv_start("/srv/clipboard");
@@ -384,4 +386,5 @@
 	srv_start("/srv/input", HID_INPUT);
 	srv_start("/srv/output", HID_OUTPUT);
+	srv_start("/srv/hound");
 	
 	int rc = compositor(HID_INPUT, HID_COMPOSITOR_SERVER);
Index: uspace/app/klog/klog.c
===================================================================
--- uspace/app/klog/klog.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/klog/klog.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -206,6 +206,6 @@
 	klog_length = size / sizeof(wchar_t);
 	
-	rc = physmem_map((void *) faddr, pages,
-	    AS_AREA_READ | AS_AREA_CACHEABLE, (void *) &klog);
+	rc = physmem_map(faddr, pages, AS_AREA_READ | AS_AREA_CACHEABLE,
+	    (void *) &klog);
 	if (rc != EOK) {
 		fprintf(stderr, "%s: Unable to map klog\n", NAME);
Index: uspace/app/mixerctl/Makefile
===================================================================
--- uspace/app/mixerctl/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/mixerctl/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,41 @@
+#
+# Copyright (c) 2011 Jan Vesely
+# 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 = mixerctl
+
+LIBS = \
+	$(LIBDRV_PREFIX)/libdrv.a
+
+EXTRA_CFLAGS = \
+	-I$(LIBDRV_PREFIX)/include
+
+SOURCES = \
+	mixerctl.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/mixerctl/mixerctl.c
===================================================================
--- uspace/app/mixerctl/mixerctl.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/mixerctl/mixerctl.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 mixerctl
+ * @{
+ */
+/** @file Mixer control for audio devices
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <str_error.h>
+#include <str.h>
+#include <devman.h>
+#include <audio_mixer_iface.h>
+#include <stdio.h>
+
+#define DEFAULT_DEVICE "/hw/pci0/00:01.0/sb16/control"
+
+/**
+ * Print volume levels on all channels on all control items.
+ * @param exch IPC exchange
+ */
+static void print_levels(async_exch_t *exch)
+{
+	const char* name = NULL;
+	unsigned count = 0;
+	int ret = audio_mixer_get_info(exch, &name, &count);
+	if (ret != EOK) {
+		printf("Failed to get mixer info: %s.\n", str_error(ret));
+		return;
+	}
+	printf("MIXER %s:\n\n", name);
+
+	for (unsigned i = 0; i < count; ++i) {
+		const char *name = NULL;
+		unsigned levels = 0, current = 0;
+		int ret =
+		    audio_mixer_get_item_info(exch, i, &name, &levels);
+		if (ret != EOK) {
+			printf("Failed to get item %u info: %s.\n",
+			    i, str_error(ret));
+			continue;
+		}
+		ret = audio_mixer_get_item_level(exch, i, &current);
+		if (ret != EOK) {
+			printf("Failed to get item %u info: %s.\n",
+			    i, str_error(ret));
+			continue;
+		}
+
+		printf("Control item %u `%s' : %u/%u.\n",
+		    i, name, current, levels - 1);
+		free(name);
+
+	}
+}
+
+static unsigned get_number(const char* str)
+{
+	uint16_t num;
+	str_uint16_t(str, NULL, 10, false, &num);
+	return num;
+}
+
+static void set_level(async_exch_t *exch, int argc, char *argv[])
+{
+	assert(exch);
+	if (argc != 4 && argc != 5) {
+		printf("%s [device] setlevel item value\n", argv[0]);
+		return;
+	}
+	unsigned params = argc == 5 ? 3 : 2;
+	const unsigned item = get_number(argv[params++]);
+	const unsigned value = get_number(argv[params]);
+	int ret = audio_mixer_set_item_level(exch, item, value);
+	if (ret != EOK) {
+		printf("Failed to set item level: %s.\n", str_error(ret));
+		return;
+	}
+	printf("Control item %u new level is %u.\n", item, value);
+}
+
+static void get_level(async_exch_t *exch, int argc, char *argv[])
+{
+	assert(exch);
+	if (argc != 3 && argc != 4) {
+		printf("%s [device] getlevel item \n", argv[0]);
+		return;
+	}
+	unsigned params = argc == 4 ? 3 : 2;
+	const unsigned item = get_number(argv[params++]);
+	unsigned value = 0;
+
+	int ret = audio_mixer_get_item_level(exch, item, &value);
+	if (ret != EOK) {
+		printf("Failed to get item level: %s.\n", str_error(ret));
+		return;
+	}
+	printf("Control item %u level: %u.\n", item, value);
+}
+
+int main(int argc, char *argv[])
+{
+	const char *device = DEFAULT_DEVICE;
+	void (*command)(async_exch_t *, int, char*[]) = NULL;
+
+	if (argc >= 2 && str_cmp(argv[1], "setlevel") == 0) {
+		command = set_level;
+		if (argc == 5)
+			device = argv[1];
+	}
+
+	if (argc >= 2 && str_cmp(argv[1], "getlevel") == 0) {
+		command = get_level;
+		if (argc == 4)
+			device = argv[1];
+	}
+
+	if ((argc == 2 && command == NULL))
+		device = argv[1];
+
+
+	devman_handle_t mixer_handle;
+	int ret = devman_fun_get_handle(device, &mixer_handle, 0);
+	if (ret != EOK) {
+		printf("Failed to get device(%s) handle: %s.\n",
+		    device, str_error(ret));
+		return 1;
+	}
+
+	async_sess_t *session = devman_device_connect(
+	    EXCHANGE_ATOMIC, mixer_handle, IPC_FLAG_BLOCKING);
+	if (!session) {
+		printf("Failed to connect to device.\n");
+		return 1;
+	}
+
+	async_exch_t *exch = async_exchange_begin(session);
+	if (!exch) {
+		printf("Failed to start session exchange.\n");
+		async_hangup(session);
+		return 1;
+	}
+
+	if (command) {
+		command(exch, argc, argv);
+	} else {
+		print_levels(exch);
+		printf("\n%s:\n", argv[0]);
+		printf("Use '%s getlevel idx' command to read individual "
+		    "settings\n", argv[0]);
+		printf("Use '%s setlevel idx' command to change "
+		    "settings\n", argv[0]);
+	}
+
+	async_exchange_end(exch);
+	async_hangup(session);
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/app/netspeed/Makefile
===================================================================
--- uspace/app/netspeed/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/netspeed/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2013 Martin Sucha
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+LIBS = 
+EXTRA_CFLAGS = 
+BINARY = netspeed
+
+SOURCES = \
+	netspeed.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/netspeed/netspeed.c
===================================================================
--- uspace/app/netspeed/netspeed.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/netspeed/netspeed.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup netspeed
+ * @{
+ */
+
+/** @file
+ * Network speed measurement (iperf counterpart)
+ *
+ */
+
+#include <assert.h>
+#include <inet/dnsr.h>
+#include <net/in.h>
+#include <net/inet.h>
+#include <net/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <str_error.h>
+#include <task.h>
+
+#define NAME "netspeed"
+
+static int server(sock_type_t sock_type, unsigned port, void *buf, size_t bufsize)
+{
+	struct sockaddr_in addr;
+	
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	
+	int rc = inet_pton(AF_INET, "127.0.0.1", (void *)
+	    &addr.sin_addr.s_addr);
+	if (rc != EOK) {
+		fprintf(stderr, "inet_pton failed: %s\n", str_error(rc));
+		return rc;
+	}
+
+	int listen_sd = socket(PF_INET, sock_type, 0);
+	if (listen_sd < 0) {
+		fprintf(stderr, "socket failed: %s\n", str_error(rc));
+		return rc;
+	}
+	
+	rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
+	if (rc != EOK) {
+		fprintf(stderr, "bind failed: %s\n", str_error(rc));
+		closesocket(listen_sd);
+		return rc;
+	}
+	
+	rc = listen(listen_sd, 2);
+	if (rc != EOK) {
+		fprintf(stderr, "listen failed: %s\n", str_error(rc));
+		closesocket(listen_sd);
+		return rc;
+	}
+	
+	int conn_sd;
+	struct sockaddr_in raddr;
+	socklen_t raddr_len = sizeof(raddr);
+	if (sock_type == SOCK_STREAM) {
+		conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
+		    &raddr_len);
+		if (conn_sd < 0) {
+			fprintf(stderr, "accept failed: %s\n", str_error(conn_sd));
+			closesocket(listen_sd);
+			return conn_sd;
+		}
+	} else {
+		conn_sd = listen_sd;
+	}
+	
+	while (true) {
+		rc = recvfrom(conn_sd, buf, bufsize, 0, (struct sockaddr *) &raddr,
+		    &raddr_len);
+		if (rc <= 0) {
+			if (rc < 0)
+				fprintf(stderr, "recvfrom failed: %s\n", str_error(rc));
+			break;
+		}
+	}
+	
+	if (sock_type == SOCK_STREAM)
+		closesocket(conn_sd);
+	closesocket(listen_sd);
+	
+	return rc;
+}
+
+static int client(sock_type_t sock_type, const char *host, unsigned port,
+    unsigned long count, char *buf, size_t bufsize)
+{
+	inet_addr_t iaddr;
+	struct sockaddr *saddr;
+	socklen_t saddrlen;
+	
+	int rc = inet_addr_parse(host, &iaddr);
+	if (rc != EOK) {
+		dnsr_hostinfo_t *hinfo = NULL;
+		rc = dnsr_name2host(host, &hinfo, ip_any);
+		if (rc != EOK) {
+			fprintf(stderr, "Error resolving host '%s'.\n", host);
+			return ENOENT;
+		}
+		
+		iaddr = hinfo->addr;
+	}
+	
+	rc = inet_addr_sockaddr(&iaddr, port, &saddr, &saddrlen);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		fprintf(stderr, "Out of memory.\n");
+		return ENOMEM;
+	}
+	
+	int conn_sd = socket(saddr->sa_family, sock_type, 0);
+	if (conn_sd < 0) {
+		fprintf(stderr, "socket failed: %s\n", str_error(rc));
+		return rc;
+	}
+	
+	if (sock_type == SOCK_STREAM) {
+		rc = connect(conn_sd, saddr, saddrlen);
+		if (rc != EOK) {
+			fprintf(stderr, "connect failed: %s\n", str_error(rc));
+			closesocket(conn_sd);
+			return rc;
+		}
+	}
+	
+	for (size_t pos = 0; pos < bufsize; pos++) {
+		buf[pos] = '0' + (pos % 10);
+	}
+	
+	for (unsigned long i = 0; i < count; i++) {
+		if (sock_type == SOCK_STREAM) {
+			rc = send(conn_sd, buf, bufsize, 0);
+		} else {
+			rc = sendto(conn_sd, buf, bufsize, 0, saddr, saddrlen);
+		}
+		if (rc != EOK) {
+			fprintf(stderr, "send failed: %s\n", str_error(rc));
+			break;
+		}
+	}
+	
+	closesocket(conn_sd);
+	free(saddr);
+	return rc;
+}
+
+static void syntax_print(void)
+{
+	fprintf(stderr, "Usage: netspeed <tcp|udp> server [port] <buffer size>\n");
+	fprintf(stderr, "       netspeed <tcp|udp> client <host> <port> <count> <buffer size>\n");
+}
+
+int main(int argc, char *argv[])
+{
+	if (argc < 3) {
+		syntax_print();
+		return 2;
+	}
+	
+	sock_type_t sock_type;
+	if (str_cmp(argv[1], "tcp") == 0) {
+		sock_type = SOCK_STREAM;
+	} else if (str_cmp(argv[1], "udp") == 0) {
+		sock_type = SOCK_DGRAM;
+	} else {
+		fprintf(stderr, "Invalid socket type\n");
+		syntax_print();
+		return 1;
+	}
+	
+	char *endptr;
+	int arg = 3;
+	unsigned long port = 5001;
+	const char *address;
+	bool is_server;
+	unsigned long count = 0;
+	
+	if (str_cmp(argv[2], "server") == 0) {
+		is_server = true;
+		if (argc < 4) {
+			syntax_print();
+			return 2;
+		}
+		if (argc > 4) {
+			port = strtoul(argv[3], &endptr, 0);
+			if (*endptr != 0) {
+				fprintf(stderr, "Invalid port number\n");
+				syntax_print();
+				return 1;
+			}
+			arg = 4;
+		}
+	}
+	else if (str_cmp(argv[2], "client") == 0) {
+		is_server = false;
+		if (argc < 6) {
+			syntax_print();
+			return 2;
+		}
+		address = argv[3];
+		port = strtoul(argv[4], &endptr, 0);
+		if (*endptr != 0) {
+			fprintf(stderr, "Invalid port number\n");
+			syntax_print();
+			return 1;
+		}
+		
+		count = strtoul(argv[5], &endptr, 0);
+		if (*endptr != 0) {
+			fprintf(stderr, "Invalid count\n");
+			syntax_print();
+			return 1;
+		}
+		
+		arg = 6;
+	}
+	else {
+		fprintf(stderr, "Invalid client/server mode\n");
+		syntax_print();
+		return 2;
+	}
+	
+	if (argc < arg + 1) {
+		syntax_print();
+		return 2;
+	}
+	
+	unsigned long bufsize = strtoul(argv[arg], &endptr, 0);
+	if (*endptr != 0 || bufsize == 0) {
+		fprintf(stderr, "Invalid buffer size\n");
+		syntax_print();
+		return 1;
+	}
+	
+	void *buf = malloc(bufsize);
+	if (buf == NULL) {
+		fprintf(stderr, "Cannot allocate buffer\n");
+		return ENOMEM;
+	}
+	
+	int rc = EOK;
+	if (is_server) {
+		rc = server(sock_type, port, buf, bufsize);
+		if (rc != EOK)
+			fprintf(stderr, "Server failed: %s\n", str_error(rc));
+	} else {
+		rc = client(sock_type, address, port, count, buf, bufsize);
+		if (rc != EOK)
+			fprintf(stderr, "Client failed: %s\n", str_error(rc));
+	}
+	
+	free(buf);
+
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/app/nettest1/nettest1.c
===================================================================
--- uspace/app/nettest1/nettest1.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/nettest1/nettest1.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,4 +38,5 @@
 #include "print_error.h"
 
+#include <assert.h>
 #include <malloc.h>
 #include <stdio.h>
@@ -59,5 +60,5 @@
 #define NETTEST1_TEXT  "Networking test 1 - sockets"
 
-static uint16_t family = AF_INET;
+static uint16_t family = AF_NONE;
 static sock_type_t type = SOCK_DGRAM;
 static size_t size = 27;
@@ -326,48 +327,36 @@
 	}
 	
-	char *addr_s = argv[argc - 1];
+	char *host = argv[argc - 1];
 	
 	/* Interpret as address */
-	inet_addr_t addr_addr;
-	rc = inet_addr_parse(addr_s, &addr_addr);
+	inet_addr_t iaddr;
+	rc = inet_addr_parse(host, &iaddr);
 	
 	if (rc != EOK) {
 		/* Interpret as a host name */
 		dnsr_hostinfo_t *hinfo = NULL;
-		rc = dnsr_name2host(addr_s, &hinfo, family);
+		rc = dnsr_name2host(host, &hinfo, ipver_from_af(family));
 		
 		if (rc != EOK) {
-			printf("Error resolving host '%s'.\n", addr_s);
+			printf("Error resolving host '%s'.\n", host);
 			return EINVAL;
 		}
 		
-		addr_addr = hinfo->addr;
-	}
-	
-	struct sockaddr_in addr;
-	struct sockaddr_in6 addr6;
-	uint16_t af = inet_addr_sockaddr_in(&addr_addr, &addr, &addr6);
-	
-	if (af != family) {
+		iaddr = hinfo->addr;
+	}
+	
+	rc = inet_addr_sockaddr(&iaddr, port, &address, &addrlen);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		printf("Out of memory.\n");
+		return ENOMEM;
+	}
+	
+	if (family == AF_NONE)
+		family = address->sa_family;
+	
+	if (address->sa_family != family) {
 		printf("Address family does not match explicitly set family.\n");
 		return EINVAL;
-	}
-	
-	/* Prepare the address buffer */
-	
-	switch (af) {
-	case AF_INET:
-		addr.sin_port = htons(port);
-		address = (struct sockaddr *) &addr;
-		addrlen = sizeof(addr);
-		break;
-	case AF_INET6:
-		addr6.sin6_port = htons(port);
-		address = (struct sockaddr *) &addr6;
-		addrlen = sizeof(addr6);
-		break;
-	default:
-		fprintf(stderr, "Address family is not supported\n");
-		return EAFNOSUPPORT;
 	}
 	
@@ -434,4 +423,6 @@
 	    &time_before));
 	
+	free(address);
+	
 	if (verbose)
 		printf("Exiting\n");
Index: uspace/app/nettest2/nettest2.c
===================================================================
--- uspace/app/nettest2/nettest2.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/nettest2/nettest2.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,4 +38,5 @@
 #include "print_error.h"
 
+#include <assert.h>
 #include <malloc.h>
 #include <stdio.h>
@@ -60,5 +61,5 @@
 #define NETTEST2_TEXT  "Networking test 2 - transfer"
 
-static uint16_t family = PF_INET;
+static uint16_t family = AF_NONE;
 static size_t size = 28;
 static bool verbose = false;
@@ -271,5 +272,5 @@
 		/* Interpret as a host name */
 		dnsr_hostinfo_t *hinfo = NULL;
-		rc = dnsr_name2host(addr_s, &hinfo, family);
+		rc = dnsr_name2host(addr_s, &hinfo, ipver_from_af(family));
 		
 		if (rc != EOK) {
@@ -281,32 +282,19 @@
 	}
 	
-	struct sockaddr_in addr;
-	struct sockaddr_in6 addr6;
-	uint16_t af = inet_addr_sockaddr_in(&addr_addr, &addr, &addr6);
-	
-	if (af != family) {
+	struct sockaddr *address;
+	socklen_t addrlen;
+	rc = inet_addr_sockaddr(&addr_addr, port, &address, &addrlen);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		printf("Out of memory.\n");
+		return ENOMEM;
+	}
+	
+	if (family == AF_NONE)
+		family = address->sa_family;
+	
+	if (address->sa_family != family) {
 		printf("Address family does not match explicitly set family.\n");
 		return EINVAL;
-	}
-	
-	/* Prepare the address buffer */
-	
-	struct sockaddr *address;
-	socklen_t addrlen;
-	
-	switch (af) {
-	case AF_INET:
-		addr.sin_port = htons(port);
-		address = (struct sockaddr *) &addr;
-		addrlen = sizeof(addr);
-		break;
-	case AF_INET6:
-		addr6.sin6_port = htons(port);
-		address = (struct sockaddr *) &addr6;
-		addrlen = sizeof(addr6);
-		break;
-	default:
-		fprintf(stderr, "Address family is not supported\n");
-		return EAFNOSUPPORT;
 	}
 	
@@ -424,4 +412,6 @@
 		return rc;
 	
+	free(address);
+	
 	if (verbose)
 		printf("\nExiting\n");
Index: uspace/app/nettest3/nettest3.c
===================================================================
--- uspace/app/nettest3/nettest3.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/nettest3/nettest3.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,4 +37,5 @@
 #include <async.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <str.h>
 
@@ -52,5 +53,6 @@
 static char buf[BUF_SIZE];
 
-static struct sockaddr_in addr;
+static struct sockaddr *address;
+static socklen_t addrlen;
 
 static uint16_t port;
@@ -62,4 +64,6 @@
 	char *endptr;
 	dnsr_hostinfo_t *hinfo;
+	inet_addr_t addr;
+	char *addr_s;
 
 	port = 7;
@@ -69,32 +73,33 @@
 
 	/* Connect to local IP address by default */
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(port);
-	addr.sin_addr.s_addr = htonl(0x7f000001);
+	inet_addr(&addr, 127, 0, 0, 1);
 
 	if (argc >= 2) {
 		printf("parsing address '%s'\n", argv[1]);
-		rc = inet_pton(AF_INET, argv[1], (uint8_t *)&addr.sin_addr.s_addr);
+		rc = inet_addr_parse(argv[1], &addr);
 		if (rc != EOK) {
 			/* Try interpreting as a host name */
-			rc = dnsr_name2host(argv[1], &hinfo, AF_INET);
+			rc = dnsr_name2host(argv[1], &hinfo, ip_v4);
 			if (rc != EOK) {
 				printf("Error resolving host '%s'.\n", argv[1]);
 				return rc;
 			}
-			
-			uint16_t af = inet_addr_sockaddr_in(&hinfo->addr, &addr, NULL);
-			if (af != AF_INET) {
-				printf("Host '%s' not resolved as IPv4 address.\n", argv[1]);
-				return rc;
-			}
+
+			addr = hinfo->addr;
 		}
-		printf("result: rc=%d, family=%d, addr=%x\n", rc,
-		    addr.sin_family, addr.sin_addr.s_addr);
+		rc = inet_addr_format(&addr, &addr_s);
+		if (rc != EOK) {
+			assert(rc == ENOMEM);
+			printf("Out of memory.\n");
+			return rc;
+		}
+		printf("result: rc=%d, ver=%d, addr=%s\n", rc,
+		    addr.version, addr_s);
+		free(addr_s);
 	}
 
 	if (argc >= 3) {
 		printf("parsing port '%s'\n", argv[2]);
-		addr.sin_port = htons(strtoul(argv[2], &endptr, 10));
+		port = htons(strtoul(argv[2], &endptr, 10));
 		if (*endptr != '\0') {
 			fprintf(stderr, "Error parsing port\n");
@@ -103,6 +108,12 @@
 	}
 
+	rc = inet_addr_sockaddr(&hinfo->addr, port, &address, &addrlen);
+	if (rc != EOK) {
+		printf("Out of memory.\n");
+		return rc;
+	}
+
 	printf("socket()\n");
-	fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+	fd = socket(address->sa_family, SOCK_STREAM, IPPROTO_TCP);
 	printf(" -> %d\n", fd);
 	if (fd < 0)
@@ -110,5 +121,5 @@
 
 	printf("connect()\n");
-	rc = connect(fd, (struct sockaddr *) &addr, sizeof(addr));
+	rc = connect(fd, address, addrlen);
 	printf(" -> %d\n", rc);
 	if (rc != 0)
@@ -133,4 +144,6 @@
 	printf(" -> %d\n", rc);
 
+	free(address);
+
 	return 0;
 }
Index: uspace/app/nterm/conn.c
===================================================================
--- uspace/app/nterm/conn.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/nterm/conn.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,6 +38,8 @@
 #include <fibril.h>
 #include <inet/dnsr.h>
+#include <net/inet.h>
 #include <net/socket.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <str_error.h>
 #include <sys/types.h>
@@ -73,28 +75,26 @@
 }
 
-int conn_open(const char *addr_s, const char *port_s)
+int conn_open(const char *host, const char *port_s)
 {
 	int conn_fd = -1;
+	struct sockaddr *saddr = NULL;
+	socklen_t saddrlen;
 	
 	/* Interpret as address */
-	inet_addr_t addr_addr;
-	int rc = inet_addr_parse(addr_s, &addr_addr);
+	inet_addr_t iaddr;
+	int rc = inet_addr_parse(host, &iaddr);
 	
 	if (rc != EOK) {
 		/* Interpret as a host name */
 		dnsr_hostinfo_t *hinfo = NULL;
-		rc = dnsr_name2host(addr_s, &hinfo, 0);
+		rc = dnsr_name2host(host, &hinfo, ip_any);
 		
 		if (rc != EOK) {
-			printf("Error resolving host '%s'.\n", addr_s);
+			printf("Error resolving host '%s'.\n", host);
 			goto error;
 		}
 		
-		addr_addr = hinfo->addr;
+		iaddr = hinfo->addr;
 	}
-	
-	struct sockaddr_in addr;
-	struct sockaddr_in6 addr6;
-	uint16_t af = inet_addr_sockaddr_in(&addr_addr, &addr, &addr6);
 	
 	char *endptr;
@@ -105,24 +105,18 @@
 	}
 	
-	printf("Connecting to host %s port %u\n", addr_s, port);
+	rc = inet_addr_sockaddr(&iaddr, port, &saddr, &saddrlen);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		printf("Out of memory.\n");
+		return ENOMEM;
+	}
 	
-	conn_fd = socket(PF_INET, SOCK_STREAM, 0);
+	printf("Connecting to host %s port %u\n", host, port);
+	
+	conn_fd = socket(saddr->sa_family, SOCK_STREAM, 0);
 	if (conn_fd < 0)
 		goto error;
 	
-	switch (af) {
-	case AF_INET:
-		addr.sin_port = htons(port);
-		rc = connect(conn_fd, (struct sockaddr *) &addr, sizeof(addr));
-		break;
-	case AF_INET6:
-		addr6.sin6_port = htons(port);
-		rc = connect(conn_fd, (struct sockaddr *) &addr6, sizeof(addr6));
-		break;
-	default:
-		printf("Unknown address family.\n");
-		goto error;
-	}
-	
+	rc = connect(conn_fd, saddr, saddrlen);
 	if (rc != EOK)
 		goto error;
@@ -134,6 +128,6 @@
 	fibril_add_ready(rcv_fid);
 	
+	free(saddr);
 	return EOK;
-	
 error:
 	if (conn_fd >= 0) {
@@ -141,4 +135,5 @@
 		conn_fd = -1;
 	}
+	free(saddr);
 	
 	return EIO;
Index: uspace/app/ping/ping.c
===================================================================
--- uspace/app/ping/ping.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/ping/ping.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,5 +37,4 @@
 #include <errno.h>
 #include <fibril_synch.h>
-#include <net/socket_codes.h>
 #include <inet/dnsr.h>
 #include <inet/addr.h>
@@ -77,15 +76,18 @@
 };
 
-static addr32_t src;
-static addr32_t dest;
+static inet_addr_t src_addr;
+static inet_addr_t dest_addr;
 
 static bool repeat_forever = false;
 static size_t repeat_count = 1;
 
-static const char *short_options = "rn:";
+static const char *short_options = "46rn:";
 
 static void print_syntax(void)
 {
-	printf("Syntax: %s [-n <count>|-r] <host>\n", NAME);
+	printf("Syntax: %s [<options>] <host>\n", NAME);
+	printf("\t-n <count> Repeat the specified number of times\n");
+	printf("\t-r         Repeat forever\n");
+	printf("\t-4|-6      Use IPv4 or IPv6 destination host address\n");
 }
 
@@ -108,10 +110,4 @@
 static int ping_ev_recv(inetping_sdu_t *sdu)
 {
-	inet_addr_t src_addr;
-	inet_addr_set(sdu->src, &src_addr);
-	
-	inet_addr_t dest_addr;
-	inet_addr_set(sdu->dest, &dest_addr);
-	
 	char *asrc;
 	int rc = inet_addr_format(&src_addr, &asrc);
@@ -140,6 +136,6 @@
 	inetping_sdu_t sdu;
 	
-	sdu.src = src;
-	sdu.dest = dest;
+	sdu.src = src_addr;
+	sdu.dest = dest_addr;
 	sdu.seq_no = seq_no;
 	sdu.data = (void *) "foo";
@@ -222,4 +218,5 @@
 	char *adest = NULL;
 	char *sdest = NULL;
+	ip_ver_t ip_ver = ip_any;
 	
 	int rc = inetping_init(&ev_ops);
@@ -244,4 +241,10 @@
 			}
 			break;
+		case '4':
+			ip_ver = ip_v4;
+			break;
+		case '6':
+			ip_ver = ip_v6;
+			break;
 		default:
 			printf("Unknown option passed.\n");
@@ -258,9 +261,8 @@
 	
 	/* Parse destination address */
-	inet_addr_t dest_addr;
 	rc = inet_addr_parse(argv[optind], &dest_addr);
 	if (rc != EOK) {
 		/* Try interpreting as a host name */
-		rc = dnsr_name2host(argv[optind], &hinfo, AF_INET);
+		rc = dnsr_name2host(argv[optind], &hinfo, ip_ver);
 		if (rc != EOK) {
 			printf("Error resolving host '%s'.\n", argv[optind]);
@@ -271,20 +273,10 @@
 	}
 	
-	uint16_t af = inet_addr_get(&dest_addr, &dest, NULL);
-	if (af != AF_INET) {
-		printf("Destination '%s' is not an IPv4 address.\n",
-		    argv[optind]);
-		goto error;
-	}
-	
 	/* Determine source address */
-	rc = inetping_get_srcaddr(dest, &src);
+	rc = inetping_get_srcaddr(&dest_addr, &src_addr);
 	if (rc != EOK) {
 		printf("Failed determining source address.\n");
 		goto error;
 	}
-	
-	inet_addr_t src_addr;
-	inet_addr_set(src, &src_addr);
 	
 	rc = inet_addr_format(&src_addr, &asrc);
Index: pace/app/ping6/Makefile
===================================================================
--- uspace/app/ping6/Makefile	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ 	(revision )
@@ -1,35 +1,0 @@
-#
-# Copyright (c) 2013 Antonin Steinhauser
-# 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 = ping6
-
-SOURCES = \
-	ping6.c
-
-include $(USPACE_PREFIX)/Makefile.common
Index: pace/app/ping6/ping6.c
===================================================================
--- uspace/app/ping6/ping6.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ 	(revision )
@@ -1,352 +1,0 @@
-/*
- * Copyright (c) 2013 Antonin Steinhauser
- * 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 ping6
- * @{
- */
-/** @file ICMPv6 echo utility.
- */
-
-#include <async.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <fibril_synch.h>
-#include <net/socket_codes.h>
-#include <inet/dnsr.h>
-#include <inet/addr.h>
-#include <inet/inetping6.h>
-#include <io/console.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <str.h>
-#include <str_error.h>
-#include <sys/types.h>
-
-#define NAME "ping6"
-
-/** Delay between subsequent ping requests in microseconds */
-#define PING_DELAY (1000 * 1000)
-
-/** Ping request timeout in microseconds */
-#define PING_TIMEOUT (1000 * 1000)
-
-typedef enum {
-	RECEIVED_NONE,
-	RECEIVED_SUCCESS,
-	RECEIVED_INTERRUPT
-} received_t;
-
-static received_t received;
-static FIBRIL_CONDVAR_INITIALIZE(received_cv);
-static FIBRIL_MUTEX_INITIALIZE(received_lock);
-
-static bool quit = false;
-static FIBRIL_CONDVAR_INITIALIZE(quit_cv);
-static FIBRIL_MUTEX_INITIALIZE(quit_lock);
-
-static int ping_ev_recv(inetping6_sdu_t *);
-
-static inetping6_ev_ops_t ev_ops = {
-	.recv = ping_ev_recv
-};
-
-static addr128_t src;
-static addr128_t dest;
-
-static bool repeat_forever = false;
-static size_t repeat_count = 1;
-
-static const char *short_options = "rn:";
-
-static void print_syntax(void)
-{
-	printf("Syntax: %s [-n <count>|-r] <host>\n", NAME);
-}
-
-static void ping_signal_received(received_t value)
-{
-	fibril_mutex_lock(&received_lock);
-	received = value;
-	fibril_mutex_unlock(&received_lock);
-	fibril_condvar_broadcast(&received_cv);
-}
-
-static void ping_signal_quit(void)
-{
-	fibril_mutex_lock(&quit_lock);
-	quit = true;
-	fibril_mutex_unlock(&quit_lock);
-	fibril_condvar_broadcast(&quit_cv);
-}
-
-static int ping_ev_recv(inetping6_sdu_t *sdu)
-{
-	inet_addr_t src_addr;
-	inet_addr_set6(sdu->src, &src_addr);
-	
-	inet_addr_t dest_addr;
-	inet_addr_set6(sdu->dest, &dest_addr);
-	
-	char *asrc;
-	int rc = inet_addr_format(&src_addr, &asrc);
-	if (rc != EOK)
-		return ENOMEM;
-	
-	char *adest;
-	rc = inet_addr_format(&dest_addr, &adest);
-	if (rc != EOK) {
-		free(asrc);
-		return ENOMEM;
-	}
-	
-	printf("Received ICMPv6 echo reply: from %s to %s, seq. no %u, "
-	    "payload size %zu\n", asrc, adest, sdu->seq_no, sdu->size);
-	
-	ping_signal_received(RECEIVED_SUCCESS);
-	
-	free(asrc);
-	free(adest);
-	return EOK;
-}
-
-static int ping_send(uint16_t seq_no)
-{
-	inetping6_sdu_t sdu;
-	
-	addr128(src, sdu.src);
-	addr128(dest, sdu.dest);
-	sdu.seq_no = seq_no;
-	sdu.data = (void *) "foo";
-	sdu.size = 3;
-	
-	int rc = inetping6_send(&sdu);
-	if (rc != EOK)
-		printf("Failed sending echo request: %s (%d).\n",
-		    str_error(rc), rc);
-	
-	return rc;
-}
-
-static int transmit_fibril(void *arg)
-{
-	uint16_t seq_no = 0;
-	
-	while ((repeat_count--) || (repeat_forever)) {
-		fibril_mutex_lock(&received_lock);
-		received = RECEIVED_NONE;
-		fibril_mutex_unlock(&received_lock);
-		
-		(void) ping_send(++seq_no);
-		
-		fibril_mutex_lock(&received_lock);
-		int rc = fibril_condvar_wait_timeout(&received_cv, &received_lock,
-		    PING_TIMEOUT);
-		received_t recv = received;
-		fibril_mutex_unlock(&received_lock);
-		
-		if ((rc == ETIMEOUT) || (recv == RECEIVED_NONE))
-			printf("Echo request timed out (seq. no %u)\n", seq_no);
-		
-		if (recv == RECEIVED_INTERRUPT)
-			break;
-		
-		if ((repeat_count > 0) || (repeat_forever)) {
-			fibril_mutex_lock(&received_lock);
-			rc = fibril_condvar_wait_timeout(&received_cv, &received_lock,
-			    PING_DELAY);
-			recv = received;
-			fibril_mutex_unlock(&received_lock);
-			
-			if (recv == RECEIVED_INTERRUPT)
-				break;
-		}
-	}
-	
-	ping_signal_quit();
-	return 0;
-}
-
-static int input_fibril(void *arg)
-{
-	console_ctrl_t *con = console_init(stdin, stdout);
-	
-	while (true) {
-		cons_event_t ev;
-		if (!console_get_event(con, &ev))
-			break;
-		
-		if ((ev.type == CEV_KEY) && (ev.ev.key.type == KEY_PRESS) &&
-		    ((ev.ev.key.mods & (KM_ALT | KM_SHIFT)) == 0) &&
-		    ((ev.ev.key.mods & KM_CTRL) != 0)) {
-			/* Ctrl+key */
-			if (ev.ev.key.key == KC_Q) {
-				ping_signal_received(RECEIVED_INTERRUPT);
-				break;
-			}
-		}
-	}
-	
-	return 0;
-}
-
-int main(int argc, char *argv[])
-{
-	dnsr_hostinfo_t *hinfo = NULL;
-	char *asrc = NULL;
-	char *adest = NULL;
-	char *sdest = NULL;
-	
-	int rc = inetping6_init(&ev_ops);
-	if (rc != EOK) {
-		printf("Failed connecting to internet ping service: "
-		    "%s (%d).\n", str_error(rc), rc);
-		goto error;
-	}
-	
-	int c;
-	while ((c = getopt(argc, argv, short_options)) != -1) {
-		switch (c) {
-		case 'r':
-			repeat_forever = true;
-			break;
-		case 'n':
-			rc = str_size_t(optarg, NULL, 10, true, &repeat_count);
-			if (rc != EOK) {
-				printf("Invalid repeat count.\n");
-				print_syntax();
-				goto error;
-			}
-			break;
-		default:
-			printf("Unknown option passed.\n");
-			print_syntax();
-			goto error;
-		}
-	}
-	
-	if (optind >= argc) {
-		printf("IP address or host name not supplied.\n");
-		print_syntax();
-		goto error;
-	}
-	
-	/* Parse destination address */
-	inet_addr_t dest_addr;
-	rc = inet_addr_parse(argv[optind], &dest_addr);
-	if (rc != EOK) {
-		/* Try interpreting as a host name */
-		rc = dnsr_name2host(argv[optind], &hinfo, AF_INET);
-		if (rc != EOK) {
-			printf("Error resolving host '%s'.\n", argv[optind]);
-			goto error;
-		}
-		
-		dest_addr = hinfo->addr;
-	}
-	
-	uint16_t af = inet_addr_get(&dest_addr, NULL, &dest);
-	if (af != AF_INET6) {
-		printf("Destination '%s' is not an IPv6 address.\n",
-		    argv[optind]);
-		goto error;
-	}
-	
-	/* Determine source address */
-	rc = inetping6_get_srcaddr(dest, src);
-	if (rc != EOK) {
-		printf("Failed determining source address.\n");
-		goto error;
-	}
-	
-	inet_addr_t src_addr;
-	inet_addr_set6(src, &src_addr);
-	
-	rc = inet_addr_format(&src_addr, &asrc);
-	if (rc != EOK) {
-		printf("Out of memory.\n");
-		goto error;
-	}
-	
-	rc = inet_addr_format(&dest_addr, &adest);
-	if (rc != EOK) {
-		printf("Out of memory.\n");
-		goto error;
-	}
-	
-	if (hinfo != NULL) {
-		rc = asprintf(&sdest, "%s (%s)", hinfo->cname, adest);
-		if (rc < 0) {
-			printf("Out of memory.\n");
-			goto error;
-		}
-	} else {
-		sdest = adest;
-		adest = NULL;
-	}
-	
-	printf("Sending ICMP echo request from %s to %s (Ctrl+Q to quit)\n",
-	    asrc, sdest);
-	
-	fid_t fid = fibril_create(transmit_fibril, NULL);
-	if (fid == 0) {
-		printf("Failed creating transmit fibril.\n");
-		goto error;
-	}
-	
-	fibril_add_ready(fid);
-	
-	fid = fibril_create(input_fibril, NULL);
-	if (fid == 0) {
-		printf("Failed creating input fibril.\n");
-		goto error;
-	}
-	
-	fibril_add_ready(fid);
-	
-	fibril_mutex_lock(&quit_lock);
-	while (!quit)
-		fibril_condvar_wait(&quit_cv, &quit_lock);
-	fibril_mutex_unlock(&quit_lock);
-	
-	free(asrc);
-	free(adest);
-	free(sdest);
-	dnsr_hostinfo_destroy(hinfo);
-	return 0;
-	
-error:
-	free(asrc);
-	free(adest);
-	free(sdest);
-	dnsr_hostinfo_destroy(hinfo);
-	return 1;
-}
-
-/** @}
- */
Index: uspace/app/sportdmp/sportdmp.c
===================================================================
--- uspace/app/sportdmp/sportdmp.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/sportdmp/sportdmp.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -44,8 +44,8 @@
 	sysarg_t baud = 9600;
 	service_id_t svc_id;
-	
+
 	int arg = 1;
 	int rc;
-		
+
 	if (argc > arg && str_test_prefix(argv[arg], "--baud=")) {
 		size_t arg_offset = str_lsize(argv[arg], 7);
@@ -65,5 +65,5 @@
 		arg++;
 	}
-	
+
 	if (argc > arg) {
 		rc = loc_service_get_id(argv[arg], &svc_id, 0);
@@ -77,5 +77,5 @@
 	else {
 		category_id_t serial_cat_id;
-		
+
 		rc = loc_category_get_id("serial", &serial_cat_id, 0);
 		if (rc != EOK) {
@@ -84,13 +84,14 @@
 			return 1;
 		}
-		
+
 		service_id_t *svc_ids;
 		size_t svc_count;
-		
-		rc = loc_category_get_svcs(serial_cat_id, &svc_ids, &svc_count);		if (rc != EOK) {
+
+		rc = loc_category_get_svcs(serial_cat_id, &svc_ids, &svc_count);
+		if (rc != EOK) {
 			fprintf(stderr, "Failed getting list of services\n");
 			return 1;
 		}
-		
+
 		if (svc_count == 0) {
 			fprintf(stderr, "No service in category 'serial'\n");
@@ -98,9 +99,9 @@
 			return 1;
 		}
-		
+
 		svc_id = svc_ids[0];
 		free(svc_ids);
 	}
-	
+
 	if (argc > arg) {
 		fprintf(stderr, "Too many arguments\n");
@@ -108,6 +109,6 @@
 		return 1;
 	}
-	
-	
+
+
 	async_sess_t *sess = loc_service_connect(EXCHANGE_SERIALIZE, svc_id,
 	    IPC_FLAG_BLOCKING);
@@ -115,15 +116,15 @@
 		fprintf(stderr, "Failed connecting to service\n");
 	}
-	
+
 	async_exch_t *exch = async_exchange_begin(sess);
 	rc = async_req_4_0(exch, SERIAL_SET_COM_PROPS, baud,
 	    SERIAL_NO_PARITY, 8, 1);
 	async_exchange_end(exch);
-	
+
 	if (rc != EOK) {
 		fprintf(stderr, "Failed setting serial properties\n");
 		return 2;
 	}
-	
+
 	uint8_t *buf = (uint8_t *) malloc(BUF_SIZE);
 	if (buf == NULL) {
@@ -131,5 +132,5 @@
 		return 3;
 	}
-	
+
 	while (true) {
 		ssize_t read = char_dev_read(sess, buf, BUF_SIZE);
@@ -144,6 +145,7 @@
 		fflush(stdout);
 	}
-	
+
 	free(buf);
 	return 0;
 }
+
Index: uspace/app/tester/mm/common.c
===================================================================
--- uspace/app/tester/mm/common.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/tester/mm/common.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -84,8 +84,6 @@
 }
 
-static bool overlap_match(link_t *link, void *addr, size_t size)
-{
-	mem_block_t *block = list_get_instance(link, mem_block_t, link);
-	
+static bool overlap_match(mem_block_t *block, void *addr, size_t size)
+{
 	/* Entry block control structure <mbeg, mend) */
 	uint8_t *mbeg = (uint8_t *) block;
@@ -125,6 +123,6 @@
 	bool fnd = false;
 	
-	list_foreach(mem_blocks, link) {
-		if (overlap_match(link, addr, size)) {
+	list_foreach(mem_blocks, link, mem_block_t, block) {
+		if (overlap_match(block, addr, size)) {
 			fnd = true;
 			break;
Index: uspace/app/trace/trace.c
===================================================================
--- uspace/app/trace/trace.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/trace/trace.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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/app/usbinfo/dump.c
===================================================================
--- uspace/app/usbinfo/dump.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/usbinfo/dump.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -103,7 +103,5 @@
 void dump_match_ids(match_id_list_t *matches, const char *line_prefix)
 {
-	list_foreach(matches->ids, link) {
-		match_id_t *match = list_get_instance(link, match_id_t, link);
-
+	list_foreach(matches->ids, link, match_id_t, match) {
 		printf("%s%3d %s\n", line_prefix, match->score, match->id);
 	}
Index: uspace/app/usbinfo/hid.c
===================================================================
--- uspace/app/usbinfo/hid.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/app/usbinfo/hid.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -100,13 +100,11 @@
 	printf("%sParsed HID report descriptor for interface %d\n",
 	    get_indent(0), iface_no);
-	list_foreach(report->reports, report_it) {
-		usb_hid_report_description_t *description = list_get_instance(
-		    report_it, usb_hid_report_description_t, reports_link);
+	list_foreach(report->reports, reports_link,
+	    usb_hid_report_description_t, description) {
 		printf("%sReport %d (type %d)\n", get_indent(1),
 		    (int) description->report_id,
 		    (int) description->type);
-		list_foreach(description->report_items, item_it) {
-			usb_hid_report_field_t *field = list_get_instance(
-			    item_it, usb_hid_report_field_t, ritems_link);
+		list_foreach(description->report_items, ritems_link,
+		    usb_hid_report_field_t, field) {
 			printf("%sUsage page = 0x%04x    Usage = 0x%04x\n",
 			    get_indent(2),
Index: uspace/app/viewer/Makefile
===================================================================
--- uspace/app/viewer/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/viewer/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,39 @@
+#
+# Copyright (c) 2013 Martin Decky
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+LIBS = $(LIBGUI_PREFIX)/libgui.a $(LIBDRAW_PREFIX)/libdraw.a \
+	$(LIBSOFTREND_PREFIX)/libsoftrend.a $(LIBSOFTFLOAT_PREFIX)/libsoftfloat.a
+EXTRA_CFLAGS += -I$(LIBGUI_PREFIX) -I$(LIBDRAW_PREFIX) \
+	-I$(LIBSOFTREND_PREFIX)
+BINARY = viewer
+
+SOURCES = \
+	viewer.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/viewer/viewer.c
===================================================================
--- uspace/app/viewer/viewer.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/viewer/viewer.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2013 Martin Decky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup viewer
+ * @{
+ */
+/** @file
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stdbool.h>
+#include <window.h>
+#include <canvas.h>
+#include <surface.h>
+#include <codec/tga.h>
+
+#define NAME  "viewer"
+
+#define WINDOW_WIDTH   1024
+#define WINDOW_HEIGHT  768
+
+static size_t imgs_count;
+static size_t imgs_current = 0;
+static char **imgs;
+
+static window_t *main_window;
+static surface_t *surface = NULL;
+static canvas_t *canvas = NULL;
+
+static bool img_load(const char *);
+
+static void on_keyboard_event(widget_t *widget, void *data)
+{
+	kbd_event_t *event = (kbd_event_t *) data;
+	bool update = false;
+	
+	if ((event->type == KEY_PRESS) && (event->c == 'q'))
+		exit(0);
+	
+	if ((event->type == KEY_PRESS) && (event->key == KC_PAGE_DOWN)) {
+		if (imgs_current == imgs_count - 1)
+			imgs_current = 0;
+		else
+			imgs_current++;
+		
+		update = true;
+	}
+	
+	if ((event->type == KEY_PRESS) && (event->key == KC_PAGE_UP)) {
+		if (imgs_current == 0)
+			imgs_current = imgs_count - 1;
+		else
+			imgs_current--;
+		
+		update = true;
+	}
+	
+	if (update) {
+		if (!img_load(imgs[imgs_current])) {
+			printf("Cannot load image \"%s\".\n", imgs[imgs_current]);
+			exit(2);
+		}
+	}
+}
+
+static bool img_load(const char *fname)
+{
+	int fd = open(fname, O_RDONLY);
+	if (fd < 0)
+		return false;
+	
+	struct stat stat;
+	int rc = fstat(fd, &stat);
+	if (rc != EOK) {
+		close(fd);
+		return false;
+	}
+	
+	void *tga = malloc(stat.size);
+	if (tga == NULL) {
+		close(fd);
+		return false;
+	}
+	
+	ssize_t rd = read_all(fd, tga, stat.size);
+	if ((rd < 0) || (rd != (ssize_t) stat.size)) {
+		free(tga);
+		close(fd);
+		return false;
+	}
+	
+	close(fd);
+	
+	surface_t *local_surface = decode_tga(tga, stat.size, 0);
+	if (local_surface == NULL) {
+		free(tga);
+		return false;
+	}
+	
+	free(tga);
+	
+	if (canvas != NULL) {
+		if (!update_canvas(canvas, local_surface)) {
+			surface_destroy(local_surface);
+			return false;
+		}
+	} else {
+		canvas = create_canvas(window_root(main_window),
+		    WINDOW_WIDTH, WINDOW_HEIGHT, local_surface);
+		if (canvas == NULL) {
+			surface_destroy(local_surface);
+			return false;
+		}
+		
+		sig_connect(&canvas->keyboard_event, NULL, on_keyboard_event);
+	}
+	
+	if (surface != NULL)
+		surface_destroy(surface);
+	
+	surface = local_surface;
+	
+	return true;
+}
+
+int main(int argc, char *argv[])
+{
+	if (argc < 2) {
+		printf("Compositor server not specified.\n");
+		return 1;
+	}
+	
+	if (argc < 3) {
+		printf("No image files specified.\n");
+		return 1;
+	}
+	
+	main_window = window_open(argv[1], true, false, "viewer", 0, 0);
+	if (!main_window) {
+		printf("Cannot open main window.\n");
+		return 2;
+	}
+	
+	imgs_count = argc - 2;
+	imgs = calloc(imgs_count, sizeof(char *));
+	if (imgs == NULL) {
+		printf("Out of memory.\n");
+		return 3;
+	}
+	
+	for (int i = 0; i < argc - 2; i++) {
+		imgs[i] = str_dup(argv[i + 2]);
+		if (imgs[i] == NULL) {
+			printf("Out of memory.\n");
+			return 4;
+		}
+	}
+	
+	if (!img_load(imgs[imgs_current])) {
+		printf("Cannot load image \"%s\".\n", imgs[imgs_current]);
+		return 2;
+	}
+	
+	window_resize(main_window, WINDOW_WIDTH, WINDOW_HEIGHT);
+	window_exec(main_window);
+	
+	task_retval(0);
+	async_manager();
+	
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/app/wavplay/Makefile
===================================================================
--- uspace/app/wavplay/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/wavplay/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,49 @@
+#
+# Copyright (c) 2012 Jan Vesely
+# 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 = wavplay
+
+LIBS = \
+	$(LIBHOUND_PREFIX)/libhound.a \
+	$(LIBDRV_PREFIX)/libdrv.a \
+	$(LIBPCM_PREFIX)/libpcm.a
+
+EXTRA_CFLAGS = \
+	-I$(LIBDRV_PREFIX)/include \
+	-I$(LIBHOUND_PREFIX)/include \
+	-I$(LIBPCM_PREFIX)/include
+
+SOURCES = \
+	dplay.c \
+	drec.c \
+	main.c \
+	wave.c
+
+include $(USPACE_PREFIX)/Makefile.common
+
Index: uspace/app/wavplay/dplay.c
===================================================================
--- uspace/app/wavplay/dplay.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/wavplay/dplay.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 dplay
+ * @{
+ */
+/**
+ * @file PCM playback audio devices
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <str_error.h>
+#include <str.h>
+#include <audio_pcm_iface.h>
+#include <fibril_synch.h>
+#include <pcm/format.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <inttypes.h>
+
+#include <stdio.h>
+#include <macros.h>
+
+#include "wave.h"
+#include "dplay.h"
+
+#define DEFAULT_FRAGMENTS 2
+
+/** Playback helper structure */
+typedef struct {
+	struct {
+		void *base;
+		size_t size;
+		void* write_ptr;
+	} buffer;
+	pcm_format_t f;
+	FILE* source;
+	volatile bool playing;
+	fibril_mutex_t mutex;
+	fibril_condvar_t cv;
+	audio_pcm_sess_t *device;
+} playback_t;
+
+/**
+ * Initialize playback helper structure.
+ * @param pb Pointer to helper structure to initialize
+ * @param sess Pointer to audio device IPC session
+ * @return
+ */
+static void playback_initialize(playback_t *pb, audio_pcm_sess_t *sess)
+{
+	assert(sess);
+	assert(pb);
+	pb->buffer.base = NULL;
+	pb->buffer.size = 0;
+	pb->buffer.write_ptr = NULL;
+	pb->playing = false;
+	pb->source = NULL;
+	pb->device = sess;
+	fibril_mutex_initialize(&pb->mutex);
+	fibril_condvar_initialize(&pb->cv);
+}
+
+/**
+ * Fragment playback callback function.
+ * @param iid IPC call id.
+ * @param icall Pointer to the call structure
+ * @param arg Argument, pointer to the playback helper function
+ */
+static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void* arg)
+{
+	async_answer_0(iid, EOK);
+	playback_t *pb = arg;
+	const size_t fragment_size = pb->buffer.size / DEFAULT_FRAGMENTS;
+	while (1) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		switch(IPC_GET_IMETHOD(call)) {
+		case PCM_EVENT_PLAYBACK_STARTED:
+		case PCM_EVENT_FRAMES_PLAYED:
+			printf("%" PRIun " frames: ", IPC_GET_ARG1(call));
+			async_answer_0(callid, EOK);
+			break;
+		case PCM_EVENT_PLAYBACK_TERMINATED:
+			printf("Playback terminated\n");
+			fibril_mutex_lock(&pb->mutex);
+			pb->playing = false;
+			fibril_condvar_signal(&pb->cv);
+			async_answer_0(callid, EOK);
+			fibril_mutex_unlock(&pb->mutex);
+			return;
+		default:
+			printf("Unknown event %" PRIun ".\n", IPC_GET_IMETHOD(call));
+			async_answer_0(callid, ENOTSUP);
+			continue;
+
+		}
+		const size_t bytes = fread(pb->buffer.write_ptr,
+		   sizeof(uint8_t), fragment_size, pb->source);
+		printf("Copied from position %p size %zu/%zu\n",
+		    pb->buffer.write_ptr, bytes, fragment_size);
+		if (bytes == 0) {
+			audio_pcm_last_playback_fragment(pb->device);
+		}
+		/* any constant is silence */
+		memset(pb->buffer.write_ptr + bytes, 0, fragment_size - bytes);
+		pb->buffer.write_ptr += fragment_size;
+
+		if (pb->buffer.write_ptr >= (pb->buffer.base + pb->buffer.size))
+			pb->buffer.write_ptr -= pb->buffer.size;
+	}
+}
+
+/**
+ * Start event based playback.
+ * @param pb Playback helper structure.
+ */
+static void play_fragment(playback_t *pb)
+{
+	assert(pb);
+	assert(pb->device);
+	const size_t fragment_size = pb->buffer.size / DEFAULT_FRAGMENTS;
+	printf("Registering event callback\n");
+	int ret = audio_pcm_register_event_callback(pb->device,
+	    device_event_callback, pb);
+	if (ret != EOK) {
+		printf("Failed to register event callback: %s.\n",
+		    str_error(ret));
+		return;
+	}
+	printf("Playing: %dHz, %s, %d channel(s).\n", pb->f.sampling_rate,
+	    pcm_sample_format_str(pb->f.sample_format), pb->f.channels);
+	const size_t bytes = fread(pb->buffer.base, sizeof(uint8_t),
+	    fragment_size, pb->source);
+	if (bytes != fragment_size)
+		memset(pb->buffer.base + bytes, 0, fragment_size - bytes);
+	printf("Initial: Copied from position %p size %zu/%zu\n",
+	    pb->buffer.base, bytes, fragment_size);
+	pb->buffer.write_ptr = pb->buffer.base + fragment_size;
+	fibril_mutex_lock(&pb->mutex);
+	const unsigned frames =
+	    pcm_format_size_to_frames(fragment_size, &pb->f);
+	ret = audio_pcm_start_playback_fragment(pb->device, frames,
+	    pb->f.channels, pb->f.sampling_rate, pb->f.sample_format);
+	if (ret != EOK) {
+		fibril_mutex_unlock(&pb->mutex);
+		printf("Failed to start playback: %s.\n", str_error(ret));
+		audio_pcm_unregister_event_callback(pb->device);
+		return;
+	}
+
+	for (pb->playing = true; pb->playing;
+	    fibril_condvar_wait(&pb->cv, &pb->mutex));
+
+	fibril_mutex_unlock(&pb->mutex);
+	printf("\n");
+	audio_pcm_unregister_event_callback(pb->device);
+}
+
+/**
+ * Count occupied space in a cyclic buffer.
+ * @param pb Playback helper structure.
+ * @param pos read pointer position.
+ * @return Occupied space size.
+ */
+static size_t buffer_occupied(const playback_t *pb, size_t pos)
+{
+	assert(pb);
+	void *read_ptr = pb->buffer.base + pos;
+	if (read_ptr > pb->buffer.write_ptr)
+		return pb->buffer.write_ptr + pb->buffer.size - read_ptr;
+	return pb->buffer.write_ptr - read_ptr;
+
+}
+
+/**
+ * Count available space in a cyclic buffer.
+ * @param pb Playback helper structure.
+ * @param pos read pointer position.
+ * @return Free space size.
+ */
+static size_t buffer_avail(const playback_t *pb, size_t pos)
+{
+	assert(pb);
+	void *read_ptr = pb->buffer.base + pos;
+	if (read_ptr <= pb->buffer.write_ptr)
+		return read_ptr + pb->buffer.size - pb->buffer.write_ptr - 1;
+	return (read_ptr - pb->buffer.write_ptr) - 1;
+}
+
+/**
+ * Size of the space between write pointer and the end of a cyclic buffer
+ * @param pb Playback helper structure.
+ */
+static size_t buffer_remain(const playback_t *pb)
+{
+	assert(pb);
+	return (pb->buffer.base + pb->buffer.size) - pb->buffer.write_ptr;
+}
+
+/**
+ * Move write pointer forward. Wrap around the end.
+ * @param pb Playback helper structure.
+ * @param bytes NUmber of bytes to advance.
+ */
+static void buffer_advance(playback_t *pb, size_t bytes)
+{
+	assert(pb);
+	pb->buffer.write_ptr += bytes;
+	while (pb->buffer.write_ptr >= (pb->buffer.base + pb->buffer.size))
+		pb->buffer.write_ptr -= pb->buffer.size;
+}
+
+#define DPRINTF(f, ...) \
+	printf("%.2lu:%.6lu   "f, time.tv_sec % 100, time.tv_usec, __VA_ARGS__)
+
+/**
+ * Start playback using buffer position api.
+ * @param pb Playback helper function.
+ */
+static void play(playback_t *pb)
+{
+	assert(pb);
+	assert(pb->device);
+	pb->buffer.write_ptr = pb->buffer.base;
+	printf("Playing: %dHz, %s, %d channel(s).\n", pb->f.sampling_rate,
+	    pcm_sample_format_str(pb->f.sample_format), pb->f.channels);
+	useconds_t work_time = 50000; /* 50 ms */
+	bool started = false;
+	size_t pos = 0;
+	struct timeval time = { 0 };
+	getuptime(&time);
+	do {
+		size_t available = buffer_avail(pb, pos);
+		/* Writing might need wrap around the end,
+		 * read directly to device buffer */
+		size_t bytes = fread(pb->buffer.write_ptr, sizeof(uint8_t),
+		    min(available, buffer_remain(pb)), pb->source);
+		buffer_advance(pb, bytes);
+		DPRINTF("POS %zu: %zu bytes free in buffer, read %zu, wp %zu\n",
+		    pos, available, bytes,
+		    pb->buffer.write_ptr - pb->buffer.base);
+		available -= bytes;
+
+		/* continue if we wrapped around the end */
+		if (available) {
+			bytes = fread(pb->buffer.write_ptr,
+			    sizeof(uint8_t), min(available, buffer_remain(pb)),
+			    pb->source);
+			buffer_advance(pb, bytes);
+			DPRINTF("POS %zu: %zu bytes still free in buffer, "
+			    "read %zu, wp %zu\n", pos, available, bytes,
+			    pb->buffer.write_ptr - pb->buffer.base);
+			available -= bytes;
+		}
+
+		if (!started) {
+			int ret = audio_pcm_start_playback(pb->device,
+			    pb->f.channels, pb->f.sampling_rate,
+			    pb->f.sample_format);
+			if (ret != EOK) {
+				printf("Failed to start playback: %s\n",
+				    str_error(ret));
+				return;
+			}
+			started = true;
+			ret = audio_pcm_get_buffer_pos(pb->device, &pos);
+			if (ret != EOK) {
+				printf("Failed to update position indicator "
+				   "%s\n", str_error(ret));
+			}
+		}
+		const size_t to_play = buffer_occupied(pb, pos);
+		const useconds_t usecs =
+		    pcm_format_size_to_usec(to_play, &pb->f);
+
+		/* Compute delay time */
+		const useconds_t real_delay = (usecs > work_time)
+		    ? usecs - work_time : 0;
+		DPRINTF("POS %zu: %u usecs (%u) to play %zu bytes.\n",
+		    pos, usecs, real_delay, to_play);
+		if (real_delay)
+			async_usleep(real_delay);
+		/* update buffer position */
+		const int ret = audio_pcm_get_buffer_pos(pb->device, &pos);
+		if (ret != EOK) {
+			printf("Failed to update position indicator %s\n",
+			    str_error(ret));
+		}
+		getuptime(&time);
+
+		/* we did not use all the space we had,
+		 * that is the end */
+		if (available)
+			break;
+
+	} while (1);
+	audio_pcm_stop_playback_immediate(pb->device);
+}
+
+/**
+ * Play audio file usign direct device access.
+ * @param device The device.
+ * @param file The file.
+ * @return Error code.
+ */
+int dplay(const char *device, const char *file)
+{
+	int ret = EOK;
+	audio_pcm_sess_t *session = NULL;
+	if (str_cmp(device, "default") == 0) {
+		session = audio_pcm_open_default();
+	} else {
+		session = audio_pcm_open(device);
+	}
+	if (!session) {
+		printf("Failed to connect to device %s.\n", device);
+		return 1;
+	}
+	printf("Playing on device: %s.\n", device);
+	if (audio_pcm_query_cap(session, AUDIO_CAP_PLAYBACK) <= 0) {
+		printf("Device %s does not support playback\n", device);
+		ret = ENOTSUP;
+		goto close_session;
+	}
+
+	const char* info = NULL;
+	ret = audio_pcm_get_info_str(session, &info);
+	if (ret != EOK) {
+		printf("Failed to get PCM info: %s.\n", str_error(ret));
+		goto close_session;
+	}
+	printf("Playing on %s.\n", info);
+	free(info);
+
+	playback_t pb;
+	playback_initialize(&pb, session);
+
+	ret = audio_pcm_get_buffer(pb.device, &pb.buffer.base, &pb.buffer.size);
+	if (ret != EOK) {
+		printf("Failed to get PCM buffer: %s.\n", str_error(ret));
+		goto close_session;
+	}
+	printf("Buffer: %p %zu.\n", pb.buffer.base, pb.buffer.size);
+
+	pb.source = fopen(file, "rb");
+	if (pb.source == NULL) {
+		ret = ENOENT;
+		printf("Failed to open file: %s.\n", file);
+		goto cleanup;
+	}
+
+	wave_header_t header;
+	fread(&header, sizeof(header), 1, pb.source);
+	const char *error;
+	ret = wav_parse_header(&header, NULL, NULL,
+	    &pb.f.channels, &pb.f.sampling_rate, &pb.f.sample_format, &error);
+	if (ret != EOK) {
+		printf("Error parsing wav header: %s.\n", error);
+		goto cleanup;
+	}
+	if (audio_pcm_query_cap(pb.device, AUDIO_CAP_BUFFER_POS) > 0) {
+		play(&pb);
+	} else {
+		if (audio_pcm_query_cap(pb.device, AUDIO_CAP_INTERRUPT) > 0)
+			play_fragment(&pb);
+		else
+			printf("Neither playing method is supported");
+	}
+
+cleanup:
+	fclose(pb.source);
+	munmap(pb.buffer.base, pb.buffer.size);
+	audio_pcm_release_buffer(pb.device);
+close_session:
+	audio_pcm_close(session);
+	return ret == EOK ? 0 : 1;
+}
+/**
+ * @}
+ */
Index: uspace/app/wavplay/dplay.h
===================================================================
--- uspace/app/wavplay/dplay.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/wavplay/dplay.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 wavplay
+ * @{
+ */
+/** @file
+ * @brief .wav file format.
+ */
+#ifndef DPLAY_H
+#define DPLAY_H
+
+int dplay(const char *device, const char *file);
+
+#endif
+/**
+ * @}
+ */
+
Index: uspace/app/wavplay/drec.c
===================================================================
--- uspace/app/wavplay/drec.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/wavplay/drec.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2013 Jan Vesely
+ * 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 wavplay
+ * @{
+ */
+/**
+ * @file PCM playback audio devices
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <str_error.h>
+#include <audio_pcm_iface.h>
+#include <pcm/format.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <inttypes.h>
+
+#include "wave.h"
+#include "drec.h"
+
+
+#define BUFFER_PARTS   2
+
+/** Recording format */
+static const pcm_format_t format = {
+	.sampling_rate = 44100,
+	.channels = 2,
+	.sample_format = PCM_SAMPLE_SINT16_LE,
+};
+
+/** Recording helper structure */
+typedef struct {
+	struct {
+		void *base;
+		size_t size;
+		unsigned id;
+		void* position;
+	} buffer;
+	FILE* file;
+	audio_pcm_sess_t *device;
+} record_t;
+
+/**
+ * Initialize recording helper structure.
+ * @param rec Recording structure.
+ * @param sess Session to IPC device.
+ */
+static void record_initialize(record_t *rec, audio_pcm_sess_t *sess)
+{
+	assert(sess);
+	assert(rec);
+	rec->buffer.base = NULL;
+	rec->buffer.size = 0;
+	rec->buffer.position = NULL;
+	rec->file = NULL;
+	rec->device = sess;
+}
+
+/**
+ * Recording callback. Writes recorded data.
+ * @param iid IPC call id.
+ * @param icall Poitner to IPC call structure.
+ * @param arg Argument. Poitner to recording helper structure.
+ */
+static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void* arg)
+{
+	async_answer_0(iid, EOK);
+	record_t *rec = arg;
+	const size_t buffer_part = rec->buffer.size / BUFFER_PARTS;
+	bool record = true;
+	while (record) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		switch(IPC_GET_IMETHOD(call)) {
+		case PCM_EVENT_CAPTURE_TERMINATED:
+			printf("Recording terminated\n");
+			record = false;
+		case PCM_EVENT_FRAMES_CAPTURED:
+			printf("%" PRIun " frames\n", IPC_GET_ARG1(call));
+			async_answer_0(callid, EOK);
+			break;
+		default:
+			printf("Unknown event %" PRIun ".\n", IPC_GET_IMETHOD(call));
+			async_answer_0(callid, ENOTSUP);
+			continue;
+
+		}
+
+		/* Write directly from device buffer to file */
+		const size_t bytes = fwrite(rec->buffer.position,
+		   sizeof(uint8_t), buffer_part, rec->file);
+		printf("%zu ", bytes);
+		rec->buffer.position += buffer_part;
+
+		if (rec->buffer.position >= (rec->buffer.base + rec->buffer.size))
+			rec->buffer.position = rec->buffer.base;
+		async_answer_0(callid, EOK);
+	}
+}
+
+/**
+ * Start fragment based recording.
+ * @param rec Recording helper structure.
+ * @param f PCM format
+ */
+static void record_fragment(record_t *rec, pcm_format_t f)
+{
+	assert(rec);
+	assert(rec->device);
+	int ret = audio_pcm_register_event_callback(rec->device,
+	    device_event_callback, rec);
+	if (ret != EOK) {
+		printf("Failed to register for events: %s.\n", str_error(ret));
+		return;
+	}
+	rec->buffer.position = rec->buffer.base;
+	printf("Recording: %dHz, %s, %d channel(s).\n", f.sampling_rate,
+	    pcm_sample_format_str(f.sample_format), f.channels);
+	const unsigned frames =
+		pcm_format_size_to_frames(rec->buffer.size / BUFFER_PARTS, &f);
+	ret = audio_pcm_start_capture_fragment(rec->device,
+	    frames, f.channels, f.sampling_rate, f.sample_format);
+	if (ret != EOK) {
+		printf("Failed to start recording: %s.\n", str_error(ret));
+		return;
+	}
+
+	getchar();
+	printf("\n");
+	audio_pcm_stop_capture(rec->device);
+}
+
+/**
+ * Record directly from a device to a file.
+ * @param device The device.
+ * @param file The file.
+ * @return Error code.
+ */
+int drecord(const char *device, const char *file)
+{
+	int ret = EOK;
+	audio_pcm_sess_t *session = NULL;
+	if (str_cmp(device, "default") == 0) {
+		session = audio_pcm_open_default();
+	} else {
+		session = audio_pcm_open(device);
+	}
+	if (!session) {
+		printf("Failed to connect to device %s.\n", device);
+		return 1;
+	}
+	printf("Recording on device: %s.\n", device);
+	if (audio_pcm_query_cap(session, AUDIO_CAP_CAPTURE) <= 0) {
+		printf("Device %s does not support recording\n", device);
+		ret = ENOTSUP;
+		goto close_session;
+	}
+
+	const char* info = NULL;
+	ret = audio_pcm_get_info_str(session, &info);
+	if (ret != EOK) {
+		printf("Failed to get PCM info.\n");
+		goto close_session;
+	}
+	printf("Capturing on %s.\n", info);
+	free(info);
+
+	record_t rec;
+	record_initialize(&rec, session);
+
+	ret = audio_pcm_get_buffer(rec.device, &rec.buffer.base,
+	    &rec.buffer.size);
+	if (ret != EOK) {
+		printf("Failed to get PCM buffer: %s.\n", str_error(ret));
+		goto close_session;
+	}
+	printf("Buffer: %p %zu.\n", rec.buffer.base, rec.buffer.size);
+
+	rec.file = fopen(file, "w");
+	if (rec.file == NULL) {
+		ret = ENOENT;
+		printf("Failed to open file: %s.\n", file);
+		goto cleanup;
+	}
+
+	wave_header_t header;
+	fseek(rec.file, sizeof(header), SEEK_SET);
+	if (ret != EOK) {
+		printf("Error parsing wav header\n");
+		goto cleanup;
+	}
+	if (audio_pcm_query_cap(rec.device, AUDIO_CAP_INTERRUPT) > 0)
+		record_fragment(&rec, format);
+	else
+		printf("Recording method is not supported");
+	//TODO consider buffer position interface
+
+	wav_init_header(&header, format, ftell(rec.file) - sizeof(header));
+	fseek(rec.file, 0, SEEK_SET);
+	fwrite(&header, sizeof(header), 1, rec.file);
+
+cleanup:
+	fclose(rec.file);
+	munmap(rec.buffer.base, rec.buffer.size);
+	audio_pcm_release_buffer(rec.device);
+close_session:
+	audio_pcm_close(session);
+	return ret == EOK ? 0 : 1;
+}
+/**
+ * @}
+ */
Index: uspace/app/wavplay/drec.h
===================================================================
--- uspace/app/wavplay/drec.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/wavplay/drec.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013 Jan Vesely
+ * 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 wavplay
+ * @{
+ */
+/** @file
+ * @brief .wav file format.
+ */
+#ifndef DREC_H
+#define DREC_H
+
+int drecord(const char *device, const char *file);
+
+#endif
+/**
+ * @}
+ */
+
Index: uspace/app/wavplay/main.c
===================================================================
--- uspace/app/wavplay/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/wavplay/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 wavplay
+ * @{
+ */
+/**
+ * @file PCM playback audio devices
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <malloc.h>
+#include <str_error.h>
+#include <stdio.h>
+#include <hound/client.h>
+#include <pcm/sample_format.h>
+#include <getopt.h>
+
+#include "dplay.h"
+#include "drec.h"
+#include "wave.h"
+
+#define READ_SIZE   (32 * 1024)
+#define STREAM_BUFFER_SIZE   (64 * 1024)
+
+/**
+ * Play audio file using a new stream on provided context.
+ * @param ctx Provided context.
+ * @param filename File to play.
+ * @return Error code.
+ */
+static int hplay_ctx(hound_context_t *ctx, const char *filename)
+{
+	printf("Hound context playback: %s\n", filename);
+	FILE *source = fopen(filename, "rb");
+	if (!source) {
+		printf("Failed to open file %s\n", filename);
+		return EINVAL;
+	}
+
+	/* Read and parse WAV header */
+	wave_header_t header;
+	size_t read = fread(&header, sizeof(header), 1, source);
+	if (read != 1) {
+		printf("Failed to read WAV header: %zu\n", read);
+		fclose(source);
+		return EIO;
+	}
+	pcm_format_t format;
+	const char *error;
+	int ret = wav_parse_header(&header, NULL, NULL, &format.channels,
+	    &format.sampling_rate, &format.sample_format, &error);
+	if (ret != EOK) {
+		printf("Error parsing `%s' wav header: %s.\n", filename, error);
+		fclose(source);
+		return EINVAL;
+	}
+
+	printf("File `%s' format: %u channel(s), %uHz, %s.\n", filename,
+	    format.channels, format.sampling_rate,
+	    pcm_sample_format_str(format.sample_format));
+
+	/* Allocate buffer and create new context */
+	char * buffer = malloc(READ_SIZE);
+	if (!buffer) {
+		fclose(source);
+		return ENOMEM;
+	}
+	hound_stream_t *stream = hound_stream_create(ctx,
+	    HOUND_STREAM_DRAIN_ON_EXIT, format, STREAM_BUFFER_SIZE);
+
+	/* Read and play */
+	while ((read = fread(buffer, sizeof(char), READ_SIZE, source)) > 0) {
+		ret = hound_stream_write(stream, buffer, read);
+		if (ret != EOK) {
+			printf("Failed to write to hound stream: %s\n",
+			    str_error(ret));
+			break;
+		}
+	}
+
+	/* Cleanup */
+	free(buffer);
+	fclose(source);
+	return ret;
+}
+
+/**
+ * Play audio file via hound server.
+ * @param filename File to play.
+ * @return Error code
+ */
+static int hplay(const char *filename)
+{
+	printf("Hound playback: %s\n", filename);
+	FILE *source = fopen(filename, "rb");
+	if (!source) {
+		printf("Failed to open file %s\n", filename);
+		return EINVAL;
+	}
+
+	/* Read and parse WAV header */
+	wave_header_t header;
+	size_t read = fread(&header, sizeof(header), 1, source);
+	if (read != 1) {
+		printf("Failed to read WAV header: %zu\n", read);
+		fclose(source);
+		return EIO;
+	}
+	pcm_format_t format;
+	const char *error;
+	int ret = wav_parse_header(&header, NULL, NULL, &format.channels,
+	    &format.sampling_rate, &format.sample_format, &error);
+	if (ret != EOK) {
+		printf("Error parsing `%s' wav header: %s.\n", filename, error);
+		fclose(source);
+		return EINVAL;
+	}
+	printf("File `%s' format: %u channel(s), %uHz, %s.\n", filename,
+	    format.channels, format.sampling_rate,
+	    pcm_sample_format_str(format.sample_format));
+
+	/* Connect new playback context */
+	hound_context_t *hound = hound_context_create_playback(filename,
+	    format, STREAM_BUFFER_SIZE);
+	if (!hound) {
+		printf("Failed to create HOUND context\n");
+		fclose(source);
+		return ENOMEM;
+	}
+
+	ret = hound_context_connect_target(hound, HOUND_DEFAULT_TARGET);
+	if (ret != EOK) {
+		printf("Failed to connect to default target: %s\n",
+		    str_error(ret));
+		hound_context_destroy(hound);
+		fclose(source);
+		return ret;
+	}
+
+	/* Read and play */
+	static char buffer[READ_SIZE];
+	while ((read = fread(buffer, sizeof(char), READ_SIZE, source)) > 0) {
+		ret = hound_write_main_stream(hound, buffer, read);
+		if (ret != EOK) {
+			printf("Failed to write to main context stream: %s\n",
+			    str_error(ret));
+			break;
+		}
+	}
+
+	/* Cleanup */
+	hound_context_destroy(hound);
+	fclose(source);
+	return ret;
+}
+
+/**
+ * Helper structure for playback in separate fibrils
+ */
+typedef struct {
+	hound_context_t *ctx;
+	atomic_t *count;
+	const char *file;
+} fib_play_t;
+
+/**
+ * Fibril playback wrapper.
+ * @param arg Argument, pointer to playback helper structure.
+ * @return Error code.
+ */
+static int play_wrapper(void *arg)
+{
+	assert(arg);
+	fib_play_t *p = arg;
+	const int ret = hplay_ctx(p->ctx, p->file);
+	atomic_dec(p->count);
+	free(arg);
+	return ret;
+}
+
+/**
+ * Array of supported commandline options
+ */
+static const struct option opts[] = {
+	{"device", required_argument, 0, 'd'},
+	{"parallel", no_argument, 0, 'p'},
+	{"record", no_argument, 0, 'r'},
+	{"help", no_argument, 0, 'h'},
+	{0, 0, 0, 0}
+};
+
+/**
+ * Print usage help.
+ * @param name Name of the program.
+ */
+static void print_help(const char* name)
+{
+	printf("Usage: %s [options] file [files...]\n", name);
+	printf("supported options:\n");
+	printf("\t -h, --help\t Print this help.\n");
+	printf("\t -r, --record\t Start recording instead of playback. "
+	    "(Not implemented)\n");
+	printf("\t -d, --device\t Use specified device instead of the sound "
+	    "service. Use location path or a special device `default'\n");
+	printf("\t -p, --parallel\t Play given files in parallel instead of "
+	    "sequentially (does not work with -d).\n");
+}
+
+int main(int argc, char *argv[])
+{
+	const char *device = "default";
+	int idx = 0;
+	bool direct = false, record = false, parallel = false;
+	optind = 0;
+	int ret = 0;
+
+	/* Parse command line options */
+	while (ret != -1) {
+		ret = getopt_long(argc, argv, "d:prh", opts, &idx);
+		switch (ret) {
+		case 'd':
+			direct = true;
+			device = optarg;
+			break;
+		case 'r':
+			record = true;
+			break;
+		case 'p':
+			parallel = true;
+			break;
+		case 'h':
+			print_help(*argv);
+			return 0;
+		};
+	}
+
+	if (parallel && direct) {
+		printf("Parallel playback is available only if using sound "
+		    "server (no -d)\n");
+		print_help(*argv);
+		return 1;
+	}
+
+	if (optind == argc) {
+		printf("Not enough arguments.\n");
+		print_help(*argv);
+		return 1;
+	}
+
+	/* Init parallel playback variables */
+	hound_context_t *hound_ctx = NULL;
+	atomic_t playcount;
+	atomic_set(&playcount, 0);
+
+	/* Init parallel playback context if necessary */
+	if (parallel) {
+		hound_ctx = hound_context_create_playback("wavplay",
+		    AUDIO_FORMAT_DEFAULT, STREAM_BUFFER_SIZE);
+		if (!hound_ctx) {
+			printf("Failed to create global hound context\n");
+			return 1;
+		}
+		const int ret = hound_context_connect_target(hound_ctx,
+		    HOUND_DEFAULT_TARGET);
+		if (ret != EOK) {
+			printf("Failed to connect hound context to default "
+			   "target.\n");
+			hound_context_destroy(hound_ctx);
+			return 1;
+		}
+	}
+
+	/* play or record all files */
+	for (int i = optind; i < argc; ++i) {
+		const char *file = argv[i];
+
+		printf("%s (%d/%d) %s\n", record ? "Recording" : "Playing",
+		    i - optind + 1, argc - optind, file);
+		if (record) {
+			if (direct) {
+				drecord(device, file);
+			} else {
+				printf("Indirect recording is not supported "
+				    "yet.\n");
+				break;
+			}
+		}
+
+		if (direct) {
+			dplay(device, file);
+		} else {
+			if (parallel) {
+				/* Start new fibril for parallel playback */
+				fib_play_t *data = malloc(sizeof(fib_play_t));
+				if (!data) {
+					printf("Playback of %s failed.\n",
+						file);
+					continue;
+				}
+				data->file = file;
+				data->count = &playcount;
+				data->ctx = hound_ctx;
+				fid_t fid = fibril_create(play_wrapper, data);
+				atomic_inc(&playcount);
+				fibril_add_ready(fid);
+			} else {
+				hplay(file);
+			}
+		}
+	}
+
+	/* Wait for all fibrils to finish */
+	while (atomic_get(&playcount) > 0)
+		async_usleep(1000000);
+
+	/* Destroy parallel playback context, if initialized */
+	if (hound_ctx)
+		hound_context_destroy(hound_ctx);
+	return 0;
+}
+/**
+ * @}
+ */
Index: uspace/app/wavplay/wave.c
===================================================================
--- uspace/app/wavplay/wave.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/wavplay/wave.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 dplay
+ * @{
+ */
+/**
+ * @file PCM playback audio devices
+ */
+
+#include <byteorder.h>
+#include <str.h>
+#include <errno.h>
+
+#include "wave.h"
+
+/**
+ * Parse wav header data.
+ * @param[in] hdata Header data to parse.
+ * @param[out] data Pointer to audio data.
+ * @param[out] data_size Size of the data after the header.
+ * @param[out] channels Number of channels in stored audio format.
+ * @param[out] sampling_rate Sampling rate of the store data.
+ * @param[out] format Sample format.
+ * @param[out] error String representatoin of error, if any.
+ * @return Error code.
+ *
+ * Does sanity checks and endian conversion.
+ */
+int wav_parse_header(const void *hdata, const void **data, size_t *data_size,
+    unsigned *channels, unsigned *sampling_rate, pcm_sample_format_t *format,
+    const char **error)
+{
+	if (!hdata) {
+		if (error)
+			*error = "no header";
+		return EINVAL;
+	}
+
+	const wave_header_t *header = hdata;
+	if (str_lcmp(header->chunk_id, CHUNK_ID, 4) != 0) {
+		if (error)
+			*error = "invalid chunk id";
+		return EINVAL;
+	}
+
+	if (str_lcmp(header->format, FORMAT_STR, 4) != 0) {
+		if (error)
+			*error = "invalid format string";
+		return EINVAL;
+	}
+
+	if (str_lcmp(header->subchunk1_id, SUBCHUNK1_ID, 4) != 0) {
+		if (error)
+			*error = "invalid subchunk1 id";
+		return EINVAL;
+	}
+
+	if (uint32_t_le2host(header->subchunk1_size) != PCM_SUBCHUNK1_SIZE) {
+		//TODO subchunk 1 sizes other than 16 are allowed ( 18, 40)
+		//http://www-mmsp.ece.mcgill.ca/documents/AudioFormats/WAVE/WAVE.html
+		if (error)
+			*error = "invalid subchunk1 size";
+//		return EINVAL;
+	}
+
+	if (uint16_t_le2host(header->audio_format) != FORMAT_LINEAR_PCM) {
+		if (error)
+			*error = "unknown format";
+		return ENOTSUP;
+	}
+
+	if (str_lcmp(header->subchunk2_id, SUBCHUNK2_ID, 4) != 0) {
+		//TODO basedd on subchunk1 size, we might be reading wrong
+		//offset
+		if (error)
+			*error = "invalid subchunk2 id";
+//		return EINVAL;
+	}
+
+
+	//TODO data and data_size are incorrect in extended wav formats
+	//pcm params are OK
+	if (data)
+		*data = header->data;
+	if (data_size)
+		*data_size = uint32_t_le2host(header->subchunk2_size);
+
+	if (sampling_rate)
+		*sampling_rate = uint32_t_le2host(header->sampling_rate);
+	if (channels)
+		*channels = uint16_t_le2host(header->channels);
+	if (format) {
+		const unsigned size = uint32_t_le2host(header->sample_size);
+		switch (size) {
+		case 8: *format = PCM_SAMPLE_UINT8; break;
+		case 16: *format = PCM_SAMPLE_SINT16_LE; break;
+		case 24: *format = PCM_SAMPLE_SINT24_LE; break;
+		case 32: *format = PCM_SAMPLE_SINT32_LE; break;
+		default:
+			*error = "Unknown format";
+			return ENOTSUP;
+		}
+	}
+	if (error)
+		*error = "no error";
+
+	return EOK;
+}
+
+/**
+ * Initialize wave fromat ehader structure.
+ * @param header Structure to initialize.
+ * @param format Desired PCM format
+ * @param size Size of the stored data.
+ *
+ * Initializes format specific elements and covnerts endian
+ */
+void wav_init_header(wave_header_t *header, pcm_format_t format, size_t size)
+{
+	assert(header);
+#define COPY_STR(dst, src)   memcpy(dst, src, str_size(src))
+
+	COPY_STR(&header->chunk_id, CHUNK_ID);
+	COPY_STR(&header->format, FORMAT_STR);
+	COPY_STR(&header->subchunk1_id, SUBCHUNK1_ID);
+	header->subchunk1_size = host2uint16_t_le(PCM_SUBCHUNK1_SIZE);
+	header->audio_format = host2uint16_t_le(FORMAT_LINEAR_PCM);
+
+	COPY_STR(&header->subchunk2_id, SUBCHUNK2_ID);
+	header->subchunk2_size = host2uint32_t_le(size);
+	header->sampling_rate = host2uint32_t_le(format.sampling_rate);
+	header->channels = host2uint32_t_le(format.channels);
+	header->sample_size =
+	    host2uint32_t_le(pcm_sample_format_size(format.sample_format));
+}
+/**
+ * @}
+ */
Index: uspace/app/wavplay/wave.h
===================================================================
--- uspace/app/wavplay/wave.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/app/wavplay/wave.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 dplay
+ * @{
+ */
+/** @file
+ * @brief .wav file format.
+ */
+#ifndef WAVE_H
+#define WAVE_H
+
+#include <stdint.h>
+#include <pcm/format.h>
+#include <pcm/sample_format.h>
+
+/** Wave file header format.
+ *
+ * https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
+ * @note: 8-bit samples are stored as unsigned bytes,
+ * 16-bit samples are stored as signed integers.
+ * @note: The default byte ordering assumed for WAVE data files is
+ * little-endian. Files written using the big-endian byte ordering scheme have
+ * the identifier RIFX instead of RIFF.
+ */
+typedef struct wave_header {
+	/** Should be 'R', 'I', 'F', 'F'. */
+	char chunk_id[4];
+#define CHUNK_ID "RIFF"
+
+	/** Total size minus the first 8 bytes */
+	uint32_t chunk_size;
+	/** Should be 'W', 'A', 'V', 'E'. */
+	char format[4];
+#define FORMAT_STR "WAVE"
+
+	/** Should be 'f', 'm', 't', ' '. */
+	char subchunk1_id[4];
+#define SUBCHUNK1_ID "fmt "
+
+	/** Size of the ret of this subchunk. 16 for PCM file. */
+	uint32_t subchunk1_size;
+#define PCM_SUBCHUNK1_SIZE 16
+	/** Format. 1 for Linear PCM */
+	uint16_t audio_format;
+#define FORMAT_LINEAR_PCM 1
+	/** Number of channels. */
+	uint16_t channels;
+	/** Sampling rate. */
+	uint32_t sampling_rate;
+	/** Byte rate. */
+	uint32_t byte_rate;
+	/** Block align. Bytes in one block (samples for all channels). */
+	uint16_t block_align;
+	/** Bits per sample (one channel). */
+	uint16_t sample_size;
+
+	/** Should be 'd', 'a', 't', 'a'. */
+	char subchunk2_id[4];
+#define SUBCHUNK2_ID "data"
+	/** Audio data size. */
+	uint32_t subchunk2_size;
+	/** Audio data. */
+	uint8_t data[];
+
+} wave_header_t;
+
+int wav_parse_header(const void *, const void**, size_t *, unsigned *,
+    unsigned *, pcm_sample_format_t *, const char **);
+
+void wav_init_header(wave_header_t *, pcm_format_t , size_t);
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/Makefile
===================================================================
--- uspace/drv/audio/sb16/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,47 @@
+#
+# Copyright (c) 2011 Jan Vesely
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+
+LIBS = \
+	$(LIBDRV_PREFIX)/libdrv.a
+
+EXTRA_CFLAGS += \
+	-I$(LIBDRV_PREFIX)/include -I$(LIBPCM_PREFIX)/include
+
+BINARY = sb16
+
+SOURCES = \
+	dsp.c \
+	main.c \
+	mixer.c \
+	mixer_iface.c \
+	pcm_iface.c \
+	sb16.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/audio/sb16/ddf_log.h
===================================================================
--- uspace/drv/audio/sb16/ddf_log.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/ddf_log.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief DDF log helper macros
+ */
+#ifndef DRV_AUDIO_SB16_DDF_LOG_H
+#define DRV_AUDIO_SB16_DDF_LOG_H
+
+#include <ddf/log.h>
+
+#define ddf_log_fatal(msg...) ddf_msg(LVL_FATAL, msg)
+#define ddf_log_error(msg...) ddf_msg(LVL_ERROR, msg)
+#define ddf_log_warning(msg...) ddf_msg(LVL_WARN, msg)
+#define ddf_log_note(msg...) ddf_msg(LVL_NOTE, msg)
+#define ddf_log_debug(msg...) ddf_msg(LVL_DEBUG, msg)
+#define ddf_log_verbose(msg...) ddf_msg(LVL_DEBUG2, msg)
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/dsp.c
===================================================================
--- uspace/drv/audio/sb16/dsp.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/dsp.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,559 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief DSP helper functions implementation
+ */
+
+#include <as.h>
+#include <stdbool.h>
+#include <ddi.h>
+#include <devman.h>
+#include <device/hw_res.h>
+#include <libarch/ddi.h>
+#include <libarch/barrier.h>
+#include <macros.h>
+#include <str_error.h>
+#include <audio_pcm_iface.h>
+
+#include "ddf_log.h"
+#include "dsp_commands.h"
+#include "dsp.h"
+
+/* Maximum allowed transfer size for ISA DMA transfers is 64kB */
+#define MAX_BUFFER_SIZE (64 * 1024)
+
+#ifndef DSP_RETRY_COUNT
+#define DSP_RETRY_COUNT 100
+#endif
+
+#define DSP_RESET_RESPONSE 0xaa
+
+/* These are only for SB16 (DSP4.00+) */
+#define DSP_RATE_UPPER_LIMIT 44100
+#define DSP_RATE_LOWER_LIMIT 5000
+
+#define AUTO_DMA_MODE
+
+static inline const char * dsp_state_to_str(dsp_state_t state)
+{
+	static const char* state_names[] = {
+		[DSP_PLAYBACK_ACTIVE_EVENTS] = "PLAYBACK w/ EVENTS",
+		[DSP_CAPTURE_ACTIVE_EVENTS] = "CAPTURE w/ EVENTS",
+		[DSP_PLAYBACK_NOEVENTS] = "PLAYBACK w/o EVENTS",
+		[DSP_CAPTURE_NOEVENTS] = "CAPTURE w/o EVENTS",
+		[DSP_PLAYBACK_TERMINATE] = "PLAYBACK TERMINATE",
+		[DSP_CAPTURE_TERMINATE] = "CAPTURE TERMINATE",
+		[DSP_READY] = "READY",
+		[DSP_NO_BUFFER] = "NO BUFFER",
+	};
+	if (state < ARRAY_SIZE(state_names))
+		return state_names[state];
+	return "UNKNOWN";
+}
+
+
+static inline void dsp_change_state(sb_dsp_t *dsp, dsp_state_t state)
+{
+	assert(dsp);
+	ddf_log_verbose("Changing state from %s to %s",
+	    dsp_state_to_str(dsp->state), dsp_state_to_str(state));
+	dsp->state = state;
+}
+
+static inline int dsp_read(sb_dsp_t *dsp, uint8_t *data)
+{
+	assert(data);
+	assert(dsp);
+	uint8_t status;
+	size_t attempts = DSP_RETRY_COUNT;
+	do {
+		status = pio_read_8(&dsp->regs->dsp_read_status);
+	} while (--attempts && ((status & DSP_READ_READY) == 0));
+
+	if ((status & DSP_READ_READY) == 0)
+		return EIO;
+
+	*data = pio_read_8(&dsp->regs->dsp_data_read);
+	return EOK;
+}
+
+static inline int dsp_write(sb_dsp_t *dsp, uint8_t data)
+{
+	assert(dsp);
+	uint8_t status;
+	size_t attempts = DSP_RETRY_COUNT;
+	do {
+		status = pio_read_8(&dsp->regs->dsp_write);
+	} while (--attempts && ((status & DSP_WRITE_BUSY) != 0));
+
+	if ((status & DSP_WRITE_BUSY))
+		return EIO;
+
+	pio_write_8(&dsp->regs->dsp_write, data);
+	return EOK;
+}
+
+static inline void dsp_reset(sb_dsp_t *dsp)
+{
+	assert(dsp);
+	/* Reset DSP, see Chapter 2 of Sound Blaster HW programming guide */
+	pio_write_8(&dsp->regs->dsp_reset, 1);
+	udelay(3); /* Keep reset for 3 us */
+	pio_write_8(&dsp->regs->dsp_reset, 0);
+	/* "DSP takes about 100 microseconds to initialize itself" */
+	udelay(100);
+}
+
+static inline void dsp_start_current_active(sb_dsp_t *dsp, uint8_t command)
+{
+	dsp_write(dsp, command);
+	dsp_write(dsp, dsp->active.mode);
+	dsp_write(dsp, (dsp->active.samples - 1) & 0xff);
+	dsp_write(dsp, (dsp->active.samples - 1) >> 8);
+}
+
+static inline void dsp_set_sampling_rate(sb_dsp_t *dsp, unsigned rate)
+{
+	dsp_write(dsp, SET_SAMPLING_RATE_OUTPUT);
+	dsp_write(dsp, rate >> 8);
+	dsp_write(dsp, rate & 0xff);
+	ddf_log_verbose("Sampling rate: %hhx:%hhx.", rate >> 8, rate & 0xff);
+}
+
+static inline void dsp_report_event(sb_dsp_t *dsp, pcm_event_t event)
+{
+	assert(dsp);
+	if (!dsp->event_exchange)
+		ddf_log_warning("No one listening for event %u", event);
+	async_msg_1(dsp->event_exchange, event, dsp->active.frame_count);
+}
+
+static inline int setup_dma(sb_dsp_t *dsp, uintptr_t pa, size_t size)
+{
+	async_sess_t *sess = devman_parent_device_connect(EXCHANGE_ATOMIC,
+	    ddf_dev_get_handle(dsp->sb_dev), IPC_FLAG_BLOCKING);
+
+	const int ret = hw_res_dma_channel_setup(sess,
+	    dsp->dma16_channel, pa, size,
+	    DMA_MODE_READ | DMA_MODE_AUTO | DMA_MODE_ON_DEMAND);
+
+	async_hangup(sess);
+	return ret;
+}
+
+static inline int setup_buffer(sb_dsp_t *dsp, size_t size)
+{
+	assert(dsp);
+	
+	if ((size > MAX_BUFFER_SIZE) || (size == 0) || ((size % 2) == 1))
+		size = MAX_BUFFER_SIZE;
+	
+	uintptr_t pa = 0;
+	void *buffer = NULL;
+	
+	int ret = dmamem_map_anonymous(size, DMAMEM_16MiB | 0x0000ffff,
+	    AS_AREA_WRITE | AS_AREA_READ, 0, &pa, &buffer);
+	if (ret != EOK) {
+		ddf_log_error("Failed to allocate DMA buffer.");
+		return ENOMEM;
+	}
+	
+	ddf_log_verbose("Setup DMA buffer at %p (%zu) %zu.", buffer, pa, size);
+	assert(pa < (1 << 24));
+	
+	/* Setup 16 bit channel */
+	ret = setup_dma(dsp, pa, size);
+	if (ret == EOK) {
+		dsp->buffer.data = buffer;
+		dsp->buffer.size = size;
+	} else {
+		ddf_log_error("Failed to setup DMA16 channel: %s.",
+		    str_error(ret));
+		dmamem_unmap_anonymous(buffer);
+	}
+	
+	return ret;
+}
+
+static inline size_t sample_count(pcm_sample_format_t format, size_t byte_count)
+{
+	return byte_count / pcm_sample_format_size(format);
+}
+
+int sb_dsp_init(sb_dsp_t *dsp, sb16_regs_t *regs, ddf_dev_t *dev,
+    int dma8, int dma16)
+{
+	assert(dsp);
+	dsp->regs = regs;
+	dsp->dma8_channel = dma8;
+	dsp->dma16_channel = dma16;
+	dsp->event_session = NULL;
+	dsp->event_exchange = NULL;
+	dsp->sb_dev = dev;
+	dsp->state = DSP_NO_BUFFER;
+	dsp_reset(dsp);
+	uint8_t response;
+	const int ret = dsp_read(dsp, &response);
+	if (ret != EOK) {
+		ddf_log_error("Failed to read DSP reset response value.");
+		return ret;
+	}
+	if (response != DSP_RESET_RESPONSE) {
+		ddf_log_error("Invalid DSP reset response: %x.", response);
+		return EIO;
+	}
+
+	/* Get DSP version number */
+	dsp_write(dsp, DSP_VERSION);
+	dsp_read(dsp, &dsp->version.major);
+	dsp_read(dsp, &dsp->version.minor);
+
+	return ret;
+}
+
+void sb_dsp_interrupt(sb_dsp_t *dsp)
+{
+	assert(dsp);
+
+	dsp->active.frame_count +=
+	    dsp->active.samples / ((dsp->active.mode & DSP_MODE_STEREO) ? 2 : 1);
+
+	switch (dsp->state)
+	{
+	case DSP_PLAYBACK_ACTIVE_EVENTS:
+		dsp_report_event(dsp, PCM_EVENT_FRAMES_PLAYED);
+	case DSP_PLAYBACK_NOEVENTS:
+#ifndef AUTO_DMA_MODE
+		dsp_start_current_active(dsp, SINGLE_DMA_16B_DA);
+#endif
+		break;
+	case DSP_CAPTURE_ACTIVE_EVENTS:
+		dsp_report_event(dsp, PCM_EVENT_FRAMES_CAPTURED);
+	case DSP_CAPTURE_NOEVENTS:
+#ifndef AUTO_DMA_MODE
+		dsp_start_current_active(dsp, SINGLE_DMA_16B_DA);
+#endif
+		break;
+	case DSP_CAPTURE_TERMINATE:
+		dsp_change_state(dsp, DSP_READY);
+		dsp_report_event(dsp, PCM_EVENT_CAPTURE_TERMINATED);
+		async_exchange_end(dsp->event_exchange);
+		dsp->event_exchange = NULL;
+		break;
+	case DSP_PLAYBACK_TERMINATE:
+		dsp_change_state(dsp, DSP_READY);
+		dsp_report_event(dsp, PCM_EVENT_PLAYBACK_TERMINATED);
+		async_exchange_end(dsp->event_exchange);
+		dsp->event_exchange = NULL;
+		break;
+	default:
+		ddf_log_warning("Interrupt while DSP not active (%s)",
+		    dsp_state_to_str(dsp->state));
+	}
+}
+
+unsigned sb_dsp_query_cap(sb_dsp_t *dsp, audio_cap_t cap)
+{
+	ddf_log_verbose("Querying cap %s", audio_pcm_cap_str(cap));
+	switch(cap) {
+	case AUDIO_CAP_CAPTURE:
+	case AUDIO_CAP_PLAYBACK:
+	case AUDIO_CAP_INTERRUPT:
+	case AUDIO_CAP_BUFFER_POS:
+		return 1;
+	case AUDIO_CAP_MAX_BUFFER:
+		return MAX_BUFFER_SIZE;
+	case AUDIO_CAP_INTERRUPT_MIN_FRAMES:
+		return 1;
+	case AUDIO_CAP_INTERRUPT_MAX_FRAMES:
+		return 16535;
+	default:
+		return ENOTSUP;
+	}
+}
+
+int sb_dsp_get_buffer_position(sb_dsp_t *dsp, size_t *pos)
+{
+	if (dsp->state == DSP_NO_BUFFER)
+		return ENOENT;
+
+	assert(dsp->buffer.data);
+	async_sess_t *sess = devman_parent_device_connect(EXCHANGE_ATOMIC,
+	    ddf_dev_get_handle(dsp->sb_dev), IPC_FLAG_BLOCKING);
+
+	// TODO: Assumes DMA 16
+	const int remain = hw_res_dma_channel_remain(sess, dsp->dma16_channel);
+	async_hangup(sess);
+	if (remain >= 0) {
+		*pos = dsp->buffer.size - remain;
+		return EOK;
+	}
+	return remain;
+}
+
+int sb_dsp_test_format(sb_dsp_t *dsp, unsigned *channels, unsigned *rate,
+  pcm_sample_format_t *format)
+{
+	int ret = EOK;
+	if (*channels == 0 || *channels > 2) {
+		*channels = 2;
+		ret = ELIMIT;
+	}
+	//TODO 8bit DMA supports 8bit formats
+	if (*format != PCM_SAMPLE_SINT16_LE && *format != PCM_SAMPLE_UINT16_LE) {
+		*format = pcm_sample_format_is_signed(*format) ?
+		    PCM_SAMPLE_SINT16_LE : PCM_SAMPLE_UINT16_LE;
+		ret = ELIMIT;
+	}
+	if (*rate > DSP_RATE_UPPER_LIMIT) {
+		*rate = DSP_RATE_UPPER_LIMIT;
+		ret = ELIMIT;
+	}
+	if (*rate < DSP_RATE_LOWER_LIMIT) {
+		*rate = DSP_RATE_LOWER_LIMIT;
+		ret = ELIMIT;
+	}
+	return ret;
+}
+
+int sb_dsp_set_event_session(sb_dsp_t *dsp, async_sess_t *session)
+{
+	assert(dsp);
+	if (dsp->event_session && session)
+		return EBUSY;
+	dsp->event_session = session;
+	ddf_log_debug("Set event session to %p.", session);
+	return EOK;
+}
+
+async_sess_t * sb_dsp_get_event_session(sb_dsp_t *dsp)
+{
+	assert(dsp);
+	ddf_log_debug("Get event session: %p.", dsp->event_session);
+	return dsp->event_session;
+}
+
+int sb_dsp_get_buffer(sb_dsp_t *dsp, void **buffer, size_t *size)
+{
+	assert(dsp);
+	assert(size);
+
+	/* buffer is already setup by for someone, refuse to work until
+	 * it's released */
+	if (dsp->state != DSP_NO_BUFFER)
+		return EBUSY;
+	assert(dsp->buffer.data == NULL);
+
+	const int ret = setup_buffer(dsp, *size);
+	if (ret == EOK) {
+		ddf_log_debug("Providing buffer: %p, %zu B.",
+		    dsp->buffer.data, dsp->buffer.size);
+
+		if (buffer)
+			*buffer = dsp->buffer.data;
+		if (size)
+			*size = dsp->buffer.size;
+		dsp_change_state(dsp, DSP_READY);
+	}
+	return ret;
+}
+
+int sb_dsp_release_buffer(sb_dsp_t *dsp)
+{
+	assert(dsp);
+	if (dsp->state != DSP_READY)
+		return EINVAL;
+	assert(dsp->buffer.data);
+	dmamem_unmap_anonymous(dsp->buffer.data);
+	dsp->buffer.data = NULL;
+	dsp->buffer.size = 0;
+	ddf_log_debug("DSP buffer released.");
+	dsp_change_state(dsp, DSP_NO_BUFFER);
+	return EOK;
+}
+
+int sb_dsp_start_playback(sb_dsp_t *dsp, unsigned frames,
+    unsigned channels, unsigned sampling_rate, pcm_sample_format_t format)
+{
+	assert(dsp);
+
+	if (!dsp->buffer.data || dsp->state != DSP_READY)
+		return EINVAL;
+
+	/* Check supported parameters */
+	ddf_log_debug("Requested playback: %u frames, %uHz, %s, %u channel(s).",
+	    frames, sampling_rate, pcm_sample_format_str(format), channels);
+	if (sb_dsp_test_format(dsp, &channels, &sampling_rate, &format) != EOK)
+		return ENOTSUP;
+
+	/* Client requested regular events */
+	if (frames && !dsp->event_session)
+		return EINVAL;
+
+	if (dsp->event_session) {
+		dsp->event_exchange = async_exchange_begin(dsp->event_session);
+		if (!dsp->event_exchange)
+			return ENOMEM;
+	}
+
+	dsp->active.mode = 0
+	    | (pcm_sample_format_is_signed(format) ? DSP_MODE_SIGNED : 0)
+	    | (channels == 2 ? DSP_MODE_STEREO : 0);
+	dsp->active.samples = frames * channels;
+	dsp->active.frame_count = 0;
+
+	dsp_set_sampling_rate(dsp, sampling_rate);
+
+#ifdef AUTO_DMA_MODE
+	dsp_start_current_active(dsp, AUTO_DMA_16B_DA_FIFO);
+#else
+	dsp_start_current_active(dsp, SINGLE_DMA_16B_DA);
+#endif
+
+	ddf_log_verbose("Playback started, event every %u samples",
+	    dsp->active.samples);
+
+	dsp_change_state(dsp,
+	    frames ? DSP_PLAYBACK_ACTIVE_EVENTS : DSP_PLAYBACK_NOEVENTS);
+	if (dsp->state == DSP_PLAYBACK_ACTIVE_EVENTS)
+		dsp_report_event(dsp, PCM_EVENT_PLAYBACK_STARTED);
+
+	return EOK;
+}
+
+int sb_dsp_stop_playback(sb_dsp_t *dsp, bool immediate)
+{
+	assert(dsp);
+	if ((dsp->state == DSP_PLAYBACK_NOEVENTS ||
+	    dsp->state == DSP_PLAYBACK_ACTIVE_EVENTS) &&
+	    immediate)
+	{
+		dsp_write(dsp, DMA_16B_PAUSE);
+		dsp_reset(dsp);
+		ddf_log_debug("Stopped playback");
+		dsp_change_state(dsp, DSP_READY);
+		if (dsp->event_exchange) {
+			dsp_report_event(dsp, PCM_EVENT_PLAYBACK_TERMINATED);
+			async_exchange_end(dsp->event_exchange);
+			dsp->event_exchange = NULL;
+		}
+		return EOK;
+	}
+	if (dsp->state == DSP_PLAYBACK_ACTIVE_EVENTS)
+	{
+		/* Stop after current fragment */
+		assert(!immediate);
+		dsp_write(dsp, DMA_16B_EXIT);
+		ddf_log_debug("Last playback fragment");
+		dsp_change_state(dsp, DSP_PLAYBACK_TERMINATE);
+		return EOK;
+	}
+	return EINVAL;
+}
+
+int sb_dsp_start_capture(sb_dsp_t *dsp, unsigned frames,
+    unsigned channels, unsigned sampling_rate, pcm_sample_format_t format)
+{
+	assert(dsp);
+	if (!dsp->buffer.data || dsp->state != DSP_READY)
+		return EINVAL;
+
+	/* Check supported parameters */
+	ddf_log_debug("Requested capture: %u frames, %uHz, %s, %u channel(s).",
+	    frames, sampling_rate, pcm_sample_format_str(format), channels);
+	if (sb_dsp_test_format(dsp, &channels, &sampling_rate, &format) != EOK)
+		return ENOTSUP;
+
+	/* Client requested regular events */
+	if (frames && !dsp->event_session)
+		return EINVAL;
+
+	if (dsp->event_session) {
+		dsp->event_exchange = async_exchange_begin(dsp->event_session);
+		if (!dsp->event_exchange)
+			return ENOMEM;
+	}
+
+	dsp->active.mode = 0
+	    | (pcm_sample_format_is_signed(format) ? DSP_MODE_SIGNED : 0)
+	    | (channels == 2 ? DSP_MODE_STEREO : 0);
+	dsp->active.samples = frames * channels;
+	dsp->active.frame_count = 0;
+
+	dsp_set_sampling_rate(dsp, sampling_rate);
+
+#ifdef AUTO_DMA_MODE
+	dsp_start_current_active(dsp, AUTO_DMA_16B_AD_FIFO);
+#else
+	dsp_start_current_active(dsp, SINGLE_DMA_16B_AD);
+#endif
+
+	ddf_log_verbose("Capture started started, event every %u samples",
+	    dsp->active.samples);
+	dsp_change_state(dsp,
+	    frames ? DSP_CAPTURE_ACTIVE_EVENTS : DSP_CAPTURE_NOEVENTS);
+	if (dsp->state == DSP_CAPTURE_ACTIVE_EVENTS)
+		dsp_report_event(dsp, PCM_EVENT_CAPTURE_STARTED);
+	return EOK;
+}
+
+int sb_dsp_stop_capture(sb_dsp_t *dsp, bool immediate)
+{
+	assert(dsp);
+	if ((dsp->state == DSP_CAPTURE_NOEVENTS ||
+	    dsp->state == DSP_CAPTURE_ACTIVE_EVENTS) &&
+	    immediate)
+	{
+		dsp_write(dsp, DMA_16B_PAUSE);
+		dsp_reset(dsp);
+		ddf_log_debug("Stopped capture fragment");
+		dsp_change_state(dsp, DSP_READY);
+		if (dsp->event_exchange) {
+			dsp_report_event(dsp, PCM_EVENT_CAPTURE_TERMINATED);
+			async_exchange_end(dsp->event_exchange);
+			dsp->event_exchange = NULL;
+		}
+		return EOK;
+	}
+	if (dsp->state == DSP_CAPTURE_ACTIVE_EVENTS)
+	{
+		/* Stop after current fragment */
+		assert(!immediate);
+		dsp_write(dsp, DMA_16B_EXIT);
+		ddf_log_debug("Last capture fragment");
+		dsp_change_state(dsp, DSP_CAPTURE_TERMINATE);
+		return EOK;
+	}
+	return EINVAL;
+}
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/dsp.h
===================================================================
--- uspace/drv/audio/sb16/dsp.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/dsp.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief Sound Blaster Digital Sound Processor (DSP) helper functions.
+ */
+#ifndef DRV_AUDIO_SB16_DSP_H
+#define DRV_AUDIO_SB16_DSP_H
+
+#include <ddf/driver.h>
+#include <libarch/ddi.h>
+#include <errno.h>
+#include <pcm/sample_format.h>
+#include <audio_pcm_iface.h>
+
+#include "registers.h"
+typedef enum {
+	DSP_PLAYBACK_ACTIVE_EVENTS,
+	DSP_CAPTURE_ACTIVE_EVENTS,
+	DSP_PLAYBACK_NOEVENTS,
+	DSP_CAPTURE_NOEVENTS,
+	DSP_PLAYBACK_TERMINATE,
+	DSP_CAPTURE_TERMINATE,
+	DSP_READY,
+	DSP_NO_BUFFER,
+} dsp_state_t;
+
+typedef struct sb_dsp {
+	sb16_regs_t *regs;
+	int dma8_channel;
+	int dma16_channel;
+	struct {
+		uint8_t major;
+		uint8_t minor;
+	} version;
+	struct {
+		uint8_t *data;
+		size_t size;
+	} buffer;
+	struct {
+		uint8_t mode;
+		uint16_t samples;
+		unsigned frame_count;
+	} active;
+	dsp_state_t state;
+	async_sess_t *event_session;
+	async_exch_t *event_exchange;
+	ddf_dev_t *sb_dev;
+} sb_dsp_t;
+
+int sb_dsp_init(sb_dsp_t *dsp, sb16_regs_t *regs, ddf_dev_t *dev,
+    int dma8, int dma16);
+void sb_dsp_interrupt(sb_dsp_t *dsp);
+unsigned sb_dsp_query_cap(sb_dsp_t *dsp, audio_cap_t cap);
+int sb_dsp_get_buffer_position(sb_dsp_t *dsp, size_t *size);
+int sb_dsp_test_format(sb_dsp_t *dsp, unsigned *channels, unsigned *rate,
+  pcm_sample_format_t *format);
+int sb_dsp_get_buffer(sb_dsp_t *dsp, void **buffer, size_t *size);
+int sb_dsp_set_event_session(sb_dsp_t *dsp, async_sess_t *session);
+async_sess_t * sb_dsp_get_event_session(sb_dsp_t *dsp);
+int sb_dsp_release_buffer(sb_dsp_t *dsp);
+int sb_dsp_start_playback(sb_dsp_t *dsp, unsigned frames,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format);
+int sb_dsp_stop_playback(sb_dsp_t *dsp, bool immediate);
+int sb_dsp_start_capture(sb_dsp_t *dsp, unsigned frames,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format);
+int sb_dsp_stop_capture(sb_dsp_t *dsp, bool immediate);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/dsp_commands.h
===================================================================
--- uspace/drv/audio/sb16/dsp_commands.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/dsp_commands.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief SB16 DSP Command constants
+ */
+#ifndef DRV_AUDIO_SB16_DSP_COMMANDS_H
+#define DRV_AUDIO_SB16_DSP_COMMANDS_H
+
+/** See Sound Blaster Series HW programming Guide Chapter 6. */
+typedef enum dsp_command {
+	DIRECT_8B_OUTPUT = 0x10, /* Followed by unsigned byte of digital data,
+	                          * software controls sampling rate */
+	DIRECT_8B_INPUT = 0x20,  /* Same as DIRECT_8B_OUTPUT but for input */
+
+	TRANSFER_TIME_CONSTANT = 0x40, /* Followed by time constant.
+	                                * TC = 65536 - (256 000 000 /
+					*   (channels * sampling rate))
+					* Send only high byte */
+
+	SINGLE_DMA_8B_OUTPUT = 0x14, /* Followed by length.high and length.low
+	                              * starts single-cycle DMA, length is -1 */
+	SINGLE_DMA_8B_INPUT = 0x24,  /* Same as SINGLE_DMA_8B_OUTPUT, but for
+	                              * input */
+	SINGLE_DMA_8B_ADPCM_2B_OUT = 0x16, /* Starts single-cycle DMA using
+	                                    * Creative ADPSM 8->2 bit compressed
+	                                    * data, Followed by length.low
+					    * and length.high. Length is -1 */
+	SINGLE_DMA_8B_ADPCM_2B_OUT_REF = 0x17, /* Starts single-cycle DMA using
+	                                        * DPSM 8->2 bit compressed data
+	                                        * with reference byte.
+						* Followed by length.low and
+						* length.high. Length is -1 */
+	SINGLE_DMA_8B_ADPCM_4B_OUT = 0x74, /* Same as
+	                                    * SINGLE_DMA_8B_ADPCM_2B_OUT */
+	SINGLE_DMA_8B_ADPCM_4B_OUT_REF = 0x75, /* Same as
+	                                        * SINGLE_DMA_8B_ADPCM_2B_OUT_REF
+						*/
+	SINGLE_DMA_8B_ADPCM_3B_OUT = 0x76, /* Same as
+	                                    * SINGLE_DMA_8B_ADPCM_2B_OUT */
+	SINGLE_DMA_8B_ADPCM_3B_OUT_REF = 0x77, /* Same as
+	                                        * SINGLE_DMA_8B_ADPCM_2B_OUT_REF
+						*/
+
+	DMA_8B_PAUSE = 0xd0, /* Stop sending DMA request,
+	                      * works for SINGLE and AUTO */
+	DMA_8B_CONTINUE = 0xd4, /* Resume transfers paused by DMA_8B_PAUSE */
+
+	SPEAKER_ON = 0xd1,  /* Connect speaker via internal amplifier,
+	                     * has no effect on 4.xx */
+	SPEAKER_OFF = 0xd3, /* Disconnect output from the amplifier,
+	                     * has no effect on 4.xx */
+
+	MIDI_POLLING = 0x30, /* Read DSP for MIDI data */
+	MIDI_INTERRUPT = 0x31, /* Start interrupt mode, interrupt will be
+	                        * generated when there is in-bound data.
+				* To exit send again */
+	MIDI_OUTPUT = 0x38, /* Followed by midi_data */
+
+	PAUSE = 0x80, /* Followed by duration.low, duration.high. Duration is -1
+	               * In the units of sampling period. Generates interrupt
+		       * at the end of period */
+	DSP_VERSION = 0xe1, /* Read 2 bytes, major and minor number */
+
+	AUTO_DMA_8B_OUTPUT = 0x1c, /* Starts auto-init DMA mode using 8-bit
+	                            * Interrupt after every block.
+				    * To terminate, switch to single or use
+				    * EXIT command*/
+	AUTO_DMA_8B_INPUT = 0x2c, /* Same as AUTO_DMA_8B_OUTPUT, but for input*/
+	AUTO_DMA_8B_ADPCM_2B_REF = 0x1f, /* Same as AUTO_DMA_8B_OUTPUT, but use
+	                                  * 8->2bit ADPCM audio format */
+	AUTO_DMA_8B_ADPCM_4B_REF = 0x7d, /* Same as AUTO_DMA_8B_ADPCM_2B_REF */
+	AUTO_DMA_8B_ADPCM_3B_REF = 0x7f, /* Same as AUTO_DMA_8B_ADPCM_2B_REF */
+
+	DMA_8B_EXIT = 0xda, /* Ends DMA transfer and terminates I/O process */
+
+	BLOCK_TRANSFER_SIZE = 0x48, /* Followed by size.low, size.high
+	                             * Used with HIGH_SPEED AUTO_DMA */
+
+	UART_MIDI_POLLING = 0x34, /* Start UART MIDI polling mode, read and
+	                           * write from/to DSP is interpreted as
+				   * read/write from/to MIDI.
+				   * To exit use reset signal. Note that reset
+				   * will restore previous state and won't do
+				   * complete reset */
+	UART_MIDI_INTERRUPT = 0x35, /* Same as UART_MIDI_POLLING, but use
+	                             * interrupts instead of polling. */
+	UART_MIDI_POLLING_TS = 0x36, /* Add time stamp to inbound data, the
+	                              * order is time.low time.mid time.high
+				      * data */
+	UART_MIDI_INTERRUPT_TS = 0x37, /* Same as UART_MIDI_POLLING_TS, but use
+	                                * interrupts instead of polling */
+
+	SPEAKER_STATUS = 0xd8, /* 0xff means amp is on, 0x00 means it's off */
+
+	AUTO_DMA_8B_HIGH_OUTPUT = 0x90, /* DSP will generate interrupt after
+	                                 * every block. No other commands are
+					 * accepted in this mode. To exit
+					 * the mode send RESET command.
+					 * Note that reset will restore
+					 * previous state. */
+	AUTO_DMA_8B_HIGH_INPUT = 0x98, /* Same as AUTO_DMA_8B_HIGH_OUTPUT */
+	SINGLE_DMA_8B_HIGH_OUTPUT = 0x91, /* Transfer one block and exit,
+	                                   * generates interrupt */
+	SINGLE_DMA_8B_HIGH_INPUT = 0x99, /* Same as SINGLE_DMA_8B_HIGH_OUTPUT */
+
+	SET_MONO_INPUT = 0xa0, /* Mono mode is the default, only on 3.xx */
+	SET_STEREO_INPUT = 0xa8, /* Switch to stereo recording, only on 3.xx */
+
+	SET_SAMPLING_RATE_OUTPUT = 0x41, /* Followed by sapling rate
+	                                  * 5000 to 45000 Hz, inclusive */
+	SET_SAMPLING_RATE_INPUT = 0x42, /* Same as SET_SAMPLING_RATE_OUTPUT */
+
+	SINGLE_DMA_16B_DA = 0xb0,     /* Followed by mode, size.low, size.high*/
+	SINGLE_DMA_16B_DA_FIFO = 0xb2,/* mode format is:                      */
+	AUTO_DMA_16B_DA = 0xb4,       /*    0x00 - unsigned mono              */
+	AUTO_DMA_16B_DA_FIFO = 0xb6,  /*    0x10 - signed mono                */
+	SINGLE_DMA_16B_AD = 0xb8,     /*    0x20 - unsigned stereo            */
+	SINGLE_DMA_16B_AD_FIFO = 0xba,/*    0x30 - signed stereo              */
+	AUTO_DMA_16B_AD = 0xbc,       /* Size is -1. Terminate by EXIT        */
+	AUTO_DMA_16B_AD_FIFO = 0xbe,  /* or switch to SINGLE_DMA              */
+
+	SINGLE_DMA_8B_DA = 0xc0,     /* Followed by mode, size.low, size.high */
+	SINGLE_DMA_8B_DA_FIFO = 0xc2,/* mode format is:                       */
+	AUTO_DMA_8B_DA = 0xc4,       /*    0x00 - unsigned mono               */
+	AUTO_DMA_8B_DA_FIFO = 0xc6,  /*    0x10 - signed mono                 */
+	SINGLE_DMA_8B_AD = 0xc8,     /*    0x20 - unsigned stereo             */
+	SINGLE_DMA_8B_AD_FIFO = 0xca,/*    0x30 - signed stereo               */
+	AUTO_DMA_8B_AD = 0xcc,       /* Size is -1. Terminate by EXIT         */
+	AUTO_DMA_8B_AD_FIFO = 0xce,  /* or switch to SINGLE_DMA               */
+
+	DMA_16B_PAUSE = 0xd5,/* Stop sending DMA request, both SINGLE and AUTO*/
+	DMA_16B_CONTINUE = 0xd6, /* Resume requests paused by DMA_16B_PAUSE */
+	DMA_16B_EXIT = 0xd9, /* Ends DMA transfer and terminates I/O process */
+} dsp_command_t;
+
+#define DSP_MODE_SIGNED 0x10
+#define DSP_MODE_STEREO 0x20
+
+static inline const char * mode_to_str(uint8_t mode)
+{
+	if (mode & 0xcf)
+		return "unknown";
+	static const char * names[] = {
+		"unsigned mono (8bit)",
+		"signed mono (16bit)",
+		"unsigned stereo (8bit)",
+		"signed stereo (16bit)",
+	};
+	return names[mode >> 4];
+}
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/main.c
===================================================================
--- uspace/drv/audio/sb16/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2011 Vojtech Horky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/** @addtogroup drvaudiosb16
+ * @{
+ */
+/** @file
+ * Main routines of Creative Labs SoundBlaster 16 driver
+ */
+#include <ddf/driver.h>
+#include <ddf/interrupt.h>
+#include <ddf/log.h>
+#include <device/hw_res_parsed.h>
+#include <devman.h>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <str_error.h>
+
+#include "ddf_log.h"
+#include "sb16.h"
+
+#define NAME "sb16"
+
+static int sb_add_device(ddf_dev_t *device);
+static int sb_get_res(ddf_dev_t *device, addr_range_t **pp_sb_regs,
+    addr_range_t **pp_mpu_regs, int *irq, int *dma8, int *dma16);
+static int sb_enable_interrupts(ddf_dev_t *device);
+
+static driver_ops_t sb_driver_ops = {
+	.dev_add = sb_add_device,
+};
+
+static driver_t sb_driver = {
+	.name = NAME,
+	.driver_ops = &sb_driver_ops
+};
+
+/** Initialize global driver structures (NONE).
+ *
+ * @param[in] argc Nmber of arguments in argv vector (ignored).
+ * @param[in] argv Cmdline argument vector (ignored).
+ * @return Error code.
+ *
+ * Driver debug level is set here.
+ */
+int main(int argc, char *argv[])
+{
+	printf(NAME": HelenOS SB16 audio driver.\n");
+	ddf_log_init(NAME);
+	return ddf_driver_main(&sb_driver);
+}
+
+static void irq_handler(ddf_dev_t *dev, ipc_callid_t iid, ipc_call_t *call)
+{
+	sb16_t *sb16_dev = ddf_dev_data_get(dev);
+	sb16_interrupt(sb16_dev);
+}
+
+/** Initialize new SB16 driver instance.
+ *
+ * @param[in] device DDF instance of the device to initialize.
+ * @return Error code.
+ */
+static int sb_add_device(ddf_dev_t *device)
+{
+	bool handler_regd = false;
+	const size_t irq_cmd_count = sb16_irq_code_size();
+	irq_cmd_t irq_cmds[irq_cmd_count];
+	irq_pio_range_t irq_ranges[1];
+
+	sb16_t *soft_state = ddf_dev_data_alloc(device, sizeof(sb16_t));
+	int rc = soft_state ? EOK : ENOMEM;
+	if (rc != EOK) {
+		ddf_log_error("Failed to allocate sb16 structure.");
+		goto error;
+	}
+
+	addr_range_t sb_regs;
+	addr_range_t *p_sb_regs = &sb_regs;
+	addr_range_t mpu_regs;
+	addr_range_t *p_mpu_regs = &mpu_regs;
+	int irq = 0, dma8 = 0, dma16 = 0;
+
+	rc = sb_get_res(device, &p_sb_regs, &p_mpu_regs, &irq, &dma8, &dma16);
+	if (rc != EOK) {
+		ddf_log_error("Failed to get resources: %s.", str_error(rc));
+		goto error;
+	}
+
+	sb16_irq_code(p_sb_regs, dma8, dma16, irq_cmds, irq_ranges);
+
+	irq_code_t irq_code = {
+		.cmdcount = irq_cmd_count,
+		.cmds = irq_cmds,
+		.rangecount = 1,
+		.ranges = irq_ranges
+	};
+
+	rc = register_interrupt_handler(device, irq, irq_handler, &irq_code);
+	if (rc != EOK) {
+		ddf_log_error("Failed to register irq handler: %s.",
+		    str_error(rc));
+		goto error;
+	}
+
+	handler_regd = true;
+
+	rc = sb_enable_interrupts(device);
+	if (rc != EOK) {
+		ddf_log_error("Failed to enable interrupts: %s.",
+		    str_error(rc));
+		goto error;
+	}
+
+	rc = sb16_init_sb16(soft_state, p_sb_regs, device, dma8, dma16);
+	if (rc != EOK) {
+		ddf_log_error("Failed to init sb16 driver: %s.",
+		    str_error(rc));
+		goto error;
+	}
+
+	rc = sb16_init_mpu(soft_state, p_mpu_regs);
+	if (rc == EOK) {
+		ddf_fun_t *mpu_fun =
+		    ddf_fun_create(device, fun_exposed, "midi");
+		if (mpu_fun) {
+			rc = ddf_fun_bind(mpu_fun);
+			if (rc != EOK)
+				ddf_log_error(
+				    "Failed to bind midi function: %s.",
+				    str_error(rc));
+		} else {
+			ddf_log_error("Failed to create midi function.");
+		}
+	} else {
+		ddf_log_warning("Failed to init mpu driver: %s.",
+		    str_error(rc));
+	}
+
+	/* MPU state does not matter */
+	return EOK;
+error:
+	if (handler_regd)
+		unregister_interrupt_handler(device, irq);
+	return rc;
+}
+
+static int sb_get_res(ddf_dev_t *device, addr_range_t **pp_sb_regs,
+    addr_range_t **pp_mpu_regs, int *irq, int *dma8, int *dma16)
+{
+	assert(device);
+
+	async_sess_t *parent_sess = devman_parent_device_connect(
+	    EXCHANGE_SERIALIZE, ddf_dev_get_handle(device), IPC_FLAG_BLOCKING);
+	if (!parent_sess)
+		return ENOMEM;
+
+	hw_res_list_parsed_t hw_res;
+	hw_res_list_parsed_init(&hw_res);
+	const int ret = hw_res_get_list_parsed(parent_sess, &hw_res, 0);
+	async_hangup(parent_sess);
+	if (ret != EOK) {
+		return ret;
+	}
+
+	/* 1x IRQ, 1-2x DMA(8,16), 1-2x IO (MPU is separate). */
+	if (hw_res.irqs.count != 1 ||
+	   (hw_res.io_ranges.count != 1 && hw_res.io_ranges.count != 2) ||
+	   (hw_res.dma_channels.count != 1 && hw_res.dma_channels.count != 2)) {
+		hw_res_list_parsed_clean(&hw_res);
+		return EINVAL;
+	}
+
+	if (irq)
+		*irq = hw_res.irqs.irqs[0];
+
+	if (dma8) {
+		if (hw_res.dma_channels.channels[0] < 4) {
+			*dma8 = hw_res.dma_channels.channels[0];
+		} else {
+			if (hw_res.dma_channels.count == 2 &&
+			    hw_res.dma_channels.channels[1] < 4) {
+				*dma8 = hw_res.dma_channels.channels[1];
+			}
+		}
+	}
+
+	if (dma16) {
+		if (hw_res.dma_channels.channels[0] > 4) {
+			*dma16 = hw_res.dma_channels.channels[0];
+		} else {
+			if (hw_res.dma_channels.count == 2 &&
+			    hw_res.dma_channels.channels[1] > 4) {
+				*dma16 = hw_res.dma_channels.channels[1];
+			}
+		}
+	}
+
+	if (hw_res.io_ranges.count == 1) {
+		if (pp_sb_regs && *pp_sb_regs)
+			**pp_sb_regs = hw_res.io_ranges.ranges[0];
+		if (pp_mpu_regs)
+			*pp_mpu_regs = NULL;
+	} else {
+		const int sb =
+		    (hw_res.io_ranges.ranges[0].size >= sizeof(sb16_regs_t))
+		        ? 0 : 1;
+		const int mpu = 1 - sb;
+		if (pp_sb_regs && *pp_sb_regs)
+			**pp_sb_regs = hw_res.io_ranges.ranges[sb];
+		if (pp_mpu_regs && *pp_mpu_regs)
+			**pp_mpu_regs = hw_res.io_ranges.ranges[mpu];
+	}
+
+	return EOK;
+}
+
+int sb_enable_interrupts(ddf_dev_t *device)
+{
+	async_sess_t *parent_sess = devman_parent_device_connect(
+	    EXCHANGE_SERIALIZE, ddf_dev_get_handle(device), IPC_FLAG_BLOCKING);
+	if (!parent_sess)
+		return ENOMEM;
+
+	bool enabled = hw_res_enable_interrupt(parent_sess);
+	async_hangup(parent_sess);
+
+	return enabled ? EOK : EIO;
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/mixer.c
===================================================================
--- uspace/drv/audio/sb16/mixer.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/mixer.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <libarch/ddi.h>
+#include <macros.h>
+#include <unistd.h>
+
+#include "ddf_log.h"
+#include "mixer.h"
+
+typedef struct channel {
+	const char *name;
+	uint8_t address;
+	unsigned shift;
+	unsigned volume_levels;
+	bool preserve_bits;
+} channel_t;
+
+/* CT1335 channels */
+static const channel_t channels_table_ct1335[] = {
+	{ "Master", 0x02, 1, 8, false }, /* Master, Mono, 3bit volume level */
+	{ "Midi", 0x06, 1, 8, false }, /* Midi, Mono, 3bit volume level */
+	{ "CD", 0x08, 1, 8, false }, /* CD, Mono, 3bit volume level */
+	{ "Voice", 0x0a, 1, 4, false }, /* Voice, Mono, 2bit volume level */
+};
+
+/* CT1345 channels */
+static const channel_t channels_table_ct1345[] = {
+	{ "Master Left", 0x22, 5, 8, true }, /* Master, Left, 3bit volume level */
+	{ "Master Right", 0x22, 1, 8, true }, /* Master, Right, 3bit volume level */
+	{ "MIDI Left", 0x26, 5, 8, true }, /* Midi, Left, 3bit volume level */
+	{ "MIDI Right", 0x26, 1, 8, true }, /* Midi, Right, 3bit volume level */
+	{ "CD Left", 0x28, 5, 8, true }, /* CD, Left, 3bit volume level */
+	{ "CD Right", 0x28, 1, 8, true }, /* CD, Right, 3bit volume level */
+	{ "Line In Left", 0x2e, 5, 8, true }, /* Line, Left, 3bit volume level */
+	{ "Line In Right", 0x2e, 1, 8, true }, /* Line, Right, 3bit volume level */
+	{ "Voice Left", 0x04, 5, 8, true }, /* Voice, Left, 3bit volume level */
+	{ "Voice Right", 0x04, 1, 8, true }, /* Voice, Right, 3bit volume level */
+	{ "Mic", 0x0a, 1, 4, false }, /* Mic, Mono, 2bit volume level */
+};
+
+/* CT1745 channels */
+static const channel_t channels_table_ct1745[] = {
+	{ "Master Left", 0x30, 3, 32, false },  /* Master, Left, 5bit volume level */
+	{ "Master Right", 0x31, 3, 32, false }, /* Master, Right, 5bit volume level */
+	{ "Voice Left", 0x32, 3, 32, false },  /* Voice, Left, 5bit volume level */
+	{ "Voice Right", 0x33, 3, 32, false }, /* Voice, Right, 5bit volume level */
+	{ "MIDI Left", 0x34, 3, 32, false }, /* MIDI, Left, 5bit volume level */
+	{ "MIDI Right", 0x35, 3, 32, false }, /* MIDI, Right, 5bit volume level */
+	{ "CD Left", 0x36, 3, 32, false }, /* CD, Left, 5bit volume level */
+	{ "CD Right", 0x37, 3, 32, false }, /* CD, Right, 5bit volume level */
+	{ "Line In Left", 0x38, 3, 32, false }, /* Line, Left, 5bit volume level */
+	{ "Line In Right", 0x39, 3, 32, false }, /* Line, Right, 5bit volume level */
+	{ "Mic", 0x3a, 3, 32, false }, /* Mic, Mono, 5bit volume level */
+	{ "PC Speaker", 0x3b, 6, 4, false }, /* PC speaker, Mono, 2bit level */
+	{ "Input Gain Left", 0x3f, 6, 4, false }, /* Input Gain, Left, 2bit level */
+	{ "Input Gain Right", 0x40, 6, 4, false }, /* Input Gain, Right, 2bit level */
+	{ "Output Gain Left", 0x41, 6, 4, false }, /* Output Gain, Left, 2bit level */
+	{ "Output Gain Right", 0x42, 6, 4, false }, /* Output Gain, Right, 2bit level */
+	{ "Treble Left", 0x44, 4, 16, false }, /* Treble, Left, 4bit volume level */
+	{ "Treble Right", 0x45, 4, 16, false }, /* Treble, Right, 4bit volume level */
+	{ "Bass Left", 0x46, 4, 16, false }, /* Bass, Left, 4bit volume level */
+	{ "Bass Right", 0x47, 4, 16, false }, /* Bass, Right, 4bit volume level */
+};
+
+static const struct {
+	const channel_t *table;
+	size_t count;
+} volume_table[] = {
+	[SB_MIXER_NONE] = { NULL, 0 },
+	[SB_MIXER_UNKNOWN] = { NULL, 0 },
+	[SB_MIXER_CT1335] = {
+	    channels_table_ct1335,
+	    ARRAY_SIZE(channels_table_ct1335),
+	},
+	[SB_MIXER_CT1345] = {
+	    channels_table_ct1345,
+	    ARRAY_SIZE(channels_table_ct1345),
+	},
+	[SB_MIXER_CT1745] = {
+	    channels_table_ct1745,
+	    ARRAY_SIZE(channels_table_ct1745),
+	},
+};
+
+const char * sb_mixer_type_str(sb_mixer_type_t type)
+{
+	static const char *names[] = {
+		[SB_MIXER_NONE] = "No mixer",
+		[SB_MIXER_CT1335] = "CT 1335",
+		[SB_MIXER_CT1345] = "CT 1345",
+		[SB_MIXER_CT1745] = "CT 1745",
+		[SB_MIXER_UNKNOWN] = "Unknown mixer",
+	};
+	return names[type];
+}
+
+int sb_mixer_init(sb_mixer_t *mixer, sb16_regs_t *regs, sb_mixer_type_t type)
+{
+	assert(mixer);
+	mixer->regs = regs;
+	mixer->type = type;
+	if (type == SB_MIXER_UNKNOWN)
+		return ENOTSUP;
+
+	if (type != SB_MIXER_NONE) {
+		pio_write_8(&regs->mixer_address, MIXER_RESET_ADDRESS);
+		pio_write_8(&regs->mixer_data, 1);
+	}
+	pio_write_8(&regs->mixer_address, MIXER_PNP_IRQ_ADDRESS);
+	const uint8_t irq = pio_read_8(&regs->mixer_data);
+	pio_write_8(&regs->mixer_address, MIXER_PNP_DMA_ADDRESS);
+	const uint8_t dma = pio_read_8(&regs->mixer_data);
+	ddf_log_debug("SB16 setup with IRQ 0x%hhx and DMA 0x%hhx.", irq, dma);
+	return EOK;
+}
+
+int sb_mixer_get_control_item_count(const sb_mixer_t *mixer)
+{
+	assert(mixer);
+	return volume_table[mixer->type].count;
+}
+
+int sb_mixer_get_control_item_info(const sb_mixer_t *mixer, unsigned item,
+    const char** name, unsigned *levels)
+{
+	assert(mixer);
+	if (item > volume_table[mixer->type].count)
+		return ENOENT;
+
+	const channel_t *ch = &volume_table[mixer->type].table[item];
+	if (name)
+		*name = ch->name;
+	if (levels)
+		*levels = ch->volume_levels;
+	return EOK;
+}
+
+/**
+ * Write new volume level from mixer registers.
+ * @param mixer SB Mixer to use.
+ * @param index Control item(channel) index.
+ * @param value New volume level.
+ * @return Error code.
+ */
+int sb_mixer_get_control_item_value(const sb_mixer_t *mixer, unsigned item,
+    unsigned *value)
+{
+	assert(mixer);
+	if (!value)
+		return EBADMEM;
+	if (item > volume_table[mixer->type].count)
+		return ENOENT;
+
+	const channel_t *chan = &volume_table[mixer->type].table[item];
+	pio_write_8(&mixer->regs->mixer_address, chan->address);
+	*value = (pio_read_8(&mixer->regs->mixer_data) >> chan->shift)
+	    & (chan->volume_levels - 1);
+	return EOK;
+}
+
+/**
+ * Write new volume level to mixer registers.
+ * @param mixer SB Mixer to use.
+ * @param index Control item(channel) index.
+ * @param value New volume level.
+ * @return Error code.
+ */
+int sb_mixer_set_control_item_value(const sb_mixer_t *mixer, unsigned item,
+    unsigned value)
+{
+	assert(mixer);
+	if (item > volume_table[mixer->type].count)
+		return ENOENT;
+
+	const channel_t *chan = &volume_table[mixer->type].table[item];
+	if (value >= chan->volume_levels)
+		value = chan->volume_levels - 1;
+
+	pio_write_8(&mixer->regs->mixer_address, chan->address);
+
+	uint8_t regv = 0;
+	if (chan->preserve_bits) {
+		regv = pio_read_8(&mixer->regs->mixer_data);
+		regv &= ~(uint8_t)((chan->volume_levels - 1) << chan->shift);
+	}
+
+	regv |= value << chan->shift;
+	pio_write_8(&mixer->regs->mixer_data, regv);
+	ddf_log_note("Item %s new value is: %u.",
+	    volume_table[mixer->type].table[item].name, value);
+	return EOK;
+}
Index: uspace/drv/audio/sb16/mixer.h
===================================================================
--- uspace/drv/audio/sb16/mixer.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/mixer.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief SB16 main structure combining all functionality
+ */
+#ifndef DRV_AUDIO_SB16_MIXER_H
+#define DRV_AUDIO_SB16_MIXER_H
+
+#include "registers.h"
+
+typedef enum mixer_type {
+	SB_MIXER_NONE,
+	SB_MIXER_CT1335,
+	SB_MIXER_CT1345,
+	SB_MIXER_CT1745,
+	SB_MIXER_UNKNOWN,
+} sb_mixer_type_t;
+
+typedef struct sb_mixer {
+	sb16_regs_t *regs;
+	sb_mixer_type_t type;
+} sb_mixer_t;
+
+const char * sb_mixer_type_str(sb_mixer_type_t type);
+int sb_mixer_init(sb_mixer_t *mixer, sb16_regs_t *regs, sb_mixer_type_t type);
+int sb_mixer_get_control_item_count(const sb_mixer_t *mixer);
+int sb_mixer_get_control_item_info(const sb_mixer_t *mixer, unsigned index,
+    const char **name, unsigned *levels);
+int sb_mixer_get_control_item_value(const sb_mixer_t *mixer, unsigned index,
+    unsigned *value);
+int sb_mixer_set_control_item_value(const sb_mixer_t *mixer, unsigned index,
+    unsigned value);
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/mixer_iface.c
===================================================================
--- uspace/drv/audio/sb16/mixer_iface.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/mixer_iface.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * Main routines of Creative Labs SoundBlaster 16 driver
+ */
+
+#include <errno.h>
+#include <audio_mixer_iface.h>
+
+#include "mixer.h"
+
+static int sb_get_info(ddf_fun_t *fun, const char** name, unsigned *items)
+{
+	assert(fun);
+	const sb_mixer_t *mixer = ddf_fun_data_get(fun);
+	assert(mixer);
+	if (name)
+		*name = sb_mixer_type_str(mixer->type);
+	if (items)
+		*items = sb_mixer_get_control_item_count(mixer);
+
+	return EOK;
+}
+
+static int sb_get_item_info(ddf_fun_t *fun, unsigned item, const char** name,
+    unsigned *max_level)
+{
+	assert(fun);
+	const sb_mixer_t *mixer = ddf_fun_data_get(fun);
+	assert(mixer);
+	return
+	    sb_mixer_get_control_item_info(mixer, item, name, max_level);
+}
+
+static int sb_set_item_level(ddf_fun_t *fun, unsigned item, unsigned value)
+{
+	assert(fun);
+	const sb_mixer_t *mixer = ddf_fun_data_get(fun);
+	assert(mixer);
+	return sb_mixer_set_control_item_value(mixer, item, value);
+}
+
+static int sb_get_item_level(ddf_fun_t *fun, unsigned item, unsigned *value)
+{
+	assert(fun);
+	const sb_mixer_t *mixer = ddf_fun_data_get(fun);
+	assert(mixer);
+	return sb_mixer_get_control_item_value(mixer, item, value);
+}
+
+audio_mixer_iface_t sb_mixer_iface = {
+	.get_info = sb_get_info,
+	.get_item_info = sb_get_item_info,
+
+	.get_item_level = sb_get_item_level,
+	.set_item_level = sb_set_item_level,
+};
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/pcm_iface.c
===================================================================
--- uspace/drv/audio/sb16/pcm_iface.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/pcm_iface.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * Main routines of Creative Labs SoundBlaster 16 driver
+ */
+
+#include <async.h>
+#include <errno.h>
+#include <audio_pcm_iface.h>
+#include <pcm/sample_format.h>
+
+#include "dsp.h"
+
+static inline sb_dsp_t * fun_to_dsp(ddf_fun_t *fun)
+{
+	assert(fun);
+	sb_dsp_t *dsp = ddf_fun_data_get(fun);
+	assert(dsp);
+	return dsp;
+}
+
+static int sb_get_info_str(ddf_fun_t *fun, const char** name)
+{
+	if (name)
+		*name = "SB 16 DSP";
+	return EOK;
+}
+
+static unsigned sb_query_cap(ddf_fun_t *fun, audio_cap_t cap)
+{
+	return sb_dsp_query_cap(fun_to_dsp(fun), cap);
+}
+
+static int sb_test_format(ddf_fun_t *fun, unsigned *channels, unsigned *rate,
+    pcm_sample_format_t *format)
+{
+	return sb_dsp_test_format(fun_to_dsp(fun), channels, rate, format);
+}
+static int sb_get_buffer(ddf_fun_t *fun, void **buffer, size_t *size)
+{
+	return sb_dsp_get_buffer(fun_to_dsp(fun), buffer, size);
+}
+
+static int sb_get_buffer_position(ddf_fun_t *fun, size_t *size)
+{
+	return sb_dsp_get_buffer_position(fun_to_dsp(fun), size);
+}
+
+static int sb_set_event_session(ddf_fun_t *fun, async_sess_t *sess)
+{
+	return sb_dsp_set_event_session(fun_to_dsp(fun), sess);
+}
+
+static async_sess_t * sb_get_event_session(ddf_fun_t *fun)
+{
+	return sb_dsp_get_event_session(fun_to_dsp(fun));
+}
+
+static int sb_release_buffer(ddf_fun_t *fun)
+{
+	return sb_dsp_release_buffer(fun_to_dsp(fun));
+}
+
+static int sb_start_playback(ddf_fun_t *fun, unsigned frames,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
+{
+	return sb_dsp_start_playback(
+	    fun_to_dsp(fun), frames, channels, sample_rate, format);
+}
+
+static int sb_stop_playback(ddf_fun_t *fun, bool immediate)
+{
+	return sb_dsp_stop_playback(fun_to_dsp(fun), immediate);
+}
+
+static int sb_start_capture(ddf_fun_t *fun, unsigned frames,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
+{
+	return sb_dsp_start_capture(
+	    fun_to_dsp(fun), frames, channels, sample_rate, format);
+}
+
+static int sb_stop_capture(ddf_fun_t *fun, bool immediate)
+{
+	return sb_dsp_stop_capture(fun_to_dsp(fun), immediate);
+}
+
+audio_pcm_iface_t sb_pcm_iface = {
+	.get_info_str = sb_get_info_str,
+	.test_format = sb_test_format,
+	.query_cap = sb_query_cap,
+
+	.get_buffer = sb_get_buffer,
+	.release_buffer = sb_release_buffer,
+	.set_event_session = sb_set_event_session,
+	.get_event_session = sb_get_event_session,
+	.get_buffer_pos = sb_get_buffer_position,
+
+	.start_playback = sb_start_playback,
+	.stop_playback = sb_stop_playback,
+
+	.start_capture = sb_start_capture,
+	.stop_capture = sb_stop_capture,
+};
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/registers.h
===================================================================
--- uspace/drv/audio/sb16/registers.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/registers.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief SB16 main structure combining all functionality
+ */
+#ifndef DRV_AUDIO_SB16_REGISTERS_H
+#define DRV_AUDIO_SB16_REGISTERS_H
+
+#include <ddi.h>
+
+typedef struct sb16_regs {
+	ioport8_t fm_address_status;
+	ioport8_t fm_data;
+	ioport8_t afm_address_status;
+	ioport8_t afm_data;
+	ioport8_t mixer_address;
+#define MIXER_RESET_ADDRESS 0x00
+#define MIXER_PNP_IRQ_ADDRESS 0x80
+#define MIXER_PNP_DMA_ADDRESS 0x81
+#define MIXER_IRQ_STATUS_ADDRESS 0x82 /* The Interrupt Status register,
+                                       * addressed as register 82h on the
+                                       * Mixer register map p.27 */
+	ioport8_t mixer_data;
+	ioport8_t dsp_reset;
+	ioport8_t __reserved1; /* 0x7 */
+	ioport8_t fm_address_status2;
+	ioport8_t fm_data2;
+	ioport8_t dsp_data_read;
+	ioport8_t __reserved2; /*0xb*/
+	ioport8_t dsp_write; /* Both command and data, bit 7 is write status */
+#define DSP_WRITE_BUSY (1 << 7)
+	ioport8_t __reserved3; /*0xd*/
+	ioport8_t dsp_read_status; /* Bit 7 */
+#define DSP_READ_READY (1 << 7)
+	ioport8_t dma16_ack; /*0xf*/
+	ioport8_t cd_command_data;
+	ioport8_t cd_status;
+	ioport8_t cd_reset;
+	ioport8_t cd_enable;
+} sb16_regs_t;
+
+typedef struct mpu_regs {
+	ioport8_t data;
+#define MPU_CMD_ACK (0xfe)
+
+	ioport8_t status_command;
+#define MPU_STATUS_OUTPUT_BUSY (1 << 6)
+#define MPU_STATUS_INPUT_BUSY (1 << 7)
+
+#define MPU_CMD_RESET (0xff)
+#define MPU_CMD_ENTER_UART (0x3f)
+} mpu_regs_t;
+
+#endif
+/**
+ * @}
+ */
+
Index: uspace/drv/audio/sb16/sb16.c
===================================================================
--- uspace/drv/audio/sb16/sb16.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/sb16.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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.
+ */
+
+#define _DDF_DATA_IMPLANT
+
+#include <errno.h>
+#include <str_error.h>
+#include <macros.h>
+#include <audio_mixer_iface.h>
+#include <audio_pcm_iface.h>
+
+#include "ddf_log.h"
+#include "dsp_commands.h"
+#include "dsp.h"
+#include "sb16.h"
+
+extern audio_mixer_iface_t sb_mixer_iface;
+extern audio_pcm_iface_t sb_pcm_iface;
+
+static ddf_dev_ops_t sb_mixer_ops = {
+	.interfaces[AUDIO_MIXER_IFACE] = &sb_mixer_iface,
+};
+
+static ddf_dev_ops_t sb_pcm_ops = {
+	.interfaces[AUDIO_PCM_BUFFER_IFACE] = &sb_pcm_iface,
+};
+
+/* ISA interrupts should be edge-triggered so there should be no need for
+ * irq code magic, but we still need to ack those interrupts ASAP. */
+static const irq_cmd_t irq_cmds[] = {
+	{ .cmd = CMD_PIO_READ_8, .dstarg = 1 }, /* Address patched at runtime */
+	{ .cmd = CMD_PIO_READ_8, .dstarg = 1 }, /* Address patched at runtime */
+	{ .cmd = CMD_ACCEPT },
+};
+
+static inline sb_mixer_type_t sb_mixer_type_by_dsp_version(
+    unsigned major, unsigned minor)
+{
+	switch (major)
+	{
+	case 1: return SB_MIXER_NONE; /* SB 1.5 and early 2.0 = no mixer chip */
+	case 2: return (minor == 0) ? SB_MIXER_NONE : SB_MIXER_CT1335;
+	case 3: return SB_MIXER_CT1345; /* SB Pro */
+	case 4: return SB_MIXER_CT1745; /* SB 16  */
+	default: return SB_MIXER_UNKNOWN;
+	}
+}
+
+size_t sb16_irq_code_size(void)
+{
+	return ARRAY_SIZE(irq_cmds);
+}
+
+void sb16_irq_code(addr_range_t *regs, int dma8, int dma16, irq_cmd_t cmds[],
+    irq_pio_range_t ranges[])
+{
+	assert(regs);
+	assert(dma8 > 0 && dma8 < 4);
+
+	sb16_regs_t *registers = RNGABSPTR(*regs);
+	memcpy(cmds, irq_cmds, sizeof(irq_cmds));
+	cmds[0].addr = (void *) &registers->dsp_read_status;
+	ranges[0].base = (uintptr_t) registers;
+	ranges[0].size = sizeof(*registers);
+	if (dma16 > 4 && dma16 < 8) {
+		/* Valid dma16 */
+		cmds[1].addr = (void *) &registers->dma16_ack;
+	} else {
+		cmds[1].cmd = CMD_ACCEPT;
+	}
+}
+
+int sb16_init_sb16(sb16_t *sb, addr_range_t *regs, ddf_dev_t *dev, int dma8,
+    int dma16)
+{
+	assert(sb);
+
+	/* Setup registers */
+	int ret = pio_enable_range(regs, (void **) &sb->regs);
+	if (ret != EOK)
+		return ret;
+	ddf_log_note("PIO registers at %p accessible.", sb->regs);
+
+	/* Initialize DSP */
+	ddf_fun_t *dsp_fun = ddf_fun_create(dev, fun_exposed, "pcm");
+	if (!dsp_fun) {
+		ddf_log_error("Failed to create dsp function.");
+		return ENOMEM;
+	}
+
+	ret = sb_dsp_init(&sb->dsp, sb->regs, dev, dma8, dma16);
+	if (ret != EOK) {
+		ddf_log_error("Failed to initialize SB DSP: %s.",
+		    str_error(ret));
+		ddf_fun_destroy(dsp_fun);
+		return ret;
+	}
+	//TODO remove data implant
+	ddf_fun_data_implant(dsp_fun, &sb->dsp);
+	ddf_fun_set_ops(dsp_fun, &sb_pcm_ops);
+	ddf_log_note("Sound blaster DSP (%x.%x) initialized.",
+	    sb->dsp.version.major, sb->dsp.version.minor);
+
+	ret = ddf_fun_bind(dsp_fun);
+	if (ret != EOK) {
+		ddf_log_error(
+		    "Failed to bind PCM function: %s.", str_error(ret));
+		// TODO implanted data
+		ddf_fun_destroy(dsp_fun);
+		return ret;
+	}
+
+	ret = ddf_fun_add_to_category(dsp_fun, "audio-pcm");
+	if (ret != EOK) {
+		ddf_log_error("Failed register PCM function in category: %s.",
+		    str_error(ret));
+		ddf_fun_unbind(dsp_fun);
+		// TODO implanted data
+		ddf_fun_destroy(dsp_fun);
+		return ret;
+	}
+
+	/* Initialize mixer */
+	const sb_mixer_type_t mixer_type = sb_mixer_type_by_dsp_version(
+	    sb->dsp.version.major, sb->dsp.version.minor);
+
+	ddf_fun_t *mixer_fun = ddf_fun_create(dev, fun_exposed, "control");
+	if (!mixer_fun) {
+		ddf_log_error("Failed to create mixer function.");
+		ddf_fun_unbind(dsp_fun);
+		// TODO implanted data
+		ddf_fun_destroy(dsp_fun);
+		return ENOMEM;
+	}
+	ret = sb_mixer_init(&sb->mixer, sb->regs, mixer_type);
+	if (ret != EOK) {
+		ddf_log_error("Failed to initialize SB mixer: %s.",
+		    str_error(ret));
+		ddf_fun_unbind(dsp_fun);
+		// TODO implanted data
+		ddf_fun_destroy(dsp_fun);
+		ddf_fun_destroy(mixer_fun);
+		return ret;
+	}
+
+	ddf_log_note("Initialized mixer: %s.",
+	    sb_mixer_type_str(sb->mixer.type));
+	ddf_fun_data_implant(mixer_fun, &sb->mixer);
+	ddf_fun_set_ops(mixer_fun, &sb_mixer_ops);
+
+	ret = ddf_fun_bind(mixer_fun);
+	if (ret != EOK) {
+		ddf_log_error(
+		    "Failed to bind mixer function: %s.", str_error(ret));
+		// TODO implanted data
+		ddf_fun_destroy(mixer_fun);
+
+		ddf_fun_unbind(dsp_fun);
+		// TODO implanted data
+		ddf_fun_destroy(dsp_fun);
+		return ret;
+	}
+
+	return EOK;
+}
+
+int sb16_init_mpu(sb16_t *sb, addr_range_t *regs)
+{
+	sb->mpu_regs = NULL;
+	return ENOTSUP;
+}
+
+void sb16_interrupt(sb16_t *sb)
+{
+	assert(sb);
+	/* The acknowledgment of interrupts on DSP version 4.xx is different;
+	 * It can contain MPU-401 indicator and DMA16 transfers are acked
+	 * differently */
+	if (sb->dsp.version.major >= 4) {
+		pio_write_8(&sb->regs->mixer_address, MIXER_IRQ_STATUS_ADDRESS);
+		const uint8_t irq_mask = pio_read_8(&sb->regs->mixer_data);
+		/* Third bit is MPU-401 interrupt */
+		if (irq_mask & 0x4) {
+			return;
+		}
+	} else {
+		ddf_log_debug("SB16 interrupt.");
+	}
+	sb_dsp_interrupt(&sb->dsp);
+}
Index: uspace/drv/audio/sb16/sb16.h
===================================================================
--- uspace/drv/audio/sb16/sb16.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/sb16.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 drvaudiosb16
+ * @{
+ */
+/** @file
+ * @brief SB16 main structure combining all functionality
+ */
+#ifndef DRV_AUDIO_SB16_SB16_H
+#define DRV_AUDIO_SB16_SB16_H
+
+#include <ddf/driver.h>
+#include <ddi.h>
+#include <device/hw_res_parsed.h>
+
+#include "dsp.h"
+#include "mixer.h"
+#include "registers.h"
+
+typedef struct sb16 {
+	sb16_regs_t *regs;
+	mpu_regs_t *mpu_regs;
+	sb_dsp_t dsp;
+	sb_mixer_t mixer;
+} sb16_t;
+
+size_t sb16_irq_code_size(void);
+void sb16_irq_code(addr_range_t *regs, int dma8, int dma16, irq_cmd_t cmds[], irq_pio_range_t ranges[]);
+int sb16_init_sb16(sb16_t *sb, addr_range_t *regs, ddf_dev_t *dev, int dma8, int dma16);
+int sb16_init_mpu(sb16_t *sb, addr_range_t *regs);
+void sb16_interrupt(sb16_t *sb);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/drv/audio/sb16/sb16.ma
===================================================================
--- uspace/drv/audio/sb16/sb16.ma	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/audio/sb16/sb16.ma	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,1 @@
+100 isa/sb16
Index: uspace/drv/block/ahci/ahci.c
===================================================================
--- uspace/drv/block/ahci/ahci.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/block/ahci/ahci.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -119,6 +119,6 @@
 static int ahci_identify_device(sata_dev_t *);
 static int ahci_set_highest_ultra_dma_mode(sata_dev_t *);
-static int ahci_rb_fpdma(sata_dev_t *, void *, uint64_t);
-static int ahci_wb_fpdma(sata_dev_t *, void *, uint64_t);
+static int ahci_rb_fpdma(sata_dev_t *, uintptr_t, uint64_t);
+static int ahci_wb_fpdma(sata_dev_t *, uintptr_t, uint64_t);
 
 static void ahci_sata_devices_create(ahci_dev_t *, ddf_dev_t *);
@@ -233,8 +233,8 @@
 	sata_dev_t *sata = fun_sata_dev(fun);
 	
-	void *phys;
+	uintptr_t phys;
 	void *ibuf;
-	int rc = dmamem_map_anonymous(sata->block_size, AS_AREA_READ | AS_AREA_WRITE,
-	    0, &phys, (void **) &ibuf);
+	int rc = dmamem_map_anonymous(sata->block_size, DMAMEM_4GiB,
+	    AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &ibuf);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Cannot allocate read buffer.");
@@ -276,8 +276,8 @@
 	sata_dev_t *sata = fun_sata_dev(fun);
 	
-	void *phys;
+	uintptr_t phys;
 	void *ibuf;
-	int rc = dmamem_map_anonymous(sata->block_size, AS_AREA_READ | AS_AREA_WRITE,
-	    0, &phys, (void **) &ibuf);
+	int rc = dmamem_map_anonymous(sata->block_size, DMAMEM_4GiB,
+	    AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &ibuf);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Cannot allocate write buffer.");
@@ -336,5 +336,5 @@
  *
  */
-static void ahci_identify_device_cmd(sata_dev_t *sata, void *phys)
+static void ahci_identify_device_cmd(sata_dev_t *sata, uintptr_t phys)
 {
 	volatile sata_std_command_frame_t *cmd =
@@ -381,5 +381,5 @@
  *
  */
-static void ahci_identify_packet_device_cmd(sata_dev_t *sata, void *phys)
+static void ahci_identify_packet_device_cmd(sata_dev_t *sata, uintptr_t phys)
 {
 	volatile sata_std_command_frame_t *cmd =
@@ -435,8 +435,9 @@
 	}
 	
-	void *phys;
+	uintptr_t phys;
 	sata_identify_data_t *idata;
 	int rc = dmamem_map_anonymous(SATA_IDENTIFY_DEVICE_BUFFER_LENGTH,
-	    AS_AREA_READ | AS_AREA_WRITE, 0, &phys, (void **) &idata);
+	    DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &phys,
+	    (void **) &idata);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Cannot allocate buffer to identify device.");
@@ -561,5 +562,5 @@
  *
  */
-static void ahci_set_mode_cmd(sata_dev_t *sata, void* phys, uint8_t mode)
+static void ahci_set_mode_cmd(sata_dev_t *sata, uintptr_t phys, uint8_t mode)
 {
 	volatile sata_std_command_frame_t *cmd =
@@ -567,5 +568,5 @@
 	
 	cmd->fis_type = SATA_CMD_FIS_TYPE;
-	cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR; 
+	cmd->c = SATA_CMD_FIS_COMMAND_INDICATOR;
 	cmd->command = 0xef;
 	cmd->features = 0x03;
@@ -628,8 +629,9 @@
 	}
 	
-	void *phys;
+	uintptr_t phys;
 	sata_identify_data_t *idata;
 	int rc = dmamem_map_anonymous(SATA_SET_FEATURE_BUFFER_LENGTH,
-	    AS_AREA_READ | AS_AREA_WRITE, 0, &phys, (void **) &idata);
+	    DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &phys,
+	    (void **) &idata);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Cannot allocate buffer for device set mode.");
@@ -677,5 +679,6 @@
  *
  */
-static void ahci_rb_fpdma_cmd(sata_dev_t *sata, void *phys, uint64_t blocknum)
+static void ahci_rb_fpdma_cmd(sata_dev_t *sata, uintptr_t phys,
+    uint64_t blocknum)
 {
 	volatile sata_ncq_command_frame_t *cmd =
@@ -734,5 +737,5 @@
  *
  */
-static int ahci_rb_fpdma(sata_dev_t *sata, void *phys, uint64_t blocknum)
+static int ahci_rb_fpdma(sata_dev_t *sata, uintptr_t phys, uint64_t blocknum)
 {
 	if (sata->is_invalid_device) {
@@ -763,5 +766,6 @@
  *
  */
-static void ahci_wb_fpdma_cmd(sata_dev_t *sata, void *phys, uint64_t blocknum)
+static void ahci_wb_fpdma_cmd(sata_dev_t *sata, uintptr_t phys,
+    uint64_t blocknum)
 {
 	volatile sata_ncq_command_frame_t *cmd =
@@ -821,5 +825,5 @@
  *
  */
-static int ahci_wb_fpdma(sata_dev_t *sata, void *phys, uint64_t blocknum)
+static int ahci_wb_fpdma(sata_dev_t *sata, uintptr_t phys, uint64_t blocknum)
 {
 	if (sata->is_invalid_device) {
@@ -933,5 +937,5 @@
 {
 	size_t size = 4096;
-	void *phys = NULL;
+	uintptr_t phys = 0;
 	void *virt_fb = NULL;
 	void *virt_cmd = NULL;
@@ -949,6 +953,6 @@
 	
 	/* Allocate and init retfis structure. */
-	int rc = dmamem_map_anonymous(size, AS_AREA_READ | AS_AREA_WRITE, 0,
-	    &phys, &virt_fb);
+	int rc = dmamem_map_anonymous(size, DMAMEM_4GiB,
+	    AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &virt_fb);
 	if (rc != EOK)
 		goto error_retfis;
@@ -959,6 +963,6 @@
 	
 	/* Allocate and init command header structure. */
-	rc = dmamem_map_anonymous(size, AS_AREA_READ | AS_AREA_WRITE, 0,
-	    &phys, &virt_cmd);
+	rc = dmamem_map_anonymous(size, DMAMEM_4GiB,
+	    AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &virt_cmd);
 	if (rc != EOK)
 		goto error_cmd;
@@ -970,6 +974,6 @@
 	
 	/* Allocate and init command table structure. */
-	rc = dmamem_map_anonymous(size, AS_AREA_READ | AS_AREA_WRITE, 0,
-	    &phys, &virt_table);
+	rc = dmamem_map_anonymous(size, DMAMEM_4GiB,
+	    AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &virt_table);
 	if (rc != EOK)
 		goto error_table;
@@ -1153,5 +1157,5 @@
 	ahci->memregs = NULL;
 	
-	physmem_map((void *) (size_t) (hw_res_parsed.mem_ranges.ranges[0].address),
+	physmem_map(RNGABS(hw_res_parsed.mem_ranges.ranges[0]),
 	    AHCI_MEMREGS_PAGES_COUNT, AS_AREA_READ | AS_AREA_WRITE,
 	    (void **) &ahci->memregs);
@@ -1160,5 +1164,5 @@
 	
 	/* Register interrupt handler */
-	ahci_ranges[0].base = (size_t) hw_res_parsed.mem_ranges.ranges[0].address;
+	ahci_ranges[0].base = RNGABS(hw_res_parsed.mem_ranges.ranges[0]);
 	ahci_ranges[0].size = sizeof(ahci_memregs_t);
 	
@@ -1167,5 +1171,5 @@
 		
 		ahci_cmds[base].addr =
-		    ((uint32_t *) (size_t) hw_res_parsed.mem_ranges.ranges[0].address) +
+		    ((uint32_t *) RNGABSPTR(hw_res_parsed.mem_ranges.ranges[0])) +
 		    AHCI_PORTS_REGISTERS_OFFSET + port * AHCI_PORT_REGISTERS_SIZE +
 		    AHCI_PORT_IS_REGISTER_OFFSET;
@@ -1173,5 +1177,5 @@
 		
 		ahci_cmds[base + 3].addr =
-		    ((uint32_t *) (size_t) hw_res_parsed.mem_ranges.ranges[0].address) +
+		    ((uint32_t *) RNGABSPTR(hw_res_parsed.mem_ranges.ranges[0])) +
 		    AHCI_GHC_IS_REGISTER_OFFSET;
 		ahci_cmds[base + 4].addr = ahci_cmds[base + 3].addr;
Index: uspace/drv/block/ata_bd/ata_bd.c
===================================================================
--- uspace/drv/block/ata_bd/ata_bd.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/block/ata_bd/ata_bd.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1132,5 +1132,4 @@
 	cnt = 100;
 	while ((status & ~n_reset) != 0 || (status & set) != set) {
-		async_usleep(1);
 		--cnt;
 		if (cnt <= 0) break;
Index: uspace/drv/block/ata_bd/main.c
===================================================================
--- uspace/drv/block/ata_bd/main.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/block/ata_bd/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -84,13 +84,15 @@
 	}
 
-	ata_res->cmd = hw_res.io_ranges.ranges[0].address;
-	ata_res->ctl = hw_res.io_ranges.ranges[1].address;
-
-	if (hw_res.io_ranges.ranges[0].size < sizeof(ata_ctl_t)) {
+	addr_range_t *cmd_rng = &hw_res.io_ranges.ranges[0];
+	addr_range_t *ctl_rng = &hw_res.io_ranges.ranges[1];
+	ata_res->cmd = RNGABS(*cmd_rng);
+	ata_res->ctl = RNGABS(*ctl_rng);
+
+	if (RNGSZ(*ctl_rng) < sizeof(ata_ctl_t)) {
 		rc = EINVAL;
 		goto error;
 	}
 
-	if (hw_res.io_ranges.ranges[1].size < sizeof(ata_cmd_t)) {
+	if (RNGSZ(*cmd_rng) < sizeof(ata_cmd_t)) {
 		rc = EINVAL;
 		goto error;
Index: uspace/drv/bus/isa/i8237.c
===================================================================
--- uspace/drv/bus/isa/i8237.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/isa/i8237.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,4 +38,6 @@
 #include <stdbool.h>
 #include <errno.h>
+#include <ddi.h>
+#include <ddf/log.h>
 #include <fibril_synch.h>
 #include <ddi.h>
@@ -198,69 +200,69 @@
 	.channels = {
 		/* The first chip 8-bit */
-		{
-			(uint8_t *) 0x00,
-			(uint8_t *) 0x01,
-			(uint8_t *) 0x87,
-			(uint8_t *) 0x0a,
-			(uint8_t *) 0x0b,
-			(uint8_t *) 0x0c,
-		},
-		{
-			(uint8_t *) 0x02,
-			(uint8_t *) 0x03,
-			(uint8_t *) 0x83,
-			(uint8_t *) 0x0a,
-			(uint8_t *) 0x0b,
-			(uint8_t *) 0x0c,
-		},
-		{
-			(uint8_t *) 0x04,
-			(uint8_t *) 0x05,
-			(uint8_t *) 0x81,
-			(uint8_t *) 0x0a,
-			(uint8_t *) 0x0b,
-			(uint8_t *) 0x0c,
-		},
-		{
-			(uint8_t *) 0x06,
-			(uint8_t *) 0x07,
-			(uint8_t *) 0x82,
-			(uint8_t *) 0x0a,
-			(uint8_t *) 0x0b,
-			(uint8_t *) 0x0c,
+		{ /* Channel 0 - Unusable*/
+			.offset_reg_address = (uint8_t *) 0x00,
+			.size_reg_address = (uint8_t *) 0x01,
+			.page_reg_address = (uint8_t *) 0x87,
+			.single_mask_address = (uint8_t *) 0x0a,
+			.mode_address = (uint8_t *) 0x0b,
+			.flip_flop_address = (uint8_t *) 0x0c,
+		},
+		{ /* Channel 1 */
+			.offset_reg_address = (uint8_t *) 0x02,
+			.size_reg_address = (uint8_t *) 0x03,
+			.page_reg_address = (uint8_t *) 0x83,
+			.single_mask_address = (uint8_t *) 0x0a,
+			.mode_address = (uint8_t *) 0x0b,
+			.flip_flop_address = (uint8_t *) 0x0c,
+		},
+		{ /* Channel 2 */
+			.offset_reg_address = (uint8_t *) 0x04,
+			.size_reg_address = (uint8_t *) 0x05,
+			.page_reg_address = (uint8_t *) 0x81,
+			.single_mask_address = (uint8_t *) 0x0a,
+			.mode_address = (uint8_t *) 0x0b,
+			.flip_flop_address = (uint8_t *) 0x0c,
+		},
+		{ /* Channel 3 */
+			.offset_reg_address = (uint8_t *) 0x06,
+			.size_reg_address = (uint8_t *) 0x07,
+			.page_reg_address = (uint8_t *) 0x82,
+			.single_mask_address = (uint8_t *) 0x0a,
+			.mode_address = (uint8_t *) 0x0b,
+			.flip_flop_address = (uint8_t *) 0x0c,
 		},
 		
 		/* The second chip 16-bit */
-		{
-			(uint8_t *) 0xc0,
-			(uint8_t *) 0xc2,
-			(uint8_t *) 0x8f,
-			(uint8_t *) 0xd4,
-			(uint8_t *) 0xd6,
-			(uint8_t *) 0xd8,
-		},
-		{
-			(uint8_t *) 0xc4,
-			(uint8_t *) 0xc6,
-			(uint8_t *) 0x8b,
-			(uint8_t *) 0xd4,
-			(uint8_t *) 0xd6,
-			(uint8_t *) 0xd8,
-		},
-		{
-			(uint8_t *) 0xc8,
-			(uint8_t *) 0xca,
-			(uint8_t *) 0x89,
-			(uint8_t *) 0xd4,
-			(uint8_t *) 0xd6,
-			(uint8_t *) 0xd8,
-		},
-		{
-			(uint8_t *) 0xcc,
-			(uint8_t *) 0xce,
-			(uint8_t *) 0x8a,
-			(uint8_t *) 0xd4,
-			(uint8_t *) 0xd6,
-			(uint8_t *) 0xd8,
+		{ /* Channel 4 - Unusable */
+			.offset_reg_address = (uint8_t *) 0xc0,
+			.size_reg_address = (uint8_t *) 0xc2,
+			.page_reg_address = (uint8_t *) 0x8f,
+			.single_mask_address = (uint8_t *) 0xd4,
+			.mode_address = (uint8_t *) 0xd6,
+			.flip_flop_address = (uint8_t *) 0xd8,
+		},
+		{ /* Channel 5 */
+			.offset_reg_address = (uint8_t *) 0xc4,
+			.size_reg_address = (uint8_t *) 0xc6,
+			.page_reg_address = (uint8_t *) 0x8b,
+			.single_mask_address = (uint8_t *) 0xd4,
+			.mode_address = (uint8_t *) 0xd6,
+			.flip_flop_address = (uint8_t *) 0xd8,
+		},
+		{ /* Channel 6 */
+			.offset_reg_address = (uint8_t *) 0xc8,
+			.size_reg_address = (uint8_t *) 0xca,
+			.page_reg_address = (uint8_t *) 0x89,
+			.single_mask_address = (uint8_t *) 0xd4,
+			.mode_address = (uint8_t *) 0xd6,
+			.flip_flop_address = (uint8_t *) 0xd8,
+		},
+		{ /* Channel 7 */
+			.offset_reg_address = (uint8_t *) 0xcc,
+			.size_reg_address = (uint8_t *) 0xce,
+			.page_reg_address = (uint8_t *) 0x8a,
+			.single_mask_address = (uint8_t *) 0xd4,
+			.mode_address = (uint8_t *) 0xd6,
+			.flip_flop_address = (uint8_t *) 0xd8,
 		},
 	},
@@ -272,5 +274,5 @@
 };
 
-/* Initialize I/O access to DMA controller I/O ports.
+/** Initialize I/O access to DMA controller I/O ports.
  *
  * @param controller DMA Controller structure to initialize.
@@ -304,4 +306,22 @@
 	
 	return EOK;
+}
+
+/** Helper function. Channels 4,5,6, and 7 are 8 bit DMA.
+ * @pram channel DMA channel.
+ * @reutrn True, if channel is 4,5,6, or 7, false otherwise.
+ */
+static inline bool is_dma16(unsigned channel)
+{
+	return (channel >= 4) && (channel < 8);
+}
+
+/** Helper function. Channels 0,1,2, and 3 are 8 bit DMA.
+ * @pram channel DMA channel.
+ * @reutrn True, if channel is 0,1,2, or 3, false otherwise.
+ */
+static inline bool is_dma8(unsigned channel)
+{
+	return (channel < 4);
 }
 
@@ -320,14 +340,13 @@
  *
  * @return Error code.
- *
- */
-int dma_setup_channel(unsigned int channel, uint32_t pa, uint16_t size,
+ */
+int dma_channel_setup(unsigned int channel, uint32_t pa, uint32_t size,
     uint8_t mode)
 {
+	if (!is_dma8(channel) && !is_dma16(channel))
+		return ENOENT;
+
 	if ((channel == 0) || (channel == 4))
 		return ENOTSUP;
-	
-	if (channel > 7)
-		return ENOENT;
 	
 	/* DMA is limited to 24bit addresses. */
@@ -336,5 +355,9 @@
 	
 	/* 8 bit channels use only 4 bits from the page register. */
-	if ((channel > 0) && (channel < 4) && (pa >= (1 << 20)))
+	if (is_dma8(channel) && (pa >= (1 << 20)))
+		return EINVAL;
+
+	/* Buffers cannot cross 64K page boundaries */
+	if ((pa & 0xffff0000) != ((pa + size - 1) & 0xffff0000))
 		return EINVAL;
 	
@@ -352,5 +375,5 @@
 	ddf_msg(LVL_DEBUG, "Unspoiled address %#" PRIx32 " (size %" PRIu16 ")",
 	    pa, size);
-	if (channel > 4) {
+	if (is_dma16(channel)) {
 		/* Size must be aligned to 16 bits */
 		if ((size & 1) != 0) {
@@ -358,7 +381,6 @@
 			return EINVAL;
 		}
-		
+		/* Size is in 2byte words */
 		size >>= 1;
-		
 		/* Address is fun: lower 16 bits need to be shifted by 1 */
 		pa = ((pa & 0xffff) >> 1) | (pa & 0xff0000);
@@ -426,4 +448,51 @@
 }
 
+/** Query remaining buffer size.
+ *
+ * @param channel DMA Channel 1, 2, 3 for 8 bit transfers,
+ *                    5, 6, 7 for 16 bit.
+ * @param size    Place to store number of bytes pending in the assigned buffer.
+ *
+ * @return Error code.
+ */
+int dma_channel_remain(unsigned channel, size_t *size)
+{
+	assert(size);
+	if (!is_dma8(channel) && !is_dma16(channel))
+		return ENOENT;
+	
+	if ((channel == 0) || (channel == 4))
+		return ENOTSUP;
+	
+	fibril_mutex_lock(&guard);
+	if (!controller_8237.initialized) {
+		fibril_mutex_unlock(&guard);
+		return EIO;
+	}
+
+	const dma_channel_t dma_channel = controller_8237.channels[channel];
+	/* Get size - reset flip-flop */
+	pio_write_8(dma_channel.flip_flop_address, 0);
+	
+	/* Low byte */
+	const uint8_t value_low = pio_read_8(dma_channel.size_reg_address);
+	ddf_msg(LVL_DEBUG2, "Read size low byte: %p:%x.",
+	    dma_channel.size_reg_address, value_low);
+	
+	/* High byte */
+	const uint8_t value_high = pio_read_8(dma_channel.size_reg_address);
+	ddf_msg(LVL_DEBUG2, "Read size high byte: %p:%x.",
+	    dma_channel.size_reg_address, value_high);
+	fibril_mutex_unlock(&guard);
+
+	uint16_t remain = (value_high << 8 | value_low) ;
+	/* 16 bit DMA size is in words,
+	 * the upper bits are bogus for 16bit transfers so we need to get
+	 * rid of them. Using limited type works well.*/
+	if (is_dma16(channel))
+		remain <<= 1;
+	*size =  is_dma16(channel) ? remain + 2: remain + 1;
+	return EOK;
+}
 /**
  * @}
Index: uspace/drv/bus/isa/i8237.h
===================================================================
--- uspace/drv/bus/isa/i8237.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/isa/i8237.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,5 +38,6 @@
 #define DRV_BUS_ISA_I8237_H
 
-extern int dma_setup_channel(unsigned int, uint32_t, uint16_t, uint8_t);
+extern int dma_channel_setup(unsigned, uint32_t, uint32_t, uint8_t);
+extern int dma_channel_remain(unsigned, size_t *);
 
 #endif
Index: uspace/drv/bus/isa/isa.c
===================================================================
--- uspace/drv/bus/isa/isa.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/isa/isa.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -65,6 +65,8 @@
 #include <ddf/log.h>
 #include <ops/hw_res.h>
+#include <ops/pio_window.h>
 
 #include <device/hw_res.h>
+#include <device/pio_window.h>
 
 #include "i8237.h"
@@ -79,4 +81,5 @@
 	ddf_dev_t *dev;
 	ddf_fun_t *fctl;
+	pio_window_t pio_win;
 	list_t functions;
 } isa_bus_t;
@@ -85,4 +88,5 @@
 	fibril_mutex_t mutex;
 	ddf_fun_t *fnode;
+	hw_resource_t resources[ISA_MAX_HW_RES];
 	hw_resource_list_t hw_resources;
 	link_t bus_link;
@@ -101,17 +105,18 @@
 }
 
-static hw_resource_list_t *isa_get_fun_resources(ddf_fun_t *fnode)
+static hw_resource_list_t *isa_fun_get_resources(ddf_fun_t *fnode)
 {
 	isa_fun_t *fun = isa_fun(fnode);
-	assert(fun != NULL);
+	assert(fun);
 
 	return &fun->hw_resources;
 }
 
-static bool isa_enable_fun_interrupt(ddf_fun_t *fnode)
+static bool isa_fun_enable_interrupt(ddf_fun_t *fnode)
 {
 	/* This is an old ugly way, copied from pci driver */
 	assert(fnode);
 	isa_fun_t *fun = isa_fun(fnode);
+	assert(fun);
 
 	sysarg_t apic;
@@ -151,32 +156,73 @@
 }
 
-static int isa_dma_channel_fun_setup(ddf_fun_t *fnode,
-    unsigned int channel, uint32_t pa, uint16_t size, uint8_t mode)
+static int isa_fun_setup_dma(ddf_fun_t *fnode,
+    unsigned int channel, uint32_t pa, uint32_t size, uint8_t mode)
 {
 	assert(fnode);
 	isa_fun_t *fun = isa_fun(fnode);
+	assert(fun);
 	const hw_resource_list_t *res = &fun->hw_resources;
 	assert(res);
-	
-	const unsigned int ch = channel;
+
 	for (size_t i = 0; i < res->count; ++i) {
+		/* Check for assigned channel */
 		if (((res->resources[i].type == DMA_CHANNEL_16) &&
-		    (res->resources[i].res.dma_channel.dma16 == ch)) ||
+		    (res->resources[i].res.dma_channel.dma16 == channel)) ||
 		    ((res->resources[i].type == DMA_CHANNEL_8) &&
-		    (res->resources[i].res.dma_channel.dma8 == ch))) {
-			return dma_setup_channel(channel, pa, size, mode);
-		}
-	}
-	
+		    (res->resources[i].res.dma_channel.dma8 == channel))) {
+			return dma_channel_setup(channel, pa, size, mode);
+		}
+	}
+
 	return EINVAL;
 }
 
+static int isa_fun_remain_dma(ddf_fun_t *fnode,
+    unsigned channel, size_t *size)
+{
+	assert(size);
+	assert(fnode);
+	isa_fun_t *fun = isa_fun(fnode);
+	assert(fun);
+	const hw_resource_list_t *res = &fun->hw_resources;
+	assert(res);
+
+	for (size_t i = 0; i < res->count; ++i) {
+		/* Check for assigned channel */
+		if (((res->resources[i].type == DMA_CHANNEL_16) &&
+		    (res->resources[i].res.dma_channel.dma16 == channel)) ||
+		    ((res->resources[i].type == DMA_CHANNEL_8) &&
+		    (res->resources[i].res.dma_channel.dma8 == channel))) {
+			return dma_channel_remain(channel, size);
+		}
+	}
+
+	return EINVAL;
+}
+
 static hw_res_ops_t isa_fun_hw_res_ops = {
-	.get_resource_list = isa_get_fun_resources,
-	.enable_interrupt = isa_enable_fun_interrupt,
-	.dma_channel_setup = isa_dma_channel_fun_setup,
+	.get_resource_list = isa_fun_get_resources,
+	.enable_interrupt = isa_fun_enable_interrupt,
+	.dma_channel_setup = isa_fun_setup_dma,
+	.dma_channel_remain = isa_fun_remain_dma,
 };
 
-static ddf_dev_ops_t isa_fun_ops;
+static pio_window_t *isa_fun_get_pio_window(ddf_fun_t *fnode)
+{
+	ddf_dev_t *dev = ddf_fun_get_dev(fnode);
+	isa_bus_t *isa = isa_bus(dev);
+	assert(isa);
+
+	return &isa->pio_win;
+}
+
+static pio_window_ops_t isa_fun_pio_window_ops = {
+	.get_pio_window = isa_fun_get_pio_window
+};
+
+static ddf_dev_ops_t isa_fun_ops= {
+	.interfaces[HW_RES_DEV_IFACE] = &isa_fun_hw_res_ops,
+	.interfaces[PIO_WINDOW_DEV_IFACE] = &isa_fun_pio_window_ops,
+};
 
 static int isa_dev_add(ddf_dev_t *dev);
@@ -212,4 +258,6 @@
 
 	fibril_mutex_initialize(&fun->mutex);
+	fun->hw_resources.resources = fun->resources;
+
 	fun->fnode = fnode;
 	return fun;
@@ -270,7 +318,7 @@
 {
 	char *line = str;
+	*next = NULL;
 
 	if (str == NULL) {
-		*next = NULL;
 		return NULL;
 	}
@@ -282,6 +330,4 @@
 	if (*str != '\0') {
 		*next = str + 1;
-	} else {
-		*next = NULL;
 	}
 
@@ -310,18 +356,8 @@
 	/* Get the name part of the rest of the line. */
 	strtok(line, ":");
-
-	/* Allocate output buffer. */
-	size_t size = str_size(line) + 1;
-	char *name = malloc(size);
-
-	if (name != NULL) {
-		/* Copy the result to the output buffer. */
-		str_cpy(name, size, line);
-	}
-
-	return name;
-}
-
-static inline char *skip_spaces(char *line)
+	return line;
+}
+
+static inline const char *skip_spaces(const char *line)
 {
 	/* Skip leading spaces. */
@@ -332,5 +368,5 @@
 }
 
-static void isa_fun_set_irq(isa_fun_t *fun, int irq)
+static void isa_fun_add_irq(isa_fun_t *fun, int irq)
 {
 	size_t count = fun->hw_resources.count;
@@ -348,5 +384,5 @@
 }
 
-static void isa_fun_set_dma(isa_fun_t *fun, int dma)
+static void isa_fun_add_dma(isa_fun_t *fun, int dma)
 {
 	size_t count = fun->hw_resources.count;
@@ -381,13 +417,17 @@
 }
 
-static void isa_fun_set_io_range(isa_fun_t *fun, size_t addr, size_t len)
+static void isa_fun_add_io_range(isa_fun_t *fun, size_t addr, size_t len)
 {
 	size_t count = fun->hw_resources.count;
 	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.relative = false;
 		resources[count].res.io_range.endianness = LITTLE_ENDIAN;
 
@@ -400,5 +440,5 @@
 }
 
-static void fun_parse_irq(isa_fun_t *fun, char *val)
+static void fun_parse_irq(isa_fun_t *fun, const char *val)
 {
 	int irq = 0;
@@ -409,20 +449,19 @@
 
 	if (val != end)
-		isa_fun_set_irq(fun, irq);
-}
-
-static void fun_parse_dma(isa_fun_t *fun, char *val)
-{
-	unsigned int dma = 0;
+		isa_fun_add_irq(fun, irq);
+}
+
+static void fun_parse_dma(isa_fun_t *fun, const char *val)
+{
 	char *end = NULL;
 	
 	val = skip_spaces(val);
-	dma = (unsigned int) strtol(val, &end, 10);
+	const int dma = strtol(val, &end, 10);
 	
 	if (val != end)
-		isa_fun_set_dma(fun, dma);
-}
-
-static void fun_parse_io_range(isa_fun_t *fun, char *val)
+		isa_fun_add_dma(fun, dma);
+}
+
+static void fun_parse_io_range(isa_fun_t *fun, const char *val)
 {
 	size_t addr, len;
@@ -441,10 +480,10 @@
 		return;
 
-	isa_fun_set_io_range(fun, addr, len);
-}
-
-static void get_match_id(char **id, char *val)
-{
-	char *end = val;
+	isa_fun_add_io_range(fun, addr, len);
+}
+
+static void get_match_id(char **id, const char *val)
+{
+	const char *end = val;
 
 	while (!isspace(*end))
@@ -456,14 +495,12 @@
 }
 
-static void fun_parse_match_id(isa_fun_t *fun, char *val)
+static void fun_parse_match_id(isa_fun_t *fun, const char *val)
 {
 	char *id = NULL;
-	int score = 0;
 	char *end = NULL;
-	int rc;
 
 	val = skip_spaces(val);
 
-	score = (int)strtol(val, &end, 10);
+	int score = (int)strtol(val, &end, 10);
 	if (val == end) {
 		ddf_msg(LVL_ERROR, "Cannot read match score for function "
@@ -483,5 +520,5 @@
 	    "function %s", id, score, ddf_fun_get_name(fun->fnode));
 
-	rc = ddf_fun_add_match_id(fun->fnode, id, score);
+	int rc = ddf_fun_add_match_id(fun->fnode, id, score);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed adding match ID: %s",
@@ -492,6 +529,6 @@
 }
 
-static bool prop_parse(isa_fun_t *fun, char *line, const char *prop,
-    void (*read_fn)(isa_fun_t *, char *))
+static bool prop_parse(isa_fun_t *fun, const char *line, const char *prop,
+    void (*read_fn)(isa_fun_t *, const char *))
 {
 	size_t proplen = str_size(prop);
@@ -508,5 +545,5 @@
 }
 
-static void fun_prop_parse(isa_fun_t *fun, char *line)
+static void fun_prop_parse(isa_fun_t *fun, const char *line)
 {
 	/* Skip leading spaces. */
@@ -523,23 +560,10 @@
 }
 
-static void fun_hw_res_alloc(isa_fun_t *fun)
-{
-	fun->hw_resources.resources =
-	    (hw_resource_t *) malloc(sizeof(hw_resource_t) * ISA_MAX_HW_RES);
-}
-
-static void fun_hw_res_free(isa_fun_t *fun)
-{
-	free(fun->hw_resources.resources);
-	fun->hw_resources.resources = NULL;
-}
-
 static char *isa_fun_read_info(char *fun_conf, isa_bus_t *isa)
 {
 	char *line;
-	char *fun_name = NULL;
 
 	/* Skip empty lines. */
-	while (true) {
+	do {
 		line = str_get_line(fun_conf, &fun_conf);
 
@@ -549,21 +573,15 @@
 		}
 
-		if (!line_empty(line))
-			break;
-	}
+	} while (line_empty(line));
 
 	/* Get device name. */
-	fun_name = get_device_name(line);
+	const char *fun_name = get_device_name(line);
 	if (fun_name == NULL)
 		return NULL;
 
 	isa_fun_t *fun = isa_fun_create(isa, fun_name);
-	free(fun_name);
 	if (fun == NULL) {
 		return NULL;
 	}
-
-	/* Allocate buffer for the list of hardware resources of the device. */
-	fun_hw_res_alloc(fun);
 
 	/* Get properties of the device (match ids, irq and io range). */
@@ -596,30 +614,22 @@
 }
 
-static void fun_conf_parse(char *conf, isa_bus_t *isa)
-{
+static void isa_functions_add(isa_bus_t *isa)
+{
+	char *conf = fun_conf_read(CHILD_FUN_CONF_PATH);
 	while (conf != NULL && *conf != '\0') {
 		conf = isa_fun_read_info(conf, isa);
 	}
-}
-
-static void isa_functions_add(isa_bus_t *isa)
-{
-	char *fun_conf;
-
-	fun_conf = fun_conf_read(CHILD_FUN_CONF_PATH);
-	if (fun_conf != NULL) {
-		fun_conf_parse(fun_conf, isa);
-		free(fun_conf);
-	}
+	free(conf);
 }
 
 static int isa_dev_add(ddf_dev_t *dev)
 {
-	isa_bus_t *isa;
+	async_sess_t *sess;
+	int rc;
 
 	ddf_msg(LVL_DEBUG, "isa_dev_add, device handle = %d",
 	    (int) ddf_dev_get_handle(dev));
 
-	isa = ddf_dev_data_alloc(dev, sizeof(isa_bus_t));
+	isa_bus_t *isa = ddf_dev_data_alloc(dev, sizeof(isa_bus_t));
 	if (isa == NULL)
 		return ENOMEM;
@@ -628,4 +638,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. */
@@ -658,5 +682,4 @@
 {
 	isa_bus_t *isa = isa_bus(dev);
-	int rc;
 
 	fibril_mutex_lock(&isa->mutex);
@@ -666,5 +689,5 @@
 		    isa_fun_t, bus_link);
 
-		rc = ddf_fun_offline(fun->fnode);
+		int rc = ddf_fun_offline(fun->fnode);
 		if (rc != EOK) {
 			fibril_mutex_unlock(&isa->mutex);
@@ -682,5 +705,4 @@
 		list_remove(&fun->bus_link);
 
-		fun_hw_res_free(fun);
 		ddf_fun_destroy(fun->fnode);
 	}
@@ -709,15 +731,8 @@
 }
 
-
-static void isa_init()
-{
+int main(int argc, char *argv[])
+{
+	printf(NAME ": HelenOS ISA bus driver\n");
 	ddf_log_init(NAME);
-	isa_fun_ops.interfaces[HW_RES_DEV_IFACE] = &isa_fun_hw_res_ops;
-}
-
-int main(int argc, char *argv[])
-{
-	printf(NAME ": HelenOS ISA bus driver\n");
-	isa_init();
 	return ddf_driver_main(&isa_driver);
 }
Index: uspace/drv/bus/pci/pciintel/pci.c
===================================================================
--- uspace/drv/bus/pci/pciintel/pci.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/pci/pciintel/pci.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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
 };
@@ -233,23 +250,28 @@
 static void pci_conf_read(pci_fun_t *fun, int reg, uint8_t *buf, size_t len)
 {
+	const uint32_t conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
 	pci_bus_t *bus = pci_bus_from_fun(fun);
+	uint32_t val;
 	
 	fibril_mutex_lock(&bus->conf_mutex);
-	
-	const uint32_t conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
-	void *addr = bus->conf_data_port + (reg & 3);
-	
-	pio_write_32(bus->conf_addr_port, host2uint32_t_le(conf_addr));
-	
+
+	pio_write_32(bus->conf_addr_reg, host2uint32_t_le(conf_addr));
+
+	/*
+	 * Always read full 32-bits from the PCI conf_data_port register and
+	 * get the desired portion of it afterwards. Some architectures do not
+	 * support shorter PIO reads offset from this register.
+ 	 */
+	val = uint32_t_le2host(pio_read_32(bus->conf_data_reg));
+
 	switch (len) {
 	case 1:
-		/* No endianness change for 1 byte */
-		buf[0] = pio_read_8(addr);
+		*buf = (uint8_t) (val >> ((reg & 3) * 8));
 		break;
 	case 2:
-		((uint16_t *) buf)[0] = uint16_t_le2host(pio_read_16(addr));
+		*((uint16_t *) buf) = (uint16_t) (val >> ((reg & 3)) * 8);
 		break;
 	case 4:
-		((uint32_t *) buf)[0] = uint32_t_le2host(pio_read_32(addr));
+		*((uint32_t *) buf) = (uint32_t) val;
 		break;
 	}
@@ -260,25 +282,41 @@
 static void pci_conf_write(pci_fun_t *fun, int reg, uint8_t *buf, size_t len)
 {
+	const uint32_t conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
 	pci_bus_t *bus = pci_bus_from_fun(fun);
+	uint32_t val;
 	
 	fibril_mutex_lock(&bus->conf_mutex);
-	
-	const uint32_t conf_addr = CONF_ADDR(fun->bus, fun->dev, fun->fn, reg);
-	void *addr = bus->conf_data_port + (reg & 3);
-	
-	pio_write_32(bus->conf_addr_port, host2uint32_t_le(conf_addr));
+
+	/*
+	 * Prepare to write full 32-bits to the PCI conf_data_port register.
+	 * Some architectures do not support shorter PIO writes offset from this
+	 * register.
+ 	 */
+
+	if (len < 4) {
+		/*
+ 		 * We have fewer than full 32-bits, so we need to read the
+ 		 * missing bits first.
+ 		 */
+		pio_write_32(bus->conf_addr_reg, host2uint32_t_le(conf_addr));
+		val = uint32_t_le2host(pio_read_32(bus->conf_data_reg));
+	}
 	
 	switch (len) {
 	case 1:
-		/* No endianness change for 1 byte */
-		pio_write_8(addr, buf[0]);
+		val &= ~(0xffU << ((reg & 3) * 8));
+		val |= *buf << ((reg & 3) * 8);
 		break;
 	case 2:
-		pio_write_16(addr, host2uint16_t_le(((uint16_t *) buf)[0]));
+		val &= ~(0xffffU << ((reg & 3) * 8));
+		val |= *((uint16_t *) buf) << ((reg & 3) * 8);
 		break;
 	case 4:
-		pio_write_32(addr, host2uint32_t_le(((uint32_t *) buf)[0]));
+		val = *((uint32_t *) buf);
 		break;
 	}
+
+	pio_write_32(bus->conf_addr_reg, host2uint32_t_le(conf_addr));
+	pio_write_32(bus->conf_data_reg, host2uint32_t_le(val));
 	
 	fibril_mutex_unlock(&bus->conf_mutex);
@@ -411,4 +449,5 @@
 		hw_resources[count].res.io_range.address = range_addr;
 		hw_resources[count].res.io_range.size = range_size;
+		hw_resources[count].res.io_range.relative = true;
 		hw_resources[count].res.io_range.endianness = LITTLE_ENDIAN;
 	} else {
@@ -416,4 +455,5 @@
 		hw_resources[count].res.mem_range.address = range_addr;
 		hw_resources[count].res.mem_range.size = range_size;
+		hw_resources[count].res.mem_range.relative = false;
 		hw_resources[count].res.mem_range.endianness = LITTLE_ENDIAN;
 	}
@@ -433,5 +473,8 @@
 {
 	/* Value of the BAR */
-	uint32_t val, mask;
+	uint32_t val;
+	uint32_t bar;
+	uint32_t mask;
+
 	/* IO space address */
 	bool io;
@@ -471,6 +514,14 @@
 	/* Get the address mask. */
 	pci_conf_write_32(fun, addr, 0xffffffff);
-	mask &= pci_conf_read_32(fun, addr);
-	
+	bar = pci_conf_read_32(fun, addr);
+
+	/*
+ 	 * Unimplemented BARs read back as all 0's.
+ 	 */
+	if (!bar)
+		return addr + (addrw64 ? 8 : 4);
+
+	mask &= bar;	
+
 	/* Restore the original value. */
 	pci_conf_write_32(fun, addr, val);
@@ -520,5 +571,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);
 }
@@ -583,4 +636,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);
@@ -613,4 +669,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;
@@ -638,6 +695,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);
@@ -662,17 +724,12 @@
 	    hw_resources.resources[1].res.io_range.address);
 	
-	bus->conf_io_addr =
-	    (uint32_t) hw_resources.resources[0].res.io_range.address;
-	bus->conf_io_data =
-	    (uint32_t) hw_resources.resources[1].res.io_range.address;
-	
-	if (pio_enable((void *)(uintptr_t)bus->conf_io_addr, 4,
-	    &bus->conf_addr_port)) {
+	if (pio_enable_resource(&bus->pio_win, &hw_resources.resources[0],
+	    (void **) &bus->conf_addr_reg)) {
 		ddf_msg(LVL_ERROR, "Failed to enable configuration ports.");
 		rc = EADDRNOTAVAIL;
 		goto fail;
 	}
-	if (pio_enable((void *)(uintptr_t)bus->conf_io_data, 4,
-	    &bus->conf_data_port)) {
+	if (pio_enable_resource(&bus->pio_win, &hw_resources.resources[1],
+	    (void **) &bus->conf_data_reg)) {
 		ddf_msg(LVL_ERROR, "Failed to enable configuration ports.");
 		rc = EADDRNOTAVAIL;
@@ -729,6 +786,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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/pci/pciintel/pci.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -40,13 +40,12 @@
 #include "pci_regs.h"
 
-#define PCI_MAX_HW_RES 8
+#define PCI_MAX_HW_RES 10 
 
 typedef struct pciintel_bus {
 	/** DDF device node */
 	ddf_dev_t *dnode;
-	uint32_t conf_io_addr;
-	uint32_t conf_io_data;
-	void *conf_data_port;
-	void *conf_addr_port;
+	ioport32_t *conf_addr_reg;
+	ioport32_t *conf_data_reg;
+	pio_window_t pio_win;
 	fibril_mutex_t conf_mutex;
 } pci_bus_t;
@@ -68,4 +67,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/bus/usb/ehci/main.c
===================================================================
--- uspace/drv/bus/usb/ehci/main.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/ehci/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -77,9 +77,8 @@
 	assert(device);
 
-	uintptr_t reg_base = 0;
-	size_t reg_size = 0;
+	addr_range_t reg_range;
 	int irq = 0;
 
-	int rc = get_my_registers(device, &reg_base, &reg_size, &irq);
+	int rc = get_my_registers(device, &reg_range, &irq);
 	if (rc != EOK) {
 		usb_log_error("Failed to get memory addresses for %" PRIun
@@ -88,8 +87,8 @@
 	}
 
-	usb_log_info("Memory mapped regs at 0x%" PRIxn " (size %zu), IRQ %d.\n",
-	    reg_base, reg_size, irq);
+	usb_log_info("Memory mapped regs at %p (size %zu), IRQ %d.\n",
+	    RNGABSPTR(reg_range), RNGSZ(reg_range), irq);
 
-	rc = disable_legacy(device, reg_base, reg_size);
+	rc = disable_legacy(device, &reg_range);
 	if (rc != EOK) {
 		usb_log_error("Failed to disable legacy USB: %s.\n",
Index: uspace/drv/bus/usb/ehci/res.c
===================================================================
--- uspace/drv/bus/usb/ehci/res.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/ehci/res.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -71,11 +71,10 @@
  *
  * @param[in] dev Device asking for the addresses.
- * @param[out] mem_reg_address Base address of the memory range.
- * @param[out] mem_reg_size Size of the memory range.
+ * @param[out] mem_regs_p Pointer to the register range.
  * @param[out] irq_no IRQ assigned to the device.
  * @return Error code.
  */
 int get_my_registers(ddf_dev_t *dev,
-    uintptr_t *mem_reg_address, size_t *mem_reg_size, int *irq_no)
+    addr_range_t *mem_regs_p, int *irq_no)
 {
 	assert(dev);
@@ -99,8 +98,6 @@
 	}
 
-	if (mem_reg_address)
-		*mem_reg_address = hw_res.mem_ranges.ranges[0].address;
-	if (mem_reg_size)
-		*mem_reg_size = hw_res.mem_ranges.ranges[0].size;
+	if (mem_regs_p)
+		*mem_regs_p = hw_res.mem_ranges.ranges[0];
 	if (irq_no)
 		*irq_no = hw_res.irqs.irqs[0];
@@ -267,5 +264,5 @@
 }
 
-int disable_legacy(ddf_dev_t *device, uintptr_t reg_base, size_t reg_size)
+int disable_legacy(ddf_dev_t *device, addr_range_t *reg_range)
 {
 	assert(device);
@@ -274,8 +271,8 @@
 	/* Map EHCI registers */
 	void *regs = NULL;
-	int rc = pio_enable((void*)reg_base, reg_size, &regs);
+	int rc = pio_enable_range(reg_range, &regs);
 	if (rc != EOK) {
 		usb_log_error("Failed to map registers %p: %s.\n",
-		    (void *) reg_base, str_error(rc));
+		    RNGABSPTR(*reg_range), str_error(rc));
 		return rc;
 	}
Index: uspace/drv/bus/usb/ehci/res.h
===================================================================
--- uspace/drv/bus/usb/ehci/res.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/ehci/res.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,8 +37,9 @@
 
 #include <ddf/driver.h>
+#include <device/hw_res_parsed.h>
 
-int get_my_registers(ddf_dev_t *, uintptr_t *, size_t *, int *);
+int get_my_registers(ddf_dev_t *, addr_range_t *, int *);
 int enable_interrupts(ddf_dev_t *);
-int disable_legacy(ddf_dev_t *, uintptr_t, size_t);
+int disable_legacy(ddf_dev_t *, addr_range_t *);
 
 #endif
Index: uspace/drv/bus/usb/ohci/hc.c
===================================================================
--- uspace/drv/bus/usb/ohci/hc.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/ohci/hc.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -106,6 +106,5 @@
  * @param[out] cmds Commands buffer.
  * @param[in] cmds_size Size of the commands buffer (bytes).
- * @param[in] regs Physical address of device's registers.
- * @param[in] reg_size Size of the register area (bytes).
+ * @param[in] regs Device's register range.
  *
  * @return Error code.
@@ -113,16 +112,16 @@
 int
 hc_get_irq_code(irq_pio_range_t ranges[], size_t ranges_size, irq_cmd_t cmds[],
-    size_t cmds_size, uintptr_t regs, size_t reg_size)
+    size_t cmds_size, addr_range_t *regs)
 {
 	if ((ranges_size < sizeof(ohci_pio_ranges)) ||
 	    (cmds_size < sizeof(ohci_irq_commands)) ||
-	    (reg_size < sizeof(ohci_regs_t)))
+	    (RNGSZ(*regs) < sizeof(ohci_regs_t)))
 		return EOVERFLOW;
 
 	memcpy(ranges, ohci_pio_ranges, sizeof(ohci_pio_ranges));
-	ranges[0].base = regs;
+	ranges[0].base = RNGABS(*regs);
 
 	memcpy(cmds, ohci_irq_commands, sizeof(ohci_irq_commands));
-	ohci_regs_t *registers = (ohci_regs_t *) regs;
+	ohci_regs_t *registers = (ohci_regs_t *) RNGABSPTR(*regs);
 	cmds[0].addr = (void *) &registers->interrupt_status;
 	cmds[3].addr = (void *) &registers->interrupt_status;
@@ -135,6 +134,5 @@
  *
  * @param[in] device Host controller DDF device
- * @param[in] reg_base Register range base
- * @param[in] reg_size Register range size
+ * @param[in] regs Register range
  * @param[in] irq Interrupt number
  * @paran[in] handler Interrupt handler
@@ -142,6 +140,6 @@
  * @return EOK on success or negative error code
  */
-int hc_register_irq_handler(ddf_dev_t *device, uintptr_t reg_base, size_t reg_size,
-    int irq, interrupt_handler_t handler)
+int hc_register_irq_handler(ddf_dev_t *device, addr_range_t *regs, int irq,
+    interrupt_handler_t handler)
 {
 	int rc;
@@ -158,5 +156,5 @@
 
 	rc = hc_get_irq_code(irq_ranges, sizeof(irq_ranges), irq_cmds,
-	    sizeof(irq_cmds), reg_base, reg_size);
+	    sizeof(irq_cmds), regs);
 	if (rc != EOK) {
 		usb_log_error("Failed to generate IRQ code: %s.\n",
@@ -259,15 +257,13 @@
  *
  * @param[in] instance Memory place for the structure.
- * @param[in] regs Address of the memory mapped I/O registers.
- * @param[in] reg_size Size of the memory mapped area.
+ * @param[in] regs Device's I/O registers range.
  * @param[in] interrupts True if w interrupts should be used
  * @return Error code
  */
-int hc_init(hc_t *instance, uintptr_t regs, size_t reg_size, bool interrupts)
-{
-	assert(instance);
-
-	int rc =
-	    pio_enable((void*)regs, reg_size, (void**)&instance->registers);
+int hc_init(hc_t *instance, addr_range_t *regs, bool interrupts)
+{
+	assert(instance);
+
+	int rc = pio_enable_range(regs, (void **) &instance->registers);
 	if (rc != EOK) {
 		usb_log_error("Failed to gain access to device registers: %s.\n",
Index: uspace/drv/bus/usb/ohci/hc.h
===================================================================
--- uspace/drv/bus/usb/ohci/hc.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/ohci/hc.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -75,9 +75,10 @@
 } hc_t;
 
-int hc_get_irq_code(irq_pio_range_t [], size_t, irq_cmd_t [], size_t, uintptr_t,
-    size_t);
-int hc_register_irq_handler(ddf_dev_t *, uintptr_t, size_t, int, interrupt_handler_t);
+int hc_get_irq_code(irq_pio_range_t [], size_t, irq_cmd_t [], size_t,
+    addr_range_t *);
+int hc_register_irq_handler(ddf_dev_t *, addr_range_t *, int,
+    interrupt_handler_t);
 int hc_register_hub(hc_t *instance, ddf_fun_t *hub_fun);
-int hc_init(hc_t *instance, uintptr_t regs, size_t reg_size, bool interrupts);
+int hc_init(hc_t *instance, addr_range_t *regs, bool interrupts);
 
 /** Safely dispose host controller internal structures
Index: uspace/drv/bus/usb/ohci/ohci.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/ohci/ohci.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -177,9 +177,8 @@
 	ddf_fun_set_ops(instance->rh_fun, &rh_ops);
 
-	uintptr_t reg_base = 0;
-	size_t reg_size = 0;
+	addr_range_t regs;
 	int irq = 0;
 
-	rc = get_my_registers(device, &reg_base, &reg_size, &irq);
+	rc = get_my_registers(device, &regs, &irq);
 	if (rc != EOK) {
 		usb_log_error("Failed to get register memory addresses "
@@ -190,7 +189,7 @@
 
 	usb_log_debug("Memory mapped regs at %p (size %zu), IRQ %d.\n",
-	    (void *) reg_base, reg_size, irq);
-
-	rc = hc_register_irq_handler(device, reg_base, reg_size, irq, irq_handler);
+	    RNGABSPTR(regs), RNGSZ(regs), irq);
+
+	rc = hc_register_irq_handler(device, &regs, irq, irq_handler);
 	if (rc != EOK) {
 		usb_log_error("Failed to register interrupt handler: %s.\n",
@@ -215,5 +214,5 @@
 	}
 
-	rc = hc_init(&instance->hc, reg_base, reg_size, interrupts);
+	rc = hc_init(&instance->hc, &regs, interrupts);
 	if (rc != EOK) {
 		usb_log_error("Failed to init ohci_hcd: %s.\n", str_error(rc));
Index: uspace/drv/bus/usb/ohci/res.c
===================================================================
--- uspace/drv/bus/usb/ohci/res.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/ohci/res.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -48,11 +48,9 @@
  *
  * @param[in] dev Device asking for the addresses.
- * @param[out] mem_reg_address Base address of the memory range.
- * @param[out] mem_reg_size Size of the memory range.
+ * @param[out] p_regs Pointer to register range.
  * @param[out] irq_no IRQ assigned to the device.
  * @return Error code.
  */
-int get_my_registers(ddf_dev_t *dev,
-    uintptr_t *mem_reg_address, size_t *mem_reg_size, int *irq_no)
+int get_my_registers(ddf_dev_t *dev, addr_range_t *p_regs, int *irq_no)
 {
 	assert(dev);
@@ -66,5 +64,5 @@
 	hw_res_list_parsed_t hw_res;
 	hw_res_list_parsed_init(&hw_res);
-	const int ret =  hw_res_get_list_parsed(parent_sess, &hw_res, 0);
+	const int ret = hw_res_get_list_parsed(parent_sess, &hw_res, 0);
 	async_hangup(parent_sess);
 	if (ret != EOK) {
@@ -78,8 +76,6 @@
 	}
 
-	if (mem_reg_address)
-		*mem_reg_address = hw_res.mem_ranges.ranges[0].address;
-	if (mem_reg_size)
-		*mem_reg_size = hw_res.mem_ranges.ranges[0].size;
+	if (p_regs)
+		*p_regs = hw_res.mem_ranges.ranges[0];
 	if (irq_no)
 		*irq_no = hw_res.irqs.irqs[0];
Index: uspace/drv/bus/usb/ohci/res.h
===================================================================
--- uspace/drv/bus/usb/ohci/res.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/ohci/res.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -36,6 +36,7 @@
 
 #include <ddf/driver.h>
+#include <device/hw_res_parsed.h>
 
-int get_my_registers(ddf_dev_t *, uintptr_t *, size_t *, int *);
+int get_my_registers(ddf_dev_t *, addr_range_t *, int *);
 int enable_interrupts(ddf_dev_t *);
 
Index: uspace/drv/bus/usb/uhci/hc.c
===================================================================
--- uspace/drv/bus/usb/uhci/hc.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/uhci/hc.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -105,6 +105,5 @@
  * @param[out] cmds Commands buffer.
  * @param[in] cmds_size Size of the commands buffer (bytes).
- * @param[in] regs Physical address of device's registers.
- * @param[in] reg_size Size of the register area (bytes).
+ * @param[in] regs Device's register range.
  *
  * @return Error code.
@@ -112,16 +111,16 @@
 int
 hc_get_irq_code(irq_pio_range_t ranges[], size_t ranges_size, irq_cmd_t cmds[],
-    size_t cmds_size, uintptr_t regs, size_t reg_size)
+    size_t cmds_size, addr_range_t *regs)
 {
 	if ((ranges_size < sizeof(uhci_irq_pio_ranges)) ||
 	    (cmds_size < sizeof(uhci_irq_commands)) ||
-	    (reg_size < sizeof(uhci_regs_t)))
+	    (RNGSZ(*regs) < sizeof(uhci_regs_t)))
 		return EOVERFLOW;
 
 	memcpy(ranges, uhci_irq_pio_ranges, sizeof(uhci_irq_pio_ranges));
-	ranges[0].base = regs;
+	ranges[0].base = RNGABS(*regs);
 
 	memcpy(cmds, uhci_irq_commands, sizeof(uhci_irq_commands));
-	uhci_regs_t *registers = (uhci_regs_t *) regs;
+	uhci_regs_t *registers = (uhci_regs_t *) RNGABSPTR(*regs);
 	cmds[0].addr = &registers->usbsts;
 	cmds[3].addr = &registers->usbsts;
@@ -133,6 +132,5 @@
  *
  * @param[in] device Host controller DDF device
- * @param[in] reg_base Register range base
- * @param[in] reg_size Register range size
+ * @param[in] regs Register range
  * @param[in] irq Interrupt number
  * @paran[in] handler Interrupt handler
@@ -140,6 +138,6 @@
  * @return EOK on success or negative error code
  */
-int hc_register_irq_handler(ddf_dev_t *device, uintptr_t reg_base, size_t reg_size,
-    int irq, interrupt_handler_t handler)
+int hc_register_irq_handler(ddf_dev_t *device, addr_range_t *regs, int irq,
+    interrupt_handler_t handler)
 {
 	int rc;
@@ -147,5 +145,5 @@
 	irq_cmd_t irq_cmds[hc_irq_cmd_count];
 	rc = hc_get_irq_code(irq_ranges, sizeof(irq_ranges), irq_cmds,
-	    sizeof(irq_cmds), reg_base, reg_size);
+	    sizeof(irq_cmds), regs);
 	if (rc != EOK) {
 		usb_log_error("Failed to generate IRQ commands: %s.\n",
@@ -232,6 +230,5 @@
  *
  * @param[in] instance Memory place to initialize.
- * @param[in] regs Address of I/O control registers.
- * @param[in] reg_size Size of I/O control registers.
+ * @param[in] regs Range of device's I/O control registers.
  * @param[in] interrupts True if hw interrupts should be used.
  * @return Error code.
@@ -241,7 +238,7 @@
  * interrupt fibrils.
  */
-int hc_init(hc_t *instance, void *regs, size_t reg_size, bool interrupts)
-{
-	assert(reg_size >= sizeof(uhci_regs_t));
+int hc_init(hc_t *instance, addr_range_t *regs, bool interrupts)
+{
+	assert(regs->size >= sizeof(uhci_regs_t));
 	int rc;
 
@@ -251,5 +248,5 @@
 	/* allow access to hc control registers */
 	uhci_regs_t *io;
-	rc = pio_enable(regs, reg_size, (void **)&io);
+	rc = pio_enable_range(regs, (void **) &io);
 	if (rc != EOK) {
 		usb_log_error("Failed to gain access to registers at %p: %s.\n",
@@ -260,5 +257,5 @@
 	instance->registers = io;
 	usb_log_debug(
-	    "Device registers at %p (%zuB) accessible.\n", io, reg_size);
+	    "Device registers at %p (%zuB) accessible.\n", io, regs->size);
 
 	rc = hc_init_mem_structures(instance);
Index: uspace/drv/bus/usb/uhci/hc.h
===================================================================
--- uspace/drv/bus/usb/uhci/hc.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/uhci/hc.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,4 +37,5 @@
 
 #include <ddf/interrupt.h>
+#include <device/hw_res_parsed.h>
 #include <fibril.h>
 #include <usb/host/hcd.h>
@@ -120,9 +121,10 @@
 } hc_t;
 
-int hc_register_irq_handler(ddf_dev_t *, uintptr_t, size_t, int, interrupt_handler_t);
-int hc_get_irq_code(irq_pio_range_t [], size_t, irq_cmd_t [], size_t, uintptr_t,
-    size_t);
+int hc_register_irq_handler(ddf_dev_t *, addr_range_t *, int,
+    interrupt_handler_t);
+int hc_get_irq_code(irq_pio_range_t [], size_t, irq_cmd_t [], size_t,
+    addr_range_t *);
 void hc_interrupt(hc_t *instance, uint16_t status);
-int hc_init(hc_t *instance, void *regs, size_t reg_size, bool interupts);
+int hc_init(hc_t *instance, addr_range_t *regs, bool interupts);
 
 /** Safely dispose host controller internal structures
@@ -132,4 +134,5 @@
 static inline void hc_fini(hc_t *instance) {} /* TODO: implement*/
 #endif
+
 /**
  * @}
Index: uspace/drv/bus/usb/uhci/res.c
===================================================================
--- uspace/drv/bus/usb/uhci/res.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/uhci/res.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -46,11 +46,9 @@
  *
  * @param[in] dev Device asking for the addresses.
- * @param[out] io_reg_address Base address of the I/O range.
- * @param[out] io_reg_size Size of the I/O range.
+ * @param[out] io_regs_p Pointer to register I/O range.
  * @param[out] irq_no IRQ assigned to the device.
  * @return Error code.
  */
-int get_my_registers(ddf_dev_t *dev,
-    uintptr_t *io_reg_address, size_t *io_reg_size, int *irq_no)
+int get_my_registers(ddf_dev_t *dev, addr_range_t *io_regs_p, int *irq_no)
 {
 	assert(dev);
@@ -76,8 +74,6 @@
 	}
 
-	if (io_reg_address)
-		*io_reg_address = hw_res.io_ranges.ranges[0].address;
-	if (io_reg_size)
-		*io_reg_size = hw_res.io_ranges.ranges[0].size;
+	if (io_regs_p)
+		*io_regs_p = hw_res.io_ranges.ranges[0];
 	if (irq_no)
 		*irq_no = hw_res.irqs.irqs[0];
Index: uspace/drv/bus/usb/uhci/res.h
===================================================================
--- uspace/drv/bus/usb/uhci/res.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/uhci/res.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,6 +37,7 @@
 
 #include <ddf/driver.h>
+#include <device/hw_res_parsed.h>
 
-int get_my_registers(ddf_dev_t *, uintptr_t *, size_t *, int *);
+int get_my_registers(ddf_dev_t *, addr_range_t *, int *);
 int enable_interrupts(ddf_dev_t *);
 int disable_legacy(ddf_dev_t *);
Index: uspace/drv/bus/usb/uhci/uhci.c
===================================================================
--- uspace/drv/bus/usb/uhci/uhci.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/uhci/uhci.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -184,9 +184,8 @@
 	ddf_fun_data_implant(instance->rh_fun, &instance->rh);
 
-	uintptr_t reg_base = 0;
-	size_t reg_size = 0;
+	addr_range_t regs;
 	int irq = 0;
 
-	rc = get_my_registers(device, &reg_base, &reg_size, &irq);
+	rc = get_my_registers(device, &regs, &irq);
 	if (rc != EOK) {
 		usb_log_error("Failed to get I/O addresses for %" PRIun ": %s.\n",
@@ -194,6 +193,6 @@
 		goto error;
 	}
-	usb_log_debug("I/O regs at 0x%p (size %zu), IRQ %d.\n",
-	    (void *) reg_base, reg_size, irq);
+	usb_log_debug("I/O regs at %p (size %zu), IRQ %d.\n",
+	    RNGABSPTR(regs), RNGSZ(regs), irq);
 
 	rc = disable_legacy(device);
@@ -204,5 +203,5 @@
 	}
 
-	rc = hc_register_irq_handler(device, reg_base, reg_size, irq, irq_handler);
+	rc = hc_register_irq_handler(device, &regs, irq, irq_handler);
 	if (rc != EOK) {
 		usb_log_error("Failed to register interrupt handler: %s.\n",
@@ -223,5 +222,5 @@
 	}
 
-	rc = hc_init(&instance->hc, (void*)reg_base, reg_size, interrupts);
+	rc = hc_init(&instance->hc, &regs, interrupts);
 	if (rc != EOK) {
 		usb_log_error("Failed to init uhci_hcd: %s.\n", str_error(rc));
Index: uspace/drv/bus/usb/uhci/utils/malloc32.h
===================================================================
--- uspace/drv/bus/usb/uhci/utils/malloc32.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/uhci/utils/malloc32.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -92,5 +92,7 @@
  */
 static inline void free32(void *addr)
-	{ free(addr); }
+{
+	free(addr);
+}
 
 /** Create 4KB page mapping
@@ -98,10 +100,14 @@
  * @return Address of the mapped page, NULL on failure.
  */
-static inline void * get_page(void)
+static inline void *get_page(void)
 {
-	void *address, *phys;
+	uintptr_t phys;
+	void *address;
+	
 	const int ret = dmamem_map_anonymous(UHCI_REQUIRED_PAGE_SIZE,
-	    AS_AREA_READ | AS_AREA_WRITE, 0, &phys, &address);
-	return ret == EOK ? address : NULL;
+	    DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0, &phys,
+	    &address);
+	
+	return ((ret == EOK) ? address : NULL);
 }
 
Index: uspace/drv/bus/usb/uhcirh/main.c
===================================================================
--- uspace/drv/bus/usb/uhcirh/main.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/uhcirh/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -48,6 +48,5 @@
 #define NAME "uhcirh"
 
-static int hc_get_my_registers(ddf_dev_t *dev,
-    uintptr_t *io_reg_address, size_t *io_reg_size);
+static int hc_get_my_registers(ddf_dev_t *dev, addr_range_t *io_regs);
 
 static int uhci_rh_dev_add(ddf_dev_t *device);
@@ -90,10 +89,9 @@
 	    ddf_dev_get_handle(device));
 
-	uintptr_t io_regs = 0;
-	size_t io_size = 0;
+	addr_range_t regs;
 	uhci_root_hub_t *rh = NULL;
 	int rc;
 
-	rc = hc_get_my_registers(device, &io_regs, &io_size);
+	rc = hc_get_my_registers(device, &regs);
 	if (rc != EOK) {
 		usb_log_error( "Failed to get registers from HC: %s.\n",
@@ -103,5 +101,5 @@
 
 	usb_log_debug("I/O regs at %p (size %zuB).\n",
-	    (void *) io_regs, io_size);
+	    RNGABSPTR(regs), RNGSZ(regs));
 
 	rh = ddf_dev_data_alloc(device, sizeof(uhci_root_hub_t));
@@ -111,5 +109,5 @@
 	}
 
-	rc = uhci_root_hub_init(rh, (void*)io_regs, io_size, device);
+	rc = uhci_root_hub_init(rh, &regs, device);
 	if (rc != EOK) {
 		usb_log_error("Failed(%d) to initialize rh driver instance: "
@@ -127,10 +125,8 @@
  *
  * @param[in] dev Device asking for the addresses.
- * @param[out] io_reg_address Base address of the memory range.
- * @param[out] io_reg_size Size of the memory range.
+ * @param[out] io_regs_p Pointer to the device's register range.
  * @return Error code.
  */
-int hc_get_my_registers(
-    ddf_dev_t *dev, uintptr_t *io_reg_address, size_t *io_reg_size)
+int hc_get_my_registers(ddf_dev_t *dev, addr_range_t *io_regs_p)
 {
 	async_sess_t *parent_sess =
@@ -153,9 +149,6 @@
 	}
 
-	if (io_reg_address != NULL)
-		*io_reg_address = hw_res.io_ranges.ranges[0].address;
-
-	if (io_reg_size != NULL)
-		*io_reg_size = hw_res.io_ranges.ranges[0].size;
+	if (io_regs_p != NULL)
+		*io_regs_p = hw_res.io_ranges.ranges[0];
 
 	hw_res_list_parsed_clean(&hw_res);
Index: uspace/drv/bus/usb/uhcirh/root_hub.c
===================================================================
--- uspace/drv/bus/usb/uhcirh/root_hub.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/uhcirh/root_hub.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -36,4 +36,5 @@
 #include <ddi.h>
 #include <usb/debug.h>
+#include <device/hw_res_parsed.h>
 
 #include "root_hub.h"
@@ -42,23 +43,24 @@
  *
  * @param[in] instance Driver memory structure to use.
- * @param[in] addr Address of I/O registers.
- * @param[in] size Size of available I/O space.
+ * @param[in] io_regs Range of I/O registers.
  * @param[in] rh Pointer to DDF instance of the root hub driver.
  * @return Error code.
  */
-int uhci_root_hub_init(
-  uhci_root_hub_t *instance, void *addr, size_t size, ddf_dev_t *rh)
+int uhci_root_hub_init(uhci_root_hub_t *instance, addr_range_t *io_regs,
+    ddf_dev_t *rh)
 {
+	port_status_t *regs;
+
 	assert(instance);
 	assert(rh);
 
 	/* Allow access to root hub port registers */
-	assert(sizeof(port_status_t) * UHCI_ROOT_HUB_PORT_COUNT <= size);
-	port_status_t *regs;
-	int ret = pio_enable(addr, size, (void**)&regs);
+	assert(sizeof(*regs) * UHCI_ROOT_HUB_PORT_COUNT <= io_regs->size);
+
+	int ret = pio_enable_range(io_regs, (void **) &regs);
 	if (ret < 0) {
 		usb_log_error(
 		    "Failed(%d) to gain access to port registers at %p: %s.\n",
-		    ret, regs, str_error(ret));
+		    ret, RNGABSPTR(*io_regs), str_error(ret));
 		return ret;
 	}
Index: uspace/drv/bus/usb/uhcirh/root_hub.h
===================================================================
--- uspace/drv/bus/usb/uhcirh/root_hub.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/uhcirh/root_hub.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -36,4 +36,5 @@
 
 #include <ddf/driver.h>
+#include <device/hw_res_parsed.h>
 
 #include "port.h"
@@ -48,6 +49,6 @@
 } uhci_root_hub_t;
 
-int uhci_root_hub_init(
-    uhci_root_hub_t *instance, void *addr, size_t size, ddf_dev_t *rh);
+int uhci_root_hub_init(uhci_root_hub_t *instance, addr_range_t *regs,
+    ddf_dev_t *rh);
 
 void uhci_root_hub_fini(uhci_root_hub_t *instance);
Index: uspace/drv/bus/usb/usbmid/explore.c
===================================================================
--- uspace/drv/bus/usb/usbmid/explore.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/usbmid/explore.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -56,6 +56,5 @@
 static bool interface_in_list(const list_t *list, int interface_no)
 {
-	list_foreach(*list, l) {
-		usbmid_interface_t *iface = usbmid_interface_from_link(l);
+	list_foreach(*list, link, usbmid_interface_t, iface) {
 		if (iface->interface_no == interface_no) {
 			return true;
@@ -190,7 +189,5 @@
 
 	/* Start child function for every interface. */
-	list_foreach(usb_mid->interface_list, link) {
-		usbmid_interface_t *iface = usbmid_interface_from_link(link);
-
+	list_foreach(usb_mid->interface_list, link, usbmid_interface_t, iface) {
 		usb_log_info("Creating child for interface %d (%s).\n",
 		    iface->interface_no,
Index: uspace/drv/bus/usb/usbmid/usbmid.c
===================================================================
--- uspace/drv/bus/usb/usbmid/usbmid.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/usbmid/usbmid.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -135,6 +135,5 @@
 	}
 
-	list_foreach(match_ids.ids, link) {
-		match_id_t *match_id = list_get_instance(link, match_id_t, link);
+	list_foreach(match_ids.ids, link, match_id_t, match_id) {
 		rc = ddf_fun_add_match_id(child, match_id->id, match_id->score);
 		if (rc != EOK) {
Index: uspace/drv/bus/usb/vhc/transfer.c
===================================================================
--- uspace/drv/bus/usb/vhc/transfer.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/bus/usb/vhc/transfer.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -92,6 +92,5 @@
 
 	bool target_found = false;
-	list_foreach(vhc->devices, pos) {
-		vhc_virtdev_t *dev = list_get_instance(pos, vhc_virtdev_t, link);
+	list_foreach(vhc->devices, link, vhc_virtdev_t, dev) {
 		fibril_mutex_lock(&dev->guard);
 		if (dev->address == transfer->address) {
Index: uspace/drv/char/i8042/i8042.c
===================================================================
--- uspace/drv/char/i8042/i8042.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/char/i8042/i8042.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -145,6 +145,5 @@
  *
  * @param dev       Driver structure to initialize.
- * @param regs      I/O address of registers.
- * @param reg_size  size of the reserved I/O address space.
+ * @param regs      I/O range  of registers.
  * @param irq_kbd   IRQ for primary port.
  * @param irq_mouse IRQ for aux port.
@@ -154,5 +153,5 @@
  *
  */
-int i8042_init(i8042_t *dev, void *regs, size_t reg_size, int irq_kbd,
+int i8042_init(i8042_t *dev, addr_range_t *regs, int irq_kbd,
     int irq_mouse, ddf_dev_t *ddf_dev)
 {
@@ -162,4 +161,5 @@
 	const size_t cmd_count = sizeof(i8042_cmds) / sizeof(irq_cmd_t);
 	irq_cmd_t cmds[cmd_count];
+	i8042_regs_t *ar;
 
 	int rc;
@@ -170,10 +170,10 @@
 	dev->aux_fun = NULL;
 	
-	if (reg_size < sizeof(i8042_regs_t)) {
+	if (regs->size < sizeof(i8042_regs_t)) {
 		rc = EINVAL;
 		goto error;
 	}
 	
-	if (pio_enable(regs, sizeof(i8042_regs_t), (void **) &dev->regs) != 0) {
+	if (pio_enable_range(regs, (void **) &dev->regs) != 0) {
 		rc = EIO;
 		goto error;
@@ -234,9 +234,11 @@
 
 	memcpy(ranges, i8042_ranges, sizeof(i8042_ranges));
-	ranges[0].base = (uintptr_t) regs;
-
+	ranges[0].base = RNGABS(*regs);
+
+
+	ar = RNGABSPTR(*regs);
 	memcpy(cmds, i8042_cmds, sizeof(i8042_cmds));
-	cmds[0].addr = (void *) &(((i8042_regs_t *) regs)->status);
-	cmds[3].addr = (void *) &(((i8042_regs_t *) regs)->data);
+	cmds[0].addr = (void *) &ar->status;
+	cmds[3].addr = (void *) &ar->data;
 
 	irq_code_t irq_code = {
Index: uspace/drv/char/i8042/i8042.h
===================================================================
--- uspace/drv/char/i8042/i8042.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/char/i8042/i8042.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -68,5 +68,5 @@
 } i8042_t;
 
-int i8042_init(i8042_t *, void *, size_t, int, int, ddf_dev_t *);
+int i8042_init(i8042_t *, addr_range_t *, int, int, ddf_dev_t *);
 
 #endif
Index: uspace/drv/char/i8042/main.c
===================================================================
--- uspace/drv/char/i8042/main.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/char/i8042/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -49,6 +49,5 @@
  *
  * @param[in]  dev            Device asking for the addresses.
- * @param[out] io_reg_address Base address of the memory range.
- * @param[out] io_reg_size    Size of the memory range.
+ * @param[out] p_io_reg       Pointer to register range.
  * @param[out] kbd_irq        Primary port IRQ.
  * @param[out] mouse_irq      Auxiliary port IRQ.
@@ -57,6 +56,6 @@
  *
  */
-static int get_my_registers(ddf_dev_t *dev, uintptr_t *io_reg_address,
-    size_t *io_reg_size, int *kbd_irq, int *mouse_irq)
+static int get_my_registers(ddf_dev_t *dev, addr_range_t *p_io_reg,
+    int *kbd_irq, int *mouse_irq)
 {
 	assert(dev);
@@ -79,9 +78,6 @@
 	}
 	
-	if (io_reg_address)
-		*io_reg_address = hw_resources.io_ranges.ranges[0].address;
-	
-	if (io_reg_size)
-		*io_reg_size = hw_resources.io_ranges.ranges[0].size;
+	if (p_io_reg)
+		*p_io_reg = hw_resources.io_ranges.ranges[0];
 	
 	if (kbd_irq)
@@ -104,6 +100,5 @@
 static int i8042_dev_add(ddf_dev_t *device)
 {
-	uintptr_t io_regs = 0;
-	size_t io_size = 0;
+	addr_range_t io_regs;
 	int kbd = 0;
 	int mouse = 0;
@@ -113,5 +108,5 @@
 		return EINVAL;
 	
-	rc = get_my_registers(device, &io_regs, &io_size, &kbd, &mouse);
+	rc = get_my_registers(device, &io_regs, &kbd, &mouse);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed to get registers: %s.",
@@ -120,6 +115,7 @@
 	}
 	
-	ddf_msg(LVL_DEBUG, "I/O regs at %p (size %zuB), IRQ kbd %d, IRQ mouse %d.",
-	    (void *) io_regs, io_size, kbd, mouse);
+	ddf_msg(LVL_DEBUG,
+	    "I/O regs at %p (size %zuB), IRQ kbd %d, IRQ mouse %d.",
+	    RNGABSPTR(io_regs), RNGSZ(io_regs), kbd, mouse);
 	
 	i8042_t *i8042 = ddf_dev_data_alloc(device, sizeof(i8042_t));
@@ -129,5 +125,5 @@
 	}
 	
-	rc = i8042_init(i8042, (void *) io_regs, io_size, kbd, mouse, device);
+	rc = i8042_init(i8042, &io_regs, kbd, mouse, device);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed to initialize i8042 driver: %s.",
Index: uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.c
===================================================================
--- uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -273,11 +273,13 @@
 	ddf_log_note("Setting mode: %ux%ux%u\n", x, y, bpp*8);
 	const size_t size = ALIGN_UP(x * y * bpp, PAGE_SIZE);
-	void *buffer, *pa;
-	int ret = dmamem_map_anonymous(size, AS_AREA_READ | AS_AREA_WRITE,
-	    0, &pa, &buffer);
+	uintptr_t pa;
+	void *buffer;
+	int ret = dmamem_map_anonymous(size, DMAMEM_4GiB,
+	    AS_AREA_READ | AS_AREA_WRITE, 0, &pa, &buffer);
 	if (ret != EOK) {
 		ddf_log_error("Failed to get new FB\n");
 		return ret;
 	}
+	
 	amdm37x_dispc_setup_fb(dispc->regs, x, y, bpp *8, (uint32_t)pa);
 	dispc->active_fb.idx = mode.index;
Index: uspace/drv/fb/kfb/port.c
===================================================================
--- uspace/drv/fb/kfb/port.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/fb/kfb/port.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -333,5 +333,5 @@
 	
 	kfb.size = scanline * height;
-	rc = physmem_map((void *) paddr + offset,
+	rc = physmem_map(paddr + offset,
 	    ALIGN_UP(kfb.size, PAGE_SIZE) >> PAGE_WIDTH,
 	    AS_AREA_READ | AS_AREA_WRITE, (void *) &kfb.addr);
Index: uspace/drv/infrastructure/root/root.c
===================================================================
--- uspace/drv/infrastructure/root/root.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/infrastructure/root/root.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -208,11 +208,13 @@
 	/*
 	 * Register virtual devices root.
-	 * We ignore error occurrence because virtual devices shall not be
+	 * We warn on error occurrence because virtual devices shall not be
 	 * vital for the system.
 	 */
-	(void) add_virtual_root_fun(dev);
+	int res = add_virtual_root_fun(dev);
+	if (res != EOK)
+		ddf_msg(LVL_WARN, "Failed to add virtual child.");
 
 	/* Register root device's children. */
-	int res = add_platform_fun(dev);
+	res = add_platform_fun(dev);
 	if (EOK != res)
 		ddf_msg(LVL_ERROR, "Failed adding child device for platform.");
Index: uspace/drv/infrastructure/rootamdm37x/rootamdm37x.c
===================================================================
--- uspace/drv/infrastructure/rootamdm37x/rootamdm37x.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/infrastructure/rootamdm37x/rootamdm37x.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -48,12 +48,23 @@
 
 typedef struct {
+	const char *name;
+	match_id_t match_id;
 	hw_resource_list_t hw_resources;
 } rootamdm37x_fun_t;
 
-/* See amdm37x TRM page. 3316 for these values */
-#define OHCI_BASE_ADDRESS  0x48064400
-#define OHCI_SIZE  1024
-#define EHCI_BASE_ADDRESS  0x48064800
-#define EHCI_SIZE  1024
+/* See amdm37x TRM page 3316 for these values */
+#define OHCI_BASE_ADDRESS   0x48064400
+#define OHCI_SIZE   1024
+#define EHCI_BASE_ADDRESS   0x48064800
+#define EHCI_SIZE   1024
+
+/* See amdm37x TRM page 1813 for these values */
+#define DSS_BASE_ADDRESS   0x48050000
+#define DSS_SIZE   512
+#define DISPC_BASE_ADDRESS   0x48050400
+#define DISPC_SIZE   1024
+#define VIDEO_ENC_BASE_ADDRESS   0x48050C00
+#define VIDEO_ENC_SIZE   256
+
 
 static hw_resource_t ohci_res[] = {
@@ -88,17 +99,53 @@
 };
 
-static const rootamdm37x_fun_t ohci = {
-	.hw_resources = {
-	    .resources = ohci_res,
-	    .count = sizeof(ohci_res)/sizeof(ohci_res[0]),
-	}
-};
-
-static const rootamdm37x_fun_t ehci = {
-	.hw_resources = {
-	    .resources = ehci_res,
-	    .count = sizeof(ehci_res) / sizeof(ehci_res[0]),
-	}
-};
+static hw_resource_t disp_res[] = {
+	{
+		.type = MEM_RANGE,
+		.res.io_range = {
+			.address = DSS_BASE_ADDRESS,
+			.size = DSS_SIZE,
+			.endianness = LITTLE_ENDIAN
+		},
+	},
+	{
+		.type = MEM_RANGE,
+		.res.io_range = {
+			.address = DISPC_BASE_ADDRESS,
+			.size = DISPC_SIZE,
+			.endianness = LITTLE_ENDIAN
+		},
+	},
+	{
+		.type = MEM_RANGE,
+		.res.io_range = {
+			.address = VIDEO_ENC_BASE_ADDRESS,
+			.size = VIDEO_ENC_SIZE,
+			.endianness = LITTLE_ENDIAN
+		},
+	},
+	{
+		.type = INTERRUPT,
+		.res.interrupt = { .irq = 25 },
+	},
+};
+
+static const rootamdm37x_fun_t amdm37x_funcs[] = {
+{
+	.name = "ohci",
+	.match_id = { .id = "usb/host=ohci", .score = 90 },
+	.hw_resources = { .resources = ohci_res, .count = ARRAY_SIZE(ohci_res) }
+},
+{
+	.name = "ehci",
+	.match_id = { .id = "usb/host=ehci", .score = 90 },
+	.hw_resources = { .resources = ehci_res, .count = ARRAY_SIZE(ehci_res) }
+},
+{
+	.name = "fb",
+	.match_id = { .id = "amdm37x&dispc", .score = 90 },
+	.hw_resources = { .resources = disp_res, .count = ARRAY_SIZE(disp_res) }
+},
+};
+
 
 static hw_resource_list_t *rootamdm37x_get_resources(ddf_fun_t *fnode);
@@ -114,16 +161,19 @@
 };
 
-static int rootamdm37x_add_fun(ddf_dev_t *dev, const char *name,
-    const char *str_match_id, const rootamdm37x_fun_t *fun)
-{
-	ddf_msg(LVL_DEBUG, "Adding new function '%s'.", name);
-	
+static int rootamdm37x_add_fun(ddf_dev_t *dev, const rootamdm37x_fun_t *fun)
+{
+	assert(dev);
+	assert(fun);
+
+	ddf_msg(LVL_DEBUG, "Adding new function '%s'.", fun->name);
+
 	/* Create new device function. */
-	ddf_fun_t *fnode = ddf_fun_create(dev, fun_inner, name);
+	ddf_fun_t *fnode = ddf_fun_create(dev, fun_inner, fun->name);
 	if (fnode == NULL)
 		return ENOMEM;
 	
 	/* Add match id */
-	int ret = ddf_fun_add_match_id(fnode, str_match_id, 100);
+	int ret = ddf_fun_add_match_id(fnode,
+	    fun->match_id.id, fun->match_id.score);
 	if (ret != EOK) {
 		ddf_fun_destroy(fnode);
@@ -146,5 +196,5 @@
 	ret = ddf_fun_bind(fnode);
 	if (ret != EOK) {
-		ddf_msg(LVL_ERROR, "Failed binding function %s.", name);
+		ddf_msg(LVL_ERROR, "Failed binding function %s.", fun->name);
 		ddf_fun_destroy(fnode);
 		return ret;
@@ -189,14 +239,9 @@
 
 	/* Register functions */
-	if (rootamdm37x_add_fun(dev, "ohci", "usb/host=ohci", &ohci) != EOK)
-		ddf_msg(LVL_ERROR, "Failed to add OHCI function for "
-		    "BeagleBoard-xM platform.");
-	if (rootamdm37x_add_fun(dev, "ehci", "usb/host=ehci", &ehci) != EOK)
-		ddf_msg(LVL_ERROR, "Failed to add EHCI function for "
-		    "BeagleBoard-xM platform.");
-	if (rootamdm37x_add_fun(dev, "dispc", "amdm37x&dispc", &ehci) != EOK)
-		ddf_msg(LVL_ERROR, "Failed to add dispc function for "
-		    "BeagleBoard-xM platform.");
-
+	for (unsigned i = 0; i < ARRAY_SIZE(amdm37x_funcs); ++i) {
+		if (rootamdm37x_add_fun(dev, &amdm37x_funcs[i]) != EOK)
+			ddf_msg(LVL_ERROR, "Failed to add %s function for "
+			    "BeagleBoard-xM platform.", amdm37x_funcs[i].name);
+	}
 	return EOK;
 }
Index: uspace/drv/infrastructure/rootamdm37x/uhh.h
===================================================================
--- uspace/drv/infrastructure/rootamdm37x/uhh.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/infrastructure/rootamdm37x/uhh.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -85,5 +85,5 @@
 #define UHH_DEBUG_CSR_EHCI_SIMULATION_MODE_FLAG  (1 << 6)
 #define UHH_DEBUG_CSR_OHCI_CNTSEL_FLAG  (1 << 7)
-#define UHH_DEBUG_CSR_OHCI_GLOBAL_sUSPEND_FLAG  (1 << 16)
+#define UHH_DEBUG_CSR_OHCI_GLOBAL_SUSPEND_FLAG  (1 << 16)
 #define UHH_DEBUG_CSR_OHCI_CCS1_FLAG  (1 << 17)
 #define UHH_DEBUG_CSR_OHCI_CCS2_FLAG  (1 << 18)
Index: uspace/drv/infrastructure/rootmac/rootmac.c
===================================================================
--- uspace/drv/infrastructure/rootmac/rootmac.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/infrastructure/rootmac/rootmac.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -54,4 +54,5 @@
 			.address = 0xfec00000,
 			.size = 4,
+			.relative = false,
 			.endianness = LITTLE_ENDIAN
 		}
@@ -62,4 +63,5 @@
 			.address = 0xfee00000,
 			.size = 4,
+			.relative = false,
 			.endianness = LITTLE_ENDIAN
 		}
Index: uspace/drv/infrastructure/rootmalta/Makefile
===================================================================
--- uspace/drv/infrastructure/rootmalta/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/infrastructure/rootmalta/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,37 @@
+#
+# 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.
+#
+
+USPACE_PREFIX = ../../..
+LIBS = $(LIBDRV_PREFIX)/libdrv.a
+EXTRA_CFLAGS += -I$(LIBDRV_PREFIX)/include
+BINARY = rootmalta
+
+SOURCES = \
+	rootmalta.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/drv/infrastructure/rootmalta/rootmalta.c
===================================================================
--- uspace/drv/infrastructure/rootmalta/rootmalta.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/infrastructure/rootmalta/rootmalta.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * 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.
+ */
+
+/**
+ * @defgroup root_malta Malta board platform driver.
+ * @brief HelenOS Malta board platform driver.
+ * @{
+ */
+
+/** @file
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <fibril_synch.h>
+#include <stdlib.h>
+#include <str.h>
+#include <ctype.h>
+#include <macros.h>
+
+#include <ddi.h>
+#include <ddf/driver.h>
+#include <ddf/log.h>
+#include <ipc/dev_iface.h>
+#include <ops/hw_res.h>
+#include <device/hw_res.h>
+#include <ops/pio_window.h>
+#include <device/pio_window.h>
+#include <byteorder.h>
+
+#define NAME "rootmalta"
+
+#define GT_BASE		UINT32_C(0x1be00000)
+#define GT_SIZE 	(2 * 1024 * 1024)
+
+#define GT_PCI_CMD	0xc00
+#define GT_PCI_CONFADDR	0xcf8
+#define GT_PCI_CONFDATA	0xcfc
+
+#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;
+
+static int rootmalta_dev_add(ddf_dev_t *dev);
+static void root_malta_init(void);
+
+/** The root device driver's standard operations. */
+static driver_ops_t rootmalta_ops = {
+	.dev_add = &rootmalta_dev_add
+};
+
+/** The root device driver structure. */
+static driver_t rootmalta_driver = {
+	.name = NAME,
+	.driver_ops = &rootmalta_ops
+};
+
+static hw_resource_t pci_conf_regs[] = {
+	{
+		.type = IO_RANGE,
+		.res.io_range = {
+			.address = GT_BASE + GT_PCI_CONFADDR,
+			.size = 4,
+			.relative = false,
+			.endianness = LITTLE_ENDIAN
+		}
+	},
+	{
+		.type = IO_RANGE,
+		.res.io_range = {
+			.address = GT_BASE + GT_PCI_CONFDATA,
+			.size = 4,
+			.relative = false,
+			.endianness = LITTLE_ENDIAN
+		}
+	}
+};
+
+static rootmalta_fun_t pci_data = {
+	.hw_resources = {
+		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
+		}
+	}
+};
+
+/** Obtain function soft-state from DDF function node */
+static rootmalta_fun_t *rootmalta_fun(ddf_fun_t *fnode)
+{
+	return ddf_fun_data_get(fnode);
+}
+
+static hw_resource_list_t *rootmalta_get_resources(ddf_fun_t *fnode)
+{
+	rootmalta_fun_t *fun = rootmalta_fun(fnode);
+	
+	assert(fun != NULL);
+	return &fun->hw_resources;
+}
+
+static bool rootmalta_enable_interrupt(ddf_fun_t *fun)
+{
+	/* TODO */
+	
+	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;
+}
+
+static hw_res_ops_t fun_hw_res_ops = {
+	.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
+};
+
+/* Initialized in root_malta_init() function. */
+static ddf_dev_ops_t rootmalta_fun_ops;
+
+static bool
+rootmalta_add_fun(ddf_dev_t *dev, const char *name, const char *str_match_id,
+    rootmalta_fun_t *fun_proto)
+{
+	ddf_msg(LVL_DEBUG, "Adding new function '%s'.", name);
+	
+	ddf_fun_t *fnode = NULL;
+	int rc;
+	
+	/* Create new device. */
+	fnode = ddf_fun_create(dev, fun_inner, name);
+	if (fnode == NULL)
+		goto failure;
+	
+	rootmalta_fun_t *fun = ddf_fun_data_alloc(fnode, sizeof(rootmalta_fun_t));
+	*fun = *fun_proto;
+	
+	/* Add match ID */
+	rc = ddf_fun_add_match_id(fnode, str_match_id, 100);
+	if (rc != EOK)
+		goto failure;
+	
+	/* Set provided operations to the device. */
+	ddf_fun_set_ops(fnode, &rootmalta_fun_ops);
+	
+	/* Register function. */
+	if (ddf_fun_bind(fnode) != EOK) {
+		ddf_msg(LVL_ERROR, "Failed binding function %s.", name);
+		goto failure;
+	}
+	
+	return true;
+	
+failure:
+	if (fnode != NULL)
+		ddf_fun_destroy(fnode);
+	
+	ddf_msg(LVL_ERROR, "Failed adding function '%s'.", name);
+	
+	return false;
+}
+
+static bool rootmalta_add_functions(ddf_dev_t *dev)
+{
+	return rootmalta_add_fun(dev, "pci0", "intel_pci", &pci_data);
+}
+
+/** Get the root device.
+ *
+ * @param dev		The device which is root of the whole device tree (both
+ *			of HW and pseudo devices).
+ * @return		Zero on success, negative error number otherwise.
+ */
+static int rootmalta_dev_add(ddf_dev_t *dev)
+{
+	ioport32_t *gt;
+	uint32_t val;
+	int ret;
+
+	ddf_msg(LVL_DEBUG, "rootmalta_dev_add, device handle = %d",
+	    (int)ddf_dev_get_handle(dev));
+
+	/*
+ 	 * We need to disable byte swapping of the outgoing and incoming
+ 	 * PCI data, because the PCI driver assumes no byte swapping behind
+ 	 * the scenes and takes care of it itself.
+ 	 */
+	ret = pio_enable((void *) GT_BASE, GT_SIZE, (void **) &gt);
+	if (ret != EOK)
+                return ret;
+	val = uint32_t_le2host(pio_read_32(
+	    &gt[GT_PCI_CMD / sizeof(ioport32_t)]));
+	val |= GT_PCI_CMD_MBYTESWAP;
+	pio_write_32(
+	    &gt[GT_PCI_CMD / sizeof(ioport32_t)], host2uint32_t_le(val));
+
+	
+	/* Register functions. */
+	if (!rootmalta_add_functions(dev)) {
+		ddf_msg(LVL_ERROR, "Failed to add functions for the Malta platform.");
+	}
+	
+	return EOK;
+}
+
+static void root_malta_init(void)
+{
+	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;
+}
+
+int main(int argc, char *argv[])
+{
+	printf(NAME ": HelenOS Malta platform driver\n");
+	root_malta_init();
+	return ddf_driver_main(&rootmalta_driver);
+}
+
+/**
+ * @}
+ */
Index: uspace/drv/infrastructure/rootmalta/rootmalta.ma
===================================================================
--- uspace/drv/infrastructure/rootmalta/rootmalta.ma	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/drv/infrastructure/rootmalta/rootmalta.ma	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,1 @@
+10 platform/malta
Index: uspace/drv/infrastructure/rootpc/rootpc.c
===================================================================
--- uspace/drv/infrastructure/rootpc/rootpc.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/infrastructure/rootpc/rootpc.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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;
 
@@ -78,4 +81,5 @@
 			.address = 0xCF8,
 			.size = 4,
+			.relative = false,
 			.endianness = LITTLE_ENDIAN
 		}
@@ -86,4 +90,5 @@
 			.address = 0xCFC,
 			.size = 4,
+			.relative = false,
 			.endianness = LITTLE_ENDIAN
 		}
@@ -93,6 +98,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 +132,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 +145,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 +224,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/drv/nic/e1k/e1k.c
===================================================================
--- uspace/drv/nic/e1k/e1k.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/nic/e1k/e1k.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -129,20 +129,20 @@
 	
 	/** Physical tx ring address */
-	void *tx_ring_phys;
+	uintptr_t tx_ring_phys;
 	/** Virtual tx ring address */
 	void *tx_ring_virt;
 	
 	/** Ring of TX frames, physical address */
-	void **tx_frame_phys;
+	uintptr_t *tx_frame_phys;
 	/** Ring of TX frames, virtual address */
 	void **tx_frame_virt;
 	
 	/** Physical rx ring address */
-	void *rx_ring_phys;
+	uintptr_t rx_ring_phys;
 	/** Virtual rx ring address */
 	void *rx_ring_virt;
 	
 	/** Ring of RX frames, physical address */
-	void **rx_frame_phys;
+	uintptr_t *rx_frame_phys;
 	/** Ring of RX frames, virtual address */
 	void **rx_frame_virt;
@@ -1377,6 +1377,6 @@
 	int rc = dmamem_map_anonymous(
 	    E1000_RX_FRAME_COUNT * sizeof(e1000_rx_descriptor_t),
-	    AS_AREA_READ | AS_AREA_WRITE, 0, &e1000->rx_ring_phys,
-	    &e1000->rx_ring_virt);
+	    DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0,
+	    &e1000->rx_ring_phys, &e1000->rx_ring_virt);
 	if (rc != EOK)
 		return rc;
@@ -1387,9 +1387,9 @@
 	    (uint32_t) PTR_TO_U64(e1000->rx_ring_phys));
 	
-	e1000->rx_frame_phys =
-	    calloc(E1000_RX_FRAME_COUNT, sizeof(void *));
+	e1000->rx_frame_phys = (uintptr_t *)
+	    calloc(E1000_RX_FRAME_COUNT, sizeof(uintptr_t));
 	e1000->rx_frame_virt =
 	    calloc(E1000_RX_FRAME_COUNT, sizeof(void *));
-	if (e1000->rx_frame_phys == NULL || e1000->rx_frame_virt == NULL) {
+	if ((e1000->rx_frame_phys == NULL) || (e1000->rx_frame_virt == NULL)) {
 		rc = ENOMEM;
 		goto error;
@@ -1397,16 +1397,16 @@
 	
 	size_t i;
+	uintptr_t frame_phys;
 	void *frame_virt;
-	void *frame_phys;
 	
 	for (i = 0; i < E1000_RX_FRAME_COUNT; i++) {
-		rc = dmamem_map_anonymous(
-		    E1000_MAX_SEND_FRAME_SIZE, AS_AREA_READ | AS_AREA_WRITE,
-		    0, &frame_phys, &frame_virt);
+		rc = dmamem_map_anonymous(E1000_MAX_SEND_FRAME_SIZE,
+		    DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0,
+		    &frame_phys, &frame_virt);
 		if (rc != EOK)
 			goto error;
 		
+		e1000->rx_frame_phys[i] = frame_phys;
 		e1000->rx_frame_virt[i] = frame_virt;
-		e1000->rx_frame_phys[i] = frame_phys;
 	}
 	
@@ -1424,6 +1424,6 @@
 		if (e1000->rx_frame_virt[i] != NULL) {
 			dmamem_unmap_anonymous(e1000->rx_frame_virt[i]);
+			e1000->rx_frame_phys[i] = 0;
 			e1000->rx_frame_virt[i] = NULL;
-			e1000->rx_frame_phys[i] = NULL;
 		}
 	}
@@ -1436,5 +1436,5 @@
 	if (e1000->rx_frame_virt != NULL) {
 		free(e1000->rx_frame_virt);
-		e1000->rx_frame_phys = NULL;
+		e1000->rx_frame_virt = NULL;
 	}
 	
@@ -1454,12 +1454,13 @@
 	for (unsigned int offset = 0; offset < E1000_RX_FRAME_COUNT; offset++) {
 		dmamem_unmap_anonymous(e1000->rx_frame_virt[offset]);
+		e1000->rx_frame_phys[offset] = 0;
 		e1000->rx_frame_virt[offset] = NULL;
-		e1000->rx_frame_phys[offset] = NULL;
 	}
 	
 	free(e1000->rx_frame_virt);
-	free(e1000->rx_frame_phys);
+	
+	e1000->rx_frame_phys = NULL;
 	e1000->rx_frame_virt = NULL;
-	e1000->rx_frame_phys = NULL;
+	
 	dmamem_unmap_anonymous(e1000->rx_ring_virt);
 }
@@ -1569,6 +1570,7 @@
 	fibril_mutex_lock(&e1000->tx_lock);
 	
-	e1000->tx_ring_phys = NULL;
+	e1000->tx_ring_phys = 0;
 	e1000->tx_ring_virt = NULL;
+	
 	e1000->tx_frame_phys = NULL;
 	e1000->tx_frame_virt = NULL;
@@ -1576,6 +1578,6 @@
 	int rc = dmamem_map_anonymous(
 	    E1000_TX_FRAME_COUNT * sizeof(e1000_tx_descriptor_t),
-	    AS_AREA_READ | AS_AREA_WRITE, 0, &e1000->tx_ring_phys,
-	    &e1000->tx_ring_virt);
+	    DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE, 0,
+	    &e1000->tx_ring_phys, &e1000->tx_ring_virt);
 	if (rc != EOK)
 		goto error;
@@ -1584,8 +1586,10 @@
 	    E1000_TX_FRAME_COUNT * sizeof(e1000_tx_descriptor_t));
 	
-	e1000->tx_frame_phys = calloc(E1000_TX_FRAME_COUNT, sizeof(void *));
-	e1000->tx_frame_virt = calloc(E1000_TX_FRAME_COUNT, sizeof(void *));
-
-	if (e1000->tx_frame_phys == NULL || e1000->tx_frame_virt == NULL) {
+	e1000->tx_frame_phys = (uintptr_t *)
+	    calloc(E1000_TX_FRAME_COUNT, sizeof(uintptr_t));
+	e1000->tx_frame_virt =
+	    calloc(E1000_TX_FRAME_COUNT, sizeof(void *));
+
+	if ((e1000->tx_frame_phys == NULL) || (e1000->tx_frame_virt == NULL)) {
 		rc = ENOMEM;
 		goto error;
@@ -1593,6 +1597,6 @@
 	
 	for (i = 0; i < E1000_TX_FRAME_COUNT; i++) {
-		rc = dmamem_map_anonymous(
-		    E1000_MAX_SEND_FRAME_SIZE, AS_AREA_READ | AS_AREA_WRITE,
+		rc = dmamem_map_anonymous(E1000_MAX_SEND_FRAME_SIZE,
+		    DMAMEM_4GiB, AS_AREA_READ | AS_AREA_WRITE,
 		    0, &e1000->tx_frame_phys[i], &e1000->tx_frame_virt[i]);
 		if (rc != EOK)
@@ -1616,10 +1620,10 @@
 	}
 	
-	if (e1000->tx_frame_phys != NULL && e1000->tx_frame_virt != NULL) {
+	if ((e1000->tx_frame_phys != NULL) && (e1000->tx_frame_virt != NULL)) {
 		for (i = 0; i < E1000_TX_FRAME_COUNT; i++) {
 			if (e1000->tx_frame_virt[i] != NULL) {
 				dmamem_unmap_anonymous(e1000->tx_frame_virt[i]);
+				e1000->tx_frame_phys[i] = 0;
 				e1000->tx_frame_virt[i] = NULL;
-				e1000->tx_frame_phys[i] = NULL;
 			}
 		}
@@ -1633,5 +1637,5 @@
 	if (e1000->tx_frame_virt != NULL) {
 		free(e1000->tx_frame_virt);
-		e1000->tx_frame_phys = NULL;
+		e1000->tx_frame_virt = NULL;
 	}
 	
@@ -1650,6 +1654,6 @@
 	for (i = 0; i < E1000_TX_FRAME_COUNT; i++) {
 		dmamem_unmap_anonymous(e1000->tx_frame_virt[i]);
+		e1000->tx_frame_phys[i] = 0;
 		e1000->tx_frame_virt[i] = NULL;
-		e1000->tx_frame_phys[i] = NULL;
 	}
 	
@@ -1661,5 +1665,5 @@
 	if (e1000->tx_frame_virt != NULL) {
 		free(e1000->tx_frame_virt);
-		e1000->tx_frame_phys = NULL;
+		e1000->tx_frame_virt = NULL;
 	}
 	
@@ -1937,5 +1941,5 @@
 	e1000->irq = hw_resources->irqs.irqs[0];
 	e1000->reg_base_phys =
-	    MEMADDR_TO_PTR(hw_resources->mem_ranges.ranges[0].address);
+	    MEMADDR_TO_PTR(RNGABS(hw_resources->mem_ranges.ranges[0]));
 	
 	return EOK;
Index: uspace/drv/nic/ne2k/ne2k.c
===================================================================
--- uspace/drv/nic/ne2k/ne2k.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/nic/ne2k/ne2k.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -211,13 +211,12 @@
 	ne2k->irq = hw_res_parsed.irqs.irqs[0];
 	
-	ne2k->base_port = (void *) (uintptr_t)
-	    hw_res_parsed.io_ranges.ranges[0].address;
+	addr_range_t regs = hw_res_parsed.io_ranges.ranges[0];
+	ne2k->base_port = RNGABSPTR(regs);
 	
 	hw_res_list_parsed_clean(&hw_res_parsed);
 	
-	/* Enable port I/O */
-	if (pio_enable(ne2k->base_port, NE2K_IO_SIZE, &ne2k->port) != EOK)
+	/* Enable programmed I/O */
+	if (pio_enable_range(&regs, &ne2k->port) != EOK)
 		return EADDRNOTAVAIL;
-	
 	
 	ne2k->data_port = ne2k->port + NE2K_DATA;
Index: uspace/drv/nic/rtl8139/driver.c
===================================================================
--- uspace/drv/nic/rtl8139/driver.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/nic/rtl8139/driver.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1087,5 +1087,5 @@
 	ddf_msg(LVL_DEBUG, "%s device: irq 0x%x assigned", ddf_dev_get_name(dev), rtl8139->irq);
 
-	rtl8139->io_addr = IOADDR_TO_PTR(hw_resources->io_ranges.ranges[0].address);
+	rtl8139->io_addr = IOADDR_TO_PTR(RNGABS(hw_resources->io_ranges.ranges[0]));
 	if (hw_resources->io_ranges.ranges[0].size < RTL8139_IO_SIZE) {
 		ddf_msg(LVL_ERROR, "i/o range assigned to the device "
@@ -1144,6 +1144,6 @@
 	ddf_msg(LVL_DEBUG, "Creating buffers");
 
-	rc = dmamem_map_anonymous(TX_PAGES * PAGE_SIZE, AS_AREA_WRITE, 0,
-	    &rtl8139->tx_buff_phys, &rtl8139->tx_buff_virt);
+	rc = dmamem_map_anonymous(TX_PAGES * PAGE_SIZE, DMAMEM_4GiB,
+	    AS_AREA_WRITE, 0, &rtl8139->tx_buff_phys, &rtl8139->tx_buff_virt);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Can not allocate transmitter buffers.");
@@ -1164,6 +1164,6 @@
 	    RxBUF_TOT_LENGTH);
 
-	rc = dmamem_map_anonymous(RxBUF_TOT_LENGTH, AS_AREA_READ, 0,
-	    &rtl8139->rx_buff_phys, &rtl8139->rx_buff_virt);
+	rc = dmamem_map_anonymous(RxBUF_TOT_LENGTH, DMAMEM_4GiB,
+	    AS_AREA_READ, 0, &rtl8139->rx_buff_phys, &rtl8139->rx_buff_virt);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Can not allocate receive buffer.");
Index: uspace/drv/nic/rtl8139/driver.h
===================================================================
--- uspace/drv/nic/rtl8139/driver.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/drv/nic/rtl8139/driver.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -100,5 +100,5 @@
 	 *  Each buffer takes 2kB
 	 */
-	void *tx_buff_phys;
+	uintptr_t tx_buff_phys;
 	void *tx_buff_virt;
 
@@ -117,5 +117,5 @@
 
 	/** Buffer for receiving frames */
-	void *rx_buff_phys;
+	uintptr_t rx_buff_phys;
 	void *rx_buff_virt;
 
Index: uspace/lib/block/block.c
===================================================================
--- uspace/lib/block/block.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/block/block.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,5 +37,4 @@
  */
 
-#include "../../srv/vfs/vfs.h"
 #include <ipc/loc.h>
 #include <ipc/services.h>
@@ -93,6 +92,5 @@
 	fibril_mutex_lock(&dcl_lock);
 	
-	list_foreach(dcl, cur) {
-		devcon_t *devcon = list_get_instance(cur, devcon_t, link);
+	list_foreach(dcl, link, devcon_t, devcon) {
 		if (devcon->service_id == service_id) {
 			fibril_mutex_unlock(&dcl_lock);
@@ -124,6 +122,5 @@
 	
 	fibril_mutex_lock(&dcl_lock);
-	list_foreach(dcl, cur) {
-		devcon_t *d = list_get_instance(cur, devcon_t, link);
+	list_foreach(dcl, link, devcon_t, d) {
 		if (d->service_id == service_id) {
 			fibril_mutex_unlock(&dcl_lock);
Index: uspace/lib/block/block.h
===================================================================
--- uspace/lib/block/block.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/block/block.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -29,7 +29,7 @@
  */
 
-/** @addtogroup libblock 
+/** @addtogroup libblock
  * @{
- */ 
+ */
 /**
  * @file
@@ -41,8 +41,8 @@
 #include <stdint.h>
 #include <async.h>
-#include "../../srv/vfs/vfs.h"
 #include <fibril_synch.h>
 #include <adt/hash_table.h>
 #include <adt/list.h>
+#include <loc.h>
 
 /*
@@ -50,5 +50,5 @@
  */
 
-/** 
+/**
  * This macro is a symbolic value for situations where no special flags are
  * needed.
Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -65,7 +65,9 @@
 	generic/cfg.c \
 	generic/clipboard.c \
+	generic/corecfg.c \
 	generic/devman.c \
 	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 \
@@ -75,4 +77,5 @@
 	generic/device/pci.c \
 	generic/device/ahci.c \
+	generic/dhcp.c \
 	generic/dnsr.c \
 	generic/dlfcn.c \
@@ -97,5 +100,4 @@
 	generic/inetcfg.c \
 	generic/inetping.c \
-	generic/inetping6.c \
 	generic/io/asprintf.c \
 	generic/io/input.c \
Index: uspace/lib/c/arch/sparc64/Makefile.common
===================================================================
--- uspace/lib/c/arch/sparc64/Makefile.common	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/arch/sparc64/Makefile.common	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -27,5 +27,12 @@
 #
 
-GCC_CFLAGS += -mcpu=ultrasparc -m64 -mcmodel=medlow
+ifeq ($(PROCESSOR),sun4v)
+GCC_CFLAGS += -mcpu=niagara -mno-vis
+else
+GCC_CFLAGS += -mcpu=ultrasparc
+endif
+
+GCC_CFLAGS += -m64 -mcmodel=medlow
+
 LFLAGS = -no-check-sections
 
Index: uspace/lib/c/generic/adt/hash_table.c
===================================================================
--- uspace/lib/c/generic/adt/hash_table.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/adt/hash_table.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -227,10 +227,9 @@
 	
 	/* Check for duplicates. */
-	list_foreach(h->bucket[idx], cur) {
+	list_foreach(h->bucket[idx], link, ht_link_t, cur_link) {
 		/* 
 		 * We could filter out items using their hashes first, but 
 		 * calling equal() might very well be just as fast.
 		 */
-		ht_link_t *cur_link = member_to_inst(cur, ht_link_t, link);
 		if (h->op->equal(cur_link, item))
 			return false;
@@ -258,6 +257,5 @@
 	size_t idx = h->op->key_hash(key) % h->bucket_cnt;
 
-	list_foreach(h->bucket[idx], cur) {
-		ht_link_t *cur_link = member_to_inst(cur, ht_link_t, link);
+	list_foreach(h->bucket[idx], link, ht_link_t, cur_link) {
 		/* 
 		 * Is this is the item we are looking for? We could have first 
Index: uspace/lib/c/generic/adt/list.c
===================================================================
--- uspace/lib/c/generic/adt/list.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/adt/list.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -102,6 +102,8 @@
 	unsigned int count = 0;
 	
-	list_foreach(*list, link) {
+	link_t *link = list_first(list);
+	while (link != NULL) {
 		count++;
+		link = list_next(link, list);
 	}
 	
Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/async.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -2281,14 +2281,35 @@
 bool async_data_read_receive(ipc_callid_t *callid, size_t *size)
 {
+	ipc_call_t data;
+	return async_data_read_receive_call(callid, &data, size);
+}
+
+/** Wrapper for receiving the IPC_M_DATA_READ calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to receive IPC_M_DATA_READ
+ * calls so that the user doesn't have to remember the meaning of each IPC
+ * argument.
+ *
+ * So far, this wrapper is to be used from within a connection fibril.
+ *
+ * @param callid Storage for the hash of the IPC_M_DATA_READ.
+ * @param size   Storage for the maximum size. Can be NULL.
+ *
+ * @return True on success, false on failure.
+ *
+ */
+bool async_data_read_receive_call(ipc_callid_t *callid, ipc_call_t *data,
+    size_t *size)
+{
 	assert(callid);
-	
-	ipc_call_t data;
-	*callid = async_get_call(&data);
-	
-	if (IPC_GET_IMETHOD(data) != IPC_M_DATA_READ)
+	assert(data);
+	
+	*callid = async_get_call(data);
+	
+	if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_READ)
 		return false;
 	
 	if (size)
-		*size = (size_t) IPC_GET_ARG2(data);
+		*size = (size_t) IPC_GET_ARG2(*data);
 	
 	return true;
@@ -2385,14 +2406,36 @@
 bool async_data_write_receive(ipc_callid_t *callid, size_t *size)
 {
+	ipc_call_t data;
+	return async_data_write_receive_call(callid, &data, size);
+}
+
+/** Wrapper for receiving the IPC_M_DATA_WRITE calls using the async framework.
+ *
+ * This wrapper only makes it more comfortable to receive IPC_M_DATA_WRITE
+ * calls so that the user doesn't have to remember the meaning of each IPC
+ * argument.
+ *
+ * So far, this wrapper is to be used from within a connection fibril.
+ *
+ * @param callid Storage for the hash of the IPC_M_DATA_WRITE.
+ * @param data   Storage for the ipc call data.
+ * @param size   Storage for the suggested size. May be NULL.
+ *
+ * @return True on success, false on failure.
+ *
+ */
+bool async_data_write_receive_call(ipc_callid_t *callid, ipc_call_t *data,
+    size_t *size)
+{
 	assert(callid);
-	
-	ipc_call_t data;
-	*callid = async_get_call(&data);
-	
-	if (IPC_GET_IMETHOD(data) != IPC_M_DATA_WRITE)
+	assert(data);
+	
+	*callid = async_get_call(data);
+	
+	if (IPC_GET_IMETHOD(*data) != IPC_M_DATA_WRITE)
 		return false;
 	
 	if (size)
-		*size = (size_t) IPC_GET_ARG2(data);
+		*size = (size_t) IPC_GET_ARG2(*data);
 	
 	return true;
Index: uspace/lib/c/generic/cfg.c
===================================================================
--- uspace/lib/c/generic/cfg.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/cfg.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -83,7 +83,5 @@
 		return true;
 	
-	list_foreach(data->sections, link) {
-		const cfg_section_t *section = cfg_section_instance(link);
-		
+	cfg_file_foreach(data, section) {
 		if (!list_empty(&section->entries))
 			return false;
@@ -413,7 +411,5 @@
 const cfg_section_t *cfg_find_section(const cfg_file_t *data, const char *title)
 {
-	list_foreach(data->sections, link) {
-		const cfg_section_t *section = cfg_section_instance(link);
-		
+	cfg_file_foreach(data, section) {
 		if (str_cmp(section->title, title) == 0)
 			return section;
@@ -434,7 +430,5 @@
 const char *cfg_find_value(const cfg_section_t *section, const char *key)
 {
-	list_foreach(section->entries, link) {
-		const cfg_entry_t *entry = cfg_entry_instance(link);
-		
+	cfg_section_foreach(section, entry) {
 		if (str_cmp(entry->key, key) == 0)
 			return entry->value;
Index: uspace/lib/c/generic/corecfg.c
===================================================================
--- uspace/lib/c/generic/corecfg.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/c/generic/corecfg.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#include <async.h>
+#include <assert.h>
+#include <corecfg.h>
+#include <errno.h>
+#include <ipc/corecfg.h>
+#include <ipc/services.h>
+#include <loc.h>
+#include <stdlib.h>
+
+static async_sess_t *corecfg_sess = NULL;
+
+int corecfg_init(void)
+{
+	service_id_t corecfg_svc;
+	int rc;
+
+	assert(corecfg_sess == NULL);
+
+	rc = loc_service_get_id(SERVICE_NAME_CORECFG, &corecfg_svc,
+	    IPC_FLAG_BLOCKING);
+	if (rc != EOK)
+		return ENOENT;
+
+	corecfg_sess = loc_service_connect(EXCHANGE_SERIALIZE, corecfg_svc,
+	    IPC_FLAG_BLOCKING);
+	if (corecfg_sess == NULL)
+		return ENOENT;
+
+	return EOK;
+}
+
+/** Get core dump enable status. */
+int corecfg_get_enable(bool *renable)
+{
+	async_exch_t *exch = async_exchange_begin(corecfg_sess);
+	sysarg_t enable;
+
+	int rc = async_req_0_1(exch, CORECFG_GET_ENABLE, &enable);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	*renable = enable;
+	return EOK;
+}
+
+/** Enable or disable core dumps. */
+int corecfg_set_enable(bool enable)
+{
+	async_exch_t *exch = async_exchange_begin(corecfg_sess);
+	int rc = async_req_1_0(exch, CORECFG_SET_ENABLE, (sysarg_t) enable);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/ddi.c
===================================================================
--- uspace/lib/c/generic/ddi.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/ddi.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -42,4 +42,7 @@
 #include <ddi.h>
 #include <libarch/ddi.h>
+#include <device/hw_res.h>
+#include <device/hw_res_parsed.h>
+#include <device/pio_window.h>
 #include <libc.h>
 #include <task.h>
@@ -76,5 +79,5 @@
  *
  */
-int physmem_map(void *phys, size_t pages, unsigned int flags, void **virt)
+int physmem_map(uintptr_t phys, size_t pages, unsigned int flags, void **virt)
 {
 	return __SYSCALL5(SYS_PHYSMEM_MAP, (sysarg_t) phys,
@@ -83,5 +86,5 @@
 
 int dmamem_map(void *virt, size_t size, unsigned int map_flags,
-    unsigned int flags, void **phys)
+    unsigned int flags, uintptr_t *phys)
 {
 	return (int) __SYSCALL6(SYS_DMAMEM_MAP, (sysarg_t) size,
@@ -90,7 +93,9 @@
 }
 
-int dmamem_map_anonymous(size_t size, unsigned int map_flags,
-    unsigned int flags, void **phys, void **virt)
-{
+int dmamem_map_anonymous(size_t size, uintptr_t constraint,
+    unsigned int map_flags, unsigned int flags, uintptr_t *phys, void **virt)
+{
+	*phys = constraint;
+	
 	return (int) __SYSCALL6(SYS_DMAMEM_MAP, (sysarg_t) size,
 	    (sysarg_t) map_flags, (sysarg_t) flags | DMAMEM_FLAGS_ANONYMOUS,
@@ -132,4 +137,56 @@
 	
 	return __SYSCALL1(SYS_IOSPACE_ENABLE, (sysarg_t) &arg);
+}
+
+/** Enable PIO for specified address range.
+ *
+ * @param range I/O range to be enable.
+ * @param virt  Virtual address for application's PIO operations. 
+ */
+int pio_enable_range(addr_range_t *range, void **virt)
+{
+	return pio_enable(RNGABSPTR(*range), RNGSZ(*range), virt);
+}
+
+/** Enable PIO for specified HW resource wrt. to the PIO window.
+ *
+ * @param win      PIO window. May be NULL if the resources are known to be
+ *                 absolute.
+ * @param res      Resources specifying the I/O range wrt. to the PIO window.
+ * @param virt     Virtual address for application's PIO operations.
+ *
+ * @return EOK on success.
+ * @return Negative error code on failure.
+ *
+ */
+int pio_enable_resource(pio_window_t *win, hw_resource_t *res, void **virt)
+{
+	uintptr_t addr;
+	size_t size;
+
+	switch (res->type) {
+	case IO_RANGE:
+		addr = res->res.io_range.address;
+		if (res->res.io_range.relative) {
+			if (!win)
+				return EINVAL;
+			addr += win->io.base;
+		}
+		size = res->res.io_range.size;
+		break;
+	case MEM_RANGE:
+		addr = res->res.mem_range.address;
+		if (res->res.mem_range.relative) {
+			if (!win)
+				return EINVAL;
+			addr += win->mem.base;
+		}
+		size = res->res.mem_range.size;
+		break;
+	default:
+		return EINVAL;
+	}
+
+	return pio_enable((void *) addr, size, virt);	
 }
 
@@ -158,8 +215,8 @@
 	if (!virt)
 		return EINVAL;
-
-	void *phys_frame =
-	    (void *) ALIGN_DOWN((uintptr_t) pio_addr, PAGE_SIZE);
-	size_t offset = pio_addr - phys_frame;
+	
+	uintptr_t phys_frame =
+	    ALIGN_DOWN((uintptr_t) pio_addr, PAGE_SIZE);
+	size_t offset = (uintptr_t) pio_addr - phys_frame;
 	size_t pages = SIZE2PAGES(offset + size);
 	
Index: uspace/lib/c/generic/device/hw_res.c
===================================================================
--- uspace/lib/c/generic/device/hw_res.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/device/hw_res.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -44,4 +44,5 @@
 	
 	async_exch_t *exch = async_exchange_begin(sess);
+	
 	int rc = async_req_1_1(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
 	    HW_RES_GET_RESOURCE_LIST, &count);
@@ -77,4 +78,5 @@
 {
 	async_exch_t *exch = async_exchange_begin(sess);
+	
 	int rc = async_req_1_0(exch, DEV_IFACE_ID(HW_RES_DEV_IFACE),
 	    HW_RES_ENABLE_INTERRUPT);
@@ -84,4 +86,56 @@
 }
 
+/** 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,
+    unsigned channel, uint32_t pa, uint32_t size, uint8_t mode)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	
+	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.
+ *
+ * @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);
+	
+	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/hw_res_parsed.c
===================================================================
--- uspace/lib/c/generic/device/hw_res_parsed.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/device/hw_res_parsed.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,6 +38,28 @@
 #include <errno.h>
 
-static void hw_res_parse_add_irq(hw_res_list_parsed_t *out, hw_resource_t *res,
-    int flags)
+static void hw_res_parse_add_dma_channel(hw_res_list_parsed_t *out,
+    const hw_resource_t *res, int flags)
+{
+	assert(res);
+	assert((res->type == DMA_CHANNEL_8) || (res->type == DMA_CHANNEL_16));
+	
+	const unsigned channel = (res->type == DMA_CHANNEL_8) ?
+	    res->res.dma_channel.dma8 : res->res.dma_channel.dma16;
+	const size_t count = out->dma_channels.count;
+	const int keep_duplicit = flags & HW_RES_KEEP_DUPLICIT;
+	
+	if (!keep_duplicit) {
+		for (size_t i = 0; i < count; ++i) {
+			if (out->dma_channels.channels[i] == channel)
+				return;
+		}
+	}
+	
+	out->dma_channels.channels[count] = channel;
+	++out->dma_channels.count;
+}
+
+static void hw_res_parse_add_irq(hw_res_list_parsed_t *out,
+    const hw_resource_t *res, int flags)
 {
 	assert(res && (res->type == INTERRUPT));
@@ -58,12 +80,36 @@
 }
 
+static uint64_t absolutize(uint64_t addr, bool relative, uint64_t base)
+{
+	if (!relative)
+		return addr;
+	else
+		return addr + base;
+}
+
+static uint64_t relativize(uint64_t addr, bool relative, uint64_t base)
+{
+	if (relative)
+		return addr;
+	else
+		return addr - base;
+}
+
 static void hw_res_parse_add_io_range(hw_res_list_parsed_t *out,
-    hw_resource_t *res, int flags)
-{
+    const pio_window_t *win, const hw_resource_t *res, int flags)
+{
+	endianness_t endianness;
+	uint64_t absolute;
+	uint64_t relative;
+	size_t size;
+
 	assert(res && (res->type == IO_RANGE));
 	
-	uint64_t address = res->res.io_range.address;
-	endianness_t endianness = res->res.io_range.endianness;
-	size_t size = res->res.io_range.size;
+	absolute = absolutize(res->res.io_range.address,
+	    res->res.io_range.relative, win->io.base);
+	relative = relativize(res->res.io_range.address,
+	    res->res.io_range.relative, win->io.base);
+	size = res->res.io_range.size;
+	endianness = res->res.io_range.endianness;
 	
 	if ((size == 0) && (!(flags & HW_RES_KEEP_ZERO_AREA)))
@@ -75,26 +121,38 @@
 	if (!keep_duplicit) {
 		for (size_t i = 0; i < count; i++) {
-			uint64_t s_address = out->io_ranges.ranges[i].address;
-			size_t s_size = out->io_ranges.ranges[i].size;
+			uint64_t s_address;
+			size_t s_size;
+
+			s_address = RNGABS(out->io_ranges.ranges[i]);
+			s_size = RNGSZ(out->io_ranges.ranges[i]);
 			
-			if ((address == s_address) && (size == s_size))
-				return;
-		}
-	}
-	
-	out->io_ranges.ranges[count].address = address;
+			if ((absolute == s_address) && (size == s_size))
+				return;
+		}
+	}
+	
+	RNGABS(out->io_ranges.ranges[count]) = absolute;
+	RNGREL(out->io_ranges.ranges[count]) = relative;
+	RNGSZ(out->io_ranges.ranges[count]) = size;
 	out->io_ranges.ranges[count].endianness = endianness;
-	out->io_ranges.ranges[count].size = size;
 	out->io_ranges.count++;
 }
 
 static void hw_res_parse_add_mem_range(hw_res_list_parsed_t *out,
-    hw_resource_t *res, int flags)
-{
+    const pio_window_t *win, const hw_resource_t *res, int flags)
+{
+	endianness_t endianness;
+	uint64_t absolute;
+	uint64_t relative;
+	size_t size;
+	
 	assert(res && (res->type == MEM_RANGE));
 	
-	uint64_t address = res->res.mem_range.address;
-	endianness_t endianness = res->res.mem_range.endianness;
-	size_t size = res->res.mem_range.size;
+	absolute = absolutize(res->res.mem_range.address,
+	    res->res.mem_range.relative, win->mem.base);
+	relative = relativize(res->res.mem_range.address,
+	    res->res.mem_range.relative, win->mem.base);
+	size = res->res.mem_range.size;
+	endianness = res->res.mem_range.endianness;
 	
 	if ((size == 0) && (!(flags & HW_RES_KEEP_ZERO_AREA)))
@@ -106,15 +164,19 @@
 	if (!keep_duplicit) {
 		for (size_t i = 0; i < count; ++i) {
-			uint64_t s_address = out->mem_ranges.ranges[i].address;
-			size_t s_size = out->mem_ranges.ranges[i].size;
+			uint64_t s_address;
+			size_t s_size;
+
+			s_address = RNGABS(out->mem_ranges.ranges[i]);;
+			s_size = RNGSZ(out->mem_ranges.ranges[i]);
 			
-			if ((address == s_address) && (size == s_size))
-				return;
-		}
-	}
-	
-	out->mem_ranges.ranges[count].address = address;
+			if ((absolute == s_address) && (size == s_size))
+				return;
+		}
+	}
+	
+	RNGABS(out->mem_ranges.ranges[count]) = absolute;
+	RNGREL(out->mem_ranges.ranges[count]) = relative;
+	RNGSZ(out->mem_ranges.ranges[count]) = size;
 	out->mem_ranges.ranges[count].endianness = endianness;
-	out->mem_ranges.ranges[count].size = size;
 	out->mem_ranges.count++;
 }
@@ -122,29 +184,35 @@
 /** Parse list of hardware resources
  *
- * @param      hw_resources Original structure resource
- * @param[out] out          Output parsed resources
- * @param      flags        Flags of the parsing.
- *                          HW_RES_KEEP_ZERO_AREA for keeping
- *                          zero-size areas, HW_RES_KEEP_DUPLICITIES
- *                          for keep duplicit areas
+ * @param      win          PIO window.
+ * @param      res          Original structure resource.
+ * @param[out] out          Output parsed resources.
+ * @param      flags        Flags of the parsing:
+ *                          HW_RES_KEEP_ZERO_AREA for keeping zero-size areas,
+ *                          HW_RES_KEEP_DUPLICITIES to keep duplicit areas.
  *
  * @return EOK if succeed, error code otherwise.
  *
  */
-int hw_res_list_parse(hw_resource_list_t *hw_resources,
-    hw_res_list_parsed_t *out, int flags)
-{
-	if ((!hw_resources) || (!out))
+int hw_res_list_parse(const pio_window_t *win,
+    const hw_resource_list_t *res, hw_res_list_parsed_t *out, int flags)
+{
+	if (!res || !out)
 		return EINVAL;
 	
-	size_t res_count = hw_resources->count;
+	size_t res_count = res->count;
 	hw_res_list_parsed_clean(out);
 	
-	out->irqs.irqs = malloc(res_count * sizeof(int));
-	out->io_ranges.ranges = malloc(res_count * sizeof(io_range_t));
-	out->mem_ranges.ranges = malloc(res_count * sizeof(mem_range_t));
+	out->irqs.irqs = calloc(res_count, sizeof(int));
+	out->dma_channels.channels = calloc(res_count, sizeof(int));
+	out->io_ranges.ranges = calloc(res_count, sizeof(io_range_t));
+	out->mem_ranges.ranges = calloc(res_count, sizeof(mem_range_t));
+	if (!out->irqs.irqs || !out->dma_channels.channels ||
+	    !out->io_ranges.ranges || !out->mem_ranges.ranges) {
+		hw_res_list_parsed_clean(out);
+		return ENOMEM;
+	}
 	
 	for (size_t i = 0; i < res_count; ++i) {
-		hw_resource_t *resource = &(hw_resources->resources[i]);
+		const hw_resource_t *resource = &res->resources[i];
 		
 		switch (resource->type) {
@@ -153,10 +221,15 @@
 			break;
 		case IO_RANGE:
-			hw_res_parse_add_io_range(out, resource, flags);
+			hw_res_parse_add_io_range(out, win, resource, flags);
 			break;
 		case MEM_RANGE:
-			hw_res_parse_add_mem_range(out, resource, flags);
+			hw_res_parse_add_mem_range(out, win, resource, flags);
+			break;
+		case DMA_CHANNEL_8:
+		case DMA_CHANNEL_16:
+			hw_res_parse_add_dma_channel(out, resource, flags);
 			break;
 		default:
+			hw_res_list_parsed_clean(out);
 			return EINVAL;
 		}
@@ -183,4 +256,7 @@
     hw_res_list_parsed_t *hw_res_parsed, int flags)
 {
+	pio_window_t pio_window;
+	int rc;
+
 	if (!hw_res_parsed)
 		return EBADMEM;
@@ -189,10 +265,15 @@
 	hw_res_list_parsed_clean(hw_res_parsed);
 	memset(&hw_resources, 0, sizeof(hw_resource_list_t));
-	
-	int rc = hw_res_get_resource_list(sess, &hw_resources);
+
+	rc = pio_window_get(sess, &pio_window);
+	if (rc != EOK)
+		return rc; 
+	
+	rc = hw_res_get_resource_list(sess, &hw_resources);
 	if (rc != EOK)
 		return rc;
-	
-	rc = hw_res_list_parse(&hw_resources, hw_res_parsed, flags);
+
+	rc = hw_res_list_parse(&pio_window, &hw_resources, hw_res_parsed,
+	    flags);
 	hw_res_clean_resource_list(&hw_resources);
 	
Index: uspace/lib/c/generic/device/pio_window.c
===================================================================
--- uspace/lib/c/generic/device/pio_window.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/c/generic/device/pio_window.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/devman.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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.
@@ -230,9 +230,5 @@
 	}
 	
-	match_id_t *match_id = NULL;
-	
-	list_foreach(match_ids->ids, link) {
-		match_id = list_get_instance(link, match_id_t, link);
-		
+	list_foreach(match_ids->ids, link, match_id_t, match_id) {
 		ipc_call_t answer2;
 		aid_t req2 = async_send_1(exch, DEVMAN_ADD_MATCH_ID,
@@ -405,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;
@@ -416,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);
@@ -434,4 +430,6 @@
 	}
 	
+	if (r1 != NULL)
+		*r1 = IPC_GET_ARG1(answer);
 	act_size = IPC_GET_ARG2(dreply);
 	assert(act_size <= buf_size - 1);
@@ -443,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);
 }
 
@@ -583,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)
 {
@@ -596,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/dhcp.c
===================================================================
--- uspace/lib/c/generic/dhcp.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/c/generic/dhcp.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,87 @@
+/*
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#include <async.h>
+#include <assert.h>
+#include <errno.h>
+#include <inet/dhcp.h>
+#include <ipc/dhcp.h>
+#include <ipc/services.h>
+#include <loc.h>
+#include <stdlib.h>
+
+static async_sess_t *dhcp_sess = NULL;
+
+int dhcp_init(void)
+{
+	service_id_t dhcp_svc;
+	int rc;
+
+	assert(dhcp_sess == NULL);
+
+	rc = loc_service_get_id(SERVICE_NAME_DHCP, &dhcp_svc,
+	    IPC_FLAG_BLOCKING);
+	if (rc != EOK)
+		return ENOENT;
+
+	dhcp_sess = loc_service_connect(EXCHANGE_SERIALIZE, dhcp_svc,
+	    IPC_FLAG_BLOCKING);
+	if (dhcp_sess == NULL)
+		return ENOENT;
+
+	return EOK;
+}
+
+int dhcp_link_add(sysarg_t link_id)
+{
+	async_exch_t *exch = async_exchange_begin(dhcp_sess);
+
+	int rc = async_req_1_0(exch, DHCP_LINK_ADD, link_id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+int dhcp_link_remove(sysarg_t link_id)
+{
+	async_exch_t *exch = async_exchange_begin(dhcp_sess);
+
+	int rc = async_req_1_0(exch, DHCP_LINK_REMOVE, link_id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/dnsr.c
===================================================================
--- uspace/lib/c/generic/dnsr.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/dnsr.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -67,5 +67,5 @@
 }
 
-int dnsr_name2host(const char *name, dnsr_hostinfo_t **rinfo, uint16_t af)
+int dnsr_name2host(const char *name, dnsr_hostinfo_t **rinfo, ip_ver_t ver)
 {
 	dnsr_hostinfo_t *info = calloc(1, sizeof(dnsr_hostinfo_t));
@@ -76,5 +76,5 @@
 	
 	ipc_call_t answer;
-	aid_t req = async_send_1(exch, DNSR_NAME2HOST, (sysarg_t) af,
+	aid_t req = async_send_1(exch, DNSR_NAME2HOST, (sysarg_t) ver,
 	    &answer);
 	
Index: uspace/lib/c/generic/futex.c
===================================================================
--- uspace/lib/c/generic/futex.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/futex.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -35,4 +35,5 @@
 #include <futex.h>
 #include <atomic.h>
+#include <libarch/barrier.h>
 #include <libc.h>
 #include <sys/types.h>
@@ -59,5 +60,10 @@
 int futex_trydown(futex_t *futex)
 {
-	return cas(futex, 1, 0);
+	int rc;
+
+	rc = cas(futex, 1, 0);
+	CS_ENTER_BARRIER();
+
+	return rc;
 }
 
@@ -73,5 +79,9 @@
 int futex_down(futex_t *futex)
 {
-	if ((atomic_signed_t) atomic_predec(futex) < 0)
+	atomic_signed_t nv;
+
+	nv = (atomic_signed_t) atomic_predec(futex);
+	CS_ENTER_BARRIER();
+	if (nv < 0)
 		return __SYSCALL1(SYS_FUTEX_SLEEP, (sysarg_t) &futex->count);
 	
@@ -89,4 +99,6 @@
 int futex_up(futex_t *futex)
 {
+	CS_LEAVE_BARRIER();
+
 	if ((atomic_signed_t) atomic_postinc(futex) < 0)
 		return __SYSCALL1(SYS_FUTEX_WAKEUP, (sysarg_t) &futex->count);
Index: uspace/lib/c/generic/inet.c
===================================================================
--- uspace/lib/c/generic/inet.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/inet.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -30,5 +30,4 @@
 #include <assert.h>
 #include <errno.h>
-#include <net/socket_codes.h>
 #include <inet/inet.h>
 #include <ipc/inet.h>
@@ -111,6 +110,6 @@
 	
 	ipc_call_t answer;
-	aid_t req = async_send_3(exch, INET_SEND, dgram->tos, ttl, df,
-	    &answer);
+	aid_t req = async_send_4(exch, INET_SEND, dgram->iplink, dgram->tos,
+	    ttl, df, &answer);
 	
 	int rc = async_data_write_start(exch, &dgram->src, sizeof(inet_addr_t));
Index: uspace/lib/c/generic/inet/addr.c
===================================================================
--- uspace/lib/c/generic/inet/addr.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/inet/addr.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -33,4 +33,5 @@
  */
 
+#include <assert.h>
 #include <errno.h>
 #include <unistd.h>
@@ -48,4 +49,6 @@
 #endif
 
+const addr32_t addr32_broadcast_all_hosts = 0xffffffff;
+
 const addr48_t addr48_broadcast = {
 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
@@ -57,10 +60,10 @@
 
 static const inet_addr_t inet_addr_any_addr = {
-	.family = AF_INET,
+	.version = ip_v4,
 	.addr = 0
 };
 
 static const inet_addr_t inet_addr_any_addr6 = {
-	.family = AF_INET6,
+	.version = ip_v6,
 	.addr6 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
 };
@@ -76,12 +79,20 @@
 }
 
+/** Compare addr48.
+  *
+  * @return Non-zero if equal, zero if not equal.
+  */
 int addr48_compare(const addr48_t a, const addr48_t b)
 {
-	return memcmp(a, b, 6);
-}
-
+	return memcmp(a, b, 6) == 0;
+}
+
+/** Compare addr128.
+  *
+  * @return Non-zero if equal, zero if not equal.
+  */
 int addr128_compare(const addr128_t a, const addr128_t b)
 {
-	return memcmp(a, b, 16);
+	return memcmp(a, b, 16) == 0;
 }
 
@@ -100,53 +111,15 @@
 void host2addr128_t_be(const addr128_t host, addr128_t be)
 {
-#ifdef __BE__
 	memcpy(be, host, 16);
-#else
-	be[0] = host[15];
-	be[1] = host[14];
-	be[2] = host[13];
-	be[3] = host[12];
-	be[4] = host[11];
-	be[5] = host[10];
-	be[6] = host[9];
-	be[7] = host[8];
-	be[8] = host[7];
-	be[9] = host[6];
-	be[10] = host[5];
-	be[11] = host[4];
-	be[12] = host[3];
-	be[13] = host[2];
-	be[14] = host[1];
-	be[15] = host[0];
-#endif
 }
 
 void addr128_t_be2host(const addr128_t be, addr128_t host)
 {
-#ifdef __BE__
 	memcpy(host, be, 16);
-#else
-	host[0] = be[15];
-	host[1] = be[14];
-	host[2] = be[13];
-	host[3] = be[12];
-	host[4] = be[11];
-	host[5] = be[10];
-	host[6] = be[9];
-	host[7] = be[8];
-	host[8] = be[7];
-	host[9] = be[6];
-	host[10] = be[5];
-	host[11] = be[4];
-	host[12] = be[3];
-	host[13] = be[2];
-	host[14] = be[1];
-	host[15] = be[0];
-#endif
 }
 
 void inet_addr(inet_addr_t *addr, uint8_t a, uint8_t b, uint8_t c, uint8_t d)
 {
-	addr->family = AF_INET;
+	addr->version = ip_v4;
 	addr->addr = ((addr32_t) a << 24) | ((addr32_t) b << 16) |
 	    ((addr32_t) c << 8) | ((addr32_t) d);
@@ -156,5 +129,5 @@
     uint8_t prefix)
 {
-	naddr->family = AF_INET;
+	naddr->version = ip_v4;
 	naddr->addr = ((addr32_t) a << 24) | ((addr32_t) b << 16) |
 	    ((addr32_t) c << 8) | ((addr32_t) d);
@@ -165,5 +138,5 @@
     uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h)
 {
-	addr->family = AF_INET6;
+	addr->version = ip_v6;
 	addr->addr6[0] = (a >> 8) & 0xff;
 	addr->addr6[1] = a & 0xff;
@@ -187,5 +160,5 @@
     uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h, uint8_t prefix)
 {
-	naddr->family = AF_INET6;
+	naddr->version = ip_v6;
 	naddr->addr6[0] = (a >> 8) & 0xff;
 	naddr->addr6[1] = a & 0xff;
@@ -207,32 +180,62 @@
 }
 
-/** Parse network address family.
- *
- * @param text Network address in common notation.
- * @param af   Place to store network address family.
+/** Determine address version.
+ *
+ * @param text Address in common notation.
+ * @param af   Place to store address version.
  *
  * @return EOK on success, EINVAL if input is not in valid format.
  *
  */
-int inet_addr_family(const char *text, uint16_t *af)
+static int inet_addr_version(const char *text, ip_ver_t *ver)
 {
 	char *dot = str_chr(text, '.');
 	if (dot != NULL) {
-		*af = AF_INET;
+		*ver = ip_v4;
 		return EOK;
 	}
-	
+
 	char *collon = str_chr(text, ':');
 	if (collon != NULL) {
-		*af = AF_INET6;
+		*ver = ip_v6;
 		return EOK;
 	}
-	
+
 	return EINVAL;
 }
 
+static int ipver_af(ip_ver_t ver)
+{
+	switch (ver) {
+	case ip_any:
+		return AF_NONE;
+	case ip_v4:
+		return AF_INET;
+	case ip_v6:
+		return AF_INET6;
+	default:
+		assert(false);
+		return EINVAL;
+	}
+}
+
+ip_ver_t ipver_from_af(int af)
+{
+	switch (af) {
+	case AF_NONE:
+		return ip_any;
+	case AF_INET:
+		return ip_v4;
+	case AF_INET6:
+		return ip_v6;
+	default:
+		assert(false);
+		return EINVAL;
+	}
+}
+
 void inet_naddr_addr(const inet_naddr_t *naddr, inet_addr_t *addr)
 {
-	addr->family = naddr->family;
+	addr->version = naddr->version;
 	memcpy(addr->addr6, naddr->addr6, 16);
 }
@@ -241,5 +244,5 @@
     inet_naddr_t *naddr)
 {
-	naddr->family = addr->family;
+	naddr->version = addr->version;
 	memcpy(naddr->addr6, addr->addr6, 16);
 	naddr->prefix = prefix;
@@ -248,5 +251,5 @@
 void inet_addr_any(inet_addr_t *addr)
 {
-	addr->family = AF_NONE;
+	addr->version = ip_any;
 	memset(addr->addr6, 0, 16);
 }
@@ -254,5 +257,5 @@
 void inet_naddr_any(inet_naddr_t *naddr)
 {
-	naddr->family = AF_NONE;
+	naddr->version = ip_any;
 	memset(naddr->addr6, 0, 16);
 	naddr->prefix = 0;
@@ -261,11 +264,11 @@
 int inet_addr_compare(const inet_addr_t *a, const inet_addr_t *b)
 {
-	if (a->family != b->family)
+	if (a->version != b->version)
 		return 0;
-	
-	switch (a->family) {
-	case AF_INET:
+
+	switch (a->version) {
+	case ip_v4:
 		return (a->addr == b->addr);
-	case AF_INET6:
+	case ip_v6:
 		return addr128_compare(a->addr6, b->addr6);
 	default:
@@ -276,5 +279,5 @@
 int inet_addr_is_any(const inet_addr_t *addr)
 {
-	return ((addr->family == 0) ||
+	return ((addr->version == ip_any) ||
 	    (inet_addr_compare(addr, &inet_addr_any_addr)) ||
 	    (inet_addr_compare(addr, &inet_addr_any_addr6)));
@@ -283,11 +286,11 @@
 int inet_naddr_compare(const inet_naddr_t *naddr, const inet_addr_t *addr)
 {
-	if (naddr->family != addr->family)
+	if (naddr->version != addr->version)
 		return 0;
 	
-	switch (naddr->family) {
-	case AF_INET:
+	switch (naddr->version) {
+	case ip_v4:
 		return (naddr->addr == addr->addr);
-	case AF_INET6:
+	case ip_v6:
 		return addr128_compare(naddr->addr6, addr->addr6);
 	default:
@@ -298,16 +301,16 @@
 int inet_naddr_compare_mask(const inet_naddr_t *naddr, const inet_addr_t *addr)
 {
-	if (naddr->family != addr->family)
+	if (naddr->version != addr->version)
 		return 0;
-	
-	switch (naddr->family) {
-	case AF_INET:
+
+	switch (naddr->version) {
+	case ip_v4:
 		if (naddr->prefix > 32)
 			return 0;
-		
+
 		addr32_t mask =
 		    BIT_RANGE(addr32_t, 31, 31 - (naddr->prefix - 1));
 		return ((naddr->addr & mask) == (addr->addr & mask));
-	case AF_INET6:
+	case ip_v6:
 		if (naddr->prefix > 128)
 			return 0;
@@ -350,19 +353,19 @@
 int inet_addr_parse(const char *text, inet_addr_t *addr)
 {
-	int rc = inet_addr_family(text, &addr->family);
+	int rc = inet_addr_version(text, &addr->version);
 	if (rc != EOK)
 		return rc;
 	
 	uint8_t buf[16];
-	rc = inet_pton(addr->family, text, buf);
+	rc = inet_pton(ipver_af(addr->version), text, buf);
 	if (rc != EOK)
 		return rc;
 	
-	switch (addr->family) {
-	case AF_INET:
+	switch (addr->version) {
+	case ip_v4:
 		addr->addr = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) |
 		    buf[3];
 		break;
-	case AF_INET6:
+	case ip_v6:
 		memcpy(addr->addr6, buf, 16);
 		break;
@@ -390,10 +393,10 @@
 	*slash = 0;
 	
-	int rc = inet_addr_family(text, &naddr->family);
+	int rc = inet_addr_version(text, &naddr->version);
 	if (rc != EOK)
 		return rc;
 	
 	uint8_t buf[16];
-	rc = inet_pton(naddr->family, text, buf);
+	rc = inet_pton(ipver_af(naddr->version), text, buf);
 	*slash = '/';
 	
@@ -404,6 +407,6 @@
 	uint8_t prefix;
 	
-	switch (naddr->family) {
-	case AF_INET:
+	switch (naddr->version) {
+	case ip_v4:
 		prefix = strtoul(slash, &slash, 10);
 		if (prefix > 32)
@@ -415,5 +418,5 @@
 		
 		break;
-	case AF_INET6:
+	case ip_v6:
 		prefix = strtoul(slash, &slash, 10);
 		if (prefix > 128)
@@ -445,14 +448,14 @@
 	int rc = 0;
 	
-	switch (addr->family) {
-	case AF_NONE:
+	switch (addr->version) {
+	case ip_any:
 		rc = asprintf(bufp, "none");
 		break;
-	case AF_INET:
+	case ip_v4:
 		rc = asprintf(bufp, "%u.%u.%u.%u", (addr->addr >> 24) & 0xff,
 		    (addr->addr >> 16) & 0xff, (addr->addr >> 8) & 0xff,
 		    addr->addr & 0xff);
 		break;
-	case AF_INET6:
+	case ip_v6:
 		*bufp = (char *) malloc(INET6_ADDRSTRLEN);
 		if (*bufp == NULL)
@@ -485,9 +488,9 @@
 	char prefix[INET_PREFIXSTRSIZE];
 	
-	switch (naddr->family) {
-	case AF_NONE:
+	switch (naddr->version) {
+	case ip_any:
 		rc = asprintf(bufp, "none");
 		break;
-	case AF_INET:
+	case ip_v4:
 		rc = asprintf(bufp, "%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8
 		    "/%" PRIu8, (naddr->addr >> 24) & 0xff,
@@ -495,5 +498,5 @@
 		    naddr->addr & 0xff, naddr->prefix);
 		break;
-	case AF_INET6:
+	case ip_v6:
 		*bufp = (char *) malloc(INET6_ADDRSTRLEN + INET_PREFIXSTRSIZE);
 		if (*bufp == NULL)
@@ -527,50 +530,50 @@
 }
 
-uint16_t inet_addr_get(const inet_addr_t *addr, addr32_t *v4, addr128_t *v6)
-{
-	switch (addr->family) {
-	case AF_INET:
+ip_ver_t inet_addr_get(const inet_addr_t *addr, addr32_t *v4, addr128_t *v6)
+{
+	switch (addr->version) {
+	case ip_v4:
 		if (v4 != NULL)
 			*v4 = addr->addr;
-		
-		break;
-	case AF_INET6:
+		break;
+	case ip_v6:
 		if (v6 != NULL)
 			memcpy(*v6, addr->addr6, 16);
-		
-		break;
-	}
-	
-	return addr->family;
-}
-
-uint16_t inet_naddr_get(const inet_naddr_t *naddr, addr32_t *v4, addr128_t *v6,
+		break;
+	default:
+		assert(false);
+		break;
+	}
+
+	return addr->version;
+}
+
+ip_ver_t inet_naddr_get(const inet_naddr_t *naddr, addr32_t *v4, addr128_t *v6,
     uint8_t *prefix)
 {
-	switch (naddr->family) {
-	case AF_INET:
+	switch (naddr->version) {
+	case ip_v4:
 		if (v4 != NULL)
 			*v4 = naddr->addr;
-		
 		if (prefix != NULL)
 			*prefix = naddr->prefix;
-		
-		break;
-	case AF_INET6:
+		break;
+	case ip_v6:
 		if (v6 != NULL)
 			memcpy(*v6, naddr->addr6, 16);
-		
 		if (prefix != NULL)
 			*prefix = naddr->prefix;
-		
-		break;
-	}
-	
-	return naddr->family;
+		break;
+	default:
+		assert(false);
+		break;
+	}
+
+	return naddr->version;
 }
 
 void inet_addr_set(addr32_t v4, inet_addr_t *addr)
 {
-	addr->family = AF_INET;
+	addr->version = ip_v4;
 	addr->addr = v4;
 }
@@ -578,5 +581,5 @@
 void inet_naddr_set(addr32_t v4, uint8_t prefix, inet_naddr_t *naddr)
 {
-	naddr->family = AF_INET;
+	naddr->version = ip_v4;
 	naddr->addr = v4;
 	naddr->prefix = prefix;
@@ -585,5 +588,5 @@
 void inet_sockaddr_in_addr(const sockaddr_in_t *sockaddr_in, inet_addr_t *addr)
 {
-	addr->family = AF_INET;
+	addr->version = ip_v4;
 	addr->addr = uint32_t_be2host(sockaddr_in->sin_addr.s_addr);
 }
@@ -591,5 +594,5 @@
 void inet_addr_set6(addr128_t v6, inet_addr_t *addr)
 {
-	addr->family = AF_INET6;
+	addr->version = ip_v6;
 	memcpy(addr->addr6, v6, 16);
 }
@@ -597,5 +600,5 @@
 void inet_naddr_set6(addr128_t v6, uint8_t prefix, inet_naddr_t *naddr)
 {
-	naddr->family = AF_INET6;
+	naddr->version = ip_v6;
 	memcpy(naddr->addr6, v6, 16);
 	naddr->prefix = prefix;
@@ -605,5 +608,5 @@
     inet_addr_t *addr)
 {
-	addr->family = AF_INET6;
+	addr->version = ip_v6;
 	addr128_t_be2host(sockaddr_in6->sin6_addr.s6_addr, addr->addr6);
 }
@@ -612,22 +615,64 @@
     sockaddr_in_t *sockaddr_in, sockaddr_in6_t *sockaddr_in6)
 {
-	switch (addr->family) {
-	case AF_INET:
+	switch (addr->version) {
+	case ip_v4:
 		if (sockaddr_in != NULL) {
 			sockaddr_in->sin_family = AF_INET;
 			sockaddr_in->sin_addr.s_addr = host2uint32_t_be(addr->addr);
 		}
-		
-		break;
-	case AF_INET6:
+		break;
+	case ip_v6:
 		if (sockaddr_in6 != NULL) {
 			sockaddr_in6->sin6_family = AF_INET6;
 			host2addr128_t_be(addr->addr6, sockaddr_in6->sin6_addr.s6_addr);
 		}
-		
-		break;
-	}
-	
-	return addr->family;
+		break;
+	default:
+		assert(false);
+		break;
+	}
+
+	return ipver_af(addr->version);
+}
+
+int inet_addr_sockaddr(const inet_addr_t *addr, uint16_t port,
+    sockaddr_t **nsockaddr, socklen_t *naddrlen)
+{
+	sockaddr_in_t *sa4;
+	sockaddr_in6_t *sa6;
+
+	switch (addr->version) {
+	case ip_v4:
+		sa4 = calloc(1, sizeof(sockaddr_in_t));
+		if (sa4 == NULL)
+			return ENOMEM;
+
+		sa4->sin_family = AF_INET;
+		sa4->sin_port = host2uint16_t_be(port);
+		sa4->sin_addr.s_addr = host2uint32_t_be(addr->addr);
+		if (nsockaddr != NULL)
+			*nsockaddr = (sockaddr_t *)sa4;
+		if (naddrlen != NULL)
+			*naddrlen = sizeof(*sa4);
+		break;
+	case ip_v6:
+		sa6 = calloc(1, sizeof(sockaddr_in6_t));
+		if (sa6 == NULL)
+			return ENOMEM;
+
+		sa6->sin6_family = AF_INET6;
+		sa6->sin6_port = host2uint16_t_be(port);
+		host2addr128_t_be(addr->addr6, sa6->sin6_addr.s6_addr);
+		if (nsockaddr != NULL)
+			*nsockaddr = (sockaddr_t *)sa6;
+		if (naddrlen != NULL)
+			*naddrlen = sizeof(*sa6);
+		break;
+	default:
+		assert(false);
+		break;
+	}
+
+	return EOK;
 }
 
Index: uspace/lib/c/generic/inetcfg.c
===================================================================
--- uspace/lib/c/generic/inetcfg.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/inetcfg.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -267,4 +267,14 @@
 }
 
+int inetcfg_link_add(sysarg_t link_id)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	int rc = async_req_1_0(exch, INETCFG_LINK_ADD, link_id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
 int inetcfg_link_get(sysarg_t link_id, inet_link_info_t *linfo)
 {
@@ -279,9 +289,10 @@
 	aid_t req = async_send_1(exch, INETCFG_LINK_GET, link_id, &answer);
 	aid_t dreq = async_data_read(exch, name_buf, LOC_NAME_MAXLEN, &dreply);
+	int rc = async_data_read_start(exch, &linfo->mac_addr, sizeof(addr48_t));
 	async_wait_for(dreq, &dretval);
 
 	async_exchange_end(exch);
 
-	if (dretval != EOK) {
+	if (dretval != EOK || rc != EOK) {
 		async_forget(req);
 		return dretval;
@@ -302,4 +313,14 @@
 
 	return EOK;
+}
+
+int inetcfg_link_remove(sysarg_t link_id)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	int rc = async_req_1_0(exch, INETCFG_LINK_REMOVE, link_id);
+	async_exchange_end(exch);
+
+	return rc;
 }
 
Index: uspace/lib/c/generic/inetping.c
===================================================================
--- uspace/lib/c/generic/inetping.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/inetping.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1,4 +1,5 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
+ * Copyright (c) 2013 Martin Decky
  * All rights reserved.
  *
@@ -49,22 +50,22 @@
 
 	assert(inetping_sess == NULL);
-	
+
 	inetping_ev_ops = ev_ops;
-	
+
 	rc = loc_service_get_id(SERVICE_NAME_INETPING, &inetping_svc,
 	    IPC_FLAG_BLOCKING);
 	if (rc != EOK)
 		return ENOENT;
-	
+
 	inetping_sess = loc_service_connect(EXCHANGE_SERIALIZE, inetping_svc,
 	    IPC_FLAG_BLOCKING);
 	if (inetping_sess == NULL)
 		return ENOENT;
-	
+
 	async_exch_t *exch = async_exchange_begin(inetping_sess);
 
 	rc = async_connect_to_me(exch, 0, 0, 0, inetping_cb_conn, NULL);
 	async_exchange_end(exch);
-	
+
 	if (rc != EOK) {
 		async_hangup(inetping_sess);
@@ -72,5 +73,5 @@
 		return rc;
 	}
-	
+
 	return EOK;
 }
@@ -79,55 +80,126 @@
 {
 	async_exch_t *exch = async_exchange_begin(inetping_sess);
-	
+
 	ipc_call_t answer;
-	aid_t req = async_send_3(exch, INETPING_SEND, (sysarg_t) sdu->src,
-	    (sysarg_t) sdu->dest, sdu->seq_no, &answer);
-	sysarg_t retval = async_data_write_start(exch, sdu->data, sdu->size);
-	
+	aid_t req = async_send_1(exch, INETPING_SEND, sdu->seq_no, &answer);
+
+	int rc = async_data_write_start(exch, &sdu->src, sizeof(sdu->src));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_write_start(exch, &sdu->dest, sizeof(sdu->dest));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_write_start(exch, sdu->data, sdu->size);
+
 	async_exchange_end(exch);
-	
-	if (retval != EOK) {
-		async_forget(req);
-		return retval;
-	}
-	
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	sysarg_t retval;
 	async_wait_for(req, &retval);
-	return retval;
-}
-
-int inetping_get_srcaddr(uint32_t remote, uint32_t *local)
+
+	return (int) retval;
+}
+
+int inetping_get_srcaddr(const inet_addr_t *remote, inet_addr_t *local)
 {
 	async_exch_t *exch = async_exchange_begin(inetping_sess);
-	
-	sysarg_t local_addr;
-	int rc = async_req_1_1(exch, INETPING_GET_SRCADDR, (sysarg_t) remote,
-	    &local_addr);
-	
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, INETPING_GET_SRCADDR, &answer);
+
+	int rc = async_data_write_start(exch, remote, sizeof(*remote));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	ipc_call_t answer_local;
+	aid_t req_local = async_data_read(exch, local, sizeof(*local),
+	    &answer_local);
+
 	async_exchange_end(exch);
-	
-	if (rc != EOK)
-		return rc;
-	
-	*local = (uint32_t) local_addr;
-	return EOK;
-}
-
-static void inetping_ev_recv(ipc_callid_t callid, ipc_call_t *call)
+
+	sysarg_t retval_local;
+	async_wait_for(req_local, &retval_local);
+
+	if (retval_local != EOK) {
+		async_forget(req);
+		return (int) retval_local;
+	}
+
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+
+	return (int) retval;
+}
+
+static void inetping_ev_recv(ipc_callid_t iid, ipc_call_t *icall)
 {
 	inetping_sdu_t sdu;
-	
-	sdu.src = IPC_GET_ARG1(*call);
-	sdu.dest = IPC_GET_ARG2(*call);
-	sdu.seq_no = IPC_GET_ARG3(*call);
-	
-	int rc = async_data_write_accept(&sdu.data, false, 0, 0, 0, &sdu.size);
+
+	sdu.seq_no = IPC_GET_ARG1(*icall);
+
+	ipc_callid_t callid;
+	size_t size;
+	if (!async_data_write_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(sdu.src)) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	int rc = async_data_write_finalize(callid, &sdu.src, size);
 	if (rc != EOK) {
 		async_answer_0(callid, rc);
-		return;
-	}
-	
+		async_answer_0(iid, rc);
+		return;
+	}
+
+	if (!async_data_write_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(sdu.dest)) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	rc = async_data_write_finalize(callid, &sdu.dest, size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+
+	rc = async_data_write_accept(&sdu.data, false, 0, 0, 0, &sdu.size);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+
 	rc = inetping_ev_ops->recv(&sdu);
 	free(sdu.data);
-	async_answer_0(callid, rc);
+	async_answer_0(iid, rc);
 }
 
Index: pace/lib/c/generic/inetping6.c
===================================================================
--- uspace/lib/c/generic/inetping6.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ 	(revision )
@@ -1,225 +1,0 @@
-/*
- * Copyright (c) 2013 Martin Decky
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <async.h>
-#include <assert.h>
-#include <errno.h>
-#include <inet/inetping6.h>
-#include <ipc/inet.h>
-#include <ipc/services.h>
-#include <loc.h>
-#include <stdlib.h>
-#include <str.h>
-
-static void inetping6_cb_conn(ipc_callid_t, ipc_call_t *, void *);
-static void inetping6_ev_recv(ipc_callid_t, ipc_call_t *);
-
-static async_sess_t *inetping6_sess = NULL;
-static inetping6_ev_ops_t *inetping6_ev_ops;
-
-int inetping6_init(inetping6_ev_ops_t *ev_ops)
-{
-	assert(inetping6_sess == NULL);
-	
-	inetping6_ev_ops = ev_ops;
-	
-	service_id_t inetping6_svc;
-	int rc = loc_service_get_id(SERVICE_NAME_INETPING6, &inetping6_svc,
-	    IPC_FLAG_BLOCKING);
-	if (rc != EOK)
-		return ENOENT;
-	
-	inetping6_sess = loc_service_connect(EXCHANGE_SERIALIZE, inetping6_svc,
-	    IPC_FLAG_BLOCKING);
-	if (inetping6_sess == NULL)
-		return ENOENT;
-	
-	async_exch_t *exch = async_exchange_begin(inetping6_sess);
-	
-	rc = async_connect_to_me(exch, 0, 0, 0, inetping6_cb_conn, NULL);
-	async_exchange_end(exch);
-	
-	if (rc != EOK) {
-		async_hangup(inetping6_sess);
-		inetping6_sess = NULL;
-		return rc;
-	}
-	
-	return EOK;
-}
-
-int inetping6_send(inetping6_sdu_t *sdu)
-{
-	async_exch_t *exch = async_exchange_begin(inetping6_sess);
-	
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, INETPING6_SEND, sdu->seq_no, &answer);
-	
-	int rc = async_data_write_start(exch, &sdu->src, sizeof(addr128_t));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-	
-	rc = async_data_write_start(exch, &sdu->dest, sizeof(addr128_t));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-	
-	rc = async_data_write_start(exch, sdu->data, sdu->size);
-	
-	async_exchange_end(exch);
-	
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-	
-	sysarg_t retval;
-	async_wait_for(req, &retval);
-	
-	return (int) retval;
-}
-
-int inetping6_get_srcaddr(addr128_t remote, addr128_t local)
-{
-	async_exch_t *exch = async_exchange_begin(inetping6_sess);
-	
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, INETPING6_GET_SRCADDR, &answer);
-	
-	int rc = async_data_write_start(exch, remote, sizeof(addr128_t));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-	
-	ipc_call_t answer_local;
-	aid_t req_local = async_data_read(exch, local, sizeof(addr128_t),
-	    &answer_local);
-	
-	async_exchange_end(exch);
-	
-	sysarg_t retval_local;
-	async_wait_for(req_local, &retval_local);
-	
-	if (retval_local != EOK) {
-		async_forget(req);
-		return (int) retval_local;
-	}
-	
-	sysarg_t retval;
-	async_wait_for(req, &retval);
-	
-	return (int) retval;
-}
-
-static void inetping6_ev_recv(ipc_callid_t iid, ipc_call_t *icall)
-{
-	inetping6_sdu_t sdu;
-	
-	sdu.seq_no = IPC_GET_ARG1(*icall);
-	
-	ipc_callid_t callid;
-	size_t size;
-	if (!async_data_write_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	if (size != sizeof(addr128_t)) {
-		async_answer_0(callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	int rc = async_data_write_finalize(callid, &sdu.src, size);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	if (!async_data_write_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	if (size != sizeof(addr128_t)) {
-		async_answer_0(callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	rc = async_data_write_finalize(callid, &sdu.dest, size);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	rc = async_data_write_accept(&sdu.data, false, 0, 0, 0, &sdu.size);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	rc = inetping6_ev_ops->recv(&sdu);
-	free(sdu.data);
-	async_answer_0(iid, rc);
-}
-
-static void inetping6_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call)) {
-			/* TODO: Handle hangup */
-			return;
-		}
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case INETPING6_EV_RECV:
-			inetping6_ev_recv(callid, &call);
-			break;
-		default:
-			async_answer_0(callid, ENOTSUP);
-		}
-	}
-}
-
-/** @}
- */
Index: uspace/lib/c/generic/io/asprintf.c
===================================================================
--- uspace/lib/c/generic/io/asprintf.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/io/asprintf.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -50,4 +50,25 @@
 }
 
+int vprintf_size(const char *fmt, va_list args)
+{
+	printf_spec_t ps = {
+		asprintf_str_write,
+		asprintf_wstr_write,
+		NULL
+	};
+	
+	return printf_core(fmt, &ps, args);
+}
+
+int printf_size(const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	int ret = vprintf_size(fmt, args);
+	va_end(args);
+	
+	return ret;
+}
+
 /** Allocate and print to string.
  *
@@ -61,14 +82,7 @@
 int asprintf(char **strp, const char *fmt, ...)
 {
-	printf_spec_t ps = {
-		asprintf_str_write,
-		asprintf_wstr_write,
-		NULL
-	};
-	
 	va_list args;
 	va_start(args, fmt);
-	
-	int ret = printf_core(fmt, &ps, args);
+	int ret = vprintf_size(fmt, args);
 	va_end(args);
 	
Index: uspace/lib/c/generic/iplink.c
===================================================================
--- uspace/lib/c/generic/iplink.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/iplink.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -218,5 +218,5 @@
 	iplink_recv_sdu_t sdu;
 	
-	uint16_t af = IPC_GET_ARG1(*icall);
+	ip_ver_t ver = IPC_GET_ARG1(*icall);
 	
 	int rc = async_data_write_accept(&sdu.data, false, 0, 0, 0,
@@ -227,5 +227,5 @@
 	}
 	
-	rc = iplink->ev_ops->recv(iplink, &sdu, af);
+	rc = iplink->ev_ops->recv(iplink, &sdu, ver);
 	free(sdu.data);
 	async_answer_0(iid, rc);
Index: uspace/lib/c/generic/iplink_srv.c
===================================================================
--- uspace/lib/c/generic/iplink_srv.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/iplink_srv.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -272,5 +272,6 @@
 }
 
-int iplink_ev_recv(iplink_srv_t *srv, iplink_recv_sdu_t *sdu, uint16_t af)
+/* XXX Version should be part of @a sdu */
+int iplink_ev_recv(iplink_srv_t *srv, iplink_recv_sdu_t *sdu, ip_ver_t ver)
 {
 	if (srv->client_sess == NULL)
@@ -280,5 +281,5 @@
 	
 	ipc_call_t answer;
-	aid_t req = async_send_1(exch, IPLINK_EV_RECV, (sysarg_t) af,
+	aid_t req = async_send_1(exch, IPLINK_EV_RECV, (sysarg_t)ver,
 	    &answer);
 	
Index: uspace/lib/c/generic/net/socket_client.c
===================================================================
--- uspace/lib/c/generic/net/socket_client.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/net/socket_client.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -923,5 +923,5 @@
  *			message.
  */
-int send(int socket_id, void *data, size_t datalength, int flags)
+int send(int socket_id, const void *data, size_t datalength, int flags)
 {
 	/* Without the address */
Index: uspace/lib/c/generic/pio_trace.c
===================================================================
--- uspace/lib/c/generic/pio_trace.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/pio_trace.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -95,8 +95,5 @@
 	pio_regions_t *regions = get_regions();
 	fibril_rwlock_read_lock(&regions->guard);
-	list_foreach(regions->list, it) {
-		assert(it);
-		region_t *reg = region_instance(it);
-		assert(reg);
+	list_foreach(regions->list, link, region_t, reg) {
 		if ((r >= reg->base) && (r < reg->base + reg->size)) {
 			if (reg->log)
@@ -131,10 +128,8 @@
 	fibril_rwlock_write_lock(&regions->guard);
 	list_foreach_safe(regions->list, it, next) {
-		assert(it);
 		region_t *reg = region_instance(it);
-		assert(reg);
 		if (r >= reg->base && (r < reg->base + reg->size)) {
-				list_remove(&reg->link);
-				region_destroy(reg);
+			list_remove(&reg->link);
+			region_destroy(reg);
 		}
 	}
Index: uspace/lib/c/generic/time.c
===================================================================
--- uspace/lib/c/generic/time.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/time.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -556,6 +556,6 @@
 		
 		void *addr;
-		rc = physmem_map((void *) faddr, 1,
-		    AS_AREA_READ | AS_AREA_CACHEABLE, &addr);
+		rc = physmem_map(faddr, 1, AS_AREA_READ | AS_AREA_CACHEABLE,
+		    &addr);
 		if (rc != EOK) {
 			as_area_destroy(addr);
Index: uspace/lib/c/generic/vfs/vfs.c
===================================================================
--- uspace/lib/c/generic/vfs/vfs.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/generic/vfs/vfs.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -43,4 +43,5 @@
 #include <stdio.h>
 #include <sys/stat.h>
+#include <sys/statfs.h>
 #include <sys/types.h>
 #include <ipc/services.h>
@@ -341,5 +342,5 @@
 }
 
-ssize_t read(int fildes, void *buf, size_t nbyte) 
+ssize_t read(int fildes, void *buf, size_t nbyte)
 {
 	sysarg_t rc;
@@ -347,8 +348,11 @@
 	aid_t req;
 	
+	if (nbyte > DATA_XFER_LIMIT)
+		nbyte = DATA_XFER_LIMIT;
+	
 	async_exch_t *exch = vfs_exchange_begin();
 	
 	req = async_send_1(exch, VFS_IN_READ, fildes, &answer);
-	rc = async_data_read_start(exch, (void *)buf, nbyte);
+	rc = async_data_read_start(exch, (void *) buf, nbyte);
 	if (rc != EOK) {
 		vfs_exchange_end(exch);
@@ -376,8 +380,11 @@
 	aid_t req;
 	
+	if (nbyte > DATA_XFER_LIMIT)
+		nbyte = DATA_XFER_LIMIT;
+	
 	async_exch_t *exch = vfs_exchange_begin();
 	
 	req = async_send_1(exch, VFS_IN_WRITE, fildes, &answer);
-	rc = async_data_write_start(exch, (void *)buf, nbyte);
+	rc = async_data_write_start(exch, (void *) buf, nbyte);
 	if (rc != EOK) {
 		vfs_exchange_end(exch);
@@ -892,4 +899,30 @@
 }
 
+int statfs(const char *path, struct statfs *st)
+{
+	sysarg_t rc, 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)
+		goto exit;
+
+	rc = async_data_read_start(exch, (void *) st, sizeof(*st));
+
+exit:
+	vfs_exchange_end(exch);
+	free(pa);
+	async_wait_for(req, &rc_orig);
+	return (int) (rc_orig != EOK ? rc_orig : rc);
+}
+
 /** @}
  */
Index: uspace/lib/c/include/adt/list.h
===================================================================
--- uspace/lib/c/include/adt/list.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/adt/list.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2001-2004 Jakub Jermar
- * Copyright (c) 2011 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
  * All rights reserved.
  *
@@ -65,12 +65,14 @@
 
 #define list_get_instance(link, type, member) \
-	((type *) (((void *)(link)) - ((void *) &(((type *) NULL)->member))))
-
-#define list_foreach(list, iterator) \
-	for (link_t *iterator = (list).head.next; \
-	    iterator != &(list).head; iterator = iterator->next)
+	((type *) (((void *)(link)) - list_link_to_void(&(((type *) NULL)->member))))
+
+#define list_foreach(list, member, itype, iterator) \
+	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) 1) \
+	    for (link_t *_link = (list).head.next; \
+	    iterator = list_get_instance(_link, itype, member), \
+	    _link != &(list).head; _link = _link->next)
 
 /** Unlike list_foreach(), allows removing items while traversing a list.
- * 
+ *
  * @code
  * list_t mylist;
@@ -238,5 +240,31 @@
 static inline link_t *list_last(list_t *list)
 {
-	return ((list->head.prev == &list->head) ? NULL : list->head.prev);
+	return (list->head.prev == &list->head) ? NULL : list->head.prev;
+}
+
+/** Get next item in list.
+ *
+ * @param link Current item link
+ * @param list List containing @a link
+ *
+ * @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)
+{
+	return (link->next == &list->head) ? NULL : link->next;
+}
+
+/** Get previous item in list.
+ *
+ * @param link Current item link
+ * @param list List containing @a link
+ *
+ * @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)
+{
+	return (link->prev == &list->head) ? NULL : link->prev;
 }
 
@@ -308,12 +336,23 @@
 	unsigned int cnt = 0;
 	
-	list_foreach(*list, link) {
+	link_t *link = list_first(list);
+	while (link != NULL) {
 		if (cnt == n)
 			return link;
 		
 		cnt++;
+		link = list_next(link, list);
 	}
 	
 	return NULL;
+}
+
+/** Verify that argument type is a pointer to link_t (at compile time).
+ *
+ * This can be used to check argument type in a macro.
+ */
+static inline const void *list_link_to_void(const link_t *link)
+{
+	return link;
 }
 
Index: uspace/lib/c/include/async.h
===================================================================
--- uspace/lib/c/include/async.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/async.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -399,4 +399,5 @@
 extern int async_data_read_start(async_exch_t *, void *, size_t);
 extern bool async_data_read_receive(ipc_callid_t *, size_t *);
+extern bool async_data_read_receive_call(ipc_callid_t *, ipc_call_t *, size_t *);
 extern int async_data_read_finalize(ipc_callid_t, const void *, size_t);
 
@@ -437,4 +438,5 @@
 extern int async_data_write_start(async_exch_t *, const void *, size_t);
 extern bool async_data_write_receive(ipc_callid_t *, size_t *);
+extern bool async_data_write_receive_call(ipc_callid_t *, ipc_call_t *, size_t *);
 extern int async_data_write_finalize(ipc_callid_t, void *, size_t);
 
Index: uspace/lib/c/include/cfg.h
===================================================================
--- uspace/lib/c/include/cfg.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/cfg.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -80,5 +80,5 @@
 
 #define cfg_file_foreach(file, cur) \
-	list_foreach((file)->sections, (cur))
+	list_foreach((file)->sections, link, const cfg_section_t, (cur))
 
 #define cfg_section_instance(cur) \
@@ -86,5 +86,5 @@
 
 #define cfg_section_foreach(section, cur) \
-	list_foreach((section)->entries, (cur))
+	list_foreach((section)->entries, link, const cfg_entry_t, (cur))
 
 #define cfg_entry_instance(cur) \
Index: uspace/lib/c/include/corecfg.h
===================================================================
--- uspace/lib/c/include/corecfg.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/c/include/corecfg.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,47 @@
+/*
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_INET_INETCFG_H_
+#define LIBC_INET_INETCFG_H_
+
+#include <stdbool.h>
+
+extern int corecfg_init(void);
+extern int corecfg_set_enable(bool);
+extern int corecfg_get_enable(bool *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/ddi.h
===================================================================
--- uspace/lib/c/include/ddi.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/ddi.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -40,16 +40,24 @@
 #include <sys/time.h>
 #include <abi/ddi/irq.h>
+#include <device/hw_res.h>
+#include <device/hw_res_parsed.h>
+#include <device/pio_window.h>
 #include <task.h>
+
+#define DMAMEM_16MiB  ((uintptr_t) UINT64_C(0xffffffffff000000))
+#define DMAMEM_4GiB   ((uintptr_t) UINT64_C(0xffffffff00000000))
 
 extern int device_assign_devno(void);
 
-extern int physmem_map(void *, size_t, unsigned int, void **);
+extern int physmem_map(uintptr_t, size_t, unsigned int, void **);
 
-extern int dmamem_map(void *, size_t, unsigned int, unsigned int, void **);
-extern int dmamem_map_anonymous(size_t, unsigned int, unsigned int, void **,
-    void **);
+extern int dmamem_map(void *, size_t, unsigned int, unsigned int, uintptr_t *);
+extern int dmamem_map_anonymous(size_t, uintptr_t, unsigned int, unsigned int,
+    uintptr_t *, void **);
 extern int dmamem_unmap(void *, size_t);
 extern int dmamem_unmap_anonymous(void *);
 
+extern int pio_enable_range(addr_range_t *, void **);
+extern int pio_enable_resource(pio_window_t *, hw_resource_t *, void **);
 extern int pio_enable(void *, size_t, void **);
 
@@ -69,6 +77,6 @@
 extern uint32_t pio_read_32(const ioport32_t *);
 
-static inline uint8_t pio_change_8(
-    ioport8_t *reg, uint8_t val, uint8_t mask, useconds_t delay)
+static inline uint8_t pio_change_8(ioport8_t *reg, uint8_t val, uint8_t mask,
+    useconds_t delay)
 {
 	uint8_t v = pio_read_8(reg);
@@ -78,6 +86,6 @@
 }
 
-static inline uint16_t pio_change_16(
-    ioport16_t *reg, uint16_t val, uint16_t mask, useconds_t delay)
+static inline uint16_t pio_change_16(ioport16_t *reg, uint16_t val,
+    uint16_t mask, useconds_t delay)
 {
 	uint16_t v = pio_read_16(reg);
@@ -87,6 +95,6 @@
 }
 
-static inline uint32_t pio_change_32(
-    ioport32_t *reg, uint32_t val, uint32_t mask, useconds_t delay)
+static inline uint32_t pio_change_32(ioport32_t *reg, uint32_t val,
+    uint32_t mask, useconds_t delay)
 {
 	uint32_t v = pio_read_32(reg);
Index: uspace/lib/c/include/device/hw_res.h
===================================================================
--- uspace/lib/c/include/device/hw_res.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/device/hw_res.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -53,4 +53,5 @@
 	HW_RES_ENABLE_INTERRUPT,
 	HW_RES_DMA_CHANNEL_SETUP,
+	HW_RES_DMA_CHANNEL_REMAIN,
 } hw_res_method_t;
 
@@ -75,12 +76,14 @@
 		struct {
 			uint64_t address;
+			size_t size;
+			bool relative;
 			endianness_t endianness;
-			size_t size;
 		} mem_range;
 		
 		struct {
 			uint64_t address;
+			size_t size;
+			bool relative;
 			endianness_t endianness;
-			size_t size;
 		} io_range;
 		
@@ -115,5 +118,6 @@
 
 extern int hw_res_dma_channel_setup(async_sess_t *, unsigned int, uint32_t,
-    uint16_t, uint8_t);
+    uint32_t, uint8_t);
+extern int hw_res_dma_channel_remain(async_sess_t *, unsigned);
 
 #endif
Index: uspace/lib/c/include/device/hw_res_parsed.h
===================================================================
--- uspace/lib/c/include/device/hw_res_parsed.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/device/hw_res_parsed.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,4 +37,5 @@
 
 #include <device/hw_res.h>
+#include <device/pio_window.h>
 #include <str.h>
 
@@ -45,14 +46,28 @@
 #define HW_RES_KEEP_DUPLICIT   0x2
 
+
+#define RNGABS(rng)	(rng).address.absolute
+#define RNGREL(rng)	(rng).address.relative
+#define RNGSZ(rng)	(rng).size
+
+#define RNGABSPTR(rng)	((void *) ((uintptr_t) RNGABS((rng))))
+
+typedef struct address64 {
+	/** Aboslute address. */ 
+	uint64_t absolute;
+	/** PIO window base relative address. */
+	uint64_t relative; 
+} address64_t;
+
 /** Address range structure */
 typedef struct addr_range {
 	/** Start address */
-	uint64_t address;
-	
-	/** Endianness */
-	endianness_t endianness;
+	address64_t address;
 	
 	/** Area size */
 	size_t size;
+
+	/** Endianness */
+	endianness_t endianness;
 } addr_range_t;
 
@@ -139,5 +154,6 @@
 }
 
-extern int hw_res_list_parse(hw_resource_list_t *, hw_res_list_parsed_t *, int);
+extern int hw_res_list_parse(const pio_window_t *, const hw_resource_list_t *,
+    hw_res_list_parsed_t *, int);
 extern int hw_res_get_list_parsed(async_sess_t *, hw_res_list_parsed_t *, int);
 
Index: uspace/lib/c/include/device/pio_window.h
===================================================================
--- uspace/lib/c/include/device/pio_window.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/c/include/device/pio_window.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/devman.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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/inet/addr.h
===================================================================
--- uspace/lib/c/include/inet/addr.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/inet/addr.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,4 +39,5 @@
 #include <net/in.h>
 #include <net/in6.h>
+#include <net/socket.h>
 
 typedef uint32_t addr32_t;
@@ -44,7 +45,17 @@
 typedef uint8_t addr128_t[16];
 
+typedef enum {
+	/** Any IP protocol version */
+	ip_any,
+	/** IPv4 */
+	ip_v4,
+	/** IPv6 */
+	ip_v6
+} ip_ver_t;
+
 /** Node address */
 typedef struct {
-	uint16_t family;
+	/** IP version */
+	ip_ver_t version;
 	union {
 		addr32_t addr;
@@ -55,6 +66,6 @@
 /** Network address */
 typedef struct {
-	/** Address family */
-	uint16_t family;
+	/** IP version */
+	ip_ver_t version;
 	
 	/** Address */
@@ -68,4 +79,5 @@
 } inet_naddr_t;
 
+extern const addr32_t addr32_broadcast_all_hosts;
 extern const addr48_t addr48_broadcast;
 
@@ -90,5 +102,4 @@
     uint16_t, uint16_t, uint16_t, uint16_t, uint8_t);
 
-extern int inet_addr_family(const char *, uint16_t *);
 extern void inet_naddr_addr(const inet_naddr_t *, inet_addr_t *);
 extern void inet_addr_naddr(const inet_addr_t *, uint8_t, inet_naddr_t *);
@@ -109,6 +120,6 @@
 extern int inet_naddr_format(const inet_naddr_t *, char **);
 
-extern uint16_t inet_addr_get(const inet_addr_t *, addr32_t *, addr128_t *);
-extern uint16_t inet_naddr_get(const inet_naddr_t *, addr32_t *, addr128_t *,
+extern ip_ver_t inet_addr_get(const inet_addr_t *, addr32_t *, addr128_t *);
+extern ip_ver_t inet_naddr_get(const inet_naddr_t *, addr32_t *, addr128_t *,
     uint8_t *);
 
@@ -124,4 +135,8 @@
     sockaddr_in6_t *);
 
+extern ip_ver_t ipver_from_af(int af);
+extern int inet_addr_sockaddr(const inet_addr_t *, uint16_t, sockaddr_t **,
+    socklen_t *);
+
 #endif
 
Index: uspace/lib/c/include/inet/dhcp.h
===================================================================
--- uspace/lib/c/include/inet/dhcp.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/c/include/inet/dhcp.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,47 @@
+/*
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_INET_DHCP_H_
+#define LIBC_INET_DHCP_H_
+
+#include <sys/types.h>
+
+extern int dhcp_init(void);
+extern int dhcp_link_add(sysarg_t);
+extern int dhcp_link_remove(sysarg_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/inet/dnsr.h
===================================================================
--- uspace/lib/c/include/inet/dnsr.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/inet/dnsr.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -51,5 +51,5 @@
 
 extern int dnsr_init(void);
-extern int dnsr_name2host(const char *, dnsr_hostinfo_t **, uint16_t);
+extern int dnsr_name2host(const char *, dnsr_hostinfo_t **, ip_ver_t);
 extern void dnsr_hostinfo_destroy(dnsr_hostinfo_t *);
 extern int dnsr_get_srvaddr(inet_addr_t *);
Index: uspace/lib/c/include/inet/inet.h
===================================================================
--- uspace/lib/c/include/inet/inet.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/inet/inet.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,23 +37,7 @@
 
 #include <inet/addr.h>
+#include <ipc/loc.h>
 #include <sys/types.h>
-
-#define INET_TTL_MAX 255
-
-typedef struct {
-	inet_addr_t src;
-	inet_addr_t dest;
-	uint8_t tos;
-	void *data;
-	size_t size;
-} inet_dgram_t;
-
-typedef struct {
-	int (*recv)(inet_dgram_t *);
-} inet_ev_ops_t;
-
-typedef enum {
-	INET_DF = 1
-} inet_df_t;
+#include <types/inet.h>
 
 extern int inet_init(uint8_t, inet_ev_ops_t *);
Index: uspace/lib/c/include/inet/inetcfg.h
===================================================================
--- uspace/lib/c/include/inet/inetcfg.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/inet/inetcfg.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,32 +38,5 @@
 #include <inet/inet.h>
 #include <sys/types.h>
-
-/** Address object info */
-typedef struct {
-	/** Network address */
-	inet_naddr_t naddr;
-	/** Link service ID */
-	sysarg_t ilink;
-	/** Address object name */
-	char *name;
-} inet_addr_info_t;
-
-/** IP link info */
-typedef struct {
-	/** Link service name */
-	char *name;
-	/** Default MTU */
-	size_t def_mtu;
-} inet_link_info_t;
-
-/** Static route info */
-typedef struct {
-	/** Destination network address */
-	inet_naddr_t dest;
-	/** Router address */
-	inet_addr_t router;
-	/** Static route name */
-	char *name;
-} inet_sroute_info_t;
+#include <types/inetcfg.h>
 
 extern int inetcfg_init(void);
@@ -75,5 +48,7 @@
 extern int inetcfg_get_link_list(sysarg_t **, size_t *);
 extern int inetcfg_get_sroute_list(sysarg_t **, size_t *);
+extern int inetcfg_link_add(sysarg_t);
 extern int inetcfg_link_get(sysarg_t, inet_link_info_t *);
+extern int inetcfg_link_remove(sysarg_t);
 extern int inetcfg_sroute_get(sysarg_t, inet_sroute_info_t *);
 extern int inetcfg_sroute_get_id(const char *, sysarg_t *);
Index: uspace/lib/c/include/inet/inetping.h
===================================================================
--- uspace/lib/c/include/inet/inetping.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/inet/inetping.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,12 +38,5 @@
 #include <inet/inet.h>
 #include <sys/types.h>
-
-typedef struct {
-	uint32_t src;
-	uint32_t dest;
-	uint16_t seq_no;
-	void *data;
-	size_t size;
-} inetping_sdu_t;
+#include <types/inetping.h>
 
 typedef struct inetping_ev_ops {
@@ -53,5 +46,5 @@
 extern int inetping_init(inetping_ev_ops_t *);
 extern int inetping_send(inetping_sdu_t *);
-extern int inetping_get_srcaddr(uint32_t, uint32_t *);
+extern int inetping_get_srcaddr(const inet_addr_t *, inet_addr_t *);
 
 #endif
Index: pace/lib/c/include/inet/inetping6.h
===================================================================
--- uspace/lib/c/include/inet/inetping6.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ 	(revision )
@@ -1,60 +1,0 @@
-/*
- * Copyright (c) 2013 Martin Decky
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup libc
- * @{
- */
-/** @file
- */
-
-#ifndef LIBC_INET_INETPING6_H_
-#define LIBC_INET_INETPING6_H_
-
-#include <inet/inet.h>
-#include <sys/types.h>
-
-typedef struct {
-	addr128_t src;
-	addr128_t dest;
-	uint16_t seq_no;
-	void *data;
-	size_t size;
-} inetping6_sdu_t;
-
-typedef struct inetping6_ev_ops {
-	int (*recv)(inetping6_sdu_t *);
-} inetping6_ev_ops_t;
-
-extern int inetping6_init(inetping6_ev_ops_t *);
-extern int inetping6_send(inetping6_sdu_t *);
-extern int inetping6_get_srcaddr(addr128_t, addr128_t);
-
-#endif
-
-/** @}
- */
Index: uspace/lib/c/include/inet/iplink.h
===================================================================
--- uspace/lib/c/include/inet/iplink.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/inet/iplink.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,5 +37,4 @@
 
 #include <async.h>
-#include <sys/types.h>
 #include <inet/addr.h>
 
@@ -78,5 +77,5 @@
 
 typedef struct iplink_ev_ops {
-	int (*recv)(iplink_t *, iplink_recv_sdu_t *, uint16_t);
+	int (*recv)(iplink_t *, iplink_recv_sdu_t *, ip_ver_t);
 } iplink_ev_ops_t;
 
Index: uspace/lib/c/include/inet/iplink_srv.h
===================================================================
--- uspace/lib/c/include/inet/iplink_srv.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/inet/iplink_srv.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,5 +39,4 @@
 #include <fibril_synch.h>
 #include <stdbool.h>
-#include <sys/types.h>
 #include <inet/addr.h>
 #include <inet/iplink.h>
@@ -67,5 +66,5 @@
 
 extern int iplink_conn(ipc_callid_t, ipc_call_t *, void *);
-extern int iplink_ev_recv(iplink_srv_t *, iplink_recv_sdu_t *, uint16_t);
+extern int iplink_ev_recv(iplink_srv_t *, iplink_recv_sdu_t *, ip_ver_t);
 
 #endif
Index: uspace/lib/c/include/ipc/corecfg.h
===================================================================
--- uspace/lib/c/include/ipc/corecfg.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/c/include/ipc/corecfg.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,46 @@
+/*
+ * 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 libcipc
+ * @{
+ */
+
+#ifndef LIBC_IPC_CORECFG_H_
+#define LIBC_IPC_CORECFG_H_
+
+#include <ipc/common.h>
+
+typedef enum {
+	CORECFG_GET_ENABLE = IPC_FIRST_USER_METHOD,
+	CORECFG_SET_ENABLE
+} corecfg_request_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/ipc/dev_iface.h
===================================================================
--- uspace/lib/c/include/ipc/dev_iface.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/ipc/dev_iface.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -36,4 +36,6 @@
 typedef enum {
 	HW_RES_DEV_IFACE = 0,
+	PIO_WINDOW_DEV_IFACE,
+
 	/** Character device interface */
 	CHAR_DEV_IFACE,
@@ -41,4 +43,9 @@
 	/** Graphic device interface */
 	GRAPH_DEV_IFACE,
+
+	/** Audio device mixer interface */
+	AUDIO_MIXER_IFACE,
+	/** Audio device pcm buffer interface */
+	AUDIO_PCM_BUFFER_IFACE,
 	
 	/** Network interface controller interface */
@@ -54,8 +61,11 @@
 	/** Interface provided by USB HID devices. */
 	USBHID_DEV_IFACE,
+
 	/** Interface provided by Real Time Clock devices */
 	CLOCK_DEV_IFACE,
+
 	/** Interface provided by battery powered devices */
 	BATTERY_DEV_IFACE,
+
 	/** Interface provided by AHCI devices. */
 	AHCI_DEV_IFACE,
Index: uspace/lib/c/include/ipc/devman.h
===================================================================
--- uspace/lib/c/include/ipc/devman.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/ipc/devman.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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/dhcp.h
===================================================================
--- uspace/lib/c/include/ipc/dhcp.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/c/include/ipc/dhcp.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,50 @@
+/*
+ * 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 libcipc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_IPC_DHCP_H_
+#define LIBC_IPC_DHCP_H_
+
+#include <ipc/common.h>
+
+/** DHCP service requests */
+typedef enum {
+	DHCP_LINK_ADD = IPC_FIRST_USER_METHOD,
+	DHCP_LINK_REMOVE
+} dhcp_request_t;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/c/include/ipc/inet.h
===================================================================
--- uspace/lib/c/include/ipc/inet.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/ipc/inet.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -72,5 +72,7 @@
 	INETCFG_GET_LINK_LIST,
 	INETCFG_GET_SROUTE_LIST,
+	INETCFG_LINK_ADD,
 	INETCFG_LINK_GET,
+	INETCFG_LINK_REMOVE,
 	INETCFG_SROUTE_CREATE,
 	INETCFG_SROUTE_DELETE,
@@ -90,15 +92,4 @@
 } inetping_request_t;
 
-/** Events on Inet ping6 port */
-typedef enum {
-	INETPING6_EV_RECV = IPC_FIRST_USER_METHOD
-} inetping6_event_t;
-
-/** Requests on Inet ping6 port */
-typedef enum {
-	INETPING6_SEND = IPC_FIRST_USER_METHOD,
-	INETPING6_GET_SRCADDR
-} inetping6_request_t;
-
 #endif
 
Index: uspace/lib/c/include/ipc/services.h
===================================================================
--- uspace/lib/c/include/ipc/services.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/ipc/services.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -53,4 +53,6 @@
 } services_t;
 
+#define SERVICE_NAME_CORECFG	"corecfg"
+#define SERVICE_NAME_DHCP       "net/dhcp"
 #define SERVICE_NAME_DNSR       "net/dnsr"
 #define SERVICE_NAME_INET       "net/inet"
@@ -58,4 +60,5 @@
 #define SERVICE_NAME_INETPING   "net/inetping"
 #define SERVICE_NAME_INETPING6  "net/inetping6"
+#define SERVICE_NAME_NETCONF    "net/netconf"
 
 #endif
Index: uspace/lib/c/include/ipc/socket.h
===================================================================
--- uspace/lib/c/include/ipc/socket.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/ipc/socket.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -198,5 +198,5 @@
 #define SOCKET_GET_OPT_NAME(call) \
 	({ \
-		int opt_name = (int) IPC_GET_ARG4(call); \
+		int opt_name = (int) IPC_GET_ARG2(call); \
 		opt_name; \
 	})
Index: uspace/lib/c/include/ipc/vfs.h
===================================================================
--- uspace/lib/c/include/ipc/vfs.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/ipc/vfs.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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/macros.h
===================================================================
--- uspace/lib/c/include/macros.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/macros.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -40,4 +40,5 @@
 #define abs(a)     ((a) >= 0 ? (a) : -(a))
 
+#define ARRAY_SIZE(array)   (sizeof(array) / sizeof(array[0]))
 
 #define KiB2SIZE(kb)  ((kb) << 10)
Index: pace/lib/c/include/net/device.h
===================================================================
--- uspace/lib/c/include/net/device.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ 	(revision )
@@ -1,58 +1,0 @@
-/*
- * Copyright (c) 2009 Lukas Mejdrech
- * 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
- * Network device.
- */
-
-#ifndef LIBC_NET_DEVICE_H_
-#define LIBC_NET_DEVICE_H_
-
-#include <adt/int_map.h>
-#include <nic/nic.h>
-
-/** Device identifier to generic type map declaration. */
-#define DEVICE_MAP_DECLARE  INT_MAP_DECLARE
-
-/** Device identifier to generic type map implementation. */
-#define DEVICE_MAP_IMPLEMENT  INT_MAP_IMPLEMENT
-
-/** Device identifier type. */
-typedef int nic_device_id_t;
-
-/** Invalid device identifier. */
-#define NIC_DEVICE_INVALID_ID  (-1)
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/net/ip_codes.h
===================================================================
--- uspace/lib/c/include/net/ip_codes.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ 	(revision )
@@ -1,118 +1,0 @@
-/*
- * Copyright (c) 2009 Lukas Mejdrech
- * 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
- * IP codes and definitions.
- */
-
-#ifndef LIBC_IP_CODES_H_
-#define LIBC_IP_CODES_H_
-
-#include <sys/types.h>
-
-/** IP time to live counter type definition. */
-typedef uint8_t ip_ttl_t;
-
-/** IP type of service type definition. */
-typedef uint8_t ip_tos_t;
-
-/** IP transport protocol type definition. */
-typedef uint8_t ip_protocol_t;
-
-/** Default IPVERSION. */
-#define IPVERSION	4
-
-/** Default time to live counter. */
-#define IPDEFTTL	64
-
-/** @name IP options definitions */
-/*@{*/
-
-/** Copy shift. */
-#define IPOPT_COPY_SHIFT	7
-
-/** Class shift. */
-#define IPOPT_CLASS_SHIFT	5
-
-/** Number shift. */
-#define IPOPT_NUMBER_SHIFT	0
-
-/** Class mask. */
-#define IPOPT_CLASS_MASK	0x60
-
-/** Number mask. */
-#define IPOPT_NUMBER_MASK	0x1f
-
-/** Copy flag. */
-#define IPOPT_COPY		(1 << IPOPT_COPY_SHIFT)
-
-/** Returns IP option type.
- *
- * @param[in] copy	The value indication whether the IP option should be
- *			copied.
- * @param[in] class	The IP option class.
- * @param[in] number	The IP option number.
- */
-#define IPOPT_TYPE(copy, class, number) \
-	(((copy) & IPOPT_COPY) | ((class) & IPOPT_CLASS_MASK) | \
-	(((number) << IPOPT_NUMBER_SHIFT) & IPOPT_NUMBER_MASK))
-
-/** Returns a value indicating whether the IP option should be copied.
- * @param[in] o The IP option.
- */
-#define IPOPT_COPIED(o)		((o) & IPOPT_COPY)
-
-/*@}*/
-
-/** @name IP option class definitions */
-/*@{*/
-
-/** Control class. */
-#define IPOPT_CONTROL		(0 << IPOPT_CLASS_SHIFT)
-
-/*@}*/
-
-/** @name IP option type definitions */
-/*@{*/
-
-/** End of list. */
-#define IPOPT_END		IPOPT_TYPE(0, IPOPT_CONTROL, 0)
-
-/** No operation. */
-#define IPOPT_NOOP		IPOPT_TYPE(0, IPOPT_CONTROL, 1)
-
-/*@}*/
-
-#endif
-
-/** @}
- */
Index: uspace/lib/c/include/net/socket.h
===================================================================
--- uspace/lib/c/include/net/socket.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/net/socket.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -57,5 +57,5 @@
 extern int connect(int, const struct sockaddr *, socklen_t);
 extern int closesocket(int);
-extern int send(int, void *, size_t, int);
+extern int send(int, const void *, size_t, int);
 extern int sendto(int, const void *, size_t, int, const struct sockaddr *,
     socklen_t);
Index: uspace/lib/c/include/net/socket_codes.h
===================================================================
--- uspace/lib/c/include/net/socket_codes.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/net/socket_codes.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -75,4 +75,13 @@
 typedef int32_t socklen_t;
 
+/* Socket options */
+
+enum {
+	SOL_SOCKET = 1,
+
+	/* IP link to transmit on */
+	SO_IPLINK
+};
+
 #endif
 
Index: uspace/lib/c/include/stdio.h
===================================================================
--- uspace/lib/c/include/stdio.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/stdio.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -124,4 +124,8 @@
 extern int vsnprintf(char *, size_t, const char *, va_list);
 
+extern int printf_size(const char *, ...)
+    PRINTF_ATTRIBUTE(1, 2);
+extern int vprintf_size(const char *, va_list);
+
 /* File stream functions */
 extern FILE *fopen(const char *, const char *);
Index: uspace/lib/c/include/sys/statfs.h
===================================================================
--- uspace/lib/c/include/sys/statfs.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/c/include/sys/statfs.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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/types/inet.h
===================================================================
--- uspace/lib/c/include/types/inet.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/c/include/types/inet.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2012 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_TYPES_INET_H_
+#define LIBC_TYPES_INET_H_
+
+#include <inet/addr.h>
+#include <ipc/loc.h>
+#include <sys/types.h>
+
+#define INET_TTL_MAX 255
+
+typedef struct {
+	/** Local IP link service ID (optional) */
+	service_id_t iplink;
+	inet_addr_t src;
+	inet_addr_t dest;
+	uint8_t tos;
+	void *data;
+	size_t size;
+} inet_dgram_t;
+
+typedef struct {
+	int (*recv)(inet_dgram_t *);
+} inet_ev_ops_t;
+
+typedef enum {
+	INET_DF = 1
+} inet_df_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/types/inetcfg.h
===================================================================
--- uspace/lib/c/include/types/inetcfg.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/c/include/types/inetcfg.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,74 @@
+/*
+ * 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_TYPES_INETCFG_H_
+#define LIBC_TYPES_INETCFG_H_
+
+#include <inet/inet.h>
+#include <sys/types.h>
+
+/** Address object info */
+typedef struct {
+	/** Network address */
+	inet_naddr_t naddr;
+	/** Link service ID */
+	sysarg_t ilink;
+	/** Address object name */
+	char *name;
+} inet_addr_info_t;
+
+/** IP link info */
+typedef struct {
+	/** Link service name */
+	char *name;
+	/** Default MTU */
+	size_t def_mtu;
+	/** Link layer address */
+	addr48_t mac_addr;
+} inet_link_info_t;
+
+/** Static route info */
+typedef struct {
+	/** Destination network address */
+	inet_naddr_t dest;
+	/** Router address */
+	inet_addr_t router;
+	/** Static route name */
+	char *name;
+} inet_sroute_info_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/types/inetping.h
===================================================================
--- uspace/lib/c/include/types/inetping.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/c/include/types/inetping.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,54 @@
+/*
+ * 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 libc
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef LIBC_TYPES_INETPING_H_
+#define LIBC_TYPES_INETPING_H_
+
+#include <inet/addr.h>
+#include <sys/types.h>
+
+typedef struct {
+	inet_addr_t src;
+	inet_addr_t dest;
+	uint16_t seq_no;
+	void *data;
+	size_t size;
+} inetping_sdu_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/vfs/vfs.h
===================================================================
--- uspace/lib/c/include/vfs/vfs.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/c/include/vfs/vfs.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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/draw/drawctx.c
===================================================================
--- uspace/lib/draw/drawctx.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/draw/drawctx.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -188,6 +188,5 @@
 	 * transferred to the original surface. */
 
-	list_foreach(*((list_t *) path), link) {
-		path_step_t *step = (path_step_t *) link;
+	list_foreach(*((list_t *) path), link, path_step_t, step) {
 		switch (step->type) {
 		case PATH_STEP_MOVETO:
Index: uspace/lib/drv/Makefile
===================================================================
--- uspace/lib/drv/Makefile	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/drv/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -29,5 +29,5 @@
 
 USPACE_PREFIX = ../..
-EXTRA_CFLAGS = -Iinclude -I$(LIBUSB_PREFIX)/include
+EXTRA_CFLAGS = -Iinclude -I$(LIBUSB_PREFIX)/include -I$(LIBPCM_PREFIX)/include
 LIBRARY = libdrv
 
@@ -38,5 +38,8 @@
 	generic/log.c \
 	generic/logbuf.c \
+	generic/remote_audio_mixer.c \
+	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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/drv/generic/dev_iface.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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"
@@ -49,19 +50,24 @@
 #include "remote_usbhid.h"
 #include "remote_pci.h"
+#include "remote_audio_mixer.h"
+#include "remote_audio_pcm.h"
 #include "remote_ahci.h"
 
-static iface_dipatch_table_t remote_ifaces = {
+static const iface_dipatch_table_t remote_ifaces = {
 	.ifaces = {
-		&remote_hw_res_iface,
-		&remote_char_dev_iface,
-		&remote_graph_dev_iface,
-		&remote_nic_iface,
-		&remote_pci_iface,
-		&remote_usb_iface,
-		&remote_usbhc_iface,
-		&remote_usbhid_iface,
-		&remote_clock_dev_iface,
-		&remote_battery_dev_iface,
-		&remote_ahci_iface
+		[AUDIO_MIXER_IFACE] = &remote_audio_mixer_iface,
+		[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,
+		[NIC_DEV_IFACE] = &remote_nic_iface,
+		[PCI_DEV_IFACE] = &remote_pci_iface,
+		[USB_DEV_IFACE] = &remote_usb_iface,
+		[USBHC_DEV_IFACE] = &remote_usbhc_iface,
+		[USBHID_DEV_IFACE] = &remote_usbhid_iface,
+		[CLOCK_DEV_IFACE] = &remote_clock_dev_iface,
+		[BATTERY_DEV_IFACE] = &remote_battery_dev_iface,
+		[AHCI_DEV_IFACE] = &remote_ahci_iface
 	}
 };
Index: uspace/lib/drv/generic/driver.c
===================================================================
--- uspace/lib/drv/generic/driver.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/drv/generic/driver.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -98,10 +98,7 @@
 static ddf_dev_t *driver_get_device(devman_handle_t handle)
 {
-	ddf_dev_t *dev = NULL;
-	
 	assert(fibril_mutex_is_locked(&devices_mutex));
 	
-	list_foreach(devices, link) {
-		dev = list_get_instance(link, ddf_dev_t, link);
+	list_foreach(devices, link, ddf_dev_t, dev) {
 		if (dev->handle == handle)
 			return dev;
@@ -113,10 +110,7 @@
 static ddf_fun_t *driver_get_function(devman_handle_t handle)
 {
-	ddf_fun_t *fun = NULL;
-	
 	assert(fibril_mutex_is_locked(&functions_mutex));
 	
-	list_foreach(functions, link) {
-		fun = list_get_instance(link, ddf_fun_t, link);
+	list_foreach(functions, link, ddf_fun_t, fun) {
 		if (fun->handle == handle)
 			return fun;
Index: uspace/lib/drv/generic/interrupt.c
===================================================================
--- uspace/lib/drv/generic/interrupt.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/drv/generic/interrupt.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -138,10 +138,7 @@
     interrupt_context_list_t *list, int id)
 {
-	interrupt_context_t *ctx;
-	
-	fibril_mutex_lock(&list->mutex);
-	
-	list_foreach(list->contexts, link) {
-		ctx = list_get_instance(link, interrupt_context_t, link);
+	fibril_mutex_lock(&list->mutex);
+	
+	list_foreach(list->contexts, link, interrupt_context_t, ctx) {
 		if (ctx->id == id) {
 			fibril_mutex_unlock(&list->mutex);
@@ -157,10 +154,7 @@
     interrupt_context_list_t *list, ddf_dev_t *dev, int irq)
 {
-	interrupt_context_t *ctx;
-	
-	fibril_mutex_lock(&list->mutex);
-	
-	list_foreach(list->contexts, link) {
-		ctx = list_get_instance(link, interrupt_context_t, link);
+	fibril_mutex_lock(&list->mutex);
+	
+	list_foreach(list->contexts, link, interrupt_context_t, ctx) {
 		if (ctx->irq == irq && ctx->dev == dev) {
 			fibril_mutex_unlock(&list->mutex);
Index: uspace/lib/drv/generic/remote_audio_mixer.c
===================================================================
--- uspace/lib/drv/generic/remote_audio_mixer.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/drv/generic/remote_audio_mixer.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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 <assert.h>
+#include <str.h>
+
+#include "audio_mixer_iface.h"
+#include "ddf/driver.h"
+
+typedef enum {
+	/** Asks for basic mixer info: Mixer name and number of controllable
+	 * items.
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - EOK - call successful, info is valid
+	 * Answer arguments:
+	 * - Mixer name
+	 * - Number of controllable items
+	 */
+	IPC_M_AUDIO_MIXER_GET_INFO,
+
+	/** Asks for item mixer info: Item name and number of controllable
+	 * channels.
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - ENOENT - no such item
+	 * - EOK - call successful, info is valid
+	 * Answer arguments:
+	 * - Item name
+	 * - Number of controllable channels
+	 */
+	IPC_M_AUDIO_MIXER_GET_ITEM_INFO,
+
+	/** Set new control item setting
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - ENOENT - no such control item
+	 * - EOK - call successful, info is valid
+	 */
+	IPC_M_AUDIO_MIXER_SET_ITEM_LEVEL,
+
+	/** Get control item setting
+	 * Answer:
+	 * - ENOTSUP - call not supported
+	 * - ENOENT - no such control item
+	 * - EOK - call successful, info is valid
+	 */
+	IPC_M_AUDIO_MIXER_GET_ITEM_LEVEL,
+} audio_mixer_iface_funcs_t;
+
+/*
+ * CLIENT SIDE
+ */
+
+/**
+ * Query audio mixer for basic info (name and items count).
+ * @param[in] exch IPC exchange connected to the device
+ * @param[out] name Audio mixer string identifier
+ * @param[out] items Number of items controlled by the mixer.
+ * @return Error code.
+ */
+int audio_mixer_get_info(async_exch_t *exch, const char **name, unsigned *items)
+{
+	if (!exch)
+		return EINVAL;
+	sysarg_t name_size, itemc;
+	const int ret = async_req_1_2(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_GET_INFO, &name_size, &itemc);
+	if (ret == EOK && name) {
+		char *name_place = calloc(1, name_size);
+		if (!name_place) {
+			/* Make the other side fail
+			 * as it waits for read request */
+			async_data_read_start(exch, (void*)-1, 0);
+			return ENOMEM;
+		}
+		const int ret =
+		    async_data_read_start(exch, name_place, name_size);
+		if (ret != EOK) {
+			free(name_place);
+			return ret;
+		}
+		*name = name_place;
+	}
+	if (ret == EOK && items)
+		*items = itemc;
+	return ret;
+}
+
+/**
+ * Query audio mixer for item specific info (name and channels count).
+ * @param[in] exch IPC exchange connected to the device
+ * @param[in] item The control item
+ * @param[out] name Control item string identifier.
+ * @param[out] channles Number of channels associated with this control item.
+ * @return Error code.
+ */
+int audio_mixer_get_item_info(async_exch_t *exch, unsigned item,
+    const char **name, unsigned *levels)
+{
+	if (!exch)
+		return EINVAL;
+	sysarg_t name_size, lvls;
+	const int ret = async_req_2_2(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_GET_ITEM_INFO, item, &name_size, &lvls);
+	if (ret == EOK && name) {
+		char *name_place = calloc(1, name_size);
+		if (!name_place) {
+			/* Make the other side fail
+			 * as it waits for read request */
+			async_data_read_start(exch, (void*)-1, 0);
+			return ENOMEM;
+		}
+		const int ret =
+		    async_data_read_start(exch, name_place, name_size);
+		if (ret != EOK) {
+			free(name_place);
+			return ret;
+		}
+		*name = name_place;
+	}
+	if (ret == EOK && levels)
+		*levels = lvls;
+	return ret;
+}
+
+/**
+ * Set control item to a new level.
+ * @param[in] exch IPC exchange connected to the device.
+ * @param[in] item The control item controlling the channel.
+ * @param[in] level The new value.
+ * @return Error code.
+ */
+int audio_mixer_set_item_level(async_exch_t *exch, unsigned item,
+    unsigned level)
+{
+	if (!exch)
+		return EINVAL;
+	return async_req_3_0(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_SET_ITEM_LEVEL, item, level);
+}
+
+/**
+ * Get current level of a control item.
+ * @param[in] exch IPC exchange connected to the device.
+ * @param[in] item The control item controlling the channel.
+ * @param[in] channel The channel index.
+ * @param[out] level Currently set value.
+ * @return Error code.
+ */
+int audio_mixer_get_item_level(async_exch_t *exch, unsigned item,
+    unsigned *level)
+{
+	if (!exch)
+		return EINVAL;
+	sysarg_t current;
+	const int ret = async_req_2_1(exch, DEV_IFACE_ID(AUDIO_MIXER_IFACE),
+	    IPC_M_AUDIO_MIXER_GET_ITEM_LEVEL, item, &current);
+	if (ret == EOK && level)
+		*level = current;
+	return ret;
+}
+
+/*
+ * SERVER SIDE
+ */
+static void remote_audio_mixer_get_info(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_mixer_get_item_info(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_mixer_get_item_level(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_mixer_set_item_level(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+
+/** Remote audio mixer interface operations. */
+static remote_iface_func_ptr_t remote_audio_mixer_iface_ops[] = {
+	[IPC_M_AUDIO_MIXER_GET_INFO] = remote_audio_mixer_get_info,
+	[IPC_M_AUDIO_MIXER_GET_ITEM_INFO] = remote_audio_mixer_get_item_info,
+	[IPC_M_AUDIO_MIXER_GET_ITEM_LEVEL] = remote_audio_mixer_get_item_level,
+	[IPC_M_AUDIO_MIXER_SET_ITEM_LEVEL] = remote_audio_mixer_set_item_level,
+};
+
+/** Remote audio mixer interface structure. */
+remote_iface_t remote_audio_mixer_iface = {
+	.method_count = sizeof(remote_audio_mixer_iface_ops) /
+	    sizeof(remote_audio_mixer_iface_ops[0]),
+	.methods = remote_audio_mixer_iface_ops
+};
+
+void remote_audio_mixer_get_info(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->get_info) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const char *name = NULL;
+	unsigned items = 0;
+	const int ret = mixer_iface->get_info(fun, &name, &items);
+	const size_t name_size = name ? str_size(name) + 1 : 0;
+	async_answer_2(callid, ret, name_size, items);
+	/* Send the name. */
+	if (ret == EOK && name_size > 0) {
+		size_t size;
+		ipc_callid_t name_id;
+		if (!async_data_read_receive(&name_id, &size)) {
+			async_answer_0(name_id, EPARTY);
+			return;
+		}
+		if (size != name_size) {
+			async_answer_0(name_id, ELIMIT);
+			return;
+		}
+		async_data_read_finalize(name_id, name, name_size);
+	}
+}
+
+void remote_audio_mixer_get_item_info(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->get_item_info) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const char *name = NULL;
+	unsigned values = 0;
+	const int ret = mixer_iface->get_item_info(fun, item, &name, &values);
+	const size_t name_size = name ? str_size(name) + 1 : 0;
+	async_answer_2(callid, ret, name_size, values);
+	/* Send the name. */
+	if (ret == EOK && name_size > 0) {
+		size_t size;
+		ipc_callid_t name_id;
+		if (!async_data_read_receive(&name_id, &size)) {
+			async_answer_0(name_id, EPARTY);
+			return;
+		}
+		if (size != name_size) {
+			async_answer_0(name_id, ELIMIT);
+			return;
+		}
+		async_data_read_finalize(name_id, name, name_size);
+	}
+}
+
+void remote_audio_mixer_set_item_level(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->set_item_level) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	const unsigned value = DEV_IPC_GET_ARG2(*call);
+	const int ret = mixer_iface->set_item_level(fun, item, value);
+	async_answer_0(callid, ret);
+}
+
+void remote_audio_mixer_get_item_level(
+    ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	audio_mixer_iface_t *mixer_iface = iface;
+
+	if (!mixer_iface->get_item_level) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned item = DEV_IPC_GET_ARG1(*call);
+	unsigned current = 0;
+	const int ret =
+	    mixer_iface->get_item_level(fun, item, &current);
+	async_answer_1(callid, ret, current);
+}
+
+/**
+ * @}
+ */
Index: uspace/lib/drv/generic/remote_audio_pcm.c
===================================================================
--- uspace/lib/drv/generic/remote_audio_pcm.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/drv/generic/remote_audio_pcm.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,858 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 <devman.h>
+#include <ddf/log.h>
+#include <errno.h>
+#include <macros.h>
+#include <str.h>
+#include <as.h>
+#include <sys/mman.h>
+
+#include "audio_pcm_iface.h"
+#include "ddf/driver.h"
+
+typedef enum {
+	IPC_M_AUDIO_PCM_GET_INFO_STR,
+	IPC_M_AUDIO_PCM_QUERY_CAPS,
+	IPC_M_AUDIO_PCM_REGISTER_EVENTS,
+	IPC_M_AUDIO_PCM_UNREGISTER_EVENTS,
+	IPC_M_AUDIO_PCM_TEST_FORMAT,
+	IPC_M_AUDIO_PCM_GET_BUFFER,
+	IPC_M_AUDIO_PCM_RELEASE_BUFFER,
+	IPC_M_AUDIO_PCM_GET_BUFFER_POS,
+	IPC_M_AUDIO_PCM_START_PLAYBACK,
+	IPC_M_AUDIO_PCM_STOP_PLAYBACK,
+	IPC_M_AUDIO_PCM_START_CAPTURE,
+	IPC_M_AUDIO_PCM_STOP_CAPTURE,
+} audio_pcm_iface_funcs_t;
+
+/**
+ * Get human readable capability name.
+ * @param cap audio capability.
+ * @return Valid string
+ */
+const char *audio_pcm_cap_str(audio_cap_t cap)
+{
+	static const char *caps[] = {
+		[AUDIO_CAP_CAPTURE] = "CAPTURE",
+		[AUDIO_CAP_PLAYBACK] = "PLAYBACK",
+		[AUDIO_CAP_MAX_BUFFER] = "MAXIMUM BUFFER SIZE",
+		[AUDIO_CAP_BUFFER_POS] = "KNOWS BUFFER POSITION",
+		[AUDIO_CAP_INTERRUPT] = "FRAGMENT INTERRUPTS",
+		[AUDIO_CAP_INTERRUPT_MIN_FRAMES] = "MINIMUM FRAGMENT SIZE",
+		[AUDIO_CAP_INTERRUPT_MAX_FRAMES] = "MAXIMUM FRAGMENT SIZE",
+	};
+	if (cap >= ARRAY_SIZE(caps))
+		return "UNKNOWN CAP";
+	return caps[cap];
+
+}
+
+/**
+ * Get human readable event name.
+ * @param event Audio device event
+ * @return Valid string
+ */
+const char *audio_pcm_event_str(pcm_event_t event)
+{
+	static const char *events[] = {
+		[PCM_EVENT_PLAYBACK_STARTED] = "PLAYBACK STARTED",
+		[PCM_EVENT_CAPTURE_STARTED] = "CAPTURE STARTED",
+		[PCM_EVENT_FRAMES_PLAYED] = "FRAGMENT PLAYED",
+		[PCM_EVENT_FRAMES_CAPTURED] = "FRAGMENT CAPTURED",
+		[PCM_EVENT_PLAYBACK_TERMINATED] = "PLAYBACK TERMINATED",
+		[PCM_EVENT_CAPTURE_TERMINATED] = "CAPTURE TERMINATED",
+	};
+	if (event >= ARRAY_SIZE(events))
+		return "UNKNOWN EVENT";
+	return events[event];
+}
+
+/*
+ * CLIENT SIDE
+ */
+
+/**
+ * Open audio session with the first registered device.
+ *
+ * @return Pointer to a new audio device session, NULL on failure.
+ */
+audio_pcm_sess_t *audio_pcm_open_default(void)
+{
+	static bool resolved = false;
+	static category_id_t pcm_id = 0;
+	if (!resolved) {
+		const int ret = loc_category_get_id("audio-pcm", &pcm_id,
+		    IPC_FLAG_BLOCKING);
+		if (ret != EOK)
+			return NULL;
+		resolved = true;
+	}
+
+	service_id_t *svcs = NULL;
+	size_t count = 0;
+	const int ret = loc_category_get_svcs(pcm_id, &svcs, &count);
+	if (ret != EOK)
+		return NULL;
+
+	audio_pcm_sess_t *session = NULL;
+	if (count)
+		session = audio_pcm_open_service(svcs[0]);
+	free(svcs);
+	return session;
+}
+
+/**
+ * Open audio session with device identified by location service string.
+ *
+ * @param name Location service string.
+ * @return Pointer to a new audio device session, NULL on failure.
+ */
+audio_pcm_sess_t *audio_pcm_open(const char *name)
+{
+	devman_handle_t device_handle = 0;
+	const int ret = devman_fun_get_handle(name, &device_handle, 0);
+	if (ret != EOK)
+		return NULL;
+	return devman_device_connect(EXCHANGE_SERIALIZE, device_handle,
+	    IPC_FLAG_BLOCKING);
+}
+
+/**
+ * Open audio session with device identified by location service id
+ *
+ * @param name Location service id.
+ * @return Pointer to a new audio device session, NULL on failure.
+ */
+audio_pcm_sess_t *audio_pcm_open_service(service_id_t id)
+{
+	return loc_service_connect(EXCHANGE_SERIALIZE, id, IPC_FLAG_BLOCKING);
+}
+
+/**
+ * Close open audio device session.
+ *
+ * @param name Open audio device session.
+ *
+ * @note Calling this function on already closed or invalid session results
+ * in undefined behavior.
+ */
+void audio_pcm_close(audio_pcm_sess_t *sess)
+{
+	if (sess)
+		async_hangup(sess);
+}
+
+/**
+ * Get a short description string.
+ *
+ * @param sess Audio device session.
+ * @param name Place to store newly allocated string.
+ *
+ * @return Error code.
+ *
+ * @note Caller is responsible for freeing newly allocated memory.
+ */
+int audio_pcm_get_info_str(audio_pcm_sess_t *sess, const char **name)
+{
+	if (!name)
+		return EINVAL;
+	async_exch_t *exch = async_exchange_begin(sess);
+	sysarg_t name_size;
+	const int ret = async_req_1_1(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_GET_INFO_STR, &name_size);
+	if (ret == EOK) {
+		char *name_place = calloc(1, name_size);
+		if (!name_place) {
+			/* Make the other side fail
+			 * as it waits for read request */
+			async_data_read_start(exch, (void*)-1, 0);
+			async_exchange_end(exch);
+			return ENOMEM;
+		}
+		const int ret =
+		    async_data_read_start(exch, name_place, name_size);
+		if (ret != EOK) {
+			free(name_place);
+			async_exchange_end(exch);
+			return ret;
+		}
+		*name = name_place;
+	}
+	async_exchange_end(exch);
+	return ret;
+}
+
+
+/**
+ * Query value of specified capability.
+ *
+ * @param sess Audio device session.
+ * @param cap  Audio device capability.
+ * @param val  Place to store queried value.
+ *
+ * @return Error code.
+ */
+int audio_pcm_query_cap(audio_pcm_sess_t *sess, audio_cap_t cap)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	sysarg_t value = 0;
+	const int ret = async_req_2_1(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE), IPC_M_AUDIO_PCM_QUERY_CAPS,
+	    cap, &value);
+	async_exchange_end(exch);
+	if (ret == EOK)
+		return value;
+	return ret;
+}
+
+/**
+ * Query current position in device buffer.
+ *
+ * @param sess Audio device session.
+ * @param pos Place to store the result.
+ *
+ * @return Error code.
+ *
+ * Works for both playback and capture.
+ */
+int audio_pcm_get_buffer_pos(audio_pcm_sess_t *sess, size_t *pos)
+{
+	if (!pos)
+		return EINVAL;
+	async_exch_t *exch = async_exchange_begin(sess);
+	sysarg_t value = 0;
+	const int ret = async_req_1_1(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_GET_BUFFER_POS, &value);
+	if (ret == EOK)
+		*pos = value;
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Test format parameters for device support.
+ *
+ * @param sess Audio device session.
+ * @param channels Number of channels
+ * @param rate Sampling rate.
+ * @format Sample format.
+ *
+ * @return Error code.
+ *
+ * Works for both playback and capture. This function modifies provided
+ * parameters to the nearest values supported by the device.
+ */
+int audio_pcm_test_format(audio_pcm_sess_t *sess, unsigned *channels,
+    unsigned *rate, pcm_sample_format_t *format)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	sysarg_t channels_arg = channels ? *channels : 0;
+	sysarg_t rate_arg = rate ? *rate : 0;
+	sysarg_t format_arg = format ? *format : 0;
+	const int ret = async_req_4_3(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_TEST_FORMAT, channels_arg, rate_arg, format_arg,
+	    &channels_arg, &rate_arg, &format_arg);
+	async_exchange_end(exch);
+
+	/* All OK or something has changed. Verify that it was not one of the
+	 * params we care about */
+	if ((ret == EOK || ret == ELIMIT)
+	    && (!channels || *channels == channels_arg)
+	    && (!rate || *rate == rate_arg)
+	    && (!format || *format == format_arg))
+		return EOK;
+	if (channels)
+		*channels = channels_arg;
+	if (rate)
+		*rate = rate_arg;
+	if (format)
+		*format = format_arg;
+	return ret;
+}
+
+/**
+ * Register callback for device generated events.
+ *
+ * @param sess Audio device session.
+ * @param event_rec Event callback function.
+ * @param arg Event callback custom parameter.
+ *
+ * @return Error code.
+ */
+int audio_pcm_register_event_callback(audio_pcm_sess_t *sess,
+    async_client_conn_t event_callback, void *arg)
+{
+	if (!event_callback)
+		return EINVAL;
+
+	async_exch_t *exch = async_exchange_begin(sess);
+	int ret = async_req_1_0(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_REGISTER_EVENTS);
+	if (ret == EOK) {
+		ret = async_connect_to_me(exch, 0, 0, 0, event_callback, arg);
+	}
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Unregister callback for device generated events.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_unregister_event_callback(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_1_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_UNREGISTER_EVENTS);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Get device accessible playback/capture buffer.
+ *
+ * @param sess Audio device session.
+ * @param buffer Place to store pointer to the buffer.
+ * @param size Place to store buffer size (bytes).
+ *
+ * @return Error code.
+ */
+int audio_pcm_get_buffer(audio_pcm_sess_t *sess, void **buffer, size_t *size)
+{
+	if (!buffer || !size)
+		return EINVAL;
+
+	async_exch_t *exch = async_exchange_begin(sess);
+
+	sysarg_t buffer_size = *size;
+	int ret = async_req_2_1(exch, DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_GET_BUFFER, (sysarg_t)buffer_size, &buffer_size);
+	if (ret == EOK) {
+		void *dst = NULL;
+		ret = async_share_in_start_0_0(exch, buffer_size, &dst);
+		if (ret != EOK) {
+			async_exchange_end(exch);
+			return ret;
+		}
+		*buffer = dst;
+		*size = buffer_size;
+	}
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Release device accessible playback/capture buffer.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_release_buffer(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_1_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_RELEASE_BUFFER);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Start playback on buffer from position 0.
+ *
+ * @param sess Audio device session.
+ * @param frames Size of fragment (in frames).
+ * @param channels Number of channels.
+ * @param sample_rate Sampling rate (for one channel).
+ * @param format Sample format.
+ *
+ * @return Error code.
+ *
+ * Event will be generated after every fragment. Set fragment size to
+ * 0 to turn off event generation.
+ */
+int audio_pcm_start_playback_fragment(audio_pcm_sess_t *sess, unsigned frames,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
+{
+	if (channels > UINT16_MAX)
+		return EINVAL;
+	assert((format & UINT16_MAX) == format);
+	const sysarg_t packed = (channels << 16) | (format & UINT16_MAX);
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_4_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_START_PLAYBACK,
+	    frames, sample_rate, packed);
+	async_exchange_end(exch);
+	return ret;
+}
+/**
+ * Stops playback after current fragment.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_last_playback_fragment(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_2_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_PLAYBACK, false);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Start playback on buffer from the current position.
+ *
+ * @param sess Audio device session.
+ * @param channels Number of channels.
+ * @param sample_rate Sampling rate (for one channel).
+ * @param format Sample format.
+ *
+ * @return Error code.
+ */
+int audio_pcm_start_playback(audio_pcm_sess_t *sess,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
+{
+	return audio_pcm_start_playback_fragment(
+	    sess, 0, channels, sample_rate, format);
+}
+
+/**
+ * Immediately stops current playback.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_stop_playback_immediate(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_2_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_PLAYBACK, true);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Stops playback at the end of the current fragment.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_stop_playback(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_2_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_PLAYBACK, false);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Start capture on buffer from the current position.
+ *
+ * @param sess Audio device session.
+ * @param frames Size of fragment (in frames).
+ * @param channels Number of channels.
+ * @param sample_rate Sampling rate (for one channel).
+ * @param format Sample format.
+ *
+ * @return Error code.
+ *
+ * Event will be generated after every fragment. Set fragment size to
+ * 0 to turn off event generation.
+ */
+int audio_pcm_start_capture_fragment(audio_pcm_sess_t *sess, unsigned frames,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
+{
+	if (channels > UINT16_MAX)
+		return EINVAL;
+	assert((format & UINT16_MAX) == format);
+	const sysarg_t packed = (channels << 16) | (format & UINT16_MAX);
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_4_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE), IPC_M_AUDIO_PCM_START_CAPTURE,
+	    frames, sample_rate, packed);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Start capture on buffer from the current position.
+ *
+ * @param sess Audio device session.
+ * @param channels Number of channels.
+ * @param sample_rate Sampling rate (for one channel).
+ * @param format Sample format.
+ *
+ * @return Error code.
+ */
+int audio_pcm_start_capture(audio_pcm_sess_t *sess,
+    unsigned channels, unsigned sample_rate, pcm_sample_format_t format)
+{
+	return audio_pcm_start_capture_fragment(
+	    sess, 0, channels, sample_rate, format);
+}
+
+/**
+ * Stops capture at the end of current fragment.
+ *
+ * Won't work if capture was started with fragment size 0.
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_last_capture_fragment(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_2_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_CAPTURE, false);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Immediately stops current capture.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_stop_capture_immediate(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_2_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_CAPTURE, true);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Stops capture at the end of the current fragment.
+ *
+ * @param sess Audio device session.
+ *
+ * @return Error code.
+ */
+int audio_pcm_stop_capture(audio_pcm_sess_t *sess)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret = async_req_2_0(exch,
+	    DEV_IFACE_ID(AUDIO_PCM_BUFFER_IFACE),
+	    IPC_M_AUDIO_PCM_STOP_CAPTURE, false);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/*
+ * SERVER SIDE
+ */
+static void remote_audio_pcm_get_info_str(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_query_caps(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_events_register(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_events_unregister(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_get_buffer_pos(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_test_format(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_get_buffer(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_release_buffer(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_start_playback(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_stop_playback(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_start_capture(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_audio_pcm_stop_capture(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
+
+/** Remote audio pcm buffer interface operations. */
+static remote_iface_func_ptr_t remote_audio_pcm_iface_ops[] = {
+	[IPC_M_AUDIO_PCM_GET_INFO_STR] = remote_audio_pcm_get_info_str,
+	[IPC_M_AUDIO_PCM_QUERY_CAPS] = remote_audio_pcm_query_caps,
+	[IPC_M_AUDIO_PCM_REGISTER_EVENTS] = remote_audio_pcm_events_register,
+	[IPC_M_AUDIO_PCM_UNREGISTER_EVENTS] = remote_audio_pcm_events_unregister,
+	[IPC_M_AUDIO_PCM_GET_BUFFER_POS] = remote_audio_pcm_get_buffer_pos,
+	[IPC_M_AUDIO_PCM_TEST_FORMAT] = remote_audio_pcm_test_format,
+	[IPC_M_AUDIO_PCM_GET_BUFFER] = remote_audio_pcm_get_buffer,
+	[IPC_M_AUDIO_PCM_RELEASE_BUFFER] = remote_audio_pcm_release_buffer,
+	[IPC_M_AUDIO_PCM_START_PLAYBACK] = remote_audio_pcm_start_playback,
+	[IPC_M_AUDIO_PCM_STOP_PLAYBACK] = remote_audio_pcm_stop_playback,
+	[IPC_M_AUDIO_PCM_START_CAPTURE] = remote_audio_pcm_start_capture,
+	[IPC_M_AUDIO_PCM_STOP_CAPTURE] = remote_audio_pcm_stop_capture,
+};
+
+/** Remote audio mixer interface structure. */
+remote_iface_t remote_audio_pcm_iface = {
+	.method_count = sizeof(remote_audio_pcm_iface_ops) /
+	    sizeof(remote_audio_pcm_iface_ops[0]),
+	.methods = remote_audio_pcm_iface_ops
+};
+
+void remote_audio_pcm_get_info_str(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+
+	if (!pcm_iface->get_info_str) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const char *name = NULL;
+	const int ret = pcm_iface->get_info_str(fun, &name);
+	const size_t name_size = name ? str_size(name) + 1 : 0;
+	async_answer_1(callid, ret, name_size);
+	/* Send the string. */
+	if (ret == EOK && name_size > 0) {
+		size_t size;
+		ipc_callid_t name_id;
+		if (!async_data_read_receive(&name_id, &size)) {
+			async_answer_0(name_id, EPARTY);
+			return;
+		}
+		if (size != name_size) {
+			async_answer_0(name_id, ELIMIT);
+			return;
+		}
+		async_data_read_finalize(name_id, name, name_size);
+	}
+}
+
+void remote_audio_pcm_query_caps(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	const audio_cap_t cap = DEV_IPC_GET_ARG1(*call);
+	if (pcm_iface->query_cap) {
+		const unsigned value = pcm_iface->query_cap(fun, cap);
+		async_answer_1(callid, EOK, value);
+	} else {
+		async_answer_0(callid, ENOTSUP);
+	}
+}
+
+static void remote_audio_pcm_events_register(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	if (!pcm_iface->get_event_session ||
+	    !pcm_iface->set_event_session) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	async_answer_0(callid, EOK);
+
+	ipc_call_t callback_call;
+	ipc_callid_t callback_id = async_get_call(&callback_call);
+	async_sess_t *sess =
+	    async_callback_receive_start(EXCHANGE_ATOMIC, &callback_call);
+	if (sess == NULL) {
+		ddf_msg(LVL_DEBUG, "Failed to create event callback");
+		async_answer_0(callback_id, EAGAIN);
+		return;
+	}
+	const int ret = pcm_iface->set_event_session(fun, sess);
+	if (ret != EOK) {
+		ddf_msg(LVL_DEBUG, "Failed to set event callback.");
+		async_hangup(sess);
+		async_answer_0(callback_id, ret);
+		return;
+	}
+	async_answer_0(callback_id, EOK);
+}
+
+static void remote_audio_pcm_events_unregister(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	if (!pcm_iface->get_event_session ||
+	    !pcm_iface->set_event_session) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	async_sess_t *sess = pcm_iface->get_event_session(fun);
+	if (sess) {
+		async_hangup(sess);
+		pcm_iface->set_event_session(fun, NULL);
+	}
+	async_answer_0(callid, EOK);
+}
+
+void remote_audio_pcm_get_buffer_pos(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	size_t pos = 0;
+	const int ret = pcm_iface->get_buffer_pos ?
+	    pcm_iface->get_buffer_pos(fun, &pos) : ENOTSUP;
+	async_answer_1(callid, ret, pos);
+}
+
+void remote_audio_pcm_test_format(ddf_fun_t *fun, void *iface, ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	unsigned channels = DEV_IPC_GET_ARG1(*call);
+	unsigned rate = DEV_IPC_GET_ARG2(*call);
+	pcm_sample_format_t format = DEV_IPC_GET_ARG3(*call);
+	const int ret = pcm_iface->test_format ?
+	    pcm_iface->test_format(fun, &channels, &rate, &format) : ENOTSUP;
+	async_answer_3(callid, ret, channels, rate, format);
+}
+
+void remote_audio_pcm_get_buffer(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+
+	if (!pcm_iface->get_buffer ||
+	    !pcm_iface->release_buffer) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	void *buffer = NULL;
+	size_t size = DEV_IPC_GET_ARG1(*call);
+	int ret = pcm_iface->get_buffer(fun, &buffer, &size);
+	async_answer_1(callid, ret, size);
+	if (ret != EOK || size == 0)
+		return;
+
+	/* Share the buffer. */
+	size_t share_size = 0;
+	ipc_callid_t share_id = 0;
+
+	ddf_msg(LVL_DEBUG2, "Receiving share request.");
+	if (!async_share_in_receive(&share_id, &share_size)) {
+		ddf_msg(LVL_DEBUG, "Failed to share pcm buffer.");
+		pcm_iface->release_buffer(fun);
+		async_answer_0(share_id, EPARTY);
+		return;
+	}
+
+	ddf_msg(LVL_DEBUG2, "Checking requested share size.");
+	if (share_size != size) {
+		ddf_msg(LVL_DEBUG, "Incorrect pcm buffer size requested.");
+		pcm_iface->release_buffer(fun);
+		async_answer_0(share_id, ELIMIT);
+		return;
+	}
+
+	ddf_msg(LVL_DEBUG2, "Calling share finalize.");
+	ret = async_share_in_finalize(share_id, buffer, AS_AREA_WRITE
+	| AS_AREA_READ);
+	if (ret != EOK) {
+		ddf_msg(LVL_DEBUG, "Failed to share buffer.");
+		pcm_iface->release_buffer(fun);
+		return;
+	}
+
+	ddf_msg(LVL_DEBUG2, "Buffer shared with size %zu.", share_size);
+}
+
+void remote_audio_pcm_release_buffer(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+
+	const int ret = pcm_iface->release_buffer ?
+	    pcm_iface->release_buffer(fun) : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+
+void remote_audio_pcm_start_playback(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+
+	const unsigned frames = DEV_IPC_GET_ARG1(*call);
+	const unsigned rate = DEV_IPC_GET_ARG2(*call);
+	const unsigned channels = (DEV_IPC_GET_ARG3(*call) >> 16) & UINT8_MAX;
+	const pcm_sample_format_t format = DEV_IPC_GET_ARG3(*call) & UINT16_MAX;
+
+	const int ret = pcm_iface->start_playback
+	    ? pcm_iface->start_playback(fun, frames, channels, rate, format)
+	    : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+
+void remote_audio_pcm_stop_playback(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	const bool immediate = DEV_IPC_GET_ARG1(*call);
+
+	const int ret = pcm_iface->stop_playback ?
+	    pcm_iface->stop_playback(fun, immediate) : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+
+void remote_audio_pcm_start_capture(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+
+	const unsigned frames = DEV_IPC_GET_ARG1(*call);
+	const unsigned rate = DEV_IPC_GET_ARG2(*call);
+	const unsigned channels = (DEV_IPC_GET_ARG3(*call) >> 16) & UINT16_MAX;
+	const pcm_sample_format_t format = DEV_IPC_GET_ARG3(*call) & UINT16_MAX;
+
+	const int ret = pcm_iface->start_capture
+	    ? pcm_iface->start_capture(fun, frames, channels, rate, format)
+	    : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+
+void remote_audio_pcm_stop_capture(ddf_fun_t *fun, void *iface,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	const audio_pcm_iface_t *pcm_iface = iface;
+	const bool immediate = DEV_IPC_GET_ARG1(*call);
+
+	const int ret = pcm_iface->stop_capture ?
+	    pcm_iface->stop_capture(fun, immediate) : ENOTSUP;
+	async_answer_0(callid, ret);
+}
+
+/**
+ * @}
+ */
+
Index: uspace/lib/drv/generic/remote_hw_res.c
===================================================================
--- uspace/lib/drv/generic/remote_hw_res.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/drv/generic/remote_hw_res.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2011 Jan Vesely
  * All rights reserved.
  *
@@ -43,8 +44,14 @@
 static void remote_hw_res_enable_interrupt(ddf_fun_t *, void *, ipc_callid_t,
     ipc_call_t *);
+static void remote_hw_res_dma_channel_setup(ddf_fun_t *, void *, ipc_callid_t,
+    ipc_call_t *);
+static void remote_hw_res_dma_channel_remain(ddf_fun_t *, void *, ipc_callid_t,
+    ipc_call_t *);
 
 static remote_iface_func_ptr_t remote_hw_res_iface_ops [] = {
-	&remote_hw_res_get_resource_list,
-	&remote_hw_res_enable_interrupt
+	[HW_RES_GET_RESOURCE_LIST] = &remote_hw_res_get_resource_list,
+	[HW_RES_ENABLE_INTERRUPT] = &remote_hw_res_enable_interrupt,
+	[HW_RES_DMA_CHANNEL_SETUP] = &remote_hw_res_dma_channel_setup,
+	[HW_RES_DMA_CHANNEL_REMAIN] = &remote_hw_res_dma_channel_remain,
 };
 
@@ -94,4 +101,37 @@
 }
 
+static void remote_hw_res_dma_channel_setup(ddf_fun_t *fun, void *ops,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	hw_res_ops_t *hw_res_ops = ops;
+
+	if (hw_res_ops->dma_channel_setup == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned channel = DEV_IPC_GET_ARG1(*call) & 0xffff;
+	const uint8_t  mode = DEV_IPC_GET_ARG1(*call) >> 16;
+	const uint32_t address = DEV_IPC_GET_ARG2(*call);
+	const uint32_t size = DEV_IPC_GET_ARG3(*call);
+
+	const int ret = hw_res_ops->dma_channel_setup(
+	    fun, channel, address, size, mode);
+	async_answer_0(callid, ret);
+}
+
+static void remote_hw_res_dma_channel_remain(ddf_fun_t *fun, void *ops,
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	hw_res_ops_t *hw_res_ops = ops;
+
+	if (hw_res_ops->dma_channel_setup == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+	const unsigned channel = DEV_IPC_GET_ARG1(*call);
+	size_t remain = 0;
+	const int ret = hw_res_ops->dma_channel_remain(fun, channel, &remain);
+	async_answer_1(callid, ret, remain);
+}
 /**
  * @}
Index: uspace/lib/drv/generic/remote_pio_window.c
===================================================================
--- uspace/lib/drv/generic/remote_pio_window.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/drv/generic/remote_pio_window.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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/audio_mixer_iface.h
===================================================================
--- uspace/lib/drv/include/audio_mixer_iface.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/drv/include/audio_mixer_iface.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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
+ * @addtogroup usb
+ * @{
+ */
+/** @file
+ * @brief Audio mixer control interface.
+ */
+
+#ifndef LIBDRV_AUDIO_MIXER_IFACE_H_
+#define LIBDRV_AUDIO_MIXER_IFACE_H_
+
+#include <async.h>
+#include <stdbool.h>
+
+#include "ddf/driver.h"
+
+int audio_mixer_get_info(async_exch_t *, const char **, unsigned *);
+int audio_mixer_get_item_info(async_exch_t *, unsigned,
+    const char **, unsigned *);
+int audio_mixer_get_item_level(async_exch_t *, unsigned, unsigned *);
+int audio_mixer_set_item_level(async_exch_t *, unsigned, unsigned);
+
+
+/** Audio mixer communication interface. */
+typedef struct {
+	int (*get_info)(ddf_fun_t *, const char **, unsigned *);
+	int (*get_item_info)(ddf_fun_t *, unsigned, const char **, unsigned *);
+	int (*get_item_level)(ddf_fun_t *, unsigned, unsigned *);
+	int (*set_item_level)(ddf_fun_t *, unsigned, unsigned);
+} audio_mixer_iface_t;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/drv/include/audio_pcm_iface.h
===================================================================
--- uspace/lib/drv/include/audio_pcm_iface.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/drv/include/audio_pcm_iface.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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
+ * @addtogroup usb
+ * @{
+ */
+/** @file
+ * @brief Audio PCM buffer interface.
+ */
+
+#ifndef LIBDRV_AUDIO_PCM_IFACE_H_
+#define LIBDRV_AUDIO_PCM_IFACE_H_
+
+#include <async.h>
+#include <stdbool.h>
+#include <loc.h>
+#include <pcm/sample_format.h>
+
+#include "ddf/driver.h"
+
+typedef enum {
+	/** Device is capable of audio capture */
+	AUDIO_CAP_CAPTURE,
+	/** Device is capable of audio playback */
+	AUDIO_CAP_PLAYBACK,
+	/** Maximum size of device buffer */
+	AUDIO_CAP_MAX_BUFFER,
+	/** Device is capable of providing accurate buffer position info */
+	AUDIO_CAP_BUFFER_POS,
+	/** Device is capable of event based playback or capture */
+	AUDIO_CAP_INTERRUPT,
+	/** Minimal size of playback/record fragment */
+	AUDIO_CAP_INTERRUPT_MIN_FRAMES,
+	/** Maximum size of playback/record fragment */
+	AUDIO_CAP_INTERRUPT_MAX_FRAMES,
+} audio_cap_t;
+
+typedef enum {
+	PCM_EVENT_PLAYBACK_STARTED = IPC_FIRST_USER_METHOD,
+	PCM_EVENT_CAPTURE_STARTED,
+	PCM_EVENT_FRAMES_PLAYED,
+	PCM_EVENT_FRAMES_CAPTURED,
+	PCM_EVENT_PLAYBACK_TERMINATED,
+	PCM_EVENT_CAPTURE_TERMINATED,
+} pcm_event_t;
+
+const char *audio_pcm_cap_str(audio_cap_t);
+const char *audio_pcm_event_str(pcm_event_t);
+
+typedef async_sess_t audio_pcm_sess_t;
+
+audio_pcm_sess_t *audio_pcm_open(const char *);
+audio_pcm_sess_t *audio_pcm_open_default(void);
+audio_pcm_sess_t *audio_pcm_open_service(service_id_t service);
+void audio_pcm_close(audio_pcm_sess_t *);
+
+int audio_pcm_get_info_str(audio_pcm_sess_t *, const char **);
+int audio_pcm_test_format(audio_pcm_sess_t *, unsigned *, unsigned *,
+    pcm_sample_format_t *);
+int audio_pcm_query_cap(audio_pcm_sess_t *, audio_cap_t);
+int audio_pcm_register_event_callback(audio_pcm_sess_t *,
+    async_client_conn_t, void *);
+int audio_pcm_unregister_event_callback(audio_pcm_sess_t *);
+
+int audio_pcm_get_buffer(audio_pcm_sess_t *, void **, size_t *);
+int audio_pcm_get_buffer_pos(audio_pcm_sess_t *, size_t *);
+int audio_pcm_release_buffer(audio_pcm_sess_t *);
+
+int audio_pcm_start_playback_fragment(audio_pcm_sess_t *, unsigned,
+    unsigned, unsigned, pcm_sample_format_t);
+int audio_pcm_last_playback_fragment(audio_pcm_sess_t *);
+
+int audio_pcm_start_playback(audio_pcm_sess_t *,
+    unsigned, unsigned, pcm_sample_format_t);
+int audio_pcm_stop_playback_immediate(audio_pcm_sess_t *);
+int audio_pcm_stop_playback(audio_pcm_sess_t *);
+
+int audio_pcm_start_capture_fragment(audio_pcm_sess_t *, unsigned,
+    unsigned, unsigned, pcm_sample_format_t);
+int audio_pcm_last_capture_fragment(audio_pcm_sess_t *);
+
+int audio_pcm_start_capture(audio_pcm_sess_t *,
+    unsigned, unsigned, pcm_sample_format_t);
+int audio_pcm_stop_capture_immediate(audio_pcm_sess_t *);
+int audio_pcm_stop_capture(audio_pcm_sess_t *);
+
+/** Audio pcm communication interface. */
+typedef struct {
+	int (*get_info_str)(ddf_fun_t *, const char **);
+	int (*test_format)(ddf_fun_t *, unsigned *, unsigned *,
+	    pcm_sample_format_t *);
+	unsigned (*query_cap)(ddf_fun_t *, audio_cap_t);
+	int (*get_buffer_pos)(ddf_fun_t *, size_t *);
+	int (*get_buffer)(ddf_fun_t *, void **, size_t *);
+	int (*release_buffer)(ddf_fun_t *);
+	int (*set_event_session)(ddf_fun_t *, async_sess_t *);
+	async_sess_t * (*get_event_session)(ddf_fun_t *);
+	int (*start_playback)(ddf_fun_t *, unsigned,
+	    unsigned, unsigned, pcm_sample_format_t);
+	int (*stop_playback)(ddf_fun_t *, bool);
+	int (*start_capture)(ddf_fun_t *, unsigned,
+	    unsigned, unsigned, pcm_sample_format_t);
+	int (*stop_capture)(ddf_fun_t *, bool);
+} audio_pcm_iface_t;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/drv/include/ops/hw_res.h
===================================================================
--- uspace/lib/drv/include/ops/hw_res.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/drv/include/ops/hw_res.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -44,5 +44,6 @@
 	hw_resource_list_t *(*get_resource_list)(ddf_fun_t *);
 	bool (*enable_interrupt)(ddf_fun_t *);
-	int (*dma_channel_setup)(ddf_fun_t *, unsigned, uint32_t, uint16_t, uint8_t);
+	int (*dma_channel_setup)(ddf_fun_t *, unsigned, uint32_t, uint32_t, uint8_t);
+	int (*dma_channel_remain)(ddf_fun_t *, unsigned, size_t *);
 } hw_res_ops_t;
 
Index: uspace/lib/drv/include/ops/pio_window.h
===================================================================
--- uspace/lib/drv/include/ops/pio_window.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/drv/include/ops/pio_window.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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_audio_mixer.h
===================================================================
--- uspace/lib/drv/include/remote_audio_mixer.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/drv/include/remote_audio_mixer.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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_AUDIO_MIXER_H_
+#define LIBDRV_REMOTE_AUDIO_MIXER_H_
+
+extern remote_iface_t remote_audio_mixer_iface;
+
+#endif
+
+/**
+ * @}
+ */
+
Index: uspace/lib/drv/include/remote_audio_pcm.h
===================================================================
--- uspace/lib/drv/include/remote_audio_pcm.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/drv/include/remote_audio_pcm.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * 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_AUDIO_PCM_H_
+#define LIBDRV_REMOTE_AUDIO_PCM_H_
+
+extern remote_iface_t remote_audio_pcm_iface;
+
+#endif
+
+/**
+ * @}
+ */
+
Index: uspace/lib/drv/include/remote_pio_window.h
===================================================================
--- uspace/lib/drv/include/remote_pio_window.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/drv/include/remote_pio_window.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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/ext4/libext4_filesystem.c
===================================================================
--- uspace/lib/ext4/libext4_filesystem.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/ext4/libext4_filesystem.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,4 +39,5 @@
 #include <errno.h>
 #include <malloc.h>
+#include <ipc/vfs.h>
 #include "libext4.h"
 
Index: uspace/lib/fs/libfs.c
===================================================================
--- uspace/lib/fs/libfs.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/fs/libfs.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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:
@@ -797,9 +806,9 @@
 	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;
@@ -811,8 +820,8 @@
 		return;
 	}
-	
+
 	struct stat stat;
 	memset(&stat, 0, sizeof(struct stat));
-	
+
 	stat.fs_handle = fs_handle;
 	stat.service_id = service_id;
@@ -823,10 +832,61 @@
 	stat.size = ops->size_get(fn);
 	stat.service = ops->service_get(fn);
-	
+
 	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))) {
+		goto error;
+	}
+
+	struct statfs st;
+	memset(&st, 0, sizeof(struct statfs));
+
+	if (ops->size_block != NULL) {
+		rc = ops->size_block(service_id, &st.f_bsize);
+		if (rc != EOK)
+			goto error;
+	}
+
+	if (ops->total_block_count != NULL) {
+		rc = ops->total_block_count(service_id, &st.f_blocks);
+		if (rc != EOK)
+			goto error;
+	}
+
+	if (ops->free_block_count != NULL) {
+		rc = ops->free_block_count(service_id, &st.f_bfree);
+		if (rc != EOK)
+			goto error;
+	}
+
+	ops->node_put(fn);
+	async_data_read_finalize(callid, &st, 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.
@@ -882,8 +942,5 @@
 
 	fibril_mutex_lock(&instances_mutex);
-	list_foreach(instances_list, link) {
-		fs_instance_t *cur = list_get_instance(link, fs_instance_t,
-		    link);
-
+	list_foreach(instances_list, link, fs_instance_t, cur) {
 		if (cur->service_id == service_id) {
 			fibril_mutex_unlock(&instances_mutex);
@@ -908,8 +965,5 @@
 {
 	fibril_mutex_lock(&instances_mutex);
-	list_foreach(instances_list, link) {
-		fs_instance_t *inst = list_get_instance(link, fs_instance_t,
-		    link);
-
+	list_foreach(instances_list, link, fs_instance_t, inst) {
 		if (inst->service_id == service_id) {
 			*idp = inst->data;
@@ -925,8 +979,5 @@
 {
 	fibril_mutex_lock(&instances_mutex);
-	list_foreach(instances_list, link) {
-		fs_instance_t *inst = list_get_instance(link, fs_instance_t,
-		    link);
-
+	list_foreach(instances_list, link, fs_instance_t, inst) {
 		if (inst->service_id == service_id) {
 			list_remove(&inst->link);
Index: uspace/lib/fs/libfs.h
===================================================================
--- uspace/lib/fs/libfs.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/fs/libfs.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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/graph/graph.c
===================================================================
--- uspace/lib/graph/graph.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/graph/graph.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -165,8 +165,7 @@
 
 	fibril_mutex_lock(&visualizer_list_mtx);
-	list_foreach(visualizer_list, link) {
-		visualizer_t *cur = list_get_instance(link, visualizer_t, link);
-		if (cur->reg_svc_handle == handle) {
-			vs = cur;
+	list_foreach(visualizer_list, link, visualizer_t, vcur) {
+		if (vcur->reg_svc_handle == handle) {
+			vs = vcur;
 			break;
 		}
@@ -182,8 +181,7 @@
 
 	fibril_mutex_lock(&renderer_list_mtx);
-	list_foreach(renderer_list, link) {
-		renderer_t *cur = list_get_instance(link, renderer_t, link);
-		if (cur->reg_svc_handle == handle) {
-			rnd = cur;
+	list_foreach(renderer_list, link, renderer_t, rcur) {
+		if (rcur->reg_svc_handle == handle) {
+			rnd = rcur;
 			break;
 		}
@@ -322,6 +320,5 @@
 	fibril_mutex_lock(&vs->mode_mtx);
 	vslmode_list_element_t *mode_elem = NULL;
-	list_foreach(vs->modes, link) {
-		vslmode_list_element_t *cur = list_get_instance(link, vslmode_list_element_t, link);
+	list_foreach(vs->modes, link, vslmode_list_element_t, cur) {
 		if (cur->mode.index == vs->def_mode_idx) {
 			mode_elem = cur;
@@ -382,6 +379,5 @@
 	fibril_mutex_lock(&vs->mode_mtx);
 	vslmode_list_element_t *mode_elem = NULL;
-	list_foreach(vs->modes, link) {
-		vslmode_list_element_t *cur = list_get_instance(link, vslmode_list_element_t, link);
+	list_foreach(vs->modes, link, vslmode_list_element_t, cur) {
 		if (cur->mode.index == mode_idx) {
 			mode_elem = cur;
@@ -425,6 +421,5 @@
 	fibril_mutex_lock(&vs->mode_mtx);
 	vslmode_list_element_t *mode_elem = NULL;
-	list_foreach(vs->modes, link) {
-		vslmode_list_element_t *cur = list_get_instance(link, vslmode_list_element_t, link);
+	list_foreach(vs->modes, link, vslmode_list_element_t, cur) {
 		if (cur->mode.index == mode_idx) {
 			mode_elem = cur;
Index: uspace/lib/gui/canvas.c
===================================================================
--- uspace/lib/gui/canvas.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/gui/canvas.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -105,5 +105,7 @@
 static void canvas_handle_keyboard_event(widget_t *widget, kbd_event_t event)
 {
-	/* No-op */
+	canvas_t *canvas = (canvas_t *) widget;
+	
+	sig_send(&canvas->keyboard_event, &event);
 }
 
@@ -142,4 +144,13 @@
 }
 
+bool update_canvas(canvas_t *canvas, surface_t *surface)
+{
+	if (surface != NULL)
+		canvas->surface = surface;
+	
+	canvas_repaint(&canvas->widget);
+	return true;
+}
+
 canvas_t *create_canvas(widget_t *parent, sysarg_t width, sysarg_t height,
     surface_t *surface)
Index: uspace/lib/gui/canvas.h
===================================================================
--- uspace/lib/gui/canvas.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/gui/canvas.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -42,4 +42,5 @@
 #include <surface.h>
 #include "widget.h"
+#include "connection.h"
 
 typedef struct {
@@ -48,4 +49,5 @@
 	sysarg_t height;
 	surface_t *surface;
+	signal_t keyboard_event;
 } canvas_t;
 
@@ -53,4 +55,5 @@
     surface_t *);
 extern canvas_t *create_canvas(widget_t *, sysarg_t, sysarg_t, surface_t *);
+extern bool update_canvas(canvas_t *, surface_t *);
 extern void deinit_canvas(canvas_t *);
 
Index: uspace/lib/gui/connection.c
===================================================================
--- uspace/lib/gui/connection.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/gui/connection.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -65,6 +65,5 @@
 
 	signal_node_t *sig_node = NULL;
-	list_foreach(connection_list, link) {
-		signal_node_t *cur = list_get_instance(link, signal_node_t, link);
+	list_foreach(connection_list, link, signal_node_t, cur) {
 		if (cur->signal == signal) {
 			sig_node = cur;
@@ -88,6 +87,5 @@
 
 	slot_node_t *slt_node = NULL;
-	list_foreach(sig_node->slots, link) {
-		slot_node_t *cur = list_get_instance(link, slot_node_t, link);
+	list_foreach(sig_node->slots, link, slot_node_t, cur) {
 		if (cur->widget == widget && cur->slot == slot) {
 			slt_node = cur;
@@ -121,6 +119,5 @@
 
 	signal_node_t *sig_node = NULL;
-	list_foreach(connection_list, link) {
-		signal_node_t *cur = list_get_instance(link, signal_node_t, link);
+	list_foreach(connection_list, link, signal_node_t, cur) {
 		if (cur->signal == signal) {
 			sig_node = cur;
@@ -135,6 +132,5 @@
 
 	slot_node_t *slt_node = NULL;
-	list_foreach(sig_node->slots, link) {
-		slot_node_t *cur = list_get_instance(link, slot_node_t, link);
+	list_foreach(sig_node->slots, link, slot_node_t, cur) {
 		if (cur->widget == widget && cur->slot == slot) {
 			slt_node = cur;
@@ -164,6 +160,5 @@
 
 	signal_node_t *sig_node = NULL;
-	list_foreach(connection_list, link) {
-		signal_node_t *cur = list_get_instance(link, signal_node_t, link);
+	list_foreach(connection_list, link, signal_node_t, cur) {
 		if (cur->signal == signal) {
 			sig_node = cur;
@@ -177,6 +172,5 @@
 	}
 
-	list_foreach(sig_node->slots, link) {
-		slot_node_t *cur = list_get_instance(link, slot_node_t, link);
+	list_foreach(sig_node->slots, link, slot_node_t, cur) {
 		cur->slot(cur->widget, data);
 	}
@@ -190,6 +184,5 @@
 
 	signal_node_t *sig_node = NULL;
-	list_foreach(connection_list, link) {
-		signal_node_t *cur = list_get_instance(link, signal_node_t, link);
+	list_foreach(connection_list, link, signal_node_t, cur) {
 		if (cur->signal == signal) {
 			sig_node = cur;
@@ -203,7 +196,5 @@
 	}
 
-	list_foreach(sig_node->slots, link) {
-		slot_node_t *cur = list_get_instance(link, slot_node_t, link);
-
+	list_foreach(sig_node->slots, link, slot_node_t, cur) {
 		void *data_copy = NULL;
 		if (data != NULL) {
Index: uspace/lib/gui/grid.c
===================================================================
--- uspace/lib/gui/grid.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/gui/grid.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -298,6 +298,5 @@
 	paint_internal(widget);
 	
-	list_foreach(widget->children, link) {
-		widget_t *child = list_get_instance(link, widget_t, link);
+	list_foreach(widget->children, link, widget_t, child) {
 		child->repaint(child);
 	}
Index: uspace/lib/gui/terminal.c
===================================================================
--- uspace/lib/gui/terminal.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/gui/terminal.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -685,7 +685,5 @@
 	terminal_t *term = NULL;
 	
-	list_foreach(terms, link) {
-		terminal_t *cur = list_get_instance(link, terminal_t, link);
-		
+	list_foreach(terms, link, terminal_t, cur) {
 		if (cur->dsid == (service_id_t) IPC_GET_ARG1(*icall)) {
 			term = cur;
Index: uspace/lib/gui/window.c
===================================================================
--- uspace/lib/gui/window.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/gui/window.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -66,7 +66,7 @@
 
 static pixel_t border_color = PIXEL(255, 0, 0, 0);
-static pixel_t header_bg_focus_color = PIXEL(255, 25, 25, 112);
+static pixel_t header_bg_focus_color = PIXEL(255, 88, 106, 196);
 static pixel_t header_fg_focus_color = PIXEL(255, 255, 255, 255);
-static pixel_t header_bg_unfocus_color = PIXEL(255, 70, 130, 180);
+static pixel_t header_bg_unfocus_color = PIXEL(255, 12, 57, 92);
 static pixel_t header_fg_unfocus_color = PIXEL(255, 255, 255, 255);
 
@@ -137,6 +137,5 @@
 {
 	if (widget->window->is_decorated) {
-		list_foreach(widget->children, link) {
-			widget_t *child = list_get_instance(link, widget_t, link);
+		list_foreach(widget->children, link, widget_t, child) {
 			child->rearrange(child, 
 			    widget->hpos + border_thickness,
@@ -146,6 +145,5 @@
 		}
 	} else {
-		list_foreach(widget->children, link) {
-			widget_t *child = list_get_instance(link, widget_t, link);
+		list_foreach(widget->children, link, widget_t, child) {
 			child->rearrange(child, widget->hpos, widget->vpos,
 			    widget->width, widget->height);
@@ -160,6 +158,5 @@
 	if (widget->window->is_decorated) {
 		paint_internal(widget);
-		list_foreach(widget->children, link) {
-			widget_t *child = list_get_instance(link, widget_t, link);
+		list_foreach(widget->children, link, widget_t, child) {
 			child->rearrange(child, 
 			    hpos + border_thickness,
@@ -169,6 +166,5 @@
 		}
 	} else {
-		list_foreach(widget->children, link) {
-			widget_t *child = list_get_instance(link, widget_t, link);
+		list_foreach(widget->children, link, widget_t, child) {
 			child->rearrange(child, hpos, vpos, width, height);
 		}
@@ -181,6 +177,5 @@
 		paint_internal(widget);
 	}
-	list_foreach(widget->children, link) {
-		widget_t *child = list_get_instance(link, widget_t, link);
+	list_foreach(widget->children, link, widget_t, child) {
 		child->repaint(child);
 	}
@@ -268,12 +263,10 @@
 			win_grab(widget->window->osess, event.pos_id, flags);
 		} else {
-			list_foreach(widget->children, link) {
-				widget_t *child = list_get_instance(link, widget_t, link);
+			list_foreach(widget->children, link, widget_t, child) {
 				child->handle_position_event(child, event);
 			}
 		}
 	} else {
-		list_foreach(widget->children, link) {
-			widget_t *child = list_get_instance(link, widget_t, link);
+		list_foreach(widget->children, link, widget_t, child) {
 			child->handle_position_event(child, event);
 		}
Index: uspace/lib/hound/Makefile
===================================================================
--- uspace/lib/hound/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/hound/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2012 Jan Vesely
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+EXTRA_CFLAGS = -Iinclude/hound -Iinclude -I$(LIBPCM_PREFIX)/include
+LIBRARY = libhound
+
+SOURCES = \
+	src/protocol.c \
+	src/client.c
+include $(USPACE_PREFIX)/Makefile.common
+
Index: uspace/lib/hound/include/hound/client.h
===================================================================
--- uspace/lib/hound/include/hound/client.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/hound/include/hound/client.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 libhound
+ * @addtogroup audio
+ * @{
+ */
+/** @file
+ * @brief Audio PCM buffer interface.
+ */
+
+#ifndef LIBHOUND_CLIENT_H_
+#define LIBHOUND_CLIENT_H_
+
+#include <async.h>
+#include <stdbool.h>
+#include <pcm/format.h>
+#include <hound/protocol.h>
+
+#define HOUND_DEFAULT_TARGET "default"
+#define HOUND_ALL_TARGETS "all"
+
+typedef struct hound_context hound_context_t;
+typedef struct hound_stream hound_stream_t;
+
+hound_context_t * hound_context_create_playback(const char *name,
+    pcm_format_t format, size_t bsize);
+hound_context_t * hound_context_create_capture(const char *name,
+    pcm_format_t format, size_t bsize);
+void hound_context_destroy(hound_context_t *hound);
+
+int hound_context_set_main_stream_params(hound_context_t *hound,
+    pcm_format_t format, size_t bsize);
+
+int hound_context_get_available_targets(hound_context_t *hound,
+    const char ***names, size_t *count);
+int hound_context_get_connected_targets(hound_context_t *hound,
+    const char ***names, size_t *count);
+
+int hound_context_connect_target(hound_context_t *hound, const char* target);
+int hound_context_disconnect_target(hound_context_t *hound, const char* target);
+
+hound_stream_t *hound_stream_create(hound_context_t *hound, unsigned flags,
+    pcm_format_t format, size_t bsize);
+void hound_stream_destroy(hound_stream_t *stream);
+
+int hound_stream_write(hound_stream_t *stream, const void *data, size_t size);
+int hound_stream_read(hound_stream_t *stream, void *data, size_t size);
+int hound_stream_drain(hound_stream_t *stream);
+
+int hound_write_main_stream(hound_context_t *hound,
+    const void *data, size_t size);
+int hound_read_main_stream(hound_context_t *hound, void *data, size_t size);
+int hound_write_replace_main_stream(hound_context_t *hound,
+    const void *data, size_t size);
+int hound_write_immediate(hound_context_t *hound,
+    pcm_format_t format, const void *data, size_t size);
+
+#endif
Index: uspace/lib/hound/include/hound/protocol.h
===================================================================
--- uspace/lib/hound/include/hound/protocol.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/hound/include/hound/protocol.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2013 Jan Vesely
+ * 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 libhound
+ * @addtogroup audio
+ * @{
+ */
+/** @file
+ * @brief Audio PCM buffer interface.
+ */
+
+#ifndef LIBHOUND_PROTOCOL_H_
+#define LIBHOUND_PROTOCOL_H_
+
+#include <async.h>
+#include <errno.h>
+#include <pcm/format.h>
+
+extern const char *HOUND_SERVICE;
+
+typedef enum {
+	HOUND_SINK_APPS = 0x1,
+	HOUND_SINK_DEVS = 0x2,
+	HOUND_SOURCE_APPS = 0x4,
+	HOUND_SOURCE_DEVS = 0x8,
+	HOUND_CONNECTED = 0x10,
+
+	HOUND_STREAM_DRAIN_ON_EXIT = 0x1,
+	HOUND_STREAM_IGNORE_UNDERFLOW = 0x2,
+	HOUND_STREAM_IGNORE_OVERFLOW = 0x4,
+} hound_flags_t;
+
+typedef async_sess_t hound_sess_t;
+typedef intptr_t hound_context_id_t;
+
+/**
+ * Check context id for errors.
+ * @param id Context id
+ * @return Error code.
+ */
+static inline int hound_context_id_err(hound_context_id_t id)
+{
+	return id > 0 ? EOK : (id == 0 ? ENOENT : id);
+}
+
+hound_sess_t *hound_service_connect(const char *service);
+void hound_service_disconnect(hound_sess_t *sess);
+
+hound_context_id_t hound_service_register_context(hound_sess_t *sess,
+    const char *name, bool record);
+int hound_service_unregister_context(hound_sess_t *sess, hound_context_id_t id);
+
+int hound_service_get_list(hound_sess_t *sess, const char ***ids, size_t *count,
+    int flags, const char *connection);
+
+/**
+ * Wrapper for list queries with no connection parameter.
+ * @param[in] sess hound daemon session.
+ * @param[out] ids list of string identifiers
+ * @param[out] count Number of elements in @p ids
+ * @param[in] flags Flags limiting the query.
+ * @return Error code.
+ */
+static inline int hound_service_get_list_all(hound_sess_t *sess,
+    const char ***ids, size_t *count, int flags)
+{
+	return hound_service_get_list(sess, ids, count, flags, NULL);
+}
+
+int hound_service_connect_source_sink(hound_sess_t *sess, const char *source,
+    const char *sink);
+int hound_service_disconnect_source_sink(hound_sess_t *sess, const char *source,
+    const char *sink);
+
+int hound_service_stream_enter(async_exch_t *exch, hound_context_id_t id,
+    int flags, pcm_format_t format, size_t bsize);
+int hound_service_stream_drain(async_exch_t *exch);
+int hound_service_stream_exit(async_exch_t *exch);
+
+int hound_service_stream_write(async_exch_t *exch, const void *data, size_t size);
+int hound_service_stream_read(async_exch_t *exch, void *data, size_t size);
+
+/* Server */
+
+/** Hound server interace structure */
+typedef struct hound_server_iface {
+	/** Create new context */
+	int (*add_context)(void *, hound_context_id_t *, const char *, bool);
+	/** Destroy existing context */
+	int (*rem_context)(void *, hound_context_id_t);
+	/** Query context direction */
+	bool (*is_record_context)(void *, hound_context_id_t);
+	/** Get string identifiers of specified objects */
+	int (*get_list)(void *, const char ***, size_t *, const char *, int);
+	/** Create connection between source and sink */
+	int (*connect)(void *, const char *, const char *);
+	/** Destroy connection between source and sink */
+	int (*disconnect)(void *, const char *, const char *);
+	/** Create new stream tied to the context */
+	int (*add_stream)(void *, hound_context_id_t, int, pcm_format_t, size_t,
+	    void **);
+	/** Destroy existing stream */
+	int (*rem_stream)(void *, void *);
+	/** Block until the stream buffer is empty */
+	int (*drain_stream)(void *);
+	/** Write new data to the stream */
+	int (*stream_data_write)(void *, const void *, size_t);
+	/** Read data from the stream */
+	int (*stream_data_read)(void *, void *, size_t);
+	void *server;
+} hound_server_iface_t;
+
+void hound_service_set_server_iface(const hound_server_iface_t *iface);
+
+void hound_connection_handler(ipc_callid_t iid, ipc_call_t *icall, void *arg);
+
+#endif
+/** @}
+ */
Index: uspace/lib/hound/include/hound/server.h
===================================================================
--- uspace/lib/hound/include/hound/server.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/hound/include/hound/server.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 libhound
+ * @addtogroup audio
+ * @{
+ */
+/** @file
+ * @brief Audio PCM buffer interface.
+ */
+
+#ifndef LIBHOUND_SERVER_H_
+#define LIBHOUND_SERVER_H_
+
+#include <async.h>
+#include <loc.h>
+
+typedef void (*dev_change_callback_t)(void);
+typedef int (*device_callback_t)(service_id_t, const char *);
+
+int hound_server_register(const char *name, service_id_t *id);
+void hound_server_unregister(service_id_t id);
+int hound_server_set_device_change_callback(dev_change_callback_t cb);
+int hound_server_devices_iterate(device_callback_t callback);
+
+#endif
Index: uspace/lib/hound/src/client.c
===================================================================
--- uspace/lib/hound/src/client.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/hound/src/client.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 libhound
+ * @addtogroup audio
+ * @{
+ */
+/** @file
+ * Common USB functions.
+ */
+#include <adt/list.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <loc.h>
+#include <str.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <libarch/types.h>
+
+#include "protocol.h"
+#include "client.h"
+
+/** Stream structure */
+typedef struct hound_stream {
+	/** link in context's list */
+	link_t link;
+	/** audio data format fo the stream */
+	pcm_format_t format;
+	/** IPC exchange representing the stream (in STREAM MODE) */
+	async_exch_t *exch;
+	/** parent context */
+	hound_context_t *context;
+	/** Stream flags */
+	int flags;
+} hound_stream_t;
+
+/**
+ * Linked list isntacen helper function.
+ * @param l link
+ * @return hound stream isntance.
+ */
+static inline hound_stream_t * hound_stream_from_link(link_t *l)
+{
+	return l ? list_get_instance(l, hound_stream_t, link) : NULL;
+}
+
+/** Hound client context structure */
+typedef struct hound_context {
+	/** Audio session */
+	hound_sess_t *session;
+	/** context name, reported to the daemon */
+	const char *name;
+	/** True if the instance is record context */
+	bool record;
+	/** List of associated streams */
+	list_t stream_list;
+	/** Main stream helper structure */
+	struct {
+		hound_stream_t *stream;
+		pcm_format_t format;
+		size_t bsize;
+	} main;
+	/** Assigned context id */
+	hound_context_id_t id;
+} hound_context_t;
+
+/**
+ * Alloc and initialize context structure.
+ * @param name Base for the real context name, will add task id.
+ * @param record True if the new context should capture audio data.
+ * @param format PCM format of the main pipe.
+ * @param bsize Server size buffer size of the main stream.
+ * @return valid pointer to initialized structure on success, NULL on failure
+ */
+static hound_context_t *hound_context_create(const char *name, bool record,
+    pcm_format_t format, size_t bsize)
+{
+	hound_context_t *new_context = malloc(sizeof(hound_context_t));
+	if (new_context) {
+		char *cont_name;
+		int ret = asprintf(&cont_name, "%" PRIu64 ":%s",
+		    task_get_id(), name);
+		if (ret < 0) {
+			free(new_context);
+			return NULL;
+		}
+		list_initialize(&new_context->stream_list);
+		new_context->name = cont_name;
+		new_context->record = record;
+		new_context->session = hound_service_connect(HOUND_SERVICE);
+		new_context->main.stream = NULL;
+		new_context->main.format = format;
+		new_context->main.bsize = bsize;
+		if (!new_context->session) {
+			free(new_context->name);
+			free(new_context);
+			return NULL;
+		}
+		new_context->id = hound_service_register_context(
+		    new_context->session, new_context->name, record);
+		if (hound_context_id_err(new_context->id) != EOK) {
+			hound_service_disconnect(new_context->session);
+			free(new_context->name);
+			free(new_context);
+			return NULL;
+		}
+	}
+	return new_context;
+}
+
+/**
+ * Playback context helper function.
+ * @param name Base for the real context name, will add task id.
+ * @param format PCM format of the main pipe.
+ * @param bsize Server size buffer size of the main stream.
+ * @return valid pointer to initialized structure on success, NULL on failure
+ */
+hound_context_t * hound_context_create_playback(const char *name,
+    pcm_format_t format, size_t bsize)
+{
+	return hound_context_create(name, false, format, bsize);
+}
+
+/**
+ * Record context helper function.
+ * @param name Base for the real context name, will add task id.
+ * @param format PCM format of the main pipe.
+ * @param bsize Server size buffer size of the main stream.
+ * @return valid pointer to initialized structure on success, NULL on failure
+ */
+hound_context_t * hound_context_create_capture(const char *name,
+    pcm_format_t format, size_t bsize)
+{
+	return hound_context_create(name, true, format, bsize);
+}
+
+/**
+ * Correctly dispose of the hound context structure.
+ * @param hound context to remove.
+ *
+ * The function will destroy all associated streams first. Pointers
+ * to these structures will become invalid and the function will block
+ * if any of these stream needs to be drained first.
+ */
+void hound_context_destroy(hound_context_t *hound)
+{
+	assert(hound);
+
+	while (!list_empty(&hound->stream_list)) {
+		link_t *first = list_first(&hound->stream_list);
+		hound_stream_t *stream = hound_stream_from_link(first);
+		hound_stream_destroy(stream);
+	}
+
+	hound_service_unregister_context(hound->session, hound->id);
+	hound_service_disconnect(hound->session);
+	free(hound->name);
+	free(hound);
+}
+
+/**
+ * Get a list of possible connection targets.
+ * @param[in] hound Hound context.
+ * @param[out] names list of target string ids.
+ * @param[out] count Number of elements in @p names list
+ * @return Error code.
+ *
+ * The function will return deice sinks or source based on the context type.
+ */
+int hound_context_get_available_targets(hound_context_t *hound,
+    const char ***names, size_t *count)
+{
+	assert(hound);
+	assert(names);
+	assert(count);
+	return hound_service_get_list_all(hound->session, names, count,
+	    hound->record ? HOUND_SOURCE_DEVS : HOUND_SINK_DEVS);
+}
+
+/**
+ * Get a list of targets connected to the context.
+ * @param[in] hound Hound context.
+ * @param[out] names list of target string ids.
+ * @param[out] count Number of elements in @p names list
+ * @return Error code.
+ */
+int hound_context_get_connected_targets(hound_context_t *hound,
+    const char ***names, size_t *count)
+{
+	assert(hound);
+	assert(names);
+	assert(count);
+	return hound_service_get_list(hound->session, names, count,
+	    HOUND_CONNECTED | (hound->record ?
+	        HOUND_SOURCE_DEVS : HOUND_SINK_DEVS), hound->name);
+}
+
+/**
+ * Create a new connection to the target.
+ * @param hound Hound context.
+ * @param target String identifier of the desired target.
+ * @return Error code.
+ *
+ * The function recognizes special 'HOUND_DEFAULT_TARGET' and will
+ * connect to the first possible target if it is passed this value.
+ */
+int hound_context_connect_target(hound_context_t *hound, const char* target)
+{
+	assert(hound);
+	assert(target);
+
+	const char **tgt = NULL;
+	size_t count = 1;
+	int ret = EOK;
+	if (str_cmp(target, HOUND_DEFAULT_TARGET) == 0) {
+		ret = hound_context_get_available_targets(hound, &tgt, &count);
+		if (ret != EOK)
+			return ret;
+		target = tgt[0];
+	}
+	//TODO handle all-targets
+
+	if (hound->record) {
+		ret = hound_service_connect_source_sink(
+		    hound->session, target, hound->name);
+	} else {
+		ret = hound_service_connect_source_sink(
+		    hound->session, hound->name, target);
+	}
+	if (tgt)
+		free(tgt[0]);
+	free(tgt);
+	return ret;
+}
+
+/**
+ * Destroy a connection to the target.
+ * @param hound Hound context.
+ * @param target String identifier of the desired target.
+ * @return Error code.
+ */
+int hound_context_disconnect_target(hound_context_t *hound, const char* target)
+{
+	assert(hound);
+	assert(target);
+	//TODO handle all-targets
+	if (hound->record) {
+		return hound_service_disconnect_source_sink(
+		    hound->session, target, hound->name);
+	} else {
+		return hound_service_disconnect_source_sink(
+		    hound->session, hound->name, target);
+	}
+}
+
+/**
+ * Create a new stream associated with the context.
+ * @param hound Hound context.
+ * @param flags new stream flags.
+ * @param format new stream PCM format.
+ * @param bzise new stream server side buffer size (in bytes)
+ * @return Valid pointer to a stream instance, NULL on failure.
+ */
+hound_stream_t *hound_stream_create(hound_context_t *hound, unsigned flags,
+    pcm_format_t format, size_t bsize)
+{
+	assert(hound);
+	async_exch_t *stream_exch = async_exchange_begin(hound->session);
+	if (!stream_exch)
+		return NULL;
+	hound_stream_t *new_stream = malloc(sizeof(hound_stream_t));
+	if (new_stream) {
+		link_initialize(&new_stream->link);
+		new_stream->exch = stream_exch;
+		new_stream->format = format;
+		new_stream->context = hound;
+		new_stream->flags = flags;
+		const int ret = hound_service_stream_enter(new_stream->exch,
+		    hound->id, flags, format, bsize);
+		if (ret != EOK) {
+			async_exchange_end(new_stream->exch);
+			free(new_stream);
+			return NULL;
+		}
+		list_append(&new_stream->link, &hound->stream_list);
+	}
+	return new_stream;
+}
+
+/**
+ * Destroy existing stream
+ * @param stream The stream to destroy.
+ *
+ * Function will wait until the server side buffer is empty if the
+ * HOUND_STREAM_DRAIN_ON_EXIT flag was set on creation.
+ */
+void hound_stream_destroy(hound_stream_t *stream)
+{
+	if (stream) {
+		if (stream->flags & HOUND_STREAM_DRAIN_ON_EXIT)
+			hound_service_stream_drain(stream->exch);
+		hound_service_stream_exit(stream->exch);
+		async_exchange_end(stream->exch);
+		list_remove(&stream->link);
+		free(stream);
+	}
+}
+
+/**
+ * Send new data to a stream.
+ * @param stream The target stream
+ * @param data data buffer
+ * @param size size of the @p data buffer.
+ * @return error code.
+ */
+int hound_stream_write(hound_stream_t *stream, const void *data, size_t size)
+{
+	assert(stream);
+	if (!data || size == 0)
+		return EBADMEM;
+	return hound_service_stream_write(stream->exch, data, size);
+}
+
+/**
+ * Get data from a stream.
+ * @param stream The target stream.
+ * @param data data buffer.
+ * @param size size of the @p data buffer.
+ * @return error code.
+ */
+int hound_stream_read(hound_stream_t *stream, void *data, size_t size)
+{
+	assert(stream);
+	if (!data || size == 0)
+		return EBADMEM;
+	return hound_service_stream_read(stream->exch, data, size);
+}
+
+/**
+ * Wait until the server side buffer is empty.
+ * @param stream The stream that shoulod be drained.
+ * @return Error code.
+ */
+int hound_stream_drain(hound_stream_t *stream)
+{
+	assert(stream);
+	return hound_service_stream_drain(stream->exch);
+}
+
+/**
+ * Main stream getter function.
+ * @param hound Houndcontext.
+ * @return Valid stream pointer, NULL on failure.
+ *
+ * The function will create new stream, or return a pointer to the exiting one
+ * if it exists.
+ */
+static hound_stream_t * hound_get_main_stream(hound_context_t *hound)
+{
+	assert(hound);
+	if (!hound->main.stream)
+		hound->main.stream = hound_stream_create(hound,
+		    HOUND_STREAM_DRAIN_ON_EXIT,hound->main.format,
+		    hound->main.bsize);
+	return hound->main.stream;
+}
+
+/**
+ * Send new data to the main stream.
+ * @param stream The target stream
+ * @param data data buffer
+ * @param size size of the @p data buffer.
+ * @return error code.
+ */
+int hound_write_main_stream(hound_context_t *hound,
+    const void *data, size_t size)
+{
+	assert(hound);
+	if (hound->record)
+		return EINVAL;
+
+	hound_stream_t *mstream = hound_get_main_stream(hound);
+	if (!mstream)
+		return ENOMEM;
+	return hound_stream_write(mstream, data, size);
+}
+
+/**
+ * Get data from the main stream.
+ * @param stream The target stream
+ * @param data data buffer
+ * @param size size of the @p data buffer.
+ * @return error code.
+ */
+int hound_read_main_stream(hound_context_t *hound, void *data, size_t size)
+{
+	assert(hound);
+	if (!hound->record)
+		return EINVAL;
+	hound_stream_t *mstream = hound_get_main_stream(hound);
+	if (!mstream)
+		return ENOMEM;
+	return hound_stream_read(mstream, data, size);
+}
+
+/**
+ * Destroy the old main stream and replace it with a new one with fresh data.
+ * @param hound Hound context.
+ * @param data data buffer.
+ * @param size size of the @p data buffer.
+ * @return error code.
+ *
+ * NOT IMPLEMENTED
+ */
+int hound_write_replace_main_stream(hound_context_t *hound,
+    const void *data, size_t size)
+{
+	assert(hound);
+	if (!data || size == 0)
+		return EBADMEM;
+	// TODO implement
+	return ENOTSUP;
+}
+
+/**
+ * Destroy the old main stream and replace it with a new one using new params.
+ * @param hound Hound context.
+ * @param channels
+ * @return error code.
+ *
+ * NOT IMPLEMENTED
+ */
+int hound_context_set_main_stream_params(hound_context_t *hound,
+    pcm_format_t format, size_t bsize)
+{
+	assert(hound);
+	// TODO implement
+	return ENOTSUP;
+}
+
+/**
+ * Write data immediately to a new stream, and wait for it to drain.
+ * @param hound Hound context.
+ * @param format pcm data format.
+ * @param data data buffer
+ * @param size @p data buffer size
+ * @return Error code.
+ *
+ * This functnion creates a new stream writes the data, ti waits for the stream
+ * to drain and destroys it before returning.
+ */
+int hound_write_immediate(hound_context_t *hound, pcm_format_t format,
+    const void *data, size_t size)
+{
+	assert(hound);
+	if (hound->record)
+		return EINVAL;
+	hound_stream_t *tmpstream = hound_stream_create(hound, 0, format, size);
+	if (!tmpstream)
+		return ENOMEM;
+	const int ret = hound_stream_write(tmpstream, data, size);
+	if (ret == EOK) {
+		//TODO drain?
+		hound_service_stream_drain(tmpstream->exch);
+	}
+	hound_stream_destroy(tmpstream);
+	return ret;
+}
+/**
+ * @}
+ */
Index: uspace/lib/hound/src/protocol.c
===================================================================
--- uspace/lib/hound/src/protocol.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/hound/src/protocol.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,781 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 libhound
+ * @addtogroup audio
+ * @{
+ */
+/** @file
+ * Common USB functions.
+ */
+#include <adt/list.h>
+#include <errno.h>
+#include <loc.h>
+#include <macros.h>
+#include <str.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <libarch/types.h>
+
+#include "protocol.h"
+#include "client.h"
+#include "server.h"
+
+enum ipc_methods {
+	/** Create new context representation on the server side */
+	IPC_M_HOUND_CONTEXT_REGISTER = IPC_FIRST_USER_METHOD,
+	/** Release existing context representation on the server side */
+	IPC_M_HOUND_CONTEXT_UNREGISTER,
+	/** Request list of objects */
+	IPC_M_HOUND_GET_LIST,
+	/** Create new connection */
+	IPC_M_HOUND_CONNECT,
+	/** Destroy connection */
+	IPC_M_HOUND_DISCONNECT,
+	/** Switch IPC pipe to stream mode */
+	IPC_M_HOUND_STREAM_ENTER,
+	/** Switch IPC pipe back to general mode */
+	IPC_M_HOUND_STREAM_EXIT,
+	/** Wait until there is no data in the stream */
+	IPC_M_HOUND_STREAM_DRAIN,
+};
+
+
+/** PCM format conversion helper structure */
+typedef union {
+	struct {
+		uint16_t rate;
+		uint8_t channels;
+		uint8_t format;
+	} f __attribute__((packed));
+	sysarg_t arg;
+} format_convert_t;
+
+
+/****
+ * CLIENT
+ ****/
+
+/** Well defined service name */
+const char *HOUND_SERVICE = "audio/hound";
+
+/**
+ * Start a new audio session.
+ * @param service Named service typically 'HOUND_SERVICE' constant.
+ * @return Valid session on success, NULL on failure.
+ */
+hound_sess_t *hound_service_connect(const char *service)
+{
+	service_id_t id = 0;
+	const int ret =
+	    loc_service_get_id(service, &id, IPC_FLAG_BLOCKING);
+	if (ret != EOK)
+		return NULL;
+	return loc_service_connect(EXCHANGE_PARALLEL, id, IPC_FLAG_BLOCKING);
+}
+
+/**
+ * End an existing audio session.
+ * @param sess The session.
+ */
+void hound_service_disconnect(hound_sess_t *sess)
+{
+	if (sess)
+		async_hangup(sess);
+}
+
+/**
+ * Register a named application context to the audio server.
+ * @param sess Valid audio session.
+ * @param name Valid string identifier
+ * @param record True if the application context wishes to receive data.
+ * @return Valid ID on success, Error code on failure.
+ */
+hound_context_id_t hound_service_register_context(hound_sess_t *sess,
+    const char *name, bool record)
+{
+	assert(sess);
+	assert(name);
+	ipc_call_t call;
+	async_exch_t *exch = async_exchange_begin(sess);
+	aid_t mid =
+	    async_send_1(exch, IPC_M_HOUND_CONTEXT_REGISTER, record, &call);
+	int ret = mid ? EOK : EPARTY;
+
+	if (ret == EOK)
+		ret = async_data_write_start(exch, name, str_size(name));
+	else
+		async_forget(mid);
+
+	if (ret == EOK)
+		async_wait_for(mid, (sysarg_t *)&ret);
+
+	async_exchange_end(exch);
+	return ret == EOK ? (hound_context_id_t)IPC_GET_ARG1(call) : ret;
+}
+
+/**
+ * Remove application context from the server's list.
+ * @param sess Valid audio session.
+ * @param id Valid context id.
+ * @return Error code.
+ */
+int hound_service_unregister_context(hound_sess_t *sess, hound_context_id_t id)
+{
+	assert(sess);
+	async_exch_t *exch = async_exchange_begin(sess);
+	const int ret =
+	    async_req_1_0(exch, IPC_M_HOUND_CONTEXT_UNREGISTER, id);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Retrieve a list of server side actors.
+ * @param[in] sess Valid audio session.
+ * @param[out] ids list of string identifiers.
+ * @param[out] count Number of elements int the @p ids list.
+ * @param[in] flags list requirements.
+ * @param[in] connection name of target actor. Used only if the list should
+ *            contain connected actors.
+ * @retval Error code.
+ */
+int hound_service_get_list(hound_sess_t *sess, const char ***ids, size_t *count,
+    int flags, const char *connection)
+{
+	assert(sess);
+	assert(ids);
+	assert(count);
+
+	if (connection && !(flags & HOUND_CONNECTED))
+		return EINVAL;
+
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (!exch)
+		return ENOMEM;
+
+	ipc_call_t res_call;
+	aid_t mid = async_send_3(exch, IPC_M_HOUND_GET_LIST, flags, *count,
+	    (bool)connection, &res_call);
+
+	int ret = EOK;
+	if (mid && connection)
+		ret = async_data_write_start(exch, connection,
+		    str_size(connection));
+
+	if (ret == EOK)
+		async_wait_for(mid, (sysarg_t*)&ret);
+
+	if (ret != EOK) {
+		async_exchange_end(exch);
+		return ret;
+	}
+	unsigned name_count = IPC_GET_ARG1(res_call);
+
+	/* Start receiving names */
+	const char **names = NULL;
+	if (name_count) {
+		size_t *sizes = calloc(name_count, sizeof(size_t));
+		names = calloc(name_count, sizeof(char *));
+		if (!names || !sizes)
+			ret = ENOMEM;
+
+		if (ret == EOK)
+			ret = async_data_read_start(exch, sizes,
+			    name_count * sizeof(size_t));
+		for (unsigned i = 0; i < name_count && ret == EOK; ++i) {
+			char *name = malloc(sizes[i] + 1);
+			if (name) {
+				memset(name, 0, sizes[i] + 1);
+				ret = async_data_read_start(exch, name, sizes[i]);
+				names[i] = name;
+			} else {
+				ret = ENOMEM;
+			}
+		}
+		free(sizes);
+	}
+	async_exchange_end(exch);
+	if (ret != EOK) {
+		for (unsigned i = 0; i < name_count; ++i)
+			free(names[i]);
+		free(names);
+	} else {
+		*ids = names;
+		*count = name_count;
+	}
+	return ret;
+}
+
+/**
+ * Create a new connection between a source and a sink.
+ * @param sess Valid audio session.
+ * @param source Source name, valid string.
+ * @param sink Sink name, valid string.
+ * @return Error code.
+ */
+int hound_service_connect_source_sink(hound_sess_t *sess, const char *source,
+    const char *sink)
+{
+	assert(sess);
+	assert(source);
+	assert(sink);
+
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (!exch)
+		return ENOMEM;
+	ipc_call_t call;
+	aid_t id = async_send_0(exch, IPC_M_HOUND_CONNECT, &call);
+	int ret = id ? EOK : EPARTY;
+	if (ret == EOK)
+		ret = async_data_write_start(exch, source, str_size(source));
+	if (ret == EOK)
+		ret = async_data_write_start(exch, sink, str_size(sink));
+	async_wait_for(id, (sysarg_t*)&ret);
+	async_exchange_end(exch);
+	return ret;
+}
+
+/**
+ * Destroy an existing connection between a source and a sink.
+ * @param sess Valid audio session.
+ * @param source Source name, valid string.
+ * @param sink Sink name, valid string.
+ * @return Error code.
+ */
+int hound_service_disconnect_source_sink(hound_sess_t *sess, const char *source,
+    const char *sink)
+{
+	assert(sess);
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (!exch)
+		return ENOMEM;
+	ipc_call_t call;
+	aid_t id = async_send_0(exch, IPC_M_HOUND_DISCONNECT, &call);
+	int ret = id ? EOK : EPARTY;
+	if (ret == EOK)
+		ret = async_data_write_start(exch, source, str_size(source));
+	if (ret == EOK)
+		ret = async_data_write_start(exch, sink, str_size(sink));
+	async_wait_for(id, (sysarg_t*)&ret);
+	async_exchange_end(exch);
+	return ENOTSUP;
+}
+
+/**
+ * Switch IPC exchange to a STREAM mode.
+ * @param exch IPC exchange.
+ * @param id context id this stream should be associated with
+ * @param flags set stream properties
+ * @param format format of the new stream.
+ * @param bsize size of the server side buffer.
+ * @return Error code.
+ */
+int hound_service_stream_enter(async_exch_t *exch, hound_context_id_t id,
+    int flags, pcm_format_t format, size_t bsize)
+{
+	const format_convert_t c = { .f = {
+		.channels = format.channels,
+		.rate = format.sampling_rate / 100,
+		.format = format.sample_format,
+	}};
+	return async_req_4_0(exch, IPC_M_HOUND_STREAM_ENTER, id, flags,
+	    c.arg, bsize);
+}
+
+/**
+ * Destroy existing stream and return IPC exchange to general mode.
+ * @param exch IPC exchange.
+ * @return Error code.
+ */
+int hound_service_stream_exit(async_exch_t *exch)
+{
+	return async_req_0_0(exch, IPC_M_HOUND_STREAM_EXIT);
+}
+
+/**
+ * Wait until the server side buffer is empty.
+ * @param exch IPC exchange.
+ * @return Error code.
+ */
+int hound_service_stream_drain(async_exch_t *exch)
+{
+	return async_req_0_0(exch, IPC_M_HOUND_STREAM_DRAIN);
+}
+
+/**
+ * Write audio data to a stream.
+ * @param exch IPC exchange in STREAM MODE.
+ * @param data Audio data buffer.
+ * @size size of the buffer
+ * @return Error code.
+ */
+int hound_service_stream_write(async_exch_t *exch, const void *data, size_t size)
+{
+	return async_data_write_start(exch, data, size);
+}
+
+/**
+ * Read data from a stream.
+ * @param exch IPC exchange in STREAM MODE.
+ * @param data Audio data buffer.
+ * @size size of the buffer
+ * @return Error code.
+ */
+int hound_service_stream_read(async_exch_t *exch, void *data, size_t size)
+{
+	return async_data_read_start(exch, data, size);
+}
+
+/****
+ * SERVER
+ ****/
+
+static void hound_server_read_data(void *stream);
+static void hound_server_write_data(void *stream);
+static const hound_server_iface_t *server_iface;
+
+/**
+ * Set hound server interface implementation.
+ * @param iface Initialized Hound server interface.
+ */
+void hound_service_set_server_iface(const hound_server_iface_t *iface)
+{
+	server_iface = iface;
+}
+
+/**
+ * Server side implementation of the hound protocol. IPC connection handler.
+ * @param iid initial call id
+ * @param icall pointer to initial call structure.
+ * @param arg (unused)
+ */
+void hound_connection_handler(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	/* Accept connection if there is a valid iface*/
+	if (server_iface) {
+		async_answer_0(iid, EOK);
+	} else {
+		async_answer_0(iid, ENOTSUP);
+		return;
+	}
+
+	while (1) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		switch (IPC_GET_IMETHOD(call)) {
+		case IPC_M_HOUND_CONTEXT_REGISTER: {
+			/* check interface functions */
+			if (!server_iface || !server_iface->add_context) {
+				async_answer_0(callid, ENOTSUP);
+				break;
+			}
+			bool record = IPC_GET_ARG1(call);
+			void *name;
+
+			/* Get context name */
+			int ret =
+			    async_data_write_accept(&name, true, 0, 0, 0, 0);
+			if (ret != EOK) {
+				async_answer_0(callid, ret);
+				break;
+			}
+			hound_context_id_t id = 0;
+			ret = server_iface->add_context(server_iface->server,
+			    &id, name, record);
+			/** new context should create a copy */
+			free(name);
+			if (ret != EOK) {
+				async_answer_0(callid, ret);
+			} else {
+				async_answer_1(callid, EOK, id);
+			}
+			break;
+		}
+		case IPC_M_HOUND_CONTEXT_UNREGISTER: {
+			/* check interface functions */
+			if (!server_iface || !server_iface->rem_context) {
+				async_answer_0(callid, ENOTSUP);
+				break;
+			}
+
+			/* get id, 1st param */
+			hound_context_id_t id = IPC_GET_ARG1(call);
+			const int ret =
+			    server_iface->rem_context(server_iface->server, id);
+			async_answer_0(callid, ret);
+			break;
+		}
+		case IPC_M_HOUND_GET_LIST: {
+			/* check interface functions */
+			if (!server_iface || !server_iface->get_list) {
+				async_answer_0(callid, ENOTSUP);
+				break;
+			}
+
+			const char **list = NULL;
+			const int flags = IPC_GET_ARG1(call);
+			size_t count = IPC_GET_ARG2(call);
+			const bool conn = IPC_GET_ARG3(call);
+			char *conn_name = NULL;
+			int ret = EOK;
+
+			/* get connected actor name if provided */
+			if (conn)
+				ret = async_data_write_accept(
+				    (void**)&conn_name, true, 0, 0, 0, 0);
+
+			if (ret == EOK)
+				ret = server_iface->get_list(
+				    server_iface->server, &list, &count,
+				    conn_name, flags);
+			free(conn_name);
+
+			/* Alloc string sizes array */
+			size_t *sizes = NULL;
+			if (count)
+				sizes = calloc(count, sizeof(size_t));
+			if (count && !sizes)
+				ret = ENOMEM;
+			async_answer_1(callid, ret, count);
+
+			/* We are done */
+			if (count == 0 || ret != EOK)
+				break;
+
+			/* Prepare sizes table */
+			for (unsigned i = 0; i < count; ++i)
+				sizes[i] = str_size(list[i]);
+
+			/* Send sizes table */
+			ipc_callid_t id;
+			if (async_data_read_receive(&id, NULL)) {
+				ret = async_data_read_finalize(id, sizes,
+				    count * sizeof(size_t));
+			}
+			free(sizes);
+
+			/* Proceed to send names */
+			for (unsigned i = 0; i < count; ++i) {
+				size_t size = str_size(list[i]);
+				ipc_callid_t id;
+				if (ret == EOK &&
+				    async_data_read_receive(&id, NULL)) {
+					ret = async_data_read_finalize(id,
+					    list[i], size);
+				}
+				free(list[i]);
+			}
+			free(list);
+			break;
+		}
+		case IPC_M_HOUND_CONNECT: {
+			/* check interface functions */
+			if (!server_iface || !server_iface->connect) {
+				async_answer_0(callid, ENOTSUP);
+				break;
+			}
+
+			void *source = NULL;
+			void *sink = NULL;
+
+			/* read source name */
+			int ret =
+			    async_data_write_accept(&source, true, 0, 0, 0, 0);
+			/* read sink name */
+			if (ret == EOK)
+				ret = async_data_write_accept(&sink,
+				    true, 0, 0, 0, 0);
+
+			if (ret == EOK)
+				ret = server_iface->connect(
+				    server_iface->server, source, sink);
+			free(source);
+			free(sink);
+			async_answer_0(callid, ret);
+			break;
+		}
+		case IPC_M_HOUND_DISCONNECT: {
+			/* check interface functions */
+			if (!server_iface || !server_iface->disconnect) {
+				async_answer_0(callid, ENOTSUP);
+				break;
+			}
+
+			void *source = NULL;
+			void *sink = NULL;
+
+			/* read source name */
+			int ret =
+			    async_data_write_accept(&source, true, 0, 0, 0, 0);
+			/*read sink name */
+			if (ret == EOK)
+				ret = async_data_write_accept(&sink,
+				    true, 0, 0, 0, 0);
+			if (ret == EOK)
+				ret = server_iface->connect(
+				    server_iface->server, source, sink);
+			free(source);
+			free(sink);
+			async_answer_0(callid, ret);
+			break;
+		}
+		case IPC_M_HOUND_STREAM_ENTER: {
+			/* check interface functions */
+			if (!server_iface || !server_iface->is_record_context
+			    || !server_iface->add_stream
+			    || !server_iface->rem_stream) {
+				async_answer_0(callid, ENOTSUP);
+				break;
+			}
+
+			/* get parameters */
+			hound_context_id_t id = IPC_GET_ARG1(call);
+			const int flags = IPC_GET_ARG2(call);
+			const format_convert_t c = {.arg = IPC_GET_ARG3(call)};
+			const pcm_format_t f = {
+			    .sampling_rate = c.f.rate * 100,
+			    .channels = c.f.channels,
+			    .sample_format = c.f.format,
+			};
+			size_t bsize = IPC_GET_ARG4(call);
+
+			void *stream;
+			int ret = server_iface->add_stream(server_iface->server,
+			    id, flags, f, bsize, &stream);
+			if (ret != EOK) {
+				async_answer_0(callid, ret);
+				break;
+			}
+			const bool rec = server_iface->is_record_context(
+			    server_iface->server, id);
+			if (rec) {
+				if(server_iface->stream_data_read) {
+					async_answer_0(callid, EOK);
+					/* start answering read calls */
+					hound_server_write_data(stream);
+					server_iface->rem_stream(
+					    server_iface->server, stream);
+				} else {
+					async_answer_0(callid, ENOTSUP);
+				}
+			} else {
+				if (server_iface->stream_data_write) {
+					async_answer_0(callid, EOK);
+					/* accept write calls */
+					hound_server_read_data(stream);
+					server_iface->rem_stream(
+					    server_iface->server, stream);
+				} else {
+					async_answer_0(callid, ENOTSUP);
+				}
+			}
+			break;
+		}
+		case IPC_M_HOUND_STREAM_EXIT:
+		case IPC_M_HOUND_STREAM_DRAIN:
+			/* Stream exit/drain is only allowed in stream context*/
+			async_answer_0(callid, EINVAL);
+			break;
+		default:
+			async_answer_0(callid, ENOTSUP);
+			return;
+		}
+	}
+}
+
+/**
+ * Read data and push it to the stream.
+ * @param stream target stream, will push data there.
+ */
+static void hound_server_read_data(void *stream)
+{
+	ipc_callid_t callid;
+	ipc_call_t call;
+	size_t size = 0;
+	int ret_answer = EOK;
+	/* accept data write or drain */
+	while (async_data_write_receive_call(&callid, &call, &size)
+	    || (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) {
+		/* check drain first */
+		if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) {
+			int ret = ENOTSUP;
+			if (server_iface->drain_stream)
+				ret = server_iface->drain_stream(stream);
+			async_answer_0(callid, ret);
+			continue;
+		}
+
+		/* there was an error last time */
+		if (ret_answer != EOK) {
+			async_answer_0(callid, ret_answer);
+			continue;
+		}
+
+		char *buffer = malloc(size);
+		if (!buffer) {
+			async_answer_0(callid, ENOMEM);
+			continue;
+		}
+		const int ret = async_data_write_finalize(callid, buffer, size);
+		if (ret == EOK) {
+			/* push data to stream */
+			ret_answer = server_iface->stream_data_write(
+			    stream, buffer, size);
+		}
+	}
+	const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT
+	    ? EOK : EINVAL;
+
+	async_answer_0(callid, ret);
+}
+
+/**
+ * Accept reads and pull data from the stream.
+ * @param stream target stream, will pull data from there.
+ */
+static void hound_server_write_data(void *stream)
+{
+
+	ipc_callid_t callid;
+	ipc_call_t call;
+	size_t size = 0;
+	int ret_answer = EOK;
+	/* accept data read and drain */
+	while (async_data_read_receive_call(&callid, &call, &size)
+	    || (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN)) {
+		/* drain does not make much sense but it is allowed */
+		if (IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_DRAIN) {
+			int ret = ENOTSUP;
+			if (server_iface->drain_stream)
+				ret = server_iface->drain_stream(stream);
+			async_answer_0(callid, ret);
+			continue;
+		}
+		/* there was an error last time */
+		if (ret_answer != EOK) {
+			async_answer_0(callid, ret_answer);
+			continue;
+		}
+		char *buffer = malloc(size);
+		if (!buffer) {
+			async_answer_0(callid, ENOMEM);
+			continue;
+		}
+		int ret = server_iface->stream_data_read(stream, buffer, size);
+		if (ret == EOK) {
+			ret_answer =
+			    async_data_read_finalize(callid, buffer, size);
+		}
+	}
+	const int ret = IPC_GET_IMETHOD(call) == IPC_M_HOUND_STREAM_EXIT
+	    ? EOK : EINVAL;
+
+	async_answer_0(callid, ret);
+}
+
+
+/***
+ * SERVER SIDE
+ ***/
+
+/**
+ * Register new hound service to the location service.
+ * @param[in] name server name
+ * @param[out] id assigned service id.
+ * @return Error code.
+ */
+int hound_server_register(const char *name, service_id_t *id)
+{
+	if (!name || !id)
+		return EINVAL;
+
+	int ret = loc_server_register(name);
+	if (ret != EOK)
+		return ret;
+
+	return loc_service_register(HOUND_SERVICE, id);
+}
+
+/**
+ * Unregister server from the location service.
+ * @param id previously assigned service id.
+ */
+void hound_server_unregister(service_id_t id)
+{
+	loc_service_unregister(id);
+}
+
+/**
+ * Set callback on device category change event.
+ * @param cb Callback function.
+ * @return Error code.
+ */
+int hound_server_set_device_change_callback(dev_change_callback_t cb)
+{
+	return loc_register_cat_change_cb(cb);
+}
+
+/**
+ * Walk through all device in the audio-pcm category.
+ * @param callback Function to call on every device.
+ * @return Error code.
+ */
+int hound_server_devices_iterate(device_callback_t callback)
+{
+	if (!callback)
+		return EINVAL;
+	static bool resolved = false;
+	static category_id_t cat_id = 0;
+
+	if (!resolved) {
+		const int ret = loc_category_get_id("audio-pcm", &cat_id,
+		    IPC_FLAG_BLOCKING);
+		if (ret != EOK)
+			return ret;
+		resolved = true;
+	}
+
+	service_id_t *svcs = NULL;
+	size_t count = 0;
+	const int ret = loc_category_get_svcs(cat_id, &svcs, &count);
+	if (ret != EOK)
+		return ret;
+
+	for (unsigned i = 0; i < count; ++i) {
+		char *name = NULL;
+		loc_service_get_name(svcs[i], &name);
+		callback(svcs[i], name);
+		free(name);
+	}
+	free(svcs);
+	return EOK;
+}
+/**
+ * @}
+ */
Index: uspace/lib/http/Makefile
===================================================================
--- uspace/lib/http/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/http/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,42 @@
+#
+# Copyright (c) 2013 Martin Sucha
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+LIBRARY = libhttp
+SLIBRARY = libhttp.so.0.0
+LSONAME = libhttp.so0
+EXTRA_CFLAGS += -Iinclude
+
+SOURCES = \
+	src/http.c \
+	src/headers.c \
+	src/request.c \
+	src/response.c \
+	src/receive-buffer.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/lib/http/include/http/ctype.h
===================================================================
--- uspace/lib/http/include/http/ctype.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/http/include/http/ctype.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup http
+ * @{
+ */
+/**
+ * @file
+ */
+
+#ifndef HTTP_CTYPE_H_
+#define HTTP_CTYPE_H_
+
+static inline bool is_separator(char c)
+{
+	switch (c) {
+	case '(':
+	case ')':
+	case '<':
+	case '>':
+	case '@':
+	case ',':
+	case ';':
+	case ':':
+	case '\\':
+	case '"':
+	case '/':
+	case '[':
+	case ']':
+	case '?':
+	case '=':
+	case '{':
+	case '}':
+	case ' ':
+	case '\t':
+		return true;
+	default:
+		return false;
+	}
+}
+
+static inline bool is_lws(char c)
+{
+	switch (c) {
+	case '\r':
+	case '\n':
+	case ' ':
+	case '\t':
+		return true;
+	default:
+		return false;
+	}
+}
+
+static inline bool is_ctl(char c)
+{
+	return c < 32 || c == 127;
+}
+
+static inline bool is_token(char c)
+{
+	return !is_separator(c) && !is_ctl(c);
+}
+
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/http/include/http/errno.h
===================================================================
--- uspace/lib/http/include/http/errno.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/http/include/http/errno.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup http
+ * @{
+ */
+/**
+ * @file
+ */
+
+#ifndef HTTP_ERRNO_H_
+#define HTTP_ERRNO_H_
+
+#include <errno.h>
+
+#define HTTP_EMULTIPLE_HEADERS -20001
+#define HTTP_EMISSING_HEADER -20002
+#define HTTP_EPARSE -20003
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/http/include/http/http.h
===================================================================
--- uspace/lib/http/include/http/http.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/http/include/http/http.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup http
+ * @{
+ */
+/**
+ * @file
+ */
+
+#ifndef HTTP_HTTP_H_
+#define HTTP_HTTP_H_
+
+#include <net/socket.h>
+#include <adt/list.h>
+#include <inet/addr.h>
+
+#include "receive-buffer.h"
+
+typedef struct {
+	char *host;
+	uint16_t port;
+	inet_addr_t addr;
+
+	bool connected;
+	int conn_sd;
+	
+	size_t buffer_size;
+	receive_buffer_t recv_buffer;
+} http_t;
+
+typedef struct {
+	uint8_t minor;
+	uint8_t major;
+} http_version_t;
+
+typedef struct {
+	link_t link;
+	char *name;
+	char *value;
+} http_header_t;
+
+typedef struct {
+	list_t list;
+} http_headers_t;
+
+typedef struct {
+	char *method;
+	char *path;
+	http_headers_t headers;
+} http_request_t;
+
+typedef struct {
+	http_version_t version;
+	uint16_t status;
+	char *message;
+	http_headers_t headers;
+} http_response_t;
+
+extern http_t *http_create(const char *, uint16_t);
+extern int http_connect(http_t *);
+
+extern void http_header_init(http_header_t *);
+extern http_header_t *http_header_create(const char *, const char *);
+extern int http_header_receive_name(receive_buffer_t *, receive_buffer_mark_t *);
+extern int http_header_receive_value(receive_buffer_t *, receive_buffer_mark_t *,
+    receive_buffer_mark_t *);
+extern int http_header_receive(receive_buffer_t *, http_header_t *, size_t,
+    size_t *);
+extern void http_header_normalize_value(char *);
+extern bool http_header_name_match(const char *, const char *);
+ssize_t http_header_encode(http_header_t *, char *, size_t);
+extern void http_header_destroy(http_header_t *);
+
+extern void http_headers_init(http_headers_t *);
+extern int http_headers_find_single(http_headers_t *, const char *,
+    http_header_t **);
+extern int http_headers_append(http_headers_t *, const char *, const char *);
+extern int http_headers_set(http_headers_t *, const char *, const char *);
+extern int http_headers_get(http_headers_t *, const char *, char **);
+extern int http_headers_receive(receive_buffer_t *, http_headers_t *, size_t,
+    unsigned);
+extern void http_headers_clear(http_headers_t *);
+
+#define http_headers_foreach(headers, iter) \
+    list_foreach((headers).list, link, http_header_t, (iter))
+
+static inline void http_headers_remove(http_headers_t *headers,
+    http_header_t *header)
+{
+	list_remove(&header->link);
+}
+
+static inline void http_headers_append_header(http_headers_t *headers,
+    http_header_t *header)
+{
+	list_append(&header->link, &headers->list);
+}
+
+extern http_request_t *http_request_create(const char *, const char *);
+extern void http_request_destroy(http_request_t *);
+extern int http_request_format(http_request_t *, char **, size_t *);
+extern int http_send_request(http_t *, http_request_t *);
+extern int http_receive_status(receive_buffer_t *, http_version_t *, uint16_t *,
+    char **);
+extern int http_receive_response(receive_buffer_t *, http_response_t **,
+    size_t, unsigned);
+extern void http_response_destroy(http_response_t *);
+extern int http_close(http_t *);
+extern void http_destroy(http_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/http/include/http/receive-buffer.h
===================================================================
--- uspace/lib/http/include/http/receive-buffer.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/http/include/http/receive-buffer.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup http
+ * @{
+ */
+/**
+ * @file
+ */
+
+#ifndef HTTP_RECEIVE_BUFFER_H_
+#define HTTP_RECEIVE_BUFFER_H_
+
+#include <adt/list.h>
+
+/** Receive data.
+ *
+ * @param client_data client data
+ * @param buf buffer to store the data
+ * @param buf_size buffer size
+ * @return number of bytes received or negative error code
+ */
+typedef ssize_t (*receive_func_t)(void *, void *, size_t);
+
+typedef struct {
+	size_t size;
+	char *buffer;
+	size_t in;
+	size_t out;
+	
+	void *client_data;
+	receive_func_t receive;
+	
+	list_t marks;
+} receive_buffer_t;
+
+typedef struct {
+	link_t link;
+	size_t offset;
+} receive_buffer_mark_t;
+
+typedef bool (*char_class_func_t)(char);
+
+extern int recv_buffer_init(receive_buffer_t *, size_t, receive_func_t, void *);
+extern int recv_buffer_init_const(receive_buffer_t *, void *, size_t);
+extern void recv_buffer_fini(receive_buffer_t *);
+extern void recv_reset(receive_buffer_t *);
+extern void recv_mark(receive_buffer_t *, receive_buffer_mark_t *);
+extern void recv_unmark(receive_buffer_t *, receive_buffer_mark_t *);
+extern void recv_mark_update(receive_buffer_t *, receive_buffer_mark_t *);
+extern int recv_cut(receive_buffer_t *, receive_buffer_mark_t *,
+    receive_buffer_mark_t *, void **, size_t *);
+extern int recv_cut_str(receive_buffer_t *, receive_buffer_mark_t *,
+    receive_buffer_mark_t *, char **);
+extern int recv_char(receive_buffer_t *, char *, bool);
+extern ssize_t recv_buffer(receive_buffer_t *, char *, size_t);
+extern ssize_t recv_discard(receive_buffer_t *, char);
+extern ssize_t recv_discard_str(receive_buffer_t *, const char *);
+extern ssize_t recv_while(receive_buffer_t *, char_class_func_t);
+extern ssize_t recv_eol(receive_buffer_t *);
+extern ssize_t recv_line(receive_buffer_t *, char *, size_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/http/src/headers.c
===================================================================
--- uspace/lib/http/src/headers.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/http/src/headers.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup http
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <macros.h>
+
+#include <http/http.h>
+#include <http/ctype.h>
+#include <http/errno.h>
+
+#define HTTP_HEADER_LINE "%s: %s\r\n"
+
+void http_header_init(http_header_t *header)
+{
+	link_initialize(&header->link);
+	header->name = NULL;
+	header->value = NULL;
+}
+
+http_header_t *http_header_create(const char *name, const char *value)
+{
+	http_header_t *header = malloc(sizeof(http_header_t));
+	if (header == NULL)
+		return NULL;
+	http_header_init(header);
+
+	header->name = str_dup(name);
+	if (header->name == NULL) {
+		free(header);
+		return NULL;
+	}
+	
+	header->value = str_dup(value);
+	if (header->value == NULL) {
+		free(header->name);
+		free(header);
+		return NULL;
+	}
+
+	return header;
+}
+
+void http_header_destroy(http_header_t *header)
+{
+	free(header->name);
+	free(header->value);
+	free(header);
+}
+
+ssize_t http_header_encode(http_header_t *header, char *buf, size_t buf_size)
+{
+	/* TODO properly split long header values */
+	if (buf == NULL) {
+		return printf_size(HTTP_HEADER_LINE, header->name, header->value);
+	}
+	else {
+		return snprintf(buf, buf_size,
+		    HTTP_HEADER_LINE, header->name, header->value);
+	}
+}
+
+int http_header_receive_name(receive_buffer_t *rb,
+    receive_buffer_mark_t *name_end)
+{
+	char c = 0;
+	do {
+		if (name_end)
+			recv_mark_update(rb, name_end);
+		
+		int rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			return rc;
+	} while (is_token(c));
+	
+	if (c != ':')
+		return EINVAL;
+	
+	return EOK;
+}
+
+int http_header_receive_value(receive_buffer_t *rb,
+    receive_buffer_mark_t *value_start, receive_buffer_mark_t *value_end)
+{
+	int rc = EOK;
+	char c = 0;
+	
+	/* Ignore any inline LWS */
+	while (true) {
+		if (value_start)
+			recv_mark_update(rb, value_start);
+		
+		rc = recv_char(rb, &c, false);
+		if (rc != EOK)
+			return rc;
+		
+		if (c != ' ' && c != '\t')
+			break;
+		
+		rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			return rc;
+	}
+	
+	while (true) {
+		recv_mark_update(rb, value_end);
+		
+		rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			return rc;
+		
+		if (c != '\r' && c != '\n')
+			continue;
+		
+		rc = recv_discard(rb, (c == '\r' ? '\n' : '\r'));
+		if (rc < 0)
+			return rc;
+		
+		rc = recv_char(rb, &c, false);
+		if (rc != EOK)
+			return rc;
+		
+		if (c != ' ' && c != '\t')
+			break;
+		
+		/* Ignore the char */
+		rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			return rc;
+	}
+	
+	return EOK;
+}
+
+int http_header_receive(receive_buffer_t *rb, http_header_t *header,
+    size_t size_limit, size_t *out_bytes_used)
+{
+	receive_buffer_mark_t mark_start;
+	receive_buffer_mark_t mark_end;
+	
+	recv_mark(rb, &mark_start);
+	recv_mark(rb, &mark_end);
+	
+	int rc = http_header_receive_name(rb, &mark_end);
+	if (rc != EOK)
+		goto end;
+	
+	size_t name_size = mark_end.offset - mark_start.offset;
+	if (size_limit > 0 && name_size > size_limit) {
+		rc = ELIMIT;
+		goto end;
+	}
+	
+	char *name = NULL;
+	rc = recv_cut_str(rb, &mark_start, &mark_end, &name);
+	if (rc != EOK)
+		goto end;
+	
+	rc = http_header_receive_value(rb, &mark_start, &mark_end);
+	if (rc != EOK)
+		goto end_with_name;
+	
+	size_t value_size = mark_end.offset - mark_start.offset;
+	if (size_limit > 0 && (name_size + value_size) > size_limit) {
+		rc = ELIMIT;
+		goto end_with_name;
+	}
+	
+	char *value = NULL;
+	rc = recv_cut_str(rb, &mark_start, &mark_end, &value);
+	if (rc != EOK)
+		goto end_with_name;
+	
+	if (out_bytes_used)
+		*out_bytes_used = name_size + value_size;
+	
+	header->name = name;
+	header->value = value;
+	goto end;
+end_with_name:
+	free(name);
+end:
+	recv_unmark(rb, &mark_start);
+	recv_unmark(rb, &mark_end);
+	return rc;
+}
+
+/** Normalize HTTP header value
+ *
+ * @see RFC2616 section 4.2
+ */
+void http_header_normalize_value(char *value)
+{
+	size_t read_index = 0;
+	size_t write_index = 0;
+	
+	while (is_lws(value[read_index])) read_index++;
+	
+	while (value[read_index] != 0) {
+		if (is_lws(value[read_index])) {
+			while (is_lws(value[read_index])) read_index++;
+			
+			if (value[read_index] != 0)
+				value[write_index++] = ' ';
+			
+			continue;
+		}
+		
+		value[write_index++] = value[read_index++];
+	}
+	
+	value[write_index] = 0;
+}
+
+/** Test if two header names are equivalent
+ *
+ */
+bool http_header_name_match(const char *name_a, const char *name_b)
+{
+	return stricmp(name_a, name_b) == 0;
+}
+
+void http_headers_init(http_headers_t *headers) {
+	list_initialize(&headers->list);
+}
+
+int http_headers_find_single(http_headers_t *headers, const char *name,
+    http_header_t **out_header)
+{
+	http_header_t *found = NULL;
+	http_headers_foreach(*headers, header) {
+		if (!http_header_name_match(header->name, name))
+			continue;
+		
+		if (found == NULL) {
+			found = header;
+		}
+		else {
+			return HTTP_EMULTIPLE_HEADERS;
+		}
+	}
+	
+	if (found == NULL)
+		return HTTP_EMISSING_HEADER;
+	
+	*out_header = found;
+	return EOK;
+}
+
+int http_headers_append(http_headers_t *headers, const char *name,
+    const char *value)
+{
+	http_header_t *header = http_header_create(name, value);
+	if (header == NULL)
+		return ENOMEM;
+	
+	http_headers_append_header(headers, header);
+	return EOK;
+}
+
+int http_headers_set(http_headers_t *headers, const char *name,
+    const char *value)
+{
+	http_header_t *header = NULL;
+	int rc = http_headers_find_single(headers, name, &header);
+	if (rc != EOK && rc != HTTP_EMISSING_HEADER)
+		return rc;
+	
+	if (rc == HTTP_EMISSING_HEADER)
+		return http_headers_append(headers, name, value);
+	
+	char *new_value = str_dup(value);
+	if (new_value == NULL)
+		return ENOMEM;
+	
+	free(header->value);
+	header->value = new_value;
+	return EOK;
+}
+
+int http_headers_get(http_headers_t *headers, const char *name, char **value)
+{
+	http_header_t *header = NULL;
+	int rc = http_headers_find_single(headers, name, &header);
+	if (rc != EOK)
+		return rc;
+	
+	*value = header->value;
+	return EOK;
+}
+
+int http_headers_receive(receive_buffer_t *rb, http_headers_t *headers,
+    size_t limit_alloc, unsigned limit_count)
+{
+	int rc = EOK;
+	unsigned added = 0;
+	
+	while (true) {
+		char c = 0;
+		rc = recv_char(rb, &c, false);
+		if (rc != EOK)
+			goto error;
+		
+		if (c == '\n' || c == '\r')
+			break;
+		
+		if (limit_count > 0 && added >= limit_count) {
+			rc = ELIMIT;
+			goto error;
+		}
+		
+		http_header_t *header = malloc(sizeof(http_header_t));
+		if (header == NULL) {
+			rc = ENOMEM;
+			goto error;
+		}
+		http_header_init(header);
+		
+		size_t header_size;
+		rc = http_header_receive(rb, header, limit_alloc, &header_size);
+		if (rc != EOK) {
+			free(header);
+			goto error;
+		}
+		limit_alloc -= header_size;
+		
+		http_headers_append_header(headers, header);
+		added++;
+	}
+	
+	return EOK;
+error:
+	while (added-- > 0) {
+		link_t *link = list_last(&headers->list);
+		http_header_t *header = list_get_instance(link, http_header_t, link);
+		http_headers_remove(headers, header);
+		http_header_destroy(header);
+	}
+	return rc;
+}
+
+void http_headers_clear(http_headers_t *headers)
+{
+	link_t *link = list_first(&headers->list);
+	while (link != NULL) {
+		link_t *next = list_next(link, &headers->list);
+		http_header_t *header = list_get_instance(link, http_header_t, link);
+		list_remove(link);
+		http_header_destroy(header);
+		link = next;
+	}
+}
+
+/** @}
+ */
Index: uspace/lib/http/src/http.c
===================================================================
--- uspace/lib/http/src/http.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/http/src/http.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup http
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <macros.h>
+
+#include <net/socket.h>
+#include <inet/dnsr.h>
+
+#include <http/http.h>
+#include <http/receive-buffer.h>
+
+static ssize_t http_receive(void *client_data, void *buf, size_t buf_size)
+{
+	http_t *http = client_data;
+	return recv(http->conn_sd, buf, buf_size, 0);
+}
+
+http_t *http_create(const char *host, uint16_t port)
+{
+	http_t *http = malloc(sizeof(http_t));
+	if (http == NULL)
+		return NULL;
+	
+	http->host = str_dup(host);
+	if (http->host == NULL) {
+		free(http);
+		return NULL;
+	}
+	http->port = port;
+	
+	http->buffer_size = 4096;
+	int rc = recv_buffer_init(&http->recv_buffer, http->buffer_size,
+	    http_receive, http);
+	if (rc != EOK) {
+		free(http);
+		return NULL;
+	}
+	
+	return http;
+}
+
+int http_connect(http_t *http)
+{
+	if (http->connected)
+		return EBUSY;
+	
+	/* Interpret as address */
+	int rc = inet_addr_parse(http->host, &http->addr);
+	
+	if (rc != EOK) {
+		/* Interpret as a host name */
+		dnsr_hostinfo_t *hinfo = NULL;
+		rc = dnsr_name2host(http->host, &hinfo, ip_any);
+		
+		if (rc != EOK)
+			return rc;
+		
+		http->addr = hinfo->addr;
+		dnsr_hostinfo_destroy(hinfo);
+	}
+	
+	struct sockaddr *saddr;
+	socklen_t saddrlen;
+	
+	rc = inet_addr_sockaddr(&http->addr, http->port, &saddr, &saddrlen);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		return ENOMEM;
+	}
+	
+	http->conn_sd = socket(saddr->sa_family, SOCK_STREAM, 0);
+	if (http->conn_sd < 0)
+		return http->conn_sd;
+	
+	rc = connect(http->conn_sd, saddr, saddrlen);
+	free(saddr);
+	
+	return rc;
+}
+
+int http_close(http_t *http)
+{
+	if (!http->connected)
+		return EINVAL;
+	
+	return closesocket(http->conn_sd);
+}
+
+void http_destroy(http_t *http)
+{
+	recv_buffer_fini(&http->recv_buffer);
+	free(http);
+}
+
+/** @}
+ */
Index: uspace/lib/http/src/receive-buffer.c
===================================================================
--- uspace/lib/http/src/receive-buffer.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/http/src/receive-buffer.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup http
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdlib.h>
+#include <str.h>
+#include <errno.h>
+#include <macros.h>
+#include <adt/list.h>
+
+#include <http/receive-buffer.h>
+
+int recv_buffer_init(receive_buffer_t *rb, size_t buffer_size,
+    receive_func_t receive, void *client_data)
+{
+	rb->receive = receive;
+	rb->client_data = client_data;
+	
+	rb->in = 0;
+	rb->out = 0;
+	rb->size = buffer_size;
+	
+	list_initialize(&rb->marks);
+	
+	rb->buffer = malloc(buffer_size);
+	if (rb->buffer == NULL)
+		return ENOMEM;
+	return EOK;
+}
+
+static ssize_t dummy_receive(void *unused, void *buf, size_t buf_size)
+{
+	return 0;
+}
+
+int recv_buffer_init_const(receive_buffer_t *rb, void *buf, size_t size)
+{
+	int rc = recv_buffer_init(rb, size, dummy_receive, NULL);
+	if (rc != EOK)
+		return rc;
+	
+	memcpy(rb->buffer, buf, size);
+	rb->in = size;
+	return EOK;
+}
+
+void recv_buffer_fini(receive_buffer_t *rb)
+{
+	free(rb->buffer);
+}
+
+void recv_reset(receive_buffer_t *rb)
+{
+	rb->in = 0;
+	rb->out = 0;
+}
+
+void recv_mark(receive_buffer_t *rb, receive_buffer_mark_t *mark)
+{
+	link_initialize(&mark->link);
+	list_append(&mark->link, &rb->marks);
+	recv_mark_update(rb, mark);
+}
+
+void recv_unmark(receive_buffer_t *rb, receive_buffer_mark_t *mark)
+{
+	list_remove(&mark->link);
+}
+
+void recv_mark_update(receive_buffer_t *rb, receive_buffer_mark_t *mark)
+{
+	mark->offset = rb->out;
+}
+
+int recv_cut(receive_buffer_t *rb, receive_buffer_mark_t *a, receive_buffer_mark_t *b, void **out_buf, size_t *out_size)
+{
+	if (a->offset > b->offset)
+		return EINVAL;
+	
+	size_t size = b->offset - a->offset;
+	void *buf = malloc(size);
+	if (buf == NULL)
+		return ENOMEM;
+	
+	memcpy(buf, rb->buffer + a->offset, size);
+	*out_buf = buf;
+	*out_size = size;
+	return EOK;
+}
+
+int recv_cut_str(receive_buffer_t *rb, receive_buffer_mark_t *a, receive_buffer_mark_t *b, char **out_buf)
+{
+	if (a->offset > b->offset)
+		return EINVAL;
+	
+	size_t size = b->offset - a->offset;
+	char *buf = malloc(size + 1);
+	if (buf == NULL)
+		return ENOMEM;
+	
+	memcpy(buf, rb->buffer + a->offset, size);
+	buf[size] = 0;
+	for (size_t i = 0; i < size; i++) {
+		if (buf[i] == 0) {
+			free(buf);
+			return EIO;
+		}
+	}
+	*out_buf = buf;
+	return EOK;
+}
+
+
+/** Receive one character (with buffering) */
+int recv_char(receive_buffer_t *rb, char *c, bool consume)
+{
+	if (rb->out == rb->in) {
+		size_t free = rb->size - rb->in;
+		if (free == 0) {
+			size_t min_mark = rb->size;
+			list_foreach(rb->marks, link, receive_buffer_mark_t, mark) {
+				min_mark = min(min_mark, mark->offset);
+			}
+			
+			if (min_mark == 0)
+				return ELIMIT;
+			
+			size_t new_in = rb->in - min_mark;
+			memmove(rb->buffer, rb->buffer + min_mark, new_in);
+			rb->out = rb->in = new_in;
+			free = rb->size - rb->in;
+			list_foreach(rb->marks, link, receive_buffer_mark_t, mark) {
+				mark->offset -= min_mark;
+			}
+		}
+		
+		ssize_t rc = rb->receive(rb->client_data, rb->buffer + rb->in, free);
+		if (rc <= 0)
+			return rc;
+		
+		rb->in = rc;
+	}
+	
+	*c = rb->buffer[rb->out];
+	if (consume)
+		rb->out++;
+	return EOK;
+}
+
+ssize_t recv_buffer(receive_buffer_t *rb, char *buf, size_t buf_size)
+{
+	/* Flush any buffered data*/
+	if (rb->out != rb->in) {
+		size_t size = min(rb->in - rb->out, buf_size);
+		memcpy(buf, rb->buffer + rb->out, size);
+		rb->out += size;
+		return size;
+	}
+	
+	return rb->receive(rb->client_data, buf, buf_size);
+}
+
+/** Receive a character and if it is c, discard it from input buffer
+ * @return number of characters discarded (0 or 1) or negative error code
+ */
+ssize_t recv_discard(receive_buffer_t *rb, char discard)
+{
+	char c = 0;
+	int rc = recv_char(rb, &c, false);
+	if (rc != EOK)
+		return rc;
+	if (c != discard)
+		return 0;
+	rc = recv_char(rb, &c, true);
+	if (rc != EOK)
+		return rc;
+	return 1;
+}
+
+/** Receive a prefix of constant string discard and return number of bytes read
+ * @return number of characters discarded or negative error code
+ */
+ssize_t recv_discard_str(receive_buffer_t *rb, const char *discard)
+{
+	size_t discarded = 0;
+	while (*discard) {
+		ssize_t rc = recv_discard(rb, *discard);
+		if (rc < 0)
+			return rc;
+		if (rc == 0)
+			break;
+		discarded++;
+		discard++;
+	}
+	return discarded;
+}
+
+ssize_t recv_while(receive_buffer_t *rb, char_class_func_t class)
+{
+	size_t received = 0;
+	
+	while (true) {
+		char c = 0;
+		int rc = recv_char(rb, &c, false);
+		if (rc != EOK)
+			return rc;
+		
+		if (!class(c))
+			break;
+		
+		rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			return rc;
+		
+		received++;
+	}
+	
+	return received;
+}
+
+/** Receive an end of line, either CR, LF, CRLF or LFCR
+ *
+ * @return number of bytes read (0 if no newline is present in the stream)
+ *         or negative error code
+ */
+ssize_t recv_eol(receive_buffer_t *rb)
+{
+	char c = 0;
+	int rc = recv_char(rb, &c, false);
+	if (rc != EOK)
+		return rc;
+	
+	if (c != '\r' && c != '\n')
+		return 0;
+	
+	rc = recv_char(rb, &c, true);
+	if (rc != EOK)
+		return rc;
+	
+	ssize_t rc2 = recv_discard(rb, (c == '\r' ? '\n' : '\r'));
+	if (rc2 < 0)
+		return rc2;
+	
+	return 1 + rc2;
+}
+
+/* Receive a single line */
+ssize_t recv_line(receive_buffer_t *rb, char *line, size_t size)
+{
+	size_t written = 0;
+	
+	while (written < size) {
+		char c = 0;
+		int rc = recv_char(rb, &c, true);
+		if (rc != EOK)
+			return rc;
+		if (c == '\n') {
+			recv_discard(rb, '\r');
+			line[written++] = 0;
+			return written;
+		}
+		else if (c == '\r') {
+			recv_discard(rb, '\n');
+			line[written++] = 0;
+			return written;
+		}
+		line[written++] = c;
+	}
+	
+	return ELIMIT;
+}
+
+/** @}
+ */
Index: uspace/lib/http/src/request.c
===================================================================
--- uspace/lib/http/src/request.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/http/src/request.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup http
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <macros.h>
+
+#include <net/socket.h>
+
+#include <http/http.h>
+
+#define HTTP_METHOD_LINE "%s %s HTTP/1.1\r\n"
+#define HTTP_REQUEST_LINE "\r\n"
+
+http_request_t *http_request_create(const char *method, const char *path)
+{
+	http_request_t *req = malloc(sizeof(http_request_t));
+	if (req == NULL)
+		return NULL;
+	
+	req->method = str_dup(method);
+	if (req->method == NULL) {
+		free(req);
+		return NULL;
+	}
+	
+	req->path = str_dup(path);
+	if (req->path == NULL) {
+		free(req->method);
+		free(req);
+		return NULL;
+	}
+	
+	http_headers_init(&req->headers);
+	
+	return req;
+}
+
+void http_request_destroy(http_request_t *req)
+{
+	free(req->method);
+	free(req->path);
+	http_headers_clear(&req->headers);
+	free(req);
+}
+
+static ssize_t http_encode_method(char *buf, size_t buf_size,
+    const char *method, const char *path)
+{
+	if (buf == NULL) {
+		return printf_size(HTTP_METHOD_LINE, method, path);
+	}
+	else {
+		return snprintf(buf, buf_size, HTTP_METHOD_LINE, method, path);
+	}
+}
+
+int http_request_format(http_request_t *req, char **out_buf,
+    size_t *out_buf_size)
+{
+	/* Compute the size of the request */
+	ssize_t meth_size = http_encode_method(NULL, 0, req->method, req->path);
+	if (meth_size < 0)
+		return meth_size;
+	size_t size = meth_size;
+	
+	http_headers_foreach(req->headers, header) {
+		ssize_t header_size = http_header_encode(header, NULL, 0);
+		if (header_size < 0)
+			return header_size;
+		size += header_size;
+	}
+	size += str_length(HTTP_REQUEST_LINE);
+	
+	char *buf = malloc(size);
+	if (buf == NULL)
+		return ENOMEM;
+	
+	char *pos = buf;
+	size_t pos_size = size;
+	ssize_t written = http_encode_method(pos, pos_size, req->method, req->path);
+	if (written < 0) {
+		free(buf);
+		return written;
+	}
+	pos += written;
+	pos_size -= written;
+	
+	http_headers_foreach(req->headers, header) {
+		written = http_header_encode(header, pos, pos_size);
+		if (written < 0) {
+			free(buf);
+			return written;
+		}
+		pos += written;
+		pos_size -= written;
+	}
+	
+	size_t rlsize = str_size(HTTP_REQUEST_LINE);
+	memcpy(pos, HTTP_REQUEST_LINE, rlsize);
+	pos_size -= rlsize;
+	assert(pos_size == 0);
+	
+	*out_buf = buf;
+	*out_buf_size = size;
+	return EOK;
+}
+
+int http_send_request(http_t *http, http_request_t *req)
+{
+	char *buf;
+	size_t buf_size;
+	
+	int rc = http_request_format(req, &buf, &buf_size);
+	if (rc != EOK)
+		return rc;
+	
+	rc = send(http->conn_sd, buf, buf_size, 0);
+	free(buf);
+	
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/http/src/response.c
===================================================================
--- uspace/lib/http/src/response.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/http/src/response.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup http
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+#include <macros.h>
+
+#include <http/http.h>
+#include <http/errno.h>
+
+static bool is_digit(char c)
+{
+	return (c >= '0' && c <= '9');
+}
+
+static int receive_number(receive_buffer_t *rb, char **str)
+{
+	receive_buffer_mark_t start;
+	receive_buffer_mark_t end;
+	
+	recv_mark(rb, &start);
+	int rc = recv_while(rb, is_digit);
+	if (rc < 0) {
+		recv_unmark(rb, &start);
+		return rc;
+	}
+	recv_mark(rb, &end);
+	
+	rc = recv_cut_str(rb, &start, &end, str);
+	recv_unmark(rb, &start);
+	recv_unmark(rb, &end);
+	return rc;
+}
+
+static int receive_uint8_t(receive_buffer_t *rb, uint8_t *out_value)
+{
+	char *str = NULL;
+	int rc = receive_number(rb, &str);
+	
+	if (rc != EOK)
+		return rc;
+	
+	rc = str_uint8_t(str, NULL, 10, true, out_value);
+	free(str);
+	
+	return rc;
+}
+
+static int receive_uint16_t(receive_buffer_t *rb, uint16_t *out_value)
+{
+	char *str = NULL;
+	int rc = receive_number(rb, &str);
+	
+	if (rc != EOK)
+		return rc;
+	
+	rc = str_uint16_t(str, NULL, 10, true, out_value);
+	free(str);
+	
+	return rc;
+}
+
+static int expect(receive_buffer_t *rb, const char *expect)
+{
+	int rc = recv_discard_str(rb, expect);
+	if (rc < 0)
+		return rc;
+	if ((size_t) rc < str_length(expect))
+		return HTTP_EPARSE;
+	return EOK;
+}
+
+static bool is_not_newline(char c)
+{
+	return (c != '\n' && c != '\r');
+}
+
+int http_receive_status(receive_buffer_t *rb, http_version_t *out_version,
+    uint16_t *out_status, char **out_message)
+{
+	http_version_t version;
+	uint16_t status;
+	char *message = NULL;
+	
+	int rc = expect(rb, "HTTP/");
+	if (rc != EOK)
+		return rc;
+	
+	rc = receive_uint8_t(rb, &version.major);
+	if (rc != EOK)
+		return rc;
+	
+	rc = expect(rb, ".");
+	if (rc != EOK)
+		return rc;
+	
+	rc = receive_uint8_t(rb, &version.minor);
+	if (rc != EOK)
+		return rc;
+	
+	rc = expect(rb, " ");
+	if (rc != EOK)
+		return rc;
+	
+	rc = receive_uint16_t(rb, &status);
+	if (rc != EOK)
+		return rc;
+	
+	rc = expect(rb, " ");
+	if (rc != EOK)
+		return rc;
+	
+	receive_buffer_mark_t msg_start;
+	recv_mark(rb, &msg_start);
+	
+	rc = recv_while(rb, is_not_newline);
+	if (rc < 0) {
+		recv_unmark(rb, &msg_start);
+		return rc;
+	}
+	
+	receive_buffer_mark_t msg_end;
+	recv_mark(rb, &msg_end);
+	
+	if (out_message) {
+		rc = recv_cut_str(rb, &msg_start, &msg_end, &message);
+		if (rc != EOK) {
+			recv_unmark(rb, &msg_start);
+			recv_unmark(rb, &msg_end);
+			return rc;
+		}
+	}
+	
+	recv_unmark(rb, &msg_start);
+	recv_unmark(rb, &msg_end);
+	
+	rc = recv_eol(rb);
+	if (rc == 0)
+		rc = HTTP_EPARSE;
+	if (rc < 0) {
+		free(message);
+		return rc;
+	}
+	
+	if (out_version)
+		*out_version = version;
+	if (out_status)
+		*out_status = status;
+	if (out_message)
+		*out_message = message;
+	return EOK;
+}
+
+int http_receive_response(receive_buffer_t *rb, http_response_t **out_response,
+    size_t max_headers_size, unsigned max_headers_count)
+{
+	http_response_t *resp = malloc(sizeof(http_response_t));
+	if (resp == NULL)
+		return ENOMEM;
+	memset(resp, 0, sizeof(http_response_t));
+	http_headers_init(&resp->headers);
+	
+	int rc = http_receive_status(rb, &resp->version, &resp->status,
+	    &resp->message);
+	if (rc != EOK)
+		goto error;
+	
+	rc = http_headers_receive(rb, &resp->headers, max_headers_size,
+	    max_headers_count);
+	if (rc != EOK)
+		goto error;
+	
+	rc = recv_eol(rb);
+	if (rc == 0)
+		rc = HTTP_EPARSE;
+	if (rc < 0)
+		goto error;
+	
+	*out_response = resp;
+	
+	return EOK;
+error:
+	http_response_destroy(resp);
+	return rc;
+}
+
+void http_response_destroy(http_response_t *resp)
+{
+	free(resp->message);
+	http_headers_clear(&resp->headers);
+	free(resp);
+}
+
+/** @}
+ */
Index: uspace/lib/net/include/socket_core.h
===================================================================
--- uspace/lib/net/include/socket_core.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/net/include/socket_core.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -43,5 +43,4 @@
 #include <adt/int_map.h>
 #include <net/in.h>
-#include <net/device.h>
 #include <async.h>
 
Index: uspace/lib/pcm/Makefile
===================================================================
--- uspace/lib/pcm/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/pcm/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2012 Jan Vesely
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+EXTRA_CFLAGS = -Iinclude/pcm -Iinclude
+LIBRARY = libpcm
+
+SOURCES = \
+	src/format.c
+include $(USPACE_PREFIX)/Makefile.common
+
+
Index: uspace/lib/pcm/include/pcm/format.h
===================================================================
--- uspace/lib/pcm/include/pcm/format.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/pcm/include/pcm/format.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#ifndef PCM_FORMAT_H_
+#define PCM_FORMAT_H_
+
+#include <assert.h>
+#include <stdbool.h>
+#include <pcm/sample_format.h>
+
+/** Linear PCM audio parameters */
+typedef struct {
+	unsigned channels;
+	unsigned sampling_rate;
+	pcm_sample_format_t sample_format;
+} pcm_format_t;
+
+extern const pcm_format_t AUDIO_FORMAT_DEFAULT;
+extern const pcm_format_t AUDIO_FORMAT_ANY;
+
+/**
+ * Frame size helper function.
+ * @param a pointer to a PCM format structure.
+ * @return Size in bytes.
+ */
+static inline size_t pcm_format_frame_size(const pcm_format_t *a)
+{
+	return pcm_sample_format_frame_size(a->channels, a->sample_format);
+}
+
+/**
+ * Convert byte size to frame count.
+ * @param size Byte-size.
+ * @param a pointer to a PCM format structure.
+ * @return Frame count.
+ */
+static inline size_t pcm_format_size_to_frames(size_t size,
+    const pcm_format_t *a)
+{
+	return pcm_sample_format_size_to_frames(size, a->channels,
+	    a->sample_format);
+}
+
+/**
+ * Convert byte size to audio playback time.
+ * @param size Byte-size.
+ * @param a pointer to a PCM format structure.
+ * @return Number of microseconds.
+ */
+static inline useconds_t pcm_format_size_to_usec(size_t size,
+    const pcm_format_t *a)
+{
+	return pcm_sample_format_size_to_usec(size, a->sampling_rate,
+	    a->channels, a->sample_format);
+}
+
+bool pcm_format_same(const pcm_format_t *a, const pcm_format_t* b);
+
+/**
+ * Helper function, compares with ANY metaformat.
+ * @param f pointer to format structure.
+ * @return True if @p f points to ANY format, false otherwise.
+ */
+static inline bool pcm_format_is_any(const pcm_format_t *f)
+{
+	return pcm_format_same(f, &AUDIO_FORMAT_ANY);
+}
+void pcm_format_silence(void *dst, size_t size, const pcm_format_t *f);
+int pcm_format_convert_and_mix(void *dst, size_t dst_size, const void *src,
+    size_t src_size, const pcm_format_t *sf, const pcm_format_t *df);
+int pcm_format_mix(void *dst, const void *src, size_t size, const pcm_format_t *f);
+int pcm_format_convert(pcm_format_t a, void* srca, size_t sizea,
+    pcm_format_t b, void* srcb, size_t *sizeb);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/pcm/include/pcm/sample_format.h
===================================================================
--- uspace/lib/pcm/include/pcm/sample_format.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/pcm/include/pcm/sample_format.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief PCM sample format
+ * @{
+ */
+/** @file
+ */
+
+#ifndef PCM_SAMPLE_FORMAT_H_
+#define PCM_SAMPLE_FORMAT_H_
+
+#include <stdbool.h>
+#include <time.h>
+
+/** Known and supported PCM sample formats */
+typedef enum {
+	PCM_SAMPLE_UINT8,
+	PCM_SAMPLE_SINT8,
+	PCM_SAMPLE_UINT16_LE,
+	PCM_SAMPLE_UINT16_BE,
+	PCM_SAMPLE_SINT16_LE,
+	PCM_SAMPLE_SINT16_BE,
+	PCM_SAMPLE_UINT24_LE,
+	PCM_SAMPLE_UINT24_BE,
+	PCM_SAMPLE_SINT24_LE,
+	PCM_SAMPLE_SINT24_BE,
+	PCM_SAMPLE_UINT24_32_LE,
+	PCM_SAMPLE_UINT24_32_BE,
+	PCM_SAMPLE_SINT24_32_LE,
+	PCM_SAMPLE_SINT24_32_BE,
+	PCM_SAMPLE_UINT32_LE,
+	PCM_SAMPLE_UINT32_BE,
+	PCM_SAMPLE_SINT32_LE,
+	PCM_SAMPLE_SINT32_BE,
+	PCM_SAMPLE_FLOAT32,
+	PCM_SAMPLE_FORMAT_LAST = PCM_SAMPLE_FLOAT32,
+} pcm_sample_format_t;
+
+/**
+ * Query if the format uses signed values.
+ * @param format PCM sample format.
+ * @return True if the format uses signed values, false otherwise.
+ */
+static inline bool pcm_sample_format_is_signed(pcm_sample_format_t format)
+{
+	switch(format) {
+	case PCM_SAMPLE_SINT8:
+	case PCM_SAMPLE_SINT16_LE:
+	case PCM_SAMPLE_SINT16_BE:
+	case PCM_SAMPLE_SINT24_LE:
+	case PCM_SAMPLE_SINT24_BE:
+	case PCM_SAMPLE_SINT24_32_LE:
+	case PCM_SAMPLE_SINT24_32_BE:
+	case PCM_SAMPLE_SINT32_LE:
+	case PCM_SAMPLE_SINT32_BE:
+		return true;
+	case PCM_SAMPLE_UINT8:
+	case PCM_SAMPLE_UINT16_LE:
+	case PCM_SAMPLE_UINT16_BE:
+	case PCM_SAMPLE_UINT24_LE:
+	case PCM_SAMPLE_UINT24_BE:
+	case PCM_SAMPLE_UINT24_32_LE:
+	case PCM_SAMPLE_UINT24_32_BE:
+	case PCM_SAMPLE_UINT32_LE:
+	case PCM_SAMPLE_UINT32_BE:
+	case PCM_SAMPLE_FLOAT32:
+	default:
+		return false;
+	}
+}
+
+/**
+ * Query byte-size of samples.
+ * @param format PCM sample format.
+ * @return Size in bytes of a single sample.
+ */
+static inline size_t pcm_sample_format_size(pcm_sample_format_t format)
+{
+	switch(format) {
+	case PCM_SAMPLE_UINT8:
+	case PCM_SAMPLE_SINT8:
+		return 1;
+	case PCM_SAMPLE_UINT16_LE:
+	case PCM_SAMPLE_UINT16_BE:
+	case PCM_SAMPLE_SINT16_LE:
+	case PCM_SAMPLE_SINT16_BE:
+		return 2;
+	case PCM_SAMPLE_UINT24_LE:
+	case PCM_SAMPLE_UINT24_BE:
+	case PCM_SAMPLE_SINT24_LE:
+	case PCM_SAMPLE_SINT24_BE:
+		return 3;
+	case PCM_SAMPLE_UINT24_32_LE:
+	case PCM_SAMPLE_UINT24_32_BE:
+	case PCM_SAMPLE_SINT24_32_LE:
+	case PCM_SAMPLE_SINT24_32_BE:
+	case PCM_SAMPLE_UINT32_LE:
+	case PCM_SAMPLE_UINT32_BE:
+	case PCM_SAMPLE_SINT32_LE:
+	case PCM_SAMPLE_SINT32_BE:
+	case PCM_SAMPLE_FLOAT32:
+		return 4;
+	default:
+		return 0;
+	}
+}
+
+/**
+ * Query sie of the entire frame.
+ * @param channels Number of samples in every frame.
+ * @param format PCM sample format.
+ * @return Size in bytes.
+ */
+static inline size_t pcm_sample_format_frame_size(unsigned channels,
+    pcm_sample_format_t format)
+{
+	return pcm_sample_format_size(format) * channels;
+}
+
+/**
+ * Count number of frames that fit into a buffer (even incomplete frames).
+ * @param size Size of the buffer.
+ * @param channels Number of samples in every frame.
+ * @param format PCM sample format.
+ * @return Number of frames (even incomplete).
+ */
+static inline size_t pcm_sample_format_size_to_frames(size_t size,
+    unsigned channels, pcm_sample_format_t format)
+{
+	const size_t frame_size = pcm_sample_format_frame_size(channels, format);
+	return (size + frame_size - 1) / frame_size;
+}
+
+/**
+ * Convert byte size to time.
+ * @param size Size of the buffer.
+ * @param sample_rate Samples per second.
+ * @param channels Number of samples in every frame.
+ * @param format PCM sample format.
+ * @return Number of useconds of audio data.
+ */
+static inline useconds_t pcm_sample_format_size_to_usec(size_t size,
+    unsigned sample_rate, unsigned channels, pcm_sample_format_t format)
+{
+	const unsigned long long frames =
+	    pcm_sample_format_size_to_frames(size, channels, format);
+	return (frames * 1000000ULL) / sample_rate;
+}
+
+/**
+ * Get readable name of a sample format.
+ * @param format PCM sample format.
+ * @return Valid string representation.
+ */
+static inline const char * pcm_sample_format_str(pcm_sample_format_t format)
+{
+	switch(format) {
+	case PCM_SAMPLE_UINT8:
+		return "8 bit unsinged";
+	case PCM_SAMPLE_SINT8:
+		return "8 bit singed";
+	case PCM_SAMPLE_UINT16_LE:
+		return "16 bit unsigned(LE)";
+	case PCM_SAMPLE_SINT16_LE:
+		return "16 bit singed(LE)";
+	case PCM_SAMPLE_UINT16_BE:
+		return "16 bit unsigned(BE)";
+	case PCM_SAMPLE_SINT16_BE:
+		return "16 bit signed(BE)";
+	case PCM_SAMPLE_UINT24_LE:
+		return "24 bit unsigned(LE)";
+	case PCM_SAMPLE_SINT24_LE:
+		return "24 bit signed(LE)";
+	case PCM_SAMPLE_UINT24_BE:
+		return "24 bit unsigned(BE)";
+	case PCM_SAMPLE_SINT24_BE:
+		return "24 bit signed(BE)";
+	case PCM_SAMPLE_UINT24_32_LE:
+		return "24 bit(4byte aligned) unsigned(LE)";
+	case PCM_SAMPLE_UINT24_32_BE:
+		return "24 bit(4byte aligned) unsigned(BE)";
+	case PCM_SAMPLE_SINT24_32_LE:
+		return "24 bit(4byte aligned) signed(LE)";
+	case PCM_SAMPLE_SINT24_32_BE:
+		return "24 bit(4byte aligned) signed(BE)";
+	case PCM_SAMPLE_UINT32_LE:
+		return "32 bit unsigned(LE)";
+	case PCM_SAMPLE_UINT32_BE:
+		return "32 bit unsigned(BE)";
+	case PCM_SAMPLE_SINT32_LE:
+		return "32 bit signed(LE)";
+	case PCM_SAMPLE_SINT32_BE:
+		return "32 bit signed(BE)";
+	case PCM_SAMPLE_FLOAT32:
+		return "32 bit float";
+	default:
+		return "Unknown sample format";
+	}
+}
+
+#endif
+
+/**
+ * @}
+ */
+
Index: uspace/lib/pcm/src/format.c
===================================================================
--- uspace/lib/pcm/src/format.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/pcm/src/format.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#include <assert.h>
+#include <byteorder.h>
+#include <errno.h>
+#include <macros.h>
+#include <stdio.h>
+
+#include "format.h"
+
+#define uint8_t_le2host(x) (x)
+#define host2uint8_t_le(x) (x)
+#define uint8_t_be2host(x) (x)
+#define host2uint8_t_be(x) (x)
+
+#define int8_t_le2host(x) (x)
+#define host2int8_t_le(x) (x)
+
+#define int16_t_le2host(x) uint16_t_le2host(x)
+#define host2int16_t_le(x) host2uint16_t_le(x)
+
+#define int32_t_le2host(x) uint32_t_le2host(x)
+#define host2int32_t_le(x) host2uint32_t_le(x)
+
+#define int8_t_be2host(x) (x)
+#define host2int8_t_be(x) (x)
+
+#define int16_t_be2host(x) uint16_t_be2host(x)
+#define host2int16_t_be(x) host2uint16_t_be(x)
+
+#define int32_t_be2host(x) uint32_t_be2host(x)
+#define host2int32_t_be(x) host2uint32_t_be(x)
+
+// TODO float endian?
+#define float_le2host(x) (x)
+#define float_be2host(x) (x)
+
+#define host2float_le(x) (x)
+#define host2float_be(x) (x)
+
+#define from(x, type, endian) (float)(type ## _ ## endian ## 2host(x))
+#define to(x, type, endian) (float)(host2 ## type ## _ ## endian(x))
+
+/** Default linear PCM format */
+const pcm_format_t AUDIO_FORMAT_DEFAULT = {
+	.channels = 2,
+	.sampling_rate = 44100,
+	.sample_format = PCM_SAMPLE_SINT16_LE,
+	};
+
+/** Special ANY PCM format.
+ * This format is used if the real format is no know or important.
+ */
+const pcm_format_t AUDIO_FORMAT_ANY = {
+	.channels = 0,
+	.sampling_rate = 0,
+	.sample_format = 0,
+	};
+
+static float get_normalized_sample(const void *buffer, size_t size,
+    unsigned frame, unsigned channel, const pcm_format_t *f);
+
+/**
+ * Compare PCM format attribtues.
+ * @param a Format description.
+ * @param b Format description.
+ * @return True if a and b describe the same format, false otherwise.
+ */
+bool pcm_format_same(const pcm_format_t *a, const pcm_format_t* b)
+{
+	assert(a);
+	assert(b);
+	return
+	    a->sampling_rate == b->sampling_rate &&
+	    a->channels == b->channels &&
+	    a->sample_format == b->sample_format;
+}
+
+/**
+ * Fill audio buffer with silence in the specified format.
+ * @param dst Destination audio buffer.
+ * @param size Size of the destination audio buffer.
+ * @param f Pointer to the format description.
+ */
+void pcm_format_silence(void *dst, size_t size, const pcm_format_t *f)
+{
+#define SET_NULL(type, endian, nullv) \
+do { \
+	type *buffer = dst; \
+	const size_t sample_count = size / sizeof(type); \
+	for (unsigned i = 0; i < sample_count; ++i) { \
+		buffer[i] = to((type)nullv, type, endian); \
+	} \
+} while (0)
+
+	switch (f->sample_format) {
+	case PCM_SAMPLE_UINT8:
+		SET_NULL(uint8_t, le, INT8_MIN); break;
+	case PCM_SAMPLE_SINT8:
+		SET_NULL(int8_t, le, 0); break;
+	case PCM_SAMPLE_UINT16_LE:
+		SET_NULL(uint16_t, le, INT16_MIN); break;
+	case PCM_SAMPLE_SINT16_LE:
+		SET_NULL(int16_t, le, 0); break;
+	case PCM_SAMPLE_UINT16_BE:
+		SET_NULL(uint16_t, be, INT16_MIN); break;
+	case PCM_SAMPLE_SINT16_BE:
+		SET_NULL(int16_t, be, 0); break;
+	case PCM_SAMPLE_UINT32_LE:
+		SET_NULL(uint32_t, le, INT32_MIN); break;
+	case PCM_SAMPLE_SINT32_LE:
+		SET_NULL(int32_t, le, 0); break;
+	case PCM_SAMPLE_UINT32_BE:
+		SET_NULL(uint32_t, be, INT32_MIN); break;
+	case PCM_SAMPLE_SINT32_BE:
+		SET_NULL(int32_t, le, 0); break;
+	case PCM_SAMPLE_UINT24_32_LE:
+	case PCM_SAMPLE_SINT24_32_LE:
+	case PCM_SAMPLE_UINT24_32_BE:
+	case PCM_SAMPLE_SINT24_32_BE:
+	case PCM_SAMPLE_UINT24_LE:
+	case PCM_SAMPLE_SINT24_LE:
+	case PCM_SAMPLE_UINT24_BE:
+	case PCM_SAMPLE_SINT24_BE:
+	case PCM_SAMPLE_FLOAT32:
+	default: ;
+	}
+#undef SET_NULL
+}
+
+/**
+ * Mix audio data of the same format and size.
+ * @param dst Destination buffer
+ * @param src Source buffer
+ * @param size Size of both the destination and the source buffer
+ * @param f Pointer to the format descriptor.
+ * @return Error code.
+ */
+int pcm_format_mix(void *dst, const void *src, size_t size, const pcm_format_t *f)
+{
+	return pcm_format_convert_and_mix(dst, size, src, size, f, f);
+}
+
+/**
+ * Add and mix audio data.
+ * @param dst Destination audio buffer
+ * @param dst_size Size of the destination buffer
+ * @param src Source audio buffer
+ * @param src_size Size of the source buffer.
+ * @param sf Pointer to the source format descriptor.
+ * @param df Pointer to the destination format descriptor.
+ * @return Error code.
+ *
+ * Buffers must contain entire frames. Destination buffer is always filled.
+ * If there are not enough data in the source buffer silent data is assumed.
+ */
+int pcm_format_convert_and_mix(void *dst, size_t dst_size, const void *src,
+    size_t src_size, const pcm_format_t *sf, const pcm_format_t *df)
+{
+	if (!dst || !src || !sf || !df)
+		return EINVAL;
+	const size_t src_frame_size = pcm_format_frame_size(sf);
+	if ((src_size % src_frame_size) != 0)
+		return EINVAL;
+
+	const size_t dst_frame_size = pcm_format_frame_size(df);
+	if ((dst_size % dst_frame_size) != 0)
+		return EINVAL;
+
+	/* This is so ugly it eats kittens, and puppies, and ducklings,
+	 * and all little fluffy things...
+	 */
+#define LOOP_ADD(type, endian, low, high) \
+do { \
+	const unsigned frame_count = dst_size / dst_frame_size; \
+	for (size_t i = 0; i < frame_count; ++i) { \
+		for (unsigned j = 0; j < df->channels; ++j) { \
+			const float a = \
+			    get_normalized_sample(dst, dst_size, i, j, df);\
+			const float b = \
+			    get_normalized_sample(src, src_size, i, j, sf);\
+			float c = (a + b); \
+			if (c < -1.0) c = -1.0; \
+			if (c > 1.0) c = 1.0; \
+			c += 1.0; \
+			c *= ((float)(type)high - (float)(type)low) / 2; \
+			c += (float)(type)low; \
+			type *dst_buf = dst; \
+			const unsigned pos = i * df->channels  + j; \
+			if (pos < (dst_size / sizeof(type))) \
+				dst_buf[pos] = to((type)c, type, endian); \
+		} \
+	} \
+} while (0)
+
+	switch (df->sample_format) {
+	case PCM_SAMPLE_UINT8:
+		LOOP_ADD(uint8_t, le, UINT8_MIN, UINT8_MAX); break;
+	case PCM_SAMPLE_SINT8:
+		LOOP_ADD(uint8_t, le, INT8_MIN, INT8_MAX); break;
+	case PCM_SAMPLE_UINT16_LE:
+		LOOP_ADD(uint16_t, le, UINT16_MIN, UINT16_MAX); break;
+	case PCM_SAMPLE_SINT16_LE:
+		LOOP_ADD(int16_t, le, INT16_MIN, INT16_MAX); break;
+	case PCM_SAMPLE_UINT16_BE:
+		LOOP_ADD(uint16_t, be, UINT16_MIN, UINT16_MAX); break;
+	case PCM_SAMPLE_SINT16_BE:
+		LOOP_ADD(int16_t, be, INT16_MIN, INT16_MAX); break;
+	case PCM_SAMPLE_UINT24_32_LE:
+	case PCM_SAMPLE_UINT32_LE: // TODO this are not right for 24bit
+		LOOP_ADD(uint32_t, le, UINT32_MIN, UINT32_MAX); break;
+	case PCM_SAMPLE_SINT24_32_LE:
+	case PCM_SAMPLE_SINT32_LE:
+		LOOP_ADD(int32_t, le, INT32_MIN, INT32_MAX); break;
+	case PCM_SAMPLE_UINT24_32_BE:
+	case PCM_SAMPLE_UINT32_BE:
+		LOOP_ADD(uint32_t, be, UINT32_MIN, UINT32_MAX); break;
+	case PCM_SAMPLE_SINT24_32_BE:
+	case PCM_SAMPLE_SINT32_BE:
+		LOOP_ADD(int32_t, be, INT32_MIN, INT32_MAX); break;
+	case PCM_SAMPLE_UINT24_LE:
+	case PCM_SAMPLE_SINT24_LE:
+	case PCM_SAMPLE_UINT24_BE:
+	case PCM_SAMPLE_SINT24_BE:
+	case PCM_SAMPLE_FLOAT32:
+	default:
+		return ENOTSUP;
+	}
+	return EOK;
+#undef LOOP_ADD
+}
+
+/**
+ * Converts all sample formats to float <-1,1>
+ * @param buffer Audio data
+ * @param size Size of the buffer
+ * @param frame Index of the frame to read
+ * @param channel Channel within the frame
+ * @param f Pointer to a format descriptor
+ * @return Normalized sample <-1,1>, 0.0 if the data could not be read
+ */
+static float get_normalized_sample(const void *buffer, size_t size,
+    unsigned frame, unsigned channel, const pcm_format_t *f)
+{
+	assert(f);
+	assert(buffer);
+	if (channel >= f->channels)
+		return 0.0f;
+#define GET(type, endian, low, high) \
+do { \
+	const type *src = buffer; \
+	const size_t sample_count = size / sizeof(type); \
+	const size_t sample_pos = frame * f->channels + channel; \
+	if (sample_pos >= sample_count) {\
+		return 0.0f; \
+	} \
+	float sample = from(src[sample_pos], type, endian); \
+	/* This makes it positive */ \
+	sample -= (float)(type)low; \
+	/* This makes it <0,2> */ \
+	sample /= (((float)(type)high - (float)(type)low) / 2.0f); \
+	return sample - 1.0f; \
+} while (0)
+
+	switch (f->sample_format) {
+	case PCM_SAMPLE_UINT8:
+		GET(uint8_t, le, UINT8_MIN, UINT8_MAX);
+	case PCM_SAMPLE_SINT8:
+		GET(int8_t, le, INT8_MIN, INT8_MAX);
+	case PCM_SAMPLE_UINT16_LE:
+		GET(uint16_t, le, UINT16_MIN, UINT16_MAX);
+	case PCM_SAMPLE_SINT16_LE:
+		GET(int16_t, le, INT16_MIN, INT16_MAX);
+	case PCM_SAMPLE_UINT16_BE:
+		GET(uint16_t, be, UINT16_MIN, UINT16_MAX);
+	case PCM_SAMPLE_SINT16_BE:
+		GET(int16_t, be, INT16_MIN, INT16_MAX);
+	case PCM_SAMPLE_UINT24_32_LE:
+	case PCM_SAMPLE_UINT32_LE:
+		GET(uint32_t, le, UINT32_MIN, UINT32_MAX);
+	case PCM_SAMPLE_SINT24_32_LE:
+	case PCM_SAMPLE_SINT32_LE:
+		GET(int32_t, le, INT32_MIN, INT32_MAX);
+	case PCM_SAMPLE_UINT24_32_BE:
+	case PCM_SAMPLE_UINT32_BE:
+		GET(uint32_t, be, UINT32_MIN, UINT32_MAX);
+	case PCM_SAMPLE_SINT24_32_BE:
+	case PCM_SAMPLE_SINT32_BE:
+		GET(int32_t, le, INT32_MIN, INT32_MAX);
+	case PCM_SAMPLE_UINT24_LE:
+	case PCM_SAMPLE_SINT24_LE:
+	case PCM_SAMPLE_UINT24_BE:
+	case PCM_SAMPLE_SINT24_BE:
+	case PCM_SAMPLE_FLOAT32:
+	default: ;
+	}
+	return 0;
+#undef GET
+}
+/**
+ * @}
+ */
Index: uspace/lib/posix/include/posix/time.h
===================================================================
--- uspace/lib/posix/include/posix/time.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/posix/include/posix/time.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -87,4 +87,7 @@
 extern void __POSIX_DEF__(tzset)(void);
 
+/* Time */
+extern time_t __POSIX_DEF__(time)(time_t *t);
+
 /* Broken-down Time */
 extern struct tm *__POSIX_DEF__(gmtime_r)(const time_t *restrict timer,
Index: uspace/lib/posix/source/time.c
===================================================================
--- uspace/lib/posix/source/time.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/posix/source/time.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -74,4 +74,17 @@
 	posix_daylight = 0;
 	posix_timezone = 0;
+}
+
+/**
+ * Get the time in seconds
+ *
+ * @param t If t is non-NULL, the return value is also stored in the memory
+ *          pointed to by t.
+ * @return  On success, the value of time in seconds since the Epoch
+ *          is returned. On error, (time_t)-1 is returned.
+ */
+time_t posix_time(time_t *t)
+{
+	return time(t);
 }
 
Index: uspace/lib/softfloat/softfloat.c
===================================================================
--- uspace/lib/softfloat/softfloat.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/softfloat/softfloat.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1265,4 +1265,14 @@
 }
 
+float __aeabi_i2f(int i)
+{
+	return __floatsisf(i);
+}
+
+float __aeabi_ui2f(int i)
+{
+	return __floatunsisf(i);
+}
+
 double __aeabi_i2d(int i)
 {
@@ -1280,4 +1290,9 @@
 }
 
+int __aeabi_f2uiz(float a)
+{
+	return __fixunssfsi(a);
+}
+
 int __aeabi_d2iz(double a)
 {
@@ -1288,4 +1303,24 @@
 {
 	return __fixunsdfsi(a);
+}
+
+int __aeabi_fcmpge(float a, float b)
+{
+	return __gesf2(a, b);
+}
+
+int __aeabi_fcmpgt(float a, float b)
+{
+	return __gtsf2(a, b);
+}
+
+int __aeabi_fcmplt(float a, float b)
+{
+	return __ltsf2(a, b);
+}
+
+int __aeabi_fcmpeq(float a, float b)
+{
+	return __eqsf2(a, b);
 }
 
Index: uspace/lib/softfloat/softfloat.h
===================================================================
--- uspace/lib/softfloat/softfloat.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/softfloat/softfloat.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -204,4 +204,6 @@
 
 /* ARM EABI */
+extern float __aeabi_i2f(int);
+extern float __aeabi_ui2f(int);
 extern double __aeabi_i2d(int);
 extern double __aeabi_ui2d(unsigned int);
@@ -209,5 +211,11 @@
 
 extern int __aeabi_f2iz(float);
+extern int __aeabi_f2uiz(float);
 extern int __aeabi_d2iz(double);
+
+extern int __aeabi_fcmpge(float, float);
+extern int __aeabi_fcmpgt(float, float);
+extern int __aeabi_fcmplt(float, float);
+extern int __aeabi_fcmpeq(float, float);
 
 extern int __aeabi_dcmpge(double, double);
Index: uspace/lib/uri/Makefile
===================================================================
--- uspace/lib/uri/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/uri/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2013 Martin Sucha
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../..
+LIBRARY = liburi
+SLIBRARY = liburi.so.0.0
+LSONAME = liburi.so0
+#EXTRA_CFLAGS += 
+
+SOURCES = \
+	uri.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/lib/uri/ctype.h
===================================================================
--- uspace/lib/uri/ctype.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/uri/ctype.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup uri
+ * @{
+ */
+/**
+ * @file
+ */
+
+#ifndef URI_CTYPE_H_
+#define URI_CTYPE_H_
+
+static inline bool is_unreserved(char c)
+{
+	return isalpha(c) || isdigit(c) ||
+	    c == '-' || c == '.' || c == '_' || c == '~';
+}
+
+static inline bool is_hexdig(char c)
+{
+	return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
+	    (c >= 'A' && c <= 'F');
+}
+
+static inline bool is_subdelim(char c)
+{
+	switch (c) {
+	case '!':
+	case '$':
+	case '&':
+	case '\'':
+	case '(':
+	case ')':
+	case '*':
+	case '+':
+	case ',':
+	case ';':
+	case '=':
+		return true;
+	default:
+		return false;
+	}
+}
+
+static inline bool is_gendelim(char c)
+{
+	switch (c) {
+	case ':':
+	case '/':
+	case '?':
+	case '#':
+	case '[':
+	case ']':
+	case '@':
+		return true;
+	default:
+		return false;
+	}
+}
+
+static inline bool is_reserved(char c)
+{
+	return is_gendelim(c) || is_subdelim(c);
+}
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/uri/uri.c
===================================================================
--- uspace/lib/uri/uri.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/uri/uri.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup uri
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <str.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "uri.h"
+#include "ctype.h"
+
+static char *cut_str(const char *start, const char *end)
+{
+	size_t size = end - start;
+	return str_ndup(start, size);
+}
+
+uri_t *uri_parse(const char *str)
+{
+	uri_t *uri = malloc(sizeof(uri_t));
+	if (uri == NULL)
+		return NULL;
+	memset(uri, 0, sizeof(uri_t));
+	
+	/* scheme ":" */
+	const char *scheme = str;
+	while (*str != 0 && *str != ':') str++;
+	if (*str == 0) {
+		uri_destroy(uri);
+		return NULL;
+	}
+	uri->scheme = cut_str(scheme, str);
+	
+	/* skip the colon */
+	str++;
+	
+	if (*str == '/' && str[1] == '/') {
+		/* "//" [user-info [":" user-credential] "@"] host [":" port] */
+		str++;
+		str++;
+		const char *authority_start = str;
+	
+		char *host_or_user_info = NULL;
+		char *port_or_user_credential = NULL;
+	
+		while (*str != 0 && *str != '?' && *str != '#' && *str != '@'
+			&& *str != ':' && *str != '/')
+			str++;
+	
+		host_or_user_info = cut_str(authority_start, str);
+		if (*str == ':') {
+			str++;
+			const char *second_part = str;
+			while (*str != 0 && *str != '?' && *str != '#' &&
+				*str != '@' && *str != '/') str++;
+			port_or_user_credential = cut_str(second_part, str);
+		}
+	
+		if (*str == '@') {
+			uri->user_info = host_or_user_info;
+			uri->user_credential = port_or_user_credential;
+		
+			str++;
+			const char *host_start = str;
+			while (*str != 0 && *str != '?' && *str != '#'
+				&& *str != ':' && *str != '/') str++;
+			uri->host = cut_str(host_start, str);
+		
+			if (*str == ':') {
+				str++;
+				const char *port_start = str;
+				while (*str != 0 && *str != '?' && *str != '#' && *str != '/')
+					str++;
+				uri->port = cut_str(port_start, str);
+			}
+		}
+		else {
+			uri->host = host_or_user_info;
+			uri->port = port_or_user_credential;
+		}
+	}
+	
+	const char *path_start = str;
+	while (*str != 0 && *str != '?' && *str != '#') str++;
+	uri->path = cut_str(path_start, str);
+	
+	if (*str == '?') {
+		str++;
+		const char *query_start = str;
+		while (*str != 0 && *str != '#') str++;
+		uri->query = cut_str(query_start, str);
+	}
+	
+	if (*str == '#') {
+		str++;
+		const char *fragment_start = str;
+		while (*str != 0) str++;
+		uri->fragment = cut_str(fragment_start, str);
+	}
+	
+	assert(*str == 0);
+	return uri;
+}
+
+/** Parse the URI scheme
+ *
+ * @param endptr The position of the first character after the scheme
+ *               is stored there.
+ * @return EOK on success
+ */
+int uri_scheme_parse(const char *str, const char **endptr)
+{
+	if (*str == 0) {
+		*endptr = str;
+		return ELIMIT;
+	}
+	
+	if (!isalpha(*str)) {
+		*endptr = str;
+		return EINVAL;
+	}
+	
+	while (isalpha(*str) || isdigit(*str) ||
+	    *str == '+' || *str == '-' || *str == '.') {
+		str++;
+	}
+	
+	*endptr = str;
+	return EOK;
+}
+
+/** Determine if the URI scheme is valid
+ *
+ */
+bool uri_scheme_validate(const char *str)
+{
+	const char *endptr = NULL;
+	if (uri_scheme_parse(str, &endptr) != EOK)
+		return false;
+	return *endptr == 0;
+}
+
+int uri_percent_parse(const char *str, const char **endptr,
+    uint8_t *decoded)
+{
+	*endptr = str;
+	if (str[0] == 0 || str[1] == 0 || str[2] == 0)
+		return ELIMIT;
+	
+	if (str[0] != '%' || !is_hexdig(str[1]) || !is_hexdig(str[2]))
+		return EINVAL;
+	
+	if (decoded != NULL) {
+		int rc = str_uint8_t(str + 1, NULL, 16, true, decoded);
+		if (rc != EOK)
+			return rc;
+	}
+	
+	*endptr = str + 3;
+	return EOK;
+}
+
+int uri_user_info_parse(const char *str, const char **endptr)
+{
+	while (*str != 0) {
+		while (is_unreserved(*str) || is_subdelim(*str) || *str == ':') str++;
+		if (*str == 0)
+			break;
+		int rc = uri_percent_parse(str, &str, NULL);
+		if (rc != EOK) {
+			*endptr = str;
+			return rc;
+		}
+	}
+	
+	*endptr = str;
+	return EOK;
+}
+
+bool uri_user_info_validate(const char *str)
+{
+	const char *endptr = NULL;
+	if (uri_user_info_parse(str, &endptr) != EOK)
+		return false;
+	return *endptr == 0;
+}
+
+int uri_port_parse(const char *str, const char **endptr)
+{
+	if (*str == 0)
+		return ELIMIT;
+	if (!isdigit(*str))
+		return EINVAL;
+	while (isdigit(*str)) str++;
+	*endptr = str;
+	return EOK;
+}
+
+bool uri_port_validate(const char *str)
+{
+	const char *endptr = NULL;
+	if (uri_port_parse(str, &endptr) != EOK)
+		return false;
+	return *endptr == 0;
+}
+
+bool uri_validate(uri_t *uri)
+{
+	if (uri->scheme && !uri_scheme_validate(uri->scheme))
+		return false;
+	
+	if (uri->user_info && !uri_user_info_validate(uri->user_info))
+		return false;
+	
+	if (uri->user_credential && !uri_user_info_validate(uri->user_credential))
+		return false;
+	
+	if (uri->port && !uri_port_validate(uri->port))
+		return false;
+	
+	return true;
+}
+
+void uri_destroy(uri_t *uri)
+{
+	free(uri->scheme);
+	free(uri->user_info);
+	free(uri->user_credential);
+	free(uri->host);
+	free(uri->port);
+	free(uri->path);
+	free(uri->query);
+	free(uri->fragment);
+	free(uri);
+}
+
+/** @}
+ */
Index: uspace/lib/uri/uri.h
===================================================================
--- uspace/lib/uri/uri.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/lib/uri/uri.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup uri
+ * @{
+ */
+/**
+ * @file
+ */
+
+#ifndef URI_URI_H_
+#define URI_URI_H_
+
+typedef struct {
+	char *scheme;
+	char *user_info;
+	char *user_credential;
+	char *host;
+	char *port;
+	char *path;
+	char *query;
+	char *fragment;
+} uri_t;
+
+extern uri_t *uri_parse(const char *);
+extern int uri_scheme_parse(const char *, const char **);
+extern bool uri_scheme_validate(const char *);
+extern int uri_percent_parse(const char *, const char **, uint8_t *);
+extern int uri_user_info_parse(const char *, const char **);
+extern bool uri_user_info_validate(const char *);
+extern int uri_port_parse(const char *, const char **);
+extern bool uri_port_validate(const char *);
+extern bool uri_validate(uri_t *);
+extern void uri_destroy(uri_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/usbdev/src/recognise.c
===================================================================
--- uspace/lib/usbdev/src/recognise.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/usbdev/src/recognise.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -383,6 +383,5 @@
 		goto failure;
 	
-	list_foreach(match_ids.ids, id_link) {
-		match_id_t *match_id = list_get_instance(id_link, match_id_t, link);
+	list_foreach(match_ids.ids, link, match_id_t, match_id) {
 		rc = ddf_fun_add_match_id(child, match_id->id, match_id->score);
 		if (rc != EOK) {
Index: uspace/lib/usbhid/src/hiddescriptor.c
===================================================================
--- uspace/lib/usbhid/src/hiddescriptor.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/usbhid/src/hiddescriptor.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -333,10 +333,6 @@
 	}
 
-	usb_hid_report_description_t *report_des = NULL;
-	
-	list_foreach(report->reports, report_it) {
-		report_des = list_get_instance(report_it,
-				usb_hid_report_description_t, reports_link);
-
+	list_foreach(report->reports, reports_link,
+	    usb_hid_report_description_t, report_des) {
 		// if report id not set, return the first of the type
 		if(((report_des->report_id == report_id) || (report_id == 0)) && 
@@ -902,6 +898,4 @@
 void usb_hid_descriptor_print_list(list_t *list)
 {
-	usb_hid_report_field_t *report_item;
-
 	if(list == NULL || list_empty(list)) {
 	    usb_log_debug("\tempty\n");
@@ -909,8 +903,6 @@
 	}
 
-        list_foreach(*list, item) {
-		report_item = list_get_instance(item, usb_hid_report_field_t,
-				ritems_link);
-
+        list_foreach(*list, ritems_link, usb_hid_report_field_t,
+    	    report_item) {
 		usb_log_debug("\t\tOFFSET: %X\n", report_item->offset);
 		usb_log_debug("\t\tSIZE: %zu\n", report_item->size);
@@ -937,7 +929,5 @@
 
 		usb_log_debug("\n");
-
-	}
-
+	}
 }
 
@@ -951,13 +941,9 @@
 void usb_hid_descriptor_print(usb_hid_report_t *report)
 {
-	if(report == NULL) {
+	if (report == NULL)
 		return;
-	}
-
-	usb_hid_report_description_t *report_des;
-
-	list_foreach(report->reports, report_it) {
-		report_des = list_get_instance(report_it,
-			usb_hid_report_description_t, reports_link);
+
+	list_foreach(report->reports, reports_link,
+	    usb_hid_report_description_t, report_des) {
 		usb_log_debug("Report ID: %d\n", report_des->report_id);
 		usb_log_debug("\tType: %d\n", report_des->type);
Index: uspace/lib/usbhid/src/hidparser.c
===================================================================
--- uspace/lib/usbhid/src/hidparser.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/usbhid/src/hidparser.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -130,6 +130,4 @@
     size_t size, uint8_t *report_id)
 {
-	usb_hid_report_field_t *item;
-
 	usb_hid_report_description_t *report_des;
 	usb_hid_report_type_t type = USB_HID_REPORT_TYPE_INPUT;
@@ -153,7 +151,6 @@
 
 	/* read data */
-	list_foreach(report_des->report_items, list_item) {
-		item = list_get_instance(list_item, usb_hid_report_field_t, 
-		    ritems_link);
+	list_foreach(report_des->report_items, ritems_link,
+	    usb_hid_report_field_t, item) {
 
 		if (USB_HID_ITEM_FLAG_CONSTANT(item->item_flags) == 0) {
@@ -163,5 +160,5 @@
 				item->value = 
 					usb_hid_translate_data(item, data);
-		
+				
 				item->usage = USB_HID_EXTENDED_USAGE(
 				    item->usages[item->value -
@@ -184,6 +181,6 @@
 				/* variable item */
 				item->value = usb_hid_translate_data(item, 
-				    data);				
-			}			
+				    data);
+			}
 		}
 	}
@@ -295,10 +292,9 @@
 	usb_hid_report_description_t *report_des = NULL;
 
-	list_foreach(report->reports, report_it) {
-		report_des = list_get_instance(report_it,
-		    usb_hid_report_description_t, reports_link);
-		
-		if ((report_des->report_id == report_id) &&
-		    (report_des->type == USB_HID_REPORT_TYPE_OUTPUT)) {
+	list_foreach(report->reports, reports_link,
+	    usb_hid_report_description_t, rdes) {
+		if ((rdes->report_id == report_id) &&
+		    (rdes->type == USB_HID_REPORT_TYPE_OUTPUT)) {
+			report_des = rdes;
 			break;
 		}
@@ -351,5 +347,5 @@
 
 	if (report->use_report_ids != 0) {
-		buffer[0] = report_id;		
+		buffer[0] = report_id;
 	}
 
@@ -362,10 +358,6 @@
 	}
 
-	usb_hid_report_field_t *report_item;
-
-	list_foreach(report_des->report_items, item) {
-		report_item = list_get_instance(item, usb_hid_report_field_t,
-		    ritems_link);
-
+	list_foreach(report_des->report_items, ritems_link,
+	    usb_hid_report_field_t, report_item) {
 		value = usb_hid_translate_data_reverse(report_item, 
 		    report_item->value);
@@ -382,7 +374,7 @@
 			}
 			size_t shift = 8 - offset % 8 - length;
-			value = value << shift;							
+			value = value << shift;
 			value = value & (((1 << length) - 1) << shift);
-				
+			
 			uint8_t mask = 0;
 			mask = 0xff - (((1 << length) - 1) << shift);
@@ -400,5 +392,5 @@
 
 					tmp_value = tmp_value << (offset % 8);
-	
+					
 					mask = ~(((1 << (8 - (offset % 8))) - 1)
 					    << (offset % 8));
@@ -413,5 +405,5 @@
 					value = value & ((1 << (length - 
 					    ((offset + length) % 8))) - 1);
-				
+					
 					mask = (1 << (length - 
 					    ((offset + length) % 8))) - 1;
Index: uspace/lib/usbhid/src/hidpath.c
===================================================================
--- uspace/lib/usbhid/src/hidpath.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/usbhid/src/hidpath.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -176,9 +176,6 @@
 	usb_log_debug("\tLENGTH: %d\n", path->depth);
 
-	usb_hid_report_usage_path_t *path_item;
-
-	list_foreach(path->items, item) {
-		path_item = list_get_instance(item, usb_hid_report_usage_path_t,
-			rpath_items_link);
+	list_foreach(path->items, rpath_items_link,
+	    usb_hid_report_usage_path_t, path_item) {
 
 		usb_log_debug("\tUSAGE_PAGE: %X\n", path_item->usage_page);
@@ -237,18 +234,14 @@
 			usb_hid_report_usage_path_t, rpath_items_link);
 
-		list_foreach(report_path->items, report_link) {
-			report_item = list_get_instance(report_link,
-				usb_hid_report_usage_path_t, rpath_items_link);
+		list_foreach(report_path->items, rpath_items_link,
+		    usb_hid_report_usage_path_t, report_item) {
+
+			if (USB_HID_SAME_USAGE_PAGE(report_item->usage_page,
+				path_item->usage_page)) {
 				
-			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 (only_page == 0) {
+					if (USB_HID_SAME_USAGE(report_item->usage,
+					    path_item->usage))
 						return EOK;
-					}
 				}
 				else {
@@ -266,44 +259,39 @@
 			return 1;
 		}
-		
+
 	/* path is prefix of the report_path */
 	case USB_HID_PATH_COMPARE_BEGIN:
-	
+
 		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 {
+			} else {
 				report_link = report_link->next;
-				path_link = path_link->next;			
+				path_link = path_link->next;
 			}
-			
-		}
-
-		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;
 
@@ -405,5 +393,4 @@
 	usb_hid_report_path_t *usage_path)
 {
-	usb_hid_report_usage_path_t *path_item;
 	usb_hid_report_usage_path_t *new_path_item;
 	usb_hid_report_path_t *new_usage_path = usb_hid_report_path ();
@@ -419,7 +406,6 @@
 	}
 
-	list_foreach(usage_path->items, path_link) {
-		path_item = list_get_instance(path_link,
-			usb_hid_report_usage_path_t, rpath_items_link);
+	list_foreach(usage_path->items, rpath_items_link,
+	    usb_hid_report_usage_path_t, path_item) {
 
 		new_path_item = malloc(sizeof(usb_hid_report_usage_path_t));
Index: uspace/lib/usbhost/src/usb_endpoint_manager.c
===================================================================
--- uspace/lib/usbhost/src/usb_endpoint_manager.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/lib/usbhost/src/usb_endpoint_manager.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -90,6 +90,5 @@
 	if (address < 0)
 		return NULL;
-	list_foreach(*get_list(instance, address), iterator) {
-		endpoint_t *ep = endpoint_get_instance(iterator);
+	list_foreach(*get_list(instance, address), link, endpoint_t, ep) {
 		if (ep_match(ep, address, endpoint, direction))
 			return ep;
@@ -196,6 +195,6 @@
 			fibril_mutex_lock(&instance->guard);
 			/* endpoint number is < 16, thus first byte is enough */
-			list_foreach(*get_list(instance, target.address), it) {
-				endpoint_t *ep = endpoint_get_instance(it);
+			list_foreach(*get_list(instance, target.address),
+			    link, endpoint_t, ep) {
 				if ((ep->address == target.address)
 				    && (ep->endpoint = data[4])) {
@@ -216,6 +215,6 @@
 		if ((data[0] & 0xf) == 0) {
 			fibril_mutex_lock(&instance->guard);
-			list_foreach(*get_list(instance, target.address), it) {
-				endpoint_t *ep = endpoint_get_instance(it);
+			list_foreach(*get_list(instance, target.address),
+			    link, endpoint_t, ep) {
 				if (ep->address == target.address) {
 					endpoint_toggle_set(ep,0);
@@ -408,11 +407,19 @@
     usb_address_t address, void (*callback)(endpoint_t *, void *), void *arg)
 {
+	list_t *list;
+	link_t *link;
+	link_t *next;
+
 	assert(address >= 0);
 	assert(instance);
 	fibril_mutex_lock(&instance->guard);
-	list_foreach(*get_list(instance, address), iterator) {
-		endpoint_t *ep = endpoint_get_instance(iterator);
+
+	list = get_list(instance, address);
+	link = list_first(list);
+	while (link != NULL) {
+		endpoint_t *ep = list_get_instance(link, endpoint_t, link);
+		next = list_next(link, list);
+
 		if (ep->address == address) {
-			iterator = iterator->next;
 			list_remove(&ep->link);
 			if (callback)
@@ -420,4 +427,5 @@
 			endpoint_destroy(ep);
 		}
+		link = next;
 	}
 	fibril_mutex_unlock(&instance->guard);
Index: uspace/srv/audio/hound/Makefile
===================================================================
--- uspace/srv/audio/hound/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,56 @@
+#
+# Copyright (c) 2012 Jan Vesely
+# 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 = hound
+
+EXTRA_CFLAGS = \
+	-DNAME="\"hound\"" \
+	-I$(LIBDRV_PREFIX)/include \
+	-I$(LIBHOUND_PREFIX)/include \
+	-I$(LIBPCM_PREFIX)/include
+
+LIBS = \
+	$(LIBDRV_PREFIX)/libdrv.a \
+	$(LIBHOUND_PREFIX)/libhound.a \
+	$(LIBPCM_PREFIX)/libpcm.a
+
+SOURCES = \
+	audio_data.c \
+	audio_device.c \
+	audio_sink.c \
+	audio_source.c \
+	connection.c \
+	hound.c \
+	hound_ctx.c \
+	iface.c \
+	main.c
+
+
+include $(USPACE_PREFIX)/Makefile.common
+
Index: uspace/srv/audio/hound/audio_data.c
===================================================================
--- uspace/srv/audio/hound/audio_data.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/audio_data.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2013 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#include <macros.h>
+#include <malloc.h>
+
+#include "audio_data.h"
+#include "log.h"
+
+/**
+ * Create reference counted buffer out of ordinary data buffer.
+ * @param data audio buffer
+ * @param size Size of the @p data buffer.
+ * @param fomart audio data format.
+ * @return pointer to valid audio data structre, NULL on failure.
+ */
+audio_data_t *audio_data_create(const void *data, size_t size,
+    pcm_format_t format)
+{
+	audio_data_t *adata = malloc(sizeof(audio_data_t));
+	if (adata) {
+		unsigned overflow = size % pcm_format_frame_size(&format);
+		if (overflow)
+			log_warning("Data not a multiple of frame size, "
+			    "clipping.");
+
+		adata->data = data;
+		adata->size = size - overflow;
+		adata->format = format;
+		atomic_set(&adata->refcount, 1);
+	}
+	return adata;
+}
+
+/**
+ * Get a new reference to the audio data buffer.
+ * @param adata The audio data buffer.
+ */
+void audio_data_addref(audio_data_t *adata)
+{
+	assert(adata);
+	assert(atomic_get(&adata->refcount) > 0);
+	atomic_inc(&adata->refcount);
+}
+
+/**
+ * Release a reference to the audio data buffer.
+ * @param adata The audio data buffer.
+ */
+void audio_data_unref(audio_data_t *adata)
+{
+	assert(adata);
+	assert(atomic_get(&adata->refcount) > 0);
+	atomic_count_t refc = atomic_predec(&adata->refcount);
+	if (refc == 0) {
+		free(adata->data);
+		free(adata);
+	}
+}
+
+/* Data link helpers */
+
+/** Audio data buffer list helper structure. */
+typedef struct {
+	link_t link;
+	audio_data_t *adata;
+	size_t position;
+} audio_data_link_t;
+
+/** List instance helper function.
+ * @param l link
+ * @return valid pointer to data link structure, NULL on failure.
+ */
+static inline audio_data_link_t * audio_data_link_list_instance(link_t *l)
+{
+	return l ? list_get_instance(l, audio_data_link_t, link) : NULL;
+}
+
+/**
+ * Create a new audio data link.
+ * @param adata Audio data to store.
+ * @return Valid pointer to a new audio data link structure, NULL on failure.
+ */
+static audio_data_link_t *audio_data_link_create(audio_data_t *adata)
+{
+	assert(adata);
+	audio_data_link_t *link = malloc(sizeof(audio_data_link_t));
+	if (link) {
+		audio_data_addref(adata);
+		link->adata = adata;
+		link->position = 0;
+	}
+	return link;
+}
+
+/**
+ * Destroy audio data link.
+ * @param link The link to destroy.
+ *
+ * Releases data reference.
+ */
+static void audio_data_link_destroy(audio_data_link_t *link)
+{
+	assert(link);
+	assert(!link_in_use(&link->link));
+	audio_data_unref(link->adata);
+	free(link);
+}
+
+/**
+ * Data link buffer start helper function.
+ * @param alink audio data link
+ * @return pointer to the beginning of data buffer.
+ */
+static inline const void * audio_data_link_start(audio_data_link_t *alink)
+{
+	assert(alink);
+	assert(alink->adata);
+	return alink->adata->data + alink->position;
+}
+
+/**
+ * Data link remaining size getter.
+ * @param alink audio data link
+ * @return Remaining size of valid data in the buffer.
+ */
+static inline size_t audio_data_link_remain_size(audio_data_link_t *alink)
+{
+	assert(alink);
+	assert(alink->adata);
+	assert(alink->position <= alink->adata->size);
+	return alink->adata->size - alink->position;
+}
+
+
+/**
+ * Data link remaining frames getter.
+ * @param alink audio data link
+ * @return Number of remaining frames in the buffer.
+ */
+static inline size_t audio_data_link_available_frames(audio_data_link_t *alink)
+{
+	assert(alink);
+	assert(alink->adata);
+	return pcm_format_size_to_frames(audio_data_link_remain_size(alink),
+	    &alink->adata->format);
+}
+
+/* Audio Pipe */
+
+/**
+ * Initialize audio pipe structure.
+ * @param pipe The pipe structure to initialize.
+ */
+void audio_pipe_init(audio_pipe_t *pipe)
+{
+	assert(pipe);
+	list_initialize(&pipe->list);
+	fibril_mutex_initialize(&pipe->guard);
+	pipe->frames = 0;
+	pipe->bytes = 0;
+}
+
+/**
+ * Destroy all data in a pipe.
+ * @param pipe The audio pipe to clean.
+ */
+void audio_pipe_fini(audio_pipe_t *pipe)
+{
+	assert(pipe);
+	while (!list_empty(&pipe->list)) {
+		audio_data_t *adata = audio_pipe_pop(pipe);
+		audio_data_unref(adata);
+	}
+}
+
+/**
+ * Add new audio data to a pipe.
+ * @param pipe The target pipe.
+ * @param data The data.
+ * @return Error code.
+ */
+int audio_pipe_push(audio_pipe_t *pipe, audio_data_t *data)
+{
+	assert(pipe);
+	assert(data);
+	audio_data_link_t *alink = audio_data_link_create(data);
+	if (!alink)
+		return ENOMEM;
+
+	fibril_mutex_lock(&pipe->guard);
+	list_append(&alink->link, &pipe->list);
+	pipe->bytes += audio_data_link_remain_size(alink);
+	pipe->frames += audio_data_link_available_frames(alink);
+	fibril_mutex_unlock(&pipe->guard);
+	return EOK;
+}
+
+/**
+ * Retrieve data form a audio pipe.
+ * @param pipe THe target pipe.
+ * @return Valid pointer to audio data, NULL if the pipe was empty.
+ */
+audio_data_t *audio_pipe_pop(audio_pipe_t *pipe)
+{
+	assert(pipe);
+	fibril_mutex_lock(&pipe->guard);
+	audio_data_t *adata = NULL;
+	link_t *l = list_first(&pipe->list);
+	if (l) {
+		audio_data_link_t *alink = audio_data_link_list_instance(l);
+		list_remove(&alink->link);
+		pipe->bytes -= audio_data_link_remain_size(alink);
+		pipe->frames -= audio_data_link_available_frames(alink);
+		adata = alink->adata;
+		audio_data_addref(adata);
+		audio_data_link_destroy(alink);
+	}
+	fibril_mutex_unlock(&pipe->guard);
+	return adata;
+}
+
+
+/**
+ * Use data store in a pipe and mix it into the provided buffer.
+ * @param pipe The piep that should provide data.
+ * @param data Target buffer.
+ * @param size Target buffer size.
+ * @param format Target data format.
+ * @return Size of the target buffer used, Error code on failure.
+ */
+ssize_t audio_pipe_mix_data(audio_pipe_t *pipe, void *data,
+    size_t size, const pcm_format_t *f)
+{
+	assert(pipe);
+
+	const size_t dst_frame_size = pcm_format_frame_size(f);
+	size_t needed_frames = pcm_format_size_to_frames(size, f);
+	size_t copied_size = 0;
+
+	fibril_mutex_lock(&pipe->guard);
+	while (needed_frames > 0 && !list_empty(&pipe->list)) {
+		/* Get first audio chunk */
+		link_t *l = list_first(&pipe->list);
+		audio_data_link_t *alink = audio_data_link_list_instance(l);
+
+		/* Get audio chunk metadata */
+		const size_t src_frame_size =
+		    pcm_format_frame_size(&alink->adata->format);
+		const size_t available_frames =
+		    audio_data_link_available_frames(alink);
+		const size_t copy_frames = min(available_frames, needed_frames);
+		const size_t dst_copy_size = copy_frames * dst_frame_size;
+		const size_t src_copy_size = copy_frames * src_frame_size;
+
+		assert(src_copy_size <= audio_data_link_remain_size(alink));
+
+		/* Copy audio data */
+		pcm_format_convert_and_mix(data, dst_copy_size,
+		    audio_data_link_start(alink), src_copy_size,
+		    &alink->adata->format, f);
+
+		/* Update values */
+		needed_frames -= copy_frames;
+		copied_size += dst_copy_size;
+		data += dst_copy_size;
+		alink->position += src_copy_size;
+		pipe->bytes -= src_copy_size;
+		pipe->frames -= copy_frames;
+		if (audio_data_link_remain_size(alink) == 0) {
+			list_remove(&alink->link);
+			audio_data_link_destroy(alink);
+		} else {
+			assert(needed_frames == 0);
+		}
+	}
+	fibril_mutex_unlock(&pipe->guard);
+	return copied_size;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/audio_data.h
===================================================================
--- uspace/srv/audio/hound/audio_data.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/audio_data.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#ifndef AUDIO_DATA_H_
+#define AUDIO_DATA_H_
+
+#include <adt/list.h>
+#include <atomic.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <pcm/format.h>
+
+/** Reference counted audio buffer */
+typedef struct {
+	/** Audio data */
+	const void *data;
+	/** Size of the buffer pointer to by data */
+	size_t size;
+	/** Format of the audio data */
+	pcm_format_t format;
+	/** Reference counter */
+	atomic_t refcount;
+} audio_data_t;
+
+/** Audio data pipe structure */
+typedef struct {
+	/** List of audio data buffers */
+	list_t list;
+	/** Total size of all buffers */
+	size_t bytes;
+	/** Total frames stored in all buffers */
+	size_t frames;
+	/** List access synchronization */
+	fibril_mutex_t guard;
+} audio_pipe_t;
+
+audio_data_t * audio_data_create(const void *data, size_t size,
+    pcm_format_t format);
+void audio_data_addref(audio_data_t *adata);
+void audio_data_unref(audio_data_t *adata);
+
+void audio_pipe_init(audio_pipe_t *pipe);
+void audio_pipe_fini(audio_pipe_t *pipe);
+
+int audio_pipe_push(audio_pipe_t *pipe, audio_data_t *data);
+audio_data_t *audio_pipe_pop(audio_pipe_t *pipe);
+
+ssize_t audio_pipe_mix_data(audio_pipe_t *pipe, void *buffer, size_t size,
+    const pcm_format_t *f);
+
+/**
+ * Total bytes getter.
+ * @param pipe The audio pipe.
+ * @return Total size of buffer stored in the pipe.
+ */
+static inline size_t audio_pipe_bytes(audio_pipe_t *pipe)
+{
+	assert(pipe);
+	return pipe->bytes;
+}
+
+/**
+ * Total bytes getter.
+ * @param pipe The audio pipe.
+ * @return Total number of frames stored in the pipe.
+ */
+static inline size_t audio_pipe_frames(audio_pipe_t *pipe)
+{
+	assert(pipe);
+	return pipe->frames;
+}
+
+/**
+ * Push data form buffer directly to pipe.
+ * @param pipe The target pipe.
+ * @param data audio buffer.
+ * @param size size of the @p data buffer
+ * @param f format of the audio data.
+ * @return Error code.
+ *
+ * Reference counted buffer is created automatically.
+ */
+static inline int audio_pipe_push_data(audio_pipe_t *pipe,
+    const void *data, size_t size, pcm_format_t f)
+{
+	audio_data_t *adata = audio_data_create(data, size, f);
+	if (adata) {
+		const int ret = audio_pipe_push(pipe, adata);
+		audio_data_unref(adata);
+		return ret;
+	}
+	return ENOMEM;
+}
+
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/audio_device.c
===================================================================
--- uspace/srv/audio/hound/audio_device.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/audio_device.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server.
+ * @{
+ */
+/** @file
+ */
+
+#include <assert.h>
+#include <async.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <loc.h>
+#include <str.h>
+#include <str_error.h>
+
+
+#include "audio_device.h"
+#include "log.h"
+
+/* hardwired to provide ~21ms per fragment */
+#define BUFFER_PARTS   16
+
+static int device_sink_connection_callback(audio_sink_t *sink, bool new);
+static int device_source_connection_callback(audio_source_t *source, bool new);
+static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void *arg);
+static int device_check_format(audio_sink_t* sink);
+static int get_buffer(audio_device_t *dev);
+static int release_buffer(audio_device_t *dev);
+static void advance_buffer(audio_device_t *dev, size_t size);
+static inline bool is_running(audio_device_t *dev)
+{
+	assert(dev);
+	/* we release buffer on stop so this should be enough */
+	return (bool)dev->buffer.base;
+}
+
+/**
+ * Initialize audio device structure.
+ * @param dev The structure to initialize.
+ * @param id Location service id of the device driver.
+ * @param name Name of the device.
+ * @return Error code.
+ */
+int audio_device_init(audio_device_t *dev, service_id_t id, const char *name)
+{
+	assert(dev);
+	link_initialize(&dev->link);
+	dev->id = id;
+	dev->name = str_dup(name);
+	dev->sess = audio_pcm_open_service(id);
+	if (!dev->sess) {
+		log_debug("Failed to connect to device \"%s\"", name);
+		return ENOMEM;
+	}
+
+	audio_sink_init(&dev->sink, name, dev, device_sink_connection_callback,
+	    device_check_format, NULL, &AUDIO_FORMAT_ANY);
+	audio_source_init(&dev->source, name, dev,
+	    device_source_connection_callback, NULL, &AUDIO_FORMAT_ANY);
+
+	/* Init buffer members */
+	dev->buffer.base = NULL;
+	dev->buffer.position = NULL;
+	dev->buffer.size = 0;
+	dev->buffer.fragment_size = 0;
+
+	log_verbose("Initialized device (%p) '%s' with id %" PRIun ".",
+	    dev, dev->name, dev->id);
+
+	return EOK;
+}
+
+/**
+ * Restore resource cplaimed during initialization.
+ * @param dev The device to release.
+ *
+ * NOT IMPLEMENTED
+ */
+void audio_device_fini(audio_device_t *dev)
+{
+	//TODO implement;
+}
+
+/**
+ * Get device provided audio source.
+ * @param dev Th device.
+ * @return pointer to aa audio source structure, NULL if the device is not
+ *         capable of capturing audio.
+ */
+audio_source_t * audio_device_get_source(audio_device_t *dev)
+{
+	assert(dev);
+	if (audio_pcm_query_cap(dev->sess, AUDIO_CAP_CAPTURE))
+		return &dev->source;
+	return NULL;
+}
+
+/**
+ * Get device provided audio sink.
+ * @param dev Th device.
+ * @return pointer to aa audio source structure, NULL if the device is not
+ *         capable of audio playback.
+ */
+audio_sink_t * audio_device_get_sink(audio_device_t *dev)
+{
+	assert(dev);
+	if (audio_pcm_query_cap(dev->sess, AUDIO_CAP_PLAYBACK))
+		return &dev->sink;
+	return NULL;
+}
+
+/**
+ * Handle connection addition and removal.
+ * @param sink audio sink that is connected or disconnected.
+ * @param new True of a connection was added, false otherwise.
+ * @return Error code.
+ *
+ * Starts playback on first connection. Stops playback when there are no
+ * connections.
+ */
+static int device_sink_connection_callback(audio_sink_t* sink, bool new)
+{
+	assert(sink);
+	audio_device_t *dev = sink->private_data;
+	if (new && list_count(&sink->connections) == 1) {
+		log_verbose("First connection on device sink '%s'", sink->name);
+
+		int ret = get_buffer(dev);
+		if (ret != EOK) {
+			log_error("Failed to get device buffer: %s",
+			    str_error(ret));
+			return ret;
+		}
+		audio_pcm_register_event_callback(dev->sess,
+		    device_event_callback, dev);
+
+		/* Fill the buffer first. Fill the first two fragments,
+		 * so that we stay one fragment ahead */
+		pcm_format_silence(dev->buffer.base, dev->buffer.size,
+		    &dev->sink.format);
+		//TODO add underrun detection.
+		const size_t size = dev->buffer.fragment_size * 2;
+		/* We never cross the end of the buffer here */
+		audio_sink_mix_inputs(&dev->sink, dev->buffer.position, size);
+		advance_buffer(dev, size);
+
+		const unsigned frames = dev->buffer.fragment_size /
+		    pcm_format_frame_size(&dev->sink.format);
+		log_verbose("Fragment frame count %u", frames);
+		ret = audio_pcm_start_playback_fragment(dev->sess, frames,
+		    dev->sink.format.channels, dev->sink.format.sampling_rate,
+		    dev->sink.format.sample_format);
+		if (ret != EOK) {
+			log_error("Failed to start playback: %s",
+			    str_error(ret));
+			release_buffer(dev);
+			return ret;
+		}
+	}
+	if (list_count(&sink->connections) == 0) {
+		assert(!new);
+		log_verbose("Removed last connection on device sink '%s'",
+		    sink->name);
+		int ret = audio_pcm_stop_playback(dev->sess);
+		if (ret != EOK) {
+			log_error("Failed to stop playback: %s",
+			    str_error(ret));
+			return ret;
+		}
+	}
+	return EOK;
+}
+
+/**
+ * Handle connection addition and removal.
+ * @param source audio source that is connected or disconnected.
+ * @param new True of a connection was added, false otherwise.
+ * @return Error code.
+ *
+ * Starts capture on first connection. Stops capture when there are no
+ * connections.
+ */
+static int device_source_connection_callback(audio_source_t *source, bool new)
+{
+	assert(source);
+	audio_device_t *dev = source->private_data;
+	if (new && list_count(&source->connections) == 1) {
+		int ret = get_buffer(dev);
+		if (ret != EOK) {
+			log_error("Failed to get device buffer: %s",
+			    str_error(ret));
+			return ret;
+		}
+
+		//TODO set and test format
+
+		const unsigned frames = dev->buffer.fragment_size /
+		    pcm_format_frame_size(&dev->sink.format);
+		ret = audio_pcm_start_capture_fragment(dev->sess, frames,
+		    dev->source.format.channels,
+		    dev->source.format.sampling_rate,
+		    dev->source.format.sample_format);
+		if (ret != EOK) {
+			log_error("Failed to start recording: %s",
+			    str_error(ret));
+			release_buffer(dev);
+			return ret;
+		}
+	}
+	if (list_count(&source->connections) == 0) { /* Disconnected */
+		assert(!new);
+		int ret = audio_pcm_stop_capture_immediate(dev->sess);
+		if (ret != EOK) {
+			log_error("Failed to start recording: %s",
+			    str_error(ret));
+			return ret;
+		}
+	}
+
+	return EOK;
+}
+
+/**
+ * Audio device event handler.
+ * @param iid initial call id.
+ * @param icall initial call structure.
+ * @param arg (unused)
+ */
+static void device_event_callback(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	/* Answer initial request */
+	async_answer_0(iid, EOK);
+	audio_device_t *dev = arg;
+	assert(dev);
+	while (1) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		async_answer_0(callid, EOK);
+		switch(IPC_GET_IMETHOD(call)) {
+		case PCM_EVENT_FRAMES_PLAYED: {
+			struct timeval time1;
+			getuptime(&time1);
+			//TODO add underrun detection.
+			/* We never cross the end of the buffer here */
+			audio_sink_mix_inputs(&dev->sink, dev->buffer.position,
+			    dev->buffer.fragment_size);
+			advance_buffer(dev, dev->buffer.fragment_size);
+			struct timeval time2;
+			getuptime(&time2);
+			log_verbose("Time to mix sources: %li\n",
+			    tv_sub(&time2, &time1));
+			break;
+		}
+		case PCM_EVENT_CAPTURE_TERMINATED: {
+			log_verbose("Capture terminated");
+			dev->source.format = AUDIO_FORMAT_ANY;
+			const int ret = release_buffer(dev);
+			if (ret != EOK) {
+				log_error("Failed to release buffer: %s",
+				    str_error(ret));
+			}
+			audio_pcm_unregister_event_callback(dev->sess);
+			break;
+		}
+		case PCM_EVENT_PLAYBACK_TERMINATED: {
+			log_verbose("Playback Terminated");
+			dev->sink.format = AUDIO_FORMAT_ANY;
+			const int ret = release_buffer(dev);
+			if (ret != EOK) {
+				log_error("Failed to release buffer: %s",
+				    str_error(ret));
+			}
+			audio_pcm_unregister_event_callback(dev->sess);
+			break;
+		}
+		case PCM_EVENT_FRAMES_CAPTURED: {
+			const int ret = audio_source_push_data(&dev->source,
+			    dev->buffer.position, dev->buffer.fragment_size);
+			advance_buffer(dev, dev->buffer.fragment_size);
+			if (ret != EOK)
+				log_warning("Failed to push recorded data");
+			break;
+		}
+		case 0:
+			log_info("Device event callback hangup");
+			return;
+		}
+
+	}
+}
+
+/**
+ * Test format against hardware limits.
+ * @param sink audio playback device.
+ * @return Error code.
+ */
+static int device_check_format(audio_sink_t* sink)
+{
+	assert(sink);
+	audio_device_t *dev = sink->private_data;
+	assert(dev);
+	/* Check whether we are running */
+	if (is_running(dev))
+		return EBUSY;
+	log_verbose("Checking format on sink %s", sink->name);
+	return audio_pcm_test_format(dev->sess, &sink->format.channels,
+	    &sink->format.sampling_rate, &sink->format.sample_format);
+}
+
+/**
+ * Get access to device buffer.
+ * @param dev Audio device.
+ * @return Error code.
+ */
+static int get_buffer(audio_device_t *dev)
+{
+	assert(dev);
+	if (!dev->sess) {
+		log_debug("No connection to device");
+		return EIO;
+	}
+	if (dev->buffer.base) {
+		log_debug("We already have a buffer");
+		return EBUSY;
+	}
+
+	/* Ask for largest buffer possible */
+	size_t preferred_size = 0;
+
+	const int ret = audio_pcm_get_buffer(dev->sess, &dev->buffer.base,
+	    &preferred_size);
+	if (ret == EOK) {
+		dev->buffer.size = preferred_size;
+		dev->buffer.fragment_size = dev->buffer.size / BUFFER_PARTS;
+		dev->buffer.position = dev->buffer.base;
+	}
+	return ret;
+
+}
+
+/**
+ * Surrender access to device buffer.
+ * @param dev Audio device.
+ * @return Error code.
+ */
+static int release_buffer(audio_device_t *dev)
+{
+	assert(dev);
+	assert(dev->buffer.base);
+
+	const int ret = audio_pcm_release_buffer(dev->sess);
+	if (ret == EOK) {
+		dev->buffer.base = NULL;
+		dev->buffer.size = 0;
+		dev->buffer.position = NULL;
+	} else {
+		log_warning("Failed to release buffer: %s", str_error(ret));
+	}
+	return ret;
+}
+
+/**
+ * Move buffer position pointer.
+ * @param dev Audio device.
+ * @param size number of bytes to move forward
+ */
+static void advance_buffer(audio_device_t *dev, size_t size)
+{
+	assert(dev);
+	assert(dev->buffer.position >= dev->buffer.base);
+	assert(dev->buffer.position < (dev->buffer.base + dev->buffer.size));
+	dev->buffer.position += size;
+	if (dev->buffer.position == (dev->buffer.base + dev->buffer.size))
+		dev->buffer.position = dev->buffer.base;
+}
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/audio_device.h
===================================================================
--- uspace/srv/audio/hound/audio_device.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/audio_device.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#ifndef AUDIO_DEVICE_H_
+#define AUDIO_DEVICE_H_
+
+#include <adt/list.h>
+#include <async.h>
+#include <fibril_synch.h>
+#include <errno.h>
+#include <ipc/loc.h>
+#include <audio_pcm_iface.h>
+
+#include "audio_source.h"
+#include "audio_sink.h"
+
+/** Audio device structure. */
+typedef struct {
+	/** Link in hound's device list */
+	link_t link;
+	/** Location service id of the audio driver */
+	service_id_t id;
+	/** IPC session to the device driver. */
+	audio_pcm_sess_t *sess;
+	/** Device name */
+	char *name;
+	/** Device buffer */
+	struct {
+		void *base;
+		size_t size;
+		void *position;
+		size_t fragment_size;
+	} buffer;
+	/** Capture device abstraction. */
+	audio_source_t source;
+	/** Playback device abstraction. */
+	audio_sink_t sink;
+} audio_device_t;
+
+/** Linked list instance helper */
+static inline audio_device_t * audio_device_list_instance(link_t *l)
+{
+	return l ? list_get_instance(l, audio_device_t, link) : NULL;
+};
+
+int audio_device_init(audio_device_t *dev, service_id_t id, const char *name);
+void audio_device_fini(audio_device_t *dev);
+audio_source_t * audio_device_get_source(audio_device_t *dev);
+audio_sink_t * audio_device_get_sink(audio_device_t *dev);
+int audio_device_recorded_data(audio_device_t *dev, void **base, size_t *size);
+int audio_device_available_buffer(audio_device_t *dev, void **base, size_t *size);
+
+#endif
+
+/**
+ * @}
+ */
+
Index: uspace/srv/audio/hound/audio_sink.c
===================================================================
--- uspace/srv/audio/hound/audio_sink.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/audio_sink.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <str.h>
+#include <str_error.h>
+
+#include "audio_sink.h"
+#include "connection.h"
+#include "log.h"
+
+
+/**
+ * Initialize audio sink structure.
+ * @param sink The structure to initialize.
+ * @param name string identifier
+ * @param private_data backend data
+ * @param connection_change connect/disconnect callback
+ * @param check_format format testing callback
+ * @param data_available trigger data prcoessing
+ * @param f sink format
+ * @return Error code.
+ */
+int audio_sink_init(audio_sink_t *sink, const char *name, void *private_data,
+    int (*connection_change)(audio_sink_t *, bool),
+    int (*check_format)(audio_sink_t *), int (*data_available)(audio_sink_t *),
+    const pcm_format_t *f)
+{
+	assert(sink);
+	if (!name)
+		return EINVAL;
+	link_initialize(&sink->link);
+	list_initialize(&sink->connections);
+	sink->name = str_dup(name);
+	if (!sink->name)
+		return ENOMEM;
+	sink->private_data = private_data;
+	sink->format = *f;
+	sink->connection_change = connection_change;
+	sink->check_format = check_format;
+	sink->data_available = data_available;
+	log_verbose("Initialized sink (%p) '%s'", sink, sink->name);
+	return EOK;
+}
+
+/**
+ * Release resources claimed by initialization.
+ * @param sink the structure to release.
+ */
+void audio_sink_fini(audio_sink_t *sink)
+{
+	assert(sink);
+	assert(list_empty(&sink->connections));
+	assert(!sink->private_data);
+	free(sink->name);
+	sink->name = NULL;
+}
+
+/**
+ * Set audio sink format and check with backend,
+ * @param sink The target sink isntance.
+ * @param format Th new format.
+ * @return Error code.
+ */
+int audio_sink_set_format(audio_sink_t *sink, const pcm_format_t *format)
+{
+	assert(sink);
+	assert(format);
+	if (!pcm_format_is_any(&sink->format)) {
+		log_debug("Sink %s already has a format", sink->name);
+		return EEXISTS;
+	}
+	const pcm_format_t old_format = sink->format;
+
+	if (pcm_format_is_any(format)) {
+		log_verbose("Setting DEFAULT format for sink %s", sink->name);
+		sink->format = AUDIO_FORMAT_DEFAULT;
+	} else {
+		sink->format = *format;
+	}
+	if (sink->check_format) {
+		const int ret = sink->check_format(sink);
+		if (ret != EOK && ret != ELIMIT) {
+			log_debug("Format check failed on sink %s", sink->name);
+			sink->format = old_format;
+			return ret;
+		}
+	}
+	log_verbose("Set format for sink %s: %u channel(s), %uHz, %s",
+	    sink->name, format->channels, format->sampling_rate,
+	    pcm_sample_format_str(format->sample_format));
+	return EOK;
+}
+
+/**
+ * Pull data from all connections and add the mix to the destination.
+ * @param sink The sink to mix data.
+ * @param dest Destination buffer.
+ * @param size size of the @p dest buffer.
+ * @return Error code.
+ */
+void audio_sink_mix_inputs(audio_sink_t *sink, void* dest, size_t size)
+{
+	assert(sink);
+	assert(dest);
+
+	pcm_format_silence(dest, size, &sink->format);
+	list_foreach(sink->connections, sink_link, connection_t, conn) {
+		const int ret = connection_add_source_data(
+		    conn, dest, size, sink->format);
+		if (ret != EOK) {
+			log_warning("Failed to mix source %s: %s",
+			    connection_source_name(conn), str_error(ret));
+		}
+	}
+}
+
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/audio_sink.h
===================================================================
--- uspace/srv/audio/hound/audio_sink.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/audio_sink.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#ifndef AUDIO_SINK_H_
+#define AUDIO_SINK_H_
+
+#include <adt/list.h>
+#include <async.h>
+#include <stdbool.h>
+#include <fibril.h>
+#include <pcm/format.h>
+
+#include "audio_source.h"
+
+typedef struct audio_sink audio_sink_t;
+
+/** Audio sink abstraction structure */
+struct audio_sink {
+	/** Link in hound's sink list */
+	link_t link;
+	/** List of all related connections */
+	list_t connections;
+	/** Sink's name */
+	const char *name;
+	/** Consumes data in this format */
+	pcm_format_t format;
+	/** Backend data */
+	void *private_data;
+	/** Connect/disconnect callback */
+	int (*connection_change)(audio_sink_t *, bool);
+	/** Backend callback to check data */
+	int (*check_format)(audio_sink_t *);
+	/** new data notifier */
+	int (*data_available)(audio_sink_t *);
+};
+
+/**
+ * List instance helper.
+ * @param l link
+ * @return pointer to a sink structure, NULL on failure.
+ */
+static inline audio_sink_t * audio_sink_list_instance(link_t *l)
+{
+	return l ? list_get_instance(l, audio_sink_t, link) : NULL;
+}
+
+int audio_sink_init(audio_sink_t *sink, const char *name, void *private_data,
+    int (*connection_change)(audio_sink_t *, bool),
+    int (*check_format)(audio_sink_t *), int (*data_available)(audio_sink_t *),
+    const pcm_format_t *f);
+
+void audio_sink_fini(audio_sink_t *sink);
+int audio_sink_set_format(audio_sink_t *sink, const pcm_format_t *format);
+void audio_sink_mix_inputs(audio_sink_t *sink, void* dest, size_t size);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/audio_source.c
===================================================================
--- uspace/srv/audio/hound/audio_source.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/audio_source.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <macros.h>
+#include <stdlib.h>
+#include <str.h>
+#include <str_error.h>
+
+#include "audio_data.h"
+#include "audio_source.h"
+#include "audio_sink.h"
+#include "connection.h"
+#include "log.h"
+
+/**
+ * Initialize audio source strcture.
+ * @param source The structure to initialize.
+ * @param name String identifier of the audio source.
+ * @param data Backend data.
+ * @param connection_change Connect/disconnect callback.
+ * @param update_available_data Data request callback.
+ * @return Error code.
+ */
+int audio_source_init(audio_source_t *source, const char *name, void *data,
+    int (*connection_change)(audio_source_t *, bool new),
+    int (*update_available_data)(audio_source_t *, size_t),
+    const pcm_format_t *f)
+{
+	assert(source);
+	if (!name || !f) {
+		log_debug("Incorrect parameters.");
+		return EINVAL;
+	}
+	link_initialize(&source->link);
+	list_initialize(&source->connections);
+	source->name = str_dup(name);
+	if (!source->name)
+		return ENOMEM;
+	source->private_data = data;
+	source->connection_change = connection_change;
+	source->update_available_data = update_available_data;
+	source->format = *f;
+	log_verbose("Initialized source (%p) '%s'", source, source->name);
+	return EOK;
+}
+
+/**
+ * Release resources claimed by initialization.
+ * @param source The structure to cleanup.
+ */
+void audio_source_fini(audio_source_t *source)
+{
+	assert(source);
+	free(source->name);
+	source->name = NULL;
+}
+/**
+ * Push data to all connections.
+ * @param source The source of the data.
+ * @param dest Destination buffer.
+ * @param size size of the @p dest buffer.
+ * @return Error code.
+ */
+int audio_source_push_data(audio_source_t *source, const void *data,
+    size_t size)
+{
+	assert(source);
+	assert(data);
+
+	audio_data_t *adata = audio_data_create(data, size, source->format);
+	if (!adata)
+		return ENOMEM;
+
+	list_foreach(source->connections, source_link, connection_t, conn) {
+		const int ret = connection_push_data(conn, adata);
+		if (ret != EOK) {
+			log_warning("Failed push data to %s: %s",
+			    connection_sink_name(conn), str_error(ret));
+		}
+	}
+	audio_data_unref(adata);
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/audio_source.h
===================================================================
--- uspace/srv/audio/hound/audio_source.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/audio_source.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#ifndef AUDIO_SOURCE_H_
+#define AUDIO_SOURCE_H_
+
+#include <adt/list.h>
+#include <pcm/format.h>
+
+typedef struct audio_source audio_source_t;
+
+/** Audio data source abstraction structure */
+struct audio_source {
+	/** Link in hound's sources list */
+	link_t link;
+	/** List of connections */
+	list_t connections;
+	/** String identifier */
+	const char *name;
+	/** audio data format */
+	pcm_format_t format;
+	/** backend data */
+	void *private_data;
+	/** Callback for connection and disconnection */
+	int (*connection_change)(audio_source_t *source, bool added);
+	/** Ask backend for more data */
+	int (*update_available_data)(audio_source_t *source, size_t size);
+};
+
+/**
+ * List instance helper.
+ * @param l link
+ * @return pointer to a source structure, NULL on failure.
+ */
+static inline audio_source_t * audio_source_list_instance(link_t *l)
+{
+	return l ? list_get_instance(l, audio_source_t, link) : NULL;
+}
+
+int audio_source_init(audio_source_t *source, const char *name, void *data,
+    int (*connection_change)(audio_source_t *, bool),
+    int (*update_available_data)(audio_source_t *, size_t),
+    const pcm_format_t *f);
+void audio_source_fini(audio_source_t *source);
+int audio_source_push_data(audio_source_t *source, const void *data,
+    size_t size);
+static inline const pcm_format_t *audio_source_format(const audio_source_t *s)
+{
+	assert(s);
+	return &s->format;
+}
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/connection.c
===================================================================
--- uspace/srv/audio/hound/connection.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/connection.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2013 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#include <malloc.h>
+#include <errno.h>
+
+#include "log.h"
+#include "connection.h"
+
+/**
+ * Create connection between source and sink.
+ * @param source Valid source structure.
+ * @param sink Valid sink structure.
+ * @return pointer to a valid connection structure, NULL on failure.
+ *
+ * Reports new connection to both the source and sink.
+ */
+connection_t *connection_create(audio_source_t *source, audio_sink_t *sink)
+{
+	assert(source);
+	assert(sink);
+	connection_t *conn = malloc(sizeof(connection_t));
+	if (conn) {
+		audio_pipe_init(&conn->fifo);
+		link_initialize(&conn->source_link);
+		link_initialize(&conn->sink_link);
+		link_initialize(&conn->hound_link);
+		conn->sink = sink;
+		conn->source = source;
+		list_append(&conn->source_link, &source->connections);
+		list_append(&conn->sink_link, &sink->connections);
+		audio_sink_set_format(sink, audio_source_format(source));
+		if (source->connection_change)
+			source->connection_change(source, true);
+		if (sink->connection_change)
+			sink->connection_change(sink, true);
+		log_debug("CONNECTED: %s -> %s", source->name, sink->name);
+	}
+	return conn;
+}
+
+/**
+ * Destroy existing connection
+ * @param connection The connection to destroy.
+ *
+ * Disconnects from both the source and the sink.
+ */
+void connection_destroy(connection_t *connection)
+{
+	assert(connection);
+	assert(!link_in_use(&connection->hound_link));
+	list_remove(&connection->source_link);
+	list_remove(&connection->sink_link);
+	if (connection->sink && connection->sink->connection_change)
+		connection->sink->connection_change(connection->sink, false);
+	if (connection->source && connection->source->connection_change)
+		connection->source->connection_change(connection->source, false);
+	audio_pipe_fini(&connection->fifo);
+	log_debug("DISCONNECTED: %s -> %s",
+	    connection->source->name, connection->sink->name);
+	free(connection);
+}
+
+/**
+ * Update and mix data provided by the source.
+ * @param connection the connection to add.
+ * @param data Destination audio buffer.
+ * @param size size of the destination audio buffer.
+ * @param format format of the destination audio buffer.
+ */
+ssize_t connection_add_source_data(connection_t *connection, void *data,
+    size_t size, pcm_format_t format)
+{
+	assert(connection);
+	if (!data)
+		return EBADMEM;
+	const size_t needed_frames = pcm_format_size_to_frames(size, &format);
+	if (needed_frames > audio_pipe_frames(&connection->fifo) &&
+	    connection->source->update_available_data) {
+		log_debug("Asking source to provide more data");
+		connection->source->update_available_data(
+		    connection->source, size);
+	}
+	log_verbose("Data available after update: %zu",
+	    audio_pipe_bytes(&connection->fifo));
+	ssize_t ret =
+	    audio_pipe_mix_data(&connection->fifo, data, size, &format);
+	if (ret != (ssize_t)size)
+		log_warning("Connection failed to provide enough data %zd/%zu",
+		    ret, size);
+	return ret > 0 ? EOK : ret;
+}
+/**
+ * Add new data to the connection buffer.
+ * @param connection Target conneciton.
+ * @aparam adata Reference counted audio data buffer.
+ * @return Error code.
+ */
+int connection_push_data(connection_t *connection,
+    audio_data_t *adata)
+{
+	assert(connection);
+	assert(adata);
+	const int ret = audio_pipe_push(&connection->fifo, adata);
+	if (ret == EOK && connection->sink->data_available)
+		connection->sink->data_available(connection->sink);
+	return ret;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/connection.h
===================================================================
--- uspace/srv/audio/hound/connection.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/connection.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2013 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#ifndef CONNECTION_H_
+#define CONNECTION_H_
+
+#include <assert.h>
+#include <adt/list.h>
+#include <pcm/format.h>
+
+#include "audio_data.h"
+#include "audio_source.h"
+#include "audio_sink.h"
+
+/** Source->sink connection structure */
+typedef struct {
+	/** Source's connections link */
+	link_t source_link;
+	/** Sink's connections link */
+	link_t sink_link;
+	/** Hound's connections link */
+	link_t hound_link;
+	/** audio data pipe */
+	audio_pipe_t fifo;
+	/** Target sink */
+	audio_sink_t *sink;
+	/** Target source */
+	audio_source_t *source;
+} connection_t;
+
+/**
+ * List instance helper.
+ * @param l link
+ * @return pointer to a connection structure, NULL on failure.
+ */
+static inline connection_t * connection_from_source_list(link_t *l)
+{
+	return l ? list_get_instance(l, connection_t, source_link) : NULL;
+}
+
+/**
+ * List instance helper.
+ * @param l link
+ * @return pointer to a connection structure, NULL on failure.
+ */
+static inline connection_t * connection_from_hound_list(link_t *l)
+{
+	return l ? list_get_instance(l, connection_t, hound_link) : NULL;
+}
+
+connection_t *connection_create(audio_source_t *source, audio_sink_t *sink);
+void connection_destroy(connection_t *connection);
+
+ssize_t connection_add_source_data(connection_t *connection, void *data,
+    size_t size, pcm_format_t format);
+
+int connection_push_data(connection_t *connection, audio_data_t *adata);
+
+/**
+ * Source name getter.
+ * @param connection Connection to the source.
+ * @param valid string identifier, "no source" or "unnamed source" on failure.
+ */
+static inline const char *connection_source_name(connection_t *connection)
+{
+	assert(connection);
+	if (connection->source && connection->source->name)
+		return connection->source->name;
+	// TODO assert?
+	return connection->source ? "unnamed source" : "no source";
+}
+
+/**
+ * Sink name getter.
+ * @param connection Connection to the sink.
+ * @param valid string identifier, "no source" or "unnamed source" on failure.
+ */
+static inline const char *connection_sink_name(connection_t *connection)
+{
+	assert(connection);
+	if (connection->sink && connection->sink->name)
+		return connection->sink->name;
+	// TODO assert?
+	return connection->source ? "unnamed sink" : "no sink";
+}
+
+
+#endif
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/hound.c
===================================================================
--- uspace/srv/audio/hound/hound.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/hound.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server.
+ * @{
+ */
+/** @file
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "hound.h"
+#include "audio_device.h"
+#include "audio_sink.h"
+#include "audio_source.h"
+#include "connection.h"
+#include "log.h"
+#include "errno.h"
+#include "str_error.h"
+
+/**
+ * Search devices by name.
+ * @param name String identifier.
+ * @return Pointer to the found device, NULL on failure.
+ */
+static audio_device_t *find_device_by_name(list_t *list, const char *name)
+{
+	assert(list);
+	assert(name);
+
+	list_foreach(*list, link, audio_device_t, dev) {
+		if (str_cmp(name, dev->name) == 0) {
+			log_debug("device with name '%s' is in the list",
+			    name);
+			return dev;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * Search sources by name.
+ * @param name String identifier.
+ * @return Pointer to the found source, NULL on failure.
+ */
+static audio_source_t *find_source_by_name(list_t *list, const char *name)
+{
+	assert(list);
+	assert(name);
+
+	list_foreach(*list, link, audio_source_t, dev) {
+		if (str_cmp(name, dev->name) == 0) {
+			log_debug("source with name '%s' is in the list",
+			    name);
+			return dev;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * Search sinks by name.
+ * @param name String identifier.
+ * @return Pointer to the found sink, NULL on failure.
+ */
+static audio_sink_t *find_sink_by_name(list_t *list, const char *name)
+{
+	assert(list);
+	assert(name);
+
+	list_foreach(*list, link, audio_sink_t, dev) {
+		if (str_cmp(name, dev->name) == 0) {
+			log_debug("sink with name '%s' is in the list",
+			    name);
+			return dev;
+		}
+	}
+
+	return NULL;
+}
+
+static int hound_disconnect_internal(hound_t *hound, const char* source_name, const char* sink_name);
+
+/**
+ * Remove provided sink.
+ * @param hound The hound structure.
+ * @param sink Target sink to remove.
+ *
+ * This function has to be called with the list_guard lock held.
+ */
+static void hound_remove_sink_internal(hound_t *hound, audio_sink_t *sink)
+{
+	assert(hound);
+	assert(sink);
+	assert(fibril_mutex_is_locked(&hound->list_guard));
+	log_verbose("Removing sink '%s'.", sink->name);
+	if (!list_empty(&sink->connections))
+		log_warning("Removing sink '%s' while still connected.", sink->name);
+	while (!list_empty(&sink->connections)) {
+		connection_t *conn = list_get_instance(
+		    list_first(&sink->connections), connection_t, sink_link);
+		list_remove(&conn->hound_link);
+		connection_destroy(conn);
+	}
+	list_remove(&sink->link);
+}
+
+/**
+ * Remove provided source.
+ * @param hound The hound structure.
+ * @param sink Target source to remove.
+ *
+ * This function has to be called with the guard lock held.
+ */
+static void hound_remove_source_internal(hound_t *hound, audio_source_t *source)
+{
+	log_verbose("Removing source '%s'.", source->name);
+	if (!list_empty(&source->connections))
+		log_warning("Removing source '%s' while still connected.", source->name);
+	while (!list_empty(&source->connections)) {
+		connection_t *conn = list_get_instance(
+		    list_first(&source->connections), connection_t, source_link);
+		list_remove(&conn->hound_link);
+		connection_destroy(conn);
+	}
+	list_remove(&source->link);
+}
+
+/**
+ * Initialize hound structure.
+ * @param hound The structure to initialize.
+ * @return Error code.
+ */
+int hound_init(hound_t *hound)
+{
+	assert(hound);
+	fibril_mutex_initialize(&hound->list_guard);
+	list_initialize(&hound->devices);
+	list_initialize(&hound->contexts);
+	list_initialize(&hound->sources);
+	list_initialize(&hound->sinks);
+	list_initialize(&hound->connections);
+	return EOK;
+}
+
+/**
+ * Add a new application context.
+ * @param hound Hound structure.
+ * @param ctx Context to add.
+ * @return Error code.
+ */
+int hound_add_ctx(hound_t *hound, hound_ctx_t *ctx)
+{
+	log_info("Trying to add context %p", ctx);
+	assert(hound);
+	if (!ctx)
+		return EINVAL;
+	fibril_mutex_lock(&hound->list_guard);
+	list_append(&ctx->link, &hound->contexts);
+	fibril_mutex_unlock(&hound->list_guard);
+	int ret = EOK;
+	if (ret == EOK && ctx->source)
+		ret = hound_add_source(hound, ctx->source);
+	if (ret == EOK && ctx->sink)
+		ret = hound_add_sink(hound, ctx->sink);
+	if (ret != EOK) {
+		fibril_mutex_lock(&hound->list_guard);
+		list_remove(&ctx->link);
+		fibril_mutex_unlock(&hound->list_guard);
+	}
+	return ret;
+}
+
+/**
+ * Remove existing application context.
+ * @param hound Hound structure.
+ * @param ctx Context to remove.
+ * @return Error code.
+ */
+int hound_remove_ctx(hound_t *hound, hound_ctx_t *ctx)
+{
+	assert(hound);
+	if (!ctx)
+		return EINVAL;
+	if (!list_empty(&ctx->streams))
+		return EBUSY;
+	fibril_mutex_lock(&hound->list_guard);
+	list_remove(&ctx->link);
+	if (ctx->source)
+		hound_remove_source_internal(hound, ctx->source);
+	if (ctx->sink)
+		hound_remove_sink_internal(hound, ctx->sink);
+	fibril_mutex_unlock(&hound->list_guard);
+	return EOK;
+}
+
+/**
+ * Search registered contexts for the matching id.
+ * @param hound The hound structure.
+ * @param id Requested id.
+ * @return Pointer to the found structure, NULL on failure.
+ */
+hound_ctx_t *hound_get_ctx_by_id(hound_t *hound, hound_context_id_t id)
+{
+	assert(hound);
+
+	fibril_mutex_lock(&hound->list_guard);
+	hound_ctx_t *res = NULL;
+	list_foreach(hound->contexts, link, hound_ctx_t, ctx) {
+		if (hound_ctx_get_id(ctx) == id) {
+			res = ctx;
+			break;
+		}
+	}
+	fibril_mutex_unlock(&hound->list_guard);
+	return res;
+}
+
+/**
+ * Add a new device.
+ * @param hound The hound structure.
+ * @param id Locations service id representing the device driver.
+ * @param name String identifier.
+ * @return Error code.
+ */
+int hound_add_device(hound_t *hound, service_id_t id, const char *name)
+{
+	log_verbose("Adding device \"%s\", service: %zu", name, id);
+
+	assert(hound);
+	if (!name || !id) {
+		log_debug("Incorrect parameters.");
+		return EINVAL;
+	}
+
+	list_foreach(hound->devices, link, audio_device_t, dev) {
+		if (dev->id == id) {
+			log_debug("Device with id %zu is already present", id);
+			return EEXISTS;
+		}
+	}
+
+	audio_device_t *dev = find_device_by_name(&hound->devices, name);
+	if (dev) {
+		log_debug("Device with name %s is already present", name);
+		return EEXISTS;
+	}
+
+	dev = malloc(sizeof(audio_device_t));
+	if (!dev) {
+		log_debug("Failed to malloc device structure.");
+		return ENOMEM;
+	}
+
+	const int ret = audio_device_init(dev, id, name);
+	if (ret != EOK) {
+		log_debug("Failed to initialize new audio device: %s",
+			str_error(ret));
+		free(dev);
+		return ret;
+	}
+
+	list_append(&dev->link, &hound->devices);
+	log_info("Added new device: '%s'", dev->name);
+
+	audio_source_t *source = audio_device_get_source(dev);
+	if (source) {
+		const int ret = hound_add_source(hound, source);
+		if (ret != EOK) {
+			log_debug("Failed to add device source: %s",
+			    str_error(ret));
+			audio_device_fini(dev);
+			return ret;
+		}
+		log_verbose("Added source: '%s'.", source->name);
+	}
+
+	audio_sink_t *sink = audio_device_get_sink(dev);
+	if (sink) {
+		const int ret = hound_add_sink(hound, sink);
+		if (ret != EOK) {
+			log_debug("Failed to add device sink: %s",
+			    str_error(ret));
+			audio_device_fini(dev);
+			return ret;
+		}
+		log_verbose("Added sink: '%s'.", sink->name);
+	}
+
+	if (!source && !sink)
+		log_warning("Neither sink nor source on device '%s'.", name);
+
+	return ret;
+}
+
+/**
+ * Register a new source.
+ * @param hound The hound structure.
+ * @param source A new source to add.
+ * @return Error code.
+ */
+int hound_add_source(hound_t *hound, audio_source_t *source)
+{
+	assert(hound);
+	if (!source || !source->name || str_cmp(source->name, "default") == 0) {
+		log_debug("Invalid source specified.");
+		return EINVAL;
+	}
+	fibril_mutex_lock(&hound->list_guard);
+	if (find_source_by_name(&hound->sources, source->name)) {
+		log_debug("Source by that name already exists");
+		fibril_mutex_unlock(&hound->list_guard);
+		return EEXISTS;
+	}
+	list_append(&source->link, &hound->sources);
+	fibril_mutex_unlock(&hound->list_guard);
+	return EOK;
+}
+
+/**
+ * Register a new sink.
+ * @param hound The hound structure.
+ * @param sink A new sink to add.
+ * @return Error code.
+ */
+int hound_add_sink(hound_t *hound, audio_sink_t *sink)
+{
+	assert(hound);
+	if (!sink || !sink->name || str_cmp(sink->name, "default") == 0) {
+		log_debug("Invalid source specified.");
+		return EINVAL;
+	}
+	fibril_mutex_lock(&hound->list_guard);
+	if (find_sink_by_name(&hound->sinks, sink->name)) {
+		log_debug("Sink by that name already exists");
+		fibril_mutex_unlock(&hound->list_guard);
+		return EEXISTS;
+	}
+	list_append(&sink->link, &hound->sinks);
+	fibril_mutex_unlock(&hound->list_guard);
+	return EOK;
+}
+
+/**
+ * Remove a registered source.
+ * @param hound The hound structure.
+ * @param source A registered source to remove.
+ * @return Error code.
+ */
+int hound_remove_source(hound_t *hound, audio_source_t *source)
+{
+	assert(hound);
+	if (!source)
+		return EINVAL;
+	fibril_mutex_lock(&hound->list_guard);
+	hound_remove_source_internal(hound, source);
+	fibril_mutex_unlock(&hound->list_guard);
+	return EOK;
+}
+
+/**
+ * Remove a registered sink.
+ * @param hound The hound structure.
+ * @param sink A registered sink to remove.
+ * @return Error code.
+ */
+int hound_remove_sink(hound_t *hound, audio_sink_t *sink)
+{
+	assert(hound);
+	if (!sink)
+		return EINVAL;
+	fibril_mutex_lock(&hound->list_guard);
+	hound_remove_sink_internal(hound, sink);
+	fibril_mutex_unlock(&hound->list_guard);
+	return EOK;
+}
+
+/**
+ * List all registered sources.
+ * @param[in] hound The hound structure.
+ * @param[out] list List of the string identifiers.
+ * @param[out] size Number of identifiers int he @p list.
+ * @return Error code.
+ */
+int hound_list_sources(hound_t *hound, const char ***list, size_t *size)
+{
+	assert(hound);
+	if (!list || !size)
+		return EINVAL;
+
+	fibril_mutex_lock(&hound->list_guard);
+	const size_t count = list_count(&hound->sources);
+	if (count == 0) {
+		*list = NULL;
+		*size = 0;
+		fibril_mutex_unlock(&hound->list_guard);
+		return EOK;
+	}
+	const char **names = calloc(count, sizeof(char *));
+	int ret = names ? EOK : ENOMEM;
+	for (size_t i = 0; i < count && ret == EOK; ++i) {
+		link_t *slink = list_nth(&hound->sources, i);
+		audio_source_t *source = audio_source_list_instance(slink);
+		names[i] = str_dup(source->name);
+		if (names[i])
+			ret = ENOMEM;
+	}
+	if (ret == EOK) {
+		*size = count;
+		*list = names;
+	} else {
+		for (size_t i = 0; i < count; ++i)
+			free(names[i]);
+		free(names);
+	}
+	fibril_mutex_unlock(&hound->list_guard);
+	return ret;
+}
+
+/**
+ * List all registered sinks.
+ * @param[in] hound The hound structure.
+ * @param[out] list List of the string identifiers.
+ * @param[out] size Number of identifiers int he @p list.
+ * @return Error code.
+ */
+int hound_list_sinks(hound_t *hound, const char ***list, size_t *size)
+{
+	assert(hound);
+	if (!list || !size)
+		return EINVAL;
+
+	fibril_mutex_lock(&hound->list_guard);
+	const size_t count = list_count(&hound->sinks);
+	if (count == 0) {
+		*list = NULL;
+		*size = 0;
+		fibril_mutex_unlock(&hound->list_guard);
+		return EOK;
+	}
+	const char **names = calloc(count, sizeof(char *));
+	int ret = names ? EOK : ENOMEM;
+	for (size_t i = 0; i < count && ret == EOK; ++i) {
+		link_t *slink = list_nth(&hound->sinks, i);
+		audio_sink_t *sink = audio_sink_list_instance(slink);
+		names[i] = str_dup(sink->name);
+		if (!names[i])
+			ret = ENOMEM;
+	}
+	if (ret == EOK) {
+		*size = count;
+		*list = names;
+	} else {
+		for (size_t i = 0; i < count; ++i)
+			free(names[i]);
+		free(names);
+	}
+	fibril_mutex_unlock(&hound->list_guard);
+	return ret;
+}
+
+/**
+ * List all connections
+ * @param[in] hound The hound structure.
+ * @param[out] sources List of the source string identifiers.
+ * @param[out] sinks List of the sinks string identifiers.
+ * @param[out] size Number of identifiers int he @p list.
+ * @return Error code.
+ *
+ * Lists include duplicit name entries. The order of entries is important,
+ * identifiers with the same index are connected.
+ */
+int hound_list_connections(hound_t *hound, const char ***sources,
+    const char ***sinks, size_t *size)
+{
+	fibril_mutex_lock(&hound->list_guard);
+	fibril_mutex_unlock(&hound->list_guard);
+	return ENOTSUP;
+}
+
+/**
+ * Create and register a new connection.
+ * @param hound The hound structure.
+ * @param source_name Source's string id.
+ * @param sink_name Sink's string id.
+ * @return Error code.
+ */
+int hound_connect(hound_t *hound, const char* source_name, const char* sink_name)
+{
+	assert(hound);
+	log_verbose("Connecting '%s' to '%s'.", source_name, sink_name);
+	fibril_mutex_lock(&hound->list_guard);
+
+	audio_source_t *source =
+	    audio_source_list_instance(list_first(&hound->sources));
+	if (str_cmp(source_name, "default") != 0)
+	    source = find_source_by_name(&hound->sources, source_name);
+
+	audio_sink_t *sink =
+	    audio_sink_list_instance(list_first(&hound->sinks));
+	if (str_cmp(sink_name, "default") != 0)
+	    sink = find_sink_by_name(&hound->sinks, sink_name);
+
+	if (!source || !sink) {
+		fibril_mutex_unlock(&hound->list_guard);
+		log_debug("Source (%p), or sink (%p) not found", source, sink);
+		return ENOENT;
+	}
+	connection_t *conn = connection_create(source, sink);
+	if (!conn) {
+		fibril_mutex_unlock(&hound->list_guard);
+		log_debug("Failed to create connection");
+		return ENOMEM;
+	}
+	list_append(&conn->hound_link, &hound->connections);
+	fibril_mutex_unlock(&hound->list_guard);
+	return EOK;
+}
+
+/**
+ * Find and destroy connection between source and sink.
+ * @param hound The hound structure.
+ * @param source_name Source's string id.
+ * @param sink_name Sink's string id.
+ * @return Error code.
+ */
+int hound_disconnect(hound_t *hound, const char* source_name, const char* sink_name)
+{
+	assert(hound);
+	fibril_mutex_lock(&hound->list_guard);
+	const int ret = hound_disconnect_internal(hound, source_name, sink_name);
+	fibril_mutex_unlock(&hound->list_guard);
+	return ret;
+}
+
+/**
+ * Internal disconnect helper.
+ * @param hound The hound structure.
+ * @param source_name Source's string id.
+ * @param sink_name Sink's string id.
+ * @return Error code.
+ *
+ * This function has to be called with the list_guard lock held.
+ */
+static int hound_disconnect_internal(hound_t *hound, const char* source_name,
+    const char* sink_name)
+{
+	assert(hound);
+	assert(fibril_mutex_is_locked(&hound->list_guard));
+	log_debug("Disconnecting '%s' to '%s'.", source_name, sink_name);
+
+	list_foreach_safe(hound->connections, it, next) {
+		connection_t *conn = connection_from_hound_list(it);
+		if (str_cmp(connection_source_name(conn), source_name) == 0 ||
+		    str_cmp(connection_sink_name(conn), sink_name) == 0) {
+		    log_debug("Removing %s -> %s", connection_source_name(conn),
+		        connection_sink_name(conn));
+		    list_remove(it);
+		    connection_destroy(conn);
+		}
+	}
+
+	return EOK;
+}
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/hound.h
===================================================================
--- uspace/srv/audio/hound/hound.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/hound.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#ifndef HOUND_H_
+#define HOUND_H_
+
+#include <async.h>
+#include <adt/list.h>
+#include <ipc/loc.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <pcm/format.h>
+#include <hound/protocol.h>
+
+#include "hound_ctx.h"
+#include "audio_source.h"
+#include "audio_sink.h"
+
+/** The main Hound structure */
+typedef struct {
+	/** List access guard */
+	fibril_mutex_t list_guard;
+	/** enumerated devices */
+	list_t devices;
+	/** registered contexts */
+	list_t contexts;
+	/** Provided sources */
+	list_t sources;
+	/** Provided sinks */
+	list_t sinks;
+	/** Existing connections. */
+	list_t connections;
+} hound_t;
+
+int hound_init(hound_t *hound);
+int hound_add_ctx(hound_t *hound, hound_ctx_t *ctx);
+int hound_remove_ctx(hound_t *hound, hound_ctx_t *ctx);
+hound_ctx_t *hound_get_ctx_by_id(hound_t *hound, hound_context_id_t id);
+
+int hound_add_device(hound_t *hound, service_id_t id, const char* name);
+int hound_add_source(hound_t *hound, audio_source_t *source);
+int hound_add_sink(hound_t *hound, audio_sink_t *sink);
+int hound_list_sources(hound_t *hound, const char ***list, size_t *size);
+int hound_list_sinks(hound_t *hound, const char ***list, size_t *size);
+int hound_list_connections(hound_t *hound, const char ***sources,
+    const char ***sinks, size_t *size);
+int hound_remove_source(hound_t *hound, audio_source_t *source);
+int hound_remove_sink(hound_t *hound, audio_sink_t *sink);
+int hound_connect(hound_t *hound, const char* source_name, const char* sink_name);
+int hound_disconnect(hound_t *hound, const char* source_name, const char* sink_name);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/hound_ctx.c
===================================================================
--- uspace/srv/audio/hound/hound_ctx.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/hound_ctx.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2013 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server.
+ * @{
+ */
+/** @file
+ */
+
+#include <malloc.h>
+#include <macros.h>
+#include <errno.h>
+#include <str_error.h>
+
+#include "hound_ctx.h"
+#include "audio_data.h"
+#include "connection.h"
+#include "log.h"
+
+static int update_data(audio_source_t *source, size_t size);
+static int new_data(audio_sink_t *sink);
+
+/**
+ * Allocate and initialize hound context structure.
+ * @param name String identifier.
+ * @return Pointer to a new context structure, NULL on failure
+ *
+ * Creates record context structure.
+ */
+hound_ctx_t *hound_record_ctx_get(const char *name)
+{
+	hound_ctx_t *ctx = malloc(sizeof(hound_ctx_t));
+	if (ctx) {
+		link_initialize(&ctx->link);
+		list_initialize(&ctx->streams);
+		fibril_mutex_initialize(&ctx->guard);
+		ctx->source = NULL;
+		ctx->sink = malloc(sizeof(audio_sink_t));
+		if (!ctx->sink) {
+			free(ctx);
+			return NULL;
+		}
+		const int ret = audio_sink_init(ctx->sink, name, ctx, NULL,
+		    NULL, new_data, &AUDIO_FORMAT_DEFAULT);
+		if (ret != EOK) {
+			free(ctx->sink);
+			free(ctx);
+			return NULL;
+		}
+	}
+	return ctx;
+}
+
+/**
+ * Allocate and initialize hound context structure.
+ * @param name String identifier.
+ * @return Pointer to a new context structure, NULL on failure
+ *
+ * Creates record context structure.
+ */
+hound_ctx_t *hound_playback_ctx_get(const char *name)
+{
+	hound_ctx_t *ctx = malloc(sizeof(hound_ctx_t));
+	if (ctx) {
+		link_initialize(&ctx->link);
+		list_initialize(&ctx->streams);
+		fibril_mutex_initialize(&ctx->guard);
+		ctx->sink = NULL;
+		ctx->source = malloc(sizeof(audio_source_t));
+		if (!ctx->source) {
+			free(ctx);
+			return NULL;
+		}
+		const int ret = audio_source_init(ctx->source, name, ctx, NULL,
+		    update_data, &AUDIO_FORMAT_DEFAULT);
+		if (ret != EOK) {
+			free(ctx->source);
+			free(ctx);
+			return NULL;
+		}
+	}
+	return ctx;
+}
+
+/**
+ * Destroy existing context structure.
+ * @param ctx hound cotnext to destroy.
+ */
+void hound_ctx_destroy(hound_ctx_t *ctx)
+{
+	assert(ctx);
+	assert(!link_in_use(&ctx->link));
+	assert(list_empty(&ctx->streams));
+	if (ctx->source)
+		audio_source_fini(ctx->source);
+	if (ctx->sink)
+		audio_sink_fini(ctx->sink);
+	free(ctx->source);
+	free(ctx->sink);
+	free(ctx);
+}
+
+/**
+ * Retrieve associated context id.
+ * @param ctx hound context.
+ * @return context id of the context.
+ */
+hound_context_id_t hound_ctx_get_id(hound_ctx_t *ctx)
+{
+	assert(ctx);
+	return (hound_context_id_t)ctx;
+}
+
+/**
+ * Query playback/record status of a hound context.
+ * @param ctx Hound context.
+ * @return True if ctx  is a recording context.
+ */
+bool hound_ctx_is_record(hound_ctx_t *ctx)
+{
+	assert(ctx);
+	return ctx->source == NULL;
+}
+
+
+/*
+ * STREAMS
+ */
+
+/** Hound stream structure. */
+typedef struct hound_ctx_stream {
+	/** Hound context streams link */
+	link_t link;
+	/** Audio data pipe */
+	audio_pipe_t fifo;
+	/** Parent context */
+	hound_ctx_t *ctx;
+	/** Stream data format */
+	pcm_format_t format;
+	/** Stream modifiers */
+	int flags;
+	/** Maximum allowed buffer size */
+	size_t allowed_size;
+	/** Fifo access synchronization */
+	fibril_mutex_t guard;
+	/** buffer status change condition */
+	fibril_condvar_t change;
+} hound_ctx_stream_t;
+
+/**
+ * New stream append helper.
+ * @param ctx hound context.
+ * @param stream A new stream.
+ */
+static inline void stream_append(hound_ctx_t *ctx, hound_ctx_stream_t *stream)
+{
+	assert(ctx);
+	assert(stream);
+	fibril_mutex_lock(&ctx->guard);
+	list_append(&stream->link, &ctx->streams);
+	if (list_count(&ctx->streams) == 1) {
+		if (ctx->source && list_count(&ctx->source->connections) == 0)
+			ctx->source->format = stream->format;
+	}
+	fibril_mutex_unlock(&ctx->guard);
+}
+
+/**
+ * Push new data to stream, do not block.
+ * @param stream The target stream.
+ * @param adata The new data.
+ * @return Error code.
+ */
+static int stream_push_data(hound_ctx_stream_t *stream, audio_data_t *adata)
+{
+	assert(stream);
+	assert(adata);
+
+	if (stream->allowed_size && adata->size > stream->allowed_size)
+		return EINVAL;
+
+	fibril_mutex_lock(&stream->guard);
+	if (stream->allowed_size &&
+	    (audio_pipe_bytes(&stream->fifo) + adata->size
+	        > stream->allowed_size)) {
+		fibril_mutex_unlock(&stream->guard);
+		return EOVERFLOW;
+
+	}
+
+	const int ret = audio_pipe_push(&stream->fifo, adata);
+	fibril_mutex_unlock(&stream->guard);
+	if (ret == EOK)
+		fibril_condvar_signal(&stream->change);
+	return ret;
+}
+
+/**
+ * Old stream remove helper.
+ * @param ctx hound context.
+ * @param stream An old stream.
+ */
+static inline void stream_remove(hound_ctx_t *ctx, hound_ctx_stream_t *stream)
+{
+	assert(ctx);
+	assert(stream);
+	fibril_mutex_lock(&ctx->guard);
+	list_remove(&stream->link);
+	fibril_mutex_unlock(&ctx->guard);
+}
+
+/**
+ * Create new stream.
+ * @param ctx Assocaited hound context.
+ * @param flags Stream modidfiers.
+ * @param format PCM data format.
+ * @param buffer_size Maximum allowed buffer size.
+ * @return Pointer to a new stream structure, NULL on failure.
+ */
+hound_ctx_stream_t *hound_ctx_create_stream(hound_ctx_t *ctx, int flags,
+	pcm_format_t format, size_t buffer_size)
+{
+	assert(ctx);
+	hound_ctx_stream_t *stream = malloc(sizeof(hound_ctx_stream_t));
+	if (stream) {
+		audio_pipe_init(&stream->fifo);
+		link_initialize(&stream->link);
+		fibril_mutex_initialize(&stream->guard);
+		fibril_condvar_initialize(&stream->change);
+		stream->ctx = ctx;
+		stream->flags = flags;
+		stream->format = format;
+		stream->allowed_size = buffer_size;
+		stream_append(ctx, stream);
+		log_verbose("CTX: %p added stream; flags:%#x ch: %u r:%u f:%s",
+		    ctx, flags, format.channels, format.sampling_rate,
+		    pcm_sample_format_str(format.sample_format));
+	}
+	return stream;
+}
+
+/**
+ * Destroy existing stream structure.
+ * @param stream The stream to destroy.
+ *
+ * The function will print warning if there are data in the buffer.
+ */
+void hound_ctx_destroy_stream(hound_ctx_stream_t *stream)
+{
+	if (stream) {
+		stream_remove(stream->ctx, stream);
+		if (audio_pipe_bytes(&stream->fifo))
+			log_warning("Destroying stream with non empty buffer");
+		log_verbose("CTX: %p remove stream (%zu/%zu); "
+		    "flags:%#x ch: %u r:%u f:%s",
+		    stream->ctx, audio_pipe_bytes(&stream->fifo),
+		    stream->allowed_size, stream->flags,
+		    stream->format.channels, stream->format.sampling_rate,
+		    pcm_sample_format_str(stream->format.sample_format));
+		audio_pipe_fini(&stream->fifo);
+		free(stream);
+	}
+}
+
+/**
+ * Write new data to a stream.
+ * @param stream The destination stream.
+ * @param data audio data buffer.
+ * @param size size of the @p data buffer.
+ * @return Error code.
+ */
+int hound_ctx_stream_write(hound_ctx_stream_t *stream, const void *data,
+    size_t size)
+{
+	assert(stream);
+
+	if (stream->allowed_size && size > stream->allowed_size)
+		return EINVAL;
+
+	fibril_mutex_lock(&stream->guard);
+	while (stream->allowed_size &&
+	    (audio_pipe_bytes(&stream->fifo) + size > stream->allowed_size)) {
+	    fibril_condvar_wait(&stream->change, &stream->guard);
+
+	}
+
+	const int ret =
+	    audio_pipe_push_data(&stream->fifo, data, size, stream->format);
+	fibril_mutex_unlock(&stream->guard);
+	if (ret == EOK)
+		fibril_condvar_signal(&stream->change);
+	return ret;
+}
+
+/**
+ * Read data from a buffer.
+ * @param stream The source buffer.
+ * @param data Destination data buffer.
+ * @param size Size of the @p data buffer.
+ * @return Error code.
+ */
+int hound_ctx_stream_read(hound_ctx_stream_t *stream, void *data, size_t size)
+{
+	assert(stream);
+
+	if (stream->allowed_size && size > stream->allowed_size)
+		return EINVAL;
+
+	fibril_mutex_lock(&stream->guard);
+	while (audio_pipe_bytes(&stream->fifo) < size) {
+	    fibril_condvar_wait(&stream->change, &stream->guard);
+	}
+
+	pcm_format_silence(data, size, &stream->format);
+	const int ret =
+	    audio_pipe_mix_data(&stream->fifo, data, size, &stream->format);
+	fibril_mutex_unlock(&stream->guard);
+	if (ret == EOK)
+		fibril_condvar_signal(&stream->change);
+	return ret;
+}
+
+/**
+ * Add (mix) stream data to the destination buffer.
+ * @param stream The source stream.
+ * @param data Destination audio buffer.
+ * @param size Size of the @p data buffer.
+ * @param format Destination data format.
+ * @return Size of the destination buffer touch with stream's data,
+ *         error code on failure.
+ */
+ssize_t hound_ctx_stream_add_self(hound_ctx_stream_t *stream, void *data,
+    size_t size, const pcm_format_t *f)
+{
+	assert(stream);
+	fibril_mutex_lock(&stream->guard);
+	const int ret = audio_pipe_mix_data(&stream->fifo, data, size, f);
+	fibril_condvar_signal(&stream->change);
+	fibril_mutex_unlock(&stream->guard);
+	return ret;
+}
+
+/**
+ * Block until the stream's buffer is empty.
+ * @param stream Target stream.
+ */
+void hound_ctx_stream_drain(hound_ctx_stream_t *stream)
+{
+	assert(stream);
+	log_debug("Draining stream");
+	fibril_mutex_lock(&stream->guard);
+	while (audio_pipe_bytes(&stream->fifo))
+		fibril_condvar_wait(&stream->change, &stream->guard);
+	fibril_mutex_unlock(&stream->guard);
+}
+
+/**
+ * Update context data.
+ * @param source Source abstraction.
+ * @param size Required size in source's format.
+ * @return error code.
+ *
+ * Mixes data from all streams and pushes it to all connections.
+ */
+int update_data(audio_source_t *source, size_t size)
+{
+	assert(source);
+	assert(source->private_data);
+	hound_ctx_t *ctx = source->private_data;
+	void *buffer = malloc(size);
+	if (!buffer)
+		return ENOMEM;
+	audio_data_t *adata = audio_data_create(buffer, size, source->format);
+	if (!adata) {
+		free(buffer);
+		return ENOMEM;
+	}
+	log_verbose("CTX: %p: Mixing %u streams", ctx,
+	    list_count(&ctx->streams));
+	pcm_format_silence(buffer, size, &source->format);
+	fibril_mutex_lock(&ctx->guard);
+	list_foreach(ctx->streams, link, hound_ctx_stream_t, stream) {
+		ssize_t copied = hound_ctx_stream_add_self(
+		    stream, buffer, size, &source->format);
+		if (copied != (ssize_t)size)
+			log_warning("Not enough data in stream buffer");
+	}
+	log_verbose("CTX: %p. Pushing audio to %u connections", ctx,
+	    list_count(&source->connections));
+	list_foreach(source->connections, source_link, connection_t, conn) {
+		connection_push_data(conn, adata);
+	}
+	fibril_mutex_unlock(&ctx->guard);
+	return EOK;
+}
+
+int new_data(audio_sink_t *sink)
+{
+	assert(sink);
+	assert(sink->private_data);
+	hound_ctx_t *ctx = sink->private_data;
+
+	fibril_mutex_lock(&ctx->guard);
+
+	/* count available data */
+	size_t available_frames = -1;  /* this is ugly.... */
+	list_foreach(sink->connections, source_link, connection_t, conn) {
+		available_frames = min(available_frames,
+		    audio_pipe_frames(&conn->fifo));
+	}
+
+	const size_t bsize =
+	    available_frames * pcm_format_frame_size(&sink->format);
+	void *buffer = malloc(bsize);
+	if (!buffer) {
+		fibril_mutex_unlock(&ctx->guard);
+		return ENOMEM;
+	}
+	audio_data_t *adata = audio_data_create(buffer, bsize, sink->format);
+	if (!adata) {
+		fibril_mutex_unlock(&ctx->guard);
+		free(buffer);
+		return ENOMEM;
+	}
+
+	/* mix data */
+	pcm_format_silence(buffer, bsize, &sink->format);
+	list_foreach(sink->connections, source_link, connection_t, conn) {
+		/* This should not trigger data update on the source */
+		const size_t copied = connection_add_source_data(
+		    conn, buffer, bsize, sink->format);
+		if (copied != bsize)
+			log_error("Copied less than advertised data, "
+			    "something is wrong");
+	}
+	/* push to all streams */
+	list_foreach(ctx->streams, link, hound_ctx_stream_t, stream) {
+		const int ret = stream_push_data(stream, adata);
+		if (ret != EOK)
+			log_error("Failed to push data to stream: %s",
+				str_error(ret));
+	}
+	audio_data_unref(adata);
+	fibril_mutex_unlock(&ctx->guard);
+	return ENOTSUP;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/hound_ctx.h
===================================================================
--- uspace/srv/audio/hound/hound_ctx.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/hound_ctx.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#ifndef HOUND_CTX_H_
+#define HOUND_CTX_H_
+
+#include <adt/list.h>
+#include <hound/protocol.h>
+#include <fibril_synch.h>
+
+#include "audio_source.h"
+#include "audio_sink.h"
+
+/** Application context structure */
+typedef struct {
+	/** Hound's contexts link */
+	link_t link;
+	/** List of streams */
+	list_t streams;
+	/** Provided audio source abstraction */
+	audio_source_t *source;
+	/** Provided audio sink abstraction */
+	audio_sink_t *sink;
+	/** List access synchronization */
+	fibril_mutex_t guard;
+} hound_ctx_t;
+
+typedef struct hound_ctx_stream hound_ctx_stream_t;
+
+
+hound_ctx_t *hound_record_ctx_get(const char *name);
+hound_ctx_t *hound_playback_ctx_get(const char *name);
+void hound_ctx_destroy(hound_ctx_t *context);
+
+hound_context_id_t hound_ctx_get_id(hound_ctx_t *ctx);
+bool hound_ctx_is_record(hound_ctx_t *ctx);
+
+hound_ctx_stream_t *hound_ctx_create_stream(hound_ctx_t *ctx, int flags,
+	pcm_format_t format, size_t buffer_size);
+void hound_ctx_destroy_stream(hound_ctx_stream_t *stream);
+
+int hound_ctx_stream_write(hound_ctx_stream_t *stream, const void *buffer,
+    size_t size);
+int hound_ctx_stream_read(hound_ctx_stream_t *stream, void *buffer, size_t size);
+ssize_t hound_ctx_stream_add_self(hound_ctx_stream_t *stream, void *data,
+    size_t size, const pcm_format_t *f);
+void hound_ctx_stream_drain(hound_ctx_stream_t *stream);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/iface.c
===================================================================
--- uspace/srv/audio/hound/iface.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/iface.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2013 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server.
+ * @{
+ */
+/** @file
+ */
+
+#include <errno.h>
+#include <hound/protocol.h>
+#include <inttypes.h>
+#include <malloc.h>
+
+#include "hound.h"
+#include "hound_ctx.h"
+#include "log.h"
+
+static int iface_add_context(void *server, hound_context_id_t *id,
+    const char *name, bool record)
+{
+	assert(server);
+	assert(id);
+	assert(name);
+
+	hound_ctx_t *ctx = record ? hound_record_ctx_get(name) :
+	    hound_playback_ctx_get(name);
+	if (!ctx)
+		return ENOMEM;
+
+	const int ret = hound_add_ctx(server, ctx);
+	if (ret != EOK)
+		hound_ctx_destroy(ctx);
+	else
+		*id = hound_ctx_get_id(ctx);
+	return ret;
+}
+
+static int iface_rem_context(void *server, hound_context_id_t id)
+{
+	assert(server);
+	hound_ctx_t *ctx = hound_get_ctx_by_id(server, id);
+	if (!ctx)
+		return EINVAL;
+	int ret = hound_remove_ctx(server, ctx);
+	if (ret != EOK)
+		return ret;
+	hound_ctx_destroy(ctx);
+	log_info("%s: %p, %#" PRIxn, __FUNCTION__, server, id);
+	return EOK;
+}
+
+static bool iface_is_record_context(void *server, hound_context_id_t id)
+{
+	assert(server);
+	hound_ctx_t *ctx = hound_get_ctx_by_id(server, id);
+	if (!ctx)
+		return false;
+	return hound_ctx_is_record(ctx);
+}
+
+static int iface_get_list(void *server, const char ***list, size_t *size,
+    const char *connection, int flags)
+{
+	log_info("%s: %p, %zu, %s, %#x\n", __FUNCTION__, server, *size,
+	    connection, flags);
+	if ((flags & (HOUND_SINK_DEVS | HOUND_SINK_APPS)) != 0)
+		return hound_list_sinks(server, list, size);
+	if ((flags & (HOUND_SOURCE_DEVS | HOUND_SOURCE_APPS)) != 0)
+		return hound_list_sources(server, list, size);
+	return ENOTSUP;
+}
+
+static int iface_connect(void *server, const char *source, const char *sink)
+{
+	log_info("%s: %p, %s -> %s", __FUNCTION__, server, source, sink);
+	return hound_connect(server, source, sink);
+}
+
+static int iface_disconnect(void *server, const char *source, const char *sink)
+{
+	log_info("%s: %p, %s -> %s", __FUNCTION__, server, source, sink);
+	return hound_disconnect(server, source, sink);
+}
+
+static int iface_add_stream(void *server, hound_context_id_t id, int flags,
+    pcm_format_t format, size_t size, void **data)
+{
+	assert(data);
+	assert(server);
+
+	log_verbose("%s: %p, %" PRIxn " %x ch:%u r:%u f:%s", __FUNCTION__,
+	    server, id, flags, format.channels, format.sampling_rate,
+	    pcm_sample_format_str(format.sample_format));
+	hound_ctx_t *ctx = hound_get_ctx_by_id(server, id);
+	if (!ctx)
+		return ENOENT;
+	hound_ctx_stream_t *stream =
+	    hound_ctx_create_stream(ctx, flags, format, size);
+	if (!stream)
+		return ENOMEM;
+	*data = stream;
+	return EOK;
+}
+
+static int iface_rem_stream(void *server, void *stream)
+{
+	hound_ctx_destroy_stream(stream);
+	return EOK;
+}
+
+static int iface_drain_stream(void *stream)
+{
+	hound_ctx_stream_drain(stream);
+	return EOK;
+}
+
+static int iface_stream_data_read(void *stream, void *buffer, size_t size)
+{
+	return hound_ctx_stream_read(stream, buffer, size);
+}
+
+static int iface_stream_data_write(void *stream, const void *buffer, size_t size)
+{
+	return hound_ctx_stream_write(stream, buffer, size);
+}
+
+hound_server_iface_t hound_iface = {
+	.add_context = iface_add_context,
+	.rem_context = iface_rem_context,
+	.is_record_context = iface_is_record_context,
+	.get_list = iface_get_list,
+	.connect = iface_connect,
+	.disconnect = iface_disconnect,
+	.add_stream = iface_add_stream,
+	.rem_stream = iface_rem_stream,
+	.drain_stream = iface_drain_stream,
+	.stream_data_write = iface_stream_data_write,
+	.stream_data_read = iface_stream_data_read,
+	.server = NULL,
+};
Index: uspace/srv/audio/hound/log.h
===================================================================
--- uspace/srv/audio/hound/log.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/log.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LOG_H_
+#define LOG_H_
+
+#ifndef NAME
+#define NAME "NONAME"
+#endif
+
+#include <stdio.h>
+
+#define log_fatal(msg, ...) printf(NAME ": Fatal: " msg "\n", ##__VA_ARGS__);
+#define log_error(msg, ...) printf(NAME ": Error: " msg "\n", ##__VA_ARGS__);
+#define log_warning(msg, ...) printf(NAME ": Warn: " msg "\n", ##__VA_ARGS__);
+#define log_info(msg, ...) printf(NAME ": Info: " msg "\n", ##__VA_ARGS__);
+#define log_debug(msg, ...) printf("%s: Debug: %s: " msg "\n", NAME, __FUNCTION__, ##__VA_ARGS__);
+#define log_verbose(msg, ...) printf("%s: %s: " msg "\n", NAME, __FUNCTION__, ##__VA_ARGS__);
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/audio/hound/main.c
===================================================================
--- uspace/srv/audio/hound/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/audio/hound/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2012 Jan Vesely
+ * 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 audio
+ * @brief HelenOS sound server.
+ * @{
+ */
+/** @file
+ */
+
+#include <async.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str_error.h>
+#include <hound/server.h>
+#include <hound/protocol.h>
+
+#include "hound.h"
+
+#define NAMESPACE "audio"
+#define NAME "hound"
+#define CATEGORY "audio-pcm"
+
+#include "log.h"
+
+extern hound_server_iface_t hound_iface;
+
+static hound_t hound;
+
+static int device_callback(service_id_t id, const char *name)
+{
+	return hound_add_device(&hound, id, name);
+}
+
+static void scan_for_devices(void)
+{
+	hound_server_devices_iterate(device_callback);
+}
+
+int main(int argc, char **argv)
+{
+	printf("%s: HelenOS sound service\n", NAME);
+
+	int ret = hound_init(&hound);
+	if (ret != EOK) {
+		log_fatal("Failed to initialize hound structure: %s",
+		    str_error(ret));
+		return -ret;
+	}
+
+	hound_iface.server = &hound;
+	hound_service_set_server_iface(&hound_iface);
+	async_set_client_connection(hound_connection_handler);
+
+	service_id_t id = 0;
+	ret = hound_server_register(NAME, &id);
+	if (ret != EOK) {
+		log_fatal("Failed to register server: %s", str_error(ret));
+		return -ret;
+	}
+
+	ret = hound_server_set_device_change_callback(scan_for_devices);
+	if (ret != EOK) {
+		log_fatal("Failed to register for device changes: %s",
+		    str_error(ret));
+		hound_server_unregister(id);
+		return -ret;
+	}
+	log_info("Running with service id %" PRIun, id);
+
+	scan_for_devices();
+	task_retval(0);
+	async_manager();
+	return 0;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/bd/rd/rd.c
===================================================================
--- uspace/srv/bd/rd/rd.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/bd/rd/rd.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -165,5 +165,5 @@
 	    AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE;
 	
-	ret = physmem_map((void *) addr_phys,
+	ret = physmem_map(addr_phys,
 	    ALIGN_UP(rd_size, PAGE_SIZE) >> PAGE_WIDTH, flags, &rd_addr);
 	if (ret != EOK) {
Index: uspace/srv/devman/Makefile
===================================================================
--- uspace/srv/devman/Makefile	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/devman/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/client_conn.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/client_conn.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/dev.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/dev.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ 	(revision )
@@ -1,1528 +1,0 @@
-/*
- * Copyright (c) 2010 Lenka Trochtova
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup devman
- * @{
- */
-/** @file Device Manager
- *
- * Locking order:
- *   (1) driver_t.driver_mutex
- *   (2) dev_tree_t.rwlock
- *
- * Synchronization:
- *    - device_tree.rwlock protects:
- *        - tree root, complete tree topology
- *        - complete contents of device and function nodes
- *    - dev_node_t.refcnt, fun_node_t.refcnt prevent nodes from
- *      being deallocated
- *    - find_xxx() functions increase reference count of returned object
- *    - find_xxx_no_lock() do not increase reference count
- *
- * TODO
- *    - Track all steady and transient device/function states
- *    - Check states, wait for steady state on certain operations
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <io/log.h>
-#include <ipc/driver.h>
-#include <ipc/devman.h>
-#include <loc.h>
-#include <str_error.h>
-#include <stdio.h>
-
-#include "devman.h"
-
-static fun_node_t *find_node_child(dev_tree_t *, fun_node_t *, const char *);
-
-/* hash table operations */
-
-static inline size_t handle_key_hash(void *key)
-{
-	devman_handle_t handle = *(devman_handle_t*)key;
-	return handle;
-}
-
-static size_t devman_devices_hash(const ht_link_t *item)
-{
-	dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
-	return handle_key_hash(&dev->handle);
-}
-
-static size_t devman_functions_hash(const ht_link_t *item)
-{
-	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
-	return handle_key_hash(&fun->handle);
-}
-
-static bool devman_devices_key_equal(void *key, const ht_link_t *item)
-{
-	devman_handle_t handle = *(devman_handle_t*)key;
-	dev_node_t *dev = hash_table_get_inst(item, dev_node_t, devman_dev);
-	return dev->handle == handle;
-}
-
-static bool devman_functions_key_equal(void *key, const ht_link_t *item)
-{
-	devman_handle_t handle = *(devman_handle_t*)key;
-	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, devman_fun);
-	return fun->handle == handle;
-}
-
-static inline size_t service_id_key_hash(void *key)
-{
-	service_id_t service_id = *(service_id_t*)key;
-	return service_id;
-}
-
-static size_t loc_functions_hash(const ht_link_t *item)
-{
-	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
-	return service_id_key_hash(&fun->service_id);
-}
-
-static bool loc_functions_key_equal(void *key, const ht_link_t *item)
-{
-	service_id_t service_id = *(service_id_t*)key;
-	fun_node_t *fun = hash_table_get_inst(item, fun_node_t, loc_fun);
-	return fun->service_id == service_id;
-}
-
-
-static hash_table_ops_t devman_devices_ops = {
-	.hash = devman_devices_hash,
-	.key_hash = handle_key_hash,
-	.key_equal = devman_devices_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-static hash_table_ops_t devman_functions_ops = {
-	.hash = devman_functions_hash,
-	.key_hash = handle_key_hash,
-	.key_equal = devman_functions_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-static hash_table_ops_t loc_devices_ops = {
-	.hash = loc_functions_hash,
-	.key_hash = service_id_key_hash,
-	.key_equal = loc_functions_key_equal,
-	.equal = NULL,
-	.remove_callback = NULL
-};
-
-/**
- * Initialize the list of device driver's.
- *
- * @param drv_list the list of device driver's.
- *
- */
-void init_driver_list(driver_list_t *drv_list)
-{
-	assert(drv_list != NULL);
-	
-	list_initialize(&drv_list->drivers);
-	fibril_mutex_initialize(&drv_list->drivers_mutex);
-}
-
-/** Allocate and initialize a new driver structure.
- *
- * @return	Driver structure.
- */
-driver_t *create_driver(void)
-{
-	driver_t *res = malloc(sizeof(driver_t));
-	if (res != NULL)
-		init_driver(res);
-	return res;
-}
-
-/** Add a driver to the list of drivers.
- *
- * @param drivers_list	List of drivers.
- * @param drv		Driver structure.
- */
-void add_driver(driver_list_t *drivers_list, driver_t *drv)
-{
-	fibril_mutex_lock(&drivers_list->drivers_mutex);
-	list_prepend(&drv->drivers, &drivers_list->drivers);
-	fibril_mutex_unlock(&drivers_list->drivers_mutex);
-
-	log_msg(LOG_DEFAULT, LVL_NOTE, "Driver `%s' was added to the list of available "
-	    "drivers.", drv->name);
-}
-
-/** Read match id at the specified position of a string and set the position in
- * the string to the first character following the id.
- *
- * @param buf		The position in the input string.
- * @return		The match id.
- */
-char *read_match_id(char **buf)
-{
-	char *res = NULL;
-	size_t len = get_nonspace_len(*buf);
-	
-	if (len > 0) {
-		res = malloc(len + 1);
-		if (res != NULL) {
-			str_ncpy(res, len + 1, *buf, len);
-			*buf += len;
-		}
-	}
-	
-	return res;
-}
-
-/**
- * Read match ids and associated match scores from a string.
- *
- * Each match score in the string is followed by its match id.
- * The match ids and match scores are separated by whitespaces.
- * Neither match ids nor match scores can contain whitespaces.
- *
- * @param buf		The string from which the match ids are read.
- * @param ids		The list of match ids into which the match ids and
- *			scores are added.
- * @return		True if at least one match id and associated match score
- *			was successfully read, false otherwise.
- */
-bool parse_match_ids(char *buf, match_id_list_t *ids)
-{
-	int score = 0;
-	char *id = NULL;
-	int ids_read = 0;
-	
-	while (true) {
-		/* skip spaces */
-		if (!skip_spaces(&buf))
-			break;
-		
-		/* read score */
-		score = strtoul(buf, &buf, 10);
-		
-		/* skip spaces */
-		if (!skip_spaces(&buf))
-			break;
-		
-		/* read id */
-		id = read_match_id(&buf);
-		if (NULL == id)
-			break;
-		
-		/* create new match_id structure */
-		match_id_t *mid = create_match_id();
-		mid->id = id;
-		mid->score = score;
-		
-		/* add it to the list */
-		add_match_id(ids, mid);
-		
-		ids_read++;
-	}
-	
-	return ids_read > 0;
-}
-
-/**
- * Read match ids and associated match scores from a file.
- *
- * Each match score in the file is followed by its match id.
- * The match ids and match scores are separated by whitespaces.
- * Neither match ids nor match scores can contain whitespaces.
- *
- * @param buf		The path to the file from which the match ids are read.
- * @param ids		The list of match ids into which the match ids and
- *			scores are added.
- * @return		True if at least one match id and associated match score
- *			was successfully read, false otherwise.
- */
-bool read_match_ids(const char *conf_path, match_id_list_t *ids)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "read_match_ids(conf_path=\"%s\")", conf_path);
-	
-	bool suc = false;
-	char *buf = NULL;
-	bool opened = false;
-	int fd;
-	size_t len = 0;
-	
-	fd = open(conf_path, O_RDONLY);
-	if (fd < 0) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to open `%s' for reading: %s.",
-		    conf_path, str_error(fd));
-		goto cleanup;
-	}
-	opened = true;
-	
-	len = lseek(fd, 0, SEEK_END);
-	lseek(fd, 0, SEEK_SET);
-	if (len == 0) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Configuration file '%s' is empty.",
-		    conf_path);
-		goto cleanup;
-	}
-	
-	buf = malloc(len + 1);
-	if (buf == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Memory allocation failed when parsing file "
-		    "'%s'.", conf_path);
-		goto cleanup;
-	}
-	
-	ssize_t read_bytes = read_all(fd, buf, len);
-	if (read_bytes <= 0) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Unable to read file '%s' (%zd).", conf_path,
-		    read_bytes);
-		goto cleanup;
-	}
-	buf[read_bytes] = 0;
-	
-	suc = parse_match_ids(buf, ids);
-	
-cleanup:
-	free(buf);
-	
-	if (opened)
-		close(fd);
-	
-	return suc;
-}
-
-/**
- * Get information about a driver.
- *
- * Each driver has its own directory in the base directory.
- * The name of the driver's directory is the same as the name of the driver.
- * The driver's directory contains driver's binary (named as the driver without
- * extension) and the configuration file with match ids for device-to-driver
- *  matching (named as the driver with a special extension).
- *
- * This function searches for the driver's directory and containing
- * configuration files. If all the files needed are found, they are parsed and
- * the information about the driver is stored in the driver's structure.
- *
- * @param base_path	The base directory, in which we look for driver's
- *			subdirectory.
- * @param name		The name of the driver.
- * @param drv		The driver structure to fill information in.
- *
- * @return		True on success, false otherwise.
- */
-bool get_driver_info(const char *base_path, const char *name, driver_t *drv)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "get_driver_info(base_path=\"%s\", name=\"%s\")",
-	    base_path, name);
-	
-	assert(base_path != NULL && name != NULL && drv != NULL);
-	
-	bool suc = false;
-	char *match_path = NULL;
-	size_t name_size = 0;
-	
-	/* Read the list of match ids from the driver's configuration file. */
-	match_path = get_abs_path(base_path, name, MATCH_EXT);
-	if (match_path == NULL)
-		goto cleanup;
-	
-	if (!read_match_ids(match_path, &drv->match_ids))
-		goto cleanup;
-	
-	/* Allocate and fill driver's name. */
-	name_size = str_size(name) + 1;
-	drv->name = malloc(name_size);
-	if (drv->name == NULL)
-		goto cleanup;
-	str_cpy(drv->name, name_size, name);
-	
-	/* Initialize path with driver's binary. */
-	drv->binary_path = get_abs_path(base_path, name, "");
-	if (drv->binary_path == NULL)
-		goto cleanup;
-	
-	/* Check whether the driver's binary exists. */
-	struct stat s;
-	if (stat(drv->binary_path, &s) == ENOENT) { /* FIXME!! */
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Driver not found at path `%s'.",
-		    drv->binary_path);
-		goto cleanup;
-	}
-	
-	suc = true;
-	
-cleanup:
-	if (!suc) {
-		free(drv->binary_path);
-		free(drv->name);
-		/* Set the driver structure to the default state. */
-		init_driver(drv);
-	}
-	
-	free(match_path);
-	
-	return suc;
-}
-
-/** Lookup drivers in the directory.
- *
- * @param drivers_list	The list of available drivers.
- * @param dir_path	The path to the directory where we search for drivers.
- * @return		Number of drivers which were found.
- */
-int lookup_available_drivers(driver_list_t *drivers_list, const char *dir_path)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "lookup_available_drivers(dir=\"%s\")", dir_path);
-	
-	int drv_cnt = 0;
-	DIR *dir = NULL;
-	struct dirent *diren;
-
-	dir = opendir(dir_path);
-	
-	if (dir != NULL) {
-		driver_t *drv = create_driver();
-		while ((diren = readdir(dir))) {
-			if (get_driver_info(dir_path, diren->d_name, drv)) {
-				add_driver(drivers_list, drv);
-				drv_cnt++;
-				drv = create_driver();
-			}
-		}
-		delete_driver(drv);
-		closedir(dir);
-	}
-	
-	return drv_cnt;
-}
-
-/** Create root device and function node in the device tree.
- *
- * @param tree	The device tree.
- * @return	True on success, false otherwise.
- */
-bool create_root_nodes(dev_tree_t *tree)
-{
-	fun_node_t *fun;
-	dev_node_t *dev;
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "create_root_nodes()");
-	
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	/*
-	 * Create root function. This is a pseudo function to which
-	 * the root device node is attached. It allows us to match
-	 * the root device driver in a standard manner, i.e. against
-	 * the parent function.
-	 */
-	
-	fun = create_fun_node();
-	if (fun == NULL) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		return false;
-	}
-	
-	fun_add_ref(fun);
-	insert_fun_node(tree, fun, str_dup(""), NULL);
-	
-	match_id_t *id = create_match_id();
-	id->id = str_dup("root");
-	id->score = 100;
-	add_match_id(&fun->match_ids, id);
-	tree->root_node = fun;
-	
-	/*
-	 * Create root device node.
-	 */
-	dev = create_dev_node();
-	if (dev == NULL) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		return false;
-	}
-	
-	dev_add_ref(dev);
-	insert_dev_node(tree, dev, fun);
-	
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	
-	return dev != NULL;
-}
-
-/** Lookup the best matching driver for the specified device in the list of
- * drivers.
- *
- * A match between a device and a driver is found if one of the driver's match
- * ids match one of the device's match ids. The score of the match is the
- * product of the driver's and device's score associated with the matching id.
- * The best matching driver for a device is the driver with the highest score
- * of the match between the device and the driver.
- *
- * @param drivers_list	The list of drivers, where we look for the driver
- *			suitable for handling the device.
- * @param node		The device node structure of the device.
- * @return		The best matching driver or NULL if no matching driver
- *			is found.
- */
-driver_t *find_best_match_driver(driver_list_t *drivers_list, dev_node_t *node)
-{
-	driver_t *best_drv = NULL, *drv = NULL;
-	int best_score = 0, score = 0;
-	
-	fibril_mutex_lock(&drivers_list->drivers_mutex);
-	
-	list_foreach(drivers_list->drivers, link) {
-		drv = list_get_instance(link, driver_t, drivers);
-		score = get_match_score(drv, node);
-		if (score > best_score) {
-			best_score = score;
-			best_drv = drv;
-		}
-	}
-	
-	fibril_mutex_unlock(&drivers_list->drivers_mutex);
-	
-	return best_drv;
-}
-
-/** Assign a driver to a device.
- *
- * @param tree		Device tree
- * @param node		The device's node in the device tree.
- * @param drv		The driver.
- */
-void attach_driver(dev_tree_t *tree, dev_node_t *dev, driver_t *drv)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "attach_driver(dev=\"%s\",drv=\"%s\")",
-	    dev->pfun->pathname, drv->name);
-	
-	fibril_mutex_lock(&drv->driver_mutex);
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	dev->drv = drv;
-	list_append(&dev->driver_devices, &drv->devices);
-	
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	fibril_mutex_unlock(&drv->driver_mutex);
-}
-
-/** Detach driver from device.
- *
- * @param tree		Device tree
- * @param node		The device's node in the device tree.
- * @param drv		The driver.
- */
-void detach_driver(dev_tree_t *tree, dev_node_t *dev)
-{
-	driver_t *drv = dev->drv;
-	
-	assert(drv != NULL);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "detach_driver(dev=\"%s\",drv=\"%s\")",
-	    dev->pfun->pathname, drv->name);
-	
-	fibril_mutex_lock(&drv->driver_mutex);
-	fibril_rwlock_write_lock(&tree->rwlock);
-	
-	dev->drv = NULL;
-	list_remove(&dev->driver_devices);
-	
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	fibril_mutex_unlock(&drv->driver_mutex);
-}
-
-/** Start a driver
- *
- * @param drv		The driver's structure.
- * @return		True if the driver's task is successfully spawned, false
- *			otherwise.
- */
-bool start_driver(driver_t *drv)
-{
-	int rc;
-
-	assert(fibril_mutex_is_locked(&drv->driver_mutex));
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "start_driver(drv=\"%s\")", drv->name);
-	
-	rc = task_spawnl(NULL, drv->binary_path, drv->binary_path, NULL);
-	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Spawning driver `%s' (%s) failed: %s.",
-		    drv->name, drv->binary_path, str_error(rc));
-		return false;
-	}
-	
-	drv->state = DRIVER_STARTING;
-	return true;
-}
-
-/** Find device driver in the list of device drivers.
- *
- * @param drv_list	The list of device drivers.
- * @param drv_name	The name of the device driver which is searched.
- * @return		The device driver of the specified name, if it is in the
- *			list, NULL otherwise.
- */
-driver_t *find_driver(driver_list_t *drv_list, const char *drv_name)
-{
-	driver_t *res = NULL;
-	driver_t *drv = NULL;
-	
-	fibril_mutex_lock(&drv_list->drivers_mutex);
-	
-	list_foreach(drv_list->drivers, link) {
-		drv = list_get_instance(link, driver_t, drivers);
-		if (str_cmp(drv->name, drv_name) == 0) {
-			res = drv;
-			break;
-		}
-	}
-	
-	fibril_mutex_unlock(&drv_list->drivers_mutex);
-	
-	return res;
-}
-
-/** Notify driver about the devices to which it was assigned.
- *
- * @param driver	The driver to which the devices are passed.
- */
-static void pass_devices_to_driver(driver_t *driver, dev_tree_t *tree)
-{
-	dev_node_t *dev;
-	link_t *link;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver(driver=\"%s\")",
-	    driver->name);
-
-	fibril_mutex_lock(&driver->driver_mutex);
-
-	/*
-	 * Go through devices list as long as there is some device
-	 * that has not been passed to the driver.
-	 */
-	link = driver->devices.head.next;
-	while (link != &driver->devices.head) {
-		dev = list_get_instance(link, dev_node_t, driver_devices);
-		fibril_rwlock_write_lock(&tree->rwlock);
-		
-		if (dev->passed_to_driver) {
-			fibril_rwlock_write_unlock(&tree->rwlock);
-			link = link->next;
-			continue;
-		}
-
-		log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver: dev->refcnt=%d\n",
-		    (int)atomic_get(&dev->refcnt));
-		dev_add_ref(dev);
-
-		/*
-		 * Unlock to avoid deadlock when adding device
-		 * handled by itself.
-		 */
-		fibril_mutex_unlock(&driver->driver_mutex);
-		fibril_rwlock_write_unlock(&tree->rwlock);
-
-		add_device(driver, dev, tree);
-
-		dev_del_ref(dev);
-
-		/*
-		 * Lock again as we will work with driver's
-		 * structure.
-		 */
-		fibril_mutex_lock(&driver->driver_mutex);
-
-		/*
-		 * Restart the cycle to go through all devices again.
-		 */
-		link = driver->devices.head.next;
-	}
-
-	/*
-	 * Once we passed all devices to the driver, we need to mark the
-	 * driver as running.
-	 * It is vital to do it here and inside critical section.
-	 *
-	 * If we would change the state earlier, other devices added to
-	 * the driver would be added to the device list and started
-	 * immediately and possibly started here as well.
-	 */
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' enters running state.", driver->name);
-	driver->state = DRIVER_RUNNING;
-
-	fibril_mutex_unlock(&driver->driver_mutex);
-}
-
-/** Finish the initialization of a driver after it has succesfully started
- * and after it has registered itself by the device manager.
- *
- * Pass devices formerly matched to the driver to the driver and remember the
- * driver is running and fully functional now.
- *
- * @param driver	The driver which registered itself as running by the
- *			device manager.
- */
-void initialize_running_driver(driver_t *driver, dev_tree_t *tree)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "initialize_running_driver(driver=\"%s\")",
-	    driver->name);
-	
-	/*
-	 * Pass devices which have been already assigned to the driver to the
-	 * driver.
-	 */
-	pass_devices_to_driver(driver, tree);
-}
-
-/** Initialize device driver structure.
- *
- * @param drv		The device driver structure.
- */
-void init_driver(driver_t *drv)
-{
-	assert(drv != NULL);
-
-	memset(drv, 0, sizeof(driver_t));
-	list_initialize(&drv->match_ids.ids);
-	list_initialize(&drv->devices);
-	fibril_mutex_initialize(&drv->driver_mutex);
-	drv->sess = NULL;
-}
-
-/** Device driver structure clean-up.
- *
- * @param drv		The device driver structure.
- */
-void clean_driver(driver_t *drv)
-{
-	assert(drv != NULL);
-
-	free_not_null(drv->name);
-	free_not_null(drv->binary_path);
-
-	clean_match_ids(&drv->match_ids);
-
-	init_driver(drv);
-}
-
-/** Delete device driver structure.
- *
- * @param drv		The device driver structure.
- */
-void delete_driver(driver_t *drv)
-{
-	assert(drv != NULL);
-	
-	clean_driver(drv);
-	free(drv);
-}
-
-/** Create loc path and name for the function. */
-void loc_register_tree_function(fun_node_t *fun, dev_tree_t *tree)
-{
-	char *loc_pathname = NULL;
-	char *loc_name = NULL;
-	
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-	
-	asprintf(&loc_name, "%s", fun->pathname);
-	if (loc_name == NULL)
-		return;
-	
-	replace_char(loc_name, '/', LOC_SEPARATOR);
-	
-	asprintf(&loc_pathname, "%s/%s", LOC_DEVICE_NAMESPACE,
-	    loc_name);
-	if (loc_pathname == NULL) {
-		free(loc_name);
-		return;
-	}
-	
-	loc_service_register_with_iface(loc_pathname,
-	    &fun->service_id, DEVMAN_CONNECT_FROM_LOC);
-	
-	tree_add_loc_function(tree, fun);
-	
-	free(loc_name);
-	free(loc_pathname);
-}
-
-/** Pass a device to running driver.
- *
- * @param drv		The driver's structure.
- * @param node		The device's node in the device tree.
- */
-void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
-{
-	/*
-	 * We do not expect to have driver's mutex locked as we do not
-	 * access any structures that would affect driver_t.
-	 */
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
-	    drv->name, dev->pfun->name);
-	
-	/* Send the device to the driver. */
-	devman_handle_t parent_handle;
-	if (dev->pfun) {
-		parent_handle = dev->pfun->handle;
-	} else {
-		parent_handle = 0;
-	}
-	
-	async_exch_t *exch = async_exchange_begin(drv->sess);
-	
-	ipc_call_t answer;
-	aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
-	    parent_handle, &answer);
-	
-	/* Send the device name to the driver. */
-	sysarg_t rc = async_data_write_start(exch, dev->pfun->name,
-	    str_size(dev->pfun->name) + 1);
-	
-	async_exchange_end(exch);
-	
-	if (rc != EOK) {
-		/* TODO handle error */
-	}
-
-	/* Wait for answer from the driver. */
-	async_wait_for(req, &rc);
-
-	switch(rc) {
-	case EOK:
-		dev->state = DEVICE_USABLE;
-		break;
-	case ENOENT:
-		dev->state = DEVICE_NOT_PRESENT;
-		break;
-	default:
-		dev->state = DEVICE_INVALID;
-		break;
-	}
-	
-	dev->passed_to_driver = true;
-
-	return;
-}
-
-/** Find suitable driver for a device and assign the driver to it.
- *
- * @param node		The device node of the device in the device tree.
- * @param drivers_list	The list of available drivers.
- * @return		True if the suitable driver is found and
- *			successfully assigned to the device, false otherwise.
- */
-bool assign_driver(dev_node_t *dev, driver_list_t *drivers_list,
-    dev_tree_t *tree)
-{
-	assert(dev != NULL);
-	assert(drivers_list != NULL);
-	assert(tree != NULL);
-	
-	/*
-	 * Find the driver which is the most suitable for handling this device.
-	 */
-	driver_t *drv = find_best_match_driver(drivers_list, dev);
-	if (drv == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "No driver found for device `%s'.",
-		    dev->pfun->pathname);
-		return false;
-	}
-	
-	/* Attach the driver to the device. */
-	attach_driver(tree, dev, drv);
-	
-	fibril_mutex_lock(&drv->driver_mutex);
-	if (drv->state == DRIVER_NOT_STARTED) {
-		/* Start the driver. */
-		start_driver(drv);
-	}
-	bool is_running = drv->state == DRIVER_RUNNING;
-	fibril_mutex_unlock(&drv->driver_mutex);
-
-	/* Notify the driver about the new device. */
-	if (is_running)
-		add_device(drv, dev, tree);
-	
-	fibril_mutex_lock(&drv->driver_mutex);
-	fibril_mutex_unlock(&drv->driver_mutex);
-
-	fibril_rwlock_write_lock(&tree->rwlock);
-	if (dev->pfun != NULL) {
-		dev->pfun->state = FUN_ON_LINE;
-	}
-	fibril_rwlock_write_unlock(&tree->rwlock);
-	return true;
-}
-
-int driver_dev_remove(dev_tree_t *tree, dev_node_t *dev)
-{
-	async_exch_t *exch;
-	sysarg_t retval;
-	driver_t *drv;
-	devman_handle_t handle;
-	
-	assert(dev != NULL);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_remove(%p)", dev);
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	drv = dev->drv;
-	handle = dev->handle;
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	exch = async_exchange_begin(drv->sess);
-	retval = async_req_1_0(exch, DRIVER_DEV_REMOVE, handle);
-	async_exchange_end(exch);
-	
-	return retval;
-}
-
-int driver_dev_gone(dev_tree_t *tree, dev_node_t *dev)
-{
-	async_exch_t *exch;
-	sysarg_t retval;
-	driver_t *drv;
-	devman_handle_t handle;
-	
-	assert(dev != NULL);
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_gone(%p)", dev);
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	drv = dev->drv;
-	handle = dev->handle;
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	exch = async_exchange_begin(drv->sess);
-	retval = async_req_1_0(exch, DRIVER_DEV_GONE, handle);
-	async_exchange_end(exch);
-	
-	return retval;
-}
-
-int driver_fun_online(dev_tree_t *tree, fun_node_t *fun)
-{
-	async_exch_t *exch;
-	sysarg_t retval;
-	driver_t *drv;
-	devman_handle_t handle;
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_online(%p)", fun);
-
-	fibril_rwlock_read_lock(&tree->rwlock);
-	
-	if (fun->dev == NULL) {
-		/* XXX root function? */
-		fibril_rwlock_read_unlock(&tree->rwlock);
-		return EINVAL;
-	}
-	
-	drv = fun->dev->drv;
-	handle = fun->handle;
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	exch = async_exchange_begin(drv->sess);
-	retval = async_req_1_0(exch, DRIVER_FUN_ONLINE, handle);
-	loc_exchange_end(exch);
-	
-	return retval;
-}
-
-int driver_fun_offline(dev_tree_t *tree, fun_node_t *fun)
-{
-	async_exch_t *exch;
-	sysarg_t retval;
-	driver_t *drv;
-	devman_handle_t handle;
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_offline(%p)", fun);
-
-	fibril_rwlock_read_lock(&tree->rwlock);
-	if (fun->dev == NULL) {
-		/* XXX root function? */
-		fibril_rwlock_read_unlock(&tree->rwlock);
-		return EINVAL;
-	}
-	
-	drv = fun->dev->drv;
-	handle = fun->handle;
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	exch = async_exchange_begin(drv->sess);
-	retval = async_req_1_0(exch, DRIVER_FUN_OFFLINE, handle);
-	loc_exchange_end(exch);
-	
-	return retval;
-
-}
-
-/** Initialize the device tree.
- *
- * Create root device node of the tree and assign driver to it.
- *
- * @param tree		The device tree.
- * @param drivers_list	the list of available drivers.
- * @return		True on success, false otherwise.
- */
-bool init_device_tree(dev_tree_t *tree, driver_list_t *drivers_list)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "init_device_tree()");
-	
-	tree->current_handle = 0;
-	
-	hash_table_create(&tree->devman_devices, 0, 0, &devman_devices_ops);
-	hash_table_create(&tree->devman_functions, 0, 0, &devman_functions_ops);
-	hash_table_create(&tree->loc_functions, 0, 0, &loc_devices_ops);
-	
-	fibril_rwlock_initialize(&tree->rwlock);
-	
-	/* Create root function and root device and add them to the device tree. */
-	if (!create_root_nodes(tree))
-		return false;
-    
-	/* Find suitable driver and start it. */
-	dev_node_t *rdev = tree->root_node->child;
-	dev_add_ref(rdev);
-	int rc = assign_driver(rdev, drivers_list, tree);
-	dev_del_ref(rdev);
-	
-	return rc;
-}
-
-/* Device nodes */
-
-/** Create a new device node.
- *
- * @return		A device node structure.
- */
-dev_node_t *create_dev_node(void)
-{
-	dev_node_t *dev;
-	
-	dev = calloc(1, sizeof(dev_node_t));
-	if (dev == NULL)
-		return NULL;
-	
-	atomic_set(&dev->refcnt, 0);
-	list_initialize(&dev->functions);
-	link_initialize(&dev->driver_devices);
-	
-	return dev;
-}
-
-/** Delete a device node.
- *
- * @param node		The device node structure.
- */
-void delete_dev_node(dev_node_t *dev)
-{
-	assert(list_empty(&dev->functions));
-	assert(dev->pfun == NULL);
-	assert(dev->drv == NULL);
-	
-	free(dev);
-}
-
-/** Increase device node reference count.
- *
- * @param dev	Device node
- */
-void dev_add_ref(dev_node_t *dev)
-{
-	atomic_inc(&dev->refcnt);
-}
-
-/** Decrease device node reference count.
- *
- * When the count drops to zero the device node is freed.
- *
- * @param dev	Device node
- */
-void dev_del_ref(dev_node_t *dev)
-{
-	if (atomic_predec(&dev->refcnt) == 0)
-		delete_dev_node(dev);
-}
-
-/** Find the device node structure of the device witch has the specified handle.
- *
- * @param tree		The device tree where we look for the device node.
- * @param handle	The handle of the device.
- * @return		The device node.
- */
-dev_node_t *find_dev_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
-{
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-	
-	ht_link_t *link = hash_table_find(&tree->devman_devices, &handle);
-	if (link == NULL)
-		return NULL;
-	
-	return hash_table_get_inst(link, dev_node_t, devman_dev);
-}
-
-/** Find the device node structure of the device witch has the specified handle.
- *
- * @param tree		The device tree where we look for the device node.
- * @param handle	The handle of the device.
- * @return		The device node.
- */
-dev_node_t *find_dev_node(dev_tree_t *tree, devman_handle_t handle)
-{
-	dev_node_t *dev = NULL;
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	dev = find_dev_node_no_lock(tree, handle);
-	if (dev != NULL)
-		dev_add_ref(dev);
-	
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	return dev;
-}
-
-/** Get list of device functions. */
-int dev_get_functions(dev_tree_t *tree, dev_node_t *dev,
-    devman_handle_t *hdl_buf, size_t buf_size, size_t *act_size)
-{
-	size_t act_cnt;
-	size_t buf_cnt;
-
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-
-	buf_cnt = buf_size / sizeof(devman_handle_t);
-
-	act_cnt = list_count(&dev->functions);
-	*act_size = act_cnt * sizeof(devman_handle_t);
-
-	if (buf_size % sizeof(devman_handle_t) != 0)
-		return EINVAL;
-
-	size_t pos = 0;
-	list_foreach(dev->functions, item) {
-		fun_node_t *fun =
-		    list_get_instance(item, fun_node_t, dev_functions);
-
-		if (pos < buf_cnt) {
-			hdl_buf[pos] = fun->handle;
-		}
-
-		pos++;
-	}
-
-	return EOK;
-}
-
-
-/* Function nodes */
-
-/** Create a new function node.
- *
- * @return		A function node structure.
- */
-fun_node_t *create_fun_node(void)
-{
-	fun_node_t *fun;
-
-	fun = calloc(1, sizeof(fun_node_t));
-	if (fun == NULL)
-		return NULL;
-	
-	fun->state = FUN_INIT;
-	atomic_set(&fun->refcnt, 0);
-	fibril_mutex_initialize(&fun->busy_lock);
-	link_initialize(&fun->dev_functions);
-	list_initialize(&fun->match_ids.ids);
-	
-	return fun;
-}
-
-/** Delete a function node.
- *
- * @param fun		The device node structure.
- */
-void delete_fun_node(fun_node_t *fun)
-{
-	assert(fun->dev == NULL);
-	assert(fun->child == NULL);
-	
-	clean_match_ids(&fun->match_ids);
-	free_not_null(fun->name);
-	free_not_null(fun->pathname);
-	free(fun);
-}
-
-/** Increase function node reference count.
- *
- * @param fun	Function node
- */
-void fun_add_ref(fun_node_t *fun)
-{
-	atomic_inc(&fun->refcnt);
-}
-
-/** Decrease function node reference count.
- *
- * When the count drops to zero the function node is freed.
- *
- * @param fun	Function node
- */
-void fun_del_ref(fun_node_t *fun)
-{
-	if (atomic_predec(&fun->refcnt) == 0)
-		delete_fun_node(fun);
-}
-
-/** Make function busy for reconfiguration operations. */
-void fun_busy_lock(fun_node_t *fun)
-{
-	fibril_mutex_lock(&fun->busy_lock);
-}
-
-/** Mark end of reconfiguration operation. */
-void fun_busy_unlock(fun_node_t *fun)
-{
-	fibril_mutex_unlock(&fun->busy_lock);
-}
-
-/** Find the function node with the specified handle.
- *
- * @param tree		The device tree where we look for the device node.
- * @param handle	The handle of the function.
- * @return		The function node.
- */
-fun_node_t *find_fun_node_no_lock(dev_tree_t *tree, devman_handle_t handle)
-{
-	fun_node_t *fun;
-	
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-	
-	ht_link_t *link = hash_table_find(&tree->devman_functions, &handle);
-	if (link == NULL)
-		return NULL;
-	
-	fun = hash_table_get_inst(link, fun_node_t, devman_fun);
-	
-	return fun;
-}
-
-/** Find the function node with the specified handle.
- *
- * @param tree		The device tree where we look for the device node.
- * @param handle	The handle of the function.
- * @return		The function node.
- */
-fun_node_t *find_fun_node(dev_tree_t *tree, devman_handle_t handle)
-{
-	fun_node_t *fun = NULL;
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	
-	fun = find_fun_node_no_lock(tree, handle);
-	if (fun != NULL)
-		fun_add_ref(fun);
-	
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	return fun;
-}
-
-/** Create and set device's full path in device tree.
- *
- * @param tree		Device tree
- * @param node		The device's device node.
- * @param parent	The parent device node.
- * @return		True on success, false otherwise (insufficient
- *			resources etc.).
- */
-static bool set_fun_path(dev_tree_t *tree, fun_node_t *fun, fun_node_t *parent)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	assert(fun->name != NULL);
-	
-	size_t pathsize = (str_size(fun->name) + 1);
-	if (parent != NULL)
-		pathsize += str_size(parent->pathname) + 1;
-	
-	fun->pathname = (char *) malloc(pathsize);
-	if (fun->pathname == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate device path.");
-		return false;
-	}
-	
-	if (parent != NULL) {
-		str_cpy(fun->pathname, pathsize, parent->pathname);
-		str_append(fun->pathname, pathsize, "/");
-		str_append(fun->pathname, pathsize, fun->name);
-	} else {
-		str_cpy(fun->pathname, pathsize, fun->name);
-	}
-	
-	return true;
-}
-
-/** Insert new device into device tree.
- *
- * @param tree		The device tree.
- * @param dev		The newly added device node.
- * @param pfun		The parent function node.
- *
- * @return		True on success, false otherwise (insufficient resources
- *			etc.).
- */
-bool insert_dev_node(dev_tree_t *tree, dev_node_t *dev, fun_node_t *pfun)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "insert_dev_node(dev=%p, pfun=%p [\"%s\"])",
-	    dev, pfun, pfun->pathname);
-
-	/* Add the node to the handle-to-node map. */
-	dev->handle = ++tree->current_handle;
-	hash_table_insert(&tree->devman_devices, &dev->devman_dev);
-
-	/* Add the node to the list of its parent's children. */
-	dev->pfun = pfun;
-	pfun->child = dev;
-	
-	return true;
-}
-
-/** Remove device from device tree.
- *
- * @param tree		Device tree
- * @param dev		Device node
- */
-void remove_dev_node(dev_tree_t *tree, dev_node_t *dev)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
-	
-	/* Remove node from the handle-to-node map. */
-	hash_table_remove(&tree->devman_devices, &dev->handle);
-	
-	/* Unlink from parent function. */
-	dev->pfun->child = NULL;
-	dev->pfun = NULL;
-	
-	dev->state = DEVICE_REMOVED;
-}
-
-
-/** Insert new function into device tree.
- *
- * @param tree		The device tree.
- * @param fun		The newly added function node. 
- * @param fun_name	The name of the newly added function.
- * @param dev		Owning device node.
- *
- * @return		True on success, false otherwise (insufficient resources
- *			etc.).
- */
-bool insert_fun_node(dev_tree_t *tree, fun_node_t *fun, char *fun_name,
-    dev_node_t *dev)
-{
-	fun_node_t *pfun;
-	
-	assert(fun_name != NULL);
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	/*
-	 * The root function is a special case, it does not belong to any
-	 * device so for the root function dev == NULL.
-	 */
-	pfun = (dev != NULL) ? dev->pfun : NULL;
-	
-	fun->name = fun_name;
-	if (!set_fun_path(tree, fun, pfun)) {
-		return false;
-	}
-	
-	/* Add the node to the handle-to-node map. */
-	fun->handle = ++tree->current_handle;
-	hash_table_insert(&tree->devman_functions, &fun->devman_fun);
-
-	/* Add the node to the list of its parent's children. */
-	fun->dev = dev;
-	if (dev != NULL)
-		list_append(&fun->dev_functions, &dev->functions);
-	
-	return true;
-}
-
-/** Remove function from device tree.
- *
- * @param tree		Device tree
- * @param node		Function node to remove
- */
-void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	/* Remove the node from the handle-to-node map. */
-	hash_table_remove(&tree->devman_functions, &fun->handle);
-	
-	/* Remove the node from the list of its parent's children. */
-	if (fun->dev != NULL)
-		list_remove(&fun->dev_functions);
-	
-	fun->dev = NULL;
-	fun->state = FUN_REMOVED;
-}
-
-/** Find function node with a specified path in the device tree.
- * 
- * @param path		The path of the function node in the device tree.
- * @param tree		The device tree.
- * @return		The function node if it is present in the tree, NULL
- *			otherwise.
- */
-fun_node_t *find_fun_node_by_path(dev_tree_t *tree, char *path)
-{
-	assert(path != NULL);
-
-	bool is_absolute = path[0] == '/';
-	if (!is_absolute) {
-		return NULL;
-	}
-
-	fibril_rwlock_read_lock(&tree->rwlock);
-	
-	fun_node_t *fun = tree->root_node;
-	fun_add_ref(fun);
-	/*
-	 * Relative path to the function from its parent (but with '/' at the
-	 * beginning)
-	 */
-	char *rel_path = path;
-	char *next_path_elem = NULL;
-	bool cont = (rel_path[1] != '\0');
-	
-	while (cont && fun != NULL) {
-		next_path_elem  = get_path_elem_end(rel_path + 1);
-		if (next_path_elem[0] == '/') {
-			cont = true;
-			next_path_elem[0] = 0;
-		} else {
-			cont = false;
-		}
-		
-		fun_node_t *cfun = find_node_child(tree, fun, rel_path + 1);
-		fun_del_ref(fun);
-		fun = cfun;
-		
-		if (cont) {
-			/* Restore the original path. */
-			next_path_elem[0] = '/';
-		}
-		rel_path = next_path_elem;
-	}
-	
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	return fun;
-}
-
-/** Find function with a specified name belonging to given device.
- *
- * Device tree rwlock should be held at least for reading.
- *
- * @param tree Device tree
- * @param dev Device the function belongs to.
- * @param name Function name (not path).
- * @return Function node.
- * @retval NULL No function with given name.
- */
-fun_node_t *find_fun_node_in_device(dev_tree_t *tree, dev_node_t *dev,
-    const char *name)
-{
-	assert(name != NULL);
-	assert(fibril_rwlock_is_locked(&tree->rwlock));
-
-	fun_node_t *fun;
-
-	list_foreach(dev->functions, link) {
-		fun = list_get_instance(link, fun_node_t, dev_functions);
-
-		if (str_cmp(name, fun->name) == 0) {
-			fun_add_ref(fun);
-			return fun;
-		}
-	}
-
-	return NULL;
-}
-
-/** Find child function node with a specified name.
- *
- * Device tree rwlock should be held at least for reading.
- *
- * @param tree		Device tree
- * @param parent	The parent function node.
- * @param name		The name of the child function.
- * @return		The child function node.
- */
-static fun_node_t *find_node_child(dev_tree_t *tree, fun_node_t *pfun,
-    const char *name)
-{
-	return find_fun_node_in_device(tree, pfun->child, name);
-}
-
-/* loc devices */
-
-fun_node_t *find_loc_tree_function(dev_tree_t *tree, service_id_t service_id)
-{
-	fun_node_t *fun = NULL;
-	
-	fibril_rwlock_read_lock(&tree->rwlock);
-	ht_link_t *link = hash_table_find(&tree->loc_functions, &service_id);
-	if (link != NULL) {
-		fun = hash_table_get_inst(link, fun_node_t, loc_fun);
-		fun_add_ref(fun);
-	}
-	fibril_rwlock_read_unlock(&tree->rwlock);
-	
-	return fun;
-}
-
-void tree_add_loc_function(dev_tree_t *tree, fun_node_t *fun)
-{
-	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
-	
-	hash_table_insert(&tree->loc_functions, &fun->loc_fun);
-}
-
-/** @}
- */
Index: uspace/srv/devman/devman.h
===================================================================
--- uspace/srv/devman/devman.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/devman/devman.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/devtree.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/devtree.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/driver.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/driver.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/drv_conn.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/drv_conn.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/fun.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/fun.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/loc.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/loc.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/devman/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/main.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/devman/match.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/devman/match.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup devman
+ * @{
+ */
+
+#ifndef MATCH_H_
+#define MATCH_H_
+
+#include <stdbool.h>
+
+#include "devman.h"
+
+#define MATCH_EXT ".ma"
+
+extern int get_match_score(driver_t *, dev_node_t *);
+extern bool parse_match_ids(char *, match_id_list_t *);
+extern bool read_match_ids(const char *, match_id_list_t *);
+extern char *read_match_id(char **);
+extern char *read_id(const char **);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/devman/util.c
===================================================================
--- uspace/srv/devman/util.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/devman/util.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -85,10 +85,4 @@
 }
 
-void free_not_null(const void *ptr)
-{
-	if (ptr != NULL)
-		free(ptr);
-}
-
 void replace_char(char *str, char orig, char repl)
 {
Index: uspace/srv/devman/util.h
===================================================================
--- uspace/srv/devman/util.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/devman/util.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/fs/cdfs/cdfs_ops.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -365,8 +365,5 @@
 	
 	/* Check for duplicate entries */
-	list_foreach(parent->cs_list, link) {
-		cdfs_dentry_t *dentry =
-		    list_get_instance(link, cdfs_dentry_t, link);
-		
+	list_foreach(parent->cs_list, link, cdfs_dentry_t, dentry) {
 		if (str_cmp(dentry->name, name) == 0)
 			return EEXIST;
@@ -524,8 +521,5 @@
 	}
 	
-	list_foreach(parent->cs_list, link) {
-		cdfs_dentry_t *dentry =
-		    list_get_instance(link, cdfs_dentry_t, link);
-		
+	list_foreach(parent->cs_list, link, cdfs_dentry_t, dentry) {
 		if (str_cmp(dentry->name, component) == 0) {
 			*fn = get_cached_node(parent->service_id, dentry->index);
@@ -623,4 +617,25 @@
 {
 	return 0;
+}
+
+static int cdfs_size_block(service_id_t service_id, uint32_t *size)
+{
+	*size = BLOCK_SIZE;
+
+	return EOK; 
+}
+
+static int cdfs_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	*count = 0;
+	
+	return EOK;
+}
+
+static int cdfs_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	*count = 0;
+	
+	return EOK;
 }
 
@@ -641,5 +656,8 @@
 	.is_directory = cdfs_is_directory,
 	.is_file = cdfs_is_file,
-	.service_get = cdfs_service_get
+	.service_get = cdfs_service_get,
+	.size_block = cdfs_size_block,
+	.total_block_count = cdfs_total_block_count,
+	.free_block_count = cdfs_free_block_count
 };
 
Index: uspace/srv/fs/exfat/exfat_idx.c
===================================================================
--- uspace/srv/fs/exfat/exfat_idx.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/fs/exfat/exfat_idx.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -88,11 +88,8 @@
 static unused_t *unused_find(service_id_t service_id, bool lock)
 {
-	unused_t *u;
-
 	if (lock)
 		fibril_mutex_lock(&unused_lock);
 
-	list_foreach(unused_list, l) {
-		u = list_get_instance(l, unused_t, link);
+	list_foreach(unused_list, link, unused_t, u) {
 		if (u->service_id == service_id) 
 			return u;
Index: uspace/srv/fs/exfat/exfat_ops.c
===================================================================
--- uspace/srv/fs/exfat/exfat_ops.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/fs/exfat/exfat_ops.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -89,4 +89,7 @@
 static bool exfat_is_file(fs_node_t *node);
 static service_id_t exfat_service_get(fs_node_t *node);
+static int exfat_size_block(service_id_t, uint32_t *);
+static int exfat_total_block_count(service_id_t, uint64_t *);
+static int exfat_free_block_count(service_id_t, uint64_t *);
 
 /*
@@ -155,5 +158,4 @@
 static int exfat_node_fini_by_service_id(service_id_t service_id)
 {
-	exfat_node_t *nodep;
 	int rc;
 
@@ -166,6 +168,5 @@
 restart:
 	fibril_mutex_lock(&ffn_mutex);
-	list_foreach(ffn_list, lnk) {
-		nodep = list_get_instance(lnk, exfat_node_t, ffn_link);
+	list_foreach(ffn_list, ffn_link, exfat_node_t, nodep) {
 		if (!fibril_mutex_trylock(&nodep->lock)) {
 			fibril_mutex_unlock(&ffn_mutex);
@@ -912,4 +913,81 @@
 }
 
+int exfat_size_block(service_id_t service_id, uint32_t *size)
+{
+	exfat_bs_t *bs;
+	bs = block_bb_get(service_id);
+	*size = BPC(bs);
+
+	return EOK;
+}
+
+int exfat_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	exfat_bs_t *bs;
+	bs = block_bb_get(service_id);
+	*count = DATA_CNT(bs);
+	
+	return EOK;
+}
+
+int exfat_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	fs_node_t *node;
+	exfat_node_t *bmap_node;
+	exfat_bs_t *bs;
+	uint64_t free_block_count = 0;
+	uint64_t block_count;
+	unsigned sector;
+	int rc;
+
+	rc = exfat_total_block_count(service_id, &block_count);
+	if (rc != EOK)
+		goto exit;
+
+	bs = block_bb_get(service_id);
+	node = NULL;
+	rc = exfat_bitmap_get(&node, service_id);
+	if (rc != EOK)
+		goto exit;
+
+	bmap_node = (exfat_node_t *) node->data;
+
+	unsigned const bmap_sectors = ROUND_UP(bmap_node->size, BPS(bs)) /
+	    BPS(bs);
+
+	for (sector = 0; sector < bmap_sectors; ++sector) {
+
+		block_t *block;
+		uint8_t *bitmap;
+		unsigned bit;
+
+		rc = exfat_block_get(&block, bs, bmap_node, sector,
+		    BLOCK_FLAGS_NONE);
+		if (rc != EOK) {
+			free_block_count = 0;
+			goto exit;
+		}
+
+		bitmap = (uint8_t *) block->data;
+
+		for (bit = 0; bit < BPS(bs) * 8 && block_count > 0;
+		    ++bit, --block_count) {
+			if (!(bitmap[bit / 8] & (1 << (bit % 8))))
+				++free_block_count;
+		}
+
+		block_put(block);
+
+		if (block_count == 0) {
+			/* Reached the end of the bitmap */
+			goto exit;
+		}
+	}
+
+exit:
+	exfat_node_put(node);
+	*count = free_block_count;
+	return rc;
+}
 
 /** libfs operations */
@@ -930,5 +1008,8 @@
 	.is_directory = exfat_is_directory,
 	.is_file = exfat_is_file,
-	.service_get = exfat_service_get
+	.service_get = exfat_service_get,
+	.size_block = exfat_size_block,
+	.total_block_count = exfat_total_block_count,
+	.free_block_count = exfat_free_block_count
 };
 
Index: uspace/srv/fs/ext4fs/ext4fs_ops.c
===================================================================
--- uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/fs/ext4fs/ext4fs_ops.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -101,4 +101,7 @@
 static bool ext4fs_is_file(fs_node_t *node);
 static service_id_t ext4fs_service_get(fs_node_t *node);
+static int ext4fs_size_block(service_id_t, uint32_t *);
+static int ext4fs_total_block_count(service_id_t, uint64_t *);
+static int ext4fs_free_block_count(service_id_t, uint64_t *);
 
 /* Static variables */
@@ -194,8 +197,5 @@
 	}
 	
-	list_foreach(instance_list, link) {
-		ext4fs_instance_t *tmp =
-		    list_get_instance(link, ext4fs_instance_t, link);
-		
+	list_foreach(instance_list, link, ext4fs_instance_t, tmp) {
 		if (tmp->service_id == service_id) {
 			*inst = tmp;
@@ -836,4 +836,49 @@
 	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;
+
+	ext4_superblock_t *sb = inst->filesystem->superblock;
+	*count = ext4_superblock_get_free_blocks_count(sb);
+
+	return EOK;
 }
 
@@ -857,5 +902,8 @@
 	.is_directory = ext4fs_is_directory,
 	.is_file = ext4fs_is_file,
-	.service_get = ext4fs_service_get
+	.service_get = ext4fs_service_get,
+	.size_block = ext4fs_size_block,
+	.total_block_count = ext4fs_total_block_count,
+	.free_block_count = ext4fs_free_block_count
 };
 
Index: uspace/srv/fs/fat/fat_idx.c
===================================================================
--- uspace/srv/fs/fat/fat_idx.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/fs/fat/fat_idx.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -88,11 +88,8 @@
 static unused_t *unused_find(service_id_t service_id, bool lock)
 {
-	unused_t *u;
-
 	if (lock)
 		fibril_mutex_lock(&unused_lock);
 
-	list_foreach(unused_list, l) {
-		u = list_get_instance(l, unused_t, link);
+	list_foreach(unused_list, link, unused_t, u) {
 		if (u->service_id == service_id) 
 			return u;
Index: uspace/srv/fs/fat/fat_ops.c
===================================================================
--- uspace/srv/fs/fat/fat_ops.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/fs/fat/fat_ops.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -91,4 +91,7 @@
 static bool fat_is_file(fs_node_t *node);
 static service_id_t fat_service_get(fs_node_t *node);
+static int fat_size_block(service_id_t, uint32_t *);
+static int fat_total_block_count(service_id_t, uint64_t *);
+static int fat_free_block_count(service_id_t, uint64_t *);
 
 /*
@@ -149,5 +152,4 @@
 static int fat_node_fini_by_service_id(service_id_t service_id)
 {
-	fat_node_t *nodep;
 	int rc;
 
@@ -160,6 +162,5 @@
 restart:
 	fibril_mutex_lock(&ffn_mutex);
-	list_foreach(ffn_list, lnk) {
-		nodep = list_get_instance(lnk, fat_node_t, ffn_link);
+	list_foreach(ffn_list, ffn_link, fat_node_t, nodep) {
 		if (!fibril_mutex_trylock(&nodep->lock)) {
 			fibril_mutex_unlock(&ffn_mutex);
@@ -843,4 +844,48 @@
 }
 
+int fat_size_block(service_id_t service_id, uint32_t *size)
+{
+	fat_bs_t *bs;
+
+	bs = block_bb_get(service_id);
+	*size = BPC(bs);
+
+	return EOK;
+}
+
+int fat_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	fat_bs_t *bs;
+	
+	bs = block_bb_get(service_id);
+	*count = (SPC(bs)) ? TS(bs) / SPC(bs) : 0;
+
+	return EOK;
+}
+
+int fat_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	fat_bs_t *bs;
+	fat_cluster_t e0;
+	uint64_t block_count;
+	int rc;
+	uint32_t cluster_no, clusters;
+
+	block_count = 0;
+	bs = block_bb_get(service_id);
+	clusters = (SPC(bs)) ? TS(bs) / SPC(bs) : 0;
+	for (cluster_no = 0; cluster_no < clusters; cluster_no++) {
+		rc = fat_get_cluster(bs, service_id, FAT1, cluster_no, &e0);
+		if (rc != EOK)
+			return EIO;
+
+		if (e0 == FAT_CLST_RES0)
+			block_count++;
+	}
+	*count = block_count;
+	
+	return EOK;
+}
+
 /** libfs operations */
 libfs_ops_t fat_libfs_ops = {
@@ -860,5 +905,8 @@
 	.is_directory = fat_is_directory,
 	.is_file = fat_is_file,
-	.service_get = fat_service_get
+	.service_get = fat_service_get,
+	.size_block = fat_size_block,
+	.total_block_count = fat_total_block_count,
+	.free_block_count = fat_free_block_count
 };
 
Index: uspace/srv/fs/mfs/mfs.h
===================================================================
--- uspace/srv/fs/mfs/mfs.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/fs/mfs/mfs.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -44,4 +44,5 @@
 #include <errno.h>
 #include <assert.h>
+#include <stdbool.h>
 #include "../../vfs/vfs.h"
 
@@ -57,4 +58,14 @@
 #define mfsdebug(...)
 #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;
@@ -95,4 +106,14 @@
 	unsigned isearch;
 	unsigned zsearch;
+
+	/* Indicates wether if the cached number of free zones
+	 * is to be considered valid or not.
+	 */
+	bool nfree_zones_valid;
+	/* Cached number of free zones, used to avoid to scan
+	 * the whole bitmap every time the mfs_free_block_count()
+	 * is invoked.
+	 */
+	unsigned nfree_zones;
 };
 
@@ -201,4 +222,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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/fs/mfs/mfs_balloc.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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.
  *
@@ -84,4 +88,11 @@
 {
 	int r = mfs_alloc_bit(inst, zone, BMAP_ZONE);
+	if (r != EOK)
+		return r;
+
+	/* Update the cached number of free zones */
+	struct mfs_sb_info *sbi = inst->sbi;
+	if (sbi->nfree_zones_valid)
+		sbi->nfree_zones--;
 
 	*zone += inst->sbi->firstdatazone - 1;
@@ -99,7 +110,111 @@
 mfs_free_zone(struct mfs_instance *inst, uint32_t zone)
 {
+	int r;
+
 	zone -= inst->sbi->firstdatazone - 1;
 
-	return mfs_free_bit(inst, zone, BMAP_ZONE);
+	r = mfs_free_bit(inst, zone, BMAP_ZONE);
+	if (r != EOK)
+		return r;
+
+	/* Update the cached number of free zones */
+	struct mfs_sb_info *sbi = inst->sbi;
+	if (sbi->nfree_zones_valid)
+		sbi->nfree_zones++;
+
+	return r;
+}
+
+/** 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 +239,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 +251,4 @@
 		/* bid == BMAP_INODE */
 		search = &sbi->isearch;
-		start_block = 2;
 		if (idx > sbi->ninodes) {
 			printf(NAME ": Error! Trying to free beyond the "
@@ -192,15 +307,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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/fs/mfs/mfs_ops.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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
 };
 
@@ -210,4 +216,6 @@
 	sbi->isearch = 0;
 	sbi->zsearch = 0;
+	sbi->nfree_zones_valid = false;
+	sbi->nfree_zones = 0;
 
 	if (version == MFS_VERSION_V3) {
@@ -1129,4 +1137,71 @@
 }
 
+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;
+
+	struct mfs_sb_info *sbi = inst->sbi;
+
+	if (!sbi->nfree_zones_valid) {
+		/* The cached number of free zones is not valid,
+		 * we need to scan the bitmap to retrieve the
+		 * current value.
+		 */
+
+		rc = mfs_count_free_zones(inst, &block_free);
+		if (rc != EOK)
+			return rc;
+
+		sbi->nfree_zones = block_free;
+		sbi->nfree_zones_valid = true;
+	}
+
+	*count = sbi->nfree_zones;
+
+	return rc;
+}
+
 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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/fs/mfs/mfs_rw.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -104,7 +104,8 @@
     bool write_mode, uint32_t w_block)
 {
-	int r, nr_direct;
+	int nr_direct;
 	int ptrs_per_block;
-	uint32_t *ind_zone, *ind2_zone;
+	uint32_t *ind_zone = NULL, *ind2_zone = NULL;
+	int r = EOK;
 
 	struct mfs_ino_info *ino_i = mnode->ino_i;
@@ -130,5 +131,5 @@
 			ino_i->dirty = true;
 		}
-		return EOK;
+		goto out;
 	}
 
@@ -142,5 +143,5 @@
 				r = alloc_zone_and_clear(inst, &zone);
 				if (r != EOK)
-					return r;
+					goto out;
 
 				ino_i->i_izone[0] = zone;
@@ -149,5 +150,5 @@
 				/* Sparse block */
 				*b = 0;
-				return EOK;
+				goto out;
 			}
 		}
@@ -155,5 +156,5 @@
 		r = read_ind_zone(inst, ino_i->i_izone[0], &ind_zone);
 		if (r != EOK)
-			return r;
+			goto out;
 
 		*b = ind_zone[rblock];
@@ -163,5 +164,5 @@
 		}
 
-		goto out_free_ind1;
+		goto out;
 	}
 
@@ -176,5 +177,5 @@
 			r = alloc_zone_and_clear(inst, &zone);
 			if (r != EOK)
-				return r;
+				goto out;
 
 			ino_i->i_izone[1] = zone;
@@ -183,5 +184,5 @@
 			/* Sparse block */
 			*b = 0;
-			return EOK;
+			goto out;
 		}
 	}
@@ -189,5 +190,5 @@
 	r = read_ind_zone(inst, ino_i->i_izone[1], &ind_zone);
 	if (r != EOK)
-		return r;
+		goto out;
 
 	/*
@@ -203,5 +204,5 @@
 			r = alloc_zone_and_clear(inst, &zone);
 			if (r != EOK)
-				goto out_free_ind1;
+				goto out;
 
 			ind_zone[ind2_off] = zone;
@@ -209,7 +210,6 @@
 		} else {
 			/* Sparse block */
-			r = EOK;
 			*b = 0;
-			goto out_free_ind1;
+			goto out;
 		}
 	}
@@ -217,5 +217,5 @@
 	r = read_ind_zone(inst, ind_zone[ind2_off], &ind2_zone);
 	if (r != EOK)
-		goto out_free_ind1;
+		goto out;
 
 	*b = ind2_zone[rblock - (ind2_off * ptrs_per_block)];
@@ -225,8 +225,6 @@
 	}
 
-	r = EOK;
-
+out:
 	free(ind2_zone);
-out_free_ind1:
 	free(ind_zone);
 	return r;
Index: uspace/srv/fs/tmpfs/tmpfs_ops.c
===================================================================
--- uspace/srv/fs/tmpfs/tmpfs_ops.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/fs/tmpfs/tmpfs_ops.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -259,7 +259,5 @@
 	tmpfs_node_t *parentp = TMPFS_NODE(pfn);
 
-	list_foreach(parentp->cs_list, lnk) {
-		tmpfs_dentry_t *dentryp;
-		dentryp = list_get_instance(lnk, tmpfs_dentry_t, link);
+	list_foreach(parentp->cs_list, link, tmpfs_dentry_t, dentryp) {
 		if (!str_cmp(dentryp->name, component)) {
 			*rfn = FS_NODE(dentryp->node);
@@ -365,7 +363,6 @@
 
 	/* Check for duplicit entries. */
-	list_foreach(parentp->cs_list, lnk) {
-		dentryp = list_get_instance(lnk, tmpfs_dentry_t, link);	
-		if (!str_cmp(dentryp->name, nm))
+	list_foreach(parentp->cs_list, link, tmpfs_dentry_t, dp) {
+		if (!str_cmp(dp->name, nm))
 			return EEXIST;
 	}
@@ -400,8 +397,8 @@
 	if (!parentp)
 		return EBUSY;
-	
-	list_foreach(parentp->cs_list, lnk) {
-		dentryp = list_get_instance(lnk, tmpfs_dentry_t, link);
-		if (!str_cmp(dentryp->name, nm)) {
+
+	list_foreach(parentp->cs_list, link, tmpfs_dentry_t, dp) {
+		if (!str_cmp(dp->name, nm)) {
+			dentryp = dp;
 			childp = dentryp->node;
 			assert(FS_NODE(childp) == cfn);
Index: uspace/srv/fs/udf/udf_ops.c
===================================================================
--- uspace/srv/fs/udf/udf_ops.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/fs/udf/udf_ops.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -249,4 +249,33 @@
 }
 
+static int udf_size_block(service_id_t service_id, uint32_t *size)
+{
+	udf_instance_t *instance;
+	int rc = fs_instance_get(service_id, (void **) &instance);
+	if (rc != EOK)
+		return rc;
+
+	if (NULL == instance)
+		return ENOENT;
+	
+	*size = instance->volumes[DEFAULT_VOL].logical_block_size;
+	
+	return EOK;
+}
+
+static int udf_total_block_count(service_id_t service_id, uint64_t *count)
+{
+	*count = 0;
+	
+	return EOK;
+}
+
+static int udf_free_block_count(service_id_t service_id, uint64_t *count)
+{
+	*count = 0;
+	
+	return EOK;
+}
+
 libfs_ops_t udf_libfs_ops = {
 	.root_get = udf_root_get,
@@ -265,5 +294,8 @@
 	.is_directory = udf_is_directory,
 	.is_file = udf_is_file,
-	.service_get = udf_service_get
+	.service_get = udf_service_get,
+	.size_block = udf_size_block,
+	.total_block_count = udf_total_block_count,
+	.free_block_count = udf_free_block_count
 };
 
Index: uspace/srv/hid/compositor/compositor.c
===================================================================
--- uspace/srv/hid/compositor/compositor.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/hid/compositor/compositor.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -143,4 +143,6 @@
 static LIST_INITIALIZE(viewport_list);
 
+static FIBRIL_MUTEX_INITIALIZE(discovery_mtx);
+
 /** Input server proxy */
 static input_t *input;
@@ -335,6 +337,5 @@
 	}
 
-	list_foreach(viewport_list, link) {
-		viewport_t *vp = list_get_instance(link, viewport_t, link);
+	list_foreach(viewport_list, link, viewport_t, vp) {
 		sysarg_t w_vp, h_vp;
 		surface_get_resolution(vp->surface, &w_vp, &h_vp);
@@ -354,6 +355,5 @@
 	fibril_mutex_lock(&pointer_list_mtx);
 
-	list_foreach(pointer_list, link) {
-		pointer_t *ptr = list_get_instance(link, pointer_t, link);
+	list_foreach(pointer_list, link, pointer_t, ptr) {
 		ptr->pos.x = ptr->pos.x > viewport_bound_rect.x ? ptr->pos.x : viewport_bound_rect.x;
 		ptr->pos.y = ptr->pos.y > viewport_bound_rect.y ? ptr->pos.y : viewport_bound_rect.y;
@@ -374,8 +374,6 @@
 	fibril_mutex_lock(&pointer_list_mtx);
 
-	list_foreach(viewport_list, link) {
-
+	list_foreach(viewport_list, link, viewport_t, vp) {
 		/* Determine what part of the viewport must be updated. */
-		viewport_t *vp = list_get_instance(link, viewport_t, link);
 		sysarg_t x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp;
 		surface_get_resolution(vp->surface, &w_dmg_vp, &h_dmg_vp);
@@ -446,7 +444,5 @@
 			}
 
-			list_foreach(pointer_list, link) {
-
-				pointer_t *ptr = list_get_instance(link, pointer_t, link);
+			list_foreach(pointer_list, link, pointer_t, ptr) {
 				if (ptr->ghost.surface) {
 
@@ -522,9 +518,8 @@
 			}
 
-			list_foreach(pointer_list, link) {
+			list_foreach(pointer_list, link, pointer_t, ptr) {
 
 				/* Determine what part of the pointer intersects with the
 				 * updated area of the current viewport. */
-				pointer_t *ptr = list_get_instance(link, pointer_t, link);
 				sysarg_t x_dmg_ptr, y_dmg_ptr, w_dmg_ptr, h_dmg_ptr;
 				surface_t *sf_ptr = ptr->cursor.states[ptr->state];
@@ -569,6 +564,5 @@
 
 	/* Notify visualizers about updated regions. */
-	list_foreach(viewport_list, link) {
-		viewport_t *vp = list_get_instance(link, viewport_t, link);
+	list_foreach(viewport_list, link, viewport_t, vp) {
 		sysarg_t x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp;
 		surface_get_damaged_region(vp->surface, &x_dmg_vp, &y_dmg_vp, &w_dmg_vp, &h_dmg_vp);
@@ -631,6 +625,5 @@
 
 	fibril_mutex_lock(&pointer_list_mtx);
-	list_foreach(pointer_list, link) {
-		pointer_t *pointer = list_get_instance(link, pointer_t, link);
+	list_foreach(pointer_list, link, pointer_t, pointer) {
 		if (pointer->id == pos_id) {
 			pointer->grab_flags = pointer->pressed ? grab_flags : GF_EMPTY;
@@ -710,15 +703,15 @@
 {
 	fibril_mutex_lock(&window_list_mtx);
-	window_t *window = NULL;
-	list_foreach(window_list, link) {
-		window = list_get_instance(link, window_t, link);
+
+	list_foreach(window_list, link, window_t, window) {
 		if (window == target) {
 			prodcons_produce(&window->queue, &event->link);
-		}
-	}
-	if (!window) {
-		free(event);
-	}
+			fibril_mutex_unlock(&window_list_mtx);
+			return;
+		}
+	}
+
 	fibril_mutex_unlock(&window_list_mtx);
+	free(event);
 }
 
@@ -863,6 +856,5 @@
 	window_t *win = NULL;
 	fibril_mutex_lock(&window_list_mtx);
-	list_foreach(window_list, link) {
-		window_t *cur = list_get_instance(link, window_t, link);
+	list_foreach(window_list, link, window_t, cur) {
 		if (cur->in_dsid == service_id || cur->out_dsid == service_id) {
 			win = cur;
@@ -1007,6 +999,5 @@
 		/* Close all clients and their windows. */
 		fibril_mutex_lock(&window_list_mtx);
-		list_foreach(window_list, link) {
-			window_t *win = list_get_instance(link, window_t, link);
+		list_foreach(window_list, link, window_t, win) {
 			window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
 			if (event) {
@@ -1034,6 +1025,5 @@
 	viewport_t *vp = NULL;
 	fibril_mutex_lock(&viewport_list_mtx);
-	list_foreach(viewport_list, link) {
-		viewport_t *cur = list_get_instance(link, viewport_t, link);
+	list_foreach(viewport_list, link, viewport_t, cur) {
 		if (cur->dsid == (service_id_t) IPC_GET_ARG1(*icall)) {
 			vp = cur;
@@ -1571,6 +1561,6 @@
 
 	/* Determine the window which the mouse click belongs to. */
-	list_foreach(window_list, link) {
-		win = list_get_instance(link, window_t, link);
+	list_foreach(window_list, link, window_t, cw) {
+		win = cw;
 		if (win->surface) {
 			surface_get_resolution(win->surface, &width, &height);
@@ -2126,4 +2116,58 @@
 }
 
+static int discover_viewports(void)
+{
+	/* Create viewports and connect them to visualizers. */
+	category_id_t cat_id;
+	int rc = loc_category_get_id("visualizer", &cat_id, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		printf("%s: Failed to get visualizer category.\n", NAME);
+		return -1;
+	}
+	
+	service_id_t *svcs;
+	size_t svcs_cnt = 0;
+	rc = loc_category_get_svcs(cat_id, &svcs, &svcs_cnt);
+	if (rc != EOK || svcs_cnt == 0) {
+		printf("%s: Failed to get visualizer category services.\n", NAME);
+		return -1;
+	}
+
+	fibril_mutex_lock(&viewport_list_mtx);	
+	for (size_t i = 0; i < svcs_cnt; ++i) {
+		bool exists = false;
+		list_foreach(viewport_list, link, viewport_t, vp) {
+			if (vp->dsid == svcs[i]) {
+				exists = true;
+				break;
+			}
+		}
+		
+		if (exists)
+			continue;
+		
+		char *svc_name;
+		rc = loc_service_get_name(svcs[i], &svc_name);
+		if (rc == EOK) {
+			viewport_t *vp = viewport_create(svc_name);
+			if (vp != NULL) {
+				list_append(&vp->link, &viewport_list);
+			}
+		}
+	}
+	fibril_mutex_unlock(&viewport_list_mtx);
+	
+	/* TODO damage only newly added viewports */
+	comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
+	return EOK;
+}
+
+static void category_change_cb(void)
+{
+	fibril_mutex_lock(&discovery_mtx);
+	discover_viewports();
+	fibril_mutex_unlock(&discovery_mtx);
+}
+
 static int compositor_srv_init(char *input_svc, char *name)
 {
@@ -2132,5 +2176,5 @@
 	
 	/* Color of the viewport background. Must be opaque. */
-	bg_color = PIXEL(255, 75, 70, 75);
+	bg_color = PIXEL(255, 69, 51, 103);
 	
 	/* Register compositor server. */
@@ -2172,41 +2216,31 @@
 	/* Establish input bidirectional connection. */
 	rc = input_connect(input_svc);
-	if (rc != EOK)
+	if (rc != EOK) {
+		printf("%s: Failed to connect to input service.\n", NAME);
 		return rc;
-
-	/* Create viewports and connect them to visualizers. */
-	category_id_t cat_id;
-	rc = loc_category_get_id("visualizer", &cat_id, IPC_FLAG_BLOCKING);
+	}
+
+	rc = loc_register_cat_change_cb(category_change_cb);
+	if (rc != EOK) {
+		printf("%s: Failed to register category change callback\n", NAME);
+		input_disconnect();
+		return rc;
+	}	
+
+	rc = discover_viewports();
 	if (rc != EOK) {
 		input_disconnect();
-		return -1;
-	}
-	
-	service_id_t *svcs;
-	size_t svcs_cnt = 0;
-	rc = loc_category_get_svcs(cat_id, &svcs, &svcs_cnt);
-	if (rc != EOK || svcs_cnt == 0) {
+		return rc;
+	}
+	
+	if (list_empty(&viewport_list)) {
+		printf("%s: Failed to get viewports.\n", NAME);
 		input_disconnect();
 		return -1;
 	}
-	
-	for (size_t i = 0; i < svcs_cnt; ++i) {
-		char *svc_name;
-		rc = loc_service_get_name(svcs[i], &svc_name);
-		if (rc == EOK) {
-			viewport_t *vp = viewport_create(svc_name);
-			if (vp != NULL) {
-				list_append(&vp->link, &viewport_list);
-			}
-		}
-	}
-	
-	if (list_empty(&viewport_list)) {
-		input_disconnect();
-		return -1;
-	}
 
 	comp_restrict_pointers();
 	comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
+	
 	
 	return EOK;
Index: uspace/srv/hid/input/input.c
===================================================================
--- uspace/srv/hid/input/input.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/hid/input/input.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -470,8 +470,5 @@
 {
 	/* For each keyboard device */
-	list_foreach(kbd_devs, kdev_link) {
-		kbd_dev_t *kdev = list_get_instance(kdev_link, kbd_dev_t,
-		    kbd_devs);
-		
+	list_foreach(kbd_devs, kbd_devs, kbd_dev_t, kdev) {
 		/* Yield port */
 		if (kdev->port_ops != NULL)
@@ -483,8 +480,5 @@
 {
 	/* For each keyboard device */
-	list_foreach(kbd_devs, kdev_link) {
-		kbd_dev_t *kdev = list_get_instance(kdev_link, kbd_dev_t,
-		    kbd_devs);
-		
+	list_foreach(kbd_devs, kbd_devs, kbd_dev_t, kdev) {
 		/* Reclaim port */
 		if (kdev->port_ops != NULL)
@@ -521,7 +515,5 @@
 		
 		/* Determine whether we already know this device. */
-		list_foreach(kbd_devs, kdev_link) {
-			kbd_dev_t *kdev = list_get_instance(kdev_link,
-			    kbd_dev_t, kbd_devs);
+		list_foreach(kbd_devs, kbd_devs, kbd_dev_t, kdev) {
 			if (kdev->svc_id == svcs[i]) {
 				already_known = true;
@@ -574,7 +566,5 @@
 		
 		/* Determine whether we already know this device. */
-		list_foreach(mouse_devs, mdev_link) {
-			mouse_dev_t *mdev = list_get_instance(mdev_link,
-			    mouse_dev_t, mouse_devs);
+		list_foreach(mouse_devs, mouse_devs, mouse_dev_t, mdev) {
 			if (mdev->svc_id == svcs[i]) {
 				already_known = true;
Index: uspace/srv/hid/input/port/niagara.c
===================================================================
--- uspace/srv/hid/input/port/niagara.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/hid/input/port/niagara.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -97,6 +97,6 @@
 		return -1;
 	
-	int rc = physmem_map((void *) paddr, 1,
-	    AS_AREA_READ | AS_AREA_WRITE, (void *) &input_buffer);
+	int rc = physmem_map(paddr, 1, AS_AREA_READ | AS_AREA_WRITE,
+	    (void *) &input_buffer);
 	if (rc != 0) {
 		printf("Niagara: uspace driver couldn't map physical memory: %d\n",
Index: uspace/srv/hid/output/output.c
===================================================================
--- uspace/srv/hid/output/output.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/hid/output/output.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -82,7 +82,5 @@
 	int ret = EOK;
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		assert(dev->ops.yield);
 		
@@ -99,7 +97,5 @@
 	int ret = EOK;
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		assert(dev->ops.claim);
 		
@@ -117,7 +113,5 @@
 	sysarg_t rows = MAX_ROWS;
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		cols = min(cols, dev->cols);
 		rows = min(rows, dev->rows);
@@ -131,7 +125,5 @@
 	console_caps_t caps = 0;
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		assert(dev->ops.get_caps);
 		
@@ -145,6 +137,5 @@
 {
 	frontbuf_t *frontbuf = NULL;
-	list_foreach(frontbufs, link) {
-		frontbuf_t *cur = list_get_instance(link, frontbuf_t, link);
+	list_foreach(frontbufs, link, frontbuf_t, cur) {
 		if (cur == (frontbuf_t *) handle) {
 			frontbuf = cur;
@@ -216,7 +207,5 @@
 	chargrid_get_cursor(buf, &col, &row);
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		assert(dev->ops.cursor_update);
 		
@@ -237,7 +226,5 @@
 static void srv_set_style(ipc_callid_t iid, ipc_call_t *icall)
 {
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		dev->attrs.type = CHAR_ATTR_STYLE;
 		dev->attrs.val.style =
@@ -250,7 +237,5 @@
 static void srv_set_color(ipc_callid_t iid, ipc_call_t *icall)
 {
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		dev->attrs.type = CHAR_ATTR_INDEX;
 		dev->attrs.val.index.bgcolor =
@@ -267,7 +252,5 @@
 static void srv_set_rgb_color(ipc_callid_t iid, ipc_call_t *icall)
 {
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		dev->attrs.type = CHAR_ATTR_RGB;
 		dev->attrs.val.rgb.bgcolor = IPC_GET_ARG1(*icall);
@@ -325,7 +308,5 @@
 	chargrid_t *buf = (chargrid_t *) frontbuf->data;
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		assert(dev->ops.char_update);
 		
@@ -374,7 +355,5 @@
 	chargrid_t *buf = (chargrid_t *) frontbuf->data;
 	
-	list_foreach(outdevs, link) {
-		outdev_t *dev = list_get_instance(link, outdev_t, link);
-		
+	list_foreach(outdevs, link, outdev_t, dev) {
 		assert(dev->ops.char_update);
 		
Index: uspace/srv/hid/output/port/ega.c
===================================================================
--- uspace/srv/hid/output/port/ega.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/hid/output/port/ega.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -216,5 +216,5 @@
 	ega.size = (ega.cols * ega.rows) << 1;
 	
-	rc = physmem_map((void *) paddr,
+	rc = physmem_map(paddr,
 	    ALIGN_UP(ega.size, PAGE_SIZE) >> PAGE_WIDTH,
 	    AS_AREA_READ | AS_AREA_WRITE, (void *) &ega.addr);
Index: uspace/srv/hid/output/port/kchar.c
===================================================================
--- uspace/srv/hid/output/port/kchar.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/hid/output/port/kchar.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -84,5 +84,5 @@
 		return rc;
 	
-	rc = physmem_map((void *) paddr,
+	rc = physmem_map(paddr,
 	    ALIGN_UP(1, PAGE_SIZE) >> PAGE_WIDTH,
 	    AS_AREA_READ | AS_AREA_WRITE, (void *) &kchar.addr);
Index: uspace/srv/hid/output/port/niagara.c
===================================================================
--- uspace/srv/hid/output/port/niagara.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/hid/output/port/niagara.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -104,6 +104,6 @@
 		return rc;
 	
-	rc = physmem_map((void *) paddr, 1,
-	    AS_AREA_READ | AS_AREA_WRITE, (void *) &niagara.fifo);
+	rc = physmem_map(paddr, 1, AS_AREA_READ | AS_AREA_WRITE,
+	    (void *) &niagara.fifo);
 	if (rc != EOK)
 		return rc;
Index: uspace/srv/hid/remcons/user.c
===================================================================
--- uspace/srv/hid/remcons/user.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/hid/remcons/user.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -128,6 +128,5 @@
 
 	fibril_mutex_lock(&users_guard);
-	list_foreach(users, link) {
-		telnet_user_t *tmp = list_get_instance(link, telnet_user_t, link);
+	list_foreach(users, link, telnet_user_t, tmp) {
 		if (tmp->service_id == id) {
 			user = tmp;
Index: uspace/srv/hid/rfb/Makefile
===================================================================
--- uspace/srv/hid/rfb/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/hid/rfb/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2013 Martin Sucha
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+LIBS = $(LIBGRAPH_PREFIX)/libgraph.a
+EXTRA_CFLAGS = -I$(LIBGRAPH_PREFIX)
+BINARY = rfb
+
+SOURCES = \
+	main.c \
+	rfb.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/hid/rfb/main.c
===================================================================
--- uspace/srv/hid/rfb/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/hid/rfb/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <loc.h>
+#include <stdio.h>
+#include <fibril_synch.h>
+#include <abi/ipc/methods.h>
+#include <inttypes.h>
+#include <io/log.h>
+
+#include <abi/fb/visuals.h>
+#include <adt/list.h>
+#include <io/mode.h>
+#include <io/pixelmap.h>
+#include <io/chargrid.h>
+#include <graph.h>
+
+#include "rfb.h"
+
+#define NAME "rfb"
+
+static vslmode_list_element_t pixel_mode;
+static visualizer_t *vis;
+static rfb_t rfb;
+
+static int rfb_claim(visualizer_t *vs)
+{
+	return EOK;
+}
+
+static int rfb_yield(visualizer_t *vs)
+{
+	return EOK;
+}
+
+static int rfb_suspend(visualizer_t *vs)
+{
+	return EOK;
+}
+
+static int rfb_wakeup(visualizer_t *vs)
+{
+	return EOK;
+}
+
+static int rfb_handle_damage_pixels(visualizer_t *vs,
+    sysarg_t x0, sysarg_t y0, sysarg_t width, sysarg_t height,
+    sysarg_t x_offset, sysarg_t y_offset)
+{
+	fibril_mutex_lock(&rfb.lock);
+	
+	if (x0 + width > rfb.width || y0 + height > rfb.height) {
+		fibril_mutex_unlock(&rfb.lock);
+		return EINVAL;
+	}
+	
+	/* TODO update surface_t and use it */
+	if (!rfb.damage_valid) {
+		rfb.damage_rect.x = x0;
+		rfb.damage_rect.y = y0;
+		rfb.damage_rect.width = width;
+		rfb.damage_rect.height = height;
+		rfb.damage_valid = true;
+	}
+	else {
+		if (x0 < rfb.damage_rect.x) {
+			rfb.damage_rect.width += rfb.damage_rect.x - x0;
+			rfb.damage_rect.x = x0;
+		}
+		if (y0 < rfb.damage_rect.y) {
+			rfb.damage_rect.height += rfb.damage_rect.y - y0;
+			rfb.damage_rect.y = y0;
+		}
+		sysarg_t x1 = x0 + width;
+		sysarg_t dx1 = rfb.damage_rect.x + rfb.damage_rect.width;
+		if (x1 > dx1) {
+			rfb.damage_rect.width += x1 - dx1;
+		}
+		sysarg_t y1 = y0 + height;
+		sysarg_t dy1 = rfb.damage_rect.y + rfb.damage_rect.height;
+		if (y1 > dy1) {
+			rfb.damage_rect.height += y1 - dy1;
+		}
+	}
+	
+	pixelmap_t *map = &vs->cells;
+	
+	for (sysarg_t y = y0; y < height + y0; ++y) {
+		for (sysarg_t x = x0; x < width + x0; ++x) {
+			pixel_t pix = pixelmap_get_pixel(map, (x + x_offset) % map->width,
+			    (y + y_offset) % map->height);
+			pixelmap_put_pixel(&rfb.framebuffer, x, y, pix);
+		}
+	}
+	
+	fibril_mutex_unlock(&rfb.lock);
+	return EOK;
+}
+
+static int rfb_change_mode(visualizer_t *vs, vslmode_t new_mode)
+{
+	return EOK;
+}
+
+static visualizer_ops_t rfb_ops = {
+	.claim = rfb_claim,
+	.yield = rfb_yield,
+	.change_mode = rfb_change_mode,
+	.handle_damage = rfb_handle_damage_pixels,
+	.suspend = rfb_suspend,
+	.wakeup = rfb_wakeup
+};
+
+static void syntax_print(void)
+{
+	fprintf(stderr, "Usage: %s <name> <width> <height> [port]\n", NAME);
+}
+
+static void client_connection(ipc_callid_t callid, ipc_call_t *call, void *data)
+{
+	graph_visualizer_connection(vis, callid, call, data);
+}
+
+static int socket_fibril(void *unused)
+{
+	rfb_accept(&rfb);
+	
+	/* Not reached */
+	return EOK;
+}
+
+int main(int argc, char **argv)
+{
+	log_init(NAME);
+
+	if (argc <= 3) {
+		syntax_print();
+		return 1;
+	}
+
+	const char *rfb_name = argv[1];
+	
+	char *endptr;
+	unsigned long width = strtoul(argv[2], &endptr, 0);
+	if (*endptr != 0) {
+		fprintf(stderr, "Invalid width\n");
+		syntax_print();
+		return 1;
+	}
+	
+	unsigned long height = strtoul(argv[3], &endptr, 0);
+	if (*endptr != 0) {
+		fprintf(stderr, "Invalid height\n");
+		syntax_print();
+		return 1;
+	}
+	
+	unsigned long port = 5900;
+	if (argc > 4) {
+		port = strtoul(argv[4], &endptr, 0);
+		if (*endptr != 0) {
+			fprintf(stderr, "Invalid port number\n");
+			syntax_print();
+			return 1;
+		}
+	}
+	
+	rfb_init(&rfb, width, height, rfb_name);
+	
+	vis = malloc(sizeof(visualizer_t));
+	if (vis == NULL) {
+		fprintf(stderr, "Failed allocating visualizer struct\n");
+		return 3;
+	}
+	
+	graph_init_visualizer(vis);
+	
+	pixel_mode.mode.index = 0;
+	pixel_mode.mode.version = 0;
+	pixel_mode.mode.refresh_rate = 0;
+	pixel_mode.mode.screen_aspect.width = rfb.width;
+	pixel_mode.mode.screen_aspect.height = rfb.height;
+	pixel_mode.mode.screen_width = rfb.width;
+	pixel_mode.mode.screen_height = rfb.height;
+	pixel_mode.mode.cell_aspect.width = 1;
+	pixel_mode.mode.cell_aspect.height = 1;
+	pixel_mode.mode.cell_visual.pixel_visual = VISUAL_RGB_8_8_8;
+	
+	link_initialize(&pixel_mode.link);
+	list_append(&pixel_mode.link, &vis->modes);
+	
+	vis->def_mode_idx = 0;
+	
+	vis->ops = rfb_ops;
+	vis->dev_ctx = NULL;
+
+	async_set_client_connection(client_connection);
+
+	int rc = loc_server_register(NAME);
+	if (rc != EOK) {
+		printf("%s: Unable to register server.\n", NAME);
+		return rc;
+	}
+
+	char *service_name;
+	rc = asprintf(&service_name, "rfb/%s", rfb_name);
+	if (rc < 0) {
+		printf(NAME ": Unable to create service name\n");
+		return rc;
+	}
+
+	service_id_t service_id;
+	
+	rc = loc_service_register(service_name, &service_id);
+	if (rc != EOK) {
+		printf(NAME ": Unable to register service %s.\n", service_name);
+		return rc;
+	}
+	
+	free(service_name);
+
+	category_id_t visualizer_category;
+	rc = loc_category_get_id("visualizer", &visualizer_category, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		fprintf(stderr, NAME ": Unable to get visualizer category id.\n");
+		return 1;
+	}
+	
+	rc = loc_service_add_to_cat(service_id, visualizer_category);
+	if (rc != EOK) {
+		fprintf(stderr, NAME ": Unable to add service to visualizer category.\n");
+		return 1;
+	}
+	
+	rc = rfb_listen(&rfb, port);
+	if (rc != EOK) {
+		fprintf(stderr, NAME ": Unable to listen at rfb port\n");
+		return 2;
+	}
+	
+	fid_t fib = fibril_create(socket_fibril, NULL);
+	if (!fib) {
+		fprintf(stderr, NAME ": Unable to create socket fibril.\n");
+		return 2;
+	}
+	
+	fibril_add_ready(fib);
+	
+	printf("%s: Accepting connections\n", NAME);
+	task_retval(0);
+	async_manager();
+
+	/* Not reached */
+	return 0;
+}
Index: uspace/srv/hid/rfb/rfb.c
===================================================================
--- uspace/srv/hid/rfb/rfb.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/hid/rfb/rfb.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,800 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fibril_synch.h>
+#include <inttypes.h>
+#include <str.h>
+#include <str_error.h>
+#include <byteorder.h>
+#include <macros.h>
+#include <io/log.h>
+
+#include <net/in.h>
+#include <net/inet.h>
+#include <net/socket.h>
+
+#include "rfb.h"
+
+/** Buffer for receiving the request. */
+#define BUFFER_SIZE  1024
+
+static char rbuf[BUFFER_SIZE];
+static size_t rbuf_out;
+static size_t rbuf_in;
+
+
+/** Receive one character (with buffering) */
+static int recv_char(int fd, char *c)
+{
+	if (rbuf_out == rbuf_in) {
+		rbuf_out = 0;
+		rbuf_in = 0;
+		
+		ssize_t rc = recv(fd, rbuf, BUFFER_SIZE, 0);
+		if (rc <= 0)
+			return rc;
+		
+		rbuf_in = rc;
+	}
+	
+	*c = rbuf[rbuf_out++];
+	return EOK;
+}
+
+/** Receive count characters (with buffering) */
+static int recv_chars(int fd, char *c, size_t count)
+{
+	for (size_t i = 0; i < count; i++) {
+		int rc = recv_char(fd, c);
+		if (rc != EOK)
+			return rc;
+		c++;
+	}
+	return EOK;
+}
+
+static int recv_skip_chars(int fd, size_t count)
+{
+	for (size_t i = 0; i < count; i++) {
+		char c;
+		int rc = recv_char(fd, &c);
+		if (rc != EOK)
+			return rc;
+	}
+	return EOK;
+}
+
+static void rfb_pixel_format_to_be(rfb_pixel_format_t *src, rfb_pixel_format_t *dst)
+{
+	dst->r_max = host2uint16_t_be(src->r_max);
+	dst->g_max = host2uint16_t_be(src->g_max);
+	dst->b_max = host2uint16_t_be(src->b_max);
+}
+
+static void rfb_pixel_format_to_host(rfb_pixel_format_t *src, rfb_pixel_format_t *dst)
+{
+	dst->r_max = uint16_t_be2host(src->r_max);
+	dst->g_max = uint16_t_be2host(src->g_max);
+	dst->b_max = uint16_t_be2host(src->b_max);
+}
+
+static void rfb_server_init_to_be(rfb_server_init_t *src, rfb_server_init_t *dst)
+{
+	dst->width = host2uint16_t_be(src->width);
+	dst->height = host2uint16_t_be(src->height);
+	rfb_pixel_format_to_be(&src->pixel_format, &dst->pixel_format);
+	dst->name_length = host2uint32_t_be(src->name_length);
+}
+
+static void rfb_set_encodings_to_host(rfb_set_encodings_t *src, rfb_set_encodings_t *dst)
+{
+	dst->count = uint16_t_be2host(src->count);
+}
+
+static void rfb_framebuffer_update_request_to_host(rfb_framebuffer_update_request_t *src,
+    rfb_framebuffer_update_request_t *dst)
+{
+	dst->x = uint16_t_be2host(src->x);
+	dst->y = uint16_t_be2host(src->x);
+	dst->width = uint16_t_be2host(src->width);
+	dst->height = uint16_t_be2host(src->height);
+}
+
+static void rfb_framebuffer_update_to_be(rfb_framebuffer_update_t *src,
+    rfb_framebuffer_update_t *dst)
+{
+	dst->rect_count = host2uint16_t_be(src->rect_count);
+}
+
+static void rfb_rectangle_to_be(rfb_rectangle_t *src, rfb_rectangle_t *dst)
+{
+	dst->x = host2uint16_t_be(src->x);
+	dst->y = host2uint16_t_be(src->y);
+	dst->width = host2uint16_t_be(src->width);
+	dst->height = host2uint16_t_be(src->height);
+	dst->enctype = host2uint32_t_be(src->enctype);
+}
+
+static void rfb_key_event_to_host(rfb_key_event_t *src, rfb_key_event_t *dst)
+{
+	dst->key = uint32_t_be2host(src->key);
+}
+
+static void rfb_pointer_event_to_host(rfb_pointer_event_t *src, rfb_pointer_event_t *dst)
+{
+	dst->x = uint16_t_be2host(src->x);
+	dst->y = uint16_t_be2host(src->y);
+}
+
+static void rfb_client_cut_text_to_host(rfb_client_cut_text_t *src,
+    rfb_client_cut_text_t *dst)
+{
+	dst->length = uint32_t_be2host(src->length);
+}
+
+int rfb_init(rfb_t *rfb, uint16_t width, uint16_t height, const char *name)
+{
+	memset(rfb, 0, sizeof(rfb_t));
+	fibril_mutex_initialize(&rfb->lock);
+	
+	rfb_pixel_format_t *pf = &rfb->pixel_format;
+	pf->bpp = 32;
+	pf->depth = 24;
+	pf->big_endian = 1;
+	pf->true_color = 1;
+	pf->r_max = 255;
+	pf->g_max = 255;
+	pf->b_max = 255;
+	pf->r_shift = 0;
+	pf->g_shift = 8;
+	pf->b_shift = 16;
+	
+	rfb->name = str_dup(name);
+	rfb->supports_trle = false;
+	
+	return rfb_set_size(rfb, width, height);
+}
+
+int rfb_set_size(rfb_t *rfb, uint16_t width, uint16_t height)
+{
+	size_t new_size = width * height * sizeof(pixel_t);
+	void *pixbuf = malloc(new_size);
+	if (pixbuf == NULL)
+		return ENOMEM;
+
+	free(rfb->framebuffer.data);
+	rfb->framebuffer.data = pixbuf;
+	rfb->framebuffer.width = width;
+	rfb->framebuffer.height = height;
+	rfb->width = width;
+	rfb->height = height;
+	
+	/* Fill with white */
+	memset(rfb->framebuffer.data, 255, new_size);
+	
+	return EOK;
+}
+
+static int recv_message(int conn_sd, char type, void *buf, size_t size)
+{
+	memcpy(buf, &type, 1);
+	return recv_chars(conn_sd, ((char *) buf) + 1, size -1);
+}
+
+static uint32_t rfb_scale_channel(uint8_t val, uint32_t max)
+{
+	return val * max / 255;
+}
+
+static void rfb_encode_index(rfb_t *rfb, uint8_t *buf, pixel_t pixel)
+{
+	int first_free_index = -1;
+	for (size_t i = 0; i < 256; i++) {
+		bool free = ALPHA(rfb->palette[i]) == 0;
+		if (free && first_free_index == -1) {
+			first_free_index = i;
+		}
+		else if (!free && RED(rfb->palette[i]) == RED(pixel) &&
+		    GREEN(rfb->palette[i]) == GREEN(pixel) &&
+		    BLUE(rfb->palette[i]) == BLUE(pixel)) {
+			*buf = i;
+			return;
+		}
+	}
+	
+	if (first_free_index != -1) {
+		rfb->palette[first_free_index] = PIXEL(255, RED(pixel), GREEN(pixel),
+		    BLUE(pixel));
+		rfb->palette_used = max(rfb->palette_used, (unsigned) first_free_index + 1);
+		*buf = first_free_index;
+		return;
+	}
+	
+	/* TODO find nearest color index. We are lazy so return index 0 for now */
+	*buf = 0;
+}
+
+static void rfb_encode_true_color(rfb_pixel_format_t *pf, void *buf,
+    pixel_t pixel)
+{
+	uint32_t pix = 0;
+	pix |= rfb_scale_channel(RED(pixel), pf->r_max) << pf->r_shift;
+	pix |= rfb_scale_channel(GREEN(pixel), pf->g_max) << pf->g_shift;
+	pix |= rfb_scale_channel(BLUE(pixel), pf->b_max) << pf->b_shift;
+	
+	if (pf->bpp == 8) {
+		uint8_t pix8 = pix;
+		memcpy(buf, &pix8, 1);
+	}
+	else if (pf->bpp == 16) {
+		uint16_t pix16 = pix;
+		if (pf->big_endian) {
+			pix16 = host2uint16_t_be(pix16);
+		}
+		else {
+			pix16 = host2uint16_t_le(pix16);
+		}
+		memcpy(buf, &pix16, 2);
+	}
+	else if (pf->bpp == 32) {
+		if (pf->big_endian) {
+			pix = host2uint32_t_be(pix);
+		}
+		else {
+			pix = host2uint32_t_le(pix);
+		}
+		memcpy(buf, &pix, 4);
+	}
+}
+
+static void rfb_encode_pixel(rfb_t *rfb, void *buf, pixel_t pixel)
+{
+	if (rfb->pixel_format.true_color) {
+		rfb_encode_true_color(&rfb->pixel_format, buf, pixel);
+	}
+	else {
+		rfb_encode_index(rfb, buf, pixel);
+	}
+}
+
+static void rfb_set_color_map_entries_to_be(rfb_set_color_map_entries_t *src,
+    rfb_set_color_map_entries_t *dst)
+{
+	dst->first_color = host2uint16_t_be(src->first_color);
+	dst->color_count = host2uint16_t_be(src->color_count);
+}
+
+static void rfb_color_map_entry_to_be(rfb_color_map_entry_t *src,
+    rfb_color_map_entry_t *dst)
+{
+	dst->red = host2uint16_t_be(src->red);
+	dst->green = host2uint16_t_be(src->green);
+	dst->blue = host2uint16_t_be(src->blue);
+}
+
+static void *rfb_send_palette_message(rfb_t *rfb, size_t *psize)
+{
+	size_t size = sizeof(rfb_set_color_map_entries_t) +
+	    rfb->palette_used * sizeof(rfb_color_map_entry_t);
+	
+	void *buf = malloc(size);
+	if (buf == NULL)
+		return NULL;
+	
+	void *pos = buf;
+	
+	rfb_set_color_map_entries_t *scme = pos;
+	scme->message_type = RFB_SMSG_SET_COLOR_MAP_ENTRIES;
+	scme->first_color = 0;
+	scme->color_count = rfb->palette_used;
+	rfb_set_color_map_entries_to_be(scme, scme);
+	pos += sizeof(rfb_set_color_map_entries_t);
+	
+	rfb_color_map_entry_t *entries = pos;
+	for (unsigned i = 0; i < rfb->palette_used; i++) {
+		entries[i].red = 65535 * RED(rfb->palette[i]) / 255;
+		entries[i].green = 65535 * GREEN(rfb->palette[i]) / 255;
+		entries[i].blue = 65535 * BLUE(rfb->palette[i]) / 255;
+		rfb_color_map_entry_to_be(&entries[i], &entries[i]);
+	}
+	
+	*psize = size;
+	return buf;
+}
+
+static size_t rfb_rect_encode_raw(rfb_t *rfb, rfb_rectangle_t *rect, void *buf)
+{
+	size_t pixel_size = rfb->pixel_format.bpp / 8;
+	size_t size = (rect->width * rect->height * pixel_size);
+	
+	if (buf == NULL)
+		return size;
+	
+	for (uint16_t y = 0; y < rect->height; y++) {
+		for (uint16_t x = 0; x < rect->width; x++) {
+			pixel_t pixel = pixelmap_get_pixel(&rfb->framebuffer,
+			    x + rect->x, y + rect->y);
+			rfb_encode_pixel(rfb, buf, pixel);
+			buf += pixel_size;
+		}
+	}
+	
+	return size;
+}
+
+typedef enum {
+	COMP_NONE, COMP_SKIP_START, COMP_SKIP_END
+} cpixel_compress_type_t;
+
+typedef struct {
+	size_t size;
+	cpixel_compress_type_t compress_type;
+} cpixel_ctx_t;
+
+static void cpixel_context_init(cpixel_ctx_t *ctx, rfb_pixel_format_t *pixel_format)
+{
+	ctx->size = pixel_format->bpp / 8;
+	ctx->compress_type = COMP_NONE;
+	
+	if (pixel_format->bpp == 32 && pixel_format->depth <= 24) {
+		uint32_t mask = 0;
+		mask |= pixel_format->r_max << pixel_format->r_shift;
+		mask |= pixel_format->g_max << pixel_format->g_shift;
+		mask |= pixel_format->b_max << pixel_format->b_shift;
+		
+		if (pixel_format->big_endian) {
+			mask = host2uint32_t_be(mask);
+		}
+		else {
+			mask = host2uint32_t_le(mask);
+		}
+		
+		uint8_t *mask_data = (uint8_t *) &mask;
+		if (mask_data[0] == 0) {
+			ctx->compress_type = COMP_SKIP_START;
+			ctx->size = 3;
+		}
+		else if (mask_data[3] == 0) {
+			ctx->compress_type = COMP_SKIP_END;
+			ctx->size = 3;
+		}
+	}
+}
+
+static void cpixel_encode(rfb_t *rfb, cpixel_ctx_t *cpixel, void *buf,
+    pixel_t pixel)
+{
+	uint8_t data[4];
+	rfb_encode_pixel(rfb, data, pixel);
+	
+	switch (cpixel->compress_type) {
+	case COMP_NONE:
+	case COMP_SKIP_END:
+		memcpy(buf, data, cpixel->size);
+		break;
+	case COMP_SKIP_START:
+		memcpy(buf, data + 1, cpixel->size);
+	}
+}
+
+static ssize_t rfb_tile_encode_raw(rfb_t *rfb, cpixel_ctx_t *cpixel,
+    rfb_rectangle_t *tile, void *buf)
+{
+	ssize_t size = tile->width * tile->height * cpixel->size;
+	if (buf == NULL)
+		return size;
+	
+	for (uint16_t y = tile->y; y < tile->y + tile->height; y++) {
+		for (uint16_t x = tile->x; x < tile->x + tile->width; x++) {
+			pixel_t pixel = pixelmap_get_pixel(&rfb->framebuffer, x, y);
+			cpixel_encode(rfb, cpixel, buf, pixel);
+		}
+	}
+	
+	return size;
+}
+
+static ssize_t rfb_tile_encode_solid(rfb_t *rfb, cpixel_ctx_t *cpixel,
+    rfb_rectangle_t *tile, void *buf)
+{
+	/* Check if it is single color */
+	pixel_t the_color = pixelmap_get_pixel(&rfb->framebuffer, tile->x, tile->y);
+	for (uint16_t y = tile->y; y < tile->y + tile->height; y++) {
+		for (uint16_t x = tile->x; x < tile->x + tile->width; x++) {
+			if (pixelmap_get_pixel(&rfb->framebuffer, x, y) != the_color)
+				return -1;
+		}
+	}
+	
+	/* OK, encode it */
+	if (buf)
+		cpixel_encode(rfb, cpixel, buf, the_color);
+	return cpixel->size;
+}
+
+static size_t rfb_rect_encode_trle(rfb_t *rfb, rfb_rectangle_t *rect, void *buf)
+{
+	cpixel_ctx_t cpixel;
+	cpixel_context_init(&cpixel, &rfb->pixel_format);
+	
+	size_t size = 0;
+	for (uint16_t y = 0; y < rect->height; y += 16) {
+		for (uint16_t x = 0; x < rect->width; x += 16) {
+			rfb_rectangle_t tile = {
+				.x = x,
+				.y = y,
+				.width = (x + 16 <= rect->width ? 16 : rect->width - x),
+				.height = (y + 16 <= rect->height ? 16 : rect->height - y)
+			};
+			
+			size += 1;
+			uint8_t *tile_enctype_ptr = buf;
+			if (buf)
+				buf +=  1;
+			
+			uint8_t tile_enctype = RFB_TILE_ENCODING_SOLID;
+			ssize_t tile_size = rfb_tile_encode_solid(rfb, &cpixel, &tile, buf);
+			if (tile_size < 0) {
+				tile_size = rfb_tile_encode_raw(rfb, &cpixel, &tile, buf);
+				tile_enctype = RFB_TILE_ENCODING_RAW;
+			}
+			
+			if (buf) {
+				*tile_enctype_ptr = tile_enctype;
+				buf += tile_size;
+			}
+		}
+	}
+	return size;
+}
+
+static int rfb_send_framebuffer_update(rfb_t *rfb, int conn_sd, bool incremental)
+{
+	fibril_mutex_lock(&rfb->lock);
+	if (!incremental || !rfb->damage_valid) {
+		rfb->damage_rect.x = 0;
+		rfb->damage_rect.y = 0;
+		rfb->damage_rect.width = rfb->width;
+		rfb->damage_rect.height = rfb->height;
+	}
+	
+
+	/* We send only single raw rectangle right now */
+	size_t buf_size = sizeof(rfb_framebuffer_update_t) +
+	    sizeof(rfb_rectangle_t) * 1 +
+	    rfb_rect_encode_raw(rfb, &rfb->damage_rect, NULL)
+	    ;
+	
+	void *buf = malloc(buf_size);
+	if (buf == NULL) {
+		fibril_mutex_unlock(&rfb->lock);
+		return ENOMEM;
+	}
+	memset(buf, 0, buf_size);
+	
+	void *pos = buf;
+	rfb_framebuffer_update_t *fbu = buf;
+	fbu->message_type = RFB_SMSG_FRAMEBUFFER_UPDATE;
+	fbu->rect_count = 1;
+	rfb_framebuffer_update_to_be(fbu, fbu);
+	pos += sizeof(rfb_framebuffer_update_t);
+	
+	rfb_rectangle_t *rect = pos;
+	pos += sizeof(rfb_rectangle_t);
+	
+	*rect = rfb->damage_rect;
+	
+	if (rfb->supports_trle) {
+		rect->enctype = RFB_ENCODING_TRLE;
+		pos += rfb_rect_encode_trle(rfb, rect, pos);
+	}
+	else {
+		rect->enctype = RFB_ENCODING_RAW;
+		pos += rfb_rect_encode_raw(rfb, rect, pos);
+	}
+	rfb_rectangle_to_be(rect, rect);
+	
+	rfb->damage_valid = false;
+	
+	size_t send_palette_size = 0;
+	void *send_palette = NULL;
+	
+	if (!rfb->pixel_format.true_color) {
+		send_palette = rfb_send_palette_message(rfb, &send_palette_size);
+		if (send_palette == NULL) {
+			free(buf);
+			fibril_mutex_unlock(&rfb->lock);
+			return ENOMEM;
+		}
+	}
+	
+	fibril_mutex_unlock(&rfb->lock);
+	
+	if (!rfb->pixel_format.true_color) {
+		int rc = send(conn_sd, send_palette, send_palette_size, 0);
+		if (rc != EOK) {
+			free(buf);
+			return rc;
+		}
+	}
+	
+	int rc = send(conn_sd, buf, buf_size, 0);
+	free(buf);
+	
+	return rc;
+}
+
+static int rfb_set_pixel_format(rfb_t *rfb, rfb_pixel_format_t *pixel_format)
+{
+	rfb->pixel_format = *pixel_format;
+	if (rfb->pixel_format.true_color) {
+		free(rfb->palette);
+		rfb->palette = NULL;
+		rfb->palette_used = 0;
+		log_msg(LOG_DEFAULT, LVL_DEBUG,
+		    "changed pixel format to %d-bit true color (%x<<%d, %x<<%d, %x<<%d)",
+		    pixel_format->depth, pixel_format->r_max, pixel_format->r_shift,
+		    pixel_format->g_max, pixel_format->g_shift, pixel_format->b_max,
+		    pixel_format->b_shift);
+	}
+	else {
+		if (rfb->palette == NULL) {
+			rfb->palette = malloc(sizeof(pixel_t) * 256);
+			if (rfb->palette == NULL)
+				return ENOMEM;
+			memset(rfb->palette, 0, sizeof(pixel_t) * 256);
+			rfb->palette_used = 0;
+		}
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "changed pixel format to %d-bit palette",
+		    pixel_format->depth);
+	}
+	return EOK;
+}
+
+static void rfb_socket_connection(rfb_t *rfb, int conn_sd)
+{
+	/* Version handshake */
+	int rc = send(conn_sd, "RFB 003.008\n", 12, 0);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed sending server version %d", rc);
+		return;
+	}
+	
+	char client_version[12];
+	rc = recv_chars(conn_sd, client_version, 12);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed receiving client version: %d", rc);
+		return;
+	}
+	
+	if (memcmp(client_version, "RFB 003.008\n", 12) != 0) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Client version is not RFB 3.8");
+		return;
+	}
+	
+	/* Security handshake
+	 * 1 security type supported, which is 1 - None
+	 */
+	char sec_types[2];
+	sec_types[0] = 1; /* length */
+	sec_types[1] = RFB_SECURITY_NONE;
+	rc = send(conn_sd, sec_types, 2, 0);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN,
+		    "Failed sending security handshake: %d", rc);
+		return;
+	}
+	
+	char selected_sec_type = 0;
+	rc = recv_char(conn_sd, &selected_sec_type);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed receiving security type: %d", rc);
+		return;
+	}
+	if (selected_sec_type != RFB_SECURITY_NONE) {
+		log_msg(LOG_DEFAULT, LVL_WARN,
+		    "Client selected security type other than none");
+		return;
+	}
+	uint32_t security_result = RFB_SECURITY_HANDSHAKE_OK;
+	rc = send(conn_sd, &security_result, sizeof(uint32_t), 0);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed sending security result: %d", rc);
+		return;
+	}
+	
+	/* Client init */
+	char shared_flag;
+	rc = recv_char(conn_sd, &shared_flag);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed receiving client init: %d", rc);
+		return;
+	}
+	
+	/* Server init */
+	fibril_mutex_lock(&rfb->lock);
+	size_t name_length = str_length(rfb->name);
+	size_t msg_length = sizeof(rfb_server_init_t) + name_length;
+	rfb_server_init_t *server_init = malloc(msg_length);
+	if (server_init == NULL) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Cannot allocate memory for server init");
+		fibril_mutex_unlock(&rfb->lock);
+		return;
+	}
+	server_init->width = rfb->width;
+	server_init->height = rfb->height;
+	server_init->pixel_format = rfb->pixel_format,
+	server_init->name_length = name_length;
+	rfb_server_init_to_be(server_init, server_init);
+	memcpy(server_init->name, rfb->name, name_length);
+	fibril_mutex_unlock(&rfb->lock);
+	rc = send(conn_sd, server_init, msg_length, 0);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed sending server init: %d", rc);
+		return;
+	}
+	
+	while (true) {
+		char message_type = 0;
+		rc = recv_char(conn_sd, &message_type);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_WARN,
+			    "Failed receiving client message type");
+			return;
+		}
+		
+		rfb_set_pixel_format_t spf;
+		rfb_set_encodings_t se;
+		rfb_framebuffer_update_request_t fbur;
+		rfb_key_event_t ke;
+		rfb_pointer_event_t pe;
+		rfb_client_cut_text_t cct;
+		switch (message_type) {
+		case RFB_CMSG_SET_PIXEL_FORMAT:
+			recv_message(conn_sd, message_type, &spf, sizeof(spf));
+			rfb_pixel_format_to_host(&spf.pixel_format, &spf.pixel_format);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2, "Received SetPixelFormat message");
+			fibril_mutex_lock(&rfb->lock);
+			rc = rfb_set_pixel_format(rfb, &spf.pixel_format);
+			fibril_mutex_unlock(&rfb->lock);
+			if (rc != EOK)
+				return;
+			break;
+		case RFB_CMSG_SET_ENCODINGS:
+			recv_message(conn_sd, message_type, &se, sizeof(se));
+			rfb_set_encodings_to_host(&se, &se);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2, "Received SetEncodings message");
+			for (uint16_t i = 0; i < se.count; i++) {
+				int32_t encoding = 0;
+				rc = recv_chars(conn_sd, (char *) &encoding, sizeof(int32_t));
+				if (rc != EOK)
+					return;
+				encoding = uint32_t_be2host(encoding);
+				if (encoding == RFB_ENCODING_TRLE) {
+					log_msg(LOG_DEFAULT, LVL_DEBUG,
+					    "Client supports TRLE encoding");
+					rfb->supports_trle = true;
+				}
+			}
+			break;
+		case RFB_CMSG_FRAMEBUFFER_UPDATE_REQUEST:
+			recv_message(conn_sd, message_type, &fbur, sizeof(fbur));
+			rfb_framebuffer_update_request_to_host(&fbur, &fbur);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2,
+			    "Received FramebufferUpdateRequest message");
+			rfb_send_framebuffer_update(rfb, conn_sd, fbur.incremental);
+			break;
+		case RFB_CMSG_KEY_EVENT:
+			recv_message(conn_sd, message_type, &ke, sizeof(ke));
+			rfb_key_event_to_host(&ke, &ke);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2, "Received KeyEvent message");
+			break;
+		case RFB_CMSG_POINTER_EVENT:
+			recv_message(conn_sd, message_type, &pe, sizeof(pe));
+			rfb_pointer_event_to_host(&pe, &pe);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2, "Received PointerEvent message");
+			break;
+		case RFB_CMSG_CLIENT_CUT_TEXT:
+			recv_message(conn_sd, message_type, &cct, sizeof(cct));
+			rfb_client_cut_text_to_host(&cct, &cct);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2, "Received ClientCutText message");
+			recv_skip_chars(conn_sd, cct.length);
+			break;
+		default:
+			log_msg(LOG_DEFAULT, LVL_WARN,
+			    "Invalid client message type encountered");
+			return;
+		}
+	}
+}
+
+int rfb_listen(rfb_t *rfb, uint16_t port) {
+	struct sockaddr_in addr;
+	
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	
+	int rc = inet_pton(AF_INET, "127.0.0.1", (void *)
+	    &addr.sin_addr.s_addr);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error parsing network address (%s)",
+		    str_error(rc));
+		return rc;
+	}
+
+	int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
+	if (listen_sd < 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating listening socket (%s)",
+		    str_error(listen_sd));
+		return rc;
+	}
+	
+	rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error binding socket (%s)",
+		    str_error(rc));
+		return rc;
+	}
+	
+	rc = listen(listen_sd, 2);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "listen() failed (%s)", str_error(rc));
+		return rc;
+	}
+	
+	rfb->listen_sd = listen_sd;
+	
+	return EOK;
+}
+
+void rfb_accept(rfb_t *rfb)
+{
+	while (true) {
+		struct sockaddr_in raddr;
+		socklen_t raddr_len = sizeof(raddr);
+		int conn_sd = accept(rfb->listen_sd, (struct sockaddr *) &raddr,
+		    &raddr_len);
+		
+		if (conn_sd < 0) {
+			log_msg(LOG_DEFAULT, LVL_WARN, "accept() failed (%s)",
+			    str_error(conn_sd));
+			continue;
+		}
+		
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Connection accepted");
+		
+		rbuf_out = 0;
+		rbuf_in = 0;
+		
+		rfb_socket_connection(rfb, conn_sd);
+		closesocket(conn_sd);
+	}
+}
Index: uspace/srv/hid/rfb/rfb.h
===================================================================
--- uspace/srv/hid/rfb/rfb.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/hid/rfb/rfb.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RFB_H__
+#define RFB_H__
+
+#include <io/pixelmap.h>
+#include <fibril_synch.h>
+
+#define RFB_SECURITY_NONE 1
+#define RFB_SECURITY_HANDSHAKE_OK 0
+
+#define RFB_CMSG_SET_PIXEL_FORMAT 0
+#define RFB_CMSG_SET_ENCODINGS 2
+#define RFB_CMSG_FRAMEBUFFER_UPDATE_REQUEST 3
+#define RFB_CMSG_KEY_EVENT 4
+#define RFB_CMSG_POINTER_EVENT 5
+#define RFB_CMSG_CLIENT_CUT_TEXT 6
+
+#define RFB_SMSG_FRAMEBUFFER_UPDATE 0
+#define RFB_SMSG_SET_COLOR_MAP_ENTRIES 1
+#define RFB_SMSG_BELL 2
+#define RFB_SMSG_SERVER_CUT_TEXT 3
+
+#define RFB_ENCODING_RAW 0
+#define RFB_ENCODING_TRLE 15
+
+#define RFB_TILE_ENCODING_RAW 0
+#define RFB_TILE_ENCODING_SOLID 1
+
+typedef struct {
+	uint8_t bpp;
+	uint8_t depth;
+	uint8_t big_endian;
+	uint8_t true_color;
+	uint16_t r_max;
+	uint16_t g_max;
+	uint16_t b_max;
+	uint8_t r_shift;
+	uint8_t g_shift;
+	uint8_t b_shift;
+	uint8_t pad[3];
+} __attribute__((packed)) rfb_pixel_format_t;
+
+typedef struct {
+	uint16_t width;
+	uint16_t height;
+	rfb_pixel_format_t pixel_format;
+	uint32_t name_length;
+	char name[0];
+} __attribute__((packed)) rfb_server_init_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t pad[3];
+	rfb_pixel_format_t pixel_format;
+} __attribute__((packed)) rfb_set_pixel_format_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t pad;
+	uint16_t count;
+} __attribute__((packed)) rfb_set_encodings_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t incremental;
+	uint16_t x;
+	uint16_t y;
+	uint16_t width;
+	uint16_t height;
+} __attribute__((packed)) rfb_framebuffer_update_request_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t down_flag;
+	uint8_t pad[2];
+	uint32_t key;
+} __attribute__((packed)) rfb_key_event_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t button_mask;
+	uint16_t x;
+	uint16_t y;
+} __attribute__((packed)) rfb_pointer_event_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t pad;
+	uint32_t length;
+	char text[0];
+} __attribute__((packed)) rfb_client_cut_text_t;
+
+typedef struct {
+	uint16_t x;
+	uint16_t y;
+	uint16_t width;
+	uint16_t height;
+	int32_t enctype;
+	uint8_t data[0];
+} __attribute__((packed)) rfb_rectangle_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t pad;
+	uint16_t rect_count;
+} __attribute__((packed)) rfb_framebuffer_update_t;
+
+typedef struct {
+	uint8_t message_type;
+	uint8_t pad;
+	uint16_t first_color;
+	uint16_t color_count;
+} __attribute((packed)) rfb_set_color_map_entries_t;
+
+typedef struct {
+	uint16_t red;
+	uint16_t green;
+	uint16_t blue;
+} __attribute__((packed)) rfb_color_map_entry_t;
+
+typedef struct {
+	uint16_t width;
+	uint16_t height;
+	rfb_pixel_format_t pixel_format;
+	const char *name;
+	int listen_sd;
+	pixelmap_t framebuffer;
+	rfb_rectangle_t damage_rect;
+	bool damage_valid;
+	fibril_mutex_t lock;
+	pixel_t *palette;
+	size_t palette_used;
+	bool supports_trle;
+} rfb_t;
+
+
+extern int rfb_init(rfb_t *, uint16_t, uint16_t, const char *);
+extern int rfb_set_size(rfb_t *, uint16_t, uint16_t);
+extern int rfb_listen(rfb_t *, uint16_t);
+extern void rfb_accept(rfb_t *);
+
+#endif
Index: uspace/srv/hw/irc/obio/obio.c
===================================================================
--- uspace/srv/hw/irc/obio/obio.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/hw/irc/obio/obio.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -69,5 +69,5 @@
 #define INO_MASK	0x1f
 
-static void *base_phys;
+static uintptr_t base_phys;
 static volatile uint64_t *base_virt;
 
@@ -123,5 +123,5 @@
 	}
 	
-	base_phys = (void *) paddr;
+	base_phys = (uintptr_t) paddr;
 	
 	int flags = AS_AREA_READ | AS_AREA_WRITE;
@@ -135,5 +135,5 @@
 	}
 	
-	printf("%s: OBIO registers with base at %p\n", NAME, base_phys);
+	printf("%s: OBIO registers with base at %zu\n", NAME, base_phys);
 	
 	async_set_client_connection(obio_connection);
Index: uspace/srv/locsrv/category.c
===================================================================
--- uspace/srv/locsrv/category.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/locsrv/category.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -73,8 +73,5 @@
 
 	size_t pos = 0;
-	list_foreach(cdir->categories, item) {
-		category_t *cat =
-		    list_get_instance(item, category_t, cat_list);
-
+	list_foreach(cdir->categories, cat_list, category_t, cat) {
 		if (pos < buf_cnt)
 			id_buf[pos] = cat->id;
@@ -116,7 +113,5 @@
 
 	/* Verify that category does not contain this service yet. */
-	list_foreach(cat->svc_memb, item) {
-		svc_categ_t *memb = list_get_instance(item, svc_categ_t,
-		    cat_link);
+	list_foreach(cat->svc_memb, cat_link, svc_categ_t, memb) {
 		if (memb->svc == svc) {
 			return EEXIST;
@@ -154,7 +149,5 @@
 	assert(fibril_mutex_is_locked(&cdir->mutex));
 
-	list_foreach(cdir->categories, item) {
-		category_t *cat = list_get_instance(item, category_t,
-		    cat_list);
+	list_foreach(cdir->categories, cat_list, category_t, cat) {
 		if (cat->id == catid)
 			return cat;
@@ -169,7 +162,5 @@
 	assert(fibril_mutex_is_locked(&cdir->mutex));
 
-	list_foreach(cdir->categories, item) {
-		category_t *cat = list_get_instance(item, category_t,
-		    cat_list);
+	list_foreach(cdir->categories, cat_list, category_t, cat) {
 		if (str_cmp(cat->name, name) == 0)
 			return cat;
@@ -197,8 +188,5 @@
 
 	size_t pos = 0;
-	list_foreach(cat->svc_memb, item) {
-		svc_categ_t *memb =
-		    list_get_instance(item, svc_categ_t, cat_link);
-
+	list_foreach(cat->svc_memb, cat_link, svc_categ_t, memb) {
 		if (pos < buf_cnt)
 			id_buf[pos] = memb->svc->id;
Index: uspace/srv/locsrv/locsrv.c
===================================================================
--- uspace/srv/locsrv/locsrv.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/locsrv/locsrv.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -184,7 +184,5 @@
 	assert(fibril_mutex_is_locked(&services_list_mutex));
 	
-	list_foreach(namespaces_list, item) {
-		loc_namespace_t *namespace =
-		    list_get_instance(item, loc_namespace_t, namespaces);
+	list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
 		if (str_cmp(namespace->name, name) == 0)
 			return namespace;
@@ -203,7 +201,5 @@
 	assert(fibril_mutex_is_locked(&services_list_mutex));
 	
-	list_foreach(namespaces_list, item) {
-		loc_namespace_t *namespace =
-		    list_get_instance(item, loc_namespace_t, namespaces);
+	list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
 		if (namespace->id == id)
 			return namespace;
@@ -219,7 +215,5 @@
 	assert(fibril_mutex_is_locked(&services_list_mutex));
 	
-	list_foreach(services_list, item) {
-		loc_service_t *service =
-		    list_get_instance(item, loc_service_t, services);
+	list_foreach(services_list, services, loc_service_t, service) {
 		if ((str_cmp(service->namespace->name, ns_name) == 0)
 		    && (str_cmp(service->name, name) == 0))
@@ -239,7 +233,5 @@
 	assert(fibril_mutex_is_locked(&services_list_mutex));
 	
-	list_foreach(services_list, item) {
-		loc_service_t *service =
-		    list_get_instance(item, loc_service_t, services);
+	list_foreach(services_list, services, loc_service_t, service) {
 		if (service->id == id)
 			return service;
@@ -889,9 +881,5 @@
 	fibril_mutex_lock(&callback_sess_mutex);
 	
-	list_foreach(callback_sess_list, link) {
-		cb_sess_t *cb_sess;
-		
-		cb_sess = list_get_instance(link, cb_sess_t, cb_sess_list);
-		
+	list_foreach(callback_sess_list, cb_sess_list, cb_sess_t, cb_sess) {
 		async_exch_t *exch = async_exchange_begin(cb_sess->sess);
 		async_msg_0(exch, LOC_EVENT_CAT_CHANGE);
@@ -1050,8 +1038,5 @@
 	
 	size_t pos = 0;
-	list_foreach(namespaces_list, item) {
-		loc_namespace_t *namespace =
-		    list_get_instance(item, loc_namespace_t, namespaces);
-		
+	list_foreach(namespaces_list, namespaces, loc_namespace_t, namespace) {
 		desc[pos].id = namespace->id;
 		str_cpy(desc[pos].name, LOC_NAME_MAXLEN, namespace->name);
@@ -1114,8 +1099,5 @@
 	
 	size_t pos = 0;
-	list_foreach(services_list, item) {
-		loc_service_t *service =
-		    list_get_instance(item, loc_service_t, services);
-		
+	list_foreach(services_list, services, loc_service_t, service) {
 		if (service->namespace == namespace) {
 			desc[pos].id = service->id;
@@ -1375,4 +1357,7 @@
 
 	cat = category_new("renderer");
+	categ_dir_add_cat(&cdir, cat);
+	
+	cat = category_new("audio-pcm");
 	categ_dir_add_cat(&cdir, cat);
 	
Index: uspace/srv/logger/logs.c
===================================================================
--- uspace/srv/logger/logs.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/logger/logs.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -44,6 +44,5 @@
 static logger_log_t *find_log_by_name_and_parent_no_list_lock(const char *name, logger_log_t *parent)
 {
-	list_foreach(log_list, it) {
-		logger_log_t *log = list_get_instance(it, logger_log_t, link);
+	list_foreach(log_list, link, logger_log_t, log) {
 		if ((parent == log->parent) && (str_cmp(log->name, name) == 0))
 			return log;
@@ -148,6 +147,5 @@
 
 	fibril_mutex_lock(&log_list_guard);
-	list_foreach(log_list, it) {
-		logger_log_t *log = list_get_instance(it, logger_log_t, link);
+	list_foreach(log_list, link, logger_log_t, log) {
 		if (str_cmp(log->full_name, name) == 0) {
 			fibril_mutex_lock(&log->guard);
@@ -166,6 +164,5 @@
 
 	fibril_mutex_lock(&log_list_guard);
-	list_foreach(log_list, it) {
-		logger_log_t *log = list_get_instance(it, logger_log_t, link);
+	list_foreach(log_list, link, logger_log_t, log) {
 		if ((sysarg_t) log == id) {
 			fibril_mutex_lock(&log->guard);
Index: uspace/srv/net/dhcp/Makefile
===================================================================
--- uspace/srv/net/dhcp/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/net/dhcp/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2013 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+BINARY = dhcp
+
+SOURCES = \
+	dhcp.c \
+	main.c \
+	transport.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/net/dhcp/dhcp.c
===================================================================
--- uspace/srv/net/dhcp/dhcp.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/net/dhcp/dhcp.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,626 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup dhcp
+ * @{
+ */
+/**
+ * @file
+ * @brief DHCP client
+ */
+
+#include <adt/list.h>
+#include <bitops.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <inet/addr.h>
+#include <inet/dnsr.h>
+#include <inet/inetcfg.h>
+#include <io/log.h>
+#include <loc.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "dhcp.h"
+#include "dhcp_std.h"
+#include "transport.h"
+
+enum {
+	/** In microseconds */
+	dhcp_discover_timeout_val = 5 * 1000 * 1000,
+	/** In microseconds */
+	dhcp_request_timeout_val = 1 * 1000 * 1000,
+	dhcp_discover_retries = 5,
+	dhcp_request_retries = 3
+};
+
+#define MAX_MSG_SIZE 1024
+static uint8_t msgbuf[MAX_MSG_SIZE];
+
+/** List of registered links (of dhcp_link_t) */
+static list_t dhcp_links;
+
+static void dhcpsrv_discover_timeout(void *);
+static void dhcpsrv_request_timeout(void *);
+
+typedef enum {
+	ds_bound,
+	ds_fail,
+	ds_init,
+	ds_init_reboot,
+	ds_rebinding,
+	ds_renewing,
+	ds_requesting,
+	ds_selecting
+} dhcp_state_t;
+
+typedef struct {
+	/** Message type */
+	enum dhcp_msg_type msg_type;
+	/** Offered address */
+	inet_naddr_t oaddr;
+	/** Server address */
+	inet_addr_t srv_addr;
+	/** Router address */
+	inet_addr_t router;
+	/** DNS server */
+	inet_addr_t dns_server;
+} dhcp_offer_t;
+
+typedef struct {
+	/** Link to dhcp_links list */
+	link_t links;
+	/** Link service ID */
+	service_id_t link_id;
+	/** Link info */
+	inet_link_info_t link_info;
+	/** Transport */
+	dhcp_transport_t dt;
+	/** Transport timeout */
+	fibril_timer_t *timeout;
+	/** Number of retries */
+	int retries_left;
+	/** Link state */
+	dhcp_state_t state;
+	/** Last received offer */
+	dhcp_offer_t offer;
+} dhcp_link_t;
+
+static void dhcpsrv_recv(void *, void *, size_t);
+
+/** Decode subnet mask into subnet prefix length. */
+static int subnet_mask_decode(uint32_t mask, int *bits)
+{
+	int zbits;
+	uint32_t nmask;
+
+	if (mask == 0xffffffff) {
+		*bits = 32;
+		return EOK;
+	}
+
+	zbits = 1 + fnzb32(mask ^ 0xffffffff);
+	nmask = BIT_RRANGE(uint32_t, zbits);
+
+	if ((mask ^ nmask) != 0xffffffff) {
+		/* The mask is not in the form 1**n,0**m */
+		return EINVAL;
+	}
+
+	*bits = 32 - zbits;
+	return EOK;
+}
+
+static uint32_t dhcp_uint32_decode(uint8_t *data)
+{
+	return
+	    ((uint32_t)data[0] << 24) |
+	    ((uint32_t)data[1] << 16) |
+	    ((uint32_t)data[2] << 8) |
+	    ((uint32_t)data[3]);
+}
+
+static int dhcp_send_discover(dhcp_link_t *dlink)
+{
+	dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
+	uint8_t *opt = msgbuf + sizeof(dhcp_hdr_t);
+
+	memset(msgbuf, 0, MAX_MSG_SIZE);
+	hdr->op = op_bootrequest;
+	hdr->htype = 1; /* AHRD_ETHERNET */
+	hdr->hlen = sizeof(addr48_t);
+	hdr->xid = host2uint32_t_be(42);
+	hdr->flags = flag_broadcast;
+
+	addr48(dlink->link_info.mac_addr, hdr->chaddr);
+	hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
+
+	opt[0] = opt_msg_type;
+	opt[1] = 1;
+	opt[2] = msg_dhcpdiscover;
+	opt[3] = opt_end;
+
+	return dhcp_send(&dlink->dt, msgbuf, sizeof(dhcp_hdr_t) + 4);
+}
+
+static int dhcp_send_request(dhcp_link_t *dlink, dhcp_offer_t *offer)
+{
+	dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
+	uint8_t *opt = msgbuf + sizeof(dhcp_hdr_t);
+	size_t i;
+
+	memset(msgbuf, 0, MAX_MSG_SIZE);
+	hdr->op = op_bootrequest;
+	hdr->htype = 1; /* AHRD_ETHERNET */
+	hdr->hlen = 6;
+	hdr->xid = host2uint32_t_be(42);
+	hdr->flags = flag_broadcast;
+	hdr->ciaddr = host2uint32_t_be(offer->oaddr.addr);
+	addr48(dlink->link_info.mac_addr, hdr->chaddr);
+	hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
+
+	i = 0;
+
+	opt[i++] = opt_msg_type;
+	opt[i++] = 1;
+	opt[i++] = msg_dhcprequest;
+
+	opt[i++] = opt_req_ip_addr;
+	opt[i++] = 4;
+	opt[i++] = offer->oaddr.addr >> 24;
+	opt[i++] = (offer->oaddr.addr >> 16) & 0xff;
+	opt[i++] = (offer->oaddr.addr >> 8) & 0xff;
+	opt[i++] = offer->oaddr.addr & 0xff;
+
+	opt[i++] = opt_server_id;
+	opt[i++] = 4;
+	opt[i++] = offer->srv_addr.addr >> 24;
+	opt[i++] = (offer->srv_addr.addr >> 16) & 0xff;
+	opt[i++] = (offer->srv_addr.addr >> 8) & 0xff;
+	opt[i++] = offer->srv_addr.addr & 0xff;
+
+	opt[i++] = opt_end;
+
+	return dhcp_send(&dlink->dt, msgbuf, sizeof(dhcp_hdr_t) + i);
+}
+
+static int dhcp_parse_reply(void *msg, size_t size, dhcp_offer_t *offer)
+{
+	dhcp_hdr_t *hdr = (dhcp_hdr_t *)msg;
+	inet_addr_t yiaddr;
+	inet_addr_t siaddr;
+	inet_addr_t giaddr;
+	uint32_t subnet_mask;
+	bool have_subnet_mask = false;
+	bool have_server_id = false;
+	int subnet_bits;
+	char *saddr;
+	uint8_t opt_type, opt_len;
+	uint8_t *msgb;
+	int rc;
+	size_t i;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Receive reply");
+	memset(offer, 0, sizeof(*offer));
+
+	inet_addr_set(uint32_t_be2host(hdr->yiaddr), &yiaddr);
+	rc = inet_addr_format(&yiaddr, &saddr);
+	if (rc != EOK)
+		return rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Your IP address: %s", saddr);
+	free(saddr);
+
+	inet_addr_set(uint32_t_be2host(hdr->siaddr), &siaddr);
+	rc = inet_addr_format(&siaddr, &saddr);
+	if (rc != EOK)
+		return rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Next server IP address: %s", saddr);
+	free(saddr);
+
+	inet_addr_set(uint32_t_be2host(hdr->giaddr), &giaddr);
+	rc = inet_addr_format(&giaddr, &saddr);
+	if (rc != EOK)
+		return rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Relay agent IP address: %s", saddr);
+	free(saddr);
+
+	inet_naddr_set(yiaddr.addr, 0, &offer->oaddr);
+
+	msgb = (uint8_t *)msg;
+
+	i = sizeof(dhcp_hdr_t);
+	while (i < size) {
+		opt_type = msgb[i++];
+
+		if (opt_type == opt_pad)
+			continue;
+		if (opt_type == opt_end)
+			break;
+
+		if (i >= size)
+			return EINVAL;
+
+		opt_len = msgb[i++];
+
+		if (i + opt_len > size)
+			return EINVAL;
+
+		switch (opt_type) {
+		case opt_subnet_mask:
+			if (opt_len != 4)
+				return EINVAL;
+			subnet_mask = dhcp_uint32_decode(&msgb[i]);
+			rc = subnet_mask_decode(subnet_mask, &subnet_bits);
+			if (rc != EOK)
+				return EINVAL;
+			offer->oaddr.prefix = subnet_bits;
+			have_subnet_mask = true;
+			break;
+		case opt_msg_type:
+			if (opt_len != 1)
+				return EINVAL;
+			offer->msg_type = msgb[i];
+			break;
+		case opt_server_id:
+			if (opt_len != 4)
+				return EINVAL;
+			inet_addr_set(dhcp_uint32_decode(&msgb[i]),
+			    &offer->srv_addr);
+			have_server_id = true;
+			break;
+		case opt_router:
+			if (opt_len != 4)
+				return EINVAL;
+			inet_addr_set(dhcp_uint32_decode(&msgb[i]),
+			    &offer->router);
+			break;
+		case opt_dns_server:
+			if (opt_len != 4)
+				return EINVAL;
+			inet_addr_set(dhcp_uint32_decode(&msgb[i]),
+			    &offer->dns_server);
+			break;
+		case opt_end:
+			break;
+		default:
+			break;
+		}
+
+		/* Advance to the next option */
+		i = i + opt_len;
+	}
+
+	if (!have_server_id) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Missing server ID option.");
+		return rc;
+	}
+
+	if (!have_subnet_mask) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Missing subnet mask option.");
+		return rc;
+	}
+
+	rc = inet_naddr_format(&offer->oaddr, &saddr);
+	if (rc != EOK)
+		return rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Offered network address: %s", saddr);
+	free(saddr);
+
+	if (offer->router.addr != 0) {
+		rc = inet_addr_format(&offer->router, &saddr);
+		if (rc != EOK)
+			return rc;
+
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Router address: %s", saddr);
+		free(saddr);
+	}
+
+	if (offer->dns_server.addr != 0) {
+		rc = inet_addr_format(&offer->dns_server, &saddr);
+		if (rc != EOK)
+			return rc;
+
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "DNS server: %s", saddr);
+		free(saddr);
+	}
+
+	return EOK;
+}
+
+static int dhcp_cfg_create(service_id_t iplink, dhcp_offer_t *offer)
+{
+	int rc;
+	service_id_t addr_id;
+	service_id_t sroute_id;
+	inet_naddr_t defr;
+
+	rc = inetcfg_addr_create_static("dhcp4a", &offer->oaddr, iplink,
+	    &addr_id);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR,
+		    "Error creating IP address %s (%d)", "dhcp4a", rc);
+		return rc;
+	}
+
+	if (offer->router.addr != 0) {
+		inet_naddr_set(0, 0, &defr);
+
+		rc = inetcfg_sroute_create("dhcpdef", &defr, &offer->router, &sroute_id);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating "
+			    "default route %s (%d).", "dhcpdef", rc);
+			return rc;
+		}
+	}
+
+	if (offer->dns_server.addr != 0) {
+		rc = dnsr_set_srvaddr(&offer->dns_server);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_ERROR, "Error setting "
+			    "nameserver address (%d))", rc);
+			return rc;
+		}
+	}
+
+	return EOK;
+}
+
+void dhcpsrv_links_init(void)
+{
+	list_initialize(&dhcp_links);
+}
+
+static dhcp_link_t *dhcpsrv_link_find(service_id_t link_id)
+{
+	list_foreach(dhcp_links, links, dhcp_link_t, dlink) {
+		if (dlink->link_id == link_id)
+			return dlink;
+	}
+
+	return NULL;
+}
+
+static void dhcp_link_set_failed(dhcp_link_t *dlink)
+{
+	log_msg(LOG_DEFAULT, LVL_NOTE, "Giving up on link %s",
+	    dlink->link_info.name);
+	dlink->state = ds_fail;
+}
+
+int dhcpsrv_link_add(service_id_t link_id)
+{
+	dhcp_link_t *dlink;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcpsrv_link_add(%zu)", link_id);
+
+	if (dhcpsrv_link_find(link_id) != NULL) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Link %zu already added",
+		    link_id);
+		return EEXIST;
+	}
+
+	dlink = calloc(1, sizeof(dhcp_link_t));
+	if (dlink == NULL)
+		return ENOMEM;
+
+	dlink->link_id = link_id;
+	dlink->timeout = fibril_timer_create();
+	if (dlink->timeout == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	/* Get link hardware address */
+	rc = inetcfg_link_get(link_id, &dlink->link_info);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error getting properties "
+		    "for link %zu.", link_id);
+		rc = EIO;
+		goto error;
+	}
+
+	rc = dhcp_transport_init(&dlink->dt, link_id, dhcpsrv_recv, dlink);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error initializing DHCP "
+		    "transport for link %s.", dlink->link_info.name);
+		rc = EIO;
+		goto error;
+	}
+
+	dlink->state = ds_selecting;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPDISCOVER");
+	rc = dhcp_send_discover(dlink);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error sending DHCPDISCOVER.");
+		dhcp_link_set_failed(dlink);
+		rc = EIO;
+		goto error;
+	}
+
+	dlink->retries_left = dhcp_discover_retries;
+	fibril_timer_set(dlink->timeout, dhcp_discover_timeout_val,
+	    dhcpsrv_discover_timeout, dlink);
+
+	list_append(&dlink->links, &dhcp_links);
+
+	return EOK;
+error:
+	if (dlink != NULL && dlink->timeout != NULL)
+		fibril_timer_destroy(dlink->timeout);
+	free(dlink);
+	return rc;
+}
+
+int dhcpsrv_link_remove(service_id_t link_id)
+{
+	return ENOTSUP;
+}
+
+static void dhcpsrv_recv_offer(dhcp_link_t *dlink, dhcp_offer_t *offer)
+{
+	int rc;
+
+	if (dlink->state != ds_selecting) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Received offer in state "
+		    " %d, ignoring.", (int)dlink->state);
+		return;
+	}
+
+	fibril_timer_clear(dlink->timeout);
+	dlink->offer = *offer;
+	dlink->state = ds_requesting;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPREQUEST");
+	rc = dhcp_send_request(dlink, offer);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Error sending request.");
+		return;
+	}
+
+	dlink->retries_left = dhcp_request_retries;
+	fibril_timer_set(dlink->timeout, dhcp_request_timeout_val,
+	    dhcpsrv_request_timeout, dlink);
+}
+
+static void dhcpsrv_recv_ack(dhcp_link_t *dlink, dhcp_offer_t *offer)
+{
+	int rc;
+
+	if (dlink->state != ds_requesting) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Received ack in state "
+		    " %d, ignoring.", (int)dlink->state);
+		return;
+	}
+
+	fibril_timer_clear(dlink->timeout);
+	dlink->offer = *offer;
+	dlink->state = ds_bound;
+
+	rc = dhcp_cfg_create(dlink->link_id, offer);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Error creating configuration.");
+		return;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "%s: Successfully configured.",
+	    dlink->link_info.name);
+}
+
+static void dhcpsrv_recv(void *arg, void *msg, size_t size)
+{
+	dhcp_link_t *dlink = (dhcp_link_t *)arg;
+	dhcp_offer_t offer;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: dhcpsrv_recv() %zu bytes",
+	    dlink->link_info.name, size);
+
+	rc = dhcp_parse_reply(msg, size, &offer);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Error parsing reply");
+		return;
+	}
+
+	switch (offer.msg_type) {
+	case msg_dhcpoffer:
+		dhcpsrv_recv_offer(dlink, &offer);
+		break;
+	case msg_dhcpack:
+		dhcpsrv_recv_ack(dlink, &offer);
+		break;
+	default:
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Received unexpected "
+		    "message type. %d", (int)offer.msg_type);
+		break;
+	}
+}
+
+static void dhcpsrv_discover_timeout(void *arg)
+{
+	dhcp_link_t *dlink = (dhcp_link_t *)arg;
+	int rc;
+
+	assert(dlink->state == ds_selecting);
+	log_msg(LOG_DEFAULT, LVL_NOTE, "%s: dcpsrv_discover_timeout",
+	    dlink->link_info.name);
+
+	if (dlink->retries_left == 0) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Retries exhausted");
+		dhcp_link_set_failed(dlink);
+		return;
+	}
+	--dlink->retries_left;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPDISCOVER");
+	rc = dhcp_send_discover(dlink);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error sending DHCPDISCOVER");
+		dhcp_link_set_failed(dlink);
+		return;
+	}
+
+	fibril_timer_set(dlink->timeout, dhcp_discover_timeout_val,
+	    dhcpsrv_discover_timeout, dlink);
+}
+
+static void dhcpsrv_request_timeout(void *arg)
+{
+	dhcp_link_t *dlink = (dhcp_link_t *)arg;
+	int rc;
+
+	assert(dlink->state == ds_requesting);
+	log_msg(LOG_DEFAULT, LVL_NOTE, "%s: dcpsrv_request_timeout",
+	    dlink->link_info.name);
+
+	if (dlink->retries_left == 0) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Retries exhausted");
+		dhcp_link_set_failed(dlink);
+		return;
+	}
+	--dlink->retries_left;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPREQUEST");
+	rc = dhcp_send_request(dlink, &dlink->offer);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Error sending request.");
+		dhcp_link_set_failed(dlink);
+		return;
+	}
+
+	fibril_timer_set(dlink->timeout, dhcp_request_timeout_val,
+	    dhcpsrv_request_timeout, dlink);
+}
+
+/** @}
+ */
Index: uspace/srv/net/dhcp/dhcp.h
===================================================================
--- uspace/srv/net/dhcp/dhcp.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/net/dhcp/dhcp.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup dhcp
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef DHCP_H
+#define DHCP_H
+
+#include <ipc/loc.h>
+
+extern void dhcpsrv_links_init(void);
+extern int dhcpsrv_link_add(service_id_t);
+extern int dhcpsrv_link_remove(service_id_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/dhcp/dhcp_std.h
===================================================================
--- uspace/srv/net/dhcp/dhcp_std.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/net/dhcp/dhcp_std.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup dhcp
+ * @{
+ */
+/**
+ * @file
+ * @brief DHCP standard definitions
+ */
+
+#ifndef DHCP_STD_H
+#define DHCP_STD_H
+
+#include <stdint.h>
+
+enum {
+	dhcp_server_port = 67,
+	dhcp_client_port = 68
+};
+
+/** Fixed part of DHCP message */
+typedef struct {
+	/** Message op code */
+	uint8_t op;
+	/** Hardware address type */
+	uint8_t htype;
+	/** Hardware address length */
+	uint8_t hlen;
+	/** Hops */
+	uint8_t hops;
+	/** Transaction ID */
+	uint32_t xid;
+	/** Seconds elapsed since client began address acqusition or renewal */
+	uint16_t secs;
+	/** Flags */
+	uint16_t flags;
+	/** Client IP address */
+	uint32_t ciaddr;
+	/** Your (client) IP address */
+	uint32_t yiaddr;
+	/** IP address of next server */
+	uint32_t siaddr;
+	/** Relay agent IP address */
+	uint32_t giaddr;
+	/** Client hardware address */
+	uint8_t chaddr[16];
+	/** Server host name */
+	uint8_t sname[64];
+	/** Boot file name */
+	uint8_t file[128];
+	/** Magic cookie signalling the start of DHCP options */
+	uint32_t opt_magic;
+} dhcp_hdr_t;
+
+/** Values for dhcp_hdr_t.op */
+enum dhcp_op {
+	op_bootrequest = 1,
+	op_bootreply = 2
+};
+
+/** Values for dhcp_hdr_t.flags */
+enum dhcp_flags {
+	flag_broadcast = 0x80
+};
+
+/** Magic cookie signalling the start of DHCP options field */
+enum {
+	dhcp_opt_magic = (99 << 24) | (130 << 16) | (83 << 8) | 99
+};
+
+enum dhcp_option_code {
+	/** Padding */
+	opt_pad = 0,
+	/** Subnet mask */
+	opt_subnet_mask = 1,
+	/** Router IP address */
+	opt_router = 3,
+	/** Domain name server */
+	opt_dns_server = 6,
+	/** Requested IP address */
+	opt_req_ip_addr = 50,
+	/** DHCP message type */
+	opt_msg_type = 53,
+	/** Server identifier */
+	opt_server_id = 54,
+	/** End */
+	opt_end = 255
+};
+
+/** DHCP message type */
+enum dhcp_msg_type {
+	msg_dhcpdiscover = 1,
+	msg_dhcpoffer = 2,
+	msg_dhcprequest = 3,
+	msg_dhcpdecline = 4,
+	msg_dhcpack = 5,
+	msg_dhcpnak = 6,
+	msg_dhcprelease = 7
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/dhcp/main.c
===================================================================
--- uspace/srv/net/dhcp/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/net/dhcp/main.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup dhcp
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <async.h>
+#include <errno.h>
+#include <io/log.h>
+#include <inet/inetcfg.h>
+#include <ipc/dhcp.h>
+#include <ipc/services.h>
+#include <loc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <task.h>
+
+#include "dhcp.h"
+
+#define NAME  "dhcp"
+
+static void dhcp_client_conn(ipc_callid_t, ipc_call_t *, void *);
+
+static int dhcp_init(void)
+{
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcp_init()");
+
+	dhcpsrv_links_init();
+
+	rc = inetcfg_init();
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error contacting inet configuration service.\n");
+		return EIO;
+	}
+
+	async_set_client_connection(dhcp_client_conn);
+
+	rc = loc_server_register(NAME);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server (%d).", rc);
+		return EEXIST;
+	}
+
+	service_id_t sid;
+	rc = loc_service_register(SERVICE_NAME_DHCP, &sid);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service (%d).", rc);
+		return EEXIST;
+	}
+
+	return EOK;
+}
+
+static void dhcp_link_add_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	sysarg_t link_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcp_link_add_srv()");
+
+	link_id = IPC_GET_ARG1(*call);
+
+	rc = dhcpsrv_link_add(link_id);
+	async_answer_0(callid, rc);
+}
+
+static void dhcp_link_remove_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	sysarg_t link_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcp_link_remove_srv()");
+
+	link_id = IPC_GET_ARG1(*call);
+
+	rc = dhcpsrv_link_remove(link_id);
+	async_answer_0(callid, rc);
+}
+
+static void dhcp_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcp_client_conn()");
+
+	/* Accept the connection */
+	async_answer_0(iid, EOK);
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		sysarg_t method = IPC_GET_IMETHOD(call);
+
+		if (!method) {
+			/* The other side has hung up */
+			async_answer_0(callid, EOK);
+			return;
+		}
+
+		switch (method) {
+		case DHCP_LINK_ADD:
+			dhcp_link_add_srv(callid, &call);
+			break;
+		case DHCP_LINK_REMOVE:
+			dhcp_link_remove_srv(callid, &call);
+			break;
+		default:
+			async_answer_0(callid, EINVAL);
+		}
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	int rc;
+
+	printf("%s: DHCP Service\n", NAME);
+
+	if (log_init(NAME) != EOK) {
+		printf(NAME ": Failed to initialize logging.\n");
+		return 1;
+	}
+
+	rc = dhcp_init();
+	if (rc != EOK)
+		return 1;
+
+	printf(NAME ": Accepting connections.\n");
+	task_retval(0);
+	async_manager();
+
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/srv/net/dhcp/transport.c
===================================================================
--- uspace/srv/net/dhcp/transport.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/net/dhcp/transport.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup dhcp
+ * @{
+ */
+/**
+ * @file
+ * @brief DHCP client
+ */
+
+#include <bitops.h>
+#include <inet/addr.h>
+#include <inet/dnsr.h>
+#include <inet/inetcfg.h>
+#include <io/log.h>
+#include <loc.h>
+#include <net/in.h>
+#include <net/inet.h>
+#include <net/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "dhcp.h"
+#include "dhcp_std.h"
+#include "transport.h"
+
+#define MAX_MSG_SIZE 1024
+static uint8_t msgbuf[MAX_MSG_SIZE];
+
+typedef struct {
+	/** Message type */
+	enum dhcp_msg_type msg_type;
+	/** Offered address */
+	inet_naddr_t oaddr;
+	/** Server address */
+	inet_addr_t srv_addr;
+	/** Router address */
+	inet_addr_t router;
+	/** DNS server */
+	inet_addr_t dns_server;
+} dhcp_offer_t;
+
+static int dhcp_recv_fibril(void *);
+
+int dhcp_send(dhcp_transport_t *dt, void *msg, size_t size)
+{
+	struct sockaddr_in addr;
+	int rc;
+
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(dhcp_server_port);
+	addr.sin_addr.s_addr = htonl(addr32_broadcast_all_hosts);
+
+	rc = sendto(dt->fd, msg, size, 0,
+	    (struct sockaddr *)&addr, sizeof(addr));
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Sending failed");
+		return rc;
+	}
+
+	return EOK;
+}
+
+static int dhcp_recv_msg(dhcp_transport_t *dt, void **rmsg, size_t *rsize)
+{
+	struct sockaddr_in src_addr;
+	socklen_t src_addr_size;
+	size_t recv_size;
+	int rc;
+
+	src_addr_size = sizeof(src_addr);
+	rc = recvfrom(dt->fd, msgbuf, MAX_MSG_SIZE, 0,
+	    (struct sockaddr *)&src_addr, &src_addr_size);
+	if (rc < 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "recvfrom failed (%d)", rc);
+		return rc;
+	}
+
+	recv_size = (size_t)rc;
+	*rmsg = msgbuf;
+	*rsize = recv_size;
+
+	return EOK;
+}
+
+int dhcp_transport_init(dhcp_transport_t *dt, service_id_t link_id,
+    dhcp_recv_cb_t recv_cb, void *arg)
+{
+	int fd;
+	struct sockaddr_in laddr;
+	int fid;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcptransport_init()");
+
+	laddr.sin_family = AF_INET;
+	laddr.sin_port = htons(dhcp_client_port);
+	laddr.sin_addr.s_addr = INADDR_ANY;
+
+	fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (fd < 0) {
+		rc = EIO;
+		goto error;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Bind socket.");
+	rc = bind(fd, (struct sockaddr *)&laddr, sizeof(laddr));
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Set socket options");
+	rc = setsockopt(fd, SOL_SOCKET, SO_IPLINK, &link_id, sizeof(link_id));
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
+	dt->fd = fd;
+	dt->recv_cb = recv_cb;
+	dt->cb_arg = arg;
+
+	fid = fibril_create(dhcp_recv_fibril, dt);
+	if (fid == 0) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	dt->recv_fid = fid;
+	fibril_add_ready(fid);
+
+	return EOK;
+error:
+	closesocket(fd);
+	return rc;
+}
+
+void dhcp_transport_fini(dhcp_transport_t *dt)
+{
+	closesocket(dt->fd);
+}
+
+static int dhcp_recv_fibril(void *arg)
+{
+	dhcp_transport_t *dt = (dhcp_transport_t *)arg;
+	void *msg;
+	size_t size;
+	int rc;
+
+	while (true) {
+		rc = dhcp_recv_msg(dt, &msg, &size);
+		if (rc != EOK)
+			break;
+
+		dt->recv_cb(dt->cb_arg, msg, size);
+	}
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/net/dhcp/transport.h
===================================================================
--- uspace/srv/net/dhcp/transport.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/net/dhcp/transport.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup dhcp
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef TRANSPORT_H
+#define TRANSPORT_H
+
+#include <ipc/loc.h>
+#include <sys/types.h>
+
+struct dhcp_transport;
+typedef struct dhcp_transport dhcp_transport_t;
+
+typedef void (*dhcp_recv_cb_t)(void *, void *, size_t);
+
+struct dhcp_transport {
+	/** Transport socket */
+	int fd;
+	/** Receive callback */
+	dhcp_recv_cb_t recv_cb;
+	/** Callback argument */
+	void *cb_arg;
+	/** Receive fibril ID */
+	int recv_fid;
+};
+
+extern int dhcp_transport_init(dhcp_transport_t *, service_id_t,
+    dhcp_recv_cb_t, void *);
+extern void dhcp_transport_fini(dhcp_transport_t *);
+extern int dhcp_send(dhcp_transport_t *dt, void *msg, size_t size);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/dnsrsrv/dns_msg.c
===================================================================
--- uspace/srv/net/dnsrsrv/dns_msg.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/dnsrsrv/dns_msg.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -510,6 +510,5 @@
 	size = sizeof(dns_header_t);
 
-	list_foreach(msg->question, link) {
-		dns_question_t *q = list_get_instance(link, dns_question_t, msg);
+	list_foreach(msg->question, msg, dns_question_t, q) {
 		rc = dns_question_encode(q, NULL, 0, &q_size);
 		if (rc != EOK)
@@ -526,6 +525,5 @@
 	di = sizeof(dns_header_t);
 
-	list_foreach(msg->question, link) {
-		dns_question_t *q = list_get_instance(link, dns_question_t, msg);
+	list_foreach(msg->question, msg, dns_question_t, q) {
 		rc = dns_question_encode(q, data + di, size - di, &q_size);
 		if (rc != EOK) {
Index: uspace/srv/net/dnsrsrv/dnsrsrv.c
===================================================================
--- uspace/srv/net/dnsrsrv/dnsrsrv.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/dnsrsrv/dnsrsrv.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -60,5 +60,5 @@
 	rc = transport_init();
 	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed initializing tarnsport.");
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed initializing transport.");
 		return EIO;
 	}
@@ -89,5 +89,5 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_get_srvaddr_srv()");
 	
-	uint16_t af = IPC_GET_ARG1(*icall);
+	ip_ver_t ver = IPC_GET_ARG1(*icall);
 	
 	char *name;
@@ -100,5 +100,5 @@
 	
 	dns_host_info_t *hinfo;
-	rc = dns_name2host(name, &hinfo, af);
+	rc = dns_name2host(name, &hinfo, ver);
 	if (rc != EOK) {
 		async_answer_0(iid, rc);
Index: uspace/srv/net/dnsrsrv/query.c
===================================================================
--- uspace/srv/net/dnsrsrv/query.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/dnsrsrv/query.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,5 +39,4 @@
 #include <stdlib.h>
 #include <str.h>
-#include <net/socket_codes.h>
 #include "dns_msg.h"
 #include "dns_std.h"
@@ -99,7 +98,5 @@
 	}
 	
-	list_foreach(amsg->answer, link) {
-		dns_rr_t *rr = list_get_instance(link, dns_rr_t, msg);
-		
+	list_foreach(amsg->answer, msg, dns_rr_t, rr) {
 		log_msg(LOG_DEFAULT, LVL_DEBUG, " - '%s' %u/%u, dsize %zu",
 		    rr->name, rr->rtype, rr->rclass, rr->rdata_size);
@@ -192,5 +189,5 @@
 }
 
-int dns_name2host(const char *name, dns_host_info_t **rinfo, uint16_t af)
+int dns_name2host(const char *name, dns_host_info_t **rinfo, ip_ver_t ver)
 {
 	dns_host_info_t *info = calloc(1, sizeof(dns_host_info_t));
@@ -200,6 +197,6 @@
 	int rc;
 	
-	switch (af) {
-	case AF_NONE:
+	switch (ver) {
+	case ip_any:
 		rc = dns_name_query(name, DTYPE_AAAA, info);
 		
@@ -208,8 +205,8 @@
 		
 		break;
-	case AF_INET:
+	case ip_v4:
 		rc = dns_name_query(name, DTYPE_A, info);
 		break;
-	case AF_INET6:
+	case ip_v6:
 		rc = dns_name_query(name, DTYPE_AAAA, info);
 		break;
Index: uspace/srv/net/dnsrsrv/query.h
===================================================================
--- uspace/srv/net/dnsrsrv/query.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/dnsrsrv/query.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -37,7 +37,8 @@
 #define QUERY_H
 
+#include <inet/addr.h>
 #include "dns_type.h"
 
-extern int dns_name2host(const char *, dns_host_info_t **, uint16_t);
+extern int dns_name2host(const char *, dns_host_info_t **, ip_ver_t);
 extern void dns_hostinfo_destroy(dns_host_info_t *);
 
Index: uspace/srv/net/dnsrsrv/transport.c
===================================================================
--- uspace/srv/net/dnsrsrv/transport.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/dnsrsrv/transport.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -158,7 +158,5 @@
 	assert(fibril_mutex_is_locked(&treq_lock));
 
-	list_foreach(treq_list, link) {
-		trans_req_t *treq = list_get_instance(link, trans_req_t, lreq);
-
+	list_foreach(treq_list, lreq, trans_req_t, treq) {
 		if (treq->req->id == resp->id) {
 			/* Match */
@@ -184,4 +182,6 @@
 {
 	trans_req_t *treq = NULL;
+	struct sockaddr *saddr = NULL;
+	socklen_t saddrlen;
 	
 	void *req_data;
@@ -191,25 +191,8 @@
 		goto error;
 	
-	struct sockaddr_in addr;
-	struct sockaddr_in6 addr6;
-	uint16_t af =
-	    inet_addr_sockaddr_in(&dns_server_addr, &addr, &addr6);
-	
-	struct sockaddr *address;
-	socklen_t addrlen;
-	
-	switch (af) {
-	case AF_INET:
-		addr.sin_port = htons(DNS_SERVER_PORT);
-		address = (struct sockaddr *) &addr;
-		addrlen = sizeof(addr);
-		break;
-	case AF_INET6:
-		addr6.sin6_port = htons(DNS_SERVER_PORT);
-		address = (struct sockaddr *) &addr6;
-		addrlen = sizeof(addr6);
-		break;
-	default:
-		rc = EAFNOSUPPORT;
+	rc = inet_addr_sockaddr(&dns_server_addr, DNS_SERVER_PORT,
+	    &saddr, &saddrlen);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
 		goto error;
 	}
@@ -219,5 +202,5 @@
 	while (ntry < REQ_RETRY_MAX) {
 		rc = sendto(transport_fd, req_data, req_size, 0,
-		    (struct sockaddr *) address, addrlen);
+		    saddr, saddrlen);
 		if (rc != EOK)
 			goto error;
@@ -258,4 +241,5 @@
 	treq_destroy(treq);
 	free(req_data);
+	free(saddr);
 	return EOK;
 	
@@ -265,4 +249,5 @@
 	
 	free(req_data);
+	free(saddr);
 	return rc;
 }
Index: uspace/srv/net/ethip/arp.c
===================================================================
--- uspace/srv/net/ethip/arp.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/ethip/arp.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -40,5 +40,4 @@
 #include <inet/addr.h>
 #include <stdlib.h>
-#include <net/socket_codes.h>
 #include "arp.h"
 #include "atrans.h"
@@ -73,6 +72,6 @@
 	
 	addr32_t laddr_v4;
-	uint16_t laddr_af = inet_addr_get(&laddr->addr, &laddr_v4, NULL);
-	if (laddr_af != AF_INET)
+	ip_ver_t laddr_ver = inet_addr_get(&laddr->addr, &laddr_v4, NULL);
+	if (laddr_ver != ip_v4)
 		return;
 	
@@ -98,4 +97,10 @@
     addr48_t mac_addr)
 {
+	/* Broadcast address */
+	if (ip_addr == addr32_broadcast_all_hosts) {
+		addr48(addr48_broadcast, mac_addr);
+		return EOK;
+	}
+
 	int rc = atrans_lookup(ip_addr, mac_addr);
 	if (rc == EOK)
Index: uspace/srv/net/ethip/atrans.c
===================================================================
--- uspace/srv/net/ethip/atrans.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/ethip/atrans.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -51,8 +51,5 @@
 static ethip_atrans_t *atrans_find(addr32_t ip_addr)
 {
-	list_foreach(atrans_list, link) {
-		ethip_atrans_t *atrans = list_get_instance(link,
-		    ethip_atrans_t, atrans_list);
-
+	list_foreach(atrans_list, atrans_list, ethip_atrans_t, atrans) {
 		if (atrans->ip_addr == ip_addr)
 			return atrans;
Index: uspace/srv/net/ethip/ethip.c
===================================================================
--- uspace/srv/net/ethip/ethip.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/ethip/ethip.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -44,5 +44,4 @@
 #include <stdio.h>
 #include <stdlib.h>
-#include <net/socket_codes.h>
 #include "arp.h"
 #include "ethip.h"
@@ -249,5 +248,5 @@
 		sdu.size = frame.size;
 		log_msg(LOG_DEFAULT, LVL_DEBUG, " - call iplink_ev_recv");
-		rc = iplink_ev_recv(&nic->iplink, &sdu, AF_INET);
+		rc = iplink_ev_recv(&nic->iplink, &sdu, ip_v4);
 		break;
 	case ETYPE_IPV6:
@@ -256,5 +255,5 @@
 		sdu.size = frame.size;
 		log_msg(LOG_DEFAULT, LVL_DEBUG, " - call iplink_ev_recv");
-		rc = iplink_ev_recv(&nic->iplink, &sdu, AF_INET6);
+		rc = iplink_ev_recv(&nic->iplink, &sdu, ip_v6);
 		break;
 	default:
Index: uspace/srv/net/ethip/ethip_nic.c
===================================================================
--- uspace/srv/net/ethip/ethip_nic.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/ethip/ethip_nic.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -45,5 +45,4 @@
 #include <device/nic.h>
 #include <stdlib.h>
-#include <net/socket_codes.h>
 #include <mem.h>
 #include "ethip.h"
@@ -84,7 +83,5 @@
 		already_known = false;
 
-		list_foreach(ethip_nic_list, link) {
-			ethip_nic_t *nic = list_get_instance(link,
-			    ethip_nic_t, link);
+		list_foreach(ethip_nic_list, link, ethip_nic_t, nic) {
 			if (nic->svc_id == svcs[i]) {
 				already_known = true;
@@ -201,4 +198,11 @@
 		log_msg(LOG_DEFAULT, LVL_ERROR, "Error activating NIC '%s'.",
 		    nic->svc_name);
+		goto error;
+	}
+
+	rc = nic_broadcast_set_mode(nic->sess, NIC_BROADCAST_ACCEPTED);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error enabling "
+		    "reception of broadcast frames on '%s'.", nic->svc_name);
 		goto error;
 	}
@@ -313,8 +317,6 @@
 	    (unsigned) iplink_sid);
 
-	list_foreach(ethip_nic_list, link) {
+	list_foreach(ethip_nic_list, link, ethip_nic_t, nic) {
 		log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_find_by_iplink_sid - element");
-		ethip_nic_t *nic = list_get_instance(link, ethip_nic_t, link);
-
 		if (nic->iplink_sid == iplink_sid) {
 			log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_find_by_iplink_sid - found %p", nic);
@@ -350,10 +352,7 @@
 	size_t count = 0;
 	
-	list_foreach(nic->addr_list, link) {
-		ethip_link_addr_t *laddr = list_get_instance(link,
-		    ethip_link_addr_t, link);
-		
-		uint16_t af = inet_addr_get(&laddr->addr, NULL, NULL);
-		if (af == AF_INET6)
+	list_foreach(nic->addr_list, link, ethip_link_addr_t, laddr) {
+		ip_ver_t ver = inet_addr_get(&laddr->addr, NULL, NULL);
+		if (ver == ip_v6)
 			count++;
 	}
@@ -371,14 +370,11 @@
 	size_t i = 0;
 	
-	list_foreach(nic->addr_list, link) {
+	list_foreach(nic->addr_list, link, ethip_link_addr_t, laddr) {
+		addr128_t v6;
+		ip_ver_t ver = inet_addr_get(&laddr->addr, NULL, &v6);
+		if (ver != ip_v6)
+			continue;
+		
 		assert(i < count);
-		
-		ethip_link_addr_t *laddr = list_get_instance(link,
-		    ethip_link_addr_t, link);
-		
-		addr128_t v6;
-		uint16_t af = inet_addr_get(&laddr->addr, NULL, &v6);
-		if (af != AF_INET6)
-			continue;
 		
 		addr48_t mac;
@@ -444,8 +440,5 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_nic_addr_find()");
 	
-	list_foreach(nic->addr_list, link) {
-		ethip_link_addr_t *laddr = list_get_instance(link,
-		    ethip_link_addr_t, link);
-		
+	list_foreach(nic->addr_list, link, ethip_link_addr_t, laddr) {
 		if (inet_addr_compare(addr, &laddr->addr))
 			return laddr;
Index: uspace/srv/net/inetsrv/Makefile
===================================================================
--- uspace/srv/net/inetsrv/Makefile	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,5 +38,4 @@
 	inetcfg.c \
 	inetping.c \
-	inetping6.c \
 	ndp.c \
 	ntrans.c \
Index: uspace/srv/net/inetsrv/addrobj.c
===================================================================
--- uspace/srv/net/inetsrv/addrobj.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/addrobj.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -42,5 +42,4 @@
 #include <stdlib.h>
 #include <str.h>
-#include <net/socket_codes.h>
 #include "addrobj.h"
 #include "inetsrv.h"
@@ -115,8 +114,5 @@
 	fibril_mutex_lock(&addr_list_lock);
 	
-	list_foreach(addr_list, link) {
-		inet_addrobj_t *naddr = list_get_instance(link,
-		    inet_addrobj_t, addr_list);
-		
+	list_foreach(addr_list, addr_list, inet_addrobj_t, naddr) {
 		switch (find) {
 		case iaf_net:
@@ -158,8 +154,5 @@
 	    name, ilink->svc_name);
 
-	list_foreach(addr_list, link) {
-		inet_addrobj_t *naddr = list_get_instance(link,
-		    inet_addrobj_t, addr_list);
-
+	list_foreach(addr_list, addr_list, inet_addrobj_t, naddr) {
 		if (naddr->ilink == ilink && str_cmp(naddr->name, name) == 0) {
 			log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_addrobj_find_by_name_locked: found %p",
@@ -206,8 +199,5 @@
 	fibril_mutex_lock(&addr_list_lock);
 
-	list_foreach(addr_list, link) {
-		inet_addrobj_t *naddr = list_get_instance(link,
-		    inet_addrobj_t, addr_list);
-
+	list_foreach(addr_list, addr_list, inet_addrobj_t, naddr) {
 		if (naddr->id == id) {
 			fibril_mutex_unlock(&addr_list_lock);
@@ -227,24 +217,24 @@
 	inet_addr_t lsrc_addr;
 	inet_naddr_addr(&addr->naddr, &lsrc_addr);
-	
+
 	addr32_t lsrc_v4;
 	addr128_t lsrc_v6;
-	uint16_t lsrc_af = inet_addr_get(&lsrc_addr, &lsrc_v4, &lsrc_v6);
-	
+	ip_ver_t lsrc_ver = inet_addr_get(&lsrc_addr, &lsrc_v4, &lsrc_v6);
+
 	addr32_t ldest_v4;
 	addr128_t ldest_v6;
-	uint16_t ldest_af = inet_addr_get(ldest, &ldest_v4, &ldest_v6);
-	
-	if (lsrc_af != ldest_af)
+	ip_ver_t ldest_ver = inet_addr_get(ldest, &ldest_v4, &ldest_v6);
+
+	if (lsrc_ver != ldest_ver)
 		return EINVAL;
-	
+
 	int rc;
 	addr48_t ldest_mac;
-	
-	switch (ldest_af) {
-	case AF_INET:
+
+	switch (ldest_ver) {
+	case ip_v4:
 		return inet_link_send_dgram(addr->ilink, lsrc_v4, ldest_v4,
 		    dgram, proto, ttl, df);
-	case AF_INET6:
+	case ip_v6:
 		/*
 		 * Translate local destination IPv6 address.
@@ -253,9 +243,12 @@
 		if (rc != EOK)
 			return rc;
-		
+
 		return inet_link_send_dgram6(addr->ilink, ldest_mac, dgram,
 		    proto, ttl, df);
-	}
-	
+	default:
+		assert(false);
+		break;
+	}
+
 	return ENOTSUP;
 }
@@ -277,8 +270,5 @@
 
 	i = 0;
-	list_foreach(addr_list, link) {
-		inet_addrobj_t *addr = list_get_instance(link,
-		    inet_addrobj_t, addr_list);
-
+	list_foreach(addr_list, addr_list, inet_addrobj_t, addr) {
 		id_list[i++] = addr->id;
 	}
Index: uspace/srv/net/inetsrv/icmp.c
===================================================================
--- uspace/srv/net/inetsrv/icmp.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/icmp.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -40,5 +40,5 @@
 #include <mem.h>
 #include <stdlib.h>
-#include <net/socket_codes.h>
+#include <types/inetping.h>
 #include "icmp.h"
 #include "icmp_std.h"
@@ -105,4 +105,5 @@
 	reply->checksum = host2uint16_t_be(checksum);
 
+	rdgram.iplink = 0;
 	rdgram.src = dgram->dest;
 	rdgram.dest = dgram->src;
@@ -121,24 +122,18 @@
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "icmp_recv_echo_reply()");
-	
+
 	if (dgram->size < sizeof(icmp_echo_t))
 		return EINVAL;
-	
+
 	icmp_echo_t *reply = (icmp_echo_t *) dgram->data;
-	
+
 	inetping_sdu_t sdu;
-	
-	uint16_t family = inet_addr_get(&dgram->src, &sdu.src, NULL);
-	if (family != AF_INET)
-		return EINVAL;
-	
-	family = inet_addr_get(&dgram->dest, &sdu.dest, NULL);
-	if (family != AF_INET)
-		return EINVAL;
-	
+
+	sdu.src = dgram->src;
+	sdu.dest = dgram->dest;
 	sdu.seq_no = uint16_t_be2host(reply->seq_no);
 	sdu.data = reply + sizeof(icmp_echo_t);
 	sdu.size = dgram->size - sizeof(icmp_echo_t);
-	
+
 	uint16_t ident = uint16_t_be2host(reply->ident);
 
@@ -152,7 +147,7 @@
 	if (rdata == NULL)
 		return ENOMEM;
-	
+
 	icmp_echo_t *request = (icmp_echo_t *) rdata;
-	
+
 	request->type = ICMP_ECHO_REQUEST;
 	request->code = 0;
@@ -160,21 +155,21 @@
 	request->ident = host2uint16_t_be(ident);
 	request->seq_no = host2uint16_t_be(sdu->seq_no);
-	
+
 	memcpy(rdata + sizeof(icmp_echo_t), sdu->data, sdu->size);
-	
+
 	uint16_t checksum = inet_checksum_calc(INET_CHECKSUM_INIT, rdata, rsize);
 	request->checksum = host2uint16_t_be(checksum);
-	
+
 	inet_dgram_t dgram;
-	
-	inet_addr_set(sdu->src, &dgram.src);
-	inet_addr_set(sdu->dest, &dgram.dest);
-	
+
+	dgram.src = sdu->src;
+	dgram.dest = sdu->dest;
+	dgram.iplink = 0;
 	dgram.tos = ICMP_TOS;
 	dgram.data = rdata;
 	dgram.size = rsize;
-	
+
 	int rc = inet_route_packet(&dgram, IP_PROTO_ICMP, INET_TTL_MAX, 0);
-	
+
 	free(rdata);
 	return rc;
Index: uspace/srv/net/inetsrv/icmp.h
===================================================================
--- uspace/srv/net/inetsrv/icmp.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/icmp.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,4 +38,5 @@
 #define ICMP_H_
 
+#include <types/inetping.h>
 #include "inetsrv.h"
 
Index: uspace/srv/net/inetsrv/icmpv6.c
===================================================================
--- uspace/srv/net/inetsrv/icmpv6.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/icmpv6.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -40,9 +40,9 @@
 #include <mem.h>
 #include <stdlib.h>
-#include <net/socket_codes.h>
+#include <types/inetping.h>
 #include "icmpv6.h"
 #include "icmpv6_std.h"
 #include "inetsrv.h"
-#include "inetping6.h"
+#include "inetping.h"
 #include "pdu.h"
 
@@ -58,10 +58,10 @@
 	
 	addr128_t src_v6;
-	uint16_t src_af = inet_addr_get(&dgram->src, NULL, &src_v6);
+	ip_ver_t src_ver = inet_addr_get(&dgram->src, NULL, &src_v6);
 	
 	addr128_t dest_v6;
-	uint16_t dest_af = inet_addr_get(&dgram->dest, NULL, &dest_v6);
-	
-	if ((src_af != dest_af) || (src_af != AF_INET6))
+	ip_ver_t dest_ver = inet_addr_get(&dgram->dest, NULL, &dest_v6);
+	
+	if ((src_ver != dest_ver) || (src_ver != ip_v6))
 		return EINVAL;
 	
@@ -80,4 +80,5 @@
 	inet_get_srcaddr(&dgram->src, 0, &rdgram.src);
 	rdgram.dest = dgram->src;
+	rdgram.iplink = 0;
 	rdgram.tos = 0;
 	rdgram.data = reply;
@@ -115,11 +116,8 @@
 		return EINVAL;
 	
-	inetping6_sdu_t sdu;
-	
-	uint16_t src_af = inet_addr_get(&dgram->src, NULL, &sdu.src);
-	uint16_t dest_af = inet_addr_get(&dgram->dest, NULL, &sdu.dest);
-	
-	if ((src_af != dest_af) || (src_af != AF_INET6))
-		return EINVAL;
+	inetping_sdu_t sdu;
+	
+	sdu.src = dgram->src;
+	sdu.dest = dgram->dest;
 	
 	icmpv6_message_t *reply = (icmpv6_message_t *) dgram->data;
@@ -131,5 +129,5 @@
 	uint16_t ident = uint16_t_be2host(reply->un.echo.ident);
 	
-	return inetping6_recv(ident, &sdu);
+	return inetping_recv(ident, &sdu);
 }
 
@@ -159,5 +157,5 @@
 }
 
-int icmpv6_ping_send(uint16_t ident, inetping6_sdu_t *sdu)
+int icmpv6_ping_send(uint16_t ident, inetping_sdu_t *sdu)
 {
 	size_t rsize = sizeof(icmpv6_message_t) + sdu->size;
@@ -178,6 +176,7 @@
 	inet_dgram_t dgram;
 	
-	inet_addr_set6(sdu->src, &dgram.src);
-	inet_addr_set6(sdu->dest, &dgram.dest);
+	dgram.src = sdu->src;
+	dgram.dest = sdu->dest;
+	dgram.iplink = 0;
 	dgram.tos = 0;
 	dgram.data = rdata;
@@ -186,6 +185,9 @@
 	icmpv6_phdr_t phdr;
 	
-	host2addr128_t_be(sdu->src, phdr.src_addr);
-	host2addr128_t_be(sdu->dest, phdr.dest_addr);
+	assert(sdu->src.version == ip_v6);
+	assert(sdu->dest.version == ip_v6);
+	
+	host2addr128_t_be(sdu->src.addr6, phdr.src_addr);
+	host2addr128_t_be(sdu->dest.addr6, phdr.dest_addr);
 	phdr.length = host2uint32_t_be(dgram.size);
 	memset(phdr.zeroes, 0, 3);
Index: uspace/srv/net/inetsrv/icmpv6.h
===================================================================
--- uspace/srv/net/inetsrv/icmpv6.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/icmpv6.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,8 +38,9 @@
 #define ICMPV6_H_
 
+#include <types/inetping.h>
 #include "inetsrv.h"
 
 extern int icmpv6_recv(inet_dgram_t *);
-extern int icmpv6_ping_send(uint16_t, inetping6_sdu_t *);
+extern int icmpv6_ping_send(uint16_t, inetping_sdu_t *);
 
 #endif
Index: uspace/srv/net/inetsrv/inet_link.c
===================================================================
--- uspace/srv/net/inetsrv/inet_link.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/inet_link.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -43,5 +43,4 @@
 #include <stdlib.h>
 #include <str.h>
-#include <net/socket_codes.h>
 #include "addrobj.h"
 #include "inetsrv.h"
@@ -55,6 +54,6 @@
 static uint16_t ip_ident = 0;
 
-static int inet_link_open(service_id_t);
-static int inet_iplink_recv(iplink_t *, iplink_recv_sdu_t *, uint16_t);
+static int inet_iplink_recv(iplink_t *, iplink_recv_sdu_t *, ip_ver_t);
+static inet_link_t *inet_link_get_by_id_locked(sysarg_t);
 
 static iplink_ev_ops_t inet_iplink_ev_ops = {
@@ -62,6 +61,6 @@
 };
 
-static LIST_INITIALIZE(inet_link_list);
-static FIBRIL_MUTEX_INITIALIZE(inet_discovery_lock);
+static LIST_INITIALIZE(inet_links);
+static FIBRIL_MUTEX_INITIALIZE(inet_links_lock);
 
 static addr128_t link_local_node_ip =
@@ -81,5 +80,5 @@
 }
 
-static int inet_iplink_recv(iplink_t *iplink, iplink_recv_sdu_t *sdu, uint16_t af)
+static int inet_iplink_recv(iplink_t *iplink, iplink_recv_sdu_t *sdu, ip_ver_t ver)
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_iplink_recv()");
@@ -88,13 +87,13 @@
 	inet_packet_t packet;
 	
-	switch (af) {
-	case AF_INET:
+	switch (ver) {
+	case ip_v4:
 		rc = inet_pdu_decode(sdu->data, sdu->size, &packet);
 		break;
-	case AF_INET6:
+	case ip_v6:
 		rc = inet_pdu_decode6(sdu->data, sdu->size, &packet);
 		break;
 	default:
-		log_msg(LOG_DEFAULT, LVL_DEBUG, "invalid address family");
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "invalid IP version");
 		return EINVAL;
 	}
@@ -113,53 +112,4 @@
 }
 
-static int inet_link_check_new(void)
-{
-	bool already_known;
-	category_id_t iplink_cat;
-	service_id_t *svcs;
-	size_t count, i;
-	int rc;
-
-	fibril_mutex_lock(&inet_discovery_lock);
-
-	rc = loc_category_get_id("iplink", &iplink_cat, IPC_FLAG_BLOCKING);
-	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed resolving category 'iplink'.");
-		fibril_mutex_unlock(&inet_discovery_lock);
-		return ENOENT;
-	}
-
-	rc = loc_category_get_svcs(iplink_cat, &svcs, &count);
-	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting list of IP links.");
-		fibril_mutex_unlock(&inet_discovery_lock);
-		return EIO;
-	}
-
-	for (i = 0; i < count; i++) {
-		already_known = false;
-
-		list_foreach(inet_link_list, ilink_link) {
-			inet_link_t *ilink = list_get_instance(ilink_link,
-			    inet_link_t, link_list);
-			if (ilink->svc_id == svcs[i]) {
-				already_known = true;
-				break;
-			}
-		}
-
-		if (!already_known) {
-			log_msg(LOG_DEFAULT, LVL_DEBUG, "Found IP link '%lu'",
-			    (unsigned long) svcs[i]);
-			rc = inet_link_open(svcs[i]);
-			if (rc != EOK)
-				log_msg(LOG_DEFAULT, LVL_ERROR, "Could not open IP link.");
-		}
-	}
-
-	fibril_mutex_unlock(&inet_discovery_lock);
-	return EOK;
-}
-
 static inet_link_t *inet_link_new(void)
 {
@@ -185,5 +135,5 @@
 }
 
-static int inet_link_open(service_id_t sid)
+int inet_link_open(service_id_t sid)
 {
 	inet_link_t *ilink;
@@ -233,8 +183,21 @@
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "Opened IP link '%s'", ilink->svc_name);
-	list_append(&ilink->link_list, &inet_link_list);
+
+	fibril_mutex_lock(&inet_links_lock);
+
+	if (inet_link_get_by_id_locked(sid) != NULL) {
+		fibril_mutex_unlock(&inet_links_lock);
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Link %zu already open",
+		    sid);
+		rc = EEXIST;
+		goto error;
+	}
+
+	list_append(&ilink->link_list, &inet_links);
+	fibril_mutex_unlock(&inet_links_lock);
 
 	inet_addrobj_t *addr = NULL;
 	
+	/* XXX FIXME Cannot rely on loopback being the first IP link service!! */
 	if (first_link) {
 		addr = inet_addrobj_new();
@@ -242,13 +205,4 @@
 		inet_naddr(&addr->naddr, 127, 0, 0, 1, 24);
 		first_link = false;
-	} else {
-		/*
-		 * FIXME
-		 * Setting static IPv4 address for testing purposes:
-		 * 10.0.2.15/24
-		 */
-		addr = inet_addrobj_new();
-		
-		inet_naddr(&addr->naddr, 10, 0, 2, 15, 24);
 	}
 	
@@ -309,4 +263,5 @@
 	}
 	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "Configured link '%s'.", ilink->svc_name);
 	return EOK;
 	
@@ -317,23 +272,4 @@
 	inet_link_delete(ilink);
 	return rc;
-}
-
-static void inet_link_cat_change_cb(void)
-{
-	(void) inet_link_check_new();
-}
-
-int inet_link_discovery_start(void)
-{
-	int rc;
-
-	rc = loc_register_cat_change_cb(inet_link_cat_change_cb);
-	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering callback for IP link "
-		    "discovery (%d).", rc);
-		return rc;
-	}
-
-	return inet_link_check_new();
 }
 
@@ -357,11 +293,11 @@
 {
 	addr32_t src_v4;
-	uint16_t src_af = inet_addr_get(&dgram->src, &src_v4, NULL);
-	if (src_af != AF_INET)
+	ip_ver_t src_ver = inet_addr_get(&dgram->src, &src_v4, NULL);
+	if (src_ver != ip_v4)
 		return EINVAL;
 	
 	addr32_t dest_v4;
-	uint16_t dest_af = inet_addr_get(&dgram->dest, &dest_v4, NULL);
-	if (dest_af != AF_INET)
+	ip_ver_t dest_ver = inet_addr_get(&dgram->dest, &dest_v4, NULL);
+	if (dest_ver != ip_v4)
 		return EINVAL;
 	
@@ -432,11 +368,11 @@
 {
 	addr128_t src_v6;
-	uint16_t src_af = inet_addr_get(&dgram->src, NULL, &src_v6);
-	if (src_af != AF_INET6)
+	ip_ver_t src_ver = inet_addr_get(&dgram->src, NULL, &src_v6);
+	if (src_ver != ip_v6)
 		return EINVAL;
 	
 	addr128_t dest_v6;
-	uint16_t dest_af = inet_addr_get(&dgram->dest, NULL, &dest_v6);
-	if (dest_af != AF_INET6)
+	ip_ver_t dest_ver = inet_addr_get(&dgram->dest, NULL, &dest_v6);
+	if (dest_ver != ip_v6)
 		return EINVAL;
 	
@@ -488,20 +424,55 @@
 }
 
+static inet_link_t *inet_link_get_by_id_locked(sysarg_t link_id)
+{
+	assert(fibril_mutex_is_locked(&inet_links_lock));
+
+	list_foreach(inet_links, link_list, inet_link_t, ilink) {
+		if (ilink->svc_id == link_id)
+			return ilink;
+	}
+
+	return NULL;
+}
+
 inet_link_t *inet_link_get_by_id(sysarg_t link_id)
 {
-	fibril_mutex_lock(&inet_discovery_lock);
-
-	list_foreach(inet_link_list, elem) {
-		inet_link_t *ilink = list_get_instance(elem, inet_link_t,
-		    link_list);
-
-		if (ilink->svc_id == link_id) {
-			fibril_mutex_unlock(&inet_discovery_lock);
-			return ilink;
-		}
-	}
-
-	fibril_mutex_unlock(&inet_discovery_lock);
-	return NULL;
+	inet_link_t *ilink;
+
+	fibril_mutex_lock(&inet_links_lock);
+	ilink = inet_link_get_by_id_locked(link_id);
+	fibril_mutex_unlock(&inet_links_lock);
+
+	return ilink;
+}
+
+/** Get IDs of all links. */
+int inet_link_get_id_list(sysarg_t **rid_list, size_t *rcount)
+{
+	sysarg_t *id_list;
+	size_t count, i;
+
+	fibril_mutex_lock(&inet_links_lock);
+	count = list_count(&inet_links);
+
+	id_list = calloc(count, sizeof(sysarg_t));
+	if (id_list == NULL) {
+		fibril_mutex_unlock(&inet_links_lock);
+		return ENOMEM;
+	}
+
+	i = 0;
+	list_foreach(inet_links, link_list, inet_link_t, ilink) {
+		id_list[i++] = ilink->svc_id;
+		log_msg(LOG_DEFAULT, LVL_NOTE, "add link to list");
+	}
+
+	fibril_mutex_unlock(&inet_links_lock);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "return %zu links", count);
+	*rid_list = id_list;
+	*rcount = count;
+
+	return EOK;
 }
 
Index: uspace/srv/net/inetsrv/inet_link.h
===================================================================
--- uspace/srv/net/inetsrv/inet_link.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/inet_link.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -41,5 +41,5 @@
 #include "inetsrv.h"
 
-extern int inet_link_discovery_start(void);
+extern int inet_link_open(service_id_t);
 extern int inet_link_send_dgram(inet_link_t *, addr32_t,
     addr32_t, inet_dgram_t *, uint8_t, uint8_t, int);
@@ -47,4 +47,5 @@
     uint8_t, uint8_t, int);
 extern inet_link_t *inet_link_get_by_id(sysarg_t);
+extern int inet_link_get_id_list(sysarg_t **, size_t *);
 
 #endif
Index: uspace/srv/net/inetsrv/inetcfg.c
===================================================================
--- uspace/srv/net/inetsrv/inetcfg.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/inetcfg.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
  * All rights reserved.
  *
@@ -44,4 +44,5 @@
 #include <str.h>
 #include <sys/types.h>
+#include <types/inetcfg.h>
 
 #include "addrobj.h"
@@ -151,5 +152,5 @@
 static int inetcfg_get_link_list(sysarg_t **addrs, size_t *count)
 {
-	return ENOTSUP;
+	return inet_link_get_id_list(addrs, count);
 }
 
@@ -157,4 +158,9 @@
 {
 	return inet_sroute_get_id_list(sroutes, count);
+}
+
+static int inetcfg_link_add(sysarg_t link_id)
+{
+	return inet_link_open(link_id);
 }
 
@@ -170,5 +176,16 @@
 	linfo->name = str_dup(ilink->svc_name);
 	linfo->def_mtu = ilink->def_mtu;
+	if (ilink->mac_valid) {
+		addr48(ilink->mac, linfo->mac_addr);
+	} else {
+		memset(linfo->mac_addr, 0, sizeof(linfo->mac_addr));
+	}
+
 	return EOK;
+}
+
+static int inetcfg_link_remove(sysarg_t link_id)
+{
+	return ENOTSUP;
 }
 
@@ -411,4 +428,5 @@
 {
 	ipc_callid_t rcallid;
+	size_t count;
 	size_t max_size;
 	size_t act_size;
@@ -417,5 +435,5 @@
 	int rc;
 
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "inetcfg_get_link_list_srv()");
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "inetcfg_get_addr_list_srv()");
 
 	if (!async_data_read_receive(&rcallid, &max_size)) {
@@ -425,5 +443,5 @@
 	}
 
-	rc = inetcfg_get_link_list(&id_buf, &act_size);
+	rc = inetcfg_get_link_list(&id_buf, &count);
 	if (rc != EOK) {
 		async_answer_0(rcallid, rc);
@@ -432,4 +450,5 @@
 	}
 
+	act_size = count * sizeof(sysarg_t);
 	size = min(act_size, max_size);
 
@@ -474,8 +493,23 @@
 }
 
+static void inetcfg_link_add_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	sysarg_t link_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "inetcfg_link_add_srv()");
+
+	link_id = IPC_GET_ARG1(*call);
+
+	rc = inetcfg_link_add(link_id);
+	async_answer_0(callid, rc);
+}
+
 static void inetcfg_link_get_srv(ipc_callid_t callid, ipc_call_t *call)
 {
-	ipc_callid_t rcallid;
-	size_t max_size;
+	ipc_callid_t name_callid;
+	ipc_callid_t laddr_callid;
+	size_t name_max_size;
+	size_t laddr_max_size;
 
 	sysarg_t link_id;
@@ -488,6 +522,12 @@
 	linfo.name = NULL;
 
-	if (!async_data_read_receive(&rcallid, &max_size)) {
-		async_answer_0(rcallid, EREFUSED);
+	if (!async_data_read_receive(&name_callid, &name_max_size)) {
+		async_answer_0(name_callid, EREFUSED);
+		async_answer_0(callid, EREFUSED);
+		return;
+	}
+
+	if (!async_data_read_receive(&laddr_callid, &laddr_max_size)) {
+		async_answer_0(name_callid, EREFUSED);
 		async_answer_0(callid, EREFUSED);
 		return;
@@ -496,14 +536,38 @@
 	rc = inetcfg_link_get(link_id, &linfo);
 	if (rc != EOK) {
-		async_answer_0(rcallid, rc);
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	sysarg_t retval = async_data_read_finalize(rcallid, linfo.name,
-	    min(max_size, str_size(linfo.name)));
+		async_answer_0(laddr_callid, rc);
+		async_answer_0(name_callid, rc);
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	sysarg_t retval = async_data_read_finalize(name_callid, linfo.name,
+	    min(name_max_size, str_size(linfo.name)));
+	if (retval != EOK) {
+		free(linfo.name);
+		async_answer_0(laddr_callid, retval);
+		async_answer_0(callid, retval);
+		return;
+	}
+
+	retval = async_data_read_finalize(laddr_callid, &linfo.mac_addr,
+	    min(laddr_max_size, sizeof(linfo.mac_addr)));
+
 	free(linfo.name);
 
 	async_answer_1(callid, retval, linfo.def_mtu);
+}
+
+static void inetcfg_link_remove_srv(ipc_callid_t callid, ipc_call_t *call)
+{
+	sysarg_t link_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "inetcfg_link_remove_srv()");
+
+	link_id = IPC_GET_ARG1(*call);
+
+	rc = inetcfg_link_remove(link_id);
+	async_answer_0(callid, rc);
 }
 
@@ -686,4 +750,5 @@
 		sysarg_t method = IPC_GET_IMETHOD(call);
 
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "method %d", (int)method);
 		if (!method) {
 			/* The other side has hung up */
@@ -714,6 +779,12 @@
 			inetcfg_get_sroute_list_srv(callid, &call);
 			break;
+		case INETCFG_LINK_ADD:
+			inetcfg_link_add_srv(callid, &call);
+			break;
 		case INETCFG_LINK_GET:
 			inetcfg_link_get_srv(callid, &call);
+			break;
+		case INETCFG_LINK_REMOVE:
+			inetcfg_link_remove_srv(callid, &call);
 			break;
 		case INETCFG_SROUTE_CREATE:
Index: uspace/srv/net/inetsrv/inetping.c
===================================================================
--- uspace/srv/net/inetsrv/inetping.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/inetping.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1,4 +1,5 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
+ * Copyright (c) 2013 Martin Decky
  * All rights reserved.
  *
@@ -43,6 +44,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
-#include <net/socket_codes.h>
+#include <types/inetping.h>
 #include "icmp.h"
+#include "icmpv6.h"
 #include "icmp_std.h"
 #include "inetsrv.h"
@@ -57,23 +59,21 @@
 static int inetping_send(inetping_client_t *client, inetping_sdu_t *sdu)
 {
-	return icmp_ping_send(client->ident, sdu);
-}
-
-static int inetping_get_srcaddr(inetping_client_t *client, addr32_t remote,
-    addr32_t *local)
-{
-	inet_addr_t remote_addr;
-	inet_addr_set(remote, &remote_addr);
-	
-	inet_addr_t local_addr;
-	int rc = inet_get_srcaddr(&remote_addr, ICMP_TOS, &local_addr);
-	if (rc != EOK)
-		return rc;
-	
-	uint16_t family = inet_addr_get(&local_addr, local, NULL);
-	if (family != AF_INET)
+	if (sdu->src.version != sdu->dest.version)
 		return EINVAL;
-	
-	return EOK;
+
+	switch (sdu->src.version) {
+	case ip_v4:
+		return icmp_ping_send(client->ident, sdu);
+	case ip_v6:
+		return icmpv6_ping_send(client->ident, sdu);
+	default:
+		return EINVAL;
+	}
+}
+
+static int inetping_get_srcaddr(inetping_client_t *client,
+    inet_addr_t *remote, inet_addr_t *local)
+{
+	return inet_get_srcaddr(remote, ICMP_TOS, local);
 }
 
@@ -81,9 +81,6 @@
 {
 	fibril_mutex_lock(&client_list_lock);
-	
-	list_foreach(client_list, link) {
-		inetping_client_t *client = list_get_instance(link,
-		    inetping_client_t, client_list);
-		
+
+	list_foreach(client_list, client_list, inetping_client_t, client) {
 		if (client->ident == ident) {
 			fibril_mutex_unlock(&client_list_lock);
@@ -91,5 +88,5 @@
 		}
 	}
-	
+
 	fibril_mutex_unlock(&client_list_lock);
 	return NULL;
@@ -103,60 +100,155 @@
 		return ENOENT;
 	}
-	
+
 	async_exch_t *exch = async_exchange_begin(client->sess);
-	
+
 	ipc_call_t answer;
-	aid_t req = async_send_3(exch, INETPING_EV_RECV, (sysarg_t) sdu->src,
-	    (sysarg_t) sdu->dest, sdu->seq_no, &answer);
-	int rc = async_data_write_start(exch, sdu->data, sdu->size);
-	
-	async_exchange_end(exch);
-	
-	if (rc != EOK) {
+	aid_t req = async_send_1(exch, INETPING_EV_RECV, sdu->seq_no, &answer);
+
+	int rc = async_data_write_start(exch, &sdu->src, sizeof(sdu->src));
+	if (rc != EOK) {
+		async_exchange_end(exch);
 		async_forget(req);
 		return rc;
 	}
-	
+
+	rc = async_data_write_start(exch, &sdu->dest, sizeof(sdu->dest));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_write_start(exch, sdu->data, sdu->size);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
 	sysarg_t retval;
 	async_wait_for(req, &retval);
-	
+
 	return (int) retval;
 }
 
-static void inetping_send_srv(inetping_client_t *client, ipc_callid_t callid,
-    ipc_call_t *call)
-{
+static void inetping_send_srv(inetping_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "inetping_send_srv()");
+
 	inetping_sdu_t sdu;
 	int rc;
 
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "inetping_send_srv()");
+	sdu.seq_no = IPC_GET_ARG1(*icall);
+
+	ipc_callid_t callid;
+	size_t size;
+	if (!async_data_write_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(sdu.src)) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	rc = async_data_write_finalize(callid, &sdu.src, size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+
+	if (!async_data_write_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(sdu.dest)) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	rc = async_data_write_finalize(callid, &sdu.dest, size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
 
 	rc = async_data_write_accept((void **) &sdu.data, false, 0, 0, 0,
 	    &sdu.size);
 	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		return;
-	}
-
-	sdu.src = IPC_GET_ARG1(*call);
-	sdu.dest = IPC_GET_ARG2(*call);
-	sdu.seq_no = IPC_GET_ARG3(*call);
+		async_answer_0(iid, rc);
+		return;
+	}
 
 	rc = inetping_send(client, &sdu);
 	free(sdu.data);
 
-	async_answer_0(callid, rc);
+	async_answer_0(iid, rc);
 }
 
 static void inetping_get_srcaddr_srv(inetping_client_t *client,
-    ipc_callid_t callid, ipc_call_t *call)
+    ipc_callid_t iid, ipc_call_t *icall)
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "inetping_get_srcaddr_srv()");
-	
-	uint32_t remote = IPC_GET_ARG1(*call);
-	uint32_t local = 0;
-	
-	int rc = inetping_get_srcaddr(client, remote, &local);
-	async_answer_1(callid, rc, (sysarg_t) local);
+
+	ipc_callid_t callid;
+	size_t size;
+
+	inet_addr_t local;
+	inet_addr_t remote;
+
+	if (!async_data_write_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(remote)) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	int rc = async_data_write_finalize(callid, &remote, size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+
+	rc = inetping_get_srcaddr(client, &remote, &local);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(local)) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	rc = async_data_read_finalize(callid, &local, size);
+	if (rc != EOK)
+		async_answer_0(callid, rc);
+
+	async_answer_0(iid, rc);
 }
 
@@ -166,13 +258,13 @@
 	if (sess == NULL)
 		return ENOMEM;
-	
+
 	client->sess = sess;
 	link_initialize(&client->client_list);
-	
+
 	fibril_mutex_lock(&client_list_lock);
 	client->ident = ++inetping_ident;
 	list_append(&client->client_list, &client_list);
 	fibril_mutex_unlock(&client_list_lock);
-	
+
 	return EOK;
 }
@@ -182,5 +274,5 @@
 	async_hangup(client->sess);
 	client->sess = NULL;
-	
+
 	fibril_mutex_lock(&client_list_lock);
 	list_remove(&client->client_list);
@@ -191,18 +283,18 @@
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "inetping_conn()");
-	
+
 	/* Accept the connection */
 	async_answer_0(iid, EOK);
-	
+
 	inetping_client_t client;
 	int rc = inetping_client_init(&client);
 	if (rc != EOK)
 		return;
-	
+
 	while (true) {
 		ipc_call_t call;
 		ipc_callid_t callid = async_get_call(&call);
 		sysarg_t method = IPC_GET_IMETHOD(call);
-		
+
 		if (!method) {
 			/* The other side has hung up */
@@ -210,5 +302,5 @@
 			break;
 		}
-		
+
 		switch (method) {
 		case INETPING_SEND:
@@ -222,5 +314,5 @@
 		}
 	}
-	
+
 	inetping_client_fini(&client);
 }
Index: uspace/srv/net/inetsrv/inetping.h
===================================================================
--- uspace/srv/net/inetsrv/inetping.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/inetping.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,4 +38,5 @@
 #define INETPING_H_
 
+#include <types/inetping.h>
 #include "inetsrv.h"
 
Index: pace/srv/net/inetsrv/inetping6.c
===================================================================
--- uspace/srv/net/inetsrv/inetping6.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ 	(revision )
@@ -1,321 +1,0 @@
-/*
- * Copyright (c) 2013 Martin Decky
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup inet
- * @{
- */
-/**
- * @file
- * @brief
- */
-
-#include <async.h>
-#include <errno.h>
-#include <fibril_synch.h>
-#include <io/log.h>
-#include <ipc/inet.h>
-#include <loc.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <net/socket_codes.h>
-#include "icmpv6.h"
-#include "icmpv6_std.h"
-#include "inetsrv.h"
-#include "inetping6.h"
-
-static FIBRIL_MUTEX_INITIALIZE(client_list_lock);
-static LIST_INITIALIZE(client_list);
-
-/** Last used session identifier. Protected by @c client_list_lock */
-static uint16_t inetping6_ident = 0;
-
-static int inetping6_send(inetping6_client_t *client, inetping6_sdu_t *sdu)
-{
-	return icmpv6_ping_send(client->ident, sdu);
-}
-
-static int inetping6_get_srcaddr(inetping6_client_t *client, addr128_t remote,
-    addr128_t *local)
-{
-	inet_addr_t remote_addr;
-	inet_addr_set6(remote, &remote_addr);
-	
-	inet_addr_t local_addr;
-	int rc = inet_get_srcaddr(&remote_addr, ICMPV6_TOS, &local_addr);
-	if (rc != EOK)
-		return rc;
-	
-	uint16_t family = inet_addr_get(&local_addr, NULL, local);
-	if (family != AF_INET6)
-		return EINVAL;
-	
-	return EOK;
-}
-
-static inetping6_client_t *inetping6_client_find(uint16_t ident)
-{
-	fibril_mutex_lock(&client_list_lock);
-	
-	list_foreach(client_list, link) {
-		inetping6_client_t *client = list_get_instance(link,
-		    inetping6_client_t, client_list);
-		
-		if (client->ident == ident) {
-			fibril_mutex_unlock(&client_list_lock);
-			return client;
-		}
-	}
-	
-	fibril_mutex_unlock(&client_list_lock);
-	return NULL;
-}
-
-int inetping6_recv(uint16_t ident, inetping6_sdu_t *sdu)
-{
-	inetping6_client_t *client = inetping6_client_find(ident);
-	if (client == NULL) {
-		log_msg(LOG_DEFAULT, LVL_DEBUG, "Unknown ICMP ident. Dropping.");
-		return ENOENT;
-	}
-	
-	async_exch_t *exch = async_exchange_begin(client->sess);
-	
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, INETPING6_EV_RECV, sdu->seq_no, &answer);
-	
-	int rc = async_data_write_start(exch, sdu->src, sizeof(addr128_t));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-	
-	rc = async_data_write_start(exch, sdu->dest, sizeof(addr128_t));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-	
-	rc = async_data_write_start(exch, sdu->data, sdu->size);
-	
-	async_exchange_end(exch);
-	
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-	
-	sysarg_t retval;
-	async_wait_for(req, &retval);
-	
-	return (int) retval;
-}
-
-static void inetping6_send_srv(inetping6_client_t *client, ipc_callid_t iid,
-    ipc_call_t *icall)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "inetping6_send_srv()");
-	
-	inetping6_sdu_t sdu;
-	
-	sdu.seq_no = IPC_GET_ARG1(*icall);
-	
-	ipc_callid_t callid;
-	size_t size;
-	if (!async_data_write_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	if (size != sizeof(addr128_t)) {
-		async_answer_0(callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	int rc = async_data_write_finalize(callid, &sdu.src, size);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	if (!async_data_write_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	if (size != sizeof(addr128_t)) {
-		async_answer_0(callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	rc = async_data_write_finalize(callid, &sdu.dest, size);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	rc = async_data_write_accept((void **) &sdu.data, false, 0, 0, 0,
-	    &sdu.size);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	rc = inetping6_send(client, &sdu);
-	free(sdu.data);
-	
-	async_answer_0(iid, rc);
-}
-
-static void inetping6_get_srcaddr_srv(inetping6_client_t *client,
-    ipc_callid_t iid, ipc_call_t *icall)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "inetping6_get_srcaddr_srv()");
-	
-	ipc_callid_t callid;
-	size_t size;
-	if (!async_data_write_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	if (size != sizeof(addr128_t)) {
-		async_answer_0(callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	addr128_t remote;
-	int rc = async_data_write_finalize(callid, &remote, size);
-	if (rc != EOK) {
-		async_answer_0(callid, rc);
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	addr128_t local;
-	rc = inetping6_get_srcaddr(client, remote, &local);
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EREFUSED);
-		async_answer_0(iid, EREFUSED);
-		return;
-	}
-	
-	if (size != sizeof(addr128_t)) {
-		async_answer_0(callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	rc = async_data_read_finalize(callid, &local, size);
-	if (rc != EOK)
-		async_answer_0(callid, rc);
-	
-	async_answer_0(iid, rc);
-}
-
-static int inetping6_client_init(inetping6_client_t *client)
-{
-	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
-	if (sess == NULL)
-		return ENOMEM;
-	
-	client->sess = sess;
-	link_initialize(&client->client_list);
-	
-	fibril_mutex_lock(&client_list_lock);
-	client->ident = ++inetping6_ident;
-	list_append(&client->client_list, &client_list);
-	fibril_mutex_unlock(&client_list_lock);
-	
-	return EOK;
-}
-
-static void inetping6_client_fini(inetping6_client_t *client)
-{
-	async_hangup(client->sess);
-	client->sess = NULL;
-	
-	fibril_mutex_lock(&client_list_lock);
-	list_remove(&client->client_list);
-	fibril_mutex_unlock(&client_list_lock);
-}
-
-void inetping6_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "inetping6_conn()");
-	
-	/* Accept the connection */
-	async_answer_0(iid, EOK);
-	
-	inetping6_client_t client;
-	int rc = inetping6_client_init(&client);
-	if (rc != EOK)
-		return;
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		sysarg_t method = IPC_GET_IMETHOD(call);
-		
-		if (!method) {
-			/* The other side has hung up */
-			async_answer_0(callid, EOK);
-			break;
-		}
-		
-		switch (method) {
-		case INETPING6_SEND:
-			inetping6_send_srv(&client, callid, &call);
-			break;
-		case INETPING6_GET_SRCADDR:
-			inetping6_get_srcaddr_srv(&client, callid, &call);
-			break;
-		default:
-			async_answer_0(callid, EINVAL);
-		}
-	}
-	
-	inetping6_client_fini(&client);
-}
-
-/** @}
- */
Index: pace/srv/net/inetsrv/inetping6.h
===================================================================
--- uspace/srv/net/inetsrv/inetping6.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ 	(revision )
@@ -1,48 +1,0 @@
-/*
- * Copyright (c) 2013 Martin Decky
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup inet
- * @{
- */
-/**
- * @file
- * @brief
- */
-
-#ifndef INETPING6_H_
-#define INETPING6_H_
-
-#include "inetsrv.h"
-
-extern void inetping6_conn(ipc_callid_t, ipc_call_t *, void *);
-extern int inetping6_recv(uint16_t, inetping6_sdu_t *);
-
-#endif
-
-/** @}
- */
Index: uspace/srv/net/inetsrv/inetsrv.c
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/inetsrv.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -46,5 +46,4 @@
 #include <stdlib.h>
 #include <sys/types.h>
-#include <net/socket_codes.h>
 #include "addrobj.h"
 #include "icmp.h"
@@ -55,5 +54,4 @@
 #include "inetcfg.h"
 #include "inetping.h"
-#include "inetping6.h"
 #include "inet_link.h"
 #include "reass.h"
@@ -63,11 +61,16 @@
 
 static inet_naddr_t solicited_node_mask = {
-	.family = AF_INET6,
+	.version = ip_v6,
 	.addr6 = {0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0xff, 0, 0, 0},
 	.prefix = 104
 };
 
+static inet_addr_t broadcast4_all_hosts = {
+	.version = ip_v4,
+	.addr = 0xffffffff
+};
+
 static inet_addr_t multicast_all_nodes = {
-	.family = AF_INET6,
+	.version = ip_v6,
 	.addr6 = {0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}
 };
@@ -111,26 +114,4 @@
 		return EEXIST;
 	}
-	
-	rc = loc_service_register_with_iface(SERVICE_NAME_INETPING6, &sid,
-	    INET_PORT_PING6);
-	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service (%d).", rc);
-		return EEXIST;
-	}
-	
-	inet_sroute_t *sroute = inet_sroute_new();
-	if (sroute == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed creating default route (%d).", rc);
-		return ENOMEM;
-	}
-
-	inet_naddr(&sroute->dest, 0, 0, 0, 0, 0);
-	inet_addr(&sroute->router, 10, 0, 2, 2);
-	sroute->name = str_dup("default");
-	inet_sroute_add(sroute);
-
-	rc = inet_link_discovery_start();
-	if (rc != EOK)
-		return EEXIST;
 	
 	return EOK;
@@ -186,5 +167,27 @@
 {
 	inet_dir_t dir;
+	inet_link_t *ilink;
 	int rc;
+
+	if (dgram->iplink != 0) {
+		/* XXX TODO - IPv6 */
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "dgram directly to iplink %zu",
+		    dgram->iplink);
+		/* Send packet directly to the specified IP link */
+		ilink = inet_link_get_by_id(dgram->iplink);
+		if (ilink == 0)
+			return ENOENT;
+
+		if (dgram->src.version != ip_v4 ||
+			dgram->dest.version != ip_v4)
+			return EINVAL;
+
+		return inet_link_send_dgram(ilink, dgram->src.addr,
+		    dgram->dest.addr, dgram, proto, ttl, df);
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dgram to be routed");
+
+	/* Route packet using source/destination addresses */
 
 	rc = inet_find_dir(&dgram->src, &dgram->dest, dgram->tos, &dir);
@@ -214,4 +217,11 @@
 
 	/* Take source address from the address object */
+	if (remote->version == ip_v4 && remote->addr == 0xffffffff) {
+		/* XXX TODO - IPv6 */
+		local->version = ip_v4;
+		local->addr = 0;
+		return EOK;
+	}
+
 	inet_naddr_addr(&dir.aobj->naddr, local);
 	return EOK;
@@ -282,7 +292,8 @@
 	inet_dgram_t dgram;
 	
-	dgram.tos = IPC_GET_ARG1(*icall);
-	
-	uint8_t ttl = IPC_GET_ARG2(*icall);
+	dgram.iplink = IPC_GET_ARG1(*icall);
+	dgram.tos = IPC_GET_ARG2(*icall);
+	
+	uint8_t ttl = IPC_GET_ARG3(*icall);
 	int df = IPC_GET_ARG3(*icall);
 	
@@ -433,7 +444,4 @@
 		inetping_conn(iid, icall, arg);
 		break;
-	case INET_PORT_PING6:
-		inetping6_conn(iid, icall, arg);
-		break;
 	default:
 		async_answer_0(iid, ENOTSUP);
@@ -446,8 +454,5 @@
 	fibril_mutex_lock(&client_list_lock);
 
-	list_foreach(client_list, link) {
-		inet_client_t *client = list_get_instance(link, inet_client_t,
-		    client_list);
-
+	list_foreach(client_list, client_list, inet_client_t, client) {
 		if (client->protocol == proto) {
 			fibril_mutex_unlock(&client_list_lock);
@@ -527,5 +532,6 @@
 	if ((addr != NULL) ||
 	    (inet_naddr_compare_mask(&solicited_node_mask, &packet->dest)) ||
-	    (inet_addr_compare(&multicast_all_nodes, &packet->dest))) {
+	    (inet_addr_compare(&multicast_all_nodes, &packet->dest)) || 
+	    (inet_addr_compare(&broadcast4_all_hosts, &packet->dest))) {
 		/* Destined for one of the local addresses */
 
Index: uspace/srv/net/inetsrv/inetsrv.h
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/inetsrv.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -44,4 +44,5 @@
 #include <ipc/loc.h>
 #include <sys/types.h>
+#include <types/inet.h>
 #include <async.h>
 
@@ -73,32 +74,4 @@
 } inetping6_client_t;
 
-/** Address object info */
-typedef struct {
-	/** Network address */
-	inet_naddr_t naddr;
-	/** Link service ID */
-	sysarg_t ilink;
-	/** Address object name */
-	char *name;
-} inet_addr_info_t;
-
-/** IP link info */
-typedef struct {
-	/** Link service name */
-	char *name;
-	/** Default MTU */
-	size_t def_mtu;
-} inet_link_info_t;
-
-/** Static route info */
-typedef struct {
-	/** Destination network address */
-	inet_naddr_t dest;
-	/** Router address */
-	inet_addr_t router;
-	/** Static route name */
-	char *name;
-} inet_sroute_info_t;
-
 typedef struct {
 	/** Source address */
@@ -125,12 +98,4 @@
 	size_t size;
 } inet_packet_t;
-
-typedef struct {
-	inet_addr_t src;
-	inet_addr_t dest;
-	uint8_t tos;
-	void *data;
-	size_t size;
-} inet_dgram_t;
 
 typedef struct {
@@ -183,20 +148,4 @@
 } inet_dir_t;
 
-typedef struct {
-	uint32_t src;
-	uint32_t dest;
-	uint16_t seq_no;
-	void *data;
-	size_t size;
-} inetping_sdu_t;
-
-typedef struct {
-	addr128_t src;
-	addr128_t dest;
-	uint16_t seq_no;
-	void *data;
-	size_t size;
-} inetping6_sdu_t;
-
 extern int inet_ev_recv(inet_client_t *, inet_dgram_t *);
 extern int inet_recv_packet(inet_packet_t *);
Index: uspace/srv/net/inetsrv/ndp.c
===================================================================
--- uspace/srv/net/inetsrv/ndp.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/ndp.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -39,5 +39,4 @@
 #include <malloc.h>
 #include <io/log.h>
-#include <net/socket_codes.h>
 #include "ntrans.h"
 #include "addrobj.h"
Index: uspace/srv/net/inetsrv/ntrans.c
===================================================================
--- uspace/srv/net/inetsrv/ntrans.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/ntrans.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -56,8 +56,5 @@
 static inet_ntrans_t *ntrans_find(addr128_t ip_addr)
 {
-	list_foreach(ntrans_list, link) {
-		inet_ntrans_t *ntrans = list_get_instance(link,
-		    inet_ntrans_t, ntrans_list);
-
+	list_foreach(ntrans_list, ntrans_list, inet_ntrans_t, ntrans) {
 		if (addr128_compare(ntrans->ip_addr, ip_addr))
 			return ntrans;
Index: uspace/srv/net/inetsrv/pdu.c
===================================================================
--- uspace/srv/net/inetsrv/pdu.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/pdu.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -44,5 +44,4 @@
 #include <mem.h>
 #include <stdlib.h>
-#include <net/socket_codes.h>
 #include "inetsrv.h"
 #include "inet_std.h"
@@ -533,7 +532,7 @@
 int ndp_pdu_decode(inet_dgram_t *dgram, ndp_packet_t *ndp)
 {
-	uint16_t src_af = inet_addr_get(&dgram->src, NULL,
+	ip_ver_t src_ver = inet_addr_get(&dgram->src, NULL,
 	    &ndp->sender_proto_addr);
-	if (src_af != AF_INET6)
+	if (src_ver != ip_v6)
 		return EINVAL;
 	
Index: uspace/srv/net/inetsrv/reass.c
===================================================================
--- uspace/srv/net/inetsrv/reass.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/reass.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -129,8 +129,5 @@
 	assert(fibril_mutex_is_locked(&reass_dgram_map_lock));
 
-	list_foreach(reass_dgram_map, link) {
-		reass_dgram_t *rdg = list_get_instance(link, reass_dgram_t,
-		    map_link);
-
+	list_foreach(reass_dgram_map, map_link, reass_dgram_t, rdg) {
 		link_t *f1_link = list_first(&rdg->frags);
 		assert(f1_link != NULL);
@@ -289,6 +286,6 @@
 	size_t fragoff_limit;
 	inet_dgram_t dgram;
+	uint8_t proto;
 	reass_frag_t *frag;
-	uint8_t proto;
 
 	/*
@@ -297,9 +294,9 @@
 	 */
 	frag = NULL;
-	list_foreach(rdg->frags, link) {
-		frag = list_get_instance(link, reass_frag_t, dgram_link);
-
-		if (!frag->packet.mf)
+	list_foreach(rdg->frags, dgram_link, reass_frag_t, cfrag) {
+		if (!cfrag->packet.mf) {
+			frag = cfrag;
 			break;
+		}
 	}
 
@@ -330,20 +327,17 @@
 	size_t doffs = 0;
 
-	frag = NULL;
-	list_foreach(rdg->frags, link) {
-		frag = list_get_instance(link, reass_frag_t, dgram_link);
-
+	list_foreach(rdg->frags, dgram_link, reass_frag_t, cfrag) {
 		size_t cb, ce;
 
-		cb = max(doffs, frag->packet.offs);
-		ce = min(dgram_size, frag->packet.offs + frag->packet.size);
+		cb = max(doffs, cfrag->packet.offs);
+		ce = min(dgram_size, cfrag->packet.offs + cfrag->packet.size);
 
 		if (ce > cb) {
 			memcpy(dgram.data + cb,
-			    frag->packet.data + cb - frag->packet.offs,
+			    cfrag->packet.data + cb - cfrag->packet.offs,
 			    ce - cb);
 		}
 
-		if (!frag->packet.mf)
+		if (!cfrag->packet.mf)
 			break;
 	}
Index: uspace/srv/net/inetsrv/sroute.c
===================================================================
--- uspace/srv/net/inetsrv/sroute.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/inetsrv/sroute.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -89,5 +89,5 @@
 }
 
-/** Find address object matching address @a addr.
+/** Find static route object matching address @a addr.
  *
  * @param addr	Address
@@ -95,5 +95,5 @@
 inet_sroute_t *inet_sroute_find(inet_addr_t *addr)
 {
-	uint16_t addr_af = inet_addr_get(addr, NULL, NULL);
+	ip_ver_t addr_ver = inet_addr_get(addr, NULL, NULL);
 	
 	inet_sroute_t *best = NULL;
@@ -102,14 +102,11 @@
 	fibril_mutex_lock(&sroute_list_lock);
 	
-	list_foreach(sroute_list, link) {
-		inet_sroute_t *sroute = list_get_instance(link,
-		    inet_sroute_t, sroute_list);
-		
+	list_foreach(sroute_list, sroute_list, inet_sroute_t, sroute) {
 		uint8_t dest_bits;
-		uint16_t dest_af = inet_naddr_get(&sroute->dest, NULL, NULL,
+		ip_ver_t dest_ver = inet_naddr_get(&sroute->dest, NULL, NULL,
 		    &dest_bits);
 		
 		/* Skip comparison with different address family */
-		if (addr_af != dest_af)
+		if (addr_ver != dest_ver)
 			continue;
 		
@@ -147,8 +144,5 @@
 	fibril_mutex_lock(&sroute_list_lock);
 
-	list_foreach(sroute_list, link) {
-		inet_sroute_t *sroute = list_get_instance(link,
-		    inet_sroute_t, sroute_list);
-
+	list_foreach(sroute_list, sroute_list, inet_sroute_t, sroute) {
 		if (str_cmp(sroute->name, name) == 0) {
 			fibril_mutex_unlock(&sroute_list_lock);
@@ -176,8 +170,5 @@
 	fibril_mutex_lock(&sroute_list_lock);
 
-	list_foreach(sroute_list, link) {
-		inet_sroute_t *sroute = list_get_instance(link,
-		    inet_sroute_t, sroute_list);
-
+	list_foreach(sroute_list, sroute_list, inet_sroute_t, sroute) {
 		if (sroute->id == id) {
 			fibril_mutex_unlock(&sroute_list_lock);
@@ -207,8 +198,5 @@
 
 	i = 0;
-	list_foreach(sroute_list, link) {
-		inet_sroute_t *sroute = list_get_instance(link,
-		    inet_sroute_t, sroute_list);
-
+	list_foreach(sroute_list, sroute_list, inet_sroute_t, sroute) {
 		id_list[i++] = sroute->id;
 	}
Index: uspace/srv/net/loopip/loopip.c
===================================================================
--- uspace/srv/net/loopip/loopip.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/loopip/loopip.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -40,5 +40,4 @@
 #include <inet/iplink_srv.h>
 #include <inet/addr.h>
-#include <net/socket_codes.h>
 #include <io/log.h>
 #include <loc.h>
@@ -76,5 +75,6 @@
 	link_t link;
 	
-	uint16_t af;
+	/* XXX Version should be part of SDU */
+	ip_ver_t ver;
 	iplink_recv_sdu_t sdu;
 } rqueue_entry_t;
@@ -88,5 +88,5 @@
 		    list_get_instance(link, rqueue_entry_t, link);
 		
-		(void) iplink_ev_recv(&loopip_iplink, &rqe->sdu, rqe->af);
+		(void) iplink_ev_recv(&loopip_iplink, &rqe->sdu, rqe->ver);
 		
 		free(rqe->sdu.data);
@@ -174,5 +174,5 @@
 	 * Clone SDU
 	 */
-	rqe->af = AF_INET;
+	rqe->ver = ip_v4;
 	rqe->sdu.data = malloc(sdu->size);
 	if (rqe->sdu.data == NULL) {
@@ -203,5 +203,5 @@
 	 * Clone SDU
 	 */
-	rqe->af = AF_INET6;
+	rqe->ver = ip_v6;
 	rqe->sdu.data = malloc(sdu->size);
 	if (rqe->sdu.data == NULL) {
Index: uspace/srv/net/nconfsrv/Makefile
===================================================================
--- uspace/srv/net/nconfsrv/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/net/nconfsrv/Makefile	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,36 @@
+#
+# Copyright (c) 2013 Jiri Svoboda
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+BINARY = nconfsrv
+
+SOURCES = \
+	iplink.c \
+	nconfsrv.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/net/nconfsrv/iplink.c
===================================================================
--- uspace/srv/net/nconfsrv/iplink.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/net/nconfsrv/iplink.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,236 @@
+/*
+ * 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 nconfsrv
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#include <stdbool.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <inet/dhcp.h>
+#include <inet/inetcfg.h>
+#include <io/log.h>
+#include <loc.h>
+#include <stdlib.h>
+#include <str.h>
+
+#include "iplink.h"
+#include "nconfsrv.h"
+
+static int ncs_link_add(service_id_t);
+
+static LIST_INITIALIZE(ncs_links);
+static FIBRIL_MUTEX_INITIALIZE(ncs_links_lock);
+
+static int ncs_link_check_new(void)
+{
+	bool already_known;
+	category_id_t iplink_cat;
+	service_id_t *svcs;
+	size_t count, i;
+	int rc;
+
+	fibril_mutex_lock(&ncs_links_lock);
+
+	rc = loc_category_get_id("iplink", &iplink_cat, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed resolving category 'iplink'.");
+		fibril_mutex_unlock(&ncs_links_lock);
+		return ENOENT;
+	}
+
+	rc = loc_category_get_svcs(iplink_cat, &svcs, &count);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting list of IP links.");
+		fibril_mutex_unlock(&ncs_links_lock);
+		return EIO;
+	}
+
+	for (i = 0; i < count; i++) {
+		already_known = false;
+
+		list_foreach(ncs_links, link_list, ncs_link_t, ilink) {
+			if (ilink->svc_id == svcs[i]) {
+				already_known = true;
+				break;
+			}
+		}
+
+		if (!already_known) {
+			log_msg(LOG_DEFAULT, LVL_NOTE, "Found IP link '%lu'",
+			    (unsigned long) svcs[i]);
+			rc = ncs_link_add(svcs[i]);
+			if (rc != EOK)
+				log_msg(LOG_DEFAULT, LVL_ERROR, "Could not add IP link.");
+		}
+	}
+
+	fibril_mutex_unlock(&ncs_links_lock);
+	return EOK;
+}
+
+static ncs_link_t *ncs_link_new(void)
+{
+	ncs_link_t *nlink = calloc(1, sizeof(ncs_link_t));
+
+	if (nlink == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed allocating link structure. "
+		    "Out of memory.");
+		return NULL;
+	}
+
+	link_initialize(&nlink->link_list);
+
+	return nlink;
+}
+
+static void ncs_link_delete(ncs_link_t *nlink)
+{
+	if (nlink->svc_name != NULL)
+		free(nlink->svc_name);
+
+	free(nlink);
+}
+
+static int ncs_link_add(service_id_t sid)
+{
+	ncs_link_t *nlink;
+	int rc;
+
+	assert(fibril_mutex_is_locked(&ncs_links_lock));
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "ncs_link_add()");
+	nlink = ncs_link_new();
+	if (nlink == NULL)
+		return ENOMEM;
+
+	nlink->svc_id = sid;
+
+	rc = loc_service_get_name(sid, &nlink->svc_name);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting service name.");
+		goto error;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "Configure link %s", nlink->svc_name);
+	rc = inetcfg_link_add(sid);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed configuring link "
+		    "'%s'.\n", nlink->svc_name);
+		goto error;
+	}
+
+	if (str_lcmp(nlink->svc_name, "net/eth", str_length("net/eth")) == 0) {
+		rc = dhcp_link_add(sid);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed configuring DHCP on "
+			    " link '%s'.\n", nlink->svc_name);
+			goto error;
+		}
+	}
+
+	list_append(&nlink->link_list, &ncs_links);
+
+	return EOK;
+
+error:
+	ncs_link_delete(nlink);
+	return rc;
+}
+
+static void ncs_link_cat_change_cb(void)
+{
+	(void) ncs_link_check_new();
+}
+
+int ncs_link_discovery_start(void)
+{
+	int rc;
+
+	rc = loc_register_cat_change_cb(ncs_link_cat_change_cb);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering callback for IP link "
+		    "discovery (%d).", rc);
+		return rc;
+	}
+
+	return ncs_link_check_new();
+}
+
+ncs_link_t *ncs_link_get_by_id(sysarg_t link_id)
+{
+	fibril_mutex_lock(&ncs_links_lock);
+
+	list_foreach(ncs_links, link_list, ncs_link_t, nlink) {
+		if (nlink->svc_id == link_id) {
+			fibril_mutex_unlock(&ncs_links_lock);
+			return nlink;
+		}
+	}
+
+	fibril_mutex_unlock(&ncs_links_lock);
+	return NULL;
+}
+
+/** Get IDs of all links. */
+int ncs_link_get_id_list(sysarg_t **rid_list, size_t *rcount)
+{
+	sysarg_t *id_list;
+	size_t count, i;
+
+	fibril_mutex_lock(&ncs_links_lock);
+	count = list_count(&ncs_links);
+
+	id_list = calloc(count, sizeof(sysarg_t));
+	if (id_list == NULL) {
+		fibril_mutex_unlock(&ncs_links_lock);
+		return ENOMEM;
+	}
+
+	i = 0;
+	list_foreach(ncs_links, link_list, ncs_link_t, nlink) {
+		id_list[i++] = nlink->svc_id;
+		log_msg(LOG_DEFAULT, LVL_NOTE, "add link to list");
+	}
+
+	fibril_mutex_unlock(&ncs_links_lock);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "return %zu links", count);
+	*rid_list = id_list;
+	*rcount = count;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/net/nconfsrv/iplink.h
===================================================================
--- uspace/srv/net/nconfsrv/iplink.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/net/nconfsrv/iplink.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,50 @@
+/*
+ * 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 nconfsrv
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef NCONFSRV_IPLINK_H_
+#define NCONFSRV_IPLINK_H_
+
+#include <sys/types.h>
+#include "nconfsrv.h"
+
+extern int ncs_link_discovery_start(void);
+extern ncs_link_t *ncs_link_get_by_id(sysarg_t);
+extern int ncs_link_get_id_list(sysarg_t **, size_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/nconfsrv/nconfsrv.c
===================================================================
--- uspace/srv/net/nconfsrv/nconfsrv.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/net/nconfsrv/nconfsrv.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,128 @@
+/*
+ * 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 nconfsrv
+ * @{
+ */
+/**
+ * @file
+ * @brief Network configuration Service
+ */
+
+#include <adt/list.h>
+#include <async.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <inet/dhcp.h>
+#include <inet/inetcfg.h>
+#include <io/log.h>
+#include <ipc/inet.h>
+#include <ipc/services.h>
+#include <loc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include "iplink.h"
+#include "nconfsrv.h"
+
+#define NAME "nconfsrv"
+
+static void ncs_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg);
+
+static int ncs_init(void)
+{
+	service_id_t sid;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "ncs_init()");
+
+	rc = inetcfg_init();
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error contacting inet "
+		    "configuration service.");
+		return EIO;
+	}
+
+	rc = dhcp_init();
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error contacting dhcp "
+		    "configuration service.");
+		return EIO;
+	}
+
+	async_set_client_connection(ncs_client_conn);
+
+	rc = loc_server_register(NAME);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server (%d).", rc);
+		return EEXIST;
+	}
+
+	rc = loc_service_register(SERVICE_NAME_NETCONF, &sid);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service (%d).", rc);
+		return EEXIST;
+	}
+
+	rc = ncs_link_discovery_start();
+	if (rc != EOK)
+		return EEXIST;
+
+	return EOK;
+}
+
+static void ncs_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	async_answer_0(iid, ENOTSUP);
+}
+
+int main(int argc, char *argv[])
+{
+	int rc;
+
+	printf(NAME ": HelenOS Network configuration service\n");
+
+	if (log_init(NAME) != EOK) {
+		printf(NAME ": Failed to initialize logging.\n");
+		return 1;
+	}
+
+	rc = ncs_init();
+	if (rc != EOK)
+		return 1;
+
+	printf(NAME ": Accepting connections.\n");
+	task_retval(0);
+	async_manager();
+
+	/* Not reached */
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/srv/net/nconfsrv/nconfsrv.h
===================================================================
--- uspace/srv/net/nconfsrv/nconfsrv.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
+++ uspace/srv/net/nconfsrv/nconfsrv.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -0,0 +1,52 @@
+/*
+ * 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 inetsrv
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef INETSRV_H_
+#define INETSRV_H_
+
+#include <adt/list.h>
+#include <ipc/loc.h>
+
+typedef struct {
+	link_t link_list;
+	service_id_t svc_id;
+	char *svc_name;
+} ncs_link_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/slip/slip.c
===================================================================
--- uspace/srv/net/slip/slip.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/slip/slip.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,5 +38,4 @@
 #include <stdint.h>
 #include <loc.h>
-#include <net/socket_codes.h>
 #include <inet/addr.h>
 #include <inet/iplink_srv.h>
@@ -277,5 +276,5 @@
 
 pass:
-		rc = iplink_ev_recv(&slip_iplink, &sdu, AF_INET);
+		rc = iplink_ev_recv(&slip_iplink, &sdu, ip_v4);
 		if (rc != EOK) {
 			log_msg(LOG_DEFAULT, LVL_ERROR,
@@ -333,5 +332,5 @@
 		    "Failed to connect to service %s (ID=%d)",
 		    svcstr, (int) svcid);
-		return rc;
+		return ENOENT;
 	}
 	slip_iplink.arg = sess_out;
@@ -342,4 +341,5 @@
 		    "Failed to connect to service %s (ID=%d)",
 		    svcstr, (int) svcid);
+		rc = ENOENT;
 		goto fail;
 	}
@@ -365,4 +365,5 @@
 		log_msg(LOG_DEFAULT, LVL_ERROR,
 		    "Failed to create receive fibril.");
+		rc = ENOENT;
 		goto fail;
 	}
Index: uspace/srv/net/tcp/conn.c
===================================================================
--- uspace/srv/net/tcp/conn.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/tcp/conn.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -360,6 +360,5 @@
 	fibril_mutex_lock(&conn_list_lock);
 	
-	list_foreach(conn_list, link) {
-		tcp_conn_t *conn = list_get_instance(link, tcp_conn_t, link);
+	list_foreach(conn_list, link, tcp_conn_t, conn) {
 		tcp_sockpair_t *csp = &conn->ident;
 		
@@ -382,5 +381,5 @@
  * @param conn	Connection
  */
-static void tcp_conn_reset(tcp_conn_t *conn)
+void tcp_conn_reset(tcp_conn_t *conn)
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_reset()", conn->name);
Index: uspace/srv/net/tcp/conn.h
===================================================================
--- uspace/srv/net/tcp/conn.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/tcp/conn.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -43,4 +43,5 @@
 extern void tcp_conn_add(tcp_conn_t *);
 extern void tcp_conn_remove(tcp_conn_t *);
+extern void tcp_conn_reset(tcp_conn_t *conn);
 extern void tcp_conn_sync(tcp_conn_t *);
 extern void tcp_conn_fin_sent(tcp_conn_t *);
Index: uspace/srv/net/tcp/pdu.c
===================================================================
--- uspace/srv/net/tcp/pdu.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/tcp/pdu.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -40,5 +40,4 @@
 #include <mem.h>
 #include <stdlib.h>
-#include <net/socket_codes.h>
 #include "pdu.h"
 #include "segment.h"
@@ -145,19 +144,19 @@
 }
 
-static uint16_t tcp_phdr_setup(tcp_pdu_t *pdu, tcp_phdr_t *phdr,
+static ip_ver_t tcp_phdr_setup(tcp_pdu_t *pdu, tcp_phdr_t *phdr,
     tcp_phdr6_t *phdr6)
 {
 	addr32_t src_v4;
 	addr128_t src_v6;
-	uint16_t src_af = inet_addr_get(&pdu->src, &src_v4, &src_v6);
-	
+	uint16_t src_ver = inet_addr_get(&pdu->src, &src_v4, &src_v6);
+
 	addr32_t dest_v4;
 	addr128_t dest_v6;
-	uint16_t dest_af = inet_addr_get(&pdu->dest, &dest_v4, &dest_v6);
-	
-	assert(src_af == dest_af);
-	
-	switch (src_af) {
-	case AF_INET:
+	uint16_t dest_ver = inet_addr_get(&pdu->dest, &dest_v4, &dest_v6);
+
+	assert(src_ver == dest_ver);
+
+	switch (src_ver) {
+	case ip_v4:
 		phdr->src = host2uint32_t_be(src_v4);
 		phdr->dest = host2uint32_t_be(dest_v4);
@@ -167,5 +166,5 @@
 		    host2uint16_t_be(pdu->header_size + pdu->text_size);
 		break;
-	case AF_INET6:
+	case ip_v6:
 		host2addr128_t_be(src_v6, phdr6->src);
 		host2addr128_t_be(dest_v6, phdr6->dest);
@@ -178,6 +177,6 @@
 		assert(false);
 	}
-	
-	return src_af;
+
+	return src_ver;
 }
 
@@ -266,12 +265,12 @@
 	tcp_phdr_t phdr;
 	tcp_phdr6_t phdr6;
-	
-	uint16_t af = tcp_phdr_setup(pdu, &phdr, &phdr6);
-	switch (af) {
-	case AF_INET:
+
+	ip_ver_t ver = tcp_phdr_setup(pdu, &phdr, &phdr6);
+	switch (ver) {
+	case ip_v4:
 		cs_phdr = tcp_checksum_calc(TCP_CHECKSUM_INIT, (void *) &phdr,
 		    sizeof(tcp_phdr_t));
 		break;
-	case AF_INET6:
+	case ip_v6:
 		cs_phdr = tcp_checksum_calc(TCP_CHECKSUM_INIT, (void *) &phdr6,
 		    sizeof(tcp_phdr6_t));
@@ -280,5 +279,5 @@
 		assert(false);
 	}
-	
+
 	cs_headers = tcp_checksum_calc(cs_phdr, pdu->header, pdu->header_size);
 	return tcp_checksum_calc(cs_headers, pdu->text, pdu->text_size);
Index: uspace/srv/net/tcp/sock.c
===================================================================
--- uspace/srv/net/tcp/sock.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/tcp/sock.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -882,4 +882,5 @@
 	tcp_sockdata_t *socket;
 	tcp_error_t trc;
+	int i;
 	int rc;
 
@@ -897,4 +898,5 @@
 
 	if (socket->conn != NULL) {
+		/* Close connection */
 		trc = tcp_uc_close(socket->conn);
 		if (trc != TCP_EOK && trc != TCP_ENOTEXIST) {
@@ -905,4 +907,20 @@
 	}
 
+	if (socket->lconn != NULL) {
+		/* Close listening connections */
+		for (i = 0; i < socket->backlog; i++) {
+			tcp_uc_set_cstate_cb(socket->lconn[i]->conn, NULL, NULL);
+			trc = tcp_uc_close(socket->lconn[i]->conn);
+			if (trc != TCP_EOK && trc != TCP_ENOTEXIST) {
+				fibril_mutex_unlock(&socket->lock);
+				async_answer_0(callid, EBADF);
+				return;
+			}
+
+			free(socket->lconn[i]);
+			socket->lconn[i] = NULL;
+		}
+	}
+
 	/* Grab recv_buffer_lock because of CV wait in tcp_sock_recv_fibril() */
 	fibril_mutex_lock(&socket->recv_buffer_lock);
Index: uspace/srv/net/tcp/tcp.c
===================================================================
--- uspace/srv/net/tcp/tcp.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/tcp/tcp.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -141,4 +141,5 @@
 	    pdu->text_size);
 
+	dgram.iplink = 0;
 	dgram.src = pdu->src;
 	dgram.dest = pdu->dest;
Index: uspace/srv/net/tcp/ucall.c
===================================================================
--- uspace/srv/net/tcp/ucall.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/tcp/ucall.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -83,4 +83,5 @@
 
 	if (oflags == tcp_open_nonblock) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_open -> %p", nconn);
 		*conn = nconn;
 		return TCP_EOK;
@@ -234,18 +235,29 @@
 tcp_error_t tcp_uc_close(tcp_conn_t *conn)
 {
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_uc_close()", conn->name);
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_uc_close(%p)", conn->name,
+	    conn);
 
 	fibril_mutex_lock(&conn->lock);
 
 	if (conn->cstate == st_closed) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_close - ENOTEXIST");
 		fibril_mutex_unlock(&conn->lock);
 		return TCP_ENOTEXIST;
 	}
 
+	if (conn->cstate == st_listen || conn->cstate == st_syn_sent) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_close - listen/syn_sent");
+		tcp_conn_reset(conn);
+		tcp_conn_remove(conn);
+		return TCP_EOK;
+	}
+
 	if (conn->snd_buf_fin) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_close - ECLOSING");
 		fibril_mutex_unlock(&conn->lock);
 		return TCP_ECLOSING;
 	}
 
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_close - set snd_buf_fin");
 	conn->snd_buf_fin = true;
 	tcp_tqueue_new_data(conn);
Index: uspace/srv/net/udp/assoc.c
===================================================================
--- uspace/srv/net/udp/assoc.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/udp/assoc.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -186,4 +186,18 @@
 }
 
+/** Set IP link in association.
+ *
+ * @param assoc		Association
+ * @param iplink	IP link
+ */
+void udp_assoc_set_iplink(udp_assoc_t *assoc, service_id_t iplink)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_set_iplink(%p, %zu)",
+	    assoc, iplink);
+	fibril_mutex_lock(&assoc->lock);
+	assoc->ident.iplink = iplink;
+	fibril_mutex_unlock(&assoc->lock);
+}
+
 /** Set foreign socket in association.
  *
@@ -419,6 +433,5 @@
 	fibril_mutex_lock(&assoc_list_lock);
 	
-	list_foreach(assoc_list, link) {
-		udp_assoc_t *assoc = list_get_instance(link, udp_assoc_t, link);
+	list_foreach(assoc_list, link, udp_assoc_t, assoc) {
 		udp_sockpair_t *asp = &assoc->ident;
 		
Index: uspace/srv/net/udp/assoc.h
===================================================================
--- uspace/srv/net/udp/assoc.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/udp/assoc.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -36,4 +36,5 @@
 #define ASSOC_H
 
+#include <ipc/loc.h>
 #include <sys/types.h>
 #include "udp_type.h"
@@ -45,4 +46,5 @@
 extern void udp_assoc_addref(udp_assoc_t *);
 extern void udp_assoc_delref(udp_assoc_t *);
+extern void udp_assoc_set_iplink(udp_assoc_t *, service_id_t);
 extern void udp_assoc_set_foreign(udp_assoc_t *, udp_sock_t *);
 extern void udp_assoc_set_local(udp_assoc_t *, udp_sock_t *);
Index: uspace/srv/net/udp/pdu.c
===================================================================
--- uspace/srv/net/udp/pdu.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/udp/pdu.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -85,19 +85,19 @@
 }
 
-static uint16_t udp_phdr_setup(udp_pdu_t *pdu, udp_phdr_t *phdr,
+static ip_ver_t udp_phdr_setup(udp_pdu_t *pdu, udp_phdr_t *phdr,
     udp_phdr6_t *phdr6)
 {
 	addr32_t src_v4;
 	addr128_t src_v6;
-	uint16_t src_af = inet_addr_get(&pdu->src, &src_v4, &src_v6);
-	
+	ip_ver_t src_ver = inet_addr_get(&pdu->src, &src_v4, &src_v6);
+
 	addr32_t dest_v4;
 	addr128_t dest_v6;
-	uint16_t dest_af = inet_addr_get(&pdu->dest, &dest_v4, &dest_v6);
-	
-	assert(src_af == dest_af);
-	
-	switch (src_af) {
-	case AF_INET:
+	ip_ver_t dest_ver = inet_addr_get(&pdu->dest, &dest_v4, &dest_v6);
+
+	assert(src_ver == dest_ver);
+
+	switch (src_ver) {
+	case ip_v4:
 		phdr->src_addr = host2uint32_t_be(src_v4);
 		phdr->dest_addr = host2uint32_t_be(dest_v4);
@@ -106,5 +106,5 @@
 		phdr->udp_length = host2uint16_t_be(pdu->data_size);
 		break;
-	case AF_INET6:
+	case ip_v6:
 		host2addr128_t_be(src_v6, phdr6->src_addr);
 		host2addr128_t_be(dest_v6, phdr6->dest_addr);
@@ -116,6 +116,6 @@
 		assert(false);
 	}
-	
-	return src_af;
+
+	return src_ver;
 }
 
@@ -136,12 +136,12 @@
 	udp_phdr_t phdr;
 	udp_phdr6_t phdr6;
-	
-	uint16_t af = udp_phdr_setup(pdu, &phdr, &phdr6);
-	switch (af) {
-	case AF_INET:
+
+	ip_ver_t ver = udp_phdr_setup(pdu, &phdr, &phdr6);
+	switch (ver) {
+	case ip_v4:
 		cs_phdr = udp_checksum_calc(UDP_CHECKSUM_INIT, (void *) &phdr,
 		    sizeof(udp_phdr_t));
 		break;
-	case AF_INET6:
+	case ip_v6:
 		cs_phdr = udp_checksum_calc(UDP_CHECKSUM_INIT, (void *) &phdr6,
 		    sizeof(udp_phdr6_t));
@@ -150,5 +150,5 @@
 		assert(false);
 	}
-	
+
 	return udp_checksum_calc(cs_phdr, pdu->data, pdu->data_size);
 }
@@ -215,4 +215,5 @@
 		return ENOMEM;
 
+	npdu->iplink = sp->iplink;
 	npdu->src = sp->local.addr;
 	npdu->dest = sp->foreign.addr;
Index: uspace/srv/net/udp/sock.c
===================================================================
--- uspace/srv/net/udp/sock.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/udp/sock.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2008 Lukas Mejdrech
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
  * All rights reserved.
  *
@@ -349,5 +349,6 @@
 	fibril_mutex_lock(&socket->lock);
 	
-	if (inet_addr_is_any(&socket->assoc->ident.local.addr)) {
+	if (inet_addr_is_any(&socket->assoc->ident.local.addr) &&
+		socket->assoc->ident.iplink == 0) {
 		/* Determine local IP address */
 		inet_addr_t loc_addr;
@@ -665,7 +666,49 @@
 static void udp_sock_setsockopt(udp_client_t *client, ipc_callid_t callid, ipc_call_t call)
 {
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sock_setsockopt()");
-	async_answer_0(callid, ENOTSUP);
-}
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sock_setsockopt)");
+	log_msg(LOG_DEFAULT, LVL_DEBUG, " - async_data_write_accept");
+	
+	void *data = NULL;
+	size_t data_len;
+	int rc = async_data_write_accept(&data, false, 0, 0, 0, &data_len);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, " - failed accepting data");
+		async_answer_0(callid, rc);
+		return;
+	}
+	
+	sysarg_t opt_level = SOL_SOCKET;
+	sysarg_t opt_name = SOCKET_GET_OPT_NAME(call);
+	
+	if (opt_level != SOL_SOCKET || opt_name != SO_IPLINK ||
+	    data_len != sizeof(service_id_t)) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, " - failed opt_level/name/len");
+		log_msg(LOG_DEFAULT, LVL_DEBUG, " - failed opt_level=%d, "
+		    "opt_name=%d, data_len=%zu", (int)opt_level, (int)opt_name,
+		    data_len);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, " - call socket_cores_find");
+	
+	socket_core_t *sock_core = socket_cores_find(&client->sockets,
+	    SOCKET_GET_SOCKET_ID(call));
+	if (sock_core == NULL) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, " - failed getting sock_core");
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	udp_sockdata_t *socket =
+	    (udp_sockdata_t *) sock_core->specific_data;
+	
+	service_id_t iplink = *(service_id_t *)data;
+	udp_uc_set_iplink(socket->assoc, iplink);
+	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, " - success");
+	async_answer_0(callid, EOK);
+}
+
 
 static int udp_sock_recv_fibril(void *arg)
@@ -714,5 +757,4 @@
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sock_recv_fibril() exited loop");
-	fibril_mutex_unlock(&sock->recv_buffer_lock);
 	udp_uc_destroy(sock->assoc);
 
@@ -730,4 +772,6 @@
 	/* Accept the connection */
 	async_answer_0(iid, EOK);
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_sock_connection: begin");
 
 	client.sess = async_callback_receive(EXCHANGE_SERIALIZE);
Index: uspace/srv/net/udp/ucall.c
===================================================================
--- uspace/srv/net/udp/ucall.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/udp/ucall.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -55,4 +55,12 @@
 	*assoc = nassoc;
 	return UDP_EOK;
+}
+
+void udp_uc_set_iplink(udp_assoc_t *assoc, service_id_t iplink)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_uc_set_iplink(%p, %zu)",
+	    assoc, iplink);
+
+	udp_assoc_set_iplink(assoc, iplink);
 }
 
Index: uspace/srv/net/udp/ucall.h
===================================================================
--- uspace/srv/net/udp/ucall.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/udp/ucall.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -36,8 +36,10 @@
 #define UCALL_H
 
+#include <ipc/loc.h>
 #include <sys/types.h>
 #include "udp_type.h"
 
 extern udp_error_t udp_uc_create(udp_assoc_t **);
+extern void udp_uc_set_iplink(udp_assoc_t *, service_id_t);
 extern udp_error_t udp_uc_set_foreign(udp_assoc_t *, udp_sock_t *);
 extern udp_error_t udp_uc_set_local(udp_assoc_t *, udp_sock_t *);
Index: uspace/srv/net/udp/udp_inet.c
===================================================================
--- uspace/srv/net/udp/udp_inet.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/udp/udp_inet.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -84,4 +84,5 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_transmit_pdu()");
 
+	dgram.iplink = pdu->iplink;
 	dgram.src = pdu->src;
 	dgram.dest = pdu->dest;
Index: uspace/srv/net/udp/udp_type.h
===================================================================
--- uspace/srv/net/udp/udp_type.h	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/net/udp/udp_type.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -38,4 +38,5 @@
 #include <fibril.h>
 #include <fibril_synch.h>
+#include <ipc/loc.h>
 #include <socket_core.h>
 #include <sys/types.h>
@@ -71,4 +72,5 @@
 
 typedef struct {
+	service_id_t iplink;
 	udp_sock_t local;
 	udp_sock_t foreign;
@@ -85,9 +87,10 @@
 /** Encoded PDU */
 typedef struct {
+	/** IP link (optional) */
+	service_id_t iplink;
 	/** Source address */
 	inet_addr_t src;
 	/** Destination address */
 	inet_addr_t dest;
-	
 	/** Encoded PDU data including header */
 	void *data;
@@ -143,4 +146,6 @@
 	/** Connection */
 	udp_assoc_t *assoc;
+	/** User-configured IP link */
+	service_id_t iplink;
 	/** Receiving fibril */
 	fid_t recv_fibril;
Index: uspace/srv/ns/service.c
===================================================================
--- uspace/srv/ns/service.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/ns/service.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -106,7 +106,5 @@
 {
 loop:
-	list_foreach(pending_conn, cur) {
-		pending_conn_t *pr = list_get_instance(cur, pending_conn_t, link);
-		
+	list_foreach(pending_conn, link, pending_conn_t, pr) {
 		ht_link_t *link = hash_table_find(&service_hash_table, &pr->service);
 		if (!link)
@@ -117,5 +115,5 @@
 		    pr->arg3, 0, IPC_FF_NONE);
 		
-		list_remove(cur);
+		list_remove(&pr->link);
 		free(pr);
 		goto loop;
Index: uspace/srv/ns/task.c
===================================================================
--- uspace/srv/ns/task.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/ns/task.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -179,7 +179,5 @@
 	
 loop:
-	list_foreach(pending_wait, cur) {
-		pending_wait_t *pr = list_get_instance(cur, pending_wait_t, link);
-		
+	list_foreach(pending_wait, link, pending_wait_t, pr) {
 		ht_link_t *link = hash_table_find(&task_hash_table, &pr->id);
 		if (!link)
@@ -198,5 +196,5 @@
 		
 		hash_table_remove(&task_hash_table, &pr->id);
-		list_remove(cur);
+		list_remove(&pr->link);
 		free(pr);
 		goto loop;
Index: uspace/srv/taskmon/taskmon.c
===================================================================
--- uspace/srv/taskmon/taskmon.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/taskmon/taskmon.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2010 Jiri Svoboda
+ * Copyright (c) 2013 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,4 +41,6 @@
 #include <task.h>
 #include <event.h>
+#include <ipc/corecfg.h>
+#include <loc.h>
 #include <macros.h>
 #include <errno.h>
@@ -47,8 +49,13 @@
 #define NAME  "taskmon"
 
+static bool write_core_files;
+
+static void corecfg_client_conn(ipc_callid_t , ipc_call_t *, void *);
+
 static void fault_event(ipc_callid_t callid, ipc_call_t *call)
 {
 	const char *fname;
 	char *s_taskid;
+	char *dump_fname;
 	int rc;
 
@@ -69,22 +76,59 @@
 	fname = "/app/taskdump";
 
-#ifdef CONFIG_WRITE_CORE_FILES
-	char *dump_fname;
+	if (write_core_files) {
+		if (asprintf(&dump_fname, "/data/core%" PRIu64, taskid) < 0) {
+			printf("Memory allocation failed.\n");
+			return;
+		}
 
-	if (asprintf(&dump_fname, "/data/core%" PRIu64, taskid) < 0) {
-		printf("Memory allocation failed.\n");
-		return;
+		printf(NAME ": Executing %s -c %s -t %s\n", fname, dump_fname, s_taskid);
+		rc = task_spawnl(NULL, fname, fname, "-c", dump_fname, "-t", s_taskid,
+		    NULL);
+	} else {
+		printf(NAME ": Executing %s -t %s\n", fname, s_taskid);
+		rc = task_spawnl(NULL, fname, fname, "-t", s_taskid, NULL);
 	}
 
-	printf(NAME ": Executing %s -c %s -t %s\n", fname, dump_fname, s_taskid);
-	rc = task_spawnl(NULL, fname, fname, "-c", dump_fname, "-t", s_taskid,
-	    NULL);
-#else
-	printf(NAME ": Executing %s -t %s\n", fname, s_taskid);
-	rc = task_spawnl(NULL, fname, fname, "-t", s_taskid, NULL);
-#endif
 	if (rc != EOK) {
 		printf("%s: Error spawning %s (%s).\n", NAME, fname,
 		    str_error(rc));
+	}
+}
+
+static void corecfg_get_enable_srv(ipc_callid_t iid, ipc_call_t *icall)
+{
+	async_answer_1(iid, EOK, write_core_files);
+}
+
+static void corecfg_set_enable_srv(ipc_callid_t iid, ipc_call_t *icall)
+{
+	write_core_files = IPC_GET_ARG1(*icall);
+	async_answer_0(iid, EOK);
+}
+
+static void corecfg_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	/* Accept the connection */
+	async_answer_0(iid, EOK);
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		sysarg_t method = IPC_GET_IMETHOD(call);
+
+		if (!method) {
+			/* The other side has hung up */
+			async_answer_0(callid, EOK);
+			return;
+		}
+
+		switch (method) {
+		case CORECFG_GET_ENABLE:
+			corecfg_get_enable_srv(callid, &call);
+			break;
+		case CORECFG_SET_ENABLE:
+			corecfg_set_enable_srv(callid, &call);
+			break;
+		}
 	}
 }
@@ -94,6 +138,28 @@
 	printf("%s: Task Monitoring Service\n", NAME);
 	
+#ifdef CONFIG_WRITE_CORE_FILES
+	write_core_files = true;
+#else
+	write_core_files = false;
+#endif
 	if (event_subscribe(EVENT_FAULT, 0) != EOK) {
 		printf("%s: Error registering fault notifications.\n", NAME);
+		return -1;
+	}
+	
+	async_set_client_connection(corecfg_client_conn);
+	
+	int rc = loc_server_register(NAME);
+	if (rc != EOK) {
+		printf("%s: Failed registering server (%d).\n",
+		    NAME, rc);
+		return -1;
+	}
+	
+	service_id_t sid;
+	rc = loc_service_register(SERVICE_NAME_CORECFG, &sid);
+	if (rc != EOK) {
+		printf("%s: Failed registering service (%d).\n",
+		    NAME, rc);
 		return -1;
 	}
Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/vfs/vfs.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/vfs/vfs.h	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -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 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/vfs/vfs_ops.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -548,8 +548,5 @@
 	int found = 0;
 
-	list_foreach(mtab_list, cur) {
-		mtab_ent_t *mtab_ent = list_get_instance(cur, mtab_ent_t,
-		    link);
-
+	list_foreach(mtab_list, link, mtab_ent_t, mtab_ent) {
 		if (str_cmp(mtab_ent->mp, mp) == 0) {
 			list_remove(&mtab_ent->link);
@@ -1371,8 +1368,5 @@
 	async_answer_1(callid, EOK, mtab_size);
 
-	list_foreach(mtab_list, cur) {
-		mtab_ent_t *mtab_ent = list_get_instance(cur, mtab_ent_t,
-		    link);
-
+	list_foreach(mtab_list, link, mtab_ent_t, mtab_ent) {
 		rc = ENOTSUP;
 
@@ -1418,4 +1412,58 @@
 }
 
+void vfs_statfs(ipc_callid_t rid, ipc_call_t *request)
+{
+	char *path;
+	int rc = async_data_write_accept((void **) &path, true, 0, 0, 0, NULL);
+	if (rc != EOK) {
+		async_answer_0(rid, rc);
+		return;
+	}
+	
+	ipc_callid_t callid;
+	if (!async_data_read_receive(&callid, NULL)) {
+		free(path);
+		async_answer_0(callid, EINVAL);
+		async_answer_0(rid, EINVAL);
+		return;
+	}
+
+	vfs_lookup_res_t lr;
+	fibril_rwlock_read_lock(&namespace_rwlock);
+	rc = vfs_lookup_internal(path, L_NONE, &lr, NULL);
+	free(path);
+	if (rc != EOK) {
+		fibril_rwlock_read_unlock(&namespace_rwlock);
+		async_answer_0(callid, rc);
+		async_answer_0(rid, rc);
+		return;
+	}
+	vfs_node_t *node = vfs_node_get(&lr);
+	if (!node) {
+		fibril_rwlock_read_unlock(&namespace_rwlock);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(rid, ENOMEM);
+		return;
+	}
+
+	fibril_rwlock_read_unlock(&namespace_rwlock);
+
+	async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
+	
+	aid_t msg;
+	msg = async_send_3(exch, VFS_OUT_STATFS, node->service_id,
+	    node->index, false, NULL);
+	async_forward_fast(callid, exch, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
+	
+	vfs_exchange_release(exch);
+	
+	sysarg_t rv;
+	async_wait_for(msg, &rv);
+
+	async_answer_0(rid, rv);
+
+	vfs_node_put(node);
+}
+
 /**
  * @}
Index: uspace/srv/vfs/vfs_register.c
===================================================================
--- uspace/srv/vfs/vfs_register.c	(revision 820104d36ba9cd28a40e7a63571bf4eedbb7f2a1)
+++ uspace/srv/vfs/vfs_register.c	(revision c2a698383608df44fde7e7cb4047a416d8688eee)
@@ -260,7 +260,5 @@
 	fibril_mutex_lock(&fs_list_lock);
 	
-	list_foreach(fs_list, cur) {
-		fs_info_t *fs = list_get_instance(cur, fs_info_t, fs_link);
-		
+	list_foreach(fs_list, fs_link, fs_info_t, fs) {
 		if (fs->fs_handle == handle) {
 			fibril_mutex_unlock(&fs_list_lock);
@@ -305,6 +303,5 @@
 		fibril_mutex_lock(&fs_list_lock);
 	
-	list_foreach(fs_list, cur) {
-		fs_info_t *fs = list_get_instance(cur, fs_info_t, fs_link);
+	list_foreach(fs_list, fs_link, fs_info_t, fs) {
 		if (str_cmp(fs->vfs_info.name, name) == 0 &&
 		    instance == fs->vfs_info.instance) {
@@ -332,6 +329,5 @@
 	
 	fibril_mutex_lock(&fs_list_lock);
-	list_foreach(fs_list, cur) {
-		fs_info_t *fs = list_get_instance(cur, fs_info_t, fs_link);
+	list_foreach(fs_list, fs_link, fs_info_t, fs) {
 		if (fs->fs_handle == handle) { 
 			info = &fs->vfs_info;
