Index: uspace/drv/char/ps2mouse/ps2mouse.c
===================================================================
--- uspace/drv/char/ps2mouse/ps2mouse.c	(revision db9ef0c61e487e3425327e1970bd33de373bf43b)
+++ uspace/drv/char/ps2mouse/ps2mouse.c	(revision c637571774317573011c6c6333a7bf3c06b247a5)
@@ -45,9 +45,13 @@
 #include "ps2mouse.h"
 
-#define PS2_MOUSE_OUT_INIT  0xf4
-#define PS2_MOUSE_ACK       0xfa
-
-#define BUFSIZE 3
-
+#define PS2_MOUSE_GET_DEVICE_ID   0xf2
+#define PS2_MOUSE_SET_SAMPLE_RATE   0xf3
+#define PS2_MOUSE_ENABLE_DATA_REPORT   0xf4
+#define PS2_MOUSE_ACK   0xfa
+
+#define PS2_BUFSIZE 3
+#define INTELLIMOUSE_BUFSIZE 4
+
+#define Z_SIGN (1 << 3)
 #define X_SIGN (1 << 4)
 #define Y_SIGN (1 << 5)
@@ -58,10 +62,36 @@
 #define BUTTON_RIGHT   1
 #define BUTTON_MIDDLE   2
-#define BUTTON_COUNT   3
-
-#define BUTTON_MASK(button) (1 << button)
-
+#define PS2_BUTTON_COUNT   3
+#define INTELLIMOUSE_BUTTON_COUNT 5
+
+#define PS2_BUTTON_MASK(button) (1 << button)
+
+#define MOUSE_READ_BYTE_TEST(sess, value) \
+do { \
+	uint8_t data = 0; \
+	const ssize_t size = char_dev_read(session, &data, 1); \
+	if (size != 1) { \
+		ddf_msg(LVL_ERROR, "Failed reading byte: %d)", size);\
+		return size < 0 ? size : EIO; \
+	} \
+	if (data != value) { \
+		ddf_msg(LVL_ERROR, "Failed testing byte: got %hhx vs. %hhx)", \
+		    data, value); \
+		return EIO; \
+	} \
+} while (0)
+#define MOUSE_WRITE_BYTE(sess, value) \
+do { \
+	uint8_t data = value; \
+	const ssize_t size = char_dev_write(session, &data, 1); \
+	if (size < 0 ) { \
+		ddf_msg(LVL_ERROR, "Failed writing byte: %hhx", value); \
+		return size; \
+	} \
+} while (0)
 /*----------------------------------------------------------------------------*/
 static int polling_ps2(void *);
+static int polling_intellimouse(void *);
+static int probe_intellimouse(async_sess_t *);
 static void default_connection_handler(ddf_fun_t *, ipc_callid_t, ipc_call_t *);
 /*----------------------------------------------------------------------------*/
@@ -111,9 +141,15 @@
 		return ENOMEM;
 	}
+	/* Probe IntelliMouse extensions. */
+	int (*polling_f)(void*) = polling_ps2;
+	if (probe_intellimouse(mouse->parent_sess) == EOK) {
+		ddf_msg(LVL_NOTE, "Enabled IntelliMouse extensions");
+		polling_f = polling_intellimouse;
+	}
 	/* Enable mouse data reporting. */
