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