pms.c revision 1.32
1/* $OpenBSD: pms.c,v 1.32 2012/10/29 11:54:45 stsp 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/device.h>
30#include <sys/ioctl.h>
31#include <sys/malloc.h>
32
33#include <machine/bus.h>
34
35#include <dev/ic/pckbcvar.h>
36
37#include <dev/pckbc/pmsreg.h>
38
39#include <dev/wscons/wsconsio.h>
40#include <dev/wscons/wsmousevar.h>
41
42#ifdef DEBUG
43#define DPRINTF(x...)	do { printf(x); } while (0);
44#else
45#define DPRINTF(x...)
46#endif
47
48#define DEVNAME(sc)	((sc)->sc_dev.dv_xname)
49
50#define WSMOUSE_BUTTON(x)	(1 << ((x) - 1))
51
52struct pms_softc;
53
54struct pms_protocol {
55	int type;
56#define PMS_STANDARD		0
57#define PMS_INTELLI		1
58#define PMS_SYNAPTICS		2
59#define PMS_ALPS		3
60#define PMS_ELANTECH_V1		4
61#define PMS_ELANTECH_V2		5
62#define PMS_ELANTECH_V3		6
63	u_int packetsize;
64	int (*enable)(struct pms_softc *);
65	int (*ioctl)(struct pms_softc *, u_long, caddr_t, int, struct proc *);
66	int (*sync)(struct pms_softc *, int);
67	void (*proc)(struct pms_softc *);
68	void (*disable)(struct pms_softc *);
69};
70
71struct synaptics_softc {
72	int identify;
73	int capabilities, ext_capabilities;
74	int model, ext_model;
75	int resolution, dimension;
76
77	int mode;
78
79	int res_x, res_y;
80	int min_x, min_y;
81	int max_x, max_y;
82
83	/* Compat mode */
84	int wsmode;
85	int old_x, old_y;
86	u_int old_buttons;
87#define SYNAPTICS_SCALE		4
88#define SYNAPTICS_PRESSURE	30
89};
90
91struct alps_softc {
92	int model;
93#define ALPS_GLIDEPOINT		(1 << 1)
94#define ALPS_DUALPOINT		(1 << 2)
95#define ALPS_PASSTHROUGH	(1 << 3)
96#define ALPS_INTERLEAVED	(1 << 4)
97
98	int mask;
99	int version;
100
101	int min_x, min_y;
102	int max_x, max_y;
103	int old_fin;
104
105	u_int sec_buttons;	/* trackpoint */
106
107	/* Compat mode */
108	int wsmode;
109	int old_x, old_y;
110	u_int old_buttons;
111#define ALPS_PRESSURE		40
112};
113
114struct elantech_softc {
115	int flags;
116#define ELANTECH_F_REPORTS_PRESSURE	0x01
117#define ELANTECH_F_HAS_ROCKER		0x02
118#define ELANTECH_F_2FINGER_PACKET	0x04
119#define ELANTECH_F_HW_V1_OLD		0x08
120
121	int min_x, min_y;
122	int max_x, max_y;
123
124	u_char parity[256];
125	u_char p1, p2, p3;
126
127	/* Compat mode */
128	int wsmode;
129	int old_x, old_y;
130	u_int old_buttons;
131};
132
133struct pms_softc {		/* driver status information */
134	struct device sc_dev;
135
136	pckbc_tag_t sc_kbctag;
137
138	int sc_state;
139#define PMS_STATE_DISABLED	0
140#define PMS_STATE_ENABLED	1
141#define PMS_STATE_SUSPENDED	2
142
143	int sc_dev_enable;
144#define PMS_DEV_IGNORE		0x00
145#define PMS_DEV_PRIMARY		0x01
146#define PMS_DEV_SECONDARY	0x02
147
148	int poll;
149	int inputstate;
150
151	const struct pms_protocol *protocol;
152	struct synaptics_softc *synaptics;
153	struct alps_softc *alps;
154	struct elantech_softc *elantech;
155
156	u_char packet[8];
157
158	struct device *sc_wsmousedev;
159	struct device *sc_sec_wsmousedev;
160};
161
162static const u_int butmap[8] = {
163	0,
164	WSMOUSE_BUTTON(1),
165	WSMOUSE_BUTTON(3),
166	WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(3),
167	WSMOUSE_BUTTON(2),
168	WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2),
169	WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3),
170	WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3)
171};
172
173static const struct alps_model {
174	int version;
175	int mask;
176	int model;
177} alps_models[] = {
178	{ 0x2021, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
179	{ 0x2221, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
180	{ 0x2222, 0xff, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
181	{ 0x3222, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
182	{ 0x5212, 0xff, ALPS_DUALPOINT | ALPS_PASSTHROUGH | ALPS_INTERLEAVED },
183	{ 0x5321, 0xf8, ALPS_GLIDEPOINT },
184	{ 0x5322, 0xf8, ALPS_GLIDEPOINT },
185	{ 0x603b, 0xf8, ALPS_GLIDEPOINT },
186	{ 0x6222, 0xcf, ALPS_DUALPOINT | ALPS_PASSTHROUGH | ALPS_INTERLEAVED },
187	{ 0x6321, 0xf8, ALPS_GLIDEPOINT },
188	{ 0x6322, 0xf8, ALPS_GLIDEPOINT },
189	{ 0x6323, 0xf8, ALPS_GLIDEPOINT },
190	{ 0x6324, 0x8f, ALPS_GLIDEPOINT },
191	{ 0x6325, 0xef, ALPS_GLIDEPOINT },
192	{ 0x6326, 0xf8, ALPS_GLIDEPOINT },
193	{ 0x7301, 0xf8, ALPS_DUALPOINT },
194	{ 0x7321, 0xf8, ALPS_GLIDEPOINT },
195	{ 0x7322, 0xf8, ALPS_GLIDEPOINT },
196	{ 0x7325, 0xcf, ALPS_GLIDEPOINT },
197#if 0
198	/*
199	 * This model has a clitpad sending almost compatible PS2
200	 * packets but not compatible enough to be used with the
201	 * ALPS protocol.
202	 */
203	{ 0x633b, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
204
205	{ 0x7326, 0, 0 },	/* XXX Uses unknown v3 protocol */
206#endif
207};
208
209int	pmsprobe(struct device *, void *, void *);
210void	pmsattach(struct device *, struct device *, void *);
211int	pmsactivate(struct device *, int);
212
213void	pmsinput(void *, int);
214
215int	pms_change_state(struct pms_softc *, int, int);
216
217int	pms_ioctl(void *, u_long, caddr_t, int, struct proc *);
218int	pms_enable(void *);
219void	pms_disable(void *);
220
221int	pms_sec_ioctl(void *, u_long, caddr_t, int, struct proc *);
222int	pms_sec_enable(void *);
223void	pms_sec_disable(void *);
224
225int	pms_cmd(struct pms_softc *, u_char *, int, u_char *, int);
226int	pms_spec_cmd(struct pms_softc *, int);
227int	pms_get_devid(struct pms_softc *, u_char *);
228int	pms_get_status(struct pms_softc *, u_char *);
229int	pms_set_rate(struct pms_softc *, int);
230int	pms_set_resolution(struct pms_softc *, int);
231int	pms_set_scaling(struct pms_softc *, int);
232int	pms_reset(struct pms_softc *);
233int	pms_dev_enable(struct pms_softc *);
234int	pms_dev_disable(struct pms_softc *);
235
236int	pms_enable_intelli(struct pms_softc *);
237
238int	pms_ioctl_mouse(struct pms_softc *, u_long, caddr_t, int, struct proc *);
239int	pms_sync_mouse(struct pms_softc *, int);
240void	pms_proc_mouse(struct pms_softc *);
241
242int	pms_enable_synaptics(struct pms_softc *);
243int	pms_ioctl_synaptics(struct pms_softc *, u_long, caddr_t, int, struct proc *);
244int	pms_sync_synaptics(struct pms_softc *, int);
245void	pms_proc_synaptics(struct pms_softc *);
246void	pms_disable_synaptics(struct pms_softc *);
247
248int	pms_enable_alps(struct pms_softc *);
249int	pms_ioctl_alps(struct pms_softc *, u_long, caddr_t, int, struct proc *);
250int	pms_sync_alps(struct pms_softc *, int);
251void	pms_proc_alps(struct pms_softc *);
252
253int	pms_enable_elantech_v1(struct pms_softc *);
254int	pms_enable_elantech_v2(struct pms_softc *);
255int	pms_enable_elantech_v3(struct pms_softc *);
256int	pms_ioctl_elantech(struct pms_softc *, u_long, caddr_t, int,
257    struct proc *);
258int	pms_sync_elantech_v1(struct pms_softc *, int);
259int	pms_sync_elantech_v2(struct pms_softc *, int);
260int	pms_sync_elantech_v3(struct pms_softc *, int);
261void	pms_proc_elantech_v1(struct pms_softc *);
262void	pms_proc_elantech_v2(struct pms_softc *);
263void	pms_proc_elantech_v3(struct pms_softc *);
264
265int	synaptics_set_mode(struct pms_softc *, int);
266int	synaptics_query(struct pms_softc *, int, int *);
267int	synaptics_get_hwinfo(struct pms_softc *);
268void	synaptics_sec_proc(struct pms_softc *);
269
270int	alps_sec_proc(struct pms_softc *);
271int	alps_get_hwinfo(struct pms_softc *);
272
273int	elantech_knock(struct pms_softc *);
274void	elantech_send_input(struct pms_softc *, u_int, int, int, int, int);
275int	elantech_get_hwinfo_v1(struct pms_softc *);
276int	elantech_get_hwinfo_v2(struct pms_softc *);
277int	elantech_get_hwinfo_v3(struct pms_softc *);
278int	elantech_ps2_cmd(struct pms_softc *, u_char);
279int	elantech_set_absolute_mode_v1(struct pms_softc *);
280int	elantech_set_absolute_mode_v2(struct pms_softc *);
281int	elantech_set_absolute_mode_v3(struct pms_softc *);
282
283
284struct cfattach pms_ca = {
285	sizeof(struct pms_softc), pmsprobe, pmsattach, NULL,
286	pmsactivate
287};
288
289struct cfdriver pms_cd = {
290	NULL, "pms", DV_DULL
291};
292
293const struct wsmouse_accessops pms_accessops = {
294	pms_enable,
295	pms_ioctl,
296	pms_disable,
297};
298
299const struct wsmouse_accessops pms_sec_accessops = {
300	pms_sec_enable,
301	pms_sec_ioctl,
302	pms_sec_disable,
303};
304
305const struct pms_protocol pms_protocols[] = {
306	/* Generic PS/2 mouse */
307	{
308		PMS_STANDARD, 3,
309		NULL,
310		pms_ioctl_mouse,
311		pms_sync_mouse,
312		pms_proc_mouse,
313		NULL
314	},
315	/* Microsoft IntelliMouse */
316	{
317		PMS_INTELLI, 4,
318		pms_enable_intelli,
319		pms_ioctl_mouse,
320		pms_sync_mouse,
321		pms_proc_mouse,
322		NULL
323	},
324	/* Synaptics touchpad */
325	{
326		PMS_SYNAPTICS, 6,
327		pms_enable_synaptics,
328		pms_ioctl_synaptics,
329		pms_sync_synaptics,
330		pms_proc_synaptics,
331		pms_disable_synaptics
332	},
333	/* ALPS touchpad */
334	{
335		PMS_ALPS, 6,
336		pms_enable_alps,
337		pms_ioctl_alps,
338		pms_sync_alps,
339		pms_proc_alps,
340		NULL
341	},
342#ifdef notyet
343	/* Elantech touchpad (hardware version 1) */
344	{
345		PMS_ELANTECH_V1, 4,
346		pms_enable_elantech_v1,
347		pms_ioctl_elantech,
348		pms_sync_elantech_v1,
349		pms_proc_elantech_v1,
350		NULL
351	},
352	/* Elantech touchpad (hardware version 2) */
353	{
354		PMS_ELANTECH_V2, 6,
355		pms_enable_elantech_v2,
356		pms_ioctl_elantech,
357		pms_sync_elantech_v2,
358		pms_proc_elantech_v2,
359		NULL
360	},
361#endif
362	/* Elantech touchpad (hardware version 3) */
363	{
364		PMS_ELANTECH_V3, 6,
365		pms_enable_elantech_v3,
366		pms_ioctl_elantech,
367		pms_sync_elantech_v3,
368		pms_proc_elantech_v3,
369		NULL
370	},
371};
372
373int
374pms_cmd(struct pms_softc *sc, u_char *cmd, int len, u_char *resp, int resplen)
375{
376	if (sc->poll) {
377		return pckbc_poll_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT,
378		    cmd, len, resplen, resp, 1);
379	} else {
380		return pckbc_enqueue_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT,
381		    cmd, len, resplen, 1, resp);
382	}
383}
384
385int
386pms_spec_cmd(struct pms_softc *sc, int cmd)
387{
388	if (pms_set_scaling(sc, 1) ||
389	    pms_set_resolution(sc, (cmd >> 6) & 0x03) ||
390	    pms_set_resolution(sc, (cmd >> 4) & 0x03) ||
391	    pms_set_resolution(sc, (cmd >> 2) & 0x03) ||
392	    pms_set_resolution(sc, (cmd >> 0) & 0x03))
393		return (-1);
394	return (0);
395}
396
397int
398pms_get_devid(struct pms_softc *sc, u_char *resp)
399{
400	u_char cmd[1];
401
402	cmd[0] = PMS_SEND_DEV_ID;
403	return (pms_cmd(sc, cmd, 1, resp, 1));
404}
405
406int
407pms_get_status(struct pms_softc *sc, u_char *resp)
408{
409	u_char cmd[1];
410
411	cmd[0] = PMS_SEND_DEV_STATUS;
412	return (pms_cmd(sc, cmd, 1, resp, 3));
413}
414
415int
416pms_set_rate(struct pms_softc *sc, int value)
417{
418	u_char cmd[2];
419
420	cmd[0] = PMS_SET_SAMPLE;
421	cmd[1] = value;
422	return (pms_cmd(sc, cmd, 2, NULL, 0));
423}
424
425int
426pms_set_resolution(struct pms_softc *sc, int value)
427{
428	u_char cmd[2];
429
430	cmd[0] = PMS_SET_RES;
431	cmd[1] = value;
432	return (pms_cmd(sc, cmd, 2, NULL, 0));
433}
434
435int
436pms_set_scaling(struct pms_softc *sc, int scale)
437{
438	u_char cmd[1];
439
440	switch (scale) {
441	case 1:
442	default:
443		cmd[0] = PMS_SET_SCALE11;
444		break;
445	case 2:
446		cmd[0] = PMS_SET_SCALE21;
447		break;
448	}
449	return (pms_cmd(sc, cmd, 1, NULL, 0));
450}
451
452int
453pms_reset(struct pms_softc *sc)
454{
455	u_char cmd[1], resp[2];
456	int res;
457
458	cmd[0] = PMS_RESET;
459	res = pms_cmd(sc, cmd, 1, resp, 2);
460#ifdef DEBUG
461	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0)
462		printf("%s: reset error %d (response 0x%02x, type 0x%02x)\n",
463		    DEVNAME(sc), res, resp[0], resp[1]);
464#endif
465	return (res);
466}
467
468int
469pms_dev_enable(struct pms_softc *sc)
470{
471	u_char cmd[1];
472	int res;
473
474	cmd[0] = PMS_DEV_ENABLE;
475	res = pms_cmd(sc, cmd, 1, NULL, 0);
476	if (res)
477		printf("%s: enable error\n", DEVNAME(sc));
478	return (res);
479}
480
481int
482pms_dev_disable(struct pms_softc *sc)
483{
484	u_char cmd[1];
485	int res;
486
487	cmd[0] = PMS_DEV_DISABLE;
488	res = pms_cmd(sc, cmd, 1, NULL, 0);
489	if (res)
490		printf("%s: disable error\n", DEVNAME(sc));
491	return (res);
492}
493
494int
495pms_enable_intelli(struct pms_softc *sc)
496{
497	u_char resp;
498
499	/* the special sequence to enable the third button and the roller */
500	if (pms_set_rate(sc, PMS_INTELLI_MAGIC1) ||
501	    pms_set_rate(sc, PMS_INTELLI_MAGIC2) ||
502	    pms_set_rate(sc, PMS_INTELLI_MAGIC3) ||
503	    pms_get_devid(sc, &resp) ||
504	    resp != PMS_INTELLI_ID)
505		return (0);
506
507	return (1);
508}
509
510int
511pms_ioctl_mouse(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
512    struct proc *p)
513{
514	int i;
515
516	switch (cmd) {
517	case WSMOUSEIO_GTYPE:
518		*(u_int *)data = WSMOUSE_TYPE_PS2;
519		break;
520	case WSMOUSEIO_SRES:
521		i = ((int) *(u_int *)data - 12) / 25;
522		/* valid values are {0,1,2,3} */
523		if (i < 0)
524			i = 0;
525		if (i > 3)
526			i = 3;
527
528		if (pms_set_resolution(sc, i))
529			printf("%s: SET_RES command error\n", DEVNAME(sc));
530		break;
531	default:
532		return (-1);
533	}
534	return (0);
535}
536
537int
538pms_sync_mouse(struct pms_softc *sc, int data)
539{
540	if (sc->inputstate != 0)
541		return (0);
542
543	switch (sc->protocol->type) {
544	case PMS_STANDARD:
545		if ((data & 0xc0) != 0)
546			return (-1);
547		break;
548	case PMS_INTELLI:
549		if ((data & 0x08) != 0x08)
550			return (-1);
551		break;
552	}
553
554	return (0);
555}
556
557void
558pms_proc_mouse(struct pms_softc *sc)
559{
560	u_int buttons;
561	int  dx, dy, dz;
562
563	buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK];
564	dx = (sc->packet[0] & PMS_PS2_XNEG) ?
565	    (int)sc->packet[1] - 256 : sc->packet[1];
566	dy = (sc->packet[0] & PMS_PS2_YNEG) ?
567	    (int)sc->packet[2] - 256 : sc->packet[2];
568
569	switch (sc->protocol->type) {
570	case PMS_STANDARD:
571		dz = 0;
572		break;
573	case PMS_INTELLI:
574		dz = (signed char)sc->packet[3];
575		break;
576	}
577
578	wsmouse_input(sc->sc_wsmousedev,
579	    buttons, dx, dy, dz, 0, WSMOUSE_INPUT_DELTA);
580}
581
582int
583pmsprobe(struct device *parent, void *match, void *aux)
584{
585	struct pckbc_attach_args *pa = aux;
586	u_char cmd[1], resp[2];
587	int res;
588
589	if (pa->pa_slot != PCKBC_AUX_SLOT)
590		return (0);
591
592	/* Flush any garbage. */
593	pckbc_flush(pa->pa_tag, pa->pa_slot);
594
595	/* reset the device */
596	cmd[0] = PMS_RESET;
597	res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
598	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
599#ifdef DEBUG
600		printf("pms: reset error %d (response 0x%02x, type 0x%02x)\n",
601		    res, resp[0], resp[1]);
602#endif
603		return (0);
604	}
605
606	return (1);
607}
608
609void
610pmsattach(struct device *parent, struct device *self, void *aux)
611{
612	struct pms_softc *sc = (void *)self;
613	struct pckbc_attach_args *pa = aux;
614	struct wsmousedev_attach_args a;
615	int i;
616
617	sc->sc_kbctag = pa->pa_tag;
618
619	printf("\n");
620
621	pckbc_set_inputhandler(sc->sc_kbctag, PCKBC_AUX_SLOT,
622	    pmsinput, sc, DEVNAME(sc));
623
624	a.accessops = &pms_accessops;
625	a.accesscookie = sc;
626
627	/*
628	 * Attach the wsmouse, saving a handle to it.
629	 * Note that we don't need to check this pointer against NULL
630	 * here or in pmsintr, because if this fails pms_enable() will
631	 * never be called, so pmsinput() will never be called.
632	 */
633	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
634
635	sc->poll = 1;
636	sc->sc_dev_enable = 0;
637
638	sc->protocol = &pms_protocols[0];
639	for (i = 1; i < nitems(pms_protocols); i++) {
640		pms_reset(sc);
641		if (pms_protocols[i].enable(sc))
642			sc->protocol = &pms_protocols[i];
643	}
644
645	DPRINTF("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type);
646
647	/* no interrupts until enabled */
648	pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_IGNORE);
649}
650
651int
652pmsactivate(struct device *self, int act)
653{
654	struct pms_softc *sc = (struct pms_softc *)self;
655
656	switch (act) {
657	case DVACT_SUSPEND:
658		if (sc->sc_state == PMS_STATE_ENABLED)
659			pms_change_state(sc, PMS_STATE_SUSPENDED,
660			    PMS_DEV_IGNORE);
661		break;
662	case DVACT_RESUME:
663		if (sc->sc_state == PMS_STATE_SUSPENDED)
664			pms_change_state(sc, PMS_STATE_ENABLED,
665			    PMS_DEV_IGNORE);
666		break;
667	}
668	return (0);
669}
670
671int
672pms_change_state(struct pms_softc *sc, int newstate, int dev)
673{
674	int i;
675
676	if (dev != PMS_DEV_IGNORE) {
677		switch (newstate) {
678		case PMS_STATE_ENABLED:
679			if (sc->sc_dev_enable & dev)
680				return (EBUSY);
681
682			sc->sc_dev_enable |= dev;
683
684			if (sc->sc_state == PMS_STATE_ENABLED)
685				return (0);
686
687			break;
688		case PMS_STATE_DISABLED:
689			sc->sc_dev_enable &= ~dev;
690
691			if (sc->sc_dev_enable)
692				return (0);
693
694			break;
695		}
696	}
697
698	switch (newstate) {
699	case PMS_STATE_ENABLED:
700		sc->inputstate = 0;
701
702		pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 1);
703
704		if (sc->poll)
705			pckbc_flush(sc->sc_kbctag, PCKBC_AUX_SLOT);
706
707		pms_reset(sc);
708
709		if (sc->protocol->type != PMS_STANDARD &&
710		    sc->protocol->enable(sc) == 0)
711			sc->protocol = &pms_protocols[0];
712
713		if (sc->protocol->type == PMS_STANDARD)
714			for (i = 1; i < nitems(pms_protocols); i++) {
715				pms_reset(sc);
716				if (pms_protocols[i].enable(sc))
717					sc->protocol = &pms_protocols[i];
718			}
719
720#ifdef DEBUG
721		printf("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type);
722#endif
723
724		pms_dev_enable(sc);
725		break;
726	case PMS_STATE_DISABLED:
727	case PMS_STATE_SUSPENDED:
728		pms_dev_disable(sc);
729
730		if (sc->protocol && sc->protocol->disable)
731			sc->protocol->disable(sc);
732
733		pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 0);
734		break;
735	}
736
737	sc->sc_state = newstate;
738	sc->poll = (newstate == PMS_STATE_SUSPENDED) ? 1 : 0;
739
740	return (0);
741}
742
743int
744pms_enable(void *v)
745{
746	struct pms_softc *sc = v;
747
748	return pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_PRIMARY);
749}
750
751void
752pms_disable(void *v)
753{
754	struct pms_softc *sc = v;
755
756	pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_PRIMARY);
757}
758
759int
760pms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
761{
762	struct pms_softc *sc = v;
763
764	if (sc->protocol && sc->protocol->ioctl)
765		return (sc->protocol->ioctl(sc, cmd, data, flag, p));
766	else
767		return (-1);
768}
769
770int
771pms_sec_enable(void *v)
772{
773	struct pms_softc *sc = v;
774
775	return (pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_SECONDARY));
776}
777
778void
779pms_sec_disable(void *v)
780{
781	struct pms_softc *sc = v;
782
783	pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_SECONDARY);
784}
785
786int
787pms_sec_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
788{
789	switch (cmd) {
790	case WSMOUSEIO_GTYPE:
791		*(u_int *)data = WSMOUSE_TYPE_PS2;
792		break;
793	default:
794		return (-1);
795	}
796	return (0);
797}
798
799void
800pmsinput(void *vsc, int data)
801{
802	struct pms_softc *sc = vsc;
803
804	if (sc->sc_state != PMS_STATE_ENABLED) {
805		/* Interrupts are not expected.  Discard the byte. */
806		return;
807	}
808
809	sc->packet[sc->inputstate] = data;
810	if (sc->protocol->sync(sc, data)) {
811#ifdef DIAGNOSTIC
812		printf("%s: not in sync yet, discard input\n", DEVNAME(sc));
813#endif
814		sc->inputstate = 0;
815		return;
816	}
817
818	sc->inputstate++;
819
820	if (sc->inputstate != sc->protocol->packetsize)
821		return;
822
823	sc->inputstate = 0;
824	sc->protocol->proc(sc);
825}
826
827int
828synaptics_set_mode(struct pms_softc *sc, int mode)
829{
830	struct synaptics_softc *syn = sc->synaptics;
831
832	if (pms_spec_cmd(sc, mode) ||
833	    pms_set_rate(sc, SYNAPTICS_CMD_SET_MODE))
834		return (-1);
835
836	syn->mode = mode;
837
838	return (0);
839}
840
841int
842synaptics_query(struct pms_softc *sc, int query, int *val)
843{
844	u_char resp[3];
845
846	if (pms_spec_cmd(sc, query) ||
847	    pms_get_status(sc, resp))
848		return (-1);
849
850	if (val)
851		*val = (resp[0] << 16) | (resp[1] << 8) | resp[2];
852
853	return (0);
854}
855
856int
857synaptics_get_hwinfo(struct pms_softc *sc)
858{
859	struct synaptics_softc *syn = sc->synaptics;
860
861	if (synaptics_query(sc, SYNAPTICS_QUE_IDENTIFY, &syn->identify))
862		return (-1);
863	if (synaptics_query(sc, SYNAPTICS_QUE_CAPABILITIES,
864	    &syn->capabilities))
865		return (-1);
866	if (synaptics_query(sc, SYNAPTICS_QUE_MODEL, &syn->model))
867		return (-1);
868	if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 1) &&
869	    synaptics_query(sc, SYNAPTICS_QUE_EXT_MODEL, &syn->ext_model))
870		return (-1);
871	if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 4) &&
872	    synaptics_query(sc, SYNAPTICS_QUE_EXT_CAPABILITIES,
873		&syn->ext_capabilities))
874		return (-1);
875	if ((SYNAPTICS_ID_MAJOR(syn->identify) >= 4) &&
876	    synaptics_query(sc, SYNAPTICS_QUE_RESOLUTION, &syn->resolution))
877		return (-1);
878	if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 5) &&
879	    (syn->ext_capabilities & SYNAPTICS_EXT_CAP_MAX_DIMENSIONS) &&
880	    synaptics_query(sc, SYNAPTICS_QUE_EXT_DIMENSIONS, &syn->dimension))
881		return (-1);
882
883	syn->res_x = SYNAPTICS_RESOLUTION_X(syn->resolution);
884	syn->res_y = SYNAPTICS_RESOLUTION_Y(syn->resolution);
885	syn->min_x = SYNAPTICS_XMIN_BEZEL;
886	syn->min_y = SYNAPTICS_YMIN_BEZEL;
887	syn->max_x = (syn->dimension) ?
888	    SYNAPTICS_DIM_X(syn->dimension) : SYNAPTICS_XMAX_BEZEL;
889	syn->max_y = (syn->dimension) ?
890	    SYNAPTICS_DIM_Y(syn->dimension) : SYNAPTICS_YMAX_BEZEL;
891
892	if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) > 8)
893		syn->ext_model &= ~0xf000;
894
895	return (0);
896}
897
898void
899synaptics_sec_proc(struct pms_softc *sc)
900{
901	u_int buttons;
902	int dx, dy;
903
904	if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) == 0)
905		return;
906
907	buttons = butmap[sc->packet[1] & PMS_PS2_BUTTONSMASK];
908	dx = (sc->packet[1] & PMS_PS2_XNEG) ?
909	    (int)sc->packet[4] - 256 : sc->packet[4];
910	dy = (sc->packet[1] & PMS_PS2_YNEG) ?
911	    (int)sc->packet[5] - 256 : sc->packet[5];
912
913	wsmouse_input(sc->sc_sec_wsmousedev,
914	    buttons, dx, dy, 0, 0, WSMOUSE_INPUT_DELTA);
915}
916
917int
918pms_enable_synaptics(struct pms_softc *sc)
919{
920	struct synaptics_softc *syn = sc->synaptics;
921	struct wsmousedev_attach_args a;
922	u_char resp[3];
923	int mode;
924
925	if (pms_set_resolution(sc, 0) ||
926	    pms_set_resolution(sc, 0) ||
927	    pms_set_resolution(sc, 0) ||
928	    pms_set_resolution(sc, 0) ||
929	    pms_get_status(sc, resp) ||
930	    resp[1] != SYNAPTICS_ID_MAGIC)
931		return (0);
932
933	if (sc->synaptics == NULL) {
934		sc->synaptics = syn = malloc(sizeof(struct synaptics_softc),
935		    M_DEVBUF, M_WAITOK | M_ZERO);
936		if (syn == NULL) {
937			printf("%s: synaptics: not enough memory\n",
938			    DEVNAME(sc));
939			return (0);
940		}
941
942		if (synaptics_get_hwinfo(sc))
943			return (0);
944
945		if ((syn->model & SYNAPTICS_MODEL_NEWABS) == 0) {
946			printf("%s: don't support Synaptics OLDABS\n",
947			    DEVNAME(sc));
948			return (0);
949		}
950
951		/* enable pass-through PS/2 port if supported */
952		if (syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) {
953			a.accessops = &pms_sec_accessops;
954			a.accesscookie = sc;
955			sc->sc_sec_wsmousedev = config_found((void *)sc, &a,
956			    wsmousedevprint);
957		}
958
959		syn->wsmode = WSMOUSE_COMPAT;
960
961		printf("%s: Synaptics %s, firmware %d.%d\n", DEVNAME(sc),
962		    (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD ?
963			"clickpad" : "touchpad"),
964		    SYNAPTICS_ID_MAJOR(syn->identify),
965		    SYNAPTICS_ID_MINOR(syn->identify));
966	}
967
968	mode = SYNAPTICS_ABSOLUTE_MODE | SYNAPTICS_HIGH_RATE;
969	if (SYNAPTICS_ID_MAJOR(syn->identify) >= 4)
970		mode |= SYNAPTICS_DISABLE_GESTURE;
971	if (syn->capabilities & SYNAPTICS_CAP_EXTENDED)
972		mode |= SYNAPTICS_W_MODE;
973	if (synaptics_set_mode(sc, mode))
974		return (0);
975
976	/* enable advanced gesture mode if supported */
977	if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_ADV_GESTURE) &&
978	    (pms_spec_cmd(sc, SYNAPTICS_QUE_MODEL) ||
979	     pms_set_rate(sc, SYNAPTICS_CMD_SET_ADV_GESTURE_MODE)))
980		return (0);
981
982	return (1);
983}
984
985int
986pms_ioctl_synaptics(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
987    struct proc *p)
988{
989	struct synaptics_softc *syn = sc->synaptics;
990	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
991	int wsmode;
992
993	switch (cmd) {
994	case WSMOUSEIO_GTYPE:
995		*(u_int *)data = WSMOUSE_TYPE_SYNAPTICS;
996		break;
997	case WSMOUSEIO_GCALIBCOORDS:
998		wsmc->minx = syn->min_x;
999		wsmc->maxx = syn->max_x;
1000		wsmc->miny = syn->min_y;
1001		wsmc->maxy = syn->max_y;
1002		wsmc->swapxy = 0;
1003		wsmc->resx = syn->res_x;
1004		wsmc->resy = syn->res_y;
1005		break;
1006	case WSMOUSEIO_SETMODE:
1007		wsmode = *(u_int *)data;
1008		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
1009			return (EINVAL);
1010		syn->wsmode = wsmode;
1011		break;
1012	default:
1013		return (-1);
1014	}
1015	return (0);
1016}
1017
1018int
1019pms_sync_synaptics(struct pms_softc *sc, int data)
1020{
1021	switch (sc->inputstate) {
1022	case 0:
1023		if ((data & 0xc8) != 0x80)
1024			return (-1);
1025		break;
1026	case 3:
1027		if ((data & 0xc8) != 0xc0)
1028			return (-1);
1029		break;
1030	}
1031
1032	return (0);
1033}
1034
1035void
1036pms_proc_synaptics(struct pms_softc *sc)
1037{
1038	struct synaptics_softc *syn = sc->synaptics;
1039	u_int buttons;
1040	int x, y, z, w, dx, dy;
1041
1042	w = ((sc->packet[0] & 0x30) >> 2) | ((sc->packet[0] & 0x04) >> 1) |
1043	    ((sc->packet[3] & 0x04) >> 2);
1044
1045	if ((syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) && w == 3) {
1046		synaptics_sec_proc(sc);
1047		return;
1048	}
1049
1050	if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0)
1051		return;
1052
1053	/* XXX ignore advanced gesture packet, not yet supported */
1054	if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_ADV_GESTURE) && w == 2)
1055		return;
1056
1057	x = ((sc->packet[3] & 0x10) << 8) | ((sc->packet[1] & 0x0f) << 8) |
1058	    sc->packet[4];
1059	y = ((sc->packet[3] & 0x20) << 7) | ((sc->packet[1] & 0xf0) << 4) |
1060	    sc->packet[5];
1061	z = sc->packet[2];
1062
1063	buttons = ((sc->packet[0] & sc->packet[3]) & 0x01) ?
1064	    WSMOUSE_BUTTON(1) : 0;
1065	buttons |= ((sc->packet[0] & sc->packet[3]) & 0x02) ?
1066	    WSMOUSE_BUTTON(3) : 0;
1067
1068	if (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) {
1069		buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
1070		    WSMOUSE_BUTTON(1) : 0;
1071	} else if (syn->capabilities & SYNAPTICS_CAP_MIDDLE_BUTTON) {
1072		buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
1073		    WSMOUSE_BUTTON(2) : 0;
1074	}
1075
1076	if (syn->capabilities & SYNAPTICS_CAP_FOUR_BUTTON) {
1077		buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
1078		    WSMOUSE_BUTTON(4) : 0;
1079		buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x02) ?
1080		    WSMOUSE_BUTTON(5) : 0;
1081	} else if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) &&
1082	    ((sc->packet[0] ^ sc->packet[3]) & 0x02)) {
1083		buttons |= (sc->packet[4] & 0x01) ? WSMOUSE_BUTTON(6) : 0;
1084		buttons |= (sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(7) : 0;
1085		buttons |= (sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(8) : 0;
1086		buttons |= (sc->packet[5] & 0x02) ? WSMOUSE_BUTTON(9) : 0;
1087		buttons |= (sc->packet[4] & 0x04) ? WSMOUSE_BUTTON(10) : 0;
1088		buttons |= (sc->packet[5] & 0x04) ? WSMOUSE_BUTTON(11) : 0;
1089		buttons |= (sc->packet[4] & 0x08) ? WSMOUSE_BUTTON(12) : 0;
1090		buttons |= (sc->packet[5] & 0x08) ? WSMOUSE_BUTTON(13) : 0;
1091		x &= ~0x0f;
1092		y &= ~0x0f;
1093	}
1094
1095	/* ignore final events that happen when removing all fingers */
1096	if (x <= 1 || y <= 1) {
1097		x = syn->old_x;
1098		y = syn->old_y;
1099	}
1100
1101	if (syn->wsmode == WSMOUSE_NATIVE) {
1102		wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w,
1103		    WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
1104		    WSMOUSE_INPUT_ABSOLUTE_Z | WSMOUSE_INPUT_ABSOLUTE_W |
1105		    WSMOUSE_INPUT_SYNC);
1106	} else {
1107		dx = dy = 0;
1108		if (z > SYNAPTICS_PRESSURE) {
1109			dx = x - syn->old_x;
1110			dy = y - syn->old_y;
1111			dx /= SYNAPTICS_SCALE;
1112			dy /= SYNAPTICS_SCALE;
1113		}
1114		if (dx || dy || buttons != syn->old_buttons)
1115			wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
1116			    WSMOUSE_INPUT_DELTA);
1117		syn->old_buttons = buttons;
1118	}
1119
1120	syn->old_x = x;
1121	syn->old_y = y;
1122}
1123
1124void
1125pms_disable_synaptics(struct pms_softc *sc)
1126{
1127	struct synaptics_softc *syn = sc->synaptics;
1128
1129	if (syn->capabilities & SYNAPTICS_CAP_SLEEP)
1130		synaptics_set_mode(sc, SYNAPTICS_SLEEP_MODE |
1131		    SYNAPTICS_DISABLE_GESTURE);
1132}
1133
1134int
1135alps_sec_proc(struct pms_softc *sc)
1136{
1137	struct alps_softc *alps = sc->alps;
1138	int dx, dy, pos = 0;
1139
1140	if ((sc->packet[0] & PMS_ALPS_PS2_MASK) == PMS_ALPS_PS2_VALID) {
1141		/*
1142		 * We need to keep buttons states because interleaved
1143		 * packets only signalize x/y movements.
1144		 */
1145		alps->sec_buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK];
1146	} else if ((sc->packet[3] & PMS_ALPS_INTERLEAVED_MASK) ==
1147	    PMS_ALPS_INTERLEAVED_VALID) {
1148		sc->inputstate = 3;
1149		pos = 3;
1150	} else {
1151		return (0);
1152	}
1153
1154	if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) == 0)
1155		return (1);
1156
1157	dx = (sc->packet[pos] & PMS_PS2_XNEG) ?
1158	    (int)sc->packet[pos + 1] - 256 : sc->packet[pos + 1];
1159	dy = (sc->packet[pos] & PMS_PS2_YNEG) ?
1160	    (int)sc->packet[pos + 2] - 256 : sc->packet[pos + 2];
1161
1162	wsmouse_input(sc->sc_sec_wsmousedev, alps->sec_buttons,
1163	    dx, dy, 0, 0, WSMOUSE_INPUT_DELTA);
1164
1165	return (1);
1166}
1167
1168int
1169alps_get_hwinfo(struct pms_softc *sc)
1170{
1171	struct alps_softc *alps = sc->alps;
1172	u_char resp[3];
1173	int i;
1174
1175	if (pms_set_resolution(sc, 0) ||
1176	    pms_set_scaling(sc, 2) ||
1177	    pms_set_scaling(sc, 2) ||
1178	    pms_set_scaling(sc, 2) ||
1179	    pms_get_status(sc, resp)) {
1180		DPRINTF("%s: alps: model query error\n", DEVNAME(sc));
1181		return (-1);
1182	}
1183
1184	alps->version = (resp[0] << 8) | (resp[1] << 4) | (resp[2] / 20 + 1);
1185
1186	for (i = 0; i < nitems(alps_models); i++)
1187		if (alps->version == alps_models[i].version) {
1188			alps->model = alps_models[i].model;
1189			alps->mask = alps_models[i].mask;
1190			return (0);
1191		}
1192
1193	return (-1);
1194}
1195
1196int
1197pms_enable_alps(struct pms_softc *sc)
1198{
1199	struct alps_softc *alps = sc->alps;
1200	struct wsmousedev_attach_args a;
1201	u_char resp[3];
1202
1203	if (pms_set_resolution(sc, 0) ||
1204	    pms_set_scaling(sc, 1) ||
1205	    pms_set_scaling(sc, 1) ||
1206	    pms_set_scaling(sc, 1) ||
1207	    pms_get_status(sc, resp) ||
1208	    resp[0] != PMS_ALPS_MAGIC1 ||
1209	    resp[1] != PMS_ALPS_MAGIC2 ||
1210	    (resp[2] != PMS_ALPS_MAGIC3_1 && resp[2] != PMS_ALPS_MAGIC3_2 &&
1211	    resp[2] != PMS_ALPS_MAGIC3_3))
1212		return (0);
1213
1214	if (sc->alps == NULL) {
1215		sc->alps = alps = malloc(sizeof(struct alps_softc),
1216		    M_DEVBUF, M_WAITOK | M_ZERO);
1217		if (alps == NULL) {
1218			printf("%s: alps: not enough memory\n", DEVNAME(sc));
1219			goto err;
1220		}
1221
1222		if (alps_get_hwinfo(sc))
1223			goto err;
1224
1225		printf("%s: ALPS %s, version 0x%04x\n", DEVNAME(sc),
1226		    (alps->model & ALPS_DUALPOINT ? "Dualpoint" : "Glidepoint"),
1227		    alps->version);
1228
1229		alps->min_x = ALPS_XMIN_BEZEL;
1230		alps->min_y = ALPS_YMIN_BEZEL;
1231		alps->max_x = ALPS_XMAX_BEZEL;
1232		alps->max_y = ALPS_YMAX_BEZEL;
1233
1234		alps->wsmode = WSMOUSE_COMPAT;
1235
1236		if (alps->model & ALPS_DUALPOINT) {
1237			a.accessops = &pms_sec_accessops;
1238			a.accesscookie = sc;
1239			sc->sc_sec_wsmousedev = config_found((void *)sc, &a,
1240			    wsmousedevprint);
1241		}
1242	}
1243
1244	if (alps->model == 0)
1245		goto err;
1246
1247	if ((alps->model & ALPS_PASSTHROUGH) &&
1248	   (pms_set_scaling(sc, 2) ||
1249	    pms_set_scaling(sc, 2) ||
1250	    pms_set_scaling(sc, 2) ||
1251	    pms_dev_disable(sc))) {
1252		DPRINTF("%s: alps: passthrough on error\n", DEVNAME(sc));
1253		goto err;
1254	}
1255
1256	if (pms_dev_disable(sc) ||
1257	    pms_dev_disable(sc) ||
1258	    pms_set_rate(sc, 0x0a)) {
1259		DPRINTF("%s: alps: tapping error\n", DEVNAME(sc));
1260		goto err;
1261	}
1262
1263	if (pms_dev_disable(sc) ||
1264	    pms_dev_disable(sc) ||
1265	    pms_dev_disable(sc) ||
1266	    pms_dev_disable(sc) ||
1267	    pms_dev_enable(sc)) {
1268		DPRINTF("%s: alps: absolute mode error\n", DEVNAME(sc));
1269		goto err;
1270	}
1271
1272	if ((alps->model & ALPS_PASSTHROUGH) &&
1273	   (pms_set_scaling(sc, 1) ||
1274	    pms_set_scaling(sc, 1) ||
1275	    pms_set_scaling(sc, 1) ||
1276	    pms_dev_disable(sc))) {
1277		DPRINTF("%s: alps: passthrough off error\n", DEVNAME(sc));
1278		goto err;
1279	}
1280
1281	alps->sec_buttons = 0;
1282
1283	return (1);
1284
1285err:
1286	pms_reset(sc);
1287
1288	return (0);
1289}
1290
1291int
1292pms_ioctl_alps(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
1293    struct proc *p)
1294{
1295	struct alps_softc *alps = sc->alps;
1296	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
1297	int wsmode;
1298
1299	switch (cmd) {
1300	case WSMOUSEIO_GTYPE:
1301		*(u_int *)data = WSMOUSE_TYPE_ALPS;
1302		break;
1303	case WSMOUSEIO_GCALIBCOORDS:
1304		wsmc->minx = alps->min_x;
1305		wsmc->maxx = alps->max_x;
1306		wsmc->miny = alps->min_y;
1307		wsmc->maxy = alps->max_y;
1308		wsmc->swapxy = 0;
1309		break;
1310	case WSMOUSEIO_SETMODE:
1311		wsmode = *(u_int *)data;
1312		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
1313			return (EINVAL);
1314		alps->wsmode = wsmode;
1315		break;
1316	default:
1317		return (-1);
1318	}
1319	return (0);
1320}
1321
1322int
1323pms_sync_alps(struct pms_softc *sc, int data)
1324{
1325	struct alps_softc *alps = sc->alps;
1326
1327	if ((alps->model & ALPS_DUALPOINT) &&
1328	    (sc->packet[0] & PMS_ALPS_PS2_MASK) == PMS_ALPS_PS2_VALID) {
1329		if (sc->inputstate == 2)
1330			sc->inputstate += 3;
1331		return (0);
1332	}
1333
1334	switch (sc->inputstate) {
1335	case 0:
1336		if ((data & alps->mask) != alps->mask)
1337			return (-1);
1338		break;
1339	case 1:
1340	case 2:
1341	case 3:
1342		if ((data & PMS_ALPS_MASK) != PMS_ALPS_VALID)
1343			return (-1);
1344		break;
1345	case 4:
1346	case 5:
1347		if ((alps->model & ALPS_INTERLEAVED) == 0 &&
1348		    (data & PMS_ALPS_MASK) != PMS_ALPS_VALID)
1349			return (-1);
1350		break;
1351	}
1352
1353	return (0);
1354}
1355
1356void
1357pms_proc_alps(struct pms_softc *sc)
1358{
1359	struct alps_softc *alps = sc->alps;
1360	int x, y, z, w, dx, dy;
1361	u_int buttons;
1362	int fin, ges;
1363
1364	if ((alps->model & ALPS_DUALPOINT) && alps_sec_proc(sc))
1365		return;
1366
1367	x = sc->packet[1] | ((sc->packet[2] & 0x78) << 4);
1368	y = sc->packet[4] | ((sc->packet[3] & 0x70) << 3);
1369	z = sc->packet[5];
1370
1371	buttons = ((sc->packet[3] & 1) ? WSMOUSE_BUTTON(1) : 0) |
1372	    ((sc->packet[3] & 2) ? WSMOUSE_BUTTON(3) : 0) |
1373	    ((sc->packet[3] & 4) ? WSMOUSE_BUTTON(2) : 0);
1374
1375	if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) && z == ALPS_Z_MAGIC) {
1376		dx = (x > ALPS_XSEC_BEZEL / 2) ? (x - ALPS_XSEC_BEZEL) : x;
1377		dy = (y > ALPS_YSEC_BEZEL / 2) ? (y - ALPS_YSEC_BEZEL) : y;
1378
1379		wsmouse_input(sc->sc_sec_wsmousedev, buttons, dx, dy, 0, 0,
1380		    WSMOUSE_INPUT_DELTA);
1381
1382		return;
1383	}
1384
1385	if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0)
1386		return;
1387
1388	/*
1389	 * XXX The Y-axis is in the oposit direction compared to
1390	 * Synaptics touchpads and PS/2 mouses.
1391	 * It's why we need to translate the y value here for both
1392	 * NATIVE and COMPAT modes.
1393	 */
1394	y = ALPS_YMAX_BEZEL - y + ALPS_YMIN_BEZEL;
1395
1396	if (alps->wsmode == WSMOUSE_NATIVE) {
1397		ges = sc->packet[2] & 0x01;
1398		fin = sc->packet[2] & 0x02;
1399
1400		/* Simulate click (tap) */
1401		if (ges && !fin)
1402			z = 35;
1403
1404		/* Generate a null pressure event (needed for tap & drag) */
1405		if (ges && fin && !alps->old_fin)
1406			z = 0;
1407
1408		/* Generate a width value corresponding to one finger */
1409		if (z > 0)
1410			w = 4;
1411
1412		wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w,
1413		    WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
1414		    WSMOUSE_INPUT_ABSOLUTE_Z | WSMOUSE_INPUT_ABSOLUTE_W |
1415		    WSMOUSE_INPUT_SYNC);
1416
1417		alps->old_fin = fin;
1418	} else {
1419		dx = dy = 0;
1420		if (z > ALPS_PRESSURE) {
1421			dx = x - alps->old_x;
1422			dy = y - alps->old_y;
1423
1424			/* Prevent jump */
1425			dx = abs(dx) > 50 ? 0 : dx;
1426			dy = abs(dy) > 50 ? 0 : dy;
1427		}
1428
1429		if (dx || dy || buttons != alps->old_buttons)
1430			wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
1431			    WSMOUSE_INPUT_DELTA);
1432
1433		alps->old_x = x;
1434		alps->old_y = y;
1435		alps->old_buttons = buttons;
1436	}
1437}
1438
1439int
1440elantech_set_absolute_mode_v1(struct pms_softc *sc)
1441{
1442	int i;
1443	u_char resp[3];
1444
1445	/* Enable absolute mode. Magic numbers from Linux driver. */
1446	if (pms_spec_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
1447	    pms_spec_cmd(sc, 0x10) ||
1448	    pms_spec_cmd(sc, 0x16) ||
1449	    pms_set_scaling(sc, 1) ||
1450	    pms_spec_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
1451	    pms_spec_cmd(sc, 0x11) ||
1452	    pms_spec_cmd(sc, 0x8f) ||
1453	    pms_set_scaling(sc, 1))
1454		return (-1);
1455
1456	/* Read back reg 0x10 to ensure hardware is ready. */
1457	for (i = 0; i < 5; i++) {
1458		if (pms_spec_cmd(sc, ELANTECH_CMD_READ_REG) ||
1459		    pms_spec_cmd(sc, 0x10) ||
1460		    pms_get_status(sc, resp) == 0)
1461			break;
1462		delay(2000);
1463	}
1464	if (i == 5)
1465		return (-1);
1466
1467	if ((resp[0] & ELANTECH_ABSOLUTE_MODE) == 0)
1468		return (-1);
1469
1470	return (0);
1471}
1472
1473int
1474elantech_set_absolute_mode_v2(struct pms_softc *sc)
1475{
1476	int i;
1477	u_char resp[3];
1478
1479	/* Enable absolute mode. Magic numbers from Linux driver. */
1480	if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1481	    elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
1482	    elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1483	    elantech_ps2_cmd(sc, 0x10) ||
1484	    elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1485	    elantech_ps2_cmd(sc, 0x54) ||
1486	    pms_set_scaling(sc, 1) ||
1487	    elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1488	    elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
1489	    elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1490	    elantech_ps2_cmd(sc, 0x11) ||
1491	    elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1492	    elantech_ps2_cmd(sc, 0x88) ||
1493	    pms_set_scaling(sc, 1) ||
1494	    elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1495	    elantech_ps2_cmd(sc, ELANTECH_CMD_WRITE_REG) ||
1496	    elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1497	    elantech_ps2_cmd(sc, 0x21) ||
1498	    elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1499	    elantech_ps2_cmd(sc, 0x88) ||
1500	    pms_set_scaling(sc, 1))
1501		return (-1);
1502
1503	/* Read back reg 0x10 to ensure hardware is ready. */
1504	for (i = 0; i < 5; i++) {
1505		if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1506		    elantech_ps2_cmd(sc, ELANTECH_CMD_READ_REG) ||
1507		    elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1508		    elantech_ps2_cmd(sc, 0x10) ||
1509		    pms_get_status(sc, resp) == 0)
1510			break;
1511		delay(2000);
1512	}
1513	if (i == 5)
1514		return (-1);
1515
1516	return (0);
1517}
1518
1519int
1520elantech_set_absolute_mode_v3(struct pms_softc *sc)
1521{
1522	int i;
1523	u_char resp[3];
1524
1525	/* Enable absolute mode. Magic numbers from Linux driver. */
1526	if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1527	    elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) ||
1528	    elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1529	    elantech_ps2_cmd(sc, 0x10) ||
1530	    elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1531	    elantech_ps2_cmd(sc, 0x0b) ||
1532	    pms_set_scaling(sc, 1))
1533		return (-1);
1534
1535	/* Read back reg 0x10 to ensure hardware is ready. */
1536	for (i = 0; i < 5; i++) {
1537		if (elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1538		    elantech_ps2_cmd(sc, ELANTECH_CMD_READ_WRITE_REG) ||
1539		    elantech_ps2_cmd(sc, ELANTECH_PS2_CUSTOM_COMMAND) ||
1540		    elantech_ps2_cmd(sc, 0x10) ||
1541		    pms_get_status(sc, resp) == 0)
1542			break;
1543		delay(2000);
1544	}
1545	if (i == 5)
1546		return (-1);
1547
1548	return (0);
1549}
1550
1551int
1552elantech_get_hwinfo_v1(struct pms_softc *sc)
1553{
1554	struct elantech_softc *elantech = sc->elantech;
1555	int fw_version;
1556	u_char capabilities[3];
1557
1558	if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
1559		return (-1);
1560
1561	if (fw_version < 0x20030 || fw_version == 0x20600) {
1562		if (fw_version < 0x20000)
1563			elantech->flags |= ELANTECH_F_HW_V1_OLD;
1564	} else
1565		return (-1);
1566
1567	if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) ||
1568	    pms_get_status(sc, capabilities))
1569		return (-1);
1570
1571	if (capabilities[0] & ELANTECH_CAP_HAS_ROCKER)
1572		elantech->flags |= ELANTECH_F_HAS_ROCKER;
1573
1574	if (elantech_set_absolute_mode_v1(sc))
1575		return (-1);
1576
1577	elantech->min_x = ELANTECH_V1_X_MIN;
1578	elantech->max_x = ELANTECH_V1_X_MAX;
1579	elantech->min_y = ELANTECH_V1_Y_MIN;
1580	elantech->max_y = ELANTECH_V1_Y_MAX;
1581
1582	return (0);
1583}
1584
1585int
1586elantech_get_hwinfo_v2(struct pms_softc *sc)
1587{
1588	struct elantech_softc *elantech = sc->elantech;
1589	int fw_version, ic_ver;
1590	u_char capabilities[3];
1591	int i, fixed_dpi;
1592	u_char resp[3];
1593
1594	if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
1595		return (-1);
1596
1597	ic_ver = (fw_version & 0x0f0000) >> 16;
1598	if (ic_ver != 2 && ic_ver != 4)
1599		return (-1);
1600
1601	if (fw_version >= 0x20800)
1602		elantech->flags |= ELANTECH_F_REPORTS_PRESSURE;
1603
1604	if (pms_spec_cmd(sc, ELANTECH_QUE_CAPABILITIES) ||
1605	    pms_get_status(sc, capabilities))
1606		return (-1);
1607
1608	if (elantech_set_absolute_mode_v2(sc))
1609		return (-1);
1610
1611	if (fw_version == 0x20800 || fw_version == 0x20b00 ||
1612	    fw_version == 0x20030) {
1613		elantech->max_x = ELANTECH_V2_X_MAX;
1614		elantech->max_y = ELANTECH_V2_Y_MAX;
1615	} else {
1616		if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) ||
1617		    pms_get_status(sc, resp))
1618			return (-1);
1619		fixed_dpi = resp[1] & 0x10;
1620		i = (fw_version > 0x20800 && fw_version < 0x20900) ? 1 : 2;
1621		if ((fw_version >> 16) == 0x14 && fixed_dpi) {
1622			if (pms_spec_cmd(sc, ELANTECH_QUE_SAMPLE) ||
1623			    pms_get_status(sc, resp))
1624				return (-1);
1625			elantech->max_x = (capabilities[1] - i) * resp[1] / 2;
1626			elantech->max_y = (capabilities[2] - i) * resp[2] / 2;
1627		} else if (fw_version == 0x040216) {
1628			elantech->max_x = 819;
1629			elantech->max_y = 405;
1630		} else if (fw_version == 0x040219 || fw_version == 0x040215) {
1631			elantech->max_x = 900;
1632			elantech->max_y = 500;
1633		} else {
1634			elantech->max_x = (capabilities[1] - i) * 64;
1635			elantech->max_y = (capabilities[2] - i) * 64;
1636		}
1637	}
1638
1639	return (0);
1640}
1641
1642int
1643elantech_get_hwinfo_v3(struct pms_softc *sc)
1644{
1645	struct elantech_softc *elantech = sc->elantech;
1646	int fw_version;
1647	u_char resp[3];
1648
1649	if (synaptics_query(sc, ELANTECH_QUE_FW_VER, &fw_version))
1650		return (-1);
1651
1652	if (((fw_version & 0x0f0000) >> 16) != 5)
1653		return (-1);
1654
1655	elantech->flags |= ELANTECH_F_REPORTS_PRESSURE;
1656
1657	if (elantech_set_absolute_mode_v3(sc))
1658		return (-1);
1659
1660	if (pms_spec_cmd(sc, ELANTECH_QUE_FW_ID) ||
1661	    pms_get_status(sc, resp))
1662		return (-1);
1663
1664	elantech->max_x = (resp[0] & 0x0f) << 8 | resp[1];
1665	elantech->max_y = (resp[0] & 0xf0) << 4 | resp[2];
1666
1667	return (0);
1668}
1669
1670int
1671elantech_ps2_cmd(struct pms_softc *sc, u_char command)
1672{
1673	u_char cmd[1];
1674
1675	cmd[0] = command;
1676	return (pms_cmd(sc, cmd, 1, NULL, 0));
1677}
1678
1679int
1680elantech_knock(struct pms_softc *sc)
1681{
1682	u_char resp[3];
1683
1684	if (pms_dev_disable(sc) ||
1685	    pms_set_scaling(sc, 1) ||
1686	    pms_set_scaling(sc, 1) ||
1687	    pms_set_scaling(sc, 1) ||
1688	    pms_get_status(sc, resp) ||
1689	    resp[0] != PMS_ELANTECH_MAGIC1 ||
1690	    resp[1] != PMS_ELANTECH_MAGIC2 ||
1691	    (resp[2] != PMS_ELANTECH_MAGIC3_1 &&
1692	    resp[2] != PMS_ELANTECH_MAGIC3_2))
1693		return (-1);
1694
1695	return (0);
1696}
1697
1698int
1699pms_enable_elantech_v1(struct pms_softc *sc)
1700{
1701	struct elantech_softc *elantech = sc->elantech;
1702	int i;
1703
1704	if (elantech_knock(sc))
1705		return (0);
1706
1707	if (sc->elantech == NULL) {
1708		sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
1709		    M_DEVBUF, M_WAITOK | M_ZERO);
1710		if (elantech == NULL) {
1711			printf("%s: elantech: not enough memory\n",
1712			    DEVNAME(sc));
1713			goto err;
1714		}
1715
1716		if (elantech_get_hwinfo_v1(sc)) {
1717			free(sc->elantech, M_DEVBUF);
1718			sc->elantech = NULL;
1719			goto err;
1720		}
1721
1722		printf("%s: Elantech Touchpad, version %d\n", DEVNAME(sc), 1);
1723	} else if (elantech_set_absolute_mode_v1(sc)) {
1724		free(sc->elantech, M_DEVBUF);
1725		sc->elantech = NULL;
1726		goto err;
1727	}
1728
1729	for (i = 0; i < nitems(sc->elantech->parity); i++)
1730		sc->elantech->parity[i] = sc->elantech->parity[i & (i - 1)] ^ 1;
1731
1732	return (1);
1733
1734err:
1735	pms_reset(sc);
1736
1737	return (0);
1738}
1739
1740int
1741pms_enable_elantech_v2(struct pms_softc *sc)
1742{
1743	struct elantech_softc *elantech = sc->elantech;
1744
1745	if (elantech_knock(sc))
1746		return (0);
1747
1748	if (sc->elantech == NULL) {
1749		sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
1750		    M_DEVBUF, M_WAITOK | M_ZERO);
1751		if (elantech == NULL) {
1752			printf("%s: elantech: not enough memory\n",
1753			    DEVNAME(sc));
1754			goto err;
1755		}
1756
1757		if (elantech_get_hwinfo_v2(sc)) {
1758			free(sc->elantech, M_DEVBUF);
1759			sc->elantech = NULL;
1760			goto err;
1761		}
1762
1763		printf("%s: Elantech Touchpad, version %d\n", DEVNAME(sc), 2);
1764	} else if (elantech_set_absolute_mode_v2(sc)) {
1765		free(sc->elantech, M_DEVBUF);
1766		sc->elantech = NULL;
1767		goto err;
1768	}
1769
1770	return (1);
1771
1772err:
1773	pms_reset(sc);
1774
1775	return (0);
1776}
1777
1778int
1779pms_enable_elantech_v3(struct pms_softc *sc)
1780{
1781	struct elantech_softc *elantech = sc->elantech;
1782
1783	if (elantech_knock(sc))
1784		return (0);
1785
1786	if (sc->elantech == NULL) {
1787		sc->elantech = elantech = malloc(sizeof(struct elantech_softc),
1788		    M_DEVBUF, M_WAITOK | M_ZERO);
1789		if (elantech == NULL) {
1790			printf("%s: elantech: not enough memory\n",
1791			    DEVNAME(sc));
1792			goto err;
1793		}
1794
1795		if (elantech_get_hwinfo_v3(sc)) {
1796			free(sc->elantech, M_DEVBUF);
1797			sc->elantech = NULL;
1798			goto err;
1799		}
1800
1801		printf("%s: Elantech Touchpad, version %d\n", DEVNAME(sc), 3);
1802	} else if (elantech_set_absolute_mode_v3(sc)) {
1803		free(sc->elantech, M_DEVBUF);
1804		sc->elantech = NULL;
1805		goto err;
1806	}
1807
1808	return (1);
1809
1810err:
1811	pms_reset(sc);
1812
1813	return (0);
1814}
1815
1816int
1817pms_ioctl_elantech(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
1818    struct proc *p)
1819{
1820	struct elantech_softc *elantech = sc->elantech;
1821	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
1822	int wsmode;
1823
1824	switch (cmd) {
1825	case WSMOUSEIO_GTYPE:
1826		*(u_int *)data = WSMOUSE_TYPE_ELANTECH;
1827		break;
1828	case WSMOUSEIO_GCALIBCOORDS:
1829		wsmc->minx = elantech->min_x;
1830		wsmc->maxx = elantech->max_x;
1831		wsmc->miny = elantech->min_y;
1832		wsmc->maxy = elantech->max_y;
1833		wsmc->swapxy = 0;
1834		wsmc->resx = 0;
1835		wsmc->resy = 0;
1836		break;
1837	case WSMOUSEIO_SETMODE:
1838		wsmode = *(u_int *)data;
1839		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
1840			return (EINVAL);
1841		elantech->wsmode = wsmode;
1842		break;
1843	default:
1844		return (-1);
1845	}
1846	return (0);
1847}
1848
1849int
1850pms_sync_elantech_v1(struct pms_softc *sc, int data)
1851{
1852	struct elantech_softc *elantech = sc->elantech;
1853	u_char p;
1854
1855	switch (sc->inputstate) {
1856	case 0:
1857		if (elantech->flags & ELANTECH_F_HW_V1_OLD) {
1858			elantech->p1 = (data & 0x20) >> 5;
1859			elantech->p2 = (data & 0x10) >> 4;
1860		} else {
1861			elantech->p1 = (data & 0x10) >> 4;
1862			elantech->p2 = (data & 0x20) >> 5;
1863		}
1864		elantech->p3 = (data & 0x04) >> 2;
1865		return (0);
1866	case 1:
1867		p = elantech->p1;
1868		break;
1869	case 2:
1870		p = elantech->p2;
1871		break;
1872	case 3:
1873		p = elantech->p3;
1874		break;
1875	default:
1876		return (-1);
1877	}
1878
1879	if (data < 0 || data >= nitems(elantech->parity) ||
1880	    elantech->parity[data] != p)
1881		return (-1);
1882
1883	return (0);
1884}
1885
1886int
1887pms_sync_elantech_v2(struct pms_softc *sc, int data)
1888{
1889	struct elantech_softc *elantech = sc->elantech;
1890
1891	/* Variants reporting pressure always have the same constant bits. */
1892	if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE) {
1893		if (sc->inputstate == 0 && (data & 0x0c) != 0x04)
1894			return (-1);
1895		if (sc->inputstate == 3 && (data & 0x0f) != 0x02)
1896			return (-1);
1897		return (0);
1898	}
1899
1900	/* For variants not reporting pressure, 1 and 3 finger touch packets
1901	 * have different constant bits than 2 finger touch pakets. */
1902	switch (sc->inputstate) {
1903	case 0:
1904		if ((data & 0xc0) == 0x80) {
1905			if ((data & 0x0c) != 0x0c)
1906				return (-1);
1907			elantech->flags |= ELANTECH_F_2FINGER_PACKET;
1908		} else {
1909			if ((data & 0x3c) != 0x3c)
1910				return (-1);
1911			elantech->flags &= ~ELANTECH_F_2FINGER_PACKET;
1912		}
1913		break;
1914	case 1:
1915	case 4:
1916		if (elantech->flags & ELANTECH_F_2FINGER_PACKET)
1917			break;
1918		if ((data & 0xf0) != 0x00)
1919			return (-1);
1920		break;
1921	case 3:
1922		if (elantech->flags & ELANTECH_F_2FINGER_PACKET) {
1923			if ((data & 0x0e) != 0x08)
1924				return (-1);
1925		} else {
1926			if ((data & 0x3e) != 0x38)
1927				return (-1);
1928		}
1929		break;
1930	default:
1931		break;
1932	}
1933
1934	return (0);
1935}
1936
1937int
1938pms_sync_elantech_v3(struct pms_softc *sc, int data)
1939{
1940	switch (sc->inputstate) {
1941	case 0:
1942		if ((data & 0x0c) != 0x04 && (data & 0x0c) != 0x0c)
1943			return (-1);
1944		break;
1945	case 3:
1946		if ((data & 0xcf) != 0x02 && (data & 0xce) != 0x0c)
1947			return (-1);
1948		break;
1949	}
1950
1951	return (0);
1952}
1953
1954void
1955pms_proc_elantech_v1(struct pms_softc *sc)
1956{
1957	struct elantech_softc *elantech = sc->elantech;
1958	u_int buttons;
1959	int x, y, w, z;
1960
1961	if (elantech->flags & ELANTECH_F_HW_V1_OLD)
1962		w = ((sc->packet[1] & 0x80) >> 7) +
1963		    ((sc->packet[1] & 0x30) >> 4);
1964	else
1965		w = (sc->packet[0] & 0xc0) >> 6;
1966
1967	/* Hardware version 1 doesn't report pressure. */
1968	if (w) {
1969		x = ((sc->packet[1] & 0x0c) << 6) | sc->packet[2];
1970		y = ((sc->packet[1] & 0x03) << 8) | sc->packet[3];
1971		z = SYNAPTICS_PRESSURE;
1972	} else {
1973		x = elantech->old_x;
1974		y = elantech->old_y;
1975		z = 0;
1976	}
1977
1978	if (sc->packet[0] & 0x01)
1979		buttons |= WSMOUSE_BUTTON(1);
1980	if (sc->packet[0] & 0x02)
1981		buttons |= WSMOUSE_BUTTON(3);
1982	if (elantech->flags & ELANTECH_F_HAS_ROCKER) {
1983		if (sc->packet[0] & 0x40) /* up */
1984			buttons |= WSMOUSE_BUTTON(4);
1985		if (sc->packet[0] & 0x80) /* down */
1986			buttons |= WSMOUSE_BUTTON(5);
1987	}
1988
1989	elantech_send_input(sc, buttons, x, y, z, w);
1990}
1991
1992void
1993pms_proc_elantech_v2(struct pms_softc *sc)
1994{
1995	const u_char debounce_pkt[] = { 0x84, 0xff, 0xff, 0x02, 0xff, 0xff };
1996	struct elantech_softc *elantech = sc->elantech;
1997	u_int buttons;
1998	int x, y, w, z;
1999
2000	/*
2001	 * The hardware sends this packet when in debounce state.
2002	 * The packet should be ignored.
2003	 */
2004	if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt)))
2005		return;
2006
2007	w = (sc->packet[0] & 0xc0) >> 6;
2008	if (w == 1 || w == 3) {
2009		x = ((sc->packet[1] & 0x0f) << 8) | sc->packet[2];
2010		y = ((sc->packet[4] & 0x0f) << 8) | sc->packet[5];
2011		if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE)
2012			z = ((sc->packet[1] & 0xf0) |
2013			    (sc->packet[4] & 0xf0) >> 4);
2014		else
2015			z = SYNAPTICS_PRESSURE;
2016	} else if (w == 2) {
2017		x = (((sc->packet[0] & 0x10) << 4) | sc->packet[1]) << 2;
2018		y = (((sc->packet[0] & 0x20) << 3) | sc->packet[2]) << 2;
2019		z = SYNAPTICS_PRESSURE;
2020	} else {
2021		x = elantech->old_x;
2022		y = elantech->old_y;
2023		z = 0;
2024	}
2025
2026	buttons = ((sc->packet[0] & 0x01 ? WSMOUSE_BUTTON(1) : 0) |
2027	    ((sc->packet[0] & 0x02) ? WSMOUSE_BUTTON(3): 0));
2028
2029	elantech_send_input(sc, buttons, x, y, z, w);
2030}
2031
2032void
2033pms_proc_elantech_v3(struct pms_softc *sc)
2034{
2035	const u_char debounce_pkt[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff };
2036	struct elantech_softc *elantech = sc->elantech;
2037	u_int buttons;
2038	int x, y, w, z;
2039
2040	/* The hardware sends this packet when in debounce state.
2041	 * The packet should be ignored. */
2042	if (!memcmp(sc->packet, debounce_pkt, sizeof(debounce_pkt)))
2043		return;
2044
2045	buttons = ((sc->packet[0] & 0x01 ? WSMOUSE_BUTTON(1) : 0) |
2046	    ((sc->packet[0] & 0x02) ? WSMOUSE_BUTTON(3): 0));
2047	x = ((sc->packet[1] & 0x0f) << 8 | sc->packet[2]);
2048	y = ((sc->packet[4] & 0x0f) << 8 | sc->packet[5]);
2049	z = 0;
2050	w = (sc->packet[0] & 0xc0) >> 6;
2051	if (w == 2) {
2052		/*
2053		 * Two-finger touch causes two packets -- a head packet
2054		 * and a tail packet. We report a single event and ignore
2055		 * the tail packet.
2056		 */
2057		if ((sc->packet[0] & 0x0c) != 0x04 &&
2058		    (sc->packet[3] & 0xfc) != 0x02) {
2059			/* not the head packet -- ignore */
2060			return;
2061		}
2062	}
2063
2064	/* Prevent juming cursor if pad isn't touched or reports garbage. */
2065	if (w == 0 ||
2066	    ((x == 0 || y == 0 || x == elantech->max_x || y == elantech->max_y)
2067	    && (x != elantech->old_x || y != elantech->old_y))) {
2068		x = elantech->old_x;
2069		y = elantech->old_y;
2070	}
2071
2072	if (elantech->flags & ELANTECH_F_REPORTS_PRESSURE)
2073		z = (sc->packet[1] & 0xf0) | ((sc->packet[4] & 0xf0) >> 4);
2074	else if (w)
2075		z = SYNAPTICS_PRESSURE;
2076
2077	elantech_send_input(sc, buttons, x, y, z, w);
2078}
2079
2080void
2081elantech_send_input(struct pms_softc *sc, u_int buttons, int x, int y, int z,
2082    int w)
2083 {
2084	struct elantech_softc *elantech = sc->elantech;
2085	int dx, dy;
2086
2087	if (elantech->wsmode == WSMOUSE_NATIVE) {
2088		wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w,
2089		    WSMOUSE_INPUT_ABSOLUTE_X |
2090		    WSMOUSE_INPUT_ABSOLUTE_Y |
2091		    WSMOUSE_INPUT_ABSOLUTE_Z |
2092		    WSMOUSE_INPUT_ABSOLUTE_W |
2093		    WSMOUSE_INPUT_SYNC);
2094	} else {
2095		dx = dy = 0;
2096
2097		if ((elantech->flags & ELANTECH_F_REPORTS_PRESSURE) &&
2098		    z > SYNAPTICS_PRESSURE) {
2099			dx = x - elantech->old_x;
2100			dy = y - elantech->old_y;
2101			dx /= SYNAPTICS_SCALE;
2102			dy /= SYNAPTICS_SCALE;
2103		}
2104		if (dx || dy || buttons != elantech->old_buttons)
2105			wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
2106			    WSMOUSE_INPUT_DELTA);
2107		elantech->old_buttons = buttons;
2108	}
2109
2110	elantech->old_x = x;
2111	elantech->old_y = y;
2112}
2113