public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Vojtech Pavlik <vojtech@suse.cz>
To: torvalds@transmeta.com, linux-kernel@vger.kernel.org
Subject: [PATCH 4/11] input: Big Synaptics pad update
Date: Fri, 19 Sep 2003 12:26:41 +0200	[thread overview]
Message-ID: <1063967201965@twilight.ucw.cz> (raw)
In-Reply-To: <10639672012246@twilight.ucw.cz>

You can pull this changeset from:
	bk://kernel.bkbits.net/vojtech/input

===================================================================

ChangeSet@1.1342, 2003-09-19 01:17:37-07:00, petero2@telia.com
  Input: Big Synaptics update:
  	Restore synaptics pad mode on module unload.
  	Support Synaptics touchpads with multiple buttons.
  	Make Synaptics touchpad support optional.
  	Add passthrough support for Synaptics touchpads. [Dmitry]
  	Add support for old Synaptics protocol.
  	Set mode byte correctly for old Synaptics pads.
  	Fix multibutton support of Synaptics pads.


 Documentation/kernel-parameters.txt |    4 
 drivers/input/mouse/Kconfig         |   20 +
 drivers/input/mouse/psmouse-base.c  |   69 +++++-
 drivers/input/mouse/psmouse.h       |   22 +-
 drivers/input/mouse/synaptics.c     |  364 ++++++++++++++++++++++++++++--------
 drivers/input/mouse/synaptics.h     |   27 ++
 drivers/input/serio/serio.c         |   25 ++
 include/linux/serio.h               |    3 
 8 files changed, 424 insertions(+), 110 deletions(-)

===================================================================

diff -Nru a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
--- a/Documentation/kernel-parameters.txt	Fri Sep 19 12:16:32 2003
+++ b/Documentation/kernel-parameters.txt	Fri Sep 19 12:16:32 2003
@@ -788,6 +788,10 @@
 
 	psmouse_noext	[HW,MOUSE] Disable probing for PS2 mouse protocol extensions
 
+	psmouse_resetafter=
+			[HW,MOUSE] Try to reset Synaptics Touchpad after so many
+			bad packets (0 = never).
+
 	pss=		[HW,OSS] Personal Sound System (ECHO ESC614)
 			Format: <io>,<mss_io>,<mss_irq>,<mss_dma>,<mpu_io>,<mpu_irq>
 
diff -Nru a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
--- a/drivers/input/mouse/Kconfig	Fri Sep 19 12:16:32 2003
+++ b/drivers/input/mouse/Kconfig	Fri Sep 19 12:16:32 2003
@@ -19,9 +19,7 @@
 	  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. Support for Synaptics TouchPads is also included.
-	  For Synaptics TouchPad support in XFree86 you'll need this XFree86
-	  driver: http://w1.894.telia.com/~u89404340/touchpad/index.html
+	  compatible.
 
 	  If unsure, say Y.
 
@@ -29,6 +27,22 @@
 	  inserted in and removed from the running kernel whenever you want).
 	  The module will be called psmouse. If you want to compile it as a
 	  module, say M here and read <file:Documentation/modules.txt>.
+
+config MOUSE_PS2_SYNAPTICS
+	bool "Synaptics TouchPad"
+	default n
+	depends on INPUT && INPUT_MOUSE && SERIO && MOUSE_PS2
+	---help---
+	  Say Y here if you have a Synaptics TouchPad connected to your system.
+	  This touchpad is found on many modern laptop computers.
+
+	  Note that you also need a user space driver to interpret the data
+	  generated by the kernel. A compatible driver for XFree86 is available
+	  from http://w1.894.telia.com/~u89404340/touchpad/index.html
+
+	  The gpm program is not yet able to interpret the data from this
+	  driver, so if you need to use the touchpad in the console, you have to
+	  say N for now.
 
 config MOUSE_SERIAL
 	tristate "Serial mouse"
diff -Nru a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
--- a/drivers/input/mouse/psmouse-base.c	Fri Sep 19 12:16:32 2003
+++ b/drivers/input/mouse/psmouse-base.c	Fri Sep 19 12:16:32 2003
@@ -29,6 +29,8 @@
 MODULE_PARM_DESC(psmouse_resolution, "Resolution, in dpi.");
 MODULE_PARM(psmouse_smartscroll, "i");
 MODULE_PARM_DESC(psmouse_smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled.");
+MODULE_PARM(psmouse_resetafter, "i");
+MODULE_PARM_DESC(psmouse_resetafter, "Reset Synaptics Touchpad after so many bad packets (0 = never).");
 MODULE_LICENSE("GPL");
 
 #define PSMOUSE_LOGITECH_SMARTSCROLL	1
@@ -36,11 +38,12 @@
 static int psmouse_noext;
 int psmouse_resolution;
 int psmouse_smartscroll = PSMOUSE_LOGITECH_SMARTSCROLL;
+unsigned int psmouse_resetafter;
 
 static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "PS2T++", "GenPS/2", "ImPS/2", "ImExPS/2", "Synaptics"};
 
 /*
- * psmouse_process_packet() anlyzes the PS/2 mouse packet contents and
+ * psmouse_process_packet() analyzes the PS/2 mouse packet contents and
  * reports relevant events to the input module.
  */
 
