From nobody@FreeBSD.org  Sun Jan  2 19:38:51 2005
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id C87A116A4CE
	for <freebsd-gnats-submit@FreeBSD.org>; Sun,  2 Jan 2005 19:38:51 +0000 (GMT)
Received: from www.freebsd.org (www.freebsd.org [216.136.204.117])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 8A8EC43D2D
	for <freebsd-gnats-submit@FreeBSD.org>; Sun,  2 Jan 2005 19:38:51 +0000 (GMT)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.13.1/8.13.1) with ESMTP id j02JcphE018227
	for <freebsd-gnats-submit@FreeBSD.org>; Sun, 2 Jan 2005 19:38:51 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.13.1/8.13.1/Submit) id j02Jcp2k018226;
	Sun, 2 Jan 2005 19:38:51 GMT
	(envelope-from nobody)
Message-Id: <200501021938.j02Jcp2k018226@www.freebsd.org>
Date: Sun, 2 Jan 2005 19:38:51 GMT
From: Jason Kuri <jay@oneway.com>
To: freebsd-gnats-submit@FreeBSD.org
Subject: [patch]  better synaptics support for psm(4)
X-Send-Pr-Version: www-2.3

>Number:         75725
>Category:       kern
>Synopsis:       [patch]  better synaptics support for psm(4)
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    philip
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Jan 02 19:40:25 GMT 2005
>Closed-Date:    Mon Jan 10 13:07:02 GMT 2005
>Last-Modified:  Mon Jan 10 13:07:02 GMT 2005
>Originator:     Jason Kuri
>Release:        5.3-RELEASE
>Organization:
Oneway.com
>Environment:
FreeBSD keeper 5.3-RELEASE FreeBSD 5.3-RELEASE #0: Fri Dec 23 21:46:50 UTC 2004     jk@keeper:/usr/src/sys/i386/compile/ACER  i386
>Description:
From the 5.3-TODO:  "Synaptics updates to the psm(4) driver have resulted in poor interactivity for taps and button press events for some users." 

The synaptics support is disabled due to aiming problems.  The patch attached fixes them.  It also adds scroll button support and sysctls to control various thresholds (such as the pressure required for a tap to register / the switch point between high and low speed tracking modes, etc.)
>How-To-Repeat:
Turn on hw.psm.synaptics_support.  Start X, try and grab a window corner to resize something.  Try again.  Curse.
>Fix:
I'm not sure how pasting a patch here will work, so I'll prefix this by providing a link to where the patch can be retrieved online:

http://oneway.com/hx/psm-synaptics.diff

--- below this line there be monsters ---

--- /usr/src/sys/isa/psm.c.orig	Fri Dec 24 01:22:51 2004
+++ /usr/src/sys/isa/psm.c	Fri Dec 24 01:22:40 2004
@@ -182,6 +182,9 @@
     int           button;	/* the latest button state */
     int		  xold;	/* previous absolute X position */
     int		  yold;	/* previous absolute Y position */
+    int		  xaverage;	/* average X position */
+    int		  yaverage;	/* average Y position */
+    int		  squelch;	/* level to filter movement data at low speed */
     int		  zmax;	/* maximum pressure value for touchpads */
     int		  syncerrors;	/* # of bytes discarded searching for sync */
     int		  pkterrors;	/* # of packets failed during quaranteen. */
@@ -234,8 +237,8 @@
 #define PSM_FLAGS_FINGERDOWN	0x0001 /* VersaPad finger down */
 
 /* Tunables */
-static int synaptics_support = 0;
-TUNABLE_INT("hw.psm.synaptics_support", &synaptics_support);
+static int synaptics_support = 1;
+TUNABLE_INT("hw.psm.synaptics_support", (int *) &synaptics_support);
 
 /* for backward compatibility */
 #define OLD_MOUSE_GETHWINFO	_IOR('M', 1, old_mousehw_t)
@@ -2029,8 +2032,42 @@
 SYSCTL_INT(_debug, OID_AUTO, psmpkterrthresh, CTLFLAG_RW,
     &psmpkterrthresh, 0, "");
 
+TUNABLE_INT("hw.psm.loglevel", (int *) &verbose);
 SYSCTL_INT(_debug, OID_AUTO, psmloglevel, CTLFLAG_RW, &verbose, 0, "");
 
