1 /* $NetBSD: wsmux.c,v 1.26 2001/11/22 00:54:23 augustss 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.26 2001/11/22 00:54:23 augustss 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_set_display(struct device *, struct wsevsrc *); 106 #else 107 #define wsmux_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_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); 603 if (sc == NULL) 604 return (NULL); 605 memset(sc, 0, sizeof *sc); 606 CIRCLEQ_INIT(&sc->sc_cld); 607 snprintf(sc->sc_base.me_dv.dv_xname, sizeof sc->sc_base.me_dv.dv_xname, 608 "%s%d", name, unit); 609 sc->sc_base.me_dv.dv_unit = unit; 610 sc->sc_base.me_ops = &wsmux_srcops; 611 return (sc); 612 } 613 614 /* Attach me as a child to sc. */ 615 int 616 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me) 617 { 618 int error; 619 620 if (sc == NULL) 621 return (EINVAL); 622 623 DPRINTF(("wsmux_attach_sc: %s(%p): type=%d\n", 624 sc->sc_base.me_dv.dv_xname, sc, me->me_ops->type)); 625 626 #ifdef DIAGNOSTIC 627 if (me->me_parent != NULL) { 628 printf("wsmux_attach_sc: busy\n"); 629 return (EBUSY); 630 } 631 #endif 632 me->me_parent = sc; 633 CIRCLEQ_INSERT_TAIL(&sc->sc_cld, me, me_next); 634 635 error = 0; 636 #if NWSDISPLAY > 0 637 if (sc->sc_base.me_dispdv != NULL) { 638 /* This is a display mux, so attach the new device to it. */ 639 DPRINTF(("wsmux_attach_sc: %s: set display %p\n", 640 sc->sc_base.me_dv.dv_xname, sc->sc_base.me_dispdv)); 641 if (me->me_ops->dsetdisplay != NULL) { 642 error = wsevsrc_set_display(me, &sc->sc_base); 643 /* Ignore that the console already has a display. */ 644 if (error == EBUSY) 645 error = 0; 646 if (!error) { 647 #ifdef WSDISPLAY_COMPAT_RAWKBD 648 DPRINTF(("wsmux_attach_sc: %s set rawkbd=%d\n", 649 me->me_dv.dv_xname, sc->sc_rawkbd)); 650 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE, 651 &sc->sc_rawkbd, 0, 0); 652 #endif 653 } 654 } 655 } 656 #endif 657 if (sc->sc_base.me_evp != NULL) { 658 /* Mux is open, so open the new subdevice */ 659 DPRINTF(("wsmux_attach_sc: %s: calling open of %s\n", 660 sc->sc_base.me_dv.dv_xname, me->me_dv.dv_xname)); 661 error = wsevsrc_open(me, sc->sc_base.me_evp); 662 } else { 663 DPRINTF(("wsmux_attach_sc: %s not open\n", 664 sc->sc_base.me_dv.dv_xname)); 665 } 666 667 if (error) { 668 me->me_parent = NULL; 669 CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next); 670 } 671 672 DPRINTF(("wsmux_attach_sc: %s(%p) done, error=%d\n", 673 sc->sc_base.me_dv.dv_xname, sc, error)); 674 return (error); 675 } 676 677 /* Remove me from the parent. */ 678 void 679 wsmux_detach_sc(struct wsevsrc *me) 680 { 681 struct wsmux_softc *sc = me->me_parent; 682 683 DPRINTF(("wsmux_detach_sc: %s(%p) parent=%p\n", 684 me->me_dv.dv_xname, me, sc)); 685 686 #ifdef DIAGNOSTIC 687 if (sc == NULL) { 688 printf("wsmux_detach_sc: %s has no parent\n", 689 me->me_dv.dv_xname); 690 return; 691 } 692 #endif 693 694 #if NWSDISPLAY > 0 695 if (sc->sc_base.me_dispdv != NULL) { 696 if (me->me_ops->dsetdisplay != NULL) 697 /* ignore error, there's nothing we can do */ 698 (void)wsevsrc_set_display(me, NULL); 699 } else 700 #endif 701 if (me->me_evp != NULL) { 702 DPRINTF(("wsmux_detach_sc: close\n")); 703 /* mux device is open, so close multiplexee */ 704 (void)wsevsrc_close(me); 705 } 706 707 CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next); 708 me->me_parent = NULL; 709 710 DPRINTF(("wsmux_detach_sc: done sc=%p\n", sc)); 711 } 712 713 /* 714 * Display ioctl() of a mux via the parent mux. 715 */ 716 int 717 wsmux_do_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag, 718 struct proc *p) 719 { 720 struct wsmux_softc *sc = (struct wsmux_softc *)dv; 721 struct wsevsrc *me; 722 int error, ok; 723 724 DPRINTF(("wsmux_displayioctl: %s: sc=%p, cmd=%08lx\n", 725 sc->sc_base.me_dv.dv_xname, sc, cmd)); 726 727 #ifdef WSDISPLAY_COMPAT_RAWKBD 728 if (cmd == WSKBDIO_SETMODE) { 729 sc->sc_rawkbd = *(int *)data; 730 DPRINTF(("wsmux_displayioctl: rawkbd = %d\n", sc->sc_rawkbd)); 731 } 732 #endif 733 734 /* 735 * Return 0 if any of the ioctl() succeeds, otherwise the last error. 736 * Return -1 if no mux component accepts the ioctl. 737 */ 738 error = -1; 739 ok = 0; 740 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) { 741 DPRINTF(("wsmux_displayioctl: me=%p\n", me)); 742 #ifdef DIAGNOSTIC 743 if (me->me_parent != sc) { 744 printf("wsmux_displayioctl: bad child %p\n", me); 745 continue; 746 } 747 #endif 748 if (me->me_ops->ddispioctl != NULL) { 749 error = wsevsrc_display_ioctl(me, cmd, data, flag, p); 750 DPRINTF(("wsmux_displayioctl: me=%p dev=%s ==> %d\n", 751 me, me->me_dv.dv_xname, error)); 752 if (!error) 753 ok = 1; 754 } 755 } 756 if (ok) 757 error = 0; 758 759 return (error); 760 } 761 762 #if NWSDISPLAY > 0 763 /* 764 * Set display of a mux via the parent mux. 765 */ 766 int 767 wsmux_set_display(struct device *dv, struct wsevsrc *ame) 768 { 769 struct wsmux_softc *muxsc = (struct wsmux_softc *)ame; 770 struct wsmux_softc *sc = (struct wsmux_softc *)dv; 771 struct wsmux_softc *nsc = muxsc ? sc : NULL; 772 struct device *displaydv = muxsc ? muxsc->sc_base.me_dispdv : NULL; 773 struct device *odisplaydv; 774 struct wsevsrc *me; 775 int error, ok; 776 777 DPRINTF(("wsmux_set_display: %s: displaydv=%p\n", 778 sc->sc_base.me_dv.dv_xname, displaydv)); 779 780 if (displaydv != NULL) { 781 if (sc->sc_base.me_dispdv != NULL) 782 return (EBUSY); 783 } else { 784 if (sc->sc_base.me_dispdv == NULL) 785 return (ENXIO); 786 } 787 788 odisplaydv = sc->sc_base.me_dispdv; 789 sc->sc_base.me_dispdv = displaydv; 790 791 if (displaydv) 792 printf("%s: connecting to %s\n", 793 sc->sc_base.me_dv.dv_xname, displaydv->dv_xname); 794 ok = 0; 795 error = 0; 796 CIRCLEQ_FOREACH(me, &sc->sc_cld,me_next) { 797 #ifdef DIAGNOSTIC 798 if (me->me_parent != sc) { 799 printf("wsmux_set_display: bad child parent %p\n", me); 800 continue; 801 } 802 #endif 803 if (me->me_ops->dsetdisplay != NULL) { 804 error = wsevsrc_set_display(me, &nsc->sc_base); 805 DPRINTF(("wsmux_set_display: m=%p dev=%s error=%d\n", 806 me, me->me_dv.dv_xname, error)); 807 if (!error) { 808 ok = 1; 809 #ifdef WSDISPLAY_COMPAT_RAWKBD 810 DPRINTF(("wsmux_set_display: %s set rawkbd=%d\n", 811 me->me_dv.dv_xname, sc->sc_rawkbd)); 812 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE, 813 &sc->sc_rawkbd, 0, 0); 814 #endif 815 } 816 } 817 } 818 if (ok) 819 error = 0; 820 821 if (displaydv == NULL) 822 printf("%s: disconnecting from %s\n", 823 sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname); 824 825 return (error); 826 } 827 #endif /* NWSDISPLAY > 0 */ 828