1/* $NetBSD: wsdisplay.c,v 1.166 2023/03/01 08:42:33 riastradh Exp $ */
2
3/*
4 * Copyright (c) 1996, 1997 Christopher G. Demetriou.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *      This product includes software developed by Christopher G. Demetriou
17 *	for the NetBSD Project.
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__KERNEL_RCSID(0, "$NetBSD: wsdisplay.c,v 1.166 2023/03/01 08:42:33 riastradh Exp $");
35
36#ifdef _KERNEL_OPT
37#include "opt_wsdisplay_compat.h"
38#include "opt_wsmsgattrs.h"
39#endif
40
41#include "wskbd.h"
42#include "wsmux.h"
43#include "wsdisplay.h"
44
45#include <sys/param.h>
46#include <sys/conf.h>
47#include <sys/device.h>
48#include <sys/ioctl.h>
49#include <sys/poll.h>
50#include <sys/kernel.h>
51#include <sys/proc.h>
52#include <sys/malloc.h>
53#include <sys/syslog.h>
54#include <sys/systm.h>
55#include <sys/tty.h>
56#include <sys/signalvar.h>
57#include <sys/errno.h>
58#include <sys/fcntl.h>
59#include <sys/vnode.h>
60#include <sys/kauth.h>
61#include <sys/sysctl.h>
62
63#include <dev/wscons/wsconsio.h>
64#include <dev/wscons/wseventvar.h>
65#include <dev/wscons/wsmuxvar.h>
66#include <dev/wscons/wsdisplayvar.h>
67#include <dev/wscons/wsksymvar.h>
68#include <dev/wscons/wsksymdef.h>
69#include <dev/wscons/wsemulvar.h>
70#include <dev/wscons/wscons_callbacks.h>
71#include <dev/cons.h>
72
73#include "locators.h"
74#include "ioconf.h"
75
76#ifdef WSDISPLAY_MULTICONS
77static bool wsdisplay_multicons_enable = true;
78static bool wsdisplay_multicons_suspended = false;
79#endif
80
81/* Console device before replaced by wsdisplay */
82static struct consdev *wsdisplay_ocn;
83
84struct wsscreen_internal {
85	const struct wsdisplay_emulops *emulops;
86	void	*emulcookie;
87
88	const struct wsscreen_descr *scrdata;
89
90	const struct wsemul_ops *wsemul;
91	void	*wsemulcookie;
92};
93
94struct wsscreen {
95	struct wsscreen_internal *scr_dconf;
96
97	struct tty *scr_tty;
98	int	scr_hold_screen;		/* hold tty output */
99
100	int scr_flags;
101#define SCR_OPEN 1		/* is it open? */
102#define SCR_WAITACTIVE 2	/* someone waiting on activation */
103#define SCR_GRAPHICS 4		/* graphics mode, no text (emulation) output */
104#define	SCR_DUMBFB 8		/* in use as a dumb fb (iff SCR_GRAPHICS) */
105	const struct wscons_syncops *scr_syncops;
106	void *scr_synccookie;
107
108#ifdef WSDISPLAY_COMPAT_RAWKBD
109	int scr_rawkbd;
110#endif
111
112#ifdef WSDISPLAY_MULTICONS
113	callout_t scr_getc_ch;
114#endif
115
116	struct wsdisplay_softc *sc;
117
118	/* XXX this is to support a hack in emulinput, see comment below */
119	int scr_in_ttyoutput;
120};
121
122static struct wsscreen *wsscreen_attach(struct wsdisplay_softc *, int,
123				 const char *,
124				 const struct wsscreen_descr *, void *,
125				 int, int, long);
126static void wsscreen_detach(struct wsscreen *);
127static int wsdisplay_addscreen(struct wsdisplay_softc *, int, const char *, const char *);
128static void wsdisplay_addscreen_print(struct wsdisplay_softc *, int, int);
129static void wsdisplay_closescreen(struct wsdisplay_softc *, struct wsscreen *);
130static int wsdisplay_delscreen(struct wsdisplay_softc *, int, int);
131
132#define WSDISPLAY_MAXSCREEN 8
133
134struct wsdisplay_softc {
135	device_t sc_dev;
136
137	const struct wsdisplay_accessops *sc_accessops;
138	void	*sc_accesscookie;
139
140	const struct wsscreen_list *sc_scrdata;
141#ifdef WSDISPLAY_SCROLLSUPPORT
142	struct wsdisplay_scroll_data sc_scroll_values;
143#endif
144
145	struct wsscreen *sc_scr[WSDISPLAY_MAXSCREEN];
146	int sc_focusidx;	/* available only if sc_focus isn't null */
147	struct wsscreen *sc_focus;
148
149	struct wseventvar evar;
150
151	int	sc_isconsole;
152
153	int sc_flags;
154#define SC_SWITCHPENDING 1
155#define SC_SWITCHERROR 2
156#define SC_XATTACHED 4 /* X server active */
157	kmutex_t sc_flagsmtx; /* for flags, might also be used for focus */
158	kcondvar_t sc_flagscv;
159
160	int sc_screenwanted, sc_oldscreen; /* valid with SC_SWITCHPENDING */
161
162#if NWSKBD > 0
163	struct wsevsrc *sc_input;
164#ifdef WSDISPLAY_COMPAT_RAWKBD
165	int sc_rawkbd;
166#endif
167#endif /* NWSKBD > 0 */
168};
169
170#ifdef WSDISPLAY_SCROLLSUPPORT
171
172struct wsdisplay_scroll_data wsdisplay_default_scroll_values = {
173	WSDISPLAY_SCROLL_DOALL,
174	25,
175	2,
176};
177#endif
178
179/* Autoconfiguration definitions. */
180static int wsdisplay_emul_match(device_t , cfdata_t, void *);
181static void wsdisplay_emul_attach(device_t, device_t, void *);
182static int wsdisplay_emul_detach(device_t, int);
183static int wsdisplay_noemul_match(device_t, cfdata_t, void *);
184static void wsdisplay_noemul_attach(device_t, device_t, void *);
185static bool wsdisplay_suspend(device_t, const pmf_qual_t *);
186
187CFATTACH_DECL_NEW(wsdisplay_emul, sizeof (struct wsdisplay_softc),
188    wsdisplay_emul_match, wsdisplay_emul_attach, wsdisplay_emul_detach, NULL);
189
190CFATTACH_DECL_NEW(wsdisplay_noemul, sizeof (struct wsdisplay_softc),
191    wsdisplay_noemul_match, wsdisplay_noemul_attach, NULL, NULL);
192
193dev_type_open(wsdisplayopen);
194dev_type_close(wsdisplayclose);
195dev_type_read(wsdisplayread);
196dev_type_write(wsdisplaywrite);
197dev_type_ioctl(wsdisplayioctl);
198dev_type_stop(wsdisplaystop);
199dev_type_tty(wsdisplaytty);
200dev_type_poll(wsdisplaypoll);
201dev_type_mmap(wsdisplaymmap);
202dev_type_kqfilter(wsdisplaykqfilter);
203
204const struct cdevsw wsdisplay_cdevsw = {
205	.d_open = wsdisplayopen,
206	.d_close = wsdisplayclose,
207	.d_read = wsdisplayread,
208	.d_write = wsdisplaywrite,
209	.d_ioctl = wsdisplayioctl,
210	.d_stop = wsdisplaystop,
211	.d_tty = wsdisplaytty,
212	.d_poll = wsdisplaypoll,
213	.d_mmap = wsdisplaymmap,
214	.d_kqfilter = wsdisplaykqfilter,
215	.d_discard = nodiscard,
216	.d_flag = D_TTY
217};
218
219static void wsdisplaystart(struct tty *);
220static int wsdisplayparam(struct tty *, struct termios *);
221
222
223#define	WSDISPLAYUNIT(dev)	(minor(dev) >> 8)
224#define	WSDISPLAYSCREEN(dev)	(minor(dev) & 0xff)
225#define ISWSDISPLAYSTAT(dev)	(WSDISPLAYSCREEN(dev) == 254)
226#define ISWSDISPLAYCTL(dev)	(WSDISPLAYSCREEN(dev) == 255)
227#define WSDISPLAYMINOR(unit, screen)	(((unit) << 8) | (screen))
228
229#define	WSSCREEN_HAS_EMULATOR(scr)	((scr)->scr_dconf->wsemul != NULL)
230#define	WSSCREEN_HAS_TTY(scr)	((scr)->scr_tty != NULL)
231
232static void wsdisplay_common_attach(struct wsdisplay_softc *sc,
233	    int console, int kbdmux, const struct wsscreen_list *,
234	    const struct wsdisplay_accessops *accessops,
235	    void *accesscookie);
236
237#ifdef WSDISPLAY_COMPAT_RAWKBD
238int wsdisplay_update_rawkbd(struct wsdisplay_softc *,
239				 struct wsscreen *);
240#endif
241
242static int wsdisplay_console_initted;
243static int wsdisplay_console_attached;
244static struct wsdisplay_softc *wsdisplay_console_device;
245static struct wsscreen_internal wsdisplay_console_conf;
246
247static int wsdisplay_getc(dev_t);
248static void wsdisplay_pollc(dev_t, int);
249
250static int wsdisplay_cons_pollmode;
251static int (*wsdisplay_cons_kbd_getc)(dev_t);
252static void (*wsdisplay_cons_kbd_pollc)(dev_t, int);
253
254static struct consdev wsdisplay_cons = {
255	.cn_getc = wsdisplay_getc,
256	.cn_putc = wsdisplay_cnputc,
257	.cn_pollc = wsdisplay_pollc,
258	.cn_dev = NODEV,
259	.cn_pri = CN_NORMAL
260};
261
262#ifndef WSDISPLAY_DEFAULTSCREENS
263# define WSDISPLAY_DEFAULTSCREENS	0
264#endif
265int wsdisplay_defaultscreens = WSDISPLAY_DEFAULTSCREENS;
266
267static int wsdisplay_switch1(device_t, int, int);
268static void wsdisplay_switch1_cb(void *, int, int);
269static int wsdisplay_switch2(device_t, int, int);
270static void wsdisplay_switch2_cb(void *, int, int);
271static int wsdisplay_switch3(device_t, int, int);
272static void wsdisplay_switch3_cb(void *, int, int);
273
274static void wsdisplay_swdone_cb(void *, int, int);
275static int wsdisplay_dosync(struct wsdisplay_softc *, int);
276
277int wsdisplay_clearonclose;
278
279#ifdef WSDISPLAY_MULTICONS
280/*
281 * Replace cn_isconsole() so that we can enter DDB from old console.
282 */
283bool
284wsdisplay_cn_isconsole(dev_t dev)
285{
286
287	return (cn_tab != NULL && cn_tab->cn_dev == dev) ||
288	    (cn_tab == &wsdisplay_cons && !wsdisplay_multicons_suspended &&
289	    wsdisplay_multicons_enable && wsdisplay_ocn != NULL &&
290	    wsdisplay_ocn->cn_dev == dev);
291}
292
293static void
294wsscreen_getc_poll(void *priv)
295{
296	struct wsscreen *scr = priv;
297	int c;
298
299	if (wsdisplay_multicons_enable &&
300	    wsdisplay_ocn && wsdisplay_ocn->cn_getc &&
301	    WSSCREEN_HAS_EMULATOR(scr) && WSSCREEN_HAS_TTY(scr)) {
302		struct tty *tp = scr->scr_tty;
303		do {
304			c = wsdisplay_ocn->cn_getc(wsdisplay_ocn->cn_dev);
305			if (c >= 0)
306				(*tp->t_linesw->l_rint)((unsigned char)c, tp);
307		} while (c >= 0);
308	}
309
310	callout_schedule(&scr->scr_getc_ch, mstohz(10));
311}
312#endif
313
314static struct wsscreen *
315wsscreen_attach(struct wsdisplay_softc *sc, int console, const char *emul,
316	const struct wsscreen_descr *type, void *cookie, int ccol,
317	int crow, long defattr)
318{
319	struct wsscreen_internal *dconf;
320	struct wsscreen *scr;
321
322	scr = malloc(sizeof(struct wsscreen), M_DEVBUF, M_WAITOK);
323
324	if (console) {
325		dconf = &wsdisplay_console_conf;
326		/*
327		 * If there's an emulation, tell it about the callback argument.
328		 * The other stuff is already there.
329		 */
330		if (dconf->wsemul != NULL)
331			(*dconf->wsemul->attach)(1, 0, 0, 0, 0, scr, 0);
332	} else { /* not console */
333		dconf = malloc(sizeof(struct wsscreen_internal),
334			       M_DEVBUF, M_WAITOK);
335		dconf->emulops = type->textops;
336		dconf->emulcookie = cookie;
337		if (dconf->emulops) {
338			dconf->wsemul = wsemul_pick(emul);
339			if (dconf->wsemul == NULL) {
340				free(dconf, M_DEVBUF);
341				free(scr, M_DEVBUF);
342				return NULL;
343			}
344			dconf->wsemulcookie =
345			  (*dconf->wsemul->attach)(0, type, cookie,
346						   ccol, crow, scr, defattr);
347		} else
348			dconf->wsemul = NULL;
349		dconf->scrdata = type;
350	}
351
352	scr->scr_dconf = dconf;
353
354	scr->scr_tty = tty_alloc();
355	tty_attach(scr->scr_tty);
356	scr->scr_hold_screen = 0;
357	if (WSSCREEN_HAS_EMULATOR(scr))
358		scr->scr_flags = 0;
359	else
360		scr->scr_flags = SCR_GRAPHICS;
361
362	scr->scr_syncops = 0;
363	scr->sc = sc;
364#ifdef WSDISPLAY_COMPAT_RAWKBD
365	scr->scr_rawkbd = 0;
366#endif
367#ifdef WSDISPLAY_MULTICONS
368	callout_init(&scr->scr_getc_ch, 0);
369	callout_setfunc(&scr->scr_getc_ch, wsscreen_getc_poll, scr);
370	if (console)
371		callout_schedule(&scr->scr_getc_ch, mstohz(10));
372#endif
373	return scr;
374}
375
376static void
377wsscreen_detach(struct wsscreen *scr)
378{
379	u_int ccol, crow; /* XXX */
380
381	if (WSSCREEN_HAS_TTY(scr)) {
382		tty_detach(scr->scr_tty);
383		tty_free(scr->scr_tty);
384	}
385	if (WSSCREEN_HAS_EMULATOR(scr)) {
386		(*scr->scr_dconf->wsemul->detach)(scr->scr_dconf->wsemulcookie,
387						  &ccol, &crow);
388		wsemul_drop(scr->scr_dconf->wsemul);
389	}
390	if (scr->scr_dconf->scrdata->capabilities & WSSCREEN_FREE)
391		free(__UNCONST(scr->scr_dconf->scrdata), M_DEVBUF);
392#ifdef WSDISPLAY_MULTICONS
393	callout_halt(&scr->scr_getc_ch, NULL);
394	callout_destroy(&scr->scr_getc_ch);
395#endif
396	free(scr->scr_dconf, M_DEVBUF);
397	free(scr, M_DEVBUF);
398}
399
400const struct wsscreen_descr *
401wsdisplay_screentype_pick(const struct wsscreen_list *scrdata, const char *name)
402{
403	int i;
404	const struct wsscreen_descr *scr;
405
406	KASSERT(scrdata->nscreens > 0);
407	if (name == NULL)
408		return scrdata->screens[0];
409
410	for (i = 0; i < scrdata->nscreens; i++) {
411		scr = scrdata->screens[i];
412		if (!strcmp(name, scr->name))
413			return scr;
414	}
415
416	return 0;
417}
418
419/*
420 * print info about attached screen
421 */
422static void
423wsdisplay_addscreen_print(struct wsdisplay_softc *sc, int idx, int count)
424{
425	aprint_verbose_dev(sc->sc_dev, "screen %d", idx);
426	if (count > 1)
427		aprint_verbose("-%d", idx + (count-1));
428	aprint_verbose(" added (%s", sc->sc_scr[idx]->scr_dconf->scrdata->name);
429	if (WSSCREEN_HAS_EMULATOR(sc->sc_scr[idx])) {
430		aprint_verbose(", %s emulation",
431			sc->sc_scr[idx]->scr_dconf->wsemul->name);
432	}
433	aprint_verbose(")\n");
434}
435
436static int
437wsdisplay_addscreen(struct wsdisplay_softc *sc, int idx,
438	const char *screentype, const char *emul)
439{
440	const struct wsscreen_descr *scrdesc;
441	struct wsscreen_descr *scrdescr2;
442	int error;
443	void *cookie;
444	int ccol, crow;
445	long defattr;
446	struct wsscreen *scr;
447	int s;
448
449	if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN)
450		return EINVAL;
451	if (sc->sc_scr[idx] != NULL)
452		return EBUSY;
453	scrdesc = wsdisplay_screentype_pick(sc->sc_scrdata, screentype);
454	if (!scrdesc)
455		return ENXIO;
456
457	/*
458	 * if this screen can resize we need to copy the descr so each screen
459	 * gets its own
460	 */
461	if (scrdesc->capabilities & WSSCREEN_RESIZE) {
462		/* we want per screen wsscreen_descr */
463		scrdescr2 = malloc(sizeof(struct wsscreen_descr), M_DEVBUF, M_WAITOK);
464		memcpy(scrdescr2, scrdesc, sizeof(struct wsscreen_descr));
465		scrdescr2->capabilities |= WSSCREEN_FREE;
466		scrdesc = scrdescr2;
467	}
468
469	error = (*sc->sc_accessops->alloc_screen)(sc->sc_accesscookie,
470			scrdesc, &cookie, &ccol, &crow, &defattr);
471	if (error)
472		return error;
473
474	scr = wsscreen_attach(sc, 0, emul, scrdesc,
475			      cookie, ccol, crow, defattr);
476	if (scr == NULL) {
477		(*sc->sc_accessops->free_screen)(sc->sc_accesscookie,
478						 cookie);
479		return ENXIO;
480	}
481
482	sc->sc_scr[idx] = scr;
483
484	/* if no screen has focus yet, activate the first we get */
485	s = spltty();
486	if (!sc->sc_focus) {
487		(*sc->sc_accessops->show_screen)(sc->sc_accesscookie,
488						 scr->scr_dconf->emulcookie,
489						 0, 0, 0);
490		sc->sc_focusidx = idx;
491		sc->sc_focus = scr;
492	}
493	splx(s);
494	return 0;
495}
496
497static void
498wsdisplay_closescreen(struct wsdisplay_softc *sc, struct wsscreen *scr)
499{
500	int maj, mn, idx;
501
502	/* hangup */
503	if (WSSCREEN_HAS_TTY(scr)) {
504		struct tty *tp = scr->scr_tty;
505		(*tp->t_linesw->l_modem)(tp, 0);
506	}
507
508	/* locate the major number */
509	maj = cdevsw_lookup_major(&wsdisplay_cdevsw);
510	/* locate the screen index */
511	for (idx = 0; idx < WSDISPLAY_MAXSCREEN; idx++)
512		if (scr == sc->sc_scr[idx])
513			break;
514#ifdef DIAGNOSTIC
515	if (idx == WSDISPLAY_MAXSCREEN)
516		panic("wsdisplay_forceclose: bad screen");
517#endif
518
519	/* nuke the vnodes */
520	mn = WSDISPLAYMINOR(device_unit(sc->sc_dev), idx);
521	vdevgone(maj, mn, mn, VCHR);
522}
523
524#ifdef WSDISPLAY_SCROLLSUPPORT
525void
526wsdisplay_scroll(void *arg, int op)
527{
528	device_t dv = arg;
529	struct wsdisplay_softc *sc = device_private(dv);
530	struct wsscreen *scr;
531	int lines;
532
533	scr = sc->sc_focus;
534
535	if (!scr)
536		return;
537
538	if (op == WSDISPLAY_SCROLL_RESET)
539		lines = 0;
540	else {
541		lines = (op & WSDISPLAY_SCROLL_LOW) ?
542			sc->sc_scroll_values.slowlines :
543			sc->sc_scroll_values.fastlines;
544		if (op & WSDISPLAY_SCROLL_BACKWARD)
545			lines = -(lines);
546	}
547
548	if (sc->sc_accessops->scroll) {
549		(*sc->sc_accessops->scroll)(sc->sc_accesscookie,
550		    sc->sc_focus->scr_dconf->emulcookie, lines);
551	}
552}
553#endif
554
555static int
556wsdisplay_delscreen(struct wsdisplay_softc *sc, int idx, int flags)
557{
558	struct wsscreen *scr;
559	int s;
560	void *cookie;
561
562	if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN)
563		return EINVAL;
564	if ((scr = sc->sc_scr[idx]) == NULL)
565		return ENXIO;
566
567	if (scr->scr_dconf == &wsdisplay_console_conf ||
568	    scr->scr_syncops ||
569	    ((scr->scr_flags & SCR_OPEN) && !(flags & WSDISPLAY_DELSCR_FORCE)))
570		return EBUSY;
571
572	wsdisplay_closescreen(sc, scr);
573
574	/*
575	 * delete pointers, so neither device entries
576	 * nor keyboard input can reference it anymore
577	 */
578	s = spltty();
579	if (sc->sc_focus == scr) {
580		sc->sc_focus = 0;
581#ifdef WSDISPLAY_COMPAT_RAWKBD
582		wsdisplay_update_rawkbd(sc, 0);
583#endif
584	}
585	sc->sc_scr[idx] = 0;
586	splx(s);
587
588	/*
589	 * Wake up processes waiting for the screen to
590	 * be activated. Sleepers must check whether
591	 * the screen still exists.
592	 */
593	if (scr->scr_flags & SCR_WAITACTIVE)
594		wakeup(scr);
595
596	/* save a reference to the graphics screen */
597	cookie = scr->scr_dconf->emulcookie;
598
599	wsscreen_detach(scr);
600
601	(*sc->sc_accessops->free_screen)(sc->sc_accesscookie,
602					 cookie);
603
604	aprint_verbose_dev(sc->sc_dev, "screen %d deleted\n", idx);
605	return 0;
606}
607
608/*
609 * Autoconfiguration functions.
610 */
611int
612wsdisplay_emul_match(device_t parent, cfdata_t match, void *aux)
613{
614	struct wsemuldisplaydev_attach_args *ap = aux;
615
616	if (match->cf_loc[WSEMULDISPLAYDEVCF_CONSOLE] !=
617	    WSEMULDISPLAYDEVCF_CONSOLE_DEFAULT) {
618		/*
619		 * If console-ness of device specified, either match
620		 * exactly (at high priority), or fail.
621		 */
622		if (match->cf_loc[WSEMULDISPLAYDEVCF_CONSOLE] != 0 &&
623		    ap->console != 0)
624			return 10;
625		else
626			return 0;
627	}
628
629	/* If console-ness unspecified, it wins. */
630	return 1;
631}
632
633void
634wsdisplay_emul_attach(device_t parent, device_t self, void *aux)
635{
636	struct wsdisplay_softc *sc = device_private(self);
637	struct wsemuldisplaydev_attach_args *ap = aux;
638
639	sc->sc_dev = self;
640
641	/* Don't allow more than one console to attach */
642	if (wsdisplay_console_attached && ap->console)
643		ap->console = 0;
644
645	wsdisplay_common_attach(sc, ap->console,
646	     device_cfdata(self)->cf_loc[WSEMULDISPLAYDEVCF_KBDMUX],
647	     ap->scrdata, ap->accessops, ap->accesscookie);
648
649	if (ap->console) {
650		int maj;
651
652		/* locate the major number */
653		maj = cdevsw_lookup_major(&wsdisplay_cdevsw);
654
655		cn_tab->cn_dev = makedev(maj, WSDISPLAYMINOR(device_unit(self),
656					 0));
657	}
658}
659
660/* Print function (for parent devices). */
661int
662wsemuldisplaydevprint(void *aux, const char *pnp)
663{
664#if 0 /* -Wunused */
665	struct wsemuldisplaydev_attach_args *ap = aux;
666#endif
667
668	if (pnp)
669		aprint_normal("wsdisplay at %s", pnp);
670#if 0 /* don't bother; it's ugly */
671	aprint_normal(" console %d", ap->console);
672#endif
673
674	return UNCONF;
675}
676
677int
678wsdisplay_emul_detach(device_t dev, int how)
679{
680	struct wsdisplay_softc *sc = device_private(dev);
681	int flag, i, res;
682
683	flag = (how & DETACH_FORCE ? WSDISPLAY_DELSCR_FORCE : 0);
684	for (i = 0; i < WSDISPLAY_MAXSCREEN; i++)
685		if (sc->sc_scr[i]) {
686			res = wsdisplay_delscreen(sc, i, flag);
687			if (res)
688				return res;
689		}
690
691	cv_destroy(&sc->sc_flagscv);
692	mutex_destroy(&sc->sc_flagsmtx);
693	return 0;
694}
695
696int
697wsdisplay_noemul_match(device_t parent, cfdata_t match, void *aux)
698{
699#if 0 /* -Wunused */
700	struct wsdisplaydev_attach_args *ap = aux;
701#endif
702
703	/* Always match. */
704	return 1;
705}
706
707void
708wsdisplay_noemul_attach(device_t parent, device_t self, void *aux)
709{
710	struct wsdisplay_softc *sc = device_private(self);
711	struct wsdisplaydev_attach_args *ap = aux;
712
713	sc->sc_dev = self;
714
715	wsdisplay_common_attach(sc, 0,
716	    device_cfdata(self)->cf_loc[WSDISPLAYDEVCF_KBDMUX], NULL,
717	    ap->accessops, ap->accesscookie);
718}
719
720static void
721wsdisplay_swdone_cb(void *arg, int error, int waitok)
722{
723	struct wsdisplay_softc *sc = arg;
724
725	mutex_enter(&sc->sc_flagsmtx);
726	KASSERT(sc->sc_flags & SC_SWITCHPENDING);
727	if (error)
728		sc->sc_flags |= SC_SWITCHERROR;
729	sc->sc_flags &= ~SC_SWITCHPENDING;
730	cv_signal(&sc->sc_flagscv);
731	mutex_exit(&sc->sc_flagsmtx);
732}
733
734static int
735wsdisplay_dosync(struct wsdisplay_softc *sc, int attach)
736{
737	struct wsscreen *scr;
738	int (*op)(void *, int, void (*)(void *, int, int), void *);
739	int res;
740
741	scr = sc->sc_focus;
742	if (!scr || !scr->scr_syncops)
743		return 0; /* XXX check SCR_GRAPHICS? */
744
745	sc->sc_flags |= SC_SWITCHPENDING;
746	sc->sc_flags &= ~SC_SWITCHERROR;
747	if (attach)
748		op = scr->scr_syncops->attach;
749	else
750		op = scr->scr_syncops->detach;
751	res = (*op)(scr->scr_synccookie, 1, wsdisplay_swdone_cb, sc);
752	if (res == EAGAIN) {
753		/* wait for callback */
754		mutex_enter(&sc->sc_flagsmtx);
755		while (sc->sc_flags & SC_SWITCHPENDING)
756			cv_wait_sig(&sc->sc_flagscv, &sc->sc_flagsmtx);
757		mutex_exit(&sc->sc_flagsmtx);
758		if (sc->sc_flags & SC_SWITCHERROR)
759			return EIO; /* XXX pass real error */
760	} else {
761		sc->sc_flags &= ~SC_SWITCHPENDING;
762		if (res)
763			return res;
764	}
765	if (attach)
766		sc->sc_flags |= SC_XATTACHED;
767	else
768		sc->sc_flags &= ~SC_XATTACHED;
769	return 0;
770}
771
772int
773wsdisplay_handlex(int resume)
774{
775	int i, res;
776	device_t dv;
777
778	for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) {
779		dv = device_lookup(&wsdisplay_cd, i);
780		if (!dv)
781			continue;
782		res = wsdisplay_dosync(device_private(dv), resume);
783		if (res)
784			return res;
785	}
786	return 0;
787}
788
789static bool
790wsdisplay_suspend(device_t dv, const pmf_qual_t *qual)
791{
792	struct wsdisplay_softc *sc = device_private(dv);
793	struct wsscreen *scr = sc->sc_focus;
794
795	if (sc->sc_flags & SC_XATTACHED) {
796		KASSERT(scr);
797		KASSERT(scr->scr_syncops);
798	}
799
800#if 1
801	/*
802	 * XXX X servers should have been detached earlier.
803	 * pmf currently ignores our return value and suspends the system
804	 * after device suspend failures. We try to avoid bigger damage
805	 * and try to detach the X server here. This is not safe because
806	 * other parts of the system which the X server deals with
807	 * might already be suspended.
808	 */
809	if (sc->sc_flags & SC_XATTACHED) {
810		printf("%s: emergency X server detach\n", device_xname(dv));
811		wsdisplay_dosync(sc, 0);
812	}
813#endif
814	return !(sc->sc_flags & SC_XATTACHED);
815}
816
817/* Print function (for parent devices). */
818int
819wsdisplaydevprint(void *aux, const char *pnp)
820{
821#if 0 /* -Wunused */
822	struct wsdisplaydev_attach_args *ap = aux;
823#endif
824
825	if (pnp)
826		aprint_normal("wsdisplay at %s", pnp);
827
828	return UNCONF;
829}
830
831static void
832wsdisplay_common_attach(struct wsdisplay_softc *sc, int console, int kbdmux,
833	const struct wsscreen_list *scrdata,
834	const struct wsdisplay_accessops *accessops,
835	void *accesscookie)
836{
837	int i, start=0;
838#if NWSKBD > 0
839	struct wsevsrc *kme;
840#if NWSMUX > 0
841	struct wsmux_softc *mux;
842
843	if (kbdmux >= 0)
844		mux = wsmux_getmux(kbdmux);
845	else
846		mux = wsmux_create("dmux", device_unit(sc->sc_dev));
847	sc->sc_input = &mux->sc_base;
848	mux->sc_base.me_dispdv = sc->sc_dev;
849	aprint_normal(" kbdmux %d", kbdmux);
850#else
851	if (kbdmux >= 0)
852		aprint_normal(" (kbdmux ignored)");
853#endif
854#endif
855
856	sc->sc_isconsole = console;
857
858	if (console) {
859		KASSERT(wsdisplay_console_initted);
860		KASSERT(wsdisplay_console_device == NULL);
861
862		sc->sc_scr[0] = wsscreen_attach(sc, 1, 0, 0, 0, 0, 0, 0);
863		wsdisplay_console_device = sc;
864
865		aprint_normal(": console (%s, %s emulation)",
866		       wsdisplay_console_conf.scrdata->name,
867		       wsdisplay_console_conf.wsemul->name);
868
869#if NWSKBD > 0
870		kme = wskbd_set_console_display(sc->sc_dev, sc->sc_input);
871		if (kme != NULL)
872			aprint_normal(", using %s", device_xname(kme->me_dv));
873#if NWSMUX == 0
874		sc->sc_input = kme;
875#endif
876#endif
877
878		sc->sc_focusidx = 0;
879		sc->sc_focus = sc->sc_scr[0];
880		start = 1;
881
882		wsdisplay_console_attached = 1;
883	}
884	aprint_normal("\n");
885	aprint_naive("\n");
886
887#if NWSKBD > 0 && NWSMUX > 0
888	wsmux_set_display(mux, sc->sc_dev);
889#endif
890
891	mutex_init(&sc->sc_flagsmtx, MUTEX_DEFAULT, IPL_NONE);
892	cv_init(&sc->sc_flagscv, "wssw");
893
894	sc->sc_accessops = accessops;
895	sc->sc_accesscookie = accesscookie;
896	sc->sc_scrdata = scrdata;
897
898#ifdef WSDISPLAY_SCROLLSUPPORT
899	sc->sc_scroll_values = wsdisplay_default_scroll_values;
900#endif
901
902	/*
903	 * Set up a number of virtual screens if wanted. The
904	 * WSDISPLAYIO_ADDSCREEN ioctl is more flexible, so this code
905	 * is for special cases like installation kernels.
906	 */
907	for (i = start; i < wsdisplay_defaultscreens; i++) {
908		if (wsdisplay_addscreen(sc, i, 0, 0))
909			break;
910	}
911
912	if (i > start)
913		wsdisplay_addscreen_print(sc, start, i-start);
914
915	if (!pmf_device_register(sc->sc_dev, wsdisplay_suspend, NULL))
916		aprint_error_dev(sc->sc_dev,
917		    "couldn't establish power handler\n");
918}
919
920void
921wsdisplay_cnattach(const struct wsscreen_descr *type, void *cookie,
922	int ccol, int crow, long defattr)
923{
924	const struct wsemul_ops *wsemul;
925
926	KASSERT(wsdisplay_console_initted < 2);
927	KASSERT(type->nrows > 0);
928	KASSERT(type->ncols > 0);
929	KASSERT(crow < type->nrows);
930	KASSERT(ccol < type->ncols);
931
932	wsdisplay_console_conf.emulops = type->textops;
933	wsdisplay_console_conf.emulcookie = cookie;
934	wsdisplay_console_conf.scrdata = type;
935
936	wsemul = wsemul_pick(0); /* default */
937	wsdisplay_console_conf.wsemul = wsemul;
938	wsdisplay_console_conf.wsemulcookie = (*wsemul->cnattach)(type, cookie,
939								  ccol, crow,
940								  defattr);
941
942	if (cn_tab != &wsdisplay_cons)
943		wsdisplay_ocn = cn_tab;
944
945	if (wsdisplay_ocn != NULL && wsdisplay_ocn->cn_halt != NULL)
946		wsdisplay_ocn->cn_halt(wsdisplay_ocn->cn_dev);
947
948	cn_tab = &wsdisplay_cons;
949	wsdisplay_console_initted = 2;
950}
951
952void
953wsdisplay_preattach(const struct wsscreen_descr *type, void *cookie,
954	int ccol, int crow, long defattr)
955{
956	const struct wsemul_ops *wsemul;
957
958	KASSERT(!wsdisplay_console_initted);
959	KASSERT(type->nrows > 0);
960	KASSERT(type->ncols > 0);
961	KASSERT(crow < type->nrows);
962	KASSERT(ccol < type->ncols);
963
964	wsdisplay_console_conf.emulops = type->textops;
965	wsdisplay_console_conf.emulcookie = cookie;
966	wsdisplay_console_conf.scrdata = type;
967
968	wsemul = wsemul_pick(0); /* default */
969	wsdisplay_console_conf.wsemul = wsemul;
970	wsdisplay_console_conf.wsemulcookie = (*wsemul->cnattach)(type, cookie,
971								  ccol, crow,
972								  defattr);
973
974	if (cn_tab != &wsdisplay_cons)
975		wsdisplay_ocn = cn_tab;
976
977	if (wsdisplay_ocn != NULL && wsdisplay_ocn->cn_halt != NULL)
978		wsdisplay_ocn->cn_halt(wsdisplay_ocn->cn_dev);
979
980	cn_tab = &wsdisplay_cons;
981	wsdisplay_console_initted = 1;
982}
983
984void
985wsdisplay_predetach(void)
986{
987	KASSERT(wsdisplay_console_initted == 1);
988
989	cn_tab = wsdisplay_ocn;
990	wsdisplay_console_initted = 0;
991}
992
993void
994wsdisplay_cndetach(void)
995{
996	KASSERT(wsdisplay_console_initted == 2);
997
998	cn_tab = wsdisplay_ocn;
999	wsdisplay_console_initted = 0;
1000}
1001
1002/*
1003 * Tty and cdevsw functions.
1004 */
1005int
1006wsdisplayopen(dev_t dev, int flag, int mode, struct lwp *l)
1007{
1008	struct wsdisplay_softc *sc;
1009	struct tty *tp;
1010	int newopen, error;
1011	struct wsscreen *scr;
1012
1013	sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev));
1014	if (sc == NULL)			/* make sure it was attached */
1015		return ENXIO;
1016
1017	if (ISWSDISPLAYSTAT(dev)) {
1018		wsevent_init(&sc->evar, l->l_proc);
1019		return 0;
1020	}
1021
1022	if (ISWSDISPLAYCTL(dev))
1023		return 0;
1024
1025	if (WSDISPLAYSCREEN(dev) >= WSDISPLAY_MAXSCREEN)
1026		return ENXIO;
1027	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1028		return ENXIO;
1029
1030	if (WSSCREEN_HAS_TTY(scr)) {
1031		tp = scr->scr_tty;
1032		tp->t_oproc = wsdisplaystart;
1033		tp->t_param = wsdisplayparam;
1034		tp->t_dev = dev;
1035		newopen = (tp->t_state & TS_ISOPEN) == 0;
1036
1037		if (kauth_authorize_device_tty(l->l_cred,
1038			KAUTH_DEVICE_TTY_OPEN, tp))
1039			return EBUSY;
1040
1041		if (newopen) {
1042			ttychars(tp);
1043			tp->t_iflag = TTYDEF_IFLAG;
1044			tp->t_oflag = TTYDEF_OFLAG;
1045			tp->t_cflag = TTYDEF_CFLAG;
1046			tp->t_lflag = TTYDEF_LFLAG;
1047			tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
1048			wsdisplayparam(tp, &tp->t_termios);
1049			ttsetwater(tp);
1050		}
1051		tp->t_state |= TS_CARR_ON;
1052
1053		error = ((*tp->t_linesw->l_open)(dev, tp));
1054		if (error)
1055			return error;
1056
1057		if (newopen && WSSCREEN_HAS_EMULATOR(scr)) {
1058			/* set window sizes as appropriate, and reset
1059			 the emulation */
1060			tp->t_winsize.ws_row = scr->scr_dconf->scrdata->nrows;
1061			tp->t_winsize.ws_col = scr->scr_dconf->scrdata->ncols;
1062
1063			/* wsdisplay_set_emulation() */
1064		}
1065	}
1066
1067	scr->scr_flags |= SCR_OPEN;
1068	return 0;
1069}
1070
1071int
1072wsdisplayclose(dev_t dev, int flag, int mode, struct lwp *l)
1073{
1074	device_t dv;
1075	struct wsdisplay_softc *sc;
1076	struct tty *tp;
1077	struct wsscreen *scr;
1078
1079	dv = device_lookup(&wsdisplay_cd, WSDISPLAYUNIT(dev));
1080	sc = device_private(dv);
1081
1082	if (ISWSDISPLAYSTAT(dev)) {
1083		wsevent_fini(&sc->evar);
1084		return 0;
1085	}
1086
1087	if (ISWSDISPLAYCTL(dev))
1088		return 0;
1089
1090	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1091		return 0;
1092
1093	if (WSSCREEN_HAS_TTY(scr)) {
1094		if (scr->scr_hold_screen) {
1095			int s;
1096
1097			/* XXX RESET KEYBOARD LEDS, etc. */
1098			s = spltty();	/* avoid conflict with keyboard */
1099			wsdisplay_kbdholdscreen(dv, 0);
1100			splx(s);
1101		}
1102		tp = scr->scr_tty;
1103		(*tp->t_linesw->l_close)(tp, flag);
1104		ttyclose(tp);
1105	}
1106
1107	if (scr->scr_syncops)
1108		(*scr->scr_syncops->destroy)(scr->scr_synccookie);
1109
1110	if (WSSCREEN_HAS_EMULATOR(scr)) {
1111		scr->scr_flags &= ~SCR_GRAPHICS;
1112		(*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie,
1113						 WSEMUL_RESET);
1114		if (wsdisplay_clearonclose)
1115			(*scr->scr_dconf->wsemul->reset)
1116				(scr->scr_dconf->wsemulcookie,
1117				 WSEMUL_CLEARSCREEN);
1118	}
1119
1120#ifdef WSDISPLAY_COMPAT_RAWKBD
1121	if (scr->scr_rawkbd) {
1122		int kbmode = WSKBD_TRANSLATED;
1123		(void)wsdisplay_internal_ioctl(sc, scr, WSKBDIO_SETMODE,
1124					       (void *)&kbmode, 0, l);
1125	}
1126#endif
1127
1128	scr->scr_flags &= ~SCR_OPEN;
1129
1130	return 0;
1131}
1132
1133int
1134wsdisplayread(dev_t dev, struct uio *uio, int flag)
1135{
1136	struct wsdisplay_softc *sc;
1137	struct tty *tp;
1138	struct wsscreen *scr;
1139	int error;
1140
1141	sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev));
1142
1143	if (ISWSDISPLAYSTAT(dev)) {
1144		error = wsevent_read(&sc->evar, uio, flag);
1145		return error;
1146	}
1147
1148	if (ISWSDISPLAYCTL(dev))
1149		return 0;
1150
1151	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1152		return ENXIO;
1153
1154	if (!WSSCREEN_HAS_TTY(scr))
1155		return ENODEV;
1156
1157	tp = scr->scr_tty;
1158	return (*tp->t_linesw->l_read)(tp, uio, flag);
1159}
1160
1161int
1162wsdisplaywrite(dev_t dev, struct uio *uio, int flag)
1163{
1164	struct wsdisplay_softc *sc;
1165	struct tty *tp;
1166	struct wsscreen *scr;
1167
1168	sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev));
1169
1170	if (ISWSDISPLAYSTAT(dev)) {
1171		return 0;
1172	}
1173
1174	if (ISWSDISPLAYCTL(dev))
1175		return 0;
1176
1177	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1178		return ENXIO;
1179
1180	if (!WSSCREEN_HAS_TTY(scr))
1181		return ENODEV;
1182
1183	tp = scr->scr_tty;
1184	return (*tp->t_linesw->l_write)(tp, uio, flag);
1185}
1186
1187int
1188wsdisplaypoll(dev_t dev, int events, struct lwp *l)
1189{
1190	struct wsdisplay_softc *sc;
1191	struct tty *tp;
1192	struct wsscreen *scr;
1193
1194	sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev));
1195
1196	if (ISWSDISPLAYSTAT(dev))
1197		return wsevent_poll(&sc->evar, events, l);
1198
1199	if (ISWSDISPLAYCTL(dev))
1200		return 0;
1201
1202	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1203		return POLLHUP;
1204
1205	if (!WSSCREEN_HAS_TTY(scr))
1206		return POLLERR;
1207
1208	tp = scr->scr_tty;
1209	return (*tp->t_linesw->l_poll)(tp, events, l);
1210}
1211
1212int
1213wsdisplaykqfilter(dev_t dev, struct knote *kn)
1214{
1215	struct wsdisplay_softc *sc;
1216	struct wsscreen *scr;
1217
1218	sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev));
1219
1220	if (ISWSDISPLAYCTL(dev))
1221		return 1;
1222
1223	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1224		return 1;
1225
1226
1227	if (WSSCREEN_HAS_TTY(scr))
1228		return ttykqfilter(dev, kn);
1229	else
1230		return 1;
1231}
1232
1233struct tty *
1234wsdisplaytty(dev_t dev)
1235{
1236	struct wsdisplay_softc *sc;
1237	struct wsscreen *scr;
1238
1239	sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev));
1240
1241	if (ISWSDISPLAYSTAT(dev))
1242		panic("wsdisplaytty() on status device");
1243
1244	if (ISWSDISPLAYCTL(dev))
1245		panic("wsdisplaytty() on ctl device");
1246
1247	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1248		return NULL;
1249
1250	return scr->scr_tty;
1251}
1252
1253int
1254wsdisplayioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
1255{
1256	device_t dv;
1257	struct wsdisplay_softc *sc;
1258	struct tty *tp;
1259	int error;
1260	struct wsscreen *scr;
1261
1262	dv = device_lookup(&wsdisplay_cd, WSDISPLAYUNIT(dev));
1263	sc = device_private(dv);
1264
1265#ifdef WSDISPLAY_COMPAT_USL
1266	error = wsdisplay_usl_ioctl1(dv, cmd, data, flag, l);
1267	if (error != EPASSTHROUGH)
1268		return error;
1269#endif
1270
1271	if (ISWSDISPLAYSTAT(dev))
1272		return wsdisplay_stat_ioctl(sc, cmd, data, flag, l);
1273
1274	if (ISWSDISPLAYCTL(dev))
1275		return wsdisplay_cfg_ioctl(sc, cmd, data, flag, l);
1276
1277	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1278		return ENXIO;
1279
1280	if (WSSCREEN_HAS_TTY(scr)) {
1281		tp = scr->scr_tty;
1282
1283		/* do the line discipline ioctls first */
1284		error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
1285		if (error != EPASSTHROUGH)
1286			return error;
1287
1288		/* then the tty ioctls */
1289		error = ttioctl(tp, cmd, data, flag, l);
1290		if (error != EPASSTHROUGH)
1291			return error;
1292	}
1293
1294#ifdef WSDISPLAY_COMPAT_USL
1295	error = wsdisplay_usl_ioctl2(sc, scr, cmd, data, flag, l);
1296	if (error != EPASSTHROUGH)
1297		return error;
1298#endif
1299
1300	return wsdisplay_internal_ioctl(sc, scr, cmd, data, flag, l);
1301}
1302
1303int
1304wsdisplay_param(device_t dv, u_long cmd, struct wsdisplay_param *dp)
1305{
1306	struct wsdisplay_softc *sc = device_private(dv);
1307	return (*sc->sc_accessops->ioctl)(sc->sc_accesscookie,
1308	    sc->sc_focus->scr_dconf->emulcookie,
1309	    cmd, (void *)dp, 0, NULL);
1310}
1311
1312int
1313wsdisplay_internal_ioctl(struct wsdisplay_softc *sc, struct wsscreen *scr,
1314	u_long cmd, void *data, int flag, struct lwp *l)
1315{
1316	int error;
1317	char namebuf[32];
1318	struct wsdisplay_font fd;
1319#ifdef WSDISPLAY_SCROLLSUPPORT
1320	struct wsdisplay_scroll_data *ksdp, *usdp;
1321#endif
1322
1323#if NWSKBD > 0
1324	struct wsevsrc *inp;
1325
1326#ifdef WSDISPLAY_COMPAT_RAWKBD
1327	switch (cmd) {
1328	case WSKBDIO_SETMODE:
1329		scr->scr_rawkbd = (*(int *)data == WSKBD_RAW);
1330		return wsdisplay_update_rawkbd(sc, scr);
1331	case WSKBDIO_GETMODE:
1332		*(int *)data = (scr->scr_rawkbd ?
1333				WSKBD_RAW : WSKBD_TRANSLATED);
1334		return 0;
1335	}
1336#endif
1337	inp = sc->sc_input;
1338	if (inp == NULL)
1339		return ENXIO;
1340	error = wsevsrc_display_ioctl(inp, cmd, data, flag, l);
1341	if (error != EPASSTHROUGH)
1342		return error;
1343#endif /* NWSKBD > 0 */
1344
1345	switch (cmd) {
1346	case WSDISPLAYIO_GMODE:
1347		if (scr->scr_flags & SCR_GRAPHICS) {
1348			if (scr->scr_flags & SCR_DUMBFB)
1349				*(u_int *)data = WSDISPLAYIO_MODE_DUMBFB;
1350			else
1351				*(u_int *)data = WSDISPLAYIO_MODE_MAPPED;
1352		} else
1353			*(u_int *)data = WSDISPLAYIO_MODE_EMUL;
1354		return 0;
1355
1356	case WSDISPLAYIO_SMODE:
1357#define d (*(int *)data)
1358		if (d != WSDISPLAYIO_MODE_EMUL &&
1359		    d != WSDISPLAYIO_MODE_MAPPED &&
1360		    d != WSDISPLAYIO_MODE_DUMBFB)
1361			return EINVAL;
1362
1363	    if (WSSCREEN_HAS_EMULATOR(scr)) {
1364		    scr->scr_flags &= ~SCR_GRAPHICS;
1365		    if (d == WSDISPLAYIO_MODE_MAPPED ||
1366			d == WSDISPLAYIO_MODE_DUMBFB)
1367			    scr->scr_flags |= SCR_GRAPHICS |
1368				    ((d == WSDISPLAYIO_MODE_DUMBFB) ? SCR_DUMBFB : 0);
1369	    } else if (d == WSDISPLAYIO_MODE_EMUL)
1370		    return EINVAL;
1371
1372	    (void)(*sc->sc_accessops->ioctl)(sc->sc_accesscookie,
1373	        scr->scr_dconf->emulcookie, cmd, data, flag, l);
1374
1375	    return 0;
1376#undef d
1377
1378#ifdef WSDISPLAY_SCROLLSUPPORT
1379#define	SETSCROLLLINES(dstp, srcp, dfltp)				\
1380    do {								\
1381	(dstp)->fastlines = ((srcp)->which &				\
1382			     WSDISPLAY_SCROLL_DOFASTLINES) ?		\
1383			     (srcp)->fastlines : (dfltp)->fastlines;	\
1384	(dstp)->slowlines = ((srcp)->which &				\
1385			     WSDISPLAY_SCROLL_DOSLOWLINES) ?		\
1386			     (srcp)->slowlines : (dfltp)->slowlines;	\
1387	(dstp)->which = WSDISPLAY_SCROLL_DOALL;				\
1388    } while (0)
1389
1390
1391	case WSDISPLAYIO_DSSCROLL:
1392		usdp = (struct wsdisplay_scroll_data *)data;
1393		ksdp = &sc->sc_scroll_values;
1394		SETSCROLLLINES(ksdp, usdp, ksdp);
1395		return 0;
1396
1397	case WSDISPLAYIO_DGSCROLL:
1398		usdp = (struct wsdisplay_scroll_data *)data;
1399		ksdp = &sc->sc_scroll_values;
1400		SETSCROLLLINES(usdp, ksdp, ksdp);
1401		return 0;
1402#else
1403	case WSDISPLAYIO_DSSCROLL:
1404	case WSDISPLAYIO_DGSCROLL:
1405		return ENODEV;
1406#endif
1407
1408	case WSDISPLAYIO_SFONT:
1409#define d ((struct wsdisplay_usefontdata *)data)
1410		if (!sc->sc_accessops->load_font)
1411			return EINVAL;
1412		if (d->name) {
1413			error = copyinstr(d->name, namebuf, sizeof(namebuf), 0);
1414			if (error)
1415				return error;
1416			fd.name = namebuf;
1417		} else
1418			fd.name = 0;
1419		fd.data = 0;
1420		error = (*sc->sc_accessops->load_font)(sc->sc_accesscookie,
1421					scr->scr_dconf->emulcookie, &fd);
1422		if (!error && WSSCREEN_HAS_EMULATOR(scr)) {
1423			(*scr->scr_dconf->wsemul->reset)
1424				(scr->scr_dconf->wsemulcookie, WSEMUL_SYNCFONT);
1425			if (scr->scr_dconf->wsemul->resize) {
1426				(*scr->scr_dconf->wsemul->resize)
1427					(scr->scr_dconf->wsemulcookie,
1428					 scr->scr_dconf->scrdata);
1429				/* update the tty's size */
1430				scr->scr_tty->t_winsize.ws_row =
1431				    scr->scr_dconf->scrdata->nrows;
1432				scr->scr_tty->t_winsize.ws_col =
1433				    scr->scr_dconf->scrdata->ncols;
1434				/* send SIGWINCH to the process group on our tty */
1435				kpreempt_disable();
1436				ttysig(scr->scr_tty, TTYSIG_PG1, SIGWINCH);
1437				kpreempt_enable();
1438			}
1439		}
1440		return error;
1441#undef d
1442
1443#ifdef WSDISPLAY_CUSTOM_OUTPUT
1444	case WSDISPLAYIO_GMSGATTRS:
1445#define d ((struct wsdisplay_msgattrs *)data)
1446		(*scr->scr_dconf->wsemul->getmsgattrs)
1447		    (scr->scr_dconf->wsemulcookie, d);
1448		return 0;
1449#undef d
1450
1451	case WSDISPLAYIO_SMSGATTRS: {
1452#define d ((struct wsdisplay_msgattrs *)data)
1453		int i;
1454		for (i = 0; i < WSDISPLAY_MAXSCREEN; i++)
1455			if (sc->sc_scr[i] != NULL)
1456				(*sc->sc_scr[i]->scr_dconf->wsemul->setmsgattrs)
1457				    (sc->sc_scr[i]->scr_dconf->wsemulcookie,
1458				     sc->sc_scr[i]->scr_dconf->scrdata,
1459				     d);
1460		}
1461		return 0;
1462#undef d
1463#else
1464	case WSDISPLAYIO_GMSGATTRS:
1465	case WSDISPLAYIO_SMSGATTRS:
1466		return ENODEV;
1467#endif
1468	case WSDISPLAYIO_SETVERSION:
1469		return wsevent_setversion(&sc->evar, *(int *)data);
1470	}
1471
1472	/* check ioctls for display */
1473	return (*sc->sc_accessops->ioctl)(sc->sc_accesscookie,
1474	    scr->scr_dconf->emulcookie, cmd, data, flag, l);
1475}
1476
1477int
1478wsdisplay_stat_ioctl(struct wsdisplay_softc *sc, u_long cmd, void *data,
1479	int flag, struct lwp *l)
1480{
1481	switch (cmd) {
1482	case WSDISPLAYIO_GETACTIVESCREEN:
1483		*(int*)data = wsdisplay_getactivescreen(sc);
1484		return 0;
1485	}
1486
1487	return EPASSTHROUGH;
1488}
1489
1490int
1491wsdisplay_cfg_ioctl(struct wsdisplay_softc *sc, u_long cmd, void *data,
1492	int flag, struct lwp *l)
1493{
1494	int error;
1495	char *type, typebuf[16], *emul, emulbuf[16];
1496	void *tbuf;
1497	u_int fontsz;
1498#if defined(COMPAT_14) && NWSKBD > 0
1499	struct wsmux_device wsmuxdata;
1500#endif
1501#if NWSKBD > 0
1502	struct wsevsrc *inp;
1503#endif
1504
1505	switch (cmd) {
1506	case WSDISPLAYIO_ADDSCREEN:
1507#define d ((struct wsdisplay_addscreendata *)data)
1508		if (d->screentype) {
1509			error = copyinstr(d->screentype, typebuf,
1510					  sizeof(typebuf), 0);
1511			if (error)
1512				return error;
1513			type = typebuf;
1514		} else
1515			type = 0;
1516		if (d->emul) {
1517			error = copyinstr(d->emul, emulbuf, sizeof(emulbuf),0);
1518			if (error)
1519				return error;
1520			emul = emulbuf;
1521		} else
1522			emul = 0;
1523
1524		if ((error = wsdisplay_addscreen(sc, d->idx, type, emul)) == 0)
1525			wsdisplay_addscreen_print(sc, d->idx, 0);
1526		return error;
1527#undef d
1528	case WSDISPLAYIO_DELSCREEN:
1529#define d ((struct wsdisplay_delscreendata *)data)
1530		return wsdisplay_delscreen(sc, d->idx, d->flags);
1531#undef d
1532	case WSDISPLAYIO_LDFONT:
1533#define d ((struct wsdisplay_font *)data)
1534		if (!sc->sc_accessops->load_font)
1535			return EINVAL;
1536		if (d->name) {
1537			error = copyinstr(d->name, typebuf, sizeof(typebuf), 0);
1538			if (error)
1539				return error;
1540			d->name = typebuf;
1541		} else
1542			d->name = "loaded"; /* ??? */
1543		fontsz = d->fontheight * d->stride * d->numchars;
1544		if (fontsz > WSDISPLAY_MAXFONTSZ)
1545			return EINVAL;
1546
1547		tbuf = malloc(fontsz, M_DEVBUF, M_WAITOK);
1548		error = copyin(d->data, tbuf, fontsz);
1549		if (error) {
1550			free(tbuf, M_DEVBUF);
1551			return error;
1552		}
1553		d->data = tbuf;
1554		error =
1555		  (*sc->sc_accessops->load_font)(sc->sc_accesscookie, 0, d);
1556		free(tbuf, M_DEVBUF);
1557#undef d
1558		return error;
1559
1560#if NWSKBD > 0
1561#ifdef COMPAT_14
1562	case _O_WSDISPLAYIO_SETKEYBOARD:
1563#define d ((struct wsdisplay_kbddata *)data)
1564		inp = sc->sc_input;
1565		if (inp == NULL)
1566			return ENXIO;
1567		switch (d->op) {
1568		case _O_WSDISPLAY_KBD_ADD:
1569			if (d->idx == -1) {
1570				d->idx = wskbd_pickfree();
1571				if (d->idx == -1)
1572					return ENXIO;
1573			}
1574			wsmuxdata.type = WSMUX_KBD;
1575			wsmuxdata.idx = d->idx;
1576			return wsevsrc_ioctl(inp, WSMUX_ADD_DEVICE,
1577			    &wsmuxdata, flag, l);
1578		case _O_WSDISPLAY_KBD_DEL:
1579			wsmuxdata.type = WSMUX_KBD;
1580			wsmuxdata.idx = d->idx;
1581			return wsevsrc_ioctl(inp, WSMUX_REMOVE_DEVICE,
1582			    &wsmuxdata, flag, l);
1583		default:
1584			return EINVAL;
1585		}
1586#undef d
1587#endif
1588
1589	case WSMUXIO_ADD_DEVICE:
1590#define d ((struct wsmux_device *)data)
1591		if (d->idx == -1 && d->type == WSMUX_KBD)
1592			d->idx = wskbd_pickfree();
1593#undef d
1594		/* FALLTHROUGH */
1595	case WSMUXIO_INJECTEVENT:
1596	case WSMUXIO_REMOVE_DEVICE:
1597	case WSMUXIO_LIST_DEVICES:
1598		inp = sc->sc_input;
1599		if (inp == NULL)
1600			return ENXIO;
1601		return wsevsrc_ioctl(inp, cmd, data, flag, l);
1602#endif /* NWSKBD > 0 */
1603
1604	}
1605	return EPASSTHROUGH;
1606}
1607
1608int
1609wsdisplay_stat_inject(device_t dv, u_int type, int value)
1610{
1611	struct wsdisplay_softc *sc = device_private(dv);
1612	struct wseventvar *evar;
1613	struct wscons_event event;
1614
1615	evar = &sc->evar;
1616
1617	if (evar == NULL)
1618		return 0;
1619
1620	if (evar->q == NULL)
1621		return 1;
1622
1623	event.type = type;
1624	event.value = value;
1625	if (wsevent_inject(evar, &event, 1) != 0) {
1626		log(LOG_WARNING, "wsdisplay: event queue overflow\n");
1627		return 1;
1628	}
1629
1630	return 0;
1631}
1632
1633paddr_t
1634wsdisplaymmap(dev_t dev, off_t offset, int prot)
1635{
1636	struct wsdisplay_softc *sc;
1637	struct wsscreen *scr;
1638
1639	sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev));
1640
1641	if (ISWSDISPLAYSTAT(dev))
1642		return -1;
1643
1644	if (ISWSDISPLAYCTL(dev))
1645		return -1;
1646
1647	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL)
1648		return -1;
1649
1650	if (!(scr->scr_flags & SCR_GRAPHICS))
1651		return -1;
1652
1653	/* pass mmap to display */
1654	return (*sc->sc_accessops->mmap)(sc->sc_accesscookie,
1655	    scr->scr_dconf->emulcookie, offset, prot);
1656}
1657
1658void
1659wsdisplaystart(struct tty *tp)
1660{
1661	struct wsdisplay_softc *sc;
1662	struct wsscreen *scr;
1663	int s, n;
1664	u_char *tbuf;
1665
1666	s = spltty();
1667	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
1668		splx(s);
1669		return;
1670	}
1671	sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(tp->t_dev));
1672	if ((scr = sc->sc_scr[WSDISPLAYSCREEN(tp->t_dev)]) == NULL) {
1673		splx(s);
1674		return;
1675	}
1676
1677	if (scr->scr_hold_screen) {
1678		tp->t_state |= TS_TIMEOUT;
1679		splx(s);
1680		return;
1681	}
1682	tp->t_state |= TS_BUSY;
1683	splx(s);
1684
1685#ifdef DIAGNOSTIC
1686	scr->scr_in_ttyoutput = 1;
1687#endif
1688
1689	/*
1690	 * Drain output from ring buffer.
1691	 * The output will normally be in one contiguous chunk, but when the
1692	 * ring wraps, it will be in two pieces.. one at the end of the ring,
1693	 * the other at the start.  For performance, rather than loop here,
1694	 * we output one chunk, see if there's another one, and if so, output
1695	 * it too.
1696	 */
1697
1698	n = ndqb(&tp->t_outq, 0);
1699	tbuf = tp->t_outq.c_cf;
1700
1701	if (!(scr->scr_flags & SCR_GRAPHICS)) {
1702		KASSERT(WSSCREEN_HAS_EMULATOR(scr));
1703		(*scr->scr_dconf->wsemul->output)(scr->scr_dconf->wsemulcookie,
1704						  tbuf, n, 0);
1705#ifdef WSDISPLAY_MULTICONS
1706		if (wsdisplay_multicons_enable &&
1707		    scr->scr_dconf == &wsdisplay_console_conf &&
1708		    wsdisplay_ocn && wsdisplay_ocn->cn_putc) {
1709			for (int i = 0; i < n; i++)
1710				wsdisplay_ocn->cn_putc(
1711				    wsdisplay_ocn->cn_dev, tbuf[i]);
1712		}
1713#endif
1714	}
1715	ndflush(&tp->t_outq, n);
1716
1717	if ((n = ndqb(&tp->t_outq, 0)) > 0) {
1718		tbuf = tp->t_outq.c_cf;
1719
1720		if (!(scr->scr_flags & SCR_GRAPHICS)) {
1721			KASSERT(WSSCREEN_HAS_EMULATOR(scr));
1722			(*scr->scr_dconf->wsemul->output)
1723			    (scr->scr_dconf->wsemulcookie, tbuf, n, 0);
1724
1725#ifdef WSDISPLAY_MULTICONS
1726			if (wsdisplay_multicons_enable &&
1727			    scr->scr_dconf == &wsdisplay_console_conf &&
1728			    wsdisplay_ocn && wsdisplay_ocn->cn_putc) {
1729				for (int i = 0; i < n; i++)
1730					wsdisplay_ocn->cn_putc(
1731					    wsdisplay_ocn->cn_dev, tbuf[i]);
1732			}
1733#endif
1734		}
1735		ndflush(&tp->t_outq, n);
1736	}
1737
1738#ifdef DIAGNOSTIC
1739	scr->scr_in_ttyoutput = 0;
1740#endif
1741
1742	s = spltty();
1743	tp->t_state &= ~TS_BUSY;
1744	/* Come back if there's more to do */
1745	if (ttypull(tp)) {
1746		tp->t_state |= TS_TIMEOUT;
1747		callout_schedule(&tp->t_rstrt_ch, (hz > 128) ? (hz / 128) : 1);
1748	}
1749	splx(s);
1750}
1751
1752void
1753wsdisplaystop(struct tty *tp, int flag)
1754{
1755	int s;
1756
1757	s = spltty();
1758	if (ISSET(tp->t_state, TS_BUSY))
1759		if (!ISSET(tp->t_state, TS_TTSTOP))
1760			SET(tp->t_state, TS_FLUSH);
1761	splx(s);
1762}
1763
1764/* Set line parameters. */
1765int
1766wsdisplayparam(struct tty *tp, struct termios *t)
1767{
1768
1769	tp->t_ispeed = t->c_ispeed;
1770	tp->t_ospeed = t->c_ospeed;
1771	tp->t_cflag = t->c_cflag;
1772	return 0;
1773}
1774
1775/*
1776 * Callbacks for the emulation code.
1777 */
1778void
1779wsdisplay_emulbell(void *v)
1780{
1781	struct wsscreen *scr = v;
1782
1783	if (scr == NULL)		/* console, before real attach */
1784		return;
1785
1786	if (scr->scr_flags & SCR_GRAPHICS) /* can this happen? */
1787		return;
1788
1789	(void) wsdisplay_internal_ioctl(scr->sc, scr, WSKBDIO_BELL, NULL,
1790					FWRITE, NULL);
1791}
1792
1793void
1794wsdisplay_emulinput(void *v, const u_char *data, u_int count)
1795{
1796	struct wsscreen *scr = v;
1797	struct tty *tp;
1798	int (*ifcn)(int, struct tty *);
1799
1800	if (v == NULL)			/* console, before real attach */
1801		return;
1802
1803	if (scr->scr_flags & SCR_GRAPHICS) /* XXX can't happen */
1804		return;
1805	if (!WSSCREEN_HAS_TTY(scr))
1806		return;
1807
1808	tp = scr->scr_tty;
1809
1810	/*
1811	 * XXX bad hack to work around locking problems in tty.c:
1812	 * ttyinput() will try to lock again, causing deadlock.
1813	 * We assume that wsdisplay_emulinput() can only be called
1814	 * from within wsdisplaystart(), and thus the tty lock
1815	 * is already held. Use an entry point which doesn't lock.
1816	 */
1817	KASSERT(scr->scr_in_ttyoutput);
1818	ifcn = tp->t_linesw->l_rint;
1819	if (ifcn == ttyinput)
1820		ifcn = ttyinput_wlock;
1821
1822	while (count-- > 0)
1823		(*ifcn)(*data++, tp);
1824}
1825
1826/*
1827 * Calls from the keyboard interface.
1828 */
1829void
1830wsdisplay_kbdinput(device_t dv, keysym_t ks)
1831{
1832	struct wsdisplay_softc *sc = device_private(dv);
1833	struct wsscreen *scr;
1834	const char *dp;
1835	int count;
1836	struct tty *tp;
1837
1838	KASSERT(sc != NULL);
1839
1840	scr = sc->sc_focus;
1841
1842	if (!scr || !WSSCREEN_HAS_TTY(scr))
1843		return;
1844
1845	tp = scr->scr_tty;
1846
1847	if (KS_GROUP(ks) == KS_GROUP_Plain && KS_VALUE(ks) <= 0x7f)
1848		(*tp->t_linesw->l_rint)(KS_VALUE(ks), tp);
1849	else if (WSSCREEN_HAS_EMULATOR(scr)) {
1850		count = (*scr->scr_dconf->wsemul->translate)
1851		    (scr->scr_dconf->wsemulcookie, ks, &dp);
1852		while (count-- > 0)
1853			(*tp->t_linesw->l_rint)((unsigned char)(*dp++), tp);
1854	}
1855}
1856
1857#if defined(WSDISPLAY_COMPAT_RAWKBD)
1858int
1859wsdisplay_update_rawkbd(struct wsdisplay_softc *sc, struct wsscreen *scr)
1860{
1861#if NWSKBD > 0
1862	int s, raw, data, error;
1863	struct wsevsrc *inp;
1864
1865	s = spltty();
1866
1867	raw = (scr ? scr->scr_rawkbd : 0);
1868
1869	if (scr != sc->sc_focus ||
1870	    sc->sc_rawkbd == raw) {
1871		splx(s);
1872		return 0;
1873	}
1874
1875	data = raw ? WSKBD_RAW : WSKBD_TRANSLATED;
1876	inp = sc->sc_input;
1877	if (inp == NULL) {
1878		splx(s);
1879		return ENXIO;
1880	}
1881	error = wsevsrc_display_ioctl(inp, WSKBDIO_SETMODE, &data, 0, 0);
1882	if (!error)
1883		sc->sc_rawkbd = raw;
1884	splx(s);
1885	return error;
1886#else
1887	return 0;
1888#endif
1889}
1890#endif
1891
1892static void
1893wsdisplay_switch3_cb(void *arg, int error, int waitok)
1894{
1895	device_t dv = arg;
1896
1897	wsdisplay_switch3(dv, error, waitok);
1898}
1899
1900static int
1901wsdisplay_switch3(device_t dv, int error, int waitok)
1902{
1903	struct wsdisplay_softc *sc = device_private(dv);
1904	int no;
1905	struct wsscreen *scr;
1906
1907	if (!(sc->sc_flags & SC_SWITCHPENDING)) {
1908		aprint_error_dev(dv, "wsdisplay_switch3: not switching\n");
1909		return EINVAL;
1910	}
1911
1912	no = sc->sc_screenwanted;
1913	if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
1914		panic("wsdisplay_switch3: invalid screen %d", no);
1915	scr = sc->sc_scr[no];
1916	if (!scr) {
1917		aprint_error_dev(dv,
1918		    "wsdisplay_switch3: screen %d disappeared\n", no);
1919		error = ENXIO;
1920	}
1921
1922	if (error) {
1923		/* try to recover, avoid recursion */
1924
1925		if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) {
1926			aprint_error_dev(dv, "wsdisplay_switch3: giving up\n");
1927			sc->sc_focus = 0;
1928#ifdef WSDISPLAY_COMPAT_RAWKBD
1929			wsdisplay_update_rawkbd(sc, 0);
1930#endif
1931			sc->sc_flags &= ~SC_SWITCHPENDING;
1932			return error;
1933		}
1934
1935		sc->sc_screenwanted = sc->sc_oldscreen;
1936		sc->sc_oldscreen = WSDISPLAY_NULLSCREEN;
1937		return wsdisplay_switch1(dv, 0, waitok);
1938	}
1939
1940	if (scr->scr_syncops && !error)
1941		sc->sc_flags |= SC_XATTACHED;
1942
1943	sc->sc_flags &= ~SC_SWITCHPENDING;
1944
1945	if (!error && (scr->scr_flags & SCR_WAITACTIVE))
1946		wakeup(scr);
1947	return error;
1948}
1949
1950static void
1951wsdisplay_switch2_cb(void *arg, int error, int waitok)
1952{
1953	device_t dv = arg;
1954
1955	wsdisplay_switch2(dv, error, waitok);
1956}
1957
1958static int
1959wsdisplay_switch2(device_t dv, int error, int waitok)
1960{
1961	struct wsdisplay_softc *sc = device_private(dv);
1962	int no;
1963	struct wsscreen *scr;
1964
1965	if (!(sc->sc_flags & SC_SWITCHPENDING)) {
1966		aprint_error_dev(dv, "wsdisplay_switch2: not switching\n");
1967		return EINVAL;
1968	}
1969
1970	no = sc->sc_screenwanted;
1971	if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
1972		panic("wsdisplay_switch2: invalid screen %d", no);
1973	scr = sc->sc_scr[no];
1974	if (!scr) {
1975		aprint_error_dev(dv,
1976		    "wsdisplay_switch2: screen %d disappeared\n", no);
1977		error = ENXIO;
1978	}
1979
1980	if (error) {
1981		/* try to recover, avoid recursion */
1982
1983		if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) {
1984			aprint_error_dev(dv, "wsdisplay_switch2: giving up\n");
1985			sc->sc_focus = 0;
1986			sc->sc_flags &= ~SC_SWITCHPENDING;
1987			return error;
1988		}
1989
1990		sc->sc_screenwanted = sc->sc_oldscreen;
1991		sc->sc_oldscreen = WSDISPLAY_NULLSCREEN;
1992		return wsdisplay_switch1(dv, 0, waitok);
1993	}
1994
1995	sc->sc_focusidx = no;
1996	sc->sc_focus = scr;
1997
1998#ifdef WSDISPLAY_COMPAT_RAWKBD
1999	(void) wsdisplay_update_rawkbd(sc, scr);
2000#endif
2001	/* keyboard map??? */
2002
2003	if (scr->scr_syncops &&
2004	    !(sc->sc_isconsole && wsdisplay_cons_pollmode)) {
2005		error = (*scr->scr_syncops->attach)(scr->scr_synccookie, waitok,
2006						    wsdisplay_switch3_cb, dv);
2007		if (error == EAGAIN) {
2008			/* switch will be done asynchronously */
2009			return 0;
2010		}
2011	}
2012
2013	return wsdisplay_switch3(dv, error, waitok);
2014}
2015
2016static void
2017wsdisplay_switch1_cb(void *arg, int error, int waitok)
2018{
2019	device_t dv = arg;
2020
2021	wsdisplay_switch1(dv, error, waitok);
2022}
2023
2024static int
2025wsdisplay_switch1(device_t dv, int error, int waitok)
2026{
2027	struct wsdisplay_softc *sc = device_private(dv);
2028	int no;
2029	struct wsscreen *scr;
2030
2031	if (!(sc->sc_flags & SC_SWITCHPENDING)) {
2032		aprint_error_dev(dv, "wsdisplay_switch1: not switching\n");
2033		return EINVAL;
2034	}
2035
2036	no = sc->sc_screenwanted;
2037	if (no == WSDISPLAY_NULLSCREEN) {
2038		sc->sc_flags &= ~SC_SWITCHPENDING;
2039		if (!error) {
2040			sc->sc_flags &= ~SC_XATTACHED;
2041			sc->sc_focus = 0;
2042		}
2043		wakeup(sc);
2044		return error;
2045	}
2046	if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
2047		panic("wsdisplay_switch1: invalid screen %d", no);
2048	scr = sc->sc_scr[no];
2049	if (!scr) {
2050		aprint_error_dev(dv,
2051		    "wsdisplay_switch1: screen %d disappeared\n", no);
2052		error = ENXIO;
2053	}
2054
2055	if (error) {
2056		sc->sc_flags &= ~SC_SWITCHPENDING;
2057		return error;
2058	}
2059
2060	sc->sc_flags &= ~SC_XATTACHED;
2061
2062	error = (*sc->sc_accessops->show_screen)(sc->sc_accesscookie,
2063						 scr->scr_dconf->emulcookie,
2064						 waitok,
2065	  sc->sc_isconsole && wsdisplay_cons_pollmode ? 0 : wsdisplay_switch2_cb, dv);
2066	if (error == EAGAIN) {
2067		/* switch will be done asynchronously */
2068		return 0;
2069	}
2070
2071	return wsdisplay_switch2(dv, error, waitok);
2072}
2073
2074int
2075wsdisplay_switch(device_t dv, int no, int waitok)
2076{
2077	struct wsdisplay_softc *sc = device_private(dv);
2078	int s, res = 0;
2079	struct wsscreen *scr;
2080
2081	if (no != WSDISPLAY_NULLSCREEN) {
2082		if ((no < 0 || no >= WSDISPLAY_MAXSCREEN))
2083			return EINVAL;
2084		if (sc->sc_scr[no] == NULL)
2085			return ENXIO;
2086	}
2087
2088	wsdisplay_stat_inject(dv, WSCONS_EVENT_SCREEN_SWITCH, no);
2089
2090	s = spltty();
2091
2092	if ((sc->sc_focus && no == sc->sc_focusidx) ||
2093	    (sc->sc_focus == NULL && no == WSDISPLAY_NULLSCREEN)) {
2094		splx(s);
2095		return 0;
2096	}
2097
2098	if (sc->sc_flags & SC_SWITCHPENDING) {
2099		splx(s);
2100		return EBUSY;
2101	}
2102
2103	sc->sc_flags |= SC_SWITCHPENDING;
2104	sc->sc_screenwanted = no;
2105
2106	splx(s);
2107
2108	scr = sc->sc_focus;
2109	if (!scr) {
2110		sc->sc_oldscreen = WSDISPLAY_NULLSCREEN;
2111		return wsdisplay_switch1(dv, 0, waitok);
2112	} else
2113		sc->sc_oldscreen = sc->sc_focusidx;
2114
2115	if (scr->scr_syncops) {
2116		if (!(sc->sc_flags & SC_XATTACHED) ||
2117		    (sc->sc_isconsole && wsdisplay_cons_pollmode)) {
2118			/* nothing to do here */
2119			return wsdisplay_switch1(dv, 0, waitok);
2120		}
2121		res = (*scr->scr_syncops->detach)(scr->scr_synccookie, waitok,
2122						  wsdisplay_switch1_cb, dv);
2123		if (res == EAGAIN) {
2124			/* switch will be done asynchronously */
2125			return 0;
2126		}
2127	} else if (scr->scr_flags & SCR_GRAPHICS) {
2128		/* no way to save state */
2129		res = EBUSY;
2130	}
2131
2132	return wsdisplay_switch1(dv, res, waitok);
2133}
2134
2135void
2136wsdisplay_reset(device_t dv, enum wsdisplay_resetops op)
2137{
2138	struct wsdisplay_softc *sc = device_private(dv);
2139	struct wsscreen *scr;
2140
2141	KASSERT(sc != NULL);
2142	scr = sc->sc_focus;
2143
2144	if (!scr)
2145		return;
2146
2147	switch (op) {
2148	case WSDISPLAY_RESETEMUL:
2149		if (!WSSCREEN_HAS_EMULATOR(scr))
2150			break;
2151		(*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie,
2152						 WSEMUL_RESET);
2153		break;
2154	case WSDISPLAY_RESETCLOSE:
2155		wsdisplay_closescreen(sc, scr);
2156		break;
2157	}
2158}
2159
2160
2161bool
2162wsdisplay_isconsole(struct wsdisplay_softc *sc)
2163{
2164	return sc->sc_isconsole;
2165}
2166
2167/*
2168 * Interface for (external) VT switch / process synchronization code
2169 */
2170int
2171wsscreen_attach_sync(struct wsscreen *scr, const struct wscons_syncops *ops,
2172	void *cookie)
2173{
2174	if (scr->scr_syncops) {
2175		/*
2176		 * The screen is already claimed.
2177		 * Check if the owner is still alive.
2178		 */
2179		if ((*scr->scr_syncops->check)(scr->scr_synccookie))
2180			return EBUSY;
2181	}
2182	scr->scr_syncops = ops;
2183	scr->scr_synccookie = cookie;
2184	if (scr == scr->sc->sc_focus)
2185		scr->sc->sc_flags |= SC_XATTACHED;
2186	return 0;
2187}
2188
2189int
2190wsscreen_detach_sync(struct wsscreen *scr)
2191{
2192	if (!scr->scr_syncops)
2193		return EINVAL;
2194	scr->scr_syncops = 0;
2195	if (scr == scr->sc->sc_focus)
2196		scr->sc->sc_flags &= ~SC_XATTACHED;
2197	return 0;
2198}
2199
2200int
2201wsscreen_lookup_sync(struct wsscreen *scr,
2202	const struct wscons_syncops *ops, /* used as ID */
2203	void **cookiep)
2204{
2205	if (!scr->scr_syncops || ops != scr->scr_syncops)
2206		return EINVAL;
2207	*cookiep = scr->scr_synccookie;
2208	return 0;
2209}
2210
2211/*
2212 * Interface to virtual screen stuff
2213 */
2214int
2215wsdisplay_maxscreenidx(struct wsdisplay_softc *sc)
2216{
2217	return (WSDISPLAY_MAXSCREEN - 1);
2218}
2219
2220int
2221wsdisplay_screenstate(struct wsdisplay_softc *sc, int idx)
2222{
2223	if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN)
2224		return EINVAL;
2225	if (!sc->sc_scr[idx])
2226		return ENXIO;
2227	return ((sc->sc_scr[idx]->scr_flags & SCR_OPEN) ? EBUSY : 0);
2228}
2229
2230int
2231wsdisplay_getactivescreen(struct wsdisplay_softc *sc)
2232{
2233	return (sc->sc_focus ? sc->sc_focusidx : WSDISPLAY_NULLSCREEN);
2234}
2235
2236int
2237wsscreen_switchwait(struct wsdisplay_softc *sc, int no)
2238{
2239	struct wsscreen *scr;
2240	int s, res = 0;
2241
2242	if (no == WSDISPLAY_NULLSCREEN) {
2243		s = spltty();
2244		while (sc->sc_focus && res == 0) {
2245			res = tsleep(sc, PCATCH, "wswait", 0);
2246		}
2247		splx(s);
2248		return res;
2249	}
2250
2251	if (no < 0 || no >= WSDISPLAY_MAXSCREEN)
2252		return ENXIO;
2253	scr = sc->sc_scr[no];
2254	if (!scr)
2255		return ENXIO;
2256
2257	s = spltty();
2258	if (scr != sc->sc_focus) {
2259		scr->scr_flags |= SCR_WAITACTIVE;
2260		res = tsleep(scr, PCATCH, "wswait", 0);
2261		if (scr != sc->sc_scr[no])
2262			res = ENXIO; /* disappeared in the meantime */
2263		else
2264			scr->scr_flags &= ~SCR_WAITACTIVE;
2265	}
2266	splx(s);
2267	return res;
2268}
2269
2270void
2271wsdisplay_kbdholdscreen(device_t dv, int hold)
2272{
2273	struct wsdisplay_softc *sc = device_private(dv);
2274	struct wsscreen *scr;
2275
2276	scr = sc->sc_focus;
2277
2278	if (!scr)
2279		return;
2280
2281	if (hold)
2282		scr->scr_hold_screen = 1;
2283	else {
2284		scr->scr_hold_screen = 0;
2285		callout_schedule(&scr->scr_tty->t_rstrt_ch, 0);
2286	}
2287}
2288
2289#if NWSKBD > 0
2290void
2291wsdisplay_set_console_kbd(struct wsevsrc *src)
2292{
2293	if (wsdisplay_console_device == NULL) {
2294		src->me_dispdv = NULL;
2295		return;
2296	}
2297#if NWSMUX > 0
2298	if (wsmux_attach_sc((struct wsmux_softc *)
2299			    wsdisplay_console_device->sc_input, src)) {
2300		src->me_dispdv = NULL;
2301		return;
2302	}
2303#else
2304	wsdisplay_console_device->sc_input = src;
2305#endif
2306	src->me_dispdv = wsdisplay_console_device->sc_dev;
2307}
2308#endif /* NWSKBD > 0 */
2309
2310/*
2311 * Console interface.
2312 */
2313void
2314wsdisplay_cnputc(dev_t dev, int i)
2315{
2316	struct wsscreen_internal *dc;
2317	u_char c = i;
2318
2319	if (!wsdisplay_console_initted)
2320		return;
2321
2322	if ((wsdisplay_console_device != NULL) &&
2323	    (wsdisplay_console_device->sc_scr[0] != NULL) &&
2324	    (wsdisplay_console_device->sc_scr[0]->scr_flags & SCR_GRAPHICS))
2325		return;
2326
2327	dc = &wsdisplay_console_conf;
2328	(*dc->wsemul->output)(dc->wsemulcookie, &c, 1, 1);
2329
2330#ifdef WSDISPLAY_MULTICONS
2331	if (!wsdisplay_multicons_suspended &&
2332	    wsdisplay_multicons_enable && wsdisplay_ocn && wsdisplay_ocn->cn_putc)
2333		wsdisplay_ocn->cn_putc(wsdisplay_ocn->cn_dev, i);
2334#endif
2335}
2336
2337static int
2338wsdisplay_getc(dev_t dev)
2339{
2340	int c;
2341
2342	if (wsdisplay_cons_kbd_getc) {
2343		c = wsdisplay_cons_kbd_getc(wsdisplay_cons.cn_dev);
2344		if (c >= 0)
2345			return c;
2346	}
2347
2348#ifdef WSDISPLAY_MULTICONS
2349	if (!wsdisplay_multicons_suspended &&
2350	    wsdisplay_multicons_enable && wsdisplay_ocn && wsdisplay_ocn->cn_getc) {
2351		c = wsdisplay_ocn->cn_getc(wsdisplay_ocn->cn_dev);
2352		if (c >= 0)
2353			return c;
2354	}
2355#endif
2356	return -1;
2357}
2358
2359static void
2360wsdisplay_pollc(dev_t dev, int on)
2361{
2362
2363	wsdisplay_cons_pollmode = on;
2364
2365	/* notify to fb drivers */
2366	if (wsdisplay_console_device != NULL &&
2367	    wsdisplay_console_device->sc_accessops->pollc != NULL)
2368		(*wsdisplay_console_device->sc_accessops->pollc)
2369			(wsdisplay_console_device->sc_accesscookie, on);
2370
2371	/* notify to kbd drivers */
2372	if (wsdisplay_cons_kbd_pollc)
2373		(*wsdisplay_cons_kbd_pollc)(NODEV, on);
2374
2375#ifdef WSDISPLAY_MULTICONS
2376	/* notify to old console driver */
2377	if (!wsdisplay_multicons_suspended &&
2378	    wsdisplay_multicons_enable && wsdisplay_ocn && wsdisplay_ocn->cn_pollc)
2379		wsdisplay_ocn->cn_pollc(wsdisplay_ocn->cn_dev, on);
2380#endif
2381}
2382
2383void
2384wsdisplay_set_cons_kbd(int (*get)(dev_t), void (*poll)(dev_t, int),
2385	void (*bell)(dev_t, u_int, u_int, u_int))
2386{
2387	wsdisplay_cons.cn_bell = bell;
2388	wsdisplay_cons_kbd_getc = get;
2389	wsdisplay_cons_kbd_pollc = poll;
2390}
2391
2392void
2393wsdisplay_unset_cons_kbd(void)
2394{
2395	wsdisplay_cons.cn_bell = NULL;
2396	wsdisplay_cons_kbd_getc = NULL;
2397	wsdisplay_cons_kbd_pollc = NULL;
2398}
2399
2400#ifdef WSDISPLAY_MULTICONS
2401void
2402wsdisplay_multicons_suspend(bool suspend)
2403{
2404	wsdisplay_multicons_suspended = suspend;
2405}
2406#endif
2407
2408#ifdef WSDISPLAY_MULTICONS
2409SYSCTL_SETUP(sysctl_hw_wsdisplay_setup, "sysctl hw.wsdisplay subtree setup")
2410{
2411	const struct sysctlnode *wsdisplay_node;
2412
2413	if (sysctl_createv(clog, 0, NULL, &wsdisplay_node,
2414	    CTLFLAG_PERMANENT,
2415	    CTLTYPE_NODE, "wsdisplay", NULL,
2416	    NULL, 0, NULL, 0,
2417	    CTL_HW, CTL_CREATE, CTL_EOL) != 0)
2418		return;
2419
2420	sysctl_createv(clog, 0, NULL, NULL,
2421	    CTLFLAG_READWRITE,
2422	    CTLTYPE_BOOL, "multicons",
2423	    SYSCTL_DESCR("Enable wsdisplay multicons"),
2424	    NULL, 0, &wsdisplay_multicons_enable, 0,
2425	    CTL_HW, wsdisplay_node->sysctl_num, CTL_CREATE, CTL_EOL);
2426}
2427#endif
2428