1 /* $OpenBSD: wsmux.c,v 1.49 2020/01/08 16:27:41 visa 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 void 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 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 wsmux_do_open(sc, evar); 219 220 return (0); 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_evp != NULL) { 233 printf("wsmux_mux_open: busy\n"); 234 return (EBUSY); 235 } 236 if (sc->sc_base.me_parent == NULL) { 237 printf("wsmux_mux_open: no parent\n"); 238 return (EINVAL); 239 } 240 #endif 241 242 wsmux_do_open(sc, evar); 243 244 return (0); 245 } 246 247 /* Common part of opening a mux. */ 248 void 249 wsmux_do_open(struct wsmux_softc *sc, struct wseventvar *evar) 250 { 251 struct wsevsrc *me; 252 #ifdef DIAGNOSTIC 253 int error; 254 #endif 255 256 sc->sc_base.me_evp = evar; /* remember event variable, mark as open */ 257 258 /* Open all children. */ 259 rw_enter_read(&sc->sc_lock); 260 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 261 DPRINTF(("%s: %s: m=%p dev=%s\n", __func__, 262 sc->sc_base.me_dv.dv_xname, me, 263 me->me_dv.dv_xname)); 264 #ifdef DIAGNOSTIC 265 if (me->me_evp != NULL) { 266 printf("wsmuxopen: dev already in use\n"); 267 continue; 268 } 269 if (me->me_parent != sc) { 270 printf("wsmux_do_open: bad child=%p\n", me); 271 continue; 272 } 273 error = wsevsrc_open(me, evar); 274 if (error) { 275 DPRINTF(("%s: open failed %d\n", __func__, error)); 276 } 277 #else 278 /* ignore errors, failing children will not be marked open */ 279 (void)wsevsrc_open(me, evar); 280 #endif 281 } 282 rw_exit_read(&sc->sc_lock); 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 wsmux_do_close((struct wsmux_softc *)me); 312 me->me_evp = NULL; 313 return (0); 314 } 315 316 /* Common part of closing a mux. */ 317 void 318 wsmux_do_close(struct wsmux_softc *sc) 319 { 320 struct wsevsrc *me; 321 322 DPRINTF(("%s: %s: sc=%p\n", __func__, sc->sc_base.me_dv.dv_xname, sc)); 323 324 /* Close all the children. */ 325 rw_enter_read(&sc->sc_lock); 326 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 327 DPRINTF(("%s %s: m=%p dev=%s\n", __func__, 328 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname)); 329 #ifdef DIAGNOSTIC 330 if (me->me_parent != sc) { 331 printf("wsmuxclose: bad child=%p\n", me); 332 continue; 333 } 334 #endif 335 (void)wsevsrc_close(me); 336 me->me_evp = NULL; 337 } 338 rw_exit_read(&sc->sc_lock); 339 } 340 341 /* 342 * read() of the pseudo device from device table. 343 */ 344 int 345 wsmuxread(dev_t dev, struct uio *uio, int flags) 346 { 347 struct wsmux_softc *sc = wsmuxdevs[minor(dev)]; 348 struct wseventvar *evar; 349 int error; 350 351 evar = sc->sc_base.me_evp; 352 if (evar == NULL) { 353 #ifdef DIAGNOSTIC 354 /* XXX can we get here? */ 355 printf("wsmuxread: not open\n"); 356 #endif 357 return (EINVAL); 358 } 359 360 DPRINTFN(5, ("%s: %s event read evar=%p\n", __func__, 361 sc->sc_base.me_dv.dv_xname, evar)); 362 error = wsevent_read(evar, uio, flags); 363 DPRINTFN(5, ("%s: %s event read ==> error=%d\n", __func__, 364 sc->sc_base.me_dv.dv_xname, error)); 365 return (error); 366 } 367 368 /* 369 * ioctl of the pseudo device from device table. 370 */ 371 int 372 wsmuxioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 373 { 374 return wsmux_do_ioctl(&wsmuxdevs[minor(dev)]->sc_base.me_dv, cmd, data, flag, p); 375 } 376 377 /* 378 * ioctl of a mux via the parent mux, continuation of wsmuxioctl(). 379 */ 380 int 381 wsmux_do_ioctl(struct device *dv, u_long cmd, caddr_t data, int flag, 382 struct proc *p) 383 { 384 struct wsmux_softc *sc = (struct wsmux_softc *)dv; 385 struct wsevsrc *me; 386 int error, ok; 387 int s, put, get, n; 388 struct wseventvar *evar; 389 struct wscons_event *ev; 390 struct wsmux_device_list *l; 391 392 DPRINTF(("%s: %s: enter sc=%p, cmd=%08lx\n", __func__, 393 sc->sc_base.me_dv.dv_xname, sc, cmd)); 394 395 switch (cmd) { 396 case WSMUXIO_INJECTEVENT: 397 case WSMUXIO_ADD_DEVICE: 398 case WSMUXIO_REMOVE_DEVICE: 399 #ifdef WSDISPLAY_COMPAT_RAWKBD 400 case WSKBDIO_SETMODE: 401 #endif 402 if ((flag & FWRITE) == 0) 403 return (EACCES); 404 } 405 406 switch (cmd) { 407 case WSMUXIO_INJECTEVENT: 408 /* Inject an event, e.g., from moused. */ 409 DPRINTF(("%s: inject\n", sc->sc_base.me_dv.dv_xname)); 410 evar = sc->sc_base.me_evp; 411 if (evar == NULL) { 412 /* No event sink, so ignore it. */ 413 DPRINTF(("%s: event ignored\n", __func__)); 414 return (0); 415 } 416 417 s = spltty(); 418 get = evar->get; 419 put = evar->put; 420 ev = &evar->q[put]; 421 if (++put % WSEVENT_QSIZE == get) { 422 put--; 423 splx(s); 424 return (ENOSPC); 425 } 426 if (put >= WSEVENT_QSIZE) 427 put = 0; 428 *ev = *(struct wscons_event *)data; 429 nanotime(&ev->time); 430 evar->put = put; 431 WSEVENT_WAKEUP(evar); 432 splx(s); 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 FIONBIO: 494 DPRINTF(("%s: FIONBIO\n", sc->sc_base.me_dv.dv_xname)); 495 return (0); 496 497 case FIOASYNC: 498 DPRINTF(("%s: FIOASYNC\n", sc->sc_base.me_dv.dv_xname)); 499 evar = sc->sc_base.me_evp; 500 if (evar == NULL) 501 return (EINVAL); 502 evar->async = *(int *)data != 0; 503 return (0); 504 case FIOGETOWN: 505 case TIOCGPGRP: 506 DPRINTF(("%s: getown (%lu)\n", sc->sc_base.me_dv.dv_xname, 507 cmd)); 508 evar = sc->sc_base.me_evp; 509 if (evar == NULL) 510 return (EINVAL); 511 sigio_getown(&evar->sigio, cmd, data); 512 return (0); 513 case FIOSETOWN: 514 case TIOCSPGRP: 515 DPRINTF(("%s: setown (%lu)\n", sc->sc_base.me_dv.dv_xname, 516 cmd)); 517 evar = sc->sc_base.me_evp; 518 if (evar == NULL) 519 return (EINVAL); 520 return (sigio_setown(&evar->sigio, cmd, data)); 521 default: 522 DPRINTF(("%s: unknown\n", sc->sc_base.me_dv.dv_xname)); 523 break; 524 } 525 526 if (sc->sc_base.me_evp == NULL 527 #if NWSDISPLAY > 0 528 && sc->sc_displaydv == NULL 529 #endif 530 ) 531 return (EACCES); 532 533 /* 534 * If children are attached: return 0 if any of the ioctl() succeeds, 535 * otherwise the last error. 536 */ 537 error = ENOTTY; 538 ok = 0; 539 rw_enter_read(&sc->sc_lock); 540 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 541 #ifdef DIAGNOSTIC 542 /* XXX check evp? */ 543 if (me->me_parent != sc) { 544 printf("wsmux_do_ioctl: bad child %p\n", me); 545 continue; 546 } 547 #endif 548 error = wsevsrc_ioctl(me, cmd, data, flag, p); 549 DPRINTF(("%s: %s: me=%p dev=%s ==> %d\n", __func__, 550 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname, 551 error)); 552 if (!error) 553 ok = 1; 554 } 555 rw_exit_read(&sc->sc_lock); 556 if (ok) 557 error = 0; 558 559 return (error); 560 } 561 562 /* 563 * poll() of the pseudo device from device table. 564 */ 565 int 566 wsmuxpoll(dev_t dev, int events, struct proc *p) 567 { 568 struct wsmux_softc *sc = wsmuxdevs[minor(dev)]; 569 570 if (sc->sc_base.me_evp == NULL) { 571 #ifdef DIAGNOSTIC 572 printf("wsmuxpoll: not open\n"); 573 #endif 574 return (POLLERR); 575 } 576 577 return (wsevent_poll(sc->sc_base.me_evp, events, p)); 578 } 579 580 int 581 wsmuxkqfilter(dev_t dev, struct knote *kn) 582 { 583 struct wsmux_softc *sc = wsmuxdevs[minor(dev)]; 584 585 if (sc->sc_base.me_evp == NULL) 586 return (ENXIO); 587 return (wsevent_kqfilter(sc->sc_base.me_evp, kn)); 588 } 589 590 /* 591 * Add mux unit as a child to muxsc. 592 */ 593 int 594 wsmux_add_mux(int unit, struct wsmux_softc *muxsc) 595 { 596 struct wsmux_softc *sc, *m; 597 int error; 598 int depth = 0; 599 600 sc = wsmux_getmux(unit); 601 if (sc == NULL) 602 return (ENXIO); 603 604 rw_enter_write(&wsmux_tree_lock); 605 606 DPRINTF(("%s: %s(%p) to %s(%p)\n", __func__, 607 sc->sc_base.me_dv.dv_xname, sc, 608 muxsc->sc_base.me_dv.dv_xname, muxsc)); 609 610 if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL) { 611 error = EBUSY; 612 goto out; 613 } 614 615 /* The mux we are adding must not be an ancestor of itself. */ 616 for (m = muxsc; m != NULL; m = m->sc_base.me_parent) { 617 if (m == sc) { 618 error = EINVAL; 619 goto out; 620 } 621 depth++; 622 } 623 624 /* 625 * Limit the number of stacked wsmux devices to avoid exhausting 626 * the kernel stack during wsmux_do_open(). 627 */ 628 if (depth + wsmux_depth(sc) > WSMUX_MAXDEPTH) { 629 error = EBUSY; 630 goto out; 631 } 632 633 error = wsmux_attach_sc(muxsc, &sc->sc_base); 634 out: 635 rw_exit_write(&wsmux_tree_lock); 636 return (error); 637 } 638 639 /* Create a new mux softc. */ 640 struct wsmux_softc * 641 wsmux_create(const char *name, int unit) 642 { 643 struct wsmux_softc *sc; 644 645 DPRINTF(("%s: allocating\n", __func__)); 646 sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT | M_ZERO); 647 if (sc == NULL) 648 return (NULL); 649 TAILQ_INIT(&sc->sc_cld); 650 rw_init_flags(&sc->sc_lock, "wsmuxlk", RWL_DUPOK); 651 snprintf(sc->sc_base.me_dv.dv_xname, sizeof sc->sc_base.me_dv.dv_xname, 652 "%s%d", name, unit); 653 sc->sc_base.me_dv.dv_unit = unit; 654 sc->sc_base.me_ops = &wsmux_srcops; 655 sc->sc_kbd_layout = KB_NONE; 656 return (sc); 657 } 658 659 /* Attach me as a child to sc. */ 660 int 661 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me) 662 { 663 int error; 664 665 if (sc == NULL) 666 return (EINVAL); 667 668 rw_enter_write(&sc->sc_lock); 669 670 DPRINTF(("%s: %s(%p): type=%d\n", __func__, 671 sc->sc_base.me_dv.dv_xname, sc, me->me_ops->type)); 672 673 #ifdef DIAGNOSTIC 674 if (me->me_parent != NULL) { 675 rw_exit_write(&sc->sc_lock); 676 printf("wsmux_attach_sc: busy\n"); 677 return (EBUSY); 678 } 679 #endif 680 me->me_parent = sc; 681 TAILQ_INSERT_TAIL(&sc->sc_cld, me, me_next); 682 683 error = 0; 684 #if NWSDISPLAY > 0 685 if (sc->sc_displaydv != NULL) { 686 /* This is a display mux, so attach the new device to it. */ 687 DPRINTF(("%s: %s: set display %p\n", __func__, 688 sc->sc_base.me_dv.dv_xname, sc->sc_displaydv)); 689 if (me->me_ops->dsetdisplay != NULL) { 690 error = wsevsrc_set_display(me, sc->sc_displaydv); 691 /* Ignore that the console already has a display. */ 692 if (error == EBUSY) 693 error = 0; 694 if (!error) { 695 #ifdef WSDISPLAY_COMPAT_RAWKBD 696 DPRINTF(("%s: %s set rawkbd=%d\n", __func__, 697 me->me_dv.dv_xname, sc->sc_rawkbd)); 698 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE, 699 &sc->sc_rawkbd, FWRITE, 0); 700 #endif 701 } 702 } 703 } 704 #endif 705 if (sc->sc_base.me_evp != NULL) { 706 /* Mux is open, so open the new subdevice */ 707 DPRINTF(("%s: %s: calling open of %s\n", __func__, 708 sc->sc_base.me_dv.dv_xname, me->me_dv.dv_xname)); 709 error = wsevsrc_open(me, sc->sc_base.me_evp); 710 } else { 711 DPRINTF(("%s: %s not open\n", __func__, 712 sc->sc_base.me_dv.dv_xname)); 713 } 714 715 if (error) { 716 me->me_parent = NULL; 717 TAILQ_REMOVE(&sc->sc_cld, me, me_next); 718 } 719 720 rw_exit_write(&sc->sc_lock); 721 722 DPRINTF(("%s: %s(%p) done, error=%d\n", __func__, 723 sc->sc_base.me_dv.dv_xname, sc, error)); 724 return (error); 725 } 726 727 /* Remove me from the parent. */ 728 void 729 wsmux_detach_sc(struct wsevsrc *me) 730 { 731 struct wsmux_softc *sc = me->me_parent; 732 733 if (sc == NULL) { 734 printf("wsmux_detach_sc: %s has no parent\n", 735 me->me_dv.dv_xname); 736 return; 737 } 738 739 rw_enter_write(&sc->sc_lock); 740 wsmux_detach_sc_locked(sc, me); 741 rw_exit_write(&sc->sc_lock); 742 } 743 744 void 745 wsmux_detach_sc_locked(struct wsmux_softc *sc, struct wsevsrc *me) 746 { 747 rw_assert_wrlock(&sc->sc_lock); 748 749 DPRINTF(("%s: %s(%p) parent=%p\n", __func__, 750 me->me_dv.dv_xname, me, sc)); 751 752 if (me->me_parent != sc) { 753 /* Device detached or attached to another mux while sleeping. */ 754 return; 755 } 756 757 #if NWSDISPLAY > 0 758 if (sc->sc_displaydv != NULL) { 759 if (me->me_ops->dsetdisplay != NULL) 760 /* ignore error, there's nothing we can do */ 761 (void)wsevsrc_set_display(me, NULL); 762 } else 763 #endif 764 if (me->me_evp != NULL) { 765 DPRINTF(("%s: close\n", __func__)); 766 /* mux device is open, so close multiplexee */ 767 (void)wsevsrc_close(me); 768 } 769 770 TAILQ_REMOVE(&sc->sc_cld, me, me_next); 771 me->me_parent = NULL; 772 773 DPRINTF(("%s: done sc=%p\n", __func__, sc)); 774 } 775 776 /* 777 * Display ioctl() of a mux via the parent mux. 778 */ 779 int 780 wsmux_do_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag, 781 struct proc *p) 782 { 783 struct wsmux_softc *sc = (struct wsmux_softc *)dv; 784 struct wsevsrc *me; 785 int error, ok; 786 787 DPRINTF(("%s: %s: sc=%p, cmd=%08lx\n", __func__, 788 sc->sc_base.me_dv.dv_xname, sc, cmd)); 789 790 #ifdef WSDISPLAY_COMPAT_RAWKBD 791 if (cmd == WSKBDIO_SETMODE) { 792 sc->sc_rawkbd = *(int *)data; 793 DPRINTF(("%s: rawkbd = %d\n", __func__, sc->sc_rawkbd)); 794 } 795 #endif 796 797 /* 798 * Return 0 if any of the ioctl() succeeds, otherwise the last error. 799 * Return -1 if no mux component accepts the ioctl. 800 */ 801 error = -1; 802 ok = 0; 803 rw_enter_read(&sc->sc_lock); 804 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 805 DPRINTF(("%s: me=%p\n", __func__, me)); 806 #ifdef DIAGNOSTIC 807 if (me->me_parent != sc) { 808 printf("wsmux_displayioctl: bad child %p\n", me); 809 continue; 810 } 811 #endif 812 if (me->me_ops->ddispioctl != NULL) { 813 error = wsevsrc_display_ioctl(me, cmd, data, flag, p); 814 DPRINTF(("%s: me=%p dev=%s ==> %d\n", __func__, 815 me, me->me_dv.dv_xname, error)); 816 if (!error) 817 ok = 1; 818 } 819 } 820 rw_exit_read(&sc->sc_lock); 821 if (ok) 822 error = 0; 823 824 return (error); 825 } 826 827 #if NWSDISPLAY > 0 828 /* 829 * Set display of a mux via the parent mux. 830 */ 831 int 832 wsmux_evsrc_set_display(struct device *dv, struct device *displaydv) 833 { 834 struct wsmux_softc *sc = (struct wsmux_softc *)dv; 835 836 DPRINTF(("%s: %s: displaydv=%p\n", __func__, 837 sc->sc_base.me_dv.dv_xname, displaydv)); 838 839 if (displaydv != NULL) { 840 if (sc->sc_displaydv != NULL) 841 return (EBUSY); 842 } else { 843 if (sc->sc_displaydv == NULL) 844 return (ENXIO); 845 } 846 847 return wsmux_set_display(sc, displaydv); 848 } 849 850 int 851 wsmux_set_display(struct wsmux_softc *sc, struct device *displaydv) 852 { 853 struct device *odisplaydv; 854 struct wsevsrc *me; 855 struct wsmux_softc *nsc = displaydv ? sc : NULL; 856 int error, ok; 857 858 rw_enter_read(&sc->sc_lock); 859 860 odisplaydv = sc->sc_displaydv; 861 sc->sc_displaydv = displaydv; 862 863 if (displaydv) { 864 DPRINTF(("%s: connecting to %s\n", 865 sc->sc_base.me_dv.dv_xname, displaydv->dv_xname)); 866 } 867 ok = 0; 868 error = 0; 869 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 870 #ifdef DIAGNOSTIC 871 if (me->me_parent != sc) { 872 printf("wsmux_set_display: bad child parent %p\n", me); 873 continue; 874 } 875 #endif 876 if (me->me_ops->dsetdisplay != NULL) { 877 error = wsevsrc_set_display(me, 878 nsc ? nsc->sc_displaydv : NULL); 879 DPRINTF(("%s: m=%p dev=%s error=%d\n", __func__, 880 me, me->me_dv.dv_xname, error)); 881 if (!error) { 882 ok = 1; 883 #ifdef WSDISPLAY_COMPAT_RAWKBD 884 DPRINTF(("%s: %s set rawkbd=%d\n", __func__, 885 me->me_dv.dv_xname, sc->sc_rawkbd)); 886 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE, 887 &sc->sc_rawkbd, FWRITE, 0); 888 #endif 889 } 890 } 891 } 892 if (ok) 893 error = 0; 894 895 if (displaydv == NULL) { 896 DPRINTF(("%s: disconnecting from %s\n", 897 sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname)); 898 } 899 900 rw_exit_read(&sc->sc_lock); 901 902 return (error); 903 } 904 #endif /* NWSDISPLAY > 0 */ 905 906 uint32_t 907 wsmux_get_layout(struct wsmux_softc *sc) 908 { 909 return sc->sc_kbd_layout; 910 } 911 912 void 913 wsmux_set_layout(struct wsmux_softc *sc, uint32_t layout) 914 { 915 if ((layout & KB_DEFAULT) == 0) 916 sc->sc_kbd_layout = layout; 917 } 918 919 /* 920 * Returns the depth of the longest chain of nested wsmux devices starting 921 * from sc. 922 */ 923 int 924 wsmux_depth(struct wsmux_softc *sc) 925 { 926 struct wsevsrc *me; 927 int depth; 928 int maxdepth = 0; 929 930 rw_assert_anylock(&wsmux_tree_lock); 931 932 rw_enter_read(&sc->sc_lock); 933 TAILQ_FOREACH(me, &sc->sc_cld, me_next) { 934 if (me->me_ops->type != WSMUX_MUX) 935 continue; 936 937 depth = wsmux_depth((struct wsmux_softc *)me); 938 if (depth > maxdepth) 939 maxdepth = depth; 940 } 941 rw_exit_read(&sc->sc_lock); 942 943 return (maxdepth + 1); 944 } 945