Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 26 Sep 2016 22:06:19 +0000 (UTC)
From:      Oleksandr Tymoshenko <gonzo@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r306355 - in head/sys: dev/atkbdc sys
Message-ID:  <201609262206.u8QM6J5q070986@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: gonzo
Date: Mon Sep 26 22:06:19 2016
New Revision: 306355
URL: https://svnweb.freebsd.org/changeset/base/306355

Log:
  Add Elantech trackpad support
  
  Elantech trackpads are found in some laptops like the Asus UX31E. They
  are "synaptics compatible" but use a slightly different protocol.
  
  Elantech hardware support is not enabled by default and just like
  Synaptic or TrackPoint devices it should be enabled by setting
  tunable, in this case hw.psm.elantech_support, to non-zero value
  
  PR:		205690
  Submitted by:	Vladimir Kondratyev <wulf@cicgroup.ru>
  MFC after:	1 week

Modified:
  head/sys/dev/atkbdc/psm.c
  head/sys/sys/mouse.h

Modified: head/sys/dev/atkbdc/psm.c
==============================================================================
--- head/sys/dev/atkbdc/psm.c	Mon Sep 26 20:26:19 2016	(r306354)
+++ head/sys/dev/atkbdc/psm.c	Mon Sep 26 22:06:19 2016	(r306355)
@@ -81,6 +81,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/sysctl.h>
 #include <sys/time.h>
 #include <sys/uio.h>
+#include <sys/libkern.h>
 
 #include <sys/limits.h>
 #include <sys/mouse.h>
