1 /* $NetBSD: wsmux.c,v 1.49 2008/03/25 00:49:20 cube 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.49 2008/03/25 00:49:20 cube 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(device_t, struct wsevsrc *); 108 #else 109 #define wsmux_evsrc_set_display NULL 110 #endif 111 112 static int wsmux_do_displayioctl(device_t dev, u_long cmd, 113 void *data, int flag, struct lwp *l); 114 static int wsmux_do_ioctl(device_t, u_long, void *,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, D_OTHER 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", 207 device_xname(sc->sc_base.me_dv), 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 device_xname(sc->sc_base.me_dv), me, 276 device_xname(me->me_dv))); 277 #ifdef DIAGNOSTIC 278 if (me->me_evp != NULL) { 279 printf("wsmuxopen: dev already in use\n"); 280 continue; 281 } 282 if (me->me_parent != sc) { 283 printf("wsmux_do_open: bad child=%p\n", me); 284 continue; 285 } 286 { 287 int error = wsevsrc_open(me, evar); 288 if (error) { 289 DPRINTF(("wsmuxopen: open failed %d\n", error)); 290 } 291 } 292 #else 293 /* ignore errors, failing children will not be marked open */ 294 (void)wsevsrc_open(me, evar); 295 #endif 296 } 297 } 298 299 /* 300 * close() of the pseudo device from device table. 301 */ 302 int 303 wsmuxclose(dev_t dev, int flags, int mode, 304 struct lwp *l) 305 { 306 int minr = minor(dev); 307 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)]; 308 struct wseventvar *evar = sc->sc_base.me_evp; 309 310 if (WSMUXCTL(minr)) 311 /* control device */ 312 return (0); 313 if (evar == NULL) 314 /* Not open for read */ 315 return (0); 316 317 wsmux_do_close(sc); 318 sc->sc_base.me_evp = NULL; 319 wsevent_fini(evar); 320 return (0); 321 } 322 323 /* 324 * Close of a mux via the parent mux. 325 */ 326 int 327 wsmux_mux_close(struct wsevsrc *me) 328 { 329 me->me_evp = NULL; 330 wsmux_do_close((struct wsmux_softc *)me); 331 return (0); 332 } 333 334 /* Common part of closing a mux. */ 335 void 336 wsmux_do_close(struct wsmux_softc *sc) 337 { 338 struct wsevsrc *me; 339 340 DPRINTF(("wsmuxclose: %s: sc=%p\n", 341 device_xname(sc->sc_base.me_dv), sc)); 342 343 /* Close all the children. */ 344 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) { 345 DPRINTF(("wsmuxclose %s: m=%p dev=%s\n", 346 device_xname(sc->sc_base.me_dv), me, 347 device_xname(me->me_dv))); 348 #ifdef DIAGNOSTIC 349 if (me->me_parent != sc) { 350 printf("wsmuxclose: bad child=%p\n", me); 351 continue; 352 } 353 #endif 354 (void)wsevsrc_close(me); 355 me->me_evp = NULL; 356 } 357 } 358 359 /* 360 * read() of the pseudo device from device table. 361 */ 362 int 363 wsmuxread(dev_t dev, struct uio *uio, int flags) 364 { 365 int minr = minor(dev); 366 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)]; 367 struct wseventvar *evar; 368 int error; 369 370 if (WSMUXCTL(minr)) { 371 /* control device */ 372 return (EINVAL); 373 } 374 375 evar = sc->sc_base.me_evp; 376 if (evar == NULL) { 377 #ifdef DIAGNOSTIC 378 /* XXX can we get here? */ 379 printf("wsmuxread: not open\n"); 380 #endif 381 return (EINVAL); 382 } 383 384 DPRINTFN(5,("wsmuxread: %s event read evar=%p\n", 385 device_xname(sc->sc_base.me_dv), evar)); 386 error = wsevent_read(evar, uio, flags); 387 DPRINTFN(5,("wsmuxread: %s event read ==> error=%d\n", 388 device_xname(sc->sc_base.me_dv), error)); 389 return (error); 390 } 391 392 /* 393 * ioctl of the pseudo device from device table. 394 */ 395 int 396 wsmuxioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 397 { 398 int u = WSMUXDEV(minor(dev)); 399 400 return wsmux_do_ioctl(wsmuxdevs[u]->sc_base.me_dv, cmd, data, flag, l); 401 } 402 403 /* 404 * ioctl of a mux via the parent mux, continuation of wsmuxioctl(). 405 */ 406 int 407 wsmux_do_ioctl(device_t dv, u_long cmd, void *data, int flag, 408 struct lwp *lwp) 409 { 410 struct wsmux_softc *sc = device_private(dv); 411 struct wsevsrc *me; 412 int error, ok; 413 int s, n; 414 struct wseventvar *evar; 415 struct wscons_event event; 416 struct wsmux_device_list *l; 417 418 DPRINTF(("wsmux_do_ioctl: %s: enter sc=%p, cmd=%08lx\n", 419 device_xname(sc->sc_base.me_dv), sc, cmd)); 420 421 switch (cmd) { 422 case WSMUXIO_INJECTEVENT: 423 /* Inject an event, e.g., from moused. */ 424 DPRINTF(("%s: inject\n", device_xname(sc->sc_base.me_dv))); 425 426 evar = sc->sc_base.me_evp; 427 if (evar == NULL) { 428 /* No event sink, so ignore it. */ 429 DPRINTF(("wsmux_do_ioctl: event ignored\n")); 430 return (0); 431 } 432 433 s = spltty(); 434 event.type = ((struct wscons_event *)data)->type; 435 event.value = ((struct wscons_event *)data)->value; 436 error = wsevent_inject(evar, &event, 1); 437 splx(s); 438 439 return error; 440 case WSMUXIO_ADD_DEVICE: 441 #define d ((struct wsmux_device *)data) 442 DPRINTF(("%s: add type=%d, no=%d\n", 443 device_xname(sc->sc_base.me_dv), d->type, d->idx)); 444 switch (d->type) { 445 #if NWSMOUSE > 0 446 case WSMUX_MOUSE: 447 return (wsmouse_add_mux(d->idx, sc)); 448 #endif 449 #if NWSKBD > 0 450 case WSMUX_KBD: 451 return (wskbd_add_mux(d->idx, sc)); 452 #endif 453 case WSMUX_MUX: 454 return (wsmux_add_mux(d->idx, sc)); 455 default: 456 return (EINVAL); 457 } 458 case WSMUXIO_REMOVE_DEVICE: 459 DPRINTF(("%s: rem type=%d, no=%d\n", 460 device_xname(sc->sc_base.me_dv), d->type, d->idx)); 461 /* Locate the device */ 462 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) { 463 if (me->me_ops->type == d->type && 464 device_unit(me->me_dv) == d->idx) { 465 DPRINTF(("wsmux_do_ioctl: detach\n")); 466 wsmux_detach_sc(me); 467 return (0); 468 } 469 } 470 return (EINVAL); 471 #undef d 472 473 case WSMUXIO_LIST_DEVICES: 474 DPRINTF(("%s: list\n", device_xname(sc->sc_base.me_dv))); 475 l = (struct wsmux_device_list *)data; 476 n = 0; 477 CIRCLEQ_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 = device_unit(me->me_dv); 482 n++; 483 } 484 l->ndevices = n; 485 return (0); 486 #ifdef WSDISPLAY_COMPAT_RAWKBD 487 case WSKBDIO_SETMODE: 488 sc->sc_rawkbd = *(int *)data; 489 DPRINTF(("wsmux_do_ioctl: save rawkbd = %d\n", sc->sc_rawkbd)); 490 break; 491 #endif 492 case FIONBIO: 493 DPRINTF(("%s: FIONBIO\n", device_xname(sc->sc_base.me_dv))); 494 return (0); 495 496 case FIOASYNC: 497 DPRINTF(("%s: FIOASYNC\n", device_xname(sc->sc_base.me_dv))); 498 evar = sc->sc_base.me_evp; 499 if (evar == NULL) 500 return (EINVAL); 501 evar->async = *(int *)data != 0; 502 return (0); 503 case FIOSETOWN: 504 DPRINTF(("%s: FIOSETOWN\n", device_xname(sc->sc_base.me_dv))); 505 evar = sc->sc_base.me_evp; 506 if (evar == NULL) 507 return (EINVAL); 508 if (-*(int *)data != evar->io->p_pgid 509 && *(int *)data != evar->io->p_pid) 510 return (EPERM); 511 return (0); 512 case TIOCSPGRP: 513 DPRINTF(("%s: TIOCSPGRP\n", device_xname(sc->sc_base.me_dv))); 514 evar = sc->sc_base.me_evp; 515 if (evar == NULL) 516 return (EINVAL); 517 if (*(int *)data != evar->io->p_pgid) 518 return (EPERM); 519 return (0); 520 default: 521 DPRINTF(("%s: unknown\n", device_xname(sc->sc_base.me_dv))); 522 break; 523 } 524 525 if (sc->sc_base.me_evp == NULL 526 #if NWSDISPLAY > 0 527 && sc->sc_base.me_dispdv == NULL 528 #endif 529 ) 530 return (EACCES); 531 532 /* Return 0 if any of the ioctl() succeeds, otherwise the last error */ 533 error = 0; 534 ok = 0; 535 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) { 536 #ifdef DIAGNOSTIC 537 /* XXX check evp? */ 538 if (me->me_parent != sc) { 539 printf("wsmux_do_ioctl: bad child %p\n", me); 540 continue; 541 } 542 #endif 543 error = wsevsrc_ioctl(me, cmd, data, flag, lwp); 544 DPRINTF(("wsmux_do_ioctl: %s: me=%p dev=%s ==> %d\n", 545 device_xname(sc->sc_base.me_dv), me, 546 device_xname(me->me_dv), error)); 547 if (!error) 548 ok = 1; 549 } 550 if (ok) { 551 error = 0; 552 if (cmd == WSKBDIO_SETENCODING) { 553 sc->sc_kbd_layout = *((kbd_t *)data); 554 } 555 556 } 557 558 return (error); 559 } 560 561 /* 562 * poll() of the pseudo device from device table. 563 */ 564 int 565 wsmuxpoll(dev_t dev, int events, struct lwp *l) 566 { 567 int minr = minor(dev); 568 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)]; 569 570 if (WSMUXCTL(minr)) { 571 /* control device */ 572 return (0); 573 } 574 575 if (sc->sc_base.me_evp == NULL) { 576 #ifdef DIAGNOSTIC 577 printf("wsmuxpoll: not open\n"); 578 #endif 579 return (POLLHUP); 580 } 581 582 return (wsevent_poll(sc->sc_base.me_evp, events, l)); 583 } 584 585 /* 586 * kqfilter() of the pseudo device from device table. 587 */ 588 int 589 wsmuxkqfilter(dev_t dev, struct knote *kn) 590 { 591 int minr = minor(dev); 592 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)]; 593 594 if (WSMUXCTL(minr)) { 595 /* control device */ 596 return (1); 597 } 598 599 if (sc->sc_base.me_evp == NULL) { 600 #ifdef DIAGNOSTIC 601 printf("wsmuxkqfilter: not open\n"); 602 #endif 603 return (1); 604 } 605 606 return (wsevent_kqfilter(sc->sc_base.me_evp, kn)); 607 } 608 609 /* 610 * Add mux unit as a child to muxsc. 611 */ 612 int 613 wsmux_add_mux(int unit, struct wsmux_softc *muxsc) 614 { 615 struct wsmux_softc *sc, *m; 616 617 sc = wsmux_getmux(unit); 618 if (sc == NULL) 619 return (ENXIO); 620 621 DPRINTF(("wsmux_add_mux: %s(%p) to %s(%p)\n", 622 device_xname(sc->sc_base.me_dv), sc, 623 device_xname(muxsc->sc_base.me_dv), muxsc)); 624 625 if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL) 626 return (EBUSY); 627 628 /* The mux we are adding must not be an ancestor of itself. */ 629 for (m = muxsc; m != NULL ; m = m->sc_base.me_parent) 630 if (m == sc) 631 return (EINVAL); 632 633 return (wsmux_attach_sc(muxsc, &sc->sc_base)); 634 } 635 636 /* Create a new mux softc. */ 637 struct wsmux_softc * 638 wsmux_create(const char *name, int unit) 639 { 640 struct wsmux_softc *sc; 641 642 /* XXX This is wrong -- should use autoconfiguraiton framework */ 643 644 DPRINTF(("wsmux_create: allocating\n")); 645 sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT|M_ZERO); 646 if (sc == NULL) 647 return (NULL); 648 sc->sc_base.me_dv = malloc(sizeof(struct device), M_DEVBUF, M_NOWAIT|M_ZERO); 649 if (sc->sc_base.me_dv == NULL) { 650 free(sc, M_DEVBUF); 651 return NULL; 652 } 653 CIRCLEQ_INIT(&sc->sc_cld); 654 snprintf(sc->sc_base.me_dv->dv_xname, sizeof sc->sc_base.me_dv->dv_xname, 655 "%s%d", name, unit); 656 sc->sc_base.me_dv->dv_private = sc; 657 sc->sc_base.me_dv->dv_unit = unit; 658 sc->sc_base.me_ops = &wsmux_srcops; 659 sc->sc_kbd_layout = KB_NONE; 660 return (sc); 661 } 662 663 /* Attach me as a child to sc. */ 664 int 665 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me) 666 { 667 int error; 668 669 if (sc == NULL) 670 return (EINVAL); 671 672 DPRINTF(("wsmux_attach_sc: %s(%p): type=%d\n", 673 device_xname(sc->sc_base.me_dv), sc, me->me_ops->type)); 674 675 #ifdef DIAGNOSTIC 676 if (me->me_parent != NULL) { 677 printf("wsmux_attach_sc: busy\n"); 678 return (EBUSY); 679 } 680 #endif 681 me->me_parent = sc; 682 CIRCLEQ_INSERT_TAIL(&sc->sc_cld, me, me_next); 683 684 error = 0; 685 #if NWSDISPLAY > 0 686 if (sc->sc_base.me_dispdv != NULL) { 687 /* This is a display mux, so attach the new device to it. */ 688 DPRINTF(("wsmux_attach_sc: %s: set display %p\n", 689 device_xname(sc->sc_base.me_dv), 690 sc->sc_base.me_dispdv)); 691 if (me->me_ops->dsetdisplay != NULL) { 692 error = wsevsrc_set_display(me, &sc->sc_base); 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(("wsmux_attach_sc: %s set rawkbd=%d\n", 699 device_xname(me->me_dv), 700 sc->sc_rawkbd)); 701 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE, 702 &sc->sc_rawkbd, 0, 0); 703 #endif 704 if (sc->sc_kbd_layout != KB_NONE) 705 (void)wsevsrc_ioctl(me, 706 WSKBDIO_SETENCODING, 707 &sc->sc_kbd_layout, FWRITE, 0); 708 } 709 } 710 } 711 #endif 712 if (sc->sc_base.me_evp != NULL) { 713 /* Mux is open, so open the new subdevice */ 714 DPRINTF(("wsmux_attach_sc: %s: calling open of %s\n", 715 device_xname(sc->sc_base.me_dv), 716 device_xname(me->me_dv))); 717 error = wsevsrc_open(me, sc->sc_base.me_evp); 718 } else { 719 DPRINTF(("wsmux_attach_sc: %s not open\n", 720 device_xname(sc->sc_base.me_dv))); 721 } 722 723 if (error) { 724 me->me_parent = NULL; 725 CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next); 726 } 727 728 DPRINTF(("wsmux_attach_sc: %s(%p) done, error=%d\n", 729 device_xname(sc->sc_base.me_dv), sc, error)); 730 return (error); 731 } 732 733 /* Remove me from the parent. */ 734 void 735 wsmux_detach_sc(struct wsevsrc *me) 736 { 737 struct wsmux_softc *sc = me->me_parent; 738 739 DPRINTF(("wsmux_detach_sc: %s(%p) parent=%p\n", 740 device_xname(me->me_dv), me, sc)); 741 742 #ifdef DIAGNOSTIC 743 if (sc == NULL) { 744 printf("wsmux_detach_sc: %s has no parent\n", 745 device_xname(me->me_dv)); 746 return; 747 } 748 #endif 749 750 #if NWSDISPLAY > 0 751 if (sc->sc_base.me_dispdv != NULL) { 752 if (me->me_ops->dsetdisplay != NULL) 753 /* ignore error, there's nothing we can do */ 754 (void)wsevsrc_set_display(me, NULL); 755 } else 756 #endif 757 if (me->me_evp != NULL) { 758 DPRINTF(("wsmux_detach_sc: close\n")); 759 /* mux device is open, so close multiplexee */ 760 (void)wsevsrc_close(me); 761 } 762 763 CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next); 764 me->me_parent = NULL; 765 766 DPRINTF(("wsmux_detach_sc: done sc=%p\n", sc)); 767 } 768 769 /* 770 * Display ioctl() of a mux via the parent mux. 771 */ 772 int 773 wsmux_do_displayioctl(device_t dv, u_long cmd, void *data, int flag, 774 struct lwp *l) 775 { 776 struct wsmux_softc *sc = device_private(dv); 777 struct wsevsrc *me; 778 int error, ok; 779 780 DPRINTF(("wsmux_displayioctl: %s: sc=%p, cmd=%08lx\n", 781 device_xname(sc->sc_base.me_dv), sc, cmd)); 782 783 #ifdef WSDISPLAY_COMPAT_RAWKBD 784 if (cmd == WSKBDIO_SETMODE) { 785 sc->sc_rawkbd = *(int *)data; 786 DPRINTF(("wsmux_displayioctl: rawkbd = %d\n", sc->sc_rawkbd)); 787 } 788 #endif 789 790 /* 791 * Return 0 if any of the ioctl() succeeds, otherwise the last error. 792 * Return EPASSTHROUGH if no mux component accepts the ioctl. 793 */ 794 error = EPASSTHROUGH; 795 ok = 0; 796 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) { 797 DPRINTF(("wsmux_displayioctl: me=%p\n", me)); 798 #ifdef DIAGNOSTIC 799 if (me->me_parent != sc) { 800 printf("wsmux_displayioctl: bad child %p\n", me); 801 continue; 802 } 803 #endif 804 if (me->me_ops->ddispioctl != NULL) { 805 error = wsevsrc_display_ioctl(me, cmd, data, flag, l); 806 DPRINTF(("wsmux_displayioctl: me=%p dev=%s ==> %d\n", 807 me, device_xname(me->me_dv), error)); 808 if (!error) 809 ok = 1; 810 } 811 } 812 if (ok) 813 error = 0; 814 815 return (error); 816 } 817 818 #if NWSDISPLAY > 0 819 /* 820 * Set display of a mux via the parent mux. 821 */ 822 int 823 wsmux_evsrc_set_display(device_t dv, struct wsevsrc *ame) 824 { 825 struct wsmux_softc *muxsc = (struct wsmux_softc *)ame; 826 struct wsmux_softc *sc = device_private(dv); 827 device_t displaydv = muxsc ? muxsc->sc_base.me_dispdv : NULL; 828 829 DPRINTF(("wsmux_set_display: %s: displaydv=%p\n", 830 device_xname(sc->sc_base.me_dv), displaydv)); 831 832 if (displaydv != NULL) { 833 if (sc->sc_base.me_dispdv != NULL) 834 return (EBUSY); 835 } else { 836 if (sc->sc_base.me_dispdv == NULL) 837 return (ENXIO); 838 } 839 840 return wsmux_set_display(sc, displaydv); 841 } 842 843 int 844 wsmux_set_display(struct wsmux_softc *sc, device_t displaydv) 845 { 846 device_t odisplaydv; 847 struct wsevsrc *me; 848 struct wsmux_softc *nsc = displaydv ? sc : NULL; 849 int error, ok; 850 851 odisplaydv = sc->sc_base.me_dispdv; 852 sc->sc_base.me_dispdv = displaydv; 853 854 if (displaydv) 855 aprint_verbose_dev(sc->sc_base.me_dv, "connecting to %s\n", 856 device_xname(displaydv)); 857 ok = 0; 858 error = 0; 859 CIRCLEQ_FOREACH(me, &sc->sc_cld,me_next) { 860 #ifdef DIAGNOSTIC 861 if (me->me_parent != sc) { 862 printf("wsmux_set_display: bad child parent %p\n", me); 863 continue; 864 } 865 #endif 866 if (me->me_ops->dsetdisplay != NULL) { 867 error = wsevsrc_set_display(me, &nsc->sc_base); 868 DPRINTF(("wsmux_set_display: m=%p dev=%s error=%d\n", 869 me, device_xname(me->me_dv), error)); 870 if (!error) { 871 ok = 1; 872 #ifdef WSDISPLAY_COMPAT_RAWKBD 873 DPRINTF(("wsmux_set_display: %s set rawkbd=%d\n", 874 device_xname(me->me_dv), sc->sc_rawkbd)); 875 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE, 876 &sc->sc_rawkbd, 0, 0); 877 #endif 878 } 879 } 880 } 881 if (ok) 882 error = 0; 883 884 if (displaydv == NULL) 885 aprint_verbose("%s: disconnecting from %s\n", 886 device_xname(sc->sc_base.me_dv), 887 device_xname(odisplaydv)); 888 889 return (error); 890 } 891 #endif /* NWSDISPLAY > 0 */ 892