1 /* $NetBSD: wsmouse.c,v 1.75 2025/04/07 11:25:42 hans Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Julio M. Merino Vidal. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. All advertising materials mentioning features or use of this software 44 * must display the following acknowledgement: 45 * This product includes software developed by Christopher G. Demetriou 46 * for the NetBSD Project. 47 * 4. The name of the author may not be used to endorse or promote products 48 * derived from this software without specific prior written permission 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 51 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 52 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 53 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 54 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 55 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 56 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 57 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 58 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 59 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 60 */ 61 62 /* 63 * Copyright (c) 1992, 1993 64 * The Regents of the University of California. All rights reserved. 65 * 66 * This software was developed by the Computer Systems Engineering group 67 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 68 * contributed to Berkeley. 69 * 70 * All advertising materials mentioning features or use of this software 71 * must display the following acknowledgement: 72 * This product includes software developed by the University of 73 * California, Lawrence Berkeley Laboratory. 74 * 75 * Redistribution and use in source and binary forms, with or without 76 * modification, are permitted provided that the following conditions 77 * are met: 78 * 1. Redistributions of source code must retain the above copyright 79 * notice, this list of conditions and the following disclaimer. 80 * 2. Redistributions in binary form must reproduce the above copyright 81 * notice, this list of conditions and the following disclaimer in the 82 * documentation and/or other materials provided with the distribution. 83 * 3. Neither the name of the University nor the names of its contributors 84 * may be used to endorse or promote products derived from this software 85 * without specific prior written permission. 86 * 87 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 88 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 89 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 90 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 91 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 92 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 93 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 94 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 95 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 96 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 97 * SUCH DAMAGE. 98 * 99 * @(#)ms.c 8.1 (Berkeley) 6/11/93 100 */ 101 102 /* 103 * Mouse driver. 104 */ 105 106 #include <sys/cdefs.h> 107 __KERNEL_RCSID(0, "$NetBSD: wsmouse.c,v 1.75 2025/04/07 11:25:42 hans Exp $"); 108 109 #include "wsmouse.h" 110 #include "wsdisplay.h" 111 #include "wsmux.h" 112 113 #include <sys/param.h> 114 #include <sys/conf.h> 115 #include <sys/ioctl.h> 116 #include <sys/poll.h> 117 #include <sys/fcntl.h> 118 #include <sys/kernel.h> 119 #include <sys/proc.h> 120 #include <sys/syslog.h> 121 #include <sys/systm.h> 122 #include <sys/tty.h> 123 #include <sys/signalvar.h> 124 #include <sys/device.h> 125 #include <sys/vnode.h> 126 #include <sys/callout.h> 127 128 #include <dev/wscons/wsconsio.h> 129 #include <dev/wscons/wsmousevar.h> 130 #include <dev/wscons/wseventvar.h> 131 #include <dev/wscons/wsmuxvar.h> 132 133 #include "ioconf.h" 134 135 #if defined(WSMUX_DEBUG) && NWSMUX > 0 136 #define DPRINTF(x) if (wsmuxdebug) printf x 137 #define DPRINTFN(n,x) if (wsmuxdebug > (n)) printf x 138 extern int wsmuxdebug; 139 #else 140 #define DPRINTF(x) 141 #define DPRINTFN(n,x) 142 #endif 143 144 #define INVALID_X INT_MAX 145 #define INVALID_Y INT_MAX 146 #define INVALID_Z INT_MAX 147 #define INVALID_W INT_MAX 148 149 struct wsmouse_softc { 150 struct wsevsrc sc_base; 151 152 const struct wsmouse_accessops *sc_accessops; 153 void *sc_accesscookie; 154 155 u_int sc_mb; /* mouse button state */ 156 u_int sc_ub; /* user button state */ 157 int sc_dx; /* delta-x */ 158 int sc_dy; /* delta-y */ 159 int sc_dz; /* delta-z */ 160 int sc_dw; /* delta-w */ 161 int sc_x; /* absolute-x */ 162 int sc_y; /* absolute-y */ 163 int sc_z; /* absolute-z */ 164 int sc_w; /* absolute-w */ 165 166 int sc_refcnt; 167 u_char sc_dying; /* device is being detached */ 168 169 struct wsmouse_repeat sc_repeat; 170 int sc_repeat_button; 171 callout_t sc_repeat_callout; 172 unsigned int sc_repeat_delay; 173 174 int sc_reverse_scroll; 175 int sc_horiz_scroll_dist; 176 int sc_vert_scroll_dist; 177 }; 178 179 static int wsmouse_match(device_t, cfdata_t, void *); 180 static void wsmouse_attach(device_t, device_t, void *); 181 static int wsmouse_detach(device_t, int); 182 static int wsmouse_activate(device_t, enum devact); 183 184 static int wsmouse_set_params(struct wsmouse_softc *, 185 struct wsmouse_param *, size_t); 186 static int wsmouse_get_params(struct wsmouse_softc *, 187 struct wsmouse_param *, size_t); 188 static int wsmouse_handle_params(struct wsmouse_softc *, 189 struct wsmouse_parameters *, bool); 190 191 static int wsmouse_do_ioctl(struct wsmouse_softc *, u_long, void *, 192 int, struct lwp *); 193 194 #if NWSMUX > 0 195 static int wsmouse_mux_open(struct wsevsrc *, struct wseventvar *); 196 static int wsmouse_mux_close(struct wsevsrc *); 197 #endif 198 199 static int wsmousedoioctl(device_t, u_long, void *, int, struct lwp *); 200 201 static int wsmousedoopen(struct wsmouse_softc *, struct wseventvar *); 202 203 CFATTACH_DECL_NEW(wsmouse, sizeof (struct wsmouse_softc), 204 wsmouse_match, wsmouse_attach, wsmouse_detach, wsmouse_activate); 205 206 static void wsmouse_repeat(void *v); 207 208 dev_type_open(wsmouseopen); 209 dev_type_close(wsmouseclose); 210 dev_type_read(wsmouseread); 211 dev_type_ioctl(wsmouseioctl); 212 dev_type_poll(wsmousepoll); 213 dev_type_kqfilter(wsmousekqfilter); 214 215 const struct cdevsw wsmouse_cdevsw = { 216 .d_open = wsmouseopen, 217 .d_close = wsmouseclose, 218 .d_read = wsmouseread, 219 .d_write = nowrite, 220 .d_ioctl = wsmouseioctl, 221 .d_stop = nostop, 222 .d_tty = notty, 223 .d_poll = wsmousepoll, 224 .d_mmap = nommap, 225 .d_kqfilter = wsmousekqfilter, 226 .d_discard = nodiscard, 227 .d_flag = D_OTHER 228 }; 229 230 #if NWSMUX > 0 231 struct wssrcops wsmouse_srcops = { 232 WSMUX_MOUSE, 233 wsmouse_mux_open, wsmouse_mux_close, wsmousedoioctl, NULL, NULL 234 }; 235 #endif 236 237 /* 238 * Print function (for parent devices). 239 */ 240 int 241 wsmousedevprint(void *aux, const char *pnp) 242 { 243 244 if (pnp) 245 aprint_normal("wsmouse at %s", pnp); 246 return (UNCONF); 247 } 248 249 int 250 wsmouse_match(device_t parent, cfdata_t match, void *aux) 251 { 252 return (1); 253 } 254 255 void 256 wsmouse_attach(device_t parent, device_t self, void *aux) 257 { 258 struct wsmouse_softc *sc = device_private(self); 259 struct wsmousedev_attach_args *ap = aux; 260 #if NWSMUX > 0 261 int mux, error; 262 #endif 263 264 sc->sc_base.me_dv = self; 265 sc->sc_accessops = ap->accessops; 266 sc->sc_accesscookie = ap->accesscookie; 267 268 /* Initialize button repeating. */ 269 memset(&sc->sc_repeat, 0, sizeof(sc->sc_repeat)); 270 sc->sc_repeat_button = -1; 271 sc->sc_repeat_delay = 0; 272 sc->sc_reverse_scroll = 0; 273 sc->sc_horiz_scroll_dist = WSMOUSE_DEFAULT_SCROLL_DIST; 274 sc->sc_vert_scroll_dist = WSMOUSE_DEFAULT_SCROLL_DIST; 275 callout_init(&sc->sc_repeat_callout, 0); 276 callout_setfunc(&sc->sc_repeat_callout, wsmouse_repeat, sc); 277 278 #if NWSMUX > 0 279 sc->sc_base.me_ops = &wsmouse_srcops; 280 mux = device_cfdata(self)->wsmousedevcf_mux; 281 if (mux >= 0) { 282 error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base); 283 if (error) 284 aprint_error(" attach error=%d", error); 285 else 286 aprint_normal(" mux %d", mux); 287 } 288 #else 289 if (device_cfdata(self)->wsmousedevcf_mux >= 0) 290 aprint_normal(" (mux ignored)"); 291 #endif 292 293 aprint_naive("\n"); 294 aprint_normal("\n"); 295 296 if (!pmf_device_register(self, NULL, NULL)) 297 aprint_error_dev(self, "couldn't establish power handler\n"); 298 } 299 300 int 301 wsmouse_activate(device_t self, enum devact act) 302 { 303 struct wsmouse_softc *sc = device_private(self); 304 305 if (act == DVACT_DEACTIVATE) 306 sc->sc_dying = 1; 307 return (0); 308 } 309 310 /* 311 * Detach a mouse. To keep track of users of the softc we keep 312 * a reference count that's incremented while inside, e.g., read. 313 * If the mouse is active and the reference count is > 0 (0 is the 314 * normal state) we post an event and then wait for the process 315 * that had the reference to wake us up again. Then we blow away the 316 * vnode and return (which will deallocate the softc). 317 */ 318 int 319 wsmouse_detach(device_t self, int flags) 320 { 321 struct wsmouse_softc *sc = device_private(self); 322 struct wseventvar *evar; 323 int maj, mn; 324 int s; 325 326 #if NWSMUX > 0 327 /* Tell parent mux we're leaving. */ 328 if (sc->sc_base.me_parent != NULL) { 329 DPRINTF(("wsmouse_detach:\n")); 330 wsmux_detach_sc(&sc->sc_base); 331 } 332 #endif 333 334 /* If we're open ... */ 335 evar = sc->sc_base.me_evp; 336 if (evar != NULL && evar->io != NULL) { 337 s = spltty(); 338 if (--sc->sc_refcnt >= 0) { 339 struct wscons_event event; 340 341 /* Wake everyone by generating a dummy event. */ 342 event.type = 0; 343 event.value = 0; 344 if (wsevent_inject(evar, &event, 1) != 0) 345 wsevent_wakeup(evar); 346 347 /* Wait for processes to go away. */ 348 if (tsleep(sc, PZERO, "wsmdet", hz * 60)) 349 printf("wsmouse_detach: %s didn't detach\n", 350 device_xname(self)); 351 } 352 splx(s); 353 } 354 355 /* locate the major number */ 356 maj = cdevsw_lookup_major(&wsmouse_cdevsw); 357 358 /* Nuke the vnodes for any open instances (calls close). */ 359 mn = device_unit(self); 360 vdevgone(maj, mn, mn, VCHR); 361 362 return (0); 363 } 364 365 void 366 wsmouse_input(device_t wsmousedev, u_int btns /* 0 is up */, 367 int x, int y, int z, int w, u_int flags) 368 { 369 struct wsmouse_softc *sc = device_private(wsmousedev); 370 struct wseventvar *evar; 371 int mb, ub, d, nevents; 372 /* one for each dimension (4) + a bit for each button */ 373 struct wscons_event events[4 + sizeof(d) * 8]; 374 375 KERNEL_LOCK(1, NULL); 376 377 /* 378 * Discard input if not open. 379 */ 380 evar = sc->sc_base.me_evp; 381 if (evar == NULL || evar->q == NULL) 382 goto out; 383 384 #if NWSMUX > 0 385 DPRINTFN(5,("wsmouse_input: %s mux=%p, evar=%p\n", 386 device_xname(sc->sc_base.me_dv), 387 sc->sc_base.me_parent, evar)); 388 #endif 389 390 sc->sc_mb = btns; 391 if (!(flags & WSMOUSE_INPUT_ABSOLUTE_X)) 392 sc->sc_dx += x; 393 if (!(flags & WSMOUSE_INPUT_ABSOLUTE_Y)) 394 sc->sc_dy += y; 395 if (!(flags & WSMOUSE_INPUT_ABSOLUTE_Z)) 396 sc->sc_dz += z; 397 if (!(flags & WSMOUSE_INPUT_ABSOLUTE_W)) 398 sc->sc_dw += w; 399 400 /* 401 * We have at least one event (mouse button, delta-X, or 402 * delta-Y; possibly all three, and possibly three separate 403 * button events). Deliver these events until we are out 404 * of changes or out of room. As events get delivered, 405 * mark them `unchanged'. 406 */ 407 ub = sc->sc_ub; 408 nevents = 0; 409 410 if (flags & WSMOUSE_INPUT_ABSOLUTE_X) { 411 if (sc->sc_x != x) { 412 events[nevents].type = WSCONS_EVENT_MOUSE_ABSOLUTE_X; 413 events[nevents].value = x; 414 nevents++; 415 } 416 } else { 417 if (sc->sc_dx) { 418 events[nevents].type = WSCONS_EVENT_MOUSE_DELTA_X; 419 events[nevents].value = sc->sc_dx; 420 nevents++; 421 } 422 } 423 if (flags & WSMOUSE_INPUT_ABSOLUTE_Y) { 424 if (sc->sc_y != y) { 425 events[nevents].type = WSCONS_EVENT_MOUSE_ABSOLUTE_Y; 426 events[nevents].value = y; 427 nevents++; 428 } 429 } else { 430 if (sc->sc_dy) { 431 events[nevents].type = WSCONS_EVENT_MOUSE_DELTA_Y; 432 events[nevents].value = sc->sc_dy; 433 nevents++; 434 } 435 } 436 if (flags & WSMOUSE_INPUT_ABSOLUTE_Z) { 437 if (sc->sc_z != z) { 438 events[nevents].type = WSCONS_EVENT_MOUSE_ABSOLUTE_Z; 439 events[nevents].value = z; 440 nevents++; 441 } 442 } else { 443 if (sc->sc_dz) { 444 events[nevents].type = WSCONS_EVENT_MOUSE_DELTA_Z; 445 events[nevents].value = sc->sc_dz; 446 nevents++; 447 } 448 } 449 if (flags & WSMOUSE_INPUT_ABSOLUTE_W) { 450 if (sc->sc_w != w) { 451 events[nevents].type = WSCONS_EVENT_MOUSE_ABSOLUTE_W; 452 events[nevents].value = w; 453 nevents++; 454 } 455 } else { 456 if (sc->sc_dw) { 457 events[nevents].type = WSCONS_EVENT_MOUSE_DELTA_W; 458 events[nevents].value = sc->sc_dw; 459 nevents++; 460 } 461 } 462 463 mb = sc->sc_mb; 464 while ((d = mb ^ ub) != 0) { 465 int btnno; 466 467 /* 468 * Cancel button repeating if button status changed. 469 */ 470 if (sc->sc_repeat_button != -1) { 471 KASSERT(sc->sc_repeat_button >= 0); 472 KASSERT(sc->sc_repeat.wr_buttons & 473 (1 << sc->sc_repeat_button)); 474 ub &= ~(1 << sc->sc_repeat_button); 475 sc->sc_repeat_button = -1; 476 callout_stop(&sc->sc_repeat_callout); 477 } 478 479 /* 480 * Mouse button change. Find the first change and drop 481 * it into the event queue. 482 */ 483 btnno = ffs(d) - 1; 484 KASSERT(btnno >= 0); 485 486 if (nevents >= __arraycount(events)) { 487 aprint_error_dev(sc->sc_base.me_dv, 488 "Event queue full (button status mb=0x%x" 489 " ub=0x%x)\n", mb, ub); 490 break; 491 } 492 493 events[nevents].type = 494 (mb & d) ? WSCONS_EVENT_MOUSE_DOWN : WSCONS_EVENT_MOUSE_UP; 495 events[nevents].value = btnno; 496 nevents++; 497 498 ub ^= (1 << btnno); 499 500 /* 501 * Program button repeating if configured for this button. 502 */ 503 if ((mb & d) && (sc->sc_repeat.wr_buttons & (1 << btnno)) && 504 sc->sc_repeat.wr_delay_first > 0) { 505 sc->sc_repeat_button = btnno; 506 sc->sc_repeat_delay = sc->sc_repeat.wr_delay_first; 507 callout_schedule(&sc->sc_repeat_callout, 508 mstohz(sc->sc_repeat_delay)); 509 } 510 } 511 512 if (nevents == 0 || wsevent_inject(evar, events, nevents) == 0) { 513 /* All events were correctly injected into the queue. 514 * Synchronize the mouse's status with what the user 515 * has received. */ 516 sc->sc_x = x; sc->sc_dx = 0; 517 sc->sc_y = y; sc->sc_dy = 0; 518 sc->sc_z = z; sc->sc_dz = 0; 519 sc->sc_w = w; sc->sc_dw = 0; 520 sc->sc_ub = ub; 521 #if NWSMUX > 0 522 DPRINTFN(5,("wsmouse_input: %s wakeup evar=%p\n", 523 device_xname(sc->sc_base.me_dv), evar)); 524 #endif 525 } 526 527 out: KERNEL_UNLOCK_ONE(NULL); 528 } 529 530 void 531 wsmouse_precision_scroll(device_t wsmousedev, int x, int y) 532 { 533 struct wsmouse_softc *sc = device_private(wsmousedev); 534 struct wseventvar *evar; 535 struct wscons_event events[2]; 536 int nevents = 0; 537 538 evar = sc->sc_base.me_evp; 539 if (evar == NULL) 540 return; 541 542 if (sc->sc_reverse_scroll) { 543 x = -x; 544 y = -y; 545 } 546 547 x = (x * 4096) / sc->sc_horiz_scroll_dist; 548 y = (y * 4096) / sc->sc_vert_scroll_dist; 549 550 if (x != 0) { 551 events[nevents].type = WSCONS_EVENT_HSCROLL; 552 events[nevents].value = x; 553 nevents++; 554 } 555 556 if (y != 0) { 557 events[nevents].type = WSCONS_EVENT_VSCROLL; 558 events[nevents].value = y; 559 nevents++; 560 } 561 562 (void)wsevent_inject(evar, events, nevents); 563 } 564 565 static void 566 wsmouse_repeat(void *v) 567 { 568 int oldspl; 569 unsigned int newdelay; 570 struct wsmouse_softc *sc; 571 struct wscons_event events[2]; 572 573 oldspl = spltty(); 574 sc = (struct wsmouse_softc *)v; 575 576 if (sc->sc_repeat_button == -1) { 577 /* Race condition: a "button up" event came in when 578 * this function was already called but did not do 579 * spltty() yet. */ 580 splx(oldspl); 581 return; 582 } 583 KASSERT(sc->sc_repeat_button >= 0); 584 585 KASSERT(sc->sc_repeat.wr_buttons & (1 << sc->sc_repeat_button)); 586 587 newdelay = sc->sc_repeat_delay; 588 589 events[0].type = WSCONS_EVENT_MOUSE_UP; 590 events[0].value = sc->sc_repeat_button; 591 events[1].type = WSCONS_EVENT_MOUSE_DOWN; 592 events[1].value = sc->sc_repeat_button; 593 594 if (wsevent_inject(sc->sc_base.me_evp, events, 2) == 0) { 595 sc->sc_ub = 1 << sc->sc_repeat_button; 596 597 if (newdelay - sc->sc_repeat.wr_delay_decrement < 598 sc->sc_repeat.wr_delay_minimum) 599 newdelay = sc->sc_repeat.wr_delay_minimum; 600 else if (newdelay > sc->sc_repeat.wr_delay_minimum) 601 newdelay -= sc->sc_repeat.wr_delay_decrement; 602 KASSERT(newdelay >= sc->sc_repeat.wr_delay_minimum); 603 KASSERT(newdelay <= sc->sc_repeat.wr_delay_first); 604 } 605 606 /* 607 * Reprogram the repeating event. 608 */ 609 sc->sc_repeat_delay = newdelay; 610 callout_schedule(&sc->sc_repeat_callout, mstohz(newdelay)); 611 612 splx(oldspl); 613 } 614 615 static int 616 wsmouse_set_params(struct wsmouse_softc *sc, 617 struct wsmouse_param *buf, size_t nparams) 618 { 619 size_t i = 0; 620 621 for (i = 0; i < nparams; ++i) { 622 switch (buf[i].key) { 623 case WSMOUSECFG_REVERSE_SCROLLING: 624 sc->sc_reverse_scroll = (buf[i].value != 0); 625 break; 626 case WSMOUSECFG_HORIZSCROLLDIST: 627 sc->sc_horiz_scroll_dist = buf[i].value; 628 break; 629 case WSMOUSECFG_VERTSCROLLDIST: 630 sc->sc_vert_scroll_dist = buf[i].value; 631 break; 632 } 633 } 634 return 0; 635 } 636 637 static int 638 wsmouse_get_params(struct wsmouse_softc *sc, 639 struct wsmouse_param *buf, size_t nparams) 640 { 641 size_t i = 0; 642 643 for (i = 0; i < nparams; ++i) { 644 switch (buf[i].key) { 645 case WSMOUSECFG_REVERSE_SCROLLING: 646 buf[i].value = sc->sc_reverse_scroll; 647 break; 648 case WSMOUSECFG_HORIZSCROLLDIST: 649 buf[i].value = sc->sc_horiz_scroll_dist; 650 break; 651 case WSMOUSECFG_VERTSCROLLDIST: 652 buf[i].value = sc->sc_vert_scroll_dist; 653 break; 654 } 655 } 656 return 0; 657 } 658 659 static int 660 wsmouse_handle_params(struct wsmouse_softc *sc, struct wsmouse_parameters *upl, 661 bool set) 662 { 663 size_t len; 664 struct wsmouse_param *buf; 665 int error = 0; 666 667 if (upl->params == NULL || upl->nparams > WSMOUSECFG_MAX) 668 return EINVAL; 669 if (upl->nparams == 0) 670 return 0; 671 672 len = upl->nparams * sizeof(struct wsmouse_param); 673 674 buf = kmem_alloc(len, KM_SLEEP); 675 if (buf == NULL) 676 return ENOMEM; 677 if ((error = copyin(upl->params, buf, len)) != 0) 678 goto error; 679 680 if (set) { 681 error = wsmouse_set_params(sc, buf, upl->nparams); 682 if (error != 0) 683 goto error; 684 } else { 685 error = wsmouse_get_params(sc, buf, upl->nparams); 686 if (error != 0) 687 goto error; 688 if ((error = copyout(buf, upl->params, len)) != 0) 689 goto error; 690 } 691 692 error: 693 kmem_free(buf, len); 694 return error; 695 } 696 697 int 698 wsmouseopen(dev_t dev, int flags, int mode, struct lwp *l) 699 { 700 struct wsmouse_softc *sc; 701 struct wseventvar *evar; 702 int error; 703 704 sc = device_lookup_private(&wsmouse_cd, minor(dev)); 705 if (sc == NULL) 706 return ENXIO; 707 708 #if NWSMUX > 0 709 DPRINTF(("wsmouseopen: %s mux=%p p=%p\n", device_xname(sc->sc_base.me_dv), 710 sc->sc_base.me_parent, l)); 711 #endif 712 713 if (sc->sc_dying) 714 return (EIO); 715 716 if ((flags & (FREAD | FWRITE)) == FWRITE) 717 return (0); /* always allow open for write 718 so ioctl() is possible. */ 719 720 if (sc->sc_base.me_evp != NULL) 721 return (EBUSY); 722 723 evar = &sc->sc_base.me_evar; 724 wsevent_init(evar, l->l_proc); 725 sc->sc_base.me_evp = evar; 726 727 error = wsmousedoopen(sc, evar); 728 if (error) { 729 DPRINTF(("wsmouseopen: %s open failed\n", 730 device_xname(sc->sc_base.me_dv))); 731 sc->sc_base.me_evp = NULL; 732 wsevent_fini(evar); 733 } 734 return (error); 735 } 736 737 int 738 wsmouseclose(dev_t dev, int flags, int mode, 739 struct lwp *l) 740 { 741 struct wsmouse_softc *sc = 742 device_lookup_private(&wsmouse_cd, minor(dev)); 743 struct wseventvar *evar = sc->sc_base.me_evp; 744 745 #if NWSMUX > 0 746 DPRINTF(("wsmouseclose: %s mux=%p p=%p\n", device_xname(sc->sc_base.me_dv), 747 sc->sc_base.me_parent, l)); 748 #endif 749 if (evar == NULL) { 750 /* not open for read */ 751 return (0); 752 } 753 754 sc->sc_base.me_evp = NULL; 755 (*sc->sc_accessops->disable)(sc->sc_accesscookie); 756 wsevent_fini(evar); 757 758 return (0); 759 } 760 761 int 762 wsmousedoopen(struct wsmouse_softc *sc, struct wseventvar *evp) 763 { 764 sc->sc_base.me_evp = evp; 765 sc->sc_x = INVALID_X; 766 sc->sc_y = INVALID_Y; 767 sc->sc_z = INVALID_Z; 768 sc->sc_w = INVALID_W; 769 770 /* Stop button repeating when messing with the device. */ 771 if (sc->sc_repeat_button != -1) { 772 KASSERT(sc->sc_repeat_button >= 0); 773 sc->sc_repeat_button = -1; 774 callout_stop(&sc->sc_repeat_callout); 775 } 776 777 /* enable the device, and punt if that's not possible */ 778 return (*sc->sc_accessops->enable)(sc->sc_accesscookie); 779 } 780 781 int 782 wsmouseread(dev_t dev, struct uio *uio, int flags) 783 { 784 struct wsmouse_softc *sc = 785 device_lookup_private(&wsmouse_cd, minor(dev)); 786 int error; 787 788 if (sc->sc_dying) 789 return (EIO); 790 791 KASSERTMSG(sc->sc_base.me_evp != NULL, "wsmouseread: evp == NULL\n"); 792 793 sc->sc_refcnt++; 794 error = wsevent_read(sc->sc_base.me_evp, uio, flags); 795 if (--sc->sc_refcnt < 0) { 796 wakeup(sc); 797 error = EIO; 798 } 799 return (error); 800 } 801 802 int 803 wsmouseioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 804 { 805 return (wsmousedoioctl(device_lookup(&wsmouse_cd, minor(dev)), 806 cmd, data, flag, l)); 807 } 808 809 /* A wrapper around the ioctl() workhorse to make reference counting easy. */ 810 int 811 wsmousedoioctl(device_t dv, u_long cmd, void *data, int flag, 812 struct lwp *l) 813 { 814 struct wsmouse_softc *sc = device_private(dv); 815 int error; 816 817 sc->sc_refcnt++; 818 error = wsmouse_do_ioctl(sc, cmd, data, flag, l); 819 if (--sc->sc_refcnt < 0) 820 wakeup(sc); 821 return (error); 822 } 823 824 int 825 wsmouse_do_ioctl(struct wsmouse_softc *sc, u_long cmd, void *data, 826 int flag, struct lwp *l) 827 { 828 int error; 829 struct wsmouse_repeat *wr; 830 831 if (sc->sc_dying) 832 return (EIO); 833 834 /* 835 * Try the generic ioctls that the wsmouse interface supports. 836 */ 837 switch (cmd) { 838 case FIONBIO: /* we will remove this someday (soon???) */ 839 return (0); 840 841 case FIOASYNC: 842 if (sc->sc_base.me_evp == NULL) 843 return (EINVAL); 844 sc->sc_base.me_evp->async = *(int *)data != 0; 845 return (0); 846 847 case FIOSETOWN: 848 if (sc->sc_base.me_evp == NULL) 849 return (EINVAL); 850 if (-*(int *)data != sc->sc_base.me_evp->io->p_pgid 851 && *(int *)data != sc->sc_base.me_evp->io->p_pid) 852 return (EPERM); 853 return (0); 854 855 case TIOCSPGRP: 856 if (sc->sc_base.me_evp == NULL) 857 return (EINVAL); 858 if (*(int *)data != sc->sc_base.me_evp->io->p_pgid) 859 return (EPERM); 860 return (0); 861 } 862 863 /* 864 * Try the wsmouse specific ioctls. 865 */ 866 switch (cmd) { 867 case WSMOUSEIO_GETREPEAT: 868 wr = (struct wsmouse_repeat *)data; 869 memcpy(wr, &sc->sc_repeat, sizeof(sc->sc_repeat)); 870 return 0; 871 872 case WSMOUSEIO_SETREPEAT: 873 if ((flag & FWRITE) == 0) 874 return EACCES; 875 876 /* Validate input data. */ 877 wr = (struct wsmouse_repeat *)data; 878 if (wr->wr_delay_first != 0 && 879 (wr->wr_delay_first < wr->wr_delay_decrement || 880 wr->wr_delay_first < wr->wr_delay_minimum || 881 wr->wr_delay_first < wr->wr_delay_minimum + 882 wr->wr_delay_decrement)) 883 return EINVAL; 884 885 /* Stop current repeating and set new data. */ 886 sc->sc_repeat_button = -1; 887 callout_stop(&sc->sc_repeat_callout); 888 memcpy(&sc->sc_repeat, wr, sizeof(sc->sc_repeat)); 889 890 return 0; 891 892 case WSMOUSEIO_SETVERSION: 893 return wsevent_setversion(sc->sc_base.me_evp, *(int *)data); 894 895 case WSMOUSEIO_GETPARAMS: 896 return wsmouse_handle_params(sc, 897 (struct wsmouse_parameters *)data, false); 898 899 case WSMOUSEIO_SETPARAMS: 900 if ((flag & FWRITE) == 0) 901 return EACCES; 902 return wsmouse_handle_params(sc, 903 (struct wsmouse_parameters *)data, true); 904 } 905 906 /* 907 * Try the mouse driver for WSMOUSEIO ioctls. It returns -1 908 * if it didn't recognize the request. 909 */ 910 error = (*sc->sc_accessops->ioctl)(sc->sc_accesscookie, cmd, 911 data, flag, l); 912 return (error); /* may be EPASSTHROUGH */ 913 } 914 915 int 916 wsmousepoll(dev_t dev, int events, struct lwp *l) 917 { 918 struct wsmouse_softc *sc = 919 device_lookup_private(&wsmouse_cd, minor(dev)); 920 921 if (sc->sc_base.me_evp == NULL) 922 return (POLLERR); 923 return (wsevent_poll(sc->sc_base.me_evp, events, l)); 924 } 925 926 int 927 wsmousekqfilter(dev_t dev, struct knote *kn) 928 { 929 struct wsmouse_softc *sc = 930 device_lookup_private(&wsmouse_cd, minor(dev)); 931 932 if (sc->sc_base.me_evp == NULL) 933 return (1); 934 return (wsevent_kqfilter(sc->sc_base.me_evp, kn)); 935 } 936 937 #if NWSMUX > 0 938 int 939 wsmouse_mux_open(struct wsevsrc *me, struct wseventvar *evp) 940 { 941 struct wsmouse_softc *sc = (struct wsmouse_softc *)me; 942 943 if (sc->sc_base.me_evp != NULL) 944 return (EBUSY); 945 946 return wsmousedoopen(sc, evp); 947 } 948 949 int 950 wsmouse_mux_close(struct wsevsrc *me) 951 { 952 struct wsmouse_softc *sc = (struct wsmouse_softc *)me; 953 954 sc->sc_base.me_evp = NULL; 955 (*sc->sc_accessops->disable)(sc->sc_accesscookie); 956 957 return (0); 958 } 959 960 int 961 wsmouse_add_mux(int unit, struct wsmux_softc *muxsc) 962 { 963 struct wsmouse_softc *sc; 964 965 sc = device_lookup_private(&wsmouse_cd, unit); 966 if (sc == NULL) 967 return ENXIO; 968 969 if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL) 970 return (EBUSY); 971 972 return (wsmux_attach_sc(muxsc, &sc->sc_base)); 973 } 974 #endif /* NWSMUX > 0 */ 975