vt_core.c revision 256145
1219888Sed/*-
2219888Sed * Copyright (c) 2009 The FreeBSD Foundation
3219888Sed * All rights reserved.
4219888Sed *
5219888Sed * This software was developed by Ed Schouten under sponsorship from the
6219888Sed * FreeBSD Foundation.
7219888Sed *
8219888Sed * Redistribution and use in source and binary forms, with or without
9219888Sed * modification, are permitted provided that the following conditions
10219888Sed * are met:
11219888Sed * 1. Redistributions of source code must retain the above copyright
12219888Sed *    notice, this list of conditions and the following disclaimer.
13219888Sed * 2. Redistributions in binary form must reproduce the above copyright
14219888Sed *    notice, this list of conditions and the following disclaimer in the
15219888Sed *    documentation and/or other materials provided with the distribution.
16219888Sed *
17219888Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18219888Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19219888Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20219888Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21219888Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22219888Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23219888Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24219888Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25219888Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26219888Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27219888Sed * SUCH DAMAGE.
28219888Sed */
29219888Sed
30219888Sed#include <sys/cdefs.h>
31219888Sed__FBSDID("$FreeBSD: user/ed/newcons/sys/dev/vt/vt_core.c 256145 2013-10-08 12:40:04Z ray $");
32219888Sed
33219888Sed#include <sys/param.h>
34219888Sed#include <sys/consio.h>
35219888Sed#include <sys/eventhandler.h>
36219888Sed#include <sys/fbio.h>
37219888Sed#include <sys/kbio.h>
38219888Sed#include <sys/kdb.h>
39219888Sed#include <sys/kernel.h>
40219888Sed#include <sys/lock.h>
41219888Sed#include <sys/malloc.h>
42219888Sed#include <sys/mutex.h>
43256145Sray#include <sys/proc.h>
44219888Sed#include <sys/reboot.h>
45219888Sed#include <sys/systm.h>
46219888Sed#include <sys/terminal.h>
47219888Sed
48219888Sed#include <dev/kbd/kbdreg.h>
49219888Sed#include <dev/vt/vt.h>
50219888Sed
51219888Sedstatic tc_bell_t	vtterm_bell;
52219888Sedstatic tc_cursor_t	vtterm_cursor;
53219888Sedstatic tc_putchar_t	vtterm_putchar;
54219888Sedstatic tc_fill_t	vtterm_fill;
55219888Sedstatic tc_copy_t	vtterm_copy;
56219888Sedstatic tc_param_t	vtterm_param;
57219888Sedstatic tc_done_t	vtterm_done;
58219888Sed
59219888Sedstatic tc_cnprobe_t	vtterm_cnprobe;
60219888Sedstatic tc_cngetc_t	vtterm_cngetc;
61219888Sed
62219888Sedstatic tc_opened_t	vtterm_opened;
63219888Sedstatic tc_ioctl_t	vtterm_ioctl;
64219888Sed
65219888Sedconst struct terminal_class vt_termclass = {
66219888Sed	.tc_bell	= vtterm_bell,
67219888Sed	.tc_cursor	= vtterm_cursor,
68219888Sed	.tc_putchar	= vtterm_putchar,
69219888Sed	.tc_fill	= vtterm_fill,
70219888Sed	.tc_copy	= vtterm_copy,
71219888Sed	.tc_param	= vtterm_param,
72219888Sed	.tc_done	= vtterm_done,
73219888Sed
74219888Sed	.tc_cnprobe	= vtterm_cnprobe,
75219888Sed	.tc_cngetc	= vtterm_cngetc,
76219888Sed
77219888Sed	.tc_opened	= vtterm_opened,
78219888Sed	.tc_ioctl	= vtterm_ioctl,
79219888Sed};
80219888Sed
81219888Sed/*
82219888Sed * Use a constant timer of 25 Hz to redraw the screen.
83219888Sed *
84219888Sed * XXX: In theory we should only fire up the timer when there is really
85219888Sed * activity. Unfortunately we cannot always start timers. We really
86219888Sed * don't want to process kernel messages synchronously, because it
87219888Sed * really slows down the system.
88219888Sed */
89219888Sed#define	VT_TIMERFREQ	25
90219888Sed
91219888Sed/* Bell pitch/duration. */
92219888Sed#define VT_BELLDURATION	((5 * hz + 99) / 100)
93219888Sed#define VT_BELLPITCH	800
94219888Sed
95219888Sed#define	VT_LOCK(vd)	mtx_lock(&(vd)->vd_lock)
96219888Sed#define	VT_UNLOCK(vd)	mtx_unlock(&(vd)->vd_lock)
97219888Sed
98219888Sed#define	VT_UNIT(vw)	((vw)->vw_device->vd_unit * VT_MAXWINDOWS + \
99219888Sed			(vw)->vw_number)
100219888Sed
101256145Sray/* XXX while syscons is here. */
102256145Srayint sc_txtmouse_no_retrace_wait;
103256145Sray
104256145Sraystatic SYSCTL_NODE(_kern, OID_AUTO, vt, CTLFLAG_RD, 0, "Newcons parameters");
105256145SrayVT_SYSCTL_INT(debug, 0, "Newcons debug level");
106256145SrayVT_SYSCTL_INT(deadtimer, 15, "Time to wait busy process in VT_PROCESS mode");
107256145Sray
108219888Sedstatic unsigned int vt_unit = 0;
109219888Sedstatic MALLOC_DEFINE(M_VT, "vt", "vt device");
110256145Sraystruct vt_device *main_vd = NULL;
111219888Sed
112219888Sed/* Boot logo. */
113219888Sedextern unsigned int vt_logo_width;
114219888Sedextern unsigned int vt_logo_height;
115219888Sedextern unsigned int vt_logo_depth;
116219888Sedextern unsigned char vt_logo_image[];
117219888Sed
118219888Sed/* Font. */
119219888Sedextern struct vt_font vt_font_default;
120219888Sed
121256145Sraystatic int signal_vt_rel(struct vt_window *);
122256145Sraystatic int signal_vt_acq(struct vt_window *);
123256145Sraystatic int finish_vt_rel(struct vt_window *, int, int *);
124256145Sraystatic int finish_vt_acq(struct vt_window *);
125256145Sraystatic int vt_window_switch(struct vt_window *);
126256145Sraystatic int vt_late_window_switch(struct vt_window *);
127256145Sraystatic int vt_proc_alive(struct vt_window *);
128256145Sraystatic void vt_resize(struct vt_device *);
129256145Sray
130219888Sedstatic void
131256145Srayvt_switch_timer(void *arg)
132256145Sray{
133256145Sray
134256145Sray	vt_late_window_switch((struct vt_window *)arg);
135256145Sray}
136256145Sray
137256145Sraystatic int
138256145Srayvt_window_preswitch(struct vt_window *vw, struct vt_window *curvw)
139256145Sray{
140256145Sray
141256145Sray	DPRINTF(40, "%s\n", __func__);
142256145Sray	curvw->vw_switch_to = vw;
143256145Sray	/* Set timer to allow switch in case when process hang. */
144256145Sray	callout_reset(&vw->vw_proc_dead_timer, hz * vt_deadtimer,
145256145Sray	    vt_switch_timer, (void *)vw);
146256145Sray	/* Notify process about vt switch attempt. */
147256145Sray	DPRINTF(30, "%s: Notify process.\n", __func__);
148256145Sray	signal_vt_rel(curvw);
149256145Sray
150256145Sray	return (0);
151256145Sray}
152256145Sray
153256145Sraystatic int
154256145Srayvt_window_postswitch(struct vt_window *vw)
155256145Sray{
156256145Sray
157256145Sray	signal_vt_acq(vw);
158256145Sray	return (0);
159256145Sray}
160256145Sray
161256145Sray/* vt_late_window_switch will done VT switching for regular case. */
162256145Sraystatic int
163256145Srayvt_late_window_switch(struct vt_window *vw)
164256145Sray{
165256145Sray	int ret;
166256145Sray
167256145Sray	callout_stop(&vw->vw_proc_dead_timer);
168256145Sray
169256145Sray	ret = vt_window_switch(vw);
170256145Sray	if (ret)
171256145Sray		return (ret);
172256145Sray
173256145Sray	/* Notify owner process about terminal availability. */
174256145Sray	if (vw->vw_smode.mode == VT_PROCESS) {
175256145Sray		ret = vt_window_postswitch(vw);
176256145Sray	}
177256145Sray	return (ret);
178256145Sray}
179256145Sray
180256145Sray/* Switch window. */
181256145Sraystatic int
182256145Srayvt_proc_window_switch(struct vt_window *vw)
183256145Sray{
184256145Sray	struct vt_window *curvw;
185256145Sray	struct vt_device *vd;
186256145Sray	int ret;
187256145Sray
188256145Sray	if (vw->vw_flags & VWF_VTYLOCK)
189256145Sray		return (EBUSY);
190256145Sray
191256145Sray	vd = vw->vw_device;
192256145Sray	curvw = vd->vd_curwindow;
193256145Sray
194256145Sray	/* Ask current process permitions to switch away. */
195256145Sray	if (curvw->vw_smode.mode == VT_PROCESS) {
196256145Sray		DPRINTF(30, "%s: VT_PROCESS ", __func__);
197256145Sray		if (vt_proc_alive(curvw) == FALSE) {
198256145Sray			DPRINTF(30, "Dead. Cleaning.");
199256145Sray			/* Dead */
200256145Sray		} else {
201256145Sray			DPRINTF(30, "%s: Signaling process.\n", __func__);
202256145Sray			/* Alive, try to ask him. */
203256145Sray			ret = vt_window_preswitch(vw, curvw);
204256145Sray			/* Wait for process answer or timeout. */
205256145Sray			return (ret);
206256145Sray		}
207256145Sray		DPRINTF(30, "\n");
208256145Sray	}
209256145Sray
210256145Sray	ret = vt_late_window_switch(vw);
211256145Sray	return (ret);
212256145Sray}
213256145Sray
214256145Sray/* Switch window ignoring process locking. */
215256145Sraystatic int
216219888Sedvt_window_switch(struct vt_window *vw)
217219888Sed{
218219888Sed	struct vt_device *vd = vw->vw_device;
219256145Sray	struct vt_window *curvw = vd->vd_curwindow;
220219888Sed	keyboard_t *kbd;
221219888Sed
222219888Sed	VT_LOCK(vd);
223256145Sray	if (curvw == vw) {
224256145Sray		/* Nothing to do. */
225219888Sed		VT_UNLOCK(vd);
226256145Sray		return (0);
227219888Sed	}
228256145Sray	if (!(vw->vw_flags & (VWF_OPENED|VWF_CONSOLE))) {
229256145Sray		VT_UNLOCK(vd);
230256145Sray		return (EINVAL);
231256145Sray	}
232256145Sray
233219888Sed	vd->vd_curwindow = vw;
234219888Sed	vd->vd_flags |= VDF_INVALID;
235219888Sed	cv_broadcast(&vd->vd_winswitch);
236219888Sed	VT_UNLOCK(vd);
237219888Sed
238256145Sray	if (vd->vd_driver->vd_postswitch)
239256145Sray		vd->vd_driver->vd_postswitch(vd);
240256145Sray
241219888Sed	/* Restore per-window keyboard mode. */
242219888Sed	mtx_lock(&Giant);
243219888Sed	kbd = kbd_get_keyboard(vd->vd_keyboard);
244256145Sray	if (kbd != NULL) {
245219888Sed		kbdd_ioctl(kbd, KDSKBMODE, (void *)&vw->vw_kbdmode);
246256145Sray	}
247219888Sed	mtx_unlock(&Giant);
248256145Sray	DPRINTF(10, "%s(ttyv%d) done\n", __func__, vw->vw_number);
249256145Sray
250256145Sray	return (0);
251219888Sed}
252219888Sed
253219888Sedstatic inline void
254219888Sedvt_termsize(struct vt_device *vd, struct vt_font *vf, term_pos_t *size)
255219888Sed{
256219888Sed
257219888Sed	size->tp_row = vd->vd_height;
258219888Sed	size->tp_col = vd->vd_width;
259219888Sed	if (vf != NULL) {
260219888Sed		size->tp_row /= vf->vf_height;
261219888Sed		size->tp_col /= vf->vf_width;
262219888Sed	}
263219888Sed}
264219888Sed
265219888Sedstatic inline void
266219888Sedvt_winsize(struct vt_device *vd, struct vt_font *vf, struct winsize *size)
267219888Sed{
268219888Sed
269219888Sed	size->ws_row = size->ws_ypixel = vd->vd_height;
270219888Sed	size->ws_col = size->ws_xpixel = vd->vd_width;
271219888Sed	if (vf != NULL) {
272219888Sed		size->ws_row /= vf->vf_height;
273219888Sed		size->ws_col /= vf->vf_width;
274219888Sed	}
275219888Sed}
276219888Sed
277219888Sedstatic int
278219888Sedvt_kbdevent(keyboard_t *kbd, int event, void *arg)
279219888Sed{
280219888Sed	struct vt_device *vd = arg;
281219888Sed	struct vt_window *vw = vd->vd_curwindow;
282256145Sray	int c;
283219888Sed
284219888Sed	switch (event) {
285219888Sed	case KBDIO_KEYINPUT:
286219888Sed		break;
287219888Sed	case KBDIO_UNLOADING:
288219888Sed		mtx_lock(&Giant);
289219888Sed		vd->vd_keyboard = -1;
290219888Sed		kbd_release(kbd, (void *)&vd->vd_keyboard);
291219888Sed		mtx_unlock(&Giant);
292219888Sed		return (0);
293219888Sed	default:
294219888Sed		return (EINVAL);
295219888Sed	}
296219888Sed
297219888Sed	c = kbdd_read_char(kbd, 0);
298219888Sed	if (c & RELKEY)
299219888Sed		return (0);
300219888Sed
301219888Sed	if (c & SPCLKEY) {
302219888Sed		c &= ~SPCLKEY;
303219888Sed
304219888Sed		if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) {
305219888Sed			vw = vd->vd_windows[c - F_SCR];
306219888Sed			if (vw != NULL)
307256145Sray				vt_proc_window_switch(vw);
308219888Sed			return (0);
309219888Sed		}
310219888Sed
311219888Sed		switch (c) {
312219888Sed		case DBG:
313219888Sed			kdb_enter(KDB_WHY_BREAK, "manual escape to debugger");
314219888Sed			break;
315219888Sed		case RBT:
316219888Sed			/* XXX: Make this configurable! */
317219888Sed			shutdown_nice(0);
318219888Sed			break;
319219888Sed		case HALT:
320219888Sed			shutdown_nice(RB_HALT);
321219888Sed			break;
322219888Sed		case PDWN:
323219888Sed			shutdown_nice(RB_HALT|RB_POWEROFF);
324219888Sed			break;
325219888Sed		case SLK: {
326243802Snwhitehorn			int state = 0;
327219888Sed
328219888Sed			kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state);
329219888Sed			VT_LOCK(vd);
330219888Sed			if (state & SLKED) {
331219888Sed				/* Turn scrolling on. */
332219888Sed				vw->vw_flags |= VWF_SCROLL;
333256145Sray				VTBUF_SLCK_ENABLE(&vw->vw_buf);
334219888Sed			} else {
335219888Sed				/* Turn scrolling off. */
336219888Sed				vw->vw_flags &= ~VWF_SCROLL;
337256145Sray				VTBUF_SLCK_DISABLE(&vw->vw_buf);
338256145Sray				vthistory_seek(&vw->vw_buf, 0, VHS_END);
339256145Sray				vd->vd_flags |= VDF_INVALID;
340219888Sed			}
341219888Sed			VT_UNLOCK(vd);
342219888Sed			break;
343219888Sed		}
344219888Sed		case FKEY | F(1):  case FKEY | F(2):  case FKEY | F(3):
345219888Sed		case FKEY | F(4):  case FKEY | F(5):  case FKEY | F(6):
346219888Sed		case FKEY | F(7):  case FKEY | F(8):  case FKEY | F(9):
347219888Sed		case FKEY | F(10): case FKEY | F(11): case FKEY | F(12):
348219888Sed			/* F1 through F12 keys. */
349219888Sed			terminal_input_special(vw->vw_terminal,
350219888Sed			    TKEY_F1 + c - (FKEY | F(1)));
351219888Sed			break;
352219888Sed		case FKEY | F(49): /* Home key. */
353219888Sed			VT_LOCK(vd);
354219888Sed			if (vw->vw_flags & VWF_SCROLL) {
355256145Sray				if (vthistory_seek(&vw->vw_buf, 0, VHS_END))
356256145Sray					vd->vd_flags |= VDF_INVALID;
357219888Sed				VT_UNLOCK(vd);
358219888Sed				break;
359219888Sed			}
360219888Sed			VT_UNLOCK(vd);
361219888Sed			terminal_input_special(vw->vw_terminal, TKEY_HOME);
362219888Sed			break;
363219888Sed		case FKEY | F(50): /* Arrow up. */
364219888Sed			VT_LOCK(vd);
365219888Sed			if (vw->vw_flags & VWF_SCROLL) {
366256145Sray				if (vthistory_seek(&vw->vw_buf, -1, VHS_CUR))
367256145Sray					vd->vd_flags |= VDF_INVALID;
368219888Sed				VT_UNLOCK(vd);
369219888Sed				break;
370219888Sed			}
371219888Sed			VT_UNLOCK(vd);
372219888Sed			terminal_input_special(vw->vw_terminal, TKEY_UP);
373219888Sed			break;
374219888Sed		case FKEY | F(51): /* Page up. */
375219888Sed			VT_LOCK(vd);
376219888Sed			if (vw->vw_flags & VWF_SCROLL) {
377219888Sed				term_pos_t size;
378219888Sed
379219888Sed				vt_termsize(vd, vw->vw_font, &size);
380256145Sray				if (vthistory_seek(&vw->vw_buf, -size.tp_row,
381256145Sray				    VHS_CUR))
382256145Sray					vd->vd_flags |= VDF_INVALID;
383219888Sed				VT_UNLOCK(vd);
384219888Sed				break;
385219888Sed			}
386219888Sed			VT_UNLOCK(vd);
387219888Sed			terminal_input_special(vw->vw_terminal, TKEY_PAGE_UP);
388219888Sed			break;
389219888Sed		case FKEY | F(53): /* Arrow left. */
390219888Sed			terminal_input_special(vw->vw_terminal, TKEY_LEFT);
391219888Sed			break;
392219888Sed		case FKEY | F(55): /* Arrow right. */
393219888Sed			terminal_input_special(vw->vw_terminal, TKEY_RIGHT);
394219888Sed			break;
395219888Sed		case FKEY | F(57): /* End key. */
396219888Sed			VT_LOCK(vd);
397219888Sed			if (vw->vw_flags & VWF_SCROLL) {
398256145Sray				if (vthistory_seek(&vw->vw_buf, 0, VHS_SET))
399256145Sray					vd->vd_flags |= VDF_INVALID;
400219888Sed				VT_UNLOCK(vd);
401219888Sed				break;
402219888Sed			}
403219888Sed			VT_UNLOCK(vd);
404219888Sed			terminal_input_special(vw->vw_terminal, TKEY_END);
405219888Sed			break;
406219888Sed		case FKEY | F(58): /* Arrow down. */
407219888Sed			VT_LOCK(vd);
408219888Sed			if (vw->vw_flags & VWF_SCROLL) {
409256145Sray				if (vthistory_seek(&vw->vw_buf, 1, VHS_CUR))
410256145Sray					vd->vd_flags |= VDF_INVALID;
411219888Sed				VT_UNLOCK(vd);
412219888Sed				break;
413219888Sed			}
414219888Sed			VT_UNLOCK(vd);
415219888Sed			terminal_input_special(vw->vw_terminal, TKEY_DOWN);
416219888Sed			break;
417219888Sed		case FKEY | F(59): /* Page down. */
418219888Sed			VT_LOCK(vd);
419219888Sed			if (vw->vw_flags & VWF_SCROLL) {
420219888Sed				term_pos_t size;
421219888Sed
422219888Sed				vt_termsize(vd, vw->vw_font, &size);
423256145Sray				if (vthistory_seek(&vw->vw_buf, size.tp_row,
424256145Sray				    VHS_CUR))
425256145Sray					vd->vd_flags |= VDF_INVALID;
426219888Sed				VT_UNLOCK(vd);
427219888Sed				break;
428219888Sed			}
429219888Sed			VT_UNLOCK(vd);
430219888Sed			terminal_input_special(vw->vw_terminal, TKEY_PAGE_DOWN);
431219888Sed			break;
432219888Sed		case FKEY | F(60): /* Insert key. */
433219888Sed			terminal_input_special(vw->vw_terminal, TKEY_INSERT);
434219888Sed			break;
435219888Sed		case FKEY | F(61): /* Delete key. */
436219888Sed			terminal_input_special(vw->vw_terminal, TKEY_DELETE);
437219888Sed			break;
438219888Sed		}
439219888Sed	} else if (KEYFLAGS(c) == 0) {
440219888Sed		/* Don't do UTF-8 conversion when doing raw mode. */
441219888Sed		if (vw->vw_kbdmode == K_XLATE)
442256145Sray			terminal_input_char(vw->vw_terminal, KEYCHAR(c));
443219888Sed		else
444219888Sed			terminal_input_raw(vw->vw_terminal, c);
445219888Sed	}
446219888Sed	return (0);
447219888Sed}
448219888Sed
449219888Sedstatic int
450219888Sedvt_allocate_keyboard(struct vt_device *vd)
451219888Sed{
452219888Sed	int		 idx0, idx;
453219888Sed	keyboard_t	*k0, *k;
454219888Sed	keyboard_info_t	 ki;
455219888Sed
456219888Sed	idx0 = kbd_allocate("kbdmux", -1, (void *)&vd->vd_keyboard,
457219888Sed	    vt_kbdevent, vd);
458256145Sray	/* XXX: kb_token lost */
459256145Sray	vd->vd_keyboard = idx0;
460219888Sed	if (idx0 != -1) {
461256145Sray		DPRINTF(20, "%s: kbdmux allocated, idx = %d\n", __func__, idx0);
462219888Sed		k0 = kbd_get_keyboard(idx0);
463219888Sed
464219888Sed		for (idx = kbd_find_keyboard2("*", -1, 0);
465219888Sed		     idx != -1;
466219888Sed		     idx = kbd_find_keyboard2("*", -1, idx + 1)) {
467219888Sed			k = kbd_get_keyboard(idx);
468219888Sed
469219888Sed			if (idx == idx0 || KBD_IS_BUSY(k))
470219888Sed				continue;
471219888Sed
472219888Sed			bzero(&ki, sizeof(ki));
473219888Sed			strcpy(ki.kb_name, k->kb_name);
474219888Sed			ki.kb_unit = k->kb_unit;
475219888Sed
476219888Sed			kbdd_ioctl(k0, KBADDKBD, (caddr_t) &ki);
477219888Sed		}
478256145Sray	} else {
479256145Sray		DPRINTF(20, "%s: no kbdmux allocated\n", __func__);
480219888Sed		idx0 = kbd_allocate("*", -1, (void *)&vd->vd_keyboard,
481219888Sed		    vt_kbdevent, vd);
482256145Sray	}
483256145Sray	DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard);
484219888Sed
485219888Sed	return (idx0);
486219888Sed}
487219888Sed
488219888Sedstatic void
489219888Sedvtterm_bell(struct terminal *tm)
490219888Sed{
491219888Sed
492219888Sed	sysbeep(1193182 / VT_BELLPITCH, VT_BELLDURATION);
493219888Sed}
494219888Sed
495219888Sedstatic void
496219888Sedvtterm_cursor(struct terminal *tm, const term_pos_t *p)
497219888Sed{
498219888Sed	struct vt_window *vw = tm->tm_softc;
499219888Sed
500219888Sed	vtbuf_cursor_position(&vw->vw_buf, p);
501219888Sed}
502219888Sed
503219888Sedstatic void
504219888Sedvtterm_putchar(struct terminal *tm, const term_pos_t *p, term_char_t c)
505219888Sed{
506219888Sed	struct vt_window *vw = tm->tm_softc;
507219888Sed
508219888Sed	vtbuf_putchar(&vw->vw_buf, p, c);
509219888Sed}
510219888Sed
511219888Sedstatic void
512219888Sedvtterm_fill(struct terminal *tm, const term_rect_t *r, term_char_t c)
513219888Sed{
514219888Sed	struct vt_window *vw = tm->tm_softc;
515219888Sed
516256145Sray	vtbuf_fill_locked(&vw->vw_buf, r, c);
517219888Sed}
518219888Sed
519219888Sedstatic void
520219888Sedvtterm_copy(struct terminal *tm, const term_rect_t *r,
521219888Sed    const term_pos_t *p)
522219888Sed{
523219888Sed	struct vt_window *vw = tm->tm_softc;
524219888Sed
525219888Sed	vtbuf_copy(&vw->vw_buf, r, p);
526219888Sed}
527219888Sed
528219888Sedstatic void
529219888Sedvtterm_param(struct terminal *tm, int cmd, unsigned int arg)
530219888Sed{
531219888Sed	struct vt_window *vw = tm->tm_softc;
532219888Sed
533219888Sed	switch (cmd) {
534219888Sed	case TP_SHOWCURSOR:
535219888Sed		vtbuf_cursor_visibility(&vw->vw_buf, arg);
536219888Sed		break;
537219888Sed	}
538219888Sed}
539219888Sed
540219888Sedstatic inline void
541219888Sedvt_determine_colors(term_char_t c, int cursor,
542219888Sed    term_color_t *fg, term_color_t *bg)
543219888Sed{
544219888Sed
545219888Sed	*fg = TCHAR_FGCOLOR(c);
546219888Sed	if (TCHAR_FORMAT(c) & TF_BOLD)
547219888Sed		*fg = TCOLOR_LIGHT(*fg);
548219888Sed	*bg = TCHAR_BGCOLOR(c);
549219888Sed
550219888Sed	if (TCHAR_FORMAT(c) & TF_REVERSE) {
551219888Sed		term_color_t tmp;
552219888Sed
553219888Sed		tmp = *fg;
554219888Sed		*fg = *bg;
555219888Sed		*bg = tmp;
556219888Sed	}
557219888Sed
558219888Sed	if (cursor) {
559219888Sed		*fg = *bg;
560219888Sed		*bg = TC_WHITE;
561219888Sed	}
562219888Sed}
563219888Sed
564219888Sedstatic void
565219888Sedvt_bitblt_char(struct vt_device *vd, struct vt_font *vf, term_char_t c,
566219888Sed    int iscursor, unsigned int row, unsigned int col)
567219888Sed{
568219888Sed	term_color_t fg, bg;
569219888Sed
570219888Sed	vt_determine_colors(c, iscursor, &fg, &bg);
571219888Sed
572219888Sed	if (vf != NULL) {
573219888Sed		const uint8_t *src;
574219888Sed		vt_axis_t top, left;
575219888Sed
576219888Sed		src = vtfont_lookup(vf, c);
577219888Sed
578219888Sed		/*
579219888Sed		 * Align the terminal to the centre of the screen.
580219888Sed		 * Fonts may not always be able to fill the entire
581219888Sed		 * screen.
582219888Sed		 */
583219888Sed		top = row * vf->vf_height +
584219888Sed		    (vd->vd_height % vf->vf_height) / 2;
585219888Sed		left = col * vf->vf_width +
586219888Sed		    (vd->vd_width % vf->vf_width) / 2;
587219888Sed
588219888Sed		vd->vd_driver->vd_bitblt(vd, src, top, left,
589219888Sed		    vf->vf_width, vf->vf_height, fg, bg);
590219888Sed	} else {
591219888Sed		vd->vd_driver->vd_putchar(vd, TCHAR_CHARACTER(c),
592219888Sed		    row, col, fg, bg);
593219888Sed	}
594219888Sed}
595219888Sed
596219888Sedstatic void
597219888Sedvt_flush(struct vt_device *vd)
598219888Sed{
599219888Sed	struct vt_window *vw = vd->vd_curwindow;
600219888Sed	struct vt_font *vf = vw->vw_font;
601219888Sed	term_pos_t size;
602219888Sed	term_rect_t tarea;
603219888Sed	struct vt_bufmask tmask;
604256145Sray	unsigned int row, col;
605256145Sray	term_char_t *r;
606219888Sed
607219888Sed	if (vd->vd_flags & VDF_SPLASH || vw->vw_flags & VWF_BUSY)
608219888Sed		return;
609219888Sed
610219888Sed	vtbuf_undirty(&vw->vw_buf, &tarea, &tmask);
611219888Sed	vt_termsize(vd, vf, &size);
612219888Sed
613219888Sed	/* Force a full redraw when the screen contents are invalid. */
614256145Sray	if (vd->vd_flags & VDF_INVALID) {
615219888Sed		tarea.tr_begin.tp_row = tarea.tr_begin.tp_col = 0;
616219888Sed		tarea.tr_end = size;
617219888Sed		tmask.vbm_row = tmask.vbm_col = VBM_DIRTY;
618219888Sed
619219888Sed		/*
620219888Sed		 * Blank to prevent borders with artifacts.  This is
621219888Sed		 * only required when the font doesn't exactly fill the
622219888Sed		 * screen.
623219888Sed		 */
624219888Sed		if (vd->vd_flags & VDF_INVALID && vf != NULL &&
625219888Sed		    (vd->vd_width % vf->vf_width != 0 ||
626219888Sed		    vd->vd_height % vf->vf_height != 0))
627219888Sed			vd->vd_driver->vd_blank(vd, TC_BLACK);
628219888Sed
629219888Sed		vd->vd_flags &= ~VDF_INVALID;
630219888Sed	}
631219888Sed
632219888Sed
633219888Sed	for (row = tarea.tr_begin.tp_row; row < tarea.tr_end.tp_row; row++) {
634219888Sed		if (!VTBUF_DIRTYROW(&tmask, row))
635219888Sed			continue;
636256145Sray		r = VTBUF_GET_ROW(&vw->vw_buf, row);
637219888Sed		for (col = tarea.tr_begin.tp_col;
638219888Sed		    col < tarea.tr_end.tp_col; col++) {
639219888Sed			if (!VTBUF_DIRTYCOL(&tmask, col))
640219888Sed				continue;
641219888Sed
642256145Sray			vt_bitblt_char(vd, vf, r[col],
643219888Sed			    VTBUF_ISCURSOR(&vw->vw_buf, row, col),
644256145Sray			    row, col);
645219888Sed		}
646219888Sed	}
647219888Sed}
648219888Sed
649219888Sedstatic void
650219888Sedvt_timer(void *arg)
651219888Sed{
652219888Sed	struct vt_device *vd = arg;
653256145Sray	unsigned int i;
654219888Sed
655219888Sed	vt_flush(vd);
656256145Sray
657256145Sray	for (i = 0; i < VT_MAXWINDOWS; i++)
658256145Sray		vt_proc_alive(vd->vd_windows[i]);
659256145Sray
660219888Sed	callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ);
661219888Sed}
662219888Sed
663219888Sedstatic void
664219888Sedvtterm_done(struct terminal *tm)
665219888Sed{
666219888Sed	struct vt_window *vw = tm->tm_softc;
667219888Sed	struct vt_device *vd = vw->vw_device;
668219888Sed
669219888Sed	if (kdb_active || panicstr != NULL) {
670219888Sed		/* Switch to the debugger. */
671219888Sed		if (vd->vd_curwindow != vw) {
672219888Sed			vd->vd_curwindow = vw;
673219888Sed			vd->vd_flags |= VDF_INVALID;
674219888Sed		}
675219888Sed		vd->vd_flags &= ~VDF_SPLASH;
676219888Sed		vt_flush(vd);
677219888Sed	} else if (!(vd->vd_flags & VDF_ASYNC)) {
678219888Sed		vt_flush(vd);
679219888Sed	}
680219888Sed}
681219888Sed
682219888Sedstatic void
683256145Srayvtterm_splash(struct vt_device *vd)
684256145Sray{
685256145Sray	vt_axis_t top, left;
686256145Sray
687256145Sray	/* Display a nice boot splash. */
688256145Sray	if (!(vd->vd_flags & VDF_TEXTMODE)) {
689256145Sray
690256145Sray		top = (vd->vd_height - vt_logo_height) / 2;
691256145Sray		left = (vd->vd_width - vt_logo_width) / 2;
692256145Sray		switch (vt_logo_depth) {
693256145Sray		case 1:
694256145Sray			/* XXX: Unhardcode colors! */
695256145Sray			vd->vd_driver->vd_bitblt(vd, vt_logo_image, top, left,
696256145Sray			    vt_logo_width, vt_logo_height, 0xf, 0x0);
697256145Sray		}
698256145Sray		vd->vd_flags |= VDF_SPLASH;
699256145Sray	}
700256145Sray}
701256145Sray
702256145Sraystatic void
703219888Sedvtterm_cnprobe(struct terminal *tm, struct consdev *cp)
704219888Sed{
705219888Sed	struct vt_window *vw = tm->tm_softc;
706219888Sed	struct vt_device *vd = vw->vw_device;
707219888Sed	struct winsize wsz;
708219888Sed
709256145Sray	if (vd->vd_flags & VDF_INITIALIZED)
710256145Sray		/* Initialization already done. */
711256145Sray		return;
712256145Sray
713219888Sed	cp->cn_pri = vd->vd_driver->vd_init(vd);
714219888Sed	if (cp->cn_pri == CN_DEAD) {
715219888Sed		vd->vd_flags |= VDF_DEAD;
716219888Sed		return;
717219888Sed	}
718219888Sed
719230469Snwhitehorn	/* Initialize any early-boot keyboard drivers */
720230469Snwhitehorn	kbd_configure(KB_CONF_PROBE_ONLY);
721230469Snwhitehorn
722219888Sed	vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1);
723256145Sray	vd->vd_windows[VT_CONSWINDOW] = vw;
724219888Sed	sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw));
725219888Sed
726219888Sed	if (!(vd->vd_flags & VDF_TEXTMODE))
727219888Sed		vw->vw_font = vtfont_ref(&vt_font_default);
728219888Sed
729219888Sed	vtbuf_init_early(&vw->vw_buf);
730219888Sed	vt_winsize(vd, vw->vw_font, &wsz);
731219888Sed	terminal_set_winsize(tm, &wsz);
732219888Sed
733256145Sray	vtterm_splash(vd);
734219888Sed
735256145Sray	vd->vd_flags |= VDF_INITIALIZED;
736256145Sray	main_vd = vd;
737219888Sed}
738219888Sed
739219888Sedstatic int
740219888Sedvtterm_cngetc(struct terminal *tm)
741219888Sed{
742219888Sed	struct vt_window *vw = tm->tm_softc;
743219888Sed	struct vt_device *vd = vw->vw_device;
744219888Sed	keyboard_t *kbd;
745219888Sed	u_int c;
746219888Sed
747219888Sed	/* Make sure the splash screen is not there. */
748219888Sed	if (vd->vd_flags & VDF_SPLASH) {
749256145Sray		/* Remove splash */
750219888Sed		vd->vd_flags &= ~VDF_SPLASH;
751256145Sray		/* Mark screen as invalid to force update */
752256145Sray		vd->vd_flags |= VDF_INVALID;
753219888Sed		vt_flush(vd);
754219888Sed	}
755219888Sed
756219888Sed	/* Stripped down keyboard handler. */
757219888Sed	kbd = kbd_get_keyboard(vd->vd_keyboard);
758219888Sed	if (kbd == NULL)
759219888Sed		return (-1);
760219888Sed
761256145Sray	/* Force keyboard input mode to K_XLATE */
762256145Sray	c = K_XLATE;
763256145Sray	kbdd_ioctl(kbd, KDSKBMODE, (void *)&c);
764256145Sray
765219888Sed	/* Switch the keyboard to polling to make it work here. */
766219888Sed	kbdd_poll(kbd, TRUE);
767219888Sed	c = kbdd_read_char(kbd, 0);
768219888Sed	kbdd_poll(kbd, FALSE);
769219888Sed	if (c & RELKEY)
770219888Sed		return (-1);
771256145Sray
772219888Sed	/* Stripped down handling of vt_kbdevent(), without locking, etc. */
773219888Sed	if (c & SPCLKEY) {
774219888Sed		c &= ~SPCLKEY;
775219888Sed
776219888Sed		switch (c) {
777219888Sed		case SLK: {
778243802Snwhitehorn			int state = 0;
779219888Sed
780219888Sed			kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state);
781219888Sed			if (state & SLKED) {
782219888Sed				/* Turn scrolling on. */
783219888Sed				vw->vw_flags |= VWF_SCROLL;
784256145Sray				VTBUF_SLCK_ENABLE(&vw->vw_buf);
785219888Sed			} else {
786219888Sed				/* Turn scrolling off. */
787219888Sed				vw->vw_flags &= ~VWF_SCROLL;
788256145Sray				VTBUF_SLCK_DISABLE(&vw->vw_buf);
789256145Sray				vthistory_seek(&vw->vw_buf, 0, VHS_END);
790256145Sray				vd->vd_flags |= VDF_INVALID;
791219888Sed			}
792219888Sed			break;
793219888Sed		}
794219888Sed		case FKEY | F(49): /* Home key. */
795219888Sed			if (vw->vw_flags & VWF_SCROLL)
796256145Sray				if (vthistory_seek(&vw->vw_buf, 0, VHS_END))
797256145Sray					vd->vd_flags |= VDF_INVALID;
798219888Sed			break;
799219888Sed		case FKEY | F(50): /* Arrow up. */
800219888Sed			if (vw->vw_flags & VWF_SCROLL)
801256145Sray				if (vthistory_seek(&vw->vw_buf, -1, VHS_CUR))
802256145Sray					vd->vd_flags |= VDF_INVALID;
803219888Sed			break;
804219888Sed		case FKEY | F(51): /* Page up. */
805219888Sed			if (vw->vw_flags & VWF_SCROLL) {
806219888Sed				term_pos_t size;
807219888Sed
808219888Sed				vt_termsize(vd, vw->vw_font, &size);
809256145Sray				if (vthistory_seek(&vw->vw_buf, -size.tp_row,
810256145Sray				    VHS_CUR))
811256145Sray					vd->vd_flags |= VDF_INVALID;
812219888Sed			}
813219888Sed			break;
814219888Sed		case FKEY | F(57): /* End key. */
815219888Sed			if (vw->vw_flags & VWF_SCROLL)
816256145Sray				if (vthistory_seek(&vw->vw_buf, 0, VHS_SET))
817256145Sray					vd->vd_flags |= VDF_INVALID;
818219888Sed			break;
819219888Sed		case FKEY | F(58): /* Arrow down. */
820219888Sed			if (vw->vw_flags & VWF_SCROLL)
821256145Sray				if (vthistory_seek(&vw->vw_buf, 1, VHS_CUR))
822256145Sray					vd->vd_flags |= VDF_INVALID;
823219888Sed			break;
824219888Sed		case FKEY | F(59): /* Page down. */
825219888Sed			if (vw->vw_flags & VWF_SCROLL) {
826219888Sed				term_pos_t size;
827219888Sed
828219888Sed				vt_termsize(vd, vw->vw_font, &size);
829256145Sray				if (vthistory_seek(&vw->vw_buf, size.tp_row,
830256145Sray				    VHS_CUR))
831256145Sray					vd->vd_flags |= VDF_INVALID;
832219888Sed			}
833219888Sed			break;
834219888Sed		}
835219888Sed
836219888Sed		/* Force refresh to make scrollback work. */
837219888Sed		vt_flush(vd);
838219888Sed	} else if (KEYFLAGS(c) == 0) {
839219888Sed		return KEYCHAR(c);
840219888Sed	}
841256145Sray
842219888Sed	return (-1);
843219888Sed}
844219888Sed
845219888Sedstatic void
846219888Sedvtterm_opened(struct terminal *tm, int opened)
847219888Sed{
848219888Sed	struct vt_window *vw = tm->tm_softc;
849219888Sed	struct vt_device *vd = vw->vw_device;
850219888Sed
851219888Sed	VT_LOCK(vd);
852219888Sed	vd->vd_flags &= ~VDF_SPLASH;
853219888Sed	if (opened)
854219888Sed		vw->vw_flags |= VWF_OPENED;
855256145Sray	else {
856219888Sed		vw->vw_flags &= ~VWF_OPENED;
857256145Sray		/* TODO: finish ACQ/REL */
858256145Sray	}
859219888Sed	VT_UNLOCK(vd);
860219888Sed}
861219888Sed
862219888Sedstatic int
863219888Sedvt_change_font(struct vt_window *vw, struct vt_font *vf)
864219888Sed{
865219888Sed	struct vt_device *vd = vw->vw_device;
866219888Sed	struct terminal *tm = vw->vw_terminal;
867219888Sed	term_pos_t size;
868219888Sed	struct winsize wsz;
869219888Sed
870219888Sed	/*
871219888Sed	 * Changing fonts.
872219888Sed	 *
873219888Sed	 * Changing fonts is a little tricky.  We must prevent
874219888Sed	 * simultaneous access to the device, so we must stop
875219888Sed	 * the display timer and the terminal from accessing.
876219888Sed	 * We need to switch fonts and grow our screen buffer.
877219888Sed	 *
878219888Sed	 * XXX: Right now the code uses terminal_mute() to
879219888Sed	 * prevent data from reaching the console driver while
880219888Sed	 * resizing the screen buffer.  This isn't elegant...
881219888Sed	 */
882219888Sed
883219888Sed	VT_LOCK(vd);
884219888Sed	if (vw->vw_flags & VWF_BUSY) {
885219888Sed		/* Another process is changing the font. */
886219888Sed		VT_UNLOCK(vd);
887219888Sed		return (EBUSY);
888219888Sed	}
889219888Sed	if (vw->vw_font == NULL) {
890219888Sed		/* Our device doesn't need fonts. */
891219888Sed		VT_UNLOCK(vd);
892219888Sed		return (ENOTTY);
893219888Sed	}
894219888Sed	vw->vw_flags |= VWF_BUSY;
895219888Sed	VT_UNLOCK(vd);
896219888Sed
897219888Sed	vt_termsize(vd, vf, &size);
898219888Sed	vt_winsize(vd, vf, &wsz);
899219888Sed
900219888Sed	/* Grow the screen buffer and terminal. */
901219888Sed	terminal_mute(tm, 1);
902256145Sray	vtbuf_grow(&vw->vw_buf, &size, vw->vw_buf.vb_history_size);
903219888Sed	terminal_set_winsize(tm, &wsz);
904219888Sed	terminal_mute(tm, 0);
905219888Sed
906219888Sed	/* Actually apply the font to the current window. */
907219888Sed	VT_LOCK(vd);
908219888Sed	vtfont_unref(vw->vw_font);
909219888Sed	vw->vw_font = vtfont_ref(vf);
910219888Sed
911219888Sed	/* Force a full redraw the next timer tick. */
912219888Sed	if (vd->vd_curwindow == vw)
913219888Sed		vd->vd_flags |= VDF_INVALID;
914219888Sed	vw->vw_flags &= ~VWF_BUSY;
915219888Sed	VT_UNLOCK(vd);
916219888Sed	return (0);
917219888Sed}
918219888Sed
919219888Sedstatic int
920256145Srayvt_proc_alive(struct vt_window *vw)
921256145Sray{
922256145Sray	struct proc *p;
923256145Sray
924256145Sray	if (vw->vw_smode.mode != VT_PROCESS)
925256145Sray		return FALSE;
926256145Sray
927256145Sray	if (vw->vw_proc) {
928256145Sray		if ((p = pfind(vw->vw_pid)) != NULL)
929256145Sray			PROC_UNLOCK(p);
930256145Sray		if (vw->vw_proc == p)
931256145Sray			return TRUE;
932256145Sray		vw->vw_proc = NULL;
933256145Sray		vw->vw_smode.mode = VT_AUTO;
934256145Sray		DPRINTF(1, "vt controlling process %d died\n", vw->vw_pid);
935256145Sray		vw->vw_pid = 0;
936256145Sray	}
937256145Sray	return FALSE;
938256145Sray}
939256145Sray
940256145Sraystatic int
941256145Sraysignal_vt_rel(struct vt_window *vw)
942256145Sray{
943256145Sray	if (vw->vw_smode.mode != VT_PROCESS)
944256145Sray		return FALSE;
945256145Sray	if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) {
946256145Sray		vw->vw_proc = NULL;
947256145Sray		vw->vw_pid = 0;
948256145Sray		return TRUE;
949256145Sray	}
950256145Sray	vw->vw_flags |= VWF_SWWAIT_REL;
951256145Sray	PROC_LOCK(vw->vw_proc);
952256145Sray	kern_psignal(vw->vw_proc, vw->vw_smode.relsig);
953256145Sray	PROC_UNLOCK(vw->vw_proc);
954256145Sray	DPRINTF(1, "sending relsig to %d\n", vw->vw_pid);
955256145Sray	return TRUE;
956256145Sray}
957256145Sray
958256145Sraystatic int
959256145Sraysignal_vt_acq(struct vt_window *vw)
960256145Sray{
961256145Sray	if (vw->vw_smode.mode != VT_PROCESS)
962256145Sray		return FALSE;
963256145Sray	if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW])
964256145Sray		cnavailable(vw->vw_terminal->consdev, FALSE);
965256145Sray	if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) {
966256145Sray		vw->vw_proc = NULL;
967256145Sray		vw->vw_pid = 0;
968256145Sray		return TRUE;
969256145Sray	}
970256145Sray	vw->vw_flags |= VWF_SWWAIT_ACQ;
971256145Sray	PROC_LOCK(vw->vw_proc);
972256145Sray	kern_psignal(vw->vw_proc, vw->vw_smode.acqsig);
973256145Sray	PROC_UNLOCK(vw->vw_proc);
974256145Sray	DPRINTF(1, "sending acqsig to %d\n", vw->vw_pid);
975256145Sray	return TRUE;
976256145Sray}
977256145Sray
978256145Sraystatic int
979256145Srayfinish_vt_rel(struct vt_window *vw, int release, int *s)
980256145Sray{
981256145Sray	if (vw->vw_flags & VWF_SWWAIT_REL) {
982256145Sray		vw->vw_flags &= ~VWF_SWWAIT_REL;
983256145Sray		if (release) {
984256145Sray			callout_drain(&vw->vw_proc_dead_timer);
985256145Sray			vt_late_window_switch(vw->vw_switch_to);
986256145Sray		}
987256145Sray		return 0;
988256145Sray	}
989256145Sray	return EINVAL;
990256145Sray}
991256145Sray
992256145Sraystatic int
993256145Srayfinish_vt_acq(struct vt_window *vw)
994256145Sray{
995256145Sray	if (vw->vw_flags & VWF_SWWAIT_ACQ) {
996256145Sray		vw->vw_flags &= ~VWF_SWWAIT_ACQ;
997256145Sray		return 0;
998256145Sray	}
999256145Sray	return EINVAL;
1000256145Sray}
1001256145Sray
1002256145Sraystatic int
1003219888Sedvtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data,
1004219888Sed    struct thread *td)
1005219888Sed{
1006219888Sed	struct vt_window *vw = tm->tm_softc;
1007219888Sed	struct vt_device *vd = vw->vw_device;
1008256145Sray	int error, s;
1009219888Sed
1010219888Sed	switch (cmd) {
1011219888Sed	case GIO_KEYMAP:
1012219888Sed	case PIO_KEYMAP:
1013219888Sed	case GIO_DEADKEYMAP:
1014219888Sed	case PIO_DEADKEYMAP:
1015219888Sed	case GETFKEY:
1016219888Sed	case SETFKEY:
1017219888Sed	case KDGKBINFO: {
1018219888Sed		keyboard_t *kbd;
1019256145Sray		error = 0;
1020219888Sed
1021219888Sed		mtx_lock(&Giant);
1022219888Sed		kbd = kbd_get_keyboard(vd->vd_keyboard);
1023219888Sed		if (kbd != NULL)
1024219888Sed			error = kbdd_ioctl(kbd, cmd, data);
1025219888Sed		mtx_unlock(&Giant);
1026219888Sed		if (error == ENOIOCTL)
1027219888Sed			return (ENODEV);
1028219888Sed		return (error);
1029219888Sed	}
1030256145Sray	case KDGKBMODE: {
1031256145Sray		int mode = -1;
1032256145Sray		keyboard_t *kbd;
1033256145Sray
1034256145Sray		mtx_lock(&Giant);
1035256145Sray		kbd = kbd_get_keyboard(vd->vd_keyboard);
1036256145Sray		if (kbd != NULL) {
1037256145Sray			kbdd_ioctl(kbd, KDGKBMODE, (void *)&mode);
1038256145Sray		}
1039256145Sray		mtx_unlock(&Giant);
1040256145Sray		DPRINTF(20, "mode %d, vw_kbdmode %d\n", mode, vw->vw_kbdmode);
1041256145Sray		*(int *)data = mode;
1042256145Sray		return (0);
1043256145Sray	}
1044219888Sed	case KDSKBMODE: {
1045219888Sed		int mode;
1046219888Sed
1047219888Sed		mode = *(int *)data;
1048219888Sed		switch (mode) {
1049219888Sed		case K_XLATE:
1050219888Sed		case K_RAW:
1051219888Sed		case K_CODE:
1052219888Sed			vw->vw_kbdmode = mode;
1053219888Sed			if (vw == vd->vd_curwindow) {
1054219888Sed				keyboard_t *kbd;
1055256145Sray				error = 0;
1056219888Sed
1057256145Sray				DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard);
1058219888Sed				mtx_lock(&Giant);
1059219888Sed				kbd = kbd_get_keyboard(vd->vd_keyboard);
1060256145Sray				if (kbd != NULL) {
1061256145Sray					DPRINTF(20, "kbdd_ioctl(KDSKBMODE, %d)\n", mode);
1062256145Sray					error = kbdd_ioctl(kbd, KDSKBMODE,
1063219888Sed					    (void *)&mode);
1064256145Sray				}
1065219888Sed				mtx_unlock(&Giant);
1066256145Sray				if (error)
1067256145Sray					DPRINTF(20, "kbdd_ioctl(KDSKBMODE) return %d\n", error);
1068219888Sed			}
1069219888Sed			return (0);
1070219888Sed		default:
1071219888Sed			return (EINVAL);
1072219888Sed		}
1073219888Sed	}
1074219888Sed	case CONS_BLANKTIME:
1075219888Sed		/* XXX */
1076219888Sed		return (0);
1077219888Sed	case CONS_GET:
1078219888Sed		/* XXX */
1079219888Sed		*(int *)data = M_CG640x480;
1080219888Sed		return (0);
1081219888Sed	case CONS_GETINFO: {
1082219888Sed		vid_info_t *vi = (vid_info_t *)data;
1083219888Sed
1084219888Sed		vi->m_num = vd->vd_curwindow->vw_number + 1;
1085219888Sed		/* XXX: other fields! */
1086219888Sed		return (0);
1087219888Sed	}
1088219888Sed	case CONS_GETVERS:
1089219888Sed		*(int *)data = 0x200;
1090219888Sed		return 0;
1091219888Sed	case CONS_MODEINFO:
1092219888Sed		/* XXX */
1093219888Sed		return (0);
1094219888Sed	case CONS_MOUSECTL: {
1095219888Sed		mouse_info_t *mouse = (mouse_info_t*)data;
1096219888Sed
1097219888Sed		/*
1098219888Sed		 * This has no effect on vt(4).  We don't draw any mouse
1099219888Sed		 * cursor.  Just ignore MOUSE_HIDE and MOUSE_SHOW to
1100219888Sed		 * prevent excessive errors.  All the other commands
1101219888Sed		 * should not be applied to individual TTYs, but only to
1102219888Sed		 * consolectl.
1103219888Sed		 */
1104219888Sed		switch (mouse->operation) {
1105219888Sed		case MOUSE_HIDE:
1106219888Sed		case MOUSE_SHOW:
1107219888Sed			return (0);
1108219888Sed		default:
1109219888Sed			return (EINVAL);
1110219888Sed		}
1111219888Sed	}
1112219888Sed	case PIO_VFONT: {
1113219888Sed		struct vt_font *vf;
1114219888Sed
1115219888Sed		error = vtfont_load((void *)data, &vf);
1116219888Sed		if (error != 0)
1117219888Sed			return (error);
1118219888Sed
1119219888Sed		error = vt_change_font(vw, vf);
1120219888Sed		vtfont_unref(vf);
1121219888Sed		return (error);
1122219888Sed	}
1123219888Sed	case GIO_SCRNMAP: {
1124219888Sed		scrmap_t *sm = (scrmap_t *)data;
1125219888Sed		int i;
1126219888Sed
1127219888Sed		/* We don't have screen maps, so return a handcrafted one. */
1128219888Sed		for (i = 0; i < 256; i++)
1129219888Sed			sm->scrmap[i] = i;
1130219888Sed		return (0);
1131219888Sed	}
1132219888Sed	case KDGETLED:
1133219888Sed		/* XXX */
1134219888Sed		return (0);
1135219888Sed	case KDSETLED:
1136219888Sed		/* XXX */
1137219888Sed		return (0);
1138219888Sed	case KDSETMODE:
1139219888Sed		/* XXX */
1140219888Sed		return (0);
1141219888Sed	case KDSETRAD:
1142219888Sed		/* XXX */
1143219888Sed		return (0);
1144256145Sray	case VT_ACTIVATE: {
1145256145Sray		int win;
1146256145Sray		win = *(int *)data - 1;
1147256145Sray		DPRINTF(5, "%s%d: VT_ACTIVATE ttyv%d ", SC_DRIVER_NAME, VT_UNIT(vw), win);
1148256145Sray		if ((win > VT_MAXWINDOWS) || (win < 0))
1149256145Sray			return (EINVAL);
1150256145Sray		return (vt_proc_window_switch(vd->vd_windows[win]));
1151256145Sray	}
1152219888Sed	case VT_GETACTIVE:
1153219888Sed		*(int *)data = vd->vd_curwindow->vw_number + 1;
1154219888Sed		return (0);
1155219888Sed	case VT_GETINDEX:
1156219888Sed		*(int *)data = vw->vw_number + 1;
1157219888Sed		return (0);
1158256145Sray	case VT_LOCKSWITCH:
1159256145Sray		/* TODO: Check current state, switching can be in progress. */
1160256145Sray		if ((*(int *)data) & 0x01)
1161256145Sray			vw->vw_flags |= VWF_VTYLOCK;
1162256145Sray		else
1163256145Sray			vw->vw_flags &= ~VWF_VTYLOCK;
1164219888Sed	case VT_OPENQRY: {
1165219888Sed		unsigned int i;
1166219888Sed
1167219888Sed		VT_LOCK(vd);
1168219888Sed		for (i = 0; i < VT_MAXWINDOWS; i++) {
1169219888Sed			vw = vd->vd_windows[i];
1170219888Sed			if (vw == NULL)
1171219888Sed				continue;
1172219888Sed			if (!(vw->vw_flags & VWF_OPENED)) {
1173219888Sed				*(int *)data = vw->vw_number + 1;
1174219888Sed				VT_UNLOCK(vd);
1175219888Sed				return (0);
1176219888Sed			}
1177219888Sed		}
1178219888Sed		VT_UNLOCK(vd);
1179219888Sed		return (EINVAL);
1180219888Sed	}
1181219888Sed	case VT_WAITACTIVE: {
1182219888Sed		unsigned int i;
1183256145Sray		error = 0;
1184219888Sed
1185219888Sed		i = *(unsigned int *)data;
1186219888Sed		if (i > VT_MAXWINDOWS)
1187219888Sed			return (EINVAL);
1188219888Sed		if (i != 0)
1189219888Sed			vw = vd->vd_windows[i - 1];
1190219888Sed
1191219888Sed		VT_LOCK(vd);
1192219888Sed		while (vd->vd_curwindow != vw && error == 0)
1193219888Sed			error = cv_wait_sig(&vd->vd_winswitch, &vd->vd_lock);
1194219888Sed		VT_UNLOCK(vd);
1195219888Sed		return (error);
1196219888Sed	}
1197256145Sray	case VT_SETMODE:    	/* set screen switcher mode */
1198256145Sray	{
1199256145Sray		struct vt_mode *mode;
1200256145Sray		struct proc *p1;
1201256145Sray
1202256145Sray		mode = (struct vt_mode *)data;
1203256145Sray		DPRINTF(5, "%s%d: VT_SETMODE ", SC_DRIVER_NAME, VT_UNIT(vw));
1204256145Sray		if (vw->vw_smode.mode == VT_PROCESS) {
1205256145Sray			p1 = pfind(vw->vw_pid);
1206256145Sray			if (vw->vw_proc == p1 && vw->vw_proc != td->td_proc) {
1207256145Sray				if (p1)
1208256145Sray					PROC_UNLOCK(p1);
1209256145Sray				DPRINTF(5, "error EPERM\n");
1210256145Sray				return (EPERM);
1211256145Sray			}
1212256145Sray			if (p1)
1213256145Sray				PROC_UNLOCK(p1);
1214256145Sray		}
1215256145Sray		if (mode->mode == VT_AUTO) {
1216256145Sray			vw->vw_smode.mode = VT_AUTO;
1217256145Sray			vw->vw_proc = NULL;
1218256145Sray			vw->vw_pid = 0;
1219256145Sray			DPRINTF(5, "VT_AUTO, ");
1220256145Sray			if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW])
1221256145Sray				cnavailable(vw->vw_terminal->consdev, TRUE);
1222256145Sray			/* were we in the middle of the vty switching process? */
1223256145Sray			if (finish_vt_rel(vw, TRUE, &s) == 0)
1224256145Sray				DPRINTF(5, "reset WAIT_REL, ");
1225256145Sray			if (finish_vt_acq(vw) == 0)
1226256145Sray				DPRINTF(5, "reset WAIT_ACQ, ");
1227256145Sray			return (0);
1228256145Sray		} else if (mode->mode == VT_PROCESS) {
1229256145Sray			if (!ISSIGVALID(mode->relsig) ||
1230256145Sray			    !ISSIGVALID(mode->acqsig) ||
1231256145Sray			    !ISSIGVALID(mode->frsig)) {
1232256145Sray				DPRINTF(5, "error EINVAL\n");
1233256145Sray				return (EINVAL);
1234256145Sray			}
1235256145Sray			DPRINTF(5, "VT_PROCESS %d, ", td->td_proc->p_pid);
1236256145Sray			bcopy(data, &vw->vw_smode, sizeof(struct vt_mode));
1237256145Sray			vw->vw_proc = td->td_proc;
1238256145Sray			vw->vw_pid = vw->vw_proc->p_pid;
1239256145Sray			if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW])
1240256145Sray				cnavailable(vw->vw_terminal->consdev, FALSE);
1241256145Sray		} else {
1242256145Sray			DPRINTF(5, "VT_SETMODE failed, unknown mode %d\n",
1243256145Sray			    mode->mode);
1244256145Sray			return (EINVAL);
1245256145Sray		}
1246256145Sray		DPRINTF(5, "\n");
1247256145Sray		return 0;
1248219888Sed	}
1249219888Sed
1250256145Sray	case VT_GETMODE:	/* get screen switcher mode */
1251256145Sray		bcopy(&vw->vw_smode, data, sizeof(struct vt_mode));
1252256145Sray		return 0;
1253256145Sray
1254256145Sray	case VT_RELDISP:	/* screen switcher ioctl */
1255256145Sray		/*
1256256145Sray		 * This must be the current vty which is in the VT_PROCESS
1257256145Sray		 * switching mode...
1258256145Sray		 */
1259256145Sray		if ((vw != vd->vd_curwindow) || (vw->vw_smode.mode !=
1260256145Sray		    VT_PROCESS)) {
1261256145Sray			return EINVAL;
1262256145Sray		}
1263256145Sray		/* ...and this process is controlling it. */
1264256145Sray		if (vw->vw_proc != td->td_proc) {
1265256145Sray			return EPERM;
1266256145Sray		}
1267256145Sray		error = EINVAL;
1268256145Sray		switch(*(int *)data) {
1269256145Sray		case VT_FALSE:	/* user refuses to release screen, abort */
1270256145Sray			if ((error = finish_vt_rel(vw, FALSE, &s)) == 0)
1271256145Sray				DPRINTF(5, "%s%d: VT_RELDISP: VT_FALSE\n", SC_DRIVER_NAME,
1272256145Sray				    VT_UNIT(vw));
1273256145Sray			break;
1274256145Sray		case VT_TRUE:	/* user has released screen, go on */
1275256145Sray			/* finish_vt_rel(..., TRUE, ...) should not be locked */
1276256145Sray			if (vw->vw_flags & VWF_SWWAIT_REL) {
1277256145Sray				if ((error = finish_vt_rel(vw, TRUE, &s)) == 0)
1278256145Sray					DPRINTF(5, "%s%d: VT_RELDISP: VT_TRUE\n",
1279256145Sray					    SC_DRIVER_NAME, VT_UNIT(vw));
1280256145Sray			} else {
1281256145Sray				error = EINVAL;
1282256145Sray			}
1283256145Sray			return (error);
1284256145Sray		case VT_ACKACQ:	/* acquire acknowledged, switch completed */
1285256145Sray			if ((error = finish_vt_acq(vw)) == 0)
1286256145Sray				DPRINTF(5, "%s%d: VT_RELDISP: VT_ACKACQ\n", SC_DRIVER_NAME,
1287256145Sray				    VT_UNIT(vw));
1288256145Sray			break;
1289256145Sray		default:
1290256145Sray			break;
1291256145Sray		}
1292256145Sray		return error;
1293256145Sray	}
1294256145Sray
1295219888Sed	return (ENOIOCTL);
1296219888Sed}
1297219888Sed
1298219888Sedstatic struct vt_window *
1299219888Sedvt_allocate_window(struct vt_device *vd, unsigned int window)
1300219888Sed{
1301219888Sed	struct vt_window *vw;
1302219888Sed	struct terminal *tm;
1303219888Sed	term_pos_t size;
1304219888Sed	struct winsize wsz;
1305219888Sed
1306219888Sed	vw = malloc(sizeof *vw, M_VT, M_WAITOK|M_ZERO);
1307219888Sed	vw->vw_device = vd;
1308219888Sed	vw->vw_number = window;
1309219888Sed	vw->vw_kbdmode = K_XLATE;
1310219888Sed
1311219888Sed	if (!(vd->vd_flags & VDF_TEXTMODE))
1312219888Sed		vw->vw_font = vtfont_ref(&vt_font_default);
1313256145Sray
1314219888Sed	vt_termsize(vd, vw->vw_font, &size);
1315219888Sed	vt_winsize(vd, vw->vw_font, &wsz);
1316219888Sed	vtbuf_init(&vw->vw_buf, &size);
1317219888Sed
1318219888Sed	tm = vw->vw_terminal = terminal_alloc(&vt_termclass, vw);
1319219888Sed	terminal_set_winsize(tm, &wsz);
1320219888Sed	vd->vd_windows[window] = vw;
1321256145Sray	callout_init(&vw->vw_proc_dead_timer, 0);
1322219888Sed
1323219888Sed	return (vw);
1324219888Sed}
1325219888Sed
1326219888Sedvoid
1327219888Sedvt_upgrade(struct vt_device *vd)
1328219888Sed{
1329219888Sed	struct vt_window *vw;
1330219888Sed	unsigned int i;
1331219888Sed
1332256145Sray	/* Device didn't pass vd_init() or already upgraded. */
1333256145Sray	if (vd->vd_flags & (VDF_ASYNC|VDF_DEAD))
1334219888Sed		return;
1335256145Sray	vd->vd_flags |= VDF_ASYNC;
1336219888Sed
1337219888Sed	mtx_init(&vd->vd_lock, "vtdev", NULL, MTX_DEF);
1338219888Sed	cv_init(&vd->vd_winswitch, "vtwswt");
1339219888Sed
1340256145Sray	/* Init 25 Hz timer. */
1341219888Sed	callout_init_mtx(&vd->vd_timer, &vd->vd_lock, 0);
1342219888Sed
1343219888Sed	for (i = 0; i < VT_MAXWINDOWS; i++) {
1344219888Sed		vw = vd->vd_windows[i];
1345219888Sed		if (vw == NULL) {
1346219888Sed			/* New window. */
1347219888Sed			vw = vt_allocate_window(vd, i);
1348256145Sray		}
1349256145Sray		if (i == VT_CONSWINDOW) {
1350219888Sed			/* Console window. */
1351219888Sed			EVENTHANDLER_REGISTER(shutdown_pre_sync,
1352219888Sed			    vt_window_switch, vw, SHUTDOWN_PRI_DEFAULT);
1353219888Sed		}
1354219888Sed		terminal_maketty(vw->vw_terminal, "v%r", VT_UNIT(vw));
1355219888Sed	}
1356256145Sray	if (vd->vd_curwindow == NULL)
1357256145Sray		vd->vd_curwindow = vd->vd_windows[VT_CONSWINDOW];
1358219888Sed
1359219888Sed	/* Attach keyboard. */
1360219888Sed	vt_allocate_keyboard(vd);
1361256145Sray	DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard);
1362256145Sray
1363256145Sray	/* Start timer when everything ready. */
1364256145Sray	callout_reset(&vd->vd_timer, hz / VT_TIMERFREQ, vt_timer, vd);
1365219888Sed}
1366219888Sed
1367256145Sraystatic void
1368256145Srayvt_resize(struct vt_device *vd)
1369256145Sray{
1370256145Sray	struct vt_window *vw;
1371256145Sray	int i;
1372256145Sray
1373256145Sray	for (i = 0; i < VT_MAXWINDOWS; i++) {
1374256145Sray		vw = vd->vd_windows[i];
1375256145Sray		/* Resize terminal windows */
1376256145Sray		vt_change_font(vw, vw->vw_font);
1377256145Sray	}
1378256145Sray}
1379256145Sray
1380219888Sedvoid
1381219888Sedvt_allocate(struct vt_driver *drv, void *softc)
1382219888Sed{
1383219888Sed	struct vt_device *vd;
1384219888Sed
1385256145Sray	if (main_vd == NULL) {
1386256145Sray		main_vd = malloc(sizeof *vd, M_VT, M_WAITOK|M_ZERO);
1387256145Sray	} else {
1388256145Sray		/*
1389256145Sray		 * Check if have rights to replace current driver. For example:
1390256145Sray		 * it is bad idea to replace KMS driver with generic VGA one.
1391256145Sray		 */
1392256145Sray		/* Lowest preferred. */
1393256145Sray		if (drv->vd_priority >= main_vd->vd_driver->vd_priority)
1394256145Sray			return;
1395256145Sray	}
1396256145Sray	vd = main_vd;
1397256145Sray
1398256145Sray	/* Stop vd_flash periodic task. */
1399256145Sray	if (vd->vd_curwindow != NULL)
1400256145Sray		callout_drain(&vd->vd_timer);
1401256145Sray
1402219888Sed	vd->vd_driver = drv;
1403219888Sed	vd->vd_softc = softc;
1404219888Sed	vd->vd_driver->vd_init(vd);
1405256145Sray
1406219888Sed	vt_upgrade(vd);
1407256145Sray
1408256145Sray	/* Refill settings with new sizes. */
1409256145Sray	vt_resize(vd);
1410256145Sray
1411256145Sray	if (vd->vd_flags & VDF_SPLASH)
1412256145Sray		vtterm_splash(vd);
1413256145Sray
1414256145Sray	if (vd->vd_curwindow != NULL)
1415256145Sray		callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ);
1416256145Sray
1417256145Sray	termcn_cnregister(vd->vd_windows[VT_CONSWINDOW]->vw_terminal);
1418219888Sed}
1419