pms.c revision 1.25
1/* $OpenBSD: pms.c,v 1.25 2011/12/03 19:43:00 mpi 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	u_int packetsize;
61	int (*enable)(struct pms_softc *);
62	int (*ioctl)(struct pms_softc *, u_long, caddr_t, int, struct proc *);
63	int (*sync)(struct pms_softc *, int);
64	void (*proc)(struct pms_softc *);
65	void (*disable)(struct pms_softc *);
66};
67
68struct synaptics_softc {
69	int identify;
70	int capabilities, ext_capabilities;
71	int model, ext_model;
72	int resolution, dimension;
73
74	int mode;
75
76	int res_x, res_y;
77	int min_x, min_y;
78	int max_x, max_y;
79
80	/* Compat mode */
81	int wsmode;
82	int old_x, old_y;
83	u_int old_buttons;
84#define SYNAPTICS_SCALE		4
85#define SYNAPTICS_PRESSURE	30
86};
87
88struct alps_softc {
89	int model;
90	int mask;
91	int version;
92
93	int min_x, min_y;
94	int max_x, max_y;
95	int old_fin;
96
97	/* Compat mode */
98	int wsmode;
99	int old_x, old_y;
100	u_int old_buttons;
101#define ALPS_PRESSURE	40
102};
103
104struct pms_softc {		/* driver status information */
105	struct device sc_dev;
106
107	pckbc_tag_t sc_kbctag;
108
109	int sc_state;
110#define PMS_STATE_DISABLED	0
111#define PMS_STATE_ENABLED	1
112#define PMS_STATE_SUSPENDED	2
113
114	int sc_dev_enable;
115#define PMS_DEV_IGNORE		0x00
116#define PMS_DEV_PRIMARY		0x01
117#define PMS_DEV_SECONDARY	0x02
118
119	int poll;
120	int inputstate;
121
122	const struct pms_protocol *protocol;
123	struct synaptics_softc *synaptics;
124	struct alps_softc *alps;
125
126	u_char packet[8];
127
128	struct device *sc_wsmousedev;
129	struct device *sc_pt_wsmousedev;
130};
131
132static const u_int butmap[8] = {
133	0,
134	WSMOUSE_BUTTON(1),
135	WSMOUSE_BUTTON(3),
136	WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(3),
137	WSMOUSE_BUTTON(2),
138	WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2),
139	WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3),
140	WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3)
141};
142
143static const struct alps_model {
144	int version;
145	int mask;
146	int model;
147} alps_models[] = {
148#if 0
149	/* FIXME some clipads are not working yet */
150	{ 0x5212, 0xff, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
151	{ 0x6222, 0xcf, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
152#endif
153	{ 0x2021, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
154	{ 0x2221, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
155	{ 0x2222, 0xff, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
156	{ 0x3222, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
157	{ 0x5321, 0xf8, ALPS_GLIDEPOINT },
158	{ 0x5322, 0xf8, ALPS_GLIDEPOINT },
159	{ 0x603b, 0xf8, ALPS_GLIDEPOINT },
160	{ 0x6321, 0xf8, ALPS_GLIDEPOINT },
161	{ 0x6322, 0xf8, ALPS_GLIDEPOINT },
162	{ 0x6323, 0xf8, ALPS_GLIDEPOINT },
163	{ 0x6324, 0x8f, ALPS_GLIDEPOINT },
164	{ 0x6325, 0xef, ALPS_GLIDEPOINT },
165	{ 0x6326, 0xf8, ALPS_GLIDEPOINT },
166	{ 0x633b, 0xf8, ALPS_DUALPOINT | ALPS_PASSTHROUGH },
167	{ 0x7301, 0xf8, ALPS_DUALPOINT },
168	{ 0x7321, 0xf8, ALPS_GLIDEPOINT },
169	{ 0x7322, 0xf8, ALPS_GLIDEPOINT },
170	{ 0x7325, 0xcf, ALPS_GLIDEPOINT },
171#if 0
172	{ 0x7326, 0, 0 },	/* XXX Uses unknown v3 protocol */
173#endif
174};
175
176int	pmsprobe(struct device *, void *, void *);
177void	pmsattach(struct device *, struct device *, void *);
178int	pmsactivate(struct device *, int);
179
180void	pmsinput(void *, int);
181
182int	pms_change_state(struct pms_softc *, int, int);
183int	pms_ioctl(void *, u_long, caddr_t, int, struct proc *);
184int	pms_enable(void *);
185void	pms_disable(void *);
186
187int	pms_cmd(struct pms_softc *, u_char *, int, u_char *, int);
188int	pms_spec_cmd(struct pms_softc *, int);
189int	pms_get_devid(struct pms_softc *, u_char *);
190int	pms_get_status(struct pms_softc *, u_char *);
191int	pms_set_rate(struct pms_softc *, int);
192int	pms_set_resolution(struct pms_softc *, int);
193int	pms_set_scaling(struct pms_softc *, int);
194int	pms_reset(struct pms_softc *);
195int	pms_dev_enable(struct pms_softc *);
196int	pms_dev_disable(struct pms_softc *);
197
198int	pms_enable_intelli(struct pms_softc *);
199
200int	pms_ioctl_mouse(struct pms_softc *, u_long, caddr_t, int, struct proc *);
201int	pms_sync_mouse(struct pms_softc *, int);
202void	pms_proc_mouse(struct pms_softc *);
203
204int	pms_enable_synaptics(struct pms_softc *);
205int	pms_ioctl_synaptics(struct pms_softc *, u_long, caddr_t, int, struct proc *);
206int	pms_sync_synaptics(struct pms_softc *, int);
207void	pms_proc_synaptics(struct pms_softc *);
208void	pms_disable_synaptics(struct pms_softc *);
209
210int	pms_enable_alps(struct pms_softc *);
211int	pms_ioctl_alps(struct pms_softc *, u_long, caddr_t, int, struct proc *);
212int	pms_sync_alps(struct pms_softc *, int);
213void	pms_proc_alps(struct pms_softc *);
214
215int	synaptics_set_mode(struct pms_softc *, int);
216int	synaptics_query(struct pms_softc *, int, int *);
217int	synaptics_get_hwinfo(struct pms_softc *);
218
219void	synaptics_pt_proc(struct pms_softc *);
220
221int	synaptics_pt_ioctl(void *, u_long, caddr_t, int, struct proc *);
222int	synaptics_pt_enable(void *);
223void	synaptics_pt_disable(void *);
224
225int	alps_get_hwinfo(struct pms_softc *);
226
227struct cfattach pms_ca = {
228	sizeof(struct pms_softc), pmsprobe, pmsattach, NULL,
229	pmsactivate
230};
231
232struct cfdriver pms_cd = {
233	NULL, "pms", DV_DULL
234};
235
236const struct wsmouse_accessops pms_accessops = {
237	pms_enable,
238	pms_ioctl,
239	pms_disable,
240};
241
242const struct wsmouse_accessops synaptics_pt_accessops = {
243	synaptics_pt_enable,
244	synaptics_pt_ioctl,
245	synaptics_pt_disable,
246};
247
248const struct pms_protocol pms_protocols[] = {
249	/* Generic PS/2 mouse */
250	{
251		PMS_STANDARD, 3,
252		NULL,
253		pms_ioctl_mouse,
254		pms_sync_mouse,
255		pms_proc_mouse,
256		NULL
257	},
258	/* Microsoft IntelliMouse */
259	{
260		PMS_INTELLI, 4,
261		pms_enable_intelli,
262		pms_ioctl_mouse,
263		pms_sync_mouse,
264		pms_proc_mouse,
265		NULL
266	},
267	/* Synaptics touchpad */
268	{
269		PMS_SYNAPTICS, 6,
270		pms_enable_synaptics,
271		pms_ioctl_synaptics,
272		pms_sync_synaptics,
273		pms_proc_synaptics,
274		pms_disable_synaptics
275	},
276	/* ALPS touchpad */
277	{
278		PMS_ALPS, 6,
279		pms_enable_alps,
280		pms_ioctl_alps,
281		pms_sync_alps,
282		pms_proc_alps,
283		NULL
284	},
285};
286
287int
288pms_cmd(struct pms_softc *sc, u_char *cmd, int len, u_char *resp, int resplen)
289{
290	if (sc->poll) {
291		return pckbc_poll_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT,
292		    cmd, len, resplen, resp, 1);
293	} else {
294		return pckbc_enqueue_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT,
295		    cmd, len, resplen, 1, resp);
296	}
297}
298
299int
300pms_spec_cmd(struct pms_softc *sc, int cmd)
301{
302	if (pms_set_scaling(sc, 1) ||
303	    pms_set_resolution(sc, (cmd >> 6) & 0x03) ||
304	    pms_set_resolution(sc, (cmd >> 4) & 0x03) ||
305	    pms_set_resolution(sc, (cmd >> 2) & 0x03) ||
306	    pms_set_resolution(sc, (cmd >> 0) & 0x03))
307		return (-1);
308	return (0);
309}
310
311int
312pms_get_devid(struct pms_softc *sc, u_char *resp)
313{
314	u_char cmd[1];
315
316	cmd[0] = PMS_SEND_DEV_ID;
317	return (pms_cmd(sc, cmd, 1, resp, 1));
318}
319
320int
321pms_get_status(struct pms_softc *sc, u_char *resp)
322{
323	u_char cmd[1];
324
325	cmd[0] = PMS_SEND_DEV_STATUS;
326	return (pms_cmd(sc, cmd, 1, resp, 3));
327}
328
329int
330pms_set_rate(struct pms_softc *sc, int value)
331{
332	u_char cmd[2];
333
334	cmd[0] = PMS_SET_SAMPLE;
335	cmd[1] = value;
336	return (pms_cmd(sc, cmd, 2, NULL, 0));
337}
338
339int
340pms_set_resolution(struct pms_softc *sc, int value)
341{
342	u_char cmd[2];
343
344	cmd[0] = PMS_SET_RES;
345	cmd[1] = value;
346	return (pms_cmd(sc, cmd, 2, NULL, 0));
347}
348
349int
350pms_set_scaling(struct pms_softc *sc, int scale)
351{
352	u_char cmd[1];
353
354	switch (scale) {
355	case 1:
356	default:
357		cmd[0] = PMS_SET_SCALE11;
358		break;
359	case 2:
360		cmd[0] = PMS_SET_SCALE21;
361		break;
362	}
363	return (pms_cmd(sc, cmd, 1, NULL, 0));
364}
365
366int
367pms_reset(struct pms_softc *sc)
368{
369	u_char cmd[1], resp[2];
370	int res;
371
372	cmd[0] = PMS_RESET;
373	res = pms_cmd(sc, cmd, 1, resp, 2);
374#ifdef DEBUG
375	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0)
376		printf("%s: reset error %d (response 0x%02x, type 0x%02x)\n",
377		    DEVNAME(sc), res, resp[0], resp[1]);
378#endif
379	return (res);
380}
381
382int
383pms_dev_enable(struct pms_softc *sc)
384{
385	u_char cmd[1];
386	int res;
387
388	cmd[0] = PMS_DEV_ENABLE;
389	res = pms_cmd(sc, cmd, 1, NULL, 0);
390	if (res)
391		printf("%s: enable error\n", DEVNAME(sc));
392	return (res);
393}
394
395int
396pms_dev_disable(struct pms_softc *sc)
397{
398	u_char cmd[1];
399	int res;
400
401	cmd[0] = PMS_DEV_DISABLE;
402	res = pms_cmd(sc, cmd, 1, NULL, 0);
403	if (res)
404		printf("%s: disable error\n", DEVNAME(sc));
405	return (res);
406}
407
408int
409pms_enable_intelli(struct pms_softc *sc)
410{
411	u_char resp;
412
413	/* the special sequence to enable the third button and the roller */
414	if (pms_set_rate(sc, PMS_INTELLI_MAGIC1) ||
415	    pms_set_rate(sc, PMS_INTELLI_MAGIC2) ||
416	    pms_set_rate(sc, PMS_INTELLI_MAGIC3) ||
417	    pms_get_devid(sc, &resp) ||
418	    resp != PMS_INTELLI_ID)
419		return (0);
420
421	return (1);
422}
423
424int
425pms_ioctl_mouse(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
426    struct proc *p)
427{
428	int i;
429
430	switch (cmd) {
431	case WSMOUSEIO_GTYPE:
432		*(u_int *)data = WSMOUSE_TYPE_PS2;
433		break;
434	case WSMOUSEIO_SRES:
435		i = ((int) *(u_int *)data - 12) / 25;
436		/* valid values are {0,1,2,3} */
437		if (i < 0)
438			i = 0;
439		if (i > 3)
440			i = 3;
441
442		if (pms_set_resolution(sc, i))
443			printf("%s: SET_RES command error\n", DEVNAME(sc));
444		break;
445	default:
446		return (-1);
447	}
448	return (0);
449}
450
451int
452pms_sync_mouse(struct pms_softc *sc, int data)
453{
454	if (sc->inputstate != 0)
455		return (0);
456
457	switch (sc->protocol->type) {
458	case PMS_STANDARD:
459		if ((data & 0xc0) != 0)
460			return (-1);
461		break;
462	case PMS_INTELLI:
463		if ((data & 0x08) != 0x08)
464			return (-1);
465		break;
466	}
467
468	return (0);
469}
470
471void
472pms_proc_mouse(struct pms_softc *sc)
473{
474	u_int buttons;
475	int  dx, dy, dz;
476
477	buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK];
478	dx = (sc->packet[0] & PMS_PS2_XNEG) ?
479	    (int)sc->packet[1] - 256 : sc->packet[1];
480	dy = (sc->packet[0] & PMS_PS2_YNEG) ?
481	    (int)sc->packet[2] - 256 : sc->packet[2];
482
483	switch (sc->protocol->type) {
484	case PMS_STANDARD:
485		dz = 0;
486		break;
487	case PMS_INTELLI:
488		dz = (signed char)sc->packet[3];
489		break;
490	}
491
492	wsmouse_input(sc->sc_wsmousedev,
493	    buttons, dx, dy, dz, 0, WSMOUSE_INPUT_DELTA);
494}
495
496int
497pmsprobe(struct device *parent, void *match, void *aux)
498{
499	struct pckbc_attach_args *pa = aux;
500	u_char cmd[1], resp[2];
501	int res;
502
503	if (pa->pa_slot != PCKBC_AUX_SLOT)
504		return (0);
505
506	/* Flush any garbage. */
507	pckbc_flush(pa->pa_tag, pa->pa_slot);
508
509	/* reset the device */
510	cmd[0] = PMS_RESET;
511	res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
512	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
513#ifdef DEBUG
514		printf("pms: reset error %d (response 0x%02x, type 0x%02x)\n",
515		    res, resp[0], resp[1]);
516#endif
517		return (0);
518	}
519
520	return (1);
521}
522
523void
524pmsattach(struct device *parent, struct device *self, void *aux)
525{
526	struct pms_softc *sc = (void *)self;
527	struct pckbc_attach_args *pa = aux;
528	struct wsmousedev_attach_args a;
529	int i;
530
531	sc->sc_kbctag = pa->pa_tag;
532
533	printf("\n");
534
535	pckbc_set_inputhandler(sc->sc_kbctag, PCKBC_AUX_SLOT,
536	    pmsinput, sc, DEVNAME(sc));
537
538	a.accessops = &pms_accessops;
539	a.accesscookie = sc;
540
541	/*
542	 * Attach the wsmouse, saving a handle to it.
543	 * Note that we don't need to check this pointer against NULL
544	 * here or in pmsintr, because if this fails pms_enable() will
545	 * never be called, so pmsinput() will never be called.
546	 */
547	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
548
549	sc->poll = 1;
550	sc->sc_dev_enable = 0;
551
552	sc->protocol = &pms_protocols[0];
553	for (i = 1; i < nitems(pms_protocols); i++) {
554		pms_reset(sc);
555		if (pms_protocols[i].enable(sc))
556			sc->protocol = &pms_protocols[i];
557	}
558
559	DPRINTF("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type);
560
561	/* no interrupts until enabled */
562	pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_IGNORE);
563}
564
565int
566pmsactivate(struct device *self, int act)
567{
568	struct pms_softc *sc = (struct pms_softc *)self;
569
570	switch (act) {
571	case DVACT_SUSPEND:
572		if (sc->sc_state == PMS_STATE_ENABLED)
573			pms_change_state(sc, PMS_STATE_SUSPENDED,
574			    PMS_DEV_IGNORE);
575		break;
576	case DVACT_RESUME:
577		if (sc->sc_state == PMS_STATE_SUSPENDED)
578			pms_change_state(sc, PMS_STATE_ENABLED,
579			    PMS_DEV_IGNORE);
580		break;
581	}
582	return (0);
583}
584
585int
586pms_change_state(struct pms_softc *sc, int newstate, int dev)
587{
588	int i;
589
590	if (dev != PMS_DEV_IGNORE) {
591		switch (newstate) {
592		case PMS_STATE_ENABLED:
593			if (sc->sc_dev_enable & dev)
594				return (EBUSY);
595
596			sc->sc_dev_enable |= dev;
597
598			if (sc->sc_state == PMS_STATE_ENABLED)
599				return (0);
600
601			break;
602		case PMS_STATE_DISABLED:
603			sc->sc_dev_enable &= ~dev;
604
605			if (sc->sc_dev_enable)
606				return (0);
607
608			break;
609		}
610	}
611
612	switch (newstate) {
613	case PMS_STATE_ENABLED:
614		sc->inputstate = 0;
615
616		pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 1);
617
618		if (sc->poll)
619			pckbc_flush(sc->sc_kbctag, PCKBC_AUX_SLOT);
620
621		pms_reset(sc);
622
623		if (sc->protocol->type != PMS_STANDARD &&
624		    sc->protocol->enable(sc) == 0)
625			sc->protocol = &pms_protocols[0];
626
627		if (sc->protocol->type == PMS_STANDARD)
628			for (i = 1; i < nitems(pms_protocols); i++) {
629				pms_reset(sc);
630				if (pms_protocols[i].enable(sc))
631					sc->protocol = &pms_protocols[i];
632			}
633
634#ifdef DEBUG
635		printf("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type);
636#endif
637
638		pms_dev_enable(sc);
639		break;
640	case PMS_STATE_DISABLED:
641	case PMS_STATE_SUSPENDED:
642		pms_dev_disable(sc);
643
644		if (sc->protocol && sc->protocol->disable)
645			sc->protocol->disable(sc);
646
647		pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 0);
648		break;
649	}
650
651	sc->sc_state = newstate;
652	sc->poll = (newstate == PMS_STATE_SUSPENDED) ? 1 : 0;
653
654	return (0);
655}
656
657int
658pms_enable(void *v)
659{
660	struct pms_softc *sc = v;
661
662	return pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_PRIMARY);
663}
664
665void
666pms_disable(void *v)
667{
668	struct pms_softc *sc = v;
669
670	pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_PRIMARY);
671}
672
673int
674pms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
675{
676	struct pms_softc *sc = v;
677
678	if (sc->protocol && sc->protocol->ioctl)
679		return (sc->protocol->ioctl(sc, cmd, data, flag, p));
680	else
681		return (-1);
682}
683
684void
685pmsinput(void *vsc, int data)
686{
687	struct pms_softc *sc = vsc;
688
689	if (sc->sc_state != PMS_STATE_ENABLED) {
690		/* Interrupts are not expected.  Discard the byte. */
691		return;
692	}
693
694	if (sc->protocol->sync(sc, data)) {
695#ifdef DIAGNOSTIC
696		printf("%s: not in sync yet, discard input\n", DEVNAME(sc));
697#endif
698		sc->inputstate = 0;
699		return;
700	}
701
702	sc->packet[sc->inputstate++] = data;
703	if (sc->inputstate != sc->protocol->packetsize)
704		return;
705
706	sc->protocol->proc(sc);
707	sc->inputstate = 0;
708}
709
710int
711synaptics_set_mode(struct pms_softc *sc, int mode)
712{
713	struct synaptics_softc *syn = sc->synaptics;
714
715	if (pms_spec_cmd(sc, mode) ||
716	    pms_set_rate(sc, SYNAPTICS_CMD_SET_MODE))
717		return (-1);
718
719	syn->mode = mode;
720
721	return (0);
722}
723
724int
725synaptics_query(struct pms_softc *sc, int query, int *val)
726{
727	u_char resp[3];
728
729	if (pms_spec_cmd(sc, query) ||
730	    pms_get_status(sc, resp))
731		return (-1);
732
733	if (val)
734		*val = (resp[0] << 16) | (resp[1] << 8) | resp[2];
735
736	return (0);
737}
738
739int
740synaptics_get_hwinfo(struct pms_softc *sc)
741{
742	struct synaptics_softc *syn = sc->synaptics;
743
744	if (synaptics_query(sc, SYNAPTICS_QUE_IDENTIFY, &syn->identify))
745		return (-1);
746	if (synaptics_query(sc, SYNAPTICS_QUE_CAPABILITIES,
747	    &syn->capabilities))
748		return (-1);
749	if (synaptics_query(sc, SYNAPTICS_QUE_MODEL, &syn->model))
750		return (-1);
751	if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 1) &&
752	    synaptics_query(sc, SYNAPTICS_QUE_EXT_MODEL, &syn->ext_model))
753		return (-1);
754	if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 4) &&
755	    synaptics_query(sc, SYNAPTICS_QUE_EXT_CAPABILITIES,
756		&syn->ext_capabilities))
757		return (-1);
758	if ((SYNAPTICS_ID_MAJOR(syn->identify) >= 4) &&
759	    synaptics_query(sc, SYNAPTICS_QUE_RESOLUTION, &syn->resolution))
760		return (-1);
761	if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 5) &&
762	    (syn->ext_capabilities & SYNAPTICS_EXT_CAP_MAX_DIMENSIONS) &&
763	    synaptics_query(sc, SYNAPTICS_QUE_EXT_DIMENSIONS, &syn->dimension))
764		return (-1);
765
766	syn->res_x = SYNAPTICS_RESOLUTION_X(syn->resolution);
767	syn->res_y = SYNAPTICS_RESOLUTION_Y(syn->resolution);
768	syn->min_x = SYNAPTICS_XMIN_BEZEL;
769	syn->min_y = SYNAPTICS_YMIN_BEZEL;
770	syn->max_x = (syn->dimension) ?
771	    SYNAPTICS_DIM_X(syn->dimension) : SYNAPTICS_XMAX_BEZEL;
772	syn->max_y = (syn->dimension) ?
773	    SYNAPTICS_DIM_Y(syn->dimension) : SYNAPTICS_YMAX_BEZEL;
774
775	if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) > 8)
776		syn->ext_model &= ~0xf000;
777
778	return (0);
779}
780
781void
782synaptics_pt_proc(struct pms_softc *sc)
783{
784	u_int buttons;
785	int dx, dy;
786
787	if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) == 0)
788		return;
789
790	buttons = butmap[sc->packet[1] & PMS_PS2_BUTTONSMASK];
791	dx = (sc->packet[1] & PMS_PS2_XNEG) ?
792	    (int)sc->packet[4] - 256 : sc->packet[4];
793	dy = (sc->packet[1] & PMS_PS2_YNEG) ?
794	    (int)sc->packet[5] - 256 : sc->packet[5];
795
796	wsmouse_input(sc->sc_pt_wsmousedev,
797	    buttons, dx, dy, 0, 0, WSMOUSE_INPUT_DELTA);
798}
799
800int
801synaptics_pt_enable(void *v)
802{
803	struct pms_softc *sc = v;
804
805	return (pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_SECONDARY));
806}
807
808void
809synaptics_pt_disable(void *v)
810{
811	struct pms_softc *sc = v;
812
813	pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_SECONDARY);
814}
815
816int
817synaptics_pt_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
818{
819	switch (cmd) {
820	case WSMOUSEIO_GTYPE:
821		*(u_int *)data = WSMOUSE_TYPE_PS2;
822		break;
823	default:
824		return (-1);
825	}
826	return (0);
827}
828
829int
830pms_enable_synaptics(struct pms_softc *sc)
831{
832	struct synaptics_softc *syn = sc->synaptics;
833	struct wsmousedev_attach_args a;
834	u_char resp[3];
835	int mode;
836
837	if (pms_set_resolution(sc, 0) ||
838	    pms_set_resolution(sc, 0) ||
839	    pms_set_resolution(sc, 0) ||
840	    pms_set_resolution(sc, 0) ||
841	    pms_get_status(sc, resp) ||
842	    resp[1] != SYNAPTICS_ID_MAGIC)
843		return (0);
844
845	if (sc->synaptics == NULL) {
846		sc->synaptics = syn = malloc(sizeof(struct synaptics_softc),
847		    M_DEVBUF, M_WAITOK | M_ZERO);
848		if (syn == NULL) {
849			printf("%s: synaptics: not enough memory\n",
850			    DEVNAME(sc));
851			return (0);
852		}
853
854		if (synaptics_get_hwinfo(sc))
855			return (0);
856
857		if ((syn->model & SYNAPTICS_MODEL_NEWABS) == 0) {
858			printf("%s: don't support Synaptics OLDABS\n",
859			    DEVNAME(sc));
860			return (0);
861		}
862
863		/* enable pass-through PS/2 port if supported */
864		if (syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) {
865			a.accessops = &synaptics_pt_accessops;
866			a.accesscookie = sc;
867			sc->sc_pt_wsmousedev = config_found((void *)sc, &a,
868			    wsmousedevprint);
869		}
870
871		syn->wsmode = WSMOUSE_COMPAT;
872
873		printf("%s: Synaptics %s, firmware %d.%d\n", DEVNAME(sc),
874		    (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD ?
875			"clickpad" : "touchpad"),
876		    SYNAPTICS_ID_MAJOR(syn->identify),
877		    SYNAPTICS_ID_MINOR(syn->identify));
878	}
879
880	mode = SYNAPTICS_ABSOLUTE_MODE | SYNAPTICS_HIGH_RATE;
881	if (SYNAPTICS_ID_MAJOR(syn->identify) >= 4)
882		mode |= SYNAPTICS_DISABLE_GESTURE;
883	if (syn->capabilities & SYNAPTICS_CAP_EXTENDED)
884		mode |= SYNAPTICS_W_MODE;
885	if (synaptics_set_mode(sc, mode))
886		return (0);
887
888	/* enable advanced gesture mode if supported */
889	if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_ADV_GESTURE) &&
890	    (pms_spec_cmd(sc, SYNAPTICS_QUE_MODEL) ||
891	     pms_set_rate(sc, SYNAPTICS_CMD_SET_ADV_GESTURE_MODE)))
892		return (0);
893
894	return (1);
895}
896
897int
898pms_ioctl_synaptics(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
899    struct proc *p)
900{
901	struct synaptics_softc *syn = sc->synaptics;
902	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
903	int wsmode;
904
905	switch (cmd) {
906	case WSMOUSEIO_GTYPE:
907		*(u_int *)data = WSMOUSE_TYPE_SYNAPTICS;
908		break;
909	case WSMOUSEIO_GCALIBCOORDS:
910		wsmc->minx = syn->min_x;
911		wsmc->maxx = syn->max_x;
912		wsmc->miny = syn->min_y;
913		wsmc->maxy = syn->max_y;
914		wsmc->swapxy = 0;
915		wsmc->resx = syn->res_x;
916		wsmc->resy = syn->res_y;
917		break;
918	case WSMOUSEIO_SETMODE:
919		wsmode = *(u_int *)data;
920		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
921			return (EINVAL);
922		syn->wsmode = wsmode;
923		break;
924	default:
925		return (-1);
926	}
927	return (0);
928}
929
930int
931pms_sync_synaptics(struct pms_softc *sc, int data)
932{
933	switch (sc->inputstate) {
934	case 0:
935		if ((data & 0xc8) != 0x80)
936			return (-1);
937		break;
938	case 3:
939		if ((data & 0xc8) != 0xc0)
940			return (-1);
941		break;
942	}
943
944	return (0);
945}
946
947void
948pms_proc_synaptics(struct pms_softc *sc)
949{
950	struct synaptics_softc *syn = sc->synaptics;
951	u_int buttons;
952	int x, y, z, w, dx, dy;
953
954	w = ((sc->packet[0] & 0x30) >> 2) | ((sc->packet[0] & 0x04) >> 1) |
955	    ((sc->packet[3] & 0x04) >> 2);
956
957	if ((syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) && w == 3) {
958		synaptics_pt_proc(sc);
959		return;
960	}
961
962	if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0)
963		return;
964
965	/* XXX ignore advanced gesture packet, not yet supported */
966	if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_ADV_GESTURE) && w == 2)
967		return;
968
969	x = ((sc->packet[3] & 0x10) << 8) | ((sc->packet[1] & 0x0f) << 8) |
970	    sc->packet[4];
971	y = ((sc->packet[3] & 0x20) << 7) | ((sc->packet[1] & 0xf0) << 4) |
972	    sc->packet[5];
973	z = sc->packet[2];
974
975	buttons = ((sc->packet[0] & sc->packet[3]) & 0x01) ?
976	    WSMOUSE_BUTTON(1) : 0;
977	buttons |= ((sc->packet[0] & sc->packet[3]) & 0x02) ?
978	    WSMOUSE_BUTTON(3) : 0;
979
980	if (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) {
981		buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
982		    WSMOUSE_BUTTON(1) : 0;
983	} else if (syn->capabilities & SYNAPTICS_CAP_MIDDLE_BUTTON) {
984		buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
985		    WSMOUSE_BUTTON(2) : 0;
986	}
987
988	if (syn->capabilities & SYNAPTICS_CAP_FOUR_BUTTON) {
989		buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
990		    WSMOUSE_BUTTON(4) : 0;
991		buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x02) ?
992		    WSMOUSE_BUTTON(5) : 0;
993	} else if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) &&
994	    ((sc->packet[0] ^ sc->packet[3]) & 0x02)) {
995		buttons |= (sc->packet[4] & 0x01) ? WSMOUSE_BUTTON(6) : 0;
996		buttons |= (sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(7) : 0;
997		buttons |= (sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(8) : 0;
998		buttons |= (sc->packet[5] & 0x02) ? WSMOUSE_BUTTON(9) : 0;
999		buttons |= (sc->packet[4] & 0x04) ? WSMOUSE_BUTTON(10) : 0;
1000		buttons |= (sc->packet[5] & 0x04) ? WSMOUSE_BUTTON(11) : 0;
1001		buttons |= (sc->packet[4] & 0x08) ? WSMOUSE_BUTTON(12) : 0;
1002		buttons |= (sc->packet[5] & 0x08) ? WSMOUSE_BUTTON(13) : 0;
1003		x &= ~0x0f;
1004		y &= ~0x0f;
1005	}
1006
1007	/* ignore final events that happen when removing all fingers */
1008	if (x <= 1 || y <= 1) {
1009		x = syn->old_x;
1010		y = syn->old_y;
1011	}
1012
1013	if (syn->wsmode == WSMOUSE_NATIVE) {
1014		wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w,
1015		    WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
1016		    WSMOUSE_INPUT_ABSOLUTE_Z | WSMOUSE_INPUT_ABSOLUTE_W);
1017	} else {
1018		dx = dy = 0;
1019		if (z > SYNAPTICS_PRESSURE) {
1020			dx = x - syn->old_x;
1021			dy = y - syn->old_y;
1022			dx /= SYNAPTICS_SCALE;
1023			dy /= SYNAPTICS_SCALE;
1024		}
1025		if (dx || dy || buttons != syn->old_buttons)
1026			wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
1027			    WSMOUSE_INPUT_DELTA);
1028		syn->old_buttons = buttons;
1029	}
1030
1031	syn->old_x = x;
1032	syn->old_y = y;
1033}
1034
1035void
1036pms_disable_synaptics(struct pms_softc *sc)
1037{
1038	struct synaptics_softc *syn = sc->synaptics;
1039
1040	if (syn->capabilities & SYNAPTICS_CAP_SLEEP)
1041		synaptics_set_mode(sc, SYNAPTICS_SLEEP_MODE |
1042		    SYNAPTICS_DISABLE_GESTURE);
1043}
1044
1045int
1046alps_get_hwinfo(struct pms_softc *sc)
1047{
1048	struct alps_softc *alps = sc->alps;
1049	u_char resp[3];
1050	int i;
1051
1052	if (pms_set_resolution(sc, 0) ||
1053	    pms_set_scaling(sc, 2) ||
1054	    pms_set_scaling(sc, 2) ||
1055	    pms_set_scaling(sc, 2) ||
1056	    pms_get_status(sc, resp)) {
1057		DPRINTF("%s: alps: model query error\n", DEVNAME(sc));
1058		return (-1);
1059	}
1060
1061	alps->version = (resp[0] << 8) | (resp[1] << 4) | (resp[2] / 20 + 1);
1062
1063	for (i = 0; i < nitems(alps_models); i++)
1064		if (alps->version == alps_models[i].version) {
1065			alps->model = alps_models[i].model;
1066			alps->mask = alps_models[i].mask;
1067			return (0);
1068		}
1069
1070	return (-1);
1071
1072}
1073
1074int
1075pms_enable_alps(struct pms_softc *sc)
1076{
1077	struct alps_softc *alps = sc->alps;
1078	struct wsmousedev_attach_args a;
1079	u_char resp[3];
1080
1081	if (pms_set_resolution(sc, 0) ||
1082	    pms_set_scaling(sc, 1) ||
1083	    pms_set_scaling(sc, 1) ||
1084	    pms_set_scaling(sc, 1) ||
1085	    pms_get_status(sc, resp) ||
1086	    resp[0] != PMS_ALPS_MAGIC1 ||
1087	    resp[1] != PMS_ALPS_MAGIC2 ||
1088	    (resp[2] != PMS_ALPS_MAGIC3_1 && resp[2] != PMS_ALPS_MAGIC3_2))
1089		return (0);
1090
1091	if (sc->alps == NULL) {
1092		sc->alps = alps = malloc(sizeof(struct alps_softc),
1093		    M_DEVBUF, M_WAITOK | M_ZERO);
1094		if (alps == NULL) {
1095			printf("%s: alps: not enough memory\n", DEVNAME(sc));
1096			goto err;
1097		}
1098
1099		if (alps_get_hwinfo(sc))
1100			goto err;
1101
1102		printf("%s: ALPS %s, version 0x%04x\n", DEVNAME(sc),
1103		    (alps->model & ALPS_DUALPOINT ? "Dualpoint" : "Glidepoint"),
1104		    alps->version);
1105
1106		alps->min_x = ALPS_XMIN_BEZEL;
1107		alps->min_y = ALPS_YMIN_BEZEL;
1108		alps->max_x = ALPS_XMAX_BEZEL;
1109		alps->max_y = ALPS_YMAX_BEZEL;
1110
1111		alps->wsmode = WSMOUSE_COMPAT;
1112
1113		if (alps->model & ALPS_DUALPOINT) {
1114			a.accessops = &synaptics_pt_accessops;
1115			a.accesscookie = sc;
1116			sc->sc_pt_wsmousedev = config_found((void *)sc, &a,
1117			    wsmousedevprint);
1118		}
1119	}
1120
1121	if (alps->model == 0)
1122		goto err;
1123
1124	if ((alps->model & ALPS_PASSTHROUGH) &&
1125	   (pms_set_scaling(sc, 2) ||
1126	    pms_set_scaling(sc, 2) ||
1127	    pms_set_scaling(sc, 2) ||
1128	    pms_dev_disable(sc))) {
1129		DPRINTF("%s: alps: passthrough on error\n", DEVNAME(sc));
1130		goto err;
1131	}
1132
1133	if (pms_dev_disable(sc) ||
1134	    pms_dev_disable(sc) ||
1135	    pms_set_rate(sc, 0x0a)) {
1136		DPRINTF("%s: alps: tapping error\n", DEVNAME(sc));
1137		goto err;
1138	}
1139
1140	if (pms_dev_disable(sc) ||
1141	    pms_dev_disable(sc) ||
1142	    pms_dev_disable(sc) ||
1143	    pms_dev_disable(sc) ||
1144	    pms_dev_enable(sc)) {
1145		DPRINTF("%s: alps: absolute mode error\n", DEVNAME(sc));
1146		goto err;
1147	}
1148
1149	if ((alps->model & ALPS_PASSTHROUGH) &&
1150	   (pms_set_scaling(sc, 1) ||
1151	    pms_set_scaling(sc, 1) ||
1152	    pms_set_scaling(sc, 1) ||
1153	    pms_dev_disable(sc))) {
1154		DPRINTF("%s: alps: passthrough off error\n", DEVNAME(sc));
1155		goto err;
1156	}
1157
1158	return (1);
1159
1160err:
1161	pms_reset(sc);
1162
1163	return (0);
1164}
1165
1166int
1167pms_ioctl_alps(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
1168    struct proc *p)
1169{
1170	struct alps_softc *alps = sc->alps;
1171	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
1172	int wsmode;
1173
1174	switch (cmd) {
1175	case WSMOUSEIO_GTYPE:
1176		*(u_int *)data = WSMOUSE_TYPE_ALPS;
1177		break;
1178	case WSMOUSEIO_GCALIBCOORDS:
1179		wsmc->minx = alps->min_x;
1180		wsmc->maxx = alps->max_x;
1181		wsmc->miny = alps->min_y;
1182		wsmc->maxy = alps->max_y;
1183		wsmc->swapxy = 0;
1184		break;
1185	case WSMOUSEIO_SETMODE:
1186		wsmode = *(u_int *)data;
1187		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
1188			return (EINVAL);
1189		alps->wsmode = wsmode;
1190		break;
1191	default:
1192		return (-1);
1193	}
1194	return (0);
1195}
1196
1197int
1198pms_sync_alps(struct pms_softc *sc, int data)
1199{
1200	struct alps_softc *alps = sc->alps;
1201
1202	switch (sc->inputstate) {
1203	case 0:
1204		if ((data & alps->mask) != alps->mask)
1205			return (-1);
1206		break;
1207	case 1:
1208	case 2:
1209	case 3:
1210	case 4:
1211	case 5:
1212		if ((data & 0x80) != 0)
1213			return (-1);
1214		break;
1215	}
1216
1217	return (0);
1218}
1219
1220void
1221pms_proc_alps(struct pms_softc *sc)
1222{
1223	struct alps_softc *alps = sc->alps;
1224	int x, y, z, dx, dy;
1225	u_int buttons;
1226	int fin, ges;
1227
1228	x = sc->packet[1] | ((sc->packet[2] & 0x78) << 4);
1229	y = sc->packet[4] | ((sc->packet[3] & 0x70) << 3);
1230	z = sc->packet[5];
1231
1232	buttons = ((sc->packet[3] & 1) ? WSMOUSE_BUTTON(1) : 0) |
1233	    ((sc->packet[3] & 2) ? WSMOUSE_BUTTON(3) : 0) |
1234	    ((sc->packet[3] & 4) ? WSMOUSE_BUTTON(2) : 0);
1235
1236	if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) && z == ALPS_Z_MAGIC) {
1237		dx = (x > ALPS_XSEC_BEZEL / 2) ? (x - ALPS_XSEC_BEZEL) : x;
1238		dy = (y > ALPS_YSEC_BEZEL / 2) ? (y - ALPS_YSEC_BEZEL) : y;
1239
1240		wsmouse_input(sc->sc_pt_wsmousedev, buttons, dx, dy, 0, 0,
1241		    WSMOUSE_INPUT_DELTA);
1242
1243		return;
1244	}
1245
1246	if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0)
1247		return;
1248
1249	/*
1250	 * XXX The Y-axis is in the oposit direction compared to
1251	 * Synaptics touchpads and PS/2 mouses.
1252	 * It's why we need to translate the y value here for both
1253	 * NATIVE and COMPAT modes.
1254	 */
1255	y = ALPS_YMAX_BEZEL - y + ALPS_YMIN_BEZEL;
1256
1257	if (alps->wsmode == WSMOUSE_NATIVE) {
1258		ges = sc->packet[2] & 0x01;
1259		fin = sc->packet[2] & 0x02;
1260
1261		/* Simulate click (tap) */
1262		if (ges && !fin)
1263			z = 35;
1264
1265		/* Generate a null pressure event (needed for tap & drag) */
1266		if (ges && fin && !alps->old_fin)
1267			z = 0;
1268
1269		wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, 0,
1270		    WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
1271		    WSMOUSE_INPUT_ABSOLUTE_Z);
1272
1273		alps->old_fin = fin;
1274	} else {
1275		dx = dy = 0;
1276		if (z > ALPS_PRESSURE) {
1277			dx = x - alps->old_x;
1278			dy = y - alps->old_y;
1279
1280			/* Prevent jump */
1281			dx = abs(dx) > 50 ? 0 : dx;
1282			dy = abs(dy) > 50 ? 0 : dy;
1283		}
1284
1285		if (dx || dy || buttons != alps->old_buttons)
1286			wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
1287			    WSMOUSE_INPUT_DELTA);
1288
1289		alps->old_x = x;
1290		alps->old_y = y;
1291		alps->old_buttons = buttons;
1292	}
1293}
1294