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