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