All of lore.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.