+/* some sysctl additions to things that may need tuning at runtime */
+SYSCTL_NODE(_hw, OID_AUTO, psm, CTLFLAG_RD, 0, "ps/2 mouse");
+
+static int psm_tap_threshold = PSM_TAP_THRESHOLD;
+SYSCTL_INT(_hw_psm, OID_AUTO, tap_threshold, CTLFLAG_RW, &psm_tap_threshold, 0, "touchpad tap threshold");
+
+static int psm_tap_timeout = PSM_TAP_TIMEOUT;
+SYSCTL_INT(_hw_psm, OID_AUTO, tap_timeout, CTLFLAG_RW, &psm_tap_timeout, 0, "touchpad tap timeout");
+
+/* synaptics_directional_scrolls - if non-zero, the directional 
+ * pad scrolls, otherwise it registers as a middle-click.
+ */ 
+static int synaptics_directional_scrolls = 1;
+SYSCTL_INT(_hw_psm, OID_AUTO, synaptics_directional_scrolls, CTLFLAG_RW, &synaptics_directional_scrolls, 0, "directional pad scrolls (1=yes  0=3rd button)");
+
+/* synaptics_low_speed_threshold - the number of touchpad units 
+ * below-which we go into low-speed tracking mode.
+ */
+static int synaptics_low_speed_threshold = 20;
+SYSCTL_INT(_hw_psm, OID_AUTO, synaptics_low_speed_threshold, CTLFLAG_RW, &synaptics_low_speed_threshold, 0, "threshold between low and hi speed positioning");
+
+/* synaptics_min_movement - the number of touchpad units below 
+ * which we ignore altogether.
+ */
+static int synaptics_min_movement = 2;
+SYSCTL_INT(_hw_psm, OID_AUTO, synaptics_min_movement, CTLFLAG_RW, &synaptics_min_movement, 0, "ignore touchpad movements less than this");
+
+/* synaptics_squelch_level - level at which we squelch movement 
+ * packets.  This effectively sends 1 out of every 
+ * synaptics_squelch_level packets when running in low-speed mode.
+ */
+static int synaptics_squelch_level = 3;
+SYSCTL_INT(_hw_psm, OID_AUTO, synaptics_squelch_level, CTLFLAG_RW, &synaptics_squelch_level, 0, "squelch level for synaptics pads");
 
 static void
 psmintr(void *arg)
@@ -2205,7 +2242,7 @@
     int w, x, y, z;
     int c;
     int l;
-    int x0, y0;
+    int x0, y0, xavg, yavg, xsensitivity, ysensitivity, sensitivity = 0;
     int s;
     packetbuf_t *pb;
 
@@ -2579,6 +2616,30 @@
 		    touchpad_buttons |= MOUSE_BUTTON5DOWN;
 	    }
 
+	    /* In newer pads - bit 0x02 in the third byte of 
+	     * the packet indicates that we have an extended 
+	     * button press.
+	     */
+	    if (pb->ipacket[3] & 0x02) {
+	        /* if synaptics_directional_scrolls is not 1, we treat
+	     	 * any of the scrolling directions as middle-click.
+	     	 */
+		if (synaptics_directional_scrolls) {
+		    if (pb->ipacket[4] & 0x01)
+			touchpad_buttons |= MOUSE_BUTTON4DOWN;
+		    if (pb->ipacket[5] & 0x01)
+			touchpad_buttons |= MOUSE_BUTTON5DOWN;
+		    if (pb->ipacket[4] & 0x02)
+			touchpad_buttons |= MOUSE_BUTTON6DOWN;
+   		    if (pb->ipacket[5] & 0x02)
+			touchpad_buttons |= MOUSE_BUTTON7DOWN;
+		} else {
+		    if ((pb->ipacket[4] & 0x0F) || (pb->ipacket[5] & 0x0F))
+			touchpad_buttons |= MOUSE_BUTTON2DOWN;
+		}
+		   
+	    }
+
 	    ms.button = touchpad_buttons | guest_buttons;
 		
 	    /* There is a finger on the pad. */
@@ -2591,22 +2652,103 @@
 		    pb->ipacket[5];
 
 		if (sc->flags & PSM_FLAGS_FINGERDOWN) {
-		    x0 = (x0 + sc->xold * 3) / 4;
-		    y0 = (y0 + sc->yold * 3) / 4;
+		    x = x0 - sc->xold;
+		    y = y0 - sc->yold;
+
+		    /* we compute averages of x and y movement */
+
+		    if (sc->xaverage==0) { sc->xaverage=x; }
+		    if (sc->yaverage==0) { sc->yaverage=y; }
+                    xavg = sc->xaverage;
+                    yavg = sc->yaverage;
+		    sc->xaverage = (xavg + x) >>1;
+		    sc->yaverage = (yavg + y) >>1;
+		    
+		    /* then use the averages to compute a sensitivity level 
+		     * in each dimension 
+		     */
+		    xsensitivity = (sc->xaverage - xavg);
+		    if (xsensitivity <0)
+			xsensitivity = -xsensitivity;
+		    ysensitivity = (sc->yaverage - yavg);
+		    if (ysensitivity <0)
+			ysensitivity = -ysensitivity;
+
+		    /* the sensitivity level is higher the faster the finger
+		     * is moving. It also tends to be higher in the middle 
+		     * of a touchpad motion than on either end
+		     * Note - sensitivity gets to 0 when moving slowly - so 
+		     * we add 1 to it to give it a meaningful value in that case.
+		     */ 
+		    sensitivity = (xsensitivity & ysensitivity)+1;
+
+		    /* if either our x or y change is greater than our
+		     * hi/low speed threshold - we do the high-speed 
+		     * absolute to relative calculation otherwise we 
+		     * do the low-speed calculation.
+		     */
+		    if ((x>synaptics_low_speed_threshold || 
+			 x<-synaptics_low_speed_threshold) || 
+			(y>synaptics_low_speed_threshold || 
+			 y<-synaptics_low_speed_threshold))
+                    { 
+			x0 = (x0 + sc->xold * 3) / 4;
+			y0 = (y0 + sc->yold * 3) / 4;
+			x = (x0 - sc->xold) * 10 / 85;
+			y = (y0 - sc->yold) * 10 / 85;
+		    } else {
+			/* This is the low speed calculation.
+			 * We simply check to see if our movement
+			 * is more than our minimum movement threshold
+			 * and if it is - set the movement to 1 in the
+			 * correct direction. 
+			 * NOTE - Normally this would result in pointer
+			 * movement that was WAY too fast.  This works
+			 * due to the movement squelch we do later.
+			 */
+			if (x<-synaptics_min_movement)	
+		   	    x=-1;
+			else if (x>synaptics_min_movement)
+			    x=1;
+			else 
+			   x=0;
+			if (y < -synaptics_min_movement)
+			   y=-1;
+			else if (y > synaptics_min_movement)
+			   y=1;
+			else 
+			   y=0;
 
-		    x = (x0 - sc->xold) * 10 / 85;
-		    y = (y0 - sc->yold) * 10 / 85;
+		    }
 		} else {
 		    sc->flags |= PSM_FLAGS_FINGERDOWN;
 		}
 
