Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision 070ad1248d05a7bef143a66fe673b655d873b448)
+++ uspace/lib/c/Makefile	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -106,4 +106,5 @@
 	generic/io/vsnprintf.c \
 	generic/io/printf_core.c \
+	generic/io/con_srv.c \
 	generic/io/console.c \
 	generic/io/visualizer.c \
Index: uspace/lib/c/generic/io/con_srv.c
===================================================================
--- uspace/lib/c/generic/io/con_srv.c	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
+++ uspace/lib/c/generic/io/con_srv.c	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/**
+ * @file
+ * @brief Console protocol server stub
+ */
+#include <errno.h>
+#include <ipc/console.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <io/con_srv.h>
+
+static void con_read_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	void *buf;
+	size_t size;
+	int rc;
+	ipc_callid_t rcallid;
+
+	if (!async_data_read_receive(&rcallid, &size)) {
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+
+	buf = malloc(size);
+	if (buf == NULL) {
+		async_answer_0(rcallid, ENOMEM);
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+
+	if (srv->srvs->ops->read == NULL) {
+		async_answer_0(rcallid, ENOTSUP);
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->read(srv, buf, size);
+	if (rc < 0) {
+		async_answer_0(rcallid, rc);
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	async_data_read_finalize(rcallid, buf, size);
+	free(buf);
+
+	if (rc >= 0)
+		async_answer_1(callid, EOK, rc);
+	else
+		async_answer_0(callid, rc);
+}
+
+static void con_write_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	void *data;
+	size_t size;
+	int rc;
+
+	rc = async_data_write_accept(&data, false, 0, 0, 0, &size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
+
+	if (srv->srvs->ops->write == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->write(srv, data, size);
+	free(data);
+
+	if (rc >= 0)
+		async_answer_1(callid, EOK, rc);
+	else
+		async_answer_0(callid, rc);
+}
+
+static void con_sync_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	if (srv->srvs->ops->sync == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	srv->srvs->ops->sync(srv);
+	async_answer_0(callid, EOK);
+}
+
+static void con_clear_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	if (srv->srvs->ops->clear == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	srv->srvs->ops->clear(srv);
+	async_answer_0(callid, EOK);
+}
+
+static void con_set_pos_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	sysarg_t col;
+	sysarg_t row;
+
+	col = IPC_GET_ARG1(*call);
+	row = IPC_GET_ARG2(*call);
+
+	if (srv->srvs->ops->set_pos == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	srv->srvs->ops->set_pos(srv, col, row);
+	async_answer_0(callid, EOK);
+}
+
+static void con_get_pos_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	int rc;
+	sysarg_t col;
+	sysarg_t row;
+
+	if (srv->srvs->ops->get_pos == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->get_pos(srv, &col, &row);
+	async_answer_2(callid, rc, col, row);
+}
+
+static void con_get_size_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	int rc;
+	sysarg_t cols;
+	sysarg_t rows;
+
+	if (srv->srvs->ops->get_size == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->get_size(srv, &cols, &rows);
+	async_answer_2(callid, rc, cols, rows);
+}
+
+static void con_get_color_cap_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	int rc;
+	console_caps_t ccap;
+
+	if (srv->srvs->ops->get_color_cap == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->get_color_cap(srv, &ccap);
+	async_answer_1(callid, rc, (sysarg_t)ccap);
+}
+
+static void con_set_style_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	console_style_t style;
+
+	style = IPC_GET_ARG1(*call);
+
+	if (srv->srvs->ops->set_style == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	srv->srvs->ops->set_style(srv, style);
+	async_answer_0(callid, EOK);
+}
+
+static void con_set_color_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	console_color_t bgcolor;
+	console_color_t fgcolor;
+	console_color_attr_t flags;
+
+	bgcolor = IPC_GET_ARG1(*call);
+	fgcolor = IPC_GET_ARG2(*call);
+	flags = IPC_GET_ARG3(*call);
+
+	if (srv->srvs->ops->set_color == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	srv->srvs->ops->set_color(srv, bgcolor, fgcolor, flags);
+	async_answer_0(callid, EOK);
+}
+
+static void con_set_rgb_color_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	pixel_t bgcolor;
+	pixel_t fgcolor;
+
+	bgcolor = IPC_GET_ARG1(*call);
+	fgcolor = IPC_GET_ARG2(*call);
+
+	if (srv->srvs->ops->set_rgb_color == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	srv->srvs->ops->set_rgb_color(srv, bgcolor, fgcolor);
+	async_answer_0(callid, EOK);
+}
+
+static void con_set_cursor_visibility_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	bool show;
+
+	show = IPC_GET_ARG1(*call);
+
+	if (srv->srvs->ops->set_cursor_visibility == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	srv->srvs->ops->set_cursor_visibility(srv, show);
+	async_answer_0(callid, EOK);
+}
+
+static void con_get_event_srv(con_srv_t *srv, ipc_callid_t callid,
+    ipc_call_t *call)
+{
+	int rc;
+	kbd_event_t event;
+
+	if (srv->srvs->ops->get_event == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->get_event(srv, &event);
+	async_answer_4(callid, rc, event.type, event.key, event.mods, event.c);
+}
+
+static con_srv_t *con_srv_create(con_srvs_t *srvs)
+{
+	con_srv_t *srv;
+
+	srv = calloc(1, sizeof(*srv));
+	if (srv == NULL)
+		return NULL;
+
+	srv->srvs = srvs;
+	return srv;
+}
+
+void con_srvs_init(con_srvs_t *srvs)
+{
+	srvs->ops = NULL;
+	srvs->sarg = NULL;
+	srvs->abort_timeout = 0;
+	srvs->aborted = false;
+}
+
+int con_conn(ipc_callid_t iid, ipc_call_t *icall, con_srvs_t *srvs)
+{
+	con_srv_t *srv;
+	int rc;
+
+	/* Accept the connection */
+	async_answer_0(iid, EOK);
+
+	srv = con_srv_create(srvs);
+	if (srv == NULL)
+		return ENOMEM;
+
+/*	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (sess == NULL)
+		return ENOMEM;
+
+	srv->client_sess = sess;
+*/
+	srv->client_sess = NULL;
+
+	rc = srvs->ops->open(srvs, srv);
+	if (rc != EOK)
+		return rc;
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = 0;
+
+		while (callid == 0) {
+			/* XXX Need to be able to abort immediately */
+			callid = async_get_call_timeout(&call,
+			    srvs->abort_timeout);
+
+			if (srv->srvs->aborted) {
+				if (callid != 0)
+					async_answer_0(callid, EINTR);
+				break;
+			}
+		}
+
+		if (callid == 0)
+			break;
+
+		sysarg_t method = IPC_GET_IMETHOD(call);
+
+		if (!method) {
+			/* The other side has hung up */
+			async_answer_0(callid, EOK);
+			break;
+		}
+
+		switch (method) {
+		case VFS_OUT_READ:
+			con_read_srv(srv, callid, &call);
+			break;
+		case VFS_OUT_WRITE:
+			con_write_srv(srv, callid, &call);
+			break;
+		case VFS_OUT_SYNC:
+			con_sync_srv(srv, callid, &call);
+			break;
+		case CONSOLE_CLEAR:
+			con_clear_srv(srv, callid, &call);
+			break;
+		case CONSOLE_SET_POS:
+			con_set_pos_srv(srv, callid, &call);
+			break;
+		case CONSOLE_GET_POS:
+			con_get_pos_srv(srv, callid, &call);
+			break;
+		case CONSOLE_GET_SIZE:
+			con_get_size_srv(srv, callid, &call);
+			break;
+		case CONSOLE_GET_COLOR_CAP:
+			con_get_color_cap_srv(srv, callid, &call);
+			break;
+		case CONSOLE_SET_STYLE:
+			con_set_style_srv(srv, callid, &call);
+			break;
+		case CONSOLE_SET_COLOR:
+			con_set_color_srv(srv, callid, &call);
+			break;
+		case CONSOLE_SET_RGB_COLOR:
+			con_set_rgb_color_srv(srv, callid, &call);
+			break;
+		case CONSOLE_SET_CURSOR_VISIBILITY:
+			con_set_cursor_visibility_srv(srv, callid, &call);
+			break;
+		case CONSOLE_GET_EVENT:
+			con_get_event_srv(srv, callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOTSUP);
+		}
+	}
+
+	rc = srvs->ops->close(srv);
+	free(srv);
+
+	return rc;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/io/console.c
===================================================================
--- uspace/lib/c/generic/io/console.c	(revision 070ad1248d05a7bef143a66fe673b655d873b448)
+++ uspace/lib/c/generic/io/console.c	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -38,5 +38,4 @@
 #include <async.h>
 #include <errno.h>
-#include <stdio.h>
 #include <malloc.h>
 #include <vfs/vfs_sess.h>
@@ -126,5 +125,5 @@
 {
 	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
-	async_req_1_0(exch, CONSOLE_CURSOR_VISIBILITY, (show != false));
+	async_req_1_0(exch, CONSOLE_SET_CURSOR_VISIBILITY, (show != false));
 	async_exchange_end(exch);
 }
@@ -151,5 +150,5 @@
 {
 	async_exch_t *exch = async_exchange_begin(ctrl->output_sess);
-	async_req_2_0(exch, CONSOLE_GOTO, col, row);
+	async_req_2_0(exch, CONSOLE_SET_POS, col, row);
 	async_exchange_end(exch);
 }
Index: uspace/lib/c/generic/io/output.c
===================================================================
--- uspace/lib/c/generic/io/output.c	(revision 070ad1248d05a7bef143a66fe673b655d873b448)
+++ uspace/lib/c/generic/io/output.c	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -37,4 +37,5 @@
 #include <as.h>
 #include <ipc/output.h>
+#include <io/concaps.h>
 #include <io/output.h>
 
Index: uspace/lib/c/include/io/con_srv.h
===================================================================
--- uspace/lib/c/include/io/con_srv.h	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
+++ uspace/lib/c/include/io/con_srv.h	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_CON_SRV_H_
+#define LIBC_CON_SRV_H_
+
+#include <adt/list.h>
+#include <async.h>
+#include <fibril_synch.h>
+#include <io/color.h>
+#include <io/concaps.h>
+#include <io/kbd_event.h>
+#include <io/pixel.h>
+#include <io/style.h>
+#include <bool.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+typedef struct con_ops con_ops_t;
+
+/** Service setup (per sevice) */
+typedef struct {
+	con_ops_t *ops;
+	void *sarg;
+	/** Period to check for abort */
+	suseconds_t abort_timeout;
+	bool aborted;
+} con_srvs_t;
+
+/** Server structure (per client session) */
+typedef struct {
+	con_srvs_t *srvs;
+	async_sess_t *client_sess;
+	void *carg;
+} con_srv_t;
+
+typedef struct con_ops {
+	int (*open)(con_srvs_t *, con_srv_t *);
+	int (*close)(con_srv_t *);
+	int (*read)(con_srv_t *, void *, size_t);
+	int (*write)(con_srv_t *, void *, size_t);
+	void (*sync)(con_srv_t *);
+	void (*clear)(con_srv_t *);
+	void (*set_pos)(con_srv_t *, sysarg_t col, sysarg_t row);
+	int (*get_pos)(con_srv_t *, sysarg_t *, sysarg_t *);
+	int (*get_size)(con_srv_t *, sysarg_t *, sysarg_t *);
+	int (*get_color_cap)(con_srv_t *, console_caps_t *);
+	void (*set_style)(con_srv_t *, console_style_t);
+	void (*set_color)(con_srv_t *, console_color_t, console_color_t,
+	    console_color_attr_t);
+	void (*set_rgb_color)(con_srv_t *, pixel_t, pixel_t);
+	void (*set_cursor_visibility)(con_srv_t *, bool);
+	int (*get_event)(con_srv_t *, kbd_event_t *);
+} con_ops_t;
+
+extern void con_srvs_init(con_srvs_t *);
+
+extern int con_conn(ipc_callid_t, ipc_call_t *, con_srvs_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/io/concaps.h
===================================================================
--- uspace/lib/c/include/io/concaps.h	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
+++ uspace/lib/c/include/io/concaps.h	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libcipc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_IO_CONCAPS_H_
+#define LIBC_IO_CONCAPS_H_
+
+typedef enum {
+	CONSOLE_CAP_NONE = 0,
+	CONSOLE_CAP_STYLE = 1,
+	CONSOLE_CAP_INDEXED = 2,
+	CONSOLE_CAP_RGB = 4
+} console_caps_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/io/console.h
===================================================================
--- uspace/lib/c/include/io/console.h	(revision 070ad1248d05a7bef143a66fe673b655d873b448)
+++ uspace/lib/c/include/io/console.h	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -37,4 +37,5 @@
 
 #include <sys/time.h>
+#include <io/concaps.h>
 #include <io/kbd_event.h>
 #include <io/keycode.h>
@@ -42,11 +43,4 @@
 #include <bool.h>
 #include <stdio.h>
-
-typedef enum {
-	CONSOLE_CAP_NONE = 0,
-	CONSOLE_CAP_STYLE = 1,
-	CONSOLE_CAP_INDEXED = 2,
-	CONSOLE_CAP_RGB = 4
-} console_caps_t;
 
 /** Console control structure. */
Index: uspace/lib/c/include/ipc/console.h
===================================================================
--- uspace/lib/c/include/ipc/console.h	(revision 070ad1248d05a7bef143a66fe673b655d873b448)
+++ uspace/lib/c/include/ipc/console.h	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -43,10 +43,10 @@
 	CONSOLE_GET_EVENT,
 	CONSOLE_GET_POS,
-	CONSOLE_GOTO,
+	CONSOLE_SET_POS,
 	CONSOLE_CLEAR,
 	CONSOLE_SET_STYLE,
 	CONSOLE_SET_COLOR,
 	CONSOLE_SET_RGB_COLOR,
-	CONSOLE_CURSOR_VISIBILITY
+	CONSOLE_SET_CURSOR_VISIBILITY
 } console_request_t;
 
Index: uspace/lib/gui/terminal.c
===================================================================
--- uspace/lib/gui/terminal.c	(revision 070ad1248d05a7bef143a66fe673b655d873b448)
+++ uspace/lib/gui/terminal.c	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -39,6 +39,7 @@
 #include <surface.h>
 #include <gfx/font-8x16.h>
+#include <io/con_srv.h>
+#include <io/concaps.h>
 #include <io/console.h>
-#include <ipc/console.h>
 #include <task.h>
 #include <adt/list.h>
@@ -60,4 +61,44 @@
 
 static LIST_INITIALIZE(terms);
+
+static int term_open(con_srvs_t *, con_srv_t *);
+static int term_close(con_srv_t *);
+static int term_read(con_srv_t *, void *, size_t);
+static int term_write(con_srv_t *, void *, size_t);
+static void term_sync(con_srv_t *);
+static void term_clear(con_srv_t *);
+static void term_set_pos(con_srv_t *, sysarg_t col, sysarg_t row);
+static int term_get_pos(con_srv_t *, sysarg_t *, sysarg_t *);
+static int term_get_size(con_srv_t *, sysarg_t *, sysarg_t *);
+static int term_get_color_cap(con_srv_t *, console_caps_t *);
+static void term_set_style(con_srv_t *, console_style_t);
+static void term_set_color(con_srv_t *, console_color_t, console_color_t,
+    console_color_attr_t);
+static void term_set_rgb_color(con_srv_t *, pixel_t, pixel_t);
+static void term_set_cursor_visibility(con_srv_t *, bool);
+static int term_get_event(con_srv_t *, kbd_event_t *);
+
+static con_ops_t con_ops = {
+	.open = term_open,
+	.close = term_close,
+	.read = term_read,
+	.write = term_write,
+	.sync = term_sync,
+	.clear = term_clear,
+	.set_pos = term_set_pos,
+	.get_pos = term_get_pos,
+	.get_size = term_get_size,
+	.get_color_cap = term_get_color_cap,
+	.set_style = term_set_style,
+	.set_color = term_set_color,
+	.set_rgb_color = term_set_rgb_color,
+	.set_cursor_visibility = term_set_cursor_visibility,
+	.get_event = term_get_event
+};
+
+static terminal_t *srv_to_terminal(con_srv_t *srv)
+{
+	return srv->srvs->sarg;
+}
 
 static void getterm(const char *svc, const char *app)
@@ -341,39 +382,18 @@
 }
 
-static void term_set_cursor(terminal_t *term, sysarg_t col, sysarg_t row)
-{
-	fibril_mutex_lock(&term->mtx);
-	chargrid_set_cursor(term->frontbuf, col, row);
-	fibril_mutex_unlock(&term->mtx);
-	
-	term_update(term);
-}
-
-static void term_set_cursor_visibility(terminal_t *term, bool visible)
-{
-	fibril_mutex_lock(&term->mtx);
-	chargrid_set_cursor_visibility(term->frontbuf, visible);
-	fibril_mutex_unlock(&term->mtx);
-	
-	term_update(term);
-}
-
-static void term_read(terminal_t *term, ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	char *buf = (char *) malloc(size);
-	if (buf == NULL) {
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
+static int term_open(con_srvs_t *srvs, con_srv_t *srv)
+{
+	return EOK;
+}
+
+static int term_close(con_srv_t *srv)
+{
+	return EOK;
+}
+
+static int term_read(con_srv_t *srv, void *buf, size_t size)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	uint8_t *bbuf = buf;
 	size_t pos = 0;
 	
@@ -386,5 +406,5 @@
 		/* Copy to the buffer remaining characters. */
 		while ((pos < size) && (term->char_remains_len > 0)) {
-			buf[pos] = term->char_remains[0];
+			bbuf[pos] = term->char_remains[0];
 			pos++;
 			
@@ -416,7 +436,5 @@
 	}
 	
-	(void) async_data_read_finalize(callid, buf, size);
-	async_answer_1(iid, EOK, size);
-	free(buf);
+	return size;
 }
 
@@ -449,25 +467,26 @@
 }
 
-static void term_write(terminal_t *term, ipc_callid_t iid, ipc_call_t *icall)
-{
-	void *buf;
-	size_t size;
-	int rc = async_data_write_accept(&buf, false, 0, 0, 0, &size);
-	
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
+static int term_write(con_srv_t *srv, void *data, size_t size)
+{
+	terminal_t *term = srv_to_terminal(srv);
 	
 	size_t off = 0;
 	while (off < size)
-		term_write_char(term, str_decode(buf, &off, size));
-	
-	async_answer_1(iid, EOK, size);
-	free(buf);
-}
-
-static void term_clear(terminal_t *term)
-{
+		term_write_char(term, str_decode(data, &off, size));
+	
+	return size;
+}
+
+static void term_sync(con_srv_t *srv)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	
+	term_update(term);
+}
+
+static void term_clear(con_srv_t *srv)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	
 	fibril_mutex_lock(&term->mtx);
 	chargrid_clear(term->frontbuf);
@@ -477,18 +496,50 @@
 }
 
-static void term_get_cursor(terminal_t *term, ipc_callid_t iid, ipc_call_t *icall)
-{
-	sysarg_t col;
-	sysarg_t row;
-	
-	fibril_mutex_lock(&term->mtx);
-	chargrid_get_cursor(term->frontbuf, &col, &row);
-	fibril_mutex_unlock(&term->mtx);
-	
-	async_answer_2(iid, EOK, col, row);
-}
-
-static void term_set_style(terminal_t *term, console_style_t style)
-{
+static void term_set_pos(con_srv_t *srv, sysarg_t col, sysarg_t row)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	
+	fibril_mutex_lock(&term->mtx);
+	chargrid_set_cursor(term->frontbuf, col, row);
+	fibril_mutex_unlock(&term->mtx);
+	
+	term_update(term);
+}
+
+static int term_get_pos(con_srv_t *srv, sysarg_t *col, sysarg_t *row)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	
+	fibril_mutex_lock(&term->mtx);
+	chargrid_get_cursor(term->frontbuf, col, row);
+	fibril_mutex_unlock(&term->mtx);
+	
+	return EOK;
+}
+
+static int term_get_size(con_srv_t *srv, sysarg_t *cols, sysarg_t *rows)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	
+	fibril_mutex_lock(&term->mtx);
+	*cols = term->cols;
+	*rows = term->rows;
+	fibril_mutex_unlock(&term->mtx);
+	
+	return EOK;
+}
+
+static int term_get_color_cap(con_srv_t *srv, console_caps_t *caps)
+{
+	(void) srv;
+	*caps = TERM_CAPS;
+	
+	return EOK;
+}
+
+static void term_set_style(con_srv_t *srv, console_style_t style)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	
 	fibril_mutex_lock(&term->mtx);
 	chargrid_set_style(term->frontbuf, style);
@@ -496,7 +547,9 @@
 }
 
