xref: /openbsd/sys/dev/wscons/wsmux.c (revision 4af6d148)
1 /*	$OpenBSD: wsmux.c,v 1.61 2025/01/22 15:06:56 mvs 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 
59 #include <dev/wscons/wsconsio.h>
60 #include <dev/wscons/wsksymdef.h>
61 #include <dev/wscons/wseventvar.h>
62 #include <dev/wscons/wsmuxvar.h>
63 
64 #define WSMUX_MAXDEPTH	8
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 int	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 int	wsmux_depth(struct wsmux_softc *);
108 
109 void	wsmuxattach(int);
110 
111 void	wsmux_detach_sc_locked(struct wsmux_softc *, struct wsevsrc *);
112 
113 struct wssrcops wsmux_srcops = {
114 	.type		= WSMUX_MUX,
115 	.dopen		= wsmux_mux_open,
116 	.dclose		= wsmux_mux_close,
117 	.dioctl		= wsmux_do_ioctl,
118 	.ddispioctl	= wsmux_do_displayioctl,
119 	.dsetdisplay	= wsmux_evsrc_set_display,
120 };
121 
122 /*
123  * Lock used by wsmux_add_mux() to grant exclusive access to the tree of
124  * stacked wsmux devices.
125  */
126 struct rwlock wsmux_tree_lock = RWLOCK_INITIALIZER("wsmuxtreelk");
127 
128 /* From upper level */
129 void
wsmuxattach(int n)130 wsmuxattach(int n)
131 {
132 }
133 
134 /* Keep track of all muxes that have been allocated */
135 int nwsmux = 0;
136 struct wsmux_softc **wsmuxdevs = NULL;
137 
138 /* Return mux n, create if necessary */
139 struct wsmux_softc *
wsmux_getmux(int n)140 wsmux_getmux(int n)
141 {
142 	struct wsmux_softc *sc;
143 	struct wsmux_softc **new, **old;
144 	int i;
145 
146 	if (n < 0 || n >= WSMUX_MAXDEV)
147 		return (NULL);
148 
149 	/* Make sure there is room for mux n in the table */
150 	if (n >= nwsmux) {
151 		old = wsmuxdevs;
152 		new = mallocarray(n + 1, sizeof (*wsmuxdevs),
153 		    M_DEVBUF, M_NOWAIT);
154 		if (new == NULL) {
155 			printf("wsmux_getmux: no memory for mux %d\n", n);
156 			return (NULL);
157 		}
158 		if (old != NULL)
159 			bcopy(old, new, nwsmux * sizeof(*wsmuxdevs));
160 		for (i = nwsmux; i < (n + 1); i++)
161 			new[i] = NULL;
162 		if (old != NULL)
163 			free(old, M_DEVBUF, nwsmux * sizeof(*wsmuxdevs));
164 		wsmuxdevs = new;
165 		nwsmux = n + 1;
166 	}
167 
168 	sc = wsmuxdevs[n];
169 	if (sc == NULL) {
170 		sc = wsmux_create("wsmux", n);
171 		if (sc == NULL)
172 			printf("wsmux: attach out of memory\n");
173 		wsmuxdevs[n] = sc;
174 	}
175 	return (sc);
176 }
177 
178 /*
179  * open() of the pseudo device from device table.
180  */
181 int
wsmuxopen(dev_t dev,int flags,int mode,struct proc * p)182 wsmuxopen(dev_t dev, int flags, int mode, struct proc *p)
183 {
184 	struct wsmux_softc *sc;
185 	struct wseventvar *evar;
186 	int error, unit;
187 
188 	unit = minor(dev);
189 	sc = wsmux_getmux(unit);
190 	if (sc == NULL)
191 		return (ENXIO);
192 
193 	DPRINTF(("%s: %s: sc=%p\n", __func__, sc->sc_base.me_dv.dv_xname, sc));
194 
195 	if ((flags & (FREAD | FWRITE)) == FWRITE) {
196 		/* Not opening for read, only ioctl is available. */
197 		return (0);
198 	}
199 
200 	if (sc->sc_base.me_parent != NULL) {
201 		/* Grab the mux out of the greedy hands of the parent mux. */
202 		DPRINTF(("%s: detach\n", __func__));
203 		wsmux_detach_sc(&sc->sc_base);
204 	}
205 
206 	if (sc->sc_base.me_evp != NULL)
207 		/* Already open. */
208 		return (EBUSY);
209 
210 	evar = &sc->sc_base.me_evar;
211 	if (wsevent_init_flags(evar, WSEVENT_MPSAFE))
212 		return (EBUSY);
213 #ifdef WSDISPLAY_COMPAT_RAWKBD
214 	sc->sc_rawkbd = 0;
215 #endif
216 
217 	error = wsmux_do_open(sc, evar);
218 	if (error)
219 		wsevent_fini(evar);
220 	return (error);
221 }
222 
223 /*
224  * Open of a mux via the parent mux.
225  */
226 int
wsmux_mux_open(struct wsevsrc * me,struct wseventvar * evar)227 wsmux_mux_open(struct wsevsrc *me, struct wseventvar *evar)
228 {
229 	struct wsmux_softc *sc = (struct wsmux_softc *)me;
230 
231 #ifdef DIAGNOSTIC
232 	if (sc->sc_base.me_parent == NULL) {
233 		printf("wsmux_mux_open: no parent\n");
234 		return (EINVAL);
235 	}
236 #endif
237 
238 	return (wsmux_do_open(sc, evar));
239 }
240 
241 /* Common part of opening a mux. */
242 int
wsmux_do_open(struct wsmux_softc * sc,struct wseventvar * evar)243 wsmux_do_open(struct wsmux_softc *sc, struct wseventvar *evar)
244 {
245 	struct wsevsrc *me;
246 #ifdef DIAGNOSTIC
247 	int error;
248 #endif
249 
250 	/* The device could already be attached to a mux. */
251 	if (sc->sc_base.me_evp != NULL)
252 		return (EBUSY);
253 	sc->sc_base.me_evp = evar; /* remember event variable, mark as open */
254 
255 	/* Open all children. */
256 	rw_enter_read(&sc->sc_lock);
257 	TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
258 		DPRINTF(("%s: %s: m=%p dev=%s\n", __func__,
259 			 sc->sc_base.me_dv.dv_xname, me,
260 			 me->me_dv.dv_xname));
261 #ifdef DIAGNOSTIC
262 		if (me->me_evp != NULL) {
263 			printf("wsmuxopen: dev already in use\n");
264 			continue;
265 		}
266 		if (me->me_parent != sc) {
267 			printf("wsmux_do_open: bad child=%p\n", me);
268 			continue;
269 		}
270 		error = wsevsrc_open(me, evar);
271 		if (error) {
272 			DPRINTF(("%s: open failed %d\n", __func__, error));
273 		}
274 #else
275 		/* ignore errors, failing children will not be marked open */
276 		(void)wsevsrc_open(me, evar);
277 #endif
278 	}
279 	rw_exit_read(&sc->sc_lock);
280 
281 	return (0);
282 }
283 
284 /*
285  * close() of the pseudo device from device table.
286  */
287 int
wsmuxclose(dev_t dev,int flags,int mode,struct proc * p)288 wsmuxclose(dev_t dev, int flags, int mode, struct proc *p)
289 {
290 	struct wsmux_softc *sc =
291 	    (struct wsmux_softc *)wsmuxdevs[minor(dev)];
292 	struct wseventvar *evar = sc->sc_base.me_evp;
293 
294 	if ((flags & (FREAD | FWRITE)) == FWRITE)
295 		/* Not open for read */
296 		return (0);
297 
298 	wsmux_do_close(sc);
299 	sc->sc_base.me_evp = NULL;
300 	wsevent_fini(evar);
301 	return (0);
302 }
303 
304 /*
305  * Close of a mux via the parent mux.
306  */
307 int
wsmux_mux_close(struct wsevsrc * me)308 wsmux_mux_close(struct wsevsrc *me)
309 {
310 	struct wsmux_softc *sc = (struct wsmux_softc *)me;
311 
312 	wsmux_do_close(sc);
313 	sc->sc_base.me_evp = NULL;
314 
315 	return (0);
316 }
317 
318 /* Common part of closing a mux. */
319 void
wsmux_do_close(struct wsmux_softc * sc)320 wsmux_do_close(struct wsmux_softc *sc)
321 {
322 	struct wsevsrc *me;
323 
324 	DPRINTF(("%s: %s: sc=%p\n", __func__, sc->sc_base.me_dv.dv_xname, sc));
325 
326 	/* Close all the children. */
327 	rw_enter_read(&sc->sc_lock);
328 	TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
329 		DPRINTF(("%s %s: m=%p dev=%s\n", __func__,
330 			 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname));
331 #ifdef DIAGNOSTIC
332 		if (me->me_parent != sc) {
333 			printf("wsmuxclose: bad child=%p\n", me);
334 			continue;
335 		}
336 #endif
337 		(void)wsevsrc_close(me);
338 	}
339 	rw_exit_read(&sc->sc_lock);
340 }
341 
342 /*
343  * read() of the pseudo device from device table.
344  */
345 int
wsmuxread(dev_t dev,struct uio * uio,int flags)346 wsmuxread(dev_t dev, struct uio *uio, int flags)
347 {
348 	struct wsmux_softc *sc = wsmuxdevs[minor(dev)];
349 	struct wseventvar *evar;
350 	int error;
351 
352 	evar = sc->sc_base.me_evp;
353 	if (evar == NULL) {
354 #ifdef DIAGNOSTIC
355 		/* XXX can we get here? */
356 		printf("wsmuxread: not open\n");
357 #endif
358 		return (EINVAL);
359 	}
360 
361 	DPRINTFN(5, ("%s: %s event read evar=%p\n", __func__,
362 		     sc->sc_base.me_dv.dv_xname, evar));
363 	error = wsevent_read(evar, uio, flags);
364 	DPRINTFN(5, ("%s: %s event read ==> error=%d\n", __func__,
365 		     sc->sc_base.me_dv.dv_xname, error));
366 	return (error);
367 }
368 
369 /*
370  * ioctl of the pseudo device from device table.
371  */
372 int
wsmuxioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)373 wsmuxioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
374 {
375 	return wsmux_do_ioctl(&wsmuxdevs[minor(dev)]->sc_base.me_dv, cmd, data, flag, p);
376 }
377 
378 /*
379  * ioctl of a mux via the parent mux, continuation of wsmuxioctl().
380  */
381 int
wsmux_do_ioctl(struct device * dv,u_long cmd,caddr_t data,int flag,struct proc * p)382 wsmux_do_ioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
383     struct proc *p)
384 {
385 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
386 	struct wsevsrc *me;
387 	int error, ok;
388 	int put, get, n;
389 	struct wseventvar *evar;
390 	struct wscons_event *ev;
391 	struct wsmux_device_list *l;
392 
393 	DPRINTF(("%s: %s: enter sc=%p, cmd=%08lx\n", __func__,
394 		 sc->sc_base.me_dv.dv_xname, sc, cmd));
395 
396 	switch (cmd) {
397 	case WSMUXIO_INJECTEVENT:
398 	case WSMUXIO_ADD_DEVICE:
399 	case WSMUXIO_REMOVE_DEVICE:
400 #ifdef WSDISPLAY_COMPAT_RAWKBD
401 	case WSKBDIO_SETMODE:
402 #endif
403 		if ((flag & FWRITE) == 0)
404 			return (EACCES);
405 	}
406 
407 	switch (cmd) {
408 	case WSMUXIO_INJECTEVENT:
409 		/* Inject an event, e.g., from moused. */
410 		DPRINTF(("%s: inject\n", sc->sc_base.me_dv.dv_xname));
411 		evar = sc->sc_base.me_evp;
412 		if (evar == NULL) {
413 			/* No event sink, so ignore it. */
414 			DPRINTF(("%s: event ignored\n", __func__));
415 			return (0);
416 		}
417 
418 		mtx_enter(&evar->ws_mtx);
419 		get = evar->ws_get;
420 		put = evar->ws_put;
421 		ev = &evar->ws_q[put];
422 		if (++put % WSEVENT_QSIZE == get) {
423 			mtx_leave(&evar->ws_mtx);
424 			return (ENOSPC);
425 		}
426 		if (put >= WSEVENT_QSIZE)
427 			put = 0;
428 		*ev = *(struct wscons_event *)data;
429 		nanotime(&ev->time);
430 		evar->ws_put = put;
431 		mtx_leave(&evar->ws_mtx);
432 		wsevent_wakeup(evar);
433 		return (0);
434 	case WSMUXIO_ADD_DEVICE:
435 #define d ((struct wsmux_device *)data)
436 		DPRINTF(("%s: add type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
437 			 d->type, d->idx));
438 		if (d->idx < 0)
439 			return (ENXIO);
440 		switch (d->type) {
441 #if NWSMOUSE > 0
442 		case WSMUX_MOUSE:
443 			return (wsmouse_add_mux(d->idx, sc));
444 #endif
445 #if NWSKBD > 0
446 		case WSMUX_KBD:
447 			return (wskbd_add_mux(d->idx, sc));
448 #endif
449 		case WSMUX_MUX:
450 			return (wsmux_add_mux(d->idx, sc));
451 		default:
452 			return (EINVAL);
453 		}
454 	case WSMUXIO_REMOVE_DEVICE:
455 		DPRINTF(("%s: rem type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
456 			 d->type, d->idx));
457 		/* Locate the device */
458 		rw_enter_write(&sc->sc_lock);
459 		TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
460 			if (me->me_ops->type == d->type &&
461 			    me->me_dv.dv_unit == d->idx) {
462 				DPRINTF(("%s: detach\n", __func__));
463 				wsmux_detach_sc_locked(sc, me);
464 				rw_exit_write(&sc->sc_lock);
465 				return (0);
466 			}
467 		}
468 		rw_exit_write(&sc->sc_lock);
469 		return (EINVAL);
470 #undef d
471 
472 	case WSMUXIO_LIST_DEVICES:
473 		DPRINTF(("%s: list\n", sc->sc_base.me_dv.dv_xname));
474 		l = (struct wsmux_device_list *)data;
475 		n = 0;
476 		rw_enter_read(&sc->sc_lock);
477 		TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
478 			if (n >= WSMUX_MAXDEV)
479 				break;
480 			l->devices[n].type = me->me_ops->type;
481 			l->devices[n].idx = me->me_dv.dv_unit;
482 			n++;
483 		}
484 		rw_exit_read(&sc->sc_lock);
485 		l->ndevices = n;
486 		return (0);
487 #ifdef WSDISPLAY_COMPAT_RAWKBD
488 	case WSKBDIO_SETMODE:
489 		sc->sc_rawkbd = *(int *)data;
490 		DPRINTF(("%s: save rawkbd = %d\n", __func__, sc->sc_rawkbd));
491 		break;
492 #endif
493 	case FIOASYNC:
494 		DPRINTF(("%s: FIOASYNC\n", sc->sc_base.me_dv.dv_xname));
495 		evar = sc->sc_base.me_evp;
496 		if (evar == NULL)
497 			return (EINVAL);
498 		mtx_enter(&evar->ws_mtx);
499 		evar->ws_async = *(int *)data != 0;
500 		mtx_leave(&evar->ws_mtx);
501 		return (0);
502 	case FIOGETOWN:
503 	case TIOCGPGRP:
504 		DPRINTF(("%s: getown (%lu)\n", sc->sc_base.me_dv.dv_xname,
505 			 cmd));
506 		evar = sc->sc_base.me_evp;
507 		if (evar == NULL)
508 			return (EINVAL);
509 		sigio_getown(&evar->ws_sigio, cmd, data);
510 		return (0);
511 	case FIOSETOWN:
512 	case TIOCSPGRP:
513 		DPRINTF(("%s: setown (%lu)\n", sc->sc_base.me_dv.dv_xname,
514 			 cmd));
515 		evar = sc->sc_base.me_evp;
516 		if (evar == NULL)
517 			return (EINVAL);
518 		return (sigio_setown(&evar->ws_sigio, cmd, data));
519 	default:
520 		DPRINTF(("%s: unknown\n", sc->sc_base.me_dv.dv_xname));
521 		break;
522 	}
523 
524 	if (sc->sc_base.me_evp == NULL
525 #if NWSDISPLAY > 0
526 	    && sc->sc_displaydv == NULL
527 #endif
528 	    )
529 		return (EACCES);
530 
531 	/*
532 	 * If children are attached: return 0 if any of the ioctl() succeeds,
533 	 * otherwise the last error.
534 	 */
535 	error = ENOTTY;
536 	ok = 0;
537 	rw_enter_read(&sc->sc_lock);
538 	TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
539 #ifdef DIAGNOSTIC
540 		/* XXX check evp? */
541 		if (me->me_parent != sc) {
542 			printf("wsmux_do_ioctl: bad child %p\n", me);
543 			continue;
544 		}
545 #endif
546 		error = wsevsrc_ioctl(me, cmd, data, flag, p);
547 		DPRINTF(("%s: %s: me=%p dev=%s ==> %d\n", __func__,
548 			 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname,
549 			 error));
550 		if (!error)
551 			ok = 1;
552 	}
553 	rw_exit_read(&sc->sc_lock);
554 	if (ok)
555 		error = 0;
556 
557 	return (error);
558 }
559 
560 int
wsmuxkqfilter(dev_t dev,struct knote * kn)561 wsmuxkqfilter(dev_t dev, struct knote *kn)
562 {
563 	struct wsmux_softc *sc = wsmuxdevs[minor(dev)];
564 
565 	if (sc->sc_base.me_evp == NULL)
566 		return (ENXIO);
567 	return (wsevent_kqfilter(sc->sc_base.me_evp, kn));
568 }
569 
570 /*
571  * Add mux unit as a child to muxsc.
572  */
573 int
wsmux_add_mux(int unit,struct wsmux_softc * muxsc)574 wsmux_add_mux(int unit, struct wsmux_softc *muxsc)
575 {
576 	struct wsmux_softc *sc, *m;
577 	int error;
578 	int depth = 0;
579 
580 	sc = wsmux_getmux(unit);
581 	if (sc == NULL)
582 		return (ENXIO);
583 
584 	rw_enter_write(&wsmux_tree_lock);
585 
586 	DPRINTF(("%s: %s(%p) to %s(%p)\n", __func__,
587 		 sc->sc_base.me_dv.dv_xname, sc,
588 		 muxsc->sc_base.me_dv.dv_xname, muxsc));
589 
590 	if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL) {
591 		error = EBUSY;
592 		goto out;
593 	}
594 
595 	/* The mux we are adding must not be an ancestor of itself. */
596 	for (m = muxsc; m != NULL; m = m->sc_base.me_parent) {
597 		if (m == sc) {
598 			error = EINVAL;
599 			goto out;
600 		}
601 		depth++;
602 	}
603 
604 	/*
605 	 * Limit the number of stacked wsmux devices to avoid exhausting
606 	 * the kernel stack during wsmux_do_open().
607 	 */
608 	if (depth + wsmux_depth(sc) > WSMUX_MAXDEPTH) {
609 		error = EBUSY;
610 		goto out;
611 	}
612 
613 	error = wsmux_attach_sc(muxsc, &sc->sc_base);
614 out:
615 	rw_exit_write(&wsmux_tree_lock);
616 	return (error);
617 }
618 
619 /* Create a new mux softc. */
620 struct wsmux_softc *
wsmux_create(const char * name,int unit)621 wsmux_create(const char *name, int unit)
622 {
623 	struct wsmux_softc *sc;
624 
625 	DPRINTF(("%s: allocating\n", __func__));
626 	sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT | M_ZERO);
627 	if (sc == NULL)
628 		return (NULL);
629 	TAILQ_INIT(&sc->sc_cld);
630 	rw_init_flags(&sc->sc_lock, "wsmuxlk", RWL_DUPOK);
631 	snprintf(sc->sc_base.me_dv.dv_xname, sizeof sc->sc_base.me_dv.dv_xname,
632 		 "%s%d", name, unit);
633 	sc->sc_base.me_dv.dv_unit = unit;
634 	sc->sc_base.me_ops = &wsmux_srcops;
635 	sc->sc_kbd_layout = KB_NONE;
636 	return (sc);
637 }
638 
639 /* Attach me as a child to sc. */
640 int
wsmux_attach_sc(struct wsmux_softc * sc,struct wsevsrc * me)641 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me)
642 {
643 	int error;
644 
645 	if (sc == NULL)
646 		return (EINVAL);
647 
648 	rw_enter_write(&sc->sc_lock);
649 
650 	DPRINTF(("%s: %s(%p): type=%d\n", __func__,
651 		 sc->sc_base.me_dv.dv_xname, sc, me->me_ops->type));
652 
653 #ifdef DIAGNOSTIC
654 	if (me->me_parent != NULL) {
655 		rw_exit_write(&sc->sc_lock);
656 		printf("wsmux_attach_sc: busy\n");
657 		return (EBUSY);
658 	}
659 #endif
660 	me->me_parent = sc;
661 	TAILQ_INSERT_TAIL(&sc->sc_cld, me, me_next);
662 
663 	error = 0;
664 #if NWSDISPLAY > 0
665 	if (sc->sc_displaydv != NULL) {
666 		/* This is a display mux, so attach the new device to it. */
667 		DPRINTF(("%s: %s: set display %p\n", __func__,
668 			 sc->sc_base.me_dv.dv_xname, sc->sc_displaydv));
669 		if (me->me_ops->dsetdisplay != NULL) {
670 			error = wsevsrc_set_display(me, sc->sc_displaydv);
671 			/* Ignore that the console already has a display. */
672 			if (error == EBUSY)
673 				error = 0;
674 			if (!error) {
675 #ifdef WSDISPLAY_COMPAT_RAWKBD
676 				DPRINTF(("%s: %s set rawkbd=%d\n", __func__,
677 					 me->me_dv.dv_xname, sc->sc_rawkbd));
678 				(void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
679 						    &sc->sc_rawkbd, FWRITE, 0);
680 #endif
681 			}
682 		}
683 	}
684 #endif
685 	if (sc->sc_base.me_evp != NULL) {
686 		/* Mux is open, try to open the subdevice. */
687 		error = wsevsrc_open(me, sc->sc_base.me_evp);
688 	} else {
689 		/* Mux is closed, ensure that the subdevice is also closed. */
690 		if (me->me_evp != NULL)
691 			error = EBUSY;
692 	}
693 
694 	if (error) {
695 		me->me_parent = NULL;
696 		TAILQ_REMOVE(&sc->sc_cld, me, me_next);
697 	}
698 
699 	rw_exit_write(&sc->sc_lock);
700 
701 	DPRINTF(("%s: %s(%p) done, error=%d\n", __func__,
702 		 sc->sc_base.me_dv.dv_xname, sc, error));
703 	return (error);
704 }
705 
706 /* Remove me from the parent. */
707 void
wsmux_detach_sc(struct wsevsrc * me)708 wsmux_detach_sc(struct wsevsrc *me)
709 {
710 	struct wsmux_softc *sc = me->me_parent;
711 
712 	if (sc == NULL) {
713 		printf("wsmux_detach_sc: %s has no parent\n",
714 		       me->me_dv.dv_xname);
715 		return;
716 	}
717 
718 	rw_enter_write(&sc->sc_lock);
719 	wsmux_detach_sc_locked(sc, me);
720 	rw_exit_write(&sc->sc_lock);
721 }
722 
723 void
wsmux_detach_sc_locked(struct wsmux_softc * sc,struct wsevsrc * me)724 wsmux_detach_sc_locked(struct wsmux_softc *sc, struct wsevsrc *me)
725 {
726 	rw_assert_wrlock(&sc->sc_lock);
727 
728 	DPRINTF(("%s: %s(%p) parent=%p\n", __func__,
729 		 me->me_dv.dv_xname, me, sc));
730 
731 	if (me->me_parent != sc) {
732 		/* Device detached or attached to another mux while sleeping. */
733 		return;
734 	}
735 
736 #if NWSDISPLAY > 0
737 	if (sc->sc_displaydv != NULL) {
738 		if (me->me_ops->dsetdisplay != NULL)
739 			/* ignore error, there's nothing we can do */
740 			(void)wsevsrc_set_display(me, NULL);
741 	} else
742 #endif
743 		if (me->me_evp != NULL) {
744 		DPRINTF(("%s: close\n", __func__));
745 		/* mux device is open, so close multiplexee */
746 		(void)wsevsrc_close(me);
747 	}
748 
749 	TAILQ_REMOVE(&sc->sc_cld, me, me_next);
750 	me->me_parent = NULL;
751 
752 	DPRINTF(("%s: done sc=%p\n", __func__, sc));
753 }
754 
755 /*
756  * Display ioctl() of a mux via the parent mux.
757  */
758 int
wsmux_do_displayioctl(struct device * dv,u_long cmd,caddr_t data,int flag,struct proc * p)759 wsmux_do_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
760     struct proc *p)
761 {
762 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
763 	struct wsevsrc *me;
764 	int error, ok;
765 
766 	DPRINTF(("%s: %s: sc=%p, cmd=%08lx\n", __func__,
767 		 sc->sc_base.me_dv.dv_xname, sc, cmd));
768 
769 #ifdef WSDISPLAY_COMPAT_RAWKBD
770 	if (cmd == WSKBDIO_SETMODE) {
771 		sc->sc_rawkbd = *(int *)data;
772 		DPRINTF(("%s: rawkbd = %d\n", __func__, sc->sc_rawkbd));
773 	}
774 #endif
775 
776 	/*
777 	 * Return 0 if any of the ioctl() succeeds, otherwise the last error.
778 	 * Return -1 if no mux component accepts the ioctl.
779 	 */
780 	error = -1;
781 	ok = 0;
782 	rw_enter_read(&sc->sc_lock);
783 	TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
784 		DPRINTF(("%s: me=%p\n", __func__, me));
785 #ifdef DIAGNOSTIC
786 		if (me->me_parent != sc) {
787 			printf("wsmux_displayioctl: bad child %p\n", me);
788 			continue;
789 		}
790 #endif
791 		if (me->me_ops->ddispioctl != NULL) {
792 			error = wsevsrc_display_ioctl(me, cmd, data, flag, p);
793 			DPRINTF(("%s: me=%p dev=%s ==> %d\n", __func__,
794 				 me, me->me_dv.dv_xname, error));
795 			if (!error)
796 				ok = 1;
797 		}
798 	}
799 	rw_exit_read(&sc->sc_lock);
800 	if (ok)
801 		error = 0;
802 
803 	return (error);
804 }
805 
806 #if NWSDISPLAY > 0
807 /*
808  * Set display of a mux via the parent mux.
809  */
810 int
wsmux_evsrc_set_display(struct device * dv,struct device * displaydv)811 wsmux_evsrc_set_display(struct device *dv, struct device *displaydv)
812 {
813 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
814 
815 	DPRINTF(("%s: %s: displaydv=%p\n", __func__,
816 		 sc->sc_base.me_dv.dv_xname, displaydv));
817 
818 	if (displaydv != NULL) {
819 		if (sc->sc_displaydv != NULL)
820 			return (EBUSY);
821 	} else {
822 		if (sc->sc_displaydv == NULL)
823 			return (ENXIO);
824 	}
825 
826 	return wsmux_set_display(sc, displaydv);
827 }
828 
829 int
wsmux_set_display(struct wsmux_softc * sc,struct device * displaydv)830 wsmux_set_display(struct wsmux_softc *sc, struct device *displaydv)
831 {
832 	struct device *odisplaydv;
833 	struct wsevsrc *me;
834 	struct wsmux_softc *nsc = displaydv ? sc : NULL;
835 	int error, ok;
836 
837 	rw_enter_read(&sc->sc_lock);
838 
839 	odisplaydv = sc->sc_displaydv;
840 	sc->sc_displaydv = displaydv;
841 
842 	if (displaydv) {
843 		DPRINTF(("%s: connecting to %s\n",
844 		       sc->sc_base.me_dv.dv_xname, displaydv->dv_xname));
845 	}
846 	ok = 0;
847 	error = 0;
848 	TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
849 #ifdef DIAGNOSTIC
850 		if (me->me_parent != sc) {
851 			printf("wsmux_set_display: bad child parent %p\n", me);
852 			continue;
853 		}
854 #endif
855 		if (me->me_ops->dsetdisplay != NULL) {
856 			error = wsevsrc_set_display(me,
857 			    nsc ? nsc->sc_displaydv : NULL);
858 			DPRINTF(("%s: m=%p dev=%s error=%d\n", __func__,
859 				 me, me->me_dv.dv_xname, error));
860 			if (!error) {
861 				ok = 1;
862 #ifdef WSDISPLAY_COMPAT_RAWKBD
863 				DPRINTF(("%s: %s set rawkbd=%d\n", __func__,
864 					 me->me_dv.dv_xname, sc->sc_rawkbd));
865 				(void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
866 						    &sc->sc_rawkbd, FWRITE, 0);
867 #endif
868 			}
869 		}
870 	}
871 	if (ok)
872 		error = 0;
873 
874 	if (displaydv == NULL) {
875 		DPRINTF(("%s: disconnecting from %s\n",
876 		       sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname));
877 	}
878 
879 	rw_exit_read(&sc->sc_lock);
880 
881 	return (error);
882 }
883 #endif /* NWSDISPLAY > 0 */
884 
885 uint32_t
wsmux_get_layout(struct wsmux_softc * sc)886 wsmux_get_layout(struct wsmux_softc *sc)
887 {
888 	return sc->sc_kbd_layout;
889 }
890 
891 void
wsmux_set_layout(struct wsmux_softc * sc,uint32_t layout)892 wsmux_set_layout(struct wsmux_softc *sc, uint32_t layout)
893 {
894 	if ((layout & (KB_DEFAULT | KB_NOENCODING)) == 0)
895 		sc->sc_kbd_layout = layout;
896 }
897 
898 /*
899  * Returns the depth of the longest chain of nested wsmux devices starting
900  * from sc.
901  */
902 int
wsmux_depth(struct wsmux_softc * sc)903 wsmux_depth(struct wsmux_softc *sc)
904 {
905 	struct wsevsrc *me;
906 	int depth;
907 	int maxdepth = 0;
908 
909 	rw_assert_anylock(&wsmux_tree_lock);
910 
911 	rw_enter_read(&sc->sc_lock);
912 	TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
913 		if (me->me_ops->type != WSMUX_MUX)
914 			continue;
915 
916 		depth = wsmux_depth((struct wsmux_softc *)me);
917 		if (depth > maxdepth)
918 			maxdepth = depth;
919 	}
920 	rw_exit_read(&sc->sc_lock);
921 
922 	return (maxdepth + 1);
923 }
924