xref: /openbsd-src/sys/dev/wscons/wsmouse.c (revision b9ae17a00bed12afbf856d60e03f648694a9de20)
1 /* $OpenBSD: wsmouse.c,v 1.74 2024/12/30 02:46:00 guenther Exp $ */
2 /* $NetBSD: wsmouse.c,v 1.35 2005/02/27 00:27:52 perry Exp $ */
3 
4 /*
5  * Copyright (c) 1996, 1997 Christopher G. Demetriou.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Christopher G. Demetriou
18  *	for the NetBSD Project.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * Copyright (c) 1992, 1993
36  *	The Regents of the University of California.  All rights reserved.
37  *
38  * This software was developed by the Computer Systems Engineering group
39  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
40  * contributed to Berkeley.
41  *
42  * All advertising materials mentioning features or use of this software
43  * must display the following acknowledgement:
44  *	This product includes software developed by the University of
45  *	California, Lawrence Berkeley Laboratory.
46  *
47  * Redistribution and use in source and binary forms, with or without
48  * modification, are permitted provided that the following conditions
49  * are met:
50  * 1. Redistributions of source code must retain the above copyright
51  *    notice, this list of conditions and the following disclaimer.
52  * 2. Redistributions in binary form must reproduce the above copyright
53  *    notice, this list of conditions and the following disclaimer in the
54  *    documentation and/or other materials provided with the distribution.
55  * 3. Neither the name of the University nor the names of its contributors
56  *    may be used to endorse or promote products derived from this software
57  *    without specific prior written permission.
58  *
59  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69  * SUCH DAMAGE.
70  *
71  *	@(#)ms.c	8.1 (Berkeley) 6/11/93
72  */
73 
74 /*
75  * Copyright (c) 2015, 2016 Ulf Brosziewski
76  *
77  * Permission to use, copy, modify, and distribute this software for any
78  * purpose with or without fee is hereby granted, provided that the above
79  * copyright notice and this permission notice appear in all copies.
80  *
81  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
82  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
83  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
84  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
85  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
86  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
87  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
88  */
89 
90 /*
91  * Mouse driver.
92  */
93 
94 #include <sys/param.h>
95 #include <sys/conf.h>
96 #include <sys/ioctl.h>
97 #include <sys/fcntl.h>
98 #include <sys/kernel.h>
99 #include <sys/proc.h>
100 #include <sys/syslog.h>
101 #include <sys/systm.h>
102 #include <sys/tty.h>
103 #include <sys/signalvar.h>
104 #include <sys/device.h>
105 #include <sys/vnode.h>
106 #include <sys/malloc.h>
107 
108 #include <dev/wscons/wscons_features.h>
109 #include <dev/wscons/wsconsio.h>
110 #include <dev/wscons/wsmousevar.h>
111 #include <dev/wscons/wseventvar.h>
112 #include <dev/wscons/wsmouseinput.h>
113 
114 #include "wsmux.h"
115 #include "wsdisplay.h"
116 #include "wskbd.h"
117 
118 #include <dev/wscons/wsmuxvar.h>
119 
120 #if defined(WSMUX_DEBUG) && NWSMUX > 0
121 #define	DPRINTF(x)	if (wsmuxdebug) printf x
122 extern int wsmuxdebug;
123 #else
124 #define	DPRINTF(x)
125 #endif
126 
127 struct wsmouse_softc {
128 	struct wsevsrc	sc_base;
129 
130 	const struct wsmouse_accessops *sc_accessops;
131 	void		*sc_accesscookie;
132 
133 	struct wsmouseinput sc_input;
134 
135 	int		sc_refcnt;
136 	u_char		sc_dying;	/* device is being detached */
137 };
138 
139 int	wsmouse_match(struct device *, void *, void *);
140 void	wsmouse_attach(struct device *, struct device *, void *);
141 int	wsmouse_detach(struct device *, int);
142 int	wsmouse_activate(struct device *, int);
143 
144 int	wsmouse_do_ioctl(struct wsmouse_softc *, u_long, caddr_t,
145 			      int, struct proc *);
146 
147 #if NWSMUX > 0
148 int	wsmouse_mux_open(struct wsevsrc *, struct wseventvar *);
149 int	wsmouse_mux_close(struct wsevsrc *);
150 #endif
151 
152 int	wsmousedoioctl(struct device *, u_long, caddr_t, int,
153 			    struct proc *);
154 int	wsmousedoopen(struct wsmouse_softc *, struct wseventvar *);
155 
156 struct cfdriver wsmouse_cd = {
157 	NULL, "wsmouse", DV_TTY
158 };
159 
160 const struct cfattach wsmouse_ca = {
161 	sizeof (struct wsmouse_softc), wsmouse_match, wsmouse_attach,
162 	wsmouse_detach, wsmouse_activate
163 };
164 
165 #if NWSMUX > 0
166 struct wssrcops wsmouse_srcops = {
167 	.type		= WSMUX_MOUSE,
168 	.dopen		= wsmouse_mux_open,
169 	.dclose		= wsmouse_mux_close,
170 	.dioctl		= wsmousedoioctl,
171 	.ddispioctl	= NULL,
172 	.dsetdisplay	= NULL,
173 };
174 #endif
175 
176 /*
177  * Print function (for parent devices).
178  */
179 int
180 wsmousedevprint(void *aux, const char *pnp)
181 {
182 
183 	if (pnp)
184 		printf("wsmouse at %s", pnp);
185 	return (UNCONF);
186 }
187 
188 int
189 wsmouse_match(struct device *parent, void *match, void *aux)
190 {
191 	return (1);
192 }
193 
194 void
195 wsmouse_attach(struct device *parent, struct device *self, void *aux)
196 {
197 	struct wsmouse_softc *sc = (struct wsmouse_softc *)self;
198 	struct wsmousedev_attach_args *ap = aux;
199 #if NWSMUX > 0
200 	int mux, error;
201 #endif
202 
203 	sc->sc_accessops = ap->accessops;
204 	sc->sc_accesscookie = ap->accesscookie;
205 
206 	sc->sc_input.evar = &sc->sc_base.me_evp;
207 
208 #if NWSMUX > 0
209 	sc->sc_base.me_ops = &wsmouse_srcops;
210 	mux = sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux;
211 	if (mux >= 0) {
212 		error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base);
213 		if (error)
214 			printf(" attach error=%d", error);
215 		else
216 			printf(" mux %d", mux);
217 	}
218 #else
219 #if 0	/* not worth keeping, especially since the default value is not -1... */
220 	if (sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux >= 0)
221 		printf(" (mux ignored)");
222 #endif
223 #endif	/* NWSMUX > 0 */
224 
225 	printf("\n");
226 }
227 
228 int
229 wsmouse_activate(struct device *self, int act)
230 {
231 	struct wsmouse_softc *sc = (struct wsmouse_softc *)self;
232 
233 	if (act == DVACT_DEACTIVATE)
234 		sc->sc_dying = 1;
235 	return (0);
236 }
237 
238 /*
239  * Detach a mouse.  To keep track of users of the softc we keep
240  * a reference count that's incremented while inside, e.g., read.
241  * If the mouse is active and the reference count is > 0 (0 is the
242  * normal state) we post an event and then wait for the process
243  * that had the reference to wake us up again.  Then we blow away the
244  * vnode and return (which will deallocate the softc).
245  */
246 int
247 wsmouse_detach(struct device *self, int flags)
248 {
249 	struct wsmouse_softc *sc = (struct wsmouse_softc *)self;
250 	struct wseventvar *evar;
251 	int maj, mn;
252 	int s;
253 
254 #if NWSMUX > 0
255 	/* Tell parent mux we're leaving. */
256 	if (sc->sc_base.me_parent != NULL) {
257 		DPRINTF(("%s\n", __func__));
258 		wsmux_detach_sc(&sc->sc_base);
259 	}
260 #endif
261 
262 	/* If we're open ... */
263 	evar = sc->sc_base.me_evp;
264 	if (evar != NULL) {
265 		s = spltty();
266 		if (--sc->sc_refcnt >= 0) {
267 			/* Wake everyone by generating a dummy event. */
268 			if (++evar->ws_put >= WSEVENT_QSIZE)
269 				evar->ws_put = 0;
270 			WSEVENT_WAKEUP(evar);
271 			/* Wait for processes to go away. */
272 			if (tsleep_nsec(sc, PZERO, "wsmdet", SEC_TO_NSEC(60)))
273 				printf("wsmouse_detach: %s didn't detach\n",
274 				       sc->sc_base.me_dv.dv_xname);
275 		}
276 		splx(s);
277 	}
278 
279 	/* locate the major number */
280 	for (maj = 0; maj < nchrdev; maj++)
281 		if (cdevsw[maj].d_open == wsmouseopen)
282 			break;
283 
284 	/* Nuke the vnodes for any open instances (calls close). */
285 	mn = self->dv_unit;
286 	vdevgone(maj, mn, mn, VCHR);
287 
288 	wsmouse_input_cleanup(&sc->sc_input);
289 
290 	return (0);
291 }
292 
293 int
294 wsmouseopen(dev_t dev, int flags, int mode, struct proc *p)
295 {
296 	struct wsmouse_softc *sc;
297 	struct wseventvar *evar;
298 	int error, unit;
299 
300 	unit = minor(dev);
301 	if (unit >= wsmouse_cd.cd_ndevs ||	/* make sure it was attached */
302 	    (sc = wsmouse_cd.cd_devs[unit]) == NULL)
303 		return (ENXIO);
304 
305 #if NWSMUX > 0
306 	DPRINTF(("%s: %s mux=%p\n", __func__, sc->sc_base.me_dv.dv_xname,
307 		 sc->sc_base.me_parent));
308 #endif
309 
310 	if (sc->sc_dying)
311 		return (EIO);
312 
313 	if ((flags & (FREAD | FWRITE)) == FWRITE)
314 		return (0);			/* always allow open for write
315 						   so ioctl() is possible. */
316 
317 #if NWSMUX > 0
318 	if (sc->sc_base.me_parent != NULL) {
319 		/* Grab the mouse out of the greedy hands of the mux. */
320 		DPRINTF(("%s: detach\n", __func__));
321 		wsmux_detach_sc(&sc->sc_base);
322 	}
323 #endif
324 
325 	if (sc->sc_base.me_evp != NULL)
326 		return (EBUSY);
327 
328 	evar = &sc->sc_base.me_evar;
329 	if (wsevent_init(evar))
330 		return (EBUSY);
331 
332 	error = wsmousedoopen(sc, evar);
333 	if (error)
334 		wsevent_fini(evar);
335 	return (error);
336 }
337 
338 int
339 wsmouseclose(dev_t dev, int flags, int mode, struct proc *p)
340 {
341 	struct wsmouse_softc *sc =
342 	    (struct wsmouse_softc *)wsmouse_cd.cd_devs[minor(dev)];
343 	struct wseventvar *evar = sc->sc_base.me_evp;
344 
345 	if ((flags & (FREAD | FWRITE)) == FWRITE)
346 		/* Not open for read */
347 		return (0);
348 
349 	sc->sc_base.me_evp = NULL;
350 	(*sc->sc_accessops->disable)(sc->sc_accesscookie);
351 	wsevent_fini(evar);
352 
353 #if NWSMUX > 0
354 	if (sc->sc_base.me_parent == NULL) {
355 		int mux, error;
356 
357 		DPRINTF(("%s: attach\n", __func__));
358 		mux = sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux;
359 		if (mux >= 0) {
360 			error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base);
361 			if (error)
362 				printf("%s: can't attach mux (error=%d)\n",
363 				    sc->sc_base.me_dv.dv_xname, error);
364 		}
365 	}
366 #endif
367 
368 	return (0);
369 }
370 
371 int
372 wsmousedoopen(struct wsmouse_softc *sc, struct wseventvar *evp)
373 {
374 	int error;
375 
376 	/* The device could already be attached to a mux. */
377 	if (sc->sc_base.me_evp != NULL)
378 		return (EBUSY);
379 	sc->sc_base.me_evp = evp;
380 
381 	wsmouse_input_reset(&sc->sc_input);
382 
383 	/* enable the device, and punt if that's not possible */
384 	error = (*sc->sc_accessops->enable)(sc->sc_accesscookie);
385 	if (error)
386 		sc->sc_base.me_evp = NULL;
387 	return (error);
388 }
389 
390 int
391 wsmouseread(dev_t dev, struct uio *uio, int flags)
392 {
393 	struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
394 	int error;
395 
396 	if (sc->sc_dying)
397 		return (EIO);
398 
399 #ifdef DIAGNOSTIC
400 	if (sc->sc_base.me_evp == NULL) {
401 		printf("wsmouseread: evp == NULL\n");
402 		return (EINVAL);
403 	}
404 #endif
405 
406 	sc->sc_refcnt++;
407 	error = wsevent_read(sc->sc_base.me_evp, uio, flags);
408 	if (--sc->sc_refcnt < 0) {
409 		wakeup(sc);
410 		error = EIO;
411 	}
412 	return (error);
413 }
414 
415 int
416 wsmouseioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
417 {
418 	return (wsmousedoioctl(wsmouse_cd.cd_devs[minor(dev)],
419 	    cmd, data, flag, p));
420 }
421 
422 /* A wrapper around the ioctl() workhorse to make reference counting easy. */
423 int
424 wsmousedoioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
425     struct proc *p)
426 {
427 	struct wsmouse_softc *sc = (struct wsmouse_softc *)dv;
428 	int error;
429 
430 	sc->sc_refcnt++;
431 	error = wsmouse_do_ioctl(sc, cmd, data, flag, p);
432 	if (--sc->sc_refcnt < 0)
433 		wakeup(sc);
434 	return (error);
435 }
436 
437 int
438 wsmouse_param_ioctl(struct wsmouse_softc *sc,
439     u_long cmd, struct wsmouse_param *params, u_int nparams)
440 {
441 	struct wsmouse_param *buf;
442 	int error, s, size;
443 
444 	if (params == NULL || nparams > WSMOUSECFG_MAX)
445 		return (EINVAL);
446 
447 	size = nparams * sizeof(struct wsmouse_param);
448 	buf = malloc(size, M_DEVBUF, M_WAITOK);
449 	if (buf == NULL)
450 		return (ENOMEM);
451 
452 	if ((error = copyin(params, buf, size))) {
453 		free(buf, M_DEVBUF, size);
454 		return (error);
455 	}
456 
457 	s = spltty();
458 	if (cmd == WSMOUSEIO_SETPARAMS) {
459 		if (wsmouse_set_params((struct device *) sc, buf, nparams))
460 			error = EINVAL;
461 	} else {
462 		if (wsmouse_get_params((struct device *) sc, buf, nparams))
463 			error = EINVAL;
464 		else
465 			error = copyout(buf, params, size);
466 	}
467 	splx(s);
468 	free(buf, M_DEVBUF, size);
469 	return (error);
470 }
471 
472 int
473 wsmouse_do_ioctl(struct wsmouse_softc *sc, u_long cmd, caddr_t data, int flag,
474     struct proc *p)
475 {
476 	struct wseventvar *evar;
477 	int error;
478 
479 	if (sc->sc_dying)
480 		return (EIO);
481 
482 	/*
483 	 * Try the generic ioctls that the wsmouse interface supports.
484 	 */
485 
486 	switch (cmd) {
487 	case FIOASYNC:
488 	case FIOSETOWN:
489 	case TIOCSPGRP:
490 		if ((flag & FWRITE) == 0)
491 			return (EACCES);
492 	}
493 
494 	switch (cmd) {
495 	case FIOASYNC:
496 		if (sc->sc_base.me_evp == NULL)
497 			return (EINVAL);
498 		sc->sc_base.me_evp->ws_async = *(int *)data != 0;
499 		return (0);
500 
501 	case FIOGETOWN:
502 	case TIOCGPGRP:
503 		evar = sc->sc_base.me_evp;
504 		if (evar == NULL)
505 			return (EINVAL);
506 		sigio_getown(&evar->ws_sigio, cmd, data);
507 		return (0);
508 
509 	case FIOSETOWN:
510 	case TIOCSPGRP:
511 		evar = sc->sc_base.me_evp;
512 		if (evar == NULL)
513 			return (EINVAL);
514 		return (sigio_setown(&evar->ws_sigio, cmd, data));
515 
516 	case WSMOUSEIO_GETPARAMS:
517 	case WSMOUSEIO_SETPARAMS:
518 		return (wsmouse_param_ioctl(sc, cmd,
519 		    ((struct wsmouse_parameters *) data)->params,
520 		    ((struct wsmouse_parameters *) data)->nparams));
521 	}
522 
523 	/*
524 	 * Try the mouse driver for WSMOUSEIO ioctls.  It returns -1
525 	 * if it didn't recognize the request.
526 	 */
527 	error = (*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd,
528 	    data, flag, p);
529 	return (error != -1 ? error : ENOTTY);
530 }
531 
532 int
533 wsmousekqfilter(dev_t dev, struct knote *kn)
534 {
535 	struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
536 
537 	if (sc->sc_base.me_evp == NULL)
538 		return (ENXIO);
539 	return (wsevent_kqfilter(sc->sc_base.me_evp, kn));
540 }
541 
542 #if NWSMUX > 0
543 int
544 wsmouse_mux_open(struct wsevsrc *me, struct wseventvar *evp)
545 {
546 	struct wsmouse_softc *sc = (struct wsmouse_softc *)me;
547 
548 	return (wsmousedoopen(sc, evp));
549 }
550 
551 int
552 wsmouse_mux_close(struct wsevsrc *me)
553 {
554 	struct wsmouse_softc *sc = (struct wsmouse_softc *)me;
555 
556 	(*sc->sc_accessops->disable)(sc->sc_accesscookie);
557 	sc->sc_base.me_evp = NULL;
558 
559 	return (0);
560 }
561 
562 int
563 wsmouse_add_mux(int unit, struct wsmux_softc *muxsc)
564 {
565 	struct wsmouse_softc *sc;
566 
567 	if (unit < 0 || unit >= wsmouse_cd.cd_ndevs ||
568 	    (sc = wsmouse_cd.cd_devs[unit]) == NULL)
569 		return (ENXIO);
570 
571 	if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
572 		return (EBUSY);
573 
574 	return (wsmux_attach_sc(muxsc, &sc->sc_base));
575 }
576 #endif	/* NWSMUX > 0 */
577 
578 void
579 wsmouse_buttons(struct device *sc, u_int buttons)
580 {
581 	struct btn_state *btn = &((struct wsmouse_softc *) sc)->sc_input.btn;
582 
583 	if (btn->sync)
584 		/* Restore the old state. */
585 		btn->buttons ^= btn->sync;
586 
587 	btn->sync = btn->buttons ^ buttons;
588 	btn->buttons = buttons;
589 }
590 
591 void
592 wsmouse_motion(struct device *sc, int dx, int dy, int dz, int dw)
593 {
594 	struct motion_state *motion =
595 	    &((struct wsmouse_softc *) sc)->sc_input.motion;
596 
597 	motion->dx = dx;
598 	motion->dy = dy;
599 	motion->dz = dz;
600 	motion->dw = dw;
601 	if (dx || dy || dz || dw)
602 		motion->sync |= SYNC_DELTAS;
603 }
604 
605 static inline void
606 set_x(struct position *pos, int x, u_int *sync, u_int mask)
607 {
608 	if (*sync & mask) {
609 		if (x == pos->x)
610 			return;
611 		pos->x -= pos->dx;
612 		pos->acc_dx -= pos->dx;
613 	}
614 	if ((pos->dx = x - pos->x)) {
615 		pos->x = x;
616 		if ((pos->dx > 0) == (pos->acc_dx > 0))
617 			pos->acc_dx += pos->dx;
618 		else
619 			pos->acc_dx = pos->dx;
620 		*sync |= mask;
621 	}
622 }
623 
624 static inline void
625 set_y(struct position *pos, int y, u_int *sync, u_int mask)
626 {
627 	if (*sync & mask) {
628 		if (y == pos->y)
629 			return;
630 		pos->y -= pos->dy;
631 		pos->acc_dy -= pos->dy;
632 	}
633 	if ((pos->dy = y - pos->y)) {
634 		pos->y = y;
635 		if ((pos->dy > 0) == (pos->acc_dy > 0))
636 			pos->acc_dy += pos->dy;
637 		else
638 			pos->acc_dy = pos->dy;
639 		*sync |= mask;
640 	}
641 }
642 
643 static inline void
644 cleardeltas(struct position *pos)
645 {
646 	pos->dx = pos->acc_dx = 0;
647 	pos->dy = pos->acc_dy = 0;
648 }
649 
650 void
651 wsmouse_position(struct device *sc, int x, int y)
652 {
653 	struct motion_state *motion =
654 	    &((struct wsmouse_softc *) sc)->sc_input.motion;
655 
656 	set_x(&motion->pos, x, &motion->sync, SYNC_X);
657 	set_y(&motion->pos, y, &motion->sync, SYNC_Y);
658 }
659 
660 static inline int
661 normalized_pressure(struct wsmouseinput *input, int pressure)
662 {
663 	int limit = imax(input->touch.min_pressure, 1);
664 
665 	if (pressure >= limit)
666 		return pressure;
667 	else
668 		return (pressure < 0 ? limit : 0);
669 }
670 
671 void
672 wsmouse_touch(struct device *sc, int pressure, int contacts)
673 {
674 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
675 	struct touch_state *touch = &input->touch;
676 
677 	pressure = normalized_pressure(input, pressure);
678 	contacts = (pressure ? imax(contacts, 1) : 0);
679 
680 	if (pressure == 0 || pressure != touch->pressure) {
681 		/*
682 		 * pressure == 0: Drivers may report possibly arbitrary
683 		 * coordinates in this case; touch_update will correct them.
684 		 */
685 		touch->pressure = pressure;
686 		touch->sync |= SYNC_PRESSURE;
687 	}
688 	if (contacts != touch->contacts) {
689 		touch->contacts = contacts;
690 		touch->sync |= SYNC_CONTACTS;
691 	}
692 }
693 
694 void
695 wsmouse_mtstate(struct device *sc, int slot, int x, int y, int pressure)
696 {
697 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
698 	struct mt_state *mt = &input->mt;
699 	struct mt_slot *mts;
700 	u_int bit;
701 
702 	if (slot < 0 || slot >= mt->num_slots)
703 		return;
704 
705 	bit = (1 << slot);
706 	mt->frame |= bit;
707 
708 	mts = &mt->slots[slot];
709 
710 	set_x(&mts->pos, x, mt->sync + MTS_X, bit);
711 	set_y(&mts->pos, y, mt->sync + MTS_Y, bit);
712 
713 	/* Is this a new touch? */
714 	if ((mt->touches & bit) == (mt->sync[MTS_TOUCH] & bit))
715 		cleardeltas(&mts->pos);
716 
717 	pressure = normalized_pressure(input, pressure);
718 	if (pressure != mts->pressure) {
719 		mts->pressure = pressure;
720 		mt->sync[MTS_PRESSURE] |= bit;
721 
722 		if (pressure) {
723 			if ((mt->touches & bit) == 0) {
724 				mt->num_touches++;
725 				mt->touches |= bit;
726 				mt->sync[MTS_TOUCH] |= bit;
727 
728 				mt->sync[MTS_X] |= bit;
729 				mt->sync[MTS_Y] |= bit;
730 			}
731 		} else if (mt->touches & bit) {
732 			mt->num_touches--;
733 			mt->touches ^= bit;
734 			mt->sync[MTS_TOUCH] |= bit;
735 			mt->ptr_mask &= mt->touches;
736 		}
737 	}
738 }
739 
740 void
741 wsmouse_set(struct device *sc, enum wsmouseval type, int value, int aux)
742 {
743 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
744 	struct mt_slot *mts;
745 
746 	if (WSMOUSE_IS_MT_CODE(type)) {
747 		if (aux < 0 || aux >= input->mt.num_slots)
748 			return;
749 		mts = &input->mt.slots[aux];
750 	}
751 
752 	switch (type) {
753 	case WSMOUSE_REL_X:
754 		value += input->motion.pos.x; /* fall through */
755 	case WSMOUSE_ABS_X:
756 		wsmouse_position(sc, value, input->motion.pos.y);
757 		return;
758 	case WSMOUSE_REL_Y:
759 		value += input->motion.pos.y; /* fall through */
760 	case WSMOUSE_ABS_Y:
761 		wsmouse_position(sc, input->motion.pos.x, value);
762 		return;
763 	case WSMOUSE_PRESSURE:
764 		wsmouse_touch(sc, value, input->touch.contacts);
765 		return;
766 	case WSMOUSE_CONTACTS:
767 		/* Contact counts can be overridden by wsmouse_touch. */
768 		if (value != input->touch.contacts) {
769 			input->touch.contacts = value;
770 			input->touch.sync |= SYNC_CONTACTS;
771 		}
772 		return;
773 	case WSMOUSE_TOUCH_WIDTH:
774 		if (value != input->touch.width) {
775 			input->touch.width = value;
776 			input->touch.sync |= SYNC_TOUCH_WIDTH;
777 		}
778 		return;
779 	case WSMOUSE_MT_REL_X:
780 		value += mts->pos.x; /* fall through */
781 	case WSMOUSE_MT_ABS_X:
782 		wsmouse_mtstate(sc, aux, value, mts->pos.y, mts->pressure);
783 		return;
784 	case WSMOUSE_MT_REL_Y:
785 		value += mts->pos.y; /* fall through */
786 	case WSMOUSE_MT_ABS_Y:
787 		wsmouse_mtstate(sc, aux, mts->pos.x, value, mts->pressure);
788 		return;
789 	case WSMOUSE_MT_PRESSURE:
790 		wsmouse_mtstate(sc, aux, mts->pos.x, mts->pos.y, value);
791 		return;
792 	}
793 }
794 
795 /* Make touch and motion state consistent. */
796 void
797 wsmouse_touch_update(struct wsmouseinput *input)
798 {
799 	struct motion_state *motion = &input->motion;
800 	struct touch_state *touch = &input->touch;
801 
802 	if (touch->pressure == 0) {
803 		/*
804 		 * There may be zero coordinates, or coordinates of
805 		 * touches with pressure values below min_pressure.
806 		 */
807 		if (motion->sync & SYNC_POSITION) {
808 			/* Restore valid coordinates. */
809 			motion->pos.x -= motion->pos.dx;
810 			motion->pos.y -= motion->pos.dy;
811 			motion->sync &= ~SYNC_POSITION;
812 		}
813 
814 		if (touch->prev_contacts == 0)
815 			touch->sync &= ~SYNC_PRESSURE;
816 
817 	}
818 
819 	if (touch->sync & SYNC_CONTACTS)
820 		/* Suppress pointer movement. */
821 		cleardeltas(&motion->pos);
822 
823 	if ((touch->sync & SYNC_PRESSURE) && touch->min_pressure) {
824 		if (touch->pressure >= input->filter.pressure_hi)
825 			touch->min_pressure = input->filter.pressure_lo;
826 		else if (touch->pressure < input->filter.pressure_lo)
827 			touch->min_pressure = input->filter.pressure_hi;
828 	}
829 }
830 
831 /* Normalize multitouch state. */
832 void
833 wsmouse_mt_update(struct wsmouseinput *input)
834 {
835 	int i;
836 
837 	/*
838 	 * The same as above: There may be arbitrary coordinates if
839 	 * (pressure == 0). Clear the sync flags for touches that have
840 	 * been released.
841 	 */
842 	if (input->mt.frame & ~input->mt.touches) {
843 		for (i = MTS_X; i < MTS_SIZE; i++)
844 			input->mt.sync[i] &= input->mt.touches;
845 	}
846 }
847 
848 /* Return TRUE if a coordinate update may be noise. */
849 int
850 wsmouse_hysteresis(struct wsmouseinput *input, struct position *pos)
851 {
852 	return (abs(pos->acc_dx) < input->filter.h.hysteresis
853 	    && abs(pos->acc_dy) < input->filter.v.hysteresis);
854 }
855 
856 /*
857  * Select the pointer-controlling MT slot.
858  *
859  * Pointer-control is assigned to slots with non-zero motion deltas if
860  * at least one such slot exists. This function doesn't impose any
861  * restrictions on the way drivers use wsmouse_mtstate(), it covers
862  * partial, unordered, and "delta-filtered" input.
863  *
864  * The "cycle" is the set of slots with X/Y updates in previous sync
865  * operations; it will be cleared and rebuilt whenever a slot that is
866  * being updated is already a member. If a cycle ends that doesn't
867  * contain the pointer-controlling slot, a new slot will be selected.
868  */
869 void
870 wsmouse_ptr_ctrl(struct wsmouseinput *input)
871 {
872 	struct mt_state *mt = &input->mt;
873 	u_int updates;
874 	int select, slot;
875 
876 	mt->prev_ptr = mt->ptr;
877 
878 	if (mt->num_touches <= 1) {
879 		mt->ptr = mt->touches;
880 		mt->ptr_cycle = mt->ptr;
881 		return;
882 	}
883 
884 	updates = (mt->sync[MTS_X] | mt->sync[MTS_Y]) & ~mt->sync[MTS_TOUCH];
885 	FOREACHBIT(updates, slot) {
886 		/*
887 		 * Touches that just produce noise are no problem if the
888 		 * frequency of zero deltas is high enough, but there might
889 		 * be no guarantee for that.
890 		 */
891 		if (wsmouse_hysteresis(input, &mt->slots[slot].pos))
892 			updates ^= (1 << slot);
893 	}
894 
895 	/*
896 	 * If there is no pointer-controlling slot, or if it should be
897 	 * masked, select a new one.
898 	 */
899 	select = ((mt->ptr & mt->touches & ~mt->ptr_mask) == 0);
900 
901 	/* Remove slots without coordinate deltas from the cycle. */
902 	mt->ptr_cycle &= ~(mt->frame ^ updates);
903 
904 	if (mt->ptr_cycle & updates) {
905 		select |= ((mt->ptr_cycle & mt->ptr) == 0);
906 		mt->ptr_cycle = updates;
907 	} else {
908 		mt->ptr_cycle |= updates;
909 	}
910 	if (select) {
911 		if (mt->ptr_cycle & ~mt->ptr_mask)
912 			slot = ffs(mt->ptr_cycle & ~mt->ptr_mask) - 1;
913 		else if (mt->touches & ~mt->ptr_mask)
914 			slot = ffs(mt->touches & ~mt->ptr_mask) - 1;
915 		else
916 			slot = ffs(mt->touches) - 1;
917 		mt->ptr = (1 << slot);
918 	}
919 }
920 
921 /* Derive touch and motion state from MT state. */
922 void
923 wsmouse_mt_convert(struct device *sc)
924 {
925 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
926 	struct mt_state *mt = &input->mt;
927 	struct mt_slot *mts;
928 	int slot, pressure;
929 
930 	wsmouse_ptr_ctrl(input);
931 
932 	if (mt->ptr) {
933 		slot = ffs(mt->ptr) - 1;
934 		mts = &mt->slots[slot];
935 		if (mts->pos.x != input->motion.pos.x)
936 			input->motion.sync |= SYNC_X;
937 		if (mts->pos.y != input->motion.pos.y)
938 			input->motion.sync |= SYNC_Y;
939 		if (mt->ptr != mt->prev_ptr)
940 			/* Suppress pointer movement. */
941 			mts->pos.dx = mts->pos.dy = 0;
942 		memcpy(&input->motion.pos, &mts->pos, sizeof(struct position));
943 
944 		pressure = mts->pressure;
945 	} else {
946 		pressure = 0;
947 	}
948 
949 	wsmouse_touch(sc, pressure, mt->num_touches);
950 }
951 
952 void
953 wsmouse_evq_put(struct evq_access *evq, int ev_type, int ev_value)
954 {
955 	struct wscons_event *ev;
956 	int space;
957 
958 	space = evq->evar->ws_get - evq->put;
959 	if (space != 1 && space != 1 - WSEVENT_QSIZE) {
960 		ev = &evq->evar->ws_q[evq->put++];
961 		evq->put %= WSEVENT_QSIZE;
962 		ev->type = ev_type;
963 		ev->value = ev_value;
964 		memcpy(&ev->time, &evq->ts, sizeof(struct timespec));
965 		evq->result |= EVQ_RESULT_SUCCESS;
966 	} else {
967 		evq->result = EVQ_RESULT_OVERFLOW;
968 	}
969 }
970 
971 
972 void
973 wsmouse_btn_sync(struct btn_state *btn, struct evq_access *evq)
974 {
975 	int button, ev_type;
976 	u_int bit, sync;
977 
978 	for (sync = btn->sync; sync; sync ^= bit) {
979 		button = ffs(sync) - 1;
980 		bit = (1 << button);
981 		ev_type = (btn->buttons & bit) ? BTN_DOWN_EV : BTN_UP_EV;
982 		wsmouse_evq_put(evq, ev_type, button);
983 	}
984 }
985 
986 /*
987  * Scale with a [*.12] fixed-point factor and a remainder:
988  */
989 static inline int
990 scale(int val, int factor, int *rmdr)
991 {
992 	val = val * factor + *rmdr;
993 	if (val >= 0) {
994 		*rmdr = val & 0xfff;
995 		return (val >> 12);
996 	} else {
997 		*rmdr = -(-val & 0xfff);
998 		return -(-val >> 12);
999 	}
1000 }
1001 
1002 void
1003 wsmouse_motion_sync(struct wsmouseinput *input, struct evq_access *evq)
1004 {
1005 	struct motion_state *motion = &input->motion;
1006 	struct axis_filter *h = &input->filter.h;
1007 	struct axis_filter *v = &input->filter.v;
1008 	int x, y, dx, dy, dz, dw;
1009 
1010 	if (motion->sync & SYNC_DELTAS) {
1011 		dx = h->inv ? -motion->dx : motion->dx;
1012 		dy = v->inv ? -motion->dy : motion->dy;
1013 		if (h->scale)
1014 			dx = scale(dx, h->scale, &h->rmdr);
1015 		if (v->scale)
1016 			dy = scale(dy, v->scale, &v->rmdr);
1017 		if (dx)
1018 			wsmouse_evq_put(evq, DELTA_X_EV(input), dx);
1019 		if (dy)
1020 			wsmouse_evq_put(evq, DELTA_Y_EV(input), dy);
1021 		if (motion->dz) {
1022 			dz = (input->flags & REVERSE_SCROLLING)
1023 			    ? -motion->dz : motion->dz;
1024 			if (IS_TOUCHPAD(input))
1025 				wsmouse_evq_put(evq, VSCROLL_EV, dz);
1026 			else
1027 				wsmouse_evq_put(evq, DELTA_Z_EV, dz);
1028 		}
1029 		if (motion->dw) {
1030 			dw = (input->flags & REVERSE_SCROLLING)
1031 			    ? -motion->dw : motion->dw;
1032 			if (IS_TOUCHPAD(input))
1033 				wsmouse_evq_put(evq, HSCROLL_EV, dw);
1034 			else
1035 				wsmouse_evq_put(evq, DELTA_W_EV, dw);
1036 		}
1037 	}
1038 	if (motion->sync & SYNC_POSITION) {
1039 		if (motion->sync & SYNC_X) {
1040 			x = (h->inv ? h->inv - motion->pos.x : motion->pos.x);
1041 			wsmouse_evq_put(evq, ABS_X_EV(input), x);
1042 		}
1043 		if (motion->sync & SYNC_Y) {
1044 			y = (v->inv ? v->inv - motion->pos.y : motion->pos.y);
1045 			wsmouse_evq_put(evq, ABS_Y_EV(input), y);
1046 		}
1047 		if (motion->pos.dx == 0 && motion->pos.dy == 0
1048 		    && (input->flags & TPAD_NATIVE_MODE ))
1049 			/* Suppress pointer motion. */
1050 			wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_RESET, 0);
1051 	}
1052 }
1053 
1054 void
1055 wsmouse_touch_sync(struct wsmouseinput *input, struct evq_access *evq)
1056 {
1057 	struct touch_state *touch = &input->touch;
1058 
1059 	if (touch->sync & SYNC_PRESSURE)
1060 		wsmouse_evq_put(evq, ABS_Z_EV, touch->pressure);
1061 	if (touch->sync & SYNC_CONTACTS)
1062 		wsmouse_evq_put(evq, ABS_W_EV, touch->contacts);
1063 	if ((touch->sync & SYNC_TOUCH_WIDTH)
1064 	    && (input->flags & TPAD_NATIVE_MODE))
1065 		wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_WIDTH, touch->width);
1066 }
1067 
1068 void
1069 wsmouse_log_input(struct wsmouseinput *input, struct timespec *ts)
1070 {
1071 	struct motion_state *motion = &input->motion;
1072 	int t_sync, mt_sync;
1073 
1074 	t_sync = (input->touch.sync & SYNC_CONTACTS);
1075 	mt_sync = (input->mt.frame && (input->mt.sync[MTS_TOUCH]
1076 	    || input->mt.ptr != input->mt.prev_ptr));
1077 
1078 	if (motion->sync || mt_sync || t_sync || input->btn.sync)
1079 		printf("[%s-in][%04d]", DEVNAME(input), LOGTIME(ts));
1080 	else
1081 		return;
1082 
1083 	if (motion->sync & SYNC_POSITION)
1084 		printf(" abs:%d,%d", motion->pos.x, motion->pos.y);
1085 	if (motion->sync & SYNC_DELTAS)
1086 		printf(" rel:%d,%d,%d,%d", motion->dx, motion->dy,
1087 		    motion->dz, motion->dw);
1088 	if (mt_sync)
1089 		printf(" mt:0x%02x:%d", input->mt.touches,
1090 		    ffs(input->mt.ptr) - 1);
1091 	else if (t_sync)
1092 		printf(" t:%d", input->touch.contacts);
1093 	if (input->btn.sync)
1094 		printf(" btn:0x%02x", input->btn.buttons);
1095 	printf("\n");
1096 }
1097 
1098 void
1099 wsmouse_log_events(struct wsmouseinput *input, struct evq_access *evq)
1100 {
1101 	struct wscons_event *ev;
1102 	int n = evq->evar->ws_put;
1103 
1104 	if (n != evq->put) {
1105 		printf("[%s-ev][%04d]", DEVNAME(input), LOGTIME(&evq->ts));
1106 		while (n != evq->put) {
1107 			ev = &evq->evar->ws_q[n++];
1108 			n %= WSEVENT_QSIZE;
1109 			printf(" %d:%d", ev->type, ev->value);
1110 		}
1111 		printf("\n");
1112 	}
1113 }
1114 
1115 static inline void
1116 clear_sync_flags(struct wsmouseinput *input)
1117 {
1118 	int i;
1119 
1120 	input->btn.sync = 0;
1121 	input->sbtn.sync = 0;
1122 	input->motion.sync = 0;
1123 	input->touch.sync = 0;
1124 	input->touch.prev_contacts = input->touch.contacts;
1125 	if (input->mt.frame) {
1126 		input->mt.frame = 0;
1127 		for (i = 0; i < MTS_SIZE; i++)
1128 			input->mt.sync[i] = 0;
1129 	}
1130 }
1131 
1132 void
1133 wsmouse_input_sync(struct device *sc)
1134 {
1135 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
1136 	struct evq_access evq;
1137 
1138 	evq.evar = *input->evar;
1139 	if (evq.evar == NULL)
1140 		return;
1141 	evq.put = evq.evar->ws_put;
1142 	evq.result = EVQ_RESULT_NONE;
1143 	getnanotime(&evq.ts);
1144 
1145 	enqueue_randomness(input->btn.buttons
1146 	    ^ input->motion.dx ^ input->motion.dy
1147 	    ^ input->motion.pos.x ^ input->motion.pos.y
1148 	    ^ input->motion.dz ^ input->motion.dw);
1149 
1150 	if (input->mt.frame) {
1151 		wsmouse_mt_update(input);
1152 		wsmouse_mt_convert(sc);
1153 	}
1154 	if (input->touch.sync)
1155 		wsmouse_touch_update(input);
1156 
1157 	if (input->flags & LOG_INPUT)
1158 		wsmouse_log_input(input, &evq.ts);
1159 
1160 	if (input->flags & TPAD_COMPAT_MODE)
1161 		wstpad_compat_convert(input, &evq);
1162 
1163 	if (input->flags & RESYNC) {
1164 		input->flags &= ~RESYNC;
1165 		input->motion.sync &= SYNC_POSITION;
1166 	}
1167 
1168 	if (input->btn.sync)
1169 		wsmouse_btn_sync(&input->btn, &evq);
1170 	if (input->sbtn.sync)
1171 		wsmouse_btn_sync(&input->sbtn, &evq);
1172 	if (input->motion.sync)
1173 		wsmouse_motion_sync(input, &evq);
1174 	if (input->touch.sync)
1175 		wsmouse_touch_sync(input, &evq);
1176 	/* No MT events are generated yet. */
1177 
1178 	if (evq.result == EVQ_RESULT_SUCCESS) {
1179 		wsmouse_evq_put(&evq, WSCONS_EVENT_SYNC, 0);
1180 		if (evq.result == EVQ_RESULT_SUCCESS) {
1181 			if (input->flags & LOG_EVENTS) {
1182 				wsmouse_log_events(input, &evq);
1183 			}
1184 			evq.evar->ws_put = evq.put;
1185 			WSEVENT_WAKEUP(evq.evar);
1186 		}
1187 	}
1188 
1189 	if (evq.result != EVQ_RESULT_OVERFLOW)
1190 		clear_sync_flags(input);
1191 	else
1192 		input->flags |= RESYNC;
1193 }
1194 
1195 int
1196 wsmouse_id_to_slot(struct device *sc, int id)
1197 {
1198 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
1199 	struct mt_state *mt = &input->mt;
1200 	int slot;
1201 
1202 	if (mt->num_slots == 0)
1203 		return (-1);
1204 
1205 	FOREACHBIT(mt->touches, slot) {
1206 		if (mt->slots[slot].id == id)
1207 			return slot;
1208 	}
1209 	slot = ffs(~(mt->touches | mt->frame)) - 1;
1210 	if (slot >= 0 && slot < mt->num_slots) {
1211 		mt->frame |= 1 << slot;
1212 		mt->slots[slot].id = id;
1213 		return (slot);
1214 	} else {
1215 		return (-1);
1216 	}
1217 }
1218 
1219 /*
1220  * Find a minimum-weight matching for an m-by-n matrix.
1221  *
1222  * m must be greater than or equal to n. The size of the buffer must be
1223  * at least 3m + 3n.
1224  *
1225  * On return, the first m elements of the buffer contain the row-to-
1226  * column mappings, i.e., buffer[i] is the column index for row i, or -1
1227  * if there is no assignment for that row (which may happen if n < m).
1228  *
1229  * Wrong results because of overflows will not occur with input values
1230  * in the range of 0 to INT_MAX / 2 inclusive.
1231  *
1232  * The function applies the Dinic-Kronrod algorithm. It is not modern or
1233  * popular, but it seems to be a good choice for small matrices at least.
1234  * The original form of the algorithm is modified as follows: There is no
1235  * initial search for row minima, the initial assignments are in a
1236  * "virtual" column with the index -1 and zero values. This permits inputs
1237  * with n < m, and it simplifies the reassignments.
1238  */
1239 void
1240 wsmouse_matching(int *matrix, int m, int n, int *buffer)
1241 {
1242 	int i, j, k, d, e, row, col, delta;
1243 	int *p;
1244 	int *r2c = buffer;	/* row-to-column assignments */
1245 	int *red = r2c + m;	/* reduced values of the assignments */
1246 	int *mc = red + m;	/* row-wise minimal elements of cs */
1247 	int *cs = mc + m;	/* the column set */
1248 	int *c2r = cs + n;	/* column-to-row assignments in cs */
1249 	int *cd = c2r + n;	/* column deltas (reduction) */
1250 
1251 	for (p = r2c; p < red; *p++ = -1) {}
1252 	for (; p < mc; *p++ = 0) {}
1253 	for (col = 0; col < n; col++) {
1254 		delta = INT_MAX;
1255 		row = 0;
1256 		for (i = 0, p = matrix + col; i < m; i++, p += n) {
1257 			d = *p - red[i];
1258 			if (d < delta || (d == delta && r2c[i] < 0)) {
1259 				delta = d;
1260 				row = i;
1261 			}
1262 		}
1263 		cd[col] = delta;
1264 		if (r2c[row] < 0) {
1265 			r2c[row] = col;
1266 			continue;
1267 		}
1268 		for (p = mc; p < cs; *p++ = col) {}
1269 		for (k = 0; (j = r2c[row]) >= 0;) {
1270 			cs[k++] = j;
1271 			c2r[j] = row;
1272 			mc[row] -= n;
1273 			delta = INT_MAX;
1274 			for (i = 0, p = matrix; i < m; i++, p += n)
1275 				if (mc[i] >= 0) {
1276 					d = p[mc[i]] - cd[mc[i]];
1277 					e = p[j] - cd[j];
1278 					if (e < d) {
1279 						d = e;
1280 						mc[i] = j;
1281 					}
1282 					d -= red[i];
1283 					if (d < delta || (d == delta
1284 					    && r2c[i] < 0)) {
1285 						delta = d;
1286 						row = i;
1287 					}
1288 				}
1289 			cd[col] += delta;
1290 			for (i = 0; i < k; i++) {
1291 				cd[cs[i]] += delta;
1292 				red[c2r[cs[i]]] -= delta;
1293 			}
1294 		}
1295 		for (j = mc[row]; (r2c[row] = j) != col;) {
1296 			row = c2r[j];
1297 			j = mc[row] + n;
1298 		}
1299 	}
1300 }
1301 
1302 /*
1303  * Assign slot numbers to the points in the pt array, and update all slots by
1304  * calling wsmouse_mtstate internally.  The slot numbers are passed to the
1305  * caller in the pt->slot fields.
1306  *
1307  * The slot assignment pairs the points with points of the previous frame in
1308  * such a way that the sum of the squared distances is minimal.  Using
1309  * squares instead of simple distances favours assignments with more uniform
1310  * distances, and it is faster.
1311  */
1312 void
1313 wsmouse_mtframe(struct device *sc, struct mtpoint *pt, int size)
1314 {
1315 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
1316 	struct mt_state *mt = &input->mt;
1317 	int i, j, m, n, dx, dy, slot, maxdist;
1318 	int *p, *r2c, *c2r;
1319 	u_int touches;
1320 
1321 	if (mt->num_slots == 0 || mt->matrix == NULL)
1322 		return;
1323 
1324 	size = imax(0, imin(size, mt->num_slots));
1325 	p = mt->matrix;
1326 	touches = mt->touches;
1327 	if (mt->num_touches >= size) {
1328 		FOREACHBIT(touches, slot)
1329 			for (i = 0; i < size; i++) {
1330 				dx = pt[i].x - mt->slots[slot].pos.x;
1331 				dy = pt[i].y - mt->slots[slot].pos.y;
1332 				*p++ = dx * dx + dy * dy;
1333 			}
1334 		m = mt->num_touches;
1335 		n = size;
1336 	} else {
1337 		for (i = 0; i < size; i++)
1338 			FOREACHBIT(touches, slot) {
1339 				dx = pt[i].x - mt->slots[slot].pos.x;
1340 				dy = pt[i].y - mt->slots[slot].pos.y;
1341 				*p++ = dx * dx + dy * dy;
1342 			}
1343 		m = size;
1344 		n = mt->num_touches;
1345 	}
1346 	wsmouse_matching(mt->matrix, m, n, p);
1347 
1348 	r2c = p;
1349 	c2r = p + m;
1350 	maxdist = input->filter.tracking_maxdist;
1351 	maxdist = (maxdist ? maxdist * maxdist : INT_MAX);
1352 	for (i = 0, p = mt->matrix; i < m; i++, p += n)
1353 		if ((j = r2c[i]) >= 0) {
1354 			if (p[j] <= maxdist)
1355 				c2r[j] = i;
1356 			else
1357 				c2r[j] = r2c[i] = -1;
1358 		}
1359 
1360 	p = (n == size ? c2r : r2c);
1361 	for (i = 0; i < size; i++)
1362 		if (*p++ < 0) {
1363 			slot = ffs(~(mt->touches | mt->frame)) - 1;
1364 			if (slot < 0 || slot >= mt->num_slots)
1365 				break;
1366 			wsmouse_mtstate(sc, slot,
1367 			    pt[i].x, pt[i].y, pt[i].pressure);
1368 			pt[i].slot = slot;
1369 		}
1370 
1371 	p = (n == size ? r2c : c2r);
1372 	FOREACHBIT(touches, slot)
1373 		if ((i = *p++) >= 0) {
1374 			wsmouse_mtstate(sc, slot,
1375 			    pt[i].x, pt[i].y, pt[i].pressure);
1376 			pt[i].slot = slot;
1377 		} else {
1378 			wsmouse_mtstate(sc, slot, 0, 0, 0);
1379 		}
1380 }
1381 
1382 static inline void
1383 free_mt_slots(struct wsmouseinput *input)
1384 {
1385 	int n, size;
1386 
1387 	if ((n = input->mt.num_slots)) {
1388 		size = n * sizeof(struct mt_slot);
1389 		if (input->flags & MT_TRACKING)
1390 			size += MATRIX_SIZE(n);
1391 		input->mt.num_slots = 0;
1392 		free(input->mt.slots, M_DEVBUF, size);
1393 		input->mt.slots = NULL;
1394 		input->mt.matrix = NULL;
1395 	}
1396 }
1397 
1398 /* Allocate the MT slots and, if necessary, the buffers for MT tracking. */
1399 int
1400 wsmouse_mt_init(struct device *sc, int num_slots, int tracking)
1401 {
1402 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
1403 	int n, size;
1404 
1405 	if (num_slots == input->mt.num_slots
1406 	    && (!tracking == ((input->flags & MT_TRACKING) == 0)))
1407 		return (0);
1408 
1409 	free_mt_slots(input);
1410 
1411 	if (tracking)
1412 		input->flags |= MT_TRACKING;
1413 	else
1414 		input->flags &= ~MT_TRACKING;
1415 	n = imin(imax(num_slots, 0), WSMOUSE_MT_SLOTS_MAX);
1416 	if (n) {
1417 		size = n * sizeof(struct mt_slot);
1418 		if (input->flags & MT_TRACKING)
1419 			size += MATRIX_SIZE(n);
1420 		input->mt.slots = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
1421 		if (input->mt.slots != NULL) {
1422 			if (input->flags & MT_TRACKING)
1423 				input->mt.matrix = (int *)
1424 				    (input->mt.slots + n);
1425 			input->mt.num_slots = n;
1426 			return (0);
1427 		}
1428 	}
1429 	return (-1);
1430 }
1431 
1432 int
1433 wsmouse_get_params(struct device *sc,
1434     struct wsmouse_param *params, u_int nparams)
1435 {
1436 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
1437 	int i, key, error = 0;
1438 
1439 	for (i = 0; i < nparams; i++) {
1440 		key = params[i].key;
1441 		switch (key) {
1442 		case WSMOUSECFG_DX_SCALE:
1443 			params[i].value = input->filter.h.scale;
1444 			break;
1445 		case WSMOUSECFG_DY_SCALE:
1446 			params[i].value = input->filter.v.scale;
1447 			break;
1448 		case WSMOUSECFG_PRESSURE_LO:
1449 			params[i].value = input->filter.pressure_lo;
1450 			break;
1451 		case WSMOUSECFG_PRESSURE_HI:
1452 			params[i].value = input->filter.pressure_hi;
1453 			break;
1454 		case WSMOUSECFG_TRKMAXDIST:
1455 			params[i].value = input->filter.tracking_maxdist;
1456 			break;
1457 		case WSMOUSECFG_SWAPXY:
1458 			params[i].value = input->filter.swapxy;
1459 			break;
1460 		case WSMOUSECFG_X_INV:
1461 			params[i].value = input->filter.h.inv;
1462 			break;
1463 		case WSMOUSECFG_Y_INV:
1464 			params[i].value = input->filter.v.inv;
1465 			break;
1466 		case WSMOUSECFG_REVERSE_SCROLLING:
1467 			params[i].value = !!(input->flags & REVERSE_SCROLLING);
1468 			break;
1469 		case WSMOUSECFG_DX_MAX:
1470 			params[i].value = input->filter.h.dmax;
1471 			break;
1472 		case WSMOUSECFG_DY_MAX:
1473 			params[i].value = input->filter.v.dmax;
1474 			break;
1475 		case WSMOUSECFG_X_HYSTERESIS:
1476 			params[i].value = input->filter.h.hysteresis;
1477 			break;
1478 		case WSMOUSECFG_Y_HYSTERESIS:
1479 			params[i].value = input->filter.v.hysteresis;
1480 			break;
1481 		case WSMOUSECFG_DECELERATION:
1482 			params[i].value = input->filter.dclr;
1483 			break;
1484 		case WSMOUSECFG_STRONG_HYSTERESIS:
1485 			params[i].value = 0; /* The feature has been removed. */
1486 			break;
1487 		case WSMOUSECFG_SMOOTHING:
1488 			params[i].value =
1489 			    input->filter.mode & SMOOTHING_MASK;
1490 			break;
1491 		case WSMOUSECFG_LOG_INPUT:
1492 			params[i].value = !!(input->flags & LOG_INPUT);
1493 			break;
1494 		case WSMOUSECFG_LOG_EVENTS:
1495 			params[i].value = !!(input->flags & LOG_EVENTS);
1496 			break;
1497 		default:
1498 			error = wstpad_get_param(input, key, &params[i].value);
1499 			if (error != 0)
1500 				return (error);
1501 			break;
1502 		}
1503 	}
1504 
1505 	return (0);
1506 }
1507 
1508 int
1509 wsmouse_set_params(struct device *sc,
1510     const struct wsmouse_param *params, u_int nparams)
1511 {
1512 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
1513 	int i, val, key, needreset = 0, error = 0;
1514 
1515 	for (i = 0; i < nparams; i++) {
1516 		key = params[i].key;
1517 		val = params[i].value;
1518 		switch (params[i].key) {
1519 		case WSMOUSECFG_PRESSURE_LO:
1520 			input->filter.pressure_lo = val;
1521 			if (val > input->filter.pressure_hi)
1522 				input->filter.pressure_hi = val;
1523 			input->touch.min_pressure = input->filter.pressure_hi;
1524 			break;
1525 		case WSMOUSECFG_PRESSURE_HI:
1526 			input->filter.pressure_hi = val;
1527 			if (val < input->filter.pressure_lo)
1528 				input->filter.pressure_lo = val;
1529 			input->touch.min_pressure = val;
1530 			break;
1531 		case WSMOUSECFG_X_HYSTERESIS:
1532 			input->filter.h.hysteresis = val;
1533 			break;
1534 		case WSMOUSECFG_Y_HYSTERESIS:
1535 			input->filter.v.hysteresis = val;
1536 			break;
1537 		case WSMOUSECFG_DECELERATION:
1538 			input->filter.dclr = val;
1539 			wstpad_init_deceleration(input);
1540 			break;
1541 		case WSMOUSECFG_DX_SCALE:
1542 			input->filter.h.scale = val;
1543 			break;
1544 		case WSMOUSECFG_DY_SCALE:
1545 			input->filter.v.scale = val;
1546 			break;
1547 		case WSMOUSECFG_TRKMAXDIST:
1548 			input->filter.tracking_maxdist = val;
1549 			break;
1550 		case WSMOUSECFG_SWAPXY:
1551 			input->filter.swapxy = val;
1552 			break;
1553 		case WSMOUSECFG_X_INV:
1554 			input->filter.h.inv = val;
1555 			break;
1556 		case WSMOUSECFG_Y_INV:
1557 			input->filter.v.inv = val;
1558 			break;
1559 		case WSMOUSECFG_REVERSE_SCROLLING:
1560 			if (val)
1561 				input->flags |= REVERSE_SCROLLING;
1562 			else
1563 				input->flags &= ~REVERSE_SCROLLING;
1564 			break;
1565 		case WSMOUSECFG_DX_MAX:
1566 			input->filter.h.dmax = val;
1567 			break;
1568 		case WSMOUSECFG_DY_MAX:
1569 			input->filter.v.dmax = val;
1570 			break;
1571 		case WSMOUSECFG_SMOOTHING:
1572 			input->filter.mode &= ~SMOOTHING_MASK;
1573 			input->filter.mode |= (val & SMOOTHING_MASK);
1574 			break;
1575 		case WSMOUSECFG_LOG_INPUT:
1576 			if (val)
1577 				input->flags |= LOG_INPUT;
1578 			else
1579 				input->flags &= ~LOG_INPUT;
1580 			break;
1581 		case WSMOUSECFG_LOG_EVENTS:
1582 			if (val)
1583 				input->flags |= LOG_EVENTS;
1584 			else
1585 				input->flags &= ~LOG_EVENTS;
1586 			break;
1587 		default:
1588 			needreset = 1;
1589 			error = wstpad_set_param(input, key, val);
1590 			if (error != 0)
1591 				return (error);
1592 			break;
1593 		}
1594 	}
1595 
1596 	/* Reset soft-states if touchpad parameters changed */
1597 	if (needreset) {
1598 		wstpad_reset(input);
1599 		return (wstpad_configure(input));
1600 	}
1601 
1602 	return (0);
1603 }
1604 
1605 int
1606 wsmouse_set_mode(struct device *sc, int mode)
1607 {
1608 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
1609 
1610 	if (mode == WSMOUSE_COMPAT) {
1611 		input->flags &= ~TPAD_NATIVE_MODE;
1612 		input->flags |= TPAD_COMPAT_MODE;
1613 		return (0);
1614 	} else if (mode == WSMOUSE_NATIVE) {
1615 		input->flags &= ~TPAD_COMPAT_MODE;
1616 		input->flags |= TPAD_NATIVE_MODE;
1617 		return (0);
1618 	}
1619 	return (-1);
1620 }
1621 
1622 struct wsmousehw *
1623 wsmouse_get_hw(struct device *sc)
1624 {
1625 	return &((struct wsmouse_softc *) sc)->sc_input.hw;
1626 }
1627 
1628 /*
1629  * Create a default configuration based on the hardware infos in the 'hw'
1630  * fields. The 'params' argument is optional, hardware drivers can use it
1631  * to modify the generic defaults. Up to now this function is only useful
1632  * for touchpads.
1633  */
1634 int
1635 wsmouse_configure(struct device *sc,
1636     struct wsmouse_param *params, u_int nparams)
1637 {
1638 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input;
1639 	int error;
1640 
1641 	if (!(input->flags & CONFIGURED)) {
1642 		if (input->hw.x_max && input->hw.y_max) {
1643 			if (input->hw.flags & WSMOUSEHW_LR_DOWN) {
1644 				input->filter.v.inv =
1645 				    input->hw.y_max + input->hw.y_min;
1646 			}
1647 		}
1648 		input->filter.ratio = 1 << 12;
1649 		if (input->hw.h_res > 0 && input->hw.v_res > 0) {
1650 			input->filter.ratio *= input->hw.h_res;
1651 			input->filter.ratio /= input->hw.v_res;
1652 		}
1653 		if (wsmouse_mt_init(sc, input->hw.mt_slots,
1654 		    (input->hw.flags & WSMOUSEHW_MT_TRACKING))) {
1655 			printf("wsmouse_configure: "
1656 			    "MT initialization failed.\n");
1657 			return (-1);
1658 		}
1659 		if (IS_TOUCHPAD(input) && wstpad_configure(input)) {
1660 			printf("wstpad_configure: "
1661 			    "Initialization failed.\n");
1662 			return (-1);
1663 		}
1664 		input->flags |= CONFIGURED;
1665 		if (params != NULL) {
1666 			if ((error = wsmouse_set_params(sc, params, nparams)))
1667 				return (error);
1668 		}
1669 	}
1670 	if (IS_TOUCHPAD(input))
1671 		wsmouse_set_mode(sc, WSMOUSE_COMPAT);
1672 
1673 	return (0);
1674 }
1675 
1676 
1677 void
1678 wsmouse_input_reset(struct wsmouseinput *input)
1679 {
1680 	int num_slots, *matrix;
1681 	struct mt_slot *slots;
1682 
1683 	memset(&input->btn, 0, sizeof(struct btn_state));
1684 	memset(&input->motion, 0, sizeof(struct motion_state));
1685 	memset(&input->touch, 0, sizeof(struct touch_state));
1686 	input->touch.min_pressure = input->filter.pressure_hi;
1687 	if ((num_slots = input->mt.num_slots)) {
1688 		slots = input->mt.slots;
1689 		matrix = input->mt.matrix;
1690 		memset(&input->mt, 0, sizeof(struct mt_state));
1691 		memset(slots, 0, num_slots * sizeof(struct mt_slot));
1692 		input->mt.num_slots = num_slots;
1693 		input->mt.slots = slots;
1694 		input->mt.matrix = matrix;
1695 	}
1696 	if (input->tp != NULL)
1697 		wstpad_reset(input);
1698 }
1699 
1700 void
1701 wsmouse_input_cleanup(struct wsmouseinput *input)
1702 {
1703 	if (input->tp != NULL)
1704 		wstpad_cleanup(input);
1705 
1706 	free_mt_slots(input);
1707 }
1708