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