Home | History | Annotate | Line # | Download | only in pckbport
synaptics.c revision 1.81.4.1
      1  1.81.4.1    martin /*	$NetBSD: synaptics.c,v 1.81.4.1 2024/06/22 10:02:05 martin Exp $	*/
      2       1.1  christos 
      3       1.1  christos /*
      4       1.3       scw  * Copyright (c) 2005, Steve C. Woodford
      5       1.1  christos  * Copyright (c) 2004, Ales Krenek
      6       1.1  christos  * Copyright (c) 2004, Kentaro A. Kurahone
      7       1.1  christos  * All rights reserved.
      8       1.5     perry  *
      9       1.1  christos  * Redistribution and use in source and binary forms, with or without
     10       1.1  christos  * modification, are permitted provided that the following conditions
     11       1.1  christos  * are met:
     12       1.1  christos  *
     13       1.1  christos  *   * Redistributions of source code must retain the above copyright
     14       1.1  christos  *     notice, this list of conditions and the following disclaimer.
     15       1.1  christos  *   * Redistributions in binary form must reproduce the above
     16       1.1  christos  *     copyright notice, this list of conditions and the following
     17       1.1  christos  *     disclaimer in the documentation and/or other materials provided
     18       1.1  christos  *     with the distribution.
     19       1.1  christos  *   * Neither the name of the authors nor the names of its
     20       1.1  christos  *     contributors may be used to endorse or promote products derived
     21       1.1  christos  *     from this software without specific prior written permission.
     22       1.1  christos  *
     23       1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     24       1.1  christos  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     25       1.1  christos  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     26       1.1  christos  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     27       1.1  christos  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     28       1.1  christos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     29       1.1  christos  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     30       1.1  christos  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     31       1.1  christos  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32       1.1  christos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     33       1.1  christos  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     34       1.1  christos  * POSSIBILITY OF SUCH DAMAGE.
     35       1.1  christos  *
     36       1.1  christos  */
     37       1.1  christos 
     38       1.3       scw /*
     39       1.3       scw  * TODO:
     40       1.3       scw  *	- Make the sysctl values per-instance instead of global.
     41       1.3       scw  *	- Consider setting initial scaling factors at runtime according
     42       1.3       scw  *	  to the values returned by the 'Read Resolutions' command.
     43       1.3       scw  *	- Support the serial protocol (we only support PS/2 for now)
     44       1.3       scw  *	- Support auto-repeat for up/down button Z-axis emulation.
     45       1.3       scw  *	- Maybe add some more gestures (can we use Palm support somehow?)
     46       1.3       scw  */
     47       1.3       scw 
     48       1.4       scw #include "opt_pms.h"
     49       1.4       scw 
     50       1.1  christos #include <sys/cdefs.h>
     51  1.81.4.1    martin __KERNEL_RCSID(0, "$NetBSD: synaptics.c,v 1.81.4.1 2024/06/22 10:02:05 martin Exp $");
     52       1.1  christos 
     53       1.1  christos #include <sys/param.h>
     54       1.1  christos #include <sys/systm.h>
     55       1.1  christos #include <sys/device.h>
     56       1.1  christos #include <sys/ioctl.h>
     57       1.1  christos #include <sys/sysctl.h>
     58       1.1  christos #include <sys/kernel.h>
     59      1.25  uebayasi #include <sys/proc.h>
     60       1.1  christos 
     61      1.16        ad #include <sys/bus.h>
     62       1.1  christos 
     63       1.1  christos #include <dev/pckbport/pckbportvar.h>
     64       1.1  christos 
     65       1.1  christos #include <dev/pckbport/synapticsreg.h>
     66       1.1  christos #include <dev/pckbport/synapticsvar.h>
     67       1.1  christos 
     68       1.1  christos #include <dev/pckbport/pmsreg.h>
     69       1.1  christos #include <dev/pckbport/pmsvar.h>
     70       1.1  christos 
     71       1.1  christos #include <dev/wscons/wsconsio.h>
     72       1.1  christos #include <dev/wscons/wsmousevar.h>
     73       1.1  christos 
     74       1.3       scw /*
     75       1.3       scw  * Absolute-mode packets are decoded and passed around using
     76       1.3       scw  * the following structure.
     77       1.3       scw  */
     78       1.3       scw struct synaptics_packet {
     79       1.3       scw 	signed short	sp_x;	/* Unscaled absolute X/Y coordinates */
     80       1.3       scw 	signed short	sp_y;
     81       1.3       scw 	u_char	sp_z;		/* Z (pressure) */
     82      1.73     blymn 	signed short	sp_sx;	/* Unscaled absolute X/Y coordinates */
     83      1.73     blymn 	signed short	sp_sy;  /* for secondary finger */
     84      1.73     blymn 	u_char	sp_sz;		/* Z (pressure) */
     85       1.3       scw 	u_char	sp_w;		/* W (contact patch width) */
     86      1.73     blymn 	u_char  sp_primary;	/* seen primary finger packet */
     87      1.73     blymn 	u_char  sp_secondary;	/* seen secondary finger packet */
     88      1.73     blymn 	u_char	sp_finger_status; /* seen extended finger packet */
     89      1.73     blymn 	u_char	sp_finger_count; /* number of fingers seen */
     90       1.3       scw 	char	sp_left;	/* Left mouse button status */
     91       1.3       scw 	char	sp_right;	/* Right mouse button status */
     92       1.3       scw 	char	sp_middle;	/* Middle button status (possibly emulated) */
     93       1.3       scw 	char	sp_up;		/* Up button status */
     94       1.3       scw 	char	sp_down;	/* Down button status */
     95       1.3       scw };
     96       1.3       scw 
     97       1.1  christos static void pms_synaptics_input(void *, int);
     98       1.3       scw static void pms_synaptics_process_packet(struct pms_softc *,
     99       1.3       scw 		struct synaptics_packet *);
    100       1.2  christos static void pms_sysctl_synaptics(struct sysctllog **);
    101       1.1  christos static int pms_sysctl_synaptics_verify(SYSCTLFN_ARGS);
    102       1.1  christos 
    103      1.28  jakllsch /* Controlled by sysctl. */
    104      1.56       nia static int synaptics_up_down_emul = 3;
    105       1.3       scw static int synaptics_up_down_motion_delta = 1;
    106       1.3       scw static int synaptics_gesture_move = 200;
    107       1.3       scw static int synaptics_gesture_length = 20;
    108       1.3       scw static int synaptics_edge_left = SYNAPTICS_EDGE_LEFT;
    109       1.3       scw static int synaptics_edge_right = SYNAPTICS_EDGE_RIGHT;
    110       1.3       scw static int synaptics_edge_top = SYNAPTICS_EDGE_TOP;
    111       1.3       scw static int synaptics_edge_bottom = SYNAPTICS_EDGE_BOTTOM;
    112       1.3       scw static int synaptics_edge_motion_delta = 32;
    113       1.3       scw static u_int synaptics_finger_high = SYNAPTICS_FINGER_LIGHT + 5;
    114       1.3       scw static u_int synaptics_finger_low = SYNAPTICS_FINGER_LIGHT - 10;
    115  1.81.4.1    martin static int synaptics_hscroll_pct = 0;
    116  1.81.4.1    martin static int synaptics_vscroll_pct = 0;
    117  1.81.4.1    martin static int synaptics_button_pct = 0;
    118  1.81.4.1    martin static int synaptics_button_boundary = SYNAPTICS_EDGE_BOTTOM;
    119      1.77     blymn static int synaptics_button2;
    120      1.77     blymn static int synaptics_button3;
    121       1.3       scw static int synaptics_two_fingers_emul = 0;
    122      1.81       nia static int synaptics_scale_x = 8;
    123      1.81       nia static int synaptics_scale_y = 8;
    124      1.63       nia static int synaptics_scale_z = 32;
    125       1.3       scw static int synaptics_max_speed_x = 32;
    126       1.3       scw static int synaptics_max_speed_y = 32;
    127      1.63       nia static int synaptics_max_speed_z = 2;
    128       1.3       scw static int synaptics_movement_threshold = 4;
    129      1.36  jmcneill static int synaptics_movement_enable = 1;
    130      1.73     blymn static int synaptics_button_region_movement = 1;
    131      1.70       nia static bool synaptics_aux_mid_button_scroll = TRUE;
    132      1.71  riastrad static int synaptics_debug = 0;
    133      1.71  riastrad 
    134      1.73     blymn #define	DPRINTF(LEVEL, SC, FMT, ARGS...) do					      \
    135      1.71  riastrad {									      \
    136      1.73     blymn 	if (synaptics_debug >= LEVEL) {						      \
    137      1.71  riastrad 		struct pms_softc *_dprintf_psc =			      \
    138      1.71  riastrad 		    container_of((SC), struct pms_softc, u.synaptics);	      \
    139      1.71  riastrad 		device_printf(_dprintf_psc->sc_dev, FMT, ##ARGS);	      \
    140      1.71  riastrad 	}								      \
    141      1.71  riastrad } while (0)
    142       1.1  christos 
    143       1.1  christos /* Sysctl nodes. */
    144      1.34     blymn static int synaptics_button_boundary_nodenum;
    145      1.34     blymn static int synaptics_button2_nodenum;
    146      1.34     blymn static int synaptics_button3_nodenum;
    147       1.3       scw static int synaptics_up_down_emul_nodenum;
    148       1.3       scw static int synaptics_up_down_motion_delta_nodenum;
    149       1.3       scw static int synaptics_gesture_move_nodenum;
    150       1.3       scw static int synaptics_gesture_length_nodenum;
    151       1.3       scw static int synaptics_edge_left_nodenum;
    152       1.3       scw static int synaptics_edge_right_nodenum;
    153       1.3       scw static int synaptics_edge_top_nodenum;
    154       1.3       scw static int synaptics_edge_bottom_nodenum;
    155       1.3       scw static int synaptics_edge_motion_delta_nodenum;
    156       1.3       scw static int synaptics_finger_high_nodenum;
    157       1.3       scw static int synaptics_finger_low_nodenum;
    158       1.3       scw static int synaptics_two_fingers_emul_nodenum;
    159       1.3       scw static int synaptics_scale_x_nodenum;
    160       1.3       scw static int synaptics_scale_y_nodenum;
    161      1.44     blymn static int synaptics_scale_z_nodenum;
    162       1.3       scw static int synaptics_max_speed_x_nodenum;
    163       1.3       scw static int synaptics_max_speed_y_nodenum;
    164      1.44     blymn static int synaptics_max_speed_z_nodenum;
    165       1.3       scw static int synaptics_movement_threshold_nodenum;
    166      1.36  jmcneill static int synaptics_movement_enable_nodenum;
    167      1.73     blymn static int synaptics_button_region_movement_nodenum;
    168      1.70       nia static int synaptics_aux_mid_button_scroll_nodenum;
    169  1.81.4.1    martin static int synaptics_hscroll_pct_nodenum;
    170  1.81.4.1    martin static int synaptics_vscroll_pct_nodenum;
    171      1.77     blymn static int synaptics_button_pct_nodenum;
    172      1.77     blymn 
    173      1.77     blymn /*
    174      1.77     blymn  * copy of edges so we can recalculate edge limit if there is
    175      1.77     blymn  * vertical scroll region
    176      1.77     blymn  */
    177  1.81.4.1    martin static int synaptics_true_edge_right;
    178  1.81.4.1    martin static int synaptics_true_edge_bottom;
    179      1.77     blymn 
    180  1.81.4.1    martin /*
    181  1.81.4.1    martin  * invalid old values, recalculate everything
    182  1.81.4.1    martin  */
    183  1.81.4.1    martin static int synaptics_old_vscroll_pct = -1;
    184  1.81.4.1    martin static int synaptics_old_hscroll_pct = -1;
    185  1.81.4.1    martin static int synaptics_old_button_pct = -1;
    186  1.81.4.1    martin static int synaptics_old_button_boundary = -1;
    187  1.81.4.1    martin static int synaptics_old_edge_right = -1;
    188  1.81.4.1    martin static int synaptics_old_edge_bottom = -1;
    189       1.1  christos 
    190      1.73     blymn /*
    191      1.73     blymn  * This holds the processed packet data, it is global because multiple
    192      1.73     blymn  * packets from the trackpad may be processed when handling multiple
    193      1.73     blymn  * fingers on the trackpad to gather all the data.
    194      1.73     blymn  */
    195      1.73     blymn static struct synaptics_packet packet;
    196      1.73     blymn 
    197      1.34     blymn static int
    198      1.34     blymn synaptics_poll_cmd(struct pms_softc *psc, ...)
    199      1.34     blymn {
    200      1.34     blymn 	u_char cmd[4];
    201      1.34     blymn 	size_t i;
    202      1.34     blymn 	va_list ap;
    203      1.34     blymn 
    204      1.34     blymn 	va_start(ap, psc);
    205      1.34     blymn 
    206      1.34     blymn 	for (i = 0; i < __arraycount(cmd); i++)
    207      1.34     blymn 		if ((cmd[i] = (u_char)va_arg(ap, int)) == 0)
    208      1.34     blymn 			break;
    209      1.34     blymn 	va_end(ap);
    210      1.34     blymn 
    211      1.34     blymn 	int res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd, i, 0,
    212      1.34     blymn     	    NULL, 0);
    213      1.34     blymn 	if (res)
    214  1.81.4.1    martin 		device_printf(psc->sc_dev, "command error %#x\n", cmd[0]);
    215      1.34     blymn 	return res;
    216      1.34     blymn }
    217      1.34     blymn 
    218      1.34     blymn static int
    219      1.34     blymn synaptics_poll_reset(struct pms_softc *psc)
    220      1.34     blymn {
    221      1.34     blymn 	u_char resp[2];
    222      1.34     blymn 	int res;
    223      1.34     blymn 
    224      1.34     blymn 	u_char cmd[1] = { PMS_RESET };
    225      1.34     blymn 	res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd, 1, 2,
    226      1.34     blymn 	    resp, 1);
    227  1.81.4.1    martin 	DPRINTF(10, &psc->u.synaptics, "reset %d 0x%02x 0x%02x\n",
    228      1.34     blymn 	    res, resp[0], resp[1]);
    229      1.34     blymn 	return res;
    230      1.34     blymn }
    231      1.34     blymn 
    232      1.34     blymn static int
    233      1.42      maya synaptics_special_read(struct pms_softc *psc, u_char slice, u_char resp[3])
    234      1.34     blymn {
    235      1.34     blymn 	u_char cmd[1] = { PMS_SEND_DEV_STATUS };
    236      1.34     blymn 	int res = pms_sliced_command(psc->sc_kbctag, psc->sc_kbcslot, slice);
    237      1.34     blymn 
    238      1.34     blymn 	return res | pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    239      1.34     blymn 	    cmd, 1, 3, resp, 0);
    240      1.34     blymn }
    241      1.34     blymn 
    242      1.42      maya static int
    243      1.42      maya synaptics_special_write(struct pms_softc *psc, u_char command, u_char arg)
    244      1.42      maya {
    245      1.42      maya 	int res = pms_sliced_command(psc->sc_kbctag, psc->sc_kbcslot, arg);
    246      1.42      maya 	if (res)
    247      1.42      maya 		return res;
    248      1.42      maya 
    249      1.42      maya 	u_char cmd[2];
    250      1.42      maya 	cmd[0] = PMS_SET_SAMPLE;
    251      1.42      maya 	cmd[1] = command;
    252      1.42      maya 	res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot,
    253      1.42      maya 	    cmd, 2, 0, NULL, 0);
    254      1.42      maya 	return res;
    255      1.42      maya }
    256      1.42      maya 
    257  1.81.4.1    martin static int
    258  1.81.4.1    martin synaptics_value(int pct, int low, int high)
    259  1.81.4.1    martin {
    260  1.81.4.1    martin 	return low + pct * (high - low) / 100UL;
    261  1.81.4.1    martin }
    262  1.81.4.1    martin 
    263  1.81.4.1    martin static int
    264  1.81.4.1    martin synaptics_percentage(int val, int low, int high)
    265  1.81.4.1    martin {
    266  1.81.4.1    martin 	return ((val - low) * 100UL + high - low - 1) / (high - low);
    267  1.81.4.1    martin }
    268  1.81.4.1    martin 
    269      1.32  christos static void
    270      1.77     blymn pms_synaptics_set_boundaries(void)
    271      1.77     blymn {
    272  1.81.4.1    martin 	if (synaptics_vscroll_pct != synaptics_old_vscroll_pct ) {
    273  1.81.4.1    martin 		synaptics_edge_right = synaptics_value(
    274  1.81.4.1    martin 		    100 - synaptics_vscroll_pct,
    275  1.81.4.1    martin 		    synaptics_edge_left,
    276  1.81.4.1    martin 		    synaptics_true_edge_right);
    277  1.81.4.1    martin 		synaptics_old_vscroll_pct = synaptics_vscroll_pct;
    278      1.77     blymn 	}
    279      1.77     blymn 
    280  1.81.4.1    martin 	if (synaptics_edge_right != synaptics_old_edge_right) {
    281  1.81.4.1    martin 		if (synaptics_edge_right >= synaptics_true_edge_right) {
    282  1.81.4.1    martin 			synaptics_vscroll_pct = 0;
    283  1.81.4.1    martin 			synaptics_edge_right = synaptics_true_edge_right;
    284      1.77     blymn 		} else {
    285  1.81.4.1    martin 			synaptics_vscroll_pct = 100 - synaptics_percentage(
    286  1.81.4.1    martin 			    synaptics_edge_right,
    287  1.81.4.1    martin 			    synaptics_edge_left,
    288  1.81.4.1    martin 			    synaptics_true_edge_right);
    289  1.81.4.1    martin 		}
    290  1.81.4.1    martin 		synaptics_old_vscroll_pct = synaptics_vscroll_pct;
    291  1.81.4.1    martin 		synaptics_old_edge_right = synaptics_edge_right;
    292  1.81.4.1    martin 	}
    293  1.81.4.1    martin 
    294  1.81.4.1    martin 	if (synaptics_hscroll_pct != synaptics_old_hscroll_pct ) {
    295  1.81.4.1    martin 		synaptics_edge_bottom = synaptics_value(
    296  1.81.4.1    martin 		    synaptics_hscroll_pct,
    297  1.81.4.1    martin 		    synaptics_true_edge_bottom,
    298  1.81.4.1    martin 		    synaptics_edge_top);
    299  1.81.4.1    martin 		synaptics_old_hscroll_pct = synaptics_hscroll_pct;
    300  1.81.4.1    martin 	}
    301  1.81.4.1    martin 
    302  1.81.4.1    martin 	if (synaptics_edge_bottom != synaptics_old_edge_bottom) {
    303  1.81.4.1    martin 		if (synaptics_edge_bottom <= synaptics_true_edge_bottom) {
    304  1.81.4.1    martin 			synaptics_hscroll_pct = 0;
    305  1.81.4.1    martin 			synaptics_edge_bottom = synaptics_true_edge_bottom;
    306      1.77     blymn 		} else {
    307  1.81.4.1    martin 			synaptics_hscroll_pct = synaptics_percentage(
    308  1.81.4.1    martin 			    synaptics_edge_bottom,
    309  1.81.4.1    martin 			    synaptics_true_edge_bottom,
    310  1.81.4.1    martin 			    synaptics_edge_top);
    311      1.77     blymn 		}
    312  1.81.4.1    martin 		synaptics_old_hscroll_pct = synaptics_hscroll_pct;
    313  1.81.4.1    martin 		synaptics_old_edge_bottom = synaptics_edge_bottom;
    314      1.77     blymn 	}
    315      1.77     blymn 
    316      1.77     blymn 	if (synaptics_button_pct != synaptics_old_button_pct) {
    317  1.81.4.1    martin 		synaptics_button_boundary = synaptics_value(
    318  1.81.4.1    martin 		    synaptics_button_pct,
    319  1.81.4.1    martin 		    synaptics_edge_bottom,
    320  1.81.4.1    martin 		    synaptics_edge_top);
    321      1.77     blymn 		synaptics_old_button_pct = synaptics_button_pct;
    322      1.77     blymn 	}
    323      1.77     blymn 
    324      1.77     blymn 	if (synaptics_button_boundary != synaptics_old_button_boundary) {
    325      1.77     blymn 		if (synaptics_button_boundary <= synaptics_edge_bottom) {
    326      1.77     blymn 			synaptics_button_pct = 0;
    327  1.81.4.1    martin 			synaptics_button_boundary = synaptics_edge_bottom;
    328      1.80   mlelstv 		} else if (synaptics_button_boundary >= synaptics_edge_top) {
    329      1.80   mlelstv 			synaptics_button_pct = 100;
    330  1.81.4.1    martin 			synaptics_button_boundary = synaptics_edge_top;
    331      1.77     blymn 		} else {
    332  1.81.4.1    martin 			synaptics_button_pct = synaptics_percentage(
    333  1.81.4.1    martin 			    synaptics_button_boundary,
    334  1.81.4.1    martin 			    synaptics_edge_bottom,
    335  1.81.4.1    martin 			    synaptics_edge_top);
    336      1.77     blymn 		}
    337      1.80   mlelstv 		synaptics_old_button_pct = synaptics_button_pct;
    338  1.81.4.1    martin 		synaptics_old_button_boundary = synaptics_button_boundary;
    339      1.77     blymn 	}
    340      1.77     blymn 
    341      1.77     blymn 	synaptics_button2 = synaptics_edge_left +
    342      1.77     blymn 	    (synaptics_edge_right - synaptics_edge_left) / 3;
    343      1.77     blymn 	synaptics_button3 = synaptics_edge_left +
    344      1.77     blymn 	    2 * (synaptics_edge_right - synaptics_edge_left) / 3;
    345      1.77     blymn 
    346      1.77     blymn }
    347      1.77     blymn 
    348      1.77     blymn static void
    349      1.32  christos pms_synaptics_probe_extended(struct pms_softc *psc)
    350      1.32  christos {
    351      1.32  christos 	struct synaptics_softc *sc = &psc->u.synaptics;
    352      1.34     blymn 	u_char resp[3];
    353      1.32  christos 	int res;
    354      1.32  christos 
    355  1.81.4.1    martin 	DPRINTF(10, sc,
    356      1.32  christos 	    "synaptics_probe: Capabilities 0x%04x.\n", sc->caps);
    357      1.32  christos 	if (sc->caps & SYNAPTICS_CAP_PASSTHROUGH)
    358      1.32  christos 		sc->flags |= SYN_FLAG_HAS_PASSTHROUGH;
    359      1.32  christos 
    360      1.32  christos 	if (sc->caps & SYNAPTICS_CAP_PALMDETECT)
    361      1.32  christos 		sc->flags |= SYN_FLAG_HAS_PALM_DETECT;
    362      1.32  christos 
    363      1.32  christos 	if (sc->caps & SYNAPTICS_CAP_MULTIDETECT)
    364      1.32  christos 		sc->flags |= SYN_FLAG_HAS_MULTI_FINGER;
    365      1.32  christos 
    366      1.32  christos 	if (sc->caps & SYNAPTICS_CAP_MULTIFINGERREPORT)
    367      1.32  christos 		sc->flags |= SYN_FLAG_HAS_MULTI_FINGER_REPORT;
    368      1.32  christos 
    369      1.32  christos 	/* Ask about extra buttons to detect up/down. */
    370  1.81.4.1    martin 	if ((__SHIFTOUT(sc->caps, SYNAPTICS_CAP_EXTNUM) + 0x08)
    371      1.32  christos 	    >= SYNAPTICS_EXTENDED_QUERY)
    372      1.32  christos 	{
    373      1.42      maya 		res = synaptics_special_read(psc, SYNAPTICS_EXTENDED_QUERY, resp);
    374      1.32  christos 		if (res == 0) {
    375      1.77     blymn 			sc->num_buttons = (resp[1] >> 4);
    376      1.77     blymn 			if (sc->num_buttons > 0)
    377      1.77     blymn 				sc->button_mask = sc->button_mask <<
    378      1.77     blymn 				    ((sc->num_buttons + 1) >> 1);
    379      1.77     blymn 
    380  1.81.4.1    martin 			DPRINTF(10, sc,
    381  1.81.4.1    martin 			    "Extended Buttons: %d.\n",
    382      1.77     blymn 			    sc->num_buttons);
    383      1.32  christos 
    384  1.81.4.1    martin 			DPRINTF(10, sc, "Extended "
    385  1.81.4.1    martin 			    "Capabilities: 0x%02x 0x%02x 0x%02x.\n",
    386      1.32  christos 			    resp[0], resp[1], resp[2]);
    387      1.77     blymn 			if (sc->num_buttons >= 2) {
    388      1.32  christos 				/* Yes. */
    389      1.32  christos 				sc->flags |= SYN_FLAG_HAS_UP_DOWN_BUTTONS;
    390      1.32  christos 			}
    391      1.32  christos 			if (resp[0] & 0x1) {
    392      1.32  christos 				/* Vertical scroll area */
    393      1.32  christos 				sc->flags |= SYN_FLAG_HAS_VERTICAL_SCROLL;
    394      1.32  christos 			}
    395      1.32  christos 			if (resp[0] & 0x2) {
    396      1.32  christos 				/* Horizontal scroll area */
    397      1.32  christos 				sc->flags |= SYN_FLAG_HAS_HORIZONTAL_SCROLL;
    398      1.32  christos 			}
    399      1.32  christos 			if (resp[0] & 0x4) {
    400      1.32  christos 				/* Extended W-Mode */
    401      1.32  christos 				sc->flags |= SYN_FLAG_HAS_EXTENDED_WMODE;
    402      1.32  christos 			}
    403      1.32  christos 		}
    404      1.32  christos 	}
    405      1.32  christos 
    406      1.32  christos 	/* Ask about click pad */
    407  1.81.4.1    martin 	if ((__SHIFTOUT(sc->caps, SYNAPTICS_CAP_EXTNUM) + 0x08) >=
    408      1.32  christos 	    SYNAPTICS_CONTINUED_CAPABILITIES)
    409      1.32  christos 	{
    410      1.42      maya 		res = synaptics_special_read(psc,
    411      1.34     blymn 		    SYNAPTICS_CONTINUED_CAPABILITIES, resp);
    412      1.34     blymn 
    413      1.33  christos /*
    414      1.33  christos  * The following describes response for the
    415      1.33  christos  * SYNAPTICS_CONTINUED_CAPABILITIES query.
    416      1.33  christos  *
    417      1.33  christos  * byte	mask	name			meaning
    418      1.33  christos  * ----	----	-------			------------
    419      1.33  christos  * 0	0x01	adjustable threshold	capacitive button sensitivity
    420      1.33  christos  *					can be adjusted
    421      1.33  christos  * 0	0x02	report max		query 0x0d gives max coord reported
    422      1.33  christos  * 0	0x04	clearpad		sensor is ClearPad product
    423      1.33  christos  * 0	0x08	advanced gesture	not particularly meaningful
    424      1.33  christos  * 0	0x10	clickpad bit 0		1-button ClickPad
    425      1.33  christos  * 0	0x60	multifinger mode	identifies firmware finger counting
    426      1.33  christos  *					(not reporting!) algorithm.
    427      1.33  christos  *					Not particularly meaningful
    428      1.33  christos  * 0	0x80	covered pad		W clipped to 14, 15 == pad mostly covered
    429      1.33  christos  * 1	0x01	clickpad bit 1		2-button ClickPad
    430      1.33  christos  * 1	0x02	deluxe LED controls	touchpad support LED commands
    431      1.33  christos  *					ala multimedia control bar
    432      1.33  christos  * 1	0x04	reduced filtering	firmware does less filtering on
    433      1.33  christos  *					position data, driver should watch
    434      1.33  christos  *					for noise.
    435      1.33  christos  * 1	0x08	image sensor		image sensor tracks 5 fingers, but only
    436      1.33  christos  *					reports 2.
    437      1.47     blymn  * 1	0x10	uniform clickpad	whole clickpad moves instead of being
    438      1.33  christos  *					hinged at the top.
    439      1.33  christos  * 1	0x20	report min		query 0x0f gives min coord reported
    440      1.33  christos  */
    441      1.32  christos 		if (res == 0) {
    442      1.48     blymn 			uint val = SYN_CCAP_VALUE(resp);
    443      1.32  christos 
    444  1.81.4.1    martin 			DPRINTF(10, sc, "Continued "
    445  1.81.4.1    martin 			    "Capabilities 0x%02x 0x%02x 0x%02x.\n",
    446      1.32  christos 			    resp[0], resp[1], resp[2]);
    447      1.48     blymn 			switch (SYN_CCAP_CLICKPAD_TYPE(val)) {
    448      1.48     blymn 			case 0: /* not a clickpad */
    449      1.48     blymn 				break;
    450      1.48     blymn 			case 1:
    451      1.32  christos 				sc->flags |= SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD;
    452      1.32  christos 				break;
    453      1.48     blymn 			case 2:
    454      1.32  christos 				sc->flags |= SYN_FLAG_HAS_TWO_BUTTON_CLICKPAD;
    455      1.32  christos 				break;
    456      1.48     blymn 			case 3: /* reserved */
    457      1.32  christos 			default:
    458      1.48     blymn 				/* unreached */
    459      1.32  christos 				break;
    460      1.32  christos 			}
    461      1.49     blymn 
    462      1.49     blymn 			if ((val & SYN_CCAP_HAS_ADV_GESTURE_MODE))
    463      1.49     blymn 				sc->flags |= SYN_FLAG_HAS_ADV_GESTURE_MODE;
    464      1.76     blymn 
    465      1.76     blymn 			if ((val & SYN_CCAP_REPORT_MAX))
    466      1.76     blymn 				sc->flags |= SYN_FLAG_HAS_MAX_REPORT;
    467      1.76     blymn 
    468      1.76     blymn 			if ((val & SYN_CCAP_REPORT_MIN))
    469      1.76     blymn 				sc->flags |= SYN_FLAG_HAS_MIN_REPORT;
    470      1.32  christos 		}
    471      1.32  christos 	}
    472      1.32  christos }
    473      1.32  christos 
    474      1.40  christos static const struct {
    475      1.40  christos 	int bit;
    476      1.40  christos 	const char *desc;
    477      1.40  christos } syn_flags[] = {
    478      1.40  christos 	{ SYN_FLAG_HAS_EXTENDED_WMODE, "Extended W mode", },
    479      1.40  christos 	{ SYN_FLAG_HAS_PASSTHROUGH, "Passthrough", },
    480      1.40  christos 	{ SYN_FLAG_HAS_MIDDLE_BUTTON, "Middle button", },
    481      1.40  christos 	{ SYN_FLAG_HAS_BUTTONS_4_5, "Buttons 4/5", },
    482      1.40  christos 	{ SYN_FLAG_HAS_UP_DOWN_BUTTONS, "Up/down buttons", },
    483      1.40  christos 	{ SYN_FLAG_HAS_PALM_DETECT, "Palm detect", },
    484      1.40  christos 	{ SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD, "One button click pad", },
    485      1.40  christos 	{ SYN_FLAG_HAS_TWO_BUTTON_CLICKPAD, "Two button click pad", },
    486      1.40  christos 	{ SYN_FLAG_HAS_VERTICAL_SCROLL, "Vertical scroll", },
    487      1.40  christos 	{ SYN_FLAG_HAS_HORIZONTAL_SCROLL, "Horizontal scroll", },
    488      1.40  christos 	{ SYN_FLAG_HAS_MULTI_FINGER_REPORT, "Multi-finger Report", },
    489      1.40  christos 	{ SYN_FLAG_HAS_MULTI_FINGER, "Multi-finger", },
    490      1.76     blymn 	{ SYN_FLAG_HAS_MAX_REPORT, "Reports max", },
    491      1.76     blymn 	{ SYN_FLAG_HAS_MIN_REPORT, "Reports min", },
    492      1.40  christos };
    493      1.40  christos 
    494       1.1  christos int
    495       1.1  christos pms_synaptics_probe_init(void *vsc)
    496       1.1  christos {
    497       1.3       scw 	struct pms_softc *psc = vsc;
    498       1.3       scw 	struct synaptics_softc *sc = &psc->u.synaptics;
    499      1.28  jakllsch 	u_char cmd[1], resp[3];
    500       1.3       scw 	int res, ver_minor, ver_major;
    501       1.2  christos 	struct sysctllog *clog = NULL;
    502       1.1  christos 
    503      1.27  jakllsch 	res = pms_sliced_command(psc->sc_kbctag, psc->sc_kbcslot,
    504       1.1  christos 	    SYNAPTICS_IDENTIFY_TOUCHPAD);
    505       1.1  christos 	cmd[0] = PMS_SEND_DEV_STATUS;
    506       1.3       scw 	res |= pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd, 1, 3,
    507       1.3       scw 	    resp, 0);
    508       1.1  christos 	if (res) {
    509      1.26    cegger 		aprint_debug_dev(psc->sc_dev,
    510      1.20      cube 		    "synaptics_probe: Identify Touchpad error.\n");
    511       1.3       scw 		/*
    512       1.3       scw 		 * Reset device in case the probe confused it.
    513       1.3       scw 		 */
    514       1.3       scw  doreset:
    515      1.35     ryoon 		(void)synaptics_poll_reset(psc);
    516      1.35     ryoon 		return res;
    517       1.1  christos 	}
    518       1.1  christos 
    519       1.1  christos 	if (resp[1] != SYNAPTICS_MAGIC_BYTE) {
    520      1.26    cegger 		aprint_debug_dev(psc->sc_dev,
    521      1.20      cube 		    "synaptics_probe: Not synaptics.\n");
    522       1.3       scw 		res = 1;
    523       1.3       scw 		goto doreset;
    524       1.1  christos 	}
    525       1.1  christos 
    526       1.3       scw 	sc->flags = 0;
    527      1.77     blymn 	sc->num_buttons = 0;
    528      1.77     blymn 	sc->button_mask = 0xff;
    529      1.77     blymn 
    530  1.81.4.1    martin 	synaptics_true_edge_right = synaptics_edge_right;
    531  1.81.4.1    martin 	synaptics_true_edge_bottom = synaptics_edge_bottom;
    532       1.3       scw 
    533       1.1  christos 	/* Check for minimum version and print a nice message. */
    534       1.1  christos 	ver_major = resp[2] & 0x0f;
    535       1.1  christos 	ver_minor = resp[0];
    536      1.20      cube 	aprint_normal_dev(psc->sc_dev, "Synaptics touchpad version %d.%d\n",
    537      1.20      cube 	    ver_major, ver_minor);
    538       1.1  christos 	if (ver_major * 10 + ver_minor < SYNAPTICS_MIN_VERSION) {
    539       1.1  christos 		/* No capability query support. */
    540       1.3       scw 		sc->caps = 0;
    541       1.1  christos 		goto done;
    542       1.1  christos 	}
    543       1.1  christos 
    544       1.1  christos 	/* Query the hardware capabilities. */
    545      1.42      maya 	res = synaptics_special_read(psc, SYNAPTICS_READ_CAPABILITIES, resp);
    546       1.1  christos 	if (res) {
    547      1.74    andvar 		/* Hmm, failed to get capabilities. */
    548      1.20      cube 		aprint_error_dev(psc->sc_dev,
    549      1.20      cube 		    "synaptics_probe: Failed to query capabilities.\n");
    550       1.3       scw 		goto doreset;
    551       1.1  christos 	}
    552       1.1  christos 
    553      1.48     blymn 	sc->caps = SYNAPTICS_CAP_VALUE(resp);
    554       1.3       scw 
    555       1.3       scw 	if (sc->caps & SYNAPTICS_CAP_MBUTTON)
    556       1.3       scw 		sc->flags |= SYN_FLAG_HAS_MIDDLE_BUTTON;
    557       1.3       scw 
    558       1.3       scw 	if (sc->caps & SYNAPTICS_CAP_4BUTTON)
    559       1.3       scw 		sc->flags |= SYN_FLAG_HAS_BUTTONS_4_5;
    560       1.3       scw 
    561       1.3       scw 	if (sc->caps & SYNAPTICS_CAP_EXTENDED) {
    562      1.32  christos 		pms_synaptics_probe_extended(psc);
    563       1.3       scw 	}
    564       1.3       scw 
    565       1.3       scw 	if (sc->flags) {
    566       1.3       scw 		const char comma[] = ", ";
    567       1.3       scw 		const char *sep = "";
    568      1.20      cube 		aprint_normal_dev(psc->sc_dev, "");
    569      1.40  christos 		for (size_t f = 0; f < __arraycount(syn_flags); f++) {
    570      1.40  christos 			if (sc->flags & syn_flags[f].bit) {
    571      1.40  christos 				aprint_normal("%s%s", sep, syn_flags[f].desc);
    572      1.40  christos 				sep = comma;
    573      1.40  christos 			}
    574       1.3       scw 		}
    575      1.41  christos 		aprint_normal("\n");
    576       1.1  christos 	}
    577       1.1  christos 
    578      1.76     blymn 	if (sc->flags & SYN_FLAG_HAS_MAX_REPORT) {
    579      1.76     blymn 		res = synaptics_special_read(psc, SYNAPTICS_READ_MAX_COORDS,
    580      1.76     blymn 		    resp);
    581      1.76     blymn 		if (res) {
    582      1.76     blymn 			aprint_error_dev(psc->sc_dev,
    583      1.76     blymn 			    "synaptics_probe: Failed to query max coords.\n");
    584      1.76     blymn 		} else {
    585      1.76     blymn 			synaptics_edge_right = (resp[0] << 5) +
    586      1.76     blymn 			    ((resp[1] & 0x0f) << 1);
    587      1.76     blymn 			synaptics_edge_top = (resp[2] << 5) +
    588      1.76     blymn 			    ((resp[1] & 0xf0) >> 3);
    589      1.76     blymn 
    590  1.81.4.1    martin 			synaptics_true_edge_right = synaptics_edge_right;
    591      1.77     blymn 
    592      1.77     blymn 			/*
    593      1.77     blymn 			 * If we have vertical scroll then steal 10%
    594      1.77     blymn 			 * for that region.
    595      1.77     blymn 			 */
    596      1.77     blymn 			if (sc->flags & SYN_FLAG_HAS_VERTICAL_SCROLL)
    597      1.77     blymn 				synaptics_edge_right -=
    598      1.77     blymn 				    synaptics_edge_right / 10;
    599      1.77     blymn 
    600      1.76     blymn 			aprint_normal_dev(psc->sc_dev,
    601      1.76     blymn 			    "Probed max coordinates right: %d, top: %d\n",
    602      1.76     blymn 			    synaptics_edge_right, synaptics_edge_top);
    603      1.76     blymn 		}
    604      1.76     blymn 	}
    605      1.76     blymn 
    606      1.76     blymn 	if (sc->flags & SYN_FLAG_HAS_MIN_REPORT) {
    607      1.76     blymn 		res = synaptics_special_read(psc, SYNAPTICS_READ_MIN_COORDS,
    608      1.76     blymn 		    resp);
    609      1.76     blymn 		if (res) {
    610      1.76     blymn 			aprint_error_dev(psc->sc_dev,
    611      1.76     blymn 			    "synaptics_probe: Failed to query min coords.\n");
    612      1.76     blymn 		} else {
    613      1.76     blymn 			synaptics_edge_left = (resp[0] << 5) +
    614      1.76     blymn 			    ((resp[1] & 0x0f) << 1);
    615      1.76     blymn 			synaptics_edge_bottom = (resp[2] << 5) +
    616      1.76     blymn 			    ((resp[1] & 0xf0) >> 3);
    617      1.76     blymn 
    618  1.81.4.1    martin 			synaptics_true_edge_bottom = synaptics_edge_bottom;
    619      1.77     blymn 
    620      1.77     blymn 			/*
    621      1.77     blymn 			 * If we have horizontal scroll then steal 10%
    622      1.77     blymn 			 * for that region.
    623      1.77     blymn 			 */
    624      1.77     blymn 			if (sc->flags & SYN_FLAG_HAS_HORIZONTAL_SCROLL)
    625  1.81.4.1    martin 				synaptics_hscroll_pct = 10;
    626      1.77     blymn 
    627      1.76     blymn 			aprint_normal_dev(psc->sc_dev,
    628      1.76     blymn 			    "Probed min coordinates left: %d, bottom: %d\n",
    629      1.76     blymn 			    synaptics_edge_left, synaptics_edge_bottom);
    630      1.76     blymn 		}
    631      1.76     blymn 	}
    632      1.76     blymn 
    633  1.81.4.1    martin done:
    634      1.77     blymn 	pms_synaptics_set_boundaries();
    635      1.77     blymn 
    636       1.2  christos 	pms_sysctl_synaptics(&clog);
    637       1.3       scw 	pckbport_set_inputhandler(psc->sc_kbctag, psc->sc_kbcslot,
    638      1.20      cube 	    pms_synaptics_input, psc, device_xname(psc->sc_dev));
    639       1.3       scw 
    640       1.3       scw 	return (0);
    641       1.1  christos }
    642       1.1  christos 
    643       1.1  christos void
    644       1.1  christos pms_synaptics_enable(void *vsc)
    645       1.1  christos {
    646       1.3       scw 	struct pms_softc *psc = vsc;
    647       1.3       scw 	struct synaptics_softc *sc = &psc->u.synaptics;
    648      1.34     blymn 	u_char enable_modes;
    649      1.44     blymn 	int res, i;
    650       1.1  christos 
    651      1.23    plunky 	if (sc->flags & SYN_FLAG_HAS_PASSTHROUGH) {
    652      1.32  christos 		/*
    653      1.40  christos 		 * Extended capability probes can confuse the passthrough
    654      1.40  christos 		 * device; reset the touchpad now to cure that.
    655      1.23    plunky 		 */
    656      1.34     blymn 		res = synaptics_poll_reset(psc);
    657      1.23    plunky 	}
    658      1.23    plunky 
    659       1.3       scw 	/*
    660       1.3       scw 	 * Enable Absolute mode with W (width) reporting, and set
    661      1.34     blymn 	 * the packet rate to maximum (80 packets per second). Enable
    662      1.34     blymn 	 * extended W mode if supported so we can report second finger
    663      1.34     blymn 	 * position.
    664       1.3       scw 	 */
    665      1.34     blymn 	enable_modes =
    666      1.34     blymn 	   SYNAPTICS_MODE_ABSOLUTE | SYNAPTICS_MODE_W | SYNAPTICS_MODE_RATE;
    667      1.34     blymn 
    668      1.38     ryoon 	if (sc->flags & SYN_FLAG_HAS_EXTENDED_WMODE)
    669      1.34     blymn 		enable_modes |= SYNAPTICS_MODE_EXTENDED_W;
    670      1.34     blymn 
    671      1.34     blymn 	/*
    672      1.34     blymn  	* Synaptics documentation says to disable device before
    673      1.34     blymn  	* setting mode.
    674      1.34     blymn  	*/
    675      1.34     blymn 	synaptics_poll_cmd(psc, PMS_DEV_DISABLE, 0);
    676      1.34     blymn 	/* a couple of set scales to clear out pending commands */
    677      1.44     blymn 	for (i = 0; i < 2; i++)
    678      1.34     blymn 		synaptics_poll_cmd(psc, PMS_SET_SCALE11, 0);
    679      1.34     blymn 
    680      1.42      maya 	res = synaptics_special_write(psc, SYNAPTICS_CMD_SET_MODE2, enable_modes);
    681      1.34     blymn 	if (res)
    682  1.81.4.1    martin 		device_printf(psc->sc_dev, "set mode error\n");
    683      1.34     blymn 
    684      1.34     blymn 	/* a couple of set scales to clear out pending commands */
    685      1.44     blymn 	for (i = 0; i < 2; i++)
    686      1.34     blymn 		synaptics_poll_cmd(psc, PMS_SET_SCALE11, 0);
    687      1.34     blymn 
    688      1.42      maya 	/* Set advanced gesture mode */
    689      1.49     blymn 	if ((sc->flags & SYN_FLAG_HAS_EXTENDED_WMODE) ||
    690      1.49     blymn 	    (sc->flags & SYN_FLAG_HAS_ADV_GESTURE_MODE))
    691      1.42      maya 		synaptics_special_write(psc, SYNAPTICS_WRITE_DELUXE_3, 0x3);
    692      1.39     ryoon 
    693      1.73     blymn 	/* Disable motion in the button region for clickpads */
    694      1.73     blymn 	if(sc->flags & SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD)
    695      1.73     blymn 		synaptics_button_region_movement = 0;
    696      1.73     blymn 
    697       1.3       scw 	sc->up_down = 0;
    698       1.3       scw 	sc->prev_fingers = 0;
    699       1.3       scw 	sc->gesture_start_x = sc->gesture_start_y = 0;
    700       1.3       scw 	sc->gesture_start_packet = 0;
    701       1.3       scw 	sc->gesture_tap_packet = 0;
    702       1.3       scw 	sc->gesture_type = 0;
    703       1.3       scw 	sc->gesture_buttons = 0;
    704      1.73     blymn 	sc->total_packets = 0;
    705      1.44     blymn 	for (i = 0; i < SYN_MAX_FINGERS; i++) {
    706      1.44     blymn 		sc->rem_x[i] = sc->rem_y[i] = sc->rem_z[i] = 0;
    707      1.44     blymn 	}
    708      1.34     blymn 	sc->button_history = 0;
    709      1.73     blymn 
    710      1.73     blymn 	/* clear the packet decode structure */
    711      1.73     blymn 	memset(&packet, 0, sizeof(packet));
    712       1.1  christos }
    713       1.1  christos 
    714       1.1  christos void
    715       1.1  christos pms_synaptics_resume(void *vsc)
    716       1.1  christos {
    717      1.34     blymn 	(void)synaptics_poll_reset(vsc);
    718       1.1  christos }
    719       1.1  christos 
    720       1.3       scw static void
    721       1.3       scw pms_sysctl_synaptics(struct sysctllog **clog)
    722       1.1  christos {
    723       1.1  christos 	int rc, root_num;
    724       1.6    atatat 	const struct sysctlnode *node;
    725       1.1  christos 
    726       1.1  christos 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    727       1.1  christos 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "synaptics",
    728       1.1  christos 	    SYSCTL_DESCR("Synaptics touchpad controls"),
    729       1.1  christos 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0)
    730       1.1  christos 	    goto err;
    731       1.1  christos 
    732       1.1  christos 	root_num = node->sysctl_num;
    733       1.1  christos 
    734       1.1  christos 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    735       1.1  christos 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    736       1.3       scw 	    CTLTYPE_INT, "up_down_emulation",
    737       1.3       scw 	    SYSCTL_DESCR("Middle button/Z-axis emulation with up/down buttons"),
    738       1.3       scw 	    pms_sysctl_synaptics_verify, 0,
    739       1.3       scw 	    &synaptics_up_down_emul,
    740       1.3       scw 	    0, CTL_HW, root_num, CTL_CREATE,
    741       1.3       scw 	    CTL_EOL)) != 0)
    742       1.3       scw 		goto err;
    743       1.3       scw 
    744       1.3       scw 	synaptics_up_down_emul_nodenum = node->sysctl_num;
    745       1.3       scw 
    746       1.3       scw 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    747       1.3       scw 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    748       1.3       scw 	    CTLTYPE_INT, "up_down_motion_delta",
    749       1.3       scw 	    SYSCTL_DESCR("Up/down button Z-axis emulation rate"),
    750       1.3       scw 	    pms_sysctl_synaptics_verify, 0,
    751       1.3       scw 	    &synaptics_up_down_motion_delta,
    752       1.3       scw 	    0, CTL_HW, root_num, CTL_CREATE,
    753       1.3       scw 	    CTL_EOL)) != 0)
    754       1.3       scw 		goto err;
    755       1.3       scw 
    756       1.3       scw 	synaptics_up_down_motion_delta_nodenum = node->sysctl_num;
    757       1.3       scw 
    758       1.3       scw 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    759       1.3       scw 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    760       1.3       scw 	    CTLTYPE_INT, "gesture_move",
    761       1.3       scw 	    SYSCTL_DESCR("Movement greater than this between taps cancels gesture"),
    762       1.3       scw 	    pms_sysctl_synaptics_verify, 0,
    763       1.3       scw 	    &synaptics_gesture_move,
    764       1.3       scw 	    0, CTL_HW, root_num, CTL_CREATE,
    765       1.3       scw 	    CTL_EOL)) != 0)
    766       1.3       scw 		goto err;
    767       1.3       scw 
    768       1.3       scw 	synaptics_gesture_move_nodenum = node->sysctl_num;
    769       1.3       scw 
    770       1.3       scw 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    771       1.3       scw 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    772       1.3       scw 	    CTLTYPE_INT, "gesture_length",
    773       1.3       scw 	    SYSCTL_DESCR("Time period in which tap is recognised as a gesture"),
    774       1.3       scw 	    pms_sysctl_synaptics_verify, 0,
    775       1.3       scw 	    &synaptics_gesture_length,
    776       1.3       scw 	    0, CTL_HW, root_num, CTL_CREATE,
    777       1.3       scw 	    CTL_EOL)) != 0)
    778       1.3       scw 		goto err;
    779       1.3       scw 
    780       1.3       scw 	synaptics_gesture_length_nodenum = node->sysctl_num;
    781       1.3       scw 
    782       1.3       scw 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    783       1.3       scw 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    784       1.3       scw 	    CTLTYPE_INT, "edge_left",
    785       1.3       scw 	    SYSCTL_DESCR("Define left edge of touchpad"),
    786       1.3       scw 	    pms_sysctl_synaptics_verify, 0,
    787       1.3       scw 	    &synaptics_edge_left,
    788       1.3       scw 	    0, CTL_HW, root_num, CTL_CREATE,
    789       1.3       scw 	    CTL_EOL)) != 0)
    790       1.3       scw 		goto err;
    791       1.3       scw 
    792       1.3       scw 	synaptics_edge_left_nodenum = node->sysctl_num;
    793       1.3       scw 
    794       1.3       scw 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    795       1.3       scw 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    796       1.3       scw 	    CTLTYPE_INT, "edge_right",
    797       1.3       scw 	    SYSCTL_DESCR("Define right edge of touchpad"),
    798       1.1  christos 	    pms_sysctl_synaptics_verify, 0,
    799       1.3       scw 	    &synaptics_edge_right,
    800       1.1  christos 	    0, CTL_HW, root_num, CTL_CREATE,
    801       1.1  christos 	    CTL_EOL)) != 0)
    802       1.1  christos 		goto err;
    803       1.1  christos 
    804       1.3       scw 	synaptics_edge_right_nodenum = node->sysctl_num;
    805       1.1  christos 
    806       1.1  christos 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    807       1.1  christos 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    808       1.3       scw 	    CTLTYPE_INT, "edge_top",
    809       1.3       scw 	    SYSCTL_DESCR("Define top edge of touchpad"),
    810       1.1  christos 	    pms_sysctl_synaptics_verify, 0,
    811       1.3       scw 	    &synaptics_edge_top,
    812       1.1  christos 	    0, CTL_HW, root_num, CTL_CREATE,
    813       1.1  christos 	    CTL_EOL)) != 0)
    814       1.3       scw 		goto err;
    815       1.3       scw 
    816       1.3       scw 	synaptics_edge_top_nodenum = node->sysctl_num;
    817       1.3       scw 
    818       1.3       scw 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    819       1.3       scw 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    820       1.3       scw 	    CTLTYPE_INT, "edge_bottom",
    821       1.3       scw 	    SYSCTL_DESCR("Define bottom edge of touchpad"),
    822       1.3       scw 	    pms_sysctl_synaptics_verify, 0,
    823       1.3       scw 	    &synaptics_edge_bottom,
    824       1.3       scw 	    0, CTL_HW, root_num, CTL_CREATE,
    825       1.3       scw 	    CTL_EOL)) != 0)
    826       1.3       scw 		goto err;
    827       1.3       scw 
    828       1.3       scw 	synaptics_edge_bottom_nodenum = node->sysctl_num;
    829       1.3       scw 
    830       1.3       scw 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    831       1.3       scw 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    832       1.3       scw 	    CTLTYPE_INT, "edge_motion_delta",
    833       1.3       scw 	    SYSCTL_DESCR("Define edge motion rate"),
    834       1.3       scw 	    pms_sysctl_synaptics_verify, 0,
    835       1.3       scw 	    &synaptics_edge_motion_delta,
    836       1.3       scw 	    0, CTL_HW, root_num, CTL_CREATE,
    837       1.3       scw 	    CTL_EOL)) != 0)
    838       1.3       scw 		goto err;
    839       1.3       scw 
    840       1.3       scw 	synaptics_edge_motion_delta_nodenum = node->sysctl_num;
    841       1.3       scw 
    842       1.3       scw 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    843       1.3       scw 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    844       1.3       scw 	    CTLTYPE_INT, "finger_high",
    845       1.3       scw 	    SYSCTL_DESCR("Define finger applied pressure threshold"),
    846       1.3       scw 	    pms_sysctl_synaptics_verify, 0,
    847       1.3       scw 	    &synaptics_finger_high,
    848       1.3       scw 	    0, CTL_HW, root_num, CTL_CREATE,
    849       1.3       scw 	    CTL_EOL)) != 0)
    850       1.3       scw 		goto err;
    851       1.3       scw 
    852       1.3       scw 	synaptics_finger_high_nodenum = node->sysctl_num;
    853       1.3       scw 
    854       1.3       scw 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    855       1.3       scw 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    856       1.3       scw 	    CTLTYPE_INT, "finger_low",
    857       1.3       scw 	    SYSCTL_DESCR("Define finger removed pressure threshold"),
    858       1.3       scw 	    pms_sysctl_synaptics_verify, 0,
    859       1.3       scw 	    &synaptics_finger_low,
    860       1.3       scw 	    0, CTL_HW, root_num, CTL_CREATE,
    861       1.3       scw 	    CTL_EOL)) != 0)
    862       1.3       scw 		goto err;
    863       1.3       scw 
    864       1.3       scw 	synaptics_finger_low_nodenum = node->sysctl_num;
    865       1.3       scw 
    866       1.3       scw 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    867       1.3       scw 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    868       1.3       scw 	    CTLTYPE_INT, "two_fingers_emulation",
    869       1.3       scw 	    SYSCTL_DESCR("Map two fingers to middle button"),
    870       1.3       scw 	    pms_sysctl_synaptics_verify, 0,
    871       1.3       scw 	    &synaptics_two_fingers_emul,
    872       1.3       scw 	    0, CTL_HW, root_num, CTL_CREATE,
    873       1.3       scw 	    CTL_EOL)) != 0)
    874       1.3       scw 		goto err;
    875       1.3       scw 
    876       1.3       scw 	synaptics_two_fingers_emul_nodenum = node->sysctl_num;
    877       1.3       scw 
    878       1.3       scw 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    879       1.3       scw 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    880       1.3       scw 	    CTLTYPE_INT, "scale_x",
    881       1.3       scw 	    SYSCTL_DESCR("Horizontal movement scale factor"),
    882       1.3       scw 	    pms_sysctl_synaptics_verify, 0,
    883      1.30       dsl 	    &synaptics_scale_x,
    884       1.3       scw 	    0, CTL_HW, root_num, CTL_CREATE,
    885       1.3       scw 	    CTL_EOL)) != 0)
    886       1.3       scw 		goto err;
    887       1.3       scw 
    888       1.3       scw 	synaptics_scale_x_nodenum = node->sysctl_num;
    889       1.3       scw 
    890       1.3       scw 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    891       1.3       scw 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    892       1.3       scw 	    CTLTYPE_INT, "scale_y",
    893       1.3       scw 	    SYSCTL_DESCR("Vertical movement scale factor"),
    894       1.3       scw 	    pms_sysctl_synaptics_verify, 0,
    895      1.30       dsl 	    &synaptics_scale_y,
    896       1.3       scw 	    0, CTL_HW, root_num, CTL_CREATE,
    897       1.3       scw 	    CTL_EOL)) != 0)
    898       1.3       scw 		goto err;
    899       1.1  christos 
    900       1.3       scw 	synaptics_scale_y_nodenum = node->sysctl_num;
    901       1.1  christos 
    902       1.1  christos 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    903       1.1  christos 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    904      1.44     blymn 	    CTLTYPE_INT, "scale_z",
    905      1.44     blymn 	    SYSCTL_DESCR("Sroll wheel emulation scale factor"),
    906      1.44     blymn 	    pms_sysctl_synaptics_verify, 0,
    907      1.44     blymn 	    &synaptics_scale_z,
    908      1.44     blymn 	    0, CTL_HW, root_num, CTL_CREATE,
    909      1.44     blymn 	    CTL_EOL)) != 0)
    910      1.44     blymn 		goto err;
    911      1.44     blymn 
    912      1.44     blymn 	synaptics_scale_z_nodenum = node->sysctl_num;
    913      1.44     blymn 
    914      1.44     blymn 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    915      1.44     blymn 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    916       1.3       scw 	    CTLTYPE_INT, "max_speed_x",
    917       1.3       scw 	    SYSCTL_DESCR("Horizontal movement maximum speed"),
    918       1.1  christos 	    pms_sysctl_synaptics_verify, 0,
    919       1.3       scw 	    &synaptics_max_speed_x,
    920       1.1  christos 	    0, CTL_HW, root_num, CTL_CREATE,
    921       1.1  christos 	    CTL_EOL)) != 0)
    922       1.1  christos 		goto err;
    923       1.1  christos 
    924       1.3       scw 	synaptics_max_speed_x_nodenum = node->sysctl_num;
    925       1.1  christos 
    926       1.1  christos 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    927       1.1  christos 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    928       1.3       scw 	    CTLTYPE_INT, "max_speed_y",
    929       1.3       scw 	    SYSCTL_DESCR("Vertical movement maximum speed"),
    930       1.1  christos 	    pms_sysctl_synaptics_verify, 0,
    931       1.3       scw 	    &synaptics_max_speed_y,
    932       1.1  christos 	    0, CTL_HW, root_num, CTL_CREATE,
    933       1.1  christos 	    CTL_EOL)) != 0)
    934       1.1  christos 		goto err;
    935       1.1  christos 
    936       1.3       scw 	synaptics_max_speed_y_nodenum = node->sysctl_num;
    937       1.1  christos 
    938       1.1  christos 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    939       1.1  christos 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    940      1.44     blymn 	    CTLTYPE_INT, "max_speed_z",
    941      1.44     blymn 	    SYSCTL_DESCR("Scroll wheel emulation maximum speed"),
    942      1.44     blymn 	    pms_sysctl_synaptics_verify, 0,
    943      1.44     blymn 	    &synaptics_max_speed_z,
    944      1.44     blymn 	    0, CTL_HW, root_num, CTL_CREATE,
    945      1.44     blymn 	    CTL_EOL)) != 0)
    946      1.44     blymn 		goto err;
    947      1.44     blymn 
    948      1.44     blymn 	synaptics_max_speed_z_nodenum = node->sysctl_num;
    949      1.44     blymn 
    950      1.44     blymn 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    951      1.44     blymn 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    952       1.3       scw 	    CTLTYPE_INT, "movement_threshold",
    953       1.3       scw 	    SYSCTL_DESCR("Minimum reported movement threshold"),
    954       1.1  christos 	    pms_sysctl_synaptics_verify, 0,
    955       1.3       scw 	    &synaptics_movement_threshold,
    956       1.1  christos 	    0, CTL_HW, root_num, CTL_CREATE,
    957       1.1  christos 	    CTL_EOL)) != 0)
    958       1.1  christos 		goto err;
    959       1.1  christos 
    960       1.3       scw 	synaptics_movement_threshold_nodenum = node->sysctl_num;
    961      1.34     blymn 
    962      1.34     blymn 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    963      1.34     blymn 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    964      1.36  jmcneill 	    CTLTYPE_INT, "movement_enable",
    965      1.36  jmcneill 	    SYSCTL_DESCR("Enable movement reporting"),
    966      1.36  jmcneill 	    pms_sysctl_synaptics_verify, 0,
    967      1.36  jmcneill 	    &synaptics_movement_enable,
    968      1.36  jmcneill 	    0, CTL_HW, root_num, CTL_CREATE,
    969      1.36  jmcneill 	    CTL_EOL)) != 0)
    970      1.36  jmcneill 		goto err;
    971      1.36  jmcneill 
    972      1.36  jmcneill 	synaptics_movement_enable_nodenum = node->sysctl_num;
    973      1.36  jmcneill 
    974      1.36  jmcneill 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    975      1.36  jmcneill 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    976      1.73     blymn 	    CTLTYPE_INT, "button_region_movement_enable",
    977      1.73     blymn 	    SYSCTL_DESCR("Enable movement within clickpad button region"),
    978      1.73     blymn 	    pms_sysctl_synaptics_verify, 0,
    979      1.73     blymn 	    &synaptics_button_region_movement,
    980      1.73     blymn 	    0, CTL_HW, root_num, CTL_CREATE,
    981      1.73     blymn 	    CTL_EOL)) != 0)
    982      1.73     blymn 		goto err;
    983      1.73     blymn 
    984      1.73     blymn 	synaptics_button_region_movement_nodenum = node->sysctl_num;
    985      1.73     blymn 
    986      1.73     blymn 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    987      1.73     blymn 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    988      1.34     blymn 	    CTLTYPE_INT, "button_boundary",
    989      1.34     blymn 	    SYSCTL_DESCR("Top edge of button area"),
    990      1.34     blymn 	    pms_sysctl_synaptics_verify, 0,
    991      1.34     blymn 	    &synaptics_button_boundary,
    992      1.34     blymn 	    0, CTL_HW, root_num, CTL_CREATE,
    993      1.34     blymn 	    CTL_EOL)) != 0)
    994      1.34     blymn 		goto err;
    995      1.34     blymn 
    996      1.34     blymn 	synaptics_button_boundary_nodenum = node->sysctl_num;
    997      1.34     blymn 
    998      1.34     blymn 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    999      1.34     blymn 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
   1000      1.34     blymn 	    CTLTYPE_INT, "button2_edge",
   1001      1.34     blymn 	    SYSCTL_DESCR("Left edge of button 2 region"),
   1002      1.34     blymn 	    pms_sysctl_synaptics_verify, 0,
   1003      1.34     blymn 	    &synaptics_button2,
   1004      1.34     blymn 	    0, CTL_HW, root_num, CTL_CREATE,
   1005      1.34     blymn 	    CTL_EOL)) != 0)
   1006      1.34     blymn 		goto err;
   1007      1.34     blymn 
   1008      1.34     blymn 	synaptics_button2_nodenum = node->sysctl_num;
   1009      1.34     blymn 
   1010      1.34     blymn 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
   1011      1.34     blymn 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
   1012      1.34     blymn 	    CTLTYPE_INT, "button3_edge",
   1013      1.34     blymn 	    SYSCTL_DESCR("Left edge of button 3 region"),
   1014      1.34     blymn 	    pms_sysctl_synaptics_verify, 0,
   1015      1.34     blymn 	    &synaptics_button3,
   1016      1.34     blymn 	    0, CTL_HW, root_num, CTL_CREATE,
   1017      1.34     blymn 	    CTL_EOL)) != 0)
   1018      1.34     blymn 		goto err;
   1019      1.34     blymn 
   1020      1.34     blymn 	synaptics_button3_nodenum = node->sysctl_num;
   1021      1.44     blymn 
   1022      1.44     blymn 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
   1023      1.44     blymn 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
   1024      1.70       nia 	    CTLTYPE_BOOL, "aux_mid_button_scroll",
   1025      1.79    andvar 	    SYSCTL_DESCR("Interpret Y-Axis movement with the middle button held as scrolling on the passthrough device (e.g. TrackPoint)"),
   1026      1.70       nia 	    pms_sysctl_synaptics_verify, 0,
   1027      1.70       nia 	    &synaptics_aux_mid_button_scroll,
   1028      1.70       nia 	    0, CTL_HW, root_num, CTL_CREATE,
   1029      1.70       nia 	    CTL_EOL)) != 0)
   1030      1.70       nia 		goto err;
   1031      1.70       nia 
   1032      1.70       nia 	synaptics_aux_mid_button_scroll_nodenum = node->sysctl_num;
   1033      1.71  riastrad 
   1034      1.71  riastrad 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
   1035      1.71  riastrad 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
   1036      1.77     blymn 	    CTLTYPE_INT, "vert_scroll_percent",
   1037      1.77     blymn 	    SYSCTL_DESCR("Percent of trackpad width to reserve for vertical scroll region"),
   1038      1.77     blymn 	    pms_sysctl_synaptics_verify, 0,
   1039  1.81.4.1    martin 	    &synaptics_vscroll_pct,
   1040      1.77     blymn 	    0, CTL_HW, root_num, CTL_CREATE,
   1041      1.77     blymn 	    CTL_EOL)) != 0)
   1042      1.77     blymn 		goto err;
   1043      1.77     blymn 
   1044  1.81.4.1    martin 	synaptics_vscroll_pct_nodenum = node->sysctl_num;
   1045      1.77     blymn 
   1046      1.77     blymn 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
   1047      1.77     blymn 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
   1048      1.77     blymn 	    CTLTYPE_INT, "horizontal_scroll_percent",
   1049      1.77     blymn 	    SYSCTL_DESCR("Percent of trackpad height to reserve for scroll region"),
   1050      1.77     blymn 	    pms_sysctl_synaptics_verify, 0,
   1051  1.81.4.1    martin 	    &synaptics_hscroll_pct,
   1052      1.77     blymn 	    0, CTL_HW, root_num, CTL_CREATE,
   1053      1.77     blymn 	    CTL_EOL)) != 0)
   1054      1.77     blymn 		goto err;
   1055      1.77     blymn 
   1056  1.81.4.1    martin 	synaptics_hscroll_pct_nodenum = node->sysctl_num;
   1057      1.77     blymn 
   1058      1.77     blymn 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
   1059      1.77     blymn 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
   1060      1.77     blymn 	    CTLTYPE_INT, "button_region_percent",
   1061      1.77     blymn 	    SYSCTL_DESCR("Percent of trackpad height to reserve for button region"),
   1062      1.77     blymn 	    pms_sysctl_synaptics_verify, 0,
   1063      1.77     blymn 	    &synaptics_button_pct,
   1064      1.77     blymn 	    0, CTL_HW, root_num, CTL_CREATE,
   1065      1.77     blymn 	    CTL_EOL)) != 0)
   1066      1.77     blymn 		goto err;
   1067      1.77     blymn 
   1068      1.77     blymn 	synaptics_button_pct_nodenum = node->sysctl_num;
   1069      1.77     blymn 
   1070      1.77     blymn 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
   1071      1.77     blymn 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
   1072      1.71  riastrad 	    CTLTYPE_INT, "debug",
   1073      1.71  riastrad 	    SYSCTL_DESCR("Enable debug output"),
   1074      1.71  riastrad 	    NULL, 0,
   1075      1.71  riastrad 	    &synaptics_debug,
   1076      1.71  riastrad 	    0, CTL_HW, root_num, CTL_CREATE,
   1077      1.71  riastrad 	    CTL_EOL)) != 0)
   1078      1.71  riastrad 		goto err;
   1079      1.71  riastrad 
   1080       1.1  christos 	return;
   1081       1.1  christos 
   1082       1.1  christos err:
   1083       1.3       scw 	aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
   1084       1.1  christos }
   1085       1.1  christos 
   1086       1.1  christos static int
   1087       1.1  christos pms_sysctl_synaptics_verify(SYSCTLFN_ARGS)
   1088       1.1  christos {
   1089       1.1  christos 	int error, t;
   1090       1.1  christos 	struct sysctlnode node;
   1091       1.1  christos 
   1092       1.1  christos 	node = *rnode;
   1093       1.1  christos 	t = *(int *)rnode->sysctl_data;
   1094       1.1  christos 	node.sysctl_data = &t;
   1095       1.1  christos 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
   1096       1.1  christos 	if (error || newp == NULL)
   1097       1.1  christos 		return error;
   1098       1.1  christos 
   1099       1.1  christos 	/* Sanity check the params. */
   1100      1.55       nia 	if (node.sysctl_num == synaptics_up_down_emul_nodenum) {
   1101      1.55       nia 		if (t < 0 || t > 3)
   1102      1.55       nia 			return (EINVAL);
   1103      1.55       nia 	} else
   1104      1.55       nia 	if (node.sysctl_num == synaptics_two_fingers_emul_nodenum) {
   1105       1.3       scw 		if (t < 0 || t > 2)
   1106       1.3       scw 			return (EINVAL);
   1107       1.3       scw 	} else
   1108       1.3       scw 	if (node.sysctl_num == synaptics_gesture_length_nodenum ||
   1109       1.3       scw 	    node.sysctl_num == synaptics_edge_motion_delta_nodenum ||
   1110      1.58       nia 	    node.sysctl_num == synaptics_up_down_motion_delta_nodenum ||
   1111      1.57       nia 	    node.sysctl_num == synaptics_max_speed_x_nodenum ||
   1112      1.57       nia 	    node.sysctl_num == synaptics_max_speed_y_nodenum ||
   1113      1.57       nia 	    node.sysctl_num == synaptics_max_speed_z_nodenum) {
   1114       1.1  christos 		if (t < 0)
   1115       1.3       scw 			return (EINVAL);
   1116       1.3       scw 	} else
   1117       1.3       scw 	if (node.sysctl_num == synaptics_edge_left_nodenum ||
   1118       1.3       scw 	    node.sysctl_num == synaptics_edge_bottom_nodenum) {
   1119       1.3       scw 		if (t < 0 || t > (SYNAPTICS_EDGE_MAX / 2))
   1120       1.3       scw 			return (EINVAL);
   1121       1.3       scw 	} else
   1122       1.3       scw 	if (node.sysctl_num == synaptics_edge_right_nodenum ||
   1123       1.3       scw 	    node.sysctl_num == synaptics_edge_top_nodenum) {
   1124       1.3       scw 		if (t < (SYNAPTICS_EDGE_MAX / 2))
   1125       1.3       scw 			return (EINVAL);
   1126       1.1  christos 	} else
   1127       1.3       scw 	if (node.sysctl_num == synaptics_scale_x_nodenum ||
   1128      1.46     blymn 	    node.sysctl_num == synaptics_scale_y_nodenum ||
   1129      1.46     blymn 	    node.sysctl_num == synaptics_scale_z_nodenum) {
   1130       1.3       scw 		if (t < 1 || t > (SYNAPTICS_EDGE_MAX / 4))
   1131       1.3       scw 			return (EINVAL);
   1132       1.3       scw 	} else
   1133       1.3       scw 	if (node.sysctl_num == synaptics_finger_high_nodenum) {
   1134       1.3       scw 		if (t < 0 || t > SYNAPTICS_FINGER_PALM ||
   1135       1.3       scw 		    t < synaptics_finger_low)
   1136       1.3       scw 			return (EINVAL);
   1137       1.3       scw 	} else
   1138       1.3       scw 	if (node.sysctl_num == synaptics_finger_low_nodenum) {
   1139       1.3       scw 		if (t < 0 || t > SYNAPTICS_FINGER_PALM ||
   1140       1.3       scw 		    t > synaptics_finger_high)
   1141       1.3       scw 			return (EINVAL);
   1142       1.3       scw 	} else
   1143       1.3       scw 	if (node.sysctl_num == synaptics_gesture_move_nodenum ||
   1144       1.3       scw 	    node.sysctl_num == synaptics_movement_threshold_nodenum) {
   1145       1.3       scw 		if (t < 0 || t > (SYNAPTICS_EDGE_MAX / 4))
   1146       1.3       scw 			return (EINVAL);
   1147       1.3       scw 	} else
   1148      1.36  jmcneill 	if (node.sysctl_num == synaptics_button_boundary_nodenum) {
   1149      1.77     blymn 		if (t < 0 || t < synaptics_edge_bottom ||
   1150      1.77     blymn 		    t > synaptics_edge_top)
   1151      1.34     blymn 			return (EINVAL);
   1152      1.34     blymn 	} else
   1153      1.36  jmcneill 	if (node.sysctl_num == synaptics_button2_nodenum ||
   1154      1.36  jmcneill 	    node.sysctl_num == synaptics_button3_nodenum) {
   1155      1.77     blymn 		if (t < synaptics_edge_left || t > synaptics_edge_right)
   1156      1.34     blymn 			return (EINVAL);
   1157      1.34     blymn 	} else
   1158      1.36  jmcneill 	if (node.sysctl_num == synaptics_movement_enable_nodenum) {
   1159      1.36  jmcneill 		if (t < 0 || t > 1)
   1160      1.36  jmcneill 			return (EINVAL);
   1161      1.36  jmcneill 	} else
   1162      1.73     blymn 	if (node.sysctl_num == synaptics_button_region_movement_nodenum) {
   1163      1.73     blymn 		if (t < 0 || t > 1)
   1164      1.73     blymn 			return (EINVAL);
   1165      1.73     blymn 	} else
   1166      1.70       nia 	if (node.sysctl_num == synaptics_aux_mid_button_scroll_nodenum) {
   1167      1.70       nia 		if (t < 0 || t > 1)
   1168      1.70       nia 			return (EINVAL);
   1169      1.70       nia 	} else
   1170  1.81.4.1    martin 	if (node.sysctl_num == synaptics_vscroll_pct_nodenum) {
   1171      1.77     blymn 		if (t < 0 || t > 100)
   1172      1.77     blymn 			return (EINVAL);
   1173      1.77     blymn 	} else
   1174  1.81.4.1    martin 	if (node.sysctl_num == synaptics_hscroll_pct_nodenum) {
   1175      1.77     blymn 		if (t < 0 || t > 100)
   1176      1.77     blymn 			return (EINVAL);
   1177      1.77     blymn 	} else
   1178      1.77     blymn 	if (node.sysctl_num == synaptics_button_pct_nodenum) {
   1179      1.77     blymn 		if (t < 0 || t > 100)
   1180      1.77     blymn 			return (EINVAL);
   1181      1.77     blymn 	} else
   1182       1.3       scw 		return (EINVAL);
   1183       1.1  christos 
   1184       1.3       scw 	*(int *)rnode->sysctl_data = t;
   1185       1.1  christos 
   1186      1.77     blymn 	pms_synaptics_set_boundaries();
   1187      1.77     blymn 
   1188       1.3       scw 	return (0);
   1189       1.1  christos }
   1190       1.1  christos 
   1191      1.73     blymn /*
   1192      1.73     blymn  * Extract the number of fingers from the current packet and return
   1193      1.73     blymn  * it to the caller.
   1194      1.73     blymn  */
   1195      1.73     blymn static unsigned
   1196      1.73     blymn pms_synaptics_get_fingers(struct pms_softc *psc, u_char w, short z)
   1197      1.73     blymn {
   1198      1.73     blymn 	struct synaptics_softc *sc = &psc->u.synaptics;
   1199      1.73     blymn 	unsigned short ew_mode;
   1200      1.73     blymn 	unsigned fingers;
   1201      1.73     blymn 
   1202      1.73     blymn 	fingers = 0;
   1203      1.73     blymn 
   1204      1.73     blymn 
   1205      1.73     blymn 	/*
   1206      1.73     blymn 	 * If w is zero and z says no fingers then return
   1207      1.73     blymn 	 * no fingers, w == can also mean 2 fingers... confusing.
   1208      1.73     blymn 	 */
   1209      1.73     blymn 	if (w == 0 && z == SYNAPTICS_FINGER_NONE)
   1210      1.73     blymn 		return 0;
   1211      1.73     blymn 
   1212      1.73     blymn 	if ((sc->flags & SYN_FLAG_HAS_EXTENDED_WMODE) &&
   1213      1.73     blymn 	    (w == SYNAPTICS_WIDTH_EXTENDED_W)) {
   1214      1.73     blymn 		ew_mode = psc->packet[5] >> 4;
   1215      1.73     blymn 		switch (ew_mode)
   1216      1.73     blymn 		{
   1217      1.73     blymn 		case SYNAPTICS_EW_WHEEL:
   1218      1.73     blymn 			break;
   1219      1.73     blymn 
   1220      1.73     blymn 		case SYNAPTICS_EW_SECONDARY_FINGER:
   1221      1.73     blymn 			/* to get here we must have 2 fingers at least */
   1222      1.73     blymn 			fingers = 2;
   1223      1.73     blymn 			break;
   1224      1.73     blymn 
   1225      1.73     blymn 		case SYNAPTICS_EW_FINGER_STATUS:
   1226      1.73     blymn 			fingers = psc->packet[1] & 0x0f;
   1227      1.73     blymn 			break;
   1228      1.73     blymn 
   1229      1.73     blymn 		default:
   1230  1.81.4.1    martin 			device_printf(psc->sc_dev,
   1231      1.73     blymn 			    "invalid extended w mode %d\n",
   1232      1.73     blymn 			    ew_mode);
   1233      1.73     blymn 			return 0; /* pretend there are no fingers */
   1234      1.73     blymn 		}
   1235      1.73     blymn 	} else {
   1236      1.73     blymn 
   1237      1.73     blymn 		fingers = 1;
   1238      1.73     blymn 
   1239      1.73     blymn 		/*
   1240      1.73     blymn 		 * If SYN_FLAG_HAS_MULTI_FINGER is set then check
   1241      1.73     blymn 		 * sp_w is below SYNAPTICS_WIDTH_FINGER_MIN, if it is
   1242      1.73     blymn 		 * then this will be the finger count.
   1243      1.73     blymn 		 *
   1244      1.73     blymn 		 * There are a couple of "special" values otherwise
   1245      1.73     blymn 		 * just punt with one finger, if this really is a palm
   1246      1.73     blymn 		 * then it will be caught later.
   1247      1.73     blymn 		 */
   1248      1.80   mlelstv 		if (sc->flags & (SYN_FLAG_HAS_MULTI_FINGER | SYN_FLAG_HAS_MULTI_FINGER_REPORT)) {
   1249      1.73     blymn 			if (w == SYNAPTICS_WIDTH_TWO_FINGERS)
   1250      1.73     blymn 				fingers = 2;
   1251      1.73     blymn 			else if (w == SYNAPTICS_WIDTH_THREE_OR_MORE)
   1252      1.73     blymn 				fingers = 3;
   1253      1.73     blymn 		}
   1254      1.73     blymn 
   1255      1.73     blymn 	}
   1256      1.73     blymn 
   1257      1.73     blymn 	return fingers;
   1258      1.73     blymn }
   1259      1.73     blymn 
   1260       1.3       scw /* Masks for the first byte of a packet */
   1261       1.3       scw #define PMS_LBUTMASK 0x01
   1262       1.3       scw #define PMS_RBUTMASK 0x02
   1263       1.3       scw #define PMS_MBUTMASK 0x04
   1264       1.1  christos 
   1265       1.1  christos static void
   1266      1.14   mlelstv pms_synaptics_parse(struct pms_softc *psc)
   1267      1.14   mlelstv {
   1268      1.14   mlelstv 	struct synaptics_softc *sc = &psc->u.synaptics;
   1269      1.73     blymn 	struct synaptics_packet nsp;
   1270      1.34     blymn 	char new_buttons, ew_mode;
   1271      1.77     blymn 	uint8_t btn_mask, packet4, packet5;
   1272      1.73     blymn 	unsigned v, primary_finger, secondary_finger;
   1273      1.75       nia 	int ext_left = -1, ext_right = -1, ext_middle = -1,
   1274      1.75       nia 	    ext_up = -1, ext_down = -1;
   1275      1.14   mlelstv 
   1276      1.73     blymn 	sc->total_packets++;
   1277      1.73     blymn 
   1278      1.73     blymn 	memcpy(&nsp, &packet, sizeof(packet));
   1279      1.32  christos 
   1280      1.14   mlelstv 	/* Width of finger */
   1281      1.73     blymn 	nsp.sp_w = ((psc->packet[0] & 0x30) >> 2)
   1282      1.73     blymn 	    + ((psc->packet[0] & 0x04) >> 1)
   1283      1.73     blymn 	    + ((psc->packet[3] & 0x04) >> 2);
   1284      1.73     blymn 
   1285      1.73     blymn 	v = 0;
   1286      1.73     blymn 	primary_finger = 0;
   1287      1.73     blymn 	secondary_finger = 0;
   1288      1.52     ryoon 	if ((sc->flags & SYN_FLAG_HAS_EXTENDED_WMODE) &&
   1289      1.73     blymn 	    (nsp.sp_w == SYNAPTICS_WIDTH_EXTENDED_W)) {
   1290      1.34     blymn 		ew_mode = psc->packet[5] >> 4;
   1291      1.34     blymn 		switch (ew_mode)
   1292      1.34     blymn 		{
   1293      1.34     blymn 		case SYNAPTICS_EW_WHEEL:
   1294      1.34     blymn 			/* scroll wheel report, ignore for now */
   1295  1.81.4.1    martin 			DPRINTF(10, sc, "mouse wheel packet\n");
   1296      1.34     blymn 			return;
   1297      1.14   mlelstv 
   1298      1.34     blymn 		case SYNAPTICS_EW_SECONDARY_FINGER:
   1299      1.34     blymn 			/* parse the second finger report */
   1300      1.51     ryoon 
   1301      1.73     blymn 			nsp.sp_secondary = 1;
   1302      1.73     blymn 
   1303      1.73     blymn 			nsp.sp_sx = ((psc->packet[1] & 0xfe) << 1)
   1304      1.73     blymn 			    + ((psc->packet[4] & 0x0f) << 9);
   1305      1.73     blymn 			nsp.sp_sy = ((psc->packet[2] & 0xfe) << 1)
   1306      1.73     blymn 			    + ((psc->packet[4] & 0xf0) << 5);
   1307      1.73     blymn 			nsp.sp_sz = (psc->packet[3] & 0x30)
   1308      1.73     blymn 			    + ((psc->packet[5] & 0x0e) << 1);
   1309      1.73     blymn 
   1310      1.76     blymn 			/*
   1311      1.76     blymn 			 * Check if the x and y are non-zero that they
   1312      1.76     blymn 			 * are within the bounds of the trackpad
   1313      1.76     blymn 			 * otherwise ignore the packet.
   1314      1.76     blymn 			 */
   1315      1.76     blymn 			if (((nsp.sp_sx != 0) &&
   1316      1.76     blymn 			    ((nsp.sp_sx < synaptics_edge_left) ||
   1317      1.76     blymn 			     (nsp.sp_sx > synaptics_edge_right))) ||
   1318      1.76     blymn 			   ((nsp.sp_sy != 0) &&
   1319      1.76     blymn 			    ((nsp.sp_sy < synaptics_edge_bottom) ||
   1320      1.76     blymn 			     (nsp.sp_sy > synaptics_edge_top)))) {
   1321      1.76     blymn 				sc->gesture_type = 0;
   1322      1.76     blymn 				sc->gesture_buttons = 0;
   1323      1.76     blymn 				sc->total_packets--;
   1324      1.76     blymn 				DPRINTF(20, sc,
   1325      1.76     blymn 				    "synaptics_parse: dropping out of bounds "
   1326      1.76     blymn 				    "packet sp_sx %d sp_sy %d\n",
   1327      1.76     blymn 				    nsp.sp_sx, nsp.sp_sy);
   1328      1.76     blymn 				return;
   1329      1.76     blymn 			}
   1330      1.76     blymn 
   1331      1.73     blymn 			/* work out the virtual finger width */
   1332      1.73     blymn 			v = 8 + (psc->packet[1] & 0x01) +
   1333      1.73     blymn 				((psc->packet[2] & 0x01) << 1) +
   1334      1.73     blymn 				((psc->packet[5] & 0x01) << 2);
   1335      1.34     blymn 
   1336      1.34     blymn 			/* keep same buttons down as primary */
   1337      1.73     blymn 			nsp.sp_left = sc->button_history & PMS_LBUTMASK;
   1338      1.73     blymn 			nsp.sp_middle = sc->button_history & PMS_MBUTMASK;
   1339      1.73     blymn 			nsp.sp_right = sc->button_history & PMS_RBUTMASK;
   1340      1.34     blymn 			break;
   1341      1.34     blymn 
   1342      1.34     blymn 		case SYNAPTICS_EW_FINGER_STATUS:
   1343      1.73     blymn 			/* This works but what is it good for?
   1344      1.73     blymn 			 * it gives us an index of the primary/secondary
   1345      1.73     blymn 			 * fingers but no other packets pass this
   1346      1.73     blymn 			 * index.
   1347      1.73     blymn 			 *
   1348      1.73     blymn 			 * XXX Park this, possibly handle a finger
   1349      1.73     blymn 			 * XXX change if indexes change.
   1350      1.34     blymn 			 */
   1351      1.73     blymn 			primary_finger = psc->packet[2];
   1352      1.73     blymn 			secondary_finger = psc->packet[4];
   1353      1.73     blymn 			nsp.sp_finger_status = 1;
   1354      1.73     blymn 			nsp.sp_finger_count = pms_synaptics_get_fingers(psc,
   1355      1.73     blymn 			    nsp.sp_w, nsp.sp_z);
   1356      1.73     blymn 			goto skip_position;
   1357      1.34     blymn 
   1358      1.34     blymn 		default:
   1359  1.81.4.1    martin 			device_printf(psc->sc_dev,
   1360      1.34     blymn 			    "invalid extended w mode %d\n",
   1361      1.34     blymn 			    ew_mode);
   1362      1.34     blymn 			return;
   1363      1.34     blymn 		}
   1364      1.14   mlelstv 	} else {
   1365      1.73     blymn 		nsp.sp_primary = 1;
   1366      1.34     blymn 
   1367      1.73     blymn 		/*
   1368      1.77     blymn 		 * If the trackpad has external buttons and one of
   1369      1.77     blymn 		 * those buttons is pressed then the lower bits of
   1370      1.77     blymn 		 * x and y are "stolen" for button status. We can tell
   1371      1.77     blymn 		 * this has happened by doing an xor of the two right
   1372      1.77     blymn 		 * button status bits residing in byte 0 and 3, if the
   1373      1.77     blymn 		 * result is non-zero then there is an external button
   1374      1.77     blymn 		 * report and the position bytes need to be masked.
   1375      1.77     blymn 		 */
   1376      1.77     blymn 		btn_mask = 0xff;
   1377      1.77     blymn 		if ((sc->num_buttons > 0) &&
   1378      1.77     blymn 		    ((psc->packet[0] & PMS_RBUTMASK) ^
   1379      1.77     blymn 		     (psc->packet[3] & PMS_RBUTMASK))) {
   1380      1.77     blymn 			btn_mask = sc->button_mask;
   1381      1.77     blymn 		}
   1382      1.77     blymn 
   1383      1.77     blymn 		packet4 = psc->packet[4] & btn_mask;
   1384      1.77     blymn 		packet5 = psc->packet[5] & btn_mask;
   1385      1.77     blymn 
   1386      1.77     blymn 		/*
   1387      1.73     blymn 		 * If SYN_FLAG_HAS_MULTI_FINGER is set then check
   1388      1.73     blymn 		 * sp_w is below SYNAPTICS_WIDTH_FINGER_MIN, if it is
   1389      1.73     blymn 		 * then this will be the finger count.
   1390      1.73     blymn 		 *
   1391      1.73     blymn 		 * There are a couple of "special" values otherwise
   1392      1.73     blymn 		 * just punt with one finger, if this really is a palm
   1393      1.73     blymn 		 * then it will be caught later.
   1394      1.73     blymn 		 */
   1395      1.77     blymn 		if ((sc->flags & SYN_FLAG_HAS_MULTI_FINGER) &&
   1396      1.77     blymn 		    ((nsp.sp_w == SYNAPTICS_WIDTH_TWO_FINGERS) ||
   1397      1.77     blymn 		     (nsp.sp_w == SYNAPTICS_WIDTH_THREE_OR_MORE))) {
   1398      1.73     blymn 			/*
   1399      1.73     blymn 			 * To make life interesting if there are
   1400      1.73     blymn 			 * two or more fingers on the touchpad then
   1401      1.73     blymn 			 * the coordinate reporting changes and an extra
   1402      1.73     blymn 			 * "virtual" finger width is reported.
   1403      1.73     blymn 			 */
   1404      1.77     blymn 			nsp.sp_x = (packet4 & 0xfc) +
   1405      1.77     blymn 				((packet4 & 0x02) << 1) +
   1406      1.73     blymn 				((psc->packet[1] & 0x0f) << 8) +
   1407      1.73     blymn 				((psc->packet[3] & 0x10) << 8);
   1408      1.73     blymn 
   1409      1.77     blymn 			nsp.sp_y = (packet5 & 0xfc) +
   1410      1.77     blymn 				((packet5 & 0x02) << 1) +
   1411      1.73     blymn 				((psc->packet[1] & 0xf0) << 4) +
   1412      1.73     blymn 				((psc->packet[3] & 0x20) << 7);
   1413      1.73     blymn 
   1414      1.73     blymn 			/* Pressure */
   1415      1.73     blymn 			nsp.sp_z = psc->packet[2] & 0xfe;
   1416      1.73     blymn 
   1417      1.73     blymn 			/* derive the virtual finger width */
   1418      1.77     blymn 			v = 8 + ((packet4 & 0x02) >> 1) +
   1419      1.77     blymn 				(packet5 & 0x02) +
   1420      1.73     blymn 				((psc->packet[2] & 0x01) << 2);
   1421      1.73     blymn 
   1422      1.73     blymn 		} else {
   1423      1.73     blymn 			/* Absolute X/Y coordinates of finger */
   1424      1.77     blymn 			nsp.sp_x = packet4 +
   1425      1.73     blymn 				((psc->packet[1] & 0x0f) << 8) +
   1426      1.73     blymn 				((psc->packet[3] & 0x10) << 8);
   1427      1.73     blymn 
   1428      1.77     blymn 			nsp.sp_y = packet5 +
   1429      1.73     blymn 				((psc->packet[1] & 0xf0) << 4) +
   1430      1.73     blymn 				((psc->packet[3] & 0x20) << 7);
   1431      1.73     blymn 
   1432      1.73     blymn 			/* Pressure */
   1433      1.73     blymn 			nsp.sp_z = psc->packet[2];
   1434      1.73     blymn 		}
   1435      1.34     blymn 
   1436      1.76     blymn 		/*
   1437      1.76     blymn 		 * Check if the x and y are non-zero that they
   1438      1.76     blymn 		 * are within the bounds of the trackpad
   1439      1.76     blymn 		 * otherwise ignore the packet.
   1440      1.76     blymn 		 */
   1441      1.76     blymn 		if (((nsp.sp_x != 0) &&
   1442      1.76     blymn 		    ((nsp.sp_x < synaptics_edge_left) ||
   1443      1.76     blymn 		     (nsp.sp_x > synaptics_edge_right))) ||
   1444      1.76     blymn 		    ((nsp.sp_y != 0) &&
   1445      1.76     blymn 		    ((nsp.sp_y < synaptics_edge_bottom) ||
   1446      1.76     blymn 		     (nsp.sp_y > synaptics_edge_top)))) {
   1447      1.76     blymn 			sc->gesture_type = 0;
   1448      1.76     blymn 			sc->gesture_buttons = 0;
   1449      1.76     blymn 			sc->total_packets--;
   1450      1.76     blymn 			DPRINTF(20, sc,
   1451      1.76     blymn 			    "synaptics_parse: dropping out of bounds packet "
   1452      1.76     blymn 			    "sp_x %d sp_y %d\n",
   1453      1.76     blymn 			    nsp.sp_x, nsp.sp_y);
   1454      1.76     blymn 			return;
   1455      1.76     blymn 		}
   1456      1.76     blymn 
   1457      1.73     blymn 		nsp.sp_finger_count = pms_synaptics_get_fingers(psc,
   1458      1.73     blymn 		    nsp.sp_w, nsp.sp_z);
   1459      1.73     blymn 
   1460      1.73     blymn 		/*
   1461      1.73     blymn 		 * We don't have extended W so we only know if there
   1462      1.73     blymn 		 * are multiple fingers on the touchpad, only the primary
   1463      1.73     blymn 		 * location is reported so just pretend we have an
   1464      1.73     blymn 		 * unmoving second finger.
   1465      1.73     blymn 		 */
   1466      1.73     blymn 		if (((sc->flags & SYN_FLAG_HAS_EXTENDED_WMODE)
   1467      1.73     blymn 			!= SYN_FLAG_HAS_EXTENDED_WMODE) &&
   1468      1.73     blymn 		    (nsp.sp_finger_count > 1)) {
   1469      1.73     blymn 			nsp.sp_secondary = 1;
   1470      1.73     blymn 			nsp.sp_sx = 0;
   1471      1.73     blymn 			nsp.sp_sy = 0;
   1472      1.73     blymn 			nsp.sp_sz = 0;
   1473      1.73     blymn 		}
   1474      1.34     blymn 
   1475      1.49     blymn 		if ((psc->packet[0] ^ psc->packet[3]) & 0x02) {
   1476      1.49     blymn 			/* extended buttons */
   1477      1.49     blymn 
   1478  1.81.4.1    martin 			DPRINTF(10, sc,
   1479      1.49     blymn 			    "synaptics_parse: %02x %02x %02x %02x %02x %02x\n",
   1480      1.49     blymn 			    psc->packet[0], psc->packet[1], psc->packet[2],
   1481      1.49     blymn 			    psc->packet[3], psc->packet[4], psc->packet[5]);
   1482      1.49     blymn 
   1483      1.49     blymn 			if ((psc->packet[4] & SYN_1BUTMASK) != 0)
   1484      1.75       nia 				ext_left = PMS_LBUTMASK;
   1485      1.65  jmcneill 			else
   1486      1.75       nia 				ext_left = 0;
   1487      1.49     blymn 
   1488      1.49     blymn 			if ((psc->packet[4] & SYN_3BUTMASK) != 0)
   1489      1.75       nia 				ext_middle = PMS_MBUTMASK;
   1490      1.65  jmcneill 			else
   1491      1.75       nia 				ext_middle = 0;
   1492      1.49     blymn 
   1493      1.49     blymn 			if ((psc->packet[5] & SYN_2BUTMASK) != 0)
   1494      1.75       nia 				ext_right = PMS_RBUTMASK;
   1495      1.65  jmcneill 			else
   1496      1.75       nia 				ext_right = 0;
   1497      1.49     blymn 
   1498      1.49     blymn 			if ((psc->packet[5] & SYN_4BUTMASK) != 0)
   1499      1.75       nia 				ext_up = 1;
   1500      1.65  jmcneill 			else
   1501      1.75       nia 				ext_up = 0;
   1502      1.49     blymn 
   1503      1.49     blymn 			if ((psc->packet[4] & SYN_5BUTMASK) != 0)
   1504      1.75       nia 				ext_down = 1;
   1505      1.65  jmcneill 			else
   1506      1.75       nia 				ext_down = 0;
   1507      1.49     blymn 		} else {
   1508      1.49     blymn 			/* Left/Right button handling. */
   1509      1.73     blymn 			nsp.sp_left = psc->packet[0] & PMS_LBUTMASK;
   1510      1.73     blymn 			nsp.sp_right = psc->packet[0] & PMS_RBUTMASK;
   1511      1.49     blymn 		}
   1512      1.34     blymn 
   1513      1.34     blymn 		/* Up/Down buttons. */
   1514      1.34     blymn 		if (sc->flags & SYN_FLAG_HAS_BUTTONS_4_5) {
   1515      1.34     blymn 			/* Old up/down buttons. */
   1516      1.73     blymn 			nsp.sp_up = nsp.sp_left ^
   1517      1.34     blymn 		    	    (psc->packet[3] & PMS_LBUTMASK);
   1518      1.73     blymn 			nsp.sp_down = nsp.sp_right ^
   1519      1.34     blymn 		    	    (psc->packet[3] & PMS_RBUTMASK);
   1520      1.34     blymn 		} else if (sc->flags & SYN_FLAG_HAS_UP_DOWN_BUTTONS &&
   1521      1.34     blymn 	   	    ((psc->packet[0] & PMS_RBUTMASK) ^
   1522      1.34     blymn 	   	    (psc->packet[3] & PMS_RBUTMASK))) {
   1523      1.34     blymn 			/* New up/down button. */
   1524      1.73     blymn 			nsp.sp_up = psc->packet[4] & SYN_1BUTMASK;
   1525      1.73     blymn 			nsp.sp_down = psc->packet[5] & SYN_2BUTMASK;
   1526      1.34     blymn 		} else {
   1527      1.73     blymn 			nsp.sp_up = 0;
   1528      1.73     blymn 			nsp.sp_down = 0;
   1529      1.34     blymn 		}
   1530      1.34     blymn 
   1531      1.34     blymn 		new_buttons = 0;
   1532      1.34     blymn 		if(sc->flags & SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD) {
   1533      1.34     blymn 			/* This is not correctly specified. Read this button press
   1534      1.38     ryoon 		 	* from L/U bit.  Emulate 3 buttons by checking the
   1535      1.34     blymn 		 	* coordinates of the click and returning the appropriate
   1536      1.34     blymn 		 	* button code.  Outside the button region default to a
   1537      1.34     blymn 		 	* left click.
   1538      1.34     blymn 		 	*/
   1539      1.34     blymn 			u_char bstate = (psc->packet[0] ^ psc->packet[3])
   1540      1.34     blymn 					    & 0x01;
   1541      1.73     blymn 			if (nsp.sp_y < synaptics_button_boundary) {
   1542      1.73     blymn 				if (nsp.sp_x > synaptics_button3) {
   1543      1.73     blymn 					nsp.sp_right =
   1544      1.34     blymn 			   			bstate ? PMS_RBUTMASK : 0;
   1545      1.73     blymn 				} else if (nsp.sp_x > synaptics_button2) {
   1546      1.73     blymn 					nsp.sp_middle =
   1547      1.34     blymn 				   		bstate ? PMS_MBUTMASK : 0;
   1548      1.34     blymn 				} else {
   1549      1.73     blymn 					nsp.sp_left = bstate ? PMS_LBUTMASK : 0;
   1550      1.34     blymn 				}
   1551      1.34     blymn 			} else
   1552      1.73     blymn 				nsp.sp_left = bstate ? 1 : 0;
   1553      1.73     blymn 			new_buttons = nsp.sp_left | nsp.sp_middle | nsp.sp_right;
   1554      1.34     blymn 			if (new_buttons != sc->button_history) {
   1555      1.34     blymn 				if (sc->button_history == 0)
   1556      1.34     blymn 					sc->button_history = new_buttons;
   1557      1.34     blymn 				else if (new_buttons == 0) {
   1558      1.34     blymn 					sc->button_history = 0;
   1559      1.34     blymn 				       /* ensure all buttons are cleared just in
   1560      1.34     blymn 				 	* case finger comes off in a different
   1561      1.34     blymn 				 	* region.
   1562      1.34     blymn 				 	*/
   1563      1.73     blymn 					nsp.sp_left = 0;
   1564      1.73     blymn 					nsp.sp_middle = 0;
   1565      1.73     blymn 					nsp.sp_right = 0;
   1566      1.34     blymn 				} else {
   1567      1.34     blymn 					/* make sure we keep the same button even
   1568      1.34     blymn 				 	* if the finger moves to a different
   1569      1.34     blymn 				 	* region.  This precludes chording
   1570      1.34     blymn 				 	* but, oh well.
   1571      1.34     blymn 				 	*/
   1572      1.73     blymn 					nsp.sp_left = sc->button_history & PMS_LBUTMASK;
   1573      1.73     blymn 					nsp.sp_middle = sc->button_history
   1574      1.34     blymn 				    	& PMS_MBUTMASK;
   1575      1.73     blymn 					nsp.sp_right = sc->button_history & PMS_RBUTMASK;
   1576      1.34     blymn 				}
   1577      1.34     blymn 			}
   1578      1.34     blymn 		} else if (sc->flags & SYN_FLAG_HAS_MIDDLE_BUTTON) {
   1579      1.34     blymn 			/* Old style Middle Button. */
   1580      1.73     blymn 			nsp.sp_middle = (psc->packet[0] & PMS_LBUTMASK) ^
   1581      1.34     blymn 		    	    (psc->packet[3] & PMS_LBUTMASK);
   1582      1.75       nia 		} else {
   1583      1.73     blymn 			nsp.sp_middle = 0;
   1584      1.55       nia 		}
   1585      1.55       nia 
   1586      1.75       nia 		/*
   1587      1.75       nia 		 * Overlay extended button state if anything changed,
   1588      1.75       nia 		 * preserve the state if a button is being held.
   1589      1.75       nia 		 */
   1590      1.75       nia 		if (ext_left != -1)
   1591      1.75       nia 			nsp.sp_left = sc->ext_left = ext_left;
   1592      1.75       nia 		else if (sc->ext_left != 0)
   1593      1.75       nia 			nsp.sp_left = sc->ext_left;
   1594      1.75       nia 
   1595      1.75       nia 		if (ext_right != -1)
   1596      1.75       nia 			nsp.sp_right = sc->ext_right = ext_right;
   1597      1.75       nia 		else if (sc->ext_right != 0)
   1598      1.75       nia 			nsp.sp_right = sc->ext_right;
   1599      1.75       nia 
   1600      1.75       nia 		if (ext_middle != -1)
   1601      1.75       nia 			nsp.sp_middle = sc->ext_middle = ext_middle;
   1602      1.75       nia 		else if (sc->ext_middle != 0)
   1603      1.75       nia 			nsp.sp_middle = sc->ext_middle;
   1604      1.75       nia 
   1605      1.75       nia 		if (ext_up != -1)
   1606      1.75       nia 			nsp.sp_up = sc->ext_up = ext_up;
   1607      1.75       nia 		else if (sc->ext_up != 0)
   1608      1.75       nia 			nsp.sp_up = sc->ext_up;
   1609      1.75       nia 
   1610      1.75       nia 		if (ext_down != -1)
   1611      1.75       nia 			nsp.sp_down = sc->ext_down = ext_down;
   1612      1.75       nia 		else if (sc->ext_down != 0)
   1613      1.75       nia 			nsp.sp_down = sc->ext_down;
   1614      1.65  jmcneill 
   1615      1.55       nia 		switch (synaptics_up_down_emul) {
   1616      1.55       nia 		case 1:
   1617      1.34     blymn 			/* Do middle button emulation using up/down buttons */
   1618      1.73     blymn 			nsp.sp_middle = nsp.sp_up | nsp.sp_down;
   1619      1.73     blymn 			nsp.sp_up = nsp.sp_down = 0;
   1620      1.55       nia 			break;
   1621      1.55       nia 		case 3:
   1622      1.55       nia 			/* Do left/right button emulation using up/down buttons */
   1623      1.73     blymn 			nsp.sp_left = nsp.sp_left | nsp.sp_up;
   1624      1.73     blymn 			nsp.sp_right = nsp.sp_right | nsp.sp_down;
   1625      1.73     blymn 			nsp.sp_up = nsp.sp_down = 0;
   1626      1.55       nia 			break;
   1627      1.55       nia 		default:
   1628      1.55       nia 			/*
   1629      1.55       nia 			 * Don't do any remapping...
   1630      1.55       nia 			 * Z-axis emulation is handled in pms_synaptics_process_packet
   1631      1.55       nia 			 */
   1632      1.55       nia 			break;
   1633      1.55       nia 		}
   1634      1.14   mlelstv 	}
   1635      1.14   mlelstv 
   1636      1.73     blymn 	/* set the finger count only if we haven't seen an extended-w
   1637      1.73     blymn 	 * finger count status
   1638      1.73     blymn 	 */
   1639      1.73     blymn 	if (nsp.sp_finger_status == 0)
   1640      1.73     blymn 		nsp.sp_finger_count = pms_synaptics_get_fingers(psc, nsp.sp_w,
   1641      1.73     blymn 		    nsp.sp_z);
   1642      1.73     blymn 
   1643      1.73     blymn skip_position:
   1644      1.73     blymn 	DPRINTF(20, sc,
   1645      1.73     blymn 	    "synaptics_parse: sp_x %d sp_y %d sp_z %d, sp_sx %d, sp_sy %d, "
   1646      1.73     blymn 	    "sp_sz %d, sp_w %d sp_finger_count %d, sp_primary %d, "
   1647      1.73     blymn 	    "sp_secondary %d, v %d, primary_finger %d, secondary_finger %d\n",
   1648      1.73     blymn 	    nsp.sp_x, nsp.sp_y, nsp.sp_z, nsp.sp_sx,
   1649      1.73     blymn 	    nsp.sp_sy, nsp.sp_sz, nsp.sp_w, nsp.sp_finger_count,
   1650      1.73     blymn 	    nsp.sp_primary, nsp.sp_secondary, v, primary_finger,
   1651      1.73     blymn 	    secondary_finger);
   1652      1.73     blymn 
   1653      1.80   mlelstv 	pms_synaptics_process_packet(psc, &nsp);
   1654      1.73     blymn 
   1655      1.80   mlelstv 	/* Clear fingers */
   1656      1.80   mlelstv 	if ((nsp.sp_primary && nsp.sp_finger_count <= 1) || nsp.sp_secondary) {
   1657      1.73     blymn 		nsp.sp_primary = 0;
   1658      1.73     blymn 		nsp.sp_secondary = 0;
   1659      1.73     blymn 		nsp.sp_finger_status = 0;
   1660      1.73     blymn 	}
   1661      1.73     blymn 
   1662      1.73     blymn 	memcpy(&packet, &nsp, sizeof(packet));
   1663      1.14   mlelstv }
   1664      1.14   mlelstv 
   1665      1.69       nia /*
   1666      1.69       nia  * Passthrough is used for e.g. TrackPoints and additional pointing
   1667      1.69       nia  * devices connected to a Synaptics touchpad.
   1668      1.69       nia  */
   1669      1.14   mlelstv static void
   1670      1.14   mlelstv pms_synaptics_passthrough(struct pms_softc *psc)
   1671      1.14   mlelstv {
   1672      1.14   mlelstv 	int dx, dy, dz;
   1673      1.14   mlelstv 	int buttons, changed;
   1674      1.14   mlelstv 	int s;
   1675      1.14   mlelstv 
   1676      1.14   mlelstv 	buttons = ((psc->packet[1] & PMS_LBUTMASK) ? 0x20 : 0) |
   1677      1.14   mlelstv 		((psc->packet[1] & PMS_MBUTMASK) ? 0x40 : 0) |
   1678      1.14   mlelstv 		((psc->packet[1] & PMS_RBUTMASK) ? 0x80 : 0);
   1679      1.14   mlelstv 
   1680      1.14   mlelstv 	dx = psc->packet[4];
   1681      1.14   mlelstv 	if (dx >= 128)
   1682      1.14   mlelstv 		dx -= 256;
   1683      1.14   mlelstv 	if (dx == -128)
   1684      1.14   mlelstv 		dx = -127;
   1685      1.14   mlelstv 
   1686      1.14   mlelstv 	dy = psc->packet[5];
   1687      1.14   mlelstv 	if (dy >= 128)
   1688      1.14   mlelstv 		dy -= 256;
   1689      1.14   mlelstv 	if (dy == -128)
   1690      1.14   mlelstv 		dy = -127;
   1691      1.14   mlelstv 
   1692      1.14   mlelstv 	dz = 0;
   1693      1.14   mlelstv 
   1694      1.14   mlelstv 	changed = buttons ^ (psc->buttons & 0xe0);
   1695      1.14   mlelstv 	psc->buttons ^= changed;
   1696      1.14   mlelstv 
   1697      1.14   mlelstv 	if (dx || dy || dz || changed) {
   1698      1.72       nia 		s = spltty();
   1699      1.70       nia 		/*
   1700      1.72       nia 		 * If the middle button is held, interpret movement as
   1701      1.72       nia 		 * scrolling.
   1702      1.70       nia 		 */
   1703      1.70       nia 		if (synaptics_aux_mid_button_scroll &&
   1704      1.70       nia 		    dy && (psc->buttons & 0x2)) {
   1705      1.72       nia 			wsmouse_precision_scroll(psc->sc_wsmousedev, dx, dy);
   1706      1.72       nia 		} else {
   1707      1.72       nia 			buttons = (psc->buttons & 0x1f) | ((psc->buttons >> 5) & 0x7);
   1708      1.72       nia 			wsmouse_input(psc->sc_wsmousedev,
   1709      1.72       nia 				buttons, dx, dy, dz, 0,
   1710      1.72       nia 				WSMOUSE_INPUT_DELTA);
   1711      1.70       nia 		}
   1712      1.14   mlelstv 		splx(s);
   1713      1.14   mlelstv 	}
   1714      1.14   mlelstv }
   1715      1.14   mlelstv 
   1716      1.14   mlelstv static void
   1717       1.1  christos pms_synaptics_input(void *vsc, int data)
   1718       1.1  christos {
   1719       1.3       scw 	struct pms_softc *psc = vsc;
   1720       1.3       scw 	struct timeval diff;
   1721       1.1  christos 
   1722       1.3       scw 	if (!psc->sc_enabled) {
   1723      1.28  jakllsch 		/* Interrupts are not expected. Discard the byte. */
   1724       1.1  christos 		return;
   1725       1.1  christos 	}
   1726       1.1  christos 
   1727      1.10    kardel 	getmicrouptime(&psc->current);
   1728       1.1  christos 
   1729      1.67       nia 	if (psc->inputstate > 0) {
   1730       1.3       scw 		timersub(&psc->current, &psc->last, &diff);
   1731       1.1  christos 		if (diff.tv_sec > 0 || diff.tv_usec >= 40000) {
   1732  1.81.4.1    martin 			device_printf(psc->sc_dev,
   1733      1.53     ryoon 			    "pms_synaptics_input: unusual delay (%ld.%06ld s), "
   1734      1.20      cube 			    "scheduling reset\n",
   1735       1.1  christos 			    (long)diff.tv_sec, (long)diff.tv_usec);
   1736       1.3       scw 			psc->inputstate = 0;
   1737       1.3       scw 			psc->sc_enabled = 0;
   1738       1.3       scw 			wakeup(&psc->sc_enabled);
   1739       1.1  christos 			return;
   1740       1.1  christos 		}
   1741       1.1  christos 	}
   1742       1.3       scw 	psc->last = psc->current;
   1743       1.1  christos 
   1744       1.3       scw 	switch (psc->inputstate) {
   1745      1.50   mlelstv 	case -5:
   1746      1.50   mlelstv 	case -4:
   1747      1.50   mlelstv 	case -3:
   1748      1.50   mlelstv 	case -2:
   1749      1.50   mlelstv 	case -1:
   1750       1.3       scw 	case 0:
   1751       1.1  christos 		if ((data & 0xc8) != 0x80) {
   1752  1.81.4.1    martin 			device_printf(psc->sc_dev,
   1753      1.54     ryoon 			    "pms_synaptics_input: 0x%02x out of sync\n", data);
   1754      1.50   mlelstv 			/* use negative counts to limit resync phase */
   1755      1.50   mlelstv 			psc->inputstate--;
   1756       1.1  christos 			return;	/* not in sync yet, discard input */
   1757       1.1  christos 		}
   1758      1.50   mlelstv 		psc->inputstate = 0;
   1759       1.3       scw 		/*FALLTHROUGH*/
   1760       1.3       scw 
   1761      1.50   mlelstv 	case -6:
   1762       1.3       scw 	case 3:
   1763       1.5     perry 		if ((data & 8) == 8) {
   1764  1.81.4.1    martin 			device_printf(psc->sc_dev,
   1765      1.54     ryoon 			    "pms_synaptics_input: dropped in relative mode, reset\n");
   1766       1.3       scw 			psc->inputstate = 0;
   1767       1.3       scw 			psc->sc_enabled = 0;
   1768       1.3       scw 			wakeup(&psc->sc_enabled);
   1769       1.1  christos 			return;
   1770       1.1  christos 		}
   1771       1.1  christos 	}
   1772       1.1  christos 
   1773       1.3       scw 	psc->packet[psc->inputstate++] = data & 0xff;
   1774       1.3       scw 	if (psc->inputstate == 6) {
   1775  1.81.4.1    martin 		struct synaptics_softc *sc = &psc->u.synaptics;
   1776  1.81.4.1    martin 		DPRINTF(10, sc, "synaptics: packet: 0x%02x%02x%02x%02x%02x%02x\n",
   1777  1.81.4.1    martin 			psc->packet[0], psc->packet[1], psc->packet[2],
   1778  1.81.4.1    martin 			psc->packet[3], psc->packet[4], psc->packet[5]);
   1779       1.3       scw 		/*
   1780       1.3       scw 		 * We have a complete packet.
   1781       1.3       scw 		 * Extract the pertinent details.
   1782       1.3       scw 		 */
   1783       1.3       scw 		psc->inputstate = 0;
   1784      1.14   mlelstv 		if ((psc->packet[0] & 0xfc) == 0x84 &&
   1785      1.14   mlelstv 		    (psc->packet[3] & 0xcc) == 0xc4) {
   1786      1.28  jakllsch 			/* W = SYNAPTICS_WIDTH_PASSTHROUGH, PS/2 passthrough */
   1787      1.14   mlelstv 			pms_synaptics_passthrough(psc);
   1788       1.3       scw 		} else {
   1789      1.14   mlelstv 			pms_synaptics_parse(psc);
   1790       1.1  christos 		}
   1791       1.1  christos 	}
   1792       1.3       scw }
   1793       1.1  christos 
   1794       1.9     perry static inline int
   1795       1.3       scw synaptics_finger_detect(struct synaptics_softc *sc, struct synaptics_packet *sp,
   1796       1.3       scw     int *palmp)
   1797       1.3       scw {
   1798       1.3       scw 	int fingers;
   1799       1.3       scw 
   1800       1.3       scw 	/* Assume no palm */
   1801       1.3       scw 	*palmp = 0;
   1802       1.3       scw 
   1803       1.3       scw 	/*
   1804       1.3       scw 	 * Apply some hysteresis when checking for a finger.
   1805       1.3       scw 	 * When the finger is first applied, we ignore it until the
   1806       1.3       scw 	 * pressure exceeds the 'high' threshold. The finger is considered
   1807       1.3       scw 	 * removed only when pressure falls beneath the 'low' threshold.
   1808       1.3       scw 	 */
   1809       1.3       scw 	if ((sc->prev_fingers == 0 && sp->sp_z > synaptics_finger_high) ||
   1810       1.3       scw 	    (sc->prev_fingers != 0 && sp->sp_z > synaptics_finger_low))
   1811       1.3       scw 		fingers = 1;
   1812       1.3       scw 	else
   1813       1.3       scw 		fingers = 0;
   1814       1.3       scw 
   1815       1.3       scw 	/*
   1816       1.3       scw 	 * If the pad can't do palm detection, skip the rest.
   1817       1.3       scw 	 */
   1818       1.3       scw 	if (fingers == 0 || (sc->flags & SYN_FLAG_HAS_PALM_DETECT) == 0)
   1819       1.3       scw 		return (fingers);
   1820       1.3       scw 
   1821       1.3       scw 	/*
   1822       1.3       scw 	 * Palm detection
   1823       1.3       scw 	 */
   1824       1.3       scw 	if (sp->sp_z > SYNAPTICS_FINGER_FLAT &&
   1825       1.3       scw 	    sp->sp_w >= SYNAPTICS_WIDTH_PALM_MIN)
   1826       1.3       scw 		*palmp = 1;
   1827       1.3       scw 
   1828       1.3       scw 	if (sc->prev_fingers == 0 &&
   1829       1.3       scw 	    (sp->sp_z > SYNAPTICS_FINGER_FLAT ||
   1830       1.3       scw 	     sp->sp_w >= SYNAPTICS_WIDTH_PALM_MIN)) {
   1831       1.3       scw 		/*
   1832       1.3       scw 		 * Contact area or pressure is too great to be a finger.
   1833       1.3       scw 		 * Just ignore it for now.
   1834       1.3       scw 		 */
   1835       1.3       scw 		return (0);
   1836       1.3       scw 	}
   1837       1.3       scw 
   1838      1.63       nia 	/*
   1839      1.63       nia 	 * Detect 2 and 3 fingers if supported, but only if multiple
   1840      1.63       nia 	 * fingers appear within the tap gesture time period.
   1841      1.63       nia 	 */
   1842      1.73     blymn 	if ((sc->flags & SYN_FLAG_HAS_MULTI_FINGER) &&
   1843      1.73     blymn 	    ((SYN_TIME(sc, sc->gesture_start_packet)
   1844      1.73     blymn 	     < synaptics_gesture_length) ||
   1845      1.73     blymn 	    SYN_TIME(sc, sc->gesture_start_packet)
   1846      1.73     blymn 	     < synaptics_gesture_length)) {
   1847       1.3       scw 		switch (sp->sp_w) {
   1848       1.3       scw 		case SYNAPTICS_WIDTH_TWO_FINGERS:
   1849       1.3       scw 			fingers = 2;
   1850       1.3       scw 			break;
   1851       1.3       scw 
   1852       1.3       scw 		case SYNAPTICS_WIDTH_THREE_OR_MORE:
   1853       1.3       scw 			fingers = 3;
   1854       1.3       scw 			break;
   1855       1.3       scw 
   1856       1.3       scw 		default:
   1857       1.3       scw 			/*
   1858       1.3       scw 			 * The width value can report spurious single-finger
   1859       1.3       scw 			 * events after a multi-finger event.
   1860       1.3       scw 			 */
   1861      1.61       nia 			fingers = sc->prev_fingers <= 1 ? 1 : sc->prev_fingers;
   1862       1.3       scw 			break;
   1863       1.1  christos 		}
   1864       1.1  christos 	}
   1865       1.3       scw 
   1866       1.3       scw 	return (fingers);
   1867       1.1  christos }
   1868       1.1  christos 
   1869       1.9     perry static inline void
   1870       1.3       scw synaptics_gesture_detect(struct synaptics_softc *sc,
   1871       1.3       scw     struct synaptics_packet *sp, int fingers)
   1872       1.3       scw {
   1873      1.32  christos 	int gesture_len, gesture_buttons;
   1874       1.3       scw 	int set_buttons;
   1875       1.3       scw 
   1876      1.73     blymn 	gesture_len = SYN_TIME(sc, sc->gesture_start_packet);
   1877       1.3       scw 	gesture_buttons = sc->gesture_buttons;
   1878       1.1  christos 
   1879      1.32  christos 	if (fingers > 0 && (fingers == sc->prev_fingers)) {
   1880      1.32  christos 		/* Finger is still present */
   1881      1.32  christos 		sc->gesture_move_x = abs(sc->gesture_start_x - sp->sp_x);
   1882      1.32  christos 		sc->gesture_move_y = abs(sc->gesture_start_y - sp->sp_y);
   1883      1.32  christos 	} else
   1884       1.3       scw 	if (fingers && sc->prev_fingers == 0) {
   1885       1.3       scw 		/*
   1886       1.3       scw 		 * Finger was just applied.
   1887       1.3       scw 		 * If the previous gesture was a single-click, set things
   1888       1.3       scw 		 * up to deal with a possible drag or double-click gesture.
   1889       1.3       scw 		 * Basically, if the finger is removed again within
   1890       1.3       scw 		 * 'synaptics_gesture_length' packets, this is treated
   1891       1.3       scw 		 * as a double-click. Otherwise we will emulate holding
   1892       1.3       scw 		 * the left button down whilst dragging the mouse.
   1893       1.3       scw 		 */
   1894       1.3       scw 		if (SYN_IS_SINGLE_TAP(sc->gesture_type))
   1895       1.3       scw 			sc->gesture_type |= SYN_GESTURE_DRAG;
   1896       1.3       scw 
   1897      1.32  christos 		sc->gesture_start_x = abs(sp->sp_x);
   1898      1.32  christos 		sc->gesture_start_y = abs(sp->sp_y);
   1899      1.32  christos 		sc->gesture_move_x = 0;
   1900      1.32  christos 		sc->gesture_move_y = 0;
   1901      1.73     blymn 		sc->gesture_start_packet = sc->total_packets;
   1902      1.32  christos 
   1903      1.73     blymn 		DPRINTF(10, sc, "Finger applied:"
   1904      1.71  riastrad 		    " gesture_start_x: %d"
   1905      1.71  riastrad 		    " gesture_start_y: %d\n",
   1906      1.71  riastrad 		    sc->gesture_start_x, sc->gesture_start_y);
   1907       1.3       scw 	} else
   1908       1.3       scw 	if (fingers == 0 && sc->prev_fingers != 0) {
   1909       1.3       scw 		/*
   1910       1.3       scw 		 * Finger was just removed.
   1911       1.3       scw 		 * Check if the contact time and finger movement were
   1912       1.3       scw 		 * small enough to qualify as a gesture.
   1913       1.3       scw 		 * Ignore finger movement if multiple fingers were
   1914       1.3       scw 		 * detected (the pad may report coordinates for any
   1915       1.3       scw 		 * of the fingers).
   1916       1.3       scw 		 */
   1917      1.32  christos 
   1918      1.73     blymn 		DPRINTF(10, sc, "Finger removed: gesture_len: %d (%d)\n",
   1919      1.71  riastrad 		    gesture_len, synaptics_gesture_length);
   1920      1.73     blymn 		DPRINTF(10, sc, "gesture_move_x: %d (%d) sp_x: %d\n",
   1921      1.71  riastrad 		    sc->gesture_move_x, synaptics_gesture_move, abs(sp->sp_x));
   1922      1.73     blymn 		DPRINTF(10, sc, "gesture_move_y: %d (%d) sp_y: %d\n",
   1923      1.71  riastrad 		    sc->gesture_move_y, synaptics_gesture_move, abs(sp->sp_y));
   1924       1.3       scw 
   1925       1.3       scw 		if (gesture_len < synaptics_gesture_length &&
   1926      1.32  christos 		    ((sc->gesture_move_x < synaptics_gesture_move &&
   1927      1.32  christos 		     sc->gesture_move_y < synaptics_gesture_move))) {
   1928       1.3       scw 			/*
   1929       1.3       scw 			 * Looking good so far.
   1930       1.3       scw 			 */
   1931       1.3       scw 			if (SYN_IS_DRAG(sc->gesture_type)) {
   1932       1.3       scw 				/*
   1933       1.3       scw 				 * Promote this gesture to double-click.
   1934       1.3       scw 				 */
   1935       1.3       scw 				sc->gesture_type |= SYN_GESTURE_DOUBLE;
   1936       1.3       scw 				sc->gesture_type &= ~SYN_GESTURE_SINGLE;
   1937       1.3       scw 			} else {
   1938       1.3       scw 				/*
   1939       1.3       scw 				 * Single tap gesture. Set the tap length timer
   1940       1.3       scw 				 * and flag a single-click.
   1941       1.3       scw 				 */
   1942      1.73     blymn 				sc->gesture_tap_packet = sc->total_packets;
   1943       1.3       scw 				sc->gesture_type |= SYN_GESTURE_SINGLE;
   1944       1.3       scw 
   1945       1.3       scw 				/*
   1946       1.3       scw 				 * The gesture can be modified depending on
   1947       1.3       scw 				 * the number of fingers detected.
   1948       1.3       scw 				 *
   1949       1.3       scw 				 * 1: Normal left button emulation.
   1950       1.3       scw 				 * 2: Either middle button or right button
   1951       1.3       scw 				 *    depending on the value of the two_fingers
   1952       1.3       scw 				 *    sysctl variable.
   1953       1.3       scw 				 * 3: Right button.
   1954       1.3       scw 				 */
   1955       1.3       scw 				switch (sc->prev_fingers) {
   1956       1.3       scw 				case 2:
   1957       1.3       scw 					if (synaptics_two_fingers_emul == 1)
   1958       1.3       scw 						gesture_buttons |= PMS_RBUTMASK;
   1959       1.3       scw 					else
   1960       1.3       scw 					if (synaptics_two_fingers_emul == 2)
   1961       1.3       scw 						gesture_buttons |= PMS_MBUTMASK;
   1962       1.3       scw 					break;
   1963       1.3       scw 				case 3:
   1964       1.3       scw 					gesture_buttons |= PMS_RBUTMASK;
   1965       1.3       scw 					break;
   1966       1.3       scw 				default:
   1967       1.3       scw 					gesture_buttons |= PMS_LBUTMASK;
   1968       1.3       scw 					break;
   1969       1.3       scw 				}
   1970       1.1  christos 			}
   1971       1.1  christos 		}
   1972       1.1  christos 
   1973       1.3       scw 		/*
   1974       1.3       scw 		 * Always clear drag state when the finger is removed.
   1975       1.3       scw 		 */
   1976       1.3       scw 		sc->gesture_type &= ~SYN_GESTURE_DRAG;
   1977       1.3       scw 	}
   1978       1.3       scw 
   1979       1.3       scw 	if (sc->gesture_type == 0) {
   1980       1.3       scw 		/*
   1981       1.3       scw 		 * There is no gesture in progress.
   1982       1.3       scw 		 * Clear emulated button state.
   1983       1.3       scw 		 */
   1984       1.3       scw 		sc->gesture_buttons = 0;
   1985       1.3       scw 		return;
   1986       1.3       scw 	}
   1987       1.3       scw 
   1988       1.3       scw 	/*
   1989       1.3       scw 	 * A gesture is in progress.
   1990       1.3       scw 	 */
   1991       1.3       scw 	set_buttons = 0;
   1992       1.3       scw 
   1993       1.3       scw 	if (SYN_IS_SINGLE_TAP(sc->gesture_type)) {
   1994       1.3       scw 		/*
   1995       1.3       scw 		 * Single-click.
   1996       1.3       scw 		 * Activate the relevant button(s) until the
   1997       1.3       scw 		 * gesture tap timer has expired.
   1998       1.3       scw 		 */
   1999      1.73     blymn 		if (SYN_TIME(sc, sc->gesture_tap_packet) <
   2000       1.3       scw 		    synaptics_gesture_length)
   2001       1.3       scw 			set_buttons = 1;
   2002       1.3       scw 		else
   2003       1.3       scw 			sc->gesture_type &= ~SYN_GESTURE_SINGLE;
   2004      1.73     blymn 		DPRINTF(10, sc, "synaptics_gesture: single tap, buttons %d\n",
   2005      1.73     blymn 		    set_buttons);
   2006      1.73     blymn 		DPRINTF(10, sc, "synaptics_gesture: single tap, tap at %d, current %d\n",
   2007      1.73     blymn 		    sc->gesture_tap_packet, sc->total_packets);
   2008      1.73     blymn 		DPRINTF(10, sc, "synaptics_gesture: single tap, tap_time %d, gesture len %d\n",
   2009      1.73     blymn 		    SYN_TIME(sc, sc->gesture_tap_packet), synaptics_gesture_length);
   2010       1.3       scw 	} else
   2011       1.3       scw 	if (SYN_IS_DOUBLE_TAP(sc->gesture_type) && sc->prev_fingers == 0) {
   2012       1.3       scw 		/*
   2013       1.3       scw 		 * Double-click.
   2014       1.3       scw 		 * Activate the relevant button(s) once.
   2015       1.3       scw 		 */
   2016       1.3       scw 		set_buttons = 1;
   2017       1.3       scw 		sc->gesture_type &= ~SYN_GESTURE_DOUBLE;
   2018       1.3       scw 	}
   2019       1.3       scw 
   2020       1.3       scw 	if (set_buttons || SYN_IS_DRAG(sc->gesture_type)) {
   2021       1.3       scw 		/*
   2022       1.3       scw 		 * Single-click and drag.
   2023       1.3       scw 		 * Maintain button state until the finger is removed.
   2024       1.3       scw 		 */
   2025       1.3       scw 		sp->sp_left |= gesture_buttons & PMS_LBUTMASK;
   2026       1.3       scw 		sp->sp_right |= gesture_buttons & PMS_RBUTMASK;
   2027       1.3       scw 		sp->sp_middle |= gesture_buttons & PMS_MBUTMASK;
   2028       1.3       scw 	}
   2029       1.3       scw 
   2030       1.3       scw 	sc->gesture_buttons = gesture_buttons;
   2031       1.3       scw }
   2032       1.3       scw 
   2033       1.9     perry static inline int
   2034      1.34     blymn synaptics_filter_policy(struct synaptics_softc *sc, int finger, int *history,
   2035      1.73     blymn 			int value, u_int count)
   2036       1.3       scw {
   2037      1.73     blymn 	int a, b, rv;
   2038       1.3       scw 
   2039       1.3       scw 	/*
   2040       1.3       scw 	 * Once we've accumulated at least SYN_HIST_SIZE values, combine
   2041       1.3       scw 	 * each new value with the previous two and return the average.
   2042       1.3       scw 	 *
   2043       1.3       scw 	 * This is necessary when the touchpad is operating in 80 packets
   2044       1.3       scw 	 * per second mode, as it performs little internal filtering on
   2045       1.3       scw 	 * reported values.
   2046       1.3       scw 	 *
   2047       1.3       scw 	 * Using a rolling average helps to filter out jitter caused by
   2048       1.3       scw 	 * tiny finger movements.
   2049       1.3       scw 	 */
   2050      1.73     blymn 	if (count >= SYN_HIST_SIZE) {
   2051      1.80   mlelstv 		a = (history[(count - 2) % SYN_HIST_SIZE] +
   2052      1.80   mlelstv 		     history[(count - 1) % SYN_HIST_SIZE]) / 2;
   2053       1.3       scw 
   2054      1.80   mlelstv 		b = (value + history[(count - 1) % SYN_HIST_SIZE]) / 2;
   2055       1.3       scw 
   2056       1.3       scw 		rv = b - a;
   2057       1.3       scw 
   2058       1.3       scw 		/*
   2059       1.3       scw 		 * Don't report the movement if it's below a certain
   2060       1.3       scw 		 * threshold.
   2061       1.3       scw 		 */
   2062       1.3       scw 		if (abs(rv) < synaptics_movement_threshold)
   2063       1.3       scw 			rv = 0;
   2064       1.3       scw 	} else
   2065       1.3       scw 		rv = 0;
   2066       1.3       scw 
   2067       1.3       scw 	/*
   2068       1.3       scw 	 * Add the new value to the history buffer.
   2069       1.3       scw 	 */
   2070      1.80   mlelstv 	history[(count + 0) % SYN_HIST_SIZE] = value;
   2071       1.3       scw 
   2072       1.3       scw 	return (rv);
   2073       1.3       scw }
   2074       1.3       scw 
   2075       1.3       scw /* Edge detection */
   2076       1.3       scw #define	SYN_EDGE_TOP		1
   2077       1.3       scw #define	SYN_EDGE_BOTTOM		2
   2078       1.3       scw #define	SYN_EDGE_LEFT		4
   2079       1.3       scw #define	SYN_EDGE_RIGHT		8
   2080       1.3       scw 
   2081       1.9     perry static inline int
   2082       1.3       scw synaptics_check_edge(int x, int y)
   2083       1.3       scw {
   2084       1.3       scw 	int rv = 0;
   2085       1.3       scw 
   2086       1.3       scw 	if (x < synaptics_edge_left)
   2087       1.3       scw 		rv |= SYN_EDGE_LEFT;
   2088       1.3       scw 	else
   2089       1.3       scw 	if (x > synaptics_edge_right)
   2090       1.3       scw 		rv |= SYN_EDGE_RIGHT;
   2091       1.3       scw 
   2092       1.3       scw 	if (y < synaptics_edge_bottom)
   2093       1.3       scw 		rv |= SYN_EDGE_BOTTOM;
   2094       1.3       scw 	else
   2095       1.3       scw 	if (y > synaptics_edge_top)
   2096       1.3       scw 		rv |= SYN_EDGE_TOP;
   2097       1.3       scw 
   2098       1.3       scw 	return (rv);
   2099       1.3       scw }
   2100       1.3       scw 
   2101       1.9     perry static inline int
   2102      1.13  christos synaptics_edge_motion(struct synaptics_softc *sc, int delta, int dir)
   2103       1.3       scw {
   2104       1.3       scw 
   2105       1.3       scw 	/*
   2106       1.3       scw 	 * When edge motion is enabled, synaptics_edge_motion_delta is
   2107       1.3       scw 	 * combined with the current delta, together with the direction
   2108       1.3       scw 	 * in which to simulate the motion. The result is added to
   2109       1.3       scw 	 * the delta derived from finger movement. This provides a smooth
   2110       1.3       scw 	 * transition from finger movement to edge motion.
   2111       1.3       scw 	 */
   2112       1.3       scw 	delta = synaptics_edge_motion_delta + (dir * delta);
   2113       1.3       scw 	if (delta < 0)
   2114       1.3       scw 		return (0);
   2115       1.3       scw 	if (delta > synaptics_edge_motion_delta)
   2116       1.3       scw 		return (synaptics_edge_motion_delta);
   2117       1.3       scw 	return (delta);
   2118       1.3       scw }
   2119       1.3       scw 
   2120       1.9     perry static inline int
   2121       1.3       scw synaptics_scale(int delta, int scale, int *remp)
   2122       1.3       scw {
   2123       1.3       scw 	int rv;
   2124       1.3       scw 
   2125       1.3       scw 	/*
   2126       1.3       scw 	 * Scale the raw delta in Synaptics coordinates (0-6143) into
   2127       1.3       scw 	 * something more reasonable by dividing the raw delta by a
   2128       1.3       scw 	 * scale factor. Any remainder from the previous scale result
   2129       1.3       scw 	 * is added to the current delta before scaling.
   2130       1.3       scw 	 * This prevents loss of resolution for very small/slow
   2131       1.3       scw 	 * movements of the finger.
   2132       1.3       scw 	 */
   2133       1.3       scw 	delta += *remp;
   2134       1.3       scw 	rv = delta / scale;
   2135       1.3       scw 	*remp = delta % scale;
   2136       1.3       scw 
   2137       1.3       scw 	return (rv);
   2138       1.3       scw }
   2139       1.3       scw 
   2140       1.9     perry static inline void
   2141       1.3       scw synaptics_movement(struct synaptics_softc *sc, struct synaptics_packet *sp,
   2142      1.73     blymn     int *dxp, int *dyp, int *dzp, int *sdxp, int *sdyp, int *sdzp)
   2143       1.3       scw {
   2144      1.73     blymn 	int dx, dy, dz, sdx, sdy, sdz, edge;
   2145      1.44     blymn 
   2146      1.73     blymn 	dx = dy = dz = sdx = sdy = sdz = 0;
   2147       1.3       scw 
   2148       1.3       scw 	/*
   2149      1.73     blymn 	 * Compute the next values of dx, dy, dz, sdx, sdy, sdz.
   2150       1.3       scw 	 */
   2151      1.73     blymn 	dx = synaptics_filter_policy(sc, 0,
   2152      1.73     blymn 	    sc->history_x[SYN_PRIMARY_FINGER], sp->sp_x,
   2153      1.73     blymn 	    sc->packet_count[SYN_PRIMARY_FINGER]);
   2154      1.73     blymn 	dy = synaptics_filter_policy(sc, 0,
   2155      1.73     blymn 	    sc->history_y[SYN_PRIMARY_FINGER], sp->sp_y,
   2156      1.73     blymn 	    sc->packet_count[SYN_PRIMARY_FINGER]);
   2157      1.80   mlelstv 	sc->packet_count[SYN_PRIMARY_FINGER]++;
   2158      1.73     blymn 
   2159      1.73     blymn 	if (sp->sp_finger_count > 1) {
   2160      1.73     blymn 		sdx = synaptics_filter_policy(sc, 1,
   2161      1.73     blymn 		    sc->history_x[SYN_SECONDARY_FINGER], sp->sp_sx,
   2162      1.73     blymn 		    sc->packet_count[SYN_SECONDARY_FINGER]);
   2163      1.73     blymn 		sdy = synaptics_filter_policy(sc, 1,
   2164      1.73     blymn 		    sc->history_y[SYN_SECONDARY_FINGER], sp->sp_sy,
   2165      1.73     blymn 		    sc->packet_count[SYN_SECONDARY_FINGER]);
   2166      1.80   mlelstv 		sc->packet_count[SYN_SECONDARY_FINGER]++;
   2167      1.73     blymn 		DPRINTF(10, sc, "synaptics_movement: dx %d dy %d sdx %d sdy %d\n",
   2168      1.73     blymn 		    dx, dy, sdx, sdy);
   2169      1.73     blymn 	}
   2170       1.3       scw 
   2171       1.3       scw 	/*
   2172       1.3       scw 	 * If we're dealing with a drag gesture, and the finger moves to
   2173       1.3       scw 	 * the edge of the touchpad, apply edge motion emulation if it
   2174       1.3       scw 	 * is enabled.
   2175       1.3       scw 	 */
   2176       1.3       scw 	if (synaptics_edge_motion_delta && SYN_IS_DRAG(sc->gesture_type)) {
   2177       1.3       scw 		edge = synaptics_check_edge(sp->sp_x, sp->sp_y);
   2178       1.3       scw 
   2179       1.3       scw 		if (edge & SYN_EDGE_LEFT)
   2180       1.3       scw 			dx -= synaptics_edge_motion(sc, dx, 1);
   2181       1.3       scw 		if (edge & SYN_EDGE_RIGHT)
   2182       1.3       scw 			dx += synaptics_edge_motion(sc, dx, -1);
   2183       1.3       scw 		if (edge & SYN_EDGE_BOTTOM)
   2184       1.3       scw 			dy -= synaptics_edge_motion(sc, dy, 1);
   2185       1.3       scw 		if (edge & SYN_EDGE_TOP)
   2186       1.3       scw 			dy += synaptics_edge_motion(sc, dy, -1);
   2187      1.73     blymn 
   2188      1.73     blymn 		if (sp->sp_finger_count > 1) {
   2189      1.73     blymn 			edge = synaptics_check_edge(sp->sp_sx, sp->sp_sy);
   2190      1.73     blymn 
   2191      1.73     blymn 			if (edge & SYN_EDGE_LEFT)
   2192      1.73     blymn 				sdx -= synaptics_edge_motion(sc, sdx, 1);
   2193      1.73     blymn 			if (edge & SYN_EDGE_RIGHT)
   2194      1.73     blymn 				sdx += synaptics_edge_motion(sc, sdx, -1);
   2195      1.73     blymn 			if (edge & SYN_EDGE_BOTTOM)
   2196      1.73     blymn 				sdy -= synaptics_edge_motion(sc, sdy, 1);
   2197      1.73     blymn 			if (edge & SYN_EDGE_TOP)
   2198      1.73     blymn 				sdy += synaptics_edge_motion(sc, sdy, -1);
   2199      1.73     blymn 		}
   2200       1.3       scw 	}
   2201       1.3       scw 
   2202       1.3       scw 	/*
   2203      1.44     blymn 	 * Apply scaling to the deltas
   2204       1.3       scw 	 */
   2205      1.73     blymn 	dx = synaptics_scale(dx, synaptics_scale_x,
   2206      1.73     blymn 	    &sc->rem_x[SYN_PRIMARY_FINGER]);
   2207      1.73     blymn 	dy = synaptics_scale(dy, synaptics_scale_y,
   2208      1.73     blymn 	    &sc->rem_y[SYN_PRIMARY_FINGER]);
   2209      1.73     blymn 	dz = synaptics_scale(dz, synaptics_scale_z,
   2210      1.73     blymn 	    &sc->rem_z[SYN_PRIMARY_FINGER]);
   2211      1.73     blymn 
   2212      1.73     blymn 	if (sp->sp_finger_count > 1) {
   2213      1.73     blymn 		sdx = synaptics_scale(sdx, synaptics_scale_x,
   2214      1.73     blymn 		    &sc->rem_x[SYN_SECONDARY_FINGER]);
   2215      1.73     blymn 		sdy = synaptics_scale(sdy, synaptics_scale_y,
   2216      1.73     blymn 		    &sc->rem_y[SYN_SECONDARY_FINGER]);
   2217      1.73     blymn 		sdz = synaptics_scale(sdz, synaptics_scale_z,
   2218      1.73     blymn 		    &sc->rem_z[SYN_SECONDARY_FINGER]);
   2219      1.73     blymn 
   2220      1.73     blymn 		DPRINTF(10, sc,
   2221      1.73     blymn 		    "synaptics_movement 2: dx %d dy %d sdx %d sdy %d\n",
   2222      1.73     blymn 		    dx, dy, sdx, sdy);
   2223      1.73     blymn 	}
   2224       1.1  christos 
   2225       1.3       scw 	/*
   2226       1.3       scw 	 * Clamp deltas to specified maximums.
   2227       1.3       scw 	 */
   2228      1.43     blymn 	if (abs(dx) > synaptics_max_speed_x)
   2229      1.43     blymn 		dx = ((dx >= 0)? 1 : -1) * synaptics_max_speed_x;
   2230      1.43     blymn 	if (abs(dy) > synaptics_max_speed_y)
   2231      1.43     blymn 		dy = ((dy >= 0)? 1 : -1) * synaptics_max_speed_y;
   2232      1.44     blymn 	if (abs(dz) > synaptics_max_speed_z)
   2233      1.44     blymn 		dz = ((dz >= 0)? 1 : -1) * synaptics_max_speed_z;
   2234       1.1  christos 
   2235      1.73     blymn 	if (sp->sp_finger_count > 1) {
   2236      1.73     blymn 		if (abs(sdx) > synaptics_max_speed_x)
   2237      1.73     blymn 			sdx = ((sdx >= 0)? 1 : -1) * synaptics_max_speed_x;
   2238      1.73     blymn 		if (abs(dy) > synaptics_max_speed_y)
   2239      1.73     blymn 			sdy = ((sdy >= 0)? 1 : -1) * synaptics_max_speed_y;
   2240      1.73     blymn 		if (abs(sdz) > synaptics_max_speed_z)
   2241      1.73     blymn 			sdz = ((sdz >= 0)? 1 : -1) * synaptics_max_speed_z;
   2242      1.73     blymn 	}
   2243      1.73     blymn 
   2244       1.3       scw 	*dxp = dx;
   2245       1.3       scw 	*dyp = dy;
   2246      1.44     blymn 	*dzp = dz;
   2247      1.73     blymn 	*sdxp = sdx;
   2248      1.73     blymn 	*sdyp = sdy;
   2249      1.73     blymn 	*sdzp = sdz;
   2250       1.1  christos 
   2251       1.3       scw }
   2252       1.1  christos 
   2253       1.3       scw static void
   2254       1.3       scw pms_synaptics_process_packet(struct pms_softc *psc, struct synaptics_packet *sp)
   2255       1.3       scw {
   2256       1.3       scw 	struct synaptics_softc *sc = &psc->u.synaptics;
   2257      1.73     blymn 	int dx, dy, dz, sdx, sdy, sdz;
   2258       1.3       scw 	int fingers, palm, buttons, changed;
   2259      1.72       nia 	int s;
   2260       1.1  christos 
   2261      1.73     blymn 	sdx = sdy = sdz = 0;
   2262      1.73     blymn 
   2263       1.3       scw 	/*
   2264       1.3       scw 	 * Do Z-axis emulation using up/down buttons if required.
   2265       1.3       scw 	 * Note that the pad will send a one second burst of packets
   2266       1.3       scw 	 * when an up/down button is pressed and held. At the moment
   2267       1.3       scw 	 * we don't deal with auto-repeat, so convert the burst into
   2268       1.3       scw 	 * a one-shot.
   2269       1.3       scw 	 */
   2270       1.3       scw 	dz = 0;
   2271       1.3       scw 	if (synaptics_up_down_emul == 2) {
   2272       1.3       scw 		if (sc->up_down == 0) {
   2273       1.3       scw 			if (sp->sp_up && sp->sp_down) {
   2274       1.3       scw 				sp->sp_middle = 1;
   2275      1.68       nia 			} else if (sp->sp_up) {
   2276       1.3       scw 				dz = -synaptics_up_down_motion_delta;
   2277      1.68       nia 			} else if (sp->sp_down) {
   2278       1.3       scw 				dz = synaptics_up_down_motion_delta;
   2279      1.68       nia 			}
   2280       1.1  christos 		}
   2281       1.1  christos 
   2282       1.3       scw 		sc->up_down = sp->sp_up | sp->sp_down;
   2283       1.3       scw 		sp->sp_up = sp->sp_down = 0;
   2284       1.3       scw 	}
   2285       1.3       scw 
   2286       1.3       scw 	/*
   2287       1.3       scw 	 * Determine whether or not a finger is on the pad.
   2288       1.3       scw 	 * On some pads, this will return the number of fingers
   2289       1.3       scw 	 * detected.
   2290       1.3       scw 	 */
   2291       1.3       scw 	fingers = synaptics_finger_detect(sc, sp, &palm);
   2292       1.3       scw 
   2293       1.3       scw 	/*
   2294      1.73     blymn 	 * Do gesture processing only if we didn't detect a palm.
   2295       1.3       scw 	 */
   2296      1.73     blymn 	if (palm == 0)
   2297       1.3       scw 		synaptics_gesture_detect(sc, sp, fingers);
   2298       1.3       scw 	else
   2299       1.3       scw 		sc->gesture_type = sc->gesture_buttons = 0;
   2300       1.1  christos 
   2301       1.3       scw 	/*
   2302       1.3       scw 	 * Determine what buttons to report
   2303       1.3       scw 	 */
   2304       1.3       scw 	buttons = (sp->sp_left ? 0x1 : 0) |
   2305       1.3       scw 	    (sp->sp_middle ? 0x2 : 0) |
   2306       1.3       scw 	    (sp->sp_right ? 0x4 : 0) |
   2307       1.3       scw 	    (sp->sp_up ? 0x8 : 0) |
   2308       1.3       scw 	    (sp->sp_down ? 0x10 : 0);
   2309      1.14   mlelstv 	changed = buttons ^ (psc->buttons & 0x1f);
   2310      1.14   mlelstv 	psc->buttons ^= changed;
   2311       1.1  christos 
   2312       1.3       scw 	sc->prev_fingers = fingers;
   2313       1.1  christos 
   2314       1.3       scw 	/*
   2315      1.34     blymn 	 * Do movement processing IFF we have a single finger and no palm or
   2316      1.34     blymn 	 * a secondary finger and no palm.
   2317       1.3       scw 	 */
   2318      1.36  jmcneill 	if (palm == 0 && synaptics_movement_enable) {
   2319      1.73     blymn 		if (fingers > 0) {
   2320      1.73     blymn 			synaptics_movement(sc, sp, &dx, &dy, &dz, &sdx, &sdy,
   2321      1.73     blymn 			    &sdz);
   2322      1.73     blymn 
   2323      1.72       nia 			/*
   2324      1.73     blymn 			 * Check if there are two fingers, if there are then
   2325      1.73     blymn 			 * check if we have a clickpad, if we do then we
   2326      1.73     blymn 			 * don't scroll iff one of the fingers is in the
   2327      1.73     blymn 			 * button region.  Otherwise interpret as a scroll
   2328      1.72       nia 			 */
   2329      1.73     blymn 			if (sp->sp_finger_count >= 2 && sc->gesture_type == 0 ) {
   2330      1.73     blymn 				if (!(sc->flags & SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD) ||
   2331      1.73     blymn 				    ((sc->flags & SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD) &&
   2332      1.73     blymn 				    (sp->sp_y > synaptics_button_boundary)  &&
   2333      1.73     blymn 				    (sp->sp_sy > synaptics_button_boundary))) {
   2334      1.73     blymn 					s = spltty();
   2335      1.73     blymn 					wsmouse_precision_scroll(
   2336      1.73     blymn 					    psc->sc_wsmousedev, dx, dy);
   2337      1.73     blymn 					splx(s);
   2338      1.73     blymn 					return;
   2339      1.73     blymn 				}
   2340      1.73     blymn 			}
   2341      1.73     blymn 
   2342      1.72       nia 			/*
   2343      1.73     blymn 			 * Allow user to turn off movements in the button
   2344      1.73     blymn 			 * region of a click pad.
   2345      1.72       nia 			 */
   2346      1.73     blymn 			if (sc->flags & SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD) {
   2347      1.73     blymn 				if ((sp->sp_y < synaptics_button_boundary) &&
   2348      1.73     blymn 				    (!synaptics_button_region_movement)) {
   2349      1.73     blymn 					dx = dy = dz = 0;
   2350      1.73     blymn 				}
   2351      1.73     blymn 
   2352      1.73     blymn 				if ((sp->sp_sy < synaptics_button_boundary) &&
   2353      1.73     blymn 				    (!synaptics_button_region_movement)) {
   2354      1.73     blymn 					sdx = sdy = sdz = 0;
   2355      1.73     blymn 				}
   2356      1.73     blymn 			}
   2357      1.73     blymn 
   2358      1.73     blymn 			/* Now work out which finger to report, just
   2359      1.73     blymn 			 * go with the one that has moved the most.
   2360      1.73     blymn 			 */
   2361      1.73     blymn 			if ((abs(dx) + abs(dy) + abs(dz)) <
   2362      1.73     blymn 			    (abs(sdx) + abs(sdy) + abs(sdz))) {
   2363      1.73     blymn 				/* secondary finger wins */
   2364      1.73     blymn 				dx = sdx;
   2365      1.73     blymn 				dy = sdy;
   2366      1.73     blymn 				dz = sdz;
   2367      1.73     blymn 			}
   2368      1.34     blymn 		} else {
   2369      1.34     blymn 			/*
   2370      1.34     blymn 			 * No valid finger. Therefore no movement.
   2371      1.34     blymn 			 */
   2372      1.73     blymn 			sc->rem_x[SYN_PRIMARY_FINGER] = 0;
   2373      1.73     blymn 			sc->rem_y[SYN_PRIMARY_FINGER] = 0;
   2374      1.73     blymn 			sc->rem_x[SYN_SECONDARY_FINGER] = 0;
   2375      1.73     blymn 			sc->rem_y[SYN_SECONDARY_FINGER] = 0;
   2376      1.34     blymn 			dx = dy = 0;
   2377      1.80   mlelstv 
   2378      1.80   mlelstv 			sc->packet_count[SYN_PRIMARY_FINGER] = 0;
   2379      1.80   mlelstv 			sc->packet_count[SYN_SECONDARY_FINGER] = 0;
   2380      1.34     blymn 		}
   2381      1.34     blymn 	} else {
   2382       1.3       scw 		/*
   2383       1.3       scw 		 * No valid finger. Therefore no movement.
   2384       1.3       scw 		 */
   2385      1.73     blymn 		sc->rem_x[SYN_PRIMARY_FINGER] = 0;
   2386      1.73     blymn 		sc->rem_y[SYN_PRIMARY_FINGER] = 0;
   2387      1.73     blymn 		sc->rem_z[SYN_PRIMARY_FINGER] = 0;
   2388      1.44     blymn 		dx = dy = dz = 0;
   2389       1.3       scw 	}
   2390       1.1  christos 
   2391      1.73     blymn 	DPRINTF(10, sc,
   2392      1.73     blymn 	    "pms_synaptics_process_packet: dx %d dy %d dz %d sdx %d sdy %d sdz %d\n",
   2393      1.73     blymn 	    dx, dy, dz, sdx, sdy, sdz);
   2394      1.73     blymn 
   2395       1.3       scw 	/*
   2396       1.3       scw 	 * Pass the final results up to wsmouse_input() if necessary.
   2397       1.3       scw 	 */
   2398       1.3       scw 	if (dx || dy || dz || changed) {
   2399      1.14   mlelstv 		buttons = (psc->buttons & 0x1f) | ((psc->buttons >> 5) & 0x7);
   2400       1.3       scw 		s = spltty();
   2401      1.12    plunky 		wsmouse_input(psc->sc_wsmousedev,
   2402      1.12    plunky 				buttons,
   2403      1.12    plunky 				dx, dy, dz, 0,
   2404      1.12    plunky 		    		WSMOUSE_INPUT_DELTA);
   2405       1.3       scw 		splx(s);
   2406       1.1  christos 	}
   2407       1.1  christos }
   2408