Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ boot/Makefile.common	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -108,4 +108,5 @@
 	$(USPACE_PATH)/srv/fs/ext4fs/ext4fs \
 	$(USPACE_PATH)/srv/hid/remcons/remcons \
+	$(USPACE_PATH)/srv/hid/isdv4_tablet/isdv4_tablet \
 	$(USPACE_PATH)/srv/net/ethip/ethip \
 	$(USPACE_PATH)/srv/net/inetsrv/inetsrv \
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/Makefile	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -102,4 +102,5 @@
 	srv/hid/console \
 	srv/hid/s3c24xx_ts \
+	srv/hid/isdv4_tablet \
 	srv/hid/fb \
 	srv/hid/input \
Index: uspace/app/edit/edit.c
===================================================================
--- uspace/app/edit/edit.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/app/edit/edit.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -842,6 +842,6 @@
 		/* Fill until the end of display area. */
 
-		if (str_length(row_buf) < (unsigned) scr_columns)
-			fill = scr_columns - str_length(row_buf);
+		if ((unsigned)s_column - 1 < scr_columns)
+			fill = scr_columns - (s_column - 1);
 		else
 			fill = 0;
Index: uspace/app/sportdmp/sportdmp.c
===================================================================
--- uspace/app/sportdmp/sportdmp.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/app/sportdmp/sportdmp.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -140,10 +140,7 @@
 		ssize_t i;
 		for (i = 0; i < read; i++) {
-			if ((buf[i] >= 32) && (buf[i] < 128))
-				putchar((wchar_t) buf[i]);
-			else
-				putchar('.');
-			fflush(stdout);
+			printf("%02hhx ", buf[i]);
 		}
+		fflush(stdout);
 	}
 	
Index: uspace/drv/bus/usb/usbmast/main.c
===================================================================
--- uspace/drv/bus/usb/usbmast/main.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/drv/bus/usb/usbmast/main.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -82,5 +82,5 @@
     void *arg);
 
-static int usbmast_bd_open(bd_srv_t *);
+static int usbmast_bd_open(bd_srvs_t *, bd_srv_t *);
 static int usbmast_bd_close(bd_srv_t *);
 static int usbmast_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
@@ -100,5 +100,5 @@
 static usbmast_fun_t *bd_srv_usbmast(bd_srv_t *bd)
 {
-	return (usbmast_fun_t *)bd->arg;
+	return (usbmast_fun_t *)bd->srvs->sarg;
 }
 
@@ -240,7 +240,7 @@
 	mfun->lun = lun;
 
-	bd_srv_init(&mfun->bd);
-	mfun->bd.ops = &usbmast_bd_ops;
-	mfun->bd.arg = mfun;
+	bd_srvs_init(&mfun->bds);
+	mfun->bds.ops = &usbmast_bd_ops;
+	mfun->bds.sarg = mfun;
 
 	/* Set up a connection handler. */
@@ -311,9 +311,9 @@
 
 	mfun = (usbmast_fun_t *) ((ddf_fun_t *)arg)->driver_data;
-	bd_conn(iid, icall, &mfun->bd);
+	bd_conn(iid, icall, &mfun->bds);
 }
 
 /** Open device. */
-static int usbmast_bd_open(bd_srv_t *bd)
+static int usbmast_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
 {
 	return EOK;
Index: uspace/drv/bus/usb/usbmast/usbmast.h
===================================================================
--- uspace/drv/bus/usb/usbmast/usbmast.h	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/drv/bus/usb/usbmast/usbmast.h	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -69,6 +69,6 @@
 	/** Block size in bytes */
 	size_t block_size;
-	/** Block device server structure */
-	bd_srv_t bd;
+	/** Block device service structure */
+	bd_srvs_t bds;
 } usbmast_fun_t;
 
Index: uspace/drv/char/ns8250/cyclic_buffer.h
===================================================================
--- uspace/drv/char/ns8250/cyclic_buffer.h	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/drv/char/ns8250/cyclic_buffer.h	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -36,5 +36,5 @@
 #define CYCLIC_BUFFER_H_
 
