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