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