xref: /openbsd-src/sys/dev/wscons/wsmux.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: wsmux.c,v 1.27 2014/07/12 18:48:53 tedu 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, 0);
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 	TAILQ_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 	TAILQ_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 		TAILQ_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 		TAILQ_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 	TAILQ_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 
525 	return (error);
526 }
527 
528 /*
529  * poll() of the pseudo device from device table.
530  */
531 int
532 wsmuxpoll(dev_t dev, int events, struct proc *p)
533 {
534 	struct wsmux_softc *sc = wsmuxdevs[minor(dev)];
535 
536 	if (sc->sc_base.me_evp == NULL) {
537 #ifdef DIAGNOSTIC
538 		printf("wsmuxpoll: not open\n");
539 #endif
540 		return (POLLERR);
541 	}
542 
543 	return (wsevent_poll(sc->sc_base.me_evp, events, p));
544 }
545 
546 /*
547  * Add mux unit as a child to muxsc.
548  */
549 int
550 wsmux_add_mux(int unit, struct wsmux_softc *muxsc)
551 {
552 	struct wsmux_softc *sc, *m;
553 
554 	sc = wsmux_getmux(unit);
555 	if (sc == NULL)
556 		return (ENXIO);
557 
558 	DPRINTF(("wsmux_add_mux: %s(%p) to %s(%p)\n",
559 		 sc->sc_base.me_dv.dv_xname, sc, muxsc->sc_base.me_dv.dv_xname,
560 		 muxsc));
561 
562 	if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
563 		return (EBUSY);
564 
565 	/* The mux we are adding must not be an ancestor of itself. */
566 	for (m = muxsc; m != NULL ; m = m->sc_base.me_parent)
567 		if (m == sc)
568 			return (EINVAL);
569 
570 	return (wsmux_attach_sc(muxsc, &sc->sc_base));
571 }
572 
573 /* Create a new mux softc. */
574 struct wsmux_softc *
575 wsmux_create(const char *name, int unit)
576 {
577 	struct wsmux_softc *sc;
578 
579 	DPRINTF(("wsmux_create: allocating\n"));
580 	sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT | M_ZERO);
581 	if (sc == NULL)
582 		return (NULL);
583 	TAILQ_INIT(&sc->sc_cld);
584 	snprintf(sc->sc_base.me_dv.dv_xname, sizeof sc->sc_base.me_dv.dv_xname,
585 		 "%s%d", name, unit);
586 	sc->sc_base.me_dv.dv_unit = unit;
587 	sc->sc_base.me_ops = &wsmux_srcops;
588 	sc->sc_kbd_layout = KB_NONE;
589 	return (sc);
590 }
591 
592 /* Attach me as a child to sc. */
593 int
594 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me)
595 {
596 	int error;
597 
598 	if (sc == NULL)
599 		return (EINVAL);
600 
601 	DPRINTF(("wsmux_attach_sc: %s(%p): type=%d\n",
602 		 sc->sc_base.me_dv.dv_xname, sc, me->me_ops->type));
603 
604 #ifdef DIAGNOSTIC
605 	if (me->me_parent != NULL) {
606 		printf("wsmux_attach_sc: busy\n");
607 		return (EBUSY);
608 	}
609 #endif
610 	me->me_parent = sc;
611 	TAILQ_INSERT_TAIL(&sc->sc_cld, me, me_next);
612 
613 	error = 0;
614 #if NWSDISPLAY > 0
615 	if (sc->sc_displaydv != NULL) {
616 		/* This is a display mux, so attach the new device to it. */
617 		DPRINTF(("wsmux_attach_sc: %s: set display %p\n",
618 			 sc->sc_base.me_dv.dv_xname, sc->sc_displaydv));
619 		if (me->me_ops->dsetdisplay != NULL) {
620 			error = wsevsrc_set_display(me, sc->sc_displaydv);
621 			/* Ignore that the console already has a display. */
622 			if (error == EBUSY)
623 				error = 0;
624 			if (!error) {
625 #ifdef WSDISPLAY_COMPAT_RAWKBD
626 				DPRINTF(("wsmux_attach_sc: %s set rawkbd=%d\n",
627 					 me->me_dv.dv_xname, sc->sc_rawkbd));
628 				(void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
629 						    &sc->sc_rawkbd, FWRITE, 0);
630 #endif
631 			}
632 		}
633 	}
634 #endif
635 	if (sc->sc_base.me_evp != NULL) {
636 		/* Mux is open, so open the new subdevice */
637 		DPRINTF(("wsmux_attach_sc: %s: calling open of %s\n",
638 			 sc->sc_base.me_dv.dv_xname, me->me_dv.dv_xname));
639 		error = wsevsrc_open(me, sc->sc_base.me_evp);
640 	} else {
641 		DPRINTF(("wsmux_attach_sc: %s not open\n",
642 			 sc->sc_base.me_dv.dv_xname));
643 	}
644 
645 	if (error) {
646 		me->me_parent = NULL;
647 		TAILQ_REMOVE(&sc->sc_cld, me, me_next);
648 	}
649 
650 	DPRINTF(("wsmux_attach_sc: %s(%p) done, error=%d\n",
651 		 sc->sc_base.me_dv.dv_xname, sc, error));
652 	return (error);
653 }
654 
655 /* Remove me from the parent. */
656 void
657 wsmux_detach_sc(struct wsevsrc *me)
658 {
659 	struct wsmux_softc *sc = me->me_parent;
660 
661 	DPRINTF(("wsmux_detach_sc: %s(%p) parent=%p\n",
662 		 me->me_dv.dv_xname, me, sc));
663 
664 #ifdef DIAGNOSTIC
665 	if (sc == NULL) {
666 		printf("wsmux_detach_sc: %s has no parent\n",
667 		       me->me_dv.dv_xname);
668 		return;
669 	}
670 #endif
671 
672 #if NWSDISPLAY > 0
673 	if (sc->sc_displaydv != NULL) {
674 		if (me->me_ops->dsetdisplay != NULL)
675 			/* ignore error, there's nothing we can do */
676 			(void)wsevsrc_set_display(me, NULL);
677 	} else
678 #endif
679 		if (me->me_evp != NULL) {
680 		DPRINTF(("wsmux_detach_sc: close\n"));
681 		/* mux device is open, so close multiplexee */
682 		(void)wsevsrc_close(me);
683 	}
684 
685 	TAILQ_REMOVE(&sc->sc_cld, me, me_next);
686 	me->me_parent = NULL;
687 
688 	DPRINTF(("wsmux_detach_sc: done sc=%p\n", sc));
689 }
690 
691 /*
692  * Display ioctl() of a mux via the parent mux.
693  */
694 int
695 wsmux_do_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
696     struct proc *p)
697 {
698 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
699 	struct wsevsrc *me;
700 	int error, ok;
701 
702 	DPRINTF(("wsmux_displayioctl: %s: sc=%p, cmd=%08lx\n",
703 		 sc->sc_base.me_dv.dv_xname, sc, cmd));
704 
705 #ifdef WSDISPLAY_COMPAT_RAWKBD
706 	if (cmd == WSKBDIO_SETMODE) {
707 		sc->sc_rawkbd = *(int *)data;
708 		DPRINTF(("wsmux_displayioctl: rawkbd = %d\n", sc->sc_rawkbd));
709 	}
710 #endif
711 
712 	/*
713 	 * Return 0 if any of the ioctl() succeeds, otherwise the last error.
714 	 * Return -1 if no mux component accepts the ioctl.
715 	 */
716 	error = -1;
717 	ok = 0;
718 	TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
719 		DPRINTF(("wsmux_displayioctl: me=%p\n", me));
720 #ifdef DIAGNOSTIC
721 		if (me->me_parent != sc) {
722 			printf("wsmux_displayioctl: bad child %p\n", me);
723 			continue;
724 		}
725 #endif
726 		if (me->me_ops->ddispioctl != NULL) {
727 			error = wsevsrc_display_ioctl(me, cmd, data, flag, p);
728 			DPRINTF(("wsmux_displayioctl: me=%p dev=%s ==> %d\n",
729 				 me, me->me_dv.dv_xname, error));
730 			if (!error)
731 				ok = 1;
732 		}
733 	}
734 	if (ok)
735 		error = 0;
736 
737 	return (error);
738 }
739 
740 #if NWSDISPLAY > 0
741 /*
742  * Set display of a mux via the parent mux.
743  */
744 int
745 wsmux_evsrc_set_display(struct device *dv, struct device *displaydv)
746 {
747 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
748 
749 	DPRINTF(("wsmux_evsrc_set_display: %s: displaydv=%p\n",
750 		 sc->sc_base.me_dv.dv_xname, displaydv));
751 
752 	if (displaydv != NULL) {
753 		if (sc->sc_displaydv != NULL)
754 			return (EBUSY);
755 	} else {
756 		if (sc->sc_displaydv == NULL)
757 			return (ENXIO);
758 	}
759 
760 	return wsmux_set_display(sc, displaydv);
761 }
762 
763 int
764 wsmux_set_display(struct wsmux_softc *sc, struct device *displaydv)
765 {
766 	struct device *odisplaydv;
767 	struct wsevsrc *me;
768 	struct wsmux_softc *nsc = displaydv ? sc : NULL;
769 	int error, ok;
770 
771 	odisplaydv = sc->sc_displaydv;
772 	sc->sc_displaydv = displaydv;
773 
774 	if (displaydv) {
775 		DPRINTF(("%s: connecting to %s\n",
776 		       sc->sc_base.me_dv.dv_xname, displaydv->dv_xname));
777 	}
778 	ok = 0;
779 	error = 0;
780 	TAILQ_FOREACH(me, &sc->sc_cld,me_next) {
781 #ifdef DIAGNOSTIC
782 		if (me->me_parent != sc) {
783 			printf("wsmux_set_display: bad child parent %p\n", me);
784 			continue;
785 		}
786 #endif
787 		if (me->me_ops->dsetdisplay != NULL) {
788 			error = wsevsrc_set_display(me,
789 			    nsc ? nsc->sc_displaydv : NULL);
790 			DPRINTF(("wsmux_set_display: m=%p dev=%s error=%d\n",
791 				 me, me->me_dv.dv_xname, error));
792 			if (!error) {
793 				ok = 1;
794 #ifdef WSDISPLAY_COMPAT_RAWKBD
795 				DPRINTF(("wsmux_set_display: %s set rawkbd=%d\n"
796 ,
797 					 me->me_dv.dv_xname, sc->sc_rawkbd));
798 				(void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
799 						    &sc->sc_rawkbd, FWRITE, 0);
800 #endif
801 			}
802 		}
803 	}
804 	if (ok)
805 		error = 0;
806 
807 	if (displaydv == NULL) {
808 		DPRINTF(("%s: disconnecting from %s\n",
809 		       sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname));
810 	}
811 
812 	return (error);
813 }
814 #endif /* NWSDISPLAY > 0 */
815 
816 uint32_t
817 wsmux_get_layout(struct wsmux_softc *sc)
818 {
819 	return sc->sc_kbd_layout;
820 }
821 
822 void
823 wsmux_set_layout(struct wsmux_softc *sc, uint32_t layout)
824 {
825 	if ((layout & KB_DEFAULT) == 0)
826 		sc->sc_kbd_layout = layout;
827 }
828