-#define BUF_LEN 256
+#define BUF_LEN 4096
 
 typedef struct cyclic_buffer {
Index: uspace/drv/char/ns8250/ns8250.c
===================================================================
--- uspace/drv/char/ns8250/ns8250.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/drv/char/ns8250/ns8250.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -82,4 +82,6 @@
 /** Interrupt ID Register definition. */
 #define	NS8250_IID_ACTIVE	(1 << 0)
+#define NS8250_IID_CAUSE_MASK 0x0e
+#define NS8250_IID_CAUSE_RXSTATUS 0x06
 
 /** FIFO Control Register definition. */
@@ -179,4 +181,6 @@
 	/** The fibril mutex for synchronizing the access to the device. */
 	fibril_mutex_t mutex;
+	/** Indicates that some data has become available */
+	fibril_condvar_t input_buffer_available;
 	/** True if device is removed. */
 	bool removed;
@@ -238,7 +242,11 @@
 {
 	ns8250_t *ns = NS8250(fun);
-	int ret = EOK;
+	int ret = 0;
+	
+	if (count == 0) return 0;
 	
 	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);
@@ -460,5 +468,5 @@
 {
 	/* Interrupt when data received. */
-	pio_write_8(&regs->ier, NS8250_IER_RXREADY);
+	pio_write_8(&regs->ier, NS8250_IER_RXREADY | NS8250_IER_RXSTATUS);
 	pio_write_8(&regs->mcr, NS8250_MCR_DTR | NS8250_MCR_RTS 
 	    | NS8250_MCR_OUT2);
@@ -499,4 +507,7 @@
 	async_exchange_end(exch);
 
+	/* Read LSR to clear possible previous LSR interrupt */
+	pio_read_8(&ns->regs->lsr);
+
 	/* Enable interrupt on the serial port. */
 	ns8250_port_interrupts_enable(ns->regs);
@@ -695,8 +706,11 @@
 	/* 8 bits, no parity, two stop bits. */
 	ns8250_port_set_com_props(ns->regs, SERIAL_NO_PARITY, 8, 2);
-	/* Enable FIFO, clear them, with 14-byte threshold. */
+	/*
+	 * Enable FIFO, clear them, with 4-byte threshold for greater
+	 * reliability.
+	 */
 	pio_write_8(&ns->regs->iid, NS8250_FCR_FIFOENABLE
-	    | NS8250_FCR_RXFIFORESET | NS8250_FCR_TXFIFORESET 
-	    | NS8250_FCR_RXTRIGGERLOW | NS8250_FCR_RXTRIGGERHI);
+	    | NS8250_FCR_RXFIFORESET | NS8250_FCR_TXFIFORESET
+	    | NS8250_FCR_RXTRIGGERLOW);
 	/*
 	 * RTS/DSR set (Request to Send and Data Terminal Ready lines enabled),
@@ -731,7 +745,6 @@
 	bool cont = true;
 	
+	fibril_mutex_lock(&ns->mutex);
 	while (cont) {
-		fibril_mutex_lock(&ns->mutex);
-		
 		cont = ns8250_received(regs);
 		if (cont) {
@@ -739,24 +752,28 @@
 			
 			if (ns->client_connected) {
+				bool buf_was_empty = buf_is_empty(&ns->input_buffer);
 				if (!buf_push_back(&ns->input_buffer, val)) {
 					ddf_msg(LVL_WARN, "Buffer overflow on "
 					    "%s.", ns->dev->name);
+					break;
 				} else {
 					ddf_msg(LVL_DEBUG2, "Character %c saved "
 					    "to the buffer of %s.",
 					    val, ns->dev->name);
+					if (buf_was_empty)
+						fibril_condvar_broadcast(&ns->input_buffer_available);
 				}
 			}
 		}
-		
-		fibril_mutex_unlock(&ns->mutex);
-		fibril_yield();
-	}
+	}
+	fibril_mutex_unlock(&ns->mutex);
+	fibril_yield();
 }
 
 /** The interrupt handler.
  *
- * The serial port is initialized to interrupt when some data come, so the
- * interrupt is handled by reading the incomming data.
+ * The serial port is initialized to interrupt when some data come or line
+ * status register changes, so the interrupt is handled by reading the incoming
+ * data and reading the line status register.
  *
  * @param dev		The serial port device.
@@ -765,5 +782,15 @@
     ipc_call_t *icall)
 {
-	ns8250_read_from_device(NS8250_FROM_DEV(dev));
+	ns8250_t *ns = NS8250_FROM_DEV(dev);
+
+	uint8_t iir = pio_read_8(&ns->regs->iid);
+	if ((iir & NS8250_IID_CAUSE_MASK) == NS8250_IID_CAUSE_RXSTATUS) {
+		uint8_t lsr = pio_read_8(&ns->regs->lsr);
+		if (lsr & NS8250_LSR_OE) {
+			ddf_msg(LVL_WARN, "Overrun error on %s", ns->dev->name);
+		}
+	}
+	
+	ns8250_read_from_device(ns);
 }
 
@@ -811,4 +838,5 @@
 	
 	fibril_mutex_initialize(&ns->mutex);
+	fibril_condvar_initialize(&ns->input_buffer_available);
 	ns->dev = dev;
 	
@@ -1053,5 +1081,5 @@
 static void ns8250_init(void)
 {
-	ddf_log_init(NAME, LVL_ERROR);
+	ddf_log_init(NAME, LVL_WARN);
 	
 	ns8250_dev_ops.open = &ns8250_open;
Index: uspace/lib/c/generic/bd_srv.c
===================================================================
--- uspace/lib/c/generic/bd_srv.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/lib/c/generic/bd_srv.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -67,5 +67,5 @@
 	}
 
-	if (srv->ops->read_blocks == NULL) {
+	if (srv->srvs->ops->read_blocks == NULL) {
 		async_answer_0(rcallid, ENOTSUP);
 		async_answer_0(callid, ENOTSUP);
@@ -73,5 +73,5 @@
 	}
 
-	rc = srv->ops->read_blocks(srv, ba, cnt, buf, size);
+	rc = srv->srvs->ops->read_blocks(srv, ba, cnt, buf, size);
 	if (rc != EOK) {
 		async_answer_0(rcallid, ENOMEM);
@@ -109,5 +109,5 @@
 	}
 
-	if (srv->ops->read_toc == NULL) {
+	if (srv->srvs->ops->read_toc == NULL) {
 		async_answer_0(rcallid, ENOTSUP);
 		async_answer_0(callid, ENOTSUP);
@@ -115,5 +115,5 @@
 	}
 
-	rc = srv->ops->read_toc(srv, session, buf, size);
+	rc = srv->srvs->ops->read_toc(srv, session, buf, size);
 	if (rc != EOK) {
 		async_answer_0(rcallid, ENOMEM);
@@ -146,10 +146,10 @@
 	}
 
-	if (srv->ops->write_blocks == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	rc = srv->ops->write_blocks(srv, ba, cnt, data, size);
+	if (srv->srvs->ops->write_blocks == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->write_blocks(srv, ba, cnt, data, size);
 	free(data);
 	async_answer_0(callid, rc);
@@ -162,10 +162,10 @@
 	size_t block_size;
 
-	if (srv->ops->get_block_size == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	rc = srv->ops->get_block_size(srv, &block_size);
+	if (srv->srvs->ops->get_block_size == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->get_block_size(srv, &block_size);
 	async_answer_1(callid, rc, block_size);
 }
@@ -177,39 +177,42 @@
 	aoff64_t num_blocks;
 
-	if (srv->ops->get_num_blocks == NULL) {
-		async_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	rc = srv->ops->get_num_blocks(srv, &num_blocks);
+	if (srv->srvs->ops->get_num_blocks == NULL) {
+		async_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	rc = srv->srvs->ops->get_num_blocks(srv, &num_blocks);
 	async_answer_2(callid, rc, LOWER32(num_blocks), UPPER32(num_blocks));
 }
 
-void bd_srv_init(bd_srv_t *srv)
-{
-	fibril_mutex_initialize(&srv->lock);
-	srv->connected = false;
-	srv->ops = NULL;
-	srv->arg = NULL;
-	srv->client_sess = NULL;
-}
-
-int bd_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	bd_srv_t *srv = (bd_srv_t *)arg;
-	int rc;
-
-	fibril_mutex_lock(&srv->lock);
-	if (srv->connected) {
-		fibril_mutex_unlock(&srv->lock);
-		async_answer_0(iid, EBUSY);
-		return EBUSY;
-	}
-
-	srv->connected = true;
-	fibril_mutex_unlock(&srv->lock);
+static bd_srv_t *bd_srv_create(bd_srvs_t *srvs)
+{
+	bd_srv_t *srv;
+
+	srv = calloc(1, sizeof(srv));
+	if (srv == NULL)
+		return NULL;
+
+	srv->srvs = srvs;
+	return srv;
+}
+
+void bd_srvs_init(bd_srvs_t *srvs)
+{
+	srvs->ops = NULL;
+	srvs->sarg = NULL;
+}
+
+int bd_conn(ipc_callid_t iid, ipc_call_t *icall, bd_srvs_t *srvs)
+{
+	bd_srv_t *srv;
+	int rc;
 
 	/* Accept the connection */
 	async_answer_0(iid, EOK);
+
+	srv = bd_srv_create(srvs);
+	if (srv == NULL)
+		return ENOMEM;
 
 	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
@@ -219,5 +222,5 @@
 	srv->client_sess = sess;
 
-	rc = srv->ops->open(srv);
+	rc = srvs->ops->open(srvs, srv);
 	if (rc != EOK)
 		return rc;
@@ -230,7 +233,4 @@
 		if (!method) {
 			/* The other side has hung up */
-			fibril_mutex_lock(&srv->lock);
-			srv->connected = false;
-			fibril_mutex_unlock(&srv->lock);
 			async_answer_0(callid, EOK);
 			break;
@@ -258,5 +258,8 @@
 	}
 
-	return srv->ops->close(srv);
+	rc = srvs->ops->close(srv);
+	free(srv);
+
+	return rc;
 }
 