-	uint8_t report = PS2_MOUSE_OUT_INIT;
+	uint8_t report = PS2_MOUSE_ENABLE_DATA_REPORT;
 	ssize_t size = char_dev_write(mouse->parent_sess, &report, 1);
 	if (size != 1) {
-		ddf_msg(LVL_ERROR, "Failed to write INIT.");
+		ddf_msg(LVL_ERROR, "Failed to enable data reporting.");
 		async_hangup(mouse->parent_sess);
 		ddf_fun_unbind(mouse->mouse_fun);
@@ -125,5 +161,6 @@
 	size = char_dev_read(mouse->parent_sess, &report, 1);
 	if (size != 1 || report != PS2_MOUSE_ACK) {
-		ddf_msg(LVL_ERROR, "MOUSE FAILED TO ACK %hhx.", report);
+		ddf_msg(LVL_ERROR, "Failed to confirm data reporting: %hhx.",
+		    report);
 		async_hangup(mouse->parent_sess);
 		ddf_fun_unbind(mouse->mouse_fun);
@@ -133,5 +170,5 @@
 	}
 
-	mouse->polling_fibril = fibril_create(polling_ps2, mouse);
+	mouse->polling_fibril = fibril_create(polling_f, mouse);
 	if (!mouse->polling_fibril) {
 		async_hangup(mouse->parent_sess);
@@ -155,12 +192,12 @@
 
 	assert(mouse->parent_sess);
-	bool buttons[BUTTON_COUNT] = {};
+	bool buttons[PS2_BUTTON_COUNT] = {};
 	while (1) {
 
-		uint8_t packet[BUFSIZE] = {};
+		uint8_t packet[PS2_BUFSIZE] = {};
 		const ssize_t size =
-		    char_dev_read(mouse->parent_sess, packet, BUFSIZE);
-
-		if (size != 3) {
+		    char_dev_read(mouse->parent_sess, packet, PS2_BUFSIZE);
+
+		if (size != PS2_BUFSIZE) {
 			ddf_msg(LVL_WARN, "Incorrect packet size: %zd.", size);
 			continue;
@@ -178,7 +215,8 @@
 
 		/* Buttons */
-		for (unsigned i = 0; i < BUTTON_COUNT; ++i) {
-			if (buttons[i] != (bool)(packet[0] & BUTTON_MASK(i))) {
-				buttons[i] = !buttons[i];
+		for (unsigned i = 0; i < PS2_BUTTON_COUNT; ++i) {
+			const bool status = (packet[0] & PS2_BUTTON_MASK(i));
+			if (buttons[i] != status) {
+				buttons[i] = status;
 				async_msg_2(exch, MOUSEEV_BUTTON_EVENT, i + 1,
 				    buttons[i]);
@@ -197,4 +235,89 @@
 		async_exchange_end(exch);
 	}
+}
+/*----------------------------------------------------------------------------*/
+/** Get data and parse ps2 protocol with intellimouse extension packets.
+ * @param arg Pointer to ps2_mouse_t structure.
+ * @return Never.
+ */
+static int polling_intellimouse(void *arg)
+{
+	assert(arg);
+	const ps2_mouse_t *mouse = arg;
+
+	assert(mouse->parent_sess);
+	bool buttons[INTELLIMOUSE_BUTTON_COUNT] = {};
+	while (1) {
+
+		uint8_t packet[INTELLIMOUSE_BUFSIZE] = {};
+		const ssize_t size = char_dev_read(
+		    mouse->parent_sess, packet, INTELLIMOUSE_BUFSIZE);
+
+		if (size != INTELLIMOUSE_BUFSIZE) {
+			ddf_msg(LVL_WARN, "Incorrect packet size: %zd.", size);
+			continue;
+		}
+		ddf_msg(LVL_DEBUG2, "Got packet: %hhx:%hhx:%hhx:%hhx.",
+		    packet[0], packet[1], packet[2], packet[3]);
+
+		async_exch_t *exch =
+		    async_exchange_begin(mouse->input_sess);
+		if (!exch) {
+			ddf_msg(LVL_ERROR,
+			    "Failed to create input exchange.");
+			continue;
+		}
+		/* ps/2 Buttons */
+		for (unsigned i = 0; i < PS2_BUTTON_COUNT; ++i) {
+			const bool status = (packet[0] & PS2_BUTTON_MASK(i));
+			if (buttons[i] != status) {
+				buttons[i] = status;
+				async_msg_2(exch, MOUSEEV_BUTTON_EVENT, i + 1,
+				    buttons[i]);
+			}
+		}
+
+		/* Movement */
+		const int16_t move_x =
+		    ((packet[0] & X_SIGN) ? 0xff00 : 0) | packet[1];
+		const int16_t move_y =
+		    (((packet[0] & Y_SIGN) ? 0xff00 : 0) | packet[2]);
+		const int8_t move_z =
+		    (((packet[3] & Z_SIGN) ? 0xf0 : 0) | (packet[3] & 0xf));
+		ddf_msg(LVL_DEBUG2, "Parsed moves: %d:%d:%hhd", move_x, move_y,
+		    move_z);
+		//TODO: Consider overflow bit
+		if (move_x != 0 || move_y != 0 || move_z != 0) {
+			async_msg_3(exch, MOUSEEV_MOVE_EVENT,
+			    move_x, -move_y, -move_z);
+		}
+		async_exchange_end(exch);
+	}
+}
+/*----------------------------------------------------------------------------*/
+static int probe_intellimouse(async_sess_t *session)
+{
+	assert(session);
+
+	MOUSE_WRITE_BYTE(session, PS2_MOUSE_SET_SAMPLE_RATE);
+	MOUSE_READ_BYTE_TEST(session, PS2_MOUSE_ACK);
+	MOUSE_WRITE_BYTE(session, 200);
+	MOUSE_READ_BYTE_TEST(session, PS2_MOUSE_ACK);
+
+	MOUSE_WRITE_BYTE(session, PS2_MOUSE_SET_SAMPLE_RATE);
+	MOUSE_READ_BYTE_TEST(session, PS2_MOUSE_ACK);
+	MOUSE_WRITE_BYTE(session, 100);
+	MOUSE_READ_BYTE_TEST(session, PS2_MOUSE_ACK);
+
+	MOUSE_WRITE_BYTE(session, PS2_MOUSE_SET_SAMPLE_RATE);
+	MOUSE_READ_BYTE_TEST(session, PS2_MOUSE_ACK);
+	MOUSE_WRITE_BYTE(session, 80);
+	MOUSE_READ_BYTE_TEST(session, PS2_MOUSE_ACK);
+
+	MOUSE_WRITE_BYTE(session, PS2_MOUSE_GET_DEVICE_ID);
+	MOUSE_READ_BYTE_TEST(session, PS2_MOUSE_ACK);
+	MOUSE_READ_BYTE_TEST(session, 3);
+
+	return EOK;
 }
 /*----------------------------------------------------------------------------*/
