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