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