[OLPC touchpad] Support PT-only and GS-only operation modes.

David Woodhouse dwmw2 at infradead.unroutableorg
Wed Nov 15 04:40:31 EST 2006


Commit:     bc0b9a38bcd71cef7cb013b8593d80f2f54c924a
Parent:     714e2030f905b0ecf2c3310a042617affdca263c
commit bc0b9a38bcd71cef7cb013b8593d80f2f54c924a
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Wed Nov 15 17:44:51 2006 +0800
Commit:     David Woodhouse <dwmw2 at infradead.org>
CommitDate: Wed Nov 15 17:44:51 2006 +0800

    [OLPC touchpad] Support PT-only and GS-only operation modes.
    
    Handle all types of incoming packets, and add 'tpmode' parameter to
    initialise as desired. Also add 'tpdebug' parameter.
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 drivers/input/mouse/olpc.c |  184 ++++++++++++++++++++++++++++++++++++++++----
 drivers/input/mouse/olpc.h |    1 
 2 files changed, 169 insertions(+), 16 deletions(-)

diff --git a/drivers/input/mouse/olpc.c b/drivers/input/mouse/olpc.c
index 1023ea5..9b2a56c 100644
--- a/drivers/input/mouse/olpc.c
+++ b/drivers/input/mouse/olpc.c
@@ -20,7 +20,7 @@
  * The touchpad on the OLPC is fairly wide, with the entire area usable
  * as a tablet, and the center 1/3rd also usable as a touchpad.
  *
- * The device has simultanious reporting, so that both can be used at once.
+ * The device has simultaneous reporting, so that both can be used at once.
  *
  * The PT+GS protocol is similar to the base ALPS protocol, in that the
  * GS data is where the ALPS parser would expect to find it, however
@@ -36,14 +36,23 @@ #include <linux/libps2.h>
 
 #include "psmouse.h"
 #include "olpc.h"
+static int tpmode = 0;
+module_param(tpmode, int, 0644);
+
+static int tpdebug;
+module_param(tpdebug, int, 0644);
 
 static const struct olpc_model_info olpc_model_data[] = {
-	{ { 0x67, 0x00, 0x0a }, 0xeb, 0xff, 0 }, /* OLPC in PT+GS mode. */
-	{ { 0x67, 0x00, 0x14 }, 0xeb, 0xff, 0 }, /* Newer OLPC in PT+GS mode. */
+	{ { 0x67, 0x00, 0x0a }, 0 }, /* OLPC in PT+GS mode. */
+	{ { 0x67, 0x00, 0x14 }, 0 }, /* Newer OLPC in PT+GS mode. */
 };
 