+		/* ok - the squelch process.  Take our sensitivity value
+		 * and add it to the current squelch value - if squelch
+		 * is less than our squelch threshold we kill the movement,
+		 * otherwise we reset squelch and pass the movement through.
+		 * Since squelch is cumulative - when mouse movement is slow
+		 * (around sensitivity 1) the net result is that only 
+		 * 1 out of every synaptics_squelch_level packets is 
+		 * delivered, effectively slowing down the movement.
+		 */
+		sc->squelch += sensitivity;
+		if (sc->squelch < synaptics_squelch_level) {
+		    x=0;
+		    y=0;
+       		} else {
+		    sc->squelch=0;
+		}
+
 		sc->xold = x0;
 		sc->yold = y0;
 		sc->zmax = imax(z, sc->zmax);
+
 	    } else {
 		sc->flags &= ~PSM_FLAGS_FINGERDOWN;
 
-		if (sc->zmax > PSM_TAP_THRESHOLD &&
+		if (sc->zmax > psm_tap_threshold &&
 		    timevalcmp(&sc->lastsoftintr, &sc->taptimeout, <=)) {
 			if (w == 0)
 			    ms.button |= MOUSE_BUTTON3DOWN;
@@ -2617,8 +2759,8 @@
 		}
 
 		sc->zmax = 0;
-		sc->taptimeout.tv_sec = PSM_TAP_TIMEOUT / 1000000;
-		sc->taptimeout.tv_usec = PSM_TAP_TIMEOUT % 1000000;
+		sc->taptimeout.tv_sec = psm_tap_timeout / 1000000;
+		sc->taptimeout.tv_usec = psm_tap_timeout % 1000000;
 		timevaladd(&sc->taptimeout, &sc->lastsoftintr);
 	    }
 
@@ -2637,6 +2779,8 @@
 	    break;
 	}
 
+	
+
         /* scale values */
         if (sc->mode.accelfactor >= 1) {
             if (x != 0) {
@@ -3120,6 +3264,8 @@
 
     kbdc = sc->kbdc;
     disable_aux_dev(kbdc);
+    sc->hw.buttons = 3;
+    sc->squelch=0;
 
     /* Just to be on the safe side */
     set_mouse_scaling(kbdc, 1);
@@ -3203,6 +3349,20 @@
 	    printf("   capMultiFinger: %d\n", sc->synhw.capMultiFinger);
 	    printf("   capPalmDetect: %d\n", sc->synhw.capPalmDetect);
 	}
+
+	/*  if we have bits set in status[0] & 0x70 - then we can load
+	 *  more information about buttons using query 0x09
+	 */
+        if (status[0] & 0x70) {
+    	    if (mouse_ext_command(kbdc, 0x09) == 0)
+		return (FALSE);
+	    if (get_mouse_status(kbdc, status, 0, 3) != 3)
+		return (FALSE);
+            sc->hw.buttons = ((status[1] & 0xf0)>>4) + 3;
+	    if (verbose >= 2)
+	       printf("   Additional Buttons: %d\n", sc->hw.buttons -3);
+	}
+	
     } else {
 	sc->synhw.capExtended = 0;
 	    
@@ -3242,8 +3402,6 @@
      */
     if (sc->synhw.capExtended && sc->synhw.capFourButtons)
 	sc->hw.buttons = 4;
-    else
-	sc->hw.buttons = 3;
 
     return (TRUE);
 }


>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->philip 
Responsible-Changed-By: philip 
Responsible-Changed-When: Sun Jan 2 20:23:52 GMT 2005 
Responsible-Changed-Why:  
I'll take care of this. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=75725 
State-Changed-From-To: open->feedback 
State-Changed-By: philip 
State-Changed-When: Mon Jan 3 15:15:44 GMT 2005 
State-Changed-Why:  
I've updated your patch to match style and the current state of psm.c, 
can you try to get the synaptics sysctls to only appear when a  
synaptics device has actually been probed?  Then it should be good to 
commit.  Thanks! 