@@ -108,6 +111,9 @@
 {
 	struct psmouse *psmouse = serio->private;
 
+	if (psmouse->state == PSMOUSE_IGNORE)
+		goto out;
+
 	if (psmouse->acking) {
 		switch (data) {
 			case PSMOUSE_RET_ACK:
@@ -132,31 +138,46 @@
 	}
 
 	if (psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
-		printk(KERN_WARNING "psmouse.c: Lost synchronization, throwing %d bytes away.\n", psmouse->pktcnt);
+		printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n",
+		       psmouse->name, psmouse->phys, psmouse->pktcnt);
 		psmouse->pktcnt = 0;
 	}
 	
 	psmouse->last = jiffies;
 	psmouse->packet[psmouse->pktcnt++] = data;
 
-	if (psmouse->pktcnt == 3 + (psmouse->type >= PSMOUSE_GENPS)) {
-		psmouse_process_packet(psmouse, regs);
-		psmouse->pktcnt = 0;
-		goto out;
+	if (psmouse->packet[0] == PSMOUSE_RET_BAT) {
+		if (psmouse->pktcnt == 1)
+			goto out;
+		
+		if (psmouse->pktcnt == 2) {
+			if (psmouse->packet[1] == PSMOUSE_RET_ID) {
+				psmouse->state = PSMOUSE_IGNORE;
+				serio_rescan(serio);
+				goto out;
+			}
+			if (psmouse->type == PSMOUSE_SYNAPTICS) {
+				/* neither 0xAA nor 0x00 are valid first bytes
+				 * for a packet in absolute mode
+				 */
+				psmouse->pktcnt = 0;
+				goto out;
+			}
+		}
 	}
 
-	if (psmouse->pktcnt == 1 && psmouse->type == PSMOUSE_SYNAPTICS) {
+	if (psmouse->type == PSMOUSE_SYNAPTICS) {
 		/*
 		 * The synaptics driver has its own resync logic,
 		 * so it needs to receive all bytes one at a time.
 		 */
 		synaptics_process_byte(psmouse, regs);
-		psmouse->pktcnt = 0;
 		goto out;
 	}
 
-	if (psmouse->pktcnt == 1 && psmouse->packet[0] == PSMOUSE_RET_BAT) {
-		serio_rescan(serio);
+	if (psmouse->pktcnt == 3 + (psmouse->type >= PSMOUSE_GENPS)) {
+		psmouse_process_packet(psmouse, regs);
+		psmouse->pktcnt = 0;
 		goto out;
 	}
 out:
@@ -227,7 +248,7 @@
 	for (i = 0; i < receive; i++)
 		param[i] = psmouse->cmdbuf[(receive - 1) - i];
 
-	if (psmouse->cmdcnt) 
+	if (psmouse->cmdcnt)
 		return (psmouse->cmdcnt = 0) - 1;
 
 	return 0;
@@ -450,14 +471,18 @@
  */
 
 	psmouse_command(psmouse, param, PSMOUSE_CMD_SETSTREAM);
+}
 
 /*
- * Last, we enable the mouse so that we get reports from it.
+ * psmouse_activate() enables the mouse so that we get motion reports from it.
  */
 
+static void psmouse_activate(struct psmouse *psmouse)
+{
 	if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE))
 		printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys);
 
+	psmouse->state = PSMOUSE_ACTIVATED;
 }
 
 /*
@@ -478,8 +503,9 @@
 static void psmouse_disconnect(struct serio *serio)
 {
 	struct psmouse *psmouse = serio->private;
-	if (psmouse->type == PSMOUSE_SYNAPTICS)
-		synaptics_disconnect(psmouse);
+
+	psmouse->state = PSMOUSE_IGNORE;
+	synaptics_disconnect(psmouse);
 	input_unregister_device(&psmouse->dev);
 	serio_close(serio);
 	kfree(psmouse);
@@ -494,7 +520,8 @@
 {
 	struct psmouse *psmouse;
 	
-	if ((serio->type & SERIO_TYPE) != SERIO_8042)
+	if ((serio->type & SERIO_TYPE) != SERIO_8042 &&
+	    (serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU)
 		return;
 
 	if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL)))
@@ -507,6 +534,7 @@
 	psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
 	psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
 
+	psmouse->state = PSMOUSE_NEW_DEVICE;
 	psmouse->serio = serio;
 	psmouse->dev.private = psmouse;
 
@@ -540,6 +568,10 @@
 	printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys);
 
 	psmouse_initialize(psmouse);
+
+	synaptics_pt_init(psmouse);
+
+	psmouse_activate(psmouse);
 }
 
 static struct serio_dev psmouse_dev = {
@@ -568,9 +600,16 @@
 	return 1;
 }
 
+static int __init psmouse_resetafter_setup(char *str)
+{
+	get_option(&str, &psmouse_resetafter);
+	return 1;
+}
+
 __setup("psmouse_noext", psmouse_noext_setup);
 __setup("psmouse_resolution=", psmouse_resolution_setup);
 __setup("psmouse_smartscroll=", psmouse_smartscroll_setup);
+__setup("psmouse_resetafter=", psmouse_resetafter_setup);
 
 #endif
 
diff -Nru a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
--- a/drivers/input/mouse/psmouse.h	Fri Sep 19 12:16:32 2003
+++ b/drivers/input/mouse/psmouse.h	Fri Sep 19 12:16:32 2003
@@ -13,9 +13,15 @@
 #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
 
+/* psmouse states */
+#define PSMOUSE_NEW_DEVICE	0
+#define PSMOUSE_ACTIVATED	1
+#define PSMOUSE_IGNORE		2
+
 struct psmouse {
 	void *private;
 	struct input_dev dev;
@@ -29,6 +35,7 @@
 	unsigned char type;
 	unsigned char model;
 	unsigned long last;
+	unsigned char state;
 	char acking;
 	volatile char ack;
 	char error;
@@ -36,16 +43,17 @@
 	char phys[32];
 };
 
