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