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