-static void term_set_color(terminal_t *term, console_color_t bgcolor,
+static void term_set_color(con_srv_t *srv, console_color_t bgcolor,
     console_color_t fgcolor, console_color_attr_t attr)
 {
+	terminal_t *term = srv_to_terminal(srv);
+	
 	fibril_mutex_lock(&term->mtx);
 	chargrid_set_color(term->frontbuf, bgcolor, fgcolor, attr);
@@ -504,7 +557,9 @@
 }
 
-static void term_set_rgb_color(terminal_t *term, pixel_t bgcolor,
+static void term_set_rgb_color(con_srv_t *srv, pixel_t bgcolor,
     pixel_t fgcolor)
 {
+	terminal_t *term = srv_to_terminal(srv);
+	
 	fibril_mutex_lock(&term->mtx);
 	chargrid_set_rgb_color(term->frontbuf, bgcolor, fgcolor);
@@ -512,11 +567,24 @@
 }
 
-static void term_get_event(terminal_t *term, ipc_callid_t iid, ipc_call_t *icall)
-{
+static void term_set_cursor_visibility(con_srv_t *srv, bool visible)
+{
+	terminal_t *term = srv_to_terminal(srv);
+	
+	fibril_mutex_lock(&term->mtx);
+	chargrid_set_cursor_visibility(term->frontbuf, visible);
+	fibril_mutex_unlock(&term->mtx);
+	
+	term_update(term);
+}
+
+static int term_get_event(con_srv_t *srv, kbd_event_t *event)
+{
+	terminal_t *term = srv_to_terminal(srv);
 	link_t *link = prodcons_consume(&term->input_pc);
-	kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
-	
-	async_answer_4(iid, EOK, event->type, event->key, event->mods, event->c);
-	free(event);
+	kbd_event_t *kevent = list_get_instance(link, kbd_event_t, link);
+	
+	*event = *kevent;
+	free(kevent);
+	return EOK;
 }
 
@@ -612,68 +680,7 @@
 	
 	if (atomic_postinc(&term->refcnt) == 0)
-		term_set_cursor_visibility(term, true);
-	
-	/* Accept the connection */
-	async_answer_0(iid, EOK);
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			return;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case VFS_OUT_READ:
-			term_read(term, callid, &call);
-			break;
-		case VFS_OUT_WRITE:
-			term_write(term, callid, &call);
-			break;
-		case VFS_OUT_SYNC:
-			term_update(term);
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_CLEAR:
-			term_clear(term);
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_GOTO:
-			term_set_cursor(term, IPC_GET_ARG1(call), IPC_GET_ARG2(call));
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_GET_POS:
-			term_get_cursor(term, callid, &call);
-			break;
-		case CONSOLE_GET_SIZE:
-			async_answer_2(callid, EOK, term->cols, term->rows);
-			break;
-		case CONSOLE_GET_COLOR_CAP:
-			async_answer_1(callid, EOK, TERM_CAPS);
-			break;
-		case CONSOLE_SET_STYLE:
-			term_set_style(term, IPC_GET_ARG1(call));
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_SET_COLOR:
-			term_set_color(term, IPC_GET_ARG1(call), IPC_GET_ARG2(call),
-			    IPC_GET_ARG3(call));
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_SET_RGB_COLOR:
-			term_set_rgb_color(term, IPC_GET_ARG1(call), IPC_GET_ARG2(call));
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_CURSOR_VISIBILITY:
-			term_set_cursor_visibility(term, IPC_GET_ARG1(call));
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_GET_EVENT:
-			term_get_event(term, callid, &call);
-			break;
-		default:
-			async_answer_0(callid, EINVAL);
-		}
-	}
+		chargrid_set_cursor_visibility(term->frontbuf, true);
+	
+	con_conn(iid, icall, &term->srvs);
 }
 
