synaptics.c revision 1.23 1 /* $NetBSD: synaptics.c,v 1.23 2010/03/21 19:57:05 plunky Exp $ */
2
3 /*
4 * Copyright (c) 2005, Steve C. Woodford
5 * Copyright (c) 2004, Ales Krenek
6 * Copyright (c) 2004, Kentaro A. Kurahone
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * * Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
19 * * Neither the name of the authors nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
38 /*
39 * TODO:
40 * - Make the sysctl values per-instance instead of global.
41 * - Consider setting initial scaling factors at runtime according
42 * to the values returned by the 'Read Resolutions' command.
43 * - Support the serial protocol (we only support PS/2 for now)
44 * - Support auto-repeat for up/down button Z-axis emulation.
45 * - Maybe add some more gestures (can we use Palm support somehow?)
46 */
47
48 #include "opt_pms.h"
49
50 #include <sys/cdefs.h>
51 __KERNEL_RCSID(0, "$NetBSD: synaptics.c,v 1.23 2010/03/21 19:57:05 plunky Exp $");
52
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/device.h>
56 #include <sys/ioctl.h>
57 #include <sys/sysctl.h>
58 #include <sys/kernel.h>
59
60 #include <sys/bus.h>
61
62 #include <dev/pckbport/pckbportvar.h>
63
64 #include <dev/pckbport/synapticsreg.h>
65 #include <dev/pckbport/synapticsvar.h>
66
67 #include <dev/pckbport/pmsreg.h>
68 #include <dev/pckbport/pmsvar.h>
69
70 #include <dev/wscons/wsconsio.h>
71 #include <dev/wscons/wsmousevar.h>
72
73 /*
74 * Absolute-mode packets are decoded and passed around using
75 * the following structure.
76 */
77 struct synaptics_packet {
78 signed short sp_x; /* Unscaled absolute X/Y coordinates */
79 signed short sp_y;
80 u_char sp_z; /* Z (pressure) */
81 u_char sp_w; /* W (contact patch width) */
82 char sp_left; /* Left mouse button status */
83 char sp_right; /* Right mouse button status */
84 char sp_middle; /* Middle button status (possibly emulated) */
85 char sp_up; /* Up button status */
86 char sp_down; /* Down button status */
87 };
88
89 static int pms_synaptics_send_command(pckbport_tag_t, pckbport_slot_t, u_char);
90 static void pms_synaptics_input(void *, int);
91 static void pms_synaptics_process_packet(struct pms_softc *,
92 struct synaptics_packet *);
93 static void pms_sysctl_synaptics(struct sysctllog **);
94 static int pms_sysctl_synaptics_verify(SYSCTLFN_ARGS);
95
96 /* Controled by sysctl. */
97 static int synaptics_up_down_emul = 2;
98 static int synaptics_up_down_motion_delta = 1;
99 static int synaptics_gesture_move = 200;
100 static int synaptics_gesture_length = 20;
101 static int synaptics_edge_left = SYNAPTICS_EDGE_LEFT;
102 static int synaptics_edge_right = SYNAPTICS_EDGE_RIGHT;
103 static int synaptics_edge_top = SYNAPTICS_EDGE_TOP;
104 static int synaptics_edge_bottom = SYNAPTICS_EDGE_BOTTOM;
105 static int synaptics_edge_motion_delta = 32;
106 static u_int synaptics_finger_high = SYNAPTICS_FINGER_LIGHT + 5;
107 static u_int synaptics_finger_low = SYNAPTICS_FINGER_LIGHT - 10;
108 static int synaptics_two_fingers_emul = 0;
109 static int synaptics_scale_x = 16;
110 static int synaptics_scale_y = 16;
111 static int synaptics_max_speed_x = 32;
112 static int synaptics_max_speed_y = 32;
113 static int synaptics_movement_threshold = 4;
114
115 /* Sysctl nodes. */
116 static int synaptics_up_down_emul_nodenum;
117 static int synaptics_up_down_motion_delta_nodenum;
118 static int synaptics_gesture_move_nodenum;
119 static int synaptics_gesture_length_nodenum;
120 static int synaptics_edge_left_nodenum;
121 static int synaptics_edge_right_nodenum;
122 static int synaptics_edge_top_nodenum;
123 static int synaptics_edge_bottom_nodenum;
124 static int synaptics_edge_motion_delta_nodenum;
125 static int synaptics_finger_high_nodenum;
126 static int synaptics_finger_low_nodenum;
127 static int synaptics_two_fingers_emul_nodenum;
128 static int synaptics_scale_x_nodenum;
129 static int synaptics_scale_y_nodenum;
130 static int synaptics_max_speed_x_nodenum;
131 static int synaptics_max_speed_y_nodenum;
132 static int synaptics_movement_threshold_nodenum;
133
134 int
135 pms_synaptics_probe_init(void *vsc)
136 {
137 struct pms_softc *psc = vsc;
138 struct synaptics_softc *sc = &psc->u.synaptics;
139 u_char cmd[2], resp[3];
140 int res, ver_minor, ver_major;
141 struct sysctllog *clog = NULL;
142
143 res = pms_synaptics_send_command(psc->sc_kbctag, psc->sc_kbcslot,
144 SYNAPTICS_IDENTIFY_TOUCHPAD);
145 cmd[0] = PMS_SEND_DEV_STATUS;
146 res |= pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd, 1, 3,
147 resp, 0);
148 if (res) {
149 #ifdef SYNAPTICSDEBUG
150 aprint_normal_dev(psc->sc_dev,
151 "synaptics_probe: Identify Touchpad error.\n");
152 #endif
153 /*
154 * Reset device in case the probe confused it.
155 */
156 doreset:
157 cmd[0] = PMS_RESET;
158 (void) pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd,
159 1, 2, resp, 1);
160 return (res);
161 }
162
163 if (resp[1] != SYNAPTICS_MAGIC_BYTE) {
164 #ifdef SYNAPTICSDEBUG
165 aprint_normal_dev(psc->sc_dev,
166 "synaptics_probe: Not synaptics.\n");
167 #endif
168 res = 1;
169 goto doreset;
170 }
171
172 sc->flags = 0;
173
174 /* Check for minimum version and print a nice message. */
175 ver_major = resp[2] & 0x0f;
176 ver_minor = resp[0];
177 aprint_normal_dev(psc->sc_dev, "Synaptics touchpad version %d.%d\n",
178 ver_major, ver_minor);
179 if (ver_major * 10 + ver_minor < SYNAPTICS_MIN_VERSION) {
180 /* No capability query support. */
181 sc->caps = 0;
182 goto done;
183 }
184
185 /* Query the hardware capabilities. */
186 res = pms_synaptics_send_command(psc->sc_kbctag, psc->sc_kbcslot,
187 SYNAPTICS_READ_CAPABILITIES);
188 cmd[0] = PMS_SEND_DEV_STATUS;
189 res |= pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd, 1, 3,
190 resp, 0);
191 if (res) {
192 /* Hmm, failed to get capabilites. */
193 aprint_error_dev(psc->sc_dev,
194 "synaptics_probe: Failed to query capabilities.\n");
195 goto doreset;
196 }
197
198 sc->caps = (resp[0] << 8) | resp[2];
199
200 if (sc->caps & SYNAPTICS_CAP_MBUTTON)
201 sc->flags |= SYN_FLAG_HAS_MIDDLE_BUTTON;
202
203 if (sc->caps & SYNAPTICS_CAP_4BUTTON)
204 sc->flags |= SYN_FLAG_HAS_BUTTONS_4_5;
205
206 if (sc->caps & SYNAPTICS_CAP_EXTENDED) {
207 #ifdef SYNAPTICSDEBUG
208 aprint_normal_dev(psc->sc_dev,
209 "synaptics_probe: Capabilities 0x%04x.\n", sc->caps);
210 #endif
211 if (sc->caps & SYNAPTICS_CAP_PASSTHROUGH)
212 sc->flags |= SYN_FLAG_HAS_PASSTHROUGH;
213
214 if (sc->caps & SYNAPTICS_CAP_PALMDETECT)
215 sc->flags |= SYN_FLAG_HAS_PALM_DETECT;
216
217 if (sc->caps & SYNAPTICS_CAP_MULTIDETECT)
218 sc->flags |= SYN_FLAG_HAS_MULTI_FINGER;
219
220 /* Ask about extra buttons to detect up/down. */
221 if (sc->caps & SYNAPTICS_CAP_EXTNUM) {
222 res = pms_synaptics_send_command(psc->sc_kbctag,
223 psc->sc_kbcslot, SYNAPTICS_EXTENDED_QUERY);
224 cmd[0] = PMS_SEND_DEV_STATUS;
225 res |= pckbport_poll_cmd(psc->sc_kbctag,
226 psc->sc_kbcslot, cmd, 1, 3, resp, 0);
227 #ifdef SYNAPTICSDEBUG
228 if (res == 0)
229 aprint_normal_dev(psc->sc_dev,
230 "synaptics_probe: Extended "
231 "Capabilities 0x%02x.\n", resp[1]);
232 #endif
233 if (!res && (resp[1] >> 4) >= 2) {
234 /* Yes. */
235 sc->flags |= SYN_FLAG_HAS_UP_DOWN_BUTTONS;
236 }
237 }
238 }
239
240 if (sc->flags) {
241 const char comma[] = ", ";
242 const char *sep = "";
243 aprint_normal_dev(psc->sc_dev, "");
244 if (sc->flags & SYN_FLAG_HAS_PASSTHROUGH) {
245 aprint_normal("%sPassthrough", sep);
246 sep = comma;
247 }
248 if (sc->flags & SYN_FLAG_HAS_MIDDLE_BUTTON) {
249 aprint_normal("%sMiddle button", sep);
250 sep = comma;
251 }
252 if (sc->flags & SYN_FLAG_HAS_BUTTONS_4_5) {
253 aprint_normal("%sButtons 4/5", sep);
254 sep = comma;
255 }
256 if (sc->flags & SYN_FLAG_HAS_UP_DOWN_BUTTONS) {
257 aprint_normal("%sUp/down buttons", sep);
258 sep = comma;
259 }
260 if (sc->flags & SYN_FLAG_HAS_PALM_DETECT) {
261 aprint_normal("%sPalm detect", sep);
262 sep = comma;
263 }
264 if (sc->flags & SYN_FLAG_HAS_MULTI_FINGER)
265 aprint_normal("%sMulti-finger", sep);
266
267 aprint_normal("\n");
268 }
269
270 done:
271 pms_sysctl_synaptics(&clog);
272 pckbport_set_inputhandler(psc->sc_kbctag, psc->sc_kbcslot,
273 pms_synaptics_input, psc, device_xname(psc->sc_dev));
274
275 return (0);
276 }
277
278 void
279 pms_synaptics_enable(void *vsc)
280 {
281 struct pms_softc *psc = vsc;
282 struct synaptics_softc *sc = &psc->u.synaptics;
283 u_char cmd[2], resp[2];
284 int res;
285
286 if (sc->flags & SYN_FLAG_HAS_PASSTHROUGH) {
287 /*
288 * Extended capability probes can confuse the passthrough device;
289 * reset the touchpad now to cure that.
290 */
291 cmd[0] = PMS_RESET;
292 res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd,
293 1, 2, resp, 1);
294 }
295
296 /*
297 * Enable Absolute mode with W (width) reporting, and set
298 * the packet rate to maximum (80 packets per second).
299 */
300 res = pms_synaptics_send_command(psc->sc_kbctag, psc->sc_kbcslot,
301 SYNAPTICS_MODE_ABSOLUTE | SYNAPTICS_MODE_W | SYNAPTICS_MODE_RATE);
302 cmd[0] = PMS_SET_SAMPLE;
303 cmd[1] = SYNAPTICS_CMD_SET_MODE2;
304 res |= pckbport_enqueue_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd, 2, 0,
305 1, NULL);
306 sc->up_down = 0;
307 sc->prev_fingers = 0;
308 sc->gesture_start_x = sc->gesture_start_y = 0;
309 sc->gesture_start_packet = 0;
310 sc->gesture_tap_packet = 0;
311 sc->gesture_type = 0;
312 sc->gesture_buttons = 0;
313 sc->rem_x = sc->rem_y = 0;
314 sc->movement_history = 0;
315 if (res) {
316 aprint_error_dev(psc->sc_dev,
317 "synaptics_enable: Error enabling device.\n");
318 }
319 }
320
321 void
322 pms_synaptics_resume(void *vsc)
323 {
324 struct pms_softc *psc = vsc;
325 unsigned char cmd[1],resp[2] = { 0,0 };
326 int res;
327
328 cmd[0] = PMS_RESET;
329 res = pckbport_poll_cmd(psc->sc_kbctag, psc->sc_kbcslot, cmd, 1, 2,
330 resp, 1);
331 aprint_debug_dev(psc->sc_dev,
332 "pms_synaptics_resume: reset on resume %d 0x%02x 0x%02x\n",
333 res, resp[0], resp[1]);
334 }
335
336 static void
337 pms_sysctl_synaptics(struct sysctllog **clog)
338 {
339 int rc, root_num;
340 const struct sysctlnode *node;
341
342 if ((rc = sysctl_createv(clog, 0, NULL, NULL,
343 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
344 NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0)
345 goto err;
346
347 if ((rc = sysctl_createv(clog, 0, NULL, &node,
348 CTLFLAG_PERMANENT, CTLTYPE_NODE, "synaptics",
349 SYSCTL_DESCR("Synaptics touchpad controls"),
350 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0)
351 goto err;
352
353 root_num = node->sysctl_num;
354
355 if ((rc = sysctl_createv(clog, 0, NULL, &node,
356 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
357 CTLTYPE_INT, "up_down_emulation",
358 SYSCTL_DESCR("Middle button/Z-axis emulation with up/down buttons"),
359 pms_sysctl_synaptics_verify, 0,
360 &synaptics_up_down_emul,
361 0, CTL_HW, root_num, CTL_CREATE,
362 CTL_EOL)) != 0)
363 goto err;
364
365 synaptics_up_down_emul_nodenum = node->sysctl_num;
366
367 if ((rc = sysctl_createv(clog, 0, NULL, &node,
368 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
369 CTLTYPE_INT, "up_down_motion_delta",
370 SYSCTL_DESCR("Up/down button Z-axis emulation rate"),
371 pms_sysctl_synaptics_verify, 0,
372 &synaptics_up_down_motion_delta,
373 0, CTL_HW, root_num, CTL_CREATE,
374 CTL_EOL)) != 0)
375 goto err;
376
377 synaptics_up_down_motion_delta_nodenum = node->sysctl_num;
378
379 if ((rc = sysctl_createv(clog, 0, NULL, &node,
380 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
381 CTLTYPE_INT, "gesture_move",
382 SYSCTL_DESCR("Movement greater than this between taps cancels gesture"),
383 pms_sysctl_synaptics_verify, 0,
384 &synaptics_gesture_move,
385 0, CTL_HW, root_num, CTL_CREATE,
386 CTL_EOL)) != 0)
387 goto err;
388
389 synaptics_gesture_move_nodenum = node->sysctl_num;
390
391 if ((rc = sysctl_createv(clog, 0, NULL, &node,
392 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
393 CTLTYPE_INT, "gesture_length",
394 SYSCTL_DESCR("Time period in which tap is recognised as a gesture"),
395 pms_sysctl_synaptics_verify, 0,
396 &synaptics_gesture_length,
397 0, CTL_HW, root_num, CTL_CREATE,
398 CTL_EOL)) != 0)
399 goto err;
400
401 synaptics_gesture_length_nodenum = node->sysctl_num;
402
403 if ((rc = sysctl_createv(clog, 0, NULL, &node,
404 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
405 CTLTYPE_INT, "edge_left",
406 SYSCTL_DESCR("Define left edge of touchpad"),
407 pms_sysctl_synaptics_verify, 0,
408 &synaptics_edge_left,
409 0, CTL_HW, root_num, CTL_CREATE,
410 CTL_EOL)) != 0)
411 goto err;
412
413 synaptics_edge_left_nodenum = node->sysctl_num;
414
415 if ((rc = sysctl_createv(clog, 0, NULL, &node,
416 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
417 CTLTYPE_INT, "edge_right",
418 SYSCTL_DESCR("Define right edge of touchpad"),
419 pms_sysctl_synaptics_verify, 0,
420 &synaptics_edge_right,
421 0, CTL_HW, root_num, CTL_CREATE,
422 CTL_EOL)) != 0)
423 goto err;
424
425 synaptics_edge_right_nodenum = node->sysctl_num;
426
427 if ((rc = sysctl_createv(clog, 0, NULL, &node,
428 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
429 CTLTYPE_INT, "edge_top",
430 SYSCTL_DESCR("Define top edge of touchpad"),
431 pms_sysctl_synaptics_verify, 0,
432 &synaptics_edge_top,
433 0, CTL_HW, root_num, CTL_CREATE,
434 CTL_EOL)) != 0)
435 goto err;
436
437 synaptics_edge_top_nodenum = node->sysctl_num;
438
439 if ((rc = sysctl_createv(clog, 0, NULL, &node,
440 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
441 CTLTYPE_INT, "edge_bottom",
442 SYSCTL_DESCR("Define bottom edge of touchpad"),
443 pms_sysctl_synaptics_verify, 0,
444 &synaptics_edge_bottom,
445 0, CTL_HW, root_num, CTL_CREATE,
446 CTL_EOL)) != 0)
447 goto err;
448
449 synaptics_edge_bottom_nodenum = node->sysctl_num;
450
451 if ((rc = sysctl_createv(clog, 0, NULL, &node,
452 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
453 CTLTYPE_INT, "edge_motion_delta",
454 SYSCTL_DESCR("Define edge motion rate"),
455 pms_sysctl_synaptics_verify, 0,
456 &synaptics_edge_motion_delta,
457 0, CTL_HW, root_num, CTL_CREATE,
458 CTL_EOL)) != 0)
459 goto err;
460
461 synaptics_edge_motion_delta_nodenum = node->sysctl_num;
462
463 if ((rc = sysctl_createv(clog, 0, NULL, &node,
464 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
465 CTLTYPE_INT, "finger_high",
466 SYSCTL_DESCR("Define finger applied pressure threshold"),
467 pms_sysctl_synaptics_verify, 0,
468 &synaptics_finger_high,
469 0, CTL_HW, root_num, CTL_CREATE,
470 CTL_EOL)) != 0)
471 goto err;
472
473 synaptics_finger_high_nodenum = node->sysctl_num;
474
475 if ((rc = sysctl_createv(clog, 0, NULL, &node,
476 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
477 CTLTYPE_INT, "finger_low",
478 SYSCTL_DESCR("Define finger removed pressure threshold"),
479 pms_sysctl_synaptics_verify, 0,
480 &synaptics_finger_low,
481 0, CTL_HW, root_num, CTL_CREATE,
482 CTL_EOL)) != 0)
483 goto err;
484
485 synaptics_finger_low_nodenum = node->sysctl_num;
486
487 if ((rc = sysctl_createv(clog, 0, NULL, &node,
488 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
489 CTLTYPE_INT, "two_fingers_emulation",
490 SYSCTL_DESCR("Map two fingers to middle button"),
491 pms_sysctl_synaptics_verify, 0,
492 &synaptics_two_fingers_emul,
493 0, CTL_HW, root_num, CTL_CREATE,
494 CTL_EOL)) != 0)
495 goto err;
496
497 synaptics_two_fingers_emul_nodenum = node->sysctl_num;
498
499 if ((rc = sysctl_createv(clog, 0, NULL, &node,
500 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
501 CTLTYPE_INT, "scale_x",
502 SYSCTL_DESCR("Horizontal movement scale factor"),
503 pms_sysctl_synaptics_verify, 0,
504 &synaptics_scale_x,
505 0, CTL_HW, root_num, CTL_CREATE,
506 CTL_EOL)) != 0)
507 goto err;
508
509 synaptics_scale_x_nodenum = node->sysctl_num;
510
511 if ((rc = sysctl_createv(clog, 0, NULL, &node,
512 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
513 CTLTYPE_INT, "scale_y",
514 SYSCTL_DESCR("Vertical movement scale factor"),
515 pms_sysctl_synaptics_verify, 0,
516 &synaptics_scale_y,
517 0, CTL_HW, root_num, CTL_CREATE,
518 CTL_EOL)) != 0)
519 goto err;
520
521 synaptics_scale_y_nodenum = node->sysctl_num;
522
523 if ((rc = sysctl_createv(clog, 0, NULL, &node,
524 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
525 CTLTYPE_INT, "max_speed_x",
526 SYSCTL_DESCR("Horizontal movement maximum speed"),
527 pms_sysctl_synaptics_verify, 0,
528 &synaptics_max_speed_x,
529 0, CTL_HW, root_num, CTL_CREATE,
530 CTL_EOL)) != 0)
531 goto err;
532
533 synaptics_max_speed_x_nodenum = node->sysctl_num;
534
535 if ((rc = sysctl_createv(clog, 0, NULL, &node,
536 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
537 CTLTYPE_INT, "max_speed_y",
538 SYSCTL_DESCR("Vertical movement maximum speed"),
539 pms_sysctl_synaptics_verify, 0,
540 &synaptics_max_speed_y,
541 0, CTL_HW, root_num, CTL_CREATE,
542 CTL_EOL)) != 0)
543 goto err;
544
545 synaptics_max_speed_y_nodenum = node->sysctl_num;
546
547 if ((rc = sysctl_createv(clog, 0, NULL, &node,
548 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
549 CTLTYPE_INT, "movement_threshold",
550 SYSCTL_DESCR("Minimum reported movement threshold"),
551 pms_sysctl_synaptics_verify, 0,
552 &synaptics_movement_threshold,
553 0, CTL_HW, root_num, CTL_CREATE,
554 CTL_EOL)) != 0)
555 goto err;
556
557 synaptics_movement_threshold_nodenum = node->sysctl_num;
558 return;
559
560 err:
561 aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
562 }
563
564 static int
565 pms_sysctl_synaptics_verify(SYSCTLFN_ARGS)
566 {
567 int error, t;
568 struct sysctlnode node;
569
570 node = *rnode;
571 t = *(int *)rnode->sysctl_data;
572 node.sysctl_data = &t;
573 error = sysctl_lookup(SYSCTLFN_CALL(&node));
574 if (error || newp == NULL)
575 return error;
576
577 /* Sanity check the params. */
578 if (node.sysctl_num == synaptics_up_down_emul_nodenum ||
579 node.sysctl_num == synaptics_two_fingers_emul_nodenum) {
580 if (t < 0 || t > 2)
581 return (EINVAL);
582 } else
583 if (node.sysctl_num == synaptics_gesture_length_nodenum ||
584 node.sysctl_num == synaptics_edge_motion_delta_nodenum ||
585 node.sysctl_num == synaptics_up_down_motion_delta_nodenum) {
586 if (t < 0)
587 return (EINVAL);
588 } else
589 if (node.sysctl_num == synaptics_edge_left_nodenum ||
590 node.sysctl_num == synaptics_edge_bottom_nodenum) {
591 if (t < 0 || t > (SYNAPTICS_EDGE_MAX / 2))
592 return (EINVAL);
593 } else
594 if (node.sysctl_num == synaptics_edge_right_nodenum ||
595 node.sysctl_num == synaptics_edge_top_nodenum) {
596 if (t < (SYNAPTICS_EDGE_MAX / 2))
597 return (EINVAL);
598 } else
599 if (node.sysctl_num == synaptics_scale_x_nodenum ||
600 node.sysctl_num == synaptics_scale_y_nodenum) {
601 if (t < 1 || t > (SYNAPTICS_EDGE_MAX / 4))
602 return (EINVAL);
603 } else
604 if (node.sysctl_num == synaptics_finger_high_nodenum) {
605 if (t < 0 || t > SYNAPTICS_FINGER_PALM ||
606 t < synaptics_finger_low)
607 return (EINVAL);
608 } else
609 if (node.sysctl_num == synaptics_finger_low_nodenum) {
610 if (t < 0 || t > SYNAPTICS_FINGER_PALM ||
611 t > synaptics_finger_high)
612 return (EINVAL);
613 } else
614 if (node.sysctl_num == synaptics_gesture_move_nodenum ||
615 node.sysctl_num == synaptics_movement_threshold_nodenum) {
616 if (t < 0 || t > (SYNAPTICS_EDGE_MAX / 4))
617 return (EINVAL);
618 } else
619 return (EINVAL);
620
621 *(int *)rnode->sysctl_data = t;
622
623 return (0);
624 }
625
626 static int
627 pms_synaptics_send_command(pckbport_tag_t tag, pckbport_slot_t slot,
628 u_char syn_cmd)
629 {
630 u_char cmd[2];
631 int res;
632
633 /*
634 * Need to send 4 Set Resolution commands, with the argument
635 * encoded in the bottom most 2 bits.
636 */
637 cmd[0] = PMS_SET_RES;
638 cmd[1] = syn_cmd >> 6;
639 res = pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
640
641 cmd[0] = PMS_SET_RES;
642 cmd[1] = (syn_cmd & 0x30) >> 4;
643 res |= pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
644
645 cmd[0] = PMS_SET_RES;
646 cmd[1] = (syn_cmd & 0x0c) >> 2;
647 res |= pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
648
649 cmd[0] = PMS_SET_RES;
650 cmd[1] = (syn_cmd & 0x03);
651 res |= pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
652
653 return (res);
654 }
655
656 /* Masks for the first byte of a packet */
657 #define PMS_LBUTMASK 0x01
658 #define PMS_RBUTMASK 0x02
659 #define PMS_MBUTMASK 0x04
660
661 static void
662 pms_synaptics_parse(struct pms_softc *psc)
663 {
664 struct synaptics_softc *sc = &psc->u.synaptics;
665 struct synaptics_packet sp;
666
667 /* Absolute X/Y coordinates of finger */
668 sp.sp_x = psc->packet[4] + ((psc->packet[1] & 0x0f) << 8) +
669 ((psc->packet[3] & 0x10) << 8);
670 sp.sp_y = psc->packet[5] + ((psc->packet[1] & 0xf0) << 4) +
671 ((psc->packet[3] & 0x20) << 7);
672
673 /* Pressure */
674 sp.sp_z = psc->packet[2];
675
676 /* Width of finger */
677 sp.sp_w = ((psc->packet[0] & 0x30) >> 2) +
678 ((psc->packet[0] & 0x04) >> 1) +
679 ((psc->packet[3] & 0x04) >> 2);
680
681 /* Left/Right button handling. */
682 sp.sp_left = psc->packet[0] & PMS_LBUTMASK;
683 sp.sp_right = psc->packet[0] & PMS_RBUTMASK;
684
685 /* Up/Down buttons. */
686 if (sc->flags & SYN_FLAG_HAS_BUTTONS_4_5) {
687 /* Old up/down buttons. */
688 sp.sp_up = sp.sp_left ^
689 (psc->packet[3] & PMS_LBUTMASK);
690 sp.sp_down = sp.sp_right ^
691 (psc->packet[3] & PMS_RBUTMASK);
692 } else
693 if (sc->flags & SYN_FLAG_HAS_UP_DOWN_BUTTONS &&
694 ((psc->packet[0] & PMS_RBUTMASK) ^
695 (psc->packet[3] & PMS_RBUTMASK))) {
696 /* New up/down button. */
697 sp.sp_up = psc->packet[4] & SYN_1BUTMASK;
698 sp.sp_down = psc->packet[5] & SYN_2BUTMASK;
699 } else {
700 sp.sp_up = 0;
701 sp.sp_down = 0;
702 }
703
704 /* Middle button. */
705 if (sc->flags & SYN_FLAG_HAS_MIDDLE_BUTTON) {
706 /* Old style Middle Button. */
707 sp.sp_middle = (psc->packet[0] & PMS_LBUTMASK) ^
708 (psc->packet[3] & PMS_LBUTMASK);
709 } else
710 if (synaptics_up_down_emul == 1) {
711 /* Do middle button emulation using up/down buttons */
712 sp.sp_middle = sp.sp_up | sp.sp_down;
713 sp.sp_up = sp.sp_down = 0;
714 } else
715 sp.sp_middle = 0;
716
717 pms_synaptics_process_packet(psc, &sp);
718 }
719
720 static void
721 pms_synaptics_passthrough(struct pms_softc *psc)
722 {
723 int dx, dy, dz;
724 int buttons, changed;
725 int s;
726
727 buttons = ((psc->packet[1] & PMS_LBUTMASK) ? 0x20 : 0) |
728 ((psc->packet[1] & PMS_MBUTMASK) ? 0x40 : 0) |
729 ((psc->packet[1] & PMS_RBUTMASK) ? 0x80 : 0);
730
731 dx = psc->packet[4];
732 if (dx >= 128)
733 dx -= 256;
734 if (dx == -128)
735 dx = -127;
736
737 dy = psc->packet[5];
738 if (dy >= 128)
739 dy -= 256;
740 if (dy == -128)
741 dy = -127;
742
743 dz = 0;
744
745 changed = buttons ^ (psc->buttons & 0xe0);
746 psc->buttons ^= changed;
747
748 if (dx || dy || dz || changed) {
749 buttons = (psc->buttons & 0x1f) | ((psc->buttons >> 5) & 0x7);
750 s = spltty();
751 wsmouse_input(psc->sc_wsmousedev,
752 buttons, dx, dy, dz, 0,
753 WSMOUSE_INPUT_DELTA);
754 splx(s);
755 }
756 }
757
758 static void
759 pms_synaptics_input(void *vsc, int data)
760 {
761 struct pms_softc *psc = vsc;
762 struct timeval diff;
763
764 if (!psc->sc_enabled) {
765 /* Interrupts are not expected. Discard the byte. */
766 return;
767 }
768
769 getmicrouptime(&psc->current);
770
771 if (psc->inputstate > 0) {
772 timersub(&psc->current, &psc->last, &diff);
773 if (diff.tv_sec > 0 || diff.tv_usec >= 40000) {
774 aprint_debug_dev(psc->sc_dev,
775 "pms_input: unusual delay (%ld.%06ld s), "
776 "scheduling reset\n",
777 (long)diff.tv_sec, (long)diff.tv_usec);
778 psc->inputstate = 0;
779 psc->sc_enabled = 0;
780 wakeup(&psc->sc_enabled);
781 return;
782 }
783 }
784 psc->last = psc->current;
785
786 switch (psc->inputstate) {
787 case 0:
788 if ((data & 0xc8) != 0x80) {
789 #ifdef SYNAPTICSDEBUG
790 aprint_normal_dev(psc->sc_dev,
791 "pms_input: 0x%02x out of sync\n", data);
792 #endif
793 return; /* not in sync yet, discard input */
794 }
795 /*FALLTHROUGH*/
796
797 case 3:
798 if ((data & 8) == 8) {
799 #ifdef SYNAPTICSDEBUG
800 aprint_normal_dev(psc->sc_dev,
801 "pms_input: dropped in relative mode, reset\n");
802 #endif
803 psc->inputstate = 0;
804 psc->sc_enabled = 0;
805 wakeup(&psc->sc_enabled);
806 return;
807 }
808 }
809
810 psc->packet[psc->inputstate++] = data & 0xff;
811 if (psc->inputstate == 6) {
812 /*
813 * We have a complete packet.
814 * Extract the pertinent details.
815 */
816 psc->inputstate = 0;
817
818 if ((psc->packet[0] & 0xfc) == 0x84 &&
819 (psc->packet[3] & 0xcc) == 0xc4) {
820 /* PS/2 passthrough */
821 pms_synaptics_passthrough(psc);
822 } else {
823 pms_synaptics_parse(psc);
824 }
825 }
826 }
827
828 static inline int
829 synaptics_finger_detect(struct synaptics_softc *sc, struct synaptics_packet *sp,
830 int *palmp)
831 {
832 int fingers;
833
834 /* Assume no palm */
835 *palmp = 0;
836
837 /*
838 * Apply some hysteresis when checking for a finger.
839 * When the finger is first applied, we ignore it until the
840 * pressure exceeds the 'high' threshold. The finger is considered
841 * removed only when pressure falls beneath the 'low' threshold.
842 */
843 if ((sc->prev_fingers == 0 && sp->sp_z > synaptics_finger_high) ||
844 (sc->prev_fingers != 0 && sp->sp_z > synaptics_finger_low))
845 fingers = 1;
846 else
847 fingers = 0;
848
849 /*
850 * If the pad can't do palm detection, skip the rest.
851 */
852 if (fingers == 0 || (sc->flags & SYN_FLAG_HAS_PALM_DETECT) == 0)
853 return (fingers);
854
855 /*
856 * Palm detection
857 */
858 if (sp->sp_z > SYNAPTICS_FINGER_FLAT &&
859 sp->sp_w >= SYNAPTICS_WIDTH_PALM_MIN)
860 *palmp = 1;
861
862 if (sc->prev_fingers == 0 &&
863 (sp->sp_z > SYNAPTICS_FINGER_FLAT ||
864 sp->sp_w >= SYNAPTICS_WIDTH_PALM_MIN)) {
865 /*
866 * Contact area or pressure is too great to be a finger.
867 * Just ignore it for now.
868 */
869 return (0);
870 }
871
872 /*
873 * Detect 2 and 3 fingers if supported, but only if multiple
874 * fingers appear within the tap gesture time period.
875 */
876 if (sc->flags & SYN_FLAG_HAS_MULTI_FINGER &&
877 SYN_TIME(sc, sc->gesture_start_packet) < synaptics_gesture_length) {
878 switch (sp->sp_w) {
879 case SYNAPTICS_WIDTH_TWO_FINGERS:
880 fingers = 2;
881 break;
882
883 case SYNAPTICS_WIDTH_THREE_OR_MORE:
884 fingers = 3;
885 break;
886
887 case SYNAPTICS_WIDTH_PEN:
888 fingers = 1;
889 break;
890
891 default:
892 /*
893 * The width value can report spurious single-finger
894 * events after a multi-finger event.
895 */
896 if (sc->prev_fingers > 1)
897 fingers = sc->prev_fingers;
898 else
899 fingers = 1;
900 break;
901 }
902 }
903
904 return (fingers);
905 }
906
907 static inline void
908 synaptics_gesture_detect(struct synaptics_softc *sc,
909 struct synaptics_packet *sp, int fingers)
910 {
911 int gesture_len, gesture_move_x, gesture_move_y, gesture_buttons;
912 int set_buttons;
913
914 gesture_len = SYN_TIME(sc, sc->gesture_start_packet);
915 gesture_buttons = sc->gesture_buttons;
916
917 if (fingers && sc->prev_fingers == 0) {
918 /*
919 * Finger was just applied.
920 * If the previous gesture was a single-click, set things
921 * up to deal with a possible drag or double-click gesture.
922 * Basically, if the finger is removed again within
923 * 'synaptics_gesture_length' packets, this is treated
924 * as a double-click. Otherwise we will emulate holding
925 * the left button down whilst dragging the mouse.
926 */
927 if (SYN_IS_SINGLE_TAP(sc->gesture_type))
928 sc->gesture_type |= SYN_GESTURE_DRAG;
929
930 sc->gesture_start_x = sp->sp_x;
931 sc->gesture_start_y = sp->sp_y;
932 sc->gesture_start_packet = sc->total_packets;
933 } else
934 if (fingers == 0 && sc->prev_fingers != 0) {
935 /*
936 * Finger was just removed.
937 * Check if the contact time and finger movement were
938 * small enough to qualify as a gesture.
939 * Ignore finger movement if multiple fingers were
940 * detected (the pad may report coordinates for any
941 * of the fingers).
942 */
943 gesture_move_x = abs(sc->gesture_start_x - sp->sp_x);
944 gesture_move_y = abs(sc->gesture_start_y - sp->sp_y);
945
946 if (gesture_len < synaptics_gesture_length &&
947 (sc->prev_fingers > 1 ||
948 (gesture_move_x < synaptics_gesture_move &&
949 gesture_move_y < synaptics_gesture_move))) {
950 /*
951 * Looking good so far.
952 */
953 if (SYN_IS_DRAG(sc->gesture_type)) {
954 /*
955 * Promote this gesture to double-click.
956 */
957 sc->gesture_type |= SYN_GESTURE_DOUBLE;
958 sc->gesture_type &= ~SYN_GESTURE_SINGLE;
959 } else {
960 /*
961 * Single tap gesture. Set the tap length timer
962 * and flag a single-click.
963 */
964 sc->gesture_tap_packet = sc->total_packets;
965 sc->gesture_type |= SYN_GESTURE_SINGLE;
966
967 /*
968 * The gesture can be modified depending on
969 * the number of fingers detected.
970 *
971 * 1: Normal left button emulation.
972 * 2: Either middle button or right button
973 * depending on the value of the two_fingers
974 * sysctl variable.
975 * 3: Right button.
976 */
977 switch (sc->prev_fingers) {
978 case 2:
979 if (synaptics_two_fingers_emul == 1)
980 gesture_buttons |= PMS_RBUTMASK;
981 else
982 if (synaptics_two_fingers_emul == 2)
983 gesture_buttons |= PMS_MBUTMASK;
984 break;
985 case 3:
986 gesture_buttons |= PMS_RBUTMASK;
987 break;
988 default:
989 gesture_buttons |= PMS_LBUTMASK;
990 break;
991 }
992 }
993 }
994
995 /*
996 * Always clear drag state when the finger is removed.
997 */
998 sc->gesture_type &= ~SYN_GESTURE_DRAG;
999 }
1000
1001 if (sc->gesture_type == 0) {
1002 /*
1003 * There is no gesture in progress.
1004 * Clear emulated button state.
1005 */
1006 sc->gesture_buttons = 0;
1007 return;
1008 }
1009
1010 /*
1011 * A gesture is in progress.
1012 */
1013 set_buttons = 0;
1014
1015 if (SYN_IS_SINGLE_TAP(sc->gesture_type)) {
1016 /*
1017 * Single-click.
1018 * Activate the relevant button(s) until the
1019 * gesture tap timer has expired.
1020 */
1021 if (SYN_TIME(sc, sc->gesture_tap_packet) <
1022 synaptics_gesture_length)
1023 set_buttons = 1;
1024 else
1025 sc->gesture_type &= ~SYN_GESTURE_SINGLE;
1026 } else
1027 if (SYN_IS_DOUBLE_TAP(sc->gesture_type) && sc->prev_fingers == 0) {
1028 /*
1029 * Double-click.
1030 * Activate the relevant button(s) once.
1031 */
1032 set_buttons = 1;
1033 sc->gesture_type &= ~SYN_GESTURE_DOUBLE;
1034 }
1035
1036 if (set_buttons || SYN_IS_DRAG(sc->gesture_type)) {
1037 /*
1038 * Single-click and drag.
1039 * Maintain button state until the finger is removed.
1040 */
1041 sp->sp_left |= gesture_buttons & PMS_LBUTMASK;
1042 sp->sp_right |= gesture_buttons & PMS_RBUTMASK;
1043 sp->sp_middle |= gesture_buttons & PMS_MBUTMASK;
1044 }
1045
1046 sc->gesture_buttons = gesture_buttons;
1047 }
1048
1049 static inline int
1050 synaptics_filter_policy(struct synaptics_softc *sc, int *history, int value)
1051 {
1052 int a, b, rv, count;
1053
1054 count = sc->total_packets;
1055
1056 /*
1057 * Once we've accumulated at least SYN_HIST_SIZE values, combine
1058 * each new value with the previous two and return the average.
1059 *
1060 * This is necessary when the touchpad is operating in 80 packets
1061 * per second mode, as it performs little internal filtering on
1062 * reported values.
1063 *
1064 * Using a rolling average helps to filter out jitter caused by
1065 * tiny finger movements.
1066 */
1067 if (sc->movement_history >= SYN_HIST_SIZE) {
1068 a = (history[(count + 0) % SYN_HIST_SIZE] +
1069 history[(count + 1) % SYN_HIST_SIZE]) / 2;
1070
1071 b = (value + history[(count + 0) % SYN_HIST_SIZE]) / 2;
1072
1073 rv = b - a;
1074
1075 /*
1076 * Don't report the movement if it's below a certain
1077 * threshold.
1078 */
1079 if (abs(rv) < synaptics_movement_threshold)
1080 rv = 0;
1081 } else
1082 rv = 0;
1083
1084 /*
1085 * Add the new value to the history buffer.
1086 */
1087 history[(count + 1) % SYN_HIST_SIZE] = value;
1088
1089 return (rv);
1090 }
1091
1092 /* Edge detection */
1093 #define SYN_EDGE_TOP 1
1094 #define SYN_EDGE_BOTTOM 2
1095 #define SYN_EDGE_LEFT 4
1096 #define SYN_EDGE_RIGHT 8
1097
1098 static inline int
1099 synaptics_check_edge(int x, int y)
1100 {
1101 int rv = 0;
1102
1103 if (x < synaptics_edge_left)
1104 rv |= SYN_EDGE_LEFT;
1105 else
1106 if (x > synaptics_edge_right)
1107 rv |= SYN_EDGE_RIGHT;
1108
1109 if (y < synaptics_edge_bottom)
1110 rv |= SYN_EDGE_BOTTOM;
1111 else
1112 if (y > synaptics_edge_top)
1113 rv |= SYN_EDGE_TOP;
1114
1115 return (rv);
1116 }
1117
1118 static inline int
1119 synaptics_edge_motion(struct synaptics_softc *sc, int delta, int dir)
1120 {
1121
1122 /*
1123 * When edge motion is enabled, synaptics_edge_motion_delta is
1124 * combined with the current delta, together with the direction
1125 * in which to simulate the motion. The result is added to
1126 * the delta derived from finger movement. This provides a smooth
1127 * transition from finger movement to edge motion.
1128 */
1129 delta = synaptics_edge_motion_delta + (dir * delta);
1130 if (delta < 0)
1131 return (0);
1132 if (delta > synaptics_edge_motion_delta)
1133 return (synaptics_edge_motion_delta);
1134 return (delta);
1135 }
1136
1137 static inline int
1138 synaptics_scale(int delta, int scale, int *remp)
1139 {
1140 int rv;
1141
1142 /*
1143 * Scale the raw delta in Synaptics coordinates (0-6143) into
1144 * something more reasonable by dividing the raw delta by a
1145 * scale factor. Any remainder from the previous scale result
1146 * is added to the current delta before scaling.
1147 * This prevents loss of resolution for very small/slow
1148 * movements of the finger.
1149 */
1150 delta += *remp;
1151 rv = delta / scale;
1152 *remp = delta % scale;
1153
1154 return (rv);
1155 }
1156
1157 static inline void
1158 synaptics_movement(struct synaptics_softc *sc, struct synaptics_packet *sp,
1159 int *dxp, int *dyp)
1160 {
1161 int dx, dy, edge;
1162
1163 /*
1164 * Compute the next values of dx and dy
1165 */
1166 dx = synaptics_filter_policy(sc, sc->history_x, sp->sp_x);
1167 dy = synaptics_filter_policy(sc, sc->history_y, sp->sp_y);
1168
1169 /*
1170 * If we're dealing with a drag gesture, and the finger moves to
1171 * the edge of the touchpad, apply edge motion emulation if it
1172 * is enabled.
1173 */
1174 if (synaptics_edge_motion_delta && SYN_IS_DRAG(sc->gesture_type)) {
1175 edge = synaptics_check_edge(sp->sp_x, sp->sp_y);
1176
1177 if (edge & SYN_EDGE_LEFT)
1178 dx -= synaptics_edge_motion(sc, dx, 1);
1179 if (edge & SYN_EDGE_RIGHT)
1180 dx += synaptics_edge_motion(sc, dx, -1);
1181 if (edge & SYN_EDGE_BOTTOM)
1182 dy -= synaptics_edge_motion(sc, dy, 1);
1183 if (edge & SYN_EDGE_TOP)
1184 dy += synaptics_edge_motion(sc, dy, -1);
1185 }
1186
1187 /*
1188 * Apply scaling to both deltas
1189 */
1190 dx = synaptics_scale(dx, synaptics_scale_x, &sc->rem_x);
1191 dy = synaptics_scale(dy, synaptics_scale_y, &sc->rem_y);
1192
1193 /*
1194 * Clamp deltas to specified maximums.
1195 */
1196 if (dx > synaptics_max_speed_x)
1197 dx = synaptics_max_speed_x;
1198 if (dy > synaptics_max_speed_y)
1199 dy = synaptics_max_speed_y;
1200
1201 *dxp = dx;
1202 *dyp = dy;
1203
1204 sc->movement_history++;
1205 }
1206
1207 static void
1208 pms_synaptics_process_packet(struct pms_softc *psc, struct synaptics_packet *sp)
1209 {
1210 struct synaptics_softc *sc = &psc->u.synaptics;
1211 int dx, dy, dz;
1212 int fingers, palm, buttons, changed;
1213 int s;
1214
1215 /*
1216 * Do Z-axis emulation using up/down buttons if required.
1217 * Note that the pad will send a one second burst of packets
1218 * when an up/down button is pressed and held. At the moment
1219 * we don't deal with auto-repeat, so convert the burst into
1220 * a one-shot.
1221 */
1222 dz = 0;
1223 if (synaptics_up_down_emul == 2) {
1224 if (sc->up_down == 0) {
1225 if (sp->sp_up && sp->sp_down) {
1226 /*
1227 * Most up/down buttons will be actuated using
1228 * a rocker switch, so we should never see
1229 * them both simultaneously. But just in case,
1230 * treat this situation as a middle button
1231 * event.
1232 */
1233 sp->sp_middle = 1;
1234 } else
1235 if (sp->sp_up)
1236 dz = -synaptics_up_down_motion_delta;
1237 else
1238 if (sp->sp_down)
1239 dz = synaptics_up_down_motion_delta;
1240 }
1241
1242 sc->up_down = sp->sp_up | sp->sp_down;
1243 sp->sp_up = sp->sp_down = 0;
1244 }
1245
1246 /*
1247 * Determine whether or not a finger is on the pad.
1248 * On some pads, this will return the number of fingers
1249 * detected.
1250 */
1251 fingers = synaptics_finger_detect(sc, sp, &palm);
1252
1253 /*
1254 * Do gesture processing only if we didn't detect a palm.
1255 */
1256 if (palm == 0)
1257 synaptics_gesture_detect(sc, sp, fingers);
1258 else
1259 sc->gesture_type = sc->gesture_buttons = 0;
1260
1261 /*
1262 * Determine what buttons to report
1263 */
1264 buttons = (sp->sp_left ? 0x1 : 0) |
1265 (sp->sp_middle ? 0x2 : 0) |
1266 (sp->sp_right ? 0x4 : 0) |
1267 (sp->sp_up ? 0x8 : 0) |
1268 (sp->sp_down ? 0x10 : 0);
1269 changed = buttons ^ (psc->buttons & 0x1f);
1270 psc->buttons ^= changed;
1271
1272 sc->prev_fingers = fingers;
1273 sc->total_packets++;
1274
1275 /*
1276 * Do movement processing IFF we have a single finger and no palm.
1277 */
1278 if (fingers == 1 && palm == 0)
1279 synaptics_movement(sc, sp, &dx, &dy);
1280 else {
1281 /*
1282 * No valid finger. Therefore no movement.
1283 */
1284 sc->movement_history = 0;
1285 sc->rem_x = sc->rem_y = 0;
1286 dx = dy = 0;
1287 }
1288
1289 /*
1290 * Pass the final results up to wsmouse_input() if necessary.
1291 */
1292 if (dx || dy || dz || changed) {
1293 buttons = (psc->buttons & 0x1f) | ((psc->buttons >> 5) & 0x7);
1294 s = spltty();
1295 wsmouse_input(psc->sc_wsmousedev,
1296 buttons,
1297 dx, dy, dz, 0,
1298 WSMOUSE_INPUT_DELTA);
1299 splx(s);
1300 }
1301 }
1302