Home | History | Annotate | Line # | Download | only in wscons
wsmux.c revision 1.15
      1 /*
      2   TODO:
      3   use method to get sc of muxee, common code for add&rem
      4  */
      5 
      6 /*	$NetBSD: wsmux.c,v 1.15 2001/10/25 13:19:41 shin Exp $	*/
      7 
      8 /*
      9  * Copyright (c) 1998 The NetBSD Foundation, Inc.
     10  * All rights reserved.
     11  *
     12  * Author: Lennart Augustsson <augustss (at) carlstedt.se>
     13  *         Carlstedt Research & Technology
     14  *
     15  * Redistribution and use in source and binary forms, with or without
     16  * modification, are permitted provided that the following conditions
     17  * are met:
     18  * 1. Redistributions of source code must retain the above copyright
     19  *    notice, this list of conditions and the following disclaimer.
     20  * 2. Redistributions in binary form must reproduce the above copyright
     21  *    notice, this list of conditions and the following disclaimer in the
     22  *    documentation and/or other materials provided with the distribution.
     23  * 3. All advertising materials mentioning features or use of this software
     24  *    must display the following acknowledgement:
     25  *        This product includes software developed by the NetBSD
     26  *        Foundation, Inc. and its contributors.
     27  * 4. Neither the name of The NetBSD Foundation nor the names of its
     28  *    contributors may be used to endorse or promote products derived
     29  *    from this software without specific prior written permission.
     30  *
     31  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     32  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     33  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     34  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     35  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     36  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     37  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     38  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     39  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     40  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     41  * POSSIBILITY OF SUCH DAMAGE.
     42  */
     43 
     44 /*
     45  * wscons mux device.
     46  *
     47  * The mux device is a collection of real mice and keyboards and acts as
     48  * a merge point for all the events from the different real devices.
     49  */
     50 
     51 #include "wsdisplay.h"
     52 #include "wsmux.h"
     53 #include "wskbd.h"
     54 #include "wsmouse.h"
     55 
     56 #include <sys/param.h>
     57 #include <sys/conf.h>
     58 #include <sys/ioctl.h>
     59 #include <sys/fcntl.h>
     60 #include <sys/kernel.h>
     61 #include <sys/malloc.h>
     62 #include <sys/proc.h>
     63 #include <sys/queue.h>
     64 #include <sys/syslog.h>
     65 #include <sys/systm.h>
     66 #include <sys/tty.h>
     67 #include <sys/signalvar.h>
     68 #include <sys/device.h>
     69 
     70 #include "opt_wsdisplay_compat.h"
     71 
     72 #include <dev/wscons/wsconsio.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 int	wsmuxdebug = 0;
     80 #else
     81 #define DPRINTF(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 		wsmux_detach_sc(&sc->sc_base);
    210 	}
    211 
    212 	if (sc->sc_base.me_evp != NULL)
    213 		/* Already open. */
    214 		return (EBUSY);
    215 
    216 	evar = wsevent_alloc();
    217 	evar->io = p;
    218 #ifdef WSDISPLAY_COMPAT_RAWKBD
    219 	sc->sc_rawkbd = 0;
    220 #endif
    221 
    222 	wsmux_do_open(sc, evar);
    223 
    224 	return (0);
    225 }
    226 
    227 /*
    228  * Open of a mux via the parent mux.
    229  */
    230 int
    231 wsmux_mux_open(struct wsevsrc *me, struct wseventvar *evar)
    232 {
    233 	struct wsmux_softc *sc = (struct wsmux_softc *)me;
    234 
    235 #ifdef DIAGNOSTIC
    236 	if (sc->sc_base.me_evp != NULL) {
    237 		printf("wsmux_mux_open: busy\n");
    238 		return (EBUSY);
    239 	}
    240 	if (sc->sc_base.me_parent == NULL) {
    241 		printf("wsmux_mux_open: no parent\n");
    242 		return (EINVAL);
    243 	}
    244 #endif
    245 
    246 	wsmux_do_open(sc, evar);
    247 
    248 	return (0);
    249 }
    250 
    251 /* Common part of opening a mux. */
    252 void
    253 wsmux_do_open(struct wsmux_softc *sc, struct wseventvar *evar)
    254 {
    255 	struct wsevsrc *me;
    256 
    257 	sc->sc_base.me_evp = evar; /* remember event variable, mark as open */
    258 
    259 	/* Open all children. */
    260 	CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
    261 		DPRINTF(("wsmuxopen: %s: m=%p dev=%s\n",
    262 			 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname));
    263 #ifdef DIAGNOSTIC
    264 		if (me->me_evp != NULL) {
    265 			printf("wsmuxopen: dev already in use\n");
    266 			continue;
    267 		}
    268 		if (me->me_parent != sc) {
    269 			printf("wsmux_do_open: bad child=%p\n", me);
    270 			continue;
    271 		}
    272 		{
    273 		int error = me->me_ops->dopen(me, evar);
    274 		if (error) {
    275 			DPRINTF(("wsmuxopen: open failed %d\n", error));
    276 		}
    277 		}
    278 #else
    279 		/* ignore errors, failing children will not be marked open */
    280 		(void)me->me_ops->dopen(me, evar);
    281 #endif
    282 	}
    283 }
    284 
    285 /*
    286  * close() of the pseudo device from device table.
    287  */
    288 int
    289 wsmuxclose(dev_t dev, int flags, int mode, struct proc *p)
    290 {
    291 	int minr = minor(dev);
    292 	struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
    293 	struct wseventvar *evar = sc->sc_base.me_evp;
    294 
    295 	if (WSMUXCTL(minr))
    296 		/* control device */
    297 		return (0);
    298 	if (evar == NULL)
    299 		/* Not open for read */
    300 		return (0);
    301 
    302 	sc->sc_base.me_evp = NULL;
    303 	wsevent_free(evar);
    304 	wsmux_do_close(sc);
    305 	return (0);
    306 }
    307 
    308 /*
    309  * Close of a mux via the parent mux.
    310  */
    311 int
    312 wsmux_mux_close(struct wsevsrc *me)
    313 {
    314 	me->me_evp = NULL;
    315 	wsmux_do_close((struct wsmux_softc *)me);
    316 	return (0);
    317 }
    318 
    319 /* Common part of closing a mux. */
    320 void
    321 wsmux_do_close(struct wsmux_softc *sc)
    322 {
    323 	struct wsevsrc *me;
    324 
    325 	DPRINTF(("wsmuxclose: %s: sc=%p\n", sc->sc_base.me_dv.dv_xname, sc));
    326 
    327 	/* Close all the children. */
    328 	CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
    329 		DPRINTF(("wsmuxclose %s: m=%p dev=%s\n",
    330 			 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname));
    331 #ifdef DIAGNOSTIC
    332 		if (me->me_parent != sc) {
    333 			printf("wsmuxclose: bad child=%p\n", me);
    334 			continue;
    335 		}
    336 #endif
    337 		(void)me->me_ops->dclose(me);
    338 		me->me_evp = NULL;
    339 	}
    340 }
    341 
    342 /*
    343  * read() of the pseudo device from device table.
    344  */
    345 int
    346 wsmuxread(dev_t dev, struct uio *uio, int flags)
    347 {
    348 	int minr = minor(dev);
    349 	struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
    350 
    351 	if (WSMUXCTL(minr)) {
    352 		/* control device */
    353 		return (EINVAL);
    354 	}
    355 
    356 	if (sc->sc_base.me_evp == NULL) {
    357 #ifdef DIAGNOSTIC
    358 		/* XXX can we get here? */
    359 		printf("wsmuxread: not open\n");
    360 #endif
    361 		return (EINVAL);
    362 	}
    363 
    364 	return (wsevent_read(sc->sc_base.me_evp, uio, flags));
    365 }
    366 
    367 /*
    368  * ioctl of the pseudo device from device table.
    369  */
    370 int
    371 wsmuxioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
    372 {
    373 	int u = WSMUXDEV(minor(dev));
    374 
    375 	return wsmux_do_ioctl(&wsmuxdevs[u]->sc_base.me_dv, cmd, data, flag, p);
    376 }
    377 
    378 /*
    379  * ioctl of a mux via the parent mux, continuation of wsmuxioctl().
    380  */
    381 int
    382 wsmux_do_ioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
    383 	       struct proc *p)
    384 {
    385 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
    386 	struct wsevsrc *me;
    387 	int error, ok;
    388 	int s, put, get, n;
    389 	struct wseventvar *evar;
    390 	struct wscons_event *ev;
    391 	struct timeval thistime;
    392 	struct wsmux_device_list *l;
    393 
    394 	DPRINTF(("wsmux_do_ioctl: %s: sc=%p, cmd=%08lx\n",
    395 		 sc->sc_base.me_dv.dv_xname, sc, cmd));
    396 
    397 	switch (cmd) {
    398 	case WSMUXIO_INJECTEVENT:
    399 		/* Inject an event, e.g., from moused. */
    400 		DPRINTF(("%s: inject\n", sc->sc_base.me_dv.dv_xname));
    401 
    402 		evar = sc->sc_base.me_evp;
    403 		if (evar == NULL) /* XXX is this an error? */
    404 			return (EACCES);
    405 
    406 		s = spltty();
    407 		get = evar->get;
    408 		put = evar->put;
    409 		if (++put % WSEVENT_QSIZE == get) {
    410 			put--;
    411 			splx(s);
    412 			return (ENOSPC);
    413 		}
    414 		if (put >= WSEVENT_QSIZE)
    415 			put = 0;
    416 		ev = &evar->q[put];
    417 		*ev = *(struct wscons_event *)data;
    418 		microtime(&thistime);
    419 		TIMEVAL_TO_TIMESPEC(&thistime, &ev->time);
    420 		evar->put = put;
    421 		WSEVENT_WAKEUP(evar);
    422 		splx(s);
    423 		return (0);
    424 	case WSMUXIO_ADD_DEVICE:
    425 #define d ((struct wsmux_device *)data)
    426 		DPRINTF(("%s: add type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
    427 			 d->type, d->idx));
    428 		switch (d->type) {
    429 #if NWSMOUSE > 0
    430 		case WSMUX_MOUSE:
    431 			return (wsmouse_add_mux(d->idx, sc));
    432 #endif
    433 #if NWSKBD > 0
    434 		case WSMUX_KBD:
    435 			return (wskbd_add_mux(d->idx, sc));
    436 #endif
    437 		case WSMUX_MUX:
    438 			return (wsmux_add_mux(d->idx, sc));
    439 		default:
    440 			return (EINVAL);
    441 		}
    442 	case WSMUXIO_REMOVE_DEVICE:
    443 		DPRINTF(("%s: rem type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
    444 			 d->type, d->idx));
    445 		/* Locate the device */
    446 		CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
    447 			if (me->me_ops->type == d->type &&
    448 			    me->me_dv.dv_unit == d->idx) {
    449 				wsmux_detach_sc(me);
    450 				return (0);
    451 			}
    452 		}
    453 		return (EINVAL);
    454 #undef d
    455 
    456 	case WSMUXIO_LIST_DEVICES:
    457 		DPRINTF(("%s: list\n", sc->sc_base.me_dv.dv_xname));
    458 		l = (struct wsmux_device_list *)data;
    459 		n = 0;
    460 		CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
    461 			if (n >= WSMUX_MAXDEV)
    462 				break;
    463 			l->devices[n].type = me->me_ops->type;
    464 			l->devices[n].idx = me->me_dv.dv_unit;
    465 			n++;
    466 		}
    467 		l->ndevices = n;
    468 		return (0);
    469 #ifdef WSDISPLAY_COMPAT_RAWKBD
    470 	case WSKBDIO_SETMODE:
    471 		sc->sc_rawkbd = *(int *)data;
    472 		DPRINTF(("wsmux_do_ioctl: save rawkbd = %d\n", sc->sc_rawkbd));
    473 		break;
    474 #endif
    475 	case FIOASYNC:
    476 		DPRINTF(("%s: FIOASYNC\n", sc->sc_base.me_dv.dv_xname));
    477 		evar = sc->sc_base.me_evp;
    478 		if (evar == NULL)
    479 			return (EINVAL);
    480 		evar->async = *(int *)data != 0;
    481 		return (0);
    482 	case TIOCSPGRP:
    483 		DPRINTF(("%s: TIOCSPGRP\n", sc->sc_base.me_dv.dv_xname));
    484 		evar = sc->sc_base.me_evp;
    485 		if (evar == NULL)
    486 			return (EINVAL);
    487 		if (*(int *)data != evar->io->p_pgid)
    488 			return (EPERM);
    489 		return (0);
    490 	default:
    491 		DPRINTF(("%s: unknown\n", sc->sc_base.me_dv.dv_xname));
    492 		break;
    493 	}
    494 
    495 	if (sc->sc_base.me_evp == NULL
    496 #if NWSDISPLAY > 0
    497 	    && sc->sc_base.me_dispdv == NULL
    498 #endif
    499 	    )
    500 		return (EACCES);
    501 
    502 	/* Return 0 if any of the ioctl() succeeds, otherwise the last error */
    503 	error = 0;
    504 	ok = 0;
    505 	CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
    506 		DPRINTF(("wsmux_do_ioctl: me=%p\n", me));
    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 		DPRINTF(("wsmux_do_ioctl: %s: me=%p dev=%s\n",
    515 			 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname));
    516 		error = wsevsrc_ioctl(me, cmd, data, flag, p);
    517 		if (!error)
    518 			ok = 1;
    519 	}
    520 	if (ok)
    521 		error = 0;
    522 
    523 	return (error);
    524 }
    525 
    526 /*
    527  * poll() of the pseudo device from device table.
    528  */
    529 int
    530 wsmuxpoll(dev_t dev, int events, struct proc *p)
    531 {
    532 	int minr = minor(dev);
    533 	struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
    534 
    535 	if (WSMUXCTL(minr)) {
    536 		/* control device */
    537 		return (EINVAL);
    538 	}
    539 
    540 	if (sc->sc_base.me_evp == NULL) {
    541 #ifdef DIAGNOSTIC
    542 		printf("wsmuxpoll: not open\n");
    543 #endif
    544 		return (EACCES);
    545 	}
    546 
    547 	return (wsevent_poll(sc->sc_base.me_evp, events, p));
    548 }
    549 
    550 /*
    551  * Add mux unit as a child to muxsc.
    552  */
    553 int
    554 wsmux_add_mux(int unit, struct wsmux_softc *muxsc)
    555 {
    556 	struct wsmux_softc *sc, *m;
    557 
    558 	sc = wsmux_getmux(unit);
    559 	if (sc == NULL)
    560 		return (ENXIO);
    561 
    562 	DPRINTF(("wsmux_add_mux: %s(%p) to %s(%p)\n",
    563 		 sc->sc_base.me_dv.dv_xname, sc, muxsc->sc_base.me_dv.dv_xname,
    564 		 muxsc));
    565 
    566 	if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
    567 		return (EBUSY);
    568 
    569 	/* The mux we are adding must not be an ancestor of itself. */
    570 	for (m = muxsc; m != NULL ; m = m->sc_base.me_parent)
    571 		if (m == sc)
    572 			return (EINVAL);
    573 
    574 	return (wsmux_attach_sc(muxsc, &sc->sc_base));
    575 }
    576 
    577 /* Create a new mux softc. */
    578 struct wsmux_softc *
    579 wsmux_create(const char *name, int unit)
    580 {
    581 	struct wsmux_softc *sc;
    582 
    583 	DPRINTF(("wsmux_create: allocating\n"));
    584 	sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT);
    585 	if (sc == NULL)
    586 		return (NULL);
    587 	memset(sc, 0, sizeof *sc);
    588 	CIRCLEQ_INIT(&sc->sc_cld);
    589 	snprintf(sc->sc_base.me_dv.dv_xname, sizeof sc->sc_base.me_dv.dv_xname,
    590 		 "%s%d", name, unit);
    591 	sc->sc_base.me_dv.dv_unit = unit;
    592 	sc->sc_base.me_ops = &wsmux_srcops;
    593 	return (sc);
    594 }
    595 
    596 /* Attach me as a child to sc. */
    597 int
    598 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me)
    599 {
    600 	int error;
    601 
    602 	if (sc == NULL)
    603 		return (EINVAL);
    604 
    605 	DPRINTF(("wsmux_attach_sc: %s: type=%d\n",
    606 		 sc->sc_base.me_dv.dv_xname, me->me_ops->type));
    607 
    608 #ifdef DIAGNOSTIC
    609 	if (me->me_parent != NULL) {
    610 		printf("wsmux_attach_sc: busy\n");
    611 		return (EBUSY);
    612 	}
    613 #endif
    614 	me->me_parent = sc;
    615 	CIRCLEQ_INSERT_TAIL(&sc->sc_cld, me, me_next);
    616 
    617 #if NWSDISPLAY > 0
    618 	if (sc->sc_base.me_dispdv != NULL) {
    619 		/* This is a display mux, so attach the new device to it. */
    620 		DPRINTF(("wsmux_attach_sc: %s: set display %p\n",
    621 			 sc->sc_base.me_dv.dv_xname, sc->sc_base.me_dispdv));
    622 		error = 0;
    623 		if (me->me_ops->dsetdisplay != NULL) {
    624 			error = wsevsrc_set_display(me, &sc->sc_base);
    625 			/* Ignore that the console already has a display. */
    626 			if (error == EBUSY)
    627 				error = 0;
    628 			if (!error) {
    629 #ifdef WSDISPLAY_COMPAT_RAWKBD
    630 				DPRINTF(("wsmux_attach_sc: on %s set rawkbd=%d\n",
    631 					 me->me_dv.dv_xname, sc->sc_rawkbd));
    632 				(void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
    633 						    &sc->sc_rawkbd, 0, 0);
    634 #endif
    635 			}
    636 		}
    637 	} else
    638 #endif
    639 		if (sc->sc_base.me_evp != NULL) {
    640 		/* Mux is open, so open the new subdevice */
    641 		DPRINTF(("wsmux_attach_sc: %s: calling open of %s\n",
    642 			 sc->sc_base.me_dv.dv_xname, me->me_dv.dv_xname));
    643 		error = me->me_ops->dopen(me, sc->sc_base.me_evp);
    644 	} else {
    645 		DPRINTF(("wsmux_attach_sc: %s not open\n",
    646 			 sc->sc_base.me_dv.dv_xname));
    647 		error = 0;
    648 	}
    649 
    650 	if (error) {
    651 		me->me_parent = NULL;
    652 		CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next);
    653 	}
    654 
    655 	DPRINTF(("wsmux_attach_sc: done sc=%p, error=%d\n", sc, error));
    656 	return (error);
    657 }
    658 
    659 /* Remove me from the parent. */
    660 void
    661 wsmux_detach_sc(struct wsevsrc *me)
    662 {
    663 	struct wsmux_softc *sc = me->me_parent;
    664 
    665 	DPRINTF(("wsmux_detach_sc: %s\n", sc->sc_base.me_dv.dv_xname));
    666 
    667 #ifdef DIAGNOSTIC
    668 	if (sc == NULL) {
    669 		printf("wsmux_detach_sc: not allocated\n");
    670 		return;
    671 	}
    672 #endif
    673 
    674 #if NWSDISPLAY > 0
    675 	if (sc->sc_base.me_dispdv != NULL) {
    676 		if (me->me_ops->dsetdisplay != NULL)
    677 			/* ignore error, there's nothing we can do */
    678 			(void)wsevsrc_set_display(me, NULL);
    679 	} else
    680 #endif
    681 		if (me->me_evp != NULL) {
    682 		DPRINTF(("wsmux_detach_sc: close\n"));
    683 		/* mux device is open, so close multiplexee */
    684 		(void)me->me_ops->dclose(me);
    685 	}
    686 
    687 	CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next);
    688 	me->me_parent = NULL;
    689 
    690 	DPRINTF(("wsmux_detach_sc: done sc=%p\n", sc));
    691 }
    692 
    693 /*
    694  * Display ioctl() of a mux via the parent mux.
    695  */
    696 int
    697 wsmux_do_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
    698 		      struct proc *p)
    699 {
    700 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
    701 	struct wsevsrc *me;
    702 	int error, ok;
    703 
    704 	DPRINTF(("wsmux_displayioctl: %s: sc=%p, cmd=%08lx\n",
    705 		 sc->sc_base.me_dv.dv_xname, sc, cmd));
    706 
    707 #ifdef WSDISPLAY_COMPAT_RAWKBD
    708 	if (cmd == WSKBDIO_SETMODE) {
    709 		sc->sc_rawkbd = *(int *)data;
    710 		DPRINTF(("wsmux_displayioctl: rawkbd = %d\n", sc->sc_rawkbd));
    711 	}
    712 #endif
    713 
    714 	/*
    715 	 * Return 0 if any of the ioctl() succeeds, otherwise the last error.
    716 	 * Return -1 if no mux component accepts the ioctl.
    717 	 */
    718 	error = -1;
    719 	ok = 0;
    720 	CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
    721 		DPRINTF(("wsmux_displayioctl: me=%p\n", me));
    722 #ifdef DIAGNOSTIC
    723 		if (me->me_parent != sc) {
    724 			printf("wsmux_displayioctl: bad child %p\n", me);
    725 			continue;
    726 		}
    727 #endif
    728 		if (me->me_ops->ddispioctl != NULL) {
    729 			error = wsevsrc_display_ioctl(me, cmd, data, flag, p);
    730 			DPRINTF(("wsmux_displayioctl: me=%p dev=%s ==> %d\n",
    731 				 me, me->me_dv.dv_xname, error));
    732 			if (!error)
    733 				ok = 1;
    734 		}
    735 	}
    736 	if (ok)
    737 		error = 0;
    738 
    739 	return (error);
    740 }
    741 
    742 #if NWSDISPLAY > 0
    743 /*
    744  * Set display of a mux via the parent mux.
    745  */
    746 int
    747 wsmux_set_display(struct device *dv, struct wsevsrc *ame)
    748 {
    749 	struct wsmux_softc *muxsc = (struct wsmux_softc *)ame;
    750 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
    751 	struct wsmux_softc *nsc = muxsc ? sc : NULL;
    752 	struct device *displaydv = muxsc ? muxsc->sc_base.me_dispdv : NULL;
    753 	struct device *odisplaydv;
    754 	struct wsevsrc *me;
    755 	int error, ok;
    756 
    757 	DPRINTF(("wsmux_set_display: %s: displaydv=%p\n",
    758 		 sc->sc_base.me_dv.dv_xname, displaydv));
    759 
    760 	if (displaydv != NULL) {
    761 		if (sc->sc_base.me_dispdv != NULL)
    762 			return (EBUSY);
    763 	} else {
    764 		if (sc->sc_base.me_dispdv == NULL)
    765 			return (ENXIO);
    766 	}
    767 
    768 	odisplaydv = sc->sc_base.me_dispdv;
    769 	sc->sc_base.me_dispdv = displaydv;
    770 
    771 	if (displaydv)
    772 		printf("%s: connecting to %s\n",
    773 		       sc->sc_base.me_dv.dv_xname, displaydv->dv_xname);
    774 	ok = 0;
    775 	error = 0;
    776 	CIRCLEQ_FOREACH(me, &sc->sc_cld,me_next) {
    777 #ifdef DIAGNOSTIC
    778 		if (me->me_parent != sc) {
    779 			printf("wsmux_set_display: bad child parent %p\n", me);
    780 			continue;
    781 		}
    782 #endif
    783 		if (me->me_ops->dsetdisplay != NULL) {
    784 			error = wsevsrc_set_display(me, &nsc->sc_base);
    785 			DPRINTF(("wsmux_set_display: m=%p dev=%s error=%d\n",
    786 				 me, me->me_dv.dv_xname, error));
    787 			if (!error) {
    788 				ok = 1;
    789 #ifdef WSDISPLAY_COMPAT_RAWKBD
    790 				DPRINTF(("wsmux_set_display: on %s set rawkbd=%d\n",
    791 					 me->me_dv.dv_xname, sc->sc_rawkbd));
    792 				(void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
    793 						    &sc->sc_rawkbd, 0, 0);
    794 #endif
    795 			}
    796 		}
    797 	}
    798 	if (ok)
    799 		error = 0;
    800 
    801 	if (displaydv == NULL)
    802 		printf("%s: disconnecting from %s\n",
    803 		       sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname);
    804 
    805 	return (error);
    806 }
    807 #endif /* NWSDISPLAY > 0 */
    808