@@ -727,4 +734,8 @@
 	
 	async_set_client_connection(term_connection);
+	con_srvs_init(&term->srvs);
+	term->srvs.ops = &con_ops;
+	term->srvs.sarg = term;
+	
 	int rc = loc_server_register(NAME);
 	if (rc != EOK) {
Index: uspace/lib/gui/terminal.h
===================================================================
--- uspace/lib/gui/terminal.h	(revision 070ad1248d05a7bef143a66fe673b655d873b448)
+++ uspace/lib/gui/terminal.h	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -41,4 +41,5 @@
 #include <font.h>
 #include <io/chargrid.h>
+#include <io/con_srv.h>
 #include <adt/list.h>
 #include <adt/prodcons.h>
@@ -66,4 +67,5 @@
 	
 	service_id_t dsid;
+	con_srvs_t srvs;
 } terminal_t;
 
Index: uspace/srv/hid/console/console.c
===================================================================
--- uspace/srv/hid/console/console.c	(revision 070ad1248d05a7bef143a66fe673b655d873b448)
+++ uspace/srv/hid/console/console.c	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -37,5 +37,4 @@
 #include <adt/prodcons.h>
 #include <io/input.h>
-#include <ipc/console.h>
 #include <ipc/vfs.h>
 #include <errno.h>