-#define PSMOUSE_PS2	1
-#define PSMOUSE_PS2PP	2
-#define PSMOUSE_PS2TPP	3
-#define PSMOUSE_GENPS	4
-#define PSMOUSE_IMPS	5
-#define PSMOUSE_IMEX	6
-#define PSMOUSE_SYNAPTICS 7
+#define PSMOUSE_PS2		1
+#define PSMOUSE_PS2PP		2
+#define PSMOUSE_PS2TPP		3
+#define PSMOUSE_GENPS		4
+#define PSMOUSE_IMPS		5
+#define PSMOUSE_IMEX		6
+#define PSMOUSE_SYNAPTICS 	7
 
 int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command);
 
 extern int psmouse_smartscroll;
+extern unsigned int psmouse_resetafter;
 
 #endif /* _PSMOUSE_H */
diff -Nru a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
--- a/drivers/input/mouse/synaptics.c	Fri Sep 19 12:16:32 2003
+++ b/drivers/input/mouse/synaptics.c	Fri Sep 19 12:16:32 2003
@@ -1,6 +1,9 @@
 /*
  * Synaptics TouchPad PS/2 mouse driver
  *
+ *   2003 Dmitry Torokhov <dtor@mail.ru>
+ *     Added support for pass-through port
+ *
  *   2003 Peter Osterlund <petero2@telia.com>
  *     Ported to 2.5 input device infrastructure.
  *
@@ -21,6 +24,7 @@
 
 #include <linux/module.h>
 #include <linux/input.h>
+#include <linux/serio.h>
 #include "psmouse.h"
 #include "synaptics.h"
 
@@ -71,7 +75,7 @@
 
 	if (synaptics_special_cmd(psmouse, mode))
 		return -1;
-	param[0] = 0x14;
+	param[0] = SYN_PS_SET_MODE2;
 	if (psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE))
 		return -1;
 	return 0;
@@ -83,7 +87,7 @@
 
 	if (psmouse_command(psmouse, r, PSMOUSE_CMD_RESET_BAT))
 		return -1;
-	if (r[0] == 0xAA && r[1] == 0x00)
+	if (r[0] == PSMOUSE_RET_BAT && r[1] == PSMOUSE_RET_ID)
 		return 0;
 	return -1;
 }
@@ -106,16 +110,25 @@
  * Read the capability-bits from the touchpad
  * see also the SYN_CAP_* macros
  */
