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