@@ -161,40 +162,6 @@ typedef struct packetbuf {
 #define	PSM_PACKETQUEUE	128
 #endif
 
-enum {
-	SYNAPTICS_SYSCTL_MIN_PRESSURE,
-	SYNAPTICS_SYSCTL_MAX_PRESSURE,
-	SYNAPTICS_SYSCTL_MAX_WIDTH,
-	SYNAPTICS_SYSCTL_MARGIN_TOP,
-	SYNAPTICS_SYSCTL_MARGIN_RIGHT,
-	SYNAPTICS_SYSCTL_MARGIN_BOTTOM,
-	SYNAPTICS_SYSCTL_MARGIN_LEFT,
-	SYNAPTICS_SYSCTL_NA_TOP,
-	SYNAPTICS_SYSCTL_NA_RIGHT,
-	SYNAPTICS_SYSCTL_NA_BOTTOM,
-	SYNAPTICS_SYSCTL_NA_LEFT,
-	SYNAPTICS_SYSCTL_WINDOW_MIN,
-	SYNAPTICS_SYSCTL_WINDOW_MAX,
-	SYNAPTICS_SYSCTL_MULTIPLICATOR,
-	SYNAPTICS_SYSCTL_WEIGHT_CURRENT,
-	SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS,
-	SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS_NA,
-	SYNAPTICS_SYSCTL_WEIGHT_LEN_SQUARED,
-	SYNAPTICS_SYSCTL_DIV_MIN,
-	SYNAPTICS_SYSCTL_DIV_MAX,
-	SYNAPTICS_SYSCTL_DIV_MAX_NA,
-	SYNAPTICS_SYSCTL_DIV_LEN,
-	SYNAPTICS_SYSCTL_TAP_MAX_DELTA,
-	SYNAPTICS_SYSCTL_TAP_MIN_QUEUE,
-	SYNAPTICS_SYSCTL_TAPHOLD_TIMEOUT,
-	SYNAPTICS_SYSCTL_VSCROLL_HOR_AREA,
-	SYNAPTICS_SYSCTL_VSCROLL_VER_AREA,
-	SYNAPTICS_SYSCTL_VSCROLL_MIN_DELTA,
-	SYNAPTICS_SYSCTL_VSCROLL_DIV_MIN,
-	SYNAPTICS_SYSCTL_VSCROLL_DIV_MAX,
-	SYNAPTICS_SYSCTL_TOUCHPAD_OFF
-};
-
 typedef struct synapticsinfo {
 	struct sysctl_ctx_list	 sysctl_ctx;
 	struct sysctl_oid	*sysctl_tree;
@@ -231,6 +198,11 @@ typedef struct synapticsinfo {
 	int			 vscroll_div_min;
 	int			 vscroll_div_max;
 	int			 touchpad_off;
+	int			 softbuttons_y;
+	int			 softbutton2_x;
+	int			 softbutton3_x;
+	int			 max_x;
+	int			 max_y;
 } synapticsinfo_t;
 
 typedef struct synapticspacket {
@@ -246,22 +218,29 @@ typedef struct synapticspacket {
     ((synhw).infoMajor > (major) ||					\
      ((synhw).infoMajor == (major) && (synhw).infoMinor >= (minor)))
 
-typedef struct synapticsaction {
+typedef struct smoother {
 	synapticspacket_t	queue[SYNAPTICS_PACKETQUEUE];
 	int			queue_len;
 	int			queue_cursor;
-	int			window_min;
 	int			start_x;
 	int			start_y;
 	int			avg_dx;
 	int			avg_dy;
 	int			squelch_x;
 	int			squelch_y;
+	int			is_fuzzy;
+	int			active;
+} smoother_t;
+
+typedef struct gesture {
+	int			window_min;
 	int			fingers_nb;
 	int			tap_button;
 	int			in_taphold;
 	int			in_vscroll;
-} synapticsaction_t;
+	int			zmax;		/* maximum pressure value */
+	struct timeval		taptimeout;	/* tap timeout for touchpads */
+} gesture_t;
 
 enum {
 	TRACKPOINT_SYSCTL_SENSITIVITY,
@@ -295,6 +274,100 @@ typedef struct trackpointinfo {
 	int	skipback;
 } trackpointinfo_t;
 
+typedef struct finger {
+	int			x;
+	int			y;
+	int			p;
+	int			w;
+	int			flags;
+} finger_t;
+#define	PSM_FINGERS		2	/* # of processed fingers */
+#define	PSM_FINGER_IS_PEN	(1<<0)
+#define	PSM_FINGER_FUZZY	(1<<1)
+#define	PSM_FINGER_DEFAULT_P	tap_threshold
+#define	PSM_FINGER_DEFAULT_W	1
+#define	PSM_FINGER_IS_SET(f) ((f).x != -1 && (f).y != -1 && (f).p != 0)
+#define	PSM_FINGER_RESET(f) do { \
+	(f) = (finger_t) { .x = -1, .y = -1, .p = 0, .w = 0, .flags = 0 }; \
+} while (0)
+
+typedef struct elantechhw {
+	int			hwversion;
+	int			fwversion;
+	int			sizex;
+	int			sizey;
+	int			dpmmx;
+	int			dpmmy;
+	int			ntracesx;
+	int			ntracesy;
+	int			issemimt;
+	int			isclickpad;
+	int			hascrc;
+	int			hastrackpoint;
+	int			haspressure;
+} elantechhw_t;
+
+/* minimum versions supported by this driver */
+#define	ELANTECH_HW_IS_V1(fwver) ((fwver) < 0x020030 || (fwver) == 0x020600)
+
+#define	ELANTECH_MAGIC(magic)				\
+	((magic)[0] == 0x3c && (magic)[1] == 0x03 &&	\
+	((magic)[2] == 0xc8 || (magic)[2] == 0x00))
+
+#define	ELANTECH_FW_ID		0x00
+#define	ELANTECH_FW_VERSION	0x01
+#define	ELANTECH_CAPABILITIES	0x02
+#define	ELANTECH_SAMPLE		0x03
+#define	ELANTECH_RESOLUTION	0x04
+#define	ELANTECH_REG_READ	0x10
+#define	ELANTECH_REG_WRITE	0x11
+#define	ELANTECH_REG_RDWR	0x00
+#define	ELANTECH_CUSTOM_CMD	0xf8
+
+#define	ELANTECH_MAX_FINGERS	PSM_FINGERS
+
+#define	ELANTECH_FINGER_SET_XYP(pb) (finger_t) {			\
+    .x = (((pb)->ipacket[1] & 0x0f) << 8) | (pb)->ipacket[2],		\
+    .y = (((pb)->ipacket[4] & 0x0f) << 8) | (pb)->ipacket[5],		\
+    .p = ((pb)->ipacket[1] & 0xf0) | (((pb)->ipacket[4] >> 4) & 0x0f),	\
+    .w = PSM_FINGER_DEFAULT_W,						\
+    .flags = 0								\
+}
+
+enum {
+	ELANTECH_PKT_NOP,
+	ELANTECH_PKT_TRACKPOINT,
+	ELANTECH_PKT_V2_COMMON,
+	ELANTECH_PKT_V2_2FINGER,
+	ELANTECH_PKT_V3,
+	ELANTECH_PKT_V4_STATUS,
+	ELANTECH_PKT_V4_HEAD,
+	ELANTECH_PKT_V4_MOTION
+};
+
+#define	ELANTECH_PKT_IS_TRACKPOINT(pb) (((pb)->ipacket[3] & 0x0f) == 0x06)
+#define	ELANTECH_PKT_IS_DEBOUNCE(pb, hwversion) ((hwversion) == 4 ? 0 :	\
+    (pb)->ipacket[0] == ((hwversion) == 2 ? 0x84 : 0xc4) &&		\
+    (pb)->ipacket[1] == 0xff && (pb)->ipacket[2] == 0xff &&		\
+    (pb)->ipacket[3] == 0x02 && (pb)->ipacket[4] == 0xff &&		\
+    (pb)->ipacket[5] == 0xff)
+#define	ELANTECH_PKT_IS_V2(pb) 						\
+    (((pb)->ipacket[0] & 0x0c) == 0x04 && ((pb)->ipacket[3] & 0x0f) == 0x02)
+#define	ELANTECH_PKT_IS_V3_HEAD(pb, hascrc) ((hascrc) ? 		\
+    ((pb)->ipacket[3] & 0x09) == 0x08 : 				\
+    ((pb)->ipacket[0] & 0x0c) == 0x04 && ((pb)->ipacket[3] & 0xcf) == 0x02)
+#define	ELANTECH_PKT_IS_V3_TAIL(pb, hascrc) ((hascrc) ? 		\
+    ((pb)->ipacket[3] & 0x09) == 0x09 : 				\
+    ((pb)->ipacket[0] & 0x0c) == 0x0c && ((pb)->ipacket[3] & 0xce) == 0x0c)
+#define	ELANTECH_PKT_IS_V4(pb, hascrc) ((hascrc) ? 			\
+    ((pb)->ipacket[3] & 0x08) == 0x00 :					\
+    ((pb)->ipacket[0] & 0x0c) == 0x04 && ((pb)->ipacket[3] & 0x1c) == 0x10)
+
+typedef struct elantechaction {
+	finger_t		fingers[ELANTECH_MAX_FINGERS];
+	int			mask;
+} elantechaction_t;
+
 /* driver control block */
 struct psm_softc {		/* Driver status information */
 	int		unit;
@@ -308,7 +381,10 @@ struct psm_softc {		/* Driver status inf
 	mousehw_t	hw;		/* hardware information */
 	synapticshw_t	synhw;		/* Synaptics hardware information */
 	synapticsinfo_t	syninfo;	/* Synaptics configuration */
-	synapticsaction_t synaction;	/* Synaptics action context */
+	smoother_t	smoother[PSM_FINGERS];	/* Motion smoothing */
+	gesture_t	gesture;	/* Gesture context */
+	elantechhw_t	elanhw;		/* Elantech hardware information */
+	elantechaction_t elanaction;	/* Elantech action context */
 	int		tphw;		/* TrackPoint hardware information */
 	trackpointinfo_t tpinfo;	/* TrackPoint configuration */
 	mousemode_t	mode;		/* operation mode */
@@ -324,13 +400,13 @@ struct psm_softc {		/* Driver status inf
 	int		xaverage;	/* average X position */
 	int		yaverage;	/* average Y position */
 	int		squelch; /* level to filter movement at low speed */
-	int		zmax;	/* maximum pressure value for touchpads */
 	int		syncerrors; /* # of bytes discarded to synchronize */
 	int		pkterrors;  /* # of packets failed during quaranteen. */
 	struct timeval	inputtimeout;
 	struct timeval	lastsoftintr;	/* time of last soft interrupt */
 	struct timeval	lastinputerr;	/* time last sync error happened */
-	struct timeval	taptimeout;	/* tap timeout for touchpads */
+	struct timeval	idletimeout;
+	packetbuf_t	idlepacket;	/* packet to send after idle timeout */
 	int		watchdog;	/* watchdog timer flag */
 	struct callout	callout;	/* watchdog timer call out */
 	struct callout	softcallout; /* buffer timer call out */
@@ -389,6 +465,9 @@ TUNABLE_INT("hw.psm.synaptics_support", 
 static int trackpoint_support = 0;
 TUNABLE_INT("hw.psm.trackpoint_support", &trackpoint_support);
 
+static int elantech_support = 0;
+TUNABLE_INT("hw.psm.elantech_support", &elantech_support);
+
 static int verbose = PSM_DEBUG;
 TUNABLE_INT("debug.psm.loglevel", &verbose);
 
@@ -411,6 +490,44 @@ typedef struct old_mousemode {
 	int	accelfactor;
 } old_mousemode_t;
 
+#define SYN_OFFSET(field) offsetof(struct psm_softc, syninfo.field)
+enum {
+	SYNAPTICS_SYSCTL_MIN_PRESSURE =		SYN_OFFSET(min_pressure),
+	SYNAPTICS_SYSCTL_MAX_PRESSURE =		SYN_OFFSET(max_pressure),
+	SYNAPTICS_SYSCTL_MAX_WIDTH =		SYN_OFFSET(max_width),
+	SYNAPTICS_SYSCTL_MARGIN_TOP =		SYN_OFFSET(margin_top),
+	SYNAPTICS_SYSCTL_MARGIN_RIGHT =		SYN_OFFSET(margin_right),
+	SYNAPTICS_SYSCTL_MARGIN_BOTTOM =	SYN_OFFSET(margin_bottom),
+	SYNAPTICS_SYSCTL_MARGIN_LEFT =		SYN_OFFSET(margin_left),
+	SYNAPTICS_SYSCTL_NA_TOP =		SYN_OFFSET(na_top),
+	SYNAPTICS_SYSCTL_NA_RIGHT =		SYN_OFFSET(na_right),
+	SYNAPTICS_SYSCTL_NA_BOTTOM =		SYN_OFFSET(na_bottom),
+	SYNAPTICS_SYSCTL_NA_LEFT = 		SYN_OFFSET(na_left),
+	SYNAPTICS_SYSCTL_WINDOW_MIN =		SYN_OFFSET(window_min),
+	SYNAPTICS_SYSCTL_WINDOW_MAX =		SYN_OFFSET(window_max),
+	SYNAPTICS_SYSCTL_MULTIPLICATOR =	SYN_OFFSET(multiplicator),
+	SYNAPTICS_SYSCTL_WEIGHT_CURRENT =	SYN_OFFSET(weight_current),
+	SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS =	SYN_OFFSET(weight_previous),
+	SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS_NA =	SYN_OFFSET(weight_previous_na),
+	SYNAPTICS_SYSCTL_WEIGHT_LEN_SQUARED =	SYN_OFFSET(weight_len_squared),
+	SYNAPTICS_SYSCTL_DIV_MIN =		SYN_OFFSET(div_min),
+	SYNAPTICS_SYSCTL_DIV_MAX =		SYN_OFFSET(div_max),
+	SYNAPTICS_SYSCTL_DIV_MAX_NA =		SYN_OFFSET(div_max_na),
+	SYNAPTICS_SYSCTL_DIV_LEN =		SYN_OFFSET(div_len),
+	SYNAPTICS_SYSCTL_TAP_MAX_DELTA =	SYN_OFFSET(tap_max_delta),
+	SYNAPTICS_SYSCTL_TAP_MIN_QUEUE =	SYN_OFFSET(tap_min_queue),
+	SYNAPTICS_SYSCTL_TAPHOLD_TIMEOUT =	SYN_OFFSET(taphold_timeout),
+	SYNAPTICS_SYSCTL_VSCROLL_HOR_AREA =	SYN_OFFSET(vscroll_hor_area),
+	SYNAPTICS_SYSCTL_VSCROLL_VER_AREA =	SYN_OFFSET(vscroll_ver_area),
+	SYNAPTICS_SYSCTL_VSCROLL_MIN_DELTA =	SYN_OFFSET(vscroll_min_delta),
+	SYNAPTICS_SYSCTL_VSCROLL_DIV_MIN =	SYN_OFFSET(vscroll_div_min),
+	SYNAPTICS_SYSCTL_VSCROLL_DIV_MAX =	SYN_OFFSET(vscroll_div_max),
+	SYNAPTICS_SYSCTL_TOUCHPAD_OFF =		SYN_OFFSET(touchpad_off),
+	SYNAPTICS_SYSCTL_SOFTBUTTONS_Y =	SYN_OFFSET(softbuttons_y),
+	SYNAPTICS_SYSCTL_SOFTBUTTON2_X =	SYN_OFFSET(softbutton2_x),
+	SYNAPTICS_SYSCTL_SOFTBUTTON3_X =	SYN_OFFSET(softbutton3_x),
+};
+
 /* packet formatting function */
 typedef int	packetfunc_t(struct psm_softc *, u_char *, int *, int,
     mousestatus_t *);
@@ -446,6 +563,7 @@ static int	doopen(struct psm_softc *, in
 static int	reinitialize(struct psm_softc *, int);
 static char	*model_name(int);
 static void	psmsoftintr(void *);
+static void	psmsoftintridle(void *);
 static void	psmintr(void *);
 static void	psmtimeout(void *);
 static int	timeelapsed(const struct timeval *, int, int,
@@ -458,6 +576,13 @@ static int	proc_synaptics(struct psm_sof
 		    mousestatus_t *, int *, int *, int *);
 static void	proc_versapad(struct psm_softc *, packetbuf_t *,
 		    mousestatus_t *, int *, int *, int *);
+static int	proc_elantech(struct psm_softc *, packetbuf_t *,
+		    mousestatus_t *, int *, int *, int *);
+static int	psmpalmdetect(struct psm_softc *, finger_t *, int);
+static void	psmgestures(struct psm_softc *, finger_t *, int,
+		    mousestatus_t *);
+static void	psmsmoother(struct psm_softc *, finger_t *, int,
+		    mousestatus_t *, int *, int *);
 static int	tame_mouse(struct psm_softc *, packetbuf_t *, mousestatus_t *,
 		    u_char *);
 
@@ -480,6 +605,7 @@ static probefunc_t	enable_mmanplus;
 static probefunc_t	enable_synaptics;
 static probefunc_t	enable_trackpoint;
 static probefunc_t	enable_versapad;
+static probefunc_t	enable_elantech;
 
 static void set_trackpoint_parameters(struct psm_softc *sc);
 static void synaptics_passthrough_on(struct psm_softc *sc);
@@ -511,6 +637,8 @@ static struct {
 	  0xc8, MOUSE_4DPLUS_PACKETSIZE, enable_4dplus },
 	{ MOUSE_MODEL_SYNAPTICS,	/* Synaptics Touchpad */
 	  0xc0, MOUSE_SYNAPTICS_PACKETSIZE, enable_synaptics },
+	{ MOUSE_MODEL_ELANTECH,		/* Elantech Touchpad */
+	  0x04, MOUSE_ELANTECH_PACKETSIZE, enable_elantech },
 	{ MOUSE_MODEL_INTELLI,		/* Microsoft IntelliMouse */
 	  0x08, MOUSE_PS2INTELLI_PACKETSIZE, enable_msintelli },
 	{ MOUSE_MODEL_GLIDEPOINT,	/* ALPS GlidePoint */
@@ -762,6 +890,7 @@ model_name(int model)
 		{ MOUSE_MODEL_4DPLUS,		"4D+ Mouse" },
 		{ MOUSE_MODEL_SYNAPTICS,	"Synaptics Touchpad" },
 		{ MOUSE_MODEL_TRACKPOINT,	"IBM/Lenovo TrackPoint" },
+		{ MOUSE_MODEL_ELANTECH,		"Elantech Touchpad" },
 		{ MOUSE_MODEL_GENERIC,		"Generic PS/2 mouse" },
 		{ MOUSE_MODEL_UNKNOWN,		"Unknown" },
 	};
@@ -1504,6 +1633,7 @@ psmattach(device_t dev)
 	case MOUSE_MODEL_SYNAPTICS:
 	case MOUSE_MODEL_GLIDEPOINT:
 	case MOUSE_MODEL_VERSAPAD:
+	case MOUSE_MODEL_ELANTECH:
 		sc->config |= PSM_CONFIG_INITAFTERSUSPEND;
 		break;
 	default:
@@ -1512,6 +1642,11 @@ psmattach(device_t dev)
 		break;
 	}
 
+	/* Elantech trackpad`s sync bit differs from touchpad`s one */
+	if (sc->hw.model == MOUSE_MODEL_ELANTECH &&
+	    (sc->elanhw.hascrc || sc->elanhw.hastrackpoint))
+		sc->config |= PSM_CONFIG_NOCHECKSYNC;
+
 	if (!verbose)
 		printf("psm%d: model %s, device ID %d\n",
 		    unit, model_name(sc->hw.model), sc->hw.hwid & 0x00ff);
@@ -2165,20 +2300,6 @@ psmioctl(struct cdev *dev, u_long cmd, c
 		    (*(int *)addr > PSM_LEVEL_MAX))
 			return (EINVAL);
 		sc->mode.level = *(int *)addr;
-
-		if (sc->hw.model == MOUSE_MODEL_SYNAPTICS) {
-			/*
-			 * If we are entering PSM_LEVEL_NATIVE, we want to
-			 * enable sending of "extended W mode" packets to
-			 * userland. Reset the mode of the touchpad so that the
-			 * change in the level is picked up.
-			 */
-			error = block_mouse_data(sc, &command_byte);
-			if (error)
-				return (error);
-			synaptics_set_mode(sc, synaptics_preferred_mode(sc));
-			unblock_mouse_data(sc, command_byte);
-		}
 		break;
 
 	case MOUSE_GETSTATUS:
@@ -2627,7 +2748,8 @@ proc_synaptics(struct psm_softc *sc, pac
 {
 	static int touchpad_buttons;
 	static int guest_buttons;
-	int w, x0, y0;
+	static finger_t f[PSM_FINGERS];
+	int w, id, nfingers, ewcode;
 
 	/* TouchPad PS/2 absolute mode message format with capFourButtons:
 	 *
@@ -2683,6 +2805,7 @@ proc_synaptics(struct psm_softc *sc, pac
 		return (-1);
 
 	*x = *y = 0;
+	ms->button = ms->obutton;
 
 	/*
 	 * Pressure value.
@@ -2718,34 +2841,79 @@ proc_synaptics(struct psm_softc *sc, pac
 		w = 4;
 	}
 
-	/*
-	 * Handle packets from the guest device. See:
-	 * Synaptics PS/2 TouchPad Interfacing Guide, Section 5.1
-	 */
-	if (w == 3 && sc->synhw.capPassthrough) {
-		*x = ((pb->ipacket[1] & 0x10) ?
-		    pb->ipacket[4] - 256 : pb->ipacket[4]);
-		*y = ((pb->ipacket[1] & 0x20) ?
-		    pb->ipacket[5] - 256 : pb->ipacket[5]);
-		*z = 0;
+	switch (w) {
+	case 3:
+		/*
+		 * Handle packets from the guest device. See:
+		 * Synaptics PS/2 TouchPad Interfacing Guide, Section 5.1
+		 */
+		if (sc->synhw.capPassthrough) {
+			*x = ((pb->ipacket[1] & 0x10) ?
+			    pb->ipacket[4] - 256 : pb->ipacket[4]);
+			*y = ((pb->ipacket[1] & 0x20) ?
+			    pb->ipacket[5] - 256 : pb->ipacket[5]);
+			*z = 0;
+
+			guest_buttons = 0;
+			if (pb->ipacket[1] & 0x01)
+				guest_buttons |= MOUSE_BUTTON1DOWN;
+			if (pb->ipacket[1] & 0x04)
+				guest_buttons |= MOUSE_BUTTON2DOWN;
+				if (pb->ipacket[1] & 0x02)
+				guest_buttons |= MOUSE_BUTTON3DOWN;
 
-		guest_buttons = 0;
-		if (pb->ipacket[1] & 0x01)
-			guest_buttons |= MOUSE_BUTTON1DOWN;
-		if (pb->ipacket[1] & 0x04)
-			guest_buttons |= MOUSE_BUTTON2DOWN;
-		if (pb->ipacket[1] & 0x02)
-			guest_buttons |= MOUSE_BUTTON3DOWN;
+			ms->button = touchpad_buttons | guest_buttons;
+		}
+		goto SYNAPTICS_END;
 
-		ms->button = touchpad_buttons | guest_buttons;
+	case 2:
+		/* Handle Extended W mode packets */
+		ewcode = (pb->ipacket[5] & 0xf0) >> 4;
+#if PSM_FINGERS > 1
+		switch (ewcode) {
+		case 1:
+			/* Secondary finger */
+			if (sc->synhw.capAdvancedGestures)
+				f[1] = (finger_t) {
+					.x = (((pb->ipacket[4] & 0x0f) << 8) |
+					    pb->ipacket[1]) << 1,
+					.y = (((pb->ipacket[4] & 0xf0) << 4) |
+					    pb->ipacket[2]) << 1,
+					.p = ((pb->ipacket[3] & 0x30) |
+					    (pb->ipacket[5] & 0x0f)) << 1,
+					.w = PSM_FINGER_DEFAULT_W,
+					.flags = PSM_FINGER_FUZZY,
+				};
+			else if (sc->synhw.capReportsV)
+				f[1] = (finger_t) {
+					.x = (((pb->ipacket[4] & 0x0f) << 8) |
+					    (pb->ipacket[1] & 0xfe)) << 1,
+					.y = (((pb->ipacket[4] & 0xf0) << 4) |
+					    (pb->ipacket[2] & 0xfe)) << 1,
+					.p = ((pb->ipacket[3] & 0x30) |
+					    (pb->ipacket[5] & 0x0e)) << 1,
+					.w = (((pb->ipacket[5] & 0x01) << 2) |
+					    ((pb->ipacket[2] & 0x01) << 1) |
+					    (pb->ipacket[1] & 0x01)) + 8,
+					.flags = PSM_FINGER_FUZZY,
+				};
+		default:
+			break;
+		}
+#endif
 		goto SYNAPTICS_END;
+
+	case 1:
+	case 0:
+		nfingers = w + 2;
+		break;
+
+	default:
+		nfingers = 1;
 	}
 
-	if (sc->syninfo.touchpad_off) {
-		*x = *y = *z = 0;
-		ms->button = ms->obutton;
+	if (sc->syninfo.touchpad_off)
 		goto SYNAPTICS_END;
-	}
 
 	/* Button presses */
 	touchpad_buttons = 0;
@@ -2764,6 +2932,10 @@ proc_synaptics(struct psm_softc *sc, pac
 		/* Middle Button */
 		if ((pb->ipacket[0] ^ pb->ipacket[3]) & 0x01)
 			touchpad_buttons |= MOUSE_BUTTON2DOWN;
+	} else if (sc->synhw.capExtended && sc->synhw.capClickPad) {
+		/* ClickPad Button */
+		if ((pb->ipacket[0] ^ pb->ipacket[3]) & 0x01)
+			touchpad_buttons = MOUSE_BUTTON1DOWN;
 	} else if (sc->synhw.capExtended && (sc->synhw.nExtendedButtons > 0)) {
 		/* Extended Buttons */
 		if ((pb->ipacket[0] ^ pb->ipacket[3]) & 0x02) {
@@ -2806,7 +2978,7 @@ proc_synaptics(struct psm_softc *sc, pac
 			pb->ipacket[4] &= ~(mask);
 			pb->ipacket[5] &= ~(mask);
 		} else	if (!sc->syninfo.directional_scrolls &&
-		    !sc->synaction.in_vscroll) {
+		    !sc->gesture.in_vscroll) {
 			/*
 			 * Keep reporting MOUSE DOWN until we get a new packet
 			 * indicating otherwise.
@@ -2814,93 +2986,179 @@ proc_synaptics(struct psm_softc *sc, pac
 			touchpad_buttons |= sc->extended_buttons;
 		}
 	}
-	/* Handle ClickPad. */
-	if (sc->synhw.capClickPad &&
-	    ((pb->ipacket[0] ^ pb->ipacket[3]) & 0x01))
-		touchpad_buttons |= MOUSE_BUTTON1DOWN;
+
+	if (sc->synhw.capReportsV && nfingers > 1)
+		f[0] = (finger_t) {
+			.x = ((pb->ipacket[3] & 0x10) << 8) |
+			    ((pb->ipacket[1] & 0x0f) << 8) |
+			    (pb->ipacket[4] & 0xfd),
+			.y = ((pb->ipacket[3] & 0x20) << 7) |
+			    ((pb->ipacket[1] & 0xf0) << 4) |
+			    (pb->ipacket[5] & 0xfd),
+			.p = *z & 0xfe,
+			.w = (((pb->ipacket[2] & 0x01) << 2) |
+			    (pb->ipacket[5] & 0x02) |
+			    ((pb->ipacket[4] & 0x02) >> 1)) + 8,
+			.flags = PSM_FINGER_FUZZY,
+		};
+	else
+		f[0] = (finger_t) {
+			.x = ((pb->ipacket[3] & 0x10) << 8) |
+			    ((pb->ipacket[1] & 0x0f) << 8) |
+			    pb->ipacket[4],
+			.y = ((pb->ipacket[3] & 0x20) << 7) |
+			    ((pb->ipacket[1] & 0xf0) << 4) |
+			    pb->ipacket[5],
+			.p = *z,
+			.w = w,
+			.flags = nfingers > 1 ? PSM_FINGER_FUZZY : 0,
+		};
+
+	/* Ignore hovering and unmeasurable touches */
+	if (f[0].p < sc->syninfo.min_pressure || f[0].x < 2)
+		nfingers = 0;
+
+	for (id = 0; id < PSM_FINGERS; id++)
+		if (id >= nfingers)
+			PSM_FINGER_RESET(f[id]);
 
 	ms->button = touchpad_buttons | guest_buttons;
 
+	/* Palm detection doesn't terminate the current action. */
+	if (!psmpalmdetect(sc, &f[0], nfingers)) {
+		psmgestures(sc, &f[0], nfingers, ms);
+		for (id = 0; id < PSM_FINGERS; id++)
+			psmsmoother(sc, &f[id], id, ms, x, y);
+	} else {
+		VLOG(2, (LOG_DEBUG, "synaptics: palm detected! (%d)\n", f[0].w));
+	}
+
+SYNAPTICS_END:
+	/*
+	 * Use the extra buttons as a scrollwheel
+	 *
+	 * XXX X.Org uses the Z axis for vertical wheel only,
+	 * whereas moused(8) understands special values to differ
+	 * vertical and horizontal wheels.
+	 *
+	 * xf86-input-mouse needs therefore a small patch to
+	 * understand these special values. Without it, the
+	 * horizontal wheel acts as a vertical wheel in X.Org.
+	 *
+	 * That's why the horizontal wheel is disabled by
+	 * default for now.
+	 */
+	if (ms->button & MOUSE_BUTTON4DOWN)
+		*z = -1;
+	else if (ms->button & MOUSE_BUTTON5DOWN)
+		*z = 1;
+	else if (ms->button & MOUSE_BUTTON6DOWN)
+		*z = -2;
+	else if (ms->button & MOUSE_BUTTON7DOWN)
+		*z = 2;
+	else
+		*z = 0;
+	ms->button &= ~(MOUSE_BUTTON4DOWN | MOUSE_BUTTON5DOWN |
+	    MOUSE_BUTTON6DOWN | MOUSE_BUTTON7DOWN);
+
+	return (0);
+}
+
+static int
+psmpalmdetect(struct psm_softc *sc, finger_t *f, int nfingers)
+{
+	if (!(
+	    ((sc->synhw.capMultiFinger ||
+	      sc->synhw.capAdvancedGestures) && nfingers > 1) ||
+	    (sc->synhw.capPalmDetect && f->w <= sc->syninfo.max_width) ||
+	    (!sc->synhw.capPalmDetect && f->p <= sc->syninfo.max_pressure) ||
+	    (sc->synhw.capPen && f->flags & PSM_FINGER_IS_PEN))) {
+		/*
+		 * We consider the packet irrelevant for the current
+		 * action when:
+		 *  - the width isn't comprised in:
+		 *    [1; max_width]
+		 *  - the pressure isn't comprised in:
+		 *    [min_pressure; max_pressure]
+		 *  - pen aren't supported but PSM_FINGER_IS_PEN is set
+		 */
+		return (1);
+	}
+	return (0);
+}
+
+static void
+psmgestures(struct psm_softc *sc, finger_t *fingers, int nfingers,
+    mousestatus_t *ms)
+{
+	smoother_t *smoother;
+	gesture_t *gest;
+	finger_t *f;
+	int y_ok, center_button, center_x, right_button, right_x, i;
+
+	f = &fingers[0];
+	smoother = &sc->smoother[0];
+	gest = &sc->gesture;
+
+	/* Find first active finger. */
+	if (nfingers > 0) {
+		for (i = 0; i < PSM_FINGERS; i++) {
+			if (PSM_FINGER_IS_SET(fingers[i])) {
+				f = &fingers[i];
+				smoother = &sc->smoother[i];
+				break;
+			}
+		}
+	}
+
 	/*
 	 * Check pressure to detect a real wanted action on the
 	 * touchpad.
 	 */
-	if (*z >= sc->syninfo.min_pressure) {
-		synapticsaction_t *synaction;
-		int cursor, peer, window;
-		int dx, dy, dxp, dyp;
-		int max_width, max_pressure;
+	if (f->p >= sc->syninfo.min_pressure) {
+		int x0, y0;
+		int dxp, dyp;
+		int start_x, start_y;
+		int queue_len;
 		int margin_top, margin_right, margin_bottom, margin_left;
-		int na_top, na_right, na_bottom, na_left;
 		int window_min, window_max;
-		int multiplicator;
-		int weight_current, weight_previous, weight_len_squared;
-		int div_min, div_max, div_len;
 		int vscroll_hor_area, vscroll_ver_area;
 		int two_finger_scroll;
-		int len, weight_prev_x, weight_prev_y;
-		int div_max_x, div_max_y, div_x, div_y;
-		int exiting_scroll;
+		int max_x, max_y;
 
 		/* Read sysctl. */
 		/* XXX Verify values? */
-		max_width = sc->syninfo.max_width;
-		max_pressure = sc->syninfo.max_pressure;
 		margin_top = sc->syninfo.margin_top;
 		margin_right = sc->syninfo.margin_right;
 		margin_bottom = sc->syninfo.margin_bottom;
 		margin_left = sc->syninfo.margin_left;
-		na_top = sc->syninfo.na_top;
-		na_right = sc->syninfo.na_right;
-		na_bottom = sc->syninfo.na_bottom;
-		na_left = sc->syninfo.na_left;
 		window_min = sc->syninfo.window_min;
 		window_max = sc->syninfo.window_max;
-		multiplicator = sc->syninfo.multiplicator;
-		weight_current = sc->syninfo.weight_current;
-		weight_previous = sc->syninfo.weight_previous;
-		weight_len_squared = sc->syninfo.weight_len_squared;
-		div_min = sc->syninfo.div_min;
-		div_max = sc->syninfo.div_max;
-		div_len = sc->syninfo.div_len;
 		vscroll_hor_area = sc->syninfo.vscroll_hor_area;
 		vscroll_ver_area = sc->syninfo.vscroll_ver_area;
 		two_finger_scroll = sc->syninfo.two_finger_scroll;
-
-		exiting_scroll = 0;
-
-		/* Palm detection. */
-		if (!(
-		    ((sc->synhw.capMultiFinger ||
-		      sc->synhw.capAdvancedGestures) && (w == 0 || w == 1)) ||
-		    (sc->synhw.capPalmDetect && w >= 4 && w <= max_width) ||
-		    (!sc->synhw.capPalmDetect && *z <= max_pressure) ||
-		    (sc->synhw.capPen && w == 2))) {
-			/*
-			 * We consider the packet irrelevant for the current
-			 * action when:
-			 *  - the width isn't comprised in:
-			 *    [4; max_width]
-			 *  - the pressure isn't comprised in:
-			 *    [min_pressure; max_pressure]
-			 *  - pen aren't supported but w is 2
-			 *
-			 *  Note that this doesn't terminate the current action.
-			 */
-			VLOG(2, (LOG_DEBUG,
-			    "synaptics: palm detected! (%d)\n", w));
-			goto SYNAPTICS_END;
-		}
+		max_x = sc->syninfo.max_x;
+		max_y = sc->syninfo.max_y;
 
 		/* Read current absolute position. */
-		x0 = ((pb->ipacket[3] & 0x10) << 8) |
-		    ((pb->ipacket[1] & 0x0f) << 8) |
-		    pb->ipacket[4];
-		y0 = ((pb->ipacket[3] & 0x20) << 7) |
-		    ((pb->ipacket[1] & 0xf0) << 4) |
-		    pb->ipacket[5];
+		x0 = f->x;
+		y0 = f->y;
+
+		/*
+		 * Limit the coordinates to the specified margins because
+		 * this area isn't very reliable.
+		 */
+		if (x0 <= margin_left)
+			x0 = margin_left;
+		else if (x0 >= max_x - margin_right)
+			x0 = max_x - margin_right;
+		if (y0 <= margin_bottom)
+			y0 = margin_bottom;
+		else if (y0 >= max_y - margin_top)
+			y0 = max_y - margin_top;
 
-		synaction = &(sc->synaction);
+		VLOG(3, (LOG_DEBUG, "synaptics: ipacket: [%d, %d], %d, %d\n",
+		    x0, y0, f->p, f->w));
 
 		/*
 		 * If the action is just beginning, init the structure and
@@ -2909,107 +3167,82 @@ proc_synaptics(struct psm_softc *sc, pac
 		if (!(sc->flags & PSM_FLAGS_FINGERDOWN)) {
 			VLOG(3, (LOG_DEBUG, "synaptics: ----\n"));
 
-			/* Store the first point of this action. */
-			synaction->start_x = x0;
-			synaction->start_y = y0;
-			dx = dy = 0;
-
 			/* Initialize queue. */
-			synaction->queue_cursor = SYNAPTICS_PACKETQUEUE;
-			synaction->queue_len = 0;
-			synaction->window_min = window_min;
-
-			/* Reset average. */
-			synaction->avg_dx = 0;
-			synaction->avg_dy = 0;
-
-			/* Reset squelch. */
-			synaction->squelch_x = 0;
-			synaction->squelch_y = 0;
+			gest->window_min = window_min;
 
 			/* Reset pressure peak. */
-			sc->zmax = 0;
+			gest->zmax = 0;
 
 			/* Reset fingers count. */
-			synaction->fingers_nb = 0;
+			gest->fingers_nb = 0;
 
 			/* Reset virtual scrolling state. */
-			synaction->in_vscroll = 0;
+			gest->in_vscroll = 0;
 
 			/* Compute tap timeout. */
-			sc->taptimeout.tv_sec  = tap_timeout / 1000000;
-			sc->taptimeout.tv_usec = tap_timeout % 1000000;
-			timevaladd(&sc->taptimeout, &sc->lastsoftintr);
+			gest->taptimeout.tv_sec  = tap_timeout / 1000000;
+			gest->taptimeout.tv_usec = tap_timeout % 1000000;
+			timevaladd(&gest->taptimeout, &sc->lastsoftintr);
 
 			sc->flags |= PSM_FLAGS_FINGERDOWN;
+
+			/* Smoother has not been reset yet */
+			queue_len = 1;
+			start_x = x0;
+			start_y = y0;
 		} else {
-			/* Calculate the current delta. */
-			cursor = synaction->queue_cursor;
-			dx = x0 - synaction->queue[cursor].x;
-			dy = y0 - synaction->queue[cursor].y;
+			queue_len = smoother->queue_len + 1;
+			start_x = smoother->start_x;
+			start_y = smoother->start_y;
 		}
 
-		/* If in tap-hold, add the recorded button. */
-		if (synaction->in_taphold)
-			ms->button |= synaction->tap_button;
-
-		/*
-		 * From now on, we can use the SYNAPTICS_END label to skip
-		 * the current packet.
-		 */
-
-		/*
-		 * Limit the coordinates to the specified margins because
-		 * this area isn't very reliable.
-		 */
-		if (x0 <= margin_left)
-			x0 = margin_left;
-		else if (x0 >= 6143 - margin_right)
-			x0 = 6143 - margin_right;
-		if (y0 <= margin_bottom)
-			y0 = margin_bottom;
-		else if (y0 >= 6143 - margin_top)
-			y0 = 6143 - margin_top;
+		/* Process ClickPad softbuttons */
+		if (sc->synhw.capClickPad && ms->button & MOUSE_BUTTON1DOWN) {
+			y_ok = sc->syninfo.softbuttons_y >= 0 ?
+			    start_y < sc->syninfo.softbuttons_y :
+			    start_y > max_y - sc->syninfo.softbuttons_y;
+
+			center_button = MOUSE_BUTTON2DOWN;
+			center_x = sc->syninfo.softbutton2_x;
+			right_button = MOUSE_BUTTON3DOWN;
+			right_x = sc->syninfo.softbutton3_x;
+
+			if (center_x > 0 && right_x > 0 && center_x > right_x) {
+				center_button = MOUSE_BUTTON3DOWN;
+				center_x = sc->syninfo.softbutton3_x;
+				right_button = MOUSE_BUTTON2DOWN;
+				right_x = sc->syninfo.softbutton2_x;
+			}
 
-		VLOG(3, (LOG_DEBUG, "synaptics: ipacket: [%d, %d], %d, %d\n",
-		    x0, y0, *z, w));
+			if (right_x > 0 && start_x > right_x && y_ok)
+				ms->button = (ms->button &
+				    ~MOUSE_BUTTON1DOWN) | right_button;
+			else if (center_x > 0 && start_x > center_x && y_ok)
+				ms->button = (ms->button &
+				    ~MOUSE_BUTTON1DOWN) | center_button;
+		}
 
-		/* Queue this new packet. */
-		cursor = SYNAPTICS_QUEUE_CURSOR(synaction->queue_cursor - 1);
-		synaction->queue[cursor].x = x0;
-		synaction->queue[cursor].y = y0;
-		synaction->queue_cursor = cursor;
-		if (synaction->queue_len < SYNAPTICS_PACKETQUEUE)
-			synaction->queue_len++;
-		VLOG(5, (LOG_DEBUG,
-		    "synaptics: cursor[%d]: x=%d, y=%d, dx=%d, dy=%d\n",
-		    cursor, x0, y0, dx, dy));
+		/* If in tap-hold, add the recorded button. */
+		if (gest->in_taphold)
+			ms->button |= gest->tap_button;
 
 		/*
 		 * For tap, we keep the maximum number of fingers and the
 		 * pressure peak. Also with multiple fingers, we increase
 		 * the minimum window.
 		 */
-		switch (w) {
-		case 1: /* Three or more fingers. */
-			synaction->fingers_nb = imax(3, synaction->fingers_nb);
-			synaction->window_min = window_max;
-			break;
-		case 0: /* Two fingers. */
-			synaction->fingers_nb = imax(2, synaction->fingers_nb);
-			synaction->window_min = window_max;
-			break;
-		default: /* One finger or undetectable. */
-			synaction->fingers_nb = imax(1, synaction->fingers_nb);
-		}
-		sc->zmax = imax(*z, sc->zmax);
-
-		/* Do we have enough packets to consider this a movement? */
-		if (synaction->queue_len < synaction->window_min)
-			goto SYNAPTICS_END;
+		if (nfingers > 1)
+			gest->window_min = window_max;
+		gest->fingers_nb = imax(nfingers, gest->fingers_nb);
+		gest->zmax = imax(f->p, gest->zmax);
+
+		/* Do we have enough packets to consider this a gesture? */
+		if (queue_len < gest->window_min)
+			return;
 
 		/* Is a scrolling action occurring? */
-		if (!synaction->in_taphold && !synaction->in_vscroll) {
+		if (!gest->in_taphold && !ms->button &&
+		    (!gest->in_vscroll || two_finger_scroll)) {
 			/*
 			 * A scrolling action must not conflict with a tap
 			 * action. Here are the conditions to consider a
@@ -3020,12 +3253,10 @@ proc_synaptics(struct psm_softc *sc, pac
 			 *       first should be above a configurable minimum
 			 *     . tap timed out
 			 */
-			dxp = abs(synaction->queue[synaction->queue_cursor].x -
-			    synaction->start_x);
-			dyp = abs(synaction->queue[synaction->queue_cursor].y -
-			    synaction->start_y);
+			dxp = abs(x0 - start_x);
+			dyp = abs(y0 - start_y);
 
-			if (timevalcmp(&sc->lastsoftintr, &sc->taptimeout, >) ||
+			if (timevalcmp(&sc->lastsoftintr, &gest->taptimeout, >) ||
 			    dxp >= sc->syninfo.vscroll_min_delta ||
 			    dyp >= sc->syninfo.vscroll_min_delta) {
 				/*
@@ -3034,176 +3265,65 @@ proc_synaptics(struct psm_softc *sc, pac
 				 * as that keeps the maximum number of fingers.
 				 */
 				if (two_finger_scroll) {
-					if (w == 0) {
-						synaction->in_vscroll +=
+					if (nfingers == 2) {
+						gest->in_vscroll +=
 						    dyp ? 2 : 0;
-						synaction->in_vscroll +=
+						gest->in_vscroll +=
 						    dxp ? 1 : 0;
 					}
 				} else {
 					/* Check for horizontal scrolling. */
 					if ((vscroll_hor_area > 0 &&
-					    synaction->start_y <=
-					        vscroll_hor_area) ||
+					    start_y <= vscroll_hor_area) ||
 					    (vscroll_hor_area < 0 &&
-					     synaction->start_y >=
-					     6143 + vscroll_hor_area))
-						synaction->in_vscroll += 2;
+					     start_y >=
+					     max_y + vscroll_hor_area))
+						gest->in_vscroll += 2;
 
 					/* Check for vertical scrolling. */
 					if ((vscroll_ver_area > 0 &&
-					    synaction->start_x <=
-						vscroll_ver_area) ||
+					    start_x <= vscroll_ver_area) ||
 					    (vscroll_ver_area < 0 &&
-					     synaction->start_x >=
-					     6143 + vscroll_ver_area))
-						synaction->in_vscroll += 1;
+					     start_x >=
+					     max_x + vscroll_ver_area))
+						gest->in_vscroll += 1;
 				}
 
 				/* Avoid conflicts if area overlaps. */
-				if (synaction->in_vscroll >= 3)
-					synaction->in_vscroll =
+				if (gest->in_vscroll >= 3)
+					gest->in_vscroll =
 					    (dxp > dyp) ? 2 : 1;
 			}
 		}
 		/*
 		 * Reset two finger scrolling when the number of fingers
-		 * is different from two.
+		 * is different from two or any button is pressed.
 		 */
-		if (two_finger_scroll && w != 0 && synaction->in_vscroll != 0) {
-			synaction->in_vscroll = 0;
-			exiting_scroll = 1;
-		}
+		if (two_finger_scroll && gest->in_vscroll != 0 &&
+		    (nfingers != 2 || ms->button))
+			gest->in_vscroll = 0;
 
 		VLOG(5, (LOG_DEBUG,
 			"synaptics: virtual scrolling: %s "
 			"(direction=%d, dxp=%d, dyp=%d, fingers=%d)\n",
-			synaction->in_vscroll ? "YES" : "NO",
-			synaction->in_vscroll, dxp, dyp,
-			synaction->fingers_nb));
-
-		weight_prev_x = weight_prev_y = weight_previous;
-		div_max_x = div_max_y = div_max;
-
-		if (synaction->in_vscroll) {
-			/* Dividers are different with virtual scrolling. */
-			div_min = sc->syninfo.vscroll_div_min;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201609262206.u8QM6J5q070986>