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