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