Index: psm.c 
=================================================================== 
RCS file: /home/philip/projects/freebsd/FreeBSD-CVS/src/sys/isa/psm.c,v 
retrieving revision 1.83 
diff -u -r1.83 psm.c 
--- psm.c	3 Jan 2005 13:19:49 -0000	1.83 
+++ psm.c	3 Jan 2005 14:49:52 -0000 
@@ -180,9 +180,12 @@ 
int           pqueue_start; /* start of data in queue */ 
int           pqueue_end;   /* end of data in queue */ 
int           button;	/* the latest button state */ 
-    int		  xold;	/* previous absolute X position */ 
-    int		  yold;	/* previous absolute Y position */ 
-    int		  zmax;	/* maximum pressure value for touchpads */ 
+    int		  xold;		/* previous absolute X position */ 
+    int		  yold;		/* previous absolute Y position */ 
+    int		  xaverage;	/* average X position */ 
+    int		  yaverage;	/* average Y position */ 
+    int		  squelch;	/* level to filter movement data at low speed */ 
+    int		  zmax;		/* maximum pressure value for touchpads */ 
int		  syncerrors;	/* # of bytes discarded searching for sync */ 
int		  pkterrors;	/* # of packets failed during quaranteen. */ 
struct timeval inputtimeout; 
@@ -2032,6 +2035,49 @@ 
SYSCTL_INT(_hw_psm, OID_AUTO, tap_threshold, CTLFLAG_RW, &tap_threshold, 0, ""); 
static int tap_timeout = PSM_TAP_TIMEOUT; 
SYSCTL_INT(_hw_psm, OID_AUTO, tap_timeout, CTLFLAG_RW, &tap_timeout, 0, ""); 
+     
+/* Attach extra synaptics sysctl nodes under hw.psm.synaptics */ 
+/* XXX: this should only happen when synaptics devices are actually present */ 
+SYSCTL_NODE(_hw_psm, OID_AUTO, synaptics, CTLFLAG_RD, 0, "Synaptics TouchPad"); 
+ 
+/* 
+ * synaptics_directional_scrolls - if non-zero, the directional 
+ * pad scrolls, otherwise it registers as a middle-click. 
+ */ 
+static int synaptics_directional_scrolls = 1; 
+SYSCTL_INT(_hw_psm_synaptics, OID_AUTO, directional_scrolls, 
+    CTLFLAG_RW, &synaptics_directional_scrolls, 0, 
+    "directional pad scrolls (1=yes  0=3rd button)"); 
+ 
+/* 
+ * Synaptics_low_speed_threshold - the number of touchpad units 
+ * below-which we go into low-speed tracking mode. 
+ */ 
+static int synaptics_low_speed_threshold = 20; 
+SYSCTL_INT(_hw_psm_synaptics, OID_AUTO, low_speed_threshold, 
+    CTLFLAG_RW, &synaptics_low_speed_threshold, 0, 
+    "threshold between low and hi speed positioning"); 
+ 
+/* 
+ * Synaptics_min_movement - the number of touchpad units below 
+ * which we ignore altogether. 
+ */ 
+static int synaptics_min_movement = 2; 
+SYSCTL_INT(_hw_psm_synaptics, OID_AUTO, min_movement, 
+    CTLFLAG_RW, &synaptics_min_movement, 0, 
+    "ignore touchpad movements less than this"); 
+ 
+/* 
+ * Synaptics_squelch_level - level at which we squelch movement 
+ * packets. 
+ * 
+ * This effectively sends 1 out of every synaptics_squelch_level 
+ * packets when * running in low-speed mode. 
+ */ 
+static int synaptics_squelch_level = 3; 
+SYSCTL_INT(_hw_psm_synaptics, OID_AUTO, squelch_level, 
+    CTLFLAG_RW, &synaptics_squelch_level, 0, 
+    "squelch level for synaptics touchpads"); 

static void 
psmintr(void *arg) 
@@ -2206,7 +2252,7 @@ 
int w, x, y, z; 
int c; 
int l; 
-    int x0, y0; 
+    int x0, y0, xavg, yavg, xsensitivity, ysensitivity, sensitivity = 0; 
int s; 
packetbuf_t *pb; 

@@ -2580,6 +2626,32 @@ 
touchpad_buttons |= MOUSE_BUTTON5DOWN; 
} 

+	    /*  
+	     * In newer pads - bit 0x02 in the third byte of 
+	     * the packet indicates that we have an extended 
+	     * button press. 
+	     */ 
+	    if (pb->ipacket[3] & 0x02) { 
+	        /*  
+		 * if synaptics_directional_scrolls is not 1, we treat 
+	     	 * any of the scrolling directions as middle-click. 
+	     	 */ 
+		if (synaptics_directional_scrolls) { 
+		    if (pb->ipacket[4] & 0x01) 
+			touchpad_buttons |= MOUSE_BUTTON4DOWN; 
+		    if (pb->ipacket[5] & 0x01) 
+			touchpad_buttons |= MOUSE_BUTTON5DOWN; 
+		    if (pb->ipacket[4] & 0x02) 
+			touchpad_buttons |= MOUSE_BUTTON6DOWN; 
+   		    if (pb->ipacket[5] & 0x02) 
+			touchpad_buttons |= MOUSE_BUTTON7DOWN; 
+		} else { 
+		    if ((pb->ipacket[4] & 0x0F) || (pb->ipacket[5] & 0x0F)) 
+			touchpad_buttons |= MOUSE_BUTTON2DOWN; 
+		} 
+ 
+	    } 
+ 
ms.button = touchpad_buttons | guest_buttons; 

/* There is a finger on the pad. */ 
@@ -2592,22 +2664,110 @@ 
pb->ipacket[5]; 