+#define OLPC_PKT_SIM	0xeb
+#define OLPC_PKT_PT	0xcf
+#define OLPC_PKT_GS	0xff
+
 /*
- * OLPC absolute Mode - simultanious format
+ * OLPC absolute Mode - simultaneous format
  *
  * byte 0:  1    1    1    0    1    0     1     1
  * byte 1:  0  gx6  gx5  gx4  gx3  gx2   gx1   gx0
@@ -110,12 +119,126 @@ static void olpc_process_packet(struct p
 	input_sync(dev2);
 }
 
-static psmouse_ret_t olpc_process_byte(struct psmouse *psmouse)
+/*
+ * OLPC absolute Mode - single-mode format
+ *
+ * byte 0:      1    1    0    0    1    1     1     1
+ * byte 1:      0   x6   x5   x4   x3   x2    x1    x0
+ * byte 2(PT):  0    0   x9   x8   x7    ? pt-dsw gs-dsw
+ * byte 2(GS):  0  x10   x9   x8   x7    ? gs-dsw pt-dsw
+ * byte 3:      0   y9   y8   y7    1    0   swr   swl
+ * byte 4:      0   y6   y5   y4   y3   y2    y1    y0
+ * byte 5:      0   z6   z5   z4   z3   z2    z1    z0
+ *
+ * ?'s are not defined in the protocol spec, may vary between models.
+ *
+ * swr/swl are the left/right buttons.
+ *
+ * pt-dsw/gs-dsw indicate that the pt/gs sensor is detecting a
+ * pen/finger
+ */
+
+static void olpc_process_pt_packet(struct psmouse *psmouse)
+{
+	struct olpc_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev = psmouse->dev;
+	struct input_dev *dev2 = priv->dev2;
+	int x, y, z, gs_down, pt_down, left, right;
+
+	left = packet[3] & 1;
+	right = packet[3] & 2;
+	x = packet[1] | ((packet[2] & 0x78) << (7 - 3));
+	y = packet[4] | ((packet[3] & 0x70) << (7 - 4));
+	z = packet[5];
+
+	pt_down = packet[2] & 2;
+	gs_down = packet[2] & 1;
+
+	if (tpdebug) {
+		printk(KERN_DEBUG "%s %02x %02x %02x %02x %02x %02x\n",
+		       __func__, psmouse->packet[0], psmouse->packet[1],
+		       psmouse->packet[2], psmouse->packet[3], psmouse->packet[4],
+		       psmouse->packet[5]);
+		printk(KERN_DEBUG "l=%d r=%d p=%d g=%d x=%d y=%d z=%d\n",
+		       left, right, pt_down, gs_down, x, y, z);
+	}
+
+	input_report_key(dev, BTN_LEFT, left);
+	input_report_key(dev2, BTN_LEFT, left);
+	input_report_key(dev, BTN_RIGHT, right);
+	input_report_key(dev2, BTN_RIGHT, right);
+
+	input_report_key(dev, BTN_TOUCH, pt_down);
+	input_report_key(dev, BTN_TOOL_PEN, pt_down);
+	input_report_key(dev2, BTN_TOUCH, gs_down);
+	input_report_key(dev2, BTN_TOOL_FINGER, gs_down);
+
+	input_report_abs(dev2, ABS_PRESSURE, z);
+
+	if (pt_down) {
+		input_report_abs(dev, ABS_X, x);
+		input_report_abs(dev, ABS_Y, y);
+	}
+
+	input_sync(dev);
+	input_sync(dev2);
+}
+
+static void olpc_process_gs_packet(struct psmouse *psmouse)
 {
 	struct olpc_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev = psmouse->dev;
+	struct input_dev *dev2 = priv->dev2;
+	int x, y, z, gs_down, pt_down, left, right;
+
+	left = packet[3] & 1;
+	right = packet[3] & 2;
+	x = packet[1] | ((packet[2] & 0x78) << (7 - 3));
+	y = packet[4] | ((packet[3] & 0x70) << (7 - 4));
+	z = packet[5];
+
+	gs_down = packet[2] & 2;
+	pt_down = packet[2] & 1;
+
+	if (tpdebug) {
+		printk(KERN_DEBUG "%s %02x %02x %02x %02x %02x %02x\n",
+		       __func__, psmouse->packet[0], psmouse->packet[1],
+		       psmouse->packet[2], psmouse->packet[3], psmouse->packet[4],
+		       psmouse->packet[5]);
+		printk(KERN_DEBUG "l=%d r=%d p=%d g=%d x=%d y=%d z=%d\n",
+		       left, right, pt_down, gs_down, x, y, z);
+	}
+
+	input_report_key(dev, BTN_LEFT, left);
+	input_report_key(dev2, BTN_LEFT, left);
+	input_report_key(dev, BTN_RIGHT, right);
+	input_report_key(dev2, BTN_RIGHT, right);
+
+	input_report_key(dev, BTN_TOUCH, pt_down);
+	input_report_key(dev, BTN_TOOL_PEN, pt_down);
+	input_report_key(dev2, BTN_TOUCH, gs_down);
+	input_report_key(dev2, BTN_TOOL_FINGER, gs_down);
+
+	input_report_abs(dev2, ABS_PRESSURE, z);
+
+	if (gs_down) {
+		input_report_abs(dev2, ABS_X, x);
+		input_report_abs(dev2, ABS_Y, y);
+	}
+
+	input_sync(dev);
+	input_sync(dev2);
+}
+
+static psmouse_ret_t olpc_process_byte(struct psmouse *psmouse)
+{
 	psmouse_ret_t ret = PSMOUSE_BAD_DATA;
 
-	if ((psmouse->packet[0] & priv->i->mask0) != priv->i->byte0)
+	if (psmouse->packet[0] != OLPC_PKT_SIM &&
+	    psmouse->packet[0] != OLPC_PKT_PT &&
+	    psmouse->packet[0] != OLPC_PKT_GS)
 		goto out;
 
 	/* Bytes 2 - 9 should have 0 in the highest bit */
@@ -124,13 +247,33 @@ static psmouse_ret_t olpc_process_byte(s
 		goto out;
 
 	/*
-	 * Bytes 4 and 7 should have 1 in the 4th bit, and the high bit unset.
+	 * For simultaneous mode, bytes 4 and 7 should have 1 in the 4th bit,
+	 * and the high bit unset.
+	 */
+	if (psmouse->packet[0] == OLPC_PKT_SIM &&
+	    (psmouse->pktcnt == 4 || psmouse->pktcnt == 7) &&
+	    ((psmouse->packet[psmouse->pktcnt - 1] & 0x88) != 8))
+		goto out;
+
+	/*
+	 * For single mode, byte 4 should have bit 3 set and bit 2 clear
 	 */
-	if ((psmouse->pktcnt == 4 || psmouse->pktcnt == 7) &&
-			((psmouse->packet[psmouse->pktcnt - 1] & 0x88) != 8))
+	if (psmouse->packet[0] != OLPC_PKT_SIM &&
+	    psmouse->pktcnt == 4 &&
+	    ((psmouse->packet[psmouse->pktcnt - 1] & 0xc) != 0x8))
 		goto out;
 
-	if (psmouse->pktcnt == 9) {
+	if (psmouse->packet[0] == OLPC_PKT_PT &&
+	    psmouse->pktcnt == 6) {
+		olpc_process_pt_packet(psmouse);
+		ret = PSMOUSE_FULL_PACKET;
+		goto out;
+	} else if (psmouse->packet[0] == OLPC_PKT_GS &&
+		   psmouse->pktcnt == 6) {
+		olpc_process_gs_packet(psmouse);
+		ret = PSMOUSE_FULL_PACKET;
+		goto out;
+	} else if (psmouse->pktcnt == 9) {
 		olpc_process_packet(psmouse);
 
 		ret = PSMOUSE_FULL_PACKET;
@@ -194,16 +337,27 @@ static int olpc_absolute_mode(struct psm
 		return -1;
 
 	/*
-	 * Switch to simultanious mode, F2 (GETID) three times with no
+	 * Switch to simultaneous mode, F2 (GETID) three times with no
 	 * arguments or reply, followed by SETRES with an argument of 2.
 	 */
 	ps2_command(ps2dev, NULL, 0xF2);
 	ps2_command(ps2dev, NULL, 0xF2);
 	ps2_command(ps2dev, NULL, 0xF2);
-	param = 0x02;
-	ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
-
-	ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+	switch(tpmode) {
+	default:
+		printk(KERN_WARNING "Invalid tpmode %d. Default to simultaneous mode\n", tpmode);
+	case 0: /* Dual mode */
+		param = 0x02;
+		ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
+		ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+		break;
+	case 1: /* GS only */
+		ps2_command(ps2dev, NULL, 0xe6);
+		break;
+	case 2: /* PT only */
+		ps2_command(ps2dev, NULL, 0xe7);
+		break;
+	}
 	param = 100;
 	ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
 
diff --git a/drivers/input/mouse/olpc.h b/drivers/input/mouse/olpc.h
index f61bc90..4e7c76a 100644
--- a/drivers/input/mouse/olpc.h
+++ b/drivers/input/mouse/olpc.h
@@ -20,7 +20,6 @@ int olpc_init(struct psmouse *psmouse);
 
 struct olpc_model_info {
 	unsigned char signature[3];
-	unsigned char byte0, mask0;
 	unsigned char flags;
 };
 


More information about the Commits-kernel mailing list