xref: /openbsd/sys/dev/wscons/wsmux.c (revision 666d181c)
1 /*	$OpenBSD: wsmux.c,v 1.60 2024/12/30 02:46:00 guenther 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 < 0 || 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->ws_get;
420 		put = evar->ws_put;
421 		ev = &evar->ws_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->ws_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 FIOASYNC:
495 		DPRINTF(("%s: FIOASYNC\n", sc->sc_base.me_dv.dv_xname));
496 		evar = sc->sc_base.me_evp;
497 		if (evar == NULL)
498 			return (EINVAL);
499 		evar->ws_async = *(int *)data != 0;
500 		return (0);
501 	case FIOGETOWN:
502 	case TIOCGPGRP:
503 		DPRINTF(("%s: getown (%lu)\n", sc->sc_base.me_dv.dv_xname,
504 			 cmd));
505 		evar = sc->sc_base.me_evp;
506 		if (evar == NULL)
507 			return (EINVAL);
508 		sigio_getown(&evar->ws_sigio, cmd, data);
509 		return (0);
510 	case FIOSETOWN:
511 	case TIOCSPGRP:
512 		DPRINTF(("%s: setown (%lu)\n", sc->sc_base.me_dv.dv_xname,
513 			 cmd));
514 		evar = sc->sc_base.me_evp;
515 		if (evar == NULL)
516 			return (EINVAL);
517 		return (sigio_setown(&evar->ws_sigio, cmd, data));
518 	default:
519 		DPRINTF(("%s: unknown\n", sc->sc_base.me_dv.dv_xname));
520 		break;
521 	}
522 
523 	if (sc->sc_base.me_evp == NULL
524 #if NWSDISPLAY > 0
525 	    && sc->sc_displaydv == NULL
526 #endif
527 	    )
528 		return (EACCES);
529 
530 	/*
531 	 * If children are attached: return 0 if any of the ioctl() succeeds,
532 	 * otherwise the last error.
533 	 */
534 	error = ENOTTY;
535 	ok = 0;
536 	rw_enter_read(&sc->sc_lock);
537 	TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
538 #ifdef DIAGNOSTIC
539 		/* XXX check evp? */
540 		if (me->me_parent != sc) {
541 			printf("wsmux_do_ioctl: bad child %p\n", me);
542 			continue;
543 		}
544 #endif
545 		error = wsevsrc_ioctl(me, cmd, data, flag, p);
546 		DPRINTF(("%s: %s: me=%p dev=%s ==> %d\n", __func__,
547 			 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname,
548 			 error));
549 		if (!error)
550 			ok = 1;
551 	}
552 	rw_exit_read(&sc->sc_lock);
553 	if (ok)
554 		error = 0;
555 
556 	return (error);
557 }
558 
559 int
560 wsmuxkqfilter(dev_t dev, struct knote *kn)
561 {
562 	struct wsmux_softc *sc = wsmuxdevs[minor(dev)];
563 
564 	if (sc->sc_base.me_evp == NULL)
565 		return (ENXIO);
566 	return (wsevent_kqfilter(sc->sc_base.me_evp, kn));
567 }
568 
569 /*
570  * Add mux unit as a child to muxsc.
571  */
572 int
573 wsmux_add_mux(int unit, struct wsmux_softc *muxsc)
574 {
575 	struct wsmux_softc *sc, *m;
576 	int error;
577 	int depth = 0;
578 
579 	sc = wsmux_getmux(unit);
580 	if (sc == NULL)
581 		return (ENXIO);
582 
583 	rw_enter_write(&wsmux_tree_lock);
584 
585 	DPRINTF(("%s: %s(%p) to %s(%p)\n", __func__,
586 		 sc->sc_base.me_dv.dv_xname, sc,
587 		 muxsc->sc_base.me_dv.dv_xname, muxsc));
588 
589 	if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL) {
590 		error = EBUSY;
591 		goto out;
592 	}
593 
594 	/* The mux we are adding must not be an ancestor of itself. */
595 	for (m = muxsc; m != NULL; m = m->sc_base.me_parent) {
596 		if (m == sc) {
597 			error = EINVAL;
598 			goto out;
599 		}
600 		depth++;
601 	}
602 
603 	/*
604 	 * Limit the number of stacked wsmux devices to avoid exhausting
605 	 * the kernel stack during wsmux_do_open().
606 	 */
607 	if (depth + wsmux_depth(sc) > WSMUX_MAXDEPTH) {
608 		error = EBUSY;
609 		goto out;
610 	}
611 
612 	error = wsmux_attach_sc(muxsc, &sc->sc_base);
613 out:
614 	rw_exit_write(&wsmux_tree_lock);
615 	return (error);
616 }
617 
618 /* Create a new mux softc. */
619 struct wsmux_softc *
620 wsmux_create(const char *name, int unit)
621 {
622 	struct wsmux_softc *sc;
623 
624 	DPRINTF(("%s: allocating\n", __func__));
625 	sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT | M_ZERO);
626 	if (sc == NULL)
627 		return (NULL);
628 	TAILQ_INIT(&sc->sc_cld);
629 	rw_init_flags(&sc->sc_lock, "wsmuxlk", RWL_DUPOK);
630 	snprintf(sc->sc_base.me_dv.dv_xname, sizeof sc->sc_base.me_dv.dv_xname,
631 		 "%s%d", name, unit);
632 	sc->sc_base.me_dv.dv_unit = unit;
633 	sc->sc_base.me_ops = &wsmux_srcops;
634 	sc->sc_kbd_layout = KB_NONE;
635 	return (sc);
636 }
637 
638 /* Attach me as a child to sc. */
639 int
640 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me)
641 {
642 	int error;
643 
644 	if (sc == NULL)
645 		return (EINVAL);
646 
647 	rw_enter_write(&sc->sc_lock);
648 
649 	DPRINTF(("%s: %s(%p): type=%d\n", __func__,
650 		 sc->sc_base.me_dv.dv_xname, sc, me->me_ops->type));
651 
652 #ifdef DIAGNOSTIC
653 	if (me->me_parent != NULL) {
654 		rw_exit_write(&sc->sc_lock);
655 		printf("wsmux_attach_sc: busy\n");
656 		return (EBUSY);
657 	}
658 #endif
659 	me->me_parent = sc;
660 	TAILQ_INSERT_TAIL(&sc->sc_cld, me, me_next);
661 
662 	error = 0;
663 #if NWSDISPLAY > 0
664 	if (sc->sc_displaydv != NULL) {
665 		/* This is a display mux, so attach the new device to it. */
666 		DPRINTF(("%s: %s: set display %p\n", __func__,
667 			 sc->sc_base.me_dv.dv_xname, sc->sc_displaydv));
668 		if (me->me_ops->dsetdisplay != NULL) {
669 			error = wsevsrc_set_display(me, sc->sc_displaydv);
670 			/* Ignore that the console already has a display. */
671 			if (error == EBUSY)
672 				error = 0;
673 			if (!error) {
674 #ifdef WSDISPLAY_COMPAT_RAWKBD
675 				DPRINTF(("%s: %s set rawkbd=%d\n", __func__,
676 					 me->me_dv.dv_xname, sc->sc_rawkbd));
677 				(void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
678 						    &sc->sc_rawkbd, FWRITE, 0);
679 #endif
680 			}
681 		}
682 	}
683 #endif
684 	if (sc->sc_base.me_evp != NULL) {
685 		/* Mux is open, try to open the subdevice. */
686 		error = wsevsrc_open(me, sc->sc_base.me_evp);
687 	} else {
688 		/* Mux is closed, ensure that the subdevice is also closed. */
689 		if (me->me_evp != NULL)
690 			error = EBUSY;
691 	}
692 
693 	if (error) {
694 		me->me_parent = NULL;
695 		TAILQ_REMOVE(&sc->sc_cld, me, me_next);
696 	}
697 
698 	rw_exit_write(&sc->sc_lock);
699 
700 	DPRINTF(("%s: %s(%p) done, error=%d\n", __func__,
701 		 sc->sc_base.me_dv.dv_xname, sc, error));
702 	return (error);
703 }
704 
705 /* Remove me from the parent. */
706 void
707 wsmux_detach_sc(struct wsevsrc *me)
708 {
709 	struct wsmux_softc *sc = me->me_parent;
710 
711 	if (sc == NULL) {
712 		printf("wsmux_detach_sc: %s has no parent\n",
713 		       me->me_dv.dv_xname);
714 		return;
715 	}
716 
717 	rw_enter_write(&sc->sc_lock);
718 	wsmux_detach_sc_locked(sc, me);
719 	rw_exit_write(&sc->sc_lock);
720 }
721 
722 void
723 wsmux_detach_sc_locked(struct wsmux_softc *sc, struct wsevsrc *me)
724 {
725 	rw_assert_wrlock(&sc->sc_lock);
726 
727 	DPRINTF(("%s: %s(%p) parent=%p\n", __func__,
728 		 me->me_dv.dv_xname, me, sc));
729 
730 	if (me->me_parent != sc) {
731 		/* Device detached or attached to another mux while sleeping. */
732 		return;
733 	}
734 
735 #if NWSDISPLAY > 0
736 	if (sc->sc_displaydv != NULL) {
737 		if (me->me_ops->dsetdisplay != NULL)
738 			/* ignore error, there's nothing we can do */
739 			(void)wsevsrc_set_display(me, NULL);
740 	} else
741 #endif
742 		if (me->me_evp != NULL) {
743 		DPRINTF(("%s: close\n", __func__));
744 		/* mux device is open, so close multiplexee */
745 		(void)wsevsrc_close(me);
746 	}
747 
748 	TAILQ_REMOVE(&sc->sc_cld, me, me_next);
749 	me->me_parent = NULL;
750 
751 	DPRINTF(("%s: done sc=%p\n", __func__, sc));
752 }
753 
754 /*
755  * Display ioctl() of a mux via the parent mux.
756  */
757 int
758 wsmux_do_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
759     struct proc *p)
760 {
761 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
762 	struct wsevsrc *me;
763 	int error, ok;
764 
765 	DPRINTF(("%s: %s: sc=%p, cmd=%08lx\n", __func__,
766 		 sc->sc_base.me_dv.dv_xname, sc, cmd));
767 
768 #ifdef WSDISPLAY_COMPAT_RAWKBD
769 	if (cmd == WSKBDIO_SETMODE) {
770 		sc->sc_rawkbd = *(int *)data;
771 		DPRINTF(("%s: rawkbd = %d\n", __func__, sc->sc_rawkbd));
772 	}
773 #endif
774 
775 	/*
776 	 * Return 0 if any of the ioctl() succeeds, otherwise the last error.
777 	 * Return -1 if no mux component accepts the ioctl.
778 	 */
779 	error = -1;
780 	ok = 0;
781 	rw_enter_read(&sc->sc_lock);
782 	TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
783 		DPRINTF(("%s: me=%p\n", __func__, me));
784 #ifdef DIAGNOSTIC
785 		if (me->me_parent != sc) {
786 			printf("wsmux_displayioctl: bad child %p\n", me);
787 			continue;
788 		}
789 #endif
790 		if (me->me_ops->ddispioctl != NULL) {
791 			error = wsevsrc_display_ioctl(me, cmd, data, flag, p);
792 			DPRINTF(("%s: me=%p dev=%s ==> %d\n", __func__,
793 				 me, me->me_dv.dv_xname, error));
794 			if (!error)
795 				ok = 1;
796 		}
797 	}
798 	rw_exit_read(&sc->sc_lock);
799 	if (ok)
800 		error = 0;
801 
802 	return (error);
803 }
804 
805 #if NWSDISPLAY > 0
806 /*
807  * Set display of a mux via the parent mux.
808  */
809 int
810 wsmux_evsrc_set_display(struct device *dv, struct device *displaydv)
811 {
812 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
813 
814 	DPRINTF(("%s: %s: displaydv=%p\n", __func__,
815 		 sc->sc_base.me_dv.dv_xname, displaydv));
816 
817 	if (displaydv != NULL) {
818 		if (sc->sc_displaydv != NULL)
819 			return (EBUSY);
820 	} else {
821 		if (sc->sc_displaydv == NULL)
822 			return (ENXIO);
823 	}
824 
825 	return wsmux_set_display(sc, displaydv);
826 }
827 
828 int
829 wsmux_set_display(struct wsmux_softc *sc, struct device *displaydv)
830 {
831 	struct device *odisplaydv;
832 	struct wsevsrc *me;
833 	struct wsmux_softc *nsc = displaydv ? sc : NULL;
834 	int error, ok;
835 
836 	rw_enter_read(&sc->sc_lock);
837 
838 	odisplaydv = sc->sc_displaydv;
839 	sc->sc_displaydv = displaydv;
840 
841 	if (displaydv) {
842 		DPRINTF(("%s: connecting to %s\n",
843 		       sc->sc_base.me_dv.dv_xname, displaydv->dv_xname));
844 	}
845 	ok = 0;
846 	error = 0;
847 	TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
848 #ifdef DIAGNOSTIC
849 		if (me->me_parent != sc) {
850 			printf("wsmux_set_display: bad child parent %p\n", me);
851 			continue;
852 		}
853 #endif
854 		if (me->me_ops->dsetdisplay != NULL) {
855 			error = wsevsrc_set_display(me,
856 			    nsc ? nsc->sc_displaydv : NULL);
857 			DPRINTF(("%s: m=%p dev=%s error=%d\n", __func__,
858 				 me, me->me_dv.dv_xname, error));
859 			if (!error) {
860 				ok = 1;
861 #ifdef WSDISPLAY_COMPAT_RAWKBD
862 				DPRINTF(("%s: %s set rawkbd=%d\n", __func__,
863 					 me->me_dv.dv_xname, sc->sc_rawkbd));
864 				(void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
865 						    &sc->sc_rawkbd, FWRITE, 0);
866 #endif
867 			}
868 		}
869 	}
870 	if (ok)
871 		error = 0;
872 
873 	if (displaydv == NULL) {
874 		DPRINTF(("%s: disconnecting from %s\n",
875 		       sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname));
876 	}
877 
878 	rw_exit_read(&sc->sc_lock);
879 
880 	return (error);
881 }
882 #endif /* NWSDISPLAY > 0 */
883 
884 uint32_t
885 wsmux_get_layout(struct wsmux_softc *sc)
886 {
887 	return sc->sc_kbd_layout;
888 }
889 
890 void
891 wsmux_set_layout(struct wsmux_softc *sc, uint32_t layout)
892 {
893 	if ((layout & (KB_DEFAULT | KB_NOENCODING)) == 0)
894 		sc->sc_kbd_layout = layout;
895 }
896 
897 /*
898  * Returns the depth of the longest chain of nested wsmux devices starting
899  * from sc.
900  */
901 int
902 wsmux_depth(struct wsmux_softc *sc)
903 {
904 	struct wsevsrc *me;
905 	int depth;
906 	int maxdepth = 0;
907 
908 	rw_assert_anylock(&wsmux_tree_lock);
909 
910 	rw_enter_read(&sc->sc_lock);
911 	TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
912 		if (me->me_ops->type != WSMUX_MUX)
913 			continue;
914 
915 		depth = wsmux_depth((struct wsmux_softc *)me);
916 		if (depth > maxdepth)
917 			maxdepth = depth;
918 	}
919 	rw_exit_read(&sc->sc_lock);
920 
921 	return (maxdepth + 1);
922 }
923