Add Zephaniah's touchpad driver (part of psmouse).

Andres Salomon dilinger at debian.org
Tue Nov 7 22:37:11 EST 2006


Commit:     ed7c663acf60b1818060a5d998298d8b58698638
Parent:     61aac31ced5c30deaa8da7a72d9a65c5c971a097
commit ed7c663acf60b1818060a5d998298d8b58698638
Author:     Andres Salomon <dilinger at debian.org>
AuthorDate: Tue Oct 3 15:41:44 2006 -0400
Commit:     Andres Salomon <dilinger at debian.org>
CommitDate: Tue Oct 3 15:41:44 2006 -0400

    Add Zephaniah's touchpad driver (part of psmouse).
---
 drivers/input/mouse/Makefile       |    2 
 drivers/input/mouse/olpc.c         |  339 ++++++++++++++++++++++++++++++++++++
 drivers/input/mouse/olpc.h         |   35 ++++
 drivers/input/mouse/psmouse-base.c |   21 ++
 drivers/input/mouse/psmouse.h      |    3 
 5 files changed, 398 insertions(+), 2 deletions(-)

diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 21a1de6..6218e5a 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -14,4 +14,4 @@ obj-$(CONFIG_MOUSE_SERIAL)	+= sermouse.o
 obj-$(CONFIG_MOUSE_HIL)		+= hil_ptr.o
 obj-$(CONFIG_MOUSE_VSXXXAA)	+= vsxxxaa.o
 
