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