pms.c revision 1.19
1/* $OpenBSD: pms.c,v 1.19 2011/08/17 16:10:27 shadchin Exp $ */
2/* $NetBSD: psm.c,v 1.11 2000/06/05 22:20:57 sommerfeld Exp $ */
3
4/*-
5 * Copyright (c) 1994 Charles M. Hannum.
6 * Copyright (c) 1992, 1993 Erik Forsberg.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
18 * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
22 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/param.h>
28#include <sys/systm.h>
29#include <sys/device.h>
30#include <sys/ioctl.h>
31#include <sys/malloc.h>
32
33#include <machine/bus.h>
34
35#include <dev/ic/pckbcvar.h>
36
37#include <dev/pckbc/pmsreg.h>
38
39#include <dev/wscons/wsconsio.h>
40#include <dev/wscons/wsmousevar.h>
41
42#define DEVNAME(sc)	((sc)->sc_dev.dv_xname)
43
44#define WSMOUSE_BUTTON(x)	(1 << ((x) - 1))
45
46struct pms_softc;
47
48struct pms_protocol {
49	int type;
50#define PMS_STANDARD	0
51#define PMS_INTELLI	1
52#define PMS_SYNAPTICS	2
53	u_int packetsize;
54	int (*enable)(struct pms_softc *);
55	int (*ioctl)(struct pms_softc *, u_long, caddr_t, int, struct proc *);
56	int (*sync)(struct pms_softc *, int);
57	void (*proc)(struct pms_softc *);
58	void (*disable)(struct pms_softc *);
59};
60
61struct synaptics_softc {
62	int identify;
63	int capabilities, ext_capabilities;
64	int model, ext_model;
65	int resolution, dimension;
66
67	int mode;
68
69	int res_x, res_y;
70	int min_x, min_y;
71	int max_x, max_y;
72
73	/* Compat mode */
74	int wsmode;
75	int old_x, old_y;
76	u_int old_buttons;
77#define SYNAPTICS_SCALE		4
78#define SYNAPTICS_PRESSURE	30
79};
80
81struct pms_softc {		/* driver status information */
82	struct device sc_dev;
83
84	pckbc_tag_t sc_kbctag;
85
86	int sc_state;
87#define PMS_STATE_DISABLED	0
88#define PMS_STATE_ENABLED	1
89#define PMS_STATE_SUSPENDED	2
90
91	int sc_dev_enable;
92#define PMS_DEV_IGNORE		0x00
93#define PMS_DEV_PRIMARY		0x01
94#define PMS_DEV_SECONDARY	0x02
95
96	int poll;
97	int inputstate;
98
99	const struct pms_protocol *protocol;
100	struct synaptics_softc *synaptics;
101
102	u_char packet[8];
103
104	struct device *sc_wsmousedev;
105	struct device *sc_pt_wsmousedev;
106};
107
108static const u_int butmap[8] = {
109	0,
110	WSMOUSE_BUTTON(1),
111	WSMOUSE_BUTTON(3),
112	WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(3),
113	WSMOUSE_BUTTON(2),
114	WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2),
115	WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3),
116	WSMOUSE_BUTTON(1) | WSMOUSE_BUTTON(2) | WSMOUSE_BUTTON(3)
117};
118
119int	pmsprobe(struct device *, void *, void *);
120void	pmsattach(struct device *, struct device *, void *);
121int	pmsactivate(struct device *, int);
122
123void	pmsinput(void *, int);
124
125int	pms_change_state(struct pms_softc *, int, int);
126int	pms_ioctl(void *, u_long, caddr_t, int, struct proc *);
127int	pms_enable(void *);
128void	pms_disable(void *);
129
130int	pms_cmd(struct pms_softc *, u_char *, int, u_char *, int);
131int	pms_spec_cmd(struct pms_softc *, int);
132int	pms_get_devid(struct pms_softc *, u_char *);
133int	pms_get_status(struct pms_softc *, u_char *);
134int	pms_set_rate(struct pms_softc *, int);
135int	pms_set_resolution(struct pms_softc *, int);
136int	pms_set_scaling(struct pms_softc *, int);
137int	pms_reset(struct pms_softc *);
138int	pms_dev_enable(struct pms_softc *);
139int	pms_dev_disable(struct pms_softc *);
140
141int	pms_enable_intelli(struct pms_softc *);
142
143int	pms_ioctl_mouse(struct pms_softc *, u_long, caddr_t, int, struct proc *);
144int	pms_sync_mouse(struct pms_softc *, int);
145void	pms_proc_mouse(struct pms_softc *);
146
147int	pms_enable_synaptics(struct pms_softc *);
148int	pms_ioctl_synaptics(struct pms_softc *, u_long, caddr_t, int, struct proc *);
149int	pms_sync_synaptics(struct pms_softc *, int);
150void	pms_proc_synaptics(struct pms_softc *);
151void	pms_disable_synaptics(struct pms_softc *);
152
153int	synaptics_set_mode(struct pms_softc *, int);
154int	synaptics_query(struct pms_softc *, int, int *);
155int	synaptics_get_hwinfo(struct pms_softc *);
156
157void	synaptics_pt_proc(struct pms_softc *);
158
159int	synaptics_pt_ioctl(void *, u_long, caddr_t, int, struct proc *);
160int	synaptics_pt_enable(void *);
161void	synaptics_pt_disable(void *);
162
163struct cfattach pms_ca = {
164	sizeof(struct pms_softc), pmsprobe, pmsattach, NULL,
165	pmsactivate
166};
167
168struct cfdriver pms_cd = {
169	NULL, "pms", DV_DULL
170};
171
172const struct wsmouse_accessops pms_accessops = {
173	pms_enable,
174	pms_ioctl,
175	pms_disable,
176};
177
178const struct wsmouse_accessops synaptics_pt_accessops = {
179	synaptics_pt_enable,
180	synaptics_pt_ioctl,
181	synaptics_pt_disable,
182};
183
184const struct pms_protocol pms_protocols[] = {
185	/* Generic PS/2 mouse */
186	{
187		PMS_STANDARD, 3,
188		NULL,
189		pms_ioctl_mouse,
190		pms_sync_mouse,
191		pms_proc_mouse,
192		NULL
193	},
194	/* Microsoft IntelliMouse */
195	{
196		PMS_INTELLI, 4,
197		pms_enable_intelli,
198		pms_ioctl_mouse,
199		pms_sync_mouse,
200		pms_proc_mouse,
201		NULL
202	},
203	/* Synaptics touchpad */
204	{
205		PMS_SYNAPTICS, 6,
206		pms_enable_synaptics,
207		pms_ioctl_synaptics,
208		pms_sync_synaptics,
209		pms_proc_synaptics,
210		pms_disable_synaptics
211	}
212};
213
214int
215pms_cmd(struct pms_softc *sc, u_char *cmd, int len, u_char *resp, int resplen)
216{
217	if (sc->poll) {
218		return pckbc_poll_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT,
219		    cmd, len, resplen, resp, 1);
220	} else {
221		return pckbc_enqueue_cmd(sc->sc_kbctag, PCKBC_AUX_SLOT,
222		    cmd, len, resplen, 1, resp);
223	}
224}
225
226int
227pms_spec_cmd(struct pms_softc *sc, int cmd)
228{
229	if (pms_set_scaling(sc, 1) ||
230	    pms_set_resolution(sc, (cmd >> 6) & 0x03) ||
231	    pms_set_resolution(sc, (cmd >> 4) & 0x03) ||
232	    pms_set_resolution(sc, (cmd >> 2) & 0x03) ||
233	    pms_set_resolution(sc, (cmd >> 0) & 0x03))
234		return (-1);
235	return (0);
236}
237
238int
239pms_get_devid(struct pms_softc *sc, u_char *resp)
240{
241	u_char cmd[1];
242
243	cmd[0] = PMS_SEND_DEV_ID;
244	return (pms_cmd(sc, cmd, 1, resp, 1));
245}
246
247int
248pms_get_status(struct pms_softc *sc, u_char *resp)
249{
250	u_char cmd[1];
251
252	cmd[0] = PMS_SEND_DEV_STATUS;
253	return (pms_cmd(sc, cmd, 1, resp, 3));
254}
255
256int
257pms_set_rate(struct pms_softc *sc, int value)
258{
259	u_char cmd[2];
260
261	cmd[0] = PMS_SET_SAMPLE;
262	cmd[1] = value;
263	return (pms_cmd(sc, cmd, 2, NULL, 0));
264}
265
266int
267pms_set_resolution(struct pms_softc *sc, int value)
268{
269	u_char cmd[2];
270
271	cmd[0] = PMS_SET_RES;
272	cmd[1] = value;
273	return (pms_cmd(sc, cmd, 2, NULL, 0));
274}
275
276int
277pms_set_scaling(struct pms_softc *sc, int scale)
278{
279	u_char cmd[1];
280
281	switch (scale) {
282	case 1:
283	default:
284		cmd[0] = PMS_SET_SCALE11;
285		break;
286	case 2:
287		cmd[0] = PMS_SET_SCALE21;
288		break;
289	}
290	return (pms_cmd(sc, cmd, 1, NULL, 0));
291}
292
293int
294pms_reset(struct pms_softc *sc)
295{
296	u_char cmd[1], resp[2];
297	int res;
298
299	cmd[0] = PMS_RESET;
300	res = pms_cmd(sc, cmd, 1, resp, 2);
301#ifdef DEBUG
302	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0)
303		printf("%s: reset error %d (response 0x%02x, type 0x%02x)\n",
304		    DEVNAME(sc), res, resp[0], resp[1]);
305#endif
306	return (res);
307}
308
309int
310pms_dev_enable(struct pms_softc *sc)
311{
312	u_char cmd[1];
313	int res;
314
315	cmd[0] = PMS_DEV_ENABLE;
316	res = pms_cmd(sc, cmd, 1, NULL, 0);
317	if (res)
318		printf("%s: enable error\n", DEVNAME(sc));
319	return (res);
320}
321
322int
323pms_dev_disable(struct pms_softc *sc)
324{
325	u_char cmd[1];
326	int res;
327
328	cmd[0] = PMS_DEV_DISABLE;
329	res = pms_cmd(sc, cmd, 1, NULL, 0);
330	if (res)
331		printf("%s: disable error\n", DEVNAME(sc));
332	return (res);
333}
334
335int
336pms_enable_intelli(struct pms_softc *sc)
337{
338	u_char resp;
339
340	/* the special sequence to enable the third button and the roller */
341	if (pms_set_rate(sc, PMS_INTELLI_MAGIC1) ||
342	    pms_set_rate(sc, PMS_INTELLI_MAGIC2) ||
343	    pms_set_rate(sc, PMS_INTELLI_MAGIC3) ||
344	    pms_get_devid(sc, &resp) ||
345	    resp != PMS_INTELLI_ID)
346		return (0);
347
348	return (1);
349}
350
351int
352pms_ioctl_mouse(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
353    struct proc *p)
354{
355	int i;
356
357	switch (cmd) {
358	case WSMOUSEIO_GTYPE:
359		*(u_int *)data = WSMOUSE_TYPE_PS2;
360		break;
361	case WSMOUSEIO_SRES:
362		i = ((int) *(u_int *)data - 12) / 25;
363		/* valid values are {0,1,2,3} */
364		if (i < 0)
365			i = 0;
366		if (i > 3)
367			i = 3;
368
369		if (pms_set_resolution(sc, i))
370			printf("%s: SET_RES command error\n", DEVNAME(sc));
371		break;
372	default:
373		return (-1);
374	}
375	return (0);
376}
377
378int
379pms_sync_mouse(struct pms_softc *sc, int data)
380{
381	if (sc->inputstate != 0)
382		return (0);
383
384	switch (sc->protocol->type) {
385	case PMS_STANDARD:
386		if ((data & 0xc0) != 0)
387			return (-1);
388		break;
389	case PMS_INTELLI:
390		if ((data & 0x08) != 0x08)
391			return (-1);
392		break;
393	}
394
395	return (0);
396}
397
398void
399pms_proc_mouse(struct pms_softc *sc)
400{
401	u_int buttons;
402	int  dx, dy, dz;
403
404	buttons = butmap[sc->packet[0] & PMS_PS2_BUTTONSMASK];
405	dx = (sc->packet[0] & PMS_PS2_XNEG) ?
406	    (int)sc->packet[1] - 256 : sc->packet[1];
407	dy = (sc->packet[0] & PMS_PS2_YNEG) ?
408	    (int)sc->packet[2] - 256 : sc->packet[2];
409
410	switch (sc->protocol->type) {
411	case PMS_STANDARD:
412		dz = 0;
413		break;
414	case PMS_INTELLI:
415		dz = (signed char)sc->packet[3];
416		break;
417	}
418
419	wsmouse_input(sc->sc_wsmousedev,
420	    buttons, dx, dy, dz, 0, WSMOUSE_INPUT_DELTA);
421}
422
423int
424pmsprobe(struct device *parent, void *match, void *aux)
425{
426	struct pckbc_attach_args *pa = aux;
427	u_char cmd[1], resp[2];
428	int res;
429
430	if (pa->pa_slot != PCKBC_AUX_SLOT)
431		return (0);
432
433	/* Flush any garbage. */
434	pckbc_flush(pa->pa_tag, pa->pa_slot);
435
436	/* reset the device */
437	cmd[0] = PMS_RESET;
438	res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
439	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
440#ifdef DEBUG
441		printf("pms: reset error %d (response 0x%02x, type 0x%02x)\n",
442		    res, resp[0], resp[1]);
443#endif
444		return (0);
445	}
446
447	return (1);
448}
449
450void
451pmsattach(struct device *parent, struct device *self, void *aux)
452{
453	struct pms_softc *sc = (void *)self;
454	struct pckbc_attach_args *pa = aux;
455	struct wsmousedev_attach_args a;
456	int i;
457
458	sc->sc_kbctag = pa->pa_tag;
459
460	printf("\n");
461
462	pckbc_set_inputhandler(sc->sc_kbctag, PCKBC_AUX_SLOT,
463	    pmsinput, sc, DEVNAME(sc));
464
465	a.accessops = &pms_accessops;
466	a.accesscookie = sc;
467
468	/*
469	 * Attach the wsmouse, saving a handle to it.
470	 * Note that we don't need to check this pointer against NULL
471	 * here or in pmsintr, because if this fails pms_enable() will
472	 * never be called, so pmsinput() will never be called.
473	 */
474	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
475
476	sc->poll = 1;
477	sc->sc_dev_enable = 0;
478
479	sc->protocol = &pms_protocols[0];
480	for (i = 1; i < nitems(pms_protocols); i++) {
481		pms_reset(sc);
482		if (pms_protocols[i].enable(sc))
483			sc->protocol = &pms_protocols[i];
484	}
485
486	/* no interrupts until enabled */
487	pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_IGNORE);
488}
489
490int
491pmsactivate(struct device *self, int act)
492{
493	struct pms_softc *sc = (struct pms_softc *)self;
494
495	switch (act) {
496	case DVACT_SUSPEND:
497		if (sc->sc_state == PMS_STATE_ENABLED)
498			pms_change_state(sc, PMS_STATE_SUSPENDED,
499			    PMS_DEV_IGNORE);
500		break;
501	case DVACT_RESUME:
502		if (sc->sc_state == PMS_STATE_SUSPENDED)
503			pms_change_state(sc, PMS_STATE_ENABLED,
504			    PMS_DEV_IGNORE);
505		break;
506	}
507	return (0);
508}
509
510int
511pms_change_state(struct pms_softc *sc, int newstate, int dev)
512{
513	int i;
514
515	if (dev != PMS_DEV_IGNORE) {
516		switch (newstate) {
517		case PMS_STATE_ENABLED:
518			if (sc->sc_dev_enable & dev)
519				return (EBUSY);
520
521			sc->sc_dev_enable |= dev;
522
523			if (sc->sc_state == PMS_STATE_ENABLED)
524				return (0);
525
526			break;
527		case PMS_STATE_DISABLED:
528			sc->sc_dev_enable &= ~dev;
529
530			if (sc->sc_dev_enable)
531				return (0);
532
533			break;
534		}
535	}
536
537	switch (newstate) {
538	case PMS_STATE_ENABLED:
539		sc->inputstate = 0;
540
541		pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 1);
542
543		if (sc->poll)
544			pckbc_flush(sc->sc_kbctag, PCKBC_AUX_SLOT);
545
546		pms_reset(sc);
547
548		if (sc->protocol->type != PMS_STANDARD &&
549		    sc->protocol->enable(sc) == 0)
550			sc->protocol = &pms_protocols[0];
551
552		if (sc->protocol->type == PMS_STANDARD)
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#ifdef DEBUG
560		printf("%s: protocol type %d\n", DEVNAME(sc), sc->protocol->type);
561#endif
562
563		pms_dev_enable(sc);
564		break;
565	case PMS_STATE_DISABLED:
566	case PMS_STATE_SUSPENDED:
567		pms_dev_disable(sc);
568
569		if (sc->protocol && sc->protocol->disable)
570			sc->protocol->disable(sc);
571
572		pckbc_slot_enable(sc->sc_kbctag, PCKBC_AUX_SLOT, 0);
573		break;
574	}
575
576	sc->sc_state = newstate;
577	sc->poll = (newstate == PMS_STATE_SUSPENDED) ? 1 : 0;
578
579	return (0);
580}
581
582int
583pms_enable(void *v)
584{
585	struct pms_softc *sc = v;
586
587	return pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_PRIMARY);
588}
589
590void
591pms_disable(void *v)
592{
593	struct pms_softc *sc = v;
594
595	pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_PRIMARY);
596}
597
598int
599pms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
600{
601	struct pms_softc *sc = v;
602
603	if (sc->protocol && sc->protocol->ioctl)
604		return (sc->protocol->ioctl(sc, cmd, data, flag, p));
605	else
606		return (-1);
607}
608
609void
610pmsinput(void *vsc, int data)
611{
612	struct pms_softc *sc = vsc;
613
614	if (sc->sc_state != PMS_STATE_ENABLED) {
615		/* Interrupts are not expected.  Discard the byte. */
616		return;
617	}
618
619	if (sc->protocol->sync(sc, data)) {
620#ifdef DIAGNOSTIC
621		printf("%s: not in sync yet, discard input\n", DEVNAME(sc));
622#endif
623		sc->inputstate = 0;
624		return;
625	}
626
627	sc->packet[sc->inputstate++] = data;
628	if (sc->inputstate != sc->protocol->packetsize)
629		return;
630
631	sc->protocol->proc(sc);
632	sc->inputstate = 0;
633}
634
635int
636synaptics_set_mode(struct pms_softc *sc, int mode)
637{
638	struct synaptics_softc *syn = sc->synaptics;
639
640	if (pms_spec_cmd(sc, mode) ||
641	    pms_set_rate(sc, SYNAPTICS_CMD_SET_MODE))
642		return (-1);
643
644	syn->mode = mode;
645
646	return (0);
647}
648
649int
650synaptics_query(struct pms_softc *sc, int query, int *val)
651{
652	u_char resp[3];
653
654	if (pms_spec_cmd(sc, query) ||
655	    pms_get_status(sc, resp))
656		return (-1);
657
658	if (val)
659		*val = (resp[0] << 16) | (resp[1] << 8) | resp[2];
660
661	return (0);
662}
663
664int
665synaptics_get_hwinfo(struct pms_softc *sc)
666{
667	struct synaptics_softc *syn = sc->synaptics;
668
669	if (synaptics_query(sc, SYNAPTICS_QUE_IDENTIFY, &syn->identify))
670		return (-1);
671	if (synaptics_query(sc, SYNAPTICS_QUE_CAPABILITIES,
672	    &syn->capabilities))
673		return (-1);
674	if (synaptics_query(sc, SYNAPTICS_QUE_MODEL, &syn->model))
675		return (-1);
676	if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 1) &&
677	    synaptics_query(sc, SYNAPTICS_QUE_EXT_MODEL, &syn->ext_model))
678		return (-1);
679	if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 4) &&
680	    synaptics_query(sc, SYNAPTICS_QUE_EXT_CAPABILITIES,
681		&syn->ext_capabilities))
682		return (-1);
683	if ((SYNAPTICS_ID_MAJOR(syn->identify) >= 4) &&
684	    synaptics_query(sc, SYNAPTICS_QUE_RESOLUTION, &syn->resolution))
685		return (-1);
686	if ((SYNAPTICS_CAP_EXTENDED_QUERIES(syn->capabilities) >= 5) &&
687	    (syn->ext_capabilities & SYNAPTICS_EXT_CAP_MAX_DIMENSIONS) &&
688	    synaptics_query(sc, SYNAPTICS_QUE_EXT_DIMENSIONS, &syn->dimension))
689		return (-1);
690
691	syn->res_x = SYNAPTICS_RESOLUTION_X(syn->resolution);
692	syn->res_y = SYNAPTICS_RESOLUTION_Y(syn->resolution);
693	syn->min_x = SYNAPTICS_XMIN_BEZEL;
694	syn->min_y = SYNAPTICS_YMIN_BEZEL;
695	syn->max_x = (syn->dimension) ?
696	    SYNAPTICS_DIM_X(syn->dimension) : SYNAPTICS_XMAX_BEZEL;
697	syn->max_y = (syn->dimension) ?
698	    SYNAPTICS_DIM_Y(syn->dimension) : SYNAPTICS_YMAX_BEZEL;
699
700	if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) > 8)
701		syn->ext_model &= ~0xf000;
702
703	return (0);
704}
705
706void
707synaptics_pt_proc(struct pms_softc *sc)
708{
709	u_int buttons;
710	int dx, dy;
711
712	if ((sc->sc_dev_enable & PMS_DEV_SECONDARY) == 0)
713		return;
714
715	buttons = butmap[sc->packet[1] & PMS_PS2_BUTTONSMASK];
716	dx = (sc->packet[1] & PMS_PS2_XNEG) ?
717	    (int)sc->packet[4] - 256 : sc->packet[4];
718	dy = (sc->packet[1] & PMS_PS2_YNEG) ?
719	    (int)sc->packet[5] - 256 : sc->packet[5];
720
721	wsmouse_input(sc->sc_pt_wsmousedev,
722	    buttons, dx, dy, 0, 0, WSMOUSE_INPUT_DELTA);
723}
724
725int
726synaptics_pt_enable(void *v)
727{
728	struct pms_softc *sc = v;
729
730	return (pms_change_state(sc, PMS_STATE_ENABLED, PMS_DEV_SECONDARY));
731}
732
733void
734synaptics_pt_disable(void *v)
735{
736	struct pms_softc *sc = v;
737
738	pms_change_state(sc, PMS_STATE_DISABLED, PMS_DEV_SECONDARY);
739}
740
741int
742synaptics_pt_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
743{
744	switch (cmd) {
745	case WSMOUSEIO_GTYPE:
746		*(u_int *)data = WSMOUSE_TYPE_PS2;
747		break;
748	default:
749		return (-1);
750	}
751	return (0);
752}
753
754int
755pms_enable_synaptics(struct pms_softc *sc)
756{
757	struct synaptics_softc *syn = sc->synaptics;
758	struct wsmousedev_attach_args a;
759	u_char resp[3];
760	int mode;
761
762	if (pms_set_resolution(sc, 0) ||
763	    pms_set_resolution(sc, 0) ||
764	    pms_set_resolution(sc, 0) ||
765	    pms_set_resolution(sc, 0) ||
766	    pms_get_status(sc, resp) ||
767	    resp[1] != SYNAPTICS_ID_MAGIC)
768		return (0);
769
770	if (sc->synaptics == NULL) {
771		sc->synaptics = syn = malloc(sizeof(struct synaptics_softc),
772		    M_DEVBUF, M_WAITOK | M_ZERO);
773		if (syn == NULL) {
774			printf("%s: synaptics: not enough memory\n",
775			    DEVNAME(sc));
776			return (0);
777		}
778
779		if (synaptics_get_hwinfo(sc))
780			return (0);
781
782		if ((syn->model & SYNAPTICS_MODEL_NEWABS) == 0) {
783			printf("%s: don't support Synaptics OLDABS\n",
784			    DEVNAME(sc));
785			return (0);
786		}
787
788		/* enable pass-through PS/2 port if supported */
789		if (syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) {
790			a.accessops = &synaptics_pt_accessops;
791			a.accesscookie = sc;
792			sc->sc_pt_wsmousedev = config_found((void *)sc, &a,
793			    wsmousedevprint);
794		}
795
796		syn->wsmode = WSMOUSE_COMPAT;
797
798		printf("%s: Synaptics %s, firmware %d.%d\n", DEVNAME(sc),
799		    (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD ?
800			"clickpad" : "touchpad"),
801		    SYNAPTICS_ID_MAJOR(syn->identify),
802		    SYNAPTICS_ID_MINOR(syn->identify));
803	}
804
805	mode = SYNAPTICS_ABSOLUTE_MODE | SYNAPTICS_HIGH_RATE;
806	if (SYNAPTICS_ID_MAJOR(syn->identify) >= 4)
807		mode |= SYNAPTICS_DISABLE_GESTURE;
808	if (syn->capabilities & SYNAPTICS_CAP_EXTENDED)
809		mode |= SYNAPTICS_W_MODE;
810	if (synaptics_set_mode(sc, mode))
811		return (0);
812
813	/* enable advanced gesture mode if supported */
814	if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_ADV_GESTURE) &&
815	    (pms_spec_cmd(sc, SYNAPTICS_QUE_MODEL) ||
816	     pms_set_rate(sc, SYNAPTICS_CMD_SET_ADV_GESTURE_MODE)))
817		return (0);
818
819	return (1);
820}
821
822int
823pms_ioctl_synaptics(struct pms_softc *sc, u_long cmd, caddr_t data, int flag,
824    struct proc *p)
825{
826	struct synaptics_softc *syn = sc->synaptics;
827	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
828	int wsmode;
829
830	switch (cmd) {
831	case WSMOUSEIO_GTYPE:
832		*(u_int *)data = WSMOUSE_TYPE_SYNAPTICS;
833		break;
834	case WSMOUSEIO_GCALIBCOORDS:
835		wsmc->minx = syn->min_x;
836		wsmc->maxx = syn->max_x;
837		wsmc->miny = syn->min_y;
838		wsmc->maxy = syn->max_y;
839		wsmc->swapxy = 0;
840		wsmc->resx = syn->res_x;
841		wsmc->resy = syn->res_y;
842		break;
843	case WSMOUSEIO_SETMODE:
844		wsmode = *(u_int *)data;
845		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE)
846			return (EINVAL);
847		syn->wsmode = wsmode;
848		break;
849	default:
850		return (-1);
851	}
852	return (0);
853}
854
855int
856pms_sync_synaptics(struct pms_softc *sc, int data)
857{
858	switch (sc->inputstate) {
859	case 0:
860		if ((data & 0xc8) != 0x80)
861			return (-1);
862		break;
863	case 3:
864		if ((data & 0xc8) != 0xc0)
865			return (-1);
866		break;
867	}
868
869	return (0);
870}
871
872void
873pms_proc_synaptics(struct pms_softc *sc)
874{
875	struct synaptics_softc *syn = sc->synaptics;
876	u_int buttons;
877	int x, y, z, w, dx, dy;
878
879	w = ((sc->packet[0] & 0x30) >> 2) | ((sc->packet[0] & 0x04) >> 1) |
880	    ((sc->packet[3] & 0x04) >> 2);
881
882	if ((syn->capabilities & SYNAPTICS_CAP_PASSTHROUGH) && w == 3) {
883		synaptics_pt_proc(sc);
884		return;
885	}
886
887	if ((sc->sc_dev_enable & PMS_DEV_PRIMARY) == 0)
888		return;
889
890	/* XXX ignore advanced gesture packet, not yet supported */
891	if ((syn->ext_capabilities & SYNAPTICS_EXT_CAP_ADV_GESTURE) && w == 2)
892		return;
893
894	x = ((sc->packet[3] & 0x10) << 8) | ((sc->packet[1] & 0x0f) << 8) |
895	    sc->packet[4];
896	y = ((sc->packet[3] & 0x20) << 7) | ((sc->packet[1] & 0xf0) << 4) |
897	    sc->packet[5];
898	z = sc->packet[2];
899
900	buttons = ((sc->packet[0] & sc->packet[3]) & 0x01) ?
901	    WSMOUSE_BUTTON(1) : 0;
902	buttons |= ((sc->packet[0] & sc->packet[3]) & 0x02) ?
903	    WSMOUSE_BUTTON(3) : 0;
904
905	if (syn->ext_capabilities & SYNAPTICS_EXT_CAP_CLICKPAD) {
906		buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
907		    WSMOUSE_BUTTON(1) : 0;
908	} else if (syn->capabilities & SYNAPTICS_CAP_MIDDLE_BUTTON) {
909		buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
910		    WSMOUSE_BUTTON(2) : 0;
911	}
912
913	if (syn->capabilities & SYNAPTICS_CAP_FOUR_BUTTON) {
914		buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x01) ?
915		    WSMOUSE_BUTTON(4) : 0;
916		buttons |= ((sc->packet[0] ^ sc->packet[3]) & 0x02) ?
917		    WSMOUSE_BUTTON(5) : 0;
918	} else if (SYNAPTICS_EXT_MODEL_BUTTONS(syn->ext_model) &&
919	    ((sc->packet[0] ^ sc->packet[3]) & 0x02)) {
920		buttons |= (sc->packet[4] & 0x01) ? WSMOUSE_BUTTON(6) : 0;
921		buttons |= (sc->packet[5] & 0x01) ? WSMOUSE_BUTTON(7) : 0;
922		buttons |= (sc->packet[4] & 0x02) ? WSMOUSE_BUTTON(8) : 0;
923		buttons |= (sc->packet[5] & 0x02) ? WSMOUSE_BUTTON(9) : 0;
924		buttons |= (sc->packet[4] & 0x04) ? WSMOUSE_BUTTON(10) : 0;
925		buttons |= (sc->packet[5] & 0x04) ? WSMOUSE_BUTTON(11) : 0;
926		buttons |= (sc->packet[4] & 0x08) ? WSMOUSE_BUTTON(12) : 0;
927		buttons |= (sc->packet[5] & 0x08) ? WSMOUSE_BUTTON(13) : 0;
928		x &= ~0x0f;
929		y &= ~0x0f;
930	}
931
932	/* ignore final events that happen when removing all fingers */
933	if (x <= 1 || y <= 1) {
934		x = syn->old_x;
935		y = syn->old_y;
936	}
937
938	if (syn->wsmode == WSMOUSE_NATIVE) {
939		wsmouse_input(sc->sc_wsmousedev, buttons, x, y, z, w,
940		    WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y |
941		    WSMOUSE_INPUT_ABSOLUTE_Z | WSMOUSE_INPUT_ABSOLUTE_W);
942	} else {
943		dx = dy = 0;
944		if (z > SYNAPTICS_PRESSURE) {
945			dx = x - syn->old_x;
946			dy = y - syn->old_y;
947			dx /= SYNAPTICS_SCALE;
948			dy /= SYNAPTICS_SCALE;
949		}
950		if (dx || dy || buttons != syn->old_buttons)
951			wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, 0, 0,
952			    WSMOUSE_INPUT_DELTA);
953		syn->old_buttons = buttons;
954	}
955
956	syn->old_x = x;
957	syn->old_y = y;
958}
959
960void
961pms_disable_synaptics(struct pms_softc *sc)
962{
963	struct synaptics_softc *syn = sc->synaptics;
964
965	if (syn->capabilities & SYNAPTICS_CAP_SLEEP)
966		synaptics_set_mode(sc, SYNAPTICS_SLEEP_MODE |
967		    SYNAPTICS_DISABLE_GESTURE);
968}
969