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