Index: .gitignore
===================================================================
--- .gitignore	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ .gitignore	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -248,4 +248,5 @@
 uspace/dist/logo.tga
 uspace/dist/srv/cdfs
+uspace/dist/srv/chardev-test
 uspace/dist/srv/clipboard
 uspace/dist/srv/compositor
@@ -420,4 +421,5 @@
 uspace/srv/ns/ns
 uspace/srv/taskmon/taskmon
+uspace/srv/test/chardev-test/chardev-test
 uspace/srv/vfs/vfs
 uspace/srv/volsrv/volsrv
Index: uspace/app/sportdmp/sportdmp.c
===================================================================
--- uspace/app/sportdmp/sportdmp.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/app/sportdmp/sportdmp.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -27,9 +27,10 @@
  */
 
-#include <char_dev_iface.h>
 #include <errno.h>
+#include <io/chardev.h>
 #include <io/serial.h>
 #include <loc.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 #define BUF_SIZE 1
@@ -44,5 +45,7 @@
 	sysarg_t baud = 9600;
 	service_id_t svc_id;
+	chardev_t *chardev;
 	serial_t *serial;
+	size_t nread;
 
 	int arg = 1;
@@ -119,4 +122,10 @@
 	}
 
+	rc = chardev_open(sess, &chardev);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed opening character device\n");
+		return 2;
+	}
+
 	rc = serial_open(sess, &serial);
 	if (rc != EOK) {
@@ -138,12 +147,11 @@
 
 	while (true) {
-		ssize_t read = char_dev_read(sess, buf, BUF_SIZE);
-		if (read < 0) {
-			fprintf(stderr, "Failed reading from serial device\n");
+		rc = chardev_read(chardev, buf, BUF_SIZE, &nread);
+		for (size_t i = 0; i < nread; i++) {
+			printf("%02hhx ", buf[i]);
+		}
+		if (rc != EOK) {
+			fprintf(stderr, "\nFailed reading from serial device\n");
 			break;
-		}
-		ssize_t i;
-		for (i = 0; i < read; i++) {
-			printf("%02hhx ", buf[i]);
 		}
 		fflush(stdout);
@@ -152,4 +160,5 @@
 	free(buf);
 	serial_close(serial);
+	chardev_close(chardev);
 	async_hangup(sess);
 	return 0;
Index: uspace/app/tester/Makefile
===================================================================
--- uspace/app/tester/Makefile	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/app/tester/Makefile	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -68,5 +68,4 @@
 	mm/mapping1.c \
 	mm/pager1.c \
-	hw/misc/virtchar1.c \
 	hw/serial/serial1.c \
 	chardev/chardev1.c
Index: pace/app/tester/hw/misc/virtchar1.c
===================================================================
--- uspace/app/tester/hw/misc/virtchar1.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ 	(revision )
@@ -1,106 +1,0 @@
-/*
- * Copyright (c) 2010 Vojtech Horky
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @addtogroup tester
- * @brief Test the virtual char driver
- * @{
- */
-/**
- * @file
- */
-
-#include <inttypes.h>
-#include <errno.h>
-#include <str_error.h>
-#include <stddef.h>
-#include <async.h>
-#include <char_dev_iface.h>
-#include <str.h>
-#include <vfs/vfs.h>
-#include <vfs/vfs_sess.h>
-#include "../../tester.h"
-
-#define DEVICE_PATH_NORMAL  "/loc/devices/\\virt\\null\\a"
-
-#define BUFFER_SIZE  64
-
-static const char *test_virtchar1_internal(const char *path)
-{
-	TPRINTF("Opening `%s'...\n", path);
-	int fd = vfs_lookup(path, WALK_REGULAR);
-	if (fd < 0) {
-		TPRINTF("   ...error: %s\n", str_error(errno));
-		if (fd == ENOENT) {
-			TPRINTF("   (error was ENOENT: " \
-			    "have you compiled test drivers?)\n");
-		}
-		return "Failed opening NULL device for reading";
-	}
-	
-	TPRINTF("   ...file handle %d\n", fd);
-	
-	TPRINTF(" Asking for session...\n");
-	async_sess_t *sess = vfs_fd_session(fd, INTERFACE_DDF);
-	if (!sess) {
-		vfs_put(fd);
-		TPRINTF("   ...error: %s\n", str_error(errno));
-		return "Failed to get session to device";
-	}
-	TPRINTF("   ...session is %p\n", sess);
-	
-	TPRINTF(" Will try to read...\n");
-	size_t i;
-	char buffer[BUFFER_SIZE];
-	char_dev_read(sess, buffer, BUFFER_SIZE);
-	TPRINTF(" ...verifying that we read zeroes only...\n");
-	for (i = 0; i < BUFFER_SIZE; i++) {
-		if (buffer[i] != 0) {
-			return "Not all bytes are zeroes";
-		}
-	}
-	TPRINTF("   ...data read okay\n");
-	
-	/* Clean-up. */
-	TPRINTF(" Closing session and file descriptor\n");
-	async_hangup(sess);
-	vfs_put(fd);
-	
-	return NULL;
-}
-
-const char *test_virtchar1(void)
-{
-	const char *res = test_virtchar1_internal(DEVICE_PATH_NORMAL);
-	if (res != NULL)
-		return res;
-	
-	return NULL;
-}
-
-/** @}
- */
Index: pace/app/tester/hw/misc/virtchar1.def
===================================================================
--- uspace/app/tester/hw/misc/virtchar1.def	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ 	(revision )
@@ -1,6 +1,0 @@
-{
-	"virtchar1",
-	"Virtual char device test",
-	&test_virtchar1,
-	false
-},
Index: uspace/app/tester/hw/serial/serial1.c
===================================================================
--- uspace/app/tester/hw/serial/serial1.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/app/tester/hw/serial/serial1.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -35,16 +35,15 @@
  */
 
-#include <inttypes.h>
+#include <async.h>
 #include <errno.h>
+#include <io/chardev.h>
+#include <io/serial.h>
+#include <ipc/services.h>
+#include <loc.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <stddef.h>
-#include <async.h>
+#include <str.h>
 #include <thread.h>
-#include <ipc/services.h>
-#include <loc.h>
-#include <char_dev_iface.h>
-#include <str.h>
-#include <io/serial.h>
 #include "../../tester.h"
 
@@ -57,4 +56,8 @@
 	size_t cnt;
 	serial_t *serial;
+	chardev_t *chardev;
+	int rc;
+	size_t nread;
+	size_t nwritten;
 	
 	if (test_argc < 1)
@@ -83,10 +86,20 @@
 		return "Failed connecting to serial device";
 	
+	res = chardev_open(sess, &chardev);
+	if (res != EOK) {
+		async_hangup(sess);
+		return "Failed opening serial port";
+	}
+	
 	res = serial_open(sess, &serial);
-	if (res != EOK)
+	if (res != EOK) {
+		chardev_close(chardev);
+		async_hangup(sess);
 		return "Failed opening serial port";
+	}
 	
 	char *buf = (char *) malloc(cnt + 1);
 	if (buf == NULL) {
+		chardev_close(chardev);
 		serial_close(serial);
 		async_hangup(sess);
@@ -103,4 +116,5 @@
 	if (res != EOK) {
 		free(buf);
+		chardev_close(chardev);
 		serial_close(serial);
 		async_hangup(sess);
@@ -111,4 +125,5 @@
 	if (EOK != res) {
 		free(buf);
+		chardev_close(chardev);
 		serial_close(serial);
 		async_hangup(sess);
@@ -121,11 +136,12 @@
 	size_t total = 0;
 	while (total < cnt) {
-		ssize_t read = char_dev_read(sess, buf, cnt - total);
-		
-		if (read < 0) {
+		
+		rc = chardev_read(chardev, buf, cnt - total, &nread);
+		if (rc != EOK) {
 			(void) serial_set_comm_props(serial, old_baud,
 			    old_par, old_word_size, old_stop);
 			
 			free(buf);
+			chardev_close(chardev);
 			serial_close(serial);
 			async_hangup(sess);
@@ -133,9 +149,10 @@
 		}
 		
-		if ((size_t) read > cnt - total) {
+		if (nread > cnt - total) {
 			(void) serial_set_comm_props(serial, old_baud,
 			    old_par, old_word_size, old_stop);
 			
 			free(buf);
+			chardev_close(chardev);
 			serial_close(serial);
 			async_hangup(sess);
@@ -143,10 +160,10 @@
 		}
 		
-		TPRINTF("Read %zd bytes\n", read);
-		
-		if (read == 0)
+		TPRINTF("Read %zd bytes\n", nread);
+		
+		if (nread == 0)
 			thread_usleep(DEFAULT_SLEEP);
 		else {
-			buf[read] = 0;
+			buf[nread] = 0;
 			
 			/*
@@ -154,11 +171,11 @@
 			 * direction of data transfer.
 			 */
-			ssize_t written = char_dev_write(sess, buf, read);
-			
-			if (written < 0) {
+			rc = chardev_write(chardev, buf, nread, &nwritten);
+			if (rc != EOK) {
 				(void) serial_set_comm_props(serial, old_baud,
 				    old_par, old_word_size, old_stop);
 				
 				free(buf);
+				chardev_close(chardev);
 				serial_close(serial);
 				async_hangup(sess);
@@ -166,9 +183,10 @@
 			}
 			
-			if (written != read) {
+			if (nwritten != nread) {
 				(void) serial_set_comm_props(serial, old_baud,
 				    old_par, old_word_size, old_stop);
 				
 				free(buf);
+				chardev_close(chardev);
 				serial_close(serial);
 				async_hangup(sess);
@@ -176,8 +194,8 @@
 			}
 			
-			TPRINTF("Written %zd bytes\n", written);
-		}
-		
-		total += read;
+			TPRINTF("Written %zd bytes\n", nwritten);
+		}
+		
+		total += nread;
 	}
 	
@@ -185,5 +203,5 @@
 	
 	size_t eot_size = str_size(EOT);
-	ssize_t written = char_dev_write(sess, (void *) EOT, eot_size);
+	rc = chardev_write(chardev, (void *) EOT, eot_size, &nwritten);
 	
 	(void) serial_set_comm_props(serial, old_baud, old_par, old_word_size,
@@ -191,11 +209,12 @@
 	
 	free(buf);
+	chardev_close(chardev);
 	serial_close(serial);
 	async_hangup(sess);
 	
-	if (written < 0)
+	if (rc != EOK)
 		return "Failed to write EOT banner to serial device";
 	
-	if ((size_t) written != eot_size)
+	if (nwritten != eot_size)
 		return "Written less data than the size of the EOT banner "
 		    "to serial device";
Index: uspace/app/tester/tester.c
===================================================================
--- uspace/app/tester/tester.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/app/tester/tester.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -76,5 +76,4 @@
 #include "mm/pager1.def"
 #include "hw/serial/serial1.def"
-#include "hw/misc/virtchar1.def"
 #include "chardev/chardev1.def"
 	{NULL, NULL, NULL, false}
Index: uspace/app/tester/tester.h
===================================================================
--- uspace/app/tester/tester.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/app/tester/tester.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -108,5 +108,4 @@
 extern const char *test_pager1(void);
 extern const char *test_serial1(void);
-extern const char *test_virtchar1(void);
 extern const char *test_devman1(void);
 extern const char *test_devman2(void);
Index: pace/drv/char/i8042/buffer.h
===================================================================
--- uspace/drv/char/i8042/buffer.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ 	(revision )
@@ -1,161 +1,0 @@
-/*
- * Copyright (c) 2011 Jan Vesely
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @addtogroup kbd
- * @{
- */
-
-/** @file
- * @brief Cyclic buffer structure.
- */
-
-#ifndef BUFFER_H_
-#define BUFFER_H_
-
-#include <assert.h>
-#include <fibril_synch.h>
-
-/** Cyclic buffer that blocks on full/empty.
- *
- * read_head == write_head means that the buffer is empty.
- * write_head + 1 == read_head means that the buffer is full.
- * Attempt to insert byte into the full buffer will block until it can succeed.
- * Attempt to read from empty buffer will block until it can succeed.
- *
- */
-typedef struct {
-	uint8_t *buffer;          /**< Storage space. */
-	uint8_t *buffer_end;      /**< End of storage place. */
-	fibril_mutex_t guard;     /**< Protects buffer structures. */
-	fibril_condvar_t change;  /**< Indicates change (empty/full). */
-	uint8_t *read_head;       /**< Place of the next readable element. */
-	uint8_t *write_head;      /**< Pointer to the next writable place. */
-} buffer_t;
-
-/** Initialize cyclic buffer using provided memory space.
- *
- * @param buffer Cyclic buffer structure to initialize.
- * @param data   Memory space to use.
- * @param size   Size of the memory place.
- *
- */
-static inline void buffer_init(buffer_t *buffer, uint8_t *data, size_t size)
-{
-	assert(buffer);
-	
-	fibril_mutex_initialize(&buffer->guard);
-	fibril_condvar_initialize(&buffer->change);
-	buffer->buffer = data;
-	buffer->buffer_end = data + size;
-	buffer->read_head = buffer->buffer;
-	buffer->write_head = buffer->buffer;
-	memset(buffer->buffer, 0, size);
-}
-
-/** Write byte to cyclic buffer.
- *
- * @param buffer Cyclic buffer to write to.
- * @param data   Data to write.
- *
- */
-static inline void buffer_write(buffer_t *buffer, uint8_t data)
-{
-	fibril_mutex_lock(&buffer->guard);
-	
-	/* Next position. */
-	uint8_t *new_head = buffer->write_head + 1;
-	if (new_head == buffer->buffer_end)
-		new_head = buffer->buffer;
-	
-	/* Buffer full. */
-	while (new_head == buffer->read_head)
-		fibril_condvar_wait(&buffer->change, &buffer->guard);
-	
-	/* Write data. */
-	*buffer->write_head = data;
-	
-	/* Buffer was empty. */
-	if (buffer->write_head == buffer->read_head)
-		fibril_condvar_broadcast(&buffer->change);
-	
-	/* Move head */
-	buffer->write_head = new_head;
-	fibril_mutex_unlock(&buffer->guard);
-}
-
-/** Read byte from cyclic buffer.
- *
- * @param buffer Cyclic buffer to read from.
- * @param rb Place to store byte read
- * @param wait @c True to wait until byte is available
- *
- * @return EOK on success, EAGAIN if @c wait is false and no data is available
- *
- */
-static inline int buffer_read(buffer_t *buffer, uint8_t *rb, bool wait)
-{
-	fibril_mutex_lock(&buffer->guard);
-	
-	/* Buffer is empty. */
-	while (wait && buffer->write_head == buffer->read_head)
-		fibril_condvar_wait(&buffer->change, &buffer->guard);
-	
-	if (buffer->write_head == buffer->read_head) {
-		fibril_mutex_unlock(&buffer->guard);
-		return EAGAIN;
-	}
-	
-	/* Next position. */
-	uint8_t *new_head = buffer->read_head + 1;
-	if (new_head == buffer->buffer_end)
-		new_head = buffer->buffer;
-	
-	/* Read data. */
-	const uint8_t data = *buffer->read_head;
-	
-	/* Buffer was full. */
-	uint8_t *new_write_head = buffer->write_head + 1;
-	if (new_write_head == buffer->buffer_end)
-		new_write_head = buffer->buffer;
-	if (new_write_head == buffer->read_head)
-		fibril_condvar_broadcast(&buffer->change);
-	
-	/* Move head */
-	buffer->read_head = new_head;
-	
-	fibril_mutex_unlock(&buffer->guard);
-	*rb = data;
-	return EOK;
-}
-
-#endif
-
-/**
- * @}
- */
Index: uspace/drv/char/i8042/i8042.c
===================================================================
--- uspace/drv/char/i8042/i8042.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/char/i8042/i8042.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -2,5 +2,5 @@
  * Copyright (c) 2001-2004 Jakub Jermar
  * Copyright (c) 2006 Josef Cejka
- * Copyright (c) 2014 Jiri Svoboda
+ * Copyright (c) 2017 Jiri Svoboda
  * Copyright (c) 2011 Jan Vesely
  * All rights reserved.
@@ -39,4 +39,5 @@
  */
 
+#include <adt/circ_buf.h>
 #include <ddf/log.h>
 #include <ddf/interrupt.h>
@@ -130,12 +131,20 @@
 {
 	i8042_t *controller = ddf_dev_data_get(dev);
+	int rc;
 	
 	const uint8_t status = IPC_GET_ARG1(*call);
 	const uint8_t data = IPC_GET_ARG2(*call);
 	
-	buffer_t *buffer = (status & i8042_AUX_DATA) ?
-	    &controller->aux_buffer : &controller->kbd_buffer;
-	
-	buffer_write(buffer, data);
+	i8042_port_t *port = (status & i8042_AUX_DATA) ?
+	    controller->aux : controller->kbd;
+	
+	fibril_mutex_lock(&port->buf_lock);
+	
+	rc = circ_buf_push(&port->cbuf, &data);
+	if (rc != EOK)
+		ddf_msg(LVL_ERROR, "Buffer overrun");
+
+	fibril_mutex_unlock(&port->buf_lock);
+	fibril_condvar_broadcast(&port->buf_cv);
 }
 
@@ -159,12 +168,11 @@
 	const size_t cmd_count = sizeof(i8042_cmds) / sizeof(irq_cmd_t);
 	irq_cmd_t cmds[cmd_count];
+	ddf_fun_t *kbd_fun;
+	ddf_fun_t *aux_fun;
 	i8042_regs_t *ar;
-
+	
 	int rc;
 	bool kbd_bound = false;
 	bool aux_bound = false;
-
-	dev->kbd_fun = NULL;
-	dev->aux_fun = NULL;
 	
 	if (regs->size < sizeof(i8042_regs_t)) {
@@ -178,11 +186,11 @@
 	}
 	
-	dev->kbd_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2a");
-	if (dev->kbd_fun == NULL) {
+	kbd_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2a");
+	if (kbd_fun == NULL) {
 		rc = ENOMEM;
 		goto error;
 	};
 	
-	dev->kbd = ddf_fun_data_alloc(dev->kbd_fun, sizeof(i8042_port_t));
+	dev->kbd = ddf_fun_data_alloc(kbd_fun, sizeof(i8042_port_t));
 	if (dev->kbd == NULL) {
 		rc = ENOMEM;
@@ -190,20 +198,23 @@
 	}
 	
+	dev->kbd->fun = kbd_fun;
 	dev->kbd->ctl = dev;
 	chardev_srvs_init(&dev->kbd->cds);
 	dev->kbd->cds.ops = &i8042_chardev_ops;
 	dev->kbd->cds.sarg = dev->kbd;
-	
-	rc = ddf_fun_add_match_id(dev->kbd_fun, "char/xtkbd", 90);
+	fibril_mutex_initialize(&dev->kbd->buf_lock);
+	fibril_condvar_initialize(&dev->kbd->buf_cv);
+	
+	rc = ddf_fun_add_match_id(dev->kbd->fun, "char/xtkbd", 90);
 	if (rc != EOK)
 		goto error;
 	
-	dev->aux_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2b");
-	if (dev->aux_fun == NULL) {
+	aux_fun = ddf_fun_create(ddf_dev, fun_inner, "ps2b");
+	if (aux_fun == NULL) {
 		rc = ENOMEM;
 		goto error;
 	}
 	
-	dev->aux = ddf_fun_data_alloc(dev->aux_fun, sizeof(i8042_port_t));
+	dev->aux = ddf_fun_data_alloc(aux_fun, sizeof(i8042_port_t));
 	if (dev->aux == NULL) {
 		rc = ENOMEM;
@@ -211,32 +222,35 @@
 	}
 	
+	dev->aux->fun = aux_fun;
 	dev->aux->ctl = dev;
 	chardev_srvs_init(&dev->aux->cds);
 	dev->aux->cds.ops = &i8042_chardev_ops;
 	dev->aux->cds.sarg = dev->aux;
-	
-	rc = ddf_fun_add_match_id(dev->aux_fun, "char/ps2mouse", 90);
+	fibril_mutex_initialize(&dev->aux->buf_lock);
+	fibril_condvar_initialize(&dev->aux->buf_cv);
+	
+	rc = ddf_fun_add_match_id(dev->aux->fun, "char/ps2mouse", 90);
 	if (rc != EOK)
 		goto error;
 	
-	ddf_fun_set_conn_handler(dev->kbd_fun, i8042_char_conn);
-	ddf_fun_set_conn_handler(dev->aux_fun, i8042_char_conn);
-	
-	buffer_init(&dev->kbd_buffer, dev->kbd_data, BUFFER_SIZE);
-	buffer_init(&dev->aux_buffer, dev->aux_data, BUFFER_SIZE);
+	ddf_fun_set_conn_handler(dev->kbd->fun, i8042_char_conn);
+	ddf_fun_set_conn_handler(dev->aux->fun, i8042_char_conn);
+	
+	circ_buf_init(&dev->kbd->cbuf, dev->kbd->buf_data, BUFFER_SIZE, 1);
+	circ_buf_init(&dev->aux->cbuf, dev->aux->buf_data, BUFFER_SIZE, 1);
 	fibril_mutex_initialize(&dev->write_guard);
 	
-	rc = ddf_fun_bind(dev->kbd_fun);
+	rc = ddf_fun_bind(dev->kbd->fun);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed to bind keyboard function: %s.",
-		    ddf_fun_get_name(dev->kbd_fun));
+		    ddf_fun_get_name(dev->kbd->fun));
 		goto error;
 	}
 	kbd_bound = true;
 	
-	rc = ddf_fun_bind(dev->aux_fun);
+	rc = ddf_fun_bind(dev->aux->fun);
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed to bind aux function: %s.",
-		    ddf_fun_get_name(dev->aux_fun));
+		    ddf_fun_get_name(dev->aux->fun));
 		goto error;
 	}
@@ -317,11 +331,11 @@
 error:
 	if (kbd_bound)
-		ddf_fun_unbind(dev->kbd_fun);
+		ddf_fun_unbind(dev->kbd->fun);
 	if (aux_bound)
-		ddf_fun_unbind(dev->aux_fun);
-	if (dev->kbd_fun != NULL)
-		ddf_fun_destroy(dev->kbd_fun);
-	if (dev->aux_fun != NULL)
-		ddf_fun_destroy(dev->aux_fun);
+		ddf_fun_unbind(dev->aux->fun);
+	if (dev->kbd->fun != NULL)
+		ddf_fun_destroy(dev->kbd->fun);
+	if (dev->aux->fun != NULL)
+		ddf_fun_destroy(dev->aux->fun);
 
 	return rc;
@@ -377,20 +391,24 @@
 {
 	i8042_port_t *port = (i8042_port_t *)srv->srvs->sarg;
-	i8042_t *i8042 = port->ctl;
+	size_t p;
 	uint8_t *destp = (uint8_t *)dest;
 	int rc;
-	size_t i;
-	
-	buffer_t *buffer = (port == i8042->aux) ?
-	    &i8042->aux_buffer : &i8042->kbd_buffer;
-	
-	for (i = 0; i < size; ++i) {
-		rc = buffer_read(buffer, destp, i == 0);
+	
+	fibril_mutex_lock(&port->buf_lock);
+	
+	while (circ_buf_nused(&port->cbuf) == 0)
+		fibril_condvar_wait(&port->buf_cv, &port->buf_lock);
+
+	p = 0;
+	while (p < size) {
+		rc = circ_buf_pop(&port->cbuf, &destp[p]);
 		if (rc != EOK)
 			break;
-		++destp;
-	}
-	
-	*nread = i;
+		++p;
+	}
+
+	fibril_mutex_unlock(&port->buf_lock);
+
+	*nread = p;
 	return EOK;
 }
Index: uspace/drv/char/i8042/i8042.h
===================================================================
--- uspace/drv/char/i8042/i8042.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/char/i8042/i8042.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -2,4 +2,5 @@
  * Copyright (c) 2006 Josef Cejka
  * Copyright (c) 2011 Jan Vesely
+ * Copyright (c) 2017 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,9 +41,9 @@
 #define i8042_H_
 
+#include <adt/circ_buf.h>
 #include <io/chardev_srv.h>
 #include <ddi.h>
 #include <fibril_synch.h>
 #include <ddf/driver.h>
-#include "buffer.h"
 
 #define NAME  "i8042"
@@ -59,22 +60,31 @@
 /** i8042 Port. */
 typedef struct {
-	struct i8042 *ctl;		/**< Controller */
-	chardev_srvs_t cds;		/**< Character device server data */
+	/** Controller */
+	struct i8042 *ctl;
+	/** Device function */
+	ddf_fun_t *fun;
+	/** Character device server data */
+	chardev_srvs_t cds;
+	/** Circular buffer */
+	circ_buf_t cbuf;
+	/** Buffer data space */
+	uint8_t buf_data[BUFFER_SIZE];
+	/** Protect buffer */
+	fibril_mutex_t buf_lock;
+	/** Signal new data in buffer */
+	fibril_condvar_t buf_cv;
 } i8042_port_t;
 
 /** i8042 Controller. */
 typedef struct i8042 {
-	i8042_regs_t *regs;             /**< I/O registers. */
-	ddf_fun_t *kbd_fun;             /**< Pirmary port device function. */
-	ddf_fun_t *aux_fun;             /**< Auxiliary port device function. */
-	buffer_t kbd_buffer;            /**< Primary port buffer. */
-	buffer_t aux_buffer;            /**< Aux. port buffer. */
-	uint8_t aux_data[BUFFER_SIZE];  /**< Primary port buffer space. */
-	uint8_t kbd_data[BUFFER_SIZE];  /**< Aux. port buffer space. */
+	/**< I/O registers. */
+	i8042_regs_t *regs;
+	/** Keyboard port */
 	i8042_port_t *kbd;
+	/** AUX port */
 	i8042_port_t *aux;
-	fibril_mutex_t write_guard;     /**< Prevents simultanous port writes.*/
+	/** Prevents simultanous port writes.*/
+	fibril_mutex_t write_guard;
 } i8042_t;
-
 
 extern int i8042_init(i8042_t *, addr_range_t *, int, int, ddf_dev_t *);
Index: uspace/drv/char/msim-con/msim-con.c
===================================================================
--- uspace/drv/char/msim-con/msim-con.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/char/msim-con/msim-con.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -37,9 +37,17 @@
 #include <ddi.h>
 #include <errno.h>
-#include <ipc/char.h>
+#include <io/chardev_srv.h>
 
 #include "msim-con.h"
 
 static void msim_con_connection(ipc_callid_t, ipc_call_t *, void *);
+
+static int msim_con_read(chardev_srv_t *, void *, size_t, size_t *);
+static int msim_con_write(chardev_srv_t *, const void *, size_t, size_t *);
+
+static chardev_ops_t msim_con_chardev_ops = {
+	.read = msim_con_read,
+	.write = msim_con_write
+};
 
 static irq_cmd_t msim_cmds_proto[] = {
@@ -58,12 +66,15 @@
 	msim_con_t *con = (msim_con_t *) arg;
 	uint8_t c;
+	int rc;
+
+	fibril_mutex_lock(&con->buf_lock);
 
 	c = IPC_GET_ARG2(*call);
-
-	if (con->client_sess != NULL) {
-		async_exch_t *exch = async_exchange_begin(con->client_sess);
-		async_msg_1(exch, CHAR_NOTIF_BYTE, c);
-		async_exchange_end(exch);
-	}
+	rc = circ_buf_push(&con->cbuf, &c);
+	if (rc != EOK)
+		ddf_msg(LVL_ERROR, "Buffer overrun");
+
+	fibril_mutex_unlock(&con->buf_lock);
+	fibril_condvar_broadcast(&con->buf_cv);
 }
 
@@ -75,4 +86,8 @@
 	irq_cmd_t *msim_cmds = NULL;
 	int rc;
+
+	circ_buf_init(&con->cbuf, con->buf, msim_con_buf_size, 1);
+	fibril_mutex_initialize(&con->buf_lock);
+	fibril_condvar_initialize(&con->buf_cv);
 
 	msim_cmds = malloc(sizeof(msim_cmds_proto));
@@ -106,4 +121,8 @@
 	async_irq_subscribe(res->irq, msim_irq_handler, con, &con->irq_code);
 	subscribed = true;
+
+	chardev_srvs_init(&con->cds);
+	con->cds.ops = &msim_con_chardev_ops;
+	con->cds.sarg = con;
 
 	rc = ddf_fun_bind(fun);
@@ -140,47 +159,55 @@
 }
 
+/** Read from msim console device */
+static int msim_con_read(chardev_srv_t *srv, void *buf, size_t size,
+    size_t *nread)
+{
+	msim_con_t *con = (msim_con_t *) srv->srvs->sarg;
+	size_t p;
+	uint8_t *bp = (uint8_t *) buf;
+	int rc;
+
+	fibril_mutex_lock(&con->buf_lock);
+
+	while (circ_buf_nused(&con->cbuf) == 0)
+		fibril_condvar_wait(&con->buf_cv, &con->buf_lock);
+
+	p = 0;
+	while (p < size) {
+		rc = circ_buf_pop(&con->cbuf, &bp[p]);
+		if (rc != EOK)
+			break;
+		++p;
+	}
+
+	fibril_mutex_unlock(&con->buf_lock);
+
+	*nread = p;
+	return EOK;
+}
+
+/** Write to msim console device */
+static int msim_con_write(chardev_srv_t *srv, const void *data, size_t size,
+    size_t *nwr)
+{
+	msim_con_t *con = (msim_con_t *) srv->srvs->sarg;
+	size_t i;
+	uint8_t *dp = (uint8_t *) data;
+
+	for (i = 0; i < size; i++)
+		msim_con_putchar(con, dp[i]); 
+
+	*nwr = size;
+	return EOK;
+}
+
 /** Character device connection handler. */
 static void msim_con_connection(ipc_callid_t iid, ipc_call_t *icall,
     void *arg)
 {
-	msim_con_t *con;
-
-	/* Answer the IPC_M_CONNECT_ME_TO call. */
-	async_answer_0(iid, EOK);
-
-	con = (msim_con_t *)ddf_dev_data_get(ddf_fun_get_dev((ddf_fun_t *)arg));
-
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		sysarg_t method = IPC_GET_IMETHOD(call);
-
-		if (!method) {
-			/* The other side has hung up. */
-			async_answer_0(callid, EOK);
-			return;
-		}
-
-		async_sess_t *sess =
-		    async_callback_receive_start(EXCHANGE_SERIALIZE, &call);
-		if (sess != NULL) {
-			if (con->client_sess == NULL) {
-				con->client_sess = sess;
-				async_answer_0(callid, EOK);
-			} else
-				async_answer_0(callid, ELIMIT);
-		} else {
-			switch (method) {
-			case CHAR_WRITE_BYTE:
-				ddf_msg(LVL_DEBUG, "Write %" PRIun " to device\n",
-				    IPC_GET_ARG1(call));
-				msim_con_putchar(con, (uint8_t) IPC_GET_ARG1(call));
-				async_answer_0(callid, EOK);
-				break;
-			default:
-				async_answer_0(callid, EINVAL);
-			}
-		}
-	}
+	msim_con_t *con = (msim_con_t *) ddf_dev_data_get(
+	    ddf_fun_get_dev((ddf_fun_t *) arg));
+
+	chardev_conn(iid, icall, &con->cds);
 }
 
Index: uspace/drv/char/msim-con/msim-con.h
===================================================================
--- uspace/drv/char/msim-con/msim-con.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/char/msim-con/msim-con.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -36,8 +36,15 @@
 #define MSIM_CON_H
 
+#include <adt/circ_buf.h>
 #include <async.h>
 #include <ddf/driver.h>
+#include <fibril_synch.h>
+#include <io/chardev_srv.h>
 #include <loc.h>
 #include <stdint.h>
+
+enum {
+	msim_con_buf_size = 64
+};
 
 /** MSIM console resources */
@@ -51,7 +58,12 @@
 	async_sess_t *client_sess;
 	ddf_dev_t *dev;
+	chardev_srvs_t cds;
 	msim_con_res_t res;
 	irq_pio_range_t irq_range[1];
 	irq_code_t irq_code;
+	circ_buf_t cbuf;
+	uint8_t buf[msim_con_buf_size];
+	fibril_mutex_t buf_lock;
+	fibril_condvar_t buf_cv;
 } msim_con_t;
 
Index: uspace/drv/char/ns8250/ns8250.c
===================================================================
--- uspace/drv/char/ns8250/ns8250.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/char/ns8250/ns8250.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2010 Lenka Trochtova
- * Copyright (c) 2011 Jiri Svoboda
+ * Copyright (c) 2017 Jiri Svoboda
  * All rights reserved.
  *
@@ -53,5 +53,5 @@
 #include <ddf/interrupt.h>
 #include <ddf/log.h>
-#include <ops/char_dev.h>
+#include <io/chardev_srv.h>
 
 #include <device/hw_res.h>
@@ -153,4 +153,6 @@
 	/** DDF function node */
 	ddf_fun_t *fun;
+	/** Character device service */
+	chardev_srvs_t cds;
 	/** Parent session */
 	async_sess_t *parent_sess;
@@ -189,4 +191,11 @@
 }
 
+/** Obtain soft-state structure from chardev srv */
+static ns8250_t *srv_ns8250(chardev_srv_t *srv)
+{
+	return (ns8250_t *)srv->srvs->sarg;
+}
+
+
 /** Find out if there is some incoming data available on the serial port.
  *
@@ -234,28 +243,33 @@
 /** Read data from the serial port device.
  *
- * @param fun		The serial port function
+ * @param srv		Server-side connection data
  * @param buf		The output buffer for read data.
  * @param count		The number of bytes to be read.
- *
- * @return		The number of bytes actually read on success, negative
- *			error number otherwise.
- */
-static int ns8250_read(ddf_fun_t *fun, char *buf, size_t count)
-{
-	ns8250_t *ns = fun_ns8250(fun);
-	int ret = 0;
-	
-	if (count == 0) return 0;
+ * @param nread		Place to store number of bytes actually read
+ *
+ * @return		EOK on success or non-zero error code
+ */
+static int ns8250_read(chardev_srv_t *srv, void *buf, size_t count, size_t *nread)
+{
+	ns8250_t *ns = srv_ns8250(srv);
+	char *bp = (char *) buf;
+	size_t pos = 0;
+	
+	if (count == 0) {
+		*nread = 0;
+		return EOK;
+	}
 	
 	fibril_mutex_lock(&ns->mutex);
 	while (buf_is_empty(&ns->input_buffer))
 		fibril_condvar_wait(&ns->input_buffer_available, &ns->mutex);
-	while (!buf_is_empty(&ns->input_buffer) && (size_t)ret < count) {
-		buf[ret] = (char)buf_pop_front(&ns->input_buffer);
-		ret++;
+	while (!buf_is_empty(&ns->input_buffer) && pos < count) {
+		bp[pos] = (char)buf_pop_front(&ns->input_buffer);
+		pos++;
 	}
 	fibril_mutex_unlock(&ns->mutex);
 	
-	return ret;
+	*nread = pos;
+	return EOK;
 }
 
@@ -274,27 +288,38 @@
 /** Write data to the serial port.
  *
- * @param fun		The serial port function
+ * @param srv		Server-side connection data
  * @param buf		The data to be written
  * @param count		The number of bytes to be written
- * @return		Zero on success
- */
-static int ns8250_write(ddf_fun_t *fun, char *buf, size_t count)
-{
-	ns8250_t *ns = fun_ns8250(fun);
+ * @param nwritten	Place to store number of bytes successfully written
+ * @return		EOK on success or non-zero error code
+ */
+static int ns8250_write(chardev_srv_t *srv, const void *buf, size_t count,
+    size_t *nwritten)
+{
+	ns8250_t *ns = srv_ns8250(srv);
 	size_t idx;
+	uint8_t *bp = (uint8_t *) buf;
 	
 	for (idx = 0; idx < count; idx++)
-		ns8250_putchar(ns, (uint8_t) buf[idx]);
-	
-	return count;
-}
-
-static ddf_dev_ops_t ns8250_dev_ops;
+		ns8250_putchar(ns, bp[idx]);
+	
+	*nwritten = count;
+	return EOK;
+}
+
+static int ns8250_open(chardev_srvs_t *, chardev_srv_t *);
+static int ns8250_close(chardev_srv_t *);
+static void ns8250_default_handler(chardev_srv_t *, ipc_callid_t, ipc_call_t *);
 
 /** The character interface's callbacks. */
-static char_dev_ops_t ns8250_char_dev_ops = {
-	.read = &ns8250_read,
-	.write = &ns8250_write
+static chardev_ops_t ns8250_chardev_ops = {
+	.open = ns8250_open,
+	.close = ns8250_close,
+	.read = ns8250_read,
+	.write = ns8250_write,
+	.def_handler = ns8250_default_handler
 };
+
+static void ns8250_char_conn(ipc_callid_t, ipc_call_t *, void *);
 
 static int ns8250_dev_add(ddf_dev_t *dev);
@@ -872,6 +897,10 @@
 	}
 	
-	/* Set device operations. */
-	ddf_fun_set_ops(fun, &ns8250_dev_ops);
+	ddf_fun_set_conn_handler(fun, ns8250_char_conn);
+	
+	chardev_srvs_init(&ns->cds);
+	ns->cds.ops = &ns8250_chardev_ops;
+	ns->cds.sarg = ns;
+	
 	rc = ddf_fun_bind(fun);
 	if (rc != EOK) {
@@ -930,9 +959,10 @@
  * device.
  *
- * @param dev		The device.
- */
-static int ns8250_open(ddf_fun_t *fun)
-{
-	ns8250_t *ns = fun_ns8250(fun);
+ * @param srvs		Service structure
+ * @param srv		Server-side connection structure
+ */
+static int ns8250_open(chardev_srvs_t *srvs, chardev_srv_t *srv)
+{
+	ns8250_t *ns = srv_ns8250(srv);
 	int res;
 	
@@ -954,9 +984,9 @@
  * the device.
  *
- * @param dev		The device.
- */
-static void ns8250_close(ddf_fun_t *fun)
-{
-	ns8250_t *data = fun_ns8250(fun);
+ * @param srv		Server-side connection structure
+ */
+static int ns8250_close(chardev_srv_t *srv)
+{
+	ns8250_t *data = srv_ns8250(srv);
 	
 	fibril_mutex_lock(&data->mutex);
@@ -968,4 +998,6 @@
 	
 	fibril_mutex_unlock(&data->mutex);
+	
+	return EOK;
 }
 
@@ -1034,7 +1066,8 @@
  * Configure the parameters of the serial communication.
  */
-static void ns8250_default_handler(ddf_fun_t *fun, ipc_callid_t callid,
+static void ns8250_default_handler(chardev_srv_t *srv, ipc_callid_t callid,
     ipc_call_t *call)
 {
+	ns8250_t *ns8250 = srv_ns8250(srv);
 	sysarg_t method = IPC_GET_IMETHOD(*call);
 	int ret;
@@ -1043,5 +1076,5 @@
 	switch (method) {
 	case SERIAL_GET_COM_PROPS:
-		ns8250_get_props(ddf_fun_get_dev(fun), &baud_rate, &parity, &word_length,
+		ns8250_get_props(ns8250->dev, &baud_rate, &parity, &word_length,
 		    &stop_bits);
 		async_answer_4(callid, EOK, baud_rate, parity, word_length,
@@ -1054,5 +1087,5 @@
 		word_length = IPC_GET_ARG3(*call);
 		stop_bits = IPC_GET_ARG4(*call);
-		ret = ns8250_set_props(ddf_fun_get_dev(fun), baud_rate, parity, word_length,
+		ret = ns8250_set_props(ns8250->dev, baud_rate, parity, word_length,
 		    stop_bits);
 		async_answer_0(callid, ret);
@@ -1064,4 +1097,11 @@
 }
 
+void ns8250_char_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	ns8250_t *ns8250 = fun_ns8250((ddf_fun_t *)arg);
+
+	chardev_conn(iid, icall, &ns8250->cds);
+}
+
 /** Initialize the serial port driver.
  *
@@ -1072,10 +1112,4 @@
 {
 	ddf_log_init(NAME);
-	
-	ns8250_dev_ops.open = &ns8250_open;
-	ns8250_dev_ops.close = &ns8250_close;
-	
-	ns8250_dev_ops.interfaces[CHAR_DEV_IFACE] = &ns8250_char_dev_ops;
-	ns8250_dev_ops.default_handler = &ns8250_default_handler;
 }
 
Index: uspace/drv/char/ski-con/ski-con.c
===================================================================
--- uspace/drv/char/ski-con/ski-con.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/char/ski-con/ski-con.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2005 Jakub Jermar
- * Copyright (c) 2011 Jiri Svoboda
+ * Copyright (c) 2017 Jiri Svoboda
  * All rights reserved.
  *
@@ -34,8 +34,8 @@
 #include <ddf/log.h>
 #include <errno.h>
-#include <ipc/char.h>
+#include <fibril.h>
+#include <io/chardev.h>
 #include <stdint.h>
 #include <stdlib.h>
-#include <thread.h>
 #include <stdbool.h>
 
@@ -46,15 +46,27 @@
 #define POLL_INTERVAL		10000
 
-static void ski_con_thread_impl(void *arg);
+static int ski_con_fibril(void *arg);
 static int32_t ski_con_getchar(void);
 static void ski_con_connection(ipc_callid_t, ipc_call_t *, void *);
 
+static int ski_con_read(chardev_srv_t *, void *, size_t, size_t *);
+static int ski_con_write(chardev_srv_t *, const void *, size_t, size_t *);
+
+static chardev_ops_t ski_con_chardev_ops = {
+	.read = ski_con_read,
+	.write = ski_con_write
+};
+
 /** Add ski console device. */
 int ski_con_add(ski_con_t *con)
 {
-	thread_id_t tid;
+	fid_t fid;
 	ddf_fun_t *fun = NULL;
 	bool bound = false;
 	int rc;
+
+	circ_buf_init(&con->cbuf, con->buf, ski_con_buf_size, 1);
+	fibril_mutex_initialize(&con->buf_lock);
+	fibril_condvar_initialize(&con->buf_cv);
 
 	fun = ddf_fun_create(con->dev, fun_exposed, "a");
@@ -67,4 +79,8 @@
 	ddf_fun_set_conn_handler(fun, ski_con_connection);
 
+	chardev_srvs_init(&con->cds);
+	con->cds.ops = &ski_con_chardev_ops;
+	con->cds.sarg = con;
+
 	rc = ddf_fun_bind(fun);
 	if (rc != EOK) {
@@ -75,9 +91,12 @@
 	bound = true;
 
-	rc = thread_create(ski_con_thread_impl, con, "kbd_poll", &tid);
-	if (rc != 0) {
-		return rc;
-	}
-
+	fid = fibril_create(ski_con_fibril, con);
+	if (fid == 0) {
+		ddf_msg(LVL_ERROR, "Error creating fibril.");
+		rc = ENOMEM;
+		goto error;
+	}
+
+	fibril_add_ready(fid);
 	return EOK;
 error:
@@ -102,9 +121,10 @@
 }
 
-/** Thread to poll Ski for keypresses. */
-static void ski_con_thread_impl(void *arg)
+/** Poll Ski for keypresses. */
+static int ski_con_fibril(void *arg)
 {
 	int32_t c;
 	ski_con_t *con = (ski_con_t *) arg;
+	int rc;
 
 	while (1) {
@@ -114,13 +134,18 @@
 				break;
 
-			if (con->client_sess != NULL) {
-				async_exch_t *exch = async_exchange_begin(con->client_sess);
-				async_msg_1(exch, CHAR_NOTIF_BYTE, c);
-				async_exchange_end(exch);
-			}
+			fibril_mutex_lock(&con->buf_lock);
+
+			rc = circ_buf_push(&con->cbuf, &c);
+			if (rc != EOK)
+				ddf_msg(LVL_ERROR, "Buffer overrun");
+
+			fibril_mutex_unlock(&con->buf_lock);
+			fibril_condvar_broadcast(&con->buf_cv);
 		}
 
-		thread_usleep(POLL_INTERVAL);
-	}
+		fibril_usleep(POLL_INTERVAL);
+	}
+
+	return 0;
 }
 
@@ -157,47 +182,55 @@
 }
 
+/** Read from Ski console device */
+static int ski_con_read(chardev_srv_t *srv, void *buf, size_t size,
+    size_t *nread)
+{
+	ski_con_t *con = (ski_con_t *) srv->srvs->sarg;
+	size_t p;
+	uint8_t *bp = (uint8_t *) buf;
+	int rc;
+
+	fibril_mutex_lock(&con->buf_lock);
+
+	while (circ_buf_nused(&con->cbuf) == 0)
+		fibril_condvar_wait(&con->buf_cv, &con->buf_lock);
+
+	p = 0;
+	while (p < size) {
+		rc = circ_buf_pop(&con->cbuf, &bp[p]);
+		if (rc != EOK)
+			break;
+		++p;
+	}
+
+	fibril_mutex_unlock(&con->buf_lock);
+
+	*nread = p;
+	return EOK;
+}
+
+/** Write to Ski console device */
+static int ski_con_write(chardev_srv_t *srv, const void *data, size_t size,
+    size_t *nwr)
+{
+	ski_con_t *con = (ski_con_t *) srv->srvs->sarg;
+	size_t i;
+	uint8_t *dp = (uint8_t *) data;
+
+	for (i = 0; i < size; i++)
+		ski_con_putchar(con, dp[i]); 
+
+	*nwr = size;
+	return EOK;
+}
+
 /** Character device connection handler. */
 static void ski_con_connection(ipc_callid_t iid, ipc_call_t *icall,
     void *arg)
 {
-	ski_con_t *con;
-
-	/* Answer the IPC_M_CONNECT_ME_TO call. */
-	async_answer_0(iid, EOK);
-
-	con = (ski_con_t *)ddf_dev_data_get(ddf_fun_get_dev((ddf_fun_t *)arg));
-
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		sysarg_t method = IPC_GET_IMETHOD(call);
-
-		if (!method) {
-			/* The other side has hung up. */
-			async_answer_0(callid, EOK);
-			return;
-		}
-
-		async_sess_t *sess =
-		    async_callback_receive_start(EXCHANGE_SERIALIZE, &call);
-		if (sess != NULL) {
-			if (con->client_sess == NULL) {
-				con->client_sess = sess;
-				async_answer_0(callid, EOK);
-			} else
-				async_answer_0(callid, ELIMIT);
-		} else {
-			switch (method) {
-			case CHAR_WRITE_BYTE:
-				ddf_msg(LVL_DEBUG, "Write %" PRIun " to device\n",
-				    IPC_GET_ARG1(call));
-				ski_con_putchar(con, (uint8_t) IPC_GET_ARG1(call));
-				async_answer_0(callid, EOK);
-				break;
-			default:
-				async_answer_0(callid, EINVAL);
-			}
-		}
-	}
+	ski_con_t *con = (ski_con_t *) ddf_dev_data_get(
+	    ddf_fun_get_dev((ddf_fun_t *) arg));
+
+	chardev_conn(iid, icall, &con->cds);
 }
 
Index: uspace/drv/char/ski-con/ski-con.h
===================================================================
--- uspace/drv/char/ski-con/ski-con.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/char/ski-con/ski-con.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -36,8 +36,14 @@
 #define SKI_CON_H
 
+#include <adt/circ_buf.h>
 #include <async.h>
 #include <ddf/driver.h>
+#include <io/chardev_srv.h>
 #include <loc.h>
 #include <stdint.h>
+
+enum {
+	ski_con_buf_size = 64
+};
 
 /** Ski console */
@@ -45,4 +51,9 @@
 	async_sess_t *client_sess;
 	ddf_dev_t *dev;
+	chardev_srvs_t cds;
+	circ_buf_t cbuf;
+	uint8_t buf[ski_con_buf_size];
+	fibril_mutex_t buf_lock;
+	fibril_condvar_t buf_cv;
 } ski_con_t;
 
Index: uspace/drv/char/sun4v-con/sun4v-con.c
===================================================================
--- uspace/drv/char/sun4v-con/sun4v-con.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/char/sun4v-con/sun4v-con.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2008 Pavel Rimsky
- * Copyright (c) 2011 Jiri Svoboda
+ * Copyright (c) 2017 Jiri Svoboda
  * All rights reserved.
  *
@@ -36,5 +36,5 @@
 #include <ddi.h>
 #include <errno.h>
-#include <ipc/char.h>
+#include <io/chardev_srv.h>
 #include <stdbool.h>
 #include <thread.h>
@@ -62,5 +62,11 @@
 static input_buffer_t input_buffer;
 
-static void sun4v_thread_impl(void *arg);
+static int sun4v_con_read(chardev_srv_t *, void *, size_t, size_t *);
+static int sun4v_con_write(chardev_srv_t *, const void *, size_t, size_t *);
+
+static chardev_ops_t sun4v_con_chardev_ops = {
+	.read = sun4v_con_read,
+	.write = sun4v_con_write
+};
 
 static void sun4v_con_putchar(sun4v_con_t *con, uint8_t data)
@@ -86,4 +92,8 @@
 	}
 
+	chardev_srvs_init(&con->cds);
+	con->cds.ops = &sun4v_con_chardev_ops;
+	con->cds.sarg = con;
+
 	ddf_fun_set_conn_handler(fun, sun4v_con_connection);
 
@@ -94,9 +104,4 @@
 		goto error;
 	}
-
-	thread_id_t tid;
-	rc = thread_create(sun4v_thread_impl, con, "kbd_poll", &tid);
-	if (rc != EOK)
-		goto error;
 
 	rc = ddf_fun_bind(fun);
@@ -131,36 +136,40 @@
 }
 
-/**
- * Called regularly by the polling thread. Reads codes of all the
- * pressed keys from the buffer.
- */
-static void sun4v_key_pressed(sun4v_con_t *con)
+/** Read from Sun4v console device */
+static int sun4v_con_read(chardev_srv_t *srv, void *buf, size_t size,
+    size_t *nread)
 {
+	size_t p;
+	uint8_t *bp = (uint8_t *) buf;
 	char c;
 
-	while (input_buffer->read_ptr != input_buffer->write_ptr) {
+	while (input_buffer->read_ptr == input_buffer->write_ptr)
+		fibril_usleep(POLL_INTERVAL);
+
+	p = 0;
+	while (p < size && input_buffer->read_ptr != input_buffer->write_ptr) {
 		c = input_buffer->data[input_buffer->read_ptr];
 		input_buffer->read_ptr =
 		    ((input_buffer->read_ptr) + 1) % INPUT_BUFFER_SIZE;
-		if (con->client_sess != NULL) {
-			async_exch_t *exch = async_exchange_begin(con->client_sess);
-			async_msg_1(exch, CHAR_NOTIF_BYTE, c);
-			async_exchange_end(exch);
-		}
-		(void) c;
+		bp[p++] = c;
 	}
+
+	*nread = p;
+	return EOK;
 }
 
-/**
- * Thread to poll Sun4v console for keypresses.
- */
-static void sun4v_thread_impl(void *arg)
+/** Write to Sun4v console device */
+static int sun4v_con_write(chardev_srv_t *srv, const void *data, size_t size,
+    size_t *nwr)
 {
-	sun4v_con_t *con = (sun4v_con_t *) arg;
+	sun4v_con_t *con = (sun4v_con_t *) srv->srvs->sarg;
+	size_t i;
+	uint8_t *dp = (uint8_t *) data;
 
-	while (true) {
-		sun4v_key_pressed(con);
-		thread_usleep(POLL_INTERVAL);
-	}
+	for (i = 0; i < size; i++)
+		sun4v_con_putchar(con, dp[i]); 
+
+	*nwr = size;
+	return EOK;
 }
 
@@ -169,43 +178,8 @@
     void *arg)
 {
-	sun4v_con_t *con;
+	sun4v_con_t *con = (sun4v_con_t *) ddf_dev_data_get(
+	    ddf_fun_get_dev((ddf_fun_t *) arg));
 
-	/* Answer the IPC_M_CONNECT_ME_TO call. */
-	async_answer_0(iid, EOK);
-
-	con = (sun4v_con_t *)ddf_dev_data_get(ddf_fun_get_dev((ddf_fun_t *)arg));
-
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		sysarg_t method = IPC_GET_IMETHOD(call);
-
-		if (!method) {
-			/* The other side has hung up. */
-			async_answer_0(callid, EOK);
-			return;
-		}
-
-		async_sess_t *sess =
-		    async_callback_receive_start(EXCHANGE_SERIALIZE, &call);
-		if (sess != NULL) {
-			if (con->client_sess == NULL) {
-				con->client_sess = sess;
-				async_answer_0(callid, EOK);
-			} else
-				async_answer_0(callid, ELIMIT);
-		} else {
-			switch (method) {
-			case CHAR_WRITE_BYTE:
-				ddf_msg(LVL_DEBUG, "Write %" PRIun " to device\n",
-				    IPC_GET_ARG1(call));
-				sun4v_con_putchar(con, (uint8_t) IPC_GET_ARG1(call));
-				async_answer_0(callid, EOK);
-				break;
-			default:
-				async_answer_0(callid, EINVAL);
-			}
-		}
-	}
+	chardev_conn(iid, icall, &con->cds);
 }
 
Index: uspace/drv/char/sun4v-con/sun4v-con.h
===================================================================
--- uspace/drv/char/sun4v-con/sun4v-con.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/char/sun4v-con/sun4v-con.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -38,4 +38,5 @@
 #include <async.h>
 #include <ddf/driver.h>
+#include <io/chardev_srv.h>
 #include <loc.h>
 #include <stdint.h>
@@ -50,4 +51,5 @@
 	async_sess_t *client_sess;
 	ddf_dev_t *dev;
+	chardev_srvs_t cds;
 	sun4v_con_res_t res;
 } sun4v_con_t;
Index: uspace/drv/root/virt/devices.def
===================================================================
--- uspace/drv/root/virt/devices.def	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/root/virt/devices.def	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -26,8 +26,4 @@
 },
 {
-	.name = "null",
-	.match_id = "virtual&test1"
-},
-{
 	.name = "test3",
 	.match_id = "virtual&test3"
Index: uspace/drv/test/test1/Makefile
===================================================================
--- uspace/drv/test/test1/Makefile	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/test/test1/Makefile	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -32,5 +32,4 @@
 
 SOURCES = \
-	char.c \
 	test1.c
 
Index: pace/drv/test/test1/char.c
===================================================================
--- uspace/drv/test/test1/char.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ 	(revision )
@@ -1,56 +1,0 @@
-/*
- * Copyright (c) 2010 Vojtech Horky
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/** @file
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <mem.h>
-#include <ops/char_dev.h>
-
-#include "test1.h"
-
-static int impl_char_read(ddf_fun_t *fun, char *buf, size_t count) {
-	memset(buf, 0, count);
-	return count;
-}
-
-static int imp_char_write(ddf_fun_t *fun, char *buf, size_t count) {
-	return count;
-}
-
-static char_dev_ops_t char_dev_ops = {
-	.read = &impl_char_read,
-	.write = &imp_char_write
-};
-
-ddf_dev_ops_t char_device_ops = {
-	.interfaces[CHAR_DEV_IFACE] = &char_dev_ops
-};
-
Index: uspace/drv/test/test1/test1.c
===================================================================
--- uspace/drv/test/test1/test1.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/test/test1/test1.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -177,8 +177,5 @@
 	ddf_fun_add_to_category(fun_a, "virtual");
 
-	if (str_cmp(dev_name, "null") == 0) {
-		ddf_fun_set_ops(fun_a,  &char_device_ops);
-		ddf_fun_add_to_category(fun_a, "virt-null");
-	} else if (str_cmp(dev_name, "test1") == 0) {
+	if (str_cmp(dev_name, "test1") == 0) {
 		(void) register_fun_verbose(dev,
 		    "cloning myself ;-)", "clone",
Index: uspace/drv/test/test1/test1.h
===================================================================
--- uspace/drv/test/test1/test1.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/drv/test/test1/test1.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -36,5 +36,3 @@
 #define NAME "test1"
 
-extern ddf_dev_ops_t char_device_ops;
-
 #endif
Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/lib/c/Makefile	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -143,4 +143,5 @@
 	generic/getopt.c \
 	generic/adt/checksum.c \
+	generic/adt/circ_buf.c \
 	generic/adt/list.c \
 	generic/adt/hash_table.c \
@@ -181,4 +182,5 @@
 
 TEST_SOURCES = \
+	test/adt/circ_buf.c \
 	test/fibril/timer.c \
 	test/main.c \
Index: uspace/lib/c/generic/adt/circ_buf.c
===================================================================
--- uspace/lib/c/generic/adt/circ_buf.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
+++ uspace/lib/c/generic/adt/circ_buf.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -0,0 +1,113 @@
+/*
+ * 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 Circular buffer
+ */
+
+#include <adt/circ_buf.h>
+#include <errno.h>
+#include <mem.h>
+#include <stddef.h>
+
+/** Initialize circular buffer.
+ *
+ * @param cbuf Circular buffer
+ * @param buf Buffer for storing data
+ * @param nmemb Number of entries in @a buf
+ * @param size Size of individual buffer entry
+ */
+void circ_buf_init(circ_buf_t *cbuf, void *buf, size_t nmemb, size_t size)
+{
+	cbuf->buf = buf;
+	cbuf->nmemb = nmemb;
+	cbuf->size = size;
+	cbuf->rp = 0;
+	cbuf->wp = 0;
+	cbuf->nused = 0;
+}
+
+/** Return number of free buffer entries.
+ *
+ * @param cbuf Circular buffer
+ * @return Number of free buffer entries
+ */
+size_t circ_buf_nfree(circ_buf_t *cbuf)
+{
+	return cbuf->nmemb - cbuf->nused;
+}
+
+/** Return number of used buffer entries.
+ *
+ * @param cbuf Circular buffer
+ * @return Number of used buffer entries
+ */
+size_t circ_buf_nused(circ_buf_t *cbuf)
+{
+	return cbuf->nused;
+}
+
+/** Push new entry into circular buffer.
+ *
+ * @param cbuf Circular buffer
+ * @param data Pointer to entry data
+ * @return EOK on success, EAGAIN if circular buffer is full
+ */
+int circ_buf_push(circ_buf_t *cbuf, const void *data)
+{
+	if (circ_buf_nfree(cbuf) == 0)
+		return EAGAIN;
+
+	memcpy(cbuf->buf + cbuf->size * cbuf->wp, data, cbuf->size);
+	cbuf->wp = (cbuf->wp + 1) % cbuf->nmemb;
+	cbuf->nused++;
+	return EOK;
+}
+
+/** Pop entry from circular buffer.
+ *
+ * @param cbuf Circular buffer
+ * @param datab Pointer to data buffer for storing entry
+ * @return EOK on success, EAGAIN if circular buffer is full
+ */
+int circ_buf_pop(circ_buf_t *cbuf, void *datab)
+{
+	if (cbuf->nused == 0)
+		return EAGAIN;
+
+	memcpy(datab, cbuf->buf + cbuf->size * cbuf->rp, cbuf->size);
+	cbuf->rp = (cbuf->rp + 1) % cbuf->nmemb;
+	cbuf->nused--;
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/io/chardev_srv.c
===================================================================
--- uspace/lib/c/generic/io/chardev_srv.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/lib/c/generic/io/chardev_srv.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -169,5 +169,8 @@
 			break;
 		default:
-			async_answer_0(callid, EINVAL);
+			if (srv->srvs->ops->def_handler != NULL)
+				srv->srvs->ops->def_handler(srv, callid, &call);
+			else
+				async_answer_0(callid, ENOTSUP);
 		}
 	}
Index: uspace/lib/c/include/adt/circ_buf.h
===================================================================
--- uspace/lib/c/include/adt/circ_buf.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
+++ uspace/lib/c/include/adt/circ_buf.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -0,0 +1,65 @@
+/*
+ * 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 Circular buffer
+ */
+
+#ifndef LIBC_CIRC_BUF_H_
+#define LIBC_CIRC_BUF_H_
+
+#include <stddef.h>
+
+/** Circular buffer */
+typedef struct {
+	/** Buffer */
+	void *buf;
+	/** Number of buffer members */
+	size_t nmemb;
+	/** Member size */
+	size_t size;
+	/** Read position */
+	size_t rp;
+	/** Write position */
+	size_t wp;
+	/** Number of used entries */
+	size_t nused;
+} circ_buf_t;
+
+extern void circ_buf_init(circ_buf_t *, void *, size_t, size_t);
+extern size_t circ_buf_nfree(circ_buf_t *);
+extern size_t circ_buf_nused(circ_buf_t *);
+extern int circ_buf_push(circ_buf_t *, const void *);
+extern int circ_buf_pop(circ_buf_t *, void *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/io/chardev_srv.h
===================================================================
--- uspace/lib/c/include/io/chardev_srv.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/lib/c/include/io/chardev_srv.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -61,4 +61,5 @@
 	int (*read)(chardev_srv_t *, void *, size_t, size_t *);
 	int (*write)(chardev_srv_t *, const void *, size_t, size_t *);
+	void (*def_handler)(chardev_srv_t *, ipc_callid_t, ipc_call_t *);
 };
 
Index: pace/lib/c/include/ipc/char.h
===================================================================
--- uspace/lib/c/include/ipc/char.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ 	(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
- * @brief Character device interface.
- */
-
-#ifndef LIBC_IPC_CHAR_H_
-#define LIBC_IPC_CHAR_H_
-
-#include <ipc/common.h>
-
-typedef enum {
-	CHAR_WRITE_BYTE = IPC_FIRST_USER_METHOD
-} char_request_t;
-
-typedef enum {
-	CHAR_NOTIF_BYTE = IPC_FIRST_USER_METHOD
-} char_notif_t;
-
-#endif
-
-/** @}
- */
Index: uspace/lib/c/include/ipc/chardev.h
===================================================================
--- uspace/lib/c/include/ipc/chardev.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/lib/c/include/ipc/chardev.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -41,6 +41,10 @@
 typedef enum {
 	CHARDEV_READ = IPC_FIRST_USER_METHOD,
-	CHARDEV_WRITE
+	CHARDEV_WRITE,
 } chardev_request_t;
+
+enum {
+	CHARDEV_LIMIT = CHARDEV_WRITE + 1
+};
 
 #endif
Index: uspace/lib/c/include/ipc/serial_ctl.h
===================================================================
--- uspace/lib/c/include/ipc/serial_ctl.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/lib/c/include/ipc/serial_ctl.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -30,5 +30,5 @@
 #define LIBC_IPC_SERIAL_CTL_H_
 
-#include <ipc/dev_iface.h>
+#include <ipc/chardev.h>
 
 /** IPC methods for getting/setting serial communication properties
@@ -41,5 +41,5 @@
  */
 typedef enum {
-	SERIAL_GET_COM_PROPS = DEV_FIRST_CUSTOM_METHOD,
+	SERIAL_GET_COM_PROPS = CHARDEV_LIMIT,
 	SERIAL_SET_COM_PROPS
 } serial_ctl_t;
Index: uspace/lib/c/test/adt/circ_buf.c
===================================================================
--- uspace/lib/c/test/adt/circ_buf.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
+++ uspace/lib/c/test/adt/circ_buf.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -0,0 +1,81 @@
+/*
+ * 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 <adt/circ_buf.h>
+#include <pcut/pcut.h>
+
+PCUT_INIT
+
+PCUT_TEST_SUITE(circ_buf);
+
+enum {
+	buffer_size = 16
+};
+
+static int buffer[buffer_size];
+
+/** Basic insertion/deletion test.
+ *
+ * Test initialization, emptiness, pushing buffer until it is full,
+ * then emptying it again.
+ */
+PCUT_TEST(push_pop)
+{
+	circ_buf_t cbuf;
+	int i;
+	int j;
+	int rc;
+
+	circ_buf_init(&cbuf, buffer, buffer_size, sizeof(int));
+
+	for (i = 0; i < buffer_size; i++) {
+		PCUT_ASSERT_INT_EQUALS(buffer_size - i, circ_buf_nfree(&cbuf));
+		PCUT_ASSERT_INT_EQUALS(i, circ_buf_nused(&cbuf));
+		rc = circ_buf_push(&cbuf, &i);
+		PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	}
+
+	rc = circ_buf_push(&cbuf, &i);
+	PCUT_ASSERT_ERRNO_VAL(EAGAIN, rc);
+
+	for (i = 0; i < buffer_size; i++) {
+		PCUT_ASSERT_INT_EQUALS(i, circ_buf_nfree(&cbuf));
+		PCUT_ASSERT_INT_EQUALS(buffer_size - i, circ_buf_nused(&cbuf));
+		rc = circ_buf_pop(&cbuf, &j);
+		PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+		PCUT_ASSERT_INT_EQUALS(i, j);
+	}
+
+	PCUT_ASSERT_INT_EQUALS(buffer_size, circ_buf_nfree(&cbuf));
+	PCUT_ASSERT_INT_EQUALS(0, circ_buf_nused(&cbuf));
+
+	rc = circ_buf_pop(&cbuf, &j);
+	PCUT_ASSERT_ERRNO_VAL(EAGAIN, rc);
+}
+
+PCUT_EXPORT(circ_buf);
Index: uspace/lib/c/test/main.c
===================================================================
--- uspace/lib/c/test/main.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/lib/c/test/main.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -32,4 +32,5 @@
 PCUT_INIT
 
+PCUT_IMPORT(circ_buf);
 PCUT_IMPORT(fibril_timer);
 PCUT_IMPORT(odict);
Index: uspace/lib/drv/Makefile
===================================================================
--- uspace/lib/drv/Makefile	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/lib/drv/Makefile	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -43,5 +43,4 @@
 	generic/remote_hw_res.c \
 	generic/remote_pio_window.c \
-	generic/remote_char_dev.c \
 	generic/remote_nic.c \
 	generic/remote_ieee80211.c \
Index: uspace/lib/drv/generic/dev_iface.c
===================================================================
--- uspace/lib/drv/generic/dev_iface.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/lib/drv/generic/dev_iface.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -42,5 +42,4 @@
 #include "remote_hw_res.h"
 #include "remote_pio_window.h"
-#include "remote_char_dev.h"
 #include "remote_clock_dev.h"
 #include "remote_led_dev.h"
@@ -62,5 +61,4 @@
 		[HW_RES_DEV_IFACE] = &remote_hw_res_iface,
 		[PIO_WINDOW_DEV_IFACE] = &remote_pio_window_iface,
-		[CHAR_DEV_IFACE] = &remote_char_dev_iface,
 		[NIC_DEV_IFACE] = &remote_nic_iface,
 		[IEEE80211_DEV_IFACE] = &remote_ieee80211_iface,
Index: pace/lib/drv/generic/private/remote_char_dev.h
===================================================================
--- uspace/lib/drv/generic/private/remote_char_dev.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ 	(revision )
@@ -1,44 +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 libdrv
- * @{
- */
-/** @file
- */
-
-#ifndef LIBDRV_REMOTE_CHAR_DEV_H_
-#define LIBDRV_REMOTE_CHAR_DEV_H_
-
-extern remote_iface_t remote_char_dev_iface;
-
-#endif
-
-/**
- * @}
- */
Index: pace/lib/drv/generic/remote_char_dev.c
===================================================================
--- uspace/lib/drv/generic/remote_char_dev.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ 	(revision )
@@ -1,246 +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 libdrv
- * @{
- */
-/** @file
- */
-
-#include <async.h>
-#include <errno.h>
-#include <macros.h>
-
-#include "ops/char_dev.h"
-#include "char_dev_iface.h"
-#include "ddf/driver.h"
-
-#define MAX_CHAR_RW_COUNT 256
-
-/** Read to or write from device.
- *
- * Helper function to read to or write from a device
- * using its character interface.
- *
- * @param sess Session to the device.
- * @param buf  Buffer for the data read from or written to the device.
- * @param size Maximum size of data (in bytes) to be read or written.
- * @param read Read from the device if true, write to it otherwise.
- *
- * @return Non-negative number of bytes actually read from or
- *         written to the device on success, negative error number
- *         otherwise.
- *
- */
-static ssize_t char_dev_rw(async_sess_t *sess, void *buf, size_t size, bool read)
-{
-	ipc_call_t answer;
-	aid_t req;
-	int ret;
-	
-	async_exch_t *exch = async_exchange_begin(sess);
-	
-	if (read) {
-		req = async_send_1(exch, DEV_IFACE_ID(CHAR_DEV_IFACE),
-		    CHAR_DEV_READ, &answer);
-		ret = async_data_read_start(exch, buf, size);
-	} else {
-		req = async_send_1(exch, DEV_IFACE_ID(CHAR_DEV_IFACE),
-		    CHAR_DEV_WRITE, &answer);
-		ret = async_data_write_start(exch, buf, size);
-	}
-	
-	async_exchange_end(exch);
-	
-	sysarg_t rc;
-	if (ret != EOK) {
-		async_wait_for(req, &rc);
-		if (rc == EOK)
-			return (ssize_t) ret;
-		
-		return (ssize_t) rc;
-	}
-	
-	async_wait_for(req, &rc);
-	
-	ret = (int) rc;
-	if (ret != EOK)
-		return (ssize_t) ret;
-	
-	return (ssize_t) IPC_GET_ARG1(answer);
-}
-
-/** Read from character device.
- *
- * @param sess Session to the device.
- * @param buf  Output buffer for the data read from the device.
- * @param size Maximum size (in bytes) of the data to be read.
- *
- * @return Non-negative number of bytes actually read from the
- *         device on success, negative error number otherwise.
- *
- */
-ssize_t char_dev_read(async_sess_t *sess, void *buf, size_t size)
-{
-	return char_dev_rw(sess, buf, size, true);
-}
-
-/** Write to character device.
- *
- * @param sess Session to the device.
- * @param buf  Input buffer containg the data to be written to the
- *             device.
- * @param size Maximum size (in bytes) of the data to be written.
- *
- * @return Non-negative number of bytes actually written to the
- *         device on success, negative error number otherwise.
- *
- */
-ssize_t char_dev_write(async_sess_t *sess, void *buf, size_t size)
-{
-	return char_dev_rw(sess, buf, size, false);
-}
-
-static void remote_char_read(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-static void remote_char_write(ddf_fun_t *, void *, ipc_callid_t, ipc_call_t *);
-
-/** Remote character interface operations. */
-static const remote_iface_func_ptr_t remote_char_dev_iface_ops[] = {
-	[CHAR_DEV_READ] = remote_char_read,
-	[CHAR_DEV_WRITE] = remote_char_write
-};
-
-/** Remote character interface structure.
- *
- * Interface for processing request from remote clients addressed to the
- * character interface.
- */
-const remote_iface_t remote_char_dev_iface = {
-	.method_count = ARRAY_SIZE(remote_char_dev_iface_ops),
-	.methods = remote_char_dev_iface_ops
-};
-
-/** Process the read request from the remote client.
- *
- * Receive the read request's parameters from the remote client and pass them
- * to the local interface. Return the result of the operation processed by the
- * local interface to the remote client.
- *
- * @param fun		The function from which the data are read.
- * @param ops		The local ops structure.
- */
-static void
-remote_char_read(ddf_fun_t *fun, void *ops, ipc_callid_t callid,
-    ipc_call_t *call)
-{
-	char_dev_ops_t *char_dev_ops = (char_dev_ops_t *) ops;
-	ipc_callid_t cid;
-	
-	size_t len;
-	if (!async_data_read_receive(&cid, &len)) {
-		/* TODO handle protocol error. */
-		async_answer_0(callid, EINVAL);
-		return;
-	}
-	
-	if (!char_dev_ops->read) {
-		async_data_read_finalize(cid, NULL, 0);
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-	
-	if (len > MAX_CHAR_RW_COUNT)
-		len = MAX_CHAR_RW_COUNT;
-	
-	char buf[MAX_CHAR_RW_COUNT];
-	int ret = (*char_dev_ops->read)(fun, buf, len);
-	
-	if (ret < 0) {
-		/* Some error occured. */
-		async_data_read_finalize(cid, buf, 0);
-		async_answer_0(callid, ret);
-		return;
-	}
-	
-	/* The operation was successful, return the number of data read. */
-	async_data_read_finalize(cid, buf, ret);
-	async_answer_1(callid, EOK, ret);
-}
-
-/** Process the write request from the remote client.
- *
- * Receive the write request's parameters from the remote client and pass them
- * to the local interface. Return the result of the operation processed by the
- * local interface to the remote client.
- *
- * @param fun		The function to which the data are written.
- * @param ops		The local ops structure.
- */
-static void
-remote_char_write(ddf_fun_t *fun, void *ops, ipc_callid_t callid,
-    ipc_call_t *call)
-{
-	char_dev_ops_t *char_dev_ops = (char_dev_ops_t *) ops;
-	ipc_callid_t cid;
-	size_t len;
-	
-	if (!async_data_write_receive(&cid, &len)) {
-		/* TODO handle protocol error. */
-		async_answer_0(callid, EINVAL);
-		return;
-	}
-	
-	if (!char_dev_ops->write) {
-		async_data_write_finalize(cid, NULL, 0);
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-	
-	if (len > MAX_CHAR_RW_COUNT)
-		len = MAX_CHAR_RW_COUNT;
-	
-	char buf[MAX_CHAR_RW_COUNT];
-	
-	async_data_write_finalize(cid, buf, len);
-	
-	int ret = (*char_dev_ops->write)(fun, buf, len);
-	if (ret < 0) {
-		/* Some error occured. */
-		async_answer_0(callid, ret);
-	} else {
-		/*
-		 * The operation was successful, return the number of data
-		 * written.
-		 */
-		async_answer_1(callid, EOK, ret);
-	}
-}
-
-/**
- * @}
- */
Index: uspace/srv/hid/input/input.c
===================================================================
--- uspace/srv/hid/input/input.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/srv/hid/input/input.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -37,30 +37,31 @@
  */
 
+#include <adt/fifo.h>
 #include <adt/list.h>
-#include <stdbool.h>
+#include <async.h>
+#include <config.h>
+#include <errno.h>
+#include <fibril.h>
 #include <fibril_synch.h>
+#include <io/chardev.h>
+#include <io/console.h>
+#include <io/keycode.h>
 #include <ipc/services.h>
 #include <ipc/input.h>
-#include <config.h>
+#include <loc.h>
+#include <ns.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <ns.h>
-#include <async.h>
-#include <errno.h>
-#include <adt/fifo.h>
-#include <io/console.h>
-#include <io/keycode.h>
-#include <loc.h>
 #include <str_error.h>
-#include <char_dev_iface.h>
-#include <fibril.h>
-#include "layout.h"
+
+#include "input.h"
 #include "kbd.h"
 #include "kbd_port.h"
 #include "kbd_ctl.h"
+#include "layout.h"
 #include "mouse.h"
 #include "mouse_proto.h"
 #include "serial.h"
-#include "input.h"
 
 #define NUM_LAYOUTS  4
@@ -536,6 +537,8 @@
 	while (true) {
 		uint8_t data;
-
-		char_dev_read(sdev->sess, &data, sizeof(data));
+		size_t nread;
+
+		chardev_read(sdev->chardev, &data, sizeof(data), &nread);
+		/* XXX Handle error */
 		kbd_push_data(sdev->kdev, data);
 	}
@@ -552,4 +555,5 @@
 {
 	bool match = false;
+	int rc;
 
 	serial_dev_t *sdev = serial_dev_new();
@@ -559,5 +563,5 @@
 	sdev->kdev->svc_id = service_id;
 	
-	int rc = loc_service_get_name(service_id, &sdev->kdev->svc_name);
+	rc = loc_service_get_name(service_id, &sdev->kdev->svc_name);
 	if (rc != EOK)
 		goto fail;
@@ -582,4 +586,12 @@
 		sdev->sess = loc_service_connect(service_id, INTERFACE_DDF,
 		    IPC_FLAG_BLOCKING);
+
+		rc = chardev_open(sdev->sess, &sdev->chardev);
+		if (rc != EOK) {
+			async_hangup(sdev->sess);
+			sdev->sess = NULL;
+			list_remove(&sdev->link);
+			goto fail;
+		}
 
 		fid_t fid = fibril_create(serial_consumer, sdev);
Index: uspace/srv/hid/input/port/chardev.c
===================================================================
--- uspace/srv/hid/input/port/chardev.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/srv/hid/input/port/chardev.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -35,8 +35,9 @@
  */
 
-#include <ipc/char.h>
 #include <async.h>
+#include <errno.h>
+#include <fibril.h>
+#include <io/chardev.h>
 #include <loc.h>
-#include <errno.h>
 #include <stdio.h>
 #include "../input.h"
@@ -44,8 +45,8 @@
 #include "../kbd.h"
 
-static void kbd_port_events(ipc_callid_t iid, ipc_call_t *icall, void *arg);
+static int kbd_port_fibril(void *);
 
 static int chardev_port_init(kbd_dev_t *);
-static void chardev_port_write(uint8_t data);
+static void chardev_port_write(uint8_t);
 
 kbd_port_ops_t chardev_port = {
@@ -56,4 +57,5 @@
 static kbd_dev_t *kbd_dev;
 static async_sess_t *dev_sess;
+static chardev_t *chardev;
 
 /** List of devices to try connecting to. */
@@ -70,6 +72,6 @@
 {
 	service_id_t service_id;
-	async_exch_t *exch;
 	unsigned int i;
+	fid_t fid;
 	int rc;
 	
@@ -96,22 +98,20 @@
 	}
 	
-	exch = async_exchange_begin(dev_sess);
-	if (exch == NULL) {
-		printf("%s: Failed starting exchange with device\n", NAME);
+	rc = chardev_open(dev_sess, &chardev);
+	if (rc != EOK) {
+		printf("%s: Failed opening character device\n", NAME);
 		async_hangup(dev_sess);
 		return ENOMEM;
 	}
 	
-	port_id_t port;
-	rc = async_create_callback_port(exch, INTERFACE_CHAR_CB, 0, 0,
-	    kbd_port_events, NULL, &port);
-	
-	async_exchange_end(exch);
-	
-	if (rc != 0) {
-		printf("%s: Failed to create callback from device\n", NAME);
+	fid = fibril_create(kbd_port_fibril, NULL);
+	if (fid == 0) {
+		printf("%s: Failed creating fibril\n", NAME);
+		chardev_close(chardev);
 		async_hangup(dev_sess);
-		return -1;
+		return ENOMEM;
 	}
+
+	fibril_add_ready(fid);
 	
 	printf("%s: Found input device '%s'\n", NAME, in_devs[i]);
@@ -121,40 +121,32 @@
 static void chardev_port_write(uint8_t data)
 {
-	async_exch_t *exch = async_exchange_begin(dev_sess);
-	if (exch == NULL) {
-		printf("%s: Failed starting exchange with device\n", NAME);
+	int rc;
+	size_t nwr;
+
+	rc = chardev_write(chardev, &data, sizeof(data), &nwr);
+	if (rc != EOK || nwr != sizeof(data)) {
+		printf("%s: Failed writing to character device\n", NAME);
 		return;
-	}
-
-	async_msg_1(exch, CHAR_WRITE_BYTE, data);
-	async_exchange_end(exch);
-}
-
-static void kbd_port_events(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	/* Ignore parameters, the connection is already opened */
-	while (true) {
-
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call)) {
-			/* TODO: Handle hangup */
-			return;
-		}
-
-		int retval = EOK;
-
-		switch (IPC_GET_IMETHOD(call)) {
-		case CHAR_NOTIF_BYTE:
-			kbd_push_data(kbd_dev, IPC_GET_ARG1(call));
-			break;
-		default:
-			retval = ENOENT;
-		}
-		async_answer_0(callid, retval);
 	}
 }
 
+static int kbd_port_fibril(void *arg)
+{
+	int rc;
+	size_t nread;
+	uint8_t b;
+
+	while (true) {
+		rc = chardev_read(chardev, &b, sizeof(b), &nread);
+		if (rc != EOK || nread != sizeof(b)) {
+			printf("%s: Error reading data", NAME);
+			continue;
+		}
+
+		kbd_push_data(kbd_dev, b);
+	}
+
+	return 0;
+}
 
 /**
Index: uspace/srv/hid/input/serial.h
===================================================================
--- uspace/srv/hid/input/serial.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/srv/hid/input/serial.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -39,4 +39,5 @@
 
 #include <async.h>
+#include <io/chardev.h>
 #include "kbd.h"
 
@@ -45,4 +46,5 @@
 	link_t link;
 	async_sess_t *sess;
+	chardev_t *chardev;
 } serial_dev_t;
 
Index: uspace/srv/hid/isdv4_tablet/isdv4.c
===================================================================
--- uspace/srv/hid/isdv4_tablet/isdv4.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/srv/hid/isdv4_tablet/isdv4.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -27,10 +27,10 @@
  */
 
-#include <char_dev_iface.h>
 #include <errno.h>
+#include <io/chardev.h>
+#include <mem.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
-#include <mem.h>
 #include <thread.h>
 
@@ -298,9 +298,12 @@
 	bool reading = true;
 	while (reading) {
-		ssize_t read = char_dev_read(state->sess, state->buf + state->buf_end,
-		    state->buf_size - state->buf_end);
-		if (read < 0)
+		size_t nread;
+		int rc;
+
+		rc = chardev_read(state->chardev, state->buf + state->buf_end,
+		    state->buf_size - state->buf_end, &nread);
+		if (rc != EOK && nread == 0)
 			return EIO;
-		state->buf_end += read;
+		state->buf_end += nread;
 
 		size_t i = 0;
@@ -357,7 +360,12 @@
 	return EOK;
 }
-static bool write_command(async_sess_t *sess, uint8_t command)
-{
-	return char_dev_write(sess, &command, 1) == 1;
+
+static bool write_command(chardev_t *chardev, uint8_t command)
+{
+	int rc;
+	size_t nwr;
+
+	rc = chardev_write(chardev, &command, 1, &nwr);
+	return rc == EOK;
 }
 
@@ -365,9 +373,22 @@
     isdv4_event_fn event_fn)
 {
+	chardev_t *chardev;
+	int rc;
+
+	rc = chardev_open(sess, &chardev);
+	if (rc != EOK)
+		return rc;
+
 	memset(state, 0, sizeof(isdv4_state_t));
+
 	state->sess = sess;
+	state->chardev = chardev;
+
 	state->buf = malloc(BUF_SIZE);
-	if (state->buf == NULL)
+	if (state->buf == NULL) {
+		chardev_close(chardev);
 		return ENOMEM;
+	}
+
 	state->buf_size = BUF_SIZE;
 	state->emit_event_fn = event_fn;
@@ -377,5 +398,5 @@
 int isdv4_init_tablet(isdv4_state_t *state)
 {
-	if (!write_command(state->sess, CMD_STOP))
+	if (!write_command(state->chardev, CMD_STOP))
 		return EIO;
 
@@ -383,5 +404,5 @@
 
 	// FIXME: Read all possible garbage before sending commands
-	if (!write_command(state->sess, CMD_QUERY_STYLUS))
+	if (!write_command(state->chardev, CMD_QUERY_STYLUS))
 		return EIO;
 
@@ -390,5 +411,5 @@
 		return rc;
 
-	if (!write_command(state->sess, CMD_QUERY_TOUCH))
+	if (!write_command(state->chardev, CMD_QUERY_TOUCH))
 		return EIO;
 
@@ -397,5 +418,5 @@
 		return rc;
 
-	if (!write_command(state->sess, CMD_START))
+	if (!write_command(state->chardev, CMD_START))
 		return EIO;
 
Index: uspace/srv/hid/isdv4_tablet/isdv4.h
===================================================================
--- uspace/srv/hid/isdv4_tablet/isdv4.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/srv/hid/isdv4_tablet/isdv4.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -31,4 +31,5 @@
 
 #include <async.h>
+#include <io/chardev.h>
 
 typedef struct isdv4_event isdv4_event_t;
@@ -58,6 +59,8 @@
 	bool finger1_pressed; /* Reported as touch button 1 */
 
-	/* Session to the serial device */
+	/** Session with the serial device */
 	async_sess_t *sess;
+	/** Character device */
+	chardev_t *chardev;
 
 	/* Receive buffer state */
@@ -66,5 +69,5 @@
 	size_t buf_end;
 
-	/* Callbacks */
+	/** Callbacks */
 	isdv4_event_fn emit_event_fn;
 } isdv4_state_t;
Index: uspace/srv/hid/output/port/chardev.c
===================================================================
--- uspace/srv/hid/output/port/chardev.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/srv/hid/output/port/chardev.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -30,15 +30,15 @@
  */
 
+#include <async.h>
+#include <config.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <io/chardev.h>
+#include <loc.h>
 #include <stddef.h>
 #include <stdint.h>
-#include <char_dev_iface.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <async.h>
-#include <fibril_synch.h>
-#include <loc.h>
-#include <errno.h>
 #include <str.h>
-#include <config.h>
 #include "../ctl/serial.h"
 #include "../output.h"
@@ -48,4 +48,5 @@
 
 static async_sess_t *sess;
+static chardev_t *chardev;
 static service_id_t serial_cat_id;
 
@@ -57,10 +58,14 @@
 {
 	uint8_t byte = (uint8_t) ch;
-	char_dev_write(sess, &byte, 1); 
+	size_t nwr;
+	chardev_write(chardev, &byte, 1, &nwr);
+	/* XXX Handle error */
 }
 
 static void chardev_control_puts(const char *str)
 {
-	char_dev_write(sess, (void *) str, str_size(str));
+	size_t nwr;
+	chardev_write(chardev, (void *) str, str_size(str), &nwr);
+	/* XXX Handle error */
 }
 
@@ -126,4 +131,12 @@
 		return;
 	}
+
+	rc = chardev_open(sess, &chardev);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&discovery_lock);
+		printf("%s: Failed opening character device\n", NAME);
+		return;
+	}
+
 	serial_init(chardev_putchar, chardev_control_puts);
 
Index: uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c
===================================================================
--- uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -27,7 +27,4 @@
  */
 
-/** @addtogroup driver_serial
- * @{
- */
 /**
  * @file
@@ -37,13 +34,13 @@
  */
 
+#include <async.h>
 #include <ddi.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <io/chardev_srv.h>
 #include <loc.h>
-#include <ipc/char.h>
-#include <async.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sysinfo.h>
-#include <errno.h>
-#include <inttypes.h>
 #include "s3c24xx_uart.h"
 
@@ -72,9 +69,17 @@
 static void s3c24xx_uart_sendb(s3c24xx_uart_t *, uint8_t);
 
+static int s3c24xx_uart_read(chardev_srv_t *, void *, size_t, size_t *);
+static int s3c24xx_uart_write(chardev_srv_t *, const void *, size_t, size_t *);
+
+static chardev_ops_t s3c24xx_uart_chardev_ops = {
+	.read = s3c24xx_uart_read,
+	.write = s3c24xx_uart_write
+};
+
 int main(int argc, char *argv[])
 {
 	printf("%s: S3C24xx on-chip UART driver\n", NAME);
 	
-	async_set_fallback_port_handler(s3c24xx_uart_connection, NULL);
+	async_set_fallback_port_handler(s3c24xx_uart_connection, uart);
 	int rc = loc_server_register(NAME);
 	if (rc != EOK) {
@@ -111,44 +116,15 @@
     void *arg)
 {
-	/* Answer the IPC_M_CONNECT_ME_TO call. */
-	async_answer_0(iid, EOK);
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		sysarg_t method = IPC_GET_IMETHOD(call);
-		
-		if (!method) {
-			/* The other side has hung up. */
-			async_answer_0(callid, EOK);
-			return;
-		}
-		
-		async_sess_t *sess =
-		    async_callback_receive_start(EXCHANGE_SERIALIZE, &call);
-		if (sess != NULL) {
-			if (uart->client_sess == NULL) {
-				uart->client_sess = sess;
-				async_answer_0(callid, EOK);
-			} else
-				async_answer_0(callid, ELIMIT);
-		} else {
-			switch (method) {
-			case CHAR_WRITE_BYTE:
-				printf(NAME ": write %" PRIun " to device\n",
-				    IPC_GET_ARG1(call));
-				s3c24xx_uart_sendb(uart, (uint8_t) IPC_GET_ARG1(call));
-				async_answer_0(callid, EOK);
-				break;
-			default:
-				async_answer_0(callid, EINVAL);
-			}
-		}
-	}
-}
+	s3c24xx_uart_t *uart = (s3c24xx_uart_t *) arg;
+
+	chardev_conn(iid, icall, &uart->cds);
+}
+
 
 static void s3c24xx_uart_irq_handler(ipc_callid_t iid, ipc_call_t *call,
     void *arg)
 {
+	int rc;
+
 	(void) iid;
 	(void) call;
@@ -159,9 +135,12 @@
 		uint32_t status = pio_read_32(&uart->io->uerstat);
 
-		if (uart->client_sess != NULL) {
-			async_exch_t *exch = async_exchange_begin(uart->client_sess);
-			async_msg_1(exch, CHAR_NOTIF_BYTE, data);
-			async_exchange_end(exch);
-		}
+		fibril_mutex_lock(&uart->buf_lock);
+
+		rc = circ_buf_push(&uart->cbuf, &data);
+		if (rc != EOK)
+			printf(NAME ": Buffer overrun\n");
+
+		fibril_mutex_unlock(&uart->buf_lock);
+		fibril_condvar_broadcast(&uart->buf_cv);
 
 		if (status != 0)
@@ -176,4 +155,8 @@
 	sysarg_t inr;
 
+	circ_buf_init(&uart->cbuf, uart->buf, s3c24xx_uart_buf_size, 1);
+	fibril_mutex_initialize(&uart->buf_lock);
+	fibril_condvar_initialize(&uart->buf_cv);
+
 	if (sysinfo_get_value("s3c24xx_uart.address.physical",
 	    &uart->paddr) != EOK)
@@ -188,5 +171,4 @@
 
 	uart->io = vaddr;
-	uart->client_sess = NULL;
 
 	printf(NAME ": device at physical address %p, inr %" PRIun ".\n",
@@ -203,4 +185,8 @@
 	    pio_read_32(&uart->io->ucon) & ~UCON_RX_INT_LEVEL);
 
+	chardev_srvs_init(&uart->cds);
+	uart->cds.ops = &s3c24xx_uart_chardev_ops;
+	uart->cds.sarg = uart;
+
 	return EOK;
 }
@@ -216,4 +202,46 @@
 }
 
+static int s3c24xx_uart_read(chardev_srv_t *srv, void *buf, size_t size,
+    size_t *nread)
+{
+	s3c24xx_uart_t *uart = (s3c24xx_uart_t *) srv->srvs->sarg;
+	size_t p;
+	uint8_t *bp = (uint8_t *) buf;
+	int rc;
+
+	fibril_mutex_lock(&uart->buf_lock);
+
+	while (circ_buf_nused(&uart->cbuf) == 0)
+		fibril_condvar_wait(&uart->buf_cv, &uart->buf_lock);
+
+	p = 0;
+	while (p < size) {
+		rc = circ_buf_pop(&uart->cbuf, &bp[p]);
+		if (rc != EOK)
+			break;
+		++p;
+	}
+
+	fibril_mutex_unlock(&uart->buf_lock);
+
+	*nread = p;
+	return EOK;
+}
+
+static int s3c24xx_uart_write(chardev_srv_t *srv, const void *data, size_t size,
+    size_t *nwr)
+{
+	s3c24xx_uart_t *uart = (s3c24xx_uart_t *) srv->srvs->sarg;
+	size_t i;
+	uint8_t *dp = (uint8_t *) data;
+
+	for (i = 0; i < size; i++)
+		s3c24xx_uart_sendb(uart, dp[i]); 
+
+	*nwr = size;
+	return EOK;
+}
+
+
 /** @}
  */
Index: uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.h
===================================================================
--- uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.h	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.h	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -38,6 +38,9 @@
 #define S3C24XX_UART_H_
 
+#include <adt/circ_buf.h>
+#include <async.h>
+#include <fibril_synch.h>
+#include <io/chardev_srv.h>
 #include <stdint.h>
-#include <async.h>
 
 /** S3C24xx UART I/O */
@@ -76,4 +79,7 @@
 #define UFCON_FIFO_ENABLE		0x01
 
+enum {
+	s3c24xx_uart_buf_size = 64
+};
 
 /** S3C24xx UART instance */
@@ -85,9 +91,18 @@
 	s3c24xx_uart_io_t *io;
 
-	/** Callback session to the client */
-	async_sess_t *client_sess;
+	/** Character device service */
+	chardev_srvs_t cds;
 
 	/** Service ID */
 	service_id_t service_id;
+
+	/** Circular buffer */
+	circ_buf_t cbuf;
+	/** Buffer */
+	uint8_t buf[s3c24xx_uart_buf_size];
+	/** Buffer lock */
+	fibril_mutex_t buf_lock;
+	/** Signal newly added data in buffer */
+	fibril_condvar_t buf_cv;
 } s3c24xx_uart_t;
 
Index: uspace/srv/net/slip/slip.c
===================================================================
--- uspace/srv/net/slip/slip.c	(revision f571ca49efe28da95a101816b820671161b895b2)
+++ uspace/srv/net/slip/slip.c	(revision ac307b25a02622848f9ee9d1bff85d6cd4c35ca2)
@@ -40,5 +40,5 @@
 #include <inet/addr.h>
 #include <inet/iplink_srv.h>
-#include <char_dev_iface.h>
+#include <io/chardev.h>
 #include <io/log.h>
 #include <errno.h>
@@ -96,29 +96,29 @@
 }
 
-static void write_flush(async_sess_t *sess)
+static void write_flush(chardev_t *chardev)
 {
 	size_t written = 0;
 
 	while (slip_send_pending > 0) {
-		ssize_t size;
-
-		size = char_dev_write(sess, &slip_send_buf[written],
-		    slip_send_pending);
-		if (size < 0) {
+		int rc;
+		size_t nwr;
+
+		rc = chardev_write(chardev, &slip_send_buf[written],
+		    slip_send_pending, &nwr);
+		if (rc != EOK) {
 			log_msg(LOG_DEFAULT, LVL_ERROR,
-			    "char_dev_write() returned %d",
-			    (int) size);
+			    "chardev_write() returned %d", rc);
 			slip_send_pending = 0;
 			break;
 		}
-		written += size;
-		slip_send_pending -= size;
-	}
-}
-
-static void write_buffered(async_sess_t *sess, uint8_t ch)
+		written += nwr;
+		slip_send_pending -= nwr;
+	}
+}
+
+static void write_buffered(chardev_t *chardev, uint8_t ch)
 {
 	if (slip_send_pending == sizeof(slip_send_buf))
-		write_flush(sess);
+		write_flush(chardev);
 	slip_send_buf[slip_send_pending++] = ch;
 }
@@ -128,5 +128,5 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "slip_send()");
 	
-	async_sess_t *sess = (async_sess_t *) srv->arg;
+	chardev_t *chardev = (chardev_t *) srv->arg;
 	uint8_t *data = sdu->data;
 	
@@ -136,24 +136,24 @@
 	 * measure for dealing with previous possible noise on the line.
 	 */
-	write_buffered(sess, SLIP_END);
+	write_buffered(chardev, SLIP_END);
 	
 	for (size_t i = 0; i < sdu->size; i++) {
 		switch (data[i]) {
 		case SLIP_END:
-			write_buffered(sess, SLIP_ESC);
-			write_buffered(sess, SLIP_ESC_END);
+			write_buffered(chardev, SLIP_ESC);
+			write_buffered(chardev, SLIP_ESC_END);
 			break;
 		case SLIP_ESC:
-			write_buffered(sess, SLIP_ESC);
-			write_buffered(sess, SLIP_ESC_ESC);
+			write_buffered(chardev, SLIP_ESC);
+			write_buffered(chardev, SLIP_ESC_ESC);
 			break;
 		default:
-			write_buffered(sess, data[i]);
+			write_buffered(chardev, data[i]);
 			break;
 		}
 	}
 	
-	write_buffered(sess, SLIP_END);
-	write_flush(sess);
+	write_buffered(chardev, SLIP_END);
+	write_flush(chardev);
 	
 	return EOK;
@@ -203,17 +203,21 @@
 }
 
-static uint8_t read_buffered(async_sess_t *sess)
+static uint8_t read_buffered(chardev_t *chardev)
 {
 	while (slip_recv_pending == 0) {
-		ssize_t size;
-
-		size = char_dev_read(sess, slip_recv_buf,
-		    sizeof(slip_recv_buf));
-		if (size < 0) {
+		int rc;
+		size_t nread;
+
+		rc = chardev_read(chardev, slip_recv_buf,
+		    sizeof(slip_recv_buf), &nread);
+		if (rc != EOK) {
 			log_msg(LOG_DEFAULT, LVL_ERROR,
-			    "char_dev_read() returned %d", (int) size);
+			    "char_dev_read() returned %d", rc);
+		}
+
+		if (nread == 0)
 			return SLIP_END;
-		}
-		slip_recv_pending = size;
+
+		slip_recv_pending = nread;
 		slip_recv_read = 0;
 	}
@@ -224,5 +228,5 @@
 static int slip_recv_fibril(void *arg)
 {
-	async_sess_t *sess = (async_sess_t *) arg;
+	chardev_t *chardev = (chardev_t *) arg;
 	static uint8_t recv_final[SLIP_MTU];
 	iplink_recv_sdu_t sdu;
@@ -234,5 +238,5 @@
 	while (true) {
 		for (sdu.size = 0; sdu.size < sizeof(recv_final); /**/) {
-			ch = read_buffered(sess);
+			ch = read_buffered(chardev);
 			switch (ch) {
 			case SLIP_END:
@@ -246,9 +250,9 @@
 
 			case SLIP_ESC:
-				ch = read_buffered(sess);
+				ch = read_buffered(chardev);
 				if (ch == SLIP_ESC_END) {
 					recv_final[sdu.size++] = SLIP_END;
 					break;
-				} else if (ch ==  SLIP_ESC_ESC) {
+				} else if (ch == SLIP_ESC_ESC) {
 					recv_final[sdu.size++] = SLIP_ESC;
 					break;
@@ -295,4 +299,6 @@
 	async_sess_t *sess_in = NULL;
 	async_sess_t *sess_out = NULL;
+	chardev_t *chardev_in = NULL;
+	chardev_t *chardev_out = NULL;
 	fid_t fid;
 	int rc;
@@ -336,5 +342,13 @@
 		return ENOENT;
 	}
-	slip_iplink.arg = sess_out;
+
+	rc = chardev_open(sess_out, &chardev_out);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR,
+		    "Failed opening character device.");
+		return ENOENT;
+	}
+
+	slip_iplink.arg = chardev_out;
 
 	sess_in = loc_service_connect(svcid, INTERFACE_DDF, 0);
@@ -347,4 +361,11 @@
 	}
 
+	rc = chardev_open(sess_in, &chardev_in);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR,
+		    "Failed opening character device.");
+		return ENOENT;
+	}
+
 	rc = loc_service_register(linkstr, &linksid);
 	if (rc != EOK) {
@@ -363,5 +384,5 @@
 	}
 
-	fid = fibril_create(slip_recv_fibril, sess_in);
+	fid = fibril_create(slip_recv_fibril, chardev_in);
 	if (!fid) {
 		log_msg(LOG_DEFAULT, LVL_ERROR,
@@ -375,6 +396,8 @@
 
 fail:
+	chardev_close(chardev_out);
 	if (sess_out)
 		async_hangup(sess_out);
+	chardev_close(chardev_in);
 	if (sess_in)
 		async_hangup(sess_in);