Index: uspace/lib/c/include/bd_srv.h
===================================================================
--- uspace/lib/c/include/bd_srv.h	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/lib/c/include/bd_srv.h	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -36,4 +36,5 @@
 #define LIBC_BD_SRV_H_
 
+#include <adt/list.h>
 #include <async.h>
 #include <fibril_synch.h>
@@ -43,14 +44,19 @@
 typedef struct bd_ops bd_ops_t;
 
+/** Service setup (per sevice) */
 typedef struct {
-	fibril_mutex_t lock;
-	bool connected;
 	bd_ops_t *ops;
-	void *arg;
+	void *sarg;
+} bd_srvs_t;
+
+/** Server structure (per client session) */
+typedef struct {
+	bd_srvs_t *srvs;
 	async_sess_t *client_sess;
+	void *carg;
 } bd_srv_t;
 
 typedef struct bd_ops {
-	int (*open)(bd_srv_t *);
+	int (*open)(bd_srvs_t *, bd_srv_t *);
 	int (*close)(bd_srv_t *);
 	int (*read_blocks)(bd_srv_t *, aoff64_t, size_t, void *, size_t);
@@ -61,7 +67,7 @@
 } bd_ops_t;
 
-extern void bd_srv_init(bd_srv_t *);
+extern void bd_srvs_init(bd_srvs_t *);
 
-extern int bd_conn(ipc_callid_t, ipc_call_t *, void *);
+extern int bd_conn(ipc_callid_t, ipc_call_t *, bd_srvs_t *);
 
 #endif
Index: uspace/lib/c/include/ipc/input.h
===================================================================
--- uspace/lib/c/include/ipc/input.h	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/lib/c/include/ipc/input.h	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -46,4 +46,5 @@
 	INPUT_EVENT_KEY = IPC_FIRST_USER_METHOD,
 	INPUT_EVENT_MOVE,
+	INPUT_EVENT_ABS_MOVE,
 	INPUT_EVENT_BUTTON
 } input_notif_t;
Index: uspace/lib/c/include/ipc/mouseev.h
===================================================================
--- uspace/lib/c/include/ipc/mouseev.h	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/lib/c/include/ipc/mouseev.h	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -47,4 +47,5 @@
 typedef enum {
 	MOUSEEV_MOVE_EVENT = IPC_FIRST_USER_METHOD,
+	MOUSEEV_ABS_MOVE_EVENT,
 	MOUSEEV_BUTTON_EVENT
 } mouseev_notif_t;
Index: uspace/srv/bd/ata_bd/ata_bd.c
===================================================================
--- uspace/srv/bd/ata_bd/ata_bd.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/bd/ata_bd/ata_bd.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -104,5 +104,5 @@
 static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *);
 
-static int ata_bd_open(bd_srv_t *);
+static int ata_bd_open(bd_srvs_t *, bd_srv_t *);
 static int ata_bd_close(bd_srv_t *);
 static int ata_bd_read_blocks(bd_srv_t *, uint64_t ba, size_t cnt, void *buf,
@@ -146,5 +146,5 @@
 static disk_t *bd_srv_disk(bd_srv_t *bd)
 {
-	return (disk_t *)bd->arg;
+	return (disk_t *)bd->srvs->sarg;
 }
 
@@ -312,5 +312,5 @@
 	}
 
-	bd_conn(iid, icall, &ata_disk[disk_id].bd);
+	bd_conn(iid, icall, &ata_disk[disk_id].bds);
 }
 
@@ -336,7 +336,7 @@
 	fibril_mutex_initialize(&d->lock);
 
-	bd_srv_init(&d->bd);
-	d->bd.ops = &ata_bd_ops;
-	d->bd.arg = d;
+	bd_srvs_init(&d->bds);
+	d->bds.ops = &ata_bd_ops;
+	d->bds.sarg = d;
 
 	/* Try identify command. */
@@ -467,5 +467,5 @@
 }
 
-static int ata_bd_open(bd_srv_t *bd)
+static int ata_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
 {
 	return EOK;
Index: uspace/srv/bd/ata_bd/ata_bd.h
===================================================================
--- uspace/srv/bd/ata_bd/ata_bd.h	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/bd/ata_bd/ata_bd.h	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -119,5 +119,5 @@
 	service_id_t service_id;
 	int disk_id;
-	bd_srv_t bd;
+	bd_srvs_t bds;
 } disk_t;
 
Index: uspace/srv/bd/file_bd/file_bd.c
===================================================================
--- uspace/srv/bd/file_bd/file_bd.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/bd/file_bd/file_bd.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -62,5 +62,5 @@
 
 static service_id_t service_id;
-static bd_srv_t bd_srv;
+static bd_srvs_t bd_srvs;
 static fibril_mutex_t dev_lock;
 
@@ -69,5 +69,5 @@
 static void file_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *);
 
-static int file_bd_open(bd_srv_t *);
+static int file_bd_open(bd_srvs_t *, bd_srv_t *);
 static int file_bd_close(bd_srv_t *);
 static int file_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
@@ -154,6 +154,6 @@
 static int file_bd_init(const char *fname)
 {
-	bd_srv_init(&bd_srv);
-	bd_srv.ops = &file_bd_ops;
+	bd_srvs_init(&bd_srvs);
+	bd_srvs.ops = &file_bd_ops;
 	
 	async_set_client_connection(file_bd_connection);
@@ -188,9 +188,9 @@
 static void file_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
-	bd_conn(iid, icall, &bd_srv);
+	bd_conn(iid, icall, &bd_srvs);
 }
 
 /** Open device. */
-static int file_bd_open(bd_srv_t *bd)
+static int file_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
 {
 	return EOK;
Index: uspace/srv/bd/gxe_bd/gxe_bd.c
===================================================================
--- uspace/srv/bd/gxe_bd/gxe_bd.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/bd/gxe_bd/gxe_bd.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -88,6 +88,6 @@
 /** GXE block device soft state */
 typedef struct {
-	/** Block device server structure */
-	bd_srv_t bd;
+	/** Block device service structure */
+	bd_srvs_t bds;
 	int disk_id;
 } gxe_bd_t;
@@ -109,5 +109,5 @@
 static int gxe_bd_write_block(int disk_id, uint64_t ba, const void *buf);
 
-static int gxe_bd_open(bd_srv_t *);
+static int gxe_bd_open(bd_srvs_t *, bd_srv_t *);
 static int gxe_bd_close(bd_srv_t *);
 static int gxe_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
@@ -127,5 +127,5 @@
 static gxe_bd_t *bd_srv_gxe(bd_srv_t *bd)
 {
-	return (gxe_bd_t *)bd->arg;
+	return (gxe_bd_t *)bd->srvs->sarg;
 }
 
@@ -166,7 +166,7 @@
 		char name[16];
 		
-		bd_srv_init(&gxe_bd[i].bd);
-		gxe_bd[i].bd.ops = &gxe_bd_ops;
-		gxe_bd[i].bd.arg = (void *)&gxe_bd[i];
+		bd_srvs_init(&gxe_bd[i].bds);
+		gxe_bd[i].bds.ops = &gxe_bd_ops;
+		gxe_bd[i].bds.sarg = (void *)&gxe_bd[i];
 		
 		snprintf(name, 16, "%s/disk%u", NAMESPACE, i);
@@ -203,9 +203,9 @@
 	}
 
