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