Index: uspace/srv/hid/input/Makefile
===================================================================
--- uspace/srv/hid/input/Makefile	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/Makefile	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -36,5 +36,4 @@
 	generic/input.c \
 	generic/layout.c \
-	generic/mouse.c \
 	generic/stroke.c \
 	layout/cz.c \
@@ -42,5 +41,7 @@
 	layout/us_dvorak.c \
 	port/adb.c \
+	port/adb_mouse.c \
 	port/chardev.c \
+	port/chardev_mouse.c \
 	port/gxemul.c \
 	port/msim.c \
@@ -51,4 +52,7 @@
 	port/ski.c \
 	port/z8530.c \
+	proto/adb.c \
+	proto/mousedev.c \
+	proto/ps2.c \
 	ctl/apple.c \
 	ctl/gxe_fb.c \
Index: uspace/srv/hid/input/ctl/apple.c
===================================================================
--- uspace/srv/hid/input/ctl/apple.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/ctl/apple.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -33,5 +33,5 @@
 /**
  * @file
- * @brief	Apple ADB keyboard controller driver.
+ * @brief Apple ADB keyboard controller driver.
  */
 
@@ -42,10 +42,10 @@
 #include <kbd_port.h>
 
-static void apple_ctl_parse_scancode(int);
+static void apple_ctl_parse(sysarg_t);
 static int apple_ctl_init(kbd_dev_t *);
-static void apple_ctl_set_ind(kbd_dev_t *, unsigned);
+static void apple_ctl_set_ind(kbd_dev_t *, unsigned int);
 
 kbd_ctl_ops_t apple_ctl = {
-	.parse_scancode = apple_ctl_parse_scancode,
+	.parse = apple_ctl_parse,
 	.init = apple_ctl_init,
 	.set_ind = apple_ctl_set_ind
@@ -64,10 +64,10 @@
 }
 
-static void apple_ctl_parse_scancode(int scancode)
+static void apple_ctl_parse(sysarg_t scancode)
 {
 	kbd_event_type_t type;
 	unsigned int key;
 
-	if (scancode < 0 || scancode >= 0x100)
+	if (scancode >= 0x100)
 		return;
 
@@ -81,5 +81,5 @@
 	key = scanmap[scancode];
 	if (key != 0)
-		kbd_push_ev(kbd_dev, type, key);
+		kbd_push_event(kbd_dev, type, key);
 }
 
Index: uspace/srv/hid/input/ctl/gxe_fb.c
===================================================================
--- uspace/srv/hid/input/ctl/gxe_fb.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/ctl/gxe_fb.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -44,10 +44,10 @@
 #include <stroke.h>
 
-static void gxe_fb_ctl_parse_scancode(int);
+static void gxe_fb_ctl_parse(sysarg_t);
 static int gxe_fb_ctl_init(kbd_dev_t *);
-static void gxe_fb_ctl_set_ind(kbd_dev_t *, unsigned);
+static void gxe_fb_ctl_set_ind(kbd_dev_t *, unsigned int);
 
 kbd_ctl_ops_t gxe_fb_ctl = {
-	.parse_scancode = gxe_fb_ctl_parse_scancode,
+	.parse = gxe_fb_ctl_parse,
 	.init = gxe_fb_ctl_init,
 	.set_ind = gxe_fb_ctl_set_ind
@@ -229,5 +229,5 @@
 }
 
