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