xref: /netbsd-src/sys/dev/pckbport/pms.c (revision eb2723f8d4119209ee04772789ed3bcb8009bf41)
1*eb2723f8Schs /* $NetBSD: pms.c,v 1.42 2024/12/07 23:25:19 chs Exp $ */
2dff5222dSbjh21 
3dff5222dSbjh21 /*-
481e1a623Schristos  * Copyright (c) 2004 Kentaro Kurahone.
581e1a623Schristos  * Copyright (c) 2004 Ales Krenek.
6dff5222dSbjh21  * Copyright (c) 1994 Charles M. Hannum.
7dff5222dSbjh21  * Copyright (c) 1992, 1993 Erik Forsberg.
8dff5222dSbjh21  * All rights reserved.
9dff5222dSbjh21  *
10dff5222dSbjh21  * Redistribution and use in source and binary forms, with or without
11dff5222dSbjh21  * modification, are permitted provided that the following conditions
12dff5222dSbjh21  * are met:
13dff5222dSbjh21  * 1. Redistributions of source code must retain the above copyright
14dff5222dSbjh21  *    notice, this list of conditions and the following disclaimer.
15dff5222dSbjh21  *
16dff5222dSbjh21  * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
17dff5222dSbjh21  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18dff5222dSbjh21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
19dff5222dSbjh21  * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20dff5222dSbjh21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21dff5222dSbjh21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22dff5222dSbjh21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23dff5222dSbjh21  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24dff5222dSbjh21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25dff5222dSbjh21  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26dff5222dSbjh21  */
27dff5222dSbjh21 
28dff5222dSbjh21 #include <sys/cdefs.h>
29*eb2723f8Schs __KERNEL_RCSID(0, "$NetBSD: pms.c,v 1.42 2024/12/07 23:25:19 chs Exp $");
301b3b0fe4Sdsl 
311b3b0fe4Sdsl #include "opt_pms.h"
32dff5222dSbjh21 
33dff5222dSbjh21 #include <sys/param.h>
34dff5222dSbjh21 #include <sys/systm.h>
35dff5222dSbjh21 #include <sys/device.h>
36dff5222dSbjh21 #include <sys/ioctl.h>
37dff5222dSbjh21 #include <sys/kernel.h>
38dff5222dSbjh21 #include <sys/kthread.h>
39dff5222dSbjh21 
40a2a38285Sad #include <sys/bus.h>
41dff5222dSbjh21 
42dff5222dSbjh21 #include <dev/pckbport/pckbportvar.h>
432d0fc26cSscw #ifdef PMS_SYNAPTICS_TOUCHPAD
4481e1a623Schristos #include <dev/pckbport/synapticsvar.h>
452d0fc26cSscw #endif
463e8db259Sjmcneill #ifdef PMS_ELANTECH_TOUCHPAD
473e8db259Sjmcneill #include <dev/pckbport/elantechvar.h>
483e8db259Sjmcneill #endif
49014b6d5aSchristos #ifdef PMS_ALPS_TOUCHPAD
50014b6d5aSchristos #include <dev/pckbport/alpsvar.h>
51014b6d5aSchristos #endif
52dff5222dSbjh21 
53dff5222dSbjh21 #include <dev/pckbport/pmsreg.h>
5481e1a623Schristos #include <dev/pckbport/pmsvar.h>
5581e1a623Schristos 
56dff5222dSbjh21 
57dff5222dSbjh21 #include <dev/wscons/wsconsio.h>
58dff5222dSbjh21 #include <dev/wscons/wsmousevar.h>
59dff5222dSbjh21 
60dff5222dSbjh21 #ifdef PMSDEBUG
61dff5222dSbjh21 int pmsdebug = 1;
62dff5222dSbjh21 #define DPRINTF(x)      if (pmsdebug) printf x
63dff5222dSbjh21 #else
64dff5222dSbjh21 #define DPRINTF(x)
65dff5222dSbjh21 #endif
66dff5222dSbjh21 
679779175eSjakllsch static const enum pms_type tries[] = {
6881e1a623Schristos 	PMS_SCROLL5, PMS_SCROLL3, PMS_STANDARD, PMS_UNKNOWN
6981e1a623Schristos };
70dff5222dSbjh21 
719779175eSjakllsch static const struct pms_protocol pms_protocols[] = {
72dff5222dSbjh21 	{ { 0, 0, 0 }, 0, "unknown protocol" },
73dff5222dSbjh21 	{ { 0, 0, 0 }, 0, "no scroll wheel (3 buttons)" },
74dff5222dSbjh21 	{ { 200, 100, 80 }, 3, "scroll wheel (3 buttons)" },
7567a3f474Srpaulo 	{ { 200, 200, 80 }, 4, "scroll wheel (5 buttons)" },
763e8db259Sjmcneill 	{ { 0, 0, 0 }, 0, "synaptics" },
773e8db259Sjmcneill 	{ { 0, 0, 0 }, 0, "elantech" }
78dff5222dSbjh21 };
79dff5222dSbjh21 
80dff5222dSbjh21 
819779175eSjakllsch static int pmsprobe(device_t, cfdata_t, void *);
829779175eSjakllsch static void pmsattach(device_t, device_t, void *);
839779175eSjakllsch static void pmsinput(void *, int);
84dff5222dSbjh21 
857582ef2fScube CFATTACH_DECL_NEW(pms, sizeof(struct pms_softc),
86dff5222dSbjh21     pmsprobe, pmsattach, NULL, NULL);
87dff5222dSbjh21 
8839d9753eSbjh21 static int	pms_protocol(pckbport_tag_t, pckbport_slot_t);
8939d9753eSbjh21 static void	do_enable(struct pms_softc *);
9039d9753eSbjh21 static void	do_disable(struct pms_softc *);
9139d9753eSbjh21 static void	pms_reset_thread(void*);
929779175eSjakllsch static int	pms_enable(void *);
939779175eSjakllsch static int	pms_ioctl(void *, u_long, void *, int, struct lwp *);
949779175eSjakllsch static void	pms_disable(void *);
954c1d81b2Sjmcneill 
96c1b390d4Sdyoung static bool	pms_suspend(device_t, const pmf_qual_t *);
97c1b390d4Sdyoung static bool	pms_resume(device_t, const pmf_qual_t *);
98dff5222dSbjh21 
999779175eSjakllsch static const struct wsmouse_accessops pms_accessops = {
1007fec8270Sriastradh 	.enable = pms_enable,
1017fec8270Sriastradh 	.ioctl = pms_ioctl,
1027fec8270Sriastradh 	.disable = pms_disable,
103dff5222dSbjh21 };
104dff5222dSbjh21 
105dff5222dSbjh21 static int
10639d9753eSbjh21 pms_protocol(pckbport_tag_t tag, pckbport_slot_t slot)
107dff5222dSbjh21 {
108dff5222dSbjh21 	u_char cmd[2], resp[1];
109dff5222dSbjh21 	int i, j, res;
110bb7b3655Sjdolecek 	const struct pms_protocol *p;
111dff5222dSbjh21 
112dff5222dSbjh21 	for (j = 0; j < sizeof(tries) / sizeof(tries[0]); ++j) {
113dff5222dSbjh21 		p = &pms_protocols[tries[j]];
114dff5222dSbjh21 		if (!p->rates[0])
115dff5222dSbjh21 			break;
116dff5222dSbjh21 		cmd[0] = PMS_SET_SAMPLE;
117dff5222dSbjh21 		for (i = 0; i < 3; i++) {
118dff5222dSbjh21 			cmd[1] = p->rates[i];
119dff5222dSbjh21 			res = pckbport_enqueue_cmd(tag, slot, cmd, 2, 0, 1, 0);
120dff5222dSbjh21 			if (res)
121dff5222dSbjh21 				return PMS_STANDARD;
122dff5222dSbjh21 		}
123dff5222dSbjh21 
124dff5222dSbjh21 		cmd[0] = PMS_SEND_DEV_ID;
125dff5222dSbjh21 		res = pckbport_enqueue_cmd(tag, slot, cmd, 1, 1, 1, resp);
126dff5222dSbjh21 		if (res)
127dff5222dSbjh21 			return PMS_UNKNOWN;
128dff5222dSbjh21 		if (resp[0] == p->response) {
129dff5222dSbjh21 			DPRINTF(("pms_protocol: found mouse protocol %d\n",
130dff5222dSbjh21 				tries[j]));
131dff5222dSbjh21 			return tries[j];
132dff5222dSbjh21 		}
133dff5222dSbjh21 	}
134dff5222dSbjh21 	DPRINTF(("pms_protocol: standard PS/2 protocol (no scroll wheel)\n"));
135dff5222dSbjh21 	return PMS_STANDARD;
136dff5222dSbjh21 }
137dff5222dSbjh21 
138dff5222dSbjh21 int
13988906c66Scube pmsprobe(device_t parent, cfdata_t match, void *aux)
140dff5222dSbjh21 {
141dff5222dSbjh21 	struct pckbport_attach_args *pa = aux;
142dff5222dSbjh21 	u_char cmd[1], resp[2];
143dff5222dSbjh21 	int res;
144dff5222dSbjh21 
145dff5222dSbjh21 	if (pa->pa_slot != PCKBPORT_AUX_SLOT)
14639d9753eSbjh21 		return 0;
147dff5222dSbjh21 
148dff5222dSbjh21 	/* Flush any garbage. */
149dff5222dSbjh21 	pckbport_flush(pa->pa_tag, pa->pa_slot);
150dff5222dSbjh21 
151dff5222dSbjh21 	/* reset the device */
152dff5222dSbjh21 	cmd[0] = PMS_RESET;
153dff5222dSbjh21 	res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
154dff5222dSbjh21 	if (res) {
1550458d57aSad 		aprint_debug("pmsprobe: reset error %d\n", res);
15639d9753eSbjh21 		return 0;
157dff5222dSbjh21 	}
158dff5222dSbjh21 	if (resp[0] != PMS_RSTDONE) {
159dff5222dSbjh21 		printf("pmsprobe: reset response 0x%x\n", resp[0]);
16039d9753eSbjh21 		return 0;
161dff5222dSbjh21 	}
162dff5222dSbjh21 
163dff5222dSbjh21 	/* get type number (0 = mouse) */
164dff5222dSbjh21 	if (resp[1] != 0) {
1650458d57aSad 		aprint_debug("pmsprobe: type 0x%x\n", resp[1]);
16639d9753eSbjh21 		return 0;
167dff5222dSbjh21 	}
168dff5222dSbjh21 
16939d9753eSbjh21 	return 10;
170dff5222dSbjh21 }
171dff5222dSbjh21 
1729779175eSjakllsch static void
17388906c66Scube pmsattach(device_t parent, device_t self, void *aux)
174dff5222dSbjh21 {
175838ee1e0Sthorpej 	struct pms_softc *sc = device_private(self);
176dff5222dSbjh21 	struct pckbport_attach_args *pa = aux;
177dff5222dSbjh21 	struct wsmousedev_attach_args a;
17881e1a623Schristos 	u_char cmd[2], resp[2];
179dff5222dSbjh21 	int res;
180dff5222dSbjh21 
1817582ef2fScube 	sc->sc_dev = self;
182dff5222dSbjh21 	sc->sc_kbctag = pa->pa_tag;
183dff5222dSbjh21 	sc->sc_kbcslot = pa->pa_slot;
184dff5222dSbjh21 
185a0d3b0d7Sjmcneill 	aprint_naive("\n");
186a0d3b0d7Sjmcneill 	aprint_normal("\n");
187dff5222dSbjh21 
188dff5222dSbjh21 	/* Flush any garbage. */
189dff5222dSbjh21 	pckbport_flush(pa->pa_tag, pa->pa_slot);
190dff5222dSbjh21 
191dff5222dSbjh21 	/* reset the device */
192dff5222dSbjh21 	cmd[0] = PMS_RESET;
193dff5222dSbjh21 	res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
194dff5222dSbjh21 	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
1950458d57aSad 		aprint_debug("pmsattach: reset error\n");
196dff5222dSbjh21 		return;
197dff5222dSbjh21 	}
198dff5222dSbjh21 	sc->inputstate = 0;
199dff5222dSbjh21 	sc->buttons = 0;
200dff5222dSbjh21 	sc->protocol = PMS_UNKNOWN;
201dff5222dSbjh21 
2022d0fc26cSscw #ifdef PMS_SYNAPTICS_TOUCHPAD
20381e1a623Schristos 	/* Probe for synaptics touchpad. */
20481e1a623Schristos 	if (pms_synaptics_probe_init(sc) == 0) {
20581e1a623Schristos 		sc->protocol = PMS_SYNAPTICS;
2062d0fc26cSscw 	} else
2072d0fc26cSscw #endif
2083e8db259Sjmcneill #ifdef PMS_ELANTECH_TOUCHPAD
2093e8db259Sjmcneill 	if (pms_elantech_probe_init(sc) == 0) {
2103e8db259Sjmcneill 		sc->protocol = PMS_ELANTECH;
2113e8db259Sjmcneill 	} else
2123e8db259Sjmcneill #endif
213014b6d5aSchristos #ifdef PMS_ALPS_TOUCHPAD
214014b6d5aSchristos 	if (pms_alps_probe_init(sc) == 0) {
215014b6d5aSchristos 		sc->protocol = PMS_ALPS;
216014b6d5aSchristos 	} else
217014b6d5aSchristos #endif
21881e1a623Schristos 		/* Install generic handler. */
219dff5222dSbjh21 		pckbport_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot,
2207582ef2fScube 		    pmsinput, sc, device_xname(sc->sc_dev));
2212d0fc26cSscw 
222dff5222dSbjh21 	a.accessops = &pms_accessops;
223dff5222dSbjh21 	a.accesscookie = sc;
224dff5222dSbjh21 
225dff5222dSbjh21 	/*
226dff5222dSbjh21 	 * Attach the wsmouse, saving a handle to it.
227dff5222dSbjh21 	 * Note that we don't need to check this pointer against NULL
228dff5222dSbjh21 	 * here or in pmsintr, because if this fails pms_enable() will
229dff5222dSbjh21 	 * never be called, so pmsinput() will never be called.
230dff5222dSbjh21 	 */
231c7fb772bSthorpej 	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint, CFARGS_NONE);
232dff5222dSbjh21 
233dff5222dSbjh21 	/* no interrupts until enabled */
234dff5222dSbjh21 	cmd[0] = PMS_DEV_DISABLE;
2352eb0dcceSjakllsch 	res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 0, NULL, 0);
236dff5222dSbjh21 	if (res)
237a0d3b0d7Sjmcneill 		aprint_error("pmsattach: disable error\n");
238dff5222dSbjh21 	pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
239dff5222dSbjh21 
24088ab7da9Sad 	kthread_create(PRI_NONE, 0, NULL, pms_reset_thread, sc,
241c22277f8Sjoerg 	    &sc->sc_event_thread, "%s", device_xname(sc->sc_dev));
242dff5222dSbjh21 
2434c1d81b2Sjmcneill 	if (!pmf_device_register(self, pms_suspend, pms_resume))
2444c1d81b2Sjmcneill 		aprint_error_dev(self, "couldn't establish power handler\n");
245dff5222dSbjh21 }
246dff5222dSbjh21 
247dff5222dSbjh21 static void
24839d9753eSbjh21 do_enable(struct pms_softc *sc)
249dff5222dSbjh21 {
25081e1a623Schristos 	u_char cmd[2];
251dff5222dSbjh21 	int res;
252dff5222dSbjh21 
253dff5222dSbjh21 	sc->inputstate = 0;
254dff5222dSbjh21 	sc->buttons = 0;
255dff5222dSbjh21 
256dff5222dSbjh21 	pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 1);
257dff5222dSbjh21 
2582d0fc26cSscw #ifdef PMS_SYNAPTICS_TOUCHPAD
25981e1a623Schristos 	if (sc->protocol == PMS_SYNAPTICS)
26081e1a623Schristos 		pms_synaptics_enable(sc);
2612d0fc26cSscw #endif
2623e8db259Sjmcneill #ifdef PMS_ELANTECH_TOUCHPAD
2633e8db259Sjmcneill 	if (sc->protocol == PMS_ELANTECH)
2643e8db259Sjmcneill 		pms_elantech_enable(sc);
2653e8db259Sjmcneill #endif
266014b6d5aSchristos #ifdef PMS_ALPS_TOUCHPAD
267014b6d5aSchristos 	if (sc->protocol == PMS_ALPS)
268014b6d5aSchristos 		pms_alps_enable(sc);
269014b6d5aSchristos #endif
27081e1a623Schristos 
271dff5222dSbjh21 	cmd[0] = PMS_DEV_ENABLE;
27281e1a623Schristos 	res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
27381e1a623Schristos 	    1, 0, 1, 0);
274dff5222dSbjh21 	if (res)
275a0d3b0d7Sjmcneill 		aprint_error("pms_enable: command error %d\n", res);
276dff5222dSbjh21 
277dff5222dSbjh21 	if (sc->protocol == PMS_UNKNOWN)
278dff5222dSbjh21 		sc->protocol = pms_protocol(sc->sc_kbctag, sc->sc_kbcslot);
279dff5222dSbjh21 	DPRINTF(("pms_enable: using %s protocol\n",
280dff5222dSbjh21 	    pms_protocols[sc->protocol].name));
281dff5222dSbjh21 #if 0
282dff5222dSbjh21 	{
283dff5222dSbjh21 		u_char scmd[2];
284dff5222dSbjh21 
285dff5222dSbjh21 		scmd[0] = PMS_SET_RES;
286dff5222dSbjh21 		scmd[1] = 3; /* 8 counts/mm */
287dff5222dSbjh21 		res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd,
288dff5222dSbjh21 		    2, 0, 1, 0);
289dff5222dSbjh21 		if (res)
290dff5222dSbjh21 			printf("pms_enable: setup error1 (%d)\n", res);
291dff5222dSbjh21 
292dff5222dSbjh21 		scmd[0] = PMS_SET_SCALE21;
293dff5222dSbjh21 		res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd,
294dff5222dSbjh21 		    1, 0, 1, 0);
295dff5222dSbjh21 		if (res)
296dff5222dSbjh21 			printf("pms_enable: setup error2 (%d)\n", res);
297dff5222dSbjh21 
298dff5222dSbjh21 		scmd[0] = PMS_SET_SAMPLE;
299dff5222dSbjh21 		scmd[1] = 100; /* 100 samples/sec */
300dff5222dSbjh21 		res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd,
301dff5222dSbjh21 		    2, 0, 1, 0);
302dff5222dSbjh21 		if (res)
303dff5222dSbjh21 			printf("pms_enable: setup error3 (%d)\n", res);
304dff5222dSbjh21 	}
305dff5222dSbjh21 #endif
306dff5222dSbjh21 }
307dff5222dSbjh21 
308dff5222dSbjh21 static void
30939d9753eSbjh21 do_disable(struct pms_softc *sc)
310dff5222dSbjh21 {
311dff5222dSbjh21 	u_char cmd[1];
312dff5222dSbjh21 	int res;
313dff5222dSbjh21 
314dff5222dSbjh21 	cmd[0] = PMS_DEV_DISABLE;
31581e1a623Schristos 	res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
31681e1a623Schristos 	    1, 0, 1, 0);
317dff5222dSbjh21 	if (res)
318a0d3b0d7Sjmcneill 		aprint_error("pms_disable: command error\n");
319dff5222dSbjh21 
320dff5222dSbjh21 	pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
321dff5222dSbjh21 }
322dff5222dSbjh21 
3239779175eSjakllsch static int
32439d9753eSbjh21 pms_enable(void *v)
325dff5222dSbjh21 {
326dff5222dSbjh21 	struct pms_softc *sc = v;
327dff5222dSbjh21 	int s;
328dff5222dSbjh21 
329dff5222dSbjh21 	if (sc->sc_enabled)
330dff5222dSbjh21 		return EBUSY;
331dff5222dSbjh21 
332dff5222dSbjh21 	do_enable(sc);
333dff5222dSbjh21 
334dff5222dSbjh21 	s = spltty();
335dff5222dSbjh21 	sc->sc_enabled = 1;
336dff5222dSbjh21 	splx(s);
337dff5222dSbjh21 
338dff5222dSbjh21 	return 0;
339dff5222dSbjh21 }
340dff5222dSbjh21 
3419779175eSjakllsch static void
34239d9753eSbjh21 pms_disable(void *v)
343dff5222dSbjh21 {
344dff5222dSbjh21 	struct pms_softc *sc = v;
345dff5222dSbjh21 	int s;
346dff5222dSbjh21 
347dff5222dSbjh21 	do_disable(sc);
348dff5222dSbjh21 
349dff5222dSbjh21 	s = spltty();
350dff5222dSbjh21 	sc->sc_enabled = 0;
351dff5222dSbjh21 	splx(s);
352dff5222dSbjh21 }
353dff5222dSbjh21 
3544c1d81b2Sjmcneill static bool
355c1b390d4Sdyoung pms_suspend(device_t dv, const pmf_qual_t *qual)
356dff5222dSbjh21 {
3574c1d81b2Sjmcneill 	struct pms_softc *sc = device_private(dv);
358dff5222dSbjh21 
3594c1d81b2Sjmcneill 	if (sc->sc_enabled)
360dff5222dSbjh21 		do_disable(sc);
3614c1d81b2Sjmcneill 
3624c1d81b2Sjmcneill 	return true;
363dff5222dSbjh21 }
3644c1d81b2Sjmcneill 
3654c1d81b2Sjmcneill static bool
366c1b390d4Sdyoung pms_resume(device_t dv, const pmf_qual_t *qual)
3674c1d81b2Sjmcneill {
3684c1d81b2Sjmcneill 	struct pms_softc *sc = device_private(dv);
3694c1d81b2Sjmcneill 
3702d0fc26cSscw #ifdef PMS_SYNAPTICS_TOUCHPAD
3710a95a062Sjmcneill 	if (sc->protocol == PMS_SYNAPTICS) {
37281e1a623Schristos 		pms_synaptics_resume(sc);
3731ec278b3Sjmcneill 		if (sc->sc_enabled) {
3740a95a062Sjmcneill 			do_enable(sc);
3751ec278b3Sjmcneill 		}
3764c1d81b2Sjmcneill 	} else
3772d0fc26cSscw #endif
3783e8db259Sjmcneill #ifdef PMS_ELANTECH_TOUCHPAD
3793e8db259Sjmcneill 	if (sc->protocol == PMS_ELANTECH) {
3803e8db259Sjmcneill 		pms_elantech_resume(sc);
3813e8db259Sjmcneill 		if (sc->sc_enabled) {
3823e8db259Sjmcneill 			do_enable(sc);
3833e8db259Sjmcneill 		}
3843e8db259Sjmcneill 	} else
3853e8db259Sjmcneill #endif
386014b6d5aSchristos #ifdef PMS_ALPS_TOUCHPAD
387014b6d5aSchristos 	if (sc->protocol == PMS_ALPS) {
388014b6d5aSchristos 		pms_alps_resume(sc);
389014b6d5aSchristos 		if (sc->sc_enabled) {
390014b6d5aSchristos 			do_enable(sc);
391014b6d5aSchristos 		}
392014b6d5aSchristos 	} else
393014b6d5aSchristos #endif
3944c1d81b2Sjmcneill 	if (sc->sc_enabled) {
39581e1a623Schristos 		/* recheck protocol & init mouse */
39681e1a623Schristos 		sc->protocol = PMS_UNKNOWN;
397dff5222dSbjh21 		do_enable(sc); /* only if we were suspended */
398dff5222dSbjh21 	}
3994c1d81b2Sjmcneill 
4004c1d81b2Sjmcneill 	return true;
401dff5222dSbjh21 }
402dff5222dSbjh21 
4039779175eSjakllsch static int
40453524e44Schristos pms_ioctl(void *v, u_long cmd, void *data, int flag,
405168cd830Schristos     struct lwp *l)
406dff5222dSbjh21 {
407dff5222dSbjh21 	struct pms_softc *sc = v;
408dff5222dSbjh21 	u_char kbcmd[2];
409dff5222dSbjh21 	int i;
410dff5222dSbjh21 
411dff5222dSbjh21 	switch (cmd) {
412dff5222dSbjh21 	case WSMOUSEIO_GTYPE:
413dff5222dSbjh21 		*(u_int *)data = WSMOUSE_TYPE_PS2;
414dff5222dSbjh21 		break;
415dff5222dSbjh21 
416dff5222dSbjh21 	case WSMOUSEIO_SRES:
417dff5222dSbjh21 		i = (*(u_int *)data - 12) / 25;
418dff5222dSbjh21 
419dff5222dSbjh21 		if (i < 0)
420dff5222dSbjh21 			i = 0;
421dff5222dSbjh21 
422dff5222dSbjh21 		if (i > 3)
423dff5222dSbjh21 			i = 3;
424dff5222dSbjh21 
425dff5222dSbjh21 		kbcmd[0] = PMS_SET_RES;
426dff5222dSbjh21 		kbcmd[1] = i;
427dff5222dSbjh21 		i = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, kbcmd,
428dff5222dSbjh21 		    2, 0, 1, 0);
429dff5222dSbjh21 
430dff5222dSbjh21 		if (i)
431dff5222dSbjh21 			printf("pms_ioctl: SET_RES command error\n");
432dff5222dSbjh21 		break;
433dff5222dSbjh21 
434dff5222dSbjh21 	default:
43539d9753eSbjh21 		return EPASSTHROUGH;
436dff5222dSbjh21 	}
43739d9753eSbjh21 	return 0;
438dff5222dSbjh21 }
439dff5222dSbjh21 
440dff5222dSbjh21 static void
44139d9753eSbjh21 pms_reset_thread(void *arg)
442dff5222dSbjh21 {
443dff5222dSbjh21 	struct pms_softc *sc = arg;
444dff5222dSbjh21 	u_char cmd[1], resp[2];
445dff5222dSbjh21 	int res;
446dff5222dSbjh21 	int save_protocol;
447dff5222dSbjh21 
448dff5222dSbjh21 	for (;;) {
449dff5222dSbjh21 		tsleep(&sc->sc_enabled, PWAIT, "pmsreset", 0);
450dff5222dSbjh21 #ifdef PMSDEBUG
451dff5222dSbjh21 		if (pmsdebug)
452dff5222dSbjh21 #endif
453dff5222dSbjh21 #if defined(PMSDEBUG) || defined(DIAGNOSTIC)
4547582ef2fScube 			aprint_debug_dev(sc->sc_dev,
4557582ef2fScube 			    "resetting mouse interface\n");
456dff5222dSbjh21 #endif
457dff5222dSbjh21 		save_protocol = sc->protocol;
458dff5222dSbjh21 		pms_disable(sc);
459dff5222dSbjh21 		cmd[0] = PMS_RESET;
46081e1a623Schristos 		res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
46181e1a623Schristos 		    1, 2, 1, resp);
462b4c0be5dSchristos 		if (res) {
4637582ef2fScube 			DPRINTF(("%s: reset error %d\n",
4647582ef2fScube 			    device_xname(sc->sc_dev), res));
465b4c0be5dSchristos 		}
46681e1a623Schristos 
4673e8db259Sjmcneill 		/* For the synaptics and elantech case, leave the protocol alone. */
468014b6d5aSchristos 		if (sc->protocol != PMS_SYNAPTICS && sc->protocol != PMS_ELANTECH
469014b6d5aSchristos 			&& sc->protocol != PMS_ALPS)
470dff5222dSbjh21 			sc->protocol = PMS_UNKNOWN;
4712d0fc26cSscw 
472dff5222dSbjh21 		pms_enable(sc);
473dff5222dSbjh21 		if (sc->protocol != save_protocol) {
474dff5222dSbjh21 #if defined(PMSDEBUG) || defined(DIAGNOSTIC)
4757582ef2fScube 			aprint_verbose_dev(sc->sc_dev,
4767582ef2fScube 			    "protocol change, sleeping and retrying\n");
477dff5222dSbjh21 #endif
478dff5222dSbjh21 			pms_disable(sc);
479dff5222dSbjh21 			cmd[0] = PMS_RESET;
480dff5222dSbjh21 			res = pckbport_enqueue_cmd(sc->sc_kbctag,
481dff5222dSbjh21 			    sc->sc_kbcslot, cmd, 1, 2, 1, resp);
482b4c0be5dSchristos 			if (res) {
483dff5222dSbjh21 				DPRINTF(("%s: reset error %d\n",
4847582ef2fScube 				    device_xname(sc->sc_dev), res));
485b4c0be5dSchristos 			}
486dff5222dSbjh21 			tsleep(pms_reset_thread, PWAIT, "pmsreset", hz);
487dff5222dSbjh21 			cmd[0] = PMS_RESET;
488dff5222dSbjh21 			res = pckbport_enqueue_cmd(sc->sc_kbctag,
489dff5222dSbjh21 			    sc->sc_kbcslot, cmd, 1, 2, 1, resp);
490b4c0be5dSchristos 			if (res) {
491dff5222dSbjh21 				DPRINTF(("%s: reset error %d\n",
4927582ef2fScube 				    device_xname(sc->sc_dev), res));
493b4c0be5dSchristos 			}
494dff5222dSbjh21 			sc->protocol = PMS_UNKNOWN;	/* reprobe protocol */
495dff5222dSbjh21 			pms_enable(sc);
496dff5222dSbjh21 #if defined(PMSDEBUG) || defined(DIAGNOSTIC)
497dff5222dSbjh21 			if (sc->protocol != save_protocol) {
498dff5222dSbjh21 				printf("%s: protocol changed.\n",
4997582ef2fScube 				    device_xname(sc->sc_dev));
500dff5222dSbjh21 			}
501dff5222dSbjh21 #endif
502dff5222dSbjh21 		}
503dff5222dSbjh21 	}
504dff5222dSbjh21 }
505dff5222dSbjh21 
506dff5222dSbjh21 /* Masks for the first byte of a packet */
507dff5222dSbjh21 #define PMS_LBUTMASK 0x01
508dff5222dSbjh21 #define PMS_RBUTMASK 0x02
509dff5222dSbjh21 #define PMS_MBUTMASK 0x04
510dff5222dSbjh21 #define PMS_4BUTMASK 0x10
511dff5222dSbjh21 #define PMS_5BUTMASK 0x20
512dff5222dSbjh21 
5139779175eSjakllsch static void
51439d9753eSbjh21 pmsinput(void *vsc, int data)
515dff5222dSbjh21 {
516dff5222dSbjh21 	struct pms_softc *sc = vsc;
517dff5222dSbjh21 	u_int changed;
518dff5222dSbjh21 	int dx, dy, dz = 0;
519dff5222dSbjh21 	int newbuttons = 0;
520dff5222dSbjh21 
521dff5222dSbjh21 	if (!sc->sc_enabled) {
522dff5222dSbjh21 		/* Interrupts are not expected. Discard the byte. */
523dff5222dSbjh21 		return;
524dff5222dSbjh21 	}
525dff5222dSbjh21 
526de4337abSkardel 	getmicrouptime(&sc->current);
527dff5222dSbjh21 
528dff5222dSbjh21 	if (sc->inputstate > 0) {
529dff5222dSbjh21 		struct timeval diff;
530dff5222dSbjh21 
531dff5222dSbjh21 		timersub(&sc->current, &sc->last, &diff);
532dff5222dSbjh21 		/*
533dff5222dSbjh21 		 * Empirically, the delay should be about 1700us on a standard
534dff5222dSbjh21 		 * PS/2 port.  I have seen delays as large as 4500us (rarely)
535dff5222dSbjh21 		 * in regular use.  When using a confused mouse, I generally
536dff5222dSbjh21 		 * see delays at least as large as 30,000us.  -seebs
537dff5222dSbjh21 		 *
538dff5222dSbjh21 		 * The thinkpad trackball returns at 22-23ms. So we use
539dff5222dSbjh21 		 * >= 40ms. In the future, I'll implement adaptable timeout
540dff5222dSbjh21 		 * by increasing the timeout if the mouse reset happens
541dff5222dSbjh21 		 * too frequently -christos
542dff5222dSbjh21 		 */
543dff5222dSbjh21 		if (diff.tv_sec > 0 || diff.tv_usec >= 40000) {
544dff5222dSbjh21 			DPRINTF(("pms_input: unusual delay (%ld.%06ld s), "
545dff5222dSbjh21 			    "scheduling reset\n",
546dff5222dSbjh21 			    (long)diff.tv_sec, (long)diff.tv_usec));
547dff5222dSbjh21 			sc->inputstate = 0;
548dff5222dSbjh21 			sc->sc_enabled = 0;
549dff5222dSbjh21 			wakeup(&sc->sc_enabled);
550dff5222dSbjh21 			return;
551dff5222dSbjh21 		}
552dff5222dSbjh21 	}
553dff5222dSbjh21 	sc->last = sc->current;
554dff5222dSbjh21 
555dff5222dSbjh21 	if (sc->inputstate == 0) {
556dff5222dSbjh21 		/*
55739d9753eSbjh21 		 * Some devices (seen on trackballs anytime, and on
55839d9753eSbjh21 		 * some mice shortly after reset) output garbage bytes
55939d9753eSbjh21 		 * between packets.  Just ignore them.
560dff5222dSbjh21 		 */
561*eb2723f8Schs 		if ((data & 0xc0) != 0 && (data & 0xff) != PMS_RSTDONE)
562dff5222dSbjh21 			return;	/* not in sync yet, discard input */
563dff5222dSbjh21 	}
5649b39f0e7Smrg 	if (sc->inputstate >= sizeof(sc->packet))
5659b39f0e7Smrg 		panic("inputstate should never be %d", sc->inputstate);
566dff5222dSbjh21 
567dff5222dSbjh21 	sc->packet[sc->inputstate++] = data & 0xff;
568dff5222dSbjh21 	switch (sc->inputstate) {
569dff5222dSbjh21 	case 0:
570dff5222dSbjh21 		/* no useful processing can be done yet */
571dff5222dSbjh21 		break;
572dff5222dSbjh21 
573dff5222dSbjh21 	case 1:
574dff5222dSbjh21 		/*
575dff5222dSbjh21 		 * Why should we test for bit 0x8 and insist on it here?
576dff5222dSbjh21 		 * The old (psm.c and psm_intelli.c) drivers didn't do
577dff5222dSbjh21 		 * it, and there are devices where it does harm (that's
578dff5222dSbjh21 		 * why it is not used if using PMS_STANDARD protocol).
579dff5222dSbjh21 		 * Anyway, it does not to cause any harm to accept packets
580dff5222dSbjh21 		 * without this bit.
581dff5222dSbjh21 		 */
582dff5222dSbjh21 #if 0
583dff5222dSbjh21 		if (sc->protocol == PMS_STANDARD)
584dff5222dSbjh21 			break;
585dff5222dSbjh21 		if (!(sc->packet[0] & 0x8)) {
586dff5222dSbjh21 			DPRINTF(("pmsinput: 0x8 not set in first byte "
587dff5222dSbjh21 			    "[0x%02x], resetting\n", sc->packet[0]));
588dff5222dSbjh21 			sc->inputstate = 0;
589dff5222dSbjh21 			sc->sc_enabled = 0;
590dff5222dSbjh21 			wakeup(&sc->sc_enabled);
591dff5222dSbjh21 			return;
592dff5222dSbjh21 		}
593dff5222dSbjh21 #endif
594dff5222dSbjh21 		break;
595dff5222dSbjh21 
596dff5222dSbjh21 	case 2:
597*eb2723f8Schs 		if (sc->packet[0] == PMS_RSTDONE && sc->packet[1] == 0) {
598*eb2723f8Schs 			device_printf(sc->sc_dev, "received BAT completion, resetting\n");
599*eb2723f8Schs 			sc->inputstate = 0;
600*eb2723f8Schs 			sc->sc_enabled = 0;
601*eb2723f8Schs 			wakeup(&sc->sc_enabled);
602*eb2723f8Schs 			return;
603*eb2723f8Schs 		}
604dff5222dSbjh21 		break;
605dff5222dSbjh21 
606dff5222dSbjh21 	case 4:
607dff5222dSbjh21 		/* Case 4 is a superset of case 3. This is *not* an accident. */
608dff5222dSbjh21 		if (sc->protocol == PMS_SCROLL3) {
609dff5222dSbjh21 			dz = sc->packet[3];
610dff5222dSbjh21 			if (dz >= 128)
611dff5222dSbjh21 				dz -= 256;
612dff5222dSbjh21 			if (dz == -128)
613dff5222dSbjh21 				dz = -127;
614dff5222dSbjh21 		} else if (sc->protocol == PMS_SCROLL5) {
615dff5222dSbjh21 			dz = sc->packet[3] & 0xf;
616dff5222dSbjh21 			if (dz >= 8)
617dff5222dSbjh21 				dz -= 16;
618dff5222dSbjh21 			if (sc->packet[3] & PMS_4BUTMASK)
619dff5222dSbjh21 				newbuttons |= 0x8;
620dff5222dSbjh21 			if (sc->packet[3] & PMS_5BUTMASK)
621dff5222dSbjh21 				newbuttons |= 0x10;
622dff5222dSbjh21 		} else {
623dff5222dSbjh21 			DPRINTF(("pmsinput: why am I looking at this byte?\n"));
624dff5222dSbjh21 			dz = 0;
625dff5222dSbjh21 		}
626dff5222dSbjh21 		/* FALLTHROUGH */
627dff5222dSbjh21 	case 3:
628dff5222dSbjh21 		/*
629dff5222dSbjh21 		 * This is only an endpoint for scroll protocols with 4
630dff5222dSbjh21 		 * bytes, or the standard protocol with 3.
631dff5222dSbjh21 		 */
632dff5222dSbjh21 		if (sc->protocol != PMS_STANDARD && sc->inputstate == 3)
633dff5222dSbjh21 			break;
634dff5222dSbjh21 
635dff5222dSbjh21 		newbuttons |= ((sc->packet[0] & PMS_LBUTMASK) ? 0x1 : 0) |
636dff5222dSbjh21 		    ((sc->packet[0] & PMS_MBUTMASK) ? 0x2 : 0) |
637dff5222dSbjh21 		    ((sc->packet[0] & PMS_RBUTMASK) ? 0x4 : 0);
638dff5222dSbjh21 
639dff5222dSbjh21 		dx = sc->packet[1];
640dff5222dSbjh21 		if (dx >= 128)
641dff5222dSbjh21 			dx -= 256;
642dff5222dSbjh21 		if (dx == -128)
643dff5222dSbjh21 			dx = -127;
644dff5222dSbjh21 
645dff5222dSbjh21 		dy = sc->packet[2];
646dff5222dSbjh21 		if (dy >= 128)
647dff5222dSbjh21 			dy -= 256;
648dff5222dSbjh21 		if (dy == -128)
649dff5222dSbjh21 			dy = -127;
650dff5222dSbjh21 
651dff5222dSbjh21 		sc->inputstate = 0;
652dff5222dSbjh21 		changed = (sc->buttons ^ newbuttons);
653dff5222dSbjh21 		sc->buttons = newbuttons;
654dff5222dSbjh21 
655dff5222dSbjh21 #ifdef PMSDEBUG
656dff5222dSbjh21 		if (sc->protocol == PMS_STANDARD) {
657dff5222dSbjh21 			DPRINTF(("pms: packet: 0x%02x%02x%02x\n",
658dff5222dSbjh21 			    sc->packet[0], sc->packet[1], sc->packet[2]));
659dff5222dSbjh21 		} else {
660dff5222dSbjh21 			DPRINTF(("pms: packet: 0x%02x%02x%02x%02x\n",
661dff5222dSbjh21 			    sc->packet[0], sc->packet[1], sc->packet[2],
662dff5222dSbjh21 			    sc->packet[3]));
663dff5222dSbjh21 		}
664dff5222dSbjh21 #endif
665dff5222dSbjh21 		if (dx || dy || dz || changed) {
666dff5222dSbjh21 #ifdef PMSDEBUG
667dff5222dSbjh21 			DPRINTF(("pms: x %+03d y %+03d z %+03d "
668dff5222dSbjh21 			    "buttons 0x%02x\n",	dx, dy, dz, sc->buttons));
669dff5222dSbjh21 #endif
670dff5222dSbjh21 			wsmouse_input(sc->sc_wsmousedev,
67157c0199dSplunky 			    sc->buttons, dx, dy, dz, 0,
672dff5222dSbjh21 			    WSMOUSE_INPUT_DELTA);
673dff5222dSbjh21 		}
674dff5222dSbjh21 		memset(sc->packet, 0, 4);
675dff5222dSbjh21 		break;
676dff5222dSbjh21 
677dff5222dSbjh21 	/* If we get here, we have problems. */
678dff5222dSbjh21 	default:
679dff5222dSbjh21 		printf("pmsinput: very confused.  resetting.\n");
680dff5222dSbjh21 		sc->inputstate = 0;
681dff5222dSbjh21 		sc->sc_enabled = 0;
682dff5222dSbjh21 		wakeup(&sc->sc_enabled);
683dff5222dSbjh21 		return;
684dff5222dSbjh21 	}
685dff5222dSbjh21 }
6862eb0dcceSjakllsch 
687cead564cSmaya /*
688cead564cSmaya  * Touchpad special command sequence used by Synaptics and others.
689cead564cSmaya  * Sends 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
690cead564cSmaya  */
6912eb0dcceSjakllsch int
6922eb0dcceSjakllsch pms_sliced_command(pckbport_tag_t tag, pckbport_slot_t slot, u_char scmd)
6932eb0dcceSjakllsch {
6942eb0dcceSjakllsch 	u_char cmd[2];
6952eb0dcceSjakllsch 	int i, err, ret = 0;
6962eb0dcceSjakllsch 
6972eb0dcceSjakllsch 	cmd[0] = PMS_SET_SCALE11;
6982eb0dcceSjakllsch 	ret = pckbport_poll_cmd(tag, slot, cmd, 1, 0, NULL, 0);
6992eb0dcceSjakllsch 
7002eb0dcceSjakllsch 	/*
7012eb0dcceSjakllsch 	 * Need to send 4 Set Resolution commands, with the argument
7022eb0dcceSjakllsch 	 * encoded in the bottom most 2 bits.
7032eb0dcceSjakllsch 	 */
7042eb0dcceSjakllsch 	for (i = 6; i >= 0; i -= 2) {
7052eb0dcceSjakllsch 		cmd[0] = PMS_SET_RES;
7062eb0dcceSjakllsch 		cmd[1] = (scmd >> i) & 3;
7072eb0dcceSjakllsch 		err = pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
7082eb0dcceSjakllsch 		if (ret == 0 && err != 0) {
7092eb0dcceSjakllsch 			ret = err;
7102eb0dcceSjakllsch 		}
7112eb0dcceSjakllsch 	}
7122eb0dcceSjakllsch 
7132eb0dcceSjakllsch 	return ret;
7142eb0dcceSjakllsch }
715