vt_core.c revision 257815
1219888Sed/*-
2257547Sray * Copyright (c) 2009, 2013 The FreeBSD Foundation
3219888Sed * All rights reserved.
4219888Sed *
5219888Sed * This software was developed by Ed Schouten under sponsorship from the
6219888Sed * FreeBSD Foundation.
7219888Sed *
8257547Sray * Portions of this software were developed by Oleksandr Rybalko
9257547Sray * under sponsorship from the FreeBSD Foundation.
10257547Sray *
11219888Sed * Redistribution and use in source and binary forms, with or without
12219888Sed * modification, are permitted provided that the following conditions
13219888Sed * are met:
14219888Sed * 1. Redistributions of source code must retain the above copyright
15219888Sed *    notice, this list of conditions and the following disclaimer.
16219888Sed * 2. Redistributions in binary form must reproduce the above copyright
17219888Sed *    notice, this list of conditions and the following disclaimer in the
18219888Sed *    documentation and/or other materials provided with the distribution.
19219888Sed *
20219888Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21219888Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22219888Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23219888Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24219888Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25219888Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26219888Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27219888Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28219888Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29219888Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30219888Sed * SUCH DAMAGE.
31219888Sed */
32219888Sed
33219888Sed#include <sys/cdefs.h>
34219888Sed__FBSDID("$FreeBSD: user/ed/newcons/sys/dev/vt/vt_core.c 257815 2013-11-07 21:08:52Z ray $");
35219888Sed
36219888Sed#include <sys/param.h>
37219888Sed#include <sys/consio.h>
38219888Sed#include <sys/eventhandler.h>
39219888Sed#include <sys/fbio.h>
40219888Sed#include <sys/kbio.h>
41219888Sed#include <sys/kdb.h>
42219888Sed#include <sys/kernel.h>
43219888Sed#include <sys/lock.h>
44219888Sed#include <sys/malloc.h>
45219888Sed#include <sys/mutex.h>
46256145Sray#include <sys/proc.h>
47219888Sed#include <sys/reboot.h>
48219888Sed#include <sys/systm.h>
49219888Sed#include <sys/terminal.h>
50219888Sed
51219888Sed#include <dev/kbd/kbdreg.h>
52219888Sed#include <dev/vt/vt.h>
53219888Sed
54219888Sedstatic tc_bell_t	vtterm_bell;
55219888Sedstatic tc_cursor_t	vtterm_cursor;
56219888Sedstatic tc_putchar_t	vtterm_putchar;
57219888Sedstatic tc_fill_t	vtterm_fill;
58219888Sedstatic tc_copy_t	vtterm_copy;
59219888Sedstatic tc_param_t	vtterm_param;
60219888Sedstatic tc_done_t	vtterm_done;
61219888Sed
62219888Sedstatic tc_cnprobe_t	vtterm_cnprobe;
63219888Sedstatic tc_cngetc_t	vtterm_cngetc;
64219888Sed
65219888Sedstatic tc_opened_t	vtterm_opened;
66219888Sedstatic tc_ioctl_t	vtterm_ioctl;
67219888Sed
68219888Sedconst struct terminal_class vt_termclass = {
69219888Sed	.tc_bell	= vtterm_bell,
70219888Sed	.tc_cursor	= vtterm_cursor,
71219888Sed	.tc_putchar	= vtterm_putchar,
72219888Sed	.tc_fill	= vtterm_fill,
73219888Sed	.tc_copy	= vtterm_copy,
74219888Sed	.tc_param	= vtterm_param,
75219888Sed	.tc_done	= vtterm_done,
76219888Sed
77219888Sed	.tc_cnprobe	= vtterm_cnprobe,
78219888Sed	.tc_cngetc	= vtterm_cngetc,
79219888Sed
80219888Sed	.tc_opened	= vtterm_opened,
81219888Sed	.tc_ioctl	= vtterm_ioctl,
82219888Sed};
83219888Sed
84219888Sed/*
85257296Snwhitehorn * Use a constant timer of 25 Hz to redraw the screen.
86219888Sed *
87219888Sed * XXX: In theory we should only fire up the timer when there is really
88219888Sed * activity. Unfortunately we cannot always start timers. We really
89219888Sed * don't want to process kernel messages synchronously, because it
90219888Sed * really slows down the system.
91219888Sed */
92257296Snwhitehorn#define	VT_TIMERFREQ	25
93219888Sed
94219888Sed/* Bell pitch/duration. */
95219888Sed#define VT_BELLDURATION	((5 * hz + 99) / 100)
96219888Sed#define VT_BELLPITCH	800
97219888Sed
98219888Sed#define	VT_LOCK(vd)	mtx_lock(&(vd)->vd_lock)
99219888Sed#define	VT_UNLOCK(vd)	mtx_unlock(&(vd)->vd_lock)
100219888Sed
101219888Sed#define	VT_UNIT(vw)	((vw)->vw_device->vd_unit * VT_MAXWINDOWS + \
102219888Sed			(vw)->vw_number)
103219888Sed
104256145Sray/* XXX while syscons is here. */
105256145Srayint sc_txtmouse_no_retrace_wait;
106256145Sray
107256145Sraystatic SYSCTL_NODE(_kern, OID_AUTO, vt, CTLFLAG_RD, 0, "Newcons parameters");
108256145SrayVT_SYSCTL_INT(debug, 0, "Newcons debug level");
109256145SrayVT_SYSCTL_INT(deadtimer, 15, "Time to wait busy process in VT_PROCESS mode");
110256145Sray
111219888Sedstatic unsigned int vt_unit = 0;
112219888Sedstatic MALLOC_DEFINE(M_VT, "vt", "vt device");
113256145Sraystruct vt_device *main_vd = NULL;
114219888Sed
115219888Sed/* Boot logo. */
116219888Sedextern unsigned int vt_logo_width;
117219888Sedextern unsigned int vt_logo_height;
118219888Sedextern unsigned int vt_logo_depth;
119219888Sedextern unsigned char vt_logo_image[];
120219888Sed
121219888Sed/* Font. */
122219888Sedextern struct vt_font vt_font_default;
123219888Sed
124256145Sraystatic int signal_vt_rel(struct vt_window *);
125256145Sraystatic int signal_vt_acq(struct vt_window *);
126256145Sraystatic int finish_vt_rel(struct vt_window *, int, int *);
127256145Sraystatic int finish_vt_acq(struct vt_window *);
128256145Sraystatic int vt_window_switch(struct vt_window *);
129256145Sraystatic int vt_late_window_switch(struct vt_window *);
130256145Sraystatic int vt_proc_alive(struct vt_window *);
131256145Sraystatic void vt_resize(struct vt_device *);
132256145Sray
133219888Sedstatic void
134256145Srayvt_switch_timer(void *arg)
135256145Sray{
136256145Sray
137256145Sray	vt_late_window_switch((struct vt_window *)arg);
138256145Sray}
139256145Sray
140256145Sraystatic int
141256145Srayvt_window_preswitch(struct vt_window *vw, struct vt_window *curvw)
142256145Sray{
143256145Sray
144256145Sray	DPRINTF(40, "%s\n", __func__);
145256145Sray	curvw->vw_switch_to = vw;
146256145Sray	/* Set timer to allow switch in case when process hang. */
147256145Sray	callout_reset(&vw->vw_proc_dead_timer, hz * vt_deadtimer,
148256145Sray	    vt_switch_timer, (void *)vw);
149256145Sray	/* Notify process about vt switch attempt. */
150256145Sray	DPRINTF(30, "%s: Notify process.\n", __func__);
151256145Sray	signal_vt_rel(curvw);
152256145Sray
153256145Sray	return (0);
154256145Sray}
155256145Sray
156256145Sraystatic int
157256145Srayvt_window_postswitch(struct vt_window *vw)
158256145Sray{
159256145Sray
160256145Sray	signal_vt_acq(vw);
161256145Sray	return (0);
162256145Sray}
163256145Sray
164256145Sray/* vt_late_window_switch will done VT switching for regular case. */
165256145Sraystatic int
166256145Srayvt_late_window_switch(struct vt_window *vw)
167256145Sray{
168256145Sray	int ret;
169256145Sray
170256145Sray	callout_stop(&vw->vw_proc_dead_timer);
171256145Sray
172256145Sray	ret = vt_window_switch(vw);
173256145Sray	if (ret)
174256145Sray		return (ret);
175256145Sray
176256145Sray	/* Notify owner process about terminal availability. */
177256145Sray	if (vw->vw_smode.mode == VT_PROCESS) {
178256145Sray		ret = vt_window_postswitch(vw);
179256145Sray	}
180256145Sray	return (ret);
181256145Sray}
182256145Sray
183256145Sray/* Switch window. */
184256145Sraystatic int
185256145Srayvt_proc_window_switch(struct vt_window *vw)
186256145Sray{
187256145Sray	struct vt_window *curvw;
188256145Sray	struct vt_device *vd;
189256145Sray	int ret;
190256145Sray
191256145Sray	if (vw->vw_flags & VWF_VTYLOCK)
192256145Sray		return (EBUSY);
193256145Sray
194256145Sray	vd = vw->vw_device;
195256145Sray	curvw = vd->vd_curwindow;
196256145Sray
197256145Sray	/* Ask current process permitions to switch away. */
198256145Sray	if (curvw->vw_smode.mode == VT_PROCESS) {
199256145Sray		DPRINTF(30, "%s: VT_PROCESS ", __func__);
200256145Sray		if (vt_proc_alive(curvw) == FALSE) {
201256145Sray			DPRINTF(30, "Dead. Cleaning.");
202256145Sray			/* Dead */
203256145Sray		} else {
204256145Sray			DPRINTF(30, "%s: Signaling process.\n", __func__);
205256145Sray			/* Alive, try to ask him. */
206256145Sray			ret = vt_window_preswitch(vw, curvw);
207256145Sray			/* Wait for process answer or timeout. */
208256145Sray			return (ret);
209256145Sray		}
210256145Sray		DPRINTF(30, "\n");
211256145Sray	}
212256145Sray
213256145Sray	ret = vt_late_window_switch(vw);
214256145Sray	return (ret);
215256145Sray}
216256145Sray
217256145Sray/* Switch window ignoring process locking. */
218256145Sraystatic int
219219888Sedvt_window_switch(struct vt_window *vw)
220219888Sed{
221219888Sed	struct vt_device *vd = vw->vw_device;
222256145Sray	struct vt_window *curvw = vd->vd_curwindow;
223219888Sed	keyboard_t *kbd;
224219888Sed
225219888Sed	VT_LOCK(vd);
226256145Sray	if (curvw == vw) {
227256145Sray		/* Nothing to do. */
228219888Sed		VT_UNLOCK(vd);
229256145Sray		return (0);
230219888Sed	}
231256145Sray	if (!(vw->vw_flags & (VWF_OPENED|VWF_CONSOLE))) {
232256145Sray		VT_UNLOCK(vd);
233256145Sray		return (EINVAL);
234256145Sray	}
235256145Sray
236219888Sed	vd->vd_curwindow = vw;
237219888Sed	vd->vd_flags |= VDF_INVALID;
238219888Sed	cv_broadcast(&vd->vd_winswitch);
239219888Sed	VT_UNLOCK(vd);
240219888Sed
241256145Sray	if (vd->vd_driver->vd_postswitch)
242256145Sray		vd->vd_driver->vd_postswitch(vd);
243256145Sray
244219888Sed	/* Restore per-window keyboard mode. */
245219888Sed	mtx_lock(&Giant);
246219888Sed	kbd = kbd_get_keyboard(vd->vd_keyboard);
247256145Sray	if (kbd != NULL) {
248219888Sed		kbdd_ioctl(kbd, KDSKBMODE, (void *)&vw->vw_kbdmode);
249256145Sray	}
250219888Sed	mtx_unlock(&Giant);
251256145Sray	DPRINTF(10, "%s(ttyv%d) done\n", __func__, vw->vw_number);
252256145Sray
253256145Sray	return (0);
254219888Sed}
255219888Sed
256219888Sedstatic inline void
257219888Sedvt_termsize(struct vt_device *vd, struct vt_font *vf, term_pos_t *size)
258219888Sed{
259219888Sed
260219888Sed	size->tp_row = vd->vd_height;
261219888Sed	size->tp_col = vd->vd_width;
262219888Sed	if (vf != NULL) {
263219888Sed		size->tp_row /= vf->vf_height;
264219888Sed		size->tp_col /= vf->vf_width;
265219888Sed	}
266219888Sed}
267219888Sed
268219888Sedstatic inline void
269219888Sedvt_winsize(struct vt_device *vd, struct vt_font *vf, struct winsize *size)
270219888Sed{
271219888Sed
272219888Sed	size->ws_row = size->ws_ypixel = vd->vd_height;
273219888Sed	size->ws_col = size->ws_xpixel = vd->vd_width;
274219888Sed	if (vf != NULL) {
275219888Sed		size->ws_row /= vf->vf_height;
276219888Sed		size->ws_col /= vf->vf_width;
277219888Sed	}
278219888Sed}
279219888Sed
280257076Sraystatic void
281257076Srayvt_scroll(struct vt_window *vw, int offset, int whence)
282257076Sray{
283257076Sray	int diff;
284257076Sray	term_pos_t size;
285257076Sray
286257076Sray	if ((vw->vw_flags & VWF_SCROLL) == 0)
287257076Sray		return;
288257076Sray
289257076Sray	vt_termsize(vw->vw_device, vw->vw_font, &size);
290257076Sray
291257076Sray	diff = vthistory_seek(&vw->vw_buf, offset, whence);
292257076Sray	/*
293257076Sray	 * Offset changed, please update Nth lines on sceen.
294257076Sray	 * +N - Nth lines at top;
295257076Sray	 * -N - Nth lines at bottom.
296257076Sray	 */
297257076Sray
298257076Sray	if (diff < -size.tp_row || diff > size.tp_row) {
299257076Sray		vw->vw_device->vd_flags |= VDF_INVALID;
300257076Sray		return;
301257076Sray	}
302257076Sray	vw->vw_device->vd_flags |= VDF_INVALID; /*XXX*/
303257076Sray}
304257076Sray
305219888Sedstatic int
306257076Srayvt_machine_kbdevent(int c)
307257076Sray{
308257076Sray
309257076Sray	switch (c) {
310257076Sray	case SPCLKEY | DBG:
311257076Sray		kdb_enter(KDB_WHY_BREAK, "manual escape to debugger");
312257076Sray		return (1);
313257076Sray	case SPCLKEY | RBT:
314257076Sray		/* XXX: Make this configurable! */
315257076Sray		shutdown_nice(0);
316257076Sray		return (1);
317257076Sray	case SPCLKEY | HALT:
318257076Sray		shutdown_nice(RB_HALT);
319257076Sray		return (1);
320257076Sray	case SPCLKEY | PDWN:
321257076Sray		shutdown_nice(RB_HALT|RB_POWEROFF);
322257076Sray		return (1);
323257076Sray	};
324257076Sray
325257076Sray	return (0);
326257076Sray}
327257076Sray
328257076Sraystatic void
329257076Srayvt_scrollmode_kbdevent(struct vt_window *vw, int c, int console)
330257076Sray{
331257076Sray	struct vt_device *vd;
332257076Sray	term_pos_t size;
333257076Sray
334257076Sray	vd = vw->vw_device;
335257076Sray	/* Only special keys handled in ScrollLock mode */
336257076Sray	if ((c & SPCLKEY) == 0)
337257076Sray		return;
338257076Sray
339257076Sray	c &= ~SPCLKEY;
340257076Sray
341257076Sray	if (console == 0) {
342257076Sray		if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) {
343257076Sray			vw = vd->vd_windows[c - F_SCR];
344257076Sray			if (vw != NULL)
345257076Sray				vt_proc_window_switch(vw);
346257076Sray			return;
347257076Sray		}
348257076Sray		VT_LOCK(vd);
349257076Sray	}
350257076Sray
351257076Sray	switch (c) {
352257076Sray	case SLK: {
353257076Sray		/* Turn scrolling off. */
354257076Sray		vt_scroll(vw, 0, VHS_END);
355257076Sray		VTBUF_SLCK_DISABLE(&vw->vw_buf);
356257076Sray		vw->vw_flags &= ~VWF_SCROLL;
357257076Sray		break;
358257076Sray	}
359257076Sray	case FKEY | F(49): /* Home key. */
360257076Sray		vt_scroll(vw, 0, VHS_END);
361257076Sray		break;
362257076Sray	case FKEY | F(50): /* Arrow up. */
363257076Sray		vt_scroll(vw, -1, VHS_CUR);
364257076Sray		break;
365257076Sray	case FKEY | F(51): /* Page up. */
366257076Sray		vt_termsize(vd, vw->vw_font, &size);
367257076Sray		vt_scroll(vw, -size.tp_row, VHS_CUR);
368257076Sray		break;
369257076Sray	case FKEY | F(57): /* End key. */
370257076Sray		vt_scroll(vw, 0, VHS_SET);
371257076Sray		break;
372257076Sray	case FKEY | F(58): /* Arrow down. */
373257076Sray		vt_scroll(vw, 1, VHS_CUR);
374257076Sray		break;
375257076Sray	case FKEY | F(59): /* Page down. */
376257076Sray		vt_termsize(vd, vw->vw_font, &size);
377257076Sray		vt_scroll(vw, size.tp_row, VHS_CUR);
378257076Sray		break;
379257076Sray	}
380257076Sray
381257076Sray	if (console == 0)
382257076Sray		VT_UNLOCK(vd);
383257076Sray}
384257076Sray
385257076Sraystatic int
386257294Snwhitehornvt_processkey(keyboard_t *kbd, struct vt_device *vd, int c)
387219888Sed{
388219888Sed	struct vt_window *vw = vd->vd_curwindow;
389257294Snwhitehorn	int state = 0;
390219888Sed
391219888Sed	if (c & RELKEY)
392219888Sed		return (0);
393219888Sed
394257076Sray	if (vt_machine_kbdevent(c))
395257076Sray		return (0);
396257076Sray
397257076Sray	if (vw->vw_flags & VWF_SCROLL) {
398257076Sray		vt_scrollmode_kbdevent(vw, c, 0/* Not a console */);
399257076Sray		/* Scroll mode keys handled, nothing to do more. */
400257076Sray		return (0);
401257076Sray	}
402257076Sray
403219888Sed	if (c & SPCLKEY) {
404219888Sed		c &= ~SPCLKEY;
405219888Sed
406219888Sed		if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) {
407219888Sed			vw = vd->vd_windows[c - F_SCR];
408219888Sed			if (vw != NULL)
409256145Sray				vt_proc_window_switch(vw);
410219888Sed			return (0);
411219888Sed		}
412219888Sed
413219888Sed		switch (c) {
414219888Sed		case SLK: {
415219888Sed
416219888Sed			kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state);
417219888Sed			VT_LOCK(vd);
418219888Sed			if (state & SLKED) {
419219888Sed				/* Turn scrolling on. */
420219888Sed				vw->vw_flags |= VWF_SCROLL;
421256145Sray				VTBUF_SLCK_ENABLE(&vw->vw_buf);
422219888Sed			} else {
423219888Sed				/* Turn scrolling off. */
424219888Sed				vw->vw_flags &= ~VWF_SCROLL;
425256145Sray				VTBUF_SLCK_DISABLE(&vw->vw_buf);
426257076Sray				vt_scroll(vw, 0, VHS_END);
427219888Sed			}
428219888Sed			VT_UNLOCK(vd);
429219888Sed			break;
430219888Sed		}
431219888Sed		case FKEY | F(1):  case FKEY | F(2):  case FKEY | F(3):
432219888Sed		case FKEY | F(4):  case FKEY | F(5):  case FKEY | F(6):
433219888Sed		case FKEY | F(7):  case FKEY | F(8):  case FKEY | F(9):
434219888Sed		case FKEY | F(10): case FKEY | F(11): case FKEY | F(12):
435219888Sed			/* F1 through F12 keys. */
436219888Sed			terminal_input_special(vw->vw_terminal,
437219888Sed			    TKEY_F1 + c - (FKEY | F(1)));
438219888Sed			break;
439219888Sed		case FKEY | F(49): /* Home key. */
440219888Sed			terminal_input_special(vw->vw_terminal, TKEY_HOME);
441219888Sed			break;
442219888Sed		case FKEY | F(50): /* Arrow up. */
443219888Sed			terminal_input_special(vw->vw_terminal, TKEY_UP);
444219888Sed			break;
445219888Sed		case FKEY | F(51): /* Page up. */
446219888Sed			terminal_input_special(vw->vw_terminal, TKEY_PAGE_UP);
447219888Sed			break;
448219888Sed		case FKEY | F(53): /* Arrow left. */
449219888Sed			terminal_input_special(vw->vw_terminal, TKEY_LEFT);
450219888Sed			break;
451219888Sed		case FKEY | F(55): /* Arrow right. */
452219888Sed			terminal_input_special(vw->vw_terminal, TKEY_RIGHT);
453219888Sed			break;
454219888Sed		case FKEY | F(57): /* End key. */
455219888Sed			terminal_input_special(vw->vw_terminal, TKEY_END);
456219888Sed			break;
457219888Sed		case FKEY | F(58): /* Arrow down. */
458219888Sed			terminal_input_special(vw->vw_terminal, TKEY_DOWN);
459219888Sed			break;
460219888Sed		case FKEY | F(59): /* Page down. */
461219888Sed			terminal_input_special(vw->vw_terminal, TKEY_PAGE_DOWN);
462219888Sed			break;
463219888Sed		case FKEY | F(60): /* Insert key. */
464219888Sed			terminal_input_special(vw->vw_terminal, TKEY_INSERT);
465219888Sed			break;
466219888Sed		case FKEY | F(61): /* Delete key. */
467219888Sed			terminal_input_special(vw->vw_terminal, TKEY_DELETE);
468219888Sed			break;
469219888Sed		}
470219888Sed	} else if (KEYFLAGS(c) == 0) {
471219888Sed		/* Don't do UTF-8 conversion when doing raw mode. */
472219888Sed		if (vw->vw_kbdmode == K_XLATE)
473256145Sray			terminal_input_char(vw->vw_terminal, KEYCHAR(c));
474219888Sed		else
475219888Sed			terminal_input_raw(vw->vw_terminal, c);
476219888Sed	}
477219888Sed	return (0);
478219888Sed}
479219888Sed
480219888Sedstatic int
481257294Snwhitehornvt_kbdevent(keyboard_t *kbd, int event, void *arg)
482257294Snwhitehorn{
483257294Snwhitehorn	struct vt_device *vd = arg;
484257294Snwhitehorn	int c;
485257294Snwhitehorn
486257294Snwhitehorn	switch (event) {
487257294Snwhitehorn	case KBDIO_KEYINPUT:
488257294Snwhitehorn		break;
489257294Snwhitehorn	case KBDIO_UNLOADING:
490257294Snwhitehorn		mtx_lock(&Giant);
491257294Snwhitehorn		vd->vd_keyboard = -1;
492257294Snwhitehorn		kbd_release(kbd, (void *)&vd->vd_keyboard);
493257294Snwhitehorn		mtx_unlock(&Giant);
494257294Snwhitehorn		return (0);
495257294Snwhitehorn	default:
496257294Snwhitehorn		return (EINVAL);
497257294Snwhitehorn	}
498257294Snwhitehorn
499257294Snwhitehorn	while ((c = kbdd_read_char(kbd, 0)) != NOKEY)
500257294Snwhitehorn		vt_processkey(kbd, vd, c);
501257294Snwhitehorn
502257294Snwhitehorn	return (0);
503257294Snwhitehorn}
504257294Snwhitehorn
505257294Snwhitehornstatic int
506219888Sedvt_allocate_keyboard(struct vt_device *vd)
507219888Sed{
508219888Sed	int		 idx0, idx;
509219888Sed	keyboard_t	*k0, *k;
510219888Sed	keyboard_info_t	 ki;
511219888Sed
512219888Sed	idx0 = kbd_allocate("kbdmux", -1, (void *)&vd->vd_keyboard,
513219888Sed	    vt_kbdevent, vd);
514256145Sray	/* XXX: kb_token lost */
515256145Sray	vd->vd_keyboard = idx0;
516219888Sed	if (idx0 != -1) {
517256145Sray		DPRINTF(20, "%s: kbdmux allocated, idx = %d\n", __func__, idx0);
518219888Sed		k0 = kbd_get_keyboard(idx0);
519219888Sed
520219888Sed		for (idx = kbd_find_keyboard2("*", -1, 0);
521219888Sed		     idx != -1;
522219888Sed		     idx = kbd_find_keyboard2("*", -1, idx + 1)) {
523219888Sed			k = kbd_get_keyboard(idx);
524219888Sed
525219888Sed			if (idx == idx0 || KBD_IS_BUSY(k))
526219888Sed				continue;
527219888Sed
528219888Sed			bzero(&ki, sizeof(ki));
529219888Sed			strcpy(ki.kb_name, k->kb_name);
530219888Sed			ki.kb_unit = k->kb_unit;
531219888Sed
532219888Sed			kbdd_ioctl(k0, KBADDKBD, (caddr_t) &ki);
533219888Sed		}
534256145Sray	} else {
535256145Sray		DPRINTF(20, "%s: no kbdmux allocated\n", __func__);
536219888Sed		idx0 = kbd_allocate("*", -1, (void *)&vd->vd_keyboard,
537219888Sed		    vt_kbdevent, vd);
538256145Sray	}
539256145Sray	DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard);
540219888Sed
541219888Sed	return (idx0);
542219888Sed}
543219888Sed
544219888Sedstatic void
545219888Sedvtterm_bell(struct terminal *tm)
546219888Sed{
547219888Sed
548219888Sed	sysbeep(1193182 / VT_BELLPITCH, VT_BELLDURATION);
549219888Sed}
550219888Sed
551219888Sedstatic void
552219888Sedvtterm_cursor(struct terminal *tm, const term_pos_t *p)
553219888Sed{
554219888Sed	struct vt_window *vw = tm->tm_softc;
555219888Sed
556219888Sed	vtbuf_cursor_position(&vw->vw_buf, p);
557219888Sed}
558219888Sed
559219888Sedstatic void
560219888Sedvtterm_putchar(struct terminal *tm, const term_pos_t *p, term_char_t c)
561219888Sed{
562219888Sed	struct vt_window *vw = tm->tm_softc;
563219888Sed
564219888Sed	vtbuf_putchar(&vw->vw_buf, p, c);
565219888Sed}
566219888Sed
567219888Sedstatic void
568219888Sedvtterm_fill(struct terminal *tm, const term_rect_t *r, term_char_t c)
569219888Sed{
570219888Sed	struct vt_window *vw = tm->tm_softc;
571219888Sed
572256145Sray	vtbuf_fill_locked(&vw->vw_buf, r, c);
573219888Sed}
574219888Sed
575219888Sedstatic void
576219888Sedvtterm_copy(struct terminal *tm, const term_rect_t *r,
577219888Sed    const term_pos_t *p)
578219888Sed{
579219888Sed	struct vt_window *vw = tm->tm_softc;
580219888Sed
581219888Sed	vtbuf_copy(&vw->vw_buf, r, p);
582219888Sed}
583219888Sed
584219888Sedstatic void
585219888Sedvtterm_param(struct terminal *tm, int cmd, unsigned int arg)
586219888Sed{
587219888Sed	struct vt_window *vw = tm->tm_softc;
588219888Sed
589219888Sed	switch (cmd) {
590219888Sed	case TP_SHOWCURSOR:
591219888Sed		vtbuf_cursor_visibility(&vw->vw_buf, arg);
592219888Sed		break;
593219888Sed	}
594219888Sed}
595219888Sed
596219888Sedstatic inline void
597219888Sedvt_determine_colors(term_char_t c, int cursor,
598219888Sed    term_color_t *fg, term_color_t *bg)
599219888Sed{
600219888Sed
601219888Sed	*fg = TCHAR_FGCOLOR(c);
602219888Sed	if (TCHAR_FORMAT(c) & TF_BOLD)
603219888Sed		*fg = TCOLOR_LIGHT(*fg);
604219888Sed	*bg = TCHAR_BGCOLOR(c);
605219888Sed
606219888Sed	if (TCHAR_FORMAT(c) & TF_REVERSE) {
607219888Sed		term_color_t tmp;
608219888Sed
609219888Sed		tmp = *fg;
610219888Sed		*fg = *bg;
611219888Sed		*bg = tmp;
612219888Sed	}
613219888Sed
614219888Sed	if (cursor) {
615219888Sed		*fg = *bg;
616219888Sed		*bg = TC_WHITE;
617219888Sed	}
618219888Sed}
619219888Sed
620219888Sedstatic void
621219888Sedvt_bitblt_char(struct vt_device *vd, struct vt_font *vf, term_char_t c,
622219888Sed    int iscursor, unsigned int row, unsigned int col)
623219888Sed{
624219888Sed	term_color_t fg, bg;
625219888Sed
626219888Sed	vt_determine_colors(c, iscursor, &fg, &bg);
627219888Sed
628219888Sed	if (vf != NULL) {
629219888Sed		const uint8_t *src;
630219888Sed		vt_axis_t top, left;
631219888Sed
632219888Sed		src = vtfont_lookup(vf, c);
633219888Sed
634219888Sed		/*
635219888Sed		 * Align the terminal to the centre of the screen.
636219888Sed		 * Fonts may not always be able to fill the entire
637219888Sed		 * screen.
638219888Sed		 */
639219888Sed		top = row * vf->vf_height +
640219888Sed		    (vd->vd_height % vf->vf_height) / 2;
641219888Sed		left = col * vf->vf_width +
642219888Sed		    (vd->vd_width % vf->vf_width) / 2;
643219888Sed
644256527Sray		vd->vd_driver->vd_bitbltchr(vd, src, top, left,
645219888Sed		    vf->vf_width, vf->vf_height, fg, bg);
646219888Sed	} else {
647219888Sed		vd->vd_driver->vd_putchar(vd, TCHAR_CHARACTER(c),
648219888Sed		    row, col, fg, bg);
649219888Sed	}
650219888Sed}
651219888Sed
652219888Sedstatic void
653219888Sedvt_flush(struct vt_device *vd)
654219888Sed{
655219888Sed	struct vt_window *vw = vd->vd_curwindow;
656219888Sed	struct vt_font *vf = vw->vw_font;
657219888Sed	term_pos_t size;
658219888Sed	term_rect_t tarea;
659219888Sed	struct vt_bufmask tmask;
660256145Sray	unsigned int row, col;
661256145Sray	term_char_t *r;
662219888Sed
663219888Sed	if (vd->vd_flags & VDF_SPLASH || vw->vw_flags & VWF_BUSY)
664219888Sed		return;
665219888Sed
666219888Sed	vtbuf_undirty(&vw->vw_buf, &tarea, &tmask);
667219888Sed	vt_termsize(vd, vf, &size);
668219888Sed
669219888Sed	/* Force a full redraw when the screen contents are invalid. */
670256145Sray	if (vd->vd_flags & VDF_INVALID) {
671219888Sed		tarea.tr_begin.tp_row = tarea.tr_begin.tp_col = 0;
672219888Sed		tarea.tr_end = size;
673219888Sed		tmask.vbm_row = tmask.vbm_col = VBM_DIRTY;
674219888Sed
675219888Sed		/*
676219888Sed		 * Blank to prevent borders with artifacts.  This is
677219888Sed		 * only required when the font doesn't exactly fill the
678219888Sed		 * screen.
679219888Sed		 */
680219888Sed		if (vd->vd_flags & VDF_INVALID && vf != NULL &&
681219888Sed		    (vd->vd_width % vf->vf_width != 0 ||
682219888Sed		    vd->vd_height % vf->vf_height != 0))
683219888Sed			vd->vd_driver->vd_blank(vd, TC_BLACK);
684219888Sed
685219888Sed		vd->vd_flags &= ~VDF_INVALID;
686219888Sed	}
687219888Sed
688219888Sed
689219888Sed	for (row = tarea.tr_begin.tp_row; row < tarea.tr_end.tp_row; row++) {
690219888Sed		if (!VTBUF_DIRTYROW(&tmask, row))
691219888Sed			continue;
692256145Sray		r = VTBUF_GET_ROW(&vw->vw_buf, row);
693219888Sed		for (col = tarea.tr_begin.tp_col;
694219888Sed		    col < tarea.tr_end.tp_col; col++) {
695219888Sed			if (!VTBUF_DIRTYCOL(&tmask, col))
696219888Sed				continue;
697219888Sed
698256145Sray			vt_bitblt_char(vd, vf, r[col],
699256903Sray			    VTBUF_ISCURSOR(&vw->vw_buf, row, col), row, col);
700219888Sed		}
701219888Sed	}
702219888Sed}
703219888Sed
704219888Sedstatic void
705219888Sedvt_timer(void *arg)
706219888Sed{
707257387Sray	struct vt_device *vd;
708219888Sed
709257387Sray	vd = arg;
710257387Sray	/* Update screen if required. */
711219888Sed	vt_flush(vd);
712257387Sray	/* Schedule for next update. */
713219888Sed	callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ);
714219888Sed}
715219888Sed
716219888Sedstatic void
717219888Sedvtterm_done(struct terminal *tm)
718219888Sed{
719219888Sed	struct vt_window *vw = tm->tm_softc;
720219888Sed	struct vt_device *vd = vw->vw_device;
721219888Sed
722219888Sed	if (kdb_active || panicstr != NULL) {
723219888Sed		/* Switch to the debugger. */
724219888Sed		if (vd->vd_curwindow != vw) {
725219888Sed			vd->vd_curwindow = vw;
726219888Sed			vd->vd_flags |= VDF_INVALID;
727257626Sray			if (vd->vd_driver->vd_postswitch)
728257626Sray				vd->vd_driver->vd_postswitch(vd);
729219888Sed		}
730219888Sed		vd->vd_flags &= ~VDF_SPLASH;
731219888Sed		vt_flush(vd);
732219888Sed	} else if (!(vd->vd_flags & VDF_ASYNC)) {
733219888Sed		vt_flush(vd);
734219888Sed	}
735219888Sed}
736219888Sed
737219888Sedstatic void
738256145Srayvtterm_splash(struct vt_device *vd)
739256145Sray{
740256145Sray	vt_axis_t top, left;
741256145Sray
742256145Sray	/* Display a nice boot splash. */
743257722Sray	if (!(vd->vd_flags & VDF_TEXTMODE) && (boothowto & RB_MUTE)) {
744256145Sray
745256145Sray		top = (vd->vd_height - vt_logo_height) / 2;
746256145Sray		left = (vd->vd_width - vt_logo_width) / 2;
747256145Sray		switch (vt_logo_depth) {
748256145Sray		case 1:
749256145Sray			/* XXX: Unhardcode colors! */
750256527Sray			vd->vd_driver->vd_bitbltchr(vd, vt_logo_image, top, left,
751256145Sray			    vt_logo_width, vt_logo_height, 0xf, 0x0);
752256145Sray		}
753256145Sray		vd->vd_flags |= VDF_SPLASH;
754256145Sray	}
755256145Sray}
756256145Sray
757256145Sraystatic void
758219888Sedvtterm_cnprobe(struct terminal *tm, struct consdev *cp)
759219888Sed{
760219888Sed	struct vt_window *vw = tm->tm_softc;
761219888Sed	struct vt_device *vd = vw->vw_device;
762219888Sed	struct winsize wsz;
763219888Sed
764256145Sray	if (vd->vd_flags & VDF_INITIALIZED)
765256145Sray		/* Initialization already done. */
766256145Sray		return;
767256145Sray
768219888Sed	cp->cn_pri = vd->vd_driver->vd_init(vd);
769219888Sed	if (cp->cn_pri == CN_DEAD) {
770219888Sed		vd->vd_flags |= VDF_DEAD;
771219888Sed		return;
772219888Sed	}
773219888Sed
774230469Snwhitehorn	/* Initialize any early-boot keyboard drivers */
775230469Snwhitehorn	kbd_configure(KB_CONF_PROBE_ONLY);
776230469Snwhitehorn
777219888Sed	vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1);
778256145Sray	vd->vd_windows[VT_CONSWINDOW] = vw;
779219888Sed	sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw));
780219888Sed
781219888Sed	if (!(vd->vd_flags & VDF_TEXTMODE))
782219888Sed		vw->vw_font = vtfont_ref(&vt_font_default);
783219888Sed
784219888Sed	vtbuf_init_early(&vw->vw_buf);
785219888Sed	vt_winsize(vd, vw->vw_font, &wsz);
786219888Sed	terminal_set_winsize(tm, &wsz);
787219888Sed
788256145Sray	vtterm_splash(vd);
789219888Sed
790256145Sray	vd->vd_flags |= VDF_INITIALIZED;
791256145Sray	main_vd = vd;
792219888Sed}
793219888Sed
794219888Sedstatic int
795219888Sedvtterm_cngetc(struct terminal *tm)
796219888Sed{
797219888Sed	struct vt_window *vw = tm->tm_softc;
798219888Sed	struct vt_device *vd = vw->vw_device;
799219888Sed	keyboard_t *kbd;
800257076Sray	int state;
801219888Sed	u_int c;
802219888Sed
803257076Sray	if (vw->vw_kbdsq && *vw->vw_kbdsq)
804257076Sray		return (*vw->vw_kbdsq++);
805257076Sray
806257076Sray	state = 0;
807219888Sed	/* Make sure the splash screen is not there. */
808219888Sed	if (vd->vd_flags & VDF_SPLASH) {
809256145Sray		/* Remove splash */
810219888Sed		vd->vd_flags &= ~VDF_SPLASH;
811256145Sray		/* Mark screen as invalid to force update */
812256145Sray		vd->vd_flags |= VDF_INVALID;
813219888Sed		vt_flush(vd);
814219888Sed	}
815219888Sed
816219888Sed	/* Stripped down keyboard handler. */
817219888Sed	kbd = kbd_get_keyboard(vd->vd_keyboard);
818219888Sed	if (kbd == NULL)
819219888Sed		return (-1);
820219888Sed
821256145Sray	/* Force keyboard input mode to K_XLATE */
822256145Sray	c = K_XLATE;
823256145Sray	kbdd_ioctl(kbd, KDSKBMODE, (void *)&c);
824256145Sray
825219888Sed	/* Switch the keyboard to polling to make it work here. */
826219888Sed	kbdd_poll(kbd, TRUE);
827219888Sed	c = kbdd_read_char(kbd, 0);
828219888Sed	kbdd_poll(kbd, FALSE);
829219888Sed	if (c & RELKEY)
830219888Sed		return (-1);
831256145Sray
832257076Sray	if (vw->vw_flags & VWF_SCROLL) {
833257076Sray		vt_scrollmode_kbdevent(vw, c, 1/* Console mode */);
834257076Sray		vt_flush(vd);
835257076Sray		return (-1);
836257076Sray	}
837257076Sray
838219888Sed	/* Stripped down handling of vt_kbdevent(), without locking, etc. */
839219888Sed	if (c & SPCLKEY) {
840219888Sed		switch (c) {
841257076Sray		case SPCLKEY | SLK:
842219888Sed			kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state);
843219888Sed			if (state & SLKED) {
844219888Sed				/* Turn scrolling on. */
845219888Sed				vw->vw_flags |= VWF_SCROLL;
846256145Sray				VTBUF_SLCK_ENABLE(&vw->vw_buf);
847219888Sed			} else {
848219888Sed				/* Turn scrolling off. */
849257076Sray				vt_scroll(vw, 0, VHS_END);
850219888Sed				vw->vw_flags &= ~VWF_SCROLL;
851256145Sray				VTBUF_SLCK_DISABLE(&vw->vw_buf);
852219888Sed			}
853219888Sed			break;
854257076Sray		/* XXX: KDB can handle history. */
855257076Sray		case SPCLKEY | FKEY | F(50): /* Arrow up. */
856257076Sray			vw->vw_kbdsq = "\x1b[A";
857219888Sed			break;
858257076Sray		case SPCLKEY | FKEY | F(58): /* Arrow down. */
859257076Sray			vw->vw_kbdsq = "\x1b[B";
860219888Sed			break;
861257076Sray		case SPCLKEY | FKEY | F(55): /* Arrow right. */
862257076Sray			vw->vw_kbdsq = "\x1b[C";
863219888Sed			break;
864257076Sray		case SPCLKEY | FKEY | F(53): /* Arrow left. */
865257076Sray			vw->vw_kbdsq = "\x1b[D";
866219888Sed			break;
867219888Sed		}
868219888Sed
869219888Sed		/* Force refresh to make scrollback work. */
870219888Sed		vt_flush(vd);
871219888Sed	} else if (KEYFLAGS(c) == 0) {
872219888Sed		return KEYCHAR(c);
873219888Sed	}
874256145Sray
875257076Sray	if (vw->vw_kbdsq && *vw->vw_kbdsq)
876257076Sray		return (*
877257076Sray		vw->vw_kbdsq++);
878257076Sray
879219888Sed	return (-1);
880219888Sed}
881219888Sed
882219888Sedstatic void
883219888Sedvtterm_opened(struct terminal *tm, int opened)
884219888Sed{
885219888Sed	struct vt_window *vw = tm->tm_softc;
886219888Sed	struct vt_device *vd = vw->vw_device;
887219888Sed
888219888Sed	VT_LOCK(vd);
889219888Sed	vd->vd_flags &= ~VDF_SPLASH;
890219888Sed	if (opened)
891219888Sed		vw->vw_flags |= VWF_OPENED;
892256145Sray	else {
893219888Sed		vw->vw_flags &= ~VWF_OPENED;
894256145Sray		/* TODO: finish ACQ/REL */
895256145Sray	}
896219888Sed	VT_UNLOCK(vd);
897219888Sed}
898219888Sed
899219888Sedstatic int
900219888Sedvt_change_font(struct vt_window *vw, struct vt_font *vf)
901219888Sed{
902219888Sed	struct vt_device *vd = vw->vw_device;
903219888Sed	struct terminal *tm = vw->vw_terminal;
904219888Sed	term_pos_t size;
905219888Sed	struct winsize wsz;
906219888Sed
907219888Sed	/*
908219888Sed	 * Changing fonts.
909219888Sed	 *
910219888Sed	 * Changing fonts is a little tricky.  We must prevent
911219888Sed	 * simultaneous access to the device, so we must stop
912219888Sed	 * the display timer and the terminal from accessing.
913219888Sed	 * We need to switch fonts and grow our screen buffer.
914219888Sed	 *
915219888Sed	 * XXX: Right now the code uses terminal_mute() to
916219888Sed	 * prevent data from reaching the console driver while
917219888Sed	 * resizing the screen buffer.  This isn't elegant...
918219888Sed	 */
919219888Sed
920219888Sed	VT_LOCK(vd);
921219888Sed	if (vw->vw_flags & VWF_BUSY) {
922219888Sed		/* Another process is changing the font. */
923219888Sed		VT_UNLOCK(vd);
924219888Sed		return (EBUSY);
925219888Sed	}
926219888Sed	if (vw->vw_font == NULL) {
927219888Sed		/* Our device doesn't need fonts. */
928219888Sed		VT_UNLOCK(vd);
929219888Sed		return (ENOTTY);
930219888Sed	}
931219888Sed	vw->vw_flags |= VWF_BUSY;
932219888Sed	VT_UNLOCK(vd);
933219888Sed
934219888Sed	vt_termsize(vd, vf, &size);
935219888Sed	vt_winsize(vd, vf, &wsz);
936219888Sed
937219888Sed	/* Grow the screen buffer and terminal. */
938219888Sed	terminal_mute(tm, 1);
939256145Sray	vtbuf_grow(&vw->vw_buf, &size, vw->vw_buf.vb_history_size);
940256903Sray	terminal_set_winsize_blank(tm, &wsz, 0);
941219888Sed	terminal_mute(tm, 0);
942219888Sed
943219888Sed	/* Actually apply the font to the current window. */
944219888Sed	VT_LOCK(vd);
945219888Sed	vtfont_unref(vw->vw_font);
946219888Sed	vw->vw_font = vtfont_ref(vf);
947219888Sed
948219888Sed	/* Force a full redraw the next timer tick. */
949219888Sed	if (vd->vd_curwindow == vw)
950219888Sed		vd->vd_flags |= VDF_INVALID;
951219888Sed	vw->vw_flags &= ~VWF_BUSY;
952219888Sed	VT_UNLOCK(vd);
953219888Sed	return (0);
954219888Sed}
955219888Sed
956219888Sedstatic int
957256145Srayvt_proc_alive(struct vt_window *vw)
958256145Sray{
959256145Sray	struct proc *p;
960256145Sray
961256145Sray	if (vw->vw_smode.mode != VT_PROCESS)
962256145Sray		return FALSE;
963256145Sray
964256145Sray	if (vw->vw_proc) {
965256145Sray		if ((p = pfind(vw->vw_pid)) != NULL)
966256145Sray			PROC_UNLOCK(p);
967256145Sray		if (vw->vw_proc == p)
968256145Sray			return TRUE;
969256145Sray		vw->vw_proc = NULL;
970256145Sray		vw->vw_smode.mode = VT_AUTO;
971256145Sray		DPRINTF(1, "vt controlling process %d died\n", vw->vw_pid);
972256145Sray		vw->vw_pid = 0;
973256145Sray	}
974256145Sray	return FALSE;
975256145Sray}
976256145Sray
977256145Sraystatic int
978256145Sraysignal_vt_rel(struct vt_window *vw)
979256145Sray{
980257815Sray
981256145Sray	if (vw->vw_smode.mode != VT_PROCESS)
982256145Sray		return FALSE;
983256145Sray	if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) {
984256145Sray		vw->vw_proc = NULL;
985256145Sray		vw->vw_pid = 0;
986256145Sray		return TRUE;
987256145Sray	}
988256145Sray	vw->vw_flags |= VWF_SWWAIT_REL;
989256145Sray	PROC_LOCK(vw->vw_proc);
990256145Sray	kern_psignal(vw->vw_proc, vw->vw_smode.relsig);
991256145Sray	PROC_UNLOCK(vw->vw_proc);
992256145Sray	DPRINTF(1, "sending relsig to %d\n", vw->vw_pid);
993256145Sray	return TRUE;
994256145Sray}
995256145Sray
996256145Sraystatic int
997256145Sraysignal_vt_acq(struct vt_window *vw)
998256145Sray{
999257815Sray
1000256145Sray	if (vw->vw_smode.mode != VT_PROCESS)
1001256145Sray		return FALSE;
1002256145Sray	if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW])
1003256145Sray		cnavailable(vw->vw_terminal->consdev, FALSE);
1004256145Sray	if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) {
1005256145Sray		vw->vw_proc = NULL;
1006256145Sray		vw->vw_pid = 0;
1007256145Sray		return TRUE;
1008256145Sray	}
1009256145Sray	vw->vw_flags |= VWF_SWWAIT_ACQ;
1010256145Sray	PROC_LOCK(vw->vw_proc);
1011256145Sray	kern_psignal(vw->vw_proc, vw->vw_smode.acqsig);
1012256145Sray	PROC_UNLOCK(vw->vw_proc);
1013256145Sray	DPRINTF(1, "sending acqsig to %d\n", vw->vw_pid);
1014256145Sray	return TRUE;
1015256145Sray}
1016256145Sray
1017256145Sraystatic int
1018256145Srayfinish_vt_rel(struct vt_window *vw, int release, int *s)
1019256145Sray{
1020257815Sray
1021256145Sray	if (vw->vw_flags & VWF_SWWAIT_REL) {
1022256145Sray		vw->vw_flags &= ~VWF_SWWAIT_REL;
1023256145Sray		if (release) {
1024256145Sray			callout_drain(&vw->vw_proc_dead_timer);
1025256145Sray			vt_late_window_switch(vw->vw_switch_to);
1026256145Sray		}
1027256145Sray		return 0;
1028256145Sray	}
1029256145Sray	return EINVAL;
1030256145Sray}
1031256145Sray
1032256145Sraystatic int
1033256145Srayfinish_vt_acq(struct vt_window *vw)
1034256145Sray{
1035257815Sray
1036256145Sray	if (vw->vw_flags & VWF_SWWAIT_ACQ) {
1037256145Sray		vw->vw_flags &= ~VWF_SWWAIT_ACQ;
1038256145Sray		return 0;
1039256145Sray	}
1040256145Sray	return EINVAL;
1041256145Sray}
1042256145Sray
1043256145Sraystatic int
1044219888Sedvtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data,
1045219888Sed    struct thread *td)
1046219888Sed{
1047219888Sed	struct vt_window *vw = tm->tm_softc;
1048219888Sed	struct vt_device *vd = vw->vw_device;
1049256145Sray	int error, s;
1050219888Sed
1051219888Sed	switch (cmd) {
1052219888Sed	case GIO_KEYMAP:
1053219888Sed	case PIO_KEYMAP:
1054219888Sed	case GIO_DEADKEYMAP:
1055219888Sed	case PIO_DEADKEYMAP:
1056219888Sed	case GETFKEY:
1057219888Sed	case SETFKEY:
1058219888Sed	case KDGKBINFO: {
1059219888Sed		keyboard_t *kbd;
1060256145Sray		error = 0;
1061219888Sed
1062219888Sed		mtx_lock(&Giant);
1063219888Sed		kbd = kbd_get_keyboard(vd->vd_keyboard);
1064219888Sed		if (kbd != NULL)
1065219888Sed			error = kbdd_ioctl(kbd, cmd, data);
1066219888Sed		mtx_unlock(&Giant);
1067219888Sed		if (error == ENOIOCTL)
1068219888Sed			return (ENODEV);
1069219888Sed		return (error);
1070219888Sed	}
1071256145Sray	case KDGKBMODE: {
1072256145Sray		int mode = -1;
1073256145Sray		keyboard_t *kbd;
1074256145Sray
1075256145Sray		mtx_lock(&Giant);
1076256145Sray		kbd = kbd_get_keyboard(vd->vd_keyboard);
1077256145Sray		if (kbd != NULL) {
1078256145Sray			kbdd_ioctl(kbd, KDGKBMODE, (void *)&mode);
1079256145Sray		}
1080256145Sray		mtx_unlock(&Giant);
1081256145Sray		DPRINTF(20, "mode %d, vw_kbdmode %d\n", mode, vw->vw_kbdmode);
1082256145Sray		*(int *)data = mode;
1083256145Sray		return (0);
1084256145Sray	}
1085219888Sed	case KDSKBMODE: {
1086219888Sed		int mode;
1087219888Sed
1088219888Sed		mode = *(int *)data;
1089219888Sed		switch (mode) {
1090219888Sed		case K_XLATE:
1091219888Sed		case K_RAW:
1092219888Sed		case K_CODE:
1093219888Sed			vw->vw_kbdmode = mode;
1094219888Sed			if (vw == vd->vd_curwindow) {
1095219888Sed				keyboard_t *kbd;
1096256145Sray				error = 0;
1097219888Sed
1098256145Sray				DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard);
1099219888Sed				mtx_lock(&Giant);
1100219888Sed				kbd = kbd_get_keyboard(vd->vd_keyboard);
1101256145Sray				if (kbd != NULL) {
1102256145Sray					DPRINTF(20, "kbdd_ioctl(KDSKBMODE, %d)\n", mode);
1103256145Sray					error = kbdd_ioctl(kbd, KDSKBMODE,
1104219888Sed					    (void *)&mode);
1105256145Sray				}
1106219888Sed				mtx_unlock(&Giant);
1107256145Sray				if (error)
1108256145Sray					DPRINTF(20, "kbdd_ioctl(KDSKBMODE) return %d\n", error);
1109219888Sed			}
1110219888Sed			return (0);
1111219888Sed		default:
1112219888Sed			return (EINVAL);
1113219888Sed		}
1114219888Sed	}
1115219888Sed	case CONS_BLANKTIME:
1116219888Sed		/* XXX */
1117219888Sed		return (0);
1118219888Sed	case CONS_GET:
1119219888Sed		/* XXX */
1120219888Sed		*(int *)data = M_CG640x480;
1121219888Sed		return (0);
1122219888Sed	case CONS_GETINFO: {
1123219888Sed		vid_info_t *vi = (vid_info_t *)data;
1124219888Sed
1125219888Sed		vi->m_num = vd->vd_curwindow->vw_number + 1;
1126219888Sed		/* XXX: other fields! */
1127219888Sed		return (0);
1128219888Sed	}
1129219888Sed	case CONS_GETVERS:
1130219888Sed		*(int *)data = 0x200;
1131219888Sed		return 0;
1132219888Sed	case CONS_MODEINFO:
1133219888Sed		/* XXX */
1134219888Sed		return (0);
1135219888Sed	case CONS_MOUSECTL: {
1136219888Sed		mouse_info_t *mouse = (mouse_info_t*)data;
1137219888Sed
1138219888Sed		/*
1139219888Sed		 * This has no effect on vt(4).  We don't draw any mouse
1140219888Sed		 * cursor.  Just ignore MOUSE_HIDE and MOUSE_SHOW to
1141219888Sed		 * prevent excessive errors.  All the other commands
1142219888Sed		 * should not be applied to individual TTYs, but only to
1143219888Sed		 * consolectl.
1144219888Sed		 */
1145219888Sed		switch (mouse->operation) {
1146219888Sed		case MOUSE_HIDE:
1147219888Sed		case MOUSE_SHOW:
1148219888Sed			return (0);
1149219888Sed		default:
1150219888Sed			return (EINVAL);
1151219888Sed		}
1152219888Sed	}
1153219888Sed	case PIO_VFONT: {
1154219888Sed		struct vt_font *vf;
1155219888Sed
1156219888Sed		error = vtfont_load((void *)data, &vf);
1157219888Sed		if (error != 0)
1158219888Sed			return (error);
1159219888Sed
1160219888Sed		error = vt_change_font(vw, vf);
1161219888Sed		vtfont_unref(vf);
1162219888Sed		return (error);
1163219888Sed	}
1164219888Sed	case GIO_SCRNMAP: {
1165219888Sed		scrmap_t *sm = (scrmap_t *)data;
1166219888Sed		int i;
1167219888Sed
1168219888Sed		/* We don't have screen maps, so return a handcrafted one. */
1169219888Sed		for (i = 0; i < 256; i++)
1170219888Sed			sm->scrmap[i] = i;
1171219888Sed		return (0);
1172219888Sed	}
1173219888Sed	case KDGETLED:
1174219888Sed		/* XXX */
1175219888Sed		return (0);
1176219888Sed	case KDSETLED:
1177219888Sed		/* XXX */
1178219888Sed		return (0);
1179219888Sed	case KDSETMODE:
1180219888Sed		/* XXX */
1181219888Sed		return (0);
1182219888Sed	case KDSETRAD:
1183219888Sed		/* XXX */
1184219888Sed		return (0);
1185256145Sray	case VT_ACTIVATE: {
1186256145Sray		int win;
1187256145Sray		win = *(int *)data - 1;
1188256145Sray		DPRINTF(5, "%s%d: VT_ACTIVATE ttyv%d ", SC_DRIVER_NAME, VT_UNIT(vw), win);
1189256145Sray		if ((win > VT_MAXWINDOWS) || (win < 0))
1190256145Sray			return (EINVAL);
1191256145Sray		return (vt_proc_window_switch(vd->vd_windows[win]));
1192256145Sray	}
1193219888Sed	case VT_GETACTIVE:
1194219888Sed		*(int *)data = vd->vd_curwindow->vw_number + 1;
1195219888Sed		return (0);
1196219888Sed	case VT_GETINDEX:
1197219888Sed		*(int *)data = vw->vw_number + 1;
1198219888Sed		return (0);
1199256145Sray	case VT_LOCKSWITCH:
1200256145Sray		/* TODO: Check current state, switching can be in progress. */
1201256145Sray		if ((*(int *)data) & 0x01)
1202256145Sray			vw->vw_flags |= VWF_VTYLOCK;
1203256145Sray		else
1204256145Sray			vw->vw_flags &= ~VWF_VTYLOCK;
1205219888Sed	case VT_OPENQRY: {
1206219888Sed		unsigned int i;
1207219888Sed
1208219888Sed		VT_LOCK(vd);
1209219888Sed		for (i = 0; i < VT_MAXWINDOWS; i++) {
1210219888Sed			vw = vd->vd_windows[i];
1211219888Sed			if (vw == NULL)
1212219888Sed				continue;
1213219888Sed			if (!(vw->vw_flags & VWF_OPENED)) {
1214219888Sed				*(int *)data = vw->vw_number + 1;
1215219888Sed				VT_UNLOCK(vd);
1216219888Sed				return (0);
1217219888Sed			}
1218219888Sed		}
1219219888Sed		VT_UNLOCK(vd);
1220219888Sed		return (EINVAL);
1221219888Sed	}
1222219888Sed	case VT_WAITACTIVE: {
1223219888Sed		unsigned int i;
1224256145Sray		error = 0;
1225219888Sed
1226219888Sed		i = *(unsigned int *)data;
1227219888Sed		if (i > VT_MAXWINDOWS)
1228219888Sed			return (EINVAL);
1229219888Sed		if (i != 0)
1230219888Sed			vw = vd->vd_windows[i - 1];
1231219888Sed
1232219888Sed		VT_LOCK(vd);
1233219888Sed		while (vd->vd_curwindow != vw && error == 0)
1234219888Sed			error = cv_wait_sig(&vd->vd_winswitch, &vd->vd_lock);
1235219888Sed		VT_UNLOCK(vd);
1236219888Sed		return (error);
1237219888Sed	}
1238256145Sray	case VT_SETMODE:    	/* set screen switcher mode */
1239256145Sray	{
1240256145Sray		struct vt_mode *mode;
1241256145Sray		struct proc *p1;
1242256145Sray
1243256145Sray		mode = (struct vt_mode *)data;
1244256145Sray		DPRINTF(5, "%s%d: VT_SETMODE ", SC_DRIVER_NAME, VT_UNIT(vw));
1245256145Sray		if (vw->vw_smode.mode == VT_PROCESS) {
1246256145Sray			p1 = pfind(vw->vw_pid);
1247256145Sray			if (vw->vw_proc == p1 && vw->vw_proc != td->td_proc) {
1248256145Sray				if (p1)
1249256145Sray					PROC_UNLOCK(p1);
1250256145Sray				DPRINTF(5, "error EPERM\n");
1251256145Sray				return (EPERM);
1252256145Sray			}
1253256145Sray			if (p1)
1254256145Sray				PROC_UNLOCK(p1);
1255256145Sray		}
1256256145Sray		if (mode->mode == VT_AUTO) {
1257256145Sray			vw->vw_smode.mode = VT_AUTO;
1258256145Sray			vw->vw_proc = NULL;
1259256145Sray			vw->vw_pid = 0;
1260256145Sray			DPRINTF(5, "VT_AUTO, ");
1261256145Sray			if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW])
1262256145Sray				cnavailable(vw->vw_terminal->consdev, TRUE);
1263256145Sray			/* were we in the middle of the vty switching process? */
1264256145Sray			if (finish_vt_rel(vw, TRUE, &s) == 0)
1265256145Sray				DPRINTF(5, "reset WAIT_REL, ");
1266256145Sray			if (finish_vt_acq(vw) == 0)
1267256145Sray				DPRINTF(5, "reset WAIT_ACQ, ");
1268256145Sray			return (0);
1269256145Sray		} else if (mode->mode == VT_PROCESS) {
1270256145Sray			if (!ISSIGVALID(mode->relsig) ||
1271256145Sray			    !ISSIGVALID(mode->acqsig) ||
1272256145Sray			    !ISSIGVALID(mode->frsig)) {
1273256145Sray				DPRINTF(5, "error EINVAL\n");
1274256145Sray				return (EINVAL);
1275256145Sray			}
1276256145Sray			DPRINTF(5, "VT_PROCESS %d, ", td->td_proc->p_pid);
1277256145Sray			bcopy(data, &vw->vw_smode, sizeof(struct vt_mode));
1278256145Sray			vw->vw_proc = td->td_proc;
1279256145Sray			vw->vw_pid = vw->vw_proc->p_pid;
1280256145Sray			if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW])
1281256145Sray				cnavailable(vw->vw_terminal->consdev, FALSE);
1282256145Sray		} else {
1283256145Sray			DPRINTF(5, "VT_SETMODE failed, unknown mode %d\n",
1284256145Sray			    mode->mode);
1285256145Sray			return (EINVAL);
1286256145Sray		}
1287256145Sray		DPRINTF(5, "\n");
1288256145Sray		return 0;
1289219888Sed	}
1290219888Sed
1291256145Sray	case VT_GETMODE:	/* get screen switcher mode */
1292256145Sray		bcopy(&vw->vw_smode, data, sizeof(struct vt_mode));
1293256145Sray		return 0;
1294256145Sray
1295256145Sray	case VT_RELDISP:	/* screen switcher ioctl */
1296256145Sray		/*
1297256145Sray		 * This must be the current vty which is in the VT_PROCESS
1298256145Sray		 * switching mode...
1299256145Sray		 */
1300256145Sray		if ((vw != vd->vd_curwindow) || (vw->vw_smode.mode !=
1301256145Sray		    VT_PROCESS)) {
1302256145Sray			return EINVAL;
1303256145Sray		}
1304256145Sray		/* ...and this process is controlling it. */
1305256145Sray		if (vw->vw_proc != td->td_proc) {
1306256145Sray			return EPERM;
1307256145Sray		}
1308256145Sray		error = EINVAL;
1309256145Sray		switch(*(int *)data) {
1310256145Sray		case VT_FALSE:	/* user refuses to release screen, abort */
1311256145Sray			if ((error = finish_vt_rel(vw, FALSE, &s)) == 0)
1312256145Sray				DPRINTF(5, "%s%d: VT_RELDISP: VT_FALSE\n", SC_DRIVER_NAME,
1313256145Sray				    VT_UNIT(vw));
1314256145Sray			break;
1315256145Sray		case VT_TRUE:	/* user has released screen, go on */
1316256145Sray			/* finish_vt_rel(..., TRUE, ...) should not be locked */
1317256145Sray			if (vw->vw_flags & VWF_SWWAIT_REL) {
1318256145Sray				if ((error = finish_vt_rel(vw, TRUE, &s)) == 0)
1319256145Sray					DPRINTF(5, "%s%d: VT_RELDISP: VT_TRUE\n",
1320256145Sray					    SC_DRIVER_NAME, VT_UNIT(vw));
1321256145Sray			} else {
1322256145Sray				error = EINVAL;
1323256145Sray			}
1324256145Sray			return (error);
1325256145Sray		case VT_ACKACQ:	/* acquire acknowledged, switch completed */
1326256145Sray			if ((error = finish_vt_acq(vw)) == 0)
1327256145Sray				DPRINTF(5, "%s%d: VT_RELDISP: VT_ACKACQ\n", SC_DRIVER_NAME,
1328256145Sray				    VT_UNIT(vw));
1329256145Sray			break;
1330256145Sray		default:
1331256145Sray			break;
1332256145Sray		}
1333256145Sray		return error;
1334256145Sray	}
1335256145Sray
1336219888Sed	return (ENOIOCTL);
1337219888Sed}
1338219888Sed
1339219888Sedstatic struct vt_window *
1340219888Sedvt_allocate_window(struct vt_device *vd, unsigned int window)
1341219888Sed{
1342219888Sed	struct vt_window *vw;
1343219888Sed	struct terminal *tm;
1344219888Sed	term_pos_t size;
1345219888Sed	struct winsize wsz;
1346219888Sed
1347219888Sed	vw = malloc(sizeof *vw, M_VT, M_WAITOK|M_ZERO);
1348219888Sed	vw->vw_device = vd;
1349219888Sed	vw->vw_number = window;
1350219888Sed	vw->vw_kbdmode = K_XLATE;
1351219888Sed
1352219888Sed	if (!(vd->vd_flags & VDF_TEXTMODE))
1353219888Sed		vw->vw_font = vtfont_ref(&vt_font_default);
1354256145Sray
1355219888Sed	vt_termsize(vd, vw->vw_font, &size);
1356219888Sed	vt_winsize(vd, vw->vw_font, &wsz);
1357219888Sed	vtbuf_init(&vw->vw_buf, &size);
1358219888Sed
1359219888Sed	tm = vw->vw_terminal = terminal_alloc(&vt_termclass, vw);
1360219888Sed	terminal_set_winsize(tm, &wsz);
1361219888Sed	vd->vd_windows[window] = vw;
1362256145Sray	callout_init(&vw->vw_proc_dead_timer, 0);
1363219888Sed
1364219888Sed	return (vw);
1365219888Sed}
1366219888Sed
1367219888Sedvoid
1368219888Sedvt_upgrade(struct vt_device *vd)
1369219888Sed{
1370219888Sed	struct vt_window *vw;
1371219888Sed	unsigned int i;
1372219888Sed
1373256145Sray	/* Device didn't pass vd_init() or already upgraded. */
1374256145Sray	if (vd->vd_flags & (VDF_ASYNC|VDF_DEAD))
1375219888Sed		return;
1376256145Sray	vd->vd_flags |= VDF_ASYNC;
1377219888Sed
1378219888Sed	mtx_init(&vd->vd_lock, "vtdev", NULL, MTX_DEF);
1379219888Sed	cv_init(&vd->vd_winswitch, "vtwswt");
1380219888Sed
1381256145Sray	/* Init 25 Hz timer. */
1382219888Sed	callout_init_mtx(&vd->vd_timer, &vd->vd_lock, 0);
1383219888Sed
1384219888Sed	for (i = 0; i < VT_MAXWINDOWS; i++) {
1385219888Sed		vw = vd->vd_windows[i];
1386219888Sed		if (vw == NULL) {
1387219888Sed			/* New window. */
1388219888Sed			vw = vt_allocate_window(vd, i);
1389256145Sray		}
1390256145Sray		if (i == VT_CONSWINDOW) {
1391219888Sed			/* Console window. */
1392219888Sed			EVENTHANDLER_REGISTER(shutdown_pre_sync,
1393219888Sed			    vt_window_switch, vw, SHUTDOWN_PRI_DEFAULT);
1394219888Sed		}
1395219888Sed		terminal_maketty(vw->vw_terminal, "v%r", VT_UNIT(vw));
1396219888Sed	}
1397256145Sray	if (vd->vd_curwindow == NULL)
1398256145Sray		vd->vd_curwindow = vd->vd_windows[VT_CONSWINDOW];
1399219888Sed
1400219888Sed	/* Attach keyboard. */
1401219888Sed	vt_allocate_keyboard(vd);
1402256145Sray	DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard);
1403256145Sray
1404256145Sray	/* Start timer when everything ready. */
1405256145Sray	callout_reset(&vd->vd_timer, hz / VT_TIMERFREQ, vt_timer, vd);
1406219888Sed}
1407219888Sed
1408256145Sraystatic void
1409256145Srayvt_resize(struct vt_device *vd)
1410256145Sray{
1411256145Sray	struct vt_window *vw;
1412256145Sray	int i;
1413256145Sray
1414256145Sray	for (i = 0; i < VT_MAXWINDOWS; i++) {
1415256145Sray		vw = vd->vd_windows[i];
1416256145Sray		/* Resize terminal windows */
1417256145Sray		vt_change_font(vw, vw->vw_font);
1418256145Sray	}
1419256145Sray}
1420256145Sray
1421219888Sedvoid
1422219888Sedvt_allocate(struct vt_driver *drv, void *softc)
1423219888Sed{
1424219888Sed	struct vt_device *vd;
1425256903Sray	struct winsize wsz;
1426219888Sed
1427256145Sray	if (main_vd == NULL) {
1428256145Sray		main_vd = malloc(sizeof *vd, M_VT, M_WAITOK|M_ZERO);
1429256903Sray		printf("%s: VT initialize with new VT driver.\n", __func__);
1430256145Sray	} else {
1431256145Sray		/*
1432256145Sray		 * Check if have rights to replace current driver. For example:
1433256145Sray		 * it is bad idea to replace KMS driver with generic VGA one.
1434256145Sray		 */
1435256903Sray		if (drv->vd_priority <= main_vd->vd_driver->vd_priority) {
1436256903Sray			printf("%s: Driver priority %d too low. Current %d\n ",
1437256903Sray			    __func__, drv->vd_priority,
1438256903Sray			    main_vd->vd_driver->vd_priority);
1439256145Sray			return;
1440256903Sray		}
1441256903Sray		printf("%s: Replace existing VT driver.\n", __func__);
1442256145Sray	}
1443256145Sray	vd = main_vd;
1444256145Sray
1445256527Sray	/* Stop vt_flush periodic task. */
1446256145Sray	if (vd->vd_curwindow != NULL)
1447256145Sray		callout_drain(&vd->vd_timer);
1448256145Sray
1449219888Sed	vd->vd_driver = drv;
1450219888Sed	vd->vd_softc = softc;
1451219888Sed	vd->vd_driver->vd_init(vd);
1452256145Sray
1453219888Sed	vt_upgrade(vd);
1454256145Sray
1455256145Sray	/* Refill settings with new sizes. */
1456256145Sray	vt_resize(vd);
1457256145Sray
1458256145Sray	if (vd->vd_flags & VDF_SPLASH)
1459256145Sray		vtterm_splash(vd);
1460256145Sray
1461256145Sray	if (vd->vd_curwindow != NULL)
1462256145Sray		callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ);
1463256145Sray
1464256145Sray	termcn_cnregister(vd->vd_windows[VT_CONSWINDOW]->vw_terminal);
1465256903Sray
1466256903Sray	/* Update console window sizes to actual. */
1467256903Sray	vt_winsize(vd, vd->vd_windows[VT_CONSWINDOW]->vw_font, &wsz);
1468256903Sray	terminal_set_winsize(vd->vd_windows[VT_CONSWINDOW]->vw_terminal, &wsz);
1469219888Sed}
1470257815Sray
1471257815Srayvoid
1472257815Srayvt_suspend()
1473257815Sray{
1474257815Sray
1475257815Sray	/* Save current window. */
1476257815Sray	main_vd->vd_savedwindow = main_vd->vd_curwindow;
1477257815Sray	/* Ask holding process to free window and switch to console window */
1478257815Sray	vt_proc_window_switch(main_vd->vd_windows[VT_CONSWINDOW]);
1479257815Sray}
1480257815Sray
1481257815Srayvoid
1482257815Srayvt_resume()
1483257815Sray{
1484257815Sray
1485257815Sray	/* Switch back to saved window */
1486257815Sray	if (main_vd->vd_savedwindow != NULL)
1487257815Sray		vt_proc_window_switch(main_vd->vd_savedwindow);
1488257815Sray	main_vd->vd_savedwindow = NULL;
1489257815Sray}
1490