xref: /openbsd/sys/dev/wscons/wsmux.c (revision 78b63d65)
1 /*	$OpenBSD: wsmux.c,v 1.6 2001/03/30 16:38:14 aaron Exp $	*/
2 /*	$NetBSD: wsmux.c,v 1.9 2000/05/28 10:33:14 takemura Exp $	*/
3 
4 /*
5  * Copyright (c) 1998 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  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *        This product includes software developed by the NetBSD
22  *        Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 #include "wsmux.h"
41 #include "wsdisplay.h"
42 #include "wskbd.h"
43 
44 #if NWSMUX > 0 || (NWSDISPLAY > 0 && NWSKBD > 0)
45 
46 /*
47  * wscons mux device.
48  *
49  * The mux device is a collection of real mice and keyboards and acts as
50  * a merge point for all the events from the different real devices.
51  */
52 
53 #include <sys/param.h>
54 #include <sys/conf.h>
55 #include <sys/ioctl.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 <dev/wscons/wsconsio.h>
68 #include <dev/wscons/wseventvar.h>
69 #include <dev/wscons/wscons_callbacks.h>
70 #include <dev/wscons/wsmuxvar.h>
71 
72 #ifdef WSMUX_DEBUG
73 #define DPRINTF(x)	if (wsmuxdebug) printf x
74 int	wsmuxdebug = 0;
75 #else
76 #define DPRINTF(x)
77 #endif
78 
79 struct wsplink {
80 	LIST_ENTRY(wsplink) next;
81 	int type;
82 	struct wsmux_softc *mux; /* our mux device */
83 	/* The rest of the fields reflect a value in the multiplexee. */
84 	struct device *sc;	/* softc */
85 	struct wseventvar *sc_mevents; /* event var */
86 	struct wsmux_softc **sc_muxp; /* pointer to us */
87 	struct wsmuxops *sc_ops;
88 };
89 
90 int wsmuxdoclose __P((struct device *, int, int, struct proc *));
91 int wsmux_set_display __P((struct device *, struct wsmux_softc *));
92 int wsmux_isset_display __P((struct device *));
93 
94 #if NWSMUX > 0
95 cdev_decl(wsmux);
96 
97 void wsmuxattach __P((int));
98 
99 struct wsmuxops wsmux_muxops = {
100 	wsmuxopen, wsmuxdoclose, wsmuxdoioctl, wsmux_displayioctl,
101 	wsmux_set_display, wsmux_isset_display
102 };
103 
104 void wsmux_setmax __P((int n));
105 
106 int nwsmux = 0;
107 struct wsmux_softc **wsmuxdevs = NULL;
108 
109 void
110 wsmux_setmax(n)
111 	int n;
112 {
113 	int i = 0;
114 	struct wsmux_softc **wsmuxdevs_tmp = NULL;
115 
116 	if (n >= nwsmux) {
117 		if (wsmuxdevs != NULL) {
118 			wsmuxdevs_tmp = malloc(nwsmux * sizeof(*wsmuxdevs_tmp),
119 			    M_DEVBUF, M_NOWAIT);
120 			if (wsmuxdevs_tmp == 0)
121 				panic("wsmux_setmax: no mem\n");
122 			for (; i < nwsmux; i++)
123 				wsmuxdevs_tmp[i] = wsmuxdevs[i];
124 			free(wsmuxdevs, M_DEVBUF);
125 		}
126 
127 		wsmuxdevs = malloc(n + 1 * sizeof(*wsmuxdevs),
128 		    M_DEVBUF, M_NOWAIT);
129 		if (wsmuxdevs == 0)
130 			panic("wsmux_setmax: no memory\n");
131 		for (; i < n + 1; i++)
132 			wsmuxdevs[i] = 0;
133 		if (wsmuxdevs_tmp != NULL) {
134 			for (i = 0; i < nwsmux; i++)
135 				wsmuxdevs[i] = wsmuxdevs_tmp[i];
136 			free(wsmuxdevs_tmp, M_DEVBUF);
137 		}
138 		nwsmux = n + 1;
139 	}
140 }
141 
142 /* From upper level */
143 void
144 wsmuxattach(n)
145 	int n;
146 {
147 	int i;
148 
149 	wsmux_setmax(n);	/* Make sure we have room for all muxes. */
150 
151 	/* Make sure all muxes are there. */
152 	for (i = 0; i < nwsmux; i++)
153 		if (!wsmuxdevs[i])
154 			wsmuxdevs[i] = wsmux_create("wsmux", i);
155 }
156 
157 /* From mouse or keyboard. */
158 void
159 wsmux_attach(n, type, dsc, ev, psp, ops)
160 	int n;
161 	int type;
162         struct device *dsc;
163 	struct wseventvar *ev;
164 	struct wsmux_softc **psp;
165 	struct wsmuxops *ops;
166 {
167 	struct wsmux_softc *sc;
168 	int error;
169 
170 	DPRINTF(("wsmux_attach: n=%d\n", n));
171 	wsmux_setmax(n);
172 	sc = wsmuxdevs[n];
173 	if (sc == 0) {
174 		sc = wsmux_create("wsmux", n);
175 		if (sc == 0) {
176 			printf("wsmux: attach out of memory\n");
177 			return;
178 		}
179 		wsmuxdevs[n] = sc;
180 	}
181 	error = wsmux_attach_sc(sc, type, dsc, ev, psp, ops);
182 	if (error)
183 		printf("wsmux_attach: error=%d\n", error);
184 }
185 
186 /* From mouse or keyboard. */
187 void
188 wsmux_detach(n, dsc)
189 	int n;
190         struct device *dsc;
191 {
192 #ifdef DIAGNOSTIC
193 	int error;
194 
195 	if (n >= nwsmux || n < 0) {
196 		printf("wsmux_detach: detach is out of range\n");
197 		return;
198 	}
199 	if ((error = wsmux_detach_sc(wsmuxdevs[n], dsc)))
200 		printf("wsmux_detach: error=%d\n", error);
201 #else
202 	(void)wsmux_detach_sc(wsmuxdevs[n], dsc);
203 #endif
204 }
205 
206 int
207 wsmuxopen(dev, flags, mode, p)
208 	dev_t dev;
209 	int flags, mode;
210 	struct proc *p;
211 {
212 	struct wsmux_softc *sc;
213 	struct wsplink *m;
214 	int unit, error, nopen, lasterror;
215 
216 	unit = minor(dev);
217 	if (unit >= nwsmux ||	/* make sure it was attached */
218 	    (sc = wsmuxdevs[unit]) == NULL)
219 		return (ENXIO);
220 
221 	DPRINTF(("wsmuxopen: %s: sc=%p\n", sc->sc_dv.dv_xname, sc));
222 	if (!(flags & FREAD)) {
223 		/* Not opening for read, only ioctl is available. */
224 		return (0);
225 	}
226 
227 	if (sc->sc_events.io)
228 		return (EBUSY);
229 
230 	sc->sc_events.io = p;
231 	sc->sc_flags = flags;
232 	sc->sc_mode = mode;
233 	sc->sc_p = p;
234 	wsevent_init(&sc->sc_events);		/* may cause sleep */
235 
236 	nopen = 0;
237 	lasterror = 0;
238 	for (m = LIST_FIRST(&sc->sc_reals); m; m = LIST_NEXT(m, next)) {
239 		if (!m->sc_mevents->io && !*m->sc_muxp) {
240 			DPRINTF(("wsmuxopen: %s: m=%p dev=%s\n",
241 				 sc->sc_dv.dv_xname, m, m->sc->dv_xname));
242 			error = m->sc_ops->dopen(makedev(0, m->sc->dv_unit),
243 						 flags, mode, p);
244 			if (error) {
245 				/* Ignore opens that fail */
246 				lasterror = error;
247 				DPRINTF(("wsmuxopen: open failed %d\n",
248 					 error));
249 			} else {
250 				nopen++;
251 				*m->sc_muxp = sc;
252 			}
253 		}
254 	}
255 
256 	if (nopen == 0 && lasterror != 0) {
257 		wsevent_fini(&sc->sc_events);
258 		sc->sc_events.io = NULL;
259 		return (lasterror);
260 	}
261 
262 	return (0);
263 }
264 
265 int
266 wsmuxclose(dev, flags, mode, p)
267 	dev_t dev;
268 	int flags, mode;
269 	struct proc *p;
270 {
271 	return wsmuxdoclose(&wsmuxdevs[minor(dev)]->sc_dv, flags, mode, p);
272 }
273 
274 int
275 wsmuxread(dev, uio, flags)
276 	dev_t dev;
277 	struct uio *uio;
278 	int flags;
279 {
280 	struct wsmux_softc *sc = wsmuxdevs[minor(dev)];
281 
282 	if (!sc->sc_events.io)
283 		return (EACCES);
284 
285 	return (wsevent_read(&sc->sc_events, uio, flags));
286 }
287 
288 int
289 wsmuxioctl(dev, cmd, data, flag, p)
290 	dev_t dev;
291 	u_long cmd;
292 	caddr_t data;
293 	int flag;
294 	struct proc *p;
295 {
296 	return wsmuxdoioctl(&wsmuxdevs[minor(dev)]->sc_dv, cmd, data, flag, p);
297 }
298 
299 int
300 wsmuxselect(dev, events, p)
301 	dev_t dev;
302 	int events;
303 	struct proc *p;
304 {
305 	struct wsmux_softc *sc = wsmuxdevs[minor(dev)];
306 
307 	if (!sc->sc_events.io)
308 		return (EACCES);
309 
310 	return (wsevent_poll(&sc->sc_events, events, p));
311 }
312 
313 int
314 wsmux_add_mux(unit, muxsc)
315 	int unit;
316 	struct wsmux_softc *muxsc;
317 {
318 	struct wsmux_softc *sc, *m;
319 
320 	if (unit < 0 || unit >= nwsmux || (sc = wsmuxdevs[unit]) == NULL)
321 		return (ENXIO);
322 
323 	DPRINTF(("wsmux_add_mux: %s to %s\n", sc->sc_dv.dv_xname,
324 		 muxsc->sc_dv.dv_xname));
325 
326 	if (sc->sc_mux || sc->sc_events.io)
327 		return (EBUSY);
328 
329 	/* The mux we are adding must not be an ancestor of it. */
330 	for (m = muxsc->sc_mux; m; m = m->sc_mux)
331 		if (m == sc)
332 			return (EINVAL);
333 
334 	return (wsmux_attach_sc(muxsc, WSMUX_MUX, &sc->sc_dv, &sc->sc_events,
335 				&sc->sc_mux, &wsmux_muxops));
336 }
337 
338 int
339 wsmux_rem_mux(unit, muxsc)
340 	int unit;
341 	struct wsmux_softc *muxsc;
342 {
343 	struct wsmux_softc *sc;
344 
345 	if (unit < 0 || unit >= nwsmux || (sc = wsmuxdevs[unit]) == NULL)
346 		return (ENXIO);
347 
348 	DPRINTF(("wsmux_rem_mux: %s from %s\n", sc->sc_dv.dv_xname,
349 		 muxsc->sc_dv.dv_xname));
350 
351 	return (wsmux_detach_sc(muxsc, &sc->sc_dv));
352 }
353 
354 #endif /* NWSMUX > 0 */
355 
356 struct wsmux_softc *
357 wsmux_create(name, unit)
358 	const char *name;
359 	int unit;
360 {
361 	struct wsmux_softc *sc;
362 
363 	DPRINTF(("wsmux_create: allocating\n"));
364 	sc = malloc(sizeof *sc, M_DEVBUF, M_NOWAIT);
365 	if (!sc)
366 		return (0);
367 	memset(sc, 0, sizeof *sc);
368 	LIST_INIT(&sc->sc_reals);
369 	snprintf(sc->sc_dv.dv_xname, sizeof sc->sc_dv.dv_xname,
370 		 "%s%d", name, unit);
371 	sc->sc_dv.dv_unit = unit;
372 	return (sc);
373 }
374 
375 int
376 wsmux_attach_sc(sc, type, dsc, ev, psp, ops)
377 	struct wsmux_softc *sc;
378 	int type;
379         struct device *dsc;
380 	struct wseventvar *ev;
381 	struct wsmux_softc **psp;
382 	struct wsmuxops *ops;
383 {
384 	struct wsplink *m;
385 	int error;
386 
387 	DPRINTF(("wsmux_attach_sc: %s: type=%d dsc=%p, *psp=%p\n",
388 		 sc->sc_dv.dv_xname, type, dsc, *psp));
389 	m = malloc(sizeof *m, M_DEVBUF, M_NOWAIT);
390 	if (m == 0)
391 		return (ENOMEM);
392 	m->type = type;
393 	m->mux = sc;
394 	m->sc = dsc;
395 	m->sc_mevents = ev;
396 	m->sc_muxp = psp;
397 	m->sc_ops = ops;
398 	LIST_INSERT_HEAD(&sc->sc_reals, m, next);
399 
400 	if (sc->sc_displaydv) {
401 		/* This is a display mux, so attach the new device to it. */
402 		DPRINTF(("wsmux_attach_sc: %s: set display %p\n",
403 			 sc->sc_dv.dv_xname, sc->sc_displaydv));
404 		error = 0;
405 		if (m->sc_ops->dsetdisplay) {
406 			error = m->sc_ops->dsetdisplay(m->sc, sc);
407 			/* Ignore that the console already has a display. */
408 			if (error == EBUSY)
409 				error = 0;
410 			if (!error) {
411 				*m->sc_muxp = sc;
412 #ifdef WSDISPLAY_COMPAT_RAWKBD
413 				DPRINTF(("wsmux_attach_sc: on %s set rawkbd=%d\n",
414 					 m->sc->dv_xname, sc->sc_rawkbd));
415 				(void)m->sc_ops->dioctl(m->sc,
416 					     WSKBDIO_SETMODE,
417 					     (caddr_t)&sc->sc_rawkbd,
418 					     0, 0);
419 #endif
420 			}
421 		}
422 	} else if (sc->sc_events.io) {
423 		/* Mux is open, so open the new subdevice */
424 		DPRINTF(("wsmux_attach_sc: %s: calling open of %s\n",
425 			 sc->sc_dv.dv_xname, m->sc->dv_xname));
426 		/* mux already open, join in */
427 		error = m->sc_ops->dopen(makedev(0, m->sc->dv_unit),
428 					 sc->sc_flags, sc->sc_mode, sc->sc_p);
429 		if (!error)
430 			*m->sc_muxp = sc;
431 	} else {
432 		DPRINTF(("wsmux_attach_sc: %s not open\n",
433 			 sc->sc_dv.dv_xname));
434 		error = 0;
435 	}
436 	DPRINTF(("wsmux_attach_sc: done sc=%p psp=%p *psp=%p\n",
437 		 sc, psp, *psp));
438 
439 	return (error);
440 }
441 
442 int
443 wsmux_detach_sc(sc, dsc)
444 	struct wsmux_softc *sc;
445         struct device *dsc;
446 {
447 	struct wsplink *m;
448 	int error = 0;
449 
450 	DPRINTF(("wsmux_detach_sc: %s: dsc=%p\n", sc->sc_dv.dv_xname, dsc));
451 #ifdef DIAGNOSTIC
452 	if (sc == 0) {
453 		printf("wsmux_detach_sc: not allocated\n");
454 		return (ENXIO);
455 	}
456 #endif
457 
458 	for (m = LIST_FIRST(&sc->sc_reals); m; m = LIST_NEXT(m, next)) {
459 		if (m->sc == dsc)
460 			break;
461 	}
462 #ifdef DIAGNOSTIC
463 	if (!m) {
464 		printf("wsmux_detach_sc: not found\n");
465 		return (ENXIO);
466 	}
467 #endif
468 	if (sc->sc_displaydv ||
469 	    (m->sc_ops->dissetdisplay && m->sc_ops->dissetdisplay(m->sc))) {
470 		if (m->sc_ops->dsetdisplay)
471 			error = m->sc_ops->dsetdisplay(m->sc, 0);
472 		if (error)
473 			return (error);
474 		*m->sc_muxp = 0;
475 	} else if (*m->sc_muxp) {
476 		DPRINTF(("wsmux_detach_sc: close\n"));
477 		/* mux device is open, so close multiplexee */
478 		m->sc_ops->dclose(m->sc, FREAD, 0, 0);
479 		*m->sc_muxp = 0;
480 	}
481 
482 	LIST_REMOVE(m, next);
483 
484 	free(m, M_DEVBUF);
485 	DPRINTF(("wsmux_detach_sc: done sc=%p\n", sc));
486 	return (0);
487 }
488 
489 int wsmuxdoclose(dv, flags, mode, p)
490 	struct device *dv;
491 	int flags, mode;
492 	struct proc *p;
493 {
494 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
495 	struct wsplink *m;
496 
497 	DPRINTF(("wsmuxclose: %s: sc=%p\n", sc->sc_dv.dv_xname, sc));
498 	if (!(flags & FREAD)) {
499 		/* Nothing to do, because open didn't do anything. */
500 		return (0);
501 	}
502 
503 	for (m = LIST_FIRST(&sc->sc_reals); m; m = LIST_NEXT(m, next)) {
504 		if (*m->sc_muxp == sc) {
505 			DPRINTF(("wsmuxclose %s: m=%p dev=%s\n",
506 				 sc->sc_dv.dv_xname, m, m->sc->dv_xname));
507 			m->sc_ops->dclose(m->sc, flags, mode, p);
508 			*m->sc_muxp = 0;
509 		}
510 	}
511 
512 	wsevent_fini(&sc->sc_events);
513 	sc->sc_events.io = NULL;
514 
515 	return (0);
516 }
517 
518 int
519 wsmuxdoioctl(dv, cmd, data, flag, p)
520 	struct device *dv;
521 	u_long cmd;
522 	caddr_t data;
523 	int flag;
524 	struct proc *p;
525 {
526 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
527 	struct wsplink *m;
528 	int error, ok;
529 	int s, put, get, n;
530 	struct wseventvar *evar;
531 	struct wscons_event *ev;
532 	struct timeval xxxtime;
533 	struct wsmux_device_list *l;
534 
535 	DPRINTF(("wsmuxdoioctl: %s: sc=%p, cmd=%08lx\n",
536 		 sc->sc_dv.dv_xname, sc, cmd));
537 
538 	switch (cmd) {
539 	case WSMUX_INJECTEVENT:
540 		/* Inject an event, e.g., from moused. */
541 		if (!sc->sc_events.io)
542 			return (EACCES);
543 
544 		evar = &sc->sc_events;
545 		s = spltty();
546 		get = evar->get;
547 		put = evar->put;
548 		if (++put % WSEVENT_QSIZE == get) {
549 			put--;
550 			splx(s);
551 			return (ENOSPC);
552 		}
553 		if (put >= WSEVENT_QSIZE)
554 			put = 0;
555 		ev = &evar->q[put];
556 		*ev = *(struct wscons_event *)data;
557 		microtime(&xxxtime);
558 		TIMEVAL_TO_TIMESPEC(&xxxtime, &ev->time);
559 		evar->put = put;
560 		WSEVENT_WAKEUP(evar);
561 		splx(s);
562 		return (0);
563 	case WSMUX_ADD_DEVICE:
564 #define d ((struct wsmux_device *)data)
565 		switch (d->type) {
566 #if NWSMOUSE > 0
567 		case WSMUX_MOUSE:
568 			return (wsmouse_add_mux(d->idx, sc));
569 #endif
570 #if NWSKBD > 0
571 		case WSMUX_KBD:
572 			return (wskbd_add_mux(d->idx, sc));
573 #endif
574 #if NWSMUX > 0
575 		case WSMUX_MUX:
576 			return (wsmux_add_mux(d->idx, sc));
577 #endif
578 		default:
579 			return (EINVAL);
580 		}
581 	case WSMUX_REMOVE_DEVICE:
582 		switch (d->type) {
583 #if NWSMOUSE > 0
584 		case WSMUX_MOUSE:
585 			return (wsmouse_rem_mux(d->idx, sc));
586 #endif
587 #if NWSKBD > 0
588 		case WSMUX_KBD:
589 			return (wskbd_rem_mux(d->idx, sc));
590 #endif
591 #if NWSMUX > 0
592 		case WSMUX_MUX:
593 			return (wsmux_rem_mux(d->idx, sc));
594 #endif
595 		default:
596 			return (EINVAL);
597 		}
598 #undef d
599 	case WSMUX_LIST_DEVICES:
600 		l = (struct wsmux_device_list *)data;
601 		for (n = 0, m = LIST_FIRST(&sc->sc_reals);
602 		     n < WSMUX_MAXDEV && m != NULL;
603 		     m = LIST_NEXT(m, next)) {
604 			l->devices[n].type = m->type;
605 			l->devices[n].idx = m->sc->dv_unit;
606 			n++;
607 		}
608 		l->ndevices = n;
609 		return (0);
610 #ifdef WSDISPLAY_COMPAT_RAWKBD
611 	case WSKBDIO_SETMODE:
612 		sc->sc_rawkbd = *(int *)data;
613 		DPRINTF(("wsmuxdoioctl: save rawkbd = %d\n", sc->sc_rawkbd));
614 		break;
615 #endif
616 	case FIOASYNC:
617 		sc->sc_events.async = *(int *)data != 0;
618 		return (0);
619 	case TIOCSPGRP:
620 		if (*(int *)data != sc->sc_events.io->p_pgid)
621 			return (EPERM);
622 		return (0);
623 	default:
624 		break;
625 	}
626 
627 	if (sc->sc_events.io == NULL && sc->sc_displaydv == NULL)
628 		return (EACCES);
629 
630 	/* Return 0 if any of the ioctl() succeeds, otherwise the last error */
631 	error = 0;
632 	ok = 0;
633 	for (m = LIST_FIRST(&sc->sc_reals); m; m = LIST_NEXT(m, next)) {
634 		DPRINTF(("wsmuxdoioctl: m=%p *m->sc_muxp=%p sc=%p\n",
635 			 m, *m->sc_muxp, sc));
636 		if (*m->sc_muxp == sc) {
637 			DPRINTF(("wsmuxdoioctl: %s: m=%p dev=%s\n",
638 				 sc->sc_dv.dv_xname, m, m->sc->dv_xname));
639 			error = m->sc_ops->dioctl(m->sc, cmd, data, flag, p);
640 			if (!error)
641 				ok = 1;
642 		}
643 	}
644 	if (ok)
645 		error = 0;
646 
647 	return (error);
648 }
649 
650 int
651 wsmux_displayioctl(dv, cmd, data, flag, p)
652 	struct device *dv;
653 	u_long cmd;
654 	caddr_t data;
655 	int flag;
656 	struct proc *p;
657 {
658 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
659 	struct wsplink *m;
660 	int error, ok;
661 
662 	DPRINTF(("wsmux_displayioctl: %s: sc=%p, cmd=%08lx\n",
663 		 sc->sc_dv.dv_xname, sc, cmd));
664 
665 #ifdef WSDISPLAY_COMPAT_RAWKBD
666 	if (cmd == WSKBDIO_SETMODE) {
667 		sc->sc_rawkbd = *(int *)data;
668 		DPRINTF(("wsmux_displayioctl: rawkbd = %d\n", sc->sc_rawkbd));
669 	}
670 #endif
671 
672 	/*
673 	 * Return 0 if any of the ioctl() succeeds, otherwise the last error.
674 	 * Return -1 if no mux component accepts the ioctl.
675 	 */
676 	error = -1;
677 	ok = 0;
678 	for (m = LIST_FIRST(&sc->sc_reals); m; m = LIST_NEXT(m, next)) {
679 		DPRINTF(("wsmux_displayioctl: m=%p sc=%p sc_muxp=%p\n",
680 			 m, sc, *m->sc_muxp));
681 		if (m->sc_ops->ddispioctl && *m->sc_muxp == sc) {
682 			error = m->sc_ops->ddispioctl(m->sc, cmd, data,
683 						      flag, p);
684 			DPRINTF(("wsmux_displayioctl: m=%p dev=%s ==> %d\n",
685 				 m, m->sc->dv_xname, error));
686 			if (!error)
687 				ok = 1;
688 		}
689 	}
690 	if (ok)
691 		error = 0;
692 
693 	return (error);
694 }
695 
696 int
697 wsmux_set_display(dv, muxsc)
698 	struct device *dv;
699 	struct wsmux_softc *muxsc;
700 {
701 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
702 	struct wsmux_softc *nsc = muxsc ? sc : 0;
703 	struct device *displaydv = muxsc ? muxsc->sc_displaydv : 0;
704 	struct device *odisplaydv;
705 	struct wsplink *m;
706 	int error, ok;
707 
708 	DPRINTF(("wsmux_set_display: %s: displaydv=%p\n",
709 		 sc->sc_dv.dv_xname, displaydv));
710 
711 	if (displaydv) {
712 		if (sc->sc_displaydv)
713 			return (EBUSY);
714 	} else {
715 		if (sc->sc_displaydv == NULL)
716 			return (ENXIO);
717 	}
718 
719 	odisplaydv = sc->sc_displaydv;
720 	sc->sc_displaydv = displaydv;
721 
722 	if (displaydv)
723 		printf("%s: connecting to %s\n",
724 		       sc->sc_dv.dv_xname, displaydv->dv_xname);
725 	ok = 0;
726 	error = 0;
727 	for (m = LIST_FIRST(&sc->sc_reals); m; m = LIST_NEXT(m, next)) {
728 		if (m->sc_ops->dsetdisplay &&
729 		    (nsc ? m->sc_mevents->io == 0 && *m->sc_muxp == 0 :
730 		           *m->sc_muxp == sc)) {
731 			error = m->sc_ops->dsetdisplay(m->sc, nsc);
732 			DPRINTF(("wsmux_set_display: m=%p dev=%s error=%d\n",
733 				 m, m->sc->dv_xname, error));
734 			if (!error) {
735 				ok = 1;
736 				*m->sc_muxp = nsc;
737 #ifdef WSDISPLAY_COMPAT_RAWKBD
738 				DPRINTF(("wsmux_set_display: on %s set rawkbd=%d\n",
739 					 m->sc->dv_xname, sc->sc_rawkbd));
740 				(void)m->sc_ops->dioctl(m->sc,
741 					     WSKBDIO_SETMODE,
742 					     (caddr_t)&sc->sc_rawkbd,
743 					     0, 0);
744 #endif
745 			}
746 		}
747 	}
748 	if (ok)
749 		error = 0;
750 
751 	if (displaydv == NULL)
752 		printf("%s: disconnecting from %s\n",
753 		       sc->sc_dv.dv_xname, odisplaydv->dv_xname);
754 
755 	return (error);
756 }
757 
758 int
759 wsmux_isset_display(dv)
760 	struct device *dv;
761 {
762 	struct wsmux_softc *sc = (struct wsmux_softc *)dv;
763 
764 	if (sc->sc_displaydv != NULL)
765 		return (1);
766 
767 	return (0);
768 }
769 
770 #endif /* NWSMUX > 0 || (NWSDISPLAY > 0 && NWSKBD > 0) */
771