Index: uspace/app/wacomdump/Makefile
===================================================================
--- uspace/app/wacomdump/Makefile	(revision c92e30fd4c782868f0f4d1b8087da611f1efa90a)
+++ uspace/app/wacomdump/Makefile	(revision a987832985ce54ea4ef58b2bf35cb0a00ed66539)
@@ -33,5 +33,6 @@
 
 SOURCES = \
-	wacomdump.c
+	wacomdump.c \
+	isdv4.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/wacomdump/wacomdump.c
===================================================================
--- uspace/app/wacomdump/wacomdump.c	(revision c92e30fd4c782868f0f4d1b8087da611f1efa90a)
+++ uspace/app/wacomdump/wacomdump.c	(revision a987832985ce54ea4ef58b2bf35cb0a00ed66539)
@@ -32,86 +32,6 @@
 #include <loc.h>
 #include <stdio.h>
-#include <macros.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 '%'
-
-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;
-
-static void isdv4_event_init(isdv4_event_t *event)
-{
-	memset(event, 0, sizeof(isdv4_event_t));
-}
-
-/* packet_consumer_fn(uint8_t *packet, size_t size, isdv4_state_t *state)
-   return true if reading of packets should continue */
-typedef bool (*packet_consumer_fn)(uint8_t *, size_t, isdv4_state_t *);
+
+#include "isdv4.h"
 
 static void syntax_print(void)
@@ -143,5 +63,5 @@
 			break;
 	}
-	
+
 	const char *source = NULL;
 	switch (event->source) {
@@ -156,5 +76,5 @@
 			break;
 	}
-	
+
 	const char *buttons = "none";
 	switch (event->button) {
@@ -169,204 +89,7 @@
 			break;
 	}
-	
+
 	printf("%s %s %u %u %u %s\n", type, source, event->x, event->y,
 	    event->pressure, buttons);