@@ -43,8 +42,8 @@
 #include <loc.h>
 #include <event.h>
+#include <io/con_srv.h>
 #include <io/kbd_event.h>
 #include <io/keycode.h>
 #include <io/chargrid.h>
-#include <io/console.h>
 #include <io/output.h>
 #include <align.h>
@@ -80,4 +79,5 @@
 	chargrid_t *frontbuf;    /**< Front buffer */
 	frontbuf_handle_t fbid;  /**< Front buffer handle */
+	con_srvs_t srvs;         /**< Console service setup */
 } console_t;
 
@@ -114,4 +114,44 @@
 };
 
+static int cons_open(con_srvs_t *, con_srv_t *);
+static int cons_close(con_srv_t *);
+static int cons_read(con_srv_t *, void *, size_t);
+static int cons_write(con_srv_t *, void *, size_t);
+static void cons_sync(con_srv_t *);
+static void cons_clear(con_srv_t *);
+static void cons_set_pos(con_srv_t *, sysarg_t col, sysarg_t row);
+static int cons_get_pos(con_srv_t *, sysarg_t *, sysarg_t *);
+static int cons_get_size(con_srv_t *, sysarg_t *, sysarg_t *);
+static int cons_get_color_cap(con_srv_t *, console_caps_t *);
+static void cons_set_style(con_srv_t *, console_style_t);
+static void cons_set_color(con_srv_t *, console_color_t, console_color_t,
+    console_color_attr_t);
+static void cons_set_rgb_color(con_srv_t *, pixel_t, pixel_t);
+static void cons_set_cursor_visibility(con_srv_t *, bool);
+static int cons_get_event(con_srv_t *, kbd_event_t *);
+
+static con_ops_t con_ops = {
+	.open = cons_open,
+	.close = cons_close,
+	.read = cons_read,
+	.write = cons_write,
+	.sync = cons_sync,
+	.clear = cons_clear,
+	.set_pos = cons_set_pos,
+	.get_pos = cons_get_pos,
+	.get_size = cons_get_size,
+	.get_color_cap = cons_get_color_cap,
+	.set_style = cons_set_style,
+	.set_color = cons_set_color,
+	.set_rgb_color = cons_set_rgb_color,
+	.set_cursor_visibility = cons_set_cursor_visibility,
+	.get_event = cons_get_event
+};
+
+static console_t *srv_to_console(con_srv_t *srv)
+{
+	return srv->srvs->sarg;
+}
+
 static void cons_update(console_t *cons)
 {
@@ -138,13 +178,4 @@
 	fibril_mutex_unlock(&cons->mtx);
 	fibril_mutex_unlock(&switch_mtx);
-}
-
-static void cons_clear(console_t *cons)
-{
-	fibril_mutex_lock(&cons->mtx);
-	chargrid_clear(cons->frontbuf);
-	fibril_mutex_unlock(&cons->mtx);
-	
-	cons_update(cons);
 }
 