-psmouse-objs  := psmouse-base.o alps.o logips2pp.o synaptics.o lifebook.o trackpoint.o
+psmouse-objs  := psmouse-base.o alps.o logips2pp.o synaptics.o lifebook.o trackpoint.o olpc.o
diff --git a/drivers/input/mouse/olpc.c b/drivers/input/mouse/olpc.c
new file mode 100644
index 0000000..ef4093f
--- /dev/null
+++ b/drivers/input/mouse/olpc.c
@@ -0,0 +1,339 @@
+/*
+ * OLPC touchpad PS/2 mouse driver
+ *
+ * Copyright (c) 2006 One Laptop Per Child, inc.
+ * Author Zephaniah E. Hull.
+ *
+ * This driver is partly based on the ALPS driver, which is:
+ *
+ * Copyright (c) 2003 Neil Brown <neilb at cse.unsw.edu.au>
+ * Copyright (c) 2003-2005 Peter Osterlund <petero2 at telia.com>
+ * Copyright (c) 2004 Dmitry Torokhov <dtor at mail.ru>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech at suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * 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 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
+ * there are several additional bytes, the button bits are in a
+ * different byte, and the bits used for finger and gesture indication
+ * are replaced by two bits which indicate if it is reporting PT or GS
+ * coordinate data in that packet.
+ */
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+
+#include "psmouse.h"
+#include "olpc.h"
+
+static const struct olpc_model_info olpc_model_data[] = {
+	{ { 0x67, 0x00, 0x0a }, 0xeb, 0xff, 0 }, /* OLPC in PT+GS mode. */
+};
+
+/*
+ * OLPC absolute Mode - simultanious format
+ *
+ * byte 0:  1    1    1    0    1    0     1     1
+ * byte 1:  0  gx6  gx5  gx4  gx3  gx2   gx1   gx0
+ * byte 2:  0  gx10 gx9  gx8  gx7  px9   px8   px7
+ * byte 3:  0  gy9  gy8  gy7    1    ? pt-dsw gs-dsw
+ * byte 4:  0  gy6  gy5  gy4  gy3  gy2   gy1   gy0
+ * byte 5:  0  gz6  gz5  gz4  gz3  gz2   gz1   gz0
+ * byte 6:  0  py9  py8  py7    1    ?   swr   swl
+ * byte 7:  0  py6  py5  py4  py3  py2   py1   py0
+ * byte 8:  0  px6  px5  px4  px3  px2   px1   px0
+ *
+ * ?'s are not defined in the protocol spec, may vary between models.
+ *
+ * gx/gy/gz is for the GlideSensor.
+ * px/py is for the PenTablet sensor.
+ *
+ * swr/swl are the left/right buttons.
+ *
+ * pt-dsw/gs-dsw indicate that the pt/gs sensor is detecting a
+ * pen/finger and is thus sending data this packet.
+ */
+
+static void olpc_process_packet(struct psmouse *psmouse, struct pt_regs *regs)
+{
+	struct olpc_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev = psmouse->dev;
+	struct input_dev *dev2 = priv->dev2;
+	int px, py, gx, gy, gz, gs_down, pt_down, left, right;
+
+	input_regs(dev, regs);
+
+	left = packet[6] & 1;
+	right = packet[6] & 2;
+	gx = packet[1] | ((packet[2] & 0x78) << (7 - 3));
+	gy = packet[4] | ((packet[3] & 0x70) << (7 - 4));
+	gz = packet[5];
+	px = packet[8] | ((packet[2] & 0x7) << 7);
+	py = packet[7] | ((packet[6] & 0x70) << (7 - 4));
+
+	pt_down = packet[3] & 1;
+	gs_down = packet[3] & 2;
+
+	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);
+
+	if (gs_down) {
+		input_report_abs(dev2, ABS_X, gx);
+		input_report_abs(dev2, ABS_Y, gy);
+	}
+	input_report_abs(dev2, ABS_PRESSURE, gz);
+
+	if (pt_down) {
+		input_report_abs(dev, ABS_X, px);
+		input_report_abs(dev, ABS_Y, py);
+	}
+
+	input_sync(dev);
+	input_sync(dev2);
+}
+
+static psmouse_ret_t olpc_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
+{
+	struct olpc_data *priv = psmouse->private;
+	psmouse_ret_t ret = PSMOUSE_BAD_DATA;
+
+	if ((psmouse->packet[0] & priv->i->mask0) != priv->i->byte0)
+		goto out;
+
+	/* Bytes 2 - 9 should have 0 in the highest bit */
+	if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 9 &&
+			(psmouse->packet[psmouse->pktcnt - 1] & 0x80))
+		goto out;
+
+	/*
+	 * Bytes 4 and 7 should have 1 in the 4th bit, and the high bit unset.
+	 */
+	if ((psmouse->pktcnt == 4 || psmouse->pktcnt == 7) &&
+			((psmouse->packet[psmouse->pktcnt - 1] & 0x88) != 8))
+		goto out;
+
+	if (psmouse->pktcnt == 9) {
+		olpc_process_packet(psmouse, regs);
+
+		ret = PSMOUSE_FULL_PACKET;
+		goto out;
+	}
+
+	ret = PSMOUSE_GOOD_DATA;
+out:
+	if (ret != PSMOUSE_GOOD_DATA)
+		dev_dbg(psmouse->dev->cdev.dev, "olpc.c(%d): ret: %d, len: %u,"
+			"data: %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x",
+			__LINE__, ret, psmouse->pktcnt,
+			psmouse->packet[0], psmouse->packet[1],
+			psmouse->packet[2], psmouse->packet[3],
+			psmouse->packet[4], psmouse->packet[5],
+			psmouse->packet[6], psmouse->packet[7],
+			psmouse->packet[8]);
+	return ret;
+}
+
+static const struct olpc_model_info *olpc_get_model(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+	int i;
+
+	/*
+	 * Now try "E7 report". Allowed responses are in
+	 * olpc_model_data[].signature
+	 */
+	if (ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21))
+		return NULL;
+
+	param[0] = param[1] = param[2] = 0xff;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return NULL;
+
+	pr_debug("olpc.c(%d): E7 report: %2.2x %2.2x %2.2x",
+		__LINE__, param[0], param[1], param[2]);
+
+	for (i = 0; i < ARRAY_SIZE(olpc_model_data); i++)
+		if (!memcmp(param, olpc_model_data[i].signature,
+				sizeof(olpc_model_data[i].signature)))
+			return olpc_model_data + i;
+
+	return NULL;
+}
+
+static int olpc_absolute_mode(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param;
+
+	/* Switch to 'Advanced mode.', four disables in a row. */
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))
+		return -1;
+
+	/*
+	 * Switch to simultanious 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);
+	param = 100;
+	ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
+
+	return 0;
+}
+
+/*
+ * olpc_poll() - poll the touchpad for current motion packet.
+ * Used in resync.
+ * Note: We can't poll, so always return failure.
+ */
+static int olpc_poll(struct psmouse *psmouse)
+{
+	return -1;
+}
+
+static int olpc_reconnect(struct psmouse *psmouse)
+{
+	struct olpc_data *priv = psmouse->private;
+
+	psmouse_reset(psmouse);
+
+	if (!(priv->i = olpc_get_model(psmouse)))
+		return -1;
+
+	if (olpc_absolute_mode(psmouse)) {
+		printk(KERN_ERR "olpc.c: Failed to reenable absolute mode.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void olpc_disconnect(struct psmouse *psmouse)
+{
+	struct olpc_data *priv = psmouse->private;
+
+	psmouse_reset(psmouse);
+	input_unregister_device(priv->dev2);
+	kfree(priv);
+}
+
+int olpc_init(struct psmouse *psmouse)
+{
+	struct olpc_data *priv;
+	struct input_dev *dev = psmouse->dev;
+	struct input_dev *dev2;
+
+	priv = kzalloc(sizeof(struct olpc_data), GFP_KERNEL);
+	dev2 = input_allocate_device();
+	if (!priv || !dev2)
+		goto init_fail;
+
+	psmouse->private = priv;
+	priv->dev2 = dev2;
+
+	if (!(priv->i = olpc_get_model(psmouse)))
+		goto init_fail;
+
+	if (olpc_absolute_mode(psmouse)) {
+		printk(KERN_ERR "olpc.c: Failed to enable absolute mode\n");
+		goto init_fail;
+	}
+
+	/*
+	 * Unset some of the default bits for things we don't have.
+	 */
+	dev->evbit[LONG(EV_REL)] &= ~BIT(EV_REL);
+	dev->relbit[LONG(REL_X)] &= ~(BIT(REL_X) | BIT(REL_Y));
+	dev->keybit[LONG(BTN_MIDDLE)] &= ~BIT(BTN_MIDDLE);
+
+	dev->evbit[LONG(EV_KEY)] |= BIT(EV_KEY);
+	dev->keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
+	dev->keybit[LONG(BTN_TOOL_PEN)] |= BIT(BTN_TOOL_PEN);
+	dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT);
+
+	dev->evbit[LONG(EV_ABS)] |= BIT(EV_ABS);
+	input_set_abs_params(dev, ABS_X, 0, 1023, 0, 0);
+	input_set_abs_params(dev, ABS_Y, 0, 1023, 0, 0);
+
+	snprintf(priv->phys, sizeof(priv->phys),
+		"%s/input1", psmouse->ps2dev.serio->phys);
+	dev2->phys = priv->phys;
+	dev2->name = "OLPC ALPS GlideSensor";
+	dev2->id.bustype = BUS_I8042;
+	dev2->id.vendor  = 0x0002;
+	dev2->id.product = PSMOUSE_OLPC;
+	dev2->id.version = 0x0000;
+
+	dev2->evbit[LONG(EV_KEY)] |= BIT(EV_KEY);
+	dev2->keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
+	dev2->keybit[LONG(BTN_TOOL_FINGER)] |= BIT(BTN_TOOL_FINGER);
+	dev2->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT);
+
+	dev2->evbit[LONG(EV_ABS)] |= BIT(EV_ABS);
+	input_set_abs_params(dev2, ABS_X, 0, 2047, 0, 0);
+	input_set_abs_params(dev2, ABS_Y, 0, 1023, 0, 0);
+	input_set_abs_params(dev2, ABS_PRESSURE, 0, 63, 0, 0);
+
+	if (input_register_device(dev2))
+		goto init_fail;
+
+	psmouse->protocol_handler = olpc_process_byte;
+	psmouse->poll = olpc_poll;
+	psmouse->disconnect = olpc_disconnect;
+	psmouse->reconnect = olpc_reconnect;
+	psmouse->pktsize = 9;
+
+	/* Disable the idle resync. */
+	psmouse->resync_time = 0;
+
+	return 0;
+
+init_fail:
+	input_free_device(dev2);
+	kfree(priv);
+	return -1;
+}
+
+int olpc_detect(struct psmouse *psmouse, int set_properties)
+{
+	if (!olpc_get_model(psmouse))
+		return -1;
+
+	if (set_properties) {
+		psmouse->vendor = "ALPS";
+		psmouse->name = "PenTablet";
+		psmouse->model = 0;
+	}
+	return 0;
+}
+
diff --git a/drivers/input/mouse/olpc.h b/drivers/input/mouse/olpc.h
new file mode 100644
index 0000000..f61bc90
--- /dev/null
+++ b/drivers/input/mouse/olpc.h
@@ -0,0 +1,35 @@
+/*
+ * OLPC touchpad PS/2 mouse driver
+ *
+ * Copyright (c) 2006 One Laptop Per Child, inc.
+ *
+ * This driver is partly based on the ALPS driver.
+ * Copyright (c) 2003 Peter Osterlund <petero2 at telia.com>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech at suse.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _OLPC_H
+#define _OLPC_H
+
+int olpc_detect(struct psmouse *psmouse, int set_properties);
+int olpc_init(struct psmouse *psmouse);
+
+struct olpc_model_info {
+	unsigned char signature[3];
+	unsigned char byte0, mask0;
+	unsigned char flags;
+};
+
+struct olpc_data {
+	struct input_dev *dev2;		/* Relative device */
+	char name[32];			/* Name */
+	char phys[32];			/* Phys */
+	const struct olpc_model_info *i;/* Info */
+};
+
+
+#endif
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 9fb7eb6..40cfd2e 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -26,6 +26,7 @@ #include "psmouse.h"
 #include "synaptics.h"
 #include "logips2pp.h"
 #include "alps.h"
