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