@@ -288,8 +319,8 @@
 }
 
-static void cons_set_cursor(console_t *cons, sysarg_t col, sysarg_t row)
-{
-	fibril_mutex_lock(&cons->mtx);
-	chargrid_set_cursor(cons->frontbuf, col, row);
+static void cons_set_cursor_vis(console_t *cons, bool visible)
+{
+	fibril_mutex_lock(&cons->mtx);
+	chargrid_set_cursor_visibility(cons->frontbuf, visible);
 	fibril_mutex_unlock(&cons->mtx);
 	
@@ -297,61 +328,18 @@
 }
 
-static void cons_set_cursor_visibility(console_t *cons, bool visible)
-{
-	fibril_mutex_lock(&cons->mtx);
-	chargrid_set_cursor_visibility(cons->frontbuf, visible);
-	fibril_mutex_unlock(&cons->mtx);
-	
-	cons_update_cursor(cons);
-}
-
-static void cons_get_cursor(console_t *cons, ipc_callid_t iid, ipc_call_t *icall)
-{
-	sysarg_t col;
-	sysarg_t row;
-	
-	fibril_mutex_lock(&cons->mtx);
-	chargrid_get_cursor(cons->frontbuf, &col, &row);
-	fibril_mutex_unlock(&cons->mtx);
-	
-	async_answer_2(iid, EOK, col, row);
-}
-
-static void cons_write(console_t *cons, ipc_callid_t iid, ipc_call_t *icall)
-{
-	void *buf;
-	size_t size;
-	int rc = async_data_write_accept(&buf, false, 0, 0, 0, &size);
-	
-	if (rc != EOK) {
-		async_answer_0(iid, rc);
-		return;
-	}
-	
-	size_t off = 0;
-	while (off < size)
-		cons_write_char(cons, str_decode(buf, &off, size));
-	
-	async_answer_1(iid, EOK, size);
-	free(buf);
-}
-
-static void cons_read(console_t *cons, ipc_callid_t iid, ipc_call_t *icall)
-{
-	ipc_callid_t callid;
-	size_t size;
-	if (!async_data_read_receive(&callid, &size)) {
-		async_answer_0(callid, EINVAL);
-		async_answer_0(iid, EINVAL);
-		return;
-	}
-	
-	char *buf = (char *) malloc(size);
-	if (buf == NULL) {
-		async_answer_0(callid, ENOMEM);
-		async_answer_0(iid, ENOMEM);
-		return;
-	}
-	
+static int cons_open(con_srvs_t *srvs, con_srv_t *srv)
+{
+	return EOK;
+}
+
+static int cons_close(con_srv_t *srv)
+{
+	return EOK;
+}
+
+static int cons_read(con_srv_t *srv, void *buf, size_t size)
+{
+	uint8_t *bbuf = buf;
+	console_t *cons = srv_to_console(srv);
 	size_t pos = 0;
 	
@@ -364,5 +352,5 @@
 		/* Copy to the buffer remaining characters. */
 		while ((pos < size) && (cons->char_remains_len > 0)) {
-			buf[pos] = cons->char_remains[0];
+			bbuf[pos] = cons->char_remains[0];
 			pos++;
 			
@@ -389,12 +377,85 @@
 		}
 	}
-	
-	(void) async_data_read_finalize(callid, buf, size);
-	async_answer_1(iid, EOK, size);
-	free(buf);
-}
-
-static void cons_set_style(console_t *cons, console_style_t style)
-{
+
+	return size;
+}
+
+static int cons_write(con_srv_t *srv, void *data, size_t size)
+{
+	console_t *cons = srv_to_console(srv);
+
+	size_t off = 0;
+	while (off < size)
+		cons_write_char(cons, str_decode(data, &off, size));
+	return size;
+}
+
+static void cons_sync(con_srv_t *srv)
+{
+	console_t *cons = srv_to_console(srv);
+	
+	cons_update(cons);
+}
+
+static void cons_clear(con_srv_t *srv)
+{
+	console_t *cons = srv_to_console(srv);
+	
+	fibril_mutex_lock(&cons->mtx);
+	chargrid_clear(cons->frontbuf);
+	fibril_mutex_unlock(&cons->mtx);
+	
+	cons_update(cons);
+}
+
+static void cons_set_pos(con_srv_t *srv, sysarg_t col, sysarg_t row)
+{
+	console_t *cons = srv_to_console(srv);
+	
+	fibril_mutex_lock(&cons->mtx);
+	chargrid_set_cursor(cons->frontbuf, col, row);
+	fibril_mutex_unlock(&cons->mtx);
+	
+	cons_update_cursor(cons);
+}
+
+static int cons_get_pos(con_srv_t *srv, sysarg_t *col, sysarg_t *row)
+{
+	console_t *cons = srv_to_console(srv);
+	
+	fibril_mutex_lock(&cons->mtx);
+	chargrid_get_cursor(cons->frontbuf, col, row);
+	fibril_mutex_unlock(&cons->mtx);
+	
+	return EOK;
+}
+
+static int cons_get_size(con_srv_t *srv, sysarg_t *cols, sysarg_t *rows)
+{
+	console_t *cons = srv_to_console(srv);
+	
+	fibril_mutex_lock(&cons->mtx);
+	*cols = cons->cols;
+	*rows = cons->rows;
+	fibril_mutex_unlock(&cons->mtx);
+	
+	return EOK;
+}
+
+static int cons_get_color_cap(con_srv_t *srv, console_caps_t *ccaps)
+{
+	console_t *cons = srv_to_console(srv);
+	
+	fibril_mutex_lock(&cons->mtx);
+	*ccaps = cons->ccaps;
+	fibril_mutex_unlock(&cons->mtx);
+	
+	return EOK;
+}
+
+static void cons_set_style(con_srv_t *srv, console_style_t style)
+{
+	console_t *cons = srv_to_console(srv);
+	
 	fibril_mutex_lock(&cons->mtx);
 	chargrid_set_style(cons->frontbuf, style);
@@ -402,7 +463,9 @@
 }
 
-static void cons_set_color(console_t *cons, console_color_t bgcolor,
+static void cons_set_color(con_srv_t *srv, console_color_t bgcolor,
     console_color_t fgcolor, console_color_attr_t attr)
 {
+	console_t *cons = srv_to_console(srv);
+	
 	fibril_mutex_lock(&cons->mtx);
 	chargrid_set_color(cons->frontbuf, bgcolor, fgcolor, attr);
@@ -410,7 +473,9 @@
 }
 
-static void cons_set_rgb_color(console_t *cons, pixel_t bgcolor,
+static void cons_set_rgb_color(con_srv_t *srv, pixel_t bgcolor,
     pixel_t fgcolor)
 {
+	console_t *cons = srv_to_console(srv);
+	
 	fibril_mutex_lock(&cons->mtx);
 	chargrid_set_rgb_color(cons->frontbuf, bgcolor, fgcolor);
@@ -418,11 +483,20 @@
 }
 
-static void cons_get_event(console_t *cons, ipc_callid_t iid, ipc_call_t *icall)
-{
+static void cons_set_cursor_visibility(con_srv_t *srv, bool visible)
+{
+	console_t *cons = srv_to_console(srv);
+	
+	cons_set_cursor_vis(cons, visible);
+}
+
+static int cons_get_event(con_srv_t *srv, kbd_event_t *event)
+{
+	console_t *cons = srv_to_console(srv);
 	link_t *link = prodcons_consume(&cons->input_pc);
-	kbd_event_t *event = list_get_instance(link, kbd_event_t, link);
-	
-	async_answer_4(iid, EOK, event->type, event->key, event->mods, event->c);
-	free(event);
+	kbd_event_t *kevent = list_get_instance(link, kbd_event_t, link);
+	
+	*event = *kevent;
+	free(kevent);
+	return EOK;
 }
 
@@ -447,69 +521,9 @@
 	
 	if (atomic_postinc(&cons->refcnt) == 0)
-		cons_set_cursor_visibility(cons, true);
-	
-	/* Accept the connection */
-	async_answer_0(iid, EOK);
-	
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = async_get_call(&call);
-		
-		if (!IPC_GET_IMETHOD(call))
-			return;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case VFS_OUT_READ:
-			cons_read(cons, callid, &call);
-			break;
-		case VFS_OUT_WRITE:
-			cons_write(cons, callid, &call);
-			break;
-		case VFS_OUT_SYNC:
-			cons_update(cons);
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_CLEAR:
-			cons_clear(cons);
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_GOTO:
-			cons_set_cursor(cons, IPC_GET_ARG1(call), IPC_GET_ARG2(call));
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_GET_POS:
-			cons_get_cursor(cons, callid, &call);
-			break;
-		case CONSOLE_GET_SIZE:
-			async_answer_2(callid, EOK, cons->cols, cons->rows);
-			break;
-		case CONSOLE_GET_COLOR_CAP:
-			async_answer_1(callid, EOK, cons->ccaps);
-			break;
-		case CONSOLE_SET_STYLE:
-			cons_set_style(cons, IPC_GET_ARG1(call));
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_SET_COLOR:
-			cons_set_color(cons, IPC_GET_ARG1(call), IPC_GET_ARG2(call),
-			    IPC_GET_ARG3(call));
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_SET_RGB_COLOR:
-			cons_set_rgb_color(cons, IPC_GET_ARG1(call), IPC_GET_ARG2(call));
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_CURSOR_VISIBILITY:
-			cons_set_cursor_visibility(cons, IPC_GET_ARG1(call));
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_GET_EVENT:
-			cons_get_event(cons, callid, &call);
-			break;
-		default:
-			async_answer_0(callid, EINVAL);
-		}
-	}
-}
+		cons_set_cursor_vis(cons, true);
+	
+	con_conn(iid, icall, &cons->srvs);
+}
+
 
 static int input_connect(const char *svc)