-	bd_conn(iid, icall, &gxe_bd[disk_id].bd);
+	bd_conn(iid, icall, &gxe_bd[disk_id].bds);
 }
 
 /** Open device. */
-static int gxe_bd_open(bd_srv_t *bd)
+static int gxe_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
 {
 	return EOK;
Index: uspace/srv/bd/part/guid_part/guid_part.c
===================================================================
--- uspace/srv/bd/part/guid_part/guid_part.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/bd/part/guid_part/guid_part.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -83,6 +83,6 @@
 	/** Service representing the partition (outbound device) */
 	service_id_t dsid;
-	/** Block device server structure */
-	bd_srv_t bd;
+	/** Block device service structure */
+	bd_srvs_t bds;
 	/** Points to next partition structure. */
 	struct part *next;
@@ -104,5 +104,5 @@
 static int gpt_bsa_translate(part_t *p, aoff64_t ba, size_t cnt, aoff64_t *gba);
 
-static int gpt_bd_open(bd_srv_t *);
+static int gpt_bd_open(bd_srvs_t *, bd_srv_t *);
 static int gpt_bd_close(bd_srv_t *);
 static int gpt_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
@@ -122,5 +122,5 @@
 static part_t *bd_srv_part(bd_srv_t *bd)
 {
-	return (part_t *)bd->arg;
+	return (part_t *)bd->srvs->sarg;
 }
 
@@ -325,7 +325,7 @@
 	}
 
-	bd_srv_init(&part->bd);
-	part->bd.ops = &gpt_bd_ops;
-	part->bd.arg = part;
+	bd_srvs_init(&part->bds);
+	part->bds.ops = &gpt_bd_ops;
+	part->bds.sarg = part;
 
 	part->dsid = 0;
@@ -357,9 +357,9 @@
 	assert(part->present == true);
 
-	bd_conn(iid, icall, &part->bd);
+	bd_conn(iid, icall, &part->bds);
 }
 
 /** Open device. */
-static int gpt_bd_open(bd_srv_t *bd)
+static int gpt_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
 {
 	return EOK;
Index: uspace/srv/bd/part/mbr_part/mbr_part.c
===================================================================
--- uspace/srv/bd/part/mbr_part/mbr_part.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/bd/part/mbr_part/mbr_part.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -100,6 +100,6 @@
 	/** Device representing the partition (outbound device) */
 	service_id_t dsid;
-	/** Block device server structure */
-	bd_srv_t bd;
+	/** Block device service sturcture */
+	bd_srvs_t bds;
 	/** Points to next partition structure. */
 	struct part *next;
@@ -154,5 +154,5 @@
 static int mbr_bsa_translate(part_t *p, uint64_t ba, size_t cnt, uint64_t *gba);
 
-static int mbr_bd_open(bd_srv_t *);
+static int mbr_bd_open(bd_srvs_t *, bd_srv_t *);
 static int mbr_bd_close(bd_srv_t *);
 static int mbr_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
@@ -172,5 +172,5 @@
 static part_t *bd_srv_part(bd_srv_t *bd)
 {
-	return (part_t *)bd->arg;
+	return (part_t *)bd->srvs->sarg;
 }
 
@@ -402,7 +402,7 @@
 	part->present = (pte->ptype != PT_UNUSED) ? true : false;
 
-	bd_srv_init(&part->bd);
-	part->bd.ops = &mbr_bd_ops;
-	part->bd.arg = part;
+	bd_srvs_init(&part->bds);
+	part->bds.ops = &mbr_bd_ops;
+	part->bds.sarg = part;
 
 	part->dsid = 0;
@@ -433,9 +433,9 @@
 
 	assert(part->present == true);
-	bd_conn(iid, icall, &part->bd);
+	bd_conn(iid, icall, &part->bds);
 }
 
 /** Open device. */
-static int mbr_bd_open(bd_srv_t *bd)
+static int mbr_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
 {
 	return EOK;
Index: uspace/srv/bd/rd/rd.c
===================================================================
--- uspace/srv/bd/rd/rd.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/bd/rd/rd.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -68,5 +68,5 @@
 static const size_t block_size = 512;
 
-static int rd_open(bd_srv_t *);
+static int rd_open(bd_srvs_t *, bd_srv_t *);
 static int rd_close(bd_srv_t *);
 static int rd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
@@ -93,13 +93,13 @@
 };
 
-static bd_srv_t bd_srv;
+static bd_srvs_t bd_srvs;
 
 static void rd_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
-	bd_conn(iid, icall, &bd_srv);
+	bd_conn(iid, icall, &bd_srvs);
 }
 
 /** Open device. */
-static int rd_open(bd_srv_t *bd)
+static int rd_open(bd_srvs_t *bds, bd_srv_t *bd)
 {
 	return EOK;
@@ -175,6 +175,6 @@
 	    (void *) addr_phys, size);
 	
-	bd_srv_init(&bd_srv);
-	bd_srv.ops = &rd_bd_ops;
+	bd_srvs_init(&bd_srvs);
+	bd_srvs.ops = &rd_bd_ops;
 	
 	async_set_client_connection(rd_client_conn);
Index: uspace/srv/bd/sata_bd/sata_bd.c
===================================================================
--- uspace/srv/bd/sata_bd/sata_bd.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/bd/sata_bd/sata_bd.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -57,5 +57,5 @@
 static int disk_count;
 
-static int sata_bd_open(bd_srv_t *);
+static int sata_bd_open(bd_srvs_t *, bd_srv_t *);
 static int sata_bd_close(bd_srv_t *);
 static int sata_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
@@ -75,5 +75,5 @@
 static sata_bd_dev_t *bd_srv_sata(bd_srv_t *bd)
 {
-	return (sata_bd_dev_t *)bd->arg;
+	return (sata_bd_dev_t *)bd->srvs->sarg;
 }
 
@@ -104,7 +104,7 @@
 		ahci_get_num_blocks(disk[disk_count].sess, &disk[disk_count].blocks);
 		
-		bd_srv_init(&disk[disk_count].bd);
-		disk[disk_count].bd.ops = &sata_bd_ops;
-		disk[disk_count].bd.arg = &disk[disk_count];
+		bd_srvs_init(&disk[disk_count].bds);
+		disk[disk_count].bds.ops = &sata_bd_ops;
+		disk[disk_count].bds.sarg = &disk[disk_count];
 		
 		printf("Device %s - %s , blocks: %lu, block_size: %lu\n", 
@@ -183,9 +183,9 @@
 	}
 
-	bd_conn(iid, icall, &disk[disk_id].bd);
+	bd_conn(iid, icall, &disk[disk_id].bds);
 }
 
 /** Open device. */
