wsmux.c revision 1.30 1 /* $NetBSD: wsmux.c,v 1.30 2002/09/06 13:18:43 gehenna Exp $ */
2
3 /*
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Author: Lennart Augustsson <augustss (at) carlstedt.se>
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.30 2002/09/06 13:18:43 gehenna 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/wseventvar.h>
72 #include <dev/wscons/wscons_callbacks.h>
73 #include <dev/wscons/wsmuxvar.h>
74
75 #ifdef WSMUX_DEBUG
76 #define DPRINTF(x) if (wsmuxdebug) printf x
77 #define DPRINTFN(n,x) if (wsmuxdebug > (n)) printf x
78 int wsmuxdebug = 0;
79 #else
80 #define DPRINTF(x)
81 #define DPRINTFN(n,x)
82 #endif
83
84 /*
85 * The wsmux pseudo device is used to multiplex events from several wsmouse,
86 * wskbd, and/or wsmux devices together.
87 * The devices connected together form a tree with muxes in the interior
88 * and real devices (mouse and kbd) at the leaves. The special case of
89 * a tree with one node (mux or other) is supported as well.
90 * Only the device at the root of the tree can be opened (if a non-root
91 * device is opened the subtree rooted at that point is severed from the
92 * containing tree). When the root is opened it allocates a wseventvar
93 * struct which all the nodes in the tree will send their events too.
94 * An ioctl() performed on the root is propagated to all the nodes.
95 * There are also ioctl() operations to add and remove nodes from a tree.
96 */
97
98 static int wsmux_mux_open(struct wsevsrc *, struct wseventvar *);
99 static int wsmux_mux_close(struct wsevsrc *);
100
101 static void wsmux_do_open(struct wsmux_softc *, struct wseventvar *);
102
103 static void wsmux_do_close(struct wsmux_softc *);
104 #if NWSDISPLAY > 0
105 static int wsmux_evsrc_set_display(struct device *, struct wsevsrc *);
106 #else
107 #define wsmux_evsrc_set_display NULL
108 #endif
109
110 static int wsmux_do_displayioctl(struct device *dev, u_long cmd,
111 caddr_t data, int flag, struct proc *p);
112 static int wsmux_do_ioctl(struct device *, u_long, caddr_t,int,struct proc *);
113
114 static int wsmux_add_mux(int, struct wsmux_softc *);
115
116 void wsmuxattach(int);
117
118 #define WSMUXDEV(n) ((n) & 0x7f)
119 #define WSMUXCTL(n) ((n) & 0x80)
120
121 dev_type_open(wsmuxopen);
122 dev_type_close(wsmuxclose);
123 dev_type_read(wsmuxread);
124 dev_type_ioctl(wsmuxioctl);
125 dev_type_poll(wsmuxpoll);
126
127 const struct cdevsw wsmux_cdevsw = {
128 wsmuxopen, wsmuxclose, wsmuxread, nowrite, wsmuxioctl,
129 nostop, notty, wsmuxpoll, nommap,
130 };
131
132 struct wssrcops wsmux_srcops = {
133 WSMUX_MUX,
134 wsmux_mux_open, wsmux_mux_close, wsmux_do_ioctl, wsmux_do_displayioctl,
135 wsmux_evsrc_set_display
136 };
137
138 /* From upper level */
139 void
140 wsmuxattach(int n)
141 {
142 }
143
144 /* Keep track of all muxes that have been allocated */
145 static int nwsmux = 0;
146 static struct wsmux_softc **wsmuxdevs;
147
148 /* Return mux n, create if necessary */
149 struct wsmux_softc *
150 wsmux_getmux(int n)
151 {
152 struct wsmux_softc *sc;
153 int i;
154 void *new;
155
156 n = WSMUXDEV(n); /* limit range */
157
158 /* Make sure there is room for mux n in the table */
159 if (n >= nwsmux) {
160 i = nwsmux;
161 nwsmux = n + 1;
162 if (i != 0)
163 new = realloc(wsmuxdevs, nwsmux * sizeof (*wsmuxdevs),
164 M_DEVBUF, M_NOWAIT);
165 else
166 new = malloc(nwsmux * sizeof (*wsmuxdevs),
167 M_DEVBUF, M_NOWAIT);
168 if (new == NULL) {
169 printf("wsmux_getmux: no memory for mux %d\n", n);
170 return (NULL);
171 }
172 wsmuxdevs = new;
173 for (; i < nwsmux; i++)
174 wsmuxdevs[i] = NULL;
175 }
176
177 sc = wsmuxdevs[n];
178 if (sc == NULL) {
179 sc = wsmux_create("wsmux", n);
180 if (sc == NULL)
181 printf("wsmux: attach out of memory\n");
182 wsmuxdevs[n] = sc;
183 }
184 return (sc);
185 }
186
187 /*
188 * open() of the pseudo device from device table.
189 */
190 int
191 wsmuxopen(dev_t dev, int flags, int mode, struct proc *p)
192 {
193 struct wsmux_softc *sc;
194 struct wseventvar *evar;
195 int minr, unit;
196
197 minr = minor(dev);
198 unit = WSMUXDEV(minr);
199 sc = wsmux_getmux(unit);
200 if (sc == NULL)
201 return (ENXIO);
202
203 DPRINTF(("wsmuxopen: %s: sc=%p p=%p\n", sc->sc_base.me_dv.dv_xname,
204 sc, p));
205
206 if (WSMUXCTL(minr)) {
207 /* This is the control device which does not allow reads. */
208 if (flags & FREAD)
209 return (EINVAL);
210 return (0);
211 }
212 if ((flags & (FREAD | FWRITE)) == FWRITE)
213 /* Allow write only open */
214 return (0);
215
216 if (sc->sc_base.me_parent != NULL) {
217 /* Grab the mux out of the greedy hands of the parent mux. */
218 DPRINTF(("wsmuxopen: detach\n"));
219 wsmux_detach_sc(&sc->sc_base);
220 }
221
222 if (sc->sc_base.me_evp != NULL)
223 /* Already open. */
224 return (EBUSY);
225
226 evar = &sc->sc_base.me_evar;
227 wsevent_init(evar);
228 evar->io = p;
229 #ifdef WSDISPLAY_COMPAT_RAWKBD
230 sc->sc_rawkbd = 0;
231 #endif
232
233 wsmux_do_open(sc, evar);
234
235 return (0);
236 }
237
238 /*
239 * Open of a mux via the parent mux.
240 */
241 int
242 wsmux_mux_open(struct wsevsrc *me, struct wseventvar *evar)
243 {
244 struct wsmux_softc *sc = (struct wsmux_softc *)me;
245
246 #ifdef DIAGNOSTIC
247 if (sc->sc_base.me_evp != NULL) {
248 printf("wsmux_mux_open: busy\n");
249 return (EBUSY);
250 }
251 if (sc->sc_base.me_parent == NULL) {
252 printf("wsmux_mux_open: no parent\n");
253 return (EINVAL);
254 }
255 #endif
256
257 wsmux_do_open(sc, evar);
258
259 return (0);
260 }
261
262 /* Common part of opening a mux. */
263 void
264 wsmux_do_open(struct wsmux_softc *sc, struct wseventvar *evar)
265 {
266 struct wsevsrc *me;
267
268 sc->sc_base.me_evp = evar; /* remember event variable, mark as open */
269
270 /* Open all children. */
271 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
272 DPRINTF(("wsmuxopen: %s: m=%p dev=%s\n",
273 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname));
274 #ifdef DIAGNOSTIC
275 if (me->me_evp != NULL) {
276 printf("wsmuxopen: dev already in use\n");
277 continue;
278 }
279 if (me->me_parent != sc) {
280 printf("wsmux_do_open: bad child=%p\n", me);
281 continue;
282 }
283 {
284 int error = wsevsrc_open(me, evar);
285 if (error) {
286 DPRINTF(("wsmuxopen: open failed %d\n", error));
287 }
288 }
289 #else
290 /* ignore errors, failing children will not be marked open */
291 (void)wsevsrc_open(me, evar);
292 #endif
293 }
294 }
295
296 /*
297 * close() of the pseudo device from device table.
298 */
299 int
300 wsmuxclose(dev_t dev, int flags, int mode, struct proc *p)
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
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
332 wsmux_do_close(struct wsmux_softc *sc)
333 {
334 struct wsevsrc *me;
335
336 DPRINTF(("wsmuxclose: %s: sc=%p\n", sc->sc_base.me_dv.dv_xname, sc));
337
338 /* Close all the children. */
339 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
340 DPRINTF(("wsmuxclose %s: m=%p dev=%s\n",
341 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname));
342 #ifdef DIAGNOSTIC
343 if (me->me_parent != sc) {
344 printf("wsmuxclose: bad child=%p\n", me);
345 continue;
346 }
347 #endif
348 (void)wsevsrc_close(me);
349 me->me_evp = NULL;
350 }
351 }
352
353 /*
354 * read() of the pseudo device from device table.
355 */
356 int
357 wsmuxread(dev_t dev, struct uio *uio, int flags)
358 {
359 int minr = minor(dev);
360 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
361 struct wseventvar *evar;
362 int error;
363
364 if (WSMUXCTL(minr)) {
365 /* control device */
366 return (EINVAL);
367 }
368
369 evar = sc->sc_base.me_evp;
370 if (evar == NULL) {
371 #ifdef DIAGNOSTIC
372 /* XXX can we get here? */
373 printf("wsmuxread: not open\n");
374 #endif
375 return (EINVAL);
376 }
377
378 DPRINTFN(5,("wsmuxread: %s event read evar=%p\n",
379 sc->sc_base.me_dv.dv_xname, evar));
380 error = wsevent_read(evar, uio, flags);
381 DPRINTFN(5,("wsmuxread: %s event read ==> error=%d\n",
382 sc->sc_base.me_dv.dv_xname, error));
383 return (error);
384 }
385
386 /*
387 * ioctl of the pseudo device from device table.
388 */
389 int
390 wsmuxioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
391 {
392 int u = WSMUXDEV(minor(dev));
393
394 return wsmux_do_ioctl(&wsmuxdevs[u]->sc_base.me_dv, cmd, data, flag, p);
395 }
396
397 /*
398 * ioctl of a mux via the parent mux, continuation of wsmuxioctl().
399 */
400 int
401 wsmux_do_ioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
402 struct proc *p)
403 {
404 struct wsmux_softc *sc = (struct wsmux_softc *)dv;
405 struct wsevsrc *me;
406 int error, ok;
407 int s, put, get, n;
408 struct wseventvar *evar;
409 struct wscons_event *ev;
410 struct timeval thistime;
411 struct wsmux_device_list *l;
412
413 DPRINTF(("wsmux_do_ioctl: %s: enter sc=%p, cmd=%08lx\n",
414 sc->sc_base.me_dv.dv_xname, sc, cmd));
415
416 switch (cmd) {
417 case WSMUXIO_INJECTEVENT:
418 /* Inject an event, e.g., from moused. */
419 DPRINTF(("%s: inject\n", sc->sc_base.me_dv.dv_xname));
420
421 evar = sc->sc_base.me_evp;
422 if (evar == NULL) {
423 /* No event sink, so ignore it. */
424 DPRINTF(("wsmux_do_ioctl: event ignored\n"));
425 return (0);
426 }
427
428 s = spltty();
429 get = evar->get;
430 put = evar->put;
431 ev = &evar->q[put];
432 if (++put % WSEVENT_QSIZE == get) {
433 put--;
434 splx(s);
435 return (ENOSPC);
436 }
437 if (put >= WSEVENT_QSIZE)
438 put = 0;
439 *ev = *(struct wscons_event *)data;
440 microtime(&thistime);
441 TIMEVAL_TO_TIMESPEC(&thistime, &ev->time);
442 evar->put = put;
443 WSEVENT_WAKEUP(evar);
444 splx(s);
445 return (0);
446 case WSMUXIO_ADD_DEVICE:
447 #define d ((struct wsmux_device *)data)
448 DPRINTF(("%s: add type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
449 d->type, d->idx));
450 switch (d->type) {
451 #if NWSMOUSE > 0
452 case WSMUX_MOUSE:
453 return (wsmouse_add_mux(d->idx, sc));
454 #endif
455 #if NWSKBD > 0
456 case WSMUX_KBD:
457 return (wskbd_add_mux(d->idx, sc));
458 #endif
459 case WSMUX_MUX:
460 return (wsmux_add_mux(d->idx, sc));
461 default:
462 return (EINVAL);
463 }
464 case WSMUXIO_REMOVE_DEVICE:
465 DPRINTF(("%s: rem type=%d, no=%d\n", sc->sc_base.me_dv.dv_xname,
466 d->type, d->idx));
467 /* Locate the device */
468 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
469 if (me->me_ops->type == d->type &&
470 me->me_dv.dv_unit == d->idx) {
471 DPRINTF(("wsmux_do_ioctl: detach\n"));
472 wsmux_detach_sc(me);
473 return (0);
474 }
475 }
476 return (EINVAL);
477 #undef d
478
479 case WSMUXIO_LIST_DEVICES:
480 DPRINTF(("%s: list\n", sc->sc_base.me_dv.dv_xname));
481 l = (struct wsmux_device_list *)data;
482 n = 0;
483 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
484 if (n >= WSMUX_MAXDEV)
485 break;
486 l->devices[n].type = me->me_ops->type;
487 l->devices[n].idx = me->me_dv.dv_unit;
488 n++;
489 }
490 l->ndevices = n;
491 return (0);
492 #ifdef WSDISPLAY_COMPAT_RAWKBD
493 case WSKBDIO_SETMODE:
494 sc->sc_rawkbd = *(int *)data;
495 DPRINTF(("wsmux_do_ioctl: save rawkbd = %d\n", sc->sc_rawkbd));
496 break;
497 #endif
498 case FIONBIO:
499 DPRINTF(("%s: FIONBIO\n", sc->sc_base.me_dv.dv_xname));
500 return (0);
501
502 case FIOASYNC:
503 DPRINTF(("%s: FIOASYNC\n", sc->sc_base.me_dv.dv_xname));
504 evar = sc->sc_base.me_evp;
505 if (evar == NULL)
506 return (EINVAL);
507 evar->async = *(int *)data != 0;
508 return (0);
509 case TIOCSPGRP:
510 DPRINTF(("%s: TIOCSPGRP\n", sc->sc_base.me_dv.dv_xname));
511 evar = sc->sc_base.me_evp;
512 if (evar == NULL)
513 return (EINVAL);
514 if (*(int *)data != evar->io->p_pgid)
515 return (EPERM);
516 return (0);
517 default:
518 DPRINTF(("%s: unknown\n", sc->sc_base.me_dv.dv_xname));
519 break;
520 }
521
522 if (sc->sc_base.me_evp == NULL
523 #if NWSDISPLAY > 0
524 && sc->sc_base.me_dispdv == NULL
525 #endif
526 )
527 return (EACCES);
528
529 /* Return 0 if any of the ioctl() succeeds, otherwise the last error */
530 error = 0;
531 ok = 0;
532 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
533 #ifdef DIAGNOSTIC
534 /* XXX check evp? */
535 if (me->me_parent != sc) {
536 printf("wsmux_do_ioctl: bad child %p\n", me);
537 continue;
538 }
539 #endif
540 error = wsevsrc_ioctl(me, cmd, data, flag, p);
541 DPRINTF(("wsmux_do_ioctl: %s: me=%p dev=%s ==> %d\n",
542 sc->sc_base.me_dv.dv_xname, me, me->me_dv.dv_xname,
543 error));
544 if (!error)
545 ok = 1;
546 }
547 if (ok)
548 error = 0;
549
550 return (error);
551 }
552
553 /*
554 * poll() of the pseudo device from device table.
555 */
556 int
557 wsmuxpoll(dev_t dev, int events, struct proc *p)
558 {
559 int minr = minor(dev);
560 struct wsmux_softc *sc = wsmuxdevs[WSMUXDEV(minr)];
561
562 if (WSMUXCTL(minr)) {
563 /* control device */
564 return (EINVAL);
565 }
566
567 if (sc->sc_base.me_evp == NULL) {
568 #ifdef DIAGNOSTIC
569 printf("wsmuxpoll: not open\n");
570 #endif
571 return (EACCES);
572 }
573
574 return (wsevent_poll(sc->sc_base.me_evp, events, p));
575 }
576
577 /*
578 * Add mux unit as a child to muxsc.
579 */
580 int
581 wsmux_add_mux(int unit, struct wsmux_softc *muxsc)
582 {
583 struct wsmux_softc *sc, *m;
584
585 sc = wsmux_getmux(unit);
586 if (sc == NULL)
587 return (ENXIO);
588
589 DPRINTF(("wsmux_add_mux: %s(%p) to %s(%p)\n",
590 sc->sc_base.me_dv.dv_xname, sc, muxsc->sc_base.me_dv.dv_xname,
591 muxsc));
592
593 if (sc->sc_base.me_parent != NULL || sc->sc_base.me_evp != NULL)
594 return (EBUSY);
595
596 /* The mux we are adding must not be an ancestor of itself. */
597 for (m = muxsc; m != NULL ; m = m->sc_base.me_parent)
598 if (m == sc)
599 return (EINVAL);
600
601 return (wsmux_attach_sc(muxsc, &sc->sc_base));
602 }
603
604 /* Create a new mux softc. */
605 struct wsmux_softc *
606 wsmux_create(const char *name, int unit)
607 {
608 struct wsmux_softc *sc;
609
610 DPRINTF(("wsmux_create: allocating\n"));
611 sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT|M_ZERO);
612 if (sc == NULL)
613 return (NULL);
614 CIRCLEQ_INIT(&sc->sc_cld);
615 snprintf(sc->sc_base.me_dv.dv_xname, sizeof sc->sc_base.me_dv.dv_xname,
616 "%s%d", name, unit);
617 sc->sc_base.me_dv.dv_unit = unit;
618 sc->sc_base.me_ops = &wsmux_srcops;
619 return (sc);
620 }
621
622 /* Attach me as a child to sc. */
623 int
624 wsmux_attach_sc(struct wsmux_softc *sc, struct wsevsrc *me)
625 {
626 int error;
627
628 if (sc == NULL)
629 return (EINVAL);
630
631 DPRINTF(("wsmux_attach_sc: %s(%p): type=%d\n",
632 sc->sc_base.me_dv.dv_xname, sc, me->me_ops->type));
633
634 #ifdef DIAGNOSTIC
635 if (me->me_parent != NULL) {
636 printf("wsmux_attach_sc: busy\n");
637 return (EBUSY);
638 }
639 #endif
640 me->me_parent = sc;
641 CIRCLEQ_INSERT_TAIL(&sc->sc_cld, me, me_next);
642
643 error = 0;
644 #if NWSDISPLAY > 0
645 if (sc->sc_base.me_dispdv != NULL) {
646 /* This is a display mux, so attach the new device to it. */
647 DPRINTF(("wsmux_attach_sc: %s: set display %p\n",
648 sc->sc_base.me_dv.dv_xname, sc->sc_base.me_dispdv));
649 if (me->me_ops->dsetdisplay != NULL) {
650 error = wsevsrc_set_display(me, &sc->sc_base);
651 /* Ignore that the console already has a display. */
652 if (error == EBUSY)
653 error = 0;
654 if (!error) {
655 #ifdef WSDISPLAY_COMPAT_RAWKBD
656 DPRINTF(("wsmux_attach_sc: %s set rawkbd=%d\n",
657 me->me_dv.dv_xname, sc->sc_rawkbd));
658 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
659 &sc->sc_rawkbd, 0, 0);
660 #endif
661 }
662 }
663 }
664 #endif
665 if (sc->sc_base.me_evp != NULL) {
666 /* Mux is open, so open the new subdevice */
667 DPRINTF(("wsmux_attach_sc: %s: calling open of %s\n",
668 sc->sc_base.me_dv.dv_xname, me->me_dv.dv_xname));
669 error = wsevsrc_open(me, sc->sc_base.me_evp);
670 } else {
671 DPRINTF(("wsmux_attach_sc: %s not open\n",
672 sc->sc_base.me_dv.dv_xname));
673 }
674
675 if (error) {
676 me->me_parent = NULL;
677 CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next);
678 }
679
680 DPRINTF(("wsmux_attach_sc: %s(%p) done, error=%d\n",
681 sc->sc_base.me_dv.dv_xname, sc, error));
682 return (error);
683 }
684
685 /* Remove me from the parent. */
686 void
687 wsmux_detach_sc(struct wsevsrc *me)
688 {
689 struct wsmux_softc *sc = me->me_parent;
690
691 DPRINTF(("wsmux_detach_sc: %s(%p) parent=%p\n",
692 me->me_dv.dv_xname, me, sc));
693
694 #ifdef DIAGNOSTIC
695 if (sc == NULL) {
696 printf("wsmux_detach_sc: %s has no parent\n",
697 me->me_dv.dv_xname);
698 return;
699 }
700 #endif
701
702 #if NWSDISPLAY > 0
703 if (sc->sc_base.me_dispdv != NULL) {
704 if (me->me_ops->dsetdisplay != NULL)
705 /* ignore error, there's nothing we can do */
706 (void)wsevsrc_set_display(me, NULL);
707 } else
708 #endif
709 if (me->me_evp != NULL) {
710 DPRINTF(("wsmux_detach_sc: close\n"));
711 /* mux device is open, so close multiplexee */
712 (void)wsevsrc_close(me);
713 }
714
715 CIRCLEQ_REMOVE(&sc->sc_cld, me, me_next);
716 me->me_parent = NULL;
717
718 DPRINTF(("wsmux_detach_sc: done sc=%p\n", sc));
719 }
720
721 /*
722 * Display ioctl() of a mux via the parent mux.
723 */
724 int
725 wsmux_do_displayioctl(struct device *dv, u_long cmd, caddr_t data, int flag,
726 struct proc *p)
727 {
728 struct wsmux_softc *sc = (struct wsmux_softc *)dv;
729 struct wsevsrc *me;
730 int error, ok;
731
732 DPRINTF(("wsmux_displayioctl: %s: sc=%p, cmd=%08lx\n",
733 sc->sc_base.me_dv.dv_xname, sc, cmd));
734
735 #ifdef WSDISPLAY_COMPAT_RAWKBD
736 if (cmd == WSKBDIO_SETMODE) {
737 sc->sc_rawkbd = *(int *)data;
738 DPRINTF(("wsmux_displayioctl: rawkbd = %d\n", sc->sc_rawkbd));
739 }
740 #endif
741
742 /*
743 * Return 0 if any of the ioctl() succeeds, otherwise the last error.
744 * Return EPASSTHROUGH if no mux component accepts the ioctl.
745 */
746 error = EPASSTHROUGH;
747 ok = 0;
748 CIRCLEQ_FOREACH(me, &sc->sc_cld, me_next) {
749 DPRINTF(("wsmux_displayioctl: me=%p\n", me));
750 #ifdef DIAGNOSTIC
751 if (me->me_parent != sc) {
752 printf("wsmux_displayioctl: bad child %p\n", me);
753 continue;
754 }
755 #endif
756 if (me->me_ops->ddispioctl != NULL) {
757 error = wsevsrc_display_ioctl(me, cmd, data, flag, p);
758 DPRINTF(("wsmux_displayioctl: me=%p dev=%s ==> %d\n",
759 me, me->me_dv.dv_xname, error));
760 if (!error)
761 ok = 1;
762 }
763 }
764 if (ok)
765 error = 0;
766
767 return (error);
768 }
769
770 #if NWSDISPLAY > 0
771 /*
772 * Set display of a mux via the parent mux.
773 */
774 int
775 wsmux_evsrc_set_display(struct device *dv, struct wsevsrc *ame)
776 {
777 struct wsmux_softc *muxsc = (struct wsmux_softc *)ame;
778 struct wsmux_softc *sc = (struct wsmux_softc *)dv;
779 struct device *displaydv = muxsc ? muxsc->sc_base.me_dispdv : NULL;
780
781 DPRINTF(("wsmux_set_display: %s: displaydv=%p\n",
782 sc->sc_base.me_dv.dv_xname, displaydv));
783
784 if (displaydv != NULL) {
785 if (sc->sc_base.me_dispdv != NULL)
786 return (EBUSY);
787 } else {
788 if (sc->sc_base.me_dispdv == NULL)
789 return (ENXIO);
790 }
791
792 return wsmux_set_display(sc, displaydv);
793 }
794
795 int
796 wsmux_set_display(struct wsmux_softc *sc, struct device *displaydv)
797 {
798 struct device *odisplaydv;
799 struct wsevsrc *me;
800 struct wsmux_softc *nsc = displaydv ? sc : NULL;
801 int error, ok;
802
803 odisplaydv = sc->sc_base.me_dispdv;
804 sc->sc_base.me_dispdv = displaydv;
805
806 if (displaydv)
807 printf("%s: connecting to %s\n",
808 sc->sc_base.me_dv.dv_xname, displaydv->dv_xname);
809 ok = 0;
810 error = 0;
811 CIRCLEQ_FOREACH(me, &sc->sc_cld,me_next) {
812 #ifdef DIAGNOSTIC
813 if (me->me_parent != sc) {
814 printf("wsmux_set_display: bad child parent %p\n", me);
815 continue;
816 }
817 #endif
818 if (me->me_ops->dsetdisplay != NULL) {
819 error = wsevsrc_set_display(me, &nsc->sc_base);
820 DPRINTF(("wsmux_set_display: m=%p dev=%s error=%d\n",
821 me, me->me_dv.dv_xname, error));
822 if (!error) {
823 ok = 1;
824 #ifdef WSDISPLAY_COMPAT_RAWKBD
825 DPRINTF(("wsmux_set_display: %s set rawkbd=%d\n",
826 me->me_dv.dv_xname, sc->sc_rawkbd));
827 (void)wsevsrc_ioctl(me, WSKBDIO_SETMODE,
828 &sc->sc_rawkbd, 0, 0);
829 #endif
830 }
831 }
832 }
833 if (ok)
834 error = 0;
835
836 if (displaydv == NULL)
837 printf("%s: disconnecting from %s\n",
838 sc->sc_base.me_dv.dv_xname, odisplaydv->dv_xname);
839
840 return (error);
841 }
842 #endif /* NWSDISPLAY > 0 */
843