-static void gxe_fb_ctl_parse_scancode(int scancode)
+static void gxe_fb_ctl_parse(sysarg_t scancode)
 {
 	unsigned mods, key;
Index: uspace/srv/hid/input/ctl/kbdev.c
===================================================================
--- uspace/srv/hid/input/ctl/kbdev.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/ctl/kbdev.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -53,10 +53,10 @@
 
 static int kbdev_ctl_init(kbd_dev_t *);
-static void kbdev_ctl_set_ind(kbd_dev_t *, unsigned);
+static void kbdev_ctl_set_ind(kbd_dev_t *, unsigned int);
 
 static void kbdev_callback_conn(ipc_callid_t, ipc_call_t *, void *arg);
 
 kbd_ctl_ops_t kbdev_ctl = {
-	.parse_scancode = NULL,
+	.parse = NULL,
 	.init = kbdev_ctl_init,
 	.set_ind = kbdev_ctl_set_ind
@@ -116,5 +116,5 @@
 	sess = fd_session(EXCHANGE_SERIALIZE, fd);
 	if (sess == NULL) {
-		printf(NAME ": Failed starting session with '%s'\n", pathname);
+		printf("%s: Failed starting session with '%s'\n", NAME, pathname);
 		close(fd);
 		return -1;
@@ -123,6 +123,6 @@
 	kbdev = kbdev_new(kdev);
 	if (kbdev == NULL) {
-		printf(NAME ": Failed allocating device structure for '%s'.\n",
-		    pathname);
+		printf("%s: Failed allocating device structure for '%s'.\n",
+		    NAME, pathname);
 		return -1;
 	}
@@ -133,5 +133,5 @@
 	exch = async_exchange_begin(sess);
 	if (exch == NULL) {
-		printf(NAME ": Failed starting exchange with '%s'.\n", pathname);
+		printf("%s: Failed starting exchange with '%s'.\n", NAME, pathname);
 		kbdev_destroy(kbdev);
 		return -1;
@@ -140,6 +140,6 @@
 	rc = async_connect_to_me(exch, 0, 0, 0, kbdev_callback_conn, kbdev);
 	if (rc != EOK) {
-		printf(NAME ": Failed creating callback connection from '%s'.\n",
-		    pathname);
+		printf("%s: Failed creating callback connection from '%s'.\n",
+		    NAME, pathname);
 		async_exchange_end(exch);
 		kbdev_destroy(kbdev);
@@ -193,5 +193,5 @@
 			type = IPC_GET_ARG1(call);
 			key = IPC_GET_ARG2(call);
-			kbd_push_ev(kbdev->kbd_dev, type, key);
+			kbd_push_event(kbdev->kbd_dev, type, key);
 			break;
 		default:
Index: uspace/srv/hid/input/ctl/pc.c
===================================================================
--- uspace/srv/hid/input/ctl/pc.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/ctl/pc.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -43,10 +43,10 @@
 #include <gsp.h>
 
-static void pc_ctl_parse_scancode(int);
+static void pc_ctl_parse(sysarg_t);
 static int pc_ctl_init(kbd_dev_t *);
-static void pc_ctl_set_ind(kbd_dev_t *, unsigned);
+static void pc_ctl_set_ind(kbd_dev_t *, unsigned int);
 
 kbd_ctl_ops_t pc_ctl = {
-	.parse_scancode = pc_ctl_parse_scancode,
+	.parse = pc_ctl_parse,
 	.init = pc_ctl_init,
 	.set_ind = pc_ctl_set_ind
@@ -215,5 +215,5 @@
 }
 
-static void pc_ctl_parse_scancode(int scancode)
+static void pc_ctl_parse(sysarg_t scancode)
 {
 	kbd_event_type_t type;
@@ -257,10 +257,10 @@
 	}
 
-	if ((scancode < 0) || ((size_t) scancode >= map_length))
+	if ((size_t) scancode >= map_length)
 		return;
 
 	key = map[scancode];
 	if (key != 0)
-		kbd_push_ev(kbd_dev, type, key);
+		kbd_push_event(kbd_dev, type, key);
 }
 
Index: uspace/srv/hid/input/ctl/stty.c
===================================================================
--- uspace/srv/hid/input/ctl/stty.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/ctl/stty.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -33,5 +33,5 @@
 /**
  * @file
- * @brief	Serial TTY-like keyboard controller driver.
+ * @brief Serial TTY-like keyboard controller driver.
  */
 
@@ -43,10 +43,10 @@
 #include <stroke.h>
 
-static void stty_ctl_parse_scancode(int);
+static void stty_ctl_parse(sysarg_t);
 static int stty_ctl_init(kbd_dev_t *);
-static void stty_ctl_set_ind(kbd_dev_t *, unsigned);
+static void stty_ctl_set_ind(kbd_dev_t *, unsigned int);
 
 kbd_ctl_ops_t stty_ctl = {
-	.parse_scancode = stty_ctl_parse_scancode,
+	.parse = stty_ctl_parse,
 	.init = stty_ctl_init,
 	.set_ind = stty_ctl_set_ind
@@ -228,5 +228,5 @@
 }
 
-static void stty_ctl_parse_scancode(int scancode)
+static void stty_ctl_parse(sysarg_t scancode)
 {
 	unsigned mods, key;
Index: uspace/srv/hid/input/ctl/sun.c
===================================================================
--- uspace/srv/hid/input/ctl/sun.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/ctl/sun.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -34,5 +34,5 @@
 /**
  * @file
- * @brief	Sun keyboard controller driver.
+ * @brief Sun keyboard controller driver.
  */
 
@@ -43,10 +43,10 @@
 #include <kbd_port.h>
 
-static void sun_ctl_parse_scancode(int);
+static void sun_ctl_parse(sysarg_t);
 static int sun_ctl_init(kbd_dev_t *);
-static void sun_ctl_set_ind(kbd_dev_t *, unsigned);
+static void sun_ctl_set_ind(kbd_dev_t *, unsigned int);
 
 kbd_ctl_ops_t sun_ctl = {
-	.parse_scancode = sun_ctl_parse_scancode,
+	.parse = sun_ctl_parse,
 	.init = sun_ctl_init,
 	.set_ind = sun_ctl_set_ind
@@ -66,10 +66,10 @@
 }
 
-static void sun_ctl_parse_scancode(int scancode)
+static void sun_ctl_parse(sysarg_t scancode)
 {
 	kbd_event_type_t type;
 	unsigned int key;
 
-	if (scancode < 0 || scancode >= 0x100)
+	if (scancode >= 0x100)
 		return;
 
@@ -86,5 +86,5 @@
 	key = scanmap_simple[scancode];
 	if (key != 0)
-		kbd_push_ev(kbd_dev, type, key);
+		kbd_push_event(kbd_dev, type, key);
 }
 
Index: uspace/srv/hid/input/generic/input.c
===================================================================
--- uspace/srv/hid/input/generic/input.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/generic/input.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -58,4 +58,5 @@
 #include <kbd_port.h>
 #include <kbd_ctl.h>
+#include <mouse_proto.h>
 #include <layout.h>
 #include <mouse.h>
@@ -65,23 +66,7 @@
 
 /* In microseconds */
-#define DISCOVERY_POLL_INTERVAL		(10*1000*1000)
-
-static void kbd_devs_yield(void);
-static void kbd_devs_reclaim(void);
-
-static void input_event_key(int, unsigned int, unsigned, wchar_t);
-
-int client_phone = -1;
-
-/** List of keyboard devices */
-static list_t kbd_devs;
-
-/** List of mouse devices */
-list_t mouse_devs;
-
-bool irc_service = false;
-int irc_phone = -1;
-
-#define NUM_LAYOUTS 3
+#define DISCOVERY_POLL_INTERVAL  (10 * 1000 * 1000)
+
+#define NUM_LAYOUTS  3
 
 static layout_ops_t *layout[NUM_LAYOUTS] = {
@@ -91,15 +76,33 @@
 };
 
-void kbd_push_scancode(kbd_dev_t *kdev, int scancode)
-{
-/*	printf("scancode: 0x%x\n", scancode);*/
-	(*kdev->ctl_ops->parse_scancode)(scancode);
-}
-
-void kbd_push_ev(kbd_dev_t *kdev, int type, unsigned int key)
+static void kbd_devs_yield(void);
+static void kbd_devs_reclaim(void);
+
+int client_phone = -1;
+
+/** List of keyboard devices */
+static list_t kbd_devs;
+
+/** List of mouse devices */
+static list_t mouse_devs;
+
+bool irc_service = false;
+int irc_phone = -1;
+
+void kbd_push_data(kbd_dev_t *kdev, sysarg_t data)
+{
+	(*kdev->ctl_ops->parse)(data);
+}
+
+void mouse_push_data(mouse_dev_t *mdev, sysarg_t data)
+{
+	(*mdev->proto_ops->parse)(data);
+}
+
+void kbd_push_event(kbd_dev_t *kdev, int type, unsigned int key)
 {
 	kbd_event_t ev;
-	unsigned mod_mask;
-
+	unsigned int mod_mask;
+	
 	switch (key) {
 	case KC_LCTRL: mod_mask = KM_LCTRL; break;
@@ -111,5 +114,5 @@
 	default: mod_mask = 0; break;
 	}
-
+	
 	if (mod_mask != 0) {
 		if (type == KEY_PRESS)
@@ -118,5 +121,5 @@
 			kdev->mods = kdev->mods & ~mod_mask;
 	}
-
+	
 	switch (key) {
 	case KC_CAPS_LOCK: mod_mask = KM_CAPS_LOCK; break;
@@ -125,5 +128,5 @@
 	default: mod_mask = 0; break;
 	}
-
+	
 	if (mod_mask != 0) {
 		if (type == KEY_PRESS) {
@@ -135,5 +138,5 @@
 			kdev->mods = kdev->mods ^ (mod_mask & ~kdev->lock_keys);
 			kdev->lock_keys = kdev->lock_keys | mod_mask;
-
+			
 			/* Update keyboard lock indicator lights. */
 			(*kdev->ctl_ops->set_ind)(kdev, kdev->mods);
@@ -142,48 +145,37 @@
 		}
 	}
-/*
-	printf("type: %d\n", type);
-	printf("mods: 0x%x\n", mods);
-	printf("keycode: %u\n", key);
-*/
+	
 	if (type == KEY_PRESS && (kdev->mods & KM_LCTRL) &&
-		key == KC_F1) {
+	    key == KC_F1) {
 		layout_destroy(kdev->active_layout);
 		kdev->active_layout = layout_create(layout[0]);
 		return;
 	}
-
+	
 	if (type == KEY_PRESS && (kdev->mods & KM_LCTRL) &&
-		key == KC_F2) {
+	    key == KC_F2) {
 		layout_destroy(kdev->active_layout);
 		kdev->active_layout = layout_create(layout[1]);
 		return;
 	}
-
+	
 	if (type == KEY_PRESS && (kdev->mods & KM_LCTRL) &&
-		key == KC_F3) {
+	    key == KC_F3) {
 		layout_destroy(kdev->active_layout);
 		kdev->active_layout = layout_create(layout[2]);
 		return;
 	}
-
+	
 	ev.type = type;
 	ev.key = key;
 	ev.mods = kdev->mods;
-
+	
 	ev.c = layout_parse_ev(kdev->active_layout, &ev);
-	input_event_key(ev.type, ev.key, ev.mods, ev.c);
-}
-
-/** Key has been pressed or released. */
-static void input_event_key(int type, unsigned int key, unsigned mods,
-    wchar_t c)
-{
-	async_obsolete_msg_4(client_phone, INPUT_EVENT_KEY, type, key,
-	    mods, c);
+	async_obsolete_msg_4(client_phone, INPUT_EVENT_KEY, ev.type, ev.key,
+	    ev.mods, ev.c);
 }
 
 /** Mouse pointer has moved. */
-void input_event_move(int dx, int dy)
+void mouse_push_event_move(mouse_dev_t *mdev, int dx, int dy)
 {
 	async_obsolete_msg_2(client_phone, INPUT_EVENT_MOVE, dx, dy);
@@ -191,5 +183,5 @@
 
 /** Mouse button has been pressed. */
-void input_event_button(int bnum, int press)
+void mouse_push_event_button(mouse_dev_t *mdev, int bnum, int press)
 {
 	async_obsolete_msg_2(client_phone, INPUT_EVENT_BUTTON, bnum, press);
@@ -201,7 +193,7 @@
 	ipc_call_t call;
 	int retval;
-
+	
 	async_answer_0(iid, EOK);
-
+	
 	while (true) {
 		callid = async_get_call(&call);
@@ -237,26 +229,39 @@
 			retval = EINVAL;
 		}
+		
 		async_answer_0(callid, retval);
-	}	
+	}
 }
 
 static kbd_dev_t *kbd_dev_new(void)
 {
-	kbd_dev_t *kdev;
-
-	kdev = calloc(1, sizeof(kbd_dev_t));
+	kbd_dev_t *kdev = calloc(1, sizeof(kbd_dev_t));
 	if (kdev == NULL) {
-		printf(NAME ": Error allocating keyboard device. "
-		    "Out of memory.\n");
+		printf("%s: Error allocating keyboard device. "
+		    "Out of memory.\n", NAME);
 		return NULL;
 	}
-
+	
 	link_initialize(&kdev->kbd_devs);
-
+	
 	kdev->mods = KM_NUM_LOCK;
 	kdev->lock_keys = 0;
 	kdev->active_layout = layout_create(layout[0]);
-
+	
 	return kdev;
+}
+
+static mouse_dev_t *mouse_dev_new(void)
+{
+	mouse_dev_t *mdev = calloc(1, sizeof(mouse_dev_t));
+	if (mdev == NULL) {
+		printf("%s: Error allocating keyboard device. "
+		    "Out of memory.\n", NAME);
+		return NULL;
+	}
+	
+	link_initialize(&mdev->mouse_devs);
+	
+	return mdev;
 }
 
@@ -264,18 +269,16 @@
 static void kbd_add_dev(kbd_port_ops_t *port, kbd_ctl_ops_t *ctl)
 {
-	kbd_dev_t *kdev;
-
-	kdev = kbd_dev_new();
+	kbd_dev_t *kdev = kbd_dev_new();
 	if (kdev == NULL)
 		return;
-
+	
 	kdev->port_ops = port;
 	kdev->ctl_ops = ctl;
 	kdev->dev_path = NULL;
-
+	
 	/* Initialize port driver. */
 	if ((*kdev->port_ops->init)(kdev) != 0)
 		goto fail;
-
+	
 	/* Initialize controller driver. */
 	if ((*kdev->ctl_ops->init)(kdev) != 0) {
@@ -283,36 +286,93 @@
 		goto fail;
 	}
-
+	
 	list_append(&kdev->kbd_devs, &kbd_devs);
 	return;
+	
 fail:
 	free(kdev);
 }
 
+/** Add new legacy mouse device. */
+static void mouse_add_dev(mouse_port_ops_t *port, mouse_proto_ops_t *proto)
+{
+	mouse_dev_t *mdev = mouse_dev_new();
+	if (mdev == NULL)
+		return;
+	
+	mdev->port_ops = port;
+	mdev->proto_ops = proto;
+	mdev->dev_path = NULL;
+	
+	/* Initialize port driver. */
+	if ((*mdev->port_ops->init)(mdev) != 0)
+		goto fail;
+	
+	/* Initialize protocol driver. */
+	if ((*mdev->proto_ops->init)(mdev) != 0) {
+		/* XXX Uninit port */
+		goto fail;
+	}
+	
+	list_append(&mdev->mouse_devs, &mouse_devs);
+	return;
+	
+fail:
+	free(mdev);
+}
+
 /** Add new kbdev device.
  *
- * @param dev_path	Filesystem path to the device (/dev/class/...)
+ * @param dev_path Filesystem path to the device (/dev/class/...)
+ *
  */
 static int kbd_add_kbdev(const char *dev_path)
 {
-	kbd_dev_t *kdev;
-
-	kdev = kbd_dev_new();
+	kbd_dev_t *kdev = kbd_dev_new();
 	if (kdev == NULL)
 		return -1;
-
+	
 	kdev->dev_path = dev_path;
 	kdev->port_ops = NULL;
 	kdev->ctl_ops = &kbdev_ctl;
-
+	
 	/* Initialize controller driver. */
 	if ((*kdev->ctl_ops->init)(kdev) != 0) {
 		goto fail;
 	}
-
+	
 	list_append(&kdev->kbd_devs, &kbd_devs);
 	return EOK;
+	
 fail:
 	free(kdev);
+	return -1;
+}
+
+/** Add new mousedev device.
+ *
+ * @param dev_path Filesystem path to the device (/dev/class/...)
+ *
+ */
+static int mouse_add_mousedev(const char *dev_path)
+{
+	mouse_dev_t *mdev = mouse_dev_new();
+	if (mdev == NULL)
+		return -1;
+	
+	mdev->dev_path = dev_path;
+	mdev->port_ops = NULL;
+	mdev->proto_ops = &mousedev_proto;
+	
+	/* Initialize controller driver. */
+	if ((*mdev->proto_ops->init)(mdev) != 0) {
+		goto fail;
+	}
+	
+	list_append(&mdev->mouse_devs, &mouse_devs);
+	return EOK;
+	
+fail:
+	free(mdev);
 	return -1;
 }
@@ -375,4 +435,27 @@
 }
 
+/** Add legacy drivers/devices. */
+static void mouse_add_legacy_devs(void)
+{
+	/*
+	 * Need to add these drivers based on config unless we can probe
+	 * them automatically.
+	 */
+#if defined(UARCH_amd64)
+	mouse_add_dev(&chardev_mouse_port, &ps2_proto);
+#endif
+#if defined(UARCH_ia32)
+	mouse_add_dev(&chardev_mouse_port, &ps2_proto);
+#endif
+#if defined(MACHINE_i460GX)
+	mouse_add_dev(&chardev_mouse_port, &ps2_proto);
+#endif
+#if defined(UARCH_ppc32)
+	mouse_add_dev(&adb_mouse_port, &adb_proto);
+#endif
+	/* Silence warning on abs32le about mouse_add_dev() being unused */
+	(void) mouse_add_dev;
+}
+
 static void kbd_devs_yield(void)
 {
@@ -381,5 +464,5 @@
 		kbd_dev_t *kdev = list_get_instance(kdev_link, kbd_dev_t,
 		    kbd_devs);
-
+		
 		/* Yield port */
 		if (kdev->port_ops != NULL)
@@ -394,5 +477,5 @@
 		kbd_dev_t *kdev = list_get_instance(kdev_link, kbd_dev_t,
 		    kbd_devs);
-
+		
 		/* Reclaim port */
 		if (kdev->port_ops != NULL)
@@ -405,5 +488,6 @@
  * Looks under /dev/class/keyboard and /dev/class/mouse.
  *
- * @param arg	Ignored
+ * @param arg Ignored
+ *
  */
 static int dev_discovery_fibril(void *arg)
@@ -413,8 +497,8 @@
 	size_t mouse_id = 1;
 	int rc;
-
+	
 	while (true) {
 		async_usleep(DISCOVERY_POLL_INTERVAL);
-
+		
 		/*
 		 * Check for new keyboard device
@@ -423,15 +507,15 @@
 		if (rc < 0)
 			continue;
-
+		
 		if (kbd_add_kbdev(dev_path) == EOK) {
-			printf(NAME ": Connected keyboard device '%s'\n",
-			    dev_path);
-
+			printf("%s: Connected keyboard device '%s'\n",
+			    NAME, dev_path);
+			
 			/* XXX Handle device removal */
 			++kbd_id;
 		}
-
+		
 		free(dev_path);
-
+		
 		/*
 		 * Check for new mouse device
@@ -440,16 +524,16 @@
 		if (rc < 0)
 			continue;
-
-		if (mouse_add_dev(dev_path) == EOK) {
-			printf(NAME ": Connected mouse device '%s'\n",
-			    dev_path);
-
+		
+		if (mouse_add_mousedev(dev_path) == EOK) {
+			printf("%s: Connected mouse device '%s'\n",
+			    NAME, dev_path);
+			
 			/* XXX Handle device removal */
 			++mouse_id;
 		}
-
+		
 		free(dev_path);
 	}
-
+	
 	return EOK;
 }
@@ -458,12 +542,11 @@
 static void input_start_dev_discovery(void)
 {
-	fid_t fid;
-
-	fid = fibril_create(dev_discovery_fibril, NULL);
+	fid_t fid = fibril_create(dev_discovery_fibril, NULL);
 	if (!fid) {
-		printf(NAME ": Failed to create device discovery fibril.\n");
+		printf("%s: Failed to create device discovery fibril.\n",
+		    NAME);
 		return;
 	}
-
+	
 	fibril_add_ready(fid);
 }
@@ -490,7 +573,7 @@
 	/* Add legacy keyboard devices. */
 	kbd_add_legacy_devs();
-
-	/* Add legacy (devmap-style) mouse device. */
-	(void) mouse_add_dev("/dev/hid_in/mouse");
+	
+	/* Add legacy mouse devices. */
+	mouse_add_legacy_devs();
 	
 	/* Register driver */
@@ -509,11 +592,11 @@
 		return -1;
 	}