-static int sata_bd_open(bd_srv_t *bd)
+static int sata_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
 {
 	return EOK;
Index: uspace/srv/bd/sata_bd/sata_bd.h
===================================================================
--- uspace/srv/bd/sata_bd/sata_bd.h	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/bd/sata_bd/sata_bd.h	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -58,5 +58,5 @@
 	size_t block_size;
 	/** Block device server structure */
-	bd_srv_t bd;
+	bd_srvs_t bds;
 } sata_bd_dev_t;
 
Index: uspace/srv/hid/console/console.c
===================================================================
--- uspace/srv/hid/console/console.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/hid/console/console.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -383,4 +383,15 @@
 	
 	fb_pointer_update(fb_sess, mouse.x, mouse.y, true);
+}
+
+static void cons_mouse_abs_move(sysarg_t x, sysarg_t y,
+    sysarg_t max_x, sysarg_t max_y)
+{
+	if (max_x && max_y) {
+		mouse.x = limit(x * xres / max_x, 0, xres);
+		mouse.y = limit(y * yres / max_y, 0, yres);
+		
+		fb_pointer_update(fb_sess, mouse.x, mouse.y, true);
+	}
 }
 
@@ -503,4 +514,9 @@
 			async_answer_0(callid, EOK);
 			break;
+		case INPUT_EVENT_ABS_MOVE:
+			cons_mouse_abs_move(IPC_GET_ARG1(call), IPC_GET_ARG2(call),
+			    IPC_GET_ARG3(call), IPC_GET_ARG4(call));
+			async_answer_0(callid, EOK);
+			break;
 		case INPUT_EVENT_BUTTON:
 			/* Got pointer button press/release event */
Index: uspace/srv/hid/input/generic/input.c
===================================================================
--- uspace/srv/hid/input/generic/input.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/hid/input/generic/input.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -189,4 +189,15 @@
 	}
 	async_exchange_end(exch);
+}
+
+/** Mouse pointer has moved in absolute mode. */
+void mouse_push_event_abs_move(mouse_dev_t *mdev, unsigned int x, unsigned int y,
+    unsigned int max_x, unsigned int max_y)
+{
+	if (max_x && max_y) {
+		async_exch_t *exch = async_exchange_begin(client_sess);
+		async_msg_4(exch, INPUT_EVENT_ABS_MOVE, x, y, max_x, max_y);
+		async_exchange_end(exch);
+	}
 }
 
Index: uspace/srv/hid/input/include/mouse.h
===================================================================
--- uspace/srv/hid/input/include/mouse.h	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/hid/input/include/mouse.h	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -63,4 +63,6 @@
 extern void mouse_push_data(mouse_dev_t *, sysarg_t);
 extern void mouse_push_event_move(mouse_dev_t *, int, int, int);
+extern void mouse_push_event_abs_move(mouse_dev_t *, unsigned int, unsigned int,
+    unsigned int, unsigned int);
 extern void mouse_push_event_button(mouse_dev_t *, int, int);
 
Index: uspace/srv/hid/input/proto/mousedev.c
===================================================================
--- uspace/srv/hid/input/proto/mousedev.c	(revision f7ea54007c430389f32534f3b3ec662f2dad8fe7)
+++ uspace/srv/hid/input/proto/mousedev.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -96,4 +96,10 @@
 			retval = EOK;
 			break;
