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