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