+		case MOUSEEV_ABS_MOVE_EVENT:
+			mouse_push_event_abs_move(mousedev->mouse_dev,
+				IPC_GET_ARG1(call), IPC_GET_ARG2(call),
+				IPC_GET_ARG3(call), IPC_GET_ARG4(call));
+			retval = EOK;
+			break;
 		case MOUSEEV_BUTTON_EVENT:
 			mouse_push_event_button(mousedev->mouse_dev,
Index: uspace/srv/hid/isdv4_tablet/Makefile
===================================================================
--- uspace/srv/hid/isdv4_tablet/Makefile	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
+++ uspace/srv/hid/isdv4_tablet/Makefile	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2012 Martin Sucha
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+USPACE_PREFIX = ../../..
+#LIBS = 
+#EXTRA_CFLAGS = 
+BINARY = isdv4_tablet
+
+SOURCES = \
+	main.c \
+	isdv4.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/hid/isdv4_tablet/isdv4.c
===================================================================
--- uspace/srv/hid/isdv4_tablet/isdv4.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
+++ uspace/srv/hid/isdv4_tablet/isdv4.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2012 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <device/char_dev.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <mem.h>
+
+#include "isdv4.h"
+
+#define BUF_SIZE 64
+
+#define START_OF_PACKET 128
+#define CONTROL_PACKET 64
+#define TOUCH_EVENT 16
+#define FINGER1 1
+#define FINGER2 2
+#define TIP 1
+#define BUTTON1 2
+#define BUTTON2 4
+#define PROXIMITY 32
+
+#define CMD_START '1'
+#define CMD_STOP '0'
+#define CMD_QUERY_STYLUS '*'
+#define CMD_QUERY_TOUCH '%'
+
+/* packet_consumer_fn(uint8_t *packet, size_t size, isdv4_state_t *state,
+   void *data)
+   return true if reading of packets should continue */
+typedef bool (*packet_consumer_fn)(uint8_t *, size_t, isdv4_state_t *);
+
+static void isdv4_event_init(isdv4_event_t *event)
+{
+	memset(event, 0, sizeof(isdv4_event_t));
+}
+
+/**
+ * Parse event packet and emit events
+ * @return true if reading of packets should continue
+ */
+static bool parse_event(uint8_t *packet, size_t size, isdv4_state_t *state)
+{
+	if (size < 1)
+		return false;
+
+	bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
+	if (control_packet)
+		return true;
+
+	/* This is an event initiated by the device */
+	isdv4_event_t event;
+	isdv4_event_init(&event);
+
+	if (packet[0] & TOUCH_EVENT) {
+		if (size != 5)
+			return true;
+
+		/* This is a touch event */
+		bool finger1 = (packet[0] & FINGER1) > 0;
+		event.x = ((packet[1] & 127) << 7) | (packet[2] & 127);
+		event.y = ((packet[3] & 127) << 7) | (packet[4] & 127);
+		event.source = TOUCH;
+
+		if (!state->stylus_in_proximity) {
+			if (!finger1 && state->finger1_pressed) {
+				state->finger1_pressed = false;
+
+				event.type = RELEASE;
+				event.button = 1;
+				state->emit_event_fn(&event);
+			}
+			else if (finger1 && !state->finger1_pressed) {
+				state->finger1_pressed = true;
+
+				event.type = PRESS;
+				event.button = 1;
+				state->emit_event_fn(&event);
+			}
+			else {
+				event.type = MOVE;
+				event.button = 1;
+				state->emit_event_fn(&event);
+			}
+		}
+	}
+	else {
+		if (size != 9)
+			return true;
+
+		/* This is a stylus event */
+		bool tip = packet[0] & TIP;
+		bool button1 = packet[0] & BUTTON1;
+		bool button2 = packet[0] & BUTTON2;
+		bool proximity = packet[0] & PROXIMITY;
+		event.x = ((packet[1] & 127) << 7) | (packet[2] & 124) | ((packet[6] >> 5) & 3);
+		event.y = ((packet[3] & 127) << 7) | (packet[4] & 124) | ((packet[6] >> 3) & 3);
+		event.pressure = (packet[5] & 127) | ((packet[6] & 7) << 7);
+
+		if (proximity && !state->stylus_in_proximity) {
+			/* Stylus came into proximity */
+			state->stylus_in_proximity = true;
+			state->stylus_is_eraser = !tip && button2;
+			event.source = (state->stylus_is_eraser ? STYLUS_ERASER : STYLUS_TIP);
+			event.type = PROXIMITY_IN;
+			state->emit_event_fn(&event);
+		}
+		else if (!proximity && state->stylus_in_proximity) {
+			/* Stylus came out of proximity */
+			state->stylus_in_proximity = false;
+			event.source = (state->stylus_is_eraser ? STYLUS_ERASER : STYLUS_TIP);
+			event.type = PROXIMITY_OUT;
+			state->emit_event_fn(&event);
+		}
+		else {
+			/* Proximity state didn't change, but we need to check if it is still eraser */
+			if (state->stylus_is_eraser && !button2) {
+				event.type = PROXIMITY_OUT;
+				event.source = STYLUS_ERASER;
+				state->emit_event_fn(&event);
+				event.type = PROXIMITY_IN;
+				event.source = STYLUS_TIP;
+				state->emit_event_fn(&event);
+				state->stylus_is_eraser = false;
+			}
+			else if (!state->stylus_is_eraser && !tip && button2) {
+				event.type = PROXIMITY_OUT;
+				event.source = STYLUS_TIP;
+				state->emit_event_fn(&event);
+				event.type = PROXIMITY_IN;
+				event.source = STYLUS_ERASER;
+				state->emit_event_fn(&event);
+				state->stylus_is_eraser = true;
+			}
+		}
+
+		if (!state->stylus_is_eraser) {
+			if (tip && !state->tip_pressed) {
+				state->tip_pressed = true;
+				event.type = PRESS;
+				event.source = STYLUS_TIP;
+				event.button = 1;
+				state->emit_event_fn(&event);
+			}
+			else if (!tip && state->tip_pressed) {
+				state->tip_pressed = false;
+				event.type = RELEASE;
+				event.source = STYLUS_TIP;
+				event.button = 1;
+				state->emit_event_fn(&event);
+			}
+			if (button1 && !state->button1_pressed) {
+				state->button1_pressed = true;
+				event.type = PRESS;
+				event.source = STYLUS_TIP;
+				event.button = 2;
+				state->emit_event_fn(&event);
+			}
+			else if (!button1 && state->button1_pressed) {
+				state->button1_pressed = false;
+				event.type = RELEASE;
+				event.source = STYLUS_TIP;
+				event.button = 2;
+				state->emit_event_fn(&event);
+			}
+			if (button2 && !state->button2_pressed) {
+				state->button2_pressed = true;
+				event.type = PRESS;
+				event.source = STYLUS_TIP;
+				event.button = 3;
+				state->emit_event_fn(&event);
+			}
+			else if (!button2 && state->button2_pressed) {
+				state->button2_pressed = false;
+				event.type = RELEASE;
+				event.source = STYLUS_TIP;
+				event.button = 3;
+				state->emit_event_fn(&event);
+			}
+			event.type = MOVE;
+			event.source = STYLUS_TIP;
+			event.button = 0;
+			state->emit_event_fn(&event);
+		}
+		else {
+			if (tip && !state->tip_pressed) {
+				state->tip_pressed = true;
+				event.type = PRESS;
+				event.source = STYLUS_ERASER;
+				event.button = 1;
+				state->emit_event_fn(&event);
+			}
+			else if (!tip && state->tip_pressed) {
+				state->tip_pressed = false;
+				event.type = RELEASE;
+				event.source = STYLUS_ERASER;
+				event.button = 1;
+				state->emit_event_fn(&event);
+			}
+			event.type = MOVE;
+			event.source = STYLUS_ERASER;
+			event.button = 0;
+			state->emit_event_fn(&event);
+		}
+	}
+
+	return true;
+}
+
+static bool parse_response_stylus(uint8_t *packet, size_t size,
+    isdv4_state_t *state)
+{
+	if (size < 1)
+		return false;
+
+	bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
+	if (!control_packet)
+		return true;
+
+	if (size != 11)
+		return false;
+
+	state->stylus_max_x = ((packet[1] & 127) << 7) | (packet[2] & 124) |
+	    ((packet[6] >> 5) & 3);
+	state->stylus_max_y = ((packet[3] & 127) << 7) | (packet[4] & 124) |
+	    ((packet[6] >> 3) & 3);
+	state->stylus_max_pressure = (packet[5] & 63) | ((packet[6] & 7) << 7);
+	state->stylus_max_xtilt = packet[8] & 127;
+	state->stylus_max_ytilt = packet[7] & 127;
+	state->stylus_tilt_supported = (state->stylus_max_xtilt &&
+	    state->stylus_max_ytilt);
+
+	return false;
+}
+
+static bool parse_response_touch(uint8_t *packet, size_t size,
+    isdv4_state_t *state)
+{
+	if (size < 1)
+		return false;
+
+	bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
+	if (!control_packet)
+		return true;
+
+	if (size != 11)
+		return false;
+
+	state->touch_type = (packet[0] & 63);
+
+	unsigned int touch_resolution = packet[1] & 127;
+	state->touch_max_x = ((packet[2] >> 5) & 3) | ((packet[3] & 127) << 7) |
+	    (packet[4] & 124);
+	state->touch_max_y = ((packet[2] >> 3) & 3) | ((packet[5] & 127) << 7) |
+	    (packet[6] & 124);
+	
+	if (touch_resolution == 0)
+		touch_resolution = 10;
+
+	if (state->touch_max_x == 0 || state->touch_max_y == 0) {
+		state->touch_max_x = (1 << touch_resolution);
+		state->touch_max_y = (1 << touch_resolution);
+	}
+
+	return false;
+}
+
+static int read_packets(isdv4_state_t *state, packet_consumer_fn consumer)
+{
+	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)
+			return EIO;
+		state->buf_end += read;
+
+		size_t i = 0;
+
+		/* Skip data until a start of packet is found */
+		while (i < state->buf_end && (state->buf[i] & START_OF_PACKET) == 0) i++;
+
+		size_t start = i;
+		size_t end = i;
+		size_t processed_end = i;
+
+		/* Process packets one by one */
+		while (reading && i < state->buf_end) {
+			/* Determine the packet length */
+			size_t packet_remaining;
+			if (state->buf[i] & CONTROL_PACKET) {
+				packet_remaining = 11;
+			}
+			else if (state->buf[i] & TOUCH_EVENT) {
+				packet_remaining = 5;
+			}
+			else {
+				packet_remaining = 9;
+			}
+
+			/* Find the end of the packet */
+			i++; /* We need to skip the first byte with START_OF_PACKET set */
+			packet_remaining--; 
+			while (packet_remaining > 0 && i < state->buf_end &&
+			    (state->buf[i] & START_OF_PACKET) == 0) {
+				i++;
+				packet_remaining--;
+			}
+			end = i;
+
+			/* If we have whole packet, process it */
+			if (end > start && packet_remaining == 0) {
+				reading = consumer(state->buf + start, end - start, state);
+				start = end;
+				processed_end = end;
+			}
+		}
+
+		if (processed_end == 0 && state->buf_end == state->buf_size) {
+			/* Packet too large, throw it away */
+			state->buf_end = 0;
+		}
+
+		/* Shift the buffer contents to the left */
+		size_t unprocessed_len = state->buf_end - processed_end;
+		memcpy(state->buf, state->buf + processed_end, unprocessed_len);
+		state->buf_end = unprocessed_len;
+	}
+	return EOK;
+}
+static bool write_command(async_sess_t *sess, uint8_t command)
+{
+	return char_dev_write(sess, &command, 1) == 1;
+}
+
+int isdv4_init(isdv4_state_t *state, async_sess_t *sess,
+    isdv4_event_fn event_fn)
+{
+	memset(state, 0, sizeof(isdv4_state_t));
+	state->sess = sess;
+	state->buf = malloc(BUF_SIZE);
+	if (state->buf == NULL)
+		return ENOMEM;
+	state->buf_size = BUF_SIZE;
+	state->emit_event_fn = event_fn;
+	return EOK;
+}
+
+int isdv4_init_tablet(isdv4_state_t *state)
+{
+	if (!write_command(state->sess, CMD_STOP))
+		return EIO;
+
+	usleep(250000); /* 250 ms */
+
+	// FIXME: Read all possible garbage before sending commands
+	if (!write_command(state->sess, CMD_QUERY_STYLUS))
+		return EIO;
+
+	int rc = read_packets(state, parse_response_stylus);
+	if (rc != EOK)
+		return rc;
+
+	if (!write_command(state->sess, CMD_QUERY_TOUCH))
+		return EIO;
+
+	rc = read_packets(state, parse_response_touch);
+	if (rc != EOK)
+		return rc;
+
+	if (!write_command(state->sess, CMD_START))
+		return EIO;
+
+	return EOK;
+}
+
+int isdv4_read_events(isdv4_state_t *state)
+{
+	return read_packets(state, parse_event);
+}
+
+void isdv4_fini(isdv4_state_t *state)
+{
+	free(state->buf);
+}
Index: uspace/srv/hid/isdv4_tablet/isdv4.h
===================================================================
--- uspace/srv/hid/isdv4_tablet/isdv4.h	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
+++ uspace/srv/hid/isdv4_tablet/isdv4.h	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2012 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ISDV4_H__
+#define __ISDV4_H__
+
+typedef struct isdv4_event isdv4_event_t;
+
+typedef void (*isdv4_event_fn)(const isdv4_event_t *);
+
+typedef struct {
+	/* Stylus information */
+	unsigned int stylus_max_x;
+	unsigned int stylus_max_y;
+	unsigned int stylus_max_pressure;
+	unsigned int stylus_max_xtilt;
+	unsigned int stylus_max_ytilt;
+	bool stylus_tilt_supported;
+
+	/* Touch information */
+	unsigned int touch_type;
+	unsigned int touch_max_x;
+	unsigned int touch_max_y;
+
+	/* Event state */
+	bool stylus_in_proximity;
+	bool stylus_is_eraser;
+	bool tip_pressed; /* Reported as stylus button 1 */
+	bool button1_pressed; /* Reported as stylus button 2 */
+	bool button2_pressed; /* Reported as stylus button 3 */
+	bool finger1_pressed; /* Reported as touch button 1 */
+
+	/* Session to the serial device */
+	async_sess_t *sess;
+
+	/* Receive buffer state */
+	uint8_t *buf;
+	size_t buf_size;
+	size_t buf_end;
+
+	/* Callbacks */
+	isdv4_event_fn emit_event_fn;
+} isdv4_state_t;
+
+typedef enum {
+	UNKNOWN, PRESS, RELEASE, PROXIMITY_IN, PROXIMITY_OUT, MOVE
+} isdv4_event_type_t;
+
+typedef enum {
+	STYLUS_TIP, STYLUS_ERASER, TOUCH
+} isdv4_source_type_t;
+
+typedef struct isdv4_event {
+	isdv4_event_type_t type;
+	isdv4_source_type_t source;
+	unsigned int x;
+	unsigned int y;
+	unsigned int pressure;
+	unsigned int button;
+} isdv4_event_t;
+
+extern int isdv4_init(isdv4_state_t *, async_sess_t *, isdv4_event_fn);
+extern int isdv4_init_tablet(isdv4_state_t *);
+extern int isdv4_read_events(isdv4_state_t *state);
+extern void isdv4_fini(isdv4_state_t *);
+
+#endif
Index: uspace/srv/hid/isdv4_tablet/main.c
===================================================================
--- uspace/srv/hid/isdv4_tablet/main.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
+++ uspace/srv/hid/isdv4_tablet/main.c	(revision 16639bb64953741f4366b570014e0d7df860c6b7)
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2012 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <device/char_dev.h>
+#include <errno.h>
+#include <ipc/serial_ctl.h>
+#include <loc.h>
+#include <stdio.h>
+#include <fibril_synch.h>
+#include <abi/ipc/methods.h>
+#include <ipc/mouseev.h>
+#include <inttypes.h>
+
+#include "isdv4.h"
+
+#define NAME "isdv4_tablet"
+
+static async_sess_t *client_sess = NULL;
+static fibril_mutex_t client_mutex;
+static isdv4_state_t state;
+
+static void syntax_print(void)
+{
+	fprintf(stderr, "Usage: %s [--baud=<baud>] [--print-events] [device_service]\n", NAME);
+}
+
+static int read_fibril(void *unused)
+{
+	int rc = isdv4_read_events(&state);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed reading events");
+		return rc;
+	}
+
+	isdv4_fini(&state);
+	return EOK;
+}
+
+static void mouse_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	async_answer_0(iid, EOK);
+	
+	async_sess_t *sess =
+	    async_callback_receive(EXCHANGE_SERIALIZE);
+	fibril_mutex_lock(&client_mutex);
+		if (client_sess == NULL) {
+			client_sess = sess;
+		}
+	fibril_mutex_unlock(&client_mutex);
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call))
+			break;
+		
+		async_answer_0(callid, ENOTSUP);
+	}
+}
+
+static void emit_event(const isdv4_event_t *event)
+{
+	fibril_mutex_lock(&client_mutex);
+	async_sess_t *sess = client_sess;
+	fibril_mutex_unlock(&client_mutex);
+	
+	if (!sess) return;
+	
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (exch) {
+		unsigned int max_x = state.stylus_max_x;
+		unsigned int max_y = state.stylus_max_y;
+		if (event->source == TOUCH) {
+			max_x = state.touch_max_x;
+			max_y = state.touch_max_y;
+		}
+		async_msg_4(exch, MOUSEEV_ABS_MOVE_EVENT, event->x, event->y,
+				    max_x, max_y);
+		if (event->type == PRESS || event->type == RELEASE) {
+			async_msg_2(exch, MOUSEEV_BUTTON_EVENT, event->button,
+				    event->type == PRESS);
+		}
+	}
+	async_exchange_end(exch);
+}
+
+static void print_and_emit_event(const isdv4_event_t *event)
+{
+	const char *type = NULL;
+	switch (event->type) {
+		case PRESS:
+			type = "PRESS";
+			break;
+		case RELEASE:
+			type = "RELEASE";
+			break;
+		case PROXIMITY_IN:
+			type = "PROXIMITY IN";
+			break;
+		case PROXIMITY_OUT:
+			type = "PROXIMITY OUT";
+			break;
+		case MOVE:
+			type = "MOVE";
+			break;
+		default:
+			type = "UNKNOWN";
+			break;
+	}
+
+	const char *source = NULL;
+	switch (event->source) {
+		case STYLUS_TIP:
+			source = "stylus tip";
+			break;
+		case STYLUS_ERASER:
+			source = "stylus eraser";
+			break;
+		case TOUCH:
+			source = "touch";
+			break;
+	}
+
+	printf("%s %s %u %u %u %u\n", type, source, event->x, event->y,
+	    event->pressure, event->button);
+	
+	emit_event(event);
+}
+
+static const char *touch_type(unsigned int data_id)
+{
+	switch (data_id) {
+		case 0:
+			return "resistive+stylus";
+		case 1:
+			return "capacitive+stylus";
+		case 2:
+			return "resistive";
+		case 3:
+		case 4:
+			return "capacitive";
+		case 5:
+			return "penabled";
+	}
+	return "unknown";
+}
+
+int main(int argc, char **argv)
+{
+	sysarg_t baud = 38400;
+	service_id_t svc_id;
+	char *serial_port_name = NULL;
+
+	int arg = 1;
+	int rc;
+
+	isdv4_event_fn event_fn = emit_event;
+
+	if (argc > arg && str_test_prefix(argv[arg], "--baud=")) {
+		size_t arg_offset = str_lsize(argv[arg], 7);
+		char* arg_str = argv[arg] + arg_offset;
+		if (str_length(arg_str) == 0) {
+			fprintf(stderr, "--baud requires an argument\n");
+			syntax_print();
+			return 1;
+		}
+		char *endptr;
+		baud = strtol(arg_str, &endptr, 10);
+		if (*endptr != '\0') {
+			fprintf(stderr, "Invalid value for baud\n");
+			syntax_print();
+			return 1;
+		}
+		arg++;
+	}
+
+	if (argc > arg && str_cmp(argv[arg], "--print-events") == 0) {
+		event_fn = print_and_emit_event;
+		arg++;
+	}
+
+	if (argc > arg) {
+		serial_port_name = argv[arg];
+		rc = loc_service_get_id(serial_port_name, &svc_id, 0);
+		if (rc != EOK) {
+			fprintf(stderr, "Cannot find device service %s\n",
+			    argv[arg]);
+			return 1;
+		}
+		arg++;
+	}
+	else {
+		category_id_t serial_cat_id;
+
+		rc = loc_category_get_id("serial", &serial_cat_id, 0);
+		if (rc != EOK) {
+			fprintf(stderr, "Failed getting id of category "
+			    "'serial'\n");
+			return 1;
+		}
+
+		service_id_t *svc_ids;
+		size_t svc_count;
+
+		rc = loc_category_get_svcs(serial_cat_id, &svc_ids, &svc_count);		if (rc != EOK) {
+			fprintf(stderr, "Failed getting list of services\n");
+			return 1;
+		}
+
+		if (svc_count == 0) {
+			fprintf(stderr, "No service in category 'serial'\n");
+			free(svc_ids);
+			return 1;
+		}
+
+		svc_id = svc_ids[0];
+
+		rc = loc_service_get_name(svc_id, &serial_port_name);
+		if (rc != EOK) {
+			fprintf(stderr, "Failed getting name of serial service\n");
+			return 1;
+		}
+
+		free(svc_ids);
+	}
+
+	if (argc > arg) {
+		fprintf(stderr, "Too many arguments\n");
+		syntax_print();
+		return 1;
+	}
+
+	fibril_mutex_initialize(&client_mutex);
+
+	printf(NAME ": Using serial port %s\n", serial_port_name);
+
+	async_sess_t *sess = loc_service_connect(EXCHANGE_SERIALIZE, svc_id,
+	    IPC_FLAG_BLOCKING);
+	if (!sess) {
+		fprintf(stderr, "Failed connecting to service\n");
+	}
+
+	async_exch_t *exch = async_exchange_begin(sess);
+	rc = async_req_4_0(exch, SERIAL_SET_COM_PROPS, baud,
+	    SERIAL_NO_PARITY, 8, 1);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		fprintf(stderr, "Failed setting serial properties\n");
+		return 2;
+	}
+
+	rc = isdv4_init(&state, sess, event_fn);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed initializing isdv4 state");
+		return 2;
+	}
+
+	rc = isdv4_init_tablet(&state);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed initializing tablet");
+		return 2;
+	}
+
+	printf("Tablet information:\n");
+	printf(" Stylus: %ux%u pressure: %u tilt: ", state.stylus_max_x,
+	    state.stylus_max_y, state.stylus_max_pressure);
+	if (state.stylus_tilt_supported) {
+		printf("%ux%u\n", state.stylus_max_xtilt, state.stylus_max_ytilt);
+	}
+	else {
+		printf("not supported\n");
+	}
+	printf(" Touch: %ux%u type: %s\n", state.touch_max_x, state.touch_max_y,
+		touch_type(state.touch_type));
+	
+	fid_t fibril = fibril_create(read_fibril, NULL);
+	/* From this on, state is to be used only by read_fibril */
+	fibril_add_ready(fibril);
+
+	async_set_client_connection(mouse_connection);
+	rc = loc_server_register(NAME);
+	if (rc != EOK) {
+		printf("%s: Unable to register driver.\n", NAME);
+		return rc;
+	}
+
+	service_id_t service_id;
+	char *service_name;
+	rc = asprintf(&service_name, "mouse/isdv4-%" PRIun, svc_id);
+	if (rc < 0) {
+		printf(NAME ": Unable to create service name\n");
+		return rc;
+	}
+
+	rc = loc_service_register(service_name, &service_id);
+	if (rc != EOK) {
+		printf(NAME ": Unable to register service %s.\n", service_name);
+		return rc;
+	}
+
+	category_id_t mouse_category;
+	rc = loc_category_get_id("mouse", &mouse_category, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		printf(NAME ": Unable to get mouse category id.\n");
+	}
+	else {
+		rc = loc_service_add_to_cat(service_id, mouse_category);
+		if (rc != EOK) {
+			printf(NAME ": Unable to add device to mouse category.\n");
+		}
+	}
+
+	printf("%s: Accepting connections\n", NAME);
+	task_retval(0);
+	async_manager();
+
+	/* Not reached */
+	return 0;
+}