-
+	
 	/* Start looking for new input devices */
 	input_start_dev_discovery();
-
-	printf(NAME ": Accepting connections\n");
+	
+	printf("%s: Accepting connections\n", NAME);
 	async_manager();
-
+	
 	/* Not reached. */
 	return 0;
Index: uspace/srv/hid/input/generic/layout.c
===================================================================
--- uspace/srv/hid/input/generic/layout.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/generic/layout.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -48,5 +48,5 @@
 	layout = calloc(1, sizeof(layout_t));
 	if (layout == NULL) {
-		printf(NAME ": Out of memory.\n");
+		printf("%s: Out of memory.\n", NAME);
 		return NULL;
 	}
Index: uspace/srv/hid/input/generic/mouse.c
===================================================================
--- uspace/srv/hid/input/generic/mouse.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ 	(revision )
@@ -1,165 +1,0 @@
-/*
- * Copyright (c) 2011 Jiri Svoboda
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- * - Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- * - The name of the author may not be used to endorse or promote products
- *   derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @addtogroup inputgen generic
- * @brief Mouse device handling.
- * @ingroup input
- * @{
- */
-/** @file
- */
-
-#include <adt/list.h>
-#include <async.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <input.h>
-#include <ipc/mouse.h>
-#include <mouse.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <vfs/vfs_sess.h>
-
-static void mouse_callback_conn(ipc_callid_t, ipc_call_t *, void *);
-
-static mouse_dev_t *mouse_dev_new(void)
-{
-	mouse_dev_t *mdev;
-
-	mdev = calloc(1, sizeof(mouse_dev_t));
-	if (mdev == NULL) {
-		printf(NAME ": Error allocating mouse device. "
-		    "Out of memory.\n");
-		return NULL;
-	}
-
-	link_initialize(&mdev->mouse_devs);
-	return mdev;
-}
-
-static int mouse_dev_connect(mouse_dev_t *mdev, const char *dev_path)
-{
-	async_sess_t *sess;
-	async_exch_t *exch;
-	int fd;
-	int rc;
-
-	fd = open(dev_path, O_RDWR);
-	if (fd < 0) {
-		return -1;
-	}
-
-	sess = fd_session(EXCHANGE_SERIALIZE, fd);
-	if (sess == NULL) {
-		printf(NAME ": Failed starting session with '%s'\n", dev_path);
-		close(fd);
-		return -1;
-	}
-
-	exch = async_exchange_begin(sess);
-	if (exch == NULL) {
-		printf(NAME ": Failed starting exchange with '%s'.\n", dev_path);
-		return -1;
-	}
-
-	rc = async_connect_to_me(exch, 0, 0, 0, mouse_callback_conn, mdev);
-	if (rc != EOK) {
-		printf(NAME ": Failed creating callback connection from '%s'.\n",
-		    dev_path);
-		async_exchange_end(exch);
-		return -1;
-	}
-
-	async_exchange_end(exch);
-
-	mdev->dev_path = dev_path;
-	return 0;
-}
-
-/** Add new mouse device.
- *
- * @param dev_path	Filesystem path to the device (/dev/class/...)
- */
-int mouse_add_dev(const char *dev_path)
-{
-	mouse_dev_t *mdev;
-	int rc;
-
-	mdev = mouse_dev_new();
-	if (mdev == NULL)
-		return -1;
-
-	rc = mouse_dev_connect(mdev, dev_path);
-	if (rc != EOK) {
-		free(mdev);
-		return -1;
-	}
-
-	list_append(&mdev->mouse_devs, &mouse_devs);
-	return EOK;
-}
-
-/** Mouse device callback connection handler. */
-static void mouse_callback_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
-{
-	int retval;
-
-	while (true) {
-		ipc_call_t call;
-		ipc_callid_t callid;
-
-		callid = async_get_call(&call);
-		if (!IPC_GET_IMETHOD(call)) {
-			/* XXX Handle hangup */
-			return;
-		}
-
-		switch (IPC_GET_IMETHOD(call)) {
-		case MEVENT_BUTTON:
-			input_event_button(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			retval = 0;
-			break;
-		case MEVENT_MOVE:
-			input_event_move(IPC_GET_ARG1(call),
-			    IPC_GET_ARG2(call));
-			retval = 0;
-			break;
-		default:
-			retval = ENOTSUP;
-			break;
-		}
-
-		async_answer_0(callid, retval);
-	}
-}
-
-/**
- * @}
- */
Index: uspace/srv/hid/input/generic/stroke.c
===================================================================
--- uspace/srv/hid/input/generic/stroke.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/generic/stroke.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -60,5 +60,5 @@
 	while (mods_keys[i][0] != 0) {
 		if (mod & mods_keys[i][0]) {
-			kbd_push_ev(kdev, KEY_PRESS, mods_keys[i][1]);
+			kbd_push_event(kdev, KEY_PRESS, mods_keys[i][1]);
 		}
 		++i;
@@ -67,6 +67,6 @@
 	/* Simulate key press and release. */
 	if (key != 0) {
-		kbd_push_ev(kdev, KEY_PRESS, key);
-		kbd_push_ev(kdev, KEY_RELEASE, key);
+		kbd_push_event(kdev, KEY_PRESS, key);
+		kbd_push_event(kdev, KEY_RELEASE, key);
 	}
 
@@ -75,5 +75,5 @@
 	while (mods_keys[i][0] != 0) {
 		if (mod & mods_keys[i][0]) {
-			kbd_push_ev(kdev, KEY_RELEASE, mods_keys[i][1]);
+			kbd_push_event(kdev, KEY_RELEASE, mods_keys[i][1]);
 		}
 		++i;
Index: uspace/srv/hid/input/include/input.h
===================================================================
--- uspace/srv/hid/input/include/input.h	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/include/input.h	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -42,13 +42,8 @@
 
 #define NAME       "input"
-#define NAMESPACE  "hid_in"
+#define NAMESPACE  "hid"
 
 extern bool irc_service;
 extern int irc_phone;
-
-extern list_t mouse_devs;
-
-void input_event_move(int, int);
-void input_event_button(int bnum, int press);
 
 #endif
Index: uspace/srv/hid/input/include/kbd.h
===================================================================
--- uspace/srv/hid/input/include/kbd.h	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/include/kbd.h	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -71,6 +71,6 @@
 } kbd_dev_t;
 
-extern void kbd_push_scancode(kbd_dev_t *, int);
-extern void kbd_push_ev(kbd_dev_t *, int, unsigned int);
+extern void kbd_push_data(kbd_dev_t *, sysarg_t);
+extern void kbd_push_event(kbd_dev_t *, int, unsigned int);
 
 #endif
Index: uspace/srv/hid/input/include/kbd_ctl.h
===================================================================
--- uspace/srv/hid/input/include/kbd_ctl.h	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/include/kbd_ctl.h	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -28,6 +28,6 @@
 
 /** @addtogroup inputgen generic
- * @brief	Keyboard controller driver interface.
- * @ingroup  input
+ * @brief Keyboard controller driver interface.
+ * @ingroup input
  * @{
  */
@@ -43,7 +43,7 @@
 
 typedef struct kbd_ctl_ops {
-	void (*parse_scancode)(int);
+	void (*parse)(sysarg_t);
 	int (*init)(struct kbd_dev *);
-	void (*set_ind)(struct kbd_dev *, unsigned);
+	void (*set_ind)(struct kbd_dev *, unsigned int);
 } kbd_ctl_ops_t;
 
@@ -59,4 +59,3 @@
 /**
  * @}
- */ 
-
+ */
Index: uspace/srv/hid/input/include/kbd_port.h
===================================================================
--- uspace/srv/hid/input/include/kbd_port.h	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/include/kbd_port.h	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -28,6 +28,6 @@
 
 /** @addtogroup inputgen generic
- * @brief	Keyboard port driver interface.
- * @ingroup  input
+ * @brief Keyboard port driver interface.
+ * @ingroup input
  * @{
  */
@@ -51,5 +51,4 @@
 extern kbd_port_ops_t adb_port;
 extern kbd_port_ops_t chardev_port;
-extern kbd_port_ops_t dummy_port;
 extern kbd_port_ops_t gxemul_port;
 extern kbd_port_ops_t msim_port;
@@ -65,4 +64,3 @@
 /**
  * @}
- */ 
-
+ */
Index: uspace/srv/hid/input/include/mouse.h
===================================================================
--- uspace/srv/hid/input/include/mouse.h	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/include/mouse.h	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -40,13 +40,24 @@
 #include <adt/list.h>
 
+struct mouse_port_ops;
+struct mouse_proto_ops;
+
 typedef struct mouse_dev {
 	/** Link to mouse_devs list */
 	link_t mouse_devs;
-
-	/** Path to the device */
+	
+	/** Path to the device (only for mouseev devices) */
 	const char *dev_path;
+	
+	/** Port ops */
+	struct mouse_port_ops *port_ops;
+	
+	/** Protocol ops */
+	struct mouse_proto_ops *proto_ops;
 } mouse_dev_t;
 
-int mouse_add_dev(const char *dev_path);
+extern void mouse_push_data(mouse_dev_t *, sysarg_t);
+extern void mouse_push_event_move(mouse_dev_t *, int, int);
+extern void mouse_push_event_button(mouse_dev_t *, int, int);
 
 #endif
Index: uspace/srv/hid/input/include/mouse_port.h
===================================================================
--- uspace/srv/hid/input/include/mouse_port.h	(revision ee2fa30aed4921e38c357a0420357b61329be533)
+++ uspace/srv/hid/input/include/mouse_port.h	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Martin Decky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup inputgen generic
+ * @brief Mouse port driver interface.
+ * @ingroup input
+ * @{
+ */
+/** @file
+ */
+
+#ifndef MOUSE_PORT_H_
+#define MOUSE_PORT_H_
+
+#include <sys/types.h>
+
+struct mouse_dev;
+
+typedef struct mouse_port_ops {
+	int (*init)(struct mouse_dev *);
+	void (*yield)(void);
+	void (*reclaim)(void);
+	void (*write)(uint8_t);
+} mouse_port_ops_t;
+
+extern mouse_port_ops_t adb_mouse_port;
+extern mouse_port_ops_t chardev_mouse_port;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/hid/input/include/mouse_proto.h
===================================================================
--- uspace/srv/hid/input/include/mouse_proto.h	(revision ee2fa30aed4921e38c357a0420357b61329be533)
+++ uspace/srv/hid/input/include/mouse_proto.h	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2011 Martin Decky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup inputgen generic
+ * @brief Mouse protocol driver interface.
+ * @ingroup input
+ * @{
+ */
+/** @file
+ */
+
+#ifndef MOUSE_PROTO_H_
+#define MOUSE_PROTO_H_
+
+#include <mouse_port.h>
+
+struct mouse_dev;
+
+typedef struct mouse_proto_ops {
+	void (*parse)(sysarg_t);
+	int (*init)(struct mouse_dev *);
+} mouse_proto_ops_t;
+
+extern mouse_proto_ops_t adb_proto;
+extern mouse_proto_ops_t ps2_proto;
+extern mouse_proto_ops_t mousedev_proto;
+
+#endif
+
+/**
+ * @}
+ */
Index: uspace/srv/hid/input/layout/cz.c
===================================================================
--- uspace/srv/hid/input/layout/cz.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/layout/cz.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -391,5 +391,5 @@
 	cz_state = malloc(sizeof(layout_cz_t));
 	if (cz_state == NULL) {
-		printf(NAME ": Out of memory.\n");
+		printf("%s: Out of memory.\n", NAME);
 		return ENOMEM;
 	}
Index: uspace/srv/hid/input/port/adb.c
===================================================================
--- uspace/srv/hid/input/port/adb.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/port/adb.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -93,5 +93,5 @@
 	async_exchange_end(exch);
 	if (rc != EOK) {
-		printf(NAME ": Failed to create callback from device\n");
+		printf("%s: Failed to create callback from device\n", NAME);
 		async_hangup(dev_sess);
 		return rc;
@@ -142,13 +142,12 @@
 static void adb_kbd_reg0_data(uint16_t data)
 {
-	uint8_t b0, b1;
-
-	b0 = (data >> 8) & 0xff;
-	b1 = data & 0xff;
-
+	uint8_t b0 = (data >> 8) & 0xff;
+	uint8_t b1 = data & 0xff;
+	
 	if (b0 != 0xff)
-		kbd_push_scancode(kbd_dev, b0);
+		kbd_push_data(kbd_dev, b0);
+	
 	if (b1 != 0xff)
-		kbd_push_scancode(kbd_dev, b1);
+		kbd_push_data(kbd_dev, b1);
 }
 
Index: uspace/srv/hid/input/port/adb_mouse.c
===================================================================
--- uspace/srv/hid/input/port/adb_mouse.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
+++ uspace/srv/hid/input/port/adb_mouse.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2011 Martin Decky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup mouse_port
+ * @ingroup mouse
+ * @{
+ */
+/** @file
+ * @brief ADB mouse port driver.
+ */
+
+#include <ipc/adb.h>
+#include <async.h>
+#include <input.h>
+#include <mouse_port.h>
+#include <mouse.h>
+#include <errno.h>
+#include <devmap.h>
+
+static mouse_dev_t *mouse_dev;
+static async_sess_t *dev_sess;
+
+static void mouse_port_events(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	/* Ignore parameters, the connection is already opened */
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		int retval;
+		
+		if (!IPC_GET_IMETHOD(call)) {
+			/* TODO: Handle hangup */
+			return;
+		}
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case ADB_REG_NOTIF:
+			mouse_push_data(mouse_dev, IPC_GET_ARG1(call));
+			break;
+		default:
+			retval = ENOENT;
+		}
+		
+		async_answer_0(callid, retval);
+	}
+}
+
+static int adb_port_init(mouse_dev_t *mdev)
+{
+	const char *dev = "adb/mouse";
+	
+	mouse_dev = mdev;
+	
+	devmap_handle_t handle;
+	int rc = devmap_device_get_handle(dev, &handle, 0);
+	if (rc != EOK)
+		return rc;
+	
+	dev_sess = devmap_device_connect(EXCHANGE_ATOMIC, handle, 0);
+	if (dev_sess == NULL) {
+		printf("%s: Failed to connect to device\n", NAME);
+		return ENOENT;
+	}
+	
+	async_exch_t *exch = async_exchange_begin(dev_sess);
+	if (exch == NULL) {
+		printf("%s: Failed starting exchange with device\n", NAME);
+		async_hangup(dev_sess);
+		return ENOMEM;
+	}
+	
+	/* NB: The callback connection is slotted for removal */
+	rc = async_connect_to_me(exch, 0, 0, 0, mouse_port_events, NULL);
+	async_exchange_end(exch);
+	if (rc != EOK) {
+		printf("%s: Failed to create callback from device\n", NAME);
+		async_hangup(dev_sess);
+		return rc;
+	}
+	
+	return EOK;
+}
+
+static void adb_port_yield(void)
+{
+}
+
+static void adb_port_reclaim(void)
+{
+}
+
+static void adb_port_write(uint8_t data)
+{
+}
+
+mouse_port_ops_t adb_mouse_port = {
+	.init = adb_port_init,
+	.yield = adb_port_yield,
+	.reclaim = adb_port_reclaim,
+	.write = adb_port_write
+};
+
+/**
+ * @}
+ */
Index: uspace/srv/hid/input/port/chardev.c
===================================================================
--- uspace/srv/hid/input/port/chardev.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/port/chardev.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -108,5 +108,5 @@
 	
 	if (rc != 0) {
-		printf(NAME ": Failed to create callback from device\n");
+		printf("%s: Failed to create callback from device\n", NAME);
 		async_hangup(dev_sess);
 		return -1;
@@ -153,5 +153,5 @@
 		switch (IPC_GET_IMETHOD(call)) {
 		case CHAR_NOTIF_BYTE:
-			kbd_push_scancode(kbd_dev, IPC_GET_ARG1(call));
+			kbd_push_data(kbd_dev, IPC_GET_ARG1(call));
 			break;
 		default:
Index: uspace/srv/hid/input/port/chardev_mouse.c
===================================================================
--- uspace/srv/hid/input/port/chardev_mouse.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
+++ uspace/srv/hid/input/port/chardev_mouse.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2011 Martin Decky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup mouse_port
+ * @ingroup mouse
+ * @{
+ */
+/** @file
+ * @brief Chardev mouse port driver.
+ */
+
+#include <ipc/char.h>
+#include <stdio.h>
+#include <async.h>
+#include <errno.h>
+#include <devmap.h>
+#include <input.h>
+#include <mouse_port.h>
+#include <mouse.h>
+
+static mouse_dev_t *mouse_dev;
+static async_sess_t *dev_sess;
+
+/** List of devices to try connecting to. */
+static const char *in_devs[] = {
+	"char/ps2b",
+};
+
+static const unsigned int num_devs = sizeof(in_devs) / sizeof(in_devs[0]);
+
+static void mouse_port_events(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	/* Ignore parameters, the connection is already opened */
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call)) {
+			/* TODO: Handle hangup */
+			return;
+		}
+		
+		int retval;
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case CHAR_NOTIF_BYTE:
+			mouse_push_data(mouse_dev, IPC_GET_ARG1(call));
+			break;
+		default:
+			retval = ENOENT;
+		}
+		
+		async_answer_0(callid, retval);
+	}
+}
+
+static int chardev_port_init(mouse_dev_t *mdev)
+{
+	devmap_handle_t handle;
+	unsigned int i;
+	int rc;
+	
+	mouse_dev = mdev;
+	
+	for (i = 0; i < num_devs; i++) {
+		rc = devmap_device_get_handle(in_devs[i], &handle, 0);
+		if (rc == EOK)
+			break;
+	}
+	
+	if (i >= num_devs) {
+		printf("%s: Could not find any suitable input device\n", NAME);
+		return -1;
+	}
+	
+	dev_sess = devmap_device_connect(EXCHANGE_ATOMIC, handle,
+	    IPC_FLAG_BLOCKING);
+	if (dev_sess == NULL) {
+		printf("%s: Failed connecting to device\n", NAME);
+		return ENOENT;
+	}
+	
+	async_exch_t *exch = async_exchange_begin(dev_sess);
+	if (exch == NULL) {
+		printf("%s: Failed starting exchange with device\n", NAME);
+		async_hangup(dev_sess);
+		return ENOMEM;
+	}
+	
+	/* NB: The callback connection is slotted for removal */
+	rc = async_connect_to_me(exch, 0, 0, 0, mouse_port_events, NULL);
+	async_exchange_end(exch);
+	
+	if (rc != 0) {
+		printf("%s: Failed to create callback from device\n", NAME);
+		async_hangup(dev_sess);
+		return -1;
+	}
+	
+	return 0;
+}
+
+static void chardev_port_yield(void)
+{
+}
+
+static void chardev_port_reclaim(void)
+{
+}
+
+static void chardev_port_write(uint8_t data)
+{
+	async_exch_t *exch = async_exchange_begin(dev_sess);
+	if (exch == NULL) {
+		printf("%s: Failed starting exchange with device\n", NAME);
+		return;
+	}
+
+	async_msg_1(exch, CHAR_WRITE_BYTE, data);
+	async_exchange_end(exch);
+}
+
+mouse_port_ops_t chardev_mouse_port = {
+	.init = chardev_port_init,
+	.yield = chardev_port_yield,
+	.reclaim = chardev_port_reclaim,
+	.write = chardev_port_write
+};
+
+/**
+ * @}
+ */
Index: uspace/srv/hid/input/port/gxemul.c
===================================================================
--- uspace/srv/hid/input/port/gxemul.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/port/gxemul.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -108,15 +108,12 @@
 
 /** Process data sent when a key is pressed.
- *  
- *  @param keybuffer Buffer of pressed keys.
- *  @param call      IPC call.
  *
- *  @return Always 1.
+ * @param keybuffer Buffer of pressed keys.
+ * @param call      IPC call.
+ *
  */
 static void gxemul_irq_handler(ipc_callid_t iid, ipc_call_t *call)
 {
-	int scan_code = IPC_GET_ARG2(*call);
-
-	kbd_push_scancode(kbd_dev, scan_code);
+	kbd_push_data(kbd_dev, IPC_GET_ARG2(*call));
 }
 
Index: uspace/srv/hid/input/port/msim.c
===================================================================
--- uspace/srv/hid/input/port/msim.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/port/msim.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -109,8 +109,7 @@
 static void msim_irq_handler(ipc_callid_t iid, ipc_call_t *call)
 {
-	int scan_code = IPC_GET_ARG2(*call);
-	kbd_push_scancode(kbd_dev, scan_code);
+	kbd_push_data(kbd_dev, IPC_GET_ARG2(*call));
 }
 
 /** @}
-*/
+ */
Index: uspace/srv/hid/input/port/niagara.c
===================================================================
--- uspace/srv/hid/input/port/niagara.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/port/niagara.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -148,6 +148,6 @@
 		c = input_buffer->data[input_buffer->read_ptr];
 		input_buffer->read_ptr =
-			((input_buffer->read_ptr) + 1) % INPUT_BUFFER_SIZE;
-		kbd_push_scancode(kbd_dev, c);
+		    ((input_buffer->read_ptr) + 1) % INPUT_BUFFER_SIZE;
+		kbd_push_data(kbd_dev, c);
 	}
 }
Index: uspace/srv/hid/input/port/ns16550.c
===================================================================
--- uspace/srv/hid/input/port/ns16550.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/port/ns16550.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -156,6 +156,5 @@
 static void ns16550_irq_handler(ipc_callid_t iid, ipc_call_t *call)
 {
-	int scan_code = IPC_GET_ARG2(*call);
-	kbd_push_scancode(kbd_dev, scan_code);
+	kbd_push_data(kbd_dev, IPC_GET_ARG2(*call));
 	
 	if (irc_service)
Index: uspace/srv/hid/input/port/pl050.c
===================================================================
--- uspace/srv/hid/input/port/pl050.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/port/pl050.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -137,11 +137,8 @@
 static void pl050_irq_handler(ipc_callid_t iid, ipc_call_t *call)
 {
-	int scan_code = IPC_GET_ARG2(*call);
-
-	kbd_push_scancode(kbd_dev, scan_code);
-	return;
+	kbd_push_data(kbd_dev, IPC_GET_ARG2(*call));
 }
 
 /**
  * @}
- */ 
+ */
Index: uspace/srv/hid/input/port/sgcn.c
===================================================================
--- uspace/srv/hid/input/port/sgcn.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/port/sgcn.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -183,6 +183,6 @@
 		*in_rdptr_ptr = (((*in_rdptr_ptr) - begin + 1) % size) + begin;
 		buf_ptr = (volatile char *)
-			SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
-		kbd_push_scancode(kbd_dev, c);
+		    SGCN_BUFFER(char, SGCN_BUFFER_HEADER->in_rdptr);
+		kbd_push_data(kbd_dev, c);
 	}
 }
