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