1 /* $OpenBSD: wsmux.c,v 1.61 2025/01/22 15:06:56 mvs Exp $ */ 2 /* $NetBSD: wsmux.c,v 1.37 2005/04/30 03:47:12 augustss Exp $ */ 3 4 /* 5 * Copyright (c) 1998, 2005 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * Author: Lennart Augustsson <augustss@carlstedt.se> 9 * Carlstedt Research & Technology 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include "wsmux.h" 34 #include "wsdisplay.h" 35 #include "wskbd.h" 36 #include "wsmouse.h" 37 38 /* 39 * wscons mux device. 40 * 41 * The mux device is a collection of real mice and keyboards and acts as 42 * a merge point for all the events from the different real devices. 43 */ 44 45 #include <sys/param.h> 46 #include <sys/conf.h> 47 #include <sys/ioctl.h> 48 #include <sys/fcntl.h> 49 #include <sys/kernel.h> 50 #include <sys/malloc.h> 51 #include <sys/proc.h> 52 #include <sys/queue.h> 53 #include <sys/syslog.h> 54 #include <sys/systm.h> 55 #include <sys/tty.h> 56 #include <sys/signalvar.h> 57 #include <sys/device.h> 58 59 #include <dev/wscons/wsconsio.h> 60 #include <dev/wscons/wsksymdef.h> 61 #include <dev/wscons/wseventvar.h> 62 #include <dev/wscons/wsmuxvar.h> 63 64 #define WSMUX_MAXDEPTH 8 65 66 #ifdef WSMUX_DEBUG 67 #define DPRINTF(x) if (wsmuxdebug) printf x 68 #define DPRINTFN(n,x) if (wsmuxdebug > (n)) printf x 69 int wsmuxdebug = 0; 70 #else 71 #define DPRINTF(x) 72 #define DPRINTFN(n,x) 73 #endif 74 75 /* 76 * The wsmux pseudo device is used to multiplex events from several wsmouse, 77 * wskbd, and/or wsmux devices together. 78 * The devices connected together form a tree with muxes in the interior 79 * and real devices (mouse and kbd) at the leaves. The special case of 80 * a tree with one node (mux or other) is supported as well. 81 * Only the device at the root of the tree can be opened (if a non-root 82 * device is opened the subtree rooted at that point is severed from the 83 * containing tree). When the root is opened it allocates a wseventvar 84 * struct which all the nodes in the tree will send their events too. 85 * An ioctl() performed on the root is propagated to all the nodes. 86 * There are also ioctl() operations to add and remove nodes from a tree. 87 */ 88 89 int wsmux_mux_open(struct wsevsrc *, struct wseventvar *); 90 int wsmux_mux_close(struct wsevsrc *); 91 92 int wsmux_do_open(struct wsmux_softc *, struct wseventvar *); 93 94 void wsmux_do_close(struct wsmux_softc *); 95 #if NWSDISPLAY > 0 96 int wsmux_evsrc_set_display(struct device *, struct device *); 97 #else 98 #define wsmux_evsrc_set_display NULL 99 #endif 100 101 int wsmux_do_displayioctl(struct device *dev, u_long cmd, caddr_t data, 102 int flag, struct proc *p); 103 int wsmux_do_ioctl(struct device *, u_long, caddr_t,int,struct proc *); 104 105 int wsmux_add_mux(int, struct wsmux_softc *); 106 107 int wsmux_depth(struct wsmux_softc *); 108 109 void wsmuxattach(int); 110 111 void wsmux_detach_sc_locked(struct wsmux_softc *, struct wsevsrc *); 112 113 struct wssrcops wsmux_srcops = { 114 .type = WSMUX_MUX, 115 .dopen = wsmux_mux_open, 116 .dclose = wsmux_mux_close, 117 .dioctl = wsmux_do_ioctl, 118 .ddispioctl = wsmux_do_displayioctl, 119 .dsetdisplay = wsmux_evsrc_set_display, 120 }; 121 122 /* 123 * Lock used by wsmux_add_mux() to grant exclusive access to the tree of 124 * stacked wsmux devices. 125 */ 126 struct rwlock wsmux_tree_lock = RWLOCK_INITIALIZER("wsmuxtreelk"); 127 128 /* From upper level */ 129 void 130 wsmuxattach(int n) 131 { 132 } 133 134 /* Keep track of all muxes that have been allocated */ 135 int nwsmux = 0; 136 struct wsmux_softc **wsmuxdevs = NULL; 137 138 /* Return mux n, create if necessary */ 139 struct wsmux_softc * 140 wsmux_getmux(int n) 141 { 142 struct wsmux_softc *sc; 143 struct wsmux_softc **new, **old; 144 int i; 145 146 if (n < 0 || n >= WSMUX_MAXDEV) 147 return (NULL); 148 149 /* Make sure there is room for mux n in the table */ 150 if (n >= nwsmux) { 151 old = wsmuxdevs; 152 new = mallocarray(n + 1, sizeof (*wsmuxdevs), 153 M_DEVBUF, M_NOWAIT); 154 if (new == NULL) { 155 printf("wsmux_getmux: no memory for mux %d\n", n); 156 return (NULL); 157 } 158 if (old != NULL) 159 bcopy(old, new, nwsmux * sizeof(*wsmuxdevs)); 160 for (i = nwsmux; i < (n + 1); i++) 161 new[i] = NULL; 162 if (old != NULL) 163 free(old, M_DEVBUF, nwsmux * sizeof(*wsmuxdevs)); 164 wsmuxdevs = new; 165 nwsmux = n + 1; 166 } 167 168 sc = wsmuxdevs[n]; 169 if (sc == NULL) { 170 sc = wsmux_create("wsmux", n); 171 if (sc == NULL) 172 printf("wsmux: attach out of memory\n"); 173 wsmuxdevs[n] = sc; 174 } 175 return (sc); 176 } 177 178 /* 179 * open() of the pseudo device from device table. 180 */ 181 int 182 wsmuxopen(dev_t dev, int flags, int mode, struct proc *p) 183 { 184 struct wsmux_softc *sc; 185 struct wseventvar *evar; 186 int error, unit; 187 188 unit = minor(dev); 189 sc = wsmux_getmux(unit); 190 if (sc == NULL) 191 return (ENXIO); 192 193 DPRINTF(("%s: %s: sc=%p\n", __func__, sc->sc_base.me_dv.dv_xname, sc)); 194 195 if ((flags & (FREAD | FWRITE)) == FWRITE) { 196 /* Not opening for read, only ioctl is available. */ 197 return (0); 198 } 199 200 if (sc->sc_base.me_parent != NULL) { 201 /* Grab the mux out of the greedy hands of the parent mux. */ 202 DPRINTF(("%s: detach\n", __func__)); 203 wsmux_detach_sc(&sc->sc_base); 204 } 205 206 if (sc->sc_base.me_evp != NULL) 207 /* Already open. */ 208 return (EBUSY); 209 210 evar = &sc->sc_base.me_evar; 211 if (wsevent_init_flags(evar, WSEVENT_MPSAFE)) 212 return (EBUSY); 213 #ifdef WSDISPLAY_COMPAT_RAWKBD 214 sc->sc_rawkbd = 0; 215 #endif 216 217 error = wsmux_do_open(sc, evar); 218 if (error) 219 wsevent_fini(evar); 220 return (error); 221 } 222 223 /* 224 * Open of a mux via the parent mux. 225 */ 226 int 227 wsmux_mux_open(struct wsevsrc *me, struct wseventvar *evar) 228 { 229 struct wsmux_softc *sc = (struct wsmux_softc *)me; 230 231 #ifdef DIAGNOSTIC 232 if (sc->sc_base.me_parent == NULL) { 233 printf("wsmux_mux_open: no parent\n"); 234 return (EINVAL); 235 } 236 #endif 237 238 return (wsmux_do_open(sc, evar)); 239 } 240 241 /* Common part of opening a mux. */ 242 int 243 wsmux_do_open(struct wsmux_softc *sc, struct wseventvar *evar) 244 { 245 struct wsevsrc *me; 246 #ifdef DIAGNOSTIC 247 int error; 248 #endif 249 250 /* The device could already be attached to a mux. */ 251 if (sc->sc_base.me_evp != NULL) 252 return (EBUSY); 253 sc->sc_base.me_evp = evar; /* remember event variable, mark as open */ 254 255 /* Open all children. */ 256 rw_enter_read(&sc->sc_lock); 257 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 258 DPRINTF(("%s: %s: m=%p dev=%s\n", __func__, 259 sc->sc_base.me_dv.dv_xname, me, 260 me->me_dv.dv_xname)); 261 #ifdef DIAGNOSTIC 262 if (me->me_evp != NULL) { 263 printf("wsmuxopen: dev already in use\n"); 264 continue; 265 } 266 if (me->me_parent != sc) { 267 printf("wsmux_do_open: bad child=%p\n", me); 268 continue; 269 } 270 error = wsevsrc_open(me, evar); 271 if (error) { 272 DPRINTF(("%s: open failed %d\n", __func__, error)); 273 } 274 #else 275 /* ignore errors, failing children will not be marked open */ 276 (void)wsevsrc_open(me, evar); 277 #endif 278 } 279 rw_exit_read(&sc->sc_lock); 280 281 return (0); 282 } 283 284 /* 285 * close() of the pseudo device from device table. 286 */ 287 int 288 wsmuxclose(dev_t dev, int flags, int mode, struct proc *p) 289 { 290 struct wsmux_softc *sc = 291 (struct wsmux_softc *)wsmuxdevs[minor(dev)]; 292 struct wseventvar *evar = sc->sc_base.me_evp; 293 294 if ((flags & (FREAD | FWRITE)) == FWRITE) 295 /* Not open for read */ 296 return (0); 297 298 wsmux_do_close(sc); 299 sc->sc_base.me_evp = NULL; 300 wsevent_fini(evar); 301 return (0); 302 } 303 304 /* 305 * Close of a mux via the parent mux. 306 */ 307 int 308 wsmux_mux_close(struct wsevsrc *me) 309 { 310 struct wsmux_softc *sc = (struct wsmux_softc *)me; 311 312 wsmux_do_close(sc); 313 sc->sc_base.me_evp = NULL; 314 315 return (0); 316 } 317 318 /* Common part of closing a mux. */ 319 void 320 wsmux_do_close(struct wsmux_softc *sc) 321 { 322 struct wsevsrc *me; 323 324 DPRINTF(("%s: %s: sc=%p\n", __func__, sc->sc_base.me_dv.dv_xname, sc)); 325 326 /* Close all the children. */ 327 rw_enter_read(&sc->sc_lock); 328 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 329 DPRINTF(("%s %s: m=%p dev=%s\n", __func__, 330 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname)); 331 #ifdef DIAGNOSTIC 332 if (me->me_parent != sc) { 333 printf("wsmuxclose: bad child=%p\n", me); 334 continue; 335 } 336 #endif 337 (void)wsevsrc_close(me); 338 } 339 rw_exit_read(&sc->sc_lock); 340 } 341 342 /* 343 * read() of the pseudo device from device table. 344 */ 345 int 346 wsmuxread(dev_t dev, struct uio *uio, int flags) 347 { 348 struct wsmux_softc *sc = wsmuxdevs[minor(dev)]; 349 struct wseventvar *evar; 350 int error; 351 352 evar = sc->sc_base.me_evp; 353 if (evar == NULL) { 354 #ifdef DIAGNOSTIC 355 /* XXX can we get here? */ 356 printf("wsmuxread: not open\n"); 357 #endif 358 return (EINVAL); 359 } 360 361 DPRINTFN(5, ("%s: %s event read evar=%p\n", __func__, 362 sc->sc_base.me_dv.dv_xname, evar)); 363 error = wsevent_read(evar, uio, flags); 364 DPRINTFN(5, ("%s: %s event read ==> error=%d\n", __func__, 365 sc->sc_base.me_dv.dv_xname, error)); 366 return (error); 367 } 368 369 /* 370 * ioctl of the pseudo device from device table. 371 */ 372 int 373 wsmuxioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 374 { 375 return wsmux_do_ioctl(&wsmuxdevs[minor(dev)]->sc_base.me_dv, cmd, data, flag, p); 376 } 377 378 /* 379 * ioctl of a mux via the parent mux, continuation of wsmuxioctl(). 380 */ 381 int 382 wsmux_do_ioctl(struct device *dv, u_long cmd, caddr_t data, int flag, 383 struct proc *p) 384 { 385 struct wsmux_softc *sc = (struct wsmux_softc *)dv; 386 struct wsevsrc *me; 387 int error, ok; 388 int put, get, n; 389 struct wseventvar *evar; 390 struct wscons_event *ev; 391 struct wsmux_device_list *l; 392 393 DPRINTF(("%s: %s: enter sc=%p, cmd=%08lx\n", __func__, 394 sc->sc_base.me_dv.dv_xname, sc, cmd)); 395 396 switch (cmd) { 397 case WSMUXIO_INJECTEVENT: 398 case WSMUXIO_ADD_DEVICE: 399 case WSMUXIO_REMOVE_DEVICE: 400 #ifdef WSDISPLAY_COMPAT_RAWKBD 401 case WSKBDIO_SETMODE: 402 #endif 403 if ((flag & FWRITE) == 0) 404 return (EACCES); 405 } 406 407 switch (cmd) { 408 case WSMUXIO_INJECTEVENT: 409 /* Inject an event, e.g., from moused. */ 410 DPRINTF(("%s: inject\n", sc->sc_base.me_dv.dv_xname)); 411 evar = sc->sc_base.me_evp; 412 if (evar == NULL) { 413 /* No event sink, so ignore it. */ 414 DPRINTF(("%s: event ignored\n", __func__)); 415 return (0); 416 } 417 418 mtx_enter(&evar->ws_mtx); 419 get = evar->ws_get; 420 put = evar->ws_put; 421 ev = &evar->ws_q[put]; 422 if (++put % WSEVENT_QSIZE == get) { 423 mtx_leave(&evar->ws_mtx); 424 return (ENOSPC); 425 } 426 if (put >= WSEVENT_QSIZE) 427 put = 0; 428 *ev = *(struct wscons_event *)data; 429 nanotime(&ev->time); 430 evar->ws_put = put; 431 mtx_leave(&evar->ws_mtx); 432 wsevent_wakeup(evar); 433 return (0); 434 case WSMUXIO_ADD_DEVICE: 435 #define d ((struct wsmux_device *)data) 436 DPRINTF(("%s: add type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname, 437 d->type, d->idx)); 438 if (d->idx < 0) 439 return (ENXIO); 440 switch (d->type) { 441 #if NWSMOUSE > 0 442 case WSMUX_MOUSE: 443 return (wsmouse_add_mux(d->idx, sc)); 444 #endif 445 #if NWSKBD > 0 446 case WSMUX_KBD: 447 return (wskbd_add_mux(d->idx, sc)); 448 #endif 449 case WSMUX_MUX: 450 return (wsmux_add_mux(d->idx, sc)); 451 default: 452 return (EINVAL); 453 } 454 case WSMUXIO_REMOVE_DEVICE: 455 DPRINTF(("%s: rem type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname, 456 d->type, d->idx)); 457 /* Locate the device */ 458 rw_enter_write(&sc->sc_lock); 459 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 460 if (me->me_ops->type == d->type && 461 me->me_dv.dv_unit == d->idx) { 462 DPRINTF(("%s: detach\n", __func__)); 463 wsmux_detach_sc_locked(sc, me); 464 rw_exit_write(&sc->sc_lock); 465 return (0); 466 } 467 } 468 rw_exit_write(&sc->sc_lock); 469 return (EINVAL); 470 #undef d 471 472 case WSMUXIO_LIST_DEVICES: 473 DPRINTF(("%s: list\n", sc->sc_base.me_dv.dv_xname)); 474 l = (struct wsmux_device_list *)data; 475 n = 0; 476 rw_enter_read(&sc->sc_lock); 477 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 478 if (n >= WSMUX_MAXDEV) 479 break; 480 l->devices[n].type = me->me_ops->type; 481 l->devices[n].idx = me->me_dv.dv_unit; 482 n++; 483 } 484 rw_exit_read(&sc->sc_lock); 485 l->ndevices = n; 486 return (0); 487 #ifdef WSDISPLAY_COMPAT_RAWKBD 488 case WSKBDIO_SETMODE: 489 sc->sc_rawkbd = *(int *)data; 490 DPRINTF(("%s: save rawkbd = %d\n", __func__, sc->sc_rawkbd)); 491 break; 492 #endif 493 case FIOASYNC: 494 DPRINTF(("%s: FIOASYNC\n", sc->sc_base.me_dv.dv_xname)); 495 evar = sc->sc_base.me_evp; 496 if (evar == NULL) 497 return (EINVAL); 498 mtx_enter(&evar->ws_mtx); 499 evar->ws_async = *(int *)data != 0; 500 mtx_leave(&evar->ws_mtx); 501 return (0); 502 case FIOGETOWN: 503 case TIOCGPGRP: 504 DPRINTF(("%s: getown (%lu)\n", sc->sc_base.me_dv.dv_xname, 505 cmd)); 506 evar = sc->sc_base.me_evp; 507 if (evar == NULL) 508 return (EINVAL); 509 sigio_getown(&evar->ws_sigio, cmd, data); 510 return (0); 511 case FIOSETOWN: 512 case TIOCSPGRP: 513 DPRINTF(("%s: setown (%lu)\n", sc->sc_base.me_dv.dv_xname, 514 cmd)); 515 evar = sc->sc_base.me_evp; 516 if (evar == NULL) 517 return (EINVAL); 518 return (sigio_setown(&evar->ws_sigio, cmd, data)); 519 default: 520 DPRINTF(("%s: unknown\n", sc->sc_base.me_dv.dv_xname)); 521 break; 522 } 523 524 if (sc->sc_base.me_evp == NULL 525 #if NWSDISPLAY > 0 526 && sc->sc_displaydv == NULL 527 #endif 528 ) 529 return (EACCES); 530 531 /* 532 * If children are attached: return 0 if any of the ioctl() succeeds, 533 * otherwise the last error. 534 */ 535 error = ENOTTY; 536 ok = 0; 537 rw_enter_read(&sc->sc_lock); 538 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 539 #ifdef DIAGNOSTIC 540 /* XXX check evp? */ 541 if (me->me_parent != sc) { 542 printf("wsmux_do_ioctl: bad child %p\n", me); 543 continue; 544 } 545 #endif 546 error = wsevsrc_ioctl(me, cmd, data, flag, p); 547 DPRINTF(("%s: %s: me=%p dev=%s ==> %d\n", __func__, 548 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname, 549 error)); 550 if (!error) 551 ok = 1; 552 } 553 rw_exit_read(&sc->sc_lock); 554 if (ok) 555 error = 0; 556 557 return (error); 558 } 559 560 int 561 wsmuxkqfilter(dev_t dev, struct knote *kn) 562 { 563 struct wsmux_softc *sc = wsmuxdevs[minor(dev)]; 564 565 if (sc->sc_base.me_evp == NULL) 566 return (ENXIO); 567 return (wsevent_kqfilter(sc->sc_base.me_evp, kn)); 568 } 569 570 /* 571 * Add mux unit as a child to muxsc. 572 */ 573 int 574 wsmux_add_mux(int unit, struct wsmux_softc *muxsc) 575 { 576 struct wsmux_softc *sc, *m; 577 int error; 578 int depth = 0; 579 580 sc = wsmux_getmux(unit); 581 if (sc == NULL) 582 return (ENXIO); 583 584 rw_enter_write(&wsmux_tree_lock); 585 586 DPRINTF(("%s: %s(%p) to %s(%p)\n", __func__, 587 sc->sc_base.me_dv.dv_xname, sc, 588 muxsc->sc_base.me_dv.dv_xname, muxsc)); 589 590 if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL) { 591 error = EBUSY; 592 goto out; 593 } 594 595 /* The mux we are adding must not be an ancestor of itself. */ 596 for (m = muxsc; m != NULL; m = m->sc_base.me_parent) { 597 if (m == sc) { 598 error = EINVAL; 599 goto out; 600 } 601 depth++; 602 } 603 604 /* 605 * Limit the number of stacked wsmux devices to avoid exhausting 606 * the kernel stack during wsmux_do_open(). 607 */ 608 if (depth + wsmux_depth(sc) > WSMUX_MAXDEPTH) { 609 error = EBUSY; 610 goto out; 611 } 612 613 error = wsmux_attach_sc(muxsc, &sc->sc_base); 614 out: 615 rw_exit_write(&wsmux_tree_lock); 616 return (error); 617 } 618 619 /* Create a new mux softc. */ 620 struct wsmux_softc * 621 wsmux_create(const char *name, int unit) 622 { 623 struct wsmux_softc *sc; 624 625 DPRINTF(("%s: allocating\n", __func__)); 626 sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT | M_ZERO); 627 if (sc == NULL) 628 return (NULL); 629 TAILQ_INIT(&sc->sc_cld); 630 rw_init_flags(&sc->sc_lock, "wsmuxlk", RWL_DUPOK); 631 snprintf(sc->sc_base.me_dv.dv_xname, sizeof sc->sc_base.me_dv.dv_xname, 632 "%s%d", name, unit); 633 sc->sc_base.me_dv.dv_unit = unit; 634 sc->sc_base.me_ops = &wsmux_srcops; 635 sc->sc_kbd_layout = KB_NONE; 636 return (sc); 637 } 638 639 /* Attach me as a child to sc. */ 640 int 641 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me) 642 { 643 int error; 644 645 if (sc == NULL) 646 return (EINVAL); 647 648 rw_enter_write(&sc->sc_lock); 649 650 DPRINTF(("%s: %s(%p): type=%d\n", __func__, 651 sc->sc_base.me_dv.dv_xname, sc, me->me_ops->type)); 652 653 #ifdef DIAGNOSTIC 654 if (me->me_parent != NULL) { 655 rw_exit_write(&sc->sc_lock); 656 printf("wsmux_attach_sc: busy\n"); 657 return (EBUSY); 658 } 659 #endif 660 me->me_parent = sc; 661 TAILQ_INSERT_TAIL(&sc->sc_cld, me, me_next); 662 663 error = 0; 664 #if NWSDISPLAY > 0 665 if (sc->sc_displaydv != NULL) { 666 /* This is a display mux, so attach the new device to it. */ 667 DPRINTF(("%s: %s: set display %p\n", __func__, 668 sc->sc_base.me_dv.dv_xname, sc->sc_displaydv)); 669 if (me->me_ops->dsetdisplay != NULL) { 670 error = wsevsrc_set_display(me, sc->sc_displaydv); 671 /* Ignore that the console already has a display. */ 672 if (error == EBUSY) 673 error = 0; 674 if (!error) { 675 #ifdef WSDISPLAY_COMPAT_RAWKBD 676 DPRINTF(("%s: %s set rawkbd=%d\n", __func__, 677 me->me_dv.dv_xname, sc->sc_rawkbd)); 678 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE, 679 &sc->sc_rawkbd, FWRITE, 0); 680 #endif 681 } 682 } 683 } 684 #endif 685 if (sc->sc_base.me_evp != NULL) { 686 /* Mux is open, try to open the subdevice. */ 687 error = wsevsrc_open(me, sc->sc_base.me_evp); 688 } else { 689 /* Mux is closed, ensure that the subdevice is also closed. */ 690 if (me->me_evp != NULL) 691 error = EBUSY; 692 } 693 694 if (error) { 695 me->me_parent = NULL; 696 TAILQ_REMOVE(&sc->sc_cld, me, me_next); 697 } 698 699 rw_exit_write(&sc->sc_lock); 700 701 DPRINTF(("%s: %s(%p) done, error=%d\n", __func__, 702 sc->sc_base.me_dv.dv_xname, sc, error)); 703 return (error); 704 } 705 706 /* Remove me from the parent. */ 707 void 708 wsmux_detach_sc(struct wsevsrc *me) 709 { 710 struct wsmux_softc *sc = me->me_parent; 711 712 if (sc == NULL) { 713 printf("wsmux_detach_sc: %s has no parent\n", 714 me->me_dv.dv_xname); 715 return; 716 } 717 718 rw_enter_write(&sc->sc_lock); 719 wsmux_detach_sc_locked(sc, me); 720 rw_exit_write(&sc->sc_lock); 721 } 722 723 void 724 wsmux_detach_sc_locked(struct wsmux_softc *sc, struct wsevsrc *me) 725 { 726 rw_assert_wrlock(&sc->sc_lock); 727 728 DPRINTF(("%s: %s(%p) parent=%p\n", __func__, 729 me->me_dv.dv_xname, me, sc)); 730 731 if (me->me_parent != sc) { 732 /* Device detached or attached to another mux while sleeping. */ 733 return; 734 } 735 736 #if NWSDISPLAY > 0 737 if (sc->sc_displaydv != NULL) { 738 if (me->me_ops->dsetdisplay != NULL) 739 /* ignore error, there's nothing we can do */ 740 (void)wsevsrc_set_display(me, NULL); 741 } else 742 #endif 743 if (me->me_evp != NULL) { 744 DPRINTF(("%s: close\n", __func__)); 745 /* mux device is open, so close multiplexee */ 746 (void)wsevsrc_close(me); 747 } 748 749 TAILQ_REMOVE(&sc->sc_cld, me, me_next); 750 me->me_parent = NULL; 751 752 DPRINTF(("%s: done sc=%p\n", __func__, sc)); 753 } 754 755 /* 756 * Display ioctl() of a mux via the parent mux. 757 */ 758 int 759 wsmux_do_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag, 760 struct proc *p) 761 { 762 struct wsmux_softc *sc = (struct wsmux_softc *)dv; 763 struct wsevsrc *me; 764 int error, ok; 765 766 DPRINTF(("%s: %s: sc=%p, cmd=%08lx\n", __func__, 767 sc->sc_base.me_dv.dv_xname, sc, cmd)); 768 769 #ifdef WSDISPLAY_COMPAT_RAWKBD 770 if (cmd == WSKBDIO_SETMODE) { 771 sc->sc_rawkbd = *(int *)data; 772 DPRINTF(("%s: rawkbd = %d\n", __func__, sc->sc_rawkbd)); 773 } 774 #endif 775 776 /* 777 * Return 0 if any of the ioctl() succeeds, otherwise the last error. 778 * Return -1 if no mux component accepts the ioctl. 779 */ 780 error = -1; 781 ok = 0; 782 rw_enter_read(&sc->sc_lock); 783 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 784 DPRINTF(("%s: me=%p\n", __func__, me)); 785 #ifdef DIAGNOSTIC 786 if (me->me_parent != sc) { 787 printf("wsmux_displayioctl: bad child %p\n", me); 788 continue; 789 } 790 #endif 791 if (me->me_ops->ddispioctl != NULL) { 792 error = wsevsrc_display_ioctl(me, cmd, data, flag, p); 793 DPRINTF(("%s: me=%p dev=%s ==> %d\n", __func__, 794 me, me->me_dv.dv_xname, error)); 795 if (!error) 796 ok = 1; 797 } 798 } 799 rw_exit_read(&sc->sc_lock); 800 if (ok) 801 error = 0; 802 803 return (error); 804 } 805 806 #if NWSDISPLAY > 0 807 /* 808 * Set display of a mux via the parent mux. 809 */ 810 int 811 wsmux_evsrc_set_display(struct device *dv, struct device *displaydv) 812 { 813 struct wsmux_softc *sc = (struct wsmux_softc *)dv; 814 815 DPRINTF(("%s: %s: displaydv=%p\n", __func__, 816 sc->sc_base.me_dv.dv_xname, displaydv)); 817 818 if (displaydv != NULL) { 819 if (sc->sc_displaydv != NULL) 820 return (EBUSY); 821 } else { 822 if (sc->sc_displaydv == NULL) 823 return (ENXIO); 824 } 825 826 return wsmux_set_display(sc, displaydv); 827 } 828 829 int 830 wsmux_set_display(struct wsmux_softc *sc, struct device *displaydv) 831 { 832 struct device *odisplaydv; 833 struct wsevsrc *me; 834 struct wsmux_softc *nsc = displaydv ? sc : NULL; 835 int error, ok; 836 837 rw_enter_read(&sc->sc_lock); 838 839 odisplaydv = sc->sc_displaydv; 840 sc->sc_displaydv = displaydv; 841 842 if (displaydv) { 843 DPRINTF(("%s: connecting to %s\n", 844 sc->sc_base.me_dv.dv_xname, displaydv->dv_xname)); 845 } 846 ok = 0; 847 error = 0; 848 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 849 #ifdef DIAGNOSTIC 850 if (me->me_parent != sc) { 851 printf("wsmux_set_display: bad child parent %p\n", me); 852 continue; 853 } 854 #endif 855 if (me->me_ops->dsetdisplay != NULL) { 856 error = wsevsrc_set_display(me, 857 nsc ? nsc->sc_displaydv : NULL); 858 DPRINTF(("%s: m=%p dev=%s error=%d\n", __func__, 859 me, me->me_dv.dv_xname, error)); 860 if (!error) { 861 ok = 1; 862 #ifdef WSDISPLAY_COMPAT_RAWKBD 863 DPRINTF(("%s: %s set rawkbd=%d\n", __func__, 864 me->me_dv.dv_xname, sc->sc_rawkbd)); 865 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE, 866 &sc->sc_rawkbd, FWRITE, 0); 867 #endif 868 } 869 } 870 } 871 if (ok) 872 error = 0; 873 874 if (displaydv == NULL) { 875 DPRINTF(("%s: disconnecting from %s\n", 876 sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname)); 877 } 878 879 rw_exit_read(&sc->sc_lock); 880 881 return (error); 882 } 883 #endif /* NWSDISPLAY > 0 */ 884 885 uint32_t 886 wsmux_get_layout(struct wsmux_softc *sc) 887 { 888 return sc->sc_kbd_layout; 889 } 890 891 void 892 wsmux_set_layout(struct wsmux_softc *sc, uint32_t layout) 893 { 894 if ((layout & (KB_DEFAULT | KB_NOENCODING)) == 0) 895 sc->sc_kbd_layout = layout; 896 } 897 898 /* 899 * Returns the depth of the longest chain of nested wsmux devices starting 900 * from sc. 901 */ 902 int 903 wsmux_depth(struct wsmux_softc *sc) 904 { 905 struct wsevsrc *me; 906 int depth; 907 int maxdepth = 0; 908 909 rw_assert_anylock(&wsmux_tree_lock); 910 911 rw_enter_read(&sc->sc_lock); 912 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 913 if (me->me_ops->type != WSMUX_MUX) 914 continue; 915 916 depth = wsmux_depth((struct wsmux_softc *)me); 917 if (depth > maxdepth) 918 maxdepth = depth; 919 } 920 rw_exit_read(&sc->sc_lock); 921 922 return (maxdepth + 1); 923 } 924