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