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