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