if (sc->flags & PSM_FLAGS_FINGERDOWN) { 
-		    x0 = (x0 + sc->xold * 3) / 4; 
-		    y0 = (y0 + sc->yold * 3) / 4; 
+		    x = x0 - sc->xold; 
+		    y = y0 - sc->yold; 

-		    x = (x0 - sc->xold) * 10 / 85; 
-		    y = (y0 - sc->yold) * 10 / 85; 
+		    /* we compute averages of x and y movement */ 
+		    if (sc->xaverage == 0) 
+			sc->xaverage=x; 
+ 
+		    if (sc->yaverage == 0) 
+			sc->yaverage=y; 
+ 
+                    xavg = sc->xaverage; 
+                    yavg = sc->yaverage; 
+ 
+		    sc->xaverage = (xavg + x) >> 1; 
+		    sc->yaverage = (yavg + y) >> 1; 
+ 
+		    /*  
+		     * then use the averages to compute a sensitivity level 
+		     * in each dimension 
+		     */ 
+		    xsensitivity = (sc->xaverage - xavg); 
+		    if (xsensitivity < 0) 
+			xsensitivity = -xsensitivity; 
+ 
+		    ysensitivity = (sc->yaverage - yavg); 
+		    if (ysensitivity < 0) 
+			ysensitivity = -ysensitivity; 
+ 
+		    /*  
+		     * The sensitivity level is higher the faster the finger 
+		     * is moving. It also tends to be higher in the middle 
+		     * of a touchpad motion than on either end 
+		     * 
+		     * Note - sensitivity gets to 0 when moving slowly - so 
+		     * we add 1 to it to give it a meaningful value in that case. 
+		     */ 
+		    sensitivity = (xsensitivity & ysensitivity)+1; 
+ 
+		    /*  
+		     * If either our x or y change is greater than our 
+		     * hi/low speed threshold - we do the high-speed 
+		     * absolute to relative calculation otherwise we 
+		     * do the low-speed calculation. 
+		     */ 
+		    if ((x>synaptics_low_speed_threshold || 
+			 x<-synaptics_low_speed_threshold) || 
+			(y>synaptics_low_speed_threshold || 
+			 y<-synaptics_low_speed_threshold)) { 
+			x0 = (x0 + sc->xold * 3) / 4; 
+			y0 = (y0 + sc->yold * 3) / 4; 
+			x = (x0 - sc->xold) * 10 / 85; 
+			y = (y0 - sc->yold) * 10 / 85; 
+		    } else { 
+			/* This is the low speed calculation. 
+			 * We simply check to see if our movement 
+			 * is more than our minimum movement threshold 
+			 * and if it is - set the movement to 1 in the 
+			 * correct direction. 
+			 * NOTE - Normally this would result in pointer 
+			 * movement that was WAY too fast.  This works 
+			 * due to the movement squelch we do later. 
+			 */ 
+			if (x < -synaptics_min_movement) 
+		   	    x = -1; 
+			else if (x > synaptics_min_movement) 
+			    x = 1; 
+			else 
+			   x = 0; 
+			if (y < -synaptics_min_movement) 
+			   y = -1; 
+			else if (y > synaptics_min_movement) 
+			   y = 1; 
+			else 
+			   y = 0; 
+ 
+		    } 
} else { 
sc->flags |= PSM_FLAGS_FINGERDOWN; 
} 

+		/*  
+		 * ok - the squelch process.  Take our sensitivity value 
+		 * and add it to the current squelch value - if squelch 
+		 * is less than our squelch threshold we kill the movement, 
+		 * otherwise we reset squelch and pass the movement through. 
+		 * Since squelch is cumulative - when mouse movement is slow 
+		 * (around sensitivity 1) the net result is that only 
+		 * 1 out of every synaptics_squelch_level packets is 
+		 * delivered, effectively slowing down the movement. 
+		 */ 
+		sc->squelch += sensitivity; 
+		if (sc->squelch < synaptics_squelch_level) { 
+		    x = 0; 
+		    y = 0; 
+		} else 
+		    sc->squelch = 0; 
+ 
sc->xold = x0; 
sc->yold = y0; 
sc->zmax = imax(z, sc->zmax); 
} else { 
sc->flags &= ~PSM_FLAGS_FINGERDOWN; 

-		if (sc->zmax > PSM_TAP_THRESHOLD && 
+		if (sc->zmax > tap_threshold && 
timevalcmp(&sc->lastsoftintr, &sc->taptimeout, <=)) { 
if (w == 0) 
ms.button |= MOUSE_BUTTON3DOWN; 
@@ -2618,8 +2778,8 @@ 
} 

sc->zmax = 0; 
-		sc->taptimeout.tv_sec = PSM_TAP_TIMEOUT / 1000000; 
-		sc->taptimeout.tv_usec = PSM_TAP_TIMEOUT % 1000000; 
+		sc->taptimeout.tv_sec = tap_timeout / 1000000; 
+		sc->taptimeout.tv_usec = tap_timeout % 1000000; 
timevaladd(&sc->taptimeout, &sc->lastsoftintr); 
} 

@@ -3121,6 +3281,8 @@ 

kbdc = sc->kbdc; 
disable_aux_dev(kbdc); 
+    sc->hw.buttons = 3; 
+    sc->squelch = 0; 

/* Just to be on the safe side */ 
set_mouse_scaling(kbdc, 1); 
@@ -3204,6 +3366,21 @@ 
printf("   capMultiFinger: %dn", sc->synhw.capMultiFinger); 
printf("   capPalmDetect: %dn", sc->synhw.capPalmDetect); 
} 
+ 
+	/* 
+	 * if we have bits set in status[0] & 0x70 - then we can load 
+	 * more information about buttons using query 0x09 
+	 */ 
+        if (status[0] & 0x70) { 
+    	    if (mouse_ext_command(kbdc, 0x09) == 0) 
+		return (FALSE); 
+	    if (get_mouse_status(kbdc, status, 0, 3) != 3) 
+		return (FALSE); 
+	    sc->hw.buttons = ((status[1] & 0xf0) >> 4) + 3; 
+	    if (verbose >= 2) 
+	       printf("  Additional Buttons: %dn", sc->hw.buttons -3); 
+	} 
+ 
} else { 
sc->synhw.capExtended = 0; 

@@ -3243,8 +3420,6 @@ 
*/ 
if (sc->synhw.capExtended && sc->synhw.capFourButtons) 
sc->hw.buttons = 4; 
-    else 
-	sc->hw.buttons = 3; 

return (TRUE); 
} 