-}
-
-static bool parse_event(uint8_t *packet, size_t size, isdv4_state_t *state)
-{
-	if (size < 1) {
-		printf("Invalid packet size\n");
-		return false;
-	}
-	bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
-	if (control_packet) {
-		printf("This is not an event packet\n");
-		return true;
-	}
-
-	/* This is an event initiated by the device */
-	isdv4_event_t event;
-	isdv4_event_init(&event);
-
-
-	if (size == 5 || size == 7) {
-		/* 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) {
-		/* 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 = (tip ? 1: 0) | (button1 ? 2 : 0) | (button2 ? 4 : 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 = (tip ? 1 : 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 != 11) {
-		fprintf(stderr, "Unexpected length of stylus response packet\n");
-		return false;
-	}
-	bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
-	if (!control_packet) {
-		fprintf(stderr, "This is not a control packet\n");
-		return false;
-	}
-
-	unsigned int data_id = (packet[0] & 63);
-	unsigned int version = ((packet[9] & 127) << 7) | (packet[10] & 127);
-
-	unsigned int max_x = ((packet[1] & 127) << 7) | (packet[2] & 124) |
-	    ((packet[6] >> 5) & 3);
-	unsigned int max_y = ((packet[3] & 127) << 7) | (packet[4] & 124) |
-	    ((packet[6] >> 3) & 3);
-	unsigned int max_pressure = (packet[5] & 63) | ((packet[6] & 7) << 7);
-	unsigned int max_xtilt = packet[8] & 127;
-	unsigned int max_ytilt = packet[7] & 127;
-
-	printf("Stylus info: data_id=%u version=%u max_x=%u max_y=%u max_pressure=%u "
-	    "max_xtilt=%u max_ytilt=%u\n", data_id, version, max_x, max_y,
-	    max_pressure, max_xtilt, max_ytilt);
-	
-	return false;
 }
 
@@ -389,140 +112,12 @@
 }
 
-static bool parse_response_touch(uint8_t *packet, size_t size,
-    isdv4_state_t *state)
-{
-	if (size != 11) {
-		fprintf(stderr, "Unexpected length of touch response packet\n");
-		return false;
-	}
-	bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
-	if (!control_packet) {
-		fprintf(stderr, "This is not a control packet\n");
-		return false;
-	}
-
-	state->touch_type = (packet[0] & 63);
-	unsigned int version = ((packet[9] & 127) << 7) | (packet[10] & 127);
-
-	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);
-	}
-
-	printf("Touch info: data_id=%u (%s) version=%u max_x=%u "
-	    "max_y=%u\n", state->touch_type, touch_type(state->touch_type), version,
-	    state->touch_max_x, state->touch_max_y);
-	return false;
-}
-
-static void read_packets(isdv4_state_t *state, packet_consumer_fn consumer)
-{
-	bool reading = true;
-	size_t packet_remaining = 1;
-	while (reading) {
-		if (packet_remaining == 0) packet_remaining = 1;
-		ssize_t read = char_dev_read(state->sess, state->buf + state->buf_end,
-		    state->buf_size - state->buf_end);
-		if (read < 0) {
-			fprintf(stderr, "Failed reading from serial device\n");
-			return;
-		}
-		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 */
-			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 == BUF_SIZE) {
-			fprintf(stderr, "Buffer overflow detected, discarding contents\n");
-			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;
-	}
-}
-static bool write_command(async_sess_t *sess, uint8_t command)
-{
-	return char_dev_write(sess, &command, 1) == 1;
-}
-
-static void isdv4_init(isdv4_state_t *state, async_sess_t *sess,
-    uint8_t *buf, size_t buf_size, isdv4_event_fn event_fn)
-{
-	memset(state, 0, sizeof(isdv4_state_t));
-	state->sess = sess;
-	state->buf = buf;
-	state->buf_size = buf_size;
-	state->emit_event_fn = event_fn;
-}
-
-static void isdv4_init_tablet(isdv4_state_t *state)
-{
-	write_command(state->sess, CMD_STOP);
-	usleep(250000); /* 250 ms */
-	// FIXME: Read all possible garbage before sending commands
-	write_command(state->sess, CMD_QUERY_STYLUS);
-	read_packets(state, parse_response_stylus);
-	write_command(state->sess, CMD_QUERY_TOUCH);
-	read_packets(state, parse_response_touch);
-	write_command(state->sess, CMD_START);
-}
-
 int main(int argc, char **argv)
 {
 	sysarg_t baud = 38400;
 	service_id_t svc_id;
-	
+
 	int arg = 1;
 	int rc;
-	
+
 	if (argc > arg && str_test_prefix(argv[arg], "--baud=")) {
 		size_t arg_offset = str_lsize(argv[arg], 7);
@@ -542,5 +137,5 @@
 		arg++;
 	}
-	
+
 	if (argc > arg) {
 		rc = loc_service_get_id(argv[arg], &svc_id, 0);
@@ -554,5 +149,5 @@
 	else {
 		category_id_t serial_cat_id;
-		
+
 		rc = loc_category_get_id("serial", &serial_cat_id, 0);
 		if (rc != EOK) {
@@ -561,13 +156,13 @@
 			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");
@@ -575,9 +170,9 @@
 			return 1;
 		}
-		
+
 		svc_id = svc_ids[0];
 		free(svc_ids);
 	}
-	
+
 	if (argc > arg) {
 		fprintf(stderr, "Too many arguments\n");
@@ -585,6 +180,5 @@
 		return 1;
 	}
-	
-	
+
 	async_sess_t *sess = loc_service_connect(EXCHANGE_SERIALIZE, svc_id,
 	    IPC_FLAG_BLOCKING);
@@ -592,23 +186,48 @@
 		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;
 	}
-	
-	uint8_t buf[BUF_SIZE];
-	
+
 	isdv4_state_t state;
-	isdv4_init(&state, sess, buf, BUF_SIZE, print_event);
-	isdv4_init_tablet(&state);
-	
-	read_packets(&state, parse_event);
-	
+	rc = isdv4_init(&state, sess, print_event);
+	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));
+
+	rc = isdv4_read_events(&state);
+	if (rc != EOK) {
+		fprintf(stderr, "Failed reading events");
+		return 2;
+	}
+
+	isdv4_fini(&state);
+
 	return 0;
 }
