1 /*        $NetBSD: wsmux.c,v 1.68 2025/04/07 11:18:44 hans 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.68 2025/04/07 11:18:44 hans 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           if (sc->sc_base.me_evp != NULL)
245                     return (EBUSY);
246 
247           KASSERTMSG(sc->sc_base.me_parent != NULL,
248                     "wsmux_mux_open: no parent\n");
249 
250           wsmux_do_open(sc, evar);
251 
252           return (0);
253 }
254 
255 /* Common part of opening a mux. */
256 void
wsmux_do_open(struct wsmux_softc * sc,struct wseventvar * evar)257 wsmux_do_open(struct wsmux_softc *sc, struct wseventvar *evar)
258 {
259           struct wsevsrc *me;
260           int error;
261 
262           sc->sc_base.me_evp = evar; /* remember event variable, mark as open */
263 
264           /* Open all children. */
265           TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
266                     DPRINTF(("wsmuxopen: %s: m=%p dev=%s\n",
267                                device_xname(sc->sc_base.me_dv), me,
268                                device_xname(me->me_dv)));
269 
270                     if (me->me_evp != NULL) {
271                               DPRINTF(("wsmuxopen: dev %s already in use\n",
272                                         device_xname(me->me_dv)));
273                               continue;
274                     }
275 
276                     KASSERTMSG(me->me_parent == sc,
277                               "wsmux_do_open: bad child=%p\n", me);
278 
279                     error = wsevsrc_open(me, evar);
280                     if (error) {
281                               DPRINTF(("wsmuxopen: open failed %d\n", error));
282                     }
283           }
284 }
285 
286 /*
287  * close() of the pseudo device from device table.
288  */
289 int
wsmuxclose(dev_t dev,int flags,int mode,struct lwp * l)290 wsmuxclose(dev_t dev, int flags, int mode,
291     struct lwp *l)
292 {
293           int minr = minor(dev);
294           struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
295           struct wseventvar *evar = sc->sc_base.me_evp;
296 
297           if (WSMUXCTL(minr))
298                     /* control device */
299                     return (0);
300           if (evar == NULL)
301                     /* Not open for read */
302                     return (0);
303 
304           wsmux_do_close(sc);
305           sc->sc_base.me_evp = NULL;
306           wsevent_fini(evar);
307           return (0);
308 }
309 
310 /*
311  * Close of a mux via the parent mux.
312  */
313 int
wsmux_mux_close(struct wsevsrc * me)314 wsmux_mux_close(struct wsevsrc *me)
315 {
316           wsmux_do_close((struct wsmux_softc *)me);
317           me->me_evp = NULL;
318           return (0);
319 }
320 
321 /* Common part of closing a mux. */
322 void
wsmux_do_close(struct wsmux_softc * sc)323 wsmux_do_close(struct wsmux_softc *sc)
324 {
325           struct wsevsrc *me;
326 
327           DPRINTF(("wsmuxclose: %s: sc=%p\n",
328                      device_xname(sc->sc_base.me_dv), sc));
329 
330           /* Close all the children. */
331           TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
332                     DPRINTF(("wsmuxclose %s: m=%p dev=%s\n",
333                                device_xname(sc->sc_base.me_dv), me,
334                                device_xname(me->me_dv)));
335 
336                     KASSERTMSG(me->me_parent == sc,
337                               "wsmuxclose: bad child=%p\n", me);
338 
339                     if (me->me_evp != sc->sc_base.me_evp) {
340                               DPRINTF(("wsmuxclose: dev %s opened elsewhere\n",
341                                         device_xname(me->me_dv)));
342                               continue;
343                     }
344 
345                     (void)wsevsrc_close(me);
346                     me->me_evp = NULL;
347           }
348 }
349 
350 /*
351  * read() of the pseudo device from device table.
352  */
353 int
wsmuxread(dev_t dev,struct uio * uio,int flags)354 wsmuxread(dev_t dev, struct uio *uio, int flags)
355 {
356           int minr = minor(dev);
357           struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
358           struct wseventvar *evar;
359           int error;
360 
361           if (WSMUXCTL(minr)) {
362                     /* control device */
363                     return (EINVAL);
364           }
365 
366           evar = sc->sc_base.me_evp;
367           KASSERTMSG(evar != NULL, "wsmuxread: not open\n");
368 
369           DPRINTFN(5,("wsmuxread: %s event read evar=%p\n",
370                         device_xname(sc->sc_base.me_dv), evar));
371           error = wsevent_read(evar, uio, flags);
372           DPRINTFN(5,("wsmuxread: %s event read ==> error=%d\n",
373                         device_xname(sc->sc_base.me_dv), error));
374           return (error);
375 }
376 
377 /*
378  * ioctl of the pseudo device from device table.
379  */
380 int
wsmuxioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)381 wsmuxioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
382 {
383           int u = WSMUXDEV(minor(dev));
384 
385           return wsmux_do_ioctl(wsmuxdevs[u]->sc_base.me_dv, cmd, data, flag, l);
386 }
387 
388 /*
389  * ioctl of a mux via the parent mux, continuation of wsmuxioctl().
390  */
391 int
wsmux_do_ioctl(device_t dv,u_long cmd,void * data,int flag,struct lwp * lwp)392 wsmux_do_ioctl(device_t dv, u_long cmd, void *data, int flag,
393                  struct lwp *lwp)
394 {
395           struct wsmux_softc *sc = device_private(dv);
396           struct wsevsrc *me;
397           int error, ok;
398           int s, n;
399           struct wseventvar *evar;
400           struct wscons_event event;
401           struct wsmux_device_list *l;
402 
403           DPRINTF(("wsmux_do_ioctl: %s: enter sc=%p, cmd=%08lx\n",
404                      device_xname(sc->sc_base.me_dv), sc, cmd));
405 
406           switch (cmd) {
407 #if defined(COMPAT_50) || defined(MODULAR)
408           case WSMUXIO_OINJECTEVENT:
409 #endif /* defined(COMPAT_50) || defined(MODULAR) */
410           case WSMUXIO_INJECTEVENT:
411                     /* Inject an event, e.g., from moused. */
412                     DPRINTF(("%s: inject\n", device_xname(sc->sc_base.me_dv)));
413 
414                     evar = sc->sc_base.me_evp;
415                     if (evar == NULL) {
416                               /* No event sink, so ignore it. */
417                               DPRINTF(("wsmux_do_ioctl: event ignored\n"));
418                               return (0);
419                     }
420 
421                     s = spltty();
422                     event.type = ((struct wscons_event *)data)->type;
423                     event.value = ((struct wscons_event *)data)->value;
424                     error = wsevent_inject(evar, &event, 1);
425                     splx(s);
426 
427                     return error;
428           case WSMUXIO_ADD_DEVICE:
429 #define d ((struct wsmux_device *)data)
430                     DPRINTF(("%s: add type=%d, no=%d\n",
431                                device_xname(sc->sc_base.me_dv), d->type, d->idx));
432                     switch (d->type) {
433 #if NWSMOUSE > 0
434                     case WSMUX_MOUSE:
435                               return (wsmouse_add_mux(d->idx, sc));
436 #endif
437 #if NWSKBD > 0
438                     case WSMUX_KBD:
439                               return (wskbd_add_mux(d->idx, sc));
440 #endif
441                     case WSMUX_MUX:
442                               return (wsmux_add_mux(d->idx, sc));
443                     case WSMUX_BELL:
444                               return (wsbell_add_mux(d->idx, sc));
445                     default:
446                               return (EINVAL);
447                     }
448           case WSMUXIO_REMOVE_DEVICE:
449                     DPRINTF(("%s: rem type=%d, no=%d\n",
450                                device_xname(sc->sc_base.me_dv), d->type, d->idx));
451                     /* Locate the device */
452                     TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
453                               if (me->me_ops->type == d->type &&
454                                   device_unit(me->me_dv) == d->idx) {
455                                         DPRINTF(("wsmux_do_ioctl: detach\n"));
456                                         wsmux_detach_sc(me);
457                                         return (0);
458                               }
459                     }
460                     return (EINVAL);
461 #undef d
462 
463           case WSMUXIO_LIST_DEVICES:
464                     DPRINTF(("%s: list\n", device_xname(sc->sc_base.me_dv)));
465                     l = (struct wsmux_device_list *)data;
466                     n = 0;
467                     TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
468                               if (n >= WSMUX_MAXDEV)
469                                         break;
470                               l->devices[n].type = me->me_ops->type;
471                               l->devices[n].idx = device_unit(me->me_dv);
472                               n++;
473                     }
474                     l->ndevices = n;
475                     return (0);
476 #ifdef WSDISPLAY_COMPAT_RAWKBD
477           case WSKBDIO_SETMODE:
478                     sc->sc_rawkbd = *(int *)data;
479                     DPRINTF(("wsmux_do_ioctl: save rawkbd = %d\n", sc->sc_rawkbd));
480                     break;
481 #endif
482 
483           case WSKBDIO_SETVERSION:
484           case WSMOUSEIO_SETVERSION:
485           case WSDISPLAYIO_SETVERSION:
486                     DPRINTF(("%s: WSxxxIO_SETVERSION\n",
487                               device_xname(sc->sc_base.me_dv)));
488                     evar = sc->sc_base.me_evp;
489                     if (evar == NULL)
490                               return (EINVAL);
491                     return wsevent_setversion(evar, *(int *)data);
492 
493           case FIONBIO:
494                     DPRINTF(("%s: FIONBIO\n", device_xname(sc->sc_base.me_dv)));
495                     return (0);
496 
497           case FIOASYNC:
498                     DPRINTF(("%s: FIOASYNC\n", device_xname(sc->sc_base.me_dv)));
499                     evar = sc->sc_base.me_evp;
500                     if (evar == NULL)
501                               return (EINVAL);
502                     evar->async = *(int *)data != 0;
503                     return (0);
504           case FIOSETOWN:
505                     DPRINTF(("%s: FIOSETOWN\n", device_xname(sc->sc_base.me_dv)));
506                     evar = sc->sc_base.me_evp;
507                     if (evar == NULL)
508                               return (EINVAL);
509                     if (-*(int *)data != evar->io->p_pgid
510                         && *(int *)data != evar->io->p_pid)
511                               return (EPERM);
512                     return (0);
513           case TIOCSPGRP:
514                     DPRINTF(("%s: TIOCSPGRP\n", device_xname(sc->sc_base.me_dv)));
515                     evar = sc->sc_base.me_evp;
516                     if (evar == NULL)
517                               return (EINVAL);
518                     if (*(int *)data != evar->io->p_pgid)
519                               return (EPERM);
520                     return (0);
521           default:
522                     DPRINTF(("%s: unknown\n", device_xname(sc->sc_base.me_dv)));
523                     break;
524           }
525 
526           if (sc->sc_base.me_evp == NULL
527 #if NWSDISPLAY > 0
528               && sc->sc_base.me_dispdv == NULL
529 #endif
530               )
531                     return (EACCES);
532 
533           /* Return 0 if any of the ioctl() succeeds, otherwise the last error */
534           error = 0;
535           ok = 0;
536           TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
537                     /* XXX check evp? */
538                     KASSERTMSG(me->me_parent == sc,
539                               "wsmux_do_ioctl: bad child %p\n", me);
540 
541                     error = wsevsrc_ioctl(me, cmd, data, flag, lwp);
542                     DPRINTF(("wsmux_do_ioctl: %s: me=%p dev=%s ==> %d\n",
543                                device_xname(sc->sc_base.me_dv), me,
544                                device_xname(me->me_dv), error));
545                     if (!error)
546                               ok = 1;
547           }
548           if (ok) {
549                     error = 0;
550                     if (cmd == WSKBDIO_SETENCODING) {
551                               sc->sc_kbd_layout = *((kbd_t *)data);
552                     }
553 
554           }
555 
556           return (error);
557 }
558 
559 /*
560  * poll() of the pseudo device from device table.
561  */
562 int
wsmuxpoll(dev_t dev,int events,struct lwp * l)563 wsmuxpoll(dev_t dev, int events, struct lwp *l)
564 {
565           int minr = minor(dev);
566           struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
567 
568           if (WSMUXCTL(minr)) {
569                     /* control device */
570                     return (0);
571           }
572 
573           KASSERTMSG(sc->sc_base.me_evp != NULL, "wsmuxpoll: not open\n");
574 
575           return (wsevent_poll(sc->sc_base.me_evp, events, l));
576 }
577 
578 /*
579  * kqfilter() of the pseudo device from device table.
580  */
581 int
wsmuxkqfilter(dev_t dev,struct knote * kn)582 wsmuxkqfilter(dev_t dev, struct knote *kn)
583 {
584           int minr = minor(dev);
585           struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
586 
587           if (WSMUXCTL(minr)) {
588                     /* control device */
589                     return (1);
590           }
591 
592           KASSERTMSG(sc->sc_base.me_evp == NULL, "wsmuxkqfilter: not open\n");
593 
594           return (wsevent_kqfilter(sc->sc_base.me_evp, kn));
595 }
596 
597 /*
598  * Add mux unit as a child to muxsc.
599  */
600 int
wsmux_add_mux(int unit,struct wsmux_softc * muxsc)601 wsmux_add_mux(int unit, struct wsmux_softc *muxsc)
602 {
603           struct wsmux_softc *sc, *m;
604 
605           sc = wsmux_getmux(unit);
606           if (sc == NULL)
607                     return (ENXIO);
608 
609           DPRINTF(("wsmux_add_mux: %s(%p) to %s(%p)\n",
610                      device_xname(sc->sc_base.me_dv), sc,
611                      device_xname(muxsc->sc_base.me_dv), muxsc));
612 
613           if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
614                     return (EBUSY);
615 
616           /* The mux we are adding must not be an ancestor of itself. */
617           for (m = muxsc; m != NULL ; m = m->sc_base.me_parent)
618                     if (m == sc)
619                               return (EINVAL);
620 
621           return (wsmux_attach_sc(muxsc, &sc->sc_base));
622 }
623 
624 /* Create a new mux softc. */
625 struct wsmux_softc *
wsmux_create(const char * name,int unit)626 wsmux_create(const char *name, int unit)
627 {
628           struct wsmux_softc *sc;
629 
630           /* XXX This is wrong -- should use autoconfiguration framework */
631 
632           DPRINTF(("wsmux_create: allocating\n"));
633           sc = malloc(sizeof *sc, M_DEVBUF, M_WAITOK|M_ZERO);
634           sc->sc_base.me_dv = malloc(sizeof(struct device), M_DEVBUF,
635               M_WAITOK|M_ZERO);
636           TAILQ_INIT(&sc->sc_cld);
637           snprintf(sc->sc_base.me_dv->dv_xname,
638               sizeof sc->sc_base.me_dv->dv_xname, "%s%d", name, unit);
639           sc->sc_base.me_dv->dv_private = sc;
640           sc->sc_base.me_dv->dv_unit = unit;
641           sc->sc_base.me_ops = &wsmux_srcops;
642           sc->sc_kbd_layout = KB_NONE;
643           return (sc);
644 }
645 
646 /* Attach me as a child to sc. */
647 int
wsmux_attach_sc(struct wsmux_softc * sc,struct wsevsrc * me)648 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me)
649 {
650           int error;
651 
652           if (sc == NULL)
653                     return (EINVAL);
654 
655           DPRINTF(("wsmux_attach_sc: %s(%p): type=%d\n",
656                      device_xname(sc->sc_base.me_dv), sc, me->me_ops->type));
657 
658           /* This was checked in wsmux_add_mux() */
659           KASSERT(me->me_parent == NULL);
660 
661           me->me_parent = sc;
662           TAILQ_INSERT_TAIL(&sc->sc_cld, me, me_next);
663 
664           error = 0;
665 #if NWSDISPLAY > 0
666           if (sc->sc_base.me_dispdv != NULL) {
667                     /* This is a display mux, so attach the new device to it. */
668                     DPRINTF(("wsmux_attach_sc: %s: set display %p\n",
669                                device_xname(sc->sc_base.me_dv),
670                                sc->sc_base.me_dispdv));
671                     if (me->me_ops->dsetdisplay != NULL) {
672                               error = wsevsrc_set_display(me, &sc->sc_base);
673                               /* Ignore that the console already has a display. */
674                               if (error == EBUSY)
675                                         error = 0;
676                               if (!error) {
677 #ifdef WSDISPLAY_COMPAT_RAWKBD
678                                         DPRINTF(("wsmux_attach_sc: %s set rawkbd=%d\n",
679                                                    device_xname(me->me_dv),
680                                                    sc->sc_rawkbd));
681                                         (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
682                                                                 &sc->sc_rawkbd, 0, 0);
683 #endif
684                                         if (sc->sc_kbd_layout != KB_NONE)
685                                                   (void)wsevsrc_ioctl(me,
686                                                       WSKBDIO_SETENCODING,
687                                                       &sc->sc_kbd_layout, FWRITE, 0);
688                               }
689                     }
690           }
691 #endif
692           if (sc->sc_base.me_evp != NULL) {
693                     /* Mux is open, so open the new subdevice */
694                     DPRINTF(("wsmux_attach_sc: %s: calling open of %s\n",
695                                device_xname(sc->sc_base.me_dv),
696                                device_xname(me->me_dv)));
697                     error = wsevsrc_open(me, sc->sc_base.me_evp);
698           } else {
699                     DPRINTF(("wsmux_attach_sc: %s not open\n",
700                                device_xname(sc->sc_base.me_dv)));
701           }
702 
703           if (error) {
704                     me->me_parent = NULL;
705                     TAILQ_REMOVE(&sc->sc_cld, me, me_next);
706           }
707 
708           DPRINTF(("wsmux_attach_sc: %s(%p) done, error=%d\n",
709                      device_xname(sc->sc_base.me_dv), sc, error));
710           return (error);
711 }
712 
713 /* Remove me from the parent. */
714 void
wsmux_detach_sc(struct wsevsrc * me)715 wsmux_detach_sc(struct wsevsrc *me)
716 {
717           struct wsmux_softc *sc = me->me_parent;
718 
719           DPRINTF(("wsmux_detach_sc: %s(%p) parent=%p\n",
720                      device_xname(me->me_dv), me, sc));
721 
722           KASSERTMSG(sc != NULL, "wsmux_detach_sc: %s has no parent\n",
723                     device_xname(me->me_dv));
724 
725 #if NWSDISPLAY > 0
726           if (sc->sc_base.me_dispdv != NULL) {
727                     if (me->me_ops->dsetdisplay != NULL)
728                               /* ignore error, there's nothing we can do */
729                               (void)wsevsrc_set_display(me, NULL);
730           } else
731 #endif
732           if (me->me_evp != NULL) {
733                     DPRINTF(("wsmux_detach_sc: close\n"));
734                     /* mux device is open, so close multiplexee */
735                     (void)wsevsrc_close(me);
736           }
737 
738           TAILQ_REMOVE(&sc->sc_cld, me, me_next);
739           me->me_parent = NULL;
740 
741           DPRINTF(("wsmux_detach_sc: done sc=%p\n", sc));
742 }
743 
744 /*
745  * Display ioctl() of a mux via the parent mux.
746  */
747 int
wsmux_do_displayioctl(device_t dv,u_long cmd,void * data,int flag,struct lwp * l)748 wsmux_do_displayioctl(device_t dv, u_long cmd, void *data, int flag,
749                           struct lwp *l)
750 {
751           struct wsmux_softc *sc = device_private(dv);
752           struct wsevsrc *me;
753           int error, ok;
754 
755           DPRINTF(("wsmux_displayioctl: %s: sc=%p, cmd=%08lx\n",
756                      device_xname(sc->sc_base.me_dv), sc, cmd));
757 
758 #ifdef WSDISPLAY_COMPAT_RAWKBD
759           if (cmd == WSKBDIO_SETMODE) {
760                     sc->sc_rawkbd = *(int *)data;
761                     DPRINTF(("wsmux_displayioctl: rawkbd = %d\n", sc->sc_rawkbd));
762           }
763 #endif
764 
765           /*
766            * Return 0 if any of the ioctl() succeeds, otherwise the last error.
767            * Return EPASSTHROUGH if no mux component accepts the ioctl.
768            */
769           error = EPASSTHROUGH;
770           ok = 0;
771           TAILQ_FOREACH(me, &sc->sc_cld, me_next) {
772                     DPRINTF(("wsmux_displayioctl: me=%p\n", me));
773 
774                     KASSERTMSG(me->me_parent == sc,
775                               "wsmux_displayioctl: bad child %p\n", me);
776 
777                     if (me->me_ops->ddispioctl != NULL) {
778                               error = wsevsrc_display_ioctl(me, cmd, data, flag, l);
779                               DPRINTF(("wsmux_displayioctl: me=%p dev=%s ==> %d\n",
780                                          me, device_xname(me->me_dv), error));
781                               if (!error)
782                                         ok = 1;
783                     }
784           }
785           if (ok)
786                     error = 0;
787 
788           return (error);
789 }
790 
791 #if NWSDISPLAY > 0
792 /*
793  * Set display of a mux via the parent mux.
794  */
795 int
wsmux_evsrc_set_display(device_t dv,struct wsevsrc * ame)796 wsmux_evsrc_set_display(device_t dv, struct wsevsrc *ame)
797 {
798           struct wsmux_softc *muxsc = (struct wsmux_softc *)ame;
799           struct wsmux_softc *sc = device_private(dv);
800           device_t displaydv = muxsc ? muxsc->sc_base.me_dispdv : NULL;
801 
802           DPRINTF(("wsmux_set_display: %s: displaydv=%p\n",
803                      device_xname(sc->sc_base.me_dv), displaydv));
804 
805           if (displaydv != NULL) {
806                     if (sc->sc_base.me_dispdv != NULL)
807                               return (EBUSY);
808           } else {
809                     if (sc->sc_base.me_dispdv == NULL)
810                               return (ENXIO);
811           }
812 
813           return wsmux_set_display(sc, displaydv);
814 }
815 
816 int
wsmux_set_display(struct wsmux_softc * sc,device_t displaydv)817 wsmux_set_display(struct wsmux_softc *sc, device_t displaydv)
818 {
819           device_t odisplaydv;
820           struct wsevsrc *me;
821           struct wsmux_softc *nsc = displaydv ? sc : NULL;
822           int error, ok;
823 
824           odisplaydv = sc->sc_base.me_dispdv;
825           sc->sc_base.me_dispdv = displaydv;
826 
827           if (displaydv)
828                     aprint_verbose_dev(sc->sc_base.me_dv, "connecting to %s\n",
829                            device_xname(displaydv));
830           ok = 0;
831           error = 0;
832           TAILQ_FOREACH(me, &sc->sc_cld,me_next) {
833                     KASSERTMSG(me->me_parent == sc,
834                               "wsmux_set_display: bad child parent %p\n", me);
835 
836                     if (me->me_ops->dsetdisplay != NULL) {
837                               error = wsevsrc_set_display(me, &nsc->sc_base);
838                               DPRINTF(("wsmux_set_display: m=%p dev=%s error=%d\n",
839                                          me, device_xname(me->me_dv), error));
840                               if (!error) {
841                                         ok = 1;
842 #ifdef WSDISPLAY_COMPAT_RAWKBD
843                                         DPRINTF(("wsmux_set_display: %s set rawkbd=%d\n",
844                                                    device_xname(me->me_dv), sc->sc_rawkbd));
845                                         (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
846                                                                 &sc->sc_rawkbd, 0, 0);
847 #endif
848                               }
849                     }
850           }
851           if (ok)
852                     error = 0;
853 
854           if (displaydv == NULL)
855                     aprint_verbose("%s: disconnecting from %s\n",
856                            device_xname(sc->sc_base.me_dv),
857                            device_xname(odisplaydv));
858 
859           return (error);
860 }
861 #endif /* NWSDISPLAY > 0 */
862