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