-static int synaptics_capability(struct psmouse *psmouse, unsigned long int *capability)
+static int synaptics_capability(struct psmouse *psmouse, unsigned long int *capability, unsigned long int *ext_cap)
 {
 	unsigned char cap[3];
 
 	if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
 		return -1;
 	*capability = (cap[0]<<16) | (cap[1]<<8) | cap[2];
-	if (SYN_CAP_VALID(*capability))
-		return 0;
-	return -1;
+	*ext_cap = 0;
+	if (!SYN_CAP_VALID(*capability))
+		return -1;
+
+	if (SYN_EXT_CAP_REQUESTS(*capability)) {
+		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
+			*ext_cap = (cap[0]<<16) | (cap[1]<<8) | cap[2];
+	}
+	return 0;
 }
 
 /*
@@ -134,19 +147,11 @@
 	return -1;
 }
 
-static int synaptics_enable_device(struct psmouse *psmouse)
-{
-	if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE))
-		return -1;
-	return 0;
-}
-
 static void print_ident(struct synaptics_data *priv)
 {
 	printk(KERN_INFO "Synaptics Touchpad, model: %ld\n", SYN_ID_MODEL(priv->identity));
 	printk(KERN_INFO " Firmware: %ld.%ld\n", SYN_ID_MAJOR(priv->identity),
 	       SYN_ID_MINOR(priv->identity));
-
 	if (SYN_MODEL_ROT180(priv->model_id))
 		printk(KERN_INFO " 180 degree mounted touchpad\n");
 	if (SYN_MODEL_PORTRAIT(priv->model_id))
@@ -159,12 +164,17 @@
 
 	if (SYN_CAP_EXTENDED(priv->capabilities)) {
 		printk(KERN_INFO " Touchpad has extended capability bits\n");
-		if (SYN_CAP_FOUR_BUTTON(priv->capabilities))
+		if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
+			printk(KERN_INFO " -> %d multi-buttons, i.e. besides standard buttons\n",
+			       (int)(SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)));
+		else if (SYN_CAP_FOUR_BUTTON(priv->capabilities))
 			printk(KERN_INFO " -> four buttons\n");
 		if (SYN_CAP_MULTIFINGER(priv->capabilities))
 			printk(KERN_INFO " -> multifinger detection\n");
 		if (SYN_CAP_PALMDETECT(priv->capabilities))
 			printk(KERN_INFO " -> palm detection\n");
+		if (SYN_CAP_PASS_THROUGH(priv->capabilities))
+			printk(KERN_INFO " -> pass-through port\n");
 	}
 }
 
@@ -172,6 +182,7 @@
 {
 	struct synaptics_data *priv = psmouse->private;
 	int retries = 0;
+	int mode;
 
 	while ((retries++ < 3) && synaptics_reset(psmouse))
 		printk(KERN_ERR "synaptics reset failed\n");
@@ -180,17 +191,107 @@
 		return -1;
 	if (synaptics_model_id(psmouse, &priv->model_id))
 		return -1;
-	if (synaptics_capability(psmouse, &priv->capabilities))
+	if (synaptics_capability(psmouse, &priv->capabilities, &priv->ext_cap))
 		return -1;
-	if (synaptics_set_mode(psmouse, (SYN_BIT_ABSOLUTE_MODE |
-					 SYN_BIT_HIGH_RATE |
-					 SYN_BIT_DISABLE_GESTURE |
-					 SYN_BIT_W_MODE)))
+
+	mode = SYN_BIT_ABSOLUTE_MODE | SYN_BIT_HIGH_RATE;
+	if (SYN_ID_MAJOR(priv->identity) >= 4)
+		mode |= SYN_BIT_DISABLE_GESTURE;
+	if (SYN_CAP_EXTENDED(priv->capabilities))
+		mode |= SYN_BIT_W_MODE;
+	if (synaptics_set_mode(psmouse, mode))
 		return -1;
 
-	synaptics_enable_device(psmouse);
+	return 0;
+}
 
-	print_ident(priv);
+/*****************************************************************************
+ *	Synaptics pass-through PS/2 port support
+ ****************************************************************************/
+static int synaptics_pt_open(struct serio *port)
+{
+	return 0;
+}
+
+static void synaptics_pt_close(struct serio *port)
+{
+}
+
+static int synaptics_pt_write(struct serio *port, unsigned char c)
+{
+	struct psmouse *parent = port->driver;
+	char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
+
+	if (synaptics_special_cmd(parent, c))
+		return -1;
+	if (psmouse_command(parent, &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 = ptport->private;
+
+	if (child) {
+		if (child->state == PSMOUSE_ACTIVATED) {
+			serio_interrupt(ptport, packet[1], 0, NULL);
+			serio_interrupt(ptport, packet[4], 0, NULL);
+			serio_interrupt(ptport, packet[5], 0, NULL);
+			if (child->type >= PSMOUSE_GENPS)
+				serio_interrupt(ptport, packet[2], 0, NULL);
+		} else if (child->state != PSMOUSE_IGNORE) {
+			serio_interrupt(ptport, packet[1], 0, NULL);
+		}
+	}
+}
+
+int synaptics_pt_init(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	struct serio *port;
+	struct psmouse *child;
+
+	if (psmouse->type != PSMOUSE_SYNAPTICS)
+		return -1;
+	if (!SYN_CAP_EXTENDED(priv->capabilities))
+		return -1;
+	if (!SYN_CAP_PASS_THROUGH(priv->capabilities))
+		return -1;
+
+	priv->ptport = port = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!port) {
+		printk(KERN_ERR "synaptics: not enough memory to allocate serio port\n");
+		return -1;
+	}
+
+	memset(port, 0, sizeof(struct serio));
+	port->type = SERIO_PS_PSTHRU;
+	port->name = "Synaptics pass-through";
+	port->phys = "synaptics-pt/serio0";
+	port->write = synaptics_pt_write;
+	port->open = synaptics_pt_open;
+	port->close = synaptics_pt_close;
+	port->driver = psmouse;
+
+	printk(KERN_INFO "serio: %s port at %s\n", port->name, psmouse->phys);
+	serio_register_slave_port(port);
+
+	/* adjust the touchpad to child's choice of protocol */
+	child = port->private;
+	if (child && child->type >= PSMOUSE_GENPS) {
+		if (synaptics_set_mode(psmouse, (SYN_BIT_ABSOLUTE_MODE |
+					 	 SYN_BIT_HIGH_RATE |
+					 	 SYN_BIT_DISABLE_GESTURE |
+						 SYN_BIT_FOUR_BYTE_CLIENT |
+					 	 SYN_BIT_W_MODE)))
+			printk(KERN_INFO "synaptics: failed to enable 4-byte guest protocol\n");
+	}
 
 	return 0;
 }
@@ -213,18 +314,23 @@
 {
 	struct synaptics_data *priv;
 
+#ifndef CONFIG_MOUSE_PS2_SYNAPTICS
+	return -1;
+#endif
 	psmouse->private = priv = kmalloc(sizeof(struct synaptics_data), GFP_KERNEL);
 	if (!priv)
 		return -1;
 	memset(priv, 0, sizeof(struct synaptics_data));
 
-	priv->inSync = 1;
+	priv->out_of_sync = 0;
 
 	if (query_hardware(psmouse)) {
 		printk(KERN_ERR "Unable to query/initialize Synaptics hardware.\n");
 		goto init_fail;
 	}
 
+	print_ident(priv);
+
 	/*
 	 * The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
 	 * which says that they should be valid regardless of the actual size of
@@ -243,7 +349,24 @@
 	set_bit(BTN_RIGHT, psmouse->dev.keybit);
 	set_bit(BTN_FORWARD, psmouse->dev.keybit);
 	set_bit(BTN_BACK, psmouse->dev.keybit);
-
+	if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
+		switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
+		default:
+			printk(KERN_ERR "This touchpad reports more than 8 multi-buttons, don't know how to handle.\n");
+		case 8:
+			set_bit(BTN_7, psmouse->dev.keybit);
+			set_bit(BTN_6, psmouse->dev.keybit);
+		case 6:
+			set_bit(BTN_5, psmouse->dev.keybit);
+			set_bit(BTN_4, psmouse->dev.keybit);
+		case 4:
+			set_bit(BTN_3, psmouse->dev.keybit);
+			set_bit(BTN_2, psmouse->dev.keybit);
+		case 2:
+			set_bit(BTN_1, psmouse->dev.keybit);
+			set_bit(BTN_0, psmouse->dev.keybit);
+			break;
+		}
 	clear_bit(EV_REL, psmouse->dev.evbit);
 	clear_bit(REL_X, psmouse->dev.relbit);
 	clear_bit(REL_Y, psmouse->dev.relbit);
@@ -259,45 +382,85 @@
 {
 	struct synaptics_data *priv = psmouse->private;
 
-	/* Restore touchpad to power on default state */
-	synaptics_set_mode(psmouse, 0);
-
-	kfree(priv);
+	if (psmouse->type == PSMOUSE_SYNAPTICS && priv) {
+		synaptics_set_mode(psmouse, 0);
+		if (priv->ptport) {
+			serio_unregister_slave_port(priv->ptport);
+			kfree(priv->ptport);
+		}
+		kfree(priv);
+	}
 }
 
 /*****************************************************************************
  *	Functions to interpret the absolute mode packets
  ****************************************************************************/
 
-static void synaptics_parse_hw_state(struct synaptics_data *priv, struct synaptics_hw_state *hw)
+static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw)
 {
-	unsigned char *buf = priv->proto_buf;
-
-	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] & 0x2) ? 1 : 0;
 	hw->up    = 0;
 	hw->down  = 0;
