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