All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Joseph Fannin" <jhf@rivenstone.net>
To: Kernel Mailing List <linux-kernel@vger.kernel.org>
Cc: Vojtech Pavlik <vojtech@suse.cz>, Joseph Fannin <jhf@rivenstone.net>
Subject: [PATCH] Synaptics TouchPad driver for 2.5.70
Date: 11 Jun 2003 00:52:06 +0200	[thread overview]
Message-ID: <m2u1axk0kp.fsf@p4.localdomain> (raw)

Hi!

Here is a driver for the Synaptics TouchPad for 2.5.70. It is largely
based on the XFree86 driver. This driver operates the touchpad in
absolute mode and emulates a three button mouse with two scroll
wheels. Features include:

* Multi finger tapping.
* Vertical and horizontal scrolling.
* Edge scrolling during drag operations.
* Palm detection.
* Corner tapping.

The only major missing feature is runtime configuration of driver
parameters. What is the best way to implement that? I was thinking of
sending EV_MSC events to the driver using the /dev/input/event*
interface and define my own codes for the different driver parameters.

Comments?


diff -u -r -N ../../linus/main/linux/drivers/input/mouse/Kconfig linux/drivers/input/mouse/Kconfig
--- ../../linus/main/linux/drivers/input/mouse/Kconfig	Sat Jun  7 21:40:38 2003
+++ linux/drivers/input/mouse/Kconfig	Tue Jun 10 00:08:14 2003
@@ -28,6 +28,16 @@
 	  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.
+
+	  If unsure, say Y.
+
 config MOUSE_SERIAL
 	tristate "Serial mouse"
 	depends on INPUT && INPUT_MOUSE && SERIO
diff -u -r -N ../../linus/main/linux/drivers/input/mouse/psmouse.c linux/drivers/input/mouse/psmouse.c
--- ../../linus/main/linux/drivers/input/mouse/psmouse.c	Sat Jun  7 21:40:38 2003
+++ linux/drivers/input/mouse/psmouse.c	Tue Jun 10 00:11:37 2003
@@ -41,6 +41,7 @@
 #define PSMOUSE_RET_NAK		0xfe
 
 struct psmouse {
+	void *private;
 	struct input_dev dev;
 	struct serio *serio;
 	char *vendor;
@@ -65,8 +66,11 @@
 #define PSMOUSE_GENPS	4
 #define PSMOUSE_IMPS	5
 #define PSMOUSE_IMEX	6
+#define PSMOUSE_SYNAPTICS 7
 
-static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "PS2T++", "GenPS/2", "ImPS/2", "ImExPS/2" };
+#include "synaptics.c"
+
+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
@@ -209,6 +213,16 @@
 		goto out;
 	}
 
+	if (psmouse->pktcnt == 1 && 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);
 		goto out;