Index: uspace/srv/hid/input/port/ski.c
===================================================================
--- uspace/srv/hid/input/port/ski.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/port/ski.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -110,5 +110,5 @@
 			if (c == 0)
 				break;
-			kbd_push_scancode(kbd_dev, c);
+			kbd_push_data(kbd_dev, c);
 		}
 
Index: uspace/srv/hid/input/port/z8530.c
===================================================================
--- uspace/srv/hid/input/port/z8530.c	(revision a40dea38c9d586aa4f82dcde5704326ff5abae9d)
+++ uspace/srv/hid/input/port/z8530.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -142,6 +142,5 @@
 static void z8530_irq_handler(ipc_callid_t iid, ipc_call_t *call)
 {
-	int scan_code = IPC_GET_ARG2(*call);
-	kbd_push_scancode(kbd_dev, scan_code);
+	kbd_push_data(kbd_dev, IPC_GET_ARG2(*call));
 	
 	if (irc_service)
Index: uspace/srv/hid/input/proto/adb.c
===================================================================
--- uspace/srv/hid/input/proto/adb.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
+++ uspace/srv/hid/input/proto/adb.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2011 Martin Decky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup mouse_proto
+ * @ingroup input
+ * @{
+ */
+/**
+ * @file
+ * @brief ADB protocol driver.
+ */
+
+#include <bool.h>
+#include <mouse.h>
+#include <mouse_port.h>
+#include <mouse_proto.h>
+
+static mouse_dev_t *mouse_dev;
+static bool b1_pressed;
+static bool b2_pressed;
+
+static int adb_proto_init(mouse_dev_t *mdev)
+{
+	mouse_dev = mdev;
+	b1_pressed = false;
+	b2_pressed = false;
+	
+	return 0;
+}
+
+/** Process mouse data */
+static void adb_proto_parse(sysarg_t data)
+{
+	bool b1, b2;
+	uint16_t udx, udy;
+	int dx, dy;
+	
+	/* Extract fields. */
+	b1 = ((data >> 15) & 1) == 0;
+	udy = (data >> 8) & 0x7f;
+	b2 = ((data >> 7) & 1) == 0;
+	udx = data & 0x7f;
+	
+	/* Decode 7-bit two's complement signed values. */
+	dx = (udx & 0x40) ? (udx - 0x80) : udx;
+	dy = (udy & 0x40) ? (udy - 0x80) : udy;
+	
+	if (b1 != b1_pressed) {
+		mouse_push_event_button(mouse_dev, 1, b1);
+		b1_pressed = b1;
+	}
+	
+	if (b2 != b2_pressed) {
+		mouse_push_event_button(mouse_dev, 2, b2);
+		b1_pressed = b1;
+	}
+	
+	if (dx != 0 || dy != 0)
+		mouse_push_event_move(mouse_dev, dx, dy);
+}
+
+mouse_proto_ops_t adb_proto = {
+	.parse = adb_proto_parse,
+	.init = adb_proto_init
+};
+
+/**
+ * @}
+ */
Index: uspace/srv/hid/input/proto/mousedev.c
===================================================================
--- uspace/srv/hid/input/proto/mousedev.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
+++ uspace/srv/hid/input/proto/mousedev.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2011 Martin Decky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup mouse_proto
+ * @ingroup input
+ * @{
+ */
+/**
+ * @file
+ * @brief Mouse device connector controller driver.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <vfs/vfs_sess.h>
+#include <malloc.h>
+#include <async.h>
+#include <errno.h>
+#include <ipc/mouseev.h>
+#include <input.h>
+#include <mouse.h>
+#include <mouse_port.h>
+#include <mouse_proto.h>
+
+/** Mousedev softstate */
+typedef struct {
+	/** Link to generic mouse device */
+	mouse_dev_t *mouse_dev;
+	
+	/** Session to mouse device */
+	async_sess_t *sess;
+	
+	/** File descriptor of open mousedev device */
+	int fd;
+} mousedev_t;
+
+static mousedev_t *mousedev_new(mouse_dev_t *mdev)
+{
+	mousedev_t *mousedev = calloc(1, sizeof(mousedev_t));
+	if (mousedev == NULL)
+		return NULL;
+	
+	mousedev->mouse_dev = mdev;
+	mousedev->fd = -1;
+	
+	return mousedev;
+}
+
+static void mousedev_destroy(mousedev_t *mousedev)
+{
+	if (mousedev->sess != NULL)
+		async_hangup(mousedev->sess);
+	
+	if (mousedev->fd >= 0)
+		close(mousedev->fd);
+	
+	free(mousedev);
+}
+
+static void mousedev_callback_conn(ipc_callid_t iid, ipc_call_t *icall,
+    void *arg)
+{
+	/* Mousedev device structure */
+	mousedev_t *mousedev = (mousedev_t *) arg;
+	
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		
+		if (!IPC_GET_IMETHOD(call)) {
+			/* XXX Handle hangup */
+			return;
+		}
+		
+		int retval;
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case MOUSEEV_MOVE_EVENT:
+			mouse_push_event_move(mousedev->mouse_dev, IPC_GET_ARG1(call),
+			    IPC_GET_ARG2(call));
+			retval = EOK;
+			break;
+		case MOUSEEV_BUTTON_EVENT:
+			mouse_push_event_button(mousedev->mouse_dev, IPC_GET_ARG1(call),
+			    IPC_GET_ARG2(call));
+			retval = EOK;
+			break;
+		default:
+			retval = ENOTSUP;
+			break;
+		}
+		
+		async_answer_0(callid, retval);
+	}
+}
+
+static int mousedev_proto_init(mouse_dev_t *mdev)
+{
+	const char *pathname = mdev->dev_path;
+	
+	int fd = open(pathname, O_RDWR);
+	if (fd < 0)
+		return -1;
+	
+	async_sess_t *sess = fd_session(EXCHANGE_SERIALIZE, fd);
+	if (sess == NULL) {
+		printf("%s: Failed starting session with '%s'\n", NAME, pathname);
+		close(fd);
+		return -1;
+	}
+	
+	mousedev_t *mousedev = mousedev_new(mdev);
+	if (mousedev == NULL) {
+		printf("%s: Failed allocating device structure for '%s'.\n",
+		    NAME, pathname);
+		return -1;
+	}
+	
+	mousedev->fd = fd;
+	mousedev->sess = sess;
+	
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (exch == NULL) {
+		printf("%s: Failed starting exchange with '%s'.\n", NAME, pathname);
+		mousedev_destroy(mousedev);
+		return -1;
+	}
+	
+	int rc = async_connect_to_me(exch, 0, 0, 0, mousedev_callback_conn, mousedev);
+	async_exchange_end(exch);
+	
+	if (rc != EOK) {
+		printf("%s: Failed creating callback connection from '%s'.\n",
+		    NAME, pathname);
+		mousedev_destroy(mousedev);
+		return -1;
+	}
+	
+	return 0;
+}
+
+mouse_proto_ops_t mousedev_proto = {
+	.init = mousedev_proto_init
+};
+
+/**
+ * @}
+ */
Index: uspace/srv/hid/input/proto/ps2.c
===================================================================
--- uspace/srv/hid/input/proto/ps2.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
+++ uspace/srv/hid/input/proto/ps2.c	(revision ee2fa30aed4921e38c357a0420357b61329be533)
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2011 Martin Decky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup mouse_proto
+ * @ingroup input
+ * @{
+ */
+/**
+ * @file
+ * @brief PS/2 protocol driver.
+ */
+
+#include <mouse.h>
+#include <mouse_port.h>
+#include <mouse_proto.h>
+
+#define PS2_MOUSE_OUT_INIT  0xf4
+#define PS2_MOUSE_ACK       0xfa
+
+#define BUFSIZE 3
+
+typedef struct {
+	union {
+		unsigned char data[BUFSIZE];
+		struct {
+			unsigned int leftbtn : 1;
+			unsigned int rightbtn : 1;
+			unsigned int middlebtn : 1;
+			unsigned int isone : 1; /* Always one */
+			unsigned int xsign : 1;
+			unsigned int ysign : 1;
+			unsigned int xovfl : 1;
+			unsigned int yovfl : 1;
+			unsigned char x;
+			unsigned char y;
+		} val;
+	} u;
+} ps2packet_t;
+
+static ps2packet_t buf;
+static unsigned int bufpos;
+static unsigned int leftbtn;
+static unsigned int rightbtn;
+static unsigned int middlebtn;
+
+static mouse_dev_t *mouse_dev;
+
+static int ps2_proto_init(mouse_dev_t *mdev)
+{
+	mouse_dev = mdev;
+	bufpos = 0;
+	leftbtn = 0;
+	rightbtn = 0;
+	
+	mouse_dev->port_ops->write(PS2_MOUSE_OUT_INIT);
+	return 0;
+}
+
+/** Convert 9-bit 2-complement signed number to integer */
+static int bit9toint(int sign, unsigned char data)
+{
+	int tmp;
+	
+	if (!sign)
+		return data;
+	
+	tmp = ((unsigned char) ~data) + 1;
+	return -tmp;
+}
+
+/** Process mouse data */
+static void ps2_proto_parse(sysarg_t data)
+{
+	int x, y;
+	
+	/* Check that we have not lost synchronization */
+	if (bufpos == 0 && !(data & 0x8))
+		return; /* Synchro lost, ignore byte */
+	
+	buf.u.data[bufpos++] = data;
+	if (bufpos == BUFSIZE) {
+		bufpos = 0;
+		
+		if (buf.u.val.leftbtn ^ leftbtn) {
+			leftbtn = buf.u.val.leftbtn;
+			mouse_push_event_button(mouse_dev, 1, leftbtn);
+		}
+		
+		if (buf.u.val.rightbtn ^ rightbtn) {
+			rightbtn = buf.u.val.rightbtn;
+			mouse_push_event_button(mouse_dev, 2, rightbtn);
+		}
+		
+		if (buf.u.val.middlebtn ^ middlebtn) {
+			middlebtn = buf.u.val.middlebtn;
+			mouse_push_event_button(mouse_dev, 3, middlebtn);
+		}
+		
+		x = bit9toint(buf.u.val.xsign, buf.u.val.x);
+		y = -bit9toint(buf.u.val.ysign, buf.u.val.y);
+		
+		if (x != 0 || y != 0)
+			mouse_push_event_move(mouse_dev, x, y);
+	}
+}
+
+mouse_proto_ops_t ps2_proto = {
+	.parse = ps2_proto_parse,
+	.init = ps2_proto_init
+};
+
+/**
+ * @}
+ */
