1*bd69b09fSmlelstv /* $NetBSD: synaptics.c,v 1.84 2024/07/19 04:48:13 mlelstv Exp $ */ 281e1a623Schristos 381e1a623Schristos /* 471d14cdeSscw * Copyright (c) 2005, Steve C. Woodford 581e1a623Schristos * Copyright (c) 2004, Ales Krenek 681e1a623Schristos * Copyright (c) 2004, Kentaro A. Kurahone 781e1a623Schristos * All rights reserved. 881e1a623Schristos * 981e1a623Schristos * Redistribution and use in source and binary forms, with or without 1081e1a623Schristos * modification, are permitted provided that the following conditions 1181e1a623Schristos * are met: 1281e1a623Schristos * 1381e1a623Schristos * * Redistributions of source code must retain the above copyright 1481e1a623Schristos * notice, this list of conditions and the following disclaimer. 1581e1a623Schristos * * Redistributions in binary form must reproduce the above 1681e1a623Schristos * copyright notice, this list of conditions and the following 1781e1a623Schristos * disclaimer in the documentation and/or other materials provided 1881e1a623Schristos * with the distribution. 1981e1a623Schristos * * Neither the name of the authors nor the names of its 2081e1a623Schristos * contributors may be used to endorse or promote products derived 2181e1a623Schristos * from this software without specific prior written permission. 2281e1a623Schristos * 2381e1a623Schristos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2481e1a623Schristos * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2581e1a623Schristos * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2681e1a623Schristos * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2781e1a623Schristos * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2881e1a623Schristos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2981e1a623Schristos * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 3081e1a623Schristos * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 3181e1a623Schristos * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3281e1a623Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 3381e1a623Schristos * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3481e1a623Schristos * POSSIBILITY OF SUCH DAMAGE. 3581e1a623Schristos * 3681e1a623Schristos */ 3781e1a623Schristos 3871d14cdeSscw /* 3971d14cdeSscw * TODO: 4071d14cdeSscw * - Make the sysctl values per-instance instead of global. 4171d14cdeSscw * - Consider setting initial scaling factors at runtime according 4271d14cdeSscw * to the values returned by the 'Read Resolutions' command. 4371d14cdeSscw * - Support the serial protocol (we only support PS/2 for now) 4471d14cdeSscw * - Support auto-repeat for up/down button Z-axis emulation. 4571d14cdeSscw * - Maybe add some more gestures (can we use Palm support somehow?) 4671d14cdeSscw */ 4771d14cdeSscw 482d0fc26cSscw #include "opt_pms.h" 492d0fc26cSscw 5081e1a623Schristos #include <sys/cdefs.h> 51*bd69b09fSmlelstv __KERNEL_RCSID(0, "$NetBSD: synaptics.c,v 1.84 2024/07/19 04:48:13 mlelstv Exp $"); 5281e1a623Schristos 5381e1a623Schristos #include <sys/param.h> 5481e1a623Schristos #include <sys/systm.h> 5581e1a623Schristos #include <sys/device.h> 5681e1a623Schristos #include <sys/ioctl.h> 5781e1a623Schristos #include <sys/sysctl.h> 5881e1a623Schristos #include <sys/kernel.h> 591d247bb8Suebayasi #include <sys/proc.h> 6081e1a623Schristos 61a2a38285Sad #include <sys/bus.h> 6281e1a623Schristos 6381e1a623Schristos #include <dev/pckbport/pckbportvar.h> 6481e1a623Schristos 6581e1a623Schristos #include <dev/pckbport/synapticsreg.h> 6681e1a623Schristos #include <dev/pckbport/synapticsvar.h> 6781e1a623Schristos 6881e1a623Schristos #include <dev/pckbport/pmsreg.h> 6981e1a623Schristos #include <dev/pckbport/pmsvar.h> 7081e1a623Schristos 7181e1a623Schristos #include <dev/wscons/wsconsio.h> 7281e1a623Schristos #include <dev/wscons/wsmousevar.h> 7381e1a623Schristos 7471d14cdeSscw /* 7571d14cdeSscw * Absolute-mode packets are decoded and passed around using 7671d14cdeSscw * the following structure. 7771d14cdeSscw */ 7871d14cdeSscw struct synaptics_packet { 7971d14cdeSscw signed short sp_x; /* Unscaled absolute X/Y coordinates */ 8071d14cdeSscw signed short sp_y; 8171d14cdeSscw u_char sp_z; /* Z (pressure) */ 824799b3aeSblymn signed short sp_sx; /* Unscaled absolute X/Y coordinates */ 834799b3aeSblymn signed short sp_sy; /* for secondary finger */ 844799b3aeSblymn u_char sp_sz; /* Z (pressure) */ 8571d14cdeSscw u_char sp_w; /* W (contact patch width) */ 864799b3aeSblymn u_char sp_primary; /* seen primary finger packet */ 874799b3aeSblymn u_char sp_secondary; /* seen secondary finger packet */ 884799b3aeSblymn u_char sp_finger_status; /* seen extended finger packet */ 894799b3aeSblymn u_char sp_finger_count; /* number of fingers seen */ 9071d14cdeSscw char sp_left; /* Left mouse button status */ 9171d14cdeSscw char sp_right; /* Right mouse button status */ 9271d14cdeSscw char sp_middle; /* Middle button status (possibly emulated) */ 9371d14cdeSscw char sp_up; /* Up button status */ 9471d14cdeSscw char sp_down; /* Down button status */ 9571d14cdeSscw }; 9671d14cdeSscw 9781e1a623Schristos static void pms_synaptics_input(void *, int); 9871d14cdeSscw static void pms_synaptics_process_packet(struct pms_softc *, 9971d14cdeSscw struct synaptics_packet *); 100ef8015e6Schristos static void pms_sysctl_synaptics(struct sysctllog **); 10181e1a623Schristos static int pms_sysctl_synaptics_verify(SYSCTLFN_ARGS); 10281e1a623Schristos 103eefea853Sjakllsch /* Controlled by sysctl. */ 104dd302e79Snia static int synaptics_up_down_emul = 3; 10571d14cdeSscw static int synaptics_up_down_motion_delta = 1; 10671d14cdeSscw static int synaptics_gesture_move = 200; 10771d14cdeSscw static int synaptics_gesture_length = 20; 10871d14cdeSscw static int synaptics_edge_left = SYNAPTICS_EDGE_LEFT; 10971d14cdeSscw static int synaptics_edge_right = SYNAPTICS_EDGE_RIGHT; 11071d14cdeSscw static int synaptics_edge_top = SYNAPTICS_EDGE_TOP; 11171d14cdeSscw static int synaptics_edge_bottom = SYNAPTICS_EDGE_BOTTOM; 11271d14cdeSscw static int synaptics_edge_motion_delta = 32; 11371d14cdeSscw static u_int synaptics_finger_high = SYNAPTICS_FINGER_LIGHT + 5; 11471d14cdeSscw static u_int synaptics_finger_low = SYNAPTICS_FINGER_LIGHT - 10; 115043f83d9Smlelstv static int synaptics_hscroll_pct = 0; 116043f83d9Smlelstv static int synaptics_vscroll_pct = 0; 117043f83d9Smlelstv static int synaptics_button_pct = 0; 118043f83d9Smlelstv static int synaptics_button_boundary = SYNAPTICS_EDGE_BOTTOM; 119e29f2f73Sblymn static int synaptics_button2; 120e29f2f73Sblymn static int synaptics_button3; 12171d14cdeSscw static int synaptics_two_fingers_emul = 0; 122dcfec4cdSnia static int synaptics_scale_x = 8; 123dcfec4cdSnia static int synaptics_scale_y = 8; 124b5324420Snia static int synaptics_scale_z = 32; 12571d14cdeSscw static int synaptics_max_speed_x = 32; 12671d14cdeSscw static int synaptics_max_speed_y = 32; 127b5324420Snia static int synaptics_max_speed_z = 2; 12871d14cdeSscw static int synaptics_movement_threshold = 4; 12926f4df04Sjmcneill static int synaptics_movement_enable = 1; 1304799b3aeSblymn static int synaptics_button_region_movement = 1; 1319c80624bSnia static bool synaptics_aux_mid_button_scroll = TRUE; 1323c5d2f91Sriastradh static int synaptics_debug = 0; 1333c5d2f91Sriastradh 1344799b3aeSblymn #define DPRINTF(LEVEL, SC, FMT, ARGS...) do \ 1353c5d2f91Sriastradh { \ 1364799b3aeSblymn if (synaptics_debug >= LEVEL) { \ 1373c5d2f91Sriastradh struct pms_softc *_dprintf_psc = \ 1383c5d2f91Sriastradh container_of((SC), struct pms_softc, u.synaptics); \ 1393c5d2f91Sriastradh device_printf(_dprintf_psc->sc_dev, FMT, ##ARGS); \ 1403c5d2f91Sriastradh } \ 1413c5d2f91Sriastradh } while (0) 14281e1a623Schristos 14381e1a623Schristos /* Sysctl nodes. */ 144fe90bab4Sblymn static int synaptics_button_boundary_nodenum; 145fe90bab4Sblymn static int synaptics_button2_nodenum; 146fe90bab4Sblymn static int synaptics_button3_nodenum; 14771d14cdeSscw static int synaptics_up_down_emul_nodenum; 14871d14cdeSscw static int synaptics_up_down_motion_delta_nodenum; 14971d14cdeSscw static int synaptics_gesture_move_nodenum; 15071d14cdeSscw static int synaptics_gesture_length_nodenum; 15171d14cdeSscw static int synaptics_edge_left_nodenum; 15271d14cdeSscw static int synaptics_edge_right_nodenum; 15371d14cdeSscw static int synaptics_edge_top_nodenum; 15471d14cdeSscw static int synaptics_edge_bottom_nodenum; 15571d14cdeSscw static int synaptics_edge_motion_delta_nodenum; 15671d14cdeSscw static int synaptics_finger_high_nodenum; 15771d14cdeSscw static int synaptics_finger_low_nodenum; 15871d14cdeSscw static int synaptics_two_fingers_emul_nodenum; 15971d14cdeSscw static int synaptics_scale_x_nodenum; 16071d14cdeSscw static int synaptics_scale_y_nodenum; 161abbf616bSblymn static int synaptics_scale_z_nodenum; 16271d14cdeSscw static int synaptics_max_speed_x_nodenum; 16371d14cdeSscw static int synaptics_max_speed_y_nodenum; 164abbf616bSblymn static int synaptics_max_speed_z_nodenum; 16571d14cdeSscw static int synaptics_movement_threshold_nodenum; 16626f4df04Sjmcneill static int synaptics_movement_enable_nodenum; 1674799b3aeSblymn static int synaptics_button_region_movement_nodenum; 1689c80624bSnia static int synaptics_aux_mid_button_scroll_nodenum; 169043f83d9Smlelstv static int synaptics_hscroll_pct_nodenum; 170043f83d9Smlelstv static int synaptics_vscroll_pct_nodenum; 171e29f2f73Sblymn static int synaptics_button_pct_nodenum; 172e29f2f73Sblymn 173e29f2f73Sblymn /* 174e29f2f73Sblymn * copy of edges so we can recalculate edge limit if there is 175e29f2f73Sblymn * vertical scroll region 176e29f2f73Sblymn */ 177043f83d9Smlelstv static int synaptics_true_edge_right; 178043f83d9Smlelstv static int synaptics_true_edge_bottom; 179e29f2f73Sblymn 180043f83d9Smlelstv /* 181043f83d9Smlelstv * invalid old values, recalculate everything 182043f83d9Smlelstv */ 183043f83d9Smlelstv static int synaptics_old_vscroll_pct = -1; 184043f83d9Smlelstv static int synaptics_old_hscroll_pct = -1; 185043f83d9Smlelstv static int synaptics_old_button_pct = -1; 186043f83d9Smlelstv static int synaptics_old_button_boundary = -1; 187043f83d9Smlelstv static int synaptics_old_edge_right = -1; 188043f83d9Smlelstv static int synaptics_old_edge_bottom = -1; 18981e1a623Schristos 1904799b3aeSblymn /* 1914799b3aeSblymn * This holds the processed packet data, it is global because multiple 1924799b3aeSblymn * packets from the trackpad may be processed when handling multiple 1934799b3aeSblymn * fingers on the trackpad to gather all the data. 1944799b3aeSblymn */ 1954799b3aeSblymn static struct synaptics_packet packet; 1964799b3aeSblymn 197fe90bab4Sblymn static int 198fe90bab4Sblymn synaptics_poll_cmd(struct pms_softc *psc, ...) 199fe90bab4Sblymn { 200fe90bab4Sblymn u_char cmd[4]; 201fe90bab4Sblymn size_t i; 202fe90bab4Sblymn va_list ap; 203fe90bab4Sblymn 204fe90bab4Sblymn va_start(ap, psc); 205fe90bab4Sblymn 206fe90bab4Sblymn for (i = 0; i < __arraycount(cmd); i++) 207fe90bab4Sblymn if ((cmd[i] = (u_char)va_arg(ap, int)) == 0) 208fe90bab4Sblymn break; 209fe90bab4Sblymn va_end(ap); 210fe90bab4Sblymn 211fe90bab4Sblymn int res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd, i, 0, 212fe90bab4Sblymn NULL, 0); 213fe90bab4Sblymn if (res) 214043f83d9Smlelstv device_printf(psc->sc_dev, "command error %#x\n", cmd[0]); 215fe90bab4Sblymn return res; 216fe90bab4Sblymn } 217fe90bab4Sblymn 218fe90bab4Sblymn static int 219fe90bab4Sblymn synaptics_poll_reset(struct pms_softc *psc) 220fe90bab4Sblymn { 221fe90bab4Sblymn u_char resp[2]; 222fe90bab4Sblymn int res; 223fe90bab4Sblymn 224fe90bab4Sblymn u_char cmd[1] = { PMS_RESET }; 225fe90bab4Sblymn res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd, 1, 2, 226fe90bab4Sblymn resp, 1); 227043f83d9Smlelstv DPRINTF(10, &psc->u.synaptics, "reset %d 0x%02x 0x%02x\n", 228fe90bab4Sblymn res, resp[0], resp[1]); 229fe90bab4Sblymn return res; 230fe90bab4Sblymn } 231fe90bab4Sblymn 232fe90bab4Sblymn static int 233b7f7faa3Smaya synaptics_special_read(struct pms_softc *psc, u_char slice, u_char resp[3]) 234fe90bab4Sblymn { 235fe90bab4Sblymn u_char cmd[1] = { PMS_SEND_DEV_STATUS }; 236fe90bab4Sblymn int res = pms_sliced_command(psc->sc_kbctag, psc->sc_kbcslot, slice); 237fe90bab4Sblymn 238fe90bab4Sblymn return res | pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, 239fe90bab4Sblymn cmd, 1, 3, resp, 0); 240fe90bab4Sblymn } 241fe90bab4Sblymn 242b7f7faa3Smaya static int 243b7f7faa3Smaya synaptics_special_write(struct pms_softc *psc, u_char command, u_char arg) 244b7f7faa3Smaya { 245b7f7faa3Smaya int res = pms_sliced_command(psc->sc_kbctag, psc->sc_kbcslot, arg); 246b7f7faa3Smaya if (res) 247b7f7faa3Smaya return res; 248b7f7faa3Smaya 249b7f7faa3Smaya u_char cmd[2]; 250b7f7faa3Smaya cmd[0] = PMS_SET_SAMPLE; 251b7f7faa3Smaya cmd[1] = command; 252b7f7faa3Smaya res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, 253b7f7faa3Smaya cmd, 2, 0, NULL, 0); 254b7f7faa3Smaya return res; 255b7f7faa3Smaya } 256b7f7faa3Smaya 257043f83d9Smlelstv static int 258043f83d9Smlelstv synaptics_value(int pct, int low, int high) 259043f83d9Smlelstv { 260043f83d9Smlelstv return low + pct * (high - low) / 100UL; 261043f83d9Smlelstv } 262043f83d9Smlelstv 263043f83d9Smlelstv static int 264043f83d9Smlelstv synaptics_percentage(int val, int low, int high) 265043f83d9Smlelstv { 266043f83d9Smlelstv return ((val - low) * 100UL + high - low - 1) / (high - low); 267043f83d9Smlelstv } 268043f83d9Smlelstv 269ef2e77d3Schristos static void 270e29f2f73Sblymn pms_synaptics_set_boundaries(void) 271e29f2f73Sblymn { 272043f83d9Smlelstv if (synaptics_vscroll_pct != synaptics_old_vscroll_pct ) { 273043f83d9Smlelstv synaptics_edge_right = synaptics_value( 274043f83d9Smlelstv 100 - synaptics_vscroll_pct, 275043f83d9Smlelstv synaptics_edge_left, 276043f83d9Smlelstv synaptics_true_edge_right); 277043f83d9Smlelstv synaptics_old_vscroll_pct = synaptics_vscroll_pct; 278e29f2f73Sblymn } 279e29f2f73Sblymn 280043f83d9Smlelstv if (synaptics_edge_right != synaptics_old_edge_right) { 281043f83d9Smlelstv if (synaptics_edge_right >= synaptics_true_edge_right) { 282043f83d9Smlelstv synaptics_vscroll_pct = 0; 283043f83d9Smlelstv synaptics_edge_right = synaptics_true_edge_right; 284e29f2f73Sblymn } else { 285043f83d9Smlelstv synaptics_vscroll_pct = 100 - synaptics_percentage( 286043f83d9Smlelstv synaptics_edge_right, 287043f83d9Smlelstv synaptics_edge_left, 288043f83d9Smlelstv synaptics_true_edge_right); 289e29f2f73Sblymn } 290043f83d9Smlelstv synaptics_old_vscroll_pct = synaptics_vscroll_pct; 291043f83d9Smlelstv synaptics_old_edge_right = synaptics_edge_right; 292e29f2f73Sblymn } 293e29f2f73Sblymn 294043f83d9Smlelstv if (synaptics_hscroll_pct != synaptics_old_hscroll_pct ) { 295043f83d9Smlelstv synaptics_edge_bottom = synaptics_value( 296043f83d9Smlelstv synaptics_hscroll_pct, 297043f83d9Smlelstv synaptics_true_edge_bottom, 298043f83d9Smlelstv synaptics_edge_top); 299043f83d9Smlelstv synaptics_old_hscroll_pct = synaptics_hscroll_pct; 300e29f2f73Sblymn } 301e29f2f73Sblymn 302043f83d9Smlelstv if (synaptics_edge_bottom != synaptics_old_edge_bottom) { 303043f83d9Smlelstv if (synaptics_edge_bottom <= synaptics_true_edge_bottom) { 304043f83d9Smlelstv synaptics_hscroll_pct = 0; 305043f83d9Smlelstv synaptics_edge_bottom = synaptics_true_edge_bottom; 306e29f2f73Sblymn } else { 307043f83d9Smlelstv synaptics_hscroll_pct = synaptics_percentage( 308043f83d9Smlelstv synaptics_edge_bottom, 309043f83d9Smlelstv synaptics_true_edge_bottom, 310043f83d9Smlelstv synaptics_edge_top); 311e29f2f73Sblymn } 312043f83d9Smlelstv synaptics_old_hscroll_pct = synaptics_hscroll_pct; 313043f83d9Smlelstv synaptics_old_edge_bottom = synaptics_edge_bottom; 314e29f2f73Sblymn } 315e29f2f73Sblymn 316e29f2f73Sblymn if (synaptics_button_pct != synaptics_old_button_pct) { 317043f83d9Smlelstv synaptics_button_boundary = synaptics_value( 318043f83d9Smlelstv synaptics_button_pct, 319043f83d9Smlelstv synaptics_edge_bottom, 320043f83d9Smlelstv synaptics_edge_top); 321e29f2f73Sblymn synaptics_old_button_pct = synaptics_button_pct; 322e29f2f73Sblymn } 323e29f2f73Sblymn 324e29f2f73Sblymn if (synaptics_button_boundary != synaptics_old_button_boundary) { 325e29f2f73Sblymn if (synaptics_button_boundary <= synaptics_edge_bottom) { 326e29f2f73Sblymn synaptics_button_pct = 0; 327043f83d9Smlelstv synaptics_button_boundary = synaptics_edge_bottom; 328e6ab2e16Smlelstv } else if (synaptics_button_boundary >= synaptics_edge_top) { 329e6ab2e16Smlelstv synaptics_button_pct = 100; 330043f83d9Smlelstv synaptics_button_boundary = synaptics_edge_top; 331e29f2f73Sblymn } else { 332043f83d9Smlelstv synaptics_button_pct = synaptics_percentage( 333043f83d9Smlelstv synaptics_button_boundary, 334043f83d9Smlelstv synaptics_edge_bottom, 335043f83d9Smlelstv synaptics_edge_top); 336e29f2f73Sblymn } 337e6ab2e16Smlelstv synaptics_old_button_pct = synaptics_button_pct; 338e29f2f73Sblymn synaptics_old_button_boundary = synaptics_button_boundary; 339043f83d9Smlelstv } 340e29f2f73Sblymn 341e29f2f73Sblymn synaptics_button2 = synaptics_edge_left + 342e29f2f73Sblymn (synaptics_edge_right - synaptics_edge_left) / 3; 343e29f2f73Sblymn synaptics_button3 = synaptics_edge_left + 344e29f2f73Sblymn 2 * (synaptics_edge_right - synaptics_edge_left) / 3; 345e29f2f73Sblymn 346e29f2f73Sblymn } 347e29f2f73Sblymn 348e29f2f73Sblymn static void 349ef2e77d3Schristos pms_synaptics_probe_extended(struct pms_softc *psc) 350ef2e77d3Schristos { 351ef2e77d3Schristos struct synaptics_softc *sc = &psc->u.synaptics; 352fe90bab4Sblymn u_char resp[3]; 353ef2e77d3Schristos int res; 354ef2e77d3Schristos 355043f83d9Smlelstv DPRINTF(10, sc, 356ef2e77d3Schristos "synaptics_probe: Capabilities 0x%04x.\n", sc->caps); 357ef2e77d3Schristos if (sc->caps & SYNAPTICS_CAP_PASSTHROUGH) 358ef2e77d3Schristos sc->flags |= SYN_FLAG_HAS_PASSTHROUGH; 359ef2e77d3Schristos 360ef2e77d3Schristos if (sc->caps & SYNAPTICS_CAP_PALMDETECT) 361ef2e77d3Schristos sc->flags |= SYN_FLAG_HAS_PALM_DETECT; 362ef2e77d3Schristos 363ef2e77d3Schristos if (sc->caps & SYNAPTICS_CAP_MULTIDETECT) 364ef2e77d3Schristos sc->flags |= SYN_FLAG_HAS_MULTI_FINGER; 365ef2e77d3Schristos 366ef2e77d3Schristos if (sc->caps & SYNAPTICS_CAP_MULTIFINGERREPORT) 367ef2e77d3Schristos sc->flags |= SYN_FLAG_HAS_MULTI_FINGER_REPORT; 368ef2e77d3Schristos 369ef2e77d3Schristos /* Ask about extra buttons to detect up/down. */ 370043f83d9Smlelstv if ((__SHIFTOUT(sc->caps, SYNAPTICS_CAP_EXTNUM) + 0x08) 371ef2e77d3Schristos >= SYNAPTICS_EXTENDED_QUERY) 372ef2e77d3Schristos { 373b7f7faa3Smaya res = synaptics_special_read(psc, SYNAPTICS_EXTENDED_QUERY, resp); 374ef2e77d3Schristos if (res == 0) { 375e29f2f73Sblymn sc->num_buttons = (resp[1] >> 4); 376e29f2f73Sblymn if (sc->num_buttons > 0) 377e29f2f73Sblymn sc->button_mask = sc->button_mask << 378e29f2f73Sblymn ((sc->num_buttons + 1) >> 1); 379e29f2f73Sblymn 380043f83d9Smlelstv DPRINTF(10, sc, 381043f83d9Smlelstv "Extended Buttons: %d.\n", 382e29f2f73Sblymn sc->num_buttons); 383ef2e77d3Schristos 384043f83d9Smlelstv DPRINTF(10, sc, "Extended " 385043f83d9Smlelstv "Capabilities: 0x%02x 0x%02x 0x%02x.\n", 386ef2e77d3Schristos resp[0], resp[1], resp[2]); 387e29f2f73Sblymn if (sc->num_buttons >= 2) { 388ef2e77d3Schristos /* Yes. */ 389ef2e77d3Schristos sc->flags |= SYN_FLAG_HAS_UP_DOWN_BUTTONS; 390ef2e77d3Schristos } 391ef2e77d3Schristos if (resp[0] & 0x1) { 392ef2e77d3Schristos /* Vertical scroll area */ 393ef2e77d3Schristos sc->flags |= SYN_FLAG_HAS_VERTICAL_SCROLL; 394ef2e77d3Schristos } 395ef2e77d3Schristos if (resp[0] & 0x2) { 396ef2e77d3Schristos /* Horizontal scroll area */ 397ef2e77d3Schristos sc->flags |= SYN_FLAG_HAS_HORIZONTAL_SCROLL; 398ef2e77d3Schristos } 399ef2e77d3Schristos if (resp[0] & 0x4) { 400ef2e77d3Schristos /* Extended W-Mode */ 401ef2e77d3Schristos sc->flags |= SYN_FLAG_HAS_EXTENDED_WMODE; 402ef2e77d3Schristos } 403ef2e77d3Schristos } 404ef2e77d3Schristos } 405ef2e77d3Schristos 406ef2e77d3Schristos /* Ask about click pad */ 407043f83d9Smlelstv if ((__SHIFTOUT(sc->caps, SYNAPTICS_CAP_EXTNUM) + 0x08) >= 408ef2e77d3Schristos SYNAPTICS_CONTINUED_CAPABILITIES) 409ef2e77d3Schristos { 410b7f7faa3Smaya res = synaptics_special_read(psc, 411fe90bab4Sblymn SYNAPTICS_CONTINUED_CAPABILITIES, resp); 412fe90bab4Sblymn 413418d39e3Schristos /* 414418d39e3Schristos * The following describes response for the 415418d39e3Schristos * SYNAPTICS_CONTINUED_CAPABILITIES query. 416418d39e3Schristos * 417418d39e3Schristos * byte mask name meaning 418418d39e3Schristos * ---- ---- ------- ------------ 419418d39e3Schristos * 0 0x01 adjustable threshold capacitive button sensitivity 420418d39e3Schristos * can be adjusted 421418d39e3Schristos * 0 0x02 report max query 0x0d gives max coord reported 422418d39e3Schristos * 0 0x04 clearpad sensor is ClearPad product 423418d39e3Schristos * 0 0x08 advanced gesture not particularly meaningful 424418d39e3Schristos * 0 0x10 clickpad bit 0 1-button ClickPad 425418d39e3Schristos * 0 0x60 multifinger mode identifies firmware finger counting 426418d39e3Schristos * (not reporting!) algorithm. 427418d39e3Schristos * Not particularly meaningful 428418d39e3Schristos * 0 0x80 covered pad W clipped to 14, 15 == pad mostly covered 429418d39e3Schristos * 1 0x01 clickpad bit 1 2-button ClickPad 430418d39e3Schristos * 1 0x02 deluxe LED controls touchpad support LED commands 431418d39e3Schristos * ala multimedia control bar 432418d39e3Schristos * 1 0x04 reduced filtering firmware does less filtering on 433418d39e3Schristos * position data, driver should watch 434418d39e3Schristos * for noise. 435418d39e3Schristos * 1 0x08 image sensor image sensor tracks 5 fingers, but only 436418d39e3Schristos * reports 2. 43764669102Sblymn * 1 0x10 uniform clickpad whole clickpad moves instead of being 438418d39e3Schristos * hinged at the top. 439418d39e3Schristos * 1 0x20 report min query 0x0f gives min coord reported 440418d39e3Schristos */ 441ef2e77d3Schristos if (res == 0) { 442538588e8Sblymn uint val = SYN_CCAP_VALUE(resp); 443ef2e77d3Schristos 444043f83d9Smlelstv DPRINTF(10, sc, "Continued " 445043f83d9Smlelstv "Capabilities 0x%02x 0x%02x 0x%02x.\n", 446ef2e77d3Schristos resp[0], resp[1], resp[2]); 447538588e8Sblymn switch (SYN_CCAP_CLICKPAD_TYPE(val)) { 448538588e8Sblymn case 0: /* not a clickpad */ 449538588e8Sblymn break; 450538588e8Sblymn case 1: 451ef2e77d3Schristos sc->flags |= SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD; 452ef2e77d3Schristos break; 453538588e8Sblymn case 2: 454ef2e77d3Schristos sc->flags |= SYN_FLAG_HAS_TWO_BUTTON_CLICKPAD; 455ef2e77d3Schristos break; 456538588e8Sblymn case 3: /* reserved */ 457ef2e77d3Schristos default: 458538588e8Sblymn /* unreached */ 459ef2e77d3Schristos break; 460ef2e77d3Schristos } 461881d45ecSblymn 462881d45ecSblymn if ((val & SYN_CCAP_HAS_ADV_GESTURE_MODE)) 463881d45ecSblymn sc->flags |= SYN_FLAG_HAS_ADV_GESTURE_MODE; 464de9a2f9cSblymn 465de9a2f9cSblymn if ((val & SYN_CCAP_REPORT_MAX)) 466de9a2f9cSblymn sc->flags |= SYN_FLAG_HAS_MAX_REPORT; 467de9a2f9cSblymn 468de9a2f9cSblymn if ((val & SYN_CCAP_REPORT_MIN)) 469de9a2f9cSblymn sc->flags |= SYN_FLAG_HAS_MIN_REPORT; 470ef2e77d3Schristos } 471ef2e77d3Schristos } 472ef2e77d3Schristos } 473ef2e77d3Schristos 474be8d485dSchristos static const struct { 475be8d485dSchristos int bit; 476be8d485dSchristos const char *desc; 477be8d485dSchristos } syn_flags[] = { 478be8d485dSchristos { SYN_FLAG_HAS_EXTENDED_WMODE, "Extended W mode", }, 479be8d485dSchristos { SYN_FLAG_HAS_PASSTHROUGH, "Passthrough", }, 480be8d485dSchristos { SYN_FLAG_HAS_MIDDLE_BUTTON, "Middle button", }, 481be8d485dSchristos { SYN_FLAG_HAS_BUTTONS_4_5, "Buttons 4/5", }, 482be8d485dSchristos { SYN_FLAG_HAS_UP_DOWN_BUTTONS, "Up/down buttons", }, 483be8d485dSchristos { SYN_FLAG_HAS_PALM_DETECT, "Palm detect", }, 484be8d485dSchristos { SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD, "One button click pad", }, 485be8d485dSchristos { SYN_FLAG_HAS_TWO_BUTTON_CLICKPAD, "Two button click pad", }, 486be8d485dSchristos { SYN_FLAG_HAS_VERTICAL_SCROLL, "Vertical scroll", }, 487be8d485dSchristos { SYN_FLAG_HAS_HORIZONTAL_SCROLL, "Horizontal scroll", }, 488be8d485dSchristos { SYN_FLAG_HAS_MULTI_FINGER_REPORT, "Multi-finger Report", }, 489be8d485dSchristos { SYN_FLAG_HAS_MULTI_FINGER, "Multi-finger", }, 490de9a2f9cSblymn { SYN_FLAG_HAS_MAX_REPORT, "Reports max", }, 491de9a2f9cSblymn { SYN_FLAG_HAS_MIN_REPORT, "Reports min", }, 492be8d485dSchristos }; 493be8d485dSchristos 49481e1a623Schristos int 49581e1a623Schristos pms_synaptics_probe_init(void *vsc) 49681e1a623Schristos { 49771d14cdeSscw struct pms_softc *psc = vsc; 49871d14cdeSscw struct synaptics_softc *sc = &psc->u.synaptics; 499eefea853Sjakllsch u_char cmd[1], resp[3]; 50071d14cdeSscw int res, ver_minor, ver_major; 501ef8015e6Schristos struct sysctllog *clog = NULL; 50281e1a623Schristos 5032eb0dcceSjakllsch res = pms_sliced_command(psc->sc_kbctag, psc->sc_kbcslot, 50481e1a623Schristos SYNAPTICS_IDENTIFY_TOUCHPAD); 50581e1a623Schristos cmd[0] = PMS_SEND_DEV_STATUS; 50671d14cdeSscw res |= pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd, 1, 3, 50771d14cdeSscw resp, 0); 50881e1a623Schristos if (res) { 509a40029a8Scegger aprint_debug_dev(psc->sc_dev, 5107582ef2fScube "synaptics_probe: Identify Touchpad error.\n"); 51171d14cdeSscw /* 51271d14cdeSscw * Reset device in case the probe confused it. 51371d14cdeSscw */ 51471d14cdeSscw doreset: 51524512689Sryoon (void)synaptics_poll_reset(psc); 51624512689Sryoon return res; 51781e1a623Schristos } 51881e1a623Schristos 51981e1a623Schristos if (resp[1] != SYNAPTICS_MAGIC_BYTE) { 520a40029a8Scegger aprint_debug_dev(psc->sc_dev, 5217582ef2fScube "synaptics_probe: Not synaptics.\n"); 52271d14cdeSscw res = 1; 52371d14cdeSscw goto doreset; 52481e1a623Schristos } 52581e1a623Schristos 52671d14cdeSscw sc->flags = 0; 527e29f2f73Sblymn sc->num_buttons = 0; 528e29f2f73Sblymn sc->button_mask = 0xff; 529e29f2f73Sblymn 530043f83d9Smlelstv synaptics_true_edge_right = synaptics_edge_right; 531043f83d9Smlelstv synaptics_true_edge_bottom = synaptics_edge_bottom; 53271d14cdeSscw 53381e1a623Schristos /* Check for minimum version and print a nice message. */ 53481e1a623Schristos ver_major = resp[2] & 0x0f; 53581e1a623Schristos ver_minor = resp[0]; 5367582ef2fScube aprint_normal_dev(psc->sc_dev, "Synaptics touchpad version %d.%d\n", 5377582ef2fScube ver_major, ver_minor); 53881e1a623Schristos if (ver_major * 10 + ver_minor < SYNAPTICS_MIN_VERSION) { 53981e1a623Schristos /* No capability query support. */ 54071d14cdeSscw sc->caps = 0; 54181e1a623Schristos goto done; 54281e1a623Schristos } 54381e1a623Schristos 54481e1a623Schristos /* Query the hardware capabilities. */ 545b7f7faa3Smaya res = synaptics_special_read(psc, SYNAPTICS_READ_CAPABILITIES, resp); 54681e1a623Schristos if (res) { 547d1c4e519Sandvar /* Hmm, failed to get capabilities. */ 5487582ef2fScube aprint_error_dev(psc->sc_dev, 5497582ef2fScube "synaptics_probe: Failed to query capabilities.\n"); 55071d14cdeSscw goto doreset; 55181e1a623Schristos } 55281e1a623Schristos 553538588e8Sblymn sc->caps = SYNAPTICS_CAP_VALUE(resp); 55471d14cdeSscw 55571d14cdeSscw if (sc->caps & SYNAPTICS_CAP_MBUTTON) 55671d14cdeSscw sc->flags |= SYN_FLAG_HAS_MIDDLE_BUTTON; 55771d14cdeSscw 55871d14cdeSscw if (sc->caps & SYNAPTICS_CAP_4BUTTON) 55971d14cdeSscw sc->flags |= SYN_FLAG_HAS_BUTTONS_4_5; 56071d14cdeSscw 56171d14cdeSscw if (sc->caps & SYNAPTICS_CAP_EXTENDED) { 562ef2e77d3Schristos pms_synaptics_probe_extended(psc); 56371d14cdeSscw } 56471d14cdeSscw 56571d14cdeSscw if (sc->flags) { 56671d14cdeSscw const char comma[] = ", "; 56771d14cdeSscw const char *sep = ""; 5687582ef2fScube aprint_normal_dev(psc->sc_dev, ""); 569be8d485dSchristos for (size_t f = 0; f < __arraycount(syn_flags); f++) { 570be8d485dSchristos if (sc->flags & syn_flags[f].bit) { 571be8d485dSchristos aprint_normal("%s%s", sep, syn_flags[f].desc); 572ef2e77d3Schristos sep = comma; 573ef2e77d3Schristos } 5744cac2374Splunky } 575ce6d9321Schristos aprint_normal("\n"); 57681e1a623Schristos } 57781e1a623Schristos 578de9a2f9cSblymn if (sc->flags & SYN_FLAG_HAS_MAX_REPORT) { 579de9a2f9cSblymn res = synaptics_special_read(psc, SYNAPTICS_READ_MAX_COORDS, 580de9a2f9cSblymn resp); 581de9a2f9cSblymn if (res) { 582de9a2f9cSblymn aprint_error_dev(psc->sc_dev, 583de9a2f9cSblymn "synaptics_probe: Failed to query max coords.\n"); 584de9a2f9cSblymn } else { 585de9a2f9cSblymn synaptics_edge_right = (resp[0] << 5) + 586de9a2f9cSblymn ((resp[1] & 0x0f) << 1); 587de9a2f9cSblymn synaptics_edge_top = (resp[2] << 5) + 588de9a2f9cSblymn ((resp[1] & 0xf0) >> 3); 589de9a2f9cSblymn 590043f83d9Smlelstv synaptics_true_edge_right = synaptics_edge_right; 591e29f2f73Sblymn 592e29f2f73Sblymn /* 593e29f2f73Sblymn * If we have vertical scroll then steal 10% 594e29f2f73Sblymn * for that region. 595e29f2f73Sblymn */ 596e29f2f73Sblymn if (sc->flags & SYN_FLAG_HAS_VERTICAL_SCROLL) 597e29f2f73Sblymn synaptics_edge_right -= 598e29f2f73Sblymn synaptics_edge_right / 10; 599e29f2f73Sblymn 600de9a2f9cSblymn aprint_normal_dev(psc->sc_dev, 601de9a2f9cSblymn "Probed max coordinates right: %d, top: %d\n", 602de9a2f9cSblymn synaptics_edge_right, synaptics_edge_top); 603de9a2f9cSblymn } 604de9a2f9cSblymn } 605de9a2f9cSblymn 606de9a2f9cSblymn if (sc->flags & SYN_FLAG_HAS_MIN_REPORT) { 607de9a2f9cSblymn res = synaptics_special_read(psc, SYNAPTICS_READ_MIN_COORDS, 608de9a2f9cSblymn resp); 609de9a2f9cSblymn if (res) { 610de9a2f9cSblymn aprint_error_dev(psc->sc_dev, 611de9a2f9cSblymn "synaptics_probe: Failed to query min coords.\n"); 612de9a2f9cSblymn } else { 613de9a2f9cSblymn synaptics_edge_left = (resp[0] << 5) + 614de9a2f9cSblymn ((resp[1] & 0x0f) << 1); 615de9a2f9cSblymn synaptics_edge_bottom = (resp[2] << 5) + 616de9a2f9cSblymn ((resp[1] & 0xf0) >> 3); 617de9a2f9cSblymn 618043f83d9Smlelstv synaptics_true_edge_bottom = synaptics_edge_bottom; 619e29f2f73Sblymn 620e29f2f73Sblymn /* 621e29f2f73Sblymn * If we have horizontal scroll then steal 10% 622e29f2f73Sblymn * for that region. 623e29f2f73Sblymn */ 624e29f2f73Sblymn if (sc->flags & SYN_FLAG_HAS_HORIZONTAL_SCROLL) 625043f83d9Smlelstv synaptics_hscroll_pct = 10; 626e29f2f73Sblymn 627de9a2f9cSblymn aprint_normal_dev(psc->sc_dev, 628de9a2f9cSblymn "Probed min coordinates left: %d, bottom: %d\n", 629de9a2f9cSblymn synaptics_edge_left, synaptics_edge_bottom); 630de9a2f9cSblymn } 631de9a2f9cSblymn } 632de9a2f9cSblymn 633043f83d9Smlelstv done: 634e29f2f73Sblymn pms_synaptics_set_boundaries(); 635e29f2f73Sblymn 636ef8015e6Schristos pms_sysctl_synaptics(&clog); 63771d14cdeSscw pckbport_set_inputhandler(psc->sc_kbctag, psc->sc_kbcslot, 6387582ef2fScube pms_synaptics_input, psc, device_xname(psc->sc_dev)); 63971d14cdeSscw 64071d14cdeSscw return (0); 64181e1a623Schristos } 64281e1a623Schristos 64381e1a623Schristos void 64481e1a623Schristos pms_synaptics_enable(void *vsc) 64581e1a623Schristos { 64671d14cdeSscw struct pms_softc *psc = vsc; 64771d14cdeSscw struct synaptics_softc *sc = &psc->u.synaptics; 648fe90bab4Sblymn u_char enable_modes; 649abbf616bSblymn int res, i; 65081e1a623Schristos 6514cac2374Splunky if (sc->flags & SYN_FLAG_HAS_PASSTHROUGH) { 6524cac2374Splunky /* 653be8d485dSchristos * Extended capability probes can confuse the passthrough 654be8d485dSchristos * device; reset the touchpad now to cure that. 6554cac2374Splunky */ 656fe90bab4Sblymn res = synaptics_poll_reset(psc); 6574cac2374Splunky } 6584cac2374Splunky 65971d14cdeSscw /* 66071d14cdeSscw * Enable Absolute mode with W (width) reporting, and set 661fe90bab4Sblymn * the packet rate to maximum (80 packets per second). Enable 662fe90bab4Sblymn * extended W mode if supported so we can report second finger 663fe90bab4Sblymn * position. 66471d14cdeSscw */ 665fe90bab4Sblymn enable_modes = 666fe90bab4Sblymn SYNAPTICS_MODE_ABSOLUTE | SYNAPTICS_MODE_W | SYNAPTICS_MODE_RATE; 667fe90bab4Sblymn 668fe90bab4Sblymn if (sc->flags & SYN_FLAG_HAS_EXTENDED_WMODE) 669fe90bab4Sblymn enable_modes |= SYNAPTICS_MODE_EXTENDED_W; 670fe90bab4Sblymn 671fe90bab4Sblymn /* 672fe90bab4Sblymn * Synaptics documentation says to disable device before 673fe90bab4Sblymn * setting mode. 674fe90bab4Sblymn */ 675fe90bab4Sblymn synaptics_poll_cmd(psc, PMS_DEV_DISABLE, 0); 676fe90bab4Sblymn /* a couple of set scales to clear out pending commands */ 677abbf616bSblymn for (i = 0; i < 2; i++) 678fe90bab4Sblymn synaptics_poll_cmd(psc, PMS_SET_SCALE11, 0); 679fe90bab4Sblymn 680b7f7faa3Smaya res = synaptics_special_write(psc, SYNAPTICS_CMD_SET_MODE2, enable_modes); 681fe90bab4Sblymn if (res) 682043f83d9Smlelstv device_printf(psc->sc_dev, "set mode error\n"); 683fe90bab4Sblymn 684fe90bab4Sblymn /* a couple of set scales to clear out pending commands */ 685abbf616bSblymn for (i = 0; i < 2; i++) 686fe90bab4Sblymn synaptics_poll_cmd(psc, PMS_SET_SCALE11, 0); 687fe90bab4Sblymn 688b7f7faa3Smaya /* Set advanced gesture mode */ 689881d45ecSblymn if ((sc->flags & SYN_FLAG_HAS_EXTENDED_WMODE) || 690881d45ecSblymn (sc->flags & SYN_FLAG_HAS_ADV_GESTURE_MODE)) 691b7f7faa3Smaya synaptics_special_write(psc, SYNAPTICS_WRITE_DELUXE_3, 0x3); 692d515f9deSryoon 6934799b3aeSblymn /* Disable motion in the button region for clickpads */ 6944799b3aeSblymn if(sc->flags & SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD) 6954799b3aeSblymn synaptics_button_region_movement = 0; 6964799b3aeSblymn 69771d14cdeSscw sc->up_down = 0; 69871d14cdeSscw sc->prev_fingers = 0; 69971d14cdeSscw sc->gesture_start_x = sc->gesture_start_y = 0; 70071d14cdeSscw sc->gesture_start_packet = 0; 70171d14cdeSscw sc->gesture_tap_packet = 0; 70271d14cdeSscw sc->gesture_type = 0; 70371d14cdeSscw sc->gesture_buttons = 0; 7044799b3aeSblymn sc->total_packets = 0; 705abbf616bSblymn for (i = 0; i < SYN_MAX_FINGERS; i++) { 706abbf616bSblymn sc->rem_x[i] = sc->rem_y[i] = sc->rem_z[i] = 0; 707abbf616bSblymn } 708fe90bab4Sblymn sc->button_history = 0; 7094799b3aeSblymn 7104799b3aeSblymn /* clear the packet decode structure */ 7114799b3aeSblymn memset(&packet, 0, sizeof(packet)); 71281e1a623Schristos } 71381e1a623Schristos 71481e1a623Schristos void 71581e1a623Schristos pms_synaptics_resume(void *vsc) 71681e1a623Schristos { 717fe90bab4Sblymn (void)synaptics_poll_reset(vsc); 71881e1a623Schristos } 71981e1a623Schristos 72071d14cdeSscw static void 72171d14cdeSscw pms_sysctl_synaptics(struct sysctllog **clog) 72281e1a623Schristos { 72381e1a623Schristos int rc, root_num; 724df13e357Satatat const struct sysctlnode *node; 72581e1a623Schristos 72681e1a623Schristos if ((rc = sysctl_createv(clog, 0, NULL, &node, 72781e1a623Schristos CTLFLAG_PERMANENT, CTLTYPE_NODE, "synaptics", 72881e1a623Schristos SYSCTL_DESCR("Synaptics touchpad controls"), 72981e1a623Schristos NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) 73081e1a623Schristos goto err; 73181e1a623Schristos 73281e1a623Schristos root_num = node->sysctl_num; 73381e1a623Schristos 73481e1a623Schristos if ((rc = sysctl_createv(clog, 0, NULL, &node, 73581e1a623Schristos CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 73671d14cdeSscw CTLTYPE_INT, "up_down_emulation", 73771d14cdeSscw SYSCTL_DESCR("Middle button/Z-axis emulation with up/down buttons"), 73881e1a623Schristos pms_sysctl_synaptics_verify, 0, 73971d14cdeSscw &synaptics_up_down_emul, 74081e1a623Schristos 0, CTL_HW, root_num, CTL_CREATE, 74181e1a623Schristos CTL_EOL)) != 0) 74281e1a623Schristos goto err; 74381e1a623Schristos 74471d14cdeSscw synaptics_up_down_emul_nodenum = node->sysctl_num; 74581e1a623Schristos 74681e1a623Schristos if ((rc = sysctl_createv(clog, 0, NULL, &node, 74781e1a623Schristos CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 74871d14cdeSscw CTLTYPE_INT, "up_down_motion_delta", 74971d14cdeSscw SYSCTL_DESCR("Up/down button Z-axis emulation rate"), 75081e1a623Schristos pms_sysctl_synaptics_verify, 0, 75171d14cdeSscw &synaptics_up_down_motion_delta, 75281e1a623Schristos 0, CTL_HW, root_num, CTL_CREATE, 75381e1a623Schristos CTL_EOL)) != 0) 75481e1a623Schristos goto err; 75581e1a623Schristos 75671d14cdeSscw synaptics_up_down_motion_delta_nodenum = node->sysctl_num; 75781e1a623Schristos 75881e1a623Schristos if ((rc = sysctl_createv(clog, 0, NULL, &node, 75981e1a623Schristos CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 76071d14cdeSscw CTLTYPE_INT, "gesture_move", 76171d14cdeSscw SYSCTL_DESCR("Movement greater than this between taps cancels gesture"), 76281e1a623Schristos pms_sysctl_synaptics_verify, 0, 76371d14cdeSscw &synaptics_gesture_move, 76481e1a623Schristos 0, CTL_HW, root_num, CTL_CREATE, 76581e1a623Schristos CTL_EOL)) != 0) 76681e1a623Schristos goto err; 76781e1a623Schristos 76871d14cdeSscw synaptics_gesture_move_nodenum = node->sysctl_num; 76981e1a623Schristos 77081e1a623Schristos if ((rc = sysctl_createv(clog, 0, NULL, &node, 77181e1a623Schristos CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 77271d14cdeSscw CTLTYPE_INT, "gesture_length", 77371d14cdeSscw SYSCTL_DESCR("Time period in which tap is recognised as a gesture"), 77481e1a623Schristos pms_sysctl_synaptics_verify, 0, 77571d14cdeSscw &synaptics_gesture_length, 77681e1a623Schristos 0, CTL_HW, root_num, CTL_CREATE, 77781e1a623Schristos CTL_EOL)) != 0) 77881e1a623Schristos goto err; 77981e1a623Schristos 78071d14cdeSscw synaptics_gesture_length_nodenum = node->sysctl_num; 78181e1a623Schristos 78281e1a623Schristos if ((rc = sysctl_createv(clog, 0, NULL, &node, 78381e1a623Schristos CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 78471d14cdeSscw CTLTYPE_INT, "edge_left", 78571d14cdeSscw SYSCTL_DESCR("Define left edge of touchpad"), 78681e1a623Schristos pms_sysctl_synaptics_verify, 0, 78771d14cdeSscw &synaptics_edge_left, 78881e1a623Schristos 0, CTL_HW, root_num, CTL_CREATE, 78981e1a623Schristos CTL_EOL)) != 0) 79081e1a623Schristos goto err; 79181e1a623Schristos 79271d14cdeSscw synaptics_edge_left_nodenum = node->sysctl_num; 79371d14cdeSscw 79471d14cdeSscw if ((rc = sysctl_createv(clog, 0, NULL, &node, 79571d14cdeSscw CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 79671d14cdeSscw CTLTYPE_INT, "edge_right", 79771d14cdeSscw SYSCTL_DESCR("Define right edge of touchpad"), 79871d14cdeSscw pms_sysctl_synaptics_verify, 0, 79971d14cdeSscw &synaptics_edge_right, 80071d14cdeSscw 0, CTL_HW, root_num, CTL_CREATE, 80171d14cdeSscw CTL_EOL)) != 0) 80271d14cdeSscw goto err; 80371d14cdeSscw 80471d14cdeSscw synaptics_edge_right_nodenum = node->sysctl_num; 80571d14cdeSscw 80671d14cdeSscw if ((rc = sysctl_createv(clog, 0, NULL, &node, 80771d14cdeSscw CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 80871d14cdeSscw CTLTYPE_INT, "edge_top", 80971d14cdeSscw SYSCTL_DESCR("Define top edge of touchpad"), 81071d14cdeSscw pms_sysctl_synaptics_verify, 0, 81171d14cdeSscw &synaptics_edge_top, 81271d14cdeSscw 0, CTL_HW, root_num, CTL_CREATE, 81371d14cdeSscw CTL_EOL)) != 0) 81471d14cdeSscw goto err; 81571d14cdeSscw 81671d14cdeSscw synaptics_edge_top_nodenum = node->sysctl_num; 81771d14cdeSscw 81871d14cdeSscw if ((rc = sysctl_createv(clog, 0, NULL, &node, 81971d14cdeSscw CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 82071d14cdeSscw CTLTYPE_INT, "edge_bottom", 82171d14cdeSscw SYSCTL_DESCR("Define bottom edge of touchpad"), 82271d14cdeSscw pms_sysctl_synaptics_verify, 0, 82371d14cdeSscw &synaptics_edge_bottom, 82471d14cdeSscw 0, CTL_HW, root_num, CTL_CREATE, 82571d14cdeSscw CTL_EOL)) != 0) 82671d14cdeSscw goto err; 82771d14cdeSscw 82871d14cdeSscw synaptics_edge_bottom_nodenum = node->sysctl_num; 82971d14cdeSscw 83071d14cdeSscw if ((rc = sysctl_createv(clog, 0, NULL, &node, 83171d14cdeSscw CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 83271d14cdeSscw CTLTYPE_INT, "edge_motion_delta", 83371d14cdeSscw SYSCTL_DESCR("Define edge motion rate"), 83471d14cdeSscw pms_sysctl_synaptics_verify, 0, 83571d14cdeSscw &synaptics_edge_motion_delta, 83671d14cdeSscw 0, CTL_HW, root_num, CTL_CREATE, 83771d14cdeSscw CTL_EOL)) != 0) 83871d14cdeSscw goto err; 83971d14cdeSscw 84071d14cdeSscw synaptics_edge_motion_delta_nodenum = node->sysctl_num; 84171d14cdeSscw 84271d14cdeSscw if ((rc = sysctl_createv(clog, 0, NULL, &node, 84371d14cdeSscw CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 84471d14cdeSscw CTLTYPE_INT, "finger_high", 84571d14cdeSscw SYSCTL_DESCR("Define finger applied pressure threshold"), 84671d14cdeSscw pms_sysctl_synaptics_verify, 0, 84771d14cdeSscw &synaptics_finger_high, 84871d14cdeSscw 0, CTL_HW, root_num, CTL_CREATE, 84971d14cdeSscw CTL_EOL)) != 0) 85071d14cdeSscw goto err; 85171d14cdeSscw 85271d14cdeSscw synaptics_finger_high_nodenum = node->sysctl_num; 85371d14cdeSscw 85471d14cdeSscw if ((rc = sysctl_createv(clog, 0, NULL, &node, 85571d14cdeSscw CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 85671d14cdeSscw CTLTYPE_INT, "finger_low", 85771d14cdeSscw SYSCTL_DESCR("Define finger removed pressure threshold"), 85871d14cdeSscw pms_sysctl_synaptics_verify, 0, 85971d14cdeSscw &synaptics_finger_low, 86071d14cdeSscw 0, CTL_HW, root_num, CTL_CREATE, 86171d14cdeSscw CTL_EOL)) != 0) 86271d14cdeSscw goto err; 86371d14cdeSscw 86471d14cdeSscw synaptics_finger_low_nodenum = node->sysctl_num; 86571d14cdeSscw 86671d14cdeSscw if ((rc = sysctl_createv(clog, 0, NULL, &node, 86771d14cdeSscw CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 86871d14cdeSscw CTLTYPE_INT, "two_fingers_emulation", 86971d14cdeSscw SYSCTL_DESCR("Map two fingers to middle button"), 87071d14cdeSscw pms_sysctl_synaptics_verify, 0, 87171d14cdeSscw &synaptics_two_fingers_emul, 87271d14cdeSscw 0, CTL_HW, root_num, CTL_CREATE, 87371d14cdeSscw CTL_EOL)) != 0) 87471d14cdeSscw goto err; 87571d14cdeSscw 87671d14cdeSscw synaptics_two_fingers_emul_nodenum = node->sysctl_num; 87771d14cdeSscw 87871d14cdeSscw if ((rc = sysctl_createv(clog, 0, NULL, &node, 87971d14cdeSscw CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 88071d14cdeSscw CTLTYPE_INT, "scale_x", 88171d14cdeSscw SYSCTL_DESCR("Horizontal movement scale factor"), 88271d14cdeSscw pms_sysctl_synaptics_verify, 0, 883f4005a42Sdsl &synaptics_scale_x, 88471d14cdeSscw 0, CTL_HW, root_num, CTL_CREATE, 88571d14cdeSscw CTL_EOL)) != 0) 88671d14cdeSscw goto err; 88771d14cdeSscw 88871d14cdeSscw synaptics_scale_x_nodenum = node->sysctl_num; 88971d14cdeSscw 89071d14cdeSscw if ((rc = sysctl_createv(clog, 0, NULL, &node, 89171d14cdeSscw CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 89271d14cdeSscw CTLTYPE_INT, "scale_y", 89371d14cdeSscw SYSCTL_DESCR("Vertical movement scale factor"), 89471d14cdeSscw pms_sysctl_synaptics_verify, 0, 895f4005a42Sdsl &synaptics_scale_y, 89671d14cdeSscw 0, CTL_HW, root_num, CTL_CREATE, 89771d14cdeSscw CTL_EOL)) != 0) 89871d14cdeSscw goto err; 89971d14cdeSscw 90071d14cdeSscw synaptics_scale_y_nodenum = node->sysctl_num; 90171d14cdeSscw 90271d14cdeSscw if ((rc = sysctl_createv(clog, 0, NULL, &node, 90371d14cdeSscw CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 904abbf616bSblymn CTLTYPE_INT, "scale_z", 905abbf616bSblymn SYSCTL_DESCR("Sroll wheel emulation scale factor"), 906abbf616bSblymn pms_sysctl_synaptics_verify, 0, 907abbf616bSblymn &synaptics_scale_z, 908abbf616bSblymn 0, CTL_HW, root_num, CTL_CREATE, 909abbf616bSblymn CTL_EOL)) != 0) 910abbf616bSblymn goto err; 911abbf616bSblymn 912abbf616bSblymn synaptics_scale_z_nodenum = node->sysctl_num; 913abbf616bSblymn 914abbf616bSblymn if ((rc = sysctl_createv(clog, 0, NULL, &node, 915abbf616bSblymn CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 91671d14cdeSscw CTLTYPE_INT, "max_speed_x", 91771d14cdeSscw SYSCTL_DESCR("Horizontal movement maximum speed"), 91871d14cdeSscw pms_sysctl_synaptics_verify, 0, 91971d14cdeSscw &synaptics_max_speed_x, 92071d14cdeSscw 0, CTL_HW, root_num, CTL_CREATE, 92171d14cdeSscw CTL_EOL)) != 0) 92271d14cdeSscw goto err; 92371d14cdeSscw 92471d14cdeSscw synaptics_max_speed_x_nodenum = node->sysctl_num; 92571d14cdeSscw 92671d14cdeSscw if ((rc = sysctl_createv(clog, 0, NULL, &node, 92771d14cdeSscw CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 92871d14cdeSscw CTLTYPE_INT, "max_speed_y", 92971d14cdeSscw SYSCTL_DESCR("Vertical movement maximum speed"), 93071d14cdeSscw pms_sysctl_synaptics_verify, 0, 93171d14cdeSscw &synaptics_max_speed_y, 93271d14cdeSscw 0, CTL_HW, root_num, CTL_CREATE, 93371d14cdeSscw CTL_EOL)) != 0) 93471d14cdeSscw goto err; 93571d14cdeSscw 93671d14cdeSscw synaptics_max_speed_y_nodenum = node->sysctl_num; 93771d14cdeSscw 93871d14cdeSscw if ((rc = sysctl_createv(clog, 0, NULL, &node, 93971d14cdeSscw CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 940abbf616bSblymn CTLTYPE_INT, "max_speed_z", 941abbf616bSblymn SYSCTL_DESCR("Scroll wheel emulation maximum speed"), 942abbf616bSblymn pms_sysctl_synaptics_verify, 0, 943abbf616bSblymn &synaptics_max_speed_z, 944abbf616bSblymn 0, CTL_HW, root_num, CTL_CREATE, 945abbf616bSblymn CTL_EOL)) != 0) 946abbf616bSblymn goto err; 947abbf616bSblymn 948abbf616bSblymn synaptics_max_speed_z_nodenum = node->sysctl_num; 949abbf616bSblymn 950abbf616bSblymn if ((rc = sysctl_createv(clog, 0, NULL, &node, 951abbf616bSblymn CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 95271d14cdeSscw CTLTYPE_INT, "movement_threshold", 95371d14cdeSscw SYSCTL_DESCR("Minimum reported movement threshold"), 95471d14cdeSscw pms_sysctl_synaptics_verify, 0, 95571d14cdeSscw &synaptics_movement_threshold, 95671d14cdeSscw 0, CTL_HW, root_num, CTL_CREATE, 95771d14cdeSscw CTL_EOL)) != 0) 95871d14cdeSscw goto err; 95971d14cdeSscw 96071d14cdeSscw synaptics_movement_threshold_nodenum = node->sysctl_num; 961fe90bab4Sblymn 962fe90bab4Sblymn if ((rc = sysctl_createv(clog, 0, NULL, &node, 963fe90bab4Sblymn CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 96426f4df04Sjmcneill CTLTYPE_INT, "movement_enable", 96526f4df04Sjmcneill SYSCTL_DESCR("Enable movement reporting"), 96626f4df04Sjmcneill pms_sysctl_synaptics_verify, 0, 96726f4df04Sjmcneill &synaptics_movement_enable, 96826f4df04Sjmcneill 0, CTL_HW, root_num, CTL_CREATE, 96926f4df04Sjmcneill CTL_EOL)) != 0) 97026f4df04Sjmcneill goto err; 97126f4df04Sjmcneill 97226f4df04Sjmcneill synaptics_movement_enable_nodenum = node->sysctl_num; 97326f4df04Sjmcneill 97426f4df04Sjmcneill if ((rc = sysctl_createv(clog, 0, NULL, &node, 97526f4df04Sjmcneill CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 9764799b3aeSblymn CTLTYPE_INT, "button_region_movement_enable", 9774799b3aeSblymn SYSCTL_DESCR("Enable movement within clickpad button region"), 9784799b3aeSblymn pms_sysctl_synaptics_verify, 0, 9794799b3aeSblymn &synaptics_button_region_movement, 9804799b3aeSblymn 0, CTL_HW, root_num, CTL_CREATE, 9814799b3aeSblymn CTL_EOL)) != 0) 9824799b3aeSblymn goto err; 9834799b3aeSblymn 9844799b3aeSblymn synaptics_button_region_movement_nodenum = node->sysctl_num; 9854799b3aeSblymn 9864799b3aeSblymn if ((rc = sysctl_createv(clog, 0, NULL, &node, 9874799b3aeSblymn CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 988fe90bab4Sblymn CTLTYPE_INT, "button_boundary", 989fe90bab4Sblymn SYSCTL_DESCR("Top edge of button area"), 990fe90bab4Sblymn pms_sysctl_synaptics_verify, 0, 991fe90bab4Sblymn &synaptics_button_boundary, 992fe90bab4Sblymn 0, CTL_HW, root_num, CTL_CREATE, 993fe90bab4Sblymn CTL_EOL)) != 0) 994fe90bab4Sblymn goto err; 995fe90bab4Sblymn 996fe90bab4Sblymn synaptics_button_boundary_nodenum = node->sysctl_num; 997fe90bab4Sblymn 998fe90bab4Sblymn if ((rc = sysctl_createv(clog, 0, NULL, &node, 999fe90bab4Sblymn CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 1000fe90bab4Sblymn CTLTYPE_INT, "button2_edge", 1001fe90bab4Sblymn SYSCTL_DESCR("Left edge of button 2 region"), 1002fe90bab4Sblymn pms_sysctl_synaptics_verify, 0, 1003fe90bab4Sblymn &synaptics_button2, 1004fe90bab4Sblymn 0, CTL_HW, root_num, CTL_CREATE, 1005fe90bab4Sblymn CTL_EOL)) != 0) 1006fe90bab4Sblymn goto err; 1007fe90bab4Sblymn 1008fe90bab4Sblymn synaptics_button2_nodenum = node->sysctl_num; 1009fe90bab4Sblymn 1010fe90bab4Sblymn if ((rc = sysctl_createv(clog, 0, NULL, &node, 1011fe90bab4Sblymn CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 1012fe90bab4Sblymn CTLTYPE_INT, "button3_edge", 1013fe90bab4Sblymn SYSCTL_DESCR("Left edge of button 3 region"), 1014fe90bab4Sblymn pms_sysctl_synaptics_verify, 0, 1015fe90bab4Sblymn &synaptics_button3, 1016fe90bab4Sblymn 0, CTL_HW, root_num, CTL_CREATE, 1017fe90bab4Sblymn CTL_EOL)) != 0) 1018fe90bab4Sblymn goto err; 1019fe90bab4Sblymn 1020fe90bab4Sblymn synaptics_button3_nodenum = node->sysctl_num; 1021abbf616bSblymn 1022abbf616bSblymn if ((rc = sysctl_createv(clog, 0, NULL, &node, 1023abbf616bSblymn CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 10249c80624bSnia CTLTYPE_BOOL, "aux_mid_button_scroll", 1025ff23aff6Sandvar SYSCTL_DESCR("Interpret Y-Axis movement with the middle button held as scrolling on the passthrough device (e.g. TrackPoint)"), 10269c80624bSnia pms_sysctl_synaptics_verify, 0, 10279c80624bSnia &synaptics_aux_mid_button_scroll, 10289c80624bSnia 0, CTL_HW, root_num, CTL_CREATE, 10299c80624bSnia CTL_EOL)) != 0) 10309c80624bSnia goto err; 10319c80624bSnia 10329c80624bSnia synaptics_aux_mid_button_scroll_nodenum = node->sysctl_num; 10333c5d2f91Sriastradh 10343c5d2f91Sriastradh if ((rc = sysctl_createv(clog, 0, NULL, &node, 10353c5d2f91Sriastradh CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 1036e29f2f73Sblymn CTLTYPE_INT, "vert_scroll_percent", 1037e29f2f73Sblymn SYSCTL_DESCR("Percent of trackpad width to reserve for vertical scroll region"), 1038e29f2f73Sblymn pms_sysctl_synaptics_verify, 0, 1039043f83d9Smlelstv &synaptics_vscroll_pct, 1040e29f2f73Sblymn 0, CTL_HW, root_num, CTL_CREATE, 1041e29f2f73Sblymn CTL_EOL)) != 0) 1042e29f2f73Sblymn goto err; 1043e29f2f73Sblymn 1044043f83d9Smlelstv synaptics_vscroll_pct_nodenum = node->sysctl_num; 1045e29f2f73Sblymn 1046e29f2f73Sblymn if ((rc = sysctl_createv(clog, 0, NULL, &node, 1047e29f2f73Sblymn CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 1048e29f2f73Sblymn CTLTYPE_INT, "horizontal_scroll_percent", 1049e29f2f73Sblymn SYSCTL_DESCR("Percent of trackpad height to reserve for scroll region"), 1050e29f2f73Sblymn pms_sysctl_synaptics_verify, 0, 1051043f83d9Smlelstv &synaptics_hscroll_pct, 1052e29f2f73Sblymn 0, CTL_HW, root_num, CTL_CREATE, 1053e29f2f73Sblymn CTL_EOL)) != 0) 1054e29f2f73Sblymn goto err; 1055e29f2f73Sblymn 1056043f83d9Smlelstv synaptics_hscroll_pct_nodenum = node->sysctl_num; 1057e29f2f73Sblymn 1058e29f2f73Sblymn if ((rc = sysctl_createv(clog, 0, NULL, &node, 1059e29f2f73Sblymn CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 1060e29f2f73Sblymn CTLTYPE_INT, "button_region_percent", 1061e29f2f73Sblymn SYSCTL_DESCR("Percent of trackpad height to reserve for button region"), 1062e29f2f73Sblymn pms_sysctl_synaptics_verify, 0, 1063e29f2f73Sblymn &synaptics_button_pct, 1064e29f2f73Sblymn 0, CTL_HW, root_num, CTL_CREATE, 1065e29f2f73Sblymn CTL_EOL)) != 0) 1066e29f2f73Sblymn goto err; 1067e29f2f73Sblymn 1068e29f2f73Sblymn synaptics_button_pct_nodenum = node->sysctl_num; 1069e29f2f73Sblymn 1070e29f2f73Sblymn if ((rc = sysctl_createv(clog, 0, NULL, &node, 1071e29f2f73Sblymn CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 10723c5d2f91Sriastradh CTLTYPE_INT, "debug", 10733c5d2f91Sriastradh SYSCTL_DESCR("Enable debug output"), 10743c5d2f91Sriastradh NULL, 0, 10753c5d2f91Sriastradh &synaptics_debug, 10763c5d2f91Sriastradh 0, CTL_HW, root_num, CTL_CREATE, 10773c5d2f91Sriastradh CTL_EOL)) != 0) 10783c5d2f91Sriastradh goto err; 10793c5d2f91Sriastradh 108081e1a623Schristos return; 108181e1a623Schristos 108281e1a623Schristos err: 108371d14cdeSscw aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc); 108481e1a623Schristos } 108581e1a623Schristos 108681e1a623Schristos static int 108781e1a623Schristos pms_sysctl_synaptics_verify(SYSCTLFN_ARGS) 108881e1a623Schristos { 108981e1a623Schristos int error, t; 109081e1a623Schristos struct sysctlnode node; 109181e1a623Schristos 109281e1a623Schristos node = *rnode; 109381e1a623Schristos t = *(int *)rnode->sysctl_data; 109481e1a623Schristos node.sysctl_data = &t; 109581e1a623Schristos error = sysctl_lookup(SYSCTLFN_CALL(&node)); 109681e1a623Schristos if (error || newp == NULL) 109781e1a623Schristos return error; 109881e1a623Schristos 109981e1a623Schristos /* Sanity check the params. */ 11006fe7b609Snia if (node.sysctl_num == synaptics_up_down_emul_nodenum) { 11016fe7b609Snia if (t < 0 || t > 3) 11026fe7b609Snia return (EINVAL); 11036fe7b609Snia } else 11046fe7b609Snia if (node.sysctl_num == synaptics_two_fingers_emul_nodenum) { 110571d14cdeSscw if (t < 0 || t > 2) 110671d14cdeSscw return (EINVAL); 110781e1a623Schristos } else 110871d14cdeSscw if (node.sysctl_num == synaptics_gesture_length_nodenum || 110971d14cdeSscw node.sysctl_num == synaptics_edge_motion_delta_nodenum || 1110f192d2ddSnia node.sysctl_num == synaptics_up_down_motion_delta_nodenum || 1111ea7b97b6Snia node.sysctl_num == synaptics_max_speed_x_nodenum || 1112ea7b97b6Snia node.sysctl_num == synaptics_max_speed_y_nodenum || 1113ea7b97b6Snia node.sysctl_num == synaptics_max_speed_z_nodenum) { 111471d14cdeSscw if (t < 0) 111571d14cdeSscw return (EINVAL); 111671d14cdeSscw } else 111771d14cdeSscw if (node.sysctl_num == synaptics_edge_left_nodenum || 111871d14cdeSscw node.sysctl_num == synaptics_edge_bottom_nodenum) { 111971d14cdeSscw if (t < 0 || t > (SYNAPTICS_EDGE_MAX / 2)) 112071d14cdeSscw return (EINVAL); 112171d14cdeSscw } else 112271d14cdeSscw if (node.sysctl_num == synaptics_edge_right_nodenum || 112371d14cdeSscw node.sysctl_num == synaptics_edge_top_nodenum) { 112471d14cdeSscw if (t < (SYNAPTICS_EDGE_MAX / 2)) 112571d14cdeSscw return (EINVAL); 112671d14cdeSscw } else 112771d14cdeSscw if (node.sysctl_num == synaptics_scale_x_nodenum || 1128d5275023Sblymn node.sysctl_num == synaptics_scale_y_nodenum || 1129d5275023Sblymn node.sysctl_num == synaptics_scale_z_nodenum) { 113071d14cdeSscw if (t < 1 || t > (SYNAPTICS_EDGE_MAX / 4)) 113171d14cdeSscw return (EINVAL); 113271d14cdeSscw } else 113371d14cdeSscw if (node.sysctl_num == synaptics_finger_high_nodenum) { 113471d14cdeSscw if (t < 0 || t > SYNAPTICS_FINGER_PALM || 113571d14cdeSscw t < synaptics_finger_low) 113671d14cdeSscw return (EINVAL); 113771d14cdeSscw } else 113871d14cdeSscw if (node.sysctl_num == synaptics_finger_low_nodenum) { 113971d14cdeSscw if (t < 0 || t > SYNAPTICS_FINGER_PALM || 114071d14cdeSscw t > synaptics_finger_high) 114171d14cdeSscw return (EINVAL); 114271d14cdeSscw } else 114371d14cdeSscw if (node.sysctl_num == synaptics_gesture_move_nodenum || 114471d14cdeSscw node.sysctl_num == synaptics_movement_threshold_nodenum) { 114571d14cdeSscw if (t < 0 || t > (SYNAPTICS_EDGE_MAX / 4)) 114671d14cdeSscw return (EINVAL); 114771d14cdeSscw } else 114826f4df04Sjmcneill if (node.sysctl_num == synaptics_button_boundary_nodenum) { 1149e29f2f73Sblymn if (t < 0 || t < synaptics_edge_bottom || 1150e29f2f73Sblymn t > synaptics_edge_top) 1151fe90bab4Sblymn return (EINVAL); 1152fe90bab4Sblymn } else 115326f4df04Sjmcneill if (node.sysctl_num == synaptics_button2_nodenum || 115426f4df04Sjmcneill node.sysctl_num == synaptics_button3_nodenum) { 1155e29f2f73Sblymn if (t < synaptics_edge_left || t > synaptics_edge_right) 1156fe90bab4Sblymn return (EINVAL); 1157fe90bab4Sblymn } else 115826f4df04Sjmcneill if (node.sysctl_num == synaptics_movement_enable_nodenum) { 115926f4df04Sjmcneill if (t < 0 || t > 1) 116026f4df04Sjmcneill return (EINVAL); 116126f4df04Sjmcneill } else 11624799b3aeSblymn if (node.sysctl_num == synaptics_button_region_movement_nodenum) { 11634799b3aeSblymn if (t < 0 || t > 1) 11644799b3aeSblymn return (EINVAL); 11654799b3aeSblymn } else 11669c80624bSnia if (node.sysctl_num == synaptics_aux_mid_button_scroll_nodenum) { 11679c80624bSnia if (t < 0 || t > 1) 11689c80624bSnia return (EINVAL); 11699c80624bSnia } else 1170043f83d9Smlelstv if (node.sysctl_num == synaptics_vscroll_pct_nodenum) { 1171e29f2f73Sblymn if (t < 0 || t > 100) 1172e29f2f73Sblymn return (EINVAL); 1173e29f2f73Sblymn } else 1174043f83d9Smlelstv if (node.sysctl_num == synaptics_hscroll_pct_nodenum) { 1175e29f2f73Sblymn if (t < 0 || t > 100) 1176e29f2f73Sblymn return (EINVAL); 1177e29f2f73Sblymn } else 1178e29f2f73Sblymn if (node.sysctl_num == synaptics_button_pct_nodenum) { 1179e29f2f73Sblymn if (t < 0 || t > 100) 1180e29f2f73Sblymn return (EINVAL); 1181e29f2f73Sblymn } else 118271d14cdeSscw return (EINVAL); 118381e1a623Schristos 118481e1a623Schristos *(int *)rnode->sysctl_data = t; 118581e1a623Schristos 1186e29f2f73Sblymn pms_synaptics_set_boundaries(); 1187e29f2f73Sblymn 118871d14cdeSscw return (0); 118981e1a623Schristos } 119081e1a623Schristos 11914799b3aeSblymn /* 11924799b3aeSblymn * Extract the number of fingers from the current packet and return 11934799b3aeSblymn * it to the caller. 11944799b3aeSblymn */ 11954799b3aeSblymn static unsigned 11964799b3aeSblymn pms_synaptics_get_fingers(struct pms_softc *psc, u_char w, short z) 11974799b3aeSblymn { 11984799b3aeSblymn struct synaptics_softc *sc = &psc->u.synaptics; 11994799b3aeSblymn unsigned short ew_mode; 12004799b3aeSblymn unsigned fingers; 12014799b3aeSblymn 12024799b3aeSblymn fingers = 0; 12034799b3aeSblymn 12044799b3aeSblymn 12054799b3aeSblymn /* 12064799b3aeSblymn * If w is zero and z says no fingers then return 12074799b3aeSblymn * no fingers, w == can also mean 2 fingers... confusing. 12084799b3aeSblymn */ 12094799b3aeSblymn if (w == 0 && z == SYNAPTICS_FINGER_NONE) 12104799b3aeSblymn return 0; 12114799b3aeSblymn 12124799b3aeSblymn if ((sc->flags & SYN_FLAG_HAS_EXTENDED_WMODE) && 12134799b3aeSblymn (w == SYNAPTICS_WIDTH_EXTENDED_W)) { 12144799b3aeSblymn ew_mode = psc->packet[5] >> 4; 12154799b3aeSblymn switch (ew_mode) 12164799b3aeSblymn { 12174799b3aeSblymn case SYNAPTICS_EW_WHEEL: 12184799b3aeSblymn break; 12194799b3aeSblymn 12204799b3aeSblymn case SYNAPTICS_EW_SECONDARY_FINGER: 12214799b3aeSblymn /* to get here we must have 2 fingers at least */ 12224799b3aeSblymn fingers = 2; 12234799b3aeSblymn break; 12244799b3aeSblymn 12254799b3aeSblymn case SYNAPTICS_EW_FINGER_STATUS: 12264799b3aeSblymn fingers = psc->packet[1] & 0x0f; 12274799b3aeSblymn break; 12284799b3aeSblymn 12294799b3aeSblymn default: 1230043f83d9Smlelstv device_printf(psc->sc_dev, 12314799b3aeSblymn "invalid extended w mode %d\n", 12324799b3aeSblymn ew_mode); 12334799b3aeSblymn return 0; /* pretend there are no fingers */ 12344799b3aeSblymn } 12354799b3aeSblymn } else { 12364799b3aeSblymn 12374799b3aeSblymn fingers = 1; 12384799b3aeSblymn 12394799b3aeSblymn /* 12404799b3aeSblymn * If SYN_FLAG_HAS_MULTI_FINGER is set then check 12414799b3aeSblymn * sp_w is below SYNAPTICS_WIDTH_FINGER_MIN, if it is 12424799b3aeSblymn * then this will be the finger count. 12434799b3aeSblymn * 12444799b3aeSblymn * There are a couple of "special" values otherwise 12454799b3aeSblymn * just punt with one finger, if this really is a palm 12464799b3aeSblymn * then it will be caught later. 12474799b3aeSblymn */ 1248e6ab2e16Smlelstv if (sc->flags & (SYN_FLAG_HAS_MULTI_FINGER | SYN_FLAG_HAS_MULTI_FINGER_REPORT)) { 12494799b3aeSblymn if (w == SYNAPTICS_WIDTH_TWO_FINGERS) 12504799b3aeSblymn fingers = 2; 12514799b3aeSblymn else if (w == SYNAPTICS_WIDTH_THREE_OR_MORE) 12524799b3aeSblymn fingers = 3; 12534799b3aeSblymn } 12544799b3aeSblymn 12554799b3aeSblymn } 12564799b3aeSblymn 12574799b3aeSblymn return fingers; 12584799b3aeSblymn } 12594799b3aeSblymn 126081e1a623Schristos /* Masks for the first byte of a packet */ 126181e1a623Schristos #define PMS_LBUTMASK 0x01 126281e1a623Schristos #define PMS_RBUTMASK 0x02 126381e1a623Schristos #define PMS_MBUTMASK 0x04 126481e1a623Schristos 126581e1a623Schristos static void 1266f4a46a86Smlelstv pms_synaptics_parse(struct pms_softc *psc) 1267f4a46a86Smlelstv { 1268f4a46a86Smlelstv struct synaptics_softc *sc = &psc->u.synaptics; 12694799b3aeSblymn struct synaptics_packet nsp; 1270fe90bab4Sblymn char new_buttons, ew_mode; 1271e29f2f73Sblymn uint8_t btn_mask, packet4, packet5; 12724799b3aeSblymn unsigned v, primary_finger, secondary_finger; 1273f23545dbSnia int ext_left = -1, ext_right = -1, ext_middle = -1, 1274f23545dbSnia ext_up = -1, ext_down = -1; 1275f4a46a86Smlelstv 12764799b3aeSblymn sc->total_packets++; 12774799b3aeSblymn 12784799b3aeSblymn memcpy(&nsp, &packet, sizeof(packet)); 1279ef2e77d3Schristos 1280fe90bab4Sblymn /* Width of finger */ 12814799b3aeSblymn nsp.sp_w = ((psc->packet[0] & 0x30) >> 2) 12824799b3aeSblymn + ((psc->packet[0] & 0x04) >> 1) 12834799b3aeSblymn + ((psc->packet[3] & 0x04) >> 2); 12844799b3aeSblymn 12854799b3aeSblymn v = 0; 12864799b3aeSblymn primary_finger = 0; 12874799b3aeSblymn secondary_finger = 0; 128838b76fc7Sryoon if ((sc->flags & SYN_FLAG_HAS_EXTENDED_WMODE) && 12894799b3aeSblymn (nsp.sp_w == SYNAPTICS_WIDTH_EXTENDED_W)) { 1290fe90bab4Sblymn ew_mode = psc->packet[5] >> 4; 1291fe90bab4Sblymn switch (ew_mode) 1292fe90bab4Sblymn { 1293fe90bab4Sblymn case SYNAPTICS_EW_WHEEL: 1294fe90bab4Sblymn /* scroll wheel report, ignore for now */ 1295043f83d9Smlelstv DPRINTF(10, sc, "mouse wheel packet\n"); 1296fe90bab4Sblymn return; 1297fe90bab4Sblymn 1298fe90bab4Sblymn case SYNAPTICS_EW_SECONDARY_FINGER: 1299fe90bab4Sblymn /* parse the second finger report */ 1300fe90bab4Sblymn 13014799b3aeSblymn nsp.sp_secondary = 1; 13024799b3aeSblymn 13034799b3aeSblymn nsp.sp_sx = ((psc->packet[1] & 0xfe) << 1) 13044799b3aeSblymn + ((psc->packet[4] & 0x0f) << 9); 13054799b3aeSblymn nsp.sp_sy = ((psc->packet[2] & 0xfe) << 1) 13064799b3aeSblymn + ((psc->packet[4] & 0xf0) << 5); 13074799b3aeSblymn nsp.sp_sz = (psc->packet[3] & 0x30) 13084799b3aeSblymn + ((psc->packet[5] & 0x0e) << 1); 13094799b3aeSblymn 1310de9a2f9cSblymn /* 1311de9a2f9cSblymn * Check if the x and y are non-zero that they 1312de9a2f9cSblymn * are within the bounds of the trackpad 1313de9a2f9cSblymn * otherwise ignore the packet. 1314de9a2f9cSblymn */ 1315de9a2f9cSblymn if (((nsp.sp_sx != 0) && 1316de9a2f9cSblymn ((nsp.sp_sx < synaptics_edge_left) || 1317de9a2f9cSblymn (nsp.sp_sx > synaptics_edge_right))) || 1318de9a2f9cSblymn ((nsp.sp_sy != 0) && 1319de9a2f9cSblymn ((nsp.sp_sy < synaptics_edge_bottom) || 1320de9a2f9cSblymn (nsp.sp_sy > synaptics_edge_top)))) { 1321de9a2f9cSblymn sc->gesture_type = 0; 1322de9a2f9cSblymn sc->gesture_buttons = 0; 1323de9a2f9cSblymn sc->total_packets--; 1324de9a2f9cSblymn DPRINTF(20, sc, 1325de9a2f9cSblymn "synaptics_parse: dropping out of bounds " 1326de9a2f9cSblymn "packet sp_sx %d sp_sy %d\n", 1327de9a2f9cSblymn nsp.sp_sx, nsp.sp_sy); 1328de9a2f9cSblymn return; 1329de9a2f9cSblymn } 1330de9a2f9cSblymn 13314799b3aeSblymn /* work out the virtual finger width */ 13324799b3aeSblymn v = 8 + (psc->packet[1] & 0x01) + 13334799b3aeSblymn ((psc->packet[2] & 0x01) << 1) + 13344799b3aeSblymn ((psc->packet[5] & 0x01) << 2); 1335fe90bab4Sblymn 1336fe90bab4Sblymn /* keep same buttons down as primary */ 13374799b3aeSblymn nsp.sp_left = sc->button_history & PMS_LBUTMASK; 13384799b3aeSblymn nsp.sp_middle = sc->button_history & PMS_MBUTMASK; 13394799b3aeSblymn nsp.sp_right = sc->button_history & PMS_RBUTMASK; 1340fe90bab4Sblymn break; 1341fe90bab4Sblymn 1342fe90bab4Sblymn case SYNAPTICS_EW_FINGER_STATUS: 13434799b3aeSblymn /* This works but what is it good for? 13444799b3aeSblymn * it gives us an index of the primary/secondary 13454799b3aeSblymn * fingers but no other packets pass this 13464799b3aeSblymn * index. 13474799b3aeSblymn * 13484799b3aeSblymn * XXX Park this, possibly handle a finger 13494799b3aeSblymn * XXX change if indexes change. 1350fe90bab4Sblymn */ 13514799b3aeSblymn primary_finger = psc->packet[2]; 13524799b3aeSblymn secondary_finger = psc->packet[4]; 13534799b3aeSblymn nsp.sp_finger_status = 1; 13544799b3aeSblymn nsp.sp_finger_count = pms_synaptics_get_fingers(psc, 13554799b3aeSblymn nsp.sp_w, nsp.sp_z); 13564799b3aeSblymn goto skip_position; 1357fe90bab4Sblymn 1358fe90bab4Sblymn default: 1359043f83d9Smlelstv device_printf(psc->sc_dev, 1360fe90bab4Sblymn "invalid extended w mode %d\n", 1361fe90bab4Sblymn ew_mode); 1362fe90bab4Sblymn return; 1363fe90bab4Sblymn } 1364fe90bab4Sblymn } else { 13654799b3aeSblymn nsp.sp_primary = 1; 1366fe90bab4Sblymn 13674799b3aeSblymn /* 1368e29f2f73Sblymn * If the trackpad has external buttons and one of 1369e29f2f73Sblymn * those buttons is pressed then the lower bits of 1370e29f2f73Sblymn * x and y are "stolen" for button status. We can tell 1371e29f2f73Sblymn * this has happened by doing an xor of the two right 1372e29f2f73Sblymn * button status bits residing in byte 0 and 3, if the 1373e29f2f73Sblymn * result is non-zero then there is an external button 1374e29f2f73Sblymn * report and the position bytes need to be masked. 1375e29f2f73Sblymn */ 1376e29f2f73Sblymn btn_mask = 0xff; 1377e29f2f73Sblymn if ((sc->num_buttons > 0) && 1378e29f2f73Sblymn ((psc->packet[0] & PMS_RBUTMASK) ^ 1379e29f2f73Sblymn (psc->packet[3] & PMS_RBUTMASK))) { 1380e29f2f73Sblymn btn_mask = sc->button_mask; 1381e29f2f73Sblymn } 1382e29f2f73Sblymn 1383e29f2f73Sblymn packet4 = psc->packet[4] & btn_mask; 1384e29f2f73Sblymn packet5 = psc->packet[5] & btn_mask; 1385e29f2f73Sblymn 1386e29f2f73Sblymn /* 13874799b3aeSblymn * If SYN_FLAG_HAS_MULTI_FINGER is set then check 13884799b3aeSblymn * sp_w is below SYNAPTICS_WIDTH_FINGER_MIN, if it is 13894799b3aeSblymn * then this will be the finger count. 13904799b3aeSblymn * 13914799b3aeSblymn * There are a couple of "special" values otherwise 13924799b3aeSblymn * just punt with one finger, if this really is a palm 13934799b3aeSblymn * then it will be caught later. 13944799b3aeSblymn */ 1395e29f2f73Sblymn if ((sc->flags & SYN_FLAG_HAS_MULTI_FINGER) && 1396e29f2f73Sblymn ((nsp.sp_w == SYNAPTICS_WIDTH_TWO_FINGERS) || 1397e29f2f73Sblymn (nsp.sp_w == SYNAPTICS_WIDTH_THREE_OR_MORE))) { 13984799b3aeSblymn /* 13994799b3aeSblymn * To make life interesting if there are 14004799b3aeSblymn * two or more fingers on the touchpad then 14014799b3aeSblymn * the coordinate reporting changes and an extra 14024799b3aeSblymn * "virtual" finger width is reported. 14034799b3aeSblymn */ 1404e29f2f73Sblymn nsp.sp_x = (packet4 & 0xfc) + 1405e29f2f73Sblymn ((packet4 & 0x02) << 1) + 14064799b3aeSblymn ((psc->packet[1] & 0x0f) << 8) + 1407f4a46a86Smlelstv ((psc->packet[3] & 0x10) << 8); 14084799b3aeSblymn 1409e29f2f73Sblymn nsp.sp_y = (packet5 & 0xfc) + 1410e29f2f73Sblymn ((packet5 & 0x02) << 1) + 14114799b3aeSblymn ((psc->packet[1] & 0xf0) << 4) + 1412f4a46a86Smlelstv ((psc->packet[3] & 0x20) << 7); 1413f4a46a86Smlelstv 1414f4a46a86Smlelstv /* Pressure */ 14154799b3aeSblymn nsp.sp_z = psc->packet[2] & 0xfe; 14164799b3aeSblymn 14174799b3aeSblymn /* derive the virtual finger width */ 1418e29f2f73Sblymn v = 8 + ((packet4 & 0x02) >> 1) + 1419e29f2f73Sblymn (packet5 & 0x02) + 14204799b3aeSblymn ((psc->packet[2] & 0x01) << 2); 14214799b3aeSblymn 14224799b3aeSblymn } else { 14234799b3aeSblymn /* Absolute X/Y coordinates of finger */ 1424e29f2f73Sblymn nsp.sp_x = packet4 + 14254799b3aeSblymn ((psc->packet[1] & 0x0f) << 8) + 14264799b3aeSblymn ((psc->packet[3] & 0x10) << 8); 14274799b3aeSblymn 1428e29f2f73Sblymn nsp.sp_y = packet5 + 14294799b3aeSblymn ((psc->packet[1] & 0xf0) << 4) + 14304799b3aeSblymn ((psc->packet[3] & 0x20) << 7); 14314799b3aeSblymn 14324799b3aeSblymn /* Pressure */ 14334799b3aeSblymn nsp.sp_z = psc->packet[2]; 14344799b3aeSblymn } 14354799b3aeSblymn 1436de9a2f9cSblymn /* 1437de9a2f9cSblymn * Check if the x and y are non-zero that they 1438de9a2f9cSblymn * are within the bounds of the trackpad 1439de9a2f9cSblymn * otherwise ignore the packet. 1440de9a2f9cSblymn */ 1441de9a2f9cSblymn if (((nsp.sp_x != 0) && 1442de9a2f9cSblymn ((nsp.sp_x < synaptics_edge_left) || 1443de9a2f9cSblymn (nsp.sp_x > synaptics_edge_right))) || 1444de9a2f9cSblymn ((nsp.sp_y != 0) && 1445de9a2f9cSblymn ((nsp.sp_y < synaptics_edge_bottom) || 1446de9a2f9cSblymn (nsp.sp_y > synaptics_edge_top)))) { 1447de9a2f9cSblymn sc->gesture_type = 0; 1448de9a2f9cSblymn sc->gesture_buttons = 0; 1449de9a2f9cSblymn sc->total_packets--; 1450de9a2f9cSblymn DPRINTF(20, sc, 1451de9a2f9cSblymn "synaptics_parse: dropping out of bounds packet " 1452de9a2f9cSblymn "sp_x %d sp_y %d\n", 1453de9a2f9cSblymn nsp.sp_x, nsp.sp_y); 1454de9a2f9cSblymn return; 1455de9a2f9cSblymn } 1456de9a2f9cSblymn 14574799b3aeSblymn nsp.sp_finger_count = pms_synaptics_get_fingers(psc, 14584799b3aeSblymn nsp.sp_w, nsp.sp_z); 14594799b3aeSblymn 14604799b3aeSblymn /* 14614799b3aeSblymn * We don't have extended W so we only know if there 14624799b3aeSblymn * are multiple fingers on the touchpad, only the primary 14634799b3aeSblymn * location is reported so just pretend we have an 14644799b3aeSblymn * unmoving second finger. 14654799b3aeSblymn */ 14664799b3aeSblymn if (((sc->flags & SYN_FLAG_HAS_EXTENDED_WMODE) 14674799b3aeSblymn != SYN_FLAG_HAS_EXTENDED_WMODE) && 14684799b3aeSblymn (nsp.sp_finger_count > 1)) { 14694799b3aeSblymn nsp.sp_secondary = 1; 14704799b3aeSblymn nsp.sp_sx = 0; 14714799b3aeSblymn nsp.sp_sy = 0; 14724799b3aeSblymn nsp.sp_sz = 0; 14734799b3aeSblymn } 1474f4a46a86Smlelstv 1475881d45ecSblymn if ((psc->packet[0] ^ psc->packet[3]) & 0x02) { 1476881d45ecSblymn /* extended buttons */ 1477881d45ecSblymn 1478043f83d9Smlelstv DPRINTF(10, sc, 1479881d45ecSblymn "synaptics_parse: %02x %02x %02x %02x %02x %02x\n", 1480881d45ecSblymn psc->packet[0], psc->packet[1], psc->packet[2], 1481881d45ecSblymn psc->packet[3], psc->packet[4], psc->packet[5]); 1482881d45ecSblymn 1483881d45ecSblymn if ((psc->packet[4] & SYN_1BUTMASK) != 0) 1484f23545dbSnia ext_left = PMS_LBUTMASK; 14854e1981f9Sjmcneill else 1486f23545dbSnia ext_left = 0; 1487881d45ecSblymn 1488881d45ecSblymn if ((psc->packet[4] & SYN_3BUTMASK) != 0) 1489f23545dbSnia ext_middle = PMS_MBUTMASK; 14904e1981f9Sjmcneill else 1491f23545dbSnia ext_middle = 0; 1492881d45ecSblymn 1493881d45ecSblymn if ((psc->packet[5] & SYN_2BUTMASK) != 0) 1494f23545dbSnia ext_right = PMS_RBUTMASK; 14954e1981f9Sjmcneill else 1496f23545dbSnia ext_right = 0; 1497881d45ecSblymn 1498881d45ecSblymn if ((psc->packet[5] & SYN_4BUTMASK) != 0) 1499f23545dbSnia ext_up = 1; 15004e1981f9Sjmcneill else 1501f23545dbSnia ext_up = 0; 1502881d45ecSblymn 1503881d45ecSblymn if ((psc->packet[4] & SYN_5BUTMASK) != 0) 1504f23545dbSnia ext_down = 1; 15054e1981f9Sjmcneill else 1506f23545dbSnia ext_down = 0; 1507881d45ecSblymn } else { 1508f4a46a86Smlelstv /* Left/Right button handling. */ 15094799b3aeSblymn nsp.sp_left = psc->packet[0] & PMS_LBUTMASK; 15104799b3aeSblymn nsp.sp_right = psc->packet[0] & PMS_RBUTMASK; 1511881d45ecSblymn } 1512f4a46a86Smlelstv 1513f4a46a86Smlelstv /* Up/Down buttons. */ 1514f4a46a86Smlelstv if (sc->flags & SYN_FLAG_HAS_BUTTONS_4_5) { 1515f4a46a86Smlelstv /* Old up/down buttons. */ 15164799b3aeSblymn nsp.sp_up = nsp.sp_left ^ 1517f4a46a86Smlelstv (psc->packet[3] & PMS_LBUTMASK); 15184799b3aeSblymn nsp.sp_down = nsp.sp_right ^ 1519f4a46a86Smlelstv (psc->packet[3] & PMS_RBUTMASK); 1520fe90bab4Sblymn } else if (sc->flags & SYN_FLAG_HAS_UP_DOWN_BUTTONS && 1521f4a46a86Smlelstv ((psc->packet[0] & PMS_RBUTMASK) ^ 1522f4a46a86Smlelstv (psc->packet[3] & PMS_RBUTMASK))) { 1523f4a46a86Smlelstv /* New up/down button. */ 15244799b3aeSblymn nsp.sp_up = psc->packet[4] & SYN_1BUTMASK; 15254799b3aeSblymn nsp.sp_down = psc->packet[5] & SYN_2BUTMASK; 1526f4a46a86Smlelstv } else { 15274799b3aeSblymn nsp.sp_up = 0; 15284799b3aeSblymn nsp.sp_down = 0; 1529f4a46a86Smlelstv } 1530f4a46a86Smlelstv 1531fe90bab4Sblymn new_buttons = 0; 1532ef2e77d3Schristos if(sc->flags & SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD) { 1533ef2e77d3Schristos /* This is not correctly specified. Read this button press 1534fe90bab4Sblymn * from L/U bit. Emulate 3 buttons by checking the 1535fe90bab4Sblymn * coordinates of the click and returning the appropriate 1536fe90bab4Sblymn * button code. Outside the button region default to a 1537fe90bab4Sblymn * left click. 1538ef2e77d3Schristos */ 1539fe90bab4Sblymn u_char bstate = (psc->packet[0] ^ psc->packet[3]) 1540fe90bab4Sblymn & 0x01; 15414799b3aeSblymn if (nsp.sp_y < synaptics_button_boundary) { 15424799b3aeSblymn if (nsp.sp_x > synaptics_button3) { 15434799b3aeSblymn nsp.sp_right = 1544fe90bab4Sblymn bstate ? PMS_RBUTMASK : 0; 15454799b3aeSblymn } else if (nsp.sp_x > synaptics_button2) { 15464799b3aeSblymn nsp.sp_middle = 1547fe90bab4Sblymn bstate ? PMS_MBUTMASK : 0; 1548fe90bab4Sblymn } else { 15494799b3aeSblymn nsp.sp_left = bstate ? PMS_LBUTMASK : 0; 1550fe90bab4Sblymn } 1551ef2e77d3Schristos } else 15524799b3aeSblymn nsp.sp_left = bstate ? 1 : 0; 15534799b3aeSblymn new_buttons = nsp.sp_left | nsp.sp_middle | nsp.sp_right; 1554fe90bab4Sblymn if (new_buttons != sc->button_history) { 1555fe90bab4Sblymn if (sc->button_history == 0) 1556fe90bab4Sblymn sc->button_history = new_buttons; 1557fe90bab4Sblymn else if (new_buttons == 0) { 1558fe90bab4Sblymn sc->button_history = 0; 1559fe90bab4Sblymn /* ensure all buttons are cleared just in 1560fe90bab4Sblymn * case finger comes off in a different 1561fe90bab4Sblymn * region. 1562fe90bab4Sblymn */ 15634799b3aeSblymn nsp.sp_left = 0; 15644799b3aeSblymn nsp.sp_middle = 0; 15654799b3aeSblymn nsp.sp_right = 0; 1566fe90bab4Sblymn } else { 1567fe90bab4Sblymn /* make sure we keep the same button even 1568fe90bab4Sblymn * if the finger moves to a different 1569fe90bab4Sblymn * region. This precludes chording 1570fe90bab4Sblymn * but, oh well. 1571fe90bab4Sblymn */ 15724799b3aeSblymn nsp.sp_left = sc->button_history & PMS_LBUTMASK; 15734799b3aeSblymn nsp.sp_middle = sc->button_history 1574fe90bab4Sblymn & PMS_MBUTMASK; 15754799b3aeSblymn nsp.sp_right = sc->button_history & PMS_RBUTMASK; 1576fe90bab4Sblymn } 1577fe90bab4Sblymn } 1578fe90bab4Sblymn } else if (sc->flags & SYN_FLAG_HAS_MIDDLE_BUTTON) { 1579f4a46a86Smlelstv /* Old style Middle Button. */ 15804799b3aeSblymn nsp.sp_middle = (psc->packet[0] & PMS_LBUTMASK) ^ 1581f4a46a86Smlelstv (psc->packet[3] & PMS_LBUTMASK); 1582f23545dbSnia } else { 15834799b3aeSblymn nsp.sp_middle = 0; 15846fe7b609Snia } 15856fe7b609Snia 1586f23545dbSnia /* 1587f23545dbSnia * Overlay extended button state if anything changed, 1588f23545dbSnia * preserve the state if a button is being held. 1589f23545dbSnia */ 1590f23545dbSnia if (ext_left != -1) 1591f23545dbSnia nsp.sp_left = sc->ext_left = ext_left; 1592f23545dbSnia else if (sc->ext_left != 0) 1593f23545dbSnia nsp.sp_left = sc->ext_left; 1594f23545dbSnia 1595f23545dbSnia if (ext_right != -1) 1596f23545dbSnia nsp.sp_right = sc->ext_right = ext_right; 1597f23545dbSnia else if (sc->ext_right != 0) 1598f23545dbSnia nsp.sp_right = sc->ext_right; 1599f23545dbSnia 1600f23545dbSnia if (ext_middle != -1) 1601f23545dbSnia nsp.sp_middle = sc->ext_middle = ext_middle; 1602f23545dbSnia else if (sc->ext_middle != 0) 1603f23545dbSnia nsp.sp_middle = sc->ext_middle; 1604f23545dbSnia 1605f23545dbSnia if (ext_up != -1) 1606f23545dbSnia nsp.sp_up = sc->ext_up = ext_up; 1607f23545dbSnia else if (sc->ext_up != 0) 1608f23545dbSnia nsp.sp_up = sc->ext_up; 1609f23545dbSnia 1610f23545dbSnia if (ext_down != -1) 1611f23545dbSnia nsp.sp_down = sc->ext_down = ext_down; 1612f23545dbSnia else if (sc->ext_down != 0) 1613f23545dbSnia nsp.sp_down = sc->ext_down; 16144e1981f9Sjmcneill 16156fe7b609Snia switch (synaptics_up_down_emul) { 16166fe7b609Snia case 1: 1617f4a46a86Smlelstv /* Do middle button emulation using up/down buttons */ 16184799b3aeSblymn nsp.sp_middle = nsp.sp_up | nsp.sp_down; 16194799b3aeSblymn nsp.sp_up = nsp.sp_down = 0; 16206fe7b609Snia break; 16216fe7b609Snia case 3: 16226fe7b609Snia /* Do left/right button emulation using up/down buttons */ 16234799b3aeSblymn nsp.sp_left = nsp.sp_left | nsp.sp_up; 16244799b3aeSblymn nsp.sp_right = nsp.sp_right | nsp.sp_down; 16254799b3aeSblymn nsp.sp_up = nsp.sp_down = 0; 16266fe7b609Snia break; 16276fe7b609Snia default: 16286fe7b609Snia /* 16296fe7b609Snia * Don't do any remapping... 16306fe7b609Snia * Z-axis emulation is handled in pms_synaptics_process_packet 16316fe7b609Snia */ 16326fe7b609Snia break; 16336fe7b609Snia } 1634fe90bab4Sblymn } 1635fe90bab4Sblymn 16364799b3aeSblymn /* set the finger count only if we haven't seen an extended-w 16374799b3aeSblymn * finger count status 16384799b3aeSblymn */ 16394799b3aeSblymn if (nsp.sp_finger_status == 0) 16404799b3aeSblymn nsp.sp_finger_count = pms_synaptics_get_fingers(psc, nsp.sp_w, 16414799b3aeSblymn nsp.sp_z); 16424799b3aeSblymn 16434799b3aeSblymn skip_position: 16444799b3aeSblymn DPRINTF(20, sc, 16454799b3aeSblymn "synaptics_parse: sp_x %d sp_y %d sp_z %d, sp_sx %d, sp_sy %d, " 16464799b3aeSblymn "sp_sz %d, sp_w %d sp_finger_count %d, sp_primary %d, " 16474799b3aeSblymn "sp_secondary %d, v %d, primary_finger %d, secondary_finger %d\n", 16484799b3aeSblymn nsp.sp_x, nsp.sp_y, nsp.sp_z, nsp.sp_sx, 16494799b3aeSblymn nsp.sp_sy, nsp.sp_sz, nsp.sp_w, nsp.sp_finger_count, 16504799b3aeSblymn nsp.sp_primary, nsp.sp_secondary, v, primary_finger, 16514799b3aeSblymn secondary_finger); 16524799b3aeSblymn 16534799b3aeSblymn pms_synaptics_process_packet(psc, &nsp); 16544799b3aeSblymn 1655e6ab2e16Smlelstv /* Clear fingers */ 1656e6ab2e16Smlelstv if ((nsp.sp_primary && nsp.sp_finger_count <= 1) || nsp.sp_secondary) { 16574799b3aeSblymn nsp.sp_primary = 0; 16584799b3aeSblymn nsp.sp_secondary = 0; 16594799b3aeSblymn nsp.sp_finger_status = 0; 16604799b3aeSblymn } 16614799b3aeSblymn 16624799b3aeSblymn memcpy(&packet, &nsp, sizeof(packet)); 1663f4a46a86Smlelstv } 1664f4a46a86Smlelstv 1665cd04adbbSnia /* 1666cd04adbbSnia * Passthrough is used for e.g. TrackPoints and additional pointing 1667cd04adbbSnia * devices connected to a Synaptics touchpad. 1668cd04adbbSnia */ 1669f4a46a86Smlelstv static void 1670f4a46a86Smlelstv pms_synaptics_passthrough(struct pms_softc *psc) 1671f4a46a86Smlelstv { 1672f4a46a86Smlelstv int dx, dy, dz; 1673f4a46a86Smlelstv int buttons, changed; 1674f4a46a86Smlelstv int s; 1675f4a46a86Smlelstv 1676f4a46a86Smlelstv buttons = ((psc->packet[1] & PMS_LBUTMASK) ? 0x20 : 0) | 1677f4a46a86Smlelstv ((psc->packet[1] & PMS_MBUTMASK) ? 0x40 : 0) | 1678f4a46a86Smlelstv ((psc->packet[1] & PMS_RBUTMASK) ? 0x80 : 0); 1679f4a46a86Smlelstv 1680f4a46a86Smlelstv dx = psc->packet[4]; 1681f4a46a86Smlelstv if (dx >= 128) 1682f4a46a86Smlelstv dx -= 256; 1683f4a46a86Smlelstv if (dx == -128) 1684f4a46a86Smlelstv dx = -127; 1685f4a46a86Smlelstv 1686f4a46a86Smlelstv dy = psc->packet[5]; 1687f4a46a86Smlelstv if (dy >= 128) 1688f4a46a86Smlelstv dy -= 256; 1689f4a46a86Smlelstv if (dy == -128) 1690f4a46a86Smlelstv dy = -127; 1691f4a46a86Smlelstv 1692f4a46a86Smlelstv dz = 0; 1693f4a46a86Smlelstv 1694f4a46a86Smlelstv changed = buttons ^ (psc->buttons & 0xe0); 1695f4a46a86Smlelstv psc->buttons ^= changed; 1696f4a46a86Smlelstv 1697f4a46a86Smlelstv if (dx || dy || dz || changed) { 1698b7975551Snia s = spltty(); 16999c80624bSnia /* 1700b7975551Snia * If the middle button is held, interpret movement as 1701b7975551Snia * scrolling. 17029c80624bSnia */ 17039c80624bSnia if (synaptics_aux_mid_button_scroll && 1704*bd69b09fSmlelstv dy && (psc->buttons & 0x42)) { 1705b7975551Snia wsmouse_precision_scroll(psc->sc_wsmousedev, dx, dy); 1706b7975551Snia } else { 1707f4a46a86Smlelstv buttons = (psc->buttons & 0x1f) | ((psc->buttons >> 5) & 0x7); 1708f4a46a86Smlelstv wsmouse_input(psc->sc_wsmousedev, 170946e95ff4Smlelstv buttons, dx, dy, dz, 0, 1710f4a46a86Smlelstv WSMOUSE_INPUT_DELTA); 1711b7975551Snia } 1712f4a46a86Smlelstv splx(s); 1713f4a46a86Smlelstv } 1714f4a46a86Smlelstv } 1715f4a46a86Smlelstv 1716f4a46a86Smlelstv static void 171771d14cdeSscw pms_synaptics_input(void *vsc, int data) 171881e1a623Schristos { 171971d14cdeSscw struct pms_softc *psc = vsc; 172071d14cdeSscw struct timeval diff; 172181e1a623Schristos 172271d14cdeSscw if (!psc->sc_enabled) { 172371d14cdeSscw /* Interrupts are not expected. Discard the byte. */ 172471d14cdeSscw return; 172581e1a623Schristos } 172681e1a623Schristos 1727de4337abSkardel getmicrouptime(&psc->current); 172881e1a623Schristos 1729e78dab04Snia if (psc->inputstate > 0) { 173071d14cdeSscw timersub(&psc->current, &psc->last, &diff); 173171d14cdeSscw if (diff.tv_sec > 0 || diff.tv_usec >= 40000) { 1732043f83d9Smlelstv device_printf(psc->sc_dev, 17332fce724eSryoon "pms_synaptics_input: unusual delay (%ld.%06ld s), " 17347582ef2fScube "scheduling reset\n", 173571d14cdeSscw (long)diff.tv_sec, (long)diff.tv_usec); 173671d14cdeSscw psc->inputstate = 0; 173771d14cdeSscw psc->sc_enabled = 0; 173871d14cdeSscw wakeup(&psc->sc_enabled); 173971d14cdeSscw return; 174081e1a623Schristos } 174171d14cdeSscw } 174271d14cdeSscw psc->last = psc->current; 174371d14cdeSscw 174471d14cdeSscw switch (psc->inputstate) { 1745cdb8872cSmlelstv case -5: 1746cdb8872cSmlelstv case -4: 1747cdb8872cSmlelstv case -3: 1748cdb8872cSmlelstv case -2: 1749cdb8872cSmlelstv case -1: 175071d14cdeSscw case 0: 175171d14cdeSscw if ((data & 0xc8) != 0x80) { 1752043f83d9Smlelstv device_printf(psc->sc_dev, 175334b05ae8Sryoon "pms_synaptics_input: 0x%02x out of sync\n", data); 1754cdb8872cSmlelstv /* use negative counts to limit resync phase */ 1755cdb8872cSmlelstv psc->inputstate--; 175671d14cdeSscw return; /* not in sync yet, discard input */ 175771d14cdeSscw } 1758cdb8872cSmlelstv psc->inputstate = 0; 175971d14cdeSscw /*FALLTHROUGH*/ 176081e1a623Schristos 1761cdb8872cSmlelstv case -6: 176271d14cdeSscw case 3: 176371d14cdeSscw if ((data & 8) == 8) { 1764043f83d9Smlelstv device_printf(psc->sc_dev, 176534b05ae8Sryoon "pms_synaptics_input: dropped in relative mode, reset\n"); 176671d14cdeSscw psc->inputstate = 0; 176771d14cdeSscw psc->sc_enabled = 0; 176871d14cdeSscw wakeup(&psc->sc_enabled); 176971d14cdeSscw return; 177071d14cdeSscw } 177171d14cdeSscw } 17729b39f0e7Smrg if (psc->inputstate >= sizeof(psc->packet)) 17739b39f0e7Smrg panic("inputstate should never be %d", psc->inputstate); 177471d14cdeSscw 177571d14cdeSscw psc->packet[psc->inputstate++] = data & 0xff; 177671d14cdeSscw if (psc->inputstate == 6) { 1777043f83d9Smlelstv struct synaptics_softc *sc = &psc->u.synaptics; 1778043f83d9Smlelstv DPRINTF(10, sc, "synaptics: packet: 0x%02x%02x%02x%02x%02x%02x\n", 1779043f83d9Smlelstv psc->packet[0], psc->packet[1], psc->packet[2], 1780043f83d9Smlelstv psc->packet[3], psc->packet[4], psc->packet[5]); 178171d14cdeSscw /* 178271d14cdeSscw * We have a complete packet. 178371d14cdeSscw * Extract the pertinent details. 178471d14cdeSscw */ 178571d14cdeSscw psc->inputstate = 0; 1786f4a46a86Smlelstv if ((psc->packet[0] & 0xfc) == 0x84 && 1787f4a46a86Smlelstv (psc->packet[3] & 0xcc) == 0xc4) { 1788eefea853Sjakllsch /* W = SYNAPTICS_WIDTH_PASSTHROUGH, PS/2 passthrough */ 1789f4a46a86Smlelstv pms_synaptics_passthrough(psc); 179071d14cdeSscw } else { 1791f4a46a86Smlelstv pms_synaptics_parse(psc); 179271d14cdeSscw } 179371d14cdeSscw } 179471d14cdeSscw } 179571d14cdeSscw 179693124077Sperry static inline int 179771d14cdeSscw synaptics_finger_detect(struct synaptics_softc *sc, struct synaptics_packet *sp, 179871d14cdeSscw int *palmp) 179971d14cdeSscw { 180071d14cdeSscw int fingers; 180171d14cdeSscw 180271d14cdeSscw /* Assume no palm */ 180371d14cdeSscw *palmp = 0; 180471d14cdeSscw 180571d14cdeSscw /* 180671d14cdeSscw * Apply some hysteresis when checking for a finger. 180771d14cdeSscw * When the finger is first applied, we ignore it until the 180871d14cdeSscw * pressure exceeds the 'high' threshold. The finger is considered 180971d14cdeSscw * removed only when pressure falls beneath the 'low' threshold. 181071d14cdeSscw */ 181171d14cdeSscw if ((sc->prev_fingers == 0 && sp->sp_z > synaptics_finger_high) || 181271d14cdeSscw (sc->prev_fingers != 0 && sp->sp_z > synaptics_finger_low)) 181371d14cdeSscw fingers = 1; 181471d14cdeSscw else 181571d14cdeSscw fingers = 0; 181671d14cdeSscw 181771d14cdeSscw /* 181871d14cdeSscw * If the pad can't do palm detection, skip the rest. 181971d14cdeSscw */ 182071d14cdeSscw if (fingers == 0 || (sc->flags & SYN_FLAG_HAS_PALM_DETECT) == 0) 182171d14cdeSscw return (fingers); 182271d14cdeSscw 182371d14cdeSscw /* 182471d14cdeSscw * Palm detection 182571d14cdeSscw */ 182671d14cdeSscw if (sp->sp_z > SYNAPTICS_FINGER_FLAT && 182771d14cdeSscw sp->sp_w >= SYNAPTICS_WIDTH_PALM_MIN) 182871d14cdeSscw *palmp = 1; 182971d14cdeSscw 183071d14cdeSscw if (sc->prev_fingers == 0 && 183171d14cdeSscw (sp->sp_z > SYNAPTICS_FINGER_FLAT || 183271d14cdeSscw sp->sp_w >= SYNAPTICS_WIDTH_PALM_MIN)) { 183371d14cdeSscw /* 183471d14cdeSscw * Contact area or pressure is too great to be a finger. 183571d14cdeSscw * Just ignore it for now. 183671d14cdeSscw */ 183771d14cdeSscw return (0); 183871d14cdeSscw } 183971d14cdeSscw 1840b5324420Snia /* 1841b5324420Snia * Detect 2 and 3 fingers if supported, but only if multiple 1842b5324420Snia * fingers appear within the tap gesture time period. 1843b5324420Snia */ 18444799b3aeSblymn if ((sc->flags & SYN_FLAG_HAS_MULTI_FINGER) && 18454799b3aeSblymn ((SYN_TIME(sc, sc->gesture_start_packet) 18464799b3aeSblymn < synaptics_gesture_length) || 18474799b3aeSblymn SYN_TIME(sc, sc->gesture_start_packet) 18484799b3aeSblymn < synaptics_gesture_length)) { 184971d14cdeSscw switch (sp->sp_w) { 185071d14cdeSscw case SYNAPTICS_WIDTH_TWO_FINGERS: 185171d14cdeSscw fingers = 2; 185271d14cdeSscw break; 185371d14cdeSscw 185471d14cdeSscw case SYNAPTICS_WIDTH_THREE_OR_MORE: 185571d14cdeSscw fingers = 3; 185671d14cdeSscw break; 185771d14cdeSscw 185871d14cdeSscw default: 185971d14cdeSscw /* 186071d14cdeSscw * The width value can report spurious single-finger 186171d14cdeSscw * events after a multi-finger event. 186271d14cdeSscw */ 1863e1ecc07cSnia fingers = sc->prev_fingers <= 1 ? 1 : sc->prev_fingers; 186471d14cdeSscw break; 186571d14cdeSscw } 186671d14cdeSscw } 186771d14cdeSscw 186871d14cdeSscw return (fingers); 186971d14cdeSscw } 187071d14cdeSscw 187193124077Sperry static inline void 187271d14cdeSscw synaptics_gesture_detect(struct synaptics_softc *sc, 187371d14cdeSscw struct synaptics_packet *sp, int fingers) 187471d14cdeSscw { 1875ef2e77d3Schristos int gesture_len, gesture_buttons; 187671d14cdeSscw int set_buttons; 187771d14cdeSscw 18784799b3aeSblymn gesture_len = SYN_TIME(sc, sc->gesture_start_packet); 187971d14cdeSscw gesture_buttons = sc->gesture_buttons; 188071d14cdeSscw 1881ef2e77d3Schristos if (fingers > 0 && (fingers == sc->prev_fingers)) { 1882ef2e77d3Schristos /* Finger is still present */ 1883ef2e77d3Schristos sc->gesture_move_x = abs(sc->gesture_start_x - sp->sp_x); 1884ef2e77d3Schristos sc->gesture_move_y = abs(sc->gesture_start_y - sp->sp_y); 1885ef2e77d3Schristos } else 188671d14cdeSscw if (fingers && sc->prev_fingers == 0) { 188771d14cdeSscw /* 188871d14cdeSscw * Finger was just applied. 188971d14cdeSscw * If the previous gesture was a single-click, set things 189071d14cdeSscw * up to deal with a possible drag or double-click gesture. 189171d14cdeSscw * Basically, if the finger is removed again within 189271d14cdeSscw * 'synaptics_gesture_length' packets, this is treated 189371d14cdeSscw * as a double-click. Otherwise we will emulate holding 189471d14cdeSscw * the left button down whilst dragging the mouse. 189571d14cdeSscw */ 189671d14cdeSscw if (SYN_IS_SINGLE_TAP(sc->gesture_type)) 189771d14cdeSscw sc->gesture_type |= SYN_GESTURE_DRAG; 189871d14cdeSscw 1899ef2e77d3Schristos sc->gesture_start_x = abs(sp->sp_x); 1900ef2e77d3Schristos sc->gesture_start_y = abs(sp->sp_y); 1901ef2e77d3Schristos sc->gesture_move_x = 0; 1902ef2e77d3Schristos sc->gesture_move_y = 0; 19034799b3aeSblymn sc->gesture_start_packet = sc->total_packets; 1904ef2e77d3Schristos 19054799b3aeSblymn DPRINTF(10, sc, "Finger applied:" 19063c5d2f91Sriastradh " gesture_start_x: %d" 19073c5d2f91Sriastradh " gesture_start_y: %d\n", 1908ef2e77d3Schristos sc->gesture_start_x, sc->gesture_start_y); 190971d14cdeSscw } else 191071d14cdeSscw if (fingers == 0 && sc->prev_fingers != 0) { 191171d14cdeSscw /* 191271d14cdeSscw * Finger was just removed. 191371d14cdeSscw * Check if the contact time and finger movement were 191471d14cdeSscw * small enough to qualify as a gesture. 191571d14cdeSscw * Ignore finger movement if multiple fingers were 191671d14cdeSscw * detected (the pad may report coordinates for any 191771d14cdeSscw * of the fingers). 191871d14cdeSscw */ 1919ef2e77d3Schristos 19204799b3aeSblymn DPRINTF(10, sc, "Finger removed: gesture_len: %d (%d)\n", 1921ef2e77d3Schristos gesture_len, synaptics_gesture_length); 19224799b3aeSblymn DPRINTF(10, sc, "gesture_move_x: %d (%d) sp_x: %d\n", 1923ef2e77d3Schristos sc->gesture_move_x, synaptics_gesture_move, abs(sp->sp_x)); 19244799b3aeSblymn DPRINTF(10, sc, "gesture_move_y: %d (%d) sp_y: %d\n", 1925ef2e77d3Schristos sc->gesture_move_y, synaptics_gesture_move, abs(sp->sp_y)); 192671d14cdeSscw 192771d14cdeSscw if (gesture_len < synaptics_gesture_length && 1928ef2e77d3Schristos ((sc->gesture_move_x < synaptics_gesture_move && 1929ef2e77d3Schristos sc->gesture_move_y < synaptics_gesture_move))) { 193071d14cdeSscw /* 193171d14cdeSscw * Looking good so far. 193271d14cdeSscw */ 193371d14cdeSscw if (SYN_IS_DRAG(sc->gesture_type)) { 193471d14cdeSscw /* 193571d14cdeSscw * Promote this gesture to double-click. 193671d14cdeSscw */ 193771d14cdeSscw sc->gesture_type |= SYN_GESTURE_DOUBLE; 193871d14cdeSscw sc->gesture_type &= ~SYN_GESTURE_SINGLE; 193981e1a623Schristos } else { 194071d14cdeSscw /* 194171d14cdeSscw * Single tap gesture. Set the tap length timer 194271d14cdeSscw * and flag a single-click. 194371d14cdeSscw */ 19444799b3aeSblymn sc->gesture_tap_packet = sc->total_packets; 194571d14cdeSscw sc->gesture_type |= SYN_GESTURE_SINGLE; 194671d14cdeSscw 194771d14cdeSscw /* 194871d14cdeSscw * The gesture can be modified depending on 194971d14cdeSscw * the number of fingers detected. 195071d14cdeSscw * 195171d14cdeSscw * 1: Normal left button emulation. 195271d14cdeSscw * 2: Either middle button or right button 195371d14cdeSscw * depending on the value of the two_fingers 195471d14cdeSscw * sysctl variable. 195571d14cdeSscw * 3: Right button. 195671d14cdeSscw */ 195771d14cdeSscw switch (sc->prev_fingers) { 195871d14cdeSscw case 2: 195971d14cdeSscw if (synaptics_two_fingers_emul == 1) 196071d14cdeSscw gesture_buttons |= PMS_RBUTMASK; 196171d14cdeSscw else 196271d14cdeSscw if (synaptics_two_fingers_emul == 2) 196371d14cdeSscw gesture_buttons |= PMS_MBUTMASK; 196471d14cdeSscw break; 196571d14cdeSscw case 3: 196671d14cdeSscw gesture_buttons |= PMS_RBUTMASK; 196771d14cdeSscw break; 196871d14cdeSscw default: 196971d14cdeSscw gesture_buttons |= PMS_LBUTMASK; 197071d14cdeSscw break; 197171d14cdeSscw } 197271d14cdeSscw } 197381e1a623Schristos } 197481e1a623Schristos 197571d14cdeSscw /* 197671d14cdeSscw * Always clear drag state when the finger is removed. 197771d14cdeSscw */ 197871d14cdeSscw sc->gesture_type &= ~SYN_GESTURE_DRAG; 197981e1a623Schristos } 198081e1a623Schristos 198171d14cdeSscw if (sc->gesture_type == 0) { 198271d14cdeSscw /* 198371d14cdeSscw * There is no gesture in progress. 198471d14cdeSscw * Clear emulated button state. 198571d14cdeSscw */ 198671d14cdeSscw sc->gesture_buttons = 0; 198771d14cdeSscw return; 198881e1a623Schristos } 198981e1a623Schristos 199071d14cdeSscw /* 199171d14cdeSscw * A gesture is in progress. 199271d14cdeSscw */ 199371d14cdeSscw set_buttons = 0; 199481e1a623Schristos 199571d14cdeSscw if (SYN_IS_SINGLE_TAP(sc->gesture_type)) { 199671d14cdeSscw /* 199771d14cdeSscw * Single-click. 199871d14cdeSscw * Activate the relevant button(s) until the 199971d14cdeSscw * gesture tap timer has expired. 200071d14cdeSscw */ 20014799b3aeSblymn if (SYN_TIME(sc, sc->gesture_tap_packet) < 200271d14cdeSscw synaptics_gesture_length) 200371d14cdeSscw set_buttons = 1; 200471d14cdeSscw else 200571d14cdeSscw sc->gesture_type &= ~SYN_GESTURE_SINGLE; 20064799b3aeSblymn DPRINTF(10, sc, "synaptics_gesture: single tap, buttons %d\n", 20074799b3aeSblymn set_buttons); 20084799b3aeSblymn DPRINTF(10, sc, "synaptics_gesture: single tap, tap at %d, current %d\n", 20094799b3aeSblymn sc->gesture_tap_packet, sc->total_packets); 20104799b3aeSblymn DPRINTF(10, sc, "synaptics_gesture: single tap, tap_time %d, gesture len %d\n", 20114799b3aeSblymn SYN_TIME(sc, sc->gesture_tap_packet), synaptics_gesture_length); 201271d14cdeSscw } else 201371d14cdeSscw if (SYN_IS_DOUBLE_TAP(sc->gesture_type) && sc->prev_fingers == 0) { 201471d14cdeSscw /* 201571d14cdeSscw * Double-click. 201671d14cdeSscw * Activate the relevant button(s) once. 201771d14cdeSscw */ 201871d14cdeSscw set_buttons = 1; 201971d14cdeSscw sc->gesture_type &= ~SYN_GESTURE_DOUBLE; 202071d14cdeSscw } 202181e1a623Schristos 202271d14cdeSscw if (set_buttons || SYN_IS_DRAG(sc->gesture_type)) { 202371d14cdeSscw /* 202471d14cdeSscw * Single-click and drag. 202571d14cdeSscw * Maintain button state until the finger is removed. 202671d14cdeSscw */ 202771d14cdeSscw sp->sp_left |= gesture_buttons & PMS_LBUTMASK; 202871d14cdeSscw sp->sp_right |= gesture_buttons & PMS_RBUTMASK; 202971d14cdeSscw sp->sp_middle |= gesture_buttons & PMS_MBUTMASK; 203071d14cdeSscw } 203181e1a623Schristos 203271d14cdeSscw sc->gesture_buttons = gesture_buttons; 203371d14cdeSscw } 203481e1a623Schristos 203593124077Sperry static inline int 2036fe90bab4Sblymn synaptics_filter_policy(struct synaptics_softc *sc, int finger, int *history, 20374799b3aeSblymn int value, u_int count) 203871d14cdeSscw { 20394799b3aeSblymn int a, b, rv; 204081e1a623Schristos 204171d14cdeSscw /* 204271d14cdeSscw * Once we've accumulated at least SYN_HIST_SIZE values, combine 204371d14cdeSscw * each new value with the previous two and return the average. 204471d14cdeSscw * 204571d14cdeSscw * This is necessary when the touchpad is operating in 80 packets 204671d14cdeSscw * per second mode, as it performs little internal filtering on 204771d14cdeSscw * reported values. 204871d14cdeSscw * 204971d14cdeSscw * Using a rolling average helps to filter out jitter caused by 205071d14cdeSscw * tiny finger movements. 205171d14cdeSscw */ 20524799b3aeSblymn if (count >= SYN_HIST_SIZE) { 2053e6ab2e16Smlelstv a = (history[(count - 2) % SYN_HIST_SIZE] + 2054e6ab2e16Smlelstv history[(count - 1) % SYN_HIST_SIZE]) / 2; 205571d14cdeSscw 2056e6ab2e16Smlelstv b = (value + history[(count - 1) % SYN_HIST_SIZE]) / 2; 205771d14cdeSscw 205871d14cdeSscw rv = b - a; 205971d14cdeSscw 206071d14cdeSscw /* 206171d14cdeSscw * Don't report the movement if it's below a certain 206271d14cdeSscw * threshold. 206371d14cdeSscw */ 206471d14cdeSscw if (abs(rv) < synaptics_movement_threshold) 206571d14cdeSscw rv = 0; 206671d14cdeSscw } else 206771d14cdeSscw rv = 0; 206871d14cdeSscw 206971d14cdeSscw /* 207071d14cdeSscw * Add the new value to the history buffer. 207171d14cdeSscw */ 2072e6ab2e16Smlelstv history[(count + 0) % SYN_HIST_SIZE] = value; 207371d14cdeSscw 207471d14cdeSscw return (rv); 207581e1a623Schristos } 207671d14cdeSscw 207771d14cdeSscw /* Edge detection */ 207871d14cdeSscw #define SYN_EDGE_TOP 1 207971d14cdeSscw #define SYN_EDGE_BOTTOM 2 208071d14cdeSscw #define SYN_EDGE_LEFT 4 208171d14cdeSscw #define SYN_EDGE_RIGHT 8 208271d14cdeSscw 208393124077Sperry static inline int 208471d14cdeSscw synaptics_check_edge(int x, int y) 208571d14cdeSscw { 208671d14cdeSscw int rv = 0; 208771d14cdeSscw 208871d14cdeSscw if (x < synaptics_edge_left) 208971d14cdeSscw rv |= SYN_EDGE_LEFT; 209071d14cdeSscw else 209171d14cdeSscw if (x > synaptics_edge_right) 209271d14cdeSscw rv |= SYN_EDGE_RIGHT; 209371d14cdeSscw 209471d14cdeSscw if (y < synaptics_edge_bottom) 209571d14cdeSscw rv |= SYN_EDGE_BOTTOM; 209671d14cdeSscw else 209771d14cdeSscw if (y > synaptics_edge_top) 209871d14cdeSscw rv |= SYN_EDGE_TOP; 209971d14cdeSscw 210071d14cdeSscw return (rv); 210181e1a623Schristos } 210271d14cdeSscw 210393124077Sperry static inline int 2104168cd830Schristos synaptics_edge_motion(struct synaptics_softc *sc, int delta, int dir) 210571d14cdeSscw { 210671d14cdeSscw 210771d14cdeSscw /* 210871d14cdeSscw * When edge motion is enabled, synaptics_edge_motion_delta is 210971d14cdeSscw * combined with the current delta, together with the direction 211071d14cdeSscw * in which to simulate the motion. The result is added to 211171d14cdeSscw * the delta derived from finger movement. This provides a smooth 211271d14cdeSscw * transition from finger movement to edge motion. 211371d14cdeSscw */ 211471d14cdeSscw delta = synaptics_edge_motion_delta + (dir * delta); 211571d14cdeSscw if (delta < 0) 211671d14cdeSscw return (0); 211771d14cdeSscw if (delta > synaptics_edge_motion_delta) 211871d14cdeSscw return (synaptics_edge_motion_delta); 211971d14cdeSscw return (delta); 212081e1a623Schristos } 212171d14cdeSscw 212293124077Sperry static inline int 212371d14cdeSscw synaptics_scale(int delta, int scale, int *remp) 212471d14cdeSscw { 212571d14cdeSscw int rv; 212671d14cdeSscw 212771d14cdeSscw /* 212871d14cdeSscw * Scale the raw delta in Synaptics coordinates (0-6143) into 212971d14cdeSscw * something more reasonable by dividing the raw delta by a 213071d14cdeSscw * scale factor. Any remainder from the previous scale result 213171d14cdeSscw * is added to the current delta before scaling. 213271d14cdeSscw * This prevents loss of resolution for very small/slow 213371d14cdeSscw * movements of the finger. 213471d14cdeSscw */ 213571d14cdeSscw delta += *remp; 213671d14cdeSscw rv = delta / scale; 213771d14cdeSscw *remp = delta % scale; 213871d14cdeSscw 213971d14cdeSscw return (rv); 214081e1a623Schristos } 214171d14cdeSscw 214293124077Sperry static inline void 214371d14cdeSscw synaptics_movement(struct synaptics_softc *sc, struct synaptics_packet *sp, 21444799b3aeSblymn int *dxp, int *dyp, int *dzp, int *sdxp, int *sdyp, int *sdzp) 214571d14cdeSscw { 21464799b3aeSblymn int dx, dy, dz, sdx, sdy, sdz, edge; 2147abbf616bSblymn 21484799b3aeSblymn dx = dy = dz = sdx = sdy = sdz = 0; 214971d14cdeSscw 215071d14cdeSscw /* 21514799b3aeSblymn * Compute the next values of dx, dy, dz, sdx, sdy, sdz. 215271d14cdeSscw */ 21534799b3aeSblymn dx = synaptics_filter_policy(sc, 0, 21544799b3aeSblymn sc->history_x[SYN_PRIMARY_FINGER], sp->sp_x, 21554799b3aeSblymn sc->packet_count[SYN_PRIMARY_FINGER]); 21564799b3aeSblymn dy = synaptics_filter_policy(sc, 0, 21574799b3aeSblymn sc->history_y[SYN_PRIMARY_FINGER], sp->sp_y, 21584799b3aeSblymn sc->packet_count[SYN_PRIMARY_FINGER]); 2159e6ab2e16Smlelstv sc->packet_count[SYN_PRIMARY_FINGER]++; 21604799b3aeSblymn 21614799b3aeSblymn if (sp->sp_finger_count > 1) { 21624799b3aeSblymn sdx = synaptics_filter_policy(sc, 1, 21634799b3aeSblymn sc->history_x[SYN_SECONDARY_FINGER], sp->sp_sx, 21644799b3aeSblymn sc->packet_count[SYN_SECONDARY_FINGER]); 21654799b3aeSblymn sdy = synaptics_filter_policy(sc, 1, 21664799b3aeSblymn sc->history_y[SYN_SECONDARY_FINGER], sp->sp_sy, 21674799b3aeSblymn sc->packet_count[SYN_SECONDARY_FINGER]); 2168e6ab2e16Smlelstv sc->packet_count[SYN_SECONDARY_FINGER]++; 21694799b3aeSblymn DPRINTF(10, sc, "synaptics_movement: dx %d dy %d sdx %d sdy %d\n", 21704799b3aeSblymn dx, dy, sdx, sdy); 21714799b3aeSblymn } 217271d14cdeSscw 217371d14cdeSscw /* 217471d14cdeSscw * If we're dealing with a drag gesture, and the finger moves to 217571d14cdeSscw * the edge of the touchpad, apply edge motion emulation if it 217671d14cdeSscw * is enabled. 217771d14cdeSscw */ 217871d14cdeSscw if (synaptics_edge_motion_delta && SYN_IS_DRAG(sc->gesture_type)) { 217971d14cdeSscw edge = synaptics_check_edge(sp->sp_x, sp->sp_y); 218071d14cdeSscw 218171d14cdeSscw if (edge & SYN_EDGE_LEFT) 218271d14cdeSscw dx -= synaptics_edge_motion(sc, dx, 1); 218371d14cdeSscw if (edge & SYN_EDGE_RIGHT) 218471d14cdeSscw dx += synaptics_edge_motion(sc, dx, -1); 218571d14cdeSscw if (edge & SYN_EDGE_BOTTOM) 218671d14cdeSscw dy -= synaptics_edge_motion(sc, dy, 1); 218771d14cdeSscw if (edge & SYN_EDGE_TOP) 218871d14cdeSscw dy += synaptics_edge_motion(sc, dy, -1); 21894799b3aeSblymn 21904799b3aeSblymn if (sp->sp_finger_count > 1) { 21914799b3aeSblymn edge = synaptics_check_edge(sp->sp_sx, sp->sp_sy); 21924799b3aeSblymn 21934799b3aeSblymn if (edge & SYN_EDGE_LEFT) 21944799b3aeSblymn sdx -= synaptics_edge_motion(sc, sdx, 1); 21954799b3aeSblymn if (edge & SYN_EDGE_RIGHT) 21964799b3aeSblymn sdx += synaptics_edge_motion(sc, sdx, -1); 21974799b3aeSblymn if (edge & SYN_EDGE_BOTTOM) 21984799b3aeSblymn sdy -= synaptics_edge_motion(sc, sdy, 1); 21994799b3aeSblymn if (edge & SYN_EDGE_TOP) 22004799b3aeSblymn sdy += synaptics_edge_motion(sc, sdy, -1); 22014799b3aeSblymn } 220281e1a623Schristos } 220371d14cdeSscw 220471d14cdeSscw /* 2205abbf616bSblymn * Apply scaling to the deltas 220671d14cdeSscw */ 22074799b3aeSblymn dx = synaptics_scale(dx, synaptics_scale_x, 22084799b3aeSblymn &sc->rem_x[SYN_PRIMARY_FINGER]); 22094799b3aeSblymn dy = synaptics_scale(dy, synaptics_scale_y, 22104799b3aeSblymn &sc->rem_y[SYN_PRIMARY_FINGER]); 22114799b3aeSblymn dz = synaptics_scale(dz, synaptics_scale_z, 22124799b3aeSblymn &sc->rem_z[SYN_PRIMARY_FINGER]); 22134799b3aeSblymn 22144799b3aeSblymn if (sp->sp_finger_count > 1) { 22154799b3aeSblymn sdx = synaptics_scale(sdx, synaptics_scale_x, 22164799b3aeSblymn &sc->rem_x[SYN_SECONDARY_FINGER]); 22174799b3aeSblymn sdy = synaptics_scale(sdy, synaptics_scale_y, 22184799b3aeSblymn &sc->rem_y[SYN_SECONDARY_FINGER]); 22194799b3aeSblymn sdz = synaptics_scale(sdz, synaptics_scale_z, 22204799b3aeSblymn &sc->rem_z[SYN_SECONDARY_FINGER]); 22214799b3aeSblymn 22224799b3aeSblymn DPRINTF(10, sc, 22234799b3aeSblymn "synaptics_movement 2: dx %d dy %d sdx %d sdy %d\n", 22244799b3aeSblymn dx, dy, sdx, sdy); 22254799b3aeSblymn } 222671d14cdeSscw 222771d14cdeSscw /* 222871d14cdeSscw * Clamp deltas to specified maximums. 222971d14cdeSscw */ 2230335eb92cSblymn if (abs(dx) > synaptics_max_speed_x) 2231335eb92cSblymn dx = ((dx >= 0)? 1 : -1) * synaptics_max_speed_x; 2232335eb92cSblymn if (abs(dy) > synaptics_max_speed_y) 2233335eb92cSblymn dy = ((dy >= 0)? 1 : -1) * synaptics_max_speed_y; 2234abbf616bSblymn if (abs(dz) > synaptics_max_speed_z) 2235abbf616bSblymn dz = ((dz >= 0)? 1 : -1) * synaptics_max_speed_z; 223671d14cdeSscw 22374799b3aeSblymn if (sp->sp_finger_count > 1) { 22384799b3aeSblymn if (abs(sdx) > synaptics_max_speed_x) 22394799b3aeSblymn sdx = ((sdx >= 0)? 1 : -1) * synaptics_max_speed_x; 22404799b3aeSblymn if (abs(dy) > synaptics_max_speed_y) 22414799b3aeSblymn sdy = ((sdy >= 0)? 1 : -1) * synaptics_max_speed_y; 22424799b3aeSblymn if (abs(sdz) > synaptics_max_speed_z) 22434799b3aeSblymn sdz = ((sdz >= 0)? 1 : -1) * synaptics_max_speed_z; 22444799b3aeSblymn } 22454799b3aeSblymn 224671d14cdeSscw *dxp = dx; 224771d14cdeSscw *dyp = dy; 2248abbf616bSblymn *dzp = dz; 22494799b3aeSblymn *sdxp = sdx; 22504799b3aeSblymn *sdyp = sdy; 22514799b3aeSblymn *sdzp = sdz; 225271d14cdeSscw 225381e1a623Schristos } 225471d14cdeSscw 225571d14cdeSscw static void 225671d14cdeSscw pms_synaptics_process_packet(struct pms_softc *psc, struct synaptics_packet *sp) 225771d14cdeSscw { 225871d14cdeSscw struct synaptics_softc *sc = &psc->u.synaptics; 22594799b3aeSblymn int dx, dy, dz, sdx, sdy, sdz; 226071d14cdeSscw int fingers, palm, buttons, changed; 2261b7975551Snia int s; 226271d14cdeSscw 22634799b3aeSblymn sdx = sdy = sdz = 0; 22644799b3aeSblymn 226571d14cdeSscw /* 226671d14cdeSscw * Do Z-axis emulation using up/down buttons if required. 226771d14cdeSscw * Note that the pad will send a one second burst of packets 226871d14cdeSscw * when an up/down button is pressed and held. At the moment 226971d14cdeSscw * we don't deal with auto-repeat, so convert the burst into 227071d14cdeSscw * a one-shot. 227171d14cdeSscw */ 227281e1a623Schristos dz = 0; 227371d14cdeSscw if (synaptics_up_down_emul == 2) { 227471d14cdeSscw if (sc->up_down == 0) { 227571d14cdeSscw if (sp->sp_up && sp->sp_down) { 227671d14cdeSscw sp->sp_middle = 1; 22773810cfdeSnia } else if (sp->sp_up) { 227871d14cdeSscw dz = -synaptics_up_down_motion_delta; 22793810cfdeSnia } else if (sp->sp_down) { 228071d14cdeSscw dz = synaptics_up_down_motion_delta; 228181e1a623Schristos } 22823810cfdeSnia } 228381e1a623Schristos 228471d14cdeSscw sc->up_down = sp->sp_up | sp->sp_down; 228571d14cdeSscw sp->sp_up = sp->sp_down = 0; 228671d14cdeSscw } 228781e1a623Schristos 228871d14cdeSscw /* 228971d14cdeSscw * Determine whether or not a finger is on the pad. 229071d14cdeSscw * On some pads, this will return the number of fingers 229171d14cdeSscw * detected. 229271d14cdeSscw */ 229371d14cdeSscw fingers = synaptics_finger_detect(sc, sp, &palm); 229481e1a623Schristos 229571d14cdeSscw /* 22964799b3aeSblymn * Do gesture processing only if we didn't detect a palm. 229771d14cdeSscw */ 22984799b3aeSblymn if (palm == 0) 229971d14cdeSscw synaptics_gesture_detect(sc, sp, fingers); 230071d14cdeSscw else 230171d14cdeSscw sc->gesture_type = sc->gesture_buttons = 0; 230271d14cdeSscw 230371d14cdeSscw /* 230471d14cdeSscw * Determine what buttons to report 230571d14cdeSscw */ 230671d14cdeSscw buttons = (sp->sp_left ? 0x1 : 0) | 230771d14cdeSscw (sp->sp_middle ? 0x2 : 0) | 230871d14cdeSscw (sp->sp_right ? 0x4 : 0) | 230971d14cdeSscw (sp->sp_up ? 0x8 : 0) | 231071d14cdeSscw (sp->sp_down ? 0x10 : 0); 2311f4a46a86Smlelstv changed = buttons ^ (psc->buttons & 0x1f); 2312f4a46a86Smlelstv psc->buttons ^= changed; 231371d14cdeSscw 231471d14cdeSscw sc->prev_fingers = fingers; 231571d14cdeSscw 231671d14cdeSscw /* 2317fe90bab4Sblymn * Do movement processing IFF we have a single finger and no palm or 2318fe90bab4Sblymn * a secondary finger and no palm. 231971d14cdeSscw */ 232026f4df04Sjmcneill if (palm == 0 && synaptics_movement_enable) { 23214799b3aeSblymn if (fingers > 0) { 23224799b3aeSblymn synaptics_movement(sc, sp, &dx, &dy, &dz, &sdx, &sdy, 23234799b3aeSblymn &sdz); 23244799b3aeSblymn 2325b7975551Snia /* 23264799b3aeSblymn * Check if there are two fingers, if there are then 23274799b3aeSblymn * check if we have a clickpad, if we do then we 23284799b3aeSblymn * don't scroll iff one of the fingers is in the 23294799b3aeSblymn * button region. Otherwise interpret as a scroll 2330b7975551Snia */ 23314799b3aeSblymn if (sp->sp_finger_count >= 2 && sc->gesture_type == 0 ) { 23324799b3aeSblymn if (!(sc->flags & SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD) || 23334799b3aeSblymn ((sc->flags & SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD) && 23344799b3aeSblymn (sp->sp_y > synaptics_button_boundary) && 23354799b3aeSblymn (sp->sp_sy > synaptics_button_boundary))) { 2336b7975551Snia s = spltty(); 23374799b3aeSblymn wsmouse_precision_scroll( 23384799b3aeSblymn psc->sc_wsmousedev, dx, dy); 2339b7975551Snia splx(s); 2340b7975551Snia return; 23414799b3aeSblymn } 23424799b3aeSblymn } 23434799b3aeSblymn 23444799b3aeSblymn /* 23454799b3aeSblymn * Allow user to turn off movements in the button 23464799b3aeSblymn * region of a click pad. 23474799b3aeSblymn */ 23484799b3aeSblymn if (sc->flags & SYN_FLAG_HAS_ONE_BUTTON_CLICKPAD) { 23494799b3aeSblymn if ((sp->sp_y < synaptics_button_boundary) && 23504799b3aeSblymn (!synaptics_button_region_movement)) { 23514799b3aeSblymn dx = dy = dz = 0; 23524799b3aeSblymn } 23534799b3aeSblymn 23544799b3aeSblymn if ((sp->sp_sy < synaptics_button_boundary) && 23554799b3aeSblymn (!synaptics_button_region_movement)) { 23564799b3aeSblymn sdx = sdy = sdz = 0; 23574799b3aeSblymn } 23584799b3aeSblymn } 23594799b3aeSblymn 23604799b3aeSblymn /* Now work out which finger to report, just 23614799b3aeSblymn * go with the one that has moved the most. 23624799b3aeSblymn */ 23634799b3aeSblymn if ((abs(dx) + abs(dy) + abs(dz)) < 23644799b3aeSblymn (abs(sdx) + abs(sdy) + abs(sdz))) { 23654799b3aeSblymn /* secondary finger wins */ 23664799b3aeSblymn dx = sdx; 23674799b3aeSblymn dy = sdy; 23684799b3aeSblymn dz = sdz; 23694799b3aeSblymn } 2370fe90bab4Sblymn } else { 237171d14cdeSscw /* 237271d14cdeSscw * No valid finger. Therefore no movement. 237371d14cdeSscw */ 23744799b3aeSblymn sc->rem_x[SYN_PRIMARY_FINGER] = 0; 23754799b3aeSblymn sc->rem_y[SYN_PRIMARY_FINGER] = 0; 23764799b3aeSblymn sc->rem_x[SYN_SECONDARY_FINGER] = 0; 23774799b3aeSblymn sc->rem_y[SYN_SECONDARY_FINGER] = 0; 2378fe90bab4Sblymn dx = dy = 0; 2379e6ab2e16Smlelstv 2380e6ab2e16Smlelstv sc->packet_count[SYN_PRIMARY_FINGER] = 0; 2381e6ab2e16Smlelstv sc->packet_count[SYN_SECONDARY_FINGER] = 0; 2382fe90bab4Sblymn } 2383fe90bab4Sblymn } else { 2384fe90bab4Sblymn /* 2385fe90bab4Sblymn * No valid finger. Therefore no movement. 2386fe90bab4Sblymn */ 23874799b3aeSblymn sc->rem_x[SYN_PRIMARY_FINGER] = 0; 23884799b3aeSblymn sc->rem_y[SYN_PRIMARY_FINGER] = 0; 23894799b3aeSblymn sc->rem_z[SYN_PRIMARY_FINGER] = 0; 2390abbf616bSblymn dx = dy = dz = 0; 239171d14cdeSscw } 239271d14cdeSscw 23934799b3aeSblymn DPRINTF(10, sc, 23944799b3aeSblymn "pms_synaptics_process_packet: dx %d dy %d dz %d sdx %d sdy %d sdz %d\n", 23954799b3aeSblymn dx, dy, dz, sdx, sdy, sdz); 23964799b3aeSblymn 239771d14cdeSscw /* 239871d14cdeSscw * Pass the final results up to wsmouse_input() if necessary. 239971d14cdeSscw */ 240071d14cdeSscw if (dx || dy || dz || changed) { 2401f4a46a86Smlelstv buttons = (psc->buttons & 0x1f) | ((psc->buttons >> 5) & 0x7); 240271d14cdeSscw s = spltty(); 240357c0199dSplunky wsmouse_input(psc->sc_wsmousedev, 240457c0199dSplunky buttons, 240557c0199dSplunky dx, dy, dz, 0, 240681e1a623Schristos WSMOUSE_INPUT_DELTA); 240771d14cdeSscw splx(s); 240881e1a623Schristos } 240981e1a623Schristos } 2410