@@ -343,12 +357,12 @@
        psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO);
 
        if (param[1] == 0x47) {
-               /* We could do more here. But it's sufficient just
-                  to stop the subsequent probes from screwing the
-                  thing up. */
-               psmouse->vendor = "Synaptics";
-               psmouse->name = "TouchPad";
-               return PSMOUSE_PS2;
+		int type = PSMOUSE_PS2;
+		psmouse->vendor = "Synaptics";
+		psmouse->name = "TouchPad";
+		if (synaptics_init(psmouse) == 0)
+			type = PSMOUSE_SYNAPTICS;
+		return type;
        }
 
 /*
@@ -598,6 +612,7 @@
 	struct psmouse *psmouse = serio->private;
 	input_unregister_device(&psmouse->dev);
 	serio_close(serio);
+	synaptics_disconnect(psmouse);
 	kfree(psmouse);
 }
 
diff -u -r -N ../../linus/main/linux/drivers/input/mouse/synaptics.c linux/drivers/input/mouse/synaptics.c
--- ../../linus/main/linux/drivers/input/mouse/synaptics.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/input/mouse/synaptics.c	Wed Jun 11 00:34:54 2003
@@ -0,0 +1,801 @@
+/*
+ * Synaptics TouchPad PS/2 mouse driver
+ *
+ *   2003 Peter Osterlund <petero2@telia.com>
+ *     Ported to 2.5 input device infrastructure.
+ *
+ *   2002 Peter Osterlund <petero2@telia.com>
+ *     patches for fast scrolling, palm detection, edge motion,
+ *     horizontal scrolling
+ *
+ *   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) 1999 Henry Davies <hdavies@ameritech.net> for the
+ *     absolute to relative translation code (from the gpm-source)
+ *     and some other ideas
+ *
+ *   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.
+ */
+
+#ifndef CONFIG_MOUSE_PS2_SYNAPTICS
+
+static inline void synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs) {}
+static inline int synaptics_init(struct psmouse *psmouse) { return -1; }
+static inline void synaptics_disconnect(struct psmouse *psmouse) {}
+
+#else
+
+#include "synaptics.h"
+
+
+static int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command);
+
+/*
+ * Use the Synaptics extended ps/2 syntax to write a special command byte.
+ * special command: 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
+ *                  is the command. A 0xF3 or 0xE9 must follow (see synaptics_send_cmd
+ *                  and synaptics_set_mode)
+ */
+static int synaptics_special_cmd(struct psmouse *psmouse, unsigned char command)
+{
+	int i;
+
+	if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11))
+		return -1;
+
+	for (i = 6; i >= 0; i -= 2) {
+		unsigned char d = (command >> i) & 3;
+		if (psmouse_command(psmouse, &d, PSMOUSE_CMD_SETRES))
+			return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * 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 (synaptics_special_cmd(psmouse, c))
+		return -1;
+	if (psmouse_command(psmouse, param, PSMOUSE_CMD_GETINFO))
+		return -1;
+	return 0;
+}
+
+/*****************************************************************************
+ *	Synaptics communications functions
+ ****************************************************************************/
+
+/*
+ * Set the synaptics touchpad mode byte by special commands
+ */
+static int synaptics_set_mode(struct psmouse *psmouse, unsigned char mode)
+{
+	unsigned char param[1];
+
+	if (synaptics_special_cmd(psmouse, mode))
+		return -1;
+	param[0] = 0x14;
+	if (psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE))
+		return -1;
+	return 0;
+}
+
+static int synaptics_reset(struct psmouse *psmouse)
+{
+	unsigned char r[2];
+
+	if (psmouse_command(psmouse, r, PSMOUSE_CMD_RESET_BAT))
+		return -1;
+	if (r[0] == 0xAA && r[1] == 0x00)
+		return 0;
+	return -1;
+}
+
+/*
+ * Read the model-id bytes from the touchpad
+ * see also SYN_MODEL_* macros
+ */
+static int synaptics_model_id(struct psmouse *psmouse, unsigned long int *model_id)
+{
+	unsigned char mi[3];
+
+	if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi))
+		return -1;
+	*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, unsigned long int *capability)
+{
+	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;
+}
+
+/*
+ * Identify Touchpad
+ * See also the SYN_ID_* macros
+ */
+static int synaptics_identify(struct psmouse *psmouse, unsigned long int *ident)
+{
+	unsigned char id[3];
+
+	if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id))
+		return -1;
+	*ident = (id[0]<<16) | (id[1]<<8) | id[2];
+	if (SYN_ID_IS_SYNAPTICS(*ident))
+		return 0;
+	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 " Firware: %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))
+		printk(KERN_INFO " portrait touchpad\n");
+	printk(KERN_INFO " Sensor: %ld\n", SYN_MODEL_SENSOR(priv->model_id));
+	if (SYN_MODEL_NEWABS(priv->model_id))
+		printk(KERN_INFO " new absolute packet format\n");
+	if (SYN_MODEL_PEN(priv->model_id))
+		printk(KERN_INFO " pen detection\n");
+
+	if (SYN_CAP_EXTENDED(priv->capabilities)) {
+		printk(KERN_INFO " Touchpad has extended capability bits\n");
+		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");
+	}
+}
+
+static int query_hardware(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	int retries = 3;
+
+	while ((retries++ <= 3) && synaptics_reset(psmouse))
+		printk(KERN_ERR "synaptics reset failed\n");
+
+	if (synaptics_identify(psmouse, &priv->identity))
+		return -1;
+	if (synaptics_model_id(psmouse, &priv->model_id))
+		return -1;
+	if (synaptics_capability(psmouse, &priv->capabilities))
+		return -1;
+	if (synaptics_set_mode(psmouse, (SYN_BIT_ABSOLUTE_MODE |
+					 SYN_BIT_HIGH_RATE |
+					 SYN_BIT_DISABLE_GESTURE |
+					 SYN_BIT_W_MODE)))
+		return -1;
+
+	synaptics_enable_device(psmouse);
+
+	print_ident(priv);
+
+	return 0;
+}
+
+static void synaptics_repeat_timer(unsigned long data)
+{
+	struct psmouse *psmouse = (void *) data;
+	struct input_dev *dev = &psmouse->dev;
+	struct synaptics_data *priv = psmouse->private;
+
+	if (priv->repeat_buttons & 0x01)
+		input_report_rel(dev, REL_WHEEL, 1);
+	if (priv->repeat_buttons & 0x02)
+		input_report_rel(dev, REL_WHEEL, -1);
+	input_sync(dev);
+
+	if (atomic_read(&priv->timer_active))
+		mod_timer(&priv->timer, jiffies + 100 * HZ / 1000);
+}
+
+static struct synaptics_parameters default_params = {
+	.left_edge			= 1800,
+	.right_edge			= 5400,
+	.top_edge			= 4200,
+	.bottom_edge			= 1500,
+	.finger_low			= 25,
+	.finger_high			= 30,
+	.tap_time			= 15,
+	.tap_move			= 220,
+	.emulate_mid_button_time	= 6,
+	.scroll_dist_vert		= 100,
+	.scroll_dist_horiz		= 100,
+	.speed				= 25,
+	.edge_motion_speed		= 40,
+	.updown_button_scrolling	= 1
+};
+
+static int synaptics_init(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv;
+
+	psmouse->private = priv = kmalloc(sizeof(struct synaptics_data), GFP_KERNEL);
+	if (!priv)
+		return -1;
+	memset(priv, 0, sizeof(struct synaptics_data));
+
+	priv->inSync = 1;
+
+	/* Set default parameters */
+	priv->params = default_params;
+
+	if (query_hardware(psmouse)) {
+		printk(KERN_ERR "Unable to query/initialize Synaptics hardware.\n");
+		goto init_fail;
+	}
+
+	set_bit(REL_WHEEL, psmouse->dev.relbit);
+	set_bit(REL_HWHEEL, psmouse->dev.relbit);
+
+	init_timer(&priv->timer);
+	priv->timer.data = (unsigned long)psmouse;
+	priv->timer.function = synaptics_repeat_timer;
+	atomic_set(&priv->timer_active, 0);
+
+	return 0;
+
+ init_fail:
+	kfree(priv);
+	return -1;
+}
+
+static void synaptics_disconnect(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	atomic_set(&priv->timer_active, 0);
+	priv->repeat_buttons = 0;
+	del_timer_sync(&priv->timer);
+
+	kfree(priv);
+}
+
+/*****************************************************************************
+ *	Functions to interpret the absolute mode packets
+ ****************************************************************************/
+
+#define DIFF_TIME(a, b) (((a) > (b)) ? (a) - (b) : (b) - (a))
+
+typedef enum {
+	BOTTOM_EDGE = 1,
+	TOP_EDGE = 2,
+	LEFT_EDGE = 4,
+	RIGHT_EDGE = 8,
+	LEFT_BOTTOM_EDGE = BOTTOM_EDGE | LEFT_EDGE,
+	RIGHT_BOTTOM_EDGE = BOTTOM_EDGE | RIGHT_EDGE,
+	RIGHT_TOP_EDGE = TOP_EDGE | RIGHT_EDGE,
+	LEFT_TOP_EDGE = TOP_EDGE | LEFT_EDGE
+} edge_type;
+
+static edge_type
+edge_detection(struct synaptics_data *priv, int x, int y)
+{
+	edge_type edge = 0;
+
+	if (x > priv->params.right_edge)
+		edge |= RIGHT_EDGE;
+	else if (x < priv->params.left_edge)
+		edge |= LEFT_EDGE;
+
+	if (y > priv->params.top_edge)
+		edge |= TOP_EDGE;
+	else if (y < priv->params.bottom_edge)
+		edge |= BOTTOM_EDGE;
+
+	return edge;
+}
+
+#define MOVE_HIST(a) ((priv->count_packet_finger-(a))%SYNAPTICS_MOVE_HISTORY)
+
+static inline int clamp(int val, int min, int max)
+{
+	if (val < min)
+		return min;
+	else if (val < max)
+		return val;
+	else
+		return max;
+}
+
+static void synaptics_parse_hw_state(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;
+
+	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;
+	}
+}
+
+static int synaptics_emulate_mid_button(struct synaptics_data *priv,
+					struct synaptics_hw_state *hw)
+{
+	int timeout = (DIFF_TIME(priv->count_packet, priv->count_button_delay) >=
+		       priv->params.emulate_mid_button_time);
+	int mid = 0;
+
+	for (;;) {
+		switch (priv->mid_emu_state) {
+		case MBE_OFF:
+			if (hw->left) {
+				priv->mid_emu_state = MBE_LEFT;
+			} else if (hw->right) {
+				priv->mid_emu_state = MBE_RIGHT;
+			} else {
+				priv->count_button_delay = priv->count_packet;
+				goto done;
+			}
+			break;
+		case MBE_LEFT:
+			if (!hw->left || timeout) {
+				hw->left = 1;
+				priv->mid_emu_state = MBE_OFF;
+				goto done;
+			} else if (hw->right) {
+				priv->mid_emu_state = MBE_MID;
+			} else {
+				hw->left = 0;
+				goto done;
+			}
+			break;
+		case MBE_RIGHT:
+			if (!hw->right || timeout) {
+				hw->right = 1;
+				priv->mid_emu_state = MBE_OFF;
+				goto done;
+			} else if (hw->left) {
+				priv->mid_emu_state = MBE_MID;
+			} else {
+				hw->right = 0;
+				goto done;
+			}
+			break;
+		case MBE_MID:
+			if (!hw->left && !hw->right) {
+				priv->mid_emu_state = MBE_OFF;
+			} else {
+				mid = 1;
+				hw->left = hw->right = 0;
+				goto done;
+			}
+			break;
+		}
+	}
+ done:
+	return mid;
+}
+
+static int synaptics_detect_finger(struct synaptics_data *priv,
+				   const struct synaptics_hw_state *hw)
+{
+	const struct synaptics_parameters *para = &priv->params;
+	int finger;
+
+	/* finger detection thru pressure and threshold */
+	finger = (((hw->z > para->finger_high) && !priv->finger_flag) ||
+		  ((hw->z > para->finger_low)  &&  priv->finger_flag));
+
+	/* palm detection */
+	if (!SYN_CAP_EXTENDED(priv->capabilities) || !SYN_CAP_PALMDETECT(priv->capabilities))
+		return finger;
+
+	if (finger) {
+		if ((hw->z > 200) && (hw->w > 10))
+			priv->palm = 1;
+	} else {
+		priv->palm = 0;
+	}
+	if (hw->x == 0)
+		priv->avg_w = 0;
+	else
+		priv->avg_w += (hw->w - priv->avg_w + 1) / 2;
+	if (finger && !priv->finger_flag) {
+		int safe_w = max_t(int, hw->w, priv->avg_w);
+		if (hw->w < 2)
+			finger = 1;		/* more than one finger -> not a palm */
+		else if ((safe_w < 6) && (priv->prev_z < para->finger_high))
+			finger = 1;		/* thin finger, distinct touch -> not a palm */
+		else if ((safe_w < 7) && (priv->prev_z < para->finger_high / 2))
+			finger = 1;		/* thin finger, distinct touch -> not a palm */
+		else if (hw->z > priv->prev_z + 1)
+			finger = 0;		/* z not stable, may be a palm */
+		else if (hw->z < priv->prev_z - 5)
+			finger = 0;		/* z not stable, may be a palm */
+		else if (hw->z > 200)
+			finger = 0;		/* z too large -> probably palm */
+		else if (hw->w > 10)
+			finger = 0;		/* w too large -> probably palm */
+	}
+	priv->prev_z = hw->z;
+
+	return finger;
+}
+
+/*
+ *  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;
+	const struct synaptics_parameters *para = &priv->params;
+	struct synaptics_hw_state hw;
+	int dx, dy;
+	edge_type edge;
+	int scroll_up, scroll_down, scroll_left, scroll_right;
+	int double_click;
+
+	int finger;
+	int mid;
+
+	synaptics_parse_hw_state(priv, &hw);
+
+	edge = edge_detection(priv, hw.x, hw.y);
+
+	mid = synaptics_emulate_mid_button(priv, &hw);
+
+	/* Up/Down-button scrolling or middle/double-click */
+	double_click = 0;
+	if (!para->updown_button_scrolling) {
+		if (down) {
+			/* map down-button to middle-button */
+			mid = 1;
+		}
+
+		if (hw.up) {
+			/* up-button generates double-click */
+			if (!priv->prev_up)
+				double_click = 1;
+		}
+		priv->prev_up = hw.up;
+
+		/* reset up/down button events */
+		hw.up = hw.down = 0;
+	}
+
+	finger = synaptics_detect_finger(priv, &hw);
+
+	/* tap and drag detection */
+	if (priv->palm) {
+		/* Palm detected, skip tap/drag processing */
+	} else if (finger && !priv->finger_flag) {
+		/* touched */
+		if (priv->tap) {
+			priv->drag = 1; /* drag gesture */
+		}
+		priv->touch_on.x = hw.x;
+		priv->touch_on.y = hw.y;
+		priv->touch_on.packet = priv->count_packet;
+	} else if (!finger && priv->finger_flag) {
+		/* untouched */
+		/* check if
+		   1. the tap is in tap_time
+		   2. the max movement is in tap_move or more than one finger is tapped */
+		if ((DIFF_TIME(priv->count_packet, priv->touch_on.packet) < para->tap_time) &&
+		    (((abs(hw.x - priv->touch_on.x) < para->tap_move) &&
+		      (abs(hw.y - priv->touch_on.y) < para->tap_move)) ||
+		     priv->finger_count)) {
+			if (priv->drag) {
+				priv->doubletap = 1;
+				priv->tap = 0;
+			} else {
+				priv->count_packet_tapping = priv->count_packet;
+				priv->tap = 1;
+				switch (priv->finger_count) {
+				case 0:
+					switch (edge) {
+					case RIGHT_TOP_EDGE:
+						priv->tap_mid = 1;
+						break;
+					case RIGHT_BOTTOM_EDGE:
+						priv->tap_right = 1;
+						break;
+					case LEFT_TOP_EDGE:
+						break;
+					case LEFT_BOTTOM_EDGE:
+						break;
+					default:
+						priv->tap_left = 1;
+					}
+					break;
+				case 2:
+					priv->tap_mid = 1;
+					break;
+				case 3: 
+					priv->tap_right = 1;
+					break;
+				default:
+					priv->tap_left = 1;
+				}
+			}
+		}
+		priv->drag = 0;
+	}
+
+	/* detecting 2 and 3 fingers */
+	if (finger && /* finger is on the surface */
+	    (DIFF_TIME(priv->count_packet, priv->touch_on.packet) < para->tap_time) &&
+	    SYN_CAP_MULTIFINGER(priv->capabilities)) {
+		/* count fingers when reported */
+		if ((hw.w == 0) && (priv->finger_count == 0))
+			priv->finger_count = 2;
+		if (hw.w == 1)
+			priv->finger_count = 3;
+	} else {
+		priv->finger_count = 0;
+	}
+
+	/* reset tapping button flags */
+	if (!priv->tap && !priv->drag && !priv->doubletap) {
+		priv->tap_left = priv->tap_mid = priv->tap_right = 0;
+	}
+
+	/* tap processing */
+	if (priv->tap &&
+	    (DIFF_TIME(priv->count_packet, priv->count_packet_tapping) < para->tap_time)) {
+		hw.left  |= priv->tap_left;
+		mid      |= priv->tap_mid;
+		hw.right |= priv->tap_right;
+	} else {
+		priv->tap = 0;
+	}
+
+	/* drag processing */
+	if (priv->drag) {
+		hw.left  |= priv->tap_left;
+		mid      |= priv->tap_mid;
+		hw.right |= priv->tap_right;
+	}
+
+	/* double tap processing */
+	if (priv->doubletap && !priv->finger_flag) {
+		hw.left  |= priv->tap_left;
+		mid      |= priv->tap_mid;
+		hw.right |= priv->tap_right;
+		priv->doubletap = 0;
+	}
+
+	/* scroll detection */
+	if (finger && !priv->finger_flag) {
+		if (edge & RIGHT_EDGE) {
+			priv->vert_scroll_on = 1;
+			priv->scroll_y = hw.y;
+		}
+		if (edge & BOTTOM_EDGE) {
+			priv->horiz_scroll_on = 1;
+			priv->scroll_x = hw.x;
+		}
+	}
+	if (priv->vert_scroll_on && (!(edge & RIGHT_EDGE) || !finger || priv->palm)) {
+		priv->vert_scroll_on = 0;
+	}
+	if (priv->horiz_scroll_on && (!(edge & BOTTOM_EDGE) || !finger || priv->palm)) {
+		priv->horiz_scroll_on = 0;
+	}
+
+	/* scroll processing */
+	scroll_up = 0;
+	scroll_down = 0;
+	if (priv->vert_scroll_on) {
+		/* + = up, - = down */
+		while (hw.y - priv->scroll_y > para->scroll_dist_vert) {
+			scroll_up++;
+			priv->scroll_y += para->scroll_dist_vert;
+		}
+		while (hw.y - priv->scroll_y < -para->scroll_dist_vert) {
+			scroll_down++;
+			priv->scroll_y -= para->scroll_dist_vert;
+		}
+	}
+	scroll_left = 0;
+	scroll_right = 0;
+	if (priv->horiz_scroll_on) {
+		/* + = right, - = left */
+		while (hw.x - priv->scroll_x > para->scroll_dist_horiz) {
+			scroll_right++;
+			priv->scroll_x += para->scroll_dist_horiz;
+		}
+		while (hw.x - priv->scroll_x < -para->scroll_dist_horiz) {
+			scroll_left++;
+			priv->scroll_x -= para->scroll_dist_horiz;
+		}
+	}
+
+	/* movement */
+	dx = dy = 0;
+	if (finger && !priv->vert_scroll_on && !priv->horiz_scroll_on &&
+	    !priv->finger_count && !priv->palm) {
+		if (priv->count_packet_finger > 3) { /* min. 3 packets */
+			int h2x = priv->move_hist[MOVE_HIST(2)].x;
+			int h2y = priv->move_hist[MOVE_HIST(2)].y;
+
+			dx =  ((hw.x - h2x) / 2);
+			dy = -((hw.y - h2y) / 2);
+
+			if (priv->drag) {
+				if (edge & RIGHT_EDGE) {
+					dx += clamp(para->edge_motion_speed - dx,
+						    0, para->edge_motion_speed);
+				} else if (edge & LEFT_EDGE) {
+					dx -= clamp(para->edge_motion_speed + dx,
+						    0, para->edge_motion_speed);
+				}
+				if (edge & TOP_EDGE) {
+					dy -= clamp(para->edge_motion_speed + dy,
+						    0, para->edge_motion_speed);
+				} else if (edge & BOTTOM_EDGE) {
+					dy += clamp(para->edge_motion_speed - dy,
+						    0, para->edge_motion_speed);
+				}
+			}
+
+			/* Scale dx and dy to get pointer motion values */
+			priv->accum_dx += dx * para->speed;
+			priv->accum_dy += dy * para->speed;
+
+			dx = priv->accum_dx / 256;
+			dy = priv->accum_dy / 256;
+
+			priv->accum_dx -= dx * 256;
+			priv->accum_dy -= dy * 256;
+		}
+
+		priv->count_packet_finger++;
+	} else {
+		priv->count_packet_finger = 0;
+	}
+
+	priv->count_packet++;
+
+	/* Flags */
+	priv->finger_flag = finger;
+
+	/* generate a history of the absolute positions */
+	priv->move_hist[MOVE_HIST(0)].x = hw.x;
+	priv->move_hist[MOVE_HIST(0)].y = hw.y;
+
+	/* repeat timer for up/down buttons */
+	/* when you press a button the packets will only send for a second, so
+	   we have to use a timer for repeating */
+	if ((hw.up || hw.down) && para->updown_button_scrolling) {
+		if (!atomic_read(&priv->timer_active)) {
+			priv->repeat_buttons = ((hw.up    ? 0x01 : 0) |
+						(hw.down  ? 0x02 : 0));
+			atomic_set(&priv->timer_active, 1);
+			mod_timer(&priv->timer, jiffies + 200 * HZ / 1000);
+		}
+	} else if (atomic_read(&priv->timer_active)) {
+		atomic_set(&priv->timer_active, 0);
+		priv->repeat_buttons = 0;
+		del_timer_sync(&priv->timer);
+	}
+
+	/* Post events */
+	input_report_rel(dev, REL_X, dx);
+	input_report_rel(dev, REL_Y, dy);
+
+	input_report_key(dev, BTN_LEFT,   hw.left);
+	input_report_key(dev, BTN_MIDDLE, mid);
+	input_report_key(dev, BTN_RIGHT,  hw.right);
+
+	if (hw.up && !priv->last_up)
+		input_report_rel(dev, REL_WHEEL, 1);
+	if (hw.down && !priv->last_down)
+		input_report_rel(dev, REL_WHEEL, -1);
+	priv->last_up = hw.up;
+	priv->last_down = hw.down;
+
+	input_report_rel(dev, REL_WHEEL, scroll_up - scroll_down);
+	input_report_rel(dev, REL_HWHEEL, scroll_right - scroll_left);
+
+	if (double_click) {
+		int i;
+		for (i = 0; i < 2; i++) {
+			input_report_key(dev, BTN_LEFT, !hw.left);
+			input_report_key(dev, BTN_LEFT, hw.left);
+		}
+	}
+
+	input_sync(dev);
+}
+
+static void synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
+{
+	struct input_dev *dev = &psmouse->dev;
+	struct synaptics_data *priv = psmouse->private;
+	unsigned char *pBuf = priv->proto_buf;
+	unsigned char u = psmouse->packet[0];
+
+	input_regs(dev, regs);
+
+	pBuf[priv->proto_buf_tail++] = u;
+
+	/* 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");
+		}
+		synaptics_process_packet(psmouse);
+		priv->proto_buf_tail = 0;
+	}
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
diff -u -r -N ../../linus/main/linux/drivers/input/mouse/synaptics.h linux/drivers/input/mouse/synaptics.h
--- ../../linus/main/linux/drivers/input/mouse/synaptics.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/input/mouse/synaptics.h	Wed Jun 11 00:08:41 2003
@@ -0,0 +1,158 @@
+/*
+ * 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
+
+/* 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_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_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)
+
+/* 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)
+
+
+struct synaptics_parameters {
+	int left_edge;				/* Edge coordinates, absolute */
+	int right_edge;
+	int top_edge;
+	int bottom_edge;
+
+	int finger_low, finger_high;		/* finger detection values in Z-values */
+	int tap_time, tap_move;			/* max. tapping-time and movement in packets and coord. */
+	int emulate_mid_button_time;		/* Max time between left and right button presses to
+						   emulate a middle button press. */
+	int scroll_dist_vert;			/* Scrolling distance in absolute coordinates */
+	int scroll_dist_horiz;			/* Scrolling distance in absolute coordinates */
+	int speed;				/* Pointer motion speed */
+	int edge_motion_speed;			/* Edge motion speed when dragging */
+	int updown_button_scrolling;		/* Up/Down-Button scrolling or middle/double-click */
+};
+
+/*
+ * 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;
+	int left;
+	int right;
+	int up;
+	int down;
+};
+
+#define SYNAPTICS_MOVE_HISTORY	5
+
+struct SynapticsTapRec {
+	int x, y;
+	unsigned int packet;
+};
+
+struct SynapticsMoveHist {
+	int x, y;
+};
+
+enum MidButtonEmulation {
+	MBE_OFF,			/* No button pressed */
+	MBE_LEFT,			/* Left button pressed, waiting for right button or timeout */
+	MBE_RIGHT,			/* Right button pressed, waiting for left button or timeout */
+	MBE_MID				/* Left and right buttons pressed, waiting for both buttons
+					   to be released */
+};
+
+struct synaptics_data {
+	struct synaptics_parameters params;
+
+	/* Data read from the touchpad */
+	unsigned long int model_id;		/* Model-ID */
+	unsigned long int capabilities; 	/* 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;
+
+	struct SynapticsTapRec touch_on;	/* data when the touchpad is touched */
+	struct SynapticsMoveHist move_hist[SYNAPTICS_MOVE_HISTORY];
+						/* movement history */
+	int accum_dx;				/* Accumulated fractional pointer motion */
+	int accum_dy;
+	int scroll_x;				/* last x-scroll position */
+	int scroll_y;				/* last y-scroll position */
+	unsigned int count_packet_finger;	/* packet counter with finger on the touchpad */
+	unsigned int count_packet;		/* packet counter */
+	unsigned int count_packet_tapping;	/* packet counter for tapping */
+	unsigned int count_button_delay;	/* button delay for 3rd button emulation */
+	unsigned int prev_up;			/* Previous up button value, for double click emulation */
+	int finger_flag;			/* previous finger */
+	int tap, drag, doubletap;		/* feature flags */
+	int tap_left, tap_mid, tap_right;	/* tapping buttons */
+	int vert_scroll_on;			/* scrolling flag */
+	int horiz_scroll_on;			/* scrolling flag */
+	enum MidButtonEmulation mid_emu_state;	/* emulated 3rd button */
+
+	atomic_t timer_active;
+	struct timer_list timer;		/* for up/down-button repeat */
+	int repeat_buttons;			/* buttons for repeat */
+	int last_up, last_down;			/* Previous value of up/down buttons */
+
+	int finger_count;			/* tap counter for fingers */
+
+	/* For palm detection */
+	int palm;				/* Set to true when palm detected, reset to false when
+						 * palm/finger contact disappears */
+	int prev_z;				/* previous z value, for palm detection */
+	int avg_w;				/* weighted average of previous w values */
+};
+
+#endif /* _SYNAPTICS_H */

