summaryrefslogtreecommitdiffstats
path: root/drivers/input/mouse
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/mouse')
-rw-r--r--drivers/input/mouse/Kconfig289
-rw-r--r--drivers/input/mouse/Makefile29
-rw-r--r--drivers/input/mouse/alps.c533
-rw-r--r--drivers/input/mouse/alps.h42
-rw-r--r--drivers/input/mouse/amimouse.c136
-rw-r--r--drivers/input/mouse/appletouch.c942
-rw-r--r--drivers/input/mouse/atarimouse.c158
-rw-r--r--drivers/input/mouse/bcm5974.c726
-rw-r--r--drivers/input/mouse/elantech.c674
-rw-r--r--drivers/input/mouse/elantech.h124
-rw-r--r--drivers/input/mouse/gpio_mouse.c199
-rw-r--r--drivers/input/mouse/hgpk.c477
-rw-r--r--drivers/input/mouse/hgpk.h49
-rw-r--r--drivers/input/mouse/hil_ptr.c447
-rw-r--r--drivers/input/mouse/inport.c196
-rw-r--r--drivers/input/mouse/lifebook.c332
-rw-r--r--drivers/input/mouse/lifebook.h28
-rw-r--r--drivers/input/mouse/logibm.c185
-rw-r--r--drivers/input/mouse/logips2pp.c417
-rw-r--r--drivers/input/mouse/logips2pp.h23
-rw-r--r--drivers/input/mouse/pc110pad.c183
-rw-r--r--drivers/input/mouse/psmouse-base.c1672
-rw-r--r--drivers/input/mouse/psmouse.h140
-rw-r--r--drivers/input/mouse/rpcmouse.c116
-rw-r--r--drivers/input/mouse/sermouse.c369
-rw-r--r--drivers/input/mouse/synaptics.c708
-rw-r--r--drivers/input/mouse/synaptics.h110
-rw-r--r--drivers/input/mouse/touchkit_ps2.c100
-rw-r--r--drivers/input/mouse/touchkit_ps2.h25
-rw-r--r--drivers/input/mouse/trackpoint.c331
-rw-r--r--drivers/input/mouse/trackpoint.h154
-rw-r--r--drivers/input/mouse/vsxxxaa.c587
32 files changed, 10501 insertions, 0 deletions
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
new file mode 100644
index 0000000..4e99342
--- /dev/null
+++ b/drivers/input/mouse/Kconfig
@@ -0,0 +1,289 @@
+#
+# Mouse driver configuration
+#
+menuconfig INPUT_MOUSE
+ bool "Mice"
+ default y
+ help
+ Say Y here, and a list of supported mice will be displayed.
+ This option doesn't affect the kernel.
+
+ If unsure, say Y.
+
+if INPUT_MOUSE
+
+config MOUSE_PS2
+ tristate "PS/2 mouse"
+ default y
+ select SERIO
+ select SERIO_LIBPS2
+ select SERIO_I8042 if X86_PC
+ select SERIO_GSCPS2 if GSC
+ help
+ Say Y here if you have a PS/2 mouse connected to your system. This
+ includes the standard 2 or 3-button PS/2 mouse, as well as PS/2
+ mice with wheels and extra buttons, Microsoft, Logitech or Genius
+ compatible.
+
+ Synaptics, ALPS or Elantech TouchPad users might be interested
+ in a specialized Xorg/XFree86 driver at:
+ <http://w1.894.telia.com/~u89404340/touchpad/index.html>
+ and a new version of GPM at:
+ <http://www.geocities.com/dt_or/gpm/gpm.html>
+ to take advantage of the advanced features of the touchpad.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called psmouse.
+
+config MOUSE_PS2_ALPS
+ bool "ALPS PS/2 mouse protocol extension" if EMBEDDED
+ default y
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have an ALPS PS/2 touchpad connected to
+ your system.
+
+ If unsure, say Y.
+
+config MOUSE_PS2_LOGIPS2PP
+ bool "Logitech PS/2++ mouse protocol extension" if EMBEDDED
+ default y
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have a Logictech PS/2++ mouse connected to
+ your system.
+
+ If unsure, say Y.
+
+config MOUSE_PS2_SYNAPTICS
+ bool "Synaptics PS/2 mouse protocol extension" if EMBEDDED
+ default y
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have a Synaptics PS/2 TouchPad connected to
+ your system.
+
+ If unsure, say Y.
+
+config MOUSE_PS2_LIFEBOOK
+ bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EMBEDDED
+ default y
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have a Fujitsu B-series Lifebook PS/2
+ TouchScreen connected to your system.
+
+ If unsure, say Y.
+
+config MOUSE_PS2_TRACKPOINT
+ bool "IBM Trackpoint PS/2 mouse protocol extension" if EMBEDDED
+ default y
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have an IBM Trackpoint PS/2 mouse connected
+ to your system.
+
+ If unsure, say Y.
+
+config MOUSE_PS2_ELANTECH
+ bool "Elantech PS/2 protocol extension"
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have an Elantech PS/2 touchpad connected
+ to your system.
+
+ Note that if you enable this driver you will need an updated
+ X.org Synaptics driver that does not require ABS_PRESSURE
+ reports from the touchpad (i.e. post 1.5.0 version). You can
+ grab a patch for the driver here:
+
+ http://userweb.kernel.org/~dtor/synaptics-no-abspressure.patch
+
+ If unsure, say N.
+
+ This driver exposes some configuration registers via sysfs
+ entries. For further information,
+ see <file:Documentation/input/elantech.txt>.
+
+
+config MOUSE_PS2_TOUCHKIT
+ bool "eGalax TouchKit PS/2 protocol extension"
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have an eGalax TouchKit PS/2 touchscreen
+ connected to your system.
+
+ If unsure, say N.
+
+config MOUSE_PS2_OLPC
+ bool "OLPC PS/2 mouse protocol extension"
+ depends on MOUSE_PS2 && OLPC
+ help
+ Say Y here if you have an OLPC XO-1 laptop (with built-in
+ PS/2 touchpad/tablet device). The manufacturer calls the
+ touchpad an HGPK.
+
+ If unsure, say N.
+
+config MOUSE_SERIAL
+ tristate "Serial mouse"
+ select SERIO
+ help
+ Say Y here if you have a serial (RS-232, COM port) mouse connected
+ to your system. This includes Sun, MouseSystems, Microsoft,
+ Logitech and all other compatible serial mice.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sermouse.
+
+config MOUSE_APPLETOUCH
+ tristate "Apple USB Touchpad support"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use an Apple USB Touchpad.
+
+ These are the touchpads that can be found on post-February 2005
+ Apple Powerbooks (prior models have a Synaptics touchpad connected
+ to the ADB bus).
+
+ This driver provides a basic mouse driver but can be interfaced
+ with the synaptics X11 driver to provide acceleration and
+ scrolling in X11.
+
+ For further information, see
+ <file:Documentation/input/appletouch.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called appletouch.
+
+config MOUSE_BCM5974
+ tristate "Apple USB BCM5974 Multitouch trackpad support"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you have an Apple USB BCM5974 Multitouch
+ trackpad.
+
+ The BCM5974 is the multitouch trackpad found in the Macbook
+ Air (JAN2008) and Macbook Pro Penryn (FEB2008) laptops.
+
+ It is also found in the IPhone (2007) and Ipod Touch (2008).
+
+ This driver provides multitouch functionality together with
+ the synaptics X11 driver.
+
+ The interface is currently identical to the appletouch interface,
+ for further information, see
+ <file:Documentation/input/appletouch.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bcm5974.
+
+config MOUSE_INPORT
+ tristate "InPort/MS/ATIXL busmouse"
+ depends on ISA
+ help
+ Say Y here if you have an InPort, Microsoft or ATI XL busmouse.
+ They are rather rare these days.
+
+ To compile this driver as a module, choose M here: the
+ module will be called inport.
+
+config MOUSE_ATIXL
+ bool "ATI XL variant"
+ depends on MOUSE_INPORT
+ help
+ Say Y here if your mouse is of the ATI XL variety.
+
+config MOUSE_LOGIBM
+ tristate "Logitech busmouse"
+ depends on ISA
+ help
+ Say Y here if you have a Logitech busmouse.
+ They are rather rare these days.
+
+ To compile this driver as a module, choose M here: the
+ module will be called logibm.
+
+config MOUSE_PC110PAD
+ tristate "IBM PC110 touchpad"
+ depends on ISA
+ help
+ Say Y if you have the IBM PC-110 micro-notebook and want its
+ touchpad supported.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pc110pad.
+
+config MOUSE_AMIGA
+ tristate "Amiga mouse"
+ depends on AMIGA
+ help
+ Say Y here if you have an Amiga and want its native mouse
+ supported by the kernel.
+
+ To compile this driver as a module, choose M here: the
+ module will be called amimouse.
+
+config MOUSE_ATARI
+ tristate "Atari mouse"
+ depends on ATARI
+ select ATARI_KBD_CORE
+ help
+ Say Y here if you have an Atari and want its native mouse
+ supported by the kernel.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atarimouse.
+
+config MOUSE_RISCPC
+ tristate "Acorn RiscPC mouse"
+ depends on ARCH_ACORN
+ help
+ Say Y here if you have the Acorn RiscPC computer and want its
+ native mouse supported.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rpcmouse.
+
+config MOUSE_VSXXXAA
+ tristate "DEC VSXXX-AA/GA mouse and VSXXX-AB tablet"
+ select SERIO
+ help
+ Say Y (or M) if you want to use a DEC VSXXX-AA (hockey
+ puck) or a VSXXX-GA (rectangular) mouse. Theses mice are
+ typically used on DECstations or VAXstations, but can also
+ be used on any box capable of RS232 (with some adaptor
+ described in the source file). This driver also works with the
+ digitizer (VSXXX-AB) DEC produced.
+
+config MOUSE_HIL
+ tristate "HIL pointers (mice etc)."
+ depends on GSC || HP300
+ select HP_SDC
+ select HIL_MLC
+ help
+ Say Y here to support HIL pointers.
+
+config MOUSE_GPIO
+ tristate "GPIO mouse"
+ depends on GENERIC_GPIO
+ select INPUT_POLLDEV
+ help
+ This driver simulates a mouse on GPIO lines of various CPUs (and some
+ other chips).
+
+ Say Y here if your device has buttons or a simple joystick connected
+ directly to GPIO lines. Your board-specific setup logic must also
+ provide a platform device and platform data saying which GPIOs are
+ used.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gpio_mouse.
+
+endif
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
new file mode 100644
index 0000000..96f1dd8
--- /dev/null
+++ b/drivers/input/mouse/Makefile
@@ -0,0 +1,29 @@
+#
+# Makefile for the mouse drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
+obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
+obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
+obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
+obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
+obj-$(CONFIG_MOUSE_INPORT) += inport.o
+obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
+obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
+obj-$(CONFIG_MOUSE_PS2) += psmouse.o
+obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
+obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o
+obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
+obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
+
+psmouse-objs := psmouse-base.o synaptics.o
+
+psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o
+psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o
+psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o
+psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
+psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
+psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
+psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
new file mode 100644
index 0000000..cbedf95
--- /dev/null
+++ b/drivers/input/mouse/alps.c
@@ -0,0 +1,533 @@
+/*
+ * ALPS touchpad PS/2 mouse driver
+ *
+ * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com>
+ * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ *
+ * ALPS detection, tap switching and status querying info is taken from
+ * tpconfig utility (by C. Scott Ananian and Bruce Kall).
+ *
+ * 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.
+ */
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+
+#include "psmouse.h"
+#include "alps.h"
+
+#undef DEBUG
+#ifdef DEBUG
+#define dbg(format, arg...) printk(KERN_INFO "alps.c: " format "\n", ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+#define ALPS_DUALPOINT 0x01
+#define ALPS_WHEEL 0x02
+#define ALPS_FW_BK_1 0x04
+#define ALPS_4BTN 0x08
+#define ALPS_OLDPROTO 0x10
+#define ALPS_PASS 0x20
+#define ALPS_FW_BK_2 0x40
+
+static const struct alps_model_info alps_model_data[] = {
+ { { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO }, /* UMAX-530T */
+ { { 0x53, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
+ { { 0x53, 0x02, 0x14 }, 0xf8, 0xf8, 0 },
+ { { 0x60, 0x03, 0xc8 }, 0xf8, 0xf8, 0 }, /* HP ze1115 */
+ { { 0x63, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
+ { { 0x63, 0x02, 0x14 }, 0xf8, 0xf8, 0 },
+ { { 0x63, 0x02, 0x28 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */
+ { { 0x63, 0x02, 0x3c }, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */
+ { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */
+ { { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 },
+ { { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS }, /* Dell Latitude D800 */
+ { { 0x73, 0x00, 0x0a }, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */
+ { { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
+ { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */
+ { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */
+ { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
+ { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
+ { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude E6500 */
+ { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 } /* Dell Vostro 1400 */
+};
+
+/*
+ * XXX - this entry is suspicious. First byte has zero lower nibble,
+ * which is what a normal mouse would report. Also, the value 0x0e
+ * isn't valid per PS/2 spec.
+ */
+
+/*
+ * ALPS abolute Mode - new format
+ *
+ * byte 0: 1 ? ? ? 1 ? ? ?
+ * byte 1: 0 x6 x5 x4 x3 x2 x1 x0
+ * byte 2: 0 x10 x9 x8 x7 ? fin ges
+ * byte 3: 0 y9 y8 y7 1 M R L
+ * byte 4: 0 y6 y5 y4 y3 y2 y1 y0
+ * byte 5: 0 z6 z5 z4 z3 z2 z1 z0
+ *
+ * ?'s can have different meanings on different models,
+ * such as wheel rotation, extra buttons, stick buttons
+ * on a dualpoint, etc.
+ */
+
+static void alps_process_packet(struct psmouse *psmouse)
+{
+ struct alps_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, ges, fin, left, right, middle;
+ int back = 0, forward = 0;
+
+ if ((packet[0] & 0xc8) == 0x08) { /* 3-byte PS/2 packet */
+ input_report_key(dev2, BTN_LEFT, packet[0] & 1);
+ input_report_key(dev2, BTN_RIGHT, packet[0] & 2);
+ input_report_key(dev2, BTN_MIDDLE, packet[0] & 4);
+ input_report_rel(dev2, REL_X,
+ packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
+ input_report_rel(dev2, REL_Y,
+ packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
+ input_sync(dev2);
+ return;
+ }
+
+ if (priv->i->flags & ALPS_OLDPROTO) {
+ left = packet[2] & 0x10;
+ right = packet[2] & 0x08;
+ middle = 0;
+ x = packet[1] | ((packet[0] & 0x07) << 7);
+ y = packet[4] | ((packet[3] & 0x07) << 7);
+ z = packet[5];
+ } else {
+ left = packet[3] & 1;
+ right = packet[3] & 2;
+ middle = packet[3] & 4;
+ x = packet[1] | ((packet[2] & 0x78) << (7 - 3));
+ y = packet[4] | ((packet[3] & 0x70) << (7 - 4));
+ z = packet[5];
+ }
+
+ if (priv->i->flags & ALPS_FW_BK_1) {
+ back = packet[0] & 0x10;
+ forward = packet[2] & 4;
+ }
+
+ if (priv->i->flags & ALPS_FW_BK_2) {
+ back = packet[3] & 4;
+ forward = packet[2] & 4;
+ if ((middle = forward && back))
+ forward = back = 0;
+ }
+
+ ges = packet[2] & 1;
+ fin = packet[2] & 2;
+
+ input_report_key(dev, BTN_LEFT, left);
+ input_report_key(dev, BTN_RIGHT, right);
+ input_report_key(dev, BTN_MIDDLE, middle);
+
+ if ((priv->i->flags & ALPS_DUALPOINT) && z == 127) {
+ input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x));
+ input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
+ input_sync(dev);
+ input_sync(dev2);
+ return;
+ }
+
+ /* Convert hardware tap to a reasonable Z value */
+ if (ges && !fin) z = 40;
+
+ /*
+ * A "tap and drag" operation is reported by the hardware as a transition
+ * from (!fin && ges) to (fin && ges). This should be translated to the
+ * sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually.
+ */
+ if (ges && fin && !priv->prev_fin) {
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ input_report_abs(dev, ABS_PRESSURE, 0);
+ input_report_key(dev, BTN_TOOL_FINGER, 0);
+ input_sync(dev);
+ }
+ priv->prev_fin = fin;
+
+ if (z > 30) input_report_key(dev, BTN_TOUCH, 1);
+ if (z < 25) input_report_key(dev, BTN_TOUCH, 0);
+
+ if (z > 0) {
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+ }
+
+ input_report_abs(dev, ABS_PRESSURE, z);
+ input_report_key(dev, BTN_TOOL_FINGER, z > 0);
+
+ if (priv->i->flags & ALPS_WHEEL)
+ input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07));
+
+ if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
+ input_report_key(dev, BTN_FORWARD, forward);
+ input_report_key(dev, BTN_BACK, back);
+ }
+
+ input_sync(dev);
+}
+
+static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
+{
+ struct alps_data *priv = psmouse->private;
+
+ if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
+ if (psmouse->pktcnt == 3) {
+ alps_process_packet(psmouse);
+ return PSMOUSE_FULL_PACKET;
+ }
+ return PSMOUSE_GOOD_DATA;
+ }
+
+ if ((psmouse->packet[0] & priv->i->mask0) != priv->i->byte0)
+ return PSMOUSE_BAD_DATA;
+
+ /* Bytes 2 - 6 should have 0 in the highest bit */
+ if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 &&
+ (psmouse->packet[psmouse->pktcnt - 1] & 0x80))
+ return PSMOUSE_BAD_DATA;
+
+ if (psmouse->pktcnt == 6) {
+ alps_process_packet(psmouse);
+ return PSMOUSE_FULL_PACKET;
+ }
+
+ return PSMOUSE_GOOD_DATA;
+}
+
+static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 };
+ unsigned char param[4];
+ int i;
+
+ /*
+ * First try "E6 report".
+ * ALPS should return 0,0,10 or 0,0,100
+ */
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
+ return NULL;
+
+ param[0] = param[1] = param[2] = 0xff;
+ if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+ return NULL;
+
+ dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+
+ if (param[0] != 0 || param[1] != 0 || (param[2] != 10 && param[2] != 100))
+ return NULL;
+
+ /*
+ * Now try "E7 report". Allowed responses are in
+ * alps_model_data[].signature
+ */
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
+ 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;
+
+ dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+
+ if (version) {
+ for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++)
+ /* empty */;
+ *version = (param[0] << 8) | (param[1] << 4) | i;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(alps_model_data); i++)
+ if (!memcmp(param, alps_model_data[i].signature,
+ sizeof(alps_model_data[i].signature)))
+ return alps_model_data + i;
+
+ return NULL;
+}
+
+/*
+ * For DualPoint devices select the device that should respond to
+ * subsequent commands. It looks like glidepad is behind stickpointer,
+ * I'd thought it would be other way around...
+ */
+static int alps_passthrough_mode(struct psmouse *psmouse, int enable)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11;
+
+ if (ps2_command(ps2dev, NULL, cmd) ||
+ ps2_command(ps2dev, NULL, cmd) ||
+ ps2_command(ps2dev, NULL, cmd) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))
+ return -1;
+
+ /* we may get 3 more bytes, just ignore them */
+ ps2_drain(ps2dev, 3, 100);
+
+ return 0;
+}
+
+static int alps_absolute_mode(struct psmouse *psmouse)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+ /* Try ALPS magic knock - 4 disable before enable */
+ 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) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
+ return -1;
+
+ /*
+ * Switch mouse to poll (remote) mode so motion data will not
+ * get in our way
+ */
+ return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL);
+}
+
+static int alps_get_status(struct psmouse *psmouse, char *param)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+ /* Get status: 0xF5 0xF5 0xF5 0xE9 */
+ 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, param, PSMOUSE_CMD_GETINFO))
+ return -1;
+
+ dbg("Status: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+
+ return 0;
+}
+
+/*
+ * Turn touchpad tapping on or off. The sequences are:
+ * 0xE9 0xF5 0xF5 0xF3 0x0A to enable,
+ * 0xE9 0xF5 0xF5 0xE8 0x00 to disable.
+ * My guess that 0xE9 (GetInfo) is here as a sync point.
+ * For models that also have stickpointer (DualPoints) its tapping
+ * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but
+ * we don't fiddle with it.
+ */
+static int alps_tap_mode(struct psmouse *psmouse, int enable)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES;
+ unsigned char tap_arg = enable ? 0x0A : 0x00;
+ unsigned char param[4];
+
+ if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+ ps2_command(ps2dev, &tap_arg, cmd))
+ return -1;
+
+ if (alps_get_status(psmouse, param))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * alps_poll() - poll the touchpad for current motion packet.
+ * Used in resync.
+ */
+static int alps_poll(struct psmouse *psmouse)
+{
+ struct alps_data *priv = psmouse->private;
+ unsigned char buf[6];
+ int poll_failed;
+
+ if (priv->i->flags & ALPS_PASS)
+ alps_passthrough_mode(psmouse, 1);
+
+ poll_failed = ps2_command(&psmouse->ps2dev, buf,
+ PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0;
+
+ if (priv->i->flags & ALPS_PASS)
+ alps_passthrough_mode(psmouse, 0);
+
+ if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0)
+ return -1;
+
+ if ((psmouse->badbyte & 0xc8) == 0x08) {
+/*
+ * Poll the track stick ...
+ */
+ if (ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (3 << 8)))
+ return -1;
+ }
+
+ memcpy(psmouse->packet, buf, sizeof(buf));
+ return 0;
+}
+
+static int alps_hw_init(struct psmouse *psmouse, int *version)
+{
+ struct alps_data *priv = psmouse->private;
+
+ priv->i = alps_get_model(psmouse, version);
+ if (!priv->i)
+ return -1;
+
+ if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 1))
+ return -1;
+
+ if (alps_tap_mode(psmouse, 1)) {
+ printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n");
+ return -1;
+ }
+
+ if (alps_absolute_mode(psmouse)) {
+ printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
+ return -1;
+ }
+
+ if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 0))
+ return -1;
+
+ /* ALPS needs stream mode, otherwise it won't report any data */
+ if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) {
+ printk(KERN_ERR "alps.c: Failed to enable stream mode\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int alps_reconnect(struct psmouse *psmouse)
+{
+ psmouse_reset(psmouse);
+
+ if (alps_hw_init(psmouse, NULL))
+ return -1;
+
+ return 0;
+}
+
+static void alps_disconnect(struct psmouse *psmouse)
+{
+ struct alps_data *priv = psmouse->private;
+
+ psmouse_reset(psmouse);
+ input_unregister_device(priv->dev2);
+ kfree(priv);
+}
+
+int alps_init(struct psmouse *psmouse)
+{
+ struct alps_data *priv;
+ struct input_dev *dev1 = psmouse->dev, *dev2;
+ int version;
+
+ priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL);
+ dev2 = input_allocate_device();
+ if (!priv || !dev2)
+ goto init_fail;
+
+ priv->dev2 = dev2;
+ psmouse->private = priv;
+
+ if (alps_hw_init(psmouse, &version))
+ goto init_fail;
+
+ dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY);
+ dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
+ dev1->keybit[BIT_WORD(BTN_TOOL_FINGER)] |= BIT_MASK(BTN_TOOL_FINGER);
+ dev1->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+
+ dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
+ input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0);
+ input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
+ input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
+
+ if (priv->i->flags & ALPS_WHEEL) {
+ dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
+ dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL);
+ }
+
+ if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
+ dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD);
+ dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK);
+ }
+
+ snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
+ dev2->phys = priv->phys;
+ dev2->name = (priv->i->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse";
+ dev2->id.bustype = BUS_I8042;
+ dev2->id.vendor = 0x0002;
+ dev2->id.product = PSMOUSE_ALPS;
+ dev2->id.version = 0x0000;
+ dev2->dev.parent = &psmouse->ps2dev.serio->dev;
+
+ dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ dev2->relbit[BIT_WORD(REL_X)] |= BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+ dev2->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+
+ if (input_register_device(priv->dev2))
+ goto init_fail;
+
+ psmouse->protocol_handler = alps_process_byte;
+ psmouse->poll = alps_poll;
+ psmouse->disconnect = alps_disconnect;
+ psmouse->reconnect = alps_reconnect;
+ psmouse->pktsize = 6;
+
+ /* We are having trouble resyncing ALPS touchpads so disable it for now */
+ psmouse->resync_time = 0;
+
+ return 0;
+
+init_fail:
+ psmouse_reset(psmouse);
+ input_free_device(dev2);
+ kfree(priv);
+ psmouse->private = NULL;
+ return -1;
+}
+
+int alps_detect(struct psmouse *psmouse, int set_properties)
+{
+ int version;
+ const struct alps_model_info *model;
+
+ model = alps_get_model(psmouse, &version);
+ if (!model)
+ return -1;
+
+ if (set_properties) {
+ psmouse->vendor = "ALPS";
+ psmouse->name = model->flags & ALPS_DUALPOINT ?
+ "DualPoint TouchPad" : "GlidePoint";
+ psmouse->model = version;
+ }
+ return 0;
+}
+
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
new file mode 100644
index 0000000..4bbddc9
--- /dev/null
+++ b/drivers/input/mouse/alps.h
@@ -0,0 +1,42 @@
+/*
+ * ALPS touchpad PS/2 mouse driver
+ *
+ * Copyright (c) 2003 Peter Osterlund <petero2@telia.com>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@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 _ALPS_H
+#define _ALPS_H
+
+struct alps_model_info {
+ unsigned char signature[3];
+ unsigned char byte0, mask0;
+ unsigned char flags;
+};
+
+struct alps_data {
+ struct input_dev *dev2; /* Relative device */
+ char phys[32]; /* Phys */
+ const struct alps_model_info *i;/* Info */
+ int prev_fin; /* Finger bit from previous packet */
+};
+
+#ifdef CONFIG_MOUSE_PS2_ALPS
+int alps_detect(struct psmouse *psmouse, int set_properties);
+int alps_init(struct psmouse *psmouse);
+#else
+inline int alps_detect(struct psmouse *psmouse, int set_properties)
+{
+ return -ENOSYS;
+}
+inline int alps_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_ALPS */
+
+#endif
diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c
new file mode 100644
index 0000000..a185ac7
--- /dev/null
+++ b/drivers/input/mouse/amimouse.c
@@ -0,0 +1,136 @@
+/*
+ * Amiga mouse driver for Linux/m68k
+ *
+ * Copyright (c) 2000-2002 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Michael Rausch James Banks
+ * Matther Dillon David Giller
+ * Nathan Laredo Linus Torvalds
+ * Johan Myreen Jes Sorensen
+ * Russell King
+ */
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Amiga mouse driver");
+MODULE_LICENSE("GPL");
+
+static int amimouse_lastx, amimouse_lasty;
+static struct input_dev *amimouse_dev;
+
+static irqreturn_t amimouse_interrupt(int irq, void *dummy)
+{
+ unsigned short joy0dat, potgor;
+ int nx, ny, dx, dy;
+
+ joy0dat = amiga_custom.joy0dat;
+
+ nx = joy0dat & 0xff;
+ ny = joy0dat >> 8;
+
+ dx = nx - amimouse_lastx;
+ dy = ny - amimouse_lasty;
+
+ if (dx < -127) dx = (256 + nx) - amimouse_lastx;
+ if (dx > 127) dx = (nx - 256) - amimouse_lastx;
+ if (dy < -127) dy = (256 + ny) - amimouse_lasty;
+ if (dy > 127) dy = (ny - 256) - amimouse_lasty;
+
+ amimouse_lastx = nx;
+ amimouse_lasty = ny;
+
+ potgor = amiga_custom.potgor;
+
+ input_report_rel(amimouse_dev, REL_X, dx);
+ input_report_rel(amimouse_dev, REL_Y, dy);
+
+ input_report_key(amimouse_dev, BTN_LEFT, ciaa.pra & 0x40);
+ input_report_key(amimouse_dev, BTN_MIDDLE, potgor & 0x0100);
+ input_report_key(amimouse_dev, BTN_RIGHT, potgor & 0x0400);
+
+ input_sync(amimouse_dev);
+
+ return IRQ_HANDLED;
+}
+
+static int amimouse_open(struct input_dev *dev)
+{
+ unsigned short joy0dat;
+
+ joy0dat = amiga_custom.joy0dat;
+
+ amimouse_lastx = joy0dat & 0xff;
+ amimouse_lasty = joy0dat >> 8;
+
+ if (request_irq(IRQ_AMIGA_VERTB, amimouse_interrupt, 0, "amimouse", amimouse_interrupt)) {
+ printk(KERN_ERR "amimouse.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void amimouse_close(struct input_dev *dev)
+{
+ free_irq(IRQ_AMIGA_VERTB, amimouse_interrupt);
+}
+
+static int __init amimouse_init(void)
+{
+ int err;
+
+ if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_MOUSE))
+ return -ENODEV;
+
+ amimouse_dev = input_allocate_device();
+ if (!amimouse_dev)
+ return -ENOMEM;
+
+ amimouse_dev->name = "Amiga mouse";
+ amimouse_dev->phys = "amimouse/input0";
+ amimouse_dev->id.bustype = BUS_AMIGA;
+ amimouse_dev->id.vendor = 0x0001;
+ amimouse_dev->id.product = 0x0002;
+ amimouse_dev->id.version = 0x0100;
+
+ amimouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ amimouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+ amimouse_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+ amimouse_dev->open = amimouse_open;
+ amimouse_dev->close = amimouse_close;
+
+ err = input_register_device(amimouse_dev);
+ if (err) {
+ input_free_device(amimouse_dev);
+ return err;
+ }
+
+ return 0;
+}
+
+static void __exit amimouse_exit(void)
+{
+ input_unregister_device(amimouse_dev);
+}
+
+module_init(amimouse_init);
+module_exit(amimouse_exit);
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
new file mode 100644
index 0000000..079816e
--- /dev/null
+++ b/drivers/input/mouse/appletouch.c
@@ -0,0 +1,942 @@
+/*
+ * Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver
+ *
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2005-2008 Johannes Berg (johannes@sipsolutions.net)
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
+ * Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
+ * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
+ * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch)
+ * Copyright (C) 2007-2008 Sven Anders (anders@anduras.de)
+ *
+ * Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+
+/* Type of touchpad */
+enum atp_touchpad_type {
+ ATP_FOUNTAIN,
+ ATP_GEYSER1,
+ ATP_GEYSER2,
+ ATP_GEYSER3,
+ ATP_GEYSER4
+};
+
+#define ATP_DEVICE(prod, type) \
+{ \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_CLASS | \
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL, \
+ .idVendor = 0x05ac, /* Apple */ \
+ .idProduct = (prod), \
+ .bInterfaceClass = 0x03, \
+ .bInterfaceProtocol = 0x02, \
+ .driver_info = ATP_ ## type, \
+}
+
+/*
+ * Table of devices (Product IDs) that work with this driver.
+ * (The names come from Info.plist in AppleUSBTrackpad.kext,
+ * According to Info.plist Geyser IV is the same as Geyser III.)
+ */
+
+static struct usb_device_id atp_table [] = {
+ /* PowerBooks Feb 2005, iBooks G4 */
+ ATP_DEVICE(0x020e, FOUNTAIN), /* FOUNTAIN ANSI */
+ ATP_DEVICE(0x020f, FOUNTAIN), /* FOUNTAIN ISO */
+ ATP_DEVICE(0x030a, FOUNTAIN), /* FOUNTAIN TP ONLY */
+ ATP_DEVICE(0x030b, GEYSER1), /* GEYSER 1 TP ONLY */
+
+ /* PowerBooks Oct 2005 */
+ ATP_DEVICE(0x0214, GEYSER2), /* GEYSER 2 ANSI */
+ ATP_DEVICE(0x0215, GEYSER2), /* GEYSER 2 ISO */
+ ATP_DEVICE(0x0216, GEYSER2), /* GEYSER 2 JIS */
+
+ /* Core Duo MacBook & MacBook Pro */
+ ATP_DEVICE(0x0217, GEYSER3), /* GEYSER 3 ANSI */
+ ATP_DEVICE(0x0218, GEYSER3), /* GEYSER 3 ISO */
+ ATP_DEVICE(0x0219, GEYSER3), /* GEYSER 3 JIS */
+
+ /* Core2 Duo MacBook & MacBook Pro */
+ ATP_DEVICE(0x021a, GEYSER4), /* GEYSER 4 ANSI */
+ ATP_DEVICE(0x021b, GEYSER4), /* GEYSER 4 ISO */
+ ATP_DEVICE(0x021c, GEYSER4), /* GEYSER 4 JIS */
+
+ /* Core2 Duo MacBook3,1 */
+ ATP_DEVICE(0x0229, GEYSER4), /* GEYSER 4 HF ANSI */
+ ATP_DEVICE(0x022a, GEYSER4), /* GEYSER 4 HF ISO */
+ ATP_DEVICE(0x022b, GEYSER4), /* GEYSER 4 HF JIS */
+
+ /* Terminating entry */
+ { }
+};
+MODULE_DEVICE_TABLE(usb, atp_table);
+
+/*
+ * number of sensors. Note that only 16 instead of 26 X (horizontal)
+ * sensors exist on 12" and 15" PowerBooks. All models have 16 Y
+ * (vertical) sensors.
+ */
+#define ATP_XSENSORS 26
+#define ATP_YSENSORS 16
+
+/* amount of fuzz this touchpad generates */
+#define ATP_FUZZ 16
+
+/* maximum pressure this driver will report */
+#define ATP_PRESSURE 300
+/*
+ * multiplication factor for the X and Y coordinates.
+ * We try to keep the touchpad aspect ratio while still doing only simple
+ * arithmetics.
+ * The factors below give coordinates like:
+ *
+ * 0 <= x < 960 on 12" and 15" Powerbooks
+ * 0 <= x < 1600 on 17" Powerbooks and 17" MacBook Pro
+ * 0 <= x < 1216 on MacBooks and 15" MacBook Pro
+ *
+ * 0 <= y < 646 on all Powerbooks
+ * 0 <= y < 774 on all MacBooks
+ */
+#define ATP_XFACT 64
+#define ATP_YFACT 43
+
+/*
+ * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
+ * ignored.
+ */
+#define ATP_THRESHOLD 5
+
+/* Geyser initialization constants */
+#define ATP_GEYSER_MODE_READ_REQUEST_ID 1
+#define ATP_GEYSER_MODE_WRITE_REQUEST_ID 9
+#define ATP_GEYSER_MODE_REQUEST_VALUE 0x300
+#define ATP_GEYSER_MODE_REQUEST_INDEX 0
+#define ATP_GEYSER_MODE_VENDOR_VALUE 0x04
+
+/**
+ * enum atp_status_bits - status bit meanings
+ *
+ * These constants represent the meaning of the status bits.
+ * (only Geyser 3/4)
+ *
+ * @ATP_STATUS_BUTTON: The button was pressed
+ * @ATP_STATUS_BASE_UPDATE: Update of the base values (untouched pad)
+ * @ATP_STATUS_FROM_RESET: Reset previously performed
+ */
+enum atp_status_bits {
+ ATP_STATUS_BUTTON = BIT(0),
+ ATP_STATUS_BASE_UPDATE = BIT(2),
+ ATP_STATUS_FROM_RESET = BIT(4),
+};
+
+/* Structure to hold all of our device specific stuff */
+struct atp {
+ char phys[64];
+ struct usb_device *udev; /* usb device */
+ struct urb *urb; /* usb request block */
+ u8 *data; /* transferred data */
+ struct input_dev *input; /* input dev */
+ enum atp_touchpad_type type; /* type of touchpad */
+ bool open;
+ bool valid; /* are the samples valid? */
+ bool size_detect_done;
+ bool overflow_warned;
+ int x_old; /* last reported x/y, */
+ int y_old; /* used for smoothing */
+ signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS];
+ signed char xy_old[ATP_XSENSORS + ATP_YSENSORS];
+ int xy_acc[ATP_XSENSORS + ATP_YSENSORS];
+ int datalen; /* size of USB transfer */
+ int idlecount; /* number of empty packets */
+ struct work_struct work;
+};
+
+#define dbg_dump(msg, tab) \
+ if (debug > 1) { \
+ int __i; \
+ printk(KERN_DEBUG "appletouch: %s", msg); \
+ for (__i = 0; __i < ATP_XSENSORS + ATP_YSENSORS; __i++) \
+ printk(" %02x", tab[__i]); \
+ printk("\n"); \
+ }
+
+#define dprintk(format, a...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG format, ##a); \
+ } while (0)
+
+MODULE_AUTHOR("Johannes Berg");
+MODULE_AUTHOR("Stelian Pop");
+MODULE_AUTHOR("Frank Arnold");
+MODULE_AUTHOR("Michael Hanselmann");
+MODULE_AUTHOR("Sven Anders");
+MODULE_DESCRIPTION("Apple PowerBook and MacBook USB touchpad driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Make the threshold a module parameter
+ */
+static int threshold = ATP_THRESHOLD;
+module_param(threshold, int, 0644);
+MODULE_PARM_DESC(threshold, "Discard any change in data from a sensor"
+ " (the trackpad has many of these sensors)"
+ " less than this value.");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activate debugging output");
+
+/*
+ * By default newer Geyser devices send standard USB HID mouse
+ * packets (Report ID 2). This code changes device mode, so it
+ * sends raw sensor reports (Report ID 5).
+ */
+static int atp_geyser_init(struct usb_device *udev)
+{
+ char data[8];
+ int size;
+ int i;
+
+ size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ ATP_GEYSER_MODE_READ_REQUEST_ID,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ ATP_GEYSER_MODE_REQUEST_VALUE,
+ ATP_GEYSER_MODE_REQUEST_INDEX, &data, 8, 5000);
+
+ if (size != 8) {
+ dprintk("atp_geyser_init: read error\n");
+ for (i = 0; i < 8; i++)
+ dprintk("appletouch[%d]: %d\n", i, data[i]);
+
+ err("Failed to read mode from device.");
+ return -EIO;
+ }
+
+ /* Apply the mode switch */
+ data[0] = ATP_GEYSER_MODE_VENDOR_VALUE;
+
+ size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ ATP_GEYSER_MODE_WRITE_REQUEST_ID,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ ATP_GEYSER_MODE_REQUEST_VALUE,
+ ATP_GEYSER_MODE_REQUEST_INDEX, &data, 8, 5000);
+
+ if (size != 8) {
+ dprintk("atp_geyser_init: write error\n");
+ for (i = 0; i < 8; i++)
+ dprintk("appletouch[%d]: %d\n", i, data[i]);
+
+ err("Failed to request geyser raw mode");
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * Reinitialise the device. This usually stops stream of empty packets
+ * coming from it.
+ */
+static void atp_reinit(struct work_struct *work)
+{
+ struct atp *dev = container_of(work, struct atp, work);
+ struct usb_device *udev = dev->udev;
+ int retval;
+
+ dprintk("appletouch: putting appletouch to sleep (reinit)\n");
+ atp_geyser_init(udev);
+
+ retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
+ if (retval)
+ err("atp_reinit: usb_submit_urb failed with error %d",
+ retval);
+}
+
+static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
+ int *z, int *fingers)
+{
+ int i;
+ /* values to calculate mean */
+ int pcum = 0, psum = 0;
+ int is_increasing = 0;
+
+ *fingers = 0;
+
+ for (i = 0; i < nb_sensors; i++) {
+ if (xy_sensors[i] < threshold) {
+ if (is_increasing)
+ is_increasing = 0;
+
+ continue;
+ }
+
+ /*
+ * Makes the finger detection more versatile. For example,
+ * two fingers with no gap will be detected. Also, my
+ * tests show it less likely to have intermittent loss
+ * of multiple finger readings while moving around (scrolling).
+ *
+ * Changes the multiple finger detection to counting humps on
+ * sensors (transitions from nonincreasing to increasing)
+ * instead of counting transitions from low sensors (no
+ * finger reading) to high sensors (finger above
+ * sensor)
+ *
+ * - Jason Parekh <jasonparekh@gmail.com>
+ */
+ if (i < 1 ||
+ (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
+ (*fingers)++;
+ is_increasing = 1;
+ } else if (i > 0 && xy_sensors[i - 1] >= xy_sensors[i]) {
+ is_increasing = 0;
+ }
+
+ /*
+ * Subtracts threshold so a high sensor that just passes the
+ * threshold won't skew the calculated absolute coordinate.
+ * Fixes an issue where slowly moving the mouse would
+ * occasionally jump a number of pixels (slowly moving the
+ * finger makes this issue most apparent.)
+ */
+ pcum += (xy_sensors[i] - threshold) * i;
+ psum += (xy_sensors[i] - threshold);
+ }
+
+ if (psum > 0) {
+ *z = psum;
+ return pcum * fact / psum;
+ }
+
+ return 0;
+}
+
+static inline void atp_report_fingers(struct input_dev *input, int fingers)
+{
+ input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
+ input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
+ input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
+}
+
+/* Check URB status and for correct length of data package */
+
+#define ATP_URB_STATUS_SUCCESS 0
+#define ATP_URB_STATUS_ERROR 1
+#define ATP_URB_STATUS_ERROR_FATAL 2
+
+static int atp_status_check(struct urb *urb)
+{
+ struct atp *dev = urb->context;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -EOVERFLOW:
+ if (!dev->overflow_warned) {
+ printk(KERN_WARNING "appletouch: OVERFLOW with data "
+ "length %d, actual length is %d\n",
+ dev->datalen, dev->urb->actual_length);
+ dev->overflow_warned = true;
+ }
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* This urb is terminated, clean up */
+ dbg("atp_complete: urb shutting down with status: %d",
+ urb->status);
+ return ATP_URB_STATUS_ERROR_FATAL;
+
+ default:
+ dbg("atp_complete: nonzero urb status received: %d",
+ urb->status);
+ return ATP_URB_STATUS_ERROR;
+ }
+
+ /* drop incomplete datasets */
+ if (dev->urb->actual_length != dev->datalen) {
+ dprintk("appletouch: incomplete data package"
+ " (first byte: %d, length: %d).\n",
+ dev->data[0], dev->urb->actual_length);
+ return ATP_URB_STATUS_ERROR;
+ }
+
+ return ATP_URB_STATUS_SUCCESS;
+}
+
+/*
+ * USB interrupt callback functions
+ */
+
+/* Interrupt function for older touchpads: FOUNTAIN/GEYSER1/GEYSER2 */
+
+static void atp_complete_geyser_1_2(struct urb *urb)
+{
+ int x, y, x_z, y_z, x_f, y_f;
+ int retval, i, j;
+ int key;
+ struct atp *dev = urb->context;
+ int status = atp_status_check(urb);
+
+ if (status == ATP_URB_STATUS_ERROR_FATAL)
+ return;
+ else if (status == ATP_URB_STATUS_ERROR)
+ goto exit;
+
+ /* reorder the sensors values */
+ if (dev->type == ATP_GEYSER2) {
+ memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
+
+ /*
+ * The values are laid out like this:
+ * Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ...
+ * '-' is an unused value.
+ */
+
+ /* read X values */
+ for (i = 0, j = 19; i < 20; i += 2, j += 3) {
+ dev->xy_cur[i] = dev->data[j];
+ dev->xy_cur[i + 1] = dev->data[j + 1];
+ }
+
+ /* read Y values */
+ for (i = 0, j = 1; i < 9; i += 2, j += 3) {
+ dev->xy_cur[ATP_XSENSORS + i] = dev->data[j];
+ dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1];
+ }
+ } else {
+ for (i = 0; i < 8; i++) {
+ /* X values */
+ dev->xy_cur[i + 0] = dev->data[5 * i + 2];
+ dev->xy_cur[i + 8] = dev->data[5 * i + 4];
+ dev->xy_cur[i + 16] = dev->data[5 * i + 42];
+ if (i < 2)
+ dev->xy_cur[i + 24] = dev->data[5 * i + 44];
+
+ /* Y values */
+ dev->xy_cur[i + 26] = dev->data[5 * i + 1];
+ dev->xy_cur[i + 34] = dev->data[5 * i + 3];
+ }
+ }
+
+ dbg_dump("sample", dev->xy_cur);
+
+ if (!dev->valid) {
+ /* first sample */
+ dev->valid = true;
+ dev->x_old = dev->y_old = -1;
+
+ /* Store first sample */
+ memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+
+ /* Perform size detection, if not done already */
+ if (!dev->size_detect_done) {
+
+ /* 17" Powerbooks have extra X sensors */
+ for (i = (dev->type == ATP_GEYSER2 ? 15 : 16);
+ i < ATP_XSENSORS; i++) {
+ if (!dev->xy_cur[i])
+ continue;
+
+ printk(KERN_INFO
+ "appletouch: 17\" model detected.\n");
+
+ if (dev->type == ATP_GEYSER2)
+ input_set_abs_params(dev->input, ABS_X,
+ 0,
+ (20 - 1) *
+ ATP_XFACT - 1,
+ ATP_FUZZ, 0);
+ else
+ input_set_abs_params(dev->input, ABS_X,
+ 0,
+ (26 - 1) *
+ ATP_XFACT - 1,
+ ATP_FUZZ, 0);
+ break;
+ }
+
+ dev->size_detect_done = 1;
+ goto exit;
+ }
+ }
+
+ for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
+ /* accumulate the change */
+ signed char change = dev->xy_old[i] - dev->xy_cur[i];
+ dev->xy_acc[i] -= change;
+
+ /* prevent down drifting */
+ if (dev->xy_acc[i] < 0)
+ dev->xy_acc[i] = 0;
+ }
+
+ memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+
+ dbg_dump("accumulator", dev->xy_acc);
+
+ x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
+ ATP_XFACT, &x_z, &x_f);
+ y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
+ ATP_YFACT, &y_z, &y_f);
+ key = dev->data[dev->datalen - 1] & ATP_STATUS_BUTTON;
+
+ if (x && y) {
+ if (dev->x_old != -1) {
+ x = (dev->x_old * 3 + x) >> 2;
+ y = (dev->y_old * 3 + y) >> 2;
+ dev->x_old = x;
+ dev->y_old = y;
+
+ if (debug > 1)
+ printk(KERN_DEBUG "appletouch: "
+ "X: %3d Y: %3d Xz: %3d Yz: %3d\n",
+ x, y, x_z, y_z);
+
+ input_report_key(dev->input, BTN_TOUCH, 1);
+ input_report_abs(dev->input, ABS_X, x);
+ input_report_abs(dev->input, ABS_Y, y);
+ input_report_abs(dev->input, ABS_PRESSURE,
+ min(ATP_PRESSURE, x_z + y_z));
+ atp_report_fingers(dev->input, max(x_f, y_f));
+ }
+ dev->x_old = x;
+ dev->y_old = y;
+
+ } else if (!x && !y) {
+
+ dev->x_old = dev->y_old = -1;
+ input_report_key(dev->input, BTN_TOUCH, 0);
+ input_report_abs(dev->input, ABS_PRESSURE, 0);
+ atp_report_fingers(dev->input, 0);
+
+ /* reset the accumulator on release */
+ memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
+ }
+
+ input_report_key(dev->input, BTN_LEFT, key);
+ input_sync(dev->input);
+
+ exit:
+ retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
+ if (retval)
+ err("atp_complete: usb_submit_urb failed with result %d",
+ retval);
+}
+
+/* Interrupt function for older touchpads: GEYSER3/GEYSER4 */
+
+static void atp_complete_geyser_3_4(struct urb *urb)
+{
+ int x, y, x_z, y_z, x_f, y_f;
+ int retval, i, j;
+ int key;
+ struct atp *dev = urb->context;
+ int status = atp_status_check(urb);
+
+ if (status == ATP_URB_STATUS_ERROR_FATAL)
+ return;
+ else if (status == ATP_URB_STATUS_ERROR)
+ goto exit;
+
+ /* Reorder the sensors values:
+ *
+ * The values are laid out like this:
+ * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ...
+ * '-' is an unused value.
+ */
+
+ /* read X values */
+ for (i = 0, j = 19; i < 20; i += 2, j += 3) {
+ dev->xy_cur[i] = dev->data[j + 1];
+ dev->xy_cur[i + 1] = dev->data[j + 2];
+ }
+ /* read Y values */
+ for (i = 0, j = 1; i < 9; i += 2, j += 3) {
+ dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
+ dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
+ }
+
+ dbg_dump("sample", dev->xy_cur);
+
+ /* Just update the base values (i.e. touchpad in untouched state) */
+ if (dev->data[dev->datalen - 1] & ATP_STATUS_BASE_UPDATE) {
+
+ dprintk(KERN_DEBUG "appletouch: updated base values\n");
+
+ memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+ goto exit;
+ }
+
+ for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
+ /* calculate the change */
+ dev->xy_acc[i] = dev->xy_cur[i] - dev->xy_old[i];
+
+ /* this is a round-robin value, so couple with that */
+ if (dev->xy_acc[i] > 127)
+ dev->xy_acc[i] -= 256;
+
+ if (dev->xy_acc[i] < -127)
+ dev->xy_acc[i] += 256;
+
+ /* prevent down drifting */
+ if (dev->xy_acc[i] < 0)
+ dev->xy_acc[i] = 0;
+ }
+
+ dbg_dump("accumulator", dev->xy_acc);
+
+ x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
+ ATP_XFACT, &x_z, &x_f);
+ y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
+ ATP_YFACT, &y_z, &y_f);
+ key = dev->data[dev->datalen - 1] & ATP_STATUS_BUTTON;
+
+ if (x && y) {
+ if (dev->x_old != -1) {
+ x = (dev->x_old * 3 + x) >> 2;
+ y = (dev->y_old * 3 + y) >> 2;
+ dev->x_old = x;
+ dev->y_old = y;
+
+ if (debug > 1)
+ printk(KERN_DEBUG "appletouch: X: %3d Y: %3d "
+ "Xz: %3d Yz: %3d\n",
+ x, y, x_z, y_z);
+
+ input_report_key(dev->input, BTN_TOUCH, 1);
+ input_report_abs(dev->input, ABS_X, x);
+ input_report_abs(dev->input, ABS_Y, y);
+ input_report_abs(dev->input, ABS_PRESSURE,
+ min(ATP_PRESSURE, x_z + y_z));
+ atp_report_fingers(dev->input, max(x_f, y_f));
+ }
+ dev->x_old = x;
+ dev->y_old = y;
+
+ } else if (!x && !y) {
+
+ dev->x_old = dev->y_old = -1;
+ input_report_key(dev->input, BTN_TOUCH, 0);
+ input_report_abs(dev->input, ABS_PRESSURE, 0);
+ atp_report_fingers(dev->input, 0);
+
+ /* reset the accumulator on release */
+ memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
+ }
+
+ input_report_key(dev->input, BTN_LEFT, key);
+ input_sync(dev->input);
+
+ /*
+ * Geysers 3/4 will continue to send packets continually after
+ * the first touch unless reinitialised. Do so if it's been
+ * idle for a while in order to avoid waking the kernel up
+ * several hundred times a second.
+ */
+
+ /*
+ * Button must not be pressed when entering suspend,
+ * otherwise we will never release the button.
+ */
+ if (!x && !y && !key) {
+ dev->idlecount++;
+ if (dev->idlecount == 10) {
+ dev->x_old = dev->y_old = -1;
+ dev->idlecount = 0;
+ schedule_work(&dev->work);
+ /* Don't resubmit urb here, wait for reinit */
+ return;
+ }
+ } else
+ dev->idlecount = 0;
+
+ exit:
+ retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
+ if (retval)
+ err("atp_complete: usb_submit_urb failed with result %d",
+ retval);
+}
+
+static int atp_open(struct input_dev *input)
+{
+ struct atp *dev = input_get_drvdata(input);
+
+ if (usb_submit_urb(dev->urb, GFP_ATOMIC))
+ return -EIO;
+
+ dev->open = 1;
+ return 0;
+}
+
+static void atp_close(struct input_dev *input)
+{
+ struct atp *dev = input_get_drvdata(input);
+
+ usb_kill_urb(dev->urb);
+ cancel_work_sync(&dev->work);
+ dev->open = 0;
+}
+
+static int atp_handle_geyser(struct atp *dev)
+{
+ struct usb_device *udev = dev->udev;
+
+ if (dev->type != ATP_FOUNTAIN) {
+ /* switch to raw sensor mode */
+ if (atp_geyser_init(udev))
+ return -EIO;
+
+ printk(KERN_INFO "appletouch: Geyser mode initialized.\n");
+ }
+
+ return 0;
+}
+
+static int atp_probe(struct usb_interface *iface,
+ const struct usb_device_id *id)
+{
+ struct atp *dev;
+ struct input_dev *input_dev;
+ struct usb_device *udev = interface_to_usbdev(iface);
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int int_in_endpointAddr = 0;
+ int i, error = -ENOMEM;
+
+ /* set up the endpoint information */
+ /* use only the first interrupt-in endpoint */
+ iface_desc = iface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
+ /* we found an interrupt in endpoint */
+ int_in_endpointAddr = endpoint->bEndpointAddress;
+ break;
+ }
+ }
+ if (!int_in_endpointAddr) {
+ err("Could not find int-in endpoint");
+ return -EIO;
+ }
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!dev || !input_dev) {
+ err("Out of memory");
+ goto err_free_devs;
+ }
+
+ dev->udev = udev;
+ dev->input = input_dev;
+ dev->type = id->driver_info;
+ dev->overflow_warned = false;
+ if (dev->type == ATP_FOUNTAIN || dev->type == ATP_GEYSER1)
+ dev->datalen = 81;
+ else
+ dev->datalen = 64;
+
+ dev->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->urb)
+ goto err_free_devs;
+
+ dev->data = usb_buffer_alloc(dev->udev, dev->datalen, GFP_KERNEL,
+ &dev->urb->transfer_dma);
+ if (!dev->data)
+ goto err_free_urb;
+
+ /* Select the USB complete (callback) function */
+ if (dev->type == ATP_FOUNTAIN ||
+ dev->type == ATP_GEYSER1 ||
+ dev->type == ATP_GEYSER2)
+ usb_fill_int_urb(dev->urb, udev,
+ usb_rcvintpipe(udev, int_in_endpointAddr),
+ dev->data, dev->datalen,
+ atp_complete_geyser_1_2, dev, 1);
+ else
+ usb_fill_int_urb(dev->urb, udev,
+ usb_rcvintpipe(udev, int_in_endpointAddr),
+ dev->data, dev->datalen,
+ atp_complete_geyser_3_4, dev, 1);
+
+ error = atp_handle_geyser(dev);
+ if (error)
+ goto err_free_buffer;
+
+ usb_make_path(udev, dev->phys, sizeof(dev->phys));
+ strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+ input_dev->name = "appletouch";
+ input_dev->phys = dev->phys;
+ usb_to_input_id(dev->udev, &input_dev->id);
+ input_dev->dev.parent = &iface->dev;
+
+ input_set_drvdata(input_dev, dev);
+
+ input_dev->open = atp_open;
+ input_dev->close = atp_close;
+
+ set_bit(EV_ABS, input_dev->evbit);
+
+ if (dev->type == ATP_GEYSER3 || dev->type == ATP_GEYSER4) {
+ /*
+ * MacBook have 20 X sensors, 10 Y sensors
+ */
+ input_set_abs_params(input_dev, ABS_X, 0,
+ ((20 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0,
+ ((10 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
+ } else if (dev->type == ATP_GEYSER2) {
+ /*
+ * Oct 2005 15" PowerBooks have 15 X sensors, 17" are detected
+ * later.
+ */
+ input_set_abs_params(input_dev, ABS_X, 0,
+ ((15 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0,
+ ((9 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
+ } else {
+ /*
+ * 12" and 15" Powerbooks only have 16 x sensors,
+ * 17" models are detected later.
+ */
+ input_set_abs_params(input_dev, ABS_X, 0,
+ (16 - 1) * ATP_XFACT - 1,
+ ATP_FUZZ, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0,
+ (ATP_YSENSORS - 1) * ATP_YFACT - 1,
+ ATP_FUZZ, 0);
+ }
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
+
+ set_bit(EV_KEY, input_dev->evbit);
+ set_bit(BTN_TOUCH, input_dev->keybit);
+ set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+ set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+ set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
+ set_bit(BTN_LEFT, input_dev->keybit);
+
+ error = input_register_device(dev->input);
+ if (error)
+ goto err_free_buffer;
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(iface, dev);
+
+ INIT_WORK(&dev->work, atp_reinit);
+
+ return 0;
+
+ err_free_buffer:
+ usb_buffer_free(dev->udev, dev->datalen,
+ dev->data, dev->urb->transfer_dma);
+ err_free_urb:
+ usb_free_urb(dev->urb);
+ err_free_devs:
+ usb_set_intfdata(iface, NULL);
+ kfree(dev);
+ input_free_device(input_dev);
+ return error;
+}
+
+static void atp_disconnect(struct usb_interface *iface)
+{
+ struct atp *dev = usb_get_intfdata(iface);
+
+ usb_set_intfdata(iface, NULL);
+ if (dev) {
+ usb_kill_urb(dev->urb);
+ input_unregister_device(dev->input);
+ usb_buffer_free(dev->udev, dev->datalen,
+ dev->data, dev->urb->transfer_dma);
+ usb_free_urb(dev->urb);
+ kfree(dev);
+ }
+ printk(KERN_INFO "input: appletouch disconnected\n");
+}
+
+static int atp_recover(struct atp *dev)
+{
+ int error;
+
+ error = atp_handle_geyser(dev);
+ if (error)
+ return error;
+
+ if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
+ return -EIO;
+
+ return 0;
+}
+
+static int atp_suspend(struct usb_interface *iface, pm_message_t message)
+{
+ struct atp *dev = usb_get_intfdata(iface);
+
+ usb_kill_urb(dev->urb);
+ return 0;
+}
+
+static int atp_resume(struct usb_interface *iface)
+{
+ struct atp *dev = usb_get_intfdata(iface);
+
+ if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
+ return -EIO;
+
+ return 0;
+}
+
+static int atp_reset_resume(struct usb_interface *iface)
+{
+ struct atp *dev = usb_get_intfdata(iface);
+
+ return atp_recover(dev);
+}
+
+static struct usb_driver atp_driver = {
+ .name = "appletouch",
+ .probe = atp_probe,
+ .disconnect = atp_disconnect,
+ .suspend = atp_suspend,
+ .resume = atp_resume,
+ .reset_resume = atp_reset_resume,
+ .id_table = atp_table,
+};
+
+static int __init atp_init(void)
+{
+ return usb_register(&atp_driver);
+}
+
+static void __exit atp_exit(void)
+{
+ usb_deregister(&atp_driver);
+}
+
+module_init(atp_init);
+module_exit(atp_exit);
diff --git a/drivers/input/mouse/atarimouse.c b/drivers/input/mouse/atarimouse.c
new file mode 100644
index 0000000..adf45b3
--- /dev/null
+++ b/drivers/input/mouse/atarimouse.c
@@ -0,0 +1,158 @@
+/*
+ * Atari mouse driver for Linux/m68k
+ *
+ * Copyright (c) 2005 Michael Schmitz
+ *
+ * Based on:
+ * Amiga mouse driver for Linux/m68k
+ *
+ * Copyright (c) 2000-2002 Vojtech Pavlik
+ *
+ */
+/*
+ * The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c
+ * (the keyboard ACIA also handles the mouse and joystick data, and the keyboard
+ * interrupt is shared with the MIDI ACIA so MIDI data also get handled there).
+ * This driver only deals with handing key events off to the input layer.
+ *
+ * Largely based on the old:
+ *
+ * Atari Mouse Driver for Linux
+ * by Robert de Vries (robert@and.nl) 19Jul93
+ *
+ * 16 Nov 1994 Andreas Schwab
+ * Compatibility with busmouse
+ * Support for three button mouse (shamelessly stolen from MiNT)
+ * third button wired to one of the joystick directions on joystick 1
+ *
+ * 1996/02/11 Andreas Schwab
+ * Module support
+ * Allow multiple open's
+ *
+ * Converted to use new generic busmouse code. 5 Apr 1998
+ * Russell King <rmk@arm.uk.linux.org>
+ */
+
+
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/atarihw.h>
+#include <asm/atarikb.h>
+#include <asm/atariints.h>
+
+MODULE_AUTHOR("Michael Schmitz <schmitz@biophys.uni-duesseldorf.de>");
+MODULE_DESCRIPTION("Atari mouse driver");
+MODULE_LICENSE("GPL");
+
+static int mouse_threshold[2] = {2, 2};
+module_param_array(mouse_threshold, int, NULL, 0);
+
+#ifdef FIXED_ATARI_JOYSTICK
+extern int atari_mouse_buttons;
+#endif
+
+static struct input_dev *atamouse_dev;
+
+static void atamouse_interrupt(char *buf)
+{
+ int buttons, dx, dy;
+
+ buttons = (buf[0] & 1) | ((buf[0] & 2) << 1);
+#ifdef FIXED_ATARI_JOYSTICK
+ buttons |= atari_mouse_buttons & 2;
+ atari_mouse_buttons = buttons;
+#endif
+
+ /* only relative events get here */
+ dx = buf[1];
+ dy = -buf[2];
+
+ input_report_rel(atamouse_dev, REL_X, dx);
+ input_report_rel(atamouse_dev, REL_Y, dy);
+
+ input_report_key(atamouse_dev, BTN_LEFT, buttons & 0x1);
+ input_report_key(atamouse_dev, BTN_MIDDLE, buttons & 0x2);
+ input_report_key(atamouse_dev, BTN_RIGHT, buttons & 0x4);
+
+ input_sync(atamouse_dev);
+
+ return;
+}
+
+static int atamouse_open(struct input_dev *dev)
+{
+#ifdef FIXED_ATARI_JOYSTICK
+ atari_mouse_buttons = 0;
+#endif
+ ikbd_mouse_y0_top();
+ ikbd_mouse_thresh(mouse_threshold[0], mouse_threshold[1]);
+ ikbd_mouse_rel_pos();
+ atari_input_mouse_interrupt_hook = atamouse_interrupt;
+
+ return 0;
+}
+
+static void atamouse_close(struct input_dev *dev)
+{
+ ikbd_mouse_disable();
+ atari_mouse_interrupt_hook = NULL;
+}
+
+static int __init atamouse_init(void)
+{
+ int error;
+
+ if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP))
+ return -ENODEV;
+
+ if (!atari_keyb_init())
+ return -ENODEV;
+
+ atamouse_dev = input_allocate_device();
+ if (!atamouse_dev)
+ return -ENOMEM;
+
+ atamouse_dev->name = "Atari mouse";
+ atamouse_dev->phys = "atamouse/input0";
+ atamouse_dev->id.bustype = BUS_HOST;
+ atamouse_dev->id.vendor = 0x0001;
+ atamouse_dev->id.product = 0x0002;
+ atamouse_dev->id.version = 0x0100;
+
+ atamouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ atamouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+ atamouse_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+
+ atamouse_dev->open = atamouse_open;
+ atamouse_dev->close = atamouse_close;
+
+ error = input_register_device(atamouse_dev);
+ if (error) {
+ input_free_device(atamouse_dev);
+ return error;
+ }
+
+ return 0;
+}
+
+static void __exit atamouse_exit(void)
+{
+ input_unregister_device(atamouse_dev);
+}
+
+module_init(atamouse_init);
+module_exit(atamouse_exit);
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
new file mode 100644
index 0000000..2998a6a
--- /dev/null
+++ b/drivers/input/mouse/bcm5974.c
@@ -0,0 +1,726 @@
+/*
+ * Apple USB BCM5974 (Macbook Air and Penryn Macbook Pro) multitouch driver
+ *
+ * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
+ *
+ * The USB initialization and package decoding was made by
+ * Scott Shawcroft as part of the touchd user-space driver project:
+ * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com)
+ *
+ * The BCM5974 driver is based on the appletouch driver:
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
+ * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
+ * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
+ * Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
+ * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
+ * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+#include <linux/hid.h>
+#include <linux/mutex.h>
+
+#define USB_VENDOR_ID_APPLE 0x05ac
+
+/* MacbookAir, aka wellspring */
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224
+#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225
+/* MacbookProPenryn, aka wellspring2 */
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232
+
+#define BCM5974_DEVICE(prod) { \
+ .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_CLASS | \
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL), \
+ .idVendor = USB_VENDOR_ID_APPLE, \
+ .idProduct = (prod), \
+ .bInterfaceClass = USB_INTERFACE_CLASS_HID, \
+ .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE \
+}
+
+/* table of devices that work with this driver */
+static const struct usb_device_id bcm5974_table[] = {
+ /* MacbookAir1.1 */
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ISO),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_JIS),
+ /* MacbookProPenryn */
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ISO),
+ BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_JIS),
+ /* Terminating entry */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, bcm5974_table);
+
+MODULE_AUTHOR("Henrik Rydberg");
+MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver");
+MODULE_LICENSE("GPL");
+
+#define dprintk(level, format, a...)\
+ { if (debug >= level) printk(KERN_DEBUG format, ##a); }
+
+static int debug = 1;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activate debugging output");
+
+/* button data structure */
+struct bt_data {
+ u8 unknown1; /* constant */
+ u8 button; /* left button */
+ u8 rel_x; /* relative x coordinate */
+ u8 rel_y; /* relative y coordinate */
+};
+
+/* trackpad header structure */
+struct tp_header {
+ u8 unknown1[16]; /* constants, timers, etc */
+ u8 fingers; /* number of fingers on trackpad */
+ u8 unknown2[9]; /* constants, timers, etc */
+};
+
+/* trackpad finger structure */
+struct tp_finger {
+ __le16 origin; /* zero when switching track finger */
+ __le16 abs_x; /* absolute x coodinate */
+ __le16 abs_y; /* absolute y coodinate */
+ __le16 rel_x; /* relative x coodinate */
+ __le16 rel_y; /* relative y coodinate */
+ __le16 size_major; /* finger size, major axis? */
+ __le16 size_minor; /* finger size, minor axis? */
+ __le16 orientation; /* 16384 when point, else 15 bit angle */
+ __le16 force_major; /* trackpad force, major axis? */
+ __le16 force_minor; /* trackpad force, minor axis? */
+ __le16 unused[3]; /* zeros */
+ __le16 multi; /* one finger: varies, more fingers: constant */
+};
+
+/* trackpad data structure, empirically at least ten fingers */
+struct tp_data {
+ struct tp_header header;
+ struct tp_finger finger[16];
+};
+
+/* device-specific parameters */
+struct bcm5974_param {
+ int dim; /* logical dimension */
+ int fuzz; /* logical noise value */
+ int devmin; /* device minimum reading */
+ int devmax; /* device maximum reading */
+};
+
+/* device-specific configuration */
+struct bcm5974_config {
+ int ansi, iso, jis; /* the product id of this device */
+ int bt_ep; /* the endpoint of the button interface */
+ int bt_datalen; /* data length of the button interface */
+ int tp_ep; /* the endpoint of the trackpad interface */
+ int tp_datalen; /* data length of the trackpad interface */
+ struct bcm5974_param p; /* finger pressure limits */
+ struct bcm5974_param w; /* finger width limits */
+ struct bcm5974_param x; /* horizontal limits */
+ struct bcm5974_param y; /* vertical limits */
+};
+
+/* logical device structure */
+struct bcm5974 {
+ char phys[64];
+ struct usb_device *udev; /* usb device */
+ struct usb_interface *intf; /* our interface */
+ struct input_dev *input; /* input dev */
+ struct bcm5974_config cfg; /* device configuration */
+ struct mutex pm_mutex; /* serialize access to open/suspend */
+ int opened; /* 1: opened, 0: closed */
+ struct urb *bt_urb; /* button usb request block */
+ struct bt_data *bt_data; /* button transferred data */
+ struct urb *tp_urb; /* trackpad usb request block */
+ struct tp_data *tp_data; /* trackpad transferred data */
+ int fingers; /* number of fingers on trackpad */
+};
+
+/* logical dimensions */
+#define DIM_PRESSURE 256 /* maximum finger pressure */
+#define DIM_WIDTH 16 /* maximum finger width */
+#define DIM_X 1280 /* maximum trackpad x value */
+#define DIM_Y 800 /* maximum trackpad y value */
+
+/* logical signal quality */
+#define SN_PRESSURE 45 /* pressure signal-to-noise ratio */
+#define SN_WIDTH 100 /* width signal-to-noise ratio */
+#define SN_COORD 250 /* coordinate signal-to-noise ratio */
+
+/* pressure thresholds */
+#define PRESSURE_LOW (2 * DIM_PRESSURE / SN_PRESSURE)
+#define PRESSURE_HIGH (3 * PRESSURE_LOW)
+
+/* device constants */
+static const struct bcm5974_config bcm5974_config_table[] = {
+ {
+ USB_DEVICE_ID_APPLE_WELLSPRING_ANSI,
+ USB_DEVICE_ID_APPLE_WELLSPRING_ISO,
+ USB_DEVICE_ID_APPLE_WELLSPRING_JIS,
+ 0x84, sizeof(struct bt_data),
+ 0x81, sizeof(struct tp_data),
+ { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
+ { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
+ { DIM_X, DIM_X / SN_COORD, -4824, 5342 },
+ { DIM_Y, DIM_Y / SN_COORD, -172, 5820 }
+ },
+ {
+ USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI,
+ USB_DEVICE_ID_APPLE_WELLSPRING2_ISO,
+ USB_DEVICE_ID_APPLE_WELLSPRING2_JIS,
+ 0x84, sizeof(struct bt_data),
+ 0x81, sizeof(struct tp_data),
+ { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
+ { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
+ { DIM_X, DIM_X / SN_COORD, -4824, 4824 },
+ { DIM_Y, DIM_Y / SN_COORD, -172, 4290 }
+ },
+ {}
+};
+
+/* return the device-specific configuration by device */
+static const struct bcm5974_config *bcm5974_get_config(struct usb_device *udev)
+{
+ u16 id = le16_to_cpu(udev->descriptor.idProduct);
+ const struct bcm5974_config *cfg;
+
+ for (cfg = bcm5974_config_table; cfg->ansi; ++cfg)
+ if (cfg->ansi == id || cfg->iso == id || cfg->jis == id)
+ return cfg;
+
+ return bcm5974_config_table;
+}
+
+/* convert 16-bit little endian to signed integer */
+static inline int raw2int(__le16 x)
+{
+ return (signed short)le16_to_cpu(x);
+}
+
+/* scale device data to logical dimensions (asserts devmin < devmax) */
+static inline int int2scale(const struct bcm5974_param *p, int x)
+{
+ return x * p->dim / (p->devmax - p->devmin);
+}
+
+/* all logical value ranges are [0,dim). */
+static inline int int2bound(const struct bcm5974_param *p, int x)
+{
+ int s = int2scale(p, x);
+
+ return clamp_val(s, 0, p->dim - 1);
+}
+
+/* setup which logical events to report */
+static void setup_events_to_report(struct input_dev *input_dev,
+ const struct bcm5974_config *cfg)
+{
+ __set_bit(EV_ABS, input_dev->evbit);
+
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ 0, cfg->p.dim, cfg->p.fuzz, 0);
+ input_set_abs_params(input_dev, ABS_TOOL_WIDTH,
+ 0, cfg->w.dim, cfg->w.fuzz, 0);
+ input_set_abs_params(input_dev, ABS_X,
+ 0, cfg->x.dim, cfg->x.fuzz, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ 0, cfg->y.dim, cfg->y.fuzz, 0);
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
+ __set_bit(BTN_LEFT, input_dev->keybit);
+}
+
+/* report button data as logical button state */
+static int report_bt_state(struct bcm5974 *dev, int size)
+{
+ if (size != sizeof(struct bt_data))
+ return -EIO;
+
+ input_report_key(dev->input, BTN_LEFT, dev->bt_data->button);
+ input_sync(dev->input);
+
+ return 0;
+}
+
+/* report trackpad data as logical trackpad state */
+static int report_tp_state(struct bcm5974 *dev, int size)
+{
+ const struct bcm5974_config *c = &dev->cfg;
+ const struct tp_finger *f = dev->tp_data->finger;
+ struct input_dev *input = dev->input;
+ const int fingers = (size - 26) / 28;
+ int raw_p, raw_w, raw_x, raw_y;
+ int ptest = 0, origin = 0, nmin = 0, nmax = 0;
+ int abs_p = 0, abs_w = 0, abs_x = 0, abs_y = 0;
+
+ if (size < 26 || (size - 26) % 28 != 0)
+ return -EIO;
+
+ /* always track the first finger; when detached, start over */
+ if (fingers) {
+ raw_p = raw2int(f->force_major);
+ raw_w = raw2int(f->size_major);
+ raw_x = raw2int(f->abs_x);
+ raw_y = raw2int(f->abs_y);
+
+ dprintk(9,
+ "bcm5974: raw: p: %+05d w: %+05d x: %+05d y: %+05d\n",
+ raw_p, raw_w, raw_x, raw_y);
+
+ ptest = int2bound(&c->p, raw_p);
+ origin = raw2int(f->origin);
+ }
+
+ /* while tracking finger still valid, count all fingers */
+ if (ptest > PRESSURE_LOW && origin) {
+ abs_p = ptest;
+ abs_w = int2bound(&c->w, raw_w);
+ abs_x = int2bound(&c->x, raw_x - c->x.devmin);
+ abs_y = int2bound(&c->y, c->y.devmax - raw_y);
+ for (; f != dev->tp_data->finger + fingers; f++) {
+ ptest = int2bound(&c->p, raw2int(f->force_major));
+ if (ptest > PRESSURE_LOW)
+ nmax++;
+ if (ptest > PRESSURE_HIGH)
+ nmin++;
+ }
+ }
+
+ if (dev->fingers < nmin)
+ dev->fingers = nmin;
+ if (dev->fingers > nmax)
+ dev->fingers = nmax;
+
+ input_report_key(input, BTN_TOUCH, dev->fingers > 0);
+ input_report_key(input, BTN_TOOL_FINGER, dev->fingers == 1);
+ input_report_key(input, BTN_TOOL_DOUBLETAP, dev->fingers == 2);
+ input_report_key(input, BTN_TOOL_TRIPLETAP, dev->fingers > 2);
+
+ input_report_abs(input, ABS_PRESSURE, abs_p);
+ input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
+
+ if (abs_p) {
+ input_report_abs(input, ABS_X, abs_x);
+ input_report_abs(input, ABS_Y, abs_y);
+
+ dprintk(8,
+ "bcm5974: abs: p: %+05d w: %+05d x: %+05d y: %+05d "
+ "nmin: %d nmax: %d n: %d\n",
+ abs_p, abs_w, abs_x, abs_y, nmin, nmax, dev->fingers);
+
+ }
+
+ input_sync(input);
+
+ return 0;
+}
+
+/* Wellspring initialization constants */
+#define BCM5974_WELLSPRING_MODE_READ_REQUEST_ID 1
+#define BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID 9
+#define BCM5974_WELLSPRING_MODE_REQUEST_VALUE 0x300
+#define BCM5974_WELLSPRING_MODE_REQUEST_INDEX 0
+#define BCM5974_WELLSPRING_MODE_VENDOR_VALUE 0x01
+#define BCM5974_WELLSPRING_MODE_NORMAL_VALUE 0x08
+
+static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on)
+{
+ char *data = kmalloc(8, GFP_KERNEL);
+ int retval = 0, size;
+
+ if (!data) {
+ err("bcm5974: out of memory");
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ /* read configuration */
+ size = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+ BCM5974_WELLSPRING_MODE_READ_REQUEST_ID,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ BCM5974_WELLSPRING_MODE_REQUEST_VALUE,
+ BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
+
+ if (size != 8) {
+ err("bcm5974: could not read from device");
+ retval = -EIO;
+ goto out;
+ }
+
+ /* apply the mode switch */
+ data[0] = on ?
+ BCM5974_WELLSPRING_MODE_VENDOR_VALUE :
+ BCM5974_WELLSPRING_MODE_NORMAL_VALUE;
+
+ /* write configuration */
+ size = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+ BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ BCM5974_WELLSPRING_MODE_REQUEST_VALUE,
+ BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
+
+ if (size != 8) {
+ err("bcm5974: could not write to device");
+ retval = -EIO;
+ goto out;
+ }
+
+ dprintk(2, "bcm5974: switched to %s mode.\n",
+ on ? "wellspring" : "normal");
+
+ out:
+ kfree(data);
+ return retval;
+}
+
+static void bcm5974_irq_button(struct urb *urb)
+{
+ struct bcm5974 *dev = urb->context;
+ int error;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -EOVERFLOW:
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ dbg("bcm5974: button urb shutting down: %d", urb->status);
+ return;
+ default:
+ dbg("bcm5974: button urb status: %d", urb->status);
+ goto exit;
+ }
+
+ if (report_bt_state(dev, dev->bt_urb->actual_length))
+ dprintk(1, "bcm5974: bad button package, length: %d\n",
+ dev->bt_urb->actual_length);
+
+exit:
+ error = usb_submit_urb(dev->bt_urb, GFP_ATOMIC);
+ if (error)
+ err("bcm5974: button urb failed: %d", error);
+}
+
+static void bcm5974_irq_trackpad(struct urb *urb)
+{
+ struct bcm5974 *dev = urb->context;
+ int error;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -EOVERFLOW:
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ dbg("bcm5974: trackpad urb shutting down: %d", urb->status);
+ return;
+ default:
+ dbg("bcm5974: trackpad urb status: %d", urb->status);
+ goto exit;
+ }
+
+ /* control response ignored */
+ if (dev->tp_urb->actual_length == 2)
+ goto exit;
+
+ if (report_tp_state(dev, dev->tp_urb->actual_length))
+ dprintk(1, "bcm5974: bad trackpad package, length: %d\n",
+ dev->tp_urb->actual_length);
+
+exit:
+ error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
+ if (error)
+ err("bcm5974: trackpad urb failed: %d", error);
+}
+
+/*
+ * The Wellspring trackpad, like many recent Apple trackpads, share
+ * the usb device with the keyboard. Since keyboards are usually
+ * handled by the HID system, the device ends up being handled by two
+ * modules. Setting up the device therefore becomes slightly
+ * complicated. To enable multitouch features, a mode switch is
+ * required, which is usually applied via the control interface of the
+ * device. It can be argued where this switch should take place. In
+ * some drivers, like appletouch, the switch is made during
+ * probe. However, the hid module may also alter the state of the
+ * device, resulting in trackpad malfunction under certain
+ * circumstances. To get around this problem, there is at least one
+ * example that utilizes the USB_QUIRK_RESET_RESUME quirk in order to
+ * recieve a reset_resume request rather than the normal resume.
+ * Since the implementation of reset_resume is equal to mode switch
+ * plus start_traffic, it seems easier to always do the switch when
+ * starting traffic on the device.
+ */
+static int bcm5974_start_traffic(struct bcm5974 *dev)
+{
+ if (bcm5974_wellspring_mode(dev, true)) {
+ dprintk(1, "bcm5974: mode switch failed\n");
+ goto error;
+ }
+
+ if (usb_submit_urb(dev->bt_urb, GFP_KERNEL))
+ goto error;
+
+ if (usb_submit_urb(dev->tp_urb, GFP_KERNEL))
+ goto err_kill_bt;
+
+ return 0;
+
+err_kill_bt:
+ usb_kill_urb(dev->bt_urb);
+error:
+ return -EIO;
+}
+
+static void bcm5974_pause_traffic(struct bcm5974 *dev)
+{
+ usb_kill_urb(dev->tp_urb);
+ usb_kill_urb(dev->bt_urb);
+ bcm5974_wellspring_mode(dev, false);
+}
+
+/*
+ * The code below implements open/close and manual suspend/resume.
+ * All functions may be called in random order.
+ *
+ * Opening a suspended device fails with EACCES - permission denied.
+ *
+ * Failing a resume leaves the device resumed but closed.
+ */
+static int bcm5974_open(struct input_dev *input)
+{
+ struct bcm5974 *dev = input_get_drvdata(input);
+ int error;
+
+ error = usb_autopm_get_interface(dev->intf);
+ if (error)
+ return error;
+
+ mutex_lock(&dev->pm_mutex);
+
+ error = bcm5974_start_traffic(dev);
+ if (!error)
+ dev->opened = 1;
+
+ mutex_unlock(&dev->pm_mutex);
+
+ if (error)
+ usb_autopm_put_interface(dev->intf);
+
+ return error;
+}
+
+static void bcm5974_close(struct input_dev *input)
+{
+ struct bcm5974 *dev = input_get_drvdata(input);
+
+ mutex_lock(&dev->pm_mutex);
+
+ bcm5974_pause_traffic(dev);
+ dev->opened = 0;
+
+ mutex_unlock(&dev->pm_mutex);
+
+ usb_autopm_put_interface(dev->intf);
+}
+
+static int bcm5974_suspend(struct usb_interface *iface, pm_message_t message)
+{
+ struct bcm5974 *dev = usb_get_intfdata(iface);
+
+ mutex_lock(&dev->pm_mutex);
+
+ if (dev->opened)
+ bcm5974_pause_traffic(dev);
+
+ mutex_unlock(&dev->pm_mutex);
+
+ return 0;
+}
+
+static int bcm5974_resume(struct usb_interface *iface)
+{
+ struct bcm5974 *dev = usb_get_intfdata(iface);
+ int error = 0;
+
+ mutex_lock(&dev->pm_mutex);
+
+ if (dev->opened)
+ error = bcm5974_start_traffic(dev);
+
+ mutex_unlock(&dev->pm_mutex);
+
+ return error;
+}
+
+static int bcm5974_probe(struct usb_interface *iface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(iface);
+ const struct bcm5974_config *cfg;
+ struct bcm5974 *dev;
+ struct input_dev *input_dev;
+ int error = -ENOMEM;
+
+ /* find the product index */
+ cfg = bcm5974_get_config(udev);
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(struct bcm5974), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!dev || !input_dev) {
+ err("bcm5974: out of memory");
+ goto err_free_devs;
+ }
+
+ dev->udev = udev;
+ dev->intf = iface;
+ dev->input = input_dev;
+ dev->cfg = *cfg;
+ mutex_init(&dev->pm_mutex);
+
+ /* setup urbs */
+ dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->bt_urb)
+ goto err_free_devs;
+
+ dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->tp_urb)
+ goto err_free_bt_urb;
+
+ dev->bt_data = usb_buffer_alloc(dev->udev,
+ dev->cfg.bt_datalen, GFP_KERNEL,
+ &dev->bt_urb->transfer_dma);
+ if (!dev->bt_data)
+ goto err_free_urb;
+
+ dev->tp_data = usb_buffer_alloc(dev->udev,
+ dev->cfg.tp_datalen, GFP_KERNEL,
+ &dev->tp_urb->transfer_dma);
+ if (!dev->tp_data)
+ goto err_free_bt_buffer;
+
+ usb_fill_int_urb(dev->bt_urb, udev,
+ usb_rcvintpipe(udev, cfg->bt_ep),
+ dev->bt_data, dev->cfg.bt_datalen,
+ bcm5974_irq_button, dev, 1);
+
+ usb_fill_int_urb(dev->tp_urb, udev,
+ usb_rcvintpipe(udev, cfg->tp_ep),
+ dev->tp_data, dev->cfg.tp_datalen,
+ bcm5974_irq_trackpad, dev, 1);
+
+ /* create bcm5974 device */
+ usb_make_path(udev, dev->phys, sizeof(dev->phys));
+ strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+ input_dev->name = "bcm5974";
+ input_dev->phys = dev->phys;
+ usb_to_input_id(dev->udev, &input_dev->id);
+ input_dev->dev.parent = &iface->dev;
+
+ input_set_drvdata(input_dev, dev);
+
+ input_dev->open = bcm5974_open;
+ input_dev->close = bcm5974_close;
+
+ setup_events_to_report(input_dev, cfg);
+
+ error = input_register_device(dev->input);
+ if (error)
+ goto err_free_buffer;
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(iface, dev);
+
+ return 0;
+
+err_free_buffer:
+ usb_buffer_free(dev->udev, dev->cfg.tp_datalen,
+ dev->tp_data, dev->tp_urb->transfer_dma);
+err_free_bt_buffer:
+ usb_buffer_free(dev->udev, dev->cfg.bt_datalen,
+ dev->bt_data, dev->bt_urb->transfer_dma);
+err_free_urb:
+ usb_free_urb(dev->tp_urb);
+err_free_bt_urb:
+ usb_free_urb(dev->bt_urb);
+err_free_devs:
+ usb_set_intfdata(iface, NULL);
+ input_free_device(input_dev);
+ kfree(dev);
+ return error;
+}
+
+static void bcm5974_disconnect(struct usb_interface *iface)
+{
+ struct bcm5974 *dev = usb_get_intfdata(iface);
+
+ usb_set_intfdata(iface, NULL);
+
+ input_unregister_device(dev->input);
+ usb_buffer_free(dev->udev, dev->cfg.tp_datalen,
+ dev->tp_data, dev->tp_urb->transfer_dma);
+ usb_buffer_free(dev->udev, dev->cfg.bt_datalen,
+ dev->bt_data, dev->bt_urb->transfer_dma);
+ usb_free_urb(dev->tp_urb);
+ usb_free_urb(dev->bt_urb);
+ kfree(dev);
+}
+
+static struct usb_driver bcm5974_driver = {
+ .name = "bcm5974",
+ .probe = bcm5974_probe,
+ .disconnect = bcm5974_disconnect,
+ .suspend = bcm5974_suspend,
+ .resume = bcm5974_resume,
+ .reset_resume = bcm5974_resume,
+ .id_table = bcm5974_table,
+ .supports_autosuspend = 1,
+};
+
+static int __init bcm5974_init(void)
+{
+ return usb_register(&bcm5974_driver);
+}
+
+static void __exit bcm5974_exit(void)
+{
+ usb_deregister(&bcm5974_driver);
+}
+
+module_init(bcm5974_init);
+module_exit(bcm5974_exit);
+
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
new file mode 100644
index 0000000..b9a25d5
--- /dev/null
+++ b/drivers/input/mouse/elantech.c
@@ -0,0 +1,674 @@
+/*
+ * Elantech Touchpad driver (v5)
+ *
+ * Copyright (C) 2007-2008 Arjan Opmeer <arjan@opmeer.net>
+ *
+ * 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.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include "psmouse.h"
+#include "elantech.h"
+
+#define elantech_debug(format, arg...) \
+ do { \
+ if (etd->debug) \
+ printk(KERN_DEBUG format, ##arg); \
+ } while (0)
+
+/*
+ * Send a Synaptics style sliced query command
+ */
+static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c,
+ unsigned char *param)
+{
+ if (psmouse_sliced_command(psmouse, c) ||
+ ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
+ pr_err("elantech.c: synaptics_send_cmd query 0x%02x failed.\n", c);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * A retrying version of ps2_command
+ */
+static int elantech_ps2_command(struct psmouse *psmouse,
+ unsigned char *param, int command)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ struct elantech_data *etd = psmouse->private;
+ int rc;
+ int tries = ETP_PS2_COMMAND_TRIES;
+
+ do {
+ rc = ps2_command(ps2dev, param, command);
+ if (rc == 0)
+ break;
+ tries--;
+ elantech_debug("elantech.c: retrying ps2 command 0x%02x (%d).\n",
+ command, tries);
+ msleep(ETP_PS2_COMMAND_DELAY);
+ } while (tries > 0);
+
+ if (rc)
+ pr_err("elantech.c: ps2 command 0x%02x failed.\n", command);
+
+ return rc;
+}
+
+/*
+ * Send an Elantech style special command to read a value from a register
+ */
+static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
+ unsigned char *val)
+{
+ struct elantech_data *etd = psmouse->private;
+ unsigned char param[3];
+ int rc = 0;
+
+ if (reg < 0x10 || reg > 0x26)
+ return -1;
+
+ if (reg > 0x11 && reg < 0x20)
+ return -1;
+
+ switch (etd->hw_version) {
+ case 1:
+ if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) ||
+ psmouse_sliced_command(psmouse, reg) ||
+ ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) {
+ rc = -1;
+ }
+ break;
+
+ case 2:
+ if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READ) ||
+ elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, reg) ||
+ elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) {
+ rc = -1;
+ }
+ break;
+ }
+
+ if (rc)
+ pr_err("elantech.c: failed to read register 0x%02x.\n", reg);
+ else
+ *val = param[0];
+
+ return rc;
+}
+
+/*
+ * Send an Elantech style special command to write a register with a value
+ */
+static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
+ unsigned char val)
+{
+ struct elantech_data *etd = psmouse->private;
+ int rc = 0;
+
+ if (reg < 0x10 || reg > 0x26)
+ return -1;
+
+ if (reg > 0x11 && reg < 0x20)
+ return -1;
+
+ switch (etd->hw_version) {
+ case 1:
+ if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) ||
+ psmouse_sliced_command(psmouse, reg) ||
+ psmouse_sliced_command(psmouse, val) ||
+ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) {
+ rc = -1;
+ }
+ break;
+
+ case 2:
+ if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) ||
+ elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, reg) ||
+ elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
+ elantech_ps2_command(psmouse, NULL, val) ||
+ elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
+ rc = -1;
+ }
+ break;
+ }
+
+ if (rc)
+ pr_err("elantech.c: failed to write register 0x%02x with value 0x%02x.\n",
+ reg, val);
+
+ return rc;
+}
+
+/*
+ * Dump a complete mouse movement packet to the syslog
+ */
+static void elantech_packet_dump(unsigned char *packet, int size)
+{
+ int i;
+
+ printk(KERN_DEBUG "elantech.c: PS/2 packet [");
+ for (i = 0; i < size; i++)
+ printk("%s0x%02x ", (i) ? ", " : " ", packet[i]);
+ printk("]\n");
+}
+
+/*
+ * Interpret complete data packets and report absolute mode input events for
+ * hardware version 1. (4 byte packets)
+ */
+static void elantech_report_absolute_v1(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct elantech_data *etd = psmouse->private;
+ unsigned char *packet = psmouse->packet;
+ int fingers;
+
+ if (etd->fw_version_maj == 0x01) {
+ /* byte 0: D U p1 p2 1 p3 R L
+ byte 1: f 0 th tw x9 x8 y9 y8 */
+ fingers = ((packet[1] & 0x80) >> 7) +
+ ((packet[1] & 0x30) >> 4);
+ } else {
+ /* byte 0: n1 n0 p2 p1 1 p3 R L
+ byte 1: 0 0 0 0 x9 x8 y9 y8 */
+ fingers = (packet[0] & 0xc0) >> 6;
+ }
+
+ input_report_key(dev, BTN_TOUCH, fingers != 0);
+
+ /* byte 2: x7 x6 x5 x4 x3 x2 x1 x0
+ byte 3: y7 y6 y5 y4 y3 y2 y1 y0 */
+ if (fingers) {
+ input_report_abs(dev, ABS_X,
+ ((packet[1] & 0x0c) << 6) | packet[2]);
+ input_report_abs(dev, ABS_Y, ETP_YMAX_V1 -
+ (((packet[1] & 0x03) << 8) | packet[3]));
+ }
+
+ input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+ input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+ input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+ input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+ input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+
+ if ((etd->fw_version_maj == 0x01) &&
+ (etd->capabilities & ETP_CAP_HAS_ROCKER)) {
+ /* rocker up */
+ input_report_key(dev, BTN_FORWARD, packet[0] & 0x40);
+ /* rocker down */
+ input_report_key(dev, BTN_BACK, packet[0] & 0x80);
+ }
+
+ input_sync(dev);
+}
+
+/*
+ * Interpret complete data packets and report absolute mode input events for
+ * hardware version 2. (6 byte packets)
+ */
+static void elantech_report_absolute_v2(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ unsigned char *packet = psmouse->packet;
+ int fingers, x1, y1, x2, y2;
+
+ /* byte 0: n1 n0 . . . . R L */
+ fingers = (packet[0] & 0xc0) >> 6;
+ input_report_key(dev, BTN_TOUCH, fingers != 0);
+
+ switch (fingers) {
+ case 1:
+ /* byte 1: x15 x14 x13 x12 x11 x10 x9 x8
+ byte 2: x7 x6 x5 x4 x4 x2 x1 x0 */
+ input_report_abs(dev, ABS_X, (packet[1] << 8) | packet[2]);
+ /* byte 4: y15 y14 y13 y12 y11 y10 y8 y8
+ byte 5: y7 y6 y5 y4 y3 y2 y1 y0 */
+ input_report_abs(dev, ABS_Y, ETP_YMAX_V2 -
+ ((packet[4] << 8) | packet[5]));
+ break;
+
+ case 2:
+ /* The coordinate of each finger is reported separately with
+ a lower resolution for two finger touches */
+ /* byte 0: . . ay8 ax8 . . . .
+ byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 */
+ x1 = ((packet[0] & 0x10) << 4) | packet[1];
+ /* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */
+ y1 = ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2]);
+ /* byte 3: . . by8 bx8 . . . .
+ byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0 */
+ x2 = ((packet[3] & 0x10) << 4) | packet[4];
+ /* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */
+ y2 = ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]);
+ /* For compatibility with the X Synaptics driver scale up one
+ coordinate and report as ordinary mouse movent */
+ input_report_abs(dev, ABS_X, x1 << 2);
+ input_report_abs(dev, ABS_Y, y1 << 2);
+ /* For compatibility with the proprietary X Elantech driver
+ report both coordinates as hat coordinates */
+ input_report_abs(dev, ABS_HAT0X, x1);
+ input_report_abs(dev, ABS_HAT0Y, y1);
+ input_report_abs(dev, ABS_HAT1X, x2);
+ input_report_abs(dev, ABS_HAT1Y, y2);
+ break;
+ }
+
+ input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
+ input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
+ input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
+ input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
+ input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+
+ input_sync(dev);
+}
+
+static int elantech_check_parity_v1(struct psmouse *psmouse)
+{
+ struct elantech_data *etd = psmouse->private;
+ unsigned char *packet = psmouse->packet;
+ unsigned char p1, p2, p3;
+
+ /* Parity bits are placed differently */
+ if (etd->fw_version_maj == 0x01) {
+ /* byte 0: D U p1 p2 1 p3 R L */
+ p1 = (packet[0] & 0x20) >> 5;
+ p2 = (packet[0] & 0x10) >> 4;
+ } else {
+ /* byte 0: n1 n0 p2 p1 1 p3 R L */
+ p1 = (packet[0] & 0x10) >> 4;
+ p2 = (packet[0] & 0x20) >> 5;
+ }
+
+ p3 = (packet[0] & 0x04) >> 2;
+
+ return etd->parity[packet[1]] == p1 &&
+ etd->parity[packet[2]] == p2 &&
+ etd->parity[packet[3]] == p3;
+}
+
+/*
+ * Process byte stream from mouse and handle complete packets
+ */
+static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
+{
+ struct elantech_data *etd = psmouse->private;
+
+ if (psmouse->pktcnt < psmouse->pktsize)
+ return PSMOUSE_GOOD_DATA;
+
+ if (etd->debug > 1)
+ elantech_packet_dump(psmouse->packet, psmouse->pktsize);
+
+ switch (etd->hw_version) {
+ case 1:
+ if (etd->paritycheck && !elantech_check_parity_v1(psmouse))
+ return PSMOUSE_BAD_DATA;
+
+ elantech_report_absolute_v1(psmouse);
+ break;
+
+ case 2:
+ /* We don't know how to check parity in protocol v2 */
+ elantech_report_absolute_v2(psmouse);
+ break;
+ }
+
+ return PSMOUSE_FULL_PACKET;
+}
+
+/*
+ * Put the touchpad into absolute mode
+ */
+static int elantech_set_absolute_mode(struct psmouse *psmouse)
+{
+ struct elantech_data *etd = psmouse->private;
+ unsigned char val;
+ int tries = ETP_READ_BACK_TRIES;
+ int rc = 0;
+
+ switch (etd->hw_version) {
+ case 1:
+ etd->reg_10 = 0x16;
+ etd->reg_11 = 0x8f;
+ if (elantech_write_reg(psmouse, 0x10, etd->reg_10) ||
+ elantech_write_reg(psmouse, 0x11, etd->reg_11)) {
+ rc = -1;
+ }
+ break;
+
+ case 2:
+ /* Windows driver values */
+ etd->reg_10 = 0x54;
+ etd->reg_11 = 0x88; /* 0x8a */
+ etd->reg_21 = 0x60; /* 0x00 */
+ if (elantech_write_reg(psmouse, 0x10, etd->reg_10) ||
+ elantech_write_reg(psmouse, 0x11, etd->reg_11) ||
+ elantech_write_reg(psmouse, 0x21, etd->reg_21)) {
+ rc = -1;
+ break;
+ }
+ /*
+ * Read back reg 0x10. The touchpad is probably initalising
+ * and not ready until we read back the value we just wrote.
+ */
+ do {
+ rc = elantech_read_reg(psmouse, 0x10, &val);
+ if (rc == 0)
+ break;
+ tries--;
+ elantech_debug("elantech.c: retrying read (%d).\n",
+ tries);
+ msleep(ETP_READ_BACK_DELAY);
+ } while (tries > 0);
+ if (rc)
+ pr_err("elantech.c: failed to read back register 0x10.\n");
+ break;
+ }
+
+ if (rc)
+ pr_err("elantech.c: failed to initialise registers.\n");
+
+ return rc;
+}
+
+/*
+ * Set the appropriate event bits for the input subsystem
+ */
+static void elantech_set_input_params(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct elantech_data *etd = psmouse->private;
+
+ __set_bit(EV_KEY, dev->evbit);
+ __set_bit(EV_ABS, dev->evbit);
+
+ __set_bit(BTN_LEFT, dev->keybit);
+ __set_bit(BTN_RIGHT, dev->keybit);
+
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOOL_FINGER, dev->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+
+ switch (etd->hw_version) {
+ case 1:
+ /* Rocker button */
+ if ((etd->fw_version_maj == 0x01) &&
+ (etd->capabilities & ETP_CAP_HAS_ROCKER)) {
+ __set_bit(BTN_FORWARD, dev->keybit);
+ __set_bit(BTN_BACK, dev->keybit);
+ }
+ input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, ETP_XMAX_V1, 0, 0);
+ input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, ETP_YMAX_V1, 0, 0);
+ break;
+
+ case 2:
+ input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0);
+ input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0);
+ input_set_abs_params(dev, ABS_HAT0X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0);
+ input_set_abs_params(dev, ABS_HAT0Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0);
+ input_set_abs_params(dev, ABS_HAT1X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0);
+ input_set_abs_params(dev, ABS_HAT1Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0);
+ break;
+ }
+}
+
+struct elantech_attr_data {
+ size_t field_offset;
+ unsigned char reg;
+};
+
+/*
+ * Display a register value by reading a sysfs entry
+ */
+static ssize_t elantech_show_int_attr(struct psmouse *psmouse, void *data,
+ char *buf)
+{
+ struct elantech_data *etd = psmouse->private;
+ struct elantech_attr_data *attr = data;
+ unsigned char *reg = (unsigned char *) etd + attr->field_offset;
+ int rc = 0;
+
+ if (attr->reg)
+ rc = elantech_read_reg(psmouse, attr->reg, reg);
+
+ return sprintf(buf, "0x%02x\n", (attr->reg && rc) ? -1 : *reg);
+}
+
+/*
+ * Write a register value by writing a sysfs entry
+ */
+static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
+ void *data, const char *buf, size_t count)
+{
+ struct elantech_data *etd = psmouse->private;
+ struct elantech_attr_data *attr = data;
+ unsigned char *reg = (unsigned char *) etd + attr->field_offset;
+ unsigned long value;
+ int err;
+
+ err = strict_strtoul(buf, 16, &value);
+ if (err)
+ return err;
+
+ if (value > 0xff)
+ return -EINVAL;
+
+ /* Do we need to preserve some bits for version 2 hardware too? */
+ if (etd->hw_version == 1) {
+ if (attr->reg == 0x10)
+ /* Force absolute mode always on */
+ value |= ETP_R10_ABSOLUTE_MODE;
+ else if (attr->reg == 0x11)
+ /* Force 4 byte mode always on */
+ value |= ETP_R11_4_BYTE_MODE;
+ }
+
+ if (!attr->reg || elantech_write_reg(psmouse, attr->reg, value) == 0)
+ *reg = value;
+
+ return count;
+}
+
+#define ELANTECH_INT_ATTR(_name, _register) \
+ static struct elantech_attr_data elantech_attr_##_name = { \
+ .field_offset = offsetof(struct elantech_data, _name), \
+ .reg = _register, \
+ }; \
+ PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
+ &elantech_attr_##_name, \
+ elantech_show_int_attr, \
+ elantech_set_int_attr)
+
+ELANTECH_INT_ATTR(reg_10, 0x10);
+ELANTECH_INT_ATTR(reg_11, 0x11);
+ELANTECH_INT_ATTR(reg_20, 0x20);
+ELANTECH_INT_ATTR(reg_21, 0x21);
+ELANTECH_INT_ATTR(reg_22, 0x22);
+ELANTECH_INT_ATTR(reg_23, 0x23);
+ELANTECH_INT_ATTR(reg_24, 0x24);
+ELANTECH_INT_ATTR(reg_25, 0x25);
+ELANTECH_INT_ATTR(reg_26, 0x26);
+ELANTECH_INT_ATTR(debug, 0);
+ELANTECH_INT_ATTR(paritycheck, 0);
+
+static struct attribute *elantech_attrs[] = {
+ &psmouse_attr_reg_10.dattr.attr,
+ &psmouse_attr_reg_11.dattr.attr,
+ &psmouse_attr_reg_20.dattr.attr,
+ &psmouse_attr_reg_21.dattr.attr,
+ &psmouse_attr_reg_22.dattr.attr,
+ &psmouse_attr_reg_23.dattr.attr,
+ &psmouse_attr_reg_24.dattr.attr,
+ &psmouse_attr_reg_25.dattr.attr,
+ &psmouse_attr_reg_26.dattr.attr,
+ &psmouse_attr_debug.dattr.attr,
+ &psmouse_attr_paritycheck.dattr.attr,
+ NULL
+};
+
+static struct attribute_group elantech_attr_group = {
+ .attrs = elantech_attrs,
+};
+
+/*
+ * Use magic knock to detect Elantech touchpad
+ */
+int elantech_detect(struct psmouse *psmouse, int set_properties)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[3];
+
+ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+
+ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
+ ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
+ pr_err("elantech.c: sending Elantech magic knock failed.\n");
+ return -1;
+ }
+
+ /*
+ * Report this in case there are Elantech models that use a different
+ * set of magic numbers
+ */
+ if (param[0] != 0x3c || param[1] != 0x03 || param[2] != 0xc8) {
+ pr_info("elantech.c: unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n",
+ param[0], param[1], param[2]);
+ return -1;
+ }
+
+ if (set_properties) {
+ psmouse->vendor = "Elantech";
+ psmouse->name = "Touchpad";
+ }
+
+ return 0;
+}
+
+/*
+ * Clean up sysfs entries when disconnecting
+ */
+static void elantech_disconnect(struct psmouse *psmouse)
+{
+ sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
+ &elantech_attr_group);
+ kfree(psmouse->private);
+ psmouse->private = NULL;
+}
+
+/*
+ * Put the touchpad back into absolute mode when reconnecting
+ */
+static int elantech_reconnect(struct psmouse *psmouse)
+{
+ if (elantech_detect(psmouse, 0))
+ return -1;
+
+ if (elantech_set_absolute_mode(psmouse)) {
+ pr_err("elantech.c: failed to put touchpad back into absolute mode.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize the touchpad and create sysfs entries
+ */
+int elantech_init(struct psmouse *psmouse)
+{
+ struct elantech_data *etd;
+ int i, error;
+ unsigned char param[3];
+
+ etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
+ psmouse->private = etd;
+ if (!etd)
+ return -1;
+
+ etd->parity[0] = 1;
+ for (i = 1; i < 256; i++)
+ etd->parity[i] = etd->parity[i & (i - 1)] ^ 1;
+
+ /*
+ * Find out what version hardware this is
+ */
+ if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
+ pr_err("elantech.c: failed to query firmware version.\n");
+ goto init_fail;
+ }
+ pr_info("elantech.c: Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n",
+ param[0], param[1], param[2]);
+ etd->fw_version_maj = param[0];
+ etd->fw_version_min = param[2];
+
+ /*
+ * Assume every version greater than this is new EeePC style
+ * hardware with 6 byte packets
+ */
+ if (etd->fw_version_maj >= 0x02 && etd->fw_version_min >= 0x30) {
+ etd->hw_version = 2;
+ /* For now show extra debug information */
+ etd->debug = 1;
+ /* Don't know how to do parity checking for version 2 */
+ etd->paritycheck = 0;
+ } else {
+ etd->hw_version = 1;
+ etd->paritycheck = 1;
+ }
+ pr_info("elantech.c: assuming hardware version %d, firmware version %d.%d\n",
+ etd->hw_version, etd->fw_version_maj, etd->fw_version_min);
+
+ if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param)) {
+ pr_err("elantech.c: failed to query capabilities.\n");
+ goto init_fail;
+ }
+ pr_info("elantech.c: Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
+ param[0], param[1], param[2]);
+ etd->capabilities = param[0];
+
+ if (elantech_set_absolute_mode(psmouse)) {
+ pr_err("elantech.c: failed to put touchpad into absolute mode.\n");
+ goto init_fail;
+ }
+
+ elantech_set_input_params(psmouse);
+
+ error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
+ &elantech_attr_group);
+ if (error) {
+ pr_err("elantech.c: failed to create sysfs attributes, error: %d.\n",
+ error);
+ goto init_fail;
+ }
+
+ psmouse->protocol_handler = elantech_process_byte;
+ psmouse->disconnect = elantech_disconnect;
+ psmouse->reconnect = elantech_reconnect;
+ psmouse->pktsize = etd->hw_version == 2 ? 6 : 4;
+
+ return 0;
+
+ init_fail:
+ kfree(etd);
+ return -1;
+}
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
new file mode 100644
index 0000000..bee282b
--- /dev/null
+++ b/drivers/input/mouse/elantech.h
@@ -0,0 +1,124 @@
+/*
+ * Elantech Touchpad driver (v5)
+ *
+ * Copyright (C) 2007-2008 Arjan Opmeer <arjan@opmeer.net>
+ *
+ * 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.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#ifndef _ELANTECH_H
+#define _ELANTECH_H
+
+/*
+ * Command values for Synaptics style queries
+ */
+#define ETP_FW_VERSION_QUERY 0x01
+#define ETP_CAPABILITIES_QUERY 0x02
+
+/*
+ * Command values for register reading or writing
+ */
+#define ETP_REGISTER_READ 0x10
+#define ETP_REGISTER_WRITE 0x11
+
+/*
+ * Hardware version 2 custom PS/2 command value
+ */
+#define ETP_PS2_CUSTOM_COMMAND 0xf8
+
+/*
+ * Times to retry a ps2_command and millisecond delay between tries
+ */
+#define ETP_PS2_COMMAND_TRIES 3
+#define ETP_PS2_COMMAND_DELAY 500
+
+/*
+ * Times to try to read back a register and millisecond delay between tries
+ */
+#define ETP_READ_BACK_TRIES 5
+#define ETP_READ_BACK_DELAY 2000
+
+/*
+ * Register bitmasks for hardware version 1
+ */
+#define ETP_R10_ABSOLUTE_MODE 0x04
+#define ETP_R11_4_BYTE_MODE 0x02
+
+/*
+ * Capability bitmasks
+ */
+#define ETP_CAP_HAS_ROCKER 0x04
+
+/*
+ * One hard to find application note states that X axis range is 0 to 576
+ * and Y axis range is 0 to 384 for harware version 1.
+ * Edge fuzz might be necessary because of bezel around the touchpad
+ */
+#define ETP_EDGE_FUZZ_V1 32
+
+#define ETP_XMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1)
+#define ETP_XMAX_V1 (576 - ETP_EDGE_FUZZ_V1)
+#define ETP_YMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1)
+#define ETP_YMAX_V1 (384 - ETP_EDGE_FUZZ_V1)
+
+/*
+ * It seems the resolution for hardware version 2 doubled.
+ * Hence the X and Y ranges are doubled too.
+ * The bezel around the pad also appears to be smaller
+ */
+#define ETP_EDGE_FUZZ_V2 8
+
+#define ETP_XMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2)
+#define ETP_XMAX_V2 (1152 - ETP_EDGE_FUZZ_V2)
+#define ETP_YMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2)
+#define ETP_YMAX_V2 ( 768 - ETP_EDGE_FUZZ_V2)
+
+/*
+ * For two finger touches the coordinate of each finger gets reported
+ * separately but with reduced resolution.
+ */
+#define ETP_2FT_FUZZ 4
+
+#define ETP_2FT_XMIN ( 0 + ETP_2FT_FUZZ)
+#define ETP_2FT_XMAX (288 - ETP_2FT_FUZZ)
+#define ETP_2FT_YMIN ( 0 + ETP_2FT_FUZZ)
+#define ETP_2FT_YMAX (192 - ETP_2FT_FUZZ)
+
+struct elantech_data {
+ unsigned char reg_10;
+ unsigned char reg_11;
+ unsigned char reg_20;
+ unsigned char reg_21;
+ unsigned char reg_22;
+ unsigned char reg_23;
+ unsigned char reg_24;
+ unsigned char reg_25;
+ unsigned char reg_26;
+ unsigned char debug;
+ unsigned char capabilities;
+ unsigned char fw_version_maj;
+ unsigned char fw_version_min;
+ unsigned char hw_version;
+ unsigned char paritycheck;
+ unsigned char parity[256];
+};
+
+#ifdef CONFIG_MOUSE_PS2_ELANTECH
+int elantech_detect(struct psmouse *psmouse, int set_properties);
+int elantech_init(struct psmouse *psmouse);
+#else
+static inline int elantech_detect(struct psmouse *psmouse, int set_properties)
+{
+ return -ENOSYS;
+}
+static inline int elantech_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_ELANTECH */
+
+#endif
diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c
new file mode 100644
index 0000000..72cf5e3
--- /dev/null
+++ b/drivers/input/mouse/gpio_mouse.c
@@ -0,0 +1,199 @@
+/*
+ * Driver for simulating a mouse on GPIO lines.
+ *
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input-polldev.h>
+#include <linux/gpio_mouse.h>
+
+#include <asm/gpio.h>
+
+/*
+ * Timer function which is run every scan_ms ms when the device is opened.
+ * The dev input varaible is set to the the input_dev pointer.
+ */
+static void gpio_mouse_scan(struct input_polled_dev *dev)
+{
+ struct gpio_mouse_platform_data *gpio = dev->private;
+ struct input_dev *input = dev->input;
+ int x, y;
+
+ if (gpio->bleft >= 0)
+ input_report_key(input, BTN_LEFT,
+ gpio_get_value(gpio->bleft) ^ gpio->polarity);
+ if (gpio->bmiddle >= 0)
+ input_report_key(input, BTN_MIDDLE,
+ gpio_get_value(gpio->bmiddle) ^ gpio->polarity);
+ if (gpio->bright >= 0)
+ input_report_key(input, BTN_RIGHT,
+ gpio_get_value(gpio->bright) ^ gpio->polarity);
+
+ x = (gpio_get_value(gpio->right) ^ gpio->polarity)
+ - (gpio_get_value(gpio->left) ^ gpio->polarity);
+ y = (gpio_get_value(gpio->down) ^ gpio->polarity)
+ - (gpio_get_value(gpio->up) ^ gpio->polarity);
+
+ input_report_rel(input, REL_X, x);
+ input_report_rel(input, REL_Y, y);
+ input_sync(input);
+}
+
+static int __init gpio_mouse_probe(struct platform_device *pdev)
+{
+ struct gpio_mouse_platform_data *pdata = pdev->dev.platform_data;
+ struct input_polled_dev *input_poll;
+ struct input_dev *input;
+ int pin, i;
+ int error;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ error = -ENXIO;
+ goto out;
+ }
+
+ if (pdata->scan_ms < 0) {
+ dev_err(&pdev->dev, "invalid scan time\n");
+ error = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) {
+ pin = pdata->pins[i];
+
+ if (pin < 0) {
+
+ if (i <= GPIO_MOUSE_PIN_RIGHT) {
+ /* Mouse direction is required. */
+ dev_err(&pdev->dev,
+ "missing GPIO for directions\n");
+ error = -EINVAL;
+ goto out_free_gpios;
+ }
+
+ if (i == GPIO_MOUSE_PIN_BLEFT)
+ dev_dbg(&pdev->dev, "no left button defined\n");
+
+ } else {
+ error = gpio_request(pin, "gpio_mouse");
+ if (error) {
+ dev_err(&pdev->dev, "fail %d pin (%d idx)\n",
+ pin, i);
+ goto out_free_gpios;
+ }
+
+ gpio_direction_input(pin);
+ }
+ }
+
+ input_poll = input_allocate_polled_device();
+ if (!input_poll) {
+ dev_err(&pdev->dev, "not enough memory for input device\n");
+ error = -ENOMEM;
+ goto out_free_gpios;
+ }
+
+ platform_set_drvdata(pdev, input_poll);
+
+ /* set input-polldev handlers */
+ input_poll->private = pdata;
+ input_poll->poll = gpio_mouse_scan;
+ input_poll->poll_interval = pdata->scan_ms;
+
+ input = input_poll->input;
+ input->name = pdev->name;
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = &pdev->dev;
+
+ input_set_capability(input, EV_REL, REL_X);
+ input_set_capability(input, EV_REL, REL_Y);
+ if (pdata->bleft >= 0)
+ input_set_capability(input, EV_KEY, BTN_LEFT);
+ if (pdata->bmiddle >= 0)
+ input_set_capability(input, EV_KEY, BTN_MIDDLE);
+ if (pdata->bright >= 0)
+ input_set_capability(input, EV_KEY, BTN_RIGHT);
+
+ error = input_register_polled_device(input_poll);
+ if (error) {
+ dev_err(&pdev->dev, "could not register input device\n");
+ goto out_free_polldev;
+ }
+
+ dev_dbg(&pdev->dev, "%d ms scan time, buttons: %s%s%s\n",
+ pdata->scan_ms,
+ pdata->bleft < 0 ? "" : "left ",
+ pdata->bmiddle < 0 ? "" : "middle ",
+ pdata->bright < 0 ? "" : "right");
+
+ return 0;
+
+ out_free_polldev:
+ input_free_polled_device(input_poll);
+ platform_set_drvdata(pdev, NULL);
+
+ out_free_gpios:
+ while (--i >= 0) {
+ pin = pdata->pins[i];
+ if (pin)
+ gpio_free(pin);
+ }
+ out:
+ return error;
+}
+
+static int __devexit gpio_mouse_remove(struct platform_device *pdev)
+{
+ struct input_polled_dev *input = platform_get_drvdata(pdev);
+ struct gpio_mouse_platform_data *pdata = input->private;
+ int pin, i;
+
+ input_unregister_polled_device(input);
+ input_free_polled_device(input);
+
+ for (i = 0; i < GPIO_MOUSE_PIN_MAX; i++) {
+ pin = pdata->pins[i];
+ if (pin >= 0)
+ gpio_free(pin);
+ }
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:gpio_mouse");
+
+struct platform_driver gpio_mouse_device_driver = {
+ .remove = __devexit_p(gpio_mouse_remove),
+ .driver = {
+ .name = "gpio_mouse",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init gpio_mouse_init(void)
+{
+ return platform_driver_probe(&gpio_mouse_device_driver,
+ gpio_mouse_probe);
+}
+module_init(gpio_mouse_init);
+
+static void __exit gpio_mouse_exit(void)
+{
+ platform_driver_unregister(&gpio_mouse_device_driver);
+}
+module_exit(gpio_mouse_exit);
+
+MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
+MODULE_DESCRIPTION("GPIO mouse driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c
new file mode 100644
index 0000000..88f04bf
--- /dev/null
+++ b/drivers/input/mouse/hgpk.c
@@ -0,0 +1,477 @@
+/*
+ * OLPC HGPK (XO-1) touchpad PS/2 mouse driver
+ *
+ * Copyright (c) 2006-2008 One Laptop Per Child
+ * Authors:
+ * Zephaniah E. Hull
+ * Andres Salomon <dilinger@debian.org>
+ *
+ * This driver is partly based on the ALPS driver, which is:
+ *
+ * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com>
+ * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@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 spec from ALPS is available from
+ * <http://wiki.laptop.org/go/Touch_Pad/Tablet>. It refers to this
+ * device as HGPK (Hybrid GS, PT, and Keymatrix).
+ *
+ * The earliest versions of the device had simultaneous reporting; that
+ * was removed. After that, the device used the Advanced Mode GS/PT streaming
+ * stuff. That turned out to be too buggy to support, so we've finally
+ * switched to Mouse Mode (which utilizes only the center 1/3 of the touchpad).
+ */
+
+#define DEBUG
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/delay.h>
+#include <asm/olpc.h>
+
+#include "psmouse.h"
+#include "hgpk.h"
+
+static int tpdebug;
+module_param(tpdebug, int, 0644);
+MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG.");
+
+static int recalib_delta = 100;
+module_param(recalib_delta, int, 0644);
+MODULE_PARM_DESC(recalib_delta,
+ "packets containing a delta this large will cause a recalibration.");
+
+/*
+ * When the touchpad gets ultra-sensitive, one can keep their finger 1/2"
+ * above the pad and still have it send packets. This causes a jump cursor
+ * when one places their finger on the pad. We can probably detect the
+ * jump as we see a large deltas (>= 100px). In mouse mode, I've been
+ * unable to even come close to 100px deltas during normal usage, so I think
+ * this threshold is safe. If a large delta occurs, trigger a recalibration.
+ */
+static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y)
+{
+ struct hgpk_data *priv = psmouse->private;
+
+ if (abs(x) > recalib_delta || abs(y) > recalib_delta) {
+ hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n",
+ recalib_delta, x, y);
+ /* My car gets forty rods to the hogshead and that's the
+ * way I likes it! */
+ psmouse_queue_work(psmouse, &priv->recalib_wq,
+ msecs_to_jiffies(1000));
+ }
+}
+
+/*
+ * We have no idea why this particular hardware bug occurs. The touchpad
+ * will randomly start spewing packets without anything touching the
+ * pad. This wouldn't necessarily be bad, but it's indicative of a
+ * severely miscalibrated pad; attempting to use the touchpad while it's
+ * spewing means the cursor will jump all over the place, and act "drunk".
+ *
+ * The packets that are spewed tend to all have deltas between -2 and 2, and
+ * the cursor will move around without really going very far. It will
+ * tend to end up in the same location; if we tally up the changes over
+ * 100 packets, we end up w/ a final delta of close to 0. This happens
+ * pretty regularly when the touchpad is spewing, and is pretty hard to
+ * manually trigger (at least for *my* fingers). So, it makes a perfect
+ * scheme for detecting spews.
+ */
+static void hgpk_spewing_hack(struct psmouse *psmouse,
+ int l, int r, int x, int y)
+{
+ struct hgpk_data *priv = psmouse->private;
+
+ /* ignore button press packets; many in a row could trigger
+ * a false-positive! */
+ if (l || r)
+ return;
+
+ priv->x_tally += x;
+ priv->y_tally += y;
+
+ if (++priv->count > 100) {
+ if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) {
+ hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n",
+ priv->x_tally, priv->y_tally);
+ psmouse_queue_work(psmouse, &priv->recalib_wq,
+ msecs_to_jiffies(1000));
+ }
+ /* reset every 100 packets */
+ priv->count = 0;
+ priv->x_tally = 0;
+ priv->y_tally = 0;
+ }
+}
+
+/*
+ * HGPK Mouse Mode format (standard mouse format, sans middle button)
+ *
+ * byte 0: y-over x-over y-neg x-neg 1 0 swr swl
+ * byte 1: x7 x6 x5 x4 x3 x2 x1 x0
+ * byte 2: y7 y6 y5 y4 y3 y2 y1 y0
+ *
+ * swr/swl are the left/right buttons.
+ * x-neg/y-neg are the x and y delta negative bits
+ * x-over/y-over are the x and y overflow bits
+ */
+static int hgpk_validate_byte(unsigned char *packet)
+{
+ return (packet[0] & 0x0C) != 0x08;
+}
+
+static void hgpk_process_packet(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ unsigned char *packet = psmouse->packet;
+ int x, y, left, right;
+
+ left = packet[0] & 1;
+ right = (packet[0] >> 1) & 1;
+
+ x = packet[1] - ((packet[0] << 4) & 0x100);
+ y = ((packet[0] << 3) & 0x100) - packet[2];
+
+ hgpk_jumpy_hack(psmouse, x, y);
+ hgpk_spewing_hack(psmouse, left, right, x, y);
+
+ if (tpdebug)
+ hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y);
+
+ input_report_key(dev, BTN_LEFT, left);
+ input_report_key(dev, BTN_RIGHT, right);
+
+ input_report_rel(dev, REL_X, x);
+ input_report_rel(dev, REL_Y, y);
+
+ input_sync(dev);
+}
+
+static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse)
+{
+ struct hgpk_data *priv = psmouse->private;
+
+ if (hgpk_validate_byte(psmouse->packet)) {
+ hgpk_dbg(psmouse, "%s: (%d) %02x %02x %02x\n",
+ __func__, psmouse->pktcnt, psmouse->packet[0],
+ psmouse->packet[1], psmouse->packet[2]);
+ return PSMOUSE_BAD_DATA;
+ }
+
+ if (psmouse->pktcnt >= psmouse->pktsize) {
+ hgpk_process_packet(psmouse);
+ return PSMOUSE_FULL_PACKET;
+ }
+
+ if (priv->recalib_window) {
+ if (time_before(jiffies, priv->recalib_window)) {
+ /*
+ * ugh, got a packet inside our recalibration
+ * window, schedule another recalibration.
+ */
+ hgpk_dbg(psmouse,
+ "packet inside calibration window, "
+ "queueing another recalibration\n");
+ psmouse_queue_work(psmouse, &priv->recalib_wq,
+ msecs_to_jiffies(1000));
+ }
+ priv->recalib_window = 0;
+ }
+
+ return PSMOUSE_GOOD_DATA;
+}
+
+static int hgpk_force_recalibrate(struct psmouse *psmouse)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ struct hgpk_data *priv = psmouse->private;
+
+ /* C-series touchpads added the recalibrate command */
+ if (psmouse->model < HGPK_MODEL_C)
+ return 0;
+
+ /* we don't want to race with the irq handler, nor with resyncs */
+ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+ /* start by resetting the device */
+ psmouse_reset(psmouse);
+
+ /* send the recalibrate request */
+ if (ps2_command(ps2dev, NULL, 0xf5) ||
+ ps2_command(ps2dev, NULL, 0xf5) ||
+ ps2_command(ps2dev, NULL, 0xe6) ||
+ ps2_command(ps2dev, NULL, 0xf5)) {
+ return -1;
+ }
+
+ /* according to ALPS, 150mS is required for recalibration */
+ msleep(150);
+
+ /* XXX: If a finger is down during this delay, recalibration will
+ * detect capacitance incorrectly. This is a hardware bug, and
+ * we don't have a good way to deal with it. The 2s window stuff
+ * (below) is our best option for now.
+ */
+
+ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
+ return -1;
+
+ psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+
+ /* After we recalibrate, we shouldn't get any packets for 2s. If
+ * we do, it's likely that someone's finger was on the touchpad.
+ * If someone's finger *was* on the touchpad, it's probably
+ * miscalibrated. So, we should schedule another recalibration
+ */
+ priv->recalib_window = jiffies + msecs_to_jiffies(2000);
+
+ return 0;
+}
+
+/*
+ * This kills power to the touchpad; according to ALPS, current consumption
+ * goes down to 50uA after running this. To turn power back on, we drive
+ * MS-DAT low.
+ */
+static int hgpk_toggle_power(struct psmouse *psmouse, int enable)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ int timeo;
+
+ /* Added on D-series touchpads */
+ if (psmouse->model < HGPK_MODEL_D)
+ return 0;
+
+ if (enable) {
+ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+ /*
+ * Sending a byte will drive MS-DAT low; this will wake up
+ * the controller. Once we get an ACK back from it, it
+ * means we can continue with the touchpad re-init. ALPS
+ * tells us that 1s should be long enough, so set that as
+ * the upper bound.
+ */
+ for (timeo = 20; timeo > 0; timeo--) {
+ if (!ps2_sendbyte(&psmouse->ps2dev,
+ PSMOUSE_CMD_DISABLE, 20))
+ break;
+ msleep(50);
+ }
+
+ psmouse_reset(psmouse);
+
+ /* should be all set, enable the touchpad */
+ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+ psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+
+ } else {
+ hgpk_dbg(psmouse, "Powering off touchpad.\n");
+ psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+
+ if (ps2_command(ps2dev, NULL, 0xec) ||
+ ps2_command(ps2dev, NULL, 0xec) ||
+ ps2_command(ps2dev, NULL, 0xea)) {
+ return -1;
+ }
+
+ /* probably won't see an ACK, the touchpad will be off */
+ ps2_sendbyte(&psmouse->ps2dev, 0xec, 20);
+ }
+
+ return 0;
+}
+
+static int hgpk_poll(struct psmouse *psmouse)
+{
+ /* We can't poll, so always return failure. */
+ return -1;
+}
+
+static int hgpk_reconnect(struct psmouse *psmouse)
+{
+ /* During suspend/resume the ps2 rails remain powered. We don't want
+ * to do a reset because it's flush data out of buffers; however,
+ * earlier prototypes (B1) had some brokenness that required a reset. */
+ if (olpc_board_at_least(olpc_board(0xb2)))
+ if (psmouse->ps2dev.serio->dev.power.power_state.event !=
+ PM_EVENT_ON)
+ return 0;
+
+ psmouse_reset(psmouse);
+
+ return 0;
+}
+
+static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf)
+{
+ struct hgpk_data *priv = psmouse->private;
+
+ return sprintf(buf, "%d\n", priv->powered);
+}
+
+static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,
+ const char *buf, size_t count)
+{
+ struct hgpk_data *priv = psmouse->private;
+ unsigned long value;
+ int err;
+
+ err = strict_strtoul(buf, 10, &value);
+ if (err || value > 1)
+ return -EINVAL;
+
+ if (value != priv->powered) {
+ /*
+ * hgpk_toggle_power will deal w/ state so
+ * we're not racing w/ irq
+ */
+ err = hgpk_toggle_power(psmouse, value);
+ if (!err)
+ priv->powered = value;
+ }
+
+ return err ? err : count;
+}
+
+__PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL,
+ hgpk_show_powered, hgpk_set_powered, 0);
+
+static void hgpk_disconnect(struct psmouse *psmouse)
+{
+ struct hgpk_data *priv = psmouse->private;
+
+ device_remove_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_powered.dattr);
+ psmouse_reset(psmouse);
+ kfree(priv);
+}
+
+static void hgpk_recalib_work(struct work_struct *work)
+{
+ struct delayed_work *w = container_of(work, struct delayed_work, work);
+ struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq);
+ struct psmouse *psmouse = priv->psmouse;
+
+ hgpk_dbg(psmouse, "recalibrating touchpad..\n");
+
+ if (hgpk_force_recalibrate(psmouse))
+ hgpk_err(psmouse, "recalibration failed!\n");
+}
+
+static int hgpk_register(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ int err;
+
+ /* unset the things that psmouse-base sets which we don't have */
+ __clear_bit(BTN_MIDDLE, dev->keybit);
+
+ /* set the things we do have */
+ __set_bit(EV_KEY, dev->evbit);
+ __set_bit(EV_REL, dev->evbit);
+
+ __set_bit(REL_X, dev->relbit);
+ __set_bit(REL_Y, dev->relbit);
+
+ __set_bit(BTN_LEFT, dev->keybit);
+ __set_bit(BTN_RIGHT, dev->keybit);
+
+ /* register handlers */
+ psmouse->protocol_handler = hgpk_process_byte;
+ psmouse->poll = hgpk_poll;
+ psmouse->disconnect = hgpk_disconnect;
+ psmouse->reconnect = hgpk_reconnect;
+ psmouse->pktsize = 3;
+
+ /* Disable the idle resync. */
+ psmouse->resync_time = 0;
+ /* Reset after a lot of bad bytes. */
+ psmouse->resetafter = 1024;
+
+ err = device_create_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_powered.dattr);
+ if (err)
+ hgpk_err(psmouse, "Failed to create sysfs attribute\n");
+
+ return err;
+}
+
+int hgpk_init(struct psmouse *psmouse)
+{
+ struct hgpk_data *priv;
+ int err = -ENOMEM;
+
+ priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL);
+ if (!priv)
+ goto alloc_fail;
+
+ psmouse->private = priv;
+ priv->psmouse = psmouse;
+ priv->powered = 1;
+ INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work);
+
+ err = psmouse_reset(psmouse);
+ if (err)
+ goto init_fail;
+
+ err = hgpk_register(psmouse);
+ if (err)
+ goto init_fail;
+
+ return 0;
+
+init_fail:
+ kfree(priv);
+alloc_fail:
+ return err;
+}
+
+static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[3];
+
+ /* E7, E7, E7, E9 gets us a 3 byte identifier */
+ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) ||
+ ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) {
+ return -EIO;
+ }
+
+ hgpk_dbg(psmouse, "ID: %02x %02x %02x", param[0], param[1], param[2]);
+
+ /* HGPK signature: 0x67, 0x00, 0x<model> */
+ if (param[0] != 0x67 || param[1] != 0x00)
+ return -ENODEV;
+
+ hgpk_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]);
+
+ return param[2];
+}
+
+int hgpk_detect(struct psmouse *psmouse, int set_properties)
+{
+ int version;
+
+ version = hgpk_get_model(psmouse);
+ if (version < 0)
+ return version;
+
+ if (set_properties) {
+ psmouse->vendor = "ALPS";
+ psmouse->name = "HGPK";
+ psmouse->model = version;
+ }
+
+ return 0;
+}
diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h
new file mode 100644
index 0000000..a4b2a96
--- /dev/null
+++ b/drivers/input/mouse/hgpk.h
@@ -0,0 +1,49 @@
+/*
+ * OLPC HGPK (XO-1) touchpad PS/2 mouse driver
+ */
+
+#ifndef _HGPK_H
+#define _HGPK_H
+
+enum hgpk_model_t {
+ HGPK_MODEL_PREA = 0x0a, /* pre-B1s */
+ HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */
+ HGPK_MODEL_B = 0x28, /* B2s, has capacitance issues */
+ HGPK_MODEL_C = 0x3c,
+ HGPK_MODEL_D = 0x50, /* C1, mass production */
+};
+
+struct hgpk_data {
+ struct psmouse *psmouse;
+ int powered;
+ int count, x_tally, y_tally; /* hardware workaround stuff */
+ unsigned long recalib_window;
+ struct delayed_work recalib_wq;
+};
+
+#define hgpk_dbg(psmouse, format, arg...) \
+ dev_dbg(&(psmouse)->ps2dev.serio->dev, format, ## arg)
+#define hgpk_err(psmouse, format, arg...) \
+ dev_err(&(psmouse)->ps2dev.serio->dev, format, ## arg)
+#define hgpk_info(psmouse, format, arg...) \
+ dev_info(&(psmouse)->ps2dev.serio->dev, format, ## arg)
+#define hgpk_warn(psmouse, format, arg...) \
+ dev_warn(&(psmouse)->ps2dev.serio->dev, format, ## arg)
+#define hgpk_notice(psmouse, format, arg...) \
+ dev_notice(&(psmouse)->ps2dev.serio->dev, format, ## arg)
+
+#ifdef CONFIG_MOUSE_PS2_OLPC
+int hgpk_detect(struct psmouse *psmouse, int set_properties);
+int hgpk_init(struct psmouse *psmouse);
+#else
+static inline int hgpk_detect(struct psmouse *psmouse, int set_properties)
+{
+ return -ENODEV;
+}
+static inline int hgpk_init(struct psmouse *psmouse)
+{
+ return -ENODEV;
+}
+#endif
+
+#endif
diff --git a/drivers/input/mouse/hil_ptr.c b/drivers/input/mouse/hil_ptr.c
new file mode 100644
index 0000000..e532c48
--- /dev/null
+++ b/drivers/input/mouse/hil_ptr.c
@@ -0,0 +1,447 @@
+/*
+ * Generic linux-input device driver for axis-bearing devices
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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
+ *
+ * References:
+ * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A
+ *
+ */
+
+#include <linux/hil.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+
+#define PREFIX "HIL PTR: "
+#define HIL_GENERIC_NAME "HIL pointer device"
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+
+#define TABLET_SIMULATES_MOUSE /* allow tablet to be used as mouse */
+#undef TABLET_AUTOADJUST /* auto-adjust valid tablet ranges */
+
+
+#define HIL_PTR_MAX_LENGTH 16
+
+struct hil_ptr {
+ struct input_dev *dev;
+ struct serio *serio;
+
+ /* Input buffer and index for packets from HIL bus. */
+ hil_packet data[HIL_PTR_MAX_LENGTH];
+ int idx4; /* four counts per packet */
+
+ /* Raw device info records from HIL bus, see hil.h for fields. */
+ char idd[HIL_PTR_MAX_LENGTH]; /* DID byte and IDD record */
+ char rsc[HIL_PTR_MAX_LENGTH]; /* RSC record */
+ char exd[HIL_PTR_MAX_LENGTH]; /* EXD record */
+ char rnm[HIL_PTR_MAX_LENGTH + 1]; /* RNM record + NULL term. */
+
+ /* Extra device details not contained in struct input_dev. */
+ unsigned int nbtn, naxes;
+ unsigned int btnmap[7];
+
+ /* Something to sleep around with. */
+ struct semaphore sem;
+};
+
+/* Process a complete packet after transfer from the HIL */
+static void hil_ptr_process_record(struct hil_ptr *ptr)
+{
+ struct input_dev *dev = ptr->dev;
+ hil_packet *data = ptr->data;
+ hil_packet p;
+ int idx, i, cnt, laxis;
+ int ax16, absdev;
+
+ idx = ptr->idx4/4;
+ p = data[idx - 1];
+
+ if ((p & ~HIL_CMDCT_POL) ==
+ (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
+ goto report;
+ if ((p & ~HIL_CMDCT_RPL) ==
+ (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL))
+ goto report;
+
+ /* Not a poll response. See if we are loading config records. */
+ switch (p & HIL_PKT_DATA_MASK) {
+ case HIL_CMD_IDD:
+ for (i = 0; i < idx; i++)
+ ptr->idd[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_PTR_MAX_LENGTH; i++)
+ ptr->idd[i] = 0;
+ break;
+
+ case HIL_CMD_RSC:
+ for (i = 0; i < idx; i++)
+ ptr->rsc[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_PTR_MAX_LENGTH; i++)
+ ptr->rsc[i] = 0;
+ break;
+
+ case HIL_CMD_EXD:
+ for (i = 0; i < idx; i++)
+ ptr->exd[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_PTR_MAX_LENGTH; i++)
+ ptr->exd[i] = 0;
+ break;
+
+ case HIL_CMD_RNM:
+ for (i = 0; i < idx; i++)
+ ptr->rnm[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_PTR_MAX_LENGTH + 1; i++)
+ ptr->rnm[i] = 0;
+ break;
+
+ default:
+ /* These occur when device isn't present */
+ if (p == (HIL_ERR_INT | HIL_PKT_CMD))
+ break;
+ /* Anything else we'd like to know about. */
+ printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
+ break;
+ }
+ goto out;
+
+ report:
+ if ((p & HIL_CMDCT_POL) != idx - 1) {
+ printk(KERN_WARNING PREFIX
+ "Malformed poll packet %x (idx = %i)\n", p, idx);
+ goto out;
+ }
+
+ i = (ptr->data[0] & HIL_POL_AXIS_ALT) ? 3 : 0;
+ laxis = ptr->data[0] & HIL_POL_NUM_AXES_MASK;
+ laxis += i;
+
+ ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */
+ absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS;
+
+ for (cnt = 1; i < laxis; i++) {
+ unsigned int lo,hi,val;
+ lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK;
+ hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0;
+ if (absdev) {
+ val = lo + (hi<<8);
+#ifdef TABLET_AUTOADJUST
+ if (val < dev->absmin[ABS_X + i])
+ dev->absmin[ABS_X + i] = val;
+ if (val > dev->absmax[ABS_X + i])
+ dev->absmax[ABS_X + i] = val;
+#endif
+ if (i%3) val = dev->absmax[ABS_X + i] - val;
+ input_report_abs(dev, ABS_X + i, val);
+ } else {
+ val = (int) (((int8_t)lo) | ((int8_t)hi<<8));
+ if (i%3)
+ val *= -1;
+ input_report_rel(dev, REL_X + i, val);
+ }
+ }
+
+ while (cnt < idx - 1) {
+ unsigned int btn;
+ int up;
+ btn = ptr->data[cnt++];
+ up = btn & 1;
+ btn &= 0xfe;
+ if (btn == 0x8e)
+ continue; /* TODO: proximity == touch? */
+ else
+ if ((btn > 0x8c) || (btn < 0x80))
+ continue;
+ btn = (btn - 0x80) >> 1;
+ btn = ptr->btnmap[btn];
+ input_report_key(dev, btn, !up);
+ }
+ input_sync(dev);
+ out:
+ ptr->idx4 = 0;
+ up(&ptr->sem);
+}
+
+static void hil_ptr_process_err(struct hil_ptr *ptr)
+{
+ printk(KERN_WARNING PREFIX "errored HIL packet\n");
+ ptr->idx4 = 0;
+ up(&ptr->sem);
+}
+
+static irqreturn_t hil_ptr_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct hil_ptr *ptr;
+ hil_packet packet;
+ int idx;
+
+ ptr = serio_get_drvdata(serio);
+ BUG_ON(ptr == NULL);
+
+ if (ptr->idx4 >= (HIL_PTR_MAX_LENGTH * sizeof(hil_packet))) {
+ hil_ptr_process_err(ptr);
+ return IRQ_HANDLED;
+ }
+ idx = ptr->idx4/4;
+ if (!(ptr->idx4 % 4))
+ ptr->data[idx] = 0;
+ packet = ptr->data[idx];
+ packet |= ((hil_packet)data) << ((3 - (ptr->idx4 % 4)) * 8);
+ ptr->data[idx] = packet;
+
+ /* Records of N 4-byte hil_packets must terminate with a command. */
+ if ((++(ptr->idx4)) % 4)
+ return IRQ_HANDLED;
+ if ((packet & 0xffff0000) != HIL_ERR_INT) {
+ hil_ptr_process_err(ptr);
+ return IRQ_HANDLED;
+ }
+ if (packet & HIL_PKT_CMD)
+ hil_ptr_process_record(ptr);
+
+ return IRQ_HANDLED;
+}
+
+static void hil_ptr_disconnect(struct serio *serio)
+{
+ struct hil_ptr *ptr;
+
+ ptr = serio_get_drvdata(serio);
+ BUG_ON(ptr == NULL);
+
+ serio_close(serio);
+ input_unregister_device(ptr->dev);
+ kfree(ptr);
+}
+
+static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
+{
+ struct hil_ptr *ptr;
+ const char *txt;
+ unsigned int i, naxsets, btntype;
+ uint8_t did, *idd;
+ int error;
+
+ ptr = kzalloc(sizeof(struct hil_ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ ptr->dev = input_allocate_device();
+ if (!ptr->dev) {
+ error = -ENOMEM;
+ goto bail0;
+ }
+
+ error = serio_open(serio, driver);
+ if (error)
+ goto bail1;
+
+ serio_set_drvdata(serio, ptr);
+ ptr->serio = serio;
+
+ init_MUTEX_LOCKED(&ptr->sem);
+
+ /* Get device info. MLC driver supplies devid/status/etc. */
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_IDD);
+ down(&ptr->sem);
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_RSC);
+ down(&ptr->sem);
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_RNM);
+ down(&ptr->sem);
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_EXD);
+ down(&ptr->sem);
+
+ up(&ptr->sem);
+
+ did = ptr->idd[0];
+ idd = ptr->idd + 1;
+ txt = "unknown";
+
+ if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
+ ptr->dev->evbit[0] = BIT_MASK(EV_REL);
+ txt = "relative";
+ }
+
+ if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_ABS) {
+ ptr->dev->evbit[0] = BIT_MASK(EV_ABS);
+ txt = "absolute";
+ }
+
+ if (!ptr->dev->evbit[0]) {
+ error = -ENODEV;
+ goto bail2;
+ }
+
+ ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd);
+ if (ptr->nbtn)
+ ptr->dev->evbit[0] |= BIT_MASK(EV_KEY);
+
+ naxsets = HIL_IDD_NUM_AXSETS(*idd);
+ ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd);
+
+ printk(KERN_INFO PREFIX "HIL pointer device found (did: 0x%02x, axis: %s)\n",
+ did, txt);
+ printk(KERN_INFO PREFIX "HIL pointer has %i buttons and %i sets of %i axes\n",
+ ptr->nbtn, naxsets, ptr->naxes);
+
+ btntype = BTN_MISC;
+ if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET)
+#ifdef TABLET_SIMULATES_MOUSE
+ btntype = BTN_TOUCH;
+#else
+ btntype = BTN_DIGI;
+#endif
+ if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN)
+ btntype = BTN_TOUCH;
+
+ if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE)
+ btntype = BTN_MOUSE;
+
+ for (i = 0; i < ptr->nbtn; i++) {
+ set_bit(btntype | i, ptr->dev->keybit);
+ ptr->btnmap[i] = btntype | i;
+ }
+
+ if (btntype == BTN_MOUSE) {
+ /* Swap buttons 2 and 3 */
+ ptr->btnmap[1] = BTN_MIDDLE;
+ ptr->btnmap[2] = BTN_RIGHT;
+ }
+
+ if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
+ for (i = 0; i < ptr->naxes; i++)
+ set_bit(REL_X + i, ptr->dev->relbit);
+ for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++)
+ set_bit(REL_X + i, ptr->dev->relbit);
+ } else {
+ for (i = 0; i < ptr->naxes; i++) {
+ set_bit(ABS_X + i, ptr->dev->absbit);
+ ptr->dev->absmin[ABS_X + i] = 0;
+ ptr->dev->absmax[ABS_X + i] =
+ HIL_IDD_AXIS_MAX((ptr->idd + 1), i);
+ }
+ for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) {
+ set_bit(ABS_X + i, ptr->dev->absbit);
+ ptr->dev->absmin[ABS_X + i] = 0;
+ ptr->dev->absmax[ABS_X + i] =
+ HIL_IDD_AXIS_MAX((ptr->idd + 1), (i - 3));
+ }
+#ifdef TABLET_AUTOADJUST
+ for (i = 0; i < ABS_MAX; i++) {
+ int diff = ptr->dev->absmax[ABS_X + i] / 10;
+ ptr->dev->absmin[ABS_X + i] += diff;
+ ptr->dev->absmax[ABS_X + i] -= diff;
+ }
+#endif
+ }
+
+ ptr->dev->name = strlen(ptr->rnm) ? ptr->rnm : HIL_GENERIC_NAME;
+
+ ptr->dev->id.bustype = BUS_HIL;
+ ptr->dev->id.vendor = PCI_VENDOR_ID_HP;
+ ptr->dev->id.product = 0x0001; /* TODO: get from ptr->rsc */
+ ptr->dev->id.version = 0x0100; /* TODO: get from ptr->rsc */
+ ptr->dev->dev.parent = &serio->dev;
+
+ error = input_register_device(ptr->dev);
+ if (error) {
+ printk(KERN_INFO PREFIX "Unable to register input device\n");
+ goto bail2;
+ }
+
+ printk(KERN_INFO "input: %s (%s), ID: %d\n",
+ ptr->dev->name,
+ (btntype == BTN_MOUSE) ? "HIL mouse":"HIL tablet or touchpad",
+ did);
+
+ return 0;
+
+ bail2:
+ serio_close(serio);
+ bail1:
+ input_free_device(ptr->dev);
+ bail0:
+ kfree(ptr);
+ serio_set_drvdata(serio, NULL);
+ return error;
+}
+
+static struct serio_device_id hil_ptr_ids[] = {
+ {
+ .type = SERIO_HIL_MLC,
+ .proto = SERIO_HIL,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+static struct serio_driver hil_ptr_serio_driver = {
+ .driver = {
+ .name = "hil_ptr",
+ },
+ .description = "HP HIL mouse/tablet driver",
+ .id_table = hil_ptr_ids,
+ .connect = hil_ptr_connect,
+ .disconnect = hil_ptr_disconnect,
+ .interrupt = hil_ptr_interrupt
+};
+
+static int __init hil_ptr_init(void)
+{
+ return serio_register_driver(&hil_ptr_serio_driver);
+}
+
+static void __exit hil_ptr_exit(void)
+{
+ serio_unregister_driver(&hil_ptr_serio_driver);
+}
+
+module_init(hil_ptr_init);
+module_exit(hil_ptr_exit);
diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c
new file mode 100644
index 0000000..3827a22
--- /dev/null
+++ b/drivers/input/mouse/inport.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Teemu Rantanen Derrick Cole
+ * Peter Cervasio Christoph Niemann
+ * Philip Blundell Russell King
+ * Bob Harris
+ */
+
+/*
+ * Inport (ATI XL and Microsoft) busmouse driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Inport (ATI XL and Microsoft) busmouse driver");
+MODULE_LICENSE("GPL");
+
+#define INPORT_BASE 0x23c
+#define INPORT_EXTENT 4
+
+#define INPORT_CONTROL_PORT INPORT_BASE + 0
+#define INPORT_DATA_PORT INPORT_BASE + 1
+#define INPORT_SIGNATURE_PORT INPORT_BASE + 2
+
+#define INPORT_REG_BTNS 0x00
+#define INPORT_REG_X 0x01
+#define INPORT_REG_Y 0x02
+#define INPORT_REG_MODE 0x07
+#define INPORT_RESET 0x80
+
+#ifdef CONFIG_MOUSE_ATIXL
+#define INPORT_NAME "ATI XL Mouse"
+#define INPORT_VENDOR 0x0002
+#define INPORT_SPEED_30HZ 0x01
+#define INPORT_SPEED_50HZ 0x02
+#define INPORT_SPEED_100HZ 0x03
+#define INPORT_SPEED_200HZ 0x04
+#define INPORT_MODE_BASE INPORT_SPEED_100HZ
+#define INPORT_MODE_IRQ 0x08
+#else
+#define INPORT_NAME "Microsoft InPort Mouse"
+#define INPORT_VENDOR 0x0001
+#define INPORT_MODE_BASE 0x10
+#define INPORT_MODE_IRQ 0x01
+#endif
+#define INPORT_MODE_HOLD 0x20
+
+#define INPORT_IRQ 5
+
+static int inport_irq = INPORT_IRQ;
+module_param_named(irq, inport_irq, uint, 0);
+MODULE_PARM_DESC(irq, "IRQ number (5=default)");
+
+static struct input_dev *inport_dev;
+
+static irqreturn_t inport_interrupt(int irq, void *dev_id)
+{
+ unsigned char buttons;
+
+ outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+ outb(INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+ outb(INPORT_REG_X, INPORT_CONTROL_PORT);
+ input_report_rel(inport_dev, REL_X, inb(INPORT_DATA_PORT));
+
+ outb(INPORT_REG_Y, INPORT_CONTROL_PORT);
+ input_report_rel(inport_dev, REL_Y, inb(INPORT_DATA_PORT));
+
+ outb(INPORT_REG_BTNS, INPORT_CONTROL_PORT);
+ buttons = inb(INPORT_DATA_PORT);
+
+ input_report_key(inport_dev, BTN_MIDDLE, buttons & 1);
+ input_report_key(inport_dev, BTN_LEFT, buttons & 2);
+ input_report_key(inport_dev, BTN_RIGHT, buttons & 4);
+
+ outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+ outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+ input_sync(inport_dev);
+ return IRQ_HANDLED;
+}
+
+static int inport_open(struct input_dev *dev)
+{
+ if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL))
+ return -EBUSY;
+ outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+ outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+ return 0;
+}
+
+static void inport_close(struct input_dev *dev)
+{
+ outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+ outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
+ free_irq(inport_irq, NULL);
+}
+
+static int __init inport_init(void)
+{
+ unsigned char a, b, c;
+ int err;
+
+ if (!request_region(INPORT_BASE, INPORT_EXTENT, "inport")) {
+ printk(KERN_ERR "inport.c: Can't allocate ports at %#x\n", INPORT_BASE);
+ return -EBUSY;
+ }
+
+ a = inb(INPORT_SIGNATURE_PORT);
+ b = inb(INPORT_SIGNATURE_PORT);
+ c = inb(INPORT_SIGNATURE_PORT);
+ if (a == b || a != c) {
+ printk(KERN_INFO "inport.c: Didn't find InPort mouse at %#x\n", INPORT_BASE);
+ err = -ENODEV;
+ goto err_release_region;
+ }
+
+ inport_dev = input_allocate_device();
+ if (!inport_dev) {
+ printk(KERN_ERR "inport.c: Not enough memory for input device\n");
+ err = -ENOMEM;
+ goto err_release_region;
+ }
+
+ inport_dev->name = INPORT_NAME;
+ inport_dev->phys = "isa023c/input0";
+ inport_dev->id.bustype = BUS_ISA;
+ inport_dev->id.vendor = INPORT_VENDOR;
+ inport_dev->id.product = 0x0001;
+ inport_dev->id.version = 0x0100;
+
+ inport_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ inport_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+ inport_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+ inport_dev->open = inport_open;
+ inport_dev->close = inport_close;
+
+ outb(INPORT_RESET, INPORT_CONTROL_PORT);
+ outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+ outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+ err = input_register_device(inport_dev);
+ if (err)
+ goto err_free_dev;
+
+ return 0;
+
+ err_free_dev:
+ input_free_device(inport_dev);
+ err_release_region:
+ release_region(INPORT_BASE, INPORT_EXTENT);
+
+ return err;
+}
+
+static void __exit inport_exit(void)
+{
+ input_unregister_device(inport_dev);
+ release_region(INPORT_BASE, INPORT_EXTENT);
+}
+
+module_init(inport_init);
+module_exit(inport_exit);
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c
new file mode 100644
index 0000000..df81b0a
--- /dev/null
+++ b/drivers/input/mouse/lifebook.c
@@ -0,0 +1,332 @@
+/*
+ * Fujitsu B-series Lifebook PS/2 TouchScreen driver
+ *
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2005 Kenan Esau <kenan.esau@conan.de>
+ *
+ * TouchScreen detection, absolute mode setting and packet layout is taken from
+ * Harald Hoyer's description of the device.
+ *
+ * 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.
+ */
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include <linux/dmi.h>
+
+#include "psmouse.h"
+#include "lifebook.h"
+
+struct lifebook_data {
+ struct input_dev *dev2; /* Relative device */
+ char phys[32];
+};
+
+static const char *desired_serio_phys;
+
+static int lifebook_set_serio_phys(const struct dmi_system_id *d)
+{
+ desired_serio_phys = d->driver_data;
+ return 0;
+}
+
+static unsigned char lifebook_use_6byte_proto;
+
+static int lifebook_set_6byte_proto(const struct dmi_system_id *d)
+{
+ lifebook_use_6byte_proto = 1;
+ return 0;
+}
+
+static const struct dmi_system_id lifebook_dmi_table[] = {
+ {
+ .ident = "FLORA-ie 55mi",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "FLORA-ie 55mi"),
+ },
+ },
+ {
+ .ident = "LifeBook B",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"),
+ },
+ },
+ {
+ .ident = "Lifebook B",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"),
+ },
+ },
+ {
+ .ident = "Lifebook B213x/B2150",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"),
+ },
+ },
+ {
+ .ident = "Zephyr",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"),
+ },
+ },
+ {
+ .ident = "CF-18",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"),
+ },
+ .callback = lifebook_set_serio_phys,
+ .driver_data = "isa0060/serio3",
+ },
+ {
+ .ident = "Panasonic CF-28",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CF-28"),
+ },
+ .callback = lifebook_set_6byte_proto,
+ },
+ {
+ .ident = "Panasonic CF-29",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
+ },
+ .callback = lifebook_set_6byte_proto,
+ },
+ {
+ .ident = "CF-72",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"),
+ },
+ .callback = lifebook_set_serio_phys,
+ .driver_data = "isa0060/serio3",
+ },
+ {
+ .ident = "Lifebook B142",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"),
+ },
+ },
+ { }
+};
+
+static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
+{
+ struct lifebook_data *priv = psmouse->private;
+ struct input_dev *dev1 = psmouse->dev;
+ struct input_dev *dev2 = priv ? priv->dev2 : NULL;
+ unsigned char *packet = psmouse->packet;
+ int relative_packet = packet[0] & 0x08;
+
+ if (relative_packet || !lifebook_use_6byte_proto) {
+ if (psmouse->pktcnt != 3)
+ return PSMOUSE_GOOD_DATA;
+ } else {
+ switch (psmouse->pktcnt) {
+ case 1:
+ return (packet[0] & 0xf8) == 0x00 ?
+ PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+ case 2:
+ return PSMOUSE_GOOD_DATA;
+ case 3:
+ return ((packet[2] & 0x30) << 2) == (packet[2] & 0xc0) ?
+ PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+ case 4:
+ return (packet[3] & 0xf8) == 0xc0 ?
+ PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+ case 5:
+ return (packet[4] & 0xc0) == (packet[2] & 0xc0) ?
+ PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+ case 6:
+ if (((packet[5] & 0x30) << 2) != (packet[5] & 0xc0))
+ return PSMOUSE_BAD_DATA;
+ if ((packet[5] & 0xc0) != (packet[1] & 0xc0))
+ return PSMOUSE_BAD_DATA;
+ break; /* report data */
+ }
+ }
+
+ if (relative_packet) {
+ if (!dev2)
+ printk(KERN_WARNING "lifebook.c: got relative packet "
+ "but no relative device set up\n");
+ } else if (lifebook_use_6byte_proto) {
+ input_report_abs(dev1, ABS_X,
+ ((packet[1] & 0x3f) << 6) | (packet[2] & 0x3f));
+ input_report_abs(dev1, ABS_Y,
+ 4096 - (((packet[4] & 0x3f) << 6) | (packet[5] & 0x3f)));
+ } else {
+ input_report_abs(dev1, ABS_X,
+ (packet[1] | ((packet[0] & 0x30) << 4)));
+ input_report_abs(dev1, ABS_Y,
+ 1024 - (packet[2] | ((packet[0] & 0xC0) << 2)));
+ }
+
+ input_report_key(dev1, BTN_TOUCH, packet[0] & 0x04);
+ input_sync(dev1);
+
+ if (dev2) {
+ if (relative_packet) {
+ input_report_rel(dev2, REL_X,
+ ((packet[0] & 0x10) ? packet[1] - 256 : packet[1]));
+ input_report_rel(dev2, REL_Y,
+ -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2]));
+ }
+ input_report_key(dev2, BTN_LEFT, packet[0] & 0x01);
+ input_report_key(dev2, BTN_RIGHT, packet[0] & 0x02);
+ input_sync(dev2);
+ }
+
+ return PSMOUSE_FULL_PACKET;
+}
+
+static int lifebook_absolute_mode(struct psmouse *psmouse)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param;
+
+ if (psmouse_reset(psmouse))
+ return -1;
+
+ /*
+ Enable absolute output -- ps2_command fails always but if
+ you leave this call out the touchsreen will never send
+ absolute coordinates
+ */
+ param = lifebook_use_6byte_proto ? 0x08 : 0x07;
+ ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
+
+ return 0;
+}
+
+static void lifebook_relative_mode(struct psmouse *psmouse)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param = 0x06;
+
+ ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
+}
+
+static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution)
+{
+ static const unsigned char params[] = { 0, 1, 2, 2, 3 };
+ unsigned char p;
+
+ if (resolution == 0 || resolution > 400)
+ resolution = 400;
+
+ p = params[resolution / 100];
+ ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES);
+ psmouse->resolution = 50 << p;
+}
+
+static void lifebook_disconnect(struct psmouse *psmouse)
+{
+ struct lifebook_data *priv = psmouse->private;
+
+ psmouse_reset(psmouse);
+ if (priv) {
+ input_unregister_device(priv->dev2);
+ kfree(priv);
+ }
+ psmouse->private = NULL;
+}
+
+int lifebook_detect(struct psmouse *psmouse, int set_properties)
+{
+ if (!dmi_check_system(lifebook_dmi_table))
+ return -1;
+
+ if (desired_serio_phys &&
+ strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys))
+ return -1;
+
+ if (set_properties) {
+ psmouse->vendor = "Fujitsu";
+ psmouse->name = "Lifebook TouchScreen";
+ }
+
+ return 0;
+}
+
+static int lifebook_create_relative_device(struct psmouse *psmouse)
+{
+ struct input_dev *dev2;
+ struct lifebook_data *priv;
+ int error = -ENOMEM;
+
+ priv = kzalloc(sizeof(struct lifebook_data), GFP_KERNEL);
+ dev2 = input_allocate_device();
+ if (!priv || !dev2)
+ goto err_out;
+
+ priv->dev2 = dev2;
+ snprintf(priv->phys, sizeof(priv->phys),
+ "%s/input1", psmouse->ps2dev.serio->phys);
+
+ dev2->phys = priv->phys;
+ dev2->name = "PS/2 Touchpad";
+ dev2->id.bustype = BUS_I8042;
+ dev2->id.vendor = 0x0002;
+ dev2->id.product = PSMOUSE_LIFEBOOK;
+ dev2->id.version = 0x0000;
+ dev2->dev.parent = &psmouse->ps2dev.serio->dev;
+
+ dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+ dev2->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_RIGHT);
+
+ error = input_register_device(priv->dev2);
+ if (error)
+ goto err_out;
+
+ psmouse->private = priv;
+ return 0;
+
+ err_out:
+ input_free_device(dev2);
+ kfree(priv);
+ return error;
+}
+
+int lifebook_init(struct psmouse *psmouse)
+{
+ struct input_dev *dev1 = psmouse->dev;
+ int max_coord = lifebook_use_6byte_proto ? 4096 : 1024;
+
+ if (lifebook_absolute_mode(psmouse))
+ return -1;
+
+ dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+ dev1->relbit[0] = 0;
+ dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0);
+ input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0);
+
+ if (!desired_serio_phys) {
+ if (lifebook_create_relative_device(psmouse)) {
+ lifebook_relative_mode(psmouse);
+ return -1;
+ }
+ }
+
+ psmouse->protocol_handler = lifebook_process_byte;
+ psmouse->set_resolution = lifebook_set_resolution;
+ psmouse->disconnect = lifebook_disconnect;
+ psmouse->reconnect = lifebook_absolute_mode;
+
+ psmouse->model = lifebook_use_6byte_proto ? 6 : 3;
+
+ /*
+ * Use packet size = 3 even when using 6-byte protocol because
+ * that's what POLL will return on Lifebooks (according to spec).
+ */
+ psmouse->pktsize = 3;
+
+ return 0;
+}
+
diff --git a/drivers/input/mouse/lifebook.h b/drivers/input/mouse/lifebook.h
new file mode 100644
index 0000000..c1647cf
--- /dev/null
+++ b/drivers/input/mouse/lifebook.h
@@ -0,0 +1,28 @@
+/*
+ * Fujitsu B-series Lifebook PS/2 TouchScreen driver
+ *
+ * Copyright (c) 2005 Vojtech Pavlik
+ *
+ * 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 _LIFEBOOK_H
+#define _LIFEBOOK_H
+
+#ifdef CONFIG_MOUSE_PS2_LIFEBOOK
+int lifebook_detect(struct psmouse *psmouse, int set_properties);
+int lifebook_init(struct psmouse *psmouse);
+#else
+inline int lifebook_detect(struct psmouse *psmouse, int set_properties)
+{
+ return -ENOSYS;
+}
+inline int lifebook_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+#endif
+
+#endif
diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c
new file mode 100644
index 0000000..e241311
--- /dev/null
+++ b/drivers/input/mouse/logibm.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * James Banks Matthew Dillon
+ * David Giller Nathan Laredo
+ * Linus Torvalds Johan Myreen
+ * Cliff Matthews Philip Blundell
+ * Russell King
+ */
+
+/*
+ * Logitech Bus Mouse Driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Logitech busmouse driver");
+MODULE_LICENSE("GPL");
+
+#define LOGIBM_BASE 0x23c
+#define LOGIBM_EXTENT 4
+
+#define LOGIBM_DATA_PORT LOGIBM_BASE + 0
+#define LOGIBM_SIGNATURE_PORT LOGIBM_BASE + 1
+#define LOGIBM_CONTROL_PORT LOGIBM_BASE + 2
+#define LOGIBM_CONFIG_PORT LOGIBM_BASE + 3
+
+#define LOGIBM_ENABLE_IRQ 0x00
+#define LOGIBM_DISABLE_IRQ 0x10
+#define LOGIBM_READ_X_LOW 0x80
+#define LOGIBM_READ_X_HIGH 0xa0
+#define LOGIBM_READ_Y_LOW 0xc0
+#define LOGIBM_READ_Y_HIGH 0xe0
+
+#define LOGIBM_DEFAULT_MODE 0x90
+#define LOGIBM_CONFIG_BYTE 0x91
+#define LOGIBM_SIGNATURE_BYTE 0xa5
+
+#define LOGIBM_IRQ 5
+
+static int logibm_irq = LOGIBM_IRQ;
+module_param_named(irq, logibm_irq, uint, 0);
+MODULE_PARM_DESC(irq, "IRQ number (5=default)");
+
+static struct input_dev *logibm_dev;
+
+static irqreturn_t logibm_interrupt(int irq, void *dev_id)
+{
+ char dx, dy;
+ unsigned char buttons;
+
+ outb(LOGIBM_READ_X_LOW, LOGIBM_CONTROL_PORT);
+ dx = (inb(LOGIBM_DATA_PORT) & 0xf);
+ outb(LOGIBM_READ_X_HIGH, LOGIBM_CONTROL_PORT);
+ dx |= (inb(LOGIBM_DATA_PORT) & 0xf) << 4;
+ outb(LOGIBM_READ_Y_LOW, LOGIBM_CONTROL_PORT);
+ dy = (inb(LOGIBM_DATA_PORT) & 0xf);
+ outb(LOGIBM_READ_Y_HIGH, LOGIBM_CONTROL_PORT);
+ buttons = inb(LOGIBM_DATA_PORT);
+ dy |= (buttons & 0xf) << 4;
+ buttons = ~buttons >> 5;
+
+ input_report_rel(logibm_dev, REL_X, dx);
+ input_report_rel(logibm_dev, REL_Y, dy);
+ input_report_key(logibm_dev, BTN_RIGHT, buttons & 1);
+ input_report_key(logibm_dev, BTN_MIDDLE, buttons & 2);
+ input_report_key(logibm_dev, BTN_LEFT, buttons & 4);
+ input_sync(logibm_dev);
+
+ outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT);
+ return IRQ_HANDLED;
+}
+
+static int logibm_open(struct input_dev *dev)
+{
+ if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) {
+ printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq);
+ return -EBUSY;
+ }
+ outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT);
+ return 0;
+}
+
+static void logibm_close(struct input_dev *dev)
+{
+ outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
+ free_irq(logibm_irq, NULL);
+}
+
+static int __init logibm_init(void)
+{
+ int err;
+
+ if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) {
+ printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE);
+ return -EBUSY;
+ }
+
+ outb(LOGIBM_CONFIG_BYTE, LOGIBM_CONFIG_PORT);
+ outb(LOGIBM_SIGNATURE_BYTE, LOGIBM_SIGNATURE_PORT);
+ udelay(100);
+
+ if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) {
+ printk(KERN_INFO "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE);
+ err = -ENODEV;
+ goto err_release_region;
+ }
+
+ outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT);
+ outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
+
+ logibm_dev = input_allocate_device();
+ if (!logibm_dev) {
+ printk(KERN_ERR "logibm.c: Not enough memory for input device\n");
+ err = -ENOMEM;
+ goto err_release_region;
+ }
+
+ logibm_dev->name = "Logitech bus mouse";
+ logibm_dev->phys = "isa023c/input0";
+ logibm_dev->id.bustype = BUS_ISA;
+ logibm_dev->id.vendor = 0x0003;
+ logibm_dev->id.product = 0x0001;
+ logibm_dev->id.version = 0x0100;
+
+ logibm_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ logibm_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+ logibm_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+ logibm_dev->open = logibm_open;
+ logibm_dev->close = logibm_close;
+
+ err = input_register_device(logibm_dev);
+ if (err)
+ goto err_free_dev;
+
+ return 0;
+
+ err_free_dev:
+ input_free_device(logibm_dev);
+ err_release_region:
+ release_region(LOGIBM_BASE, LOGIBM_EXTENT);
+
+ return err;
+}
+
+static void __exit logibm_exit(void)
+{
+ input_unregister_device(logibm_dev);
+ release_region(LOGIBM_BASE, LOGIBM_EXTENT);
+}
+
+module_init(logibm_init);
+module_exit(logibm_exit);
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
new file mode 100644
index 0000000..390f1db
--- /dev/null
+++ b/drivers/input/mouse/logips2pp.c
@@ -0,0 +1,417 @@
+/*
+ * Logitech PS/2++ mouse driver
+ *
+ * Copyright (c) 1999-2003 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2003 Eric Wong <eric@yhbt.net>
+ *
+ * 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.
+ */
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include "psmouse.h"
+#include "logips2pp.h"
+
+/* Logitech mouse types */
+#define PS2PP_KIND_WHEEL 1
+#define PS2PP_KIND_MX 2
+#define PS2PP_KIND_TP3 3
+#define PS2PP_KIND_TRACKMAN 4
+
+/* Logitech mouse features */
+#define PS2PP_WHEEL 0x01
+#define PS2PP_HWHEEL 0x02
+#define PS2PP_SIDE_BTN 0x04
+#define PS2PP_EXTRA_BTN 0x08
+#define PS2PP_TASK_BTN 0x10
+#define PS2PP_NAV_BTN 0x20
+
+struct ps2pp_info {
+ u8 model;
+ u8 kind;
+ u16 features;
+};
+
+/*
+ * Process a PS2++ or PS2T++ packet.
+ */
+
+static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ unsigned char *packet = psmouse->packet;
+
+ if (psmouse->pktcnt < 3)
+ return PSMOUSE_GOOD_DATA;
+
+/*
+ * Full packet accumulated, process it
+ */
+
+ if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) {
+
+ /* Logitech extended packet */
+ switch ((packet[1] >> 4) | (packet[0] & 0x30)) {
+
+ case 0x0d: /* Mouse extra info */
+
+ input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
+ (int) (packet[2] & 8) - (int) (packet[2] & 7));
+ input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1);
+ input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1);
+
+ break;
+
+ case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
+
+ input_report_key(dev, BTN_SIDE, (packet[2]) & 1);
+ input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1);
+ input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1);
+ input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1);
+ input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1);
+
+ break;
+
+ case 0x0f: /* TouchPad extra info */
+
+ input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
+ (int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7));
+ packet[0] = packet[2] | 0x08;
+ break;
+
+#ifdef DEBUG
+ default:
+ printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n",
+ (packet[1] >> 4) | (packet[0] & 0x30));
+#endif
+ }
+ } else {
+ /* Standard PS/2 motion data */
+ input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
+ input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
+ }
+
+ input_report_key(dev, BTN_LEFT, packet[0] & 1);
+ input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
+ input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
+
+ input_sync(dev);
+
+ return PSMOUSE_FULL_PACKET;
+
+}
+
+/*
+ * ps2pp_cmd() sends a PS2++ command, sliced into two bit
+ * pieces through the SETRES command. This is needed to send extended
+ * commands to mice on notebooks that try to understand the PS/2 protocol
+ * Ugly.
+ */
+
+static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command)
+{
+ if (psmouse_sliced_command(psmouse, command))
+ return -1;
+
+ if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * SmartScroll / CruiseControl for some newer Logitech mice Defaults to
+ * enabled if we do nothing to it. Of course I put this in because I want it
+ * disabled :P
+ * 1 - enabled (if previously disabled, also default)
+ * 0 - disabled
+ */
+
+static void ps2pp_set_smartscroll(struct psmouse *psmouse, unsigned int smartscroll)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[4];
+
+ if (smartscroll > 1)
+ smartscroll = 1;
+
+ ps2pp_cmd(psmouse, param, 0x32);
+
+ param[0] = 0;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+
+ param[0] = smartscroll;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+}
+
+static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse, void *data, char *buf)
+{
+ return sprintf(buf, "%d\n", psmouse->smartscroll ? 1 : 0);
+}
+
+static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data, const char *buf, size_t count)
+{
+ unsigned long value;
+
+ if (strict_strtoul(buf, 10, &value) || value > 1)
+ return -EINVAL;
+
+ ps2pp_set_smartscroll(psmouse, value);
+ psmouse->smartscroll = value;
+ return count;
+}
+
+PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL,
+ ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll);
+
+/*
+ * Support 800 dpi resolution _only_ if the user wants it (there are good
+ * reasons to not use it even if the mouse supports it, and of course there are
+ * also good reasons to use it, let the user decide).
+ */
+
+static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolution)
+{
+ if (resolution > 400) {
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param = 3;
+
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+ ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
+ psmouse->resolution = 800;
+ } else
+ psmouse_set_resolution(psmouse, resolution);
+}
+
+static void ps2pp_disconnect(struct psmouse *psmouse)
+{
+ device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll.dattr);
+}
+
+static const struct ps2pp_info *get_model_info(unsigned char model)
+{
+ static const struct ps2pp_info ps2pp_list[] = {
+ { 1, 0, 0 }, /* Simple 2-button mouse */
+ { 12, 0, PS2PP_SIDE_BTN},
+ { 13, 0, 0 },
+ { 15, PS2PP_KIND_MX, /* MX1000 */
+ PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+ PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL },
+ { 40, 0, PS2PP_SIDE_BTN },
+ { 41, 0, PS2PP_SIDE_BTN },
+ { 42, 0, PS2PP_SIDE_BTN },
+ { 43, 0, PS2PP_SIDE_BTN },
+ { 50, 0, 0 },
+ { 51, 0, 0 },
+ { 52, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL },
+ { 53, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
+ { 56, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, /* Cordless MouseMan Wheel */
+ { 61, PS2PP_KIND_MX, /* MX700 */
+ PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+ PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
+ { 66, PS2PP_KIND_MX, /* MX3100 reciver */
+ PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+ PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL },
+ { 72, PS2PP_KIND_TRACKMAN, 0 }, /* T-CH11: TrackMan Marble */
+ { 73, 0, PS2PP_SIDE_BTN },
+ { 75, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
+ { 76, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
+ { 79, PS2PP_KIND_TRACKMAN, PS2PP_WHEEL }, /* TrackMan with wheel */
+ { 80, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL },
+ { 81, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
+ { 83, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
+ { 85, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
+ { 86, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
+ { 87, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
+ { 88, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
+ { 96, 0, 0 },
+ { 97, PS2PP_KIND_TP3, PS2PP_WHEEL | PS2PP_HWHEEL },
+ { 99, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
+ { 100, PS2PP_KIND_MX, /* MX510 */
+ PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+ PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
+ { 111, PS2PP_KIND_MX, PS2PP_WHEEL | PS2PP_SIDE_BTN }, /* MX300 reports task button as side */
+ { 112, PS2PP_KIND_MX, /* MX500 */
+ PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+ PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
+ { 114, PS2PP_KIND_MX, /* MX310 */
+ PS2PP_WHEEL | PS2PP_SIDE_BTN |
+ PS2PP_TASK_BTN | PS2PP_EXTRA_BTN }
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ps2pp_list); i++)
+ if (model == ps2pp_list[i].model)
+ return &ps2pp_list[i];
+
+ printk(KERN_WARNING "logips2pp: Detected unknown logitech mouse model %d\n", model);
+ return NULL;
+}
+
+/*
+ * Set up input device's properties based on the detected mouse model.
+ */
+
+static void ps2pp_set_model_properties(struct psmouse *psmouse,
+ const struct ps2pp_info *model_info,
+ int using_ps2pp)
+{
+ struct input_dev *input_dev = psmouse->dev;
+
+ if (model_info->features & PS2PP_SIDE_BTN)
+ set_bit(BTN_SIDE, input_dev->keybit);
+
+ if (model_info->features & PS2PP_EXTRA_BTN)
+ set_bit(BTN_EXTRA, input_dev->keybit);
+
+ if (model_info->features & PS2PP_TASK_BTN)
+ set_bit(BTN_TASK, input_dev->keybit);
+
+ if (model_info->features & PS2PP_NAV_BTN) {
+ set_bit(BTN_FORWARD, input_dev->keybit);
+ set_bit(BTN_BACK, input_dev->keybit);
+ }
+
+ if (model_info->features & PS2PP_WHEEL)
+ set_bit(REL_WHEEL, input_dev->relbit);
+
+ if (model_info->features & PS2PP_HWHEEL)
+ set_bit(REL_HWHEEL, input_dev->relbit);
+
+ switch (model_info->kind) {
+ case PS2PP_KIND_WHEEL:
+ psmouse->name = "Wheel Mouse";
+ break;
+
+ case PS2PP_KIND_MX:
+ psmouse->name = "MX Mouse";
+ break;
+
+ case PS2PP_KIND_TP3:
+ psmouse->name = "TouchPad 3";
+ break;
+
+ case PS2PP_KIND_TRACKMAN:
+ psmouse->name = "TrackMan";
+ break;
+
+ default:
+ /*
+ * Set name to "Mouse" only when using PS2++,
+ * otherwise let other protocols define suitable
+ * name
+ */
+ if (using_ps2pp)
+ psmouse->name = "Mouse";
+ break;
+ }
+}
+
+
+/*
+ * Logitech magic init. Detect whether the mouse is a Logitech one
+ * and its exact model and try turning on extended protocol for ones
+ * that support it.
+ */
+
+int ps2pp_init(struct psmouse *psmouse, int set_properties)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[4];
+ unsigned char model, buttons;
+ const struct ps2pp_info *model_info;
+ int use_ps2pp = 0;
+ int error;
+
+ param[0] = 0;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+ param[1] = 0;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
+
+ model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78);
+ buttons = param[1];
+
+ if (!model || !buttons)
+ return -1;
+
+ if ((model_info = get_model_info(model)) != NULL) {
+
+/*
+ * Do Logitech PS2++ / PS2T++ magic init.
+ */
+ if (model_info->kind == PS2PP_KIND_TP3) { /* Touch Pad 3 */
+
+ /* Unprotect RAM */
+ param[0] = 0x11; param[1] = 0x04; param[2] = 0x68;
+ ps2_command(ps2dev, param, 0x30d1);
+ /* Enable features */
+ param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b;
+ ps2_command(ps2dev, param, 0x30d1);
+ /* Enable PS2++ */
+ param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3;
+ ps2_command(ps2dev, param, 0x30d1);
+
+ param[0] = 0;
+ if (!ps2_command(ps2dev, param, 0x13d1) &&
+ param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) {
+ use_ps2pp = 1;
+ }
+
+ } else {
+
+ param[0] = param[1] = param[2] = 0;
+ ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */
+ ps2pp_cmd(psmouse, param, 0xDB);
+
+ if ((param[0] & 0x78) == 0x48 &&
+ (param[1] & 0xf3) == 0xc2 &&
+ (param[2] & 0x03) == ((param[1] >> 2) & 3)) {
+ ps2pp_set_smartscroll(psmouse, psmouse->smartscroll);
+ use_ps2pp = 1;
+ }
+ }
+ }
+
+ if (set_properties) {
+ psmouse->vendor = "Logitech";
+ psmouse->model = model;
+
+ if (use_ps2pp) {
+ psmouse->protocol_handler = ps2pp_process_byte;
+ psmouse->pktsize = 3;
+
+ if (model_info->kind != PS2PP_KIND_TP3) {
+ psmouse->set_resolution = ps2pp_set_resolution;
+ psmouse->disconnect = ps2pp_disconnect;
+
+ error = device_create_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_smartscroll.dattr);
+ if (error) {
+ printk(KERN_ERR
+ "logips2pp.c: failed to create smartscroll "
+ "sysfs attribute, error: %d\n", error);
+ return -1;
+ }
+ }
+ }
+
+ if (buttons < 3)
+ clear_bit(BTN_MIDDLE, psmouse->dev->keybit);
+
+ if (model_info)
+ ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
+ }
+
+ return use_ps2pp ? 0 : -1;
+}
+
diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h
new file mode 100644
index 0000000..6e57125
--- /dev/null
+++ b/drivers/input/mouse/logips2pp.h
@@ -0,0 +1,23 @@
+/*
+ * Logitech PS/2++ mouse driver header
+ *
+ * Copyright (c) 2003 Vojtech Pavlik <vojtech@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 _LOGIPS2PP_H
+#define _LOGIPS2PP_H
+
+#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
+int ps2pp_init(struct psmouse *psmouse, int set_properties);
+#else
+inline int ps2pp_init(struct psmouse *psmouse, int set_properties)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_LOGIPS2PP */
+
+#endif
diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c
new file mode 100644
index 0000000..fd09c8d
--- /dev/null
+++ b/drivers/input/mouse/pc110pad.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Alan Cox Robin O'Leary
+ */
+
+/*
+ * IBM PC110 touchpad driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("IBM PC110 touchpad driver");
+MODULE_LICENSE("GPL");
+
+#define PC110PAD_OFF 0x30
+#define PC110PAD_ON 0x38
+
+static int pc110pad_irq = 10;
+static int pc110pad_io = 0x15e0;
+
+static struct input_dev *pc110pad_dev;
+static int pc110pad_data[3];
+static int pc110pad_count;
+
+static irqreturn_t pc110pad_interrupt(int irq, void *ptr)
+{
+ int value = inb_p(pc110pad_io);
+ int handshake = inb_p(pc110pad_io + 2);
+
+ outb(handshake | 1, pc110pad_io + 2);
+ udelay(2);
+ outb(handshake & ~1, pc110pad_io + 2);
+ udelay(2);
+ inb_p(0x64);
+
+ pc110pad_data[pc110pad_count++] = value;
+
+ if (pc110pad_count < 3)
+ return IRQ_HANDLED;
+
+ input_report_key(pc110pad_dev, BTN_TOUCH,
+ pc110pad_data[0] & 0x01);
+ input_report_abs(pc110pad_dev, ABS_X,
+ pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100));
+ input_report_abs(pc110pad_dev, ABS_Y,
+ pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80));
+ input_sync(pc110pad_dev);
+
+ pc110pad_count = 0;
+ return IRQ_HANDLED;
+}
+
+static void pc110pad_close(struct input_dev *dev)
+{
+ outb(PC110PAD_OFF, pc110pad_io + 2);
+}
+
+static int pc110pad_open(struct input_dev *dev)
+{
+ pc110pad_interrupt(0, NULL);
+ pc110pad_interrupt(0, NULL);
+ pc110pad_interrupt(0, NULL);
+ outb(PC110PAD_ON, pc110pad_io + 2);
+ pc110pad_count = 0;
+
+ return 0;
+}
+
+/*
+ * We try to avoid enabling the hardware if it's not
+ * there, but we don't know how to test. But we do know
+ * that the PC110 is not a PCI system. So if we find any
+ * PCI devices in the machine, we don't have a PC110.
+ */
+static int __init pc110pad_init(void)
+{
+ struct pci_dev *dev;
+ int err;
+
+ dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ if (dev) {
+ pci_dev_put(dev);
+ return -ENODEV;
+ }
+
+ if (!request_region(pc110pad_io, 4, "pc110pad")) {
+ printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n",
+ pc110pad_io, pc110pad_io + 4);
+ return -EBUSY;
+ }
+
+ outb(PC110PAD_OFF, pc110pad_io + 2);
+
+ if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) {
+ printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq);
+ err = -EBUSY;
+ goto err_release_region;
+ }
+
+ pc110pad_dev = input_allocate_device();
+ if (!pc110pad_dev) {
+ printk(KERN_ERR "pc110pad: Not enough memory.\n");
+ err = -ENOMEM;
+ goto err_free_irq;
+ }
+
+ pc110pad_dev->name = "IBM PC110 TouchPad";
+ pc110pad_dev->phys = "isa15e0/input0";
+ pc110pad_dev->id.bustype = BUS_ISA;
+ pc110pad_dev->id.vendor = 0x0003;
+ pc110pad_dev->id.product = 0x0001;
+ pc110pad_dev->id.version = 0x0100;
+
+ pc110pad_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ pc110pad_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y);
+ pc110pad_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ pc110pad_dev->absmax[ABS_X] = 0x1ff;
+ pc110pad_dev->absmax[ABS_Y] = 0x0ff;
+
+ pc110pad_dev->open = pc110pad_open;
+ pc110pad_dev->close = pc110pad_close;
+
+ err = input_register_device(pc110pad_dev);
+ if (err)
+ goto err_free_dev;
+
+ return 0;
+
+ err_free_dev:
+ input_free_device(pc110pad_dev);
+ err_free_irq:
+ free_irq(pc110pad_irq, NULL);
+ err_release_region:
+ release_region(pc110pad_io, 4);
+
+ return err;
+}
+
+static void __exit pc110pad_exit(void)
+{
+ outb(PC110PAD_OFF, pc110pad_io + 2);
+ free_irq(pc110pad_irq, NULL);
+ input_unregister_device(pc110pad_dev);
+ release_region(pc110pad_io, 4);
+}
+
+module_init(pc110pad_init);
+module_exit(pc110pad_exit);
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
new file mode 100644
index 0000000..f8f86de
--- /dev/null
+++ b/drivers/input/mouse/psmouse-base.c
@@ -0,0 +1,1672 @@
+/*
+ * PS/2 mouse driver
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 2003-2004 Dmitry Torokhov
+ */
+
+/*
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/libps2.h>
+#include <linux/mutex.h>
+
+#include "psmouse.h"
+#include "synaptics.h"
+#include "logips2pp.h"
+#include "alps.h"
+#include "hgpk.h"
+#include "lifebook.h"
+#include "trackpoint.h"
+#include "touchkit_ps2.h"
+#include "elantech.h"
+
+#define DRIVER_DESC "PS/2 mouse driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static unsigned int psmouse_max_proto = PSMOUSE_AUTO;
+static int psmouse_set_maxproto(const char *val, struct kernel_param *kp);
+static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp);
+#define param_check_proto_abbrev(name, p) __param_check(name, p, unsigned int)
+#define param_set_proto_abbrev psmouse_set_maxproto
+#define param_get_proto_abbrev psmouse_get_maxproto
+module_param_named(proto, psmouse_max_proto, proto_abbrev, 0644);
+MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps, any). Useful for KVM switches.");
+
+static unsigned int psmouse_resolution = 200;
+module_param_named(resolution, psmouse_resolution, uint, 0644);
+MODULE_PARM_DESC(resolution, "Resolution, in dpi.");
+
+static unsigned int psmouse_rate = 100;
+module_param_named(rate, psmouse_rate, uint, 0644);
+MODULE_PARM_DESC(rate, "Report rate, in reports per second.");
+
+static unsigned int psmouse_smartscroll = 1;
+module_param_named(smartscroll, psmouse_smartscroll, bool, 0644);
+MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled.");
+
+static unsigned int psmouse_resetafter = 5;
+module_param_named(resetafter, psmouse_resetafter, uint, 0644);
+MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");
+
+static unsigned int psmouse_resync_time;
+module_param_named(resync_time, psmouse_resync_time, uint, 0644);
+MODULE_PARM_DESC(resync_time, "How long can mouse stay idle before forcing resync (in seconds, 0 = never).");
+
+PSMOUSE_DEFINE_ATTR(protocol, S_IWUSR | S_IRUGO,
+ NULL,
+ psmouse_attr_show_protocol, psmouse_attr_set_protocol);
+PSMOUSE_DEFINE_ATTR(rate, S_IWUSR | S_IRUGO,
+ (void *) offsetof(struct psmouse, rate),
+ psmouse_show_int_attr, psmouse_attr_set_rate);
+PSMOUSE_DEFINE_ATTR(resolution, S_IWUSR | S_IRUGO,
+ (void *) offsetof(struct psmouse, resolution),
+ psmouse_show_int_attr, psmouse_attr_set_resolution);
+PSMOUSE_DEFINE_ATTR(resetafter, S_IWUSR | S_IRUGO,
+ (void *) offsetof(struct psmouse, resetafter),
+ psmouse_show_int_attr, psmouse_set_int_attr);
+PSMOUSE_DEFINE_ATTR(resync_time, S_IWUSR | S_IRUGO,
+ (void *) offsetof(struct psmouse, resync_time),
+ psmouse_show_int_attr, psmouse_set_int_attr);
+
+static struct attribute *psmouse_attributes[] = {
+ &psmouse_attr_protocol.dattr.attr,
+ &psmouse_attr_rate.dattr.attr,
+ &psmouse_attr_resolution.dattr.attr,
+ &psmouse_attr_resetafter.dattr.attr,
+ &psmouse_attr_resync_time.dattr.attr,
+ NULL
+};
+
+static struct attribute_group psmouse_attribute_group = {
+ .attrs = psmouse_attributes,
+};
+
+/*
+ * psmouse_mutex protects all operations changing state of mouse
+ * (connecting, disconnecting, changing rate or resolution via
+ * sysfs). We could use a per-device semaphore but since there
+ * rarely more than one PS/2 mouse connected and since semaphore
+ * is taken in "slow" paths it is not worth it.
+ */
+static DEFINE_MUTEX(psmouse_mutex);
+
+static struct workqueue_struct *kpsmoused_wq;
+
+struct psmouse_protocol {
+ enum psmouse_type type;
+ const char *name;
+ const char *alias;
+ int maxproto;
+ int (*detect)(struct psmouse *, int);
+ int (*init)(struct psmouse *);
+};
+
+/*
+ * psmouse_process_byte() analyzes the PS/2 data stream and reports
+ * relevant events to the input module once full packet has arrived.
+ */
+
+static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ unsigned char *packet = psmouse->packet;
+
+ if (psmouse->pktcnt < psmouse->pktsize)
+ return PSMOUSE_GOOD_DATA;
+
+/*
+ * Full packet accumulated, process it
+ */
+
+/*
+ * Scroll wheel on IntelliMice, scroll buttons on NetMice
+ */
+
+ if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS)
+ input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
+
+/*
+ * Scroll wheel and buttons on IntelliMouse Explorer
+ */
+
+ if (psmouse->type == PSMOUSE_IMEX) {
+ switch (packet[3] & 0xC0) {
+ case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
+ input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
+ break;
+ case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */
+ input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
+ break;
+ case 0x00:
+ case 0xC0:
+ input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7));
+ input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
+ input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
+ break;
+ }
+ }
+
+/*
+ * Extra buttons on Genius NewNet 3D
+ */
+
+ if (psmouse->type == PSMOUSE_GENPS) {
+ input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1);
+ input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1);
+ }
+
+/*
+ * Extra button on ThinkingMouse
+ */
+ if (psmouse->type == PSMOUSE_THINKPS) {
+ input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1);
+ /* Without this bit of weirdness moving up gives wildly high Y changes. */
+ packet[1] |= (packet[0] & 0x40) << 1;
+ }
+
+/*
+ * Cortron PS2 Trackball reports SIDE button on the 4th bit of the first
+ * byte.
+ */
+ if (psmouse->type == PSMOUSE_CORTRON) {
+ input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1);
+ packet[0] |= 0x08;
+ }
+
+/*
+ * Generic PS/2 Mouse
+ */
+
+ input_report_key(dev, BTN_LEFT, packet[0] & 1);
+ input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
+ input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
+
+ input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
+ input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
+
+ input_sync(dev);
+
+ return PSMOUSE_FULL_PACKET;
+}
+
+void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
+ unsigned long delay)
+{
+ queue_delayed_work(kpsmoused_wq, work, delay);
+}
+
+/*
+ * __psmouse_set_state() sets new psmouse state and resets all flags.
+ */
+
+static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
+{
+ psmouse->state = new_state;
+ psmouse->pktcnt = psmouse->out_of_sync = 0;
+ psmouse->ps2dev.flags = 0;
+ psmouse->last = jiffies;
+}
+
+
+/*
+ * psmouse_set_state() sets new psmouse state and resets all flags and
+ * counters while holding serio lock so fighting with interrupt handler
+ * is not a concern.
+ */
+
+void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
+{
+ serio_pause_rx(psmouse->ps2dev.serio);
+ __psmouse_set_state(psmouse, new_state);
+ serio_continue_rx(psmouse->ps2dev.serio);
+}
+
+/*
+ * psmouse_handle_byte() processes one byte of the input data stream
+ * by calling corresponding protocol handler.
+ */
+
+static int psmouse_handle_byte(struct psmouse *psmouse)
+{
+ psmouse_ret_t rc = psmouse->protocol_handler(psmouse);
+
+ switch (rc) {
+ case PSMOUSE_BAD_DATA:
+ if (psmouse->state == PSMOUSE_ACTIVATED) {
+ printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n",
+ psmouse->name, psmouse->phys, psmouse->pktcnt);
+ if (++psmouse->out_of_sync == psmouse->resetafter) {
+ __psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+ printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n");
+ serio_reconnect(psmouse->ps2dev.serio);
+ return -1;
+ }
+ }
+ psmouse->pktcnt = 0;
+ break;
+
+ case PSMOUSE_FULL_PACKET:
+ psmouse->pktcnt = 0;
+ if (psmouse->out_of_sync) {
+ psmouse->out_of_sync = 0;
+ printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n",
+ psmouse->name, psmouse->phys);
+ }
+ break;
+
+ case PSMOUSE_GOOD_DATA:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * psmouse_interrupt() handles incoming characters, either passing them
+ * for normal processing or gathering them as command response.
+ */
+
+static irqreturn_t psmouse_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct psmouse *psmouse = serio_get_drvdata(serio);
+
+ if (psmouse->state == PSMOUSE_IGNORE)
+ goto out;
+
+ if (flags & (SERIO_PARITY|SERIO_TIMEOUT)) {
+ if (psmouse->state == PSMOUSE_ACTIVATED)
+ printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n",
+ flags & SERIO_TIMEOUT ? " timeout" : "",
+ flags & SERIO_PARITY ? " bad parity" : "");
+ ps2_cmd_aborted(&psmouse->ps2dev);
+ goto out;
+ }
+
+ if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK))
+ if (ps2_handle_ack(&psmouse->ps2dev, data))
+ goto out;
+
+ if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_CMD))
+ if (ps2_handle_response(&psmouse->ps2dev, data))
+ goto out;
+
+ if (psmouse->state <= PSMOUSE_RESYNCING)
+ goto out;
+
+ if (psmouse->state == PSMOUSE_ACTIVATED &&
+ psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
+ printk(KERN_INFO "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n",
+ psmouse->name, psmouse->phys, psmouse->pktcnt);
+ psmouse->badbyte = psmouse->packet[0];
+ __psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
+ psmouse_queue_work(psmouse, &psmouse->resync_work, 0);
+ goto out;
+ }
+
+ psmouse->packet[psmouse->pktcnt++] = data;
+/*
+ * Check if this is a new device announcement (0xAA 0x00)
+ */
+ if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) {
+ if (psmouse->pktcnt == 1) {
+ psmouse->last = jiffies;
+ goto out;
+ }
+
+ if (psmouse->packet[1] == PSMOUSE_RET_ID) {
+ __psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+ serio_reconnect(serio);
+ goto out;
+ }
+/*
+ * Not a new device, try processing first byte normally
+ */
+ psmouse->pktcnt = 1;
+ if (psmouse_handle_byte(psmouse))
+ goto out;
+
+ psmouse->packet[psmouse->pktcnt++] = data;
+ }
+
+/*
+ * See if we need to force resync because mouse was idle for too long
+ */
+ if (psmouse->state == PSMOUSE_ACTIVATED &&
+ psmouse->pktcnt == 1 && psmouse->resync_time &&
+ time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) {
+ psmouse->badbyte = psmouse->packet[0];
+ __psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
+ psmouse_queue_work(psmouse, &psmouse->resync_work, 0);
+ goto out;
+ }
+
+ psmouse->last = jiffies;
+ psmouse_handle_byte(psmouse);
+
+ out:
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * psmouse_sliced_command() sends an extended PS/2 command to the mouse
+ * using sliced syntax, understood by advanced devices, such as Logitech
+ * or Synaptics touchpads. The command is encoded as:
+ * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
+ * is the command.
+ */
+int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command)
+{
+ int i;
+
+ if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
+ return -1;
+
+ for (i = 6; i >= 0; i -= 2) {
+ unsigned char d = (command >> i) & 3;
+ if (ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * psmouse_reset() resets the mouse into power-on state.
+ */
+int psmouse_reset(struct psmouse *psmouse)
+{
+ unsigned char param[2];
+
+ if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT))
+ return -1;
+
+ if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID)
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ * Genius NetMouse magic init.
+ */
+static int genius_detect(struct psmouse *psmouse, int set_properties)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[4];
+
+ param[0] = 3;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
+
+ if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55)
+ return -1;
+
+ if (set_properties) {
+ set_bit(BTN_EXTRA, psmouse->dev->keybit);
+ set_bit(BTN_SIDE, psmouse->dev->keybit);
+ set_bit(REL_WHEEL, psmouse->dev->relbit);
+
+ psmouse->vendor = "Genius";
+ psmouse->name = "Mouse";
+ psmouse->pktsize = 4;
+ }
+
+ return 0;
+}
+
+/*
+ * IntelliMouse magic init.
+ */
+static int intellimouse_detect(struct psmouse *psmouse, int set_properties)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[2];
+
+ param[0] = 200;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 100;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 80;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
+
+ if (param[0] != 3)
+ return -1;
+
+ if (set_properties) {
+ set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+ set_bit(REL_WHEEL, psmouse->dev->relbit);
+
+ if (!psmouse->vendor) psmouse->vendor = "Generic";
+ if (!psmouse->name) psmouse->name = "Wheel Mouse";
+ psmouse->pktsize = 4;
+ }
+
+ return 0;
+}
+
+/*
+ * Try IntelliMouse/Explorer magic init.
+ */
+static int im_explorer_detect(struct psmouse *psmouse, int set_properties)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[2];
+
+ intellimouse_detect(psmouse, 0);
+
+ param[0] = 200;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 200;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 80;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
+
+ if (param[0] != 4)
+ return -1;
+
+/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
+ param[0] = 200;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 80;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 40;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+
+ if (set_properties) {
+ set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+ set_bit(REL_WHEEL, psmouse->dev->relbit);
+ set_bit(REL_HWHEEL, psmouse->dev->relbit);
+ set_bit(BTN_SIDE, psmouse->dev->keybit);
+ set_bit(BTN_EXTRA, psmouse->dev->keybit);
+
+ if (!psmouse->vendor) psmouse->vendor = "Generic";
+ if (!psmouse->name) psmouse->name = "Explorer Mouse";
+ psmouse->pktsize = 4;
+ }
+
+ return 0;
+}
+
+/*
+ * Kensington ThinkingMouse / ExpertMouse magic init.
+ */
+static int thinking_detect(struct psmouse *psmouse, int set_properties)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[2];
+ static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
+ int i;
+
+ param[0] = 10;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 0;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ for (i = 0; i < ARRAY_SIZE(seq); i++) {
+ param[0] = seq[i];
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ }
+ ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
+
+ if (param[0] != 2)
+ return -1;
+
+ if (set_properties) {
+ set_bit(BTN_EXTRA, psmouse->dev->keybit);
+
+ psmouse->vendor = "Kensington";
+ psmouse->name = "ThinkingMouse";
+ }
+
+ return 0;
+}
+
+/*
+ * Bare PS/2 protocol "detection". Always succeeds.
+ */
+static int ps2bare_detect(struct psmouse *psmouse, int set_properties)
+{
+ if (set_properties) {
+ if (!psmouse->vendor) psmouse->vendor = "Generic";
+ if (!psmouse->name) psmouse->name = "Mouse";
+ }
+
+ return 0;
+}
+
+/*
+ * Cortron PS/2 protocol detection. There's no special way to detect it, so it
+ * must be forced by sysfs protocol writing.
+ */
+static int cortron_detect(struct psmouse *psmouse, int set_properties)
+{
+ if (set_properties) {
+ psmouse->vendor = "Cortron";
+ psmouse->name = "PS/2 Trackball";
+ set_bit(BTN_SIDE, psmouse->dev->keybit);
+ }
+
+ return 0;
+}
+
+/*
+ * psmouse_extensions() probes for any extensions to the basic PS/2 protocol
+ * the mouse may have.
+ */
+
+static int psmouse_extensions(struct psmouse *psmouse,
+ unsigned int max_proto, int set_properties)
+{
+ int synaptics_hardware = 0;
+
+/*
+ * We always check for lifebook because it does not disturb mouse
+ * (it only checks DMI information).
+ */
+ if (lifebook_detect(psmouse, set_properties) == 0) {
+ if (max_proto > PSMOUSE_IMEX) {
+ if (!set_properties || lifebook_init(psmouse) == 0)
+ return PSMOUSE_LIFEBOOK;
+ }
+ }
+
+/*
+ * Try Kensington ThinkingMouse (we try first, because synaptics probe
+ * upsets the thinkingmouse).
+ */
+
+ if (max_proto > PSMOUSE_IMEX && thinking_detect(psmouse, set_properties) == 0)
+ return PSMOUSE_THINKPS;
+
+/*
+ * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol
+ * support is disabled in config - we need to know if it is synaptics so we
+ * can reset it properly after probing for intellimouse.
+ */
+ if (max_proto > PSMOUSE_PS2 && synaptics_detect(psmouse, set_properties) == 0) {
+ synaptics_hardware = 1;
+
+ if (max_proto > PSMOUSE_IMEX) {
+ if (!set_properties || synaptics_init(psmouse) == 0)
+ return PSMOUSE_SYNAPTICS;
+/*
+ * Some Synaptics touchpads can emulate extended protocols (like IMPS/2).
+ * Unfortunately Logitech/Genius probes confuse some firmware versions so
+ * we'll have to skip them.
+ */
+ max_proto = PSMOUSE_IMEX;
+ }
+/*
+ * Make sure that touchpad is in relative mode, gestures (taps) are enabled
+ */
+ synaptics_reset(psmouse);
+ }
+
+/*
+ * Try ALPS TouchPad
+ */
+ if (max_proto > PSMOUSE_IMEX) {
+ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+ if (alps_detect(psmouse, set_properties) == 0) {
+ if (!set_properties || alps_init(psmouse) == 0)
+ return PSMOUSE_ALPS;
+/*
+ * Init failed, try basic relative protocols
+ */
+ max_proto = PSMOUSE_IMEX;
+ }
+ }
+
+/*
+ * Try OLPC HGPK touchpad.
+ */
+ if (max_proto > PSMOUSE_IMEX &&
+ hgpk_detect(psmouse, set_properties) == 0) {
+ if (!set_properties || hgpk_init(psmouse) == 0)
+ return PSMOUSE_HGPK;
+/*
+ * Init failed, try basic relative protocols
+ */
+ max_proto = PSMOUSE_IMEX;
+ }
+
+/*
+ * Try Elantech touchpad.
+ */
+ if (max_proto > PSMOUSE_IMEX &&
+ elantech_detect(psmouse, set_properties) == 0) {
+ if (!set_properties || elantech_init(psmouse) == 0)
+ return PSMOUSE_ELANTECH;
+/*
+ * Init failed, try basic relative protocols
+ */
+ max_proto = PSMOUSE_IMEX;
+ }
+
+ if (max_proto > PSMOUSE_IMEX) {
+ if (genius_detect(psmouse, set_properties) == 0)
+ return PSMOUSE_GENPS;
+
+ if (ps2pp_init(psmouse, set_properties) == 0)
+ return PSMOUSE_PS2PP;
+
+ if (trackpoint_detect(psmouse, set_properties) == 0)
+ return PSMOUSE_TRACKPOINT;
+
+ if (touchkit_ps2_detect(psmouse, set_properties) == 0)
+ return PSMOUSE_TOUCHKIT_PS2;
+ }
+
+/*
+ * Reset to defaults in case the device got confused by extended
+ * protocol probes. Note that we follow up with full reset because
+ * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
+ */
+ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+ psmouse_reset(psmouse);
+
+ if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0)
+ return PSMOUSE_IMEX;
+
+ if (max_proto >= PSMOUSE_IMPS && intellimouse_detect(psmouse, set_properties) == 0)
+ return PSMOUSE_IMPS;
+
+/*
+ * Okay, all failed, we have a standard mouse here. The number of the buttons
+ * is still a question, though. We assume 3.
+ */
+ ps2bare_detect(psmouse, set_properties);
+
+ if (synaptics_hardware) {
+/*
+ * We detected Synaptics hardware but it did not respond to IMPS/2 probes.
+ * We need to reset the touchpad because if there is a track point on the
+ * pass through port it could get disabled while probing for protocol
+ * extensions.
+ */
+ psmouse_reset(psmouse);
+ }
+
+ return PSMOUSE_PS2;
+}
+
+static const struct psmouse_protocol psmouse_protocols[] = {
+ {
+ .type = PSMOUSE_PS2,
+ .name = "PS/2",
+ .alias = "bare",
+ .maxproto = 1,
+ .detect = ps2bare_detect,
+ },
+#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
+ {
+ .type = PSMOUSE_PS2PP,
+ .name = "PS2++",
+ .alias = "logitech",
+ .detect = ps2pp_init,
+ },
+#endif
+ {
+ .type = PSMOUSE_THINKPS,
+ .name = "ThinkPS/2",
+ .alias = "thinkps",
+ .detect = thinking_detect,
+ },
+ {
+ .type = PSMOUSE_GENPS,
+ .name = "GenPS/2",
+ .alias = "genius",
+ .detect = genius_detect,
+ },
+ {
+ .type = PSMOUSE_IMPS,
+ .name = "ImPS/2",
+ .alias = "imps",
+ .maxproto = 1,
+ .detect = intellimouse_detect,
+ },
+ {
+ .type = PSMOUSE_IMEX,
+ .name = "ImExPS/2",
+ .alias = "exps",
+ .maxproto = 1,
+ .detect = im_explorer_detect,
+ },
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
+ {
+ .type = PSMOUSE_SYNAPTICS,
+ .name = "SynPS/2",
+ .alias = "synaptics",
+ .detect = synaptics_detect,
+ .init = synaptics_init,
+ },
+#endif
+#ifdef CONFIG_MOUSE_PS2_ALPS
+ {
+ .type = PSMOUSE_ALPS,
+ .name = "AlpsPS/2",
+ .alias = "alps",
+ .detect = alps_detect,
+ .init = alps_init,
+ },
+#endif
+#ifdef CONFIG_MOUSE_PS2_LIFEBOOK
+ {
+ .type = PSMOUSE_LIFEBOOK,
+ .name = "LBPS/2",
+ .alias = "lifebook",
+ .init = lifebook_init,
+ },
+#endif
+#ifdef CONFIG_MOUSE_PS2_TRACKPOINT
+ {
+ .type = PSMOUSE_TRACKPOINT,
+ .name = "TPPS/2",
+ .alias = "trackpoint",
+ .detect = trackpoint_detect,
+ },
+#endif
+#ifdef CONFIG_MOUSE_PS2_TOUCHKIT
+ {
+ .type = PSMOUSE_TOUCHKIT_PS2,
+ .name = "touchkitPS/2",
+ .alias = "touchkit",
+ .detect = touchkit_ps2_detect,
+ },
+#endif
+#ifdef CONFIG_MOUSE_PS2_OLPC
+ {
+ .type = PSMOUSE_HGPK,
+ .name = "OLPC HGPK",
+ .alias = "hgpk",
+ .detect = hgpk_detect,
+ },
+#endif
+#ifdef CONFIG_MOUSE_PS2_ELANTECH
+ {
+ .type = PSMOUSE_ELANTECH,
+ .name = "ETPS/2",
+ .alias = "elantech",
+ .detect = elantech_detect,
+ .init = elantech_init,
+ },
+ #endif
+ {
+ .type = PSMOUSE_CORTRON,
+ .name = "CortronPS/2",
+ .alias = "cortps",
+ .detect = cortron_detect,
+ },
+ {
+ .type = PSMOUSE_AUTO,
+ .name = "auto",
+ .alias = "any",
+ .maxproto = 1,
+ },
+};
+
+static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++)
+ if (psmouse_protocols[i].type == type)
+ return &psmouse_protocols[i];
+
+ WARN_ON(1);
+ return &psmouse_protocols[0];
+}
+
+static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len)
+{
+ const struct psmouse_protocol *p;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) {
+ p = &psmouse_protocols[i];
+
+ if ((strlen(p->name) == len && !strncmp(p->name, name, len)) ||
+ (strlen(p->alias) == len && !strncmp(p->alias, name, len)))
+ return &psmouse_protocols[i];
+ }
+
+ return NULL;
+}
+
+
+/*
+ * psmouse_probe() probes for a PS/2 mouse.
+ */
+
+static int psmouse_probe(struct psmouse *psmouse)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[2];
+
+/*
+ * First, we check if it's a mouse. It should send 0x00 or 0x03
+ * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
+ * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and subsequent
+ * ID queries, probably due to a firmware bug.
+ */
+
+ param[0] = 0xa5;
+ if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
+ return -1;
+
+ if (param[0] != 0x00 && param[0] != 0x03 &&
+ param[0] != 0x04 && param[0] != 0xff)
+ return -1;
+
+/*
+ * Then we reset and disable the mouse so that it doesn't generate events.
+ */
+
+ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS))
+ printk(KERN_WARNING "psmouse.c: Failed to reset mouse on %s\n", ps2dev->serio->phys);
+
+ return 0;
+}
+
+/*
+ * Here we set the mouse resolution.
+ */
+
+void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
+{
+ static const unsigned char params[] = { 0, 1, 2, 2, 3 };
+ unsigned char p;
+
+ if (resolution == 0 || resolution > 200)
+ resolution = 200;
+
+ p = params[resolution / 50];
+ ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES);
+ psmouse->resolution = 25 << p;
+}
+
+/*
+ * Here we set the mouse report rate.
+ */
+
+static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+ static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
+ unsigned char r;
+ int i = 0;
+
+ while (rates[i] > rate) i++;
+ r = rates[i];
+ ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE);
+ psmouse->rate = r;
+}
+
+/*
+ * psmouse_initialize() initializes the mouse to a sane state.
+ */
+
+static void psmouse_initialize(struct psmouse *psmouse)
+{
+/*
+ * We set the mouse report rate, resolution and scaling.
+ */
+
+ if (psmouse_max_proto != PSMOUSE_PS2) {
+ psmouse->set_rate(psmouse, psmouse->rate);
+ psmouse->set_resolution(psmouse, psmouse->resolution);
+ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+ }
+}
+
+/*
+ * psmouse_activate() enables the mouse so that we get motion reports from it.
+ */
+
+static void psmouse_activate(struct psmouse *psmouse)
+{
+ if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE))
+ printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n",
+ psmouse->ps2dev.serio->phys);
+
+ psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+}
+
+
+/*
+ * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion
+ * reports from it unless we explicitly request it.
+ */
+
+static void psmouse_deactivate(struct psmouse *psmouse)
+{
+ if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE))
+ printk(KERN_WARNING "psmouse.c: Failed to deactivate mouse on %s\n",
+ psmouse->ps2dev.serio->phys);
+
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+}
+
+/*
+ * psmouse_poll() - default poll hanlder. Everyone except for ALPS uses it.
+ */
+
+static int psmouse_poll(struct psmouse *psmouse)
+{
+ return ps2_command(&psmouse->ps2dev, psmouse->packet,
+ PSMOUSE_CMD_POLL | (psmouse->pktsize << 8));
+}
+
+
+/*
+ * psmouse_resync() attempts to re-validate current protocol.
+ */
+
+static void psmouse_resync(struct work_struct *work)
+{
+ struct psmouse *parent = NULL, *psmouse =
+ container_of(work, struct psmouse, resync_work.work);
+ struct serio *serio = psmouse->ps2dev.serio;
+ psmouse_ret_t rc = PSMOUSE_GOOD_DATA;
+ int failed = 0, enabled = 0;
+ int i;
+
+ mutex_lock(&psmouse_mutex);
+
+ if (psmouse->state != PSMOUSE_RESYNCING)
+ goto out;
+
+ if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+ parent = serio_get_drvdata(serio->parent);
+ psmouse_deactivate(parent);
+ }
+
+/*
+ * Some mice don't ACK commands sent while they are in the middle of
+ * transmitting motion packet. To avoid delay we use ps2_sendbyte()
+ * instead of ps2_command() which would wait for 200ms for an ACK
+ * that may never come.
+ * As an additional quirk ALPS touchpads may not only forget to ACK
+ * disable command but will stop reporting taps, so if we see that
+ * mouse at least once ACKs disable we will do full reconnect if ACK
+ * is missing.
+ */
+ psmouse->num_resyncs++;
+
+ if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) {
+ if (psmouse->num_resyncs < 3 || psmouse->acks_disable_command)
+ failed = 1;
+ } else
+ psmouse->acks_disable_command = 1;
+
+/*
+ * Poll the mouse. If it was reset the packet will be shorter than
+ * psmouse->pktsize and ps2_command will fail. We do not expect and
+ * do not handle scenario when mouse "upgrades" its protocol while
+ * disconnected since it would require additional delay. If we ever
+ * see a mouse that does it we'll adjust the code.
+ */
+ if (!failed) {
+ if (psmouse->poll(psmouse))
+ failed = 1;
+ else {
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ for (i = 0; i < psmouse->pktsize; i++) {
+ psmouse->pktcnt++;
+ rc = psmouse->protocol_handler(psmouse);
+ if (rc != PSMOUSE_GOOD_DATA)
+ break;
+ }
+ if (rc != PSMOUSE_FULL_PACKET)
+ failed = 1;
+ psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
+ }
+ }
+/*
+ * Now try to enable mouse. We try to do that even if poll failed and also
+ * repeat our attempts 5 times, otherwise we may be left out with disabled
+ * mouse.
+ */
+ for (i = 0; i < 5; i++) {
+ if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
+ enabled = 1;
+ break;
+ }
+ msleep(200);
+ }
+
+ if (!enabled) {
+ printk(KERN_WARNING "psmouse.c: failed to re-enable mouse on %s\n",
+ psmouse->ps2dev.serio->phys);
+ failed = 1;
+ }
+
+ if (failed) {
+ psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+ printk(KERN_INFO "psmouse.c: resync failed, issuing reconnect request\n");
+ serio_reconnect(serio);
+ } else
+ psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+
+ if (parent)
+ psmouse_activate(parent);
+ out:
+ mutex_unlock(&psmouse_mutex);
+}
+
+/*
+ * psmouse_cleanup() resets the mouse into power-on state.
+ */
+
+static void psmouse_cleanup(struct serio *serio)
+{
+ struct psmouse *psmouse = serio_get_drvdata(serio);
+ struct psmouse *parent = NULL;
+
+ mutex_lock(&psmouse_mutex);
+
+ if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+ parent = serio_get_drvdata(serio->parent);
+ psmouse_deactivate(parent);
+ }
+
+ psmouse_deactivate(psmouse);
+
+ if (psmouse->cleanup)
+ psmouse->cleanup(psmouse);
+
+ psmouse_reset(psmouse);
+
+/*
+ * Some boxes, such as HP nx7400, get terribly confused if mouse
+ * is not fully enabled before suspending/shutting down.
+ */
+ ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+
+ if (parent) {
+ if (parent->pt_deactivate)
+ parent->pt_deactivate(parent);
+
+ psmouse_activate(parent);
+ }
+
+ mutex_unlock(&psmouse_mutex);
+}
+
+/*
+ * psmouse_disconnect() closes and frees.
+ */
+
+static void psmouse_disconnect(struct serio *serio)
+{
+ struct psmouse *psmouse, *parent = NULL;
+
+ psmouse = serio_get_drvdata(serio);
+
+ sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group);
+
+ mutex_lock(&psmouse_mutex);
+
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+ /* make sure we don't have a resync in progress */
+ mutex_unlock(&psmouse_mutex);
+ flush_workqueue(kpsmoused_wq);
+ mutex_lock(&psmouse_mutex);
+
+ if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+ parent = serio_get_drvdata(serio->parent);
+ psmouse_deactivate(parent);
+ }
+
+ if (psmouse->disconnect)
+ psmouse->disconnect(psmouse);
+
+ if (parent && parent->pt_deactivate)
+ parent->pt_deactivate(parent);
+
+ psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(psmouse->dev);
+ kfree(psmouse);
+
+ if (parent)
+ psmouse_activate(parent);
+
+ mutex_unlock(&psmouse_mutex);
+}
+
+static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse_protocol *proto)
+{
+ struct input_dev *input_dev = psmouse->dev;
+
+ input_dev->dev.parent = &psmouse->ps2dev.serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+ input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+ psmouse->set_rate = psmouse_set_rate;
+ psmouse->set_resolution = psmouse_set_resolution;
+ psmouse->poll = psmouse_poll;
+ psmouse->protocol_handler = psmouse_process_byte;
+ psmouse->pktsize = 3;
+
+ if (proto && (proto->detect || proto->init)) {
+ if (proto->detect && proto->detect(psmouse, 1) < 0)
+ return -1;
+
+ if (proto->init && proto->init(psmouse) < 0)
+ return -1;
+
+ psmouse->type = proto->type;
+ }
+ else
+ psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1);
+
+ /*
+ * If mouse's packet size is 3 there is no point in polling the
+ * device in hopes to detect protocol reset - we won't get less
+ * than 3 bytes response anyhow.
+ */
+ if (psmouse->pktsize == 3)
+ psmouse->resync_time = 0;
+
+ /*
+ * Some smart KVMs fake response to POLL command returning just
+ * 3 bytes and messing up our resync logic, so if initial poll
+ * fails we won't try polling the device anymore. Hopefully
+ * such KVM will maintain initially selected protocol.
+ */
+ if (psmouse->resync_time && psmouse->poll(psmouse))
+ psmouse->resync_time = 0;
+
+ snprintf(psmouse->devname, sizeof(psmouse->devname), "%s %s %s",
+ psmouse_protocol_by_type(psmouse->type)->name, psmouse->vendor, psmouse->name);
+
+ input_dev->name = psmouse->devname;
+ input_dev->phys = psmouse->phys;
+ input_dev->id.bustype = BUS_I8042;
+ input_dev->id.vendor = 0x0002;
+ input_dev->id.product = psmouse->type;
+ input_dev->id.version = psmouse->model;
+
+ return 0;
+}
+
+/*
+ * psmouse_connect() is a callback from the serio module when
+ * an unhandled serio port is found.
+ */
+static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct psmouse *psmouse, *parent = NULL;
+ struct input_dev *input_dev;
+ int retval = 0, error = -ENOMEM;
+
+ mutex_lock(&psmouse_mutex);
+
+ /*
+ * If this is a pass-through port deactivate parent so the device
+ * connected to this port can be successfully identified
+ */
+ if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+ parent = serio_get_drvdata(serio->parent);
+ psmouse_deactivate(parent);
+ }
+
+ psmouse = kzalloc(sizeof(struct psmouse), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!psmouse || !input_dev)
+ goto err_free;
+
+ ps2_init(&psmouse->ps2dev, serio);
+ INIT_DELAYED_WORK(&psmouse->resync_work, psmouse_resync);
+ psmouse->dev = input_dev;
+ snprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0", serio->phys);
+
+ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+ serio_set_drvdata(serio, psmouse);
+
+ error = serio_open(serio, drv);
+ if (error)
+ goto err_clear_drvdata;
+
+ if (psmouse_probe(psmouse) < 0) {
+ error = -ENODEV;
+ goto err_close_serio;
+ }
+
+ psmouse->rate = psmouse_rate;
+ psmouse->resolution = psmouse_resolution;
+ psmouse->resetafter = psmouse_resetafter;
+ psmouse->resync_time = parent ? 0 : psmouse_resync_time;
+ psmouse->smartscroll = psmouse_smartscroll;
+
+ psmouse_switch_protocol(psmouse, NULL);
+
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ psmouse_initialize(psmouse);
+
+ error = input_register_device(psmouse->dev);
+ if (error)
+ goto err_protocol_disconnect;
+
+ if (parent && parent->pt_activate)
+ parent->pt_activate(parent);
+
+ error = sysfs_create_group(&serio->dev.kobj, &psmouse_attribute_group);
+ if (error)
+ goto err_pt_deactivate;
+
+ psmouse_activate(psmouse);
+
+ out:
+ /* If this is a pass-through port the parent needs to be re-activated */
+ if (parent)
+ psmouse_activate(parent);
+
+ mutex_unlock(&psmouse_mutex);
+ return retval;
+
+ err_pt_deactivate:
+ if (parent && parent->pt_deactivate)
+ parent->pt_deactivate(parent);
+ input_unregister_device(psmouse->dev);
+ input_dev = NULL; /* so we don't try to free it below */
+ err_protocol_disconnect:
+ if (psmouse->disconnect)
+ psmouse->disconnect(psmouse);
+ psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+ err_close_serio:
+ serio_close(serio);
+ err_clear_drvdata:
+ serio_set_drvdata(serio, NULL);
+ err_free:
+ input_free_device(input_dev);
+ kfree(psmouse);
+
+ retval = error;
+ goto out;
+}
+
+
+static int psmouse_reconnect(struct serio *serio)
+{
+ struct psmouse *psmouse = serio_get_drvdata(serio);
+ struct psmouse *parent = NULL;
+ struct serio_driver *drv = serio->drv;
+ int rc = -1;
+
+ if (!drv || !psmouse) {
+ printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n");
+ return -1;
+ }
+
+ mutex_lock(&psmouse_mutex);
+
+ if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+ parent = serio_get_drvdata(serio->parent);
+ psmouse_deactivate(parent);
+ }
+
+ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+ if (psmouse->reconnect) {
+ if (psmouse->reconnect(psmouse))
+ goto out;
+ } else if (psmouse_probe(psmouse) < 0 ||
+ psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0))
+ goto out;
+
+ /* ok, the device type (and capabilities) match the old one,
+ * we can continue using it, complete intialization
+ */
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+ psmouse_initialize(psmouse);
+
+ if (parent && parent->pt_activate)
+ parent->pt_activate(parent);
+
+ psmouse_activate(psmouse);
+ rc = 0;
+
+out:
+ /* If this is a pass-through port the parent waits to be activated */
+ if (parent)
+ psmouse_activate(parent);
+
+ mutex_unlock(&psmouse_mutex);
+ return rc;
+}
+
+static struct serio_device_id psmouse_serio_ids[] = {
+ {
+ .type = SERIO_8042,
+ .proto = SERIO_ANY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_PS_PSTHRU,
+ .proto = SERIO_ANY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, psmouse_serio_ids);
+
+static struct serio_driver psmouse_drv = {
+ .driver = {
+ .name = "psmouse",
+ },
+ .description = DRIVER_DESC,
+ .id_table = psmouse_serio_ids,
+ .interrupt = psmouse_interrupt,
+ .connect = psmouse_connect,
+ .reconnect = psmouse_reconnect,
+ .disconnect = psmouse_disconnect,
+ .cleanup = psmouse_cleanup,
+};
+
+ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct serio *serio = to_serio_port(dev);
+ struct psmouse_attribute *attr = to_psmouse_attr(devattr);
+ struct psmouse *psmouse;
+ int retval;
+
+ retval = serio_pin_driver(serio);
+ if (retval)
+ return retval;
+
+ if (serio->drv != &psmouse_drv) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ psmouse = serio_get_drvdata(serio);
+
+ retval = attr->show(psmouse, attr->data, buf);
+
+out:
+ serio_unpin_driver(serio);
+ return retval;
+}
+
+ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct serio *serio = to_serio_port(dev);
+ struct psmouse_attribute *attr = to_psmouse_attr(devattr);
+ struct psmouse *psmouse, *parent = NULL;
+ int retval;
+
+ retval = serio_pin_driver(serio);
+ if (retval)
+ return retval;
+
+ if (serio->drv != &psmouse_drv) {
+ retval = -ENODEV;
+ goto out_unpin;
+ }
+
+ retval = mutex_lock_interruptible(&psmouse_mutex);
+ if (retval)
+ goto out_unpin;
+
+ psmouse = serio_get_drvdata(serio);
+
+ if (attr->protect) {
+ if (psmouse->state == PSMOUSE_IGNORE) {
+ retval = -ENODEV;
+ goto out_unlock;
+ }
+
+ if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+ parent = serio_get_drvdata(serio->parent);
+ psmouse_deactivate(parent);
+ }
+
+ psmouse_deactivate(psmouse);
+ }
+
+ retval = attr->set(psmouse, attr->data, buf, count);
+
+ if (attr->protect) {
+ if (retval != -ENODEV)
+ psmouse_activate(psmouse);
+
+ if (parent)
+ psmouse_activate(parent);
+ }
+
+ out_unlock:
+ mutex_unlock(&psmouse_mutex);
+ out_unpin:
+ serio_unpin_driver(serio);
+ return retval;
+}
+
+static ssize_t psmouse_show_int_attr(struct psmouse *psmouse, void *offset, char *buf)
+{
+ unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset);
+
+ return sprintf(buf, "%u\n", *field);
+}
+
+static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const char *buf, size_t count)
+{
+ unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset);
+ unsigned long value;
+
+ if (strict_strtoul(buf, 10, &value))
+ return -EINVAL;
+
+ if ((unsigned int)value != value)
+ return -EINVAL;
+
+ *field = value;
+
+ return count;
+}
+
+static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, void *data, char *buf)
+{
+ return sprintf(buf, "%s\n", psmouse_protocol_by_type(psmouse->type)->name);
+}
+
+static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, const char *buf, size_t count)
+{
+ struct serio *serio = psmouse->ps2dev.serio;
+ struct psmouse *parent = NULL;
+ struct input_dev *old_dev, *new_dev;
+ const struct psmouse_protocol *proto, *old_proto;
+ int error;
+ int retry = 0;
+
+ proto = psmouse_protocol_by_name(buf, count);
+ if (!proto)
+ return -EINVAL;
+
+ if (psmouse->type == proto->type)
+ return count;
+
+ new_dev = input_allocate_device();
+ if (!new_dev)
+ return -ENOMEM;
+
+ while (serio->child) {
+ if (++retry > 3) {
+ printk(KERN_WARNING "psmouse: failed to destroy child port, protocol change aborted.\n");
+ input_free_device(new_dev);
+ return -EIO;
+ }
+
+ mutex_unlock(&psmouse_mutex);
+ serio_unpin_driver(serio);
+ serio_unregister_child_port(serio);
+ serio_pin_driver_uninterruptible(serio);
+ mutex_lock(&psmouse_mutex);
+
+ if (serio->drv != &psmouse_drv) {
+ input_free_device(new_dev);
+ return -ENODEV;
+ }
+
+ if (psmouse->type == proto->type) {
+ input_free_device(new_dev);
+ return count; /* switched by other thread */
+ }
+ }
+
+ if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+ parent = serio_get_drvdata(serio->parent);
+ if (parent->pt_deactivate)
+ parent->pt_deactivate(parent);
+ }
+
+ old_dev = psmouse->dev;
+ old_proto = psmouse_protocol_by_type(psmouse->type);
+
+ if (psmouse->disconnect)
+ psmouse->disconnect(psmouse);
+
+ psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+
+ psmouse->dev = new_dev;
+ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+ if (psmouse_switch_protocol(psmouse, proto) < 0) {
+ psmouse_reset(psmouse);
+ /* default to PSMOUSE_PS2 */
+ psmouse_switch_protocol(psmouse, &psmouse_protocols[0]);
+ }
+
+ psmouse_initialize(psmouse);
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+ error = input_register_device(psmouse->dev);
+ if (error) {
+ if (psmouse->disconnect)
+ psmouse->disconnect(psmouse);
+
+ psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+ input_free_device(new_dev);
+ psmouse->dev = old_dev;
+ psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+ psmouse_switch_protocol(psmouse, old_proto);
+ psmouse_initialize(psmouse);
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+ return error;
+ }
+
+ input_unregister_device(old_dev);
+
+ if (parent && parent->pt_activate)
+ parent->pt_activate(parent);
+
+ return count;
+}
+
+static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const char *buf, size_t count)
+{
+ unsigned long value;
+
+ if (strict_strtoul(buf, 10, &value))
+ return -EINVAL;
+
+ psmouse->set_rate(psmouse, value);
+ return count;
+}
+
+static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, const char *buf, size_t count)
+{
+ unsigned long value;
+
+ if (strict_strtoul(buf, 10, &value))
+ return -EINVAL;
+
+ psmouse->set_resolution(psmouse, value);
+ return count;
+}
+
+
+static int psmouse_set_maxproto(const char *val, struct kernel_param *kp)
+{
+ const struct psmouse_protocol *proto;
+
+ if (!val)
+ return -EINVAL;
+
+ proto = psmouse_protocol_by_name(val, strlen(val));
+
+ if (!proto || !proto->maxproto)
+ return -EINVAL;
+
+ *((unsigned int *)kp->arg) = proto->type;
+
+ return 0;
+}
+
+static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp)
+{
+ int type = *((unsigned int *)kp->arg);
+
+ return sprintf(buffer, "%s\n", psmouse_protocol_by_type(type)->name);
+}
+
+static int __init psmouse_init(void)
+{
+ int err;
+
+ kpsmoused_wq = create_singlethread_workqueue("kpsmoused");
+ if (!kpsmoused_wq) {
+ printk(KERN_ERR "psmouse: failed to create kpsmoused workqueue\n");
+ return -ENOMEM;
+ }
+
+ err = serio_register_driver(&psmouse_drv);
+ if (err)
+ destroy_workqueue(kpsmoused_wq);
+
+ return err;
+}
+
+static void __exit psmouse_exit(void)
+{
+ serio_unregister_driver(&psmouse_drv);
+ destroy_workqueue(kpsmoused_wq);
+}
+
+module_init(psmouse_init);
+module_exit(psmouse_exit);
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
new file mode 100644
index 0000000..54ed267
--- /dev/null
+++ b/drivers/input/mouse/psmouse.h
@@ -0,0 +1,140 @@
+#ifndef _PSMOUSE_H
+#define _PSMOUSE_H
+
+#define PSMOUSE_CMD_SETSCALE11 0x00e6
+#define PSMOUSE_CMD_SETSCALE21 0x00e7
+#define PSMOUSE_CMD_SETRES 0x10e8
+#define PSMOUSE_CMD_GETINFO 0x03e9
+#define PSMOUSE_CMD_SETSTREAM 0x00ea
+#define PSMOUSE_CMD_SETPOLL 0x00f0
+#define PSMOUSE_CMD_POLL 0x00eb /* caller sets number of bytes to receive */
+#define PSMOUSE_CMD_GETID 0x02f2
+#define PSMOUSE_CMD_SETRATE 0x10f3
+#define PSMOUSE_CMD_ENABLE 0x00f4
+#define PSMOUSE_CMD_DISABLE 0x00f5
+#define PSMOUSE_CMD_RESET_DIS 0x00f6
+#define PSMOUSE_CMD_RESET_BAT 0x02ff
+
+#define PSMOUSE_RET_BAT 0xaa
+#define PSMOUSE_RET_ID 0x00
+#define PSMOUSE_RET_ACK 0xfa
+#define PSMOUSE_RET_NAK 0xfe
+
+enum psmouse_state {
+ PSMOUSE_IGNORE,
+ PSMOUSE_INITIALIZING,
+ PSMOUSE_RESYNCING,
+ PSMOUSE_CMD_MODE,
+ PSMOUSE_ACTIVATED,
+};
+
+/* psmouse protocol handler return codes */
+typedef enum {
+ PSMOUSE_BAD_DATA,
+ PSMOUSE_GOOD_DATA,
+ PSMOUSE_FULL_PACKET
+} psmouse_ret_t;
+
+struct psmouse {
+ void *private;
+ struct input_dev *dev;
+ struct ps2dev ps2dev;
+ struct delayed_work resync_work;
+ char *vendor;
+ char *name;
+ unsigned char packet[8];
+ unsigned char badbyte;
+ unsigned char pktcnt;
+ unsigned char pktsize;
+ unsigned char type;
+ unsigned char acks_disable_command;
+ unsigned int model;
+ unsigned long last;
+ unsigned long out_of_sync;
+ unsigned long num_resyncs;
+ enum psmouse_state state;
+ char devname[64];
+ char phys[32];
+
+ unsigned int rate;
+ unsigned int resolution;
+ unsigned int resetafter;
+ unsigned int resync_time;
+ unsigned int smartscroll; /* Logitech only */
+
+ psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse);
+ void (*set_rate)(struct psmouse *psmouse, unsigned int rate);
+ void (*set_resolution)(struct psmouse *psmouse, unsigned int resolution);
+
+ int (*reconnect)(struct psmouse *psmouse);
+ void (*disconnect)(struct psmouse *psmouse);
+ void (*cleanup)(struct psmouse *psmouse);
+ int (*poll)(struct psmouse *psmouse);
+
+ void (*pt_activate)(struct psmouse *psmouse);
+ void (*pt_deactivate)(struct psmouse *psmouse);
+};
+
+enum psmouse_type {
+ PSMOUSE_NONE,
+ PSMOUSE_PS2,
+ PSMOUSE_PS2PP,
+ PSMOUSE_THINKPS,
+ PSMOUSE_GENPS,
+ PSMOUSE_IMPS,
+ PSMOUSE_IMEX,
+ PSMOUSE_SYNAPTICS,
+ PSMOUSE_ALPS,
+ PSMOUSE_LIFEBOOK,
+ PSMOUSE_TRACKPOINT,
+ PSMOUSE_TOUCHKIT_PS2,
+ PSMOUSE_CORTRON,
+ PSMOUSE_HGPK,
+ PSMOUSE_ELANTECH,
+ PSMOUSE_AUTO /* This one should always be last */
+};
+
+void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
+ unsigned long delay);
+int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
+int psmouse_reset(struct psmouse *psmouse);
+void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state);
+void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
+
+struct psmouse_attribute {
+ struct device_attribute dattr;
+ void *data;
+ ssize_t (*show)(struct psmouse *psmouse, void *data, char *buf);
+ ssize_t (*set)(struct psmouse *psmouse, void *data,
+ const char *buf, size_t count);
+ int protect;
+};
+#define to_psmouse_attr(a) container_of((a), struct psmouse_attribute, dattr)
+
+ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *attr,
+ char *buf);
+ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+
+#define __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, _protect) \
+static ssize_t _show(struct psmouse *, void *data, char *); \
+static ssize_t _set(struct psmouse *, void *data, const char *, size_t); \
+static struct psmouse_attribute psmouse_attr_##_name = { \
+ .dattr = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = _mode, \
+ }, \
+ .show = psmouse_attr_show_helper, \
+ .store = psmouse_attr_set_helper, \
+ }, \
+ .data = _data, \
+ .show = _show, \
+ .set = _set, \
+ .protect = _protect, \
+}
+
+#define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set) \
+ __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, 1)
+
+#endif /* _PSMOUSE_H */
diff --git a/drivers/input/mouse/rpcmouse.c b/drivers/input/mouse/rpcmouse.c
new file mode 100644
index 0000000..56c079e
--- /dev/null
+++ b/drivers/input/mouse/rpcmouse.c
@@ -0,0 +1,116 @@
+/*
+ * Acorn RiscPC mouse driver for Linux/ARM
+ *
+ * Copyright (c) 2000-2002 Vojtech Pavlik
+ * Copyright (C) 1996-2002 Russell King
+ *
+ */
+
+/*
+ * 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.
+ *
+ * This handles the Acorn RiscPCs mouse. We basically have a couple of
+ * hardware registers that track the sensor count for the X-Y movement and
+ * another register holding the button state. On every VSYNC interrupt we read
+ * the complete state and then work out if something has changed.
+ */
+
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/hardware/iomd.h>
+
+MODULE_AUTHOR("Vojtech Pavlik, Russell King");
+MODULE_DESCRIPTION("Acorn RiscPC mouse driver");
+MODULE_LICENSE("GPL");
+
+static short rpcmouse_lastx, rpcmouse_lasty;
+static struct input_dev *rpcmouse_dev;
+
+static irqreturn_t rpcmouse_irq(int irq, void *dev_id)
+{
+ struct input_dev *dev = dev_id;
+ short x, y, dx, dy, b;
+
+ x = (short) iomd_readl(IOMD_MOUSEX);
+ y = (short) iomd_readl(IOMD_MOUSEY);
+ b = (short) (__raw_readl(0xe0310000) ^ 0x70);
+
+ dx = x - rpcmouse_lastx;
+ dy = y - rpcmouse_lasty;
+
+ rpcmouse_lastx = x;
+ rpcmouse_lasty = y;
+
+ input_report_rel(dev, REL_X, dx);
+ input_report_rel(dev, REL_Y, -dy);
+
+ input_report_key(dev, BTN_LEFT, b & 0x40);
+ input_report_key(dev, BTN_MIDDLE, b & 0x20);
+ input_report_key(dev, BTN_RIGHT, b & 0x10);
+
+ input_sync(dev);
+
+ return IRQ_HANDLED;
+}
+
+
+static int __init rpcmouse_init(void)
+{
+ int err;
+
+ rpcmouse_dev = input_allocate_device();
+ if (!rpcmouse_dev)
+ return -ENOMEM;
+
+ rpcmouse_dev->name = "Acorn RiscPC Mouse";
+ rpcmouse_dev->phys = "rpcmouse/input0";
+ rpcmouse_dev->id.bustype = BUS_HOST;
+ rpcmouse_dev->id.vendor = 0x0005;
+ rpcmouse_dev->id.product = 0x0001;
+ rpcmouse_dev->id.version = 0x0100;
+
+ rpcmouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ rpcmouse_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+ rpcmouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+ rpcmouse_lastx = (short) iomd_readl(IOMD_MOUSEX);
+ rpcmouse_lasty = (short) iomd_readl(IOMD_MOUSEY);
+
+ if (request_irq(IRQ_VSYNCPULSE, rpcmouse_irq, IRQF_SHARED, "rpcmouse", rpcmouse_dev)) {
+ printk(KERN_ERR "rpcmouse: unable to allocate VSYNC interrupt\n");
+ err = -EBUSY;
+ goto err_free_dev;
+ }
+
+ err = input_register_device(rpcmouse_dev);
+ if (err)
+ goto err_free_irq;
+
+ return 0;
+
+ err_free_irq:
+ free_irq(IRQ_VSYNCPULSE, rpcmouse_dev);
+ err_free_dev:
+ input_free_device(rpcmouse_dev);
+
+ return err;
+}
+
+static void __exit rpcmouse_exit(void)
+{
+ free_irq(IRQ_VSYNCPULSE, rpcmouse_dev);
+ input_unregister_device(rpcmouse_dev);
+}
+
+module_init(rpcmouse_init);
+module_exit(rpcmouse_exit);
diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c
new file mode 100644
index 0000000..17ff137
--- /dev/null
+++ b/drivers/input/mouse/sermouse.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Serial mouse driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Serial mouse driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static const char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse",
+ "Logitech M+ Mouse", "Microsoft MZ Mouse", "Logitech MZ+ Mouse",
+ "Logitech MZ++ Mouse"};
+
+struct sermouse {
+ struct input_dev *dev;
+ signed char buf[8];
+ unsigned char count;
+ unsigned char type;
+ unsigned long last;
+ char phys[32];
+};
+
+/*
+ * sermouse_process_msc() analyzes the incoming MSC/Sun bytestream and
+ * applies some prediction to the data, resulting in 96 updates per
+ * second, which is as good as a PS/2 or USB mouse.
+ */
+
+static void sermouse_process_msc(struct sermouse *sermouse, signed char data)
+{
+ struct input_dev *dev = sermouse->dev;
+ signed char *buf = sermouse->buf;
+
+ switch (sermouse->count) {
+
+ case 0:
+ if ((data & 0xf8) != 0x80)
+ return;
+ input_report_key(dev, BTN_LEFT, !(data & 4));
+ input_report_key(dev, BTN_RIGHT, !(data & 1));
+ input_report_key(dev, BTN_MIDDLE, !(data & 2));
+ break;
+
+ case 1:
+ case 3:
+ input_report_rel(dev, REL_X, data / 2);
+ input_report_rel(dev, REL_Y, -buf[1]);
+ buf[0] = data - data / 2;
+ break;
+
+ case 2:
+ case 4:
+ input_report_rel(dev, REL_X, buf[0]);
+ input_report_rel(dev, REL_Y, buf[1] - data);
+ buf[1] = data / 2;
+ break;
+ }
+
+ input_sync(dev);
+
+ if (++sermouse->count == 5)
+ sermouse->count = 0;
+}
+
+/*
+ * sermouse_process_ms() anlyzes the incoming MS(Z/+/++) bytestream and
+ * generates events. With prediction it gets 80 updates/sec, assuming
+ * standard 3-byte packets and 1200 bps.
+ */
+
+static void sermouse_process_ms(struct sermouse *sermouse, signed char data)
+{
+ struct input_dev *dev = sermouse->dev;
+ signed char *buf = sermouse->buf;
+
+ if (data & 0x40)
+ sermouse->count = 0;
+ else if (sermouse->count == 0)
+ return;
+
+ switch (sermouse->count) {
+
+ case 0:
+ buf[1] = data;
+ input_report_key(dev, BTN_LEFT, (data >> 5) & 1);
+ input_report_key(dev, BTN_RIGHT, (data >> 4) & 1);
+ break;
+
+ case 1:
+ buf[2] = data;
+ data = (signed char) (((buf[1] << 6) & 0xc0) | (data & 0x3f));
+ input_report_rel(dev, REL_X, data / 2);
+ input_report_rel(dev, REL_Y, buf[4]);
+ buf[3] = data - data / 2;
+ break;
+
+ case 2:
+ /* Guessing the state of the middle button on 3-button MS-protocol mice - ugly. */
+ if ((sermouse->type == SERIO_MS) && !data && !buf[2] && !((buf[0] & 0xf0) ^ buf[1]))
+ input_report_key(dev, BTN_MIDDLE, !test_bit(BTN_MIDDLE, dev->key));
+ buf[0] = buf[1];
+
+ data = (signed char) (((buf[1] << 4) & 0xc0) | (data & 0x3f));
+ input_report_rel(dev, REL_X, buf[3]);
+ input_report_rel(dev, REL_Y, data - buf[4]);
+ buf[4] = data / 2;
+ break;
+
+ case 3:
+
+ switch (sermouse->type) {
+
+ case SERIO_MS:
+ sermouse->type = SERIO_MP;
+
+ case SERIO_MP:
+ if ((data >> 2) & 3) break; /* M++ Wireless Extension packet. */
+ input_report_key(dev, BTN_MIDDLE, (data >> 5) & 1);
+ input_report_key(dev, BTN_SIDE, (data >> 4) & 1);
+ break;
+
+ case SERIO_MZP:
+ case SERIO_MZPP:
+ input_report_key(dev, BTN_SIDE, (data >> 5) & 1);
+
+ case SERIO_MZ:
+ input_report_key(dev, BTN_MIDDLE, (data >> 4) & 1);
+ input_report_rel(dev, REL_WHEEL, (data & 8) - (data & 7));
+ break;
+ }
+
+ break;
+
+ case 4:
+ case 6: /* MZ++ packet type. We can get these bytes for M++ too but we ignore them later. */
+ buf[1] = (data >> 2) & 0x0f;
+ break;
+
+ case 5:
+ case 7: /* Ignore anything besides MZ++ */
+ if (sermouse->type != SERIO_MZPP)
+ break;
+
+ switch (buf[1]) {
+
+ case 1: /* Extra mouse info */
+
+ input_report_key(dev, BTN_SIDE, (data >> 4) & 1);
+ input_report_key(dev, BTN_EXTRA, (data >> 5) & 1);
+ input_report_rel(dev, data & 0x80 ? REL_HWHEEL : REL_WHEEL, (data & 7) - (data & 8));
+
+ break;
+
+ default: /* We don't decode anything else yet. */
+
+ printk(KERN_WARNING
+ "sermouse.c: Received MZ++ packet %x, don't know how to handle.\n", buf[1]);
+ break;
+ }
+
+ break;
+ }
+
+ input_sync(dev);
+
+ sermouse->count++;
+}
+
+/*
+ * sermouse_interrupt() handles incoming characters, either gathering them into
+ * packets or passing them to the command routine as command output.
+ */
+
+static irqreturn_t sermouse_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct sermouse *sermouse = serio_get_drvdata(serio);
+
+ if (time_after(jiffies, sermouse->last + HZ/10))
+ sermouse->count = 0;
+
+ sermouse->last = jiffies;
+
+ if (sermouse->type > SERIO_SUN)
+ sermouse_process_ms(sermouse, data);
+ else
+ sermouse_process_msc(sermouse, data);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * sermouse_disconnect() cleans up after we don't want talk
+ * to the mouse anymore.
+ */
+
+static void sermouse_disconnect(struct serio *serio)
+{
+ struct sermouse *sermouse = serio_get_drvdata(serio);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(sermouse->dev);
+ kfree(sermouse);
+}
+
+/*
+ * sermouse_connect() is a callback form the serio module when
+ * an unhandled serio port is found.
+ */
+
+static int sermouse_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct sermouse *sermouse;
+ struct input_dev *input_dev;
+ unsigned char c = serio->id.extra;
+ int err = -ENOMEM;
+
+ sermouse = kzalloc(sizeof(struct sermouse), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!sermouse || !input_dev)
+ goto fail1;
+
+ sermouse->dev = input_dev;
+ snprintf(sermouse->phys, sizeof(sermouse->phys), "%s/input0", serio->phys);
+ sermouse->type = serio->id.proto;
+
+ input_dev->name = sermouse_protocols[sermouse->type];
+ input_dev->phys = sermouse->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = sermouse->type;
+ input_dev->id.product = c;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_RIGHT);
+ input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+ if (c & 0x01) set_bit(BTN_MIDDLE, input_dev->keybit);
+ if (c & 0x02) set_bit(BTN_SIDE, input_dev->keybit);
+ if (c & 0x04) set_bit(BTN_EXTRA, input_dev->keybit);
+ if (c & 0x10) set_bit(REL_WHEEL, input_dev->relbit);
+ if (c & 0x20) set_bit(REL_HWHEEL, input_dev->relbit);
+
+ serio_set_drvdata(serio, sermouse);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(sermouse->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(sermouse);
+ return err;
+}
+
+static struct serio_device_id sermouse_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_MSC,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_SUN,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_MS,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_MP,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_MZ,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_MZP,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_MZPP,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, sermouse_serio_ids);
+
+static struct serio_driver sermouse_drv = {
+ .driver = {
+ .name = "sermouse",
+ },
+ .description = DRIVER_DESC,
+ .id_table = sermouse_serio_ids,
+ .interrupt = sermouse_interrupt,
+ .connect = sermouse_connect,
+ .disconnect = sermouse_disconnect,
+};
+
+static int __init sermouse_init(void)
+{
+ return serio_register_driver(&sermouse_drv);
+}
+
+static void __exit sermouse_exit(void)
+{
+ serio_unregister_driver(&sermouse_drv);
+}
+
+module_init(sermouse_init);
+module_exit(sermouse_exit);
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
new file mode 100644
index 0000000..d349c4a
--- /dev/null
+++ b/drivers/input/mouse/synaptics.c
@@ -0,0 +1,708 @@
+/*
+ * Synaptics TouchPad PS/2 mouse driver
+ *
+ * 2003 Dmitry Torokhov <dtor@mail.ru>
+ * Added support for pass-through port. Special thanks to Peter Berg Larsen
+ * for explaining various Synaptics quirks.
+ *
+ * 2003 Peter Osterlund <petero2@telia.com>
+ * Ported to 2.5 input device infrastructure.
+ *
+ * Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch>
+ * start merging tpconfig and gpm code to a xfree-input module
+ * adding some changes and extensions (ex. 3rd and 4th button)
+ *
+ * Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu>
+ * Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com>
+ * code for the special synaptics commands (from the tpconfig-source)
+ *
+ * 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.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include "psmouse.h"
+#include "synaptics.h"
+
+/*
+ * The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
+ * section 2.3.2, which says that they should be valid regardless of the
+ * actual size of the sensor.
+ */
+#define XMIN_NOMINAL 1472
+#define XMAX_NOMINAL 5472
+#define YMIN_NOMINAL 1408
+#define YMAX_NOMINAL 4448
+
+
+/*****************************************************************************
+ * Stuff we need even when we do not want native Synaptics support
+ ****************************************************************************/
+
+/*
+ * Set the synaptics touchpad mode byte by special commands
+ */
+static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
+{
+ unsigned char param[1];
+
+ if (psmouse_sliced_command(psmouse, mode))
+ return -1;
+ param[0] = SYN_PS_SET_MODE2;
+ if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE))
+ return -1;
+ return 0;
+}
+
+int synaptics_detect(struct psmouse *psmouse, int set_properties)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[4];
+
+ param[0] = 0;
+
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+ ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
+
+ if (param[1] != 0x47)
+ return -ENODEV;
+
+ if (set_properties) {
+ psmouse->vendor = "Synaptics";
+ psmouse->name = "TouchPad";
+ }
+
+ return 0;
+}
+
+void synaptics_reset(struct psmouse *psmouse)
+{
+ /* reset touchpad back to relative mode, gestures enabled */
+ synaptics_mode_cmd(psmouse, 0);
+}
+
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
+
+/*****************************************************************************
+ * Synaptics communications functions
+ ****************************************************************************/
+
+/*
+ * Send a command to the synpatics touchpad by special commands
+ */
+static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
+{
+ if (psmouse_sliced_command(psmouse, c))
+ return -1;
+ if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
+ return -1;
+ return 0;
+}
+
+/*
+ * Read the model-id bytes from the touchpad
+ * see also SYN_MODEL_* macros
+ */
+static int synaptics_model_id(struct psmouse *psmouse)
+{
+ struct synaptics_data *priv = psmouse->private;
+ unsigned char mi[3];
+
+ if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi))
+ return -1;
+ priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2];
+ return 0;
+}
+
+/*
+ * Read the capability-bits from the touchpad
+ * see also the SYN_CAP_* macros
+ */
+static int synaptics_capability(struct psmouse *psmouse)
+{
+ struct synaptics_data *priv = psmouse->private;
+ unsigned char cap[3];
+
+ if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
+ return -1;
+ priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+ priv->ext_cap = 0;
+ if (!SYN_CAP_VALID(priv->capabilities))
+ return -1;
+
+ /*
+ * Unless capExtended is set the rest of the flags should be ignored
+ */
+ if (!SYN_CAP_EXTENDED(priv->capabilities))
+ priv->capabilities = 0;
+
+ if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
+ if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) {
+ printk(KERN_ERR "Synaptics claims to have extended capabilities,"
+ " but I'm not able to read them.");
+ } else {
+ priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+
+ /*
+ * if nExtBtn is greater than 8 it should be considered
+ * invalid and treated as 0
+ */
+ if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8)
+ priv->ext_cap &= 0xff0fff;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Identify Touchpad
+ * See also the SYN_ID_* macros
+ */
+static int synaptics_identify(struct psmouse *psmouse)
+{
+ struct synaptics_data *priv = psmouse->private;
+ unsigned char id[3];
+
+ if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id))
+ return -1;
+ priv->identity = (id[0]<<16) | (id[1]<<8) | id[2];
+ if (SYN_ID_IS_SYNAPTICS(priv->identity))
+ return 0;
+ return -1;
+}
+
+static int synaptics_query_hardware(struct psmouse *psmouse)
+{
+ int retries = 0;
+
+ while ((retries++ < 3) && psmouse_reset(psmouse))
+ /* empty */;
+
+ if (synaptics_identify(psmouse))
+ return -1;
+ if (synaptics_model_id(psmouse))
+ return -1;
+ if (synaptics_capability(psmouse))
+ return -1;
+
+ return 0;
+}
+
+static int synaptics_set_absolute_mode(struct psmouse *psmouse)
+{
+ struct synaptics_data *priv = psmouse->private;
+
+ priv->mode = SYN_BIT_ABSOLUTE_MODE;
+ if (SYN_ID_MAJOR(priv->identity) >= 4)
+ priv->mode |= SYN_BIT_DISABLE_GESTURE;
+ if (SYN_CAP_EXTENDED(priv->capabilities))
+ priv->mode |= SYN_BIT_W_MODE;
+
+ if (synaptics_mode_cmd(psmouse, priv->mode))
+ return -1;
+
+ return 0;
+}
+
+static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+ struct synaptics_data *priv = psmouse->private;
+
+ if (rate >= 80) {
+ priv->mode |= SYN_BIT_HIGH_RATE;
+ psmouse->rate = 80;
+ } else {
+ priv->mode &= ~SYN_BIT_HIGH_RATE;
+ psmouse->rate = 40;
+ }
+
+ synaptics_mode_cmd(psmouse, priv->mode);
+}
+
+/*****************************************************************************
+ * Synaptics pass-through PS/2 port support
+ ****************************************************************************/
+static int synaptics_pt_write(struct serio *serio, unsigned char c)
+{
+ struct psmouse *parent = serio_get_drvdata(serio->parent);
+ char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
+
+ if (psmouse_sliced_command(parent, c))
+ return -1;
+ if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE))
+ return -1;
+ return 0;
+}
+
+static inline int synaptics_is_pt_packet(unsigned char *buf)
+{
+ return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
+}
+
+static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet)
+{
+ struct psmouse *child = serio_get_drvdata(ptport);
+
+ if (child && child->state == PSMOUSE_ACTIVATED) {
+ serio_interrupt(ptport, packet[1], 0);
+ serio_interrupt(ptport, packet[4], 0);
+ serio_interrupt(ptport, packet[5], 0);
+ if (child->pktsize == 4)
+ serio_interrupt(ptport, packet[2], 0);
+ } else
+ serio_interrupt(ptport, packet[1], 0);
+}
+
+static void synaptics_pt_activate(struct psmouse *psmouse)
+{
+ struct serio *ptport = psmouse->ps2dev.serio->child;
+ struct psmouse *child = serio_get_drvdata(ptport);
+ struct synaptics_data *priv = psmouse->private;
+
+ /* adjust the touchpad to child's choice of protocol */
+ if (child) {
+ if (child->pktsize == 4)
+ priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT;
+ else
+ priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT;
+
+ if (synaptics_mode_cmd(psmouse, priv->mode))
+ printk(KERN_INFO "synaptics: failed to switch guest protocol\n");
+ }
+}
+
+static void synaptics_pt_create(struct psmouse *psmouse)
+{
+ struct serio *serio;
+
+ serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!serio) {
+ printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n");
+ return;
+ }
+
+ serio->id.type = SERIO_PS_PSTHRU;
+ strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
+ strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
+ serio->write = synaptics_pt_write;
+ serio->parent = psmouse->ps2dev.serio;
+
+ psmouse->pt_activate = synaptics_pt_activate;
+
+ printk(KERN_INFO "serio: %s port at %s\n", serio->name, psmouse->phys);
+ serio_register_port(serio);
+}
+
+/*****************************************************************************
+ * Functions to interpret the absolute mode packets
+ ****************************************************************************/
+
+static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw)
+{
+ memset(hw, 0, sizeof(struct synaptics_hw_state));
+
+ if (SYN_MODEL_NEWABS(priv->model_id)) {
+ hw->x = (((buf[3] & 0x10) << 8) |
+ ((buf[1] & 0x0f) << 8) |
+ buf[4]);
+ hw->y = (((buf[3] & 0x20) << 7) |
+ ((buf[1] & 0xf0) << 4) |
+ buf[5]);
+
+ hw->z = buf[2];
+ hw->w = (((buf[0] & 0x30) >> 2) |
+ ((buf[0] & 0x04) >> 1) |
+ ((buf[3] & 0x04) >> 2));
+
+ hw->left = (buf[0] & 0x01) ? 1 : 0;
+ hw->right = (buf[0] & 0x02) ? 1 : 0;
+
+ if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+ hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+ if (hw->w == 2)
+ hw->scroll = (signed char)(buf[1]);
+ }
+
+ if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+ hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+ hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
+ }
+
+ if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
+ ((buf[0] ^ buf[3]) & 0x02)) {
+ switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
+ default:
+ /*
+ * if nExtBtn is greater than 8 it should be
+ * considered invalid and treated as 0
+ */
+ break;
+ case 8:
+ hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0;
+ hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0;
+ case 6:
+ hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0;
+ hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0;
+ case 4:
+ hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0;
+ hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0;
+ case 2:
+ hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0;
+ hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0;
+ }
+ }
+ } else {
+ hw->x = (((buf[1] & 0x1f) << 8) | buf[2]);
+ hw->y = (((buf[4] & 0x1f) << 8) | buf[5]);
+
+ hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F));
+ hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1));
+
+ hw->left = (buf[0] & 0x01) ? 1 : 0;
+ hw->right = (buf[0] & 0x02) ? 1 : 0;
+ }
+}
+
+/*
+ * called for each full received packet from the touchpad
+ */
+static void synaptics_process_packet(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct synaptics_data *priv = psmouse->private;
+ struct synaptics_hw_state hw;
+ int num_fingers;
+ int finger_width;
+ int i;
+
+ synaptics_parse_hw_state(psmouse->packet, priv, &hw);
+
+ if (hw.scroll) {
+ priv->scroll += hw.scroll;
+
+ while (priv->scroll >= 4) {
+ input_report_key(dev, BTN_BACK, !hw.down);
+ input_sync(dev);
+ input_report_key(dev, BTN_BACK, hw.down);
+ input_sync(dev);
+ priv->scroll -= 4;
+ }
+ while (priv->scroll <= -4) {
+ input_report_key(dev, BTN_FORWARD, !hw.up);
+ input_sync(dev);
+ input_report_key(dev, BTN_FORWARD, hw.up);
+ input_sync(dev);
+ priv->scroll += 4;
+ }
+ return;
+ }
+
+ if (hw.z > 0) {
+ num_fingers = 1;
+ finger_width = 5;
+ if (SYN_CAP_EXTENDED(priv->capabilities)) {
+ switch (hw.w) {
+ case 0 ... 1:
+ if (SYN_CAP_MULTIFINGER(priv->capabilities))
+ num_fingers = hw.w + 2;
+ break;
+ case 2:
+ if (SYN_MODEL_PEN(priv->model_id))
+ ; /* Nothing, treat a pen as a single finger */
+ break;
+ case 4 ... 15:
+ if (SYN_CAP_PALMDETECT(priv->capabilities))
+ finger_width = hw.w;
+ break;
+ }
+ }
+ } else {
+ num_fingers = 0;
+ finger_width = 0;
+ }
+
+ /* Post events
+ * BTN_TOUCH has to be first as mousedev relies on it when doing
+ * absolute -> relative conversion
+ */
+ if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1);
+ if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0);
+
+ if (hw.z > 0) {
+ input_report_abs(dev, ABS_X, hw.x);
+ input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
+ }
+ input_report_abs(dev, ABS_PRESSURE, hw.z);
+
+ input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
+ input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
+ input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
+ input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
+
+ input_report_key(dev, BTN_LEFT, hw.left);
+ input_report_key(dev, BTN_RIGHT, hw.right);
+
+ if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+ input_report_key(dev, BTN_MIDDLE, hw.middle);
+
+ if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+ input_report_key(dev, BTN_FORWARD, hw.up);
+ input_report_key(dev, BTN_BACK, hw.down);
+ }
+
+ for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+ input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i));
+
+ input_sync(dev);
+}
+
+static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type)
+{
+ static const unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
+ static const unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
+ static const unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
+ static const unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
+ static const unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
+
+ if (idx < 0 || idx > 4)
+ return 0;
+
+ switch (pkt_type) {
+ case SYN_NEWABS:
+ case SYN_NEWABS_RELAXED:
+ return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx];
+
+ case SYN_NEWABS_STRICT:
+ return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx];
+
+ case SYN_OLDABS:
+ return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx];
+
+ default:
+ printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type);
+ return 0;
+ }
+}
+
+static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse)
+{
+ int i;
+
+ for (i = 0; i < 5; i++)
+ if (!synaptics_validate_byte(psmouse->packet, i, SYN_NEWABS_STRICT)) {
+ printk(KERN_INFO "synaptics: using relaxed packet validation\n");
+ return SYN_NEWABS_RELAXED;
+ }
+
+ return SYN_NEWABS_STRICT;
+}
+
+static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
+{
+ struct synaptics_data *priv = psmouse->private;
+
+ if (psmouse->pktcnt >= 6) { /* Full packet received */
+ if (unlikely(priv->pkt_type == SYN_NEWABS))
+ priv->pkt_type = synaptics_detect_pkt_type(psmouse);
+
+ if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
+ if (psmouse->ps2dev.serio->child)
+ synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet);
+ } else
+ synaptics_process_packet(psmouse);
+
+ return PSMOUSE_FULL_PACKET;
+ }
+
+ return synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type) ?
+ PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+}
+
+/*****************************************************************************
+ * Driver initialization/cleanup functions
+ ****************************************************************************/
+static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
+{
+ int i;
+
+ set_bit(EV_ABS, dev->evbit);
+ input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
+ input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
+ input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+ set_bit(ABS_TOOL_WIDTH, dev->absbit);
+
+ set_bit(EV_KEY, dev->evbit);
+ set_bit(BTN_TOUCH, dev->keybit);
+ set_bit(BTN_TOOL_FINGER, dev->keybit);
+ set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+ set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+
+ set_bit(BTN_LEFT, dev->keybit);
+ set_bit(BTN_RIGHT, dev->keybit);
+
+ if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+ set_bit(BTN_MIDDLE, dev->keybit);
+
+ if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
+ SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+ set_bit(BTN_FORWARD, dev->keybit);
+ set_bit(BTN_BACK, dev->keybit);
+ }
+
+ for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+ set_bit(BTN_0 + i, dev->keybit);
+
+ clear_bit(EV_REL, dev->evbit);
+ clear_bit(REL_X, dev->relbit);
+ clear_bit(REL_Y, dev->relbit);
+}
+
+static void synaptics_disconnect(struct psmouse *psmouse)
+{
+ synaptics_reset(psmouse);
+ kfree(psmouse->private);
+ psmouse->private = NULL;
+}
+
+static int synaptics_reconnect(struct psmouse *psmouse)
+{
+ struct synaptics_data *priv = psmouse->private;
+ struct synaptics_data old_priv = *priv;
+
+ if (synaptics_detect(psmouse, 0))
+ return -1;
+
+ if (synaptics_query_hardware(psmouse)) {
+ printk(KERN_ERR "Unable to query Synaptics hardware.\n");
+ return -1;
+ }
+
+ if (old_priv.identity != priv->identity ||
+ old_priv.model_id != priv->model_id ||
+ old_priv.capabilities != priv->capabilities ||
+ old_priv.ext_cap != priv->ext_cap)
+ return -1;
+
+ if (synaptics_set_absolute_mode(psmouse)) {
+ printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+#if defined(__i386__)
+#include <linux/dmi.h>
+static const struct dmi_system_id toshiba_dmi_table[] = {
+ {
+ .ident = "Toshiba Satellite",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"),
+ },
+ },
+ {
+ .ident = "Toshiba Dynabook",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"),
+ },
+ },
+ {
+ .ident = "Toshiba Portege M300",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"),
+ },
+ },
+ { }
+};
+#endif
+
+int synaptics_init(struct psmouse *psmouse)
+{
+ struct synaptics_data *priv;
+
+ psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
+ if (!priv)
+ return -1;
+
+ if (synaptics_query_hardware(psmouse)) {
+ printk(KERN_ERR "Unable to query Synaptics hardware.\n");
+ goto init_fail;
+ }
+
+ if (synaptics_set_absolute_mode(psmouse)) {
+ printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
+ goto init_fail;
+ }
+
+ priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
+
+ printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx\n",
+ SYN_ID_MODEL(priv->identity),
+ SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
+ priv->model_id, priv->capabilities, priv->ext_cap);
+
+ set_input_params(psmouse->dev, priv);
+
+ /*
+ * Encode touchpad model so that it can be used to set
+ * input device->id.version and be visible to userspace.
+ * Because version is __u16 we have to drop something.
+ * Hardware info bits seem to be good candidates as they
+ * are documented to be for Synaptics corp. internal use.
+ */
+ psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) |
+ (priv->model_id & 0x000000ff);
+
+ psmouse->protocol_handler = synaptics_process_byte;
+ psmouse->set_rate = synaptics_set_rate;
+ psmouse->disconnect = synaptics_disconnect;
+ psmouse->reconnect = synaptics_reconnect;
+ psmouse->cleanup = synaptics_reset;
+ psmouse->pktsize = 6;
+ /* Synaptics can usually stay in sync without extra help */
+ psmouse->resync_time = 0;
+
+ if (SYN_CAP_PASS_THROUGH(priv->capabilities))
+ synaptics_pt_create(psmouse);
+
+#if defined(__i386__)
+ /*
+ * Toshiba's KBC seems to have trouble handling data from
+ * Synaptics as full rate, switch to lower rate which is roughly
+ * thye same as rate of standard PS/2 mouse.
+ */
+ if (psmouse->rate >= 80 && dmi_check_system(toshiba_dmi_table)) {
+ printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n",
+ dmi_get_system_info(DMI_PRODUCT_NAME));
+ psmouse->rate = 40;
+ }
+#endif
+
+ return 0;
+
+ init_fail:
+ kfree(priv);
+ return -1;
+}
+
+#else /* CONFIG_MOUSE_PS2_SYNAPTICS */
+
+int synaptics_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
+
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
new file mode 100644
index 0000000..02aa4cf
--- /dev/null
+++ b/drivers/input/mouse/synaptics.h
@@ -0,0 +1,110 @@
+/*
+ * Synaptics TouchPad PS/2 mouse driver
+ *
+ * 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 _SYNAPTICS_H
+#define _SYNAPTICS_H
+
+/* synaptics queries */
+#define SYN_QUE_IDENTIFY 0x00
+#define SYN_QUE_MODES 0x01
+#define SYN_QUE_CAPABILITIES 0x02
+#define SYN_QUE_MODEL 0x03
+#define SYN_QUE_SERIAL_NUMBER_PREFIX 0x06
+#define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07
+#define SYN_QUE_RESOLUTION 0x08
+#define SYN_QUE_EXT_CAPAB 0x09
+
+/* synatics modes */
+#define SYN_BIT_ABSOLUTE_MODE (1 << 7)
+#define SYN_BIT_HIGH_RATE (1 << 6)
+#define SYN_BIT_SLEEP_MODE (1 << 3)
+#define SYN_BIT_DISABLE_GESTURE (1 << 2)
+#define SYN_BIT_FOUR_BYTE_CLIENT (1 << 1)
+#define SYN_BIT_W_MODE (1 << 0)
+
+/* synaptics model ID bits */
+#define SYN_MODEL_ROT180(m) ((m) & (1 << 23))
+#define SYN_MODEL_PORTRAIT(m) ((m) & (1 << 22))
+#define SYN_MODEL_SENSOR(m) (((m) >> 16) & 0x3f)
+#define SYN_MODEL_HARDWARE(m) (((m) >> 9) & 0x7f)
+#define SYN_MODEL_NEWABS(m) ((m) & (1 << 7))
+#define SYN_MODEL_PEN(m) ((m) & (1 << 6))
+#define SYN_MODEL_SIMPLIC(m) ((m) & (1 << 5))
+#define SYN_MODEL_GEOMETRY(m) ((m) & 0x0f)
+
+/* synaptics capability bits */
+#define SYN_CAP_EXTENDED(c) ((c) & (1 << 23))
+#define SYN_CAP_MIDDLE_BUTTON(c) ((c) & (1 << 18))
+#define SYN_CAP_PASS_THROUGH(c) ((c) & (1 << 7))
+#define SYN_CAP_SLEEP(c) ((c) & (1 << 4))
+#define SYN_CAP_FOUR_BUTTON(c) ((c) & (1 << 3))
+#define SYN_CAP_MULTIFINGER(c) ((c) & (1 << 1))
+#define SYN_CAP_PALMDETECT(c) ((c) & (1 << 0))
+#define SYN_CAP_VALID(c) ((((c) & 0x00ff00) >> 8) == 0x47)
+#define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20)
+#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12)
+
+/* synaptics modes query bits */
+#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
+#define SYN_MODE_RATE(m) ((m) & (1 << 6))
+#define SYN_MODE_BAUD_SLEEP(m) ((m) & (1 << 3))
+#define SYN_MODE_DISABLE_GESTURE(m) ((m) & (1 << 2))
+#define SYN_MODE_PACKSIZE(m) ((m) & (1 << 1))
+#define SYN_MODE_WMODE(m) ((m) & (1 << 0))
+
+/* synaptics identify query bits */
+#define SYN_ID_MODEL(i) (((i) >> 4) & 0x0f)
+#define SYN_ID_MAJOR(i) ((i) & 0x0f)
+#define SYN_ID_MINOR(i) (((i) >> 16) & 0xff)
+#define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47)
+
+/* synaptics special commands */
+#define SYN_PS_SET_MODE2 0x14
+#define SYN_PS_CLIENT_CMD 0x28
+
+/* synaptics packet types */
+#define SYN_NEWABS 0
+#define SYN_NEWABS_STRICT 1
+#define SYN_NEWABS_RELAXED 2
+#define SYN_OLDABS 3
+
+/*
+ * A structure to describe the state of the touchpad hardware (buttons and pad)
+ */
+
+struct synaptics_hw_state {
+ int x;
+ int y;
+ int z;
+ int w;
+ unsigned int left:1;
+ unsigned int right:1;
+ unsigned int middle:1;
+ unsigned int up:1;
+ unsigned int down:1;
+ unsigned char ext_buttons;
+ signed char scroll;
+};
+
+struct synaptics_data {
+ /* Data read from the touchpad */
+ unsigned long int model_id; /* Model-ID */
+ unsigned long int capabilities; /* Capabilities */
+ unsigned long int ext_cap; /* Extended Capabilities */
+ unsigned long int identity; /* Identification */
+
+ unsigned char pkt_type; /* packet type - old, new, etc */
+ unsigned char mode; /* current mode byte */
+ int scroll;
+};
+
+int synaptics_detect(struct psmouse *psmouse, int set_properties);
+int synaptics_init(struct psmouse *psmouse);
+void synaptics_reset(struct psmouse *psmouse);
+
+#endif /* _SYNAPTICS_H */
diff --git a/drivers/input/mouse/touchkit_ps2.c b/drivers/input/mouse/touchkit_ps2.c
new file mode 100644
index 0000000..3fadb2a
--- /dev/null
+++ b/drivers/input/mouse/touchkit_ps2.c
@@ -0,0 +1,100 @@
+/* ----------------------------------------------------------------------------
+ * touchkit_ps2.c -- Driver for eGalax TouchKit PS/2 Touchscreens
+ *
+ * Copyright (C) 2005 by Stefan Lucke
+ * Copyright (C) 2004 by Daniel Ritz
+ * Copyright (C) by Todd E. Johnson (mtouchusb.c)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Based upon touchkitusb.c
+ *
+ * Vendor documentation is available in support section of:
+ * http://www.egalax.com.tw/
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+
+#include "psmouse.h"
+#include "touchkit_ps2.h"
+
+#define TOUCHKIT_MAX_XC 0x07ff
+#define TOUCHKIT_MAX_YC 0x07ff
+
+#define TOUCHKIT_CMD 0x0a
+#define TOUCHKIT_CMD_LENGTH 1
+
+#define TOUCHKIT_CMD_ACTIVE 'A'
+#define TOUCHKIT_CMD_FIRMWARE_VERSION 'D'
+#define TOUCHKIT_CMD_CONTROLLER_TYPE 'E'
+
+#define TOUCHKIT_SEND_PARMS(s, r, c) ((s) << 12 | (r) << 8 | (c))
+
+#define TOUCHKIT_GET_TOUCHED(packet) (((packet)[0]) & 0x01)
+#define TOUCHKIT_GET_X(packet) (((packet)[1] << 7) | (packet)[2])
+#define TOUCHKIT_GET_Y(packet) (((packet)[3] << 7) | (packet)[4])
+
+static psmouse_ret_t touchkit_ps2_process_byte(struct psmouse *psmouse)
+{
+ unsigned char *packet = psmouse->packet;
+ struct input_dev *dev = psmouse->dev;
+
+ if (psmouse->pktcnt != 5)
+ return PSMOUSE_GOOD_DATA;
+
+ input_report_abs(dev, ABS_X, TOUCHKIT_GET_X(packet));
+ input_report_abs(dev, ABS_Y, TOUCHKIT_GET_Y(packet));
+ input_report_key(dev, BTN_TOUCH, TOUCHKIT_GET_TOUCHED(packet));
+ input_sync(dev);
+
+ return PSMOUSE_FULL_PACKET;
+}
+
+int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties)
+{
+ struct input_dev *dev = psmouse->dev;
+ unsigned char param[3];
+ int command;
+
+ param[0] = TOUCHKIT_CMD_LENGTH;
+ param[1] = TOUCHKIT_CMD_ACTIVE;
+ command = TOUCHKIT_SEND_PARMS(2, 3, TOUCHKIT_CMD);
+
+ if (ps2_command(&psmouse->ps2dev, param, command))
+ return -ENODEV;
+
+ if (param[0] != TOUCHKIT_CMD || param[1] != 0x01 ||
+ param[2] != TOUCHKIT_CMD_ACTIVE)
+ return -ENODEV;
+
+ if (set_properties) {
+ dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ set_bit(BTN_TOUCH, dev->keybit);
+ input_set_abs_params(dev, ABS_X, 0, TOUCHKIT_MAX_XC, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, TOUCHKIT_MAX_YC, 0, 0);
+
+ psmouse->vendor = "eGalax";
+ psmouse->name = "Touchscreen";
+ psmouse->protocol_handler = touchkit_ps2_process_byte;
+ psmouse->pktsize = 5;
+ }
+
+ return 0;
+}
diff --git a/drivers/input/mouse/touchkit_ps2.h b/drivers/input/mouse/touchkit_ps2.h
new file mode 100644
index 0000000..8a0dd35
--- /dev/null
+++ b/drivers/input/mouse/touchkit_ps2.h
@@ -0,0 +1,25 @@
+/* ----------------------------------------------------------------------------
+ * touchkit_ps2.h -- Driver for eGalax TouchKit PS/2 Touchscreens
+ *
+ * Copyright (C) 2005 by Stefan Lucke
+ * Copyright (c) 2005 Vojtech Pavlik
+ *
+ * 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 _TOUCHKIT_PS2_H
+#define _TOUCHKIT_PS2_H
+
+#ifdef CONFIG_MOUSE_PS2_TOUCHKIT
+int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties);
+#else
+static inline int touchkit_ps2_detect(struct psmouse *psmouse,
+ int set_properties)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_TOUCHKIT */
+
+#endif
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
new file mode 100644
index 0000000..e68c814
--- /dev/null
+++ b/drivers/input/mouse/trackpoint.c
@@ -0,0 +1,331 @@
+/*
+ * Stephen Evanchik <evanchsa@gmail.com>
+ *
+ * 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.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/delay.h>
+#include <linux/serio.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/libps2.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include "psmouse.h"
+#include "trackpoint.h"
+
+/*
+ * Device IO: read, write and toggle bit
+ */
+static int trackpoint_read(struct ps2dev *ps2dev, unsigned char loc, unsigned char *results)
+{
+ if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
+ ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int trackpoint_write(struct ps2dev *ps2dev, unsigned char loc, unsigned char val)
+{
+ if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
+ ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) ||
+ ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
+ ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, val))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int trackpoint_toggle_bit(struct ps2dev *ps2dev, unsigned char loc, unsigned char mask)
+{
+ /* Bad things will happen if the loc param isn't in this range */
+ if (loc < 0x20 || loc >= 0x2F)
+ return -1;
+
+ if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
+ ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_TOGGLE)) ||
+ ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
+ ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, mask))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Trackpoint-specific attributes
+ */
+struct trackpoint_attr_data {
+ size_t field_offset;
+ unsigned char command;
+ unsigned char mask;
+ unsigned char inverted;
+};
+
+static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, void *data, char *buf)
+{
+ struct trackpoint_data *tp = psmouse->private;
+ struct trackpoint_attr_data *attr = data;
+ unsigned char value = *(unsigned char *)((char *)tp + attr->field_offset);
+
+ if (attr->inverted)
+ value = !value;
+
+ return sprintf(buf, "%u\n", value);
+}
+
+static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,
+ const char *buf, size_t count)
+{
+ struct trackpoint_data *tp = psmouse->private;
+ struct trackpoint_attr_data *attr = data;
+ unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
+ unsigned long value;
+
+ if (strict_strtoul(buf, 10, &value) || value > 255)
+ return -EINVAL;
+
+ *field = value;
+ trackpoint_write(&psmouse->ps2dev, attr->command, value);
+
+ return count;
+}
+
+#define TRACKPOINT_INT_ATTR(_name, _command) \
+ static struct trackpoint_attr_data trackpoint_attr_##_name = { \
+ .field_offset = offsetof(struct trackpoint_data, _name), \
+ .command = _command, \
+ }; \
+ PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
+ &trackpoint_attr_##_name, \
+ trackpoint_show_int_attr, trackpoint_set_int_attr)
+
+static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,
+ const char *buf, size_t count)
+{
+ struct trackpoint_data *tp = psmouse->private;
+ struct trackpoint_attr_data *attr = data;
+ unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
+ unsigned long value;
+
+ if (strict_strtoul(buf, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (attr->inverted)
+ value = !value;
+
+ if (*field != value) {
+ *field = value;
+ trackpoint_toggle_bit(&psmouse->ps2dev, attr->command, attr->mask);
+ }
+
+ return count;
+}
+
+
+#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv) \
+ static struct trackpoint_attr_data trackpoint_attr_##_name = { \
+ .field_offset = offsetof(struct trackpoint_data, _name), \
+ .command = _command, \
+ .mask = _mask, \
+ .inverted = _inv, \
+ }; \
+ PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
+ &trackpoint_attr_##_name, \
+ trackpoint_show_int_attr, trackpoint_set_bit_attr)
+
+TRACKPOINT_INT_ATTR(sensitivity, TP_SENS);
+TRACKPOINT_INT_ATTR(speed, TP_SPEED);
+TRACKPOINT_INT_ATTR(inertia, TP_INERTIA);
+TRACKPOINT_INT_ATTR(reach, TP_REACH);
+TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS);
+TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG);
+TRACKPOINT_INT_ATTR(thresh, TP_THRESH);
+TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH);
+TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME);
+TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV);
+
+TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0);
+TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0);
+TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1);
+
+static struct attribute *trackpoint_attrs[] = {
+ &psmouse_attr_sensitivity.dattr.attr,
+ &psmouse_attr_speed.dattr.attr,
+ &psmouse_attr_inertia.dattr.attr,
+ &psmouse_attr_reach.dattr.attr,
+ &psmouse_attr_draghys.dattr.attr,
+ &psmouse_attr_mindrag.dattr.attr,
+ &psmouse_attr_thresh.dattr.attr,
+ &psmouse_attr_upthresh.dattr.attr,
+ &psmouse_attr_ztime.dattr.attr,
+ &psmouse_attr_jenks.dattr.attr,
+ &psmouse_attr_press_to_select.dattr.attr,
+ &psmouse_attr_skipback.dattr.attr,
+ &psmouse_attr_ext_dev.dattr.attr,
+ NULL
+};
+
+static struct attribute_group trackpoint_attr_group = {
+ .attrs = trackpoint_attrs,
+};
+
+static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *firmware_id)
+{
+ unsigned char param[2] = { 0 };
+
+ if (ps2_command(&psmouse->ps2dev, param, MAKE_PS2_CMD(0, 2, TP_READ_ID)))
+ return -1;
+
+ if (param[0] != TP_MAGIC_IDENT)
+ return -1;
+
+ if (firmware_id)
+ *firmware_id = param[1];
+
+ return 0;
+}
+
+static int trackpoint_sync(struct psmouse *psmouse)
+{
+ struct trackpoint_data *tp = psmouse->private;
+ unsigned char toggle;
+
+ /* Disable features that may make device unusable with this driver */
+ trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, &toggle);
+ if (toggle & TP_MASK_TWOHAND)
+ trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, TP_MASK_TWOHAND);
+
+ trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, &toggle);
+ if (toggle & TP_MASK_SOURCE_TAG)
+ trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, TP_MASK_SOURCE_TAG);
+
+ trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_MB, &toggle);
+ if (toggle & TP_MASK_MB)
+ trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_MB, TP_MASK_MB);
+
+ /* Push the config to the device */
+ trackpoint_write(&psmouse->ps2dev, TP_SENS, tp->sensitivity);
+ trackpoint_write(&psmouse->ps2dev, TP_INERTIA, tp->inertia);
+ trackpoint_write(&psmouse->ps2dev, TP_SPEED, tp->speed);
+
+ trackpoint_write(&psmouse->ps2dev, TP_REACH, tp->reach);
+ trackpoint_write(&psmouse->ps2dev, TP_DRAGHYS, tp->draghys);
+ trackpoint_write(&psmouse->ps2dev, TP_MINDRAG, tp->mindrag);
+
+ trackpoint_write(&psmouse->ps2dev, TP_THRESH, tp->thresh);
+ trackpoint_write(&psmouse->ps2dev, TP_UP_THRESH, tp->upthresh);
+
+ trackpoint_write(&psmouse->ps2dev, TP_Z_TIME, tp->ztime);
+ trackpoint_write(&psmouse->ps2dev, TP_JENKS_CURV, tp->jenks);
+
+ trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_PTSON, &toggle);
+ if (((toggle & TP_MASK_PTSON) == TP_MASK_PTSON) != tp->press_to_select)
+ trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_PTSON, TP_MASK_PTSON);
+
+ trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, &toggle);
+ if (((toggle & TP_MASK_SKIPBACK) == TP_MASK_SKIPBACK) != tp->skipback)
+ trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK);
+
+ trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, &toggle);
+ if (((toggle & TP_MASK_EXT_DEV) == TP_MASK_EXT_DEV) != tp->ext_dev)
+ trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV);
+
+ return 0;
+}
+
+static void trackpoint_defaults(struct trackpoint_data *tp)
+{
+ tp->press_to_select = TP_DEF_PTSON;
+ tp->sensitivity = TP_DEF_SENS;
+ tp->speed = TP_DEF_SPEED;
+ tp->reach = TP_DEF_REACH;
+
+ tp->draghys = TP_DEF_DRAGHYS;
+ tp->mindrag = TP_DEF_MINDRAG;
+
+ tp->thresh = TP_DEF_THRESH;
+ tp->upthresh = TP_DEF_UP_THRESH;
+
+ tp->ztime = TP_DEF_Z_TIME;
+ tp->jenks = TP_DEF_JENKS_CURV;
+
+ tp->inertia = TP_DEF_INERTIA;
+ tp->skipback = TP_DEF_SKIPBACK;
+ tp->ext_dev = TP_DEF_EXT_DEV;
+}
+
+static void trackpoint_disconnect(struct psmouse *psmouse)
+{
+ sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, &trackpoint_attr_group);
+
+ kfree(psmouse->private);
+ psmouse->private = NULL;
+}
+
+static int trackpoint_reconnect(struct psmouse *psmouse)
+{
+ if (trackpoint_start_protocol(psmouse, NULL))
+ return -1;
+
+ if (trackpoint_sync(psmouse))
+ return -1;
+
+ return 0;
+}
+
+int trackpoint_detect(struct psmouse *psmouse, int set_properties)
+{
+ struct trackpoint_data *priv;
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char firmware_id;
+ unsigned char button_info;
+ int error;
+
+ if (trackpoint_start_protocol(psmouse, &firmware_id))
+ return -1;
+
+ if (!set_properties)
+ return 0;
+
+ if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) {
+ printk(KERN_WARNING "trackpoint.c: failed to get extended button data\n");
+ button_info = 0;
+ }
+
+ psmouse->private = priv = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
+ if (!priv)
+ return -1;
+
+ psmouse->vendor = "IBM";
+ psmouse->name = "TrackPoint";
+
+ psmouse->reconnect = trackpoint_reconnect;
+ psmouse->disconnect = trackpoint_disconnect;
+
+ trackpoint_defaults(priv);
+ trackpoint_sync(psmouse);
+
+ error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);
+ if (error) {
+ printk(KERN_ERR
+ "trackpoint.c: failed to create sysfs attributes, error: %d\n",
+ error);
+ kfree(priv);
+ return -1;
+ }
+
+ printk(KERN_INFO "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n",
+ firmware_id, (button_info & 0xf0) >> 4, button_info & 0x0f);
+
+ return 0;
+}
+
diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h
new file mode 100644
index 0000000..c10a6e7
--- /dev/null
+++ b/drivers/input/mouse/trackpoint.h
@@ -0,0 +1,154 @@
+/*
+ * IBM TrackPoint PS/2 mouse driver
+ *
+ * Stephen Evanchik <evanchsa@gmail.com>
+ *
+ * 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 _TRACKPOINT_H
+#define _TRACKPOINT_H
+
+/*
+ * These constants are from the TrackPoint System
+ * Engineering documentation Version 4 from IBM Watson
+ * research:
+ * http://wwwcssrv.almaden.ibm.com/trackpoint/download.html
+ */
+
+#define TP_COMMAND 0xE2 /* Commands start with this */
+
+#define TP_READ_ID 0xE1 /* Sent for device identification */
+#define TP_MAGIC_IDENT 0x01 /* Sent after a TP_READ_ID followed */
+ /* by the firmware ID */
+
+
+/*
+ * Commands
+ */
+#define TP_RECALIB 0x51 /* Recalibrate */
+#define TP_POWER_DOWN 0x44 /* Can only be undone through HW reset */
+#define TP_EXT_DEV 0x21 /* Determines if external device is connected (RO) */
+#define TP_EXT_BTN 0x4B /* Read extended button status */
+#define TP_POR 0x7F /* Execute Power on Reset */
+#define TP_POR_RESULTS 0x25 /* Read Power on Self test results */
+#define TP_DISABLE_EXT 0x40 /* Disable external pointing device */
+#define TP_ENABLE_EXT 0x41 /* Enable external pointing device */
+
+/*
+ * Mode manipulation
+ */
+#define TP_SET_SOFT_TRANS 0x4E /* Set mode */
+#define TP_CANCEL_SOFT_TRANS 0xB9 /* Cancel mode */
+#define TP_SET_HARD_TRANS 0x45 /* Mode can only be set */
+
+
+/*
+ * Register oriented commands/properties
+ */
+#define TP_WRITE_MEM 0x81
+#define TP_READ_MEM 0x80 /* Not used in this implementation */
+
+/*
+* RAM Locations for properties
+ */
+#define TP_SENS 0x4A /* Sensitivity */
+#define TP_MB 0x4C /* Read Middle Button Status (RO) */
+#define TP_INERTIA 0x4D /* Negative Inertia */
+#define TP_SPEED 0x60 /* Speed of TP Cursor */
+#define TP_REACH 0x57 /* Backup for Z-axis press */
+#define TP_DRAGHYS 0x58 /* Drag Hysteresis */
+ /* (how hard it is to drag */
+ /* with Z-axis pressed) */
+
+#define TP_MINDRAG 0x59 /* Minimum amount of force needed */
+ /* to trigger dragging */
+
+#define TP_THRESH 0x5C /* Minimum value for a Z-axis press */
+#define TP_UP_THRESH 0x5A /* Used to generate a 'click' on Z-axis */
+#define TP_Z_TIME 0x5E /* How sharp of a press */
+#define TP_JENKS_CURV 0x5D /* Minimum curvature for double click */
+
+/*
+ * Toggling Flag bits
+ */
+#define TP_TOGGLE 0x47 /* Toggle command */
+
+#define TP_TOGGLE_MB 0x23 /* Disable/Enable Middle Button */
+#define TP_MASK_MB 0x01
+#define TP_TOGGLE_EXT_DEV 0x23 /* Disable external device */
+#define TP_MASK_EXT_DEV 0x02
+#define TP_TOGGLE_DRIFT 0x23 /* Drift Correction */
+#define TP_MASK_DRIFT 0x80
+#define TP_TOGGLE_BURST 0x28 /* Burst Mode */
+#define TP_MASK_BURST 0x80
+#define TP_TOGGLE_PTSON 0x2C /* Press to Select */
+#define TP_MASK_PTSON 0x01
+#define TP_TOGGLE_HARD_TRANS 0x2C /* Alternate method to set Hard Transparency */
+#define TP_MASK_HARD_TRANS 0x80
+#define TP_TOGGLE_TWOHAND 0x2D /* Two handed */
+#define TP_MASK_TWOHAND 0x01
+#define TP_TOGGLE_STICKY_TWO 0x2D /* Sticky two handed */
+#define TP_MASK_STICKY_TWO 0x04
+#define TP_TOGGLE_SKIPBACK 0x2D /* Suppress movement after drag release */
+#define TP_MASK_SKIPBACK 0x08
+#define TP_TOGGLE_SOURCE_TAG 0x20 /* Bit 3 of the first packet will be set to
+ to the origin of the packet (external or TP) */
+#define TP_MASK_SOURCE_TAG 0x80
+#define TP_TOGGLE_EXT_TAG 0x22 /* Bit 3 of the first packet coming from the
+ external device will be forced to 1 */
+#define TP_MASK_EXT_TAG 0x04
+
+
+/* Power on Self Test Results */
+#define TP_POR_SUCCESS 0x3B
+
+/*
+ * Default power on values
+ */
+#define TP_DEF_SENS 0x80
+#define TP_DEF_INERTIA 0x06
+#define TP_DEF_SPEED 0x61
+#define TP_DEF_REACH 0x0A
+
+#define TP_DEF_DRAGHYS 0xFF
+#define TP_DEF_MINDRAG 0x14
+
+#define TP_DEF_THRESH 0x08
+#define TP_DEF_UP_THRESH 0xFF
+#define TP_DEF_Z_TIME 0x26
+#define TP_DEF_JENKS_CURV 0x87
+
+/* Toggles */
+#define TP_DEF_MB 0x00
+#define TP_DEF_PTSON 0x00
+#define TP_DEF_SKIPBACK 0x00
+#define TP_DEF_EXT_DEV 0x00 /* 0 means enabled */
+
+#define MAKE_PS2_CMD(params, results, cmd) ((params<<12) | (results<<8) | (cmd))
+
+struct trackpoint_data
+{
+ unsigned char sensitivity, speed, inertia, reach;
+ unsigned char draghys, mindrag;
+ unsigned char thresh, upthresh;
+ unsigned char ztime, jenks;
+
+ unsigned char press_to_select;
+ unsigned char skipback;
+
+ unsigned char ext_dev;
+};
+
+#ifdef CONFIG_MOUSE_PS2_TRACKPOINT
+int trackpoint_detect(struct psmouse *psmouse, int set_properties);
+#else
+inline int trackpoint_detect(struct psmouse *psmouse, int set_properties)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_TRACKPOINT */
+
+#endif /* _TRACKPOINT_H */
diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c
new file mode 100644
index 0000000..404eedd
--- /dev/null
+++ b/drivers/input/mouse/vsxxxaa.c
@@ -0,0 +1,587 @@
+/*
+ * Driver for DEC VSXXX-AA mouse (hockey-puck mouse, ball or two rollers)
+ * DEC VSXXX-GA mouse (rectangular mouse, with ball)
+ * DEC VSXXX-AB tablet (digitizer with hair cross or stylus)
+ *
+ * Copyright (C) 2003-2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
+ *
+ * The packet format was initially taken from a patch to GPM which is (C) 2001
+ * by Karsten Merker <merker@linuxtag.org>
+ * and Maciej W. Rozycki <macro@ds2.pg.gda.pl>
+ * Later on, I had access to the device's documentation (referenced below).
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Building an adaptor to DE9 / DB25 RS232
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * DISCLAIMER: Use this description AT YOUR OWN RISK! I'll not pay for
+ * anything if you break your mouse, your computer or whatever!
+ *
+ * In theory, this mouse is a simple RS232 device. In practice, it has got
+ * a quite uncommon plug and the requirement to additionally get a power
+ * supply at +5V and -12V.
+ *
+ * If you look at the socket/jack (_not_ at the plug), we use this pin
+ * numbering:
+ * _______
+ * / 7 6 5 \
+ * | 4 --- 3 |
+ * \ 2 1 /
+ * -------
+ *
+ * DEC socket DE9 DB25 Note
+ * 1 (GND) 5 7 -
+ * 2 (RxD) 2 3 -
+ * 3 (TxD) 3 2 -
+ * 4 (-12V) - - Somewhere from the PSU. At ATX, it's
+ * the thin blue wire at pin 12 of the
+ * ATX power connector. Only required for
+ * VSXXX-AA/-GA mice.
+ * 5 (+5V) - - PSU (red wires of ATX power connector
+ * on pin 4, 6, 19 or 20) or HDD power
+ * connector (also red wire).
+ * 6 (+12V) - - HDD power connector, yellow wire. Only
+ * required for VSXXX-AB digitizer.
+ * 7 (dev. avail.) - - The mouse shorts this one to pin 1.
+ * This way, the host computer can detect
+ * the mouse. To use it with the adaptor,
+ * simply don't connect this pin.
+ *
+ * So to get a working adaptor, you need to connect the mouse with three
+ * wires to a RS232 port and two or three additional wires for +5V, +12V and
+ * -12V to the PSU.
+ *
+ * Flow specification for the link is 4800, 8o1.
+ *
+ * The mice and tablet are described in "VCB02 Video Subsystem - Technical
+ * Manual", DEC EK-104AA-TM-001. You'll find it at MANX, a search engine
+ * specific for DEC documentation. Try
+ * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet"
+
+MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_LICENSE ("GPL");
+
+#undef VSXXXAA_DEBUG
+#ifdef VSXXXAA_DEBUG
+#define DBG(x...) printk (x)
+#else
+#define DBG(x...) do {} while (0)
+#endif
+
+#define VSXXXAA_INTRO_MASK 0x80
+#define VSXXXAA_INTRO_HEAD 0x80
+#define IS_HDR_BYTE(x) (((x) & VSXXXAA_INTRO_MASK) \
+ == VSXXXAA_INTRO_HEAD)
+
+#define VSXXXAA_PACKET_MASK 0xe0
+#define VSXXXAA_PACKET_REL 0x80
+#define VSXXXAA_PACKET_ABS 0xc0
+#define VSXXXAA_PACKET_POR 0xa0
+#define MATCH_PACKET_TYPE(data, type) (((data) & VSXXXAA_PACKET_MASK) == (type))
+
+
+
+struct vsxxxaa {
+ struct input_dev *dev;
+ struct serio *serio;
+#define BUFLEN 15 /* At least 5 is needed for a full tablet packet */
+ unsigned char buf[BUFLEN];
+ unsigned char count;
+ unsigned char version;
+ unsigned char country;
+ unsigned char type;
+ char name[64];
+ char phys[32];
+};
+
+static void
+vsxxxaa_drop_bytes (struct vsxxxaa *mouse, int num)
+{
+ if (num >= mouse->count)
+ mouse->count = 0;
+ else {
+ memmove (mouse->buf, mouse->buf + num - 1, BUFLEN - num);
+ mouse->count -= num;
+ }
+}
+
+static void
+vsxxxaa_queue_byte (struct vsxxxaa *mouse, unsigned char byte)
+{
+ if (mouse->count == BUFLEN) {
+ printk (KERN_ERR "%s on %s: Dropping a byte of full buffer.\n",
+ mouse->name, mouse->phys);
+ vsxxxaa_drop_bytes (mouse, 1);
+ }
+ DBG (KERN_INFO "Queueing byte 0x%02x\n", byte);
+
+ mouse->buf[mouse->count++] = byte;
+}
+
+static void
+vsxxxaa_detection_done (struct vsxxxaa *mouse)
+{
+ switch (mouse->type) {
+ case 0x02:
+ strlcpy (mouse->name, "DEC VSXXX-AA/-GA mouse",
+ sizeof (mouse->name));
+ break;
+
+ case 0x04:
+ strlcpy (mouse->name, "DEC VSXXX-AB digitizer",
+ sizeof (mouse->name));
+ break;
+
+ default:
+ snprintf (mouse->name, sizeof (mouse->name),
+ "unknown DEC pointer device (type = 0x%02x)",
+ mouse->type);
+ break;
+ }
+
+ printk (KERN_INFO
+ "Found %s version 0x%02x from country 0x%02x on port %s\n",
+ mouse->name, mouse->version, mouse->country, mouse->phys);
+}
+
+/*
+ * Returns number of bytes to be dropped, 0 if packet is okay.
+ */
+static int
+vsxxxaa_check_packet (struct vsxxxaa *mouse, int packet_len)
+{
+ int i;
+
+ /* First byte must be a header byte */
+ if (!IS_HDR_BYTE (mouse->buf[0])) {
+ DBG ("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]);
+ return 1;
+ }
+
+ /* Check all following bytes */
+ if (packet_len > 1) {
+ for (i = 1; i < packet_len; i++) {
+ if (IS_HDR_BYTE (mouse->buf[i])) {
+ printk (KERN_ERR "Need to drop %d bytes "
+ "of a broken packet.\n",
+ i - 1);
+ DBG (KERN_INFO "check: len=%d, b[%d]=0x%02x\n",
+ packet_len, i, mouse->buf[i]);
+ return i - 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static __inline__ int
+vsxxxaa_smells_like_packet (struct vsxxxaa *mouse, unsigned char type, size_t len)
+{
+ return (mouse->count >= len) && MATCH_PACKET_TYPE (mouse->buf[0], type);
+}
+
+static void
+vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse)
+{
+ struct input_dev *dev = mouse->dev;
+ unsigned char *buf = mouse->buf;
+ int left, middle, right;
+ int dx, dy;
+
+ /*
+ * Check for normal stream packets. This is three bytes,
+ * with the first byte's 3 MSB set to 100.
+ *
+ * [0]: 1 0 0 SignX SignY Left Middle Right
+ * [1]: 0 dx dx dx dx dx dx dx
+ * [2]: 0 dy dy dy dy dy dy dy
+ */
+
+ /*
+ * Low 7 bit of byte 1 are abs(dx), bit 7 is
+ * 0, bit 4 of byte 0 is direction.
+ */
+ dx = buf[1] & 0x7f;
+ dx *= ((buf[0] >> 4) & 0x01)? 1: -1;
+
+ /*
+ * Low 7 bit of byte 2 are abs(dy), bit 7 is
+ * 0, bit 3 of byte 0 is direction.
+ */
+ dy = buf[2] & 0x7f;
+ dy *= ((buf[0] >> 3) & 0x01)? -1: 1;
+
+ /*
+ * Get button state. It's the low three bits
+ * (for three buttons) of byte 0.
+ */
+ left = (buf[0] & 0x04)? 1: 0;
+ middle = (buf[0] & 0x02)? 1: 0;
+ right = (buf[0] & 0x01)? 1: 0;
+
+ vsxxxaa_drop_bytes (mouse, 3);
+
+ DBG (KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n",
+ mouse->name, mouse->phys, dx, dy,
+ left? "L": "l", middle? "M": "m", right? "R": "r");
+
+ /*
+ * Report what we've found so far...
+ */
+ input_report_key (dev, BTN_LEFT, left);
+ input_report_key (dev, BTN_MIDDLE, middle);
+ input_report_key (dev, BTN_RIGHT, right);
+ input_report_key (dev, BTN_TOUCH, 0);
+ input_report_rel (dev, REL_X, dx);
+ input_report_rel (dev, REL_Y, dy);
+ input_sync (dev);
+}
+
+static void
+vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse)
+{
+ struct input_dev *dev = mouse->dev;
+ unsigned char *buf = mouse->buf;
+ int left, middle, right, touch;
+ int x, y;
+
+ /*
+ * Tablet position / button packet
+ *
+ * [0]: 1 1 0 B4 B3 B2 B1 Pr
+ * [1]: 0 0 X5 X4 X3 X2 X1 X0
+ * [2]: 0 0 X11 X10 X9 X8 X7 X6
+ * [3]: 0 0 Y5 Y4 Y3 Y2 Y1 Y0
+ * [4]: 0 0 Y11 Y10 Y9 Y8 Y7 Y6
+ */
+
+ /*
+ * Get X/Y position. Y axis needs to be inverted since VSXXX-AB
+ * counts down->top while monitor counts top->bottom.
+ */
+ x = ((buf[2] & 0x3f) << 6) | (buf[1] & 0x3f);
+ y = ((buf[4] & 0x3f) << 6) | (buf[3] & 0x3f);
+ y = 1023 - y;
+
+ /*
+ * Get button state. It's bits <4..1> of byte 0.
+ */
+ left = (buf[0] & 0x02)? 1: 0;
+ middle = (buf[0] & 0x04)? 1: 0;
+ right = (buf[0] & 0x08)? 1: 0;
+ touch = (buf[0] & 0x10)? 1: 0;
+
+ vsxxxaa_drop_bytes (mouse, 5);
+
+ DBG (KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n",
+ mouse->name, mouse->phys, x, y,
+ left? "L": "l", middle? "M": "m",
+ right? "R": "r", touch? "T": "t");
+
+ /*
+ * Report what we've found so far...
+ */
+ input_report_key (dev, BTN_LEFT, left);
+ input_report_key (dev, BTN_MIDDLE, middle);
+ input_report_key (dev, BTN_RIGHT, right);
+ input_report_key (dev, BTN_TOUCH, touch);
+ input_report_abs (dev, ABS_X, x);
+ input_report_abs (dev, ABS_Y, y);
+ input_sync (dev);
+}
+
+static void
+vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse)
+{
+ struct input_dev *dev = mouse->dev;
+ unsigned char *buf = mouse->buf;
+ int left, middle, right;
+ unsigned char error;
+
+ /*
+ * Check for Power-On-Reset packets. These are sent out
+ * after plugging the mouse in, or when explicitly
+ * requested by sending 'T'.
+ *
+ * [0]: 1 0 1 0 R3 R2 R1 R0
+ * [1]: 0 M2 M1 M0 D3 D2 D1 D0
+ * [2]: 0 E6 E5 E4 E3 E2 E1 E0
+ * [3]: 0 0 0 0 0 Left Middle Right
+ *
+ * M: manufacturer location code
+ * R: revision code
+ * E: Error code. If it's in the range of 0x00..0x1f, only some
+ * minor problem occured. Errors >= 0x20 are considered bad
+ * and the device may not work properly...
+ * D: <0010> == mouse, <0100> == tablet
+ */
+
+ mouse->version = buf[0] & 0x0f;
+ mouse->country = (buf[1] >> 4) & 0x07;
+ mouse->type = buf[1] & 0x0f;
+ error = buf[2] & 0x7f;
+
+ /*
+ * Get button state. It's the low three bits
+ * (for three buttons) of byte 0. Maybe even the bit <3>
+ * has some meaning if a tablet is attached.
+ */
+ left = (buf[0] & 0x04)? 1: 0;
+ middle = (buf[0] & 0x02)? 1: 0;
+ right = (buf[0] & 0x01)? 1: 0;
+
+ vsxxxaa_drop_bytes (mouse, 4);
+ vsxxxaa_detection_done (mouse);
+
+ if (error <= 0x1f) {
+ /* No (serious) error. Report buttons */
+ input_report_key (dev, BTN_LEFT, left);
+ input_report_key (dev, BTN_MIDDLE, middle);
+ input_report_key (dev, BTN_RIGHT, right);
+ input_report_key (dev, BTN_TOUCH, 0);
+ input_sync (dev);
+
+ if (error != 0)
+ printk (KERN_INFO "Your %s on %s reports error=0x%02x\n",
+ mouse->name, mouse->phys, error);
+
+ }
+
+ /*
+ * If the mouse was hot-plugged, we need to force differential mode
+ * now... However, give it a second to recover from it's reset.
+ */
+ printk (KERN_NOTICE "%s on %s: Forceing standard packet format, "
+ "incremental streaming mode and 72 samples/sec\n",
+ mouse->name, mouse->phys);
+ mouse->serio->write (mouse->serio, 'S'); /* Standard format */
+ mdelay (50);
+ mouse->serio->write (mouse->serio, 'R'); /* Incremental */
+ mdelay (50);
+ mouse->serio->write (mouse->serio, 'L'); /* 72 samples/sec */
+}
+
+static void
+vsxxxaa_parse_buffer (struct vsxxxaa *mouse)
+{
+ unsigned char *buf = mouse->buf;
+ int stray_bytes;
+
+ /*
+ * Parse buffer to death...
+ */
+ do {
+ /*
+ * Out of sync? Throw away what we don't understand. Each
+ * packet starts with a byte whose bit 7 is set. Unhandled
+ * packets (ie. which we don't know about or simply b0rk3d
+ * data...) will get shifted out of the buffer after some
+ * activity on the mouse.
+ */
+ while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) {
+ printk (KERN_ERR "%s on %s: Dropping a byte to regain "
+ "sync with mouse data stream...\n",
+ mouse->name, mouse->phys);
+ vsxxxaa_drop_bytes (mouse, 1);
+ }
+
+ /*
+ * Check for packets we know about.
+ */
+
+ if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_REL, 3)) {
+ /* Check for broken packet */
+ stray_bytes = vsxxxaa_check_packet (mouse, 3);
+ if (stray_bytes > 0) {
+ printk (KERN_ERR "Dropping %d bytes now...\n",
+ stray_bytes);
+ vsxxxaa_drop_bytes (mouse, stray_bytes);
+ continue;
+ }
+
+ vsxxxaa_handle_REL_packet (mouse);
+ continue; /* More to parse? */
+ }
+
+ if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_ABS, 5)) {
+ /* Check for broken packet */
+ stray_bytes = vsxxxaa_check_packet (mouse, 5);
+ if (stray_bytes > 0) {
+ printk (KERN_ERR "Dropping %d bytes now...\n",
+ stray_bytes);
+ vsxxxaa_drop_bytes (mouse, stray_bytes);
+ continue;
+ }
+
+ vsxxxaa_handle_ABS_packet (mouse);
+ continue; /* More to parse? */
+ }
+
+ if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_POR, 4)) {
+ /* Check for broken packet */
+ stray_bytes = vsxxxaa_check_packet (mouse, 4);
+ if (stray_bytes > 0) {
+ printk (KERN_ERR "Dropping %d bytes now...\n",
+ stray_bytes);
+ vsxxxaa_drop_bytes (mouse, stray_bytes);
+ continue;
+ }
+
+ vsxxxaa_handle_POR_packet (mouse);
+ continue; /* More to parse? */
+ }
+
+ break; /* No REL, ABS or POR packet found */
+ } while (1);
+}
+
+static irqreturn_t
+vsxxxaa_interrupt (struct serio *serio, unsigned char data, unsigned int flags)
+{
+ struct vsxxxaa *mouse = serio_get_drvdata (serio);
+
+ vsxxxaa_queue_byte (mouse, data);
+ vsxxxaa_parse_buffer (mouse);
+
+ return IRQ_HANDLED;
+}
+
+static void
+vsxxxaa_disconnect (struct serio *serio)
+{
+ struct vsxxxaa *mouse = serio_get_drvdata (serio);
+
+ serio_close (serio);
+ serio_set_drvdata (serio, NULL);
+ input_unregister_device (mouse->dev);
+ kfree (mouse);
+}
+
+static int
+vsxxxaa_connect (struct serio *serio, struct serio_driver *drv)
+{
+ struct vsxxxaa *mouse;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+
+ mouse = kzalloc (sizeof (struct vsxxxaa), GFP_KERNEL);
+ input_dev = input_allocate_device ();
+ if (!mouse || !input_dev)
+ goto fail1;
+
+ mouse->dev = input_dev;
+ mouse->serio = serio;
+ strlcat (mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer",
+ sizeof (mouse->name));
+ snprintf (mouse->phys, sizeof (mouse->phys), "%s/input0", serio->phys);
+
+ input_dev->name = mouse->name;
+ input_dev->phys = mouse->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->dev.parent = &serio->dev;
+
+ set_bit (EV_KEY, input_dev->evbit); /* We have buttons */
+ set_bit (EV_REL, input_dev->evbit);
+ set_bit (EV_ABS, input_dev->evbit);
+ set_bit (BTN_LEFT, input_dev->keybit); /* We have 3 buttons */
+ set_bit (BTN_MIDDLE, input_dev->keybit);
+ set_bit (BTN_RIGHT, input_dev->keybit);
+ set_bit (BTN_TOUCH, input_dev->keybit); /* ...and Tablet */
+ set_bit (REL_X, input_dev->relbit);
+ set_bit (REL_Y, input_dev->relbit);
+ input_set_abs_params (input_dev, ABS_X, 0, 1023, 0, 0);
+ input_set_abs_params (input_dev, ABS_Y, 0, 1023, 0, 0);
+
+ serio_set_drvdata (serio, mouse);
+
+ err = serio_open (serio, drv);
+ if (err)
+ goto fail2;
+
+ /*
+ * Request selftest. Standard packet format and differential
+ * mode will be requested after the device ID'ed successfully.
+ */
+ serio->write (serio, 'T'); /* Test */
+
+ err = input_register_device (input_dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close (serio);
+ fail2: serio_set_drvdata (serio, NULL);
+ fail1: input_free_device (input_dev);
+ kfree (mouse);
+ return err;
+}
+
+static struct serio_device_id vsxxaa_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_VSXXXAA,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, vsxxaa_serio_ids);
+
+static struct serio_driver vsxxxaa_drv = {
+ .driver = {
+ .name = "vsxxxaa",
+ },
+ .description = DRIVER_DESC,
+ .id_table = vsxxaa_serio_ids,
+ .connect = vsxxxaa_connect,
+ .interrupt = vsxxxaa_interrupt,
+ .disconnect = vsxxxaa_disconnect,
+};
+
+static int __init
+vsxxxaa_init (void)
+{
+ return serio_register_driver(&vsxxxaa_drv);
+}
+
+static void __exit
+vsxxxaa_exit (void)
+{
+ serio_unregister_driver(&vsxxxaa_drv);
+}
+
+module_init (vsxxxaa_init);
+module_exit (vsxxxaa_exit);
+
OpenPOWER on IntegriCloud