Add Zephaniah's touchpad driver (part of psmouse).
Andres Salomon
dilinger at debian.org
Tue Nov 7 22:37:12 EST 2006
Commit: 8eca627da7aa0005e2325fa9875662d5153247b5
Parent: d223a60106891bfe46febfacf46b20cd8509aaad
commit 8eca627da7aa0005e2325fa9875662d5153247b5
Author: Andres Salomon <dilinger at debian.org>
AuthorDate: Thu Oct 5 16:56:13 2006 -0400
Commit: Andres Salomon <dilinger at debian.org>
CommitDate: Thu Oct 5 16:56:13 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, ¶m, PSMOUSE_CMD_SETRES);
+
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+ param = 100;
+ ps2_command(ps2dev, ¶m, 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