-- 
Peter Osterlund - petero2@telia.com
http://w1.894.telia.com/~u89404340


             reply	other threads:[~2003-06-11 14:47 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-06-10 22:52 Joseph Fannin [this message]
2003-06-11 15:30 ` [PATCH] Synaptics TouchPad driver for 2.5.70 Joseph Fannin
2003-06-11 20:17 ` Andrew Morton
2003-06-11 20:29   ` Vojtech Pavlik
2003-06-11 22:12     ` Peter Osterlund
     [not found] <m2smqhqk4k.fsf@p4.localdomain>
2003-06-11 15:02 ` Vojtech Pavlik
2003-06-11 18:16   ` Peter Osterlund
2003-06-11 18:26     ` Vojtech Pavlik
2003-06-11 18:29     ` AlberT
2003-06-11 18:34     ` Vojtech Pavlik
2003-06-11 21:23       ` Peter Osterlund
2003-06-12  2:48         ` Joseph Fannin
2003-06-12  2:54           ` CaT
2003-06-12 18:58             ` Peter Osterlund
2003-06-12 22:01               ` Peter Berg Larsen
2003-06-12 22:57                 ` Vojtech Pavlik
2003-06-12 23:17                   ` Peter Berg Larsen
2003-06-12 23:27                     ` Vojtech Pavlik
2003-06-12 23:42                       ` Peter Berg Larsen
2003-06-13  7:44                         ` Vojtech Pavlik
2003-06-13  8:58                           ` Peter Berg Larsen
2003-06-13 20:25                           ` James Simmons
2003-06-13 20:38                             ` Vojtech Pavlik
2003-06-13 20:51                               ` James Simmons
2003-06-13 22:08                                 ` Vojtech Pavlik
2003-06-13 23:57                                   ` James Simmons
2003-06-14  8:55                                     ` Vojtech Pavlik
2003-06-16 21:28                                       ` James Simmons
2003-06-12 19:11           ` Peter Osterlund
2003-06-12  6:31         ` Vojtech Pavlik
2003-06-12  8:36         ` James H. Cloos Jr.
2003-06-13 21:15       ` Peter Osterlund
2003-06-13 21:49         ` James Simmons
2003-06-13 22:08         ` Vojtech Pavlik
2003-06-13 22:55           ` Peter Berg Larsen
2003-06-14  8:42             ` Vojtech Pavlik
2003-06-14 22:19 ` Vojtech Pavlik
2003-06-15 12:18   ` Peter Osterlund
2003-06-15 12:28     ` Vojtech Pavlik
2003-06-15 15:47       ` Peter Osterlund
2003-06-15 17:27         ` Vojtech Pavlik
2003-06-18 23:41           ` Peter Osterlund
2003-06-19  6:03             ` Vojtech Pavlik
2003-06-23 16:30             ` Andreas Jellinghaus
2003-06-23 19:04               ` Peter Osterlund
2003-06-26 20:01                 ` Vojtech Pavlik
2003-07-07 23:06                 ` Peter Osterlund
2003-07-12 10:51                   ` Andreas Jellinghaus

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=m2u1axk0kp.fsf@p4.localdomain \
    --to=jhf@rivenstone.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=vojtech@suse.cz \
    /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.