+	hw->b0    = 0;
+	hw->b1    = 0;
+	hw->b2    = 0;
+	hw->b3    = 0;
+	hw->b4    = 0;
+	hw->b5    = 0;
+	hw->b6    = 0;
+	hw->b7    = 0;
+
+	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_EXTENDED(priv->capabilities) &&
+		    (SYN_CAP_FOUR_BUTTON(priv->capabilities))) {
+			hw->up = ((buf[3] & 0x01)) ? 1 : 0;
+			if (hw->left)
+				hw->up = !hw->up;
+			hw->down = ((buf[3] & 0x02)) ? 1 : 0;
+			if (hw->right)
+				hw->down = !hw->down;
+		}
+		if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
+		    ((buf[3] & 2) ? !hw->right : hw->right)) {
+			switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
+			default:
+				; /* we did comment while initialising... */
+			case 8:
+				hw->b7 = ((buf[5] & 0x08)) ? 1 : 0;
+				hw->b6 = ((buf[4] & 0x08)) ? 1 : 0;
+			case 6:
+				hw->b5 = ((buf[5] & 0x04)) ? 1 : 0;
+				hw->b4 = ((buf[4] & 0x04)) ? 1 : 0;
+			case 4:
+				hw->b3 = ((buf[5] & 0x02)) ? 1 : 0;
+				hw->b2 = ((buf[4] & 0x02)) ? 1 : 0;
+			case 2:
+				hw->b1 = ((buf[5] & 0x01)) ? 1 : 0;
+				hw->b0 = ((buf[4] & 0x01)) ? 1 : 0;
+			}
+		}
+	} else {
+		hw->x = (((buf[1] & 0x1f) << 8) | buf[2]);
+		hw->y = (((buf[4] & 0x1f) << 8) | buf[5]);
 
-	if (SYN_CAP_EXTENDED(priv->capabilities) &&
-	    (SYN_CAP_FOUR_BUTTON(priv->capabilities))) {
-		hw->up = ((buf[3] & 0x01)) ? 1 : 0;
-		if (hw->left)
-			hw->up = !hw->up;
-		hw->down = ((buf[3] & 0x02)) ? 1 : 0;
-		if (hw->right)
-			hw->down = !hw->down;
+		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;
 	}
 }
 