@@ -624,4 +638,8 @@
 		}
 		
+		con_srvs_init(&consoles[i].srvs);
+		consoles[i].srvs.ops = &con_ops;
+		consoles[i].srvs.sarg = &consoles[i];
+		
 		char vc[LOC_NAME_MAXLEN + 1];
 		snprintf(vc, LOC_NAME_MAXLEN, "%s/vc%zu", NAMESPACE, i);
Index: uspace/srv/hid/remcons/remcons.c
===================================================================
--- uspace/srv/hid/remcons/remcons.c	(revision 070ad1248d05a7bef143a66fe673b655d873b448)
+++ uspace/srv/hid/remcons/remcons.c	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -34,10 +34,8 @@
 
 #include <async.h>
+#include <errno.h>
+#include <io/con_srv.h>
 #include <stdio.h>
-#include <adt/prodcons.h>
-#include <ipc/input.h>
-#include <ipc/console.h>
-#include <ipc/vfs.h>
-#include <errno.h>
+#include <stdlib.h>
 #include <str_error.h>
 #include <loc.h>
@@ -45,6 +43,4 @@
 #include <io/keycode.h>
 #include <align.h>
-#include <malloc.h>
-#include <as.h>
 #include <fibril_synch.h>
 #include <task.h>
@@ -75,116 +71,131 @@
     sizeof(telnet_force_character_mode_command) / sizeof(telnet_cmd_t);
 
