Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 149956486aec381b969e725ecafbf5575e325e6e)
+++ boot/Makefile.common	(revision f66ca57f67fa0ea52c699112f4682f74c09f6bb9)
@@ -107,4 +107,5 @@
 	$(USPACE_PATH)/srv/fs/ext2fs/ext2fs \
 	$(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 \
@@ -187,5 +188,4 @@
 	$(USPACE_PATH)/app/vuhid/vuh \
 	$(USPACE_PATH)/app/mkbd/mkbd \
-	$(USPACE_PATH)/app/wacomdump/wacomdump \
 	$(USPACE_PATH)/app/websrv/websrv
 
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 149956486aec381b969e725ecafbf5575e325e6e)
+++ uspace/Makefile	(revision f66ca57f67fa0ea52c699112f4682f74c09f6bb9)
@@ -70,5 +70,4 @@
 	app/sysinfo \
 	app/mkbd \
-	app/wacomdump \
 	app/websrv \
 	srv/clipboard \
@@ -100,4 +99,5 @@
 	srv/hid/console \
 	srv/hid/s3c24xx_ts \
+	srv/hid/isdv4_tablet \
 	srv/hid/fb \
 	srv/hid/input \
Index: pace/app/wacomdump/Makefile
===================================================================
--- uspace/app/wacomdump/Makefile	(revision 149956486aec381b969e725ecafbf5575e325e6e)
+++ 	(revision )
@@ -1,38 +1,0 @@
-#
-# 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 = wacomdump
-
-SOURCES = \
-	wacomdump.c \
-	isdv4.c
-
-include $(USPACE_PREFIX)/Makefile.common
Index: pace/app/wacomdump/isdv4.c
===================================================================
--- uspace/app/wacomdump/isdv4.c	(revision 149956486aec381b969e725ecafbf5575e325e6e)
+++ 	(revision )
@@ -1,410 +1,0 @@
-/*
- * 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: pace/app/wacomdump/isdv4.h
===================================================================
--- uspace/app/wacomdump/isdv4.h	(revision 149956486aec381b969e725ecafbf5575e325e6e)
+++ 	(revision )
@@ -1,92 +1,0 @@
-/*
- * 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: pace/app/wacomdump/wacomdump.c
===================================================================
--- uspace/app/wacomdump/wacomdump.c	(revision 149956486aec381b969e725ecafbf5575e325e6e)
+++ 	(revision )
@@ -1,327 +1,0 @@
-/*
- * 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 "isdv4.h"
-
-#define NAME "wacomdump"
-
-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: wacomdump [--baud=<baud>] [--print-events] [device_service]\n");
-}
-
-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;
-
-	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) {
-		rc = loc_service_get_id(argv[arg], &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];
-		free(svc_ids);
-	}
-
-	if (argc > arg) {
-		fprintf(stderr, "Too many arguments\n");
-		syntax_print();
-		return 1;
-	}
-
-	fibril_mutex_initialize(&client_mutex);
-
-	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;
-	rc = loc_service_register("mouse/wacom", &service_id);
-	if (rc != EOK) {
-		printf(NAME ": Unable to register device mouse/wacom.\n");
-		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;
-}
Index: uspace/srv/hid/isdv4_tablet/Makefile
===================================================================
--- uspace/srv/hid/isdv4_tablet/Makefile	(revision f66ca57f67fa0ea52c699112f4682f74c09f6bb9)
+++ uspace/srv/hid/isdv4_tablet/Makefile	(revision f66ca57f67fa0ea52c699112f4682f74c09f6bb9)
@@ -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 f66ca57f67fa0ea52c699112f4682f74c09f6bb9)
+++ uspace/srv/hid/isdv4_tablet/isdv4.c	(revision f66ca57f67fa0ea52c699112f4682f74c09f6bb9)
@@ -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 f66ca57f67fa0ea52c699112f4682f74c09f6bb9)
+++ uspace/srv/hid/isdv4_tablet/isdv4.h	(revision f66ca57f67fa0ea52c699112f4682f74c09f6bb9)
@@ -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 f66ca57f67fa0ea52c699112f4682f74c09f6bb9)
+++ uspace/srv/hid/isdv4_tablet/main.c	(revision f66ca57f67fa0ea52c699112f4682f74c09f6bb9)
@@ -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;
+}
