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