xref: /netbsd-src/sys/dev/pckbport/pms.c (revision eb2723f8d4119209ee04772789ed3bcb8009bf41)
1 /* $NetBSD: pms.c,v 1.42 2024/12/07 23:25:19 chs Exp $ */
2 
3 /*-
4  * Copyright (c) 2004 Kentaro Kurahone.
5  * Copyright (c) 2004 Ales Krenek.
6  * Copyright (c) 1994 Charles M. Hannum.
7  * Copyright (c) 1992, 1993 Erik Forsberg.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
19  * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: pms.c,v 1.42 2024/12/07 23:25:19 chs Exp $");
30 
31 #include "opt_pms.h"
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/device.h>
36 #include <sys/ioctl.h>
37 #include <sys/kernel.h>
38 #include <sys/kthread.h>
39 
40 #include <sys/bus.h>
41 
42 #include <dev/pckbport/pckbportvar.h>
43 #ifdef PMS_SYNAPTICS_TOUCHPAD
44 #include <dev/pckbport/synapticsvar.h>
45 #endif
46 #ifdef PMS_ELANTECH_TOUCHPAD
47 #include <dev/pckbport/elantechvar.h>
48 #endif
49 #ifdef PMS_ALPS_TOUCHPAD
50 #include <dev/pckbport/alpsvar.h>
51 #endif
52 
53 #include <dev/pckbport/pmsreg.h>
54 #include <dev/pckbport/pmsvar.h>
55 
56 
57 #include <dev/wscons/wsconsio.h>
58 #include <dev/wscons/wsmousevar.h>
59 
60 #ifdef PMSDEBUG
61 int pmsdebug = 1;
62 #define DPRINTF(x)      if (pmsdebug) printf x
63 #else
64 #define DPRINTF(x)
65 #endif
66 
67 static const enum pms_type tries[] = {
68 	PMS_SCROLL5, PMS_SCROLL3, PMS_STANDARD, PMS_UNKNOWN
69 };
70 
71 static const struct pms_protocol pms_protocols[] = {
72 	{ { 0, 0, 0 }, 0, "unknown protocol" },
73 	{ { 0, 0, 0 }, 0, "no scroll wheel (3 buttons)" },
74 	{ { 200, 100, 80 }, 3, "scroll wheel (3 buttons)" },
75 	{ { 200, 200, 80 }, 4, "scroll wheel (5 buttons)" },
76 	{ { 0, 0, 0 }, 0, "synaptics" },
77 	{ { 0, 0, 0 }, 0, "elantech" }
78 };
79 
80 
81 static int pmsprobe(device_t, cfdata_t, void *);
82 static void pmsattach(device_t, device_t, void *);
83 static void pmsinput(void *, int);
84 
85 CFATTACH_DECL_NEW(pms, sizeof(struct pms_softc),
86     pmsprobe, pmsattach, NULL, NULL);
87 
88 static int	pms_protocol(pckbport_tag_t, pckbport_slot_t);
89 static void	do_enable(struct pms_softc *);
90 static void	do_disable(struct pms_softc *);
91 static void	pms_reset_thread(void*);
92 static int	pms_enable(void *);
93 static int	pms_ioctl(void *, u_long, void *, int, struct lwp *);
94 static void	pms_disable(void *);
95 
96 static bool	pms_suspend(device_t, const pmf_qual_t *);
97 static bool	pms_resume(device_t, const pmf_qual_t *);
98 
99 static const struct wsmouse_accessops pms_accessops = {
100 	.enable = pms_enable,
101 	.ioctl = pms_ioctl,
102 	.disable = pms_disable,
103 };
104 
105 static int
106 pms_protocol(pckbport_tag_t tag, pckbport_slot_t slot)
107 {
108 	u_char cmd[2], resp[1];
109 	int i, j, res;
110 	const struct pms_protocol *p;
111 
112 	for (j = 0; j < sizeof(tries) / sizeof(tries[0]); ++j) {
113 		p = &pms_protocols[tries[j]];
114 		if (!p->rates[0])
115 			break;
116 		cmd[0] = PMS_SET_SAMPLE;
117 		for (i = 0; i < 3; i++) {
118 			cmd[1] = p->rates[i];
119 			res = pckbport_enqueue_cmd(tag, slot, cmd, 2, 0, 1, 0);
120 			if (res)
121 				return PMS_STANDARD;
122 		}
123 
124 		cmd[0] = PMS_SEND_DEV_ID;
125 		res = pckbport_enqueue_cmd(tag, slot, cmd, 1, 1, 1, resp);
126 		if (res)
127 			return PMS_UNKNOWN;
128 		if (resp[0] == p->response) {
129 			DPRINTF(("pms_protocol: found mouse protocol %d\n",
130 				tries[j]));
131 			return tries[j];
132 		}
133 	}
134 	DPRINTF(("pms_protocol: standard PS/2 protocol (no scroll wheel)\n"));
135 	return PMS_STANDARD;
136 }
137 
138 int
139 pmsprobe(device_t parent, cfdata_t match, void *aux)
140 {
141 	struct pckbport_attach_args *pa = aux;
142 	u_char cmd[1], resp[2];
143 	int res;
144 
145 	if (pa->pa_slot != PCKBPORT_AUX_SLOT)
146 		return 0;
147 
148 	/* Flush any garbage. */
149 	pckbport_flush(pa->pa_tag, pa->pa_slot);
150 
151 	/* reset the device */
152 	cmd[0] = PMS_RESET;
153 	res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
154 	if (res) {
155 		aprint_debug("pmsprobe: reset error %d\n", res);
156 		return 0;
157 	}
158 	if (resp[0] != PMS_RSTDONE) {
159 		printf("pmsprobe: reset response 0x%x\n", resp[0]);
160 		return 0;
161 	}
162 
163 	/* get type number (0 = mouse) */
164 	if (resp[1] != 0) {
165 		aprint_debug("pmsprobe: type 0x%x\n", resp[1]);
166 		return 0;
167 	}
168 
169 	return 10;
170 }
171 
172 static void
173 pmsattach(device_t parent, device_t self, void *aux)
174 {
175 	struct pms_softc *sc = device_private(self);
176 	struct pckbport_attach_args *pa = aux;
177 	struct wsmousedev_attach_args a;
178 	u_char cmd[2], resp[2];
179 	int res;
180 
181 	sc->sc_dev = self;
182 	sc->sc_kbctag = pa->pa_tag;
183 	sc->sc_kbcslot = pa->pa_slot;
184 
185 	aprint_naive("\n");
186 	aprint_normal("\n");
187 
188 	/* Flush any garbage. */
189 	pckbport_flush(pa->pa_tag, pa->pa_slot);
190 
191 	/* reset the device */
192 	cmd[0] = PMS_RESET;
193 	res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 2, resp, 1);
194 	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
195 		aprint_debug("pmsattach: reset error\n");
196 		return;
197 	}
198 	sc->inputstate = 0;
199 	sc->buttons = 0;
200 	sc->protocol = PMS_UNKNOWN;
201 
202 #ifdef PMS_SYNAPTICS_TOUCHPAD
203 	/* Probe for synaptics touchpad. */
204 	if (pms_synaptics_probe_init(sc) == 0) {
205 		sc->protocol = PMS_SYNAPTICS;
206 	} else
207 #endif
208 #ifdef PMS_ELANTECH_TOUCHPAD
209 	if (pms_elantech_probe_init(sc) == 0) {
210 		sc->protocol = PMS_ELANTECH;
211 	} else
212 #endif
213 #ifdef PMS_ALPS_TOUCHPAD
214 	if (pms_alps_probe_init(sc) == 0) {
215 		sc->protocol = PMS_ALPS;
216 	} else
217 #endif
218 		/* Install generic handler. */
219 		pckbport_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot,
220 		    pmsinput, sc, device_xname(sc->sc_dev));
221 
222 	a.accessops = &pms_accessops;
223 	a.accesscookie = sc;
224 
225 	/*
226 	 * Attach the wsmouse, saving a handle to it.
227 	 * Note that we don't need to check this pointer against NULL
228 	 * here or in pmsintr, because if this fails pms_enable() will
229 	 * never be called, so pmsinput() will never be called.
230 	 */
231 	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint, CFARGS_NONE);
232 
233 	/* no interrupts until enabled */
234 	cmd[0] = PMS_DEV_DISABLE;
235 	res = pckbport_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 0, NULL, 0);
236 	if (res)
237 		aprint_error("pmsattach: disable error\n");
238 	pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
239 
240 	kthread_create(PRI_NONE, 0, NULL, pms_reset_thread, sc,
241 	    &sc->sc_event_thread, "%s", device_xname(sc->sc_dev));
242 
243 	if (!pmf_device_register(self, pms_suspend, pms_resume))
244 		aprint_error_dev(self, "couldn't establish power handler\n");
245 }
246 
247 static void
248 do_enable(struct pms_softc *sc)
249 {
250 	u_char cmd[2];
251 	int res;
252 
253 	sc->inputstate = 0;
254 	sc->buttons = 0;
255 
256 	pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 1);
257 
258 #ifdef PMS_SYNAPTICS_TOUCHPAD
259 	if (sc->protocol == PMS_SYNAPTICS)
260 		pms_synaptics_enable(sc);
261 #endif
262 #ifdef PMS_ELANTECH_TOUCHPAD
263 	if (sc->protocol == PMS_ELANTECH)
264 		pms_elantech_enable(sc);
265 #endif
266 #ifdef PMS_ALPS_TOUCHPAD
267 	if (sc->protocol == PMS_ALPS)
268 		pms_alps_enable(sc);
269 #endif
270 
271 	cmd[0] = PMS_DEV_ENABLE;
272 	res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
273 	    1, 0, 1, 0);
274 	if (res)
275 		aprint_error("pms_enable: command error %d\n", res);
276 
277 	if (sc->protocol == PMS_UNKNOWN)
278 		sc->protocol = pms_protocol(sc->sc_kbctag, sc->sc_kbcslot);
279 	DPRINTF(("pms_enable: using %s protocol\n",
280 	    pms_protocols[sc->protocol].name));
281 #if 0
282 	{
283 		u_char scmd[2];
284 
285 		scmd[0] = PMS_SET_RES;
286 		scmd[1] = 3; /* 8 counts/mm */
287 		res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd,
288 		    2, 0, 1, 0);
289 		if (res)
290 			printf("pms_enable: setup error1 (%d)\n", res);
291 
292 		scmd[0] = PMS_SET_SCALE21;
293 		res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd,
294 		    1, 0, 1, 0);
295 		if (res)
296 			printf("pms_enable: setup error2 (%d)\n", res);
297 
298 		scmd[0] = PMS_SET_SAMPLE;
299 		scmd[1] = 100; /* 100 samples/sec */
300 		res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, scmd,
301 		    2, 0, 1, 0);
302 		if (res)
303 			printf("pms_enable: setup error3 (%d)\n", res);
304 	}
305 #endif
306 }
307 
308 static void
309 do_disable(struct pms_softc *sc)
310 {
311 	u_char cmd[1];
312 	int res;
313 
314 	cmd[0] = PMS_DEV_DISABLE;
315 	res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
316 	    1, 0, 1, 0);
317 	if (res)
318 		aprint_error("pms_disable: command error\n");
319 
320 	pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
321 }
322 
323 static int
324 pms_enable(void *v)
325 {
326 	struct pms_softc *sc = v;
327 	int s;
328 
329 	if (sc->sc_enabled)
330 		return EBUSY;
331 
332 	do_enable(sc);
333 
334 	s = spltty();
335 	sc->sc_enabled = 1;
336 	splx(s);
337 
338 	return 0;
339 }
340 
341 static void
342 pms_disable(void *v)
343 {
344 	struct pms_softc *sc = v;
345 	int s;
346 
347 	do_disable(sc);
348 
349 	s = spltty();
350 	sc->sc_enabled = 0;
351 	splx(s);
352 }
353 
354 static bool
355 pms_suspend(device_t dv, const pmf_qual_t *qual)
356 {
357 	struct pms_softc *sc = device_private(dv);
358 
359 	if (sc->sc_enabled)
360 		do_disable(sc);
361 
362 	return true;
363 }
364 
365 static bool
366 pms_resume(device_t dv, const pmf_qual_t *qual)
367 {
368 	struct pms_softc *sc = device_private(dv);
369 
370 #ifdef PMS_SYNAPTICS_TOUCHPAD
371 	if (sc->protocol == PMS_SYNAPTICS) {
372 		pms_synaptics_resume(sc);
373 		if (sc->sc_enabled) {
374 			do_enable(sc);
375 		}
376 	} else
377 #endif
378 #ifdef PMS_ELANTECH_TOUCHPAD
379 	if (sc->protocol == PMS_ELANTECH) {
380 		pms_elantech_resume(sc);
381 		if (sc->sc_enabled) {
382 			do_enable(sc);
383 		}
384 	} else
385 #endif
386 #ifdef PMS_ALPS_TOUCHPAD
387 	if (sc->protocol == PMS_ALPS) {
388 		pms_alps_resume(sc);
389 		if (sc->sc_enabled) {
390 			do_enable(sc);
391 		}
392 	} else
393 #endif
394 	if (sc->sc_enabled) {
395 		/* recheck protocol & init mouse */
396 		sc->protocol = PMS_UNKNOWN;
397 		do_enable(sc); /* only if we were suspended */
398 	}
399 
400 	return true;
401 }
402 
403 static int
404 pms_ioctl(void *v, u_long cmd, void *data, int flag,
405     struct lwp *l)
406 {
407 	struct pms_softc *sc = v;
408 	u_char kbcmd[2];
409 	int i;
410 
411 	switch (cmd) {
412 	case WSMOUSEIO_GTYPE:
413 		*(u_int *)data = WSMOUSE_TYPE_PS2;
414 		break;
415 
416 	case WSMOUSEIO_SRES:
417 		i = (*(u_int *)data - 12) / 25;
418 
419 		if (i < 0)
420 			i = 0;
421 
422 		if (i > 3)
423 			i = 3;
424 
425 		kbcmd[0] = PMS_SET_RES;
426 		kbcmd[1] = i;
427 		i = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, kbcmd,
428 		    2, 0, 1, 0);
429 
430 		if (i)
431 			printf("pms_ioctl: SET_RES command error\n");
432 		break;
433 
434 	default:
435 		return EPASSTHROUGH;
436 	}
437 	return 0;
438 }
439 
440 static void
441 pms_reset_thread(void *arg)
442 {
443 	struct pms_softc *sc = arg;
444 	u_char cmd[1], resp[2];
445 	int res;
446 	int save_protocol;
447 
448 	for (;;) {
449 		tsleep(&sc->sc_enabled, PWAIT, "pmsreset", 0);
450 #ifdef PMSDEBUG
451 		if (pmsdebug)
452 #endif
453 #if defined(PMSDEBUG) || defined(DIAGNOSTIC)
454 			aprint_debug_dev(sc->sc_dev,
455 			    "resetting mouse interface\n");
456 #endif
457 		save_protocol = sc->protocol;
458 		pms_disable(sc);
459 		cmd[0] = PMS_RESET;
460 		res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
461 		    1, 2, 1, resp);
462 		if (res) {
463 			DPRINTF(("%s: reset error %d\n",
464 			    device_xname(sc->sc_dev), res));
465 		}
466 
467 		/* For the synaptics and elantech case, leave the protocol alone. */
468 		if (sc->protocol != PMS_SYNAPTICS && sc->protocol != PMS_ELANTECH
469 			&& sc->protocol != PMS_ALPS)
470 			sc->protocol = PMS_UNKNOWN;
471 
472 		pms_enable(sc);
473 		if (sc->protocol != save_protocol) {
474 #if defined(PMSDEBUG) || defined(DIAGNOSTIC)
475 			aprint_verbose_dev(sc->sc_dev,
476 			    "protocol change, sleeping and retrying\n");
477 #endif
478 			pms_disable(sc);
479 			cmd[0] = PMS_RESET;
480 			res = pckbport_enqueue_cmd(sc->sc_kbctag,
481 			    sc->sc_kbcslot, cmd, 1, 2, 1, resp);
482 			if (res) {
483 				DPRINTF(("%s: reset error %d\n",
484 				    device_xname(sc->sc_dev), res));
485 			}
486 			tsleep(pms_reset_thread, PWAIT, "pmsreset", hz);
487 			cmd[0] = PMS_RESET;
488 			res = pckbport_enqueue_cmd(sc->sc_kbctag,
489 			    sc->sc_kbcslot, cmd, 1, 2, 1, resp);
490 			if (res) {
491 				DPRINTF(("%s: reset error %d\n",
492 				    device_xname(sc->sc_dev), res));
493 			}
494 			sc->protocol = PMS_UNKNOWN;	/* reprobe protocol */
495 			pms_enable(sc);
496 #if defined(PMSDEBUG) || defined(DIAGNOSTIC)
497 			if (sc->protocol != save_protocol) {
498 				printf("%s: protocol changed.\n",
499 				    device_xname(sc->sc_dev));
500 			}
501 #endif
502 		}
503 	}
504 }
505 
506 /* Masks for the first byte of a packet */
507 #define PMS_LBUTMASK 0x01
508 #define PMS_RBUTMASK 0x02
509 #define PMS_MBUTMASK 0x04
510 #define PMS_4BUTMASK 0x10
511 #define PMS_5BUTMASK 0x20
512 
513 static void
514 pmsinput(void *vsc, int data)
515 {
516 	struct pms_softc *sc = vsc;
517 	u_int changed;
518 	int dx, dy, dz = 0;
519 	int newbuttons = 0;
520 
521 	if (!sc->sc_enabled) {
522 		/* Interrupts are not expected. Discard the byte. */
523 		return;
524 	}
525 
526 	getmicrouptime(&sc->current);
527 
528 	if (sc->inputstate > 0) {
529 		struct timeval diff;
530 
531 		timersub(&sc->current, &sc->last, &diff);
532 		/*
533 		 * Empirically, the delay should be about 1700us on a standard
534 		 * PS/2 port.  I have seen delays as large as 4500us (rarely)
535 		 * in regular use.  When using a confused mouse, I generally
536 		 * see delays at least as large as 30,000us.  -seebs
537 		 *
538 		 * The thinkpad trackball returns at 22-23ms. So we use
539 		 * >= 40ms. In the future, I'll implement adaptable timeout
540 		 * by increasing the timeout if the mouse reset happens
541 		 * too frequently -christos
542 		 */
543 		if (diff.tv_sec > 0 || diff.tv_usec >= 40000) {
544 			DPRINTF(("pms_input: unusual delay (%ld.%06ld s), "
545 			    "scheduling reset\n",
546 			    (long)diff.tv_sec, (long)diff.tv_usec));
547 			sc->inputstate = 0;
548 			sc->sc_enabled = 0;
549 			wakeup(&sc->sc_enabled);
550 			return;
551 		}
552 	}
553 	sc->last = sc->current;
554 
555 	if (sc->inputstate == 0) {
556 		/*
557 		 * Some devices (seen on trackballs anytime, and on
558 		 * some mice shortly after reset) output garbage bytes
559 		 * between packets.  Just ignore them.
560 		 */
561 		if ((data & 0xc0) != 0 && (data & 0xff) != PMS_RSTDONE)
562 			return;	/* not in sync yet, discard input */
563 	}
564 	if (sc->inputstate >= sizeof(sc->packet))
565 		panic("inputstate should never be %d", sc->inputstate);
566 
567 	sc->packet[sc->inputstate++] = data & 0xff;
568 	switch (sc->inputstate) {
569 	case 0:
570 		/* no useful processing can be done yet */
571 		break;
572 
573 	case 1:
574 		/*
575 		 * Why should we test for bit 0x8 and insist on it here?
576 		 * The old (psm.c and psm_intelli.c) drivers didn't do
577 		 * it, and there are devices where it does harm (that's
578 		 * why it is not used if using PMS_STANDARD protocol).
579 		 * Anyway, it does not to cause any harm to accept packets
580 		 * without this bit.
581 		 */
582 #if 0
583 		if (sc->protocol == PMS_STANDARD)
584 			break;
585 		if (!(sc->packet[0] & 0x8)) {
586 			DPRINTF(("pmsinput: 0x8 not set in first byte "
587 			    "[0x%02x], resetting\n", sc->packet[0]));
588 			sc->inputstate = 0;
589 			sc->sc_enabled = 0;
590 			wakeup(&sc->sc_enabled);
591 			return;
592 		}
593 #endif
594 		break;
595 
596 	case 2:
597 		if (sc->packet[0] == PMS_RSTDONE && sc->packet[1] == 0) {
598 			device_printf(sc->sc_dev, "received BAT completion, resetting\n");
599 			sc->inputstate = 0;
600 			sc->sc_enabled = 0;
601 			wakeup(&sc->sc_enabled);
602 			return;
603 		}
604 		break;
605 
606 	case 4:
607 		/* Case 4 is a superset of case 3. This is *not* an accident. */
608 		if (sc->protocol == PMS_SCROLL3) {
609 			dz = sc->packet[3];
610 			if (dz >= 128)
611 				dz -= 256;
612 			if (dz == -128)
613 				dz = -127;
614 		} else if (sc->protocol == PMS_SCROLL5) {
615 			dz = sc->packet[3] & 0xf;
616 			if (dz >= 8)
617 				dz -= 16;
618 			if (sc->packet[3] & PMS_4BUTMASK)
619 				newbuttons |= 0x8;
620 			if (sc->packet[3] & PMS_5BUTMASK)
621 				newbuttons |= 0x10;
622 		} else {
623 			DPRINTF(("pmsinput: why am I looking at this byte?\n"));
624 			dz = 0;
625 		}
626 		/* FALLTHROUGH */
627 	case 3:
628 		/*
629 		 * This is only an endpoint for scroll protocols with 4
630 		 * bytes, or the standard protocol with 3.
631 		 */
632 		if (sc->protocol != PMS_STANDARD && sc->inputstate == 3)
633 			break;
634 
635 		newbuttons |= ((sc->packet[0] & PMS_LBUTMASK) ? 0x1 : 0) |
636 		    ((sc->packet[0] & PMS_MBUTMASK) ? 0x2 : 0) |
637 		    ((sc->packet[0] & PMS_RBUTMASK) ? 0x4 : 0);
638 
639 		dx = sc->packet[1];
640 		if (dx >= 128)
641 			dx -= 256;
642 		if (dx == -128)
643 			dx = -127;
644 
645 		dy = sc->packet[2];
646 		if (dy >= 128)
647 			dy -= 256;
648 		if (dy == -128)
649 			dy = -127;
650 
651 		sc->inputstate = 0;
652 		changed = (sc->buttons ^ newbuttons);
653 		sc->buttons = newbuttons;
654 
655 #ifdef PMSDEBUG
656 		if (sc->protocol == PMS_STANDARD) {
657 			DPRINTF(("pms: packet: 0x%02x%02x%02x\n",
658 			    sc->packet[0], sc->packet[1], sc->packet[2]));
659 		} else {
660 			DPRINTF(("pms: packet: 0x%02x%02x%02x%02x\n",
661 			    sc->packet[0], sc->packet[1], sc->packet[2],
662 			    sc->packet[3]));
663 		}
664 #endif
665 		if (dx || dy || dz || changed) {
666 #ifdef PMSDEBUG
667 			DPRINTF(("pms: x %+03d y %+03d z %+03d "
668 			    "buttons 0x%02x\n",	dx, dy, dz, sc->buttons));
669 #endif
670 			wsmouse_input(sc->sc_wsmousedev,
671 			    sc->buttons, dx, dy, dz, 0,
672 			    WSMOUSE_INPUT_DELTA);
673 		}
674 		memset(sc->packet, 0, 4);
675 		break;
676 
677 	/* If we get here, we have problems. */
678 	default:
679 		printf("pmsinput: very confused.  resetting.\n");
680 		sc->inputstate = 0;
681 		sc->sc_enabled = 0;
682 		wakeup(&sc->sc_enabled);
683 		return;
684 	}
685 }
686 
687 /*
688  * Touchpad special command sequence used by Synaptics and others.
689  * Sends 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
690  */
691 int
692 pms_sliced_command(pckbport_tag_t tag, pckbport_slot_t slot, u_char scmd)
693 {
694 	u_char cmd[2];
695 	int i, err, ret = 0;
696 
697 	cmd[0] = PMS_SET_SCALE11;
698 	ret = pckbport_poll_cmd(tag, slot, cmd, 1, 0, NULL, 0);
699 
700 	/*
701 	 * Need to send 4 Set Resolution commands, with the argument
702 	 * encoded in the bottom most 2 bits.
703 	 */
704 	for (i = 6; i >= 0; i -= 2) {
705 		cmd[0] = PMS_SET_RES;
706 		cmd[1] = (scmd >> i) & 3;
707 		err = pckbport_poll_cmd(tag, slot, cmd, 2, 0, NULL, 0);
708 		if (ret == 0 && err != 0) {
709 			ret = err;
710 		}
711 	}
712 
713 	return ret;
714 }
715