+#include "olpc.h"
 #include "lifebook.h"
 #include "trackpoint.h"
 
@@ -613,6 +614,20 @@ static int psmouse_extensions(struct psm
 		}
 	}
 
+/*
+ * Try OLPC touchpad.
+ */
+	if (max_proto > PSMOUSE_IMEX) {
+		if (olpc_detect(psmouse, set_properties) == 0) {
+			if (!set_properties || olpc_init(psmouse) == 0)
+				return PSMOUSE_OLPC;
+/*
+ * Init failed, try basic relative protocols
+ */
+			max_proto = PSMOUSE_IMEX;
+		}
+	}
+
 	if (max_proto > PSMOUSE_IMEX && genius_detect(psmouse, set_properties) == 0)
 		return PSMOUSE_GENPS;
 
@@ -721,6 +736,12 @@ static const struct psmouse_protocol psm
 		.detect		= trackpoint_detect,
 	},
 	{
+		.type		= PSMOUSE_OLPC,
+		.name		= "OLPC",
+		.alias		= "olpc",
+		.detect		= olpc_detect,
+	},
+	{
 		.type		= PSMOUSE_AUTO,
 		.name		= "auto",
 		.alias		= "any",
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 4d9107f..f3d7199 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -42,7 +42,7 @@ struct psmouse {
 	struct work_struct resync_work;
 	char *vendor;
 	char *name;
-	unsigned char packet[8];
+	unsigned char packet[9];
 	unsigned char badbyte;
 	unsigned char pktcnt;
 	unsigned char pktsize;
@@ -86,6 +86,7 @@ enum psmouse_type {
 	PSMOUSE_ALPS,
 	PSMOUSE_LIFEBOOK,
 	PSMOUSE_TRACKPOINT,
+	PSMOUSE_OLPC,
 	PSMOUSE_AUTO		/* This one should always be last */
 };
 


More information about the Commits-kernel mailing list