@@ -310,7 +473,7 @@
 	struct synaptics_data *priv = psmouse->private;
 	struct synaptics_hw_state hw;
 
-	synaptics_parse_hw_state(priv, &hw);
+	synaptics_parse_hw_state(psmouse->packet, priv, &hw);
 
 	if (hw.z > 0) {
 		int w_ok = 0;
@@ -350,7 +513,24 @@
 	input_report_key(dev, BTN_RIGHT,   hw.right);
 	input_report_key(dev, BTN_FORWARD, hw.up);
 	input_report_key(dev, BTN_BACK,    hw.down);
-
+	if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
+		switch(SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
+		default:
+			; /* we did comment while initialising... */
+		case 8:
+			input_report_key(dev, BTN_7,       hw.b7);
+			input_report_key(dev, BTN_6,       hw.b6);
+		case 6:
+			input_report_key(dev, BTN_5,       hw.b5);
+			input_report_key(dev, BTN_4,       hw.b4);
+		case 4:
+			input_report_key(dev, BTN_3,       hw.b3);
+			input_report_key(dev, BTN_2,       hw.b2);
+		case 2:
+			input_report_key(dev, BTN_1,       hw.b1);
+			input_report_key(dev, BTN_0,       hw.b0);
+			break;
+		}
 	input_sync(dev);
 }
 
