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