http://www.freebsd.org/cgi/query-pr.cgi?pr=75725 

From: jay kuri <jay@oneway.com>
To: freebsd-gnats-submit@freebsd.org, jay@oneway.com
Cc:  
Subject: Re: kern/75725: [patch]  better synaptics support for psm(4)
Date: Sat, 8 Jan 2005 15:16:09 -0600

 Good Afternoon!
 
 Below is the new patch - it is against 1.83 of psm.c.  The sysctls that
 control synaptics behavior are now only enabled when a synaptics device is
 present.    I left the tap parameters on the main hw.psm node as they may be
 used for other devices, though it would be easy to move them to the synaptics
 node and info structure.
 
 The patch is also available here:
 
 http://oneway.com/hx/psm-sysctl.patch
 
 Jay
 
 --- cut here ---
 --- psm.c.orig Fri Jan  7 22:57:48 2005
 +++ psm.c Sat Jan  8 15:03:27 2005
 @@ -160,6 +160,15 @@
  #define PSM_PACKETQUEUE 128
  #endif
 
 +typedef struct synapticsinfo {
 +    int directional_scrolls;
 +    int low_speed_threshold;
 +    int min_movement;
 +    int squelch_level;
 +    struct sysctl_ctx_list sysctl_ctx;
 +    struct sysctl_oid *sysctl_tree;
 +} synapticsinfo_t;
 +
  /* driver control block */
  struct psm_softc {		/* Driver status information */
      int		  unit;
 @@ -172,6 +181,7 @@
      void   *ih;  /* interrupt handle */
      mousehw_t     hw;  /* hardware information */
      synapticshw_t synhw; /* Synaptics-specific hardware information */
 +    synapticsinfo_t syninfo; /* Synaptics-specific information */
      mousemode_t   mode;  /* operation mode */
      mousemode_t   dflt_mode; /* default operation mode */
      mousestatus_t status; /* accumulated mouse movement */
 @@ -182,6 +192,9 @@
      int           button; /* the latest button state */
      int    xold; /* previous absolute X position */
      int    yold; /* previous absolute Y position */
 +    int    xaverage; /* average X position */
 +    int    yaverage; /* average Y position */
 +    int    squelch; /* level to filter movement data at low speed */
      int    zmax; /* maximum pressure value for touchpads */
      int    syncerrors; /* # of bytes discarded searching for sync */
      int    pkterrors; /* # of packets failed during quaranteen. */
 @@ -2011,7 +2024,13 @@
 
  /* Add all sysctls under the debug.psm and hw.psm nodes */
  SYSCTL_NODE(_debug, OID_AUTO, psm, CTLFLAG_RD, 0, "ps/2 mouse");
 +
  SYSCTL_NODE(_hw, OID_AUTO, psm, CTLFLAG_RD, 0, "ps/2 mouse");
 +static int tap_threshold = PSM_TAP_THRESHOLD;
 +SYSCTL_INT(_hw_psm, OID_AUTO, tap_threshold, CTLFLAG_RW, &tap_threshold, 0,
 "");
 +static int tap_timeout = PSM_TAP_TIMEOUT;
 +SYSCTL_INT(_hw_psm, OID_AUTO, tap_timeout, CTLFLAG_RW, &tap_timeout, 0, "");
 +
 
  SYSCTL_INT(_debug_psm, OID_AUTO, loglevel, CTLFLAG_RW, &verbose, 0, "");
 
 @@ -2028,10 +2047,6 @@
  static int pkterrthresh = 2;
  SYSCTL_INT(_debug_psm, OID_AUTO, pkterrthresh, CTLFLAG_RW, &pkterrthresh, 0,
 "");
 
 -static int tap_threshold = PSM_TAP_THRESHOLD;
 -SYSCTL_INT(_hw_psm, OID_AUTO, tap_threshold, CTLFLAG_RW, &tap_threshold, 0,
 "");
 -static int tap_timeout = PSM_TAP_TIMEOUT;
 -SYSCTL_INT(_hw_psm, OID_AUTO, tap_timeout, CTLFLAG_RW, &tap_timeout, 0, "");
 
  static void
  psmintr(void *arg)
 @@ -2206,7 +2221,7 @@
      int w, x, y, z;
      int c;
      int l;
 -    int x0, y0;
 +    int x0, y0, xavg, yavg, xsensitivity, ysensitivity, sensitivity = 0;
      int s;
      packetbuf_t *pb;
 
 @@ -2580,6 +2595,32 @@
        touchpad_buttons |= MOUSE_BUTTON5DOWN;
       }
 
 +     /*
 +      * In newer pads - bit 0x02 in the third byte of
 +      * the packet indicates that we have an extended
 +      * button press.
 +      */
 +     if (pb->ipacket[3] & 0x02) {
 +         /*
 +   * if directional_scrolls is not 1, we treat
 +        * any of the scrolling directions as middle-click.
 +        */
 +  if (sc->syninfo.directional_scrolls) {
 +		    if (pb->ipacket[4] & 0x01)
 +			touchpad_buttons |= MOUSE_BUTTON4DOWN;
 +		    if (pb->ipacket[5] & 0x01)
 +			touchpad_buttons |= MOUSE_BUTTON5DOWN;
 +		    if (pb->ipacket[4] & 0x02)
 +			touchpad_buttons |= MOUSE_BUTTON6DOWN;
 +   		    if (pb->ipacket[5] & 0x02)
 +			touchpad_buttons |= MOUSE_BUTTON7DOWN;
 +		} else {
 +		    if ((pb->ipacket[4] & 0x0F) || (pb->ipacket[5] & 0x0F))
 +			touchpad_buttons |= MOUSE_BUTTON2DOWN;
 +		}
 +
 +	    }
 +
  	    ms.button = touchpad_buttons | guest_buttons;
 
       /* There is a finger on the pad. */
 @@ -2592,22 +2633,110 @@
        pb->ipacket[5];
 
    if (sc->flags & PSM_FLAGS_FINGERDOWN) {
 +      x = x0 - sc->xold;
 +      y = y0 - sc->yold;
 +
 +      /* we compute averages of x and y movement */
 +      if (sc->xaverage == 0)
 +   sc->xaverage=x;
 +
 +      if (sc->yaverage == 0)
 +   sc->yaverage=y;
 +
 +                    xavg = sc->xaverage;
 +                    yavg = sc->yaverage;
 +
 +		    sc->xaverage = (xavg + x) >> 1;
 +		    sc->yaverage = (yavg + y) >> 1;
 +
 +		    /*
 +		     * then use the averages to compute a sensitivity level
 +		     * in each dimension
 +		     */
 +		    xsensitivity = (sc->xaverage - xavg);
 +		    if (xsensitivity < 0)
 +			xsensitivity = -xsensitivity;
 +
 +		    ysensitivity = (sc->yaverage - yavg);
 +		    if (ysensitivity < 0)
 +			ysensitivity = -ysensitivity;
 +
 +		    /*
 +		     * The sensitivity level is higher the faster the finger
 +		     * is moving. It also tends to be higher in the middle
 +		     * of a touchpad motion than on either end
 +       *
 +       * Note - sensitivity gets to 0 when moving slowly - so
 +       * we add 1 to it to give it a meaningful value in that case.
 +       */
 +      sensitivity = (xsensitivity & ysensitivity)+1;
 +
 +      /*
 +       * If either our x or y change is greater than our
 +       * hi/low speed threshold - we do the high-speed
 +       * absolute to relative calculation otherwise we
 +       * do the low-speed calculation.
 +       */
 +      if ((x>sc->syninfo.low_speed_threshold ||
 +    x<-sc->syninfo.low_speed_threshold) ||
 +   (y>sc->syninfo.low_speed_threshold ||
 +    y<-sc->syninfo.low_speed_threshold)) {
        x0 = (x0 + sc->xold * 3) / 4;
  		    y0 = (y0 + sc->yold * 3) / 4;
 -
  		    x = (x0 - sc->xold) * 10 / 85;
  		    y = (y0 - sc->yold) * 10 / 85;
  		} else {
 +			/* This is the low speed calculation.
 +			 * We simply check to see if our movement
 +			 * is more than our minimum movement threshold
 +			 * and if it is - set the movement to 1 in the
 +			 * correct direction.
 +			 * NOTE - Normally this would result in pointer
 +			 * movement that was WAY too fast.  This works
 +			 * due to the movement squelch we do later.
 +			 */
 +			if (x < -sc->syninfo.min_movement)
 +		   	    x = -1;
 +			else if (x > sc->syninfo.min_movement)
 +			    x = 1;
 +			else
 +			   x = 0;
 +			if (y < -sc->syninfo.min_movement)
 +			   y = -1;
 +			else if (y > sc->syninfo.min_movement)
 +			   y = 1;
 +			else
 +      y = 0;
 +
 +      }
 +  } else {
        sc->flags |= PSM_FLAGS_FINGERDOWN;
    }
 
 +  /*
 +   * ok - the squelch process.  Take our sensitivity value
 +   * and add it to the current squelch value - if squelch
 +   * is less than our squelch threshold we kill the movement,
 +   * otherwise we reset squelch and pass the movement through.
 +   * Since squelch is cumulative - when mouse movement is slow
 +   * (around sensitivity 1) the net result is that only
 +   * 1 out of every squelch_level packets is
 +   * delivered, effectively slowing down the movement.
 +   */
 +  sc->squelch += sensitivity;
 +		if (sc->squelch < sc->syninfo.squelch_level) {
 +		    x = 0;
 +		    y = 0;
 +		} else
 +		    sc->squelch = 0;
 +
  		sc->xold = x0;
  		sc->yold = y0;
  		sc->zmax = imax(z, sc->zmax);
  	    } else {
  		sc->flags &= ~PSM_FLAGS_FINGERDOWN;
 
 -		if (sc->zmax > PSM_TAP_THRESHOLD &&
 +		if (sc->zmax > tap_threshold &&
  		    timevalcmp(&sc->lastsoftintr, &sc->taptimeout, <=)) {
  			if (w == 0)
  			    ms.button |= MOUSE_BUTTON3DOWN;
 @@ -2618,8 +2747,8 @@
  		}
 
  		sc->zmax = 0;
 -		sc->taptimeout.tv_sec = PSM_TAP_TIMEOUT / 1000000;
 -		sc->taptimeout.tv_usec = PSM_TAP_TIMEOUT % 1000000;
 +		sc->taptimeout.tv_sec = tap_timeout / 1000000;
 +  sc->taptimeout.tv_usec = tap_timeout % 1000000;
    timevaladd(&sc->taptimeout, &sc->lastsoftintr);
       }
 
 @@ -3119,8 +3248,66 @@
      if (!synaptics_support)
   return (FALSE);
 
 +
 +    /* Attach extra synaptics sysctl nodes under hw.psm.synaptics */
 +    /* XXX: this should only happen when synaptics devices are actually
 present */
 +
 +    sysctl_ctx_init(&sc->syninfo.sysctl_ctx);
 +    sc->syninfo.sysctl_tree = SYSCTL_ADD_NODE(&sc->syninfo.sysctl_ctx,
 SYSCTL_STATIC_CHILDREN(_hw_psm), OID_AUTO, "synaptics", CTLFLAG_RD, 0,
 "Synaptics TouchPad");
 +
 +
 +    /*
 +     * synaptics_directional_scrolls - if non-zero, the directional
 +     * pad scrolls, otherwise it registers as a middle-click.
 +     */
 +    sc->syninfo.directional_scrolls=1;
 +    SYSCTL_ADD_INT(&sc->syninfo.sysctl_ctx,
 +		   SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
 +		   "directional_scrolls", CTLFLAG_RW,
 +		   &sc->syninfo.directional_scrolls, 0,
 +		   "directional pad scrolls (1=yes  0=3rd button)");
 +
 +
 +    /*
 +     * Synaptics_low_speed_threshold - the number of touchpad units
 +     * below-which we go into low-speed tracking mode.
 +     */
 +    sc->syninfo.low_speed_threshold=20;
 +    SYSCTL_ADD_INT(&sc->syninfo.sysctl_ctx,
 +     SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
 +     "low_speed_threshold", CTLFLAG_RW,
 +     &sc->syninfo.low_speed_threshold, 0,
 +     "threshold between low and hi speed positioning");
 +    /*
 +     * Synaptics_min_movement - the number of touchpad units below
 +     * which we ignore altogether.
 +     */
 +    sc->syninfo.min_movement=2;
 +    SYSCTL_ADD_INT(&sc->syninfo.sysctl_ctx,
 +     SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
 +     "min_movement", CTLFLAG_RW,
 +     &sc->syninfo.min_movement, 0,
 +     "ignore touchpad movements less than this");
 +
 +    /*
 +     * Synaptics_squelch_level - level at which we squelch movement
 +     * packets.
 +     *
 +     * This effectively sends 1 out of every synaptics_squelch_level
 +     * packets when * running in low-speed mode.
 +     */
 +    sc->syninfo.squelch_level=3;
 +    SYSCTL_ADD_INT(&sc->syninfo.sysctl_ctx,
 +		   SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
 +		   "squelch_level", CTLFLAG_RW,
 +     &sc->syninfo.squelch_level, 0,
 +     "squelch level for synaptics touchpads");
 +
 +
      kbdc = sc->kbdc;
      disable_aux_dev(kbdc);
 +    sc->hw.buttons = 3;
 +    sc->squelch = 0;
 
      /* Just to be on the safe side */
      set_mouse_scaling(kbdc, 1);
 @@ -3204,6 +3391,21 @@
       printf("   capMultiFinger: %d\n", sc->synhw.capMultiFinger);
       printf("   capPalmDetect: %d\n", sc->synhw.capPalmDetect);
   }
 +
 + /*
 +  * if we have bits set in status[0] & 0x70 - then we can load
 +	 * more information about buttons using query 0x09
 +	 */
 +        if (status[0] & 0x70) {
 +         if (mouse_ext_command(kbdc, 0x09) == 0)
 +  return (FALSE);
 +     if (get_mouse_status(kbdc, status, 0, 3) != 3)
 +  return (FALSE);
 +     sc->hw.buttons = ((status[1] & 0xf0) >> 4) + 3;
 +     if (verbose >= 2)
 +        printf("  Additional Buttons: %d\n", sc->hw.buttons -3);
 + }
 +
      } else {
   sc->synhw.capExtended = 0;
 
 @@ -3243,8 +3445,6 @@
       */
      if (sc->synhw.capExtended && sc->synhw.capFourButtons)
   sc->hw.buttons = 4;
 -    else
 - sc->hw.buttons = 3;
 
      return (TRUE);
  }
State-Changed-From-To: feedback->closed 
State-Changed-By: philip 
State-Changed-When: Mon Jan 10 13:06:33 GMT 2005 
State-Changed-Why:  
Committed, thanks! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=75725 
>Unformatted:
