xref: /openbsd-src/sys/dev/wscons/wsmouse.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /* $OpenBSD: wsmouse.c,v 1.34 2016/08/18 21:12:35 bru 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/wsmouseinput.h>
113 #include <dev/wscons/wseventvar.h>
114 #include <dev/rndvar.h>
115 
116 #include "wsmux.h"
117 #include "wsdisplay.h"
118 #include "wskbd.h"
119 
120 #include <dev/wscons/wsmuxvar.h>
121 
122 #if defined(WSMUX_DEBUG) && NWSMUX > 0
123 #define	DPRINTF(x)	if (wsmuxdebug) printf x
124 #define	DPRINTFN(n,x)	if (wsmuxdebug > (n)) printf x
125 extern int wsmuxdebug;
126 #else
127 #define	DPRINTF(x)
128 #define	DPRINTFN(n,x)
129 #endif
130 
131 struct wsmouse_softc {
132 	struct wsevsrc	sc_base;
133 
134 	const struct wsmouse_accessops *sc_accessops;
135 	void		*sc_accesscookie;
136 
137 	struct wsmouseinput input;
138 
139 	int		sc_refcnt;
140 	u_char		sc_dying;	/* device is being detached */
141 };
142 
143 int	wsmouse_match(struct device *, void *, void *);
144 void	wsmouse_attach(struct device *, struct device *, void *);
145 int	wsmouse_detach(struct device *, int);
146 int	wsmouse_activate(struct device *, int);
147 
148 int	wsmouse_do_ioctl(struct wsmouse_softc *, u_long, caddr_t,
149 			      int, struct proc *);
150 
151 #if NWSMUX > 0
152 int	wsmouse_mux_open(struct wsevsrc *, struct wseventvar *);
153 int	wsmouse_mux_close(struct wsevsrc *);
154 #endif
155 
156 int	wsmousedoioctl(struct device *, u_long, caddr_t, int,
157 			    struct proc *);
158 int	wsmousedoopen(struct wsmouse_softc *, struct wseventvar *);
159 
160 struct cfdriver wsmouse_cd = {
161 	NULL, "wsmouse", DV_TTY
162 };
163 
164 struct cfattach wsmouse_ca = {
165 	sizeof (struct wsmouse_softc), wsmouse_match, wsmouse_attach,
166 	wsmouse_detach, wsmouse_activate
167 };
168 
169 #if NWSMUX > 0
170 struct wssrcops wsmouse_srcops = {
171 	WSMUX_MOUSE,
172 	wsmouse_mux_open, wsmouse_mux_close, wsmousedoioctl, NULL, 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 	wsmouse_input_init(&sc->input, &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(("wsmouse_detach:\n"));
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 && evar->io != NULL) {
265 		s = spltty();
266 		if (--sc->sc_refcnt >= 0) {
267 			/* Wake everyone by generating a dummy event. */
268 			if (++evar->put >= WSEVENT_QSIZE)
269 				evar->put = 0;
270 			WSEVENT_WAKEUP(evar);
271 			/* Wait for processes to go away. */
272 			if (tsleep(sc, PZERO, "wsmdet", hz * 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->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(("wsmouseopen: %s mux=%p p=%p\n", sc->sc_base.me_dv.dv_xname,
307 		 sc->sc_base.me_parent, p));
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(("wsmouseopen: detach\n"));
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 	wsevent_init(evar);
330 	evar->io = p->p_p;
331 
332 	error = wsmousedoopen(sc, evar);
333 	if (error) {
334 		DPRINTF(("wsmouseopen: %s open failed\n",
335 			 sc->sc_base.me_dv.dv_xname));
336 		sc->sc_base.me_evp = NULL;
337 		wsevent_fini(evar);
338 	}
339 	return (error);
340 }
341 
342 int
343 wsmouseclose(dev_t dev, int flags, int mode, struct proc *p)
344 {
345 	struct wsmouse_softc *sc =
346 	    (struct wsmouse_softc *)wsmouse_cd.cd_devs[minor(dev)];
347 	struct wseventvar *evar = sc->sc_base.me_evp;
348 
349 	if ((flags & (FREAD | FWRITE)) == FWRITE)
350 		return (0);			/* see wsmouseopen() */
351 
352 	if (evar == NULL)
353 		/* not open for read */
354 		return (0);
355 	sc->sc_base.me_evp = NULL;
356 	(*sc->sc_accessops->disable)(sc->sc_accesscookie);
357 	wsevent_fini(evar);
358 
359 #if NWSMUX > 0
360 	if (sc->sc_base.me_parent == NULL) {
361 		int mux, error;
362 
363 		DPRINTF(("wsmouseclose: attach\n"));
364 		mux = sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux;
365 		if (mux >= 0) {
366 			error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base);
367 			if (error)
368 				printf("%s: can't attach mux (error=%d)\n",
369 				    sc->sc_base.me_dv.dv_xname, error);
370 		}
371 	}
372 #endif
373 
374 	return (0);
375 }
376 
377 int
378 wsmousedoopen(struct wsmouse_softc *sc, struct wseventvar *evp)
379 {
380 	sc->sc_base.me_evp = evp;
381 
382 	wsmouse_input_reset(&sc->input);
383 
384 	/* enable the device, and punt if that's not possible */
385 	return (*sc->sc_accessops->enable)(sc->sc_accesscookie);
386 }
387 
388 int
389 wsmouseread(dev_t dev, struct uio *uio, int flags)
390 {
391 	struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
392 	int error;
393 
394 	if (sc->sc_dying)
395 		return (EIO);
396 
397 #ifdef DIAGNOSTIC
398 	if (sc->sc_base.me_evp == NULL) {
399 		printf("wsmouseread: evp == NULL\n");
400 		return (EINVAL);
401 	}
402 #endif
403 
404 	sc->sc_refcnt++;
405 	error = wsevent_read(sc->sc_base.me_evp, uio, flags);
406 	if (--sc->sc_refcnt < 0) {
407 		wakeup(sc);
408 		error = EIO;
409 	}
410 	return (error);
411 }
412 
413 int
414 wsmouseioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
415 {
416 	return (wsmousedoioctl(wsmouse_cd.cd_devs[minor(dev)],
417 	    cmd, data, flag, p));
418 }
419 
420 /* A wrapper around the ioctl() workhorse to make reference counting easy. */
421 int
422 wsmousedoioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
423     struct proc *p)
424 {
425 	struct wsmouse_softc *sc = (struct wsmouse_softc *)dv;
426 	int error;
427 
428 	sc->sc_refcnt++;
429 	error = wsmouse_do_ioctl(sc, cmd, data, flag, p);
430 	if (--sc->sc_refcnt < 0)
431 		wakeup(sc);
432 	return (error);
433 }
434 
435 int
436 wsmouse_do_ioctl(struct wsmouse_softc *sc, u_long cmd, caddr_t data, int flag,
437     struct proc *p)
438 {
439 	int error;
440 
441 	if (sc->sc_dying)
442 		return (EIO);
443 
444 	/*
445 	 * Try the generic ioctls that the wsmouse interface supports.
446 	 */
447 
448 	switch (cmd) {
449 	case FIOASYNC:
450 	case FIOSETOWN:
451 	case TIOCSPGRP:
452 		if ((flag & FWRITE) == 0)
453 			return (EACCES);
454 	}
455 
456 	switch (cmd) {
457 	case FIONBIO:		/* we will remove this someday (soon???) */
458 		return (0);
459 
460 	case FIOASYNC:
461 		if (sc->sc_base.me_evp == NULL)
462 			return (EINVAL);
463 		sc->sc_base.me_evp->async = *(int *)data != 0;
464 		return (0);
465 
466 	case FIOSETOWN:
467 		if (sc->sc_base.me_evp == NULL)
468 			return (EINVAL);
469 		if (-*(int *)data != sc->sc_base.me_evp->io->ps_pgid
470 		    && *(int *)data != sc->sc_base.me_evp->io->ps_pid)
471 			return (EPERM);
472 		return (0);
473 
474 	case TIOCSPGRP:
475 		if (sc->sc_base.me_evp == NULL)
476 			return (EINVAL);
477 		if (*(int *)data != sc->sc_base.me_evp->io->ps_pgid)
478 			return (EPERM);
479 		return (0);
480 	}
481 
482 	/*
483 	 * Try the mouse driver for WSMOUSEIO ioctls.  It returns -1
484 	 * if it didn't recognize the request.
485 	 */
486 	error = (*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd,
487 	    data, flag, p);
488 	return (error != -1 ? error : ENOTTY);
489 }
490 
491 int
492 wsmousepoll(dev_t dev, int events, struct proc *p)
493 {
494 	struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
495 
496 	if (sc->sc_base.me_evp == NULL)
497 		return (POLLERR);
498 	return (wsevent_poll(sc->sc_base.me_evp, events, p));
499 }
500 
501 int
502 wsmousekqfilter(dev_t dev, struct knote *kn)
503 {
504 	struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)];
505 
506 	if (sc->sc_base.me_evp == NULL)
507 		return (ENXIO);
508 	return (wsevent_kqfilter(sc->sc_base.me_evp, kn));
509 }
510 
511 #if NWSMUX > 0
512 int
513 wsmouse_mux_open(struct wsevsrc *me, struct wseventvar *evp)
514 {
515 	struct wsmouse_softc *sc = (struct wsmouse_softc *)me;
516 
517 	if (sc->sc_base.me_evp != NULL)
518 		return (EBUSY);
519 
520 	return wsmousedoopen(sc, evp);
521 }
522 
523 int
524 wsmouse_mux_close(struct wsevsrc *me)
525 {
526 	struct wsmouse_softc *sc = (struct wsmouse_softc *)me;
527 
528 	sc->sc_base.me_evp = NULL;
529 	(*sc->sc_accessops->disable)(sc->sc_accesscookie);
530 
531 	return (0);
532 }
533 
534 int
535 wsmouse_add_mux(int unit, struct wsmux_softc *muxsc)
536 {
537 	struct wsmouse_softc *sc;
538 
539 	if (unit < 0 || unit >= wsmouse_cd.cd_ndevs ||
540 	    (sc = wsmouse_cd.cd_devs[unit]) == NULL)
541 		return (ENXIO);
542 
543 	if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
544 		return (EBUSY);
545 
546 	return (wsmux_attach_sc(muxsc, &sc->sc_base));
547 }
548 #endif	/* NWSMUX > 0 */
549 
550 void
551 wsmouse_buttons(struct device *sc, u_int buttons)
552 {
553 	struct btn_state *btn =
554 	    &((struct wsmouse_softc *) sc)->input.btn;
555 
556 	if (btn->sync)
557 		/* Restore the old state. */
558 		btn->buttons ^= btn->sync;
559 
560 	btn->sync = btn->buttons ^ buttons;
561 	btn->buttons = buttons;
562 }
563 
564 void
565 wsmouse_motion(struct device *sc, int dx, int dy, int dz, int dw)
566 {
567 	struct motion_state *motion =
568 	    &((struct wsmouse_softc *) sc)->input.motion;
569 
570 	motion->dx = dx;
571 	motion->dy = dy;
572 	motion->dz = dz;
573 	motion->dw = dw;
574 	if (dx || dy || dz || dw)
575 		motion->sync |= SYNC_DELTAS;
576 }
577 
578 /*
579  * Handle absolute coordinates.
580  *
581  * x_delta/y_delta are used by touchpad code. The values are only
582  * valid if the SYNC-flags are set, and will be cleared by update- or
583  * conversion-functions if a touch shouldn't trigger pointer motion.
584  */
585 void
586 wsmouse_position(struct device *sc, int x, int y)
587 {
588 	struct motion_state *motion =
589 	    &((struct wsmouse_softc *) sc)->input.motion;
590 	int delta;
591 
592 	delta = x - motion->x;
593 	if (delta) {
594 		motion->x = x;
595 		motion->sync |= SYNC_X;
596 		motion->x_delta = delta;
597 	}
598 	delta = y - motion->y;
599 	if (delta) {
600 		motion->y = y;
601 		motion->sync |= SYNC_Y;
602 		motion->y_delta = delta;
603 	}
604 }
605 
606 static __inline int
607 normalized_pressure(struct wsmouseinput *input, int pressure)
608 {
609 	int limit = imax(input->touch.min_pressure, 1);
610 
611 	if (pressure >= limit)
612 		return pressure;
613 	else
614 		return (pressure < 0 ? limit : 0);
615 }
616 
617 void
618 wsmouse_touch(struct device *sc, int pressure, int contacts)
619 {
620 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
621 	struct touch_state *touch = &input->touch;
622 
623 	pressure = normalized_pressure(input, pressure);
624 	contacts = (pressure ? imax(contacts, 1) : 0);
625 
626 	if (pressure == 0 || pressure != touch->pressure) {
627 		/*
628 		 * pressure == 0: Drivers may report possibly arbitrary
629 		 * coordinates in this case; touch_update will correct them.
630 		 */
631 		touch->pressure = pressure;
632 		touch->sync |= SYNC_PRESSURE;
633 	}
634 	if (contacts != touch->contacts) {
635 		touch->contacts = contacts;
636 		touch->sync |= SYNC_CONTACTS;
637 	}
638 }
639 
640 void
641 wsmouse_mtstate(struct device *sc, int slot, int x, int y, int pressure)
642 {
643 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
644 	struct mt_state *mt = &input->mt;
645 	struct mt_slot *mts;
646 	u_int bit;
647 	int initial;
648 
649 	if (slot < 0 || slot >= mt->num_slots)
650 		return;
651 
652 	bit = (1 << slot);
653 	mt->frame |= bit;
654 
655 	/* Is this a new touch? */
656 	initial = ((mt->touches & bit) == (mt->sync[MTS_TOUCH] & bit));
657 
658 	mts = &mt->slots[slot];
659 	if (x != mts->x || initial) {
660 		mts->x = x;
661 		mt->sync[MTS_X] |= bit;
662 	}
663 	if (y != mts->y || initial) {
664 		mts->y = y;
665 		mt->sync[MTS_Y] |= bit;
666 	}
667 	pressure = normalized_pressure(input, pressure);
668 	if (pressure != mts->pressure || initial) {
669 		mts->pressure = pressure;
670 		mt->sync[MTS_PRESSURE] |= bit;
671 
672 		if (pressure) {
673 			if ((mt->touches & bit) == 0) {
674 				mt->num_touches++;
675 				mt->touches |= bit;
676 				mt->sync[MTS_TOUCH] |= bit;
677 			}
678 		} else if (mt->touches & bit) {
679 			mt->num_touches--;
680 			mt->touches ^= bit;
681 			mt->sync[MTS_TOUCH] |= bit;
682 		}
683 	}
684 }
685 
686 void
687 wsmouse_set(struct device *sc, enum wsmouseval type, int value, int aux)
688 {
689 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
690 	struct mt_slot *mts;
691 
692 	if (WSMOUSE_IS_MT_CODE(type)) {
693 		if (aux < 0 || aux >= input->mt.num_slots)
694 			return;
695 		mts = &input->mt.slots[aux];
696 	}
697 
698 	switch (type) {
699 	case WSMOUSE_REL_X:
700 		value += input->motion.x; /* fall through */
701 	case WSMOUSE_ABS_X:
702 		wsmouse_position(sc, value, input->motion.y);
703 		return;
704 	case WSMOUSE_REL_Y:
705 		value += input->motion.y;
706 	case WSMOUSE_ABS_Y:
707 		wsmouse_position(sc, input->motion.x, value);
708 		return;
709 	case WSMOUSE_PRESSURE:
710 		wsmouse_touch(sc, value, input->touch.contacts);
711 		return;
712 	case WSMOUSE_CONTACTS:
713 		/* Contact counts can be overridden by wsmouse_touch. */
714 		if (value != input->touch.contacts) {
715 			input->touch.contacts = value;
716 			input->touch.sync |= SYNC_CONTACTS;
717 		}
718 		return;
719 	case WSMOUSE_TOUCH_WIDTH:
720 		if (value != input->touch.width) {
721 			input->touch.width = value;
722 			input->touch.sync |= SYNC_TOUCH_WIDTH;
723 		}
724 		return;
725 	case WSMOUSE_MT_REL_X:
726 		value += mts->x; /* fall through */
727 	case WSMOUSE_MT_ABS_X:
728 		wsmouse_mtstate(sc, aux, value, mts->y, mts->pressure);
729 		return;
730 	case WSMOUSE_MT_REL_Y:
731 		value += mts->y;
732 	case WSMOUSE_MT_ABS_Y:
733 		wsmouse_mtstate(sc, aux, mts->x, value, mts->pressure);
734 		return;
735 	case WSMOUSE_MT_PRESSURE:
736 		wsmouse_mtstate(sc, aux, mts->x, mts->y, value);
737 		return;
738 	}
739 }
740 
741 /* Make touch and motion state consistent. */
742 void
743 wsmouse_touch_update(struct wsmouseinput *input)
744 {
745 	struct motion_state *motion = &input->motion;
746 	struct touch_state *touch = &input->touch;
747 
748 	if (touch->pressure == 0) {
749 		/* Restore valid coordinates. */
750 		if (motion->sync & SYNC_X)
751 			motion->x -= motion->x_delta;
752 		if (motion->sync & SYNC_Y)
753 			motion->y -= motion->y_delta;
754 		/* Don't generate motion/position events. */
755 		motion->sync &= ~SYNC_POSITION;
756 	}
757 	if (touch->sync & SYNC_CONTACTS)
758 		/* Suppress pointer motion. */
759 		motion->x_delta = motion->y_delta = 0;
760 
761 	if ((touch->sync & SYNC_PRESSURE) && touch->min_pressure) {
762 		if (touch->pressure >= input->params.pressure_hi)
763 			touch->min_pressure = input->params.pressure_lo;
764 		else if (touch->pressure < input->params.pressure_lo)
765 			touch->min_pressure = input->params.pressure_hi;
766 	}
767 }
768 
769 /* Normalize multitouch state. */
770 void
771 wsmouse_mt_update(struct wsmouseinput *input)
772 {
773 	int i;
774 
775 	/*
776 	 * The same as above: There may be arbitrary coordinates if
777 	 * (pressure == 0). Clear the sync flags for touches that have
778 	 * been released.
779 	 */
780 	if (input->mt.sync[MTS_TOUCH] & ~input->mt.touches) {
781 		for (i = MTS_X; i < MTS_SIZE; i++)
782 			input->mt.sync[i] &= input->mt.touches;
783 	}
784 }
785 
786 /*
787  * Select the pointer-controlling MT slot.
788  *
789  * Pointer-control is assigned to slots with non-zero motion deltas if
790  * at least one such slot exists. This function doesn't impose any
791  * restrictions on the way drivers use wsmouse_mtstate(), it covers
792  * partial, unordered, and "delta-filtered" input.
793  *
794  * The "cycle" is the set of slots with X/Y updates in previous sync
795  * operations; it will be cleared and rebuilt whenever a slot that is
796  * being updated is already a member. If a cycle ends that doesn't
797  * contain the pointer-controlling slot, a new slot will be selected.
798  */
799 void
800 wsmouse_ptr_ctrl(struct mt_state *mt)
801 {
802 	u_int updates;
803 	int select, slot;
804 
805 	mt->prev_ptr = mt->ptr;
806 
807 	if (mt->num_touches <= 1) {
808 		mt->ptr = mt->touches;
809 		mt->ptr_cycle = mt->ptr;
810 		return;
811 	}
812 
813 	/*
814 	 * If there is no pointer-controlling slot or it is inactive,
815 	 * select a new one.
816 	 */
817 	select = ((mt->ptr & mt->touches) == 0);
818 
819 	/* Remove slots without X/Y deltas from the cycle. */
820 	updates = (mt->sync[MTS_X] | mt->sync[MTS_Y]) & ~mt->sync[MTS_TOUCH];
821 	mt->ptr_cycle &= ~(mt->frame ^ updates);
822 
823 	if (mt->ptr_cycle & updates) {
824 		select |= ((mt->ptr_cycle & mt->ptr) == 0);
825 		mt->ptr_cycle = updates;
826 	} else {
827 		mt->ptr_cycle |= updates;
828 	}
829 	if (select) {
830 		slot = (mt->ptr_cycle
831 		    ? ffs(mt->ptr_cycle) - 1 : ffs(mt->touches) - 1);
832 		mt->ptr = (1 << slot);
833 	}
834 }
835 
836 /* Derive touch and motion state from MT state. */
837 void
838 wsmouse_mt_convert(struct device *sc)
839 {
840 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
841 	struct mt_state *mt = &input->mt;
842 	struct mt_slot *mts;
843 	int slot, pressure;
844 
845 	wsmouse_ptr_ctrl(mt);
846 
847 	if (mt->ptr) {
848 		slot = ffs(mt->ptr) - 1;
849 		mts = &mt->slots[slot];
850 		wsmouse_position(sc, mts->x, mts->y);
851 		if (mt->ptr != mt->prev_ptr)
852 			/* Suppress pointer motion. */
853 			input->motion.x_delta = input->motion.y_delta = 0;
854 		pressure = mts->pressure;
855 	} else {
856 		pressure = 0;
857 	}
858 
859 	wsmouse_touch(sc, pressure, mt->num_touches);
860 }
861 
862 void
863 wsmouse_evq_put(struct evq_access *evq, int ev_type, int ev_value)
864 {
865 	struct wscons_event *ev;
866 	int space;
867 
868 	space = evq->evar->get - evq->put;
869 	if (space != 1 && space != 1 - WSEVENT_QSIZE) {
870 		ev = &evq->evar->q[evq->put++];
871 		evq->put %= WSEVENT_QSIZE;
872 		ev->type = ev_type;
873 		ev->value = ev_value;
874 		memcpy(&ev->time, &evq->ts, sizeof(struct timespec));
875 		evq->result |= EVQ_RESULT_SUCCESS;
876 	} else {
877 		evq->result = EVQ_RESULT_OVERFLOW;
878 	}
879 }
880 
881 
882 void
883 wsmouse_btn_sync(struct wsmouseinput *input, struct evq_access *evq)
884 {
885 	struct btn_state *btn = &input->btn;
886 	int button, ev_type;
887 	u_int bit, sync;
888 
889 	for (sync = btn->sync; sync; sync ^= bit) {
890 		button = ffs(sync) - 1;
891 		bit = (1 << button);
892 		ev_type = (btn->buttons & bit) ? BTN_DOWN_EV : BTN_UP_EV;
893 		wsmouse_evq_put(evq, ev_type, button);
894 	}
895 }
896 
897 /*
898  * Scale with a [*.12] fixed-point factor and a remainder:
899  */
900 static __inline int
901 scale(int val, int factor, int *rmdr)
902 {
903 	val = val * factor + *rmdr;
904 	if (val >= 0) {
905 		*rmdr = val & 0xfff;
906 		return (val >> 12);
907 	} else {
908 		*rmdr = -(-val & 0xfff);
909 		return -(-val >> 12);
910 	}
911 }
912 
913 void
914 wsmouse_motion_sync(struct wsmouseinput *input, struct evq_access *evq)
915 {
916 	struct motion_state *motion = &input->motion;
917 	struct wsmouseparams *params = &input->params;
918 	struct axis_filter *fltr;
919 	int x, y, dx, dy;
920 
921 	if (motion->sync & SYNC_DELTAS) {
922 		dx = params->x_inv ? -motion->dx : motion->dx;
923 		dy = params->y_inv ? -motion->dy : motion->dy;
924 		if (input->flags & SCALE_DELTAS) {
925 			fltr = &input->fltr.h;
926 			dx = scale(dx, fltr->scale, &fltr->rmdr);
927 			fltr = &input->fltr.v;
928 			dy = scale(dy, fltr->scale, &fltr->rmdr);
929 		}
930 		if (dx)
931 			wsmouse_evq_put(evq, DELTA_X_EV(input->flags), dx);
932 		if (dy)
933 			wsmouse_evq_put(evq, DELTA_Y_EV(input->flags), dy);
934 		if (motion->dz)
935 			wsmouse_evq_put(evq, DELTA_Z_EV, motion->dz);
936 		if (motion->dw)
937 			wsmouse_evq_put(evq, DELTA_W_EV, motion->dw);
938 	}
939 	if (motion->sync & SYNC_POSITION) {
940 		if (motion->sync & SYNC_X) {
941 			x = (params->x_inv
942 			    ? params->x_inv - motion->x : motion->x);
943 			wsmouse_evq_put(evq, ABS_X_EV(input->flags), x);
944 		}
945 		if (motion->sync & SYNC_Y) {
946 			y = (params->y_inv
947 			    ? params->y_inv - motion->y : motion->y);
948 			wsmouse_evq_put(evq, ABS_Y_EV(input->flags), y);
949 		}
950 		if (motion->x_delta == 0 && motion->y_delta == 0
951 		    && (input->flags & TPAD_NATIVE_MODE))
952 			/* Suppress pointer motion. */
953 			wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_RESET, 0);
954 	}
955 }
956 
957 void
958 wsmouse_touch_sync(struct wsmouseinput *input, struct evq_access *evq)
959 {
960 	struct touch_state *touch = &input->touch;
961 
962 	if (touch->sync & SYNC_PRESSURE)
963 		wsmouse_evq_put(evq, ABS_Z_EV, touch->pressure);
964 	if (touch->sync & SYNC_CONTACTS)
965 		wsmouse_evq_put(evq, ABS_W_EV, touch->contacts);
966 	if ((touch->sync & SYNC_TOUCH_WIDTH)
967 	    && (input->flags & TPAD_NATIVE_MODE))
968 		wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_WIDTH, touch->width);
969 }
970 
971 /*
972  * Convert absolute touchpad input (compatibility mode).
973  */
974 void
975 wsmouse_compat_convert(struct device *sc, struct evq_access *evq)
976 {
977 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
978 	struct wsmouseparams *params = &input->params;
979 	int dx, dy, dz, dw;
980 
981 	dx = (input->motion.sync & SYNC_X) ? input->motion.x_delta : 0;
982 	dy = (input->motion.sync & SYNC_Y) ? input->motion.y_delta : 0;
983 	dz = (input->motion.sync & SYNC_DELTAS) ? input->motion.dz : 0;
984 	dw = (input->motion.sync & SYNC_DELTAS) ? input->motion.dw : 0;
985 
986 	if ((params->dx_max && abs(dx) > params->dx_max)
987 	    || (params->dy_max && abs(dy) > params->dy_max)) {
988 
989 		dx = dy = 0;
990 	}
991 
992 	wsmouse_motion(sc, dx, dy, dz, dw);
993 
994 	input->motion.sync &= ~SYNC_POSITION;
995 	input->touch.sync = 0;
996 }
997 
998 static __inline void
999 clear_sync_flags(struct wsmouseinput *input)
1000 {
1001 	int i;
1002 
1003 	input->btn.sync = 0;
1004 	input->motion.sync = 0;
1005 	input->touch.sync = 0;
1006 	if (input->mt.frame) {
1007 		input->mt.frame = 0;
1008 		for (i = 0; i < MTS_SIZE; i++)
1009 			input->mt.sync[i] = 0;
1010 	}
1011 }
1012 
1013 void
1014 wsmouse_input_sync(struct device *sc)
1015 {
1016 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
1017 	struct evq_access evq;
1018 
1019 	evq.evar = *input->evar;
1020 	if (evq.evar == NULL)
1021 		return;
1022 	evq.put = evq.evar->put;
1023 	evq.result = EVQ_RESULT_NONE;
1024 	getnanotime(&evq.ts);
1025 
1026 	add_mouse_randomness(input->btn.buttons
1027 	    ^ input->motion.dx ^ input->motion.dy
1028 	    ^ input->motion.x ^ input->motion.y
1029 	    ^ input->motion.dz ^ input->motion.dw);
1030 
1031 	if (input->mt.frame) {
1032 		wsmouse_mt_update(input);
1033 		wsmouse_mt_convert(sc);
1034 	}
1035 	if (input->touch.sync)
1036 		wsmouse_touch_update(input);
1037 
1038 	if (input->flags & TPAD_COMPAT_MODE)
1039 		wsmouse_compat_convert(sc, &evq);
1040 
1041 	if (input->flags & RESYNC) {
1042 		input->flags &= ~RESYNC;
1043 		input->motion.sync &= SYNC_POSITION;
1044 		input->motion.x_delta = input->motion.y_delta = 0;
1045 	}
1046 
1047 	if (input->btn.sync)
1048 		wsmouse_btn_sync(input, &evq);
1049 	if (input->motion.sync)
1050 		wsmouse_motion_sync(input, &evq);
1051 	if (input->touch.sync)
1052 		wsmouse_touch_sync(input, &evq);
1053 	/* No MT events are generated yet. */
1054 
1055 	if (evq.result == EVQ_RESULT_SUCCESS) {
1056 		wsmouse_evq_put(&evq, WSCONS_EVENT_SYNC, 0);
1057 		if (evq.result == EVQ_RESULT_SUCCESS) {
1058 			evq.evar->put = evq.put;
1059 			WSEVENT_WAKEUP(evq.evar);
1060 		}
1061 	}
1062 
1063 	if (evq.result != EVQ_RESULT_OVERFLOW)
1064 		clear_sync_flags(input);
1065 	else
1066 		input->flags |= RESYNC;
1067 }
1068 
1069 int
1070 wsmouse_id_to_slot(struct device *sc, int id)
1071 {
1072 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
1073 	struct mt_state *mt = &input->mt;
1074 	int slot;
1075 
1076 	if (mt->num_slots == 0)
1077 		return (-1);
1078 
1079 	FOREACHBIT(mt->touches, slot) {
1080 		if (mt->slots[slot].id == id)
1081 			return slot;
1082 	}
1083 	slot = ffs(~(mt->touches | mt->frame)) - 1;
1084 	if (slot >= 0 && slot < mt->num_slots) {
1085 		mt->frame |= 1 << slot;
1086 		mt->slots[slot].id = id;
1087 		return (slot);
1088 	} else {
1089 		return (-1);
1090 	}
1091 }
1092 
1093 /*
1094  * Find a minimum-weight matching for an m-by-n matrix.
1095  *
1096  * m must be greater than or equal to n. The size of the buffer must be
1097  * at least 4m + 3n.
1098  *
1099  * On return, the first m elements of the buffer contain the row-to-
1100  * column mappings, i.e., buffer[i] is the column index for row i, or -1
1101  * if there is no assignment for that row (which may happen if n < m).
1102  *
1103  * Wrong results because of overflows will not occur with input values
1104  * in the range of 0 to INT_MAX / 2 inclusive.
1105  *
1106  * The function applies the Dinic-Kronrod algorithm. It is not modern or
1107  * popular, but it seems to be a good choice for small matrices at least.
1108  * The original form of the algorithm is modified as follows: There is no
1109  * initial search for row minima, the initial assignments are in a
1110  * "virtual" column with the index -1 and zero values. This permits inputs
1111  * with n < m, and it simplifies the reassignments.
1112  */
1113 void
1114 wsmouse_matching(int *matrix, int m, int n, int *buffer)
1115 {
1116 	int i, j, k, d, e, row, col, delta;
1117 	int *p;
1118 	int *r2c = buffer;	/* row-to-column assignments */
1119 	int *red = r2c + m;	/* reduced values of the assignments */
1120 	int *alt = red + m;	/* alternative assignments */
1121 	int *mc = alt + m;	/* row-wise minimal elements of cs */
1122 	int *cs = mc + m;	/* the column set */
1123 	int *c2r = cs + n;	/* column-to-row assignments in cs */
1124 	int *cd = c2r + n;	/* column deltas (reduction) */
1125 
1126 	for (p = r2c; p < red; *p++ = -1) {}
1127 	for (; p < alt; *p++ = 0) {}
1128 	for (col = 0; col < n; col++) {
1129 		delta = INT_MAX;
1130 		for (i = 0, p = matrix + col; i < m; i++, p += n) {
1131 			d = *p - red[i];
1132 			if (d < delta || (d == delta && r2c[i] < 0)) {
1133 				delta = d;
1134 				row = i;
1135 			}
1136 		}
1137 		cd[col] = delta;
1138 		if (r2c[row] < 0) {
1139 			r2c[row] = col;
1140 			continue;
1141 		}
1142 		for (p = alt; p < mc; *p++ = -1) {}
1143 		for (; p < cs; *p++ = col) {}
1144 		for (k = 0; (j = r2c[row]) >= 0;) {
1145 			cs[k++] = j;
1146 			c2r[j] = row;
1147 			alt[row] = mc[row];
1148 			delta = INT_MAX;
1149 			for (i = 0, p = matrix; i < m; i++, p += n)
1150 				if (alt[i] < 0) {
1151 					d = p[mc[i]] - cd[mc[i]];
1152 					e = p[j] - cd[j];
1153 					if (e < d) {
1154 						d = e;
1155 						mc[i] = j;
1156 					}
1157 					d -= red[i];
1158 					if (d < delta || (d == delta
1159 					    && r2c[i] < 0)) {
1160 						delta = d;
1161 						row = i;
1162 					}
1163 				}
1164 			cd[col] += delta;
1165 			for (i = 0; i < k; i++) {
1166 				cd[cs[i]] += delta;
1167 				red[c2r[cs[i]]] -= delta;
1168 			}
1169 		}
1170 		for (j = mc[row]; (r2c[row] = j) != col;) {
1171 			row = c2r[j];
1172 			j = alt[row];
1173 		}
1174 	}
1175 }
1176 
1177 void
1178 wsmouse_mtframe(struct device *sc, struct mtpoint *pt, int size)
1179 {
1180 	struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->input;
1181 	struct mt_state *mt = &input->mt;
1182 	int i, j, m, n, dx, dy, slot, maxdist;
1183 	int *p, *r2c, *c2r;
1184 	u_int touches;
1185 
1186 	if (mt->num_slots == 0 || mt->matrix == NULL)
1187 		return;
1188 
1189 	size = imax(0, imin(size, mt->num_slots));
1190 	p = mt->matrix;
1191 	touches = mt->touches;
1192 	if (mt->num_touches >= size) {
1193 		FOREACHBIT(touches, slot)
1194 			for (i = 0; i < size; i++) {
1195 				dx = pt[i].x - mt->slots[slot].x;
1196 				dy = pt[i].y - mt->slots[slot].y;
1197 				*p++ = dx * dx + dy * dy;
1198 			}
1199 		m = mt->num_touches;
1200 		n = size;
1201 	} else {
1202 		for (i = 0; i < size; i++)
1203 			FOREACHBIT(touches, slot) {
1204 				dx = pt[i].x - mt->slots[slot].x;
1205 				dy = pt[i].y - mt->slots[slot].y;
1206 				*p++ = dx * dx + dy * dy;
1207 			}
1208 		m = size;
1209 		n = mt->num_touches;
1210 	}
1211 	wsmouse_matching(mt->matrix, m, n, p);
1212 
1213 	r2c = p;
1214 	c2r = p + m;
1215 	maxdist = input->params.tracking_maxdist;
1216 	maxdist = (maxdist ? maxdist * maxdist : INT_MAX);
1217 	for (i = 0, p = mt->matrix; i < m; i++, p += n)
1218 		if ((j = r2c[i]) >= 0) {
1219 			if (p[j] <= maxdist)
1220 				c2r[j] = i;
1221 			else
1222 				c2r[j] = r2c[i] = -1;
1223 		}
1224 
1225 	p = (n == size ? c2r : r2c);
1226 	for (i = 0; i < size; i++)
1227 		if (*p++ < 0) {
1228 			slot = ffs(~(mt->touches | mt->frame)) - 1;
1229 			if (slot < 0 || slot >= mt->num_slots)
1230 				break;
1231 			wsmouse_mtstate(sc, slot,
1232 			    pt[i].x, pt[i].y, pt[i].pressure);
1233 			pt[i].slot = slot;
1234 		}
1235 
1236 	p = (n == size ? r2c : c2r);
1237 	FOREACHBIT(touches, slot)
1238 		if ((i = *p++) >= 0) {
1239 			wsmouse_mtstate(sc, slot,
1240 			    pt[i].x, pt[i].y, pt[i].pressure);
1241 			pt[i].slot = slot;
1242 		} else {
1243 			wsmouse_mtstate(sc, slot, 0, 0, 0);
1244 		}
1245 }
1246 
1247 static __inline void
1248 free_mt_slots(struct wsmouseinput *input)
1249 {
1250 	int n, size;
1251 
1252 	if ((n = input->mt.num_slots)) {
1253 		size = n * sizeof(struct mt_slot);
1254 		if (input->flags & MT_TRACKING)
1255 			size += MATRIX_SIZE(n);
1256 		input->mt.num_slots = 0;
1257 		free(input->mt.slots, M_DEVBUF, size);
1258 		input->mt.slots = NULL;
1259 		input->mt.matrix = NULL;
1260 	}
1261 }
1262 
1263 /* Allocate the MT slots and, if necessary, the buffers for MT tracking. */
1264 int
1265 wsmouse_mt_init(struct device *sc, int num_slots, int tracking)
1266 {
1267 	struct wsmouseinput *input =
1268 	    &((struct wsmouse_softc *) sc)->input;
1269 	int n, size;
1270 
1271 	if (num_slots == input->mt.num_slots
1272 	    && (!tracking == ((input->flags & MT_TRACKING) == 0)))
1273 		return (0);
1274 
1275 	free_mt_slots(input);
1276 
1277 	if (tracking)
1278 		input->flags |= MT_TRACKING;
1279 	else
1280 		input->flags &= ~MT_TRACKING;
1281 	n = imin(imax(num_slots, 0), WSMOUSE_MT_SLOTS_MAX);
1282 	if (n) {
1283 		size = n * sizeof(struct mt_slot);
1284 		if (input->flags & MT_TRACKING)
1285 			size += MATRIX_SIZE(n);
1286 		input->mt.slots = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
1287 		if (input->mt.slots != NULL) {
1288 			if (input->flags & MT_TRACKING)
1289 				input->mt.matrix = (int *)
1290 				    (input->mt.slots + n);
1291 			input->mt.num_slots = n;
1292 			return (0);
1293 		}
1294 	}
1295 	return (-1);
1296 }
1297 
1298 void
1299 wsmouse_init_scaling(struct wsmouseinput *input)
1300 {
1301 	struct wsmouseparams *params = &input->params;
1302 	int m, n;
1303 
1304 	if (params->dx_mul || params->dx_div
1305 	    || params->dy_mul || params->dy_div) {
1306 		/* Scale factors have a [*.12] fixed point format. */
1307 		m = (params->dx_mul ? abs(params->dx_mul) : 1);
1308 		n = (params->dx_div ? abs(params->dx_div) : 1);
1309 		input->fltr.h.scale = (m << 12) / n;
1310 		input->fltr.h.rmdr = 0;
1311 		m = (params->dy_mul ? abs(params->dy_mul) : 1);
1312 		n = (params->dy_div ? abs(params->dy_div): 1);
1313 		input->fltr.v.scale = (m << 12) / n;
1314 		input->fltr.v.rmdr = 0;
1315 		input->flags |= SCALE_DELTAS;
1316 	} else {
1317 		input->flags &= ~SCALE_DELTAS;
1318 	}
1319 }
1320 
1321 void
1322 wsmouse_set_param(struct device *sc, size_t param, int value)
1323 {
1324 	struct wsmouseinput *input =
1325 	    &((struct wsmouse_softc *) sc)->input;
1326 	struct wsmouseparams *params = &input->params;
1327 	int *p;
1328 
1329 	if (param > WSMPARAM_LASTFIELD) {
1330 		printf("wsmouse_set_param: invalid parameter type\n");
1331 		return;
1332 	}
1333 
1334 	p = (int *) (((void *) params) + param);
1335 	*p = value;
1336 
1337 	if (IS_WSMFLTR_PARAM(param)) {
1338 		wsmouse_init_scaling(input);
1339 	} else if (param == WSMPARAM_SWAPXY) {
1340 		if (value)
1341 			input->flags |= SWAPXY;
1342 		else
1343 			input->flags &= ~SWAPXY;
1344 	} else if (param == WSMPARAM_PRESSURE_LO) {
1345 		params->pressure_hi =
1346 		    imax(params->pressure_lo, params->pressure_hi);
1347 		input->touch.min_pressure = params->pressure_hi;
1348 	} else if (param == WSMPARAM_PRESSURE_HI
1349 	    && params->pressure_lo == 0) {
1350 		params->pressure_lo = params->pressure_hi;
1351 		input->touch.min_pressure = params->pressure_hi;
1352 	}
1353 }
1354 
1355 int
1356 wsmouse_set_mode(struct device *sc, int mode)
1357 {
1358 	struct wsmouseinput *input =
1359 	    &((struct wsmouse_softc *) sc)->input;
1360 
1361 	if (mode == WSMOUSE_COMPAT) {
1362 		input->flags &= ~TPAD_NATIVE_MODE;
1363 		input->flags |= TPAD_COMPAT_MODE;
1364 		return (0);
1365 	} else if (mode == WSMOUSE_NATIVE) {
1366 		input->flags &= ~TPAD_COMPAT_MODE;
1367 		input->flags |= TPAD_NATIVE_MODE;
1368 		return (0);
1369 	}
1370 	return (-1);
1371 }
1372 
1373 void
1374 wsmouse_input_reset(struct wsmouseinput *input)
1375 {
1376 	int num_slots, *matrix;
1377 	struct mt_slot *slots;
1378 
1379 	memset(&input->btn, 0, sizeof(struct btn_state));
1380 	memset(&input->motion, 0, sizeof(struct motion_state));
1381 	memset(&input->touch, 0, sizeof(struct touch_state));
1382 	input->touch.min_pressure = input->params.pressure_hi;
1383 	if ((num_slots = input->mt.num_slots)) {
1384 		slots = input->mt.slots;
1385 		matrix = input->mt.matrix;
1386 		memset(&input->mt, 0, sizeof(struct mt_state));
1387 		memset(slots, 0, num_slots * sizeof(struct mt_slot));
1388 		input->mt.num_slots = num_slots;
1389 		input->mt.slots = slots;
1390 		input->mt.matrix = matrix;
1391 	}
1392 }
1393 
1394 void
1395 wsmouse_input_init(struct wsmouseinput *input, struct wseventvar **evar)
1396 {
1397 	input->evar = evar;
1398 }
1399 
1400 void
1401 wsmouse_input_cleanup(struct wsmouseinput *input)
1402 {
1403 	free_mt_slots(input);
1404 }
1405