Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 2 Jan 2005 19:38:51 GMT
From:      Jason Kuri <jay@oneway.com>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   kern/75725: [patch]  better synaptics support for psm(4)
Message-ID:  <200501021938.j02Jcp2k018226@www.freebsd.org>
Resent-Message-ID: <200501021940.j02JeP7S068432@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         75725
>Category:       kern
>Synopsis:       [patch]  better synaptics support for psm(4)
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Jan 02 19:40:25 GMT 2005
>Closed-Date:
>Last-Modified:
>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:
>Unformatted:



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