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