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