1 /* $OpenBSD: wsmouse.c,v 1.73 2024/06/26 01:40:49 jsg Exp $ */ 2 /* $NetBSD: wsmouse.c,v 1.35 2005/02/27 00:27:52 perry Exp $ */ 3 4 /* 5 * Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Christopher G. Demetriou 18 * for the NetBSD Project. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * Copyright (c) 1992, 1993 36 * The Regents of the University of California. All rights reserved. 37 * 38 * This software was developed by the Computer Systems Engineering group 39 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 40 * contributed to Berkeley. 41 * 42 * All advertising materials mentioning features or use of this software 43 * must display the following acknowledgement: 44 * This product includes software developed by the University of 45 * California, Lawrence Berkeley Laboratory. 46 * 47 * Redistribution and use in source and binary forms, with or without 48 * modification, are permitted provided that the following conditions 49 * are met: 50 * 1. Redistributions of source code must retain the above copyright 51 * notice, this list of conditions and the following disclaimer. 52 * 2. Redistributions in binary form must reproduce the above copyright 53 * notice, this list of conditions and the following disclaimer in the 54 * documentation and/or other materials provided with the distribution. 55 * 3. Neither the name of the University nor the names of its contributors 56 * may be used to endorse or promote products derived from this software 57 * without specific prior written permission. 58 * 59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69 * SUCH DAMAGE. 70 * 71 * @(#)ms.c 8.1 (Berkeley) 6/11/93 72 */ 73 74 /* 75 * Copyright (c) 2015, 2016 Ulf Brosziewski 76 * 77 * Permission to use, copy, modify, and distribute this software for any 78 * purpose with or without fee is hereby granted, provided that the above 79 * copyright notice and this permission notice appear in all copies. 80 * 81 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 82 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 83 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 84 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 85 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 86 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 87 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 88 */ 89 90 /* 91 * Mouse driver. 92 */ 93 94 #include <sys/param.h> 95 #include <sys/conf.h> 96 #include <sys/ioctl.h> 97 #include <sys/fcntl.h> 98 #include <sys/kernel.h> 99 #include <sys/proc.h> 100 #include <sys/syslog.h> 101 #include <sys/systm.h> 102 #include <sys/tty.h> 103 #include <sys/signalvar.h> 104 #include <sys/device.h> 105 #include <sys/vnode.h> 106 #include <sys/malloc.h> 107 108 #include <dev/wscons/wscons_features.h> 109 #include <dev/wscons/wsconsio.h> 110 #include <dev/wscons/wsmousevar.h> 111 #include <dev/wscons/wseventvar.h> 112 #include <dev/wscons/wsmouseinput.h> 113 114 #include "wsmux.h" 115 #include "wsdisplay.h" 116 #include "wskbd.h" 117 118 #include <dev/wscons/wsmuxvar.h> 119 120 #if defined(WSMUX_DEBUG) && NWSMUX > 0 121 #define DPRINTF(x) if (wsmuxdebug) printf x 122 extern int wsmuxdebug; 123 #else 124 #define DPRINTF(x) 125 #endif 126 127 struct wsmouse_softc { 128 struct wsevsrc sc_base; 129 130 const struct wsmouse_accessops *sc_accessops; 131 void *sc_accesscookie; 132 133 struct wsmouseinput sc_input; 134 135 int sc_refcnt; 136 u_char sc_dying; /* device is being detached */ 137 }; 138 139 int wsmouse_match(struct device *, void *, void *); 140 void wsmouse_attach(struct device *, struct device *, void *); 141 int wsmouse_detach(struct device *, int); 142 int wsmouse_activate(struct device *, int); 143 144 int wsmouse_do_ioctl(struct wsmouse_softc *, u_long, caddr_t, 145 int, struct proc *); 146 147 #if NWSMUX > 0 148 int wsmouse_mux_open(struct wsevsrc *, struct wseventvar *); 149 int wsmouse_mux_close(struct wsevsrc *); 150 #endif 151 152 int wsmousedoioctl(struct device *, u_long, caddr_t, int, 153 struct proc *); 154 int wsmousedoopen(struct wsmouse_softc *, struct wseventvar *); 155 156 struct cfdriver wsmouse_cd = { 157 NULL, "wsmouse", DV_TTY 158 }; 159 160 const struct cfattach wsmouse_ca = { 161 sizeof (struct wsmouse_softc), wsmouse_match, wsmouse_attach, 162 wsmouse_detach, wsmouse_activate 163 }; 164 165 #if NWSMUX > 0 166 struct wssrcops wsmouse_srcops = { 167 .type = WSMUX_MOUSE, 168 .dopen = wsmouse_mux_open, 169 .dclose = wsmouse_mux_close, 170 .dioctl = wsmousedoioctl, 171 .ddispioctl = NULL, 172 .dsetdisplay = NULL, 173 }; 174 #endif 175 176 /* 177 * Print function (for parent devices). 178 */ 179 int 180 wsmousedevprint(void *aux, const char *pnp) 181 { 182 183 if (pnp) 184 printf("wsmouse at %s", pnp); 185 return (UNCONF); 186 } 187 188 int 189 wsmouse_match(struct device *parent, void *match, void *aux) 190 { 191 return (1); 192 } 193 194 void 195 wsmouse_attach(struct device *parent, struct device *self, void *aux) 196 { 197 struct wsmouse_softc *sc = (struct wsmouse_softc *)self; 198 struct wsmousedev_attach_args *ap = aux; 199 #if NWSMUX > 0 200 int mux, error; 201 #endif 202 203 sc->sc_accessops = ap->accessops; 204 sc->sc_accesscookie = ap->accesscookie; 205 206 sc->sc_input.evar = &sc->sc_base.me_evp; 207 208 #if NWSMUX > 0 209 sc->sc_base.me_ops = &wsmouse_srcops; 210 mux = sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux; 211 if (mux >= 0) { 212 error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base); 213 if (error) 214 printf(" attach error=%d", error); 215 else 216 printf(" mux %d", mux); 217 } 218 #else 219 #if 0 /* not worth keeping, especially since the default value is not -1... */ 220 if (sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux >= 0) 221 printf(" (mux ignored)"); 222 #endif 223 #endif /* NWSMUX > 0 */ 224 225 printf("\n"); 226 } 227 228 int 229 wsmouse_activate(struct device *self, int act) 230 { 231 struct wsmouse_softc *sc = (struct wsmouse_softc *)self; 232 233 if (act == DVACT_DEACTIVATE) 234 sc->sc_dying = 1; 235 return (0); 236 } 237 238 /* 239 * Detach a mouse. To keep track of users of the softc we keep 240 * a reference count that's incremented while inside, e.g., read. 241 * If the mouse is active and the reference count is > 0 (0 is the 242 * normal state) we post an event and then wait for the process 243 * that had the reference to wake us up again. Then we blow away the 244 * vnode and return (which will deallocate the softc). 245 */ 246 int 247 wsmouse_detach(struct device *self, int flags) 248 { 249 struct wsmouse_softc *sc = (struct wsmouse_softc *)self; 250 struct wseventvar *evar; 251 int maj, mn; 252 int s; 253 254 #if NWSMUX > 0 255 /* Tell parent mux we're leaving. */ 256 if (sc->sc_base.me_parent != NULL) { 257 DPRINTF(("%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) { 265 s = spltty(); 266 if (--sc->sc_refcnt >= 0) { 267 /* Wake everyone by generating a dummy event. */ 268 if (++evar->ws_put >= WSEVENT_QSIZE) 269 evar->ws_put = 0; 270 WSEVENT_WAKEUP(evar); 271 /* Wait for processes to go away. */ 272 if (tsleep_nsec(sc, PZERO, "wsmdet", SEC_TO_NSEC(60))) 273 printf("wsmouse_detach: %s didn't detach\n", 274 sc->sc_base.me_dv.dv_xname); 275 } 276 splx(s); 277 } 278 279 /* locate the major number */ 280 for (maj = 0; maj < nchrdev; maj++) 281 if (cdevsw[maj].d_open == wsmouseopen) 282 break; 283 284 /* Nuke the vnodes for any open instances (calls close). */ 285 mn = self->dv_unit; 286 vdevgone(maj, mn, mn, VCHR); 287 288 wsmouse_input_cleanup(&sc->sc_input); 289 290 return (0); 291 } 292 293 int 294 wsmouseopen(dev_t dev, int flags, int mode, struct proc *p) 295 { 296 struct wsmouse_softc *sc; 297 struct wseventvar *evar; 298 int error, unit; 299 300 unit = minor(dev); 301 if (unit >= wsmouse_cd.cd_ndevs || /* make sure it was attached */ 302 (sc = wsmouse_cd.cd_devs[unit]) == NULL) 303 return (ENXIO); 304 305 #if NWSMUX > 0 306 DPRINTF(("%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 (EIO); 312 313 if ((flags & (FREAD | FWRITE)) == FWRITE) 314 return (0); /* always allow open for write 315 so ioctl() is possible. */ 316 317 #if NWSMUX > 0 318 if (sc->sc_base.me_parent != NULL) { 319 /* Grab the mouse out of the greedy hands of the mux. */ 320 DPRINTF(("%s: detach\n", __func__)); 321 wsmux_detach_sc(&sc->sc_base); 322 } 323 #endif 324 325 if (sc->sc_base.me_evp != NULL) 326 return (EBUSY); 327 328 evar = &sc->sc_base.me_evar; 329 if (wsevent_init(evar)) 330 return (EBUSY); 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)]; 343 struct wseventvar *evar = sc->sc_base.me_evp; 344 345 if ((flags & (FREAD | FWRITE)) == FWRITE) 346 /* Not open for read */ 347 return (0); 348 349 sc->sc_base.me_evp = NULL; 350 (*sc->sc_accessops->disable)(sc->sc_accesscookie); 351 wsevent_fini(evar); 352 353 #if NWSMUX > 0 354 if (sc->sc_base.me_parent == NULL) { 355 int mux, error; 356 357 DPRINTF(("%s: attach\n", __func__)); 358 mux = sc->sc_base.me_dv.dv_cfdata->wsmousedevcf_mux; 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) 378 return (EBUSY); 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; 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)]; 394 int error; 395 396 if (sc->sc_dying) 397 return (EIO); 398 399 #ifdef DIAGNOSTIC 400 if (sc->sc_base.me_evp == NULL) { 401 printf("wsmouseread: evp == NULL\n"); 402 return (EINVAL); 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 = EIO; 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)], 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 || nparams > WSMOUSECFG_MAX) 445 return (EINVAL); 446 447 size = nparams * sizeof(struct wsmouse_param); 448 buf = malloc(size, M_DEVBUF, M_WAITOK); 449 if (buf == NULL) 450 return (ENOMEM); 451 452 if ((error = copyin(params, buf, size))) { 453 free(buf, M_DEVBUF, size); 454 return (error); 455 } 456 457 s = spltty(); 458 if (cmd == WSMOUSEIO_SETPARAMS) { 459 if (wsmouse_set_params((struct device *) sc, buf, nparams)) 460 error = EINVAL; 461 } else { 462 if (wsmouse_get_params((struct device *) sc, buf, nparams)) 463 error = EINVAL; 464 else 465 error = copyout(buf, params, size); 466 } 467 splx(s); 468 free(buf, M_DEVBUF, 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 (EIO); 481 482 /* 483 * Try the generic ioctls that the wsmouse interface supports. 484 */ 485 486 switch (cmd) { 487 case FIOASYNC: 488 case FIOSETOWN: 489 case TIOCSPGRP: 490 if ((flag & FWRITE) == 0) 491 return (EACCES); 492 } 493 494 switch (cmd) { 495 case FIONBIO: /* we will remove this someday (soon???) */ 496 return (0); 497 498 case FIOASYNC: 499 if (sc->sc_base.me_evp == NULL) 500 return (EINVAL); 501 sc->sc_base.me_evp->ws_async = *(int *)data != 0; 502 return (0); 503 504 case FIOGETOWN: 505 case TIOCGPGRP: 506 evar = sc->sc_base.me_evp; 507 if (evar == NULL) 508 return (EINVAL); 509 sigio_getown(&evar->ws_sigio, cmd, data); 510 return (0); 511 512 case FIOSETOWN: 513 case TIOCSPGRP: 514 evar = sc->sc_base.me_evp; 515 if (evar == NULL) 516 return (EINVAL); 517 return (sigio_setown(&evar->ws_sigio, cmd, data)); 518 519 case WSMOUSEIO_GETPARAMS: 520 case WSMOUSEIO_SETPARAMS: 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 : ENOTTY); 533 } 534 535 int 536 wsmousekqfilter(dev_t dev, struct knote *kn) 537 { 538 struct wsmouse_softc *sc = wsmouse_cd.cd_devs[minor(dev)]; 539 540 if (sc->sc_base.me_evp == NULL) 541 return (ENXIO); 542 return (wsevent_kqfilter(sc->sc_base.me_evp, kn)); 543 } 544 545 #if NWSMUX > 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; 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) 572 return (ENXIO); 573 574 if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL) 575 return (EBUSY); 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; 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); 660 set_y(&motion->pos, y, &motion->sync, SYNC_Y); 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; 690 } 691 if (contacts != touch->contacts) { 692 touch->contacts = contacts; 693 touch->sync |= SYNC_CONTACTS; 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_X, bit); 714 set_y(&mts->pos, y, mt->sync + MTS_Y, bit); 715 716 /* Is this a new touch? */ 717 if ((mt->touches & bit) == (mt->sync[MTS_TOUCH] & 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_PRESSURE] |= bit; 724 725 if (pressure) { 726 if ((mt->touches & bit) == 0) { 727 mt->num_touches++; 728 mt->touches |= bit; 729 mt->sync[MTS_TOUCH] |= bit; 730 731 mt->sync[MTS_X] |= bit; 732 mt->sync[MTS_Y] |= bit; 733 } 734 } else if (mt->touches & bit) { 735 mt->num_touches--; 736 mt->touches ^= bit; 737 mt->sync[MTS_TOUCH] |= 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)) { 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; 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; 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) { 811 /* Restore valid coordinates. */ 812 motion->pos.x -= motion->pos.dx; 813 motion->pos.y -= motion->pos.dy; 814 motion->sync &= ~SYNC_POSITION; 815 } 816 817 if (touch->prev_contacts == 0) 818 touch->sync &= ~SYNC_PRESSURE; 819 820 } 821 822 if (touch->sync & SYNC_CONTACTS) 823 /* Suppress pointer movement. */ 824 cleardeltas(&motion->pos); 825 826 if ((touch->sync & SYNC_PRESSURE) && 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_X; i < MTS_SIZE; 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_X] | mt->sync[MTS_Y]) & ~mt->sync[MTS_TOUCH]; 888 FOREACHBIT(updates, slot) { 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; 940 if (mts->pos.y != input->motion.pos.y) 941 input->motion.sync |= SYNC_Y; 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)); 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->ws_get - evq->put; 962 if (space != 1 && space != 1 - WSEVENT_QSIZE) { 963 ev = &evq->evar->ws_q[evq->put++]; 964 evq->put %= WSEVENT_QSIZE; 965 ev->type = ev_type; 966 ev->value = ev_value; 967 memcpy(&ev->time, &evq->ts, sizeof(struct timespec)); 968 evq->result |= EVQ_RESULT_SUCCESS; 969 } else { 970 evq->result = EVQ_RESULT_OVERFLOW; 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_EV : BTN_UP_EV; 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) { 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), dx); 1022 if (dy) 1023 wsmouse_evq_put(evq, DELTA_Y_EV(input), dy); 1024 if (motion->dz) { 1025 dz = (input->flags & REVERSE_SCROLLING) 1026 ? -motion->dz : motion->dz; 1027 if (IS_TOUCHPAD(input)) 1028 wsmouse_evq_put(evq, VSCROLL_EV, dz); 1029 else 1030 wsmouse_evq_put(evq, DELTA_Z_EV, dz); 1031 } 1032 if (motion->dw) { 1033 dw = (input->flags & REVERSE_SCROLLING) 1034 ? -motion->dw : motion->dw; 1035 if (IS_TOUCHPAD(input)) 1036 wsmouse_evq_put(evq, HSCROLL_EV, dw); 1037 else 1038 wsmouse_evq_put(evq, DELTA_W_EV, dw); 1039 } 1040 } 1041 if (motion->sync & SYNC_POSITION) { 1042 if (motion->sync & SYNC_X) { 1043 x = (h->inv ? h->inv - motion->pos.x : motion->pos.x); 1044 wsmouse_evq_put(evq, ABS_X_EV(input), x); 1045 } 1046 if (motion->sync & SYNC_Y) { 1047 y = (v->inv ? v->inv - motion->pos.y : motion->pos.y); 1048 wsmouse_evq_put(evq, ABS_Y_EV(input), y); 1049 } 1050 if (motion->pos.dx == 0 && motion->pos.dy == 0 1051 && (input->flags & TPAD_NATIVE_MODE )) 1052 /* Suppress pointer motion. */ 1053 wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_RESET, 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) 1063 wsmouse_evq_put(evq, ABS_Z_EV, touch->pressure); 1064 if (touch->sync & SYNC_CONTACTS) 1065 wsmouse_evq_put(evq, ABS_W_EV, touch->contacts); 1066 if ((touch->sync & SYNC_TOUCH_WIDTH) 1067 && (input->flags & TPAD_NATIVE_MODE)) 1068 wsmouse_evq_put(evq, WSCONS_EVENT_TOUCH_WIDTH, 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); 1078 mt_sync = (input->mt.frame && (input->mt.sync[MTS_TOUCH] 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), LOGTIME(ts)); 1083 else 1084 return; 1085 1086 if (motion->sync & SYNC_POSITION) 1087 printf(" abs:%d,%d", motion->pos.x, motion->pos.y); 1088 if (motion->sync & SYNC_DELTAS) 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->ws_put; 1106 1107 if (n != evq->put) { 1108 printf("[%s-ev][%04d]", DEVNAME(input), LOGTIME(&evq->ts)); 1109 while (n != evq->put) { 1110 ev = &evq->evar->ws_q[n++]; 1111 n %= WSEVENT_QSIZE; 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_SIZE; 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) 1143 return; 1144 evq.put = evq.evar->ws_put; 1145 evq.result = EVQ_RESULT_NONE; 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) 1161 wsmouse_log_input(input, &evq.ts); 1162 1163 if (input->flags & TPAD_COMPAT_MODE) 1164 wstpad_compat_convert(input, &evq); 1165 1166 if (input->flags & RESYNC) { 1167 input->flags &= ~RESYNC; 1168 input->motion.sync &= SYNC_POSITION; 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_SUCCESS) { 1182 wsmouse_evq_put(&evq, WSCONS_EVENT_SYNC, 0); 1183 if (evq.result == EVQ_RESULT_SUCCESS) { 1184 if (input->flags & LOG_EVENTS) { 1185 wsmouse_log_events(input, &evq); 1186 } 1187 evq.evar->ws_put = evq.put; 1188 WSEVENT_WAKEUP(evq.evar); 1189 } 1190 } 1191 1192 if (evq.result != EVQ_RESULT_OVERFLOW) 1193 clear_sync_flags(input); 1194 else 1195 input->flags |= RESYNC; 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) { 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 < n; col++) { 1257 delta = INT_MAX; 1258 row = 0; 1259 for (i = 0, p = matrix + col; i < m; i++, p += n) { 1260 d = *p - red[i]; 1261 if (d < delta || (d == delta && r2c[i] < 0)) { 1262 delta = d; 1263 row = i; 1264 } 1265 } 1266 cd[col] = delta; 1267 if (r2c[row] < 0) { 1268 r2c[row] = col; 1269 continue; 1270 } 1271 for (p = mc; p < cs; *p++ = col) {} 1272 for (k = 0; (j = r2c[row]) >= 0;) { 1273 cs[k++] = j; 1274 c2r[j] = row; 1275 mc[row] -= n; 1276 delta = INT_MAX; 1277 for (i = 0, p = matrix; i < m; i++, p += n) 1278 if (mc[i] >= 0) { 1279 d = p[mc[i]] - cd[mc[i]]; 1280 e = p[j] - cd[j]; 1281 if (e < d) { 1282 d = e; 1283 mc[i] = j; 1284 } 1285 d -= red[i]; 1286 if (d < delta || (d == delta 1287 && r2c[i] < 0)) { 1288 delta = d; 1289 row = i; 1290 } 1291 } 1292 cd[col] += delta; 1293 for (i = 0; i < k; i++) { 1294 cd[cs[i]] += delta; 1295 red[c2r[cs[i]]] -= delta; 1296 } 1297 } 1298 for (j = mc[row]; (r2c[row] = j) != col;) { 1299 row = c2r[j]; 1300 j = mc[row] + n; 1301 } 1302 } 1303 } 1304 1305 /* 1306 * Assign slot numbers to the points in the pt array, and update all slots by 1307 * calling wsmouse_mtstate internally. The slot numbers are passed to the 1308 * caller in the pt->slot fields. 1309 * 1310 * The slot assignment pairs the points with points of the previous frame in 1311 * such a way that the sum of the squared distances is minimal. Using 1312 * squares instead of simple distances favours assignments with more uniform 1313 * distances, and it is faster. 1314 */ 1315 void 1316 wsmouse_mtframe(struct device *sc, struct mtpoint *pt, int size) 1317 { 1318 struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input; 1319 struct mt_state *mt = &input->mt; 1320 int i, j, m, n, dx, dy, slot, maxdist; 1321 int *p, *r2c, *c2r; 1322 u_int touches; 1323 1324 if (mt->num_slots == 0 || mt->matrix == NULL) 1325 return; 1326 1327 size = imax(0, imin(size, mt->num_slots)); 1328 p = mt->matrix; 1329 touches = mt->touches; 1330 if (mt->num_touches >= size) { 1331 FOREACHBIT(touches, slot) 1332 for (i = 0; i < size; i++) { 1333 dx = pt[i].x - mt->slots[slot].pos.x; 1334 dy = pt[i].y - mt->slots[slot].pos.y; 1335 *p++ = dx * dx + dy * dy; 1336 } 1337 m = mt->num_touches; 1338 n = size; 1339 } else { 1340 for (i = 0; i < size; i++) 1341 FOREACHBIT(touches, slot) { 1342 dx = pt[i].x - mt->slots[slot].pos.x; 1343 dy = pt[i].y - mt->slots[slot].pos.y; 1344 *p++ = dx * dx + dy * dy; 1345 } 1346 m = size; 1347 n = mt->num_touches; 1348 } 1349 wsmouse_matching(mt->matrix, m, n, p); 1350 1351 r2c = p; 1352 c2r = p + m; 1353 maxdist = input->filter.tracking_maxdist; 1354 maxdist = (maxdist ? maxdist * maxdist : INT_MAX); 1355 for (i = 0, p = mt->matrix; i < m; i++, p += n) 1356 if ((j = r2c[i]) >= 0) { 1357 if (p[j] <= maxdist) 1358 c2r[j] = i; 1359 else 1360 c2r[j] = r2c[i] = -1; 1361 } 1362 1363 p = (n == size ? c2r : r2c); 1364 for (i = 0; i < size; i++) 1365 if (*p++ < 0) { 1366 slot = ffs(~(mt->touches | mt->frame)) - 1; 1367 if (slot < 0 || slot >= mt->num_slots) 1368 break; 1369 wsmouse_mtstate(sc, slot, 1370 pt[i].x, pt[i].y, pt[i].pressure); 1371 pt[i].slot = slot; 1372 } 1373 1374 p = (n == size ? r2c : c2r); 1375 FOREACHBIT(touches, slot) 1376 if ((i = *p++) >= 0) { 1377 wsmouse_mtstate(sc, slot, 1378 pt[i].x, pt[i].y, pt[i].pressure); 1379 pt[i].slot = slot; 1380 } else { 1381 wsmouse_mtstate(sc, slot, 0, 0, 0); 1382 } 1383 } 1384 1385 static inline void 1386 free_mt_slots(struct wsmouseinput *input) 1387 { 1388 int n, size; 1389 1390 if ((n = input->mt.num_slots)) { 1391 size = n * sizeof(struct mt_slot); 1392 if (input->flags & MT_TRACKING) 1393 size += MATRIX_SIZE(n); 1394 input->mt.num_slots = 0; 1395 free(input->mt.slots, M_DEVBUF, size); 1396 input->mt.slots = NULL; 1397 input->mt.matrix = NULL; 1398 } 1399 } 1400 1401 /* Allocate the MT slots and, if necessary, the buffers for MT tracking. */ 1402 int 1403 wsmouse_mt_init(struct device *sc, int num_slots, int tracking) 1404 { 1405 struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input; 1406 int n, size; 1407 1408 if (num_slots == input->mt.num_slots 1409 && (!tracking == ((input->flags & MT_TRACKING) == 0))) 1410 return (0); 1411 1412 free_mt_slots(input); 1413 1414 if (tracking) 1415 input->flags |= MT_TRACKING; 1416 else 1417 input->flags &= ~MT_TRACKING; 1418 n = imin(imax(num_slots, 0), WSMOUSE_MT_SLOTS_MAX); 1419 if (n) { 1420 size = n * sizeof(struct mt_slot); 1421 if (input->flags & MT_TRACKING) 1422 size += MATRIX_SIZE(n); 1423 input->mt.slots = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO); 1424 if (input->mt.slots != NULL) { 1425 if (input->flags & MT_TRACKING) 1426 input->mt.matrix = (int *) 1427 (input->mt.slots + n); 1428 input->mt.num_slots = n; 1429 return (0); 1430 } 1431 } 1432 return (-1); 1433 } 1434 1435 int 1436 wsmouse_get_params(struct device *sc, 1437 struct wsmouse_param *params, u_int nparams) 1438 { 1439 struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input; 1440 int i, key, error = 0; 1441 1442 for (i = 0; i < nparams; i++) { 1443 key = params[i].key; 1444 switch (key) { 1445 case WSMOUSECFG_DX_SCALE: 1446 params[i].value = input->filter.h.scale; 1447 break; 1448 case WSMOUSECFG_DY_SCALE: 1449 params[i].value = input->filter.v.scale; 1450 break; 1451 case WSMOUSECFG_PRESSURE_LO: 1452 params[i].value = input->filter.pressure_lo; 1453 break; 1454 case WSMOUSECFG_PRESSURE_HI: 1455 params[i].value = input->filter.pressure_hi; 1456 break; 1457 case WSMOUSECFG_TRKMAXDIST: 1458 params[i].value = input->filter.tracking_maxdist; 1459 break; 1460 case WSMOUSECFG_SWAPXY: 1461 params[i].value = input->filter.swapxy; 1462 break; 1463 case WSMOUSECFG_X_INV: 1464 params[i].value = input->filter.h.inv; 1465 break; 1466 case WSMOUSECFG_Y_INV: 1467 params[i].value = input->filter.v.inv; 1468 break; 1469 case WSMOUSECFG_REVERSE_SCROLLING: 1470 params[i].value = !!(input->flags & REVERSE_SCROLLING); 1471 break; 1472 case WSMOUSECFG_DX_MAX: 1473 params[i].value = input->filter.h.dmax; 1474 break; 1475 case WSMOUSECFG_DY_MAX: 1476 params[i].value = input->filter.v.dmax; 1477 break; 1478 case WSMOUSECFG_X_HYSTERESIS: 1479 params[i].value = input->filter.h.hysteresis; 1480 break; 1481 case WSMOUSECFG_Y_HYSTERESIS: 1482 params[i].value = input->filter.v.hysteresis; 1483 break; 1484 case WSMOUSECFG_DECELERATION: 1485 params[i].value = input->filter.dclr; 1486 break; 1487 case WSMOUSECFG_STRONG_HYSTERESIS: 1488 params[i].value = 0; /* The feature has been removed. */ 1489 break; 1490 case WSMOUSECFG_SMOOTHING: 1491 params[i].value = 1492 input->filter.mode & SMOOTHING_MASK; 1493 break; 1494 case WSMOUSECFG_LOG_INPUT: 1495 params[i].value = !!(input->flags & LOG_INPUT); 1496 break; 1497 case WSMOUSECFG_LOG_EVENTS: 1498 params[i].value = !!(input->flags & LOG_EVENTS); 1499 break; 1500 default: 1501 error = wstpad_get_param(input, key, ¶ms[i].value); 1502 if (error != 0) 1503 return (error); 1504 break; 1505 } 1506 } 1507 1508 return (0); 1509 } 1510 1511 int 1512 wsmouse_set_params(struct device *sc, 1513 const struct wsmouse_param *params, u_int nparams) 1514 { 1515 struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input; 1516 int i, val, key, needreset = 0, error = 0; 1517 1518 for (i = 0; i < nparams; i++) { 1519 key = params[i].key; 1520 val = params[i].value; 1521 switch (params[i].key) { 1522 case WSMOUSECFG_PRESSURE_LO: 1523 input->filter.pressure_lo = val; 1524 if (val > input->filter.pressure_hi) 1525 input->filter.pressure_hi = val; 1526 input->touch.min_pressure = input->filter.pressure_hi; 1527 break; 1528 case WSMOUSECFG_PRESSURE_HI: 1529 input->filter.pressure_hi = val; 1530 if (val < input->filter.pressure_lo) 1531 input->filter.pressure_lo = val; 1532 input->touch.min_pressure = val; 1533 break; 1534 case WSMOUSECFG_X_HYSTERESIS: 1535 input->filter.h.hysteresis = val; 1536 break; 1537 case WSMOUSECFG_Y_HYSTERESIS: 1538 input->filter.v.hysteresis = val; 1539 break; 1540 case WSMOUSECFG_DECELERATION: 1541 input->filter.dclr = val; 1542 wstpad_init_deceleration(input); 1543 break; 1544 case WSMOUSECFG_DX_SCALE: 1545 input->filter.h.scale = val; 1546 break; 1547 case WSMOUSECFG_DY_SCALE: 1548 input->filter.v.scale = val; 1549 break; 1550 case WSMOUSECFG_TRKMAXDIST: 1551 input->filter.tracking_maxdist = val; 1552 break; 1553 case WSMOUSECFG_SWAPXY: 1554 input->filter.swapxy = val; 1555 break; 1556 case WSMOUSECFG_X_INV: 1557 input->filter.h.inv = val; 1558 break; 1559 case WSMOUSECFG_Y_INV: 1560 input->filter.v.inv = val; 1561 break; 1562 case WSMOUSECFG_REVERSE_SCROLLING: 1563 if (val) 1564 input->flags |= REVERSE_SCROLLING; 1565 else 1566 input->flags &= ~REVERSE_SCROLLING; 1567 break; 1568 case WSMOUSECFG_DX_MAX: 1569 input->filter.h.dmax = val; 1570 break; 1571 case WSMOUSECFG_DY_MAX: 1572 input->filter.v.dmax = val; 1573 break; 1574 case WSMOUSECFG_SMOOTHING: 1575 input->filter.mode &= ~SMOOTHING_MASK; 1576 input->filter.mode |= (val & SMOOTHING_MASK); 1577 break; 1578 case WSMOUSECFG_LOG_INPUT: 1579 if (val) 1580 input->flags |= LOG_INPUT; 1581 else 1582 input->flags &= ~LOG_INPUT; 1583 break; 1584 case WSMOUSECFG_LOG_EVENTS: 1585 if (val) 1586 input->flags |= LOG_EVENTS; 1587 else 1588 input->flags &= ~LOG_EVENTS; 1589 break; 1590 default: 1591 needreset = 1; 1592 error = wstpad_set_param(input, key, val); 1593 if (error != 0) 1594 return (error); 1595 break; 1596 } 1597 } 1598 1599 /* Reset soft-states if touchpad parameters changed */ 1600 if (needreset) { 1601 wstpad_reset(input); 1602 return (wstpad_configure(input)); 1603 } 1604 1605 return (0); 1606 } 1607 1608 int 1609 wsmouse_set_mode(struct device *sc, int mode) 1610 { 1611 struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input; 1612 1613 if (mode == WSMOUSE_COMPAT) { 1614 input->flags &= ~TPAD_NATIVE_MODE; 1615 input->flags |= TPAD_COMPAT_MODE; 1616 return (0); 1617 } else if (mode == WSMOUSE_NATIVE) { 1618 input->flags &= ~TPAD_COMPAT_MODE; 1619 input->flags |= TPAD_NATIVE_MODE; 1620 return (0); 1621 } 1622 return (-1); 1623 } 1624 1625 struct wsmousehw * 1626 wsmouse_get_hw(struct device *sc) 1627 { 1628 return &((struct wsmouse_softc *) sc)->sc_input.hw; 1629 } 1630 1631 /* 1632 * Create a default configuration based on the hardware infos in the 'hw' 1633 * fields. The 'params' argument is optional, hardware drivers can use it 1634 * to modify the generic defaults. Up to now this function is only useful 1635 * for touchpads. 1636 */ 1637 int 1638 wsmouse_configure(struct device *sc, 1639 struct wsmouse_param *params, u_int nparams) 1640 { 1641 struct wsmouseinput *input = &((struct wsmouse_softc *) sc)->sc_input; 1642 int error; 1643 1644 if (!(input->flags & CONFIGURED)) { 1645 if (input->hw.x_max && input->hw.y_max) { 1646 if (input->hw.flags & WSMOUSEHW_LR_DOWN) { 1647 input->filter.v.inv = 1648 input->hw.y_max + input->hw.y_min; 1649 } 1650 } 1651 input->filter.ratio = 1 << 12; 1652 if (input->hw.h_res > 0 && input->hw.v_res > 0) { 1653 input->filter.ratio *= input->hw.h_res; 1654 input->filter.ratio /= input->hw.v_res; 1655 } 1656 if (wsmouse_mt_init(sc, input->hw.mt_slots, 1657 (input->hw.flags & WSMOUSEHW_MT_TRACKING))) { 1658 printf("wsmouse_configure: " 1659 "MT initialization failed.\n"); 1660 return (-1); 1661 } 1662 if (IS_TOUCHPAD(input) && wstpad_configure(input)) { 1663 printf("wstpad_configure: " 1664 "Initialization failed.\n"); 1665 return (-1); 1666 } 1667 input->flags |= CONFIGURED; 1668 if (params != NULL) { 1669 if ((error = wsmouse_set_params(sc, params, nparams))) 1670 return (error); 1671 } 1672 } 1673 if (IS_TOUCHPAD(input)) 1674 wsmouse_set_mode(sc, WSMOUSE_COMPAT); 1675 1676 return (0); 1677 } 1678 1679 1680 void 1681 wsmouse_input_reset(struct wsmouseinput *input) 1682 { 1683 int num_slots, *matrix; 1684 struct mt_slot *slots; 1685 1686 memset(&input->btn, 0, sizeof(struct btn_state)); 1687 memset(&input->motion, 0, sizeof(struct motion_state)); 1688 memset(&input->touch, 0, sizeof(struct touch_state)); 1689 input->touch.min_pressure = input->filter.pressure_hi; 1690 if ((num_slots = input->mt.num_slots)) { 1691 slots = input->mt.slots; 1692 matrix = input->mt.matrix; 1693 memset(&input->mt, 0, sizeof(struct mt_state)); 1694 memset(slots, 0, num_slots * sizeof(struct mt_slot)); 1695 input->mt.num_slots = num_slots; 1696 input->mt.slots = slots; 1697 input->mt.matrix = matrix; 1698 } 1699 if (input->tp != NULL) 1700 wstpad_reset(input); 1701 } 1702 1703 void 1704 wsmouse_input_cleanup(struct wsmouseinput *input) 1705 { 1706 if (input->tp != NULL) 1707 wstpad_cleanup(input); 1708 1709 free_mt_slots(input); 1710 } 1711