Index: HelenOS.config
===================================================================
--- HelenOS.config	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ HelenOS.config	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -608,9 +608,15 @@
 ! [(PLATFORM=ia32|PLATFORM=amd64|MACHINE=beagleboardxm)&CONFIG_HID_OUT!=none&CONFIG_FB=y] CONFIG_BFB_BPP (choice)
 
+% Window system
+! [CONFIG_FB=y] CONFIG_WINSYS (y/n)
+
 % Display double buffering
-! [CONFIG_FB=y] CONFIG_DISP_DOUBLE_BUF (y/n)
+! [CONFIG_WINSYS=y] CONFIG_DISP_DOUBLE_BUF (y/n)
+
+% Client-side UI rendering
+! CONFIG_UI_CS_RENDER (y/n)
 
 % Window double buffering
-! [CONFIG_FB=y] CONFIG_WIN_DOUBLE_BUF (n/y)
+! [CONFIG_UI_CS_RENDER=y] CONFIG_WIN_DOUBLE_BUF (n/y)
 
 % Start AP processors by the loader
Index: README.md
===================================================================
--- README.md	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ README.md	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -10,5 +10,5 @@
 extensible, fault tolerant and easy to understand.
 
-![screenshot](http://www.helenos.org/raw-attachment/wiki/Screenshots/screenshot.png "Screenshot")
+![screenshot](http://www.helenos.org/raw-attachment/wiki/Screenshots/newgui-aio.png "Screenshot")
 
 HelenOS aims to be compatible with the C11 and C++14 standards, but does not
@@ -35,7 +35,7 @@
 
 In order to build HelenOS, one must first build the cross-compiler toolchain
-(either as a root or by specifying the `CROSS_PREFIX` environment variable)
-by running (example for the amd64 architecture, further list of targets can be
-found in the `default` directory):
+(the default installation location can be overridden by specifying the
+`CROSS_PREFIX` environment variable) by running (example for the amd64
+architecture, further list of targets can be found in the `default` directory):
 
 ```
@@ -61,6 +61,7 @@
 ```
 # sudo dnf group install 'Development Tools'
-# sudo dnf install wget texinfo libmpc-devel mpfr-devel gmp-devel PyYAML genisoimage flex bison
+# sudo dnf install wget texinfo PyYAML genisoimage flex bison
 ```
+
 In case the toolchain script won't work no matter how hard you try, let us know.
 Please supply as many relevant information (your OS and distribution, list of
@@ -129,4 +130,20 @@
 see the files in tools/conf.
 
+## Contributing
+
+There is a whole section of our wiki devoted to
+[how to contribute to HelenOS](http://www.helenos.org/wiki/HowToContribute).
+But to highlight the most important points, you should subscribe to
+our mailing list and if you have an idea for contributing, discuss it
+with us first so that we can agree upon the design.
+
+Especially if you are a first time contributor, blindingly shooting
+a pull request may result in it being closed on the grounds that it
+does not fit well within the grand scheme of things. That would not
+be efficient use of time.
+
+Communicating early and often is the key to successful acceptance of
+your patch/PR.
+
 ## License
 
Index: boot/generic/include/gzip.h
===================================================================
--- boot/generic/include/gzip.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ boot/generic/include/gzip.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -30,6 +30,9 @@
 #define LIBCOMPRESS_GZIP_H_
 
+#include <stdbool.h>
 #include <stddef.h>
-size_t gzip_size(const void *, size_t);
+
+extern bool gzip_check(const void *, size_t);
+extern size_t gzip_size(const void *, size_t);
 extern int gzip_expand(const void *, size_t, void *, size_t);
 
Index: boot/generic/src/gzip.c
===================================================================
--- boot/generic/src/gzip.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ boot/generic/src/gzip.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -63,14 +63,22 @@
 } __attribute__((packed)) gzip_footer_t;
 
-size_t gzip_size(const void *src, size_t srclen)
+/** Check GZIP signature
+ *
+ * Checks whether the source buffer start with a GZIP signature.
+ *
+ * @param[in] src    Source data buffer.
+ * @param[in] srclen Source buffer size (bytes).
+ *
+ * @return True if GZIP signature found.
+ * @return False if no GZIP signature found.
+ *
+ */
+bool gzip_check(const void *src, size_t srclen)
 {
+	if ((srclen < (sizeof(gzip_header_t) + sizeof(gzip_footer_t))))
+		return false;
+
 	gzip_header_t header;
-	gzip_footer_t footer;
-
-	if ((srclen < sizeof(header)) || (srclen < sizeof(footer)))
-		return 0;
-
 	memcpy(&header, src, sizeof(header));
-	memcpy(&footer, src + srclen - sizeof(footer), sizeof(footer));
 
 	if ((header.id1 != GZIP_ID1) ||
@@ -78,5 +86,28 @@
 	    (header.method != GZIP_METHOD_DEFLATE) ||
 	    ((header.flags & (~GZIP_FLAGS_MASK)) != 0))
+		return false;
+
+	return true;
+}
+
+/** Get uncompressed size
+ *
+ * Note that the uncompressed size is read from the GZIP footer
+ * (and not calculated by acutally decompressing the GZip archive).
+ * Thus the source of the GZip archive needs to be trusted.
+ *
+ * @param[in]  src    Source data buffer.
+ * @param[out] srclen Source buffer size (bytes).
+ *
+ * @return Uncompressed size.
+ *
+ */
+size_t gzip_size(const void *src, size_t srclen)
+{
+	if (!gzip_check(src, srclen))
 		return 0;
+
+	gzip_footer_t footer;
+	memcpy(&footer, src + srclen - sizeof(footer), sizeof(footer));
 
 	return uint32_t_le2host(footer.size);
@@ -85,6 +116,6 @@
 /** Expand GZIP compressed data
  *
- * The routine allocates the output buffer based
- * on the size encoded in the input stream. This
+ * The routine compares the output buffer size with
+ * the size encoded in the input stream. This
  * effectively limits the size of the uncompressed
  * data to 4 GiB (expanding input streams that actually
@@ -108,20 +139,14 @@
 int gzip_expand(const void *src, size_t srclen, void *dest, size_t destlen)
 {
+	if (!gzip_check(src, srclen))
+		return EINVAL;
+
+	/* Decode header and footer */
+
 	gzip_header_t header;
+	memcpy(&header, src, sizeof(header));
+
 	gzip_footer_t footer;
-
-	if ((srclen < sizeof(header)) || (srclen < sizeof(footer)))
-		return EINVAL;
-
-	/* Decode header and footer */
-
-	memcpy(&header, src, sizeof(header));
 	memcpy(&footer, src + srclen - sizeof(footer), sizeof(footer));
-
-	if ((header.id1 != GZIP_ID1) ||
-	    (header.id2 != GZIP_ID2) ||
-	    (header.method != GZIP_METHOD_DEFLATE) ||
-	    ((header.flags & (~GZIP_FLAGS_MASK)) != 0))
-		return EINVAL;
 
 	if (destlen != uint32_t_le2host(footer.size))
Index: boot/generic/src/payload.c
===================================================================
--- boot/generic/src/payload.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ boot/generic/src/payload.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -61,12 +61,6 @@
 {
 	char *e = (char *) ext(s);
-	if (e != NULL && str_cmp(e, ".gz") == 0)
+	if ((e != NULL) && (str_cmp(e, ".gz") == 0))
 		*e = '\0';
-}
-
-static bool isgzip(const char *s)
-{
-	const char *e = ext(s);
-	return e != NULL && str_cmp(e, ".gz") == 0;
 }
 
@@ -82,17 +76,14 @@
 {
 	const char *name;
-	const uint8_t *data;
 	size_t packed_size;
-	size_t unpacked_size;
 
 	if (!tar_info(*cstart, cend, &name, &packed_size))
 		return false;
 
-	data = *cstart + TAR_BLOCK_SIZE;
+	const uint8_t *data = *cstart + TAR_BLOCK_SIZE;
 	*cstart += TAR_BLOCK_SIZE + ALIGN_UP(packed_size, TAR_BLOCK_SIZE);
 
-	bool gz = isgzip(name);
-
-	unpacked_size = gz ? gzip_size(data, packed_size) : packed_size;
+	bool gz = gzip_check(data, packed_size);
+	size_t unpacked_size = gz ? gzip_size(data, packed_size) : packed_size;
 
 	/* Components must be page-aligned. */
@@ -156,5 +147,5 @@
 	while (tar_info(start, payload_end, &name, &packed_size)) {
 		sz = ALIGN_UP(sz, PAGE_SIZE);
-		if (isgzip(name))
+		if (gzip_check(start + TAR_BLOCK_SIZE, packed_size))
 			sz += gzip_size(start + TAR_BLOCK_SIZE, packed_size);
 		else
Index: configure.sh
===================================================================
--- configure.sh	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ configure.sh	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -39,5 +39,4 @@
 CONFIG_RULES="${SOURCE_DIR}/HelenOS.config"
 CONFIG_DEFAULTS="${SOURCE_DIR}/defaults"
-
 
 test "$#" -eq 1 && { test "$1" = "-h" || test "$1" = "--help"; }
@@ -85,5 +84,5 @@
 fi
 
-if ! which meson; then
+if ! which meson >/dev/null 2>/dev/null; then
 	echo "Your system does not have Meson installed."
 	echo 'Please use `pip3 install meson`'
@@ -91,5 +90,5 @@
 fi
 
-if ! which ninja; then
+if ! which ninja >/dev/null 2>/dev/null; then
 	echo "Your system does not have ninja installed."
 	echo 'Please use `pip3 install ninja`'
@@ -98,5 +97,7 @@
 
 # Link tools directory for convenience.
-ln -s "${SOURCE_DIR}/tools" tools
+if [ ! -e tools ]; then
+	ln -s "${SOURCE_DIR}/tools" tools
+fi
 
 # Run HelenOS config tool.
@@ -122,25 +123,41 @@
 
 compname="$cc_arch-helenos-gcc"
+unset compprefix
 
-if which "$compname"; then
+if which "$compname" >/dev/null 2>/dev/null; then
 	# Compiler is in PATH
 	compprefix="$cc_arch-helenos-"
 
 elif [ -n "$CROSS_PREFIX" ]; then
-	if ! which "$CROSS_PREFIX/bin/$compname"; then
+	if which "$CROSS_PREFIX/bin/$compname" >/dev/null 2>/dev/null; then
+		compprefix="$CROSS_PREFIX/bin/$cc_arch-helenos-"
+	fi
+
+	if [ -z "$compprefix" ]; then
 		echo "ERROR: \$CROSS_PREFIX defined but $compname is not present in $CROSS_PREFIX/bin."
 		echo "Run tools/toolchain.sh to build cross-compiling toolchain."
 		exit 1
 	fi
+else
+	if [ -z "$XDG_DATA_HOME" ]; then
+			XDG_DATA_HOME="$HOME/.local/share"
+	fi
 
-	compprefix="$CROSS_PREFIX/bin/$cc_arch-helenos-"
-else
-	if ! which "/usr/local/cross/bin/$compname"; then
-		echo "ERROR: \$CROSS_PREFIX is not defined and $compname is not present in /usr/local/cross/bin."
+	if which "$XDG_DATA_HOME/HelenOS/cross/bin/$compname" >/dev/null 2>/dev/null; then
+		compprefix="$XDG_DATA_HOME/HelenOS/cross/bin/$cc_arch-helenos-"
+	elif which "/opt/HelenOS/cross/bin/$compname" >/dev/null 2>/dev/null; then
+		compprefix="/opt/HelenOS/cross/bin/$cc_arch-helenos-"
+	elif which "/usr/local/cross/bin/$compname" >/dev/null 2>/dev/null; then
+		compprefix="/usr/local/cross/bin/$cc_arch-helenos-"
+	fi
+
+	if [ -z "$compprefix" ]; then
+		echo "ERROR: \$CROSS_PREFIX is not defined and $compname is not present in any of the following standard locations."
+		echo " * $XDG_DATA_HOME/HelenOS/cross/bin"
+		echo " * /opt/HelenOS/cross/bin"
+		echo " * /usr/local/cross/bin"
 		echo "Run tools/toolchain.sh to build cross-compiling toolchain."
 		exit 1
 	fi
-
-	compprefix="/usr/local/cross/bin/$cc_arch-helenos-"
 fi
 
Index: contrib/qemu/build-from-scratch.sh
===================================================================
--- contrib/qemu/build-from-scratch.sh	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ contrib/qemu/build-from-scratch.sh	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -29,5 +29,5 @@
 #
 
-VERSION=5.1.0
+VERSION=6.0.0
 BASENAME=qemu-${VERSION}
 BASENAME_MASTER=qemu-master
Index: kernel/arch/amd64/src/amd64.c
===================================================================
--- kernel/arch/amd64/src/amd64.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/amd64/src/amd64.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -224,4 +224,6 @@
 	    ns16550_out_ptr);
 	if (ns16550_instance) {
+		ns16550_format_set(ns16550_instance, 38400,
+		    LCR_PARITY_NONE | LCR_STOP_BIT_TWO | LCR_WORD_LEN_8);
 #ifdef CONFIG_NS16550
 		srln_instance_t *srln_instance = srln_init();
Index: kernel/arch/arm32/include/arch/cp15.h
===================================================================
--- kernel/arch/arm32/include/arch/cp15.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/arm32/include/arch/cp15.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -248,5 +248,5 @@
 	SCTLR_WRITE_XN_EN_FLAG       = 1 << 19, /* Only if virt. supported */
 	SCTLR_USPCE_WRITE_XN_EN_FLAG = 1 << 20, /* Only if virt. supported */
-	SCTLR_FAST_IRQ_EN_FLAG       = 1 << 21, /* Disable impl. specific feat*/
+	SCTLR_FAST_IRQ_EN_FLAG       = 1 << 21, /* Disable impl. specific feat */
 	SCTLR_UNALIGNED_EN_FLAG      = 1 << 22, /* Must be 1 on armv7 */
 	SCTLR_EXTENDED_PT_EN_FLAG    = 1 << 23,
@@ -584,5 +584,5 @@
 CONTROL_REG_GEN_WRITE(PMCCNTR, c9, 0, c13, 0);
 
-/*c10 has tons of reserved too */
+/* c10 has tons of reserved too */
 CONTROL_REG_GEN_READ(PRRR, c10, 0, c2, 0); /* no PAE */
 CONTROL_REG_GEN_WRITE(PRRR, c10, 0, c2, 0); /* no PAE */
Index: kernel/arch/arm32/include/arch/mm/page.h
===================================================================
--- kernel/arch/arm32/include/arch/mm/page.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/arm32/include/arch/mm/page.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -159,4 +159,7 @@
 #endif
 	TTBR0_write(val);
+#if defined(PROCESSOR_ARCH_armv6) || defined(PROCESSOR_ARCH_armv7_a)
+	BPIALL_write(0);
+#endif
 }
 
Index: kernel/arch/arm32/include/arch/mm/page_armv6.h
===================================================================
--- kernel/arch/arm32/include/arch/mm/page_armv6.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/arm32/include/arch/mm/page_armv6.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -284,5 +284,5 @@
 	p->non_global = !(flags & PAGE_GLOBAL);
 
-	/* default access permission: kernel only*/
+	/* default access permission: kernel only */
 	p->access_permission_0 = PTE_AP0_USER_NO_KERNEL_FULL;
 
Index: kernel/arch/arm32/src/cpu/cpu.c
===================================================================
--- kernel/arch/arm32/src/cpu/cpu.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/arm32/src/cpu/cpu.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -170,6 +170,4 @@
 	 */
 	control_reg |= SCTLR_CACHE_EN_FLAG;
-#endif
-#ifdef PROCESSOR_ARCH_armv7_a
 	/*
 	 * ICache coherency is elaborated on in barrier.h.
Index: kernel/arch/arm32/src/fpu_context.c
===================================================================
--- kernel/arch/arm32/src/fpu_context.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/arm32/src/fpu_context.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -186,5 +186,5 @@
 	uint32_t mvfr0;
 
-	/* Enable coprocessor access*/
+	/* Enable coprocessor access */
 	fpu_enable_coprocessor_access();
 
Index: kernel/arch/arm32/src/mach/beagleboardxm/beagleboardxm.c
===================================================================
--- kernel/arch/arm32/src/mach/beagleboardxm/beagleboardxm.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/arm32/src/mach/beagleboardxm/beagleboardxm.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -158,5 +158,5 @@
 		spinlock_unlock(&irq->lock);
 	} else {
-		/* Spurious interrupt.*/
+		/* Spurious interrupt. */
 		printf("cpu%d: spurious interrupt (inum=%d)\n",
 		    CPU->id, inum);
Index: kernel/arch/arm32/src/mach/gta02/gta02.c
===================================================================
--- kernel/arch/arm32/src/mach/gta02/gta02.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/arm32/src/mach/gta02/gta02.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -148,5 +148,5 @@
 		spinlock_unlock(&irq->lock);
 	} else {
-		/* Spurious interrupt.*/
+		/* Spurious interrupt. */
 		log(LF_ARCH, LVL_DEBUG, "cpu%d: spurious interrupt (inum=%d)",
 		    CPU->id, inum);
Index: kernel/arch/arm32/src/mach/integratorcp/integratorcp.c
===================================================================
--- kernel/arch/arm32/src/mach/integratorcp/integratorcp.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/arm32/src/mach/integratorcp/integratorcp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -270,5 +270,5 @@
 				spinlock_unlock(&irq->lock);
 			} else {
-				/* Spurious interrupt.*/
+				/* Spurious interrupt. */
 				log(LF_ARCH, LVL_DEBUG,
 				    "cpu%d: spurious interrupt (inum=%d)",
Index: kernel/arch/arm32/src/mach/raspberrypi/raspberrypi.c
===================================================================
--- kernel/arch/arm32/src/mach/raspberrypi/raspberrypi.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/arm32/src/mach/raspberrypi/raspberrypi.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -161,5 +161,5 @@
 		spinlock_unlock(&irq->lock);
 	} else {
-		/* Spurious interrupt.*/
+		/* Spurious interrupt. */
 		printf("cpu%d: spurious interrupt (inum=%d)\n", CPU->id, inum);
 		bcm2835_irc_disable(raspi.irc, inum);
@@ -174,6 +174,13 @@
 {
 #ifdef CONFIG_FB
+	uint32_t width, height;
 	fb_properties_t prop;
-	if (bcm2835_fb_init(&prop)) {
+
+	if (!bcm2835_mbox_get_fb_size(&width, &height)) {
+		printf("mbox: could not get the framebuffer size\n");
+		width = 640;
+		height = 480;
+	}
+	if (bcm2835_fb_init(&prop, width, height)) {
 		outdev_t *fb_dev = fb_init(&prop);
 		if (fb_dev)
Index: kernel/arch/arm32/src/mm/page_fault.c
===================================================================
--- kernel/arch/arm32/src/mm/page_fault.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/arm32/src/mm/page_fault.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -161,14 +161,14 @@
 	} ls_inst[] = {
 		/* Store word/byte */
-		{ 0x0e100000, 0x04000000, PF_ACCESS_WRITE }, /*STR(B) imm*/
-		{ 0x0e100010, 0x06000000, PF_ACCESS_WRITE }, /*STR(B) reg*/
+		{ 0x0e100000, 0x04000000, PF_ACCESS_WRITE }, /* STR(B) imm */
+		{ 0x0e100010, 0x06000000, PF_ACCESS_WRITE }, /* STR(B) reg */
 		/* Load word/byte */
-		{ 0x0e100000, 0x04100000, PF_ACCESS_READ }, /*LDR(B) imm*/
-		{ 0x0e100010, 0x06100000, PF_ACCESS_READ }, /*LDR(B) reg*/
+		{ 0x0e100000, 0x04100000, PF_ACCESS_READ }, /* LDR(B) imm */
+		{ 0x0e100010, 0x06100000, PF_ACCESS_READ }, /* LDR(B) reg */
 		/* Store half-word/dual  A5.2.8 */
-		{ 0x0e1000b0, 0x000000b0, PF_ACCESS_WRITE }, /*STRH imm reg*/
+		{ 0x0e1000b0, 0x000000b0, PF_ACCESS_WRITE }, /* STRH imm reg */
 		/* Load half-word/dual A5.2.8 */
-		{ 0x0e0000f0, 0x000000d0, PF_ACCESS_READ }, /*LDRH imm reg*/
-		{ 0x0e1000b0, 0x001000b0, PF_ACCESS_READ }, /*LDRH imm reg*/
+		{ 0x0e0000f0, 0x000000d0, PF_ACCESS_READ }, /* LDRH imm reg */
+		{ 0x0e1000b0, 0x001000b0, PF_ACCESS_READ }, /* LDRH imm reg */
 		/* Block data transfer, Store */
 		{ 0x0e100000, 0x08000000, PF_ACCESS_WRITE }, /* STM variants */
Index: kernel/arch/arm64/src/mach/virt/virt.c
===================================================================
--- kernel/arch/arm64/src/mach/virt/virt.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/arm64/src/mach/virt/virt.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -95,5 +95,5 @@
 		irq_spinlock_unlock(&irq->lock, false);
 	} else {
-		/* Spurious interrupt.*/
+		/* Spurious interrupt. */
 		printf("cpu%d: spurious interrupt (inum=%u)\n", CPU->id, inum);
 	}
Index: kernel/arch/ia32/src/ia32.c
===================================================================
--- kernel/arch/ia32/src/ia32.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/ia32/src/ia32.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -209,4 +209,6 @@
 	    ns16550_out_ptr);
 	if (ns16550_instance) {
+		ns16550_format_set(ns16550_instance, 38400,
+		    LCR_PARITY_NONE | LCR_STOP_BIT_TWO | LCR_WORD_LEN_8);
 #ifdef CONFIG_NS16550
 		srln_instance_t *srln_instance = srln_init();
Index: kernel/arch/sparc64/include/arch/register.h
===================================================================
--- kernel/arch/sparc64/include/arch/register.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/sparc64/include/arch/register.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -69,5 +69,5 @@
 		unsigned priv : 1;	/**< Privileged Mode. */
 		unsigned ie : 1;	/**< Interrupt Enable. */
-		unsigned ag : 1;	/**< Alternate Globals*/
+		unsigned ag : 1;	/**< Alternate Globals */
 	} __attribute__((packed));
 };
Index: kernel/arch/sparc64/include/arch/sun4v/cpu.h
===================================================================
--- kernel/arch/sparc64/include/arch/sun4v/cpu.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/sparc64/include/arch/sun4v/cpu.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -54,5 +54,5 @@
 	uint64_t cpuids[MAX_CORE_STRANDS];
 	struct cpu *cpus[MAX_CORE_STRANDS];
-	atomic_t nrdy;
+	atomic_size_t nrdy;
 	SPINLOCK_DECLARE(proposed_nrdy_lock);
 } exec_unit_t;
Index: kernel/arch/sparc64/src/smp/sun4v/smp.c
===================================================================
--- kernel/arch/sparc64/src/smp/sun4v/smp.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/arch/sparc64/src/smp/sun4v/smp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -183,5 +183,5 @@
 	md_node_t node = md_get_root();
 
-	/* walk through all the CPU nodes in the MD*/
+	/* walk through all the CPU nodes in the MD */
 	while (md_next_node(&node, "cpu")) {
 
Index: kernel/genarch/include/genarch/drivers/amdm37x/gpt.h
===================================================================
--- kernel/genarch/include/genarch/drivers/amdm37x/gpt.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/genarch/include/genarch/drivers/amdm37x/gpt.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -178,5 +178,5 @@
 	const ioport32_t tcar2;
 
-	/* GPT1,2,10 only (used for 1ms time period generation)*/
+	/* GPT1,2,10 only (used for 1ms time period generation) */
 
 	/** Positive increment register */
Index: kernel/genarch/include/genarch/drivers/bcm2835/mbox.h
===================================================================
--- kernel/genarch/include/genarch/drivers/bcm2835/mbox.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/genarch/include/genarch/drivers/bcm2835/mbox.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -68,4 +68,11 @@
 
 enum {
+	MBOX_TAG_GET_PHYS_W_H	= 0x00040003,
+	MBOX_TAG_SET_PHYS_W_H	= 0x00048003,
+	MBOX_TAG_GET_VIRT_W_H	= 0x00040004,
+	MBOX_TAG_SET_VIRT_W_G	= 0x00048004
+};
+
+enum {
 	MBOX_PROP_CODE_REQ	= 0x00000000,
 	MBOX_PROP_CODE_RESP_OK	= 0x80000000,
@@ -122,4 +129,14 @@
 
 typedef struct {
+	mbox_prop_buf_hdr_t	buf_hdr;
+	mbox_tag_hdr_t		tag_hdr;
+	struct {
+		uint32_t	width;
+		uint32_t	height;
+	} body;
+	uint32_t zero;
+} mbox_getfbsize_buf_t;
+
+typedef struct {
 	ioport32_t width;
 	ioport32_t height;
@@ -135,5 +152,6 @@
 
 extern bool bcm2835_prop_get_memory(uint32_t *base, uint32_t *size);
-extern bool bcm2835_fb_init(fb_properties_t *prop);
+extern bool bcm2835_fb_init(fb_properties_t *prop, uint32_t width, uint32_t heigth);
+extern bool bcm2835_mbox_get_fb_size(uint32_t *h, uint32_t *w);
 
 #endif
Index: kernel/genarch/include/genarch/drivers/ns16550/ns16550.h
===================================================================
--- kernel/genarch/include/genarch/drivers/ns16550/ns16550.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/genarch/include/genarch/drivers/ns16550/ns16550.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -43,9 +43,25 @@
 #include <console/chardev.h>
 
-#define IER_ERBFI  0x01  /** Enable Receive Buffer Full Interrupt. */
+#define NS156440_CLOCK    115200 /** Internal clock speed, max. baud rate. */
 
-#define LCR_DLAB   0x80  /** Divisor Latch Access bit. */
+#define IER_ERBFI         0x01   /** Enable Receive Buffer Full Interrupt. */
+#define MCR_OUT2          0x08   /** OUT2. */
 
-#define MCR_OUT2   0x08  /** OUT2. */
+#define LCR_DLAB          0x80   /** Divisor Latch Access bit. */
+#define LCR_SBE           0x40   /** RS-232 Break Signal bit. */
+
+#define LCR_PARITY_NONE   0x00   /** No parity bit. */
+#define LCR_PARITY_ODD    0x08   /** Odd parity. */
+#define LCR_PARITY_EVEN   0x18   /** Even parity. */
+#define LCR_PARITY_MARK   0x28   /** Parity bit always one. */
+#define LCR_PARITY_SPACE  0x38   /** Parity bit always zero. */
+
+#define LCR_STOP_BIT_ONE  0x00   /** One stop bit. */
+#define LCR_STOP_BIT_TWO  0x04   /** Two stop bits. */
+
+#define LCR_WORD_LEN_5    0x00   /** 5-bit word length. */
+#define LCR_WORD_LEN_6    0x01   /** 6-bit word length. */
+#define LCR_WORD_LEN_7    0x02   /** 7-bit word length. */
+#define LCR_WORD_LEN_8    0x03   /** 8-bit word length. */
 
 /** NS16550 registers. */
@@ -53,5 +69,7 @@
 	NS16550_REG_RBR = 0,  /**< Receiver Buffer Register (read). */
 	NS16550_REG_THR = 0,  /**< Transmitter Holder Register (write). */
+	NS16550_REG_DLL = 0,  /**< Baud rate divisor latch low byte (write). */
 	NS16550_REG_IER = 1,  /**< Interrupt Enable Register. */
+	NS16550_REG_DLH = 1,  /**< Baud rate divisor latch high byte (write). */
 	NS16550_REG_IIR = 2,  /**< Interrupt Ident Register (read). */
 	NS16550_REG_FCR = 2,  /**< FIFO control register (write). */
@@ -73,4 +91,5 @@
 extern ns16550_instance_t *ns16550_init(ioport8_t *, unsigned, inr_t, cir_t,
     void *, outdev_t **);
+extern void ns16550_format_set(ns16550_instance_t *, unsigned, uint8_t);
 extern void ns16550_wire(ns16550_instance_t *, indev_t *);
 
Index: kernel/genarch/include/genarch/drivers/omap/uart_regs.h
===================================================================
--- kernel/genarch/include/genarch/drivers/omap/uart_regs.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/genarch/include/genarch/drivers/omap/uart_regs.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -353,5 +353,5 @@
 #define OMAP_UART_SSR_DMA_COUNTER_RESET_FLAG   (1 << 2)
 
-	/** BOF Length register (IrDA only)*/
+	/** BOF Length register (IrDA only) */
 	ioport32_t eblr;
 #define OMAP_IRDA_EBLR_DISABLED   (0x00)
Index: kernel/genarch/include/genarch/drivers/pl011/pl011.h
===================================================================
--- kernel/genarch/include/genarch/drivers/pl011/pl011.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/genarch/include/genarch/drivers/pl011/pl011.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -54,5 +54,5 @@
 
 	union {
-		/* Same values that are in upper bits of data register*/
+		/* Same values that are in upper bits of data register */
 		const ioport32_t status;
 #define PL011_UART_STATUS_FE_FLAG   (1 << 0)
Index: kernel/genarch/src/drivers/bcm2835/mbox.c
===================================================================
--- kernel/genarch/src/drivers/bcm2835/mbox.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/genarch/src/drivers/bcm2835/mbox.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -86,5 +86,5 @@
 }
 
-bool bcm2835_fb_init(fb_properties_t *prop)
+bool bcm2835_fb_init(fb_properties_t *prop, uint32_t width, uint32_t height)
 {
 	bcm2835_mbox_t *fb_mbox;
@@ -95,6 +95,6 @@
 	    KM_NATURAL_ALIGNMENT, PAGE_NOT_CACHEABLE);
 
-	fb_desc->width = 640;
-	fb_desc->height = 480;
+	fb_desc->width = width;
+	fb_desc->height = height;
 	fb_desc->virt_width = fb_desc->width;
 	fb_desc->virt_height = fb_desc->height;
@@ -128,4 +128,35 @@
 }
 
+bool bcm2835_mbox_get_fb_size(uint32_t *w, uint32_t *h)
+{
+	bool r;
+	MBOX_BUFF_ALLOC(msg, mbox_getfbsize_buf_t);
+	bcm2835_mbox_t *mbox;
+
+	mbox = (void *) km_map(BCM2835_MBOX0_ADDR, sizeof(bcm2835_mbox_t),
+	    KM_NATURAL_ALIGNMENT, PAGE_NOT_CACHEABLE);
+	assert(mbox);
+
+	msg->buf_hdr.size = sizeof(mbox_getfbsize_buf_t);
+	msg->buf_hdr.code = MBOX_PROP_CODE_REQ;
+	msg->tag_hdr.tag_id = MBOX_TAG_GET_PHYS_W_H;
+	msg->tag_hdr.buf_size = sizeof(msg->body);
+	msg->tag_hdr.val_len  = 0;
+	msg->zero = 0;
+
+	mbox_write(mbox,
+	    MBOX_CHAN_PROP_A2V, KA2VCA((uint32_t)msg));
+	mbox_read(mbox, MBOX_CHAN_PROP_A2V);
+
+	r = msg->buf_hdr.code == MBOX_PROP_CODE_RESP_OK;
+	if (r) {
+		*h = msg->body.height;
+		*w = msg->body.width;
+	}
+
+	km_unmap((uintptr_t) mbox, sizeof(bcm2835_mbox_t));
+	return r;
+}
+
 /**
  * @}
Index: kernel/genarch/src/drivers/ega/ega.c
===================================================================
--- kernel/genarch/src/drivers/ega/ega.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/genarch/src/drivers/ega/ega.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -568,15 +568,33 @@
 }
 
-static void ega_redraw(outdev_t *dev)
-{
-	ega_instance_t *instance = (ega_instance_t *) dev->data;
-
-	irq_spinlock_lock(&instance->lock, true);
-
+static void ega_redraw_internal(ega_instance_t *instance)
+{
 	memcpy(instance->addr, instance->backbuf, EGA_VRAM_SIZE);
 	ega_move_cursor(instance);
 	ega_show_cursor(instance);
-
+}
+
+static void ega_redraw(outdev_t *dev)
+{
+	ega_instance_t *instance = (ega_instance_t *) dev->data;
+
+	irq_spinlock_lock(&instance->lock, true);
+	ega_redraw_internal(instance);
 	irq_spinlock_unlock(&instance->lock, true);
+}
+
+/** EGA was mapped or unmapped.
+ *
+ * @param arg EGA instance
+ */
+static void ega_mapped_changed(void *arg)
+{
+	ega_instance_t *instance = (ega_instance_t *) arg;
+
+	if (!instance->parea.mapped) {
+		irq_spinlock_lock(&instance->lock, true);
+		ega_redraw_internal(instance);
+		irq_spinlock_unlock(&instance->lock, true);
+	}
 }
 
@@ -621,4 +639,6 @@
 	instance->parea.unpriv = false;
 	instance->parea.mapped = false;
+	instance->parea.mapped_changed = ega_mapped_changed;
+	instance->parea.arg = (void *) instance;
 	ddi_parea_register(&instance->parea);
 
Index: kernel/genarch/src/drivers/i8042/i8042.c
===================================================================
--- kernel/genarch/src/drivers/i8042/i8042.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/genarch/src/drivers/i8042/i8042.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -77,5 +77,5 @@
 }
 
-/**< Clear input buffer. */
+/** Clear input buffer. */
 static void i8042_clear_buffer(i8042_t *dev)
 {
Index: kernel/genarch/src/drivers/ns16550/ns16550.c
===================================================================
--- kernel/genarch/src/drivers/ns16550/ns16550.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/genarch/src/drivers/ns16550/ns16550.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -132,4 +132,28 @@
 };
 
+/** Configure ns16550 transmission format.
+ *
+ * @param instance   NS 16550 driver instance.
+ * @param baud_rate  Transmission speed in bits per second, also known as baud,
+ *                   maximum value is 115200.
+ * @param lcr_format Line Control Register configuration bits, as defined by
+ *                   the @c LCR_ macros.  These configure the word width,
+ *                   parity type, and stop bit count.
+ */
+void ns16550_format_set(ns16550_instance_t *instance,
+    unsigned baud_rate, uint8_t lcr_format)
+{
+	uint16_t divisor;
+
+	divisor = (uint16_t)(NS156440_CLOCK / baud_rate);
+	if (divisor == 0)
+		divisor = 1;  /* Avoid division by zero. */
+
+	ns16550_reg_write(instance, NS16550_REG_LCR, LCR_DLAB);
+	ns16550_reg_write(instance, NS16550_REG_DLL, divisor & 0xFF);
+	ns16550_reg_write(instance, NS16550_REG_DLH, (divisor >> 8) & 0xFF);
+	ns16550_reg_write(instance, NS16550_REG_LCR, lcr_format & ~LCR_DLAB);
+}
+
 /** Initialize ns16550.
  *
Index: kernel/genarch/src/fb/fb.c
===================================================================
--- kernel/genarch/src/fb/fb.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/genarch/src/fb/fb.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -516,4 +516,19 @@
 }
 
+/** Framebuffer was mapped or unmapped.
+ *
+ * @param arg Framebuffer instance
+ */
+static void fb_mapped_changed(void *arg)
+{
+	fb_instance_t *instance = (fb_instance_t *) arg;
+
+	if (!instance->parea.mapped) {
+		spinlock_lock(&instance->lock);
+		fb_redraw_internal(instance);
+		spinlock_unlock(&instance->lock);
+	}
+}
+
 /** Initialize framebuffer as a output character device
  *
@@ -661,4 +676,6 @@
 	instance->parea.unpriv = false;
 	instance->parea.mapped = false;
+	instance->parea.mapped_changed = fb_mapped_changed;
+	instance->parea.arg = (void *) instance;
 	ddi_parea_register(&instance->parea);
 
Index: kernel/genarch/src/kbrd/scanc_at.c
===================================================================
--- kernel/genarch/src/kbrd/scanc_at.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/genarch/src/kbrd/scanc_at.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -106,5 +106,5 @@
 	U_SPECIAL, /* 0x64 */
 	U_SPECIAL, /* 0x65 */
-	'\b', /* 0x66  - backspace*/
+	'\b', /* 0x66  - backspace */
 	U_SPECIAL, /* 0x67 */
 	U_SPECIAL, /* 0x68 */
@@ -117,5 +117,5 @@
 	U_SPECIAL, /* 0x6f */
 	U_SPECIAL, /* 0x70 */
-	U_DELETE, /* 0x71 - Del*/
+	U_DELETE, /* 0x71 - Del */
 	U_DOWN_ARROW, /* 0x72 Down Arrow */
 	U_SPECIAL, /* 0x73 */
@@ -123,6 +123,6 @@
 	U_UP_ARROW, /* 0x75  Up Arrow */
 	U_ESCAPE, /* 0x76 Esc */
-	U_SPECIAL, /* 0x77 - NumLock*/
-	U_SPECIAL, /* 0x78  F11*/
+	U_SPECIAL, /* 0x77 - NumLock */
+	U_SPECIAL, /* 0x78  F11 */
 	U_SPECIAL, /* 0x79 */
 	U_PAGE_DOWN, /* 0x7a */
@@ -202,5 +202,5 @@
 	U_SPECIAL, /* 0x64 */
 	U_SPECIAL, /* 0x65 */
-	'\b', /* 0x66  - backspace*/
+	'\b', /* 0x66  - backspace */
 	U_SPECIAL, /* 0x67 */
 	U_SPECIAL, /* 0x68 */
@@ -213,5 +213,5 @@
 	U_SPECIAL, /* 0x6f */
 	U_SPECIAL, /* 0x70 */
-	U_DELETE, /* 0x71 - Del*/
+	U_DELETE, /* 0x71 - Del */
 	U_DOWN_ARROW, /* 0x72 Down Arrow */
 	U_SPECIAL, /* 0x73 */
@@ -219,6 +219,6 @@
 	U_UP_ARROW, /* 0x75  Up Arrow */
 	U_ESCAPE, /* 0x76 Esc */
-	U_SPECIAL, /* 0x77 - NumLock*/
-	U_SPECIAL, /* 0x78  F11*/
+	U_SPECIAL, /* 0x77 - NumLock */
+	U_SPECIAL, /* 0x78  F11 */
 	U_SPECIAL, /* 0x79 */
 	U_PAGE_DOWN, /* 0x7a */
Index: kernel/generic/include/adt/hash_table.h
===================================================================
--- kernel/generic/include/adt/hash_table.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/adt/hash_table.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -41,4 +41,5 @@
 #include <stdbool.h>
 #include <macros.h>
+#include <member.h>
 
 /** Opaque hash table link type. */
Index: kernel/generic/include/adt/list.h
===================================================================
--- kernel/generic/include/adt/list.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/adt/list.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -38,6 +38,9 @@
 
 #include <assert.h>
+#include <macros.h>
+#include <member.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include <stdint.h>
 #include <trace.h>
 
@@ -89,8 +92,8 @@
 
 #define list_get_instance(link, type, member) \
-	((type *) (((void *)(link)) - list_link_to_void(&(((type *) NULL)->member))))
+	member_to_inst(link, type, member)
 
 #define list_foreach(list, member, itype, iterator) \
-	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) 1) \
+	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) sizeof(itype)) \
 		for (link_t *_link = (list).head.next; \
 		    iterator = list_get_instance(_link, itype, member), \
@@ -98,5 +101,5 @@
 
 #define list_foreach_rev(list, member, itype, iterator) \
-	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) 1) \
+	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) sizeof(itype)) \
 		for (link_t *_link = (list).head.prev; \
 		    iterator = list_get_instance(_link, itype, member), \
@@ -375,7 +378,7 @@
  *
  */
-static inline link_t *list_nth(const list_t *list, unsigned long n)
-{
-	unsigned long cnt = 0;
+static inline link_t *list_nth(const list_t *list, size_t n)
+{
+	size_t cnt = 0;
 
 	link_t *link = list_first(list);
@@ -391,13 +394,4 @@
 }
 
-/** 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;
-}
-
 /** Determine if link is used.
  *
Index: kernel/generic/include/adt/odict.h
===================================================================
--- kernel/generic/include/adt/odict.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/adt/odict.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -37,4 +37,5 @@
 
 #include <errno.h>
+#include <member.h>
 #include <stdbool.h>
 #include <stddef.h>
@@ -42,5 +43,5 @@
 
 #define odict_get_instance(odlink, type, member) \
-	((type *)( (void *)(odlink) - ((void *) &((type *) NULL)->member)))
+	member_to_inst(odlink, type, member)
 
 extern void odict_initialize(odict_t *, odgetkey_t, odcmp_t);
Index: kernel/generic/include/atomic.h
===================================================================
--- kernel/generic/include/atomic.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/atomic.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -40,13 +40,4 @@
 #include <stdatomic.h>
 
-// TODO: Remove.
-// Before <stdatomic.h> was available, there was only one atomic type
-// equivalent to atomic_size_t. This means that in some places, atomic_t can
-// be replaced with a more appropriate type (e.g. atomic_bool for flags or
-// a signed type for potentially signed values).
-// So atomic_t should be replaced with the most appropriate type on a case by
-// case basis, and after there are no more uses, remove this type.
-typedef atomic_size_t atomic_t;
-
 #define atomic_predec(val) \
 	(atomic_fetch_sub((val), 1) - 1)
Index: kernel/generic/include/cap/cap.h
===================================================================
--- kernel/generic/include/cap/cap.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/cap/cap.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -80,5 +80,5 @@
 typedef struct kobject {
 	kobject_type_t type;
-	atomic_t refcnt;
+	atomic_size_t refcnt;
 
 	/** Mutex protecting caps_list */
Index: kernel/generic/include/cpu.h
===================================================================
--- kernel/generic/include/cpu.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/cpu.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -58,5 +58,5 @@
 	context_t saved_context;
 
-	atomic_t nrdy;
+	atomic_size_t nrdy;
 	runq_t rq[RQ_COUNT];
 	volatile size_t needs_relink;
Index: kernel/generic/include/cpu/cpu_mask.h
===================================================================
--- kernel/generic/include/cpu/cpu_mask.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/cpu/cpu_mask.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -59,5 +59,5 @@
 	cpu_mask_t *(cpu_mask) = (cpu_mask_t*) alloca(cpu_mask_size())
 
-/** If used with DEFINE_CPU_MASK, the mask is large enough for all detected cpus.*/
+/** If used with DEFINE_CPU_MASK, the mask is large enough for all detected cpus. */
 typedef struct cpu_mask {
 	unsigned int mask[1];
Index: kernel/generic/include/ddi/ddi.h
===================================================================
--- kernel/generic/include/ddi/ddi.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/ddi/ddi.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -42,5 +42,5 @@
 
 /** Structure representing contiguous physical memory area. */
-typedef struct {
+typedef struct parea {
 	/** Link to @c pareas ordered dictionary */
 	odlink_t lpareas;
@@ -54,4 +54,8 @@
 	/** Indicate whether the area is actually mapped. */
 	bool mapped;
+	/** Called when @c mapped field has changed */
+	void (*mapped_changed)(void *);
+	/** Callback argument */
+	void *arg;
 } parea_t;
 
@@ -59,4 +63,5 @@
 extern void ddi_parea_init(parea_t *);
 extern void ddi_parea_register(parea_t *);
+extern void ddi_parea_unmap_notify(parea_t *);
 
 extern void *pio_map(void *, size_t);
Index: kernel/generic/include/halt.h
===================================================================
--- kernel/generic/include/halt.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/halt.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -38,5 +38,5 @@
 #include <atomic.h>
 
-extern atomic_t haltstate;
+extern atomic_bool haltstate;
 
 extern void halt(void) __attribute__((noreturn));
Index: kernel/generic/include/ipc/ipc.h
===================================================================
--- kernel/generic/include/ipc/ipc.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/ipc/ipc.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -71,5 +71,5 @@
 	struct call *hangup_call;
 	ipc_phone_state_t state;
-	atomic_t active_calls;
+	atomic_size_t active_calls;
 	/** User-defined label */
 	sysarg_t label;
@@ -90,5 +90,5 @@
 	 * Number of answers the answerbox is expecting to eventually arrive.
 	 */
-	atomic_t active_calls;
+	atomic_size_t active_calls;
 
 	/** Phones connected to this answerbox. */
Index: kernel/generic/include/macros.h
===================================================================
--- kernel/generic/include/macros.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/macros.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -160,8 +160,4 @@
 	})
 
-#define member_to_inst(ptr_member, type, member_identif) \
-	((type *) (((void *) (ptr_member)) - \
-	    ((void *) &(((type *) 0)->member_identif))))
-
 /** Get the size of an array in array elements
  *
Index: kernel/generic/include/member.h
===================================================================
--- kernel/generic/include/member.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ kernel/generic/include/member.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2021 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 kernel_generic
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_MEMBER_H_
+#define KERN_MEMBER_H_
+
+#include <stdint.h>
+
+#define member_to_inst(ptr_member, type, member_identif) \
+	((type *) (((uintptr_t) (ptr_member)) - \
+	    offsetof(type, member_identif)))
+
+#endif
+
+/** @}
+ */
Index: kernel/generic/include/mm/as.h
===================================================================
--- kernel/generic/include/mm/as.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/mm/as.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -236,4 +236,5 @@
 		size_t frames;
 		bool anonymous;
+		struct parea *parea;
 	};
 
Index: kernel/generic/include/mm/slab.h
===================================================================
--- kernel/generic/include/mm/slab.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/mm/slab.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -99,9 +99,9 @@
 
 	/* Statistics */
-	atomic_t allocated_slabs;
-	atomic_t allocated_objs;
-	atomic_t cached_objs;
+	atomic_size_t allocated_slabs;
+	atomic_size_t allocated_objs;
+	atomic_size_t cached_objs;
 	/** How many magazines in magazines list */
-	atomic_t magazine_counter;
+	atomic_size_t magazine_counter;
 
 	/* Slabs */
Index: kernel/generic/include/proc/scheduler.h
===================================================================
--- kernel/generic/include/proc/scheduler.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/proc/scheduler.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -52,5 +52,5 @@
 } runq_t;
 
-extern atomic_t nrdy;
+extern atomic_size_t nrdy;
 extern void scheduler_init(void);
 
Index: kernel/generic/include/proc/task.h
===================================================================
--- kernel/generic/include/proc/task.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/include/proc/task.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -87,7 +87,7 @@
 
 	/** Number of references (i.e. threads). */
-	atomic_t refcount;
+	atomic_size_t refcount;
 	/** Number of threads that haven't exited yet. */
-	atomic_t lifecount;
+	atomic_size_t lifecount;
 
 	/** Task permissions. */
Index: kernel/generic/src/adt/hash_table.c
===================================================================
--- kernel/generic/src/adt/hash_table.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/src/adt/hash_table.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -331,5 +331,5 @@
 }
 
-/** Removes an item already present in the table. The item must be in the table.*/
+/** Removes an item already present in the table. The item must be in the table. */
 void hash_table_remove_item(hash_table_t *h, ht_link_t *item)
 {
@@ -393,5 +393,5 @@
 }
 
-/** Allocates and initializes the desired number of buckets. True if successful.*/
+/** Allocates and initializes the desired number of buckets. True if successful. */
 static bool alloc_table(size_t bucket_cnt, list_t **pbuckets)
 {
Index: kernel/generic/src/ddi/ddi.c
===================================================================
--- kernel/generic/src/ddi/ddi.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/src/ddi/ddi.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -107,4 +107,15 @@
 }
 
+/** Norify physical area has been unmapped.
+ *
+ * @param parea Physical area
+ */
+void ddi_parea_unmap_notify(parea_t *parea)
+{
+	parea->mapped = false;
+	if (parea->mapped_changed != NULL)
+		parea->mapped_changed(parea->arg);
+}
+
 /** Map piece of physical memory into virtual address space of current task.
  *
@@ -204,4 +215,6 @@
 
 map:
+	backend_data.parea = parea;
+
 	if (!as_area_create(TASK->as, flags, FRAMES2SIZE(pages),
 	    AS_AREA_ATTR_NONE, &phys_backend, &backend_data, virt, bound)) {
@@ -443,4 +456,5 @@
 	backend_data.frames = frames;
 	backend_data.anonymous = true;
+	backend_data.parea = NULL;
 
 	if (!as_area_create(TASK->as, map_flags, size,
Index: kernel/generic/src/lib/halt.c
===================================================================
--- kernel/generic/src/lib/halt.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/src/lib/halt.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -36,4 +36,5 @@
  */
 
+#include <stdbool.h>
 #include <halt.h>
 #include <log.h>
@@ -44,5 +45,5 @@
 
 /** Halt flag */
-atomic_t haltstate = 0;
+atomic_bool haltstate = false;
 
 /** Halt wrapper
@@ -57,9 +58,9 @@
 
 	if (!atomic_load(&haltstate)) {
-		atomic_store(&haltstate, 1);
+		atomic_store(&haltstate, true);
 		rundebugger = true;
 	}
 #else
-	atomic_store(&haltstate, 1);
+	atomic_store(&haltstate, true);
 #endif
 
Index: kernel/generic/src/mm/as.c
===================================================================
--- kernel/generic/src/mm/as.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/src/mm/as.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -2057,5 +2057,5 @@
 	/* Check if A is adjacent to the new interval */
 	adj_a = (a != NULL) && (a->page + P2SZ(a->count) == page);
-	/* Check if the new interval is adjacent to B*/
+	/* Check if the new interval is adjacent to B */
 	adj_b = (b != NULL) && page + P2SZ(count) == b->page;
 
Index: kernel/generic/src/mm/backend_phys.c
===================================================================
--- kernel/generic/src/mm/backend_phys.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/src/mm/backend_phys.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -38,4 +38,5 @@
 
 #include <assert.h>
+#include <ddi/ddi.h>
 #include <typedefs.h>
 #include <mm/as.h>
@@ -101,8 +102,11 @@
 {
 	/*
-	 * Nothing to do.
 	 * The anonymous frames, if any, are released in
 	 * phys_destroy_shared_data().
 	 */
+
+	/* Notify parea has been unmapped */
+	if (area->backend_data.parea != NULL)
+		ddi_parea_unmap_notify(area->backend_data.parea);
 }
 
Index: kernel/generic/src/mm/frame.c
===================================================================
--- kernel/generic/src/mm/frame.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/src/mm/frame.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -85,7 +85,7 @@
 }
 
-/*******************/
-/* Zones functions */
-/*******************/
+/*
+ * Zones functions
+ */
 
 /** Insert-sort zone into zones list.
@@ -341,7 +341,7 @@
 }
 
-/******************/
-/* Zone functions */
-/******************/
+/*
+ * Zone functions
+ */
 
 /** Return frame from zone. */
@@ -781,7 +781,7 @@
 }
 
-/*******************/
-/* Frame functions */
-/*******************/
+/*
+ * Frame functions
+ */
 
 /** Set parent of frame. */
Index: kernel/generic/src/mm/slab.c
===================================================================
--- kernel/generic/src/mm/slab.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/src/mm/slab.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -151,7 +151,7 @@
 #endif
 
-/**************************************/
-/* Slab allocation functions          */
-/**************************************/
+/*
+ * Slab allocation functions
+ */
 
 /** Allocate frames for slab space and initialize
@@ -223,7 +223,7 @@
 }
 
-/******************/
-/* Slab functions */
-/******************/
+/*
+ * Slab functions
+ */
 
 /** Return object to slab and call a destructor
@@ -323,7 +323,7 @@
 }
 
-/****************************/
-/* CPU-Cache slab functions */
-/****************************/
+/*
+ * CPU-Cache slab functions
+ */
 
 /** Find a full magazine in cache, take it from list and return it
@@ -531,7 +531,7 @@
 }
 
-/************************/
-/* Slab cache functions */
-/************************/
+/*
+ * Slab cache functions
+ */
 
 /** Return number of objects that fit in certain cache size
Index: kernel/generic/src/proc/scheduler.c
===================================================================
--- kernel/generic/src/proc/scheduler.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/src/proc/scheduler.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -68,5 +68,5 @@
 static void scheduler_separated_stack(void);
 
-atomic_t nrdy;  /**< Number of ready threads in the system. */
+atomic_size_t nrdy;  /**< Number of ready threads in the system. */
 
 /** Carry out actions before new task runs. */
Index: kernel/generic/src/udebug/udebug_ops.c
===================================================================
--- kernel/generic/src/udebug/udebug_ops.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/generic/src/udebug/udebug_ops.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -123,5 +123,5 @@
 	/* Now verify that the thread belongs to the current task. */
 	if (thread->task != TASK) {
-		/* No such thread belonging this task*/
+		/* No such thread belonging this task */
 		mutex_unlock(&TASK->udebug.lock);
 		return ENOENT;
Index: kernel/test/atomic/atomic1.c
===================================================================
--- kernel/test/atomic/atomic1.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/test/atomic/atomic1.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -32,5 +32,5 @@
 const char *test_atomic1(void)
 {
-	atomic_t a;
+	atomic_int a;
 
 	atomic_store(&a, 10);
Index: kernel/test/mm/falloc2.c
===================================================================
--- kernel/test/mm/falloc2.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/test/mm/falloc2.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -43,6 +43,6 @@
 #define THREADS      8
 
-static atomic_t thread_cnt;
-static atomic_t thread_fail;
+static atomic_size_t thread_cnt;
+static atomic_size_t thread_fail;
 
 static void falloc(void *arg)
Index: kernel/test/synch/semaphore1.c
===================================================================
--- kernel/test/synch/semaphore1.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/test/synch/semaphore1.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -41,6 +41,6 @@
 
 static waitq_t can_start;
-static atomic_t items_produced;
-static atomic_t items_consumed;
+static atomic_size_t items_produced;
+static atomic_size_t items_consumed;
 
 static void producer(void *arg)
Index: kernel/test/thread/thread1.c
===================================================================
--- kernel/test/thread/thread1.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ kernel/test/thread/thread1.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -30,4 +30,5 @@
 #include <test.h>
 #include <atomic.h>
+#include <stdbool.h>
 #include <proc/thread.h>
 
@@ -36,6 +37,6 @@
 #define THREADS  5
 
-static atomic_t finish;
-static atomic_t threads_finished;
+static atomic_bool finish;
+static atomic_size_t threads_finished;
 
 static void threadtest(void *data)
@@ -55,5 +56,5 @@
 	size_t total = 0;
 
-	atomic_store(&finish, 1);
+	atomic_store(&finish, true);
 	atomic_store(&threads_finished, 0);
 
@@ -72,5 +73,5 @@
 	thread_sleep(10);
 
-	atomic_store(&finish, 0);
+	atomic_store(&finish, false);
 	while (atomic_load(&threads_finished) < total) {
 		TPRINTF("Threads left: %zu\n", total - atomic_load(&threads_finished));
Index: meson.build
===================================================================
--- meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -35,5 +35,5 @@
     'HelenOS',
     [ 'c', 'cpp' ],
-    default_options : ['buildtype=plain', 'c_std=gnu11', 'cpp_std=c++17', 'warning_level=3', 'werror=false', 'b_staticpic=false', 'prefix=/' ],
+    default_options : ['buildtype=plain', 'c_std=gnu11', 'cpp_std=c++17', 'warning_level=2', 'werror=false', 'b_staticpic=false', 'prefix=/' ],
     meson_version: '>=0.50.1',
 )
Index: tools/build-ccheck.sh
===================================================================
--- tools/build-ccheck.sh	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ tools/build-ccheck.sh	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -29,5 +29,5 @@
 
 SYCEK_GIT="https://github.com/jxsvoboda/sycek"
-SYCEK_REV="4dc1760741efdd84506478ab6effd68812b4726a"
+SYCEK_REV="14f0aadad8bb2f17eef030a74b3591377ad57343"
 
 if [ ! -d sycek ]; then
Index: tools/ew.py
===================================================================
--- tools/ew.py	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ tools/ew.py	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -139,5 +139,8 @@
 		return 'system-x86_64', pc_options(64)
 	elif platform == 'arm32':
-		return 'system-arm', '-M integratorcp'
+		if machine == 'integratorcp':
+			return 'system-arm', '-M integratorcp'
+		elif machine == 'raspberrypi':
+			return 'system-arm', '-M raspi1ap'
 	elif platform == 'arm64':
 		# Search for the EDK2 firmware image
@@ -299,4 +302,6 @@
 	elif cfg['image'] == 'image.boot':
 		cmdline += ' -kernel image.boot'
+	elif cfg['image'] == 'kernel.img@rpi':
+		cmdline += ' -bios boot/image.boot.bin'
 	else:
 		cmdline += ' ' + cfg['image']
@@ -339,5 +344,16 @@
 			'xhci' : False,
 			'tablet' : False
-		}
+		},
+		'raspberrypi' : {
+			'run' : qemu_run,
+			'image' : 'kernel.img@rpi',
+			'audio' : False,
+			'console' : True,
+			'hdd' : False,
+			'net' : False,
+			'tablet' : False,
+			'usb' : False,
+			'xhci' : False
+		},
 	},
 	'arm64' : {
Index: tools/export.sh
===================================================================
--- tools/export.sh	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ tools/export.sh	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -42,4 +42,5 @@
 	uspace/lib/libclui.a \
 	uspace/lib/libc.a \
+	uspace/lib/libcongfx.a \
 	uspace/lib/libcpp.a \
 	uspace/lib/libdisplay.a \
Index: tools/mkfat.py
===================================================================
--- tools/mkfat.py	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ tools/mkfat.py	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,3 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (c) 2008 Martin Decky
@@ -193,6 +193,6 @@
 
 	for char in name.encode('ascii', 'replace').upper():
-		if char in lchars:
-			filtered_name += char
+		if chr(char) in lchars:
+			filtered_name += str.encode(chr(char))
 		else:
 			filtered_name += b'_'
Index: tools/toolchain.sh
===================================================================
--- tools/toolchain.sh	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ tools/toolchain.sh	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -41,7 +41,8 @@
 GCC_VERSION="8.2.0"
 
-BASEDIR="`pwd`"
+BASEDIR="$PWD"
 SRCDIR="$(readlink -f $(dirname "$0"))"
 
+SYSTEM_INSTALL=false
 REAL_INSTALL=true
 USE_HELENOS_TARGET=true
@@ -57,9 +58,9 @@
 
 show_usage() {
-	echo "Cross-compiler toolchain build script"
+	echo "HelenOS cross-compiler toolchain build script"
 	echo
 	echo "Syntax:"
-	echo " $0 [--no-install] [--non-helenos-target] <platform>"
-	echo " $0 --test-version [<platform>]"
+	echo " $0 [--system-wide] [--no-install] [--non-helenos-target] <platform>"
+	echo " $0 [--system-wide] --test-version [<platform>]"
 	echo
 	echo "Possible target platforms are:"
@@ -79,28 +80,55 @@
 	echo " 2-way      same as 'all', but 2-way parallel"
 	echo
-	echo "The toolchain is installed into directory specified by the"
-	echo "CROSS_PREFIX environment variable. If the variable is not"
-	echo "defined, /usr/local/cross/ is used as default."
-	echo
-	echo "If --no-install is present, the toolchain still uses the"
-	echo "CROSS_PREFIX as the target directory but the installation"
-	echo "copies the files into PKG/ subdirectory without affecting"
-	echo "the actual root file system. That is only useful if you do"
-	echo "not want to run the script under the super user."
-	echo
-	echo "The --non-helenos-target will build non-HelenOS-specific toolchain"
-	echo "(i.e. it will use *-linux-* triplet instead of *-helenos)."
+	echo "The toolchain target installation directory is determined by matching"
+	echo "the first of the following conditions:"
+	echo
+	echo " (1) If the \$CROSS_PREFIX environment variable is set, then it is"
+	echo "     used as the target installation directory."
+	echo " (2) If the --system-wide option is used, then /opt/HelenOS/cross"
+	echo "     is used as the target installation directory. This usually"
+	echo "     requires running this script with super user privileges."
+	echo " (3) In other cases, \$XDG_DATA_HOME/HelenOS/cross is used as the"
+	echo "     target installation directory. If the \$XDG_DATA_HOME environment"
+	echo "     variable is not set, then the default value of \$HOME/.local/share"
+	echo "     is assumed."
+	echo
+	echo "If the --no-install option is used, the toolchain still uses the"
+	echo "target installation directory as determined above, but the files"
+	echo "are actually copied into the PKG/ subdirectory during the installation"
+	echo "without affecting the actual target file system. This might be useful"
+	echo "when preparing a system-wide installation, but avoiding running this"
+	echo "script under the super user."
+	echo
+	echo "The --non-helenos-target option will build non-HelenOS-specific"
+	echo "toolchain (i.e. it will use *-linux-* triplet instead of *-helenos)."
 	echo "Using this toolchain for building HelenOS is not supported."
 	echo
+	echo "The --test-version mode tests the currently installed version of the"
+	echo "toolchain."
 
 	exit 3
 }
 
+set_cross_prefix() {
+	if [ -z "$CROSS_PREFIX" ] ; then
+		if $SYSTEM_INSTALL ; then
+			CROSS_PREFIX="/opt/HelenOS/cross"
+		else
+			if [ -z "$XDG_DATA_HOME" ] ; then
+				XDG_DATA_HOME="$HOME/.local/share"
+			fi
+			CROSS_PREFIX="$XDG_DATA_HOME/HelenOS/cross"
+		fi
+	fi
+}
+
 test_version() {
-	echo "Cross-compiler toolchain build script"
-	echo
-	echo "Start testing the version of the installed software" 
-	echo
-	
+	set_cross_prefix
+
+	echo "HelenOS cross-compiler toolchain build script"
+	echo
+	echo "Testing the version of the installed software in $CROSS_PREFIX"
+	echo
+
 	if [ -z "$1" ] || [ "$1" = "all" ] ; then
 		PLATFORMS='amd64 arm32 arm64 ia32 ia64 mips32 mips32eb ppc32 riscv64 sparc64'
@@ -108,13 +136,6 @@
 		PLATFORMS="$1"
 	fi
-	
-	
-	if [ -z "${CROSS_PREFIX}" ] ; then
-		CROSS_PREFIX="/usr/local/cross"
-	fi
-
-	for i in $PLATFORMS
-	do
-		PLATFORM="$i"
+
+	for PLATFORM in $PLATFORMS ; do
 		set_target_from_platform "$PLATFORM"
 		PREFIX="${CROSS_PREFIX}/bin/${HELENOS_TARGET}"
@@ -125,6 +146,4 @@
 		test_app_version "GDB" "gdb" "GNU gdb (.*)[[:space:]]\+\([.0-9]*\)" "$GDB_VERSION"
 	done
-
-	exit
 }
 
@@ -135,5 +154,4 @@
 	INS_VERSION="$4"
 
-
 	APP="${PREFIX}-${APPNAME}"
 	if [ ! -e $APP ]; then
@@ -148,5 +166,5 @@
 
 		if [ "$INS_VERSION" = "$VERSION" ]; then
-			echo "+ $PKGNAME is uptodate ($INS_VERSION)"
+			echo "+ $PKGNAME is up-to-date ($INS_VERSION)"
 		else
 			echo "- $PKGNAME ($VERSION) is outdated ($INS_VERSION)"
@@ -155,6 +173,4 @@
 }
 
-
-
 change_title() {
 	printf "\e]0;$1\a"
@@ -178,4 +194,11 @@
 
 show_dependencies() {
+	set_cross_prefix
+
+	echo "HelenOS cross-compiler toolchain build script"
+	echo
+	echo "Installing software to $CROSS_PREFIX"
+	echo
+	echo
 	echo "IMPORTANT NOTICE:"
 	echo
@@ -255,4 +278,5 @@
 	check_error $? "Change directory failed."
 
+	change_title "Downloading sources"
 	echo ">>> Downloading sources"
 	git clone --depth 1 -b "$BINUTILS_BRANCH" "$BINUTILS_GDB_GIT" "binutils-$BINUTILS_VERSION"
@@ -265,4 +289,5 @@
 	git -C "gcc-$GCC_VERSION" pull
 
+	change_title "Downloading GCC prerequisites"
 	echo ">>> Downloading GCC prerequisites"
 	cd "gcc-${GCC_VERSION}"
@@ -325,10 +350,9 @@
 	GDBDIR="${WORKDIR}/gdb-${GDB_VERSION}"
 
-	if [ -z "${CROSS_PREFIX}" ] ; then
-		CROSS_PREFIX="/usr/local/cross"
-	fi
+	# This sets the CROSS_PREFIX variable
+	set_cross_prefix
 
 	if [ -z "$JOBS" ] ; then
-		JOBS=`nproc`
+		JOBS="`nproc`"
 	fi
 
@@ -341,4 +365,5 @@
 
 	if $USE_HELENOS_TARGET ; then
+		change_title "Creating build sysroot"
 		echo ">>> Creating build sysroot"
 		mkdir -p "${WORKDIR}/sysroot/include"
@@ -355,4 +380,5 @@
 		check_error $? "Failed to create build sysroot."
 	fi
+
 
 	echo ">>> Processing binutils (${PLATFORM})"
@@ -416,9 +442,9 @@
 		PATH="${PATH}:${PREFIX}/bin:${INSTALL_DIR}/${PREFIX}/bin" make all-target-libgcc -j$JOBS
 		check_error $? "Error compiling libgcc."
-		# TODO: needs some extra care
-		#PATH="${PATH}:${PREFIX}/bin:${INSTALL_DIR}/${PREFIX}/bin" make all-target-libatomic -j$JOBS
-		#check_error $? "Error compiling libatomic."
-		#PATH="${PATH}:${PREFIX}/bin:${INSTALL_DIR}/${PREFIX}/bin" make all-target-libstdc++-v3 -j$JOBS
-		#check_error $? "Error compiling libstdc++."
+		# TODO: libatomic and libstdc++ need some extra care
+		#    PATH="${PATH}:${PREFIX}/bin:${INSTALL_DIR}/${PREFIX}/bin" make all-target-libatomic -j$JOBS
+		#    check_error $? "Error compiling libatomic."
+		#    PATH="${PATH}:${PREFIX}/bin:${INSTALL_DIR}/${PREFIX}/bin" make all-target-libstdc++-v3 -j$JOBS
+		#    check_error $? "Error compiling libstdc++."
 	fi
 
@@ -427,6 +453,6 @@
 	if $USE_HELENOS_TARGET ; then
 		PATH="${PATH}:${INSTALL_DIR}/${PREFIX}/bin" make install-target-libgcc "DESTDIR=${INSTALL_DIR}"
-		#PATH="${PATH}:${INSTALL_DIR}/${PREFIX}/bin" make install-target-libatomic "DESTDIR=${INSTALL_DIR}"
-		#PATH="${PATH}:${INSTALL_DIR}/${PREFIX}/bin" make install-target-libstdc++-v3 "DESTDIR=${INSTALL_DIR}"
+		#    PATH="${PATH}:${INSTALL_DIR}/${PREFIX}/bin" make install-target-libatomic "DESTDIR=${INSTALL_DIR}"
+		#    PATH="${PATH}:${INSTALL_DIR}/${PREFIX}/bin" make install-target-libstdc++-v3 "DESTDIR=${INSTALL_DIR}"
 	fi
 	check_error $? "Error installing GCC."
@@ -463,5 +489,4 @@
 	if $REAL_INSTALL ; then
 		echo ">>> Moving to the destination directory."
-		echo cp -r -t "${PREFIX}" "${INSTALL_DIR}/${PREFIX}/"*
 		cp -r -t "${PREFIX}" "${INSTALL_DIR}/${PREFIX}/"*
 	fi
@@ -479,4 +504,8 @@
 while [ "$#" -gt 1 ] ; do
 	case "$1" in
+		--system-wide)
+			SYSTEM_INSTALL=true
+			shift
+			;;
 		--test-version)
 			test_version "$2"
@@ -504,4 +533,5 @@
 	--test-version)
 		test_version
+		exit
 		;;
 	amd64|arm32|arm64|ia32|ia64|mips32|mips32eb|ppc32|riscv64|sparc64)
Index: tools/xcw/bin/helenos-pkg-config
===================================================================
--- tools/xcw/bin/helenos-pkg-config	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ tools/xcw/bin/helenos-pkg-config	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -44,6 +44,6 @@
 libmath_libs="$LIB_DIR/libmath.a"
 
-libui_cflags="-I$INCLUDE_DIR/libui -I$INCLUDE_DIR/libdisplay -I$INCLUDE_DIR/libgfx -I$INCLUDE_DIR/libipcgfx"
-libui_libs="$LIB_DIR/libui.a $LIB_DIR/libdisplay.a $LIB_DIR/libipcgfx.a $LIB_DIR/libgfx.a $LIB_DIR/libgfxfont.a $LIB_DIR/libriff.a $LIB_DIR/libmemgfx.a"
+libui_cflags="-I$INCLUDE_DIR/libui -I$INCLUDE_DIR/libdisplay -I$INCLUDE_DIR/libgfx -I$INCLUDE_DIR/libipcgfx -I$INCLUDE_DIR/libcongfx"
+libui_libs="$LIB_DIR/libui.a $LIB_DIR/libdisplay.a $LIB_DIR/libipcgfx.a $LIB_DIR/libcongfx.a $LIB_DIR/libgfx.a $LIB_DIR/libgfxfont.a $LIB_DIR/libriff.a $LIB_DIR/libmemgfx.a"
 
 libgfximage_cflags="-I$INCLUDE_DIR/libgfximage"
Index: tools/xcw/demo/Makefile
===================================================================
--- tools/xcw/demo/Makefile	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ tools/xcw/demo/Makefile	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -50,7 +50,7 @@
 INSTALL = install
 TEST = helenos-test
-CFLAGS = -std=gnu11 -Wall `helenos-pkg-config --cflags libui libdraw libmath` \
+CFLAGS = -std=gnu11 -Wall `helenos-pkg-config --cflags libui libgfximage` \
     -D_HELENOS_SOURCE
-LIBS = `helenos-pkg-config --libs libui libdraw libmath`
+LIBS = `helenos-pkg-config --libs libui libgfximage`
 PREFIX = `helenos-bld-config --install-dir`
 output = viewer
Index: uspace/app/barber/barber.c
===================================================================
--- uspace/app/barber/barber.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/barber/barber.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -412,9 +412,9 @@
 	ui_image_set_bmp(frame_img, NULL, &rect);
 
+	destroy_frames();
+
 	ui_window_destroy(window);
 	ui_destroy(ui);
 
-	destroy_frames();
-
 	return 0;
 }
Index: uspace/app/barber/meson.build
===================================================================
--- uspace/app/barber/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/barber/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -27,5 +27,5 @@
 #
 
-deps = [ 'ui', 'gfximage', 'compress' ]
+deps = [ 'ui', 'gfximage', 'compress', 'device' ]
 
 _images = files(
Index: uspace/app/bdsh/cmds/modules/cat/cat.c
===================================================================
--- uspace/app/bdsh/cmds/modules/cat/cat.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/bdsh/cmds/modules/cat/cat.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -122,9 +122,10 @@
 	cons_event_t ev;
 	kbd_event_t *kev;
+	errno_t rc;
 
 	while (true) {
-		if (!console_get_event(console, &ev)) {
+		rc = console_get_event(console, &ev);
+		if (rc != EOK)
 			return;
-		}
 		if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS) {
 			kev = &ev.ev.key;
Index: uspace/app/bdsh/cmds/modules/cp/cp.c
===================================================================
--- uspace/app/bdsh/cmds/modules/cp/cp.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/bdsh/cmds/modules/cp/cp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -146,4 +146,5 @@
 {
 	va_list args;
+	errno_t rc;
 
 	va_start(args, message);
@@ -154,5 +155,7 @@
 		cons_event_t ev;
 		console_flush(con);
-		console_get_event(con, &ev);
+		rc = console_get_event(con, &ev);
+		if (rc != EOK)
+			exit(1);
 		if (ev.type != CEV_KEY || ev.ev.key.type != KEY_PRESS ||
 		    (ev.ev.key.mods & (KM_CTRL | KM_ALT)) != 0) {
Index: uspace/app/calculator/calculator.c
===================================================================
--- uspace/app/calculator/calculator.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/calculator/calculator.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2016 Martin Decky
  * All rights reserved.
@@ -37,4 +37,5 @@
  */
 
+#include <clipboard.h>
 #include <ctype.h>
 #include <io/kbd_event.h>
@@ -45,4 +46,7 @@
 #include <ui/entry.h>
 #include <ui/fixed.h>
+#include <ui/menubar.h>
+#include <ui/menu.h>
+#include <ui/menuentry.h>
 #include <ui/pbutton.h>
 #include <ui/ui.h>
@@ -95,6 +99,16 @@
 } stack_item_t;
 
+/** Dimensions. Most of this should not be needed with auto layout */
+typedef struct {
+	gfx_rect_t menubar_rect;
+	gfx_rect_t entry_rect;
+	gfx_coord2_t btn_orig;
+	gfx_coord2_t btn_stride;
+	gfx_coord2_t btn_dim;
+} calc_geom_t;
+
 typedef struct {
 	ui_t *ui;
+	ui_resource_t *ui_res;
 	ui_pbutton_t *btn_eval;
 	ui_pbutton_t *btn_clear;
@@ -113,5 +127,13 @@
 	ui_pbutton_t *btn_8;
 	ui_pbutton_t *btn_9;
+	ui_menu_bar_t *menubar;
+	calc_geom_t geom;
 } calc_t;
+
+static void display_update(void);
+
+static void calc_file_exit(ui_menu_entry_t *, void *);
+static void calc_edit_copy(ui_menu_entry_t *, void *);
+static void calc_edit_paste(ui_menu_entry_t *, void *);
 
 static void calc_pb_clicked(ui_pbutton_t *, void *);
@@ -138,4 +160,7 @@
 	.kbd = wnd_kbd_event
 };
+
+static char *expr = NULL;
+static ui_entry_t *display;
 
 /** Window close request
@@ -160,4 +185,17 @@
 {
 	calc_t *calc = (calc_t *) arg;
+
+	if (event->type == KEY_PRESS && (event->mods & KM_CTRL) != 0) {
+		switch (event->key) {
+		case KC_C:
+			calc_edit_copy(NULL, calc);
+			break;
+		case KC_V:
+			calc_edit_paste(NULL, calc);
+			break;
+		default:
+			break;
+		}
+	}
 
 	switch (event->key) {
@@ -264,6 +302,65 @@
 }
 
-static char *expr = NULL;
-static ui_entry_t *display;
+/** File / Exit menu entry selected.
+ *
+ * @param mentry Menu entry
+ * @param arg Argument (calc_t *)
+ */
+static void calc_file_exit(ui_menu_entry_t *mentry, void *arg)
+{
+	calc_t *calc = (calc_t *) arg;
+
+	ui_quit(calc->ui);
+}
+
+/** Edit / Copy menu entry selected.
+ *
+ * @param mentry Menu entry
+ * @param arg Argument (calc_t *)
+ */
+static void calc_edit_copy(ui_menu_entry_t *mentry, void *arg)
+{
+	const char *str;
+
+	(void) arg;
+	str = (expr != NULL) ? expr : NULL_DISPLAY;
+
+	(void) clipboard_put_str(str);
+}
+
+/** Edit / Paste menu entry selected.
+ *
+ * @param mentry Menu entry
+ * @param arg Argument (calc_t *)
+ */
+static void calc_edit_paste(ui_menu_entry_t *mentry, void *arg)
+{
+	char *str;
+	char *old;
+	char *cp;
+	errno_t rc;
+
+	(void) arg;
+
+	rc = clipboard_get_str(&str);
+	if (rc != EOK)
+		return;
+
+	/* Make sure string only contains allowed characters */
+	cp = str;
+	while (*cp != '\0') {
+		if (!isdigit(*cp) && *cp != '+' && *cp != '-' &&
+		    *cp != '*' && *cp != '/')
+			return;
+		++cp;
+	}
+
+	/* Update expression */
+	old = expr;
+	expr = str;
+	free(old);
+
+	display_update();
+}
 
 static bool is_digit(char c)
@@ -650,5 +747,5 @@
 }
 
-static errno_t calc_button_create(ui_resource_t *ui_res, ui_fixed_t *fixed,
+static errno_t calc_button_create(calc_t *calc, ui_fixed_t *fixed,
     int x, int y, const char *text, ui_pbutton_cb_t *cb, void *arg,
     ui_pbutton_t **rbutton)
@@ -658,5 +755,5 @@
 	errno_t rc;
 
-	rc = ui_pbutton_create(ui_res, text, &pb);
+	rc = ui_pbutton_create(calc->ui_res, text, &pb);
 	if (rc != EOK) {
 		printf("Error creating button.\n");
@@ -666,8 +763,8 @@
 	ui_pbutton_set_cb(pb, cb, arg);
 
-	rect.p0.x = 10 + 60 * x;
-	rect.p0.y = 90 + 45 * y;
-	rect.p1.x = 60 + 60 * x;
-	rect.p1.y = 125 + 45 * y;
+	rect.p0.x = calc->geom.btn_orig.x + calc->geom.btn_stride.x * x;
+	rect.p0.y = calc->geom.btn_orig.y + calc->geom.btn_stride.y * y;
+	rect.p1.x = rect.p0.x + calc->geom.btn_dim.x;
+	rect.p1.y = rect.p0.y + calc->geom.btn_dim.y;
 	ui_pbutton_set_rect(pb, &rect);
 
@@ -696,5 +793,9 @@
 	ui_wnd_params_t params;
 	ui_window_t *window;
-	gfx_rect_t rect;
+	ui_menu_t *mfile;
+	ui_menu_entry_t *mexit;
+	ui_menu_t *medit;
+	ui_menu_entry_t *mcopy;
+	ui_menu_entry_t *mpaste;
 	calc_t calc;
 	errno_t rc;
@@ -729,6 +830,42 @@
 	params.rect.p0.x = 0;
 	params.rect.p0.y = 0;
-	params.rect.p1.x = 250;
-	params.rect.p1.y = 270;
+
+	if (ui_is_textmode(ui)) {
+		params.rect.p1.x = 38;
+		params.rect.p1.y = 19;
+
+		calc.geom.menubar_rect.p0.x = 1;
+		calc.geom.menubar_rect.p0.y = 2;
+		calc.geom.menubar_rect.p1.x = params.rect.p1.x - 1;
+		calc.geom.menubar_rect.p1.y = 3;
+		calc.geom.entry_rect.p0.x = 4;
+		calc.geom.entry_rect.p0.y = 4;
+		calc.geom.entry_rect.p1.x = 34;
+		calc.geom.entry_rect.p1.y = 5;
+		calc.geom.btn_orig.x = 4;
+		calc.geom.btn_orig.y = 6;
+		calc.geom.btn_dim.x = 6;
+		calc.geom.btn_dim.y = 2;
+		calc.geom.btn_stride.x = 8;
+		calc.geom.btn_stride.y = 3;
+	} else {
+		params.rect.p1.x = 250;
+		params.rect.p1.y = 270;
+
+		calc.geom.menubar_rect.p0.x = 4;
+		calc.geom.menubar_rect.p0.y = 30;
+		calc.geom.menubar_rect.p1.x = params.rect.p1.x - 4;
+		calc.geom.menubar_rect.p1.y = 52;
+		calc.geom.entry_rect.p0.x = 10;
+		calc.geom.entry_rect.p0.y = 51;
+		calc.geom.entry_rect.p1.x = 240;
+		calc.geom.entry_rect.p1.y = 76;
+		calc.geom.btn_orig.x = 10;
+		calc.geom.btn_orig.y = 90;
+		calc.geom.btn_dim.x = 50;
+		calc.geom.btn_dim.y = 35;
+		calc.geom.btn_stride.x = 60;
+		calc.geom.btn_stride.y = 45;
+	}
 
 	rc = ui_window_create(ui, &params, &window);
@@ -742,4 +879,5 @@
 
 	ui_res = ui_window_get_res(window);
+	calc.ui_res = ui_res;
 
 	rc = ui_fixed_create(&fixed);
@@ -749,5 +887,55 @@
 	}
 
-	rc = ui_entry_create(ui_res, NULL_DISPLAY, &display);
+	rc = ui_menu_bar_create(ui, window, &calc.menubar);
+	if (rc != EOK) {
+		printf("Error creating menu bar.\n");
+		return rc;
+	}
+
+	rc = ui_menu_create(calc.menubar, "File", &mfile);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	rc = ui_menu_entry_create(mfile, "Exit", "Alt-F4", &mexit);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	ui_menu_entry_set_cb(mexit, calc_file_exit, (void *) &calc);
+
+	rc = ui_menu_create(calc.menubar, "Edit", &medit);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	rc = ui_menu_entry_create(medit, "Copy", "Ctrl-C", &mcopy);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	ui_menu_entry_set_cb(mcopy, calc_edit_copy, (void *) &calc);
+
+	rc = ui_menu_entry_create(medit, "Paste", "Ctrl-V", &mpaste);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	ui_menu_entry_set_cb(mpaste, calc_edit_paste, (void *) &calc);
+
+	ui_menu_bar_set_rect(calc.menubar, &calc.geom.menubar_rect);
+
+	rc = ui_fixed_add(fixed, ui_menu_bar_ctl(calc.menubar));
+	if (rc != EOK) {
+		printf("Error adding control to layout.\n");
+		return rc;
+	}
+
+	rc = ui_entry_create(window, NULL_DISPLAY, &display);
 	if (rc != EOK) {
 		printf("Error creating text lentry.\n");
@@ -755,10 +943,7 @@
 	}
 
-	rect.p0.x = 15;
-	rect.p0.y = 45;
-	rect.p1.x = 235;
-	rect.p1.y = 70;
-	ui_entry_set_rect(display, &rect);
+	ui_entry_set_rect(display, &calc.geom.entry_rect);
 	ui_entry_set_halign(display, gfx_halign_right);
+	ui_entry_set_read_only(display, true);
 
 	rc = ui_fixed_add(fixed, ui_entry_ctl(display));
@@ -768,80 +953,80 @@
 	}
 
-	rc = calc_button_create(ui_res, fixed, 0, 0, "7", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 0, 0, "7", &calc_pbutton_cb,
 	    (void *) "7", &calc.btn_7);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 1, 0, "8", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 1, 0, "8", &calc_pbutton_cb,
 	    (void *) "8", &calc.btn_8);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 2, 0, "9", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 2, 0, "9", &calc_pbutton_cb,
 	    (void *) "9", &calc.btn_9);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 3, 0, "/", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 3, 0, "/", &calc_pbutton_cb,
 	    (void *) "/", &calc.btn_div);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 0, 1, "4", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 0, 1, "4", &calc_pbutton_cb,
 	    (void *) "4", &calc.btn_4);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 1, 1, "5", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 1, 1, "5", &calc_pbutton_cb,
 	    (void *) "5", &calc.btn_5);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 2, 1, "6", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 2, 1, "6", &calc_pbutton_cb,
 	    (void *) "6", &calc.btn_6);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 3, 1, "*", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 3, 1, "*", &calc_pbutton_cb,
 	    (void *) "*", &calc.btn_mul);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 0, 2, "1", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 0, 2, "1", &calc_pbutton_cb,
 	    (void *) "1", &calc.btn_1);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 1, 2, "2", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 1, 2, "2", &calc_pbutton_cb,
 	    (void *) "2", &calc.btn_2);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 2, 2, "3", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 2, 2, "3", &calc_pbutton_cb,
 	    (void *) "3", &calc.btn_3);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 3, 2, "-", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 3, 2, "-", &calc_pbutton_cb,
 	    (void *) "-", &calc.btn_sub);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 0, 3, "0", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 0, 3, "0", &calc_pbutton_cb,
 	    (void *) "0", &calc.btn_0);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 1, 3, "C", &calc_clear_cb,
+	rc = calc_button_create(&calc, fixed, 1, 3, "C", &calc_clear_cb,
 	    (void *) "C", &calc.btn_clear);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 2, 3, "=", &calc_eval_cb,
+	rc = calc_button_create(&calc, fixed, 2, 3, "=", &calc_eval_cb,
 	    (void *) "=", &calc.btn_eval);
 	if (rc != EOK)
 		return rc;
 
-	rc = calc_button_create(ui_res, fixed, 3, 3, "+", &calc_pbutton_cb,
+	rc = calc_button_create(&calc, fixed, 3, 3, "+", &calc_pbutton_cb,
 	    (void *) "+", &calc.btn_add);
 	if (rc != EOK)
Index: uspace/app/devctl/meson.build
===================================================================
--- uspace/app/devctl/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/devctl/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'device' ]
 src = files('devctl.c')
Index: uspace/app/dnscfg/meson.build
===================================================================
--- uspace/app/dnscfg/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/dnscfg/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2013 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'inet' ]
 src = files('dnscfg.c')
Index: uspace/app/dnsres/meson.build
===================================================================
--- uspace/app/dnsres/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/dnsres/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2013 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'inet' ]
 src = files('dnsres.c')
Index: uspace/app/edit/edit.c
===================================================================
--- uspace/app/edit/edit.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/edit/edit.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -251,5 +251,8 @@
 
 	while (!done) {
-		console_get_event(con, &ev);
+		rc = console_get_event(con, &ev);
+		if (rc != EOK)
+			break;
+
 		pane.rflags = 0;
 
@@ -634,4 +637,5 @@
 	int nc;
 	bool done;
+	errno_t rc;
 
 	asprintf(&str, "%s: %s", prompt, init_value);
@@ -648,5 +652,7 @@
 
 	while (!done) {
-		console_get_event(con, &ev);
+		rc = console_get_event(con, &ev);
+		if (rc != EOK)
+			return NULL;
 
 		if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS) {
Index: uspace/app/fdisk/meson.build
===================================================================
--- uspace/app/fdisk/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/fdisk/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -27,4 +27,4 @@
 #
 
-deps = [ 'clui', 'fdisk' ]
+deps = [ 'clui', 'device', 'fdisk' ]
 src = files('fdisk.c')
Index: uspace/app/fontedit/fontedit.c
===================================================================
--- uspace/app/fontedit/fontedit.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/fontedit/fontedit.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -446,9 +446,10 @@
  * @param fedit Font editor
  * @param x Starting X coordinate
- * @Param y Starting Y coordinate
+ * @param y Starting Y coordinate
+ * @param color Color
  * @param str String
  */
 static errno_t font_edit_paint_preview_str(font_edit_t *fedit,
-    gfx_coord_t x, gfx_coord_t y, const char *str)
+    gfx_coord_t x, gfx_coord_t y, gfx_color_t *color, const char *str)
 {
 	gfx_text_fmt_t fmt;
@@ -456,4 +457,5 @@
 
 	gfx_text_fmt_init(&fmt);
+	fmt.color = color;
 
 	pos.x = x;
@@ -480,25 +482,25 @@
 		goto error;
 
-	rc = font_edit_paint_preview_str(fedit, 20, 20,
+	rc = font_edit_paint_preview_str(fedit, 20, 20, color,
 	    "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
 	if (rc != EOK)
 		goto error;
 
-	rc = font_edit_paint_preview_str(fedit, 20, 40,
+	rc = font_edit_paint_preview_str(fedit, 20, 40, color,
 	    "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG");
 	if (rc != EOK)
 		goto error;
 
-	rc = font_edit_paint_preview_str(fedit, 20, 60,
+	rc = font_edit_paint_preview_str(fedit, 20, 60, color,
 	    "abcdefghijklmnopqrstuvwxyz");
 	if (rc != EOK)
 		goto error;
 
-	rc = font_edit_paint_preview_str(fedit, 20, 80,
+	rc = font_edit_paint_preview_str(fedit, 20, 80, color,
 	    "the quick brown fox jumps over the lazy dog");
 	if (rc != EOK)
 		goto error;
 
-	rc = font_edit_paint_preview_str(fedit, 20, 100,
+	rc = font_edit_paint_preview_str(fedit, 20, 100, color,
 	    "0123456789,./<>?;'\\:\"|[]{}`~!@#$%^&*()-_=+");
 	if (rc != EOK)
Index: uspace/app/gfxdemo/gfxdemo.c
===================================================================
--- uspace/app/gfxdemo/gfxdemo.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/gfxdemo/gfxdemo.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -69,4 +69,7 @@
 
 static bool quit = false;
+static gfx_typeface_t *tface;
+static gfx_font_t *font;
+static gfx_coord_t vpad;
 
 /** Clear screen.
@@ -107,9 +110,153 @@
 }
 
-/** Run rectangle demo on a graphic context.
+/** Initialize demo font.
  *
  * @param gc Graphic context
  * @param w Width
  * @param h Height
+ * @return EOK on success or an error code
+ */
+static errno_t demo_font_init(gfx_context_t *gc, gfx_coord_t w, gfx_coord_t h)
+{
+	gfx_font_info_t *finfo;
+	errno_t rc;
+
+	if (quit)
+		return EOK;
+
+	/* XXX Crude way of detecting text mode */
+	if (w < 256) {
+		/* Create dummy font for text mode */
+		rc = gfx_typeface_create(gc, &tface);
+		if (rc != EOK) {
+			printf("Error creating typeface\n");
+			goto error;
+		}
+
+		rc = gfx_font_create_textmode(tface, &font);
+		if (rc != EOK) {
+			printf("Error creating font\n");
+			goto error;
+		}
+
+		vpad = 0;
+	} else {
+		/* Load font */
+		rc = gfx_typeface_open(gc, "/data/font/helena.tpf", &tface);
+		if (rc != EOK) {
+			printf("Error opening typeface\n");
+			goto error;
+		}
+
+		finfo = gfx_typeface_first_font(tface);
+		if (finfo == NULL) {
+			printf("Typeface contains no font.\n");
+			rc = ENOENT;
+			goto error;
+		}
+
+		rc = gfx_font_open(finfo, &font);
+		if (rc != EOK) {
+			printf("Error opening font.\n");
+			goto error;
+		}
+
+		vpad = 5;
+	}
+
+	return EOK;
+error:
+	if (tface != NULL)
+		gfx_typeface_destroy(tface);
+	return rc;
+}
+
+/** Finalize demo font. */
+static void demo_font_fini(void)
+{
+	if (font == NULL)
+		return;
+
+	gfx_font_close(font);
+	font = NULL;
+
+	gfx_typeface_destroy(tface);
+	tface = NULL;
+}
+
+/** Start a new demo screen.
+ *
+ * Clear the screen, display a status line and set up clipping.
+ *
+ * @param gc Graphic context
+ * @param w Width
+ * @param h Height
+ * @param text Demo screen description
+ * @return EOK on success or an error code
+ */
+static errno_t demo_begin(gfx_context_t *gc, gfx_coord_t w, gfx_coord_t h,
+    const char *text)
+{
+	gfx_text_fmt_t fmt;
+	gfx_font_metrics_t metrics;
+	gfx_coord2_t pos;
+	gfx_color_t *color;
+	gfx_rect_t rect;
+	gfx_coord_t height;
+	errno_t rc;
+
+	rc = gfx_set_clip_rect(gc, NULL);
+	if (rc != EOK)
+		return rc;
+
+	rc = clear_scr(gc, w, h);
+	if (rc != EOK)
+		return rc;
+
+	if (font != NULL) {
+		rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &color);
+		if (rc != EOK)
+			goto error;
+
+		gfx_text_fmt_init(&fmt);
+		fmt.color = color;
+		fmt.halign = gfx_halign_center;
+		fmt.valign = gfx_valign_bottom;
+
+		pos.x = w / 2;
+		pos.y = h - 1;
+		rc = gfx_puttext(font, &pos, &fmt, text);
+		if (rc != EOK) {
+			printf("Error rendering text.\n");
+			gfx_color_delete(color);
+			goto error;
+		}
+
+		gfx_color_delete(color);
+
+		gfx_font_get_metrics(font, &metrics);
+		height = metrics.ascent + metrics.descent + 1;
+	} else {
+		height = 0;
+	}
+
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = w;
+	rect.p1.y = h - height - vpad;
+	rc = gfx_set_clip_rect(gc, &rect);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Run rectangle demo on a graphic context.
+ *
+ * @param gc Graphic context
+ * @param w Width
+ * @param h Height
  */
 static errno_t demo_rects(gfx_context_t *gc, gfx_coord_t w, gfx_coord_t h)
@@ -123,5 +270,5 @@
 		return EOK;
 
-	rc = clear_scr(gc, w, h);
+	rc = demo_begin(gc, w, h, "Rectangle rendering");
 	if (rc != EOK)
 		return rc;
@@ -185,5 +332,5 @@
 		for (j = 0; j < h; j++) {
 			pixelmap_put_pixel(&pixelmap, i, j,
-			    PIXEL(255, (i % 30) < 3 ? 255 : 0,
+			    PIXEL(0, (i % 30) < 3 ? 255 : 0,
 			    (j % 30) < 3 ? 255 : 0, i / 2));
 		}
@@ -221,5 +368,5 @@
 			k = i * i + j * j;
 			pixelmap_put_pixel(&pixelmap, i, j,
-			    PIXEL(255, k, k, k));
+			    PIXEL(0, k, k, k));
 		}
 	}
@@ -256,5 +403,5 @@
 			k = i * i + j * j;
 			pixelmap_put_pixel(&pixelmap, i, j,
-			    k < w * w / 2 ? PIXEL(255, 0, 255, 0) :
+			    k < w * w / 2 ? PIXEL(0, 0, 255, 0) :
 			    PIXEL(0, 255, 0, 255));
 		}
@@ -282,5 +429,5 @@
 		return EOK;
 
-	rc = clear_scr(gc, w, h);
+	rc = demo_begin(gc, w, h, "Bitmap rendering without offset");
 	if (rc != EOK)
 		return rc;
@@ -345,5 +492,5 @@
 		return EOK;
 
-	rc = clear_scr(gc, w, h);
+	rc = demo_begin(gc, w, h, "Bitmap rendering with offset");
 	if (rc != EOK)
 		return rc;
@@ -404,5 +551,5 @@
 		return EOK;
 
-	rc = clear_scr(gc, w, h);
+	rc = demo_begin(gc, w, h, "Bitmap rendering with color key");
 	if (rc != EOK)
 		return rc;
@@ -458,7 +605,4 @@
 	gfx_color_t *color = NULL;
 	gfx_rect_t rect;
-	gfx_typeface_t *tface = NULL;
-	gfx_font_info_t *finfo;
-	gfx_font_t *font = NULL;
 	gfx_coord2_t pos;
 	gfx_text_fmt_t fmt;
@@ -469,24 +613,5 @@
 		return EOK;
 
-	rc = gfx_typeface_open(gc, "/data/font/helena.tpf", &tface);
-	if (rc != EOK) {
-		printf("Error opening typeface\n");
-		goto error;
-	}
-
-	finfo = gfx_typeface_first_font(tface);
-	if (finfo == NULL) {
-		printf("Typeface contains no font.\n");
-		rc = ENOENT;
-		goto error;
-	}
-
-	rc = gfx_font_open(finfo, &font);
-	if (rc != EOK) {
-		printf("Error opening font.\n");
-		goto error;
-	}
-
-	rc = clear_scr(gc, w, h);
+	rc = demo_begin(gc, w, h, "Text rendering");
 	if (rc != EOK)
 		goto error;
@@ -525,7 +650,7 @@
 
 	rect.p0.x = w / 20;
-	rect.p0.y = 2 * h / 15;
+	rect.p0.y = 1 * h / 15;
 	rect.p1.x = w - w / 20;
-	rect.p1.y = 5 * h / 15;
+	rect.p1.y = 4 * h / 15;
 
 	rc = gfx_fill_rect(gc, &rect);
@@ -539,9 +664,6 @@
 		goto error;
 
-	rc = gfx_set_color(gc, color);
-	if (rc != EOK)
-		goto error;
-
 	gfx_text_fmt_init(&fmt);
+	fmt.color = color;
 
 	pos.x = rect.p0.x;
@@ -623,10 +745,8 @@
 			goto error;
 
-		rc = gfx_set_color(gc, color);
-		if (rc != EOK)
-			goto error;
+		fmt.color = color;
 
 		pos.x = w / 20;
-		pos.y = (7 + i) * h / 15;
+		pos.y = (6 + i) * h / 15;
 		rc = gfx_puttext(font, &pos, &fmt, "The quick brown fox jumps over the lazy dog.");
 		if (rc != EOK)
@@ -642,16 +762,10 @@
 	}
 
-	gfx_font_close(font);
-	gfx_typeface_destroy(tface);
 	return EOK;
 error:
-	if (font != NULL)
-		gfx_font_close(font);
-	if (tface != NULL)
-		gfx_typeface_destroy(tface);
 	return rc;
 }
 
-/** Run demo loop on a graphic context.
+/** Run clipping demo on a graphic context.
  *
  * @param gc Graphic context
@@ -659,31 +773,143 @@
  * @param h Height
  */
+static errno_t demo_clip(gfx_context_t *gc, gfx_coord_t w, gfx_coord_t h)
+{
+	gfx_bitmap_t *bitmap;
+	gfx_color_t *color;
+	gfx_bitmap_params_t params;
+	int i, j;
+	gfx_coord2_t offs;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	if (quit)
+		return EOK;
+
+	rc = demo_begin(gc, w, h, "Clipping demonstration");
+	if (rc != EOK)
+		return rc;
+
+	gfx_bitmap_params_init(&params);
+	params.rect.p0.x = 0;
+	params.rect.p0.y = 0;
+	params.rect.p1.x = 40;
+	params.rect.p1.y = 20;
+
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	if (rc != EOK)
+		return rc;
+
+	rc = bitmap_moire(bitmap, 40, 20);
+	if (rc != EOK)
+		goto error;
+
+	for (j = 0; j < 10; j++) {
+		rect.p0.x = w / 8;
+		rect.p0.y = h / 8;
+		rect.p1.x = w * 7 / 8;
+		rect.p1.y = h * 3 / 8;
+
+		rc = gfx_set_clip_rect(gc, &rect);
+		if (rc != EOK)
+			return rc;
+
+		rc = gfx_color_new_rgb_i16(rand() % 0x10000, rand() % 0x10000,
+		    rand() % 0x10000, &color);
+		if (rc != EOK)
+			return rc;
+
+		rc = gfx_set_color(gc, color);
+		if (rc != EOK)
+			return rc;
+
+		for (i = 0; i < 10; i++) {
+			rect.p0.x = rand() % (w - 1);
+			rect.p0.y = rand() % (h - 1);
+			rect.p1.x = rect.p0.x + rand() % (w - 1 - rect.p0.x);
+			rect.p1.y = rect.p0.y + rand() % (h - 1 - rect.p0.y);
+
+			rc = gfx_fill_rect(gc, &rect);
+			if (rc != EOK)
+				return rc;
+		}
+
+		gfx_color_delete(color);
+
+		rect.p0.x = w / 8;
+		rect.p0.y = h * 5 / 8;
+		rect.p1.x = w * 7 / 8;
+		rect.p1.y = h * 7 / 8;
+
+		rc = gfx_set_clip_rect(gc, &rect);
+		if (rc != EOK)
+			return rc;
+
+		for (i = 0; i < 10; i++) {
+			offs.x = rand() % (w - 40);
+			offs.y = rand() % (h - 20);
+
+			rc = gfx_bitmap_render(bitmap, NULL, &offs);
+			if (rc != EOK)
+				goto error;
+		}
+
+		fibril_usleep(500 * 1000);
+
+		if (quit)
+			break;
+	}
+
+	(void) gfx_set_clip_rect(gc, NULL);
+	gfx_bitmap_destroy(bitmap);
+	return EOK;
+error:
+	(void) gfx_set_clip_rect(gc, NULL);
+	gfx_bitmap_destroy(bitmap);
+	return rc;
+}
+
+/** Run demo loop on a graphic context.
+ *
+ * @param gc Graphic context
+ * @param w Width
+ * @param h Height
+ */
 static errno_t demo_loop(gfx_context_t *gc, gfx_coord_t w, gfx_coord_t h)
 {
 	errno_t rc;
+
+	(void) demo_font_init(gc, w, h);
 
 	while (!quit) {
 		rc = demo_rects(gc, w, h);
 		if (rc != EOK)
-			return rc;
+			goto error;
 
 		rc = demo_bitmap(gc, w, h);
 		if (rc != EOK)
-			return rc;
+			goto error;
 
 		rc = demo_bitmap2(gc, w, h);
 		if (rc != EOK)
-			return rc;
+			goto error;
 
 		rc = demo_bitmap_kc(gc, w, h);
 		if (rc != EOK)
-			return rc;
+			goto error;
 
 		rc = demo_text(gc, w, h);
 		if (rc != EOK)
-			return rc;
-	}
-
-	return EOK;
+			goto error;
+
+		rc = demo_clip(gc, w, h);
+		if (rc != EOK)
+			goto error;
+	}
+
+	demo_font_fini();
+	return EOK;
+error:
+	demo_font_fini();
+	return rc;
 }
 
@@ -866,5 +1092,5 @@
 static void print_syntax(void)
 {
-	printf("Syntax: gfxdemo [-d <display>] {canvas|console|display}\n");
+	printf("Syntax: gfxdemo [-d <display>] {console|display|ui}\n");
 }
 
Index: uspace/app/inet/inet.c
===================================================================
--- uspace/app/inet/inet.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/inet/inet.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2013 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -37,4 +37,5 @@
 #include <errno.h>
 #include <inet/addr.h>
+#include <inet/eth_addr.h>
 #include <inet/inetcfg.h>
 #include <io/table.h>
@@ -340,4 +341,5 @@
 	inet_link_info_t linfo;
 	table_t *table = NULL;
+	eth_addr_str_t saddr;
 
 	size_t count;
@@ -368,10 +370,7 @@
 		}
 
-		table_printf(table, "%02x:%02x:%02x:%02x:%02x:%02x\t"
-		    "%s\t" "%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);
+		eth_addr_format(&linfo.mac_addr, &saddr);
+		table_printf(table, "%s\t %s\t %zu\n", saddr.str, linfo.name,
+		    linfo.def_mtu);
 
 		free(linfo.name);
Index: uspace/app/inet/meson.build
===================================================================
--- uspace/app/inet/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/inet/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2012 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'inet' ]
 src = files('inet.c')
Index: uspace/app/init/init.c
===================================================================
--- uspace/app/init/init.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/init/init.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -272,4 +272,6 @@
 }
 
+#ifdef CONFIG_WINSYS
+
 static errno_t display_server(void)
 {
@@ -302,4 +304,6 @@
 	return retval;
 }
+
+#endif
 
 static void getterm(const char *svc, const char *app, bool msg)
@@ -468,4 +472,5 @@
 	init_sysvol();
 
+#ifdef CONFIG_WINSYS
 	if (!config_key_exists("console")) {
 		rc = display_server();
@@ -476,5 +481,5 @@
 		}
 	}
-
+#endif
 	rc = console(HID_INPUT, HID_OUTPUT);
 	if (rc == EOK) {
Index: uspace/app/lprint/meson.build
===================================================================
--- uspace/app/lprint/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/lprint/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'device' ]
 src = files('lprint.c')
Index: uspace/app/mkbd/main.c
===================================================================
--- uspace/app/mkbd/main.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/mkbd/main.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -174,6 +174,6 @@
 	while (true) {
 		cons_event_t ev;
-		bool ok = console_get_event(con, &ev);
-		if (!ok) {
+		errno_t rc = console_get_event(con, &ev);
+		if (rc != EOK) {
 			printf("Connection with console broken: %s.\n",
 			    str_error(errno));
Index: uspace/app/mkmfs/mkmfs.c
===================================================================
--- uspace/app/mkmfs/mkmfs.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/mkmfs/mkmfs.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -480,5 +480,5 @@
 		    MFS_BLOCKSIZE;
 	} else {
-		/*Valid for MFS V2/V3*/
+		/* Valid for MFS V2/V3 */
 		size_t ptrsize;
 		if (sb->fs_version == 2)
Index: uspace/app/modplay/modplay.c
===================================================================
--- uspace/app/modplay/modplay.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/modplay/modplay.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -174,6 +174,10 @@
 	while (true) {
 		timeout = 0;
-		if (console_get_event_timeout(con, &event, &timeout))
+		rc = console_get_event_timeout(con, &event, &timeout);
+		if (rc == EOK)
 			modplay_event(&event);
+		else if (rc != ETIMEOUT)
+			break;
+
 		if (quit)
 			break;
Index: uspace/app/netecho/meson.build
===================================================================
--- uspace/app/netecho/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/netecho/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2016 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'inet' ]
 src = files('comm.c', 'netecho.c')
Index: uspace/app/netecho/netecho.c
===================================================================
--- uspace/app/netecho/netecho.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/netecho/netecho.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -121,4 +121,5 @@
 {
 	cons_event_t ev;
+	errno_t rc;
 
 	printf("Communication started. Press Ctrl-Q to quit.\n");
@@ -128,5 +129,8 @@
 	done = false;
 	while (!done) {
-		console_get_event(con, &ev);
+		rc = console_get_event(con, &ev);
+		if (rc != EOK)
+			break;
+
 		if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS)
 			key_handle(&ev.ev.key);
Index: uspace/app/nterm/meson.build
===================================================================
--- uspace/app/nterm/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/nterm/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2012 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'inet' ]
 src = files('conn.c', 'nterm.c')
Index: uspace/app/nterm/nterm.c
===================================================================
--- uspace/app/nterm/nterm.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/nterm/nterm.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -130,5 +130,8 @@
 	done = false;
 	while (!done) {
-		console_get_event(con, &ev);
+		rc = console_get_event(con, &ev);
+		if (rc != EOK)
+			break;
+
 		if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS)
 			key_handle(&ev.ev.key);
Index: uspace/app/pci/meson.build
===================================================================
--- uspace/app/pci/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/pci/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'device' ]
 src = files('pci.c')
Index: uspace/app/ping/meson.build
===================================================================
--- uspace/app/ping/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/ping/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2012 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'inet' ]
 src = files('ping.c')
Index: uspace/app/ping/ping.c
===================================================================
--- uspace/app/ping/ping.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/ping/ping.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -196,6 +196,10 @@
 	while (true) {
 		cons_event_t ev;
-		if (!console_get_event(con, &ev))
-			break;
+		errno_t rc;
+		rc = console_get_event(con, &ev);
+		if (rc != EOK) {
+			ping_signal_received(RECEIVED_INTERRUPT);
+			break;
+		}
 
 		if ((ev.type == CEV_KEY) && (ev.ev.key.type == KEY_PRESS) &&
Index: uspace/app/pkg/pkg.c
===================================================================
--- uspace/app/pkg/pkg.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/pkg/pkg.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -137,5 +137,5 @@
 		return ENOMEM;
 	}
-	/*XXX error cleanup */
+	/* XXX error cleanup */
 
 	printf("Downloading '%s'.\n", src_uri);
Index: uspace/app/sbi/src/rdata_t.h
===================================================================
--- uspace/app/sbi/src/rdata_t.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/sbi/src/rdata_t.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -137,5 +137,5 @@
 	vc_bool,
 
-	/** Character **/
+	/** Character */
 	vc_char,
 
Index: uspace/app/terminal/terminal.c
===================================================================
--- uspace/app/terminal/terminal.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/terminal/terminal.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2012 Petr Koupy
  * All rights reserved.
@@ -37,4 +37,5 @@
 #include <adt/list.h>
 #include <adt/prodcons.h>
+#include <as.h>
 #include <errno.h>
 #include <fbfont/font-8x16.h>
@@ -42,4 +43,5 @@
 #include <gfx/bitmap.h>
 #include <gfx/context.h>
+#include <gfx/render.h>
 #include <io/con_srv.h>
 #include <io/concaps.h>
@@ -48,4 +50,5 @@
 #include <task.h>
 #include <stdarg.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <str.h>
@@ -85,4 +88,8 @@
 static void term_set_cursor_visibility(con_srv_t *, bool);
 static errno_t term_get_event(con_srv_t *, cons_event_t *);
+static errno_t term_map(con_srv_t *, sysarg_t, sysarg_t, charfield_t **);
+static void term_unmap(con_srv_t *);
+static void term_buf_update(con_srv_t *, sysarg_t, sysarg_t, sysarg_t,
+    sysarg_t);
 
 static con_ops_t con_ops = {
@@ -101,5 +108,8 @@
 	.set_rgb_color = term_set_rgb_color,
 	.set_cursor_visibility = term_set_cursor_visibility,
-	.get_event = term_get_event
+	.get_event = term_get_event,
+	.map = term_map,
+	.unmap = term_unmap,
+	.update = term_buf_update
 };
 
@@ -331,6 +341,6 @@
 
 	bool update = false;
-	sysarg_t sx = 0/*term->widget.hpos*/;
-	sysarg_t sy = 0/*term->widget.vpos*/;
+	sysarg_t sx = 0;
+	sysarg_t sy = 0;
 
 	if (term_update_scroll(term, &pixelmap, sx, sy)) {
@@ -343,5 +353,5 @@
 				charfield_t *back_field =
 				    chargrid_charfield_at(term->backbuf, x, y);
-				bool update = false;
+				bool cupdate = false;
 
 				if ((front_field->flags & CHAR_FLAG_DIRTY) ==
@@ -349,5 +359,5 @@
 					if (front_field->ch != back_field->ch) {
 						back_field->ch = front_field->ch;
-						update = true;
+						cupdate = true;
 					}
 
@@ -355,5 +365,5 @@
 					    back_field->attrs)) {
 						back_field->attrs = front_field->attrs;
-						update = true;
+						cupdate = true;
 					}
 
@@ -361,5 +371,5 @@
 				}
 
-				if (update) {
+				if (cupdate) {
 					term_update_char(term, &pixelmap, sx, sy, x, y);
 					update = true;
@@ -524,4 +534,5 @@
 		term_write_char(term, str_decode(data, &off, size));
 
+	gfx_update(term->gc);
 	*nwritten = size;
 	return EOK;
@@ -533,4 +544,5 @@
 
 	term_update(term);
+	gfx_update(term->gc);
 }
 
@@ -544,4 +556,5 @@
 
 	term_update(term);
+	gfx_update(term->gc);
 }
 
@@ -555,4 +568,5 @@
 
 	term_update(term);
+	gfx_update(term->gc);
 }
 
@@ -626,4 +640,5 @@
 
 	term_update(term);
+	gfx_update(term->gc);
 }
 
@@ -637,4 +652,119 @@
 	free(ev);
 	return EOK;
+}
+
+/** Create shared buffer for efficient rendering.
+ *
+ * @param srv Console server
+ * @param cols Number of columns in buffer
+ * @param rows Number of rows in buffer
+ * @param rbuf Place to store pointer to new sharable buffer
+ *
+ * @return EOK on sucess or an error code
+ */
+static errno_t term_map(con_srv_t *srv, sysarg_t cols, sysarg_t rows,
+    charfield_t **rbuf)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	void *buf;
+
+	fibril_mutex_lock(&term->mtx);
+
+	if (term->ubuf != NULL) {
+		fibril_mutex_unlock(&term->mtx);
+		return EBUSY;
+	}
+
+	buf = as_area_create(AS_AREA_ANY, cols * rows * sizeof(charfield_t),
+	    AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
+	if (buf == AS_MAP_FAILED) {
+		fibril_mutex_unlock(&term->mtx);
+		return ENOMEM;
+	}
+
+	term->ucols = cols;
+	term->urows = rows;
+	term->ubuf = buf;
+	fibril_mutex_unlock(&term->mtx);
+
+	*rbuf = buf;
+	return EOK;
+}
+
+/** Delete shared buffer.
+ *
+ * @param srv Console server
+ */
+static void term_unmap(con_srv_t *srv)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	void *buf;
+
+	fibril_mutex_lock(&term->mtx);
+
+	buf = term->ubuf;
+	term->ubuf = NULL;
+
+	if (buf != NULL)
+		as_area_destroy(buf);
+
+	fibril_mutex_unlock(&term->mtx);
+}
+
+/** Update area of terminal from shared buffer.
+ *
+ * @param srv Console server
+ * @param c0 Column coordinate of top-left corner (inclusive)
+ * @param r0 Row coordinate of top-left corner (inclusive)
+ * @param c1 Column coordinate of bottom-right corner (exclusive)
+ * @param r1 Row coordinate of bottom-right corner (exclusive)
+ */
+static void term_buf_update(con_srv_t *srv, sysarg_t c0, sysarg_t r0,
+    sysarg_t c1, sysarg_t r1)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	charfield_t *ch;
+	sysarg_t col, row;
+
+	fibril_mutex_lock(&term->mtx);
+
+	if (term->ubuf == NULL) {
+		fibril_mutex_unlock(&term->mtx);
+		return;
+	}
+
+	/* Make sure we have meaningful coordinates, within bounds */
+
+	if (c1 > term->ucols)
+		c1 = term->ucols;
+	if (c1 > term->cols)
+		c1 = term->cols;
+	if (c0 >= c1) {
+		fibril_mutex_unlock(&term->mtx);
+		return;
+	}
+	if (r1 > term->urows)
+		r1 = term->urows;
+	if (r1 > term->rows)
+		r1 = term->rows;
+	if (r0 >= r1) {
+		fibril_mutex_unlock(&term->mtx);
+		return;
+	}
+
+	/* Update front buffer from user buffer */
+
+	for (row = r0; row < r1; row++) {
+		for (col = c0; col < c1; col++) {
+			ch = chargrid_charfield_at(term->frontbuf, col, row);
+			*ch = term->ubuf[row * term->ucols + col];
+		}
+	}
+
+	fibril_mutex_unlock(&term->mtx);
+
+	/* Update terminal */
+	term_update(term);
+	gfx_update(term->gc);
 }
 
@@ -688,4 +818,5 @@
 	term->is_focused = true;
 	term_update(term);
+	gfx_update(term->gc);
 }
 
@@ -712,5 +843,5 @@
 	sysarg_t sy = -term->off.y;
 
-	if (event->type == POS_PRESS) {
+	if (event->type == POS_PRESS || event->type == POS_RELEASE) {
 		cevent.type = CEV_POS;
 		cevent.ev.pos.type = event->type;
@@ -731,4 +862,5 @@
 	term->is_focused = false;
 	term_update(term);
+	gfx_update(term->gc);
 }
 
Index: uspace/app/terminal/terminal.h
===================================================================
--- uspace/app/terminal/terminal.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/terminal/terminal.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2012 Petr Koupy
  * All rights reserved.
@@ -85,4 +85,8 @@
 	sysarg_t top_row;
 
+	sysarg_t ucols;
+	sysarg_t urows;
+	charfield_t *ubuf;
+
 	service_id_t dsid;
 	con_srvs_t srvs;
Index: uspace/app/tester/ipc/starve.c
===================================================================
--- uspace/app/tester/ipc/starve.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/tester/ipc/starve.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -57,7 +57,12 @@
 		cons_event_t ev;
 		usec_t timeout = 0;
-		bool has_event = console_get_event_timeout(console, &ev, &timeout);
-		if (has_event && ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS) {
-			TPRINTF("Key %d pressed, terminating.\n", ev.ev.key.key);
+		errno_t rc = console_get_event_timeout(console, &ev, &timeout);
+		if (rc == EOK) {
+			if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS) {
+				TPRINTF("Key %d pressed, terminating.\n", ev.ev.key.key);
+				break;
+			}
+		} else if (rc != ETIMEOUT) {
+			TPRINTF("Got rc=%d, terminating.\n", rc);
 			break;
 		}
Index: uspace/app/tetris/scores.c
===================================================================
--- uspace/app/tetris/scores.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/tetris/scores.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -126,4 +126,5 @@
 	cons_event_t ev;
 	kbd_event_t *kev;
+	errno_t rc;
 
 	clear_screen();
@@ -141,5 +142,6 @@
 	while (true) {
 		console_flush(console);
-		if (!console_get_event(console, &ev))
+		rc = console_get_event(console, &ev);
+		if (rc != EOK)
 			exit(1);
 
Index: uspace/app/tetris/screen.c
===================================================================
--- uspace/app/tetris/screen.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/tetris/screen.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -54,4 +54,5 @@
  */
 
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -71,5 +72,6 @@
 static int isset;               /* true => terminal is in game mode */
 
-static bool use_color;          /* true => use colors */
+static bool use_rgb;          /* true => use RGB colors */
+static bool use_color;          /* true => use indexed colors */
 
 static const struct shape *lastshape;
@@ -91,7 +93,21 @@
 static void start_standout(uint32_t color)
 {
+	uint8_t bg;
+	uint8_t attr;
+
 	console_flush(console);
-	console_set_rgb_color(console, use_color ? color : 0x000000,
-	    0xffffff);
+	if (use_rgb) {
+		console_set_rgb_color(console, color, 0xffffff);
+	} else if (use_color) {
+		bg = 0x00;
+		attr = 0;
+		if ((color & 0xff0000) != 0)
+			bg |= 0x4;
+		if ((color & 0x00ff00) != 0)
+			bg |= 0x2;
+		if ((color & 0x0000ff) != 0)
+			bg |= 0x1;
+		console_set_color(console, bg, 0x00, attr);
+	}
 }
 
@@ -142,13 +158,17 @@
 }
 
-static bool get_display_color_sup(void)
+static void get_display_color_sup(bool *rgb, bool *color)
 {
 	sysarg_t ccap;
 	errno_t rc = console_get_color_cap(console, &ccap);
 
-	if (rc != EOK)
-		return false;
-
-	return ((ccap & CONSOLE_CAP_RGB) == CONSOLE_CAP_RGB);
+	if (rc != EOK) {
+		*rgb = false;
+		*color = false;
+		return;
+	}
+
+	*rgb = ((ccap & CONSOLE_CAP_RGB) == CONSOLE_CAP_RGB);
+	*color = ((ccap & CONSOLE_CAP_INDEXED) == CONSOLE_CAP_INDEXED);
 }
 
@@ -168,5 +188,5 @@
 	}
 
-	use_color = get_display_color_sup();
+	get_display_color_sup(&use_rgb, &use_color);
 
 	if ((Rows < MINROWS) || (Cols < MINCOLS)) {
@@ -340,10 +360,14 @@
 {
 	usec_t timeout = fallrate;
+	errno_t rc;
 
 	while (timeout > 0) {
 		cons_event_t event;
 
-		if (!console_get_event_timeout(console, &event, &timeout))
+		rc = console_get_event_timeout(console, &event, &timeout);
+		if (rc == ETIMEOUT)
 			break;
+		if (rc != EOK)
+			exit(1);
 	}
 }
@@ -354,4 +378,6 @@
 int tgetchar(void)
 {
+	errno_t rc;
+
 	/*
 	 * Reset timeleft to fallrate whenever it is not positive
@@ -376,8 +402,11 @@
 		cons_event_t event;
 
-		if (!console_get_event_timeout(console, &event, &timeleft)) {
+		rc = console_get_event_timeout(console, &event, &timeleft);
+		if (rc == ETIMEOUT) {
 			timeleft = 0;
 			return -1;
 		}
+		if (rc != EOK)
+			exit(1);
 
 		if (event.type == CEV_KEY && event.ev.key.type == KEY_PRESS)
@@ -394,10 +423,14 @@
 {
 	char32_t c = 0;
+	errno_t rc;
 
 	while (c == 0) {
 		cons_event_t event;
 
-		if (!console_get_event(console, &event))
+		rc = console_get_event(console, &event);
+		if (rc == ETIMEOUT)
 			return -1;
+		if (rc != EOK)
+			exit(1);
 
 		if (event.type == CEV_KEY && event.ev.key.type == KEY_PRESS)
Index: uspace/app/tetris/tetris.c
===================================================================
--- uspace/app/tetris/tetris.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/tetris/tetris.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -202,4 +202,6 @@
 	while (true) {
 		int i = getchar();
+		if (i < 0)
+			return 0;
 
 		switch (i) {
Index: uspace/app/top/screen.c
===================================================================
--- uspace/app/top/screen.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/top/screen.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -534,7 +534,12 @@
 /** Get char with timeout
  *
+ * @param sec Timeout in seconds
+ * @param rch Place to store character on success
+ * @return EOK on success, ETIMEOUT on time out, EIO on other error
  */
-int tgetchar(sec_t sec)
-{
+errno_t tgetchar(sec_t sec, int *rch)
+{
+	errno_t rc;
+
 	/*
 	 * Reset timeleft whenever it is not positive.
@@ -548,5 +553,5 @@
 	 * update timeleft so that the next call to tgetchar()
 	 * will not wait as long. If there is no input,
-	 * make timeleft zero and return -1.
+	 * make timeleft zero and return ETIMEOUT.
 	 */
 
@@ -557,8 +562,13 @@
 
 		warning_timeleft -= timeleft;
-		if (!console_get_event_timeout(console, &event, &timeleft)) {
+		rc = console_get_event_timeout(console, &event, &timeleft);
+		if (rc == ETIMEOUT) {
 			timeleft = 0;
-			return -1;
-		}
+			return ETIMEOUT;
+		}
+
+		if (rc != EOK)
+			return EIO;
+
 		warning_timeleft += timeleft;
 
@@ -567,5 +577,6 @@
 	}
 
-	return (int) c;
+	*rch = (int) c;
+	return EOK;
 }
 
Index: uspace/app/top/screen.h
===================================================================
--- uspace/app/top/screen.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/top/screen.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -35,4 +35,5 @@
 #define TOP_SCREEN_H_
 
+#include <errno.h>
 #include <io/console.h>
 #include <io/verify.h>
@@ -47,5 +48,5 @@
     _HELENOS_PRINTF_ATTRIBUTE(1, 2);
 
-extern int tgetchar(sec_t);
+extern errno_t tgetchar(sec_t, int *);
 
 #endif
Index: uspace/app/top/top.c
===================================================================
--- uspace/app/top/top.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/top/top.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -583,4 +583,6 @@
 	data_t data_prev;
 	const char *ret = NULL;
+	errno_t rc;
+	int c;
 
 	screen_init();
@@ -595,7 +597,7 @@
 	/* And paint screen until death */
 	while (true) {
-		int c = tgetchar(UPDATE_INTERVAL);
-
-		if (c < 0) { /* timeout */
+		rc = tgetchar(UPDATE_INTERVAL, &c);
+
+		if (rc == ETIMEOUT) { /* timeout */
 			data_prev = data;
 			if ((ret = read_data(&data)) != NULL) {
@@ -608,4 +610,7 @@
 
 			c = -1;
+		} else if (rc != EOK) {
+			/* Error (e.g. communication with console lost) */
+			goto out;
 		}
 
Index: uspace/app/trace/trace.c
===================================================================
--- uspace/app/trace/trace.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/trace/trace.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -511,4 +511,5 @@
 {
 	cons_event_t event;
+	errno_t rc;
 
 	(void) arg;
@@ -522,5 +523,6 @@
 		fibril_mutex_unlock(&state_lock);
 
-		if (!console_get_event(console, &event))
+		rc = console_get_event(console, &event);
+		if (rc != EOK)
 			return EINVAL;
 
Index: uspace/app/uidemo/uidemo.c
===================================================================
--- uspace/app/uidemo/uidemo.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/uidemo/uidemo.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -37,9 +37,15 @@
 #include <io/pixelmap.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <str.h>
 #include <ui/entry.h>
+#include <ui/filedialog.h>
 #include <ui/fixed.h>
 #include <ui/image.h>
 #include <ui/label.h>
+#include <ui/menubar.h>
+#include <ui/menuentry.h>
+#include <ui/menu.h>
+#include <ui/msgdialog.h>
 #include <ui/pbutton.h>
 #include <ui/resource.h>
@@ -62,4 +68,51 @@
 };
 
+static void checkbox_switched(ui_checkbox_t *, void *, bool);
+
+static ui_checkbox_cb_t checkbox_cb = {
+	.switched = checkbox_switched
+};
+
+static void rb_selected(ui_rbutton_group_t *, void *, void *);
+
+static ui_rbutton_group_cb_t rbutton_group_cb = {
+	.selected = rb_selected
+};
+
+static void slider_moved(ui_slider_t *, void *, gfx_coord_t);
+
+static ui_slider_cb_t slider_cb = {
+	.moved = slider_moved
+};
+
+static void uidemo_file_load(ui_menu_entry_t *, void *);
+static void uidemo_file_message(ui_menu_entry_t *, void *);
+static void uidemo_file_exit(ui_menu_entry_t *, void *);
+
+static void file_dialog_bok(ui_file_dialog_t *, void *, const char *);
+static void file_dialog_bcancel(ui_file_dialog_t *, void *);
+static void file_dialog_close(ui_file_dialog_t *, void *);
+
+static ui_file_dialog_cb_t file_dialog_cb = {
+	.bok = file_dialog_bok,
+	.bcancel = file_dialog_bcancel,
+	.close = file_dialog_close
+};
+
+static void msg_dialog_button(ui_msg_dialog_t *, void *, unsigned);
+static void msg_dialog_close(ui_msg_dialog_t *, void *);
+
+static ui_msg_dialog_cb_t msg_dialog_cb = {
+	.button = msg_dialog_button,
+	.close = msg_dialog_close
+};
+
+/** Horizontal alignment selected by each radio button */
+static const gfx_halign_t uidemo_halign[3] = {
+	gfx_halign_left,
+	gfx_halign_center,
+	gfx_halign_right
+};
+
 /** Window close button was clicked.
  *
@@ -88,11 +141,249 @@
 		if (rc != EOK)
 			printf("Error changing entry text.\n");
-		(void) ui_entry_paint(demo->entry);
 	} else {
 		rc = ui_entry_set_text(demo->entry, "Cancel pressed");
 		if (rc != EOK)
 			printf("Error changing entry text.\n");
-		(void) ui_entry_paint(demo->entry);
-	}
+	}
+}
+
+/** Check box was switched.
+ *
+ * @param checkbox Check box
+ * @param arg Argument (demo)
+ */
+static void checkbox_switched(ui_checkbox_t *checkbox, void *arg, bool enable)
+{
+	ui_demo_t *demo = (ui_demo_t *) arg;
+
+	ui_entry_set_read_only(demo->entry, enable);
+}
+
+/** Radio button was selected.
+ *
+ * @param rbgroup Radio button group
+ * @param garg Group argument (demo)
+ * @param barg Button argument
+ */
+static void rb_selected(ui_rbutton_group_t *rbgroup, void *garg, void *barg)
+{
+	ui_demo_t *demo = (ui_demo_t *) garg;
+	gfx_halign_t halign = *(gfx_halign_t *) barg;
+
+	ui_entry_set_halign(demo->entry, halign);
+	(void) ui_entry_paint(demo->entry);
+}
+
+/** Slider was moved.
+ *
+ * @param slider Slider
+ * @param arg Argument (demo)
+ * @param pos Position
+ */
+static void slider_moved(ui_slider_t *slider, void *arg, gfx_coord_t pos)
+{
+	ui_demo_t *demo = (ui_demo_t *) arg;
+	char *str;
+	errno_t rc;
+	int rv;
+
+	rv = asprintf(&str, "Slider at %d of %d", (int) pos,
+	    ui_slider_length(slider));
+	if (rv < 0) {
+		printf("Out of memory.\n");
+		return;
+	}
+
+	rc = ui_entry_set_text(demo->entry, str);
+	if (rc != EOK)
+		printf("Error changing entry text.\n");
+	(void) ui_entry_paint(demo->entry);
+
+	free(str);
+}
+
+/** Display a message window.
+ *
+ * @param demo UI demo
+ * @param caption Window caption
+ * @param text Message text
+ */
+static void uidemo_show_message(ui_demo_t *demo, const char *caption,
+    const char *text)
+{
+	ui_msg_dialog_params_t mdparams;
+	ui_msg_dialog_t *dialog;
+	errno_t rc;
+
+	ui_msg_dialog_params_init(&mdparams);
+	mdparams.caption = caption;
+	mdparams.text = text;
+
+	rc = ui_msg_dialog_create(demo->ui, &mdparams, &dialog);
+	if (rc != EOK) {
+		printf("Error creating message dialog.\n");
+		return;
+	}
+
+	ui_msg_dialog_set_cb(dialog, &msg_dialog_cb, &demo);
+}
+
+
+/** File/load menu entry selected.
+ *
+ * @param mentry Menu entry
+ * @param arg Argument (demo)
+ */
+static void uidemo_file_load(ui_menu_entry_t *mentry, void *arg)
+{
+	ui_demo_t *demo = (ui_demo_t *) arg;
+	ui_file_dialog_params_t fdparams;
+	ui_file_dialog_t *dialog;
+	errno_t rc;
+
+	ui_file_dialog_params_init(&fdparams);
+	fdparams.caption = "Load File";
+
+	rc = ui_file_dialog_create(demo->ui, &fdparams, &dialog);
+	if (rc != EOK) {
+		printf("Error creating message dialog.\n");
+		return;
+	}
+
+	ui_file_dialog_set_cb(dialog, &file_dialog_cb, demo);
+}
+
+/** File/message menu entry selected.
+ *
+ * @param mentry Menu entry
+ * @param arg Argument (demo)
+ */
+static void uidemo_file_message(ui_menu_entry_t *mentry, void *arg)
+{
+	ui_demo_t *demo = (ui_demo_t *) arg;
+	ui_msg_dialog_params_t mdparams;
+	ui_msg_dialog_t *dialog;
+	errno_t rc;
+
+	ui_msg_dialog_params_init(&mdparams);
+	mdparams.caption = "Message For You";
+	mdparams.text = "Hello, world!";
+
+	rc = ui_msg_dialog_create(demo->ui, &mdparams, &dialog);
+	if (rc != EOK) {
+		printf("Error creating message dialog.\n");
+		return;
+	}
+
+	ui_msg_dialog_set_cb(dialog, &msg_dialog_cb, &demo);
+}
+
+/** File/exit menu entry selected.
+ *
+ * @param mentry Menu entry
+ * @param arg Argument (demo)
+ */
+static void uidemo_file_exit(ui_menu_entry_t *mentry, void *arg)
+{
+	ui_demo_t *demo = (ui_demo_t *) arg;
+
+	ui_quit(demo->ui);
+}
+
+/** File dialog OK button press.
+ *
+ * @param dialog File dialog
+ * @param arg Argument (ui_demo_t *)
+ * @param fname File name
+ */
+static void file_dialog_bok(ui_file_dialog_t *dialog, void *arg,
+    const char *fname)
+{
+	ui_demo_t *demo = (ui_demo_t *) arg;
+	char buf[128];
+	char *p;
+	FILE *f;
+
+	ui_file_dialog_destroy(dialog);
+
+	f = fopen(fname, "rt");
+	if (f == NULL) {
+		uidemo_show_message(demo, "Error", "Error opening file.");
+		return;
+	}
+
+	p = fgets(buf, sizeof(buf), f);
+	if (p == NULL) {
+		uidemo_show_message(demo, "Error", "Error reading file.");
+		fclose(f);
+		return;
+	}
+
+	/* Cut string off at the first non-printable character */
+	p = buf;
+	while (*p != '\0') {
+		if (*p < ' ') {
+			*p = '\0';
+			break;
+		}
+		++p;
+	}
+
+	ui_entry_set_text(demo->entry, buf);
+	fclose(f);
+}
+
+/** File dialog cancel button press.
+ *
+ * @param dialog File dialog
+ * @param arg Argument (ui_demo_t *)
+ */
+static void file_dialog_bcancel(ui_file_dialog_t *dialog, void *arg)
+{
+	ui_demo_t *demo = (ui_demo_t *) arg;
+
+	(void) demo;
+	ui_file_dialog_destroy(dialog);
+}
+
+/** Message dialog close request.
+ *
+ * @param dialog File dialog
+ * @param arg Argument (ui_demo_t *)
+ */
+static void file_dialog_close(ui_file_dialog_t *dialog, void *arg)
+{
+	ui_demo_t *demo = (ui_demo_t *) arg;
+
+	(void) demo;
+	ui_file_dialog_destroy(dialog);
+}
+
+/** Message dialog button press.
+ *
+ * @param dialog Message dialog
+ * @param arg Argument (ui_demo_t *)
+ * @param bnum Button number
+ */
+static void msg_dialog_button(ui_msg_dialog_t *dialog, void *arg,
+    unsigned bnum)
+{
+	ui_demo_t *demo = (ui_demo_t *) arg;
+
+	(void) demo;
+	ui_msg_dialog_destroy(dialog);
+}
+
+/** Message dialog close request.
+ *
+ * @param dialog Message dialog
+ * @param arg Argument (ui_demo_t *)
+ */
+static void msg_dialog_close(ui_msg_dialog_t *dialog, void *arg)
+{
+	ui_demo_t *demo = (ui_demo_t *) arg;
+
+	(void) demo;
+	ui_msg_dialog_destroy(dialog);
 }
 
@@ -110,4 +401,11 @@
 	gfx_bitmap_t *bitmap;
 	gfx_coord2_t off;
+	ui_menu_entry_t *mmsg;
+	ui_menu_entry_t *mload;
+	ui_menu_entry_t *mfoo;
+	ui_menu_entry_t *mbar;
+	ui_menu_entry_t *mfoobar;
+	ui_menu_entry_t *mexit;
+	ui_menu_entry_t *mabout;
 	errno_t rc;
 
@@ -117,15 +415,24 @@
 		return rc;
 	}
+
+	memset((void *) &demo, 0, sizeof(demo));
+	demo.ui = ui;
 
 	ui_wnd_params_init(&params);
 	params.caption = "UI Demo";
 	params.style |= ui_wds_resizable;
-	params.rect.p0.x = 0;
-	params.rect.p0.y = 0;
-	params.rect.p1.x = 220;
-	params.rect.p1.y = 180;
-
-	memset((void *) &demo, 0, sizeof(demo));
-	demo.ui = ui;
+
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		params.rect.p0.x = 0;
+		params.rect.p0.y = 0;
+		params.rect.p1.x = 80;
+		params.rect.p1.y = 25;
+	} else {
+		params.rect.p0.x = 0;
+		params.rect.p0.y = 0;
+		params.rect.p1.x = 220;
+		params.rect.p1.y = 350;
+	}
 
 	rc = ui_window_create(ui, &params, &window);
@@ -147,4 +454,136 @@
 	}
 
+	rc = ui_menu_bar_create(ui, window, &demo.mbar);
+	if (rc != EOK) {
+		printf("Error creating menu bar.\n");
+		return rc;
+	}
+
+	rc = ui_menu_create(demo.mbar, "File", &demo.mfile);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	rc = ui_menu_entry_create(demo.mfile, "Message", "", &mmsg);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	ui_menu_entry_set_cb(mmsg, uidemo_file_message, (void *) &demo);
+
+	rc = ui_menu_entry_create(demo.mfile, "Load", "", &mload);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	ui_menu_entry_set_cb(mload, uidemo_file_load, (void *) &demo);
+
+	rc = ui_menu_entry_create(demo.mfile, "Foo", "Ctrl-Alt-Del", &mfoo);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	rc = ui_menu_entry_create(demo.mfile, "Bar", "", &mbar);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	rc = ui_menu_entry_create(demo.mfile, "Foobar", "", &mfoobar);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	rc = ui_menu_entry_sep_create(demo.mfile, &mexit);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	rc = ui_menu_entry_create(demo.mfile, "Exit", "Alt-F4", &mexit);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	ui_menu_entry_set_cb(mexit, uidemo_file_exit, (void *) &demo);
+
+	rc = ui_menu_create(demo.mbar, "Edit", &demo.medit);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	rc = ui_menu_create(demo.mbar, "Preferences", &demo.mpreferences);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	rc = ui_menu_create(demo.mbar, "Help", &demo.mhelp);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	rc = ui_menu_entry_create(demo.mhelp, "About", "Ctrl-H, F1", &mabout);
+	if (rc != EOK) {
+		printf("Error creating menu.\n");
+		return rc;
+	}
+
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		rect.p0.x = 1;
+		rect.p0.y = 2;
+		rect.p1.x = 79;
+		rect.p1.y = 3;
+	} else {
+		rect.p0.x = 4;
+		rect.p0.y = 30;
+		rect.p1.x = 216;
+		rect.p1.y = 52;
+	}
+	ui_menu_bar_set_rect(demo.mbar, &rect);
+
+	rc = ui_fixed_add(demo.fixed, ui_menu_bar_ctl(demo.mbar));
+	if (rc != EOK) {
+		printf("Error adding control to layout.\n");
+		return rc;
+	}
+
+	rc = ui_entry_create(window, "", &demo.entry);
+	if (rc != EOK) {
+		printf("Error creating entry.\n");
+		return rc;
+	}
+
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		rect.p0.x = 20;
+		rect.p0.y = 4;
+		rect.p1.x = 60;
+		rect.p1.y = 5;
+	} else {
+		rect.p0.x = 15;
+		rect.p0.y = 53;
+		rect.p1.x = 205;
+		rect.p1.y = 78;
+	}
+
+	ui_entry_set_rect(demo.entry, &rect);
+	ui_entry_set_halign(demo.entry, gfx_halign_center);
+
+	rc = ui_fixed_add(demo.fixed, ui_entry_ctl(demo.entry));
+	if (rc != EOK) {
+		printf("Error adding control to layout.\n");
+		return rc;
+	}
+
 	rc = ui_label_create(ui_res, "Text label", &demo.label);
 	if (rc != EOK) {
@@ -153,8 +592,17 @@
 	}
 
-	rect.p0.x = 60;
-	rect.p0.y = 37;
-	rect.p1.x = 160;
-	rect.p1.y = 50;
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		rect.p0.x = 20;
+		rect.p0.y = 6;
+		rect.p1.x = 60;
+		rect.p1.y = 7;
+	} else {
+		rect.p0.x = 60;
+		rect.p0.y = 88;
+		rect.p1.x = 160;
+		rect.p1.y = 101;
+	}
+
 	ui_label_set_rect(demo.label, &rect);
 	ui_label_set_halign(demo.label, gfx_halign_center);
@@ -174,8 +622,17 @@
 	ui_pbutton_set_cb(demo.pb1, &pbutton_cb, (void *) &demo);
 
-	rect.p0.x = 15;
-	rect.p0.y = 70;
-	rect.p1.x = 105;
-	rect.p1.y = 98;
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		rect.p0.x = 20;
+		rect.p0.y = 8;
+		rect.p1.x = 30;
+		rect.p1.y = 9;
+	} else {
+		rect.p0.x = 15;
+		rect.p0.y = 111;
+		rect.p1.x = 105;
+		rect.p1.y = 139;
+	}
+
 	ui_pbutton_set_rect(demo.pb1, &rect);
 
@@ -196,30 +653,19 @@
 	ui_pbutton_set_cb(demo.pb2, &pbutton_cb, (void *) &demo);
 
-	rect.p0.x = 115;
-	rect.p0.y = 70;
-	rect.p1.x = 205;
-	rect.p1.y = 98;
+	if (ui_is_textmode(ui)) {
+		rect.p0.x = 50;
+		rect.p0.y = 8;
+		rect.p1.x = 60;
+		rect.p1.y = 9;
+	} else {
+		rect.p0.x = 115;
+		rect.p0.y = 111;
+		rect.p1.x = 205;
+		rect.p1.y = 139;
+	}
+
 	ui_pbutton_set_rect(demo.pb2, &rect);
 
 	rc = ui_fixed_add(demo.fixed, ui_pbutton_ctl(demo.pb2));
-	if (rc != EOK) {
-		printf("Error adding control to layout.\n");
-		return rc;
-	}
-
-	rc = ui_entry_create(ui_res, "", &demo.entry);
-	if (rc != EOK) {
-		printf("Error creating entry.\n");
-		return rc;
-	}
-
-	rect.p0.x = 15;
-	rect.p0.y = 110;
-	rect.p1.x = 205;
-	rect.p1.y = 135;
-	ui_entry_set_rect(demo.entry, &rect);
-	ui_entry_set_halign(demo.entry, gfx_halign_center);
-
-	rc = ui_fixed_add(demo.fixed, ui_entry_ctl(demo.entry));
 	if (rc != EOK) {
 		printf("Error adding control to layout.\n");
@@ -248,5 +694,5 @@
 
 	off.x = 15;
-	off.y = 145;
+	off.y = 155;
 	gfx_rect_translate(&off, &bparams.rect, &rect);
 
@@ -258,4 +704,111 @@
 
 	rc = ui_fixed_add(demo.fixed, ui_image_ctl(demo.image));
+	if (rc != EOK) {
+		printf("Error adding control to layout.\n");
+		return rc;
+	}
+
+	rc = ui_checkbox_create(ui_res, "Read only", &demo.checkbox);
+	if (rc != EOK) {
+		printf("Error creating check box.\n");
+		return rc;
+	}
+
+	ui_checkbox_set_cb(demo.checkbox, &checkbox_cb, (void *) &demo);
+
+	rect.p0.x = 15;
+	rect.p0.y = 190;
+	rect.p1.x = 140;
+	rect.p1.y = 210;
+	ui_checkbox_set_rect(demo.checkbox, &rect);
+
+	rc = ui_fixed_add(demo.fixed, ui_checkbox_ctl(demo.checkbox));
+	if (rc != EOK) {
+		printf("Error adding control to layout.\n");
+		return rc;
+	}
+
+	rc = ui_rbutton_group_create(ui_res, &demo.rbgroup);
+	if (rc != EOK) {
+		printf("Error creating radio button group.\n");
+		return rc;
+	}
+
+	rc = ui_rbutton_create(demo.rbgroup, "Left", (void *) &uidemo_halign[0],
+	    &demo.rbleft);
+	if (rc != EOK) {
+		printf("Error creating radio button.\n");
+		return rc;
+	}
+
+	ui_rbutton_group_set_cb(demo.rbgroup, &rbutton_group_cb,
+	    (void *) &demo);
+
+	rect.p0.x = 15;
+	rect.p0.y = 220;
+	rect.p1.x = 140;
+	rect.p1.y = 240;
+	ui_rbutton_set_rect(demo.rbleft, &rect);
+
+	rc = ui_fixed_add(demo.fixed, ui_rbutton_ctl(demo.rbleft));
+	if (rc != EOK) {
+		printf("Error adding control to layout.\n");
+		return rc;
+	}
+
+	rc = ui_rbutton_create(demo.rbgroup, "Center", (void *) &uidemo_halign[1],
+	    &demo.rbcenter);
+	if (rc != EOK) {
+		printf("Error creating radio button.\n");
+		return rc;
+	}
+
+	rect.p0.x = 15;
+	rect.p0.y = 250;
+	rect.p1.x = 140;
+	rect.p1.y = 270;
+	ui_rbutton_set_rect(demo.rbcenter, &rect);
+	ui_rbutton_select(demo.rbcenter);
+
+	rc = ui_fixed_add(demo.fixed, ui_rbutton_ctl(demo.rbcenter));
+	if (rc != EOK) {
+		printf("Error adding control to layout.\n");
+		return rc;
+	}
+
+	rc = ui_rbutton_create(demo.rbgroup, "Right", (void *) &uidemo_halign[2],
+	    &demo.rbright);
+	if (rc != EOK) {
+		printf("Error creating radio button.\n");
+		return rc;
+	}
+
+	rect.p0.x = 15;
+	rect.p0.y = 280;
+	rect.p1.x = 140;
+	rect.p1.y = 300;
+	ui_rbutton_set_rect(demo.rbright, &rect);
+
+	rc = ui_fixed_add(demo.fixed, ui_rbutton_ctl(demo.rbright));
+	if (rc != EOK) {
+		printf("Error adding control to layout.\n");
+		return rc;
+	}
+
+	rc = ui_slider_create(ui_res, "Slide!", &demo.slider);
+	if (rc != EOK) {
+		printf("Error creating button.\n");
+		return rc;
+	}
+
+	ui_slider_set_cb(demo.slider, &slider_cb, (void *) &demo);
+
+	rect.p0.x = 15;
+	rect.p0.y = 310;
+	rect.p1.x = 130;
+	rect.p1.y = 330;
+	ui_slider_set_rect(demo.slider, &rect);
+
+	rc = ui_fixed_add(demo.fixed, ui_slider_ctl(demo.slider));
 	if (rc != EOK) {
 		printf("Error adding control to layout.\n");
Index: uspace/app/uidemo/uidemo.h
===================================================================
--- uspace/app/uidemo/uidemo.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/uidemo/uidemo.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,8 +38,13 @@
 
 #include <display.h>
+#include <ui/checkbox.h>
 #include <ui/entry.h>
 #include <ui/fixed.h>
 #include <ui/label.h>
+#include <ui/menu.h>
+#include <ui/menubar.h>
 #include <ui/pbutton.h>
+#include <ui/rbutton.h>
+#include <ui/slider.h>
 #include <ui/ui.h>
 #include <ui/window.h>
@@ -50,4 +55,9 @@
 	ui_window_t *window;
 	ui_fixed_t *fixed;
+	ui_menu_bar_t *mbar;
+	ui_menu_t *mfile;
+	ui_menu_t *medit;
+	ui_menu_t *mpreferences;
+	ui_menu_t *mhelp;
 	ui_entry_t *entry;
 	ui_image_t *image;
@@ -55,4 +65,10 @@
 	ui_pbutton_t *pb1;
 	ui_pbutton_t *pb2;
+	ui_checkbox_t *checkbox;
+	ui_rbutton_group_t *rbgroup;
+	ui_rbutton_t *rbleft;
+	ui_rbutton_t *rbcenter;
+	ui_rbutton_t *rbright;
+	ui_slider_t *slider;
 } ui_demo_t;
 
Index: uspace/app/usbinfo/info.c
===================================================================
--- uspace/app/usbinfo/info.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/usbinfo/info.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -298,5 +298,5 @@
 void dump_strings(usb_device_t *usb_dev)
 {
-	/* Find used indexes. Devices with more than 64 strings are very rare.*/
+	/* Find used indexes. Devices with more than 64 strings are very rare. */
 	uint64_t str_mask = 0;
 	find_string_indexes_callback(
Index: uspace/app/vol/meson.build
===================================================================
--- uspace/app/vol/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/vol/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'device' ]
 src = files('vol.c')
Index: uspace/app/websrv/meson.build
===================================================================
--- uspace/app/websrv/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/app/websrv/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2010 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'inet' ]
 src = files('websrv.c')
Index: uspace/drv/audio/sb16/registers.h
===================================================================
--- uspace/drv/audio/sb16/registers.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/audio/sb16/registers.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -56,11 +56,11 @@
 	ioport8_t fm_data2;
 	ioport8_t dsp_data_read;
-	ioport8_t __reserved2; /*0xb*/
+	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 __reserved3; /* 0xd */
 	ioport8_t dsp_read_status; /* Bit 7 */
 #define DSP_READ_READY (1 << 7)
-	ioport8_t dma16_ack; /*0xf*/
+	ioport8_t dma16_ack; /* 0xf */
 	ioport8_t cd_command_data;
 	ioport8_t cd_status;
Index: uspace/drv/block/ahci/ahci.c
===================================================================
--- uspace/drv/block/ahci/ahci.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/block/ahci/ahci.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -131,7 +131,7 @@
 static int sata_devices_count = 0;
 
-/*----------------------------------------------------------------------------*/
-/*-- AHCI Interface ----------------------------------------------------------*/
-/*----------------------------------------------------------------------------*/
+/*
+ * AHCI Interface
+ */
 
 static ahci_iface_t ahci_interface = {
@@ -298,7 +298,7 @@
 }
 
-/*----------------------------------------------------------------------------*/
-/*-- AHCI Commands -----------------------------------------------------------*/
-/*----------------------------------------------------------------------------*/
+/*
+ * AHCI Commands
+ */
 
 /** Wait for interrupt event.
@@ -842,7 +842,7 @@
 }
 
-/*----------------------------------------------------------------------------*/
-/*-- Interrupts handling -----------------------------------------------------*/
-/*----------------------------------------------------------------------------*/
+/*
+ * Interrupts handling
+ */
 
 static irq_pio_range_t ahci_ranges[] = {
@@ -919,7 +919,7 @@
 }
 
-/*----------------------------------------------------------------------------*/
-/*-- AHCI and SATA device creating and initializing routines -----------------*/
-/*----------------------------------------------------------------------------*/
+/*
+ * AHCI and SATA device creating and initializing routines
+ */
 
 /** Allocate SATA device structure with buffers for hardware.
@@ -1273,7 +1273,7 @@
 }
 
-/*----------------------------------------------------------------------------*/
-/*-- Helpers and utilities ---------------------------------------------------*/
-/*----------------------------------------------------------------------------*/
+/*
+ * Helpers and utilities
+ */
 
 /** Convert SATA model name
@@ -1313,7 +1313,7 @@
 }
 
-/*----------------------------------------------------------------------------*/
-/*-- AHCI Main routine -------------------------------------------------------*/
-/*----------------------------------------------------------------------------*/
+/*
+ * AHCI Main routine
+ */
 
 int main(int argc, char *argv[])
Index: uspace/drv/block/ahci/ahci_hw.h
===================================================================
--- uspace/drv/block/ahci/ahci_hw.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/block/ahci/ahci_hw.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -36,14 +36,14 @@
 #include <stdint.h>
 
-/*----------------------------------------------------------------------------*/
-/*-- AHCI standard constants -------------------------------------------------*/
-/*----------------------------------------------------------------------------*/
+/*
+ * AHCI standard constants
+ */
 
 /** AHCI standard 1.3 - maximum ports. */
 #define AHCI_MAX_PORTS  32
 
-/*----------------------------------------------------------------------------*/
-/*-- AHCI PCI Registers ------------------------------------------------------*/
-/*----------------------------------------------------------------------------*/
+/*
+ * AHCI PCI Registers
+ */
 
 /** AHCI PCI register Identifiers offset. */
@@ -296,7 +296,7 @@
 } ahci_pcireg_mlat_t;
 
-/*----------------------------------------------------------------------------*/
-/*-- AHCI Memory Registers ---------------------------------------------------*/
-/*----------------------------------------------------------------------------*/
+/*
+ * AHCI Memory Registers
+ */
 
 /** Number of pages for ahci memory registers. */
Index: uspace/drv/block/ahci/ahci_sata.h
===================================================================
--- uspace/drv/block/ahci/ahci_sata.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/block/ahci/ahci_sata.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -36,7 +36,7 @@
 #include <stdint.h>
 
-/*----------------------------------------------------------------------------*/
-/*-- SATA Buffer Lengths -----------------------------------------------------*/
-/*----------------------------------------------------------------------------*/
+/*
+ * SATA Buffer Lengths
+ */
 
 /** Default sector size in bytes. */
@@ -49,7 +49,7 @@
 #define SATA_IDENTIFY_DEVICE_BUFFER_LENGTH  512
 
-/*----------------------------------------------------------------------------*/
-/*-- SATA Fis Frames ---------------------------------------------------------*/
-/*----------------------------------------------------------------------------*/
+/*
+ * SATA Fis Frames
+ */
 
 /** Sata FIS Type number. */
@@ -130,7 +130,7 @@
 } sata_ncq_command_frame_t;
 
-/*----------------------------------------------------------------------------*/
-/*-- SATA Identify device ----------------------------------------------------*/
-/*----------------------------------------------------------------------------*/
+/*
+ * SATA Identify device
+ */
 
 /** Data returned from identify device and identify packet device command. */
Index: uspace/drv/bus/isa/i8237.c
===================================================================
--- uspace/drv/bus/isa/i8237.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/isa/i8237.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -198,5 +198,5 @@
 	.channels = {
 		/* The first chip 8-bit */
-		{ /* Channel 0 - Unusable*/
+		{ /* Channel 0 - Unusable */
 			.offset_reg_address = (uint8_t *) 0x00,
 			.size_reg_address = (uint8_t *) 0x01,
Index: uspace/drv/bus/usb/ehci/ehci_batch.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_batch.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/ehci/ehci_batch.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -138,5 +138,5 @@
 	memcpy(ehci_batch->setup_buffer, ehci_batch->base.setup.buffer, setup_size);
 
-	/* Generic data already prepared*/
+	/* Generic data already prepared */
 	ehci_batch->data_buffer = ehci_batch->base.dma_buffer.virt;
 
Index: uspace/drv/bus/usb/ehci/ehci_rh.c
===================================================================
--- uspace/drv/bus/usb/ehci/ehci_rh.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/ehci/ehci_rh.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -86,5 +86,5 @@
 	instance->hub_descriptor.header.max_current = 0;
 
-	/* Device removable and some legacy 1.0 stuff*/
+	/* Device removable and some legacy 1.0 stuff */
 	instance->hub_descriptor.rempow[0] = 0xff;
 	instance->hub_descriptor.rempow[1] = 0xff;
@@ -410,5 +410,5 @@
 	/* Enabled features to clear: see page 269 of USB specs */
 	switch (feature) {
-	case USB_HUB_FEATURE_PORT_POWER:          /*8*/
+	case USB_HUB_FEATURE_PORT_POWER:          /* 8 */
 		usb_log_debug2("RH(%p-%u): Clear port power.", hub, port);
 		EHCI_CLR(hub->registers->portsc[port],
@@ -416,5 +416,5 @@
 		return EOK;
 
-	case USB2_HUB_FEATURE_PORT_ENABLE:         /*1*/
+	case USB2_HUB_FEATURE_PORT_ENABLE:         /* 1 */
 		usb_log_debug2("RH(%p-%u): Clear port enable.", hub, port);
 		EHCI_CLR(hub->registers->portsc[port],
@@ -422,5 +422,5 @@
 		return EOK;
 
-	case USB2_HUB_FEATURE_PORT_SUSPEND:        /*2*/
+	case USB2_HUB_FEATURE_PORT_SUSPEND:        /* 2 */
 		usb_log_debug2("RH(%p-%u): Clear port suspend.", hub, port);
 		/* If not in suspend it's noop */
@@ -434,5 +434,5 @@
 		return delayed_job(stop_resume, hub, port);
 
-	case USB_HUB_FEATURE_C_PORT_CONNECTION:   /*16*/
+	case USB_HUB_FEATURE_C_PORT_CONNECTION:   /* 16 */
 		usb_log_debug2("RH(%p-%u): Clear port connection change.",
 		    hub, port);
@@ -440,5 +440,5 @@
 		    USB_PORTSC_CONNECT_CH_FLAG);
 		return EOK;
-	case USB2_HUB_FEATURE_C_PORT_ENABLE:       /*17*/
+	case USB2_HUB_FEATURE_C_PORT_ENABLE:       /* 17 */
 		usb_log_debug2("RH(%p-%u): Clear port enable change.",
 		    hub, port);
@@ -446,5 +446,5 @@
 		    USB_PORTSC_CONNECT_CH_FLAG);
 		return EOK;
-	case USB_HUB_FEATURE_C_PORT_OVER_CURRENT: /*19*/
+	case USB_HUB_FEATURE_C_PORT_OVER_CURRENT: /* 19 */
 		usb_log_debug2("RH(%p-%u): Clear port OC change.",
 		    hub, port);
@@ -452,10 +452,10 @@
 		    USB_PORTSC_OC_CHANGE_FLAG);
 		return EOK;
-	case USB2_HUB_FEATURE_C_PORT_SUSPEND:      /*18*/
+	case USB2_HUB_FEATURE_C_PORT_SUSPEND:      /* 18 */
 		usb_log_debug2("RH(%p-%u): Clear port suspend change.",
 		    hub, port);
 		hub->resume_flag[port] = false;
 		return EOK;
-	case USB_HUB_FEATURE_C_PORT_RESET:        /*20*/
+	case USB_HUB_FEATURE_C_PORT_RESET:        /* 20 */
 		usb_log_debug2("RH(%p-%u): Clear port reset change.",
 		    hub, port);
@@ -487,15 +487,15 @@
 	const unsigned feature = uint16_usb2host(setup_packet->value);
 	switch (feature) {
-	case USB2_HUB_FEATURE_PORT_ENABLE:  /*1*/
+	case USB2_HUB_FEATURE_PORT_ENABLE:  /* 1 */
 		usb_log_debug2("RH(%p-%u): Set port enable.", hub, port);
 		EHCI_SET(hub->registers->portsc[port],
 		    USB_PORTSC_ENABLED_FLAG);
 		return EOK;
-	case USB2_HUB_FEATURE_PORT_SUSPEND: /*2*/
+	case USB2_HUB_FEATURE_PORT_SUSPEND: /* 2 */
 		usb_log_debug2("RH(%p-%u): Set port suspend.", hub, port);
 		EHCI_SET(hub->registers->portsc[port],
 		    USB_PORTSC_SUSPEND_FLAG);
 		return EOK;
-	case USB_HUB_FEATURE_PORT_RESET:   /*4*/
+	case USB_HUB_FEATURE_PORT_RESET:   /* 4 */
 		usb_log_debug2("RH(%p-%u): Set port reset.", hub, port);
 		EHCI_SET(hub->registers->portsc[port],
@@ -503,5 +503,5 @@
 		//TODO: What if creating the delayed job fails?
 		return delayed_job(stop_reset, hub, port);
-	case USB_HUB_FEATURE_PORT_POWER:   /*8*/
+	case USB_HUB_FEATURE_PORT_POWER:   /* 8 */
 		usb_log_debug2("RH(%p-%u): Set port power.", hub, port);
 		EHCI_SET(hub->registers->portsc[port],
Index: uspace/drv/bus/usb/ehci/endpoint_list.h
===================================================================
--- uspace/drv/bus/usb/ehci/endpoint_list.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/ehci/endpoint_list.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -42,5 +42,5 @@
 #include "hw_struct/queue_head.h"
 
-/** Structure maintains both EHCI queue and software list of active endpoints.*/
+/** Structure maintains both EHCI queue and software list of active endpoints. */
 typedef struct endpoint_list {
 	/** Guard against add/remove races */
Index: uspace/drv/bus/usb/ehci/hc.c
===================================================================
--- uspace/drv/bus/usb/ehci/hc.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/ehci/hc.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -358,5 +358,5 @@
 		fibril_mutex_lock(&hc->guard);
 
-		usb_log_debug2("HC(%p): Scanning %lu pending endpoints", hc,
+		usb_log_debug2("HC(%p): Scanning %zu pending endpoints", hc,
 		    list_count(&hc->pending_endpoints));
 		list_foreach_safe(hc->pending_endpoints, current, next) {
Index: uspace/drv/bus/usb/ohci/endpoint_list.h
===================================================================
--- uspace/drv/bus/usb/ohci/endpoint_list.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/ohci/endpoint_list.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -44,5 +44,5 @@
 #include "hw_struct/endpoint_descriptor.h"
 
-/** Structure maintains both OHCI queue and software list of active endpoints.*/
+/** Structure maintains both OHCI queue and software list of active endpoints. */
 typedef struct endpoint_list {
 	/** Guard against add/remove races */
Index: uspace/drv/bus/usb/ohci/hc.c
===================================================================
--- uspace/drv/bus/usb/ohci/hc.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/ohci/hc.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -192,5 +192,5 @@
 {
 	assert(instance);
-	/* TODO: implement*/
+	/* TODO: implement */
 	return ENOTSUP;
 }
@@ -595,5 +595,5 @@
 	}
 
-	/*Init HCCA */
+	/* Init HCCA */
 	instance->hcca = hcca_get();
 	if (instance->hcca == NULL)
Index: uspace/drv/bus/usb/ohci/ohci_bus.h
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_bus.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/ohci/ohci_bus.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -63,5 +63,5 @@
 	dma_buffer_t dma_buffer;
 
-	/** Link in endpoint_list*/
+	/** Link in endpoint_list */
 	link_t eplist_link;
 	/** Link in pending_endpoints */
Index: uspace/drv/bus/usb/ohci/ohci_regs.h
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_regs.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/ohci/ohci_regs.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -261,7 +261,7 @@
 /** Number of downstream ports, max 15 */
 #define RHDA_NDS_MASK  (0xff)
-/** Power switching mode: 0-global, 1-per port*/
+/** Power switching mode: 0-global, 1-per port */
 #define RHDA_PSM_FLAG  (1 << 8)
-/** No power switch: 1-power on, 0-use PSM*/
+/** No power switch: 1-power on, 0-use PSM */
 #define RHDA_NPS_FLAG  (1 << 9)
 /** 1-Compound device, must be 0 */
@@ -317,5 +317,5 @@
 #define RHS_LPSC_FLAG (1 << 16)
 #define RHS_SET_GLOBAL_POWER RHS_LPSC_FLAG /* synonym for the above */
-/** Over-current change indicator*/
+/** Over-current change indicator */
 #define RHS_OCIC_FLAG (1 << 17)
 #define RHS_CLEAR_DRWE (1 << 31)
@@ -327,5 +327,5 @@
  */
 
-/** r: current connect status, w: 1-clear port enable, 0-N/S*/
+/** r: current connect status, w: 1-clear port enable, 0-N/S */
 #define RHPS_CCS_FLAG (1 << 0)
 #define RHPS_CLEAR_PORT_ENABLE RHPS_CCS_FLAG
Index: uspace/drv/bus/usb/ohci/ohci_rh.c
===================================================================
--- uspace/drv/bus/usb/ohci/ohci_rh.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/ohci/ohci_rh.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -87,5 +87,5 @@
 	instance->hub_descriptor.header.max_current = 0;
 
-	/* Device Removable and some legacy 1.0 stuff*/
+	/* Device Removable and some legacy 1.0 stuff */
 	instance->hub_descriptor.rempow[0] =
 	    (port_desc >> RHDB_DR_SHIFT) & 0xff;
@@ -139,5 +139,5 @@
 	OHCI_CLR(regs->rh_desc_a, RHDA_PSM_FLAG);
 
-	/* Turn off power (hub driver will turn this back on)*/
+	/* Turn off power (hub driver will turn this back on) */
 	OHCI_WR(regs->rh_status, RHS_CLEAR_GLOBAL_POWER);
 
@@ -348,5 +348,5 @@
 	/* Enabled features to clear: see page 269 of USB specs */
 	switch (feature) {
-	case USB_HUB_FEATURE_PORT_POWER:          /*8*/
+	case USB_HUB_FEATURE_PORT_POWER:          /* 8 */
 		rhda = OHCI_RD(hub->registers->rh_desc_a);
 		/* No power switching */
@@ -362,19 +362,19 @@
 		    RHPS_CLEAR_PORT_POWER);
 		return EOK;
-	case USB2_HUB_FEATURE_PORT_ENABLE:         /*1*/
+	case USB2_HUB_FEATURE_PORT_ENABLE:         /* 1 */
 		OHCI_WR(hub->registers->rh_port_status[port],
 		    RHPS_CLEAR_PORT_ENABLE);
 		return EOK;
 
-	case USB2_HUB_FEATURE_PORT_SUSPEND:        /*2*/
+	case USB2_HUB_FEATURE_PORT_SUSPEND:        /* 2 */
 		OHCI_WR(hub->registers->rh_port_status[port],
 		    RHPS_CLEAR_PORT_SUSPEND);
 		return EOK;
 
-	case USB_HUB_FEATURE_C_PORT_CONNECTION:   /*16*/
-	case USB2_HUB_FEATURE_C_PORT_ENABLE:       /*17*/
-	case USB2_HUB_FEATURE_C_PORT_SUSPEND:      /*18*/
-	case USB_HUB_FEATURE_C_PORT_OVER_CURRENT: /*19*/
-	case USB_HUB_FEATURE_C_PORT_RESET:        /*20*/
+	case USB_HUB_FEATURE_C_PORT_CONNECTION:   /* 16 */
+	case USB2_HUB_FEATURE_C_PORT_ENABLE:       /* 17 */
+	case USB2_HUB_FEATURE_C_PORT_SUSPEND:      /* 18 */
+	case USB_HUB_FEATURE_C_PORT_OVER_CURRENT: /* 19 */
+	case USB_HUB_FEATURE_C_PORT_RESET:        /* 20 */
 		usb_log_debug2("Clearing port C_CONNECTION, C_ENABLE, "
 		    "C_SUSPEND, C_OC or C_RESET on port %u.", port);
@@ -408,5 +408,5 @@
 
 	switch (feature) {
-	case USB_HUB_FEATURE_PORT_POWER:   /*8*/
+	case USB_HUB_FEATURE_PORT_POWER:   /* 8 */
 		rhda = OHCI_RD(hub->registers->rh_desc_a);
 
@@ -422,7 +422,7 @@
 		/* Fall through, for per port power */
 		/* Fallthrough */
-	case USB2_HUB_FEATURE_PORT_ENABLE:  /*1*/
-	case USB2_HUB_FEATURE_PORT_SUSPEND: /*2*/
-	case USB_HUB_FEATURE_PORT_RESET:   /*4*/
+	case USB2_HUB_FEATURE_PORT_ENABLE:  /* 1 */
+	case USB2_HUB_FEATURE_PORT_SUSPEND: /* 2 */
+	case USB_HUB_FEATURE_PORT_RESET:   /* 4 */
 		usb_log_debug2("Setting port POWER, ENABLE, SUSPEND or RESET "
 		    "on port %u.", port);
Index: uspace/drv/bus/usb/uhci/hc.c
===================================================================
--- uspace/drv/bus/usb/uhci/hc.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/uhci/hc.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -188,5 +188,5 @@
 
 		if (instance->hw_failures < UHCI_ALLOWED_HW_FAIL) {
-			/* reinitialize hw, this triggers virtual disconnect*/
+			/* reinitialize hw, this triggers virtual disconnect */
 			hc_init_hw(instance);
 		} else {
Index: uspace/drv/bus/usb/uhci/hc.h
===================================================================
--- uspace/drv/bus/usb/uhci/hc.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/uhci/hc.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -41,4 +41,5 @@
 #include <fibril.h>
 #include <macros.h>
+#include <member.h>
 #include <stdbool.h>
 #include <ddi.h>
Index: uspace/drv/bus/usb/usbhub/usbhub.h
===================================================================
--- uspace/drv/bus/usb/usbhub/usbhub.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/usbhub/usbhub.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -61,5 +61,5 @@
 	/** Speed of the hub */
 	usb_speed_t speed;
-	/** Generic usb device data*/
+	/** Generic usb device data */
 	usb_device_t *usb_device;
 	/** Data polling handle. */
Index: uspace/drv/bus/usb/vhc/vhcd.h
===================================================================
--- uspace/drv/bus/usb/vhc/vhcd.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/vhc/vhcd.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -41,4 +41,5 @@
 #include <async.h>
 #include <macros.h>
+#include <member.h>
 
 #include <usb/host/hcd.h>
Index: uspace/drv/bus/usb/xhci/hc.c
===================================================================
--- uspace/drv/bus/usb/xhci/hc.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/xhci/hc.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -886,5 +886,5 @@
 	}
 
-	/* Issue configure endpoint command (sec 4.3.5). */
+	/* Issue configure endpoint command (sec 4.3.5).  */
 	dma_buffer_t ictx_dma_buf;
 	if ((err = create_configure_ep_input_ctx(dev, &ictx_dma_buf)))
@@ -928,5 +928,5 @@
 	xhci_cmd_t cmd;
 
-	/* Issue configure endpoint command (sec 4.3.5). */
+	/* Issue configure endpoint command (sec 4.3.5).  */
 	dma_buffer_t ictx_dma_buf;
 	errno_t err = create_configure_ep_input_ctx(dev, &ictx_dma_buf);
@@ -957,5 +957,5 @@
 		return EOK;
 
-	/* Issue configure endpoint command (sec 4.3.5) with the DC flag. */
+	/* Issue configure endpoint command (sec 4.3.5) with the DC flag.  */
 	xhci_cmd_init(&cmd, XHCI_CMD_CONFIGURE_ENDPOINT);
 	cmd.slot_id = dev->slot_id;
@@ -981,5 +981,5 @@
 	xhci_cmd_t cmd;
 
-	/* Issue configure endpoint command (sec 4.3.5). */
+	/* Issue configure endpoint command (sec 4.3.5).  */
 	dma_buffer_t ictx_dma_buf;
 	errno_t err = create_configure_ep_input_ctx(dev, &ictx_dma_buf);
@@ -1020,5 +1020,5 @@
 		return EOK;
 
-	/* Issue configure endpoint command (sec 4.3.5). */
+	/* Issue configure endpoint command (sec 4.3.5).  */
 	dma_buffer_t ictx_dma_buf;
 	errno_t err = create_configure_ep_input_ctx(dev, &ictx_dma_buf);
Index: uspace/drv/bus/usb/xhci/hc.h
===================================================================
--- uspace/drv/bus/usb/xhci/hc.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/bus/usb/xhci/hc.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -38,4 +38,5 @@
 
 #include <fibril_synch.h>
+#include <member.h>
 #include <usb/host/usb_transfer_batch.h>
 #include <usb/host/utility.h>
Index: uspace/drv/char/i8042/i8042.h
===================================================================
--- uspace/drv/char/i8042/i8042.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/char/i8042/i8042.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,6 +1,6 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2006 Josef Cejka
  * Copyright (c) 2011 Jan Vesely
- * Copyright (c) 2017 Jiri Svoboda
  * All rights reserved.
  *
@@ -48,5 +48,8 @@
 #define NAME  "i8042"
 
-#define BUFFER_SIZE  12
+/** Buffer needs to be large enough for rate at which keyboard or mouse
+ * produces data (mouse produces data at faster rate).
+ */
+#define BUFFER_SIZE  64
 
 /** i8042 HW I/O interface */
@@ -77,5 +80,5 @@
 /** i8042 Controller. */
 typedef struct i8042 {
-	/**< I/O registers. */
+	/** I/O registers. */
 	i8042_regs_t *regs;
 	/** Keyboard port */
@@ -83,5 +86,5 @@
 	/** AUX port */
 	i8042_port_t *aux;
-	/** Prevents simultanous port writes.*/
+	/** Prevents simultanous port writes. */
 	fibril_mutex_t write_guard;
 } i8042_t;
Index: uspace/drv/char/ns8250/ns8250.c
===================================================================
--- uspace/drv/char/ns8250/ns8250.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/char/ns8250/ns8250.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -157,5 +157,5 @@
 	/** Parent session */
 	async_sess_t *parent_sess;
-	/** I/O registers **/
+	/** I/O registers */
 	ns8250_regs_t *regs;
 	/** Are there any clients connected to the device? */
Index: uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.c
===================================================================
--- uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2013 Jan Vesely
  * All rights reserved.
@@ -67,4 +67,5 @@
 static errno_t amdm37x_ddev_get_info(void *, ddev_info_t *);
 
+static errno_t amdm37x_gc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t amdm37x_gc_set_color(void *, gfx_color_t *);
 static errno_t amdm37x_gc_fill_rect(void *, gfx_rect_t *);
@@ -81,4 +82,5 @@
 
 gfx_context_ops_t amdm37x_gc_ops = {
+	.set_clip_rect = amdm37x_gc_set_clip_rect,
 	.set_color = amdm37x_gc_set_color,
 	.fill_rect = amdm37x_gc_fill_rect,
@@ -166,5 +168,5 @@
 	regs->irqenable = 0;
 
-	/* Pixel format specifics*/
+	/* Pixel format specifics */
 	uint32_t attrib_pixel_format = 0;
 	uint32_t control_data_lanes = 0;
@@ -291,4 +293,5 @@
 	dispc->rect.p1.x = x;
 	dispc->rect.p1.y = y;
+	dispc->clip_rect = dispc->rect;
 	dispc->size = size;
 
@@ -321,4 +324,23 @@
 }
 
+/** Set clipping rectangle on AMDM37x display controller.
+ *
+ * @param arg AMDM37x display controller
+ * @param rect Rectangle
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t amdm37x_gc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	amdm37x_dispc_t *dispc = (amdm37x_dispc_t *) arg;
+
+	if (rect != NULL)
+		gfx_rect_clip(rect, &dispc->rect, &dispc->clip_rect);
+	else
+		dispc->clip_rect = dispc->rect;
+
+	return EOK;
+}
+
 /** Set color on AMDM37x display controller.
  *
@@ -354,5 +376,5 @@
 
 	/* Make sure we have a sorted, clipped rectangle */
-	gfx_rect_clip(rect, &dispc->rect, &crect);
+	gfx_rect_clip(rect, &dispc->clip_rect, &crect);
 
 	for (y = crect.p0.y; y < crect.p1.y; y++) {
@@ -479,6 +501,6 @@
 	pbm.data = dcbm->alloc.pixels;
 
-	/* Transform AMDM37x bounding rectangle back to bitmap coordinate system */
-	gfx_rect_rtranslate(&offs, &dispc->rect, &skfbrect);
+	/* Transform AMDM37x clipping rectangle back to bitmap coordinate system */
+	gfx_rect_rtranslate(&offs, &dispc->clip_rect, &skfbrect);
 
 	/*
Index: uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.h
===================================================================
--- uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/fb/amdm37x_dispc/amdm37x_dispc.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2013 Jan Vesely
  * All rights reserved.
@@ -63,4 +63,5 @@
 	pixel_t color;
 	gfx_rect_t rect;
+	gfx_rect_t clip_rect;
 	size_t size;
 	void *fb_data;
Index: uspace/drv/fb/kfb/port.c
===================================================================
--- uspace/drv/fb/kfb/port.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/fb/kfb/port.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2006 Jakub Vana
  * Copyright (c) 2006 Ondrej Palkovsky
@@ -69,4 +69,5 @@
 	sysarg_t paddr;
 	gfx_rect_t rect;
+	gfx_rect_t clip_rect;
 	size_t offset;
 	size_t scanline;
@@ -97,4 +98,5 @@
 static errno_t kfb_ddev_get_info(void *, ddev_info_t *);
 
+static errno_t kfb_gc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t kfb_gc_set_color(void *, gfx_color_t *);
 static errno_t kfb_gc_fill_rect(void *, gfx_rect_t *);
@@ -111,4 +113,5 @@
 
 static gfx_context_ops_t kfb_gc_ops = {
+	.set_clip_rect = kfb_gc_set_clip_rect,
 	.set_color = kfb_gc_set_color,
 	.fill_rect = kfb_gc_fill_rect,
@@ -134,4 +137,23 @@
 	ddev_info_init(info);
 	info->rect = kfb->rect;
+	return EOK;
+}
+
+/** Set clipping rectangle on KFB.
+ *
+ * @param arg KFB
+ * @param rect Rectangle or @c NULL
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t kfb_gc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	kfb_t *kfb = (kfb_t *) arg;
+
+	if (rect != NULL)
+		gfx_rect_clip(rect, &kfb->rect, &kfb->clip_rect);
+	else
+		kfb->clip_rect = kfb->rect;
+
 	return EOK;
 }
@@ -361,5 +383,4 @@
 }
 
-#include <stdio.h>
 static void kfb_client_conn(ipc_call_t *icall, void *arg)
 {
@@ -372,9 +393,4 @@
 	kfb = (kfb_t *) ddf_fun_data_get((ddf_fun_t *) arg);
 
-	printf("kfb_client_conn arg2=%lu arg3=%lu arg4=%lu\n",
-	    (unsigned long) ipc_get_arg2(icall),
-	    (unsigned long) ipc_get_arg3(icall),
-	    (unsigned long) ipc_get_arg4(icall));
-
 	gc_id = ipc_get_arg3(icall);
 
@@ -450,5 +466,4 @@
 
 	if (!present) {
-		ddf_fun_destroy(fun);
 		rc = ENOENT;
 		goto error;
@@ -501,4 +516,6 @@
 	kfb->rect.p1.x = width;
 	kfb->rect.p1.y = height;
+
+	kfb->clip_rect = kfb->rect;
 
 	kfb->paddr = paddr;
Index: uspace/drv/hid/usbhid/generic/hiddev.c
===================================================================
--- uspace/drv/hid/usbhid/generic/hiddev.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/hid/usbhid/generic/hiddev.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -120,5 +120,5 @@
 	}
 
-	/*! @todo This should probably be somehow atomic. */
+	/* TODO This should probably be somehow atomic. */
 	memcpy(buffer, hid_dev->input_report,
 	    hid_dev->input_report_size);
Index: uspace/drv/hid/usbhid/multimedia/keymap.c
===================================================================
--- uspace/drv/hid/usbhid/multimedia/keymap.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/hid/usbhid/multimedia/keymap.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -52,18 +52,18 @@
 	[0xb7] = 0,       /* Stop */
 	[0xb8] = 0,       /* Eject */
-	[0xcd] = 0/*KC_F2*/,   /* Play/Pause */
-	[0xe2] = 0/*KC_F3*/,   /* Mute */
-	[0xe9] = 0/*KC_F5*/,   /* Volume Increment */
-	[0xea] = 0/*KC_F4*/,   /* Volume Decrement */
-	[0x183] = 0/*KC_F1*/,      /* AL Consumer Control Configuration */
+	[0xcd] = 0,       /* Play/Pause */
+	[0xe2] = 0,       /* Mute */
+	[0xe9] = 0,       /* Volume Increment */
+	[0xea] = 0,       /* Volume Decrement */
+	[0x183] = 0,      /* AL Consumer Control Configuration */
 	[0x18a] = 0,      /* AL Email Reader */
 	[0x192] = 0,      /* AL Calculator */
 	[0x221] = 0,      /* AC Search */
-	[0x223] = 0/*KC_F6*/,      /* AC Home */
+	[0x223] = 0,      /* AC Home */
 	[0x224] = 0,      /* AC Back */
 	[0x225] = 0,      /* AC Forward */
 	[0x226] = 0,      /* AC Stop */
-	[0x227] = 0,  /* AC Refresh */
-	[0x22a] = 0   /* AC Bookmarks */
+	[0x227] = 0,      /* AC Refresh */
+	[0x22a] = 0       /* AC Bookmarks */
 };
 
@@ -84,5 +84,5 @@
 		return -1;
 
-	/*! @todo What if the usage is not in the table? */
+	/* TODO What if the usage is not in the table? */
 	key = map[usage];
 
Index: uspace/drv/hid/usbhid/usbhid.c
===================================================================
--- uspace/drv/hid/usbhid/usbhid.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/hid/usbhid/usbhid.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -342,5 +342,5 @@
 
 	if (hid_dev->max_input_report_size >= buffer_size) {
-		/*! @todo This should probably be atomic. */
+		/* TODO This should probably be atomic. */
 		memcpy(hid_dev->input_report, buffer, buffer_size);
 		hid_dev->input_report_size = buffer_size;
Index: uspace/drv/nic/ar9271/ar9271.h
===================================================================
--- uspace/drv/nic/ar9271/ar9271.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/nic/ar9271/ar9271.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -234,8 +234,8 @@
 	{ 0x0000801c, 0x12e0002b },
 	{ 0x00008318, 0x00003440 },
-	{ 0x00009804, 0x000003c0 },  /*< Note: overridden */
+	{ 0x00009804, 0x000003c0 },  /**< Note: overridden */
 	{ 0x00009820, 0x02020200 },
 	{ 0x00009824, 0x01000e0e },
-	{ 0x00009828, 0x0a020001 },  /*< Note: overridden */
+	{ 0x00009828, 0x0a020001 },  /**< Note: overridden */
 	{ 0x00009834, 0x00000e0e },
 	{ 0x00009838, 0x00000007 },
Index: uspace/drv/nic/ne2k/dp8390.h
===================================================================
--- uspace/drv/nic/ne2k/dp8390.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/nic/ne2k/dp8390.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -230,5 +230,5 @@
 	async_sess_t *parent_sess;
 	/* Device configuration */
-	void *base_port; /**< Port assigned from ISA configuration **/
+	void *base_port; /**< Port assigned from ISA configuration */
 	void *port;
 	void *data_port;
Index: uspace/drv/nic/rtl8139/defs.h
===================================================================
--- uspace/drv/nic/rtl8139/defs.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/nic/rtl8139/defs.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -372,5 +372,5 @@
 	BMCR_AN_ENABLE = (1 << 12),  /**< Autonegotion enable */
 
-	/* 10,11 reserved*/
+	/* 10,11 reserved */
 
 	BMCR_AN_RESTART = (1 << 9),  /**< Restart autonegotion */
Index: uspace/drv/nic/rtl8139/driver.h
===================================================================
--- uspace/drv/nic/rtl8139/driver.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/drv/nic/rtl8139/driver.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -145,5 +145,7 @@
 } rtl8139_t;
 
-/* ***** Pointers casting - for both amd64 and ia32 ***** */
+/*
+ * Pointers casting - for both amd64 and ia32
+ */
 
 /** Cast pointer to uint32_t
Index: uspace/lib/bithenge/src/compound.c
===================================================================
--- uspace/lib/bithenge/src/compound.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/bithenge/src/compound.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -42,5 +42,7 @@
 #include "common.h"
 
-/***************** compose_transform                         *****************/
+/*
+ * compose_transform
+ */
 
 typedef struct {
@@ -148,5 +150,7 @@
 }
 
-/***************** if_transform                              *****************/
+/*
+ * if_transform
+ */
 
 typedef struct {
@@ -260,5 +264,7 @@
 }
 
-/***************** partial_transform                         *****************/
+/*
+ * partial_transform
+ */
 
 typedef struct {
Index: uspace/lib/bithenge/src/expression.c
===================================================================
--- uspace/lib/bithenge/src/expression.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/bithenge/src/expression.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -67,5 +67,7 @@
 }
 
-/***************** binary_expression                         *****************/
+/*
+ * binary_expression
+ */
 
 typedef struct {
@@ -281,5 +283,7 @@
 }
 
-/***************** in_node_expression                        *****************/
+/*
+ * in_node_expression
+ */
 
 static errno_t in_node_evaluate(bithenge_expression_t *self,
@@ -316,5 +320,7 @@
 }
 
-/***************** current_node_expression                   *****************/
+/*
+ * current_node_expression
+ */
 
 static errno_t current_node_evaluate(bithenge_expression_t *self,
@@ -347,5 +353,7 @@
 }
 
-/***************** param_expression                          *****************/
+/*
+ * param_expression
+ */
 
 typedef struct {
@@ -408,5 +416,7 @@
 }
 
-/***************** const_expression                          *****************/
+/*
+ * const_expression
+ */
 
 typedef struct {
@@ -478,5 +488,7 @@
 }
 
-/***************** scope_member_expression                   *****************/
+/*
+ * scope_member_expression
+ */
 
 typedef struct {
@@ -559,5 +571,7 @@
 }
 
-/***************** subblob_expression                        *****************/
+/*
+ * subblob_expression
+ */
 
 typedef struct {
@@ -685,5 +699,7 @@
 }
 
-/***************** param_wrapper                             *****************/
+/*
+ * param_wrapper
+ */
 
 typedef struct {
@@ -843,5 +859,7 @@
 }
 
-/***************** expression_transform           *****************/
+/*
+ * expression_transform
+ */
 
 /* Also used by inputless_transform. */
@@ -921,5 +939,7 @@
 }
 
-/***************** inputless_transform            *****************/
+/*
+ * inputless_transform
+ */
 
 static errno_t inputless_transform_prefix_length(bithenge_transform_t *base,
@@ -977,5 +997,7 @@
 }
 
-/***************** concat_blob                    *****************/
+/*
+ * concat_blob
+ */
 
 typedef struct {
Index: uspace/lib/bithenge/src/script.c
===================================================================
--- uspace/lib/bithenge/src/script.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/bithenge/src/script.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -450,5 +450,7 @@
 static bithenge_expression_t *parse_expression(state_t *state);
 
-/***************** Expressions                               *****************/
+/*
+ * Expressions
+ */
 
 /** @cond internal */
Index: uspace/lib/bithenge/src/sequence.c
===================================================================
--- uspace/lib/bithenge/src/sequence.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/bithenge/src/sequence.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -42,5 +42,7 @@
 #include "common.h"
 
-/***************** seq_node                                  *****************/
+/*
+ * seq_node
+ */
 
 typedef struct {
@@ -273,5 +275,7 @@
 }
 
-/***************** bithenge_new_struct                       *****************/
+/*
+ * bithenge_new_struct
+ */
 
 typedef struct {
@@ -626,5 +630,7 @@
 }
 
-/***************** bithenge_repeat_transform                 *****************/
+/*
+ * bithenge_repeat_transform
+ */
 
 /* TODO: ignore errors */
@@ -905,5 +911,7 @@
 }
 
-/***************** bithenge_do_while_transform               *****************/
+/*
+ * bithenge_do_while_transform
+ */
 
 typedef struct {
Index: uspace/lib/bithenge/src/transform.c
===================================================================
--- uspace/lib/bithenge/src/transform.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/bithenge/src/transform.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -44,5 +44,7 @@
 #include "common.h"
 
-/***************** transform                                 *****************/
+/*
+ * transform
+ */
 
 /** Initialize a new transform.
@@ -180,5 +182,7 @@
 }
 
-/***************** scope                                     *****************/
+/*
+ * scope
+ */
 
 /** Create a transform scope. It must be dereferenced with @a
@@ -429,5 +433,7 @@
 }
 
-/***************** barrier_transform                         *****************/
+/*
+ * barrier_transform
+ */
 
 typedef struct {
@@ -561,5 +567,7 @@
 }
 
-/***************** ascii                                     *****************/
+/*
+ * ascii
+ */
 
 static errno_t ascii_apply(bithenge_transform_t *self, bithenge_scope_t *scope,
@@ -604,5 +612,7 @@
 };
 
-/***************** bit                                       *****************/
+/*
+ * bit
+ */
 
 static errno_t bit_prefix_apply(bithenge_transform_t *self,
@@ -632,5 +642,7 @@
 };
 
-/***************** bits_be, bits_le                          *****************/
+/*
+ * bits_be, bits_le
+ */
 
 typedef struct {
@@ -763,5 +775,7 @@
 };
 
-/***************** invalid                                   *****************/
+/*
+ * invalid
+ */
 
 static errno_t invalid_apply(bithenge_transform_t *self, bithenge_scope_t *scope,
@@ -781,5 +795,7 @@
 };
 
-/***************** known_length                              *****************/
+/*
+ * known_length
+ */
 
 static errno_t known_length_apply(bithenge_transform_t *self,
@@ -934,5 +950,7 @@
 /** @endcond */
 
-/***************** uint_be, uint_le                          *****************/
+/*
+ * uint_be, uint_le
+ */
 
 static errno_t uint_xe_prefix_apply(bithenge_transform_t *self,
@@ -1004,5 +1022,7 @@
 };
 
-/***************** zero_terminated                           *****************/
+/*
+ * zero_terminated
+ */
 
 static errno_t zero_terminated_apply(bithenge_transform_t *self,
Index: uspace/lib/block/meson.build
===================================================================
--- uspace/lib/block/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/block/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -28,3 +28,4 @@
 #
 
+deps = [ 'device' ]
 src = files('block.c')
Index: uspace/lib/c/generic/adt/hash_table.c
===================================================================
--- uspace/lib/c/generic/adt/hash_table.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/generic/adt/hash_table.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -52,4 +52,5 @@
 #include <adt/list.h>
 #include <assert.h>
+#include <member.h>
 #include <stdlib.h>
 #include <str.h>
@@ -331,5 +332,5 @@
 }
 
-/** Removes an item already present in the table. The item must be in the table.*/
+/** Removes an item already present in the table. The item must be in the table. */
 void hash_table_remove_item(hash_table_t *h, ht_link_t *item)
 {
@@ -393,5 +394,5 @@
 }
 
-/** Allocates and initializes the desired number of buckets. True if successful.*/
+/** Allocates and initializes the desired number of buckets. True if successful. */
 static bool alloc_table(size_t bucket_cnt, list_t **pbuckets)
 {
Index: uspace/lib/c/generic/adt/list.c
===================================================================
--- uspace/lib/c/generic/adt/list.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/generic/adt/list.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -41,4 +41,5 @@
 #include <adt/list.h>
 #include <stdbool.h>
+#include <stdint.h>
 
 /** Check for membership
@@ -100,7 +101,7 @@
  * @return		Number of items in the list.
  */
-unsigned long list_count(const list_t *list)
+size_t list_count(const list_t *list)
 {
-	unsigned long count = 0;
+	size_t count = 0;
 
 	link_t *link = list_first(list);
Index: pace/lib/c/generic/bd.c
===================================================================
--- uspace/lib/c/generic/bd.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,221 +1,0 @@
-/*
- * 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
- * @brief Block device client interface
- */
-
-#include <async.h>
-#include <assert.h>
-#include <bd.h>
-#include <errno.h>
-#include <ipc/bd.h>
-#include <ipc/services.h>
-#include <loc.h>
-#include <macros.h>
-#include <stdlib.h>
-#include <offset.h>
-
-static void bd_cb_conn(ipc_call_t *icall, void *arg);
-
-errno_t bd_open(async_sess_t *sess, bd_t **rbd)
-{
-	bd_t *bd = calloc(1, sizeof(bd_t));
-	if (bd == NULL)
-		return ENOMEM;
-
-	bd->sess = sess;
-
-	async_exch_t *exch = async_exchange_begin(sess);
-
-	port_id_t port;
-	errno_t rc = async_create_callback_port(exch, INTERFACE_BLOCK_CB, 0, 0,
-	    bd_cb_conn, bd, &port);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK)
-		goto error;
-
-	*rbd = bd;
-	return EOK;
-
-error:
-	if (bd != NULL)
-		free(bd);
-
-	return rc;
-}
-
-void bd_close(bd_t *bd)
-{
-	/* XXX Synchronize with bd_cb_conn */
-	free(bd);
-}
-
-errno_t bd_read_blocks(bd_t *bd, aoff64_t ba, size_t cnt, void *data, size_t size)
-{
-	async_exch_t *exch = async_exchange_begin(bd->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_3(exch, BD_READ_BLOCKS, LOWER32(ba),
-	    UPPER32(ba), cnt, &answer);
-	errno_t rc = async_data_read_start(exch, data, size);
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-errno_t bd_read_toc(bd_t *bd, uint8_t session, void *buf, size_t size)
-{
-	async_exch_t *exch = async_exchange_begin(bd->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, BD_READ_TOC, session, &answer);
-	errno_t rc = async_data_read_start(exch, buf, size);
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-errno_t bd_write_blocks(bd_t *bd, aoff64_t ba, size_t cnt, const void *data,
-    size_t size)
-{
-	async_exch_t *exch = async_exchange_begin(bd->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_3(exch, BD_WRITE_BLOCKS, LOWER32(ba),
-	    UPPER32(ba), cnt, &answer);
-	errno_t rc = async_data_write_start(exch, data, size);
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-errno_t bd_sync_cache(bd_t *bd, aoff64_t ba, size_t cnt)
-{
-	async_exch_t *exch = async_exchange_begin(bd->sess);
-
-	errno_t rc = async_req_3_0(exch, BD_SYNC_CACHE, LOWER32(ba),
-	    UPPER32(ba), cnt);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-errno_t bd_get_block_size(bd_t *bd, size_t *rbsize)
-{
-	sysarg_t bsize;
-	async_exch_t *exch = async_exchange_begin(bd->sess);
-
-	errno_t rc = async_req_0_1(exch, BD_GET_BLOCK_SIZE, &bsize);
-	async_exchange_end(exch);
-
-	if (rc != EOK)
-		return rc;
-
-	*rbsize = bsize;
-	return EOK;
-}
-
-errno_t bd_get_num_blocks(bd_t *bd, aoff64_t *rnb)
-{
-	sysarg_t nb_l;
-	sysarg_t nb_h;
-	async_exch_t *exch = async_exchange_begin(bd->sess);
-
-	errno_t rc = async_req_0_2(exch, BD_GET_NUM_BLOCKS, &nb_l, &nb_h);
-	async_exchange_end(exch);
-
-	if (rc != EOK)
-		return rc;
-
-	*rnb = (aoff64_t) MERGE_LOUP32(nb_l, nb_h);
-	return EOK;
-}
-
-static void bd_cb_conn(ipc_call_t *icall, void *arg)
-{
-	bd_t *bd = (bd_t *)arg;
-
-	(void)bd;
-
-	while (true) {
-		ipc_call_t call;
-		async_get_call(&call);
-
-		if (!ipc_get_imethod(&call)) {
-			async_answer_0(&call, EOK);
-			return;
-		}
-
-		switch (ipc_get_imethod(&call)) {
-		default:
-			async_answer_0(&call, ENOTSUP);
-		}
-	}
-}
-
-/** @}
- */
Index: pace/lib/c/generic/bd_srv.c
===================================================================
--- uspace/lib/c/generic/bd_srv.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,288 +1,0 @@
-/*
- * 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
- * @brief Block device server stub
- */
-#include <errno.h>
-#include <ipc/bd.h>
-#include <macros.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include <bd_srv.h>
-
-static void bd_read_blocks_srv(bd_srv_t *srv, ipc_call_t *call)
-{
-	aoff64_t ba;
-	size_t cnt;
-	void *buf;
-	size_t size;
-	errno_t rc;
-
-	ba = MERGE_LOUP32(ipc_get_arg1(call), ipc_get_arg2(call));
-	cnt = ipc_get_arg3(call);
-
-	ipc_call_t rcall;
-	if (!async_data_read_receive(&rcall, &size)) {
-		async_answer_0(call, EINVAL);
-		return;
-	}
-
-	buf = malloc(size);
-	if (buf == NULL) {
-		async_answer_0(&rcall, ENOMEM);
-		async_answer_0(call, ENOMEM);
-		return;
-	}
-
-	if (srv->srvs->ops->read_blocks == NULL) {
-		async_answer_0(&rcall, ENOTSUP);
-		async_answer_0(call, ENOTSUP);
-		free(buf);
-		return;
-	}
-
-	rc = srv->srvs->ops->read_blocks(srv, ba, cnt, buf, size);
-	if (rc != EOK) {
-		async_answer_0(&rcall, ENOMEM);
-		async_answer_0(call, ENOMEM);
-		free(buf);
-		return;
-	}
-
-	async_data_read_finalize(&rcall, buf, size);
-
-	free(buf);
-	async_answer_0(call, EOK);
-}
-
-static void bd_read_toc_srv(bd_srv_t *srv, ipc_call_t *call)
-{
-	uint8_t session;
-	void *buf;
-	size_t size;
-	errno_t rc;
-
-	session = ipc_get_arg1(call);
-
-	ipc_call_t rcall;
-	if (!async_data_read_receive(&rcall, &size)) {
-		async_answer_0(call, EINVAL);
-		return;
-	}
-
-	buf = malloc(size);
-	if (buf == NULL) {
-		async_answer_0(&rcall, ENOMEM);
-		async_answer_0(call, ENOMEM);
-		return;
-	}
-
-	if (srv->srvs->ops->read_toc == NULL) {
-		async_answer_0(&rcall, ENOTSUP);
-		async_answer_0(call, ENOTSUP);
-		free(buf);
-		return;
-	}
-
-	rc = srv->srvs->ops->read_toc(srv, session, buf, size);
-	if (rc != EOK) {
-		async_answer_0(&rcall, ENOMEM);
-		async_answer_0(call, ENOMEM);
-		free(buf);
-		return;
-	}
-
-	async_data_read_finalize(&rcall, buf, size);
-
-	free(buf);
-	async_answer_0(call, EOK);
-}
-
-static void bd_sync_cache_srv(bd_srv_t *srv, ipc_call_t *call)
-{
-	aoff64_t ba;
-	size_t cnt;
-	errno_t rc;
-
-	ba = MERGE_LOUP32(ipc_get_arg1(call), ipc_get_arg2(call));
-	cnt = ipc_get_arg3(call);
-
-	if (srv->srvs->ops->sync_cache == NULL) {
-		async_answer_0(call, ENOTSUP);
-		return;
-	}
-
-	rc = srv->srvs->ops->sync_cache(srv, ba, cnt);
-	async_answer_0(call, rc);
-}
-
-static void bd_write_blocks_srv(bd_srv_t *srv, ipc_call_t *call)
-{
-	aoff64_t ba;
-	size_t cnt;
-	void *data;
-	size_t size;
-	errno_t rc;
-
-	ba = MERGE_LOUP32(ipc_get_arg1(call), ipc_get_arg2(call));
-	cnt = ipc_get_arg3(call);
-
-	rc = async_data_write_accept(&data, false, 0, 0, 0, &size);
-	if (rc != EOK) {
-		async_answer_0(call, rc);
-		return;
-	}
-
-	if (srv->srvs->ops->write_blocks == NULL) {
-		async_answer_0(call, ENOTSUP);
-		return;
-	}
-
-	rc = srv->srvs->ops->write_blocks(srv, ba, cnt, data, size);
-	free(data);
-	async_answer_0(call, rc);
-}
-
-static void bd_get_block_size_srv(bd_srv_t *srv, ipc_call_t *call)
-{
-	errno_t rc;
-	size_t block_size;
-
-	if (srv->srvs->ops->get_block_size == NULL) {
-		async_answer_0(call, ENOTSUP);
-		return;
-	}
-
-	rc = srv->srvs->ops->get_block_size(srv, &block_size);
-	async_answer_1(call, rc, block_size);
-}
-
-static void bd_get_num_blocks_srv(bd_srv_t *srv, ipc_call_t *call)
-{
-	errno_t rc;
-	aoff64_t num_blocks;
-
-	if (srv->srvs->ops->get_num_blocks == NULL) {
-		async_answer_0(call, ENOTSUP);
-		return;
-	}
-
-	rc = srv->srvs->ops->get_num_blocks(srv, &num_blocks);
-	async_answer_2(call, rc, LOWER32(num_blocks), UPPER32(num_blocks));
-}
-
-static bd_srv_t *bd_srv_create(bd_srvs_t *srvs)
-{
-	bd_srv_t *srv;
-
-	srv = calloc(1, sizeof(bd_srv_t));
-	if (srv == NULL)
-		return NULL;
-
-	srv->srvs = srvs;
-	return srv;
-}
-
-void bd_srvs_init(bd_srvs_t *srvs)
-{
-	srvs->ops = NULL;
-	srvs->sarg = NULL;
-}
-
-errno_t bd_conn(ipc_call_t *icall, bd_srvs_t *srvs)
-{
-	bd_srv_t *srv;
-	errno_t rc;
-
-	/* Accept the connection */
-	async_accept_0(icall);
-
-	srv = bd_srv_create(srvs);
-	if (srv == NULL)
-		return ENOMEM;
-
-	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
-	if (sess == NULL)
-		return ENOMEM;
-
-	srv->client_sess = sess;
-
-	rc = srvs->ops->open(srvs, srv);
-	if (rc != EOK)
-		return rc;
-
-	while (true) {
-		ipc_call_t call;
-		async_get_call(&call);
-		sysarg_t method = ipc_get_imethod(&call);
-
-		if (!method) {
-			/* The other side has hung up */
-			async_answer_0(&call, EOK);
-			break;
-		}
-
-		switch (method) {
-		case BD_READ_BLOCKS:
-			bd_read_blocks_srv(srv, &call);
-			break;
-		case BD_READ_TOC:
-			bd_read_toc_srv(srv, &call);
-			break;
-		case BD_SYNC_CACHE:
-			bd_sync_cache_srv(srv, &call);
-			break;
-		case BD_WRITE_BLOCKS:
-			bd_write_blocks_srv(srv, &call);
-			break;
-		case BD_GET_BLOCK_SIZE:
-			bd_get_block_size_srv(srv, &call);
-			break;
-		case BD_GET_NUM_BLOCKS:
-			bd_get_num_blocks_srv(srv, &call);
-			break;
-		default:
-			async_answer_0(&call, EINVAL);
-		}
-	}
-
-	rc = srvs->ops->close(srv);
-	free(srv);
-
-	return rc;
-}
-
-/** @}
- */
Index: uspace/lib/c/generic/capa.c
===================================================================
--- uspace/lib/c/generic/capa.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/generic/capa.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -34,7 +34,9 @@
  */
 
+#include <assert.h>
 #include <capa.h>
 #include <errno.h>
 #include <imath.h>
+#include <stddef.h>
 #include <stdio.h>
 #include <str.h>
Index: pace/lib/c/generic/device/led_dev.c
===================================================================
--- uspace/lib/c/generic/device/led_dev.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,57 +1,0 @@
-/*
- * Copyright (c) 2014 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
- */
-
-#include <errno.h>
-#include <async.h>
-#include <io/pixel.h>
-#include <ipc/dev_iface.h>
-#include <device/led_dev.h>
-
-errno_t led_dev_color_set(async_sess_t *sess, pixel_t pixel)
-{
-	async_exch_t *exch = async_exchange_begin(sess);
-
-	aid_t req = async_send_2(exch, DEV_IFACE_ID(LED_DEV_IFACE),
-	    LED_DEV_COLOR_SET, (sysarg_t) pixel, NULL);
-
-	async_exchange_end(exch);
-
-	errno_t rc;
-	async_wait_for(req, &rc);
-
-	return (errno_t) rc;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/devman.c
===================================================================
--- uspace/lib/c/generic/devman.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,734 +1,0 @@
-/*
- * Copyright (c) 2007 Josef Cejka
- * Copyright (c) 2013 Jiri Svoboda
- * 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 libc
- * @{
- */
-/** @file
- */
-
-#include <adt/list.h>
-#include <str.h>
-#include <ipc/services.h>
-#include <ns.h>
-#include <ipc/devman.h>
-#include <devman.h>
-#include <fibril_synch.h>
-#include <async.h>
-#include <errno.h>
-#include <stdlib.h>
-
-static FIBRIL_MUTEX_INITIALIZE(devman_driver_block_mutex);
-static FIBRIL_MUTEX_INITIALIZE(devman_client_block_mutex);
-
-static FIBRIL_MUTEX_INITIALIZE(devman_driver_mutex);
-static FIBRIL_MUTEX_INITIALIZE(devman_client_mutex);
-
-static async_sess_t *devman_driver_block_sess = NULL;
-static async_sess_t *devman_client_block_sess = NULL;
-
-static async_sess_t *devman_driver_sess = NULL;
-static async_sess_t *devman_client_sess = NULL;
-
-static void clone_session(fibril_mutex_t *mtx, async_sess_t *src,
-    async_sess_t **dst)
-{
-	fibril_mutex_lock(mtx);
-
-	if ((*dst == NULL) && (src != NULL))
-		*dst = src;
-
-	fibril_mutex_unlock(mtx);
-}
-
-/** Start an async exchange on the devman session (blocking).
- *
- * @param iface Device manager interface to choose
- *
- * @return New exchange.
- *
- */
-async_exch_t *devman_exchange_begin_blocking(iface_t iface)
-{
-	switch (iface) {
-	case INTERFACE_DDF_DRIVER:
-		fibril_mutex_lock(&devman_driver_block_mutex);
-
-		while (devman_driver_block_sess == NULL) {
-			clone_session(&devman_driver_mutex, devman_driver_sess,
-			    &devman_driver_block_sess);
-
-			if (devman_driver_block_sess == NULL)
-				devman_driver_block_sess =
-				    service_connect_blocking(SERVICE_DEVMAN,
-				    INTERFACE_DDF_DRIVER, 0, NULL);
-		}
-
-		fibril_mutex_unlock(&devman_driver_block_mutex);
-
-		clone_session(&devman_driver_mutex, devman_driver_block_sess,
-		    &devman_driver_sess);
-
-		return async_exchange_begin(devman_driver_block_sess);
-	case INTERFACE_DDF_CLIENT:
-		fibril_mutex_lock(&devman_client_block_mutex);
-
-		while (devman_client_block_sess == NULL) {
-			clone_session(&devman_client_mutex, devman_client_sess,
-			    &devman_client_block_sess);
-
-			if (devman_client_block_sess == NULL)
-				devman_client_block_sess =
-				    service_connect_blocking(SERVICE_DEVMAN,
-				    INTERFACE_DDF_CLIENT, 0, NULL);
-		}
-
-		fibril_mutex_unlock(&devman_client_block_mutex);
-
-		clone_session(&devman_client_mutex, devman_client_block_sess,
-		    &devman_client_sess);
-
-		return async_exchange_begin(devman_client_block_sess);
-	default:
-		return NULL;
-	}
-}
-
-/** Start an async exchange on the devman session.
- *
- * @param iface Device manager interface to choose
- *
- * @return New exchange.
- *
- */
-async_exch_t *devman_exchange_begin(iface_t iface)
-{
-	switch (iface) {
-	case INTERFACE_DDF_DRIVER:
-		fibril_mutex_lock(&devman_driver_mutex);
-
-		if (devman_driver_sess == NULL)
-			devman_driver_sess =
-			    service_connect(SERVICE_DEVMAN,
-			    INTERFACE_DDF_DRIVER, 0, NULL);
-
-		fibril_mutex_unlock(&devman_driver_mutex);
-
-		if (devman_driver_sess == NULL)
-			return NULL;
-
-		return async_exchange_begin(devman_driver_sess);
-	case INTERFACE_DDF_CLIENT:
-		fibril_mutex_lock(&devman_client_mutex);
-
-		if (devman_client_sess == NULL)
-			devman_client_sess =
-			    service_connect(SERVICE_DEVMAN,
-			    INTERFACE_DDF_CLIENT, 0, NULL);
-
-		fibril_mutex_unlock(&devman_client_mutex);
-
-		if (devman_client_sess == NULL)
-			return NULL;
-
-		return async_exchange_begin(devman_client_sess);
-	default:
-		return NULL;
-	}
-}
-
-/** Finish an async exchange on the devman session.
- *
- * @param exch Exchange to be finished.
- *
- */
-void devman_exchange_end(async_exch_t *exch)
-{
-	async_exchange_end(exch);
-}
-
-/** Register running driver with device manager. */
-errno_t devman_driver_register(const char *name)
-{
-	async_exch_t *exch = devman_exchange_begin_blocking(INTERFACE_DDF_DRIVER);
-
-	ipc_call_t answer;
-	aid_t req = async_send_2(exch, DEVMAN_DRIVER_REGISTER, 0, 0, &answer);
-	errno_t retval = async_data_write_start(exch, name, str_size(name));
-
-	devman_exchange_end(exch);
-
-	if (retval != EOK) {
-		async_forget(req);
-		return retval;
-	}
-
-	exch = devman_exchange_begin(INTERFACE_DDF_DRIVER);
-	async_connect_to_me(exch, INTERFACE_ANY, 0, 0);
-	devman_exchange_end(exch);
-
-	async_wait_for(req, &retval);
-	return retval;
-}
-
-/** Add function to a device.
- *
- * Request devman to add a new function to the specified device owned by
- * this driver task.
- *
- * @param name      Name of the new function
- * @param ftype     Function type, fun_inner or fun_exposed
- * @param match_ids Match IDs (should be empty for fun_exposed)
- * @param devh      Devman handle of the device
- * @param funh      Place to store handle of the new function
- *
- * @return EOK on success or an error code.
- *
- */
-errno_t devman_add_function(const char *name, fun_type_t ftype,
-    match_id_list_t *match_ids, devman_handle_t devh, devman_handle_t *funh)
-{
-	unsigned long match_count = list_count(&match_ids->ids);
-	async_exch_t *exch = devman_exchange_begin_blocking(INTERFACE_DDF_DRIVER);
-
-	ipc_call_t answer;
-	aid_t req = async_send_3(exch, DEVMAN_ADD_FUNCTION, (sysarg_t) ftype,
-	    devh, match_count, &answer);
-	errno_t retval = async_data_write_start(exch, name, str_size(name));
-	if (retval != EOK) {
-		devman_exchange_end(exch);
-		async_forget(req);
-		return retval;
-	}
-
-	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,
-		    match_id->score, &answer2);
-		retval = async_data_write_start(exch, match_id->id,
-		    str_size(match_id->id));
-		if (retval != EOK) {
-			devman_exchange_end(exch);
-			async_forget(req2);
-			async_forget(req);
-			return retval;
-		}
-
-		async_wait_for(req2, &retval);
-		if (retval != EOK) {
-			devman_exchange_end(exch);
-			async_forget(req);
-			return retval;
-		}
-	}
-
-	devman_exchange_end(exch);
-
-	async_wait_for(req, &retval);
-	if (retval == EOK) {
-		if (funh != NULL)
-			*funh = (int) ipc_get_arg1(&answer);
-	} else {
-		if (funh != NULL)
-			*funh = -1;
-	}
-
-	return retval;
-}
-
-errno_t devman_add_device_to_category(devman_handle_t devman_handle,
-    const char *cat_name)
-{
-	async_exch_t *exch = devman_exchange_begin_blocking(INTERFACE_DDF_DRIVER);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, DEVMAN_ADD_DEVICE_TO_CATEGORY,
-	    devman_handle, &answer);
-	errno_t retval = async_data_write_start(exch, cat_name,
-	    str_size(cat_name));
-
-	devman_exchange_end(exch);
-
-	if (retval != EOK) {
-		async_forget(req);
-		return retval;
-	}
-
-	async_wait_for(req, &retval);
-	return retval;
-}
-
-async_sess_t *devman_device_connect(devman_handle_t handle, unsigned int flags)
-{
-	async_sess_t *sess;
-
-	if (flags & IPC_FLAG_BLOCKING)
-		sess = service_connect_blocking(SERVICE_DEVMAN,
-		    INTERFACE_DEVMAN_DEVICE, handle, NULL);
-	else
-		sess = service_connect(SERVICE_DEVMAN,
-		    INTERFACE_DEVMAN_DEVICE, handle, NULL);
-
-	return sess;
-}
-
-/** Remove function from device.
- *
- * Request devman to remove function owned by this driver task.
- * @param funh      Devman handle of the function
- *
- * @return EOK on success or an error code.
- */
-errno_t devman_remove_function(devman_handle_t funh)
-{
-	async_exch_t *exch;
-	errno_t retval;
-
-	exch = devman_exchange_begin_blocking(INTERFACE_DDF_DRIVER);
-	retval = async_req_1_0(exch, DEVMAN_REMOVE_FUNCTION, (sysarg_t) funh);
-	devman_exchange_end(exch);
-
-	return retval;
-}
-
-errno_t devman_drv_fun_online(devman_handle_t funh)
-{
-	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_DRIVER);
-	if (exch == NULL)
-		return ENOMEM;
-
-	errno_t retval = async_req_1_0(exch, DEVMAN_DRV_FUN_ONLINE, funh);
-
-	devman_exchange_end(exch);
-	return retval;
-}
-
-errno_t devman_drv_fun_offline(devman_handle_t funh)
-{
-	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_DRIVER);
-	if (exch == NULL)
-		return ENOMEM;
-
-	errno_t retval = async_req_1_0(exch, DEVMAN_DRV_FUN_OFFLINE, funh);
-
-	devman_exchange_end(exch);
-	return retval;
-}
-
-async_sess_t *devman_parent_device_connect(devman_handle_t handle,
-    unsigned int flags)
-{
-	async_sess_t *sess;
-
-	if (flags & IPC_FLAG_BLOCKING)
-		sess = service_connect_blocking(SERVICE_DEVMAN,
-		    INTERFACE_DEVMAN_PARENT, handle, NULL);
-	else
-		sess = service_connect(SERVICE_DEVMAN,
-		    INTERFACE_DEVMAN_PARENT, handle, NULL);
-
-	return sess;
-}
-
-errno_t devman_fun_get_handle(const char *pathname, devman_handle_t *handle,
-    unsigned int flags)
-{
-	async_exch_t *exch;
-
-	if (flags & IPC_FLAG_BLOCKING)
-		exch = devman_exchange_begin_blocking(INTERFACE_DDF_CLIENT);
-	else {
-		exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
-		if (exch == NULL)
-			return ENOMEM;
-	}
-
-	ipc_call_t answer;
-	aid_t req = async_send_2(exch, DEVMAN_DEVICE_GET_HANDLE, flags, 0,
-	    &answer);
-	errno_t retval = async_data_write_start(exch, pathname,
-	    str_size(pathname));
-
-	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;
-}
-
-static errno_t 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;
-	ipc_call_t dreply;
-	size_t act_size;
-	errno_t dretval;
-
-	exch = devman_exchange_begin_blocking(INTERFACE_DDF_CLIENT);
-
-	ipc_call_t 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);
-
-	devman_exchange_end(exch);
-
-	if (dretval != EOK) {
-		async_forget(req);
-		return dretval;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK) {
-		return retval;
-	}
-
-	if (r1 != NULL)
-		*r1 = ipc_get_arg1(&answer);
-	act_size = ipc_get_arg2(&dreply);
-	assert(act_size <= buf_size - 1);
-	buf[act_size] = '\0';
-
-	return EOK;
-}
-
-errno_t devman_fun_get_path(devman_handle_t handle, char *buf, size_t buf_size)
-{
-	return devman_get_str_internal(DEVMAN_FUN_GET_PATH, handle, 0, NULL,
-	    buf, buf_size);
-}
-
-errno_t devman_fun_get_match_id(devman_handle_t handle, size_t index, char *buf,
-    size_t buf_size, unsigned int *rscore)
-{
-	errno_t 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;
-}
-
-errno_t devman_fun_get_name(devman_handle_t handle, char *buf, size_t buf_size)
-{
-	return devman_get_str_internal(DEVMAN_FUN_GET_NAME, handle, 0, NULL,
-	    buf, buf_size);
-}
-
-errno_t 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, 0,
-	    NULL, buf, buf_size);
-}
-
-errno_t devman_fun_online(devman_handle_t funh)
-{
-	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
-	if (exch == NULL)
-		return ENOMEM;
-
-	errno_t retval = async_req_1_0(exch, DEVMAN_FUN_ONLINE, funh);
-
-	devman_exchange_end(exch);
-	return retval;
-}
-
-errno_t devman_fun_offline(devman_handle_t funh)
-{
-	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
-	if (exch == NULL)
-		return ENOMEM;
-
-	errno_t retval = async_req_1_0(exch, DEVMAN_FUN_OFFLINE, funh);
-
-	devman_exchange_end(exch);
-	return retval;
-}
-
-static errno_t devman_get_handles_once(sysarg_t method, sysarg_t arg1,
-    devman_handle_t *handle_buf, size_t buf_size, size_t *act_size)
-{
-	async_exch_t *exch = devman_exchange_begin_blocking(INTERFACE_DDF_CLIENT);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, method, arg1, &answer);
-	errno_t rc = async_data_read_start(exch, handle_buf, buf_size);
-
-	devman_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK) {
-		return retval;
-	}
-
-	*act_size = ipc_get_arg1(&answer);
-	return EOK;
-}
-
-/** Get list of handles.
- *
- * Returns an allocated array of handles.
- *
- * @param method	IPC method
- * @param arg1		IPC argument 1
- * @param data		Place to store pointer to array of handles
- * @param count		Place to store number of handles
- * @return 		EOK on success or an error code
- */
-static errno_t devman_get_handles_internal(sysarg_t method, sysarg_t arg1,
-    devman_handle_t **data, size_t *count)
-{
-	devman_handle_t *handles;
-	size_t act_size;
-	size_t alloc_size;
-	errno_t rc;
-
-	*data = NULL;
-	act_size = 0;	/* silence warning */
-
-	rc = devman_get_handles_once(method, arg1, NULL, 0,
-	    &act_size);
-	if (rc != EOK)
-		return rc;
-
-	alloc_size = act_size;
-	handles = malloc(alloc_size);
-	if (handles == NULL)
-		return ENOMEM;
-
-	while (true) {
-		rc = devman_get_handles_once(method, arg1, handles, alloc_size,
-		    &act_size);
-		if (rc != EOK)
-			return rc;
-
-		if (act_size <= alloc_size)
-			break;
-
-		alloc_size *= 2;
-		free(handles);
-
-		handles = malloc(alloc_size);
-		if (handles == NULL)
-			return ENOMEM;
-	}
-
-	*count = act_size / sizeof(devman_handle_t);
-	*data = handles;
-	return EOK;
-}
-
-errno_t devman_fun_get_child(devman_handle_t funh, devman_handle_t *devh)
-{
-	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
-	if (exch == NULL)
-		return ENOMEM;
-
-	errno_t retval = async_req_1_1(exch, DEVMAN_FUN_GET_CHILD,
-	    funh, devh);
-
-	devman_exchange_end(exch);
-	return retval;
-}
-
-errno_t devman_dev_get_functions(devman_handle_t devh, devman_handle_t **funcs,
-    size_t *count)
-{
-	return devman_get_handles_internal(DEVMAN_DEV_GET_FUNCTIONS,
-	    devh, funcs, count);
-}
-
-errno_t devman_dev_get_parent(devman_handle_t devh, devman_handle_t *funh)
-{
-	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
-	if (exch == NULL)
-		return ENOMEM;
-
-	errno_t retval = async_req_1_1(exch, DEVMAN_DEV_GET_PARENT,
-	    devh, funh);
-
-	devman_exchange_end(exch);
-	return retval;
-}
-
-errno_t devman_fun_sid_to_handle(service_id_t sid, devman_handle_t *handle)
-{
-	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
-	if (exch == NULL)
-		return ENOMEM;
-
-	errno_t retval = async_req_1_1(exch, DEVMAN_FUN_SID_TO_HANDLE,
-	    sid, handle);
-
-	devman_exchange_end(exch);
-	return retval;
-}
-
-errno_t devman_get_drivers(devman_handle_t **drvs,
-    size_t *count)
-{
-	return devman_get_handles_internal(DEVMAN_GET_DRIVERS, 0, drvs, count);
-}
-
-errno_t 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);
-}
-
-errno_t devman_driver_get_handle(const char *drvname, devman_handle_t *handle)
-{
-	async_exch_t *exch;
-
-	exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
-	if (exch == NULL)
-		return ENOMEM;
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, DEVMAN_DRIVER_GET_HANDLE, &answer);
-	errno_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;
-}
-
-errno_t devman_driver_get_match_id(devman_handle_t handle, size_t index, char *buf,
-    size_t buf_size, unsigned int *rscore)
-{
-	errno_t 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;
-}
-
-errno_t 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);
-}
-
-errno_t devman_driver_get_state(devman_handle_t drvh, driver_state_t *rstate)
-{
-	sysarg_t state;
-	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
-	if (exch == NULL)
-		return ENOMEM;
-
-	errno_t 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;
-}
-
-errno_t devman_driver_load(devman_handle_t drvh)
-{
-	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
-	if (exch == NULL)
-		return ENOMEM;
-
-	errno_t rc = async_req_1_0(exch, DEVMAN_DRIVER_LOAD, drvh);
-
-	devman_exchange_end(exch);
-	return rc;
-}
-
-errno_t devman_driver_unload(devman_handle_t drvh)
-{
-	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
-	if (exch == NULL)
-		return ENOMEM;
-
-	errno_t rc = async_req_1_0(exch, DEVMAN_DRIVER_UNLOAD, drvh);
-
-	devman_exchange_end(exch);
-	return rc;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/dhcp.c
===================================================================
--- uspace/lib/c/generic/dhcp.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,97 +1,0 @@
-/*
- * 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;
-
-errno_t dhcp_init(void)
-{
-	service_id_t dhcp_svc;
-	errno_t 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(dhcp_svc, INTERFACE_DHCP,
-	    IPC_FLAG_BLOCKING);
-	if (dhcp_sess == NULL)
-		return ENOENT;
-
-	return EOK;
-}
-
-errno_t dhcp_link_add(sysarg_t link_id)
-{
-	async_exch_t *exch = async_exchange_begin(dhcp_sess);
-
-	errno_t rc = async_req_1_0(exch, DHCP_LINK_ADD, link_id);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-errno_t dhcp_link_remove(sysarg_t link_id)
-{
-	async_exch_t *exch = async_exchange_begin(dhcp_sess);
-
-	errno_t rc = async_req_1_0(exch, DHCP_LINK_REMOVE, link_id);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-errno_t dhcp_discover(sysarg_t link_id)
-{
-	async_exch_t *exch = async_exchange_begin(dhcp_sess);
-
-	errno_t rc = async_req_1_0(exch, DHCP_DISCOVER, link_id);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/dnsr.c
===================================================================
--- uspace/lib/c/generic/dnsr.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,195 +1,0 @@
-/*
- * 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 <errno.h>
-#include <fibril_synch.h>
-#include <inet/dnsr.h>
-#include <ipc/dnsr.h>
-#include <ipc/services.h>
-#include <loc.h>
-#include <stdlib.h>
-#include <str.h>
-
-static FIBRIL_MUTEX_INITIALIZE(dnsr_sess_mutex);
-
-static async_sess_t *dnsr_sess = NULL;
-
-static async_exch_t *dnsr_exchange_begin(void)
-{
-	fibril_mutex_lock(&dnsr_sess_mutex);
-
-	if (dnsr_sess == NULL) {
-		service_id_t dnsr_svc;
-
-		(void) loc_service_get_id(SERVICE_NAME_DNSR, &dnsr_svc,
-		    IPC_FLAG_BLOCKING);
-
-		dnsr_sess = loc_service_connect(dnsr_svc, INTERFACE_DNSR,
-		    IPC_FLAG_BLOCKING);
-	}
-
-	async_sess_t *sess = dnsr_sess;
-	fibril_mutex_unlock(&dnsr_sess_mutex);
-
-	return async_exchange_begin(sess);
-}
-
-static void dnsr_exchange_end(async_exch_t *exch)
-{
-	async_exchange_end(exch);
-}
-
-errno_t dnsr_name2host(const char *name, dnsr_hostinfo_t **rinfo, ip_ver_t ver)
-{
-	async_exch_t *exch = dnsr_exchange_begin();
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, DNSR_NAME2HOST, (sysarg_t) ver,
-	    &answer);
-
-	errno_t rc = async_data_write_start(exch, name, str_size(name));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-
-	dnsr_hostinfo_t *info = calloc(1, sizeof(dnsr_hostinfo_t));
-	if (info == NULL)
-		return ENOMEM;
-
-	ipc_call_t answer_addr;
-	aid_t req_addr = async_data_read(exch, &info->addr,
-	    sizeof(inet_addr_t), &answer_addr);
-
-	errno_t retval_addr;
-	async_wait_for(req_addr, &retval_addr);
-
-	if (retval_addr != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		free(info);
-		return retval_addr;
-	}
-
-	ipc_call_t answer_cname;
-	char cname_buf[DNSR_NAME_MAX_SIZE + 1];
-	aid_t req_cname = async_data_read(exch, cname_buf, DNSR_NAME_MAX_SIZE,
-	    &answer_cname);
-
-	dnsr_exchange_end(exch);
-
-	errno_t retval_cname;
-	async_wait_for(req_cname, &retval_cname);
-
-	if (retval_cname != EOK) {
-		async_forget(req);
-		free(info);
-		return retval_cname;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK) {
-		async_forget(req);
-		free(info);
-		return retval;
-	}
-
-	size_t act_size = ipc_get_arg2(&answer_cname);
-	assert(act_size <= DNSR_NAME_MAX_SIZE);
-
-	cname_buf[act_size] = '\0';
-
-	info->cname = str_dup(cname_buf);
-
-	if (info->cname == NULL) {
-		free(info);
-		return ENOMEM;
-	}
-
-	*rinfo = info;
-	return EOK;
-}
-
-void dnsr_hostinfo_destroy(dnsr_hostinfo_t *info)
-{
-	if (info == NULL)
-		return;
-
-	free(info->cname);
-	free(info);
-}
-
-errno_t dnsr_get_srvaddr(inet_addr_t *srvaddr)
-{
-	async_exch_t *exch = dnsr_exchange_begin();
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, DNSR_GET_SRVADDR, &answer);
-	errno_t rc = async_data_read_start(exch, srvaddr, sizeof(inet_addr_t));
-
-	loc_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-errno_t dnsr_set_srvaddr(inet_addr_t *srvaddr)
-{
-	async_exch_t *exch = dnsr_exchange_begin();
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, DNSR_SET_SRVADDR, &answer);
-	errno_t rc = async_data_write_start(exch, srvaddr, sizeof(inet_addr_t));
-
-	loc_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-/** @}
- */
Index: uspace/lib/c/generic/double_to_str.c
===================================================================
--- uspace/lib/c/generic/double_to_str.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/generic/double_to_str.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -245,5 +245,5 @@
 }
 
-/** Rounds the last digit of buf so that it is closest to the converted number.*/
+/** Rounds the last digit of buf so that it is closest to the converted number. */
 static void round_last_digit(uint64_t rest, uint64_t w_dist, uint64_t delta,
     uint64_t digit_val_diff, char *buf, int len)
Index: uspace/lib/c/generic/getopt.c
===================================================================
--- uspace/lib/c/generic/getopt.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/generic/getopt.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -56,5 +56,5 @@
 #define PRINT_ERROR	((opterr) && ((*options != ':') \
 				      || (IGNORE_FIRST && options[1] != ':')))
-/*HelenOS Port - POSIXLY_CORRECT is always false */
+/* HelenOS Port - POSIXLY_CORRECT is always false */
 #define IS_POSIXLY_CORRECT 0
 #define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
Index: pace/lib/c/generic/inet.c
===================================================================
--- uspace/lib/c/generic/inet.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,258 +1,0 @@
-/*
- * 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 <errno.h>
-#include <inet/inet.h>
-#include <ipc/inet.h>
-#include <ipc/services.h>
-#include <loc.h>
-#include <stdlib.h>
-
-static void inet_cb_conn(ipc_call_t *icall, void *arg);
-
-static async_sess_t *inet_sess = NULL;
-static inet_ev_ops_t *inet_ev_ops = NULL;
-static uint8_t inet_protocol = 0;
-
-static errno_t inet_callback_create(void)
-{
-	async_exch_t *exch = async_exchange_begin(inet_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, INET_CALLBACK_CREATE, &answer);
-
-	port_id_t port;
-	errno_t rc = async_create_callback_port(exch, INTERFACE_INET_CB, 0, 0,
-	    inet_cb_conn, NULL, &port);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK)
-		return rc;
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-static errno_t inet_set_proto(uint8_t protocol)
-{
-	async_exch_t *exch = async_exchange_begin(inet_sess);
-	errno_t rc = async_req_1_0(exch, INET_SET_PROTO, protocol);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-errno_t inet_init(uint8_t protocol, inet_ev_ops_t *ev_ops)
-{
-	service_id_t inet_svc;
-	errno_t rc;
-
-	assert(inet_sess == NULL);
-	assert(inet_ev_ops == NULL);
-	assert(inet_protocol == 0);
-
-	rc = loc_service_get_id(SERVICE_NAME_INET, &inet_svc,
-	    IPC_FLAG_BLOCKING);
-	if (rc != EOK)
-		return ENOENT;
-
-	inet_sess = loc_service_connect(inet_svc, INTERFACE_INET,
-	    IPC_FLAG_BLOCKING);
-	if (inet_sess == NULL)
-		return ENOENT;
-
-	if (inet_set_proto(protocol) != EOK) {
-		async_hangup(inet_sess);
-		inet_sess = NULL;
-		return EIO;
-	}
-
-	if (inet_callback_create() != EOK) {
-		async_hangup(inet_sess);
-		inet_sess = NULL;
-		return EIO;
-	}
-
-	inet_protocol = protocol;
-	inet_ev_ops = ev_ops;
-
-	return EOK;
-}
-
-errno_t inet_send(inet_dgram_t *dgram, uint8_t ttl, inet_df_t df)
-{
-	async_exch_t *exch = async_exchange_begin(inet_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_4(exch, INET_SEND, dgram->iplink, dgram->tos,
-	    ttl, df, &answer);
-
-	errno_t rc = async_data_write_start(exch, &dgram->src, sizeof(inet_addr_t));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-
-	rc = async_data_write_start(exch, &dgram->dest, sizeof(inet_addr_t));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-
-	rc = async_data_write_start(exch, dgram->data, dgram->size);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-errno_t inet_get_srcaddr(inet_addr_t *remote, uint8_t tos, inet_addr_t *local)
-{
-	async_exch_t *exch = async_exchange_begin(inet_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, INET_GET_SRCADDR, tos, &answer);
-
-	errno_t rc = async_data_write_start(exch, remote, sizeof(inet_addr_t));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-
-	rc = async_data_read_start(exch, local, sizeof(inet_addr_t));
-
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-static void inet_ev_recv(ipc_call_t *icall)
-{
-	inet_dgram_t dgram;
-
-	dgram.tos = ipc_get_arg1(icall);
-	dgram.iplink = ipc_get_arg2(icall);
-
-	ipc_call_t call;
-	size_t size;
-	if (!async_data_write_receive(&call, &size)) {
-		async_answer_0(&call, EINVAL);
-		async_answer_0(icall, EINVAL);
-		return;
-	}
-
-	if (size != sizeof(inet_addr_t)) {
-		async_answer_0(&call, EINVAL);
-		async_answer_0(icall, EINVAL);
-		return;
-	}
-
-	errno_t rc = async_data_write_finalize(&call, &dgram.src, size);
-	if (rc != EOK) {
-		async_answer_0(&call, rc);
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	if (!async_data_write_receive(&call, &size)) {
-		async_answer_0(&call, EINVAL);
-		async_answer_0(icall, EINVAL);
-		return;
-	}
-
-	if (size != sizeof(inet_addr_t)) {
-		async_answer_0(&call, EINVAL);
-		async_answer_0(icall, EINVAL);
-		return;
-	}
-
-	rc = async_data_write_finalize(&call, &dgram.dest, size);
-	if (rc != EOK) {
-		async_answer_0(&call, rc);
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	rc = async_data_write_accept(&dgram.data, false, 0, 0, 0, &dgram.size);
-	if (rc != EOK) {
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	rc = inet_ev_ops->recv(&dgram);
-	free(dgram.data);
-	async_answer_0(icall, rc);
-}
-
-static void inet_cb_conn(ipc_call_t *icall, void *arg)
-{
-	while (true) {
-		ipc_call_t call;
-		async_get_call(&call);
-
-		if (!ipc_get_imethod(&call)) {
-			async_answer_0(&call, EOK);
-			return;
-		}
-
-		switch (ipc_get_imethod(&call)) {
-		case INET_EV_RECV:
-			inet_ev_recv(&call);
-			break;
-		default:
-			async_answer_0(&call, ENOTSUP);
-		}
-	}
-}
-
-/** @}
- */
Index: pace/lib/c/generic/inet/addr.c
===================================================================
--- uspace/lib/c/generic/inet/addr.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,735 +1,0 @@
-/*
- * Copyright (c) 2013 Jiri Svoboda
- * 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 Internet address parsing and formatting.
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <inet/addr.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <bitops.h>
-#include <inttypes.h>
-#include <str.h>
-
-#define INET_PREFIXSTRSIZE  5
-
-#define INET6_ADDRSTRLEN (8 * 4 + 7 + 1)
-
-#if !(defined(__BE__) ^ defined(__LE__))
-#error The architecture must be either big-endian or little-endian.
-#endif
-
-const addr32_t addr32_broadcast_all_hosts = 0xffffffff;
-
-const addr48_t addr48_broadcast = {
-	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
-};
-
-static const addr48_t inet_addr48_solicited_node = {
-	0x33, 0x33, 0xff, 0, 0, 0
-};
-
-static const inet_addr_t inet_addr_any_addr = {
-	.version = ip_v4,
-	.addr = 0
-};
-
-static const inet_addr_t inet_addr_any_addr6 = {
-	.version = ip_v6,
-	.addr6 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
-};
-
-void addr48(const addr48_t src, addr48_t dst)
-{
-	memcpy(dst, src, 6);
-}
-
-void addr128(const addr128_t src, addr128_t dst)
-{
-	memcpy(dst, src, 16);
-}
-
-/** 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) == 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) == 0;
-}
-
-/** Compute solicited node MAC multicast address from target IPv6 address
- *
- * @param ip  Target IPv6 address
- * @param mac Solicited MAC address to be assigned
- *
- */
-void addr48_solicited_node(const addr128_t ip, addr48_t mac)
-{
-	memcpy(mac, inet_addr48_solicited_node, 3);
-	memcpy(mac + 3, ip + 13, 3);
-}
-
-void host2addr128_t_be(const addr128_t host, addr128_t be)
-{
-	memcpy(be, host, 16);
-}
-
-void addr128_t_be2host(const addr128_t be, addr128_t host)
-{
-	memcpy(host, be, 16);
-}
-
-void inet_addr(inet_addr_t *addr, uint8_t a, uint8_t b, uint8_t c, uint8_t d)
-{
-	addr->version = ip_v4;
-	addr->addr = ((addr32_t) a << 24) | ((addr32_t) b << 16) |
-	    ((addr32_t) c << 8) | ((addr32_t) d);
-}
-
-void inet_naddr(inet_naddr_t *naddr, uint8_t a, uint8_t b, uint8_t c, uint8_t d,
-    uint8_t prefix)
-{
-	naddr->version = ip_v4;
-	naddr->addr = ((addr32_t) a << 24) | ((addr32_t) b << 16) |
-	    ((addr32_t) c << 8) | ((addr32_t) d);
-	naddr->prefix = prefix;
-}
-
-void inet_addr6(inet_addr_t *addr, uint16_t a, uint16_t b, uint16_t c,
-    uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h)
-{
-	addr->version = ip_v6;
-	addr->addr6[0] = (a >> 8) & 0xff;
-	addr->addr6[1] = a & 0xff;
-	addr->addr6[2] = (b >> 8) & 0xff;
-	addr->addr6[3] = b & 0xff;
-	addr->addr6[4] = (c >> 8) & 0xff;
-	addr->addr6[5] = c & 0xff;
-	addr->addr6[6] = (d >> 8) & 0xff;
-	addr->addr6[7] = d & 0xff;
-	addr->addr6[8] = (e >> 8) & 0xff;
-	addr->addr6[9] = e & 0xff;
-	addr->addr6[10] = (f >> 8) & 0xff;
-	addr->addr6[11] = f & 0xff;
-	addr->addr6[12] = (g >> 8) & 0xff;
-	addr->addr6[13] = g & 0xff;
-	addr->addr6[14] = (h >> 8) & 0xff;
-	addr->addr6[15] = h & 0xff;
-}
-
-void inet_naddr6(inet_naddr_t *naddr, uint16_t a, uint16_t b, uint16_t c,
-    uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h, uint8_t prefix)
-{
-	naddr->version = ip_v6;
-	naddr->addr6[0] = (a >> 8) & 0xff;
-	naddr->addr6[1] = a & 0xff;
-	naddr->addr6[2] = (b >> 8) & 0xff;
-	naddr->addr6[3] = b & 0xff;
-	naddr->addr6[4] = (c >> 8) & 0xff;
-	naddr->addr6[5] = c & 0xff;
-	naddr->addr6[6] = (d >> 8) & 0xff;
-	naddr->addr6[7] = d & 0xff;
-	naddr->addr6[8] = (e >> 8) & 0xff;
-	naddr->addr6[9] = e & 0xff;
-	naddr->addr6[10] = (f >> 8) & 0xff;
-	naddr->addr6[11] = f & 0xff;
-	naddr->addr6[12] = (g >> 8) & 0xff;
-	naddr->addr6[13] = g & 0xff;
-	naddr->addr6[14] = (h >> 8) & 0xff;
-	naddr->addr6[15] = h & 0xff;
-	naddr->prefix = prefix;
-}
-
-void inet_naddr_addr(const inet_naddr_t *naddr, inet_addr_t *addr)
-{
-	addr->version = naddr->version;
-	memcpy(addr->addr6, naddr->addr6, 16);
-}
-
-void inet_addr_naddr(const inet_addr_t *addr, uint8_t prefix,
-    inet_naddr_t *naddr)
-{
-	naddr->version = addr->version;
-	memcpy(naddr->addr6, addr->addr6, 16);
-	naddr->prefix = prefix;
-}
-
-void inet_addr_any(inet_addr_t *addr)
-{
-	addr->version = ip_any;
-	memset(addr->addr6, 0, 16);
-}
-
-void inet_naddr_any(inet_naddr_t *naddr)
-{
-	naddr->version = ip_any;
-	memset(naddr->addr6, 0, 16);
-	naddr->prefix = 0;
-}
-
-int inet_addr_compare(const inet_addr_t *a, const inet_addr_t *b)
-{
-	if (a->version != b->version)
-		return 0;
-
-	switch (a->version) {
-	case ip_v4:
-		return (a->addr == b->addr);
-	case ip_v6:
-		return addr128_compare(a->addr6, b->addr6);
-	default:
-		return 0;
-	}
-}
-
-int inet_addr_is_any(const inet_addr_t *addr)
-{
-	return ((addr->version == ip_any) ||
-	    (inet_addr_compare(addr, &inet_addr_any_addr)) ||
-	    (inet_addr_compare(addr, &inet_addr_any_addr6)));
-}
-
-int inet_naddr_compare(const inet_naddr_t *naddr, const inet_addr_t *addr)
-{
-	if (naddr->version != addr->version)
-		return 0;
-
-	switch (naddr->version) {
-	case ip_v4:
-		return (naddr->addr == addr->addr);
-	case ip_v6:
-		return addr128_compare(naddr->addr6, addr->addr6);
-	default:
-		return 0;
-	}
-}
-
-int inet_naddr_compare_mask(const inet_naddr_t *naddr, const inet_addr_t *addr)
-{
-	if (naddr->version != addr->version)
-		return 0;
-
-	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 ip_v6:
-		if (naddr->prefix > 128)
-			return 0;
-
-		size_t pos = 0;
-		for (size_t i = 0; i < 16; i++) {
-			/* Further bits do not matter */
-			if (naddr->prefix < pos)
-				break;
-
-			if (naddr->prefix - pos > 8) {
-				/* Comparison without masking */
-				if (naddr->addr6[i] != addr->addr6[i])
-					return 0;
-			} else {
-				/* Comparison with masking */
-				uint8_t mask =
-				    BIT_RANGE(uint8_t, 8, 8 - (naddr->prefix - pos - 1));
-				if ((naddr->addr6[i] & mask) != (addr->addr6[i] & mask))
-					return 0;
-			}
-
-			pos += 8;
-		}
-
-		return 1;
-	default:
-		return 0;
-	}
-}
-
-static errno_t inet_addr_parse_v4(const char *str, inet_addr_t *raddr,
-    int *prefix, char **endptr)
-{
-	uint32_t a = 0;
-	uint8_t b;
-	char *cur = (char *)str;
-	size_t i = 0;
-
-	while (i < 4) {
-		errno_t rc = str_uint8_t(cur, (const char **)&cur, 10, false, &b);
-		if (rc != EOK)
-			return rc;
-
-		a = (a << 8) + b;
-
-		i++;
-
-		if (*cur != '.')
-			break;
-
-		if (i < 4)
-			cur++;
-	}
-
-	if (prefix != NULL) {
-		if (*cur != '/')
-			return EINVAL;
-		cur++;
-
-		*prefix = strtoul(cur, &cur, 10);
-		if (*prefix > 32)
-			return EINVAL;
-	}
-
-	if (i != 4)
-		return EINVAL;
-
-	if (endptr == NULL && *cur != '\0')
-		return EINVAL;
-
-	raddr->version = ip_v4;
-	raddr->addr = a;
-
-	if (endptr != NULL)
-		*endptr = cur;
-
-	return EOK;
-}
-
-static errno_t inet_addr_parse_v6(const char *str, inet_addr_t *raddr, int *prefix,
-    char **endptr)
-{
-	uint8_t data[16];
-	int explicit_groups;
-
-	memset(data, 0, 16);
-
-	const char *cur = str;
-	size_t i = 0;
-	size_t wildcard_pos = (size_t) -1;
-	size_t wildcard_size = 0;
-
-	/* Handle initial wildcard */
-	if ((str[0] == ':') && (str[1] == ':')) {
-		cur = str + 2;
-		wildcard_pos = 0;
-		wildcard_size = 16;
-	}
-
-	while (i < 16) {
-		uint16_t bioctet;
-		const char *gend;
-		errno_t rc = str_uint16_t(cur, &gend, 16, false, &bioctet);
-		if (rc != EOK)
-			break;
-
-		data[i] = (bioctet >> 8) & 0xff;
-		data[i + 1] = bioctet & 0xff;
-
-		if (wildcard_pos != (size_t) -1) {
-			if (wildcard_size < 2)
-				return EINVAL;
-
-			wildcard_size -= 2;
-		}
-
-		i += 2;
-
-		if (*gend != ':') {
-			cur = gend;
-			break;
-		}
-
-		if (i < 16) {
-			/* Handle wildcard */
-			if (gend[1] == ':') {
-				if (wildcard_pos != (size_t) -1)
-					return EINVAL;
-
-				wildcard_pos = i;
-				wildcard_size = 16 - i;
-				cur = gend + 2;
-			}
-		}
-	}
-
-	/* Number of explicitly specified groups */
-	explicit_groups = i;
-
-	if (prefix != NULL) {
-		if (*cur != '/')
-			return EINVAL;
-		cur++;
-
-		*prefix = strtoul(cur, (char **)&cur, 10);
-		if (*prefix > 128)
-			return EINVAL;
-	}
-
-	if (endptr == NULL && *cur != '\0')
-		return EINVAL;
-
-	/* Create wildcard positions */
-	if ((wildcard_pos != (size_t) -1) && (wildcard_size > 0)) {
-		size_t wildcard_shift = 16 - wildcard_size;
-
-		for (i = wildcard_pos + wildcard_shift; i > wildcard_pos; i--) {
-			size_t j = i - 1;
-			data[j + wildcard_size] = data[j];
-			data[j] = 0;
-		}
-	} else {
-		/* Verify that all groups have been specified */
-		if (explicit_groups != 16)
-			return EINVAL;
-	}
-
-	raddr->version = ip_v6;
-	memcpy(raddr->addr6, data, 16);
-	if (endptr != NULL)
-		*endptr = (char *)cur;
-	return EOK;
-}
-
-/** Parse node address.
- *
- * Will fail if @a text contains extra characters at the and and @a endptr
- * is @c NULL.
- *
- * @param text Network address in common notation.
- * @param addr Place to store node address.
- * @param endptr Place to store pointer to next character oc @c NULL
- *
- * @return EOK on success, EINVAL if input is not in valid format.
- *
- */
-errno_t inet_addr_parse(const char *text, inet_addr_t *addr, char **endptr)
-{
-	errno_t rc;
-
-	rc = inet_addr_parse_v4(text, addr, NULL, endptr);
-	if (rc == EOK)
-		return EOK;
-
-	rc = inet_addr_parse_v6(text, addr, NULL, endptr);
-	if (rc == EOK)
-		return EOK;
-
-	return EINVAL;
-}
-
-/** Parse network address.
- *
- * Will fail if @a text contains extra characters at the and and @a endptr
- * is @c NULL.
- *
- * @param text  Network address in common notation.
- * @param naddr Place to store network address.
- * @param endptr Place to store pointer to next character oc @c NULL
- *
- * @return EOK on success, EINVAL if input is not in valid format.
- *
- */
-errno_t inet_naddr_parse(const char *text, inet_naddr_t *naddr, char **endptr)
-{
-	errno_t rc;
-	inet_addr_t addr;
-	int prefix;
-
-	rc = inet_addr_parse_v4(text, &addr, &prefix, endptr);
-	if (rc == EOK) {
-		inet_addr_naddr(&addr, prefix, naddr);
-		return EOK;
-	}
-
-	rc = inet_addr_parse_v6(text, &addr, &prefix, endptr);
-	if (rc == EOK) {
-		inet_addr_naddr(&addr, prefix, naddr);
-		return EOK;
-	}
-
-	return EINVAL;
-}
-
-static errno_t inet_addr_format_v4(addr32_t addr, char **bufp)
-{
-	int rc;
-
-	rc = asprintf(bufp, "%u.%u.%u.%u", (addr >> 24) & 0xff,
-	    (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
-	if (rc < 0)
-		return ENOMEM;
-
-	return EOK;
-}
-
-static errno_t inet_addr_format_v6(const addr128_t addr, char **bufp)
-{
-	*bufp = (char *) malloc(INET6_ADDRSTRLEN);
-	if (*bufp == NULL)
-		return ENOMEM;
-
-	/* Find the longest zero subsequence */
-
-	uint16_t zeroes[8];
-	uint16_t bioctets[8];
-
-	for (size_t i = 8; i > 0; i--) {
-		size_t j = i - 1;
-
-		bioctets[j] = (addr[j << 1] << 8) | addr[(j << 1) + 1];
-
-		if (bioctets[j] == 0) {
-			zeroes[j] = 1;
-			if (j < 7)
-				zeroes[j] += zeroes[j + 1];
-		} else
-			zeroes[j] = 0;
-	}
-
-	size_t wildcard_pos = (size_t) -1;
-	size_t wildcard_size = 0;
-
-	for (size_t i = 0; i < 8; i++) {
-		if (zeroes[i] > wildcard_size) {
-			wildcard_pos = i;
-			wildcard_size = zeroes[i];
-		}
-	}
-
-	char *cur = *bufp;
-	size_t rest = INET6_ADDRSTRLEN;
-	bool tail_zero = false;
-	int ret;
-
-	for (size_t i = 0; i < 8; i++) {
-		if ((i == wildcard_pos) && (wildcard_size > 1)) {
-			ret = snprintf(cur, rest, ":");
-			i += wildcard_size - 1;
-			tail_zero = true;
-		} else if (i == 0) {
-			ret = snprintf(cur, rest, "%" PRIx16, bioctets[i]);
-			tail_zero = false;
-		} else {
-			ret = snprintf(cur, rest, ":%" PRIx16, bioctets[i]);
-			tail_zero = false;
-		}
-
-		if (ret < 0)
-			return EINVAL;
-
-		cur += ret;
-		rest -= ret;
-	}
-
-	if (tail_zero)
-		(void) snprintf(cur, rest, ":");
-
-	return EOK;
-}
-
-/** Format node address.
- *
- * @param addr Node address.
- * @param bufp Place to store pointer to formatted string.
- *
- * @return EOK on success.
- * @return ENOMEM if out of memory.
- * @return ENOTSUP on unsupported address family.
- *
- */
-errno_t inet_addr_format(const inet_addr_t *addr, char **bufp)
-{
-	errno_t rc;
-	int ret;
-
-	rc = ENOTSUP;
-
-	switch (addr->version) {
-	case ip_any:
-		ret = asprintf(bufp, "none");
-		if (ret < 0)
-			return ENOMEM;
-		rc = EOK;
-		break;
-	case ip_v4:
-		rc = inet_addr_format_v4(addr->addr, bufp);
-		break;
-	case ip_v6:
-		rc = inet_addr_format_v6(addr->addr6, bufp);
-		break;
-	}
-
-	return rc;
-}
-
-/** Format network address.
- *
- * @param naddr Network address.
- * @param bufp  Place to store pointer to formatted string.
- *
- * @return EOK on success.
- * @return ENOMEM if out of memory.
- * @return ENOTSUP on unsupported address family.
- *
- */
-errno_t inet_naddr_format(const inet_naddr_t *naddr, char **bufp)
-{
-	errno_t rc;
-	int ret;
-	char *astr;
-
-	rc = ENOTSUP;
-
-	switch (naddr->version) {
-	case ip_any:
-		ret = asprintf(bufp, "none");
-		if (ret < 0)
-			return ENOMEM;
-		rc = EOK;
-		break;
-	case ip_v4:
-		rc = inet_addr_format_v4(naddr->addr, &astr);
-		if (rc != EOK)
-			return ENOMEM;
-
-		ret = asprintf(bufp, "%s/%" PRIu8, astr, naddr->prefix);
-		if (ret < 0) {
-			free(astr);
-			return ENOMEM;
-		}
-
-		rc = EOK;
-		break;
-	case ip_v6:
-		rc = inet_addr_format_v6(naddr->addr6, &astr);
-		if (rc != EOK)
-			return ENOMEM;
-
-		ret = asprintf(bufp, "%s/%" PRIu8, astr, naddr->prefix);
-		if (ret < 0) {
-			free(astr);
-			return ENOMEM;
-		}
-
-		rc = EOK;
-		break;
-	}
-
-	return rc;
-}
-
-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 ip_v6:
-		if (v6 != NULL)
-			memcpy(*v6, addr->addr6, 16);
-		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->version) {
-	case ip_v4:
-		if (v4 != NULL)
-			*v4 = naddr->addr;
-		if (prefix != NULL)
-			*prefix = naddr->prefix;
-		break;
-	case ip_v6:
-		if (v6 != NULL)
-			memcpy(*v6, naddr->addr6, 16);
-		if (prefix != NULL)
-			*prefix = naddr->prefix;
-		break;
-	default:
-		assert(false);
-		break;
-	}
-
-	return naddr->version;
-}
-
-void inet_addr_set(addr32_t v4, inet_addr_t *addr)
-{
-	addr->version = ip_v4;
-	addr->addr = v4;
-}
-
-void inet_naddr_set(addr32_t v4, uint8_t prefix, inet_naddr_t *naddr)
-{
-	naddr->version = ip_v4;
-	naddr->addr = v4;
-	naddr->prefix = prefix;
-}
-
-void inet_addr_set6(addr128_t v6, inet_addr_t *addr)
-{
-	addr->version = ip_v6;
-	memcpy(addr->addr6, v6, 16);
-}
-
-void inet_naddr_set6(addr128_t v6, uint8_t prefix, inet_naddr_t *naddr)
-{
-	naddr->version = ip_v6;
-	memcpy(naddr->addr6, v6, 16);
-	naddr->prefix = prefix;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/inet/endpoint.c
===================================================================
--- uspace/lib/c/generic/inet/endpoint.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,61 +1,0 @@
-/*
- * Copyright (c) 2015 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 Internet endpoint
- */
-
-#include <inet/endpoint.h>
-#include <mem.h>
-
-/** Initialize endpoint structure.
- *
- * Sets any address, any port number.
- *
- * @param ep Endpoint
- */
-void inet_ep_init(inet_ep_t *ep)
-{
-	memset(ep, 0, sizeof(*ep));
-}
-
-/** Initialize endpoint pair structure.
- *
- * Sets any address, any port number for both local and remote sides.
- *
- * @param ep2 Endpoint pair
- */
-void inet_ep2_init(inet_ep2_t *ep2)
-{
-	memset(ep2, 0, sizeof(*ep2));
-}
-
-/** @}
- */
Index: pace/lib/c/generic/inet/host.c
===================================================================
--- uspace/lib/c/generic/inet/host.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,245 +1,0 @@
-/*
- * Copyright (c) 2016 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 Internet host specification
- *
- * Either a host name or an address.
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <inet/addr.h>
-#include <inet/dnsr.h>
-#include <inet/host.h>
-#include <inet/hostname.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <str.h>
-
-/** Parse host string.
- *
- * @param str Host string
- * @param rhost Place to store pointer to new host structure
- * @param endptr Place to store pointer to next character in string or @c NULL
- *
- * @return EOK on success. EINVAL if @a str does not start with a valid
- *         host specification or if @a endptr is @c NULL and @a str contains
- *         extra characters at the end. ENOMEM if out of memory
- */
-errno_t inet_host_parse(const char *str, inet_host_t **rhost,
-    char **endptr)
-{
-	inet_host_t *host;
-	inet_addr_t addr;
-	char *name;
-	char *aend;
-	errno_t rc;
-
-	host = calloc(1, sizeof(inet_host_t));
-	if (host == NULL)
-		return ENOMEM;
-
-	/* Try address */
-	rc = inet_addr_parse(str, &addr, &aend);
-	if (rc == EOK) {
-		host->hform = inet_host_addr;
-		host->host.addr = addr;
-		goto have_host;
-	}
-
-	/* Try <hostname> */
-	rc = inet_hostname_parse(str, &name, &aend);
-	if (rc == EOK) {
-		host->hform = inet_host_name;
-		host->host.name = name;
-		goto have_host;
-	}
-
-	free(host);
-	return EINVAL;
-
-have_host:
-	if (*aend != '\0' && endptr == NULL) {
-		/* Extra characters */
-		free(host);
-		return EINVAL;
-	}
-
-	*rhost = host;
-	if (endptr != NULL)
-		*endptr = (char *)aend;
-	return EOK;
-}
-
-/** Convert host structure to string representation.
- *
- * @param host Host structure
- * @param rstr Place to store pointer to new string
- * @return EOK on success, ENOMEM if out of memory
- */
-errno_t inet_host_format(inet_host_t *host, char **rstr)
-{
-	errno_t rc;
-	char *str = NULL;
-
-	switch (host->hform) {
-	case inet_host_addr:
-		rc = inet_addr_format(&host->host.addr, &str);
-		if (rc != EOK) {
-			assert(rc == ENOMEM);
-			return ENOMEM;
-		}
-
-		break;
-	case inet_host_name:
-		str = str_dup(host->host.name);
-		if (str == NULL)
-			return ENOMEM;
-		break;
-	}
-
-	*rstr = str;
-	return EOK;
-}
-
-/** Destroy host structure.
- *
- * @param host Host structure or @c NULL
- */
-void inet_host_destroy(inet_host_t *host)
-{
-	if (host == NULL)
-		return;
-
-	switch (host->hform) {
-	case inet_host_addr:
-		break;
-	case inet_host_name:
-		free(host->host.name);
-		break;
-	}
-
-	free(host);
-}
-
-/** Look up first address corresponding to host.
- *
- * If host contains a host name, name resolution is performed.
- *
- * @param host Host structure
- * @param version Desired IP version (ip_any to get any version)
- * @param addr Place to store address
- *
- * @reutrn EOK on success, ENOENT on resolurion failure
- */
-errno_t inet_host_lookup_one(inet_host_t *host, ip_ver_t version, inet_addr_t *addr)
-{
-	dnsr_hostinfo_t *hinfo = NULL;
-	errno_t rc;
-
-	switch (host->hform) {
-	case inet_host_addr:
-		*addr = host->host.addr;
-		break;
-	case inet_host_name:
-		rc = dnsr_name2host(host->host.name, &hinfo, version);
-		if (rc != EOK) {
-			dnsr_hostinfo_destroy(hinfo);
-			return ENOENT;
-		}
-
-		*addr = hinfo->addr;
-		dnsr_hostinfo_destroy(hinfo);
-		break;
-	}
-
-	return EOK;
-}
-
-/** Look up first address corresponding to host string.
- *
- * If host contains a host name, name resolution is performed.
- *
- * @param str Host string
- * @param version Desired IP version (ip_any to get any version)
- * @param addr Place to store address
- * @param endptr Place to store pointer to next character in string or @c NULL
- * @param errmsg Place to store pointer to error message (no need to free it)
- *               or @c NULL
- *
- * @return EOK on success. EINVAL if @a str has incorrect format or if
- *         @a endptr is @c NULL and @a str contains extra characters at
- *         the end. ENOENT if host was not found during resolution,
- *         ENOMEM if out of memory
- */
-errno_t inet_host_plookup_one(const char *str, ip_ver_t version, inet_addr_t *addr,
-    char **endptr, const char **errmsg)
-{
-	inet_host_t *host = NULL;
-	char *eptr;
-	errno_t rc;
-
-	rc = inet_host_parse(str, &host, endptr != NULL ? &eptr : NULL);
-	if (rc != EOK) {
-		switch (rc) {
-		case EINVAL:
-			if (errmsg != NULL)
-				*errmsg = "Invalid format";
-			goto error;
-		case ENOMEM:
-			if (errmsg != NULL)
-				*errmsg = "Out of memory";
-			goto error;
-		default:
-			assert(false);
-		}
-	}
-
-	rc = inet_host_lookup_one(host, version, addr);
-	if (rc != EOK) {
-		/* XXX Distinguish between 'not found' and other error */
-		rc = ENOENT;
-		if (errmsg != NULL)
-			*errmsg = "Name resolution failed";
-		goto error;
-	}
-
-	inet_host_destroy(host);
-	if (endptr != NULL)
-		*endptr = eptr;
-	return EOK;
-error:
-	inet_host_destroy(host);
-	return rc;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/inet/hostname.c
===================================================================
--- uspace/lib/c/generic/inet/hostname.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,108 +1,0 @@
-/*
- * Copyright (c) 2016 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 Internet host name
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <inet/hostname.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <str.h>
-
-/** Parse host name
- *
- * Determine whether string begins with a valid host name and where that
- * host name ends.
- *
- * Currntly accepting names per 'host' definition in RFC 1738
- * (Uniform Resource Locators) - generic URI syntax not accepted.
- *
- * @param str String containing host name
- * @param rname Place to store pointer to new string containing just the
- *              host name or @c NULL if not interested
- * @param endptr Place to store pointer to next character in string or @c NULL
- *               if no extra characters in the string are allowed
- *
- * @return EOK on success. EINVAL if @a str does not start with a valid
- *         host name or if @a endptr is @c NULL and @a str contains
- *         extra characters at the end. ENOMEM if out of memory
- */
-errno_t inet_hostname_parse(const char *str, char **rname, char **endptr)
-{
-	const char *c;
-	const char *dstart = NULL;
-	char *name;
-
-	c = str;
-	while (isalnum(*c)) {
-		/* Domain label */
-		dstart = c;
-
-		do {
-			++c;
-		} while (isalnum(*c) || *c == '-');
-
-		/* Domain separator? */
-		if (*c != '.' || !isalnum(c[1]))
-			break;
-
-		++c;
-	}
-
-	/*
-	 * Last domain label must not start with a digit (to distinguish from
-	 * IPv4 address)
-	 */
-	if (dstart == NULL || !isalpha(*dstart))
-		return EINVAL;
-
-	/* If endptr is null, check that entire string matched */
-	if (endptr == NULL && *c != '\0')
-		return EINVAL;
-
-	if (endptr != NULL)
-		*endptr = (char *)c;
-
-	if (rname != NULL) {
-		name = str_ndup(str, c - str);
-		if (name == NULL)
-			return ENOMEM;
-
-		*rname = name;
-	}
-
-	return EOK;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/inet/hostport.c
===================================================================
--- uspace/lib/c/generic/inet/hostport.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,302 +1,0 @@
-/*
- * Copyright (c) 2016 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 Internet host:port specification
- *
- * As in RFC 1738 Uniform Resouce Locators (URL) and RFC 2732 Format for
- * literal IPv6 Addresses in URLs
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <inet/addr.h>
-#include <inet/dnsr.h>
-#include <inet/endpoint.h>
-#include <inet/hostname.h>
-#include <inet/hostport.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <str.h>
-
-/** Parse host:port string.
- *
- * @param str Host:port string
- * @param rhp Place to store pointer to new hostport structure
- * @param endptr Place to store pointer to next character in string or @c NULL
- *
- * @return EOK on success. EINVAL if @a str does not start with a valid
- *         host:port or if @a endptr is @c NULL and @a str contains
- *         extra characters at the end. ENOMEM if out of memory
- */
-errno_t inet_hostport_parse(const char *str, inet_hostport_t **rhp,
-    char **endptr)
-{
-	inet_hostport_t *hp;
-	inet_addr_t addr;
-	uint16_t port;
-	char *name;
-	char *aend;
-	const char *pend;
-	errno_t rc;
-
-	hp = calloc(1, sizeof(inet_hostport_t));
-	if (hp == NULL)
-		return ENOMEM;
-
-	if (str[0] == '[') {
-		/* Try [<ip-addr-literal>] */
-		rc = inet_addr_parse(str + 1, &addr, &aend);
-		if (rc == EOK) {
-			if (*aend == ']') {
-				hp->hform = inet_host_addr;
-				hp->host.addr = addr;
-				++aend;
-			}
-			goto have_host;
-		}
-	}
-
-	/* Try <ipv4-addr> */
-	rc = inet_addr_parse(str, &addr, &aend);
-	if (rc == EOK) {
-		hp->hform = inet_host_addr;
-		hp->host.addr = addr;
-		goto have_host;
-	}
-
-	/* Try <hostname> */
-	rc = inet_hostname_parse(str, &name, &aend);
-	if (rc == EOK) {
-		hp->hform = inet_host_name;
-		hp->host.name = name;
-		goto have_host;
-	}
-
-	free(hp);
-	return EINVAL;
-
-have_host:
-	if (*aend == ':') {
-		/* Port number */
-		rc = str_uint16_t(aend + 1, &pend, 10, false, &port);
-		if (rc != EOK) {
-			free(hp);
-			return EINVAL;
-		}
-	} else {
-		/* Port number omitted */
-		port = 0;
-		pend = aend;
-	}
-
-	hp->port = port;
-
-	if (*pend != '\0' && endptr == NULL) {
-		/* Extra characters */
-		free(hp);
-		return EINVAL;
-	}
-
-	*rhp = hp;
-	if (endptr != NULL)
-		*endptr = (char *)pend;
-	return EOK;
-}
-
-/** Convert host:port to string representation.
- *
- * @param hp Host:port structure
- * @param rstr Place to store pointer to new string
- * @return EOK on success, ENOMEM if out of memory
- */
-errno_t inet_hostport_format(inet_hostport_t *hp, char **rstr)
-{
-	errno_t rc;
-	int ret;
-	char *astr, *str;
-	char *hstr = NULL;
-
-	switch (hp->hform) {
-	case inet_host_addr:
-		rc = inet_addr_format(&hp->host.addr, &astr);
-		if (rc != EOK) {
-			assert(rc == ENOMEM);
-			return ENOMEM;
-		}
-
-		if (hp->host.addr.version != ip_v4) {
-			hstr = astr;
-		} else {
-			ret = asprintf(&hstr, "[%s]", astr);
-			if (ret < 0) {
-				free(astr);
-				return ENOMEM;
-			}
-
-			free(astr);
-		}
-
-		break;
-	case inet_host_name:
-		hstr = str_dup(hp->host.name);
-		if (hstr == NULL)
-			return ENOMEM;
-		break;
-	}
-
-	ret = asprintf(&str, "%s:%u", hstr, hp->port);
-	if (ret < 0)
-		return ENOMEM;
-
-	free(hstr);
-	*rstr = str;
-	return EOK;
-}
-
-/** Destroy host:port structure.
- *
- * @param hp Host:port or @c NULL
- */
-void inet_hostport_destroy(inet_hostport_t *hp)
-{
-	if (hp == NULL)
-		return;
-
-	switch (hp->hform) {
-	case inet_host_addr:
-		break;
-	case inet_host_name:
-		free(hp->host.name);
-		break;
-	}
-
-	free(hp);
-}
-
-/** Look up first endpoint corresponding to host:port.
- *
- * If host:port contains a host name, name resolution is performed.
- *
- * @param hp Host:port structure
- * @param version Desired IP version (ip_any to get any version)
- * @param ep Place to store endpoint
- *
- * @return EOK on success, ENOENT on resolution failure
- */
-errno_t inet_hostport_lookup_one(inet_hostport_t *hp, ip_ver_t version,
-    inet_ep_t *ep)
-{
-	dnsr_hostinfo_t *hinfo = NULL;
-	errno_t rc;
-
-	inet_ep_init(ep);
-
-	switch (hp->hform) {
-	case inet_host_addr:
-		ep->addr = hp->host.addr;
-		break;
-	case inet_host_name:
-		rc = dnsr_name2host(hp->host.name, &hinfo, ip_any);
-		if (rc != EOK) {
-			dnsr_hostinfo_destroy(hinfo);
-			return ENOENT;
-		}
-
-		ep->addr = hinfo->addr;
-		dnsr_hostinfo_destroy(hinfo);
-		break;
-	}
-
-	ep->port = hp->port;
-	return EOK;
-}
-
-/** Look up first endpoint corresponding to host:port string.
- *
- * If host:port contains a host name, name resolution is performed.
- *
- * @param str Host:port string
- * @param version Desired IP version (ip_any to get any version)
- * @param ep Place to store endpoint
- * @param endptr Place to store pointer to next character in string or @c NULL
- * @param errmsg Place to store pointer to error message (no need to free it)
- *               or @c NULL
- *
- * @return EOK on success. EINVAL if @a str has incorrect format or if
- *         @a endptr is @c NULL and @a str contains extra characters at
- *         the end. ENOENT if host was not found during resolution,
- *         ENOMEM if out of memory
- */
-errno_t inet_hostport_plookup_one(const char *str, ip_ver_t version, inet_ep_t *ep,
-    char **endptr, const char **errmsg)
-{
-	inet_hostport_t *hp = NULL;
-	char *eptr;
-	errno_t rc;
-
-	rc = inet_hostport_parse(str, &hp, endptr != NULL ? &eptr : NULL);
-	if (rc != EOK) {
-		switch (rc) {
-		case EINVAL:
-			if (errmsg != NULL)
-				*errmsg = "Invalid format";
-			goto error;
-		case ENOMEM:
-			if (errmsg != NULL)
-				*errmsg = "Out of memory";
-			goto error;
-		case EOK:
-			break;
-		default:
-			assert(false);
-		}
-	}
-
-	rc = inet_hostport_lookup_one(hp, version, ep);
-	if (rc != EOK) {
-		/* XXX Distinguish between 'not found' and other error */
-		rc = ENOENT;
-		if (errmsg != NULL)
-			*errmsg = "Name resolution failed";
-		goto error;
-	}
-
-	inet_hostport_destroy(hp);
-	if (endptr != NULL)
-		*endptr = eptr;
-	return EOK;
-error:
-	inet_hostport_destroy(hp);
-	return rc;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/inet/tcp.c
===================================================================
--- uspace/lib/c/generic/inet/tcp.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,875 +1,0 @@
-/*
- * Copyright (c) 2015 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 TCP API
- */
-
-#include <errno.h>
-#include <fibril.h>
-#include <inet/endpoint.h>
-#include <inet/tcp.h>
-#include <ipc/services.h>
-#include <ipc/tcp.h>
-#include <stdlib.h>
-
-static void tcp_cb_conn(ipc_call_t *, void *);
-static errno_t tcp_conn_fibril(void *);
-
-/** Incoming TCP connection info
- *
- * Used to pass information about incoming TCP connection to the connection
- * fibril
- */
-typedef struct {
-	/** Listener who received the connection */
-	tcp_listener_t *lst;
-	/** Incoming connection */
-	tcp_conn_t *conn;
-} tcp_in_conn_t;
-
-/** Create callback connection from TCP service.
- *
- * @param tcp TCP service
- * @return EOK on success or an error code
- */
-static errno_t tcp_callback_create(tcp_t *tcp)
-{
-	async_exch_t *exch = async_exchange_begin(tcp->sess);
-
-	aid_t req = async_send_0(exch, TCP_CALLBACK_CREATE, NULL);
-
-	port_id_t port;
-	errno_t rc = async_create_callback_port(exch, INTERFACE_TCP_CB, 0, 0,
-	    tcp_cb_conn, tcp, &port);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK)
-		return rc;
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-/** Create TCP client instance.
- *
- * @param  rtcp Place to store pointer to new TCP client
- * @return EOK on success, ENOMEM if out of memory, EIO if service
- *         cannot be contacted
- */
-errno_t tcp_create(tcp_t **rtcp)
-{
-	tcp_t *tcp;
-	service_id_t tcp_svcid;
-	errno_t rc;
-
-	tcp = calloc(1, sizeof(tcp_t));
-	if (tcp == NULL) {
-		rc = ENOMEM;
-		goto error;
-	}
-
-	list_initialize(&tcp->conn);
-	list_initialize(&tcp->listener);
-	fibril_mutex_initialize(&tcp->lock);
-	fibril_condvar_initialize(&tcp->cv);
-
-	rc = loc_service_get_id(SERVICE_NAME_TCP, &tcp_svcid,
-	    IPC_FLAG_BLOCKING);
-	if (rc != EOK) {
-		rc = EIO;
-		goto error;
-	}
-
-	tcp->sess = loc_service_connect(tcp_svcid, INTERFACE_TCP,
-	    IPC_FLAG_BLOCKING);
-	if (tcp->sess == NULL) {
-		rc = EIO;
-		goto error;
-	}
-
-	rc = tcp_callback_create(tcp);
-	if (rc != EOK) {
-		rc = EIO;
-		goto error;
-	}
-
-	*rtcp = tcp;
-	return EOK;
-error:
-	free(tcp);
-	return rc;
-}
-
-/** Destroy TCP client instance.
- *
- * @param tcp TCP client
- */
-void tcp_destroy(tcp_t *tcp)
-{
-	if (tcp == NULL)
-		return;
-
-	async_hangup(tcp->sess);
-
-	fibril_mutex_lock(&tcp->lock);
-	while (!tcp->cb_done)
-		fibril_condvar_wait(&tcp->cv, &tcp->lock);
-	fibril_mutex_unlock(&tcp->lock);
-
-	free(tcp);
-}
-
-/** Create new TCP connection
- *
- * @param tcp   TCP client instance
- * @param id    Connection ID
- * @param cb    Callbacks
- * @param arg   Callback argument
- * @param rconn Place to store pointer to new connection
- *
- * @return EOK on success, ENOMEM if out of memory
- */
-static errno_t tcp_conn_new(tcp_t *tcp, sysarg_t id, tcp_cb_t *cb, void *arg,
-    tcp_conn_t **rconn)
-{
-	tcp_conn_t *conn;
-
-	conn = calloc(1, sizeof(tcp_conn_t));
-	if (conn == NULL)
-		return ENOMEM;
-
-	conn->data_avail = false;
-	fibril_mutex_initialize(&conn->lock);
-	fibril_condvar_initialize(&conn->cv);
-
-	conn->tcp = tcp;
-	conn->id = id;
-	conn->cb = cb;
-	conn->cb_arg = arg;
-
-	list_append(&conn->ltcp, &tcp->conn);
-	*rconn = conn;
-
-	return EOK;
-}
-
-/** Create new TCP connection.
- *
- * Open a connection to the specified destination. This function returns
- * even before the connection is established (or not). When the connection
- * is established, @a cb->connected is called. If the connection fails,
- * @a cb->conn_failed is called. Alternatively, the caller can call
- * @c tcp_conn_wait_connected() to wait for connection to complete or fail.
- * Other callbacks are available to monitor the changes in connection state.
- *
- * @a epp must specify the remote address and port. Both local address and
- * port are optional. If local address is not specified, address selection
- * will take place. If local port number is not specified, a suitable
- * free dynamic port number will be allocated.
- *
- * @param tcp   TCP client
- * @param epp   Internet endpoint pair
- * @param cb    Callbacks
- * @param arg   Argument to callbacks
- * @param rconn Place to store pointer to new connection
- *
- * @return EOK on success or an error code.
- */
-errno_t tcp_conn_create(tcp_t *tcp, inet_ep2_t *epp, tcp_cb_t *cb, void *arg,
-    tcp_conn_t **rconn)
-{
-	async_exch_t *exch;
-	ipc_call_t answer;
-	sysarg_t conn_id;
-
-	exch = async_exchange_begin(tcp->sess);
-	aid_t req = async_send_0(exch, TCP_CONN_CREATE, &answer);
-	errno_t rc = async_data_write_start(exch, (void *)epp,
-	    sizeof(inet_ep2_t));
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		errno_t rc_orig;
-		async_wait_for(req, &rc_orig);
-		if (rc_orig != EOK)
-			rc = rc_orig;
-		goto error;
-	}
-
-	async_wait_for(req, &rc);
-	if (rc != EOK)
-		goto error;
-
-	conn_id = ipc_get_arg1(&answer);
-
-	rc = tcp_conn_new(tcp, conn_id, cb, arg, rconn);
-	if (rc != EOK)
-		return rc;
-
-	return EOK;
-error:
-	return (errno_t) rc;
-}
-
-/** Destroy TCP connection.
- *
- * Destroy TCP connection. The caller should destroy all connections
- * he created before destroying the TCP client and before terminating.
- *
- * @param conn TCP connection
- */
-void tcp_conn_destroy(tcp_conn_t *conn)
-{
-	async_exch_t *exch;
-
-	if (conn == NULL)
-		return;
-
-	list_remove(&conn->ltcp);
-
-	exch = async_exchange_begin(conn->tcp->sess);
-	errno_t rc = async_req_1_0(exch, TCP_CONN_DESTROY, conn->id);
-	async_exchange_end(exch);
-
-	free(conn);
-	(void) rc;
-}
-
-/** Get connection based on its ID.
- *
- * @param tcp   TCP client
- * @param id    Connection ID
- * @param rconn Place to store pointer to connection
- *
- * @return EOK on success, EINVAL if no connection with the given ID exists
- */
-static errno_t tcp_conn_get(tcp_t *tcp, sysarg_t id, tcp_conn_t **rconn)
-{
-	list_foreach(tcp->conn, ltcp, tcp_conn_t, conn) {
-		if (conn->id == id) {
-			*rconn = conn;
-			return EOK;
-		}
-	}
-
-	return EINVAL;
-}
-
-/** Get the user/callback argument for a connection.
- *
- * @param conn TCP connection
- * @return User argument associated with connection
- */
-void *tcp_conn_userptr(tcp_conn_t *conn)
-{
-	return conn->cb_arg;
-}
-
-/** Create a TCP connection listener.
- *
- * A listener listens for connections on the set of endpoints specified
- * by @a ep. Each time a new incoming connection is established,
- * @a lcb->new_conn is called (and passed @a larg). Also, the new connection
- * will have callbacks set to @a cb and argument to @a arg.
- *
- * @a ep must specify a valid port number. @a ep may specify an address
- * or link to listen on. If it does not, the listener will listen on
- * all links/addresses.
- *
- * @param tcp  TCP client
- * @param ep   Internet endpoint
- * @param lcb  Listener callbacks
- * @param larg Listener callback argument
- * @param cb   Connection callbacks for every new connection
- * @param arg  Connection argument for every new connection
- * @param rlst Place to store pointer to new listener
- *
- * @return EOK on success or an error code
- */
-errno_t tcp_listener_create(tcp_t *tcp, inet_ep_t *ep, tcp_listen_cb_t *lcb,
-    void *larg, tcp_cb_t *cb, void *arg, tcp_listener_t **rlst)
-{
-	async_exch_t *exch;
-	tcp_listener_t *lst;
-	ipc_call_t answer;
-
-	lst = calloc(1, sizeof(tcp_listener_t));
-	if (lst == NULL)
-		return ENOMEM;
-
-	exch = async_exchange_begin(tcp->sess);
-	aid_t req = async_send_0(exch, TCP_LISTENER_CREATE, &answer);
-	errno_t rc = async_data_write_start(exch, (void *)ep,
-	    sizeof(inet_ep_t));
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		errno_t rc_orig;
-		async_wait_for(req, &rc_orig);
-		if (rc_orig != EOK)
-			rc = rc_orig;
-		goto error;
-	}
-
-	async_wait_for(req, &rc);
-	if (rc != EOK)
-		goto error;
-
-	lst->tcp = tcp;
-	lst->id = ipc_get_arg1(&answer);
-	lst->lcb = lcb;
-	lst->lcb_arg = larg;
-	lst->cb = cb;
-	lst->cb_arg = arg;
-
-	list_append(&lst->ltcp, &tcp->listener);
-	*rlst = lst;
-
-	return EOK;
-error:
-	free(lst);
-	return (errno_t) rc;
-}
-
-/** Destroy TCP connection listener.
- *
- * @param lst Listener
- */
-void tcp_listener_destroy(tcp_listener_t *lst)
-{
-	async_exch_t *exch;
-
-	if (lst == NULL)
-		return;
-
-	list_remove(&lst->ltcp);
-
-	exch = async_exchange_begin(lst->tcp->sess);
-	errno_t rc = async_req_1_0(exch, TCP_LISTENER_DESTROY, lst->id);
-	async_exchange_end(exch);
-
-	free(lst);
-	(void) rc;
-}
-
-/** Get TCP connection listener based on its ID.
- *
- * @param tcp TCP client
- * @param id  Listener ID
- * @param rlst Place to store pointer to listener
- *
- * @return EOK on success, EINVAL if no listener with the given ID is found
- */
-static errno_t tcp_listener_get(tcp_t *tcp, sysarg_t id, tcp_listener_t **rlst)
-{
-	list_foreach(tcp->listener, ltcp, tcp_listener_t, lst) {
-		if (lst->id == id) {
-			*rlst = lst;
-			return EOK;
-		}
-	}
-
-	return EINVAL;
-}
-
-/** Get callback/user argument associated with listener.
- *
- * @param lst Listener
- * @return Callback/user argument
- */
-void *tcp_listener_userptr(tcp_listener_t *lst)
-{
-	return lst->lcb_arg;
-}
-
-/** Wait until connection is either established or connection fails.
- *
- * Can be called after calling tcp_conn_create() to block until connection
- * either completes or fails. If the connection fails, EIO is returned.
- * In this case the connection still exists, but is in a failed
- * state.
- *
- * @param conn Connection
- * @return EOK if connection is established, EIO otherwise
- */
-errno_t tcp_conn_wait_connected(tcp_conn_t *conn)
-{
-	fibril_mutex_lock(&conn->lock);
-	while (!conn->connected && !conn->conn_failed && !conn->conn_reset)
-		fibril_condvar_wait(&conn->cv, &conn->lock);
-
-	if (conn->connected) {
-		fibril_mutex_unlock(&conn->lock);
-		return EOK;
-	} else {
-		assert(conn->conn_failed || conn->conn_reset);
-		fibril_mutex_unlock(&conn->lock);
-		return EIO;
-	}
-}
-
-/** Send data over TCP connection.
- *
- * @param conn  Connection
- * @param data  Data
- * @param bytes Data size in bytes
- *
- * @return EOK on success or an error code
- */
-errno_t tcp_conn_send(tcp_conn_t *conn, const void *data, size_t bytes)
-{
-	async_exch_t *exch;
-	errno_t rc;
-
-	exch = async_exchange_begin(conn->tcp->sess);
-	aid_t req = async_send_1(exch, TCP_CONN_SEND, conn->id, NULL);
-	rc = async_data_write_start(exch, data, bytes);
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	async_wait_for(req, &rc);
-	return rc;
-}
-
-/** Send FIN.
- *
- * Send FIN, indicating no more data will be send over the connection.
- *
- * @param conn Connection
- * @return EOK on success or an error code
- */
-errno_t tcp_conn_send_fin(tcp_conn_t *conn)
-{
-	async_exch_t *exch;
-
-	exch = async_exchange_begin(conn->tcp->sess);
-	errno_t rc = async_req_1_0(exch, TCP_CONN_SEND_FIN, conn->id);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-/** Push connection.
- *
- * @param conn Connection
- * @return EOK on success or an error code
- */
-errno_t tcp_conn_push(tcp_conn_t *conn)
-{
-	async_exch_t *exch;
-
-	exch = async_exchange_begin(conn->tcp->sess);
-	errno_t rc = async_req_1_0(exch, TCP_CONN_PUSH, conn->id);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-/** Reset connection.
- *
- * @param conn Connection
- * @return EOK on success or an error code
- */
-errno_t tcp_conn_reset(tcp_conn_t *conn)
-{
-	async_exch_t *exch;
-
-	exch = async_exchange_begin(conn->tcp->sess);
-	errno_t rc = async_req_1_0(exch, TCP_CONN_RESET, conn->id);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-/** Read received data from connection without blocking.
- *
- * If any received data is pending on the connection, up to @a bsize bytes
- * are copied to @a buf and the acutal number is stored in @a *nrecv.
- * The entire buffer of @a bsize bytes is filled except when less data
- * is currently available or FIN is received. EOK is returned.
- *
- * If no received data is pending, returns EAGAIN.
- *
- * @param conn Connection
- * @param buf  Buffer
- * @param bsize Buffer size
- * @param nrecv Place to store actual number of received bytes
- *
- * @return EOK on success, EAGAIN if no received data is pending, or other
- *         error code in case of other error
- */
-errno_t tcp_conn_recv(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
-{
-	async_exch_t *exch;
-	ipc_call_t answer;
-
-	fibril_mutex_lock(&conn->lock);
-	if (!conn->data_avail) {
-		fibril_mutex_unlock(&conn->lock);
-		return EAGAIN;
-	}
-
-	exch = async_exchange_begin(conn->tcp->sess);
-	aid_t req = async_send_1(exch, TCP_CONN_RECV, conn->id, &answer);
-	errno_t rc = async_data_read_start(exch, buf, bsize);
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		fibril_mutex_unlock(&conn->lock);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-	if (retval != EOK) {
-		fibril_mutex_unlock(&conn->lock);
-		return retval;
-	}
-
-	*nrecv = ipc_get_arg1(&answer);
-	fibril_mutex_unlock(&conn->lock);
-	return EOK;
-}
-
-/** Read received data from connection with blocking.
- *
- * Wait for @a bsize bytes of data to be received and copy them to
- * @a buf. Less data may be returned if FIN is received on the connection.
- * The actual If any received data is written to @a *nrecv and EOK
- * is returned on success.
- *
- * @param conn Connection
- * @param buf  Buffer
- * @param bsize Buffer size
- * @param nrecv Place to store actual number of received bytes
- *
- * @return EOK on success or an error code
- */
-errno_t tcp_conn_recv_wait(tcp_conn_t *conn, void *buf, size_t bsize,
-    size_t *nrecv)
-{
-	async_exch_t *exch;
-	ipc_call_t answer;
-
-again:
-	fibril_mutex_lock(&conn->lock);
-	while (!conn->data_avail) {
-		fibril_condvar_wait(&conn->cv, &conn->lock);
-	}
-
-	exch = async_exchange_begin(conn->tcp->sess);
-	aid_t req = async_send_1(exch, TCP_CONN_RECV_WAIT, conn->id, &answer);
-	errno_t rc = async_data_read_start(exch, buf, bsize);
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		if (rc == EAGAIN) {
-			conn->data_avail = false;
-			fibril_mutex_unlock(&conn->lock);
-			goto again;
-		}
-		fibril_mutex_unlock(&conn->lock);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-	if (retval != EOK) {
-		if (rc == EAGAIN) {
-			conn->data_avail = false;
-		}
-		fibril_mutex_unlock(&conn->lock);
-		return retval;
-	}
-
-	*nrecv = ipc_get_arg1(&answer);
-	fibril_mutex_unlock(&conn->lock);
-	return EOK;
-}
-
-/** Connection established event.
- *
- * @param tcp   TCP client
- * @param icall Call data
- *
- */
-static void tcp_ev_connected(tcp_t *tcp, ipc_call_t *icall)
-{
-	tcp_conn_t *conn;
-	sysarg_t conn_id;
-	errno_t rc;
-
-	conn_id = ipc_get_arg1(icall);
-
-	rc = tcp_conn_get(tcp, conn_id, &conn);
-	if (rc != EOK) {
-		async_answer_0(icall, ENOENT);
-		return;
-	}
-
-	fibril_mutex_lock(&conn->lock);
-	conn->connected = true;
-	fibril_condvar_broadcast(&conn->cv);
-	fibril_mutex_unlock(&conn->lock);
-
-	async_answer_0(icall, EOK);
-}
-
-/** Connection failed event.
- *
- * @param tcp   TCP client
- * @param icall Call data
- *
- */
-static void tcp_ev_conn_failed(tcp_t *tcp, ipc_call_t *icall)
-{
-	tcp_conn_t *conn;
-	sysarg_t conn_id;
-	errno_t rc;
-
-	conn_id = ipc_get_arg1(icall);
-
-	rc = tcp_conn_get(tcp, conn_id, &conn);
-	if (rc != EOK) {
-		async_answer_0(icall, ENOENT);
-		return;
-	}
-
-	fibril_mutex_lock(&conn->lock);
-	conn->conn_failed = true;
-	fibril_condvar_broadcast(&conn->cv);
-	fibril_mutex_unlock(&conn->lock);
-
-	async_answer_0(icall, EOK);
-}
-
-/** Connection reset event.
- *
- * @param tcp   TCP client
- * @param icall Call data
- *
- */
-static void tcp_ev_conn_reset(tcp_t *tcp, ipc_call_t *icall)
-{
-	tcp_conn_t *conn;
-	sysarg_t conn_id;
-	errno_t rc;
-
-	conn_id = ipc_get_arg1(icall);
-
-	rc = tcp_conn_get(tcp, conn_id, &conn);
-	if (rc != EOK) {
-		async_answer_0(icall, ENOENT);
-		return;
-	}
-
-	fibril_mutex_lock(&conn->lock);
-	conn->conn_reset = true;
-	fibril_condvar_broadcast(&conn->cv);
-	fibril_mutex_unlock(&conn->lock);
-
-	async_answer_0(icall, EOK);
-}
-
-/** Data available event.
- *
- * @param tcp   TCP client
- * @param icall Call data
- *
- */
-static void tcp_ev_data(tcp_t *tcp, ipc_call_t *icall)
-{
-	tcp_conn_t *conn;
-	sysarg_t conn_id;
-	errno_t rc;
-
-	conn_id = ipc_get_arg1(icall);
-
-	rc = tcp_conn_get(tcp, conn_id, &conn);
-	if (rc != EOK) {
-		async_answer_0(icall, ENOENT);
-		return;
-	}
-
-	conn->data_avail = true;
-	fibril_condvar_broadcast(&conn->cv);
-
-	if (conn->cb != NULL && conn->cb->data_avail != NULL)
-		conn->cb->data_avail(conn);
-
-	async_answer_0(icall, EOK);
-}
-
-/** Urgent data event.
- *
- * @param tcp   TCP client
- * @param icall Call data
- *
- */
-static void tcp_ev_urg_data(tcp_t *tcp, ipc_call_t *icall)
-{
-	async_answer_0(icall, ENOTSUP);
-}
-
-/** New connection event.
- *
- * @param tcp   TCP client
- * @param icall Call data
- *
- */
-static void tcp_ev_new_conn(tcp_t *tcp, ipc_call_t *icall)
-{
-	tcp_listener_t *lst;
-	tcp_conn_t *conn;
-	sysarg_t lst_id;
-	sysarg_t conn_id;
-	fid_t fid;
-	tcp_in_conn_t *cinfo;
-	errno_t rc;
-
-	lst_id = ipc_get_arg1(icall);
-	conn_id = ipc_get_arg2(icall);
-
-	rc = tcp_listener_get(tcp, lst_id, &lst);
-	if (rc != EOK) {
-		async_answer_0(icall, ENOENT);
-		return;
-	}
-
-	rc = tcp_conn_new(tcp, conn_id, lst->cb, lst->cb_arg, &conn);
-	if (rc != EOK) {
-		async_answer_0(icall, ENOMEM);
-		return;
-	}
-
-	if (lst->lcb != NULL && lst->lcb->new_conn != NULL) {
-		cinfo = calloc(1, sizeof(tcp_in_conn_t));
-		if (cinfo == NULL) {
-			async_answer_0(icall, ENOMEM);
-			return;
-		}
-
-		cinfo->lst = lst;
-		cinfo->conn = conn;
-
-		fid = fibril_create(tcp_conn_fibril, cinfo);
-		if (fid == 0) {
-			async_answer_0(icall, ENOMEM);
-		}
-
-		fibril_add_ready(fid);
-	}
-
-	async_answer_0(icall, EOK);
-}
-
-/** Callback connection handler.
- *
- * @param icall Connect call data
- * @param arg   Argument, TCP client
- *
- */
-static void tcp_cb_conn(ipc_call_t *icall, void *arg)
-{
-	tcp_t *tcp = (tcp_t *)arg;
-
-	while (true) {
-		ipc_call_t call;
-		async_get_call(&call);
-
-		if (!ipc_get_imethod(&call)) {
-			/* Hangup*/
-			async_answer_0(&call, EOK);
-			goto out;
-		}
-
-		switch (ipc_get_imethod(&call)) {
-		case TCP_EV_CONNECTED:
-			tcp_ev_connected(tcp, &call);
-			break;
-		case TCP_EV_CONN_FAILED:
-			tcp_ev_conn_failed(tcp, &call);
-			break;
-		case TCP_EV_CONN_RESET:
-			tcp_ev_conn_reset(tcp, &call);
-			break;
-		case TCP_EV_DATA:
-			tcp_ev_data(tcp, &call);
-			break;
-		case TCP_EV_URG_DATA:
-			tcp_ev_urg_data(tcp, &call);
-			break;
-		case TCP_EV_NEW_CONN:
-			tcp_ev_new_conn(tcp, &call);
-			break;
-		default:
-			async_answer_0(&call, ENOTSUP);
-			break;
-		}
-	}
-
-out:
-	fibril_mutex_lock(&tcp->lock);
-	tcp->cb_done = true;
-	fibril_mutex_unlock(&tcp->lock);
-	fibril_condvar_broadcast(&tcp->cv);
-}
-
-/** Fibril for handling incoming TCP connection in background.
- *
- * @param arg Argument, incoming connection information (@c tcp_in_conn_t)
- */
-static errno_t tcp_conn_fibril(void *arg)
-{
-	tcp_in_conn_t *cinfo = (tcp_in_conn_t *)arg;
-
-	cinfo->lst->lcb->new_conn(cinfo->lst, cinfo->conn);
-	tcp_conn_destroy(cinfo->conn);
-
-	return EOK;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/inet/udp.c
===================================================================
--- uspace/lib/c/generic/inet/udp.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,524 +1,0 @@
-/*
- * Copyright (c) 2015 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 UDP API
- */
-
-#include <errno.h>
-#include <inet/endpoint.h>
-#include <inet/udp.h>
-#include <ipc/services.h>
-#include <ipc/udp.h>
-#include <loc.h>
-#include <stdlib.h>
-
-static void udp_cb_conn(ipc_call_t *, void *);
-
-/** Create callback connection from UDP service.
- *
- * @param udp UDP service
- * @return EOK on success or an error code
- */
-static errno_t udp_callback_create(udp_t *udp)
-{
-	async_exch_t *exch = async_exchange_begin(udp->sess);
-
-	aid_t req = async_send_0(exch, UDP_CALLBACK_CREATE, NULL);
-
-	port_id_t port;
-	errno_t rc = async_create_callback_port(exch, INTERFACE_UDP_CB, 0, 0,
-	    udp_cb_conn, udp, &port);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK)
-		return rc;
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-/** Create UDP client instance.
- *
- * @param  rudp Place to store pointer to new UDP client
- * @return EOK on success, ENOMEM if out of memory, EIO if service
- *         cannot be contacted
- */
-errno_t udp_create(udp_t **rudp)
-{
-	udp_t *udp;
-	service_id_t udp_svcid;
-	errno_t rc;
-
-	udp = calloc(1, sizeof(udp_t));
-	if (udp == NULL) {
-		rc = ENOMEM;
-		goto error;
-	}
-
-	list_initialize(&udp->assoc);
-	fibril_mutex_initialize(&udp->lock);
-	fibril_condvar_initialize(&udp->cv);
-
-	rc = loc_service_get_id(SERVICE_NAME_UDP, &udp_svcid,
-	    IPC_FLAG_BLOCKING);
-	if (rc != EOK) {
-		rc = EIO;
-		goto error;
-	}
-
-	udp->sess = loc_service_connect(udp_svcid, INTERFACE_UDP,
-	    IPC_FLAG_BLOCKING);
-	if (udp->sess == NULL) {
-		rc = EIO;
-		goto error;
-	}
-
-	rc = udp_callback_create(udp);
-	if (rc != EOK) {
-		rc = EIO;
-		goto error;
-	}
-
-	*rudp = udp;
-	return EOK;
-error:
-	free(udp);
-	return rc;
-}
-
-/** Destroy UDP client instance.
- *
- * @param udp UDP client
- */
-void udp_destroy(udp_t *udp)
-{
-	if (udp == NULL)
-		return;
-
-	async_hangup(udp->sess);
-
-	fibril_mutex_lock(&udp->lock);
-	while (!udp->cb_done)
-		fibril_condvar_wait(&udp->cv, &udp->lock);
-	fibril_mutex_unlock(&udp->lock);
-
-	free(udp);
-}
-
-/** Create new UDP association.
- *
- * Create a UDP association that allows sending and receiving messages.
- *
- * @a epp may specify remote address and port, in which case only messages
- * from that remote endpoint will be received. Also, that remote endpoint
- * is used as default when @c NULL is passed as destination to
- * udp_assoc_send_msg.
- *
- * @a epp may specify a local link or address. If it does not, the association
- * will listen on all local links/addresses. If @a epp does not specify
- * a local port number, a free dynamic port number will be allocated.
- *
- * The caller is informed about incoming data by invoking @a cb->recv_msg
- *
- * @param udp    UDP client
- * @param epp    Internet endpoint pair
- * @param cb     Callbacks
- * @param arg    Argument to callbacks
- * @param rassoc Place to store pointer to new association
- *
- * @return EOK on success or an error code.
- */
-errno_t udp_assoc_create(udp_t *udp, inet_ep2_t *epp, udp_cb_t *cb, void *arg,
-    udp_assoc_t **rassoc)
-{
-	async_exch_t *exch;
-	udp_assoc_t *assoc;
-	ipc_call_t answer;
-
-	assoc = calloc(1, sizeof(udp_assoc_t));
-	if (assoc == NULL)
-		return ENOMEM;
-
-	exch = async_exchange_begin(udp->sess);
-	aid_t req = async_send_0(exch, UDP_ASSOC_CREATE, &answer);
-	errno_t rc = async_data_write_start(exch, (void *)epp,
-	    sizeof(inet_ep2_t));
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		errno_t rc_orig;
-		async_wait_for(req, &rc_orig);
-		if (rc_orig != EOK)
-			rc = rc_orig;
-		goto error;
-	}
-
-	async_wait_for(req, &rc);
-	if (rc != EOK)
-		goto error;
-
-	assoc->udp = udp;
-	assoc->id = ipc_get_arg1(&answer);
-	assoc->cb = cb;
-	assoc->cb_arg = arg;
-
-	list_append(&assoc->ludp, &udp->assoc);
-	*rassoc = assoc;
-
-	return EOK;
-error:
-	free(assoc);
-	return (errno_t) rc;
-}
-
-/** Destroy UDP association.
- *
- * Destroy UDP association. The caller should destroy all associations
- * he created before destroying the UDP client and before terminating.
- *
- * @param assoc UDP association
- */
-void udp_assoc_destroy(udp_assoc_t *assoc)
-{
-	async_exch_t *exch;
-
-	if (assoc == NULL)
-		return;
-
-	list_remove(&assoc->ludp);
-
-	exch = async_exchange_begin(assoc->udp->sess);
-	errno_t rc = async_req_1_0(exch, UDP_ASSOC_DESTROY, assoc->id);
-	async_exchange_end(exch);
-
-	free(assoc);
-	(void) rc;
-}
-
-/** Set UDP association sending messages with no local address
- *
- * @param assoc Association
- * @param flags Flags
- */
-errno_t udp_assoc_set_nolocal(udp_assoc_t *assoc)
-{
-	async_exch_t *exch;
-
-	exch = async_exchange_begin(assoc->udp->sess);
-	errno_t rc = async_req_1_0(exch, UDP_ASSOC_SET_NOLOCAL, assoc->id);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-/** Send message via UDP association.
- *
- * @param assoc Association
- * @param dest	Destination endpoint or @c NULL to use association's remote ep.
- * @param data	Message data
- * @param bytes Message size in bytes
- *
- * @return EOK on success or an error code
- */
-errno_t udp_assoc_send_msg(udp_assoc_t *assoc, inet_ep_t *dest, void *data,
-    size_t bytes)
-{
-	async_exch_t *exch;
-
-	exch = async_exchange_begin(assoc->udp->sess);
-	aid_t req = async_send_1(exch, UDP_ASSOC_SEND_MSG, assoc->id, NULL);
-
-	errno_t rc = async_data_write_start(exch, (void *)dest,
-	    sizeof(inet_ep_t));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-
-	rc = async_data_write_start(exch, data, bytes);
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	async_wait_for(req, &rc);
-	return rc;
-}
-
-/** Get the user/callback argument for an association.
- *
- * @param assoc UDP association
- * @return User argument associated with association
- */
-void *udp_assoc_userptr(udp_assoc_t *assoc)
-{
-	return assoc->cb_arg;
-}
-
-/** Get size of received message in bytes.
- *
- * Assuming jumbo messages can be received, the caller first needs to determine
- * the size of the received message by calling this function, then they can
- * read the message piece-wise using udp_rmsg_read().
- *
- * @param rmsg Received message
- * @return Size of received message in bytes
- */
-size_t udp_rmsg_size(udp_rmsg_t *rmsg)
-{
-	return rmsg->size;
-}
-
-/** Read part of received message.
- *
- * @param rmsg  Received message
- * @param off   Start offset
- * @param buf   Buffer for storing data
- * @param bsize Buffer size
- *
- * @return EOK on success or an error code.
- */
-errno_t udp_rmsg_read(udp_rmsg_t *rmsg, size_t off, void *buf, size_t bsize)
-{
-	async_exch_t *exch;
-	ipc_call_t answer;
-
-	exch = async_exchange_begin(rmsg->udp->sess);
-	aid_t req = async_send_1(exch, UDP_RMSG_READ, off, &answer);
-	errno_t rc = async_data_read_start(exch, buf, bsize);
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-	if (retval != EOK) {
-		return retval;
-	}
-
-	return EOK;
-}
-
-/** Get remote endpoint of received message.
- *
- * Place the remote endpoint (the one from which the message was supposedly
- * sent) to @a ep.
- *
- * @param rmsg Received message
- * @param ep   Place to store remote endpoint
- */
-void udp_rmsg_remote_ep(udp_rmsg_t *rmsg, inet_ep_t *ep)
-{
-	*ep = rmsg->remote_ep;
-}
-
-/** Get type of received ICMP error message.
- *
- * @param rerr Received error message
- * @return Error message type
- */
-uint8_t udp_rerr_type(udp_rerr_t *rerr)
-{
-	return 0;
-}
-
-/** Get code of received ICMP error message.
- *
- * @param rerr Received error message
- * @return Error message code
- */
-uint8_t udp_rerr_code(udp_rerr_t *rerr)
-{
-	return 0;
-}
-
-/** Get information about the next received message from UDP service.
- *
- * @param udp  UDP client
- * @param rmsg Place to store message information
- *
- * @return EOK on success or an error code
- */
-static errno_t udp_rmsg_info(udp_t *udp, udp_rmsg_t *rmsg)
-{
-	async_exch_t *exch;
-	inet_ep_t ep;
-	ipc_call_t answer;
-
-	exch = async_exchange_begin(udp->sess);
-	aid_t req = async_send_0(exch, UDP_RMSG_INFO, &answer);
-	errno_t rc = async_data_read_start(exch, &ep, sizeof(inet_ep_t));
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-	if (retval != EOK)
-		return retval;
-
-	rmsg->udp = udp;
-	rmsg->assoc_id = ipc_get_arg1(&answer);
-	rmsg->size = ipc_get_arg2(&answer);
-	rmsg->remote_ep = ep;
-	return EOK;
-}
-
-/** Discard next received message in UDP service.
- *
- * @param udp UDP client
- * @return EOK on success or an error code
- */
-static errno_t udp_rmsg_discard(udp_t *udp)
-{
-	async_exch_t *exch;
-
-	exch = async_exchange_begin(udp->sess);
-	errno_t rc = async_req_0_0(exch, UDP_RMSG_DISCARD);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-/** Get association based on its ID.
- *
- * @param udp    UDP client
- * @param id     Association ID
- * @param rassoc Place to store pointer to association
- *
- * @return EOK on success, EINVAL if no association with the given ID exists
- */
-static errno_t udp_assoc_get(udp_t *udp, sysarg_t id, udp_assoc_t **rassoc)
-{
-	list_foreach(udp->assoc, ludp, udp_assoc_t, assoc) {
-		if (assoc->id == id) {
-			*rassoc = assoc;
-			return EOK;
-		}
-	}
-
-	return EINVAL;
-}
-
-/** Handle 'data' event, i.e. some message(s) arrived.
- *
- * For each received message, get information about it, call @c recv_msg
- * callback and discard it.
- *
- * @param udp   UDP client
- * @param icall IPC message
- *
- */
-static void udp_ev_data(udp_t *udp, ipc_call_t *icall)
-{
-	udp_rmsg_t rmsg;
-	udp_assoc_t *assoc;
-	errno_t rc;
-
-	while (true) {
-		rc = udp_rmsg_info(udp, &rmsg);
-		if (rc != EOK) {
-			break;
-		}
-
-		rc = udp_assoc_get(udp, rmsg.assoc_id, &assoc);
-		if (rc != EOK) {
-			continue;
-		}
-
-		if (assoc->cb != NULL && assoc->cb->recv_msg != NULL)
-			assoc->cb->recv_msg(assoc, &rmsg);
-
-		rc = udp_rmsg_discard(udp);
-		if (rc != EOK) {
-			break;
-		}
-	}
-
-	async_answer_0(icall, EOK);
-}
-
-/** UDP service callback connection.
- *
- * @param icall Connect message
- * @param arg   Argument, UDP client
- *
- */
-static void udp_cb_conn(ipc_call_t *icall, void *arg)
-{
-	udp_t *udp = (udp_t *)arg;
-
-	while (true) {
-		ipc_call_t call;
-		async_get_call(&call);
-
-		if (!ipc_get_imethod(&call)) {
-			/* Hangup */
-			async_answer_0(&call, EOK);
-			goto out;
-		}
-
-		switch (ipc_get_imethod(&call)) {
-		case UDP_EV_DATA:
-			udp_ev_data(udp, &call);
-			break;
-		default:
-			async_answer_0(&call, ENOTSUP);
-			break;
-		}
-	}
-
-out:
-	fibril_mutex_lock(&udp->lock);
-	udp->cb_done = true;
-	fibril_mutex_unlock(&udp->lock);
-	fibril_condvar_broadcast(&udp->cv);
-}
-
-/** @}
- */
Index: pace/lib/c/generic/inetcfg.c
===================================================================
--- uspace/lib/c/generic/inetcfg.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,465 +1,0 @@
-/*
- * 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.
- */
-
-#include <async.h>
-#include <assert.h>
-#include <errno.h>
-#include <inet/inetcfg.h>
-#include <ipc/inet.h>
-#include <ipc/services.h>
-#include <loc.h>
-#include <stdlib.h>
-#include <str.h>
-
-static async_sess_t *inetcfg_sess = NULL;
-
-static errno_t inetcfg_get_ids_once(sysarg_t method, sysarg_t arg1,
-    sysarg_t *id_buf, size_t buf_size, size_t *act_size)
-{
-	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, method, arg1, &answer);
-	errno_t rc = async_data_read_start(exch, id_buf, buf_size);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK) {
-		return retval;
-	}
-
-	*act_size = ipc_get_arg1(&answer);
-	return EOK;
-}
-
-/** Get list of IDs.
- *
- * Returns an allocated array of service IDs.
- *
- * @param method	IPC method
- * @param arg1		IPC argument 1
- * @param data		Place to store pointer to array of IDs
- * @param count		Place to store number of IDs
- * @return 		EOK on success or an error code
- */
-static errno_t inetcfg_get_ids_internal(sysarg_t method, sysarg_t arg1,
-    sysarg_t **data, size_t *count)
-{
-	*data = NULL;
-	*count = 0;
-
-	size_t act_size = 0;
-	errno_t rc = inetcfg_get_ids_once(method, arg1, NULL, 0,
-	    &act_size);
-	if (rc != EOK)
-		return rc;
-
-	size_t alloc_size = act_size;
-	service_id_t *ids = malloc(alloc_size);
-	if (ids == NULL)
-		return ENOMEM;
-
-	while (true) {
-		rc = inetcfg_get_ids_once(method, arg1, ids, alloc_size,
-		    &act_size);
-		if (rc != EOK)
-			return rc;
-
-		if (act_size <= alloc_size)
-			break;
-
-		alloc_size = act_size;
-		service_id_t *tmp = realloc(ids, alloc_size);
-		if (tmp == NULL) {
-			free(ids);
-			return ENOMEM;
-		}
-		ids = tmp;
-	}
-
-	*count = act_size / sizeof(sysarg_t);
-	*data = ids;
-	return EOK;
-}
-
-errno_t inetcfg_init(void)
-{
-	service_id_t inet_svc;
-	errno_t rc;
-
-	assert(inetcfg_sess == NULL);
-
-	rc = loc_service_get_id(SERVICE_NAME_INET, &inet_svc,
-	    IPC_FLAG_BLOCKING);
-	if (rc != EOK)
-		return ENOENT;
-
-	inetcfg_sess = loc_service_connect(inet_svc, INTERFACE_INETCFG,
-	    IPC_FLAG_BLOCKING);
-	if (inetcfg_sess == NULL)
-		return ENOENT;
-
-	return EOK;
-}
-
-errno_t inetcfg_addr_create_static(const char *name, inet_naddr_t *naddr,
-    sysarg_t link_id, sysarg_t *addr_id)
-{
-	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, INETCFG_ADDR_CREATE_STATIC, link_id,
-	    &answer);
-
-	errno_t rc = async_data_write_start(exch, naddr, sizeof(inet_naddr_t));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-
-	rc = async_data_write_start(exch, name, str_size(name));
-
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	*addr_id = ipc_get_arg1(&answer);
-
-	return retval;
-}
-
-errno_t inetcfg_addr_delete(sysarg_t addr_id)
-{
-	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
-
-	errno_t rc = async_req_1_0(exch, INETCFG_ADDR_DELETE, addr_id);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-errno_t inetcfg_addr_get(sysarg_t addr_id, inet_addr_info_t *ainfo)
-{
-	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, INETCFG_ADDR_GET, addr_id, &answer);
-
-	ipc_call_t answer_naddr;
-	aid_t req_naddr = async_data_read(exch, &ainfo->naddr,
-	    sizeof(inet_naddr_t), &answer_naddr);
-
-	errno_t retval_naddr;
-	async_wait_for(req_naddr, &retval_naddr);
-
-	if (retval_naddr != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return retval_naddr;
-	}
-
-	ipc_call_t answer_name;
-	char name_buf[LOC_NAME_MAXLEN + 1];
-	aid_t req_name = async_data_read(exch, name_buf, LOC_NAME_MAXLEN,
-	    &answer_name);
-
-	async_exchange_end(exch);
-
-	errno_t retval_name;
-	async_wait_for(req_name, &retval_name);
-
-	if (retval_name != EOK) {
-		async_forget(req);
-		return retval_name;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK)
-		return retval;
-
-	size_t act_size = ipc_get_arg2(&answer_name);
-	assert(act_size <= LOC_NAME_MAXLEN);
-
-	name_buf[act_size] = '\0';
-
-	ainfo->ilink = ipc_get_arg1(&answer);
-	ainfo->name = str_dup(name_buf);
-
-	return EOK;
-}
-
-errno_t inetcfg_addr_get_id(const char *name, sysarg_t link_id, sysarg_t *addr_id)
-{
-	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, INETCFG_ADDR_GET_ID, link_id, &answer);
-	errno_t retval = async_data_write_start(exch, name, str_size(name));
-
-	async_exchange_end(exch);
-
-	if (retval != EOK) {
-		async_forget(req);
-		return retval;
-	}
-
-	async_wait_for(req, &retval);
-	*addr_id = ipc_get_arg1(&answer);
-
-	return retval;
-}
-
-errno_t inetcfg_get_addr_list(sysarg_t **addrs, size_t *count)
-{
-	return inetcfg_get_ids_internal(INETCFG_GET_ADDR_LIST,
-	    0, addrs, count);
-}
-
-errno_t inetcfg_get_link_list(sysarg_t **links, size_t *count)
-{
-	return inetcfg_get_ids_internal(INETCFG_GET_LINK_LIST,
-	    0, links, count);
-}
-
-errno_t inetcfg_get_sroute_list(sysarg_t **sroutes, size_t *count)
-{
-	return inetcfg_get_ids_internal(INETCFG_GET_SROUTE_LIST,
-	    0, sroutes, count);
-}
-
-errno_t inetcfg_link_add(sysarg_t link_id)
-{
-	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
-
-	errno_t rc = async_req_1_0(exch, INETCFG_LINK_ADD, link_id);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-errno_t inetcfg_link_get(sysarg_t link_id, inet_link_info_t *linfo)
-{
-	ipc_call_t dreply;
-	errno_t dretval;
-	size_t act_size;
-	char name_buf[LOC_NAME_MAXLEN + 1];
-
-	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
-
-	ipc_call_t answer;
-	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);
-	errno_t rc = async_data_read_start(exch, &linfo->mac_addr, sizeof(addr48_t));
-	async_wait_for(dreq, &dretval);
-
-	async_exchange_end(exch);
-
-	if (dretval != EOK || rc != EOK) {
-		async_forget(req);
-		return dretval;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK)
-		return retval;
-
-	act_size = ipc_get_arg2(&dreply);
-	assert(act_size <= LOC_NAME_MAXLEN);
-	name_buf[act_size] = '\0';
-
-	linfo->name = str_dup(name_buf);
-	linfo->def_mtu = ipc_get_arg1(&answer);
-
-	return EOK;
-}
-
-errno_t inetcfg_link_remove(sysarg_t link_id)
-{
-	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
-
-	errno_t rc = async_req_1_0(exch, INETCFG_LINK_REMOVE, link_id);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-errno_t inetcfg_sroute_create(const char *name, inet_naddr_t *dest,
-    inet_addr_t *router, sysarg_t *sroute_id)
-{
-	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, INETCFG_SROUTE_CREATE, &answer);
-
-	errno_t rc = async_data_write_start(exch, dest, sizeof(inet_naddr_t));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-
-	rc = async_data_write_start(exch, router, sizeof(inet_addr_t));
-	if (rc != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return rc;
-	}
-
-	rc = async_data_write_start(exch, name, str_size(name));
-
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	*sroute_id = ipc_get_arg1(&answer);
-
-	return retval;
-}
-
-errno_t inetcfg_sroute_delete(sysarg_t sroute_id)
-{
-	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
-
-	errno_t rc = async_req_1_0(exch, INETCFG_SROUTE_DELETE, sroute_id);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-errno_t inetcfg_sroute_get(sysarg_t sroute_id, inet_sroute_info_t *srinfo)
-{
-	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, INETCFG_SROUTE_GET, sroute_id, &answer);
-
-	ipc_call_t answer_dest;
-	aid_t req_dest = async_data_read(exch, &srinfo->dest,
-	    sizeof(inet_naddr_t), &answer_dest);
-
-	errno_t retval_dest;
-	async_wait_for(req_dest, &retval_dest);
-
-	if (retval_dest != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return retval_dest;
-	}
-
-	ipc_call_t answer_router;
-	aid_t req_router = async_data_read(exch, &srinfo->router,
-	    sizeof(inet_addr_t), &answer_router);
-
-	errno_t retval_router;
-	async_wait_for(req_router, &retval_router);
-
-	if (retval_router != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return retval_router;
-	}
-
-	ipc_call_t answer_name;
-	char name_buf[LOC_NAME_MAXLEN + 1];
-	aid_t req_name = async_data_read(exch, name_buf, LOC_NAME_MAXLEN,
-	    &answer_name);
-
-	async_exchange_end(exch);
-
-	errno_t retval_name;
-	async_wait_for(req_name, &retval_name);
-
-	if (retval_name != EOK) {
-		async_forget(req);
-		return retval_name;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK)
-		return retval;
-
-	size_t act_size = ipc_get_arg2(&answer_name);
-	assert(act_size <= LOC_NAME_MAXLEN);
-
-	name_buf[act_size] = '\0';
-
-	srinfo->name = str_dup(name_buf);
-
-	return EOK;
-}
-
-errno_t inetcfg_sroute_get_id(const char *name, sysarg_t *sroute_id)
-{
-	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, INETCFG_SROUTE_GET_ID, &answer);
-	errno_t retval = async_data_write_start(exch, name, str_size(name));
-
-	async_exchange_end(exch);
-
-	if (retval != EOK) {
-		async_forget(req);
-		return retval;
-	}
-
-	async_wait_for(req, &retval);
-	*sroute_id = ipc_get_arg1(&answer);
-
-	return retval;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/inetping.c
===================================================================
--- uspace/lib/c/generic/inetping.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,231 +1,0 @@
-/*
- * Copyright (c) 2013 Jiri Svoboda
- * 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/inetping.h>
-#include <ipc/inet.h>
-#include <ipc/services.h>
-#include <loc.h>
-#include <stdlib.h>
-#include <str.h>
-
-static void inetping_cb_conn(ipc_call_t *, void *);
-static void inetping_ev_recv(ipc_call_t *);
-
-static async_sess_t *inetping_sess = NULL;
-static inetping_ev_ops_t *inetping_ev_ops;
-
-errno_t inetping_init(inetping_ev_ops_t *ev_ops)
-{
-	service_id_t inetping_svc;
-	errno_t rc;
-
-	assert(inetping_sess == NULL);
-
-	inetping_ev_ops = ev_ops;
-
-	rc = loc_service_get_id(SERVICE_NAME_INET, &inetping_svc,
-	    IPC_FLAG_BLOCKING);
-	if (rc != EOK)
-		return ENOENT;
-
-	inetping_sess = loc_service_connect(inetping_svc, INTERFACE_INETPING,
-	    IPC_FLAG_BLOCKING);
-	if (inetping_sess == NULL)
-		return ENOENT;
-
-	async_exch_t *exch = async_exchange_begin(inetping_sess);
-
-	port_id_t port;
-	rc = async_create_callback_port(exch, INTERFACE_INETPING_CB, 0, 0,
-	    inetping_cb_conn, NULL, &port);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_hangup(inetping_sess);
-		inetping_sess = NULL;
-		return rc;
-	}
-
-	return EOK;
-}
-
-errno_t inetping_send(inetping_sdu_t *sdu)
-{
-	async_exch_t *exch = async_exchange_begin(inetping_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, INETPING_SEND, sdu->seq_no, &answer);
-
-	errno_t 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;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-errno_t inetping_get_srcaddr(const inet_addr_t *remote, inet_addr_t *local)
-{
-	async_exch_t *exch = async_exchange_begin(inetping_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, INETPING_GET_SRCADDR, &answer);
-
-	errno_t 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);
-
-	errno_t retval_local;
-	async_wait_for(req_local, &retval_local);
-
-	if (retval_local != EOK) {
-		async_forget(req);
-		return retval_local;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-static void inetping_ev_recv(ipc_call_t *icall)
-{
-	inetping_sdu_t sdu;
-
-	sdu.seq_no = ipc_get_arg1(icall);
-
-	ipc_call_t call;
-	size_t size;
-	if (!async_data_write_receive(&call, &size)) {
-		async_answer_0(&call, EREFUSED);
-		async_answer_0(icall, EREFUSED);
-		return;
-	}
-
-	if (size != sizeof(sdu.src)) {
-		async_answer_0(&call, EINVAL);
-		async_answer_0(icall, EINVAL);
-		return;
-	}
-
-	errno_t rc = async_data_write_finalize(&call, &sdu.src, size);
-	if (rc != EOK) {
-		async_answer_0(&call, rc);
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	if (!async_data_write_receive(&call, &size)) {
-		async_answer_0(&call, EREFUSED);
-		async_answer_0(icall, EREFUSED);
-		return;
-	}
-
-	if (size != sizeof(sdu.dest)) {
-		async_answer_0(&call, EINVAL);
-		async_answer_0(icall, EINVAL);
-		return;
-	}
-
-	rc = async_data_write_finalize(&call, &sdu.dest, size);
-	if (rc != EOK) {
-		async_answer_0(&call, rc);
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	rc = async_data_write_accept(&sdu.data, false, 0, 0, 0, &sdu.size);
-	if (rc != EOK) {
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	rc = inetping_ev_ops->recv(&sdu);
-	free(sdu.data);
-	async_answer_0(icall, rc);
-}
-
-static void inetping_cb_conn(ipc_call_t *icall, void *arg)
-{
-	while (true) {
-		ipc_call_t call;
-		async_get_call(&call);
-
-		if (!ipc_get_imethod(&call)) {
-			async_answer_0(&call, EOK);
-			return;
-		}
-
-		switch (ipc_get_imethod(&call)) {
-		case INETPING_EV_RECV:
-			inetping_ev_recv(&call);
-			break;
-		default:
-			async_answer_0(&call, ENOTSUP);
-		}
-	}
-}
-
-/** @}
- */
Index: uspace/lib/c/generic/io/asprintf.c
===================================================================
--- uspace/lib/c/generic/io/asprintf.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/generic/io/asprintf.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -89,5 +89,5 @@
 	va_end(args2);
 
-	if (ret > 0) {
+	if (ret >= 0) {
 		*strp = malloc(ret + 1);
 		if (*strp == NULL)
Index: pace/lib/c/generic/io/chardev.c
===================================================================
--- uspace/lib/c/generic/io/chardev.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,223 +1,0 @@
-/*
- * Copyright (c) 2011 Jan Vesely
- * Copyright (c) 2017 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 Character device client interface
- */
-
-#include <errno.h>
-#include <mem.h>
-#include <io/chardev.h>
-#include <ipc/chardev.h>
-#include <stddef.h>
-#include <stdlib.h>
-
-/** Open character device.
- *
- * @param sess Session with the character device
- * @param rchardev Place to store pointer to the new character device structure
- *
- * @return EOK on success, ENOMEM if out of memory, EIO on I/O error
- */
-errno_t chardev_open(async_sess_t *sess, chardev_t **rchardev)
-{
-	chardev_t *chardev;
-
-	chardev = calloc(1, sizeof(chardev_t));
-	if (chardev == NULL)
-		return ENOMEM;
-
-	chardev->sess = sess;
-	*rchardev = chardev;
-
-	/* EIO might be used in a future implementation */
-	return EOK;
-}
-
-/** Close character device.
- *
- * Frees the character device structure. The underlying session is
- * not affected.
- *
- * @param chardev Character device or @c NULL
- */
-void chardev_close(chardev_t *chardev)
-{
-	free(chardev);
-}
-
-/** Read from character device.
- *
- * Read as much data as is available from character device up to @a size
- * bytes into @a buf. On success EOK is returned and at least one byte
- * is read (if no byte is available the function blocks). The number
- * of bytes read is stored in @a *nread.
- *
- * On error a non-zero error code is returned and @a *nread is filled with
- * the number of bytes that were successfully transferred.
- *
- * @param chardev Character device
- * @param buf Destination buffer
- * @param size Maximum number of bytes to read
- * @param nread Place to store actual number of bytes read
- * @param flags @c chardev_f_nonblock to return immediately even if no
- *              bytes are available
- *
- * @return EOK on success or non-zero error code
- */
-errno_t chardev_read(chardev_t *chardev, void *buf, size_t size, size_t *nread,
-    chardev_flags_t flags)
-{
-	async_exch_t *exch = async_exchange_begin(chardev->sess);
-
-	if (size > DATA_XFER_LIMIT) {
-		/* This should not hurt anything. */
-		size = DATA_XFER_LIMIT;
-	}
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, CHARDEV_READ, flags, &answer);
-	errno_t rc = async_data_read_start(exch, buf, size);
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		*nread = 0;
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK) {
-		*nread = 0;
-		return retval;
-	}
-
-	*nread = ipc_get_arg2(&answer);
-	/* In case of partial success, ARG1 contains the error code */
-	return (errno_t) ipc_get_arg1(&answer);
-
-}
-
-/** Write up to DATA_XFER_LIMIT bytes to character device.
- *
- * Write up to @a size or DATA_XFER_LIMIT bytes from @a data to character
- * device. On success EOK is returned, bytes were written and @a *nwritten
- * is set to min(@a size, DATA_XFER_LIMIT)
- *
- * On error a non-zero error code is returned and @a *nwritten is filled with
- * the number of bytes that were successfully transferred.
- *
- * @param chardev Character device
- * @param buf Destination buffer
- * @param size Maximum number of bytes to read
- * @param nwritten Place to store actual number of bytes written
- *
- * @return EOK on success or non-zero error code
- */
-static errno_t chardev_write_once(chardev_t *chardev, const void *data,
-    size_t size, size_t *nwritten)
-{
-	async_exch_t *exch = async_exchange_begin(chardev->sess);
-	ipc_call_t answer;
-	aid_t req;
-	errno_t rc;
-
-	/* Break down large transfers */
-	if (size > DATA_XFER_LIMIT)
-		size = DATA_XFER_LIMIT;
-
-	req = async_send_0(exch, CHARDEV_WRITE, &answer);
-	rc = async_data_write_start(exch, data, size);
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		*nwritten = 0;
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-	if (retval != EOK) {
-		*nwritten = 0;
-		return retval;
-	}
-
-	*nwritten = ipc_get_arg2(&answer);
-	/* In case of partial success, ARG1 contains the error code */
-	return (errno_t) ipc_get_arg1(&answer);
-}
-
-/** Write to character device.
- *
- * Write @a size bytes from @a data to character device. On success EOK
- * is returned, all bytes were written and @a *nwritten is set to @a size.
- *
- * On error a non-zero error code is returned and @a *nwritten is filled with
- * the number of bytes that were successfully transferred.
- *
- * @param chardev Character device
- * @param buf Destination buffer
- * @param size Maximum number of bytes to read
- * @param nwritten Place to store actual number of bytes written
- *
- * @return EOK on success or non-zero error code
- */
-errno_t chardev_write(chardev_t *chardev, const void *data, size_t size,
-    size_t *nwritten)
-{
-	size_t nw;
-	size_t p;
-	errno_t rc;
-
-	p = 0;
-	while (p < size) {
-		rc = chardev_write_once(chardev, data + p, size - p, &nw);
-		/* nw is always valid, we can have partial success */
-		p += nw;
-
-		if (rc != EOK) {
-			/* We can return partial success */
-			*nwritten = p;
-			return rc;
-		}
-	}
-
-	*nwritten = p;
-	return EOK;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/io/chardev_srv.c
===================================================================
--- uspace/lib/c/generic/io/chardev_srv.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,190 +1,0 @@
-/*
- * Copyright (c) 2014 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 Character device server stub
- */
-#include <errno.h>
-#include <io/chardev_srv.h>
-#include <ipc/chardev.h>
-#include <macros.h>
-#include <stdlib.h>
-#include <stddef.h>
-
-static chardev_srv_t *chardev_srv_create(chardev_srvs_t *);
-
-static void chardev_read_srv(chardev_srv_t *srv, ipc_call_t *icall)
-{
-	void *buf;
-	size_t size;
-	size_t nread;
-	chardev_flags_t flags;
-	errno_t rc;
-
-	flags = ipc_get_arg1(icall);
-
-	ipc_call_t call;
-	if (!async_data_read_receive(&call, &size)) {
-		async_answer_0(icall, EINVAL);
-		return;
-	}
-
-	buf = malloc(size);
-	if (buf == NULL) {
-		async_answer_0(&call, ENOMEM);
-		async_answer_0(icall, ENOMEM);
-		return;
-	}
-
-	if (srv->srvs->ops->read == NULL) {
-		async_answer_0(&call, ENOTSUP);
-		async_answer_0(icall, ENOTSUP);
-		free(buf);
-		return;
-	}
-
-	rc = srv->srvs->ops->read(srv, buf, size, &nread, flags);
-	if (rc != EOK && nread == 0) {
-		async_answer_0(&call, rc);
-		async_answer_0(icall, rc);
-		free(buf);
-		return;
-	}
-
-	async_data_read_finalize(&call, buf, nread);
-
-	free(buf);
-	async_answer_2(icall, EOK, (sysarg_t) rc, nread);
-}
-
-static void chardev_write_srv(chardev_srv_t *srv, ipc_call_t *icall)
-{
-	void *data;
-	size_t size;
-	size_t nwr;
-	errno_t rc;
-
-	rc = async_data_write_accept(&data, false, 0, 0, 0, &size);
-	if (rc != EOK) {
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	if (srv->srvs->ops->write == NULL) {
-		async_answer_0(icall, ENOTSUP);
-		return;
-	}
-
-	rc = srv->srvs->ops->write(srv, data, size, &nwr);
-	free(data);
-	if (rc != EOK && nwr == 0) {
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	async_answer_2(icall, EOK, (sysarg_t) rc, nwr);
-}
-
-static chardev_srv_t *chardev_srv_create(chardev_srvs_t *srvs)
-{
-	chardev_srv_t *srv;
-
-	srv = calloc(1, sizeof(chardev_srv_t));
-	if (srv == NULL)
-		return NULL;
-
-	srv->srvs = srvs;
-	return srv;
-}
-
-void chardev_srvs_init(chardev_srvs_t *srvs)
-{
-	srvs->ops = NULL;
-	srvs->sarg = NULL;
-}
-
-errno_t chardev_conn(ipc_call_t *icall, chardev_srvs_t *srvs)
-{
-	chardev_srv_t *srv;
-	errno_t rc;
-
-	/* Accept the connection */
-	async_accept_0(icall);
-
-	srv = chardev_srv_create(srvs);
-	if (srv == NULL)
-		return ENOMEM;
-
-	if (srvs->ops->open != NULL) {
-		rc = srvs->ops->open(srvs, srv);
-		if (rc != EOK)
-			return rc;
-	}
-
-	while (true) {
-		ipc_call_t call;
-		async_get_call(&call);
-		sysarg_t method = ipc_get_imethod(&call);
-
-		if (!method) {
-			/* The other side has hung up */
-			async_answer_0(&call, EOK);
-			break;
-		}
-
-		switch (method) {
-		case CHARDEV_READ:
-			chardev_read_srv(srv, &call);
-			break;
-		case CHARDEV_WRITE:
-			chardev_write_srv(srv, &call);
-			break;
-		default:
-			if (srv->srvs->ops->def_handler != NULL)
-				srv->srvs->ops->def_handler(srv, &call);
-			else
-				async_answer_0(&call, ENOTSUP);
-		}
-	}
-
-	if (srvs->ops->close != NULL)
-		rc = srvs->ops->close(srv);
-	else
-		rc = EOK;
-
-	free(srv);
-
-	return rc;
-}
-
-/** @}
- */
Index: uspace/lib/c/generic/io/con_srv.c
===================================================================
--- uspace/lib/c/generic/io/con_srv.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/generic/io/con_srv.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -34,4 +34,5 @@
  * @brief Console protocol server stub
  */
+#include <as.h>
 #include <errno.h>
 #include <io/cons_event.h>
@@ -307,4 +308,82 @@
 }
 
+/** Create shared buffer for efficient rendering */
+static void con_map_srv(con_srv_t *srv, ipc_call_t *icall)
+{
+	errno_t rc;
+	charfield_t *buf;
+	sysarg_t cols, rows;
+	ipc_call_t call;
+	size_t size;
+
+	if (srv->srvs->ops->map == NULL || srv->srvs->ops->unmap == NULL) {
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	cols = ipc_get_arg1(icall);
+	rows = ipc_get_arg2(icall);
+
+	if (!async_share_in_receive(&call, &size)) {
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	/* Check size */
+	if (size != PAGES2SIZE(SIZE2PAGES(cols * rows * sizeof(charfield_t)))) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = srv->srvs->ops->map(srv, cols, rows, &buf);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = async_share_in_finalize(&call, buf, AS_AREA_READ |
+	    AS_AREA_WRITE | AS_AREA_CACHEABLE);
+	if (rc != EOK) {
+		srv->srvs->ops->unmap(srv);
+		async_answer_0(icall, EIO);
+		return;
+	}
+
+	async_answer_0(icall, EOK);
+}
+
+/** Delete shared buffer */
+static void con_unmap_srv(con_srv_t *srv, ipc_call_t *icall)
+{
+	if (srv->srvs->ops->unmap == NULL) {
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	srv->srvs->ops->unmap(srv);
+	async_answer_0(icall, EOK);
+}
+
+/** Update console area from shared buffer */
+static void con_update_srv(con_srv_t *srv, ipc_call_t *icall)
+{
+	sysarg_t c0, r0, c1, r1;
+
+	c0 = ipc_get_arg1(icall);
+	r0 = ipc_get_arg2(icall);
+	c1 = ipc_get_arg3(icall);
+	r1 = ipc_get_arg4(icall);
+
+	if (srv->srvs->ops->update == NULL) {
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	srv->srvs->ops->update(srv, c0, r0, c1, r1);
+	async_answer_0(icall, EOK);
+}
+
 static con_srv_t *con_srv_create(con_srvs_t *srvs)
 {
@@ -412,4 +491,13 @@
 			con_get_event_srv(srv, &call);
 			break;
+		case CONSOLE_MAP:
+			con_map_srv(srv, &call);
+			break;
+		case CONSOLE_UNMAP:
+			con_unmap_srv(srv, &call);
+			break;
+		case CONSOLE_UPDATE:
+			con_update_srv(srv, &call);
+			break;
 		default:
 			async_answer_0(&call, ENOTSUP);
Index: uspace/lib/c/generic/io/console.c
===================================================================
--- uspace/lib/c/generic/io/console.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/generic/io/console.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,6 +1,6 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2006 Josef Cejka
  * Copyright (c) 2006 Jakub Vana
- * Copyright (c) 2008 Jiri Svoboda
  * All rights reserved.
  *
@@ -35,4 +35,5 @@
  */
 
+#include <as.h>
 #include <libc.h>
 #include <async.h>
@@ -179,5 +180,11 @@
 }
 
-bool console_get_event(console_ctrl_t *ctrl, cons_event_t *event)
+/** Get console event.
+ *
+ * @param ctrl Console
+ * @param event Place to store event
+ * @return EOK on success, EIO on failure
+ */
+errno_t console_get_event(console_ctrl_t *ctrl, cons_event_t *event)
 {
 	if (ctrl->input_aid == 0) {
@@ -191,14 +198,10 @@
 		async_wait_for(aid, &rc);
 
-		if (rc != EOK) {
-			errno = rc;
-			return false;
-		}
+		if (rc != EOK)
+			return EIO;
 
 		rc = console_ev_decode(&result, event);
-		if (rc != EOK) {
-			errno = rc;
-			return false;
-		}
+		if (rc != EOK)
+			return EIO;
 	} else {
 		errno_t retval;
@@ -207,20 +210,26 @@
 		ctrl->input_aid = 0;
 
-		if (retval != EOK) {
-			errno = retval;
-			return false;
-		}
+		if (retval != EOK)
+			return EIO;
 
 		errno_t rc = console_ev_decode(&ctrl->input_call, event);
-		if (rc != EOK) {
-			errno = rc;
-			return false;
-		}
-	}
-
-	return true;
-}
-
-bool console_get_event_timeout(console_ctrl_t *ctrl, cons_event_t *event,
+		if (rc != EOK)
+			return EIO;
+	}
+
+	return EOK;
+}
+
+/** Get console event with timeout.
+ *
+ * @param ctrl Console
+ * @param event Place to store event
+ * @param timeout Pointer to timeout. This will be updated to reflect
+ *                the remaining time in case of timeout.
+ * @return EOK on success (event received), ETIMEOUT on time out,
+ *         EIO on I/O error (e.g. lost console connection), ENOMEM
+ *         if out of memory
+ */
+errno_t console_get_event_timeout(console_ctrl_t *ctrl, cons_event_t *event,
     usec_t *timeout)
 {
@@ -238,21 +247,18 @@
 	errno_t rc = async_wait_timeout(ctrl->input_aid, &retval, *timeout);
 	if (rc != EOK) {
+		if (rc == ENOMEM)
+			return ENOMEM;
 		*timeout = 0;
-		errno = rc;
-		return false;
+		return ETIMEOUT;
 	}
 
 	ctrl->input_aid = 0;
 
-	if (retval != EOK) {
-		errno = retval;
-		return false;
-	}
+	if (retval != EOK)
+		return EIO;
 
 	rc = console_ev_decode(&ctrl->input_call, event);
-	if (rc != EOK) {
-		errno = rc;
-		return false;
-	}
+	if (rc != EOK)
+		return EIO;
 
 	/* Update timeout */
@@ -261,5 +267,83 @@
 	*timeout -= NSEC2USEC(ts_sub_diff(&t1, &t0));
 
-	return true;
+	return EOK;
+}
+
+/** Create a shared buffer for fast rendering to the console.
+ *
+ * @param ctrl Console
+ * @param cols Number of columns
+ * @param rows Number of rows
+ * @param rbuf Place to store pointer to the shared buffer
+ * @return EOK on success or an error code
+ */
+errno_t console_map(console_ctrl_t *ctrl, sysarg_t cols, sysarg_t rows,
+    charfield_t **rbuf)
+{
+	async_exch_t *exch = NULL;
+	void *buf;
+	aid_t req;
+	ipc_call_t answer;
+	size_t asize;
+	errno_t rc;
+
+	exch = async_exchange_begin(ctrl->output_sess);
+	req = async_send_2(exch, CONSOLE_MAP, cols, rows, &answer);
+
+	asize = PAGES2SIZE(SIZE2PAGES(cols * rows * sizeof(charfield_t)));
+
+	rc = async_share_in_start_0_0(exch, asize, &buf);
+	if (rc != EOK) {
+		async_forget(req);
+		goto error;
+	}
+
+	async_exchange_end(exch);
+	exch = NULL;
+
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		goto error;
+
+	*rbuf = (charfield_t *)buf;
+	return EOK;
+error:
+	if (exch != NULL)
+		async_exchange_end(exch);
+	return rc;
+}
+
+/** Unmap console shared buffer.
+ *
+ * @param ctrl Console
+ * @param buf Buffer
+ */
+void console_unmap(console_ctrl_t *ctrl, charfield_t *buf)
+{
+	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
+	(void) async_req_0_0(exch, CONSOLE_UNMAP);
+	async_exchange_end(exch);
+
+	as_area_destroy(buf);
+}
+
+/** Update console rectangle from shared buffer.
+ *
+ * @param ctrl Console
+ * @param c0 Column coordinate of top-left corner (inclusive)
+ * @param r0 Row coordinate of top-left corner (inclusive)
+ * @param c1 Column coordinate of bottom-right corner (exclusive)
+ * @param r1 Row coordinate of bottom-right corner (exclusive)
+ *
+ * @return EOK on sucess or an error code
+ */
+errno_t console_update(console_ctrl_t *ctrl, sysarg_t c0, sysarg_t r0,
+    sysarg_t c1, sysarg_t r1)
+{
+	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
+	errno_t rc = async_req_4_0(exch, CONSOLE_UPDATE, c0, r0, c1, r1);
+	async_exchange_end(exch);
+
+	return rc;
 }
 
Index: pace/lib/c/generic/io/label.c
===================================================================
--- uspace/lib/c/generic/io/label.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,105 +1,0 @@
-/*
- * Copyright (c) 2015 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 Volume service API
- */
-
-#include <errno.h>
-#include <io/label.h>
-#include <stdlib.h>
-#include <str.h>
-
-/** Format label type as string.
- *
- * @param ltype Label type
- * @param rstr Place to return pointer to newly allocated string
- * @return EOK on success, ENOMEM if out of memory
- */
-int label_type_format(label_type_t ltype, char **rstr)
-{
-	const char *sltype;
-	char *s;
-
-	sltype = NULL;
-	switch (ltype) {
-	case lt_none:
-		sltype = "None";
-		break;
-	case lt_mbr:
-		sltype = "MBR";
-		break;
-	case lt_gpt:
-		sltype = "GPT";
-		break;
-	}
-
-	s = str_dup(sltype);
-	if (s == NULL)
-		return ENOMEM;
-
-	*rstr = s;
-	return EOK;
-}
-
-/** Format partition kind as string.
- *
- * @param pkind Partition kind
- * @param rstr Place to return pointer to newly allocated string
- * @return EOK on success, ENOMEM if out of memory
- */
-int label_pkind_format(label_pkind_t pkind, char **rstr)
-{
-	const char *spkind;
-	char *s;
-
-	spkind = NULL;
-	switch (pkind) {
-	case lpk_primary:
-		spkind = "Primary";
-		break;
-	case lpk_extended:
-		spkind = "Extended";
-		break;
-	case lpk_logical:
-		spkind = "Logical";
-		break;
-	}
-
-	s = str_dup(spkind);
-	if (s == NULL)
-		return ENOMEM;
-
-	*rstr = s;
-	return EOK;
-}
-
-/** @}
- */
Index: uspace/lib/c/generic/io/printf_core.c
===================================================================
--- uspace/lib/c/generic/io/printf_core.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/generic/io/printf_core.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1151,5 +1151,5 @@
 		}
 	} else {
-		/* Convert to get the decimal exponent and digit count.*/
+		/* Convert to get the decimal exponent and digit count. */
 		len = double_to_short_str(val, buf, buf_size, &dec_exp);
 		assert(0 < len);
Index: pace/lib/c/generic/io/serial.c
===================================================================
--- uspace/lib/c/generic/io/serial.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,105 +1,0 @@
-/*
- * Copyright (c) 2017 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 <errno.h>
-#include <io/serial.h>
-#include <ipc/serial_ctl.h>
-#include <ipc/services.h>
-#include <stdlib.h>
-
-/** Open serial port device.
- *
- * @param sess Session with the serial port device
- * @param rchardev Place to store pointer to the new serial port device structure
- *
- * @return EOK on success, ENOMEM if out of memory, EIO on I/O error
- */
-errno_t serial_open(async_sess_t *sess, serial_t **rserial)
-{
-	serial_t *serial;
-
-	serial = calloc(1, sizeof(serial_t));
-	if (serial == NULL)
-		return ENOMEM;
-
-	serial->sess = sess;
-	*rserial = serial;
-
-	return EOK;
-}
-
-/** Close serial port device.
- *
- * Frees the serial port device structure. The underlying session is
- * not affected.
- *
- * @param serial Serial port device or @c NULL
- */
-void serial_close(serial_t *serial)
-{
-	free(serial);
-}
-
-/** Set serial port communication properties. */
-errno_t serial_set_comm_props(serial_t *serial, unsigned rate,
-    serial_parity_t parity, unsigned datab, unsigned stopb)
-{
-	async_exch_t *exch = async_exchange_begin(serial->sess);
-
-	errno_t rc = async_req_4_0(exch, SERIAL_SET_COM_PROPS, rate, parity,
-	    datab, stopb);
-
-	async_exchange_end(exch);
-	return rc;
-}
-
-/** Get serial port communication properties. */
-errno_t serial_get_comm_props(serial_t *serial, unsigned *rrate,
-    serial_parity_t *rparity, unsigned *rdatab, unsigned *rstopb)
-{
-	async_exch_t *exch = async_exchange_begin(serial->sess);
-	sysarg_t rate, parity, datab, stopb;
-
-	errno_t rc = async_req_0_4(exch, SERIAL_GET_COM_PROPS, &rate, &parity,
-	    &datab, &stopb);
-
-	async_exchange_end(exch);
-	if (rc != EOK)
-		return rc;
-
-	*rrate = rate;
-	*rparity = parity;
-	*rdatab = datab;
-	*rstopb = stopb;
-
-	return EOK;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/iplink.c
===================================================================
--- uspace/lib/c/generic/iplink.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,308 +1,0 @@
-/*
- * 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
- * @brief IP link client stub
- */
-
-#include <async.h>
-#include <assert.h>
-#include <errno.h>
-#include <inet/iplink.h>
-#include <inet/addr.h>
-#include <ipc/iplink.h>
-#include <ipc/services.h>
-#include <loc.h>
-#include <stdlib.h>
-
-static void iplink_cb_conn(ipc_call_t *icall, void *arg);
-
-errno_t iplink_open(async_sess_t *sess, iplink_ev_ops_t *ev_ops, void *arg,
-    iplink_t **riplink)
-{
-	iplink_t *iplink = calloc(1, sizeof(iplink_t));
-	if (iplink == NULL)
-		return ENOMEM;
-
-	iplink->sess = sess;
-	iplink->ev_ops = ev_ops;
-	iplink->arg = arg;
-
-	async_exch_t *exch = async_exchange_begin(sess);
-
-	port_id_t port;
-	errno_t rc = async_create_callback_port(exch, INTERFACE_IPLINK_CB, 0, 0,
-	    iplink_cb_conn, iplink, &port);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK)
-		goto error;
-
-	*riplink = iplink;
-	return EOK;
-
-error:
-	if (iplink != NULL)
-		free(iplink);
-
-	return rc;
-}
-
-void iplink_close(iplink_t *iplink)
-{
-	/* XXX Synchronize with iplink_cb_conn */
-	free(iplink);
-}
-
-errno_t iplink_send(iplink_t *iplink, iplink_sdu_t *sdu)
-{
-	async_exch_t *exch = async_exchange_begin(iplink->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_2(exch, IPLINK_SEND, (sysarg_t) sdu->src,
-	    (sysarg_t) sdu->dest, &answer);
-
-	errno_t rc = async_data_write_start(exch, sdu->data, sdu->size);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-errno_t iplink_send6(iplink_t *iplink, iplink_sdu6_t *sdu)
-{
-	async_exch_t *exch = async_exchange_begin(iplink->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, IPLINK_SEND6, &answer);
-
-	errno_t rc = async_data_write_start(exch, &sdu->dest, sizeof(addr48_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;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-errno_t iplink_get_mtu(iplink_t *iplink, size_t *rmtu)
-{
-	async_exch_t *exch = async_exchange_begin(iplink->sess);
-
-	sysarg_t mtu;
-	errno_t rc = async_req_0_1(exch, IPLINK_GET_MTU, &mtu);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK)
-		return rc;
-
-	*rmtu = mtu;
-	return EOK;
-}
-
-errno_t iplink_get_mac48(iplink_t *iplink, addr48_t *mac)
-{
-	async_exch_t *exch = async_exchange_begin(iplink->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, IPLINK_GET_MAC48, &answer);
-
-	errno_t rc = async_data_read_start(exch, mac, sizeof(addr48_t));
-
-	loc_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-errno_t iplink_set_mac48(iplink_t *iplink, addr48_t mac)
-{
-	async_exch_t *exch = async_exchange_begin(iplink->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, IPLINK_GET_MAC48, &answer);
-
-	errno_t rc = async_data_read_start(exch, mac, sizeof(addr48_t));
-
-	loc_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-errno_t iplink_addr_add(iplink_t *iplink, inet_addr_t *addr)
-{
-	async_exch_t *exch = async_exchange_begin(iplink->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, IPLINK_ADDR_ADD, &answer);
-
-	errno_t rc = async_data_write_start(exch, addr, sizeof(inet_addr_t));
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-errno_t iplink_addr_remove(iplink_t *iplink, inet_addr_t *addr)
-{
-	async_exch_t *exch = async_exchange_begin(iplink->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, IPLINK_ADDR_REMOVE, &answer);
-
-	errno_t rc = async_data_write_start(exch, addr, sizeof(inet_addr_t));
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	return retval;
-}
-
-void *iplink_get_userptr(iplink_t *iplink)
-{
-	return iplink->arg;
-}
-
-static void iplink_ev_recv(iplink_t *iplink, ipc_call_t *icall)
-{
-	iplink_recv_sdu_t sdu;
-
-	ip_ver_t ver = ipc_get_arg1(icall);
-
-	errno_t rc = async_data_write_accept(&sdu.data, false, 0, 0, 0,
-	    &sdu.size);
-	if (rc != EOK) {
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	rc = iplink->ev_ops->recv(iplink, &sdu, ver);
-	free(sdu.data);
-	async_answer_0(icall, rc);
-}
-
-static void iplink_ev_change_addr(iplink_t *iplink, ipc_call_t *icall)
-{
-	addr48_t *addr;
-	size_t size;
-
-	errno_t rc = async_data_write_accept((void **) &addr, false,
-	    sizeof(addr48_t), sizeof(addr48_t), 0, &size);
-	if (rc != EOK) {
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	rc = iplink->ev_ops->change_addr(iplink, *addr);
-	free(addr);
-	async_answer_0(icall, EOK);
-}
-
-static void iplink_cb_conn(ipc_call_t *icall, void *arg)
-{
-	iplink_t *iplink = (iplink_t *) arg;
-
-	while (true) {
-		ipc_call_t call;
-		async_get_call(&call);
-
-		if (!ipc_get_imethod(&call)) {
-			async_answer_0(&call, EOK);
-			return;
-		}
-
-		switch (ipc_get_imethod(&call)) {
-		case IPLINK_EV_RECV:
-			iplink_ev_recv(iplink, &call);
-			break;
-		case IPLINK_EV_CHANGE_ADDR:
-			iplink_ev_change_addr(iplink, &call);
-			break;
-		default:
-			async_answer_0(&call, ENOTSUP);
-		}
-	}
-}
-
-/** @}
- */
Index: pace/lib/c/generic/iplink_srv.c
===================================================================
--- uspace/lib/c/generic/iplink_srv.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,351 +1,0 @@
-/*
- * 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
- * @brief IP link server stub
- */
-
-#include <errno.h>
-#include <ipc/iplink.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <inet/addr.h>
-#include <inet/iplink_srv.h>
-
-static void iplink_get_mtu_srv(iplink_srv_t *srv, ipc_call_t *call)
-{
-	size_t mtu;
-	errno_t rc = srv->ops->get_mtu(srv, &mtu);
-	async_answer_1(call, rc, mtu);
-}
-
-static void iplink_get_mac48_srv(iplink_srv_t *srv, ipc_call_t *icall)
-{
-	addr48_t mac;
-	errno_t rc = srv->ops->get_mac48(srv, &mac);
-	if (rc != EOK) {
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	ipc_call_t call;
-	size_t size;
-	if (!async_data_read_receive(&call, &size)) {
-		async_answer_0(&call, EREFUSED);
-		async_answer_0(icall, EREFUSED);
-		return;
-	}
-
-	if (size != sizeof(addr48_t)) {
-		async_answer_0(&call, EINVAL);
-		async_answer_0(icall, EINVAL);
-		return;
-	}
-
-	rc = async_data_read_finalize(&call, &mac, size);
-	if (rc != EOK)
-		async_answer_0(&call, rc);
-
-	async_answer_0(icall, rc);
-}
-
-static void iplink_set_mac48_srv(iplink_srv_t *srv, ipc_call_t *icall)
-{
-	errno_t rc;
-	size_t size;
-	addr48_t mac;
-
-	ipc_call_t call;
-	if (!async_data_write_receive(&call, &size)) {
-		async_answer_0(&call, EREFUSED);
-		async_answer_0(icall, EREFUSED);
-	}
-
-	rc = srv->ops->set_mac48(srv, &mac);
-	if (rc != EOK) {
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	rc = async_data_read_finalize(&call, &mac, sizeof(addr48_t));
-	if (rc != EOK)
-		async_answer_0(&call, rc);
-
-	async_answer_0(icall, rc);
-}
-
-static void iplink_addr_add_srv(iplink_srv_t *srv, ipc_call_t *icall)
-{
-	ipc_call_t call;
-	size_t size;
-	if (!async_data_write_receive(&call, &size)) {
-		async_answer_0(&call, EREFUSED);
-		async_answer_0(icall, EREFUSED);
-		return;
-	}
-
-	if (size != sizeof(inet_addr_t)) {
-		async_answer_0(&call, EINVAL);
-		async_answer_0(icall, EINVAL);
-		return;
-	}
-
-	inet_addr_t addr;
-	errno_t rc = async_data_write_finalize(&call, &addr, size);
-	if (rc != EOK) {
-		async_answer_0(&call, rc);
-		async_answer_0(icall, rc);
-	}
-
-	rc = srv->ops->addr_add(srv, &addr);
-	async_answer_0(icall, rc);
-}
-
-static void iplink_addr_remove_srv(iplink_srv_t *srv, ipc_call_t *icall)
-{
-	ipc_call_t call;
-	size_t size;
-	if (!async_data_write_receive(&call, &size)) {
-		async_answer_0(&call, EREFUSED);
-		async_answer_0(icall, EREFUSED);
-		return;
-	}
-
-	if (size != sizeof(inet_addr_t)) {
-		async_answer_0(&call, EINVAL);
-		async_answer_0(icall, EINVAL);
-		return;
-	}
-
-	inet_addr_t addr;
-	errno_t rc = async_data_write_finalize(&call, &addr, size);
-	if (rc != EOK) {
-		async_answer_0(&call, rc);
-		async_answer_0(icall, rc);
-	}
-
-	rc = srv->ops->addr_remove(srv, &addr);
-	async_answer_0(icall, rc);
-}
-
-static void iplink_send_srv(iplink_srv_t *srv, ipc_call_t *icall)
-{
-	iplink_sdu_t sdu;
-
-	sdu.src = ipc_get_arg1(icall);
-	sdu.dest = ipc_get_arg2(icall);
-
-	errno_t rc = async_data_write_accept(&sdu.data, false, 0, 0, 0,
-	    &sdu.size);
-	if (rc != EOK) {
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	rc = srv->ops->send(srv, &sdu);
-	free(sdu.data);
-	async_answer_0(icall, rc);
-}
-
-static void iplink_send6_srv(iplink_srv_t *srv, ipc_call_t *icall)
-{
-	iplink_sdu6_t sdu;
-
-	ipc_call_t call;
-	size_t size;
-	if (!async_data_write_receive(&call, &size)) {
-		async_answer_0(&call, EREFUSED);
-		async_answer_0(icall, EREFUSED);
-		return;
-	}
-
-	if (size != sizeof(addr48_t)) {
-		async_answer_0(&call, EINVAL);
-		async_answer_0(icall, EINVAL);
-		return;
-	}
-
-	errno_t rc = async_data_write_finalize(&call, &sdu.dest, size);
-	if (rc != EOK) {
-		async_answer_0(&call, rc);
-		async_answer_0(icall, rc);
-	}
-
-	rc = async_data_write_accept(&sdu.data, false, 0, 0, 0,
-	    &sdu.size);
-	if (rc != EOK) {
-		async_answer_0(icall, rc);
-		return;
-	}
-
-	rc = srv->ops->send6(srv, &sdu);
-	free(sdu.data);
-	async_answer_0(icall, rc);
-}
-
-void iplink_srv_init(iplink_srv_t *srv)
-{
-	fibril_mutex_initialize(&srv->lock);
-	srv->connected = false;
-	srv->ops = NULL;
-	srv->arg = NULL;
-	srv->client_sess = NULL;
-}
-
-errno_t iplink_conn(ipc_call_t *icall, void *arg)
-{
-	iplink_srv_t *srv = (iplink_srv_t *) arg;
-	errno_t rc;
-
-	fibril_mutex_lock(&srv->lock);
-	if (srv->connected) {
-		fibril_mutex_unlock(&srv->lock);
-		async_answer_0(icall, EBUSY);
-		return EBUSY;
-	}
-
-	srv->connected = true;
-	fibril_mutex_unlock(&srv->lock);
-
-	/* Accept the connection */
-	async_accept_0(icall);
-
-	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
-	if (sess == NULL)
-		return ENOMEM;
-
-	srv->client_sess = sess;
-
-	rc = srv->ops->open(srv);
-	if (rc != EOK)
-		return rc;
-
-	while (true) {
-		ipc_call_t call;
-		async_get_call(&call);
-		sysarg_t method = ipc_get_imethod(&call);
-
-		if (!method) {
-			/* The other side has hung up */
-			fibril_mutex_lock(&srv->lock);
-			srv->connected = false;
-			fibril_mutex_unlock(&srv->lock);
-			async_answer_0(&call, EOK);
-			break;
-		}
-
-		switch (method) {
-		case IPLINK_GET_MTU:
-			iplink_get_mtu_srv(srv, &call);
-			break;
-		case IPLINK_GET_MAC48:
-			iplink_get_mac48_srv(srv, &call);
-			break;
-		case IPLINK_SET_MAC48:
-			iplink_set_mac48_srv(srv, &call);
-			break;
-		case IPLINK_SEND:
-			iplink_send_srv(srv, &call);
-			break;
-		case IPLINK_SEND6:
-			iplink_send6_srv(srv, &call);
-			break;
-		case IPLINK_ADDR_ADD:
-			iplink_addr_add_srv(srv, &call);
-			break;
-		case IPLINK_ADDR_REMOVE:
-			iplink_addr_remove_srv(srv, &call);
-			break;
-		default:
-			async_answer_0(&call, EINVAL);
-		}
-	}
-
-	return srv->ops->close(srv);
-}
-
-/* XXX Version should be part of @a sdu */
-errno_t iplink_ev_recv(iplink_srv_t *srv, iplink_recv_sdu_t *sdu, ip_ver_t ver)
-{
-	if (srv->client_sess == NULL)
-		return EIO;
-
-	async_exch_t *exch = async_exchange_begin(srv->client_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, IPLINK_EV_RECV, (sysarg_t)ver,
-	    &answer);
-
-	errno_t rc = async_data_write_start(exch, sdu->data, sdu->size);
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-errno_t iplink_ev_change_addr(iplink_srv_t *srv, addr48_t *addr)
-{
-	if (srv->client_sess == NULL)
-		return EIO;
-
-	async_exch_t *exch = async_exchange_begin(srv->client_sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_0(exch, IPLINK_EV_CHANGE_ADDR, &answer);
-
-	errno_t rc = async_data_write_start(exch, addr, sizeof(addr48_t));
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/irc.c
===================================================================
--- uspace/lib/c/generic/irc.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,159 +1,0 @@
-/*
- * Copyright (c) 2014 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 <assert.h>
-#include <async.h>
-#include <errno.h>
-#include <fibril.h>
-#include <ipc/irc.h>
-#include <ipc/services.h>
-#include <irc.h>
-#include <loc.h>
-#include <stdlib.h>
-#include <sysinfo.h>
-
-static async_sess_t *irc_sess;
-
-/** Connect to IRC service.
- *
- * @return	EOK on success, EIO on failure
- */
-static errno_t irc_init(void)
-{
-	category_id_t irc_cat;
-	service_id_t *svcs;
-	size_t count;
-	errno_t rc;
-
-	assert(irc_sess == NULL);
-	rc = loc_category_get_id("irc", &irc_cat, IPC_FLAG_BLOCKING);
-	if (rc != EOK)
-		return EIO;
-
-	while (true) {
-		rc = loc_category_get_svcs(irc_cat, &svcs, &count);
-		if (rc != EOK)
-			return EIO;
-
-		if (count > 0)
-			break;
-
-		free(svcs);
-
-		// XXX This is just a temporary hack
-		fibril_usleep(500 * 1000);
-	}
-
-	irc_sess = loc_service_connect(svcs[0], INTERFACE_IRC,
-	    IPC_FLAG_BLOCKING);
-	free(svcs);
-
-	if (irc_sess == NULL)
-		return EIO;
-
-	return EOK;
-}
-
-/** Enable interrupt.
- *
- * Allow interrupt delivery.
- *
- * @param irq	IRQ number
- */
-errno_t irc_enable_interrupt(int irq)
-{
-	errno_t rc;
-
-	if (irc_sess == NULL) {
-		rc = irc_init();
-		if (rc != EOK)
-			return rc;
-	}
-
-	async_exch_t *exch = async_exchange_begin(irc_sess);
-	rc = async_req_1_0(exch, IRC_ENABLE_INTERRUPT, irq);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-/** Disable interrupt.
- *
- * Disallow interrupt delivery.
- *
- * @param irq	IRQ number
- */
-errno_t irc_disable_interrupt(int irq)
-{
-	errno_t rc;
-
-	if (irc_sess == NULL) {
-		rc = irc_init();
-		if (rc != EOK)
-			return rc;
-	}
-
-	async_exch_t *exch = async_exchange_begin(irc_sess);
-	rc = async_req_1_0(exch, IRC_DISABLE_INTERRUPT, irq);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-/** Clear interrupt.
- *
- * Clear/acknowledge interrupt in interrupt controller so that
- * another interrupt can be delivered.
- *
- * @param irq	IRQ number
- */
-errno_t irc_clear_interrupt(int irq)
-{
-	errno_t rc;
-
-	if (irc_sess == NULL) {
-		rc = irc_init();
-		if (rc != EOK)
-			return rc;
-	}
-
-	async_exch_t *exch = async_exchange_begin(irc_sess);
-	rc = async_req_1_0(exch, IRC_CLEAR_INTERRUPT, irq);
-	async_exchange_end(exch);
-
-	return rc;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/pci.c
===================================================================
--- uspace/lib/c/generic/pci.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,225 +1,0 @@
-/*
- * Copyright (c) 2019 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 PCI control service API
- */
-
-#include <abi/ipc/interfaces.h>
-#include <errno.h>
-#include <ipc/devman.h>
-#include <ipc/services.h>
-#include <ipc/pci.h>
-#include <loc.h>
-#include <pci.h>
-#include <stdlib.h>
-#include <types/pci.h>
-
-/** Open PCI service.
- *
- * @param svc_id PCI sevice ID
- * @param rpci   Place to store pointer to PCI service object.
- *
- * @return EOK on success, ENOMEM if out of memory, EIO if service
- *         cannot be contacted.
- */
-errno_t pci_open(service_id_t svc_id, pci_t **rpci)
-{
-	pci_t *pci;
-	errno_t rc;
-
-	pci = calloc(1, sizeof(pci_t));
-	if (pci == NULL) {
-		rc = ENOMEM;
-		goto error;
-	}
-
-	pci->sess = loc_service_connect(svc_id, INTERFACE_PCI, 0);
-	if (pci->sess == NULL) {
-		rc = EIO;
-		goto error;
-	}
-
-	*rpci = pci;
-	return EOK;
-error:
-	free(pci);
-	return rc;
-}
-
-/** Close PCI service.
- *
- * @param pci PCI service object or @c NULL
- */
-void pci_close(pci_t *pci)
-{
-	if (pci == NULL)
-		return;
-
-	async_hangup(pci->sess);
-	free(pci);
-}
-
-/** Get list of IDs into a buffer of fixed size.
- *
- * @param pci      PCI service
- * @param method   IPC method
- * @param arg1     First argument
- * @param id_buf   Buffer to store IDs
- * @param buf_size Buffer size
- * @param act_size Place to store actual size of complete data.
- *
- * @return EOK on success or an error code.
- */
-static errno_t pci_get_ids_once(pci_t *pci, sysarg_t method, sysarg_t arg1,
-    sysarg_t *id_buf, size_t buf_size, size_t *act_size)
-{
-	async_exch_t *exch = async_exchange_begin(pci->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, method, arg1, &answer);
-	errno_t rc = async_data_read_start(exch, id_buf, buf_size);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK) {
-		return retval;
-	}
-
-	*act_size = ipc_get_arg1(&answer);
-	return EOK;
-}
-
-/** Get list of IDs.
- *
- * Returns an allocated array of service IDs.
- *
- * @param pci    PCI service
- * @param method IPC method
- * @param arg1   IPC argument 1
- * @param data   Place to store pointer to array of IDs
- * @param count  Place to store number of IDs
- * @return       EOK on success or an error code
- */
-static errno_t pci_get_ids_internal(pci_t *pci, sysarg_t method, sysarg_t arg1,
-    sysarg_t **data, size_t *count)
-{
-	*data = NULL;
-	*count = 0;
-
-	size_t act_size = 0;
-	errno_t rc = pci_get_ids_once(pci, method, arg1, NULL, 0, &act_size);
-	if (rc != EOK)
-		return rc;
-
-	size_t alloc_size = act_size;
-	service_id_t *ids = malloc(alloc_size);
-	if (ids == NULL)
-		return ENOMEM;
-
-	while (true) {
-		rc = pci_get_ids_once(pci, method, arg1, ids, alloc_size,
-		    &act_size);
-		if (rc != EOK)
-			return rc;
-
-		if (act_size <= alloc_size)
-			break;
-
-		alloc_size = act_size;
-		service_id_t *tmp = realloc(ids, alloc_size);
-		if (tmp == NULL) {
-			free(ids);
-			return ENOMEM;
-		}
-		ids = tmp;
-	}
-
-	*count = act_size / sizeof(service_id_t);
-	*data = ids;
-	return EOK;
-}
-
-/** Get list of PCI devices as array of devman handles.
- *
- * @param pci PCI service
- * @param data Place to store pointer to array
- * @param count Place to store length of array (number of entries)
- *
- * @return EOK on success or an error code
- */
-errno_t pci_get_devices(pci_t *pci, devman_handle_t **data, size_t *count)
-{
-	return pci_get_ids_internal(pci, PCI_GET_DEVICES, 0, data, count);
-}
-
-extern errno_t pci_dev_get_info(pci_t *, devman_handle_t, pci_dev_info_t *);
-
-/** Get PCI device information.
- *
- * @param pci PCI service
- * @param dev_handle Device handle
- * @param info Place to store device information
- * @return EOK on success or an error code
- */
-errno_t pci_dev_get_info(pci_t *pci, devman_handle_t dev_handle,
-    pci_dev_info_t *info)
-{
-	async_exch_t *exch;
-	errno_t retval;
-	ipc_call_t answer;
-
-	exch = async_exchange_begin(pci->sess);
-	aid_t req = async_send_1(exch, PCI_DEV_GET_INFO, dev_handle, &answer);
-
-	errno_t rc = async_data_read_start(exch, info, sizeof(pci_dev_info_t));
-	async_exchange_end(exch);
-	if (rc != EOK) {
-		async_forget(req);
-		return EIO;
-	}
-
-	async_wait_for(req, &retval);
-	if (retval != EOK)
-		return EIO;
-
-	return EOK;
-}
-
-/** @}
- */
Index: uspace/lib/c/generic/rtld/symbol.c
===================================================================
--- uspace/lib/c/generic/rtld/symbol.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/generic/rtld/symbol.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -68,5 +68,5 @@
 	elf_symbol_t *s, *sym;
 	elf_word nbucket;
-	/*elf_word nchain;*/
+	/* elf_word nchain; */
 	elf_word i;
 	char *s_name;
@@ -77,5 +77,5 @@
 	sym_table = m->dyn.sym_tab;
 	nbucket = m->dyn.hash[0];
-	/*nchain = m->dyn.hash[1]; XXX Use to check HT range*/
+	/* nchain = m->dyn.hash[1]; XXX Use to check HT range */
 
 	bucket = elf_hash((unsigned char *)name) % nbucket;
Index: uspace/lib/c/generic/time.c
===================================================================
--- uspace/lib/c/generic/time.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/generic/time.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -47,4 +47,5 @@
 #include <stdint.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <ctype.h>
 #include <assert.h>
Index: pace/lib/c/generic/vbd.c
===================================================================
--- uspace/lib/c/generic/vbd.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,364 +1,0 @@
-/*
- * Copyright (c) 2015 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 Virtual Block Device client API
- */
-
-#include <abi/ipc/interfaces.h>
-#include <errno.h>
-#include <ipc/services.h>
-#include <ipc/vbd.h>
-#include <loc.h>
-#include <macros.h>
-#include <mem.h>
-#include <stdlib.h>
-#include <types/label.h>
-#include <vbd.h>
-
-static errno_t vbd_get_ids_internal(vbd_t *, sysarg_t, sysarg_t, sysarg_t **,
-    size_t *);
-
-errno_t vbd_create(vbd_t **rvbd)
-{
-	vbd_t *vbd;
-	service_id_t vbd_svcid;
-	errno_t rc;
-
-	vbd = calloc(1, sizeof(vbd_t));
-	if (vbd == NULL) {
-		rc = ENOMEM;
-		goto error;
-	}
-
-	rc = loc_service_get_id(SERVICE_NAME_VBD, &vbd_svcid,
-	    IPC_FLAG_BLOCKING);
-	if (rc != EOK) {
-		rc = EIO;
-		goto error;
-	}
-
-	vbd->sess = loc_service_connect(vbd_svcid, INTERFACE_VBD,
-	    IPC_FLAG_BLOCKING);
-	if (vbd->sess == NULL) {
-		rc = EIO;
-		goto error;
-	}
-
-	*rvbd = vbd;
-	return EOK;
-error:
-	free(vbd);
-	return rc;
-}
-
-void vbd_destroy(vbd_t *vbd)
-{
-	if (vbd == NULL)
-		return;
-
-	async_hangup(vbd->sess);
-	free(vbd);
-}
-
-/** Get list of partitions as array of service IDs.
- *
- * @param vbd Virtual block device service
- * @param data Place to store pointer to array
- * @param count Place to store length of array (number of entries)
- *
- * @return EOK on success or an error code
- */
-errno_t vbd_get_disks(vbd_t *vbd, service_id_t **data, size_t *count)
-{
-	return vbd_get_ids_internal(vbd, VBD_GET_DISKS, 0, data, count);
-}
-
-/** Get disk information. */
-errno_t vbd_disk_info(vbd_t *vbd, service_id_t sid, vbd_disk_info_t *vinfo)
-{
-	async_exch_t *exch;
-	errno_t retval;
-	ipc_call_t answer;
-
-	exch = async_exchange_begin(vbd->sess);
-	aid_t req = async_send_1(exch, VBD_DISK_INFO, sid, &answer);
-	errno_t rc = async_data_read_start(exch, vinfo, sizeof(vbd_disk_info_t));
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return EIO;
-	}
-
-	async_wait_for(req, &retval);
-	if (retval != EOK)
-		return EIO;
-
-	return EOK;
-}
-
-errno_t vbd_label_create(vbd_t *vbd, service_id_t sid, label_type_t ltype)
-{
-	async_exch_t *exch;
-	errno_t retval;
-
-	exch = async_exchange_begin(vbd->sess);
-	retval = async_req_2_0(exch, VBD_LABEL_CREATE, sid, ltype);
-	async_exchange_end(exch);
-
-	if (retval != EOK)
-		return EIO;
-
-	return EOK;
-}
-
-errno_t vbd_label_delete(vbd_t *vbd, service_id_t sid)
-{
-	async_exch_t *exch;
-	errno_t retval;
-
-	exch = async_exchange_begin(vbd->sess);
-	retval = async_req_1_0(exch, VBD_LABEL_DELETE, sid);
-	async_exchange_end(exch);
-
-	if (retval != EOK)
-		return EIO;
-
-	return EOK;
-}
-
-/** Get list of IDs into a buffer of fixed size.
- *
- * @param vbd      Virtual Block Device
- * @param method   IPC method
- * @param arg1     First argument
- * @param id_buf   Buffer to store IDs
- * @param buf_size Buffer size
- * @param act_size Place to store actual size of complete data.
- *
- * @return EOK on success or an error code.
- */
-static errno_t vbd_get_ids_once(vbd_t *vbd, sysarg_t method, sysarg_t arg1,
-    sysarg_t *id_buf, size_t buf_size, size_t *act_size)
-{
-	async_exch_t *exch = async_exchange_begin(vbd->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, method, arg1, &answer);
-	errno_t rc = async_data_read_start(exch, id_buf, buf_size);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK) {
-		return retval;
-	}
-
-	*act_size = ipc_get_arg1(&answer);
-	return EOK;
-}
-
-/** Get list of IDs.
- *
- * Returns an allocated array of service IDs.
- *
- * @param vbd    Virtual Block Device
- * @param method IPC method
- * @param arg1   IPC argument 1
- * @param data   Place to store pointer to array of IDs
- * @param count  Place to store number of IDs
- * @return       EOK on success or an error code
- */
-static errno_t vbd_get_ids_internal(vbd_t *vbd, sysarg_t method, sysarg_t arg1,
-    sysarg_t **data, size_t *count)
-{
-	*data = NULL;
-	*count = 0;
-
-	size_t act_size = 0;
-	errno_t rc = vbd_get_ids_once(vbd, method, arg1, NULL, 0, &act_size);
-	if (rc != EOK)
-		return rc;
-
-	size_t alloc_size = act_size;
-	service_id_t *ids = malloc(alloc_size);
-	if (ids == NULL)
-		return ENOMEM;
-
-	while (true) {
-		rc = vbd_get_ids_once(vbd, method, arg1, ids, alloc_size,
-		    &act_size);
-		if (rc != EOK)
-			return rc;
-
-		if (act_size <= alloc_size)
-			break;
-
-		alloc_size = act_size;
-		service_id_t *tmp = realloc(ids, alloc_size);
-		if (tmp == NULL) {
-			free(ids);
-			return ENOMEM;
-		}
-		ids = tmp;
-	}
-
-	*count = act_size / sizeof(service_id_t);
-	*data = ids;
-	return EOK;
-}
-
-/** Get list of disks as array of service IDs.
- *
- * @param vbd   Virtual Block Device
- * @param data  Place to store pointer to array
- * @param count Place to store length of array (number of entries)
- *
- * @return EOK on success or an error code
- */
-errno_t vbd_label_get_parts(vbd_t *vbd, service_id_t disk,
-    service_id_t **data, size_t *count)
-{
-	return vbd_get_ids_internal(vbd, VBD_LABEL_GET_PARTS, disk,
-	    data, count);
-}
-
-errno_t vbd_part_get_info(vbd_t *vbd, vbd_part_id_t part, vbd_part_info_t *pinfo)
-{
-	async_exch_t *exch;
-	errno_t retval;
-	ipc_call_t answer;
-
-	exch = async_exchange_begin(vbd->sess);
-	aid_t req = async_send_1(exch, VBD_PART_GET_INFO, part, &answer);
-	errno_t rc = async_data_read_start(exch, pinfo, sizeof(vbd_part_info_t));
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return EIO;
-	}
-
-	async_wait_for(req, &retval);
-	if (retval != EOK)
-		return EIO;
-
-	return EOK;
-}
-
-errno_t vbd_part_create(vbd_t *vbd, service_id_t disk, vbd_part_spec_t *pspec,
-    vbd_part_id_t *rpart)
-{
-	async_exch_t *exch;
-	errno_t retval;
-	ipc_call_t answer;
-
-	exch = async_exchange_begin(vbd->sess);
-	aid_t req = async_send_1(exch, VBD_PART_CREATE, disk, &answer);
-	errno_t rc = async_data_write_start(exch, pspec, sizeof(vbd_part_spec_t));
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return EIO;
-	}
-
-	async_wait_for(req, &retval);
-	if (retval != EOK)
-		return EIO;
-
-	*rpart = (vbd_part_id_t)ipc_get_arg1(&answer);
-	return EOK;
-
-}
-
-errno_t vbd_part_delete(vbd_t *vbd, vbd_part_id_t part)
-{
-	async_exch_t *exch;
-	errno_t retval;
-
-	exch = async_exchange_begin(vbd->sess);
-	retval = async_req_1_0(exch, VBD_PART_DELETE, part);
-	async_exchange_end(exch);
-
-	if (retval != EOK)
-		return EIO;
-
-	return EOK;
-}
-
-void vbd_pspec_init(vbd_part_spec_t *pspec)
-{
-	memset(pspec, 0, sizeof(vbd_part_spec_t));
-}
-
-/** Suggest partition type based on partition content.
- *
- * @param vbd   Virtual Block Device
- * @param disk  Disk on which the partition will be created
- * @param pcnt  Partition content
- * @param ptype Place to store suggested partition type
- *
- * @return EOK on success or an error code
- */
-errno_t vbd_suggest_ptype(vbd_t *vbd, service_id_t disk, label_pcnt_t pcnt,
-    label_ptype_t *ptype)
-{
-	async_exch_t *exch;
-	errno_t retval;
-	ipc_call_t answer;
-
-	exch = async_exchange_begin(vbd->sess);
-	aid_t req = async_send_2(exch, VBD_SUGGEST_PTYPE, disk, pcnt, &answer);
-	errno_t rc = async_data_read_start(exch, ptype, sizeof(label_ptype_t));
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return EIO;
-	}
-
-	async_wait_for(req, &retval);
-	if (retval != EOK)
-		return EIO;
-
-	return EOK;
-}
-
-/** @}
- */
Index: pace/lib/c/generic/vol.c
===================================================================
--- uspace/lib/c/generic/vol.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,610 +1,0 @@
-/*
- * Copyright (c) 2015 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 Volume service API
- */
-
-#include <abi/ipc/interfaces.h>
-#include <errno.h>
-#include <ipc/services.h>
-#include <ipc/vol.h>
-#include <loc.h>
-#include <stdlib.h>
-#include <str.h>
-#include <vfs/vfs.h>
-#include <vol.h>
-
-/** Create Volume service session.
- *
- * @param rvol Place to store pointer to volume service session.
- * @return     EOK on success, ENOMEM if out of memory, EIO if service
- *             cannot be contacted.
- */
-errno_t vol_create(vol_t **rvol)
-{
-	vol_t *vol;
-	service_id_t vol_svcid;
-	errno_t rc;
-
-	vol = calloc(1, sizeof(vol_t));
-	if (vol == NULL) {
-		rc = ENOMEM;
-		goto error;
-	}
-
-	rc = loc_service_get_id(SERVICE_NAME_VOLSRV, &vol_svcid, 0);
-	if (rc != EOK) {
-		rc = ENOENT;
-		goto error;
-	}
-
-	vol->sess = loc_service_connect(vol_svcid, INTERFACE_VOL, 0);
-	if (vol->sess == NULL) {
-		rc = EIO;
-		goto error;
-	}
-
-	*rvol = vol;
-	return EOK;
-error:
-	free(vol);
-	return rc;
-}
-
-/** Destroy volume service session.
- *
- * @param vol Volume service session
- */
-void vol_destroy(vol_t *vol)
-{
-	if (vol == NULL)
-		return;
-
-	async_hangup(vol->sess);
-	free(vol);
-}
-
-/** Get list of IDs into a buffer of fixed size.
- *
- * @param vol      Volume service
- * @param method   IPC method
- * @param arg1     First argument
- * @param id_buf   Buffer to store IDs
- * @param buf_size Buffer size
- * @param act_size Place to store actual size of complete data.
- *
- * @return EOK on success or an error code.
- */
-static errno_t vol_get_ids_once(vol_t *vol, sysarg_t method, sysarg_t arg1,
-    sysarg_t *id_buf, size_t buf_size, size_t *act_size)
-{
-	async_exch_t *exch = async_exchange_begin(vol->sess);
-
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, method, arg1, &answer);
-	errno_t rc = async_data_read_start(exch, id_buf, buf_size);
-
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return rc;
-	}
-
-	errno_t retval;
-	async_wait_for(req, &retval);
-
-	if (retval != EOK) {
-		return retval;
-	}
-
-	*act_size = ipc_get_arg1(&answer);
-	return EOK;
-}
-
-/** Get list of IDs.
- *
- * Returns an allocated array of service IDs.
- *
- * @param vol    Volume service
- * @param method IPC method
- * @param arg1   IPC argument 1
- * @param data   Place to store pointer to array of IDs
- * @param count  Place to store number of IDs
- * @return       EOK on success or an error code
- */
-static errno_t vol_get_ids_internal(vol_t *vol, sysarg_t method, sysarg_t arg1,
-    sysarg_t **data, size_t *count)
-{
-	*data = NULL;
-	*count = 0;
-
-	size_t act_size = 0;
-	errno_t rc = vol_get_ids_once(vol, method, arg1, NULL, 0, &act_size);
-	if (rc != EOK)
-		return rc;
-
-	size_t alloc_size = act_size;
-	service_id_t *ids = malloc(alloc_size);
-	if (ids == NULL)
-		return ENOMEM;
-
-	while (true) {
-		rc = vol_get_ids_once(vol, method, arg1, ids, alloc_size,
-		    &act_size);
-		if (rc != EOK)
-			return rc;
-
-		if (act_size <= alloc_size)
-			break;
-
-		alloc_size = act_size;
-		service_id_t *temp = realloc(ids, alloc_size);
-		if (temp == NULL) {
-			free(ids);
-			return ENOMEM;
-		}
-		ids = temp;
-	}
-
-	*count = act_size / sizeof(service_id_t);
-	*data = ids;
-	return EOK;
-}
-
-/** Get list of partitions as array of service IDs.
- *
- * @param vol Volume service
- * @param data Place to store pointer to array
- * @param count Place to store length of array (number of entries)
- *
- * @return EOK on success or an error code
- */
-errno_t vol_get_parts(vol_t *vol, service_id_t **data, size_t *count)
-{
-	return vol_get_ids_internal(vol, VOL_GET_PARTS, 0, data, count);
-}
-
-/** Add partition.
- *
- * After a partition is created (e.g. as a result of deleting a label
- * the dummy partition is created), it can take some (unknown) time
- * until it is discovered.
- *
- * @param vol Volume service
- * @param sid Service ID of the partition
- * @return EOK on success or an error code
- */
-errno_t vol_part_add(vol_t *vol, service_id_t sid)
-{
-	async_exch_t *exch;
-	errno_t retval;
-
-	exch = async_exchange_begin(vol->sess);
-	retval = async_req_1_0(exch, VOL_PART_ADD, sid);
-	async_exchange_end(exch);
-
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-/** Get partition information.
- *
- * @param vol Volume service
- * @param sid Service ID of the partition
- * @param vinfo Place to store partition information
- * @return EOK on success or an error code
- */
-errno_t vol_part_info(vol_t *vol, service_id_t sid, vol_part_info_t *vinfo)
-{
-	async_exch_t *exch;
-	errno_t retval;
-	ipc_call_t answer;
-
-	exch = async_exchange_begin(vol->sess);
-	aid_t req = async_send_1(exch, VOL_PART_INFO, sid, &answer);
-
-	errno_t rc = async_data_read_start(exch, vinfo, sizeof(vol_part_info_t));
-	async_exchange_end(exch);
-	if (rc != EOK) {
-		async_forget(req);
-		return EIO;
-	}
-
-	async_wait_for(req, &retval);
-	if (retval != EOK)
-		return EIO;
-
-	return EOK;
-}
-
-/** Unmount partition (and possibly eject the media).
- *
- * @param vol Volume service
- * @param sid Service ID of the partition
- * @return EOK on success or an error code
- */
-errno_t vol_part_eject(vol_t *vol, service_id_t sid)
-{
-	async_exch_t *exch;
-	errno_t retval;
-
-	exch = async_exchange_begin(vol->sess);
-	retval = async_req_1_0(exch, VOL_PART_EJECT, sid);
-	async_exchange_end(exch);
-
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-/** Erase partition (to the extent where we will consider it not containing
- * a file system.
- *
- * @param vol Volume service
- * @param sid Service ID of the partition
- * @return EOK on success or an error code
- */
-errno_t vol_part_empty(vol_t *vol, service_id_t sid)
-{
-	async_exch_t *exch;
-	errno_t retval;
-
-	exch = async_exchange_begin(vol->sess);
-	retval = async_req_1_0(exch, VOL_PART_EMPTY, sid);
-	async_exchange_end(exch);
-
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-/** Insert volume.
- *
- * This will re-mount the volume if it has been ejected previously.
- *
- * @param vol Volume service
- * @param sid Service ID of the partition
- * @return EOK on success or an error code
- */
-errno_t vol_part_insert(vol_t *vol, service_id_t sid)
-{
-	async_exch_t *exch;
-	errno_t retval;
-
-	exch = async_exchange_begin(vol->sess);
-	retval = async_req_1_0(exch, VOL_PART_INSERT, sid);
-	async_exchange_end(exch);
-
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-/** Insert volume by path.
- *
- * @param vol Volume service
- * @param path Filesystem path
- *
- * @return EOK on success or an error code
- */
-errno_t vol_part_insert_by_path(vol_t *vol, const char *path)
-{
-	async_exch_t *exch;
-	ipc_call_t answer;
-	errno_t retval;
-
-	exch = async_exchange_begin(vol->sess);
-	aid_t req = async_send_0(exch, VOL_PART_INSERT_BY_PATH, &answer);
-
-	retval = async_data_write_start(exch, path, str_size(path));
-	if (retval != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return retval;
-	}
-
-	async_exchange_end(exch);
-	async_wait_for(req, &retval);
-
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-/** Get volume label support.
- *
- * @param vol Volume service
- * @param fstype File system type
- * @param vlsupp Place to store volume label support information
- * @return EOK on success or an error code
- */
-errno_t vol_part_get_lsupp(vol_t *vol, vol_fstype_t fstype,
-    vol_label_supp_t *vlsupp)
-{
-	async_exch_t *exch;
-	errno_t retval;
-	ipc_call_t answer;
-
-	exch = async_exchange_begin(vol->sess);
-	aid_t req = async_send_1(exch, VOL_PART_LSUPP, fstype, &answer);
-	errno_t rc = async_data_read_start(exch, vlsupp, sizeof(vol_label_supp_t));
-	async_exchange_end(exch);
-
-	if (rc != EOK) {
-		async_forget(req);
-		return EIO;
-	}
-
-	async_wait_for(req, &retval);
-	if (retval != EOK)
-		return EIO;
-
-	return EOK;
-}
-
-/** Create file system.
- *
- * @param vol Volume service
- * @param sid Partition service ID
- * @param fstype File system type
- * @param label Volume label
- * @param mountp Mount point
- *
- * @return EOK on success or an error code
- */
-errno_t vol_part_mkfs(vol_t *vol, service_id_t sid, vol_fstype_t fstype,
-    const char *label, const char *mountp)
-{
-	async_exch_t *exch;
-	ipc_call_t answer;
-	errno_t retval;
-
-	exch = async_exchange_begin(vol->sess);
-	aid_t req = async_send_2(exch, VOL_PART_MKFS, sid, fstype, &answer);
-
-	retval = async_data_write_start(exch, label, str_size(label));
-	if (retval != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return retval;
-	}
-
-	retval = async_data_write_start(exch, mountp, str_size(mountp));
-	if (retval != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return retval;
-	}
-
-	async_exchange_end(exch);
-	async_wait_for(req, &retval);
-
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-/** Set mount point for partition.
- *
- * @param vol Volume service
- * @param sid Partition service ID
- * @param mountp Mount point
- *
- * @return EOK on success or an error code
- */
-errno_t vol_part_set_mountp(vol_t *vol, service_id_t sid,
-    const char *mountp)
-{
-	async_exch_t *exch;
-	ipc_call_t answer;
-	errno_t retval;
-
-	exch = async_exchange_begin(vol->sess);
-	aid_t req = async_send_1(exch, VOL_PART_SET_MOUNTP, sid,
-	    &answer);
-
-	retval = async_data_write_start(exch, mountp, str_size(mountp));
-	if (retval != EOK) {
-		async_exchange_end(exch);
-		async_forget(req);
-		return retval;
-	}
-
-	async_exchange_end(exch);
-	async_wait_for(req, &retval);
-
-	if (retval != EOK)
-		return retval;
-
-	return EOK;
-}
-
-/** Format file system type as string.
- *
- * @param fstype File system type
- * @param rstr Place to store pointer to newly allocated string
- * @return EOK on success, ENOMEM if out of memory
- */
-errno_t vol_fstype_format(vol_fstype_t fstype, char **rstr)
-{
-	const char *sfstype;
-	char *s;
-
-	sfstype = NULL;
-	switch (fstype) {
-	case fs_exfat:
-		sfstype = "ExFAT";
-		break;
-	case fs_fat:
-		sfstype = "FAT";
-		break;
-	case fs_minix:
-		sfstype = "MINIX";
-		break;
-	case fs_ext4:
-		sfstype = "Ext4";
-		break;
-	case fs_cdfs:
-		sfstype = "ISO 9660";
-		break;
-	}
-
-	s = str_dup(sfstype);
-	if (s == NULL)
-		return ENOMEM;
-
-	*rstr = s;
-	return EOK;
-}
-
-/** Format partition content / file system type as string.
- *
- * @param pcnt Partition content
- * @param fstype File system type
- * @param rstr Place to store pointer to newly allocated string
- * @return EOK on success, ENOMEM if out of memory
- */
-errno_t vol_pcnt_fs_format(vol_part_cnt_t pcnt, vol_fstype_t fstype,
-    char **rstr)
-{
-	int rc;
-	char *s = NULL;
-
-	switch (pcnt) {
-	case vpc_empty:
-		s = str_dup("Empty");
-		if (s == NULL)
-			return ENOMEM;
-		break;
-	case vpc_fs:
-		rc = vol_fstype_format(fstype, &s);
-		if (rc != EOK)
-			return ENOMEM;
-		break;
-	case vpc_unknown:
-		s = str_dup("Unknown");
-		if (s == NULL)
-			return ENOMEM;
-		break;
-	}
-
-	assert(s != NULL);
-	*rstr = s;
-	return EOK;
-}
-
-/** Validate mount point.
- *
- * Verify that mount point is valid. A valid mount point is
- * one of:
- *  - 'Auto'
- *  - 'None'
- *  - /path (string beginning with '/') to an existing directory
- *
- * @return EOK if mount point is in valid, EINVAL if the format is invalid,
- *         ENOENT if the directory does not exist
- */
-errno_t vol_mountp_validate(const char *mountp)
-{
-	errno_t rc;
-	vfs_stat_t stat;
-
-	if (str_cmp(mountp, "Auto") == 0 || str_cmp(mountp, "auto") == 0)
-		return EOK;
-
-	if (str_casecmp(mountp, "None") == 0 || str_cmp(mountp, "none") == 0)
-		return EOK;
-
-	if (mountp[0] == '/') {
-		rc = vfs_stat_path(mountp, &stat);
-		if (rc != EOK || !stat.is_directory)
-			return ENOENT;
-
-		return EOK;
-	}
-
-	return EINVAL;
-}
-
-/** Get list of volumes as array of volume IDs.
- *
- * @param vol Volume service
- * @param data Place to store pointer to array
- * @param count Place to store length of array (number of entries)
- *
- * @return EOK on success or an error code
- */
-errno_t vol_get_volumes(vol_t *vol, volume_id_t **data, size_t *count)
-{
-	return vol_get_ids_internal(vol, VOL_GET_VOLUMES, 0,
-	    (sysarg_t **) data, count);
-}
-
-/** Get volume configuration information.
- *
- * @param vol Volume service
- * @param vid Volume ID
- * @param vinfo Place to store volume configuration information
- * @return EOK on success or an error code
- */
-errno_t vol_info(vol_t *vol, volume_id_t vid, vol_info_t *vinfo)
-{
-	async_exch_t *exch;
-	errno_t retval;
-	ipc_call_t answer;
-
-	exch = async_exchange_begin(vol->sess);
-	aid_t req = async_send_1(exch, VOL_INFO, vid.id, &answer);
-
-	errno_t rc = async_data_read_start(exch, vinfo, sizeof(vol_info_t));
-	async_exchange_end(exch);
-	if (rc != EOK) {
-		async_forget(req);
-		return EIO;
-	}
-
-	async_wait_for(req, &retval);
-	if (retval != EOK)
-		return EIO;
-
-	return EOK;
-}
-
-/** @}
- */
Index: uspace/lib/c/include/adt/hash_table.h
===================================================================
--- uspace/lib/c/include/adt/hash_table.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/include/adt/hash_table.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -41,4 +41,5 @@
 #include <stdbool.h>
 #include <macros.h>
+#include <member.h>
 
 /** Opaque hash table link type. */
Index: uspace/lib/c/include/adt/list.h
===================================================================
--- uspace/lib/c/include/adt/list.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/include/adt/list.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -38,4 +38,5 @@
 
 #include <assert.h>
+#include <member.h>
 #include <stdbool.h>
 #include <stddef.h>
@@ -85,8 +86,8 @@
 
 #define list_get_instance(link, type, member) \
-	((type *) (((void *)(link)) - list_link_to_void(&(((type *) NULL)->member))))
+	member_to_inst(link, type, member)
 
 #define list_foreach(list, member, itype, iterator) \
-	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) 1) \
+	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) sizeof(itype)) \
 		for (link_t *_link = (list).head.next; \
 		    iterator = list_get_instance(_link, itype, member), \
@@ -94,5 +95,5 @@
 
 #define list_foreach_rev(list, member, itype, iterator) \
-	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) 1) \
+	for (itype *iterator = NULL; iterator == NULL; iterator = (itype *) sizeof(itype)) \
 		for (link_t *_link = (list).head.prev; \
 		    iterator = list_get_instance(_link, itype, member), \
@@ -154,5 +155,5 @@
 extern bool list_member(const link_t *, const list_t *);
 extern void list_splice(list_t *, link_t *);
-extern unsigned long list_count(const list_t *);
+extern size_t list_count(const list_t *);
 
 /** Returns true if the link is definitely part of a list. False if not sure. */
@@ -394,7 +395,7 @@
  *
  */
-static inline link_t *list_nth(const list_t *list, unsigned long n)
-{
-	unsigned long cnt = 0;
+static inline link_t *list_nth(const list_t *list, size_t n)
+{
+	size_t cnt = 0;
 
 	link_t *link = list_first(list);
Index: pace/lib/c/include/bd.h
===================================================================
--- uspace/lib/c/include/bd.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,57 +1,0 @@
-/*
- * 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 Block device client interface
- */
-
-#ifndef _LIBC_BD_H_
-#define _LIBC_BD_H_
-
-#include <async.h>
-#include <offset.h>
-
-typedef struct {
-	async_sess_t *sess;
-} bd_t;
-
-extern errno_t bd_open(async_sess_t *, bd_t **);
-extern void bd_close(bd_t *);
-extern errno_t bd_read_blocks(bd_t *, aoff64_t, size_t, void *, size_t);
-extern errno_t bd_read_toc(bd_t *, uint8_t, void *, size_t);
-extern errno_t bd_write_blocks(bd_t *, aoff64_t, size_t, const void *, size_t);
-extern errno_t bd_sync_cache(bd_t *, aoff64_t, size_t);
-extern errno_t bd_get_block_size(bd_t *, size_t *);
-extern errno_t bd_get_num_blocks(bd_t *, aoff64_t *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/bd_srv.h
===================================================================
--- uspace/lib/c/include/bd_srv.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,77 +1,0 @@
-/*
- * 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_BD_SRV_H_
-#define _LIBC_BD_SRV_H_
-
-#include <adt/list.h>
-#include <async.h>
-#include <fibril_synch.h>
-#include <stdbool.h>
-#include <offset.h>
-
-typedef struct bd_ops bd_ops_t;
-
-/** Service setup (per sevice) */
-typedef struct {
-	bd_ops_t *ops;
-	void *sarg;
-} bd_srvs_t;
-
-/** Server structure (per client session) */
-typedef struct {
-	bd_srvs_t *srvs;
-	async_sess_t *client_sess;
-	void *carg;
-} bd_srv_t;
-
-struct bd_ops {
-	errno_t (*open)(bd_srvs_t *, bd_srv_t *);
-	errno_t (*close)(bd_srv_t *);
-	errno_t (*read_blocks)(bd_srv_t *, aoff64_t, size_t, void *, size_t);
-	errno_t (*read_toc)(bd_srv_t *, uint8_t, void *, size_t);
-	errno_t (*sync_cache)(bd_srv_t *, aoff64_t, size_t);
-	errno_t (*write_blocks)(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
-	errno_t (*get_block_size)(bd_srv_t *, size_t *);
-	errno_t (*get_num_blocks)(bd_srv_t *, aoff64_t *);
-};
-
-extern void bd_srvs_init(bd_srvs_t *);
-
-extern errno_t bd_conn(ipc_call_t *, bd_srvs_t *);
-
-#endif
-
-/** @}
- */
Index: uspace/lib/c/include/capa.h
===================================================================
--- uspace/lib/c/include/capa.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/include/capa.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -37,10 +37,7 @@
 #define _LIBC_CAPA_H_
 
-#include <adt/list.h>
-#include <loc.h>
+#include <errno.h>
+#include <stddef.h>
 #include <stdint.h>
-#include <types/label.h>
-#include <types/vol.h>
-#include <vbd.h>
 
 /** Capacity unit */
Index: uspace/lib/c/include/clipboard.h
===================================================================
--- uspace/lib/c/include/clipboard.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/include/clipboard.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -36,4 +36,6 @@
 #define _LIBC_CLIPBOARD_H_
 
+#include <errno.h>
+
 extern errno_t clipboard_put_str(const char *);
 extern errno_t clipboard_get_str(char **);
Index: pace/lib/c/include/device/led_dev.h
===================================================================
--- uspace/lib/c/include/device/led_dev.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,47 +1,0 @@
-/*
- * Copyright (c) 2014 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_DEVICE_LED_DEV_H_
-#define _LIBC_DEVICE_LED_DEV_H_
-
-#include <async.h>
-#include <io/pixel.h>
-
-typedef enum {
-	LED_DEV_COLOR_SET = 0
-} led_dev_method_t;
-
-extern errno_t led_dev_color_set(async_sess_t *, pixel_t);
-
-#endif
Index: pace/lib/c/include/devman.h
===================================================================
--- uspace/lib/c/include/devman.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,89 +1,0 @@
-/*
- * Copyright (c) 2009 Jiri Svoboda
- * 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 libc
- * @{
- */
-/** @file
- */
-
-#ifndef _LIBC_DEVMAN_H_
-#define _LIBC_DEVMAN_H_
-
-#include <ipc/devman.h>
-#include <ipc/loc.h>
-#include <async.h>
-#include <stdbool.h>
-
-extern async_exch_t *devman_exchange_begin_blocking(iface_t);
-extern async_exch_t *devman_exchange_begin(iface_t);
-extern void devman_exchange_end(async_exch_t *);
-
-extern errno_t devman_driver_register(const char *);
-extern errno_t devman_add_function(const char *, fun_type_t, match_id_list_t *,
-    devman_handle_t, devman_handle_t *);
-extern errno_t devman_remove_function(devman_handle_t);
-extern errno_t devman_drv_fun_online(devman_handle_t);
-extern errno_t devman_drv_fun_offline(devman_handle_t);
-
-extern async_sess_t *devman_device_connect(devman_handle_t, unsigned int);
-extern async_sess_t *devman_parent_device_connect(devman_handle_t,
-    unsigned int);
-
-extern errno_t devman_fun_get_handle(const char *, devman_handle_t *,
-    unsigned int);
-extern errno_t devman_fun_get_child(devman_handle_t, devman_handle_t *);
-extern errno_t devman_dev_get_parent(devman_handle_t, devman_handle_t *);
-extern errno_t devman_dev_get_functions(devman_handle_t, devman_handle_t **,
-    size_t *);
-extern errno_t devman_fun_get_match_id(devman_handle_t, size_t, char *, size_t,
-    unsigned int *);
-extern errno_t devman_fun_get_name(devman_handle_t, char *, size_t);
-extern errno_t devman_fun_get_driver_name(devman_handle_t, char *, size_t);
-extern errno_t devman_fun_get_path(devman_handle_t, char *, size_t);
-extern errno_t devman_fun_online(devman_handle_t);
-extern errno_t devman_fun_offline(devman_handle_t);
-
-extern errno_t devman_add_device_to_category(devman_handle_t, const char *);
-extern errno_t devman_fun_sid_to_handle(service_id_t, devman_handle_t *);
-extern errno_t devman_get_drivers(devman_handle_t **, size_t *);
-extern errno_t devman_driver_get_devices(devman_handle_t, devman_handle_t **,
-    size_t *);
-extern errno_t devman_driver_get_handle(const char *, devman_handle_t *);
-extern errno_t devman_driver_get_match_id(devman_handle_t, size_t, char *, size_t,
-    unsigned int *);
-extern errno_t devman_driver_get_name(devman_handle_t, char *, size_t);
-extern errno_t devman_driver_get_state(devman_handle_t, driver_state_t *);
-extern errno_t devman_driver_load(devman_handle_t);
-extern errno_t devman_driver_unload(devman_handle_t);
-
-#endif
-
-/** @}
- */
Index: uspace/lib/c/include/double_to_str.h
===================================================================
--- uspace/lib/c/include/double_to_str.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/include/double_to_str.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -46,5 +46,5 @@
 #define MAX_DOUBLE_STR_BUF_SIZE  21
 
-/* Fwd decl.*/
+/* Fwd decl. */
 struct ieee_double_t_tag;
 
Index: pace/lib/c/include/ieee80211/ieee80211.h
===================================================================
--- uspace/lib/c/include/ieee80211/ieee80211.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,97 +1,0 @@
-/*
- * Copyright (c) 2015 Jan Kolarik
- * 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 ieee80211.h
- *  IEEE 802.11 common definitions.
- */
-
-#ifndef _LIBC_IEEE80211_H_
-#define _LIBC_IEEE80211_H_
-
-#include <adt/list.h>
-#include <nic/nic.h>
-#include <time.h>
-
-/** Max length of scan results array. */
-#define IEEE80211_MAX_RESULTS_LENGTH  32
-
-/** Max SSID length including null character. */
-#define IEEE80211_MAX_SSID_LENGTH  35
-
-/** WiFi security authentication method indicator. */
-typedef enum {
-	IEEE80211_SECURITY_OPEN,
-	IEEE80211_SECURITY_WEP,
-	IEEE80211_SECURITY_WPA,
-	IEEE80211_SECURITY_WPA2
-} ieee80211_security_type_t;
-
-/** WiFi security suite indicator. */
-typedef enum {
-	IEEE80211_SECURITY_SUITE_WEP40,
-	IEEE80211_SECURITY_SUITE_WEP104,
-	IEEE80211_SECURITY_SUITE_CCMP,
-	IEEE80211_SECURITY_SUITE_TKIP
-} ieee80211_security_suite_t;
-
-/** WiFi security authentication method indicator. */
-typedef enum {
-	IEEE80211_SECURITY_AUTH_PSK,
-	IEEE80211_SECURITY_AUTH_8021X
-} ieee80211_security_auth_t;
-
-/** Structure for indicating security network security settings. */
-typedef struct {
-	int type;
-	int group_alg;
-	int pair_alg;
-	int auth;
-} ieee80211_security_t;
-
-/** Structure with scan result info. */
-typedef struct {
-	nic_address_t bssid;
-	char ssid[IEEE80211_MAX_SSID_LENGTH];
-	uint8_t channel;
-	ieee80211_security_t security;
-} ieee80211_scan_result_t;
-
-/** Array of scan results info. */
-typedef struct {
-	uint8_t length;
-	ieee80211_scan_result_t results[IEEE80211_MAX_RESULTS_LENGTH];
-} ieee80211_scan_results_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/addr.h
===================================================================
--- uspace/lib/c/include/inet/addr.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,133 +1,0 @@
-/*
- * 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_ADDR_H_
-#define _LIBC_INET_ADDR_H_
-
-#include <errno.h>
-#include <stdint.h>
-
-typedef uint32_t addr32_t;
-typedef uint8_t addr48_t[6];
-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 {
-	/** IP version */
-	ip_ver_t version;
-	union {
-		addr32_t addr;
-		addr128_t addr6;
-	};
-} inet_addr_t;
-
-/** Network address */
-typedef struct {
-	/** IP version */
-	ip_ver_t version;
-
-	/** Address */
-	union {
-		addr32_t addr;
-		addr128_t addr6;
-	};
-
-	/** Number of valid bits */
-	uint8_t prefix;
-} inet_naddr_t;
-
-extern const addr32_t addr32_broadcast_all_hosts;
-extern const addr48_t addr48_broadcast;
-
-extern void addr48(const addr48_t, addr48_t);
-extern void addr128(const addr128_t, addr128_t);
-
-extern int addr48_compare(const addr48_t, const addr48_t);
-extern int addr128_compare(const addr128_t, const addr128_t);
-
-extern void addr48_solicited_node(const addr128_t, addr48_t);
-
-extern void host2addr128_t_be(const addr128_t, addr128_t);
-extern void addr128_t_be2host(const addr128_t, addr128_t);
-
-extern void inet_addr(inet_addr_t *, uint8_t, uint8_t, uint8_t, uint8_t);
-extern void inet_naddr(inet_naddr_t *, uint8_t, uint8_t, uint8_t, uint8_t,
-    uint8_t);
-
-extern void inet_addr6(inet_addr_t *, uint16_t, uint16_t, uint16_t, uint16_t,
-    uint16_t, uint16_t, uint16_t, uint16_t);
-extern void inet_naddr6(inet_naddr_t *, uint16_t, uint16_t, uint16_t, uint16_t,
-    uint16_t, uint16_t, uint16_t, uint16_t, uint8_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 *);
-
-extern void inet_addr_any(inet_addr_t *);
-extern void inet_naddr_any(inet_naddr_t *);
-
-extern int inet_addr_compare(const inet_addr_t *, const inet_addr_t *);
-extern int inet_addr_is_any(const inet_addr_t *);
-
-extern int inet_naddr_compare(const inet_naddr_t *, const inet_addr_t *);
-extern int inet_naddr_compare_mask(const inet_naddr_t *, const inet_addr_t *);
-
-extern errno_t inet_addr_parse(const char *, inet_addr_t *, char **);
-extern errno_t inet_naddr_parse(const char *, inet_naddr_t *, char **);
-
-extern errno_t inet_addr_format(const inet_addr_t *, char **);
-extern errno_t inet_naddr_format(const inet_naddr_t *, char **);
-
-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 *);
-
-extern void inet_addr_set(addr32_t, inet_addr_t *);
-extern void inet_naddr_set(addr32_t, uint8_t, inet_naddr_t *);
-
-extern void inet_addr_set6(addr128_t, inet_addr_t *);
-extern void inet_naddr_set6(addr128_t, uint8_t, inet_naddr_t *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/dhcp.h
===================================================================
--- uspace/lib/c/include/inet/dhcp.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,48 +1,0 @@
-/*
- * 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 <types/common.h>
-
-extern errno_t dhcp_init(void);
-extern errno_t dhcp_link_add(sysarg_t);
-extern errno_t dhcp_link_remove(sysarg_t);
-extern errno_t dhcp_discover(sysarg_t);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/dnsr.h
===================================================================
--- uspace/lib/c/include/inet/dnsr.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,61 +1,0 @@
-/*
- * 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_DNSR_H_
-#define _LIBC_INET_DNSR_H_
-
-#include <inet/inet.h>
-#include <inet/addr.h>
-
-enum {
-	DNSR_NAME_MAX_SIZE = 255
-};
-
-typedef struct {
-	/** Host canonical name */
-	char *cname;
-	/** Host address */
-	inet_addr_t addr;
-} dnsr_hostinfo_t;
-
-extern errno_t dnsr_init(void);
-extern errno_t dnsr_name2host(const char *, dnsr_hostinfo_t **, ip_ver_t);
-extern void dnsr_hostinfo_destroy(dnsr_hostinfo_t *);
-extern errno_t dnsr_get_srvaddr(inet_addr_t *);
-extern errno_t dnsr_set_srvaddr(inet_addr_t *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/endpoint.h
===================================================================
--- uspace/lib/c/include/inet/endpoint.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,86 +1,0 @@
-/*
- * Copyright (c) 2015 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_ENDPOINT_H_
-#define _LIBC_INET_ENDPOINT_H_
-
-#include <stdint.h>
-#include <inet/addr.h>
-#include <loc.h>
-
-/** Internet port number ranges
- *
- * Port number ranges per RFC 6335 section 6 (Port Number Ranges.
- * Technically port zero is a system port. But since it is reserved,
- * we will use it as a special value denoting no port is specified
- * and we will exclude it from the system port range to disallow
- * ever assigning it.
- */
-enum inet_port_ranges {
-	/** Special value meaning no specific port */
-	inet_port_any = 0,
-	/** Lowest system port (a.k.a. well known port) */
-	inet_port_sys_lo = 1,
-	/** Highest system port (a.k.a. well known port) */
-	inet_port_sys_hi = 1023,
-	/** Lowest user port (a.k.a. registered port) */
-	inet_port_user_lo = 1024,
-	/** Highest user port (a.k.a. registered port) */
-	inet_port_user_hi = 49151,
-	/** Lowest dynamic port (a.k.a. private or ephemeral port) */
-	inet_port_dyn_lo = 49152,
-	/** Highest dynamic port (a.k.a. private or ephemeral port) */
-	inet_port_dyn_hi = 65535
-};
-
-/** Internet endpoint (address-port pair), a.k.a. socket */
-typedef struct {
-	inet_addr_t addr;
-	uint16_t port;
-} inet_ep_t;
-
-/** Internet endpoint pair */
-typedef struct {
-	service_id_t local_link;
-	inet_ep_t local;
-	inet_ep_t remote;
-} inet_ep2_t;
-
-extern void inet_ep_init(inet_ep_t *);
-extern void inet_ep2_init(inet_ep2_t *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/host.h
===================================================================
--- uspace/lib/c/include/inet/host.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,52 +1,0 @@
-/*
- * Copyright (c) 2016 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_HOST_H_
-#define _LIBC_INET_HOST_H_
-
-#include <inet/addr.h>
-#include <inet/endpoint.h>
-#include <types/inet/hostport.h>
-
-extern errno_t inet_host_parse(const char *, inet_host_t **, char **);
-extern errno_t inet_host_format(inet_host_t *, char **);
-extern void inet_host_destroy(inet_host_t *);
-extern errno_t inet_host_lookup_one(inet_host_t *, ip_ver_t, inet_addr_t *);
-extern errno_t inet_host_plookup_one(const char *, ip_ver_t, inet_addr_t *,
-    char **, const char **);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/hostname.h
===================================================================
--- uspace/lib/c/include/inet/hostname.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,43 +1,0 @@
-/*
- * Copyright (c) 2016 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_HOSTNAME_H_
-#define _LIBC_INET_HOSTNAME_H_
-
-extern errno_t inet_hostname_parse(const char *, char **, char **);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/hostport.h
===================================================================
--- uspace/lib/c/include/inet/hostport.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,52 +1,0 @@
-/*
- * Copyright (c) 2016 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_HOSTPORT_H_
-#define _LIBC_INET_HOSTPORT_H_
-
-#include <inet/addr.h>
-#include <inet/endpoint.h>
-#include <types/inet/hostport.h>
-
-extern errno_t inet_hostport_parse(const char *, inet_hostport_t **, char **);
-extern errno_t inet_hostport_format(inet_hostport_t *, char **);
-extern void inet_hostport_destroy(inet_hostport_t *);
-extern errno_t inet_hostport_lookup_one(inet_hostport_t *, ip_ver_t, inet_ep_t *);
-extern errno_t inet_hostport_plookup_one(const char *, ip_ver_t, inet_ep_t *,
-    char **, const char **);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/inet.h
===================================================================
--- uspace/lib/c/include/inet/inet.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,50 +1,0 @@
-/*
- * 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_INET_INET_H_
-#define _LIBC_INET_INET_H_
-
-#include <inet/addr.h>
-#include <ipc/loc.h>
-#include <stdint.h>
-#include <types/inet.h>
-
-extern errno_t inet_init(uint8_t, inet_ev_ops_t *);
-extern errno_t inet_send(inet_dgram_t *, uint8_t, inet_df_t);
-extern errno_t inet_get_srcaddr(inet_addr_t *, uint8_t, inet_addr_t *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/inetcfg.h
===================================================================
--- uspace/lib/c/include/inet/inetcfg.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,62 +1,0 @@
-/*
- * 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_INET_INETCFG_H_
-#define _LIBC_INET_INETCFG_H_
-
-#include <inet/inet.h>
-#include <stddef.h>
-#include <types/inetcfg.h>
-
-extern errno_t inetcfg_init(void);
-extern errno_t inetcfg_addr_create_static(const char *, inet_naddr_t *, sysarg_t, sysarg_t *);
-extern errno_t inetcfg_addr_delete(sysarg_t);
-extern errno_t inetcfg_addr_get(sysarg_t, inet_addr_info_t *);
-extern errno_t inetcfg_addr_get_id(const char *, sysarg_t, sysarg_t *);
-extern errno_t inetcfg_get_addr_list(sysarg_t **, size_t *);
-extern errno_t inetcfg_get_link_list(sysarg_t **, size_t *);
-extern errno_t inetcfg_get_sroute_list(sysarg_t **, size_t *);
-extern errno_t inetcfg_link_add(sysarg_t);
-extern errno_t inetcfg_link_get(sysarg_t, inet_link_info_t *);
-extern errno_t inetcfg_link_remove(sysarg_t);
-extern errno_t inetcfg_sroute_get(sysarg_t, inet_sroute_info_t *);
-extern errno_t inetcfg_sroute_get_id(const char *, sysarg_t *);
-extern errno_t inetcfg_sroute_create(const char *, inet_naddr_t *, inet_addr_t *,
-    sysarg_t *);
-extern errno_t inetcfg_sroute_delete(sysarg_t);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/inetping.h
===================================================================
--- uspace/lib/c/include/inet/inetping.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,52 +1,0 @@
-/*
- * 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_INETPING_H_
-#define _LIBC_INET_INETPING_H_
-
-#include <inet/inet.h>
-#include <types/inetping.h>
-
-typedef struct inetping_ev_ops {
-	errno_t (*recv)(inetping_sdu_t *);
-} inetping_ev_ops_t;
-
-extern errno_t inetping_init(inetping_ev_ops_t *);
-extern errno_t inetping_send(inetping_sdu_t *);
-extern errno_t inetping_get_srcaddr(const inet_addr_t *, inet_addr_t *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/iplink.h
===================================================================
--- uspace/lib/c/include/inet/iplink.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,98 +1,0 @@
-/*
- * 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_INET_IPLINK_H_
-#define _LIBC_INET_IPLINK_H_
-
-#include <async.h>
-#include <inet/addr.h>
-
-struct iplink_ev_ops;
-
-typedef struct {
-	async_sess_t *sess;
-	struct iplink_ev_ops *ev_ops;
-	void *arg;
-} iplink_t;
-
-/** IPv4 link Service Data Unit */
-typedef struct {
-	/** Local source address */
-	addr32_t src;
-	/** Local destination address */
-	addr32_t dest;
-	/** Serialized IP packet */
-	void *data;
-	/** Size of @c data in bytes */
-	size_t size;
-} iplink_sdu_t;
-
-/** IPv6 link Service Data Unit */
-typedef struct {
-	/** Local MAC destination address */
-	addr48_t dest;
-	/** Serialized IP packet */
-	void *data;
-	/** Size of @c data in bytes */
-	size_t size;
-} iplink_sdu6_t;
-
-/** Internet link receive Service Data Unit */
-typedef struct {
-	/** Serialized datagram */
-	void *data;
-	/** Size of @c data in bytes */
-	size_t size;
-} iplink_recv_sdu_t;
-
-typedef struct iplink_ev_ops {
-	errno_t (*recv)(iplink_t *, iplink_recv_sdu_t *, ip_ver_t);
-	errno_t (*change_addr)(iplink_t *, addr48_t);
-} iplink_ev_ops_t;
-
-extern errno_t iplink_open(async_sess_t *, iplink_ev_ops_t *, void *, iplink_t **);
-extern void iplink_close(iplink_t *);
-extern errno_t iplink_send(iplink_t *, iplink_sdu_t *);
-extern errno_t iplink_send6(iplink_t *, iplink_sdu6_t *);
-extern errno_t iplink_addr_add(iplink_t *, inet_addr_t *);
-extern errno_t iplink_addr_remove(iplink_t *, inet_addr_t *);
-extern errno_t iplink_get_mtu(iplink_t *, size_t *);
-extern errno_t iplink_get_mac48(iplink_t *, addr48_t *);
-extern errno_t iplink_set_mac48(iplink_t *, addr48_t);
-extern void *iplink_get_userptr(iplink_t *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/iplink_srv.h
===================================================================
--- uspace/lib/c/include/inet/iplink_srv.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,75 +1,0 @@
-/*
- * 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_INET_IPLINK_SRV_H_
-#define _LIBC_INET_IPLINK_SRV_H_
-
-#include <async.h>
-#include <fibril_synch.h>
-#include <stdbool.h>
-#include <inet/addr.h>
-#include <inet/iplink.h>
-
-struct iplink_ops;
-
-typedef struct {
-	fibril_mutex_t lock;
-	bool connected;
-	struct iplink_ops *ops;
-	void *arg;
-	async_sess_t *client_sess;
-} iplink_srv_t;
-
-typedef struct iplink_ops {
-	errno_t (*open)(iplink_srv_t *);
-	errno_t (*close)(iplink_srv_t *);
-	errno_t (*send)(iplink_srv_t *, iplink_sdu_t *);
-	errno_t (*send6)(iplink_srv_t *, iplink_sdu6_t *);
-	errno_t (*get_mtu)(iplink_srv_t *, size_t *);
-	errno_t (*get_mac48)(iplink_srv_t *, addr48_t *);
-	errno_t (*set_mac48)(iplink_srv_t *, addr48_t *);
-	errno_t (*addr_add)(iplink_srv_t *, inet_addr_t *);
-	errno_t (*addr_remove)(iplink_srv_t *, inet_addr_t *);
-} iplink_ops_t;
-
-extern void iplink_srv_init(iplink_srv_t *);
-
-extern errno_t iplink_conn(ipc_call_t *, void *);
-extern errno_t iplink_ev_recv(iplink_srv_t *, iplink_recv_sdu_t *, ip_ver_t);
-extern errno_t iplink_ev_change_addr(iplink_srv_t *, addr48_t *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/tcp.h
===================================================================
--- uspace/lib/c/include/inet/tcp.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,123 +1,0 @@
-/*
- * Copyright (c) 2015 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_TCP_H_
-#define _LIBC_INET_TCP_H_
-
-#include <fibril_synch.h>
-#include <inet/addr.h>
-#include <inet/endpoint.h>
-#include <inet/inet.h>
-
-/** TCP connection */
-typedef struct {
-	fibril_mutex_t lock;
-	fibril_condvar_t cv;
-	struct tcp *tcp;
-	link_t ltcp;
-	sysarg_t id;
-	struct tcp_cb *cb;
-	void *cb_arg;
-	/** Some received data available in TCP server */
-	bool data_avail;
-	bool connected;
-	bool conn_failed;
-	bool conn_reset;
-} tcp_conn_t;
-
-/** TCP connection listener */
-typedef struct {
-	struct tcp *tcp;
-	link_t ltcp;
-	sysarg_t id;
-	struct tcp_listen_cb *lcb;
-	void *lcb_arg;
-	struct tcp_cb *cb;
-	void *cb_arg;
-} tcp_listener_t;
-
-/** TCP connection callbacks */
-typedef struct tcp_cb {
-	void (*connected)(tcp_conn_t *);
-	void (*conn_failed)(tcp_conn_t *);
-	void (*conn_reset)(tcp_conn_t *);
-	void (*data_avail)(tcp_conn_t *);
-	void (*urg_data)(tcp_conn_t *);
-} tcp_cb_t;
-
-/** TCP listener callbacks */
-typedef struct tcp_listen_cb {
-	void (*new_conn)(tcp_listener_t *, tcp_conn_t *);
-} tcp_listen_cb_t;
-
-/** TCP service */
-typedef struct tcp {
-	/** TCP session */
-	async_sess_t *sess;
-	/** List of connections */
-	list_t conn; /* of tcp_conn_t */
-	/** List of listeners */
-	list_t listener; /* of tcp_listener_t */
-	/** TCP service lock */
-	fibril_mutex_t lock;
-	/** For waiting on cb_done */
-	fibril_condvar_t cv;
-	/** Set to @a true when callback connection handler has terminated */
-	bool cb_done;
-} tcp_t;
-
-extern errno_t tcp_create(tcp_t **);
-extern void tcp_destroy(tcp_t *);
-extern errno_t tcp_conn_create(tcp_t *, inet_ep2_t *, tcp_cb_t *, void *,
-    tcp_conn_t **);
-extern void tcp_conn_destroy(tcp_conn_t *);
-extern void *tcp_conn_userptr(tcp_conn_t *);
-extern errno_t tcp_listener_create(tcp_t *, inet_ep_t *, tcp_listen_cb_t *, void *,
-    tcp_cb_t *, void *, tcp_listener_t **);
-extern void tcp_listener_destroy(tcp_listener_t *);
-extern void *tcp_listener_userptr(tcp_listener_t *);
-
-extern errno_t tcp_conn_wait_connected(tcp_conn_t *);
-extern errno_t tcp_conn_send(tcp_conn_t *, const void *, size_t);
-extern errno_t tcp_conn_send_fin(tcp_conn_t *);
-extern errno_t tcp_conn_push(tcp_conn_t *);
-extern errno_t tcp_conn_reset(tcp_conn_t *);
-
-extern errno_t tcp_conn_recv(tcp_conn_t *, void *, size_t, size_t *);
-extern errno_t tcp_conn_recv_wait(tcp_conn_t *, void *, size_t, size_t *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/inet/udp.h
===================================================================
--- uspace/lib/c/include/inet/udp.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,110 +1,0 @@
-/*
- * Copyright (c) 2015 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_UDP_H_
-#define _LIBC_INET_UDP_H_
-
-#include <async.h>
-#include <fibril_synch.h>
-#include <inet/addr.h>
-#include <inet/endpoint.h>
-#include <inet/inet.h>
-#include <stdbool.h>
-
-/** UDP link state */
-typedef enum {
-	udp_ls_down,
-	udp_ls_up
-} udp_link_state_t;
-
-/** UDP received message */
-typedef struct {
-	struct udp *udp;
-	sysarg_t assoc_id;
-	size_t size;
-	inet_ep_t remote_ep;
-} udp_rmsg_t;
-
-/** UDP received error */
-typedef struct {
-} udp_rerr_t;
-
-/** UDP association */
-typedef struct {
-	struct udp *udp;
-	link_t ludp;
-	sysarg_t id;
-	struct udp_cb *cb;
-	void *cb_arg;
-} udp_assoc_t;
-
-/** UDP callbacks */
-typedef struct udp_cb {
-	void (*recv_msg)(udp_assoc_t *, udp_rmsg_t *);
-	void (*recv_err)(udp_assoc_t *, udp_rerr_t *);
-	void (*link_state)(udp_assoc_t *, udp_link_state_t);
-} udp_cb_t;
-
-/** UDP service */
-typedef struct udp {
-	/** UDP session */
-	async_sess_t *sess;
-	/** List of associations */
-	list_t assoc; /* of udp_assoc_t */
-	/** UDP service lock */
-	fibril_mutex_t lock;
-	/** For waiting on cb_done */
-	fibril_condvar_t cv;
-	/** Set to @a true when callback connection handler has terminated */
-	bool cb_done;
-} udp_t;
-
-extern errno_t udp_create(udp_t **);
-extern void udp_destroy(udp_t *);
-extern errno_t udp_assoc_create(udp_t *, inet_ep2_t *, udp_cb_t *, void *,
-    udp_assoc_t **);
-extern errno_t udp_assoc_set_nolocal(udp_assoc_t *);
-extern void udp_assoc_destroy(udp_assoc_t *);
-extern errno_t udp_assoc_send_msg(udp_assoc_t *, inet_ep_t *, void *, size_t);
-extern void *udp_assoc_userptr(udp_assoc_t *);
-extern size_t udp_rmsg_size(udp_rmsg_t *);
-extern errno_t udp_rmsg_read(udp_rmsg_t *, size_t, void *, size_t);
-extern void udp_rmsg_remote_ep(udp_rmsg_t *, inet_ep_t *);
-extern uint8_t udp_rerr_type(udp_rerr_t *);
-extern uint8_t udp_rerr_code(udp_rerr_t *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/io/chardev.h
===================================================================
--- uspace/lib/c/include/io/chardev.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,53 +1,0 @@
-/*
- * Copyright (c) 2011 Jan Vesely
- * Copyright (c) 2017 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
- * @{
- */
-
-#ifndef _LIBC_IO_CHARDEV_H_
-#define _LIBC_IO_CHARDEV_H_
-
-#include <async.h>
-#include <stddef.h>
-#include <types/io/chardev.h>
-
-typedef struct {
-	async_sess_t *sess;
-} chardev_t;
-
-extern errno_t chardev_open(async_sess_t *, chardev_t **);
-extern void chardev_close(chardev_t *);
-extern errno_t chardev_read(chardev_t *, void *, size_t, size_t *,
-    chardev_flags_t);
-extern errno_t chardev_write(chardev_t *, const void *, size_t, size_t *);
-
-#endif
-/**
- * @}
- */
Index: pace/lib/c/include/io/chardev_srv.h
===================================================================
--- uspace/lib/c/include/io/chardev_srv.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,75 +1,0 @@
-/*
- * Copyright (c) 2014 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_IO_CHARDEV_SRV_H_
-#define _LIBC_IO_CHARDEV_SRV_H_
-
-#include <adt/list.h>
-#include <async.h>
-#include <fibril_synch.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <types/io/chardev.h>
-
-typedef struct chardev_ops chardev_ops_t;
-
-/** Service setup (per sevice) */
-typedef struct {
-	chardev_ops_t *ops;
-	void *sarg;
-} chardev_srvs_t;
-
-/** Server structure (per client session) */
-typedef struct {
-	chardev_srvs_t *srvs;
-	void *carg;
-} chardev_srv_t;
-
-struct chardev_ops {
-	errno_t (*open)(chardev_srvs_t *, chardev_srv_t *);
-	errno_t (*close)(chardev_srv_t *);
-	errno_t (*read)(chardev_srv_t *, void *, size_t, size_t *,
-	    chardev_flags_t);
-	errno_t (*write)(chardev_srv_t *, const void *, size_t, size_t *);
-	void (*def_handler)(chardev_srv_t *, ipc_call_t *);
-};
-
-extern void chardev_srvs_init(chardev_srvs_t *);
-
-extern errno_t chardev_conn(ipc_call_t *, chardev_srvs_t *);
-
-#endif
-
-/** @}
- */
Index: uspace/lib/c/include/io/con_srv.h
===================================================================
--- uspace/lib/c/include/io/con_srv.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/include/io/con_srv.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,4 +39,5 @@
 #include <async.h>
 #include <fibril_synch.h>
+#include <io/charfield.h>
 #include <io/color.h>
 #include <io/concaps.h>
@@ -83,4 +84,7 @@
 	void (*set_cursor_visibility)(con_srv_t *, bool);
 	errno_t (*get_event)(con_srv_t *, cons_event_t *);
+	errno_t (*map)(con_srv_t *, sysarg_t, sysarg_t, charfield_t **);
+	void (*unmap)(con_srv_t *);
+	void (*update)(con_srv_t *, sysarg_t, sysarg_t, sysarg_t, sysarg_t);
 };
 
Index: uspace/lib/c/include/io/console.h
===================================================================
--- uspace/lib/c/include/io/console.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/include/io/console.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2008 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -37,4 +37,5 @@
 
 #include <time.h>
+#include <io/charfield.h>
 #include <io/concaps.h>
 #include <io/kbd_event.h>
@@ -83,7 +84,12 @@
 extern void console_cursor_visibility(console_ctrl_t *, bool);
 extern errno_t console_get_color_cap(console_ctrl_t *, sysarg_t *);
-extern bool console_get_event(console_ctrl_t *, cons_event_t *);
-extern bool console_get_event_timeout(console_ctrl_t *, cons_event_t *,
+extern errno_t console_get_event(console_ctrl_t *, cons_event_t *);
+extern errno_t console_get_event_timeout(console_ctrl_t *, cons_event_t *,
     usec_t *);
+extern errno_t console_map(console_ctrl_t *, sysarg_t, sysarg_t,
+    charfield_t **);
+extern void console_unmap(console_ctrl_t *, charfield_t *);
+extern errno_t console_update(console_ctrl_t *, sysarg_t, sysarg_t, sysarg_t,
+    sysarg_t);
 
 #endif
Index: pace/lib/c/include/io/label.h
===================================================================
--- uspace/lib/c/include/io/label.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,46 +1,0 @@
-/*
- * Copyright (c) 2017 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_IO_LABEL_H_
-#define _LIBC_IO_LABEL_H_
-
-#include <types/label.h>
-
-extern int label_type_format(label_type_t, char **);
-extern int label_pkind_format(label_pkind_t, char **);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/io/serial.h
===================================================================
--- uspace/lib/c/include/io/serial.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,55 +1,0 @@
-/*
- * Copyright (c) 2017 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_IO_SERIAL_H_
-#define _LIBC_IO_SERIAL_H_
-
-#include <async.h>
-#include <ipc/serial_ctl.h>
-
-typedef struct {
-	async_sess_t *sess;
-} serial_t;
-
-extern errno_t serial_open(async_sess_t *, serial_t **);
-extern void serial_close(serial_t *);
-extern errno_t serial_set_comm_props(serial_t *, unsigned, serial_parity_t,
-    unsigned, unsigned);
-extern errno_t serial_get_comm_props(serial_t *, unsigned *, serial_parity_t *,
-    unsigned *, unsigned *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/ipc/adb.h
===================================================================
--- uspace/lib/c/include/ipc/adb.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,52 +1,0 @@
-/*
- * Copyright (c) 2010 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
- * @brief ADB device interface.
- */
-
-#ifndef _LIBC_IPC_ADB_H_
-#define _LIBC_IPC_ADB_H_
-
-#include <ipc/common.h>
-
-typedef enum {
-	ADB_REG_WRITE = IPC_FIRST_USER_METHOD
-} adb_request_t;
-
-typedef enum {
-	ADB_REG_NOTIF = IPC_FIRST_USER_METHOD
-} adb_notif_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/ipc/bd.h
===================================================================
--- uspace/lib/c/include/ipc/bd.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,52 +1,0 @@
-/*
- * Copyright (c) 2009 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_BD_H_
-#define _LIBC_IPC_BD_H_
-
-#include <ipc/common.h>
-
-typedef enum {
-	BD_GET_BLOCK_SIZE = IPC_FIRST_USER_METHOD,
-	BD_GET_NUM_BLOCKS,
-	BD_READ_BLOCKS,
-	BD_SYNC_CACHE,
-	BD_WRITE_BLOCKS,
-	BD_READ_TOC
-} bd_request_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/ipc/chardev.h
===================================================================
--- uspace/lib/c/include/ipc/chardev.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,53 +1,0 @@
-/*
- * Copyright (c) 2014 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
- * @brief Character device interface.
- */
-
-#ifndef _LIBC_IPC_CHARDEV_H_
-#define _LIBC_IPC_CHARDEV_H_
-
-#include <ipc/common.h>
-
-typedef enum {
-	CHARDEV_READ = IPC_FIRST_USER_METHOD,
-	CHARDEV_WRITE,
-} chardev_request_t;
-
-enum {
-	CHARDEV_LIMIT = CHARDEV_WRITE + 1
-};
-
-#endif
-
-/** @}
- */
Index: uspace/lib/c/include/ipc/console.h
===================================================================
--- uspace/lib/c/include/ipc/console.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/include/ipc/console.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2006 Josef Cejka
  * All rights reserved.
@@ -48,5 +49,8 @@
 	CONSOLE_SET_COLOR,
 	CONSOLE_SET_RGB_COLOR,
-	CONSOLE_SET_CURSOR_VISIBILITY
+	CONSOLE_SET_CURSOR_VISIBILITY,
+	CONSOLE_MAP,
+	CONSOLE_UNMAP,
+	CONSOLE_UPDATE
 } console_request_t;
 
Index: pace/lib/c/include/ipc/devman.h
===================================================================
--- uspace/lib/c/include/ipc/devman.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,186 +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
- * @{
- */
-
-#ifndef _LIBC_IPC_DEVMAN_H_
-#define _LIBC_IPC_DEVMAN_H_
-
-#include <ipc/common.h>
-#include <adt/list.h>
-#include <mem.h>
-#include <stdlib.h>
-
-#define DEVMAN_NAME_MAXLEN  256
-
-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 {
-	/** Invalid value for debugging purposes */
-	fun_invalid = 0,
-	/** Function to which child devices attach */
-	fun_inner,
-	/** Fuction exported to external clients (leaf function) */
-	fun_exposed
-} fun_type_t;
-
-/** Ids of device models used for device-to-driver matching.
- */
-typedef struct match_id {
-	/** Pointers to next and previous ids.
-	 */
-	link_t link;
-	/** Id of device model.
-	 */
-	char *id;
-	/** Relevancy of device-to-driver match.
-	 * The higher is the product of scores specified for the device by the bus driver and by the leaf driver,
-	 * the more suitable is the leaf driver for handling the device.
-	 */
-	unsigned int score;
-} match_id_t;
-
-/** List of ids for matching devices to drivers sorted
- * according to match scores in descending order.
- */
-typedef struct match_id_list {
-	list_t ids;
-} match_id_list_t;
-
-static inline match_id_t *create_match_id(void)
-{
-	match_id_t *id = malloc(sizeof(match_id_t));
-	memset(id, 0, sizeof(match_id_t));
-	return id;
-}
-
-static inline void delete_match_id(match_id_t *id)
-{
-	if (id) {
-		if (NULL != id->id) {
-			free(id->id);
-		}
-		free(id);
-	}
-}
-
-static inline void add_match_id(match_id_list_t *ids, match_id_t *id)
-{
-	match_id_t *mid = NULL;
-	link_t *link = ids->ids.head.next;
-
-	while (link != &ids->ids.head) {
-		mid = list_get_instance(link, match_id_t, link);
-		if (mid->score < id->score) {
-			break;
-		}
-		link = link->next;
-	}
-
-	list_insert_before(&id->link, link);
-}
-
-static inline void init_match_ids(match_id_list_t *id_list)
-{
-	list_initialize(&id_list->ids);
-}
-
-static inline void clean_match_ids(match_id_list_t *ids)
-{
-	link_t *link = NULL;
-	match_id_t *id;
-
-	while (!list_empty(&ids->ids)) {
-		link = list_first(&ids->ids);
-		list_remove(link);
-		id = list_get_instance(link, match_id_t, link);
-		delete_match_id(id);
-	}
-}
-
-typedef enum {
-	DEVMAN_DRIVER_REGISTER = IPC_FIRST_USER_METHOD,
-	DEVMAN_ADD_FUNCTION,
-	DEVMAN_ADD_MATCH_ID,
-	DEVMAN_ADD_DEVICE_TO_CATEGORY,
-	DEVMAN_DRV_FUN_ONLINE,
-	DEVMAN_DRV_FUN_OFFLINE,
-	DEVMAN_REMOVE_FUNCTION
-} driver_to_devman_t;
-
-typedef enum {
-	DRIVER_DEV_ADD = IPC_FIRST_USER_METHOD,
-	DRIVER_DEV_REMOVE,
-	DRIVER_DEV_GONE,
-	DRIVER_FUN_ONLINE,
-	DRIVER_FUN_OFFLINE,
-	DRIVER_STOP
-} devman_to_driver_t;
-
-typedef enum {
-	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,
-	DEVMAN_FUN_ONLINE,
-	DEVMAN_FUN_OFFLINE,
-	DEVMAN_FUN_GET_PATH,
-	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,
-	DEVMAN_DRIVER_UNLOAD
-} client_to_devman_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/ipc/dhcp.h
===================================================================
--- uspace/lib/c/include/ipc/dhcp.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,51 +1,0 @@
-/*
- * 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_DISCOVER
-} dhcp_request_t;
-
-#endif
-
-/**
- * @}
- */
Index: pace/lib/c/include/ipc/dnsr.h
===================================================================
--- uspace/lib/c/include/ipc/dnsr.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,47 +1,0 @@
-/*
- * 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_DNSR_H_
-#define _LIBC_IPC_DNSR_H_
-
-#include <ipc/common.h>
-
-typedef enum {
-	DNSR_NAME2HOST = IPC_FIRST_USER_METHOD,
-	DNSR_GET_SRVADDR,
-	DNSR_SET_SRVADDR
-} dnsr_request_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/ipc/inet.h
===================================================================
--- uspace/lib/c/include/ipc/inet.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,86 +1,0 @@
-/*
- * 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 libcipc
- * @{
- */
-/** @file
- */
-
-#ifndef _LIBC_IPC_INET_H_
-#define _LIBC_IPC_INET_H_
-
-#include <ipc/common.h>
-
-/** Requests on Inet default port */
-typedef enum {
-	INET_CALLBACK_CREATE = IPC_FIRST_USER_METHOD,
-	INET_GET_SRCADDR,
-	INET_SEND,
-	INET_SET_PROTO
-} inet_request_t;
-
-/** Events on Inet default port */
-typedef enum {
-	INET_EV_RECV = IPC_FIRST_USER_METHOD
-} inet_event_t;
-
-/** Requests on Inet configuration port */
-typedef enum {
-	INETCFG_ADDR_CREATE_STATIC = IPC_FIRST_USER_METHOD,
-	INETCFG_ADDR_DELETE,
-	INETCFG_ADDR_GET,
-	INETCFG_ADDR_GET_ID,
-	INETCFG_GET_ADDR_LIST,
-	INETCFG_GET_LINK_LIST,
-	INETCFG_GET_SROUTE_LIST,
-	INETCFG_LINK_ADD,
-	INETCFG_LINK_GET,
-	INETCFG_LINK_REMOVE,
-	INETCFG_SROUTE_CREATE,
-	INETCFG_SROUTE_DELETE,
-	INETCFG_SROUTE_GET,
-	INETCFG_SROUTE_GET_ID
-} inetcfg_request_t;
-
-/** Events on Inet ping port */
-typedef enum {
-	INETPING_EV_RECV = IPC_FIRST_USER_METHOD
-} inetping_event_t;
-
-/** Requests on Inet ping port */
-typedef enum {
-	INETPING_SEND = IPC_FIRST_USER_METHOD,
-	INETPING_GET_SRCADDR
-} inetping_request_t;
-
-#endif
-
-/**
- * @}
- */
Index: pace/lib/c/include/ipc/iplink.h
===================================================================
--- uspace/lib/c/include/ipc/iplink.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,59 +1,0 @@
-/*
- * 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 libcipc
- * @{
- */
-/** @file
- */
-
-#ifndef _LIBC_IPC_IPLINK_H_
-#define _LIBC_IPC_IPLINK_H_
-
-#include <ipc/common.h>
-
-typedef enum {
-	IPLINK_GET_MTU = IPC_FIRST_USER_METHOD,
-	IPLINK_GET_MAC48,
-	IPLINK_SET_MAC48,
-	IPLINK_SEND,
-	IPLINK_SEND6,
-	IPLINK_ADDR_ADD,
-	IPLINK_ADDR_REMOVE
-} iplink_request_t;
-
-typedef enum {
-	IPLINK_EV_RECV = IPC_FIRST_USER_METHOD,
-	IPLINK_EV_CHANGE_ADDR,
-} iplink_event_t;
-
-#endif
-
-/**
- * @}
- */
Index: pace/lib/c/include/ipc/irc.h
===================================================================
--- uspace/lib/c/include/ipc/irc.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,49 +1,0 @@
-/*
- * Copyright (c) 2009 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 libcipc
- * @{
- */
-/** @file
- */
-
-#ifndef _LIBC_IPC_IRC_H_
-#define _LIBC_IPC_IRC_H_
-
-#include <ipc/common.h>
-
-typedef enum {
-	IRC_ENABLE_INTERRUPT = IPC_FIRST_USER_METHOD,
-	IRC_DISABLE_INTERRUPT,
-	IRC_CLEAR_INTERRUPT
-} irc_request_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/ipc/pci.h
===================================================================
--- uspace/lib/c/include/ipc/pci.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,50 +1,0 @@
-/*
- * Copyright (c) 2019 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_PCI_H_
-#define _LIBC_IPC_PCI_H_
-
-#include <ipc/common.h>
-
-/** PCI control service requests */
-typedef enum {
-	PCI_GET_DEVICES = IPC_FIRST_USER_METHOD,
-	PCI_DEV_GET_INFO
-} pci_request_t;
-
-#endif
-
-/**
- * @}
- */
Index: pace/lib/c/include/ipc/serial_ctl.h
===================================================================
--- uspace/lib/c/include/ipc/serial_ctl.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,55 +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.
- */
-
-#ifndef _LIBC_IPC_SERIAL_CTL_H_
-#define _LIBC_IPC_SERIAL_CTL_H_
-
-#include <ipc/chardev.h>
-
-/** IPC methods for getting/setting serial communication properties
- *
- * 1st IPC arg: baud rate
- * 2nd IPC arg: parity
- * 3rd IPC arg: number of bits in one word
- * 4th IPC arg: number of stop bits
- *
- */
-typedef enum {
-	SERIAL_GET_COM_PROPS = CHARDEV_LIMIT,
-	SERIAL_SET_COM_PROPS
-} serial_ctl_t;
-
-typedef enum {
-	SERIAL_NO_PARITY = 0,
-	SERIAL_ODD_PARITY = 1,
-	SERIAL_EVEN_PARITY = 3,
-	SERIAL_MARK_PARITY = 5,
-	SERIAL_SPACE_PARITY = 7
-} serial_parity_t;
-
-#endif
Index: pace/lib/c/include/ipc/tcp.h
===================================================================
--- uspace/lib/c/include/ipc/tcp.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,66 +1,0 @@
-/*
- * Copyright (c) 2015 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_TCP_H_
-#define _LIBC_IPC_TCP_H_
-
-#include <ipc/common.h>
-
-typedef enum {
-	TCP_CALLBACK_CREATE = IPC_FIRST_USER_METHOD,
-	TCP_CONN_CREATE,
-	TCP_CONN_DESTROY,
-	TCP_LISTENER_CREATE,
-	TCP_LISTENER_DESTROY,
-	TCP_CONN_SEND,
-	TCP_CONN_SEND_FIN,
-	TCP_CONN_PUSH,
-	TCP_CONN_RESET,
-	TCP_CONN_RECV,
-	TCP_CONN_RECV_WAIT
-} tcp_request_t;
-
-typedef enum {
-	TCP_EV_CONNECTED = IPC_FIRST_USER_METHOD,
-	TCP_EV_CONN_FAILED,
-	TCP_EV_CONN_RESET,
-	TCP_EV_DATA,
-	TCP_EV_URG_DATA,
-	TCP_EV_NEW_CONN
-} tcp_event_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/ipc/udp.h
===================================================================
--- uspace/lib/c/include/ipc/udp.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,58 +1,0 @@
-/*
- * Copyright (c) 2015 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_UDP_H_
-#define _LIBC_IPC_UDP_H_
-
-#include <ipc/common.h>
-
-typedef enum {
-	UDP_CALLBACK_CREATE = IPC_FIRST_USER_METHOD,
-	UDP_ASSOC_CREATE,
-	UDP_ASSOC_DESTROY,
-	UDP_ASSOC_SET_NOLOCAL,
-	UDP_ASSOC_SEND_MSG,
-	UDP_RMSG_INFO,
-	UDP_RMSG_READ,
-	UDP_RMSG_DISCARD
-} udp_request_t;
-
-typedef enum {
-	UDP_EV_DATA = IPC_FIRST_USER_METHOD
-} udp_event_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/ipc/vbd.h
===================================================================
--- uspace/lib/c/include/ipc/vbd.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,53 +1,0 @@
-/*
- * Copyright (c) 2015 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_VBD_H_
-#define _LIBC_IPC_VBD_H_
-
-#include <ipc/common.h>
-
-typedef enum {
-	VBD_GET_DISKS = IPC_FIRST_USER_METHOD,
-	VBD_DISK_INFO,
-	VBD_LABEL_CREATE,
-	VBD_LABEL_DELETE,
-	VBD_LABEL_GET_PARTS,
-	VBD_PART_GET_INFO,
-	VBD_PART_CREATE,
-	VBD_PART_DELETE,
-	VBD_SUGGEST_PTYPE
-} vbd_request_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/ipc/vol.h
===================================================================
--- uspace/lib/c/include/ipc/vol.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,59 +1,0 @@
-/*
- * Copyright (c) 2015 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_VOL_H_
-#define _LIBC_IPC_VOL_H_
-
-#include <ipc/common.h>
-
-#define VOL_LABEL_MAXLEN 63
-#define VOL_MOUNTP_MAXLEN 4096
-
-typedef enum {
-	VOL_GET_PARTS = IPC_FIRST_USER_METHOD,
-	VOL_PART_ADD,
-	VOL_PART_INFO,
-	VOL_PART_EJECT,
-	VOL_PART_EMPTY,
-	VOL_PART_INSERT,
-	VOL_PART_INSERT_BY_PATH,
-	VOL_PART_LSUPP,
-	VOL_PART_MKFS,
-	VOL_PART_SET_MOUNTP,
-	VOL_GET_VOLUMES,
-	VOL_INFO
-} vol_request_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/ipc/window.h
===================================================================
--- uspace/lib/c/include/ipc/window.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,53 +1,0 @@
-/*
- * Copyright (c) 2012 Petr Koupy
- * 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_IPC_WINDOW_H_
-#define _LIBC_IPC_WINDOW_H_
-
-#include <ipc/vfs.h>
-
-typedef enum {
-	WINDOW_REGISTER = IPC_FIRST_USER_METHOD,
-	WINDOW_GET_EVENT,
-	WINDOW_DAMAGE,
-	WINDOW_GRAB,
-	WINDOW_RESIZE,
-	WINDOW_CLOSE,
-	WINDOW_CLOSE_REQUEST
-} window_request_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/irc.h
===================================================================
--- uspace/lib/c/include/irc.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,45 +1,0 @@
-/*
- * Copyright (c) 2014 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_IRC_H_
-#define _LIBC_IRC_H_
-
-extern errno_t irc_enable_interrupt(int);
-extern errno_t irc_disable_interrupt(int);
-extern errno_t irc_clear_interrupt(int);
-
-#endif
-
-/** @}
- */
Index: uspace/lib/c/include/macros.h
===================================================================
--- uspace/lib/c/include/macros.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/include/macros.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -55,8 +55,4 @@
 	    | ((((uint64_t) (up)) & 0xffffffff) << 32))
 
-#define member_to_inst(ptr_member, type, member_identif) \
-	((type *) (((void *) (ptr_member)) - \
-	    ((void *) &(((type *) 0)->member_identif))))
-
 #define _paddname(line)     PADD_ ## line ## __
 #define _padd(width, line, n)  uint ## width ## _t _paddname(line) [n]
Index: uspace/lib/c/include/member.h
===================================================================
--- uspace/lib/c/include/member.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/c/include/member.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2021 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_MEMBER_H_
+#define _LIBC_MEMBER_H_
+
+#include <stdint.h>
+
+#define member_to_inst(ptr_member, type, member_identif) \
+	((type *) (((uintptr_t) (ptr_member)) - \
+	    offsetof(type, member_identif)))
+
+#endif
+
+/** @}
+ */
Index: pace/lib/c/include/nic/eth_phys.h
===================================================================
--- uspace/lib/c/include/nic/eth_phys.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,142 +1,0 @@
-/*
- * Copyright (c) 2011 Radim Vansa
- * Copyright (c) 2011 Jiri Michalec
- * 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 _LIBC_NIC_ETH_PHYS_H_
-#define _LIBC_NIC_ETH_PHYS_H_
-
-#include <stdint.h>
-
-/*****************************************************/
-/* Definitions of possible supported physical layers */
-/*****************************************************/
-
-/* Ethernet physical layers */
-#define ETH_OLD          0
-#define ETH_10M          1
-#define ETH_100M         2
-#define ETH_1000M        3
-#define ETH_10G          4
-#define ETH_40G_100G     5
-/* 6, 7 reserved for future use */
-#define ETH_PHYS_LAYERS  8
-
-/* < 10Mbs ethernets */
-#define ETH_EXPERIMENTAL     0x0001
-#define ETH_1BASE5           0x0002
-/* 10Mbs ethernets */
-#define ETH_10BASE5          0x0001
-#define ETH_10BASE2          0x0002
-#define ETH_10BROAD36        0x0004
-#define ETH_STARLAN_10       0x0008
-#define ETH_LATTISNET        0x0010
-#define ETH_10BASE_T         0x0020
-#define ETH_FOIRL            0x0040
-#define ETH_10BASE_FL        0x0080
-#define ETH_10BASE_FB        0x0100
-#define ETH_10BASE_FP        0x0200
-/* 100Mbs (fast) ethernets */
-#define ETH_100BASE_TX       0x0001
-#define ETH_100BASE_T4       0x0002
-#define ETH_100BASE_T2       0x0004
-#define ETH_100BASE_FX       0x0008
-#define ETH_100BASE_SX       0x0010
-#define ETH_100BASE_BX10     0x0020
-#define ETH_100BASE_LX10     0x0040
-#define ETH_100BASE_VG       0x0080
-/* 1000Mbs (gigabit) ethernets */
-#define ETH_1000BASE_T       0x0001
-#define ETH_1000BASE_TX      0x0002
-#define ETH_1000BASE_SX      0x0004
-#define ETH_1000BASE_LX      0x0008
-#define ETH_1000BASE_LH      0x0010
-#define ETH_1000BASE_CX      0x0020
-#define ETH_1000BASE_BX10    0x0040
-#define ETH_1000BASE_LX10    0x0080
-#define ETH_1000BASE_PX10_D  0x0100
-#define ETH_1000BASE_PX10_U  0x0200
-#define ETH_1000BASE_PX20_D  0x0400
-#define ETH_1000BASE_PX20_U  0x0800
-#define ETH_1000BASE_ZX      0x1000
-#define ETH_1000BASE_KX      0x2000
-/* 10Gbs ethernets */
-#define ETH_10GBASE_SR       0x0001
-#define ETH_10GBASE_LX4      0x0002
-#define ETH_10GBASE_LR       0x0004
-#define ETH_10GBASE_ER       0x0008
-#define ETH_10GBASE_SW       0x0010
-#define ETH_10GBASE_LW       0x0020
-#define ETH_10GBASE_EW       0x0040
-#define ETH_10GBASE_CX4      0x0080
-#define ETH_10GBASE_T        0x0100
-#define ETH_10GBASE_LRM      0x0200
-#define ETH_10GBASE_KX4      0x0400
-#define ETH_10GBASE_KR       0x0800
-/* 40Gbs and 100Gbs ethernets */
-#define ETH_40GBASE_SR4      0x0001
-#define ETH_40GBASE_LR4      0x0002
-#define ETH_40GBASE_CR4      0x0004
-#define ETH_40GBASE_KR4      0x0008
-#define ETH_100GBASE_SR10    0x0010
-#define ETH_100GBASE_LR4     0x0020
-#define ETH_100GBASE_ER4     0x0040
-#define ETH_100GBASE_CR10    0x0080
-
-/******************************************/
-/* Auto-negotiation advertisement options */
-/******************************************/
-
-#define ETH_AUTONEG_10BASE_T_HALF    UINT32_C(0x00000001)
-#define ETH_AUTONEG_10BASE_T_FULL    UINT32_C(0x00000002)
-#define ETH_AUTONEG_100BASE_TX_HALF  UINT32_C(0x00000004)
-#define ETH_AUTONEG_100BASE_T4_HALF  UINT32_C(0x00000008)
-#define ETH_AUTONEG_100BASE_T2_HALF  UINT32_C(0x00000010)
-#define ETH_AUTONEG_100BASE_TX_FULL  UINT32_C(0x00000020)
-#define ETH_AUTONEG_100BASE_T2_FULL  UINT32_C(0x00000040)
-#define ETH_AUTONEG_1000BASE_T_HALF  UINT32_C(0x00000080)
-#define ETH_AUTONEG_1000BASE_T_FULL  UINT32_C(0x00000100)
-
-/** Symetric pause packet (802.3x standard) */
-#define ETH_AUTONEG_PAUSE_SYMETRIC   UINT32_C(0x10000000)
-/** Asymetric pause packet (802.3z standard, gigabit ethernet) */
-#define ETH_AUTONEG_PAUSE_ASYMETRIC  UINT32_C(0x20000000)
-
-#define ETH_AUTONEG_MODE_MASK      UINT32_C(0x0FFFFFFF)
-#define ETH_AUTONEG_FEATURES_MASK  UINT32_C(~(ETH_AUTONEG_MODE_MASK))
-
-#define ETH_AUTONEG_MODES  9
-
-struct eth_autoneg_map {
-	uint32_t value;
-	const char *name;
-};
-
-extern const char *ethernet_names[8][17];
-extern const struct eth_autoneg_map ethernet_autoneg_mapping[ETH_AUTONEG_MODES];
-
-#endif
Index: pace/lib/c/include/nic/nic.h
===================================================================
--- uspace/lib/c/include/nic/nic.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,476 +1,0 @@
-/*
- * Copyright (c) 2009 Lukas Mejdrech
- * Copyright (c) 2011 Radim Vansa
- * 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
- * NIC interface definitions.
- */
-
-#ifndef _LIBC_NIC_H_
-#define _LIBC_NIC_H_
-
-#include <nic/eth_phys.h>
-#include <stdbool.h>
-
-/** Ethernet address length. */
-#define ETH_ADDR  6
-
-/** MAC printing format */
-#define PRIMAC  "%02x:%02x:%02x:%02x:%02x:%02x"
-
-/** MAC arguments */
-#define ARGSMAC(__a) \
-	(__a)[0], (__a)[1], (__a)[2], (__a)[3], (__a)[4], (__a)[5]
-
-/* Compare MAC address with specific value */
-#define MAC_EQUALS_VALUE(__a, __a0, __a1, __a2, __a3, __a4, __a5) \
-	((__a)[0] == (__a0) && (__a)[1] == (__a1) && (__a)[2] == (__a2) \
-	&& (__a)[3] == (__a3) && (__a)[4] == (__a4) && (__a)[5] == (__a5))
-
-#define MAC_IS_ZERO(__x) \
-	MAC_EQUALS_VALUE(__x, 0, 0, 0, 0, 0, 0)
-
-/** Max length of any hw nic address (currently only eth) */
-#define NIC_MAX_ADDRESS_LENGTH  16
-
-#define NIC_VENDOR_MAX_LENGTH         64
-#define NIC_MODEL_MAX_LENGTH          64
-#define NIC_PART_NUMBER_MAX_LENGTH    64
-#define NIC_SERIAL_NUMBER_MAX_LENGTH  64
-
-#define NIC_DEFECTIVE_LONG               0x0001
-#define NIC_DEFECTIVE_SHORT              0x0002
-#define NIC_DEFECTIVE_BAD_CRC            0x0010
-#define NIC_DEFECTIVE_BAD_IPV4_CHECKSUM  0x0020
-#define NIC_DEFECTIVE_BAD_IPV6_CHECKSUM  0x0040
-#define NIC_DEFECTIVE_BAD_TCP_CHECKSUM   0x0080
-#define NIC_DEFECTIVE_BAD_UDP_CHECKSUM   0x0100
-
-/**
- * The bitmap uses single bit for each of the 2^12 = 4096 possible VLAN tags.
- * This means its size is 4096/8 = 512 bytes.
- */
-#define NIC_VLAN_BITMAP_SIZE  512
-
-#define NIC_DEVICE_PRINT_FMT  "%x"
-
-/**
- * Structure covering the MAC address.
- */
-typedef struct nic_address {
-	uint8_t address[ETH_ADDR];
-} nic_address_t;
-
-/** Device state. */
-typedef enum nic_device_state {
-	/**
-	 * Device present and stopped. Moving device to this state means to discard
-	 * all settings and WOL virtues, rebooting the NIC to state as if the
-	 * computer just booted (or the NIC was just inserted in case of removable
-	 * NIC).
-	 */
-	NIC_STATE_STOPPED,
-	/**
-	 * If the NIC is in this state no packets (frames) are transmitted nor
-	 * received. However, the settings are not restarted. You can use this state
-	 * to temporarily disable transmition/reception or atomically (with respect
-	 * to incoming/outcoming packets) change frames acceptance etc.
-	 */
-	NIC_STATE_DOWN,
-	/** Device is normally operating. */
-	NIC_STATE_ACTIVE,
-	/** Just a constant to limit the state numbers */
-	NIC_STATE_MAX,
-} nic_device_state_t;
-
-/**
- * Channel operating mode used on the medium.
- */
-typedef enum {
-	NIC_CM_UNKNOWN,
-	NIC_CM_FULL_DUPLEX,
-	NIC_CM_HALF_DUPLEX,
-	NIC_CM_SIMPLEX
-} nic_channel_mode_t;
-
-/**
- * Role for the device (used e.g. for 1000Gb ethernet)
- */
-typedef enum {
-	NIC_ROLE_UNKNOWN,
-	NIC_ROLE_AUTO,
-	NIC_ROLE_MASTER,
-	NIC_ROLE_SLAVE
-} nic_role_t;
-
-/**
- * Current state of the cable in the device
- */
-typedef enum {
-	NIC_CS_UNKNOWN,
-	NIC_CS_PLUGGED,
-	NIC_CS_UNPLUGGED
-} nic_cable_state_t;
-
-/**
- * Result of the requested operation
- */
-typedef enum {
-	/** Successfully disabled */
-	NIC_RESULT_DISABLED,
-	/** Successfully enabled */
-	NIC_RESULT_ENABLED,
-	/** Not supported at all */
-	NIC_RESULT_NOT_SUPPORTED,
-	/** Temporarily not available */
-	NIC_RESULT_NOT_AVAILABLE,
-	/** Result extensions */
-	NIC_RESULT_FIRST_EXTENSION
-} nic_result_t;
-
-/** Device usage statistics. */
-typedef struct nic_device_stats {
-	/** Total packets received (accepted). */
-	unsigned long receive_packets;
-	/** Total packets transmitted. */
-	unsigned long send_packets;
-	/** Total bytes received (accepted). */
-	unsigned long receive_bytes;
-	/** Total bytes transmitted. */
-	unsigned long send_bytes;
-	/** Bad packets received counter. */
-	unsigned long receive_errors;
-	/** Packet transmition problems counter. */
-	unsigned long send_errors;
-	/** Number of frames dropped due to insufficient space in RX buffers */
-	unsigned long receive_dropped;
-	/** Number of frames dropped due to insufficient space in TX buffers */
-	unsigned long send_dropped;
-	/** Total multicast packets received (accepted). */
-	unsigned long receive_multicast;
-	/** Total broadcast packets received (accepted). */
-	unsigned long receive_broadcast;
-	/** The number of collisions due to congestion on the medium. */
-	unsigned long collisions;
-	/** Unicast packets received but not accepted (filtered) */
-	unsigned long receive_filtered_unicast;
-	/** Multicast packets received but not accepted (filtered) */
-	unsigned long receive_filtered_multicast;
-	/** Broadcast packets received but not accepted (filtered) */
-	unsigned long receive_filtered_broadcast;
-
-	/* detailed receive_errors */
-
-	/** Received packet length error counter. */
-	unsigned long receive_length_errors;
-	/** Receiver buffer overflow counter. */
-	unsigned long receive_over_errors;
-	/** Received packet with crc error counter. */
-	unsigned long receive_crc_errors;
-	/** Received frame alignment error counter. */
-	unsigned long receive_frame_errors;
-	/** Receiver fifo overrun counter. */
-	unsigned long receive_fifo_errors;
-	/** Receiver missed packet counter. */
-	unsigned long receive_missed_errors;
-
-	/* detailed send_errors */
-
-	/** Transmitter aborted counter. */
-	unsigned long send_aborted_errors;
-	/** Transmitter carrier errors counter. */
-	unsigned long send_carrier_errors;
-	/** Transmitter fifo overrun counter. */
-	unsigned long send_fifo_errors;
-	/** Transmitter carrier errors counter. */
-	unsigned long send_heartbeat_errors;
-	/** Transmitter window errors counter. */
-	unsigned long send_window_errors;
-
-	/* for cslip etc */
-
-	/** Total compressed packets received. */
-	unsigned long receive_compressed;
-	/** Total compressed packet transmitted. */
-	unsigned long send_compressed;
-} nic_device_stats_t;
-
-/** Errors corresponding to those in the nic_device_stats_t */
-typedef enum {
-	NIC_SEC_BUFFER_FULL,
-	NIC_SEC_ABORTED,
-	NIC_SEC_CARRIER_LOST,
-	NIC_SEC_FIFO_OVERRUN,
-	NIC_SEC_HEARTBEAT,
-	NIC_SEC_WINDOW_ERROR,
-	/* Error encountered during TX but with other type of error */
-	NIC_SEC_OTHER
-} nic_send_error_cause_t;
-
-/** Errors corresponding to those in the nic_device_stats_t */
-typedef enum {
-	NIC_REC_BUFFER_FULL,
-	NIC_REC_LENGTH,
-	NIC_REC_BUFFER_OVERFLOW,
-	NIC_REC_CRC,
-	NIC_REC_FRAME_ALIGNMENT,
-	NIC_REC_FIFO_OVERRUN,
-	NIC_REC_MISSED,
-	/* Error encountered during RX but with other type of error */
-	NIC_REC_OTHER
-} nic_receive_error_cause_t;
-
-/**
- * Information about the NIC that never changes - name, vendor, model,
- * capabilites and so on.
- */
-typedef struct nic_device_info {
-	/* Device identification */
-	char vendor_name[NIC_VENDOR_MAX_LENGTH];
-	char model_name[NIC_MODEL_MAX_LENGTH];
-	char part_number[NIC_PART_NUMBER_MAX_LENGTH];
-	char serial_number[NIC_SERIAL_NUMBER_MAX_LENGTH];
-	uint16_t vendor_id;
-	uint16_t device_id;
-	uint16_t subsystem_vendor_id;
-	uint16_t subsystem_id;
-	/* Device capabilities */
-	uint16_t ethernet_support[ETH_PHYS_LAYERS];
-
-	/** The mask of all modes which the device can advertise
-	 *
-	 *  see ETH_AUTONEG_ macros in nic/eth_phys.h of libc
-	 */
-	uint32_t autoneg_support;
-} nic_device_info_t;
-
-/**
- * Type of the ethernet frame
- */
-typedef enum nic_frame_type {
-	NIC_FRAME_UNICAST,
-	NIC_FRAME_MULTICAST,
-	NIC_FRAME_BROADCAST
-} nic_frame_type_t;
-
-/**
- * Specifies which unicast frames is the NIC receiving.
- */
-typedef enum nic_unicast_mode {
-	NIC_UNICAST_UNKNOWN,
-	/** No unicast frames are received */
-	NIC_UNICAST_BLOCKED,
-	/** Only the frames with this NIC's MAC as destination are received */
-	NIC_UNICAST_DEFAULT,
-	/**
-	 * Both frames with this NIC's MAC and those specified in the list are
-	 * received
-	 */
-	NIC_UNICAST_LIST,
-	/** All unicast frames are received */
-	NIC_UNICAST_PROMISC
-} nic_unicast_mode_t;
-
-typedef enum nic_multicast_mode {
-	NIC_MULTICAST_UNKNOWN,
-	/** No multicast frames are received */
-	NIC_MULTICAST_BLOCKED,
-	/** Frames with multicast addresses specified in this list are received */
-	NIC_MULTICAST_LIST,
-	/** All multicast frames are received */
-	NIC_MULTICAST_PROMISC
-} nic_multicast_mode_t;
-
-typedef enum nic_broadcast_mode {
-	NIC_BROADCAST_UNKNOWN,
-	/** Broadcast frames are dropped */
-	NIC_BROADCAST_BLOCKED,
-	/** Broadcast frames are received */
-	NIC_BROADCAST_ACCEPTED
-} nic_broadcast_mode_t;
-
-/**
- * Structure covering the bitmap with VLAN tags.
- */
-typedef struct nic_vlan_mask {
-	uint8_t bitmap[NIC_VLAN_BITMAP_SIZE];
-} nic_vlan_mask_t;
-
-/* WOL virtue identifier */
-typedef unsigned int nic_wv_id_t;
-
-/**
- * Structure passed as argument for virtue NIC_WV_MAGIC_PACKET.
- */
-typedef struct nic_wv_magic_packet_data {
-	uint8_t password[6];
-} nic_wv_magic_packet_data_t;
-
-/**
- * Structure passed as argument for virtue NIC_WV_DIRECTED_IPV4
- */
-typedef struct nic_wv_ipv4_data {
-	uint8_t address[4];
-} nic_wv_ipv4_data_t;
-
-/**
- * Structure passed as argument for virtue NIC_WV_DIRECTED_IPV6
- */
-typedef struct nic_wv_ipv6_data {
-	uint8_t address[16];
-} nic_wv_ipv6_data_t;
-
-/**
- * WOL virtue types defining the interpretation of data passed to the virtue.
- * Those tagged with S can have only single virtue active at one moment, those
- * tagged with M can have multiple ones.
- */
-typedef enum nic_wv_type {
-	/**
-	 * Used for deletion of the virtue - in this case the mask, data and length
-	 * arguments are ignored.
-	 */
-	NIC_WV_NONE,
-	/** S
-	 * Enabled <=> wakeup upon link change
-	 */
-	NIC_WV_LINK_CHANGE,
-	/** S
-	 * If this virtue is set up, wakeup can be issued by a magic packet frame.
-	 * If the data argument is not NULL, it must contain
-	 * nic_wv_magic_packet_data structure with the SecureOn password.
-	 */
-	NIC_WV_MAGIC_PACKET,
-	/** M
-	 * If the virtue is set up, wakeup can be issued by a frame targeted to
-	 * device with MAC address specified in data. The data must contain
-	 * nic_address_t structure.
-	 */
-	NIC_WV_DESTINATION,
-	/** S
-	 * Enabled <=> wakeup upon receiving broadcast frame
-	 */
-	NIC_WV_BROADCAST,
-	/** S
-	 * Enabled <=> wakeup upon receiving ARP Request
-	 */
-	NIC_WV_ARP_REQUEST,
-	/** M
-	 * If enabled, the wakeup is issued upon receiving frame with an IPv4 packet
-	 * with IPv4 address specified in data. The data must contain
-	 * nic_wv_ipv4_data structure.
-	 */
-	NIC_WV_DIRECTED_IPV4,
-	/** M
-	 * If enabled, the wakeup is issued upon receiving frame with an IPv4 packet
-	 * with IPv6 address specified in data. The data must contain
-	 * nic_wv_ipv6_data structure.
-	 */
-	NIC_WV_DIRECTED_IPV6,
-	/** M
-	 * First length/2 bytes in the argument are interpreted as mask, second
-	 * length/2 bytes are interpreted as content.
-	 * If enabled, the wakeup is issued upon receiving frame where the bytes
-	 * with non-zero value in the mask equal to those in the content.
-	 */
-	NIC_WV_FULL_MATCH,
-	/**
-	 * Dummy value, do not use.
-	 */
-	NIC_WV_MAX
-} nic_wv_type_t;
-
-/**
- * Specifies the interrupt/polling mode used by the driver and NIC
- */
-typedef enum nic_poll_mode {
-	/**
-	 * NIC issues interrupts upon events.
-	 */
-	NIC_POLL_IMMEDIATE,
-	/**
-	 * Some uspace app calls nic_poll_now(...) in order to check the NIC state
-	 * - no interrupts are received from the NIC.
-	 */
-	NIC_POLL_ON_DEMAND,
-	/**
-	 * The driver itself issues a poll request in a periodic manner. It is
-	 * allowed to use hardware timer if the NIC supports it.
-	 */
-	NIC_POLL_PERIODIC,
-	/**
-	 * The driver itself issued a poll request in a periodic manner. The driver
-	 * must create software timer, internal hardware timer of NIC must not be
-	 * used even if the NIC supports it.
-	 */
-	NIC_POLL_SOFTWARE_PERIODIC
-} nic_poll_mode_t;
-
-/**
- * Says if this virtue type is a multi-virtue (there can be multiple virtues of
- * this type at once).
- *
- * @param type
- *
- * @return true or false
- */
-static inline int nic_wv_is_multi(nic_wv_type_t type)
-{
-	switch (type) {
-	case NIC_WV_FULL_MATCH:
-	case NIC_WV_DESTINATION:
-	case NIC_WV_DIRECTED_IPV4:
-	case NIC_WV_DIRECTED_IPV6:
-		return true;
-	default:
-		return false;
-	}
-}
-
-static inline const char *nic_device_state_to_string(nic_device_state_t state)
-{
-	switch (state) {
-	case NIC_STATE_STOPPED:
-		return "stopped";
-	case NIC_STATE_DOWN:
-		return "down";
-	case NIC_STATE_ACTIVE:
-		return "active";
-	default:
-		return "undefined";
-	}
-}
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/pci.h
===================================================================
--- uspace/lib/c/include/pci.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,51 +1,0 @@
-/*
- * Copyright (c) 2019 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_PCI_H_
-#define _LIBC_PCI_H_
-
-#include <errno.h>
-#include <ipc/devman.h>
-#include <loc.h>
-#include <types/pci.h>
-
-extern errno_t pci_open(service_id_t, pci_t **);
-extern void pci_close(pci_t *);
-extern errno_t pci_get_devices(pci_t *, devman_handle_t **, size_t *);
-extern errno_t pci_dev_get_info(pci_t *, devman_handle_t, pci_dev_info_t *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/types/inet.h
===================================================================
--- uspace/lib/c/include/types/inet.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,66 +1,0 @@
-/*
- * 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 <stddef.h>
-#include <stdint.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 {
-	errno_t (*recv)(inet_dgram_t *);
-} inet_ev_ops_t;
-
-typedef enum {
-	INET_DF = 1
-} inet_df_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/types/inet/host.h
===================================================================
--- uspace/lib/c/include/types/inet/host.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,67 +1,0 @@
-/*
- * Copyright (c) 2016 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_HOST_H_
-#define _LIBC_TYPES_INET_HOST_H_
-
-#include <inet/addr.h>
-
-typedef enum {
-	/** Host name */
-	inet_host_name,
-	/** Host address */
-	inet_host_addr
-} inet_host_form_t;
-
-/** Internet host:port specification
- *
- * As in RFC 1738 Uniform Resouce Locators (URL) and RFC 2732 Format for
- * literal IPv6 Addresses in URLs
- */
-typedef struct {
-	/** Host form */
-	inet_host_form_t hform;
-
-	union {
-		/** Host name */
-		char *name;
-		/** Host address */
-		inet_addr_t addr;
-	} host;
-} inet_host_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/types/inet/hostport.h
===================================================================
--- uspace/lib/c/include/types/inet/hostport.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,65 +1,0 @@
-/*
- * Copyright (c) 2016 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_HOSTPORT_H_
-#define _LIBC_TYPES_INET_HOSTPORT_H_
-
-#include <inet/addr.h>
-#include <stdint.h>
-#include <types/inet/host.h>
-
-/** Internet host:port specification
- *
- * As in RFC 1738 Uniform Resouce Locators (URL) and RFC 2732 Format for
- * literal IPv6 Addresses in URLs
- */
-typedef struct {
-	/** Host form */
-	inet_host_form_t hform;
-
-	union {
-		/** Host name */
-		char *name;
-		/** Host address */
-		inet_addr_t addr;
-	} host;
-
-	/** Port number of 0 if omitted */
-	uint16_t port;
-} inet_hostport_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/types/inetcfg.h
===================================================================
--- uspace/lib/c/include/types/inetcfg.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,74 +1,0 @@
-/*
- * 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 <stddef.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: pace/lib/c/include/types/inetping.h
===================================================================
--- uspace/lib/c/include/types/inetping.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,55 +1,0 @@
-/*
- * 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 <stddef.h>
-#include <stdint.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: pace/lib/c/include/types/io/chardev.h
===================================================================
--- uspace/lib/c/include/types/io/chardev.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,46 +1,0 @@
-/*
- * Copyright (c) 2019 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
- * @{
- */
-
-#ifndef _LIBC_TYPES_IO_CHARDEV_H_
-#define _LIBC_TYPES_IO_CHARDEV_H_
-
-/** Chardev read/write operation flags */
-typedef enum {
-	/** No flags */
-	chardev_f_none = 0,
-	/** Do not block even if no bytes can be transferred */
-	chardev_f_nonblock = 0x1
-} chardev_flags_t;
-
-#endif
-/**
- * @}
- */
Index: pace/lib/c/include/types/label.h
===================================================================
--- uspace/lib/c/include/types/label.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,133 +1,0 @@
-/*
- * Copyright (c) 2015 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_LABEL_H_
-#define _LIBC_TYPES_LABEL_H_
-
-#include <types/uuid.h>
-
-/** Partition contents */
-typedef enum {
-	/** Partition is considered empty */
-	ptc_empty = 0,
-	/** Partition contains a recognized filesystem */
-	ptc_fs,
-	/** Partition contains unrecognized data */
-	ptc_unknown
-} label_part_cnt_t;
-
-/** Disk label type */
-typedef enum {
-	/** No label */
-	lt_none,
-	/** BIOS Master Boot Record */
-	lt_mbr,
-	/** UEFI GUID Partition Table */
-	lt_gpt
-} label_type_t;
-
-#define LT_FIRST lt_mbr
-#define LT_LIMIT (lt_gpt + 1)
-
-#define LT_DEFAULT lt_mbr
-
-/** Partition kind */
-typedef enum {
-	/** Primary partition */
-	lpk_primary,
-	/** Extended partition */
-	lpk_extended,
-	/** Logical partition */
-	lpk_logical
-} label_pkind_t;
-
-/** Label flags */
-typedef enum {
-	/** Label supports extended (and logical) partitions */
-	lf_ext_supp = 0x1,
-	/** Partition type is in UUID format (otherwise in small number format) */
-	lf_ptype_uuid = 0x2,
-	/** Currently it is possible to create a primary partition */
-	lf_can_create_pri = 0x4,
-	/** Currently it is possible to create an extended partition */
-	lf_can_create_ext = 0x8,
-	/** Currently it is possible to create a logical partition */
-	lf_can_create_log = 0x10,
-	/** Currently it is possible to delete a partition */
-	lf_can_delete_part = 0x20,
-	/** Currently it is possible to modify a partition */
-	lf_can_modify_part = 0x40
-} label_flags_t;
-
-/** Partition type format */
-typedef enum {
-	/** Small number */
-	lptf_num,
-	/** UUID */
-	lptf_uuid
-} label_pt_fmt;
-
-/** Partition type */
-typedef struct {
-	/** Type format */
-	label_pt_fmt fmt;
-	/** Depending on @c fmt */
-	union {
-		/* Small number */
-		uint8_t num;
-		/** UUID */
-		uuid_t uuid;
-	} t;
-} label_ptype_t;
-
-/** Partition content (used to get partition type suggestion) */
-typedef enum {
-	/** ExFAT */
-	lpc_exfat,
-	/** Ext4 */
-	lpc_ext4,
-	/** FAT12 or FAT16 */
-	lpc_fat12_16,
-	/** FAT32 */
-	lpc_fat32,
-	/** Minix file system */
-	lpc_minix
-} label_pcnt_t;
-
-#define LPC_LIMIT (lpc_minix + 1)
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/types/pci.h
===================================================================
--- uspace/lib/c/include/types/pci.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,67 +1,0 @@
-/*
- * Copyright (c) 2019 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_PCI_H_
-#define _LIBC_TYPES_PCI_H_
-
-#include <async.h>
-#include <ipc/devman.h>
-#include <stdint.h>
-
-/** PCI device information */
-typedef struct {
-	/** Devman function handle */
-	devman_handle_t dev_handle;
-	/** Bus number */
-	uint8_t bus_num;
-	/** Device number */
-	uint8_t dev_num;
-	/** Function number */
-	uint8_t fn_num;
-	/** Vendor ID */
-	uint16_t vendor_id;
-	/** Device ID */
-	uint16_t device_id;
-} pci_dev_info_t;
-
-/** PCI service */
-typedef struct {
-	/** Session with PCI control service */
-	async_sess_t *sess;
-} pci_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/types/vol.h
===================================================================
--- uspace/lib/c/include/types/vol.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,107 +1,0 @@
-/*
- * Copyright (c) 2015 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_VOL_H_
-#define _LIBC_TYPES_VOL_H_
-
-#include <async.h>
-#include <ipc/vfs.h>
-#include <ipc/vol.h>
-#include <stdbool.h>
-
-typedef struct {
-	sysarg_t id;
-} volume_id_t;
-
-typedef enum {
-	/** Partition is empty */
-	vpc_empty,
-	/** Partition contains a recognized filesystem */
-	vpc_fs,
-	/** Partition contains unrecognized data */
-	vpc_unknown
-} vol_part_cnt_t;
-
-/** File system type */
-typedef enum {
-	fs_exfat,
-	fs_fat,
-	fs_minix,
-	fs_ext4,
-	fs_cdfs
-} vol_fstype_t;
-
-#define VOL_FSTYPE_LIMIT (fs_ext4 + 1)
-#define VOL_FSTYPE_DEFAULT fs_ext4
-
-/** Volume service */
-typedef struct vol {
-	/** Volume service session */
-	async_sess_t *sess;
-} vol_t;
-
-/** Partition information */
-typedef struct {
-	/** Partition content type */
-	vol_part_cnt_t pcnt;
-	/** Filesystem type */
-	vol_fstype_t fstype;
-	/** Volume label */
-	char label[VOL_LABEL_MAXLEN + 1];
-	/** Current mount point */
-	char cur_mp[MAX_PATH_LEN + 1]; /* XXX too big */
-	/** Current mount point is automatic */
-	bool cur_mp_auto;
-} vol_part_info_t;
-
-/** Volume configuration information */
-typedef struct {
-	/** Volume identifier */
-	volume_id_t id;
-	/** Volume label */
-	char label[VOL_LABEL_MAXLEN + 1];
-	/** Mount path */
-	char path[MAX_PATH_LEN + 1]; /* XXX too big */
-} vol_info_t;
-
-/** Volume label support */
-typedef struct {
-	/** Volume labels are supported */
-	bool supported;
-} vol_label_supp_t;
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/vbd.h
===================================================================
--- uspace/lib/c/include/vbd.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,116 +1,0 @@
-/*
- * Copyright (c) 2015 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_VBD_H_
-#define _LIBC_VBD_H_
-
-#include <async.h>
-#include <loc.h>
-#include <types/label.h>
-#include <offset.h>
-
-/** VBD service */
-typedef struct vbd {
-	/** VBD session */
-	async_sess_t *sess;
-} vbd_t;
-
-/** Disk information */
-typedef struct {
-	/** Label type */
-	label_type_t ltype;
-	/** Label flags */
-	label_flags_t flags;
-	/** First block that can be allocated */
-	aoff64_t ablock0;
-	/** Number of blocks that can be allocated */
-	aoff64_t anblocks;
-	/** Block size */
-	size_t block_size;
-	/** Total number of blocks */
-	aoff64_t nblocks;
-} vbd_disk_info_t;
-
-/** Specification of new partition */
-typedef struct {
-	/** Partition index */
-	int index;
-	/** First block */
-	aoff64_t block0;
-	/** Number of blocks */
-	aoff64_t nblocks;
-	/** Number of header blocks (EBR for logical partitions) */
-	aoff64_t hdr_blocks;
-	/** Partition kind */
-	label_pkind_t pkind;
-	/** Partition type */
-	label_ptype_t ptype;
-} vbd_part_spec_t;
-
-/** Partition info */
-typedef struct {
-	/** Partition index */
-	int index;
-	/** Partition kind */
-	label_pkind_t pkind;
-	/** First block */
-	aoff64_t block0;
-	/** Number of blocks */
-	aoff64_t nblocks;
-	/** Service ID */
-	service_id_t svc_id;
-} vbd_part_info_t;
-
-typedef sysarg_t vbd_part_id_t;
-
-extern errno_t vbd_create(vbd_t **);
-extern void vbd_destroy(vbd_t *);
-extern errno_t vbd_get_disks(vbd_t *, service_id_t **, size_t *);
-extern errno_t vbd_disk_info(vbd_t *, service_id_t, vbd_disk_info_t *);
-extern errno_t vbd_label_create(vbd_t *, service_id_t, label_type_t);
-extern errno_t vbd_label_delete(vbd_t *, service_id_t);
-extern errno_t vbd_label_get_parts(vbd_t *, service_id_t, service_id_t **,
-    size_t *);
-extern errno_t vbd_part_get_info(vbd_t *, vbd_part_id_t, vbd_part_info_t *);
-extern errno_t vbd_part_create(vbd_t *, service_id_t, vbd_part_spec_t *,
-    vbd_part_id_t *);
-extern errno_t vbd_part_delete(vbd_t *, vbd_part_id_t);
-extern void vbd_pspec_init(vbd_part_spec_t *);
-extern errno_t vbd_suggest_ptype(vbd_t *, service_id_t, label_pcnt_t,
-    label_ptype_t *);
-
-#endif
-
-/** @}
- */
Index: pace/lib/c/include/vol.h
===================================================================
--- uspace/lib/c/include/vol.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,67 +1,0 @@
-/*
- * Copyright (c) 2015 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_VOL_H_
-#define _LIBC_VOL_H_
-
-#include <async.h>
-#include <errno.h>
-#include <loc.h>
-#include <stdint.h>
-#include <types/label.h>
-#include <types/vol.h>
-
-extern errno_t vol_create(vol_t **);
-extern void vol_destroy(vol_t *);
-extern errno_t vol_get_parts(vol_t *, service_id_t **, size_t *);
-extern errno_t vol_part_add(vol_t *, service_id_t);
-extern errno_t vol_part_info(vol_t *, service_id_t, vol_part_info_t *);
-extern errno_t vol_part_eject(vol_t *, service_id_t);
-extern errno_t vol_part_empty(vol_t *, service_id_t);
-extern errno_t vol_part_insert(vol_t *, service_id_t);
-extern errno_t vol_part_insert_by_path(vol_t *, const char *);
-extern errno_t vol_part_get_lsupp(vol_t *, vol_fstype_t, vol_label_supp_t *);
-extern errno_t vol_part_mkfs(vol_t *, service_id_t, vol_fstype_t, const char *,
-    const char *);
-extern errno_t vol_part_set_mountp(vol_t *, service_id_t, const char *);
-extern errno_t vol_get_volumes(vol_t *, volume_id_t **, size_t *);
-extern errno_t vol_info(vol_t *, volume_id_t, vol_info_t *);
-extern errno_t vol_fstype_format(vol_fstype_t, char **);
-extern errno_t vol_pcnt_fs_format(vol_part_cnt_t, vol_fstype_t, char **);
-extern errno_t vol_mountp_validate(const char *);
-
-#endif
-
-/** @}
- */
Index: uspace/lib/c/meson.build
===================================================================
--- uspace/lib/c/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -60,8 +60,6 @@
 src += files(
 	'generic/libc.c',
+	'generic/as.c',
 	'generic/ddi.c',
-	'generic/as.c',
-	'generic/bd.c',
-	'generic/bd_srv.c',
 	'generic/perm.c',
 	'generic/capa.c',
@@ -71,13 +69,9 @@
 	'generic/corecfg.c',
 	'generic/ctype.c',
-	'generic/devman.c',
+	'generic/device/clock_dev.c',
 	'generic/device/hw_res.c',
 	'generic/device/hw_res_parsed.c',
 	'generic/device/pio_window.c',
-	'generic/device/clock_dev.c',
-	'generic/device/led_dev.c',
 	'generic/dirent.c',
-	'generic/dhcp.c',
-	'generic/dnsr.c',
 	'generic/dlfcn.c',
 	'generic/elf/elf.c',
@@ -97,22 +91,11 @@
 	'generic/l18n/langs.c',
 	'generic/pcb.c',
+	'generic/pio_trace.c',
 	'generic/smc.c',
 	'generic/task.c',
 	'generic/imath.c',
-	'generic/inet/addr.c',
-	'generic/inet/endpoint.c',
-	'generic/inet/host.c',
-	'generic/inet/hostname.c',
-	'generic/inet/hostport.c',
-	'generic/inet/tcp.c',
-	'generic/inet/udp.c',
-	'generic/inet.c',
-	'generic/inetcfg.c',
-	'generic/inetping.c',
 	'generic/io/asprintf.c',
 	'generic/io/input.c',
 	'generic/io/io.c',
-	'generic/io/chardev.c',
-	'generic/io/chardev_srv.c',
 	'generic/io/chargrid.c',
 	'generic/io/output.c',
@@ -120,8 +103,6 @@
 	'generic/io/log.c',
 	'generic/io/logctl.c',
-	'generic/io/label.c',
 	'generic/io/kio.c',
 	'generic/io/klog.c',
-	'generic/io/serial.c',
 	'generic/io/snprintf.c',
 	'generic/io/vprintf.c',
@@ -131,7 +112,4 @@
 	'generic/io/console.c',
 	'generic/io/table.c',
-	'generic/iplink.c',
-	'generic/iplink_srv.c',
-	'generic/irc.c',
 	'generic/irq.c',
 	'generic/ieee_double.c',
@@ -181,11 +159,7 @@
 	'generic/assert.c',
 	'generic/bsearch.c',
-	'generic/pci.c',
-	'generic/pio_trace.c',
 	'generic/qsort.c',
 	'generic/ubsan.c',
 	'generic/uuid.c',
-	'generic/vbd.c',
-	'generic/vol.c',
 )
 
Index: uspace/lib/c/test/stdio/scanf.c
===================================================================
--- uspace/lib/c/test/stdio/scanf.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/test/stdio/scanf.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -317,5 +317,5 @@
 }
 
-/** Decimal ptrdiff_t-sized unsigned int*/
+/** Decimal ptrdiff_t-sized unsigned int */
 PCUT_TEST(dec_unsigned_ptrdiff)
 {
Index: uspace/lib/c/test/uuid.c
===================================================================
--- uspace/lib/c/test/uuid.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/c/test/uuid.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -42,5 +42,5 @@
 	"69C7DB62-8309-4C58-831B-8C4E4161E8AC",
 
-	/* lower case*/
+	/* lower case */
 	"c511bf24-70cb-422e-933b-2a74ab699a56",
 	"7b1abd05-456f-4661-ab62-917685069343",
Index: uspace/lib/clui/tinput.c
===================================================================
--- uspace/lib/clui/tinput.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/clui/tinput.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -874,4 +874,6 @@
 errno_t tinput_read_i(tinput_t *ti, const char *istr, char **dstr)
 {
+	errno_t rc;
+
 	console_flush(ti->console);
 	if (console_get_size(ti->console, &ti->con_cols, &ti->con_rows) != EOK)
@@ -891,5 +893,6 @@
 
 		cons_event_t ev;
-		if (!console_get_event(ti->console, &ev))
+		rc = console_get_event(ti->console, &ev);
+		if (rc != EOK)
 			return EIO;
 
Index: uspace/lib/congfx/private/console.h
===================================================================
--- uspace/lib/congfx/private/console.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/congfx/private/console.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,4 +39,5 @@
 
 #include <gfx/context.h>
+#include <io/charfield.h>
 #include <io/console.h>
 #include <io/pixel.h>
@@ -54,8 +55,12 @@
 	/** Console bounding rectangle */
 	gfx_rect_t rect;
+	/** Clipping rectangle */
+	gfx_rect_t clip_rect;
 	/** File for printing characters */
 	FILE *fout;
 	/** Current drawing color */
 	pixel_t clr;
+	/** Shared console buffer */
+	charfield_t *buf;
 };
 
Index: uspace/lib/congfx/src/console.c
===================================================================
--- uspace/lib/congfx/src/console.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/congfx/src/console.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -48,6 +48,8 @@
 #include "../private/color.h"
 
+static errno_t console_gc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t console_gc_set_color(void *, gfx_color_t *);
 static errno_t console_gc_fill_rect(void *, gfx_rect_t *);
+static errno_t console_gc_update(void *);
 static errno_t console_gc_bitmap_create(void *, gfx_bitmap_params_t *,
     gfx_bitmap_alloc_t *, void **);
@@ -55,14 +57,41 @@
 static errno_t console_gc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
 static errno_t console_gc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
+static errno_t console_gc_cursor_get_pos(void *, gfx_coord2_t *);
+static errno_t console_gc_cursor_set_pos(void *, gfx_coord2_t *);
+static errno_t console_gc_cursor_set_visible(void *, bool);
 
 gfx_context_ops_t console_gc_ops = {
+	.set_clip_rect = console_gc_set_clip_rect,
 	.set_color = console_gc_set_color,
 	.fill_rect = console_gc_fill_rect,
+	.update = console_gc_update,
 	.bitmap_create = console_gc_bitmap_create,
 	.bitmap_destroy = console_gc_bitmap_destroy,
 	.bitmap_render = console_gc_bitmap_render,
-	.bitmap_get_alloc = console_gc_bitmap_get_alloc
+	.bitmap_get_alloc = console_gc_bitmap_get_alloc,
+	.cursor_get_pos = console_gc_cursor_get_pos,
+	.cursor_set_pos = console_gc_cursor_set_pos,
+	.cursor_set_visible = console_gc_cursor_set_visible
 };
 
+/** Set clipping rectangle on console GC.
+ *
+ * @param arg Console GC
+ * @param rect Rectangle
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t console_gc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	console_gc_t *cgc = (console_gc_t *) arg;
+
+	if (rect != NULL)
+		gfx_rect_clip(rect, &cgc->rect, &cgc->clip_rect);
+	else
+		cgc->clip_rect = cgc->rect;
+
+	return EOK;
+}
+
 /** Set color on console GC.
  *
@@ -92,23 +121,48 @@
 {
 	console_gc_t *cgc = (console_gc_t *) arg;
-	int rv;
 	gfx_coord_t x, y;
-
-	// XXX We should handle p0.x > p1.x and p0.y > p1.y
-
-	console_set_rgb_color(cgc->con, cgc->clr, cgc->clr);
-
-	for (y = rect->p0.y; y < rect->p1.y; y++) {
-		console_set_pos(cgc->con, rect->p0.x, y);
-
-		for (x = rect->p0.x; x < rect->p1.x; x++) {
-			rv = fputc('X', cgc->fout);
-			if (rv < 0)
-				return EIO;
+	gfx_coord_t cols;
+	gfx_rect_t crect;
+	charfield_t ch;
+
+	/* Make sure rectangle is clipped and sorted */
+	gfx_rect_clip(rect, &cgc->clip_rect, &crect);
+
+	cols = cgc->rect.p1.x - cgc->rect.p0.x;
+
+	ch.ch = cgc->clr >> 24;
+	ch.flags = CHAR_FLAG_DIRTY;
+	ch.attrs.type = CHAR_ATTR_RGB;
+	ch.attrs.val.rgb.fgcolor = cgc->clr ^ 0xffffff;
+	ch.attrs.val.rgb.bgcolor = cgc->clr;
+
+	for (y = crect.p0.y; y < crect.p1.y; y++) {
+		for (x = crect.p0.x; x < crect.p1.x; x++) {
+			cgc->buf[y * cols + x] = ch;
 		}
-
-		console_flush(cgc->con);
 	}
 
+	console_update(cgc->con, crect.p0.x, crect.p0.y,
+	    crect.p1.x, crect.p1.y);
+
+	return EOK;
+}
+
+/** Update console GC.
+ *
+ * @param arg Console GC
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t console_gc_update(void *arg)
+{
+	console_gc_t *cgc = (console_gc_t *) arg;
+
+	/*
+	 * XXX Before actually deferring update to here (and similarly other
+	 * GC implementations) need to make sure all consumers properly
+	 * call update.
+	 */
+	(void) cgc;
 	return EOK;
 }
@@ -131,4 +185,5 @@
 	sysarg_t rows;
 	sysarg_t cols;
+	charfield_t *buf = NULL;
 	errno_t rc;
 
@@ -140,4 +195,10 @@
 
 	rc = console_get_size(con, &cols, &rows);
+	if (rc != EOK)
+		goto error;
+
+	console_clear(con);
+
+	rc = console_map(con, cols, rows, &buf);
 	if (rc != EOK)
 		goto error;
@@ -153,9 +214,13 @@
 	cgc->rect.p0.y = 0;
 	cgc->rect.p1.x = cols;
-	cgc->rect.p1.y = rows - 1; /* make sure we avoid bottom-right corner */
+	cgc->rect.p1.y = rows;
+	cgc->clip_rect = cgc->rect;
+	cgc->buf = buf;
 
 	*rgc = cgc;
 	return EOK;
 error:
+	if (buf != NULL)
+		console_unmap(cgc->con, buf);
 	if (cgc != NULL)
 		free(cgc);
@@ -175,4 +240,7 @@
 	if (rc != EOK)
 		return rc;
+
+	console_clear(cgc->con);
+	console_unmap(cgc->con, cgc->buf);
 
 	free(cgc);
@@ -268,6 +336,6 @@
 	console_gc_bitmap_t *cbm = (console_gc_bitmap_t *)bm;
 	gfx_coord_t x, y;
-	int rv;
 	pixel_t clr;
+	charfield_t ch;
 	pixelmap_t pixelmap;
 	gfx_rect_t srect;
@@ -275,4 +343,5 @@
 	gfx_rect_t crect;
 	gfx_coord2_t offs;
+	gfx_coord_t cols;
 
 	if (srect0 != NULL)
@@ -289,5 +358,5 @@
 
 	gfx_rect_translate(&offs, &srect, &drect);
-	gfx_rect_clip(&drect, &cbm->cgc->rect, &crect);
+	gfx_rect_clip(&drect, &cbm->cgc->clip_rect, &crect);
 
 	pixelmap.width = cbm->rect.p1.x - cbm->rect.p0.x;
@@ -295,20 +364,21 @@
 	pixelmap.data = cbm->alloc.pixels;
 
+	cols = cbm->cgc->rect.p1.x - cbm->cgc->rect.p0.x;
+
 	if ((cbm->flags & bmpf_color_key) == 0) {
 		/* Simple copy */
 		for (y = crect.p0.y; y < crect.p1.y; y++) {
-			console_set_pos(cbm->cgc->con, crect.p0.x, y);
-
 			for (x = crect.p0.x; x < crect.p1.x; x++) {
 				clr = pixelmap_get_pixel(&pixelmap,
 				    x - offs.x - cbm->rect.p0.x,
 				    y - offs.y - cbm->rect.p0.y);
-				console_set_rgb_color(cbm->cgc->con, clr, clr);
-
-				rv = fputc('X', cbm->cgc->fout);
-				if (rv < 0)
-					return EIO;
-
-				console_flush(cbm->cgc->con);
+
+				ch.ch = clr >> 24;
+				ch.flags = CHAR_FLAG_DIRTY;
+				ch.attrs.type = CHAR_ATTR_RGB;
+				ch.attrs.val.rgb.fgcolor = clr ^ 0xffffff;
+				ch.attrs.val.rgb.bgcolor = clr;
+
+				cbm->cgc->buf[y * cols + x] = ch;
 			}
 		}
@@ -321,42 +391,38 @@
 				    x - offs.x - cbm->rect.p0.x,
 				    y - offs.y - cbm->rect.p0.y);
-				console_set_rgb_color(cbm->cgc->con, clr, clr);
-
-				if (clr != cbm->key_color) {
-					console_set_pos(cbm->cgc->con, x, y);
-					rv = fputc('X', cbm->cgc->fout);
-					if (rv < 0)
-						return EIO;
-
-					console_flush(cbm->cgc->con);
-				}
-
+
+				ch.ch = clr >> 24;
+				ch.flags = CHAR_FLAG_DIRTY;
+				ch.attrs.type = CHAR_ATTR_RGB;
+				ch.attrs.val.rgb.fgcolor = clr ^ 0xffffff;
+				ch.attrs.val.rgb.bgcolor = clr;
+
+				if (clr != cbm->key_color)
+					cbm->cgc->buf[y * cols + x] = ch;
 			}
 		}
 	} else {
 		/* Color key & colorize */
-		console_set_rgb_color(cbm->cgc->con, cbm->cgc->clr,
-		    cbm->cgc->clr);
+		ch.ch = 0;
+		ch.flags = CHAR_FLAG_DIRTY;
+		ch.attrs.type = CHAR_ATTR_RGB;
+		ch.attrs.val.rgb.fgcolor = cbm->cgc->clr;
+		ch.attrs.val.rgb.bgcolor = cbm->cgc->clr;
 
 		for (y = crect.p0.y; y < crect.p1.y; y++) {
 			for (x = crect.p0.x; x < crect.p1.x; x++) {
-
 				clr = pixelmap_get_pixel(&pixelmap,
 				    x - offs.x - cbm->rect.p0.x,
 				    y - offs.y - cbm->rect.p0.y);
 
-				if (clr != cbm->key_color) {
-					console_set_pos(cbm->cgc->con, x, y);
-					rv = fputc('X', cbm->cgc->fout);
-					if (rv < 0)
-						return EIO;
-
-					console_flush(cbm->cgc->con);
-				}
-
+				if (clr != cbm->key_color)
+					cbm->cgc->buf[y * cols + x] = ch;
 			}
 		}
 	}
 
+	console_update(cbm->cgc->con, crect.p0.x, crect.p0.y, crect.p1.x,
+	    crect.p1.y);
+
 	return EOK;
 }
@@ -375,4 +441,57 @@
 }
 
+/** Get cursor position on console GC.
+ *
+ * @param arg Console GC
+ * @param pos Place to store position
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t console_gc_cursor_get_pos(void *arg, gfx_coord2_t *pos)
+{
+	console_gc_t *cgc = (console_gc_t *) arg;
+	sysarg_t col;
+	sysarg_t row;
+	errno_t rc;
+
+	rc = console_get_pos(cgc->con, &col, &row);
+	if (rc != EOK)
+		return rc;
+
+	pos->x = col;
+	pos->y = row;
+	return EOK;
+}
+
+/** Set cursor position on console GC.
+ *
+ * @param arg Console GC
+ * @param pos New cursor position
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t console_gc_cursor_set_pos(void *arg, gfx_coord2_t *pos)
+{
+	console_gc_t *cgc = (console_gc_t *) arg;
+
+	console_set_pos(cgc->con, pos->x, pos->y);
+	return EOK;
+}
+
+/** Set cursor visibility on console GC.
+ *
+ * @param arg Console GC
+ * @param visible @c true iff cursor should be made visible
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t console_gc_cursor_set_visible(void *arg, bool visible)
+{
+	console_gc_t *cgc = (console_gc_t *) arg;
+
+	console_cursor_visibility(cgc->con, visible);
+	return EOK;
+}
+
 /** @}
  */
Index: uspace/lib/ddev/include/ipc/ddev.h
===================================================================
--- uspace/lib/ddev/include/ipc/ddev.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ddev/include/ipc/ddev.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -33,6 +33,6 @@
  */
 
-#ifndef _LIBC_IPC_DDEV_H_
-#define _LIBC_IPC_DDEV_H_
+#ifndef LIBDDEV_IPC_DDEV_H
+#define LIBDDEV_IPC_DDEV_H
 
 #include <ipc/common.h>
Index: uspace/lib/device/include/bd.h
===================================================================
--- uspace/lib/device/include/bd.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/bd.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,57 @@
+/*
+ * 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 libdevice
+ * @{
+ */
+/** @file Block device client interface
+ */
+
+#ifndef LIBDEVICE_BD_H
+#define LIBDEVICE_BD_H
+
+#include <async.h>
+#include <offset.h>
+
+typedef struct {
+	async_sess_t *sess;
+} bd_t;
+
+extern errno_t bd_open(async_sess_t *, bd_t **);
+extern void bd_close(bd_t *);
+extern errno_t bd_read_blocks(bd_t *, aoff64_t, size_t, void *, size_t);
+extern errno_t bd_read_toc(bd_t *, uint8_t, void *, size_t);
+extern errno_t bd_write_blocks(bd_t *, aoff64_t, size_t, const void *, size_t);
+extern errno_t bd_sync_cache(bd_t *, aoff64_t, size_t);
+extern errno_t bd_get_block_size(bd_t *, size_t *);
+extern errno_t bd_get_num_blocks(bd_t *, aoff64_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/bd_srv.h
===================================================================
--- uspace/lib/device/include/bd_srv.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/bd_srv.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,77 @@
+/*
+ * 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_BD_SRV_H
+#define LIBDEVICE_BD_SRV_H
+
+#include <adt/list.h>
+#include <async.h>
+#include <fibril_synch.h>
+#include <stdbool.h>
+#include <offset.h>
+
+typedef struct bd_ops bd_ops_t;
+
+/** Service setup (per sevice) */
+typedef struct {
+	bd_ops_t *ops;
+	void *sarg;
+} bd_srvs_t;
+
+/** Server structure (per client session) */
+typedef struct {
+	bd_srvs_t *srvs;
+	async_sess_t *client_sess;
+	void *carg;
+} bd_srv_t;
+
+struct bd_ops {
+	errno_t (*open)(bd_srvs_t *, bd_srv_t *);
+	errno_t (*close)(bd_srv_t *);
+	errno_t (*read_blocks)(bd_srv_t *, aoff64_t, size_t, void *, size_t);
+	errno_t (*read_toc)(bd_srv_t *, uint8_t, void *, size_t);
+	errno_t (*sync_cache)(bd_srv_t *, aoff64_t, size_t);
+	errno_t (*write_blocks)(bd_srv_t *, aoff64_t, size_t, const void *, size_t);
+	errno_t (*get_block_size)(bd_srv_t *, size_t *);
+	errno_t (*get_num_blocks)(bd_srv_t *, aoff64_t *);
+};
+
+extern void bd_srvs_init(bd_srvs_t *);
+
+extern errno_t bd_conn(ipc_call_t *, bd_srvs_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/device/led_dev.h
===================================================================
--- uspace/lib/device/include/device/led_dev.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/device/led_dev.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_DEVICE_LED_DEV_H
+#define LIBDEVICE_DEVICE_LED_DEV_H
+
+#include <async.h>
+#include <io/pixel.h>
+
+typedef enum {
+	LED_DEV_COLOR_SET = 0
+} led_dev_method_t;
+
+extern errno_t led_dev_color_set(async_sess_t *, pixel_t);
+
+#endif
Index: uspace/lib/device/include/devman.h
===================================================================
--- uspace/lib/device/include/devman.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/devman.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2009 Jiri Svoboda
+ * 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_DEVMAN_H
+#define LIBDEVICE_DEVMAN_H
+
+#include <ipc/devman.h>
+#include <ipc/loc.h>
+#include <async.h>
+#include <stdbool.h>
+
+extern async_exch_t *devman_exchange_begin_blocking(iface_t);
+extern async_exch_t *devman_exchange_begin(iface_t);
+extern void devman_exchange_end(async_exch_t *);
+
+extern errno_t devman_driver_register(const char *);
+extern errno_t devman_add_function(const char *, fun_type_t, match_id_list_t *,
+    devman_handle_t, devman_handle_t *);
+extern errno_t devman_remove_function(devman_handle_t);
+extern errno_t devman_drv_fun_online(devman_handle_t);
+extern errno_t devman_drv_fun_offline(devman_handle_t);
+
+extern async_sess_t *devman_device_connect(devman_handle_t, unsigned int);
+extern async_sess_t *devman_parent_device_connect(devman_handle_t,
+    unsigned int);
+
+extern errno_t devman_fun_get_handle(const char *, devman_handle_t *,
+    unsigned int);
+extern errno_t devman_fun_get_child(devman_handle_t, devman_handle_t *);
+extern errno_t devman_dev_get_parent(devman_handle_t, devman_handle_t *);
+extern errno_t devman_dev_get_functions(devman_handle_t, devman_handle_t **,
+    size_t *);
+extern errno_t devman_fun_get_match_id(devman_handle_t, size_t, char *, size_t,
+    unsigned int *);
+extern errno_t devman_fun_get_name(devman_handle_t, char *, size_t);
+extern errno_t devman_fun_get_driver_name(devman_handle_t, char *, size_t);
+extern errno_t devman_fun_get_path(devman_handle_t, char *, size_t);
+extern errno_t devman_fun_online(devman_handle_t);
+extern errno_t devman_fun_offline(devman_handle_t);
+
+extern errno_t devman_add_device_to_category(devman_handle_t, const char *);
+extern errno_t devman_fun_sid_to_handle(service_id_t, devman_handle_t *);
+extern errno_t devman_get_drivers(devman_handle_t **, size_t *);
+extern errno_t devman_driver_get_devices(devman_handle_t, devman_handle_t **,
+    size_t *);
+extern errno_t devman_driver_get_handle(const char *, devman_handle_t *);
+extern errno_t devman_driver_get_match_id(devman_handle_t, size_t, char *, size_t,
+    unsigned int *);
+extern errno_t devman_driver_get_name(devman_handle_t, char *, size_t);
+extern errno_t devman_driver_get_state(devman_handle_t, driver_state_t *);
+extern errno_t devman_driver_load(devman_handle_t);
+extern errno_t devman_driver_unload(devman_handle_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/ieee80211/ieee80211.h
===================================================================
--- uspace/lib/device/include/ieee80211/ieee80211.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/ieee80211/ieee80211.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015 Jan Kolarik
+ * 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 libdevice
+ *  @{
+ */
+
+/** @file ieee80211.h
+ *  IEEE 802.11 common definitions.
+ */
+
+#ifndef LIBDEVICE_IEEE80211_H
+#define LIBDEVICE_IEEE80211_H
+
+#include <adt/list.h>
+#include <nic/nic.h>
+#include <time.h>
+
+/** Max length of scan results array. */
+#define IEEE80211_MAX_RESULTS_LENGTH  32
+
+/** Max SSID length including null character. */
+#define IEEE80211_MAX_SSID_LENGTH  35
+
+/** WiFi security authentication method indicator. */
+typedef enum {
+	IEEE80211_SECURITY_OPEN,
+	IEEE80211_SECURITY_WEP,
+	IEEE80211_SECURITY_WPA,
+	IEEE80211_SECURITY_WPA2
+} ieee80211_security_type_t;
+
+/** WiFi security suite indicator. */
+typedef enum {
+	IEEE80211_SECURITY_SUITE_WEP40,
+	IEEE80211_SECURITY_SUITE_WEP104,
+	IEEE80211_SECURITY_SUITE_CCMP,
+	IEEE80211_SECURITY_SUITE_TKIP
+} ieee80211_security_suite_t;
+
+/** WiFi security authentication method indicator. */
+typedef enum {
+	IEEE80211_SECURITY_AUTH_PSK,
+	IEEE80211_SECURITY_AUTH_8021X
+} ieee80211_security_auth_t;
+
+/** Structure for indicating security network security settings. */
+typedef struct {
+	int type;
+	int group_alg;
+	int pair_alg;
+	int auth;
+} ieee80211_security_t;
+
+/** Structure with scan result info. */
+typedef struct {
+	nic_address_t bssid;
+	char ssid[IEEE80211_MAX_SSID_LENGTH];
+	uint8_t channel;
+	ieee80211_security_t security;
+} ieee80211_scan_result_t;
+
+/** Array of scan results info. */
+typedef struct {
+	uint8_t length;
+	ieee80211_scan_result_t results[IEEE80211_MAX_RESULTS_LENGTH];
+} ieee80211_scan_results_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/io/chardev.h
===================================================================
--- uspace/lib/device/include/io/chardev.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/io/chardev.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2017 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 libdevice
+ * @{
+ */
+
+#ifndef LIBDEVICE_IO_CHARDEV_H
+#define LIBDEVICE_IO_CHARDEV_H
+
+#include <async.h>
+#include <stddef.h>
+#include <types/io/chardev.h>
+
+typedef struct {
+	async_sess_t *sess;
+} chardev_t;
+
+extern errno_t chardev_open(async_sess_t *, chardev_t **);
+extern void chardev_close(chardev_t *);
+extern errno_t chardev_read(chardev_t *, void *, size_t, size_t *,
+    chardev_flags_t);
+extern errno_t chardev_write(chardev_t *, const void *, size_t, size_t *);
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/device/include/io/chardev_srv.h
===================================================================
--- uspace/lib/device/include/io/chardev_srv.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/io/chardev_srv.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2014 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_IO_CHARDEV_SRV_H
+#define LIBDEVICE_IO_CHARDEV_SRV_H
+
+#include <adt/list.h>
+#include <async.h>
+#include <fibril_synch.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <types/io/chardev.h>
+
+typedef struct chardev_ops chardev_ops_t;
+
+/** Service setup (per sevice) */
+typedef struct {
+	chardev_ops_t *ops;
+	void *sarg;
+} chardev_srvs_t;
+
+/** Server structure (per client session) */
+typedef struct {
+	chardev_srvs_t *srvs;
+	void *carg;
+} chardev_srv_t;
+
+struct chardev_ops {
+	errno_t (*open)(chardev_srvs_t *, chardev_srv_t *);
+	errno_t (*close)(chardev_srv_t *);
+	errno_t (*read)(chardev_srv_t *, void *, size_t, size_t *,
+	    chardev_flags_t);
+	errno_t (*write)(chardev_srv_t *, const void *, size_t, size_t *);
+	void (*def_handler)(chardev_srv_t *, ipc_call_t *);
+};
+
+extern void chardev_srvs_init(chardev_srvs_t *);
+
+extern errno_t chardev_conn(ipc_call_t *, chardev_srvs_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/io/label.h
===================================================================
--- uspace/lib/device/include/io/label.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/io/label.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_IO_LABEL_H
+#define LIBDEVICE_IO_LABEL_H
+
+#include <types/label.h>
+
+extern int label_type_format(label_type_t, char **);
+extern int label_pkind_format(label_pkind_t, char **);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/io/serial.h
===================================================================
--- uspace/lib/device/include/io/serial.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/io/serial.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2017 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_IO_SERIAL_H
+#define LIBDEVICE_IO_SERIAL_H
+
+#include <async.h>
+#include <ipc/serial_ctl.h>
+
+typedef struct {
+	async_sess_t *sess;
+} serial_t;
+
+extern errno_t serial_open(async_sess_t *, serial_t **);
+extern void serial_close(serial_t *);
+extern errno_t serial_set_comm_props(serial_t *, unsigned, serial_parity_t,
+    unsigned, unsigned);
+extern errno_t serial_get_comm_props(serial_t *, unsigned *, serial_parity_t *,
+    unsigned *, unsigned *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/ipc/adb.h
===================================================================
--- uspace/lib/device/include/ipc/adb.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/ipc/adb.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2010 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 libdevice
+ * @{
+ */
+/** @file
+ * @brief ADB device interface.
+ */
+
+#ifndef LIBDEVICE_IPC_ADB_H
+#define LIBDEVICE_IPC_ADB_H
+
+#include <ipc/common.h>
+
+typedef enum {
+	ADB_REG_WRITE = IPC_FIRST_USER_METHOD
+} adb_request_t;
+
+typedef enum {
+	ADB_REG_NOTIF = IPC_FIRST_USER_METHOD
+} adb_notif_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/ipc/bd.h
===================================================================
--- uspace/lib/device/include/ipc/bd.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/ipc/bd.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_IPC_BD_H
+#define LIBDEVICE_IPC_BD_H
+
+#include <ipc/common.h>
+
+typedef enum {
+	BD_GET_BLOCK_SIZE = IPC_FIRST_USER_METHOD,
+	BD_GET_NUM_BLOCKS,
+	BD_READ_BLOCKS,
+	BD_SYNC_CACHE,
+	BD_WRITE_BLOCKS,
+	BD_READ_TOC
+} bd_request_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/ipc/chardev.h
===================================================================
--- uspace/lib/device/include/ipc/chardev.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/ipc/chardev.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014 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 libdevice
+ * @{
+ */
+/** @file
+ * @brief Character device interface.
+ */
+
+#ifndef LIBDEVICE_IPC_CHARDEV_H
+#define LIBDEVICE_IPC_CHARDEV_H
+
+#include <ipc/common.h>
+
+typedef enum {
+	CHARDEV_READ = IPC_FIRST_USER_METHOD,
+	CHARDEV_WRITE,
+} chardev_request_t;
+
+enum {
+	CHARDEV_LIMIT = CHARDEV_WRITE + 1
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/ipc/devman.h
===================================================================
--- uspace/lib/device/include/ipc/devman.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/ipc/devman.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,186 @@
+/*
+ * 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
+ * @{
+ */
+
+#ifndef LIBDEVICE_IPC_DEVMAN_H
+#define LIBDEVICE_IPC_DEVMAN_H
+
+#include <ipc/common.h>
+#include <adt/list.h>
+#include <mem.h>
+#include <stdlib.h>
+
+#define DEVMAN_NAME_MAXLEN  256
+
+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 {
+	/** Invalid value for debugging purposes */
+	fun_invalid = 0,
+	/** Function to which child devices attach */
+	fun_inner,
+	/** Fuction exported to external clients (leaf function) */
+	fun_exposed
+} fun_type_t;
+
+/** Ids of device models used for device-to-driver matching.
+ */
+typedef struct match_id {
+	/** Pointers to next and previous ids.
+	 */
+	link_t link;
+	/** Id of device model.
+	 */
+	char *id;
+	/** Relevancy of device-to-driver match.
+	 * The higher is the product of scores specified for the device by the bus driver and by the leaf driver,
+	 * the more suitable is the leaf driver for handling the device.
+	 */
+	unsigned int score;
+} match_id_t;
+
+/** List of ids for matching devices to drivers sorted
+ * according to match scores in descending order.
+ */
+typedef struct match_id_list {
+	list_t ids;
+} match_id_list_t;
+
+static inline match_id_t *create_match_id(void)
+{
+	match_id_t *id = malloc(sizeof(match_id_t));
+	memset(id, 0, sizeof(match_id_t));
+	return id;
+}
+
+static inline void delete_match_id(match_id_t *id)
+{
+	if (id) {
+		if (NULL != id->id) {
+			free(id->id);
+		}
+		free(id);
+	}
+}
+
+static inline void add_match_id(match_id_list_t *ids, match_id_t *id)
+{
+	match_id_t *mid = NULL;
+	link_t *link = ids->ids.head.next;
+
+	while (link != &ids->ids.head) {
+		mid = list_get_instance(link, match_id_t, link);
+		if (mid->score < id->score) {
+			break;
+		}
+		link = link->next;
+	}
+
+	list_insert_before(&id->link, link);
+}
+
+static inline void init_match_ids(match_id_list_t *id_list)
+{
+	list_initialize(&id_list->ids);
+}
+
+static inline void clean_match_ids(match_id_list_t *ids)
+{
+	link_t *link = NULL;
+	match_id_t *id;
+
+	while (!list_empty(&ids->ids)) {
+		link = list_first(&ids->ids);
+		list_remove(link);
+		id = list_get_instance(link, match_id_t, link);
+		delete_match_id(id);
+	}
+}
+
+typedef enum {
+	DEVMAN_DRIVER_REGISTER = IPC_FIRST_USER_METHOD,
+	DEVMAN_ADD_FUNCTION,
+	DEVMAN_ADD_MATCH_ID,
+	DEVMAN_ADD_DEVICE_TO_CATEGORY,
+	DEVMAN_DRV_FUN_ONLINE,
+	DEVMAN_DRV_FUN_OFFLINE,
+	DEVMAN_REMOVE_FUNCTION
+} driver_to_devman_t;
+
+typedef enum {
+	DRIVER_DEV_ADD = IPC_FIRST_USER_METHOD,
+	DRIVER_DEV_REMOVE,
+	DRIVER_DEV_GONE,
+	DRIVER_FUN_ONLINE,
+	DRIVER_FUN_OFFLINE,
+	DRIVER_STOP
+} devman_to_driver_t;
+
+typedef enum {
+	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,
+	DEVMAN_FUN_ONLINE,
+	DEVMAN_FUN_OFFLINE,
+	DEVMAN_FUN_GET_PATH,
+	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,
+	DEVMAN_DRIVER_UNLOAD
+} client_to_devman_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/ipc/irc.h
===================================================================
--- uspace/lib/device/include/ipc/irc.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/ipc/irc.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_IPC_IRC_H
+#define LIBDEVICE_IPC_IRC_H
+
+#include <ipc/common.h>
+
+typedef enum {
+	IRC_ENABLE_INTERRUPT = IPC_FIRST_USER_METHOD,
+	IRC_DISABLE_INTERRUPT,
+	IRC_CLEAR_INTERRUPT
+} irc_request_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/ipc/pci.h
===================================================================
--- uspace/lib/device/include/ipc/pci.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/ipc/pci.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_IPC_PCI_H
+#define LIBDEVICE_IPC_PCI_H
+
+#include <ipc/common.h>
+
+/** PCI control service requests */
+typedef enum {
+	PCI_GET_DEVICES = IPC_FIRST_USER_METHOD,
+	PCI_DEV_GET_INFO
+} pci_request_t;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/device/include/ipc/serial_ctl.h
===================================================================
--- uspace/lib/device/include/ipc/serial_ctl.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/ipc/serial_ctl.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef LIBDEVICE_IPC_SERIAL_CTL_H
+#define LIBDEVICE_IPC_SERIAL_CTL_H
+
+#include <ipc/chardev.h>
+
+/** IPC methods for getting/setting serial communication properties
+ *
+ * 1st IPC arg: baud rate
+ * 2nd IPC arg: parity
+ * 3rd IPC arg: number of bits in one word
+ * 4th IPC arg: number of stop bits
+ *
+ */
+typedef enum {
+	SERIAL_GET_COM_PROPS = CHARDEV_LIMIT,
+	SERIAL_SET_COM_PROPS
+} serial_ctl_t;
+
+typedef enum {
+	SERIAL_NO_PARITY = 0,
+	SERIAL_ODD_PARITY = 1,
+	SERIAL_EVEN_PARITY = 3,
+	SERIAL_MARK_PARITY = 5,
+	SERIAL_SPACE_PARITY = 7
+} serial_parity_t;
+
+#endif
Index: uspace/lib/device/include/ipc/vbd.h
===================================================================
--- uspace/lib/device/include/ipc/vbd.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/ipc/vbd.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015 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 libdevice
+ * @{
+ */
+
+#ifndef LIBDEVICE_IPC_VBD_H
+#define LIBDEVICE_IPC_VBD_H
+
+#include <ipc/common.h>
+
+typedef enum {
+	VBD_GET_DISKS = IPC_FIRST_USER_METHOD,
+	VBD_DISK_INFO,
+	VBD_LABEL_CREATE,
+	VBD_LABEL_DELETE,
+	VBD_LABEL_GET_PARTS,
+	VBD_PART_GET_INFO,
+	VBD_PART_CREATE,
+	VBD_PART_DELETE,
+	VBD_SUGGEST_PTYPE
+} vbd_request_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/ipc/vol.h
===================================================================
--- uspace/lib/device/include/ipc/vol.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/ipc/vol.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015 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 libdevice
+ * @{
+ */
+
+#ifndef LIBDEVICE_IPC_VOL_H
+#define LIBDEVICE_IPC_VOL_H
+
+#include <ipc/common.h>
+
+#define VOL_LABEL_MAXLEN 63
+#define VOL_MOUNTP_MAXLEN 4096
+
+typedef enum {
+	VOL_GET_PARTS = IPC_FIRST_USER_METHOD,
+	VOL_PART_ADD,
+	VOL_PART_INFO,
+	VOL_PART_EJECT,
+	VOL_PART_EMPTY,
+	VOL_PART_INSERT,
+	VOL_PART_INSERT_BY_PATH,
+	VOL_PART_LSUPP,
+	VOL_PART_MKFS,
+	VOL_PART_SET_MOUNTP,
+	VOL_GET_VOLUMES,
+	VOL_INFO
+} vol_request_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/irc.h
===================================================================
--- uspace/lib/device/include/irc.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/irc.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_IRC_H
+#define LIBDEVICE_IRC_H
+
+extern errno_t irc_enable_interrupt(int);
+extern errno_t irc_disable_interrupt(int);
+extern errno_t irc_clear_interrupt(int);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/nic/eth_phys.h
===================================================================
--- uspace/lib/device/include/nic/eth_phys.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/nic/eth_phys.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2011 Radim Vansa
+ * Copyright (c) 2011 Jiri Michalec
+ * 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 LIBDEVICE_NIC_ETH_PHYS_H
+#define LIBDEVICE_NIC_ETH_PHYS_H
+
+#include <stdint.h>
+
+/*
+ * Definitions of possible supported physical layers
+ */
+
+/* Ethernet physical layers */
+#define ETH_OLD          0
+#define ETH_10M          1
+#define ETH_100M         2
+#define ETH_1000M        3
+#define ETH_10G          4
+#define ETH_40G_100G     5
+/* 6, 7 reserved for future use */
+#define ETH_PHYS_LAYERS  8
+
+/* < 10Mbs ethernets */
+#define ETH_EXPERIMENTAL     0x0001
+#define ETH_1BASE5           0x0002
+/* 10Mbs ethernets */
+#define ETH_10BASE5          0x0001
+#define ETH_10BASE2          0x0002
+#define ETH_10BROAD36        0x0004
+#define ETH_STARLAN_10       0x0008
+#define ETH_LATTISNET        0x0010
+#define ETH_10BASE_T         0x0020
+#define ETH_FOIRL            0x0040
+#define ETH_10BASE_FL        0x0080
+#define ETH_10BASE_FB        0x0100
+#define ETH_10BASE_FP        0x0200
+/* 100Mbs (fast) ethernets */
+#define ETH_100BASE_TX       0x0001
+#define ETH_100BASE_T4       0x0002
+#define ETH_100BASE_T2       0x0004
+#define ETH_100BASE_FX       0x0008
+#define ETH_100BASE_SX       0x0010
+#define ETH_100BASE_BX10     0x0020
+#define ETH_100BASE_LX10     0x0040
+#define ETH_100BASE_VG       0x0080
+/* 1000Mbs (gigabit) ethernets */
+#define ETH_1000BASE_T       0x0001
+#define ETH_1000BASE_TX      0x0002
+#define ETH_1000BASE_SX      0x0004
+#define ETH_1000BASE_LX      0x0008
+#define ETH_1000BASE_LH      0x0010
+#define ETH_1000BASE_CX      0x0020
+#define ETH_1000BASE_BX10    0x0040
+#define ETH_1000BASE_LX10    0x0080
+#define ETH_1000BASE_PX10_D  0x0100
+#define ETH_1000BASE_PX10_U  0x0200
+#define ETH_1000BASE_PX20_D  0x0400
+#define ETH_1000BASE_PX20_U  0x0800
+#define ETH_1000BASE_ZX      0x1000
+#define ETH_1000BASE_KX      0x2000
+/* 10Gbs ethernets */
+#define ETH_10GBASE_SR       0x0001
+#define ETH_10GBASE_LX4      0x0002
+#define ETH_10GBASE_LR       0x0004
+#define ETH_10GBASE_ER       0x0008
+#define ETH_10GBASE_SW       0x0010
+#define ETH_10GBASE_LW       0x0020
+#define ETH_10GBASE_EW       0x0040
+#define ETH_10GBASE_CX4      0x0080
+#define ETH_10GBASE_T        0x0100
+#define ETH_10GBASE_LRM      0x0200
+#define ETH_10GBASE_KX4      0x0400
+#define ETH_10GBASE_KR       0x0800
+/* 40Gbs and 100Gbs ethernets */
+#define ETH_40GBASE_SR4      0x0001
+#define ETH_40GBASE_LR4      0x0002
+#define ETH_40GBASE_CR4      0x0004
+#define ETH_40GBASE_KR4      0x0008
+#define ETH_100GBASE_SR10    0x0010
+#define ETH_100GBASE_LR4     0x0020
+#define ETH_100GBASE_ER4     0x0040
+#define ETH_100GBASE_CR10    0x0080
+
+/*
+ * Auto-negotiation advertisement options
+ */
+
+#define ETH_AUTONEG_10BASE_T_HALF    UINT32_C(0x00000001)
+#define ETH_AUTONEG_10BASE_T_FULL    UINT32_C(0x00000002)
+#define ETH_AUTONEG_100BASE_TX_HALF  UINT32_C(0x00000004)
+#define ETH_AUTONEG_100BASE_T4_HALF  UINT32_C(0x00000008)
+#define ETH_AUTONEG_100BASE_T2_HALF  UINT32_C(0x00000010)
+#define ETH_AUTONEG_100BASE_TX_FULL  UINT32_C(0x00000020)
+#define ETH_AUTONEG_100BASE_T2_FULL  UINT32_C(0x00000040)
+#define ETH_AUTONEG_1000BASE_T_HALF  UINT32_C(0x00000080)
+#define ETH_AUTONEG_1000BASE_T_FULL  UINT32_C(0x00000100)
+
+/** Symetric pause packet (802.3x standard) */
+#define ETH_AUTONEG_PAUSE_SYMETRIC   UINT32_C(0x10000000)
+/** Asymetric pause packet (802.3z standard, gigabit ethernet) */
+#define ETH_AUTONEG_PAUSE_ASYMETRIC  UINT32_C(0x20000000)
+
+#define ETH_AUTONEG_MODE_MASK      UINT32_C(0x0FFFFFFF)
+#define ETH_AUTONEG_FEATURES_MASK  UINT32_C(~(ETH_AUTONEG_MODE_MASK))
+
+#define ETH_AUTONEG_MODES  9
+
+struct eth_autoneg_map {
+	uint32_t value;
+	const char *name;
+};
+
+extern const char *ethernet_names[8][17];
+extern const struct eth_autoneg_map ethernet_autoneg_mapping[ETH_AUTONEG_MODES];
+
+#endif
Index: uspace/lib/device/include/nic/nic.h
===================================================================
--- uspace/lib/device/include/nic/nic.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/nic/nic.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2009 Lukas Mejdrech
+ * Copyright (c) 2011 Radim Vansa
+ * 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 libdevice
+ * @{
+ */
+
+/** @file
+ * NIC interface definitions.
+ */
+
+#ifndef LIBDEVICE_NIC_H
+#define LIBDEVICE_NIC_H
+
+#include <nic/eth_phys.h>
+#include <stdbool.h>
+
+/** Ethernet address length. */
+#define ETH_ADDR  6
+
+/** MAC printing format */
+#define PRIMAC  "%02x:%02x:%02x:%02x:%02x:%02x"
+
+/** MAC arguments */
+#define ARGSMAC(__a) \
+	(__a)[0], (__a)[1], (__a)[2], (__a)[3], (__a)[4], (__a)[5]
+
+/* Compare MAC address with specific value */
+#define MAC_EQUALS_VALUE(__a, __a0, __a1, __a2, __a3, __a4, __a5) \
+	((__a)[0] == (__a0) && (__a)[1] == (__a1) && (__a)[2] == (__a2) \
+	&& (__a)[3] == (__a3) && (__a)[4] == (__a4) && (__a)[5] == (__a5))
+
+#define MAC_IS_ZERO(__x) \
+	MAC_EQUALS_VALUE(__x, 0, 0, 0, 0, 0, 0)
+
+/** Max length of any hw nic address (currently only eth) */
+#define NIC_MAX_ADDRESS_LENGTH  16
+
+#define NIC_VENDOR_MAX_LENGTH         64
+#define NIC_MODEL_MAX_LENGTH          64
+#define NIC_PART_NUMBER_MAX_LENGTH    64
+#define NIC_SERIAL_NUMBER_MAX_LENGTH  64
+
+#define NIC_DEFECTIVE_LONG               0x0001
+#define NIC_DEFECTIVE_SHORT              0x0002
+#define NIC_DEFECTIVE_BAD_CRC            0x0010
+#define NIC_DEFECTIVE_BAD_IPV4_CHECKSUM  0x0020
+#define NIC_DEFECTIVE_BAD_IPV6_CHECKSUM  0x0040
+#define NIC_DEFECTIVE_BAD_TCP_CHECKSUM   0x0080
+#define NIC_DEFECTIVE_BAD_UDP_CHECKSUM   0x0100
+
+/**
+ * The bitmap uses single bit for each of the 2^12 = 4096 possible VLAN tags.
+ * This means its size is 4096/8 = 512 bytes.
+ */
+#define NIC_VLAN_BITMAP_SIZE  512
+
+#define NIC_DEVICE_PRINT_FMT  "%x"
+
+/**
+ * Structure covering the MAC address.
+ */
+typedef struct nic_address {
+	uint8_t address[ETH_ADDR];
+} nic_address_t;
+
+/** Device state. */
+typedef enum nic_device_state {
+	/**
+	 * Device present and stopped. Moving device to this state means to discard
+	 * all settings and WOL virtues, rebooting the NIC to state as if the
+	 * computer just booted (or the NIC was just inserted in case of removable
+	 * NIC).
+	 */
+	NIC_STATE_STOPPED,
+	/**
+	 * If the NIC is in this state no packets (frames) are transmitted nor
+	 * received. However, the settings are not restarted. You can use this state
+	 * to temporarily disable transmition/reception or atomically (with respect
+	 * to incoming/outcoming packets) change frames acceptance etc.
+	 */
+	NIC_STATE_DOWN,
+	/** Device is normally operating. */
+	NIC_STATE_ACTIVE,
+	/** Just a constant to limit the state numbers */
+	NIC_STATE_MAX,
+} nic_device_state_t;
+
+/**
+ * Channel operating mode used on the medium.
+ */
+typedef enum {
+	NIC_CM_UNKNOWN,
+	NIC_CM_FULL_DUPLEX,
+	NIC_CM_HALF_DUPLEX,
+	NIC_CM_SIMPLEX
+} nic_channel_mode_t;
+
+/**
+ * Role for the device (used e.g. for 1000Gb ethernet)
+ */
+typedef enum {
+	NIC_ROLE_UNKNOWN,
+	NIC_ROLE_AUTO,
+	NIC_ROLE_MASTER,
+	NIC_ROLE_SLAVE
+} nic_role_t;
+
+/**
+ * Current state of the cable in the device
+ */
+typedef enum {
+	NIC_CS_UNKNOWN,
+	NIC_CS_PLUGGED,
+	NIC_CS_UNPLUGGED
+} nic_cable_state_t;
+
+/**
+ * Result of the requested operation
+ */
+typedef enum {
+	/** Successfully disabled */
+	NIC_RESULT_DISABLED,
+	/** Successfully enabled */
+	NIC_RESULT_ENABLED,
+	/** Not supported at all */
+	NIC_RESULT_NOT_SUPPORTED,
+	/** Temporarily not available */
+	NIC_RESULT_NOT_AVAILABLE,
+	/** Result extensions */
+	NIC_RESULT_FIRST_EXTENSION
+} nic_result_t;
+
+/** Device usage statistics. */
+typedef struct nic_device_stats {
+	/** Total packets received (accepted). */
+	unsigned long receive_packets;
+	/** Total packets transmitted. */
+	unsigned long send_packets;
+	/** Total bytes received (accepted). */
+	unsigned long receive_bytes;
+	/** Total bytes transmitted. */
+	unsigned long send_bytes;
+	/** Bad packets received counter. */
+	unsigned long receive_errors;
+	/** Packet transmition problems counter. */
+	unsigned long send_errors;
+	/** Number of frames dropped due to insufficient space in RX buffers */
+	unsigned long receive_dropped;
+	/** Number of frames dropped due to insufficient space in TX buffers */
+	unsigned long send_dropped;
+	/** Total multicast packets received (accepted). */
+	unsigned long receive_multicast;
+	/** Total broadcast packets received (accepted). */
+	unsigned long receive_broadcast;
+	/** The number of collisions due to congestion on the medium. */
+	unsigned long collisions;
+	/** Unicast packets received but not accepted (filtered) */
+	unsigned long receive_filtered_unicast;
+	/** Multicast packets received but not accepted (filtered) */
+	unsigned long receive_filtered_multicast;
+	/** Broadcast packets received but not accepted (filtered) */
+	unsigned long receive_filtered_broadcast;
+
+	/* detailed receive_errors */
+
+	/** Received packet length error counter. */
+	unsigned long receive_length_errors;
+	/** Receiver buffer overflow counter. */
+	unsigned long receive_over_errors;
+	/** Received packet with crc error counter. */
+	unsigned long receive_crc_errors;
+	/** Received frame alignment error counter. */
+	unsigned long receive_frame_errors;
+	/** Receiver fifo overrun counter. */
+	unsigned long receive_fifo_errors;
+	/** Receiver missed packet counter. */
+	unsigned long receive_missed_errors;
+
+	/* detailed send_errors */
+
+	/** Transmitter aborted counter. */
+	unsigned long send_aborted_errors;
+	/** Transmitter carrier errors counter. */
+	unsigned long send_carrier_errors;
+	/** Transmitter fifo overrun counter. */
+	unsigned long send_fifo_errors;
+	/** Transmitter carrier errors counter. */
+	unsigned long send_heartbeat_errors;
+	/** Transmitter window errors counter. */
+	unsigned long send_window_errors;
+
+	/* for cslip etc */
+
+	/** Total compressed packets received. */
+	unsigned long receive_compressed;
+	/** Total compressed packet transmitted. */
+	unsigned long send_compressed;
+} nic_device_stats_t;
+
+/** Errors corresponding to those in the nic_device_stats_t */
+typedef enum {
+	NIC_SEC_BUFFER_FULL,
+	NIC_SEC_ABORTED,
+	NIC_SEC_CARRIER_LOST,
+	NIC_SEC_FIFO_OVERRUN,
+	NIC_SEC_HEARTBEAT,
+	NIC_SEC_WINDOW_ERROR,
+	/* Error encountered during TX but with other type of error */
+	NIC_SEC_OTHER
+} nic_send_error_cause_t;
+
+/** Errors corresponding to those in the nic_device_stats_t */
+typedef enum {
+	NIC_REC_BUFFER_FULL,
+	NIC_REC_LENGTH,
+	NIC_REC_BUFFER_OVERFLOW,
+	NIC_REC_CRC,
+	NIC_REC_FRAME_ALIGNMENT,
+	NIC_REC_FIFO_OVERRUN,
+	NIC_REC_MISSED,
+	/* Error encountered during RX but with other type of error */
+	NIC_REC_OTHER
+} nic_receive_error_cause_t;
+
+/**
+ * Information about the NIC that never changes - name, vendor, model,
+ * capabilites and so on.
+ */
+typedef struct nic_device_info {
+	/* Device identification */
+	char vendor_name[NIC_VENDOR_MAX_LENGTH];
+	char model_name[NIC_MODEL_MAX_LENGTH];
+	char part_number[NIC_PART_NUMBER_MAX_LENGTH];
+	char serial_number[NIC_SERIAL_NUMBER_MAX_LENGTH];
+	uint16_t vendor_id;
+	uint16_t device_id;
+	uint16_t subsystem_vendor_id;
+	uint16_t subsystem_id;
+	/* Device capabilities */
+	uint16_t ethernet_support[ETH_PHYS_LAYERS];
+
+	/** The mask of all modes which the device can advertise
+	 *
+	 *  see ETH_AUTONEG_ macros in nic/eth_phys.h of libdevice
+	 */
+	uint32_t autoneg_support;
+} nic_device_info_t;
+
+/**
+ * Type of the ethernet frame
+ */
+typedef enum nic_frame_type {
+	NIC_FRAME_UNICAST,
+	NIC_FRAME_MULTICAST,
+	NIC_FRAME_BROADCAST
+} nic_frame_type_t;
+
+/**
+ * Specifies which unicast frames is the NIC receiving.
+ */
+typedef enum nic_unicast_mode {
+	NIC_UNICAST_UNKNOWN,
+	/** No unicast frames are received */
+	NIC_UNICAST_BLOCKED,
+	/** Only the frames with this NIC's MAC as destination are received */
+	NIC_UNICAST_DEFAULT,
+	/**
+	 * Both frames with this NIC's MAC and those specified in the list are
+	 * received
+	 */
+	NIC_UNICAST_LIST,
+	/** All unicast frames are received */
+	NIC_UNICAST_PROMISC
+} nic_unicast_mode_t;
+
+typedef enum nic_multicast_mode {
+	NIC_MULTICAST_UNKNOWN,
+	/** No multicast frames are received */
+	NIC_MULTICAST_BLOCKED,
+	/** Frames with multicast addresses specified in this list are received */
+	NIC_MULTICAST_LIST,
+	/** All multicast frames are received */
+	NIC_MULTICAST_PROMISC
+} nic_multicast_mode_t;
+
+typedef enum nic_broadcast_mode {
+	NIC_BROADCAST_UNKNOWN,
+	/** Broadcast frames are dropped */
+	NIC_BROADCAST_BLOCKED,
+	/** Broadcast frames are received */
+	NIC_BROADCAST_ACCEPTED
+} nic_broadcast_mode_t;
+
+/**
+ * Structure covering the bitmap with VLAN tags.
+ */
+typedef struct nic_vlan_mask {
+	uint8_t bitmap[NIC_VLAN_BITMAP_SIZE];
+} nic_vlan_mask_t;
+
+/* WOL virtue identifier */
+typedef unsigned int nic_wv_id_t;
+
+/**
+ * Structure passed as argument for virtue NIC_WV_MAGIC_PACKET.
+ */
+typedef struct nic_wv_magic_packet_data {
+	uint8_t password[6];
+} nic_wv_magic_packet_data_t;
+
+/**
+ * Structure passed as argument for virtue NIC_WV_DIRECTED_IPV4
+ */
+typedef struct nic_wv_ipv4_data {
+	uint8_t address[4];
+} nic_wv_ipv4_data_t;
+
+/**
+ * Structure passed as argument for virtue NIC_WV_DIRECTED_IPV6
+ */
+typedef struct nic_wv_ipv6_data {
+	uint8_t address[16];
+} nic_wv_ipv6_data_t;
+
+/**
+ * WOL virtue types defining the interpretation of data passed to the virtue.
+ * Those tagged with S can have only single virtue active at one moment, those
+ * tagged with M can have multiple ones.
+ */
+typedef enum nic_wv_type {
+	/**
+	 * Used for deletion of the virtue - in this case the mask, data and length
+	 * arguments are ignored.
+	 */
+	NIC_WV_NONE,
+	/** S
+	 * Enabled <=> wakeup upon link change
+	 */
+	NIC_WV_LINK_CHANGE,
+	/** S
+	 * If this virtue is set up, wakeup can be issued by a magic packet frame.
+	 * If the data argument is not NULL, it must contain
+	 * nic_wv_magic_packet_data structure with the SecureOn password.
+	 */
+	NIC_WV_MAGIC_PACKET,
+	/** M
+	 * If the virtue is set up, wakeup can be issued by a frame targeted to
+	 * device with MAC address specified in data. The data must contain
+	 * nic_address_t structure.
+	 */
+	NIC_WV_DESTINATION,
+	/** S
+	 * Enabled <=> wakeup upon receiving broadcast frame
+	 */
+	NIC_WV_BROADCAST,
+	/** S
+	 * Enabled <=> wakeup upon receiving ARP Request
+	 */
+	NIC_WV_ARP_REQUEST,
+	/** M
+	 * If enabled, the wakeup is issued upon receiving frame with an IPv4 packet
+	 * with IPv4 address specified in data. The data must contain
+	 * nic_wv_ipv4_data structure.
+	 */
+	NIC_WV_DIRECTED_IPV4,
+	/** M
+	 * If enabled, the wakeup is issued upon receiving frame with an IPv4 packet
+	 * with IPv6 address specified in data. The data must contain
+	 * nic_wv_ipv6_data structure.
+	 */
+	NIC_WV_DIRECTED_IPV6,
+	/** M
+	 * First length/2 bytes in the argument are interpreted as mask, second
+	 * length/2 bytes are interpreted as content.
+	 * If enabled, the wakeup is issued upon receiving frame where the bytes
+	 * with non-zero value in the mask equal to those in the content.
+	 */
+	NIC_WV_FULL_MATCH,
+	/**
+	 * Dummy value, do not use.
+	 */
+	NIC_WV_MAX
+} nic_wv_type_t;
+
+/**
+ * Specifies the interrupt/polling mode used by the driver and NIC
+ */
+typedef enum nic_poll_mode {
+	/**
+	 * NIC issues interrupts upon events.
+	 */
+	NIC_POLL_IMMEDIATE,
+	/**
+	 * Some uspace app calls nic_poll_now(...) in order to check the NIC state
+	 * - no interrupts are received from the NIC.
+	 */
+	NIC_POLL_ON_DEMAND,
+	/**
+	 * The driver itself issues a poll request in a periodic manner. It is
+	 * allowed to use hardware timer if the NIC supports it.
+	 */
+	NIC_POLL_PERIODIC,
+	/**
+	 * The driver itself issued a poll request in a periodic manner. The driver
+	 * must create software timer, internal hardware timer of NIC must not be
+	 * used even if the NIC supports it.
+	 */
+	NIC_POLL_SOFTWARE_PERIODIC
+} nic_poll_mode_t;
+
+/**
+ * Says if this virtue type is a multi-virtue (there can be multiple virtues of
+ * this type at once).
+ *
+ * @param type
+ *
+ * @return true or false
+ */
+static inline int nic_wv_is_multi(nic_wv_type_t type)
+{
+	switch (type) {
+	case NIC_WV_FULL_MATCH:
+	case NIC_WV_DESTINATION:
+	case NIC_WV_DIRECTED_IPV4:
+	case NIC_WV_DIRECTED_IPV6:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static inline const char *nic_device_state_to_string(nic_device_state_t state)
+{
+	switch (state) {
+	case NIC_STATE_STOPPED:
+		return "stopped";
+	case NIC_STATE_DOWN:
+		return "down";
+	case NIC_STATE_ACTIVE:
+		return "active";
+	default:
+		return "undefined";
+	}
+}
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/pci.h
===================================================================
--- uspace/lib/device/include/pci.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/pci.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_PCI_H
+#define LIBDEVICE_PCI_H
+
+#include <errno.h>
+#include <ipc/devman.h>
+#include <loc.h>
+#include <types/pci.h>
+
+extern errno_t pci_open(service_id_t, pci_t **);
+extern void pci_close(pci_t *);
+extern errno_t pci_get_devices(pci_t *, devman_handle_t **, size_t *);
+extern errno_t pci_dev_get_info(pci_t *, devman_handle_t, pci_dev_info_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/types/io/chardev.h
===================================================================
--- uspace/lib/device/include/types/io/chardev.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/types/io/chardev.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 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 libdevice
+ * @{
+ */
+
+#ifndef LIBDEVICE_TYPES_IO_CHARDEV_H
+#define LIBDEVICE_TYPES_IO_CHARDEV_H
+
+/** Chardev read/write operation flags */
+typedef enum {
+	/** No flags */
+	chardev_f_none = 0,
+	/** Do not block even if no bytes can be transferred */
+	chardev_f_nonblock = 0x1
+} chardev_flags_t;
+
+#endif
+/**
+ * @}
+ */
Index: uspace/lib/device/include/types/label.h
===================================================================
--- uspace/lib/device/include/types/label.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/types/label.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2015 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_TYPES_LABEL_H
+#define LIBDEVICE_TYPES_LABEL_H
+
+#include <types/uuid.h>
+
+/** Partition contents */
+typedef enum {
+	/** Partition is considered empty */
+	ptc_empty = 0,
+	/** Partition contains a recognized filesystem */
+	ptc_fs,
+	/** Partition contains unrecognized data */
+	ptc_unknown
+} label_part_cnt_t;
+
+/** Disk label type */
+typedef enum {
+	/** No label */
+	lt_none,
+	/** BIOS Master Boot Record */
+	lt_mbr,
+	/** UEFI GUID Partition Table */
+	lt_gpt
+} label_type_t;
+
+#define LT_FIRST lt_mbr
+#define LT_LIMIT (lt_gpt + 1)
+
+#define LT_DEFAULT lt_mbr
+
+/** Partition kind */
+typedef enum {
+	/** Primary partition */
+	lpk_primary,
+	/** Extended partition */
+	lpk_extended,
+	/** Logical partition */
+	lpk_logical
+} label_pkind_t;
+
+/** Label flags */
+typedef enum {
+	/** Label supports extended (and logical) partitions */
+	lf_ext_supp = 0x1,
+	/** Partition type is in UUID format (otherwise in small number format) */
+	lf_ptype_uuid = 0x2,
+	/** Currently it is possible to create a primary partition */
+	lf_can_create_pri = 0x4,
+	/** Currently it is possible to create an extended partition */
+	lf_can_create_ext = 0x8,
+	/** Currently it is possible to create a logical partition */
+	lf_can_create_log = 0x10,
+	/** Currently it is possible to delete a partition */
+	lf_can_delete_part = 0x20,
+	/** Currently it is possible to modify a partition */
+	lf_can_modify_part = 0x40
+} label_flags_t;
+
+/** Partition type format */
+typedef enum {
+	/** Small number */
+	lptf_num,
+	/** UUID */
+	lptf_uuid
+} label_pt_fmt;
+
+/** Partition type */
+typedef struct {
+	/** Type format */
+	label_pt_fmt fmt;
+	/** Depending on @c fmt */
+	union {
+		/* Small number */
+		uint8_t num;
+		/** UUID */
+		uuid_t uuid;
+	} t;
+} label_ptype_t;
+
+/** Partition content (used to get partition type suggestion) */
+typedef enum {
+	/** ExFAT */
+	lpc_exfat,
+	/** Ext4 */
+	lpc_ext4,
+	/** FAT12 or FAT16 */
+	lpc_fat12_16,
+	/** FAT32 */
+	lpc_fat32,
+	/** Minix file system */
+	lpc_minix
+} label_pcnt_t;
+
+#define LPC_LIMIT (lpc_minix + 1)
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/types/pci.h
===================================================================
--- uspace/lib/device/include/types/pci.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/types/pci.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_TYPES_PCI_H
+#define LIBDEVICE_TYPES_PCI_H
+
+#include <async.h>
+#include <ipc/devman.h>
+#include <stdint.h>
+
+/** PCI device information */
+typedef struct {
+	/** Devman function handle */
+	devman_handle_t dev_handle;
+	/** Bus number */
+	uint8_t bus_num;
+	/** Device number */
+	uint8_t dev_num;
+	/** Function number */
+	uint8_t fn_num;
+	/** Vendor ID */
+	uint16_t vendor_id;
+	/** Device ID */
+	uint16_t device_id;
+} pci_dev_info_t;
+
+/** PCI service */
+typedef struct {
+	/** Session with PCI control service */
+	async_sess_t *sess;
+} pci_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/types/vol.h
===================================================================
--- uspace/lib/device/include/types/vol.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/types/vol.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2015 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_TYPES_VOL_H
+#define LIBDEVICE_TYPES_VOL_H
+
+#include <async.h>
+#include <ipc/vfs.h>
+#include <ipc/vol.h>
+#include <stdbool.h>
+
+typedef struct {
+	sysarg_t id;
+} volume_id_t;
+
+typedef enum {
+	/** Partition is empty */
+	vpc_empty,
+	/** Partition contains a recognized filesystem */
+	vpc_fs,
+	/** Partition contains unrecognized data */
+	vpc_unknown
+} vol_part_cnt_t;
+
+/** File system type */
+typedef enum {
+	fs_exfat,
+	fs_fat,
+	fs_minix,
+	fs_ext4,
+	fs_cdfs
+} vol_fstype_t;
+
+#define VOL_FSTYPE_LIMIT (fs_ext4 + 1)
+#define VOL_FSTYPE_DEFAULT fs_ext4
+
+/** Volume service */
+typedef struct vol {
+	/** Volume service session */
+	async_sess_t *sess;
+} vol_t;
+
+/** Partition information */
+typedef struct {
+	/** Partition content type */
+	vol_part_cnt_t pcnt;
+	/** Filesystem type */
+	vol_fstype_t fstype;
+	/** Volume label */
+	char label[VOL_LABEL_MAXLEN + 1];
+	/** Current mount point */
+	char cur_mp[MAX_PATH_LEN + 1]; /* XXX too big */
+	/** Current mount point is automatic */
+	bool cur_mp_auto;
+} vol_part_info_t;
+
+/** Volume configuration information */
+typedef struct {
+	/** Volume identifier */
+	volume_id_t id;
+	/** Volume label */
+	char label[VOL_LABEL_MAXLEN + 1];
+	/** Mount path */
+	char path[MAX_PATH_LEN + 1]; /* XXX too big */
+} vol_info_t;
+
+/** Volume label support */
+typedef struct {
+	/** Volume labels are supported */
+	bool supported;
+} vol_label_supp_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/vbd.h
===================================================================
--- uspace/lib/device/include/vbd.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/vbd.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2015 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_VBD_H
+#define LIBDEVICE_VBD_H
+
+#include <async.h>
+#include <loc.h>
+#include <types/label.h>
+#include <offset.h>
+
+/** VBD service */
+typedef struct vbd {
+	/** VBD session */
+	async_sess_t *sess;
+} vbd_t;
+
+/** Disk information */
+typedef struct {
+	/** Label type */
+	label_type_t ltype;
+	/** Label flags */
+	label_flags_t flags;
+	/** First block that can be allocated */
+	aoff64_t ablock0;
+	/** Number of blocks that can be allocated */
+	aoff64_t anblocks;
+	/** Block size */
+	size_t block_size;
+	/** Total number of blocks */
+	aoff64_t nblocks;
+} vbd_disk_info_t;
+
+/** Specification of new partition */
+typedef struct {
+	/** Partition index */
+	int index;
+	/** First block */
+	aoff64_t block0;
+	/** Number of blocks */
+	aoff64_t nblocks;
+	/** Number of header blocks (EBR for logical partitions) */
+	aoff64_t hdr_blocks;
+	/** Partition kind */
+	label_pkind_t pkind;
+	/** Partition type */
+	label_ptype_t ptype;
+} vbd_part_spec_t;
+
+/** Partition info */
+typedef struct {
+	/** Partition index */
+	int index;
+	/** Partition kind */
+	label_pkind_t pkind;
+	/** First block */
+	aoff64_t block0;
+	/** Number of blocks */
+	aoff64_t nblocks;
+	/** Service ID */
+	service_id_t svc_id;
+} vbd_part_info_t;
+
+typedef sysarg_t vbd_part_id_t;
+
+extern errno_t vbd_create(vbd_t **);
+extern void vbd_destroy(vbd_t *);
+extern errno_t vbd_get_disks(vbd_t *, service_id_t **, size_t *);
+extern errno_t vbd_disk_info(vbd_t *, service_id_t, vbd_disk_info_t *);
+extern errno_t vbd_label_create(vbd_t *, service_id_t, label_type_t);
+extern errno_t vbd_label_delete(vbd_t *, service_id_t);
+extern errno_t vbd_label_get_parts(vbd_t *, service_id_t, service_id_t **,
+    size_t *);
+extern errno_t vbd_part_get_info(vbd_t *, vbd_part_id_t, vbd_part_info_t *);
+extern errno_t vbd_part_create(vbd_t *, service_id_t, vbd_part_spec_t *,
+    vbd_part_id_t *);
+extern errno_t vbd_part_delete(vbd_t *, vbd_part_id_t);
+extern void vbd_pspec_init(vbd_part_spec_t *);
+extern errno_t vbd_suggest_ptype(vbd_t *, service_id_t, label_pcnt_t,
+    label_ptype_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/include/vol.h
===================================================================
--- uspace/lib/device/include/vol.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/include/vol.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBDEVICE_VOL_H
+#define LIBDEVICE_VOL_H
+
+#include <async.h>
+#include <errno.h>
+#include <loc.h>
+#include <stdint.h>
+#include <types/label.h>
+#include <types/vol.h>
+
+extern errno_t vol_create(vol_t **);
+extern void vol_destroy(vol_t *);
+extern errno_t vol_get_parts(vol_t *, service_id_t **, size_t *);
+extern errno_t vol_part_add(vol_t *, service_id_t);
+extern errno_t vol_part_info(vol_t *, service_id_t, vol_part_info_t *);
+extern errno_t vol_part_eject(vol_t *, service_id_t);
+extern errno_t vol_part_empty(vol_t *, service_id_t);
+extern errno_t vol_part_insert(vol_t *, service_id_t);
+extern errno_t vol_part_insert_by_path(vol_t *, const char *);
+extern errno_t vol_part_get_lsupp(vol_t *, vol_fstype_t, vol_label_supp_t *);
+extern errno_t vol_part_mkfs(vol_t *, service_id_t, vol_fstype_t, const char *,
+    const char *);
+extern errno_t vol_part_set_mountp(vol_t *, service_id_t, const char *);
+extern errno_t vol_get_volumes(vol_t *, volume_id_t **, size_t *);
+extern errno_t vol_info(vol_t *, volume_id_t, vol_info_t *);
+extern errno_t vol_fstype_format(vol_fstype_t, char **);
+extern errno_t vol_pcnt_fs_format(vol_part_cnt_t, vol_fstype_t, char **);
+extern errno_t vol_mountp_validate(const char *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/device/meson.build
===================================================================
--- uspace/lib/device/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,42 @@
+#
+# Copyright (c) 2021 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.
+#
+
+src = files(
+	'src/bd.c',
+	'src/bd_srv.c',
+	'src/devman.c',
+	'src/device/led_dev.c',
+	'src/io/chardev.c',
+	'src/io/chardev_srv.c',
+	'src/io/label.c',
+	'src/io/serial.c',
+	'src/irc.c',
+	'src/pci.c',
+	'src/vbd.c',
+	'src/vol.c',
+)
Index: uspace/lib/device/src/bd.c
===================================================================
--- uspace/lib/device/src/bd.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/src/bd.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,221 @@
+/*
+ * 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 libdevice
+ * @{
+ */
+/**
+ * @file
+ * @brief Block device client interface
+ */
+
+#include <async.h>
+#include <assert.h>
+#include <bd.h>
+#include <errno.h>
+#include <ipc/bd.h>
+#include <ipc/services.h>
+#include <loc.h>
+#include <macros.h>
+#include <stdlib.h>
+#include <offset.h>
+
+static void bd_cb_conn(ipc_call_t *icall, void *arg);
+
+errno_t bd_open(async_sess_t *sess, bd_t **rbd)
+{
+	bd_t *bd = calloc(1, sizeof(bd_t));
+	if (bd == NULL)
+		return ENOMEM;
+
+	bd->sess = sess;
+
+	async_exch_t *exch = async_exchange_begin(sess);
+
+	port_id_t port;
+	errno_t rc = async_create_callback_port(exch, INTERFACE_BLOCK_CB, 0, 0,
+	    bd_cb_conn, bd, &port);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		goto error;
+
+	*rbd = bd;
+	return EOK;
+
+error:
+	if (bd != NULL)
+		free(bd);
+
+	return rc;
+}
+
+void bd_close(bd_t *bd)
+{
+	/* XXX Synchronize with bd_cb_conn */
+	free(bd);
+}
+
+errno_t bd_read_blocks(bd_t *bd, aoff64_t ba, size_t cnt, void *data, size_t size)
+{
+	async_exch_t *exch = async_exchange_begin(bd->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_3(exch, BD_READ_BLOCKS, LOWER32(ba),
+	    UPPER32(ba), cnt, &answer);
+	errno_t rc = async_data_read_start(exch, data, size);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+errno_t bd_read_toc(bd_t *bd, uint8_t session, void *buf, size_t size)
+{
+	async_exch_t *exch = async_exchange_begin(bd->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, BD_READ_TOC, session, &answer);
+	errno_t rc = async_data_read_start(exch, buf, size);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+errno_t bd_write_blocks(bd_t *bd, aoff64_t ba, size_t cnt, const void *data,
+    size_t size)
+{
+	async_exch_t *exch = async_exchange_begin(bd->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_3(exch, BD_WRITE_BLOCKS, LOWER32(ba),
+	    UPPER32(ba), cnt, &answer);
+	errno_t rc = async_data_write_start(exch, data, size);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+errno_t bd_sync_cache(bd_t *bd, aoff64_t ba, size_t cnt)
+{
+	async_exch_t *exch = async_exchange_begin(bd->sess);
+
+	errno_t rc = async_req_3_0(exch, BD_SYNC_CACHE, LOWER32(ba),
+	    UPPER32(ba), cnt);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+errno_t bd_get_block_size(bd_t *bd, size_t *rbsize)
+{
+	sysarg_t bsize;
+	async_exch_t *exch = async_exchange_begin(bd->sess);
+
+	errno_t rc = async_req_0_1(exch, BD_GET_BLOCK_SIZE, &bsize);
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	*rbsize = bsize;
+	return EOK;
+}
+
+errno_t bd_get_num_blocks(bd_t *bd, aoff64_t *rnb)
+{
+	sysarg_t nb_l;
+	sysarg_t nb_h;
+	async_exch_t *exch = async_exchange_begin(bd->sess);
+
+	errno_t rc = async_req_0_2(exch, BD_GET_NUM_BLOCKS, &nb_l, &nb_h);
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	*rnb = (aoff64_t) MERGE_LOUP32(nb_l, nb_h);
+	return EOK;
+}
+
+static void bd_cb_conn(ipc_call_t *icall, void *arg)
+{
+	bd_t *bd = (bd_t *)arg;
+
+	(void)bd;
+
+	while (true) {
+		ipc_call_t call;
+		async_get_call(&call);
+
+		if (!ipc_get_imethod(&call)) {
+			async_answer_0(&call, EOK);
+			return;
+		}
+
+		switch (ipc_get_imethod(&call)) {
+		default:
+			async_answer_0(&call, ENOTSUP);
+		}
+	}
+}
+
+/** @}
+ */
Index: uspace/lib/device/src/bd_srv.c
===================================================================
--- uspace/lib/device/src/bd_srv.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/src/bd_srv.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,288 @@
+/*
+ * 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 libdevice
+ * @{
+ */
+/**
+ * @file
+ * @brief Block device server stub
+ */
+#include <errno.h>
+#include <ipc/bd.h>
+#include <macros.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <bd_srv.h>
+
+static void bd_read_blocks_srv(bd_srv_t *srv, ipc_call_t *call)
+{
+	aoff64_t ba;
+	size_t cnt;
+	void *buf;
+	size_t size;
+	errno_t rc;
+
+	ba = MERGE_LOUP32(ipc_get_arg1(call), ipc_get_arg2(call));
+	cnt = ipc_get_arg3(call);
+
+	ipc_call_t rcall;
+	if (!async_data_read_receive(&rcall, &size)) {
+		async_answer_0(call, EINVAL);
+		return;
+	}
+
+	buf = malloc(size);
+	if (buf == NULL) {
+		async_answer_0(&rcall, ENOMEM);
+		async_answer_0(call, ENOMEM);
+		return;
+	}
+
+	if (srv->srvs->ops->read_blocks == NULL) {
+		async_answer_0(&rcall, ENOTSUP);
+		async_answer_0(call, ENOTSUP);
+		free(buf);
+		return;
+	}
+
+	rc = srv->srvs->ops->read_blocks(srv, ba, cnt, buf, size);
+	if (rc != EOK) {
+		async_answer_0(&rcall, ENOMEM);
+		async_answer_0(call, ENOMEM);
+		free(buf);
+		return;
+	}
+
+	async_data_read_finalize(&rcall, buf, size);
+
+	free(buf);
+	async_answer_0(call, EOK);
+}
+
+static void bd_read_toc_srv(bd_srv_t *srv, ipc_call_t *call)
+{
+	uint8_t session;
+	void *buf;
+	size_t size;
+	errno_t rc;
+
+	session = ipc_get_arg1(call);
+
+	ipc_call_t rcall;
+	if (!async_data_read_receive(&rcall, &size)) {
+		async_answer_0(call, EINVAL);
+		return;
+	}
+
+	buf = malloc(size);
+	if (buf == NULL) {
+		async_answer_0(&rcall, ENOMEM);
+		async_answer_0(call, ENOMEM);
+		return;
+	}
+
+	if (srv->srvs->ops->read_toc == NULL) {
+		async_answer_0(&rcall, ENOTSUP);
+		async_answer_0(call, ENOTSUP);
+		free(buf);
+		return;
+	}
+
+	rc = srv->srvs->ops->read_toc(srv, session, buf, size);
+	if (rc != EOK) {
+		async_answer_0(&rcall, ENOMEM);
+		async_answer_0(call, ENOMEM);
+		free(buf);
+		return;
+	}
+
+	async_data_read_finalize(&rcall, buf, size);
+
+	free(buf);
+	async_answer_0(call, EOK);
+}
+
+static void bd_sync_cache_srv(bd_srv_t *srv, ipc_call_t *call)
+{
+	aoff64_t ba;
+	size_t cnt;
+	errno_t rc;
+
+	ba = MERGE_LOUP32(ipc_get_arg1(call), ipc_get_arg2(call));
+	cnt = ipc_get_arg3(call);
+
+	if (srv->srvs->ops->sync_cache == NULL) {
+		async_answer_0(call, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->sync_cache(srv, ba, cnt);
+	async_answer_0(call, rc);
+}
+
+static void bd_write_blocks_srv(bd_srv_t *srv, ipc_call_t *call)
+{
+	aoff64_t ba;
+	size_t cnt;
+	void *data;
+	size_t size;
+	errno_t rc;
+
+	ba = MERGE_LOUP32(ipc_get_arg1(call), ipc_get_arg2(call));
+	cnt = ipc_get_arg3(call);
+
+	rc = async_data_write_accept(&data, false, 0, 0, 0, &size);
+	if (rc != EOK) {
+		async_answer_0(call, rc);
+		return;
+	}
+
+	if (srv->srvs->ops->write_blocks == NULL) {
+		async_answer_0(call, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->write_blocks(srv, ba, cnt, data, size);
+	free(data);
+	async_answer_0(call, rc);
+}
+
+static void bd_get_block_size_srv(bd_srv_t *srv, ipc_call_t *call)
+{
+	errno_t rc;
+	size_t block_size;
+
+	if (srv->srvs->ops->get_block_size == NULL) {
+		async_answer_0(call, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->get_block_size(srv, &block_size);
+	async_answer_1(call, rc, block_size);
+}
+
+static void bd_get_num_blocks_srv(bd_srv_t *srv, ipc_call_t *call)
+{
+	errno_t rc;
+	aoff64_t num_blocks;
+
+	if (srv->srvs->ops->get_num_blocks == NULL) {
+		async_answer_0(call, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->get_num_blocks(srv, &num_blocks);
+	async_answer_2(call, rc, LOWER32(num_blocks), UPPER32(num_blocks));
+}
+
+static bd_srv_t *bd_srv_create(bd_srvs_t *srvs)
+{
+	bd_srv_t *srv;
+
+	srv = calloc(1, sizeof(bd_srv_t));
+	if (srv == NULL)
+		return NULL;
+
+	srv->srvs = srvs;
+	return srv;
+}
+
+void bd_srvs_init(bd_srvs_t *srvs)
+{
+	srvs->ops = NULL;
+	srvs->sarg = NULL;
+}
+
+errno_t bd_conn(ipc_call_t *icall, bd_srvs_t *srvs)
+{
+	bd_srv_t *srv;
+	errno_t rc;
+
+	/* Accept the connection */
+	async_accept_0(icall);
+
+	srv = bd_srv_create(srvs);
+	if (srv == NULL)
+		return ENOMEM;
+
+	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (sess == NULL)
+		return ENOMEM;
+
+	srv->client_sess = sess;
+
+	rc = srvs->ops->open(srvs, srv);
+	if (rc != EOK)
+		return rc;
+
+	while (true) {
+		ipc_call_t call;
+		async_get_call(&call);
+		sysarg_t method = ipc_get_imethod(&call);
+
+		if (!method) {
+			/* The other side has hung up */
+			async_answer_0(&call, EOK);
+			break;
+		}
+
+		switch (method) {
+		case BD_READ_BLOCKS:
+			bd_read_blocks_srv(srv, &call);
+			break;
+		case BD_READ_TOC:
+			bd_read_toc_srv(srv, &call);
+			break;
+		case BD_SYNC_CACHE:
+			bd_sync_cache_srv(srv, &call);
+			break;
+		case BD_WRITE_BLOCKS:
+			bd_write_blocks_srv(srv, &call);
+			break;
+		case BD_GET_BLOCK_SIZE:
+			bd_get_block_size_srv(srv, &call);
+			break;
+		case BD_GET_NUM_BLOCKS:
+			bd_get_num_blocks_srv(srv, &call);
+			break;
+		default:
+			async_answer_0(&call, EINVAL);
+		}
+	}
+
+	rc = srvs->ops->close(srv);
+	free(srv);
+
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/device/src/device/led_dev.c
===================================================================
--- uspace/lib/device/src/device/led_dev.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/src/device/led_dev.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2014 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#include <errno.h>
+#include <async.h>
+#include <io/pixel.h>
+#include <ipc/dev_iface.h>
+#include <device/led_dev.h>
+
+errno_t led_dev_color_set(async_sess_t *sess, pixel_t pixel)
+{
+	async_exch_t *exch = async_exchange_begin(sess);
+
+	aid_t req = async_send_2(exch, DEV_IFACE_ID(LED_DEV_IFACE),
+	    LED_DEV_COLOR_SET, (sysarg_t) pixel, NULL);
+
+	async_exchange_end(exch);
+
+	errno_t rc;
+	async_wait_for(req, &rc);
+
+	return (errno_t) rc;
+}
+
+/** @}
+ */
Index: uspace/lib/device/src/devman.c
===================================================================
--- uspace/lib/device/src/devman.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/src/devman.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,734 @@
+/*
+ * Copyright (c) 2007 Josef Cejka
+ * Copyright (c) 2013 Jiri Svoboda
+ * 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#include <adt/list.h>
+#include <str.h>
+#include <ipc/services.h>
+#include <ns.h>
+#include <ipc/devman.h>
+#include <devman.h>
+#include <fibril_synch.h>
+#include <async.h>
+#include <errno.h>
+#include <stdlib.h>
+
+static FIBRIL_MUTEX_INITIALIZE(devman_driver_block_mutex);
+static FIBRIL_MUTEX_INITIALIZE(devman_client_block_mutex);
+
+static FIBRIL_MUTEX_INITIALIZE(devman_driver_mutex);
+static FIBRIL_MUTEX_INITIALIZE(devman_client_mutex);
+
+static async_sess_t *devman_driver_block_sess = NULL;
+static async_sess_t *devman_client_block_sess = NULL;
+
+static async_sess_t *devman_driver_sess = NULL;
+static async_sess_t *devman_client_sess = NULL;
+
+static void clone_session(fibril_mutex_t *mtx, async_sess_t *src,
+    async_sess_t **dst)
+{
+	fibril_mutex_lock(mtx);
+
+	if ((*dst == NULL) && (src != NULL))
+		*dst = src;
+
+	fibril_mutex_unlock(mtx);
+}
+
+/** Start an async exchange on the devman session (blocking).
+ *
+ * @param iface Device manager interface to choose
+ *
+ * @return New exchange.
+ *
+ */
+async_exch_t *devman_exchange_begin_blocking(iface_t iface)
+{
+	switch (iface) {
+	case INTERFACE_DDF_DRIVER:
+		fibril_mutex_lock(&devman_driver_block_mutex);
+
+		while (devman_driver_block_sess == NULL) {
+			clone_session(&devman_driver_mutex, devman_driver_sess,
+			    &devman_driver_block_sess);
+
+			if (devman_driver_block_sess == NULL)
+				devman_driver_block_sess =
+				    service_connect_blocking(SERVICE_DEVMAN,
+				    INTERFACE_DDF_DRIVER, 0, NULL);
+		}
+
+		fibril_mutex_unlock(&devman_driver_block_mutex);
+
+		clone_session(&devman_driver_mutex, devman_driver_block_sess,
+		    &devman_driver_sess);
+
+		return async_exchange_begin(devman_driver_block_sess);
+	case INTERFACE_DDF_CLIENT:
+		fibril_mutex_lock(&devman_client_block_mutex);
+
+		while (devman_client_block_sess == NULL) {
+			clone_session(&devman_client_mutex, devman_client_sess,
+			    &devman_client_block_sess);
+
+			if (devman_client_block_sess == NULL)
+				devman_client_block_sess =
+				    service_connect_blocking(SERVICE_DEVMAN,
+				    INTERFACE_DDF_CLIENT, 0, NULL);
+		}
+
+		fibril_mutex_unlock(&devman_client_block_mutex);
+
+		clone_session(&devman_client_mutex, devman_client_block_sess,
+		    &devman_client_sess);
+
+		return async_exchange_begin(devman_client_block_sess);
+	default:
+		return NULL;
+	}
+}
+
+/** Start an async exchange on the devman session.
+ *
+ * @param iface Device manager interface to choose
+ *
+ * @return New exchange.
+ *
+ */
+async_exch_t *devman_exchange_begin(iface_t iface)
+{
+	switch (iface) {
+	case INTERFACE_DDF_DRIVER:
+		fibril_mutex_lock(&devman_driver_mutex);
+
+		if (devman_driver_sess == NULL)
+			devman_driver_sess =
+			    service_connect(SERVICE_DEVMAN,
+			    INTERFACE_DDF_DRIVER, 0, NULL);
+
+		fibril_mutex_unlock(&devman_driver_mutex);
+
+		if (devman_driver_sess == NULL)
+			return NULL;
+
+		return async_exchange_begin(devman_driver_sess);
+	case INTERFACE_DDF_CLIENT:
+		fibril_mutex_lock(&devman_client_mutex);
+
+		if (devman_client_sess == NULL)
+			devman_client_sess =
+			    service_connect(SERVICE_DEVMAN,
+			    INTERFACE_DDF_CLIENT, 0, NULL);
+
+		fibril_mutex_unlock(&devman_client_mutex);
+
+		if (devman_client_sess == NULL)
+			return NULL;
+
+		return async_exchange_begin(devman_client_sess);
+	default:
+		return NULL;
+	}
+}
+
+/** Finish an async exchange on the devman session.
+ *
+ * @param exch Exchange to be finished.
+ *
+ */
+void devman_exchange_end(async_exch_t *exch)
+{
+	async_exchange_end(exch);
+}
+
+/** Register running driver with device manager. */
+errno_t devman_driver_register(const char *name)
+{
+	async_exch_t *exch = devman_exchange_begin_blocking(INTERFACE_DDF_DRIVER);
+
+	ipc_call_t answer;
+	aid_t req = async_send_2(exch, DEVMAN_DRIVER_REGISTER, 0, 0, &answer);
+	errno_t retval = async_data_write_start(exch, name, str_size(name));
+
+	devman_exchange_end(exch);
+
+	if (retval != EOK) {
+		async_forget(req);
+		return retval;
+	}
+
+	exch = devman_exchange_begin(INTERFACE_DDF_DRIVER);
+	async_connect_to_me(exch, INTERFACE_ANY, 0, 0);
+	devman_exchange_end(exch);
+
+	async_wait_for(req, &retval);
+	return retval;
+}
+
+/** Add function to a device.
+ *
+ * Request devman to add a new function to the specified device owned by
+ * this driver task.
+ *
+ * @param name      Name of the new function
+ * @param ftype     Function type, fun_inner or fun_exposed
+ * @param match_ids Match IDs (should be empty for fun_exposed)
+ * @param devh      Devman handle of the device
+ * @param funh      Place to store handle of the new function
+ *
+ * @return EOK on success or an error code.
+ *
+ */
+errno_t devman_add_function(const char *name, fun_type_t ftype,
+    match_id_list_t *match_ids, devman_handle_t devh, devman_handle_t *funh)
+{
+	unsigned long match_count = list_count(&match_ids->ids);
+	async_exch_t *exch = devman_exchange_begin_blocking(INTERFACE_DDF_DRIVER);
+
+	ipc_call_t answer;
+	aid_t req = async_send_3(exch, DEVMAN_ADD_FUNCTION, (sysarg_t) ftype,
+	    devh, match_count, &answer);
+	errno_t retval = async_data_write_start(exch, name, str_size(name));
+	if (retval != EOK) {
+		devman_exchange_end(exch);
+		async_forget(req);
+		return retval;
+	}
+
+	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,
+		    match_id->score, &answer2);
+		retval = async_data_write_start(exch, match_id->id,
+		    str_size(match_id->id));
+		if (retval != EOK) {
+			devman_exchange_end(exch);
+			async_forget(req2);
+			async_forget(req);
+			return retval;
+		}
+
+		async_wait_for(req2, &retval);
+		if (retval != EOK) {
+			devman_exchange_end(exch);
+			async_forget(req);
+			return retval;
+		}
+	}
+
+	devman_exchange_end(exch);
+
+	async_wait_for(req, &retval);
+	if (retval == EOK) {
+		if (funh != NULL)
+			*funh = (int) ipc_get_arg1(&answer);
+	} else {
+		if (funh != NULL)
+			*funh = -1;
+	}
+
+	return retval;
+}
+
+errno_t devman_add_device_to_category(devman_handle_t devman_handle,
+    const char *cat_name)
+{
+	async_exch_t *exch = devman_exchange_begin_blocking(INTERFACE_DDF_DRIVER);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, DEVMAN_ADD_DEVICE_TO_CATEGORY,
+	    devman_handle, &answer);
+	errno_t retval = async_data_write_start(exch, cat_name,
+	    str_size(cat_name));
+
+	devman_exchange_end(exch);
+
+	if (retval != EOK) {
+		async_forget(req);
+		return retval;
+	}
+
+	async_wait_for(req, &retval);
+	return retval;
+}
+
+async_sess_t *devman_device_connect(devman_handle_t handle, unsigned int flags)
+{
+	async_sess_t *sess;
+
+	if (flags & IPC_FLAG_BLOCKING)
+		sess = service_connect_blocking(SERVICE_DEVMAN,
+		    INTERFACE_DEVMAN_DEVICE, handle, NULL);
+	else
+		sess = service_connect(SERVICE_DEVMAN,
+		    INTERFACE_DEVMAN_DEVICE, handle, NULL);
+
+	return sess;
+}
+
+/** Remove function from device.
+ *
+ * Request devman to remove function owned by this driver task.
+ * @param funh      Devman handle of the function
+ *
+ * @return EOK on success or an error code.
+ */
+errno_t devman_remove_function(devman_handle_t funh)
+{
+	async_exch_t *exch;
+	errno_t retval;
+
+	exch = devman_exchange_begin_blocking(INTERFACE_DDF_DRIVER);
+	retval = async_req_1_0(exch, DEVMAN_REMOVE_FUNCTION, (sysarg_t) funh);
+	devman_exchange_end(exch);
+
+	return retval;
+}
+
+errno_t devman_drv_fun_online(devman_handle_t funh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_DRIVER);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_0(exch, DEVMAN_DRV_FUN_ONLINE, funh);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+errno_t devman_drv_fun_offline(devman_handle_t funh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_DRIVER);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_0(exch, DEVMAN_DRV_FUN_OFFLINE, funh);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+async_sess_t *devman_parent_device_connect(devman_handle_t handle,
+    unsigned int flags)
+{
+	async_sess_t *sess;
+
+	if (flags & IPC_FLAG_BLOCKING)
+		sess = service_connect_blocking(SERVICE_DEVMAN,
+		    INTERFACE_DEVMAN_PARENT, handle, NULL);
+	else
+		sess = service_connect(SERVICE_DEVMAN,
+		    INTERFACE_DEVMAN_PARENT, handle, NULL);
+
+	return sess;
+}
+
+errno_t devman_fun_get_handle(const char *pathname, devman_handle_t *handle,
+    unsigned int flags)
+{
+	async_exch_t *exch;
+
+	if (flags & IPC_FLAG_BLOCKING)
+		exch = devman_exchange_begin_blocking(INTERFACE_DDF_CLIENT);
+	else {
+		exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+		if (exch == NULL)
+			return ENOMEM;
+	}
+
+	ipc_call_t answer;
+	aid_t req = async_send_2(exch, DEVMAN_DEVICE_GET_HANDLE, flags, 0,
+	    &answer);
+	errno_t retval = async_data_write_start(exch, pathname,
+	    str_size(pathname));
+
+	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;
+}
+
+static errno_t 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;
+	ipc_call_t dreply;
+	size_t act_size;
+	errno_t dretval;
+
+	exch = devman_exchange_begin_blocking(INTERFACE_DDF_CLIENT);
+
+	ipc_call_t 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);
+
+	devman_exchange_end(exch);
+
+	if (dretval != EOK) {
+		async_forget(req);
+		return dretval;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		return retval;
+	}
+
+	if (r1 != NULL)
+		*r1 = ipc_get_arg1(&answer);
+	act_size = ipc_get_arg2(&dreply);
+	assert(act_size <= buf_size - 1);
+	buf[act_size] = '\0';
+
+	return EOK;
+}
+
+errno_t devman_fun_get_path(devman_handle_t handle, char *buf, size_t buf_size)
+{
+	return devman_get_str_internal(DEVMAN_FUN_GET_PATH, handle, 0, NULL,
+	    buf, buf_size);
+}
+
+errno_t devman_fun_get_match_id(devman_handle_t handle, size_t index, char *buf,
+    size_t buf_size, unsigned int *rscore)
+{
+	errno_t 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;
+}
+
+errno_t devman_fun_get_name(devman_handle_t handle, char *buf, size_t buf_size)
+{
+	return devman_get_str_internal(DEVMAN_FUN_GET_NAME, handle, 0, NULL,
+	    buf, buf_size);
+}
+
+errno_t 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, 0,
+	    NULL, buf, buf_size);
+}
+
+errno_t devman_fun_online(devman_handle_t funh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_0(exch, DEVMAN_FUN_ONLINE, funh);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+errno_t devman_fun_offline(devman_handle_t funh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_0(exch, DEVMAN_FUN_OFFLINE, funh);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+static errno_t devman_get_handles_once(sysarg_t method, sysarg_t arg1,
+    devman_handle_t *handle_buf, size_t buf_size, size_t *act_size)
+{
+	async_exch_t *exch = devman_exchange_begin_blocking(INTERFACE_DDF_CLIENT);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, method, arg1, &answer);
+	errno_t rc = async_data_read_start(exch, handle_buf, buf_size);
+
+	devman_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		return retval;
+	}
+
+	*act_size = ipc_get_arg1(&answer);
+	return EOK;
+}
+
+/** Get list of handles.
+ *
+ * Returns an allocated array of handles.
+ *
+ * @param method	IPC method
+ * @param arg1		IPC argument 1
+ * @param data		Place to store pointer to array of handles
+ * @param count		Place to store number of handles
+ * @return 		EOK on success or an error code
+ */
+static errno_t devman_get_handles_internal(sysarg_t method, sysarg_t arg1,
+    devman_handle_t **data, size_t *count)
+{
+	devman_handle_t *handles;
+	size_t act_size;
+	size_t alloc_size;
+	errno_t rc;
+
+	*data = NULL;
+	act_size = 0;	/* silence warning */
+
+	rc = devman_get_handles_once(method, arg1, NULL, 0,
+	    &act_size);
+	if (rc != EOK)
+		return rc;
+
+	alloc_size = act_size;
+	handles = malloc(alloc_size);
+	if (handles == NULL)
+		return ENOMEM;
+
+	while (true) {
+		rc = devman_get_handles_once(method, arg1, handles, alloc_size,
+		    &act_size);
+		if (rc != EOK)
+			return rc;
+
+		if (act_size <= alloc_size)
+			break;
+
+		alloc_size *= 2;
+		free(handles);
+
+		handles = malloc(alloc_size);
+		if (handles == NULL)
+			return ENOMEM;
+	}
+
+	*count = act_size / sizeof(devman_handle_t);
+	*data = handles;
+	return EOK;
+}
+
+errno_t devman_fun_get_child(devman_handle_t funh, devman_handle_t *devh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_1(exch, DEVMAN_FUN_GET_CHILD,
+	    funh, devh);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+errno_t devman_dev_get_functions(devman_handle_t devh, devman_handle_t **funcs,
+    size_t *count)
+{
+	return devman_get_handles_internal(DEVMAN_DEV_GET_FUNCTIONS,
+	    devh, funcs, count);
+}
+
+errno_t devman_dev_get_parent(devman_handle_t devh, devman_handle_t *funh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_1(exch, DEVMAN_DEV_GET_PARENT,
+	    devh, funh);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+errno_t devman_fun_sid_to_handle(service_id_t sid, devman_handle_t *handle)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_1(exch, DEVMAN_FUN_SID_TO_HANDLE,
+	    sid, handle);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+errno_t devman_get_drivers(devman_handle_t **drvs,
+    size_t *count)
+{
+	return devman_get_handles_internal(DEVMAN_GET_DRIVERS, 0, drvs, count);
+}
+
+errno_t 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);
+}
+
+errno_t devman_driver_get_handle(const char *drvname, devman_handle_t *handle)
+{
+	async_exch_t *exch;
+
+	exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, DEVMAN_DRIVER_GET_HANDLE, &answer);
+	errno_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;
+}
+
+errno_t devman_driver_get_match_id(devman_handle_t handle, size_t index, char *buf,
+    size_t buf_size, unsigned int *rscore)
+{
+	errno_t 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;
+}
+
+errno_t 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);
+}
+
+errno_t devman_driver_get_state(devman_handle_t drvh, driver_state_t *rstate)
+{
+	sysarg_t state;
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t 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;
+}
+
+errno_t devman_driver_load(devman_handle_t drvh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t rc = async_req_1_0(exch, DEVMAN_DRIVER_LOAD, drvh);
+
+	devman_exchange_end(exch);
+	return rc;
+}
+
+errno_t devman_driver_unload(devman_handle_t drvh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t rc = async_req_1_0(exch, DEVMAN_DRIVER_UNLOAD, drvh);
+
+	devman_exchange_end(exch);
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/device/src/io/chardev.c
===================================================================
--- uspace/lib/device/src/io/chardev.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/src/io/chardev.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2017 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 libdevice
+ * @{
+ */
+/**
+ * @file
+ * @brief Character device client interface
+ */
+
+#include <errno.h>
+#include <mem.h>
+#include <io/chardev.h>
+#include <ipc/chardev.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+/** Open character device.
+ *
+ * @param sess Session with the character device
+ * @param rchardev Place to store pointer to the new character device structure
+ *
+ * @return EOK on success, ENOMEM if out of memory, EIO on I/O error
+ */
+errno_t chardev_open(async_sess_t *sess, chardev_t **rchardev)
+{
+	chardev_t *chardev;
+
+	chardev = calloc(1, sizeof(chardev_t));
+	if (chardev == NULL)
+		return ENOMEM;
+
+	chardev->sess = sess;
+	*rchardev = chardev;
+
+	/* EIO might be used in a future implementation */
+	return EOK;
+}
+
+/** Close character device.
+ *
+ * Frees the character device structure. The underlying session is
+ * not affected.
+ *
+ * @param chardev Character device or @c NULL
+ */
+void chardev_close(chardev_t *chardev)
+{
+	free(chardev);
+}
+
+/** Read from character device.
+ *
+ * Read as much data as is available from character device up to @a size
+ * bytes into @a buf. On success EOK is returned and at least one byte
+ * is read (if no byte is available the function blocks). The number
+ * of bytes read is stored in @a *nread.
+ *
+ * On error a non-zero error code is returned and @a *nread is filled with
+ * the number of bytes that were successfully transferred.
+ *
+ * @param chardev Character device
+ * @param buf Destination buffer
+ * @param size Maximum number of bytes to read
+ * @param nread Place to store actual number of bytes read
+ * @param flags @c chardev_f_nonblock to return immediately even if no
+ *              bytes are available
+ *
+ * @return EOK on success or non-zero error code
+ */
+errno_t chardev_read(chardev_t *chardev, void *buf, size_t size, size_t *nread,
+    chardev_flags_t flags)
+{
+	async_exch_t *exch = async_exchange_begin(chardev->sess);
+
+	if (size > DATA_XFER_LIMIT) {
+		/* This should not hurt anything. */
+		size = DATA_XFER_LIMIT;
+	}
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, CHARDEV_READ, flags, &answer);
+	errno_t rc = async_data_read_start(exch, buf, size);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		*nread = 0;
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		*nread = 0;
+		return retval;
+	}
+
+	*nread = ipc_get_arg2(&answer);
+	/* In case of partial success, ARG1 contains the error code */
+	return (errno_t) ipc_get_arg1(&answer);
+
+}
+
+/** Write up to DATA_XFER_LIMIT bytes to character device.
+ *
+ * Write up to @a size or DATA_XFER_LIMIT bytes from @a data to character
+ * device. On success EOK is returned, bytes were written and @a *nwritten
+ * is set to min(@a size, DATA_XFER_LIMIT)
+ *
+ * On error a non-zero error code is returned and @a *nwritten is filled with
+ * the number of bytes that were successfully transferred.
+ *
+ * @param chardev Character device
+ * @param buf Destination buffer
+ * @param size Maximum number of bytes to read
+ * @param nwritten Place to store actual number of bytes written
+ *
+ * @return EOK on success or non-zero error code
+ */
+static errno_t chardev_write_once(chardev_t *chardev, const void *data,
+    size_t size, size_t *nwritten)
+{
+	async_exch_t *exch = async_exchange_begin(chardev->sess);
+	ipc_call_t answer;
+	aid_t req;
+	errno_t rc;
+
+	/* Break down large transfers */
+	if (size > DATA_XFER_LIMIT)
+		size = DATA_XFER_LIMIT;
+
+	req = async_send_0(exch, CHARDEV_WRITE, &answer);
+	rc = async_data_write_start(exch, data, size);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		*nwritten = 0;
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK) {
+		*nwritten = 0;
+		return retval;
+	}
+
+	*nwritten = ipc_get_arg2(&answer);
+	/* In case of partial success, ARG1 contains the error code */
+	return (errno_t) ipc_get_arg1(&answer);
+}
+
+/** Write to character device.
+ *
+ * Write @a size bytes from @a data to character device. On success EOK
+ * is returned, all bytes were written and @a *nwritten is set to @a size.
+ *
+ * On error a non-zero error code is returned and @a *nwritten is filled with
+ * the number of bytes that were successfully transferred.
+ *
+ * @param chardev Character device
+ * @param buf Destination buffer
+ * @param size Maximum number of bytes to read
+ * @param nwritten Place to store actual number of bytes written
+ *
+ * @return EOK on success or non-zero error code
+ */
+errno_t chardev_write(chardev_t *chardev, const void *data, size_t size,
+    size_t *nwritten)
+{
+	size_t nw;
+	size_t p;
+	errno_t rc;
+
+	p = 0;
+	while (p < size) {
+		rc = chardev_write_once(chardev, data + p, size - p, &nw);
+		/* nw is always valid, we can have partial success */
+		p += nw;
+
+		if (rc != EOK) {
+			/* We can return partial success */
+			*nwritten = p;
+			return rc;
+		}
+	}
+
+	*nwritten = p;
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/device/src/io/chardev_srv.c
===================================================================
--- uspace/lib/device/src/io/chardev_srv.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/src/io/chardev_srv.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2014 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 libdevice
+ * @{
+ */
+/**
+ * @file
+ * @brief Character device server stub
+ */
+#include <errno.h>
+#include <io/chardev_srv.h>
+#include <ipc/chardev.h>
+#include <macros.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+static chardev_srv_t *chardev_srv_create(chardev_srvs_t *);
+
+static void chardev_read_srv(chardev_srv_t *srv, ipc_call_t *icall)
+{
+	void *buf;
+	size_t size;
+	size_t nread;
+	chardev_flags_t flags;
+	errno_t rc;
+
+	flags = ipc_get_arg1(icall);
+
+	ipc_call_t call;
+	if (!async_data_read_receive(&call, &size)) {
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	buf = malloc(size);
+	if (buf == NULL) {
+		async_answer_0(&call, ENOMEM);
+		async_answer_0(icall, ENOMEM);
+		return;
+	}
+
+	if (srv->srvs->ops->read == NULL) {
+		async_answer_0(&call, ENOTSUP);
+		async_answer_0(icall, ENOTSUP);
+		free(buf);
+		return;
+	}
+
+	rc = srv->srvs->ops->read(srv, buf, size, &nread, flags);
+	if (rc != EOK && nread == 0) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		free(buf);
+		return;
+	}
+
+	async_data_read_finalize(&call, buf, nread);
+
+	free(buf);
+	async_answer_2(icall, EOK, (sysarg_t) rc, nread);
+}
+
+static void chardev_write_srv(chardev_srv_t *srv, ipc_call_t *icall)
+{
+	void *data;
+	size_t size;
+	size_t nwr;
+	errno_t rc;
+
+	rc = async_data_write_accept(&data, false, 0, 0, 0, &size);
+	if (rc != EOK) {
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	if (srv->srvs->ops->write == NULL) {
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->write(srv, data, size, &nwr);
+	free(data);
+	if (rc != EOK && nwr == 0) {
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	async_answer_2(icall, EOK, (sysarg_t) rc, nwr);
+}
+
+static chardev_srv_t *chardev_srv_create(chardev_srvs_t *srvs)
+{
+	chardev_srv_t *srv;
+
+	srv = calloc(1, sizeof(chardev_srv_t));
+	if (srv == NULL)
+		return NULL;
+
+	srv->srvs = srvs;
+	return srv;
+}
+
+void chardev_srvs_init(chardev_srvs_t *srvs)
+{
+	srvs->ops = NULL;
+	srvs->sarg = NULL;
+}
+
+errno_t chardev_conn(ipc_call_t *icall, chardev_srvs_t *srvs)
+{
+	chardev_srv_t *srv;
+	errno_t rc;
+
+	/* Accept the connection */
+	async_accept_0(icall);
+
+	srv = chardev_srv_create(srvs);
+	if (srv == NULL)
+		return ENOMEM;
+
+	if (srvs->ops->open != NULL) {
+		rc = srvs->ops->open(srvs, srv);
+		if (rc != EOK)
+			return rc;
+	}
+
+	while (true) {
+		ipc_call_t call;
+		async_get_call(&call);
+		sysarg_t method = ipc_get_imethod(&call);
+
+		if (!method) {
+			/* The other side has hung up */
+			async_answer_0(&call, EOK);
+			break;
+		}
+
+		switch (method) {
+		case CHARDEV_READ:
+			chardev_read_srv(srv, &call);
+			break;
+		case CHARDEV_WRITE:
+			chardev_write_srv(srv, &call);
+			break;
+		default:
+			if (srv->srvs->ops->def_handler != NULL)
+				srv->srvs->ops->def_handler(srv, &call);
+			else
+				async_answer_0(&call, ENOTSUP);
+		}
+	}
+
+	if (srvs->ops->close != NULL)
+		rc = srvs->ops->close(srv);
+	else
+		rc = EOK;
+
+	free(srv);
+
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/device/src/io/label.c
===================================================================
--- uspace/lib/device/src/io/label.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/src/io/label.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2015 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 libdevice
+ * @{
+ */
+/** @file Volume service API
+ */
+
+#include <errno.h>
+#include <io/label.h>
+#include <stdlib.h>
+#include <str.h>
+
+/** Format label type as string.
+ *
+ * @param ltype Label type
+ * @param rstr Place to return pointer to newly allocated string
+ * @return EOK on success, ENOMEM if out of memory
+ */
+int label_type_format(label_type_t ltype, char **rstr)
+{
+	const char *sltype;
+	char *s;
+
+	sltype = NULL;
+	switch (ltype) {
+	case lt_none:
+		sltype = "None";
+		break;
+	case lt_mbr:
+		sltype = "MBR";
+		break;
+	case lt_gpt:
+		sltype = "GPT";
+		break;
+	}
+
+	s = str_dup(sltype);
+	if (s == NULL)
+		return ENOMEM;
+
+	*rstr = s;
+	return EOK;
+}
+
+/** Format partition kind as string.
+ *
+ * @param pkind Partition kind
+ * @param rstr Place to return pointer to newly allocated string
+ * @return EOK on success, ENOMEM if out of memory
+ */
+int label_pkind_format(label_pkind_t pkind, char **rstr)
+{
+	const char *spkind;
+	char *s;
+
+	spkind = NULL;
+	switch (pkind) {
+	case lpk_primary:
+		spkind = "Primary";
+		break;
+	case lpk_extended:
+		spkind = "Extended";
+		break;
+	case lpk_logical:
+		spkind = "Logical";
+		break;
+	}
+
+	s = str_dup(spkind);
+	if (s == NULL)
+		return ENOMEM;
+
+	*rstr = s;
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/device/src/io/serial.c
===================================================================
--- uspace/lib/device/src/io/serial.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/src/io/serial.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2017 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 <errno.h>
+#include <io/serial.h>
+#include <ipc/serial_ctl.h>
+#include <ipc/services.h>
+#include <stdlib.h>
+
+/** Open serial port device.
+ *
+ * @param sess Session with the serial port device
+ * @param rchardev Place to store pointer to the new serial port device structure
+ *
+ * @return EOK on success, ENOMEM if out of memory, EIO on I/O error
+ */
+errno_t serial_open(async_sess_t *sess, serial_t **rserial)
+{
+	serial_t *serial;
+
+	serial = calloc(1, sizeof(serial_t));
+	if (serial == NULL)
+		return ENOMEM;
+
+	serial->sess = sess;
+	*rserial = serial;
+
+	return EOK;
+}
+
+/** Close serial port device.
+ *
+ * Frees the serial port device structure. The underlying session is
+ * not affected.
+ *
+ * @param serial Serial port device or @c NULL
+ */
+void serial_close(serial_t *serial)
+{
+	free(serial);
+}
+
+/** Set serial port communication properties. */
+errno_t serial_set_comm_props(serial_t *serial, unsigned rate,
+    serial_parity_t parity, unsigned datab, unsigned stopb)
+{
+	async_exch_t *exch = async_exchange_begin(serial->sess);
+
+	errno_t rc = async_req_4_0(exch, SERIAL_SET_COM_PROPS, rate, parity,
+	    datab, stopb);
+
+	async_exchange_end(exch);
+	return rc;
+}
+
+/** Get serial port communication properties. */
+errno_t serial_get_comm_props(serial_t *serial, unsigned *rrate,
+    serial_parity_t *rparity, unsigned *rdatab, unsigned *rstopb)
+{
+	async_exch_t *exch = async_exchange_begin(serial->sess);
+	sysarg_t rate, parity, datab, stopb;
+
+	errno_t rc = async_req_0_4(exch, SERIAL_GET_COM_PROPS, &rate, &parity,
+	    &datab, &stopb);
+
+	async_exchange_end(exch);
+	if (rc != EOK)
+		return rc;
+
+	*rrate = rate;
+	*rparity = parity;
+	*rdatab = datab;
+	*rstopb = stopb;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/device/src/irc.c
===================================================================
--- uspace/lib/device/src/irc.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/src/irc.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2014 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 libdevice
+ * @{
+ */
+/** @file
+ */
+
+#include <assert.h>
+#include <async.h>
+#include <errno.h>
+#include <fibril.h>
+#include <ipc/irc.h>
+#include <ipc/services.h>
+#include <irc.h>
+#include <loc.h>
+#include <stdlib.h>
+#include <sysinfo.h>
+
+static async_sess_t *irc_sess;
+
+/** Connect to IRC service.
+ *
+ * @return	EOK on success, EIO on failure
+ */
+static errno_t irc_init(void)
+{
+	category_id_t irc_cat;
+	service_id_t *svcs;
+	size_t count;
+	errno_t rc;
+
+	assert(irc_sess == NULL);
+	rc = loc_category_get_id("irc", &irc_cat, IPC_FLAG_BLOCKING);
+	if (rc != EOK)
+		return EIO;
+
+	while (true) {
+		rc = loc_category_get_svcs(irc_cat, &svcs, &count);
+		if (rc != EOK)
+			return EIO;
+
+		if (count > 0)
+			break;
+
+		free(svcs);
+
+		// XXX This is just a temporary hack
+		fibril_usleep(500 * 1000);
+	}
+
+	irc_sess = loc_service_connect(svcs[0], INTERFACE_IRC,
+	    IPC_FLAG_BLOCKING);
+	free(svcs);
+
+	if (irc_sess == NULL)
+		return EIO;
+
+	return EOK;
+}
+
+/** Enable interrupt.
+ *
+ * Allow interrupt delivery.
+ *
+ * @param irq	IRQ number
+ */
+errno_t irc_enable_interrupt(int irq)
+{
+	errno_t rc;
+
+	if (irc_sess == NULL) {
+		rc = irc_init();
+		if (rc != EOK)
+			return rc;
+	}
+
+	async_exch_t *exch = async_exchange_begin(irc_sess);
+	rc = async_req_1_0(exch, IRC_ENABLE_INTERRUPT, irq);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** Disable interrupt.
+ *
+ * Disallow interrupt delivery.
+ *
+ * @param irq	IRQ number
+ */
+errno_t irc_disable_interrupt(int irq)
+{
+	errno_t rc;
+
+	if (irc_sess == NULL) {
+		rc = irc_init();
+		if (rc != EOK)
+			return rc;
+	}
+
+	async_exch_t *exch = async_exchange_begin(irc_sess);
+	rc = async_req_1_0(exch, IRC_DISABLE_INTERRUPT, irq);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** Clear interrupt.
+ *
+ * Clear/acknowledge interrupt in interrupt controller so that
+ * another interrupt can be delivered.
+ *
+ * @param irq	IRQ number
+ */
+errno_t irc_clear_interrupt(int irq)
+{
+	errno_t rc;
+
+	if (irc_sess == NULL) {
+		rc = irc_init();
+		if (rc != EOK)
+			return rc;
+	}
+
+	async_exch_t *exch = async_exchange_begin(irc_sess);
+	rc = async_req_1_0(exch, IRC_CLEAR_INTERRUPT, irq);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/device/src/pci.c
===================================================================
--- uspace/lib/device/src/pci.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/src/pci.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2019 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 libdevice
+ * @{
+ */
+/** @file PCI control service API
+ */
+
+#include <abi/ipc/interfaces.h>
+#include <errno.h>
+#include <ipc/devman.h>
+#include <ipc/services.h>
+#include <ipc/pci.h>
+#include <loc.h>
+#include <pci.h>
+#include <stdlib.h>
+#include <types/pci.h>
+
+/** Open PCI service.
+ *
+ * @param svc_id PCI sevice ID
+ * @param rpci   Place to store pointer to PCI service object.
+ *
+ * @return EOK on success, ENOMEM if out of memory, EIO if service
+ *         cannot be contacted.
+ */
+errno_t pci_open(service_id_t svc_id, pci_t **rpci)
+{
+	pci_t *pci;
+	errno_t rc;
+
+	pci = calloc(1, sizeof(pci_t));
+	if (pci == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	pci->sess = loc_service_connect(svc_id, INTERFACE_PCI, 0);
+	if (pci->sess == NULL) {
+		rc = EIO;
+		goto error;
+	}
+
+	*rpci = pci;
+	return EOK;
+error:
+	free(pci);
+	return rc;
+}
+
+/** Close PCI service.
+ *
+ * @param pci PCI service object or @c NULL
+ */
+void pci_close(pci_t *pci)
+{
+	if (pci == NULL)
+		return;
+
+	async_hangup(pci->sess);
+	free(pci);
+}
+
+/** Get list of IDs into a buffer of fixed size.
+ *
+ * @param pci      PCI service
+ * @param method   IPC method
+ * @param arg1     First argument
+ * @param id_buf   Buffer to store IDs
+ * @param buf_size Buffer size
+ * @param act_size Place to store actual size of complete data.
+ *
+ * @return EOK on success or an error code.
+ */
+static errno_t pci_get_ids_once(pci_t *pci, sysarg_t method, sysarg_t arg1,
+    sysarg_t *id_buf, size_t buf_size, size_t *act_size)
+{
+	async_exch_t *exch = async_exchange_begin(pci->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, method, arg1, &answer);
+	errno_t rc = async_data_read_start(exch, id_buf, buf_size);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		return retval;
+	}
+
+	*act_size = ipc_get_arg1(&answer);
+	return EOK;
+}
+
+/** Get list of IDs.
+ *
+ * Returns an allocated array of service IDs.
+ *
+ * @param pci    PCI service
+ * @param method IPC method
+ * @param arg1   IPC argument 1
+ * @param data   Place to store pointer to array of IDs
+ * @param count  Place to store number of IDs
+ * @return       EOK on success or an error code
+ */
+static errno_t pci_get_ids_internal(pci_t *pci, sysarg_t method, sysarg_t arg1,
+    sysarg_t **data, size_t *count)
+{
+	*data = NULL;
+	*count = 0;
+
+	size_t act_size = 0;
+	errno_t rc = pci_get_ids_once(pci, method, arg1, NULL, 0, &act_size);
+	if (rc != EOK)
+		return rc;
+
+	size_t alloc_size = act_size;
+	service_id_t *ids = malloc(alloc_size);
+	if (ids == NULL)
+		return ENOMEM;
+
+	while (true) {
+		rc = pci_get_ids_once(pci, method, arg1, ids, alloc_size,
+		    &act_size);
+		if (rc != EOK)
+			return rc;
+
+		if (act_size <= alloc_size)
+			break;
+
+		alloc_size = act_size;
+		service_id_t *tmp = realloc(ids, alloc_size);
+		if (tmp == NULL) {
+			free(ids);
+			return ENOMEM;
+		}
+		ids = tmp;
+	}
+
+	*count = act_size / sizeof(service_id_t);
+	*data = ids;
+	return EOK;
+}
+
+/** Get list of PCI devices as array of devman handles.
+ *
+ * @param pci PCI service
+ * @param data Place to store pointer to array
+ * @param count Place to store length of array (number of entries)
+ *
+ * @return EOK on success or an error code
+ */
+errno_t pci_get_devices(pci_t *pci, devman_handle_t **data, size_t *count)
+{
+	return pci_get_ids_internal(pci, PCI_GET_DEVICES, 0, data, count);
+}
+
+extern errno_t pci_dev_get_info(pci_t *, devman_handle_t, pci_dev_info_t *);
+
+/** Get PCI device information.
+ *
+ * @param pci PCI service
+ * @param dev_handle Device handle
+ * @param info Place to store device information
+ * @return EOK on success or an error code
+ */
+errno_t pci_dev_get_info(pci_t *pci, devman_handle_t dev_handle,
+    pci_dev_info_t *info)
+{
+	async_exch_t *exch;
+	errno_t retval;
+	ipc_call_t answer;
+
+	exch = async_exchange_begin(pci->sess);
+	aid_t req = async_send_1(exch, PCI_DEV_GET_INFO, dev_handle, &answer);
+
+	errno_t rc = async_data_read_start(exch, info, sizeof(pci_dev_info_t));
+	async_exchange_end(exch);
+	if (rc != EOK) {
+		async_forget(req);
+		return EIO;
+	}
+
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return EIO;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/device/src/vbd.c
===================================================================
--- uspace/lib/device/src/vbd.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/src/vbd.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2015 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 libdevice
+ * @{
+ */
+/** @file Virtual Block Device client API
+ */
+
+#include <abi/ipc/interfaces.h>
+#include <errno.h>
+#include <ipc/services.h>
+#include <ipc/vbd.h>
+#include <loc.h>
+#include <macros.h>
+#include <mem.h>
+#include <stdlib.h>
+#include <types/label.h>
+#include <vbd.h>
+
+static errno_t vbd_get_ids_internal(vbd_t *, sysarg_t, sysarg_t, sysarg_t **,
+    size_t *);
+
+errno_t vbd_create(vbd_t **rvbd)
+{
+	vbd_t *vbd;
+	service_id_t vbd_svcid;
+	errno_t rc;
+
+	vbd = calloc(1, sizeof(vbd_t));
+	if (vbd == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = loc_service_get_id(SERVICE_NAME_VBD, &vbd_svcid,
+	    IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
+	vbd->sess = loc_service_connect(vbd_svcid, INTERFACE_VBD,
+	    IPC_FLAG_BLOCKING);
+	if (vbd->sess == NULL) {
+		rc = EIO;
+		goto error;
+	}
+
+	*rvbd = vbd;
+	return EOK;
+error:
+	free(vbd);
+	return rc;
+}
+
+void vbd_destroy(vbd_t *vbd)
+{
+	if (vbd == NULL)
+		return;
+
+	async_hangup(vbd->sess);
+	free(vbd);
+}
+
+/** Get list of partitions as array of service IDs.
+ *
+ * @param vbd Virtual block device service
+ * @param data Place to store pointer to array
+ * @param count Place to store length of array (number of entries)
+ *
+ * @return EOK on success or an error code
+ */
+errno_t vbd_get_disks(vbd_t *vbd, service_id_t **data, size_t *count)
+{
+	return vbd_get_ids_internal(vbd, VBD_GET_DISKS, 0, data, count);
+}
+
+/** Get disk information. */
+errno_t vbd_disk_info(vbd_t *vbd, service_id_t sid, vbd_disk_info_t *vinfo)
+{
+	async_exch_t *exch;
+	errno_t retval;
+	ipc_call_t answer;
+
+	exch = async_exchange_begin(vbd->sess);
+	aid_t req = async_send_1(exch, VBD_DISK_INFO, sid, &answer);
+	errno_t rc = async_data_read_start(exch, vinfo, sizeof(vbd_disk_info_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return EIO;
+	}
+
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return EIO;
+
+	return EOK;
+}
+
+errno_t vbd_label_create(vbd_t *vbd, service_id_t sid, label_type_t ltype)
+{
+	async_exch_t *exch;
+	errno_t retval;
+
+	exch = async_exchange_begin(vbd->sess);
+	retval = async_req_2_0(exch, VBD_LABEL_CREATE, sid, ltype);
+	async_exchange_end(exch);
+
+	if (retval != EOK)
+		return EIO;
+
+	return EOK;
+}
+
+errno_t vbd_label_delete(vbd_t *vbd, service_id_t sid)
+{
+	async_exch_t *exch;
+	errno_t retval;
+
+	exch = async_exchange_begin(vbd->sess);
+	retval = async_req_1_0(exch, VBD_LABEL_DELETE, sid);
+	async_exchange_end(exch);
+
+	if (retval != EOK)
+		return EIO;
+
+	return EOK;
+}
+
+/** Get list of IDs into a buffer of fixed size.
+ *
+ * @param vbd      Virtual Block Device
+ * @param method   IPC method
+ * @param arg1     First argument
+ * @param id_buf   Buffer to store IDs
+ * @param buf_size Buffer size
+ * @param act_size Place to store actual size of complete data.
+ *
+ * @return EOK on success or an error code.
+ */
+static errno_t vbd_get_ids_once(vbd_t *vbd, sysarg_t method, sysarg_t arg1,
+    sysarg_t *id_buf, size_t buf_size, size_t *act_size)
+{
+	async_exch_t *exch = async_exchange_begin(vbd->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, method, arg1, &answer);
+	errno_t rc = async_data_read_start(exch, id_buf, buf_size);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		return retval;
+	}
+
+	*act_size = ipc_get_arg1(&answer);
+	return EOK;
+}
+
+/** Get list of IDs.
+ *
+ * Returns an allocated array of service IDs.
+ *
+ * @param vbd    Virtual Block Device
+ * @param method IPC method
+ * @param arg1   IPC argument 1
+ * @param data   Place to store pointer to array of IDs
+ * @param count  Place to store number of IDs
+ * @return       EOK on success or an error code
+ */
+static errno_t vbd_get_ids_internal(vbd_t *vbd, sysarg_t method, sysarg_t arg1,
+    sysarg_t **data, size_t *count)
+{
+	*data = NULL;
+	*count = 0;
+
+	size_t act_size = 0;
+	errno_t rc = vbd_get_ids_once(vbd, method, arg1, NULL, 0, &act_size);
+	if (rc != EOK)
+		return rc;
+
+	size_t alloc_size = act_size;
+	service_id_t *ids = malloc(alloc_size);
+	if (ids == NULL)
+		return ENOMEM;
+
+	while (true) {
+		rc = vbd_get_ids_once(vbd, method, arg1, ids, alloc_size,
+		    &act_size);
+		if (rc != EOK)
+			return rc;
+
+		if (act_size <= alloc_size)
+			break;
+
+		alloc_size = act_size;
+		service_id_t *tmp = realloc(ids, alloc_size);
+		if (tmp == NULL) {
+			free(ids);
+			return ENOMEM;
+		}
+		ids = tmp;
+	}
+
+	*count = act_size / sizeof(service_id_t);
+	*data = ids;
+	return EOK;
+}
+
+/** Get list of disks as array of service IDs.
+ *
+ * @param vbd   Virtual Block Device
+ * @param data  Place to store pointer to array
+ * @param count Place to store length of array (number of entries)
+ *
+ * @return EOK on success or an error code
+ */
+errno_t vbd_label_get_parts(vbd_t *vbd, service_id_t disk,
+    service_id_t **data, size_t *count)
+{
+	return vbd_get_ids_internal(vbd, VBD_LABEL_GET_PARTS, disk,
+	    data, count);
+}
+
+errno_t vbd_part_get_info(vbd_t *vbd, vbd_part_id_t part, vbd_part_info_t *pinfo)
+{
+	async_exch_t *exch;
+	errno_t retval;
+	ipc_call_t answer;
+
+	exch = async_exchange_begin(vbd->sess);
+	aid_t req = async_send_1(exch, VBD_PART_GET_INFO, part, &answer);
+	errno_t rc = async_data_read_start(exch, pinfo, sizeof(vbd_part_info_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return EIO;
+	}
+
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return EIO;
+
+	return EOK;
+}
+
+errno_t vbd_part_create(vbd_t *vbd, service_id_t disk, vbd_part_spec_t *pspec,
+    vbd_part_id_t *rpart)
+{
+	async_exch_t *exch;
+	errno_t retval;
+	ipc_call_t answer;
+
+	exch = async_exchange_begin(vbd->sess);
+	aid_t req = async_send_1(exch, VBD_PART_CREATE, disk, &answer);
+	errno_t rc = async_data_write_start(exch, pspec, sizeof(vbd_part_spec_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return EIO;
+	}
+
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return EIO;
+
+	*rpart = (vbd_part_id_t)ipc_get_arg1(&answer);
+	return EOK;
+
+}
+
+errno_t vbd_part_delete(vbd_t *vbd, vbd_part_id_t part)
+{
+	async_exch_t *exch;
+	errno_t retval;
+
+	exch = async_exchange_begin(vbd->sess);
+	retval = async_req_1_0(exch, VBD_PART_DELETE, part);
+	async_exchange_end(exch);
+
+	if (retval != EOK)
+		return EIO;
+
+	return EOK;
+}
+
+void vbd_pspec_init(vbd_part_spec_t *pspec)
+{
+	memset(pspec, 0, sizeof(vbd_part_spec_t));
+}
+
+/** Suggest partition type based on partition content.
+ *
+ * @param vbd   Virtual Block Device
+ * @param disk  Disk on which the partition will be created
+ * @param pcnt  Partition content
+ * @param ptype Place to store suggested partition type
+ *
+ * @return EOK on success or an error code
+ */
+errno_t vbd_suggest_ptype(vbd_t *vbd, service_id_t disk, label_pcnt_t pcnt,
+    label_ptype_t *ptype)
+{
+	async_exch_t *exch;
+	errno_t retval;
+	ipc_call_t answer;
+
+	exch = async_exchange_begin(vbd->sess);
+	aid_t req = async_send_2(exch, VBD_SUGGEST_PTYPE, disk, pcnt, &answer);
+	errno_t rc = async_data_read_start(exch, ptype, sizeof(label_ptype_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return EIO;
+	}
+
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return EIO;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/device/src/vol.c
===================================================================
--- uspace/lib/device/src/vol.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/device/src/vol.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 2015 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 libdevice
+ * @{
+ */
+/** @file Volume service API
+ */
+
+#include <abi/ipc/interfaces.h>
+#include <errno.h>
+#include <ipc/services.h>
+#include <ipc/vol.h>
+#include <loc.h>
+#include <stdlib.h>
+#include <str.h>
+#include <vfs/vfs.h>
+#include <vol.h>
+
+/** Create Volume service session.
+ *
+ * @param rvol Place to store pointer to volume service session.
+ * @return     EOK on success, ENOMEM if out of memory, EIO if service
+ *             cannot be contacted.
+ */
+errno_t vol_create(vol_t **rvol)
+{
+	vol_t *vol;
+	service_id_t vol_svcid;
+	errno_t rc;
+
+	vol = calloc(1, sizeof(vol_t));
+	if (vol == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = loc_service_get_id(SERVICE_NAME_VOLSRV, &vol_svcid, 0);
+	if (rc != EOK) {
+		rc = ENOENT;
+		goto error;
+	}
+
+	vol->sess = loc_service_connect(vol_svcid, INTERFACE_VOL, 0);
+	if (vol->sess == NULL) {
+		rc = EIO;
+		goto error;
+	}
+
+	*rvol = vol;
+	return EOK;
+error:
+	free(vol);
+	return rc;
+}
+
+/** Destroy volume service session.
+ *
+ * @param vol Volume service session
+ */
+void vol_destroy(vol_t *vol)
+{
+	if (vol == NULL)
+		return;
+
+	async_hangup(vol->sess);
+	free(vol);
+}
+
+/** Get list of IDs into a buffer of fixed size.
+ *
+ * @param vol      Volume service
+ * @param method   IPC method
+ * @param arg1     First argument
+ * @param id_buf   Buffer to store IDs
+ * @param buf_size Buffer size
+ * @param act_size Place to store actual size of complete data.
+ *
+ * @return EOK on success or an error code.
+ */
+static errno_t vol_get_ids_once(vol_t *vol, sysarg_t method, sysarg_t arg1,
+    sysarg_t *id_buf, size_t buf_size, size_t *act_size)
+{
+	async_exch_t *exch = async_exchange_begin(vol->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, method, arg1, &answer);
+	errno_t rc = async_data_read_start(exch, id_buf, buf_size);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		return retval;
+	}
+
+	*act_size = ipc_get_arg1(&answer);
+	return EOK;
+}
+
+/** Get list of IDs.
+ *
+ * Returns an allocated array of service IDs.
+ *
+ * @param vol    Volume service
+ * @param method IPC method
+ * @param arg1   IPC argument 1
+ * @param data   Place to store pointer to array of IDs
+ * @param count  Place to store number of IDs
+ * @return       EOK on success or an error code
+ */
+static errno_t vol_get_ids_internal(vol_t *vol, sysarg_t method, sysarg_t arg1,
+    sysarg_t **data, size_t *count)
+{
+	*data = NULL;
+	*count = 0;
+
+	size_t act_size = 0;
+	errno_t rc = vol_get_ids_once(vol, method, arg1, NULL, 0, &act_size);
+	if (rc != EOK)
+		return rc;
+
+	size_t alloc_size = act_size;
+	service_id_t *ids = malloc(alloc_size);
+	if (ids == NULL)
+		return ENOMEM;
+
+	while (true) {
+		rc = vol_get_ids_once(vol, method, arg1, ids, alloc_size,
+		    &act_size);
+		if (rc != EOK)
+			return rc;
+
+		if (act_size <= alloc_size)
+			break;
+
+		alloc_size = act_size;
+		service_id_t *temp = realloc(ids, alloc_size);
+		if (temp == NULL) {
+			free(ids);
+			return ENOMEM;
+		}
+		ids = temp;
+	}
+
+	*count = act_size / sizeof(service_id_t);
+	*data = ids;
+	return EOK;
+}
+
+/** Get list of partitions as array of service IDs.
+ *
+ * @param vol Volume service
+ * @param data Place to store pointer to array
+ * @param count Place to store length of array (number of entries)
+ *
+ * @return EOK on success or an error code
+ */
+errno_t vol_get_parts(vol_t *vol, service_id_t **data, size_t *count)
+{
+	return vol_get_ids_internal(vol, VOL_GET_PARTS, 0, data, count);
+}
+
+/** Add partition.
+ *
+ * After a partition is created (e.g. as a result of deleting a label
+ * the dummy partition is created), it can take some (unknown) time
+ * until it is discovered.
+ *
+ * @param vol Volume service
+ * @param sid Service ID of the partition
+ * @return EOK on success or an error code
+ */
+errno_t vol_part_add(vol_t *vol, service_id_t sid)
+{
+	async_exch_t *exch;
+	errno_t retval;
+
+	exch = async_exchange_begin(vol->sess);
+	retval = async_req_1_0(exch, VOL_PART_ADD, sid);
+	async_exchange_end(exch);
+
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+/** Get partition information.
+ *
+ * @param vol Volume service
+ * @param sid Service ID of the partition
+ * @param vinfo Place to store partition information
+ * @return EOK on success or an error code
+ */
+errno_t vol_part_info(vol_t *vol, service_id_t sid, vol_part_info_t *vinfo)
+{
+	async_exch_t *exch;
+	errno_t retval;
+	ipc_call_t answer;
+
+	exch = async_exchange_begin(vol->sess);
+	aid_t req = async_send_1(exch, VOL_PART_INFO, sid, &answer);
+
+	errno_t rc = async_data_read_start(exch, vinfo, sizeof(vol_part_info_t));
+	async_exchange_end(exch);
+	if (rc != EOK) {
+		async_forget(req);
+		return EIO;
+	}
+
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return EIO;
+
+	return EOK;
+}
+
+/** Unmount partition (and possibly eject the media).
+ *
+ * @param vol Volume service
+ * @param sid Service ID of the partition
+ * @return EOK on success or an error code
+ */
+errno_t vol_part_eject(vol_t *vol, service_id_t sid)
+{
+	async_exch_t *exch;
+	errno_t retval;
+
+	exch = async_exchange_begin(vol->sess);
+	retval = async_req_1_0(exch, VOL_PART_EJECT, sid);
+	async_exchange_end(exch);
+
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+/** Erase partition (to the extent where we will consider it not containing
+ * a file system.
+ *
+ * @param vol Volume service
+ * @param sid Service ID of the partition
+ * @return EOK on success or an error code
+ */
+errno_t vol_part_empty(vol_t *vol, service_id_t sid)
+{
+	async_exch_t *exch;
+	errno_t retval;
+
+	exch = async_exchange_begin(vol->sess);
+	retval = async_req_1_0(exch, VOL_PART_EMPTY, sid);
+	async_exchange_end(exch);
+
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+/** Insert volume.
+ *
+ * This will re-mount the volume if it has been ejected previously.
+ *
+ * @param vol Volume service
+ * @param sid Service ID of the partition
+ * @return EOK on success or an error code
+ */
+errno_t vol_part_insert(vol_t *vol, service_id_t sid)
+{
+	async_exch_t *exch;
+	errno_t retval;
+
+	exch = async_exchange_begin(vol->sess);
+	retval = async_req_1_0(exch, VOL_PART_INSERT, sid);
+	async_exchange_end(exch);
+
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+/** Insert volume by path.
+ *
+ * @param vol Volume service
+ * @param path Filesystem path
+ *
+ * @return EOK on success or an error code
+ */
+errno_t vol_part_insert_by_path(vol_t *vol, const char *path)
+{
+	async_exch_t *exch;
+	ipc_call_t answer;
+	errno_t retval;
+
+	exch = async_exchange_begin(vol->sess);
+	aid_t req = async_send_0(exch, VOL_PART_INSERT_BY_PATH, &answer);
+
+	retval = async_data_write_start(exch, path, str_size(path));
+	if (retval != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return retval;
+	}
+
+	async_exchange_end(exch);
+	async_wait_for(req, &retval);
+
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+/** Get volume label support.
+ *
+ * @param vol Volume service
+ * @param fstype File system type
+ * @param vlsupp Place to store volume label support information
+ * @return EOK on success or an error code
+ */
+errno_t vol_part_get_lsupp(vol_t *vol, vol_fstype_t fstype,
+    vol_label_supp_t *vlsupp)
+{
+	async_exch_t *exch;
+	errno_t retval;
+	ipc_call_t answer;
+
+	exch = async_exchange_begin(vol->sess);
+	aid_t req = async_send_1(exch, VOL_PART_LSUPP, fstype, &answer);
+	errno_t rc = async_data_read_start(exch, vlsupp, sizeof(vol_label_supp_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return EIO;
+	}
+
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return EIO;
+
+	return EOK;
+}
+
+/** Create file system.
+ *
+ * @param vol Volume service
+ * @param sid Partition service ID
+ * @param fstype File system type
+ * @param label Volume label
+ * @param mountp Mount point
+ *
+ * @return EOK on success or an error code
+ */
+errno_t vol_part_mkfs(vol_t *vol, service_id_t sid, vol_fstype_t fstype,
+    const char *label, const char *mountp)
+{
+	async_exch_t *exch;
+	ipc_call_t answer;
+	errno_t retval;
+
+	exch = async_exchange_begin(vol->sess);
+	aid_t req = async_send_2(exch, VOL_PART_MKFS, sid, fstype, &answer);
+
+	retval = async_data_write_start(exch, label, str_size(label));
+	if (retval != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return retval;
+	}
+
+	retval = async_data_write_start(exch, mountp, str_size(mountp));
+	if (retval != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return retval;
+	}
+
+	async_exchange_end(exch);
+	async_wait_for(req, &retval);
+
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+/** Set mount point for partition.
+ *
+ * @param vol Volume service
+ * @param sid Partition service ID
+ * @param mountp Mount point
+ *
+ * @return EOK on success or an error code
+ */
+errno_t vol_part_set_mountp(vol_t *vol, service_id_t sid,
+    const char *mountp)
+{
+	async_exch_t *exch;
+	ipc_call_t answer;
+	errno_t retval;
+
+	exch = async_exchange_begin(vol->sess);
+	aid_t req = async_send_1(exch, VOL_PART_SET_MOUNTP, sid,
+	    &answer);
+
+	retval = async_data_write_start(exch, mountp, str_size(mountp));
+	if (retval != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return retval;
+	}
+
+	async_exchange_end(exch);
+	async_wait_for(req, &retval);
+
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+/** Format file system type as string.
+ *
+ * @param fstype File system type
+ * @param rstr Place to store pointer to newly allocated string
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t vol_fstype_format(vol_fstype_t fstype, char **rstr)
+{
+	const char *sfstype;
+	char *s;
+
+	sfstype = NULL;
+	switch (fstype) {
+	case fs_exfat:
+		sfstype = "ExFAT";
+		break;
+	case fs_fat:
+		sfstype = "FAT";
+		break;
+	case fs_minix:
+		sfstype = "MINIX";
+		break;
+	case fs_ext4:
+		sfstype = "Ext4";
+		break;
+	case fs_cdfs:
+		sfstype = "ISO 9660";
+		break;
+	}
+
+	s = str_dup(sfstype);
+	if (s == NULL)
+		return ENOMEM;
+
+	*rstr = s;
+	return EOK;
+}
+
+/** Format partition content / file system type as string.
+ *
+ * @param pcnt Partition content
+ * @param fstype File system type
+ * @param rstr Place to store pointer to newly allocated string
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t vol_pcnt_fs_format(vol_part_cnt_t pcnt, vol_fstype_t fstype,
+    char **rstr)
+{
+	int rc;
+	char *s = NULL;
+
+	switch (pcnt) {
+	case vpc_empty:
+		s = str_dup("Empty");
+		if (s == NULL)
+			return ENOMEM;
+		break;
+	case vpc_fs:
+		rc = vol_fstype_format(fstype, &s);
+		if (rc != EOK)
+			return ENOMEM;
+		break;
+	case vpc_unknown:
+		s = str_dup("Unknown");
+		if (s == NULL)
+			return ENOMEM;
+		break;
+	}
+
+	assert(s != NULL);
+	*rstr = s;
+	return EOK;
+}
+
+/** Validate mount point.
+ *
+ * Verify that mount point is valid. A valid mount point is
+ * one of:
+ *  - 'Auto'
+ *  - 'None'
+ *  - /path (string beginning with '/') to an existing directory
+ *
+ * @return EOK if mount point is in valid, EINVAL if the format is invalid,
+ *         ENOENT if the directory does not exist
+ */
+errno_t vol_mountp_validate(const char *mountp)
+{
+	errno_t rc;
+	vfs_stat_t stat;
+
+	if (str_cmp(mountp, "Auto") == 0 || str_cmp(mountp, "auto") == 0)
+		return EOK;
+
+	if (str_casecmp(mountp, "None") == 0 || str_cmp(mountp, "none") == 0)
+		return EOK;
+
+	if (mountp[0] == '/') {
+		rc = vfs_stat_path(mountp, &stat);
+		if (rc != EOK || !stat.is_directory)
+			return ENOENT;
+
+		return EOK;
+	}
+
+	return EINVAL;
+}
+
+/** Get list of volumes as array of volume IDs.
+ *
+ * @param vol Volume service
+ * @param data Place to store pointer to array
+ * @param count Place to store length of array (number of entries)
+ *
+ * @return EOK on success or an error code
+ */
+errno_t vol_get_volumes(vol_t *vol, volume_id_t **data, size_t *count)
+{
+	return vol_get_ids_internal(vol, VOL_GET_VOLUMES, 0,
+	    (sysarg_t **) data, count);
+}
+
+/** Get volume configuration information.
+ *
+ * @param vol Volume service
+ * @param vid Volume ID
+ * @param vinfo Place to store volume configuration information
+ * @return EOK on success or an error code
+ */
+errno_t vol_info(vol_t *vol, volume_id_t vid, vol_info_t *vinfo)
+{
+	async_exch_t *exch;
+	errno_t retval;
+	ipc_call_t answer;
+
+	exch = async_exchange_begin(vol->sess);
+	aid_t req = async_send_1(exch, VOL_INFO, vid.id, &answer);
+
+	errno_t rc = async_data_read_start(exch, vinfo, sizeof(vol_info_t));
+	async_exchange_end(exch);
+	if (rc != EOK) {
+		async_forget(req);
+		return EIO;
+	}
+
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return EIO;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/display/include/disp_srv.h
===================================================================
--- uspace/lib/display/include/disp_srv.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/display/include/disp_srv.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -61,4 +61,5 @@
 	    gfx_coord2_t *);
 	errno_t (*window_move)(void *, sysarg_t, gfx_coord2_t *);
+	errno_t (*window_get_pos)(void *, sysarg_t, gfx_coord2_t *);
 	errno_t (*window_resize)(void *, sysarg_t, gfx_coord2_t *, gfx_rect_t *);
 	errno_t (*window_set_cursor)(void *, sysarg_t, display_stock_cursor_t);
Index: uspace/lib/display/include/display.h
===================================================================
--- uspace/lib/display/include/display.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/display/include/display.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -58,4 +58,5 @@
     display_wnd_rsztype_t, gfx_coord2_t *);
 extern errno_t display_window_move(display_window_t *, gfx_coord2_t *);
+extern errno_t display_window_get_pos(display_window_t *, gfx_coord2_t *);
 extern errno_t display_window_resize(display_window_t *,
     gfx_coord2_t *, gfx_rect_t *);
Index: uspace/lib/display/include/ipc/display.h
===================================================================
--- uspace/lib/display/include/ipc/display.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/display/include/ipc/display.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -33,6 +33,6 @@
  */
 
-#ifndef _LIBC_IPC_DISPLAY_H_
-#define _LIBC_IPC_DISPLAY_H_
+#ifndef LIBDISPLAY_IPC_DISPLAY_H
+#define LIBDISPLAY_IPC_DISPLAY_H
 
 #include <ipc/common.h>
@@ -44,4 +44,5 @@
 	DISPLAY_WINDOW_MOVE,
 	DISPLAY_WINDOW_MOVE_REQ,
+	DISPLAY_WINDOW_GET_POS,
 	DISPLAY_WINDOW_RESIZE,
 	DISPLAY_WINDOW_RESIZE_REQ,
Index: uspace/lib/display/include/types/display/cursor.h
===================================================================
--- uspace/lib/display/include/types/display/cursor.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/display/include/types/display/cursor.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -47,10 +47,12 @@
 	dcurs_size_uldr,
 	/** Double arrow pointing up-right nad down-left */
-	dcurs_size_urdl
+	dcurs_size_urdl,
+	/** I-beam (suggests editable text) */
+	dcurs_ibeam
 } display_stock_cursor_t;
 
 enum {
 	/** Number of stock cursor types */
-	dcurs_limit = dcurs_size_urdl + 1
+	dcurs_limit = dcurs_ibeam + 1
 };
 
Index: uspace/lib/display/include/types/display/wndparams.h
===================================================================
--- uspace/lib/display/include/types/display/wndparams.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/display/include/types/display/wndparams.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,4 +38,12 @@
 #include <gfx/coord.h>
 
+/** Window flags */
+typedef enum {
+	/** Popup window (capture events, no focus) */
+	wndf_popup = 0x1,
+	/** Set specific initial window position */
+	wndf_setpos = 0x2
+} display_wnd_flags_t;
+
 /** Parameters for a new window.
  *
@@ -50,4 +58,8 @@
 	/** Minimum size (when being resized) */
 	gfx_coord2_t min_size;
+	/** Initial position (if flag wndf_setpos is set) */
+	gfx_coord2_t pos;
+	/** Flags */
+	display_wnd_flags_t flags;
 } display_wnd_params_t;
 
Index: uspace/lib/display/src/disp_srv.c
===================================================================
--- uspace/lib/display/src/disp_srv.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/display/src/disp_srv.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -187,4 +187,47 @@
 }
 
+static void display_window_get_pos_srv(display_srv_t *srv, ipc_call_t *icall)
+{
+	sysarg_t wnd_id;
+	ipc_call_t call;
+	gfx_coord2_t dpos;
+	size_t size;
+	errno_t rc;
+
+	wnd_id = ipc_get_arg1(icall);
+
+	if (srv->ops->window_get_pos == NULL) {
+		async_answer_0(icall, ENOTSUP);
+		return;
+	}
+
+	if (!async_data_read_receive(&call, &size)) {
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	rc = srv->ops->window_get_pos(srv->arg, wnd_id, &dpos);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	if (size != sizeof(gfx_coord2_t)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = async_data_read_finalize(&call, &dpos, size);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	async_answer_0(icall, EOK);
+}
+
 static void display_window_resize_req_srv(display_srv_t *srv, ipc_call_t *icall)
 {
@@ -398,4 +441,7 @@
 		case DISPLAY_WINDOW_MOVE:
 			display_window_move_srv(srv, &call);
+			break;
+		case DISPLAY_WINDOW_GET_POS:
+			display_window_get_pos_srv(srv, &call);
 			break;
 		case DISPLAY_WINDOW_RESIZE_REQ:
Index: uspace/lib/display/src/display.c
===================================================================
--- uspace/lib/display/src/display.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/display/src/display.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -319,4 +319,35 @@
 }
 
+/** Get display window position.
+ *
+ * Get display window position on the display.
+ *
+ * @param window Window
+ * @param dpos Place to store position
+ * @return EOK on success or an error code
+ */
+errno_t display_window_get_pos(display_window_t *window, gfx_coord2_t *dpos)
+{
+	async_exch_t *exch;
+	aid_t req;
+	ipc_call_t answer;
+	errno_t rc;
+
+	exch = async_exchange_begin(window->display->sess);
+	req = async_send_1(exch, DISPLAY_WINDOW_GET_POS, window->id, &answer);
+	rc = async_data_read_start(exch, dpos, sizeof (gfx_coord2_t));
+	async_exchange_end(exch);
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
 /** Request a window resize.
  *
Index: uspace/lib/display/test/display.c
===================================================================
--- uspace/lib/display/test/display.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/display/test/display.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -59,4 +59,5 @@
 static errno_t test_window_move_req(void *, sysarg_t, gfx_coord2_t *);
 static errno_t test_window_move(void *, sysarg_t, gfx_coord2_t *);
+static errno_t test_window_get_pos(void *, sysarg_t, gfx_coord2_t *);
 static errno_t test_window_resize_req(void *, sysarg_t, display_wnd_rsztype_t,
     gfx_coord2_t *);
@@ -74,4 +75,5 @@
 	.window_move_req = test_window_move_req,
 	.window_move = test_window_move,
+	.window_get_pos = test_window_get_pos,
 	.window_resize_req = test_window_resize_req,
 	.window_resize = test_window_resize,
@@ -115,4 +117,8 @@
 	sysarg_t move_wnd_id;
 	gfx_coord2_t move_dpos;
+
+	bool window_get_pos_called;
+	sysarg_t get_pos_wnd_id;
+	gfx_coord2_t get_pos_rpos;
 
 	bool window_resize_req_called;
@@ -556,4 +562,116 @@
 }
 
+/** display_window_get_pos() with server returning error response works. */
+PCUT_TEST(window_get_pos_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	display_t *disp = NULL;
+	display_wnd_params_t params;
+	display_window_t *wnd;
+	gfx_coord2_t dpos;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_display_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_display_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_display_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = display_open(test_display_svc, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(disp);
+
+	resp.rc = EOK;
+	display_wnd_params_init(&params);
+	params.rect.p0.x = 0;
+	params.rect.p0.y = 0;
+	params.rect.p0.x = 100;
+	params.rect.p0.y = 100;
+
+	rc = display_window_create(disp, &params, &test_display_wnd_cb,
+	    (void *) &resp, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wnd);
+
+	resp.rc = EIO;
+	resp.window_get_pos_called = false;
+
+	dpos.x = 0;
+	dpos.y = 0;
+
+	rc = display_window_get_pos(wnd, &dpos);
+	PCUT_ASSERT_TRUE(resp.window_get_pos_called);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_INT_EQUALS(wnd->id, resp.get_pos_wnd_id);
+	PCUT_ASSERT_INT_EQUALS(0, dpos.x);
+	PCUT_ASSERT_INT_EQUALS(0, dpos.y);
+
+	display_window_destroy(wnd);
+	display_close(disp);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** display_window_get_pos() with server returning success response works. */
+PCUT_TEST(window_get_pos_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	display_t *disp = NULL;
+	display_wnd_params_t params;
+	display_window_t *wnd;
+	gfx_coord2_t dpos;
+	test_response_t resp;
+
+	async_set_fallback_port_handler(test_display_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_display_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_display_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = display_open(test_display_svc, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(disp);
+
+	resp.rc = EOK;
+	display_wnd_params_init(&params);
+	params.rect.p0.x = 0;
+	params.rect.p0.y = 0;
+	params.rect.p0.x = 100;
+	params.rect.p0.y = 100;
+
+	rc = display_window_create(disp, &params, &test_display_wnd_cb,
+	    (void *) &resp, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(wnd);
+
+	resp.rc = EOK;
+	resp.window_get_pos_called = false;
+	resp.get_pos_rpos.x = 11;
+	resp.get_pos_rpos.y = 12;
+
+	dpos.x = 0;
+	dpos.y = 0;
+
+	rc = display_window_get_pos(wnd, &dpos);
+	PCUT_ASSERT_TRUE(resp.window_get_pos_called);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_INT_EQUALS(wnd->id, resp.get_pos_wnd_id);
+	PCUT_ASSERT_INT_EQUALS(resp.get_pos_rpos.x, dpos.x);
+	PCUT_ASSERT_INT_EQUALS(resp.get_pos_rpos.y, dpos.y);
+
+	display_window_destroy(wnd);
+	display_close(disp);
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
 /** display_window_resize_req() with server returning error response works. */
 PCUT_TEST(window_resize_req_failure)
@@ -937,6 +1055,5 @@
 	resp.rc = ENOMEM;
 	rc = display_window_get_gc(wnd, &gc);
-	/* async_connect_me_to() does not return specific error */
-	PCUT_ASSERT_ERRNO_VAL(EIO, rc);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
 	PCUT_ASSERT_NULL(gc);
 
@@ -1589,4 +1706,17 @@
 }
 
+static errno_t test_window_get_pos(void *arg, sysarg_t wnd_id, gfx_coord2_t *dpos)
+{
+	test_response_t *resp = (test_response_t *) arg;
+
+	resp->window_get_pos_called = true;
+	resp->get_pos_wnd_id = wnd_id;
+
+	if (resp->rc == EOK)
+		*dpos = resp->get_pos_rpos;
+
+	return resp->rc;
+}
+
 static errno_t test_window_resize_req(void *arg, sysarg_t wnd_id,
     display_wnd_rsztype_t rsztype, gfx_coord2_t *pos)
Index: uspace/lib/drv/generic/remote_ieee80211.c
===================================================================
--- uspace/lib/drv/generic/remote_ieee80211.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/drv/generic/remote_ieee80211.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2015 Jan Kolarik
  * All rights reserved.
@@ -84,19 +85,13 @@
 }
 
-static bool mac_matches(uint8_t *mac1, uint8_t *mac2)
-{
-	for (size_t i = 0; i < ETH_ADDR; i++) {
-		if (mac1[i] != mac2[i])
-			return false;
-	}
-
-	return true;
-}
-
+// XXX This is wrong. Wifi should not have anything to do with IP links
 static sysarg_t get_link_id(uint8_t *mac)
 {
 	sysarg_t *link_list;
 	inet_link_info_t link_info;
+	eth_addr_t eth_addr;
 	size_t count;
+
+	eth_addr_decode(mac, &eth_addr);
 
 	errno_t rc = inetcfg_get_link_list(&link_list, &count);
@@ -109,5 +104,5 @@
 			return -1;
 
-		if (mac_matches(mac, link_info.mac_addr))
+		if (eth_addr_compare(&eth_addr, &link_info.mac_addr) == 0)
 			return link_list[i];
 	}
@@ -170,4 +165,6 @@
 		return rc;
 
+	// XXX This is wrong. Wifi should not initiate DHCP
+
 	/* Send DHCP discover. */
 	nic_address_t wifi_mac;
@@ -207,4 +204,7 @@
 	if (rc != EOK)
 		return rc;
+
+	eth_addr_t eth_addr;
+	eth_addr_decode(wifi_mac.address, &eth_addr);
 
 	inet_link_info_t link_info;
@@ -215,4 +215,6 @@
 	size_t count;
 
+	/// XXX This is wrong. Wifi should do nothing with DHCP
+
 	/* Remove previous DHCP address. */
 	rc = inetcfg_get_addr_list(&addr_list, &count);
@@ -229,5 +231,5 @@
 			return rc;
 
-		if (mac_matches(wifi_mac.address, link_info.mac_addr)) {
+		if (eth_addr_compare(&eth_addr, &link_info.mac_addr) == 0) {
 			if (str_test_prefix(addr_info.name, "dhcp")) {
 				rc = inetcfg_addr_delete(addr_list[i]);
Index: uspace/lib/drv/include/battery_iface.h
===================================================================
--- uspace/lib/drv/include/battery_iface.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/drv/include/battery_iface.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -27,5 +27,5 @@
  */
 
-/** @addtogroup libc
+/** @addtogroup libdrv
  * @{
  */
Index: uspace/lib/drv/include/ieee80211_iface.h
===================================================================
--- uspace/lib/drv/include/ieee80211_iface.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/drv/include/ieee80211_iface.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -27,5 +27,5 @@
  */
 
-/** @addtogroup libc
+/** @addtogroup libdrv
  * @{
  */
Index: uspace/lib/drv/include/nic_iface.h
===================================================================
--- uspace/lib/drv/include/nic_iface.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/drv/include/nic_iface.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -27,5 +27,5 @@
  */
 
-/** @addtogroup libc
+/** @addtogroup libdrv
  * @{
  */
Index: uspace/lib/drv/meson.build
===================================================================
--- uspace/lib/drv/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/drv/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -28,5 +28,5 @@
 #
 
-deps = [ 'pcm' ]
+deps = [ 'inet', 'pcm', 'device' ]
 private_includes += include_directories('generic/private')
 src = files(
Index: uspace/lib/fdisk/meson.build
===================================================================
--- uspace/lib/fdisk/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/fdisk/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'device' ]
 src = files('src/fdisk.c')
Index: uspace/lib/fdisk/src/fdisk.c
===================================================================
--- uspace/lib/fdisk/src/fdisk.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/fdisk/src/fdisk.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -857,5 +857,5 @@
 	}
 
-	if (nidx > 4 /* XXXX actual number of slots*/) {
+	if (nidx > 4 /* XXXX actual number of slots */) {
 		return ELIMIT;
 	}
Index: uspace/lib/fmtutil/fmtutil.c
===================================================================
--- uspace/lib/fmtutil/fmtutil.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/fmtutil/fmtutil.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -217,5 +217,5 @@
 			line_len = 0;
 		}
-		/* Check if we need to force wrap of long word*/
+		/* Check if we need to force wrap of long word */
 		if (pos - word_start > width) {
 			consumer(wstr + word_start, width, last, data);
Index: uspace/lib/gfx/include/gfx/cursor.h
===================================================================
--- uspace/lib/gfx/include/gfx/cursor.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/gfx/include/gfx/cursor.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021 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 libgfx
+ * @{
+ */
+/**
+ * @file Hardware cursor control
+ */
+
+#ifndef _GFX_CURSOR_H
+#define _GFX_CURSOR_H
+
+#include <errno.h>
+#include <types/gfx/coord.h>
+#include <types/gfx/context.h>
+#include <stdbool.h>
+
+extern errno_t gfx_cursor_get_pos(gfx_context_t *, gfx_coord2_t *);
+extern errno_t gfx_cursor_set_pos(gfx_context_t *, gfx_coord2_t *);
+extern errno_t gfx_cursor_set_visible(gfx_context_t *, bool);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/gfx/include/gfx/render.h
===================================================================
--- uspace/lib/gfx/include/gfx/render.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfx/include/gfx/render.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -42,6 +42,8 @@
 #include <types/gfx/context.h>
 
+extern errno_t gfx_set_clip_rect(gfx_context_t *, gfx_rect_t *);
 extern errno_t gfx_set_color(gfx_context_t *, gfx_color_t *);
 extern errno_t gfx_fill_rect(gfx_context_t *, gfx_rect_t *);
+extern errno_t gfx_update(gfx_context_t *);
 
 #endif
Index: uspace/lib/gfx/include/types/gfx/ops/context.h
===================================================================
--- uspace/lib/gfx/include/types/gfx/ops/context.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfx/include/types/gfx/ops/context.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -44,11 +44,16 @@
 #include <types/gfx/coord.h>
 #include <types/gfx/context.h>
+#include <stdbool.h>
 
 /** Graphics context ops */
 typedef struct {
+	/** Set clipping rectangle */
+	errno_t (*set_clip_rect)(void *, gfx_rect_t *);
 	/** Set drawing color */
 	errno_t (*set_color)(void *, gfx_color_t *);
 	/** Fill rectangle using the current drawing color */
 	errno_t (*fill_rect)(void *, gfx_rect_t *);
+	/** Update display */
+	errno_t (*update)(void *);
 	/** Create bitmap */
 	errno_t (*bitmap_create)(void *, gfx_bitmap_params_t *,
@@ -60,4 +65,10 @@
 	/** Get bitmap allocation info */
 	errno_t (*bitmap_get_alloc)(void *, gfx_bitmap_alloc_t *);
+	/** Get hardware cursor position */
+	errno_t (*cursor_get_pos)(void *, gfx_coord2_t *);
+	/** Set hardware cursor position */
+	errno_t (*cursor_set_pos)(void *, gfx_coord2_t *);
+	/** Set hardware cursor visibility */
+	errno_t (*cursor_set_visible)(void *, bool);
 } gfx_context_ops_t;
 
Index: uspace/lib/gfx/meson.build
===================================================================
--- uspace/lib/gfx/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfx/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2019 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -32,4 +32,5 @@
 	'src/coord.c',
 	'src/context.c',
+	'src/cursor.c',
 	'src/render.c'
 )
@@ -39,4 +40,5 @@
 	'test/color.c',
 	'test/coord.c',
+	'test/cursor.c',
 	'test/main.c',
 	'test/render.c',
Index: uspace/lib/gfx/src/cursor.c
===================================================================
--- uspace/lib/gfx/src/cursor.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/gfx/src/cursor.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2021 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 libgfx
+ * @{
+ */
+/**
+ * @file Hardware cursor control
+ */
+
+#include <errno.h>
+#include <gfx/context.h>
+#include <gfx/coord.h>
+#include <gfx/cursor.h>
+#include <gfx/render.h>
+#include <stdbool.h>
+#include "../private/context.h"
+
+/** Get hardware cursor position.
+ *
+ * @param gc Graphic context
+ * @param pos Place to store cursor position
+ *
+ * @return EOK on success, ENOTSUP if not supported,
+ *         EIO if grahic device connection was lost
+ */
+errno_t gfx_cursor_get_pos(gfx_context_t *gc, gfx_coord2_t *pos)
+{
+	if (gc->ops->cursor_get_pos != NULL)
+		return gc->ops->cursor_get_pos(gc->arg, pos);
+	else
+		return ENOTSUP;
+}
+
+/** Set hardware cursor position.
+ *
+ * @param gc Graphic context
+ * @param pos New cursor position
+ *
+ * @return EOK on success, ENOTSUP if not supported,
+ *         EIO if grahic device connection was lost
+ */
+errno_t gfx_cursor_set_pos(gfx_context_t *gc, gfx_coord2_t *pos)
+{
+	if (gc->ops->cursor_set_pos != NULL)
+		return gc->ops->cursor_set_pos(gc->arg, pos);
+	else
+		return ENOTSUP;
+}
+
+/** Set hardware cursor visibility.
+ *
+ * @param gc Graphic context
+ * @param visible @c true iff cursor should be made visible
+ *
+ * @return EOK on success, ENOTSUP if not supported,
+ *         EIO if grahic device connection was lost
+ */
+errno_t gfx_cursor_set_visible(gfx_context_t *gc, bool visible)
+{
+	if (gc->ops->cursor_set_visible != NULL)
+		return gc->ops->cursor_set_visible(gc->arg, visible);
+	else
+		return ENOTSUP;
+}
+
+/** @}
+ */
Index: uspace/lib/gfx/src/render.c
===================================================================
--- uspace/lib/gfx/src/render.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfx/src/render.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -37,4 +37,17 @@
 #include "../private/context.h"
 
+/** Set clipping rectangle.
+ *
+ * @param gc Graphic context
+ * @param rect Rectangle or @c NULL (no extra clipping)
+ *
+ * @return EOK on success, ENOMEM if insufficient resources,
+ *         EIO if grahic device connection was lost
+ */
+errno_t gfx_set_clip_rect(gfx_context_t *gc, gfx_rect_t *rect)
+{
+	return gc->ops->set_clip_rect(gc->arg, rect);
+}
+
 /** Set drawing color.
  *
@@ -63,4 +76,18 @@
 }
 
+/** Update display.
+ *
+ * Finish any deferred rendering.
+ *
+ * @param gc Graphic context
+ *
+ * @return EOK on success, ENOMEM if insufficient resources,
+ *         EIO if grahic device connection was lost
+ */
+errno_t gfx_update(gfx_context_t *gc)
+{
+	return gc->ops->update(gc->arg);
+}
+
 /** @}
  */
Index: uspace/lib/gfx/test/coord.c
===================================================================
--- uspace/lib/gfx/test/coord.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfx/test/coord.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -387,5 +387,5 @@
 }
 
-/** Rectangle envelope, b is inside a*/
+/** Rectangle envelope, b is inside a */
 PCUT_TEST(rect_envelope_nonempty_b_inside_a)
 {
Index: uspace/lib/gfx/test/cursor.c
===================================================================
--- uspace/lib/gfx/test/cursor.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/gfx/test/cursor.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2021 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 <gfx/context.h>
+#include <gfx/cursor.h>
+#include <pcut/pcut.h>
+#include <mem.h>
+#include <stdbool.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(cursor);
+
+static errno_t testgc_cursor_get_pos(void *, gfx_coord2_t *);
+static errno_t testgc_cursor_set_pos(void *, gfx_coord2_t *);
+static errno_t testgc_cursor_set_visible(void *, bool);
+
+static gfx_context_ops_t ops = {
+	.cursor_get_pos = testgc_cursor_get_pos,
+	.cursor_set_pos = testgc_cursor_set_pos,
+	.cursor_set_visible = testgc_cursor_set_visible
+};
+
+/** Test graphics context data */
+typedef struct {
+	errno_t rc;
+
+	bool cursor_get_pos;
+	gfx_coord2_t get_pos_pos;
+
+	bool cursor_set_pos;
+	gfx_coord2_t set_pos_pos;
+
+	bool cursor_set_visible;
+	bool set_visible_vis;
+} test_gc_t;
+
+/** Get hardware cursor position with error return */
+PCUT_TEST(cursor_get_pos_failure)
+{
+	errno_t rc;
+	gfx_coord2_t pos;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	tgc.rc = EIO;
+
+	rc = gfx_cursor_get_pos(gc, &pos);
+	PCUT_ASSERT_ERRNO_VAL(tgc.rc, rc);
+
+	PCUT_ASSERT_TRUE(tgc.cursor_get_pos);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Get hardware cursor position */
+PCUT_TEST(cursor_get_pos_success)
+{
+	errno_t rc;
+	gfx_coord2_t pos;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	tgc.rc = EOK;
+	tgc.get_pos_pos.x = 1;
+	tgc.get_pos_pos.y = 2;
+
+	rc = gfx_cursor_get_pos(gc, &pos);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_TRUE(tgc.cursor_get_pos);
+	PCUT_ASSERT_INT_EQUALS(tgc.get_pos_pos.x, pos.x);
+	PCUT_ASSERT_INT_EQUALS(tgc.get_pos_pos.y, pos.y);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Set hardware cursor position with error return */
+PCUT_TEST(cursor_set_pos_failure)
+{
+	errno_t rc;
+	gfx_coord2_t pos;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	tgc.rc = EIO;
+	pos.x = 1;
+	pos.y = 2;
+
+	rc = gfx_cursor_set_pos(gc, &pos);
+	PCUT_ASSERT_ERRNO_VAL(tgc.rc, rc);
+
+	PCUT_ASSERT_TRUE(tgc.cursor_set_pos);
+	PCUT_ASSERT_INT_EQUALS(pos.x, tgc.set_pos_pos.x);
+	PCUT_ASSERT_INT_EQUALS(pos.y, tgc.set_pos_pos.y);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Set hardware cursor position */
+PCUT_TEST(cursor_set_pos_success)
+{
+	errno_t rc;
+	gfx_coord2_t pos;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	tgc.rc = EOK;
+	pos.x = 1;
+	pos.y = 2;
+
+	rc = gfx_cursor_set_pos(gc, &pos);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_TRUE(tgc.cursor_set_pos);
+	PCUT_ASSERT_INT_EQUALS(pos.x, tgc.set_pos_pos.x);
+	PCUT_ASSERT_INT_EQUALS(pos.y, tgc.set_pos_pos.y);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Set hardware cursor visibility with error return */
+PCUT_TEST(cursor_set_visible_failure)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	tgc.rc = EIO;
+
+	rc = gfx_cursor_set_visible(gc, true);
+	PCUT_ASSERT_ERRNO_VAL(tgc.rc, rc);
+
+	PCUT_ASSERT_TRUE(tgc.cursor_set_visible);
+	PCUT_ASSERT_TRUE(tgc.set_visible_vis);
+
+	tgc.cursor_set_visible = false;
+
+	rc = gfx_cursor_set_visible(gc, false);
+	PCUT_ASSERT_ERRNO_VAL(tgc.rc, rc);
+
+	PCUT_ASSERT_TRUE(tgc.cursor_set_visible);
+	PCUT_ASSERT_FALSE(tgc.set_visible_vis);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Set hardware cursor visibility */
+PCUT_TEST(cursor_set_visible_success)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	tgc.rc = EOK;
+
+	rc = gfx_cursor_set_visible(gc, true);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_TRUE(tgc.cursor_set_visible);
+	PCUT_ASSERT_TRUE(tgc.set_visible_vis);
+
+	tgc.cursor_set_visible = false;
+
+	rc = gfx_cursor_set_visible(gc, false);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_TRUE(tgc.cursor_set_visible);
+	PCUT_ASSERT_FALSE(tgc.set_visible_vis);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static errno_t testgc_cursor_get_pos(void *arg, gfx_coord2_t *pos)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+
+	tgc->cursor_get_pos = true;
+	*pos = tgc->get_pos_pos;
+
+	return tgc->rc;
+}
+
+static errno_t testgc_cursor_set_pos(void *arg, gfx_coord2_t *pos)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+
+	tgc->cursor_set_pos = true;
+	tgc->set_pos_pos = *pos;
+
+	return tgc->rc;
+}
+
+static errno_t testgc_cursor_set_visible(void *arg, bool visible)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+
+	tgc->cursor_set_visible = true;
+	tgc->set_visible_vis = visible;
+
+	return tgc->rc;
+}
+
+PCUT_EXPORT(cursor);
Index: uspace/lib/gfx/test/main.c
===================================================================
--- uspace/lib/gfx/test/main.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfx/test/main.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2017 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -34,4 +34,5 @@
 PCUT_IMPORT(color);
 PCUT_IMPORT(coord);
+PCUT_IMPORT(cursor);
 PCUT_IMPORT(render);
 
Index: uspace/lib/gfx/test/render.c
===================================================================
--- uspace/lib/gfx/test/render.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfx/test/render.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -32,4 +32,5 @@
 #include <pcut/pcut.h>
 #include <mem.h>
+#include <stdbool.h>
 
 PCUT_INIT;
@@ -37,18 +38,114 @@
 PCUT_TEST_SUITE(render);
 
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
+static errno_t testgc_update(void *);
 
 static gfx_context_ops_t ops = {
+	.set_clip_rect = testgc_set_clip_rect,
 	.set_color = testgc_set_color,
-	.fill_rect = testgc_fill_rect
+	.fill_rect = testgc_fill_rect,
+	.update = testgc_update
 };
 
 /** Test graphics context data */
 typedef struct {
+	errno_t rc;
+
+	bool set_clip_rect;
+	gfx_rect_t crect;
+	bool do_clip;
+
+	bool set_color;
 	gfx_color_t *dclr;
-	gfx_rect_t *rect;
+
+	bool fill_rect;
+	gfx_rect_t frect;
+
+	bool update;
 } test_gc_t;
 
+/** Set clipping rectangle */
+PCUT_TEST(set_clip_rect)
+{
+	errno_t rc;
+	gfx_rect_t rect;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+
+	tgc.rc = EOK;
+
+	rc = gfx_set_clip_rect(gc, &rect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_TRUE(tgc.set_clip_rect);
+	PCUT_ASSERT_TRUE(tgc.do_clip);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.x, tgc.crect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.y, tgc.crect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.x, tgc.crect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.y, tgc.crect.p1.y);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Set null clipping rectangle */
+PCUT_TEST(set_clip_rect_null)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	tgc.rc = EOK;
+
+	rc = gfx_set_clip_rect(gc, NULL);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_TRUE(tgc.set_clip_rect);
+	PCUT_ASSERT_FALSE(tgc.do_clip);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Set clipping rectangle with error return */
+PCUT_TEST(set_clip_rect_failure)
+{
+	errno_t rc;
+	gfx_rect_t rect;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	tgc.rc = EIO;
+
+	rc = gfx_set_clip_rect(gc, &rect);
+	PCUT_ASSERT_ERRNO_VAL(EIO, rc);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Set drawing color */
 PCUT_TEST(set_color)
 {
@@ -56,4 +153,5 @@
 	gfx_color_t *color;
 	gfx_context_t *gc = NULL;
+	uint16_t r, g, b;
 	test_gc_t tgc;
 
@@ -66,8 +164,45 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
+	PCUT_ASSERT_FALSE(tgc.set_color);
+
+	tgc.rc = EOK;
+
 	rc = gfx_set_color(gc, color);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_EQUALS(color, tgc.dclr);
-	PCUT_ASSERT_NULL(tgc.rect);
+
+	PCUT_ASSERT_TRUE(tgc.set_color);
+
+	gfx_color_get_rgb_i16(tgc.dclr, &r, &g, &b);
+
+	PCUT_ASSERT_INT_EQUALS(0xffff, r);
+	PCUT_ASSERT_INT_EQUALS(0xffff, g);
+	PCUT_ASSERT_INT_EQUALS(0xffff, b);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Set drawing color with error return */
+PCUT_TEST(set_color_failure)
+{
+	errno_t rc;
+	gfx_color_t *color;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_FALSE(tgc.set_color);
+
+	tgc.rc = EIO;
+
+	rc = gfx_set_color(gc, color);
+	PCUT_ASSERT_ERRNO_VAL(EIO, rc);
 
 	gfx_color_delete(color);
@@ -77,8 +212,8 @@
 }
 
+/** Fill rectangle */
 PCUT_TEST(fill_rect)
 {
 	errno_t rc;
-	gfx_color_t *color;
 	gfx_rect_t rect;
 	gfx_context_t *gc = NULL;
@@ -90,19 +225,112 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &color);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = gfx_set_color(gc, color);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_EQUALS(color, tgc.dclr);
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+
+	PCUT_ASSERT_FALSE(tgc.fill_rect);
+
+	tgc.rc = EOK;
 
 	rc = gfx_fill_rect(gc, &rect);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_EQUALS(&rect, tgc.rect);
-
-	gfx_color_delete(color);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_TRUE(tgc.fill_rect);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.x, tgc.frect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.y, tgc.frect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.x, tgc.frect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.y, tgc.frect.p1.y);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Fill rectangle with error return */
+PCUT_TEST(fill_rect_failure)
+{
+	errno_t rc;
+	gfx_rect_t rect;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+
+	PCUT_ASSERT_FALSE(tgc.fill_rect);
+
+	tgc.rc = EIO;
+
+	rc = gfx_fill_rect(gc, &rect);
+	PCUT_ASSERT_ERRNO_VAL(EIO, rc);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Update GC */
+PCUT_TEST(update)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	tgc.rc = EOK;
+
+	PCUT_ASSERT_FALSE(tgc.update);
+	rc = gfx_update(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(tgc.update);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Update GC with error return */
+PCUT_TEST(update_failure)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+
+	memset(&tgc, 0, sizeof(tgc));
+
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	tgc.rc = EIO;
+	rc = gfx_update(gc);
+
+	PCUT_ASSERT_ERRNO_VAL(EIO, rc);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+
+	tgc->set_clip_rect = true;
+	if (rect != NULL) {
+		tgc->do_clip = true;
+		tgc->crect = *rect;
+	} else {
+		tgc->do_clip = false;
+	}
+
+	return tgc->rc;
 }
 
@@ -110,8 +338,10 @@
 {
 	test_gc_t *tgc = (test_gc_t *) arg;
+
+	tgc->set_color = true;
 
 	/* Technically we should copy the data */
 	tgc->dclr = color;
-	return EOK;
+	return tgc->rc;
 }
 
@@ -120,7 +350,15 @@
 	test_gc_t *tgc = (test_gc_t *) arg;
 
-	/* Technically we should copy the data */
-	tgc->rect = rect;
-	return EOK;
+	tgc->fill_rect = true;
+	tgc->frect = *rect;
+	return tgc->rc;
+}
+
+static errno_t testgc_update(void *arg)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+
+	tgc->update = true;
+	return tgc->rc;
 }
 
Index: uspace/lib/gfxfont/include/gfx/font.h
===================================================================
--- uspace/lib/gfxfont/include/gfx/font.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfxfont/include/gfx/font.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -50,4 +50,5 @@
 extern errno_t gfx_font_create(gfx_typeface_t *, gfx_font_props_t *,
     gfx_font_metrics_t *, gfx_font_t **);
+extern errno_t gfx_font_create_textmode(gfx_typeface_t *, gfx_font_t **);
 extern errno_t gfx_font_open(gfx_font_info_t *, gfx_font_t **);
 extern void gfx_font_close(gfx_font_t *);
Index: uspace/lib/gfxfont/include/gfx/text.h
===================================================================
--- uspace/lib/gfxfont/include/gfx/text.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfxfont/include/gfx/text.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -46,4 +46,12 @@
 extern errno_t gfx_puttext(gfx_font_t *, gfx_coord2_t *, gfx_text_fmt_t *,
     const char *);
+extern void gfx_text_start_pos(gfx_font_t *, gfx_coord2_t *, gfx_text_fmt_t *,
+    const char *, gfx_coord2_t *);
+extern size_t gfx_text_find_pos(gfx_font_t *, gfx_coord2_t *, gfx_text_fmt_t *,
+    const char *, gfx_coord2_t *);
+extern void gfx_text_cont(gfx_font_t *, gfx_coord2_t *, gfx_text_fmt_t *,
+    const char *, gfx_coord2_t *, gfx_text_fmt_t *);
+extern void gfx_text_rect(gfx_font_t *, gfx_coord2_t *, gfx_text_fmt_t *,
+    const char *, gfx_rect_t *);
 
 #endif
Index: uspace/lib/gfxfont/include/types/gfx/font.h
===================================================================
--- uspace/lib/gfxfont/include/types/gfx/font.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfxfont/include/types/gfx/font.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -52,5 +52,7 @@
 	gff_italic = 0x2,
 	/** Bold, italic */
-	gff_bold_italic = gff_bold | gff_italic
+	gff_bold_italic = gff_bold | gff_italic,
+	/** Text mode */
+	gff_text_mode = 0x4
 } gfx_font_flags_t;
 
Index: uspace/lib/gfxfont/include/types/gfx/text.h
===================================================================
--- uspace/lib/gfxfont/include/types/gfx/text.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfxfont/include/types/gfx/text.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -38,4 +38,5 @@
 
 #include <types/gfx/coord.h>
+#include <types/gfx/color.h>
 
 /** Text horizontal alignment */
@@ -65,4 +66,6 @@
 /** Text formatting */
 typedef struct {
+	/** Text color */
+	gfx_color_t *color;
 	/** Horizontal alignment */
 	gfx_halign_t halign;
Index: uspace/lib/gfxfont/src/font.c
===================================================================
--- uspace/lib/gfxfont/src/font.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfxfont/src/font.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -180,4 +180,30 @@
 }
 
+/** Create dummy font for printing text in text mode.
+ *
+ * @param tface Typeface
+ * @param rfont Place to store pointer to new font
+ *
+ * @return EOK on success, EINVAL if parameters are invald,
+ *         ENOMEM if insufficient resources, EIO if graphic device connection
+ *         was lost
+ */
+errno_t gfx_font_create_textmode(gfx_typeface_t *tface, gfx_font_t **rfont)
+{
+	gfx_font_props_t props;
+	gfx_font_metrics_t metrics;
+
+	gfx_font_props_init(&props);
+	props.size = 1;
+	props.flags = gff_text_mode;
+
+	gfx_font_metrics_init(&metrics);
+	metrics.ascent = 0;
+	metrics.descent = 0;
+	metrics.leading = 1;
+
+	return gfx_font_create(tface, &props, &metrics, rfont);
+}
+
 /** Open font.
  *
Index: uspace/lib/gfxfont/src/text.c
===================================================================
--- uspace/lib/gfxfont/src/text.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfxfont/src/text.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -35,8 +35,15 @@
 
 #include <errno.h>
+#include <gfx/bitmap.h>
+#include <gfx/color.h>
 #include <gfx/font.h>
 #include <gfx/glyph.h>
+#include <gfx/render.h>
 #include <gfx/text.h>
+#include <io/pixelmap.h>
 #include <mem.h>
+#include <str.h>
+#include "../private/font.h"
+#include "../private/typeface.h"
 
 /** Initialize text formatting structure.
@@ -66,4 +73,7 @@
 	gfx_coord_t width;
 	errno_t rc;
+
+	if ((font->finfo->props.flags & gff_text_mode) != 0)
+		return str_width(str);
 
 	width = 0;
@@ -85,4 +95,128 @@
 }
 
+/** Print string using text characters in text mode.
+ *
+ * @param font Font
+ * @param pos Position of top-left corner of text
+ * @param color Text color
+ * @param str String
+ * @return EOK on success or an error code
+ */
+static errno_t gfx_puttext_textmode(gfx_font_t *font, gfx_coord2_t *pos,
+    gfx_color_t *color, const char *str)
+{
+	gfx_context_t *gc = font->typeface->gc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_t *bitmap;
+	gfx_bitmap_alloc_t alloc;
+	uint16_t r, g, b;
+	pixelmap_t pmap;
+	gfx_coord_t x;
+	pixel_t pixel;
+	errno_t rc;
+
+	/*
+	 * NOTE: Creating and destroying bitmap each time is not probably
+	 * the most efficient way.
+	 */
+
+	gfx_color_get_rgb_i16(color, &r, &g, &b);
+
+	/*
+	 * We are setting the *background* color, the foreground color
+	 * will be set to its complement.
+	 */
+	r = 0xff ^ (r >> 8);
+	g = 0xff ^ (g >> 8);
+	b = 0xff ^ (b >> 8);
+
+	gfx_bitmap_params_init(&params);
+	params.rect.p0.x = 0;
+	params.rect.p0.y = 0;
+	params.rect.p1.x = str_width(str);
+	params.rect.p1.y = 1;
+
+	if (params.rect.p1.x == 0) {
+		/* Nothing to do. Avoid creating bitmap of zero width. */
+		return EOK;
+	}
+
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	if (rc != EOK)
+		return rc;
+
+	rc = gfx_bitmap_get_alloc(bitmap, &alloc);
+	if (rc != EOK) {
+		gfx_bitmap_destroy(bitmap);
+		return rc;
+	}
+
+	pmap.width = params.rect.p1.x;
+	pmap.height = 1;
+	pmap.data = alloc.pixels;
+
+	for (x = 0; x < params.rect.p1.x; x++) {
+		pixel = PIXEL(str[x], r, g, b);
+		pixelmap_put_pixel(&pmap, x, 0, pixel);
+	}
+
+	rc = gfx_bitmap_render(bitmap, NULL, pos);
+
+	gfx_bitmap_destroy(bitmap);
+	return rc;
+}
+
+/** Get text starting position.
+ *
+ * @param font Font
+ * @param pos Anchor position
+ * @param fmt Text formatting
+ * @param str String
+ * @param spos Place to store starting position
+ */
+void gfx_text_start_pos(gfx_font_t *font, gfx_coord2_t *pos,
+    gfx_text_fmt_t *fmt, const char *str, gfx_coord2_t *spos)
+{
+	gfx_font_metrics_t fmetrics;
+	gfx_coord2_t cpos;
+	gfx_coord_t width;
+
+	*spos = *pos;
+
+	/* Adjust position for horizontal alignment */
+	if (fmt->halign != gfx_halign_left) {
+		width = gfx_text_width(font, str);
+		switch (fmt->halign) {
+		case gfx_halign_center:
+			spos->x -= width / 2;
+			break;
+		case gfx_halign_right:
+			spos->x -= width - 1;
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* Adjust position for vertical alignment */
+	gfx_font_get_metrics(font, &fmetrics);
+
+	if (fmt->valign != gfx_valign_baseline) {
+		switch (fmt->valign) {
+		case gfx_valign_top:
+			spos->y += fmetrics.ascent;
+			break;
+		case gfx_valign_center:
+			spos->y += fmetrics.ascent / 2;
+			break;
+		case gfx_valign_bottom:
+			cpos.y -= fmetrics.descent;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
 /** Render text.
  *
@@ -96,5 +230,4 @@
     gfx_text_fmt_t *fmt, const char *str)
 {
-	gfx_font_metrics_t fmetrics;
 	gfx_glyph_metrics_t gmetrics;
 	size_t stradv;
@@ -102,42 +235,15 @@
 	gfx_glyph_t *glyph;
 	gfx_coord2_t cpos;
-	gfx_coord_t width;
 	errno_t rc;
 
-	cpos = *pos;
-
-	/* Adjust position for horizontal alignment */
-	if (fmt->halign != gfx_halign_left) {
-		width = gfx_text_width(font, str);
-		switch (fmt->halign) {
-		case gfx_halign_center:
-			cpos.x -= width / 2;
-			break;
-		case gfx_halign_right:
-			cpos.x -= width;
-			break;
-		default:
-			break;
-		}
-	}
-
-	/* Adjust position for vertical alignment */
-	gfx_font_get_metrics(font, &fmetrics);
-
-	if (fmt->valign != gfx_valign_baseline) {
-		switch (fmt->valign) {
-		case gfx_valign_top:
-			cpos.y += fmetrics.ascent;
-			break;
-		case gfx_valign_center:
-			cpos.y += fmetrics.ascent / 2;
-			break;
-		case gfx_valign_bottom:
-			cpos.y -= fmetrics.descent;
-			break;
-		default:
-			break;
-		}
-	}
+	gfx_text_start_pos(font, pos, fmt, str, &cpos);
+
+	/* Text mode */
+	if ((font->finfo->props.flags & gff_text_mode) != 0)
+		return gfx_puttext_textmode(font, &cpos, fmt->color, str);
+
+	rc = gfx_set_color(font->typeface->gc, fmt->color);
+	if (rc != EOK)
+		return rc;
 
 	cp = str;
@@ -162,4 +268,126 @@
 }
 
+/** Find character position in string by X coordinate.
+ *
+ * @param font Font
+ * @param pos Anchor position
+ * @param fmt Text formatting
+ * @param str String
+ * @param fpos Position for which we need to find offset
+ *
+ * @return Byte offset in @a str of character corresponding to position
+ *         @a fpos. Note that the position is rounded, that is,
+ *         if it is before the center of character A, it will return
+ *         offset of A, if it is after the center of A, it will return
+ *         offset of the following character.
+ */
+size_t gfx_text_find_pos(gfx_font_t *font, gfx_coord2_t *pos,
+    gfx_text_fmt_t *fmt, const char *str, gfx_coord2_t *fpos)
+{
+	gfx_glyph_metrics_t gmetrics;
+	size_t stradv;
+	const char *cp;
+	gfx_glyph_t *glyph;
+	gfx_coord2_t cpos;
+	size_t off;
+	size_t strsize;
+	errno_t rc;
+
+	gfx_text_start_pos(font, pos, fmt, str, &cpos);
+
+	/* Text mode */
+	if ((font->finfo->props.flags & gff_text_mode) != 0) {
+		off = 0;
+		strsize = str_size(str);
+		while (off < strsize) {
+			if (fpos->x <= cpos.x)
+				return off;
+			(void) str_decode(str, &off, strsize);
+			cpos.x++;
+		}
+
+		return off;
+	}
+
+	cp = str;
+	off = 0;
+	while (*cp != '\0') {
+		rc = gfx_font_search_glyph(font, cp, &glyph, &stradv);
+		if (rc != EOK) {
+			++cp;
+			continue;
+		}
+
+		gfx_glyph_get_metrics(glyph, &gmetrics);
+
+		if (fpos->x < cpos.x + gmetrics.advance / 2)
+			return off;
+
+		cp += stradv;
+		off += stradv;
+		cpos.x += gmetrics.advance;
+	}
+
+	return off;
+}
+
+/** Get text continuation parameters.
+ *
+ * Return the anchor position and format needed to continue printing
+ * text after the specified string. It is allowed for the sources
+ * (@a pos, @a fmt) and destinations (@a cpos, @a cfmt) to point
+ * to the same objects, respectively.
+ *
+ * @param font Font
+ * @param pos Anchor position
+ * @param fmt Text formatting
+ * @param str String
+ * @param cpos Place to store anchor position for continuation
+ * @param cfmt Place to store format for continuation
+ */
+void gfx_text_cont(gfx_font_t *font, gfx_coord2_t *pos,
+    gfx_text_fmt_t *fmt, const char *str, gfx_coord2_t *cpos,
+    gfx_text_fmt_t *cfmt)
+{
+	gfx_coord2_t spos;
+	gfx_text_fmt_t tfmt;
+
+	/* Continuation should start where the current string ends */
+	gfx_text_start_pos(font, pos, fmt, str, &spos);
+	cpos->x = spos.x + gfx_text_width(font, str);
+	cpos->y = spos.y;
+
+	/*
+	 * Formatting is the same, except the text should be aligned
+	 * so that it starts at the anchor point.
+	 */
+	tfmt = *fmt;
+	tfmt.halign = gfx_halign_left;
+	tfmt.valign = gfx_valign_baseline;
+
+	*cfmt = tfmt;
+}
+
+/** Get text bounding rectangle.
+ *
+ * @param font Font
+ * @param pos Anchor position
+ * @param fmt Text formatting
+ * @param str String
+ * @param rect Place to store bounding rectangle
+ */
+void gfx_text_rect(gfx_font_t *font, gfx_coord2_t *pos,
+    gfx_text_fmt_t *fmt, const char *str, gfx_rect_t *rect)
+{
+	gfx_coord2_t spos;
+
+	gfx_text_start_pos(font, pos, fmt, str, &spos);
+
+	rect->p0.x = spos.x;
+	rect->p0.y = spos.y - font->metrics.ascent;
+	rect->p1.x = spos.x + gfx_text_width(font, str);
+	rect->p1.y = spos.y + font->metrics.descent + 1;
+}
+
 /** @}
  */
Index: uspace/lib/gfxfont/test/font.c
===================================================================
--- uspace/lib/gfxfont/test/font.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfxfont/test/font.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,4 +39,5 @@
 PCUT_TEST_SUITE(font);
 
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
@@ -48,4 +49,5 @@
 
 static gfx_context_ops_t test_ops = {
+	.set_clip_rect = testgc_set_clip_rect,
 	.set_color = testgc_set_color,
 	.fill_rect = testgc_fill_rect,
@@ -98,4 +100,29 @@
 }
 
+/** Test creating and destroying text-mode font */
+PCUT_TEST(create_textmode_destroy)
+{
+	gfx_typeface_t *tface;
+	gfx_font_t *font;
+	gfx_context_t *gc;
+	test_gc_t tgc;
+	errno_t rc;
+
+	rc = gfx_context_new(&test_ops, (void *)&tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_typeface_create(gc, &tface);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_font_create_textmode(tface, &font);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_font_close(font);
+	gfx_typeface_destroy(tface);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
 /** Test gfx_font_get_metrics() */
 PCUT_TEST(get_metrics)
@@ -543,4 +570,9 @@
 		}
 	}
+}
+
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	return EOK;
 }
 
Index: uspace/lib/gfxfont/test/glyph.c
===================================================================
--- uspace/lib/gfxfont/test/glyph.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfxfont/test/glyph.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -43,4 +43,5 @@
 PCUT_TEST_SUITE(glyph);
 
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
@@ -52,4 +53,5 @@
 
 static gfx_context_ops_t test_ops = {
+	.set_clip_rect = testgc_set_clip_rect,
 	.set_color = testgc_set_color,
 	.fill_rect = testgc_fill_rect,
@@ -567,4 +569,9 @@
 	rc = gfx_context_delete(gc);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	return EOK;
 }
 
Index: uspace/lib/gfxfont/test/glyph_bmp.c
===================================================================
--- uspace/lib/gfxfont/test/glyph_bmp.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfxfont/test/glyph_bmp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,4 +39,5 @@
 PCUT_TEST_SUITE(glyph_bmp);
 
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
@@ -48,4 +49,5 @@
 
 static gfx_context_ops_t test_ops = {
+	.set_clip_rect = testgc_set_clip_rect,
 	.set_color = testgc_set_color,
 	.fill_rect = testgc_fill_rect,
@@ -581,4 +583,9 @@
 	rc = gfx_context_delete(gc);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	return EOK;
 }
 
Index: uspace/lib/gfxfont/test/text.c
===================================================================
--- uspace/lib/gfxfont/test/text.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfxfont/test/text.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -27,6 +27,8 @@
  */
 
+#include <gfx/color.h>
 #include <gfx/context.h>
 #include <gfx/font.h>
+#include <gfx/glyph.h>
 #include <gfx/text.h>
 #include <gfx/typeface.h>
@@ -39,4 +41,5 @@
 PCUT_TEST_SUITE(text);
 
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
@@ -48,4 +51,5 @@
 
 static gfx_context_ops_t test_ops = {
+	.set_clip_rect = testgc_set_clip_rect,
 	.set_color = testgc_set_color,
 	.fill_rect = testgc_fill_rect,
@@ -110,4 +114,5 @@
 	gfx_font_t *font;
 	gfx_context_t *gc;
+	gfx_color_t *color;
 	gfx_text_fmt_t fmt;
 	gfx_coord2_t pos;
@@ -116,4 +121,7 @@
 
 	rc = gfx_context_new(&test_ops, (void *)&tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_color_new_rgb_i16(0, 0, 0, &color);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -127,4 +135,5 @@
 
 	gfx_text_fmt_init(&fmt);
+	fmt.color = color;
 	pos.x = 0;
 	pos.y = 0;
@@ -135,7 +144,311 @@
 	gfx_font_close(font);
 	gfx_typeface_destroy(tface);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	gfx_color_delete(color);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_text_start_pos() correctly computes text start position */
+PCUT_TEST(text_start_pos)
+{
+	gfx_font_props_t props;
+	gfx_font_metrics_t metrics;
+	gfx_typeface_t *tface;
+	gfx_font_t *font;
+	gfx_context_t *gc;
+	gfx_color_t *color;
+	gfx_text_fmt_t fmt;
+	gfx_coord2_t pos;
+	test_gc_t tgc;
+	errno_t rc;
+
+	rc = gfx_context_new(&test_ops, (void *)&tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_color_new_rgb_i16(0, 0, 0, &color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_typeface_create(gc, &tface);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_font_props_init(&props);
+	gfx_font_metrics_init(&metrics);
+	metrics.ascent = 10; // XXX
+	metrics.descent = 10; // XXX
+	rc = gfx_font_create(tface, &props, &metrics, &font);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_text_fmt_init(&fmt);
+	fmt.color = color;
+	pos.x = 0;
+	pos.y = 0;
+
+	rc = gfx_puttext(font, &pos, &fmt, "Hello world!");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_font_close(font);
+	gfx_typeface_destroy(tface);
+	gfx_color_delete(color);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_text_find_pos() finds position in text */
+PCUT_TEST(text_find_pos)
+{
+	gfx_font_props_t props;
+	gfx_font_metrics_t metrics;
+	gfx_typeface_t *tface;
+	gfx_font_t *font;
+	gfx_glyph_metrics_t gmetrics;
+	gfx_glyph_t *glyph1;
+	gfx_glyph_t *glyph2;
+	gfx_context_t *gc;
+	gfx_text_fmt_t fmt;
+	gfx_coord2_t anchor;
+	gfx_coord2_t fpos;
+	size_t off;
+	test_gc_t tgc;
+	errno_t rc;
+
+	rc = gfx_context_new(&test_ops, (void *)&tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_typeface_create(gc, &tface);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_font_props_init(&props);
+	gfx_font_metrics_init(&metrics);
+	rc = gfx_font_create(tface, &props, &metrics, &font);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Need to create some glyphs with metrics */
+	gfx_glyph_metrics_init(&gmetrics);
+	gmetrics.advance = 10;
+
+	rc = gfx_glyph_create(font, &gmetrics, &glyph1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_glyph_set_pattern(glyph1, "A");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_glyph_metrics_init(&gmetrics);
+	gmetrics.advance = 1;
+
+	rc = gfx_glyph_create(font, &gmetrics, &glyph2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_glyph_set_pattern(glyph2, "i");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gfx_text_fmt_init(&fmt);
+	anchor.x = 10;
+	anchor.y = 0;
+
+	fpos.x = 9;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(0, off);
+
+	fpos.x = 10;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(0, off);
+
+	fpos.x = 11;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(0, off);
+
+	fpos.x = 19;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(1, off);
+
+	fpos.x = 20;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(2, off);
+
+	fpos.x = 21;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(3, off);
+
+	fpos.x = 22;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Aii", &fpos);
+	PCUT_ASSERT_INT_EQUALS(3, off);
+
+	gfx_glyph_destroy(glyph1);
+	gfx_glyph_destroy(glyph2);
+
+	gfx_font_close(font);
+	gfx_typeface_destroy(tface);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_text_find_pos() finds position in text in text mode */
+PCUT_TEST(text_find_pos_text)
+{
+	gfx_typeface_t *tface;
+	gfx_font_t *font;
+	gfx_context_t *gc;
+	test_gc_t tgc;
+	size_t off;
+	gfx_text_fmt_t fmt;
+	gfx_coord2_t anchor;
+	gfx_coord2_t fpos;
+	errno_t rc;
+
+	rc = gfx_context_new(&test_ops, (void *)&tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_typeface_create(gc, &tface);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_font_create_textmode(tface, &font);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	anchor.x = 10;
+	anchor.y = 0;
+	gfx_text_fmt_init(&fmt);
+
+	fpos.x = 9;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Abc", &fpos);
+	PCUT_ASSERT_INT_EQUALS(0, off);
+
+	fpos.x = 10;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Abc", &fpos);
+	PCUT_ASSERT_INT_EQUALS(0, off);
+
+	fpos.x = 11;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Abc", &fpos);
+	PCUT_ASSERT_INT_EQUALS(1, off);
+
+	fpos.x = 12;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Abc", &fpos);
+	PCUT_ASSERT_INT_EQUALS(2, off);
+
+	fpos.x = 13;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Abc", &fpos);
+	PCUT_ASSERT_INT_EQUALS(3, off);
+
+	fpos.x = 14;
+	fpos.y = 0;
+	off = gfx_text_find_pos(font, &anchor, &fmt, "Abc", &fpos);
+	PCUT_ASSERT_INT_EQUALS(3, off);
+
+	gfx_font_close(font);
+	gfx_typeface_destroy(tface);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_text_cont() produces correct continuation parameters */
+PCUT_TEST(text_cont)
+{
+	gfx_typeface_t *tface;
+	gfx_font_t *font;
+	gfx_context_t *gc;
+	gfx_color_t *color;
+	test_gc_t tgc;
+	gfx_text_fmt_t fmt;
+	gfx_coord2_t anchor;
+	gfx_coord2_t cpos;
+	gfx_text_fmt_t cfmt;
+	errno_t rc;
+
+	rc = gfx_context_new(&test_ops, (void *)&tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_typeface_create(gc, &tface);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_font_create_textmode(tface, &font);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_color_new_rgb_i16(0, 0, 0, &color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	anchor.x = 10;
+	anchor.y = 20;
+	gfx_text_fmt_init(&fmt);
+	fmt.color = color;
+
+	gfx_text_cont(font, &anchor, &fmt, "Abc", &cpos, &cfmt);
+
+	PCUT_ASSERT_INT_EQUALS(13, cpos.x);
+	PCUT_ASSERT_INT_EQUALS(20, cpos.y);
+	PCUT_ASSERT_EQUALS(fmt.color, cfmt.color);
+	PCUT_ASSERT_EQUALS(gfx_halign_left, cfmt.halign);
+	PCUT_ASSERT_EQUALS(gfx_valign_baseline, cfmt.valign);
+
+	gfx_font_close(font);
+	gfx_typeface_destroy(tface);
+	gfx_color_delete(color);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_text_rect() computes bounding rectangle */
+PCUT_TEST(text_rect)
+{
+	gfx_typeface_t *tface;
+	gfx_font_t *font;
+	gfx_context_t *gc;
+	gfx_color_t *color;
+	test_gc_t tgc;
+	gfx_text_fmt_t fmt;
+	gfx_coord2_t anchor;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rc = gfx_context_new(&test_ops, (void *)&tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_typeface_create(gc, &tface);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_font_create_textmode(tface, &font);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_color_new_rgb_i16(0, 0, 0, &color);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	anchor.x = 10;
+	anchor.y = 20;
+	gfx_text_fmt_init(&fmt);
+	fmt.color = color;
+
+	gfx_text_rect(font, &anchor, &fmt, "Abc", &rect);
+
+	PCUT_ASSERT_INT_EQUALS(10, rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(20, rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(13, rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(21, rect.p1.y);
+
+	gfx_font_close(font);
+	gfx_typeface_destroy(tface);
+	gfx_color_delete(color);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	return EOK;
 }
 
Index: uspace/lib/gfxfont/test/tpf.c
===================================================================
--- uspace/lib/gfxfont/test/tpf.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfxfont/test/tpf.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,4 +41,5 @@
 PCUT_TEST_SUITE(tpf);
 
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
@@ -50,4 +51,5 @@
 
 static gfx_context_ops_t test_ops = {
+	.set_clip_rect = testgc_set_clip_rect,
 	.set_color = testgc_set_color,
 	.fill_rect = testgc_fill_rect,
@@ -208,4 +210,9 @@
 }
 
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	return EOK;
+}
+
 static errno_t testgc_set_color(void *arg, gfx_color_t *color)
 {
Index: uspace/lib/gfxfont/test/typeface.c
===================================================================
--- uspace/lib/gfxfont/test/typeface.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/gfxfont/test/typeface.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -37,4 +37,5 @@
 PCUT_TEST_SUITE(typeface);
 
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
@@ -46,4 +47,5 @@
 
 static gfx_context_ops_t test_ops = {
+	.set_clip_rect = testgc_set_clip_rect,
 	.set_color = testgc_set_color,
 	.fill_rect = testgc_fill_rect,
@@ -94,4 +96,9 @@
 {
 	// TODO
+}
+
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	return EOK;
 }
 
Index: uspace/lib/hound/src/protocol.c
===================================================================
--- uspace/lib/hound/src/protocol.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/hound/src/protocol.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -394,5 +394,5 @@
 	void *sink;
 
-	/* Accept connection if there is a valid iface*/
+	/* Accept connection if there is a valid iface */
 	if (server_iface) {
 		async_accept_0(icall);
@@ -547,5 +547,5 @@
 			ret = async_data_write_accept(&source, true, 0, 0, 0,
 			    0);
-			/*read sink name */
+			/* read sink name */
 			if (ret == EOK)
 				ret = async_data_write_accept(&sink,
@@ -611,5 +611,5 @@
 		case IPC_M_HOUND_STREAM_EXIT:
 		case IPC_M_HOUND_STREAM_DRAIN:
-			/* Stream exit/drain is only allowed in stream context*/
+			/* Stream exit/drain is only allowed in stream context */
 			async_answer_0(&call, EINVAL);
 			break;
Index: uspace/lib/http/meson.build
===================================================================
--- uspace/lib/http/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/http/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 #
+# Copyright (c) 2021 Jiri Svoboda
 # Copyright (c) 2013 Martin Sucha
 # All rights reserved.
@@ -27,4 +28,5 @@
 #
 
+deps = [ 'inet' ]
 src = files(
 	'src/http.c',
Index: uspace/lib/inet/doc/doxygroups.h
===================================================================
--- uspace/lib/inet/doc/doxygroups.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/doc/doxygroups.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,3 @@
+/** @addtogroup libinet libinet
+ * @ingroup libs
+ */
Index: uspace/lib/inet/include/inet/addr.h
===================================================================
--- uspace/lib/inet/include/inet/addr.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/addr.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2021 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_ADDR_H
+#define LIBINET_INET_ADDR_H
+
+#include <errno.h>
+#include <inet/eth_addr.h>
+#include <stdint.h>
+
+typedef uint32_t addr32_t;
+
+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 {
+	/** IP version */
+	ip_ver_t version;
+	union {
+		addr32_t addr;
+		addr128_t addr6;
+	};
+} inet_addr_t;
+
+/** Network address */
+typedef struct {
+	/** IP version */
+	ip_ver_t version;
+
+	/** Address */
+	union {
+		addr32_t addr;
+		addr128_t addr6;
+	};
+
+	/** Number of valid bits */
+	uint8_t prefix;
+} inet_naddr_t;
+
+extern const addr32_t addr32_broadcast_all_hosts;
+
+extern void addr128(const addr128_t, addr128_t);
+
+extern int addr128_compare(const addr128_t, const addr128_t);
+extern void eth_addr_solicited_node(const addr128_t, eth_addr_t *);
+
+extern void host2addr128_t_be(const addr128_t, addr128_t);
+extern void addr128_t_be2host(const addr128_t, addr128_t);
+
+extern void inet_addr(inet_addr_t *, uint8_t, uint8_t, uint8_t, uint8_t);
+extern void inet_naddr(inet_naddr_t *, uint8_t, uint8_t, uint8_t, uint8_t,
+    uint8_t);
+
+extern void inet_addr6(inet_addr_t *, uint16_t, uint16_t, uint16_t, uint16_t,
+    uint16_t, uint16_t, uint16_t, uint16_t);
+extern void inet_naddr6(inet_naddr_t *, uint16_t, uint16_t, uint16_t, uint16_t,
+    uint16_t, uint16_t, uint16_t, uint16_t, uint8_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 *);
+
+extern void inet_addr_any(inet_addr_t *);
+extern void inet_naddr_any(inet_naddr_t *);
+
+extern int inet_addr_compare(const inet_addr_t *, const inet_addr_t *);
+extern int inet_addr_is_any(const inet_addr_t *);
+
+extern int inet_naddr_compare(const inet_naddr_t *, const inet_addr_t *);
+extern int inet_naddr_compare_mask(const inet_naddr_t *, const inet_addr_t *);
+
+extern errno_t inet_addr_parse(const char *, inet_addr_t *, char **);
+extern errno_t inet_naddr_parse(const char *, inet_naddr_t *, char **);
+
+extern errno_t inet_addr_format(const inet_addr_t *, char **);
+extern errno_t inet_naddr_format(const inet_naddr_t *, char **);
+
+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 *);
+
+extern void inet_addr_set(addr32_t, inet_addr_t *);
+extern void inet_naddr_set(addr32_t, uint8_t, inet_naddr_t *);
+
+extern void inet_addr_set6(addr128_t, inet_addr_t *);
+extern void inet_naddr_set6(addr128_t, uint8_t, inet_naddr_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/dhcp.h
===================================================================
--- uspace/lib/inet/include/inet/dhcp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/dhcp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,48 @@
+/*
+ * 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_DHCP_H
+#define LIBINET_INET_DHCP_H
+
+#include <types/common.h>
+
+extern errno_t dhcp_init(void);
+extern errno_t dhcp_link_add(sysarg_t);
+extern errno_t dhcp_link_remove(sysarg_t);
+extern errno_t dhcp_discover(sysarg_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/dnsr.h
===================================================================
--- uspace/lib/inet/include/inet/dnsr.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/dnsr.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,61 @@
+/*
+ * 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_DNSR_H
+#define LIBINET_INET_DNSR_H
+
+#include <inet/inet.h>
+#include <inet/addr.h>
+
+enum {
+	DNSR_NAME_MAX_SIZE = 255
+};
+
+typedef struct {
+	/** Host canonical name */
+	char *cname;
+	/** Host address */
+	inet_addr_t addr;
+} dnsr_hostinfo_t;
+
+extern errno_t dnsr_init(void);
+extern errno_t dnsr_name2host(const char *, dnsr_hostinfo_t **, ip_ver_t);
+extern void dnsr_hostinfo_destroy(dnsr_hostinfo_t *);
+extern errno_t dnsr_get_srvaddr(inet_addr_t *);
+extern errno_t dnsr_set_srvaddr(inet_addr_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/endpoint.h
===================================================================
--- uspace/lib/inet/include/inet/endpoint.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/endpoint.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_ENDPOINT_H
+#define LIBINET_INET_ENDPOINT_H
+
+#include <stdint.h>
+#include <inet/addr.h>
+#include <loc.h>
+
+/** Internet port number ranges
+ *
+ * Port number ranges per RFC 6335 section 6 (Port Number Ranges.
+ * Technically port zero is a system port. But since it is reserved,
+ * we will use it as a special value denoting no port is specified
+ * and we will exclude it from the system port range to disallow
+ * ever assigning it.
+ */
+enum inet_port_ranges {
+	/** Special value meaning no specific port */
+	inet_port_any = 0,
+	/** Lowest system port (a.k.a. well known port) */
+	inet_port_sys_lo = 1,
+	/** Highest system port (a.k.a. well known port) */
+	inet_port_sys_hi = 1023,
+	/** Lowest user port (a.k.a. registered port) */
+	inet_port_user_lo = 1024,
+	/** Highest user port (a.k.a. registered port) */
+	inet_port_user_hi = 49151,
+	/** Lowest dynamic port (a.k.a. private or ephemeral port) */
+	inet_port_dyn_lo = 49152,
+	/** Highest dynamic port (a.k.a. private or ephemeral port) */
+	inet_port_dyn_hi = 65535
+};
+
+/** Internet endpoint (address-port pair), a.k.a. socket */
+typedef struct {
+	inet_addr_t addr;
+	uint16_t port;
+} inet_ep_t;
+
+/** Internet endpoint pair */
+typedef struct {
+	service_id_t local_link;
+	inet_ep_t local;
+	inet_ep_t remote;
+} inet_ep2_t;
+
+extern void inet_ep_init(inet_ep_t *);
+extern void inet_ep2_init(inet_ep2_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/eth_addr.h
===================================================================
--- uspace/lib/inet/include/inet/eth_addr.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/eth_addr.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2021 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 libinet
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef LIBINET_INET_ETH_ADDR_H
+#define LIBINET_INET_ETH_ADDR_H
+
+#include <stdint.h>
+
+#define ETH_ADDR_SIZE 6
+#define ETH_ADDR_STR_SIZE (6 * 2 + 5)
+
+#define ETH_ADDR_INITIALIZER(aa, bb, cc, dd, ee, ff) \
+    { .a = ((uint64_t)(aa) << 40) | ((uint64_t)(bb) << 32) | \
+    ((uint64_t)(cc) << 24) | ((uint64_t)(dd) << 16) | \
+    ((uint64_t)(ee) << 8) | (ff) }
+
+/** Ethernet address.
+ *
+ * Defined as a structure. This provides strong type checking.
+ *
+ * Since the structure is not opaque, this allows eth_addr_t to be
+ * allocated statically and copied around using the assignment operator.
+ *
+ * It is stored in the lower 48 bits of a 64-bit integer. This is an internal
+ * representation that allows simple and efficient operation. Most CPUs
+ * will be much faster (and we will need less instructions) operating
+ * on a single 64-bit integer than on six individual 8-bit integers.
+ *
+ * Kind reader will appreciate the cleverness and elegance of this
+ * representation.
+ */
+typedef struct {
+	uint64_t a;
+} eth_addr_t;
+
+/** Ethernet address in the form of a string */
+typedef struct {
+	char str[ETH_ADDR_STR_SIZE + 1];
+} eth_addr_str_t;
+
+extern const eth_addr_t eth_addr_broadcast;
+
+extern void eth_addr_encode(eth_addr_t *, uint8_t *);
+extern void eth_addr_decode(const uint8_t *, eth_addr_t *);
+
+extern int eth_addr_compare(const eth_addr_t *, const eth_addr_t *);
+extern void eth_addr_format(eth_addr_t *, eth_addr_str_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/host.h
===================================================================
--- uspace/lib/inet/include/inet/host.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/host.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_HOST_H
+#define LIBINET_INET_HOST_H
+
+#include <inet/addr.h>
+#include <inet/endpoint.h>
+#include <types/inet/hostport.h>
+
+extern errno_t inet_host_parse(const char *, inet_host_t **, char **);
+extern errno_t inet_host_format(inet_host_t *, char **);
+extern void inet_host_destroy(inet_host_t *);
+extern errno_t inet_host_lookup_one(inet_host_t *, ip_ver_t, inet_addr_t *);
+extern errno_t inet_host_plookup_one(const char *, ip_ver_t, inet_addr_t *,
+    char **, const char **);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/hostname.h
===================================================================
--- uspace/lib/inet/include/inet/hostname.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/hostname.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_HOSTNAME_H
+#define LIBINET_INET_HOSTNAME_H
+
+extern errno_t inet_hostname_parse(const char *, char **, char **);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/hostport.h
===================================================================
--- uspace/lib/inet/include/inet/hostport.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/hostport.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_HOSTPORT_H
+#define LIBINET_INET_HOSTPORT_H
+
+#include <inet/addr.h>
+#include <inet/endpoint.h>
+#include <types/inet/hostport.h>
+
+extern errno_t inet_hostport_parse(const char *, inet_hostport_t **, char **);
+extern errno_t inet_hostport_format(inet_hostport_t *, char **);
+extern void inet_hostport_destroy(inet_hostport_t *);
+extern errno_t inet_hostport_lookup_one(inet_hostport_t *, ip_ver_t, inet_ep_t *);
+extern errno_t inet_hostport_plookup_one(const char *, ip_ver_t, inet_ep_t *,
+    char **, const char **);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/inet.h
===================================================================
--- uspace/lib/inet/include/inet/inet.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/inet.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,50 @@
+/*
+ * 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_INET_H
+#define LIBINET_INET_INET_H
+
+#include <inet/addr.h>
+#include <ipc/loc.h>
+#include <stdint.h>
+#include <types/inet.h>
+
+extern errno_t inet_init(uint8_t, inet_ev_ops_t *);
+extern errno_t inet_send(inet_dgram_t *, uint8_t, inet_df_t);
+extern errno_t inet_get_srcaddr(inet_addr_t *, uint8_t, inet_addr_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/inetcfg.h
===================================================================
--- uspace/lib/inet/include/inet/inetcfg.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/inetcfg.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,62 @@
+/*
+ * 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_INETCFG_H
+#define LIBINET_INET_INETCFG_H
+
+#include <inet/inet.h>
+#include <stddef.h>
+#include <types/inetcfg.h>
+
+extern errno_t inetcfg_init(void);
+extern errno_t inetcfg_addr_create_static(const char *, inet_naddr_t *, sysarg_t, sysarg_t *);
+extern errno_t inetcfg_addr_delete(sysarg_t);
+extern errno_t inetcfg_addr_get(sysarg_t, inet_addr_info_t *);
+extern errno_t inetcfg_addr_get_id(const char *, sysarg_t, sysarg_t *);
+extern errno_t inetcfg_get_addr_list(sysarg_t **, size_t *);
+extern errno_t inetcfg_get_link_list(sysarg_t **, size_t *);
+extern errno_t inetcfg_get_sroute_list(sysarg_t **, size_t *);
+extern errno_t inetcfg_link_add(sysarg_t);
+extern errno_t inetcfg_link_get(sysarg_t, inet_link_info_t *);
+extern errno_t inetcfg_link_remove(sysarg_t);
+extern errno_t inetcfg_sroute_get(sysarg_t, inet_sroute_info_t *);
+extern errno_t inetcfg_sroute_get_id(const char *, sysarg_t *);
+extern errno_t inetcfg_sroute_create(const char *, inet_naddr_t *, inet_addr_t *,
+    sysarg_t *);
+extern errno_t inetcfg_sroute_delete(sysarg_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/inetping.h
===================================================================
--- uspace/lib/inet/include/inet/inetping.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/inetping.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_INETPING_H
+#define LIBINET_INET_INETPING_H
+
+#include <inet/inet.h>
+#include <types/inetping.h>
+
+typedef struct inetping_ev_ops {
+	errno_t (*recv)(inetping_sdu_t *);
+} inetping_ev_ops_t;
+
+extern errno_t inetping_init(inetping_ev_ops_t *);
+extern errno_t inetping_send(inetping_sdu_t *);
+extern errno_t inetping_get_srcaddr(const inet_addr_t *, inet_addr_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/iplink.h
===================================================================
--- uspace/lib/inet/include/inet/iplink.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/iplink.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2021 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_IPLINK_H
+#define LIBINET_INET_IPLINK_H
+
+#include <async.h>
+#include <inet/addr.h>
+#include <inet/eth_addr.h>
+
+struct iplink_ev_ops;
+
+typedef struct {
+	async_sess_t *sess;
+	struct iplink_ev_ops *ev_ops;
+	void *arg;
+} iplink_t;
+
+/** IPv4 link Service Data Unit */
+typedef struct {
+	/** Local source address */
+	addr32_t src;
+	/** Local destination address */
+	addr32_t dest;
+	/** Serialized IP packet */
+	void *data;
+	/** Size of @c data in bytes */
+	size_t size;
+} iplink_sdu_t;
+
+/** IPv6 link Service Data Unit */
+typedef struct {
+	/** Local MAC destination address */
+	eth_addr_t dest;
+	/** Serialized IP packet */
+	void *data;
+	/** Size of @c data in bytes */
+	size_t size;
+} iplink_sdu6_t;
+
+/** Internet link receive Service Data Unit */
+typedef struct {
+	/** Serialized datagram */
+	void *data;
+	/** Size of @c data in bytes */
+	size_t size;
+} iplink_recv_sdu_t;
+
+typedef struct iplink_ev_ops {
+	errno_t (*recv)(iplink_t *, iplink_recv_sdu_t *, ip_ver_t);
+	errno_t (*change_addr)(iplink_t *, eth_addr_t *);
+} iplink_ev_ops_t;
+
+extern errno_t iplink_open(async_sess_t *, iplink_ev_ops_t *, void *, iplink_t **);
+extern void iplink_close(iplink_t *);
+extern errno_t iplink_send(iplink_t *, iplink_sdu_t *);
+extern errno_t iplink_send6(iplink_t *, iplink_sdu6_t *);
+extern errno_t iplink_addr_add(iplink_t *, inet_addr_t *);
+extern errno_t iplink_addr_remove(iplink_t *, inet_addr_t *);
+extern errno_t iplink_get_mtu(iplink_t *, size_t *);
+extern errno_t iplink_get_mac48(iplink_t *, eth_addr_t *);
+extern errno_t iplink_set_mac48(iplink_t *, eth_addr_t *);
+extern void *iplink_get_userptr(iplink_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/iplink_srv.h
===================================================================
--- uspace/lib/inet/include/inet/iplink_srv.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/iplink_srv.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2021 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_IPLINK_SRV_H
+#define LIBINET_INET_IPLINK_SRV_H
+
+#include <async.h>
+#include <fibril_synch.h>
+#include <inet/addr.h>
+#include <inet/eth_addr.h>
+#include <inet/iplink.h>
+#include <stdbool.h>
+
+struct iplink_ops;
+
+typedef struct {
+	fibril_mutex_t lock;
+	bool connected;
+	struct iplink_ops *ops;
+	void *arg;
+	async_sess_t *client_sess;
+} iplink_srv_t;
+
+typedef struct iplink_ops {
+	errno_t (*open)(iplink_srv_t *);
+	errno_t (*close)(iplink_srv_t *);
+	errno_t (*send)(iplink_srv_t *, iplink_sdu_t *);
+	errno_t (*send6)(iplink_srv_t *, iplink_sdu6_t *);
+	errno_t (*get_mtu)(iplink_srv_t *, size_t *);
+	errno_t (*get_mac48)(iplink_srv_t *, eth_addr_t *);
+	errno_t (*set_mac48)(iplink_srv_t *, eth_addr_t *);
+	errno_t (*addr_add)(iplink_srv_t *, inet_addr_t *);
+	errno_t (*addr_remove)(iplink_srv_t *, inet_addr_t *);
+} iplink_ops_t;
+
+extern void iplink_srv_init(iplink_srv_t *);
+
+extern errno_t iplink_conn(ipc_call_t *, void *);
+extern errno_t iplink_ev_recv(iplink_srv_t *, iplink_recv_sdu_t *, ip_ver_t);
+extern errno_t iplink_ev_change_addr(iplink_srv_t *, eth_addr_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/tcp.h
===================================================================
--- uspace/lib/inet/include/inet/tcp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/tcp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2015 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_TCP_H
+#define LIBINET_INET_TCP_H
+
+#include <fibril_synch.h>
+#include <inet/addr.h>
+#include <inet/endpoint.h>
+#include <inet/inet.h>
+
+/** TCP connection */
+typedef struct {
+	fibril_mutex_t lock;
+	fibril_condvar_t cv;
+	struct tcp *tcp;
+	link_t ltcp;
+	sysarg_t id;
+	struct tcp_cb *cb;
+	void *cb_arg;
+	/** Some received data available in TCP server */
+	bool data_avail;
+	bool connected;
+	bool conn_failed;
+	bool conn_reset;
+} tcp_conn_t;
+
+/** TCP connection listener */
+typedef struct {
+	struct tcp *tcp;
+	link_t ltcp;
+	sysarg_t id;
+	struct tcp_listen_cb *lcb;
+	void *lcb_arg;
+	struct tcp_cb *cb;
+	void *cb_arg;
+} tcp_listener_t;
+
+/** TCP connection callbacks */
+typedef struct tcp_cb {
+	void (*connected)(tcp_conn_t *);
+	void (*conn_failed)(tcp_conn_t *);
+	void (*conn_reset)(tcp_conn_t *);
+	void (*data_avail)(tcp_conn_t *);
+	void (*urg_data)(tcp_conn_t *);
+} tcp_cb_t;
+
+/** TCP listener callbacks */
+typedef struct tcp_listen_cb {
+	void (*new_conn)(tcp_listener_t *, tcp_conn_t *);
+} tcp_listen_cb_t;
+
+/** TCP service */
+typedef struct tcp {
+	/** TCP session */
+	async_sess_t *sess;
+	/** List of connections */
+	list_t conn; /* of tcp_conn_t */
+	/** List of listeners */
+	list_t listener; /* of tcp_listener_t */
+	/** TCP service lock */
+	fibril_mutex_t lock;
+	/** For waiting on cb_done */
+	fibril_condvar_t cv;
+	/** Set to @a true when callback connection handler has terminated */
+	bool cb_done;
+} tcp_t;
+
+extern errno_t tcp_create(tcp_t **);
+extern void tcp_destroy(tcp_t *);
+extern errno_t tcp_conn_create(tcp_t *, inet_ep2_t *, tcp_cb_t *, void *,
+    tcp_conn_t **);
+extern void tcp_conn_destroy(tcp_conn_t *);
+extern void *tcp_conn_userptr(tcp_conn_t *);
+extern errno_t tcp_listener_create(tcp_t *, inet_ep_t *, tcp_listen_cb_t *, void *,
+    tcp_cb_t *, void *, tcp_listener_t **);
+extern void tcp_listener_destroy(tcp_listener_t *);
+extern void *tcp_listener_userptr(tcp_listener_t *);
+
+extern errno_t tcp_conn_wait_connected(tcp_conn_t *);
+extern errno_t tcp_conn_send(tcp_conn_t *, const void *, size_t);
+extern errno_t tcp_conn_send_fin(tcp_conn_t *);
+extern errno_t tcp_conn_push(tcp_conn_t *);
+extern errno_t tcp_conn_reset(tcp_conn_t *);
+
+extern errno_t tcp_conn_recv(tcp_conn_t *, void *, size_t, size_t *);
+extern errno_t tcp_conn_recv_wait(tcp_conn_t *, void *, size_t, size_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/inet/udp.h
===================================================================
--- uspace/lib/inet/include/inet/udp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/inet/udp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2015 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_INET_UDP_H
+#define LIBINET_INET_UDP_H
+
+#include <async.h>
+#include <fibril_synch.h>
+#include <inet/addr.h>
+#include <inet/endpoint.h>
+#include <inet/inet.h>
+#include <stdbool.h>
+
+/** UDP link state */
+typedef enum {
+	udp_ls_down,
+	udp_ls_up
+} udp_link_state_t;
+
+/** UDP received message */
+typedef struct {
+	struct udp *udp;
+	sysarg_t assoc_id;
+	size_t size;
+	inet_ep_t remote_ep;
+} udp_rmsg_t;
+
+/** UDP received error */
+typedef struct {
+} udp_rerr_t;
+
+/** UDP association */
+typedef struct {
+	struct udp *udp;
+	link_t ludp;
+	sysarg_t id;
+	struct udp_cb *cb;
+	void *cb_arg;
+} udp_assoc_t;
+
+/** UDP callbacks */
+typedef struct udp_cb {
+	void (*recv_msg)(udp_assoc_t *, udp_rmsg_t *);
+	void (*recv_err)(udp_assoc_t *, udp_rerr_t *);
+	void (*link_state)(udp_assoc_t *, udp_link_state_t);
+} udp_cb_t;
+
+/** UDP service */
+typedef struct udp {
+	/** UDP session */
+	async_sess_t *sess;
+	/** List of associations */
+	list_t assoc; /* of udp_assoc_t */
+	/** UDP service lock */
+	fibril_mutex_t lock;
+	/** For waiting on cb_done */
+	fibril_condvar_t cv;
+	/** Set to @a true when callback connection handler has terminated */
+	bool cb_done;
+} udp_t;
+
+extern errno_t udp_create(udp_t **);
+extern void udp_destroy(udp_t *);
+extern errno_t udp_assoc_create(udp_t *, inet_ep2_t *, udp_cb_t *, void *,
+    udp_assoc_t **);
+extern errno_t udp_assoc_set_nolocal(udp_assoc_t *);
+extern void udp_assoc_destroy(udp_assoc_t *);
+extern errno_t udp_assoc_send_msg(udp_assoc_t *, inet_ep_t *, void *, size_t);
+extern void *udp_assoc_userptr(udp_assoc_t *);
+extern size_t udp_rmsg_size(udp_rmsg_t *);
+extern errno_t udp_rmsg_read(udp_rmsg_t *, size_t, void *, size_t);
+extern void udp_rmsg_remote_ep(udp_rmsg_t *, inet_ep_t *);
+extern uint8_t udp_rerr_type(udp_rerr_t *);
+extern uint8_t udp_rerr_code(udp_rerr_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/ipc/dhcp.h
===================================================================
--- uspace/lib/inet/include/ipc/dhcp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/ipc/dhcp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,51 @@
+/*
+ * 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_IPC_DHCP_H
+#define LIBINET_IPC_DHCP_H
+
+#include <ipc/common.h>
+
+/** DHCP service requests */
+typedef enum {
+	DHCP_LINK_ADD = IPC_FIRST_USER_METHOD,
+	DHCP_LINK_REMOVE,
+	DHCP_DISCOVER
+} dhcp_request_t;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/inet/include/ipc/dnsr.h
===================================================================
--- uspace/lib/inet/include/ipc/dnsr.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/ipc/dnsr.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -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 libinet
+ * @{
+ */
+
+#ifndef LIBINET_IPC_DNSR_H
+#define LIBINET_IPC_DNSR_H
+
+#include <ipc/common.h>
+
+typedef enum {
+	DNSR_NAME2HOST = IPC_FIRST_USER_METHOD,
+	DNSR_GET_SRVADDR,
+	DNSR_SET_SRVADDR
+} dnsr_request_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/ipc/inet.h
===================================================================
--- uspace/lib/inet/include/ipc/inet.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/ipc/inet.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,86 @@
+/*
+ * 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_IPC_INET_H
+#define LIBINET_IPC_INET_H
+
+#include <ipc/common.h>
+
+/** Requests on Inet default port */
+typedef enum {
+	INET_CALLBACK_CREATE = IPC_FIRST_USER_METHOD,
+	INET_GET_SRCADDR,
+	INET_SEND,
+	INET_SET_PROTO
+} inet_request_t;
+
+/** Events on Inet default port */
+typedef enum {
+	INET_EV_RECV = IPC_FIRST_USER_METHOD
+} inet_event_t;
+
+/** Requests on Inet configuration port */
+typedef enum {
+	INETCFG_ADDR_CREATE_STATIC = IPC_FIRST_USER_METHOD,
+	INETCFG_ADDR_DELETE,
+	INETCFG_ADDR_GET,
+	INETCFG_ADDR_GET_ID,
+	INETCFG_GET_ADDR_LIST,
+	INETCFG_GET_LINK_LIST,
+	INETCFG_GET_SROUTE_LIST,
+	INETCFG_LINK_ADD,
+	INETCFG_LINK_GET,
+	INETCFG_LINK_REMOVE,
+	INETCFG_SROUTE_CREATE,
+	INETCFG_SROUTE_DELETE,
+	INETCFG_SROUTE_GET,
+	INETCFG_SROUTE_GET_ID
+} inetcfg_request_t;
+
+/** Events on Inet ping port */
+typedef enum {
+	INETPING_EV_RECV = IPC_FIRST_USER_METHOD
+} inetping_event_t;
+
+/** Requests on Inet ping port */
+typedef enum {
+	INETPING_SEND = IPC_FIRST_USER_METHOD,
+	INETPING_GET_SRCADDR
+} inetping_request_t;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/inet/include/ipc/iplink.h
===================================================================
--- uspace/lib/inet/include/ipc/iplink.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/ipc/iplink.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,59 @@
+/*
+ * 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_IPC_IPLINK_H
+#define LIBINET_IPC_IPLINK_H
+
+#include <ipc/common.h>
+
+typedef enum {
+	IPLINK_GET_MTU = IPC_FIRST_USER_METHOD,
+	IPLINK_GET_MAC48,
+	IPLINK_SET_MAC48,
+	IPLINK_SEND,
+	IPLINK_SEND6,
+	IPLINK_ADDR_ADD,
+	IPLINK_ADDR_REMOVE
+} iplink_request_t;
+
+typedef enum {
+	IPLINK_EV_RECV = IPC_FIRST_USER_METHOD,
+	IPLINK_EV_CHANGE_ADDR,
+} iplink_event_t;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/lib/inet/include/ipc/tcp.h
===================================================================
--- uspace/lib/inet/include/ipc/tcp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/ipc/tcp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_IPC_TCP_H
+#define LIBINET_IPC_TCP_H
+
+#include <ipc/common.h>
+
+typedef enum {
+	TCP_CALLBACK_CREATE = IPC_FIRST_USER_METHOD,
+	TCP_CONN_CREATE,
+	TCP_CONN_DESTROY,
+	TCP_LISTENER_CREATE,
+	TCP_LISTENER_DESTROY,
+	TCP_CONN_SEND,
+	TCP_CONN_SEND_FIN,
+	TCP_CONN_PUSH,
+	TCP_CONN_RESET,
+	TCP_CONN_RECV,
+	TCP_CONN_RECV_WAIT
+} tcp_request_t;
+
+typedef enum {
+	TCP_EV_CONNECTED = IPC_FIRST_USER_METHOD,
+	TCP_EV_CONN_FAILED,
+	TCP_EV_CONN_RESET,
+	TCP_EV_DATA,
+	TCP_EV_URG_DATA,
+	TCP_EV_NEW_CONN
+} tcp_event_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/ipc/udp.h
===================================================================
--- uspace/lib/inet/include/ipc/udp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/ipc/udp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_IPC_UDP_H
+#define LIBINET_IPC_UDP_H
+
+#include <ipc/common.h>
+
+typedef enum {
+	UDP_CALLBACK_CREATE = IPC_FIRST_USER_METHOD,
+	UDP_ASSOC_CREATE,
+	UDP_ASSOC_DESTROY,
+	UDP_ASSOC_SET_NOLOCAL,
+	UDP_ASSOC_SEND_MSG,
+	UDP_RMSG_INFO,
+	UDP_RMSG_READ,
+	UDP_RMSG_DISCARD
+} udp_request_t;
+
+typedef enum {
+	UDP_EV_DATA = IPC_FIRST_USER_METHOD
+} udp_event_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/types/inet.h
===================================================================
--- uspace/lib/inet/include/types/inet.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/types/inet.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,66 @@
+/*
+ * 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_TYPES_INET_H
+#define LIBINET_TYPES_INET_H
+
+#include <inet/addr.h>
+#include <ipc/loc.h>
+#include <stddef.h>
+#include <stdint.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 {
+	errno_t (*recv)(inet_dgram_t *);
+} inet_ev_ops_t;
+
+typedef enum {
+	INET_DF = 1
+} inet_df_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/types/inet/host.h
===================================================================
--- uspace/lib/inet/include/types/inet/host.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/types/inet/host.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINETTYPES_INET_HOST_H
+#define LIBINETTYPES_INET_HOST_H
+
+#include <inet/addr.h>
+
+typedef enum {
+	/** Host name */
+	inet_host_name,
+	/** Host address */
+	inet_host_addr
+} inet_host_form_t;
+
+/** Internet host:port specification
+ *
+ * As in RFC 1738 Uniform Resouce Locators (URL) and RFC 2732 Format for
+ * literal IPv6 Addresses in URLs
+ */
+typedef struct {
+	/** Host form */
+	inet_host_form_t hform;
+
+	union {
+		/** Host name */
+		char *name;
+		/** Host address */
+		inet_addr_t addr;
+	} host;
+} inet_host_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/types/inet/hostport.h
===================================================================
--- uspace/lib/inet/include/types/inet/hostport.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/types/inet/hostport.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_TYPES_INET_HOSTPORT_H
+#define LIBINET_TYPES_INET_HOSTPORT_H
+
+#include <inet/addr.h>
+#include <stdint.h>
+#include <types/inet/host.h>
+
+/** Internet host:port specification
+ *
+ * As in RFC 1738 Uniform Resouce Locators (URL) and RFC 2732 Format for
+ * literal IPv6 Addresses in URLs
+ */
+typedef struct {
+	/** Host form */
+	inet_host_form_t hform;
+
+	union {
+		/** Host name */
+		char *name;
+		/** Host address */
+		inet_addr_t addr;
+	} host;
+
+	/** Port number of 0 if omitted */
+	uint16_t port;
+} inet_hostport_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/inet/include/types/inetcfg.h
===================================================================
--- uspace/lib/inet/include/types/inetcfg.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/types/inetcfg.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2021 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 libinet
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBINET_TYPES_INETCFG_H
+#define LIBINET_TYPES_INETCFG_H
+
+#include <inet/eth_addr.h>
+#include <inet/inet.h>
+#include <stddef.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 */
+	eth_addr_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/inet/include/types/inetping.h
===================================================================
--- uspace/lib/inet/include/types/inetping.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/include/types/inetping.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,55 @@
+/*
+ * 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 libinet
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef LIBINET_TYPES_INETPING_H
+#define LIBINET_TYPES_INETPING_H
+
+#include <inet/addr.h>
+#include <stddef.h>
+#include <stdint.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/inet/meson.build
===================================================================
--- uspace/lib/inet/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,50 @@
+#
+# Copyright (c) 2021 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.
+#
+
+src = files(
+	'src/addr.c',
+	'src/dhcp.c',
+	'src/dnsr.c',
+	'src/endpoint.c',
+	'src/eth_addr.c',
+	'src/host.c',
+	'src/hostname.c',
+	'src/hostport.c',
+	'src/inet.c',
+	'src/inetcfg.c',
+	'src/inetping.c',
+	'src/iplink.c',
+	'src/iplink_srv.c',
+	'src/tcp.c',
+	'src/udp.c',
+)
+
+test_src = files(
+	'test/eth_addr.c',
+	'test/main.c',
+)
Index: uspace/lib/inet/src/addr.c
===================================================================
--- uspace/lib/inet/src/addr.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/addr.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,721 @@
+/*
+ * Copyright (c) 2021 Jiri Svoboda
+ * 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 libinet
+ * @{
+ */
+/** @file Internet address parsing and formatting.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <inet/addr.h>
+#include <inet/eth_addr.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <bitops.h>
+#include <inttypes.h>
+#include <str.h>
+
+#define INET_PREFIXSTRSIZE  5
+
+#define INET6_ADDRSTRLEN (8 * 4 + 7 + 1)
+
+#if !(defined(__BE__) ^ defined(__LE__))
+#error The architecture must be either big-endian or little-endian.
+#endif
+
+const addr32_t addr32_broadcast_all_hosts = 0xffffffff;
+
+static eth_addr_t inet_eth_addr_solicited_node =
+    ETH_ADDR_INITIALIZER(0x33, 0x33, 0xff, 0, 0, 0);
+
+static const inet_addr_t inet_addr_any_addr = {
+	.version = ip_v4,
+	.addr = 0
+};
+
+static const inet_addr_t inet_addr_any_addr6 = {
+	.version = ip_v6,
+	.addr6 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+void addr128(const addr128_t src, addr128_t dst)
+{
+	memcpy(dst, src, 16);
+}
+
+/** 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) == 0;
+}
+
+/** Compute solicited node MAC multicast address from target IPv6 address
+ *
+ * @param ip  Target IPv6 address
+ * @param mac Solicited MAC address to be assigned
+ *
+ */
+void eth_addr_solicited_node(const addr128_t ip, eth_addr_t *mac)
+{
+	uint8_t b[6];
+	mac->a = inet_eth_addr_solicited_node.a;
+
+	eth_addr_encode(&inet_eth_addr_solicited_node, b);
+	memcpy(&b[3], ip + 13, 3);
+	eth_addr_decode(b, mac);
+}
+
+void host2addr128_t_be(const addr128_t host, addr128_t be)
+{
+	memcpy(be, host, 16);
+}
+
+void addr128_t_be2host(const addr128_t be, addr128_t host)
+{
+	memcpy(host, be, 16);
+}
+
+void inet_addr(inet_addr_t *addr, uint8_t a, uint8_t b, uint8_t c, uint8_t d)
+{
+	addr->version = ip_v4;
+	addr->addr = ((addr32_t) a << 24) | ((addr32_t) b << 16) |
+	    ((addr32_t) c << 8) | ((addr32_t) d);
+}
+
+void inet_naddr(inet_naddr_t *naddr, uint8_t a, uint8_t b, uint8_t c, uint8_t d,
+    uint8_t prefix)
+{
+	naddr->version = ip_v4;
+	naddr->addr = ((addr32_t) a << 24) | ((addr32_t) b << 16) |
+	    ((addr32_t) c << 8) | ((addr32_t) d);
+	naddr->prefix = prefix;
+}
+
+void inet_addr6(inet_addr_t *addr, uint16_t a, uint16_t b, uint16_t c,
+    uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h)
+{
+	addr->version = ip_v6;
+	addr->addr6[0] = (a >> 8) & 0xff;
+	addr->addr6[1] = a & 0xff;
+	addr->addr6[2] = (b >> 8) & 0xff;
+	addr->addr6[3] = b & 0xff;
+	addr->addr6[4] = (c >> 8) & 0xff;
+	addr->addr6[5] = c & 0xff;
+	addr->addr6[6] = (d >> 8) & 0xff;
+	addr->addr6[7] = d & 0xff;
+	addr->addr6[8] = (e >> 8) & 0xff;
+	addr->addr6[9] = e & 0xff;
+	addr->addr6[10] = (f >> 8) & 0xff;
+	addr->addr6[11] = f & 0xff;
+	addr->addr6[12] = (g >> 8) & 0xff;
+	addr->addr6[13] = g & 0xff;
+	addr->addr6[14] = (h >> 8) & 0xff;
+	addr->addr6[15] = h & 0xff;
+}
+
+void inet_naddr6(inet_naddr_t *naddr, uint16_t a, uint16_t b, uint16_t c,
+    uint16_t d, uint16_t e, uint16_t f, uint16_t g, uint16_t h, uint8_t prefix)
+{
+	naddr->version = ip_v6;
+	naddr->addr6[0] = (a >> 8) & 0xff;
+	naddr->addr6[1] = a & 0xff;
+	naddr->addr6[2] = (b >> 8) & 0xff;
+	naddr->addr6[3] = b & 0xff;
+	naddr->addr6[4] = (c >> 8) & 0xff;
+	naddr->addr6[5] = c & 0xff;
+	naddr->addr6[6] = (d >> 8) & 0xff;
+	naddr->addr6[7] = d & 0xff;
+	naddr->addr6[8] = (e >> 8) & 0xff;
+	naddr->addr6[9] = e & 0xff;
+	naddr->addr6[10] = (f >> 8) & 0xff;
+	naddr->addr6[11] = f & 0xff;
+	naddr->addr6[12] = (g >> 8) & 0xff;
+	naddr->addr6[13] = g & 0xff;
+	naddr->addr6[14] = (h >> 8) & 0xff;
+	naddr->addr6[15] = h & 0xff;
+	naddr->prefix = prefix;
+}
+
+void inet_naddr_addr(const inet_naddr_t *naddr, inet_addr_t *addr)
+{
+	addr->version = naddr->version;
+	memcpy(addr->addr6, naddr->addr6, 16);
+}
+
+void inet_addr_naddr(const inet_addr_t *addr, uint8_t prefix,
+    inet_naddr_t *naddr)
+{
+	naddr->version = addr->version;
+	memcpy(naddr->addr6, addr->addr6, 16);
+	naddr->prefix = prefix;
+}
+
+void inet_addr_any(inet_addr_t *addr)
+{
+	addr->version = ip_any;
+	memset(addr->addr6, 0, 16);
+}
+
+void inet_naddr_any(inet_naddr_t *naddr)
+{
+	naddr->version = ip_any;
+	memset(naddr->addr6, 0, 16);
+	naddr->prefix = 0;
+}
+
+int inet_addr_compare(const inet_addr_t *a, const inet_addr_t *b)
+{
+	if (a->version != b->version)
+		return 0;
+
+	switch (a->version) {
+	case ip_v4:
+		return (a->addr == b->addr);
+	case ip_v6:
+		return addr128_compare(a->addr6, b->addr6);
+	default:
+		return 0;
+	}
+}
+
+int inet_addr_is_any(const inet_addr_t *addr)
+{
+	return ((addr->version == ip_any) ||
+	    (inet_addr_compare(addr, &inet_addr_any_addr)) ||
+	    (inet_addr_compare(addr, &inet_addr_any_addr6)));
+}
+
+int inet_naddr_compare(const inet_naddr_t *naddr, const inet_addr_t *addr)
+{
+	if (naddr->version != addr->version)
+		return 0;
+
+	switch (naddr->version) {
+	case ip_v4:
+		return (naddr->addr == addr->addr);
+	case ip_v6:
+		return addr128_compare(naddr->addr6, addr->addr6);
+	default:
+		return 0;
+	}
+}
+
+int inet_naddr_compare_mask(const inet_naddr_t *naddr, const inet_addr_t *addr)
+{
+	if (naddr->version != addr->version)
+		return 0;
+
+	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 ip_v6:
+		if (naddr->prefix > 128)
+			return 0;
+
+		size_t pos = 0;
+		for (size_t i = 0; i < 16; i++) {
+			/* Further bits do not matter */
+			if (naddr->prefix < pos)
+				break;
+
+			if (naddr->prefix - pos > 8) {
+				/* Comparison without masking */
+				if (naddr->addr6[i] != addr->addr6[i])
+					return 0;
+			} else {
+				/* Comparison with masking */
+				uint8_t mask =
+				    BIT_RANGE(uint8_t, 8, 8 - (naddr->prefix - pos - 1));
+				if ((naddr->addr6[i] & mask) != (addr->addr6[i] & mask))
+					return 0;
+			}
+
+			pos += 8;
+		}
+
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static errno_t inet_addr_parse_v4(const char *str, inet_addr_t *raddr,
+    int *prefix, char **endptr)
+{
+	uint32_t a = 0;
+	uint8_t b;
+	char *cur = (char *)str;
+	size_t i = 0;
+
+	while (i < 4) {
+		errno_t rc = str_uint8_t(cur, (const char **)&cur, 10, false, &b);
+		if (rc != EOK)
+			return rc;
+
+		a = (a << 8) + b;
+
+		i++;
+
+		if (*cur != '.')
+			break;
+
+		if (i < 4)
+			cur++;
+	}
+
+	if (prefix != NULL) {
+		if (*cur != '/')
+			return EINVAL;
+		cur++;
+
+		*prefix = strtoul(cur, &cur, 10);
+		if (*prefix > 32)
+			return EINVAL;
+	}
+
+	if (i != 4)
+		return EINVAL;
+
+	if (endptr == NULL && *cur != '\0')
+		return EINVAL;
+
+	raddr->version = ip_v4;
+	raddr->addr = a;
+
+	if (endptr != NULL)
+		*endptr = cur;
+
+	return EOK;
+}
+
+static errno_t inet_addr_parse_v6(const char *str, inet_addr_t *raddr, int *prefix,
+    char **endptr)
+{
+	uint8_t data[16];
+	int explicit_groups;
+
+	memset(data, 0, 16);
+
+	const char *cur = str;
+	size_t i = 0;
+	size_t wildcard_pos = (size_t) -1;
+	size_t wildcard_size = 0;
+
+	/* Handle initial wildcard */
+	if ((str[0] == ':') && (str[1] == ':')) {
+		cur = str + 2;
+		wildcard_pos = 0;
+		wildcard_size = 16;
+	}
+
+	while (i < 16) {
+		uint16_t bioctet;
+		const char *gend;
+		errno_t rc = str_uint16_t(cur, &gend, 16, false, &bioctet);
+		if (rc != EOK)
+			break;
+
+		data[i] = (bioctet >> 8) & 0xff;
+		data[i + 1] = bioctet & 0xff;
+
+		if (wildcard_pos != (size_t) -1) {
+			if (wildcard_size < 2)
+				return EINVAL;
+
+			wildcard_size -= 2;
+		}
+
+		i += 2;
+
+		if (*gend != ':') {
+			cur = gend;
+			break;
+		}
+
+		if (i < 16) {
+			/* Handle wildcard */
+			if (gend[1] == ':') {
+				if (wildcard_pos != (size_t) -1)
+					return EINVAL;
+
+				wildcard_pos = i;
+				wildcard_size = 16 - i;
+				cur = gend + 2;
+			}
+		}
+	}
+
+	/* Number of explicitly specified groups */
+	explicit_groups = i;
+
+	if (prefix != NULL) {
+		if (*cur != '/')
+			return EINVAL;
+		cur++;
+
+		*prefix = strtoul(cur, (char **)&cur, 10);
+		if (*prefix > 128)
+			return EINVAL;
+	}
+
+	if (endptr == NULL && *cur != '\0')
+		return EINVAL;
+
+	/* Create wildcard positions */
+	if ((wildcard_pos != (size_t) -1) && (wildcard_size > 0)) {
+		size_t wildcard_shift = 16 - wildcard_size;
+
+		for (i = wildcard_pos + wildcard_shift; i > wildcard_pos; i--) {
+			size_t j = i - 1;
+			data[j + wildcard_size] = data[j];
+			data[j] = 0;
+		}
+	} else {
+		/* Verify that all groups have been specified */
+		if (explicit_groups != 16)
+			return EINVAL;
+	}
+
+	raddr->version = ip_v6;
+	memcpy(raddr->addr6, data, 16);
+	if (endptr != NULL)
+		*endptr = (char *)cur;
+	return EOK;
+}
+
+/** Parse node address.
+ *
+ * Will fail if @a text contains extra characters at the and and @a endptr
+ * is @c NULL.
+ *
+ * @param text Network address in common notation.
+ * @param addr Place to store node address.
+ * @param endptr Place to store pointer to next character oc @c NULL
+ *
+ * @return EOK on success, EINVAL if input is not in valid format.
+ *
+ */
+errno_t inet_addr_parse(const char *text, inet_addr_t *addr, char **endptr)
+{
+	errno_t rc;
+
+	rc = inet_addr_parse_v4(text, addr, NULL, endptr);
+	if (rc == EOK)
+		return EOK;
+
+	rc = inet_addr_parse_v6(text, addr, NULL, endptr);
+	if (rc == EOK)
+		return EOK;
+
+	return EINVAL;
+}
+
+/** Parse network address.
+ *
+ * Will fail if @a text contains extra characters at the and and @a endptr
+ * is @c NULL.
+ *
+ * @param text  Network address in common notation.
+ * @param naddr Place to store network address.
+ * @param endptr Place to store pointer to next character oc @c NULL
+ *
+ * @return EOK on success, EINVAL if input is not in valid format.
+ *
+ */
+errno_t inet_naddr_parse(const char *text, inet_naddr_t *naddr, char **endptr)
+{
+	errno_t rc;
+	inet_addr_t addr;
+	int prefix;
+
+	rc = inet_addr_parse_v4(text, &addr, &prefix, endptr);
+	if (rc == EOK) {
+		inet_addr_naddr(&addr, prefix, naddr);
+		return EOK;
+	}
+
+	rc = inet_addr_parse_v6(text, &addr, &prefix, endptr);
+	if (rc == EOK) {
+		inet_addr_naddr(&addr, prefix, naddr);
+		return EOK;
+	}
+
+	return EINVAL;
+}
+
+static errno_t inet_addr_format_v4(addr32_t addr, char **bufp)
+{
+	int rc;
+
+	rc = asprintf(bufp, "%u.%u.%u.%u", (addr >> 24) & 0xff,
+	    (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
+	if (rc < 0)
+		return ENOMEM;
+
+	return EOK;
+}
+
+static errno_t inet_addr_format_v6(const addr128_t addr, char **bufp)
+{
+	*bufp = (char *) malloc(INET6_ADDRSTRLEN);
+	if (*bufp == NULL)
+		return ENOMEM;
+
+	/* Find the longest zero subsequence */
+
+	uint16_t zeroes[8];
+	uint16_t bioctets[8];
+
+	for (size_t i = 8; i > 0; i--) {
+		size_t j = i - 1;
+
+		bioctets[j] = (addr[j << 1] << 8) | addr[(j << 1) + 1];
+
+		if (bioctets[j] == 0) {
+			zeroes[j] = 1;
+			if (j < 7)
+				zeroes[j] += zeroes[j + 1];
+		} else
+			zeroes[j] = 0;
+	}
+
+	size_t wildcard_pos = (size_t) -1;
+	size_t wildcard_size = 0;
+
+	for (size_t i = 0; i < 8; i++) {
+		if (zeroes[i] > wildcard_size) {
+			wildcard_pos = i;
+			wildcard_size = zeroes[i];
+		}
+	}
+
+	char *cur = *bufp;
+	size_t rest = INET6_ADDRSTRLEN;
+	bool tail_zero = false;
+	int ret;
+
+	for (size_t i = 0; i < 8; i++) {
+		if ((i == wildcard_pos) && (wildcard_size > 1)) {
+			ret = snprintf(cur, rest, ":");
+			i += wildcard_size - 1;
+			tail_zero = true;
+		} else if (i == 0) {
+			ret = snprintf(cur, rest, "%" PRIx16, bioctets[i]);
+			tail_zero = false;
+		} else {
+			ret = snprintf(cur, rest, ":%" PRIx16, bioctets[i]);
+			tail_zero = false;
+		}
+
+		if (ret < 0)
+			return EINVAL;
+
+		cur += ret;
+		rest -= ret;
+	}
+
+	if (tail_zero)
+		(void) snprintf(cur, rest, ":");
+
+	return EOK;
+}
+
+/** Format node address.
+ *
+ * @param addr Node address.
+ * @param bufp Place to store pointer to formatted string.
+ *
+ * @return EOK on success.
+ * @return ENOMEM if out of memory.
+ * @return ENOTSUP on unsupported address family.
+ *
+ */
+errno_t inet_addr_format(const inet_addr_t *addr, char **bufp)
+{
+	errno_t rc;
+	int ret;
+
+	rc = ENOTSUP;
+
+	switch (addr->version) {
+	case ip_any:
+		ret = asprintf(bufp, "none");
+		if (ret < 0)
+			return ENOMEM;
+		rc = EOK;
+		break;
+	case ip_v4:
+		rc = inet_addr_format_v4(addr->addr, bufp);
+		break;
+	case ip_v6:
+		rc = inet_addr_format_v6(addr->addr6, bufp);
+		break;
+	}
+
+	return rc;
+}
+
+/** Format network address.
+ *
+ * @param naddr Network address.
+ * @param bufp  Place to store pointer to formatted string.
+ *
+ * @return EOK on success.
+ * @return ENOMEM if out of memory.
+ * @return ENOTSUP on unsupported address family.
+ *
+ */
+errno_t inet_naddr_format(const inet_naddr_t *naddr, char **bufp)
+{
+	errno_t rc;
+	int ret;
+	char *astr;
+
+	rc = ENOTSUP;
+
+	switch (naddr->version) {
+	case ip_any:
+		ret = asprintf(bufp, "none");
+		if (ret < 0)
+			return ENOMEM;
+		rc = EOK;
+		break;
+	case ip_v4:
+		rc = inet_addr_format_v4(naddr->addr, &astr);
+		if (rc != EOK)
+			return ENOMEM;
+
+		ret = asprintf(bufp, "%s/%" PRIu8, astr, naddr->prefix);
+		if (ret < 0) {
+			free(astr);
+			return ENOMEM;
+		}
+
+		rc = EOK;
+		break;
+	case ip_v6:
+		rc = inet_addr_format_v6(naddr->addr6, &astr);
+		if (rc != EOK)
+			return ENOMEM;
+
+		ret = asprintf(bufp, "%s/%" PRIu8, astr, naddr->prefix);
+		if (ret < 0) {
+			free(astr);
+			return ENOMEM;
+		}
+
+		rc = EOK;
+		break;
+	}
+
+	return rc;
+}
+
+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 ip_v6:
+		if (v6 != NULL)
+			memcpy(*v6, addr->addr6, 16);
+		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->version) {
+	case ip_v4:
+		if (v4 != NULL)
+			*v4 = naddr->addr;
+		if (prefix != NULL)
+			*prefix = naddr->prefix;
+		break;
+	case ip_v6:
+		if (v6 != NULL)
+			memcpy(*v6, naddr->addr6, 16);
+		if (prefix != NULL)
+			*prefix = naddr->prefix;
+		break;
+	default:
+		assert(false);
+		break;
+	}
+
+	return naddr->version;
+}
+
+void inet_addr_set(addr32_t v4, inet_addr_t *addr)
+{
+	addr->version = ip_v4;
+	addr->addr = v4;
+}
+
+void inet_naddr_set(addr32_t v4, uint8_t prefix, inet_naddr_t *naddr)
+{
+	naddr->version = ip_v4;
+	naddr->addr = v4;
+	naddr->prefix = prefix;
+}
+
+void inet_addr_set6(addr128_t v6, inet_addr_t *addr)
+{
+	addr->version = ip_v6;
+	memcpy(addr->addr6, v6, 16);
+}
+
+void inet_naddr_set6(addr128_t v6, uint8_t prefix, inet_naddr_t *naddr)
+{
+	naddr->version = ip_v6;
+	memcpy(naddr->addr6, v6, 16);
+	naddr->prefix = prefix;
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/dhcp.c
===================================================================
--- uspace/lib/inet/src/dhcp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/dhcp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,97 @@
+/*
+ * 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 libinet
+ * @{
+ */
+/** @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;
+
+errno_t dhcp_init(void)
+{
+	service_id_t dhcp_svc;
+	errno_t 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(dhcp_svc, INTERFACE_DHCP,
+	    IPC_FLAG_BLOCKING);
+	if (dhcp_sess == NULL)
+		return ENOENT;
+
+	return EOK;
+}
+
+errno_t dhcp_link_add(sysarg_t link_id)
+{
+	async_exch_t *exch = async_exchange_begin(dhcp_sess);
+
+	errno_t rc = async_req_1_0(exch, DHCP_LINK_ADD, link_id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+errno_t dhcp_link_remove(sysarg_t link_id)
+{
+	async_exch_t *exch = async_exchange_begin(dhcp_sess);
+
+	errno_t rc = async_req_1_0(exch, DHCP_LINK_REMOVE, link_id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+errno_t dhcp_discover(sysarg_t link_id)
+{
+	async_exch_t *exch = async_exchange_begin(dhcp_sess);
+
+	errno_t rc = async_req_1_0(exch, DHCP_DISCOVER, link_id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/dnsr.c
===================================================================
--- uspace/lib/inet/src/dnsr.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/dnsr.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,195 @@
+/*
+ * 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 <errno.h>
+#include <fibril_synch.h>
+#include <inet/dnsr.h>
+#include <ipc/dnsr.h>
+#include <ipc/services.h>
+#include <loc.h>
+#include <stdlib.h>
+#include <str.h>
+
+static FIBRIL_MUTEX_INITIALIZE(dnsr_sess_mutex);
+
+static async_sess_t *dnsr_sess = NULL;
+
+static async_exch_t *dnsr_exchange_begin(void)
+{
+	fibril_mutex_lock(&dnsr_sess_mutex);
+
+	if (dnsr_sess == NULL) {
+		service_id_t dnsr_svc;
+
+		(void) loc_service_get_id(SERVICE_NAME_DNSR, &dnsr_svc,
+		    IPC_FLAG_BLOCKING);
+
+		dnsr_sess = loc_service_connect(dnsr_svc, INTERFACE_DNSR,
+		    IPC_FLAG_BLOCKING);
+	}
+
+	async_sess_t *sess = dnsr_sess;
+	fibril_mutex_unlock(&dnsr_sess_mutex);
+
+	return async_exchange_begin(sess);
+}
+
+static void dnsr_exchange_end(async_exch_t *exch)
+{
+	async_exchange_end(exch);
+}
+
+errno_t dnsr_name2host(const char *name, dnsr_hostinfo_t **rinfo, ip_ver_t ver)
+{
+	async_exch_t *exch = dnsr_exchange_begin();
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, DNSR_NAME2HOST, (sysarg_t) ver,
+	    &answer);
+
+	errno_t rc = async_data_write_start(exch, name, str_size(name));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	dnsr_hostinfo_t *info = calloc(1, sizeof(dnsr_hostinfo_t));
+	if (info == NULL)
+		return ENOMEM;
+
+	ipc_call_t answer_addr;
+	aid_t req_addr = async_data_read(exch, &info->addr,
+	    sizeof(inet_addr_t), &answer_addr);
+
+	errno_t retval_addr;
+	async_wait_for(req_addr, &retval_addr);
+
+	if (retval_addr != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		free(info);
+		return retval_addr;
+	}
+
+	ipc_call_t answer_cname;
+	char cname_buf[DNSR_NAME_MAX_SIZE + 1];
+	aid_t req_cname = async_data_read(exch, cname_buf, DNSR_NAME_MAX_SIZE,
+	    &answer_cname);
+
+	dnsr_exchange_end(exch);
+
+	errno_t retval_cname;
+	async_wait_for(req_cname, &retval_cname);
+
+	if (retval_cname != EOK) {
+		async_forget(req);
+		free(info);
+		return retval_cname;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		async_forget(req);
+		free(info);
+		return retval;
+	}
+
+	size_t act_size = ipc_get_arg2(&answer_cname);
+	assert(act_size <= DNSR_NAME_MAX_SIZE);
+
+	cname_buf[act_size] = '\0';
+
+	info->cname = str_dup(cname_buf);
+
+	if (info->cname == NULL) {
+		free(info);
+		return ENOMEM;
+	}
+
+	*rinfo = info;
+	return EOK;
+}
+
+void dnsr_hostinfo_destroy(dnsr_hostinfo_t *info)
+{
+	if (info == NULL)
+		return;
+
+	free(info->cname);
+	free(info);
+}
+
+errno_t dnsr_get_srvaddr(inet_addr_t *srvaddr)
+{
+	async_exch_t *exch = dnsr_exchange_begin();
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, DNSR_GET_SRVADDR, &answer);
+	errno_t rc = async_data_read_start(exch, srvaddr, sizeof(inet_addr_t));
+
+	loc_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+errno_t dnsr_set_srvaddr(inet_addr_t *srvaddr)
+{
+	async_exch_t *exch = dnsr_exchange_begin();
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, DNSR_SET_SRVADDR, &answer);
+	errno_t rc = async_data_write_start(exch, srvaddr, sizeof(inet_addr_t));
+
+	loc_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/endpoint.c
===================================================================
--- uspace/lib/inet/src/endpoint.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/endpoint.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015 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 libinet
+ * @{
+ */
+/** @file Internet endpoint
+ */
+
+#include <inet/endpoint.h>
+#include <mem.h>
+
+/** Initialize endpoint structure.
+ *
+ * Sets any address, any port number.
+ *
+ * @param ep Endpoint
+ */
+void inet_ep_init(inet_ep_t *ep)
+{
+	memset(ep, 0, sizeof(*ep));
+}
+
+/** Initialize endpoint pair structure.
+ *
+ * Sets any address, any port number for both local and remote sides.
+ *
+ * @param ep2 Endpoint pair
+ */
+void inet_ep2_init(inet_ep2_t *ep2)
+{
+	memset(ep2, 0, sizeof(*ep2));
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/eth_addr.c
===================================================================
--- uspace/lib/inet/src/eth_addr.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/eth_addr.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2021 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 libinet
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#include <inet/eth_addr.h>
+#include <mem.h>
+#include <stdio.h>
+
+/** Ethernet broadcast address */
+const eth_addr_t eth_addr_broadcast =
+    ETH_ADDR_INITIALIZER(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
+
+/** Encode Ethernet address to buffer.
+ *
+ * Encode Ethernet address as a sequence of ETH_ADDR_SIZE bytes into a buffer.
+ *
+ * @param addr Ethernet address
+ * @param buf Buffer (ETH_ADDR_SIZE bytes in size) to store bytes
+ */
+void eth_addr_encode(eth_addr_t *addr, uint8_t *buf)
+{
+	uint64_t a;
+	int i;
+
+	a = addr->a;
+
+	for (i = 0; i < ETH_ADDR_SIZE; i++)
+		buf[i] = (a >> (40 - 8 * i)) & 0xff;
+}
+
+/** Decode Ethernet address from buffer.
+ *
+ * Decode Ethernet address from a buffer containing a sequence of
+ * ETH_ADDR_SIZE bytes.
+ *
+ * @param buf Buffer (ETH_ADDR_SIZE bytes in size)
+ * @param addr Place to store Ethernet address
+ */
+void eth_addr_decode(const uint8_t *buf, eth_addr_t *addr)
+{
+	uint64_t a;
+	int i;
+
+	a = 0;
+	for (i = 0; i < ETH_ADDR_SIZE; i++)
+		a |= (uint64_t)buf[i] << (40 - 8 * i);
+
+	addr->a = a;
+}
+
+/** Compare Ethernet addresses.
+ *
+ * @param a First address
+ * @param b Second address,
+ * @return -1, 0, 1 iff @a a is less than, equal to or greater than @a b,
+ *         respectively.
+ */
+int eth_addr_compare(const eth_addr_t *a, const eth_addr_t *b)
+{
+	if (a->a < b->a)
+		return -1;
+	else if (a->a == b->a)
+		return 0;
+	else
+		return 1;
+}
+
+/** Format Ethernet address as a string.
+ *
+ * @param addr Ethernet address
+ * @param saddr Structure for storing string representation
+ *        of @a addr. The caller can access it as @a saddr->str.
+ */
+void eth_addr_format(eth_addr_t *addr, eth_addr_str_t *saddr)
+{
+	int i;
+
+	snprintf(saddr->str, 3, "%02x",
+	    (unsigned)((addr->a >> 40) & 0xff));
+	for (i = 1; i < ETH_ADDR_SIZE; i++) {
+		snprintf(saddr->str + 2 + 3 * (i - 1), 4, ":%02x",
+		    (unsigned)((addr->a >> (40 - i * 8)) & 0xff));
+	}
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/host.c
===================================================================
--- uspace/lib/inet/src/host.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/host.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2016 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 libinet
+ * @{
+ */
+/** @file Internet host specification
+ *
+ * Either a host name or an address.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <inet/addr.h>
+#include <inet/dnsr.h>
+#include <inet/host.h>
+#include <inet/hostname.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+
+/** Parse host string.
+ *
+ * @param str Host string
+ * @param rhost Place to store pointer to new host structure
+ * @param endptr Place to store pointer to next character in string or @c NULL
+ *
+ * @return EOK on success. EINVAL if @a str does not start with a valid
+ *         host specification or if @a endptr is @c NULL and @a str contains
+ *         extra characters at the end. ENOMEM if out of memory
+ */
+errno_t inet_host_parse(const char *str, inet_host_t **rhost,
+    char **endptr)
+{
+	inet_host_t *host;
+	inet_addr_t addr;
+	char *name;
+	char *aend;
+	errno_t rc;
+
+	host = calloc(1, sizeof(inet_host_t));
+	if (host == NULL)
+		return ENOMEM;
+
+	/* Try address */
+	rc = inet_addr_parse(str, &addr, &aend);
+	if (rc == EOK) {
+		host->hform = inet_host_addr;
+		host->host.addr = addr;
+		goto have_host;
+	}
+
+	/* Try <hostname> */
+	rc = inet_hostname_parse(str, &name, &aend);
+	if (rc == EOK) {
+		host->hform = inet_host_name;
+		host->host.name = name;
+		goto have_host;
+	}
+
+	free(host);
+	return EINVAL;
+
+have_host:
+	if (*aend != '\0' && endptr == NULL) {
+		/* Extra characters */
+		free(host);
+		return EINVAL;
+	}
+
+	*rhost = host;
+	if (endptr != NULL)
+		*endptr = (char *)aend;
+	return EOK;
+}
+
+/** Convert host structure to string representation.
+ *
+ * @param host Host structure
+ * @param rstr Place to store pointer to new string
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t inet_host_format(inet_host_t *host, char **rstr)
+{
+	errno_t rc;
+	char *str = NULL;
+
+	switch (host->hform) {
+	case inet_host_addr:
+		rc = inet_addr_format(&host->host.addr, &str);
+		if (rc != EOK) {
+			assert(rc == ENOMEM);
+			return ENOMEM;
+		}
+
+		break;
+	case inet_host_name:
+		str = str_dup(host->host.name);
+		if (str == NULL)
+			return ENOMEM;
+		break;
+	}
+
+	*rstr = str;
+	return EOK;
+}
+
+/** Destroy host structure.
+ *
+ * @param host Host structure or @c NULL
+ */
+void inet_host_destroy(inet_host_t *host)
+{
+	if (host == NULL)
+		return;
+
+	switch (host->hform) {
+	case inet_host_addr:
+		break;
+	case inet_host_name:
+		free(host->host.name);
+		break;
+	}
+
+	free(host);
+}
+
+/** Look up first address corresponding to host.
+ *
+ * If host contains a host name, name resolution is performed.
+ *
+ * @param host Host structure
+ * @param version Desired IP version (ip_any to get any version)
+ * @param addr Place to store address
+ *
+ * @reutrn EOK on success, ENOENT on resolurion failure
+ */
+errno_t inet_host_lookup_one(inet_host_t *host, ip_ver_t version, inet_addr_t *addr)
+{
+	dnsr_hostinfo_t *hinfo = NULL;
+	errno_t rc;
+
+	switch (host->hform) {
+	case inet_host_addr:
+		*addr = host->host.addr;
+		break;
+	case inet_host_name:
+		rc = dnsr_name2host(host->host.name, &hinfo, version);
+		if (rc != EOK) {
+			dnsr_hostinfo_destroy(hinfo);
+			return ENOENT;
+		}
+
+		*addr = hinfo->addr;
+		dnsr_hostinfo_destroy(hinfo);
+		break;
+	}
+
+	return EOK;
+}
+
+/** Look up first address corresponding to host string.
+ *
+ * If host contains a host name, name resolution is performed.
+ *
+ * @param str Host string
+ * @param version Desired IP version (ip_any to get any version)
+ * @param addr Place to store address
+ * @param endptr Place to store pointer to next character in string or @c NULL
+ * @param errmsg Place to store pointer to error message (no need to free it)
+ *               or @c NULL
+ *
+ * @return EOK on success. EINVAL if @a str has incorrect format or if
+ *         @a endptr is @c NULL and @a str contains extra characters at
+ *         the end. ENOENT if host was not found during resolution,
+ *         ENOMEM if out of memory
+ */
+errno_t inet_host_plookup_one(const char *str, ip_ver_t version, inet_addr_t *addr,
+    char **endptr, const char **errmsg)
+{
+	inet_host_t *host = NULL;
+	char *eptr;
+	errno_t rc;
+
+	rc = inet_host_parse(str, &host, endptr != NULL ? &eptr : NULL);
+	if (rc != EOK) {
+		switch (rc) {
+		case EINVAL:
+			if (errmsg != NULL)
+				*errmsg = "Invalid format";
+			goto error;
+		case ENOMEM:
+			if (errmsg != NULL)
+				*errmsg = "Out of memory";
+			goto error;
+		default:
+			assert(false);
+		}
+	}
+
+	rc = inet_host_lookup_one(host, version, addr);
+	if (rc != EOK) {
+		/* XXX Distinguish between 'not found' and other error */
+		rc = ENOENT;
+		if (errmsg != NULL)
+			*errmsg = "Name resolution failed";
+		goto error;
+	}
+
+	inet_host_destroy(host);
+	if (endptr != NULL)
+		*endptr = eptr;
+	return EOK;
+error:
+	inet_host_destroy(host);
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/hostname.c
===================================================================
--- uspace/lib/inet/src/hostname.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/hostname.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2016 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 libinet
+ * @{
+ */
+/** @file Internet host name
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <inet/hostname.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <str.h>
+
+/** Parse host name
+ *
+ * Determine whether string begins with a valid host name and where that
+ * host name ends.
+ *
+ * Currntly accepting names per 'host' definition in RFC 1738
+ * (Uniform Resource Locators) - generic URI syntax not accepted.
+ *
+ * @param str String containing host name
+ * @param rname Place to store pointer to new string containing just the
+ *              host name or @c NULL if not interested
+ * @param endptr Place to store pointer to next character in string or @c NULL
+ *               if no extra characters in the string are allowed
+ *
+ * @return EOK on success. EINVAL if @a str does not start with a valid
+ *         host name or if @a endptr is @c NULL and @a str contains
+ *         extra characters at the end. ENOMEM if out of memory
+ */
+errno_t inet_hostname_parse(const char *str, char **rname, char **endptr)
+{
+	const char *c;
+	const char *dstart = NULL;
+	char *name;
+
+	c = str;
+	while (isalnum(*c)) {
+		/* Domain label */
+		dstart = c;
+
+		do {
+			++c;
+		} while (isalnum(*c) || *c == '-');
+
+		/* Domain separator? */
+		if (*c != '.' || !isalnum(c[1]))
+			break;
+
+		++c;
+	}
+
+	/*
+	 * Last domain label must not start with a digit (to distinguish from
+	 * IPv4 address)
+	 */
+	if (dstart == NULL || !isalpha(*dstart))
+		return EINVAL;
+
+	/* If endptr is null, check that entire string matched */
+	if (endptr == NULL && *c != '\0')
+		return EINVAL;
+
+	if (endptr != NULL)
+		*endptr = (char *)c;
+
+	if (rname != NULL) {
+		name = str_ndup(str, c - str);
+		if (name == NULL)
+			return ENOMEM;
+
+		*rname = name;
+	}
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/hostport.c
===================================================================
--- uspace/lib/inet/src/hostport.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/hostport.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2016 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 libinet
+ * @{
+ */
+/** @file Internet host:port specification
+ *
+ * As in RFC 1738 Uniform Resouce Locators (URL) and RFC 2732 Format for
+ * literal IPv6 Addresses in URLs
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <inet/addr.h>
+#include <inet/dnsr.h>
+#include <inet/endpoint.h>
+#include <inet/hostname.h>
+#include <inet/hostport.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str.h>
+
+/** Parse host:port string.
+ *
+ * @param str Host:port string
+ * @param rhp Place to store pointer to new hostport structure
+ * @param endptr Place to store pointer to next character in string or @c NULL
+ *
+ * @return EOK on success. EINVAL if @a str does not start with a valid
+ *         host:port or if @a endptr is @c NULL and @a str contains
+ *         extra characters at the end. ENOMEM if out of memory
+ */
+errno_t inet_hostport_parse(const char *str, inet_hostport_t **rhp,
+    char **endptr)
+{
+	inet_hostport_t *hp;
+	inet_addr_t addr;
+	uint16_t port;
+	char *name;
+	char *aend;
+	const char *pend;
+	errno_t rc;
+
+	hp = calloc(1, sizeof(inet_hostport_t));
+	if (hp == NULL)
+		return ENOMEM;
+
+	if (str[0] == '[') {
+		/* Try [<ip-addr-literal>] */
+		rc = inet_addr_parse(str + 1, &addr, &aend);
+		if (rc == EOK) {
+			if (*aend == ']') {
+				hp->hform = inet_host_addr;
+				hp->host.addr = addr;
+				++aend;
+			}
+			goto have_host;
+		}
+	}
+
+	/* Try <ipv4-addr> */
+	rc = inet_addr_parse(str, &addr, &aend);
+	if (rc == EOK) {
+		hp->hform = inet_host_addr;
+		hp->host.addr = addr;
+		goto have_host;
+	}
+
+	/* Try <hostname> */
+	rc = inet_hostname_parse(str, &name, &aend);
+	if (rc == EOK) {
+		hp->hform = inet_host_name;
+		hp->host.name = name;
+		goto have_host;
+	}
+
+	free(hp);
+	return EINVAL;
+
+have_host:
+	if (*aend == ':') {
+		/* Port number */
+		rc = str_uint16_t(aend + 1, &pend, 10, false, &port);
+		if (rc != EOK) {
+			free(hp);
+			return EINVAL;
+		}
+	} else {
+		/* Port number omitted */
+		port = 0;
+		pend = aend;
+	}
+
+	hp->port = port;
+
+	if (*pend != '\0' && endptr == NULL) {
+		/* Extra characters */
+		free(hp);
+		return EINVAL;
+	}
+
+	*rhp = hp;
+	if (endptr != NULL)
+		*endptr = (char *)pend;
+	return EOK;
+}
+
+/** Convert host:port to string representation.
+ *
+ * @param hp Host:port structure
+ * @param rstr Place to store pointer to new string
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t inet_hostport_format(inet_hostport_t *hp, char **rstr)
+{
+	errno_t rc;
+	int ret;
+	char *astr, *str;
+	char *hstr = NULL;
+
+	switch (hp->hform) {
+	case inet_host_addr:
+		rc = inet_addr_format(&hp->host.addr, &astr);
+		if (rc != EOK) {
+			assert(rc == ENOMEM);
+			return ENOMEM;
+		}
+
+		if (hp->host.addr.version != ip_v4) {
+			hstr = astr;
+		} else {
+			ret = asprintf(&hstr, "[%s]", astr);
+			if (ret < 0) {
+				free(astr);
+				return ENOMEM;
+			}
+
+			free(astr);
+		}
+
+		break;
+	case inet_host_name:
+		hstr = str_dup(hp->host.name);
+		if (hstr == NULL)
+			return ENOMEM;
+		break;
+	}
+
+	ret = asprintf(&str, "%s:%u", hstr, hp->port);
+	if (ret < 0)
+		return ENOMEM;
+
+	free(hstr);
+	*rstr = str;
+	return EOK;
+}
+
+/** Destroy host:port structure.
+ *
+ * @param hp Host:port or @c NULL
+ */
+void inet_hostport_destroy(inet_hostport_t *hp)
+{
+	if (hp == NULL)
+		return;
+
+	switch (hp->hform) {
+	case inet_host_addr:
+		break;
+	case inet_host_name:
+		free(hp->host.name);
+		break;
+	}
+
+	free(hp);
+}
+
+/** Look up first endpoint corresponding to host:port.
+ *
+ * If host:port contains a host name, name resolution is performed.
+ *
+ * @param hp Host:port structure
+ * @param version Desired IP version (ip_any to get any version)
+ * @param ep Place to store endpoint
+ *
+ * @return EOK on success, ENOENT on resolution failure
+ */
+errno_t inet_hostport_lookup_one(inet_hostport_t *hp, ip_ver_t version,
+    inet_ep_t *ep)
+{
+	dnsr_hostinfo_t *hinfo = NULL;
+	errno_t rc;
+
+	inet_ep_init(ep);
+
+	switch (hp->hform) {
+	case inet_host_addr:
+		ep->addr = hp->host.addr;
+		break;
+	case inet_host_name:
+		rc = dnsr_name2host(hp->host.name, &hinfo, ip_any);
+		if (rc != EOK) {
+			dnsr_hostinfo_destroy(hinfo);
+			return ENOENT;
+		}
+
+		ep->addr = hinfo->addr;
+		dnsr_hostinfo_destroy(hinfo);
+		break;
+	}
+
+	ep->port = hp->port;
+	return EOK;
+}
+
+/** Look up first endpoint corresponding to host:port string.
+ *
+ * If host:port contains a host name, name resolution is performed.
+ *
+ * @param str Host:port string
+ * @param version Desired IP version (ip_any to get any version)
+ * @param ep Place to store endpoint
+ * @param endptr Place to store pointer to next character in string or @c NULL
+ * @param errmsg Place to store pointer to error message (no need to free it)
+ *               or @c NULL
+ *
+ * @return EOK on success. EINVAL if @a str has incorrect format or if
+ *         @a endptr is @c NULL and @a str contains extra characters at
+ *         the end. ENOENT if host was not found during resolution,
+ *         ENOMEM if out of memory
+ */
+errno_t inet_hostport_plookup_one(const char *str, ip_ver_t version, inet_ep_t *ep,
+    char **endptr, const char **errmsg)
+{
+	inet_hostport_t *hp = NULL;
+	char *eptr;
+	errno_t rc;
+
+	rc = inet_hostport_parse(str, &hp, endptr != NULL ? &eptr : NULL);
+	if (rc != EOK) {
+		switch (rc) {
+		case EINVAL:
+			if (errmsg != NULL)
+				*errmsg = "Invalid format";
+			goto error;
+		case ENOMEM:
+			if (errmsg != NULL)
+				*errmsg = "Out of memory";
+			goto error;
+		case EOK:
+			break;
+		default:
+			assert(false);
+		}
+	}
+
+	rc = inet_hostport_lookup_one(hp, version, ep);
+	if (rc != EOK) {
+		/* XXX Distinguish between 'not found' and other error */
+		rc = ENOENT;
+		if (errmsg != NULL)
+			*errmsg = "Name resolution failed";
+		goto error;
+	}
+
+	inet_hostport_destroy(hp);
+	if (endptr != NULL)
+		*endptr = eptr;
+	return EOK;
+error:
+	inet_hostport_destroy(hp);
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/inet.c
===================================================================
--- uspace/lib/inet/src/inet.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/inet.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,258 @@
+/*
+ * 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 <errno.h>
+#include <inet/inet.h>
+#include <ipc/inet.h>
+#include <ipc/services.h>
+#include <loc.h>
+#include <stdlib.h>
+
+static void inet_cb_conn(ipc_call_t *icall, void *arg);
+
+static async_sess_t *inet_sess = NULL;
+static inet_ev_ops_t *inet_ev_ops = NULL;
+static uint8_t inet_protocol = 0;
+
+static errno_t inet_callback_create(void)
+{
+	async_exch_t *exch = async_exchange_begin(inet_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, INET_CALLBACK_CREATE, &answer);
+
+	port_id_t port;
+	errno_t rc = async_create_callback_port(exch, INTERFACE_INET_CB, 0, 0,
+	    inet_cb_conn, NULL, &port);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+static errno_t inet_set_proto(uint8_t protocol)
+{
+	async_exch_t *exch = async_exchange_begin(inet_sess);
+	errno_t rc = async_req_1_0(exch, INET_SET_PROTO, protocol);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+errno_t inet_init(uint8_t protocol, inet_ev_ops_t *ev_ops)
+{
+	service_id_t inet_svc;
+	errno_t rc;
+
+	assert(inet_sess == NULL);
+	assert(inet_ev_ops == NULL);
+	assert(inet_protocol == 0);
+
+	rc = loc_service_get_id(SERVICE_NAME_INET, &inet_svc,
+	    IPC_FLAG_BLOCKING);
+	if (rc != EOK)
+		return ENOENT;
+
+	inet_sess = loc_service_connect(inet_svc, INTERFACE_INET,
+	    IPC_FLAG_BLOCKING);
+	if (inet_sess == NULL)
+		return ENOENT;
+
+	if (inet_set_proto(protocol) != EOK) {
+		async_hangup(inet_sess);
+		inet_sess = NULL;
+		return EIO;
+	}
+
+	if (inet_callback_create() != EOK) {
+		async_hangup(inet_sess);
+		inet_sess = NULL;
+		return EIO;
+	}
+
+	inet_protocol = protocol;
+	inet_ev_ops = ev_ops;
+
+	return EOK;
+}
+
+errno_t inet_send(inet_dgram_t *dgram, uint8_t ttl, inet_df_t df)
+{
+	async_exch_t *exch = async_exchange_begin(inet_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_4(exch, INET_SEND, dgram->iplink, dgram->tos,
+	    ttl, df, &answer);
+
+	errno_t rc = async_data_write_start(exch, &dgram->src, sizeof(inet_addr_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_write_start(exch, &dgram->dest, sizeof(inet_addr_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_write_start(exch, dgram->data, dgram->size);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+errno_t inet_get_srcaddr(inet_addr_t *remote, uint8_t tos, inet_addr_t *local)
+{
+	async_exch_t *exch = async_exchange_begin(inet_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, INET_GET_SRCADDR, tos, &answer);
+
+	errno_t rc = async_data_write_start(exch, remote, sizeof(inet_addr_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_read_start(exch, local, sizeof(inet_addr_t));
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+static void inet_ev_recv(ipc_call_t *icall)
+{
+	inet_dgram_t dgram;
+
+	dgram.tos = ipc_get_arg1(icall);
+	dgram.iplink = ipc_get_arg2(icall);
+
+	ipc_call_t call;
+	size_t size;
+	if (!async_data_write_receive(&call, &size)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	if (size != sizeof(inet_addr_t)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	errno_t rc = async_data_write_finalize(&call, &dgram.src, size);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	if (!async_data_write_receive(&call, &size)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	if (size != sizeof(inet_addr_t)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = async_data_write_finalize(&call, &dgram.dest, size);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = async_data_write_accept(&dgram.data, false, 0, 0, 0, &dgram.size);
+	if (rc != EOK) {
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = inet_ev_ops->recv(&dgram);
+	free(dgram.data);
+	async_answer_0(icall, rc);
+}
+
+static void inet_cb_conn(ipc_call_t *icall, void *arg)
+{
+	while (true) {
+		ipc_call_t call;
+		async_get_call(&call);
+
+		if (!ipc_get_imethod(&call)) {
+			async_answer_0(&call, EOK);
+			return;
+		}
+
+		switch (ipc_get_imethod(&call)) {
+		case INET_EV_RECV:
+			inet_ev_recv(&call);
+			break;
+		default:
+			async_answer_0(&call, ENOTSUP);
+		}
+	}
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/inetcfg.c
===================================================================
--- uspace/lib/inet/src/inetcfg.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/inetcfg.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2021 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 <errno.h>
+#include <inet/eth_addr.h>
+#include <inet/inetcfg.h>
+#include <ipc/inet.h>
+#include <ipc/services.h>
+#include <loc.h>
+#include <stdlib.h>
+#include <str.h>
+
+static async_sess_t *inetcfg_sess = NULL;
+
+static errno_t inetcfg_get_ids_once(sysarg_t method, sysarg_t arg1,
+    sysarg_t *id_buf, size_t buf_size, size_t *act_size)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, method, arg1, &answer);
+	errno_t rc = async_data_read_start(exch, id_buf, buf_size);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		return retval;
+	}
+
+	*act_size = ipc_get_arg1(&answer);
+	return EOK;
+}
+
+/** Get list of IDs.
+ *
+ * Returns an allocated array of service IDs.
+ *
+ * @param method	IPC method
+ * @param arg1		IPC argument 1
+ * @param data		Place to store pointer to array of IDs
+ * @param count		Place to store number of IDs
+ * @return 		EOK on success or an error code
+ */
+static errno_t inetcfg_get_ids_internal(sysarg_t method, sysarg_t arg1,
+    sysarg_t **data, size_t *count)
+{
+	*data = NULL;
+	*count = 0;
+
+	size_t act_size = 0;
+	errno_t rc = inetcfg_get_ids_once(method, arg1, NULL, 0,
+	    &act_size);
+	if (rc != EOK)
+		return rc;
+
+	size_t alloc_size = act_size;
+	service_id_t *ids = malloc(alloc_size);
+	if (ids == NULL)
+		return ENOMEM;
+
+	while (true) {
+		rc = inetcfg_get_ids_once(method, arg1, ids, alloc_size,
+		    &act_size);
+		if (rc != EOK)
+			return rc;
+
+		if (act_size <= alloc_size)
+			break;
+
+		alloc_size = act_size;
+		service_id_t *tmp = realloc(ids, alloc_size);
+		if (tmp == NULL) {
+			free(ids);
+			return ENOMEM;
+		}
+		ids = tmp;
+	}
+
+	*count = act_size / sizeof(sysarg_t);
+	*data = ids;
+	return EOK;
+}
+
+errno_t inetcfg_init(void)
+{
+	service_id_t inet_svc;
+	errno_t rc;
+
+	assert(inetcfg_sess == NULL);
+
+	rc = loc_service_get_id(SERVICE_NAME_INET, &inet_svc,
+	    IPC_FLAG_BLOCKING);
+	if (rc != EOK)
+		return ENOENT;
+
+	inetcfg_sess = loc_service_connect(inet_svc, INTERFACE_INETCFG,
+	    IPC_FLAG_BLOCKING);
+	if (inetcfg_sess == NULL)
+		return ENOENT;
+
+	return EOK;
+}
+
+errno_t inetcfg_addr_create_static(const char *name, inet_naddr_t *naddr,
+    sysarg_t link_id, sysarg_t *addr_id)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, INETCFG_ADDR_CREATE_STATIC, link_id,
+	    &answer);
+
+	errno_t rc = async_data_write_start(exch, naddr, sizeof(inet_naddr_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_write_start(exch, name, str_size(name));
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	*addr_id = ipc_get_arg1(&answer);
+
+	return retval;
+}
+
+errno_t inetcfg_addr_delete(sysarg_t addr_id)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	errno_t rc = async_req_1_0(exch, INETCFG_ADDR_DELETE, addr_id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+errno_t inetcfg_addr_get(sysarg_t addr_id, inet_addr_info_t *ainfo)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, INETCFG_ADDR_GET, addr_id, &answer);
+
+	ipc_call_t answer_naddr;
+	aid_t req_naddr = async_data_read(exch, &ainfo->naddr,
+	    sizeof(inet_naddr_t), &answer_naddr);
+
+	errno_t retval_naddr;
+	async_wait_for(req_naddr, &retval_naddr);
+
+	if (retval_naddr != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return retval_naddr;
+	}
+
+	ipc_call_t answer_name;
+	char name_buf[LOC_NAME_MAXLEN + 1];
+	aid_t req_name = async_data_read(exch, name_buf, LOC_NAME_MAXLEN,
+	    &answer_name);
+
+	async_exchange_end(exch);
+
+	errno_t retval_name;
+	async_wait_for(req_name, &retval_name);
+
+	if (retval_name != EOK) {
+		async_forget(req);
+		return retval_name;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK)
+		return retval;
+
+	size_t act_size = ipc_get_arg2(&answer_name);
+	assert(act_size <= LOC_NAME_MAXLEN);
+
+	name_buf[act_size] = '\0';
+
+	ainfo->ilink = ipc_get_arg1(&answer);
+	ainfo->name = str_dup(name_buf);
+
+	return EOK;
+}
+
+errno_t inetcfg_addr_get_id(const char *name, sysarg_t link_id, sysarg_t *addr_id)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, INETCFG_ADDR_GET_ID, link_id, &answer);
+	errno_t retval = async_data_write_start(exch, name, str_size(name));
+
+	async_exchange_end(exch);
+
+	if (retval != EOK) {
+		async_forget(req);
+		return retval;
+	}
+
+	async_wait_for(req, &retval);
+	*addr_id = ipc_get_arg1(&answer);
+
+	return retval;
+}
+
+errno_t inetcfg_get_addr_list(sysarg_t **addrs, size_t *count)
+{
+	return inetcfg_get_ids_internal(INETCFG_GET_ADDR_LIST,
+	    0, addrs, count);
+}
+
+errno_t inetcfg_get_link_list(sysarg_t **links, size_t *count)
+{
+	return inetcfg_get_ids_internal(INETCFG_GET_LINK_LIST,
+	    0, links, count);
+}
+
+errno_t inetcfg_get_sroute_list(sysarg_t **sroutes, size_t *count)
+{
+	return inetcfg_get_ids_internal(INETCFG_GET_SROUTE_LIST,
+	    0, sroutes, count);
+}
+
+errno_t inetcfg_link_add(sysarg_t link_id)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	errno_t rc = async_req_1_0(exch, INETCFG_LINK_ADD, link_id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+errno_t inetcfg_link_get(sysarg_t link_id, inet_link_info_t *linfo)
+{
+	ipc_call_t dreply;
+	errno_t dretval;
+	size_t act_size;
+	char name_buf[LOC_NAME_MAXLEN + 1];
+
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	ipc_call_t answer;
+	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);
+	errno_t rc = async_data_read_start(exch, &linfo->mac_addr, sizeof(eth_addr_t));
+	async_wait_for(dreq, &dretval);
+
+	async_exchange_end(exch);
+
+	if (dretval != EOK || rc != EOK) {
+		async_forget(req);
+		return dretval;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK)
+		return retval;
+
+	act_size = ipc_get_arg2(&dreply);
+	assert(act_size <= LOC_NAME_MAXLEN);
+	name_buf[act_size] = '\0';
+
+	linfo->name = str_dup(name_buf);
+	linfo->def_mtu = ipc_get_arg1(&answer);
+
+	return EOK;
+}
+
+errno_t inetcfg_link_remove(sysarg_t link_id)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	errno_t rc = async_req_1_0(exch, INETCFG_LINK_REMOVE, link_id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+errno_t inetcfg_sroute_create(const char *name, inet_naddr_t *dest,
+    inet_addr_t *router, sysarg_t *sroute_id)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, INETCFG_SROUTE_CREATE, &answer);
+
+	errno_t rc = async_data_write_start(exch, dest, sizeof(inet_naddr_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_write_start(exch, router, sizeof(inet_addr_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_write_start(exch, name, str_size(name));
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	*sroute_id = ipc_get_arg1(&answer);
+
+	return retval;
+}
+
+errno_t inetcfg_sroute_delete(sysarg_t sroute_id)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	errno_t rc = async_req_1_0(exch, INETCFG_SROUTE_DELETE, sroute_id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+errno_t inetcfg_sroute_get(sysarg_t sroute_id, inet_sroute_info_t *srinfo)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, INETCFG_SROUTE_GET, sroute_id, &answer);
+
+	ipc_call_t answer_dest;
+	aid_t req_dest = async_data_read(exch, &srinfo->dest,
+	    sizeof(inet_naddr_t), &answer_dest);
+
+	errno_t retval_dest;
+	async_wait_for(req_dest, &retval_dest);
+
+	if (retval_dest != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return retval_dest;
+	}
+
+	ipc_call_t answer_router;
+	aid_t req_router = async_data_read(exch, &srinfo->router,
+	    sizeof(inet_addr_t), &answer_router);
+
+	errno_t retval_router;
+	async_wait_for(req_router, &retval_router);
+
+	if (retval_router != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return retval_router;
+	}
+
+	ipc_call_t answer_name;
+	char name_buf[LOC_NAME_MAXLEN + 1];
+	aid_t req_name = async_data_read(exch, name_buf, LOC_NAME_MAXLEN,
+	    &answer_name);
+
+	async_exchange_end(exch);
+
+	errno_t retval_name;
+	async_wait_for(req_name, &retval_name);
+
+	if (retval_name != EOK) {
+		async_forget(req);
+		return retval_name;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK)
+		return retval;
+
+	size_t act_size = ipc_get_arg2(&answer_name);
+	assert(act_size <= LOC_NAME_MAXLEN);
+
+	name_buf[act_size] = '\0';
+
+	srinfo->name = str_dup(name_buf);
+
+	return EOK;
+}
+
+errno_t inetcfg_sroute_get_id(const char *name, sysarg_t *sroute_id)
+{
+	async_exch_t *exch = async_exchange_begin(inetcfg_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, INETCFG_SROUTE_GET_ID, &answer);
+	errno_t retval = async_data_write_start(exch, name, str_size(name));
+
+	async_exchange_end(exch);
+
+	if (retval != EOK) {
+		async_forget(req);
+		return retval;
+	}
+
+	async_wait_for(req, &retval);
+	*sroute_id = ipc_get_arg1(&answer);
+
+	return retval;
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/inetping.c
===================================================================
--- uspace/lib/inet/src/inetping.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/inetping.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2013 Jiri Svoboda
+ * 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/inetping.h>
+#include <ipc/inet.h>
+#include <ipc/services.h>
+#include <loc.h>
+#include <stdlib.h>
+#include <str.h>
+
+static void inetping_cb_conn(ipc_call_t *, void *);
+static void inetping_ev_recv(ipc_call_t *);
+
+static async_sess_t *inetping_sess = NULL;
+static inetping_ev_ops_t *inetping_ev_ops;
+
+errno_t inetping_init(inetping_ev_ops_t *ev_ops)
+{
+	service_id_t inetping_svc;
+	errno_t rc;
+
+	assert(inetping_sess == NULL);
+
+	inetping_ev_ops = ev_ops;
+
+	rc = loc_service_get_id(SERVICE_NAME_INET, &inetping_svc,
+	    IPC_FLAG_BLOCKING);
+	if (rc != EOK)
+		return ENOENT;
+
+	inetping_sess = loc_service_connect(inetping_svc, INTERFACE_INETPING,
+	    IPC_FLAG_BLOCKING);
+	if (inetping_sess == NULL)
+		return ENOENT;
+
+	async_exch_t *exch = async_exchange_begin(inetping_sess);
+
+	port_id_t port;
+	rc = async_create_callback_port(exch, INTERFACE_INETPING_CB, 0, 0,
+	    inetping_cb_conn, NULL, &port);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_hangup(inetping_sess);
+		inetping_sess = NULL;
+		return rc;
+	}
+
+	return EOK;
+}
+
+errno_t inetping_send(inetping_sdu_t *sdu)
+{
+	async_exch_t *exch = async_exchange_begin(inetping_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, INETPING_SEND, sdu->seq_no, &answer);
+
+	errno_t 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;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+errno_t inetping_get_srcaddr(const inet_addr_t *remote, inet_addr_t *local)
+{
+	async_exch_t *exch = async_exchange_begin(inetping_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, INETPING_GET_SRCADDR, &answer);
+
+	errno_t 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);
+
+	errno_t retval_local;
+	async_wait_for(req_local, &retval_local);
+
+	if (retval_local != EOK) {
+		async_forget(req);
+		return retval_local;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+static void inetping_ev_recv(ipc_call_t *icall)
+{
+	inetping_sdu_t sdu;
+
+	sdu.seq_no = ipc_get_arg1(icall);
+
+	ipc_call_t call;
+	size_t size;
+	if (!async_data_write_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(sdu.src)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	errno_t rc = async_data_write_finalize(&call, &sdu.src, size);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	if (!async_data_write_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(sdu.dest)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = async_data_write_finalize(&call, &sdu.dest, size);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = async_data_write_accept(&sdu.data, false, 0, 0, 0, &sdu.size);
+	if (rc != EOK) {
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = inetping_ev_ops->recv(&sdu);
+	free(sdu.data);
+	async_answer_0(icall, rc);
+}
+
+static void inetping_cb_conn(ipc_call_t *icall, void *arg)
+{
+	while (true) {
+		ipc_call_t call;
+		async_get_call(&call);
+
+		if (!ipc_get_imethod(&call)) {
+			async_answer_0(&call, EOK);
+			return;
+		}
+
+		switch (ipc_get_imethod(&call)) {
+		case INETPING_EV_RECV:
+			inetping_ev_recv(&call);
+			break;
+		default:
+			async_answer_0(&call, ENOTSUP);
+		}
+	}
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/iplink.c
===================================================================
--- uspace/lib/inet/src/iplink.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/iplink.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2021 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 libinet
+ * @{
+ */
+/**
+ * @file
+ * @brief IP link client stub
+ */
+
+#include <async.h>
+#include <assert.h>
+#include <errno.h>
+#include <inet/addr.h>
+#include <inet/eth_addr.h>
+#include <inet/iplink.h>
+#include <ipc/iplink.h>
+#include <ipc/services.h>
+#include <loc.h>
+#include <stdlib.h>
+
+static void iplink_cb_conn(ipc_call_t *icall, void *arg);
+
+errno_t iplink_open(async_sess_t *sess, iplink_ev_ops_t *ev_ops, void *arg,
+    iplink_t **riplink)
+{
+	iplink_t *iplink = calloc(1, sizeof(iplink_t));
+	if (iplink == NULL)
+		return ENOMEM;
+
+	iplink->sess = sess;
+	iplink->ev_ops = ev_ops;
+	iplink->arg = arg;
+
+	async_exch_t *exch = async_exchange_begin(sess);
+
+	port_id_t port;
+	errno_t rc = async_create_callback_port(exch, INTERFACE_IPLINK_CB, 0, 0,
+	    iplink_cb_conn, iplink, &port);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		goto error;
+
+	*riplink = iplink;
+	return EOK;
+
+error:
+	if (iplink != NULL)
+		free(iplink);
+
+	return rc;
+}
+
+void iplink_close(iplink_t *iplink)
+{
+	/* XXX Synchronize with iplink_cb_conn */
+	free(iplink);
+}
+
+errno_t iplink_send(iplink_t *iplink, iplink_sdu_t *sdu)
+{
+	async_exch_t *exch = async_exchange_begin(iplink->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_2(exch, IPLINK_SEND, (sysarg_t) sdu->src,
+	    (sysarg_t) sdu->dest, &answer);
+
+	errno_t rc = async_data_write_start(exch, sdu->data, sdu->size);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+errno_t iplink_send6(iplink_t *iplink, iplink_sdu6_t *sdu)
+{
+	async_exch_t *exch = async_exchange_begin(iplink->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, IPLINK_SEND6, &answer);
+
+	errno_t rc = async_data_write_start(exch, &sdu->dest, sizeof(eth_addr_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;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+errno_t iplink_get_mtu(iplink_t *iplink, size_t *rmtu)
+{
+	async_exch_t *exch = async_exchange_begin(iplink->sess);
+
+	sysarg_t mtu;
+	errno_t rc = async_req_0_1(exch, IPLINK_GET_MTU, &mtu);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	*rmtu = mtu;
+	return EOK;
+}
+
+errno_t iplink_get_mac48(iplink_t *iplink, eth_addr_t *mac)
+{
+	async_exch_t *exch = async_exchange_begin(iplink->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, IPLINK_GET_MAC48, &answer);
+
+	errno_t rc = async_data_read_start(exch, mac, sizeof(eth_addr_t));
+
+	loc_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+errno_t iplink_set_mac48(iplink_t *iplink, eth_addr_t *mac)
+{
+	async_exch_t *exch = async_exchange_begin(iplink->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, IPLINK_GET_MAC48, &answer);
+
+	errno_t rc = async_data_read_start(exch, mac, sizeof(eth_addr_t));
+
+	loc_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+errno_t iplink_addr_add(iplink_t *iplink, inet_addr_t *addr)
+{
+	async_exch_t *exch = async_exchange_begin(iplink->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, IPLINK_ADDR_ADD, &answer);
+
+	errno_t rc = async_data_write_start(exch, addr, sizeof(inet_addr_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+errno_t iplink_addr_remove(iplink_t *iplink, inet_addr_t *addr)
+{
+	async_exch_t *exch = async_exchange_begin(iplink->sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, IPLINK_ADDR_REMOVE, &answer);
+
+	errno_t rc = async_data_write_start(exch, addr, sizeof(inet_addr_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+void *iplink_get_userptr(iplink_t *iplink)
+{
+	return iplink->arg;
+}
+
+static void iplink_ev_recv(iplink_t *iplink, ipc_call_t *icall)
+{
+	iplink_recv_sdu_t sdu;
+
+	ip_ver_t ver = ipc_get_arg1(icall);
+
+	errno_t rc = async_data_write_accept(&sdu.data, false, 0, 0, 0,
+	    &sdu.size);
+	if (rc != EOK) {
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = iplink->ev_ops->recv(iplink, &sdu, ver);
+	free(sdu.data);
+	async_answer_0(icall, rc);
+}
+
+static void iplink_ev_change_addr(iplink_t *iplink, ipc_call_t *icall)
+{
+	eth_addr_t *addr;
+	size_t size;
+
+	errno_t rc = async_data_write_accept((void **) &addr, false,
+	    sizeof(eth_addr_t), sizeof(eth_addr_t), 0, &size);
+	if (rc != EOK) {
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = iplink->ev_ops->change_addr(iplink, addr);
+	free(addr);
+	async_answer_0(icall, EOK);
+}
+
+static void iplink_cb_conn(ipc_call_t *icall, void *arg)
+{
+	iplink_t *iplink = (iplink_t *) arg;
+
+	while (true) {
+		ipc_call_t call;
+		async_get_call(&call);
+
+		if (!ipc_get_imethod(&call)) {
+			async_answer_0(&call, EOK);
+			return;
+		}
+
+		switch (ipc_get_imethod(&call)) {
+		case IPLINK_EV_RECV:
+			iplink_ev_recv(iplink, &call);
+			break;
+		case IPLINK_EV_CHANGE_ADDR:
+			iplink_ev_change_addr(iplink, &call);
+			break;
+		default:
+			async_answer_0(&call, ENOTSUP);
+		}
+	}
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/iplink_srv.c
===================================================================
--- uspace/lib/inet/src/iplink_srv.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/iplink_srv.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2021 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 libinet
+ * @{
+ */
+/**
+ * @file
+ * @brief IP link server stub
+ */
+
+#include <errno.h>
+#include <inet/eth_addr.h>
+#include <ipc/iplink.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <inet/addr.h>
+#include <inet/iplink_srv.h>
+
+static void iplink_get_mtu_srv(iplink_srv_t *srv, ipc_call_t *call)
+{
+	size_t mtu;
+	errno_t rc = srv->ops->get_mtu(srv, &mtu);
+	async_answer_1(call, rc, mtu);
+}
+
+static void iplink_get_mac48_srv(iplink_srv_t *srv, ipc_call_t *icall)
+{
+	eth_addr_t mac;
+	errno_t rc = srv->ops->get_mac48(srv, &mac);
+	if (rc != EOK) {
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	ipc_call_t call;
+	size_t size;
+	if (!async_data_read_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(eth_addr_t)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	rc = async_data_read_finalize(&call, &mac, size);
+	if (rc != EOK)
+		async_answer_0(&call, rc);
+
+	async_answer_0(icall, rc);
+}
+
+static void iplink_set_mac48_srv(iplink_srv_t *srv, ipc_call_t *icall)
+{
+	errno_t rc;
+	size_t size;
+	eth_addr_t mac;
+
+	ipc_call_t call;
+	if (!async_data_write_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+	}
+
+	rc = srv->ops->set_mac48(srv, &mac);
+	if (rc != EOK) {
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = async_data_read_finalize(&call, &mac, sizeof(eth_addr_t));
+	if (rc != EOK)
+		async_answer_0(&call, rc);
+
+	async_answer_0(icall, rc);
+}
+
+static void iplink_addr_add_srv(iplink_srv_t *srv, ipc_call_t *icall)
+{
+	ipc_call_t call;
+	size_t size;
+	if (!async_data_write_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(inet_addr_t)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	inet_addr_t addr;
+	errno_t rc = async_data_write_finalize(&call, &addr, size);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+	}
+
+	rc = srv->ops->addr_add(srv, &addr);
+	async_answer_0(icall, rc);
+}
+
+static void iplink_addr_remove_srv(iplink_srv_t *srv, ipc_call_t *icall)
+{
+	ipc_call_t call;
+	size_t size;
+	if (!async_data_write_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(inet_addr_t)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	inet_addr_t addr;
+	errno_t rc = async_data_write_finalize(&call, &addr, size);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+	}
+
+	rc = srv->ops->addr_remove(srv, &addr);
+	async_answer_0(icall, rc);
+}
+
+static void iplink_send_srv(iplink_srv_t *srv, ipc_call_t *icall)
+{
+	iplink_sdu_t sdu;
+
+	sdu.src = ipc_get_arg1(icall);
+	sdu.dest = ipc_get_arg2(icall);
+
+	errno_t rc = async_data_write_accept(&sdu.data, false, 0, 0, 0,
+	    &sdu.size);
+	if (rc != EOK) {
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = srv->ops->send(srv, &sdu);
+	free(sdu.data);
+	async_answer_0(icall, rc);
+}
+
+static void iplink_send6_srv(iplink_srv_t *srv, ipc_call_t *icall)
+{
+	iplink_sdu6_t sdu;
+
+	ipc_call_t call;
+	size_t size;
+	if (!async_data_write_receive(&call, &size)) {
+		async_answer_0(&call, EREFUSED);
+		async_answer_0(icall, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(eth_addr_t)) {
+		async_answer_0(&call, EINVAL);
+		async_answer_0(icall, EINVAL);
+		return;
+	}
+
+	errno_t rc = async_data_write_finalize(&call, &sdu.dest, size);
+	if (rc != EOK) {
+		async_answer_0(&call, rc);
+		async_answer_0(icall, rc);
+	}
+
+	rc = async_data_write_accept(&sdu.data, false, 0, 0, 0,
+	    &sdu.size);
+	if (rc != EOK) {
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	rc = srv->ops->send6(srv, &sdu);
+	free(sdu.data);
+	async_answer_0(icall, rc);
+}
+
+void iplink_srv_init(iplink_srv_t *srv)
+{
+	fibril_mutex_initialize(&srv->lock);
+	srv->connected = false;
+	srv->ops = NULL;
+	srv->arg = NULL;
+	srv->client_sess = NULL;
+}
+
+errno_t iplink_conn(ipc_call_t *icall, void *arg)
+{
+	iplink_srv_t *srv = (iplink_srv_t *) arg;
+	errno_t rc;
+
+	fibril_mutex_lock(&srv->lock);
+	if (srv->connected) {
+		fibril_mutex_unlock(&srv->lock);
+		async_answer_0(icall, EBUSY);
+		return EBUSY;
+	}
+
+	srv->connected = true;
+	fibril_mutex_unlock(&srv->lock);
+
+	/* Accept the connection */
+	async_accept_0(icall);
+
+	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (sess == NULL)
+		return ENOMEM;
+
+	srv->client_sess = sess;
+
+	rc = srv->ops->open(srv);
+	if (rc != EOK)
+		return rc;
+
+	while (true) {
+		ipc_call_t call;
+		async_get_call(&call);
+		sysarg_t method = ipc_get_imethod(&call);
+
+		if (!method) {
+			/* The other side has hung up */
+			fibril_mutex_lock(&srv->lock);
+			srv->connected = false;
+			fibril_mutex_unlock(&srv->lock);
+			async_answer_0(&call, EOK);
+			break;
+		}
+
+		switch (method) {
+		case IPLINK_GET_MTU:
+			iplink_get_mtu_srv(srv, &call);
+			break;
+		case IPLINK_GET_MAC48:
+			iplink_get_mac48_srv(srv, &call);
+			break;
+		case IPLINK_SET_MAC48:
+			iplink_set_mac48_srv(srv, &call);
+			break;
+		case IPLINK_SEND:
+			iplink_send_srv(srv, &call);
+			break;
+		case IPLINK_SEND6:
+			iplink_send6_srv(srv, &call);
+			break;
+		case IPLINK_ADDR_ADD:
+			iplink_addr_add_srv(srv, &call);
+			break;
+		case IPLINK_ADDR_REMOVE:
+			iplink_addr_remove_srv(srv, &call);
+			break;
+		default:
+			async_answer_0(&call, EINVAL);
+		}
+	}
+
+	return srv->ops->close(srv);
+}
+
+/* XXX Version should be part of @a sdu */
+errno_t iplink_ev_recv(iplink_srv_t *srv, iplink_recv_sdu_t *sdu, ip_ver_t ver)
+{
+	if (srv->client_sess == NULL)
+		return EIO;
+
+	async_exch_t *exch = async_exchange_begin(srv->client_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, IPLINK_EV_RECV, (sysarg_t)ver,
+	    &answer);
+
+	errno_t rc = async_data_write_start(exch, sdu->data, sdu->size);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+errno_t iplink_ev_change_addr(iplink_srv_t *srv, eth_addr_t *addr)
+{
+	if (srv->client_sess == NULL)
+		return EIO;
+
+	async_exch_t *exch = async_exchange_begin(srv->client_sess);
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, IPLINK_EV_CHANGE_ADDR, &answer);
+
+	errno_t rc = async_data_write_start(exch, addr, sizeof(eth_addr_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return retval;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/tcp.c
===================================================================
--- uspace/lib/inet/src/tcp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/tcp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,875 @@
+/*
+ * Copyright (c) 2015 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 libinet
+ * @{
+ */
+/** @file TCP API
+ */
+
+#include <errno.h>
+#include <fibril.h>
+#include <inet/endpoint.h>
+#include <inet/tcp.h>
+#include <ipc/services.h>
+#include <ipc/tcp.h>
+#include <stdlib.h>
+
+static void tcp_cb_conn(ipc_call_t *, void *);
+static errno_t tcp_conn_fibril(void *);
+
+/** Incoming TCP connection info
+ *
+ * Used to pass information about incoming TCP connection to the connection
+ * fibril
+ */
+typedef struct {
+	/** Listener who received the connection */
+	tcp_listener_t *lst;
+	/** Incoming connection */
+	tcp_conn_t *conn;
+} tcp_in_conn_t;
+
+/** Create callback connection from TCP service.
+ *
+ * @param tcp TCP service
+ * @return EOK on success or an error code
+ */
+static errno_t tcp_callback_create(tcp_t *tcp)
+{
+	async_exch_t *exch = async_exchange_begin(tcp->sess);
+
+	aid_t req = async_send_0(exch, TCP_CALLBACK_CREATE, NULL);
+
+	port_id_t port;
+	errno_t rc = async_create_callback_port(exch, INTERFACE_TCP_CB, 0, 0,
+	    tcp_cb_conn, tcp, &port);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+/** Create TCP client instance.
+ *
+ * @param  rtcp Place to store pointer to new TCP client
+ * @return EOK on success, ENOMEM if out of memory, EIO if service
+ *         cannot be contacted
+ */
+errno_t tcp_create(tcp_t **rtcp)
+{
+	tcp_t *tcp;
+	service_id_t tcp_svcid;
+	errno_t rc;
+
+	tcp = calloc(1, sizeof(tcp_t));
+	if (tcp == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	list_initialize(&tcp->conn);
+	list_initialize(&tcp->listener);
+	fibril_mutex_initialize(&tcp->lock);
+	fibril_condvar_initialize(&tcp->cv);
+
+	rc = loc_service_get_id(SERVICE_NAME_TCP, &tcp_svcid,
+	    IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
+	tcp->sess = loc_service_connect(tcp_svcid, INTERFACE_TCP,
+	    IPC_FLAG_BLOCKING);
+	if (tcp->sess == NULL) {
+		rc = EIO;
+		goto error;
+	}
+
+	rc = tcp_callback_create(tcp);
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
+	*rtcp = tcp;
+	return EOK;
+error:
+	free(tcp);
+	return rc;
+}
+
+/** Destroy TCP client instance.
+ *
+ * @param tcp TCP client
+ */
+void tcp_destroy(tcp_t *tcp)
+{
+	if (tcp == NULL)
+		return;
+
+	async_hangup(tcp->sess);
+
+	fibril_mutex_lock(&tcp->lock);
+	while (!tcp->cb_done)
+		fibril_condvar_wait(&tcp->cv, &tcp->lock);
+	fibril_mutex_unlock(&tcp->lock);
+
+	free(tcp);
+}
+
+/** Create new TCP connection
+ *
+ * @param tcp   TCP client instance
+ * @param id    Connection ID
+ * @param cb    Callbacks
+ * @param arg   Callback argument
+ * @param rconn Place to store pointer to new connection
+ *
+ * @return EOK on success, ENOMEM if out of memory
+ */
+static errno_t tcp_conn_new(tcp_t *tcp, sysarg_t id, tcp_cb_t *cb, void *arg,
+    tcp_conn_t **rconn)
+{
+	tcp_conn_t *conn;
+
+	conn = calloc(1, sizeof(tcp_conn_t));
+	if (conn == NULL)
+		return ENOMEM;
+
+	conn->data_avail = false;
+	fibril_mutex_initialize(&conn->lock);
+	fibril_condvar_initialize(&conn->cv);
+
+	conn->tcp = tcp;
+	conn->id = id;
+	conn->cb = cb;
+	conn->cb_arg = arg;
+
+	list_append(&conn->ltcp, &tcp->conn);
+	*rconn = conn;
+
+	return EOK;
+}
+
+/** Create new TCP connection.
+ *
+ * Open a connection to the specified destination. This function returns
+ * even before the connection is established (or not). When the connection
+ * is established, @a cb->connected is called. If the connection fails,
+ * @a cb->conn_failed is called. Alternatively, the caller can call
+ * @c tcp_conn_wait_connected() to wait for connection to complete or fail.
+ * Other callbacks are available to monitor the changes in connection state.
+ *
+ * @a epp must specify the remote address and port. Both local address and
+ * port are optional. If local address is not specified, address selection
+ * will take place. If local port number is not specified, a suitable
+ * free dynamic port number will be allocated.
+ *
+ * @param tcp   TCP client
+ * @param epp   Internet endpoint pair
+ * @param cb    Callbacks
+ * @param arg   Argument to callbacks
+ * @param rconn Place to store pointer to new connection
+ *
+ * @return EOK on success or an error code.
+ */
+errno_t tcp_conn_create(tcp_t *tcp, inet_ep2_t *epp, tcp_cb_t *cb, void *arg,
+    tcp_conn_t **rconn)
+{
+	async_exch_t *exch;
+	ipc_call_t answer;
+	sysarg_t conn_id;
+
+	exch = async_exchange_begin(tcp->sess);
+	aid_t req = async_send_0(exch, TCP_CONN_CREATE, &answer);
+	errno_t rc = async_data_write_start(exch, (void *)epp,
+	    sizeof(inet_ep2_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		errno_t rc_orig;
+		async_wait_for(req, &rc_orig);
+		if (rc_orig != EOK)
+			rc = rc_orig;
+		goto error;
+	}
+
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		goto error;
+
+	conn_id = ipc_get_arg1(&answer);
+
+	rc = tcp_conn_new(tcp, conn_id, cb, arg, rconn);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+error:
+	return (errno_t) rc;
+}
+
+/** Destroy TCP connection.
+ *
+ * Destroy TCP connection. The caller should destroy all connections
+ * he created before destroying the TCP client and before terminating.
+ *
+ * @param conn TCP connection
+ */
+void tcp_conn_destroy(tcp_conn_t *conn)
+{
+	async_exch_t *exch;
+
+	if (conn == NULL)
+		return;
+
+	list_remove(&conn->ltcp);
+
+	exch = async_exchange_begin(conn->tcp->sess);
+	errno_t rc = async_req_1_0(exch, TCP_CONN_DESTROY, conn->id);
+	async_exchange_end(exch);
+
+	free(conn);
+	(void) rc;
+}
+
+/** Get connection based on its ID.
+ *
+ * @param tcp   TCP client
+ * @param id    Connection ID
+ * @param rconn Place to store pointer to connection
+ *
+ * @return EOK on success, EINVAL if no connection with the given ID exists
+ */
+static errno_t tcp_conn_get(tcp_t *tcp, sysarg_t id, tcp_conn_t **rconn)
+{
+	list_foreach(tcp->conn, ltcp, tcp_conn_t, conn) {
+		if (conn->id == id) {
+			*rconn = conn;
+			return EOK;
+		}
+	}
+
+	return EINVAL;
+}
+
+/** Get the user/callback argument for a connection.
+ *
+ * @param conn TCP connection
+ * @return User argument associated with connection
+ */
+void *tcp_conn_userptr(tcp_conn_t *conn)
+{
+	return conn->cb_arg;
+}
+
+/** Create a TCP connection listener.
+ *
+ * A listener listens for connections on the set of endpoints specified
+ * by @a ep. Each time a new incoming connection is established,
+ * @a lcb->new_conn is called (and passed @a larg). Also, the new connection
+ * will have callbacks set to @a cb and argument to @a arg.
+ *
+ * @a ep must specify a valid port number. @a ep may specify an address
+ * or link to listen on. If it does not, the listener will listen on
+ * all links/addresses.
+ *
+ * @param tcp  TCP client
+ * @param ep   Internet endpoint
+ * @param lcb  Listener callbacks
+ * @param larg Listener callback argument
+ * @param cb   Connection callbacks for every new connection
+ * @param arg  Connection argument for every new connection
+ * @param rlst Place to store pointer to new listener
+ *
+ * @return EOK on success or an error code
+ */
+errno_t tcp_listener_create(tcp_t *tcp, inet_ep_t *ep, tcp_listen_cb_t *lcb,
+    void *larg, tcp_cb_t *cb, void *arg, tcp_listener_t **rlst)
+{
+	async_exch_t *exch;
+	tcp_listener_t *lst;
+	ipc_call_t answer;
+
+	lst = calloc(1, sizeof(tcp_listener_t));
+	if (lst == NULL)
+		return ENOMEM;
+
+	exch = async_exchange_begin(tcp->sess);
+	aid_t req = async_send_0(exch, TCP_LISTENER_CREATE, &answer);
+	errno_t rc = async_data_write_start(exch, (void *)ep,
+	    sizeof(inet_ep_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		errno_t rc_orig;
+		async_wait_for(req, &rc_orig);
+		if (rc_orig != EOK)
+			rc = rc_orig;
+		goto error;
+	}
+
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		goto error;
+
+	lst->tcp = tcp;
+	lst->id = ipc_get_arg1(&answer);
+	lst->lcb = lcb;
+	lst->lcb_arg = larg;
+	lst->cb = cb;
+	lst->cb_arg = arg;
+
+	list_append(&lst->ltcp, &tcp->listener);
+	*rlst = lst;
+
+	return EOK;
+error:
+	free(lst);
+	return (errno_t) rc;
+}
+
+/** Destroy TCP connection listener.
+ *
+ * @param lst Listener
+ */
+void tcp_listener_destroy(tcp_listener_t *lst)
+{
+	async_exch_t *exch;
+
+	if (lst == NULL)
+		return;
+
+	list_remove(&lst->ltcp);
+
+	exch = async_exchange_begin(lst->tcp->sess);
+	errno_t rc = async_req_1_0(exch, TCP_LISTENER_DESTROY, lst->id);
+	async_exchange_end(exch);
+
+	free(lst);
+	(void) rc;
+}
+
+/** Get TCP connection listener based on its ID.
+ *
+ * @param tcp TCP client
+ * @param id  Listener ID
+ * @param rlst Place to store pointer to listener
+ *
+ * @return EOK on success, EINVAL if no listener with the given ID is found
+ */
+static errno_t tcp_listener_get(tcp_t *tcp, sysarg_t id, tcp_listener_t **rlst)
+{
+	list_foreach(tcp->listener, ltcp, tcp_listener_t, lst) {
+		if (lst->id == id) {
+			*rlst = lst;
+			return EOK;
+		}
+	}
+
+	return EINVAL;
+}
+
+/** Get callback/user argument associated with listener.
+ *
+ * @param lst Listener
+ * @return Callback/user argument
+ */
+void *tcp_listener_userptr(tcp_listener_t *lst)
+{
+	return lst->lcb_arg;
+}
+
+/** Wait until connection is either established or connection fails.
+ *
+ * Can be called after calling tcp_conn_create() to block until connection
+ * either completes or fails. If the connection fails, EIO is returned.
+ * In this case the connection still exists, but is in a failed
+ * state.
+ *
+ * @param conn Connection
+ * @return EOK if connection is established, EIO otherwise
+ */
+errno_t tcp_conn_wait_connected(tcp_conn_t *conn)
+{
+	fibril_mutex_lock(&conn->lock);
+	while (!conn->connected && !conn->conn_failed && !conn->conn_reset)
+		fibril_condvar_wait(&conn->cv, &conn->lock);
+
+	if (conn->connected) {
+		fibril_mutex_unlock(&conn->lock);
+		return EOK;
+	} else {
+		assert(conn->conn_failed || conn->conn_reset);
+		fibril_mutex_unlock(&conn->lock);
+		return EIO;
+	}
+}
+
+/** Send data over TCP connection.
+ *
+ * @param conn  Connection
+ * @param data  Data
+ * @param bytes Data size in bytes
+ *
+ * @return EOK on success or an error code
+ */
+errno_t tcp_conn_send(tcp_conn_t *conn, const void *data, size_t bytes)
+{
+	async_exch_t *exch;
+	errno_t rc;
+
+	exch = async_exchange_begin(conn->tcp->sess);
+	aid_t req = async_send_1(exch, TCP_CONN_SEND, conn->id, NULL);
+	rc = async_data_write_start(exch, data, bytes);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	async_wait_for(req, &rc);
+	return rc;
+}
+
+/** Send FIN.
+ *
+ * Send FIN, indicating no more data will be send over the connection.
+ *
+ * @param conn Connection
+ * @return EOK on success or an error code
+ */
+errno_t tcp_conn_send_fin(tcp_conn_t *conn)
+{
+	async_exch_t *exch;
+
+	exch = async_exchange_begin(conn->tcp->sess);
+	errno_t rc = async_req_1_0(exch, TCP_CONN_SEND_FIN, conn->id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** Push connection.
+ *
+ * @param conn Connection
+ * @return EOK on success or an error code
+ */
+errno_t tcp_conn_push(tcp_conn_t *conn)
+{
+	async_exch_t *exch;
+
+	exch = async_exchange_begin(conn->tcp->sess);
+	errno_t rc = async_req_1_0(exch, TCP_CONN_PUSH, conn->id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** Reset connection.
+ *
+ * @param conn Connection
+ * @return EOK on success or an error code
+ */
+errno_t tcp_conn_reset(tcp_conn_t *conn)
+{
+	async_exch_t *exch;
+
+	exch = async_exchange_begin(conn->tcp->sess);
+	errno_t rc = async_req_1_0(exch, TCP_CONN_RESET, conn->id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** Read received data from connection without blocking.
+ *
+ * If any received data is pending on the connection, up to @a bsize bytes
+ * are copied to @a buf and the acutal number is stored in @a *nrecv.
+ * The entire buffer of @a bsize bytes is filled except when less data
+ * is currently available or FIN is received. EOK is returned.
+ *
+ * If no received data is pending, returns EAGAIN.
+ *
+ * @param conn Connection
+ * @param buf  Buffer
+ * @param bsize Buffer size
+ * @param nrecv Place to store actual number of received bytes
+ *
+ * @return EOK on success, EAGAIN if no received data is pending, or other
+ *         error code in case of other error
+ */
+errno_t tcp_conn_recv(tcp_conn_t *conn, void *buf, size_t bsize, size_t *nrecv)
+{
+	async_exch_t *exch;
+	ipc_call_t answer;
+
+	fibril_mutex_lock(&conn->lock);
+	if (!conn->data_avail) {
+		fibril_mutex_unlock(&conn->lock);
+		return EAGAIN;
+	}
+
+	exch = async_exchange_begin(conn->tcp->sess);
+	aid_t req = async_send_1(exch, TCP_CONN_RECV, conn->id, &answer);
+	errno_t rc = async_data_read_start(exch, buf, bsize);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		fibril_mutex_unlock(&conn->lock);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK) {
+		fibril_mutex_unlock(&conn->lock);
+		return retval;
+	}
+
+	*nrecv = ipc_get_arg1(&answer);
+	fibril_mutex_unlock(&conn->lock);
+	return EOK;
+}
+
+/** Read received data from connection with blocking.
+ *
+ * Wait for @a bsize bytes of data to be received and copy them to
+ * @a buf. Less data may be returned if FIN is received on the connection.
+ * The actual If any received data is written to @a *nrecv and EOK
+ * is returned on success.
+ *
+ * @param conn Connection
+ * @param buf  Buffer
+ * @param bsize Buffer size
+ * @param nrecv Place to store actual number of received bytes
+ *
+ * @return EOK on success or an error code
+ */
+errno_t tcp_conn_recv_wait(tcp_conn_t *conn, void *buf, size_t bsize,
+    size_t *nrecv)
+{
+	async_exch_t *exch;
+	ipc_call_t answer;
+
+again:
+	fibril_mutex_lock(&conn->lock);
+	while (!conn->data_avail) {
+		fibril_condvar_wait(&conn->cv, &conn->lock);
+	}
+
+	exch = async_exchange_begin(conn->tcp->sess);
+	aid_t req = async_send_1(exch, TCP_CONN_RECV_WAIT, conn->id, &answer);
+	errno_t rc = async_data_read_start(exch, buf, bsize);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		if (rc == EAGAIN) {
+			conn->data_avail = false;
+			fibril_mutex_unlock(&conn->lock);
+			goto again;
+		}
+		fibril_mutex_unlock(&conn->lock);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK) {
+		if (rc == EAGAIN) {
+			conn->data_avail = false;
+		}
+		fibril_mutex_unlock(&conn->lock);
+		return retval;
+	}
+
+	*nrecv = ipc_get_arg1(&answer);
+	fibril_mutex_unlock(&conn->lock);
+	return EOK;
+}
+
+/** Connection established event.
+ *
+ * @param tcp   TCP client
+ * @param icall Call data
+ *
+ */
+static void tcp_ev_connected(tcp_t *tcp, ipc_call_t *icall)
+{
+	tcp_conn_t *conn;
+	sysarg_t conn_id;
+	errno_t rc;
+
+	conn_id = ipc_get_arg1(icall);
+
+	rc = tcp_conn_get(tcp, conn_id, &conn);
+	if (rc != EOK) {
+		async_answer_0(icall, ENOENT);
+		return;
+	}
+
+	fibril_mutex_lock(&conn->lock);
+	conn->connected = true;
+	fibril_condvar_broadcast(&conn->cv);
+	fibril_mutex_unlock(&conn->lock);
+
+	async_answer_0(icall, EOK);
+}
+
+/** Connection failed event.
+ *
+ * @param tcp   TCP client
+ * @param icall Call data
+ *
+ */
+static void tcp_ev_conn_failed(tcp_t *tcp, ipc_call_t *icall)
+{
+	tcp_conn_t *conn;
+	sysarg_t conn_id;
+	errno_t rc;
+
+	conn_id = ipc_get_arg1(icall);
+
+	rc = tcp_conn_get(tcp, conn_id, &conn);
+	if (rc != EOK) {
+		async_answer_0(icall, ENOENT);
+		return;
+	}
+
+	fibril_mutex_lock(&conn->lock);
+	conn->conn_failed = true;
+	fibril_condvar_broadcast(&conn->cv);
+	fibril_mutex_unlock(&conn->lock);
+
+	async_answer_0(icall, EOK);
+}
+
+/** Connection reset event.
+ *
+ * @param tcp   TCP client
+ * @param icall Call data
+ *
+ */
+static void tcp_ev_conn_reset(tcp_t *tcp, ipc_call_t *icall)
+{
+	tcp_conn_t *conn;
+	sysarg_t conn_id;
+	errno_t rc;
+
+	conn_id = ipc_get_arg1(icall);
+
+	rc = tcp_conn_get(tcp, conn_id, &conn);
+	if (rc != EOK) {
+		async_answer_0(icall, ENOENT);
+		return;
+	}
+
+	fibril_mutex_lock(&conn->lock);
+	conn->conn_reset = true;
+	fibril_condvar_broadcast(&conn->cv);
+	fibril_mutex_unlock(&conn->lock);
+
+	async_answer_0(icall, EOK);
+}
+
+/** Data available event.
+ *
+ * @param tcp   TCP client
+ * @param icall Call data
+ *
+ */
+static void tcp_ev_data(tcp_t *tcp, ipc_call_t *icall)
+{
+	tcp_conn_t *conn;
+	sysarg_t conn_id;
+	errno_t rc;
+
+	conn_id = ipc_get_arg1(icall);
+
+	rc = tcp_conn_get(tcp, conn_id, &conn);
+	if (rc != EOK) {
+		async_answer_0(icall, ENOENT);
+		return;
+	}
+
+	conn->data_avail = true;
+	fibril_condvar_broadcast(&conn->cv);
+
+	if (conn->cb != NULL && conn->cb->data_avail != NULL)
+		conn->cb->data_avail(conn);
+
+	async_answer_0(icall, EOK);
+}
+
+/** Urgent data event.
+ *
+ * @param tcp   TCP client
+ * @param icall Call data
+ *
+ */
+static void tcp_ev_urg_data(tcp_t *tcp, ipc_call_t *icall)
+{
+	async_answer_0(icall, ENOTSUP);
+}
+
+/** New connection event.
+ *
+ * @param tcp   TCP client
+ * @param icall Call data
+ *
+ */
+static void tcp_ev_new_conn(tcp_t *tcp, ipc_call_t *icall)
+{
+	tcp_listener_t *lst;
+	tcp_conn_t *conn;
+	sysarg_t lst_id;
+	sysarg_t conn_id;
+	fid_t fid;
+	tcp_in_conn_t *cinfo;
+	errno_t rc;
+
+	lst_id = ipc_get_arg1(icall);
+	conn_id = ipc_get_arg2(icall);
+
+	rc = tcp_listener_get(tcp, lst_id, &lst);
+	if (rc != EOK) {
+		async_answer_0(icall, ENOENT);
+		return;
+	}
+
+	rc = tcp_conn_new(tcp, conn_id, lst->cb, lst->cb_arg, &conn);
+	if (rc != EOK) {
+		async_answer_0(icall, ENOMEM);
+		return;
+	}
+
+	if (lst->lcb != NULL && lst->lcb->new_conn != NULL) {
+		cinfo = calloc(1, sizeof(tcp_in_conn_t));
+		if (cinfo == NULL) {
+			async_answer_0(icall, ENOMEM);
+			return;
+		}
+
+		cinfo->lst = lst;
+		cinfo->conn = conn;
+
+		fid = fibril_create(tcp_conn_fibril, cinfo);
+		if (fid == 0) {
+			async_answer_0(icall, ENOMEM);
+		}
+
+		fibril_add_ready(fid);
+	}
+
+	async_answer_0(icall, EOK);
+}
+
+/** Callback connection handler.
+ *
+ * @param icall Connect call data
+ * @param arg   Argument, TCP client
+ *
+ */
+static void tcp_cb_conn(ipc_call_t *icall, void *arg)
+{
+	tcp_t *tcp = (tcp_t *)arg;
+
+	while (true) {
+		ipc_call_t call;
+		async_get_call(&call);
+
+		if (!ipc_get_imethod(&call)) {
+			/* Hangup */
+			async_answer_0(&call, EOK);
+			goto out;
+		}
+
+		switch (ipc_get_imethod(&call)) {
+		case TCP_EV_CONNECTED:
+			tcp_ev_connected(tcp, &call);
+			break;
+		case TCP_EV_CONN_FAILED:
+			tcp_ev_conn_failed(tcp, &call);
+			break;
+		case TCP_EV_CONN_RESET:
+			tcp_ev_conn_reset(tcp, &call);
+			break;
+		case TCP_EV_DATA:
+			tcp_ev_data(tcp, &call);
+			break;
+		case TCP_EV_URG_DATA:
+			tcp_ev_urg_data(tcp, &call);
+			break;
+		case TCP_EV_NEW_CONN:
+			tcp_ev_new_conn(tcp, &call);
+			break;
+		default:
+			async_answer_0(&call, ENOTSUP);
+			break;
+		}
+	}
+
+out:
+	fibril_mutex_lock(&tcp->lock);
+	tcp->cb_done = true;
+	fibril_mutex_unlock(&tcp->lock);
+	fibril_condvar_broadcast(&tcp->cv);
+}
+
+/** Fibril for handling incoming TCP connection in background.
+ *
+ * @param arg Argument, incoming connection information (@c tcp_in_conn_t)
+ */
+static errno_t tcp_conn_fibril(void *arg)
+{
+	tcp_in_conn_t *cinfo = (tcp_in_conn_t *)arg;
+
+	cinfo->lst->lcb->new_conn(cinfo->lst, cinfo->conn);
+	tcp_conn_destroy(cinfo->conn);
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/inet/src/udp.c
===================================================================
--- uspace/lib/inet/src/udp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/src/udp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,524 @@
+/*
+ * Copyright (c) 2015 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 libinet
+ * @{
+ */
+/** @file UDP API
+ */
+
+#include <errno.h>
+#include <inet/endpoint.h>
+#include <inet/udp.h>
+#include <ipc/services.h>
+#include <ipc/udp.h>
+#include <loc.h>
+#include <stdlib.h>
+
+static void udp_cb_conn(ipc_call_t *, void *);
+
+/** Create callback connection from UDP service.
+ *
+ * @param udp UDP service
+ * @return EOK on success or an error code
+ */
+static errno_t udp_callback_create(udp_t *udp)
+{
+	async_exch_t *exch = async_exchange_begin(udp->sess);
+
+	aid_t req = async_send_0(exch, UDP_CALLBACK_CREATE, NULL);
+
+	port_id_t port;
+	errno_t rc = async_create_callback_port(exch, INTERFACE_UDP_CB, 0, 0,
+	    udp_cb_conn, udp, &port);
+
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+/** Create UDP client instance.
+ *
+ * @param  rudp Place to store pointer to new UDP client
+ * @return EOK on success, ENOMEM if out of memory, EIO if service
+ *         cannot be contacted
+ */
+errno_t udp_create(udp_t **rudp)
+{
+	udp_t *udp;
+	service_id_t udp_svcid;
+	errno_t rc;
+
+	udp = calloc(1, sizeof(udp_t));
+	if (udp == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	list_initialize(&udp->assoc);
+	fibril_mutex_initialize(&udp->lock);
+	fibril_condvar_initialize(&udp->cv);
+
+	rc = loc_service_get_id(SERVICE_NAME_UDP, &udp_svcid,
+	    IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
+	udp->sess = loc_service_connect(udp_svcid, INTERFACE_UDP,
+	    IPC_FLAG_BLOCKING);
+	if (udp->sess == NULL) {
+		rc = EIO;
+		goto error;
+	}
+
+	rc = udp_callback_create(udp);
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
+	*rudp = udp;
+	return EOK;
+error:
+	free(udp);
+	return rc;
+}
+
+/** Destroy UDP client instance.
+ *
+ * @param udp UDP client
+ */
+void udp_destroy(udp_t *udp)
+{
+	if (udp == NULL)
+		return;
+
+	async_hangup(udp->sess);
+
+	fibril_mutex_lock(&udp->lock);
+	while (!udp->cb_done)
+		fibril_condvar_wait(&udp->cv, &udp->lock);
+	fibril_mutex_unlock(&udp->lock);
+
+	free(udp);
+}
+
+/** Create new UDP association.
+ *
+ * Create a UDP association that allows sending and receiving messages.
+ *
+ * @a epp may specify remote address and port, in which case only messages
+ * from that remote endpoint will be received. Also, that remote endpoint
+ * is used as default when @c NULL is passed as destination to
+ * udp_assoc_send_msg.
+ *
+ * @a epp may specify a local link or address. If it does not, the association
+ * will listen on all local links/addresses. If @a epp does not specify
+ * a local port number, a free dynamic port number will be allocated.
+ *
+ * The caller is informed about incoming data by invoking @a cb->recv_msg
+ *
+ * @param udp    UDP client
+ * @param epp    Internet endpoint pair
+ * @param cb     Callbacks
+ * @param arg    Argument to callbacks
+ * @param rassoc Place to store pointer to new association
+ *
+ * @return EOK on success or an error code.
+ */
+errno_t udp_assoc_create(udp_t *udp, inet_ep2_t *epp, udp_cb_t *cb, void *arg,
+    udp_assoc_t **rassoc)
+{
+	async_exch_t *exch;
+	udp_assoc_t *assoc;
+	ipc_call_t answer;
+
+	assoc = calloc(1, sizeof(udp_assoc_t));
+	if (assoc == NULL)
+		return ENOMEM;
+
+	exch = async_exchange_begin(udp->sess);
+	aid_t req = async_send_0(exch, UDP_ASSOC_CREATE, &answer);
+	errno_t rc = async_data_write_start(exch, (void *)epp,
+	    sizeof(inet_ep2_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		errno_t rc_orig;
+		async_wait_for(req, &rc_orig);
+		if (rc_orig != EOK)
+			rc = rc_orig;
+		goto error;
+	}
+
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		goto error;
+
+	assoc->udp = udp;
+	assoc->id = ipc_get_arg1(&answer);
+	assoc->cb = cb;
+	assoc->cb_arg = arg;
+
+	list_append(&assoc->ludp, &udp->assoc);
+	*rassoc = assoc;
+
+	return EOK;
+error:
+	free(assoc);
+	return (errno_t) rc;
+}
+
+/** Destroy UDP association.
+ *
+ * Destroy UDP association. The caller should destroy all associations
+ * he created before destroying the UDP client and before terminating.
+ *
+ * @param assoc UDP association
+ */
+void udp_assoc_destroy(udp_assoc_t *assoc)
+{
+	async_exch_t *exch;
+
+	if (assoc == NULL)
+		return;
+
+	list_remove(&assoc->ludp);
+
+	exch = async_exchange_begin(assoc->udp->sess);
+	errno_t rc = async_req_1_0(exch, UDP_ASSOC_DESTROY, assoc->id);
+	async_exchange_end(exch);
+
+	free(assoc);
+	(void) rc;
+}
+
+/** Set UDP association sending messages with no local address
+ *
+ * @param assoc Association
+ * @param flags Flags
+ */
+errno_t udp_assoc_set_nolocal(udp_assoc_t *assoc)
+{
+	async_exch_t *exch;
+
+	exch = async_exchange_begin(assoc->udp->sess);
+	errno_t rc = async_req_1_0(exch, UDP_ASSOC_SET_NOLOCAL, assoc->id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** Send message via UDP association.
+ *
+ * @param assoc Association
+ * @param dest	Destination endpoint or @c NULL to use association's remote ep.
+ * @param data	Message data
+ * @param bytes Message size in bytes
+ *
+ * @return EOK on success or an error code
+ */
+errno_t udp_assoc_send_msg(udp_assoc_t *assoc, inet_ep_t *dest, void *data,
+    size_t bytes)
+{
+	async_exch_t *exch;
+
+	exch = async_exchange_begin(assoc->udp->sess);
+	aid_t req = async_send_1(exch, UDP_ASSOC_SEND_MSG, assoc->id, NULL);
+
+	errno_t rc = async_data_write_start(exch, (void *)dest,
+	    sizeof(inet_ep_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_write_start(exch, data, bytes);
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	async_wait_for(req, &rc);
+	return rc;
+}
+
+/** Get the user/callback argument for an association.
+ *
+ * @param assoc UDP association
+ * @return User argument associated with association
+ */
+void *udp_assoc_userptr(udp_assoc_t *assoc)
+{
+	return assoc->cb_arg;
+}
+
+/** Get size of received message in bytes.
+ *
+ * Assuming jumbo messages can be received, the caller first needs to determine
+ * the size of the received message by calling this function, then they can
+ * read the message piece-wise using udp_rmsg_read().
+ *
+ * @param rmsg Received message
+ * @return Size of received message in bytes
+ */
+size_t udp_rmsg_size(udp_rmsg_t *rmsg)
+{
+	return rmsg->size;
+}
+
+/** Read part of received message.
+ *
+ * @param rmsg  Received message
+ * @param off   Start offset
+ * @param buf   Buffer for storing data
+ * @param bsize Buffer size
+ *
+ * @return EOK on success or an error code.
+ */
+errno_t udp_rmsg_read(udp_rmsg_t *rmsg, size_t off, void *buf, size_t bsize)
+{
+	async_exch_t *exch;
+	ipc_call_t answer;
+
+	exch = async_exchange_begin(rmsg->udp->sess);
+	aid_t req = async_send_1(exch, UDP_RMSG_READ, off, &answer);
+	errno_t rc = async_data_read_start(exch, buf, bsize);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK) {
+		return retval;
+	}
+
+	return EOK;
+}
+
+/** Get remote endpoint of received message.
+ *
+ * Place the remote endpoint (the one from which the message was supposedly
+ * sent) to @a ep.
+ *
+ * @param rmsg Received message
+ * @param ep   Place to store remote endpoint
+ */
+void udp_rmsg_remote_ep(udp_rmsg_t *rmsg, inet_ep_t *ep)
+{
+	*ep = rmsg->remote_ep;
+}
+
+/** Get type of received ICMP error message.
+ *
+ * @param rerr Received error message
+ * @return Error message type
+ */
+uint8_t udp_rerr_type(udp_rerr_t *rerr)
+{
+	return 0;
+}
+
+/** Get code of received ICMP error message.
+ *
+ * @param rerr Received error message
+ * @return Error message code
+ */
+uint8_t udp_rerr_code(udp_rerr_t *rerr)
+{
+	return 0;
+}
+
+/** Get information about the next received message from UDP service.
+ *
+ * @param udp  UDP client
+ * @param rmsg Place to store message information
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t udp_rmsg_info(udp_t *udp, udp_rmsg_t *rmsg)
+{
+	async_exch_t *exch;
+	inet_ep_t ep;
+	ipc_call_t answer;
+
+	exch = async_exchange_begin(udp->sess);
+	aid_t req = async_send_0(exch, UDP_RMSG_INFO, &answer);
+	errno_t rc = async_data_read_start(exch, &ep, sizeof(inet_ep_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return retval;
+
+	rmsg->udp = udp;
+	rmsg->assoc_id = ipc_get_arg1(&answer);
+	rmsg->size = ipc_get_arg2(&answer);
+	rmsg->remote_ep = ep;
+	return EOK;
+}
+
+/** Discard next received message in UDP service.
+ *
+ * @param udp UDP client
+ * @return EOK on success or an error code
+ */
+static errno_t udp_rmsg_discard(udp_t *udp)
+{
+	async_exch_t *exch;
+
+	exch = async_exchange_begin(udp->sess);
+	errno_t rc = async_req_0_0(exch, UDP_RMSG_DISCARD);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** Get association based on its ID.
+ *
+ * @param udp    UDP client
+ * @param id     Association ID
+ * @param rassoc Place to store pointer to association
+ *
+ * @return EOK on success, EINVAL if no association with the given ID exists
+ */
+static errno_t udp_assoc_get(udp_t *udp, sysarg_t id, udp_assoc_t **rassoc)
+{
+	list_foreach(udp->assoc, ludp, udp_assoc_t, assoc) {
+		if (assoc->id == id) {
+			*rassoc = assoc;
+			return EOK;
+		}
+	}
+
+	return EINVAL;
+}
+
+/** Handle 'data' event, i.e. some message(s) arrived.
+ *
+ * For each received message, get information about it, call @c recv_msg
+ * callback and discard it.
+ *
+ * @param udp   UDP client
+ * @param icall IPC message
+ *
+ */
+static void udp_ev_data(udp_t *udp, ipc_call_t *icall)
+{
+	udp_rmsg_t rmsg;
+	udp_assoc_t *assoc;
+	errno_t rc;
+
+	while (true) {
+		rc = udp_rmsg_info(udp, &rmsg);
+		if (rc != EOK) {
+			break;
+		}
+
+		rc = udp_assoc_get(udp, rmsg.assoc_id, &assoc);
+		if (rc != EOK) {
+			continue;
+		}
+
+		if (assoc->cb != NULL && assoc->cb->recv_msg != NULL)
+			assoc->cb->recv_msg(assoc, &rmsg);
+
+		rc = udp_rmsg_discard(udp);
+		if (rc != EOK) {
+			break;
+		}
+	}
+
+	async_answer_0(icall, EOK);
+}
+
+/** UDP service callback connection.
+ *
+ * @param icall Connect message
+ * @param arg   Argument, UDP client
+ *
+ */
+static void udp_cb_conn(ipc_call_t *icall, void *arg)
+{
+	udp_t *udp = (udp_t *)arg;
+
+	while (true) {
+		ipc_call_t call;
+		async_get_call(&call);
+
+		if (!ipc_get_imethod(&call)) {
+			/* Hangup */
+			async_answer_0(&call, EOK);
+			goto out;
+		}
+
+		switch (ipc_get_imethod(&call)) {
+		case UDP_EV_DATA:
+			udp_ev_data(udp, &call);
+			break;
+		default:
+			async_answer_0(&call, ENOTSUP);
+			break;
+		}
+	}
+
+out:
+	fibril_mutex_lock(&udp->lock);
+	udp->cb_done = true;
+	fibril_mutex_unlock(&udp->lock);
+	fibril_condvar_broadcast(&udp->cv);
+}
+
+/** @}
+ */
Index: uspace/lib/inet/test/eth_addr.c
===================================================================
--- uspace/lib/inet/test/eth_addr.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/test/eth_addr.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2021 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 <errno.h>
+#include <inet/eth_addr.h>
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(eth_addr);
+
+/** Test ETH_ADDR_INITIALIZER constructs correct Ethernet address */
+PCUT_TEST(initializer)
+{
+	eth_addr_t addr = ETH_ADDR_INITIALIZER(0x11, 0x22, 0x33, 0x44, 0x55,
+	    0x66);
+	PCUT_ASSERT_INT_EQUALS(0x112233445566, addr.a);
+}
+
+/** Test eth_addr_decode() correctly decodes from array of bytes */
+PCUT_TEST(decode)
+{
+	eth_addr_t addr;
+	uint8_t b[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+
+	eth_addr_decode(b, &addr);
+	PCUT_ASSERT_INT_EQUALS(0x112233445566, addr.a);
+}
+
+/** Test eth_addr_encode() correctly encodes to array of bytes */
+PCUT_TEST(encode)
+{
+	eth_addr_t addr;
+	uint8_t b[7] = { 0, 0, 0, 0, 0, 0, 0 };
+
+	addr.a = 0x112233445566;
+	eth_addr_encode(&addr, b);
+
+	PCUT_ASSERT_INT_EQUALS(0x11, b[0]);
+	PCUT_ASSERT_INT_EQUALS(0x22, b[1]);
+	PCUT_ASSERT_INT_EQUALS(0x33, b[2]);
+	PCUT_ASSERT_INT_EQUALS(0x44, b[3]);
+	PCUT_ASSERT_INT_EQUALS(0x55, b[4]);
+	PCUT_ASSERT_INT_EQUALS(0x66, b[5]);
+}
+
+/** Test eth_addr_compare() correctly compares two Ethernet addresses */
+PCUT_TEST(compare)
+{
+	eth_addr_t a;
+	eth_addr_t b;
+
+	a.a = 1;
+	b.a = 2;
+	PCUT_ASSERT_INT_EQUALS(-1, eth_addr_compare(&a, &b));
+
+	a.a = 2;
+	b.a = 2;
+	PCUT_ASSERT_INT_EQUALS(0, eth_addr_compare(&a, &b));
+
+	a.a = 2;
+	b.a = 1;
+	PCUT_ASSERT_INT_EQUALS(1, eth_addr_compare(&a, &b));
+}
+
+/** Tets eth_addr_format() correctly formats Ethernet address to string */
+PCUT_TEST(format)
+{
+	eth_addr_t addr1 = ETH_ADDR_INITIALIZER(0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
+	eth_addr_t addr2 = ETH_ADDR_INITIALIZER(0x01, 0x02, 0x03, 0x04, 0x05, 0x06);
+	eth_addr_t addr3 = ETH_ADDR_INITIALIZER(0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff);
+	eth_addr_str_t saddr;
+
+	eth_addr_format(&addr1, &saddr);
+	PCUT_ASSERT_STR_EQUALS("11:22:33:44:55:66", saddr.str);
+
+	eth_addr_format(&addr2, &saddr);
+	PCUT_ASSERT_STR_EQUALS("01:02:03:04:05:06", saddr.str);
+
+	eth_addr_format(&addr3, &saddr);
+	PCUT_ASSERT_STR_EQUALS("aa:bb:cc:dd:ee:ff", saddr.str);
+}
+
+PCUT_EXPORT(eth_addr);
Index: uspace/lib/inet/test/main.c
===================================================================
--- uspace/lib/inet/test/main.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/inet/test/main.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021 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 <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_IMPORT(eth_addr);
+
+PCUT_MAIN();
Index: uspace/lib/ipcgfx/include/ipcgfx/ipc/gc.h
===================================================================
--- uspace/lib/ipcgfx/include/ipcgfx/ipc/gc.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ipcgfx/include/ipcgfx/ipc/gc.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,11 +39,14 @@
 
 typedef enum {
-	GC_SET_RGB_COLOR = IPC_FIRST_USER_METHOD,
+	GC_SET_CLIP_RECT = IPC_FIRST_USER_METHOD,
+	GC_SET_CLIP_RECT_NULL,
+	GC_SET_RGB_COLOR,
 	GC_FILL_RECT,
+	GC_UPDATE,
 	GC_BITMAP_CREATE,
 	GC_BITMAP_CREATE_DOUTPUT,
 	GC_BITMAP_DESTROY,
 	GC_BITMAP_RENDER,
-	GC_BITMAP_GET_ALLOC
+	GC_BITMAP_GET_ALLOC,
 } gc_request_t;
 
Index: uspace/lib/ipcgfx/src/client.c
===================================================================
--- uspace/lib/ipcgfx/src/client.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ipcgfx/src/client.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -45,6 +45,8 @@
 #include "../private/client.h"
 
+static errno_t ipc_gc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t ipc_gc_set_color(void *, gfx_color_t *);
 static errno_t ipc_gc_fill_rect(void *, gfx_rect_t *);
+static errno_t ipc_gc_update(void *);
 static errno_t ipc_gc_bitmap_create(void *, gfx_bitmap_params_t *,
     gfx_bitmap_alloc_t *, void **);
@@ -54,6 +56,8 @@
 
 gfx_context_ops_t ipc_gc_ops = {
+	.set_clip_rect = ipc_gc_set_clip_rect,
 	.set_color = ipc_gc_set_color,
 	.fill_rect = ipc_gc_fill_rect,
+	.update = ipc_gc_update,
 	.bitmap_create = ipc_gc_bitmap_create,
 	.bitmap_destroy = ipc_gc_bitmap_destroy,
@@ -62,4 +66,30 @@
 };
 
+/** Set clipping rectangle on IPC GC.
+ *
+ * @param arg IPC GC
+ * @param rect Rectangle
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t ipc_gc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	ipc_gc_t *ipcgc = (ipc_gc_t *) arg;
+	async_exch_t *exch;
+	errno_t rc;
+
+	exch = async_exchange_begin(ipcgc->sess);
+	if (rect != NULL) {
+		rc = async_req_4_0(exch, GC_SET_CLIP_RECT, rect->p0.x, rect->p0.y,
+		    rect->p1.x, rect->p1.y);
+	} else {
+		rc = async_req_0_0(exch, GC_SET_CLIP_RECT_NULL);
+	}
+
+	async_exchange_end(exch);
+
+	return rc;
+}
+
 /** Set color on IPC GC.
  *
@@ -103,4 +133,23 @@
 	rc = async_req_4_0(exch, GC_FILL_RECT, rect->p0.x, rect->p0.y,
 	    rect->p1.x, rect->p1.y);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+/** Update display on IPC GC.
+ *
+ * @param arg IPC GC
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t ipc_gc_update(void *arg)
+{
+	ipc_gc_t *ipcgc = (ipc_gc_t *) arg;
+	async_exch_t *exch;
+	errno_t rc;
+
+	exch = async_exchange_begin(ipcgc->sess);
+	rc = async_req_0_0(exch, GC_UPDATE);
 	async_exchange_end(exch);
 
Index: uspace/lib/ipcgfx/src/server.c
===================================================================
--- uspace/lib/ipcgfx/src/server.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ipcgfx/src/server.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,5 +41,4 @@
 #include <gfx/color.h>
 #include <gfx/render.h>
-#include <ipc/bd.h>
 #include <ipcgfx/ipc/gc.h>
 #include <ipcgfx/server.h>
@@ -52,25 +51,5 @@
 static ipc_gc_srv_bitmap_t *gc_bitmap_lookup(ipc_gc_srv_t *, sysarg_t);
 
-static void gc_set_rgb_color_srv(ipc_gc_srv_t *srvgc, ipc_call_t *call)
-{
-	uint16_t r, g, b;
-	gfx_color_t *color;
-	errno_t rc;
-
-	r = ipc_get_arg1(call);
-	g = ipc_get_arg2(call);
-	b = ipc_get_arg3(call);
-
-	rc = gfx_color_new_rgb_i16(r, g, b, &color);
-	if (rc != EOK) {
-		async_answer_0(call, ENOMEM);
-		return;
-	}
-
-	rc = gfx_set_color(srvgc->gc, color);
-	async_answer_0(call, rc);
-}
-
-static void gc_fill_rect_srv(ipc_gc_srv_t *srvgc, ipc_call_t *call)
+static void gc_set_clip_rect_srv(ipc_gc_srv_t *srvgc, ipc_call_t *call)
 {
 	gfx_rect_t rect;
@@ -82,5 +61,55 @@
 	rect.p1.y = ipc_get_arg4(call);
 
+	rc = gfx_set_clip_rect(srvgc->gc, &rect);
+	async_answer_0(call, rc);
+}
+
+static void gc_set_clip_rect_null_srv(ipc_gc_srv_t *srvgc, ipc_call_t *call)
+{
+	errno_t rc;
+
+	rc = gfx_set_clip_rect(srvgc->gc, NULL);
+	async_answer_0(call, rc);
+}
+
+static void gc_set_rgb_color_srv(ipc_gc_srv_t *srvgc, ipc_call_t *call)
+{
+	uint16_t r, g, b;
+	gfx_color_t *color;
+	errno_t rc;
+
+	r = ipc_get_arg1(call);
+	g = ipc_get_arg2(call);
+	b = ipc_get_arg3(call);
+
+	rc = gfx_color_new_rgb_i16(r, g, b, &color);
+	if (rc != EOK) {
+		async_answer_0(call, ENOMEM);
+		return;
+	}
+
+	rc = gfx_set_color(srvgc->gc, color);
+	async_answer_0(call, rc);
+}
+
+static void gc_fill_rect_srv(ipc_gc_srv_t *srvgc, ipc_call_t *call)
+{
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rect.p0.x = ipc_get_arg1(call);
+	rect.p0.y = ipc_get_arg2(call);
+	rect.p1.x = ipc_get_arg3(call);
+	rect.p1.y = ipc_get_arg4(call);
+
 	rc = gfx_fill_rect(srvgc->gc, &rect);
+	async_answer_0(call, rc);
+}
+
+static void gc_update_srv(ipc_gc_srv_t *srvgc, ipc_call_t *call)
+{
+	errno_t rc;
+
+	rc = gfx_update(srvgc->gc);
 	async_answer_0(call, rc);
 }
@@ -353,4 +382,10 @@
 
 		switch (method) {
+		case GC_SET_CLIP_RECT:
+			gc_set_clip_rect_srv(&srvgc, &call);
+			break;
+		case GC_SET_CLIP_RECT_NULL:
+			gc_set_clip_rect_null_srv(&srvgc, &call);
+			break;
 		case GC_SET_RGB_COLOR:
 			gc_set_rgb_color_srv(&srvgc, &call);
@@ -358,4 +393,7 @@
 		case GC_FILL_RECT:
 			gc_fill_rect_srv(&srvgc, &call);
+			break;
+		case GC_UPDATE:
+			gc_update_srv(&srvgc, &call);
 			break;
 		case GC_BITMAP_CREATE:
Index: uspace/lib/ipcgfx/test/ipcgfx.c
===================================================================
--- uspace/lib/ipcgfx/test/ipcgfx.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ipcgfx/test/ipcgfx.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -50,6 +50,8 @@
 static void test_ipcgc_conn(ipc_call_t *, void *);
 
+static errno_t test_gc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t test_gc_set_color(void *, gfx_color_t *);
 static errno_t test_gc_fill_rect(void *, gfx_rect_t *);
+static errno_t test_gc_update(void *);
 static errno_t test_gc_bitmap_create(void *, gfx_bitmap_params_t *,
     gfx_bitmap_alloc_t *, void **);
@@ -59,6 +61,8 @@
 
 static gfx_context_ops_t test_gc_ops = {
+	.set_clip_rect = test_gc_set_clip_rect,
 	.set_color = test_gc_set_color,
 	.fill_rect = test_gc_fill_rect,
+	.update = test_gc_update,
 	.bitmap_create = test_gc_bitmap_create,
 	.bitmap_destroy = test_gc_bitmap_destroy,
@@ -73,4 +77,8 @@
 	errno_t rc;
 
+	bool set_clip_rect_called;
+	bool do_clip;
+	gfx_rect_t set_clip_rect_rect;
+
 	bool set_color_called;
 	uint16_t set_color_r;
@@ -80,4 +88,6 @@
 	bool fill_rect_called;
 	gfx_rect_t fill_rect_rect;
+
+	bool update_called;
 
 	bool bitmap_create_called;
@@ -99,4 +109,148 @@
 	gfx_bitmap_alloc_t alloc;
 } test_bitmap_t;
+
+/** gfx_set_clip_rect with server returning failure */
+PCUT_TEST(set_clip_rect_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_rect_t rect;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = ENOMEM;
+	resp.set_clip_rect_called = false;
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+	rc = gfx_set_clip_rect(gc, &rect);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_TRUE(resp.set_clip_rect_called);
+	PCUT_ASSERT_EQUALS(rect.p0.x, resp.set_clip_rect_rect.p0.x);
+	PCUT_ASSERT_EQUALS(rect.p0.y, resp.set_clip_rect_rect.p0.y);
+	PCUT_ASSERT_EQUALS(rect.p1.x, resp.set_clip_rect_rect.p1.x);
+	PCUT_ASSERT_EQUALS(rect.p1.y, resp.set_clip_rect_rect.p1.y);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_set_clip_rect with server returning success */
+PCUT_TEST(set_clip_rect_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	gfx_rect_t rect;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = EOK;
+	resp.set_clip_rect_called = false;
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+	rc = gfx_set_clip_rect(gc, &rect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.set_clip_rect_called);
+	PCUT_ASSERT_TRUE(resp.do_clip);
+	PCUT_ASSERT_EQUALS(rect.p0.x, resp.set_clip_rect_rect.p0.x);
+	PCUT_ASSERT_EQUALS(rect.p0.y, resp.set_clip_rect_rect.p0.y);
+	PCUT_ASSERT_EQUALS(rect.p1.x, resp.set_clip_rect_rect.p1.x);
+	PCUT_ASSERT_EQUALS(rect.p1.y, resp.set_clip_rect_rect.p1.y);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_set_clip_rect with null rectangle, server returning success */
+PCUT_TEST(set_clip_rect_null_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = EOK;
+	resp.set_clip_rect_called = false;
+
+	rc = gfx_set_clip_rect(gc, NULL);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.set_clip_rect_called);
+	PCUT_ASSERT_FALSE(resp.do_clip);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
 
 /** gfx_set_color with server returning failure */
@@ -230,5 +384,5 @@
 
 	resp.rc = ENOMEM;
-	resp.set_color_called = false;
+	resp.fill_rect_called = false;
 	rect.p0.x = 1;
 	rect.p0.y = 2;
@@ -280,5 +434,5 @@
 
 	resp.rc = EOK;
-	resp.set_color_called = false;
+	resp.fill_rect_called = false;
 	rect.p0.x = 1;
 	rect.p0.y = 2;
@@ -292,4 +446,86 @@
 	PCUT_ASSERT_EQUALS(rect.p1.x, resp.fill_rect_rect.p1.x);
 	PCUT_ASSERT_EQUALS(rect.p1.y, resp.fill_rect_rect.p1.y);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_update with server returning failure */
+PCUT_TEST(update_failure)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = ENOMEM;
+	resp.update_called = false;
+	rc = gfx_update(gc);
+	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
+	PCUT_ASSERT_TRUE(resp.update_called);
+
+	ipc_gc_delete(ipcgc);
+	async_hangup(sess);
+
+	rc = loc_service_unregister(sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** gfx_update with server returning success */
+PCUT_TEST(update_success)
+{
+	errno_t rc;
+	service_id_t sid;
+	test_response_t resp;
+	gfx_context_t *gc;
+	async_sess_t *sess;
+	ipc_gc_t *ipcgc;
+
+	async_set_fallback_port_handler(test_ipcgc_conn, &resp);
+
+	// FIXME This causes this test to be non-reentrant!
+	rc = loc_server_register(test_ipcgfx_server);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = loc_service_register(test_ipcgfx_svc, &sid);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sess = loc_service_connect(sid, INTERFACE_GC, 0);
+	PCUT_ASSERT_NOT_NULL(sess);
+
+	rc = ipc_gc_create(sess, &ipcgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ipc_gc_get_ctx(ipcgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	resp.rc = EOK;
+	resp.update_called = false;
+	rc = gfx_update(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.update_called);
 
 	ipc_gc_delete(ipcgc);
@@ -812,4 +1048,26 @@
 }
 
+/** Set clipping rectangle in test GC.
+ *
+ * @param arg Test GC
+ * @param rect Rectangle
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t test_gc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	test_response_t *resp = (test_response_t *) arg;
+
+	resp->set_clip_rect_called = true;
+	if (rect != NULL) {
+		resp->do_clip = true;
+		resp->set_clip_rect_rect = *rect;
+	} else {
+		resp->do_clip = false;
+	}
+
+	return resp->rc;
+}
+
 /** Set color in test GC.
  *
@@ -844,4 +1102,18 @@
 	resp->fill_rect_called = true;
 	resp->fill_rect_rect = *rect;
+	return resp->rc;
+}
+
+/** Update test GC.
+ *
+ * @param arg Test GC
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t test_gc_update(void *arg)
+{
+	test_response_t *resp = (test_response_t *) arg;
+
+	resp->update_called = true;
 	return resp->rc;
 }
Index: uspace/lib/label/meson.build
===================================================================
--- uspace/lib/label/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/label/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -27,4 +27,5 @@
 #
 
+deps = [ 'device' ]
 src = files(
 	'src/dummy.c',
Index: uspace/lib/math/_dummy.h
===================================================================
--- uspace/lib/math/_dummy.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/math/_dummy.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,1 @@
+/* Just to prevent tools/export.sh from failing */
Index: uspace/lib/math/test/rounding.c
===================================================================
--- uspace/lib/math/test/rounding.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/math/test/rounding.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -266,5 +266,5 @@
 	-0x1.fffffep100f,     /* large integer with full mantissa */
 
-	/* a few random numbers*/
+	/* a few random numbers */
 	3.5, -2.1, 100.0, 50.0, -1024.0, 0.0, 768.3156, 1080.499999, -600.0, 1.0
 };
@@ -476,5 +476,5 @@
 	-0x1.fffffep100f,   /* large integer with full mantissa */
 
-	/* a few random numbers*/
+	/* a few random numbers */
 	3.5, -2.1, 100.0, 50.0, -1024.0, 0.0, 768.3156, 1080.5, -600.0, 1.0
 };
@@ -686,5 +686,5 @@
 	-0x1.fffffep100f,   /* large integer with full mantissa */
 
-	/* a few random numbers*/
+	/* a few random numbers */
 	3.0, -2.0, 100.0, 50.0, -1024.0, 0.0, 768.0, 1080.0, -600.0, 1.0
 };
@@ -896,5 +896,5 @@
 	-0x1.fffffep100f,   /* large integer with full mantissa */
 
-	/* a few random numbers*/
+	/* a few random numbers */
 	4.0, -2.0, 100.0, 50.0, -1024.0, 0.0, 768.0, 1081.0, -600.0, 1.0
 };
@@ -1106,5 +1106,5 @@
 	-0x1.fffffep100f,   /* large integer with full mantissa */
 
-	/* a few random numbers*/
+	/* a few random numbers */
 	4.0, -2.0, 100.0, 50.0, -1024.0, 0.0, 768.0, 1080.0, -600.0, 1.0
 };
Index: uspace/lib/memgfx/include/memgfx/memgc.h
===================================================================
--- uspace/lib/memgfx/include/memgfx/memgc.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/memgfx/include/memgfx/memgc.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -46,5 +46,5 @@
 
 extern errno_t mem_gc_create(gfx_rect_t *, gfx_bitmap_alloc_t *,
-    mem_gc_update_cb_t, void *, mem_gc_t **);
+    mem_gc_cb_t *, void *, mem_gc_t **);
 extern errno_t mem_gc_delete(mem_gc_t *);
 extern void mem_gc_retarget(mem_gc_t *, gfx_rect_t *, gfx_bitmap_alloc_t *);
Index: uspace/lib/memgfx/include/types/memgfx/memgc.h
===================================================================
--- uspace/lib/memgfx/include/types/memgfx/memgc.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/memgfx/include/types/memgfx/memgc.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -37,4 +37,6 @@
 #define _MEMGFX_TYPES_MEMGC_H
 
+#include <errno.h>
+#include <stdbool.h>
 #include <types/gfx/coord.h>
 
@@ -42,5 +44,16 @@
 typedef struct mem_gc mem_gc_t;
 
-typedef void (*mem_gc_update_cb_t)(void *, gfx_rect_t *);
+typedef struct {
+	/** Invalidate rectangle */
+	void (*invalidate)(void *, gfx_rect_t *);
+	/** Update display */
+	void (*update)(void *);
+	/** Get cursor position */
+	errno_t (*cursor_get_pos)(void *, gfx_coord2_t *);
+	/** Set cursor position */
+	errno_t (*cursor_set_pos)(void *, gfx_coord2_t *);
+	/** Set cursor visibility */
+	errno_t (*cursor_set_visible)(void *, bool);
+} mem_gc_cb_t;
 
 #endif
Index: uspace/lib/memgfx/private/memgc.h
===================================================================
--- uspace/lib/memgfx/private/memgc.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/memgfx/private/memgc.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -49,9 +49,11 @@
 	/** Bounding rectangle */
 	gfx_rect_t rect;
+	/** Clipping rectangle */
+	gfx_rect_t clip_rect;
 	/** Allocation info */
 	gfx_bitmap_alloc_t alloc;
-	/** Update callback */
-	mem_gc_update_cb_t update;
-	/** Argument to callback */
+	/** Callbacks */
+	mem_gc_cb_t *cb;
+	/** Argument to callbacks */
 	void *cb_arg;
 	/** Current drawing color */
Index: uspace/lib/memgfx/src/memgc.c
===================================================================
--- uspace/lib/memgfx/src/memgc.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/memgfx/src/memgc.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -48,6 +48,8 @@
 #include "../private/memgc.h"
 
+static errno_t mem_gc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t mem_gc_set_color(void *, gfx_color_t *);
 static errno_t mem_gc_fill_rect(void *, gfx_rect_t *);
+static errno_t mem_gc_update(void *);
 static errno_t mem_gc_bitmap_create(void *, gfx_bitmap_params_t *,
     gfx_bitmap_alloc_t *, void **);
@@ -55,14 +57,41 @@
 static errno_t mem_gc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
 static errno_t mem_gc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
+static errno_t mem_gc_cursor_get_pos(void *, gfx_coord2_t *);
+static errno_t mem_gc_cursor_set_pos(void *, gfx_coord2_t *);
+static errno_t mem_gc_cursor_set_visible(void *, bool);
 static void mem_gc_invalidate_rect(mem_gc_t *, gfx_rect_t *);
 
 gfx_context_ops_t mem_gc_ops = {
+	.set_clip_rect = mem_gc_set_clip_rect,
 	.set_color = mem_gc_set_color,
 	.fill_rect = mem_gc_fill_rect,
+	.update = mem_gc_update,
 	.bitmap_create = mem_gc_bitmap_create,
 	.bitmap_destroy = mem_gc_bitmap_destroy,
 	.bitmap_render = mem_gc_bitmap_render,
-	.bitmap_get_alloc = mem_gc_bitmap_get_alloc
+	.bitmap_get_alloc = mem_gc_bitmap_get_alloc,
+	.cursor_get_pos = mem_gc_cursor_get_pos,
+	.cursor_set_pos = mem_gc_cursor_set_pos,
+	.cursor_set_visible = mem_gc_cursor_set_visible
 };
+
+/** Set clipping rectangle on memory GC.
+ *
+ * @param arg Memory GC
+ * @param rect Rectangle
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t mem_gc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	mem_gc_t *mgc = (mem_gc_t *) arg;
+
+	if (rect != NULL)
+		gfx_rect_clip(rect, &mgc->rect, &mgc->clip_rect);
+	else
+		mgc->clip_rect = mgc->rect;
+
+	return EOK;
+}
 
 /** Set color on memory GC.
@@ -100,5 +129,5 @@
 
 	/* Make sure we have a sorted, clipped rectangle */
-	gfx_rect_clip(rect, &mgc->rect, &crect);
+	gfx_rect_clip(rect, &mgc->clip_rect, &crect);
 
 	assert(mgc->rect.p0.x == 0);
@@ -119,4 +148,18 @@
 }
 
+/** Update memory GC.
+ *
+ * @param arg Memory GC
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t mem_gc_update(void *arg)
+{
+	mem_gc_t *mgc = (mem_gc_t *) arg;
+
+	mgc->cb->update(mgc->cb_arg);
+	return EOK;
+}
+
 /** Create memory GC.
  *
@@ -125,6 +168,6 @@
  * @param rect Bounding rectangle
  * @param alloc Allocation info
- * @param update_cb Function called to update a rectangle
- * @param cb_arg Argument to callback function
+ * @param cb Pointer to memory GC callbacks
+ * @param cb_arg Argument to callback functions
  * @param rgc Place to store pointer to new memory GC
  *
@@ -132,5 +175,5 @@
  */
 errno_t mem_gc_create(gfx_rect_t *rect, gfx_bitmap_alloc_t *alloc,
-    mem_gc_update_cb_t update_cb, void *cb_arg, mem_gc_t **rgc)
+    mem_gc_cb_t *cb, void *cb_arg, mem_gc_t **rgc)
 {
 	mem_gc_t *mgc = NULL;
@@ -150,7 +193,8 @@
 	mgc->gc = gc;
 	mgc->rect = *rect;
+	mgc->clip_rect = *rect;
 	mgc->alloc = *alloc;
 
-	mgc->update = update_cb;
+	mgc->cb = cb;
 	mgc->cb_arg = cb_arg;
 
@@ -190,4 +234,5 @@
 {
 	mgc->rect = *rect;
+	mgc->clip_rect = *rect;
 	mgc->alloc = *alloc;
 }
@@ -205,5 +250,5 @@
 static void mem_gc_invalidate_rect(mem_gc_t *mgc, gfx_rect_t *rect)
 {
-	mgc->update(mgc->cb_arg, rect);
+	mgc->cb->invalidate(mgc->cb_arg, rect);
 }
 
@@ -331,4 +376,5 @@
 	gfx_rect_t srect;
 	gfx_rect_t drect;
+	gfx_rect_t crect;
 	gfx_coord2_t offs;
 	gfx_coord_t x, y;
@@ -352,4 +398,7 @@
 	gfx_rect_translate(&offs, &srect, &drect);
 
+	/* Clip destination rectangle */
+	gfx_rect_clip(&drect, &mbm->mgc->clip_rect, &crect);
+
 	assert(mbm->alloc.pitch == (mbm->rect.p1.x - mbm->rect.p0.x) *
 	    (int)sizeof(uint32_t));
@@ -369,6 +418,6 @@
 	} else if ((mbm->flags & bmpf_color_key) == 0) {
 		/* Simple copy */
-		for (y = drect.p0.y; y < drect.p1.y; y++) {
-			for (x = drect.p0.x; x < drect.p1.x; x++) {
+		for (y = crect.p0.y; y < crect.p1.y; y++) {
+			for (x = crect.p0.x; x < crect.p1.x; x++) {
 				pixel = pixelmap_get_pixel(&smap,
 				    x - mbm->rect.p0.x - offs.x,
@@ -379,6 +428,6 @@
 	} else if ((mbm->flags & bmpf_colorize) == 0) {
 		/* Color key */
-		for (y = drect.p0.y; y < drect.p1.y; y++) {
-			for (x = drect.p0.x; x < drect.p1.x; x++) {
+		for (y = crect.p0.y; y < crect.p1.y; y++) {
+			for (x = crect.p0.x; x < crect.p1.x; x++) {
 				pixel = pixelmap_get_pixel(&smap,
 				    x - mbm->rect.p0.x - offs.x,
@@ -390,6 +439,6 @@
 	} else {
 		/* Color key & colorization */
-		for (y = drect.p0.y; y < drect.p1.y; y++) {
-			for (x = drect.p0.x; x < drect.p1.x; x++) {
+		for (y = crect.p0.y; y < crect.p1.y; y++) {
+			for (x = crect.p0.x; x < crect.p1.x; x++) {
 				pixel = pixelmap_get_pixel(&smap,
 				    x - mbm->rect.p0.x - offs.x,
@@ -402,5 +451,5 @@
 	}
 
-	mem_gc_invalidate_rect(mbm->mgc, &drect);
+	mem_gc_invalidate_rect(mbm->mgc, &crect);
 	return EOK;
 }
@@ -419,4 +468,55 @@
 }
 
+/** Get cursor position on memory GC.
+ *
+ * @param arg Memory GC
+ * @param pos Place to store position
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t mem_gc_cursor_get_pos(void *arg, gfx_coord2_t *pos)
+{
+	mem_gc_t *mgc = (mem_gc_t *) arg;
+
+	if (mgc->cb->cursor_get_pos != NULL)
+		return mgc->cb->cursor_get_pos(mgc->cb_arg, pos);
+	else
+		return ENOTSUP;
+}
+
+/** Set cursor position on memory GC.
+ *
+ * @param arg Memory GC
+ * @param pos New position
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t mem_gc_cursor_set_pos(void *arg, gfx_coord2_t *pos)
+{
+	mem_gc_t *mgc = (mem_gc_t *) arg;
+
+	if (mgc->cb->cursor_set_pos != NULL)
+		return mgc->cb->cursor_set_pos(mgc->cb_arg, pos);
+	else
+		return ENOTSUP;
+}
+
+/** Set cursor visibility on memory GC.
+ *
+ * @param arg Memory GC
+ * @param visible @c true iff cursor should be made visible
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t mem_gc_cursor_set_visible(void *arg, bool visible)
+{
+	mem_gc_t *mgc = (mem_gc_t *) arg;
+
+	if (mgc->cb->cursor_set_visible != NULL)
+		return mgc->cb->cursor_set_visible(mgc->cb_arg, visible);
+	else
+		return ENOTSUP;
+}
+
 /** @}
  */
Index: uspace/lib/memgfx/test/memgfx.c
===================================================================
--- uspace/lib/memgfx/test/memgfx.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/memgfx/test/memgfx.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -32,4 +32,5 @@
 #include <gfx/coord.h>
 #include <gfx/context.h>
+#include <gfx/cursor.h>
 #include <gfx/render.h>
 #include <io/pixelmap.h>
@@ -42,12 +43,40 @@
 PCUT_TEST_SUITE(memgfx);
 
-static void test_update_rect(void *arg, gfx_rect_t *rect);
+static void test_invalidate_rect(void *arg, gfx_rect_t *rect);
+static void test_update(void *arg);
+static errno_t test_cursor_get_pos(void *arg, gfx_coord2_t *);
+static errno_t test_cursor_set_pos(void *arg, gfx_coord2_t *);
+static errno_t test_cursor_set_visible(void *arg, bool);
+
+static mem_gc_cb_t test_mem_gc_cb = {
+	.invalidate = test_invalidate_rect,
+	.update = test_update,
+	.cursor_get_pos = test_cursor_get_pos,
+	.cursor_set_pos = test_cursor_set_pos,
+	.cursor_set_visible = test_cursor_set_visible
+};
 
 typedef struct {
+	/** Return code to return */
+	errno_t rc;
+	/** True if invalidate was called */
+	bool invalidate_called;
+	/** Invalidate rectangle */
+	gfx_rect_t inv_rect;
 	/** True if update was called */
 	bool update_called;
-	/** Update rectangle */
-	gfx_rect_t rect;
-} test_update_t;
+	/** True if cursor_get_pos was called */
+	bool cursor_get_pos_called;
+	/** Position to return from cursor_get_pos */
+	gfx_coord2_t get_pos_pos;
+	/** True if cursor_set_pos was called */
+	bool cursor_set_pos_called;
+	/** Position passed to cursor_set_pos */
+	gfx_coord2_t set_pos_pos;
+	/** True if cursor_set_visible was called */
+	bool cursor_set_visible_called;
+	/** Value passed to cursor_set_visible */
+	bool set_visible_vis;
+} test_resp_t;
 
 /** Test creating and deleting a memory GC */
@@ -89,19 +118,19 @@
 	pixel_t pixel;
 	pixel_t expected;
-	test_update_t update;
-	errno_t rc;
-
-	/* Bounding rectangle for memory GC */
-	rect.p0.x = 0;
-	rect.p0.y = 0;
-	rect.p1.x = 10;
-	rect.p1.y = 10;
-
-	alloc.pitch = (rect.p1.x - rect.p0.x) * sizeof(uint32_t);
-	alloc.off0 = 0;
-	alloc.pixels = calloc(1, alloc.pitch * (rect.p1.y - rect.p0.y));
-	PCUT_ASSERT_NOT_NULL(alloc.pixels);
-
-	rc = mem_gc_create(&rect, &alloc, test_update_rect, &update, &mgc);
+	test_resp_t resp;
+	errno_t rc;
+
+	/* Bounding rectangle for memory GC */
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = 10;
+	rect.p1.y = 10;
+
+	alloc.pitch = (rect.p1.x - rect.p0.x) * sizeof(uint32_t);
+	alloc.off0 = 0;
+	alloc.pixels = calloc(1, alloc.pitch * (rect.p1.y - rect.p0.y));
+	PCUT_ASSERT_NOT_NULL(alloc.pixels);
+
+	rc = mem_gc_create(&rect, &alloc, &test_mem_gc_cb, &resp, &mgc);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -122,5 +151,5 @@
 	frect.p1.y = 5;
 
-	memset(&update, 0, sizeof(update));
+	memset(&resp, 0, sizeof(resp));
 
 	rc = gfx_fill_rect(gc, &frect);
@@ -141,10 +170,10 @@
 	}
 
-	/* Check that the update rect is equal to the filled rect */
-	PCUT_ASSERT_TRUE(update.update_called);
-	PCUT_ASSERT_INT_EQUALS(frect.p0.x, update.rect.p0.x);
-	PCUT_ASSERT_INT_EQUALS(frect.p0.y, update.rect.p0.y);
-	PCUT_ASSERT_INT_EQUALS(frect.p1.x, update.rect.p1.x);
-	PCUT_ASSERT_INT_EQUALS(frect.p1.y, update.rect.p1.y);
+	/* Check that the invalidate rect is equal to the filled rect */
+	PCUT_ASSERT_TRUE(resp.invalidate_called);
+	PCUT_ASSERT_INT_EQUALS(frect.p0.x, resp.inv_rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(frect.p0.y, resp.inv_rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(frect.p1.x, resp.inv_rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(frect.p1.y, resp.inv_rect.p1.y);
 
 	/* TODO: Check clipping once memgc can support pitch != width etc. */
@@ -169,19 +198,19 @@
 	pixel_t pixel;
 	pixel_t expected;
-	test_update_t update;
-	errno_t rc;
-
-	/* Bounding rectangle for memory GC */
-	rect.p0.x = 0;
-	rect.p0.y = 0;
-	rect.p1.x = 10;
-	rect.p1.y = 10;
-
-	alloc.pitch = (rect.p1.x - rect.p0.x) * sizeof(uint32_t);
-	alloc.off0 = 0;
-	alloc.pixels = calloc(1, alloc.pitch * (rect.p1.y - rect.p0.y));
-	PCUT_ASSERT_NOT_NULL(alloc.pixels);
-
-	rc = mem_gc_create(&rect, &alloc, test_update_rect, &update, &mgc);
+	test_resp_t resp;
+	errno_t rc;
+
+	/* Bounding rectangle for memory GC */
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = 10;
+	rect.p1.y = 10;
+
+	alloc.pitch = (rect.p1.x - rect.p0.x) * sizeof(uint32_t);
+	alloc.off0 = 0;
+	alloc.pixels = calloc(1, alloc.pitch * (rect.p1.y - rect.p0.y));
+	PCUT_ASSERT_NOT_NULL(alloc.pixels);
+
+	rc = mem_gc_create(&rect, &alloc, &test_mem_gc_cb, &resp, &mgc);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -191,4 +220,5 @@
 	/* Create bitmap */
 
+	gfx_bitmap_params_init(&params);
 	params.rect.p0.x = 0;
 	params.rect.p0.y = 0;
@@ -219,5 +249,5 @@
 	dpmap.data = alloc.pixels;
 
-	memset(&update, 0, sizeof(update));
+	memset(&resp, 0, sizeof(resp));
 
 	/* Render the bitmap */
@@ -237,10 +267,10 @@
 	}
 
-	/* Check that the update rect is equal to the filled rect */
-	PCUT_ASSERT_TRUE(update.update_called);
-	PCUT_ASSERT_INT_EQUALS(params.rect.p0.x, update.rect.p0.x);
-	PCUT_ASSERT_INT_EQUALS(params.rect.p0.y, update.rect.p0.y);
-	PCUT_ASSERT_INT_EQUALS(params.rect.p1.x, update.rect.p1.x);
-	PCUT_ASSERT_INT_EQUALS(params.rect.p1.y, update.rect.p1.y);
+	/* Check that the invalidate rect is equal to the filled rect */
+	PCUT_ASSERT_TRUE(resp.invalidate_called);
+	PCUT_ASSERT_INT_EQUALS(params.rect.p0.x, resp.inv_rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(params.rect.p0.y, resp.inv_rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(params.rect.p1.x, resp.inv_rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(params.rect.p1.y, resp.inv_rect.p1.y);
 
 	/* TODO: Check clipping once memgc can support pitch != width etc. */
@@ -250,11 +280,217 @@
 }
 
-/** Called by memory GC when a rectangle is updated. */
-static void test_update_rect(void *arg, gfx_rect_t *rect)
-{
-	test_update_t *update = (test_update_t *)arg;
-
-	update->update_called = true;
-	update->rect = *rect;
+/** Test gfx_update() on a memory GC */
+PCUT_TEST(gfx_update)
+{
+	mem_gc_t *mgc;
+	gfx_rect_t rect;
+	gfx_bitmap_alloc_t alloc;
+	gfx_context_t *gc;
+	test_resp_t resp;
+	errno_t rc;
+
+	/* Bounding rectangle for memory GC */
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = 10;
+	rect.p1.y = 10;
+
+	alloc.pitch = (rect.p1.x - rect.p0.x) * sizeof(uint32_t);
+	alloc.off0 = 0;
+	alloc.pixels = calloc(1, alloc.pitch * (rect.p1.y - rect.p0.y));
+	PCUT_ASSERT_NOT_NULL(alloc.pixels);
+
+	rc = mem_gc_create(&rect, &alloc, &test_mem_gc_cb, &resp, &mgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = mem_gc_get_ctx(mgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	memset(&resp, 0, sizeof(resp));
+	PCUT_ASSERT_FALSE(resp.update_called);
+
+	gfx_update(gc);
+	PCUT_ASSERT_TRUE(resp.update_called);
+
+	mem_gc_delete(mgc);
+	free(alloc.pixels);
+}
+
+/** Test gfx_cursor_get_pos() on a memory GC */
+PCUT_TEST(gfx_cursor_get_pos)
+{
+	mem_gc_t *mgc;
+	gfx_rect_t rect;
+	gfx_bitmap_alloc_t alloc;
+	gfx_coord2_t pos;
+	gfx_context_t *gc;
+	test_resp_t resp;
+	errno_t rc;
+
+	/* Bounding rectangle for memory GC */
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = 10;
+	rect.p1.y = 10;
+
+	alloc.pitch = (rect.p1.x - rect.p0.x) * sizeof(uint32_t);
+	alloc.off0 = 0;
+	alloc.pixels = calloc(1, alloc.pitch * (rect.p1.y - rect.p0.y));
+	PCUT_ASSERT_NOT_NULL(alloc.pixels);
+
+	rc = mem_gc_create(&rect, &alloc, &test_mem_gc_cb, &resp, &mgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = mem_gc_get_ctx(mgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	memset(&resp, 0, sizeof(resp));
+	resp.rc = EOK;
+	resp.get_pos_pos.x = 1;
+	resp.get_pos_pos.y = 2;
+	PCUT_ASSERT_FALSE(resp.cursor_get_pos_called);
+
+	rc = gfx_cursor_get_pos(gc, &pos);
+	PCUT_ASSERT_TRUE(resp.cursor_get_pos_called);
+	PCUT_ASSERT_INT_EQUALS(resp.get_pos_pos.x, pos.x);
+	PCUT_ASSERT_INT_EQUALS(resp.get_pos_pos.y, pos.y);
+
+	mem_gc_delete(mgc);
+	free(alloc.pixels);
+}
+
+/** Test gfx_cursor_set_pos() on a memory GC */
+PCUT_TEST(gfx_cursor_set_pos)
+{
+	mem_gc_t *mgc;
+	gfx_rect_t rect;
+	gfx_bitmap_alloc_t alloc;
+	gfx_coord2_t pos;
+	gfx_context_t *gc;
+	test_resp_t resp;
+	errno_t rc;
+
+	/* Bounding rectangle for memory GC */
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = 10;
+	rect.p1.y = 10;
+
+	alloc.pitch = (rect.p1.x - rect.p0.x) * sizeof(uint32_t);
+	alloc.off0 = 0;
+	alloc.pixels = calloc(1, alloc.pitch * (rect.p1.y - rect.p0.y));
+	PCUT_ASSERT_NOT_NULL(alloc.pixels);
+
+	rc = mem_gc_create(&rect, &alloc, &test_mem_gc_cb, &resp, &mgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = mem_gc_get_ctx(mgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	memset(&resp, 0, sizeof(resp));
+	resp.rc = EOK;
+	pos.x = 1;
+	pos.y = 2;
+	PCUT_ASSERT_FALSE(resp.cursor_set_pos_called);
+
+	rc = gfx_cursor_set_pos(gc, &pos);
+	PCUT_ASSERT_TRUE(resp.cursor_set_pos_called);
+	PCUT_ASSERT_INT_EQUALS(pos.x, resp.set_pos_pos.x);
+	PCUT_ASSERT_INT_EQUALS(pos.y, resp.set_pos_pos.y);
+
+	mem_gc_delete(mgc);
+	free(alloc.pixels);
+}
+
+/** Test gfx_cursor_set_visible() on a memory GC */
+PCUT_TEST(gfx_cursor_set_visible)
+{
+	mem_gc_t *mgc;
+	gfx_rect_t rect;
+	gfx_bitmap_alloc_t alloc;
+	gfx_context_t *gc;
+	test_resp_t resp;
+	errno_t rc;
+
+	/* Bounding rectangle for memory GC */
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = 10;
+	rect.p1.y = 10;
+
+	alloc.pitch = (rect.p1.x - rect.p0.x) * sizeof(uint32_t);
+	alloc.off0 = 0;
+	alloc.pixels = calloc(1, alloc.pitch * (rect.p1.y - rect.p0.y));
+	PCUT_ASSERT_NOT_NULL(alloc.pixels);
+
+	rc = mem_gc_create(&rect, &alloc, &test_mem_gc_cb, &resp, &mgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = mem_gc_get_ctx(mgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	memset(&resp, 0, sizeof(resp));
+	resp.rc = EOK;
+	PCUT_ASSERT_FALSE(resp.cursor_set_visible_called);
+
+	rc = gfx_cursor_set_visible(gc, true);
+	PCUT_ASSERT_TRUE(resp.cursor_set_visible_called);
+	PCUT_ASSERT_TRUE(resp.set_visible_vis);
+
+	resp.cursor_set_visible_called = false;
+
+	rc = gfx_cursor_set_visible(gc, false);
+	PCUT_ASSERT_TRUE(resp.cursor_set_visible_called);
+	PCUT_ASSERT_FALSE(resp.set_visible_vis);
+
+	mem_gc_delete(mgc);
+	free(alloc.pixels);
+}
+
+/** Called by memory GC when a rectangle is modified. */
+static void test_invalidate_rect(void *arg, gfx_rect_t *rect)
+{
+	test_resp_t *resp = (test_resp_t *)arg;
+
+	resp->invalidate_called = true;
+	resp->inv_rect = *rect;
+}
+
+/** Called by memory GC when update is called. */
+static void test_update(void *arg)
+{
+	test_resp_t *resp = (test_resp_t *)arg;
+
+	resp->update_called = true;
+}
+
+/** Called by memory GC when cursor_get_pos is called. */
+static errno_t test_cursor_get_pos(void *arg, gfx_coord2_t *pos)
+{
+	test_resp_t *resp = (test_resp_t *)arg;
+
+	resp->cursor_get_pos_called = true;
+	*pos = resp->get_pos_pos;
+	return resp->rc;
+}
+
+/** Called by memory GC when cursor_set_pos is called. */
+static errno_t test_cursor_set_pos(void *arg, gfx_coord2_t *pos)
+{
+	test_resp_t *resp = (test_resp_t *)arg;
+
+	resp->cursor_set_pos_called = true;
+	resp->set_pos_pos = *pos;
+	return resp->rc;
+}
+
+/** Called by memory GC when cursor_set_visible is called. */
+static errno_t test_cursor_set_visible(void *arg, bool visible)
+{
+	test_resp_t *resp = (test_resp_t *)arg;
+
+	resp->cursor_set_visible_called = true;
+	resp->set_visible_vis = visible;
+	return resp->rc;
 }
 
Index: uspace/lib/meson.build
===================================================================
--- uspace/lib/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -42,4 +42,5 @@
 	'gfximage',
 	'ipcgfx',
+	'congfx',
 	'display',
 	'ui',
@@ -49,4 +50,8 @@
 libs = [
 	'c',
+
+	'inet',
+
+	'device',
 
 	'block',
Index: uspace/lib/minix/minix.h
===================================================================
--- uspace/lib/minix/minix.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/minix/minix.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -109,5 +109,5 @@
 	 */
 	uint16_t	s_magic;
-	/* Flag used to detect FS errors*/
+	/* Flag used to detect FS errors */
 	uint16_t	s_state;
 	/* Total number of zones on the device (V2 only) */
Index: uspace/lib/nettl/meson.build
===================================================================
--- uspace/lib/nettl/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/nettl/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2015 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,4 +27,5 @@
 #
 
+deps = [ 'inet' ]
 src = files(
 	'src/amap.c',
Index: uspace/lib/nic/src/nic_addr_db.c
===================================================================
--- uspace/lib/nic/src/nic_addr_db.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/nic/src/nic_addr_db.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -35,4 +35,5 @@
  * @brief Generic hash-set based database of addresses
  */
+
 #include "nic_addr_db.h"
 #include <assert.h>
@@ -41,4 +42,5 @@
 #include <errno.h>
 #include <mem.h>
+#include <member.h>
 #include <adt/hash_table.h>
 #include <macros.h>
Index: uspace/lib/posix/src/fnmatch.c
===================================================================
--- uspace/lib/posix/src/fnmatch.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/posix/src/fnmatch.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -169,5 +169,7 @@
 }
 
-/************** CHARACTER CLASSES ****************/
+/*
+ * CHARACTER CLASSES
+ */
 
 #define MAX_CLASS_OR_COLL_LEN 6
@@ -254,5 +256,7 @@
 }
 
-/************** END CHARACTER CLASSES ****************/
+/*
+ * END CHARACTER CLASSES
+ */
 
 /**
Index: uspace/lib/trackmod/protracker.c
===================================================================
--- uspace/lib/trackmod/protracker.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/trackmod/protracker.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -44,5 +44,5 @@
 #include "types/protracker.h"
 
-/** Sample tag decoding table entry*/
+/** Sample tag decoding table entry */
 typedef struct {
 	/** Tag */
Index: uspace/lib/ui/include/types/ui/checkbox.h
===================================================================
--- uspace/lib/ui/include/types/ui/checkbox.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/types/ui/checkbox.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 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 libui
+ * @{
+ */
+/**
+ * @file Check box
+ */
+
+#ifndef _UI_TYPES_CHECKBOX_H
+#define _UI_TYPES_CHECKBOX_H
+
+#include <stdbool.h>
+
+struct ui_checkbox;
+typedef struct ui_checkbox ui_checkbox_t;
+
+/** Check box callbacks */
+typedef struct ui_checkbox_cb {
+	void (*switched)(ui_checkbox_t *, void *, bool);
+} ui_checkbox_cb_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/types/ui/control.h
===================================================================
--- uspace/lib/ui/include/types/ui/control.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/types/ui/control.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,4 +38,5 @@
 
 #include <errno.h>
+#include <io/kbd_event.h>
 #include <io/pos_event.h>
 #include <types/ui/event.h>
@@ -50,6 +51,10 @@
 	/** Paint */
 	errno_t (*paint)(void *);
+	/** Keyboard event */
+	ui_evclaim_t (*kbd_event)(void *, kbd_event_t *);
 	/** Position event */
 	ui_evclaim_t (*pos_event)(void *, pos_event_t *);
+	/** Unfocus */
+	void (*unfocus)(void *);
 } ui_control_ops_t;
 
Index: uspace/lib/ui/include/types/ui/cursor.h
===================================================================
--- uspace/lib/ui/include/types/ui/cursor.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/types/ui/cursor.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -47,5 +47,7 @@
 	ui_curs_size_uldr,
 	/** Double arrow pointing up-right nad down-left */
-	ui_curs_size_urdl
+	ui_curs_size_urdl,
+	/** I-beam (suggests editable text) */
+	ui_curs_ibeam
 } ui_stock_cursor_t;
 
Index: uspace/lib/ui/include/types/ui/filedialog.h
===================================================================
--- uspace/lib/ui/include/types/ui/filedialog.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/types/ui/filedialog.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file File dialog
+ */
+
+#ifndef _UI_TYPES_FILEDIALOG_H
+#define _UI_TYPES_FILEDIALOG_H
+
+#include <errno.h>
+#include <io/kbd_event.h>
+#include <io/pos_event.h>
+#include <types/ui/wdecor.h>
+
+struct ui_file_dialog;
+typedef struct ui_file_dialog ui_file_dialog_t;
+
+/** File dialog parameters */
+typedef struct {
+	/** Window caption */
+	const char *caption;
+} ui_file_dialog_params_t;
+
+/** File dialog callback */
+typedef struct ui_file_dialog_cb {
+	/** OK button was pressed */
+	void (*bok)(ui_file_dialog_t *, void *, const char *);
+	/** Cancel button was pressed */
+	void (*bcancel)(ui_file_dialog_t *, void *);
+	/** Window closure requested (e.g. via close button) */
+	void (*close)(ui_file_dialog_t *, void *);
+} ui_file_dialog_cb_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/types/ui/menu.h
===================================================================
--- uspace/lib/ui/include/types/ui/menu.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/types/ui/menu.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Menu
+ */
+
+#ifndef _UI_TYPES_MENU_H
+#define _UI_TYPES_MENU_H
+
+struct ui_menu;
+typedef struct ui_menu ui_menu_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/types/ui/menubar.h
===================================================================
--- uspace/lib/ui/include/types/ui/menubar.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/types/ui/menubar.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Menu bar
+ */
+
+#ifndef _UI_TYPES_MENUBAR_H
+#define _UI_TYPES_MENUBAR_H
+
+struct ui_menu_bar;
+typedef struct ui_menu_bar ui_menu_bar_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/types/ui/menuentry.h
===================================================================
--- uspace/lib/ui/include/types/ui/menuentry.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/types/ui/menuentry.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Menu entry
+ */
+
+#ifndef _UI_TYPES_MENUENTRY_H
+#define _UI_TYPES_MENUENTRY_H
+
+struct ui_menu_entry;
+typedef struct ui_menu_entry ui_menu_entry_t;
+
+/** Menu entry callback */
+typedef void (*ui_menu_entry_cb_t)(ui_menu_entry_t *, void *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/types/ui/msgdialog.h
===================================================================
--- uspace/lib/ui/include/types/ui/msgdialog.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/types/ui/msgdialog.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Message Dialog
+ */
+
+#ifndef _UI_TYPES_MSGDIALOG_H
+#define _UI_TYPES_MSGDIALOG_H
+
+#include <errno.h>
+#include <io/kbd_event.h>
+#include <io/pos_event.h>
+#include <types/ui/wdecor.h>
+
+struct ui_msg_dialog;
+typedef struct ui_msg_dialog ui_msg_dialog_t;
+
+/** Message dialog parameters */
+typedef struct {
+	/** Window caption */
+	const char *caption;
+	/** Message text */
+	const char *text;
+} ui_msg_dialog_params_t;
+
+/** Message dialog callback */
+typedef struct ui_msg_dialog_cb {
+	/** Dialog button was pressed */
+	void (*button)(ui_msg_dialog_t *, void *, unsigned);
+	/** Window closure requested (e.g. via close button) */
+	void (*close)(ui_msg_dialog_t *, void *);
+} ui_msg_dialog_cb_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/types/ui/paint.h
===================================================================
--- uspace/lib/ui/include/types/ui/paint.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/types/ui/paint.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Painting routines
+ */
+
+#ifndef _UI_TYPES_PAINT_H
+#define _UI_TYPES_PAINT_H
+
+/** Filled circle parts */
+typedef enum {
+	/** Upper-left half */
+	ui_fcircle_upleft = 0x1,
+	/** Lower-right half */
+	ui_fcircle_lowright = 0x2,
+	/** Entire circle */
+	ui_fcircle_entire =
+	    ui_fcircle_upleft |
+	    ui_fcircle_lowright
+} ui_fcircle_part_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/types/ui/popup.h
===================================================================
--- uspace/lib/ui/include/types/ui/popup.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/types/ui/popup.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Popup window
+ */
+
+#ifndef _UI_TYPES_POPUP_H
+#define _UI_TYPES_POPUP_H
+
+#include <errno.h>
+#include <io/kbd_event.h>
+#include <io/pos_event.h>
+
+struct ui_popup;
+typedef struct ui_popup ui_popup_t;
+
+/** Popup window parameters */
+typedef struct {
+	/** Popup rectangle */
+	gfx_rect_t rect;
+	/** Placement rectangle close to which popup should be placed */
+	gfx_rect_t place;
+} ui_popup_params_t;
+
+/** Popup callbacks */
+typedef struct ui_popup_cb {
+	void (*close)(ui_popup_t *, void *);
+	void (*kbd)(ui_popup_t *, void *, kbd_event_t *);
+	errno_t (*paint)(ui_popup_t *, void *);
+	void (*pos)(ui_popup_t *, void *, pos_event_t *);
+} ui_popup_cb_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/types/ui/rbutton.h
===================================================================
--- uspace/lib/ui/include/types/ui/rbutton.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/types/ui/rbutton.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Radio button
+ */
+
+#ifndef _UI_TYPES_RBUTTON_H
+#define _UI_TYPES_RBUTTON_H
+
+struct ui_rbutton_group;
+typedef struct ui_rbutton_group ui_rbutton_group_t;
+
+struct ui_rbutton;
+typedef struct ui_rbutton ui_rbutton_t;
+
+/** Radio button callbacks */
+typedef struct ui_rbutton_group_cb {
+	void (*selected)(ui_rbutton_group_t *, void *, void *);
+} ui_rbutton_group_cb_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/types/ui/resource.h
===================================================================
--- uspace/lib/ui/include/types/ui/resource.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/types/ui/resource.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -40,4 +40,6 @@
 typedef struct ui_resource ui_resource_t;
 
+typedef void (*ui_expose_cb_t)(void *);
+
 #endif
 
Index: uspace/lib/ui/include/types/ui/slider.h
===================================================================
--- uspace/lib/ui/include/types/ui/slider.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/types/ui/slider.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Slider
+ */
+
+#ifndef _UI_TYPES_SLIDER_H
+#define _UI_TYPES_SLIDER_H
+
+#include <gfx/coord.h>
+
+struct ui_slider;
+typedef struct ui_slider ui_slider_t;
+
+/** Slider callbacks */
+typedef struct ui_slider_cb {
+	void (*moved)(ui_slider_t *, void *, gfx_coord_t);
+} ui_slider_cb_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/types/ui/ui.h
===================================================================
--- uspace/lib/ui/include/types/ui/ui.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/types/ui/ui.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -45,4 +45,14 @@
 #define UI_DISPLAY_DEFAULT NULL
 
+/** Window system */
+typedef enum {
+	/** Unknown */
+	ui_ws_unknown,
+	/** Display service */
+	ui_ws_display,
+	/** Console */
+	ui_ws_console
+} ui_winsys_t;
+
 #endif
 
Index: uspace/lib/ui/include/types/ui/window.h
===================================================================
--- uspace/lib/ui/include/types/ui/window.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/types/ui/window.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -58,6 +58,14 @@
 	ui_wnd_place_bottom_right,
 	/** Place window accross the entire screen */
-	ui_wnd_place_full_screen
+	ui_wnd_place_full_screen,
+	/** Place window as a popup window adjacent to rectangle */
+	ui_wnd_place_popup
 } ui_wnd_placement_t;
+
+/** Window flags */
+typedef enum {
+	/** Popup window */
+	ui_wndf_popup = 0x1
+} ui_wnd_flags_t;
 
 /** Window parameters */
@@ -71,4 +79,8 @@
 	/** Window placement */
 	ui_wnd_placement_t placement;
+	/** Window flags */
+	ui_wnd_flags_t flags;
+	/** Parent rectangle for popup windows */
+	gfx_rect_t prect;
 } ui_wnd_params_t;
 
Index: uspace/lib/ui/include/ui/checkbox.h
===================================================================
--- uspace/lib/ui/include/ui/checkbox.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/ui/checkbox.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 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 libui
+ * @{
+ */
+/**
+ * @file Check box
+ */
+
+#ifndef _UI_CHECKBOX_H
+#define _UI_CHECKBOX_H
+
+#include <errno.h>
+#include <gfx/coord.h>
+#include <io/pos_event.h>
+#include <types/ui/checkbox.h>
+#include <types/ui/control.h>
+#include <types/ui/event.h>
+#include <types/ui/resource.h>
+#include <stdbool.h>
+
+extern errno_t ui_checkbox_create(ui_resource_t *, const char *,
+    ui_checkbox_t **);
+extern void ui_checkbox_destroy(ui_checkbox_t *);
+extern ui_control_t *ui_checkbox_ctl(ui_checkbox_t *);
+extern void ui_checkbox_set_cb(ui_checkbox_t *, ui_checkbox_cb_t *, void *);
+extern void ui_checkbox_set_rect(ui_checkbox_t *, gfx_rect_t *);
+extern errno_t ui_checkbox_paint(ui_checkbox_t *);
+extern void ui_checkbox_press(ui_checkbox_t *);
+extern void ui_checkbox_release(ui_checkbox_t *);
+extern void ui_checkbox_enter(ui_checkbox_t *);
+extern void ui_checkbox_leave(ui_checkbox_t *);
+extern void ui_checkbox_switched(ui_checkbox_t *);
+extern ui_evclaim_t ui_checkbox_pos_event(ui_checkbox_t *, pos_event_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/ui/control.h
===================================================================
--- uspace/lib/ui/include/ui/control.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/ui/control.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,4 +38,5 @@
 
 #include <errno.h>
+#include <io/kbd_event.h>
 #include <io/pos_event.h>
 #include <types/ui/control.h>
@@ -46,5 +47,7 @@
 extern void ui_control_destroy(ui_control_t *);
 extern errno_t ui_control_paint(ui_control_t *);
+extern ui_evclaim_t ui_control_kbd_event(ui_control_t *, kbd_event_t *);
 extern ui_evclaim_t ui_control_pos_event(ui_control_t *, pos_event_t *);
+extern void ui_control_unfocus(ui_control_t *);
 
 #endif
Index: uspace/lib/ui/include/ui/entry.h
===================================================================
--- uspace/lib/ui/include/ui/entry.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/ui/entry.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -42,7 +42,7 @@
 #include <types/ui/control.h>
 #include <types/ui/entry.h>
-#include <types/ui/resource.h>
+#include <types/ui/window.h>
 
-extern errno_t ui_entry_create(ui_resource_t *, const char *,
+extern errno_t ui_entry_create(ui_window_t *, const char *,
     ui_entry_t **);
 extern void ui_entry_destroy(ui_entry_t *);
@@ -50,6 +50,19 @@
 extern void ui_entry_set_rect(ui_entry_t *, gfx_rect_t *);
 extern void ui_entry_set_halign(ui_entry_t *, gfx_halign_t);
+extern void ui_entry_set_read_only(ui_entry_t *, bool);
 extern errno_t ui_entry_set_text(ui_entry_t *, const char *);
+extern const char *ui_entry_get_text(ui_entry_t *);
 extern errno_t ui_entry_paint(ui_entry_t *);
+extern void ui_entry_backspace(ui_entry_t *);
+extern void ui_entry_delete(ui_entry_t *);
+extern void ui_entry_copy(ui_entry_t *);
+extern void ui_entry_cut(ui_entry_t *);
+extern void ui_entry_paste(ui_entry_t *);
+extern void ui_entry_seek_start(ui_entry_t *, bool);
+extern void ui_entry_seek_end(ui_entry_t *, bool);
+extern void ui_entry_seek_prev_char(ui_entry_t *, bool);
+extern void ui_entry_seek_next_char(ui_entry_t *, bool);
+extern ui_evclaim_t ui_entry_kbd_event(ui_entry_t *, kbd_event_t *);
+extern ui_evclaim_t ui_entry_pos_event(ui_entry_t *, pos_event_t *);
 
 #endif
Index: uspace/lib/ui/include/ui/filedialog.h
===================================================================
--- uspace/lib/ui/include/ui/filedialog.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/ui/filedialog.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file File dialog
+ */
+
+#ifndef _UI_FILEDIALOG_H
+#define _UI_FILEDIALOG_H
+
+#include <errno.h>
+#include <types/ui/filedialog.h>
+#include <types/ui/ui.h>
+
+extern void ui_file_dialog_params_init(ui_file_dialog_params_t *);
+extern errno_t ui_file_dialog_create(ui_t *, ui_file_dialog_params_t *,
+    ui_file_dialog_t **);
+extern void ui_file_dialog_set_cb(ui_file_dialog_t *, ui_file_dialog_cb_t *,
+    void *);
+extern void ui_file_dialog_destroy(ui_file_dialog_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/ui/fixed.h
===================================================================
--- uspace/lib/ui/include/ui/fixed.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/ui/fixed.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -49,5 +49,7 @@
 extern void ui_fixed_remove(ui_fixed_t *, ui_control_t *);
 extern errno_t ui_fixed_paint(ui_fixed_t *);
+extern ui_evclaim_t ui_fixed_kbd_event(ui_fixed_t *, kbd_event_t *);
 extern ui_evclaim_t ui_fixed_pos_event(ui_fixed_t *, pos_event_t *);
+extern void ui_fixed_unfocus(ui_fixed_t *);
 
 #endif
Index: uspace/lib/ui/include/ui/menu.h
===================================================================
--- uspace/lib/ui/include/ui/menu.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/ui/menu.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Menu
+ */
+
+#ifndef _UI_MENU_H
+#define _UI_MENU_H
+
+#include <errno.h>
+#include <gfx/coord.h>
+#include <io/pos_event.h>
+#include <types/ui/menu.h>
+#include <types/ui/menubar.h>
+#include <types/ui/event.h>
+
+extern errno_t ui_menu_create(ui_menu_bar_t *, const char *, ui_menu_t **);
+extern void ui_menu_destroy(ui_menu_t *);
+extern ui_menu_t *ui_menu_first(ui_menu_bar_t *);
+extern ui_menu_t *ui_menu_next(ui_menu_t *);
+extern const char *ui_menu_caption(ui_menu_t *);
+extern void ui_menu_get_rect(ui_menu_t *, gfx_coord2_t *, gfx_rect_t *);
+extern errno_t ui_menu_open(ui_menu_t *, gfx_rect_t *);
+extern void ui_menu_close(ui_menu_t *);
+extern errno_t ui_menu_paint(ui_menu_t *, gfx_coord2_t *);
+extern ui_evclaim_t ui_menu_pos_event(ui_menu_t *, gfx_coord2_t *,
+    pos_event_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/ui/menubar.h
===================================================================
--- uspace/lib/ui/include/ui/menubar.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/ui/menubar.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Menu bar
+ */
+
+#ifndef _UI_MENUBAR_H
+#define _UI_MENUBAR_H
+
+#include <errno.h>
+#include <gfx/coord.h>
+#include <io/pos_event.h>
+#include <types/ui/menubar.h>
+#include <types/ui/control.h>
+#include <types/ui/event.h>
+#include <types/ui/resource.h>
+#include <types/ui/ui.h>
+#include <types/ui/window.h>
+
+extern errno_t ui_menu_bar_create(ui_t *, ui_window_t *,
+    ui_menu_bar_t **);
+extern void ui_menu_bar_destroy(ui_menu_bar_t *);
+extern ui_control_t *ui_menu_bar_ctl(ui_menu_bar_t *);
+extern void ui_menu_bar_set_rect(ui_menu_bar_t *, gfx_rect_t *);
+extern errno_t ui_menu_bar_paint(ui_menu_bar_t *);
+extern ui_evclaim_t ui_menu_bar_pos_event(ui_menu_bar_t *, pos_event_t *);
+extern void ui_menu_bar_unfocus(ui_menu_bar_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/ui/menuentry.h
===================================================================
--- uspace/lib/ui/include/ui/menuentry.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/ui/menuentry.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Menu entry
+ */
+
+#ifndef _UI_MENUENTRY_H
+#define _UI_MENUENTRY_H
+
+#include <errno.h>
+#include <gfx/coord.h>
+#include <types/ui/menu.h>
+#include <types/ui/menuentry.h>
+#include <types/ui/event.h>
+
+extern errno_t ui_menu_entry_create(ui_menu_t *, const char *, const char *,
+    ui_menu_entry_t **);
+extern errno_t ui_menu_entry_sep_create(ui_menu_t *, ui_menu_entry_t **);
+extern void ui_menu_entry_destroy(ui_menu_entry_t *);
+extern void ui_menu_entry_set_cb(ui_menu_entry_t *, ui_menu_entry_cb_t,
+    void *);
+extern ui_menu_entry_t *ui_menu_entry_first(ui_menu_t *);
+extern ui_menu_entry_t *ui_menu_entry_next(ui_menu_entry_t *);
+extern gfx_coord_t ui_menu_entry_calc_width(ui_menu_t *,
+    gfx_coord_t, gfx_coord_t);
+extern void ui_menu_entry_column_widths(ui_menu_entry_t *,
+    gfx_coord_t *, gfx_coord_t *);
+extern gfx_coord_t ui_menu_entry_height(ui_menu_entry_t *);
+extern errno_t ui_menu_entry_paint(ui_menu_entry_t *, gfx_coord2_t *);
+extern void ui_menu_entry_press(ui_menu_entry_t *, gfx_coord2_t *);
+extern void ui_menu_entry_release(ui_menu_entry_t *);
+extern void ui_menu_entry_enter(ui_menu_entry_t *, gfx_coord2_t *);
+extern void ui_menu_entry_leave(ui_menu_entry_t *, gfx_coord2_t *);
+extern ui_evclaim_t ui_menu_entry_pos_event(ui_menu_entry_t *, gfx_coord2_t *,
+    pos_event_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/ui/msgdialog.h
===================================================================
--- uspace/lib/ui/include/ui/msgdialog.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/ui/msgdialog.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Message dialog
+ */
+
+#ifndef _UI_MSGDIALOG_H
+#define _UI_MSGDIALOG_H
+
+#include <errno.h>
+#include <types/ui/msgdialog.h>
+#include <types/ui/ui.h>
+
+extern void ui_msg_dialog_params_init(ui_msg_dialog_params_t *);
+extern errno_t ui_msg_dialog_create(ui_t *, ui_msg_dialog_params_t *,
+    ui_msg_dialog_t **);
+extern void ui_msg_dialog_set_cb(ui_msg_dialog_t *, ui_msg_dialog_cb_t *,
+    void *);
+extern void ui_msg_dialog_destroy(ui_msg_dialog_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/ui/paint.h
===================================================================
--- uspace/lib/ui/include/ui/paint.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/ui/paint.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,7 +40,19 @@
 #include <gfx/color.h>
 #include <gfx/coord.h>
+#include <types/ui/paint.h>
+#include <types/ui/resource.h>
 
-errno_t ui_paint_bevel(gfx_context_t *, gfx_rect_t *, gfx_color_t *,
+extern errno_t ui_paint_bevel(gfx_context_t *, gfx_rect_t *, gfx_color_t *,
     gfx_color_t *, gfx_coord_t, gfx_rect_t *);
+extern void ui_paint_get_bevel_inside(gfx_context_t *, gfx_rect_t *,
+    gfx_coord_t, gfx_rect_t *);
+extern errno_t ui_paint_inset_frame(ui_resource_t *, gfx_rect_t *,
+    gfx_rect_t *);
+extern void ui_paint_get_inset_frame_inside(ui_resource_t *, gfx_rect_t *,
+    gfx_rect_t *);
+extern errno_t ui_paint_outset_frame(ui_resource_t *, gfx_rect_t *,
+    gfx_rect_t *);
+extern errno_t ui_paint_filled_circle(gfx_context_t *, gfx_coord2_t *,
+    gfx_coord_t, ui_fcircle_part_t);
 
 #endif
Index: uspace/lib/ui/include/ui/popup.h
===================================================================
--- uspace/lib/ui/include/ui/popup.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/ui/popup.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Popup window
+ */
+
+#ifndef _UI_POPUP_H
+#define _UI_POPUP_H
+
+#include <errno.h>
+#include <gfx/context.h>
+#include <types/ui/control.h>
+#include <types/ui/ui.h>
+#include <types/ui/popup.h>
+#include <types/ui/resource.h>
+#include <types/ui/window.h>
+
+extern void ui_popup_params_init(ui_popup_params_t *);
+extern errno_t ui_popup_create(ui_t *, ui_window_t *, ui_popup_params_t *,
+    ui_popup_t **);
+extern void ui_popup_set_cb(ui_popup_t *, ui_popup_cb_t *, void *);
+extern void ui_popup_destroy(ui_popup_t *);
+extern void ui_popup_add(ui_popup_t *, ui_control_t *);
+extern void ui_popup_remove(ui_popup_t *, ui_control_t *);
+extern ui_resource_t *ui_popup_get_res(ui_popup_t *);
+extern gfx_context_t *ui_popup_get_gc(ui_popup_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/ui/rbutton.h
===================================================================
--- uspace/lib/ui/include/ui/rbutton.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/ui/rbutton.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Radio button
+ */
+
+#ifndef _UI_RBUTTON_H
+#define _UI_RBUTTON_H
+
+#include <errno.h>
+#include <gfx/coord.h>
+#include <io/pos_event.h>
+#include <types/ui/rbutton.h>
+#include <types/ui/control.h>
+#include <types/ui/event.h>
+#include <types/ui/resource.h>
+#include <stdbool.h>
+
+extern errno_t ui_rbutton_group_create(ui_resource_t *,
+    ui_rbutton_group_t **);
+extern void ui_rbutton_group_destroy(ui_rbutton_group_t *);
+extern errno_t ui_rbutton_create(ui_rbutton_group_t *, const char *, void *,
+    ui_rbutton_t **);
+extern void ui_rbutton_destroy(ui_rbutton_t *);
+extern ui_control_t *ui_rbutton_ctl(ui_rbutton_t *);
+extern void ui_rbutton_group_set_cb(ui_rbutton_group_t *, ui_rbutton_group_cb_t *,
+    void *);
+extern void ui_rbutton_set_rect(ui_rbutton_t *, gfx_rect_t *);
+extern errno_t ui_rbutton_paint(ui_rbutton_t *);
+extern void ui_rbutton_press(ui_rbutton_t *);
+extern void ui_rbutton_release(ui_rbutton_t *);
+extern void ui_rbutton_enter(ui_rbutton_t *);
+extern void ui_rbutton_leave(ui_rbutton_t *);
+extern void ui_rbutton_select(ui_rbutton_t *);
+extern void ui_rbutton_selected(ui_rbutton_t *);
+extern ui_evclaim_t ui_rbutton_pos_event(ui_rbutton_t *, pos_event_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/ui/resource.h
===================================================================
--- uspace/lib/ui/include/ui/resource.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/ui/resource.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -39,8 +39,12 @@
 #include <errno.h>
 #include <gfx/context.h>
+#include <stdbool.h>
 #include <types/ui/resource.h>
 
-extern errno_t ui_resource_create(gfx_context_t *, ui_resource_t **);
+extern errno_t ui_resource_create(gfx_context_t *, bool, ui_resource_t **);
 extern void ui_resource_destroy(ui_resource_t *);
+extern void ui_resource_set_expose_cb(ui_resource_t *, ui_expose_cb_t,
+    void *);
+extern void ui_resource_expose(ui_resource_t *);
 
 #endif
Index: uspace/lib/ui/include/ui/slider.h
===================================================================
--- uspace/lib/ui/include/ui/slider.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/include/ui/slider.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Slider
+ */
+
+#ifndef _UI_SLIDER_H
+#define _UI_SLIDER_H
+
+#include <errno.h>
+#include <gfx/coord.h>
+#include <io/pos_event.h>
+#include <stdbool.h>
+#include <types/ui/control.h>
+#include <types/ui/event.h>
+#include <types/ui/resource.h>
+#include <types/ui/slider.h>
+
+extern errno_t ui_slider_create(ui_resource_t *, const char *,
+    ui_slider_t **);
+extern void ui_slider_destroy(ui_slider_t *);
+extern ui_control_t *ui_slider_ctl(ui_slider_t *);
+extern void ui_slider_set_cb(ui_slider_t *, ui_slider_cb_t *, void *);
+extern void ui_slider_set_rect(ui_slider_t *, gfx_rect_t *);
+extern errno_t ui_slider_paint(ui_slider_t *);
+extern errno_t ui_slider_btn_clear(ui_slider_t *);
+extern gfx_coord_t ui_slider_length(ui_slider_t *);
+extern void ui_slider_press(ui_slider_t *, gfx_coord2_t *);
+extern void ui_slider_release(ui_slider_t *, gfx_coord2_t *);
+extern void ui_slider_update(ui_slider_t *, gfx_coord2_t *);
+extern void ui_slider_moved(ui_slider_t *, gfx_coord_t);
+extern ui_evclaim_t ui_slider_pos_event(ui_slider_t *, pos_event_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/include/ui/ui.h
===================================================================
--- uspace/lib/ui/include/ui/ui.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/ui/ui.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,11 +39,17 @@
 #include <display.h>
 #include <errno.h>
+#include <io/console.h>
+#include <stdbool.h>
 #include <types/ui/ui.h>
 
 extern errno_t ui_create(const char *, ui_t **);
+extern errno_t ui_create_cons(console_ctrl_t *, ui_t **);
 extern errno_t ui_create_disp(display_t *, ui_t **);
 extern void ui_destroy(ui_t *);
 extern void ui_quit(ui_t *);
 extern void ui_run(ui_t *);
+extern errno_t ui_paint(ui_t *);
+extern bool ui_is_textmode(ui_t *);
+extern bool ui_is_fullscreen(ui_t *);
 
 #endif
Index: uspace/lib/ui/include/ui/wdecor.h
===================================================================
--- uspace/lib/ui/include/ui/wdecor.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/ui/wdecor.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -41,4 +41,5 @@
 #include <io/pos_event.h>
 #include <stdbool.h>
+#include <types/ui/event.h>
 #include <types/ui/resource.h>
 #include <types/ui/wdecor.h>
@@ -51,5 +52,5 @@
 extern void ui_wdecor_set_active(ui_wdecor_t *, bool);
 extern errno_t ui_wdecor_paint(ui_wdecor_t *);
-extern void ui_wdecor_pos_event(ui_wdecor_t *, pos_event_t *);
+extern ui_evclaim_t ui_wdecor_pos_event(ui_wdecor_t *, pos_event_t *);
 extern void ui_wdecor_rect_from_app(ui_wdecor_style_t, gfx_rect_t *,
     gfx_rect_t *);
Index: uspace/lib/ui/include/ui/window.h
===================================================================
--- uspace/lib/ui/include/ui/window.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/include/ui/window.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,4 +40,5 @@
 #include <gfx/context.h>
 #include <gfx/coord.h>
+#include <io/kbd_event.h>
 #include <io/pos_event.h>
 #include <types/ui/control.h>
@@ -53,12 +54,17 @@
 extern void ui_window_add(ui_window_t *, ui_control_t *);
 extern void ui_window_remove(ui_window_t *, ui_control_t *);
+extern ui_window_t *ui_window_get_active(ui_t *);
 extern errno_t ui_window_resize(ui_window_t *, gfx_rect_t *);
 extern ui_resource_t *ui_window_get_res(ui_window_t *);
 extern gfx_context_t *ui_window_get_gc(ui_window_t *);
+extern errno_t ui_window_get_pos(ui_window_t *, gfx_coord2_t *);
 extern errno_t ui_window_get_app_gc(ui_window_t *, gfx_context_t **);
 extern void ui_window_get_app_rect(ui_window_t *, gfx_rect_t *);
+extern void ui_window_set_ctl_cursor(ui_window_t *, ui_stock_cursor_t);
 extern errno_t ui_window_paint(ui_window_t *);
+extern void ui_window_def_kbd(ui_window_t *, kbd_event_t *);
 extern errno_t ui_window_def_paint(ui_window_t *);
 extern void ui_window_def_pos(ui_window_t *, pos_event_t *);
+extern void ui_window_def_unfocus(ui_window_t *);
 
 #endif
Index: uspace/lib/ui/meson.build
===================================================================
--- uspace/lib/ui/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2020 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,15 +27,24 @@
 #
 
-deps = [ 'gfx', 'gfxfont', 'memgfx', 'display' ]
+deps = [ 'gfx', 'gfxfont', 'memgfx', 'display', 'congfx' ]
 src = files(
+	'src/checkbox.c',
 	'src/control.c',
 	'src/dummygc.c',
 	'src/entry.c',
+	'src/filedialog.c',
 	'src/fixed.c',
 	'src/image.c',
 	'src/label.c',
+	'src/menu.c',
+	'src/menubar.c',
+	'src/menuentry.c',
+	'src/msgdialog.c',
 	'src/paint.c',
 	'src/pbutton.c',
+	'src/popup.c',
+	'src/rbutton.c',
 	'src/resource.c',
+	'src/slider.c',
 	'src/ui.c',
 	'src/wdecor.c',
@@ -44,13 +53,22 @@
 
 test_src = files(
+	'test/checkbox.c',
 	'test/control.c',
 	'test/entry.c',
+	'test/filedialog.c',
 	'test/fixed.c',
 	'test/image.c',
 	'test/label.c',
 	'test/main.c',
+	'test/menu.c',
+	'test/menubar.c',
+	'test/menuentry.c',
+	'test/msgdialog.c',
 	'test/paint.c',
 	'test/pbutton.c',
+	'test/popup.c',
+	'test/rbutton.c',
 	'test/resource.c',
+	'test/slider.c',
 	'test/ui.c',
 	'test/wdecor.c',
Index: uspace/lib/ui/private/checkbox.h
===================================================================
--- uspace/lib/ui/private/checkbox.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/private/checkbox.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020 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 libui
+ * @{
+ */
+/**
+ * @file Check box structure
+ *
+ */
+
+#ifndef _UI_PRIVATE_CHECKBOX_H
+#define _UI_PRIVATE_CHECKBOX_H
+
+#include <gfx/coord.h>
+#include <stdbool.h>
+
+/** Actual structure of check box.
+ *
+ * This is private to libui.
+ */
+struct ui_checkbox {
+	/** Base control object */
+	struct ui_control *control;
+	/** UI resource */
+	struct ui_resource *res;
+	/** Callbacks */
+	struct ui_checkbox_cb *cb;
+	/** Callback argument */
+	void *arg;
+	/** Check box rectangle */
+	gfx_rect_t rect;
+	/** Caption */
+	const char *caption;
+	/** Check box is checked */
+	bool checked;
+	/** Check box is currently held down */
+	bool held;
+	/** Pointer is currently inside */
+	bool inside;
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/private/entry.h
===================================================================
--- uspace/lib/ui/private/entry.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/private/entry.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,4 +40,5 @@
 #include <gfx/coord.h>
 #include <gfx/text.h>
+#include <stdbool.h>
 
 /** Actual structure of text entry.
@@ -48,13 +49,57 @@
 	/** Base control object */
 	struct ui_control *control;
-	/** UI resource */
-	struct ui_resource *res;
+	/** UI window */
+	struct ui_window *window;
 	/** Entry rectangle */
 	gfx_rect_t rect;
 	/** Horizontal alignment */
 	gfx_halign_t halign;
+	/** Text entry is read-only */
+	bool read_only;
 	/** Text */
 	char *text;
+	/** Current scroll position (in pixels) */
+	gfx_coord_t scroll_pos;
+	/** Cursor position in the text (offset in bytes) */
+	unsigned pos;
+	/** Selection start position in text (offset in bytes) */
+	unsigned sel_start;
+	/** Pointer is currently inside */
+	bool pointer_inside;
+	/** Entry is activated */
+	bool active;
+	/** Button is held down */
+	bool held;
+	/** Left shift is held down */
+	bool lshift_held;
+	/** Right shift is held down */
+	bool rshift_held;
 };
+
+/** Text entry geometry.
+ *
+ * Computed geometry of text entry elements.
+ */
+typedef struct {
+	/** Interior rectangle */
+	gfx_rect_t interior_rect;
+	/** Text rectangle */
+	gfx_rect_t text_rect;
+	/** Text anchor position */
+	gfx_coord2_t text_pos;
+	/** Text anchor X coordinate */
+	gfx_coord_t anchor_x;
+} ui_entry_geom_t;
+
+extern errno_t ui_entry_insert_str(ui_entry_t *, const char *);
+extern ui_evclaim_t ui_entry_key_press_ctrl(ui_entry_t *, kbd_event_t *);
+extern ui_evclaim_t ui_entry_key_press_shift(ui_entry_t *, kbd_event_t *);
+extern ui_evclaim_t ui_entry_key_press_unmod(ui_entry_t *, kbd_event_t *);
+extern void ui_entry_get_geom(ui_entry_t *, ui_entry_geom_t *);
+extern size_t ui_entry_find_pos(ui_entry_t *, gfx_coord2_t *);
+extern void ui_entry_delete_sel(ui_entry_t *);
+extern void ui_entry_activate(ui_entry_t *);
+extern void ui_entry_deactivate(ui_entry_t *);
+extern void ui_entry_scroll_update(ui_entry_t *, bool);
 
 #endif
Index: uspace/lib/ui/private/filedialog.h
===================================================================
--- uspace/lib/ui/private/filedialog.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/private/filedialog.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file File dialog structure
+ *
+ */
+
+#ifndef _UI_PRIVATE_FILEDIALOG_H
+#define _UI_PRIVATE_FILEDIALOG_H
+
+/** Actual structure of file dialog.
+ *
+ * This is private to libui.
+ */
+struct ui_file_dialog {
+	/** Dialog window */
+	struct ui_window *window;
+	/** File name entry */
+	struct ui_entry *ename;
+	/** OK button */
+	struct ui_pbutton *bok;
+	/** Cancel button */
+	struct ui_pbutton *bcancel;
+	/** File dialog callbacks */
+	struct ui_file_dialog_cb *cb;
+	/** Callback argument */
+	void *arg;
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/private/menu.h
===================================================================
--- uspace/lib/ui/private/menu.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/private/menu.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Menu structure
+ *
+ */
+
+#ifndef _UI_PRIVATE_MENU_H
+#define _UI_PRIVATE_MENU_H
+
+#include <adt/list.h>
+#include <gfx/coord.h>
+#include <stdbool.h>
+#include <types/ui/menu.h>
+#include <types/ui/resource.h>
+
+/** Actual structure of menu.
+ *
+ * This is private to libui.
+ */
+struct ui_menu {
+	/** Containing menu bar */
+	struct ui_menu_bar *mbar;
+	/** Link to @c bar->menus */
+	link_t lmenus;
+	/** Caption */
+	char *caption;
+	/** Popup window or @c NULL if menu is not currently open */
+	struct ui_popup *popup;
+	/** Selected menu entry or @c NULL */
+	struct ui_menu_entry *selected;
+	/** Maximum caption width */
+	gfx_coord_t max_caption_w;
+	/** Maximum shortcut width */
+	gfx_coord_t max_shortcut_w;
+	/** Total entry height */
+	gfx_coord_t total_h;
+	/** Menu entries (ui_menu_entry_t) */
+	list_t entries;
+};
+
+/** Menu geometry.
+ *
+ * Computed rectangles of menu elements.
+ */
+typedef struct {
+	/** Outer rectangle */
+	gfx_rect_t outer_rect;
+	/** Entries rectangle */
+	gfx_rect_t entries_rect;
+} ui_menu_geom_t;
+
+extern void ui_menu_get_geom(ui_menu_t *, gfx_coord2_t *, ui_menu_geom_t *);
+extern ui_resource_t *ui_menu_get_res(ui_menu_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/private/menubar.h
===================================================================
--- uspace/lib/ui/private/menubar.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/private/menubar.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Menu bar structure
+ *
+ */
+
+#ifndef _UI_PRIVATE_MENUBAR_H
+#define _UI_PRIVATE_MENUBAR_H
+
+#include <adt/list.h>
+#include <gfx/coord.h>
+#include <types/ui/menu.h>
+#include <types/ui/menubar.h>
+
+/** Actual structure of menu bar.
+ *
+ * This is private to libui.
+ */
+struct ui_menu_bar {
+	/** Base control object */
+	struct ui_control *control;
+	/** UI */
+	struct ui *ui;
+	/** UI window containing menu bar */
+	struct ui_window *window;
+	/** Menu bar rectangle */
+	gfx_rect_t rect;
+	/** Selected menu or @c NULL */
+	struct ui_menu *selected;
+	/** List of menus (ui_menu_t) */
+	list_t menus;
+};
+
+extern void ui_menu_bar_select(ui_menu_bar_t *, gfx_rect_t *, ui_menu_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/private/menuentry.h
===================================================================
--- uspace/lib/ui/private/menuentry.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/private/menuentry.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Menu entry structure
+ *
+ */
+
+#ifndef _UI_PRIVATE_MENUENTRY_H
+#define _UI_PRIVATE_MENUENTRY_H
+
+#include <adt/list.h>
+#include <gfx/coord.h>
+#include <types/ui/menuentry.h>
+
+/** Actual structure of menu entry.
+ *
+ * This is private to libui.
+ */
+struct ui_menu_entry {
+	/** Containing menu */
+	struct ui_menu *menu;
+	/** Link to @c menu->entries */
+	link_t lentries;
+	/** Callbacks */
+	ui_menu_entry_cb_t cb;
+	/** This entry is a separator entry */
+	bool separator;
+	/** Menu entry is currently held down */
+	bool held;
+	/** Pointer is currently inside */
+	bool inside;
+	/** Callback argument */
+	void *arg;
+	/** Caption */
+	char *caption;
+	/** Shortcut key(s) */
+	char *shortcut;
+};
+
+/** Menu entry geometry.
+ *
+ * Computed positions of menu entry elements.
+ */
+typedef struct {
+	/** Outer rectangle */
+	gfx_rect_t outer_rect;
+	/** Caption position */
+	gfx_coord2_t caption_pos;
+	/** Shortcut position */
+	gfx_coord2_t shortcut_pos;
+} ui_menu_entry_geom_t;
+
+extern void ui_menu_entry_get_geom(ui_menu_entry_t *, gfx_coord2_t *,
+    ui_menu_entry_geom_t *);
+extern void ui_menu_entry_cb(ui_menu_entry_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/private/msgdialog.h
===================================================================
--- uspace/lib/ui/private/msgdialog.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/private/msgdialog.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Message dialog structure
+ *
+ */
+
+#ifndef _UI_PRIVATE_MSGDIALOG_H
+#define _UI_PRIVATE_MSGDIALOG_H
+
+/** Actual structure of message dialog.
+ *
+ * This is private to libui.
+ */
+struct ui_msg_dialog {
+	/** Dialog window */
+	struct ui_window *window;
+	/** OK button */
+	struct ui_pbutton *bok;
+	/** Message dialog callbacks */
+	struct ui_msg_dialog_cb *cb;
+	/** Callback argument */
+	void *arg;
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/private/popup.h
===================================================================
--- uspace/lib/ui/private/popup.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/private/popup.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Popup structure
+ *
+ */
+
+#ifndef _UI_PRIVATE_POPUP_H
+#define _UI_PRIVATE_POPUP_H
+
+#include <gfx/coord.h>
+#include <types/ui/window.h>
+
+/** Actual structure of popup window.
+ *
+ * This is private to libui.
+ */
+struct ui_popup {
+	/** Containing user interface */
+	struct ui *ui;
+	/** Callbacks */
+	struct ui_popup_cb *cb;
+	/** Callback argument */
+	void *arg;
+	/** Parent window */
+	struct ui_window *parent;
+	/** Window */
+	struct ui_window *window;
+	/** Placement rectangle */
+	gfx_rect_t place;
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/private/rbutton.h
===================================================================
--- uspace/lib/ui/private/rbutton.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/private/rbutton.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Radio button structure
+ *
+ */
+
+#ifndef _UI_PRIVATE_RBUTTON_H
+#define _UI_PRIVATE_RBUTTON_H
+
+#include <gfx/coord.h>
+#include <stdbool.h>
+
+/** Actual structure of radio button group.
+ *
+ * This is private to libui.
+ */
+struct ui_rbutton_group {
+	/** UI resource */
+	struct ui_resource *res;
+	/** Callbacks */
+	struct ui_rbutton_group_cb *cb;
+	/** Callback argument */
+	void *arg;
+	/** Selected button */
+	struct ui_rbutton *selected;
+};
+
+/** Actual structure of radio button.
+ *
+ * This is private to libui.
+ */
+struct ui_rbutton {
+	/** Base control object */
+	struct ui_control *control;
+	/** Containing radio button group */
+	struct ui_rbutton_group *group;
+	/** Callback argument */
+	void *arg;
+	/** Radio button rectangle */
+	gfx_rect_t rect;
+	/** Caption */
+	const char *caption;
+	/** Radio button is currently held down */
+	bool held;
+	/** Pointer is currently inside */
+	bool inside;
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/private/resource.h
===================================================================
--- uspace/lib/ui/private/resource.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/private/resource.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -42,4 +42,6 @@
 #include <gfx/font.h>
 #include <gfx/typeface.h>
+#include <stdbool.h>
+#include <types/ui/resource.h>
 
 /** Actual structure of UI resources.
@@ -54,4 +56,6 @@
 	/** Font */
 	gfx_font_t *font;
+	/** Text mode */
+	bool textmode;
 
 	/** Button frame color */
@@ -70,4 +74,8 @@
 	/** Window text color */
 	gfx_color_t *wnd_text_color;
+	/** Window selected text color */
+	gfx_color_t *wnd_sel_text_color;
+	/** Window selected text background color */
+	gfx_color_t *wnd_sel_text_bg_color;
 	/** Window frame hightlight color */
 	gfx_color_t *wnd_frame_hi_color;
@@ -92,4 +100,11 @@
 	/** Entry (text entry, checkbox, raido button) background color */
 	gfx_color_t *entry_bg_color;
+	/** Entry (text entry, checkbox, raido button) active background color */
+	gfx_color_t *entry_act_bg_color;
+
+	/** Expose callback or @c NULL */
+	ui_expose_cb_t expose_cb;
+	/** Expose callback argument */
+	void *expose_arg;
 };
 
Index: uspace/lib/ui/private/slider.h
===================================================================
--- uspace/lib/ui/private/slider.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/private/slider.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Slider structure
+ *
+ */
+
+#ifndef _UI_PRIVATE_SLIDER_H
+#define _UI_PRIVATE_SLIDER_H
+
+#include <gfx/coord.h>
+#include <stdbool.h>
+
+/** Actual structure of slider.
+ *
+ * This is private to libui.
+ */
+struct ui_slider {
+	/** Base control object */
+	struct ui_control *control;
+	/** UI resource */
+	struct ui_resource *res;
+	/** Callbacks */
+	struct ui_slider_cb *cb;
+	/** Callback argument */
+	void *arg;
+	/** Slider rectangle */
+	gfx_rect_t rect;
+	/** Slider is currently held down */
+	bool held;
+	/** Position where button was pressed */
+	gfx_coord2_t press_pos;
+	/** Last slider position */
+	gfx_coord_t last_pos;
+	/** Slider position */
+	gfx_coord_t pos;
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/ui/private/ui.h
===================================================================
--- uspace/lib/ui/private/ui.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/private/ui.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,5 +38,7 @@
 #define _UI_PRIVATE_UI_H
 
+#include <adt/list.h>
 #include <display.h>
+#include <io/console.h>
 #include <stdbool.h>
 
@@ -46,4 +48,8 @@
  */
 struct ui {
+	/** Console */
+	console_ctrl_t *console;
+	/** Console GC */
+	struct console_gc *cgc;
 	/** Display */
 	display_t *display;
@@ -52,4 +58,6 @@
 	/** @c true if terminating */
 	bool quit;
+	/** Windows (in stacking order, ui_window_t) */
+	list_t windows;
 };
 
Index: uspace/lib/ui/private/window.h
===================================================================
--- uspace/lib/ui/private/window.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/private/window.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,5 +38,7 @@
 #define _UI_PRIVATE_WINDOW_H
 
+#include <adt/list.h>
 #include <errno.h>
+#include <congfx/console.h>
 #include <display.h>
 #include <gfx/context.h>
@@ -44,4 +46,6 @@
 #include <io/pos_event.h>
 #include <memgfx/memgc.h>
+#include <types/ui/cursor.h>
+#include <types/ui/window.h>
 
 /** Actual structure of window.
@@ -52,4 +56,6 @@
 	/** Containing user interface */
 	struct ui *ui;
+	/** Link to @c ui->windows */
+	link_t lwindows;
 	/** Callbacks */
 	struct ui_window_cb *cb;
@@ -60,4 +66,10 @@
 	/** Window GC */
 	gfx_context_t *gc;
+	/** Window bitmap (if client-side rendering) */
+	gfx_bitmap_t *bmp;
+	/** Window memory GC (if client-side rendering) */
+	mem_gc_t *mgc;
+	/** Real window GC (if client-side rendering) */
+	gfx_context_t *realgc;
 	/** Window rectangle */
 	gfx_rect_t rect;
@@ -68,4 +80,6 @@
 	/** Application area GC */
 	gfx_context_t *app_gc;
+	/** Dirty rectangle */
+	gfx_rect_t dirty_rect;
 	/** UI resource. Ideally this would be in ui_t. */
 	struct ui_resource *res;
@@ -78,4 +92,5 @@
 };
 
+extern display_stock_cursor_t wnd_dcursor_from_cursor(ui_stock_cursor_t);
 extern void ui_window_send_close(ui_window_t *);
 extern void ui_window_send_focus(ui_window_t *);
Index: uspace/lib/ui/src/checkbox.c
===================================================================
--- uspace/lib/ui/src/checkbox.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/src/checkbox.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Check box
+ */
+
+#include <errno.h>
+#include <gfx/color.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <gfx/text.h>
+#include <io/pos_event.h>
+#include <stdlib.h>
+#include <str.h>
+#include <ui/control.h>
+#include <ui/paint.h>
+#include <ui/checkbox.h>
+#include "../private/checkbox.h"
+#include "../private/resource.h"
+
+enum {
+	checkbox_box_w = 16,
+	checkbox_box_h = 16,
+	checkbox_label_margin = 8,
+};
+
+static void ui_checkbox_ctl_destroy(void *);
+static errno_t ui_checkbox_ctl_paint(void *);
+static ui_evclaim_t ui_checkbox_ctl_pos_event(void *, pos_event_t *);
+
+/** Check box control ops */
+ui_control_ops_t ui_checkbox_ops = {
+	.destroy = ui_checkbox_ctl_destroy,
+	.paint = ui_checkbox_ctl_paint,
+	.pos_event = ui_checkbox_ctl_pos_event
+};
+
+/** Create new check box.
+ *
+ * @param resource UI resource
+ * @param caption Caption
+ * @param rcheckbox Place to store pointer to new check box
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ui_checkbox_create(ui_resource_t *resource, const char *caption,
+    ui_checkbox_t **rcheckbox)
+{
+	ui_checkbox_t *checkbox;
+	errno_t rc;
+
+	checkbox = calloc(1, sizeof(ui_checkbox_t));
+	if (checkbox == NULL)
+		return ENOMEM;
+
+	rc = ui_control_new(&ui_checkbox_ops, (void *) checkbox,
+	    &checkbox->control);
+	if (rc != EOK) {
+		free(checkbox);
+		return rc;
+	}
+
+	checkbox->caption = str_dup(caption);
+	if (checkbox->caption == NULL) {
+		ui_control_delete(checkbox->control);
+		free(checkbox);
+		return ENOMEM;
+	}
+
+	checkbox->res = resource;
+	*rcheckbox = checkbox;
+	return EOK;
+}
+
+/** Destroy check box.
+ *
+ * @param checkbox Check box or @c NULL
+ */
+void ui_checkbox_destroy(ui_checkbox_t *checkbox)
+{
+	if (checkbox == NULL)
+		return;
+
+	ui_control_delete(checkbox->control);
+	free(checkbox);
+}
+
+/** Get base control from check box.
+ *
+ * @param checkbox Check box
+ * @return Control
+ */
+ui_control_t *ui_checkbox_ctl(ui_checkbox_t *checkbox)
+{
+	return checkbox->control;
+}
+
+/** Set check box callbacks.
+ *
+ * @param checkbox Check box
+ * @param cb Check box callbacks
+ * @param arg Callback argument
+ */
+void ui_checkbox_set_cb(ui_checkbox_t *checkbox, ui_checkbox_cb_t *cb, void *arg)
+{
+	checkbox->cb = cb;
+	checkbox->arg = arg;
+}
+
+/** Set button rectangle.
+ *
+ * @param checkbox Button
+ * @param rect New button rectangle
+ */
+void ui_checkbox_set_rect(ui_checkbox_t *checkbox, gfx_rect_t *rect)
+{
+	checkbox->rect = *rect;
+}
+
+/** Paint check box.
+ *
+ * @param checkbox Check box
+ * @return EOK on success or an error code
+ */
+errno_t ui_checkbox_paint(ui_checkbox_t *checkbox)
+{
+	gfx_coord2_t pos;
+	gfx_text_fmt_t fmt;
+	gfx_rect_t box_rect;
+	gfx_rect_t box_inside;
+	gfx_coord2_t box_center;
+	bool depressed;
+	errno_t rc;
+
+	box_rect.p0 = checkbox->rect.p0;
+	box_rect.p1.x = box_rect.p0.x + checkbox_box_w;
+	box_rect.p1.y = box_rect.p0.y + checkbox_box_h;
+
+	/* Paint checkbox frame */
+
+	rc = ui_paint_inset_frame(checkbox->res, &box_rect, &box_inside);
+	if (rc != EOK)
+		goto error;
+
+	/* Paint checkbox interior */
+
+	depressed = checkbox->held && checkbox->inside;
+
+	rc = gfx_set_color(checkbox->res->gc, depressed ?
+	    checkbox->res->entry_act_bg_color :
+	    checkbox->res->entry_bg_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(checkbox->res->gc, &box_inside);
+	if (rc != EOK)
+		goto error;
+
+	/* Paint cross mark */
+
+	if (checkbox->checked) {
+		box_center.x = (box_inside.p0.x + box_inside.p1.x) / 2;
+		box_center.y = (box_inside.p0.y + box_inside.p1.y) / 2;
+
+		gfx_text_fmt_init(&fmt);
+		fmt.color = checkbox->res->entry_fg_color;
+		fmt.halign = gfx_halign_center;
+		fmt.valign = gfx_valign_center;
+
+		rc = gfx_puttext(checkbox->res->font, &box_center, &fmt, "X");
+		if (rc != EOK)
+			goto error;
+	}
+
+	/* Paint checkbox label */
+
+	pos.x = box_rect.p1.x + checkbox_label_margin;
+	pos.y = (box_rect.p0.y + box_rect.p1.y) / 2;
+
+	gfx_text_fmt_init(&fmt);
+	fmt.color = checkbox->res->wnd_text_color;
+	fmt.halign = gfx_halign_left;
+	fmt.valign = gfx_valign_center;
+
+	rc = gfx_puttext(checkbox->res->font, &pos, &fmt, checkbox->caption);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_update(checkbox->res->gc);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Press down button.
+ *
+ * @param checkbox Check box
+ */
+void ui_checkbox_press(ui_checkbox_t *checkbox)
+{
+	if (checkbox->held)
+		return;
+
+	checkbox->inside = true;
+	checkbox->held = true;
+	(void) ui_checkbox_paint(checkbox);
+}
+
+/** Release button.
+ *
+ * @param checkbox Check box
+ */
+void ui_checkbox_release(ui_checkbox_t *checkbox)
+{
+	if (!checkbox->held)
+		return;
+
+	checkbox->held = false;
+
+	if (checkbox->inside) {
+		/* Toggle check box state */
+		checkbox->checked = !checkbox->checked;
+
+		/* Repaint and notify */
+		(void) ui_checkbox_paint(checkbox);
+		ui_checkbox_switched(checkbox);
+	}
+}
+
+/** Pointer entered button.
+ *
+ * @param checkbox Check box
+ */
+void ui_checkbox_enter(ui_checkbox_t *checkbox)
+{
+	if (checkbox->inside)
+		return;
+
+	checkbox->inside = true;
+	if (checkbox->held)
+		(void) ui_checkbox_paint(checkbox);
+}
+
+/** Pointer left button.
+ *
+ * @param checkbox Check box
+ */
+void ui_checkbox_leave(ui_checkbox_t *checkbox)
+{
+	if (!checkbox->inside)
+		return;
+
+	checkbox->inside = false;
+	if (checkbox->held)
+		(void) ui_checkbox_paint(checkbox);
+}
+
+/** Button was switched.
+ *
+ * @param checkbox Check box
+ */
+void ui_checkbox_switched(ui_checkbox_t *checkbox)
+{
+	if (checkbox->cb != NULL && checkbox->cb->switched != NULL) {
+		checkbox->cb->switched(checkbox, checkbox->arg,
+		    checkbox->checked);
+	}
+}
+
+/** Handle check box position event.
+ *
+ * @param checkbox Check box
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_checkbox_pos_event(ui_checkbox_t *checkbox, pos_event_t *event)
+{
+	gfx_coord2_t pos;
+	bool inside;
+
+	pos.x = event->hpos;
+	pos.y = event->vpos;
+
+	inside = gfx_pix_inside_rect(&pos, &checkbox->rect);
+
+	switch (event->type) {
+	case POS_PRESS:
+		if (inside) {
+			ui_checkbox_press(checkbox);
+			return ui_claimed;
+		}
+		break;
+	case POS_RELEASE:
+		if (checkbox->held) {
+			ui_checkbox_release(checkbox);
+			return ui_claimed;
+		}
+		break;
+	case POS_UPDATE:
+		if (inside && !checkbox->inside) {
+			ui_checkbox_enter(checkbox);
+			return ui_claimed;
+		} else if (!inside && checkbox->inside) {
+			ui_checkbox_leave(checkbox);
+		}
+		break;
+	}
+
+	return ui_unclaimed;
+}
+
+/** Destroy check box control.
+ *
+ * @param arg Argument (ui_checkbox_t *)
+ */
+void ui_checkbox_ctl_destroy(void *arg)
+{
+	ui_checkbox_t *checkbox = (ui_checkbox_t *) arg;
+
+	ui_checkbox_destroy(checkbox);
+}
+
+/** Paint check box control.
+ *
+ * @param arg Argument (ui_checkbox_t *)
+ * @return EOK on success or an error code
+ */
+errno_t ui_checkbox_ctl_paint(void *arg)
+{
+	ui_checkbox_t *checkbox = (ui_checkbox_t *) arg;
+
+	return ui_checkbox_paint(checkbox);
+}
+
+/** Handle check box control position event.
+ *
+ * @param arg Argument (ui_checkbox_t *)
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_checkbox_ctl_pos_event(void *arg, pos_event_t *event)
+{
+	ui_checkbox_t *checkbox = (ui_checkbox_t *) arg;
+
+	return ui_checkbox_pos_event(checkbox, event);
+}
+
+/** @}
+ */
Index: uspace/lib/ui/src/control.c
===================================================================
--- uspace/lib/ui/src/control.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/src/control.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -35,4 +35,5 @@
 
 #include <errno.h>
+#include <io/kbd_event.h>
 #include <io/pos_event.h>
 #include <stdlib.h>
@@ -91,4 +92,18 @@
 }
 
+/** Deliver keyboard event to UI control.
+ *
+ * @param control Control
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_control_kbd_event(ui_control_t *control, kbd_event_t *event)
+{
+	if (control->ops->kbd_event != NULL)
+		return control->ops->kbd_event(control->ext, event);
+	else
+		return ui_unclaimed;
+}
+
 /** Paint UI control.
  *
@@ -112,4 +127,14 @@
 }
 
+/** Inform UI control that window has been unfocused.
+ *
+ * @param control Control
+ */
+void ui_control_unfocus(ui_control_t *control)
+{
+	if (control->ops->unfocus != NULL)
+		control->ops->unfocus(control->ext);
+}
+
 /** @}
  */
Index: uspace/lib/ui/src/dummygc.c
===================================================================
--- uspace/lib/ui/src/dummygc.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/src/dummygc.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,6 +41,8 @@
 #include "../private/dummygc.h"
 
+static errno_t dummygc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t dummygc_set_color(void *, gfx_color_t *);
 static errno_t dummygc_fill_rect(void *, gfx_rect_t *);
+static errno_t dummygc_update(void *);
 static errno_t dummygc_bitmap_create(void *, gfx_bitmap_params_t *,
     gfx_bitmap_alloc_t *, void **);
@@ -51,6 +53,8 @@
 /** Dummy GC operations */
 gfx_context_ops_t dummygc_ops = {
+	.set_clip_rect = dummygc_set_clip_rect,
 	.set_color = dummygc_set_color,
 	.fill_rect = dummygc_fill_rect,
+	.update = dummygc_update,
 	.bitmap_create = dummygc_bitmap_create,
 	.bitmap_destroy = dummygc_bitmap_destroy,
@@ -105,4 +109,17 @@
 }
 
+/** Set clipping rectangle on dummy GC
+ *
+ * @param arg Argument (dummy_gc_t)
+ * @param rect Rectangle
+ * @return EOK on success or an error code
+ */
+static errno_t dummygc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	(void) arg;
+	(void) rect;
+	return EOK;
+}
+
 /** Set color on dummy GC
  *
@@ -128,4 +145,15 @@
 	(void) arg;
 	(void) rect;
+	return EOK;
+}
+
+/** Update dummy GC
+ *
+ * @param arg Argument (dummy_gc_t)
+ * @return EOK on success or an error code
+ */
+static errno_t dummygc_update(void *arg)
+{
+	(void) arg;
 	return EOK;
 }
@@ -199,7 +227,21 @@
 {
 	dummygc_bitmap_t *tbm = (dummygc_bitmap_t *)bm;
+
 	tbm->dgc->bm_rendered = true;
-	tbm->dgc->bm_srect = *srect;
-	tbm->dgc->bm_offs = *offs;
+
+	tbm->dgc->bm_srect.p0.x = 0;
+	tbm->dgc->bm_srect.p0.y = 0;
+	tbm->dgc->bm_srect.p1.x = 0;
+	tbm->dgc->bm_srect.p1.y = 0;
+
+	tbm->dgc->bm_offs.x = 0;
+	tbm->dgc->bm_offs.y = 0;
+
+	if (srect != NULL)
+		tbm->dgc->bm_srect = *srect;
+
+	if (offs != NULL)
+		tbm->dgc->bm_offs = *offs;
+
 	return EOK;
 }
Index: uspace/lib/ui/src/entry.c
===================================================================
--- uspace/lib/ui/src/entry.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/src/entry.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -37,8 +37,11 @@
  */
 
+#include <clipboard.h>
 #include <errno.h>
 #include <gfx/context.h>
+#include <gfx/cursor.h>
 #include <gfx/render.h>
 #include <gfx/text.h>
+#include <macros.h>
 #include <stdlib.h>
 #include <str.h>
@@ -46,4 +49,6 @@
 #include <ui/paint.h>
 #include <ui/entry.h>
+#include <ui/ui.h>
+#include <ui/window.h>
 #include "../private/entry.h"
 #include "../private/resource.h"
@@ -51,9 +56,18 @@
 static void ui_entry_ctl_destroy(void *);
 static errno_t ui_entry_ctl_paint(void *);
+static ui_evclaim_t ui_entry_ctl_kbd_event(void *, kbd_event_t *);
 static ui_evclaim_t ui_entry_ctl_pos_event(void *, pos_event_t *);
 
 enum {
 	ui_entry_hpad = 4,
-	ui_entry_vpad = 4
+	ui_entry_vpad = 4,
+	ui_entry_hpad_text = 1,
+	ui_entry_vpad_text = 0,
+	ui_entry_cursor_overshoot = 1,
+	ui_entry_cursor_width = 2,
+	ui_entry_sel_hpad = 0,
+	ui_entry_sel_vpad = 2,
+	/** Additional amount to scroll to the left after revealing cursor */
+	ui_entry_left_scroll_margin = 30
 };
 
@@ -62,4 +76,5 @@
 	.destroy = ui_entry_ctl_destroy,
 	.paint = ui_entry_ctl_paint,
+	.kbd_event = ui_entry_ctl_kbd_event,
 	.pos_event = ui_entry_ctl_pos_event
 };
@@ -67,10 +82,10 @@
 /** Create new text entry.
  *
- * @param resource UI resource
+ * @param window UI window
  * @param text Text
  * @param rentry Place to store pointer to new text entry
  * @return EOK on success, ENOMEM if out of memory
  */
-errno_t ui_entry_create(ui_resource_t *resource, const char *text,
+errno_t ui_entry_create(ui_window_t *window, const char *text,
     ui_entry_t **rentry)
 {
@@ -95,7 +110,8 @@
 	}
 
-	entry->res = resource;
+	entry->window = window;
 	entry->halign = gfx_halign_left;
 	*rentry = entry;
+
 	return EOK;
 }
@@ -142,4 +158,16 @@
 {
 	entry->halign = halign;
+	ui_entry_scroll_update(entry, true);
+	ui_entry_paint(entry);
+}
+
+/** Set text entry read-only flag.
+ *
+ * @param entry Text entry
+ * @param read_only True iff entry is to be read-only.
+ */
+void ui_entry_set_read_only(ui_entry_t *entry, bool read_only)
+{
+	entry->read_only = read_only;
 }
 
@@ -160,8 +188,85 @@
 	free(entry->text);
 	entry->text = tcopy;
+	entry->pos = str_size(text);
+	entry->sel_start = entry->pos;
+
+	ui_entry_scroll_update(entry, false);
+	ui_entry_paint(entry);
 
 	return EOK;
 }
 
+/** Get entry text.
+ *
+ * @return Pointer to entry text.
+ */
+const char *ui_entry_get_text(ui_entry_t *entry)
+{
+	return entry->text;
+}
+
+/** Paint cursor.
+ *
+ * @param entry Text entry
+ * @param pos Cursor position (top-left corner of next character)
+ * @return EOK on success or an error code
+ */
+static errno_t ui_entry_paint_cursor(ui_entry_t *entry, gfx_coord2_t *pos)
+{
+	ui_resource_t *res;
+	gfx_rect_t rect;
+	gfx_font_metrics_t metrics;
+	errno_t rc;
+
+	res = ui_window_get_res(entry->window);
+
+	if (res->textmode) {
+		rc = gfx_cursor_set_pos(res->gc, pos);
+		return rc;
+	}
+
+	gfx_font_get_metrics(res->font, &metrics);
+
+	rect.p0.x = pos->x;
+	rect.p0.y = pos->y - ui_entry_cursor_overshoot;
+	rect.p1.x = pos->x + ui_entry_cursor_width;
+	rect.p1.y = pos->y + metrics.ascent + metrics.descent + 1 +
+	    ui_entry_cursor_overshoot;
+
+	rc = gfx_set_color(res->gc, res->entry_fg_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(res->gc, &rect);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Return width of text before cursor.
+ *
+ * @param entry Text entry
+ * @return Widht of text before cursor
+ */
+static gfx_coord_t ui_entry_lwidth(ui_entry_t *entry)
+{
+	ui_resource_t *res;
+	uint8_t tmp;
+	gfx_coord_t width;
+
+	res = ui_window_get_res(entry->window);
+
+	tmp = entry->text[entry->pos];
+
+	entry->text[entry->pos] = '\0';
+	width = gfx_text_width(res->font, entry->text);
+	entry->text[entry->pos] = tmp;
+
+	return width;
+}
+
 /** Paint text entry.
  *
@@ -171,101 +276,849 @@
 errno_t ui_entry_paint(ui_entry_t *entry)
 {
+	ui_resource_t *res;
+	ui_entry_geom_t geom;
 	gfx_text_fmt_t fmt;
 	gfx_coord2_t pos;
-	gfx_rect_t frame;
+	gfx_text_fmt_t cfmt;
+	gfx_coord2_t cpos;
 	gfx_rect_t inside;
+	unsigned off1, off2;
+	gfx_rect_t sel;
+	char c;
 	errno_t rc;
 
-	/* Paint inset frame */
-
-	rc = ui_paint_bevel(entry->res->gc, &entry->rect,
-	    entry->res->wnd_shadow_color, entry->res->wnd_highlight_color,
-	    1, &frame);
+	res = ui_window_get_res(entry->window);
+
+	ui_entry_get_geom(entry, &geom);
+
+	if (res->textmode == false) {
+		/* Paint inset frame */
+		rc = ui_paint_inset_frame(res, &entry->rect, &inside);
+		if (rc != EOK)
+			goto error;
+	} else {
+		inside = entry->rect;
+	}
+
+	/* Paint entry background */
+
+	rc = gfx_set_color(res->gc, res->entry_bg_color);
 	if (rc != EOK)
 		goto error;
 
-	rc = ui_paint_bevel(entry->res->gc, &frame,
-	    entry->res->wnd_frame_sh_color, entry->res->wnd_frame_hi_color,
-	    1, &inside);
+	rc = gfx_fill_rect(res->gc, &inside);
 	if (rc != EOK)
 		goto error;
 
-	/* Paint entry background */
-
-	rc = gfx_set_color(entry->res->gc, entry->res->entry_bg_color);
+	pos = geom.text_pos;
+
+	gfx_text_fmt_init(&fmt);
+	fmt.color = res->entry_fg_color;
+	fmt.halign = gfx_halign_left;
+	fmt.valign = gfx_valign_top;
+
+	rc = gfx_set_clip_rect(res->gc, &inside);
 	if (rc != EOK)
 		goto error;
 
-	rc = gfx_fill_rect(entry->res->gc, &inside);
+	off1 = min(entry->pos, entry->sel_start);
+	off2 = max(entry->pos, entry->sel_start);
+
+	/* Render initial segment before start of selection */
+	c = entry->text[off1];
+	entry->text[off1] = '\0';
+
+	rc = gfx_puttext(res->font, &pos, &fmt, entry->text);
+	if (rc != EOK) {
+		(void) gfx_set_clip_rect(res->gc, NULL);
+		goto error;
+	}
+
+	gfx_text_cont(res->font, &pos, &fmt, entry->text, &cpos, &cfmt);
+	entry->text[off1] = c;
+
+	/* Render selected text */
+
+	if (off1 != off2) {
+		c = entry->text[off2];
+		entry->text[off2] = '\0';
+		cfmt.color = res->entry_bg_color;
+
+		gfx_text_rect(res->font, &cpos, &cfmt, entry->text + off1, &sel);
+		sel.p0.x -= ui_entry_sel_hpad;
+		sel.p0.y -= ui_entry_sel_vpad;
+		sel.p1.x += ui_entry_sel_hpad;
+		sel.p1.y += ui_entry_sel_vpad;
+
+		rc = gfx_set_color(res->gc, res->entry_fg_color);
+		if (rc != EOK)
+			goto error;
+
+		rc = gfx_fill_rect(res->gc, &sel);
+		if (rc != EOK)
+			goto error;
+
+		rc = gfx_puttext(res->font, &cpos, &cfmt, entry->text + off1);
+		if (rc != EOK) {
+			(void) gfx_set_clip_rect(res->gc, NULL);
+			goto error;
+		}
+
+		gfx_text_cont(res->font, &cpos, &cfmt, entry->text + off1,
+		    &cpos, &cfmt);
+
+		entry->text[off2] = c;
+	}
+
+	/* Render trailing, non-selected text */
+	cfmt.color = res->entry_fg_color;
+
+	rc = gfx_puttext(res->font, &cpos, &cfmt, entry->text + off2);
+	if (rc != EOK) {
+		(void) gfx_set_clip_rect(res->gc, NULL);
+		goto error;
+	}
+
+	if (entry->active) {
+		/* Cursor */
+		pos.x += ui_entry_lwidth(entry);
+
+		rc = ui_entry_paint_cursor(entry, &pos);
+		if (rc != EOK) {
+			(void) gfx_set_clip_rect(res->gc, NULL);
+			goto error;
+		}
+	}
+
+	rc = gfx_set_clip_rect(res->gc, NULL);
 	if (rc != EOK)
 		goto error;
+
+	rc = gfx_update(res->gc);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Find position in text entry.
+ *
+ * @param entry Text entry
+ * @param fpos Position for which we need to find text offset
+ * @return Corresponding byte offset in entry text
+ */
+size_t ui_entry_find_pos(ui_entry_t *entry, gfx_coord2_t *fpos)
+{
+	ui_resource_t *res;
+	ui_entry_geom_t geom;
+	gfx_text_fmt_t fmt;
+
+	res = ui_window_get_res(entry->window);
+
+	ui_entry_get_geom(entry, &geom);
+
+	gfx_text_fmt_init(&fmt);
+	fmt.halign = gfx_halign_left;
+	fmt.valign = gfx_valign_top;
+
+	return gfx_text_find_pos(res->font, &geom.text_pos, &fmt,
+	    entry->text, fpos);
+}
+
+/** Destroy text entry control.
+ *
+ * @param arg Argument (ui_entry_t *)
+ */
+void ui_entry_ctl_destroy(void *arg)
+{
+	ui_entry_t *entry = (ui_entry_t *) arg;
+
+	ui_entry_destroy(entry);
+}
+
+/** Paint text entry control.
+ *
+ * @param arg Argument (ui_entry_t *)
+ * @return EOK on success or an error code
+ */
+errno_t ui_entry_ctl_paint(void *arg)
+{
+	ui_entry_t *entry = (ui_entry_t *) arg;
+
+	return ui_entry_paint(entry);
+}
+
+/** Delete selected text.
+ *
+ * @param entry Text entry
+ */
+void ui_entry_delete_sel(ui_entry_t *entry)
+{
+	size_t off1;
+	size_t off2;
+
+	off1 = min(entry->sel_start, entry->pos);
+	off2 = max(entry->sel_start, entry->pos);
+
+	memmove(entry->text + off1, entry->text + off2,
+	    str_size(entry->text + off2) + 1);
+
+	entry->pos = off1;
+	entry->sel_start = off1;
+	ui_entry_scroll_update(entry, false);
+	ui_entry_paint(entry);
+}
+
+/** Insert string at cursor position.
+ *
+ * @param entry Text entry
+ * @param str String
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ui_entry_insert_str(ui_entry_t *entry, const char *str)
+{
+	uint8_t tmp;
+	char *ltext = NULL;
+	char *newtext;
+	char *oldtext;
+	int rc;
+
+	/* Do we have a selection? */
+	if (entry->sel_start != entry->pos)
+		ui_entry_delete_sel(entry);
+
+	tmp = entry->text[entry->pos];
+	entry->text[entry->pos] = '\0';
+	ltext = str_dup(entry->text);
+	if (ltext == NULL)
+		return ENOMEM;
+
+	entry->text[entry->pos] = tmp;
+
+	rc = asprintf(&newtext, "%s%s%s", ltext, str, entry->text + entry->pos);
+	if (rc < 0) {
+		free(ltext);
+		return ENOMEM;
+	}
+
+	oldtext = entry->text;
+	entry->text = newtext;
+	entry->pos += str_size(str);
+	free(oldtext);
+	free(ltext);
+
+	entry->sel_start = entry->pos;
+	ui_entry_scroll_update(entry, false);
+	ui_entry_paint(entry);
+
+	return EOK;
+}
+
+/** Delete character before cursor.
+ *
+ * @param entry Text entry
+ */
+void ui_entry_backspace(ui_entry_t *entry)
+{
+	size_t off;
+
+	/* Do we have a selection? */
+	if (entry->sel_start != entry->pos) {
+		ui_entry_delete_sel(entry);
+		return;
+	}
+
+	if (entry->pos == 0)
+		return;
+
+	/* Find offset where character before cursor starts */
+	off = entry->pos;
+	(void) str_decode_reverse(entry->text, &off,
+	    str_size(entry->text));
+
+	memmove(entry->text + off, entry->text + entry->pos,
+	    str_size(entry->text + entry->pos) + 1);
+	entry->pos = off;
+	entry->sel_start = off;
+
+	ui_entry_scroll_update(entry, false);
+	ui_entry_paint(entry);
+}
+
+/** Delete character after cursor.
+ *
+ * @param entry Text entry
+ */
+void ui_entry_delete(ui_entry_t *entry)
+{
+	size_t off;
+
+	/* Do we have a selection? */
+	if (entry->sel_start != entry->pos) {
+		ui_entry_delete_sel(entry);
+		return;
+	}
+
+	/* Find offset where character after cursor ends */
+	off = entry->pos;
+	(void) str_decode(entry->text, &off,
+	    str_size(entry->text));
+
+	memmove(entry->text + entry->pos, entry->text + off,
+	    str_size(entry->text + off) + 1);
+
+	ui_entry_scroll_update(entry, false);
+	ui_entry_paint(entry);
+}
+
+/** Copy selected text to clipboard.
+ *
+ * @param entry Text entry
+ */
+void ui_entry_copy(ui_entry_t *entry)
+{
+	unsigned off1;
+	unsigned off2;
+	char c;
+
+	off1 = min(entry->pos, entry->sel_start);
+	off2 = max(entry->pos, entry->sel_start);
+
+	c = entry->text[off2];
+	entry->text[off2] = '\0';
+
+	(void) clipboard_put_str(entry->text + off1);
+
+	entry->text[off2] = c;
+}
+
+/** Cut selected text to clipboard.
+ *
+ * @param entry Text entry
+ */
+void ui_entry_cut(ui_entry_t *entry)
+{
+	ui_entry_copy(entry);
+	ui_entry_delete_sel(entry);
+}
+
+/** Paste text from clipboard.
+ *
+ * @param entry Text entry
+ */
+void ui_entry_paste(ui_entry_t *entry)
+{
+	char *str;
+	errno_t rc;
+
+	rc = clipboard_get_str(&str);
+	if (rc != EOK)
+		return;
+
+	ui_entry_insert_str(entry, str);
+	free(str);
+}
+
+/** Handle text entry key press without modifiers.
+ *
+ * @param entry Text entry
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_entry_key_press_unmod(ui_entry_t *entry, kbd_event_t *event)
+{
+	assert(event->type == KEY_PRESS);
+
+	switch (event->key) {
+	case KC_BACKSPACE:
+		ui_entry_backspace(entry);
+		break;
+
+	case KC_DELETE:
+		ui_entry_delete(entry);
+		break;
+
+	case KC_ESCAPE:
+		ui_entry_deactivate(entry);
+		break;
+
+	case KC_HOME:
+		ui_entry_seek_start(entry, false);
+		break;
+
+	case KC_END:
+		ui_entry_seek_end(entry, false);
+		break;
+
+	case KC_LEFT:
+		ui_entry_seek_prev_char(entry, false);
+		break;
+
+	case KC_RIGHT:
+		ui_entry_seek_next_char(entry, false);
+		break;
+
+	default:
+		break;
+	}
+
+	return ui_claimed;
+}
+
+/** Handle text entry key press with shift modifier.
+ *
+ * @param entry Text entry
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_entry_key_press_shift(ui_entry_t *entry, kbd_event_t *event)
+{
+	assert(event->type == KEY_PRESS);
+
+	switch (event->key) {
+	case KC_HOME:
+		ui_entry_seek_start(entry, true);
+		break;
+
+	case KC_END:
+		ui_entry_seek_end(entry, true);
+		break;
+
+	case KC_LEFT:
+		ui_entry_seek_prev_char(entry, true);
+		break;
+
+	case KC_RIGHT:
+		ui_entry_seek_next_char(entry, true);
+		break;
+
+	default:
+		break;
+	}
+
+	return ui_claimed;
+}
+
+/** Handle text entry key press with control modifier.
+ *
+ * @param entry Text entry
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_entry_key_press_ctrl(ui_entry_t *entry, kbd_event_t *event)
+{
+	assert(event->type == KEY_PRESS);
+
+	switch (event->key) {
+	case KC_C:
+		ui_entry_copy(entry);
+		break;
+	case KC_V:
+		ui_entry_paste(entry);
+		break;
+	case KC_X:
+		ui_entry_cut(entry);
+		break;
+	default:
+		break;
+	}
+
+	return ui_claimed;
+}
+
+/** Handle text entry keyboard event.
+ *
+ * @param entry Text entry
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_entry_kbd_event(ui_entry_t *entry, kbd_event_t *event)
+{
+	char buf[STR_BOUNDS(1) + 1];
+	size_t off;
+	errno_t rc;
+
+	if (!entry->active)
+		return ui_unclaimed;
+
+	if (event->type == KEY_PRESS && event->c >= ' ') {
+		off = 0;
+		rc = chr_encode(event->c, buf, &off, sizeof(buf));
+		if (rc == EOK) {
+			buf[off] = '\0';
+			(void) ui_entry_insert_str(entry, buf);
+		}
+	}
+
+	/*
+	 * Need to keep track if any shift is held for the case
+	 * of selecting by shift-click. This could be simplified
+	 * if position events were decorated with modifier
+	 * state.
+	 */
+
+	if (event->type == KEY_PRESS && event->key == KC_LSHIFT)
+		entry->lshift_held = true;
+	if (event->type == KEY_RELEASE && event->key == KC_LSHIFT)
+		entry->lshift_held = false;
+	if (event->type == KEY_PRESS && event->key == KC_RSHIFT)
+		entry->rshift_held = true;
+	if (event->type == KEY_RELEASE && event->key == KC_RSHIFT)
+		entry->rshift_held = false;
+
+	if (event->type == KEY_PRESS &&
+	    (event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
+		return ui_entry_key_press_unmod(entry, event);
+
+	if (event->type == KEY_PRESS &&
+	    (event->mods & KM_SHIFT) != 0 &&
+	    (event->mods & (KM_CTRL | KM_ALT)) == 0)
+		return ui_entry_key_press_shift(entry, event);
+
+	if (event->type == KEY_PRESS &&
+	    (event->mods & KM_CTRL) != 0 &&
+	    (event->mods & (KM_ALT | KM_SHIFT)) == 0)
+		return ui_entry_key_press_ctrl(entry, event);
+
+	return ui_claimed;
+}
+
+/** Handle text entry position event.
+ *
+ * @param entry Text entry
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event)
+{
+	gfx_coord2_t pos;
+
+	if (entry->read_only)
+		return ui_unclaimed;
+
+	if (event->type == POS_UPDATE) {
+		pos.x = event->hpos;
+		pos.y = event->vpos;
+
+		/* Change cursor shape when pointer is entering/leaving */
+		if (gfx_pix_inside_rect(&pos, &entry->rect)) {
+			if (!entry->pointer_inside) {
+				ui_window_set_ctl_cursor(entry->window,
+				    ui_curs_ibeam);
+				entry->pointer_inside = true;
+			}
+		} else {
+			if (entry->pointer_inside) {
+				ui_window_set_ctl_cursor(entry->window,
+				    ui_curs_arrow);
+				entry->pointer_inside = false;
+			}
+		}
+
+		if (entry->held) {
+			/*
+			 * Selecting using mouse drag: Change pos,
+			 * keep sel_start
+			 */
+			entry->pos = ui_entry_find_pos(entry, &pos);
+			ui_entry_paint(entry);
+		}
+	}
+
+	if (event->type == POS_PRESS) {
+		pos.x = event->hpos;
+		pos.y = event->vpos;
+
+		if (gfx_pix_inside_rect(&pos, &entry->rect)) {
+			/* Clicked inside - activate, set position */
+			entry->held = true;
+			entry->pos = ui_entry_find_pos(entry, &pos);
+
+			/* Clear selection if no shift key is held */
+			if (!entry->lshift_held && !entry->rshift_held)
+				entry->sel_start = entry->pos;
+
+			if (entry->active)
+				ui_entry_paint(entry);
+			else
+				ui_entry_activate(entry);
+
+			return ui_claimed;
+		} else {
+			/* Clicked outside - deactivate */
+			ui_entry_deactivate(entry);
+		}
+	}
+
+	if (event->type == POS_RELEASE) {
+		entry->held = false;
+	}
+
+	return ui_unclaimed;
+}
+
+/** Handle text entry control keyboard event.
+ *
+ * @param arg Argument (ui_entry_t *)
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+static ui_evclaim_t ui_entry_ctl_kbd_event(void *arg, kbd_event_t *event)
+{
+	ui_entry_t *entry = (ui_entry_t *) arg;
+
+	return ui_entry_kbd_event(entry, event);
+}
+
+/** Handle text entry control position event.
+ *
+ * @param arg Argument (ui_entry_t *)
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+static ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
+{
+	ui_entry_t *entry = (ui_entry_t *) arg;
+
+	return ui_entry_pos_event(entry, event);
+}
+
+/** Get text entry geometry.
+ *
+ * @param entry Text entry
+ * @param geom Structure to fill in with computed geometry
+ */
+void ui_entry_get_geom(ui_entry_t *entry, ui_entry_geom_t *geom)
+{
+	gfx_coord_t hpad;
+	gfx_coord_t vpad;
+	ui_resource_t *res;
+
+	res = ui_window_get_res(entry->window);
+
+	if (res->textmode) {
+		hpad = ui_entry_hpad_text;
+		vpad = ui_entry_vpad_text;
+	} else {
+		hpad = ui_entry_hpad;
+		vpad = ui_entry_vpad;
+	}
+
+	if (res->textmode == false) {
+		ui_paint_get_inset_frame_inside(res, &entry->rect,
+		    &geom->interior_rect);
+	} else {
+		geom->interior_rect = entry->rect;
+	}
+
+	geom->text_rect.p0.x = geom->interior_rect.p0.x + hpad;
+	geom->text_rect.p0.y = geom->interior_rect.p0.y + vpad;
+	geom->text_rect.p1.x = geom->interior_rect.p1.x - hpad;
+	geom->text_rect.p1.y = geom->interior_rect.p1.y - vpad;
+
+	geom->text_pos.x = geom->interior_rect.p0.x + hpad +
+	    entry->scroll_pos;
+	geom->text_pos.y = geom->interior_rect.p0.y + vpad;
 
 	switch (entry->halign) {
 	case gfx_halign_left:
 	case gfx_halign_justify:
-		pos.x = inside.p0.x + ui_entry_hpad;
+		geom->anchor_x = geom->text_rect.p0.x;
 		break;
 	case gfx_halign_center:
-		pos.x = (inside.p0.x + inside.p1.x) / 2;
+		geom->anchor_x = (geom->text_rect.p0.x +
+		    geom->text_rect.p1.x) / 2;
 		break;
 	case gfx_halign_right:
-		pos.x = inside.p1.x - ui_entry_hpad;
-		break;
-	}
-
-	pos.y = inside.p0.y + ui_entry_vpad;
-
-	gfx_text_fmt_init(&fmt);
-	fmt.halign = entry->halign;
-	fmt.valign = gfx_valign_top;
-
-	rc = gfx_set_color(entry->res->gc, entry->res->entry_fg_color);
-	if (rc != EOK)
-		goto error;
-
-	rc = gfx_puttext(entry->res->font, &pos, &fmt, entry->text);
-	if (rc != EOK)
-		goto error;
-
-	return EOK;
-error:
-	return rc;
-}
-
-/** Destroy text entry control.
- *
- * @param arg Argument (ui_entry_t *)
- */
-void ui_entry_ctl_destroy(void *arg)
-{
-	ui_entry_t *entry = (ui_entry_t *) arg;
-
-	ui_entry_destroy(entry);
-}
-
-/** Paint text entry control.
- *
- * @param arg Argument (ui_entry_t *)
- * @return EOK on success or an error code
- */
-errno_t ui_entry_ctl_paint(void *arg)
-{
-	ui_entry_t *entry = (ui_entry_t *) arg;
-
-	return ui_entry_paint(entry);
-}
-
-/** Handle text entry control position event.
- *
- * @param arg Argument (ui_entry_t *)
- * @param pos_event Position event
- * @return @c ui_claimed iff the event is claimed
- */
-ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
-{
-	ui_entry_t *entry = (ui_entry_t *) arg;
-
-	(void) entry;
-	return ui_unclaimed;
+		geom->anchor_x = geom->text_rect.p1.x;
+		break;
+	}
+}
+
+/** Activate text entry.
+ *
+ * @param entry Text entry
+ */
+void ui_entry_activate(ui_entry_t *entry)
+{
+	ui_resource_t *res;
+
+	res = ui_window_get_res(entry->window);
+
+	if (entry->active)
+		return;
+
+	entry->active = true;
+	(void) ui_entry_paint(entry);
+
+	if (res->textmode)
+		gfx_cursor_set_visible(res->gc, true);
+}
+
+/** Move text cursor to the beginning of text.
+ *
+ * @param entry Text entry
+ * @param shift @c true iff shift key is pressed
+ */
+void ui_entry_seek_start(ui_entry_t *entry, bool shift)
+{
+	entry->pos = 0;
+
+	if (!shift)
+		entry->sel_start = entry->pos;
+
+	ui_entry_scroll_update(entry, false);
+	(void) ui_entry_paint(entry);
+}
+
+/** Move text cursor to the end of text.
+ *
+ * @param entry Text entry
+ * @param shift @c true iff shift key is pressed
+ */
+void ui_entry_seek_end(ui_entry_t *entry, bool shift)
+{
+	entry->pos = str_size(entry->text);
+
+	if (!shift)
+		entry->sel_start = entry->pos;
+
+	ui_entry_scroll_update(entry, false);
+	(void) ui_entry_paint(entry);
+}
+
+/** Move text cursor one character backward.
+ *
+ * @param entry Text entry
+ * @param shift @c true iff shift key is pressed
+ */
+void ui_entry_seek_prev_char(ui_entry_t *entry, bool shift)
+{
+	size_t off;
+
+	off = entry->pos;
+	(void) str_decode_reverse(entry->text, &off,
+	    str_size(entry->text));
+	entry->pos = off;
+
+	if (!shift)
+		entry->sel_start = entry->pos;
+
+	ui_entry_scroll_update(entry, false);
+	(void) ui_entry_paint(entry);
+}
+
+/** Move text cursor one character forward.
+ *
+ * @param entry Text entry
+ * @param shift @c true iff shift key is pressed
+ */
+void ui_entry_seek_next_char(ui_entry_t *entry, bool shift)
+{
+	size_t off;
+
+	off = entry->pos;
+	(void) str_decode(entry->text, &off,
+	    str_size(entry->text));
+	entry->pos = off;
+
+	if (!shift)
+		entry->sel_start = entry->pos;
+
+	ui_entry_scroll_update(entry, false);
+	(void) ui_entry_paint(entry);
+}
+
+/** Deactivate text entry.
+ *
+ * @param entry Text entry
+ */
+void ui_entry_deactivate(ui_entry_t *entry)
+{
+	ui_resource_t *res;
+
+	res = ui_window_get_res(entry->window);
+
+	if (!entry->active)
+		return;
+
+	entry->active = false;
+	entry->sel_start = entry->pos;
+	(void) ui_entry_paint(entry);
+
+	if (res->textmode)
+		gfx_cursor_set_visible(res->gc, false);
+}
+
+/** Update text entry scroll position.
+ *
+ * @param entry Text entry
+ * @param realign @c true iff we should left-align short text.
+ *                This should be only used when changing text alignment,
+ *                because left-aligned text entries should not realign
+ *                the text to the left side under normal circumstances.
+ */
+void ui_entry_scroll_update(ui_entry_t *entry, bool realign)
+{
+	ui_entry_geom_t geom;
+	gfx_coord_t x;
+	gfx_coord_t width;
+	gfx_coord2_t tpos;
+	gfx_coord2_t anchor;
+	gfx_text_fmt_t fmt;
+	ui_resource_t *res;
+
+	res = ui_window_get_res(entry->window);
+
+	ui_entry_get_geom(entry, &geom);
+
+	/* Compute position where cursor is currently displayed at */
+	x = geom.text_pos.x + ui_entry_lwidth(entry);
+
+	/* Is cursor off to the left? */
+	if (x < geom.text_rect.p0.x) {
+		/*
+		 * Scroll to make cursor visible and put some space between it
+		 * and the left edge of the text rectangle.
+		 */
+		entry->scroll_pos += geom.text_rect.p0.x - x +
+		    ui_entry_left_scroll_margin;
+
+		/*
+		 * We don't want to scroll further than what's needed
+		 * to reveal the beginning of the text.
+		 */
+		if (entry->scroll_pos > 0)
+			entry->scroll_pos = 0;
+	}
+
+	/*
+	 * Is cursor off to the right? Note that the width of the cursor
+	 * is deliberately not taken into account (i.e. we only care
+	 * about the left edge of the cursor).
+	 */
+	if (x > geom.text_rect.p1.x)
+		entry->scroll_pos -= x - geom.text_rect.p1.x;
+
+	width = gfx_text_width(res->font, entry->text);
+
+	if (width < geom.text_rect.p1.x - geom.text_rect.p0.x &&
+	    (realign || entry->halign != gfx_halign_left)) {
+		/* Text fits inside entry, so we need to align it */
+		anchor.x = geom.anchor_x;
+		anchor.y = 0;
+		gfx_text_fmt_init(&fmt);
+		fmt.halign = entry->halign;
+		gfx_text_start_pos(res->font, &anchor, &fmt, entry->text,
+		    &tpos);
+		entry->scroll_pos = tpos.x - geom.text_rect.p0.x;
+	} else if (geom.text_pos.x + width < geom.text_rect.p1.x &&
+	    entry->halign != gfx_halign_left) {
+		/* Text is long, unused space on the right */
+		entry->scroll_pos += geom.text_rect.p1.x -
+		    geom.text_pos.x - width;
+	}
 }
 
Index: uspace/lib/ui/src/filedialog.c
===================================================================
--- uspace/lib/ui/src/filedialog.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/src/filedialog.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file File dialog
+ */
+
+#include <errno.h>
+#include <mem.h>
+#include <stdlib.h>
+#include <ui/entry.h>
+#include <ui/fixed.h>
+#include <ui/label.h>
+#include <ui/filedialog.h>
+#include <ui/pbutton.h>
+#include <ui/resource.h>
+#include <ui/ui.h>
+#include <ui/window.h>
+#include "../private/filedialog.h"
+
+static void ui_file_dialog_wnd_close(ui_window_t *, void *);
+
+ui_window_cb_t ui_file_dialog_wnd_cb = {
+	.close = ui_file_dialog_wnd_close
+};
+
+static void ui_file_dialog_bok_clicked(ui_pbutton_t *, void *);
+static void ui_file_dialog_bcancel_clicked(ui_pbutton_t *, void *);
+
+ui_pbutton_cb_t ui_file_dialog_bok_cb = {
+	.clicked = ui_file_dialog_bok_clicked
+};
+
+ui_pbutton_cb_t ui_file_dialog_bcancel_cb = {
+	.clicked = ui_file_dialog_bcancel_clicked
+};
+
+/** Initialize file dialog parameters structure.
+ *
+ * File dialog parameters structure must always be initialized using
+ * this function first.
+ *
+ * @param params File dialog parameters structure
+ */
+void ui_file_dialog_params_init(ui_file_dialog_params_t *params)
+{
+	memset(params, 0, sizeof(ui_file_dialog_params_t));
+}
+
+/** Create new file dialog.
+ *
+ * @param ui User interface
+ * @param params File dialog parameters
+ * @param rdialog Place to store pointer to new dialog
+ * @return EOK on success or an error code
+ */
+errno_t ui_file_dialog_create(ui_t *ui, ui_file_dialog_params_t *params,
+    ui_file_dialog_t **rdialog)
+{
+	errno_t rc;
+	ui_file_dialog_t *dialog;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t wparams;
+	ui_fixed_t *fixed = NULL;
+	ui_label_t *label = NULL;
+	ui_entry_t *entry = NULL;
+	ui_pbutton_t *bok = NULL;
+	ui_pbutton_t *bcancel = NULL;
+	gfx_rect_t rect;
+	ui_resource_t *ui_res;
+
+	dialog = calloc(1, sizeof(ui_file_dialog_t));
+	if (dialog == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ui_wnd_params_init(&wparams);
+	wparams.caption = params->caption;
+
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		wparams.rect.p0.x = 0;
+		wparams.rect.p0.y = 0;
+		wparams.rect.p1.x = 40;
+		wparams.rect.p1.y = 10;
+	} else {
+		wparams.rect.p0.x = 0;
+		wparams.rect.p0.y = 0;
+		wparams.rect.p1.x = 300;
+		wparams.rect.p1.y = 135;
+	}
+
+	rc = ui_window_create(ui, &wparams, &window);
+	if (rc != EOK)
+		goto error;
+
+	ui_window_set_cb(window, &ui_file_dialog_wnd_cb, dialog);
+
+	ui_res = ui_window_get_res(window);
+
+	rc = ui_fixed_create(&fixed);
+	if (rc != EOK)
+		goto error;
+
+	rc = ui_label_create(ui_res, "File Name:", &label);
+	if (rc != EOK)
+		goto error;
+
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		rect.p0.x = 3;
+		rect.p0.y = 3;
+		rect.p1.x = 17;
+		rect.p1.y = 4;
+	} else {
+		rect.p0.x = 10;
+		rect.p0.y = 35;
+		rect.p1.x = 190;
+		rect.p1.y = 50;
+	}
+
+	ui_label_set_rect(label, &rect);
+
+	rc = ui_fixed_add(fixed, ui_label_ctl(label));
+	if (rc != EOK)
+		goto error;
+
+	label = NULL;
+
+	rc = ui_entry_create(window, "", &entry);
+	if (rc != EOK)
+		goto error;
+
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		rect.p0.x = 3;
+		rect.p0.y = 5;
+		rect.p1.x = 37;
+		rect.p1.y = 6;
+	} else {
+		rect.p0.x = 10;
+		rect.p0.y = 55;
+		rect.p1.x = 290;
+		rect.p1.y = 80;
+	}
+
+	ui_entry_set_rect(entry, &rect);
+
+	rc = ui_fixed_add(fixed, ui_entry_ctl(entry));
+	if (rc != EOK)
+		goto error;
+
+	dialog->ename = entry;
+	entry = NULL;
+
+	rc = ui_pbutton_create(ui_res, "OK", &bok);
+	if (rc != EOK)
+		goto error;
+
+	ui_pbutton_set_cb(bok, &ui_file_dialog_bok_cb, dialog);
+
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		rect.p0.x = 10;
+		rect.p0.y = 7;
+		rect.p1.x = 20;
+		rect.p1.y = 8;
+	} else {
+		rect.p0.x = 55;
+		rect.p0.y = 90;
+		rect.p1.x = 145;
+		rect.p1.y = 118;
+	}
+
+	ui_pbutton_set_rect(bok, &rect);
+
+	ui_pbutton_set_default(bok, true);
+
+	rc = ui_fixed_add(fixed, ui_pbutton_ctl(bok));
+	if (rc != EOK)
+		goto error;
+
+	dialog->bok = bok;
+	bok = NULL;
+
+	rc = ui_pbutton_create(ui_res, "Cancel", &bcancel);
+	if (rc != EOK)
+		goto error;
+
+	ui_pbutton_set_cb(bcancel, &ui_file_dialog_bcancel_cb, dialog);
+
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		rect.p0.x = 22;
+		rect.p0.y = 7;
+		rect.p1.x = 32;
+		rect.p1.y = 8;
+	} else {
+		rect.p0.x = 155;
+		rect.p0.y = 90;
+		rect.p1.x = 245;
+		rect.p1.y = 118;
+	}
+
+	ui_pbutton_set_rect(bcancel, &rect);
+
+	rc = ui_fixed_add(fixed, ui_pbutton_ctl(bcancel));
+	if (rc != EOK)
+		goto error;
+
+	dialog->bcancel = bcancel;
+	bcancel = NULL;
+
+	ui_window_add(window, ui_fixed_ctl(fixed));
+	fixed = NULL;
+
+	rc = ui_window_paint(window);
+	if (rc != EOK)
+		goto error;
+
+	dialog->window = window;
+	*rdialog = dialog;
+	return EOK;
+error:
+	if (entry != NULL)
+		ui_entry_destroy(entry);
+	if (bok != NULL)
+		ui_pbutton_destroy(bok);
+	if (bcancel != NULL)
+		ui_pbutton_destroy(bcancel);
+	if (label != NULL)
+		ui_label_destroy(label);
+	if (fixed != NULL)
+		ui_fixed_destroy(fixed);
+	if (window != NULL)
+		ui_window_destroy(window);
+	if (dialog != NULL)
+		free(dialog);
+	return rc;
+}
+
+/** Destroy file dialog.
+ *
+ * @param dialog File dialog or @c NULL
+ */
+void ui_file_dialog_destroy(ui_file_dialog_t *dialog)
+{
+	if (dialog == NULL)
+		return;
+
+	ui_window_destroy(dialog->window);
+	free(dialog);
+}
+
+/** Set mesage dialog callback.
+ *
+ * @param dialog File dialog
+ * @param cb File dialog callbacks
+ * @param arg Callback argument
+ */
+void ui_file_dialog_set_cb(ui_file_dialog_t *dialog, ui_file_dialog_cb_t *cb,
+    void *arg)
+{
+	dialog->cb = cb;
+	dialog->arg = arg;
+}
+
+/** File dialog window close handler.
+ *
+ * @param window Window
+ * @param arg Argument (ui_file_dialog_t *)
+ */
+static void ui_file_dialog_wnd_close(ui_window_t *window, void *arg)
+{
+	ui_file_dialog_t *dialog = (ui_file_dialog_t *) arg;
+
+	if (dialog->cb != NULL && dialog->cb->close != NULL)
+		dialog->cb->close(dialog, dialog->arg);
+}
+
+/** File dialog OK button click handler.
+ *
+ * @param pbutton Push button
+ * @param arg Argument (ui_file_dialog_t *)
+ */
+static void ui_file_dialog_bok_clicked(ui_pbutton_t *pbutton, void *arg)
+{
+	ui_file_dialog_t *dialog = (ui_file_dialog_t *) arg;
+	const char *fname;
+
+	if (dialog->cb != NULL && dialog->cb->bok != NULL) {
+		fname = ui_entry_get_text(dialog->ename);
+		dialog->cb->bok(dialog, dialog->arg, fname);
+	}
+}
+
+/** File dialog cancel button click handler.
+ *
+ * @param pbutton Push button
+ * @param arg Argument (ui_file_dialog_t *)
+ */
+static void ui_file_dialog_bcancel_clicked(ui_pbutton_t *pbutton, void *arg)
+{
+	ui_file_dialog_t *dialog = (ui_file_dialog_t *) arg;
+
+	if (dialog->cb != NULL && dialog->cb->bcancel != NULL)
+		dialog->cb->bcancel(dialog, dialog->arg);
+}
+
+/** @}
+ */
Index: uspace/lib/ui/src/fixed.c
===================================================================
--- uspace/lib/ui/src/fixed.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/src/fixed.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -46,5 +46,7 @@
 static void ui_fixed_ctl_destroy(void *);
 static errno_t ui_fixed_ctl_paint(void *);
+static ui_evclaim_t ui_fixed_ctl_kbd_event(void *, kbd_event_t *);
 static ui_evclaim_t ui_fixed_ctl_pos_event(void *, pos_event_t *);
+static void ui_fixed_ctl_unfocus(void *);
 
 /** Push button control ops */
@@ -52,5 +54,7 @@
 	.destroy = ui_fixed_ctl_destroy,
 	.paint = ui_fixed_ctl_paint,
-	.pos_event = ui_fixed_ctl_pos_event
+	.kbd_event = ui_fixed_ctl_kbd_event,
+	.pos_event = ui_fixed_ctl_pos_event,
+	.unfocus = ui_fixed_ctl_unfocus
 };
 
@@ -209,4 +213,27 @@
 }
 
+/** Handle fixed layout keyboard event.
+ *
+ * @param fixed Fixed layout
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_fixed_kbd_event(ui_fixed_t *fixed, kbd_event_t *event)
+{
+	ui_fixed_elem_t *elem;
+	ui_evclaim_t claimed;
+
+	elem = ui_fixed_first(fixed);
+	while (elem != NULL) {
+		claimed = ui_control_kbd_event(elem->control, event);
+		if (claimed == ui_claimed)
+			return ui_claimed;
+
+		elem = ui_fixed_next(elem);
+	}
+
+	return ui_unclaimed;
+}
+
 /** Handle fixed layout position event.
  *
@@ -232,4 +259,20 @@
 }
 
+/** Handle fixed layout window unfocus notification.
+ *
+ * @param fixed Fixed layout
+ */
+void ui_fixed_unfocus(ui_fixed_t *fixed)
+{
+	ui_fixed_elem_t *elem;
+
+	elem = ui_fixed_first(fixed);
+	while (elem != NULL) {
+		ui_control_unfocus(elem->control);
+
+		elem = ui_fixed_next(elem);
+	}
+}
+
 /** Destroy fixed layout control.
  *
@@ -253,4 +296,17 @@
 
 	return ui_fixed_paint(fixed);
+}
+
+/** Handle fixed layout control keyboard event.
+ *
+ * @param arg Argument (ui_fixed_t *)
+ * @param kbd_event Keyboard event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_fixed_ctl_kbd_event(void *arg, kbd_event_t *event)
+{
+	ui_fixed_t *fixed = (ui_fixed_t *) arg;
+
+	return ui_fixed_kbd_event(fixed, event);
 }
 
@@ -268,4 +324,15 @@
 }
 
+/** Handle fixed layout control window unfocus notification.
+ *
+ * @param arg Argument (ui_fixed_t *)
+ */
+void ui_fixed_ctl_unfocus(void *arg)
+{
+	ui_fixed_t *fixed = (ui_fixed_t *) arg;
+
+	ui_fixed_unfocus(fixed);
+}
+
 /** @}
  */
Index: uspace/lib/ui/src/image.c
===================================================================
--- uspace/lib/ui/src/image.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/src/image.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -37,4 +37,5 @@
 #include <gfx/bitmap.h>
 #include <gfx/context.h>
+#include <gfx/render.h>
 #include <gfx/text.h>
 #include <stdlib.h>
@@ -177,6 +178,9 @@
 	 */
 	gfx_rect_rtranslate(&offs, &irect, &srect);
-	return gfx_bitmap_render(image->bitmap, &srect, &offs);
-
+	rc = gfx_bitmap_render(image->bitmap, &srect, &offs);
+	if (rc != EOK)
+		return rc;
+
+	return gfx_update(image->res->gc);
 }
 
Index: uspace/lib/ui/src/label.c
===================================================================
--- uspace/lib/ui/src/label.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/src/label.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -193,12 +193,13 @@
 
 	gfx_text_fmt_init(&fmt);
+	fmt.color = label->res->wnd_text_color;
 	fmt.halign = label->halign;
 	fmt.valign = gfx_valign_top;
 
-	rc = gfx_set_color(label->res->gc, label->res->wnd_text_color);
-	if (rc != EOK)
-		goto error;
-
 	rc = gfx_puttext(label->res->font, &pos, &fmt, label->text);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_update(label->res->gc);
 	if (rc != EOK)
 		goto error;
Index: uspace/lib/ui/src/menu.c
===================================================================
--- uspace/lib/ui/src/menu.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/src/menu.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Menu
+ */
+
+#include <adt/list.h>
+#include <errno.h>
+#include <gfx/color.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <gfx/text.h>
+#include <io/pos_event.h>
+#include <stdlib.h>
+#include <str.h>
+#include <ui/control.h>
+#include <ui/paint.h>
+#include <ui/popup.h>
+#include <ui/menu.h>
+#include <ui/menuentry.h>
+#include <ui/resource.h>
+#include <ui/window.h>
+#include "../private/menubar.h"
+#include "../private/menu.h"
+#include "../private/resource.h"
+
+enum {
+	menu_frame_w = 4,
+	menu_frame_h = 4,
+	menu_frame_w_text = 2,
+	menu_frame_h_text = 1
+};
+
+static void ui_menu_popup_close(ui_popup_t *, void *);
+static void ui_menu_popup_pos(ui_popup_t *, void *, pos_event_t *);
+
+static ui_popup_cb_t ui_menu_popup_cb = {
+	.close = ui_menu_popup_close,
+	.pos = ui_menu_popup_pos
+};
+
+/** Create new menu.
+ *
+ * @param mbar Menu bar
+ * @param caption Caption
+ * @param rmenu Place to store pointer to new menu
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ui_menu_create(ui_menu_bar_t *mbar, const char *caption,
+    ui_menu_t **rmenu)
+{
+	ui_menu_t *menu;
+
+	menu = calloc(1, sizeof(ui_menu_t));
+	if (menu == NULL)
+		return ENOMEM;
+
+	menu->caption = str_dup(caption);
+	if (menu->caption == NULL) {
+		free(menu);
+		return ENOMEM;
+	}
+
+	menu->mbar = mbar;
+	list_append(&menu->lmenus, &mbar->menus);
+	list_initialize(&menu->entries);
+
+	*rmenu = menu;
+	return EOK;
+}
+
+/** Destroy menu.
+ *
+ * @param menu Menu or @c NULL
+ */
+void ui_menu_destroy(ui_menu_t *menu)
+{
+	ui_menu_entry_t *mentry;
+
+	if (menu == NULL)
+		return;
+
+	/* Destroy entries */
+	mentry = ui_menu_entry_first(menu);
+	while (mentry != NULL) {
+		ui_menu_entry_destroy(mentry);
+		mentry = ui_menu_entry_first(menu);
+	}
+
+	list_remove(&menu->lmenus);
+	free(menu->caption);
+	free(menu);
+}
+
+/** Get first menu in menu bar.
+ *
+ * @param mbar Menu bar
+ * @return First menu or @c NULL if there is none
+ */
+ui_menu_t *ui_menu_first(ui_menu_bar_t *mbar)
+{
+	link_t *link;
+
+	link = list_first(&mbar->menus);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ui_menu_t, lmenus);
+}
+
+/** Get next menu in menu bar.
+ *
+ * @param cur Current menu
+ * @return Next menu or @c NULL if @a cur is the last one
+ */
+ui_menu_t *ui_menu_next(ui_menu_t *cur)
+{
+	link_t *link;
+
+	link = list_next(&cur->lmenus, &cur->mbar->menus);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ui_menu_t, lmenus);
+}
+
+/** Get menu caption.
+ *
+ * @param menu Menu
+ * @return Caption (owned by @a menu)
+ */
+const char *ui_menu_caption(ui_menu_t *menu)
+{
+	return menu->caption;
+}
+
+/** Get menu geometry.
+ *
+ * @param menu Menu
+ * @param spos Starting position
+ * @param geom Structure to fill in with computed geometry
+ */
+void ui_menu_get_geom(ui_menu_t *menu, gfx_coord2_t *spos,
+    ui_menu_geom_t *geom)
+{
+	ui_resource_t *res;
+	gfx_coord2_t edim;
+	gfx_coord_t frame_w;
+	gfx_coord_t frame_h;
+
+	res = ui_window_get_res(menu->mbar->window);
+
+	if (res->textmode) {
+		frame_w = menu_frame_w_text;
+		frame_h = menu_frame_h_text;
+	} else {
+		frame_w = menu_frame_w;
+		frame_h = menu_frame_h;
+	}
+
+	edim.x = ui_menu_entry_calc_width(menu, menu->max_caption_w,
+	    menu->max_shortcut_w);
+	edim.y = menu->total_h;
+
+	geom->outer_rect.p0 = *spos;
+	geom->outer_rect.p1.x = spos->x + edim.x + 2 * frame_w;
+	geom->outer_rect.p1.y = spos->y + edim.y + 2 * frame_h;
+
+	geom->entries_rect.p0.x = spos->x + frame_w;
+	geom->entries_rect.p0.y = spos->y + frame_h;
+	geom->entries_rect.p1.x = geom->entries_rect.p0.x + edim.x;
+	geom->entries_rect.p1.y = geom->entries_rect.p0.x + edim.y;
+}
+
+/** Get menu rectangle.
+ *
+ * @param menu Menu
+ * @param spos Starting position (top-left corner)
+ * @param rect Place to store menu rectangle
+ */
+void ui_menu_get_rect(ui_menu_t *menu, gfx_coord2_t *spos, gfx_rect_t *rect)
+{
+	ui_menu_geom_t geom;
+
+	ui_menu_get_geom(menu, spos, &geom);
+	*rect = geom.outer_rect;
+}
+
+/** Get UI resource from menu.
+ *
+ * @param menu Menu
+ * @return UI resource
+ */
+ui_resource_t *ui_menu_get_res(ui_menu_t *menu)
+{
+	return ui_popup_get_res(menu->popup);
+}
+
+/** Open menu.
+ *
+ * @param menu Menu
+ * @param prect Parent rectangle around which the menu should be placed
+ */
+errno_t ui_menu_open(ui_menu_t *menu, gfx_rect_t *prect)
+{
+	ui_popup_t *popup = NULL;
+	ui_popup_params_t params;
+	ui_menu_geom_t geom;
+	gfx_coord2_t mpos;
+	errno_t rc;
+
+	/* Determine menu dimensions */
+
+	mpos.x = 0;
+	mpos.y = 0;
+	ui_menu_get_geom(menu, &mpos, &geom);
+
+	ui_popup_params_init(&params);
+	params.rect = geom.outer_rect;
+	params.place = *prect;
+
+	rc = ui_popup_create(menu->mbar->ui, menu->mbar->window, &params,
+	    &popup);
+	if (rc != EOK)
+		return rc;
+
+	menu->popup = popup;
+	ui_popup_set_cb(popup, &ui_menu_popup_cb, menu);
+
+	return ui_menu_paint(menu, &mpos);
+}
+
+/** Close menu.
+ *
+ * @param menu Menu
+ */
+void ui_menu_close(ui_menu_t *menu)
+{
+	ui_popup_destroy(menu->popup);
+	menu->popup = NULL;
+}
+
+/** Paint menu.
+ *
+ * @param menu Menu
+ * @param spos Starting position (top-left corner)
+ * @return EOK on success or an error code
+ */
+errno_t ui_menu_paint(ui_menu_t *menu, gfx_coord2_t *spos)
+{
+	ui_resource_t *res;
+	gfx_coord2_t pos;
+	ui_menu_entry_t *mentry;
+	ui_menu_geom_t geom;
+	gfx_rect_t bg_rect;
+	errno_t rc;
+
+	res = ui_menu_get_res(menu);
+	ui_menu_get_geom(menu, spos, &geom);
+
+	/* Paint menu frame */
+
+	rc = gfx_set_color(res->gc, res->wnd_face_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = ui_paint_outset_frame(res, &geom.outer_rect, &bg_rect);
+	if (rc != EOK)
+		goto error;
+
+	/* Paint menu background */
+
+	rc = gfx_set_color(res->gc, res->wnd_face_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(res->gc, &bg_rect);
+	if (rc != EOK)
+		goto error;
+
+	/* Paint entries */
+	pos = geom.entries_rect.p0;
+
+	mentry = ui_menu_entry_first(menu);
+	while (mentry != NULL) {
+		rc = ui_menu_entry_paint(mentry, &pos);
+		if (rc != EOK)
+			goto error;
+
+		pos.y += ui_menu_entry_height(mentry);
+		mentry = ui_menu_entry_next(mentry);
+	}
+
+	rc = gfx_update(res->gc);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Handle position event in menu.
+ *
+ * @param menu Menu
+ * @param spos Starting position (top-left corner)
+ * @param event Position event
+ * @return ui_claimed iff the event was claimed
+ */
+ui_evclaim_t ui_menu_pos_event(ui_menu_t *menu, gfx_coord2_t *spos,
+    pos_event_t *event)
+{
+	ui_menu_geom_t geom;
+	ui_menu_entry_t *mentry;
+	gfx_coord2_t pos;
+	gfx_coord2_t epos;
+	ui_evclaim_t claimed;
+
+	ui_menu_get_geom(menu, spos, &geom);
+	epos.x = event->hpos;
+	epos.y = event->vpos;
+
+	pos = geom.entries_rect.p0;
+
+	mentry = ui_menu_entry_first(menu);
+	while (mentry != NULL) {
+		claimed = ui_menu_entry_pos_event(mentry, &pos, event);
+		if (claimed == ui_claimed)
+			return ui_claimed;
+
+		pos.y += ui_menu_entry_height(mentry);
+		mentry = ui_menu_entry_next(mentry);
+	}
+
+	/* Event inside menu? */
+	if (gfx_pix_inside_rect(&epos, &geom.outer_rect)) {
+		/* Claim event */
+		return ui_claimed;
+	} else {
+		/* Press outside menu - close it */
+		if (event->type == POS_PRESS)
+			ui_menu_bar_select(menu->mbar, NULL, NULL);
+	}
+
+	return ui_unclaimed;
+}
+
+/** Handle close event in menu popup window.
+ *
+ * @param popup Menu popup window
+ * @param arg Argument (ui_menu_t *)
+ */
+static void ui_menu_popup_close(ui_popup_t *popup, void *arg)
+{
+	ui_menu_t *menu = (ui_menu_t *)arg;
+
+	/* Close the menu */
+	ui_menu_bar_select(menu->mbar, NULL, NULL);
+}
+
+/** Handle position event in menu popup window.
+ *
+ * @param popup Menu popup window
+ * @param arg Argument (ui_menu_t *)
+ * @param event Position event
+ */
+static void ui_menu_popup_pos(ui_popup_t *popup, void *arg, pos_event_t *event)
+{
+	ui_menu_t *menu = (ui_menu_t *)arg;
+	gfx_coord2_t spos;
+
+	spos.x = 0;
+	spos.y = 0;
+	ui_menu_pos_event(menu, &spos, event);
+}
+
+/** @}
+ */
Index: uspace/lib/ui/src/menubar.c
===================================================================
--- uspace/lib/ui/src/menubar.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/src/menubar.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Menu bar
+ */
+
+#include <adt/list.h>
+#include <errno.h>
+#include <gfx/color.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <gfx/text.h>
+#include <io/pos_event.h>
+#include <stdlib.h>
+#include <str.h>
+#include <ui/control.h>
+#include <ui/paint.h>
+#include <ui/menu.h>
+#include <ui/menubar.h>
+#include <ui/window.h>
+#include "../private/menubar.h"
+#include "../private/resource.h"
+
+enum {
+	menubar_hpad = 4,
+	menubar_vpad = 4,
+	menubar_hpad_text = 1,
+	menubar_vpad_text = 0
+};
+
+static void ui_menu_bar_ctl_destroy(void *);
+static errno_t ui_menu_bar_ctl_paint(void *);
+static ui_evclaim_t ui_menu_bar_ctl_pos_event(void *, pos_event_t *);
+
+/** Menu bar control ops */
+ui_control_ops_t ui_menu_bar_ops = {
+	.destroy = ui_menu_bar_ctl_destroy,
+	.paint = ui_menu_bar_ctl_paint,
+	.pos_event = ui_menu_bar_ctl_pos_event,
+};
+
+/** Create new menu bar.
+ *
+ * @param ui UI
+ * @param window Window that will contain the menu bar
+ * @param rmbar Place to store pointer to new menu bar
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ui_menu_bar_create(ui_t *ui, ui_window_t *window, ui_menu_bar_t **rmbar)
+{
+	ui_menu_bar_t *mbar;
+	errno_t rc;
+
+	mbar = calloc(1, sizeof(ui_menu_bar_t));
+	if (mbar == NULL)
+		return ENOMEM;
+
+	rc = ui_control_new(&ui_menu_bar_ops, (void *) mbar, &mbar->control);
+	if (rc != EOK) {
+		free(mbar);
+		return rc;
+	}
+
+	mbar->ui = ui;
+	mbar->window = window;
+	list_initialize(&mbar->menus);
+	*rmbar = mbar;
+	return EOK;
+}
+
+/** Destroy menu bar
+ *
+ * @param mbar Menu bar or @c NULL
+ */
+void ui_menu_bar_destroy(ui_menu_bar_t *mbar)
+{
+	ui_menu_t *menu;
+
+	if (mbar == NULL)
+		return;
+
+	/* Destroy menus */
+	menu = ui_menu_first(mbar);
+	while (menu != NULL) {
+		ui_menu_destroy(menu);
+		menu = ui_menu_first(mbar);
+	}
+
+	ui_control_delete(mbar->control);
+	free(mbar);
+}
+
+/** Get base control from menu bar.
+ *
+ * @param mbar Menu bar
+ * @return Control
+ */
+ui_control_t *ui_menu_bar_ctl(ui_menu_bar_t *mbar)
+{
+	return mbar->control;
+}
+
+/** Set menu bar rectangle.
+ *
+ * @param mbar Menu bar
+ * @param rect New menu bar rectangle
+ */
+void ui_menu_bar_set_rect(ui_menu_bar_t *mbar, gfx_rect_t *rect)
+{
+	mbar->rect = *rect;
+}
+
+/** Paint menu bar.
+ *
+ * @param mbar Menu bar
+ * @return EOK on success or an error code
+ */
+errno_t ui_menu_bar_paint(ui_menu_bar_t *mbar)
+{
+	ui_resource_t *res;
+	gfx_text_fmt_t fmt;
+	gfx_coord2_t pos;
+	gfx_coord2_t tpos;
+	gfx_rect_t rect;
+	gfx_color_t *bg_color;
+	ui_menu_t *menu;
+	const char *caption;
+	gfx_coord_t width;
+	gfx_coord_t hpad;
+	gfx_coord_t vpad;
+	errno_t rc;
+
+	res = ui_window_get_res(mbar->window);
+
+	/* Paint menu bar background */
+
+	rc = gfx_set_color(res->gc, res->wnd_face_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(res->gc, &mbar->rect);
+	if (rc != EOK)
+		goto error;
+
+	if (res->textmode) {
+		hpad = menubar_hpad_text;
+		vpad = menubar_vpad_text;
+	} else {
+		hpad = menubar_hpad;
+		vpad = menubar_vpad;
+	}
+
+	pos = mbar->rect.p0;
+
+	gfx_text_fmt_init(&fmt);
+	fmt.halign = gfx_halign_left;
+	fmt.valign = gfx_valign_top;
+
+	menu = ui_menu_first(mbar);
+	while (menu != NULL) {
+		caption = ui_menu_caption(menu);
+		width = gfx_text_width(res->font, caption) + 2 * hpad;
+		tpos.x = pos.x + hpad;
+		tpos.y = pos.y + vpad;
+
+		rect.p0 = pos;
+		rect.p1.x = rect.p0.x + width;
+		rect.p1.y = mbar->rect.p1.y;
+
+		if (menu == mbar->selected) {
+			fmt.color = res->wnd_sel_text_color;
+			bg_color = res->wnd_sel_text_bg_color;
+		} else {
+			fmt.color = res->wnd_text_color;
+			bg_color = res->wnd_face_color;
+		}
+
+		rc = gfx_set_color(res->gc, bg_color);
+		if (rc != EOK)
+			goto error;
+
+		rc = gfx_fill_rect(res->gc, &rect);
+		if (rc != EOK)
+			goto error;
+
+		rc = gfx_puttext(res->font, &tpos, &fmt, caption);
+		if (rc != EOK)
+			goto error;
+
+		pos.x += width;
+		menu = ui_menu_next(menu);
+	}
+
+	rc = gfx_update(res->gc);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Select or deselect menu from menu bar.
+ *
+ * Select @a menu. If @a menu is @c NULL or it is already selected,
+ * then select none.
+ *
+ * @param mbar Menu bar
+ * @param rect Menu bar entry rectangle
+ * @param menu Menu to select (or deselect if selected) or @c NULL
+ */
+void ui_menu_bar_select(ui_menu_bar_t *mbar, gfx_rect_t *rect,
+    ui_menu_t *menu)
+{
+	ui_menu_t *old_menu;
+
+	old_menu = mbar->selected;
+
+	if (mbar->selected != menu)
+		mbar->selected = menu;
+	else
+		mbar->selected = NULL;
+
+	/* Close previously open menu */
+	if (old_menu != NULL)
+		(void) ui_menu_close(old_menu);
+
+	(void) ui_menu_bar_paint(mbar);
+
+	if (mbar->selected != NULL) {
+		(void) ui_menu_open(mbar->selected, rect);
+	}
+}
+
+/** Handle menu bar position event.
+ *
+ * @param mbar Menu bar
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_menu_bar_pos_event(ui_menu_bar_t *mbar, pos_event_t *event)
+{
+	ui_resource_t *res;
+	gfx_coord2_t pos;
+	gfx_rect_t rect;
+	ui_menu_t *menu;
+	const char *caption;
+	gfx_coord_t width;
+	gfx_coord_t hpad;
+	gfx_coord2_t ppos;
+
+	res = ui_window_get_res(mbar->window);
+
+	ppos.x = event->hpos;
+	ppos.y = event->vpos;
+
+	if (res->textmode) {
+		hpad = menubar_hpad_text;
+	} else {
+		hpad = menubar_hpad;
+	}
+
+	pos = mbar->rect.p0;
+
+	menu = ui_menu_first(mbar);
+	while (menu != NULL) {
+		caption = ui_menu_caption(menu);
+		width = gfx_text_width(res->font, caption) + 2 * hpad;
+
+		rect.p0 = pos;
+		rect.p1.x = rect.p0.x + width;
+		rect.p1.y = mbar->rect.p1.y;
+
+		/* Check if press is inside menu bar entry */
+		if (event->type == POS_PRESS &&
+		    gfx_pix_inside_rect(&ppos, &rect)) {
+			ui_menu_bar_select(mbar, &rect, menu);
+			return ui_claimed;
+		}
+
+		pos.x += width;
+		menu = ui_menu_next(menu);
+	}
+
+	return ui_unclaimed;
+}
+
+/** Destroy menu bar control.
+ *
+ * @param arg Argument (ui_menu_bar_t *)
+ */
+void ui_menu_bar_ctl_destroy(void *arg)
+{
+	ui_menu_bar_t *mbar = (ui_menu_bar_t *) arg;
+
+	ui_menu_bar_destroy(mbar);
+}
+
+/** Paint menu bar control.
+ *
+ * @param arg Argument (ui_menu_bar_t *)
+ * @return EOK on success or an error code
+ */
+errno_t ui_menu_bar_ctl_paint(void *arg)
+{
+	ui_menu_bar_t *mbar = (ui_menu_bar_t *) arg;
+
+	return ui_menu_bar_paint(mbar);
+}
+
+/** Handle menu bar control position event.
+ *
+ * @param arg Argument (ui_menu_bar_t *)
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_menu_bar_ctl_pos_event(void *arg, pos_event_t *event)
+{
+	ui_menu_bar_t *mbar = (ui_menu_bar_t *) arg;
+
+	return ui_menu_bar_pos_event(mbar, event);
+}
+
+/** @}
+ */
Index: uspace/lib/ui/src/menuentry.c
===================================================================
--- uspace/lib/ui/src/menuentry.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/src/menuentry.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,534 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Menu entry
+ */
+
+#include <adt/list.h>
+#include <errno.h>
+#include <gfx/color.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <gfx/text.h>
+#include <io/pos_event.h>
+#include <stdlib.h>
+#include <str.h>
+#include <ui/control.h>
+#include <ui/paint.h>
+#include <ui/menuentry.h>
+#include <ui/window.h>
+#include "../private/menubar.h"
+#include "../private/menuentry.h"
+#include "../private/menu.h"
+#include "../private/resource.h"
+
+enum {
+	menu_entry_hpad = 4,
+	menu_entry_vpad = 4,
+	menu_entry_column_pad = 8,
+	menu_entry_sep_height = 2,
+	menu_entry_hpad_text = 1,
+	menu_entry_vpad_text = 0,
+	menu_entry_column_pad_text = 2,
+	menu_entry_sep_height_text = 1
+};
+
+/** Create new menu entry.
+ *
+ * @param menu Menu
+ * @param caption Caption
+ * @param shortcut Shotcut key(s) or empty string
+ * @param rmentry Place to store pointer to new menu entry
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ui_menu_entry_create(ui_menu_t *menu, const char *caption,
+    const char *shortcut, ui_menu_entry_t **rmentry)
+{
+	ui_menu_entry_t *mentry;
+	gfx_coord_t caption_w;
+	gfx_coord_t shortcut_w;
+
+	mentry = calloc(1, sizeof(ui_menu_entry_t));
+	if (mentry == NULL)
+		return ENOMEM;
+
+	mentry->caption = str_dup(caption);
+	if (mentry->caption == NULL) {
+		free(mentry);
+		return ENOMEM;
+	}
+
+	mentry->shortcut = str_dup(shortcut);
+	if (mentry->caption == NULL) {
+		free(mentry->caption);
+		free(mentry);
+		return ENOMEM;
+	}
+
+	mentry->menu = menu;
+	list_append(&mentry->lentries, &menu->entries);
+
+	/* Update accumulated menu entry dimensions */
+	ui_menu_entry_column_widths(mentry, &caption_w, &shortcut_w);
+	if (caption_w > menu->max_caption_w)
+		menu->max_caption_w = caption_w;
+	if (shortcut_w > menu->max_shortcut_w)
+		menu->max_shortcut_w = shortcut_w;
+	menu->total_h += ui_menu_entry_height(mentry);
+
+	*rmentry = mentry;
+	return EOK;
+}
+
+/** Create new separator menu entry.
+ *
+ * @param menu Menu
+ * @param rmentry Place to store pointer to new menu entry
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ui_menu_entry_sep_create(ui_menu_t *menu, ui_menu_entry_t **rmentry)
+{
+	ui_menu_entry_t *mentry;
+	errno_t rc;
+
+	rc = ui_menu_entry_create(menu, "", "", &mentry);
+	if (rc != EOK)
+		return rc;
+
+	/* Need to adjust menu height when changing to separator */
+	menu->total_h -= ui_menu_entry_height(mentry);
+	mentry->separator = true;
+	menu->total_h += ui_menu_entry_height(mentry);
+
+	*rmentry = mentry;
+	return EOK;
+}
+
+/** Destroy menu entry.
+ *
+ * @param mentry Menu entry or @c NULL
+ */
+void ui_menu_entry_destroy(ui_menu_entry_t *mentry)
+{
+	if (mentry == NULL)
+		return;
+
+	list_remove(&mentry->lentries);
+	free(mentry->caption);
+	free(mentry);
+}
+
+/** Set menu entry callback.
+ *
+ * @param mentry Menu entry
+ * @param cb Menu entry callback
+ * @param arg Callback argument
+ */
+void ui_menu_entry_set_cb(ui_menu_entry_t *mentry, ui_menu_entry_cb_t cb,
+    void *arg)
+{
+	mentry->cb = cb;
+	mentry->arg = arg;
+}
+
+/** Get first menu entry in menu.
+ *
+ * @param menu Menu
+ * @return First menu entry or @c NULL if there is none
+ */
+ui_menu_entry_t *ui_menu_entry_first(ui_menu_t *menu)
+{
+	link_t *link;
+
+	link = list_first(&menu->entries);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ui_menu_entry_t, lentries);
+}
+
+/** Get next menu entry in menu.
+ *
+ * @param cur Current menu entry
+ * @return Next menu entry or @c NULL if @a cur is the last one
+ */
+ui_menu_entry_t *ui_menu_entry_next(ui_menu_entry_t *cur)
+{
+	link_t *link;
+
+	link = list_next(&cur->lentries, &cur->menu->entries);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ui_menu_entry_t, lentries);
+}
+
+/** Get width of menu entry.
+ *
+ * @param mentry Menu entry
+ * @param caption_w Place to store caption width
+ * @param shortcut_w Place to store shortcut width
+ */
+void ui_menu_entry_column_widths(ui_menu_entry_t *mentry,
+    gfx_coord_t *caption_w, gfx_coord_t *shortcut_w)
+{
+	ui_resource_t *res;
+
+	/*
+	 * This needs to work even if the menu is not open, so we cannot
+	 * use the menu's resource, which is only created after the menu
+	 * is open (and its window is created). Use the menu bar's
+	 * resource instead.
+	 */
+	res = ui_window_get_res(mentry->menu->mbar->window);
+
+	*caption_w = gfx_text_width(res->font, mentry->caption);
+	*shortcut_w = gfx_text_width(res->font, mentry->shortcut);
+}
+
+/** Compute width of menu entry.
+ *
+ * @param menu Menu
+ * @param caption_w Widht of caption text
+ * @param shortcut_w Width of shortcut text
+ * @return Width in pixels
+ */
+gfx_coord_t ui_menu_entry_calc_width(ui_menu_t *menu, gfx_coord_t caption_w,
+    gfx_coord_t shortcut_w)
+{
+	ui_resource_t *res;
+	gfx_coord_t hpad;
+	gfx_coord_t width;
+
+	/*
+	 * This needs to work even if the menu is not open, so we cannot
+	 * use the menu's resource, which is only created after the menu
+	 * is open (and its window is created). Use the menu bar's
+	 * resource instead.
+	 */
+	res = ui_window_get_res(menu->mbar->window);
+
+	if (res->textmode)
+		hpad = menu_entry_hpad_text;
+	else
+		hpad = menu_entry_hpad;
+
+	width = caption_w + 2 * hpad;
+
+	if (shortcut_w != 0) {
+		if (res->textmode)
+			width += menu_entry_column_pad_text;
+		else
+			width += menu_entry_column_pad;
+
+		width += shortcut_w;
+	}
+
+	return width;
+}
+
+/** Get height of menu entry.
+ *
+ * @param mentry Menu entry
+ * @return Width in pixels
+ */
+gfx_coord_t ui_menu_entry_height(ui_menu_entry_t *mentry)
+{
+	ui_resource_t *res;
+	gfx_font_metrics_t metrics;
+	gfx_coord_t height;
+	gfx_coord_t vpad;
+
+	/*
+	 * This needs to work even if the menu is not open, so we cannot
+	 * use the menu's resource, which is only created after the menu
+	 * is open (and its window is created). Use the menu bar's
+	 * resource instead.
+	 */
+	res = ui_window_get_res(mentry->menu->mbar->window);
+
+	if (res->textmode) {
+		vpad = menu_entry_vpad_text;
+	} else {
+		vpad = menu_entry_vpad;
+	}
+
+	if (mentry->separator) {
+		/* Separator menu entry */
+		if (res->textmode)
+			height = menu_entry_sep_height_text;
+		else
+			height = menu_entry_sep_height;
+	} else {
+		/* Normal menu entry */
+		gfx_font_get_metrics(res->font, &metrics);
+		height = metrics.ascent + metrics.descent + 1;
+	}
+
+	return height + 2 * vpad;
+}
+
+/** Paint menu entry.
+ *
+ * @param mentry Menu entry
+ * @param pos Position where to paint entry
+ * @return EOK on success or an error code
+ */
+errno_t ui_menu_entry_paint(ui_menu_entry_t *mentry, gfx_coord2_t *pos)
+{
+	ui_resource_t *res;
+	gfx_text_fmt_t fmt;
+	gfx_color_t *bg_color;
+	ui_menu_entry_geom_t geom;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	res = ui_menu_get_res(mentry->menu);
+
+	ui_menu_entry_get_geom(mentry, pos, &geom);
+
+	gfx_text_fmt_init(&fmt);
+	fmt.halign = gfx_halign_left;
+	fmt.valign = gfx_valign_top;
+
+	if ((mentry->held && mentry->inside) ||
+	    mentry == mentry->menu->selected) {
+		fmt.color = res->wnd_sel_text_color;
+		bg_color = res->wnd_sel_text_bg_color;
+	} else {
+		fmt.color = res->wnd_text_color;
+		bg_color = res->wnd_face_color;
+	}
+
+	rc = gfx_set_color(res->gc, bg_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(res->gc, &geom.outer_rect);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_puttext(res->font, &geom.caption_pos, &fmt, mentry->caption);
+	if (rc != EOK)
+		goto error;
+
+	fmt.halign = gfx_halign_right;
+
+	rc = gfx_puttext(res->font, &geom.shortcut_pos, &fmt, mentry->shortcut);
+	if (rc != EOK)
+		goto error;
+
+	if (mentry->separator) {
+		rect.p0 = geom.caption_pos;
+		rect.p1.x = geom.shortcut_pos.x;
+		rect.p1.y = rect.p0.y + 2;
+		rc = ui_paint_bevel(res->gc, &rect, res->wnd_shadow_color,
+		    res->wnd_highlight_color, 1, NULL);
+		if (rc != EOK)
+			goto error;
+	}
+
+	rc = gfx_update(res->gc);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Handle button press in menu entry.
+ *
+ * @param mentry Menu entry
+ * @param pos Menu entry position
+ */
+void ui_menu_entry_press(ui_menu_entry_t *mentry, gfx_coord2_t *pos)
+{
+	if (mentry->held)
+		return;
+
+	if (mentry->separator)
+		return;
+
+	mentry->inside = true;
+	mentry->held = true;
+	ui_menu_entry_paint(mentry, pos);
+}
+
+/** Handle button release in menu entry.
+ *
+ * @param mentry Menu entry
+ */
+void ui_menu_entry_release(ui_menu_entry_t *mentry)
+{
+	if (!mentry->held)
+		return;
+
+	mentry->held = false;
+
+	if (mentry->inside) {
+		/* Close menu */
+		ui_menu_bar_select(mentry->menu->mbar, NULL, NULL);
+
+		/* Call back */
+		ui_menu_entry_cb(mentry);
+	}
+}
+
+/** Call menu entry callback.
+ *
+ * @param mentry Menu entry
+ */
+void ui_menu_entry_cb(ui_menu_entry_t *mentry)
+{
+	if (mentry->cb != NULL)
+		mentry->cb(mentry, mentry->arg);
+}
+
+/** Pointer entered menu entry.
+ *
+ * @param mentry Menu entry
+ * @param pos Menu entry position
+ */
+void ui_menu_entry_enter(ui_menu_entry_t *mentry, gfx_coord2_t *pos)
+{
+	if (mentry->inside)
+		return;
+
+	mentry->inside = true;
+	if (mentry->held)
+		(void) ui_menu_entry_paint(mentry, pos);
+}
+
+/** Pointer left menu entry.
+ *
+ * @param mentry Menu entry
+ * @param pos Menu entry position
+ */
+void ui_menu_entry_leave(ui_menu_entry_t *mentry, gfx_coord2_t *pos)
+{
+	if (!mentry->inside)
+		return;
+
+	mentry->inside = false;
+	if (mentry->held)
+		(void) ui_menu_entry_paint(mentry, pos);
+}
+
+/** Handle menu entry position event.
+ *
+ * @param mentry Menu entry
+ * @param pos Menu entry position (top-left corner)
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_menu_entry_pos_event(ui_menu_entry_t *mentry,
+    gfx_coord2_t *pos, pos_event_t *event)
+{
+	ui_menu_entry_geom_t geom;
+	gfx_coord2_t ppos;
+	bool inside;
+
+	ppos.x = event->hpos;
+	ppos.y = event->vpos;
+
+	ui_menu_entry_get_geom(mentry, pos, &geom);
+	inside = gfx_pix_inside_rect(&ppos, &geom.outer_rect);
+
+	switch (event->type) {
+	case POS_PRESS:
+		if (inside) {
+			ui_menu_entry_press(mentry, pos);
+			return ui_claimed;
+		}
+		break;
+	case POS_RELEASE:
+		if (mentry->held) {
+			ui_menu_entry_release(mentry);
+			return ui_claimed;
+		}
+		break;
+	case POS_UPDATE:
+		if (inside && !mentry->inside) {
+			ui_menu_entry_enter(mentry, pos);
+			return ui_claimed;
+		} else if (!inside && mentry->inside) {
+			ui_menu_entry_leave(mentry, pos);
+		}
+		break;
+	}
+
+	return ui_unclaimed;
+}
+
+/** Get menu entry geometry.
+ *
+ * @param mentry Menu entry
+ * @param spos Entry position
+ * @param geom Structure to fill in with computed geometry
+ */
+void ui_menu_entry_get_geom(ui_menu_entry_t *mentry, gfx_coord2_t *pos,
+    ui_menu_entry_geom_t *geom)
+{
+	ui_resource_t *res;
+	gfx_coord_t hpad;
+	gfx_coord_t vpad;
+	gfx_coord_t width;
+
+	res = ui_menu_get_res(mentry->menu);
+
+	if (res->textmode) {
+		hpad = menu_entry_hpad_text;
+		vpad = menu_entry_vpad_text;
+	} else {
+		hpad = menu_entry_hpad;
+		vpad = menu_entry_vpad;
+	}
+
+	/* Compute total width of menu entry */
+	width = ui_menu_entry_calc_width(mentry->menu,
+	    mentry->menu->max_caption_w, mentry->menu->max_shortcut_w);
+
+	geom->outer_rect.p0 = *pos;
+	geom->outer_rect.p1.x = geom->outer_rect.p0.x + width;
+	geom->outer_rect.p1.y = geom->outer_rect.p0.y +
+	    ui_menu_entry_height(mentry);
+
+	geom->caption_pos.x = pos->x + hpad;
+	geom->caption_pos.y = pos->y + vpad;
+
+	geom->shortcut_pos.x = geom->outer_rect.p1.x - hpad;
+	geom->shortcut_pos.y = pos->y + vpad;
+}
+
+/** @}
+ */
Index: uspace/lib/ui/src/msgdialog.c
===================================================================
--- uspace/lib/ui/src/msgdialog.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/src/msgdialog.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Message dialog
+ */
+
+#include <errno.h>
+#include <mem.h>
+#include <stdlib.h>
+#include <ui/fixed.h>
+#include <ui/label.h>
+#include <ui/msgdialog.h>
+#include <ui/pbutton.h>
+#include <ui/resource.h>
+#include <ui/ui.h>
+#include <ui/window.h>
+#include "../private/msgdialog.h"
+
+static void ui_msg_dialog_wnd_close(ui_window_t *, void *);
+
+ui_window_cb_t ui_msg_dialog_wnd_cb = {
+	.close = ui_msg_dialog_wnd_close
+};
+
+static void ui_msg_dialog_btn_clicked(ui_pbutton_t *, void *);
+
+ui_pbutton_cb_t ui_msg_dialog_btn_cb = {
+	.clicked = ui_msg_dialog_btn_clicked
+};
+
+/** Initialize message dialog parameters structure.
+ *
+ * Message dialog parameters structure must always be initialized using
+ * this function first.
+ *
+ * @param params Message dialog parameters structure
+ */
+void ui_msg_dialog_params_init(ui_msg_dialog_params_t *params)
+{
+	memset(params, 0, sizeof(ui_msg_dialog_params_t));
+}
+
+/** Create new message dialog.
+ *
+ * @param ui User interface
+ * @param params Message dialog parameters
+ * @param rdialog Place to store pointer to new dialog
+ * @return EOK on success or an error code
+ */
+errno_t ui_msg_dialog_create(ui_t *ui, ui_msg_dialog_params_t *params,
+    ui_msg_dialog_t **rdialog)
+{
+	errno_t rc;
+	ui_msg_dialog_t *dialog;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t wparams;
+	ui_fixed_t *fixed = NULL;
+	ui_label_t *label = NULL;
+	ui_pbutton_t *bok = NULL;
+	gfx_rect_t rect;
+	ui_resource_t *ui_res;
+
+	dialog = calloc(1, sizeof(ui_msg_dialog_t));
+	if (dialog == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	ui_wnd_params_init(&wparams);
+	wparams.caption = params->caption;
+
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		wparams.rect.p0.x = 0;
+		wparams.rect.p0.y = 0;
+		wparams.rect.p1.x = 20;
+		wparams.rect.p1.y = 7;
+	} else {
+		wparams.rect.p0.x = 0;
+		wparams.rect.p0.y = 0;
+		wparams.rect.p1.x = 200;
+		wparams.rect.p1.y = 110;
+	}
+
+	rc = ui_window_create(ui, &wparams, &window);
+	if (rc != EOK)
+		goto error;
+
+	ui_window_set_cb(window, &ui_msg_dialog_wnd_cb, dialog);
+
+	ui_res = ui_window_get_res(window);
+
+	rc = ui_fixed_create(&fixed);
+	if (rc != EOK)
+		goto error;
+
+	rc = ui_label_create(ui_res, params->text, &label);
+	if (rc != EOK)
+		goto error;
+
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		rect.p0.x = 3;
+		rect.p0.y = 2;
+		rect.p1.x = 17;
+		rect.p1.y = 3;
+	} else {
+		rect.p0.x = 10;
+		rect.p0.y = 35;
+		rect.p1.x = 190;
+		rect.p1.y = 50;
+	}
+
+	ui_label_set_rect(label, &rect);
+	ui_label_set_halign(label, gfx_halign_center);
+
+	rc = ui_fixed_add(fixed, ui_label_ctl(label));
+	if (rc != EOK)
+		goto error;
+
+	label = NULL;
+
+	rc = ui_pbutton_create(ui_res, "OK", &bok);
+	if (rc != EOK)
+		goto error;
+
+	ui_pbutton_set_cb(bok, &ui_msg_dialog_btn_cb, dialog);
+
+	/* FIXME: Auto layout */
+	if (ui_is_textmode(ui)) {
+		rect.p0.x = 8;
+		rect.p0.y = 4;
+		rect.p1.x = 12;
+		rect.p1.y = 5;
+	} else {
+		rect.p0.x = 55;
+		rect.p0.y = 60;
+		rect.p1.x = 145;
+		rect.p1.y = 88;
+	}
+
+	ui_pbutton_set_rect(bok, &rect);
+
+	ui_pbutton_set_default(bok, true);
+
+	rc = ui_fixed_add(fixed, ui_pbutton_ctl(bok));
+	if (rc != EOK)
+		goto error;
+
+	dialog->bok = bok;
+	bok = NULL;
+
+	ui_window_add(window, ui_fixed_ctl(fixed));
+	fixed = NULL;
+
+	rc = ui_window_paint(window);
+	if (rc != EOK)
+		goto error;
+
+	dialog->window = window;
+	*rdialog = dialog;
+	return EOK;
+error:
+	if (bok != NULL)
+		ui_pbutton_destroy(bok);
+	if (label != NULL)
+		ui_label_destroy(label);
+	if (fixed != NULL)
+		ui_fixed_destroy(fixed);
+	if (window != NULL)
+		ui_window_destroy(window);
+	if (dialog != NULL)
+		free(dialog);
+	return rc;
+}
+
+/** Destroy message dialog.
+ *
+ * @param dialog Message dialog or @c NULL
+ */
+void ui_msg_dialog_destroy(ui_msg_dialog_t *dialog)
+{
+	if (dialog == NULL)
+		return;
+
+	ui_window_destroy(dialog->window);
+	free(dialog);
+}
+
+/** Set mesage dialog callback.
+ *
+ * @param dialog Message dialog
+ * @param cb Message dialog callbacks
+ * @param arg Callback argument
+ */
+void ui_msg_dialog_set_cb(ui_msg_dialog_t *dialog, ui_msg_dialog_cb_t *cb,
+    void *arg)
+{
+	dialog->cb = cb;
+	dialog->arg = arg;
+}
+
+/** Message dialog window close handler.
+ *
+ * @param window Window
+ * @param arg Argument (ui_msg_dialog_t *)
+ */
+static void ui_msg_dialog_wnd_close(ui_window_t *window, void *arg)
+{
+	ui_msg_dialog_t *dialog = (ui_msg_dialog_t *) arg;
+
+	if (dialog->cb != NULL && dialog->cb->close != NULL)
+		dialog->cb->close(dialog, dialog->arg);
+}
+
+/** Message dialog button click handler.
+ *
+ * @param pbutton Push button
+ * @param arg Argument (ui_msg_dialog_t *)
+ */
+static void ui_msg_dialog_btn_clicked(ui_pbutton_t *pbutton, void *arg)
+{
+	ui_msg_dialog_t *dialog = (ui_msg_dialog_t *) arg;
+
+	if (dialog->cb != NULL && dialog->cb->button != NULL)
+		dialog->cb->button(dialog, dialog->arg, 0);
+}
+
+/** @}
+ */
Index: uspace/lib/ui/src/paint.c
===================================================================
--- uspace/lib/ui/src/paint.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/src/paint.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,4 +39,5 @@
 #include <gfx/render.h>
 #include <ui/paint.h>
+#include "../private/resource.h"
 
 /** Paint bevel.
@@ -48,5 +49,5 @@
  * @param thickness Bevel thickness in pixels
  * @param inside Place to store rectangle of the interior or @c NULL
- * @reutrn EOK on success or an error code
+ * @return EOK on success or an error code
  */
 errno_t ui_paint_bevel(gfx_context_t *gc, gfx_rect_t *rect,
@@ -106,10 +107,6 @@
 	}
 
-	if (inside != NULL) {
-		inside->p0.x = rect->p0.x + thickness;
-		inside->p0.y = rect->p0.y + thickness;
-		inside->p1.x = rect->p1.x - thickness;
-		inside->p1.y = rect->p1.y - thickness;
-	}
+	if (inside != NULL)
+		ui_paint_get_bevel_inside(gc, rect, thickness, inside);
 
 	return EOK;
@@ -118,4 +115,213 @@
 }
 
+/** Get bevel interior rectangle.
+ *
+ * Get the bevel interior rectangle without painting it.
+ *
+ * @param gc Graphic context
+ * @param rect Rectangle to paint into
+ * @param thickness Bevel thickness in pixels
+ * @param inside Place to store rectangle of the interior
+ */
+void ui_paint_get_bevel_inside(gfx_context_t *gc, gfx_rect_t *rect,
+    gfx_coord_t thickness, gfx_rect_t *inside)
+{
+	inside->p0.x = rect->p0.x + thickness;
+	inside->p0.y = rect->p0.y + thickness;
+	inside->p1.x = rect->p1.x - thickness;
+	inside->p1.y = rect->p1.y - thickness;
+}
+
+/** Paint inset frame.
+ *
+ * @param resource UI resource
+ * @param rect Rectangle to paint onto
+ * @param inside Place to store inside rectangle or @c NULL
+ * @return EOK on success or an error code
+ */
+errno_t ui_paint_inset_frame(ui_resource_t *resource, gfx_rect_t *rect,
+    gfx_rect_t *inside)
+{
+	gfx_rect_t frame;
+	errno_t rc;
+
+	rc = ui_paint_bevel(resource->gc, rect,
+	    resource->wnd_shadow_color, resource->wnd_highlight_color,
+	    1, &frame);
+	if (rc != EOK)
+		goto error;
+
+	rc = ui_paint_bevel(resource->gc, &frame,
+	    resource->wnd_frame_sh_color, resource->wnd_frame_hi_color,
+	    1, inside);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Get inset frame interior rectangle.
+ *
+ * This allows one to get the interior rectangle without actually painting
+ * the inset frame.
+ *
+ * @param resource UI resource
+ * @param rect Rectangle to paint onto
+ * @param inside Place to store inside rectangle or @c NULL
+ */
+void ui_paint_get_inset_frame_inside(ui_resource_t *resource, gfx_rect_t *rect,
+    gfx_rect_t *inside)
+{
+	ui_paint_get_bevel_inside(resource->gc, rect, 2, inside);
+}
+
+/** Paint outset frame.
+ *
+ * @param resource UI resource
+ * @param rect Rectangle to paint onto
+ * @param inside Place to store inside rectangle or @c NULL
+ * @return EOK on success or an error code
+ */
+errno_t ui_paint_outset_frame(ui_resource_t *resource, gfx_rect_t *rect,
+    gfx_rect_t *inside)
+{
+	gfx_rect_t frame;
+	errno_t rc;
+
+	rc = ui_paint_bevel(resource->gc, rect,
+	    resource->wnd_frame_hi_color, resource->wnd_frame_sh_color,
+	    1, &frame);
+	if (rc != EOK)
+		goto error;
+
+	rc = ui_paint_bevel(resource->gc, &frame,
+	    resource->wnd_highlight_color, resource->wnd_shadow_color,
+	    1, inside);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Paint filled circle vertical scanline.
+ *
+ * @param gc Graphic context
+ * @param center Coordinates of the center of the circle
+ * @param x X-coordinate of the scanline
+ * @param y0 Lowest Y coordinate of the scanline (inclusive)
+ * @param y1 Highest Y coordinate of the scanline (inclusive)
+ * @param part Which part(s) of cicle to paint
+ * @return EOK on success or an error code
+ */
+static errno_t ui_paint_fcircle_line(gfx_context_t *gc, gfx_coord2_t *center,
+    gfx_coord_t x, gfx_coord_t y0, gfx_coord_t y1, ui_fcircle_part_t part)
+{
+	gfx_rect_t rect;
+	gfx_rect_t trect;
+
+	rect.p0.x = x;
+	rect.p0.y = y0;
+	rect.p1.x = x + 1;
+	rect.p1.y = y1;
+
+	/* Clip to upper-left/lower-right half of circle, if required */
+
+	if ((part & ui_fcircle_upleft) == 0) {
+		if (rect.p0.y < -rect.p1.x)
+			rect.p0.y = -rect.p1.x;
+	}
+
+	if ((part & ui_fcircle_lowright) == 0) {
+		if (rect.p1.y > -rect.p1.x)
+			rect.p1.y = -rect.p1.x;
+	}
+
+	/* If coordinates are reversed, there is nothing to do */
+	if (rect.p1.y <= rect.p0.y)
+		return EOK;
+
+	gfx_rect_translate(center, &rect, &trect);
+
+	return gfx_fill_rect(gc, &trect);
+}
+
+/** Paint filled circle scanlines corresponding to octant point.
+ *
+ * Fills the four vertical scanlines lying between the eight reflections
+ * of a circle point.
+ *
+ * @param gc Graphic context
+ * @param center Coordinates of the center
+ * @param r Radius in pixels
+ * @param part Which part(s) of cicle to paint
+ * @return EOK on success or an error code
+ */
+static int ui_paint_fcircle_point(gfx_context_t *gc, gfx_coord2_t *center,
+    gfx_coord2_t *p, ui_fcircle_part_t part)
+{
+	errno_t rc;
+
+	rc = ui_paint_fcircle_line(gc, center, +p->x, -p->y, p->y + 1, part);
+	if (rc != EOK)
+		return rc;
+	rc = ui_paint_fcircle_line(gc, center, -p->x, -p->y, p->y + 1, part);
+	if (rc != EOK)
+		return rc;
+	rc = ui_paint_fcircle_line(gc, center, +p->y, -p->x, p->x + 1, part);
+	if (rc != EOK)
+		return rc;
+	rc = ui_paint_fcircle_line(gc, center, -p->y, -p->x, p->x + 1, part);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+/** Paint a filled circle.
+ *
+ * @param gc Graphic context
+ * @param center Coordinates of the center
+ * @param r Radius in pixels
+ * @param part Which part(s) of cicle to paint
+ * @return EOK on success or an error code
+ */
+errno_t ui_paint_filled_circle(gfx_context_t *gc, gfx_coord2_t *center,
+    gfx_coord_t r, ui_fcircle_part_t part)
+{
+	gfx_coord2_t p;
+	gfx_coord_t f;
+	errno_t rc;
+
+	/* Run through one octant using circle midpoint algorithm */
+
+	p.x = r;
+	p.y = 0;
+	f = 1 - r;
+
+	rc = ui_paint_fcircle_point(gc, center, &p, part);
+	if (rc != EOK)
+		return rc;
+
+	while (p.x > p.y) {
+		if (f < 0) {
+			f = f + 2 * p.y + 3;
+		} else {
+			f = f + 2 * (p.y - p.x) + 5;
+			--p.x;
+		}
+		++p.y;
+
+		rc = ui_paint_fcircle_point(gc, center, &p, part);
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
 /** @}
  */
Index: uspace/lib/ui/src/pbutton.c
===================================================================
--- uspace/lib/ui/src/pbutton.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/src/pbutton.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -239,10 +239,38 @@
 }
 
-/** Paint push button.
- *
- * @param pbutton Push button
- * @return EOK on success or an error code
- */
-errno_t ui_pbutton_paint(ui_pbutton_t *pbutton)
+/** Paint button text shadow.
+ *
+ * @param pbutton Push button
+ * @return EOK on success or an error code
+ */
+static errno_t ui_pbutton_paint_text_shadow(ui_pbutton_t *pbutton)
+{
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rect.p0.x = pbutton->rect.p0.x + 1;
+	rect.p0.y = pbutton->rect.p0.y + 1;
+	rect.p1.x = pbutton->rect.p1.x;
+	rect.p1.y = pbutton->rect.p1.y;
+
+	rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_shadow_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(pbutton->res->gc, &rect);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Paint push button in graphic mode.
+ *
+ * @param pbutton Push button
+ * @return EOK on success or an error code
+ */
+static errno_t ui_pbutton_paint_gfx(ui_pbutton_t *pbutton)
 {
 	gfx_coord2_t pos;
@@ -269,8 +297,4 @@
 		goto error;
 
-	rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_text_color);
-	if (rc != EOK)
-		goto error;
-
 	/* Center of button rectangle */
 	pos.x = (rect.p0.x + rect.p1.x) / 2;
@@ -283,4 +307,5 @@
 
 	gfx_text_fmt_init(&fmt);
+	fmt.color = pbutton->res->btn_text_color;
 	fmt.halign = gfx_halign_center;
 	fmt.valign = gfx_valign_center;
@@ -304,7 +329,88 @@
 	}
 
+	rc = gfx_update(pbutton->res->gc);
+	if (rc != EOK)
+		goto error;
+
 	return EOK;
 error:
 	return rc;
+}
+
+/** Paint push button in text mode.
+ *
+ * @param pbutton Push button
+ * @return EOK on success or an error code
+ */
+static errno_t ui_pbutton_paint_text(ui_pbutton_t *pbutton)
+{
+	gfx_coord2_t pos;
+	gfx_text_fmt_t fmt;
+	gfx_rect_t rect;
+	bool depressed;
+	errno_t rc;
+
+	depressed = pbutton->held && pbutton->inside;
+
+	rc = gfx_set_color(pbutton->res->gc, pbutton->res->wnd_face_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(pbutton->res->gc, &pbutton->rect);
+	if (rc != EOK)
+		goto error;
+
+	rect.p0.x = pbutton->rect.p0.x + (depressed ? 1 : 0);
+	rect.p0.y = pbutton->rect.p0.y;
+	rect.p1.x = pbutton->rect.p1.x - 1 + (depressed ? 1 : 0);
+	rect.p1.y = pbutton->rect.p0.y + 1;
+
+	rc = gfx_set_color(pbutton->res->gc, pbutton->res->btn_highlight_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(pbutton->res->gc, &rect);
+	if (rc != EOK)
+		goto error;
+
+	/* Center of button rectangle */
+	pos.x = (rect.p0.x + rect.p1.x) / 2;
+	pos.y = (rect.p0.y + rect.p1.y) / 2;
+
+	gfx_text_fmt_init(&fmt);
+	fmt.color = pbutton->res->btn_text_color;
+	fmt.halign = gfx_halign_center;
+	fmt.valign = gfx_valign_center;
+
+	rc = gfx_puttext(pbutton->res->font, &pos, &fmt, pbutton->caption);
+	if (rc != EOK)
+		goto error;
+
+	if (!depressed) {
+		rc = ui_pbutton_paint_text_shadow(pbutton);
+		if (rc != EOK)
+			goto error;
+	}
+
+	rc = gfx_update(pbutton->res->gc);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Paint push button.
+ *
+ * @param pbutton Push button
+ * @return EOK on success or an error code
+ */
+errno_t ui_pbutton_paint(ui_pbutton_t *pbutton)
+{
+	if (pbutton->res->textmode)
+		return ui_pbutton_paint_text(pbutton);
+	else
+		return ui_pbutton_paint_gfx(pbutton);
 }
 
Index: uspace/lib/ui/src/popup.c
===================================================================
--- uspace/lib/ui/src/popup.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/src/popup.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Popup window
+ */
+
+#include <errno.h>
+#include <gfx/context.h>
+//#include <io/kbd_event.h>
+#include <io/pos_event.h>
+#include <mem.h>
+#include <stdlib.h>
+#include <ui/control.h>
+#include <ui/popup.h>
+#include <ui/ui.h>
+#include <ui/window.h>
+#include "../private/popup.h"
+
+static void ui_popup_window_close(ui_window_t *, void *);
+static void ui_popup_window_pos(ui_window_t *, void *, pos_event_t *);
+
+static ui_window_cb_t ui_popup_window_cb = {
+	.close = ui_popup_window_close,
+	.pos = ui_popup_window_pos
+};
+
+/** Initialize popup parameters structure.
+ *
+ * Popup parameters structure must always be initialized using this function
+ * first.
+ *
+ * @param params Popup parameters structure
+ */
+void ui_popup_params_init(ui_popup_params_t *params)
+{
+	memset(params, 0, sizeof(ui_popup_params_t));
+}
+
+/** Create new popup window.
+ *
+ * @param ui User interface
+ * @param parent Parent window
+ * @param params Popup parameters
+ * @param rpopup Place to store pointer to new popup window
+ * @return EOK on success or an error code
+ */
+errno_t ui_popup_create(ui_t *ui, ui_window_t *parent,
+    ui_popup_params_t *params, ui_popup_t **rpopup)
+{
+	ui_popup_t *popup;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t wparams;
+	gfx_coord2_t parent_pos;
+	errno_t rc;
+
+	popup = calloc(1, sizeof(ui_popup_t));
+	if (popup == NULL)
+		return ENOMEM;
+
+	rc = ui_window_get_pos(parent, &parent_pos);
+	if (rc != EOK)
+		goto error;
+
+	ui_wnd_params_init(&wparams);
+	wparams.rect = params->rect;
+	wparams.caption = "";
+	wparams.style &= ~ui_wds_decorated;
+	wparams.placement = ui_wnd_place_popup;
+	wparams.flags |= ui_wndf_popup;
+
+	/* Compute position of parent rectangle relative to the screen */
+	gfx_rect_translate(&parent_pos, &params->place, &wparams.prect);
+
+	rc = ui_window_create(ui, &wparams, &window);
+	if (rc != EOK)
+		goto error;
+
+	popup->ui = ui;
+	popup->parent = parent;
+	popup->window = window;
+
+	ui_window_set_cb(window, &ui_popup_window_cb, popup);
+
+	*rpopup = popup;
+	return EOK;
+error:
+	free(popup);
+	return rc;
+}
+
+/** Destroy popup window.
+ *
+ * @param popup Popup window or @c NULL
+ */
+void ui_popup_destroy(ui_popup_t *popup)
+{
+	if (popup == NULL)
+		return;
+
+	ui_window_destroy(popup->window);
+	free(popup);
+}
+
+/** Add control to popup window.
+ *
+ * Only one control can be added to a popup window. If more than one control
+ * is added, the results are undefined.
+ *
+ * @param popup Popup window
+ * @param control Control
+ * @return EOK on success, ENOMEM if out of memory
+ */
+void ui_popup_add(ui_popup_t *popup, ui_control_t *control)
+{
+	ui_window_add(popup->window, control);
+}
+
+/** Remove control from popup window.
+ *
+ * @param popup Popup window
+ * @param control Control
+ */
+void ui_popup_remove(ui_popup_t *popup, ui_control_t *control)
+{
+	ui_window_remove(popup->window, control);
+}
+
+/** Set popup window callbacks.
+ *
+ * @param popup Popup window
+ * @param cb Popup window callbacks
+ * @param arg Callback argument
+ */
+void ui_popup_set_cb(ui_popup_t *popup, ui_popup_cb_t *cb, void *arg)
+{
+	popup->cb = cb;
+	popup->arg = arg;
+}
+
+/** Get UI resource from popup window.
+ *
+ * @param window Window
+ * @return UI resource
+ */
+ui_resource_t *ui_popup_get_res(ui_popup_t *popup)
+{
+	return ui_window_get_res(popup->window);
+}
+
+/** Get popup window GC.
+ *
+ * @param popup Popup window
+ * @return GC (relative to popup window)
+ */
+gfx_context_t *ui_popup_get_gc(ui_popup_t *popup)
+{
+	return ui_window_get_gc(popup->window);
+}
+
+/** Handle close event in popup window.
+ *
+ * @param window Window
+ * @param arg Argument (ui_popup_t *)
+ */
+static void ui_popup_window_close(ui_window_t *window, void *arg)
+{
+	ui_popup_t *popup = (ui_popup_t *)arg;
+
+	if (popup->cb != NULL && popup->cb->close != NULL)
+		popup->cb->close(popup, popup->arg);
+}
+
+/** Handle position event in popup window.
+ *
+ * @param window Window
+ * @param arg Argument (ui_popup_t *)
+ * @param event Position event
+ */
+static void ui_popup_window_pos(ui_window_t *window, void *arg,
+    pos_event_t *event)
+{
+	ui_popup_t *popup = (ui_popup_t *)arg;
+
+	if (popup->cb != NULL && popup->cb->pos != NULL)
+		popup->cb->pos(popup, popup->arg, event);
+}
+
+/** @}
+ */
Index: uspace/lib/ui/src/rbutton.c
===================================================================
--- uspace/lib/ui/src/rbutton.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/src/rbutton.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Radio button
+ */
+
+#include <errno.h>
+#include <gfx/color.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <gfx/text.h>
+#include <io/pos_event.h>
+#include <stdlib.h>
+#include <str.h>
+#include <ui/control.h>
+#include <ui/paint.h>
+#include <ui/rbutton.h>
+#include "../private/rbutton.h"
+#include "../private/resource.h"
+
+enum {
+	rbutton_oframe_r = 7,
+	rbutton_iframe_r = 6,
+	rbutton_interior_r = 5,
+	rbutton_indicator_r = 3,
+	rbutton_label_margin = 8
+};
+
+static void ui_rbutton_ctl_destroy(void *);
+static errno_t ui_rbutton_ctl_paint(void *);
+static ui_evclaim_t ui_rbutton_ctl_pos_event(void *, pos_event_t *);
+
+/** Radio button control ops */
+ui_control_ops_t ui_rbutton_ops = {
+	.destroy = ui_rbutton_ctl_destroy,
+	.paint = ui_rbutton_ctl_paint,
+	.pos_event = ui_rbutton_ctl_pos_event
+};
+
+/** Create new radio button group.
+ *
+ * @param res UI resource
+ * @param rgroup Place to store pointer to new radio button group
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ui_rbutton_group_create(ui_resource_t *res, ui_rbutton_group_t **rgroup)
+{
+	ui_rbutton_group_t *group;
+
+	group = calloc(1, sizeof(ui_rbutton_group_t));
+	if (group == NULL)
+		return ENOMEM;
+
+	group->res = res;
+	*rgroup = group;
+	return EOK;
+}
+
+/** Destroy radio button group.
+ *
+ * @param group Radio button group or @c NULL
+ */
+void ui_rbutton_group_destroy(ui_rbutton_group_t *group)
+{
+	if (group == NULL)
+		return;
+
+	free(group);
+}
+
+/** Create new radio button.
+ *
+ * @param group Radio button group
+ * @param caption Caption
+ * @param arg Callback argument
+ * @param rrbutton Place to store pointer to new radio button
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ui_rbutton_create(ui_rbutton_group_t *group, const char *caption,
+    void *arg, ui_rbutton_t **rrbutton)
+{
+	ui_rbutton_t *rbutton;
+	errno_t rc;
+
+	rbutton = calloc(1, sizeof(ui_rbutton_t));
+	if (rbutton == NULL)
+		return ENOMEM;
+
+	rc = ui_control_new(&ui_rbutton_ops, (void *) rbutton,
+	    &rbutton->control);
+	if (rc != EOK) {
+		free(rbutton);
+		return rc;
+	}
+
+	rbutton->caption = str_dup(caption);
+	if (rbutton->caption == NULL) {
+		ui_control_delete(rbutton->control);
+		free(rbutton);
+		return ENOMEM;
+	}
+
+	rbutton->group = group;
+	rbutton->arg = arg;
+
+	if (group->selected == NULL)
+		group->selected = rbutton;
+
+	*rrbutton = rbutton;
+	return EOK;
+}
+
+/** Destroy radio button.
+ *
+ * @param rbutton Radio button or @c NULL
+ */
+void ui_rbutton_destroy(ui_rbutton_t *rbutton)
+{
+	if (rbutton == NULL)
+		return;
+
+	ui_control_delete(rbutton->control);
+	free(rbutton);
+}
+
+/** Get base control from radio button.
+ *
+ * @param rbutton Radio button
+ * @return Control
+ */
+ui_control_t *ui_rbutton_ctl(ui_rbutton_t *rbutton)
+{
+	return rbutton->control;
+}
+
+/** Set radio button group callbacks.
+ *
+ * @param group Radio button group
+ * @param cb Radio button group callbacks
+ * @param arg Callback argument
+ */
+void ui_rbutton_group_set_cb(ui_rbutton_group_t *group,
+    ui_rbutton_group_cb_t *cb, void *arg)
+{
+	group->cb = cb;
+	group->arg = arg;
+}
+
+/** Set button rectangle.
+ *
+ * @param rbutton Button
+ * @param rect New button rectangle
+ */
+void ui_rbutton_set_rect(ui_rbutton_t *rbutton, gfx_rect_t *rect)
+{
+	rbutton->rect = *rect;
+}
+
+/** Paint radio button.
+ *
+ * @param rbutton Radio button
+ * @return EOK on success or an error code
+ */
+errno_t ui_rbutton_paint(ui_rbutton_t *rbutton)
+{
+	gfx_coord2_t pos;
+	gfx_coord2_t center;
+	gfx_text_fmt_t fmt;
+	bool depressed;
+	errno_t rc;
+
+	center.x = rbutton->rect.p0.x + rbutton_oframe_r;
+	center.y = rbutton->rect.p0.y + rbutton_oframe_r;
+
+	/* Paint rbutton frame */
+
+	rc = gfx_set_color(rbutton->group->res->gc,
+	    rbutton->group->res->wnd_shadow_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = ui_paint_filled_circle(rbutton->group->res->gc,
+	    &center, rbutton_oframe_r, ui_fcircle_upleft);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_set_color(rbutton->group->res->gc,
+	    rbutton->group->res->wnd_highlight_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = ui_paint_filled_circle(rbutton->group->res->gc,
+	    &center, rbutton_oframe_r, ui_fcircle_lowright);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_set_color(rbutton->group->res->gc,
+	    rbutton->group->res->wnd_frame_sh_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = ui_paint_filled_circle(rbutton->group->res->gc,
+	    &center, rbutton_iframe_r, ui_fcircle_upleft);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_set_color(rbutton->group->res->gc,
+	    rbutton->group->res->wnd_face_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = ui_paint_filled_circle(rbutton->group->res->gc,
+	    &center, rbutton_iframe_r, ui_fcircle_lowright);
+	if (rc != EOK)
+		goto error;
+
+	/* Paint rbutton interior */
+	depressed = rbutton->held && rbutton->inside;
+
+	rc = gfx_set_color(rbutton->group->res->gc, depressed ?
+	    rbutton->group->res->entry_act_bg_color :
+	    rbutton->group->res->entry_bg_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = ui_paint_filled_circle(rbutton->group->res->gc,
+	    &center, rbutton_interior_r, ui_fcircle_entire);
+	if (rc != EOK)
+		goto error;
+
+	/* Paint active mark */
+
+	if (rbutton->group->selected == rbutton) {
+		rc = gfx_set_color(rbutton->group->res->gc,
+		    rbutton->group->res->entry_fg_color);
+		if (rc != EOK)
+			goto error;
+
+		rc = ui_paint_filled_circle(rbutton->group->res->gc,
+		    &center, rbutton_indicator_r, ui_fcircle_entire);
+		if (rc != EOK)
+			goto error;
+	}
+
+	/* Paint rbutton label */
+
+	pos.x = center.x + rbutton_oframe_r + rbutton_label_margin;
+	pos.y = center.y;
+
+	gfx_text_fmt_init(&fmt);
+	fmt.color = rbutton->group->res->wnd_text_color;
+	fmt.halign = gfx_halign_left;
+	fmt.valign = gfx_valign_center;
+
+	rc = gfx_puttext(rbutton->group->res->font, &pos, &fmt,
+	    rbutton->caption);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_update(rbutton->group->res->gc);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Press down button.
+ *
+ * @param rbutton Radio button
+ */
+void ui_rbutton_press(ui_rbutton_t *rbutton)
+{
+	if (rbutton->held)
+		return;
+
+	rbutton->inside = true;
+	rbutton->held = true;
+	(void) ui_rbutton_paint(rbutton);
+}
+
+/** Release button.
+ *
+ * @param rbutton Radio button
+ */
+void ui_rbutton_release(ui_rbutton_t *rbutton)
+{
+	if (!rbutton->held)
+		return;
+
+	rbutton->held = false;
+
+	if (rbutton->inside) {
+		/* Activate radio button */
+		ui_rbutton_select(rbutton);
+	}
+}
+
+/** Pointer entered button.
+ *
+ * @param rbutton Radio button
+ */
+void ui_rbutton_enter(ui_rbutton_t *rbutton)
+{
+	if (rbutton->inside)
+		return;
+
+	rbutton->inside = true;
+	if (rbutton->held)
+		(void) ui_rbutton_paint(rbutton);
+}
+
+/** Pointer left button.
+ *
+ * @param rbutton Radio button
+ */
+void ui_rbutton_leave(ui_rbutton_t *rbutton)
+{
+	if (!rbutton->inside)
+		return;
+
+	rbutton->inside = false;
+	if (rbutton->held)
+		(void) ui_rbutton_paint(rbutton);
+}
+
+/** Select radio button.
+ *
+ * @param rbutton Radio button
+ */
+void ui_rbutton_select(ui_rbutton_t *rbutton)
+{
+	ui_rbutton_t *old_selected;
+
+	old_selected = rbutton->group->selected;
+
+	if (old_selected != rbutton) {
+		rbutton->group->selected = rbutton;
+		ui_rbutton_paint(old_selected);
+	}
+
+	/* Repaint and notify */
+	(void) ui_rbutton_paint(rbutton);
+
+	if (old_selected != rbutton)
+		ui_rbutton_selected(rbutton);
+}
+
+/** Notify that button was selected.
+ *
+ * @param rbutton Radio button
+ */
+void ui_rbutton_selected(ui_rbutton_t *rbutton)
+{
+	ui_rbutton_group_t *group = rbutton->group;
+
+	if (group->cb != NULL && group->cb->selected != NULL) {
+		group->cb->selected(group, group->arg, rbutton->arg);
+	}
+}
+
+/** Handle radio button position event.
+ *
+ * @param rbutton Radio button
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_rbutton_pos_event(ui_rbutton_t *rbutton, pos_event_t *event)
+{
+	gfx_coord2_t pos;
+	bool inside;
+
+	pos.x = event->hpos;
+	pos.y = event->vpos;
+
+	inside = gfx_pix_inside_rect(&pos, &rbutton->rect);
+
+	switch (event->type) {
+	case POS_PRESS:
+		if (inside) {
+			ui_rbutton_press(rbutton);
+			return ui_claimed;
+		}
+		break;
+	case POS_RELEASE:
+		if (rbutton->held) {
+			ui_rbutton_release(rbutton);
+			return ui_claimed;
+		}
+		break;
+	case POS_UPDATE:
+		if (inside && !rbutton->inside) {
+			ui_rbutton_enter(rbutton);
+			return ui_claimed;
+		} else if (!inside && rbutton->inside) {
+			ui_rbutton_leave(rbutton);
+		}
+		break;
+	}
+
+	return ui_unclaimed;
+}
+
+/** Destroy radio button control.
+ *
+ * @param arg Argument (ui_rbutton_t *)
+ */
+void ui_rbutton_ctl_destroy(void *arg)
+{
+	ui_rbutton_t *rbutton = (ui_rbutton_t *) arg;
+
+	ui_rbutton_destroy(rbutton);
+}
+
+/** Paint radio button control.
+ *
+ * @param arg Argument (ui_rbutton_t *)
+ * @return EOK on success or an error code
+ */
+errno_t ui_rbutton_ctl_paint(void *arg)
+{
+	ui_rbutton_t *rbutton = (ui_rbutton_t *) arg;
+
+	return ui_rbutton_paint(rbutton);
+}
+
+/** Handle radio button control position event.
+ *
+ * @param arg Argument (ui_rbutton_t *)
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_rbutton_ctl_pos_event(void *arg, pos_event_t *event)
+{
+	ui_rbutton_t *rbutton = (ui_rbutton_t *) arg;
+
+	return ui_rbutton_pos_event(rbutton, event);
+}
+
+/** @}
+ */
Index: uspace/lib/ui/src/resource.c
===================================================================
--- uspace/lib/ui/src/resource.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/src/resource.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -50,8 +50,10 @@
  *
  * @param gc Graphic context
+ * @param textmode @c true if running in text mode
  * @param rresource Place to store pointer to new UI resource
  * @return EOK on success, ENOMEM if out of memory
  */
-errno_t ui_resource_create(gfx_context_t *gc, ui_resource_t **rresource)
+errno_t ui_resource_create(gfx_context_t *gc, bool textmode,
+    ui_resource_t **rresource)
 {
 	ui_resource_t *resource;
@@ -66,4 +68,6 @@
 	gfx_color_t *wnd_face_color = NULL;
 	gfx_color_t *wnd_text_color = NULL;
+	gfx_color_t *wnd_sel_text_color = NULL;
+	gfx_color_t *wnd_sel_text_bg_color = NULL;
 	gfx_color_t *wnd_frame_hi_color = NULL;
 	gfx_color_t *wnd_frame_sh_color = NULL;
@@ -76,4 +80,5 @@
 	gfx_color_t *entry_fg_color = NULL;
 	gfx_color_t *entry_bg_color = NULL;
+	gfx_color_t *entry_act_bg_color = NULL;
 	errno_t rc;
 
@@ -82,17 +87,28 @@
 		return ENOMEM;
 
-	rc = gfx_typeface_open(gc, ui_typeface_path, &tface);
-	if (rc != EOK)
-		goto error;
-
-	finfo = gfx_typeface_first_font(tface);
-	if (finfo == NULL) {
-		rc = EIO;
-		goto error;
+	if (textmode) {
+		/* Create dummy font for text mode */
+		rc = gfx_typeface_create(gc, &tface);
+		if (rc != EOK)
+			goto error;
+
+		rc = gfx_font_create_textmode(tface, &font);
+		if (rc != EOK)
+			goto error;
+	} else {
+		rc = gfx_typeface_open(gc, ui_typeface_path, &tface);
+		if (rc != EOK)
+			goto error;
+
+		finfo = gfx_typeface_first_font(tface);
+		if (finfo == NULL) {
+			rc = EIO;
+			goto error;
+		}
+
+		rc = gfx_font_open(finfo, &font);
+		if (rc != EOK)
+			goto error;
 	}
-
-	rc = gfx_font_open(finfo, &font);
-	if (rc != EOK)
-		goto error;
 
 	rc = gfx_color_new_rgb_i16(0, 0, 0, &btn_frame_color);
@@ -125,9 +141,18 @@
 		goto error;
 
+	rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &wnd_sel_text_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_rgb_i16(0x5858, 0x6a6a, 0xc4c4,
+	    &wnd_sel_text_bg_color);
+	if (rc != EOK)
+		goto error;
+
 	rc = gfx_color_new_rgb_i16(0x8888, 0x8888, 0x8888, &wnd_frame_hi_color);
 	if (rc != EOK)
 		goto error;
 
-	rc = gfx_color_new_rgb_i16(0, 0, 0, &wnd_frame_sh_color);
+	rc = gfx_color_new_rgb_i16(0x4444, 0x4444, 0x4444, &wnd_frame_sh_color);
 	if (rc != EOK)
 		goto error;
@@ -166,4 +191,8 @@
 
 	rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &entry_bg_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_color_new_rgb_i16(0xc8c8, 0xc8c8, 0xc8c8, &entry_act_bg_color);
 	if (rc != EOK)
 		goto error;
@@ -172,4 +201,5 @@
 	resource->tface = tface;
 	resource->font = font;
+	resource->textmode = textmode;
 
 	resource->btn_frame_color = btn_frame_color;
@@ -181,4 +211,6 @@
 	resource->wnd_face_color = wnd_face_color;
 	resource->wnd_text_color = wnd_text_color;
+	resource->wnd_sel_text_color = wnd_sel_text_color;
+	resource->wnd_sel_text_bg_color = wnd_sel_text_bg_color;
 	resource->wnd_frame_hi_color = wnd_frame_hi_color;
 	resource->wnd_frame_sh_color = wnd_frame_sh_color;
@@ -193,4 +225,5 @@
 	resource->entry_fg_color = entry_fg_color;
 	resource->entry_bg_color = entry_bg_color;
+	resource->entry_act_bg_color = entry_act_bg_color;
 
 	*rresource = resource;
@@ -212,4 +245,8 @@
 	if (wnd_text_color != NULL)
 		gfx_color_delete(wnd_text_color);
+	if (wnd_sel_text_color != NULL)
+		gfx_color_delete(wnd_sel_text_color);
+	if (wnd_sel_text_bg_color != NULL)
+		gfx_color_delete(wnd_sel_text_bg_color);
 	if (wnd_frame_hi_color != NULL)
 		gfx_color_delete(wnd_frame_hi_color);
@@ -234,4 +271,6 @@
 	if (entry_bg_color != NULL)
 		gfx_color_delete(entry_bg_color);
+	if (entry_act_bg_color != NULL)
+		gfx_color_delete(entry_act_bg_color);
 
 	if (tface != NULL)
@@ -258,4 +297,6 @@
 	gfx_color_delete(resource->wnd_face_color);
 	gfx_color_delete(resource->wnd_text_color);
+	gfx_color_delete(resource->wnd_sel_text_color);
+	gfx_color_delete(resource->wnd_sel_text_bg_color);
 	gfx_color_delete(resource->wnd_frame_hi_color);
 	gfx_color_delete(resource->wnd_frame_sh_color);
@@ -270,4 +311,5 @@
 	gfx_color_delete(resource->entry_fg_color);
 	gfx_color_delete(resource->entry_bg_color);
+	gfx_color_delete(resource->entry_act_bg_color);
 
 	gfx_font_close(resource->font);
@@ -276,4 +318,32 @@
 }
 
+/** Set UI resource expose callback.
+ *
+ * @param resource Resource
+ * @param cb Callback
+ * @param arg Callback argument
+ */
+void ui_resource_set_expose_cb(ui_resource_t *resource,
+    ui_expose_cb_t cb, void *arg)
+{
+	resource->expose_cb = cb;
+	resource->expose_arg = arg;
+}
+
+/** Force UI repaint after an area has been exposed.
+ *
+ * This is called when a popup disappears, which could have exposed some
+ * other UI elements. It causes complete repaint of the UI.
+ *
+ * NOTE Ideally we could specify the exposed rectangle and then limit
+ * the repaint to just that. That would, however, require means of
+ * actually clipping the repaint operation.
+ */
+void ui_resource_expose(ui_resource_t *resource)
+{
+	if (resource->expose_cb != NULL)
+		resource->expose_cb(resource->expose_arg);
+}
+
 /** @}
  */
Index: uspace/lib/ui/src/slider.c
===================================================================
--- uspace/lib/ui/src/slider.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/src/slider.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 2021 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 libui
+ * @{
+ */
+/**
+ * @file Slider
+ */
+
+#include <errno.h>
+#include <gfx/color.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <io/pos_event.h>
+#include <stdlib.h>
+#include <str.h>
+#include <ui/control.h>
+#include <ui/paint.h>
+#include <ui/slider.h>
+#include "../private/resource.h"
+#include "../private/slider.h"
+
+/*
+ * The kind reader will appreciate that slider button dimensions 23:15
+ * are chosen such that, after subtracting the frame width (2 times 1),
+ * we get 21:13, which is a good approximation of the golden ratio.
+ */
+enum {
+	/** Slider button width */
+	ui_slider_btn_w = 15,
+	/** Slider button height */
+	ui_slider_btn_h = 23,
+	/** Slider button frame thickness */
+	ui_slider_btn_frame_thickness = 1,
+	/** Slider button bevel width */
+	ui_slider_btn_bevel_width = 2,
+	/** Slider groove width (total) */
+	ui_slider_groove_width = 4,
+	/** Amount by which slider groove bevel extendeds on each side
+	 * beyond nominal groove length.
+	 */
+	ui_slider_groove_margin = 2
+};
+
+static void ui_slider_ctl_destroy(void *);
+static errno_t ui_slider_ctl_paint(void *);
+static ui_evclaim_t ui_slider_ctl_pos_event(void *, pos_event_t *);
+
+/** Slider control ops */
+ui_control_ops_t ui_slider_ops = {
+	.destroy = ui_slider_ctl_destroy,
+	.paint = ui_slider_ctl_paint,
+	.pos_event = ui_slider_ctl_pos_event
+};
+
+/** Create new slider.
+ *
+ * @param resource UI resource
+ * @param caption Caption
+ * @param rslider Place to store pointer to new slider
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t ui_slider_create(ui_resource_t *resource, const char *caption,
+    ui_slider_t **rslider)
+{
+	ui_slider_t *slider;
+	errno_t rc;
+
+	slider = calloc(1, sizeof(ui_slider_t));
+	if (slider == NULL)
+		return ENOMEM;
+
+	rc = ui_control_new(&ui_slider_ops, (void *) slider,
+	    &slider->control);
+	if (rc != EOK) {
+		free(slider);
+		return rc;
+	}
+
+	slider->res = resource;
+	*rslider = slider;
+	return EOK;
+}
+
+/** Destroy slider.
+ *
+ * @param slider Slider or @c NULL
+ */
+void ui_slider_destroy(ui_slider_t *slider)
+{
+	if (slider == NULL)
+		return;
+
+	ui_control_delete(slider->control);
+	free(slider);
+}
+
+/** Get base control from slider.
+ *
+ * @param slider Slider
+ * @return Control
+ */
+ui_control_t *ui_slider_ctl(ui_slider_t *slider)
+{
+	return slider->control;
+}
+
+/** Set slider callbacks.
+ *
+ * @param slider Slider
+ * @param cb Slider callbacks
+ * @param arg Callback argument
+ */
+void ui_slider_set_cb(ui_slider_t *slider, ui_slider_cb_t *cb, void *arg)
+{
+	slider->cb = cb;
+	slider->arg = arg;
+}
+
+/** Set slider rectangle.
+ *
+ * @param slider Slider
+ * @param rect New slider rectangle
+ */
+void ui_slider_set_rect(ui_slider_t *slider, gfx_rect_t *rect)
+{
+	slider->rect = *rect;
+}
+
+/** Paint outer slider frame.
+ *
+ * @param slider Slider
+ * @return EOK on success or an error code
+ */
+static errno_t ui_slider_paint_frame(ui_resource_t *res, gfx_rect_t *rect,
+    gfx_coord_t thickness, gfx_rect_t *inside)
+{
+	gfx_rect_t drect;
+	errno_t rc;
+
+	rc = gfx_set_color(res->gc, res->btn_frame_color);
+	if (rc != EOK)
+		goto error;
+
+	drect.p0.x = rect->p0.x + 1;
+	drect.p0.y = rect->p0.y;
+	drect.p1.x = rect->p1.x - 1;
+	drect.p1.y = rect->p0.y + thickness;
+	rc = gfx_fill_rect(res->gc, &drect);
+	if (rc != EOK)
+		goto error;
+
+	drect.p0.x = rect->p0.x + 1;
+	drect.p0.y = rect->p1.y - thickness;
+	drect.p1.x = rect->p1.x - 1;
+	drect.p1.y = rect->p1.y;
+	rc = gfx_fill_rect(res->gc, &drect);
+	if (rc != EOK)
+		goto error;
+
+	drect.p0.x = rect->p0.x;
+	drect.p0.y = rect->p0.y + 1;
+	drect.p1.x = rect->p0.x + thickness;
+	drect.p1.y = rect->p1.y - 1;
+	rc = gfx_fill_rect(res->gc, &drect);
+	if (rc != EOK)
+		goto error;
+
+	drect.p0.x = rect->p1.x - thickness;
+	drect.p0.y = rect->p0.y + 1;
+	drect.p1.x = rect->p1.x;
+	drect.p1.y = rect->p1.y - 1;
+	rc = gfx_fill_rect(res->gc, &drect);
+	if (rc != EOK)
+		goto error;
+
+	if (inside != NULL) {
+		inside->p0.x = rect->p0.x + thickness;
+		inside->p0.y = rect->p0.y + thickness;
+		inside->p1.x = rect->p1.x - thickness;
+		inside->p1.y = rect->p1.y - thickness;
+	}
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Paint outset slider bevel.
+ *
+ * @param slider Slider
+ * @return EOK on success or an error code
+ */
+static errno_t ui_slider_paint_outset(ui_slider_t *slider,
+    gfx_rect_t *rect, gfx_rect_t *inside)
+{
+	return ui_paint_bevel(slider->res->gc, rect,
+	    slider->res->btn_highlight_color,
+	    slider->res->btn_shadow_color, ui_slider_btn_bevel_width, inside);
+}
+
+/** Determine slider button rectagle.
+ *
+ * @param slider Slider
+ */
+static void ui_slider_btn_rect(ui_slider_t *slider, gfx_rect_t *rect)
+{
+	gfx_coord2_t pos;
+
+	pos.x = slider->rect.p0.x + slider->pos;
+	pos.y = slider->rect.p0.y;
+
+	rect->p0.x = pos.x;
+	rect->p0.y = pos.y;
+	rect->p1.x = pos.x + ui_slider_btn_w;
+	rect->p1.y = pos.y + ui_slider_btn_h;
+}
+
+/** Determine slider length.
+ *
+ * @param slider Slider
+ * @return Slider length in pixels
+ */
+gfx_coord_t ui_slider_length(ui_slider_t *slider)
+{
+	gfx_coord2_t dims;
+
+	gfx_rect_dims(&slider->rect, &dims);
+	return dims.x - ui_slider_btn_w;
+}
+
+/** Paint slider.
+ *
+ * @param slider Slider
+ * @return EOK on success or an error code
+ */
+errno_t ui_slider_paint(ui_slider_t *slider)
+{
+	gfx_coord2_t pos;
+	gfx_coord_t length;
+	gfx_rect_t rect;
+	gfx_rect_t brect;
+	gfx_rect_t irect;
+	errno_t rc;
+
+	/* Paint slider groove */
+
+	pos = slider->rect.p0;
+	length = ui_slider_length(slider);
+
+	rect.p0.x = pos.x + ui_slider_btn_w / 2 - ui_slider_groove_margin;
+	rect.p0.y = pos.y + ui_slider_btn_h / 2 - ui_slider_groove_width / 2;
+	rect.p1.x = pos.x + ui_slider_btn_w / 2 + length +
+	    ui_slider_groove_margin;
+	rect.p1.y = pos.y + ui_slider_btn_h / 2 + ui_slider_groove_width / 2;
+
+	rc = ui_paint_inset_frame(slider->res, &rect, NULL);
+	if (rc != EOK)
+		goto error;
+
+	/* Paint slider button */
+
+	ui_slider_btn_rect(slider, &rect);
+
+	rc = ui_slider_paint_frame(slider->res, &rect,
+	    ui_slider_btn_frame_thickness, &brect);
+	if (rc != EOK)
+		goto error;
+
+	rc = ui_slider_paint_outset(slider, &brect, &irect);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_set_color(slider->res->gc, slider->res->btn_face_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(slider->res->gc, &irect);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_update(slider->res->gc);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Clear slider button.
+ *
+ * @param slider Slider
+ * @return EOK on success or an error code
+ */
+errno_t ui_slider_btn_clear(ui_slider_t *slider)
+{
+	gfx_rect_t rect;
+	errno_t rc;
+
+	ui_slider_btn_rect(slider, &rect);
+
+	rc = gfx_set_color(slider->res->gc, slider->res->wnd_face_color);
+	if (rc != EOK)
+		goto error;
+
+	rc = gfx_fill_rect(slider->res->gc, &rect);
+	if (rc != EOK)
+		goto error;
+
+	return EOK;
+error:
+	return rc;
+}
+
+/** Press down slider.
+ *
+ * @param slider Slider
+ * @param pos Pointer position
+ */
+void ui_slider_press(ui_slider_t *slider, gfx_coord2_t *pos)
+{
+	if (slider->held)
+		return;
+
+	slider->held = true;
+	slider->press_pos = *pos;
+	slider->last_pos = slider->pos;
+
+	(void) ui_slider_paint(slider);
+}
+
+/** Release slider.
+ *
+ * @param slider Slider
+ * @param pos Pointer position
+ */
+void ui_slider_release(ui_slider_t *slider, gfx_coord2_t *pos)
+{
+	if (!slider->held)
+		return;
+
+	ui_slider_update(slider, pos);
+	slider->held = false;
+}
+
+/** Pointer moved.
+ *
+ * @param slider Slider
+ * @param pos New pointer position
+ */
+void ui_slider_update(ui_slider_t *slider, gfx_coord2_t *pos)
+{
+	gfx_coord_t spos;
+	gfx_coord_t length;
+
+	if (slider->held) {
+		spos = slider->last_pos + pos->x - slider->press_pos.x;
+		length = ui_slider_length(slider);
+		if (spos < 0)
+			spos = 0;
+		if (spos > length)
+			spos = length;
+
+		if (spos != slider->pos) {
+			(void) ui_slider_btn_clear(slider);
+			slider->pos = spos;
+			(void) ui_slider_paint(slider);
+			ui_slider_moved(slider, spos);
+		}
+	}
+}
+
+/** Slider was moved.
+ *
+ * @param slider Slider
+ */
+void ui_slider_moved(ui_slider_t *slider, gfx_coord_t pos)
+{
+	if (slider->cb != NULL && slider->cb->moved != NULL)
+		slider->cb->moved(slider, slider->arg, pos);
+}
+
+/** Handle slider position event.
+ *
+ * @param slider Slider
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_slider_pos_event(ui_slider_t *slider, pos_event_t *event)
+{
+	gfx_coord2_t pos;
+	gfx_rect_t rect;
+
+	pos.x = event->hpos;
+	pos.y = event->vpos;
+
+	switch (event->type) {
+	case POS_PRESS:
+		ui_slider_btn_rect(slider, &rect);
+		if (gfx_pix_inside_rect(&pos, &rect)) {
+			ui_slider_press(slider, &pos);
+			slider->press_pos = pos;
+			return ui_claimed;
+		}
+		break;
+	case POS_RELEASE:
+		if (slider->held) {
+			ui_slider_release(slider, &pos);
+			return ui_claimed;
+		}
+		break;
+	case POS_UPDATE:
+		ui_slider_update(slider, &pos);
+		break;
+	}
+
+	return ui_unclaimed;
+}
+
+/** Destroy slider control.
+ *
+ * @param arg Argument (ui_slider_t *)
+ */
+void ui_slider_ctl_destroy(void *arg)
+{
+	ui_slider_t *slider = (ui_slider_t *) arg;
+
+	ui_slider_destroy(slider);
+}
+
+/** Paint slider control.
+ *
+ * @param arg Argument (ui_slider_t *)
+ * @return EOK on success or an error code
+ */
+errno_t ui_slider_ctl_paint(void *arg)
+{
+	ui_slider_t *slider = (ui_slider_t *) arg;
+
+	return ui_slider_paint(slider);
+}
+
+/** Handle slider control position event.
+ *
+ * @param arg Argument (ui_slider_t *)
+ * @param pos_event Position event
+ * @return @c ui_claimed iff the event is claimed
+ */
+ui_evclaim_t ui_slider_ctl_pos_event(void *arg, pos_event_t *event)
+{
+	ui_slider_t *slider = (ui_slider_t *) arg;
+
+	return ui_slider_pos_event(slider, event);
+}
+
+/** @}
+ */
Index: uspace/lib/ui/src/ui.c
===================================================================
--- uspace/lib/ui/src/ui.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/src/ui.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -34,11 +34,63 @@
  */
 
+#include <adt/list.h>
+#include <ctype.h>
 #include <display.h>
 #include <errno.h>
 #include <fibril.h>
+#include <io/console.h>
+#include <stdbool.h>
 #include <stdlib.h>
+#include <str.h>
 #include <task.h>
 #include <ui/ui.h>
+#include <ui/wdecor.h>
+#include <ui/window.h>
+#include "../private/window.h"
 #include "../private/ui.h"
+
+/** Parse output specification.
+ *
+ * Output specification has the form <proto>@<service> where proto is
+ * eiher 'disp' for display service or 'cons' for console. Service
+ * is a location ID service name (e.g. hid/display).
+ *
+ * @param ospec Output specification
+ * @param ws Place to store window system type (protocol)
+ * @param osvc Place to store pointer to output service name
+ */
+static void ui_ospec_parse(const char *ospec, ui_winsys_t *ws,
+    const char **osvc)
+{
+	const char *cp;
+
+	if (ospec == UI_DISPLAY_DEFAULT) {
+		*ws = ui_ws_display;
+		*osvc = DISPLAY_DEFAULT;
+		return;
+	}
+
+	cp = ospec;
+	while (isalpha(*cp))
+		++cp;
+
+	if (*cp == '@') {
+		if (str_lcmp(ospec, "disp@", str_length("disp@")) == 0) {
+			*ws = ui_ws_display;
+		} else if (str_lcmp(ospec, "cons@", str_length("cons@")) == 0) {
+			*ws = ui_ws_console;
+		} else {
+			*ws = ui_ws_unknown;
+		}
+
+		if (cp[1] != '\0')
+			*osvc = cp + 1;
+		else
+			*osvc = NULL;
+	} else {
+		*ws = ui_ws_display;
+		*osvc = ospec;
+	}
+}
 
 /** Create new user interface.
@@ -53,17 +105,48 @@
 	errno_t rc;
 	display_t *display;
+	console_ctrl_t *console;
+	console_gc_t *cgc;
+	ui_winsys_t ws;
+	const char *osvc;
 	ui_t *ui;
 
-	rc = display_open(ospec, &display);
-	if (rc != EOK)
-		return rc;
-
-	rc = ui_create_disp(display, &ui);
-	if (rc != EOK) {
-		display_close(display);
-		return rc;
-	}
-
-	ui->display = display;
+	ui_ospec_parse(ospec, &ws, &osvc);
+
+	if (ws == ui_ws_display) {
+		rc = display_open(osvc, &display);
+		if (rc != EOK)
+			return rc;
+
+		rc = ui_create_disp(display, &ui);
+		if (rc != EOK) {
+			display_close(display);
+			return rc;
+		}
+	} else if (ws == ui_ws_console) {
+		console = console_init(stdin, stdout);
+		if (console == NULL)
+			return EIO;
+
+		console_cursor_visibility(console, false);
+
+		/* ws == ui_ws_console */
+		rc = ui_create_cons(console, &ui);
+		if (rc != EOK) {
+			console_done(console);
+			return rc;
+		}
+
+		rc = console_gc_create(console, NULL, &cgc);
+		if (rc != EOK) {
+			ui_destroy(ui);
+			console_done(console);
+			return rc;
+		}
+
+		ui->cgc = cgc;
+	} else {
+		return EINVAL;
+	}
+
 	ui->myoutput = true;
 	*rui = ui;
@@ -71,4 +154,23 @@
 }
 
+/** Create new user interface using console service.
+ *
+ * @param rui Place to store pointer to new UI
+ * @return EOK on success or an error code
+ */
+errno_t ui_create_cons(console_ctrl_t *console, ui_t **rui)
+{
+	ui_t *ui;
+
+	ui = calloc(1, sizeof(ui_t));
+	if (ui == NULL)
+		return ENOMEM;
+
+	ui->console = console;
+	list_initialize(&ui->windows);
+	*rui = ui;
+	return EOK;
+}
+
 /** Create new user interface using display service.
  *
@@ -86,4 +188,5 @@
 
 	ui->display = disp;
+	list_initialize(&ui->windows);
 	*rui = ui;
 	return EOK;
@@ -99,7 +202,38 @@
 		return;
 
-	if (ui->myoutput)
-		display_close(ui->display);
+	if (ui->myoutput) {
+		if (ui->cgc != NULL)
+			console_gc_delete(ui->cgc);
+		if (ui->console != NULL) {
+			console_cursor_visibility(ui->console, true);
+			console_done(ui->console);
+		}
+		if (ui->display != NULL)
+			display_close(ui->display);
+	}
+
 	free(ui);
+}
+
+static void ui_cons_event_process(ui_t *ui, cons_event_t *event)
+{
+	ui_window_t *awnd;
+	ui_evclaim_t claim;
+
+	awnd = ui_window_get_active(ui);
+	if (awnd == NULL)
+		return;
+
+	switch (event->type) {
+	case CEV_KEY:
+		ui_window_send_kbd(awnd, &event->ev.key);
+		break;
+	case CEV_POS:
+		claim = ui_wdecor_pos_event(awnd->wdecor, &event->ev.pos);
+		/* Note: If event is claimed, awnd might not be valid anymore */
+		if (claim == ui_unclaimed)
+			ui_window_send_pos(awnd, &event->ev.pos);
+		break;
+	}
 }
 
@@ -113,8 +247,53 @@
 void ui_run(ui_t *ui)
 {
-	task_retval(0);
-
-	while (!ui->quit)
-		fibril_usleep(100000);
+	cons_event_t event;
+	usec_t timeout;
+	errno_t rc;
+
+	/* Only return command prompt if we are running in a separate window */
+	if (ui->display != NULL)
+		task_retval(0);
+
+	while (!ui->quit) {
+		if (ui->console != NULL) {
+			timeout = 100000;
+			rc = console_get_event_timeout(ui->console,
+			    &event, &timeout);
+
+			/* Do we actually have an event? */
+			if (rc == EOK) {
+				ui_cons_event_process(ui, &event);
+			} else if (rc != ETIMEOUT) {
+				/* Error, quit */
+				break;
+			}
+		} else {
+			fibril_usleep(100000);
+		}
+	}
+}
+
+/** Repaint UI (only used in fullscreen mode).
+ *
+ * This is used when an area is exposed in fullscreen mode.
+ *
+ * @param ui UI
+ * @return @c EOK on success or an error code
+ */
+errno_t ui_paint(ui_t *ui)
+{
+	errno_t rc;
+	ui_window_t *awnd;
+
+	/* XXX Should repaint all windows */
+	awnd = ui_window_get_active(ui);
+	if (awnd == NULL)
+		return EOK;
+
+	rc = ui_wdecor_paint(awnd->wdecor);
+	if (rc != EOK)
+		return rc;
+
+	return ui_window_paint(awnd);
 }
 
@@ -132,4 +311,28 @@
 }
 
+/** Determine if we are running in text mode.
+ *
+ * @param ui User interface
+ * @return @c true iff we are running in text mode
+ */
+bool ui_is_textmode(ui_t *ui)
+{
+	/*
+	 * XXX Currently console is always text and display is always
+	 * graphics, but this need not always be true.
+	 */
+	return (ui->console != NULL);
+}
+
+/** Determine if we are emulating windows.
+ *
+ * @param ui User interface
+ * @return @c true iff we are running in text mode
+ */
+bool ui_is_fullscreen(ui_t *ui)
+{
+	return (ui->display == NULL);
+}
+
 /** @}
  */
Index: uspace/lib/ui/src/wdecor.c
===================================================================
--- uspace/lib/ui/src/wdecor.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/src/wdecor.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -59,5 +59,9 @@
 	wdecor_corner_h = 24,
 	wdecor_edge_w = 4,
-	wdecor_edge_h = 4
+	wdecor_edge_h = 4,
+	wdecor_tbar_h = 22,
+	wdecor_tbar_h_text = 1,
+	wdecor_frame_w = 4,
+	wdecor_frame_w_text = 1
 };
 
@@ -174,21 +178,23 @@
 
 	if ((wdecor->style & ui_wds_frame) != 0) {
-		rc = ui_paint_bevel(wdecor->res->gc, &rect,
-		    wdecor->res->wnd_frame_hi_color,
-		    wdecor->res->wnd_frame_sh_color, 1, &rect);
-		if (rc != EOK)
-			return rc;
-
-		rc = ui_paint_bevel(wdecor->res->gc, &rect,
-		    wdecor->res->wnd_highlight_color,
-		    wdecor->res->wnd_shadow_color, 1, &rect);
-		if (rc != EOK)
-			return rc;
-
-		rc = ui_paint_bevel(wdecor->res->gc, &rect,
-		    wdecor->res->wnd_face_color,
-		    wdecor->res->wnd_face_color, 2, &rect);
-		if (rc != EOK)
-			return rc;
+
+		if (wdecor->res->textmode != false) {
+			rc = ui_paint_bevel(wdecor->res->gc, &rect,
+			    wdecor->res->wnd_frame_hi_color,
+			    wdecor->res->wnd_frame_sh_color, 1, &rect);
+			if (rc != EOK)
+				return rc;
+		} else {
+			rc = ui_paint_outset_frame(wdecor->res, &rect,
+			    &rect);
+			if (rc != EOK)
+				return rc;
+
+			rc = ui_paint_bevel(wdecor->res->gc, &rect,
+			    wdecor->res->wnd_face_color,
+			    wdecor->res->wnd_face_color, 2, &rect);
+			if (rc != EOK)
+				return rc;
+		}
 	}
 
@@ -196,9 +202,11 @@
 		trect = geom.title_bar_rect;
 
-		rc = ui_paint_bevel(wdecor->res->gc, &trect,
-		    wdecor->res->wnd_shadow_color,
-		    wdecor->res->wnd_highlight_color, 1, &trect);
-		if (rc != EOK)
-			return rc;
+		if (wdecor->res->textmode == false) {
+			rc = ui_paint_bevel(wdecor->res->gc, &trect,
+			    wdecor->res->wnd_shadow_color,
+			    wdecor->res->wnd_highlight_color, 1, &trect);
+			if (rc != EOK)
+				return rc;
+		}
 
 		rc = gfx_set_color(wdecor->res->gc, wdecor->active ?
@@ -213,4 +221,7 @@
 
 		gfx_text_fmt_init(&fmt);
+		fmt.color = wdecor->active ?
+		    wdecor->res->tbar_act_text_color :
+		    wdecor->res->tbar_inact_text_color;
 		fmt.halign = gfx_halign_center;
 		fmt.valign = gfx_valign_center;
@@ -218,10 +229,4 @@
 		pos.x = (trect.p0.x + trect.p1.x) / 2;
 		pos.y = (trect.p0.y + trect.p1.y) / 2;
-
-		rc = gfx_set_color(wdecor->res->gc, wdecor->active ?
-		    wdecor->res->tbar_act_text_color :
-		    wdecor->res->tbar_inact_text_color);
-		if (rc != EOK)
-			return rc;
 
 		rc = gfx_puttext(wdecor->res->font, &pos, &fmt, wdecor->caption);
@@ -236,4 +241,8 @@
 	}
 
+	rc = gfx_update(wdecor->res->gc);
+	if (rc != EOK)
+		return rc;
+
 	return EOK;
 }
@@ -291,10 +300,16 @@
 void ui_wdecor_get_geom(ui_wdecor_t *wdecor, ui_wdecor_geom_t *geom)
 {
+	gfx_coord_t frame_w;
+	gfx_coord_t tbar_h;
+
 	/* Does window have a frame? */
 	if ((wdecor->style & ui_wds_frame) != 0) {
-		geom->interior_rect.p0.x = wdecor->rect.p0.x + 4;
-		geom->interior_rect.p0.y = wdecor->rect.p0.y + 4;
-		geom->interior_rect.p1.x = wdecor->rect.p1.x - 4;
-		geom->interior_rect.p1.y = wdecor->rect.p1.y - 4;
+		frame_w = wdecor->res->textmode ?
+		    wdecor_frame_w_text : wdecor_frame_w;
+
+		geom->interior_rect.p0.x = wdecor->rect.p0.x + frame_w;
+		geom->interior_rect.p0.y = wdecor->rect.p0.y + frame_w;
+		geom->interior_rect.p1.x = wdecor->rect.p1.x - frame_w;
+		geom->interior_rect.p1.y = wdecor->rect.p1.y - frame_w;
 	} else {
 		geom->interior_rect = wdecor->rect;
@@ -303,7 +318,10 @@
 	/* Does window have a title bar? */
 	if ((wdecor->style & ui_wds_titlebar) != 0) {
+		tbar_h = wdecor->res->textmode ?
+		    wdecor_tbar_h_text : wdecor_tbar_h;
+
 		geom->title_bar_rect.p0 = geom->interior_rect.p0;
 		geom->title_bar_rect.p1.x = geom->interior_rect.p1.x;
-		geom->title_bar_rect.p1.y = geom->interior_rect.p0.y + 22;
+		geom->title_bar_rect.p1.y = geom->interior_rect.p0.y + tbar_h;
 
 		geom->app_area_rect.p0.x = geom->interior_rect.p0.x;
@@ -321,8 +339,23 @@
 	/* Does window have a close button? */
 	if ((wdecor->style & ui_wds_close_btn) != 0) {
-		geom->btn_close_rect.p0.x = geom->title_bar_rect.p1.x - 1 - 20;
-		geom->btn_close_rect.p0.y = geom->title_bar_rect.p0.y + 1;
-		geom->btn_close_rect.p1.x = geom->title_bar_rect.p1.x - 1;
-		geom->btn_close_rect.p1.y = geom->title_bar_rect.p0.y + 1 + 20;
+		if (wdecor->res->textmode == false) {
+			geom->btn_close_rect.p0.x =
+			    geom->title_bar_rect.p1.x - 1 - 20;
+			geom->btn_close_rect.p0.y =
+			    geom->title_bar_rect.p0.y + 1;
+			geom->btn_close_rect.p1.x =
+			    geom->title_bar_rect.p1.x - 1;
+			geom->btn_close_rect.p1.y =
+			    geom->title_bar_rect.p0.y + 1 + 20;
+		} else {
+			geom->btn_close_rect.p0.x =
+			    geom->title_bar_rect.p1.x - 1 - 3;
+			geom->btn_close_rect.p0.y =
+			    geom->title_bar_rect.p0.y;
+			geom->btn_close_rect.p1.x =
+			    geom->title_bar_rect.p1.x - 1;
+			geom->btn_close_rect.p1.y =
+			    geom->title_bar_rect.p0.y + 1;
+		}
 	} else {
 		geom->btn_close_rect.p0.x = 0;
@@ -523,6 +556,7 @@
  * @param wdecor Window decoration
  * @param pos_event Position event
- */
-void ui_wdecor_pos_event(ui_wdecor_t *wdecor, pos_event_t *event)
+ * @return @c ui_claimed iff event was claimed
+ */
+ui_evclaim_t ui_wdecor_pos_event(ui_wdecor_t *wdecor, pos_event_t *event)
 {
 	gfx_coord2_t pos;
@@ -538,14 +572,18 @@
 		claim = ui_pbutton_pos_event(wdecor->btn_close, event);
 		if (claim == ui_claimed)
-			return;
+			return ui_claimed;
 	}
 
 	ui_wdecor_frame_pos_event(wdecor, event);
 
-	if ((wdecor->style & ui_wds_titlebar) != 0)  {
+	if ((wdecor->style & ui_wds_titlebar) != 0) {
 		if (event->type == POS_PRESS &&
-		    gfx_pix_inside_rect(&pos, &geom.title_bar_rect))
+		    gfx_pix_inside_rect(&pos, &geom.title_bar_rect)) {
 			ui_wdecor_move(wdecor, &pos);
-	}
+			return ui_claimed;
+		}
+	}
+
+	return ui_unclaimed;
 }
 
Index: uspace/lib/ui/src/window.c
===================================================================
--- uspace/lib/ui/src/window.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/src/window.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -34,8 +34,10 @@
  */
 
+#include <congfx/console.h>
 #include <display.h>
 #include <errno.h>
 #include <gfx/bitmap.h>
 #include <gfx/context.h>
+#include <gfx/cursor.h>
 #include <gfx/render.h>
 #include <io/kbd_event.h>
@@ -46,4 +48,5 @@
 #include <ui/control.h>
 #include <ui/resource.h>
+#include <ui/ui.h>
 #include <ui/wdecor.h>
 #include <ui/window.h>
@@ -84,5 +87,29 @@
 };
 
-static void ui_window_app_update(void *, gfx_rect_t *);
+static void ui_window_invalidate(void *, gfx_rect_t *);
+static void ui_window_update(void *);
+static errno_t ui_window_cursor_get_pos(void *, gfx_coord2_t *);
+static errno_t ui_window_cursor_set_pos(void *, gfx_coord2_t *);
+static errno_t ui_window_cursor_set_visible(void *, bool);
+
+/** Window memory GC callbacks */
+static mem_gc_cb_t ui_window_mem_gc_cb = {
+	.invalidate = ui_window_invalidate,
+	.update = ui_window_update,
+	.cursor_get_pos = ui_window_cursor_get_pos,
+	.cursor_set_pos = ui_window_cursor_set_pos,
+	.cursor_set_visible = ui_window_cursor_set_visible
+};
+
+static void ui_window_app_invalidate(void *, gfx_rect_t *);
+static void ui_window_app_update(void *);
+
+/** Application area memory GC callbacks */
+static mem_gc_cb_t ui_window_app_mem_gc_cb = {
+	.invalidate = ui_window_app_invalidate,
+	.update = ui_window_app_update
+};
+
+static void ui_window_expose_cb(void *);
 
 /** Initialize window parameters structure.
@@ -103,4 +130,50 @@
 }
 
+/** Compute where window should be placed on the screen.
+ *
+ * This only applies to windows that do not use default placement.
+ *
+ * @param window Window
+ * @param display Display
+ * @param info Display info
+ * @param params Window parameters
+ * @param pos Place to store position of top-left corner
+ */
+static void ui_window_place(ui_window_t *window, display_t *display,
+    display_info_t *info, ui_wnd_params_t *params, gfx_coord2_t *pos)
+{
+	assert(params->placement != ui_wnd_place_default);
+
+	pos->x = 0;
+	pos->y = 0;
+
+	switch (params->placement) {
+	case ui_wnd_place_default:
+		assert(false);
+	case ui_wnd_place_top_left:
+	case ui_wnd_place_full_screen:
+		pos->x = info->rect.p0.x - params->rect.p0.x;
+		pos->y = info->rect.p0.y - params->rect.p0.y;
+		break;
+	case ui_wnd_place_top_right:
+		pos->x = info->rect.p1.x - params->rect.p1.x;
+		pos->y = info->rect.p0.y - params->rect.p0.y;
+		break;
+	case ui_wnd_place_bottom_left:
+		pos->x = info->rect.p0.x - params->rect.p0.x;
+		pos->y = info->rect.p1.y - params->rect.p1.y;
+		break;
+	case ui_wnd_place_bottom_right:
+		pos->x = info->rect.p1.x - params->rect.p1.x;
+		pos->y = info->rect.p1.y - params->rect.p1.y;
+		break;
+	case ui_wnd_place_popup:
+		/* Place popup window below parent rectangle */
+		pos->x = params->prect.p0.x;
+		pos->y = params->prect.p1.y;
+		break;
+	}
+}
+
 /** Create new window.
  *
@@ -115,12 +188,14 @@
 	ui_window_t *window;
 	display_info_t info;
-	gfx_coord2_t pos;
 	gfx_coord2_t scr_dims;
 	display_wnd_params_t dparams;
-	display_window_t *dwindow = NULL;
 	gfx_context_t *gc = NULL;
 	ui_resource_t *res = NULL;
 	ui_wdecor_t *wdecor = NULL;
 	dummy_gc_t *dgc = NULL;
+	gfx_bitmap_params_t bparams;
+	gfx_bitmap_alloc_t alloc;
+	gfx_bitmap_t *bmp = NULL;
+	mem_gc_t *memgc = NULL;
 	errno_t rc;
 
@@ -133,4 +208,7 @@
 	/* Only allow making the window larger */
 	gfx_rect_dims(&params->rect, &dparams.min_size);
+
+	if ((params->flags & ui_wndf_popup) != 0)
+		dparams.flags |= wndf_popup;
 
 	if (ui->display != NULL) {
@@ -148,43 +226,22 @@
 		}
 
+		if (params->placement != ui_wnd_place_default) {
+			/* Set initial display window position */
+			ui_window_place(window, ui->display, &info,
+			    params, &dparams.pos);
+
+			dparams.flags |= wndf_setpos;
+		}
+
 		rc = display_window_create(ui->display, &dparams, &dwnd_cb,
-		    (void *) window, &dwindow);
+		    (void *) window, &window->dwindow);
 		if (rc != EOK)
 			goto error;
 
-		if (params->placement != ui_wnd_place_default) {
-			pos.x = 0;
-			pos.y = 0;
-
-			switch (params->placement) {
-			case ui_wnd_place_default:
-				assert(false);
-			case ui_wnd_place_top_left:
-			case ui_wnd_place_full_screen:
-				pos.x = info.rect.p0.x - params->rect.p0.x;
-				pos.y = info.rect.p0.y - params->rect.p0.y;
-				break;
-			case ui_wnd_place_top_right:
-				pos.x = info.rect.p1.x - params->rect.p1.x;
-				pos.y = info.rect.p0.y - params->rect.p0.y;
-				break;
-			case ui_wnd_place_bottom_left:
-				pos.x = info.rect.p0.x - params->rect.p0.x;
-				pos.y = info.rect.p1.y - params->rect.p1.y;
-				break;
-			case ui_wnd_place_bottom_right:
-				pos.x = info.rect.p1.x - params->rect.p1.x;
-				pos.y = info.rect.p1.y - params->rect.p1.y;
-				break;
-			}
-
-			rc = display_window_move(dwindow, &pos);
-			if (rc != EOK)
-				goto error;
-		}
-
-		rc = display_window_get_gc(dwindow, &gc);
+		rc = display_window_get_gc(window->dwindow, &gc);
 		if (rc != EOK)
 			goto error;
+	} else if (ui->console != NULL) {
+		gc = console_gc_get_ctx(ui->cgc);
 	} else {
 		/* Needed for unit tests */
@@ -196,5 +253,46 @@
 	}
 
-	rc = ui_resource_create(gc, &res);
+#ifdef CONFIG_UI_CS_RENDER
+	/* Create window bitmap */
+	gfx_bitmap_params_init(&bparams);
+#ifndef CONFIG_WIN_DOUBLE_BUF
+	/* Console does not support direct output */
+	if (ui->display != NULL)
+		bparams.flags |= bmpf_direct_output;
+#endif
+
+	/* Move rectangle so that top-left corner is 0,0 */
+	gfx_rect_rtranslate(&params->rect.p0, &params->rect, &bparams.rect);
+
+	rc = gfx_bitmap_create(gc, &bparams, NULL, &bmp);
+	if (rc != EOK)
+		goto error;
+
+	/* Create memory GC */
+	rc = gfx_bitmap_get_alloc(bmp, &alloc);
+	if (rc != EOK) {
+		gfx_bitmap_destroy(window->app_bmp);
+		return rc;
+	}
+
+	rc = mem_gc_create(&bparams.rect, &alloc, &ui_window_mem_gc_cb,
+	    (void *) window, &memgc);
+	if (rc != EOK) {
+		gfx_bitmap_destroy(window->app_bmp);
+		return rc;
+	}
+
+	window->bmp = bmp;
+	window->mgc = memgc;
+	window->gc = mem_gc_get_ctx(memgc);
+	window->realgc = gc;
+#else
+	(void) ui_window_mem_gc_cb;
+	(void) alloc;
+	(void) bparams;
+	window->gc = gc;
+#endif
+
+	rc = ui_resource_create(window->gc, ui_is_textmode(ui), &res);
 	if (rc != EOK)
 		goto error;
@@ -208,12 +306,15 @@
 	ui_wdecor_paint(wdecor);
 
+	ui_resource_set_expose_cb(res, ui_window_expose_cb, (void *) window);
+
 	window->ui = ui;
-	window->dwindow = dwindow;
 	window->rect = dparams.rect;
-	window->gc = gc;
+
 	window->res = res;
 	window->wdecor = wdecor;
 	window->cursor = ui_curs_arrow;
 	*rwindow = window;
+
+	list_append(&window->lwindows, &ui->windows);
 	return EOK;
 error:
@@ -222,8 +323,10 @@
 	if (res != NULL)
 		ui_resource_destroy(res);
+	if (memgc != NULL)
+		mem_gc_delete(memgc);
+	if (bmp != NULL)
+		gfx_bitmap_destroy(bmp);
 	if (dgc != NULL)
 		dummygc_destroy(dgc);
-	if (dwindow != NULL)
-		display_window_destroy(dwindow);
 	free(window);
 	return rc;
@@ -236,13 +339,35 @@
 void ui_window_destroy(ui_window_t *window)
 {
+	ui_t *ui;
+
 	if (window == NULL)
 		return;
 
+	ui = window->ui;
+
+	list_remove(&window->lwindows);
 	ui_control_destroy(window->control);
 	ui_wdecor_destroy(window->wdecor);
 	ui_resource_destroy(window->res);
+	if (0 && window->app_mgc != NULL)
+		mem_gc_delete(window->app_mgc);
+	if (0 && window->app_bmp != NULL)
+		gfx_bitmap_destroy(window->app_bmp);
+	if (window->mgc != NULL) {
+		mem_gc_delete(window->mgc);
+		window->gc = NULL;
+	}
+	if (window->bmp != NULL)
+		gfx_bitmap_destroy(window->bmp);
 	gfx_context_delete(window->gc);
-	display_window_destroy(window->dwindow);
+	if (window->dwindow != NULL)
+		display_window_destroy(window->dwindow);
+
 	free(window);
+
+	/* Need to repaint if windows are emulated */
+	if (ui_is_fullscreen(ui)) {
+		ui_paint(ui);
+	}
 }
 
@@ -252,5 +377,5 @@
  * is added, the results are undefined.
  *
- * @param fixed Fixed layout
+ * @param window Window
  * @param control Control
  * @return EOK on success, ENOMEM if out of memory
@@ -276,4 +401,20 @@
 	window->control = NULL;
 	control->elemp = NULL;
+}
+
+/** Get active window (only valid in fullscreen mode).
+ *
+ * @param ui User interface
+ * @return Active window
+ */
+ui_window_t *ui_window_get_active(ui_t *ui)
+{
+	link_t *link;
+
+	link = list_last(&ui->windows);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, ui_window_t, lwindows);
 }
 
@@ -294,6 +435,9 @@
 	gfx_rect_t arect;
 	gfx_bitmap_t *app_bmp = NULL;
-	gfx_bitmap_params_t params;
-	gfx_bitmap_alloc_t alloc;
+	gfx_bitmap_t *win_bmp = NULL;
+	gfx_bitmap_params_t app_params;
+	gfx_bitmap_params_t win_params;
+	gfx_bitmap_alloc_t app_alloc;
+	gfx_bitmap_alloc_t win_alloc;
 	errno_t rc;
 
@@ -305,8 +449,32 @@
 	gfx_rect_rtranslate(&offs, rect, &nrect);
 
+	/* mgc != NULL iff client-side rendering */
+	if (window->mgc != NULL) {
+#ifdef CONFIG_WIN_DOUBLE_BUF
+		/*
+		 * Create new window bitmap in advance. If direct mapping,
+		 * will need do it after resizing the window.
+		 */
+		assert(window->bmp != NULL);
+		gfx_bitmap_params_init(&win_params);
+		win_params.rect = nrect;
+
+		rc = gfx_bitmap_create(window->realgc, &win_params, NULL,
+		    &win_bmp);
+		if (rc != EOK)
+			goto error;
+
+		rc = gfx_bitmap_get_alloc(win_bmp, &win_alloc);
+		if (rc != EOK)
+			goto error;
+#endif
+	}
+
+	/* Application area GC? */
 	if (window->app_gc != NULL) {
+		/* Resize application bitmap */
 		assert(window->app_bmp != NULL);
 
-		gfx_bitmap_params_init(&params);
+		gfx_bitmap_params_init(&app_params);
 
 		/*
@@ -315,12 +483,12 @@
 		 */
 		ui_wdecor_app_from_rect(window->wdecor->style, &nrect, &arect);
-		gfx_rect_rtranslate(&arect.p0, &arect, &params.rect);
-
-		rc = gfx_bitmap_create(window->gc, &params, NULL,
+		gfx_rect_rtranslate(&arect.p0, &arect, &app_params.rect);
+
+		rc = gfx_bitmap_create(window->gc, &app_params, NULL,
 		    &app_bmp);
 		if (rc != EOK)
 			goto error;
 
-		rc = gfx_bitmap_get_alloc(app_bmp, &alloc);
+		rc = gfx_bitmap_get_alloc(app_bmp, &app_alloc);
 		if (rc != EOK)
 			goto error;
@@ -334,9 +502,35 @@
 	}
 
+	/* Client side rendering? */
+	if (window->mgc != NULL) {
+#ifndef CONFIG_WIN_DOUBLE_BUF
+		/* Window is resized, now we can map the window bitmap again */
+		gfx_bitmap_params_init(&win_params);
+		win_params.flags |= bmpf_direct_output;
+		win_params.rect = nrect;
+
+		rc = gfx_bitmap_create(window->realgc, &win_params, NULL,
+		    &win_bmp);
+		if (rc != EOK)
+			goto error;
+
+		rc = gfx_bitmap_get_alloc(win_bmp, &win_alloc);
+		if (rc != EOK)
+			goto error;
+#endif
+
+		mem_gc_retarget(window->mgc, &win_params.rect, &win_alloc);
+
+		gfx_bitmap_destroy(window->bmp);
+		window->bmp = win_bmp;
+	}
+
 	ui_wdecor_set_rect(window->wdecor, &nrect);
 	ui_wdecor_paint(window->wdecor);
-
+	gfx_update(window->gc);
+
+	/* Application area GC? */
 	if (window->app_gc != NULL) {
-		mem_gc_retarget(window->app_mgc, &params.rect, &alloc);
+		mem_gc_retarget(window->app_mgc, &app_params.rect, &app_alloc);
 
 		gfx_bitmap_destroy(window->app_bmp);
@@ -348,4 +542,6 @@
 	if (app_bmp != NULL)
 		gfx_bitmap_destroy(app_bmp);
+	if (win_bmp != NULL)
+		gfx_bitmap_destroy(win_bmp);
 	return rc;
 }
@@ -354,5 +550,5 @@
  *
  * @param window Window
- * @param cb Window decoration callbacks
+ * @param cb Window callbacks
  * @param arg Callback argument
  */
@@ -381,4 +577,26 @@
 {
 	return window->gc;
+}
+
+/** Get window position.
+ *
+ * @param window Window
+ * @param pos Place to store position
+ * @return EOK on success or an error code
+ */
+errno_t ui_window_get_pos(ui_window_t *window, gfx_coord2_t *pos)
+{
+	errno_t rc;
+
+	if (window->dwindow != NULL) {
+		rc = display_window_get_pos(window->dwindow, pos);
+		if (rc != EOK)
+			return rc;
+	} else {
+		pos->x = 0;
+		pos->y = 0;
+	}
+
+	return EOK;
 }
 
@@ -420,6 +638,6 @@
 		}
 
-		rc = mem_gc_create(&params.rect, &alloc, ui_window_app_update,
-		    (void *) window, &memgc);
+		rc = mem_gc_create(&params.rect, &alloc,
+		    &ui_window_app_mem_gc_cb, (void *) window, &memgc);
 		if (rc != EOK) {
 			gfx_bitmap_destroy(window->app_bmp);
@@ -448,4 +666,19 @@
 }
 
+/** Set cursor when pointer is hovering over a control.
+ *
+ * @param window Window
+ * @param cursor Cursor
+ */
+void ui_window_set_ctl_cursor(ui_window_t *window, ui_stock_cursor_t cursor)
+{
+	display_stock_cursor_t dcursor;
+
+	dcursor = wnd_dcursor_from_cursor(cursor);
+
+	if (window->dwindow != NULL)
+		(void) display_window_set_cursor(window->dwindow, dcursor);
+}
+
 /** Paint window
  *
@@ -552,5 +785,6 @@
 	ui_window_t *window = (ui_window_t *) arg;
 
-	(void) display_window_move_req(window->dwindow, pos);
+	if (window->dwindow != NULL)
+		(void) display_window_move_req(window->dwindow, pos);
 }
 
@@ -567,21 +801,16 @@
 	ui_window_t *window = (ui_window_t *) arg;
 
-	(void) display_window_resize_req(window->dwindow, rsztype, pos);
-}
-
-/** Window decoration requested changing cursor.
- *
- * @param wdecor Window decoration
- * @param arg Argument (window)
- * @param cursor Cursor to set
- */
-static void wd_set_cursor(ui_wdecor_t *wdecor, void *arg,
-    ui_stock_cursor_t cursor)
-{
-	ui_window_t *window = (ui_window_t *) arg;
+	if (window->dwindow != NULL)
+		(void) display_window_resize_req(window->dwindow, rsztype, pos);
+}
+
+/** Get display stock cursor from UI stock cursor.
+ *
+ * @param cursor UI stock cursor
+ * @return Display stock cursor
+ */
+display_stock_cursor_t wnd_dcursor_from_cursor(ui_stock_cursor_t cursor)
+{
 	display_stock_cursor_t dcursor;
-
-	if (cursor == window->cursor)
-		return;
 
 	dcursor = dcurs_arrow;
@@ -603,7 +832,32 @@
 		dcursor = dcurs_size_urdl;
 		break;
-	}
-
-	(void) display_window_set_cursor(window->dwindow, dcursor);
+	case ui_curs_ibeam:
+		dcursor = dcurs_ibeam;
+		break;
+	}
+
+	return dcursor;
+}
+
+/** Window decoration requested changing cursor.
+ *
+ * @param wdecor Window decoration
+ * @param arg Argument (window)
+ * @param cursor Cursor to set
+ */
+static void wd_set_cursor(ui_wdecor_t *wdecor, void *arg,
+    ui_stock_cursor_t cursor)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+	display_stock_cursor_t dcursor;
+
+	if (cursor == window->cursor)
+		return;
+
+	dcursor = wnd_dcursor_from_cursor(cursor);
+
+	if (window->dwindow != NULL)
+		(void) display_window_set_cursor(window->dwindow, dcursor);
+
 	window->cursor = cursor;
 }
@@ -637,4 +891,6 @@
 	if (window->cb != NULL && window->cb->kbd != NULL)
 		window->cb->kbd(window, window->arg, kbd);
+	else
+		return ui_window_def_kbd(window, kbd);
 }
 
@@ -671,4 +927,16 @@
 	if (window->cb != NULL && window->cb->unfocus != NULL)
 		window->cb->unfocus(window, window->arg);
+	else
+		return ui_window_def_unfocus(window);
+}
+
+/** Default window keyboard event routine.
+ *
+ * @param window Window
+ */
+void ui_window_def_kbd(ui_window_t *window, kbd_event_t *kbd)
+{
+	if (window->control != NULL)
+		ui_control_kbd_event(window->control, kbd);
 }
 
@@ -696,4 +964,8 @@
 		return ui_control_paint(window->control);
 
+	rc = gfx_update(window->res->gc);
+	if (rc != EOK)
+		return rc;
+
 	return EOK;
 }
@@ -702,5 +974,4 @@
  *
  * @param window Window
- * @return EOK on success or an error code
  */
 void ui_window_def_pos(ui_window_t *window, pos_event_t *pos)
@@ -710,10 +981,88 @@
 }
 
-/** Application area update callback
+/** Default window unfocus routine.
+ *
+ * @param window Window
+ * @return EOK on success or an error code
+ */
+void ui_window_def_unfocus(ui_window_t *window)
+{
+	if (window->control != NULL)
+		ui_control_unfocus(window->control);
+}
+
+/** Window invalidate callback
  *
  * @param arg Argument (ui_window_t *)
  * @param rect Rectangle to update
  */
-static void ui_window_app_update(void *arg, gfx_rect_t *rect)
+static void ui_window_invalidate(void *arg, gfx_rect_t *rect)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+	gfx_rect_t env;
+
+	gfx_rect_envelope(&window->dirty_rect, rect, &env);
+	window->dirty_rect = env;
+}
+
+/** Window update callback
+ *
+ * @param arg Argument (ui_window_t *)
+ */
+static void ui_window_update(void *arg)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	if (!gfx_rect_is_empty(&window->dirty_rect))
+		(void) gfx_bitmap_render(window->bmp, &window->dirty_rect, NULL);
+
+	window->dirty_rect.p0.x = 0;
+	window->dirty_rect.p0.y = 0;
+	window->dirty_rect.p1.x = 0;
+	window->dirty_rect.p1.y = 0;
+}
+
+/** Window cursor get position callback
+ *
+ * @param arg Argument (ui_window_t *)
+ * @param pos Place to store position
+ */
+static errno_t ui_window_cursor_get_pos(void *arg, gfx_coord2_t *pos)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	return gfx_cursor_get_pos(window->realgc, pos);
+}
+
+/** Window cursor set position callback
+ *
+ * @param arg Argument (ui_window_t *)
+ * @param pos New position
+ */
+static errno_t ui_window_cursor_set_pos(void *arg, gfx_coord2_t *pos)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	return gfx_cursor_set_pos(window->realgc, pos);
+}
+
+/** Window cursor set visibility callback
+ *
+ * @param arg Argument (ui_window_t *)
+ * @param visible @c true iff cursor is to be made visible
+ */
+static errno_t ui_window_cursor_set_visible(void *arg, bool visible)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	return gfx_cursor_set_visible(window->realgc, visible);
+}
+
+/** Application area invalidate callback
+ *
+ * @param arg Argument (ui_window_t *)
+ * @param rect Rectangle to update
+ */
+static void ui_window_app_invalidate(void *arg, gfx_rect_t *rect)
 {
 	ui_window_t *window = (ui_window_t *) arg;
@@ -724,4 +1073,32 @@
 	/* Render bitmap rectangle inside the application area */
 	(void) gfx_bitmap_render(window->app_bmp, rect, &arect.p0);
+	/*
+	 * TODO Update applications to call gfx_update(), then
+	 * we can defer update to ui_window_app_update().
+	 */
+	(void) gfx_update(window->res->gc);
+}
+
+/** Application area update callback
+ *
+ * @param arg Argument (ui_window_t *)
+ */
+static void ui_window_app_update(void *arg)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	/*
+	 * Not used since display is updated immediately
+	 * in ui_window_app_invalidate
+	 */
+	(void) window;
+}
+
+/** Window expose callback. */
+static void ui_window_expose_cb(void *arg)
+{
+	ui_window_t *window = (ui_window_t *) arg;
+
+	ui_window_paint(window);
 }
 
Index: uspace/lib/ui/test/checkbox.c
===================================================================
--- uspace/lib/ui/test/checkbox.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/test/checkbox.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,584 @@
+/*
+ * Copyright (c) 2021 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 <gfx/context.h>
+#include <gfx/coord.h>
+#include <mem.h>
+#include <pcut/pcut.h>
+#include <stdbool.h>
+#include <ui/control.h>
+#include <ui/checkbox.h>
+#include <ui/resource.h>
+#include "../private/checkbox.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(checkbox);
+
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
+static errno_t testgc_set_color(void *, gfx_color_t *);
+static errno_t testgc_fill_rect(void *, gfx_rect_t *);
+static errno_t testgc_update(void *);
+static errno_t testgc_bitmap_create(void *, gfx_bitmap_params_t *,
+    gfx_bitmap_alloc_t *, void **);
+static errno_t testgc_bitmap_destroy(void *);
+static errno_t testgc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
+static errno_t testgc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
+
+static gfx_context_ops_t ops = {
+	.set_clip_rect = testgc_set_clip_rect,
+	.set_color = testgc_set_color,
+	.fill_rect = testgc_fill_rect,
+	.update = testgc_update,
+	.bitmap_create = testgc_bitmap_create,
+	.bitmap_destroy = testgc_bitmap_destroy,
+	.bitmap_render = testgc_bitmap_render,
+	.bitmap_get_alloc = testgc_bitmap_get_alloc
+};
+
+static void test_checkbox_switched(ui_checkbox_t *, void *, bool);
+
+static ui_checkbox_cb_t test_checkbox_cb = {
+	.switched = test_checkbox_switched
+};
+
+static ui_checkbox_cb_t dummy_checkbox_cb = {
+};
+
+typedef struct {
+	bool bm_created;
+	bool bm_destroyed;
+	gfx_bitmap_params_t bm_params;
+	void *bm_pixels;
+	gfx_rect_t bm_srect;
+	gfx_coord2_t bm_offs;
+	bool bm_rendered;
+	bool bm_got_alloc;
+} test_gc_t;
+
+typedef struct {
+	test_gc_t *tgc;
+	gfx_bitmap_alloc_t alloc;
+	bool myalloc;
+} testgc_bitmap_t;
+
+typedef struct {
+	bool switched;
+} test_cb_resp_t;
+
+/** Create and destroy check box */
+PCUT_TEST(create_destroy)
+{
+	ui_checkbox_t *checkbox = NULL;
+	errno_t rc;
+
+	rc = ui_checkbox_create(NULL, "Hello", &checkbox);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(checkbox);
+
+	ui_checkbox_destroy(checkbox);
+}
+
+/** ui_checkbox_destroy() can take NULL argument (no-op) */
+PCUT_TEST(destroy_null)
+{
+	ui_checkbox_destroy(NULL);
+}
+
+/** ui_checkbox_ctl() returns control that has a working virtual destructor */
+PCUT_TEST(ctl)
+{
+	ui_checkbox_t *checkbox;
+	ui_control_t *control;
+	errno_t rc;
+
+	rc = ui_checkbox_create(NULL, "Hello", &checkbox);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	control = ui_checkbox_ctl(checkbox);
+	PCUT_ASSERT_NOT_NULL(control);
+
+	ui_control_destroy(control);
+}
+
+/** Set check box rectangle sets internal field */
+PCUT_TEST(set_rect)
+{
+	ui_checkbox_t *checkbox;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rc = ui_checkbox_create(NULL, "Hello", &checkbox);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+
+	ui_checkbox_set_rect(checkbox, &rect);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.x, checkbox->rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.y, checkbox->rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.x, checkbox->rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.y, checkbox->rect.p1.y);
+
+	ui_checkbox_destroy(checkbox);
+}
+
+/** Paint check box */
+PCUT_TEST(paint)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_checkbox_t *checkbox;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_checkbox_create(resource, "Hello", &checkbox);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_checkbox_paint(checkbox);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_checkbox_destroy(checkbox);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Test ui_checkbox_switched() */
+PCUT_TEST(switched)
+{
+	errno_t rc;
+	ui_checkbox_t *checkbox;
+	test_cb_resp_t resp;
+
+	rc = ui_checkbox_create(NULL, "Hello", &checkbox);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Switched with no callbacks set */
+	ui_checkbox_switched(checkbox);
+
+	/* Switched with callback not implementing switched */
+	ui_checkbox_set_cb(checkbox, &dummy_checkbox_cb, NULL);
+	ui_checkbox_switched(checkbox);
+
+	/* Switched with real callback set */
+	resp.switched = false;
+	ui_checkbox_set_cb(checkbox, &test_checkbox_cb, &resp);
+	ui_checkbox_switched(checkbox);
+	PCUT_ASSERT_TRUE(resp.switched);
+
+	ui_checkbox_destroy(checkbox);
+}
+
+/** Press and release check box */
+PCUT_TEST(press_release)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_checkbox_t *checkbox;
+	test_cb_resp_t resp;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_checkbox_create(resource, "Hello", &checkbox);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_FALSE(checkbox->checked);
+
+	resp.switched = false;
+	ui_checkbox_set_cb(checkbox, &test_checkbox_cb, &resp);
+
+	PCUT_ASSERT_FALSE(checkbox->held);
+	PCUT_ASSERT_FALSE(checkbox->inside);
+
+	ui_checkbox_press(checkbox);
+	PCUT_ASSERT_TRUE(checkbox->held);
+	PCUT_ASSERT_TRUE(checkbox->inside);
+	PCUT_ASSERT_FALSE(resp.switched);
+	PCUT_ASSERT_FALSE(checkbox->checked);
+
+	ui_checkbox_release(checkbox);
+	PCUT_ASSERT_FALSE(checkbox->held);
+	PCUT_ASSERT_TRUE(checkbox->inside);
+	PCUT_ASSERT_TRUE(resp.switched);
+	PCUT_ASSERT_TRUE(checkbox->checked);
+
+	ui_checkbox_destroy(checkbox);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Press, leave and release check box */
+PCUT_TEST(press_leave_release)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_checkbox_t *checkbox;
+	test_cb_resp_t resp;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_checkbox_create(resource, "Hello", &checkbox);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	resp.switched = false;
+	ui_checkbox_set_cb(checkbox, &test_checkbox_cb, &resp);
+
+	PCUT_ASSERT_FALSE(checkbox->held);
+	PCUT_ASSERT_FALSE(checkbox->inside);
+
+	ui_checkbox_press(checkbox);
+	PCUT_ASSERT_TRUE(checkbox->held);
+	PCUT_ASSERT_TRUE(checkbox->inside);
+	PCUT_ASSERT_FALSE(resp.switched);
+	PCUT_ASSERT_FALSE(checkbox->checked);
+
+	ui_checkbox_leave(checkbox);
+	PCUT_ASSERT_TRUE(checkbox->held);
+	PCUT_ASSERT_FALSE(checkbox->inside);
+	PCUT_ASSERT_FALSE(resp.switched);
+	PCUT_ASSERT_FALSE(checkbox->checked);
+
+	ui_checkbox_release(checkbox);
+	PCUT_ASSERT_FALSE(checkbox->held);
+	PCUT_ASSERT_FALSE(checkbox->inside);
+	PCUT_ASSERT_FALSE(resp.switched);
+	PCUT_ASSERT_FALSE(checkbox->checked);
+
+	ui_checkbox_destroy(checkbox);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Press, leave, enter and release check box */
+PCUT_TEST(press_leave_enter_release)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_checkbox_t *checkbox;
+	test_cb_resp_t resp;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_checkbox_create(resource, "Hello", &checkbox);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_FALSE(checkbox->checked);
+
+	resp.switched = false;
+	ui_checkbox_set_cb(checkbox, &test_checkbox_cb, &resp);
+
+	PCUT_ASSERT_FALSE(checkbox->held);
+	PCUT_ASSERT_FALSE(checkbox->inside);
+
+	ui_checkbox_press(checkbox);
+	PCUT_ASSERT_TRUE(checkbox->held);
+	PCUT_ASSERT_TRUE(checkbox->inside);
+	PCUT_ASSERT_FALSE(resp.switched);
+	PCUT_ASSERT_FALSE(checkbox->checked);
+
+	ui_checkbox_leave(checkbox);
+	PCUT_ASSERT_TRUE(checkbox->held);
+	PCUT_ASSERT_FALSE(checkbox->inside);
+	PCUT_ASSERT_FALSE(resp.switched);
+	PCUT_ASSERT_FALSE(checkbox->checked);
+
+	ui_checkbox_enter(checkbox);
+	PCUT_ASSERT_TRUE(checkbox->held);
+	PCUT_ASSERT_TRUE(checkbox->inside);
+	PCUT_ASSERT_FALSE(resp.switched);
+	PCUT_ASSERT_FALSE(checkbox->checked);
+
+	ui_checkbox_release(checkbox);
+	PCUT_ASSERT_FALSE(checkbox->held);
+	PCUT_ASSERT_TRUE(checkbox->inside);
+	PCUT_ASSERT_TRUE(resp.switched);
+	PCUT_ASSERT_TRUE(checkbox->checked);
+
+	ui_checkbox_destroy(checkbox);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** ui_pos_event() correctly translates POS_PRESS/POS_RELEASE */
+PCUT_TEST(pos_event_press_release)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_checkbox_t *checkbox;
+	ui_evclaim_t claim;
+	pos_event_t event;
+	gfx_rect_t rect;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_checkbox_create(resource, "Hello", &checkbox);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_FALSE(checkbox->held);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 30;
+	rect.p1.y = 40;
+	ui_checkbox_set_rect(checkbox, &rect);
+
+	/* Press outside is not claimed and does nothing */
+	event.type = POS_PRESS;
+	event.hpos = 9;
+	event.vpos = 20;
+	claim = ui_checkbox_pos_event(checkbox, &event);
+	PCUT_ASSERT_FALSE(checkbox->held);
+	PCUT_ASSERT_EQUALS(ui_unclaimed, claim);
+
+	/* Press inside is claimed and depresses check box */
+	event.type = POS_PRESS;
+	event.hpos = 10;
+	event.vpos = 20;
+	claim = ui_checkbox_pos_event(checkbox, &event);
+	PCUT_ASSERT_TRUE(checkbox->held);
+	PCUT_ASSERT_EQUALS(ui_claimed, claim);
+
+	/* Release outside (or anywhere) is claimed and relases check box */
+	event.type = POS_RELEASE;
+	event.hpos = 9;
+	event.vpos = 20;
+	claim = ui_checkbox_pos_event(checkbox, &event);
+	PCUT_ASSERT_FALSE(checkbox->held);
+	PCUT_ASSERT_EQUALS(ui_claimed, claim);
+
+	ui_checkbox_destroy(checkbox);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** ui_pos_event() correctly translates POS_UPDATE to enter/leave */
+PCUT_TEST(pos_event_enter_leave)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_checkbox_t *checkbox;
+	pos_event_t event;
+	gfx_rect_t rect;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_checkbox_create(resource, "Hello", &checkbox);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_FALSE(checkbox->inside);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 30;
+	rect.p1.y = 40;
+	ui_checkbox_set_rect(checkbox, &rect);
+
+	/* Moving outside does nothing */
+	event.type = POS_UPDATE;
+	event.hpos = 9;
+	event.vpos = 20;
+	ui_checkbox_pos_event(checkbox, &event);
+	PCUT_ASSERT_FALSE(checkbox->inside);
+
+	/* Moving inside sets inside flag */
+	event.type = POS_UPDATE;
+	event.hpos = 10;
+	event.vpos = 20;
+	ui_checkbox_pos_event(checkbox, &event);
+	PCUT_ASSERT_TRUE(checkbox->inside);
+
+	/* Moving outside clears inside flag */
+	event.type = POS_UPDATE;
+	event.hpos = 9;
+	event.vpos = 20;
+	ui_checkbox_pos_event(checkbox, &event);
+	PCUT_ASSERT_FALSE(checkbox->inside);
+
+	ui_checkbox_destroy(checkbox);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	(void) arg;
+	(void) rect;
+	return EOK;
+}
+
+static errno_t testgc_set_color(void *arg, gfx_color_t *color)
+{
+	(void) arg;
+	(void) color;
+	return EOK;
+}
+
+static errno_t testgc_fill_rect(void *arg, gfx_rect_t *rect)
+{
+	(void) arg;
+	(void) rect;
+	return EOK;
+}
+
+static errno_t testgc_update(void *arg)
+{
+	(void) arg;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+	testgc_bitmap_t *tbm;
+
+	tbm = calloc(1, sizeof(testgc_bitmap_t));
+	if (tbm == NULL)
+		return ENOMEM;
+
+	if (alloc == NULL) {
+		tbm->alloc.pitch = (params->rect.p1.x - params->rect.p0.x) *
+		    sizeof(uint32_t);
+		tbm->alloc.off0 = 0;
+		tbm->alloc.pixels = calloc(sizeof(uint32_t),
+		    (params->rect.p1.x - params->rect.p0.x) *
+		    (params->rect.p1.y - params->rect.p0.y));
+		tbm->myalloc = true;
+		if (tbm->alloc.pixels == NULL) {
+			free(tbm);
+			return ENOMEM;
+		}
+	} else {
+		tbm->alloc = *alloc;
+	}
+
+	tbm->tgc = tgc;
+	tgc->bm_created = true;
+	tgc->bm_params = *params;
+	tgc->bm_pixels = tbm->alloc.pixels;
+	*rbm = (void *)tbm;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_destroy(void *bm)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	if (tbm->myalloc)
+		free(tbm->alloc.pixels);
+	tbm->tgc->bm_destroyed = true;
+	free(tbm);
+	return EOK;
+}
+
+static errno_t testgc_bitmap_render(void *bm, gfx_rect_t *srect,
+    gfx_coord2_t *offs)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	tbm->tgc->bm_rendered = true;
+	tbm->tgc->bm_srect = *srect;
+	tbm->tgc->bm_offs = *offs;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	*alloc = tbm->alloc;
+	tbm->tgc->bm_got_alloc = true;
+	return EOK;
+}
+
+static void test_checkbox_switched(ui_checkbox_t *checkbox, void *arg,
+    bool checked)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->switched = true;
+}
+
+PCUT_EXPORT(checkbox);
Index: uspace/lib/ui/test/control.c
===================================================================
--- uspace/lib/ui/test/control.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/test/control.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -29,4 +29,5 @@
 #include <errno.h>
 #include <mem.h>
+#include <io/kbd_event.h>
 #include <io/pos_event.h>
 #include <pcut/pcut.h>
@@ -41,10 +42,14 @@
 static void test_ctl_destroy(void *);
 static errno_t test_ctl_paint(void *);
+static ui_evclaim_t test_ctl_kbd_event(void *, kbd_event_t *);
 static ui_evclaim_t test_ctl_pos_event(void *, pos_event_t *);
+static void test_ctl_unfocus(void *);
 
 static ui_control_ops_t test_ctl_ops = {
 	.destroy = test_ctl_destroy,
 	.paint = test_ctl_paint,
-	.pos_event = test_ctl_pos_event
+	.kbd_event = test_ctl_kbd_event,
+	.pos_event = test_ctl_pos_event,
+	.unfocus = test_ctl_unfocus
 };
 
@@ -62,8 +67,16 @@
 	bool paint;
 
+	/** @c true iff kbd_event was called */
+	bool kbd;
+	/** Keyboard event that was sent */
+	kbd_event_t kevent;
+
 	/** @c true iff pos_event was called */
 	bool pos;
 	/** Position event that was sent */
 	pos_event_t pevent;
+
+	/** @c true iff unfocus was called */
+	bool unfocus;
 } test_resp_t;
 
@@ -129,4 +142,35 @@
 	PCUT_ASSERT_ERRNO_VAL(resp.rc, rc);
 	PCUT_ASSERT_TRUE(resp.paint);
+
+	ui_control_delete(control);
+}
+
+/** Test sending keyboard event to control */
+PCUT_TEST(kbd_event)
+{
+	ui_control_t *control = NULL;
+	test_resp_t resp;
+	kbd_event_t event;
+	ui_evclaim_t claim;
+	errno_t rc;
+
+	rc = ui_control_new(&test_ctl_ops, &resp, &control);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(control);
+
+	resp.claim = ui_claimed;
+	resp.kbd = false;
+	event.type = KEY_PRESS;
+	event.key = KC_2;
+	event.mods = KM_LSHIFT;
+	event.c = '@';
+
+	claim = ui_control_kbd_event(control, &event);
+	PCUT_ASSERT_EQUALS(resp.claim, claim);
+	PCUT_ASSERT_TRUE(resp.kbd);
+	PCUT_ASSERT_EQUALS(resp.kevent.type, event.type);
+	PCUT_ASSERT_INT_EQUALS(resp.kevent.key, event.key);
+	PCUT_ASSERT_INT_EQUALS(resp.kevent.mods, event.mods);
+	PCUT_ASSERT_INT_EQUALS(resp.kevent.c, event.c);
 
 	ui_control_delete(control);
@@ -166,4 +210,23 @@
 }
 
+/** Test sending unfocus to control */
+PCUT_TEST(unfocus)
+{
+	ui_control_t *control = NULL;
+	test_resp_t resp;
+	errno_t rc;
+
+	rc = ui_control_new(&test_ctl_ops, &resp, &control);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(control);
+
+	resp.unfocus = false;
+
+	ui_control_unfocus(control);
+	PCUT_ASSERT_TRUE(resp.unfocus);
+
+	ui_control_delete(control);
+}
+
 static void test_ctl_destroy(void *arg)
 {
@@ -181,4 +244,14 @@
 }
 
+static ui_evclaim_t test_ctl_kbd_event(void *arg, kbd_event_t *event)
+{
+	test_resp_t *resp = (test_resp_t *) arg;
+
+	resp->kbd = true;
+	resp->kevent = *event;
+
+	return resp->claim;
+}
+
 static ui_evclaim_t test_ctl_pos_event(void *arg, pos_event_t *event)
 {
@@ -191,3 +264,10 @@
 }
 
+static void test_ctl_unfocus(void *arg)
+{
+	test_resp_t *resp = (test_resp_t *) arg;
+
+	resp->unfocus = true;
+}
+
 PCUT_EXPORT(control);
Index: uspace/lib/ui/test/entry.c
===================================================================
--- uspace/lib/ui/test/entry.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/test/entry.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -27,4 +27,5 @@
  */
 
+#include <clipboard.h>
 #include <gfx/context.h>
 #include <gfx/coord.h>
@@ -35,4 +36,6 @@
 #include <ui/entry.h>
 #include <ui/resource.h>
+#include <ui/ui.h>
+#include <ui/window.h>
 #include "../private/entry.h"
 
@@ -40,42 +43,4 @@
 
 PCUT_TEST_SUITE(entry);
-
-static errno_t testgc_set_color(void *, gfx_color_t *);
-static errno_t testgc_fill_rect(void *, gfx_rect_t *);
-static errno_t testgc_bitmap_create(void *, gfx_bitmap_params_t *,
-    gfx_bitmap_alloc_t *, void **);
-static errno_t testgc_bitmap_destroy(void *);
-static errno_t testgc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
-static errno_t testgc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
-
-static gfx_context_ops_t ops = {
-	.set_color = testgc_set_color,
-	.fill_rect = testgc_fill_rect,
-	.bitmap_create = testgc_bitmap_create,
-	.bitmap_destroy = testgc_bitmap_destroy,
-	.bitmap_render = testgc_bitmap_render,
-	.bitmap_get_alloc = testgc_bitmap_get_alloc
-};
-
-typedef struct {
-	bool bm_created;
-	bool bm_destroyed;
-	gfx_bitmap_params_t bm_params;
-	void *bm_pixels;
-	gfx_rect_t bm_srect;
-	gfx_coord2_t bm_offs;
-	bool bm_rendered;
-	bool bm_got_alloc;
-} test_gc_t;
-
-typedef struct {
-	test_gc_t *tgc;
-	gfx_bitmap_alloc_t alloc;
-	bool myalloc;
-} testgc_bitmap_t;
-
-typedef struct {
-	bool clicked;
-} test_cb_resp_t;
 
 /** Create and destroy text entry */
@@ -155,4 +120,21 @@
 }
 
+/** Set entry read only flag sets internal field */
+PCUT_TEST(set_read_only)
+{
+	ui_entry_t *entry;
+	errno_t rc;
+
+	rc = ui_entry_create(NULL, "Hello", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_entry_set_read_only(entry, true);
+	PCUT_ASSERT_TRUE(entry->read_only);
+	ui_entry_set_read_only(entry, false);
+	PCUT_ASSERT_FALSE(entry->read_only);
+
+	ui_entry_destroy(entry);
+}
+
 /** Set text entry rectangle sets internal field */
 PCUT_TEST(set_text)
@@ -183,18 +165,20 @@
 {
 	errno_t rc;
-	gfx_context_t *gc = NULL;
-	test_gc_t tgc;
-	ui_resource_t *resource = NULL;
-	ui_entry_t *entry;
-
-	memset(&tgc, 0, sizeof(tgc));
-	rc = gfx_context_new(&ops, &tgc, &gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ui_resource_create(gc, &resource);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-	PCUT_ASSERT_NOT_NULL(resource);
-
-	rc = ui_entry_create(resource, "Hello", &entry);
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "Hello", &entry);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -203,84 +187,621 @@
 
 	ui_entry_destroy(entry);
-	ui_resource_destroy(resource);
-
-	rc = gfx_context_delete(gc);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-}
-
-static errno_t testgc_set_color(void *arg, gfx_color_t *color)
-{
-	(void) arg;
-	(void) color;
-	return EOK;
-}
-
-static errno_t testgc_fill_rect(void *arg, gfx_rect_t *rect)
-{
-	(void) arg;
-	(void) rect;
-	return EOK;
-}
-
-static errno_t testgc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
-    gfx_bitmap_alloc_t *alloc, void **rbm)
-{
-	test_gc_t *tgc = (test_gc_t *) arg;
-	testgc_bitmap_t *tbm;
-
-	tbm = calloc(1, sizeof(testgc_bitmap_t));
-	if (tbm == NULL)
-		return ENOMEM;
-
-	if (alloc == NULL) {
-		tbm->alloc.pitch = (params->rect.p1.x - params->rect.p0.x) *
-		    sizeof(uint32_t);
-		tbm->alloc.off0 = 0;
-		tbm->alloc.pixels = calloc(sizeof(uint32_t),
-		    (params->rect.p1.x - params->rect.p0.x) *
-		    (params->rect.p1.y - params->rect.p0.y));
-		tbm->myalloc = true;
-		if (tbm->alloc.pixels == NULL) {
-			free(tbm);
-			return ENOMEM;
-		}
-	} else {
-		tbm->alloc = *alloc;
-	}
-
-	tbm->tgc = tgc;
-	tgc->bm_created = true;
-	tgc->bm_params = *params;
-	tgc->bm_pixels = tbm->alloc.pixels;
-	*rbm = (void *)tbm;
-	return EOK;
-}
-
-static errno_t testgc_bitmap_destroy(void *bm)
-{
-	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
-	if (tbm->myalloc)
-		free(tbm->alloc.pixels);
-	tbm->tgc->bm_destroyed = true;
-	free(tbm);
-	return EOK;
-}
-
-static errno_t testgc_bitmap_render(void *bm, gfx_rect_t *srect,
-    gfx_coord2_t *offs)
-{
-	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
-	tbm->tgc->bm_rendered = true;
-	tbm->tgc->bm_srect = *srect;
-	tbm->tgc->bm_offs = *offs;
-	return EOK;
-}
-
-static errno_t testgc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
-{
-	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
-	*alloc = tbm->alloc;
-	tbm->tgc->bm_got_alloc = true;
-	return EOK;
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_delete_sel() deletes selected text */
+PCUT_TEST(delete_sel)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABCDEF", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("ABCDEF", entry->text);
+
+	ui_entry_activate(entry);
+
+	/* Select all but first and last character */
+	ui_entry_seek_start(entry, false);
+	ui_entry_seek_next_char(entry, false);
+	ui_entry_seek_end(entry, true);
+	ui_entry_seek_prev_char(entry, true);
+
+	ui_entry_delete_sel(entry);
+
+	PCUT_ASSERT_STR_EQUALS("AF", entry->text);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_insert_str() inserts string at cursor. */
+PCUT_TEST(insert_str)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "A", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("A", entry->text);
+
+	ui_entry_activate(entry);
+	ui_entry_seek_end(entry, false);
+
+	rc = ui_entry_insert_str(entry, "B");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("AB", entry->text);
+
+	rc = ui_entry_insert_str(entry, "EF");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("ABEF", entry->text);
+
+	entry->pos = 2;
+	entry->sel_start = 2;
+	rc = ui_entry_insert_str(entry, "CD");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("ABCDEF", entry->text);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_insert_str() deletes selection before inserting string */
+PCUT_TEST(insert_str_with_sel)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABCDE", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("ABCDE", entry->text);
+
+	/* Select all but the first and last character */
+	ui_entry_activate(entry);
+	ui_entry_seek_start(entry, false);
+	ui_entry_seek_next_char(entry, false);
+	ui_entry_seek_end(entry, true);
+	ui_entry_seek_prev_char(entry, true);
+
+	rc = ui_entry_insert_str(entry, "123");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("A123E", entry->text);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_backspace() deletes character before cursor. */
+PCUT_TEST(backspace)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABCD", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("ABCD", entry->text);
+	entry->pos = 3;
+	entry->sel_start = 3;
+
+	ui_entry_backspace(entry);
+	PCUT_ASSERT_STR_EQUALS("ABD", entry->text);
+
+	ui_entry_backspace(entry);
+	PCUT_ASSERT_STR_EQUALS("AD", entry->text);
+
+	ui_entry_backspace(entry);
+	PCUT_ASSERT_STR_EQUALS("D", entry->text);
+
+	ui_entry_backspace(entry);
+	PCUT_ASSERT_STR_EQUALS("D", entry->text);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_backspace() with selected text deletes selection. */
+PCUT_TEST(backspace_with_sel)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABCDE", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("ABCDE", entry->text);
+
+	/* Select all but the first and last character */
+	ui_entry_activate(entry);
+	ui_entry_seek_start(entry, false);
+	ui_entry_seek_next_char(entry, false);
+	ui_entry_seek_end(entry, true);
+	ui_entry_seek_prev_char(entry, true);
+
+	ui_entry_backspace(entry);
+
+	PCUT_ASSERT_STR_EQUALS("AE", entry->text);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_delete() deletes character after cursor. */
+PCUT_TEST(delete)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABCD", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("ABCD", entry->text);
+	entry->pos = 1;
+	entry->sel_start = 1;
+
+	ui_entry_delete(entry);
+	PCUT_ASSERT_STR_EQUALS("ACD", entry->text);
+
+	ui_entry_delete(entry);
+	PCUT_ASSERT_STR_EQUALS("AD", entry->text);
+
+	ui_entry_delete(entry);
+	PCUT_ASSERT_STR_EQUALS("A", entry->text);
+
+	ui_entry_delete(entry);
+	PCUT_ASSERT_STR_EQUALS("A", entry->text);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_delete() with selected text deletes selection. */
+PCUT_TEST(delete_with_sel)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABCDE", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("ABCDE", entry->text);
+
+	/* Select all but the first and last character */
+	ui_entry_activate(entry);
+	ui_entry_seek_start(entry, false);
+	ui_entry_seek_next_char(entry, false);
+	ui_entry_seek_end(entry, true);
+	ui_entry_seek_prev_char(entry, true);
+
+	ui_entry_delete(entry);
+
+	PCUT_ASSERT_STR_EQUALS("AE", entry->text);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_copy() copies selected text to clipboard. */
+PCUT_TEST(copy)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+	char *str;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABCDEF", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_entry_activate(entry);
+	ui_entry_seek_start(entry, false);
+	ui_entry_seek_next_char(entry, false);
+	ui_entry_seek_end(entry, true);
+	ui_entry_seek_prev_char(entry, true);
+
+	// FIXME: This is not safe unless we could create a private
+	// test clipboard
+
+	ui_entry_copy(entry);
+	rc = clipboard_get_str(&str);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_STR_EQUALS("BCDE", str);
+	PCUT_ASSERT_STR_EQUALS("ABCDEF", entry->text);
+	free(str);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_cut() cuts selected text to clipboard. */
+PCUT_TEST(cut)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+	char *str;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABCDEF", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_entry_activate(entry);
+	ui_entry_seek_start(entry, false);
+	ui_entry_seek_next_char(entry, false);
+	ui_entry_seek_end(entry, true);
+	ui_entry_seek_prev_char(entry, true);
+
+	// FIXME: This is not safe unless we could create a private
+	// test clipboard
+
+	ui_entry_cut(entry);
+	rc = clipboard_get_str(&str);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_STR_EQUALS("BCDE", str);
+	PCUT_ASSERT_STR_EQUALS("AF", entry->text);
+	free(str);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_paste() pastes text from clipboard. */
+PCUT_TEST(paste)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "AB", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_entry_activate(entry);
+	ui_entry_seek_start(entry, false);
+	ui_entry_seek_next_char(entry, false);
+
+	// FIXME: This is not safe unless we could create a private
+	// test clipboard
+
+	rc = clipboard_put_str("123");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_entry_paste(entry);
+	PCUT_ASSERT_STR_EQUALS("A123B", entry->text);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_seek_start() moves cursor to beginning of text */
+PCUT_TEST(seek_start)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABCDEF", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_entry_activate(entry);
+
+	entry->pos = 2;
+	entry->sel_start = 2;
+
+	ui_entry_seek_start(entry, true);
+	PCUT_ASSERT_INT_EQUALS(0, entry->pos);
+	PCUT_ASSERT_INT_EQUALS(2, entry->sel_start);
+
+	ui_entry_seek_start(entry, false);
+	PCUT_ASSERT_INT_EQUALS(0, entry->pos);
+	PCUT_ASSERT_INT_EQUALS(0, entry->sel_start);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_seek_end() moves cursor to the end of text */
+PCUT_TEST(seek_end)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABCD", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("ABCD", entry->text);
+	entry->pos = 2;
+	entry->sel_start = 2;
+
+	ui_entry_seek_end(entry, true);
+	PCUT_ASSERT_INT_EQUALS(4, entry->pos);
+	PCUT_ASSERT_INT_EQUALS(2, entry->sel_start);
+	ui_entry_seek_end(entry, false);
+	PCUT_ASSERT_INT_EQUALS(4, entry->pos);
+	PCUT_ASSERT_INT_EQUALS(4, entry->sel_start);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_seek_prev_char() moves cursor to the previous character */
+PCUT_TEST(seek_prev_char)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABCD", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("ABCD", entry->text);
+	entry->pos = 3;
+	entry->sel_start = 3;
+
+	ui_entry_seek_prev_char(entry, true);
+	PCUT_ASSERT_INT_EQUALS(2, entry->pos);
+	PCUT_ASSERT_INT_EQUALS(3, entry->sel_start);
+
+	ui_entry_seek_prev_char(entry, false);
+	PCUT_ASSERT_INT_EQUALS(1, entry->pos);
+	PCUT_ASSERT_INT_EQUALS(1, entry->sel_start);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_seek_prev_char() moves cursor to the next character */
+PCUT_TEST(seek_next_char)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABCD", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_STR_EQUALS("ABCD", entry->text);
+	entry->pos = 1;
+	entry->sel_start = 1;
+
+	ui_entry_seek_next_char(entry, true);
+	PCUT_ASSERT_INT_EQUALS(2, entry->pos);
+	PCUT_ASSERT_INT_EQUALS(1, entry->sel_start);
+	ui_entry_seek_next_char(entry, false);
+	PCUT_ASSERT_INT_EQUALS(3, entry->pos);
+	PCUT_ASSERT_INT_EQUALS(3, entry->sel_start);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_entry_activate() / ui_entry_deactivate() */
+PCUT_TEST(activate_deactivate)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_entry_t *entry;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_entry_create(window, "ABC", &entry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_FALSE(entry->active);
+
+	ui_entry_activate(entry);
+	PCUT_ASSERT_TRUE(entry->active);
+
+	ui_entry_deactivate(entry);
+	PCUT_ASSERT_FALSE(entry->active);
+
+	ui_entry_destroy(entry);
+	ui_window_destroy(window);
+	ui_destroy(ui);
 }
 
Index: uspace/lib/ui/test/filedialog.c
===================================================================
--- uspace/lib/ui/test/filedialog.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/test/filedialog.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2021 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 <pcut/pcut.h>
+#include <stdbool.h>
+#include <ui/pbutton.h>
+#include <ui/ui.h>
+#include <ui/filedialog.h>
+#include "../private/filedialog.h"
+#include "../private/window.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(file_dialog);
+
+static void test_dialog_bok(ui_file_dialog_t *, void *, const char *);
+static void test_dialog_bcancel(ui_file_dialog_t *, void *);
+static void test_dialog_close(ui_file_dialog_t *, void *);
+
+static ui_file_dialog_cb_t test_file_dialog_cb = {
+	.bok = test_dialog_bok,
+	.bcancel = test_dialog_bcancel,
+	.close = test_dialog_close
+};
+
+static ui_file_dialog_cb_t dummy_file_dialog_cb = {
+};
+
+typedef struct {
+	bool bok;
+	const char *fname;
+	bool bcancel;
+	bool close;
+} test_cb_resp_t;
+
+/** Create and destroy file dialog */
+PCUT_TEST(create_destroy)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_file_dialog_params_t params;
+	ui_file_dialog_t *dialog = NULL;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_file_dialog_params_init(&params);
+	params.caption = "Open";
+
+	rc = ui_file_dialog_create(ui, &params, &dialog);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(dialog);
+
+	ui_file_dialog_destroy(dialog);
+	ui_destroy(ui);
+}
+
+/** ui_file_dialog_destroy() can take NULL argument (no-op) */
+PCUT_TEST(destroy_null)
+{
+	ui_file_dialog_destroy(NULL);
+}
+
+/** Button click invokes callback set via ui_file_dialog_set_cb() */
+PCUT_TEST(button_cb)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_file_dialog_params_t params;
+	ui_file_dialog_t *dialog = NULL;
+	test_cb_resp_t resp;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_file_dialog_params_init(&params);
+	params.caption = "Open";
+
+	rc = ui_file_dialog_create(ui, &params, &dialog);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(dialog);
+
+	/* OK button callback with no callbacks set */
+	ui_pbutton_clicked(dialog->bok);
+
+	/* OK button callback with callback not implemented */
+	ui_file_dialog_set_cb(dialog, &dummy_file_dialog_cb, NULL);
+	ui_pbutton_clicked(dialog->bok);
+
+	/* OK button callback with real callback set */
+	resp.bok = false;
+	ui_file_dialog_set_cb(dialog, &test_file_dialog_cb, &resp);
+	ui_pbutton_clicked(dialog->bok);
+	PCUT_ASSERT_TRUE(resp.bok);
+
+	ui_file_dialog_destroy(dialog);
+	ui_destroy(ui);
+}
+
+/** Sending window close request invokes callback set via
+ * ui_file_dialog_set_cb()
+ */
+PCUT_TEST(close_cb)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_file_dialog_params_t params;
+	ui_file_dialog_t *dialog = NULL;
+	test_cb_resp_t resp;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_file_dialog_params_init(&params);
+	params.caption = "Open";
+
+	rc = ui_file_dialog_create(ui, &params, &dialog);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(dialog);
+
+	/* Button callback with no callbacks set */
+	ui_window_send_close(dialog->window);
+
+	/* Button callback with unfocus callback not implemented */
+	ui_file_dialog_set_cb(dialog, &dummy_file_dialog_cb, NULL);
+	ui_window_send_close(dialog->window);
+
+	/* Button callback with real callback set */
+	resp.close = false;
+	ui_file_dialog_set_cb(dialog, &test_file_dialog_cb, &resp);
+	ui_window_send_close(dialog->window);
+	PCUT_ASSERT_TRUE(resp.close);
+
+	ui_file_dialog_destroy(dialog);
+	ui_destroy(ui);
+}
+
+static void test_dialog_bok(ui_file_dialog_t *dialog, void *arg,
+    const char *fname)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->bok = true;
+}
+
+static void test_dialog_bcancel(ui_file_dialog_t *dialog, void *arg)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->bcancel = true;
+}
+
+static void test_dialog_close(ui_file_dialog_t *dialog, void *arg)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->close = true;
+}
+
+PCUT_EXPORT(file_dialog);
Index: uspace/lib/ui/test/fixed.c
===================================================================
--- uspace/lib/ui/test/fixed.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/test/fixed.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -41,9 +41,11 @@
 static errno_t test_ctl_paint(void *);
 static ui_evclaim_t test_ctl_pos_event(void *, pos_event_t *);
+static void test_ctl_unfocus(void *);
 
 static ui_control_ops_t test_ctl_ops = {
 	.destroy = test_ctl_destroy,
 	.paint = test_ctl_paint,
-	.pos_event = test_ctl_pos_event
+	.pos_event = test_ctl_pos_event,
+	.unfocus = test_ctl_unfocus
 };
 
@@ -62,4 +64,6 @@
 	/** Position event that was sent */
 	pos_event_t pevent;
+	/** @c true iff unfocus was called */
+	bool unfocus;
 } test_resp_t;
 
@@ -230,4 +234,29 @@
 }
 
+/** ui_fixed_unfocus() delivers unfocus notification to control */
+PCUT_TEST(unfocus)
+{
+	ui_fixed_t *fixed = NULL;
+	ui_control_t *control;
+	test_resp_t resp;
+	errno_t rc;
+
+	rc = ui_fixed_create(&fixed);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_control_new(&test_ctl_ops, (void *) &resp, &control);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_fixed_add(fixed, control);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	resp.unfocus = false;
+
+	ui_fixed_unfocus(fixed);
+	PCUT_ASSERT_TRUE(resp.unfocus);
+
+	ui_fixed_destroy(fixed);
+}
+
 static void test_ctl_destroy(void *arg)
 {
@@ -255,3 +284,10 @@
 }
 
+static void test_ctl_unfocus(void *arg)
+{
+	test_resp_t *resp = (test_resp_t *) arg;
+
+	resp->unfocus = true;
+}
+
 PCUT_EXPORT(fixed);
Index: uspace/lib/ui/test/image.c
===================================================================
--- uspace/lib/ui/test/image.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/test/image.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -34,4 +34,5 @@
 #include <ui/control.h>
 #include <ui/image.h>
+#include <ui/resource.h>
 #include <ui/ui.h>
 #include "../private/dummygc.h"
@@ -42,8 +43,4 @@
 PCUT_TEST_SUITE(image);
 
-typedef struct {
-	bool clicked;
-} test_cb_resp_t;
-
 /** Create and destroy image */
 PCUT_TEST(create_destroy)
@@ -87,10 +84,22 @@
 PCUT_TEST(set_rect)
 {
+	errno_t rc;
+	dummy_gc_t *dgc;
+	gfx_context_t *gc;
+	ui_resource_t *resource = NULL;
 	ui_image_t *image = NULL;
 	gfx_rect_t brect;
 	gfx_rect_t rect;
-	errno_t rc;
-
-	rc = ui_image_create(NULL, NULL, &brect, &image);
+
+	rc = dummygc_create(&dgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = dummygc_get_ctx(dgc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_image_create(resource, NULL, &brect, &image);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(image);
@@ -108,4 +117,6 @@
 
 	ui_image_destroy(image);
+	ui_resource_destroy(resource);
+	dummygc_destroy(dgc);
 }
 
@@ -139,4 +150,5 @@
 	dummy_gc_t *dgc;
 	gfx_context_t *gc;
+	ui_resource_t *resource = NULL;
 	errno_t rc;
 
@@ -146,5 +158,9 @@
 	gc = dummygc_get_ctx(dgc);
 
-	rc = ui_image_create(NULL, NULL, &brect, &image);
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_image_create(resource, NULL, &brect, &image);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(image);
@@ -172,4 +188,6 @@
 
 	ui_image_destroy(image);
+	ui_resource_destroy(resource);
+	dummygc_destroy(dgc);
 }
 
@@ -181,4 +199,5 @@
 	gfx_bitmap_params_t params;
 	gfx_bitmap_t *bitmap;
+	ui_resource_t *resource = NULL;
 	ui_image_t *image = NULL;
 	gfx_rect_t brect;
@@ -189,4 +208,8 @@
 
 	gc = dummygc_get_ctx(dgc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
 
 	gfx_bitmap_params_init(&params);
@@ -194,5 +217,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ui_image_create(NULL, bitmap, &brect, &image);
+	rc = ui_image_create(resource, bitmap, &brect, &image);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(image);
@@ -209,4 +232,6 @@
 
 	ui_image_destroy(image);
+	ui_resource_destroy(resource);
+	dummygc_destroy(dgc);
 }
 
Index: uspace/lib/ui/test/label.c
===================================================================
--- uspace/lib/ui/test/label.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/test/label.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,6 +41,8 @@
 PCUT_TEST_SUITE(label);
 
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
+static errno_t testgc_update(void *);
 static errno_t testgc_bitmap_create(void *, gfx_bitmap_params_t *,
     gfx_bitmap_alloc_t *, void **);
@@ -50,6 +52,8 @@
 
 static gfx_context_ops_t ops = {
+	.set_clip_rect = testgc_set_clip_rect,
 	.set_color = testgc_set_color,
 	.fill_rect = testgc_fill_rect,
+	.update = testgc_update,
 	.bitmap_create = testgc_bitmap_create,
 	.bitmap_destroy = testgc_bitmap_destroy,
@@ -192,5 +196,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ui_resource_create(gc, &resource);
+	rc = ui_resource_create(gc, false, &resource);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(resource);
@@ -207,4 +211,11 @@
 	rc = gfx_context_delete(gc);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	(void) arg;
+	(void) rect;
+	return EOK;
 }
 
@@ -220,4 +231,10 @@
 	(void) arg;
 	(void) rect;
+	return EOK;
+}
+
+static errno_t testgc_update(void *arg)
+{
+	(void) arg;
 	return EOK;
 }
Index: uspace/lib/ui/test/main.c
===================================================================
--- uspace/lib/ui/test/main.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/test/main.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -32,11 +32,20 @@
 
 PCUT_IMPORT(control);
+PCUT_IMPORT(checkbox);
 PCUT_IMPORT(entry);
+PCUT_IMPORT(file_dialog);
 PCUT_IMPORT(fixed);
 PCUT_IMPORT(image);
 PCUT_IMPORT(label);
+PCUT_IMPORT(menu);
+PCUT_IMPORT(menubar);
+PCUT_IMPORT(menuentry);
+PCUT_IMPORT(msg_dialog);
 PCUT_IMPORT(paint);
 PCUT_IMPORT(pbutton);
+PCUT_IMPORT(popup);
+PCUT_IMPORT(rbutton);
 PCUT_IMPORT(resource);
+PCUT_IMPORT(slider);
 PCUT_IMPORT(ui);
 PCUT_IMPORT(wdecor);
Index: uspace/lib/ui/test/menu.c
===================================================================
--- uspace/lib/ui/test/menu.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/test/menu.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2021 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 <gfx/context.h>
+#include <gfx/coord.h>
+#include <mem.h>
+#include <pcut/pcut.h>
+#include <stdbool.h>
+#include <str.h>
+#include <ui/control.h>
+#include <ui/menu.h>
+#include <ui/menubar.h>
+#include <ui/ui.h>
+#include <ui/window.h>
+#include "../private/menu.h"
+#include "../private/menubar.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(menu);
+
+/** Create and destroy menu */
+PCUT_TEST(create_destroy)
+{
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	errno_t rc;
+
+	rc = ui_menu_bar_create(NULL, NULL, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	/*
+	 * Normally we don't need to destroy a menu explicitly, it will
+	 * be destroyed along with menu bar, but here we'll test destroying
+	 * it explicitly.
+	 */
+	ui_menu_destroy(menu);
+	ui_menu_bar_destroy(mbar);
+}
+
+/** ui_menu_destroy() can take NULL argument (no-op) */
+PCUT_TEST(destroy_null)
+{
+	ui_menu_destroy(NULL);
+}
+
+/** ui_menu_first() / ui_menu_next() iterate over menus */
+PCUT_TEST(first_next)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu1 = NULL;
+	ui_menu_t *menu2 = NULL;
+	ui_menu_t *m;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test 1", &menu1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu1);
+
+	rc = ui_menu_create(mbar, "Test 1", &menu2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu2);
+
+	m = ui_menu_first(mbar);
+	PCUT_ASSERT_EQUALS(menu1, m);
+
+	m = ui_menu_next(m);
+	PCUT_ASSERT_EQUALS(menu2, m);
+
+	m = ui_menu_next(m);
+	PCUT_ASSERT_NULL(m);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_menu_caption() returns the menu's caption */
+PCUT_TEST(caption)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	const char *caption;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	caption = ui_menu_caption(menu);
+	PCUT_ASSERT_NOT_NULL(caption);
+
+	PCUT_ASSERT_INT_EQUALS(0, str_cmp(caption, "Test"));
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_menu_get_rect() returns outer menu rectangle */
+PCUT_TEST(get_rect)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	gfx_coord2_t pos;
+	gfx_rect_t rect;
+	const char *caption;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	caption = ui_menu_caption(menu);
+	PCUT_ASSERT_NOT_NULL(caption);
+
+	pos.x = 0;
+	pos.y = 0;
+	ui_menu_get_rect(menu, &pos, &rect);
+
+	PCUT_ASSERT_INT_EQUALS(0, rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(0, rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(16, rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(8, rect.p1.y);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Paint menu */
+PCUT_TEST(paint)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	gfx_rect_t prect;
+	gfx_coord2_t pos;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	/* Menu needs to be open to be able to paint it */
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	pos.x = 0;
+	pos.y = 0;
+	rc = ui_menu_paint(menu, &pos);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_menu_pos_event() inside menu is claimed */
+PCUT_TEST(pos_event_inside)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_evclaim_t claimed;
+	gfx_coord2_t pos;
+	pos_event_t event;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	pos.x = 0;
+	pos.y = 0;
+	event.type = POS_PRESS;
+	event.hpos = 0;
+	event.vpos = 0;
+	claimed = ui_menu_pos_event(menu, &pos, &event);
+	PCUT_ASSERT_EQUALS(ui_claimed, claimed);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Computing menu geometry */
+PCUT_TEST(get_geom)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_geom_t geom;
+	gfx_coord2_t pos;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	pos.x = 0;
+	pos.y = 0;
+	ui_menu_get_geom(menu, &pos, &geom);
+
+	PCUT_ASSERT_INT_EQUALS(0, geom.outer_rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(0, geom.outer_rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(16, geom.outer_rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(8, geom.outer_rect.p1.y);
+	PCUT_ASSERT_INT_EQUALS(4, geom.entries_rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(4, geom.entries_rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(12, geom.entries_rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(4, geom.entries_rect.p1.y);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+PCUT_EXPORT(menu);
Index: uspace/lib/ui/test/menubar.c
===================================================================
--- uspace/lib/ui/test/menubar.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/test/menubar.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2021 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 <gfx/coord.h>
+#include <mem.h>
+#include <pcut/pcut.h>
+#include <stdbool.h>
+#include <ui/control.h>
+#include <ui/menu.h>
+#include <ui/menubar.h>
+#include <ui/ui.h>
+#include <ui/window.h>
+#include "../private/menubar.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(menubar);
+
+/** Create and destroy menu bar */
+PCUT_TEST(create_destroy)
+{
+	ui_menu_bar_t *mbar = NULL;
+	errno_t rc;
+
+	rc = ui_menu_bar_create(NULL, NULL, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	ui_menu_bar_destroy(mbar);
+}
+
+/** ui_menu_bar_destroy() can take NULL argument (no-op) */
+PCUT_TEST(destroy_null)
+{
+	ui_menu_bar_destroy(NULL);
+}
+
+/** ui_menu_bar_ctl() returns control that has a working virtual destructor */
+PCUT_TEST(ctl)
+{
+	ui_menu_bar_t *mbar = NULL;
+	ui_control_t *control;
+	errno_t rc;
+
+	rc = ui_menu_bar_create(NULL, NULL, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	control = ui_menu_bar_ctl(mbar);
+	PCUT_ASSERT_NOT_NULL(control);
+
+	ui_control_destroy(control);
+}
+
+/** Set menu bar rectangle sets internal field */
+PCUT_TEST(set_rect)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	gfx_rect_t rect;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+
+	ui_menu_bar_set_rect(mbar, &rect);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.x, mbar->rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.y, mbar->rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.x, mbar->rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.y, mbar->rect.p1.y);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Paint menu bar */
+PCUT_TEST(paint)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_bar_paint(mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Press event on menu bar entry selects menu */
+PCUT_TEST(pos_event_select)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_evclaim_t claimed;
+	pos_event_t event;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = 50;
+	rect.p1.y = 25;
+	ui_menu_bar_set_rect(mbar, &rect);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	event.type = POS_PRESS;
+	event.hpos = 4;
+	event.vpos = 4;
+	claimed = ui_menu_bar_pos_event(mbar, &event);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(ui_claimed, claimed);
+
+	/* Clicking the menu bar entry should select menu */
+	PCUT_ASSERT_EQUALS(menu, mbar->selected);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Calling ui_menu_bar_select() with the same menu twice deselects it */
+PCUT_TEST(select_same)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = 0;
+	rect.p1.y = 0;
+	ui_menu_bar_select(mbar, &rect, menu);
+	PCUT_ASSERT_EQUALS(menu, mbar->selected);
+
+	/* Selecting again should unselect the menu */
+	ui_menu_bar_select(mbar, &rect, menu);
+	PCUT_ASSERT_NULL(mbar->selected);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Calling ui_menu_bar_select() with another menu selects it */
+PCUT_TEST(select_different)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu1 = NULL;
+	ui_menu_t *menu2 = NULL;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test 1", &menu1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu1);
+
+	rc = ui_menu_create(mbar, "Test 2", &menu2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu2);
+
+	rect.p0.x = 0;
+	rect.p0.y = 0;
+	rect.p1.x = 0;
+	rect.p1.y = 0;
+	ui_menu_bar_select(mbar, &rect, menu1);
+	PCUT_ASSERT_EQUALS(menu1, mbar->selected);
+
+	/* Selecting different menu should select it */
+	ui_menu_bar_select(mbar, &rect, menu2);
+	PCUT_ASSERT_EQUALS(menu2, mbar->selected);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+PCUT_EXPORT(menubar);
Index: uspace/lib/ui/test/menuentry.c
===================================================================
--- uspace/lib/ui/test/menuentry.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/test/menuentry.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,797 @@
+/*
+ * Copyright (c) 2021 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 <gfx/context.h>
+#include <gfx/coord.h>
+#include <mem.h>
+#include <pcut/pcut.h>
+#include <stdbool.h>
+#include <ui/control.h>
+#include <ui/menu.h>
+#include <ui/menubar.h>
+#include <ui/menuentry.h>
+#include <ui/ui.h>
+#include <ui/window.h>
+#include "../private/menuentry.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(menuentry);
+
+typedef struct {
+	bool activated;
+} test_resp_t;
+
+static void test_entry_cb(ui_menu_entry_t *, void *);
+
+/** Create and destroy menu entry */
+PCUT_TEST(create_destroy)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "Foo", "F1", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	/* Just for sake of test. Menu entry is destroyed along with menu */
+	ui_menu_entry_destroy(mentry);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Create and destroy separator menu entry */
+PCUT_TEST(create_sep_destroy)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_sep_create(menu, &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	/* Just for sake of test. Menu entry is destroyed along with menu */
+	ui_menu_entry_destroy(mentry);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_menu_bar_destroy() can take NULL argument (no-op) */
+PCUT_TEST(destroy_null)
+{
+	ui_menu_bar_destroy(NULL);
+}
+
+/** ui_menu_entry_set_cb() .. */
+PCUT_TEST(set_cb)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	test_resp_t resp;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "Foo", "F1", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	ui_menu_entry_set_cb(mentry, test_entry_cb, &resp);
+
+	resp.activated = false;
+	ui_menu_entry_cb(mentry);
+	PCUT_ASSERT_TRUE(resp.activated);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_menu_entry_first() / ui_menu_entry_next() iterate over entries */
+PCUT_TEST(first_next)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *entry1 = NULL;
+	ui_menu_entry_t *entry2 = NULL;
+	ui_menu_entry_t *e;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "Foo", "F1", &entry1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(entry1);
+
+	rc = ui_menu_entry_create(menu, "Bar", "F2", &entry2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(entry2);
+
+	e = ui_menu_entry_first(menu);
+	PCUT_ASSERT_EQUALS(entry1, e);
+
+	e = ui_menu_entry_next(e);
+	PCUT_ASSERT_EQUALS(entry2, e);
+
+	e = ui_menu_entry_next(e);
+	PCUT_ASSERT_NULL(e);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_menu_entry_column_widths() / ui_menu_entry_height() */
+PCUT_TEST(widths_height)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	gfx_coord_t caption_w;
+	gfx_coord_t shortcut_w;
+	gfx_coord_t width;
+	gfx_coord_t height;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "X", "Y", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	ui_menu_entry_column_widths(mentry, &caption_w, &shortcut_w);
+	PCUT_ASSERT_INT_EQUALS(11, caption_w);
+	PCUT_ASSERT_INT_EQUALS(10, shortcut_w);
+
+	width = ui_menu_entry_calc_width(menu, caption_w, shortcut_w);
+	PCUT_ASSERT_INT_EQUALS(4 + 11 + 8 + 10 + 4, width);
+
+	height = ui_menu_entry_height(mentry);
+	PCUT_ASSERT_INT_EQUALS(13 + 8, height);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Paint menu entry */
+PCUT_TEST(paint)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	gfx_coord2_t pos;
+	gfx_rect_t prect;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "Foo", "F1", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	pos.x = 0;
+	pos.y = 0;
+	rc = ui_menu_entry_paint(mentry, &pos);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Press and release activates menu entry */
+PCUT_TEST(press_release)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	gfx_coord2_t pos;
+	gfx_rect_t prect;
+	test_resp_t resp;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "X", "Y", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	ui_menu_entry_set_cb(mentry, test_entry_cb, &resp);
+	resp.activated = false;
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	pos.x = 0;
+	pos.y = 0;
+	ui_menu_entry_press(mentry, &pos);
+	PCUT_ASSERT_TRUE(mentry->inside);
+	PCUT_ASSERT_TRUE(mentry->held);
+	PCUT_ASSERT_FALSE(resp.activated);
+
+	ui_menu_entry_release(mentry);
+	PCUT_ASSERT_FALSE(mentry->held);
+	PCUT_ASSERT_TRUE(resp.activated);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Press, leave and release does not activate entry */
+PCUT_TEST(press_leave_release)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	gfx_coord2_t pos;
+	gfx_rect_t prect;
+	test_resp_t resp;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "X", "Y", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	ui_menu_entry_set_cb(mentry, test_entry_cb, &resp);
+	resp.activated = false;
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	pos.x = 0;
+	pos.y = 0;
+	ui_menu_entry_press(mentry, &pos);
+	PCUT_ASSERT_TRUE(mentry->inside);
+	PCUT_ASSERT_TRUE(mentry->held);
+	PCUT_ASSERT_FALSE(resp.activated);
+
+	ui_menu_entry_leave(mentry, &pos);
+	PCUT_ASSERT_FALSE(mentry->inside);
+	PCUT_ASSERT_TRUE(mentry->held);
+	PCUT_ASSERT_FALSE(resp.activated);
+
+	ui_menu_entry_release(mentry);
+	PCUT_ASSERT_FALSE(mentry->held);
+	PCUT_ASSERT_FALSE(resp.activated);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Press, leave, enter and release activates menu entry */
+PCUT_TEST(press_leave_enter_release)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	gfx_coord2_t pos;
+	gfx_rect_t prect;
+	test_resp_t resp;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "X", "Y", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	ui_menu_entry_set_cb(mentry, test_entry_cb, &resp);
+	resp.activated = false;
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	pos.x = 0;
+	pos.y = 0;
+	ui_menu_entry_press(mentry, &pos);
+	PCUT_ASSERT_TRUE(mentry->inside);
+	PCUT_ASSERT_TRUE(mentry->held);
+	PCUT_ASSERT_FALSE(resp.activated);
+
+	ui_menu_entry_leave(mentry, &pos);
+	PCUT_ASSERT_FALSE(mentry->inside);
+	PCUT_ASSERT_TRUE(mentry->held);
+	PCUT_ASSERT_FALSE(resp.activated);
+
+	ui_menu_entry_enter(mentry, &pos);
+	PCUT_ASSERT_TRUE(mentry->inside);
+	PCUT_ASSERT_TRUE(mentry->held);
+	PCUT_ASSERT_FALSE(resp.activated);
+
+	ui_menu_entry_release(mentry);
+	PCUT_ASSERT_FALSE(mentry->held);
+	PCUT_ASSERT_TRUE(resp.activated);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Press event inside menu entry */
+PCUT_TEST(pos_press_inside)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	gfx_coord2_t pos;
+	gfx_rect_t prect;
+	pos_event_t event;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "X", "Y", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	pos.x = 0;
+	pos.y = 0;
+
+	event.type = POS_PRESS;
+	event.hpos = 4;
+	event.vpos = 4;
+
+	ui_menu_entry_pos_event(mentry, &pos, &event);
+	PCUT_ASSERT_TRUE(mentry->inside);
+	PCUT_ASSERT_TRUE(mentry->held);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Press event outside menu entry */
+PCUT_TEST(pos_press_outside)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	gfx_coord2_t pos;
+	gfx_rect_t prect;
+	pos_event_t event;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "X", "Y", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	pos.x = 0;
+	pos.y = 0;
+
+	event.type = POS_PRESS;
+	event.hpos = 40;
+	event.vpos = 20;
+
+	ui_menu_entry_pos_event(mentry, &pos, &event);
+	PCUT_ASSERT_FALSE(mentry->inside);
+	PCUT_ASSERT_FALSE(mentry->held);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Position event moving out of menu entry */
+PCUT_TEST(pos_move_out)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	gfx_coord2_t pos;
+	gfx_rect_t prect;
+	pos_event_t event;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "X", "Y", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	pos.x = 0;
+	pos.y = 0;
+	ui_menu_entry_press(mentry, &pos);
+	PCUT_ASSERT_TRUE(mentry->inside);
+	PCUT_ASSERT_TRUE(mentry->held);
+
+	event.type = POS_UPDATE;
+	event.hpos = 40;
+	event.vpos = 20;
+
+	ui_menu_entry_pos_event(mentry, &pos, &event);
+	PCUT_ASSERT_FALSE(mentry->inside);
+	PCUT_ASSERT_TRUE(mentry->held);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Position event moving inside menu entry */
+PCUT_TEST(pos_move_in)
+{
+	ui_t *ui = NULL;
+	ui_window_t *window = NULL;
+	ui_wnd_params_t params;
+	ui_menu_bar_t *mbar = NULL;
+	ui_menu_t *menu = NULL;
+	ui_menu_entry_t *mentry = NULL;
+	gfx_coord2_t pos;
+	gfx_rect_t prect;
+	pos_event_t event;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	rc = ui_menu_bar_create(ui, window, &mbar);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mbar);
+
+	rc = ui_menu_create(mbar, "Test", &menu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(menu);
+
+	rc = ui_menu_entry_create(menu, "X", "Y", &mentry);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(mentry);
+
+	prect.p0.x = 0;
+	prect.p0.y = 0;
+	prect.p1.x = 0;
+	prect.p1.y = 0;
+
+	rc = ui_menu_open(menu, &prect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	event.type = POS_UPDATE;
+	event.hpos = 4;
+	event.vpos = 4;
+
+	pos.x = 0;
+	pos.y = 0;
+
+	ui_menu_entry_pos_event(mentry, &pos, &event);
+	PCUT_ASSERT_TRUE(mentry->inside);
+	PCUT_ASSERT_FALSE(mentry->held);
+
+	ui_menu_bar_destroy(mbar);
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+static void test_entry_cb(ui_menu_entry_t *mentry, void *arg)
+{
+	test_resp_t *resp = (test_resp_t *) arg;
+
+	resp->activated = true;
+}
+
+PCUT_EXPORT(menuentry);
Index: uspace/lib/ui/test/msgdialog.c
===================================================================
--- uspace/lib/ui/test/msgdialog.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/test/msgdialog.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2021 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 <pcut/pcut.h>
+#include <stdbool.h>
+#include <ui/pbutton.h>
+#include <ui/ui.h>
+#include <ui/msgdialog.h>
+#include "../private/msgdialog.h"
+#include "../private/window.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(msg_dialog);
+
+static void test_dialog_button(ui_msg_dialog_t *, void *, unsigned);
+static void test_dialog_close(ui_msg_dialog_t *, void *);
+
+static ui_msg_dialog_cb_t test_msg_dialog_cb = {
+	.button = test_dialog_button,
+	.close = test_dialog_close
+};
+
+static ui_msg_dialog_cb_t dummy_msg_dialog_cb = {
+};
+
+typedef struct {
+	bool button;
+	unsigned bnum;
+	bool close;
+} test_cb_resp_t;
+
+/** Create and destroy message dialog */
+PCUT_TEST(create_destroy)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_msg_dialog_params_t params;
+	ui_msg_dialog_t *dialog = NULL;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_msg_dialog_params_init(&params);
+	params.caption = "Message";
+	params.text = "Hello";
+
+	rc = ui_msg_dialog_create(ui, &params, &dialog);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(dialog);
+
+	ui_msg_dialog_destroy(dialog);
+	ui_destroy(ui);
+}
+
+/** ui_msg_dialog_destroy() can take NULL argument (no-op) */
+PCUT_TEST(destroy_null)
+{
+	ui_msg_dialog_destroy(NULL);
+}
+
+/** Button click invokes callback set via ui_msg_dialog_set_cb() */
+PCUT_TEST(button_cb)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_msg_dialog_params_t params;
+	ui_msg_dialog_t *dialog = NULL;
+	test_cb_resp_t resp;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_msg_dialog_params_init(&params);
+	params.caption = "Message";
+	params.text = "Hello";
+
+	rc = ui_msg_dialog_create(ui, &params, &dialog);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(dialog);
+
+	/* Button callback with no callbacks set */
+	ui_pbutton_clicked(dialog->bok);
+
+	/* Button callback with callback not implemented */
+	ui_msg_dialog_set_cb(dialog, &dummy_msg_dialog_cb, NULL);
+	ui_pbutton_clicked(dialog->bok);
+
+	/* Button callback with real callback set */
+	resp.button = false;
+	resp.bnum = 123;
+	ui_msg_dialog_set_cb(dialog, &test_msg_dialog_cb, &resp);
+	ui_pbutton_clicked(dialog->bok);
+	PCUT_ASSERT_TRUE(resp.button);
+	PCUT_ASSERT_INT_EQUALS(0, resp.bnum);
+
+	ui_msg_dialog_destroy(dialog);
+	ui_destroy(ui);
+}
+
+/** Sending window close request invokes callback set via
+ * ui_msg_dialog_set_cb()
+ */
+PCUT_TEST(close_cb)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_msg_dialog_params_t params;
+	ui_msg_dialog_t *dialog = NULL;
+	test_cb_resp_t resp;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_msg_dialog_params_init(&params);
+	params.caption = "Message";
+	params.text = "Hello";
+
+	rc = ui_msg_dialog_create(ui, &params, &dialog);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(dialog);
+
+	/* Button callback with no callbacks set */
+	ui_window_send_close(dialog->window);
+
+	/* Button callback with unfocus callback not implemented */
+	ui_msg_dialog_set_cb(dialog, &dummy_msg_dialog_cb, NULL);
+	ui_window_send_close(dialog->window);
+
+	/* Button callback with real callback set */
+	resp.close = false;
+	ui_msg_dialog_set_cb(dialog, &test_msg_dialog_cb, &resp);
+	ui_window_send_close(dialog->window);
+	PCUT_ASSERT_TRUE(resp.close);
+
+	ui_msg_dialog_destroy(dialog);
+	ui_destroy(ui);
+}
+
+static void test_dialog_button(ui_msg_dialog_t *dialog, void *arg,
+    unsigned bnum)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->button = true;
+	resp->bnum = bnum;
+}
+
+static void test_dialog_close(ui_msg_dialog_t *dialog, void *arg)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->close = true;
+}
+
+PCUT_EXPORT(msg_dialog);
Index: uspace/lib/ui/test/paint.c
===================================================================
--- uspace/lib/ui/test/paint.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/test/paint.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -33,4 +33,5 @@
 #include <stdbool.h>
 #include <ui/paint.h>
+#include <ui/resource.h>
 
 PCUT_INIT;
@@ -38,4 +39,5 @@
 PCUT_TEST_SUITE(paint);
 
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
@@ -47,4 +49,5 @@
 
 static gfx_context_ops_t ops = {
+	.set_clip_rect = testgc_set_clip_rect,
 	.set_color = testgc_set_color,
 	.fill_rect = testgc_fill_rect,
@@ -93,4 +96,9 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 30;
+	rect.p1.y = 40;
+
 	/* Paint bevel with NULL 'inside' output parameter */
 	rc = ui_paint_bevel(gc, &rect, color1, color2, 2, NULL);
@@ -105,4 +113,137 @@
 	rc = gfx_context_delete(gc);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Get bevel inside */
+PCUT_TEST(get_bevel_inside)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	gfx_rect_t rect;
+	gfx_rect_t inside;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 30;
+	rect.p1.y = 40;
+
+	ui_paint_get_bevel_inside(gc, &rect, 2, &inside);
+	PCUT_ASSERT_INT_EQUALS(12, inside.p0.x);
+	PCUT_ASSERT_INT_EQUALS(22, inside.p0.y);
+	PCUT_ASSERT_INT_EQUALS(28, inside.p1.x);
+	PCUT_ASSERT_INT_EQUALS(38, inside.p1.y);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Paint inset frame */
+PCUT_TEST(inset_frame)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	ui_resource_t *resource = NULL;
+	test_gc_t tgc;
+	gfx_rect_t rect;
+	gfx_rect_t inside;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 30;
+	rect.p1.y = 40;
+
+	/* Paint inset frame with NULL 'inside' output parameter */
+	rc = ui_paint_inset_frame(resource, &rect, NULL);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Paint inset frame with valid 'inside' output parameter */
+	rc = ui_paint_inset_frame(resource, &rect, &inside);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_resource_destroy(resource);
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Get inset frame inside */
+PCUT_TEST(get_inset_frame_inside)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	ui_resource_t *resource = NULL;
+	test_gc_t tgc;
+	gfx_rect_t rect;
+	gfx_rect_t inside;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 30;
+	rect.p1.y = 40;
+
+	ui_paint_get_inset_frame_inside(resource, &rect, &inside);
+	PCUT_ASSERT_INT_EQUALS(12, inside.p0.x);
+	PCUT_ASSERT_INT_EQUALS(22, inside.p0.y);
+	PCUT_ASSERT_INT_EQUALS(28, inside.p1.x);
+	PCUT_ASSERT_INT_EQUALS(38, inside.p1.y);
+
+	ui_resource_destroy(resource);
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Paint filled circle */
+PCUT_TEST(filled_circle)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	gfx_coord2_t center;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Paint filled circle / upper-left half */
+	rc = ui_paint_filled_circle(gc, &center, 10, ui_fcircle_upleft);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Paint filled circle / lower-right half */
+	rc = ui_paint_filled_circle(gc, &center, 10, ui_fcircle_lowright);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Paint entire filled circle */
+	rc = ui_paint_filled_circle(gc, &center, 10, ui_fcircle_entire);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	(void) arg;
+	(void) rect;
+	return EOK;
 }
 
Index: uspace/lib/ui/test/pbutton.c
===================================================================
--- uspace/lib/ui/test/pbutton.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/test/pbutton.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,6 +41,8 @@
 PCUT_TEST_SUITE(pbutton);
 
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
+static errno_t testgc_update(void *);
 static errno_t testgc_bitmap_create(void *, gfx_bitmap_params_t *,
     gfx_bitmap_alloc_t *, void **);
@@ -50,6 +52,8 @@
 
 static gfx_context_ops_t ops = {
+	.set_clip_rect = testgc_set_clip_rect,
 	.set_color = testgc_set_color,
 	.fill_rect = testgc_fill_rect,
+	.update = testgc_update,
 	.bitmap_create = testgc_bitmap_create,
 	.bitmap_destroy = testgc_bitmap_destroy,
@@ -178,5 +182,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ui_resource_create(gc, &resource);
+	rc = ui_resource_create(gc, false, &resource);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(resource);
@@ -235,5 +239,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ui_resource_create(gc, &resource);
+	rc = ui_resource_create(gc, false, &resource);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(resource);
@@ -279,5 +283,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ui_resource_create(gc, &resource);
+	rc = ui_resource_create(gc, false, &resource);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(resource);
@@ -328,5 +332,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ui_resource_create(gc, &resource);
+	rc = ui_resource_create(gc, false, &resource);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(resource);
@@ -384,5 +388,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ui_resource_create(gc, &resource);
+	rc = ui_resource_create(gc, false, &resource);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(resource);
@@ -445,5 +449,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ui_resource_create(gc, &resource);
+	rc = ui_resource_create(gc, false, &resource);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(resource);
@@ -488,4 +492,11 @@
 }
 
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	(void) arg;
+	(void) rect;
+	return EOK;
+}
+
 static errno_t testgc_set_color(void *arg, gfx_color_t *color)
 {
@@ -499,4 +510,10 @@
 	(void) arg;
 	(void) rect;
+	return EOK;
+}
+
+static errno_t testgc_update(void *arg)
+{
+	(void) arg;
 	return EOK;
 }
Index: uspace/lib/ui/test/popup.c
===================================================================
--- uspace/lib/ui/test/popup.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/test/popup.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2021 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 <gfx/context.h>
+#include <gfx/coord.h>
+#include <gfx/render.h>
+#include <io/kbd_event.h>
+#include <io/pos_event.h>
+#include <mem.h>
+#include <pcut/pcut.h>
+#include <stdbool.h>
+#include <ui/control.h>
+#include <ui/popup.h>
+#include <ui/resource.h>
+#include <ui/ui.h>
+#include <ui/window.h>
+#include "../private/popup.h"
+#include "../private/window.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(popup);
+
+static void test_popup_close(ui_popup_t *, void *);
+static void test_popup_kbd(ui_popup_t *, void *, kbd_event_t *);
+static errno_t test_popup_paint(ui_popup_t *, void *);
+static void test_popup_pos(ui_popup_t *, void *, pos_event_t *);
+
+static ui_popup_cb_t test_popup_cb = {
+	.close = test_popup_close,
+	.kbd = test_popup_kbd,
+	.paint = test_popup_paint,
+	.pos = test_popup_pos,
+};
+
+static ui_popup_cb_t dummy_popup_cb = {
+};
+
+static errno_t test_ctl_paint(void *);
+static ui_evclaim_t test_ctl_pos_event(void *, pos_event_t *);
+static void test_ctl_unfocus(void *);
+
+static ui_control_ops_t test_ctl_ops = {
+	.paint = test_ctl_paint,
+	.pos_event = test_ctl_pos_event,
+	.unfocus = test_ctl_unfocus
+};
+
+typedef struct {
+	errno_t rc;
+	bool close;
+	bool focus;
+	bool kbd;
+	kbd_event_t kbd_event;
+	bool paint;
+	bool pos;
+	pos_event_t pos_event;
+	bool unfocus;
+} test_cb_resp_t;
+
+typedef struct {
+	errno_t rc;
+	ui_evclaim_t claim;
+	bool paint;
+	bool pos;
+	pos_event_t pos_event;
+	bool unfocus;
+} test_ctl_resp_t;
+
+/** Create and destroy popup window */
+PCUT_TEST(create_destroy)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_popup_params_t params;
+	ui_popup_t *popup = NULL;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_popup_params_init(&params);
+
+	rc = ui_popup_create(ui, NULL, &params, &popup);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(popup);
+
+	ui_popup_destroy(popup);
+	ui_destroy(ui);
+}
+
+/** ui_popup_destroy() can take NULL argument (no-op) */
+PCUT_TEST(destroy_null)
+{
+	ui_popup_destroy(NULL);
+}
+
+/** ui_popup_add()/ui_popup_remove() ... */
+PCUT_TEST(add_remove)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_popup_params_t params;
+	ui_popup_t *popup = NULL;
+	ui_control_t *control = NULL;
+	test_ctl_resp_t resp;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_popup_params_init(&params);
+
+	rc = ui_popup_create(ui, NULL, &params, &popup);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(popup);
+
+	rc = ui_control_new(&test_ctl_ops, &resp, &control);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Control not called since it hasn't been added yet */
+	resp.rc = ENOMEM;
+	resp.paint = false;
+	rc = ui_window_def_paint(popup->window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_FALSE(resp.paint);
+
+	ui_popup_add(popup, control);
+
+	/* Now paint request should be delivered to control */
+	resp.rc = EOK;
+	resp.paint = false;
+	rc = ui_window_def_paint(popup->window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(resp.paint);
+
+	ui_popup_remove(popup, control);
+
+	/*
+	 * After having removed the control the request should no longer
+	 * be delivered to it.
+	 */
+	resp.rc = ENOMEM;
+	resp.paint = false;
+	rc = ui_window_def_paint(popup->window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_FALSE(resp.paint);
+
+	ui_popup_destroy(popup);
+	ui_destroy(ui);
+}
+
+/** ui_popup_get_res/gc() return valid objects */
+PCUT_TEST(get_res_gc)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_wnd_params_t params;
+	ui_window_t *window = NULL;
+	ui_resource_t *res;
+	gfx_context_t *gc;
+	gfx_rect_t rect;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	res = ui_window_get_res(window);
+	PCUT_ASSERT_NOT_NULL(res);
+
+	gc = ui_window_get_gc(window);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	ui_window_get_app_rect(window, &rect);
+
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** Test position event callback set via ui_popup_set_cb() */
+PCUT_TEST(send_pos)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_popup_params_t params;
+	ui_popup_t *popup = NULL;
+	pos_event_t pos_event;
+	test_cb_resp_t resp;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_popup_params_init(&params);
+
+	rc = ui_popup_create(ui, NULL, &params, &popup);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(popup);
+
+	pos_event.pos_id = 1;
+	pos_event.type = POS_PRESS;
+	pos_event.btn_num = 2;
+	pos_event.hpos = 3;
+	pos_event.vpos = 4;
+
+	/* Pos callback with no callbacks set */
+	ui_window_send_pos(popup->window, &pos_event);
+
+	/* Pos callback with pos callback not implemented */
+	ui_popup_set_cb(popup, &dummy_popup_cb, NULL);
+	ui_window_send_pos(popup->window, &pos_event);
+
+	/* Pos callback with real callback set */
+	resp.pos = false;
+	ui_popup_set_cb(popup, &test_popup_cb, &resp);
+	ui_window_send_pos(popup->window, &pos_event);
+	PCUT_ASSERT_TRUE(resp.pos);
+	PCUT_ASSERT_INT_EQUALS(pos_event.pos_id, resp.pos_event.pos_id);
+	PCUT_ASSERT_EQUALS(pos_event.type, resp.pos_event.type);
+	PCUT_ASSERT_INT_EQUALS(pos_event.btn_num, resp.pos_event.btn_num);
+	PCUT_ASSERT_INT_EQUALS(pos_event.hpos, resp.pos_event.hpos);
+	PCUT_ASSERT_INT_EQUALS(pos_event.vpos, resp.pos_event.vpos);
+
+	ui_popup_destroy(popup);
+	ui_destroy(ui);
+}
+
+static void test_popup_close(ui_popup_t *popup, void *arg)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->close = true;
+}
+
+static void test_popup_kbd(ui_popup_t *popup, void *arg,
+    kbd_event_t *event)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->kbd = true;
+	resp->kbd_event = *event;
+}
+
+static errno_t test_popup_paint(ui_popup_t *popup, void *arg)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->paint = true;
+	return resp->rc;
+}
+
+static void test_popup_pos(ui_popup_t *popup, void *arg,
+    pos_event_t *event)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->pos = true;
+	resp->pos_event = *event;
+}
+
+static errno_t test_ctl_paint(void *arg)
+{
+	test_ctl_resp_t *resp = (test_ctl_resp_t *) arg;
+
+	resp->paint = true;
+	return resp->rc;
+}
+
+static ui_evclaim_t test_ctl_pos_event(void *arg, pos_event_t *event)
+{
+	test_ctl_resp_t *resp = (test_ctl_resp_t *) arg;
+
+	resp->pos = true;
+	resp->pos_event = *event;
+
+	return resp->claim;
+}
+
+static void test_ctl_unfocus(void *arg)
+{
+	test_ctl_resp_t *resp = (test_ctl_resp_t *) arg;
+
+	resp->unfocus = true;
+}
+
+PCUT_EXPORT(popup);
Index: uspace/lib/ui/test/rbutton.c
===================================================================
--- uspace/lib/ui/test/rbutton.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/test/rbutton.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,656 @@
+/*
+ * Copyright (c) 2021 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 <gfx/context.h>
+#include <gfx/coord.h>
+#include <mem.h>
+#include <pcut/pcut.h>
+#include <stdbool.h>
+#include <ui/control.h>
+#include <ui/rbutton.h>
+#include <ui/resource.h>
+#include "../private/rbutton.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(rbutton);
+
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
+static errno_t testgc_set_color(void *, gfx_color_t *);
+static errno_t testgc_fill_rect(void *, gfx_rect_t *);
+static errno_t testgc_update(void *);
+static errno_t testgc_bitmap_create(void *, gfx_bitmap_params_t *,
+    gfx_bitmap_alloc_t *, void **);
+static errno_t testgc_bitmap_destroy(void *);
+static errno_t testgc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
+static errno_t testgc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
+
+static gfx_context_ops_t ops = {
+	.set_clip_rect = testgc_set_clip_rect,
+	.set_color = testgc_set_color,
+	.fill_rect = testgc_fill_rect,
+	.update = testgc_update,
+	.bitmap_create = testgc_bitmap_create,
+	.bitmap_destroy = testgc_bitmap_destroy,
+	.bitmap_render = testgc_bitmap_render,
+	.bitmap_get_alloc = testgc_bitmap_get_alloc
+};
+
+static void test_rbutton_select(ui_rbutton_group_t *, void *, void *);
+
+static ui_rbutton_group_cb_t test_rbutton_group_cb = {
+	.selected = test_rbutton_select
+};
+
+static ui_rbutton_group_cb_t dummy_rbutton_group_cb = {
+};
+
+typedef struct {
+	bool bm_created;
+	bool bm_destroyed;
+	gfx_bitmap_params_t bm_params;
+	void *bm_pixels;
+	gfx_rect_t bm_srect;
+	gfx_coord2_t bm_offs;
+	bool bm_rendered;
+	bool bm_got_alloc;
+} test_gc_t;
+
+typedef struct {
+	test_gc_t *tgc;
+	gfx_bitmap_alloc_t alloc;
+	bool myalloc;
+} testgc_bitmap_t;
+
+typedef struct {
+	bool selected;
+} test_cb_resp_t;
+
+/** Create and destroy radio button */
+PCUT_TEST(create_destroy)
+{
+	ui_rbutton_group_t *group = NULL;
+	ui_rbutton_t *rbutton = NULL;
+	errno_t rc;
+
+	rc = ui_rbutton_group_create(NULL, &group);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_rbutton_create(group, "Hello", NULL, &rbutton);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(rbutton);
+
+	ui_rbutton_destroy(rbutton);
+	ui_rbutton_group_destroy(group);
+}
+
+/** ui_rbutton_destroy() can take NULL argument (no-op) */
+PCUT_TEST(destroy_null)
+{
+	ui_rbutton_destroy(NULL);
+}
+
+/** ui_rbutton_ctl() returns control that has a working virtual destructor */
+PCUT_TEST(ctl)
+{
+	ui_rbutton_group_t *group = NULL;
+	ui_rbutton_t *rbutton;
+	ui_control_t *control;
+	errno_t rc;
+
+	rc = ui_rbutton_group_create(NULL, &group);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_rbutton_create(group, "Hello", NULL, &rbutton);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	control = ui_rbutton_ctl(rbutton);
+	PCUT_ASSERT_NOT_NULL(control);
+
+	ui_control_destroy(control);
+	ui_rbutton_group_destroy(group);
+}
+
+/** Set radio button rectangle sets internal field */
+PCUT_TEST(set_rect)
+{
+	ui_rbutton_group_t *group = NULL;
+	ui_rbutton_t *rbutton;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rc = ui_rbutton_group_create(NULL, &group);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_rbutton_create(group, "Hello", NULL, &rbutton);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+
+	ui_rbutton_set_rect(rbutton, &rect);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.x, rbutton->rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.y, rbutton->rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.x, rbutton->rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.y, rbutton->rect.p1.y);
+
+	ui_rbutton_destroy(rbutton);
+	ui_rbutton_group_destroy(group);
+}
+
+/** Paint radio button */
+PCUT_TEST(paint)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_rbutton_group_t *group = NULL;
+	ui_resource_t *resource = NULL;
+	ui_rbutton_t *rbutton;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_rbutton_group_create(resource, &group);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_rbutton_create(group, "Hello", NULL, &rbutton);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_rbutton_paint(rbutton);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_rbutton_destroy(rbutton);
+	ui_rbutton_group_destroy(group);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Test ui_rbutton_selected() */
+PCUT_TEST(selected)
+{
+	errno_t rc;
+	ui_rbutton_group_t *group = NULL;
+	ui_rbutton_t *rbutton;
+	test_cb_resp_t resp;
+
+	rc = ui_rbutton_group_create(NULL, &group);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_rbutton_create(group, "Hello", NULL, &rbutton);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Selected with no callbacks set */
+	ui_rbutton_selected(rbutton);
+
+	/* Selected with callback not implementing selected */
+	ui_rbutton_group_set_cb(group, &dummy_rbutton_group_cb, NULL);
+	ui_rbutton_selected(rbutton);
+
+	/* Selected with real callback set */
+	resp.selected = false;
+	ui_rbutton_group_set_cb(group, &test_rbutton_group_cb, &resp);
+	ui_rbutton_selected(rbutton);
+	PCUT_ASSERT_TRUE(resp.selected);
+
+	ui_rbutton_destroy(rbutton);
+	ui_rbutton_group_destroy(group);
+}
+
+/** Press and release radio button */
+PCUT_TEST(press_release)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_rbutton_group_t *group = NULL;
+	ui_rbutton_t *rbutton1;
+	ui_rbutton_t *rbutton2;
+	test_cb_resp_t resp;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_rbutton_group_create(resource, &group);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NULL(group->selected);
+
+	rc = ui_rbutton_create(group, "One", NULL, &rbutton1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	rc = ui_rbutton_create(group, "Two", NULL, &rbutton2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	resp.selected = false;
+	ui_rbutton_group_set_cb(group, &test_rbutton_group_cb, &resp);
+
+	PCUT_ASSERT_FALSE(rbutton2->held);
+	PCUT_ASSERT_FALSE(rbutton2->inside);
+
+	ui_rbutton_press(rbutton2);
+	PCUT_ASSERT_TRUE(rbutton2->held);
+	PCUT_ASSERT_TRUE(rbutton2->inside);
+	PCUT_ASSERT_FALSE(resp.selected);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	ui_rbutton_release(rbutton2);
+	PCUT_ASSERT_FALSE(rbutton2->held);
+	PCUT_ASSERT_TRUE(rbutton2->inside);
+	PCUT_ASSERT_TRUE(resp.selected);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton2);
+
+	ui_rbutton_destroy(rbutton1);
+	ui_rbutton_destroy(rbutton2);
+	ui_rbutton_group_destroy(group);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Press, leave and release radio button */
+PCUT_TEST(press_leave_release)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_rbutton_group_t *group = NULL;
+	ui_rbutton_t *rbutton1;
+	ui_rbutton_t *rbutton2;
+	test_cb_resp_t resp;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_rbutton_group_create(resource, &group);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NULL(group->selected);
+
+	rc = ui_rbutton_create(group, "One", NULL, &rbutton1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	rc = ui_rbutton_create(group, "Two", NULL, &rbutton2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	resp.selected = false;
+	ui_rbutton_group_set_cb(group, &test_rbutton_group_cb, &resp);
+
+	PCUT_ASSERT_FALSE(rbutton2->held);
+	PCUT_ASSERT_FALSE(rbutton2->inside);
+
+	ui_rbutton_press(rbutton2);
+	PCUT_ASSERT_TRUE(rbutton2->held);
+	PCUT_ASSERT_TRUE(rbutton2->inside);
+	PCUT_ASSERT_FALSE(resp.selected);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	ui_rbutton_leave(rbutton2);
+	PCUT_ASSERT_TRUE(rbutton2->held);
+	PCUT_ASSERT_FALSE(rbutton2->inside);
+	PCUT_ASSERT_FALSE(resp.selected);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	ui_rbutton_release(rbutton2);
+	PCUT_ASSERT_FALSE(rbutton2->held);
+	PCUT_ASSERT_FALSE(rbutton2->inside);
+	PCUT_ASSERT_FALSE(resp.selected);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	ui_rbutton_destroy(rbutton1);
+	ui_rbutton_destroy(rbutton2);
+	ui_rbutton_group_destroy(group);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Press, leave, enter and release radio button */
+PCUT_TEST(press_leave_enter_release)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_rbutton_group_t *group = NULL;
+	ui_rbutton_t *rbutton1;
+	ui_rbutton_t *rbutton2;
+	test_cb_resp_t resp;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_rbutton_group_create(resource, &group);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NULL(group->selected);
+
+	rc = ui_rbutton_create(group, "One", NULL, &rbutton1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	rc = ui_rbutton_create(group, "Two", NULL, &rbutton2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	resp.selected = false;
+	ui_rbutton_group_set_cb(group, &test_rbutton_group_cb, &resp);
+
+	PCUT_ASSERT_FALSE(rbutton2->held);
+	PCUT_ASSERT_FALSE(rbutton2->inside);
+
+	ui_rbutton_press(rbutton2);
+	PCUT_ASSERT_TRUE(rbutton2->held);
+	PCUT_ASSERT_TRUE(rbutton2->inside);
+	PCUT_ASSERT_FALSE(resp.selected);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	ui_rbutton_leave(rbutton2);
+	PCUT_ASSERT_TRUE(rbutton2->held);
+	PCUT_ASSERT_FALSE(rbutton2->inside);
+	PCUT_ASSERT_FALSE(resp.selected);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	ui_rbutton_enter(rbutton2);
+	PCUT_ASSERT_TRUE(rbutton2->held);
+	PCUT_ASSERT_TRUE(rbutton2->inside);
+	PCUT_ASSERT_FALSE(resp.selected);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton1);
+
+	ui_rbutton_release(rbutton2);
+	PCUT_ASSERT_FALSE(rbutton2->held);
+	PCUT_ASSERT_TRUE(rbutton2->inside);
+	PCUT_ASSERT_TRUE(resp.selected);
+	PCUT_ASSERT_EQUALS(group->selected, rbutton2);
+
+	ui_rbutton_destroy(rbutton1);
+	ui_rbutton_destroy(rbutton2);
+	ui_rbutton_group_destroy(group);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** ui_pos_event() correctly translates POS_PRESS/POS_RELEASE */
+PCUT_TEST(pos_event_press_release)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_rbutton_group_t *group = NULL;
+	ui_rbutton_t *rbutton;
+	ui_evclaim_t claim;
+	pos_event_t event;
+	gfx_rect_t rect;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_rbutton_group_create(resource, &group);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_rbutton_create(group, "Hello", NULL, &rbutton);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_FALSE(rbutton->held);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 30;
+	rect.p1.y = 40;
+	ui_rbutton_set_rect(rbutton, &rect);
+
+	/* Press outside is not claimed and does nothing */
+	event.type = POS_PRESS;
+	event.hpos = 9;
+	event.vpos = 20;
+	claim = ui_rbutton_pos_event(rbutton, &event);
+	PCUT_ASSERT_FALSE(rbutton->held);
+	PCUT_ASSERT_EQUALS(ui_unclaimed, claim);
+
+	/* Press inside is claimed and depresses radio button */
+	event.type = POS_PRESS;
+	event.hpos = 10;
+	event.vpos = 20;
+	claim = ui_rbutton_pos_event(rbutton, &event);
+	PCUT_ASSERT_TRUE(rbutton->held);
+	PCUT_ASSERT_EQUALS(ui_claimed, claim);
+
+	/* Release outside (or anywhere) is claimed and relases radio button */
+	event.type = POS_RELEASE;
+	event.hpos = 9;
+	event.vpos = 20;
+	claim = ui_rbutton_pos_event(rbutton, &event);
+	PCUT_ASSERT_FALSE(rbutton->held);
+	PCUT_ASSERT_EQUALS(ui_claimed, claim);
+
+	ui_rbutton_destroy(rbutton);
+	ui_rbutton_group_destroy(group);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** ui_pos_event() correctly translates POS_UPDATE to enter/leave */
+PCUT_TEST(pos_event_enter_leave)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_rbutton_group_t *group = NULL;
+	ui_rbutton_t *rbutton;
+	pos_event_t event;
+	gfx_rect_t rect;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_rbutton_group_create(resource, &group);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_rbutton_create(group, "Hello", NULL, &rbutton);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_FALSE(rbutton->inside);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 30;
+	rect.p1.y = 40;
+	ui_rbutton_set_rect(rbutton, &rect);
+
+	/* Moving outside does nothing */
+	event.type = POS_UPDATE;
+	event.hpos = 9;
+	event.vpos = 20;
+	ui_rbutton_pos_event(rbutton, &event);
+	PCUT_ASSERT_FALSE(rbutton->inside);
+
+	/* Moving inside sets inside flag */
+	event.type = POS_UPDATE;
+	event.hpos = 10;
+	event.vpos = 20;
+	ui_rbutton_pos_event(rbutton, &event);
+	PCUT_ASSERT_TRUE(rbutton->inside);
+
+	/* Moving outside clears inside flag */
+	event.type = POS_UPDATE;
+	event.hpos = 9;
+	event.vpos = 20;
+	ui_rbutton_pos_event(rbutton, &event);
+	PCUT_ASSERT_FALSE(rbutton->inside);
+
+	ui_rbutton_destroy(rbutton);
+	ui_rbutton_group_destroy(group);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	(void) arg;
+	(void) rect;
+	return EOK;
+}
+
+static errno_t testgc_set_color(void *arg, gfx_color_t *color)
+{
+	(void) arg;
+	(void) color;
+	return EOK;
+}
+
+static errno_t testgc_fill_rect(void *arg, gfx_rect_t *rect)
+{
+	(void) arg;
+	(void) rect;
+	return EOK;
+}
+
+static errno_t testgc_update(void *arg)
+{
+	(void) arg;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+	testgc_bitmap_t *tbm;
+
+	tbm = calloc(1, sizeof(testgc_bitmap_t));
+	if (tbm == NULL)
+		return ENOMEM;
+
+	if (alloc == NULL) {
+		tbm->alloc.pitch = (params->rect.p1.x - params->rect.p0.x) *
+		    sizeof(uint32_t);
+		tbm->alloc.off0 = 0;
+		tbm->alloc.pixels = calloc(sizeof(uint32_t),
+		    (params->rect.p1.x - params->rect.p0.x) *
+		    (params->rect.p1.y - params->rect.p0.y));
+		tbm->myalloc = true;
+		if (tbm->alloc.pixels == NULL) {
+			free(tbm);
+			return ENOMEM;
+		}
+	} else {
+		tbm->alloc = *alloc;
+	}
+
+	tbm->tgc = tgc;
+	tgc->bm_created = true;
+	tgc->bm_params = *params;
+	tgc->bm_pixels = tbm->alloc.pixels;
+	*rbm = (void *)tbm;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_destroy(void *bm)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	if (tbm->myalloc)
+		free(tbm->alloc.pixels);
+	tbm->tgc->bm_destroyed = true;
+	free(tbm);
+	return EOK;
+}
+
+static errno_t testgc_bitmap_render(void *bm, gfx_rect_t *srect,
+    gfx_coord2_t *offs)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	tbm->tgc->bm_rendered = true;
+	tbm->tgc->bm_srect = *srect;
+	tbm->tgc->bm_offs = *offs;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	*alloc = tbm->alloc;
+	tbm->tgc->bm_got_alloc = true;
+	return EOK;
+}
+
+static void test_rbutton_select(ui_rbutton_group_t *group, void *arg,
+    void *barg)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->selected = true;
+}
+
+PCUT_EXPORT(rbutton);
Index: uspace/lib/ui/test/resource.c
===================================================================
--- uspace/lib/ui/test/resource.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/test/resource.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -44,4 +44,6 @@
 static errno_t testgc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
 
+static void test_expose(void *);
+
 static gfx_context_ops_t ops = {
 	.bitmap_create = testgc_bitmap_create,
@@ -68,4 +70,8 @@
 } testgc_bitmap_t;
 
+typedef struct {
+	bool expose;
+} test_resp_t;
+
 /** Create and destroy UI resource */
 PCUT_TEST(create_destroy)
@@ -80,5 +86,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ui_resource_create(gc, &resource);
+	rc = ui_resource_create(gc, false, &resource);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(resource);
@@ -97,4 +103,33 @@
 {
 	ui_resource_destroy(NULL);
+}
+
+/** ui_resource_set_expose_cb() / ui_resource_expose() */
+PCUT_TEST(set_expose_cb_expose)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	test_resp_t resp;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	ui_resource_set_expose_cb(resource, test_expose, &resp);
+
+	resp.expose = false;
+	ui_resource_expose(resource);
+	PCUT_ASSERT_TRUE(resp.expose);
+
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 }
 
@@ -161,3 +196,10 @@
 }
 
+static void test_expose(void *arg)
+{
+	test_resp_t *resp = (test_resp_t *) arg;
+
+	resp->expose = true;
+}
+
 PCUT_EXPORT(resource);
Index: uspace/lib/ui/test/slider.c
===================================================================
--- uspace/lib/ui/test/slider.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/lib/ui/test/slider.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,531 @@
+/*
+ * Copyright (c) 2021 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 <gfx/context.h>
+#include <gfx/coord.h>
+#include <mem.h>
+#include <pcut/pcut.h>
+#include <stdbool.h>
+#include <ui/control.h>
+#include <ui/slider.h>
+#include <ui/resource.h>
+#include "../private/slider.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(slider);
+
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
+static errno_t testgc_set_color(void *, gfx_color_t *);
+static errno_t testgc_fill_rect(void *, gfx_rect_t *);
+static errno_t testgc_update(void *);
+static errno_t testgc_bitmap_create(void *, gfx_bitmap_params_t *,
+    gfx_bitmap_alloc_t *, void **);
+static errno_t testgc_bitmap_destroy(void *);
+static errno_t testgc_bitmap_render(void *, gfx_rect_t *, gfx_coord2_t *);
+static errno_t testgc_bitmap_get_alloc(void *, gfx_bitmap_alloc_t *);
+
+static gfx_context_ops_t ops = {
+	.set_clip_rect = testgc_set_clip_rect,
+	.set_color = testgc_set_color,
+	.fill_rect = testgc_fill_rect,
+	.update = testgc_update,
+	.bitmap_create = testgc_bitmap_create,
+	.bitmap_destroy = testgc_bitmap_destroy,
+	.bitmap_render = testgc_bitmap_render,
+	.bitmap_get_alloc = testgc_bitmap_get_alloc
+};
+
+static void test_slider_moved(ui_slider_t *, void *, gfx_coord_t);
+
+static ui_slider_cb_t test_slider_cb = {
+	.moved = test_slider_moved
+};
+
+static ui_slider_cb_t dummy_slider_cb = {
+};
+
+typedef struct {
+	bool bm_created;
+	bool bm_destroyed;
+	gfx_bitmap_params_t bm_params;
+	void *bm_pixels;
+	gfx_rect_t bm_srect;
+	gfx_coord2_t bm_offs;
+	bool bm_rendered;
+	bool bm_got_alloc;
+} test_gc_t;
+
+typedef struct {
+	test_gc_t *tgc;
+	gfx_bitmap_alloc_t alloc;
+	bool myalloc;
+} testgc_bitmap_t;
+
+typedef struct {
+	bool moved;
+	gfx_coord_t pos;
+} test_cb_resp_t;
+
+/** Create and destroy slider */
+PCUT_TEST(create_destroy)
+{
+	ui_slider_t *slider = NULL;
+	errno_t rc;
+
+	rc = ui_slider_create(NULL, "Hello", &slider);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(slider);
+
+	ui_slider_destroy(slider);
+}
+
+/** ui_slider_destroy() can take NULL argument (no-op) */
+PCUT_TEST(destroy_null)
+{
+	ui_slider_destroy(NULL);
+}
+
+/** ui_slider_ctl() returns control that has a working virtual destructor */
+PCUT_TEST(ctl)
+{
+	ui_slider_t *slider;
+	ui_control_t *control;
+	errno_t rc;
+
+	rc = ui_slider_create(NULL, "Hello", &slider);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	control = ui_slider_ctl(slider);
+	PCUT_ASSERT_NOT_NULL(control);
+
+	ui_control_destroy(control);
+}
+
+/** Set slider rectangle sets internal field */
+PCUT_TEST(set_rect)
+{
+	ui_slider_t *slider;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	rc = ui_slider_create(NULL, "Hello", &slider);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+
+	ui_slider_set_rect(slider, &rect);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.x, slider->rect.p0.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p0.y, slider->rect.p0.y);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.x, slider->rect.p1.x);
+	PCUT_ASSERT_INT_EQUALS(rect.p1.y, slider->rect.p1.y);
+
+	ui_slider_destroy(slider);
+}
+
+/** Paint slider */
+PCUT_TEST(paint)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_slider_t *slider;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_slider_create(resource, "Hello", &slider);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_slider_paint(slider);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_slider_destroy(slider);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Test ui_slider_moved() */
+PCUT_TEST(moved)
+{
+	errno_t rc;
+	ui_slider_t *slider;
+	test_cb_resp_t resp;
+
+	rc = ui_slider_create(NULL, "Hello", &slider);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Moved with no callbacks set */
+	ui_slider_moved(slider, 42);
+
+	/* Moved with callback not implementing clicked */
+	ui_slider_set_cb(slider, &dummy_slider_cb, NULL);
+	ui_slider_moved(slider, 42);
+
+	/* Moved with real callback set */
+	resp.moved = false;
+	resp.pos = 0;
+	ui_slider_set_cb(slider, &test_slider_cb, &resp);
+	ui_slider_moved(slider, 42);
+	PCUT_ASSERT_TRUE(resp.moved);
+	PCUT_ASSERT_INT_EQUALS(42, resp.pos);
+
+	ui_slider_destroy(slider);
+}
+
+/** Press and release slider */
+PCUT_TEST(press_release)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	gfx_coord2_t pos;
+	gfx_rect_t rect;
+	ui_slider_t *slider;
+	test_cb_resp_t resp;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_slider_create(resource, "Hello", &slider);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 110;
+	rect.p1.y = 120;
+	ui_slider_set_rect(slider, &rect);
+
+	resp.moved = false;
+	ui_slider_set_cb(slider, &test_slider_cb, &resp);
+
+	PCUT_ASSERT_FALSE(slider->held);
+
+	pos.x = 11;
+	pos.y = 22;
+
+	ui_slider_press(slider, &pos);
+	PCUT_ASSERT_TRUE(slider->held);
+	PCUT_ASSERT_FALSE(resp.moved);
+
+	pos.x = 21;
+	pos.y = 32;
+
+	ui_slider_release(slider, &pos);
+	PCUT_ASSERT_FALSE(slider->held);
+	PCUT_ASSERT_TRUE(resp.moved);
+	PCUT_ASSERT_INT_EQUALS(10, slider->pos);
+
+	ui_slider_destroy(slider);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Press, update and release slider */
+PCUT_TEST(press_uodate_release)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	gfx_coord2_t pos;
+	gfx_rect_t rect;
+	ui_slider_t *slider;
+	test_cb_resp_t resp;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_slider_create(resource, "Hello", &slider);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 110;
+	rect.p1.y = 120;
+	ui_slider_set_rect(slider, &rect);
+
+	resp.moved = false;
+	ui_slider_set_cb(slider, &test_slider_cb, &resp);
+
+	PCUT_ASSERT_FALSE(slider->held);
+
+	pos.x = 11;
+	pos.y = 22;
+
+	ui_slider_press(slider, &pos);
+	PCUT_ASSERT_TRUE(slider->held);
+	PCUT_ASSERT_FALSE(resp.moved);
+
+	pos.x = 21;
+	pos.y = 32;
+
+	ui_slider_update(slider, &pos);
+	PCUT_ASSERT_TRUE(slider->held);
+	PCUT_ASSERT_TRUE(resp.moved);
+	PCUT_ASSERT_INT_EQUALS(10, slider->pos);
+
+	pos.x = 31;
+	pos.y = 42;
+
+	ui_slider_release(slider, &pos);
+	PCUT_ASSERT_FALSE(slider->held);
+	PCUT_ASSERT_TRUE(resp.moved);
+	PCUT_ASSERT_INT_EQUALS(20, slider->pos);
+
+	ui_slider_destroy(slider);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** ui_pos_event() correctly translates POS_PRESS/POS_RELEASE */
+PCUT_TEST(pos_event_press_release)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_slider_t *slider;
+	ui_evclaim_t claim;
+	pos_event_t event;
+	gfx_rect_t rect;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_slider_create(resource, "Hello", &slider);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_FALSE(slider->held);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 30;
+	rect.p1.y = 40;
+	ui_slider_set_rect(slider, &rect);
+
+	/* Press outside is not claimed and does nothing */
+	event.type = POS_PRESS;
+	event.hpos = 1;
+	event.vpos = 2;
+	claim = ui_slider_pos_event(slider, &event);
+	PCUT_ASSERT_FALSE(slider->held);
+	PCUT_ASSERT_EQUALS(ui_unclaimed, claim);
+
+	/* Press inside is claimed and depresses slider */
+	event.type = POS_PRESS;
+	event.hpos = 11;
+	event.vpos = 22;
+	claim = ui_slider_pos_event(slider, &event);
+	PCUT_ASSERT_TRUE(slider->held);
+	PCUT_ASSERT_EQUALS(ui_claimed, claim);
+
+	/* Release outside (or anywhere) is claimed and relases slider */
+	event.type = POS_RELEASE;
+	event.hpos = 41;
+	event.vpos = 32;
+	claim = ui_slider_pos_event(slider, &event);
+	PCUT_ASSERT_FALSE(slider->held);
+	PCUT_ASSERT_EQUALS(ui_claimed, claim);
+
+	ui_slider_destroy(slider);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** ui_slider_length() correctly determines slider length */
+PCUT_TEST(length)
+{
+	errno_t rc;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
+	ui_slider_t *slider;
+	gfx_coord_t length;
+	gfx_rect_t rect;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_slider_create(resource, "Hello", &slider);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_FALSE(slider->held);
+
+	rect.p0.x = 10;
+	rect.p0.y = 20;
+	rect.p1.x = 110;
+	rect.p1.y = 120;
+	ui_slider_set_rect(slider, &rect);
+
+	length = ui_slider_length(slider);
+	PCUT_ASSERT_INT_EQUALS(110 - 10 - 15, length);
+
+	ui_slider_destroy(slider);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	(void) arg;
+	(void) rect;
+	return EOK;
+}
+
+static errno_t testgc_set_color(void *arg, gfx_color_t *color)
+{
+	(void) arg;
+	(void) color;
+	return EOK;
+}
+
+static errno_t testgc_fill_rect(void *arg, gfx_rect_t *rect)
+{
+	(void) arg;
+	(void) rect;
+	return EOK;
+}
+
+static errno_t testgc_update(void *arg)
+{
+	(void) arg;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_create(void *arg, gfx_bitmap_params_t *params,
+    gfx_bitmap_alloc_t *alloc, void **rbm)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+	testgc_bitmap_t *tbm;
+
+	tbm = calloc(1, sizeof(testgc_bitmap_t));
+	if (tbm == NULL)
+		return ENOMEM;
+
+	if (alloc == NULL) {
+		tbm->alloc.pitch = (params->rect.p1.x - params->rect.p0.x) *
+		    sizeof(uint32_t);
+		tbm->alloc.off0 = 0;
+		tbm->alloc.pixels = calloc(sizeof(uint32_t),
+		    (params->rect.p1.x - params->rect.p0.x) *
+		    (params->rect.p1.y - params->rect.p0.y));
+		tbm->myalloc = true;
+		if (tbm->alloc.pixels == NULL) {
+			free(tbm);
+			return ENOMEM;
+		}
+	} else {
+		tbm->alloc = *alloc;
+	}
+
+	tbm->tgc = tgc;
+	tgc->bm_created = true;
+	tgc->bm_params = *params;
+	tgc->bm_pixels = tbm->alloc.pixels;
+	*rbm = (void *)tbm;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_destroy(void *bm)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	if (tbm->myalloc)
+		free(tbm->alloc.pixels);
+	tbm->tgc->bm_destroyed = true;
+	free(tbm);
+	return EOK;
+}
+
+static errno_t testgc_bitmap_render(void *bm, gfx_rect_t *srect,
+    gfx_coord2_t *offs)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	tbm->tgc->bm_rendered = true;
+	tbm->tgc->bm_srect = *srect;
+	tbm->tgc->bm_offs = *offs;
+	return EOK;
+}
+
+static errno_t testgc_bitmap_get_alloc(void *bm, gfx_bitmap_alloc_t *alloc)
+{
+	testgc_bitmap_t *tbm = (testgc_bitmap_t *)bm;
+	*alloc = tbm->alloc;
+	tbm->tgc->bm_got_alloc = true;
+	return EOK;
+}
+
+static void test_slider_moved(ui_slider_t *slider, void *arg, gfx_coord_t pos)
+{
+	test_cb_resp_t *resp = (test_cb_resp_t *) arg;
+
+	resp->moved = true;
+	resp->pos = pos;
+}
+
+PCUT_EXPORT(slider);
Index: uspace/lib/ui/test/ui.c
===================================================================
--- uspace/lib/ui/test/ui.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/test/ui.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -35,6 +35,6 @@
 PCUT_TEST_SUITE(ui);
 
-/** Create and destroy UI */
-PCUT_TEST(create_destroy)
+/** Create and destroy UI with display */
+PCUT_TEST(create_disp_destroy)
 {
 	ui_t *ui = NULL;
@@ -49,4 +49,18 @@
 }
 
+/** Create and destroy UI with console */
+PCUT_TEST(create_cons_destroy)
+{
+	ui_t *ui = NULL;
+	errno_t rc;
+
+	rc = ui_create_cons(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(ui);
+	PCUT_ASSERT_NULL(ui->console);
+
+	ui_destroy(ui);
+}
+
 /** ui_destroy() can take NULL argument (no-op) */
 PCUT_TEST(destroy_null)
@@ -55,3 +69,85 @@
 }
 
+/** ui_run() / ui_quit() */
+PCUT_TEST(run_quit)
+{
+	ui_t *ui = NULL;
+	errno_t rc;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(ui);
+
+	/* Set exit flag */
+	ui_quit(ui);
+
+	/* ui_run() should return immediately */
+	ui_run(ui);
+
+	ui_destroy(ui);
+}
+
+/** ui_paint() */
+PCUT_TEST(paint)
+{
+	ui_t *ui = NULL;
+	errno_t rc;
+
+	rc = ui_create_cons(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(ui);
+
+	/* In absence of windows ui_paint() should just return EOK */
+	rc = ui_paint(ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_destroy(ui);
+}
+
+/** ui_is_textmode() */
+PCUT_TEST(is_textmode)
+{
+	ui_t *ui = NULL;
+	errno_t rc;
+
+	rc = ui_create_disp((display_t *)(-1), &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(ui);
+
+	PCUT_ASSERT_FALSE(ui_is_textmode(ui));
+
+	ui_destroy(ui);
+
+	rc = ui_create_cons((console_ctrl_t *)(-1), &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(ui);
+
+	PCUT_ASSERT_TRUE(ui_is_textmode(ui));
+
+	ui_destroy(ui);
+}
+
+/** ui_is_fullscreen() */
+PCUT_TEST(is_fullscreen)
+{
+	ui_t *ui = NULL;
+	errno_t rc;
+
+	rc = ui_create_disp((display_t *)(-1), &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(ui);
+
+	PCUT_ASSERT_FALSE(ui_is_fullscreen(ui));
+
+	ui_destroy(ui);
+
+	rc = ui_create_cons((console_ctrl_t *)(-1), &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(ui);
+
+	PCUT_ASSERT_TRUE(ui_is_fullscreen(ui));
+
+	ui_destroy(ui);
+}
+
 PCUT_EXPORT(ui);
Index: uspace/lib/ui/test/wdecor.c
===================================================================
--- uspace/lib/ui/test/wdecor.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/test/wdecor.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,6 +41,8 @@
 PCUT_TEST_SUITE(wdecor);
 
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
+static errno_t testgc_update(void *);
 static errno_t testgc_bitmap_create(void *, gfx_bitmap_params_t *,
     gfx_bitmap_alloc_t *, void **);
@@ -50,6 +52,8 @@
 
 static gfx_context_ops_t ops = {
+	.set_clip_rect = testgc_set_clip_rect,
 	.set_color = testgc_set_color,
 	.fill_rect = testgc_fill_rect,
+	.update = testgc_update,
 	.bitmap_create = testgc_bitmap_create,
 	.bitmap_destroy = testgc_bitmap_destroy,
@@ -177,5 +181,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = ui_resource_create(gc, &resource);
+	rc = ui_resource_create(gc, false, &resource);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 	PCUT_ASSERT_NOT_NULL(resource);
@@ -354,11 +358,22 @@
 PCUT_TEST(pos_event_move)
 {
-	ui_wdecor_t *wdecor;
+	errno_t rc;
 	gfx_rect_t rect;
 	pos_event_t event;
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
 	test_cb_resp_t resp;
-	errno_t rc;
-
-	rc = ui_wdecor_create(NULL, "Hello", ui_wds_decorated, &wdecor);
+	ui_resource_t *resource = NULL;
+	ui_wdecor_t *wdecor;
+
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_wdecor_create(resource, "Hello", ui_wds_decorated, &wdecor);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -386,4 +401,8 @@
 
 	ui_wdecor_destroy(wdecor);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 }
 
@@ -391,4 +410,7 @@
 PCUT_TEST(get_geom_none)
 {
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
 	ui_wdecor_t *wdecor;
 	gfx_rect_t rect;
@@ -396,5 +418,13 @@
 	errno_t rc;
 
-	rc = ui_wdecor_create(NULL, "Hello", ui_wds_none, &wdecor);
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_wdecor_create(resource, "Hello", ui_wds_none, &wdecor);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -428,4 +458,8 @@
 
 	ui_wdecor_destroy(wdecor);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 }
 
@@ -433,4 +467,7 @@
 PCUT_TEST(get_geom_frame)
 {
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
 	ui_wdecor_t *wdecor;
 	gfx_rect_t rect;
@@ -438,5 +475,13 @@
 	errno_t rc;
 
-	rc = ui_wdecor_create(NULL, "Hello", ui_wds_frame, &wdecor);
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_wdecor_create(resource, "Hello", ui_wds_frame, &wdecor);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -470,4 +515,8 @@
 
 	ui_wdecor_destroy(wdecor);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 }
 
@@ -475,4 +524,7 @@
 PCUT_TEST(get_geom_frame_titlebar)
 {
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
 	ui_wdecor_t *wdecor;
 	gfx_rect_t rect;
@@ -480,5 +532,13 @@
 	errno_t rc;
 
-	rc = ui_wdecor_create(NULL, "Hello", ui_wds_frame | ui_wds_titlebar,
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_wdecor_create(resource, "Hello", ui_wds_frame | ui_wds_titlebar,
 	    &wdecor);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
@@ -513,4 +573,8 @@
 
 	ui_wdecor_destroy(wdecor);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 }
 
@@ -518,4 +582,7 @@
 PCUT_TEST(get_geom_decorated)
 {
+	gfx_context_t *gc = NULL;
+	test_gc_t tgc;
+	ui_resource_t *resource = NULL;
 	ui_wdecor_t *wdecor;
 	gfx_rect_t rect;
@@ -523,5 +590,13 @@
 	errno_t rc;
 
-	rc = ui_wdecor_create(NULL, "Hello", ui_wds_decorated, &wdecor);
+	memset(&tgc, 0, sizeof(tgc));
+	rc = gfx_context_new(&ops, &tgc, &gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_resource_create(gc, false, &resource);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(resource);
+
+	rc = ui_wdecor_create(resource, "Hello", ui_wds_decorated, &wdecor);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -555,4 +630,8 @@
 
 	ui_wdecor_destroy(wdecor);
+	ui_resource_destroy(resource);
+
+	rc = gfx_context_delete(gc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 }
 
@@ -768,4 +847,11 @@
 }
 
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	(void) arg;
+	(void) rect;
+	return EOK;
+}
+
 static errno_t testgc_set_color(void *arg, gfx_color_t *color)
 {
@@ -779,4 +865,10 @@
 	(void) arg;
 	(void) rect;
+	return EOK;
+}
+
+static errno_t testgc_update(void *arg)
+{
+	(void) arg;
 	return EOK;
 }
Index: uspace/lib/ui/test/window.c
===================================================================
--- uspace/lib/ui/test/window.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/ui/test/window.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -66,8 +66,10 @@
 static errno_t test_ctl_paint(void *);
 static ui_evclaim_t test_ctl_pos_event(void *, pos_event_t *);
+static void test_ctl_unfocus(void *);
 
 static ui_control_ops_t test_ctl_ops = {
 	.paint = test_ctl_paint,
-	.pos_event = test_ctl_pos_event
+	.pos_event = test_ctl_pos_event,
+	.unfocus = test_ctl_unfocus
 };
 
@@ -90,4 +92,5 @@
 	bool pos;
 	pos_event_t pos_event;
+	bool unfocus;
 } test_ctl_resp_t;
 
@@ -175,4 +178,50 @@
 }
 
+/** ui_window_get_active */
+PCUT_TEST(get_active)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_wnd_params_t params;
+	ui_window_t *window1 = NULL;
+	ui_window_t *window2 = NULL;
+	ui_window_t *awnd;
+
+	rc = ui_create_cons(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	awnd = ui_window_get_active(ui);
+	PCUT_ASSERT_NULL(awnd);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window1);
+
+	awnd = ui_window_get_active(ui);
+	PCUT_ASSERT_EQUALS(window1, awnd);
+
+	rc = ui_window_create(ui, &params, &window2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window2);
+
+	awnd = ui_window_get_active(ui);
+	PCUT_ASSERT_EQUALS(window2, awnd);
+
+	ui_window_destroy(window2);
+
+	awnd = ui_window_get_active(ui);
+	PCUT_ASSERT_EQUALS(window1, awnd);
+
+	ui_window_destroy(window1);
+
+	awnd = ui_window_get_active(ui);
+	PCUT_ASSERT_NULL(awnd);
+
+	ui_destroy(ui);
+}
+
 /** ui_window_resize */
 PCUT_TEST(resize)
@@ -242,4 +291,28 @@
 }
 
+/** ui_window_set_ctl_cursor() */
+PCUT_TEST(set_ctl_cursor)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_wnd_params_t params;
+	ui_window_t *window = NULL;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_NOT_NULL(window);
+
+	ui_window_set_ctl_cursor(window, ui_curs_ibeam);
+
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
 /** ui_window_get_app_gc() return valid GC */
 PCUT_TEST(get_app_gc)
@@ -258,6 +331,6 @@
 	params.rect.p0.x = 0;
 	params.rect.p0.y = 0;
-	params.rect.p0.x = 10;
-	params.rect.p0.y = 10;
+	params.rect.p0.x = 100;
+	params.rect.p0.y = 100;
 
 	rc = ui_window_create(ui, &params, &window);
@@ -388,4 +461,40 @@
 	PCUT_ASSERT_INT_EQUALS(event.hpos, resp.pos_event.hpos);
 	PCUT_ASSERT_INT_EQUALS(event.vpos, resp.pos_event.vpos);
+
+	/* Need to remove first because we didn't implement the destructor */
+	ui_window_remove(window, control);
+
+	ui_window_destroy(window);
+	ui_destroy(ui);
+}
+
+/** ui_window_def_unfocus() delivers unfocus event to control in window */
+PCUT_TEST(def_unfocus)
+{
+	errno_t rc;
+	ui_t *ui = NULL;
+	ui_wnd_params_t params;
+	ui_window_t *window = NULL;
+	ui_control_t *control = NULL;
+	test_ctl_resp_t resp;
+
+	rc = ui_create_disp(NULL, &ui);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_wnd_params_init(&params);
+	params.caption = "Hello";
+
+	rc = ui_window_create(ui, &params, &window);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ui_control_new(&test_ctl_ops, &resp, &control);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ui_window_add(window, control);
+
+	resp.unfocus = false;
+
+	ui_window_def_unfocus(window);
+	PCUT_ASSERT_TRUE(resp.unfocus);
 
 	/* Need to remove first because we didn't implement the destructor */
@@ -701,3 +810,10 @@
 }
 
+static void test_ctl_unfocus(void *arg)
+{
+	test_ctl_resp_t *resp = (test_ctl_resp_t *) arg;
+
+	resp->unfocus = true;
+}
+
 PCUT_EXPORT(window);
Index: uspace/lib/usb/src/debug.c
===================================================================
--- uspace/lib/usb/src/debug.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/usb/src/debug.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -39,5 +39,5 @@
 
 #define REMAINDER_STR_FMT " (%zu)..."
-/* string + terminator + number width (enough for 4GB)*/
+/* string + terminator + number width (enough for 4GB) */
 #define REMAINDER_STR_LEN (5 + 1 + 10)
 
Index: uspace/lib/usbdev/src/devdrv.c
===================================================================
--- uspace/lib/usbdev/src/devdrv.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/usbdev/src/devdrv.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -482,5 +482,5 @@
 
 	if (endpoints) {
-		/* Create and register other pipes than default control (EP 0)*/
+		/* Create and register other pipes than default control (EP 0) */
 		rc = usb_device_create_pipes(usb_dev, endpoints);
 		if (rc != EOK) {
Index: uspace/lib/usbhid/include/usb/hid/hidpath.h
===================================================================
--- uspace/lib/usbhid/include/usb/hid/hidpath.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/usbhid/include/usb/hid/hidpath.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -83,5 +83,5 @@
 	uint32_t usage;
 
-	/** Attribute of Collection tag in report descriptor*/
+	/** Attribute of Collection tag in report descriptor */
 	uint8_t flags;
 
Index: uspace/lib/usbhid/include/usb/hid/hidtypes.h
===================================================================
--- uspace/lib/usbhid/include/usb/hid/hidtypes.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/usbhid/include/usb/hid/hidtypes.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -211,5 +211,5 @@
 	int usages_count;
 
-	/** Usage page*/
+	/** Usage page */
 	uint32_t usage_page;
 
@@ -226,8 +226,8 @@
 	int32_t logical_maximum;
 
-	/** Length of the items in bits*/
+	/** Length of the items in bits */
 	int32_t size;
 
-	/** COunt of items*/
+	/** Count of items */
 	int32_t count;
 
@@ -252,11 +252,11 @@
 	uint32_t designator_index;
 
-	/** Minimum valid designator value*/
+	/** Minimum valid designator value */
 	uint32_t designator_minimum;
 
-	/** Maximum valid designator value*/
+	/** Maximum valid designator value */
 	uint32_t designator_maximum;
 
-	/** Minimal valid physical value*/
+	/** Minimal valid physical value */
 	int32_t physical_minimum;
 
@@ -264,5 +264,5 @@
 	int32_t physical_maximum;
 
-	/** Items attributes*/
+	/** Items attributes */
 	uint8_t item_flags;
 
@@ -270,8 +270,8 @@
 	usb_hid_report_type_t type;
 
-	/** current collection path*/
+	/** current collection path */
 	usb_hid_report_path_t *usage_path;
 
-	/** Unused*/
+	/** Unused */
 	link_t link;
 
Index: uspace/lib/usbhid/src/hiddescriptor.c
===================================================================
--- uspace/lib/usbhid/src/hiddescriptor.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/usbhid/src/hiddescriptor.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -74,5 +74,5 @@
 
 #define USB_HID_INVALID                 -98
-/** Unknown tag was founded in report descriptor data*/
+/** Unknown tag was founded in report descriptor data */
 #define USB_HID_UNKNOWN_TAG		-99
 
@@ -245,5 +245,5 @@
 		field->item_flags = report_item->item_flags;
 
-		/* find the right report list*/
+		/* find the right report list */
 		usb_hid_report_description_t *report_des;
 		report_des = usb_hid_report_find_description(report,
@@ -352,10 +352,10 @@
 	list_initialize(&stack);
 
-	/* parser structure initialization*/
+	/* parser structure initialization */
 	if (usb_hid_report_init(report) != EOK) {
 		return EINVAL;
 	}
 
-	/*report item initialization*/
+	/* report item initialization */
 	if (!(report_item = malloc(sizeof(usb_hid_report_item_t)))) {
 		return ENOMEM;
Index: uspace/lib/usbhost/src/bandwidth.c
===================================================================
--- uspace/lib/usbhost/src/bandwidth.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/usbhost/src/bandwidth.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -84,5 +84,5 @@
 		 * see USB spec page 45-46.
 		 */
-		/* Speed penalty 8: low speed is 8-times slower*/
+		/* Speed penalty 8: low speed is 8-times slower */
 		return packet_count * (13 + max_packet_size) * 8;
 	case USB_SPEED_FULL:
Index: uspace/lib/usbhost/src/utility.c
===================================================================
--- uspace/lib/usbhost/src/utility.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/lib/usbhost/src/utility.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -259,5 +259,5 @@
 	switch (request->request) {
 		/* Clear Feature ENPOINT_STALL */
-	case USB_DEVREQ_CLEAR_FEATURE: /*resets only cleared ep */
+	case USB_DEVREQ_CLEAR_FEATURE: /* resets only cleared ep */
 		/* 0x2 ( HOST to device | STANDART | TO ENPOINT) */
 		if ((request->request_type == 0x2) &&
Index: uspace/srv/audio/hound/hound_ctx.c
===================================================================
--- uspace/srv/audio/hound/hound_ctx.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/audio/hound/hound_ctx.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -403,5 +403,5 @@
 		return ENOMEM;
 	}
-	log_verbose("CTX: %p: Mixing %lu streams", ctx,
+	log_verbose("CTX: %p: Mixing %zu streams", ctx,
 	    list_count(&ctx->streams));
 	pcm_format_silence(buffer, size, &source->format);
@@ -413,5 +413,5 @@
 			log_warning("Not enough data in stream buffer");
 	}
-	log_verbose("CTX: %p. Pushing audio to %lu connections", ctx,
+	log_verbose("CTX: %p. Pushing audio to %zu connections", ctx,
 	    list_count(&source->connections));
 	list_foreach(source->connections, source_link, connection_t, conn) {
Index: uspace/srv/bd/file_bd/meson.build
===================================================================
--- uspace/srv/bd/file_bd/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/bd/file_bd/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -28,3 +28,4 @@
 #
 
+deps = [ 'device' ]
 src = files('file_bd.c')
Index: uspace/srv/bd/rd/meson.build
===================================================================
--- uspace/srv/bd/rd/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/bd/rd/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -28,3 +28,4 @@
 #
 
+deps = [ 'device' ]
 src = files('rd.c')
Index: uspace/srv/devman/meson.build
===================================================================
--- uspace/srv/devman/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/devman/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -28,4 +28,5 @@
 #
 
+deps = [ 'device' ]
 src = files(
 	'client_conn.c',
Index: uspace/srv/fs/udf/udf_file.h
===================================================================
--- uspace/srv/fs/udf/udf_file.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/fs/udf/udf_file.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -178,5 +178,5 @@
 } __attribute__((packed)) udf_terminal_entry_descriptor;
 
-/* Unallocated Space Entry format (ECMA 167 4/14.11)*/
+/* Unallocated Space Entry format (ECMA 167 4/14.11) */
 typedef struct udf_unallocated_space_entry_descriptor {
 	udf_descriptor_tag_t tag;
Index: uspace/srv/hid/console/console.c
===================================================================
--- uspace/srv/hid/console/console.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/console/console.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2011 Martin Decky
  * All rights reserved.
@@ -62,5 +63,5 @@
 typedef struct {
 	atomic_flag refcnt;      /**< Connection reference count */
-	prodcons_t input_pc;  /**< Incoming keyboard events */
+	prodcons_t input_pc;  /**< Incoming console events */
 
 	/**
@@ -79,4 +80,8 @@
 	console_caps_t ccaps;  /**< Console capabilities */
 
+	sysarg_t ucols;		/**< Number of columns in user buffer */
+	sysarg_t urows;		/**< Number of rows in user buffer */
+	charfield_t *ubuf;	/**< User buffer */
+
 	chargrid_t *frontbuf;    /**< Front buffer */
 	frontbuf_handle_t fbid;  /**< Front buffer handle */
@@ -94,4 +99,14 @@
 static sysarg_t cols;
 static sysarg_t rows;
+
+/** Mouse pointer X coordinate */
+static int pointer_x;
+/** Mouse pointer Y coordinate */
+static int pointer_y;
+/** Character under mouse cursor */
+static charfield_t pointer_bg;
+
+static int mouse_scale_x = 4;
+static int mouse_scale_y = 8;
 
 /** Array of data for virtual consoles */
@@ -135,4 +150,8 @@
 static void cons_set_cursor_visibility(con_srv_t *, bool);
 static errno_t cons_get_event(con_srv_t *, cons_event_t *);
+static errno_t cons_map(con_srv_t *, sysarg_t, sysarg_t, charfield_t **);
+static void cons_unmap(con_srv_t *);
+static void cons_buf_update(con_srv_t *, sysarg_t, sysarg_t, sysarg_t,
+    sysarg_t);
 
 static con_ops_t con_ops = {
@@ -151,6 +170,12 @@
 	.set_rgb_color = cons_set_rgb_color,
 	.set_cursor_visibility = cons_set_cursor_visibility,
-	.get_event = cons_get_event
+	.get_event = cons_get_event,
+	.map = cons_map,
+	.unmap = cons_unmap,
+	.update = cons_buf_update
 };
+
+static void pointer_draw(void);
+static void pointer_undraw(void);
 
 static console_t *srv_to_console(con_srv_t *srv)
@@ -219,4 +244,5 @@
 
 	fibril_mutex_lock(&switch_mtx);
+	pointer_undraw();
 
 	if (cons == active_console) {
@@ -227,7 +253,85 @@
 	active_console = cons;
 
+	pointer_draw();
 	fibril_mutex_unlock(&switch_mtx);
 
 	cons_damage(cons);
+}
+
+/** Draw mouse pointer. */
+static void pointer_draw(void)
+{
+	charfield_t *ch;
+	int col, row;
+
+	/* Downscale coordinates to text resolution */
+	col = pointer_x / mouse_scale_x;
+	row = pointer_y / mouse_scale_y;
+
+	/* Make sure they are in range */
+	if (col < 0 || row < 0 || col >= (int)cols || row >= (int)rows)
+		return;
+
+	ch = chargrid_charfield_at(active_console->frontbuf, col, row);
+
+	/*
+	 * Store background attributes for undrawing the pointer.
+	 * This is necessary as styles cannot be inverted with
+	 * round trip (unlike RGB or INDEX)
+	 */
+	pointer_bg = *ch;
+
+	/* In general the color should be a one's complement of the background */
+	if (ch->attrs.type == CHAR_ATTR_INDEX) {
+		ch->attrs.val.index.bgcolor ^= 0xf;
+		ch->attrs.val.index.fgcolor ^= 0xf;
+	} else if (ch->attrs.type == CHAR_ATTR_RGB) {
+		ch->attrs.val.rgb.fgcolor ^= 0xffffff;
+		ch->attrs.val.rgb.bgcolor ^= 0xffffff;
+	} else if (ch->attrs.type == CHAR_ATTR_STYLE) {
+		/* Don't have a proper inverse for each style */
+		if (ch->attrs.val.style == STYLE_INVERTED)
+			ch->attrs.val.style = STYLE_NORMAL;
+		else
+			ch->attrs.val.style = STYLE_INVERTED;
+	}
+
+	/* Make sure the cell gets updated */
+	ch->flags |= CHAR_FLAG_DIRTY;
+}
+
+/** Undraw mouse pointer. */
+static void pointer_undraw(void)
+{
+	charfield_t *ch;
+	int col, row;
+
+	col = pointer_x / mouse_scale_x;
+	row = pointer_y / mouse_scale_y;
+	if (col < 0 || row < 0 || col >= (int)cols || row >= (int)rows)
+		return;
+
+	ch = chargrid_charfield_at(active_console->frontbuf, col, row);
+	*ch = pointer_bg;
+	ch->flags |= CHAR_FLAG_DIRTY;
+}
+
+/** Queue console event.
+ *
+ * @param cons Console
+ * @param ev Console event
+ */
+static void console_queue_cons_event(console_t *cons, cons_event_t *ev)
+{
+	/* Got key press/release event */
+	cons_event_t *event =
+	    (cons_event_t *) malloc(sizeof(cons_event_t));
+	if (event == NULL)
+		return;
+
+	*event = *ev;
+	link_initialize(&event->link);
+
+	prodcons_produce(&cons->input_pc, &event->link);
 }
 
@@ -252,4 +356,6 @@
     keymod_t mods, char32_t c)
 {
+	cons_event_t event;
+
 	if ((key >= KC_F1) && (key <= KC_F1 + CONSOLE_COUNT) &&
 	    ((mods & KM_CTRL) == 0)) {
@@ -257,25 +363,57 @@
 	} else {
 		/* Got key press/release event */
-		kbd_event_t *event =
-		    (kbd_event_t *) malloc(sizeof(kbd_event_t));
-		if (event == NULL) {
-			return ENOMEM;
-		}
-
-		link_initialize(&event->link);
-		event->type = type;
-		event->key = key;
-		event->mods = mods;
-		event->c = c;
-
-		prodcons_produce(&active_console->input_pc,
-		    &event->link);
-	}
-
-	return EOK;
+		event.type = CEV_KEY;
+
+		event.ev.key.type = type;
+		event.ev.key.key = key;
+		event.ev.key.mods = mods;
+		event.ev.key.c = c;
+
+		console_queue_cons_event(active_console, &event);
+	}
+
+	return EOK;
+}
+
+/** Update pointer position.
+ *
+ * @param new_x New X coordinate (in pixels)
+ * @param new_y New Y coordinate (in pixels)
+ */
+static void pointer_update(int new_x, int new_y)
+{
+	bool upd_pointer;
+
+	/* Make sure coordinates are in range */
+
+	if (new_x < 0)
+		new_x = 0;
+	if (new_x >= (int)cols * mouse_scale_x)
+		new_x = cols * mouse_scale_x - 1;
+	if (new_y < 0)
+		new_y = 0;
+	if (new_y >= (int)rows * mouse_scale_y)
+		new_y = rows * mouse_scale_y - 1;
+
+	/* Determine if pointer moved to a different character cell */
+	upd_pointer = (new_x / mouse_scale_x != pointer_x / mouse_scale_x) ||
+	    (new_y / mouse_scale_y != pointer_y / mouse_scale_y);
+
+	if (upd_pointer)
+		pointer_undraw();
+
+	/* Store new pointer position */
+	pointer_x = new_x;
+	pointer_y = new_y;
+
+	if (upd_pointer) {
+		pointer_draw();
+		cons_update(active_console);
+	}
 }
 
 static errno_t input_ev_move(input_t *input, int dx, int dy)
 {
+	pointer_update(pointer_x + dx, pointer_y + dy);
 	return EOK;
 }
@@ -284,4 +422,5 @@
     unsigned max_x, unsigned max_y)
 {
+	pointer_update(mouse_scale_x * cols * x / max_x, mouse_scale_y * rows * y / max_y);
 	return EOK;
 }
@@ -289,4 +428,13 @@
 static errno_t input_ev_button(input_t *input, int bnum, int bpress)
 {
+	cons_event_t event;
+
+	event.type = CEV_POS;
+	event.ev.pos.type = bpress ? POS_PRESS : POS_RELEASE;
+	event.ev.pos.btn_num = bnum;
+	event.ev.pos.hpos = pointer_x / mouse_scale_x;
+	event.ev.pos.vpos = pointer_y / mouse_scale_y;
+
+	console_queue_cons_event(active_console, &event);
 	return EOK;
 }
@@ -298,4 +446,5 @@
 
 	fibril_mutex_lock(&cons->mtx);
+	pointer_undraw();
 
 	switch (ch) {
@@ -315,4 +464,5 @@
 	}
 
+	pointer_draw();
 	fibril_mutex_unlock(&cons->mtx);
 
@@ -324,5 +474,7 @@
 {
 	fibril_mutex_lock(&cons->mtx);
+	pointer_undraw();
 	chargrid_set_cursor_visibility(cons->frontbuf, visible);
+	pointer_draw();
 	fibril_mutex_unlock(&cons->mtx);
 
@@ -367,9 +519,11 @@
 		if (pos < size) {
 			link_t *link = prodcons_consume(&cons->input_pc);
-			kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
+			cons_event_t *event = list_get_instance(link,
+			    cons_event_t, link);
 
 			/* Accept key presses of printable chars only. */
-			if ((event->type == KEY_PRESS) && (event->c != 0)) {
-				char32_t tmp[2] = { event->c, 0 };
+			if (event->type == CEV_KEY && event->ev.key.type == KEY_PRESS &&
+			    (event->ev.key.c != 0)) {
+				char32_t tmp[2] = { event->ev.key.c, 0 };
 				wstr_to_str(cons->char_remains, UTF8_CHAR_BUFFER_SIZE, tmp);
 				cons->char_remains_len = str_size(cons->char_remains);
@@ -408,5 +562,7 @@
 
 	fibril_mutex_lock(&cons->mtx);
+	pointer_undraw();
 	chargrid_clear(cons->frontbuf);
+	pointer_draw();
 	fibril_mutex_unlock(&cons->mtx);
 
@@ -419,5 +575,7 @@
 
 	fibril_mutex_lock(&cons->mtx);
+	pointer_undraw();
 	chargrid_set_cursor(cons->frontbuf, col, row);
+	pointer_draw();
 	fibril_mutex_unlock(&cons->mtx);
 
@@ -499,11 +657,126 @@
 	console_t *cons = srv_to_console(srv);
 	link_t *link = prodcons_consume(&cons->input_pc);
-	kbd_event_t *kevent = list_get_instance(link, kbd_event_t, link);
-
-	event->type = CEV_KEY;
-	event->ev.key = *kevent;
-
-	free(kevent);
-	return EOK;
+	cons_event_t *cevent = list_get_instance(link, cons_event_t, link);
+
+	*event = *cevent;
+	free(cevent);
+	return EOK;
+}
+
+/** Create shared buffer for efficient rendering.
+ *
+ * @param srv Console server
+ * @param cols Number of columns in buffer
+ * @param rows Number of rows in buffer
+ * @param rbuf Place to store pointer to new sharable buffer
+ *
+ * @return EOK on sucess or an error code
+ */
+static errno_t cons_map(con_srv_t *srv, sysarg_t cols, sysarg_t rows,
+    charfield_t **rbuf)
+{
+	console_t *cons = srv_to_console(srv);
+	void *buf;
+
+	fibril_mutex_lock(&cons->mtx);
+
+	if (cons->ubuf != NULL) {
+		fibril_mutex_unlock(&cons->mtx);
+		return EBUSY;
+	}
+
+	buf = as_area_create(AS_AREA_ANY, cols * rows * sizeof(charfield_t),
+	    AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
+	if (buf == AS_MAP_FAILED) {
+		fibril_mutex_unlock(&cons->mtx);
+		return ENOMEM;
+	}
+
+	cons->ucols = cols;
+	cons->urows = rows;
+	cons->ubuf = buf;
+	fibril_mutex_unlock(&cons->mtx);
+
+	*rbuf = buf;
+	return EOK;
+}
+
+/** Delete shared buffer.
+ *
+ * @param srv Console server
+ */
+static void cons_unmap(con_srv_t *srv)
+{
+	console_t *cons = srv_to_console(srv);
+	void *buf;
+
+	fibril_mutex_lock(&cons->mtx);
+
+	buf = cons->ubuf;
+	cons->ubuf = NULL;
+
+	if (buf != NULL)
+		as_area_destroy(buf);
+
+	fibril_mutex_unlock(&cons->mtx);
+}
+
+/** Update area of console from shared buffer.
+ *
+ * @param srv Console server
+ * @param c0 Column coordinate of top-left corner (inclusive)
+ * @param r0 Row coordinate of top-left corner (inclusive)
+ * @param c1 Column coordinate of bottom-right corner (exclusive)
+ * @param r1 Row coordinate of bottom-right corner (exclusive)
+ */
+static void cons_buf_update(con_srv_t *srv, sysarg_t c0, sysarg_t r0,
+    sysarg_t c1, sysarg_t r1)
+{
+	console_t *cons = srv_to_console(srv);
+	charfield_t *ch;
+	sysarg_t col, row;
+
+	fibril_mutex_lock(&cons->mtx);
+
+	if (cons->ubuf == NULL) {
+		fibril_mutex_unlock(&cons->mtx);
+		return;
+	}
+
+	/* Make sure we have meaningful coordinates, within bounds */
+
+	if (c1 > cons->ucols)
+		c1 = cons->ucols;
+	if (c1 > cons->cols)
+		c1 = cons->cols;
+	if (c0 >= c1) {
+		fibril_mutex_unlock(&cons->mtx);
+		return;
+	}
+	if (r1 > cons->urows)
+		r1 = cons->urows;
+	if (r1 > cons->rows)
+		r1 = cons->rows;
+	if (r0 >= r1) {
+		fibril_mutex_unlock(&cons->mtx);
+		return;
+	}
+
+	/* Update front buffer from user buffer */
+
+	pointer_undraw();
+
+	for (row = r0; row < r1; row++) {
+		for (col = c0; col < c1; col++) {
+			ch = chargrid_charfield_at(cons->frontbuf, col, row);
+			*ch = cons->ubuf[row * cons->ucols + col];
+		}
+	}
+
+	pointer_draw();
+	fibril_mutex_unlock(&cons->mtx);
+
+	/* Update console */
+	cons_update(cons);
 }
 
@@ -649,4 +922,6 @@
 
 		input_activate(input);
+		active = true;
+		cons_damage(active_console);
 	}
 
Index: uspace/srv/hid/display/client.c
===================================================================
--- uspace/srv/hid/display/client.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/client.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -115,7 +115,10 @@
 	seat = ds_display_first_seat(wnd->display);
 	while (seat != NULL) {
-		ds_seat_evac_focus(seat, wnd);
+		ds_seat_evac_wnd_refs(seat, wnd);
 		seat = ds_display_next_seat(seat);
 	}
+
+	/* Make sure no event in the queue is referencing the window */
+	ds_client_purge_window_events(wnd->client, wnd);
 
 	list_remove(&wnd->lcwindows);
@@ -199,4 +202,28 @@
 }
 
+/** Purge events from client event queue referring to a window.
+ *
+ * @param client Client
+ * @param window Window
+ */
+void ds_client_purge_window_events(ds_client_t *client,
+    ds_window_t *window)
+{
+	link_t *cur;
+	link_t *next;
+	ds_window_ev_t *wevent;
+
+	cur = list_first(&client->events);
+	while (cur != NULL) {
+		next = list_next(cur, &client->events);
+		wevent = list_get_instance(cur, ds_window_ev_t, levents);
+
+		if (wevent->window == window)
+			list_remove(cur);
+
+		cur = next;
+	}
+}
+
 /** Post close event to the client's message queue.
  *
Index: uspace/srv/hid/display/client.h
===================================================================
--- uspace/srv/hid/display/client.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/client.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -53,4 +53,5 @@
 extern errno_t ds_client_get_event(ds_client_t *, ds_window_t **,
     display_wnd_ev_t *);
+extern void ds_client_purge_window_events(ds_client_t *, ds_window_t *);
 extern errno_t ds_client_post_close_event(ds_client_t *, ds_window_t *);
 extern errno_t ds_client_post_focus_event(ds_client_t *, ds_window_t *);
Index: uspace/srv/hid/display/clonegc.c
===================================================================
--- uspace/srv/hid/display/clonegc.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/clonegc.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -44,4 +44,5 @@
 #include "clonegc.h"
 
+static errno_t ds_clonegc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t ds_clonegc_set_color(void *, gfx_color_t *);
 static errno_t ds_clonegc_fill_rect(void *, gfx_rect_t *);
@@ -65,4 +66,5 @@
 
 gfx_context_ops_t ds_clonegc_ops = {
+	.set_clip_rect = ds_clonegc_set_clip_rect,
 	.set_color = ds_clonegc_set_color,
 	.fill_rect = ds_clonegc_fill_rect,
@@ -73,4 +75,29 @@
 };
 
+/** Set clipping rectangle on clone GC.
+ *
+ * @param arg Clone GC
+ * @param rect Rectangle
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t ds_clonegc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	ds_clonegc_t *cgc = (ds_clonegc_t *)arg;
+	ds_clonegc_output_t *output;
+	errno_t rc;
+
+	output = ds_clonegc_first_output(cgc);
+	while (output != NULL) {
+		rc = gfx_set_clip_rect(output->gc, rect);
+		if (rc != EOK)
+			return rc;
+
+		output = ds_clonegc_next_output(output);
+	}
+
+	return EOK;
+}
+
 /** Set color on clone GC.
  *
Index: uspace/srv/hid/display/cursimg.c
===================================================================
--- uspace/srv/hid/display/cursimg.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/cursimg.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -137,4 +137,26 @@
 };
 
+static uint8_t ibeam_img[] = {
+	0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0,
+	1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1,
+	0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0,
+	0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0,
+	1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1,
+	0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0
+};
+
 ds_cursimg_t ds_cursimg[dcurs_limit] = {
 	{
@@ -157,4 +179,8 @@
 		.rect = { -7, -7, 8, 8 },
 		.image = size_urdl_img
+	},
+	{
+		.rect = { -5, -9, 6, 10 },
+		.image = ibeam_img
 	}
 };
Index: uspace/srv/hid/display/display.c
===================================================================
--- uspace/srv/hid/display/display.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/display.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -50,5 +50,11 @@
 
 static gfx_context_t *ds_display_get_unbuf_gc(ds_display_t *);
-static void ds_display_update_cb(void *, gfx_rect_t *);
+static void ds_display_invalidate_cb(void *, gfx_rect_t *);
+static void ds_display_update_cb(void *);
+
+static mem_gc_cb_t ds_display_mem_gc_cb = {
+	.invalidate = ds_display_invalidate_cb,
+	.update = ds_display_update_cb
+};
 
 /** Create display.
@@ -458,6 +464,6 @@
 		goto error;
 
-	rc = mem_gc_create(&disp->rect, &alloc,
-	    ds_display_update_cb, (void *) disp, &disp->bbgc);
+	rc = mem_gc_create(&disp->rect, &alloc, &ds_display_mem_gc_cb,
+	    (void *) disp, &disp->bbgc);
 	if (rc != EOK)
 		goto error;
@@ -732,5 +738,5 @@
 }
 
-/** Display update callback.
+/** Display invalidate callback.
  *
  * Called by backbuffer memory GC when something is rendered into it.
@@ -740,5 +746,5 @@
  * @param rect Rectangle to update
  */
-static void ds_display_update_cb(void *arg, gfx_rect_t *rect)
+static void ds_display_invalidate_cb(void *arg, gfx_rect_t *rect)
 {
 	ds_display_t *disp = (ds_display_t *) arg;
@@ -749,4 +755,15 @@
 }
 
+/** Display update callback.
+ *
+ * @param arg Argument (display cast as void *)
+ */
+static void ds_display_update_cb(void *arg)
+{
+	ds_display_t *disp = (ds_display_t *) arg;
+
+	(void) disp;
+}
+
 /** @}
  */
Index: uspace/srv/hid/display/dsops.c
===================================================================
--- uspace/srv/hid/display/dsops.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/dsops.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -48,4 +48,5 @@
 static errno_t disp_window_move_req(void *, sysarg_t, gfx_coord2_t *);
 static errno_t disp_window_move(void *, sysarg_t, gfx_coord2_t *);
+static errno_t disp_window_get_pos(void *, sysarg_t, gfx_coord2_t *);
 static errno_t disp_window_resize_req(void *, sysarg_t,
     display_wnd_rsztype_t, gfx_coord2_t *);
@@ -61,4 +62,5 @@
 	.window_move_req = disp_window_move_req,
 	.window_move = disp_window_move,
+	.window_get_pos = disp_window_get_pos,
 	.window_resize_req = disp_window_resize_req,
 	.window_resize = disp_window_resize,
@@ -73,5 +75,4 @@
 	errno_t rc;
 	ds_client_t *client = (ds_client_t *) arg;
-	ds_seat_t *seat;
 	ds_window_t *wnd;
 
@@ -90,12 +91,4 @@
 	    wnd->id);
 
-	/* XXX All the below should probably be part of ds_window_create() */
-	wnd->dpos.x = ((wnd->id - 1) & 1) * 400;
-	wnd->dpos.y = ((wnd->id - 1) & 2) / 2 * 300;
-
-	seat = ds_display_first_seat(client->display);
-	ds_seat_set_focus(seat, wnd);
-	(void) ds_display_paint(wnd->display, NULL);
-
 	ds_display_unlock(client->display);
 
@@ -118,5 +111,4 @@
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "disp_window_destroy()");
-	ds_client_remove_window(wnd);
 	ds_window_destroy(wnd);
 	ds_display_unlock(client->display);
@@ -163,4 +155,24 @@
 }
 
+static errno_t disp_window_get_pos(void *arg, sysarg_t wnd_id,
+    gfx_coord2_t *pos)
+{
+	ds_client_t *client = (ds_client_t *) arg;
+	ds_window_t *wnd;
+
+	ds_display_lock(client->display);
+
+	wnd = ds_client_find_window(client, wnd_id);
+	if (wnd == NULL) {
+		ds_display_unlock(client->display);
+		return ENOENT;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "disp_window_get_pos()");
+	ds_window_get_pos(wnd, pos);
+	ds_display_unlock(client->display);
+	return EOK;
+}
+
 static errno_t disp_window_resize_req(void *arg, sysarg_t wnd_id,
     display_wnd_rsztype_t rsztype, gfx_coord2_t *pos)
Index: uspace/srv/hid/display/main.c
===================================================================
--- uspace/srv/hid/display/main.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/main.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -188,5 +188,7 @@
 		display_conn(icall, &srv);
 
+		ds_display_lock(disp);
 		ds_client_destroy(client);
+		ds_display_unlock(disp);
 	} else {
 		/* Window GC connection */
Index: uspace/srv/hid/display/seat.c
===================================================================
--- uspace/srv/hid/display/seat.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/seat.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -104,21 +104,44 @@
 		ds_window_bring_to_top(wnd);
 	}
-}
-
-/** Evacuate focus from window.
+
+	/* When focus changes, popup window should be closed */
+	ds_seat_set_popup(seat, NULL);
+}
+
+/** Set seat popup window.
+ *
+ * @param seat Seat
+ * @param wnd Popup window
+ */
+void ds_seat_set_popup(ds_seat_t *seat, ds_window_t *wnd)
+{
+	if (wnd == seat->popup)
+		return;
+
+	if (seat->popup != NULL) {
+		/* Window is no longer the popup window, send close request */
+		ds_client_post_close_event(seat->popup->client,
+		    seat->popup);
+	}
+
+	seat->popup = wnd;
+}
+
+/** Evacuate seat references to window.
  *
  * If seat's focus is @a wnd, it will be set to a different window.
+ * If seat's popup window is @a wnd, it will be set to NULL.
  *
  * @param seat Seat
  * @param wnd Window to evacuate focus from
  */
-void ds_seat_evac_focus(ds_seat_t *seat, ds_window_t *wnd)
+void ds_seat_evac_wnd_refs(ds_seat_t *seat, ds_window_t *wnd)
 {
 	ds_window_t *nwnd;
 
 	if (seat->focus == wnd) {
-		nwnd = ds_display_next_window(wnd);
+		nwnd = ds_display_prev_window(wnd);
 		if (nwnd == NULL)
-			nwnd = ds_display_first_window(wnd->display);
+			nwnd = ds_display_last_window(wnd->display);
 		if (nwnd == wnd)
 			nwnd = NULL;
@@ -126,4 +149,28 @@
 		ds_seat_set_focus(seat, nwnd);
 	}
+
+	if (seat->popup == wnd)
+		ds_seat_set_popup(seat, NULL);
+}
+
+/** Switch focus to another window.
+ *
+ * @param seat Seat
+ * @param wnd Window to evacuate focus from
+ */
+void ds_seat_switch_focus(ds_seat_t *seat)
+{
+	ds_window_t *nwnd;
+
+	if (seat->focus != NULL)
+		nwnd = ds_display_prev_window(seat->focus);
+	else
+		nwnd = NULL;
+
+	if (nwnd == NULL)
+		nwnd = ds_display_last_window(seat->display);
+
+	if (nwnd != NULL)
+		ds_seat_set_focus(seat, nwnd);
 }
 
@@ -143,9 +190,12 @@
 	if (event->type == KEY_PRESS && alt_or_shift && event->key == KC_TAB) {
 		/* On Alt-Tab or Shift-Tab, switch focus to next window */
-		ds_seat_evac_focus(seat, seat->focus);
+		ds_seat_switch_focus(seat);
 		return EOK;
 	}
 
-	dwindow = seat->focus;
+	dwindow = seat->popup;
+	if (dwindow == NULL)
+		dwindow = seat->focus;
+
 	if (dwindow == NULL)
 		return EOK;
@@ -307,5 +357,5 @@
 	/* Focus window on button press */
 	if (event->type == PTD_PRESS && event->btn_num == 1) {
-		if (wnd != NULL) {
+		if (wnd != NULL && (wnd->flags & wndf_popup) == 0) {
 			ds_seat_set_focus(seat, wnd);
 		}
@@ -390,5 +440,18 @@
 	wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
 
-	if (seat->focus != wnd) {
+	/* Click outside popup window */
+	if (event->type == POS_PRESS && wnd != seat->popup) {
+		/* Close popup window */
+		ds_seat_set_popup(seat, NULL);
+	}
+
+	/* Deliver event to popup window. */
+	if (seat->popup != NULL) {
+		rc = ds_window_post_pos_event(seat->popup, event);
+		if (rc != EOK)
+			return rc;
+	}
+
+	if (seat->focus != wnd && seat->focus != NULL) {
 		rc = ds_window_post_pos_event(seat->focus, event);
 		if (rc != EOK)
@@ -404,7 +467,13 @@
 		ds_seat_set_client_cursor(seat, wnd->cursor);
 
-		rc = ds_window_post_pos_event(wnd, event);
-		if (rc != EOK)
-			return rc;
+		/*
+		 * Only deliver event if we didn't already deliver it
+		 * to the same window above.
+		 */
+		if (wnd != seat->popup) {
+			rc = ds_window_post_pos_event(wnd, event);
+			if (rc != EOK)
+				return rc;
+		}
 	} else {
 		/* Not over a window */
Index: uspace/srv/hid/display/seat.h
===================================================================
--- uspace/srv/hid/display/seat.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/seat.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -49,5 +49,7 @@
 extern void ds_seat_destroy(ds_seat_t *);
 extern void ds_seat_set_focus(ds_seat_t *, ds_window_t *);
-extern void ds_seat_evac_focus(ds_seat_t *, ds_window_t *);
+extern void ds_seat_set_popup(ds_seat_t *, ds_window_t *);
+extern void ds_seat_evac_wnd_refs(ds_seat_t *, ds_window_t *);
+extern void ds_seat_switch_focus(ds_seat_t *);
 extern errno_t ds_seat_post_kbd_event(ds_seat_t *, kbd_event_t *);
 extern errno_t ds_seat_post_ptd_event(ds_seat_t *, ptd_event_t *);
Index: uspace/srv/hid/display/test/client.c
===================================================================
--- uspace/srv/hid/display/test/client.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/test/client.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -34,4 +34,5 @@
 #include "../client.h"
 #include "../display.h"
+#include "../seat.h"
 #include "../window.h"
 
@@ -78,14 +79,19 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *w0;
 	ds_window_t *w1;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
-	errno_t rc;
-
-	rc = ds_display_create(NULL, df_none, &disp);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ds_client_create(disp, &test_ds_client_cb, NULL, &client);
+	bool called_cb = NULL;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, df_none, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -114,4 +120,5 @@
 	ds_window_destroy(w0);
 	ds_window_destroy(w1);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -123,14 +130,19 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *w0;
 	ds_window_t *w1;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
-	errno_t rc;
-
-	rc = ds_display_create(NULL, df_none, &disp);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ds_client_create(disp, &test_ds_client_cb, NULL, &client);
+	bool called_cb = NULL;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, df_none, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -156,4 +168,5 @@
 	ds_window_destroy(w0);
 	ds_window_destroy(w1);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -165,4 +178,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
@@ -178,12 +192,21 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	display_wnd_params_init(&params);
-	params.rect.p0.x = params.rect.p0.y = 0;
-	params.rect.p1.x = params.rect.p1.y = 1;
-
-	rc = ds_window_create(client, &params, &wnd);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	PCUT_ASSERT_FALSE(called_cb);
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* New window gets focused event */
+	PCUT_ASSERT_TRUE(called_cb);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	called_cb = false;
 
 	rc = ds_client_get_event(client, &rwindow, &revent);
@@ -203,4 +226,5 @@
 
 	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -212,4 +236,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
@@ -225,12 +250,21 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	display_wnd_params_init(&params);
-	params.rect.p0.x = params.rect.p0.y = 0;
-	params.rect.p1.x = params.rect.p1.y = 1;
-
-	rc = ds_window_create(client, &params, &wnd);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	PCUT_ASSERT_FALSE(called_cb);
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* New window gets focused event */
+	PCUT_ASSERT_TRUE(called_cb);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	called_cb = false;
 
 	rc = ds_client_get_event(client, &rwindow, &revent);
@@ -250,4 +284,5 @@
 
 	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -259,4 +294,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
@@ -273,10 +309,24 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	display_wnd_params_init(&params);
-	params.rect.p0.x = params.rect.p0.y = 0;
-	params.rect.p1.x = params.rect.p1.y = 1;
-
-	rc = ds_window_create(client, &params, &wnd);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* New window gets focused event */
+	PCUT_ASSERT_TRUE(called_cb);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	called_cb = false;
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
 
 	event.type = KEY_PRESS;
@@ -284,9 +334,4 @@
 	event.mods = 0;
 	event.c = L'\0';
-
-	PCUT_ASSERT_FALSE(called_cb);
-
-	rc = ds_client_get_event(client, &rwindow, &revent);
-	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
 
 	rc = ds_client_post_kbd_event(client, wnd, &event);
@@ -307,4 +352,5 @@
 
 	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -316,4 +362,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
@@ -330,19 +377,30 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	display_wnd_params_init(&params);
-	params.rect.p0.x = params.rect.p0.y = 0;
-	params.rect.p1.x = params.rect.p1.y = 1;
-
-	rc = ds_window_create(client, &params, &wnd);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* New window gets focused event */
+	PCUT_ASSERT_TRUE(called_cb);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	called_cb = false;
+
+	PCUT_ASSERT_FALSE(called_cb);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
 
 	event.type = POS_PRESS;
 	event.hpos = 1;
 	event.vpos = 2;
-
-	PCUT_ASSERT_FALSE(called_cb);
-
-	rc = ds_client_get_event(client, &rwindow, &revent);
-	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
 
 	rc = ds_client_post_pos_event(client, wnd, &event);
@@ -362,4 +420,5 @@
 
 	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -371,4 +430,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
@@ -385,10 +445,26 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	display_wnd_params_init(&params);
-	params.rect.p0.x = params.rect.p0.y = 0;
-	params.rect.p1.x = params.rect.p1.y = 1;
-
-	rc = ds_window_create(client, &params, &wnd);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* New window gets focused event */
+	PCUT_ASSERT_TRUE(called_cb);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	called_cb = false;
+
+	PCUT_ASSERT_FALSE(called_cb);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
 
 	rect.p0.x = 1;
@@ -396,9 +472,4 @@
 	rect.p1.x = 3;
 	rect.p1.y = 4;
-
-	PCUT_ASSERT_FALSE(called_cb);
-
-	rc = ds_client_get_event(client, &rwindow, &revent);
-	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
 
 	rc = ds_client_post_resize_event(client, wnd, &rect);
@@ -419,4 +490,5 @@
 
 	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -428,4 +500,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
@@ -441,12 +514,21 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	display_wnd_params_init(&params);
-	params.rect.p0.x = params.rect.p0.y = 0;
-	params.rect.p1.x = params.rect.p1.y = 1;
-
-	rc = ds_window_create(client, &params, &wnd);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	PCUT_ASSERT_FALSE(called_cb);
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* New window gets focused event */
+	PCUT_ASSERT_TRUE(called_cb);
+
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	called_cb = false;
 
 	rc = ds_client_get_event(client, &rwindow, &revent);
@@ -466,4 +548,50 @@
 
 	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Test ds_client_purge_window_events() */
+PCUT_TEST(client_purge_window_events)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_seat_t *seat;
+	ds_window_t *wnd;
+	display_wnd_params_t params;
+	ds_window_t *rwindow;
+	display_wnd_ev_t revent;
+	bool called_cb = NULL;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, df_none, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* New window gets focused event */
+	PCUT_ASSERT_TRUE(called_cb);
+
+	/* Purge it */
+	ds_client_purge_window_events(client, wnd);
+
+	/* The queue should be empty now */
+	rc = ds_client_get_event(client, &rwindow, &revent);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
+
+	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -479,4 +607,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
@@ -489,11 +618,15 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	display_wnd_params_init(&params);
-	params.rect.p0.x = params.rect.p0.y = 0;
-	params.rect.p1.x = params.rect.p1.y = 1;
-
-	rc = ds_window_create(client, &params, &wnd);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
Index: uspace/srv/hid/display/test/clonegc.c
===================================================================
--- uspace/srv/hid/display/test/clonegc.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/test/clonegc.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,4 +39,5 @@
 PCUT_TEST_SUITE(clonegc);
 
+static errno_t testgc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t testgc_set_color(void *, gfx_color_t *);
 static errno_t testgc_fill_rect(void *, gfx_rect_t *);
@@ -48,4 +49,5 @@
 
 static gfx_context_ops_t ops = {
+	.set_clip_rect = testgc_set_clip_rect,
 	.set_color = testgc_set_color,
 	.fill_rect = testgc_fill_rect,
@@ -59,4 +61,7 @@
 	/** Error code to return */
 	errno_t rc;
+
+	bool set_clip_rect_called;
+	gfx_rect_t *set_clip_rect_rect;
 
 	bool set_color_called;
@@ -95,4 +100,74 @@
 	rc = ds_clonegc_create(NULL, &cgc);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_clonegc_delete(cgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+/** Set clipping rectangle with two output GCs */
+PCUT_TEST(set_clip_rect)
+{
+	ds_clonegc_t *cgc;
+	gfx_context_t *gc;
+	test_gc_t tgc1;
+	gfx_context_t *gc1;
+	test_gc_t tgc2;
+	gfx_context_t *gc2;
+	gfx_rect_t rect;
+	errno_t rc;
+
+	/* Create clone GC */
+	rc = ds_clonegc_create(NULL, &cgc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	gc = ds_clonegc_get_ctx(cgc);
+	PCUT_ASSERT_NOT_NULL(gc);
+
+	/* Add two output GCs */
+
+	rc = gfx_context_new(&ops, &tgc1, &gc1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_clonegc_add_output(cgc, gc1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = gfx_context_new(&ops, &tgc2, &gc2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_clonegc_add_output(cgc, gc2);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rect.p0.x = 1;
+	rect.p0.y = 2;
+	rect.p1.x = 3;
+	rect.p1.y = 4;
+
+	/* Set clipping rectangle returning error */
+
+	tgc1.set_clip_rect_called = false;
+	tgc2.set_clip_rect_called = false;
+	tgc1.rc = EINVAL;
+	tgc2.rc = EINVAL;
+
+	rc = gfx_set_clip_rect(gc, &rect);
+	PCUT_ASSERT_ERRNO_VAL(EINVAL, rc);
+
+	PCUT_ASSERT_TRUE(tgc1.set_clip_rect_called);
+	PCUT_ASSERT_EQUALS(&rect, tgc1.set_clip_rect_rect);
+	PCUT_ASSERT_FALSE(tgc2.set_clip_rect_called);
+
+	/* Set clipping rectangle returning success for all outputs */
+	tgc1.set_clip_rect_called = false;
+	tgc2.set_clip_rect_called = false;
+	tgc1.rc = EOK;
+	tgc2.rc = EOK;
+
+	rc = gfx_set_clip_rect(gc, &rect);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_TRUE(tgc1.set_clip_rect_called);
+	PCUT_ASSERT_EQUALS(&rect, tgc1.set_clip_rect_rect);
+	PCUT_ASSERT_TRUE(tgc2.set_clip_rect_called);
+	PCUT_ASSERT_EQUALS(&rect, tgc2.set_clip_rect_rect);
 
 	rc = ds_clonegc_delete(cgc);
@@ -628,4 +703,14 @@
 }
 
+static errno_t testgc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	test_gc_t *tgc = (test_gc_t *) arg;
+
+	tgc->set_clip_rect_called = true;
+	tgc->set_clip_rect_rect = rect;
+
+	return tgc->rc;
+}
+
 static errno_t testgc_set_color(void *arg, gfx_color_t *color)
 {
Index: uspace/srv/hid/display/test/display.c
===================================================================
--- uspace/srv/hid/display/test/display.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/test/display.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -94,14 +94,19 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *w0;
 	ds_window_t *w1;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
-	errno_t rc;
-
-	rc = ds_display_create(NULL, df_none, &disp);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ds_client_create(disp, &test_ds_client_cb, NULL, &client);
+	bool called_cb = false;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, df_none, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -148,4 +153,5 @@
 	ds_window_destroy(w0);
 	ds_window_destroy(w1);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -157,4 +163,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *w0;
 	ds_window_t *w1;
@@ -162,10 +169,14 @@
 	display_wnd_params_t params;
 	gfx_coord2_t pos;
-	errno_t rc;
-
-	rc = ds_display_create(NULL, df_none, &disp);
-	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
-
-	rc = ds_client_create(disp, &test_ds_client_cb, NULL, &client);
+	bool called_cb = false;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, df_none, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -198,4 +209,5 @@
 	ds_window_destroy(w0);
 	ds_window_destroy(w1);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
Index: uspace/srv/hid/display/test/seat.c
===================================================================
--- uspace/srv/hid/display/test/seat.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/test/seat.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -90,6 +90,6 @@
 }
 
-/** Evacuate focus. */
-PCUT_TEST(evac_focus)
+/** Evacuate focus when another window is available. */
+PCUT_TEST(evac_focus_two_windows)
 {
 	ds_display_t *disp;
@@ -126,5 +126,5 @@
 	called_cb = false;
 
-	ds_seat_evac_focus(seat, w1);
+	ds_seat_evac_wnd_refs(seat, w1);
 	PCUT_ASSERT_EQUALS(w0, seat->focus);
 	PCUT_ASSERT_TRUE(called_cb);
@@ -132,4 +132,183 @@
 	ds_window_destroy(w0);
 	ds_window_destroy(w1);
+	ds_seat_destroy(seat);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Evacuate focus from the only existing window.
+ *
+ * After evacuating no window should be focused
+ */
+PCUT_TEST(evac_focus_one_window)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_seat_t *seat;
+	ds_window_t *wnd;
+	display_wnd_params_t params;
+	bool called_cb = false;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, df_none, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ds_seat_set_focus(seat, wnd);
+	PCUT_ASSERT_EQUALS(wnd, seat->focus);
+	PCUT_ASSERT_TRUE(called_cb);
+	called_cb = false;
+
+	ds_seat_evac_wnd_refs(seat, wnd);
+	PCUT_ASSERT_NULL(seat->focus);
+	PCUT_ASSERT_TRUE(called_cb);
+
+	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Evacuate popup reference from window.
+ *
+ * After evacuating no window should be set as the popup
+ */
+PCUT_TEST(evac_popup)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_seat_t *seat;
+	ds_window_t *wnd;
+	display_wnd_params_t params;
+	bool called_cb = false;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, df_none, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+	params.flags |= wndf_popup;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_EQUALS(wnd, seat->popup);
+
+	ds_seat_evac_wnd_refs(seat, wnd);
+	PCUT_ASSERT_NULL(seat->popup);
+
+	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Switch focus when another window is available. */
+PCUT_TEST(switch_focus_two_windows)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_seat_t *seat;
+	ds_window_t *w0;
+	ds_window_t *w1;
+	display_wnd_params_t params;
+	bool called_cb = false;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, df_none, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &w1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_window_create(client, &params, &w0);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ds_seat_set_focus(seat, w1);
+	PCUT_ASSERT_EQUALS(w1, seat->focus);
+	PCUT_ASSERT_TRUE(called_cb);
+	called_cb = false;
+
+	ds_seat_switch_focus(seat);
+	PCUT_ASSERT_EQUALS(w0, seat->focus);
+	PCUT_ASSERT_TRUE(called_cb);
+
+	ds_window_destroy(w0);
+	ds_window_destroy(w1);
+	ds_seat_destroy(seat);
+	ds_client_destroy(client);
+	ds_display_destroy(disp);
+}
+
+/** Switch focus with just one existing window.
+ *
+ * After switching the focus should remain with the same window.
+ */
+PCUT_TEST(switch_focus_one_window)
+{
+	ds_display_t *disp;
+	ds_client_t *client;
+	ds_seat_t *seat;
+	ds_window_t *wnd;
+	display_wnd_params_t params;
+	bool called_cb = false;
+	errno_t rc;
+
+	rc = ds_display_create(NULL, df_none, &disp);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_client_create(disp, &test_ds_client_cb, &called_cb, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	display_wnd_params_init(&params);
+	params.rect.p0.x = params.rect.p0.y = 0;
+	params.rect.p1.x = params.rect.p1.y = 1;
+
+	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	ds_seat_set_focus(seat, wnd);
+	PCUT_ASSERT_EQUALS(wnd, seat->focus);
+	PCUT_ASSERT_TRUE(called_cb);
+	called_cb = false;
+
+	ds_seat_switch_focus(seat);
+	PCUT_ASSERT_EQUALS(wnd, seat->focus);
+	PCUT_ASSERT_FALSE(called_cb);
+
+	ds_window_destroy(wnd);
 	ds_seat_destroy(seat);
 	ds_client_destroy(client);
@@ -299,5 +478,8 @@
 	w1->dpos.y = 400;
 
-	PCUT_ASSERT_FALSE(called_cb);
+	/* New window gets focused event */
+	PCUT_ASSERT_TRUE(called_cb);
+
+	called_cb = false;
 
 	ds_seat_set_focus(seat, w0);
Index: uspace/srv/hid/display/test/window.c
===================================================================
--- uspace/srv/hid/display/test/window.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/test/window.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -104,4 +104,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
@@ -113,4 +114,7 @@
 
 	rc = ds_client_create(disp, NULL, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -126,4 +130,5 @@
 
 	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -136,4 +141,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	ds_window_t *rwindow;
@@ -152,4 +158,7 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
+	rc = ds_seat_create(disp, &seat);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
 	display_wnd_params_init(&params);
 	params.rect.p0.x = params.rect.p0.y = 0;
@@ -157,4 +166,8 @@
 
 	rc = ds_window_create(client, &params, &wnd);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* New window gets focused event */
+	rc = ds_client_get_event(client, &rwindow, &revent);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -174,4 +187,5 @@
 
 	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -184,4 +198,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
@@ -196,4 +211,7 @@
 
 	rc = ds_client_create(disp, NULL, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -242,4 +260,5 @@
 
 	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -252,4 +271,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
@@ -264,4 +284,7 @@
 
 	rc = ds_client_create(disp, NULL, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -284,4 +307,5 @@
 
 	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -340,4 +364,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
@@ -353,4 +378,7 @@
 
 	rc = ds_client_create(disp, NULL, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -597,4 +625,5 @@
 
 	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
@@ -607,4 +636,5 @@
 	ds_display_t *disp;
 	ds_client_t *client;
+	ds_seat_t *seat;
 	ds_window_t *wnd;
 	display_wnd_params_t params;
@@ -618,4 +648,7 @@
 
 	rc = ds_client_create(disp, NULL, NULL, &client);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = ds_seat_create(disp, &seat);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
@@ -646,4 +679,5 @@
 
 	ds_window_destroy(wnd);
+	ds_seat_destroy(seat);
 	ds_client_destroy(client);
 	ds_display_destroy(disp);
Index: uspace/srv/hid/display/types/display/seat.h
===================================================================
--- uspace/srv/hid/display/types/display/seat.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/types/display/seat.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -48,4 +48,6 @@
 	/** Window this seat is focused on */
 	struct ds_window *focus;
+	/** This seat's popup window */
+	struct ds_window *popup;
 	/** Cursor selected by client */
 	struct ds_cursor *client_cursor;
Index: uspace/srv/hid/display/types/display/window.h
===================================================================
--- uspace/srv/hid/display/types/display/window.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/types/display/window.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,4 +39,5 @@
 #include <adt/list.h>
 #include <display/event.h>
+#include <display/wndparams.h>
 #include <display/wndresize.h>
 #include <gfx/context.h>
@@ -92,5 +93,6 @@
 	/** Cursor set by client */
 	struct ds_cursor *cursor;
-
+	/** Window flags */
+	display_wnd_flags_t flags;
 	/** State */
 	ds_window_state_t state;
Index: uspace/srv/hid/display/window.c
===================================================================
--- uspace/srv/hid/display/window.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/window.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2019 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -49,6 +49,12 @@
 #include "window.h"
 
-static void ds_window_update_cb(void *, gfx_rect_t *);
+static void ds_window_invalidate_cb(void *, gfx_rect_t *);
+static void ds_window_update_cb(void *);
 static void ds_window_get_preview_rect(ds_window_t *, gfx_rect_t *);
+
+static mem_gc_cb_t ds_window_mem_gc_cb = {
+	.invalidate = ds_window_invalidate_cb,
+	.update = ds_window_update_cb
+};
 
 /** Create window.
@@ -66,4 +72,5 @@
 {
 	ds_window_t *wnd = NULL;
+	ds_seat_t *seat;
 	gfx_context_t *dgc;
 	gfx_coord2_t dims;
@@ -106,5 +113,5 @@
 	}
 
-	rc = mem_gc_create(&params->rect, &alloc, ds_window_update_cb,
+	rc = mem_gc_create(&params->rect, &alloc, &ds_window_mem_gc_cb,
 	    (void *)wnd, &wnd->mgc);
 	if (rc != EOK)
@@ -115,4 +122,24 @@
 	wnd->gc = mem_gc_get_ctx(wnd->mgc);
 	wnd->cursor = wnd->display->cursor[dcurs_arrow];
+	wnd->flags = params->flags;
+
+	if ((params->flags & wndf_setpos) != 0) {
+		/* Specific window position */
+		wnd->dpos = params->pos;
+	} else {
+		/* Automatic window placement */
+		wnd->dpos.x = ((wnd->id - 1) & 1) * 400;
+		wnd->dpos.y = ((wnd->id - 1) & 2) / 2 * 300;
+	}
+
+	seat = ds_display_first_seat(client->display);
+
+	if ((params->flags & wndf_popup) != 0)
+		ds_seat_set_popup(seat, wnd);
+	else
+		ds_seat_set_focus(seat, wnd);
+
+	(void) ds_display_paint(wnd->display, NULL);
+
 	*rgc = wnd;
 	return EOK;
@@ -657,4 +684,13 @@
 }
 
+/** Get window position.
+ *
+ * @param wnd Window
+ */
+void ds_window_get_pos(ds_window_t *wnd, gfx_coord2_t *dpos)
+{
+	*dpos = wnd->dpos;
+}
+
 /** Start resizing a window, detected by client.
  *
@@ -789,9 +825,9 @@
 }
 
-/** Window memory GC update callback.
- *
- * This is called by the window's memory GC when a rectangle us updated.
- */
-static void ds_window_update_cb(void *arg, gfx_rect_t *rect)
+/** Window memory GC invalidate callback.
+ *
+ * This is called by the window's memory GC when a rectangle is modified.
+ */
+static void ds_window_invalidate_cb(void *arg, gfx_rect_t *rect)
 {
 	ds_window_t *wnd = (ds_window_t *)arg;
@@ -806,4 +842,15 @@
 }
 
+/** Window memory GC update callback.
+ *
+ * This is called by the window's memory GC when it is to be updated.
+ */
+static void ds_window_update_cb(void *arg)
+{
+	ds_window_t *wnd = (ds_window_t *)arg;
+
+	(void) wnd;
+}
+
 /** @}
  */
Index: uspace/srv/hid/display/window.h
===================================================================
--- uspace/srv/hid/display/window.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/display/window.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -63,4 +63,5 @@
 extern void ds_window_move_req(ds_window_t *, gfx_coord2_t *);
 extern void ds_window_move(ds_window_t *, gfx_coord2_t *);
+extern void ds_window_get_pos(ds_window_t *, gfx_coord2_t *);
 extern void ds_window_resize_req(ds_window_t *, display_wnd_rsztype_t,
     gfx_coord2_t *);
Index: uspace/srv/hid/output/meson.build
===================================================================
--- uspace/srv/hid/output/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/output/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -28,9 +28,10 @@
 #
 
-deps = [ 'drv' ]
+deps = [ 'drv', 'fbfont', 'pixconv', 'ddev' ]
 src = files(
 	'ctl/serial.c',
 	'port/ega.c',
 	'port/chardev.c',
+	'port/ddev.c',
 	'proto/vt100.c',
 	'output.c',
Index: uspace/srv/hid/output/output.c
===================================================================
--- uspace/srv/hid/output/output.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/output/output.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -41,4 +41,5 @@
 #include "port/ega.h"
 #include "port/chardev.h"
+#include "port/ddev.h"
 #include "output.h"
 
@@ -486,4 +487,12 @@
 	if (!config_key_exists("console")) {
 		ega_init();
+#if defined(CONFIG_FB) && !defined(CONFIG_WINSYS)
+		/*
+		 * NOTE: We don't have a good way of detecting the absence
+		 * of a KFB display device at run time because of the
+		 * asunchronous nature of device discovery
+		 */
+		output_ddev_init();
+#endif
 	}
 
Index: uspace/srv/hid/output/port/ddev.c
===================================================================
--- uspace/srv/hid/output/port/ddev.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/srv/hid/output/port/ddev.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2020 Jiri Svoboda
+ * Copyright (c) 2008 Martin Decky
+ * Copyright (c) 2006 Jakub Vana
+ * Copyright (c) 2006 Ondrej Palkovsky
+ * 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 output
+ * @{
+ */
+
+#include <ddev.h>
+#include <errno.h>
+#include <fbfont/font-8x16.h>
+#include <gfx/bitmap.h>
+#include <gfx/color.h>
+#include <gfx/coord.h>
+#include <gfx/context.h>
+#include <gfx/render.h>
+#include <io/chargrid.h>
+#include <io/pixelmap.h>
+#include <stdlib.h>
+#include "../output.h"
+#include "ddev.h"
+
+#define KFB_DDEV_NAME "devices/\\virt\\kfb\\kfb"
+
+typedef struct {
+	gfx_context_t *gc;
+
+	sysarg_t cols;
+	sysarg_t rows;
+
+	uint8_t style_normal;
+	uint8_t style_inverted;
+
+	gfx_bitmap_t *bitmap;
+	pixelmap_t pixelmap;
+	gfx_rect_t dirty;
+
+	sysarg_t curs_col;
+	sysarg_t curs_row;
+	bool curs_visible;
+} output_ddev_t;
+
+static pixel_t color_table[16] = {
+	[COLOR_BLACK]       = 0x000000,
+	[COLOR_BLUE]        = 0x0000f0,
+	[COLOR_GREEN]       = 0x00f000,
+	[COLOR_CYAN]        = 0x00f0f0,
+	[COLOR_RED]         = 0xf00000,
+	[COLOR_MAGENTA]     = 0xf000f0,
+	[COLOR_YELLOW]      = 0xf0f000,
+	[COLOR_WHITE]       = 0xf0f0f0,
+
+	[COLOR_BLACK + 8]   = 0x000000,
+	[COLOR_BLUE + 8]    = 0x0000ff,
+	[COLOR_GREEN + 8]   = 0x00ff00,
+	[COLOR_CYAN + 8]    = 0x00ffff,
+	[COLOR_RED + 8]     = 0xff0000,
+	[COLOR_MAGENTA + 8] = 0xff00ff,
+	[COLOR_YELLOW + 8]  = 0xffff00,
+	[COLOR_WHITE + 8]   = 0xffffff,
+};
+
+static void attrs_rgb(char_attrs_t attrs, pixel_t *bgcolor, pixel_t *fgcolor)
+{
+	switch (attrs.type) {
+	case CHAR_ATTR_STYLE:
+		switch (attrs.val.style) {
+		case STYLE_NORMAL:
+			*bgcolor = color_table[COLOR_WHITE];
+			*fgcolor = color_table[COLOR_BLACK];
+			break;
+		case STYLE_EMPHASIS:
+			*bgcolor = color_table[COLOR_WHITE];
+			*fgcolor = color_table[COLOR_RED];
+			break;
+		case STYLE_INVERTED:
+			*bgcolor = color_table[COLOR_BLACK];
+			*fgcolor = color_table[COLOR_WHITE];
+			break;
+		case STYLE_SELECTED:
+			*bgcolor = color_table[COLOR_RED];
+			*fgcolor = color_table[COLOR_WHITE];
+			break;
+		}
+		break;
+	case CHAR_ATTR_INDEX:
+		*bgcolor = color_table[(attrs.val.index.bgcolor & 7) |
+		    ((attrs.val.index.attr & CATTR_BRIGHT) ? 8 : 0)];
+		*fgcolor = color_table[(attrs.val.index.fgcolor & 7) |
+		    ((attrs.val.index.attr & CATTR_BRIGHT) ? 8 : 0)];
+		break;
+	case CHAR_ATTR_RGB:
+		*bgcolor = attrs.val.rgb.bgcolor;
+		*fgcolor = attrs.val.rgb.fgcolor;
+		break;
+	}
+}
+
+/** Draw the character at the specified position.
+ *
+ * @param field Character field.
+ * @param col   Horizontal screen position.
+ * @param row   Vertical screen position.
+ *
+ */
+static void draw_char(output_ddev_t *ddev, charfield_t *field,
+    sysarg_t col, sysarg_t row)
+{
+	pixel_t fgcolor;
+	pixel_t bgcolor;
+	pixel_t pcolor;
+	pixel_t tmp;
+	gfx_rect_t rect;
+	gfx_rect_t ndrect;
+	gfx_coord_t x, y;
+	bool inverted;
+	uint32_t glyph;
+
+	fgcolor = bgcolor = 0;
+	attrs_rgb(field->attrs, &bgcolor, &fgcolor);
+
+	inverted = (col == ddev->curs_col) && (row == ddev->curs_row) &&
+	    ddev->curs_visible;
+
+	if (inverted) {
+		/* Swap foreground and background colors */
+		tmp = bgcolor;
+		bgcolor = fgcolor;
+		fgcolor = tmp;
+	}
+
+	rect.p0.x = FONT_WIDTH * col;
+	rect.p0.y = FONT_SCANLINES * row;
+	rect.p1.x = FONT_WIDTH * (col + 1);
+	rect.p1.y = FONT_SCANLINES * (row + 1);
+
+	glyph = fb_font_glyph(field->ch, NULL);
+
+	for (y = 0; y < FONT_SCANLINES; y++) {
+		for (x = 0; x < FONT_WIDTH; x++) {
+			pcolor = fb_font[glyph][y] & (1 << (7 - x)) ?
+			    fgcolor : bgcolor;
+			pixelmap_put_pixel(&ddev->pixelmap,
+			    rect.p0.x + x, rect.p0.y + y, pcolor);
+		}
+	}
+
+	/* Compute new dirty rectangle */
+	gfx_rect_envelope(&ddev->dirty, &rect, &ndrect);
+	ddev->dirty = ndrect;
+}
+
+static errno_t output_ddev_yield(outdev_t *dev)
+{
+	return EOK;
+}
+
+static errno_t output_ddev_claim(outdev_t *dev)
+{
+	return EOK;
+}
+
+static void output_ddev_get_dimensions(outdev_t *dev, sysarg_t *cols, sysarg_t *rows)
+{
+	output_ddev_t *ddev = (output_ddev_t *) dev->data;
+
+	*cols = ddev->cols;
+	*rows = ddev->rows;
+}
+
+static console_caps_t output_ddev_get_caps(outdev_t *dev)
+{
+	return (CONSOLE_CAP_STYLE | CONSOLE_CAP_INDEXED | CONSOLE_CAP_RGB);
+}
+
+static void output_ddev_char_update(outdev_t *dev, sysarg_t col, sysarg_t row)
+{
+	output_ddev_t *ddev = (output_ddev_t *) dev->data;
+
+	charfield_t *field =
+	    chargrid_charfield_at(dev->backbuf, col, row);
+
+	draw_char(ddev, field, col, row);
+}
+
+static void output_ddev_cursor_update(outdev_t *dev, sysarg_t prev_col,
+    sysarg_t prev_row, sysarg_t col, sysarg_t row, bool visible)
+{
+	output_ddev_t *ddev = (output_ddev_t *) dev->data;
+
+	ddev->curs_col = col;
+	ddev->curs_row = row;
+	ddev->curs_visible = visible;
+
+	output_ddev_char_update(dev, prev_col, prev_row);
+	output_ddev_char_update(dev, col, row);
+}
+
+static void output_ddev_flush(outdev_t *dev)
+{
+	output_ddev_t *ddev = (output_ddev_t *) dev->data;
+
+	(void) gfx_bitmap_render(ddev->bitmap, &ddev->dirty, NULL);
+
+	ddev->dirty.p0.x = 0;
+	ddev->dirty.p0.y = 0;
+	ddev->dirty.p1.x = 0;
+	ddev->dirty.p1.y = 0;
+}
+
+static outdev_ops_t output_ddev_ops = {
+	.yield = output_ddev_yield,
+	.claim = output_ddev_claim,
+	.get_dimensions = output_ddev_get_dimensions,
+	.get_caps = output_ddev_get_caps,
+	.cursor_update = output_ddev_cursor_update,
+	.char_update = output_ddev_char_update,
+	.flush = output_ddev_flush
+};
+
+errno_t output_ddev_init(void)
+{
+	ddev_t *dd = NULL;
+	output_ddev_t *ddev = NULL;
+	ddev_info_t info;
+	gfx_coord2_t dims;
+	gfx_context_t *gc;
+	gfx_bitmap_params_t params;
+	gfx_bitmap_alloc_t alloc;
+	gfx_bitmap_t *bitmap = NULL;
+	errno_t rc;
+
+	ddev = calloc(1, sizeof(output_ddev_t));
+	if (ddev == NULL)
+		return ENOMEM;
+
+	rc = ddev_open(KFB_DDEV_NAME, &dd);
+	if (rc != EOK) {
+		printf("Error opening display device '%s'.", KFB_DDEV_NAME);
+		goto error;
+	}
+
+	rc = ddev_get_info(dd, &info);
+	if (rc != EOK) {
+		printf("Error getting information for display device '%s'.",
+		    KFB_DDEV_NAME);
+		goto error;
+	}
+
+	rc = ddev_get_gc(dd, &gc);
+	if (rc != EOK) {
+		printf("Error getting device context for '%s'.", KFB_DDEV_NAME);
+		goto error;
+	}
+
+	gfx_bitmap_params_init(&params);
+	params.rect = info.rect;
+
+	rc = gfx_bitmap_create(gc, &params, NULL, &bitmap);
+	if (rc != EOK) {
+		printf("Error creating screen bitmap.\n");
+		goto error;
+	}
+
+	rc = gfx_bitmap_get_alloc(bitmap, &alloc);
+	if (rc != EOK) {
+		printf("Error getting bitmap allocation info.\n");
+		goto error;
+	}
+
+	gfx_rect_dims(&info.rect, &dims);
+
+	ddev->gc = gc;
+	ddev->cols = dims.x / FONT_WIDTH;
+	ddev->rows = dims.y / FONT_SCANLINES;
+	ddev->bitmap = bitmap;
+	ddev->pixelmap.width = dims.x;
+	ddev->pixelmap.height = dims.y;
+	ddev->pixelmap.data = alloc.pixels;
+
+	outdev_t *dev = outdev_register(&output_ddev_ops, (void *) ddev);
+	if (dev == NULL) {
+		rc = EINVAL;
+		goto error;
+	}
+
+	return EOK;
+error:
+	if (dd != NULL)
+		ddev_close(dd);
+	if (bitmap != NULL)
+		gfx_bitmap_destroy(bitmap);
+	free(ddev);
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/srv/hid/output/port/ddev.h
===================================================================
--- uspace/srv/hid/output/port/ddev.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
+++ uspace/srv/hid/output/port/ddev.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 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 output
+ * @{
+ */
+
+#ifndef OUTPUT_PORT_DDEV_H_
+#define OUTPUT_PORT_DDEV_H_
+
+extern errno_t output_ddev_init(void);
+
+#endif
+
+/** @}
+ */
Index: pace/srv/hid/output/port/kfb.c
===================================================================
--- uspace/srv/hid/output/port/kfb.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,788 +1,0 @@
-/*
- * Copyright (c) 2008 Martin Decky
- * Copyright (c) 2006 Jakub Vana
- * Copyright (c) 2006 Ondrej Palkovsky
- * 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 output
- * @{
- */
-
-#include <abi/fb/visuals.h>
-#include <sys/types.h>
-#include <inttypes.h>
-#include <screenbuffer.h>
-#include <imgmap.h>
-#include <errno.h>
-#include <bool.h>
-#include <sysinfo.h>
-#include <ddi.h>
-#include <stdlib.h>
-#include <mem.h>
-#include <as.h>
-#include <align.h>
-#include "../gfx/font-8x16.h"
-#include "../fb.h"
-#include "kfb.h"
-
-#define DEFAULT_BGCOLOR  0xffffff
-#define DEFAULT_FGCOLOR  0x000000
-
-#define FB_POS(x, y)  ((y) * kfb.scanline + (x) * kfb.pixel_bytes)
-
-#define COL2X(col)  ((col) * FONT_WIDTH)
-#define ROW2Y(row)  ((row) * FONT_SCANLINES)
-#define X2COL(x)    ((x) / FONT_WIDTH)
-#define Y2ROW(y)    ((y) / FONT_SCANLINES)
-
-#define GLYPH_POS(glyph, y, inverted) \
-	(((glyph) + (inverted) * FONT_GLYPHS) * (kfb.glyph_bytes) + (y) * (kfb.glyph_scanline))
-
-#define POINTER_WIDTH   11
-#define POINTER_HEIGHT  18
-
-static uint8_t pointer[] = {
-	0x01, 0x00, 0x03, 0x00, 0x05, 0x00, 0x09, 0x00, 0x11, 0x00, 0x21, 0x00,
-	0x41, 0x00, 0x81, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x04, 0x01, 0x03,
-	0x81, 0x00, 0x89, 0x00, 0x15, 0x01, 0x23, 0x01, 0x21, 0x01, 0xc0, 0x00
-};
-
-static uint8_t pointer_mask[] = {
-	0x01, 0x00, 0x03, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x3f, 0x00,
-	0x7f, 0x00, 0xff, 0x00, 0xff, 0x01, 0xff, 0x03, 0xff, 0x07, 0xff, 0x03,
-	0xff, 0x00, 0xff, 0x00, 0xf7, 0x01, 0xe3, 0x01, 0xe1, 0x01, 0xc0, 0x00
-};
-
-/** Function to draw a character. */
-typedef void (*draw_char_t)(sysarg_t, sysarg_t, bool, char32_t, pixel_t,
-    pixel_t);
-
-typedef struct {
-	sysarg_t width;
-	sysarg_t height;
-	size_t offset;
-	size_t scanline;
-	visual_t visual;
-
-	size_t size;
-	uint8_t *addr;
-
-	pixel2visual_t pixel2visual;
-	visual2pixel_t visual2pixel;
-	visual_mask_t visual_mask;
-	size_t pixel_bytes;
-
-	sysarg_t pointer_x;
-	sysarg_t pointer_y;
-	bool pointer_visible;
-	imgmap_t *pointer_imgmap;
-
-	/*
-	 * Pre-rendered mask for rendering
-	 * glyphs. Specific for the framebuffer
-	 * visual.
-	 */
-
-	size_t glyph_scanline;
-	size_t glyph_bytes;
-	uint8_t *glyphs;
-
-	uint8_t *backbuf;
-} kfb_t;
-
-typedef struct {
-	/**
-	 * Character drawing function for this viewport.
-	 * Different viewports might use different drawing
-	 * functions depending on whether their scanlines
-	 * are aligned on a word boundary.
-	 */
-	draw_char_t draw_char;
-} kfb_vp_t;
-
-static kfb_t kfb;
-
-static pixel_t color_table[16] = {
-	[COLOR_BLACK]       = 0x000000,
-	[COLOR_BLUE]        = 0x0000f0,
-	[COLOR_GREEN]       = 0x00f000,
-	[COLOR_CYAN]        = 0x00f0f0,
-	[COLOR_RED]         = 0xf00000,
-	[COLOR_MAGENTA]     = 0xf000f0,
-	[COLOR_YELLOW]      = 0xf0f000,
-	[COLOR_WHITE]       = 0xf0f0f0,
-
-	[COLOR_BLACK + 8]   = 0x000000,
-	[COLOR_BLUE + 8]    = 0x0000ff,
-	[COLOR_GREEN + 8]   = 0x00ff00,
-	[COLOR_CYAN + 8]    = 0x00ffff,
-	[COLOR_RED + 8]     = 0xff0000,
-	[COLOR_MAGENTA + 8] = 0xff00ff,
-	[COLOR_YELLOW + 8]  = 0xffff00,
-	[COLOR_WHITE + 8]   = 0xffffff,
-};
-
-static void put_pixel(sysarg_t x, sysarg_t y, pixel_t pixel)
-{
-	if ((x >= kfb.width) || (y >= kfb.height))
-		return;
-
-	kfb.pixel2visual(kfb.addr + FB_POS(x, y), pixel);
-}
-
-static pixel_t get_pixel(sysarg_t x, sysarg_t y)
-{
-	if ((x >= kfb.width) || (y >= kfb.height))
-		return 0;
-
-	return kfb.visual2pixel(kfb.addr + FB_POS(x, y));
-}
-
-static void pointer_show(void)
-{
-	if (kfb.pointer_visible) {
-		for (sysarg_t y = 0; y < POINTER_HEIGHT; y++) {
-			for (sysarg_t x = 0; x < POINTER_WIDTH; x++) {
-				sysarg_t dx = kfb.pointer_x + x;
-				sysarg_t dy = kfb.pointer_y + y;
-
-				pixel_t pixel = get_pixel(dx, dy);
-				imgmap_put_pixel(kfb.pointer_imgmap, x, y, pixel);
-
-				size_t offset = y * ((POINTER_WIDTH - 1) / 8 + 1) + x / 8;
-				bool visible = pointer_mask[offset] & (1 << (x % 8));
-
-				if (visible) {
-					pixel = (pointer[offset] & (1 << (x % 8))) ?
-					    0x000000 : 0xffffff;
-					put_pixel(dx, dy, pixel);
-				}
-			}
-		}
-	}
-}
-
-static void pointer_hide(void)
-{
-	if (kfb.pointer_visible) {
-		for (sysarg_t y = 0; y < POINTER_HEIGHT; y++) {
-			for (sysarg_t x = 0; x < POINTER_WIDTH; x++) {
-				sysarg_t dx = kfb.pointer_x + x;
-				sysarg_t dy = kfb.pointer_y + y;
-
-				pixel_t pixel =
-				    imgmap_get_pixel(kfb.pointer_imgmap, x, y);
-				put_pixel(dx, dy, pixel);
-			}
-		}
-	}
-}
-
-/** Draw a filled rectangle.
- *
- */
-static void draw_filled_rect(sysarg_t x1, sysarg_t y1, sysarg_t x2, sysarg_t y2,
-    pixel_t color)
-{
-	if ((y1 >= y2) || (x1 >= x2))
-		return;
-
-	uint8_t cbuf[4];
-	kfb.pixel2visual(cbuf, color);
-
-	for (sysarg_t y = y1; y < y2; y++) {
-		uint8_t *dst = kfb.addr + FB_POS(x1, y);
-
-		for (sysarg_t x = x1; x < x2; x++) {
-			memcpy(dst, cbuf, kfb.pixel_bytes);
-			dst += kfb.pixel_bytes;
-		}
-	}
-}
-
-static void vp_put_pixel(fbvp_t *vp, sysarg_t x, sysarg_t y, pixel_t pixel)
-{
-	put_pixel(vp->x + x, vp->y + y, pixel);
-}
-
-static void attrs_rgb(char_attrs_t attrs, pixel_t *bgcolor, pixel_t *fgcolor)
-{
-	switch (attrs.type) {
-	case CHAR_ATTR_STYLE:
-		switch (attrs.val.style) {
-		case STYLE_NORMAL:
-			*bgcolor = color_table[COLOR_WHITE];
-			*fgcolor = color_table[COLOR_BLACK];
-			break;
-		case STYLE_EMPHASIS:
-			*bgcolor = color_table[COLOR_WHITE];
-			*fgcolor = color_table[COLOR_RED];
-			break;
-		case STYLE_INVERTED:
-			*bgcolor = color_table[COLOR_BLACK];
-			*fgcolor = color_table[COLOR_WHITE];
-			break;
-		case STYLE_SELECTED:
-			*bgcolor = color_table[COLOR_RED];
-			*fgcolor = color_table[COLOR_WHITE];
-			break;
-		}
-		break;
-	case CHAR_ATTR_INDEX:
-		*bgcolor = color_table[(attrs.val.index.bgcolor & 7) |
-		    ((attrs.val.index.attr & CATTR_BRIGHT) ? 8 : 0)];
-		*fgcolor = color_table[(attrs.val.index.fgcolor & 7) |
-		    ((attrs.val.index.attr & CATTR_BRIGHT) ? 8 : 0)];
-		break;
-	case CHAR_ATTR_RGB:
-		*bgcolor = attrs.val.rgb.bgcolor;
-		*fgcolor = attrs.val.rgb.fgcolor;
-		break;
-	}
-}
-
-/** Draw a character, takes advantage of alignment.
- *
- * This version can only be used if the following conditions are met:
- *
- *   - word size is divisible by pixel_bytes
- *   - cell scanline size is divisible by word size
- *   - cell scanlines are word-aligned
- *
- * It makes use of the pre-rendered mask to process (possibly) several
- * pixels at once (word size / pixel_bytes pixels at a time are processed)
- * making it very fast. Most notably this version is not applicable at 24 bits
- * per pixel.
- *
- * @param x        X coordinate of top-left corner on screen.
- * @param y        Y coordinate of top-left corner on screen.
- * @param inverted Draw inverted character.
- * @param ch       Character to draw.
- * @param bgcolor  Background color.
- * @param fbgcolor Foreground color.
- *
- */
-static void draw_char_aligned(sysarg_t x, sysarg_t y, bool inverted, char32_t ch,
-    pixel_t bgcolor, pixel_t fgcolor)
-{
-	size_t word_size = sizeof(unsigned long);
-
-	/*
-	 * Prepare a pair of words, one filled with foreground-color
-	 * pattern and the other filled with background-color pattern.
-	 */
-	unsigned long fg_buf;
-	unsigned long bg_buf;
-
-	for (size_t i = 0; i < word_size / kfb.pixel_bytes; i++) {
-		kfb.pixel2visual(&((uint8_t *) &bg_buf)[i * kfb.pixel_bytes],
-		    bgcolor);
-		kfb.pixel2visual(&((uint8_t *) &fg_buf)[i * kfb.pixel_bytes],
-		    fgcolor);
-	}
-
-	/* Pointer to the current position in the mask. */
-	unsigned long *maskp =
-	    (unsigned long *) &kfb.glyphs[GLYPH_POS(
-	    fb_font_glyph(ch), 0, inverted)];
-
-	/* Pointer to the current position on the screen. */
-	unsigned long *dst =
-	    (unsigned long *) &kfb.addr[FB_POS(x, y)];
-
-	/* Width of the character cell in words. */
-	size_t ww = FONT_WIDTH * kfb.pixel_bytes / word_size;
-
-	/* Offset to add when moving to another screen scanline. */
-	size_t d_add = kfb.scanline - FONT_WIDTH * kfb.pixel_bytes;
-
-	for (size_t yd = 0; yd < FONT_SCANLINES; yd++) {
-		/*
-		 * Now process the cell scanline, combining foreground
-		 * and background color patters using the pre-rendered mask.
-		 */
-		for (size_t i = 0; i < ww; i++) {
-			unsigned long mask = *maskp++;
-			*dst++ = (fg_buf & mask) | (bg_buf & ~mask);
-		}
-
-		/* Move to the beginning of the next scanline of the cell. */
-		dst = (unsigned long *) ((uint8_t *) dst + d_add);
-	}
-}
-
-/** Draw a character, fallback version.
- *
- * This version does not make use of the pre-rendered mask, it uses
- * the font bitmap directly. It works always, but it is slower.
- *
- * @param x        X coordinate of top-left corner on screen.
- * @param y        Y coordinate of top-left corner on screen.
- * @param inverted Draw inverted character.
- * @param ch       Character to draw.
- * @param bgcolor  Background color.
- * @param fgcolor  Foreground color.
- *
- */
-static void draw_char_fallback(sysarg_t x, sysarg_t y, bool inverted,
-    char32_t ch, pixel_t bgcolor, pixel_t fgcolor)
-{
-	/* Character glyph */
-	uint16_t glyph = fb_font_glyph(ch);
-
-	/* Pre-render the foreground and background color pixels. */
-	uint8_t fg_buf[4];
-	uint8_t bg_buf[4];
-
-	if (inverted) {
-		kfb.pixel2visual(bg_buf, fgcolor);
-		kfb.pixel2visual(fg_buf, bgcolor);
-	} else {
-		kfb.pixel2visual(bg_buf, bgcolor);
-		kfb.pixel2visual(fg_buf, fgcolor);
-	}
-
-	/* Pointer to the current position on the screen. */
-	uint8_t *dst = (uint8_t *) &kfb.addr[FB_POS(x, y)];
-
-	/* Offset to add when moving to another screen scanline. */
-	size_t d_add = kfb.scanline - FONT_WIDTH * kfb.pixel_bytes;
-
-	for (size_t yd = 0; yd < FONT_SCANLINES; yd++) {
-		/* Byte containing bits of the glyph scanline. */
-		uint8_t byte = fb_font[glyph][yd];
-
-		for (size_t i = 0; i < FONT_WIDTH; i++) {
-			/* Choose color based on the current bit. */
-			uint8_t *src = (byte & 0x80) ? fg_buf : bg_buf;
-
-			/* Copy the pixel. */
-			for (size_t j = 0; j < kfb.pixel_bytes; j++)
-				*dst++ = *src++;
-
-			/* Move to the next bit. */
-			byte <<= 1;
-		}
-
-		/* Move to the beginning of the next scanline of the cell. */
-		dst += d_add;
-	}
-}
-
-/** Draw the character at the specified position in viewport.
- *
- * @param vp     Viewport.
- * @param col    Screen position relative to viewport.
- * @param row    Screen position relative to viewport.
- *
- */
-static void draw_vp_char(fbvp_t *vp, sysarg_t col, sysarg_t row)
-{
-	kfb_vp_t *kfb_vp = (kfb_vp_t *) vp->data;
-
-	sysarg_t x = vp->x + COL2X(col);
-	sysarg_t y = vp->y + ROW2Y(row);
-
-	charfield_t *field = screenbuffer_field_at(vp->backbuf, col, row);
-
-	pixel_t bgcolor = 0;
-	pixel_t fgcolor = 0;
-	attrs_rgb(field->attrs, &bgcolor, &fgcolor);
-
-	bool inverted = (vp->cursor_flash) &&
-	    screenbuffer_cursor_at(vp->backbuf, col, row);
-
-	(*kfb_vp->draw_char)(x, y, inverted, field->ch, bgcolor, fgcolor);
-}
-
-static errno_t kfb_yield(fbdev_t *dev)
-{
-	if (kfb.backbuf == NULL) {
-		kfb.backbuf =
-		    malloc(kfb.width * kfb.height * kfb.pixel_bytes);
-		if (kfb.backbuf == NULL)
-			return ENOMEM;
-	}
-
-	for (sysarg_t y = 0; y < kfb.height; y++)
-		memcpy(kfb.backbuf + y * kfb.width * kfb.pixel_bytes,
-		    kfb.addr + FB_POS(0, y), kfb.width * kfb.pixel_bytes);
-
-	return EOK;
-}
-
-static errno_t kfb_claim(fbdev_t *dev)
-{
-	if (kfb.backbuf == NULL)
-		return ENOENT;
-
-	for (sysarg_t y = 0; y < kfb.height; y++)
-		memcpy(kfb.addr + FB_POS(0, y),
-		    kfb.backbuf + y * kfb.width * kfb.pixel_bytes,
-		    kfb.width * kfb.pixel_bytes);
-
-	return EOK;
-}
-
-static void kfb_pointer_update(struct fbdev *dev, sysarg_t x, sysarg_t y,
-    bool visible)
-{
-	pointer_hide();
-
-	kfb.pointer_x = x;
-	kfb.pointer_y = y;
-	kfb.pointer_visible = visible;
-
-	pointer_show();
-}
-
-static errno_t kfb_get_resolution(fbdev_t *dev, sysarg_t *width, sysarg_t *height)
-{
-	*width = kfb.width;
-	*height = kfb.height;
-	return EOK;
-}
-
-static void kfb_font_metrics(fbdev_t *dev, sysarg_t width, sysarg_t height,
-    sysarg_t *cols, sysarg_t *rows)
-{
-	*cols = X2COL(width);
-	*rows = Y2ROW(height);
-}
-
-static errno_t kfb_vp_create(fbdev_t *dev, fbvp_t *vp)
-{
-	kfb_vp_t *kfb_vp = malloc(sizeof(kfb_vp_t));
-	if (kfb_vp == NULL)
-		return ENOMEM;
-
-	/*
-	 * Conditions necessary to select aligned glyph
-	 * drawing variants:
-	 *  - word size is divisible by number of bytes per pixel
-	 *  - cell scanline size is divisible by word size
-	 *  - cell scanlines are word-aligned
-	 *
-	 */
-	size_t word_size = sizeof(unsigned long);
-
-	if (((word_size % kfb.pixel_bytes) == 0) &&
-	    ((FONT_WIDTH * kfb.pixel_bytes) % word_size == 0) &&
-	    ((vp->x * kfb.pixel_bytes) % word_size == 0) &&
-	    (kfb.scanline % word_size == 0))
-		kfb_vp->draw_char = draw_char_aligned;
-	else
-		kfb_vp->draw_char = draw_char_fallback;
-
-	vp->attrs.type = CHAR_ATTR_RGB;
-	vp->attrs.val.rgb.bgcolor = DEFAULT_BGCOLOR;
-	vp->attrs.val.rgb.fgcolor = DEFAULT_FGCOLOR;
-	vp->data = (void *) kfb_vp;
-
-	return EOK;
-}
-
-static void kfb_vp_destroy(fbdev_t *dev, fbvp_t *vp)
-{
-	free(vp->data);
-}
-
-static void kfb_vp_clear(fbdev_t *dev, fbvp_t *vp)
-{
-	pointer_hide();
-
-	for (sysarg_t row = 0; row < vp->rows; row++) {
-		for (sysarg_t col = 0; col < vp->cols; col++) {
-			charfield_t *field =
-			    screenbuffer_field_at(vp->backbuf, col, row);
-
-			field->ch = 0;
-			field->attrs = vp->attrs;
-		}
-	}
-
-	pixel_t bgcolor = 0;
-	pixel_t fgcolor = 0;
-	attrs_rgb(vp->attrs, &bgcolor, &fgcolor);
-
-	draw_filled_rect(vp->x, vp->y, vp->x + vp->width,
-	    vp->y + vp->height, bgcolor);
-
-	pointer_show();
-}
-
-static console_caps_t kfb_vp_get_caps(fbdev_t *dev, fbvp_t *vp)
-{
-	return (CONSOLE_CAP_STYLE | CONSOLE_CAP_INDEXED | CONSOLE_CAP_RGB);
-}
-
-static void kfb_vp_cursor_update(fbdev_t *dev, fbvp_t *vp, sysarg_t prev_col,
-    sysarg_t prev_row, sysarg_t col, sysarg_t row, bool visible)
-{
-	pointer_hide();
-	draw_vp_char(vp, prev_col, prev_row);
-	draw_vp_char(vp, col, row);
-	pointer_show();
-}
-
-static void kfb_vp_cursor_flash(fbdev_t *dev, fbvp_t *vp, sysarg_t col,
-    sysarg_t row)
-{
-	pointer_hide();
-	draw_vp_char(vp, col, row);
-	pointer_show();
-}
-
-static void kfb_vp_char_update(fbdev_t *dev, fbvp_t *vp, sysarg_t col,
-    sysarg_t row)
-{
-	pointer_hide();
-	draw_vp_char(vp, col, row);
-	pointer_show();
-}
-
-static void kfb_vp_imgmap_damage(fbdev_t *dev, fbvp_t *vp, imgmap_t *imgmap,
-    sysarg_t x0, sysarg_t y0, sysarg_t width, sysarg_t height)
-{
-	pointer_hide();
-
-	for (sysarg_t y = 0; y < height; y++) {
-		for (sysarg_t x = 0; x < width; x++) {
-			pixel_t pixel = imgmap_get_pixel(imgmap, x0 + x, y0 + y);
-			vp_put_pixel(vp, x0 + x, y0 + y, pixel);
-		}
-	}
-
-	pointer_show();
-}
-
-static fbdev_ops_t kfb_ops = {
-	.yield = kfb_yield,
-	.claim = kfb_claim,
-	.pointer_update = kfb_pointer_update,
-	.get_resolution = kfb_get_resolution,
-	.font_metrics = kfb_font_metrics,
-	.vp_create = kfb_vp_create,
-	.vp_destroy = kfb_vp_destroy,
-	.vp_clear = kfb_vp_clear,
-	.vp_get_caps = kfb_vp_get_caps,
-	.vp_cursor_update = kfb_vp_cursor_update,
-	.vp_cursor_flash = kfb_vp_cursor_flash,
-	.vp_char_update = kfb_vp_char_update,
-	.vp_imgmap_damage = kfb_vp_imgmap_damage
-};
-
-/** Render glyphs
- *
- * Convert glyphs from device independent font
- * description to current visual representation.
- *
- */
-static void render_glyphs(size_t sz)
-{
-	memset(kfb.glyphs, 0, sz);
-
-	for (unsigned int glyph = 0; glyph < FONT_GLYPHS; glyph++) {
-		for (unsigned int y = 0; y < FONT_SCANLINES; y++) {
-			for (unsigned int x = 0; x < FONT_WIDTH; x++) {
-				kfb.visual_mask(
-				    &kfb.glyphs[GLYPH_POS(glyph, y, false) + x * kfb.pixel_bytes],
-				    (fb_font[glyph][y] & (1 << (7 - x))) ? true : false);
-				kfb.visual_mask(
-				    &kfb.glyphs[GLYPH_POS(glyph, y, true) + x * kfb.pixel_bytes],
-				    (fb_font[glyph][y] & (1 << (7 - x))) ? false : true);
-			}
-		}
-	}
-}
-
-errno_t kfb_init(void)
-{
-	sysarg_t present;
-	errno_t rc = sysinfo_get_value("fb", &present);
-	if (rc != EOK)
-		present = false;
-
-	if (!present)
-		return ENOENT;
-
-	sysarg_t kind;
-	rc = sysinfo_get_value("fb.kind", &kind);
-	if (rc != EOK)
-		kind = (sysarg_t) -1;
-
-	if (kind != 1)
-		return EINVAL;
-
-	sysarg_t paddr;
-	rc = sysinfo_get_value("fb.address.physical", &paddr);
-	if (rc != EOK)
-		return rc;
-
-	sysarg_t offset;
-	rc = sysinfo_get_value("fb.offset", &offset);
-	if (rc != EOK)
-		offset = 0;
-
-	sysarg_t width;
-	rc = sysinfo_get_value("fb.width", &width);
-	if (rc != EOK)
-		return rc;
-
-	sysarg_t height;
-	rc = sysinfo_get_value("fb.height", &height);
-	if (rc != EOK)
-		return rc;
-
-	sysarg_t scanline;
-	rc = sysinfo_get_value("fb.scanline", &scanline);
-	if (rc != EOK)
-		return rc;
-
-	sysarg_t visual;
-	rc = sysinfo_get_value("fb.visual", &visual);
-	if (rc != EOK)
-		return rc;
-
-	kfb.width = width;
-	kfb.height = height;
-	kfb.offset = offset;
-	kfb.scanline = scanline;
-	kfb.visual = visual;
-
-	switch (visual) {
-	case VISUAL_INDIRECT_8:
-		kfb.pixel2visual = pixel2bgr_323;
-		kfb.visual2pixel = bgr_323_2pixel;
-		kfb.visual_mask = visual_mask_323;
-		kfb.pixel_bytes = 1;
-		break;
-	case VISUAL_RGB_5_5_5_LE:
-		kfb.pixel2visual = pixel2rgb_555_le;
-		kfb.visual2pixel = rgb_555_le_2pixel;
-		kfb.visual_mask = visual_mask_555;
-		kfb.pixel_bytes = 2;
-		break;
-	case VISUAL_RGB_5_5_5_BE:
-		kfb.pixel2visual = pixel2rgb_555_be;
-		kfb.visual2pixel = rgb_555_be_2pixel;
-		kfb.visual_mask = visual_mask_555;
-		kfb.pixel_bytes = 2;
-		break;
-	case VISUAL_RGB_5_6_5_LE:
-		kfb.pixel2visual = pixel2rgb_565_le;
-		kfb.visual2pixel = rgb_565_le_2pixel;
-		kfb.visual_mask = visual_mask_565;
-		kfb.pixel_bytes = 2;
-		break;
-	case VISUAL_RGB_5_6_5_BE:
-		kfb.pixel2visual = pixel2rgb_565_be;
-		kfb.visual2pixel = rgb_565_be_2pixel;
-		kfb.visual_mask = visual_mask_565;
-		kfb.pixel_bytes = 2;
-		break;
-	case VISUAL_RGB_8_8_8:
-		kfb.pixel2visual = pixel2rgb_888;
-		kfb.visual2pixel = rgb_888_2pixel;
-		kfb.visual_mask = visual_mask_888;
-		kfb.pixel_bytes = 3;
-		break;
-	case VISUAL_BGR_8_8_8:
-		kfb.pixel2visual = pixel2bgr_888;
-		kfb.visual2pixel = bgr_888_2pixel;
-		kfb.visual_mask = visual_mask_888;
-		kfb.pixel_bytes = 3;
-		break;
-	case VISUAL_RGB_8_8_8_0:
-		kfb.pixel2visual = pixel2rgb_8880;
-		kfb.visual2pixel = rgb_8880_2pixel;
-		kfb.visual_mask = visual_mask_8880;
-		kfb.pixel_bytes = 4;
-		break;
-	case VISUAL_RGB_0_8_8_8:
-		kfb.pixel2visual = pixel2rgb_0888;
-		kfb.visual2pixel = rgb_0888_2pixel;
-		kfb.visual_mask = visual_mask_0888;
-		kfb.pixel_bytes = 4;
-		break;
-	case VISUAL_BGR_0_8_8_8:
-		kfb.pixel2visual = pixel2bgr_0888;
-		kfb.visual2pixel = bgr_0888_2pixel;
-		kfb.visual_mask = visual_mask_0888;
-		kfb.pixel_bytes = 4;
-		break;
-	case VISUAL_BGR_8_8_8_0:
-		kfb.pixel2visual = pixel2bgr_8880;
-		kfb.visual2pixel = bgr_8880_2pixel;
-		kfb.visual_mask = visual_mask_8880;
-		kfb.pixel_bytes = 4;
-		break;
-	default:
-		return EINVAL;
-	}
-
-	kfb.glyph_scanline = FONT_WIDTH * kfb.pixel_bytes;
-	kfb.glyph_bytes = kfb.glyph_scanline * FONT_SCANLINES;
-
-	size_t sz = 2 * FONT_GLYPHS * kfb.glyph_bytes;
-	kfb.glyphs = (uint8_t *) malloc(sz);
-	if (kfb.glyphs == NULL)
-		return EINVAL;
-
-	render_glyphs(sz);
-
-	kfb.size = scanline * height;
-
-	rc = physmem_map((void *) paddr + offset,
-	    ALIGN_UP(kfb.size, PAGE_SIZE) >> PAGE_WIDTH,
-	    AS_AREA_READ | AS_AREA_WRITE, (void *) &kfb.addr);
-	if (rc != EOK) {
-		free(kfb.glyphs);
-		return rc;
-	}
-
-	kfb.pointer_x = 0;
-	kfb.pointer_y = 0;
-	kfb.pointer_visible = false;
-	kfb.pointer_imgmap = imgmap_create(POINTER_WIDTH, POINTER_HEIGHT,
-	    VISUAL_RGB_0_8_8_8, IMGMAP_FLAG_NONE);
-
-	kfb.backbuf = NULL;
-
-	fbdev_t *dev = fbdev_register(&kfb_ops, (void *) &kfb);
-	if (dev == NULL) {
-		free(kfb.glyphs);
-		as_area_destroy(kfb.addr);
-		return EINVAL;
-	}
-
-	return EOK;
-}
-
-/**
- * @}
- */
Index: pace/srv/hid/output/port/kfb.h
===================================================================
--- uspace/srv/hid/output/port/kfb.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ 	(revision )
@@ -1,41 +1,0 @@
-/*
- * Copyright (c) 2006 Ondrej Palkovsky
- * 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 output
- * @{
- */
-
-#ifndef FB_PORT_KFB_H_
-#define FB_PORT_KFB_H_
-
-extern errno_t kfb_init(void);
-
-#endif
-
-/** @}
- */
Index: uspace/srv/hid/output/proto/vt100.c
===================================================================
--- uspace/srv/hid/output/proto/vt100.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/output/proto/vt100.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2011 Martin Decky
  * All rights reserved.
@@ -65,5 +66,5 @@
 static sgr_color_index_t color_map[] = {
 	[COLOR_BLACK]   = CI_BLACK,
-	[COLOR_BLUE]    = CI_RED,
+	[COLOR_BLUE]    = CI_BLUE,
 	[COLOR_GREEN]   = CI_GREEN,
 	[COLOR_CYAN]    = CI_CYAN,
Index: uspace/srv/hid/remcons/meson.build
===================================================================
--- uspace/srv/hid/remcons/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/remcons/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 #
+# Copyright (c) 2021 Jiri Svoboda
 # Copyright (c) 2012 Vojtech Horky
 # All rights reserved.
@@ -27,3 +28,4 @@
 #
 
+deps = [ 'inet' ]
 src = files('remcons.c', 'user.c')
Index: uspace/srv/hid/rfb/main.c
===================================================================
--- uspace/srv/hid/rfb/main.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/rfb/main.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2013 Martin Sucha
  * All rights reserved.
@@ -50,4 +51,5 @@
 static errno_t rfb_ddev_get_info(void *, ddev_info_t *);
 
+static errno_t rfb_gc_set_clip_rect(void *, gfx_rect_t *);
 static errno_t rfb_gc_set_color(void *, gfx_color_t *);
 static errno_t rfb_gc_fill_rect(void *, gfx_rect_t *);
@@ -66,4 +68,6 @@
 	rfb_t rfb;
 	pixel_t color;
+	gfx_rect_t rect;
+	gfx_rect_t clip_rect;
 } rfb_gc_t;
 
@@ -78,4 +82,5 @@
 
 static gfx_context_ops_t rfb_gc_ops = {
+	.set_clip_rect = rfb_gc_set_clip_rect,
 	.set_color = rfb_gc_set_color,
 	.fill_rect = rfb_gc_fill_rect,
@@ -134,4 +139,49 @@
 }
 
+/** Create RFB GC.
+ *
+ * @param rrgb Place to store pointer to new RFB GC
+ * @return EOK on success, ENOMEM if out of memory
+ */
+static errno_t rgb_gc_create(rfb_gc_t **rrfb)
+{
+	rfb_gc_t *rfb;
+
+	rfb = calloc(1, sizeof(rfb_gc_t));
+	if (rfb == NULL)
+		return ENOMEM;
+
+	*rrfb = rfb;
+	return EOK;
+}
+
+/** Destroy RFB GC.
+ *
+ * @param rfb RFB GC
+ */
+static void rfb_gc_destroy(rfb_gc_t *rfb)
+{
+	free(rfb);
+}
+
+/** Set clipping rectangle on RFB.
+ *
+ * @param arg RFB
+ * @param rect Rectangle or @c NULL
+ *
+ * @return EOK on success or an error code
+ */
+static errno_t rfb_gc_set_clip_rect(void *arg, gfx_rect_t *rect)
+{
+	rfb_gc_t *rfb = (rfb_gc_t *) arg;
+
+	if (rect != NULL)
+		gfx_rect_clip(rect, &rfb->rect, &rfb->clip_rect);
+	else
+		rfb->clip_rect = rfb->rect;
+
+	return EOK;
+}
+
 /** Set color on RFB.
  *
@@ -163,10 +213,11 @@
 {
 	rfb_gc_t *rfb = (rfb_gc_t *) arg;
+	gfx_rect_t crect;
 	gfx_coord_t x, y;
 
-	// XXX We should handle p0.x > p1.x and p0.y > p1.y
-
-	for (y = rect->p0.y; y < rect->p1.y; y++) {
-		for (x = rect->p0.x; x < rect->p1.x; x++) {
+	gfx_rect_clip(rect, &rfb->clip_rect, &crect);
+
+	for (y = crect.p0.y; y < crect.p1.y; y++) {
+		for (x = crect.p0.x; x < crect.p1.x; x++) {
 			pixelmap_put_pixel(&rfb->rfb.framebuffer, x, y,
 			    rfb->color);
@@ -174,5 +225,5 @@
 	}
 
-	rfb_gc_invalidate_rect(rfb, rect);
+	rfb_gc_invalidate_rect(rfb, &crect);
 
 	return EOK;
@@ -258,4 +309,5 @@
 	gfx_rect_t srect;
 	gfx_rect_t drect;
+	gfx_rect_t crect;
 	gfx_coord2_t offs;
 	gfx_coord2_t bmdim;
@@ -279,5 +331,6 @@
 	/* Destination rectangle */
 	gfx_rect_translate(&offs, &srect, &drect);
-	gfx_coord2_subtract(&drect.p1, &drect.p0, &dim);
+	gfx_rect_clip(&drect, &rfbbm->rfb->clip_rect, &crect);
+	gfx_coord2_subtract(&crect.p1, &crect.p0, &dim);
 	gfx_coord2_subtract(&rfbbm->rect.p1, &rfbbm->rect.p0, &bmdim);
 
@@ -320,5 +373,5 @@
 	}
 
-	rfb_gc_invalidate_rect(rfbbm->rfb, &drect);
+	rfb_gc_invalidate_rect(rfbbm->rfb, &crect);
 
 	return EOK;
@@ -346,4 +399,5 @@
 {
 	rfb_t *rfb = (rfb_t *) arg;
+	rfb_gc_t *rfbgc;
 	ddev_srv_t srv;
 	sysarg_t svc_id;
@@ -362,6 +416,19 @@
 		ddev_conn(icall, &srv);
 	} else {
-		rc = gfx_context_new(&rfb_gc_ops, (void *) rfb, &gc);
+		rc = rgb_gc_create(&rfbgc);
 		if (rc != EOK) {
+			async_answer_0(icall, ENOMEM);
+			return;
+		}
+
+		rfbgc->rect.p0.x = 0;
+		rfbgc->rect.p0.y = 0;
+		rfbgc->rect.p1.x = rfb->width;
+		rfbgc->rect.p1.y = rfb->height;
+		rfbgc->clip_rect = rfbgc->rect;
+
+		rc = gfx_context_new(&rfb_gc_ops, (void *) rfbgc, &gc);
+		if (rc != EOK) {
+			rfb_gc_destroy(rfbgc);
 			async_answer_0(icall, ENOMEM);
 			return;
Index: uspace/srv/hid/rfb/meson.build
===================================================================
--- uspace/srv/hid/rfb/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hid/rfb/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 #
+# Copyright (c) 2021 Jiri Svoboda
 # Copyright (c) 2013 Martin Sucha
 # All rights reserved.
@@ -27,4 +28,4 @@
 #
 
-deps = [ 'ipcgfx', 'ddev' ]
+deps = [ 'inet', 'ipcgfx', 'ddev' ]
 src = files('main.c', 'rfb.c')
Index: uspace/srv/hw/char/s3c24xx_uart/meson.build
===================================================================
--- uspace/srv/hw/char/s3c24xx_uart/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/hw/char/s3c24xx_uart/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -28,3 +28,4 @@
 #
 
+deps = [ 'device' ]
 src = files('s3c24xx_uart.c')
Index: uspace/srv/net/dhcp/dhcp.c
===================================================================
--- uspace/srv/net/dhcp/dhcp.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/dhcp/dhcp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2013 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -42,4 +42,5 @@
 #include <fibril_synch.h>
 #include <inet/addr.h>
+#include <inet/eth_addr.h>
 #include <inet/dnsr.h>
 #include <inet/inetcfg.h>
@@ -157,9 +158,9 @@
 	hdr->op = op_bootrequest;
 	hdr->htype = 1; /* AHRD_ETHERNET */
-	hdr->hlen = sizeof(addr48_t);
+	hdr->hlen = ETH_ADDR_SIZE;
 	hdr->xid = host2uint32_t_be(42);
 	hdr->flags = flag_broadcast;
 
-	addr48(dlink->link_info.mac_addr, hdr->chaddr);
+	eth_addr_encode(&dlink->link_info.mac_addr, hdr->chaddr);
 	hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
 
@@ -185,5 +186,5 @@
 	hdr->flags = flag_broadcast;
 	hdr->ciaddr = host2uint32_t_be(offer->oaddr.addr);
-	addr48(dlink->link_info.mac_addr, hdr->chaddr);
+	eth_addr_encode(&dlink->link_info.mac_addr, hdr->chaddr);
 	hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
 
Index: uspace/srv/net/dhcp/meson.build
===================================================================
--- uspace/srv/net/dhcp/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/dhcp/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2013 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,4 +27,5 @@
 #
 
+deps = [ 'inet' ]
 src = files(
 	'dhcp.c',
Index: uspace/srv/net/dnsrsrv/meson.build
===================================================================
--- uspace/srv/net/dnsrsrv/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/dnsrsrv/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2012 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,4 +27,5 @@
 #
 
+deps = [ 'inet' ]
 src = files(
 	'dns_msg.c',
Index: uspace/srv/net/ethip/arp.c
===================================================================
--- uspace/srv/net/ethip/arp.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/ethip/arp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -36,7 +36,8 @@
 
 #include <errno.h>
-#include <io/log.h>
 #include <inet/iplink_srv.h>
 #include <inet/addr.h>
+#include <inet/eth_addr.h>
+#include <io/log.h>
 #include <stdlib.h>
 #include "arp.h"
@@ -79,5 +80,5 @@
 
 	(void) atrans_add(packet.sender_proto_addr,
-	    packet.sender_hw_addr);
+	    &packet.sender_hw_addr);
 
 	if (packet.opcode == aop_request) {
@@ -85,7 +86,7 @@
 
 		reply.opcode = aop_reply;
-		addr48(nic->mac_addr, reply.sender_hw_addr);
+		reply.sender_hw_addr = nic->mac_addr;
 		reply.sender_proto_addr = laddr_v4;
-		addr48(packet.sender_hw_addr, reply.target_hw_addr);
+		reply.target_hw_addr = packet.sender_hw_addr;
 		reply.target_proto_addr = packet.sender_proto_addr;
 
@@ -95,9 +96,9 @@
 
 errno_t arp_translate(ethip_nic_t *nic, addr32_t src_addr, addr32_t ip_addr,
-    addr48_t mac_addr)
+    eth_addr_t *mac_addr)
 {
 	/* Broadcast address */
 	if (ip_addr == addr32_broadcast_all_hosts) {
-		addr48(addr48_broadcast, mac_addr);
+		*mac_addr = eth_addr_broadcast;
 		return EOK;
 	}
@@ -110,7 +111,7 @@
 
 	packet.opcode = aop_request;
-	addr48(nic->mac_addr, packet.sender_hw_addr);
+	packet.sender_hw_addr = nic->mac_addr;
 	packet.sender_proto_addr = src_addr;
-	addr48(addr48_broadcast, packet.target_hw_addr);
+	packet.target_hw_addr = eth_addr_broadcast;
 	packet.target_proto_addr = ip_addr;
 
@@ -138,6 +139,6 @@
 		return rc;
 
-	addr48(packet->target_hw_addr, frame.dest);
-	addr48(packet->sender_hw_addr, frame.src);
+	frame.dest = packet->target_hw_addr;
+	frame.src = packet->sender_hw_addr;
 	frame.etype_len = ETYPE_ARP;
 	frame.data = pdata;
Index: uspace/srv/net/ethip/arp.h
===================================================================
--- uspace/srv/net/ethip/arp.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/ethip/arp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,10 +38,11 @@
 #define ARP_H_
 
+#include <inet/addr.h>
+#include <inet/eth_addr.h>
 #include <inet/iplink_srv.h>
-#include <inet/addr.h>
 #include "ethip.h"
 
 extern void arp_received(ethip_nic_t *, eth_frame_t *);
-extern errno_t arp_translate(ethip_nic_t *, addr32_t, addr32_t, addr48_t);
+extern errno_t arp_translate(ethip_nic_t *, addr32_t, addr32_t, eth_addr_t *);
 
 #endif
Index: uspace/srv/net/ethip/atrans.c
===================================================================
--- uspace/srv/net/ethip/atrans.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/ethip/atrans.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,4 +38,5 @@
 #include <errno.h>
 #include <fibril_synch.h>
+#include <inet/eth_addr.h>
 #include <inet/iplink_srv.h>
 #include <stdlib.h>
@@ -59,5 +60,5 @@
 }
 
-errno_t atrans_add(addr32_t ip_addr, addr48_t mac_addr)
+errno_t atrans_add(addr32_t ip_addr, eth_addr_t *mac_addr)
 {
 	ethip_atrans_t *atrans;
@@ -69,5 +70,5 @@
 
 	atrans->ip_addr = ip_addr;
-	addr48(mac_addr, atrans->mac_addr);
+	atrans->mac_addr = *mac_addr;
 
 	fibril_mutex_lock(&atrans_list_lock);
@@ -103,5 +104,5 @@
 }
 
-static errno_t atrans_lookup_locked(addr32_t ip_addr, addr48_t mac_addr)
+static errno_t atrans_lookup_locked(addr32_t ip_addr, eth_addr_t *mac_addr)
 {
 	ethip_atrans_t *atrans = atrans_find(ip_addr);
@@ -109,9 +110,9 @@
 		return ENOENT;
 
-	addr48(atrans->mac_addr, mac_addr);
+	*mac_addr = atrans->mac_addr;
 	return EOK;
 }
 
-errno_t atrans_lookup(addr32_t ip_addr, addr48_t mac_addr)
+errno_t atrans_lookup(addr32_t ip_addr, eth_addr_t *mac_addr)
 {
 	errno_t rc;
@@ -135,5 +136,5 @@
 
 errno_t atrans_lookup_timeout(addr32_t ip_addr, usec_t timeout,
-    addr48_t mac_addr)
+    eth_addr_t *mac_addr)
 {
 	fibril_timer_t *t;
Index: uspace/srv/net/ethip/atrans.h
===================================================================
--- uspace/srv/net/ethip/atrans.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/ethip/atrans.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,12 +38,13 @@
 #define ATRANS_H_
 
+#include <inet/addr.h>
+#include <inet/eth_addr.h>
 #include <inet/iplink_srv.h>
-#include <inet/addr.h>
 #include "ethip.h"
 
-extern errno_t atrans_add(addr32_t, addr48_t);
+extern errno_t atrans_add(addr32_t, eth_addr_t *);
 extern errno_t atrans_remove(addr32_t);
-extern errno_t atrans_lookup(addr32_t, addr48_t);
-extern errno_t atrans_lookup_timeout(addr32_t, usec_t, addr48_t);
+extern errno_t atrans_lookup(addr32_t, eth_addr_t *);
+extern errno_t atrans_lookup_timeout(addr32_t, usec_t, eth_addr_t *);
 
 #endif
Index: uspace/srv/net/ethip/ethip.c
===================================================================
--- uspace/srv/net/ethip/ethip.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/ethip/ethip.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,4 +39,5 @@
 #include <async.h>
 #include <errno.h>
+#include <inet/eth_addr.h>
 #include <inet/iplink_srv.h>
 #include <io/log.h>
@@ -58,6 +59,6 @@
 static errno_t ethip_send6(iplink_srv_t *srv, iplink_sdu6_t *sdu);
 static errno_t ethip_get_mtu(iplink_srv_t *srv, size_t *mtu);
-static errno_t ethip_get_mac48(iplink_srv_t *srv, addr48_t *mac);
-static errno_t ethip_set_mac48(iplink_srv_t *srv, addr48_t *mac);
+static errno_t ethip_get_mac48(iplink_srv_t *srv, eth_addr_t *mac);
+static errno_t ethip_set_mac48(iplink_srv_t *srv, eth_addr_t *mac);
 static errno_t ethip_addr_add(iplink_srv_t *srv, inet_addr_t *addr);
 static errno_t ethip_addr_remove(iplink_srv_t *srv, inet_addr_t *addr);
@@ -177,5 +178,5 @@
 	eth_frame_t frame;
 
-	errno_t rc = arp_translate(nic, sdu->src, sdu->dest, frame.dest);
+	errno_t rc = arp_translate(nic, sdu->src, sdu->dest, &frame.dest);
 	if (rc != EOK) {
 		log_msg(LOG_DEFAULT, LVL_WARN, "Failed to look up IPv4 address 0x%"
@@ -184,5 +185,5 @@
 	}
 
-	addr48(nic->mac_addr, frame.src);
+	frame.src = nic->mac_addr;
 	frame.etype_len = ETYPE_IP;
 	frame.data = sdu->data;
@@ -208,6 +209,6 @@
 	eth_frame_t frame;
 
-	addr48(sdu->dest, frame.dest);
-	addr48(nic->mac_addr, frame.src);
+	frame.dest = sdu->dest;
+	frame.src = nic->mac_addr;
 	frame.etype_len = ETYPE_IPV6;
 	frame.data = sdu->data;
@@ -276,20 +277,20 @@
 }
 
-static errno_t ethip_get_mac48(iplink_srv_t *srv, addr48_t *mac)
+static errno_t ethip_get_mac48(iplink_srv_t *srv, eth_addr_t *mac)
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_get_mac48()");
 
 	ethip_nic_t *nic = (ethip_nic_t *) srv->arg;
-	addr48(nic->mac_addr, *mac);
-
-	return EOK;
-}
-
-static errno_t ethip_set_mac48(iplink_srv_t *srv, addr48_t *mac)
+	*mac = nic->mac_addr;
+
+	return EOK;
+}
+
+static errno_t ethip_set_mac48(iplink_srv_t *srv, eth_addr_t *mac)
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "ethip_set_mac48()");
 
 	ethip_nic_t *nic = (ethip_nic_t *) srv->arg;
-	addr48(*mac, nic->mac_addr);
+	nic->mac_addr = *mac;
 
 	return EOK;
Index: uspace/srv/net/ethip/ethip.h
===================================================================
--- uspace/srv/net/ethip/ethip.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/ethip/ethip.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,6 +40,7 @@
 #include <adt/list.h>
 #include <async.h>
+#include <inet/addr.h>
+#include <inet/eth_addr.h>
 #include <inet/iplink_srv.h>
-#include <inet/addr.h>
 #include <loc.h>
 #include <stddef.h>
@@ -61,5 +62,5 @@
 
 	/** MAC address */
-	addr48_t mac_addr;
+	eth_addr_t mac_addr;
 
 	/**
@@ -73,7 +74,7 @@
 typedef struct {
 	/** Destination Address */
-	addr48_t dest;
+	eth_addr_t dest;
 	/** Source Address */
-	addr48_t src;
+	eth_addr_t src;
 	/** Ethertype or Length */
 	uint16_t etype_len;
@@ -100,9 +101,9 @@
 	arp_opcode_t opcode;
 	/** Sender hardware address */
-	addr48_t sender_hw_addr;
+	eth_addr_t sender_hw_addr;
 	/** Sender protocol address */
 	addr32_t sender_proto_addr;
 	/** Target hardware address */
-	addr48_t target_hw_addr;
+	eth_addr_t target_hw_addr;
 	/** Target protocol address */
 	addr32_t target_proto_addr;
@@ -113,5 +114,5 @@
 	link_t atrans_list;
 	addr32_t ip_addr;
-	addr48_t mac_addr;
+	eth_addr_t mac_addr;
 } ethip_atrans_t;
 
Index: uspace/srv/net/ethip/ethip_nic.c
===================================================================
--- uspace/srv/net/ethip/ethip_nic.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/ethip/ethip_nic.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -37,14 +37,15 @@
 #include <adt/list.h>
 #include <async.h>
-#include <stdbool.h>
 #include <errno.h>
-#include <str_error.h>
 #include <fibril_synch.h>
+#include <inet/eth_addr.h>
 #include <inet/iplink_srv.h>
 #include <io/log.h>
 #include <loc.h>
+#include <mem.h>
 #include <nic_iface.h>
+#include <stdbool.h>
 #include <stdlib.h>
-#include <mem.h>
+#include <str_error.h>
 #include "ethip.h"
 #include "ethip_nic.h"
@@ -193,5 +194,5 @@
 	}
 
-	addr48(nic_address.address, nic->mac_addr);
+	eth_addr_decode(nic_address.address, &nic->mac_addr);
 
 	rc = nic_set_state(nic->sess, NIC_STATE_ACTIVE);
@@ -399,6 +400,6 @@
 		assert(i < count);
 
-		addr48_t mac;
-		addr48_solicited_node(v6, mac);
+		eth_addr_t mac;
+		eth_addr_solicited_node(v6, &mac);
 
 		/* Avoid duplicate addresses in the list */
@@ -407,5 +408,7 @@
 
 		for (size_t j = 0; j < i; j++) {
-			if (addr48_compare(mac_list[j].address, mac)) {
+			eth_addr_t mac_entry;
+			eth_addr_decode(mac_list[j].address, &mac_entry);
+			if (eth_addr_compare(&mac_entry, &mac)) {
 				found = true;
 				break;
@@ -414,8 +417,9 @@
 
 		if (!found) {
-			addr48(mac, mac_list[i].address);
+			eth_addr_encode(&mac, mac_list[i].address);
 			i++;
-		} else
+		} else {
 			count--;
+		}
 	}
 
Index: uspace/srv/net/ethip/pdu.c
===================================================================
--- uspace/srv/net/ethip/pdu.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/ethip/pdu.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -60,6 +60,6 @@
 
 	hdr = (eth_header_t *)data;
-	addr48(frame->src, hdr->src);
-	addr48(frame->dest, hdr->dest);
+	eth_addr_encode(&frame->src, hdr->src);
+	eth_addr_encode(&frame->dest, hdr->dest);
 	hdr->etype_len = host2uint16_t_be(frame->etype_len);
 
@@ -93,6 +93,6 @@
 		return ENOMEM;
 
-	addr48(hdr->src, frame->src);
-	addr48(hdr->dest, frame->dest);
+	eth_addr_decode(hdr->src, &frame->src);
+	eth_addr_decode(hdr->dest, &frame->dest);
 	frame->etype_len = uint16_t_be2host(hdr->etype_len);
 
@@ -140,8 +140,8 @@
 	pfmt->proto_addr_size = IPV4_ADDR_SIZE;
 	pfmt->opcode = host2uint16_t_be(fopcode);
-	addr48(packet->sender_hw_addr, pfmt->sender_hw_addr);
+	eth_addr_encode(&packet->sender_hw_addr, pfmt->sender_hw_addr);
 	pfmt->sender_proto_addr =
 	    host2uint32_t_be(packet->sender_proto_addr);
-	addr48(packet->target_hw_addr, pfmt->target_hw_addr);
+	eth_addr_encode(&packet->target_hw_addr, pfmt->target_hw_addr);
 	pfmt->target_proto_addr =
 	    host2uint32_t_be(packet->target_proto_addr);
@@ -203,8 +203,8 @@
 	}
 
-	addr48(pfmt->sender_hw_addr, packet->sender_hw_addr);
+	eth_addr_decode(pfmt->sender_hw_addr, &packet->sender_hw_addr);
 	packet->sender_proto_addr =
 	    uint32_t_be2host(pfmt->sender_proto_addr);
-	addr48(pfmt->target_hw_addr, packet->target_hw_addr);
+	eth_addr_decode(pfmt->target_hw_addr, &packet->target_hw_addr);
 	packet->target_proto_addr =
 	    uint32_t_be2host(pfmt->target_proto_addr);
Index: uspace/srv/net/ethip/pdu.h
===================================================================
--- uspace/srv/net/ethip/pdu.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/ethip/pdu.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
Index: uspace/srv/net/ethip/std.h
===================================================================
--- uspace/srv/net/ethip/std.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/ethip/std.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -39,5 +39,4 @@
 
 #include <stdint.h>
-#include <inet/addr.h>
 
 #define ETH_ADDR_SIZE       6
@@ -48,7 +47,7 @@
 typedef struct {
 	/** Destination Address */
-	addr48_t dest;
+	uint8_t dest[ETH_ADDR_SIZE];
 	/** Source Address */
-	addr48_t src;
+	uint8_t src[ETH_ADDR_SIZE];
 	/** Ethertype or Length */
 	uint16_t etype_len;
@@ -68,11 +67,11 @@
 	uint16_t opcode;
 	/** Sender hardware address */
-	addr48_t sender_hw_addr;
+	uint8_t sender_hw_addr[ETH_ADDR_SIZE];
 	/** Sender protocol address */
-	addr32_t sender_proto_addr;
+	uint32_t sender_proto_addr;
 	/** Target hardware address */
-	addr48_t target_hw_addr;
+	uint8_t target_hw_addr[ETH_ADDR_SIZE];
 	/** Target protocol address */
-	addr32_t target_proto_addr;
+	uint32_t target_proto_addr;
 } __attribute__((packed)) arp_eth_packet_fmt_t;
 
Index: uspace/srv/net/inetsrv/addrobj.c
===================================================================
--- uspace/srv/net/inetsrv/addrobj.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/inetsrv/addrobj.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,4 +38,5 @@
 #include <errno.h>
 #include <fibril_synch.h>
+#include <inet/eth_addr.h>
 #include <io/log.h>
 #include <ipc/loc.h>
@@ -229,5 +230,5 @@
 
 	errno_t rc;
-	addr48_t ldest_mac;
+	eth_addr_t ldest_mac;
 
 	switch (ldest_ver) {
@@ -239,9 +240,9 @@
 		 * Translate local destination IPv6 address.
 		 */
-		rc = ndp_translate(lsrc_v6, ldest_v6, ldest_mac, addr->ilink);
+		rc = ndp_translate(lsrc_v6, ldest_v6, &ldest_mac, addr->ilink);
 		if (rc != EOK)
 			return rc;
 
-		return inet_link_send_dgram6(addr->ilink, ldest_mac, dgram,
+		return inet_link_send_dgram6(addr->ilink, &ldest_mac, dgram,
 		    proto, ttl, df);
 	default:
Index: uspace/srv/net/inetsrv/inet_link.c
===================================================================
--- uspace/srv/net/inetsrv/inet_link.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/inetsrv/inet_link.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -35,13 +35,14 @@
  */
 
-#include <stdbool.h>
 #include <errno.h>
-#include <str_error.h>
 #include <fibril_synch.h>
+#include <inet/eth_addr.h>
 #include <inet/iplink.h>
 #include <io/log.h>
 #include <loc.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <str.h>
+#include <str_error.h>
 #include "addrobj.h"
 #include "inetsrv.h"
@@ -56,5 +57,5 @@
 
 static errno_t inet_iplink_recv(iplink_t *, iplink_recv_sdu_t *, ip_ver_t);
-static errno_t inet_iplink_change_addr(iplink_t *, addr48_t);
+static errno_t inet_iplink_change_addr(iplink_t *, eth_addr_t *);
 static inet_link_t *inet_link_get_by_id_locked(sysarg_t);
 
@@ -70,15 +71,18 @@
     { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xfe, 0, 0, 0 };
 
-static void inet_link_local_node_ip(addr48_t mac_addr,
+static void inet_link_local_node_ip(eth_addr_t *mac_addr,
     addr128_t ip_addr)
 {
+	uint8_t b[ETH_ADDR_SIZE];
+
 	memcpy(ip_addr, link_local_node_ip, 16);
-
-	ip_addr[8] = mac_addr[0] ^ 0x02;
-	ip_addr[9] = mac_addr[1];
-	ip_addr[10] = mac_addr[2];
-	ip_addr[13] = mac_addr[3];
-	ip_addr[14] = mac_addr[4];
-	ip_addr[15] = mac_addr[5];
+	eth_addr_encode(mac_addr, b);
+
+	ip_addr[8] = b[0] ^ 0x02;
+	ip_addr[9] = b[1];
+	ip_addr[10] = b[2];
+	ip_addr[13] = b[3];
+	ip_addr[14] = b[4];
+	ip_addr[15] = b[5];
 }
 
@@ -121,13 +125,15 @@
 }
 
-static errno_t inet_iplink_change_addr(iplink_t *iplink, addr48_t mac)
-{
+static errno_t inet_iplink_change_addr(iplink_t *iplink, eth_addr_t *mac)
+{
+	eth_addr_str_t saddr;
+
+	eth_addr_format(mac, &saddr);
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_iplink_change_addr(): "
-	    "new addr=%02x:%02x:%02x:%02x:%02x:%02x",
-	    mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	    "new addr=%s", saddr.str);
 
 	list_foreach(inet_links, link_list, inet_link_t, ilink) {
 		if (ilink->sess == iplink->sess)
-			memcpy(&ilink->mac, mac, sizeof(addr48_t));
+			ilink->mac = *mac;
 	}
 
@@ -261,5 +267,5 @@
 
 		addr128_t link_local;
-		inet_link_local_node_ip(ilink->mac, link_local);
+		inet_link_local_node_ip(&ilink->mac, link_local);
 
 		inet_naddr_set6(link_local, 64, &addr6->naddr);
@@ -387,5 +393,5 @@
  *
  */
-errno_t inet_link_send_dgram6(inet_link_t *ilink, addr48_t ldest,
+errno_t inet_link_send_dgram6(inet_link_t *ilink, eth_addr_t *ldest,
     inet_dgram_t *dgram, uint8_t proto, uint8_t ttl, int df)
 {
@@ -401,5 +407,5 @@
 
 	iplink_sdu6_t sdu6;
-	addr48(ldest, sdu6.dest);
+	sdu6.dest = *ldest;
 
 	/*
Index: uspace/srv/net/inetsrv/inet_link.h
===================================================================
--- uspace/srv/net/inetsrv/inet_link.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/inetsrv/inet_link.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -38,4 +38,5 @@
 #define INET_LINK_H_
 
+#include <inet/eth_addr.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -45,5 +46,5 @@
 extern errno_t inet_link_send_dgram(inet_link_t *, addr32_t,
     addr32_t, inet_dgram_t *, uint8_t, uint8_t, int);
-extern errno_t inet_link_send_dgram6(inet_link_t *, addr48_t, inet_dgram_t *,
+extern errno_t inet_link_send_dgram6(inet_link_t *, eth_addr_t *, inet_dgram_t *,
     uint8_t, uint8_t, int);
 extern inet_link_t *inet_link_get_by_id(sysarg_t);
Index: uspace/srv/net/inetsrv/inetcfg.c
===================================================================
--- uspace/srv/net/inetsrv/inetcfg.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/inetsrv/inetcfg.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2013 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -177,7 +177,7 @@
 	linfo->def_mtu = ilink->def_mtu;
 	if (ilink->mac_valid) {
-		addr48(ilink->mac, linfo->mac_addr);
+		linfo->mac_addr = ilink->mac;
 	} else {
-		memset(linfo->mac_addr, 0, sizeof(linfo->mac_addr));
+		memset(&linfo->mac_addr, 0, sizeof(linfo->mac_addr));
 	}
 
Index: uspace/srv/net/inetsrv/inetsrv.h
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/inetsrv/inetsrv.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,4 +41,5 @@
 #include <stdbool.h>
 #include <inet/addr.h>
+#include <inet/eth_addr.h>
 #include <inet/iplink.h>
 #include <ipc/loc.h>
@@ -109,5 +110,5 @@
 	iplink_t *iplink;
 	size_t def_mtu;
-	addr48_t mac;
+	eth_addr_t mac;
 	bool mac_valid;
 } inet_link_t;
Index: uspace/srv/net/inetsrv/meson.build
===================================================================
--- uspace/srv/net/inetsrv/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/inetsrv/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2012 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,4 +27,5 @@
 #
 
+deps = [ 'inet' ]
 src = files(
 	'addrobj.c',
Index: uspace/srv/net/inetsrv/ndp.c
===================================================================
--- uspace/srv/net/inetsrv/ndp.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/inetsrv/ndp.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2013 Antonin Steinhauser
  * All rights reserved.
@@ -36,7 +37,8 @@
 
 #include <errno.h>
+#include <inet/eth_addr.h>
+#include <io/log.h>
 #include <mem.h>
 #include <stdlib.h>
-#include <io/log.h>
 #include "ntrans.h"
 #include "addrobj.h"
@@ -69,5 +71,5 @@
 	ndp_pdu_encode(packet, &dgram);
 
-	inet_link_send_dgram6(link, packet->target_hw_addr, &dgram,
+	inet_link_send_dgram6(link, &packet->target_hw_addr, &dgram,
 	    IP_PROTO_ICMPV6, INET6_HOP_LIMIT_MAX, 0);
 
@@ -108,5 +110,5 @@
 		if (laddr != NULL) {
 			rc = ntrans_add(packet.sender_proto_addr,
-			    packet.sender_hw_addr);
+			    &packet.sender_hw_addr);
 			if (rc != EOK)
 				return rc;
@@ -115,7 +117,7 @@
 
 			reply.opcode = ICMPV6_NEIGHBOUR_ADVERTISEMENT;
-			addr48(laddr->ilink->mac, reply.sender_hw_addr);
+			reply.sender_hw_addr = laddr->ilink->mac;
 			addr128(packet.target_proto_addr, reply.sender_proto_addr);
-			addr48(packet.sender_hw_addr, reply.target_hw_addr);
+			reply.target_hw_addr = packet.sender_hw_addr;
 			addr128(packet.sender_proto_addr, reply.target_proto_addr);
 
@@ -128,5 +130,5 @@
 		if (laddr != NULL)
 			return ntrans_add(packet.sender_proto_addr,
-			    packet.sender_hw_addr);
+			    &packet.sender_hw_addr);
 
 		break;
@@ -151,5 +153,5 @@
  *
  */
-errno_t ndp_translate(addr128_t src_addr, addr128_t ip_addr, addr48_t mac_addr,
+errno_t ndp_translate(addr128_t src_addr, addr128_t ip_addr, eth_addr_t *mac_addr,
     inet_link_t *ilink)
 {
@@ -167,8 +169,8 @@
 
 	packet.opcode = ICMPV6_NEIGHBOUR_SOLICITATION;
-	addr48(ilink->mac, packet.sender_hw_addr);
+	packet.sender_hw_addr = ilink->mac;
 	addr128(src_addr, packet.sender_proto_addr);
 	addr128(ip_addr, packet.solicited_ip);
-	addr48_solicited_node(ip_addr, packet.target_hw_addr);
+	eth_addr_solicited_node(ip_addr, &packet.target_hw_addr);
 	ndp_solicited_node_ip(ip_addr, packet.target_proto_addr);
 
Index: uspace/srv/net/inetsrv/ndp.h
===================================================================
--- uspace/srv/net/inetsrv/ndp.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/inetsrv/ndp.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2013 Antonin Steinhauser
  * All rights reserved.
@@ -39,4 +40,5 @@
 
 #include <inet/addr.h>
+#include <inet/eth_addr.h>
 #include "inetsrv.h"
 #include "icmpv6_std.h"
@@ -52,9 +54,9 @@
 	ndp_opcode_t opcode;
 	/** Sender hardware address */
-	addr48_t sender_hw_addr;
+	eth_addr_t sender_hw_addr;
 	/** Sender protocol address */
 	addr128_t sender_proto_addr;
 	/** Target hardware address */
-	addr48_t target_hw_addr;
+	eth_addr_t target_hw_addr;
 	/** Target protocol address */
 	addr128_t target_proto_addr;
@@ -64,5 +66,5 @@
 
 extern errno_t ndp_received(inet_dgram_t *);
-extern errno_t ndp_translate(addr128_t, addr128_t, addr48_t, inet_link_t *);
+extern errno_t ndp_translate(addr128_t, addr128_t, eth_addr_t *, inet_link_t *);
 
 #endif
Index: uspace/srv/net/inetsrv/ntrans.c
===================================================================
--- uspace/srv/net/inetsrv/ntrans.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/inetsrv/ntrans.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2013 Antonin Steinhauser
  * All rights reserved.
@@ -38,4 +39,5 @@
 #include <errno.h>
 #include <fibril_synch.h>
+#include <inet/eth_addr.h>
 #include <inet/iplink_srv.h>
 #include <stdlib.h>
@@ -73,5 +75,5 @@
  *
  */
-errno_t ntrans_add(addr128_t ip_addr, addr48_t mac_addr)
+errno_t ntrans_add(addr128_t ip_addr, eth_addr_t *mac_addr)
 {
 	inet_ntrans_t *ntrans;
@@ -83,5 +85,5 @@
 
 	addr128(ip_addr, ntrans->ip_addr);
-	addr48(mac_addr, ntrans->mac_addr);
+	ntrans->mac_addr = *mac_addr;
 
 	fibril_mutex_lock(&ntrans_list_lock);
@@ -134,5 +136,5 @@
  *
  */
-errno_t ntrans_lookup(addr128_t ip_addr, addr48_t mac_addr)
+errno_t ntrans_lookup(addr128_t ip_addr, eth_addr_t *mac_addr)
 {
 	fibril_mutex_lock(&ntrans_list_lock);
@@ -144,5 +146,5 @@
 
 	fibril_mutex_unlock(&ntrans_list_lock);
-	addr48(ntrans->mac_addr, mac_addr);
+	*mac_addr = ntrans->mac_addr;
 	return EOK;
 }
Index: uspace/srv/net/inetsrv/ntrans.h
===================================================================
--- uspace/srv/net/inetsrv/ntrans.h	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/inetsrv/ntrans.h	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2013 Antonin Steinhauser
  * All rights reserved.
@@ -38,6 +39,7 @@
 #define NTRANS_H_
 
+#include <inet/addr.h>
+#include <inet/eth_addr.h>
 #include <inet/iplink_srv.h>
-#include <inet/addr.h>
 
 /** Address translation table element */
@@ -45,10 +47,10 @@
 	link_t ntrans_list;
 	addr128_t ip_addr;
-	addr48_t mac_addr;
+	eth_addr_t mac_addr;
 } inet_ntrans_t;
 
-extern errno_t ntrans_add(addr128_t, addr48_t);
+extern errno_t ntrans_add(addr128_t, eth_addr_t *);
 extern errno_t ntrans_remove(addr128_t);
-extern errno_t ntrans_lookup(addr128_t, addr48_t);
+extern errno_t ntrans_lookup(addr128_t, eth_addr_t *);
 extern errno_t ntrans_wait_timeout(usec_t);
 
Index: uspace/srv/net/inetsrv/pdu.c
===================================================================
--- uspace/srv/net/inetsrv/pdu.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/inetsrv/pdu.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,4 +40,5 @@
 #include <errno.h>
 #include <fibril_synch.h>
+#include <inet/eth_addr.h>
 #include <io/log.h>
 #include <macros.h>
@@ -504,5 +505,5 @@
 
 	message->length = 1;
-	addr48(ndp->sender_hw_addr, message->mac);
+	eth_addr_encode(&ndp->sender_hw_addr, message->mac);
 
 	icmpv6_phdr_t phdr;
@@ -552,5 +553,5 @@
 
 	addr128_t_be2host(message->target_address, ndp->target_proto_addr);
-	addr48(message->mac, ndp->sender_hw_addr);
+	eth_addr_decode(message->mac, &ndp->sender_hw_addr);
 
 	return EOK;
Index: uspace/srv/net/loopip/loopip.c
===================================================================
--- uspace/srv/net/loopip/loopip.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/loopip/loopip.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2012 Jiri Svoboda
+ * Copyright (c) 2021 Jiri Svoboda
  * All rights reserved.
  *
@@ -41,4 +41,5 @@
 #include <inet/iplink_srv.h>
 #include <inet/addr.h>
+#include <inet/eth_addr.h>
 #include <io/log.h>
 #include <loc.h>
@@ -55,5 +56,5 @@
 static errno_t loopip_send6(iplink_srv_t *srv, iplink_sdu6_t *sdu);
 static errno_t loopip_get_mtu(iplink_srv_t *srv, size_t *mtu);
-static errno_t loopip_get_mac48(iplink_srv_t *srv, addr48_t *mac);
+static errno_t loopip_get_mac48(iplink_srv_t *srv, eth_addr_t *mac);
 static errno_t loopip_addr_add(iplink_srv_t *srv, inet_addr_t *addr);
 static errno_t loopip_addr_remove(iplink_srv_t *srv, inet_addr_t *addr);
@@ -231,5 +232,5 @@
 }
 
-static errno_t loopip_get_mac48(iplink_srv_t *src, addr48_t *mac)
+static errno_t loopip_get_mac48(iplink_srv_t *src, eth_addr_t *mac)
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "loopip_get_mac48()");
Index: uspace/srv/net/loopip/meson.build
===================================================================
--- uspace/srv/net/loopip/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/loopip/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2012 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'inet' ]
 src = files('loopip.c')
Index: uspace/srv/net/nconfsrv/meson.build
===================================================================
--- uspace/srv/net/nconfsrv/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/nconfsrv/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,4 +1,4 @@
 #
-# Copyright (c) 2013 Jiri Svoboda
+# Copyright (c) 2021 Jiri Svoboda
 # All rights reserved.
 #
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'inet' ]
 src = files('iplink.c', 'nconfsrv.c')
Index: uspace/srv/net/slip/slip.c
===================================================================
--- uspace/srv/net/slip/slip.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/slip/slip.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -1,3 +1,4 @@
 /*
+ * Copyright (c) 2021 Jiri Svoboda
  * Copyright (c) 2013 Jakub Jermar
  * All rights reserved.
@@ -40,4 +41,5 @@
 #include <loc.h>
 #include <inet/addr.h>
+#include <inet/eth_addr.h>
 #include <inet/iplink_srv.h>
 #include <io/chardev.h>
@@ -62,5 +64,5 @@
 static errno_t slip_send6(iplink_srv_t *, iplink_sdu6_t *);
 static errno_t slip_get_mtu(iplink_srv_t *, size_t *);
-static errno_t slip_get_mac48(iplink_srv_t *, addr48_t *);
+static errno_t slip_get_mac48(iplink_srv_t *, eth_addr_t *);
 static errno_t slip_addr_add(iplink_srv_t *, inet_addr_t *);
 static errno_t slip_addr_remove(iplink_srv_t *, inet_addr_t *);
@@ -176,5 +178,5 @@
 }
 
-errno_t slip_get_mac48(iplink_srv_t *src, addr48_t *mac)
+errno_t slip_get_mac48(iplink_srv_t *src, eth_addr_t *mac)
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_get_mac48()");
Index: uspace/srv/net/tcp/ncsim.c
===================================================================
--- uspace/srv/net/tcp/ncsim.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/tcp/ncsim.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -81,5 +81,5 @@
 	return;
 
-	if (0 /*rand() % 4 == 3*/) {
+	if (0 /* rand() % 4 == 3 */) {
 		/* Drop segment */
 		log_msg(LOG_DEFAULT, LVL_ERROR, "NCSim dropping segment");
Index: uspace/srv/net/tcp/test.c
===================================================================
--- uspace/srv/net/tcp/test.c	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/net/tcp/test.c	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -82,5 +82,5 @@
 	}
 
-	fibril_usleep(/*10**/1000 * 1000);
+	fibril_usleep(1000 * 1000);
 
 	printf("S: User close...\n");
@@ -116,5 +116,5 @@
 	tcp_uc_send(conn, (void *)msg, str_size(msg), 0);
 
-	fibril_usleep(1000 * 1000 * 20/**20*2*/);
+	fibril_usleep(1000 * 1000 * 20);
 	printf("C: User close...\n");
 	tcp_uc_close(conn);
Index: uspace/srv/test/chardev-test/meson.build
===================================================================
--- uspace/srv/test/chardev-test/meson.build	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ uspace/srv/test/chardev-test/meson.build	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -27,3 +27,4 @@
 #
 
+deps = [ 'device' ]
 src = files('main.c')
Index: version
===================================================================
--- version	(revision 5b9993b49f8397a774a9ba98c8299b2b7d423b56)
+++ version	(revision 3a4a944f9be64d2ec90b47d2a2e5502e505d46a4)
@@ -28,5 +28,5 @@
 
 
-HELENOS_RELEASE = 0.9.1
-HELENOS_CODENAME = Armonia
-HELENOS_COPYRIGHT = Copyright (c) 2001-2019 HelenOS project
+HELENOS_RELEASE = 0.11.1
+HELENOS_CODENAME = Jubilee
+HELENOS_COPYRIGHT = Copyright (c) 2001-2021 HelenOS project