-
-/** Handling client requests (VFS and console interface).
- *
- * @param user Telnet user the requests belong to.
- */
-static void client_connection_message_loop(telnet_user_t *user)
-{
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid = 0;
-
-		/*
-		 * The getterm task might terminate while we are here,
-		 * waiting for a call. Also, the socket might be closed
-		 * meanwhile.
-		 * We want to detect this situation early, so we use a
-		 * timeout variant of async_get_call().
-		 */
-		while (callid == 0) {
-			callid = async_get_call_timeout(&call, 1000);
-
-			if (telnet_user_is_zombie(user)) {
-				if (callid != 0) {
-					async_answer_0(callid, EINTR);
-				}
-				return;
-			}
-		}
-		
-		if (!IPC_GET_IMETHOD(call)) {
-			return;
-		}
-
-		switch (IPC_GET_IMETHOD(call)) {
-		case CONSOLE_GET_SIZE:
-			async_answer_2(callid, EOK, 100, 1);
-			break;
-		case CONSOLE_GET_POS:
-			fibril_mutex_lock(&user->guard);
-			async_answer_2(callid, EOK, user->cursor_x, 0);
-			fibril_mutex_unlock(&user->guard);
-			break;
-		case CONSOLE_GET_EVENT: {
-			kbd_event_t event;
-			int rc = telnet_user_get_next_keyboard_event(user, &event);
-			if (rc != EOK) {
-				/* Silently ignore. */
-				async_answer_0(callid, EOK);
-				break;
-			}
-			async_answer_4(callid, EOK, event.type, event.key, event.mods, event.c);
-			break;
-		}
-		case CONSOLE_GOTO: {
-			int new_x = IPC_GET_ARG1(call);
-			telnet_user_update_cursor_x(user, new_x);
-			async_answer_0(callid, ENOTSUP);
-			break;
-		}
-		case VFS_OUT_READ:
-			async_answer_0(callid, ENOTSUP);
-			break;
-		case VFS_OUT_WRITE: {
-			uint8_t *buf;
-			size_t size;
-			int rc = async_data_write_accept((void **)&buf, false, 0, 0, 0, &size);
-
-			if (rc != EOK) {
-				async_answer_0(callid, rc);
-				break;
-			}
-
-			rc = telnet_user_send_data(user, buf, size);
-			free(buf);
-
-			if (rc != EOK) {
-				async_answer_0(callid, rc);
-				break;
-			}
-
-			async_answer_1(callid, EOK, size);
-			break;
-		}
-		case VFS_OUT_SYNC:
-			async_answer_0(callid, EOK);
-			break;
-		case CONSOLE_CLEAR:
-			async_answer_0(callid, EOK);
-			break;
-
-		case CONSOLE_GET_COLOR_CAP:
-			async_answer_1(callid, EOK, CONSOLE_CAP_NONE);
-			break;
-		case CONSOLE_SET_STYLE:
-			async_answer_0(callid, ENOTSUP);
-			break;
-		case CONSOLE_SET_COLOR:
-			async_answer_0(callid, ENOTSUP);
-			break;
-		case CONSOLE_SET_RGB_COLOR:
-			async_answer_0(callid, ENOTSUP);
-			break;
-
-		case CONSOLE_CURSOR_VISIBILITY:
-			async_answer_0(callid, ENOTSUP);
-			break;
-
-		default:
-			async_answer_0(callid, EINVAL);
-			break;
-		}
-	}
+static int remcons_open(con_srvs_t *, con_srv_t *);
+static int remcons_close(con_srv_t *);
+static int remcons_write(con_srv_t *, void *, size_t);
+static void remcons_sync(con_srv_t *);
+static void remcons_clear(con_srv_t *);
+static void remcons_set_pos(con_srv_t *, sysarg_t col, sysarg_t row);
+static int remcons_get_pos(con_srv_t *, sysarg_t *, sysarg_t *);
+static int remcons_get_size(con_srv_t *, sysarg_t *, sysarg_t *);
+static int remcons_get_color_cap(con_srv_t *, console_caps_t *);
+static int remcons_get_event(con_srv_t *, kbd_event_t *);
+
+static con_ops_t con_ops = {
+	.open = remcons_open,
+	.close = remcons_close,
+	.read = NULL,
+	.write = remcons_write,
+	.sync = remcons_sync,
+	.clear = remcons_clear,
+	.set_pos = remcons_set_pos,
+	.get_pos = remcons_get_pos,
+	.get_size = remcons_get_size,
+	.get_color_cap = remcons_get_color_cap,
+	.set_style = NULL,
+	.set_color = NULL,
+	.set_rgb_color = NULL,
+	.set_cursor_visibility = NULL,
+	.get_event = remcons_get_event
+};
+
+static telnet_user_t *srv_to_user(con_srv_t *srv)
+{
+	return srv->srvs->sarg;
+}
+
+static int remcons_open(con_srvs_t *srvs, con_srv_t *srv)
+{
+	telnet_user_t *user = srv_to_user(srv);
+
+	telnet_user_log(user, "New client connected (%p).", srv);
+
+	/* Force character mode. */
+	send(user->socket, (void *)telnet_force_character_mode_command,
+	    telnet_force_character_mode_command_count, 0);
+
+	return EOK;
+}
+
+static int remcons_close(con_srv_t *srv)
+{
+	telnet_user_t *user = srv_to_user(srv);
+
+	telnet_user_notify_client_disconnected(user);
+	telnet_user_log(user, "Client disconnected (%p).", srv);
+
+	return EOK;
+}
+
+static int remcons_write(con_srv_t *srv, void *data, size_t size)
+{
+	telnet_user_t *user = srv_to_user(srv);
+	int rc;
+
+	rc = telnet_user_send_data(user, data, size);
+	if (rc != EOK)
+		return rc;
+
+	return size;
+}
+
+static void remcons_sync(con_srv_t *srv)
+{
+	(void) srv;
+}
+
+static void remcons_clear(con_srv_t *srv)
+{
+	(void) srv;
+}
+
+static void remcons_set_pos(con_srv_t *srv, sysarg_t col, sysarg_t row)
+{
+	telnet_user_t *user = srv_to_user(srv);
+
+	telnet_user_update_cursor_x(user, col);
+}
+
+static int remcons_get_pos(con_srv_t *srv, sysarg_t *col, sysarg_t *row)
+{
+	telnet_user_t *user = srv_to_user(srv);
+
+	*col = user->cursor_x;
+	*row = 0;
+
+	return EOK;
+}
+
+static int remcons_get_size(con_srv_t *srv, sysarg_t *cols, sysarg_t *rows)
+{
+	(void) srv;
+
+	*cols = 100;
+	*rows = 1;
+
+	return EOK;
+}
+
+static int remcons_get_color_cap(con_srv_t *srv, console_caps_t *ccaps)
+{
+	(void) srv;
+	*ccaps = CONSOLE_CAP_NONE;
+
+	return EOK;
+}
+
+static int remcons_get_event(con_srv_t *srv, kbd_event_t *event)
+{
+	telnet_user_t *user = srv_to_user(srv);
+	int rc;
+
+	rc = telnet_user_get_next_keyboard_event(user, event);
+	if (rc != EOK) {
+		/* XXX What? */
+		memset(event, 0, sizeof(*event));
+		return EOK;
+	}
+
+	return EOK;
 }
 
