synaptics.c revision 1.2 1 /* $NetBSD: synaptics.c,v 1.2 2004/12/28 20:47:18 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 void pms_sysctl_synaptics(struct sysctllog **);
65 static int pms_sysctl_synaptics_verify(SYSCTLFN_ARGS);
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 struct sysctllog *clog = NULL;
90
91 res = pms_synaptics_send_command(sc->sc_kbctag, sc->sc_kbcslot,
92 SYNAPTICS_IDENTIFY_TOUCHPAD);
93 cmd[0] = PMS_SEND_DEV_STATUS;
94 res |= pckbport_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
95 1, 3, resp, 0);
96 if (res) {
97 #ifdef SYNAPTICSDEBUG
98 printf("synaptics_probe: Identify Touchpad error.\n");
99 #endif
100 return res;
101 }
102
103 if (resp[1] != SYNAPTICS_MAGIC_BYTE) {
104 #ifdef SYNAPTICSDEBUG
105 printf("synaptics_probe: Not synaptics.\n");
106 #endif
107 return -1;
108 }
109
110 /* Check for minimum version and print a nice message. */
111 ver_major = resp[2] & 0x0f;
112 ver_minor = resp[0];
113 printf("%s: Synaptics touchpad version %d.%d\n",
114 sc->sc_dev.dv_xname, ver_major, ver_minor);
115 if (ver_major * 10 + ver_minor < SYNAPTICS_MIN_VERSION) {
116 /* No capability query support. */
117 syn_sc->caps = 0;
118 syn_sc->enable_passthrough = 0;
119 syn_sc->has_passthrough = 0;
120 syn_sc->has_m_button = 0;
121 syn_sc->has_buttons_4_5 = 0;
122 syn_sc->has_buttons_4_5_ext = 0;
123 goto done;
124 }
125
126 /* Query the hardware capabilities. */
127 res = pms_synaptics_send_command(sc->sc_kbctag, sc->sc_kbcslot,
128 SYNAPTICS_READ_CAPABILITIES);
129 cmd[0] = PMS_SEND_DEV_STATUS;
130 res |= pckbport_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
131 1, 3, resp, 0);
132 if (res) {
133 /* Hmm, failed to get capabilites. */
134 printf("%s: Failed to query capabilities.\n",
135 sc->sc_dev.dv_xname);
136 return -1;
137 }
138
139 syn_sc->caps = (resp[0] << 8) | (resp[3]);
140 syn_sc->has_m_button = syn_sc->caps & SYNAPTICS_CAP_MBUTTON;
141 syn_sc->has_buttons_4_5 = syn_sc->caps & SYNAPTICS_CAP_4BUTTON;
142 if (!(syn_sc->caps & SYNAPTICS_CAP_EXTENDED)) {
143 /* Eeep. No extended capabilities. */
144 syn_sc->has_passthrough = 0;
145 syn_sc->enable_passthrough = 0;
146 } else {
147 #ifdef SYNAPTICSDEBUG
148 printf("synaptics_probe: Capabilities %2x.\n", syn_sc->caps);
149 #endif
150 syn_sc->has_passthrough = syn_sc->caps &
151 SYNAPTICS_CAP_PASSTHROUGH;
152 syn_sc->enable_passthrough = 0; /* Not yet. */
153
154 /* Ask about extra buttons to detect up/down. */
155 if (syn_sc->caps & SYNAPTICS_CAP_EXTNUM) {
156 res = pms_synaptics_send_command(sc->sc_kbctag,
157 sc->sc_kbcslot, SYNAPTICS_EXTENDED_QUERY);
158 cmd[0] = PMS_SEND_DEV_STATUS;
159 res |= pckbport_poll_cmd(sc->sc_kbctag,
160 sc->sc_kbcslot, cmd, 1, 3, resp, 0);
161 if ((!res) && ((resp[1] >> 4) >= 2)) {
162 /* Yes. */
163 syn_sc->has_buttons_4_5_ext = 1;
164 } else {
165 /* Guess not. */
166 syn_sc->has_buttons_4_5_ext = 0;
167 }
168 } else
169 syn_sc->has_buttons_4_5_ext = 0;
170 }
171
172 done:
173 pms_sysctl_synaptics(&clog);
174 syn_sc->buf_full = 0;
175 kthread_create(pms_synaptics_spawn_thread, sc);
176 pckbport_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot,
177 pms_synaptics_input, sc, sc->sc_dev.dv_xname);
178 return 0;
179 }
180
181 void
182 pms_synaptics_enable(void *vsc)
183 {
184 struct pms_softc *sc = vsc;
185 struct synaptics_softc *syn_sc = &sc->u.synaptics;
186 u_char cmd[2];
187 int res;
188 u_char mode = SYNAPTICS_MODE_ABSOLUTE | SYNAPTICS_MODE_W;
189
190 res = pms_synaptics_send_command(sc->sc_kbctag, sc->sc_kbcslot, mode);
191 cmd[0] = PMS_SET_SAMPLE;
192 cmd[1] = 0x14; /* doit */
193 res |= pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot,
194 cmd, 2, 0, 1, NULL);
195 syn_sc->last_x = syn_sc->last_y = -1;
196 if (res)
197 printf("synaptics_enable: Error enabling device.\n");
198 }
199
200 void
201 pms_synaptics_resume(void *vsc)
202 {
203 struct pms_softc *sc = vsc;
204 unsigned char cmd[1],resp[2] = { 0,0 };
205 int res;
206
207 cmd[0] = PMS_RESET;
208 res = pckbport_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
209 1, 2, resp, 1);
210 printf("%s: reset on resume %d 0x%02x 0x%02x\n",
211 sc->sc_dev.dv_xname,res,resp[0],resp[1]);
212 }
213
214 static
215 void pms_sysctl_synaptics(struct sysctllog **clog)
216 {
217 int rc, root_num;
218 struct sysctlnode *node;
219
220 if ((rc = sysctl_createv(clog, 0, NULL, NULL,
221 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
222 NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0)
223 goto err;
224
225 if ((rc = sysctl_createv(clog, 0, NULL, &node,
226 CTLFLAG_PERMANENT, CTLTYPE_NODE, "synaptics",
227 SYSCTL_DESCR("Synaptics touchpad controls"),
228 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0)
229 goto err;
230
231 root_num = node->sysctl_num;
232
233 if ((rc = sysctl_createv(clog, 0, NULL, &node,
234 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
235 CTLTYPE_INT, "emulate_mid",
236 SYSCTL_DESCR("Emulate middle button with wheel"),
237 pms_sysctl_synaptics_verify, 0,
238 &synaptics_emulate_mid,
239 0, CTL_HW, root_num, CTL_CREATE,
240 CTL_EOL)) != 0)
241 goto err;
242
243 synaptics_emulate_mid_nodenum = node->sysctl_num;
244
245 if ((rc = sysctl_createv(clog, 0, NULL, &node,
246 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
247 CTLTYPE_INT, "slow_limit",
248 SYSCTL_DESCR("Slow limit"),
249 pms_sysctl_synaptics_verify, 0,
250 &synaptics_slow_limit,
251 0, CTL_HW, root_num, CTL_CREATE,
252 CTL_EOL)) != 0)
253 goto err;
254
255 synaptics_slow_limit_nodenum = node->sysctl_num;
256
257 if ((rc = sysctl_createv(clog, 0, NULL, &node,
258 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
259 CTLTYPE_INT, "tap_enable",
260 SYSCTL_DESCR("Process taps as clicks"),
261 pms_sysctl_synaptics_verify, 0,
262 &synaptics_tap_enable,
263 0, CTL_HW, root_num, CTL_CREATE,
264 CTL_EOL)) != 0)
265 goto err;
266
267 synaptics_tap_enable_nodenum = node->sysctl_num;
268
269 if ((rc = sysctl_createv(clog, 0, NULL, &node,
270 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
271 CTLTYPE_INT, "tap_length",
272 SYSCTL_DESCR("Tap length"),
273 pms_sysctl_synaptics_verify, 0,
274 &synaptics_tap_length,
275 0, CTL_HW, root_num, CTL_CREATE,
276 CTL_EOL)) != 0)
277 goto err;
278
279 synaptics_tap_length_nodenum = node->sysctl_num;
280
281 if ((rc = sysctl_createv(clog, 0, NULL, &node,
282 CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
283 CTLTYPE_INT, "tap_tolerance",
284 SYSCTL_DESCR("Tap tolerance"),
285 pms_sysctl_synaptics_verify, 0,
286 &synaptics_tap_tolerance,
287 0, CTL_HW, root_num, CTL_CREATE,
288 CTL_EOL)) != 0)
289 goto err;
290
291 synaptics_tap_tolerance_nodenum = node->sysctl_num;
292 return;
293
294 err:
295 printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
296 }
297
298 static int
299 pms_sysctl_synaptics_verify(SYSCTLFN_ARGS)
300 {
301 int error, t;
302 struct sysctlnode node;
303
304 node = *rnode;
305 t = *(int *)rnode->sysctl_data;
306 node.sysctl_data = &t;
307 error = sysctl_lookup(SYSCTLFN_CALL(&node));
308 if (error || newp == NULL)
309 return error;
310
311 /* Sanity check the params. */
312 if ((node.sysctl_num == synaptics_slow_limit_nodenum) ||
313 (node.sysctl_num == synaptics_tap_length_nodenum) ||
314 (node.sysctl_num == synaptics_tap_tolerance_nodenum)) {
315 if (t < 0)
316 return EINVAL;
317 } else if ((node.sysctl_num == synaptics_emulate_mid_nodenum) ||
318 (node.sysctl_num == synaptics_tap_enable_nodenum)) {
319 if (t != 0 && t != 1)
320 return EINVAL;
321 } else
322 return EINVAL;
323
324 *(int*)rnode->sysctl_data = t;
325
326 return 0;
327 }
328
329 static int
330 pms_synaptics_send_command(pckbport_tag_t tag, pckbport_slot_t slot,
331 u_char syn_cmd)
332 {
333 u_char cmd[2];
334 int res;
335
336 /* Need to send 4 Set Resolution commands, with the argument
337 * encoded in the bottom most 2 bits.
338 */
339 cmd[0] = PMS_SET_RES; cmd[1] = syn_cmd >> 6;
340 res = pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
341 cmd[0] = PMS_SET_RES; cmd[1] = (syn_cmd & 0x30) >> 4;
342 res |= pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
343 cmd[0] = PMS_SET_RES; cmd[1] = (syn_cmd & 0x0c) >> 2;
344 res |= pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
345 cmd[0] = PMS_SET_RES; cmd[1] = (syn_cmd & 0x03);
346 res |= pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
347
348 return res;
349 }
350
351 static void
352 pms_synaptics_spawn_thread(void *va)
353 {
354 struct pms_softc *sc = (struct pms_softc *)va;
355
356 kthread_create1(pms_synaptics_thread, va, &sc->u.synaptics.syn_thread,
357 "touchpad");
358 }
359
360 static void
361 pms_synaptics_input(void *vsc, int data)
362 {
363 struct pms_softc *sc = vsc;
364 struct synaptics_softc *syn_sc = &sc->u.synaptics;
365 int s;
366
367 if (!sc->sc_enabled) {
368 /* Interrupts are not expected. Discard the byte. */
369 return;
370 }
371
372 s = splclock();
373 sc->current = mono_time;
374 splx(s);
375
376 if (sc->inputstate > 0) {
377 struct timeval diff;
378
379 timersub(&sc->current, &sc->last, &diff);
380 if (diff.tv_sec > 0 || diff.tv_usec >= 40000) {
381 printf("pms_input: unusual delay (%ld.%06ld s), "
382 "scheduling reset\n",
383 (long)diff.tv_sec, (long)diff.tv_usec);
384 sc->inputstate = 0;
385 sc->sc_enabled = 0;
386 wakeup(&sc->sc_enabled);
387 return;
388 }
389 }
390 sc->last = sc->current;
391
392 switch (sc->inputstate) {
393 case 0:
394 if ((data & 0xc8) != 0x80) {
395 #ifdef SYNAPTICSDEBUG
396 printf("pms_input: 0x%02x out of sync\n",data);
397 #endif
398 return; /* not in sync yet, discard input */
399 }
400 /* fallthrough */
401 case 3:
402 if ((data & 8) == 8) {
403 #ifdef SYNAPTICS_DEBUG
404 printf("pms_input: dropped in relative mode, reset\n");
405 #endif
406 sc->inputstate = 0;
407 sc->sc_enabled = 0;
408 wakeup(&sc->sc_enabled);
409 return;
410 }
411 }
412
413 if (sc->inputstate == 6) { /* missed it last time, buffer was full */
414 if (!syn_sc->buf_full) {
415 memcpy(syn_sc->buf, sc->packet, sizeof(syn_sc->buf));
416 syn_sc->buf_full = 1;
417 sc->inputstate = 0;
418 memset(sc->packet, 0, 6);
419 wakeup(&syn_sc->buf_full);
420 }
421 else { /* bad luck, drop the packet */
422 sc->inputstate = 0;
423 }
424 }
425
426 sc->packet[sc->inputstate++] = data & 0xff;
427 if (sc->inputstate == 6) {
428 if (!syn_sc->buf_full) {
429 memcpy(syn_sc->buf, sc->packet, sizeof(syn_sc->buf));
430 syn_sc->buf_full = 1;
431 sc->inputstate = 0;
432 memset(sc->packet, 0, 6);
433 wakeup(&syn_sc->buf_full);
434 }
435 }
436 }
437
438 /* Masks for the first byte of a packet */
439 #define PMS_LBUTMASK 0x01
440 #define PMS_RBUTMASK 0x02
441 #define PMS_MBUTMASK 0x04
442
443 static void
444 pms_synaptics_thread(void *va)
445 {
446 struct pms_softc *sc = (struct pms_softc *) va;
447 struct synaptics_softc *syn_sc = &sc->u.synaptics;
448 u_int changed;
449 int dx, dy, dz = 0;
450 int left, mid, right, up, down;
451 int newbuttons = 0;
452 int s;
453 int tap = 0, tapdrag = 0;
454 struct timeval tap_start = { -1, 0 };
455 int tap_x = -1, tap_y = -1, moving = 0;
456
457 for (;;) {
458 int ret;
459 tap = 0;
460 changed = 0;
461
462 if (tapdrag == 1) {
463 struct timeval tap_len;
464 do {
465 ret = tsleep(&syn_sc->buf_full, PWAIT, "tap",
466 synaptics_tap_length * hz / 1000000);
467 /* XXX: should measure the time */
468 } while (ret != EWOULDBLOCK && !syn_sc->buf_full);
469
470 timersub(&sc->current, &tap_start, &tap_len);
471 if (tap_len.tv_usec >= synaptics_tap_length) {
472 changed = 1;
473 tapdrag = 0;
474 }
475 } else {
476 while (!syn_sc->buf_full)
477 tsleep(&syn_sc->buf_full, PWAIT, "wait", 0);
478 }
479
480
481 if ((syn_sc->has_passthrough) &&
482 ((syn_sc->buf[0] & 0xfc) == 0x84) &&
483 ((syn_sc->buf[3] & 0xcc) == 0xc4))
484 {
485 /* pass through port */
486 if (!syn_sc->enable_passthrough)
487 goto done;
488
489 /* FIXME: should do something here instead if we don't
490 * want just to drop the packets */
491
492 goto done;
493 } else { /* touchpad absolute W mode */
494 int x = syn_sc->buf[4] + ((syn_sc->buf[1] & 0x0f)<<8) +
495 ((syn_sc->buf[3] & 0x10)<<8);
496 int y = syn_sc->buf[5] + ((syn_sc->buf[1] & 0xf0)<<4) +
497 ((syn_sc->buf[3] & 0x20)<<7);
498 /* int w = ((syn_sc->buf[3] & 4)>>2) +
499 ((syn_sc->buf[0] & 4)>>1) +
500 ((syn_sc->buf[0] & 0x30)>>2); */
501 int z = syn_sc->buf[2];
502
503 #ifdef notdef
504 if (sc->palm_detect && w > 10) {
505 z = 0;
506 printf("palm detect %d\n",w);
507 }
508 #endif
509
510 /* Basic button handling. */
511 left = (syn_sc->buf[0] & PMS_LBUTMASK);
512 right = (syn_sc->buf[0] & PMS_RBUTMASK);
513
514 /* Middle button. */
515 if (syn_sc->has_m_button) {
516 /* Old style Middle Button. */
517 mid = ((syn_sc->buf[0] & PMS_LBUTMASK) ^
518 (syn_sc->buf[3] & PMS_LBUTMASK));
519 } else {
520 mid = 0;
521 }
522
523 /* Up/Down button. */
524 if (syn_sc->has_buttons_4_5) {
525 /* Old up/down button. */
526 up = left ^ (syn_sc->buf[3] & PMS_LBUTMASK);
527 down = right ^ (syn_sc->buf[3] & PMS_RBUTMASK);
528 } else if ((syn_sc->has_buttons_4_5_ext) &&
529 ((syn_sc->buf[0] & PMS_RBUTMASK) ^
530 (syn_sc->buf[3] & PMS_RBUTMASK))) {
531 /* New up/down button. */
532 up = (syn_sc->buf[4] & SYN_1BUTMASK);
533 down = (syn_sc->buf[5] & SYN_2BUTMASK);
534 } else {
535 up = 0;
536 down = 0;
537 }
538
539 /* Emulate middle button. */
540 if (synaptics_emulate_mid) {
541 mid = ((up | down) ? 0x2 : 0);
542 up = down = 0;
543 }
544
545 newbuttons = ((left) ? 0x1 : 0) |
546 ((mid) ? 0x2 : 0) |
547 ((right) ? 0x4 : 0) |
548 ((up) ? 0x8 : 0) |
549 ((down) ? 0x10 : 0);
550
551 changed |= (sc->buttons ^ newbuttons);
552 sc->buttons = newbuttons;
553
554 if (moving) {
555 struct timeval tap_len;
556
557 if (z < SYNAPTICS_NOTOUCH) {
558 moving = 0;
559
560 timersub(&sc->current, &tap_start,
561 &tap_len);
562 dx = x-tap_x; dx *= dx;
563 dy = y-tap_y; dy *= dy;
564
565 if ((synaptics_tap_enable) &&
566 (tap_len.tv_sec == 0) &&
567 (tap_len.tv_usec <
568 synaptics_tap_length) &&
569 (dx+dy <
570 synaptics_tap_tolerance *
571 synaptics_tap_tolerance)) {
572 tap = 1;
573 }
574 tap_start.tv_sec = -1;
575 tap_x = tap_y = -1;
576
577 dx = dy = 0;
578 if (tapdrag == 2) {
579 tapdrag = 0;
580 changed |= 1;
581 }
582 }
583 else {
584 dx = x - syn_sc->last_x;
585 dy = y - syn_sc->last_y;
586 syn_sc->last_x = x;
587 syn_sc->last_y = y;
588 }
589 }
590 else {
591 if (z >= SYNAPTICS_NOTOUCH) {
592 moving = 1;
593 if (tapdrag == 1) tapdrag = 2;
594 else {
595 tap_x = syn_sc->last_x = x;
596 tap_y = syn_sc->last_y = y;
597 tap_start = sc->current;
598 }
599 }
600 dx = dy = 0;
601 }
602 dz = 0;
603
604 }
605 s = dx*dx + dy*dy;
606 if (s < synaptics_slow_limit) {
607 s = s/4 + synaptics_slow_limit*3/4;
608 dx = dx * s/synaptics_slow_limit;
609 dy = dy * s/synaptics_slow_limit;
610 }
611
612 syn_sc->last_x -= dx % 8;
613 syn_sc->last_y -= dy % 8;
614 dx /= 8;
615 dy /= 8;
616
617 if (sc->buttons & 1) tap = 0;
618
619 if (dx || dy || dz || changed || tap) {
620 if (tap) {
621 wsmouse_input(sc->sc_wsmousedev,
622 sc->buttons | 1, 0,0,0,
623 WSMOUSE_INPUT_DELTA);
624
625 tapdrag = 1;
626 tap_start = sc->current;
627 }
628 else wsmouse_input(sc->sc_wsmousedev,
629 sc->buttons | (tapdrag == 2 ? 1 : 0),
630 dx, dy, dz,
631 WSMOUSE_INPUT_DELTA);
632 }
633
634 done:
635 syn_sc->buf_full = 0;
636 memset(syn_sc->buf, 0, 6);
637 }
638 }
639