synaptics.c revision 1.1 1 /* $NetBSD: synaptics.c,v 1.1 2004/12/24 18:33:06 christos Exp $ */
2
3 /*
4 * Copyright (c) 2004, Ales Krenek
5 * Copyright (c) 2004, Kentaro A. Kurahone
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 * * Neither the name of the authors nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 *
35 */
36
37 #include <sys/cdefs.h>
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/device.h>
42 #include <sys/ioctl.h>
43 #include <sys/sysctl.h>
44 #include <sys/kernel.h>
45 #include <sys/kthread.h>
46
47 #include <machine/bus.h>
48
49 #include <dev/pckbport/pckbportvar.h>
50
51 #include <dev/pckbport/synapticsreg.h>
52 #include <dev/pckbport/synapticsvar.h>
53
54 #include <dev/pckbport/pmsreg.h>
55 #include <dev/pckbport/pmsvar.h>
56
57 #include <dev/wscons/wsconsio.h>
58 #include <dev/wscons/wsmousevar.h>
59
60 static int pms_synaptics_send_command(pckbport_tag_t, pckbport_slot_t, u_char);
61 static void pms_synaptics_spawn_thread(void *);
62 static void pms_synaptics_input(void *, int);
63 static void pms_synaptics_thread(void *);
64 static int pms_sysctl_synaptics_verify(SYSCTLFN_ARGS);
65 static long sicnt,tppack,ptpack;
66
67 /* Controled by sysctl. */
68 static int synaptics_slow_limit = SYNAPTICS_SLOW_LIMIT;
69 static int synaptics_emulate_mid = 0;
70 static int synaptics_tap_enable = 1;
71 static int synaptics_tap_length = SYNAPTICS_TAP_LENGTH;
72 static int synaptics_tap_tolerance = SYNAPTICS_TAP_TOLERANCE;
73
74 /* Sysctl nodes. */
75 static int synaptics_slow_limit_nodenum;
76 static int synaptics_emulate_mid_nodenum;
77 static int synaptics_tap_enable_nodenum;
78 static int synaptics_tap_length_nodenum;
79 static int synaptics_tap_tolerance_nodenum;
80
81 int
82 pms_synaptics_probe_init(void *vsc)
83 {
84 struct pms_softc *sc = vsc;
85 struct synaptics_softc *syn_sc = &sc->u.synaptics;
86 u_char cmd[2], resp[3];
87 int res;
88 int ver_minor, ver_major;
89
90 res = pms_synaptics_send_command(sc->sc_kbctag, sc->sc_kbcslot,
91 SYNAPTICS_IDENTIFY_TOUCHPAD);
92 cmd[0] = PMS_SEND_DEV_STATUS;
93 res |= pckbport_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
94 1, 3, resp, 0);
95 if (res) {
96 #ifdef SYNAPTICSDEBUG
97 printf("synaptics_probe: Identify Touchpad error.\n");
98 #endif
99 return res;
100 }
101
102 if (resp[1] != SYNAPTICS_MAGIC_BYTE) {
103 #ifdef SYNAPTICSDEBUG
104 printf("synaptics_probe: Not synaptics.\n");
105 #endif
106 return -1;
107 }
108
109 /* Check for minimum version and print a nice message. */
110 ver_major = resp[2] & 0x0f;
111 ver_minor = resp[0];
112 printf("%s: Synaptics touchpad version %d.%d\n",
113 sc->sc_dev.dv_xname, ver_major, ver_minor);
114 if (ver_major * 10 + ver_minor < SYNAPTICS_MIN_VERSION) {
115 /* No capability query support. */
116 syn_sc->caps = 0;
117 syn_sc->enable_passthrough = 0;
118 syn_sc->has_passthrough = 0;
119 syn_sc->has_m_button = 0;
120 syn_sc->has_buttons_4_5 = 0;
121 syn_sc->has_buttons_4_5_ext = 0;
122 goto done;
123 }
124
125 /* Query the hardware capabilities. */
126 res = pms_synaptics_send_command(sc->sc_kbctag, sc->sc_kbcslot,
127 SYNAPTICS_READ_CAPABILITIES);
128 cmd[0] = PMS_SEND_DEV_STATUS;
129 res |= pckbport_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
130 1, 3, resp, 0);
131 if (res) {
132 /* Hmm, failed to get capabilites. */
133 printf("%s: Failed to query capabilities.\n",
134 sc->sc_dev.dv_xname);
135 return -1;
136 }
137
138 syn_sc->caps = (resp[0] << 8) | (resp[3]);
139 syn_sc->has_m_button = syn_sc->caps & SYNAPTICS_CAP_MBUTTON;
140 syn_sc->has_buttons_4_5 = syn_sc->caps & SYNAPTICS_CAP_4BUTTON;
141 if (!(syn_sc->caps & SYNAPTICS_CAP_EXTENDED)) {
142 /* Eeep. No extended capabilities. */
143 syn_sc->has_passthrough = 0;
144 syn_sc->enable_passthrough = 0;
145 } else {
146 #ifdef SYNAPTICSDEBUG
147 printf("synaptics_probe: Capabilities %2x.\n", syn_sc->caps);
148 #endif
149 syn_sc->has_passthrough = syn_sc->caps &
150 SYNAPTICS_CAP_PASSTHROUGH;
151 syn_sc->enable_passthrough = 0; /* Not yet. */
152
153 /* Ask about extra buttons to detect up/down. */
154 if (syn_sc->caps & SYNAPTICS_CAP_EXTNUM) {
155 res = pms_synaptics_send_command(sc->sc_kbctag,
156 sc->sc_kbcslot, SYNAPTICS_EXTENDED_QUERY);
157 cmd[0] = PMS_SEND_DEV_STATUS;
158 res |= pckbport_poll_cmd(sc->sc_kbctag,
159 sc->sc_kbcslot, cmd, 1, 3, resp, 0);
160 if ((!res) && ((resp[1] >> 4) >= 2)) {
161 /* Yes. */
162 syn_sc->has_buttons_4_5_ext = 1;
163 } else {
164 /* Guess not. */
165 syn_sc->has_buttons_4_5_ext = 0;
166 }
167 } else
168 syn_sc->has_buttons_4_5_ext = 0;
169 }
170
171 done:
172 syn_sc->buf_full = 0;
173 kthread_create(pms_synaptics_spawn_thread, sc);
174 pckbport_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot,
175 pms_synaptics_input, sc, sc->sc_dev.dv_xname);
176 return 0;
177 }
178
179 void
180 pms_synaptics_enable(void *vsc)
181 {
182 struct pms_softc *sc = vsc;
183 struct synaptics_softc *syn_sc = &sc->u.synaptics;
184 u_char cmd[2];
185 int res;
186 u_char mode = SYNAPTICS_MODE_ABSOLUTE | SYNAPTICS_MODE_W;
187
188 res = pms_synaptics_send_command(sc->sc_kbctag, sc->sc_kbcslot, mode);
189 cmd[0] = PMS_SET_SAMPLE;
190 cmd[1] = 0x14; /* doit */
191 res |= pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot,
192 cmd, 2, 0, 1, NULL);
193 syn_sc->last_x = syn_sc->last_y = -1;
194 if (res)
195 printf("synaptics_enable: Error enabling device.\n");
196 }
197
198 void
199 pms_synaptics_resume(void *vsc)
200 {
201 struct pms_softc *sc = vsc;
202 unsigned char cmd[1],resp[2] = { 0,0 };
203 int res;
204
205 cmd[0] = PMS_RESET;
206 res = pckbport_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
207 1, 2, resp, 1);
208 printf("%s: reset on resume %d 0x%02x 0x%02x\n",
209 sc->sc_dev.dv_xname,res,resp[0],resp[1]);
210 }
211
212 SYSCTL_SETUP(pms_sysctl_synaptics, "sysctl synaptics subtree setup")
213 {
214 int rc, root_num;
215 struct sysctlnode *node;
216
217 if ((rc = sysctl_createv(clog, 0, NULL, NULL,
218 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
219 NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0)
220 goto err;
221
222 if ((rc = sysctl_createv(clog, 0, NULL, &node,
223 CTLFLAG_PERMANENT, CTLTYPE_NODE, "synaptics",
224 SYSCTL_DESCR("Synaptics touchpad controls"),
225 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0)
226 goto err;
227
228 root_num = node->sysctl_num;
229
230 if ((rc = sysctl_createv(clog, 0, NULL, &node,
231 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
232 CTLTYPE_INT, "emulate_mid",
233 SYSCTL_DESCR("Emulate middle button with wheel"),
234 pms_sysctl_synaptics_verify, 0,
235 &synaptics_emulate_mid,
236 0, CTL_HW, root_num, CTL_CREATE,
237 CTL_EOL)) != 0)
238 goto err;
239
240 synaptics_emulate_mid_nodenum = node->sysctl_num;
241
242 if ((rc = sysctl_createv(clog, 0, NULL, &node,
243 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
244 CTLTYPE_INT, "slow_limit",
245 SYSCTL_DESCR("Slow limit"),
246 pms_sysctl_synaptics_verify, 0,
247 &synaptics_slow_limit,
248 0, CTL_HW, root_num, CTL_CREATE,
249 CTL_EOL)) != 0)
250 goto err;
251
252 synaptics_slow_limit_nodenum = node->sysctl_num;
253
254 if ((rc = sysctl_createv(clog, 0, NULL, &node,
255 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
256 CTLTYPE_INT, "tap_enable",
257 SYSCTL_DESCR("Process taps as clicks"),
258 pms_sysctl_synaptics_verify, 0,
259 &synaptics_tap_enable,
260 0, CTL_HW, root_num, CTL_CREATE,
261 CTL_EOL)) != 0)
262 goto err;
263
264 synaptics_tap_enable_nodenum = node->sysctl_num;
265
266 if ((rc = sysctl_createv(clog, 0, NULL, &node,
267 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
268 CTLTYPE_INT, "tap_length",
269 SYSCTL_DESCR("Tap length"),
270 pms_sysctl_synaptics_verify, 0,
271 &synaptics_tap_length,
272 0, CTL_HW, root_num, CTL_CREATE,
273 CTL_EOL)) != 0)
274 goto err;
275
276 synaptics_tap_length_nodenum = node->sysctl_num;
277
278 if ((rc = sysctl_createv(clog, 0, NULL, &node,
279 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
280 CTLTYPE_INT, "tap_tolerance",
281 SYSCTL_DESCR("Tap tolerance"),
282 pms_sysctl_synaptics_verify, 0,
283 &synaptics_tap_tolerance,
284 0, CTL_HW, root_num, CTL_CREATE,
285 CTL_EOL)) != 0)
286 goto err;
287
288 synaptics_tap_tolerance_nodenum = node->sysctl_num;
289 return;
290
291 err:
292 printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
293 }
294
295 static int
296 pms_sysctl_synaptics_verify(SYSCTLFN_ARGS)
297 {
298 int error, t;
299 struct sysctlnode node;
300
301 node = *rnode;
302 t = *(int *)rnode->sysctl_data;
303 node.sysctl_data = &t;
304 error = sysctl_lookup(SYSCTLFN_CALL(&node));
305 if (error || newp == NULL)
306 return error;
307
308 /* Sanity check the params. */
309 if ((node.sysctl_num == synaptics_slow_limit_nodenum) ||
310 (node.sysctl_num == synaptics_tap_length_nodenum) ||
311 (node.sysctl_num == synaptics_tap_tolerance_nodenum)) {
312 if (t < 0)
313 return EINVAL;
314 } else if ((node.sysctl_num == synaptics_emulate_mid_nodenum) ||
315 (node.sysctl_num == synaptics_tap_enable_nodenum)) {
316 if (t != 0 && t != 1)
317 return EINVAL;
318 } else
319 return EINVAL;
320
321 *(int*)rnode->sysctl_data = t;
322
323 return 0;
324 }
325
326 static int
327 pms_synaptics_send_command(pckbport_tag_t tag, pckbport_slot_t slot,
328 u_char syn_cmd)
329 {
330 u_char cmd[2];
331 int res;
332
333 /* Need to send 4 Set Resolution commands, with the argument
334 * encoded in the bottom most 2 bits.
335 */
336 cmd[0] = PMS_SET_RES; cmd[1] = syn_cmd >> 6;
337 res = pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
338 cmd[0] = PMS_SET_RES; cmd[1] = (syn_cmd & 0x30) >> 4;
339 res |= pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
340 cmd[0] = PMS_SET_RES; cmd[1] = (syn_cmd & 0x0c) >> 2;
341 res |= pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
342 cmd[0] = PMS_SET_RES; cmd[1] = (syn_cmd & 0x03);
343 res |= pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
344
345 return res;
346 }
347
348 static void
349 pms_synaptics_spawn_thread(void *va)
350 {
351 struct pms_softc *sc = (struct pms_softc *)va;
352
353 kthread_create1(pms_synaptics_thread, va, &sc->u.synaptics.syn_thread,
354 "touchpad");
355 }
356
357 static void
358 pms_synaptics_input(void *vsc, int data)
359 {
360 struct pms_softc *sc = vsc;
361 struct synaptics_softc *syn_sc = &sc->u.synaptics;
362 int s;
363
364 sicnt++;
365 if (!sc->sc_enabled) {
366 /* Interrupts are not expected. Discard the byte. */
367 return;
368 }
369
370 s = splclock();
371 sc->current = mono_time;
372 splx(s);
373
374 if (sc->inputstate > 0) {
375 struct timeval diff;
376
377 timersub(&sc->current, &sc->last, &diff);
378 if (diff.tv_sec > 0 || diff.tv_usec >= 40000) {
379 printf("pms_input: unusual delay (%ld.%06ld s), "
380 "scheduling reset\n",
381 (long)diff.tv_sec, (long)diff.tv_usec);
382 sc->inputstate = 0;
383 sc->sc_enabled = 0;
384 wakeup(&sc->sc_enabled);
385 return;
386 }
387 }
388 sc->last = sc->current;
389
390 switch (sc->inputstate) {
391 case 0:
392 if ((data & 0xc8) != 0x80) {
393 printf("pms_input: 0x%02x out of sync\n",data);
394 return; /* not in sync yet, discard input */
395 }
396 /* fallthrough */
397 case 3:
398 if ((data & 8) == 8) {
399 printf("pms_input: dropped in relative mode, reset\n");
400 sc->inputstate = 0;
401 sc->sc_enabled = 0;
402 wakeup(&sc->sc_enabled);
403 return;
404 }
405 }
406
407 if (sc->inputstate == 6) { /* missed it last time, buffer was full */
408 if (!syn_sc->buf_full) {
409 memcpy(syn_sc->buf, sc->packet, sizeof(syn_sc->buf));
410 syn_sc->buf_full = 1;
411 sc->inputstate = 0;
412 memset(sc->packet, 0, 6);
413 wakeup(&syn_sc->buf_full);
414 }
415 else { /* bad luck, drop the packet */
416 sc->inputstate = 0;
417 }
418 }
419
420 sc->packet[sc->inputstate++] = data & 0xff;
421 if (sc->inputstate == 6) {
422 if (!syn_sc->buf_full) {
423 memcpy(syn_sc->buf, sc->packet, sizeof(syn_sc->buf));
424 syn_sc->buf_full = 1;
425 sc->inputstate = 0;
426 memset(sc->packet, 0, 6);
427 wakeup(&syn_sc->buf_full);
428 }
429 }
430
431 /* if (sicnt % 500 == 0) printf("syninput: invocations %ld, tppack %ld, ptpack %ld\n",sicnt,tppack,ptpack); */
432 }
433
434 /* Masks for the first byte of a packet */
435 #define PMS_LBUTMASK 0x01
436 #define PMS_RBUTMASK 0x02
437 #define PMS_MBUTMASK 0x04
438
439 static void
440 pms_synaptics_thread(void *va)
441 {
442 struct pms_softc *sc = (struct pms_softc *) va;
443 struct synaptics_softc *syn_sc = &sc->u.synaptics;
444 u_int changed;
445 int dx, dy, dz = 0;
446 int left, mid, right, up, down;
447 int newbuttons = 0;
448 int s;
449 int tap = 0, tapdrag = 0;
450 struct timeval tap_start = { -1, 0 };
451 int tap_x = -1, tap_y = -1, moving = 0;
452
453 for (;;) {
454 int ret;
455 tap = 0;
456 changed = 0;
457
458 if (tapdrag == 1) {
459 struct timeval tap_len;
460 do {
461 ret = tsleep(&syn_sc->buf_full, PWAIT, "tap",
462 synaptics_tap_length*hz/1000000);
463 /* XXX: should measure the time */
464 } while (ret != EWOULDBLOCK && !syn_sc->buf_full);
465
466 timersub(&sc->current, &tap_start, &tap_len);
467 if (tap_len.tv_usec >= synaptics_tap_length) {
468 changed = 1;
469 tapdrag = 0;
470 }
471 } else {
472 while (!syn_sc->buf_full)
473 tsleep(&syn_sc->buf_full, PWAIT, "wait", 0);
474 }
475
476
477 if ((syn_sc->has_passthrough) &&
478 ((syn_sc->buf[0] & 0xfc) == 0x84) &&
479 ((syn_sc->buf[3] & 0xcc) == 0xc4))
480 {
481 /* pass through port */
482 ptpack++;
483 if (!syn_sc->enable_passthrough)
484 goto done;
485
486 /* FIXME: should do something here instead if we don't
487 * want just to drop the packets */
488
489 goto done;
490 } else { /* touchpad absolute W mode */
491 int x = syn_sc->buf[4] + ((syn_sc->buf[1] & 0x0f)<<8) +
492 ((syn_sc->buf[3] & 0x10)<<8);
493 int y = syn_sc->buf[5] + ((syn_sc->buf[1] & 0xf0)<<4) +
494 ((syn_sc->buf[3] & 0x20)<<7);
495 /* int w = ((syn_sc->buf[3] & 4)>>2) +
496 ((syn_sc->buf[0] & 4)>>1) +
497 ((syn_sc->buf[0] & 0x30)>>2); */
498 int z = syn_sc->buf[2];
499
500 /* ???: seems to return somwhat random numbers :-(
501 if (sc->palm_detect && w > 10) {
502 z = 0;
503 printf("palm detect %d\n",w);
504 }
505 */
506
507 tppack++;
508
509 /* Basic button handling. */
510 left = (syn_sc->buf[0] & PMS_LBUTMASK);
511 right = (syn_sc->buf[0] & PMS_RBUTMASK);
512
513 /* Middle button. */
514 if (syn_sc->has_m_button) {
515 /* Old style Middle Button. */
516 mid = ((syn_sc->buf[0] & PMS_LBUTMASK) ^
517 (syn_sc->buf[3] & PMS_LBUTMASK));
518 } else {
519 mid = 0;
520 }
521
522 /* Up/Down button. */
523 if (syn_sc->has_buttons_4_5) {
524 /* Old up/down button. */
525 up = left ^ (syn_sc->buf[3] & PMS_LBUTMASK);
526 down = right ^ (syn_sc->buf[3] & PMS_RBUTMASK);
527 } else if ((syn_sc->has_buttons_4_5_ext) &&
528 ((syn_sc->buf[0] & PMS_RBUTMASK) ^
529 (syn_sc->buf[3] & PMS_RBUTMASK))) {
530 /* New up/down button. */
531 up = (syn_sc->buf[4] & SYN_1BUTMASK);
532 down = (syn_sc->buf[5] & SYN_2BUTMASK);
533 } else {
534 up = 0;
535 down = 0;
536 }
537
538 /* Emulate middle button. */
539 if (synaptics_emulate_mid) {
540 mid = ((up | down) ? 0x2 : 0);
541 up = down = 0;
542 }
543
544 newbuttons = ((left) ? 0x1 : 0) |
545 ((mid) ? 0x2 : 0) |
546 ((right) ? 0x4 : 0) |
547 ((up) ? 0x8 : 0) |
548 ((down) ? 0x10 : 0);
549
550 changed |= (sc->buttons ^ newbuttons);
551 sc->buttons = newbuttons;
552
553 if (moving) {
554 struct timeval tap_len;
555
556 if (z < SYNAPTICS_NOTOUCH) {
557 moving = 0;
558
559 timersub(&sc->current, &tap_start, &tap_len);
560 dx = x-tap_x; dx *= dx;
561 dy = y-tap_y; dy *= dy;
562
563 if ((synaptics_tap_enable) &&
564 (tap_len.tv_sec == 0) &&
565 (tap_len.tv_usec < synaptics_tap_length) &&
566 (dx+dy < synaptics_tap_tolerance * synaptics_tap_tolerance)) {
567 tap = 1;
568 }
569 /* printf("tap: %d %d %ld -> %d\n",dx,dy,tap_len.tv_usec,tap); */
570 tap_start.tv_sec = -1;
571 tap_x = tap_y = -1;
572
573 dx = dy = 0;
574 if (tapdrag == 2) {
575 tapdrag = 0;
576 changed |= 1;
577 }
578 }
579 else {
580 dx = x - syn_sc->last_x;
581 dy = y - syn_sc->last_y;
582 syn_sc->last_x = x;
583 syn_sc->last_y = y;
584 }
585 }
586 else {
587 if (z >= SYNAPTICS_NOTOUCH) {
588 moving = 1;
589 if (tapdrag == 1) tapdrag = 2;
590 else {
591 tap_x = syn_sc->last_x = x;
592 tap_y = syn_sc->last_y = y;
593 tap_start = sc->current;
594 }
595 }
596 dx = dy = 0;
597 }
598 dz = 0;
599
600 }
601 s = dx*dx + dy*dy;
602 if (s < synaptics_slow_limit) {
603 s = s/4 + synaptics_slow_limit*3/4;
604 dx = dx * s/synaptics_slow_limit;
605 dy = dy * s/synaptics_slow_limit;
606 }
607
608 syn_sc->last_x -= dx % 8;
609 syn_sc->last_y -= dy % 8;
610 dx /= 8;
611 dy /= 8;
612
613 if (sc->buttons & 1) tap = 0;
614
615 if (dx || dy || dz || changed || tap) {
616 if (tap) {
617 wsmouse_input(sc->sc_wsmousedev,
618 sc->buttons | 1, 0,0,0,
619 WSMOUSE_INPUT_DELTA);
620
621 /* printf("wsmouse_input: %d\n", sc->buttons | 1); */
622 tapdrag = 1;
623 tap_start = sc->current;
624 }
625 else wsmouse_input(sc->sc_wsmousedev,
626 sc->buttons | (tapdrag == 2 ? 1 : 0),
627 dx, dy, dz,
628 WSMOUSE_INPUT_DELTA);
629 /* printf("wsmouse_input: %d\n", sc->buttons); */
630 }
631
632 done:
633 syn_sc->buf_full = 0;
634 memset(syn_sc->buf, 0, 6);
635 }
636 }
637