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