wsmux.c revision 1.37 1 /* $NetBSD: wsmux.c,v 1.37 2005/04/30 03:47:12 augustss Exp $ */
2
3 /*
4 * Copyright (c) 1998, 2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Author: Lennart Augustsson <lennart (at) 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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * wscons mux device.
41 *
42 * The mux device is a collection of real mice and keyboards and acts as
43 * a merge point for all the events from the different real devices.
44 */
45
46 #include <sys/cdefs.h>
47 __KERNEL_RCSID(0, "$NetBSD: wsmux.c,v 1.37 2005/04/30 03:47:12 augustss Exp $");
48
49 #include "wsdisplay.h"
50 #include "wsmux.h"
51 #include "wskbd.h"
52 #include "wsmouse.h"
53
54 #include <sys/param.h>
55 #include <sys/conf.h>
56 #include <sys/ioctl.h>
57 #include <sys/fcntl.h>
58 #include <sys/kernel.h>
59 #include <sys/malloc.h>
60 #include <sys/proc.h>
61 #include <sys/queue.h>
62 #include <sys/syslog.h>
63 #include <sys/systm.h>
64 #include <sys/tty.h>
65 #include <sys/signalvar.h>
66 #include <sys/device.h>
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 #ifdef WSMUX_DEBUG
77 #define DPRINTF(x) if (wsmuxdebug) printf x
78 #define DPRINTFN(n,x) if (wsmuxdebug > (n)) printf x
79 int wsmuxdebug = 0;
80 #else
81 #define DPRINTF(x)
82 #define DPRINTFN(n,x)
83 #endif
84
85 /*
86 * The wsmux pseudo device is used to multiplex events from several wsmouse,
87 * wskbd, and/or wsmux devices together.
88 * The devices connected together form a tree with muxes in the interior
89 * and real devices (mouse and kbd) at the leaves. The special case of
90 * a tree with one node (mux or other) is supported as well.
91 * Only the device at the root of the tree can be opened (if a non-root
92 * device is opened the subtree rooted at that point is severed from the
93 * containing tree). When the root is opened it allocates a wseventvar
94 * struct which all the nodes in the tree will send their events too.
95 * An ioctl() performed on the root is propagated to all the nodes.
96 * There are also ioctl() operations to add and remove nodes from a tree.
97 */
98
99 static int wsmux_mux_open(struct wsevsrc *, struct wseventvar *);
100 static int wsmux_mux_close(struct wsevsrc *);
101
102 static void wsmux_do_open(struct wsmux_softc *, struct wseventvar *);
103
104 static void wsmux_do_close(struct wsmux_softc *);
105 #if NWSDISPLAY > 0
106 static int wsmux_evsrc_set_display(struct device *, struct wsevsrc *);
107 #else
108 #define wsmux_evsrc_set_display NULL
109 #endif
110
111 static int wsmux_do_displayioctl(struct device *dev, u_long cmd,
112 caddr_t data, int flag, struct proc *p);
113 static int wsmux_do_ioctl(struct device *, u_long, caddr_t,int,struct proc *);
114
115 static int wsmux_add_mux(int, struct wsmux_softc *);
116
117 void wsmuxattach(int);
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 wsmuxopen, wsmuxclose, wsmuxread, nowrite, wsmuxioctl,
131 nostop, notty, wsmuxpoll, nommap, wsmuxkqfilter,
132 };
133
134 struct wssrcops wsmux_srcops = {
135 WSMUX_MUX,
136 wsmux_mux_open, wsmux_mux_close, wsmux_do_ioctl, wsmux_do_displayioctl,
137 wsmux_evsrc_set_display
138 };
139
140 /* From upper level */
141 void
142 wsmuxattach(int n)
143 {
144 }
145
146 /* Keep track of all muxes that have been allocated */
147 static int nwsmux = 0;
148 static struct wsmux_softc **wsmuxdevs;
149
150 /* Return mux n, create if necessary */
151 struct wsmux_softc *
152 wsmux_getmux(int n)
153 {
154 struct wsmux_softc *sc;
155 int i;
156 void *new;
157
158 n = WSMUXDEV(n); /* limit range */
159
160 /* Make sure there is room for mux n in the table */
161 if (n >= nwsmux) {
162 i = nwsmux;
163 nwsmux = n + 1;
164 if (i != 0)
165 new = realloc(wsmuxdevs, nwsmux * sizeof (*wsmuxdevs),
166 M_DEVBUF, M_NOWAIT);
167 else
168 new = malloc(nwsmux * sizeof (*wsmuxdevs),
169 M_DEVBUF, M_NOWAIT);
170 if (new == NULL) {
171 printf("wsmux_getmux: no memory for mux %d\n", n);
172 return (NULL);
173 }
174 wsmuxdevs = new;
175 for (; i < nwsmux; i++)
176 wsmuxdevs[i] = NULL;
177 }
178
179 sc = wsmuxdevs[n];
180 if (sc == NULL) {
181 sc = wsmux_create("wsmux", n);
182 if (sc == NULL)
183 printf("wsmux: attach out of memory\n");
184 wsmuxdevs[n] = sc;
185 }
186 return (sc);
187 }
188
189 /*
190 * open() of the pseudo device from device table.
191 */
192 int
193 wsmuxopen(dev_t dev, int flags, int mode, struct proc *p)
194 {
195 struct wsmux_softc *sc;
196 struct wseventvar *evar;
197 int minr, unit;
198
199 minr = minor(dev);
200 unit = WSMUXDEV(minr);
201 sc = wsmux_getmux(unit);
202 if (sc == NULL)
203 return (ENXIO);
204
205 DPRINTF(("wsmuxopen: %s: sc=%p p=%p\n", sc->sc_base.me_dv.dv_xname,
206 sc, p));
207
208 if (WSMUXCTL(minr)) {
209 /* This is the control device which does not allow reads. */
210 if (flags & FREAD)
211 return (EINVAL);
212 return (0);
213 }
214 if ((flags & (FREAD | FWRITE)) == FWRITE)
215 /* Allow write only open */
216 return (0);
217
218 if (sc->sc_base.me_parent != NULL) {
219 /* Grab the mux out of the greedy hands of the parent mux. */
220 DPRINTF(("wsmuxopen: detach\n"));
221 wsmux_detach_sc(&sc->sc_base);
222 }
223
224 if (sc->sc_base.me_evp != NULL)
225 /* Already open. */
226 return (EBUSY);
227
228 evar = &sc->sc_base.me_evar;
229 wsevent_init(evar);
230 evar->io = p;
231 #ifdef WSDISPLAY_COMPAT_RAWKBD
232 sc->sc_rawkbd = 0;
233 #endif
234
235 wsmux_do_open(sc, evar);
236
237 return (0);
238 }
239
240 /*
241 * Open of a mux via the parent mux.
242 */
243 int
244 wsmux_mux_open(struct wsevsrc *me, struct wseventvar *evar)
245 {
246 struct wsmux_softc *sc = (struct wsmux_softc *)me;
247
248 #ifdef DIAGNOSTIC
249 if (sc->sc_base.me_evp != NULL) {
250 printf("wsmux_mux_open: busy\n");
251 return (EBUSY);
252 }
253 if (sc->sc_base.me_parent == NULL) {
254 printf("wsmux_mux_open: no parent\n");
255 return (EINVAL);
256 }
257 #endif
258
259 wsmux_do_open(sc, evar);
260
261 return (0);
262 }
263
264 /* Common part of opening a mux. */
265 void
266 wsmux_do_open(struct wsmux_softc *sc, struct wseventvar *evar)
267 {
268 struct wsevsrc *me;
269
270 sc->sc_base.me_evp = evar; /* remember event variable, mark as open */
271
272 /* Open all children. */
273 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
274 DPRINTF(("wsmuxopen: %s: m=%p dev=%s\n",
275 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname));
276 #ifdef DIAGNOSTIC
277 if (me->me_evp != NULL) {
278 printf("wsmuxopen: dev already in use\n");
279 continue;
280 }
281 if (me->me_parent != sc) {
282 printf("wsmux_do_open: bad child=%p\n", me);
283 continue;
284 }
285 {
286 int error = wsevsrc_open(me, evar);
287 if (error) {
288 DPRINTF(("wsmuxopen: open failed %d\n", error));
289 }
290 }
291 #else
292 /* ignore errors, failing children will not be marked open */
293 (void)wsevsrc_open(me, evar);
294 #endif
295 }
296 }
297
298 /*
299 * close() of the pseudo device from device table.
300 */
301 int
302 wsmuxclose(dev_t dev, int flags, int mode, struct proc *p)
303 {
304 int minr = minor(dev);
305 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
306 struct wseventvar *evar = sc->sc_base.me_evp;
307
308 if (WSMUXCTL(minr))
309 /* control device */
310 return (0);
311 if (evar == NULL)
312 /* Not open for read */
313 return (0);
314
315 wsmux_do_close(sc);
316 sc->sc_base.me_evp = NULL;
317 wsevent_fini(evar);
318 return (0);
319 }
320
321 /*
322 * Close of a mux via the parent mux.
323 */
324 int
325 wsmux_mux_close(struct wsevsrc *me)
326 {
327 me->me_evp = NULL;
328 wsmux_do_close((struct wsmux_softc *)me);
329 return (0);
330 }
331
332 /* Common part of closing a mux. */
333 void
334 wsmux_do_close(struct wsmux_softc *sc)
335 {
336 struct wsevsrc *me;
337
338 DPRINTF(("wsmuxclose: %s: sc=%p\n", sc->sc_base.me_dv.dv_xname, sc));
339
340 /* Close all the children. */
341 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
342 DPRINTF(("wsmuxclose %s: m=%p dev=%s\n",
343 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname));
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
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 sc->sc_base.me_dv.dv_xname, evar));
382 error = wsevent_read(evar, uio, flags);
383 DPRINTFN(5,("wsmuxread: %s event read ==> error=%d\n",
384 sc->sc_base.me_dv.dv_xname, error));
385 return (error);
386 }
387
388 /*
389 * ioctl of the pseudo device from device table.
390 */
391 int
392 wsmuxioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
393 {
394 int u = WSMUXDEV(minor(dev));
395
396 return wsmux_do_ioctl(&wsmuxdevs[u]->sc_base.me_dv, cmd, data, flag, p);
397 }
398
399 /*
400 * ioctl of a mux via the parent mux, continuation of wsmuxioctl().
401 */
402 int
403 wsmux_do_ioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
404 struct proc *p)
405 {
406 struct wsmux_softc *sc = (struct wsmux_softc *)dv;
407 struct wsevsrc *me;
408 int error, ok;
409 int s, put, get, n;
410 struct wseventvar *evar;
411 struct wscons_event *ev;
412 struct timeval thistime;
413 struct wsmux_device_list *l;
414
415 DPRINTF(("wsmux_do_ioctl: %s: enter sc=%p, cmd=%08lx\n",
416 sc->sc_base.me_dv.dv_xname, sc, cmd));
417
418 switch (cmd) {
419 case WSMUXIO_INJECTEVENT:
420 /* Inject an event, e.g., from moused. */
421 DPRINTF(("%s: inject\n", sc->sc_base.me_dv.dv_xname));
422
423 evar = sc->sc_base.me_evp;
424 if (evar == NULL) {
425 /* No event sink, so ignore it. */
426 DPRINTF(("wsmux_do_ioctl: event ignored\n"));
427 return (0);
428 }
429
430 s = spltty();
431 get = evar->get;
432 put = evar->put;
433 ev = &evar->q[put];
434 if (++put % WSEVENT_QSIZE == get) {
435 put--;
436 splx(s);
437 return (ENOSPC);
438 }
439 if (put >= WSEVENT_QSIZE)
440 put = 0;
441 *ev = *(struct wscons_event *)data;
442 microtime(&thistime);
443 TIMEVAL_TO_TIMESPEC(&thistime, &ev->time);
444 evar->put = put;
445 WSEVENT_WAKEUP(evar);
446 splx(s);
447 return (0);
448 case WSMUXIO_ADD_DEVICE:
449 #define d ((struct wsmux_device *)data)
450 DPRINTF(("%s: add type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
451 d->type, d->idx));
452 switch (d->type) {
453 #if NWSMOUSE > 0
454 case WSMUX_MOUSE:
455 return (wsmouse_add_mux(d->idx, sc));
456 #endif
457 #if NWSKBD > 0
458 case WSMUX_KBD:
459 return (wskbd_add_mux(d->idx, sc));
460 #endif
461 case WSMUX_MUX:
462 return (wsmux_add_mux(d->idx, sc));
463 default:
464 return (EINVAL);
465 }
466 case WSMUXIO_REMOVE_DEVICE:
467 DPRINTF(("%s: rem type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
468 d->type, d->idx));
469 /* Locate the device */
470 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
471 if (me->me_ops->type == d->type &&
472 me->me_dv.dv_unit == d->idx) {
473 DPRINTF(("wsmux_do_ioctl: detach\n"));
474 wsmux_detach_sc(me);
475 return (0);
476 }
477 }
478 return (EINVAL);
479 #undef d
480
481 case WSMUXIO_LIST_DEVICES:
482 DPRINTF(("%s: list\n", sc->sc_base.me_dv.dv_xname));
483 l = (struct wsmux_device_list *)data;
484 n = 0;
485 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
486 if (n >= WSMUX_MAXDEV)
487 break;
488 l->devices[n].type = me->me_ops->type;
489 l->devices[n].idx = me->me_dv.dv_unit;
490 n++;
491 }
492 l->ndevices = n;
493 return (0);
494 #ifdef WSDISPLAY_COMPAT_RAWKBD
495 case WSKBDIO_SETMODE:
496 sc->sc_rawkbd = *(int *)data;
497 DPRINTF(("wsmux_do_ioctl: save rawkbd = %d\n", sc->sc_rawkbd));
498 break;
499 #endif
500 case FIONBIO:
501 DPRINTF(("%s: FIONBIO\n", sc->sc_base.me_dv.dv_xname));
502 return (0);
503
504 case FIOASYNC:
505 DPRINTF(("%s: FIOASYNC\n", sc->sc_base.me_dv.dv_xname));
506 evar = sc->sc_base.me_evp;
507 if (evar == NULL)
508 return (EINVAL);
509 evar->async = *(int *)data != 0;
510 return (0);
511 case FIOSETOWN:
512 DPRINTF(("%s: FIOSETOWN\n", sc->sc_base.me_dv.dv_xname));
513 evar = sc->sc_base.me_evp;
514 if (evar == NULL)
515 return (EINVAL);
516 if (-*(int *)data != evar->io->p_pgid
517 && *(int *)data != evar->io->p_pid)
518 return (EPERM);
519 return (0);
520 case TIOCSPGRP:
521 DPRINTF(("%s: TIOCSPGRP\n", sc->sc_base.me_dv.dv_xname));
522 evar = sc->sc_base.me_evp;
523 if (evar == NULL)
524 return (EINVAL);
525 if (*(int *)data != evar->io->p_pgid)
526 return (EPERM);
527 return (0);
528 default:
529 DPRINTF(("%s: unknown\n", sc->sc_base.me_dv.dv_xname));
530 break;
531 }
532
533 if (sc->sc_base.me_evp == NULL
534 #if NWSDISPLAY > 0
535 && sc->sc_base.me_dispdv == NULL
536 #endif
537 )
538 return (EACCES);
539
540 /* Return 0 if any of the ioctl() succeeds, otherwise the last error */
541 error = 0;
542 ok = 0;
543 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
544 #ifdef DIAGNOSTIC
545 /* XXX check evp? */
546 if (me->me_parent != sc) {
547 printf("wsmux_do_ioctl: bad child %p\n", me);
548 continue;
549 }
550 #endif
551 error = wsevsrc_ioctl(me, cmd, data, flag, p);
552 DPRINTF(("wsmux_do_ioctl: %s: me=%p dev=%s ==> %d\n",
553 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname,
554 error));
555 if (!error)
556 ok = 1;
557 }
558 if (ok) {
559 error = 0;
560 if (cmd == WSKBDIO_SETENCODING) {
561 sc->sc_kbd_layout = *((kbd_t *)data);
562 }
563
564 }
565
566 return (error);
567 }
568
569 /*
570 * poll() of the pseudo device from device table.
571 */
572 int
573 wsmuxpoll(dev_t dev, int events, struct proc *p)
574 {
575 int minr = minor(dev);
576 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
577
578 if (WSMUXCTL(minr)) {
579 /* control device */
580 return (EINVAL);
581 }
582
583 if (sc->sc_base.me_evp == NULL) {
584 #ifdef DIAGNOSTIC
585 printf("wsmuxpoll: not open\n");
586 #endif
587 return (EACCES);
588 }
589
590 return (wsevent_poll(sc->sc_base.me_evp, events, p));
591 }
592
593 /*
594 * kqfilter() of the pseudo device from device table.
595 */
596 int
597 wsmuxkqfilter(dev_t dev, struct knote *kn)
598 {
599 int minr = minor(dev);
600 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
601
602 if (WSMUXCTL(minr)) {
603 /* control device */
604 return (1);
605 }
606
607 if (sc->sc_base.me_evp == NULL) {
608 #ifdef DIAGNOSTIC
609 printf("wsmuxkqfilter: not open\n");
610 #endif
611 return (1);
612 }
613
614 return (wsevent_kqfilter(sc->sc_base.me_evp, kn));
615 }
616
617 /*
618 * Add mux unit as a child to muxsc.
619 */
620 int
621 wsmux_add_mux(int unit, struct wsmux_softc *muxsc)
622 {
623 struct wsmux_softc *sc, *m;
624
625 sc = wsmux_getmux(unit);
626 if (sc == NULL)
627 return (ENXIO);
628
629 DPRINTF(("wsmux_add_mux: %s(%p) to %s(%p)\n",
630 sc->sc_base.me_dv.dv_xname, sc, muxsc->sc_base.me_dv.dv_xname,
631 muxsc));
632
633 if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
634 return (EBUSY);
635
636 /* The mux we are adding must not be an ancestor of itself. */
637 for (m = muxsc; m != NULL ; m = m->sc_base.me_parent)
638 if (m == sc)
639 return (EINVAL);
640
641 return (wsmux_attach_sc(muxsc, &sc->sc_base));
642 }
643
644 /* Create a new mux softc. */
645 struct wsmux_softc *
646 wsmux_create(const char *name, int unit)
647 {
648 struct wsmux_softc *sc;
649
650 DPRINTF(("wsmux_create: allocating\n"));
651 sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT|M_ZERO);
652 if (sc == NULL)
653 return (NULL);
654 CIRCLEQ_INIT(&sc->sc_cld);
655 snprintf(sc->sc_base.me_dv.dv_xname, sizeof sc->sc_base.me_dv.dv_xname,
656 "%s%d", name, unit);
657 sc->sc_base.me_dv.dv_unit = unit;
658 sc->sc_base.me_ops = &wsmux_srcops;
659 sc->sc_kbd_layout = KB_NONE;
660 return (sc);
661 }
662
663 /* Attach me as a child to sc. */
664 int
665 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me)
666 {
667 int error;
668
669 if (sc == NULL)
670 return (EINVAL);
671
672 DPRINTF(("wsmux_attach_sc: %s(%p): type=%d\n",
673 sc->sc_base.me_dv.dv_xname, sc, me->me_ops->type));
674
675 #ifdef DIAGNOSTIC
676 if (me->me_parent != NULL) {
677 printf("wsmux_attach_sc: busy\n");
678 return (EBUSY);
679 }
680 #endif
681 me->me_parent = sc;
682 CIRCLEQ_INSERT_TAIL(&sc->sc_cld, me, me_next);
683
684 error = 0;
685 #if NWSDISPLAY > 0
686 if (sc->sc_base.me_dispdv != NULL) {
687 /* This is a display mux, so attach the new device to it. */
688 DPRINTF(("wsmux_attach_sc: %s: set display %p\n",
689 sc->sc_base.me_dv.dv_xname, 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 me->me_dv.dv_xname, sc->sc_rawkbd));
699 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
700 &sc->sc_rawkbd, 0, 0);
701 #endif
702 if (sc->sc_kbd_layout != KB_NONE)
703 (void)wsevsrc_ioctl(me,
704 WSKBDIO_SETENCODING,
705 &sc->sc_kbd_layout, FWRITE, 0);
706 }
707 }
708 }
709 #endif
710 if (sc->sc_base.me_evp != NULL) {
711 /* Mux is open, so open the new subdevice */
712 DPRINTF(("wsmux_attach_sc: %s: calling open of %s\n",
713 sc->sc_base.me_dv.dv_xname, me->me_dv.dv_xname));
714 error = wsevsrc_open(me, sc->sc_base.me_evp);
715 } else {
716 DPRINTF(("wsmux_attach_sc: %s not open\n",
717 sc->sc_base.me_dv.dv_xname));
718 }
719
720 if (error) {
721 me->me_parent = NULL;
722 CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next);
723 }
724
725 DPRINTF(("wsmux_attach_sc: %s(%p) done, error=%d\n",
726 sc->sc_base.me_dv.dv_xname, sc, error));
727 return (error);
728 }
729
730 /* Remove me from the parent. */
731 void
732 wsmux_detach_sc(struct wsevsrc *me)
733 {
734 struct wsmux_softc *sc = me->me_parent;
735
736 DPRINTF(("wsmux_detach_sc: %s(%p) parent=%p\n",
737 me->me_dv.dv_xname, me, sc));
738
739 #ifdef DIAGNOSTIC
740 if (sc == NULL) {
741 printf("wsmux_detach_sc: %s has no parent\n",
742 me->me_dv.dv_xname);
743 return;
744 }
745 #endif
746
747 #if NWSDISPLAY > 0
748 if (sc->sc_base.me_dispdv != NULL) {
749 if (me->me_ops->dsetdisplay != NULL)
750 /* ignore error, there's nothing we can do */
751 (void)wsevsrc_set_display(me, NULL);
752 } else
753 #endif
754 if (me->me_evp != NULL) {
755 DPRINTF(("wsmux_detach_sc: close\n"));
756 /* mux device is open, so close multiplexee */
757 (void)wsevsrc_close(me);
758 }
759
760 CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next);
761 me->me_parent = NULL;
762
763 DPRINTF(("wsmux_detach_sc: done sc=%p\n", sc));
764 }
765
766 /*
767 * Display ioctl() of a mux via the parent mux.
768 */
769 int
770 wsmux_do_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
771 struct proc *p)
772 {
773 struct wsmux_softc *sc = (struct wsmux_softc *)dv;
774 struct wsevsrc *me;
775 int error, ok;
776
777 DPRINTF(("wsmux_displayioctl: %s: sc=%p, cmd=%08lx\n",
778 sc->sc_base.me_dv.dv_xname, sc, cmd));
779
780 #ifdef WSDISPLAY_COMPAT_RAWKBD
781 if (cmd == WSKBDIO_SETMODE) {
782 sc->sc_rawkbd = *(int *)data;
783 DPRINTF(("wsmux_displayioctl: rawkbd = %d\n", sc->sc_rawkbd));
784 }
785 #endif
786
787 /*
788 * Return 0 if any of the ioctl() succeeds, otherwise the last error.
789 * Return EPASSTHROUGH if no mux component accepts the ioctl.
790 */
791 error = EPASSTHROUGH;
792 ok = 0;
793 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
794 DPRINTF(("wsmux_displayioctl: me=%p\n", me));
795 #ifdef DIAGNOSTIC
796 if (me->me_parent != sc) {
797 printf("wsmux_displayioctl: bad child %p\n", me);
798 continue;
799 }
800 #endif
801 if (me->me_ops->ddispioctl != NULL) {
802 error = wsevsrc_display_ioctl(me, cmd, data, flag, p);
803 DPRINTF(("wsmux_displayioctl: me=%p dev=%s ==> %d\n",
804 me, me->me_dv.dv_xname, error));
805 if (!error)
806 ok = 1;
807 }
808 }
809 if (ok)
810 error = 0;
811
812 return (error);
813 }
814
815 #if NWSDISPLAY > 0
816 /*
817 * Set display of a mux via the parent mux.
818 */
819 int
820 wsmux_evsrc_set_display(struct device *dv, struct wsevsrc *ame)
821 {
822 struct wsmux_softc *muxsc = (struct wsmux_softc *)ame;
823 struct wsmux_softc *sc = (struct wsmux_softc *)dv;
824 struct device *displaydv = muxsc ? muxsc->sc_base.me_dispdv : NULL;
825
826 DPRINTF(("wsmux_set_display: %s: displaydv=%p\n",
827 sc->sc_base.me_dv.dv_xname, displaydv));
828
829 if (displaydv != NULL) {
830 if (sc->sc_base.me_dispdv != NULL)
831 return (EBUSY);
832 } else {
833 if (sc->sc_base.me_dispdv == NULL)
834 return (ENXIO);
835 }
836
837 return wsmux_set_display(sc, displaydv);
838 }
839
840 int
841 wsmux_set_display(struct wsmux_softc *sc, struct device *displaydv)
842 {
843 struct device *odisplaydv;
844 struct wsevsrc *me;
845 struct wsmux_softc *nsc = displaydv ? sc : NULL;
846 int error, ok;
847
848 odisplaydv = sc->sc_base.me_dispdv;
849 sc->sc_base.me_dispdv = displaydv;
850
851 if (displaydv)
852 printf("%s: connecting to %s\n",
853 sc->sc_base.me_dv.dv_xname, displaydv->dv_xname);
854 ok = 0;
855 error = 0;
856 CIRCLEQ_FOREACH(me, &sc->sc_cld,me_next) {
857 #ifdef DIAGNOSTIC
858 if (me->me_parent != sc) {
859 printf("wsmux_set_display: bad child parent %p\n", me);
860 continue;
861 }
862 #endif
863 if (me->me_ops->dsetdisplay != NULL) {
864 error = wsevsrc_set_display(me, &nsc->sc_base);
865 DPRINTF(("wsmux_set_display: m=%p dev=%s error=%d\n",
866 me, me->me_dv.dv_xname, error));
867 if (!error) {
868 ok = 1;
869 #ifdef WSDISPLAY_COMPAT_RAWKBD
870 DPRINTF(("wsmux_set_display: %s set rawkbd=%d\n",
871 me->me_dv.dv_xname, sc->sc_rawkbd));
872 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
873 &sc->sc_rawkbd, 0, 0);
874 #endif
875 }
876 }
877 }
878 if (ok)
879 error = 0;
880
881 if (displaydv == NULL)
882 printf("%s: disconnecting from %s\n",
883 sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname);
884
885 return (error);
886 }
887 #endif /* NWSDISPLAY > 0 */
888