Merge from vendor branch PKGSRC:
[netbsd-mini2440.git] / sys / dev / wscons / wsmux.c
blobf2281bf61a473a7e1f1e3cdfe6f4dffb1edde8e6
1 /* $NetBSD: wsmux.c,v 1.52 2009/01/15 04:22:11 yamt Exp $ */
3 /*
4 * Copyright (c) 1998, 2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * Author: Lennart Augustsson <lennart@augustsson.net>
8 * Carlstedt Research & Technology
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.
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.
33 * wscons mux device.
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.
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: wsmux.c,v 1.52 2009/01/15 04:22:11 yamt Exp $");
42 #include "opt_compat_netbsd.h"
43 #include "opt_modular.h"
45 #include "wsdisplay.h"
46 #include "wsmux.h"
47 #include "wskbd.h"
48 #include "wsmouse.h"
50 #include <sys/param.h>
51 #include <sys/conf.h>
52 #include <sys/ioctl.h>
53 #include <sys/poll.h>
54 #include <sys/fcntl.h>
55 #include <sys/kernel.h>
56 #include <sys/malloc.h>
57 #include <sys/proc.h>
58 #include <sys/queue.h>
59 #include <sys/syslog.h>
60 #include <sys/systm.h>
61 #include <sys/tty.h>
62 #include <sys/signalvar.h>
63 #include <sys/device.h>
65 #include "opt_wsdisplay_compat.h"
67 #include <dev/wscons/wsconsio.h>
68 #include <dev/wscons/wsksymdef.h>
69 #include <dev/wscons/wseventvar.h>
70 #include <dev/wscons/wscons_callbacks.h>
71 #include <dev/wscons/wsmuxvar.h>
73 #ifdef WSMUX_DEBUG
74 #define DPRINTF(x) if (wsmuxdebug) printf x
75 #define DPRINTFN(n,x) if (wsmuxdebug > (n)) printf x
76 int wsmuxdebug = 0;
77 #else
78 #define DPRINTF(x)
79 #define DPRINTFN(n,x)
80 #endif
83 * The wsmux pseudo device is used to multiplex events from several wsmouse,
84 * wskbd, and/or wsmux devices together.
85 * The devices connected together form a tree with muxes in the interior
86 * and real devices (mouse and kbd) at the leaves. The special case of
87 * a tree with one node (mux or other) is supported as well.
88 * Only the device at the root of the tree can be opened (if a non-root
89 * device is opened the subtree rooted at that point is severed from the
90 * containing tree). When the root is opened it allocates a wseventvar
91 * struct which all the nodes in the tree will send their events too.
92 * An ioctl() performed on the root is propagated to all the nodes.
93 * There are also ioctl() operations to add and remove nodes from a tree.
96 static int wsmux_mux_open(struct wsevsrc *, struct wseventvar *);
97 static int wsmux_mux_close(struct wsevsrc *);
99 static void wsmux_do_open(struct wsmux_softc *, struct wseventvar *);
101 static void wsmux_do_close(struct wsmux_softc *);
102 #if NWSDISPLAY > 0
103 static int wsmux_evsrc_set_display(device_t, struct wsevsrc *);
104 #else
105 #define wsmux_evsrc_set_display NULL
106 #endif
108 static int wsmux_do_displayioctl(device_t dev, u_long cmd,
109 void *data, int flag, struct lwp *l);
110 static int wsmux_do_ioctl(device_t, u_long, void *,int,struct lwp *);
112 static int wsmux_add_mux(int, struct wsmux_softc *);
114 void wsmuxattach(int);
116 #define WSMUXDEV(n) ((n) & 0x7f)
117 #define WSMUXCTL(n) ((n) & 0x80)
119 dev_type_open(wsmuxopen);
120 dev_type_close(wsmuxclose);
121 dev_type_read(wsmuxread);
122 dev_type_ioctl(wsmuxioctl);
123 dev_type_poll(wsmuxpoll);
124 dev_type_kqfilter(wsmuxkqfilter);
126 const struct cdevsw wsmux_cdevsw = {
127 wsmuxopen, wsmuxclose, wsmuxread, nowrite, wsmuxioctl,
128 nostop, notty, wsmuxpoll, nommap, wsmuxkqfilter, D_OTHER
131 struct wssrcops wsmux_srcops = {
132 WSMUX_MUX,
133 wsmux_mux_open, wsmux_mux_close, wsmux_do_ioctl, wsmux_do_displayioctl,
134 wsmux_evsrc_set_display
137 /* From upper level */
138 void
139 wsmuxattach(int n)
143 /* Keep track of all muxes that have been allocated */
144 static int nwsmux = 0;
145 static struct wsmux_softc **wsmuxdevs;
147 /* Return mux n, create if necessary */
148 struct wsmux_softc *
149 wsmux_getmux(int n)
151 struct wsmux_softc *sc;
152 int i;
153 void *new;
155 n = WSMUXDEV(n); /* limit range */
157 /* Make sure there is room for mux n in the table */
158 if (n >= nwsmux) {
159 i = nwsmux;
160 nwsmux = n + 1;
161 if (i != 0)
162 new = realloc(wsmuxdevs, nwsmux * sizeof (*wsmuxdevs),
163 M_DEVBUF, M_NOWAIT);
164 else
165 new = malloc(nwsmux * sizeof (*wsmuxdevs),
166 M_DEVBUF, M_NOWAIT);
167 if (new == NULL) {
168 printf("wsmux_getmux: no memory for mux %d\n", n);
169 return (NULL);
171 wsmuxdevs = new;
172 for (; i < nwsmux; i++)
173 wsmuxdevs[i] = NULL;
176 sc = wsmuxdevs[n];
177 if (sc == NULL) {
178 sc = wsmux_create("wsmux", n);
179 if (sc == NULL)
180 printf("wsmux: attach out of memory\n");
181 wsmuxdevs[n] = sc;
183 return (sc);
187 * open() of the pseudo device from device table.
190 wsmuxopen(dev_t dev, int flags, int mode, struct lwp *l)
192 struct wsmux_softc *sc;
193 struct wseventvar *evar;
194 int minr, unit;
196 minr = minor(dev);
197 unit = WSMUXDEV(minr);
198 sc = wsmux_getmux(unit);
199 if (sc == NULL)
200 return (ENXIO);
202 DPRINTF(("wsmuxopen: %s: sc=%p l=%p\n",
203 device_xname(sc->sc_base.me_dv), sc, l));
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);
211 if ((flags & (FREAD | FWRITE)) == FWRITE)
212 /* Allow write only open */
213 return (0);
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);
221 if (sc->sc_base.me_evp != NULL)
222 /* Already open. */
223 return (EBUSY);
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
231 wsmux_do_open(sc, evar);
233 return (0);
237 * Open of a mux via the parent mux.
240 wsmux_mux_open(struct wsevsrc *me, struct wseventvar *evar)
242 struct wsmux_softc *sc = (struct wsmux_softc *)me;
244 #ifdef DIAGNOSTIC
245 if (sc->sc_base.me_evp != NULL) {
246 printf("wsmux_mux_open: busy\n");
247 return (EBUSY);
249 if (sc->sc_base.me_parent == NULL) {
250 printf("wsmux_mux_open: no parent\n");
251 return (EINVAL);
253 #endif
255 wsmux_do_open(sc, evar);
257 return (0);
260 /* Common part of opening a mux. */
261 void
262 wsmux_do_open(struct wsmux_softc *sc, struct wseventvar *evar)
264 struct wsevsrc *me;
266 sc->sc_base.me_evp = evar; /* remember event variable, mark as open */
268 /* Open all children. */
269 CIRCLEQ_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;
278 if (me->me_parent != sc) {
279 printf("wsmux_do_open: bad child=%p\n", me);
280 continue;
283 int error = wsevsrc_open(me, evar);
284 if (error) {
285 DPRINTF(("wsmuxopen: open failed %d\n", error));
288 #else
289 /* ignore errors, failing children will not be marked open */
290 (void)wsevsrc_open(me, evar);
291 #endif
296 * close() of the pseudo device from device table.
299 wsmuxclose(dev_t dev, int flags, int mode,
300 struct lwp *l)
302 int minr = minor(dev);
303 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
304 struct wseventvar *evar = sc->sc_base.me_evp;
306 if (WSMUXCTL(minr))
307 /* control device */
308 return (0);
309 if (evar == NULL)
310 /* Not open for read */
311 return (0);
313 wsmux_do_close(sc);
314 sc->sc_base.me_evp = NULL;
315 wsevent_fini(evar);
316 return (0);
320 * Close of a mux via the parent mux.
323 wsmux_mux_close(struct wsevsrc *me)
325 me->me_evp = NULL;
326 wsmux_do_close((struct wsmux_softc *)me);
327 return (0);
330 /* Common part of closing a mux. */
331 void
332 wsmux_do_close(struct wsmux_softc *sc)
334 struct wsevsrc *me;
336 DPRINTF(("wsmuxclose: %s: sc=%p\n",
337 device_xname(sc->sc_base.me_dv), sc));
339 /* Close all the children. */
340 CIRCLEQ_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;
349 #endif
350 (void)wsevsrc_close(me);
351 me->me_evp = NULL;
356 * read() of the pseudo device from device table.
359 wsmuxread(dev_t dev, struct uio *uio, int flags)
361 int minr = minor(dev);
362 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
363 struct wseventvar *evar;
364 int error;
366 if (WSMUXCTL(minr)) {
367 /* control device */
368 return (EINVAL);
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);
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);
389 * ioctl of the pseudo device from device table.
392 wsmuxioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
394 int u = WSMUXDEV(minor(dev));
396 return wsmux_do_ioctl(wsmuxdevs[u]->sc_base.me_dv, cmd, data, flag, l);
400 * ioctl of a mux via the parent mux, continuation of wsmuxioctl().
403 wsmux_do_ioctl(device_t dv, u_long cmd, void *data, int flag,
404 struct lwp *lwp)
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;
414 DPRINTF(("wsmux_do_ioctl: %s: enter sc=%p, cmd=%08lx\n",
415 device_xname(sc->sc_base.me_dv), sc, cmd));
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)));
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);
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);
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 default:
455 return (EINVAL);
457 case WSMUXIO_REMOVE_DEVICE:
458 DPRINTF(("%s: rem type=%d, no=%d\n",
459 device_xname(sc->sc_base.me_dv), d->type, d->idx));
460 /* Locate the device */
461 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
462 if (me->me_ops->type == d->type &&
463 device_unit(me->me_dv) == d->idx) {
464 DPRINTF(("wsmux_do_ioctl: detach\n"));
465 wsmux_detach_sc(me);
466 return (0);
469 return (EINVAL);
470 #undef d
472 case WSMUXIO_LIST_DEVICES:
473 DPRINTF(("%s: list\n", device_xname(sc->sc_base.me_dv)));
474 l = (struct wsmux_device_list *)data;
475 n = 0;
476 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
477 if (n >= WSMUX_MAXDEV)
478 break;
479 l->devices[n].type = me->me_ops->type;
480 l->devices[n].idx = device_unit(me->me_dv);
481 n++;
483 l->ndevices = n;
484 return (0);
485 #ifdef WSDISPLAY_COMPAT_RAWKBD
486 case WSKBDIO_SETMODE:
487 sc->sc_rawkbd = *(int *)data;
488 DPRINTF(("wsmux_do_ioctl: save rawkbd = %d\n", sc->sc_rawkbd));
489 break;
490 #endif
491 case FIONBIO:
492 DPRINTF(("%s: FIONBIO\n", device_xname(sc->sc_base.me_dv)));
493 return (0);
495 case FIOASYNC:
496 DPRINTF(("%s: FIOASYNC\n", device_xname(sc->sc_base.me_dv)));
497 evar = sc->sc_base.me_evp;
498 if (evar == NULL)
499 return (EINVAL);
500 evar->async = *(int *)data != 0;
501 return (0);
502 case FIOSETOWN:
503 DPRINTF(("%s: FIOSETOWN\n", device_xname(sc->sc_base.me_dv)));
504 evar = sc->sc_base.me_evp;
505 if (evar == NULL)
506 return (EINVAL);
507 if (-*(int *)data != evar->io->p_pgid
508 && *(int *)data != evar->io->p_pid)
509 return (EPERM);
510 return (0);
511 case TIOCSPGRP:
512 DPRINTF(("%s: TIOCSPGRP\n", device_xname(sc->sc_base.me_dv)));
513 evar = sc->sc_base.me_evp;
514 if (evar == NULL)
515 return (EINVAL);
516 if (*(int *)data != evar->io->p_pgid)
517 return (EPERM);
518 return (0);
519 default:
520 DPRINTF(("%s: unknown\n", device_xname(sc->sc_base.me_dv)));
521 break;
524 if (sc->sc_base.me_evp == NULL
525 #if NWSDISPLAY > 0
526 && sc->sc_base.me_dispdv == NULL
527 #endif
529 return (EACCES);
531 /* Return 0 if any of the ioctl() succeeds, otherwise the last error */
532 error = 0;
533 ok = 0;
534 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
535 #ifdef DIAGNOSTIC
536 /* XXX check evp? */
537 if (me->me_parent != sc) {
538 printf("wsmux_do_ioctl: bad child %p\n", me);
539 continue;
541 #endif
542 error = wsevsrc_ioctl(me, cmd, data, flag, lwp);
543 DPRINTF(("wsmux_do_ioctl: %s: me=%p dev=%s ==> %d\n",
544 device_xname(sc->sc_base.me_dv), me,
545 device_xname(me->me_dv), error));
546 if (!error)
547 ok = 1;
549 if (ok) {
550 error = 0;
551 if (cmd == WSKBDIO_SETENCODING) {
552 sc->sc_kbd_layout = *((kbd_t *)data);
557 return (error);
561 * poll() of the pseudo device from device table.
564 wsmuxpoll(dev_t dev, int events, struct lwp *l)
566 int minr = minor(dev);
567 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
569 if (WSMUXCTL(minr)) {
570 /* control device */
571 return (0);
574 if (sc->sc_base.me_evp == NULL) {
575 #ifdef DIAGNOSTIC
576 printf("wsmuxpoll: not open\n");
577 #endif
578 return (POLLHUP);
581 return (wsevent_poll(sc->sc_base.me_evp, events, l));
585 * kqfilter() of the pseudo device from device table.
588 wsmuxkqfilter(dev_t dev, struct knote *kn)
590 int minr = minor(dev);
591 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
593 if (WSMUXCTL(minr)) {
594 /* control device */
595 return (1);
598 if (sc->sc_base.me_evp == NULL) {
599 #ifdef DIAGNOSTIC
600 printf("wsmuxkqfilter: not open\n");
601 #endif
602 return (1);
605 return (wsevent_kqfilter(sc->sc_base.me_evp, kn));
609 * Add mux unit as a child to muxsc.
612 wsmux_add_mux(int unit, struct wsmux_softc *muxsc)
614 struct wsmux_softc *sc, *m;
616 sc = wsmux_getmux(unit);
617 if (sc == NULL)
618 return (ENXIO);
620 DPRINTF(("wsmux_add_mux: %s(%p) to %s(%p)\n",
621 device_xname(sc->sc_base.me_dv), sc,
622 device_xname(muxsc->sc_base.me_dv), muxsc));
624 if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
625 return (EBUSY);
627 /* The mux we are adding must not be an ancestor of itself. */
628 for (m = muxsc; m != NULL ; m = m->sc_base.me_parent)
629 if (m == sc)
630 return (EINVAL);
632 return (wsmux_attach_sc(muxsc, &sc->sc_base));
635 /* Create a new mux softc. */
636 struct wsmux_softc *
637 wsmux_create(const char *name, int unit)
639 struct wsmux_softc *sc;
641 /* XXX This is wrong -- should use autoconfiguraiton framework */
643 DPRINTF(("wsmux_create: allocating\n"));
644 sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT|M_ZERO);
645 if (sc == NULL)
646 return (NULL);
647 sc->sc_base.me_dv = malloc(sizeof(struct device), M_DEVBUF, M_NOWAIT|M_ZERO);
648 if (sc->sc_base.me_dv == NULL) {
649 free(sc, M_DEVBUF);
650 return NULL;
652 CIRCLEQ_INIT(&sc->sc_cld);
653 snprintf(sc->sc_base.me_dv->dv_xname, sizeof sc->sc_base.me_dv->dv_xname,
654 "%s%d", name, unit);
655 sc->sc_base.me_dv->dv_private = sc;
656 sc->sc_base.me_dv->dv_unit = unit;
657 sc->sc_base.me_ops = &wsmux_srcops;
658 sc->sc_kbd_layout = KB_NONE;
659 return (sc);
662 /* Attach me as a child to sc. */
664 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me)
666 int error;
668 if (sc == NULL)
669 return (EINVAL);
671 DPRINTF(("wsmux_attach_sc: %s(%p): type=%d\n",
672 device_xname(sc->sc_base.me_dv), sc, me->me_ops->type));
674 #ifdef DIAGNOSTIC
675 if (me->me_parent != NULL) {
676 printf("wsmux_attach_sc: busy\n");
677 return (EBUSY);
679 #endif
680 me->me_parent = sc;
681 CIRCLEQ_INSERT_TAIL(&sc->sc_cld, me, me_next);
683 error = 0;
684 #if NWSDISPLAY > 0
685 if (sc->sc_base.me_dispdv != NULL) {
686 /* This is a display mux, so attach the new device to it. */
687 DPRINTF(("wsmux_attach_sc: %s: set display %p\n",
688 device_xname(sc->sc_base.me_dv),
689 sc->sc_base.me_dispdv));
690 if (me->me_ops->dsetdisplay != NULL) {
691 error = wsevsrc_set_display(me, &sc->sc_base);
692 /* Ignore that the console already has a display. */
693 if (error == EBUSY)
694 error = 0;
695 if (!error) {
696 #ifdef WSDISPLAY_COMPAT_RAWKBD
697 DPRINTF(("wsmux_attach_sc: %s set rawkbd=%d\n",
698 device_xname(me->me_dv),
699 sc->sc_rawkbd));
700 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
701 &sc->sc_rawkbd, 0, 0);
702 #endif
703 if (sc->sc_kbd_layout != KB_NONE)
704 (void)wsevsrc_ioctl(me,
705 WSKBDIO_SETENCODING,
706 &sc->sc_kbd_layout, FWRITE, 0);
710 #endif
711 if (sc->sc_base.me_evp != NULL) {
712 /* Mux is open, so open the new subdevice */
713 DPRINTF(("wsmux_attach_sc: %s: calling open of %s\n",
714 device_xname(sc->sc_base.me_dv),
715 device_xname(me->me_dv)));
716 error = wsevsrc_open(me, sc->sc_base.me_evp);
717 } else {
718 DPRINTF(("wsmux_attach_sc: %s not open\n",
719 device_xname(sc->sc_base.me_dv)));
722 if (error) {
723 me->me_parent = NULL;
724 CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next);
727 DPRINTF(("wsmux_attach_sc: %s(%p) done, error=%d\n",
728 device_xname(sc->sc_base.me_dv), sc, error));
729 return (error);
732 /* Remove me from the parent. */
733 void
734 wsmux_detach_sc(struct wsevsrc *me)
736 struct wsmux_softc *sc = me->me_parent;
738 DPRINTF(("wsmux_detach_sc: %s(%p) parent=%p\n",
739 device_xname(me->me_dv), me, sc));
741 #ifdef DIAGNOSTIC
742 if (sc == NULL) {
743 printf("wsmux_detach_sc: %s has no parent\n",
744 device_xname(me->me_dv));
745 return;
747 #endif
749 #if NWSDISPLAY > 0
750 if (sc->sc_base.me_dispdv != NULL) {
751 if (me->me_ops->dsetdisplay != NULL)
752 /* ignore error, there's nothing we can do */
753 (void)wsevsrc_set_display(me, NULL);
754 } else
755 #endif
756 if (me->me_evp != NULL) {
757 DPRINTF(("wsmux_detach_sc: close\n"));
758 /* mux device is open, so close multiplexee */
759 (void)wsevsrc_close(me);
762 CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next);
763 me->me_parent = NULL;
765 DPRINTF(("wsmux_detach_sc: done sc=%p\n", sc));
769 * Display ioctl() of a mux via the parent mux.
772 wsmux_do_displayioctl(device_t dv, u_long cmd, void *data, int flag,
773 struct lwp *l)
775 struct wsmux_softc *sc = device_private(dv);
776 struct wsevsrc *me;
777 int error, ok;
779 DPRINTF(("wsmux_displayioctl: %s: sc=%p, cmd=%08lx\n",
780 device_xname(sc->sc_base.me_dv), sc, cmd));
782 #ifdef WSDISPLAY_COMPAT_RAWKBD
783 if (cmd == WSKBDIO_SETMODE) {
784 sc->sc_rawkbd = *(int *)data;
785 DPRINTF(("wsmux_displayioctl: rawkbd = %d\n", sc->sc_rawkbd));
787 #endif
790 * Return 0 if any of the ioctl() succeeds, otherwise the last error.
791 * Return EPASSTHROUGH if no mux component accepts the ioctl.
793 error = EPASSTHROUGH;
794 ok = 0;
795 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
796 DPRINTF(("wsmux_displayioctl: me=%p\n", me));
797 #ifdef DIAGNOSTIC
798 if (me->me_parent != sc) {
799 printf("wsmux_displayioctl: bad child %p\n", me);
800 continue;
802 #endif
803 if (me->me_ops->ddispioctl != NULL) {
804 error = wsevsrc_display_ioctl(me, cmd, data, flag, l);
805 DPRINTF(("wsmux_displayioctl: me=%p dev=%s ==> %d\n",
806 me, device_xname(me->me_dv), error));
807 if (!error)
808 ok = 1;
811 if (ok)
812 error = 0;
814 return (error);
817 #if NWSDISPLAY > 0
819 * Set display of a mux via the parent mux.
822 wsmux_evsrc_set_display(device_t dv, struct wsevsrc *ame)
824 struct wsmux_softc *muxsc = (struct wsmux_softc *)ame;
825 struct wsmux_softc *sc = device_private(dv);
826 device_t displaydv = muxsc ? muxsc->sc_base.me_dispdv : NULL;
828 DPRINTF(("wsmux_set_display: %s: displaydv=%p\n",
829 device_xname(sc->sc_base.me_dv), displaydv));
831 if (displaydv != NULL) {
832 if (sc->sc_base.me_dispdv != NULL)
833 return (EBUSY);
834 } else {
835 if (sc->sc_base.me_dispdv == NULL)
836 return (ENXIO);
839 return wsmux_set_display(sc, displaydv);
843 wsmux_set_display(struct wsmux_softc *sc, device_t displaydv)
845 device_t odisplaydv;
846 struct wsevsrc *me;
847 struct wsmux_softc *nsc = displaydv ? sc : NULL;
848 int error, ok;
850 odisplaydv = sc->sc_base.me_dispdv;
851 sc->sc_base.me_dispdv = displaydv;
853 if (displaydv)
854 aprint_verbose_dev(sc->sc_base.me_dv, "connecting to %s\n",
855 device_xname(displaydv));
856 ok = 0;
857 error = 0;
858 CIRCLEQ_FOREACH(me, &sc->sc_cld,me_next) {
859 #ifdef DIAGNOSTIC
860 if (me->me_parent != sc) {
861 printf("wsmux_set_display: bad child parent %p\n", me);
862 continue;
864 #endif
865 if (me->me_ops->dsetdisplay != NULL) {
866 error = wsevsrc_set_display(me, &nsc->sc_base);
867 DPRINTF(("wsmux_set_display: m=%p dev=%s error=%d\n",
868 me, device_xname(me->me_dv), error));
869 if (!error) {
870 ok = 1;
871 #ifdef WSDISPLAY_COMPAT_RAWKBD
872 DPRINTF(("wsmux_set_display: %s set rawkbd=%d\n",
873 device_xname(me->me_dv), sc->sc_rawkbd));
874 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
875 &sc->sc_rawkbd, 0, 0);
876 #endif
880 if (ok)
881 error = 0;
883 if (displaydv == NULL)
884 aprint_verbose("%s: disconnecting from %s\n",
885 device_xname(sc->sc_base.me_dv),
886 device_xname(odisplaydv));
888 return (error);
890 #endif /* NWSDISPLAY > 0 */