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