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