Home | History | Annotate | Line # | Download | only in wscons
wsmux.c revision 1.1
      1 /*	$NetBSD: wsmux.c,v 1.1 1999/07/29 18:20:03 augustss Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * Author: Lennart Augustsson <augustss (at) 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 #include "wsmux.h"
     40 #include "wsdisplay.h"
     41 #include "wskbd.h"
     42 
     43 #if NWSMUX > 0 || (NWSDISPLAY > 0 && NWSKBD > 0)
     44 
     45 /*
     46  * wscons mux device.
     47  *
     48  * The mux device is a collection of real mice and keyboards and acts as
     49  * a merge point for all the events from the different real devices.
     50  */
     51 
     52 #include <sys/param.h>
     53 #include <sys/conf.h>
     54 #include <sys/ioctl.h>
     55 #include <sys/fcntl.h>
     56 #include <sys/kernel.h>
     57 #include <sys/malloc.h>
     58 #include <sys/proc.h>
     59 #include <sys/queue.h>
     60 #include <sys/syslog.h>
     61 #include <sys/systm.h>
     62 #include <sys/tty.h>
     63 #include <sys/signalvar.h>
     64 #include <sys/device.h>
     65 
     66 #include "opt_wsdisplay_compat.h"
     67 
     68 #include <dev/wscons/wsconsio.h>
     69 #include <dev/wscons/wseventvar.h>
     70 #include <dev/wscons/wscons_callbacks.h>
     71 #include <dev/wscons/wsmuxvar.h>
     72 
     73 #ifdef WSMUX_DEBUG
     74 #define DPRINTF(x)	if (wsmuxdebug) printf x
     75 int	wsmuxdebug = 0;
     76 #else
     77 #define DPRINTF(x)
     78 #endif
     79 
     80 struct wsplink {
     81 	LIST_ENTRY(wsplink) next;
     82 	int type;
     83 	struct wsmux_softc *mux; /* our mux device */
     84 	/* The rest of the fields reflect a value in the multiplexee. */
     85 	struct device *sc;	/* softc */
     86 	struct wseventvar *sc_mevents; /* event var */
     87 	struct wsmux_softc **sc_muxp; /* pointer to us */
     88 	struct wsmuxops *sc_ops;
     89 };
     90 
     91 cdev_decl(wsmux);
     92 
     93 int wsmuxdoclose __P((struct device *, int, int, struct proc *));
     94 int wsmux_set_display __P((struct device *, struct wsmux_softc *));
     95 
     96 int nwsmux = NWSMUX;
     97 struct wsmux_softc *wsmuxdevs[NWSMUX];
     98 
     99 void wsmuxattach __P((int));
    100 
    101 struct wsmuxops wsmux_muxops = {
    102 	wsmuxopen, wsmuxdoclose, wsmuxdoioctl, wsmux_displayioctl,
    103 	wsmux_set_display
    104 };
    105 
    106 /* From upper level */
    107 void
    108 wsmuxattach(n)
    109 	int n;
    110 {
    111 	int i;
    112 
    113 #if 0
    114 /* Called too late. We use static allocation instead. */
    115 	nwsmux = n;
    116 	wsmuxdevs = malloc(n * sizeof (struct wsmux_softc *),
    117 			   M_DEVBUF, M_NOWAIT);
    118 	if (wsmuxdevs == 0)
    119 		panic("wsmuxattach: out of memory\n");
    120 #endif
    121 
    122 	/* Make sure all muxes are there. */
    123 	for (i = 0; i < nwsmux; i++)
    124 		if (!wsmuxdevs[i])
    125 			wsmuxdevs[i] = wsmux_create("wsmux", i);
    126 }
    127 
    128 struct wsmux_softc *
    129 wsmux_create(name, unit)
    130 	const char *name;
    131 	int unit;
    132 {
    133 	struct wsmux_softc *sc;
    134 
    135 	DPRINTF(("wsmux_create: allocating\n"));
    136 	sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT);
    137 	if (!sc)
    138 		return (0);
    139 	memset(sc, 0, sizeof *sc);
    140 	LIST_INIT(&sc->sc_reals);
    141 	snprintf(sc->sc_dv.dv_xname, sizeof sc->sc_dv.dv_xname,
    142 		 "%s%d", name, unit);
    143 	sc->sc_dv.dv_unit = unit;
    144 	return (sc);
    145 }
    146 
    147 /* From mouse or keyboard. */
    148 void
    149 wsmux_attach(n, type, dsc, ev, psp, ops)
    150 	int n;
    151 	int type;
    152         struct device *dsc;
    153 	struct wseventvar *ev;
    154 	struct wsmux_softc **psp;
    155 	struct wsmuxops *ops;
    156 {
    157 	struct wsmux_softc *sc;
    158 	int error;
    159 
    160 	DPRINTF(("wsmux_attach\n"));
    161 	if (n >= nwsmux || n < 0) {
    162 		printf("wsmux: attach device %d is out of range (%d..%d)\n",
    163 		       n, 0, nwsmux-1);
    164 		return;
    165 	}
    166 	sc = wsmuxdevs[n];
    167 	if (sc == 0) {
    168 		sc = wsmux_create("wsmux", n);
    169 		if (sc == 0) {
    170 			printf("wsmux: attach out of memory\n");
    171 			return;
    172 		}
    173 		wsmuxdevs[n] = sc;
    174 	}
    175 	error = wsmux_attach_sc(sc, type, dsc, ev, psp, ops);
    176 	if (error)
    177 		printf("wsmux_attach: error=%d\n", error);
    178 }
    179 
    180 int
    181 wsmux_attach_sc(sc, type, dsc, ev, psp, ops)
    182 	struct wsmux_softc *sc;
    183 	int type;
    184         struct device *dsc;
    185 	struct wseventvar *ev;
    186 	struct wsmux_softc **psp;
    187 	struct wsmuxops *ops;
    188 {
    189 	struct wsplink *m;
    190 	int error;
    191 
    192 	DPRINTF(("wsmux_attach_sc: %s: type=%d dsc=%p, *psp=%p\n",
    193 		 sc->sc_dv.dv_xname, type, dsc, *psp));
    194 	m = malloc(sizeof *m, M_DEVBUF, M_NOWAIT);
    195 	if (m == 0)
    196 		return (ENOMEM);
    197 	m->type = type;
    198 	m->mux = sc;
    199 	m->sc = dsc;
    200 	m->sc_mevents = ev;
    201 	m->sc_muxp = psp;
    202 	m->sc_ops = ops;
    203 	LIST_INSERT_HEAD(&sc->sc_reals, m, next);
    204 
    205 	if (sc->sc_displaydv) {
    206 		/* This is a display mux, so attach the new device to it. */
    207 		DPRINTF(("wsmux_attach_sc: %s: set display %p\n",
    208 			 sc->sc_dv.dv_xname, sc->sc_displaydv));
    209 		error = 0;
    210 		if (m->sc_ops->dsetdisplay) {
    211 			error = m->sc_ops->dsetdisplay(m->sc, sc);
    212 			/* Ignore that the console already has a display. */
    213 			if (error == EBUSY)
    214 				error = 0;
    215 			if (!error) {
    216 				*m->sc_muxp = sc;
    217 #ifdef WSDISPLAY_COMPAT_RAWKBD
    218 				DPRINTF(("wsmux_attach_sc: on %s set rawkbd=%d\n",
    219 					 m->sc->dv_xname, sc->sc_rawkbd));
    220 				(void)m->sc_ops->dioctl(m->sc,
    221 					     WSKBDIO_SETMODE,
    222 					     (caddr_t)&sc->sc_rawkbd,
    223 					     0, 0);
    224 #endif
    225 			}
    226 		}
    227 	} else if (sc->sc_events.io) {
    228 		/* Mux is open, so open the new subdevice */
    229 		DPRINTF(("wsmux_attach_sc: %s: calling open of %s\n",
    230 			 sc->sc_dv.dv_xname, m->sc->dv_xname));
    231 		/* mux already open, join in */
    232 		error = m->sc_ops->dopen(makedev(0, m->sc->dv_unit),
    233 					 sc->sc_flags, sc->sc_mode, sc->sc_p);
    234 		if (!error)
    235 			*m->sc_muxp = sc;
    236 	} else {
    237 		DPRINTF(("wsmux_attach_sc: %s not open\n",
    238 			 sc->sc_dv.dv_xname));
    239 		error = 0;
    240 	}
    241 	DPRINTF(("wsmux_attach_sc: done sc=%p psp=%p *psp=%p\n",
    242 		 sc, psp, *psp));
    243 
    244 	return (error);
    245 }
    246 
    247 /* From mouse or keyboard. */
    248 void
    249 wsmux_detach(n, dsc)
    250 	int n;
    251         struct device *dsc;
    252 {
    253 #ifdef DIAGNOSTIC
    254 	int error;
    255 
    256 	if (n >= nwsmux || n < 0) {
    257 		printf("wsmux_detach: detach is out of range\n");
    258 		return;
    259 	}
    260 	if ((error = wsmux_detach_sc(wsmuxdevs[n], dsc)))
    261 		printf("wsmux_detach: error=%d\n", error);
    262 #else
    263 	(void)wsmux_detach_sc(wsmuxdevs[n], dsc);
    264 #endif
    265 }
    266 
    267 int
    268 wsmux_detach_sc(sc, dsc)
    269 	struct wsmux_softc *sc;
    270         struct device *dsc;
    271 {
    272 	struct wsplink *m;
    273 	int error;
    274 
    275 	DPRINTF(("wsmux_detach_sc: %s: dsc=%p\n", sc->sc_dv.dv_xname, dsc));
    276 #ifdef DIAGNOSTIC
    277 	if (sc == 0) {
    278 		printf("wsmux_detach_sc: not allocated\n");
    279 		return (ENXIO);
    280 	}
    281 #endif
    282 
    283 	for (m = LIST_FIRST(&sc->sc_reals); m; m = LIST_NEXT(m, next)) {
    284 		if (m->sc == dsc)
    285 			break;
    286 	}
    287 #ifdef DIAGNOSTIC
    288 	if (!m) {
    289 		printf("wsmux_detach_sc: not found\n");
    290 		return (ENXIO);
    291 	}
    292 #endif
    293 	if (sc->sc_displaydv) {
    294 		if (m->sc_ops->dsetdisplay)
    295 			error = m->sc_ops->dsetdisplay(m->sc, 0);
    296 		if (error)
    297 			return (error);
    298 		*m->sc_muxp = 0;
    299 	} else if (*m->sc_muxp) {
    300 		DPRINTF(("wsmux_detach_sc: close\n"));
    301 		/* mux device is open, so close multiplexee */
    302 		m->sc_ops->dclose(m->sc, FREAD, 0, 0);
    303 		*m->sc_muxp = 0;
    304 	}
    305 
    306 	LIST_REMOVE(m, next);
    307 
    308 	free(m, M_DEVBUF);
    309 	DPRINTF(("wsmux_detach_sc: done sc=%p\n", sc));
    310 	return (0);
    311 }
    312 
    313 int
    314 wsmuxopen(dev, flags, mode, p)
    315 	dev_t dev;
    316 	int flags, mode;
    317 	struct proc *p;
    318 {
    319 	struct wsmux_softc *sc;
    320 	struct wsplink *m;
    321 	int unit, error, nopen, lasterror;
    322 
    323 	unit = minor(dev);
    324 	if (unit >= nwsmux ||	/* make sure it was attached */
    325 	    (sc = wsmuxdevs[unit]) == NULL)
    326 		return (ENXIO);
    327 
    328 	DPRINTF(("wsmuxopen: %s: sc=%p\n", sc->sc_dv.dv_xname, sc));
    329 	if (!(flags & FREAD)) {
    330 		/* Not opening for read, only ioctl is available. */
    331 		return (0);
    332 	}
    333 
    334 	if (sc->sc_events.io)
    335 		return (EBUSY);
    336 
    337 	sc->sc_events.io = p;
    338 	sc->sc_flags = flags;
    339 	sc->sc_mode = mode;
    340 	sc->sc_p = p;
    341 	wsevent_init(&sc->sc_events);		/* may cause sleep */
    342 
    343 	nopen = 0;
    344 	lasterror = 0;
    345 	for (m = LIST_FIRST(&sc->sc_reals); m; m = LIST_NEXT(m, next)) {
    346 		if (!m->sc_mevents->io && !*m->sc_muxp) {
    347 			DPRINTF(("wsmuxopen: %s: m=%p dev=%s\n",
    348 				 sc->sc_dv.dv_xname, m, m->sc->dv_xname));
    349 			error = m->sc_ops->dopen(makedev(0, m->sc->dv_unit),
    350 						 flags, mode, p);
    351 			if (error) {
    352 				/* Ignore opens that fail */
    353 				lasterror = error;
    354 				DPRINTF(("wsmuxopen: open failed %d\n",
    355 					 error));
    356 			} else {
    357 				nopen++;
    358 				*m->sc_muxp = sc;
    359 			}
    360 		}
    361 	}
    362 
    363 	if (nopen == 0 && lasterror != 0) {
    364 		wsevent_fini(&sc->sc_events);
    365 		sc->sc_events.io = NULL;
    366 		return (lasterror);
    367 	}
    368 
    369 	return (0);
    370 }
    371 
    372 int
    373 wsmuxclose(dev, flags, mode, p)
    374 	dev_t dev;
    375 	int flags, mode;
    376 	struct proc *p;
    377 {
    378 	return wsmuxdoclose(&wsmuxdevs[minor(dev)]->sc_dv, flags, mode, p);
    379 }
    380 
    381 int wsmuxdoclose(dv, flags, mode, p)
    382 	struct device *dv;
    383 	int flags, mode;
    384 	struct proc *p;
    385 {
    386 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
    387 	struct wsplink *m;
    388 
    389 	DPRINTF(("wsmuxclose: %s: sc=%p\n", sc->sc_dv.dv_xname, sc));
    390 	if (!(flags & FREAD)) {
    391 		/* Not closing read, so read still allowed. */
    392 		return 0;
    393 	}
    394 
    395 	for (m = LIST_FIRST(&sc->sc_reals); m; m = LIST_NEXT(m, next)) {
    396 		if (*m->sc_muxp == sc) {
    397 			DPRINTF(("wsmuxclose %s: m=%p dev=%s\n",
    398 				 sc->sc_dv.dv_xname, m, m->sc->dv_xname));
    399 			m->sc_ops->dclose(m->sc, flags, mode, p);
    400 			*m->sc_muxp = 0;
    401 		}
    402 	}
    403 
    404 	wsevent_fini(&sc->sc_events);
    405 	sc->sc_events.io = NULL;
    406 
    407 	return (0);
    408 }
    409 
    410 int
    411 wsmuxread(dev, uio, flags)
    412 	dev_t dev;
    413 	struct uio *uio;
    414 	int flags;
    415 {
    416 	struct wsmux_softc *sc = wsmuxdevs[minor(dev)];
    417 
    418 	if (!sc->sc_events.io)
    419 		return (EACCES);
    420 
    421 	return (wsevent_read(&sc->sc_events, uio, flags));
    422 }
    423 
    424 int
    425 wsmuxioctl(dev, cmd, data, flag, p)
    426 	dev_t dev;
    427 	u_long cmd;
    428 	caddr_t data;
    429 	int flag;
    430 	struct proc *p;
    431 {
    432 	return wsmuxdoioctl(&wsmuxdevs[minor(dev)]->sc_dv, cmd, data, flag, p);
    433 }
    434 
    435 int
    436 wsmuxdoioctl(dv, cmd, data, flag, p)
    437 	struct device *dv;
    438 	u_long cmd;
    439 	caddr_t data;
    440 	int flag;
    441 	struct proc *p;
    442 {
    443 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
    444 	struct wsplink *m;
    445 	int error, ok;
    446 	int s, put, get, n;
    447 	struct wseventvar *evar;
    448 	struct wscons_event *ev;
    449 	struct timeval xxxtime;
    450 	struct wsmux_device_list *l;
    451 
    452 	DPRINTF(("wsmuxdoioctl: %s: sc=%p, cmd=%08lx\n",
    453 		 sc->sc_dv.dv_xname, sc, cmd));
    454 
    455 	switch (cmd) {
    456 	case WSMUX_INJECTEVENT:
    457 		/* Inject an event, e.g., from moused. */
    458 		if (!sc->sc_events.io)
    459 			return (EACCES);
    460 
    461 		evar = &sc->sc_events;
    462 		s = spltty();
    463 		get = evar->get;
    464 		put = evar->put;
    465 		if (++put % WSEVENT_QSIZE == get) {
    466 			put--;
    467 			splx(s);
    468 			return (ENOSPC);
    469 		}
    470 		if (put >= WSEVENT_QSIZE)
    471 			put = 0;
    472 		ev = &evar->q[put];
    473 		*ev = *(struct wscons_event *)data;
    474 		microtime(&xxxtime);
    475 		TIMEVAL_TO_TIMESPEC(&xxxtime, &ev->time);
    476 		evar->put = put;
    477 		WSEVENT_WAKEUP(evar);
    478 		splx(s);
    479 		return (0);
    480 	case WSMUX_ADD_DEVICE:
    481 #define d ((struct wsmux_device *)data)
    482 		switch (d->type) {
    483 #if NWSMOUSE > 0
    484 		case WSMUX_MOUSE:
    485 			return (wsmouse_add_mux(d->idx, sc));
    486 #endif
    487 #if NWSKBD > 0
    488 		case WSMUX_KBD:
    489 			return (wskbd_add_mux(d->idx, sc));
    490 #endif
    491 		case WSMUX_MUX:
    492 			return (wsmux_add_mux(d->idx, sc));
    493 		default:
    494 			return (EINVAL);
    495 		}
    496 	case WSMUX_REMOVE_DEVICE:
    497 		switch (d->type) {
    498 #if NWSMOUSE > 0
    499 		case WSMUX_MOUSE:
    500 			return (wsmouse_rem_mux(d->idx, sc));
    501 #endif
    502 #if NWSKBD > 0
    503 		case WSMUX_KBD:
    504 			return (wskbd_rem_mux(d->idx, sc));
    505 #endif
    506 		case WSMUX_MUX:
    507 			return (wsmux_rem_mux(d->idx, sc));
    508 		default:
    509 			return (EINVAL);
    510 		}
    511 #undef d
    512 	case WSMUX_LIST_DEVICES:
    513 		l = (struct wsmux_device_list *)data;
    514 		for (n = 0, m = LIST_FIRST(&sc->sc_reals);
    515 		     n < WSMUX_MAXDEV && m != NULL;
    516 		     m = LIST_NEXT(m, next)) {
    517 			l->devices[n].type = m->type;
    518 			l->devices[n].idx = m->sc->dv_unit;
    519 			n++;
    520 		}
    521 		l->ndevices = n;
    522 		return (0);
    523 #ifdef WSDISPLAY_COMPAT_RAWKBD
    524 	case WSKBDIO_SETMODE:
    525 		sc->sc_rawkbd = *(int *)data;
    526 		DPRINTF(("wsmuxdoioctl: save rawkbd = %d\n", sc->sc_rawkbd));
    527 		break;
    528 #endif
    529 	default:
    530 		break;
    531 	}
    532 
    533 	if (sc->sc_events.io == NULL && sc->sc_displaydv == NULL)
    534 		return (EACCES);
    535 
    536 	/* Return 0 if any of the ioctl() succeeds, otherwise the last error */
    537 	error = 0;
    538 	ok = 0;
    539 	for (m = LIST_FIRST(&sc->sc_reals); m; m = LIST_NEXT(m, next)) {
    540 		DPRINTF(("wsmuxdoioctl: m=%p *m->sc_muxp=%p sc=%p\n",
    541 			 m, *m->sc_muxp, sc));
    542 		if (*m->sc_muxp == sc) {
    543 			DPRINTF(("wsmuxdoioctl: %s: m=%p dev=%s\n",
    544 				 sc->sc_dv.dv_xname, m, m->sc->dv_xname));
    545 			error = m->sc_ops->dioctl(m->sc, cmd, data, flag, p);
    546 			if (!error)
    547 				ok = 1;
    548 		}
    549 	}
    550 	if (ok)
    551 		error = 0;
    552 
    553 	return (error);
    554 }
    555 
    556 int
    557 wsmux_displayioctl(dv, cmd, data, flag, p)
    558 	struct device *dv;
    559 	u_long cmd;
    560 	caddr_t data;
    561 	int flag;
    562 	struct proc *p;
    563 {
    564 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
    565 	struct wsplink *m;
    566 	int error, ok;
    567 
    568 	DPRINTF(("wsmux_displayioctl: %s: sc=%p, cmd=%08lx\n",
    569 		 sc->sc_dv.dv_xname, sc, cmd));
    570 
    571 #ifdef WSDISPLAY_COMPAT_RAWKBD
    572 	if (cmd == WSKBDIO_SETMODE) {
    573 		sc->sc_rawkbd = *(int *)data;
    574 		DPRINTF(("wsmux_displayioctl: rawkbd = %d\n", sc->sc_rawkbd));
    575 	}
    576 #endif
    577 
    578 	/* Return 0 if any of the ioctl() succeeds, otherwise the last error */
    579 	error = 0;
    580 	ok = 0;
    581 	for (m = LIST_FIRST(&sc->sc_reals); m; m = LIST_NEXT(m, next)) {
    582 		DPRINTF(("wsmux_displayioctl: m=%p sc=%p sc_muxp=%p\n",
    583 			 m, sc, *m->sc_muxp));
    584 		if (m->sc_ops->ddispioctl && *m->sc_muxp == sc) {
    585 			error = m->sc_ops->ddispioctl(m->sc, cmd, data,
    586 						      flag, p);
    587 			DPRINTF(("wsmux_displayioctl: m=%p dev=%s ==> %d\n",
    588 				 m, m->sc->dv_xname, error));
    589 			if (!error)
    590 				ok = 1;
    591 		}
    592 	}
    593 	if (ok)
    594 		error = 0;
    595 
    596 	return (error);
    597 }
    598 
    599 int
    600 wsmuxpoll(dev, events, p)
    601 	dev_t dev;
    602 	int events;
    603 	struct proc *p;
    604 {
    605 	struct wsmux_softc *sc = wsmuxdevs[minor(dev)];
    606 
    607 	if (!sc->sc_events.io)
    608 		return (EACCES);
    609 
    610 	return (wsevent_poll(&sc->sc_events, events, p));
    611 }
    612 
    613 int
    614 wsmux_set_display(dv, muxsc)
    615 	struct device *dv;
    616 	struct wsmux_softc *muxsc;
    617 {
    618 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
    619 	struct wsmux_softc *nsc = muxsc ? sc : 0;
    620 	struct device *displaydv = muxsc ? muxsc->sc_displaydv : 0;
    621 	struct device *odisplaydv;
    622 	struct wsplink *m;
    623 	int error, ok;
    624 
    625 	DPRINTF(("wsmux_set_display: %s: displaydv=%p\n",
    626 		 sc->sc_dv.dv_xname, displaydv));
    627 
    628 	if (displaydv) {
    629 		if (sc->sc_displaydv)
    630 			return (EBUSY);
    631 	} else {
    632 		if (sc->sc_displaydv == NULL)
    633 			return (ENXIO);
    634 	}
    635 
    636 	odisplaydv = sc->sc_displaydv;
    637 	sc->sc_displaydv = displaydv;
    638 
    639 	if (displaydv)
    640 		printf("%s: connecting to %s\n",
    641 		       sc->sc_dv.dv_xname, displaydv->dv_xname);
    642 	ok = 0;
    643 	error = 0;
    644 	for (m = LIST_FIRST(&sc->sc_reals); m; m = LIST_NEXT(m, next)) {
    645 		if (m->sc_ops->dsetdisplay &&
    646 		    (nsc ? m->sc_mevents->io == 0 && *m->sc_muxp == 0 :
    647 		           *m->sc_muxp == sc)) {
    648 			error = m->sc_ops->dsetdisplay(m->sc, nsc);
    649 			DPRINTF(("wsmux_set_display: m=%p dev=%s error=%d\n",
    650 				 m, m->sc->dv_xname, error));
    651 			if (!error) {
    652 				ok = 1;
    653 				*m->sc_muxp = nsc;
    654 #ifdef WSDISPLAY_COMPAT_RAWKBD
    655 				DPRINTF(("wsmux_set_display: on %s set rawkbd=%d\n",
    656 					 m->sc->dv_xname, sc->sc_rawkbd));
    657 				(void)m->sc_ops->dioctl(m->sc,
    658 					     WSKBDIO_SETMODE,
    659 					     (caddr_t)&sc->sc_rawkbd,
    660 					     0, 0);
    661 #endif
    662 			}
    663 		}
    664 	}
    665 	if (ok)
    666 		error = 0;
    667 
    668 	if (displaydv == NULL)
    669 		printf("%s: disconnecting from %s\n",
    670 		       sc->sc_dv.dv_xname, odisplaydv->dv_xname);
    671 
    672 	return (error);
    673 }
    674 
    675 int
    676 wsmux_add_mux(unit, muxsc)
    677 	int unit;
    678 	struct wsmux_softc *muxsc;
    679 {
    680 	struct wsmux_softc *sc, *m;
    681 
    682 	if (unit < 0 || unit >= nwsmux || (sc = wsmuxdevs[unit]) == NULL)
    683 		return (ENXIO);
    684 
    685 	DPRINTF(("wsmux_add_mux: %s to %s\n", sc->sc_dv.dv_xname,
    686 		 muxsc->sc_dv.dv_xname));
    687 
    688 	if (sc->sc_mux || sc->sc_events.io)
    689 		return (EBUSY);
    690 
    691 	/* The mux we are adding must not be an ancestor of it. */
    692 	for (m = muxsc->sc_mux; m; m = m->sc_mux)
    693 		if (m == sc)
    694 			return (EINVAL);
    695 
    696 	return (wsmux_attach_sc(muxsc, WSMUX_MUX, &sc->sc_dv, &sc->sc_events,
    697 				&sc->sc_mux, &wsmux_muxops));
    698 }
    699 
    700 int
    701 wsmux_rem_mux(unit, muxsc)
    702 	int unit;
    703 	struct wsmux_softc *muxsc;
    704 {
    705 	struct wsmux_softc *sc;
    706 
    707 	if (unit < 0 || unit >= nwsmux || (sc = wsmuxdevs[unit]) == NULL)
    708 		return (ENXIO);
    709 
    710 	DPRINTF(("wsmux_rem_mux: %s from %s\n", sc->sc_dv.dv_xname,
    711 		 muxsc->sc_dv.dv_xname));
    712 
    713 	return (wsmux_detach_sc(muxsc, &sc->sc_dv));
    714 }
    715 
    716 #endif /* NWSMUX > 0 || (NWSDISPLAY > 0 && NWSKBD > 0) */
    717