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