Line data Source code
1 : /* $OpenBSD: pms.c,v 1.87 2018/05/13 14:48:19 bru Exp $ */
2 : /* $NetBSD: psm.c,v 1.11 2000/06/05 22:20:57 sommerfeld Exp $ */
3 :
4 : /*-
5 : * Copyright (c) 1994 Charles M. Hannum.
6 : * Copyright (c) 1992, 1993 Erik Forsberg.
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 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : *
15 : * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 : * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 : * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
18 : * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
22 : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 : */
26 :
27 : #include <sys/param.h>
28 : #include <sys/systm.h>
29 : #include <sys/rwlock.h>
30 : #include <sys/device.h>
31 : #include <sys/ioctl.h>
32 : #include <sys/malloc.h>
33 : #include <sys/task.h>
34 : #include <sys/timeout.h>
35 :
36 : #include <machine/bus.h>
37 :
38 : #include <dev/ic/pckbcvar.h>
39 :
40 : #include <dev/pckbc/pmsreg.h>
41 :
42 : #include <dev/wscons/wsconsio.h>
43 : #include <dev/wscons/wsmousevar.h>
44 :
45 : #if defined(__i386__) || defined(__amd64__)
46 : #include "acpi.h"
47 : #endif
48 :
49 : #if !defined(SMALL_KERNEL) && NACPI > 0
50 : extern int mouse_has_softbtn;
51 : #else
52 : int mouse_has_softbtn;
53 : #endif
54 :
55 : #ifdef DEBUG
56 : #define DPRINTF(x...) do { printf(x); } while (0);
57 : #else
58 : #define DPRINTF(x...)
59 : #endif
60 :
61 : #define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
62 :
63 : #define WSMOUSE_BUTTON(x) (1 << ((x) - 1))
64 :
65 : struct pms_softc;
66 :
67 : struct pms_protocol {
68 : int type;
69 : #define PMS_STANDARD 0
70 : #define PMS_INTELLI 1
71 : #define PMS_SYNAPTICS 2
72 : #define PMS_ALPS 3
73 : #define PMS_ELANTECH_V1 4
74 : #define PMS_ELANTECH_V2 5
75 : #define PMS_ELANTECH_V3 6
76 : #define PMS_ELANTECH_V4 7
77 : u_int packetsize;
78 : int (*enable)(struct pms_softc *);
79 : int (*ioctl)(struct pms_softc *, u_long, caddr_t, int, struct proc *);
80 : int (*sync)(struct pms_softc *, int);
81 : void (*proc)(struct pms_softc *);
82 : void (*disable)(struct pms_softc *);
83 : };
84 :
85 : struct synaptics_softc {
86 : int identify;
87 : int capabilities, ext_capabilities, ext2_capabilities;
88 : int model, ext_model;
89 : int modes;
90 :
91 : int mode;
92 :
93 : int mask;
94 : #define SYNAPTICS_MASK_NEWABS_STRICT 0xc8
95 : #define SYNAPTICS_MASK_NEWABS_RELAXED 0xc0
96 : #define SYNAPTICS_VALID_NEWABS_FIRST 0x80
97 : #define SYNAPTICS_VALID_NEWABS_NEXT 0xc0
98 :
99 : u_int sec_buttons;
100 :
101 : #define SYNAPTICS_PRESSURE_HI 30
102 : #define SYNAPTICS_PRESSURE_LO 25
103 : #define SYNAPTICS_PRESSURE SYNAPTICS_PRESSURE_HI
104 : #define SYNAPTICS_SCALE 4
105 : #define SYNAPTICS_MAX_FINGERS 3
106 : };
107 :
108 : struct alps_softc {
109 : int model;
110 : #define ALPS_GLIDEPOINT (1 << 1)
111 : #define ALPS_DUALPOINT (1 << 2)
112 : #define ALPS_PASSTHROUGH (1 << 3)
113 : #define ALPS_INTERLEAVED (1 << 4)
114 :
115 : int mask;
116 : int version;
117 :
118 : u_int gesture;
119 :
120 : u_int sec_buttons; /* trackpoint */
121 :
122 : int old_x, old_y;
123 : #define ALPS_PRESSURE 40
124 : };
125 :
126 : struct elantech_softc {
127 : int flags;
128 : #define ELANTECH_F_REPORTS_PRESSURE 0x01
129 : #define ELANTECH_F_HAS_ROCKER 0x02
130 : #define ELANTECH_F_2FINGER_PACKET 0x04
131 : #define ELANTECH_F_HW_V1_OLD 0x08
132 : #define ELANTECH_F_CRC_ENABLED 0x10
133 : #define ELANTECH_F_TRACKPOINT 0x20
134 : int fw_version;
135 :
136 : u_int mt_slots;
137 :
138 : int width;
139 :
140 : u_char parity[256];
141 : u_char p1, p2, p3;
142 :
143 : int max_x, max_y;
144 : int old_x, old_y;
145 : };
146 :
147 : struct pms_softc { /* driver status information */
148 : struct device sc_dev;
149 :
150 : pckbc_tag_t sc_kbctag;
151 :
152 : int sc_state;
153 : #define PMS_STATE_DISABLED 0
154 : #define PMS_STATE_ENABLED 1
155 : #define PMS_STATE_SUSPENDED 2
156 :
157 : struct rwlock sc_state_lock;
158 :
159 : int sc_dev_enable;
160 : #define PMS_DEV_IGNORE 0x00
161 : #define PMS_DEV_PRIMARY 0x01
162 : #define PMS_DEV_SECONDARY 0x02
163 :
164 : struct task sc_rsttask;
165 : struct timeout sc_rsttimo;
166 : int sc_rststate;
167 : #define PMS_RST_COMMENCE 0x01
168 : #define PMS_RST_ANNOUNCED 0x02
169 :
170 : int poll;
171 : int inputstate;
172 :
173 : const struct pms_protocol *protocol;
174 : struct synaptics_softc *synaptics;
175 : struct alps_softc *alps;
176 : struct elantech_softc *elantech;
177 :
178 : u_char packet[8];
179 :
180 : struct device *sc_wsmousedev;
181 : struct device *sc_sec_wsmousedev;
182 : };
183 :
184 : static const u_int butmap[8] = {
185 : 0,
186 : WSMOUSE_BUTTON(1),
187 : WSMOUSE_BUTTON(3),
188 : WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(3),
189 : WSMOUSE_BUTTON(2),
190 : WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2),
191 : WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3),
192 : WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3)
193 : };
194 :
195 : static const struct alps_model {
196 : int version;
197 : int mask;
198 : int model;
199 : } alps_models[] = {
200 : { 0x2021, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
201 : { 0x2221, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
202 : { 0x2222, 0xff, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
203 : { 0x3222, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
204 : { 0x5212, 0xff, ALPS_DUALPOINT | ALPS_PASSTHROUGH | ALPS_INTERLEAVED },
205 : { 0x5321, 0xf8, ALPS_GLIDEPOINT },
206 : { 0x5322, 0xf8, ALPS_GLIDEPOINT },
207 : { 0x603b, 0xf8, ALPS_GLIDEPOINT },
208 : { 0x6222, 0xcf, ALPS_DUALPOINT | ALPS_PASSTHROUGH | ALPS_INTERLEAVED },
209 : { 0x6321, 0xf8, ALPS_GLIDEPOINT },
210 : { 0x6322, 0xf8, ALPS_GLIDEPOINT },
211 : { 0x6323, 0xf8, ALPS_GLIDEPOINT },
212 : { 0x6324, 0x8f, ALPS_GLIDEPOINT },
213 : { 0x6325, 0xef, ALPS_GLIDEPOINT },
214 : { 0x6326, 0xf8, ALPS_GLIDEPOINT },
215 : { 0x7301, 0xf8, ALPS_DUALPOINT },
216 : { 0x7321, 0xf8, ALPS_GLIDEPOINT },
217 : { 0x7322, 0xf8, ALPS_GLIDEPOINT },
218 : { 0x7325, 0xcf, ALPS_GLIDEPOINT },
219 : #if 0
220 : /*
221 : * This model has a clitpad sending almost compatible PS2
222 : * packets but not compatible enough to be used with the
223 : * ALPS protocol.
224 : */
225 : { 0x633b, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
226 :
227 : { 0x7326, 0, 0 }, /* XXX Uses unknown v3 protocol */
228 :
229 : { 0x7331, 0x8f, ALPS_DUALPOINT }, /* not supported */
230 : #endif
231 : };
232 :
233 : static struct wsmouse_param synaptics_params[] = {
234 : { WSMOUSECFG_PRESSURE_LO, SYNAPTICS_PRESSURE_LO },
235 : { WSMOUSECFG_PRESSURE_HI, SYNAPTICS_PRESSURE_HI }
236 : };
237 :
238 : static struct wsmouse_param alps_params[] = {
239 : { WSMOUSECFG_SMOOTHING, 3 }
240 : };
241 :
242 : int pmsprobe(struct device *, void *, void *);
243 : void pmsattach(struct device *, struct device *, void *);
244 : int pmsactivate(struct device *, int);
245 :
246 : void pmsinput(void *, int);
247 :
248 : int pms_change_state(struct pms_softc *, int, int);
249 :
250 : int pms_ioctl(void *, u_long, caddr_t, int, struct proc *);
251 : int pms_enable(void *);
252 : void pms_disable(void *);
253 :
254 : int pms_sec_ioctl(void *, u_long, caddr_t, int, struct proc *);
255 : int pms_sec_enable(void *);
256 : void pms_sec_disable(void *);
257 :
258 : int pms_cmd(struct pms_softc *, u_char *, int, u_char *, int);
259 : int pms_spec_cmd(struct pms_softc *, int);
260 : int pms_get_devid(struct pms_softc *, u_char *);
261 : int pms_get_status(struct pms_softc *, u_char *);
262 : int pms_set_rate(struct pms_softc *, int);
263 : int pms_set_resolution(struct pms_softc *, int);
264 : int pms_set_scaling(struct pms_softc *, int);
265 : int pms_reset(struct pms_softc *);
266 : int pms_dev_enable(struct pms_softc *);
267 : int pms_dev_disable(struct pms_softc *);
268 : void pms_protocol_lookup(struct pms_softc *);
269 : void pms_reset_detect(struct pms_softc *, int);
270 : void pms_reset_task(void *);
271 : void pms_reset_timo(void *);
272 :
273 : int pms_enable_intelli(struct pms_softc *);
274 :
275 : int pms_ioctl_mouse(struct pms_softc *, u_long, caddr_t, int, struct proc *);
276 : int pms_sync_mouse(struct pms_softc *, int);
277 : void pms_proc_mouse(struct pms_softc *);
278 :
279 : int pms_enable_synaptics(struct pms_softc *);
280 : int pms_ioctl_synaptics(struct pms_softc *, u_long, caddr_t, int, struct proc *);
281 : int pms_sync_synaptics(struct pms_softc *, int);
282 : void pms_proc_synaptics(struct pms_softc *);
283 : void pms_disable_synaptics(struct pms_softc *);
284 :
285 : int pms_enable_alps(struct pms_softc *);
286 : int pms_ioctl_alps(struct pms_softc *, u_long, caddr_t, int, struct proc *);
287 : int pms_sync_alps(struct pms_softc *, int);
288 : void pms_proc_alps(struct pms_softc *);
289 :
290 : int pms_enable_elantech_v1(struct pms_softc *);
291 : int pms_enable_elantech_v2(struct pms_softc *);
292 : int pms_enable_elantech_v3(struct pms_softc *);
293 : int pms_enable_elantech_v4(struct pms_softc *);
294 : int pms_ioctl_elantech(struct pms_softc *, u_long, caddr_t, int,
295 : struct proc *);
296 : int pms_sync_elantech_v1(struct pms_softc *, int);
297 : int pms_sync_elantech_v2(struct pms_softc *, int);
298 : int pms_sync_elantech_v3(struct pms_softc *, int);
299 : int pms_sync_elantech_v4(struct pms_softc *, int);
300 : void pms_proc_elantech_v1(struct pms_softc *);
301 : void pms_proc_elantech_v2(struct pms_softc *);
302 : void pms_proc_elantech_v3(struct pms_softc *);
303 : void pms_proc_elantech_v4(struct pms_softc *);
304 :
305 : int synaptics_knock(struct pms_softc *);
306 : int synaptics_set_mode(struct pms_softc *, int);
307 : int synaptics_query(struct pms_softc *, int, int *);
308 : int synaptics_get_hwinfo(struct pms_softc *);
309 : void synaptics_sec_proc(struct pms_softc *);
310 :
311 : int alps_sec_proc(struct pms_softc *);
312 : int alps_get_hwinfo(struct pms_softc *);
313 :
314 : int elantech_knock(struct pms_softc *);
315 : int elantech_get_hwinfo_v1(struct pms_softc *);
316 : int elantech_get_hwinfo_v2(struct pms_softc *);
317 : int elantech_get_hwinfo_v3(struct pms_softc *);
318 : int elantech_get_hwinfo_v4(struct pms_softc *);
319 : int elantech_ps2_cmd(struct pms_softc *, u_char);
320 : int elantech_set_absolute_mode_v1(struct pms_softc *);
321 : int elantech_set_absolute_mode_v2(struct pms_softc *);
322 : int elantech_set_absolute_mode_v3(struct pms_softc *);
323 : int elantech_set_absolute_mode_v4(struct pms_softc *);
324 :
325 : struct cfattach pms_ca = {
326 : sizeof(struct pms_softc), pmsprobe, pmsattach, NULL,
327 : pmsactivate
328 : };
329 :
330 : struct cfdriver pms_cd = {
331 : NULL, "pms", DV_DULL
332 : };
333 :
334 : const struct wsmouse_accessops pms_accessops = {
335 : pms_enable,
336 : pms_ioctl,
337 : pms_disable,
338 : };
339 :
340 : const struct wsmouse_accessops pms_sec_accessops = {
341 : pms_sec_enable,
342 : pms_sec_ioctl,
343 : pms_sec_disable,
344 : };
345 :
346 : const struct pms_protocol pms_protocols[] = {
347 : /* Generic PS/2 mouse */
348 : {
349 : PMS_STANDARD, 3,
350 : NULL,
351 : pms_ioctl_mouse,
352 : pms_sync_mouse,
353 : pms_proc_mouse,
354 : NULL
355 : },
356 : /* Synaptics touchpad */
357 : {
358 : PMS_SYNAPTICS, 6,
359 : pms_enable_synaptics,
360 : pms_ioctl_synaptics,
361 : pms_sync_synaptics,
362 : pms_proc_synaptics,
363 : pms_disable_synaptics
364 : },
365 : /* ALPS touchpad */
366 : {
367 : PMS_ALPS, 6,
368 : pms_enable_alps,
369 : pms_ioctl_alps,
370 : pms_sync_alps,
371 : pms_proc_alps,
372 : NULL
373 : },
374 : /* Elantech touchpad (hardware version 1) */
375 : {
376 : PMS_ELANTECH_V1, 4,
377 : pms_enable_elantech_v1,
378 : pms_ioctl_elantech,
379 : pms_sync_elantech_v1,
380 : pms_proc_elantech_v1,
381 : NULL
382 : },
383 : /* Elantech touchpad (hardware version 2) */
384 : {
385 : PMS_ELANTECH_V2, 6,
386 : pms_enable_elantech_v2,
387 : pms_ioctl_elantech,
388 : pms_sync_elantech_v2,
389 : pms_proc_elantech_v2,
390 : NULL
391 : },
392 : /* Elantech touchpad (hardware version 3) */
393 : {
394 : PMS_ELANTECH_V3, 6,
395 : pms_enable_elantech_v3,
396 : pms_ioctl_elantech,
397 : pms_sync_elantech_v3,
398 : pms_proc_elantech_v3,
399 : NULL
400 : },
401 : /* Elantech touchpad (hardware version 4) */
402 : {
403 : PMS_ELANTECH_V4, 6,
404 : pms_enable_elantech_v4,
405 : pms_ioctl_elantech,
406 : pms_sync_elantech_v4,
407 : pms_proc_elantech_v4,
408 : NULL
409 : },
410 : /* Microsoft IntelliMouse */
411 : {
412 : PMS_INTELLI, 4,
413 : pms_enable_intelli,
414 : pms_ioctl_mouse,
415 : pms_sync_mouse,
416 : pms_proc_mouse,
417 : NULL
418 : },
419 : };
420 :
421 : int
422 0 : pms_cmd(struct pms_softc *sc, u_char *cmd, int len, u_char *resp, int resplen)
423 : {
424 0 : if (sc->poll) {
425 0 : return pckbc_poll_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT,
426 : cmd, len, resplen, resp, 1);
427 : } else {
428 0 : return pckbc_enqueue_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT,
429 : cmd, len, resplen, 1, resp);
430 : }
431 0 : }
432 :
433 : int
434 0 : pms_spec_cmd(struct pms_softc *sc, int cmd)
435 : {
436 0 : if (pms_set_scaling(sc, 1) ||
437 0 : pms_set_resolution(sc, (cmd >> 6) & 0x03) ||
438 0 : pms_set_resolution(sc, (cmd >> 4) & 0x03) ||
439 0 : pms_set_resolution(sc, (cmd >> 2) & 0x03) ||
440 0 : pms_set_resolution(sc, (cmd >> 0) & 0x03))
441 0 : return (-1);
442 0 : return (0);
443 0 : }
444 :
445 : int
446 0 : pms_get_devid(struct pms_softc *sc, u_char *resp)
447 : {
448 0 : u_char cmd[1];
449 :
450 0 : cmd[0] = PMS_SEND_DEV_ID;
451 0 : return (pms_cmd(sc, cmd, 1, resp, 1));
452 0 : }
453 :
454 : int
455 0 : pms_get_status(struct pms_softc *sc, u_char *resp)
456 : {
457 0 : u_char cmd[1];
458 :
459 0 : cmd[0] = PMS_SEND_DEV_STATUS;
460 0 : return (pms_cmd(sc, cmd, 1, resp, 3));
461 0 : }
462 :
463 : int
464 0 : pms_set_rate(struct pms_softc *sc, int value)
465 : {
466 0 : u_char cmd[2];
467 :
468 0 : cmd[0] = PMS_SET_SAMPLE;
469 0 : cmd[1] = value;
470 0 : return (pms_cmd(sc, cmd, 2, NULL, 0));
471 0 : }
472 :
473 : int
474 0 : pms_set_resolution(struct pms_softc *sc, int value)
475 : {
476 0 : u_char cmd[2];
477 :
478 0 : cmd[0] = PMS_SET_RES;
479 0 : cmd[1] = value;
480 0 : return (pms_cmd(sc, cmd, 2, NULL, 0));
481 0 : }
482 :
483 : int
484 0 : pms_set_scaling(struct pms_softc *sc, int scale)
485 : {
486 0 : u_char cmd[1];
487 :
488 0 : switch (scale) {
489 : case 1:
490 : default:
491 0 : cmd[0] = PMS_SET_SCALE11;
492 0 : break;
493 : case 2:
494 0 : cmd[0] = PMS_SET_SCALE21;
495 0 : break;
496 : }
497 0 : return (pms_cmd(sc, cmd, 1, NULL, 0));
498 0 : }
499 :
500 : int
501 0 : pms_reset(struct pms_softc *sc)
502 : {
503 0 : u_char cmd[1], resp[2];
504 : int res;
505 :
506 0 : cmd[0] = PMS_RESET;
507 0 : res = pms_cmd(sc, cmd, 1, resp, 2);
508 : #ifdef DEBUG
509 : if (res || resp[0] != PMS_RSTDONE || resp[1] != 0)
510 : printf("%s: reset error %d (response 0x%02x, type 0x%02x)\n",
511 : DEVNAME(sc), res, resp[0], resp[1]);
512 : #endif
513 0 : return (res);
514 0 : }
515 :
516 : int
517 0 : pms_dev_enable(struct pms_softc *sc)
518 : {
519 0 : u_char cmd[1];
520 : int res;
521 :
522 0 : cmd[0] = PMS_DEV_ENABLE;
523 0 : res = pms_cmd(sc, cmd, 1, NULL, 0);
524 0 : if (res)
525 0 : printf("%s: enable error\n", DEVNAME(sc));
526 0 : return (res);
527 0 : }
528 :
529 : int
530 0 : pms_dev_disable(struct pms_softc *sc)
531 : {
532 0 : u_char cmd[1];
533 : int res;
534 :
535 0 : cmd[0] = PMS_DEV_DISABLE;
536 0 : res = pms_cmd(sc, cmd, 1, NULL, 0);
537 0 : if (res)
538 0 : printf("%s: disable error\n", DEVNAME(sc));
539 0 : return (res);
540 0 : }
541 :
542 : void
543 0 : pms_protocol_lookup(struct pms_softc *sc)
544 : {
545 : int i;
546 :
547 0 : sc->protocol = &pms_protocols[0];
548 0 : for (i = 1; i < nitems(pms_protocols); i++) {
549 0 : pms_reset(sc);
550 0 : if (pms_protocols[i].enable(sc)) {
551 0 : sc->protocol = &pms_protocols[i];
552 0 : break;
553 : }
554 : }
555 :
556 : DPRINTF("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type);
557 0 : }
558 :
559 : /*
560 : * Detect reset announcement ([0xaa, 0x0]).
561 : * The sequence will be sent as input on rare occasions when the touchpad was
562 : * reset due to a power failure.
563 : */
564 : void
565 0 : pms_reset_detect(struct pms_softc *sc, int data)
566 : {
567 0 : switch (sc->sc_rststate) {
568 : case PMS_RST_COMMENCE:
569 0 : if (data == 0x0) {
570 0 : sc->sc_rststate = PMS_RST_ANNOUNCED;
571 0 : timeout_add_msec(&sc->sc_rsttimo, 100);
572 0 : } else if (data != PMS_RSTDONE) {
573 0 : sc->sc_rststate = 0;
574 0 : }
575 : break;
576 : default:
577 0 : if (data == PMS_RSTDONE)
578 0 : sc->sc_rststate = PMS_RST_COMMENCE;
579 : else
580 0 : sc->sc_rststate = 0;
581 : }
582 0 : }
583 :
584 : void
585 0 : pms_reset_timo(void *v)
586 : {
587 0 : struct pms_softc *sc = v;
588 0 : int s = spltty();
589 :
590 : /*
591 : * Do nothing if the reset was a false positive or if the device already
592 : * is disabled.
593 : */
594 0 : if (sc->sc_rststate == PMS_RST_ANNOUNCED &&
595 0 : sc->sc_state != PMS_STATE_DISABLED)
596 0 : task_add(systq, &sc->sc_rsttask);
597 :
598 0 : splx(s);
599 0 : }
600 :
601 : void
602 0 : pms_reset_task(void *v)
603 : {
604 0 : struct pms_softc *sc = v;
605 0 : int s = spltty();
606 :
607 : #ifdef DIAGNOSTIC
608 0 : printf("%s: device reset (state = %d)\n", DEVNAME(sc), sc->sc_rststate);
609 : #endif
610 :
611 0 : rw_enter_write(&sc->sc_state_lock);
612 :
613 0 : if (sc->sc_sec_wsmousedev != NULL)
614 0 : pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_SECONDARY);
615 0 : pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_PRIMARY);
616 :
617 0 : pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_PRIMARY);
618 0 : if (sc->sc_sec_wsmousedev != NULL)
619 0 : pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_SECONDARY);
620 :
621 0 : rw_exit_write(&sc->sc_state_lock);
622 0 : splx(s);
623 0 : }
624 :
625 : int
626 0 : pms_enable_intelli(struct pms_softc *sc)
627 : {
628 0 : u_char resp;
629 :
630 : /* the special sequence to enable the third button and the roller */
631 0 : if (pms_set_rate(sc, PMS_INTELLI_MAGIC1) ||
632 0 : pms_set_rate(sc, PMS_INTELLI_MAGIC2) ||
633 0 : pms_set_rate(sc, PMS_INTELLI_MAGIC3) ||
634 0 : pms_get_devid(sc, &resp) ||
635 0 : resp != PMS_INTELLI_ID)
636 0 : return (0);
637 :
638 0 : return (1);
639 0 : }
640 :
641 : int
642 0 : pms_ioctl_mouse(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
643 : struct proc *p)
644 : {
645 : int i;
646 :
647 0 : switch (cmd) {
648 : case WSMOUSEIO_GTYPE:
649 0 : *(u_int *)data = WSMOUSE_TYPE_PS2;
650 0 : break;
651 : case WSMOUSEIO_SRES:
652 0 : i = ((int) *(u_int *)data - 12) / 25;
653 : /* valid values are {0,1,2,3} */
654 0 : if (i < 0)
655 : i = 0;
656 0 : if (i > 3)
657 : i = 3;
658 :
659 0 : if (pms_set_resolution(sc, i))
660 0 : printf("%s: SET_RES command error\n", DEVNAME(sc));
661 : break;
662 : default:
663 0 : return (-1);
664 : }
665 0 : return (0);
666 0 : }
667 :
668 : int
669 0 : pms_sync_mouse(struct pms_softc *sc, int data)
670 : {
671 0 : if (sc->inputstate != 0)
672 0 : return (0);
673 :
674 0 : switch (sc->protocol->type) {
675 : case PMS_STANDARD:
676 0 : if ((data & 0xc0) != 0)
677 0 : return (-1);
678 : break;
679 : case PMS_INTELLI:
680 0 : if ((data & 0x08) != 0x08)
681 0 : return (-1);
682 : break;
683 : }
684 :
685 0 : return (0);
686 0 : }
687 :
688 : void
689 0 : pms_proc_mouse(struct pms_softc *sc)
690 : {
691 : u_int buttons;
692 : int dx, dy, dz;
693 :
694 0 : buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK];
695 0 : dx = (sc->packet[0] & PMS_PS2_XNEG) ?
696 0 : (int)sc->packet[1] - 256 : sc->packet[1];
697 0 : dy = (sc->packet[0] & PMS_PS2_YNEG) ?
698 0 : (int)sc->packet[2] - 256 : sc->packet[2];
699 :
700 0 : if (sc->protocol->type == PMS_INTELLI)
701 0 : dz = (signed char)sc->packet[3];
702 : else
703 : dz = 0;
704 :
705 0 : WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, dy, dz, 0);
706 0 : }
707 :
708 : int
709 0 : pmsprobe(struct device *parent, void *match, void *aux)
710 : {
711 0 : struct pckbc_attach_args *pa = aux;
712 0 : u_char cmd[1], resp[2];
713 : int res;
714 :
715 0 : if (pa->pa_slot != PCKBC_AUX_SLOT)
716 0 : return (0);
717 :
718 : /* Flush any garbage. */
719 0 : pckbc_flush(pa->pa_tag, pa->pa_slot);
720 :
721 : /* reset the device */
722 0 : cmd[0] = PMS_RESET;
723 0 : res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
724 0 : if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
725 : #ifdef DEBUG
726 : printf("pms: reset error %d (response 0x%02x, type 0x%02x)\n",
727 : res, resp[0], resp[1]);
728 : #endif
729 0 : return (0);
730 : }
731 :
732 0 : return (1);
733 0 : }
734 :
735 : void
736 0 : pmsattach(struct device *parent, struct device *self, void *aux)
737 : {
738 0 : struct pms_softc *sc = (void *)self;
739 0 : struct pckbc_attach_args *pa = aux;
740 0 : struct wsmousedev_attach_args a;
741 :
742 0 : sc->sc_kbctag = pa->pa_tag;
743 :
744 0 : pckbc_set_inputhandler(sc->sc_kbctag, PCKBC_AUX_SLOT,
745 0 : pmsinput, sc, DEVNAME(sc));
746 :
747 0 : printf("\n");
748 :
749 0 : a.accessops = &pms_accessops;
750 0 : a.accesscookie = sc;
751 :
752 0 : rw_init(&sc->sc_state_lock, "pmsst");
753 :
754 : /*
755 : * Attach the wsmouse, saving a handle to it.
756 : * Note that we don't need to check this pointer against NULL
757 : * here or in pmsintr, because if this fails pms_enable() will
758 : * never be called, so pmsinput() will never be called.
759 : */
760 0 : sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
761 :
762 0 : task_set(&sc->sc_rsttask, pms_reset_task, sc);
763 0 : timeout_set(&sc->sc_rsttimo, pms_reset_timo, sc);
764 :
765 0 : sc->poll = 1;
766 0 : sc->sc_dev_enable = 0;
767 :
768 : /* See if the device understands an extended (touchpad) protocol. */
769 0 : pms_protocol_lookup(sc);
770 :
771 : /* no interrupts until enabled */
772 0 : pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_IGNORE);
773 0 : }
774 :
775 : int
776 0 : pmsactivate(struct device *self, int act)
777 : {
778 0 : struct pms_softc *sc = (struct pms_softc *)self;
779 :
780 0 : switch (act) {
781 : case DVACT_SUSPEND:
782 0 : if (sc->sc_state == PMS_STATE_ENABLED)
783 0 : pms_change_state(sc, PMS_STATE_SUSPENDED,
784 : PMS_DEV_IGNORE);
785 : break;
786 : case DVACT_RESUME:
787 0 : if (sc->sc_state == PMS_STATE_SUSPENDED)
788 0 : pms_change_state(sc, PMS_STATE_ENABLED,
789 : PMS_DEV_IGNORE);
790 : break;
791 : }
792 0 : return (0);
793 : }
794 :
795 : int
796 0 : pms_change_state(struct pms_softc *sc, int newstate, int dev)
797 : {
798 0 : if (dev != PMS_DEV_IGNORE) {
799 0 : switch (newstate) {
800 : case PMS_STATE_ENABLED:
801 0 : if (sc->sc_dev_enable & dev)
802 0 : return (EBUSY);
803 :
804 0 : sc->sc_dev_enable |= dev;
805 :
806 0 : if (sc->sc_state == PMS_STATE_ENABLED)
807 0 : return (0);
808 :
809 : break;
810 : case PMS_STATE_DISABLED:
811 0 : sc->sc_dev_enable &= ~dev;
812 :
813 0 : if (sc->sc_dev_enable)
814 0 : return (0);
815 :
816 : break;
817 : }
818 : }
819 :
820 0 : switch (newstate) {
821 : case PMS_STATE_ENABLED:
822 0 : sc->inputstate = 0;
823 0 : sc->sc_rststate = 0;
824 :
825 0 : pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 1);
826 :
827 0 : if (sc->poll)
828 0 : pckbc_flush(sc->sc_kbctag, PCKBC_AUX_SLOT);
829 :
830 0 : pms_reset(sc);
831 0 : if (sc->protocol->enable != NULL &&
832 0 : sc->protocol->enable(sc) == 0)
833 0 : pms_protocol_lookup(sc);
834 :
835 0 : pms_dev_enable(sc);
836 0 : break;
837 : case PMS_STATE_DISABLED:
838 : case PMS_STATE_SUSPENDED:
839 0 : pms_dev_disable(sc);
840 :
841 0 : if (sc->protocol->disable)
842 0 : sc->protocol->disable(sc);
843 :
844 0 : pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 0);
845 0 : break;
846 : }
847 :
848 0 : sc->sc_state = newstate;
849 0 : sc->poll = (newstate == PMS_STATE_SUSPENDED) ? 1 : 0;
850 :
851 0 : return (0);
852 0 : }
853 :
854 : int
855 0 : pms_enable(void *v)
856 : {
857 0 : struct pms_softc *sc = v;
858 : int rv;
859 :
860 0 : rw_enter_write(&sc->sc_state_lock);
861 0 : rv = pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_PRIMARY);
862 0 : rw_exit_write(&sc->sc_state_lock);
863 :
864 0 : return (rv);
865 : }
866 :
867 : void
868 0 : pms_disable(void *v)
869 : {
870 0 : struct pms_softc *sc = v;
871 :
872 0 : rw_enter_write(&sc->sc_state_lock);
873 0 : pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_PRIMARY);
874 0 : rw_exit_write(&sc->sc_state_lock);
875 0 : }
876 :
877 : int
878 0 : pms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
879 : {
880 0 : struct pms_softc *sc = v;
881 :
882 0 : if (sc->protocol->ioctl)
883 0 : return (sc->protocol->ioctl(sc, cmd, data, flag, p));
884 : else
885 0 : return (-1);
886 0 : }
887 :
888 : int
889 0 : pms_sec_enable(void *v)
890 : {
891 0 : struct pms_softc *sc = v;
892 : int rv;
893 :
894 0 : rw_enter_write(&sc->sc_state_lock);
895 0 : rv = pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_SECONDARY);
896 0 : rw_exit_write(&sc->sc_state_lock);
897 :
898 0 : return (rv);
899 : }
900 :
901 : void
902 0 : pms_sec_disable(void *v)
903 : {
904 0 : struct pms_softc *sc = v;
905 :
906 0 : rw_enter_write(&sc->sc_state_lock);
907 0 : pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_SECONDARY);
908 0 : rw_exit_write(&sc->sc_state_lock);
909 0 : }
910 :
911 : int
912 0 : pms_sec_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
913 : {
914 0 : switch (cmd) {
915 : case WSMOUSEIO_GTYPE:
916 0 : *(u_int *)data = WSMOUSE_TYPE_PS2;
917 : break;
918 : default:
919 0 : return (-1);
920 : }
921 0 : return (0);
922 0 : }
923 :
924 : void
925 0 : pmsinput(void *vsc, int data)
926 : {
927 0 : struct pms_softc *sc = vsc;
928 :
929 0 : if (sc->sc_state != PMS_STATE_ENABLED) {
930 : /* Interrupts are not expected. Discard the byte. */
931 0 : return;
932 : }
933 :
934 0 : sc->packet[sc->inputstate] = data;
935 0 : pms_reset_detect(sc, data);
936 0 : if (sc->protocol->sync(sc, data)) {
937 : #ifdef DIAGNOSTIC
938 0 : printf("%s: not in sync yet, discard input "
939 : "(state = %d, data = %#x)\n",
940 0 : DEVNAME(sc), sc->inputstate, data);
941 : #endif
942 :
943 0 : sc->inputstate = 0;
944 0 : return;
945 : }
946 :
947 0 : sc->inputstate++;
948 :
949 0 : if (sc->inputstate != sc->protocol->packetsize)
950 0 : return;
951 :
952 0 : sc->inputstate = 0;
953 0 : sc->protocol->proc(sc);
954 0 : }
955 :
956 : int
957 0 : synaptics_set_mode(struct pms_softc *sc, int mode)
958 : {
959 0 : struct synaptics_softc *syn = sc->synaptics;
960 :
961 0 : if (pms_spec_cmd(sc, mode) ||
962 0 : pms_set_rate(sc, SYNAPTICS_CMD_SET_MODE))
963 0 : return (-1);
964 :
965 0 : syn->mode = mode;
966 :
967 0 : return (0);
968 0 : }
969 :
970 : int
971 0 : synaptics_query(struct pms_softc *sc, int query, int *val)
972 : {
973 0 : u_char resp[3];
974 :
975 0 : if (pms_spec_cmd(sc, query) ||
976 0 : pms_get_status(sc, resp))
977 0 : return (-1);
978 :
979 0 : if (val)
980 0 : *val = (resp[0] << 16) | (resp[1] << 8) | resp[2];
981 :
982 0 : return (0);
983 0 : }
984 :
985 : int
986 0 : synaptics_get_hwinfo(struct pms_softc *sc)
987 : {
988 0 : struct synaptics_softc *syn = sc->synaptics;
989 : struct wsmousehw *hw;
990 0 : int resolution = 0, max_coords = 0, min_coords = 0;
991 :
992 0 : hw = wsmouse_get_hw(sc->sc_wsmousedev);
993 :
994 0 : if (synaptics_query(sc, SYNAPTICS_QUE_IDENTIFY, &syn->identify))
995 0 : return (-1);
996 0 : if (synaptics_query(sc, SYNAPTICS_QUE_CAPABILITIES,
997 0 : &syn->capabilities))
998 0 : return (-1);
999 0 : if (synaptics_query(sc, SYNAPTICS_QUE_MODEL, &syn->model))
1000 0 : return (-1);
1001 0 : if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 1) &&
1002 0 : synaptics_query(sc, SYNAPTICS_QUE_EXT_MODEL, &syn->ext_model))
1003 0 : return (-1);
1004 0 : if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 4) &&
1005 0 : synaptics_query(sc, SYNAPTICS_QUE_EXT_CAPABILITIES,
1006 0 : &syn->ext_capabilities))
1007 0 : return (-1);
1008 0 : if ((SYNAPTICS_ID_MAJOR(syn->identify) >= 4) &&
1009 0 : synaptics_query(sc, SYNAPTICS_QUE_RESOLUTION, &resolution))
1010 0 : return (-1);
1011 0 : if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 5) &&
1012 0 : (syn->ext_capabilities & SYNAPTICS_EXT_CAP_MAX_COORDS) &&
1013 0 : synaptics_query(sc, SYNAPTICS_QUE_EXT_MAX_COORDS, &max_coords))
1014 0 : return (-1);
1015 0 : if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 7 ||
1016 0 : SYNAPTICS_ID_FULL(syn->identify) == 0x801) &&
1017 0 : (syn->ext_capabilities & SYNAPTICS_EXT_CAP_MIN_COORDS) &&
1018 0 : synaptics_query(sc, SYNAPTICS_QUE_EXT_MIN_COORDS, &min_coords))
1019 0 : return (-1);
1020 :
1021 0 : if (SYNAPTICS_ID_FULL(syn->identify) >= 0x705) {
1022 0 : if (synaptics_query(sc, SYNAPTICS_QUE_MODES, &syn->modes))
1023 0 : return (-1);
1024 0 : if ((syn->modes & SYNAPTICS_EXT2_CAP) &&
1025 0 : synaptics_query(sc, SYNAPTICS_QUE_EXT2_CAPABILITIES,
1026 0 : &syn->ext2_capabilities))
1027 0 : return (-1);
1028 : }
1029 :
1030 0 : if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) &&
1031 0 : !(syn->ext2_capabilities & SYNAPTICS_EXT2_CAP_BUTTONS_STICK)
1032 0 : && mouse_has_softbtn)
1033 0 : hw->type = WSMOUSE_TYPE_SYNAP_SBTN;
1034 : else
1035 0 : hw->type = WSMOUSE_TYPE_SYNAPTICS;
1036 :
1037 0 : hw->hw_type = (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD)
1038 : ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD;
1039 :
1040 0 : if (resolution & SYNAPTICS_RESOLUTION_VALID) {
1041 0 : hw->h_res = SYNAPTICS_RESOLUTION_X(resolution);
1042 0 : hw->v_res = SYNAPTICS_RESOLUTION_Y(resolution);
1043 0 : }
1044 :
1045 0 : hw->x_min = (min_coords ?
1046 0 : SYNAPTICS_X_LIMIT(min_coords) : SYNAPTICS_XMIN_BEZEL);
1047 0 : hw->y_min = (min_coords ?
1048 0 : SYNAPTICS_Y_LIMIT(min_coords) : SYNAPTICS_YMIN_BEZEL);
1049 0 : hw->x_max = (max_coords ?
1050 0 : SYNAPTICS_X_LIMIT(max_coords) : SYNAPTICS_XMAX_BEZEL);
1051 0 : hw->y_max = (max_coords ?
1052 0 : SYNAPTICS_Y_LIMIT(max_coords) : SYNAPTICS_YMAX_BEZEL);
1053 :
1054 0 : hw->contacts_max = SYNAPTICS_MAX_FINGERS;
1055 :
1056 0 : syn->sec_buttons = 0;
1057 :
1058 0 : if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) > 8)
1059 0 : syn->ext_model &= ~0xf000;
1060 :
1061 0 : if ((syn->model & SYNAPTICS_MODEL_NEWABS) == 0) {
1062 0 : printf("%s: don't support Synaptics OLDABS\n", DEVNAME(sc));
1063 0 : return (-1);
1064 : }
1065 :
1066 0 : if ((SYNAPTICS_ID_MAJOR(syn->identify) == 5) &&
1067 0 : (SYNAPTICS_ID_MINOR(syn->identify) == 9))
1068 0 : syn->mask = SYNAPTICS_MASK_NEWABS_RELAXED;
1069 : else
1070 0 : syn->mask = SYNAPTICS_MASK_NEWABS_STRICT;
1071 :
1072 0 : return (0);
1073 0 : }
1074 :
1075 : void
1076 0 : synaptics_sec_proc(struct pms_softc *sc)
1077 : {
1078 0 : struct synaptics_softc *syn = sc->synaptics;
1079 : u_int buttons;
1080 : int dx, dy;
1081 :
1082 0 : if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) == 0)
1083 0 : return;
1084 :
1085 0 : buttons = butmap[sc->packet[1] & PMS_PS2_BUTTONSMASK];
1086 0 : buttons |= syn->sec_buttons;
1087 0 : dx = (sc->packet[1] & PMS_PS2_XNEG) ?
1088 0 : (int)sc->packet[4] - 256 : sc->packet[4];
1089 0 : dy = (sc->packet[1] & PMS_PS2_YNEG) ?
1090 0 : (int)sc->packet[5] - 256 : sc->packet[5];
1091 :
1092 0 : WSMOUSE_INPUT(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0);
1093 0 : }
1094 :
1095 : int
1096 0 : synaptics_knock(struct pms_softc *sc)
1097 : {
1098 0 : u_char resp[3];
1099 :
1100 0 : if (pms_set_resolution(sc, 0) ||
1101 0 : pms_set_resolution(sc, 0) ||
1102 0 : pms_set_resolution(sc, 0) ||
1103 0 : pms_set_resolution(sc, 0) ||
1104 0 : pms_get_status(sc, resp) ||
1105 0 : resp[1] != SYNAPTICS_ID_MAGIC)
1106 0 : return (-1);
1107 :
1108 0 : return (0);
1109 0 : }
1110 :
1111 : int
1112 0 : pms_enable_synaptics(struct pms_softc *sc)
1113 : {
1114 0 : struct synaptics_softc *syn = sc->synaptics;
1115 0 : struct wsmousedev_attach_args a;
1116 : int mode, i;
1117 :
1118 0 : if (synaptics_knock(sc)) {
1119 0 : if (sc->synaptics == NULL)
1120 : goto err;
1121 : /*
1122 : * Some synaptics touchpads don't resume quickly.
1123 : * Retry a few times.
1124 : */
1125 0 : for (i = 10; i > 0; --i) {
1126 0 : printf("%s: device not resuming, retrying\n",
1127 0 : DEVNAME(sc));
1128 0 : pms_reset(sc);
1129 0 : if (synaptics_knock(sc) == 0)
1130 : break;
1131 0 : delay(100000);
1132 : }
1133 0 : if (i == 0) {
1134 0 : printf("%s: lost device\n", DEVNAME(sc));
1135 0 : goto err;
1136 : }
1137 : }
1138 :
1139 0 : if (sc->synaptics == NULL) {
1140 0 : sc->synaptics = syn = malloc(sizeof(struct synaptics_softc),
1141 : M_DEVBUF, M_WAITOK | M_ZERO);
1142 0 : if (syn == NULL) {
1143 0 : printf("%s: synaptics: not enough memory\n",
1144 0 : DEVNAME(sc));
1145 0 : goto err;
1146 : }
1147 :
1148 0 : if (synaptics_get_hwinfo(sc)) {
1149 0 : free(sc->synaptics, M_DEVBUF,
1150 : sizeof(struct synaptics_softc));
1151 0 : sc->synaptics = NULL;
1152 0 : goto err;
1153 : }
1154 :
1155 : /* enable pass-through PS/2 port if supported */
1156 0 : if (syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) {
1157 0 : a.accessops = &pms_sec_accessops;
1158 0 : a.accesscookie = sc;
1159 0 : sc->sc_sec_wsmousedev = config_found((void *)sc, &a,
1160 : wsmousedevprint);
1161 0 : }
1162 :
1163 0 : if (wsmouse_configure(sc->sc_wsmousedev, synaptics_params,
1164 : nitems(synaptics_params)))
1165 : goto err;
1166 :
1167 0 : printf("%s: Synaptics %s, firmware %d.%d, 0x%x 0x%x\n",
1168 0 : DEVNAME(sc),
1169 0 : (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD ?
1170 : "clickpad" : "touchpad"),
1171 0 : SYNAPTICS_ID_MAJOR(syn->identify),
1172 0 : SYNAPTICS_ID_MINOR(syn->identify),
1173 0 : syn->model, syn->ext_model);
1174 0 : }
1175 :
1176 : mode = SYNAPTICS_ABSOLUTE_MODE | SYNAPTICS_HIGH_RATE;
1177 0 : if (SYNAPTICS_ID_MAJOR(syn->identify) >= 4)
1178 0 : mode |= SYNAPTICS_DISABLE_GESTURE;
1179 0 : if (syn->capabilities & SYNAPTICS_CAP_EXTENDED)
1180 0 : mode |= SYNAPTICS_W_MODE;
1181 0 : if (synaptics_set_mode(sc, mode))
1182 : goto err;
1183 :
1184 : /* enable advanced gesture mode if supported */
1185 0 : if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_ADV_GESTURE) &&
1186 0 : (pms_spec_cmd(sc, SYNAPTICS_QUE_MODEL) ||
1187 0 : pms_set_rate(sc, SYNAPTICS_CMD_SET_ADV_GESTURE_MODE)))
1188 : goto err;
1189 :
1190 0 : return (1);
1191 :
1192 : err:
1193 0 : pms_reset(sc);
1194 :
1195 0 : return (0);
1196 0 : }
1197 :
1198 : int
1199 0 : pms_ioctl_synaptics(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
1200 : struct proc *p)
1201 : {
1202 0 : struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
1203 : struct wsmousehw *hw;
1204 : int wsmode;
1205 :
1206 0 : hw = wsmouse_get_hw(sc->sc_wsmousedev);
1207 0 : switch (cmd) {
1208 : case WSMOUSEIO_GTYPE:
1209 0 : *(u_int *)data = hw->type;
1210 0 : break;
1211 : case WSMOUSEIO_GCALIBCOORDS:
1212 0 : wsmc->minx = hw->x_min;
1213 0 : wsmc->maxx = hw->x_max;
1214 0 : wsmc->miny = hw->y_min;
1215 0 : wsmc->maxy = hw->y_max;
1216 0 : wsmc->swapxy = 0;
1217 0 : wsmc->resx = hw->h_res;
1218 0 : wsmc->resy = hw->v_res;
1219 0 : break;
1220 : case WSMOUSEIO_SETMODE:
1221 0 : wsmode = *(u_int *)data;
1222 0 : if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
1223 0 : return (EINVAL);
1224 0 : wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
1225 0 : break;
1226 : default:
1227 0 : return (-1);
1228 : }
1229 0 : return (0);
1230 0 : }
1231 :
1232 : int
1233 0 : pms_sync_synaptics(struct pms_softc *sc, int data)
1234 : {
1235 0 : struct synaptics_softc *syn = sc->synaptics;
1236 :
1237 0 : switch (sc->inputstate) {
1238 : case 0:
1239 0 : if ((data & syn->mask) != SYNAPTICS_VALID_NEWABS_FIRST)
1240 0 : return (-1);
1241 : break;
1242 : case 3:
1243 0 : if ((data & syn->mask) != SYNAPTICS_VALID_NEWABS_NEXT)
1244 0 : return (-1);
1245 : break;
1246 : }
1247 :
1248 0 : return (0);
1249 0 : }
1250 :
1251 : void
1252 0 : pms_proc_synaptics(struct pms_softc *sc)
1253 : {
1254 0 : struct synaptics_softc *syn = sc->synaptics;
1255 : u_int buttons;
1256 : int x, y, z, w, fingerwidth;
1257 :
1258 0 : w = ((sc->packet[0] & 0x30) >> 2) | ((sc->packet[0] & 0x04) >> 1) |
1259 0 : ((sc->packet[3] & 0x04) >> 2);
1260 0 : z = sc->packet[2];
1261 :
1262 0 : if ((syn->capabilities & SYNAPTICS_CAP_EXTENDED) == 0) {
1263 : /*
1264 : * Emulate W mode for models that don't provide it. Bit 3
1265 : * of the w-input signals a touch ("finger"), Bit 2 and
1266 : * the "gesture" bits 1-0 can be ignored.
1267 : */
1268 0 : if (w & 8)
1269 0 : w = 4;
1270 : else
1271 : z = w = 0;
1272 : }
1273 :
1274 :
1275 0 : if ((syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) && w == 3) {
1276 0 : synaptics_sec_proc(sc);
1277 0 : return;
1278 : }
1279 :
1280 0 : if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0)
1281 0 : return;
1282 :
1283 : /* XXX ignore advanced gesture packet, not yet supported */
1284 0 : if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_ADV_GESTURE) && w == 2)
1285 0 : return;
1286 :
1287 0 : x = ((sc->packet[3] & 0x10) << 8) | ((sc->packet[1] & 0x0f) << 8) |
1288 0 : sc->packet[4];
1289 0 : y = ((sc->packet[3] & 0x20) << 7) | ((sc->packet[1] & 0xf0) << 4) |
1290 0 : sc->packet[5];
1291 :
1292 0 : buttons = ((sc->packet[0] & sc->packet[3]) & 0x01) ?
1293 : WSMOUSE_BUTTON(1) : 0;
1294 0 : buttons |= ((sc->packet[0] & sc->packet[3]) & 0x02) ?
1295 : WSMOUSE_BUTTON(3) : 0;
1296 :
1297 0 : if (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) {
1298 0 : buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
1299 : WSMOUSE_BUTTON(1) : 0;
1300 0 : } else if (syn->capabilities & SYNAPTICS_CAP_MIDDLE_BUTTON) {
1301 0 : buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
1302 : WSMOUSE_BUTTON(2) : 0;
1303 0 : }
1304 :
1305 0 : if (syn->capabilities & SYNAPTICS_CAP_FOUR_BUTTON) {
1306 0 : buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
1307 : WSMOUSE_BUTTON(4) : 0;
1308 0 : buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x02) ?
1309 : WSMOUSE_BUTTON(5) : 0;
1310 0 : } else if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) &&
1311 0 : ((sc->packet[0] ^ sc->packet[3]) & 0x02)) {
1312 0 : if (syn->ext2_capabilities & SYNAPTICS_EXT2_CAP_BUTTONS_STICK) {
1313 : /*
1314 : * Trackstick buttons on this machine are wired to the
1315 : * trackpad as extra buttons, so route the event
1316 : * through the trackstick interface as normal buttons
1317 : */
1318 0 : syn->sec_buttons =
1319 0 : (sc->packet[4] & 0x01) ? WSMOUSE_BUTTON(1) : 0;
1320 0 : syn->sec_buttons |=
1321 0 : (sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(3) : 0;
1322 0 : syn->sec_buttons |=
1323 0 : (sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(2) : 0;
1324 0 : wsmouse_buttons(
1325 0 : sc->sc_sec_wsmousedev, syn->sec_buttons);
1326 0 : wsmouse_input_sync(sc->sc_sec_wsmousedev);
1327 0 : return;
1328 : }
1329 :
1330 0 : buttons |= (sc->packet[4] & 0x01) ? WSMOUSE_BUTTON(6) : 0;
1331 0 : buttons |= (sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(7) : 0;
1332 0 : buttons |= (sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(8) : 0;
1333 0 : buttons |= (sc->packet[5] & 0x02) ? WSMOUSE_BUTTON(9) : 0;
1334 0 : buttons |= (sc->packet[4] & 0x04) ? WSMOUSE_BUTTON(10) : 0;
1335 0 : buttons |= (sc->packet[5] & 0x04) ? WSMOUSE_BUTTON(11) : 0;
1336 0 : buttons |= (sc->packet[4] & 0x08) ? WSMOUSE_BUTTON(12) : 0;
1337 0 : buttons |= (sc->packet[5] & 0x08) ? WSMOUSE_BUTTON(13) : 0;
1338 0 : x &= ~0x0f;
1339 0 : y &= ~0x0f;
1340 0 : }
1341 :
1342 0 : if (z) {
1343 0 : fingerwidth = max(w, 4);
1344 0 : w = (w < 2 ? w + 2 : 1);
1345 0 : } else {
1346 : fingerwidth = 0;
1347 : w = 0;
1348 : }
1349 0 : wsmouse_set(sc->sc_wsmousedev, WSMOUSE_TOUCH_WIDTH, fingerwidth, 0);
1350 0 : WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w);
1351 0 : }
1352 :
1353 : void
1354 0 : pms_disable_synaptics(struct pms_softc *sc)
1355 : {
1356 0 : struct synaptics_softc *syn = sc->synaptics;
1357 :
1358 0 : if (syn->capabilities & SYNAPTICS_CAP_SLEEP)
1359 0 : synaptics_set_mode(sc, SYNAPTICS_SLEEP_MODE |
1360 : SYNAPTICS_DISABLE_GESTURE);
1361 0 : }
1362 :
1363 : int
1364 0 : alps_sec_proc(struct pms_softc *sc)
1365 : {
1366 0 : struct alps_softc *alps = sc->alps;
1367 : int dx, dy, pos = 0;
1368 :
1369 0 : if ((sc->packet[0] & PMS_ALPS_PS2_MASK) == PMS_ALPS_PS2_VALID) {
1370 : /*
1371 : * We need to keep buttons states because interleaved
1372 : * packets only signalize x/y movements.
1373 : */
1374 0 : alps->sec_buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK];
1375 0 : } else if ((sc->packet[3] & PMS_ALPS_INTERLEAVED_MASK) ==
1376 : PMS_ALPS_INTERLEAVED_VALID) {
1377 0 : sc->inputstate = 3;
1378 : pos = 3;
1379 : } else {
1380 0 : return (0);
1381 : }
1382 :
1383 0 : if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) == 0)
1384 0 : return (1);
1385 :
1386 0 : dx = (sc->packet[pos] & PMS_PS2_XNEG) ?
1387 0 : (int)sc->packet[pos + 1] - 256 : sc->packet[pos + 1];
1388 0 : dy = (sc->packet[pos] & PMS_PS2_YNEG) ?
1389 0 : (int)sc->packet[pos + 2] - 256 : sc->packet[pos + 2];
1390 :
1391 0 : WSMOUSE_INPUT(sc->sc_sec_wsmousedev, alps->sec_buttons, dx, dy, 0, 0);
1392 :
1393 0 : return (1);
1394 0 : }
1395 :
1396 : int
1397 0 : alps_get_hwinfo(struct pms_softc *sc)
1398 : {
1399 0 : struct alps_softc *alps = sc->alps;
1400 0 : u_char resp[3];
1401 : int i;
1402 : struct wsmousehw *hw;
1403 :
1404 0 : if (pms_set_resolution(sc, 0) ||
1405 0 : pms_set_scaling(sc, 2) ||
1406 0 : pms_set_scaling(sc, 2) ||
1407 0 : pms_set_scaling(sc, 2) ||
1408 0 : pms_get_status(sc, resp)) {
1409 : DPRINTF("%s: alps: model query error\n", DEVNAME(sc));
1410 0 : return (-1);
1411 : }
1412 :
1413 0 : alps->version = (resp[0] << 8) | (resp[1] << 4) | (resp[2] / 20 + 1);
1414 :
1415 0 : for (i = 0; i < nitems(alps_models); i++)
1416 0 : if (alps->version == alps_models[i].version) {
1417 0 : alps->model = alps_models[i].model;
1418 0 : alps->mask = alps_models[i].mask;
1419 :
1420 0 : hw = wsmouse_get_hw(sc->sc_wsmousedev);
1421 0 : hw->type = WSMOUSE_TYPE_ALPS;
1422 0 : hw->hw_type = WSMOUSEHW_TOUCHPAD;
1423 0 : hw->x_min = ALPS_XMIN_BEZEL;
1424 0 : hw->y_min = ALPS_YMIN_BEZEL;
1425 0 : hw->x_max = ALPS_XMAX_BEZEL;
1426 0 : hw->y_max = ALPS_YMAX_BEZEL;
1427 0 : hw->contacts_max = 1;
1428 :
1429 0 : return (0);
1430 : }
1431 :
1432 0 : return (-1);
1433 0 : }
1434 :
1435 : int
1436 0 : pms_enable_alps(struct pms_softc *sc)
1437 : {
1438 0 : struct alps_softc *alps = sc->alps;
1439 0 : struct wsmousedev_attach_args a;
1440 0 : u_char resp[3];
1441 :
1442 0 : if (pms_set_resolution(sc, 0) ||
1443 0 : pms_set_scaling(sc, 1) ||
1444 0 : pms_set_scaling(sc, 1) ||
1445 0 : pms_set_scaling(sc, 1) ||
1446 0 : pms_get_status(sc, resp) ||
1447 0 : resp[0] != PMS_ALPS_MAGIC1 ||
1448 0 : resp[1] != PMS_ALPS_MAGIC2 ||
1449 0 : (resp[2] != PMS_ALPS_MAGIC3_1 && resp[2] != PMS_ALPS_MAGIC3_2 &&
1450 0 : resp[2] != PMS_ALPS_MAGIC3_3))
1451 : goto err;
1452 :
1453 0 : if (sc->alps == NULL) {
1454 0 : sc->alps = alps = malloc(sizeof(struct alps_softc),
1455 : M_DEVBUF, M_WAITOK | M_ZERO);
1456 0 : if (alps == NULL) {
1457 0 : printf("%s: alps: not enough memory\n", DEVNAME(sc));
1458 0 : goto err;
1459 : }
1460 :
1461 0 : if (alps_get_hwinfo(sc)) {
1462 0 : free(sc->alps, M_DEVBUF, sizeof(struct alps_softc));
1463 0 : sc->alps = NULL;
1464 0 : goto err;
1465 : }
1466 :
1467 0 : if (wsmouse_configure(sc->sc_wsmousedev, alps_params,
1468 : nitems(alps_params))) {
1469 0 : free(sc->alps, M_DEVBUF, sizeof(struct alps_softc));
1470 0 : sc->alps = NULL;
1471 0 : printf("%s: setup failed\n", DEVNAME(sc));
1472 0 : goto err;
1473 : }
1474 :
1475 0 : printf("%s: ALPS %s, version 0x%04x\n", DEVNAME(sc),
1476 0 : (alps->model & ALPS_DUALPOINT ? "Dualpoint" : "Glidepoint"),
1477 0 : alps->version);
1478 :
1479 :
1480 0 : if (alps->model & ALPS_DUALPOINT) {
1481 0 : a.accessops = &pms_sec_accessops;
1482 0 : a.accesscookie = sc;
1483 0 : sc->sc_sec_wsmousedev = config_found((void *)sc, &a,
1484 : wsmousedevprint);
1485 0 : }
1486 : }
1487 :
1488 0 : if (alps->model == 0)
1489 : goto err;
1490 :
1491 0 : if ((alps->model & ALPS_PASSTHROUGH) &&
1492 0 : (pms_set_scaling(sc, 2) ||
1493 0 : pms_set_scaling(sc, 2) ||
1494 0 : pms_set_scaling(sc, 2) ||
1495 0 : pms_dev_disable(sc))) {
1496 : DPRINTF("%s: alps: passthrough on error\n", DEVNAME(sc));
1497 : goto err;
1498 : }
1499 :
1500 0 : if (pms_dev_disable(sc) ||
1501 0 : pms_dev_disable(sc) ||
1502 0 : pms_set_rate(sc, 0x0a)) {
1503 : DPRINTF("%s: alps: tapping error\n", DEVNAME(sc));
1504 : goto err;
1505 : }
1506 :
1507 0 : if (pms_dev_disable(sc) ||
1508 0 : pms_dev_disable(sc) ||
1509 0 : pms_dev_disable(sc) ||
1510 0 : pms_dev_disable(sc) ||
1511 0 : pms_dev_enable(sc)) {
1512 : DPRINTF("%s: alps: absolute mode error\n", DEVNAME(sc));
1513 : goto err;
1514 : }
1515 :
1516 0 : if ((alps->model & ALPS_PASSTHROUGH) &&
1517 0 : (pms_set_scaling(sc, 1) ||
1518 0 : pms_set_scaling(sc, 1) ||
1519 0 : pms_set_scaling(sc, 1) ||
1520 0 : pms_dev_disable(sc))) {
1521 : DPRINTF("%s: alps: passthrough off error\n", DEVNAME(sc));
1522 : goto err;
1523 : }
1524 :
1525 0 : alps->sec_buttons = 0;
1526 :
1527 0 : return (1);
1528 :
1529 : err:
1530 0 : pms_reset(sc);
1531 :
1532 0 : return (0);
1533 0 : }
1534 :
1535 : int
1536 0 : pms_ioctl_alps(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
1537 : struct proc *p)
1538 : {
1539 0 : struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
1540 : int wsmode;
1541 : struct wsmousehw *hw;
1542 :
1543 0 : switch (cmd) {
1544 : case WSMOUSEIO_GTYPE:
1545 0 : *(u_int *)data = WSMOUSE_TYPE_ALPS;
1546 0 : break;
1547 : case WSMOUSEIO_GCALIBCOORDS:
1548 0 : hw = wsmouse_get_hw(sc->sc_wsmousedev);
1549 0 : wsmc->minx = hw->x_min;
1550 0 : wsmc->maxx = hw->x_max;
1551 0 : wsmc->miny = hw->y_min;
1552 0 : wsmc->maxy = hw->y_max;
1553 0 : wsmc->swapxy = 0;
1554 0 : break;
1555 : case WSMOUSEIO_SETMODE:
1556 0 : wsmode = *(u_int *)data;
1557 0 : if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
1558 0 : return (EINVAL);
1559 0 : wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
1560 0 : break;
1561 : default:
1562 0 : return (-1);
1563 : }
1564 0 : return (0);
1565 0 : }
1566 :
1567 : int
1568 0 : pms_sync_alps(struct pms_softc *sc, int data)
1569 : {
1570 0 : struct alps_softc *alps = sc->alps;
1571 :
1572 0 : if ((alps->model & ALPS_DUALPOINT) &&
1573 0 : (sc->packet[0] & PMS_ALPS_PS2_MASK) == PMS_ALPS_PS2_VALID) {
1574 0 : if (sc->inputstate == 2)
1575 0 : sc->inputstate += 3;
1576 0 : return (0);
1577 : }
1578 :
1579 0 : switch (sc->inputstate) {
1580 : case 0:
1581 0 : if ((data & alps->mask) != alps->mask)
1582 0 : return (-1);
1583 : break;
1584 : case 1:
1585 : case 2:
1586 : case 3:
1587 0 : if ((data & PMS_ALPS_MASK) != PMS_ALPS_VALID)
1588 0 : return (-1);
1589 : break;
1590 : case 4:
1591 : case 5:
1592 0 : if ((alps->model & ALPS_INTERLEAVED) == 0 &&
1593 0 : (data & PMS_ALPS_MASK) != PMS_ALPS_VALID)
1594 0 : return (-1);
1595 : break;
1596 : }
1597 :
1598 0 : return (0);
1599 0 : }
1600 :
1601 : void
1602 0 : pms_proc_alps(struct pms_softc *sc)
1603 : {
1604 0 : struct alps_softc *alps = sc->alps;
1605 : int x, y, z, dx, dy;
1606 : u_int buttons, gesture;
1607 :
1608 0 : if ((alps->model & ALPS_DUALPOINT) && alps_sec_proc(sc))
1609 0 : return;
1610 :
1611 0 : x = sc->packet[1] | ((sc->packet[2] & 0x78) << 4);
1612 0 : y = sc->packet[4] | ((sc->packet[3] & 0x70) << 3);
1613 0 : z = sc->packet[5];
1614 :
1615 0 : buttons = ((sc->packet[3] & 1) ? WSMOUSE_BUTTON(1) : 0) |
1616 0 : ((sc->packet[3] & 2) ? WSMOUSE_BUTTON(3) : 0) |
1617 0 : ((sc->packet[3] & 4) ? WSMOUSE_BUTTON(2) : 0);
1618 :
1619 0 : if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) && z == ALPS_Z_MAGIC) {
1620 0 : dx = (x > ALPS_XSEC_BEZEL / 2) ? (x - ALPS_XSEC_BEZEL) : x;
1621 0 : dy = (y > ALPS_YSEC_BEZEL / 2) ? (y - ALPS_YSEC_BEZEL) : y;
1622 :
1623 0 : WSMOUSE_INPUT(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0);
1624 :
1625 0 : return;
1626 : }
1627 :
1628 0 : if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0)
1629 0 : return;
1630 :
1631 : /*
1632 : * XXX The Y-axis is in the oposit direction compared to
1633 : * Synaptics touchpads and PS/2 mouses.
1634 : * It's why we need to translate the y value here for both
1635 : * NATIVE and COMPAT modes.
1636 : */
1637 0 : y = ALPS_YMAX_BEZEL - y + ALPS_YMIN_BEZEL;
1638 :
1639 0 : if (alps->gesture == ALPS_TAP) {
1640 : /* Report a touch with the tap coordinates. */
1641 0 : WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons,
1642 : alps->old_x, alps->old_y, ALPS_PRESSURE, 0);
1643 0 : if (z > 0) {
1644 : /*
1645 : * The hardware doesn't send a null pressure
1646 : * event when dragging starts.
1647 : */
1648 0 : WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons,
1649 : alps->old_x, alps->old_y, 0, 0);
1650 0 : }
1651 : }
1652 :
1653 0 : gesture = sc->packet[2] & 0x03;
1654 0 : if (gesture != ALPS_TAP)
1655 0 : WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, 0);
1656 :
1657 0 : if (alps->gesture != ALPS_DRAG || gesture != ALPS_TAP)
1658 0 : alps->gesture = gesture;
1659 :
1660 0 : alps->old_x = x;
1661 0 : alps->old_y = y;
1662 0 : }
1663 :
1664 : int
1665 0 : elantech_set_absolute_mode_v1(struct pms_softc *sc)
1666 : {
1667 : int i;
1668 0 : u_char resp[3];
1669 :
1670 : /* Enable absolute mode. Magic numbers from Linux driver. */
1671 0 : if (pms_spec_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
1672 0 : pms_spec_cmd(sc, 0x10) ||
1673 0 : pms_spec_cmd(sc, 0x16) ||
1674 0 : pms_set_scaling(sc, 1) ||
1675 0 : pms_spec_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
1676 0 : pms_spec_cmd(sc, 0x11) ||
1677 0 : pms_spec_cmd(sc, 0x8f) ||
1678 0 : pms_set_scaling(sc, 1))
1679 0 : return (-1);
1680 :
1681 : /* Read back reg 0x10 to ensure hardware is ready. */
1682 0 : for (i = 0; i < 5; i++) {
1683 0 : if (pms_spec_cmd(sc, ELANTECH_CMD_READ_REG) ||
1684 0 : pms_spec_cmd(sc, 0x10) ||
1685 0 : pms_get_status(sc, resp) == 0)
1686 : break;
1687 0 : delay(2000);
1688 : }
1689 0 : if (i == 5)
1690 0 : return (-1);
1691 :
1692 0 : if ((resp[0] & ELANTECH_ABSOLUTE_MODE) == 0)
1693 0 : return (-1);
1694 :
1695 0 : return (0);
1696 0 : }
1697 :
1698 : int
1699 0 : elantech_set_absolute_mode_v2(struct pms_softc *sc)
1700 : {
1701 : int i;
1702 0 : u_char resp[3];
1703 0 : u_char reg10 = (sc->elantech->fw_version == 0x20030 ? 0x54 : 0xc4);
1704 :
1705 : /* Enable absolute mode. Magic numbers from Linux driver. */
1706 0 : if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1707 0 : elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
1708 0 : elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1709 0 : elantech_ps2_cmd(sc, 0x10) ||
1710 0 : elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1711 0 : elantech_ps2_cmd(sc, reg10) ||
1712 0 : pms_set_scaling(sc, 1) ||
1713 0 : elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1714 0 : elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
1715 0 : elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1716 0 : elantech_ps2_cmd(sc, 0x11) ||
1717 0 : elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1718 0 : elantech_ps2_cmd(sc, 0x88) ||
1719 0 : pms_set_scaling(sc, 1))
1720 0 : return (-1);
1721 :
1722 : /* Read back reg 0x10 to ensure hardware is ready. */
1723 0 : for (i = 0; i < 5; i++) {
1724 0 : if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1725 0 : elantech_ps2_cmd(sc, ELANTECH_CMD_READ_REG) ||
1726 0 : elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1727 0 : elantech_ps2_cmd(sc, 0x10) ||
1728 0 : pms_get_status(sc, resp) == 0)
1729 : break;
1730 0 : delay(2000);
1731 : }
1732 0 : if (i == 5)
1733 0 : return (-1);
1734 :
1735 0 : return (0);
1736 0 : }
1737 :
1738 : int
1739 0 : elantech_set_absolute_mode_v3(struct pms_softc *sc)
1740 : {
1741 : int i;
1742 0 : u_char resp[3];
1743 :
1744 : /* Enable absolute mode. Magic numbers from Linux driver. */
1745 0 : if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1746 0 : elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) ||
1747 0 : elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1748 0 : elantech_ps2_cmd(sc, 0x10) ||
1749 0 : elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1750 0 : elantech_ps2_cmd(sc, 0x0b) ||
1751 0 : pms_set_scaling(sc, 1))
1752 0 : return (-1);
1753 :
1754 : /* Read back reg 0x10 to ensure hardware is ready. */
1755 0 : for (i = 0; i < 5; i++) {
1756 0 : if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1757 0 : elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) ||
1758 0 : elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1759 0 : elantech_ps2_cmd(sc, 0x10) ||
1760 0 : pms_get_status(sc, resp) == 0)
1761 : break;
1762 0 : delay(2000);
1763 : }
1764 0 : if (i == 5)
1765 0 : return (-1);
1766 :
1767 0 : return (0);
1768 0 : }
1769 :
1770 : int
1771 0 : elantech_set_absolute_mode_v4(struct pms_softc *sc)
1772 : {
1773 : /* Enable absolute mode. Magic numbers from Linux driver. */
1774 0 : if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1775 0 : elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) ||
1776 0 : elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1777 0 : elantech_ps2_cmd(sc, 0x07) ||
1778 0 : elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1779 0 : elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) ||
1780 0 : elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1781 0 : elantech_ps2_cmd(sc, 0x01) ||
1782 0 : pms_set_scaling(sc, 1))
1783 0 : return (-1);
1784 :
1785 : /* v4 has no register 0x10 to read response from */
1786 :
1787 0 : return (0);
1788 0 : }
1789 :
1790 : int
1791 0 : elantech_get_hwinfo_v1(struct pms_softc *sc)
1792 : {
1793 0 : struct elantech_softc *elantech = sc->elantech;
1794 : struct wsmousehw *hw;
1795 0 : int fw_version;
1796 0 : u_char capabilities[3];
1797 :
1798 0 : if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
1799 0 : return (-1);
1800 :
1801 0 : if (fw_version < 0x20030 || fw_version == 0x20600) {
1802 0 : if (fw_version < 0x20000)
1803 0 : elantech->flags |= ELANTECH_F_HW_V1_OLD;
1804 : } else
1805 0 : return (-1);
1806 :
1807 0 : elantech->fw_version = fw_version;
1808 :
1809 0 : if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) ||
1810 0 : pms_get_status(sc, capabilities))
1811 0 : return (-1);
1812 :
1813 0 : if (capabilities[0] & ELANTECH_CAP_HAS_ROCKER)
1814 0 : elantech->flags |= ELANTECH_F_HAS_ROCKER;
1815 :
1816 0 : if (elantech_set_absolute_mode_v1(sc))
1817 0 : return (-1);
1818 :
1819 0 : hw = wsmouse_get_hw(sc->sc_wsmousedev);
1820 0 : hw->type = WSMOUSE_TYPE_ELANTECH;
1821 0 : hw->hw_type = WSMOUSEHW_TOUCHPAD;
1822 0 : hw->x_min = ELANTECH_V1_X_MIN;
1823 0 : hw->x_max = ELANTECH_V1_X_MAX;
1824 0 : hw->y_min = ELANTECH_V1_Y_MIN;
1825 0 : hw->y_max = ELANTECH_V1_Y_MAX;
1826 :
1827 0 : return (0);
1828 0 : }
1829 :
1830 : int
1831 0 : elantech_get_hwinfo_v2(struct pms_softc *sc)
1832 : {
1833 0 : struct elantech_softc *elantech = sc->elantech;
1834 : struct wsmousehw *hw;
1835 0 : int fw_version, ic_ver;
1836 0 : u_char capabilities[3];
1837 : int i, fixed_dpi;
1838 0 : u_char resp[3];
1839 :
1840 0 : if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
1841 0 : return (-1);
1842 :
1843 0 : ic_ver = (fw_version & 0x0f0000) >> 16;
1844 0 : if (ic_ver != 2 && ic_ver != 4)
1845 0 : return (-1);
1846 :
1847 0 : elantech->fw_version = fw_version;
1848 0 : if (fw_version >= 0x20800)
1849 0 : elantech->flags |= ELANTECH_F_REPORTS_PRESSURE;
1850 :
1851 0 : if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) ||
1852 0 : pms_get_status(sc, capabilities))
1853 0 : return (-1);
1854 :
1855 0 : if (elantech_set_absolute_mode_v2(sc))
1856 0 : return (-1);
1857 :
1858 0 : hw = wsmouse_get_hw(sc->sc_wsmousedev);
1859 0 : hw->type = WSMOUSE_TYPE_ELANTECH;
1860 0 : hw->hw_type = WSMOUSEHW_TOUCHPAD;
1861 :
1862 0 : if (fw_version == 0x20800 || fw_version == 0x20b00 ||
1863 0 : fw_version == 0x20030) {
1864 0 : hw->x_max = ELANTECH_V2_X_MAX;
1865 0 : hw->y_max = ELANTECH_V2_Y_MAX;
1866 0 : } else {
1867 0 : if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) ||
1868 0 : pms_get_status(sc, resp))
1869 0 : return (-1);
1870 0 : fixed_dpi = resp[1] & 0x10;
1871 0 : i = (fw_version > 0x20800 && fw_version < 0x20900) ? 1 : 2;
1872 0 : if ((fw_version >> 16) == 0x14 && fixed_dpi) {
1873 0 : if (pms_spec_cmd(sc, ELANTECH_QUE_SAMPLE) ||
1874 0 : pms_get_status(sc, resp))
1875 0 : return (-1);
1876 0 : hw->x_max = (capabilities[1] - i) * resp[1] / 2;
1877 0 : hw->y_max = (capabilities[2] - i) * resp[2] / 2;
1878 0 : } else if (fw_version == 0x040216) {
1879 0 : hw->x_max = 819;
1880 0 : hw->y_max = 405;
1881 0 : } else if (fw_version == 0x040219 || fw_version == 0x040215) {
1882 0 : hw->x_max = 900;
1883 0 : hw->y_max = 500;
1884 0 : } else {
1885 0 : hw->x_max = (capabilities[1] - i) * 64;
1886 0 : hw->y_max = (capabilities[2] - i) * 64;
1887 : }
1888 : }
1889 :
1890 0 : return (0);
1891 0 : }
1892 :
1893 : int
1894 0 : elantech_get_hwinfo_v3(struct pms_softc *sc)
1895 : {
1896 0 : struct elantech_softc *elantech = sc->elantech;
1897 : struct wsmousehw *hw;
1898 0 : int fw_version;
1899 0 : u_char resp[3];
1900 :
1901 0 : if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
1902 0 : return (-1);
1903 :
1904 0 : if (((fw_version & 0x0f0000) >> 16) != 5)
1905 0 : return (-1);
1906 :
1907 0 : elantech->fw_version = fw_version;
1908 0 : elantech->flags |= ELANTECH_F_REPORTS_PRESSURE;
1909 :
1910 0 : if ((fw_version & 0x4000) == 0x4000)
1911 0 : elantech->flags |= ELANTECH_F_CRC_ENABLED;
1912 :
1913 0 : if (elantech_set_absolute_mode_v3(sc))
1914 0 : return (-1);
1915 :
1916 0 : if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) ||
1917 0 : pms_get_status(sc, resp))
1918 0 : return (-1);
1919 :
1920 0 : hw = wsmouse_get_hw(sc->sc_wsmousedev);
1921 0 : hw->x_max = elantech->max_x = (resp[0] & 0x0f) << 8 | resp[1];
1922 0 : hw->y_max = elantech->max_y = (resp[0] & 0xf0) << 4 | resp[2];
1923 :
1924 0 : hw->type = WSMOUSE_TYPE_ELANTECH;
1925 0 : hw->hw_type = WSMOUSEHW_TOUCHPAD;
1926 :
1927 0 : return (0);
1928 0 : }
1929 :
1930 : int
1931 0 : elantech_get_hwinfo_v4(struct pms_softc *sc)
1932 : {
1933 0 : struct elantech_softc *elantech = sc->elantech;
1934 : struct wsmousehw *hw;
1935 0 : int fw_version;
1936 0 : u_char capabilities[3];
1937 0 : u_char resp[3];
1938 :
1939 0 : if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
1940 0 : return (-1);
1941 :
1942 0 : if ((fw_version & 0x0f0000) >> 16 != 6
1943 0 : && (fw_version & 0x0f0000) >> 16 != 8
1944 0 : && (fw_version & 0x0f0000) >> 16 != 15)
1945 0 : return (-1);
1946 :
1947 0 : elantech->fw_version = fw_version;
1948 0 : elantech->flags |= ELANTECH_F_REPORTS_PRESSURE;
1949 :
1950 0 : if (elantech_set_absolute_mode_v4(sc))
1951 0 : return (-1);
1952 :
1953 0 : if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) ||
1954 0 : pms_get_status(sc, capabilities))
1955 0 : return (-1);
1956 :
1957 0 : if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) ||
1958 0 : pms_get_status(sc, resp))
1959 0 : return (-1);
1960 :
1961 0 : hw = wsmouse_get_hw(sc->sc_wsmousedev);
1962 0 : hw->x_max = (resp[0] & 0x0f) << 8 | resp[1];
1963 0 : hw->y_max = (resp[0] & 0xf0) << 4 | resp[2];
1964 :
1965 0 : if ((capabilities[1] < 2) || (capabilities[1] > hw->x_max))
1966 0 : return (-1);
1967 :
1968 0 : if (capabilities[0] & ELANTECH_CAP_TRACKPOINT)
1969 0 : elantech->flags |= ELANTECH_F_TRACKPOINT;
1970 :
1971 0 : hw->type = WSMOUSE_TYPE_ELANTECH;
1972 0 : hw->hw_type = WSMOUSEHW_CLICKPAD;
1973 0 : hw->mt_slots = ELANTECH_MAX_FINGERS;
1974 :
1975 0 : elantech->width = hw->x_max / (capabilities[1] - 1);
1976 :
1977 0 : return (0);
1978 0 : }
1979 :
1980 : int
1981 0 : elantech_ps2_cmd(struct pms_softc *sc, u_char command)
1982 : {
1983 0 : u_char cmd[1];
1984 :
1985 0 : cmd[0] = command;
1986 0 : return (pms_cmd(sc, cmd, 1, NULL, 0));
1987 0 : }
1988 :
1989 : int
1990 0 : elantech_knock(struct pms_softc *sc)
1991 : {
1992 0 : u_char resp[3];
1993 :
1994 0 : if (pms_dev_disable(sc) ||
1995 0 : pms_set_scaling(sc, 1) ||
1996 0 : pms_set_scaling(sc, 1) ||
1997 0 : pms_set_scaling(sc, 1) ||
1998 0 : pms_get_status(sc, resp) ||
1999 0 : resp[0] != PMS_ELANTECH_MAGIC1 ||
2000 0 : resp[1] != PMS_ELANTECH_MAGIC2 ||
2001 0 : (resp[2] != PMS_ELANTECH_MAGIC3_1 &&
2002 0 : resp[2] != PMS_ELANTECH_MAGIC3_2))
2003 0 : return (-1);
2004 :
2005 0 : return (0);
2006 0 : }
2007 :
2008 : int
2009 0 : pms_enable_elantech_v1(struct pms_softc *sc)
2010 : {
2011 0 : struct elantech_softc *elantech = sc->elantech;
2012 : int i;
2013 :
2014 0 : if (elantech_knock(sc))
2015 : goto err;
2016 :
2017 0 : if (sc->elantech == NULL) {
2018 0 : sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
2019 : M_DEVBUF, M_WAITOK | M_ZERO);
2020 0 : if (elantech == NULL) {
2021 0 : printf("%s: elantech: not enough memory\n",
2022 0 : DEVNAME(sc));
2023 0 : goto err;
2024 : }
2025 :
2026 0 : if (elantech_get_hwinfo_v1(sc)) {
2027 0 : free(sc->elantech, M_DEVBUF,
2028 : sizeof(struct elantech_softc));
2029 0 : sc->elantech = NULL;
2030 0 : goto err;
2031 : }
2032 0 : if (wsmouse_configure(sc->sc_wsmousedev, NULL, 0)) {
2033 0 : free(sc->elantech, M_DEVBUF,
2034 : sizeof(struct elantech_softc));
2035 0 : sc->elantech = NULL;
2036 0 : printf("%s: elantech: setup failed\n", DEVNAME(sc));
2037 0 : goto err;
2038 : }
2039 :
2040 0 : printf("%s: Elantech Touchpad, version %d, firmware 0x%x\n",
2041 0 : DEVNAME(sc), 1, sc->elantech->fw_version);
2042 0 : } else if (elantech_set_absolute_mode_v1(sc))
2043 : goto err;
2044 :
2045 0 : for (i = 0; i < nitems(sc->elantech->parity); i++)
2046 0 : sc->elantech->parity[i] = sc->elantech->parity[i & (i - 1)] ^ 1;
2047 :
2048 0 : return (1);
2049 :
2050 : err:
2051 0 : pms_reset(sc);
2052 :
2053 0 : return (0);
2054 0 : }
2055 :
2056 : int
2057 0 : pms_enable_elantech_v2(struct pms_softc *sc)
2058 : {
2059 0 : struct elantech_softc *elantech = sc->elantech;
2060 :
2061 0 : if (elantech_knock(sc))
2062 : goto err;
2063 :
2064 0 : if (sc->elantech == NULL) {
2065 0 : sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
2066 : M_DEVBUF, M_WAITOK | M_ZERO);
2067 0 : if (elantech == NULL) {
2068 0 : printf("%s: elantech: not enough memory\n",
2069 0 : DEVNAME(sc));
2070 0 : goto err;
2071 : }
2072 :
2073 0 : if (elantech_get_hwinfo_v2(sc)) {
2074 0 : free(sc->elantech, M_DEVBUF,
2075 : sizeof(struct elantech_softc));
2076 0 : sc->elantech = NULL;
2077 0 : goto err;
2078 : }
2079 0 : if (wsmouse_configure(sc->sc_wsmousedev, NULL, 0)) {
2080 0 : free(sc->elantech, M_DEVBUF,
2081 : sizeof(struct elantech_softc));
2082 0 : sc->elantech = NULL;
2083 0 : printf("%s: elantech: setup failed\n", DEVNAME(sc));
2084 0 : goto err;
2085 : }
2086 :
2087 0 : printf("%s: Elantech Touchpad, version %d, firmware 0x%x\n",
2088 0 : DEVNAME(sc), 2, sc->elantech->fw_version);
2089 0 : } else if (elantech_set_absolute_mode_v2(sc))
2090 : goto err;
2091 :
2092 0 : return (1);
2093 :
2094 : err:
2095 0 : pms_reset(sc);
2096 :
2097 0 : return (0);
2098 0 : }
2099 :
2100 : int
2101 0 : pms_enable_elantech_v3(struct pms_softc *sc)
2102 : {
2103 0 : struct elantech_softc *elantech = sc->elantech;
2104 :
2105 0 : if (elantech_knock(sc))
2106 : goto err;
2107 :
2108 0 : if (sc->elantech == NULL) {
2109 0 : sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
2110 : M_DEVBUF, M_WAITOK | M_ZERO);
2111 0 : if (elantech == NULL) {
2112 0 : printf("%s: elantech: not enough memory\n",
2113 0 : DEVNAME(sc));
2114 0 : goto err;
2115 : }
2116 :
2117 0 : if (elantech_get_hwinfo_v3(sc)) {
2118 0 : free(sc->elantech, M_DEVBUF,
2119 : sizeof(struct elantech_softc));
2120 0 : sc->elantech = NULL;
2121 0 : goto err;
2122 : }
2123 0 : if (wsmouse_configure(sc->sc_wsmousedev, NULL, 0)) {
2124 0 : free(sc->elantech, M_DEVBUF,
2125 : sizeof(struct elantech_softc));
2126 0 : sc->elantech = NULL;
2127 0 : printf("%s: elantech: setup failed\n", DEVNAME(sc));
2128 0 : goto err;
2129 : }
2130 :
2131 0 : printf("%s: Elantech Touchpad, version %d, firmware 0x%x\n",
2132 0 : DEVNAME(sc), 3, sc->elantech->fw_version);
2133 0 : } else if (elantech_set_absolute_mode_v3(sc))
2134 : goto err;
2135 :
2136 0 : return (1);
2137 :
2138 : err:
2139 0 : pms_reset(sc);
2140 :
2141 0 : return (0);
2142 0 : }
2143 :
2144 : int
2145 0 : pms_enable_elantech_v4(struct pms_softc *sc)
2146 : {
2147 0 : struct elantech_softc *elantech = sc->elantech;
2148 0 : struct wsmousedev_attach_args a;
2149 :
2150 0 : if (elantech_knock(sc))
2151 : goto err;
2152 :
2153 0 : if (sc->elantech == NULL) {
2154 0 : sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
2155 : M_DEVBUF, M_WAITOK | M_ZERO);
2156 0 : if (elantech == NULL) {
2157 0 : printf("%s: elantech: not enough memory\n",
2158 0 : DEVNAME(sc));
2159 0 : goto err;
2160 : }
2161 :
2162 0 : if (elantech_get_hwinfo_v4(sc)) {
2163 0 : free(sc->elantech, M_DEVBUF,
2164 : sizeof(struct elantech_softc));
2165 0 : sc->elantech = NULL;
2166 0 : goto err;
2167 : }
2168 0 : if (wsmouse_configure(sc->sc_wsmousedev, NULL, 0)) {
2169 0 : free(sc->elantech, M_DEVBUF,
2170 : sizeof(struct elantech_softc));
2171 0 : sc->elantech = NULL;
2172 0 : printf("%s: elantech: setup failed\n", DEVNAME(sc));
2173 0 : goto err;
2174 : }
2175 :
2176 0 : printf("%s: Elantech Clickpad, version %d, firmware 0x%x\n",
2177 0 : DEVNAME(sc), 4, sc->elantech->fw_version);
2178 :
2179 0 : if (sc->elantech->flags & ELANTECH_F_TRACKPOINT) {
2180 0 : a.accessops = &pms_sec_accessops;
2181 0 : a.accesscookie = sc;
2182 0 : sc->sc_sec_wsmousedev = config_found((void *) sc, &a,
2183 : wsmousedevprint);
2184 0 : }
2185 :
2186 0 : } else if (elantech_set_absolute_mode_v4(sc))
2187 : goto err;
2188 :
2189 0 : return (1);
2190 :
2191 : err:
2192 0 : pms_reset(sc);
2193 :
2194 0 : return (0);
2195 0 : }
2196 :
2197 : int
2198 0 : pms_ioctl_elantech(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
2199 : struct proc *p)
2200 : {
2201 0 : struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
2202 : struct wsmousehw *hw;
2203 : int wsmode;
2204 :
2205 0 : switch (cmd) {
2206 : case WSMOUSEIO_GTYPE:
2207 0 : *(u_int *)data = WSMOUSE_TYPE_ELANTECH;
2208 0 : break;
2209 : case WSMOUSEIO_GCALIBCOORDS:
2210 0 : hw = wsmouse_get_hw(sc->sc_wsmousedev);
2211 0 : wsmc->minx = hw->x_min;
2212 0 : wsmc->maxx = hw->x_max;
2213 0 : wsmc->miny = hw->y_min;
2214 0 : wsmc->maxy = hw->y_max;
2215 0 : wsmc->swapxy = 0;
2216 0 : wsmc->resx = hw->h_res;
2217 0 : wsmc->resy = hw->v_res;
2218 0 : break;
2219 : case WSMOUSEIO_SETMODE:
2220 0 : wsmode = *(u_int *)data;
2221 0 : if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
2222 0 : return (EINVAL);
2223 0 : wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
2224 0 : break;
2225 : default:
2226 0 : return (-1);
2227 : }
2228 0 : return (0);
2229 0 : }
2230 :
2231 : int
2232 0 : pms_sync_elantech_v1(struct pms_softc *sc, int data)
2233 : {
2234 0 : struct elantech_softc *elantech = sc->elantech;
2235 : u_char p;
2236 :
2237 0 : switch (sc->inputstate) {
2238 : case 0:
2239 0 : if (elantech->flags & ELANTECH_F_HW_V1_OLD) {
2240 0 : elantech->p1 = (data & 0x20) >> 5;
2241 0 : elantech->p2 = (data & 0x10) >> 4;
2242 0 : } else {
2243 0 : elantech->p1 = (data & 0x10) >> 4;
2244 0 : elantech->p2 = (data & 0x20) >> 5;
2245 : }
2246 0 : elantech->p3 = (data & 0x04) >> 2;
2247 0 : return (0);
2248 : case 1:
2249 0 : p = elantech->p1;
2250 0 : break;
2251 : case 2:
2252 0 : p = elantech->p2;
2253 0 : break;
2254 : case 3:
2255 0 : p = elantech->p3;
2256 0 : break;
2257 : default:
2258 0 : return (-1);
2259 : }
2260 :
2261 0 : if (data < 0 || data >= nitems(elantech->parity) ||
2262 0 : elantech->parity[data] != p)
2263 0 : return (-1);
2264 :
2265 0 : return (0);
2266 0 : }
2267 :
2268 : int
2269 0 : pms_sync_elantech_v2(struct pms_softc *sc, int data)
2270 : {
2271 0 : struct elantech_softc *elantech = sc->elantech;
2272 :
2273 : /* Variants reporting pressure always have the same constant bits. */
2274 0 : if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE) {
2275 0 : if (sc->inputstate == 0 && (data & 0x0c) != 0x04)
2276 0 : return (-1);
2277 0 : if (sc->inputstate == 3 && (data & 0x0f) != 0x02)
2278 0 : return (-1);
2279 0 : return (0);
2280 : }
2281 :
2282 : /* For variants not reporting pressure, 1 and 3 finger touch packets
2283 : * have different constant bits than 2 finger touch packets. */
2284 0 : switch (sc->inputstate) {
2285 : case 0:
2286 0 : if ((data & 0xc0) == 0x80) {
2287 0 : if ((data & 0x0c) != 0x0c)
2288 0 : return (-1);
2289 0 : elantech->flags |= ELANTECH_F_2FINGER_PACKET;
2290 0 : } else {
2291 0 : if ((data & 0x3c) != 0x3c)
2292 0 : return (-1);
2293 0 : elantech->flags &= ~ELANTECH_F_2FINGER_PACKET;
2294 : }
2295 : break;
2296 : case 1:
2297 : case 4:
2298 0 : if (elantech->flags & ELANTECH_F_2FINGER_PACKET)
2299 : break;
2300 0 : if ((data & 0xf0) != 0x00)
2301 0 : return (-1);
2302 : break;
2303 : case 3:
2304 0 : if (elantech->flags & ELANTECH_F_2FINGER_PACKET) {
2305 0 : if ((data & 0x0e) != 0x08)
2306 0 : return (-1);
2307 : } else {
2308 0 : if ((data & 0x3e) != 0x38)
2309 0 : return (-1);
2310 : }
2311 : break;
2312 : default:
2313 : break;
2314 : }
2315 :
2316 0 : return (0);
2317 0 : }
2318 :
2319 : int
2320 0 : pms_sync_elantech_v3(struct pms_softc *sc, int data)
2321 : {
2322 0 : struct elantech_softc *elantech = sc->elantech;
2323 :
2324 0 : switch (sc->inputstate) {
2325 : case 0:
2326 0 : if (elantech->flags & ELANTECH_F_CRC_ENABLED)
2327 : break;
2328 0 : if ((data & 0x0c) != 0x04 && (data & 0x0c) != 0x0c)
2329 0 : return (-1);
2330 : break;
2331 : case 3:
2332 0 : if (elantech->flags & ELANTECH_F_CRC_ENABLED) {
2333 0 : if ((data & 0x09) != 0x08 && (data & 0x09) != 0x09)
2334 0 : return (-1);
2335 : } else {
2336 0 : if ((data & 0xcf) != 0x02 && (data & 0xce) != 0x0c)
2337 0 : return (-1);
2338 : }
2339 : break;
2340 : }
2341 :
2342 0 : return (0);
2343 0 : }
2344 :
2345 : /* Extract the type bits from packet[3]. */
2346 : static inline int
2347 0 : elantech_packet_type(u_char b)
2348 : {
2349 0 : return ((b & 4) ? (b & 0xcf) : (b & 0x1f));
2350 : }
2351 :
2352 : int
2353 0 : pms_sync_elantech_v4(struct pms_softc *sc, int data)
2354 : {
2355 0 : if (sc->inputstate == 0) {
2356 0 : if ((data & 0x0c) == 0x04)
2357 0 : return (0);
2358 0 : if ((sc->elantech->flags & ELANTECH_F_TRACKPOINT)
2359 0 : && (data & 0xc8) == 0)
2360 0 : return (0);
2361 0 : return (-1);
2362 : }
2363 0 : if (sc->inputstate == 3) {
2364 0 : switch (elantech_packet_type(data)) {
2365 : case ELANTECH_V4_PKT_STATUS:
2366 : case ELANTECH_V4_PKT_HEAD:
2367 : case ELANTECH_V4_PKT_MOTION:
2368 0 : return ((sc->packet[0] & 4) ? 0 : -1);
2369 : case ELANTECH_PKT_TRACKPOINT:
2370 0 : return ((sc->packet[0] & 0xc8) == 0
2371 0 : && sc->packet[1] == ((data & 0x10) << 3)
2372 0 : && sc->packet[2] == ((data & 0x20) << 2)
2373 0 : && (data ^ (sc->packet[0] & 0x30)) == 0x36
2374 : ? 0 : -1);
2375 : }
2376 0 : return (-1);
2377 : }
2378 0 : return (0);
2379 0 : }
2380 :
2381 : void
2382 0 : pms_proc_elantech_v1(struct pms_softc *sc)
2383 : {
2384 0 : struct elantech_softc *elantech = sc->elantech;
2385 : int x, y, w, z;
2386 : u_int buttons;
2387 :
2388 0 : buttons = butmap[sc->packet[0] & 3];
2389 :
2390 0 : if (elantech->flags & ELANTECH_F_HAS_ROCKER) {
2391 0 : if (sc->packet[0] & 0x40) /* up */
2392 0 : buttons |= WSMOUSE_BUTTON(4);
2393 0 : if (sc->packet[0] & 0x80) /* down */
2394 0 : buttons |= WSMOUSE_BUTTON(5);
2395 : }
2396 :
2397 0 : if (elantech->flags & ELANTECH_F_HW_V1_OLD)
2398 0 : w = ((sc->packet[1] & 0x80) >> 7) +
2399 0 : ((sc->packet[1] & 0x30) >> 4);
2400 : else
2401 0 : w = (sc->packet[0] & 0xc0) >> 6;
2402 :
2403 : /* Hardware version 1 doesn't report pressure. */
2404 0 : if (w) {
2405 0 : x = ((sc->packet[1] & 0x0c) << 6) | sc->packet[2];
2406 0 : y = ((sc->packet[1] & 0x03) << 8) | sc->packet[3];
2407 : z = SYNAPTICS_PRESSURE;
2408 0 : } else {
2409 0 : x = elantech->old_x;
2410 0 : y = elantech->old_y;
2411 : z = 0;
2412 : }
2413 :
2414 0 : WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w);
2415 0 : }
2416 :
2417 : void
2418 0 : pms_proc_elantech_v2(struct pms_softc *sc)
2419 : {
2420 : const u_char debounce_pkt[] = { 0x84, 0xff, 0xff, 0x02, 0xff, 0xff };
2421 0 : struct elantech_softc *elantech = sc->elantech;
2422 : int x, y, w, z;
2423 : u_int buttons;
2424 :
2425 : /*
2426 : * The hardware sends this packet when in debounce state.
2427 : * The packet should be ignored.
2428 : */
2429 0 : if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt)))
2430 0 : return;
2431 :
2432 0 : buttons = butmap[sc->packet[0] & 3];
2433 :
2434 0 : w = (sc->packet[0] & 0xc0) >> 6;
2435 0 : if (w == 1 || w == 3) {
2436 0 : x = ((sc->packet[1] & 0x0f) << 8) | sc->packet[2];
2437 0 : y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5];
2438 0 : if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE)
2439 0 : z = ((sc->packet[1] & 0xf0) |
2440 0 : (sc->packet[4] & 0xf0) >> 4);
2441 : else
2442 : z = SYNAPTICS_PRESSURE;
2443 0 : } else if (w == 2) {
2444 0 : x = (((sc->packet[0] & 0x10) << 4) | sc->packet[1]) << 2;
2445 0 : y = (((sc->packet[0] & 0x20) << 3) | sc->packet[2]) << 2;
2446 : z = SYNAPTICS_PRESSURE;
2447 0 : } else {
2448 0 : x = elantech->old_x;
2449 0 : y = elantech->old_y;
2450 : z = 0;
2451 : }
2452 :
2453 0 : WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w);
2454 0 : }
2455 :
2456 : void
2457 0 : pms_proc_elantech_v3(struct pms_softc *sc)
2458 : {
2459 : const u_char debounce_pkt[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff };
2460 0 : struct elantech_softc *elantech = sc->elantech;
2461 : int x, y, w, z;
2462 : u_int buttons;
2463 :
2464 0 : buttons = butmap[sc->packet[0] & 3];
2465 :
2466 0 : x = ((sc->packet[1] & 0x0f) << 8 | sc->packet[2]);
2467 0 : y = ((sc->packet[4] & 0x0f) << 8 | sc->packet[5]);
2468 : z = 0;
2469 0 : w = (sc->packet[0] & 0xc0) >> 6;
2470 0 : if (w == 2) {
2471 : /*
2472 : * Two-finger touch causes two packets -- a head packet
2473 : * and a tail packet. We report a single event and ignore
2474 : * the tail packet.
2475 : */
2476 0 : if (elantech->flags & ELANTECH_F_CRC_ENABLED) {
2477 0 : if ((sc->packet[3] & 0x09) != 0x08)
2478 0 : return;
2479 : } else {
2480 : /* The hardware sends this packet when in debounce state.
2481 : * The packet should be ignored. */
2482 0 : if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt)))
2483 0 : return;
2484 0 : if ((sc->packet[0] & 0x0c) != 0x04 &&
2485 0 : (sc->packet[3] & 0xcf) != 0x02) {
2486 : /* not the head packet -- ignore */
2487 0 : return;
2488 : }
2489 : }
2490 : }
2491 :
2492 : /* Prevent jumping cursor if pad isn't touched or reports garbage. */
2493 0 : if (w == 0 ||
2494 0 : ((x == 0 || y == 0 || x == elantech->max_x || y == elantech->max_y)
2495 0 : && (x != elantech->old_x || y != elantech->old_y))) {
2496 0 : x = elantech->old_x;
2497 0 : y = elantech->old_y;
2498 0 : }
2499 :
2500 0 : if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE)
2501 0 : z = (sc->packet[1] & 0xf0) | ((sc->packet[4] & 0xf0) >> 4);
2502 0 : else if (w)
2503 0 : z = SYNAPTICS_PRESSURE;
2504 :
2505 0 : WSMOUSE_TOUCH(sc->sc_wsmousedev, buttons, x, y, z, w);
2506 0 : elantech->old_x = x;
2507 0 : elantech->old_y = y;
2508 0 : }
2509 :
2510 : void
2511 0 : pms_proc_elantech_v4(struct pms_softc *sc)
2512 : {
2513 0 : struct elantech_softc *elantech = sc->elantech;
2514 0 : struct device *sc_wsmousedev = sc->sc_wsmousedev;
2515 : int id, weight, n, x, y, z;
2516 : u_int buttons, slots;
2517 :
2518 0 : switch (elantech_packet_type(sc->packet[3])) {
2519 : case ELANTECH_V4_PKT_STATUS:
2520 0 : slots = elantech->mt_slots;
2521 0 : elantech->mt_slots = sc->packet[1] & 0x1f;
2522 0 : slots &= ~elantech->mt_slots;
2523 0 : for (id = 0; slots; id++, slots >>= 1) {
2524 0 : if (slots & 1)
2525 0 : wsmouse_mtstate(sc_wsmousedev, id, 0, 0, 0);
2526 : }
2527 : break;
2528 :
2529 : case ELANTECH_V4_PKT_HEAD:
2530 0 : id = ((sc->packet[3] & 0xe0) >> 5) - 1;
2531 0 : if (id > -1 && id < ELANTECH_MAX_FINGERS) {
2532 0 : x = ((sc->packet[1] & 0x0f) << 8) | sc->packet[2];
2533 0 : y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5];
2534 0 : z = (sc->packet[1] & 0xf0)
2535 0 : | ((sc->packet[4] & 0xf0) >> 4);
2536 0 : wsmouse_mtstate(sc_wsmousedev, id, x, y, z);
2537 0 : }
2538 : break;
2539 :
2540 : case ELANTECH_V4_PKT_MOTION:
2541 0 : weight = (sc->packet[0] & 0x10) ? ELANTECH_V4_WEIGHT_VALUE : 1;
2542 0 : for (n = 0; n < 6; n += 3) {
2543 0 : id = ((sc->packet[n] & 0xe0) >> 5) - 1;
2544 0 : if (id < 0 || id >= ELANTECH_MAX_FINGERS)
2545 : continue;
2546 0 : x = weight * (signed char)sc->packet[n + 1];
2547 0 : y = weight * (signed char)sc->packet[n + 2];
2548 : z = WSMOUSE_DEFAULT_PRESSURE;
2549 0 : wsmouse_set(sc_wsmousedev, WSMOUSE_MT_REL_X, x, id);
2550 0 : wsmouse_set(sc_wsmousedev, WSMOUSE_MT_REL_Y, y, id);
2551 0 : wsmouse_set(sc_wsmousedev, WSMOUSE_MT_PRESSURE, z, id);
2552 0 : }
2553 : break;
2554 :
2555 : case ELANTECH_PKT_TRACKPOINT:
2556 0 : if (sc->sc_dev_enable & PMS_DEV_SECONDARY) {
2557 0 : x = sc->packet[4] - 0x100 + (sc->packet[1] << 1);
2558 0 : y = sc->packet[5] - 0x100 + (sc->packet[2] << 1);
2559 0 : buttons = butmap[sc->packet[0] & 7];
2560 0 : WSMOUSE_INPUT(sc->sc_sec_wsmousedev,
2561 : buttons, x, y, 0, 0);
2562 0 : }
2563 0 : return;
2564 :
2565 : default:
2566 0 : printf("%s: unknown packet type 0x%x\n", DEVNAME(sc),
2567 0 : sc->packet[3] & 0x1f);
2568 0 : return;
2569 : }
2570 :
2571 0 : buttons = butmap[sc->packet[0] & 3];
2572 0 : wsmouse_buttons(sc_wsmousedev, buttons);
2573 :
2574 0 : wsmouse_input_sync(sc_wsmousedev);
2575 0 : }
|