@@ -198,17 +209,7 @@
 		return;
 	}
-	async_answer_0(iid, EOK);
-
-	telnet_user_log(user, "New client connected (%" PRIxn").", iid);
-
-	/* Force character mode. */
-	send(user->socket, (void *)telnet_force_character_mode_command,
-	    telnet_force_character_mode_command_count, 0);
 
 	/* Handle messages. */
-	client_connection_message_loop(user);
-
-	telnet_user_notify_client_disconnected(user);
-	telnet_user_log(user, "Client disconnected (%" PRIxn").", iid);
+	con_conn(iid, icall, &user->srvs);
 }
 
@@ -232,4 +233,5 @@
 		fibril_mutex_lock(&user->guard);
 		user->task_finished = true;
+		user->srvs.aborted = true;
 		fibril_condvar_signal(&user->refcount_cv);
 		fibril_mutex_unlock(&user->guard);
@@ -251,4 +253,5 @@
 	fibril_mutex_lock(&user->guard);
 	user->task_finished = true;
+	user->srvs.aborted = true;
 	fibril_condvar_signal(&user->refcount_cv);
 	fibril_mutex_unlock(&user->guard);
@@ -295,4 +298,5 @@
 			closesocket(user->socket);
 			user->socket_closed = true;
+			user->srvs.aborted = true;
 			continue;
 		} else if (user->socket_closed) {
@@ -380,4 +384,11 @@
 		assert(user);
 
+		con_srvs_init(&user->srvs);
+		user->srvs.ops = &con_ops;
+		user->srvs.sarg = user;
+		user->srvs.abort_timeout = 1000;
+
+		telnet_user_add(user);
+
 		fid_t fid = fibril_create(network_user_fibril, user);
 		assert(fid);
Index: uspace/srv/hid/remcons/user.c
===================================================================
--- uspace/srv/hid/remcons/user.c	(revision 070ad1248d05a7bef143a66fe673b655d873b448)
+++ uspace/srv/hid/remcons/user.c	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -35,7 +35,4 @@
 #include <stdio.h>
 #include <adt/prodcons.h>
-#include <ipc/input.h>
-#include <ipc/console.h>
-#include <ipc/vfs.h>
 #include <errno.h>
 #include <str_error.h>
@@ -95,12 +92,14 @@
 	user->locsrv_connection_count = 0;
 
-
+	user->cursor_x = 0;
+
+	return user;
+}
+
+void telnet_user_add(telnet_user_t *user)
+{
 	fibril_mutex_lock(&users_guard);
 	list_append(&user->link, &users);
 	fibril_mutex_unlock(&users_guard);
-
-	user->cursor_x = 0;
-
-	return user;
 }
 
@@ -199,4 +198,5 @@
 		if ((recv_length == 0) || (recv_length == ENOTCONN)) {
 			user->socket_closed = true;
+			user->srvs.aborted = true;
 			return ENOENT;
 		}
Index: uspace/srv/hid/remcons/user.h
===================================================================
--- uspace/srv/hid/remcons/user.h	(revision 070ad1248d05a7bef143a66fe673b655d873b448)
+++ uspace/srv/hid/remcons/user.h	(revision 5d94b16cbc3039fadf35402057e9f7dff1917037)
@@ -36,6 +36,8 @@
 #define TELNET_USER_H_
 
+#include <adt/prodcons.h>
 #include <fibril_synch.h>
 #include <inttypes.h>
+#include <io/con_srv.h>
 #include "remcons.h"
 
@@ -55,4 +57,6 @@
 	/** Path name of the service. */
 	char *service_name;
+	/** Console service setup */
+	con_srvs_t srvs;
 
 	/** Producer-consumer of kbd_event_t. */
@@ -77,4 +81,5 @@
 
 extern telnet_user_t *telnet_user_create(int);
+extern void telnet_user_add(telnet_user_t *);
 extern void telnet_user_destroy(telnet_user_t *);
 extern telnet_user_t *telnet_user_get_for_client_connection(service_id_t);