@@ -358,35 +538,59 @@
 {
 	struct input_dev *dev = &psmouse->dev;
 	struct synaptics_data *priv = psmouse->private;
-	unsigned char *pBuf = priv->proto_buf;
-	unsigned char u = psmouse->packet[0];
+	unsigned char data = psmouse->packet[psmouse->pktcnt - 1];
+	int newabs = SYN_MODEL_NEWABS(priv->model_id);
 
 	input_regs(dev, regs);
 
-	pBuf[priv->proto_buf_tail++] = u;
+	switch (psmouse->pktcnt) {
+	case 1:
+		if (newabs ? ((data & 0xC8) != 0x80) : ((data & 0xC0) != 0xC0)) {
+			printk(KERN_WARNING "Synaptics driver lost sync at 1st byte\n");
+			goto bad_sync;
+		}
+		break;
+	case 2:
+		if (!newabs && ((data & 0x60) != 0x00)) {
+			printk(KERN_WARNING "Synaptics driver lost sync at 2nd byte\n");
+			goto bad_sync;
+		}
+		break;
+	case 4:
+		if (newabs ? ((data & 0xC8) != 0xC0) : ((data & 0xC0) != 0x80)) {
+			printk(KERN_WARNING "Synaptics driver lost sync at 4th byte\n");
+			goto bad_sync;
+		}
+		break;
+	case 5:
+		if (!newabs && ((data & 0x60) != 0x00)) {
+			printk(KERN_WARNING "Synaptics driver lost sync at 5th byte\n");
+			goto bad_sync;
+		}
+		break;
+	default:
+		if (psmouse->pktcnt >= 6) { /* Full packet received */
+			if (priv->out_of_sync) {
+				priv->out_of_sync = 0;
+				printk(KERN_NOTICE "Synaptics driver resynced.\n");
+			}
+
+			if (priv->ptport && synaptics_is_pt_packet(psmouse->packet))
+				synaptics_pass_pt_packet(priv->ptport, psmouse->packet);
+			else
+				synaptics_process_packet(psmouse);
 
-	/* check first byte */
-	if ((priv->proto_buf_tail == 1) && ((u & 0xC8) != 0x80)) {
-		priv->inSync = 0;
-		priv->proto_buf_tail = 0;
-		printk(KERN_WARNING "Synaptics driver lost sync at 1st byte\n");
-		return;
-	}
-
-	/* check 4th byte */
-	if ((priv->proto_buf_tail == 4) && ((u & 0xc8) != 0xc0)) {
-		priv->inSync = 0;
-		priv->proto_buf_tail = 0;
-		printk(KERN_WARNING "Synaptics driver lost sync at 4th byte\n");
-		return;
-	}
-
-	if (priv->proto_buf_tail >= 6) { /* Full packet received */
-		if (!priv->inSync) {
-			priv->inSync = 1;
-			printk(KERN_NOTICE "Synaptics driver resynced.\n");
+			psmouse->pktcnt = 0;
 		}
-		synaptics_process_packet(psmouse);
-		priv->proto_buf_tail = 0;
+		break;
+	}
+	return;
+
+ bad_sync:
+	priv->out_of_sync++;
+	psmouse->pktcnt = 0;
+	if (psmouse_resetafter > 0 && priv->out_of_sync	== psmouse_resetafter) {
+		psmouse->state = PSMOUSE_IGNORE;
+		serio_rescan(psmouse->serio);
 	}
 }
diff -Nru a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
--- a/drivers/input/mouse/synaptics.h	Fri Sep 19 12:16:32 2003
+++ b/drivers/input/mouse/synaptics.h	Fri Sep 19 12:16:32 2003
@@ -12,6 +12,7 @@
 
 extern void synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs);
 extern int synaptics_init(struct psmouse *psmouse);
+extern int synaptics_pt_init(struct psmouse *psmouse);
 extern void synaptics_disconnect(struct psmouse *psmouse);
 
 /* synaptics queries */
@@ -22,12 +23,14 @@
 #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 */
@@ -42,11 +45,14 @@
 
 /* synaptics capability bits */
 #define SYN_CAP_EXTENDED(c)		((c) & (1 << 23))
+#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) == 1)
+#define SYN_CAP_MULTI_BUTTON_NO(ec)	(((ec) & 0x00f000) >> 12)
 
 /* synaptics modes query bits */
 #define SYN_MODE_ABSOLUTE(m)		((m) & (1 << 7))
@@ -62,6 +68,10 @@
 #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
+
 /*
  * A structure to describe the state of the touchpad hardware (buttons and pad)
  */
@@ -75,21 +85,28 @@
 	int right;
 	int up;
 	int down;
+	int b0;
+	int b1;
+	int b2;
+	int b3;
+	int b4;
+	int b5;
+	int b6;
+	int b7;
 };
 
 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 */
 
 	/* Data for normal processing */
-	unsigned char proto_buf[6];		/* Buffer for Packet */
-	unsigned char last_byte;		/* last received byte */
-	int inSync;				/* Packets in sync */
-	int proto_buf_tail;
-
+	unsigned int out_of_sync;		/* # of packets out of sync */
 	int old_w;				/* Previous w value */
+	
+	struct serio *ptport;			/* pass-through port */
 };
 
 #endif /* _SYNAPTICS_H */
diff -Nru a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
--- a/drivers/input/serio/serio.c	Fri Sep 19 12:16:32 2003
+++ b/drivers/input/serio/serio.c	Fri Sep 19 12:16:32 2003
@@ -49,7 +49,9 @@
 
 EXPORT_SYMBOL(serio_interrupt);
 EXPORT_SYMBOL(serio_register_port);
+EXPORT_SYMBOL(serio_register_slave_port);
 EXPORT_SYMBOL(serio_unregister_port);
+EXPORT_SYMBOL(serio_unregister_slave_port);
 EXPORT_SYMBOL(serio_register_device);
 EXPORT_SYMBOL(serio_unregister_device);
 EXPORT_SYMBOL(serio_open);
@@ -166,6 +168,17 @@
 	up(&serio_sem);
 }
 
+/*
+ * Same as serio_register_port but does not try to acquire serio_sem.
+ * Should be used when registering a serio from other input device's
+ * connect() function.
+ */
+void serio_register_slave_port(struct serio *serio)
+{
+	list_add_tail(&serio->node, &serio_list);
+	serio_find_dev(serio);
+}
+
 void serio_unregister_port(struct serio *serio)
 {
 	down(&serio_sem);
@@ -173,6 +186,18 @@
 	if (serio->dev && serio->dev->disconnect)
 		serio->dev->disconnect(serio);
 	up(&serio_sem);
+}
+
+/*
+ * Same as serio_unregister_port but does not try to acquire serio_sem.
+ * Should be used when unregistering a serio from other input device's
+ * disconnect() function.
+ */
+void serio_unregister_slave_port(struct serio *serio)
+{
+	list_del_init(&serio->node);
+	if (serio->dev && serio->dev->disconnect)
+		serio->dev->disconnect(serio);
 }
 
 void serio_register_device(struct serio_dev *dev)
diff -Nru a/include/linux/serio.h b/include/linux/serio.h
--- a/include/linux/serio.h	Fri Sep 19 12:16:32 2003
+++ b/include/linux/serio.h	Fri Sep 19 12:16:32 2003
@@ -65,7 +65,9 @@
 irqreturn_t serio_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs);
 
 void serio_register_port(struct serio *serio);
+void serio_register_slave_port(struct serio *serio);
 void serio_unregister_port(struct serio *serio);
+void serio_unregister_slave_port(struct serio *serio);
 void serio_register_device(struct serio_dev *dev);
 void serio_unregister_device(struct serio_dev *dev);
 
@@ -104,6 +106,7 @@
 #define SERIO_RS232	0x02000000UL
 #define SERIO_HIL_MLC	0x03000000UL
 #define SERIO_PC9800	0x04000000UL
+#define SERIO_PS_PSTHRU	0x05000000UL
 
 #define SERIO_PROTO	0xFFUL
 #define SERIO_MSC	0x01


  reply	other threads:[~2003-09-19 10:33 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-09-19 10:26 [PATCH 1/11] input: Restore synaptics pad mode on module unload Vojtech Pavlik
2003-09-19 10:26 ` [PATCH 2/11] input: Forced release of keys on AT kbds Vojtech Pavlik
2003-09-19 10:26   ` [PATCH 3/11] input: Fix Sega Saturn pad support Vojtech Pavlik
2003-09-19 10:26     ` Vojtech Pavlik [this message]
2003-09-19 10:26       ` [PATCH 5/11] input: Fix resume of PS/2 mouse Vojtech Pavlik
2003-09-19 10:26         ` [PATCH 6/11] input: Change name of Synaptics protocol to SynPS/2 Vojtech Pavlik
2003-09-19 10:26           ` [PATCH 7/11] input: Fix psmouse->pktcnt in Synaptics mode Vojtech Pavlik
2003-09-19 10:26             ` [PATCH 8/11] input: Fix the INPUT_KEYCODE macro and its usage Vojtech Pavlik
2003-09-19 10:26               ` [PATCH 9/11] input: Enlarge the timeout for PS/2 mouse full reset Vojtech Pavlik
2003-09-19 10:26                 ` [PATCH 10/11] input: Fix I-Force sleeping issues Vojtech Pavlik
2003-09-19 10:26                   ` [PATCH 11/11] input: Claim serio early in serio_open() Vojtech Pavlik
2003-09-21 13:02             ` [PATCH 7/11] input: Fix psmouse->pktcnt in Synaptics mode Peter Osterlund
2003-09-21 17:19               ` Vojtech Pavlik
2003-09-22 14:20         ` [PATCH 5/11] input: Fix resume of PS/2 mouse Pavel Machek
2003-09-20 20:31 ` [PATCH 1/11] input: Restore synaptics pad mode on module unload jhf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1063967201965@twilight.ucw.cz \
    --to=vojtech@suse.cz \
    --cc=linux-kernel@vger.kernel.org \
    --cc=torvalds@transmeta.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox