vt_core.c revision 258165
1/*-
2 * Copyright (c) 2009, 2013 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Ed Schouten under sponsorship from the
6 * FreeBSD Foundation.
7 *
8 * Portions of this software were developed by Oleksandr Rybalko
9 * under sponsorship from the FreeBSD Foundation.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: user/ed/newcons/sys/dev/vt/vt_core.c 258165 2013-11-15 11:33:36Z ray $");
35
36#include <sys/param.h>
37#include <sys/consio.h>
38#include <sys/eventhandler.h>
39#include <sys/fbio.h>
40#include <sys/kbio.h>
41#include <sys/kdb.h>
42#include <sys/kernel.h>
43#include <sys/lock.h>
44#include <sys/malloc.h>
45#include <sys/mutex.h>
46#include <sys/proc.h>
47#include <sys/reboot.h>
48#include <sys/systm.h>
49#include <sys/terminal.h>
50
51#include <dev/kbd/kbdreg.h>
52#include <dev/vt/vt.h>
53
54static tc_bell_t	vtterm_bell;
55static tc_cursor_t	vtterm_cursor;
56static tc_putchar_t	vtterm_putchar;
57static tc_fill_t	vtterm_fill;
58static tc_copy_t	vtterm_copy;
59static tc_param_t	vtterm_param;
60static tc_done_t	vtterm_done;
61
62static tc_cnprobe_t	vtterm_cnprobe;
63static tc_cngetc_t	vtterm_cngetc;
64
65static tc_opened_t	vtterm_opened;
66static tc_ioctl_t	vtterm_ioctl;
67
68const struct terminal_class vt_termclass = {
69	.tc_bell	= vtterm_bell,
70	.tc_cursor	= vtterm_cursor,
71	.tc_putchar	= vtterm_putchar,
72	.tc_fill	= vtterm_fill,
73	.tc_copy	= vtterm_copy,
74	.tc_param	= vtterm_param,
75	.tc_done	= vtterm_done,
76
77	.tc_cnprobe	= vtterm_cnprobe,
78	.tc_cngetc	= vtterm_cngetc,
79
80	.tc_opened	= vtterm_opened,
81	.tc_ioctl	= vtterm_ioctl,
82};
83
84/*
85 * Use a constant timer of 25 Hz to redraw the screen.
86 *
87 * XXX: In theory we should only fire up the timer when there is really
88 * activity. Unfortunately we cannot always start timers. We really
89 * don't want to process kernel messages synchronously, because it
90 * really slows down the system.
91 */
92#define	VT_TIMERFREQ	25
93
94/* Bell pitch/duration. */
95#define VT_BELLDURATION	((5 * hz + 99) / 100)
96#define VT_BELLPITCH	800
97
98#define	VT_LOCK(vd)	mtx_lock(&(vd)->vd_lock)
99#define	VT_UNLOCK(vd)	mtx_unlock(&(vd)->vd_lock)
100
101#define	VT_UNIT(vw)	((vw)->vw_device->vd_unit * VT_MAXWINDOWS + \
102			(vw)->vw_number)
103
104/* XXX while syscons is here. */
105int sc_txtmouse_no_retrace_wait;
106
107static SYSCTL_NODE(_kern, OID_AUTO, vt, CTLFLAG_RD, 0, "Newcons parameters");
108VT_SYSCTL_INT(debug, 0, "Newcons debug level");
109VT_SYSCTL_INT(deadtimer, 15, "Time to wait busy process in VT_PROCESS mode");
110VT_SYSCTL_INT(suspendswitch, 1, "Switch to VT0 before suspend");
111
112static unsigned int vt_unit = 0;
113static MALLOC_DEFINE(M_VT, "vt", "vt device");
114struct vt_device *main_vd = NULL;
115
116/* Boot logo. */
117extern unsigned int vt_logo_width;
118extern unsigned int vt_logo_height;
119extern unsigned int vt_logo_depth;
120extern unsigned char vt_logo_image[];
121
122/* Font. */
123extern struct vt_font vt_font_default;
124extern struct mouse_cursor vt_default_mouse_pointer;
125
126static int signal_vt_rel(struct vt_window *);
127static int signal_vt_acq(struct vt_window *);
128static int finish_vt_rel(struct vt_window *, int, int *);
129static int finish_vt_acq(struct vt_window *);
130static int vt_window_switch(struct vt_window *);
131static int vt_late_window_switch(struct vt_window *);
132static int vt_proc_alive(struct vt_window *);
133static void vt_resize(struct vt_device *);
134
135static void
136vt_switch_timer(void *arg)
137{
138
139	vt_late_window_switch((struct vt_window *)arg);
140}
141
142static int
143vt_window_preswitch(struct vt_window *vw, struct vt_window *curvw)
144{
145
146	DPRINTF(40, "%s\n", __func__);
147	curvw->vw_switch_to = vw;
148	/* Set timer to allow switch in case when process hang. */
149	callout_reset(&vw->vw_proc_dead_timer, hz * vt_deadtimer,
150	    vt_switch_timer, (void *)vw);
151	/* Notify process about vt switch attempt. */
152	DPRINTF(30, "%s: Notify process.\n", __func__);
153	signal_vt_rel(curvw);
154
155	return (0);
156}
157
158static int
159vt_window_postswitch(struct vt_window *vw)
160{
161
162	signal_vt_acq(vw);
163	return (0);
164}
165
166/* vt_late_window_switch will done VT switching for regular case. */
167static int
168vt_late_window_switch(struct vt_window *vw)
169{
170	int ret;
171
172	callout_stop(&vw->vw_proc_dead_timer);
173
174	ret = vt_window_switch(vw);
175	if (ret)
176		return (ret);
177
178	/* Notify owner process about terminal availability. */
179	if (vw->vw_smode.mode == VT_PROCESS) {
180		ret = vt_window_postswitch(vw);
181	}
182	return (ret);
183}
184
185/* Switch window. */
186static int
187vt_proc_window_switch(struct vt_window *vw)
188{
189	struct vt_window *curvw;
190	struct vt_device *vd;
191	int ret;
192
193	if (vw->vw_flags & VWF_VTYLOCK)
194		return (EBUSY);
195
196	vd = vw->vw_device;
197	curvw = vd->vd_curwindow;
198
199	/* Ask current process permitions to switch away. */
200	if (curvw->vw_smode.mode == VT_PROCESS) {
201		DPRINTF(30, "%s: VT_PROCESS ", __func__);
202		if (vt_proc_alive(curvw) == FALSE) {
203			DPRINTF(30, "Dead. Cleaning.");
204			/* Dead */
205		} else {
206			DPRINTF(30, "%s: Signaling process.\n", __func__);
207			/* Alive, try to ask him. */
208			ret = vt_window_preswitch(vw, curvw);
209			/* Wait for process answer or timeout. */
210			return (ret);
211		}
212		DPRINTF(30, "\n");
213	}
214
215	ret = vt_late_window_switch(vw);
216	return (ret);
217}
218
219/* Switch window ignoring process locking. */
220static int
221vt_window_switch(struct vt_window *vw)
222{
223	struct vt_device *vd = vw->vw_device;
224	struct vt_window *curvw = vd->vd_curwindow;
225	keyboard_t *kbd;
226
227	VT_LOCK(vd);
228	if (curvw == vw) {
229		/* Nothing to do. */
230		VT_UNLOCK(vd);
231		return (0);
232	}
233	if (!(vw->vw_flags & (VWF_OPENED|VWF_CONSOLE))) {
234		VT_UNLOCK(vd);
235		return (EINVAL);
236	}
237
238	vd->vd_curwindow = vw;
239	vd->vd_flags |= VDF_INVALID;
240	cv_broadcast(&vd->vd_winswitch);
241	VT_UNLOCK(vd);
242
243	if (vd->vd_driver->vd_postswitch)
244		vd->vd_driver->vd_postswitch(vd);
245
246	/* Restore per-window keyboard mode. */
247	mtx_lock(&Giant);
248	kbd = kbd_get_keyboard(vd->vd_keyboard);
249	if (kbd != NULL) {
250		kbdd_ioctl(kbd, KDSKBMODE, (void *)&vw->vw_kbdmode);
251	}
252	mtx_unlock(&Giant);
253	DPRINTF(10, "%s(ttyv%d) done\n", __func__, vw->vw_number);
254
255	return (0);
256}
257
258static inline void
259vt_termsize(struct vt_device *vd, struct vt_font *vf, term_pos_t *size)
260{
261
262	size->tp_row = vd->vd_height;
263	size->tp_col = vd->vd_width;
264	if (vf != NULL) {
265		size->tp_row /= vf->vf_height;
266		size->tp_col /= vf->vf_width;
267	}
268}
269
270static inline void
271vt_winsize(struct vt_device *vd, struct vt_font *vf, struct winsize *size)
272{
273
274	size->ws_row = size->ws_ypixel = vd->vd_height;
275	size->ws_col = size->ws_xpixel = vd->vd_width;
276	if (vf != NULL) {
277		size->ws_row /= vf->vf_height;
278		size->ws_col /= vf->vf_width;
279	}
280}
281
282static void
283vt_scroll(struct vt_window *vw, int offset, int whence)
284{
285	int diff;
286	term_pos_t size;
287
288	if ((vw->vw_flags & VWF_SCROLL) == 0)
289		return;
290
291	vt_termsize(vw->vw_device, vw->vw_font, &size);
292
293	diff = vthistory_seek(&vw->vw_buf, offset, whence);
294	/*
295	 * Offset changed, please update Nth lines on sceen.
296	 * +N - Nth lines at top;
297	 * -N - Nth lines at bottom.
298	 */
299
300	if (diff < -size.tp_row || diff > size.tp_row) {
301		vw->vw_device->vd_flags |= VDF_INVALID;
302		return;
303	}
304	vw->vw_device->vd_flags |= VDF_INVALID; /*XXX*/
305}
306
307static int
308vt_machine_kbdevent(int c)
309{
310
311	switch (c) {
312	case SPCLKEY | DBG:
313		kdb_enter(KDB_WHY_BREAK, "manual escape to debugger");
314		return (1);
315	case SPCLKEY | RBT:
316		/* XXX: Make this configurable! */
317		shutdown_nice(0);
318		return (1);
319	case SPCLKEY | HALT:
320		shutdown_nice(RB_HALT);
321		return (1);
322	case SPCLKEY | PDWN:
323		shutdown_nice(RB_HALT|RB_POWEROFF);
324		return (1);
325	};
326
327	return (0);
328}
329
330static void
331vt_scrollmode_kbdevent(struct vt_window *vw, int c, int console)
332{
333	struct vt_device *vd;
334	term_pos_t size;
335
336	vd = vw->vw_device;
337	/* Only special keys handled in ScrollLock mode */
338	if ((c & SPCLKEY) == 0)
339		return;
340
341	c &= ~SPCLKEY;
342
343	if (console == 0) {
344		if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) {
345			vw = vd->vd_windows[c - F_SCR];
346			if (vw != NULL)
347				vt_proc_window_switch(vw);
348			return;
349		}
350		VT_LOCK(vd);
351	}
352
353	switch (c) {
354	case SLK: {
355		/* Turn scrolling off. */
356		vt_scroll(vw, 0, VHS_END);
357		VTBUF_SLCK_DISABLE(&vw->vw_buf);
358		vw->vw_flags &= ~VWF_SCROLL;
359		break;
360	}
361	case FKEY | F(49): /* Home key. */
362		vt_scroll(vw, 0, VHS_END);
363		break;
364	case FKEY | F(50): /* Arrow up. */
365		vt_scroll(vw, -1, VHS_CUR);
366		break;
367	case FKEY | F(51): /* Page up. */
368		vt_termsize(vd, vw->vw_font, &size);
369		vt_scroll(vw, -size.tp_row, VHS_CUR);
370		break;
371	case FKEY | F(57): /* End key. */
372		vt_scroll(vw, 0, VHS_SET);
373		break;
374	case FKEY | F(58): /* Arrow down. */
375		vt_scroll(vw, 1, VHS_CUR);
376		break;
377	case FKEY | F(59): /* Page down. */
378		vt_termsize(vd, vw->vw_font, &size);
379		vt_scroll(vw, size.tp_row, VHS_CUR);
380		break;
381	}
382
383	if (console == 0)
384		VT_UNLOCK(vd);
385}
386
387static int
388vt_processkey(keyboard_t *kbd, struct vt_device *vd, int c)
389{
390	struct vt_window *vw = vd->vd_curwindow;
391	int state = 0;
392
393#if VT_ALT_TO_ESC_HACK
394	if (c & RELKEY) {
395		switch (c & ~RELKEY) {
396		case (SPCLKEY | RALT):
397		case (SPCLKEY | LALT):
398			vd->vd_kbstate &= ~ALKED;
399		}
400		/* Other keys ignored for RELKEY event. */
401		return (0);
402	} else {
403		switch (c & ~RELKEY) {
404		case (SPCLKEY | RALT):
405		case (SPCLKEY | LALT):
406			vd->vd_kbstate |= ALKED;
407		}
408	}
409#else
410	if (c & RELKEY)
411		/* Other keys ignored for RELKEY event. */
412		return (0);
413#endif
414
415	if (vt_machine_kbdevent(c))
416		return (0);
417
418	if (vw->vw_flags & VWF_SCROLL) {
419		vt_scrollmode_kbdevent(vw, c, 0/* Not a console */);
420		/* Scroll mode keys handled, nothing to do more. */
421		return (0);
422	}
423
424	if (c & SPCLKEY) {
425		c &= ~SPCLKEY;
426
427		if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) {
428			vw = vd->vd_windows[c - F_SCR];
429			if (vw != NULL)
430				vt_proc_window_switch(vw);
431			return (0);
432		}
433
434		switch (c) {
435		case SLK: {
436
437			kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state);
438			VT_LOCK(vd);
439			if (state & SLKED) {
440				/* Turn scrolling on. */
441				vw->vw_flags |= VWF_SCROLL;
442				VTBUF_SLCK_ENABLE(&vw->vw_buf);
443			} else {
444				/* Turn scrolling off. */
445				vw->vw_flags &= ~VWF_SCROLL;
446				VTBUF_SLCK_DISABLE(&vw->vw_buf);
447				vt_scroll(vw, 0, VHS_END);
448			}
449			VT_UNLOCK(vd);
450			break;
451		}
452		case FKEY | F(1):  case FKEY | F(2):  case FKEY | F(3):
453		case FKEY | F(4):  case FKEY | F(5):  case FKEY | F(6):
454		case FKEY | F(7):  case FKEY | F(8):  case FKEY | F(9):
455		case FKEY | F(10): case FKEY | F(11): case FKEY | F(12):
456			/* F1 through F12 keys. */
457			terminal_input_special(vw->vw_terminal,
458			    TKEY_F1 + c - (FKEY | F(1)));
459			break;
460		case FKEY | F(49): /* Home key. */
461			terminal_input_special(vw->vw_terminal, TKEY_HOME);
462			break;
463		case FKEY | F(50): /* Arrow up. */
464			terminal_input_special(vw->vw_terminal, TKEY_UP);
465			break;
466		case FKEY | F(51): /* Page up. */
467			terminal_input_special(vw->vw_terminal, TKEY_PAGE_UP);
468			break;
469		case FKEY | F(53): /* Arrow left. */
470			terminal_input_special(vw->vw_terminal, TKEY_LEFT);
471			break;
472		case FKEY | F(55): /* Arrow right. */
473			terminal_input_special(vw->vw_terminal, TKEY_RIGHT);
474			break;
475		case FKEY | F(57): /* End key. */
476			terminal_input_special(vw->vw_terminal, TKEY_END);
477			break;
478		case FKEY | F(58): /* Arrow down. */
479			terminal_input_special(vw->vw_terminal, TKEY_DOWN);
480			break;
481		case FKEY | F(59): /* Page down. */
482			terminal_input_special(vw->vw_terminal, TKEY_PAGE_DOWN);
483			break;
484		case FKEY | F(60): /* Insert key. */
485			terminal_input_special(vw->vw_terminal, TKEY_INSERT);
486			break;
487		case FKEY | F(61): /* Delete key. */
488			terminal_input_special(vw->vw_terminal, TKEY_DELETE);
489			break;
490		}
491	} else if (KEYFLAGS(c) == 0) {
492		/* Don't do UTF-8 conversion when doing raw mode. */
493		if (vw->vw_kbdmode == K_XLATE) {
494#if VT_ALT_TO_ESC_HACK
495			if (vd->vd_kbstate & ALKED) {
496				/*
497				 * Prepend ESC sequence if one of ALT keys down.
498				 */
499				terminal_input_char(vw->vw_terminal, 0x1b);
500			}
501#endif
502
503			terminal_input_char(vw->vw_terminal, KEYCHAR(c));
504		} else
505			terminal_input_raw(vw->vw_terminal, c);
506	}
507	return (0);
508}
509
510static int
511vt_kbdevent(keyboard_t *kbd, int event, void *arg)
512{
513	struct vt_device *vd = arg;
514	int c;
515
516	switch (event) {
517	case KBDIO_KEYINPUT:
518		break;
519	case KBDIO_UNLOADING:
520		mtx_lock(&Giant);
521		vd->vd_keyboard = -1;
522		kbd_release(kbd, (void *)&vd->vd_keyboard);
523		mtx_unlock(&Giant);
524		return (0);
525	default:
526		return (EINVAL);
527	}
528
529	while ((c = kbdd_read_char(kbd, 0)) != NOKEY)
530		vt_processkey(kbd, vd, c);
531
532	return (0);
533}
534
535static int
536vt_allocate_keyboard(struct vt_device *vd)
537{
538	int		 idx0, idx;
539	keyboard_t	*k0, *k;
540	keyboard_info_t	 ki;
541
542	idx0 = kbd_allocate("kbdmux", -1, (void *)&vd->vd_keyboard,
543	    vt_kbdevent, vd);
544	/* XXX: kb_token lost */
545	vd->vd_keyboard = idx0;
546	if (idx0 != -1) {
547		DPRINTF(20, "%s: kbdmux allocated, idx = %d\n", __func__, idx0);
548		k0 = kbd_get_keyboard(idx0);
549
550		for (idx = kbd_find_keyboard2("*", -1, 0);
551		     idx != -1;
552		     idx = kbd_find_keyboard2("*", -1, idx + 1)) {
553			k = kbd_get_keyboard(idx);
554
555			if (idx == idx0 || KBD_IS_BUSY(k))
556				continue;
557
558			bzero(&ki, sizeof(ki));
559			strcpy(ki.kb_name, k->kb_name);
560			ki.kb_unit = k->kb_unit;
561
562			kbdd_ioctl(k0, KBADDKBD, (caddr_t) &ki);
563		}
564	} else {
565		DPRINTF(20, "%s: no kbdmux allocated\n", __func__);
566		idx0 = kbd_allocate("*", -1, (void *)&vd->vd_keyboard,
567		    vt_kbdevent, vd);
568	}
569	DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard);
570
571	return (idx0);
572}
573
574static void
575vtterm_bell(struct terminal *tm)
576{
577
578	sysbeep(1193182 / VT_BELLPITCH, VT_BELLDURATION);
579}
580
581static void
582vtterm_cursor(struct terminal *tm, const term_pos_t *p)
583{
584	struct vt_window *vw = tm->tm_softc;
585
586	vtbuf_cursor_position(&vw->vw_buf, p);
587}
588
589static void
590vtterm_putchar(struct terminal *tm, const term_pos_t *p, term_char_t c)
591{
592	struct vt_window *vw = tm->tm_softc;
593
594	vtbuf_putchar(&vw->vw_buf, p, c);
595}
596
597static void
598vtterm_fill(struct terminal *tm, const term_rect_t *r, term_char_t c)
599{
600	struct vt_window *vw = tm->tm_softc;
601
602	vtbuf_fill_locked(&vw->vw_buf, r, c);
603}
604
605static void
606vtterm_copy(struct terminal *tm, const term_rect_t *r,
607    const term_pos_t *p)
608{
609	struct vt_window *vw = tm->tm_softc;
610
611	vtbuf_copy(&vw->vw_buf, r, p);
612}
613
614static void
615vtterm_param(struct terminal *tm, int cmd, unsigned int arg)
616{
617	struct vt_window *vw = tm->tm_softc;
618
619	switch (cmd) {
620	case TP_SHOWCURSOR:
621		vtbuf_cursor_visibility(&vw->vw_buf, arg);
622		break;
623	}
624}
625
626static inline void
627vt_determine_colors(term_char_t c, int cursor,
628    term_color_t *fg, term_color_t *bg)
629{
630
631	*fg = TCHAR_FGCOLOR(c);
632	if (TCHAR_FORMAT(c) & TF_BOLD)
633		*fg = TCOLOR_LIGHT(*fg);
634	*bg = TCHAR_BGCOLOR(c);
635
636	if (TCHAR_FORMAT(c) & TF_REVERSE) {
637		term_color_t tmp;
638
639		tmp = *fg;
640		*fg = *bg;
641		*bg = tmp;
642	}
643
644	if (cursor) {
645		*fg = *bg;
646		*bg = TC_WHITE;
647	}
648}
649
650static void
651vt_bitblt_char(struct vt_device *vd, struct vt_font *vf, term_char_t c,
652    int iscursor, unsigned int row, unsigned int col)
653{
654	term_color_t fg, bg;
655
656	vt_determine_colors(c, iscursor, &fg, &bg);
657
658	if (vf != NULL) {
659		const uint8_t *src;
660		vt_axis_t top, left;
661
662		src = vtfont_lookup(vf, c);
663
664		/*
665		 * Align the terminal to the centre of the screen.
666		 * Fonts may not always be able to fill the entire
667		 * screen.
668		 */
669		top = row * vf->vf_height + vd->vd_offset.tp_row;
670		left = col * vf->vf_width + vd->vd_offset.tp_col;
671
672		vd->vd_driver->vd_bitbltchr(vd, src, NULL, 0, top, left,
673		    vf->vf_width, vf->vf_height, fg, bg);
674	} else {
675		vd->vd_driver->vd_putchar(vd, TCHAR_CHARACTER(c),
676		    row, col, fg, bg);
677	}
678}
679
680static void
681vt_flush(struct vt_device *vd)
682{
683	struct vt_window *vw = vd->vd_curwindow;
684	struct vt_font *vf = vw->vw_font;
685	struct vt_bufmask tmask;
686	struct mouse_cursor *m;
687	unsigned int row, col;
688	term_rect_t tarea;
689	term_pos_t size;
690	term_char_t *r;
691	int bpl, h, w;
692
693	if (vd->vd_flags & VDF_SPLASH || vw->vw_flags & VWF_BUSY)
694		return;
695
696	vtbuf_undirty(&vw->vw_buf, &tarea, &tmask);
697	vt_termsize(vd, vf, &size);
698
699	/* Force a full redraw when the screen contents are invalid. */
700	if (vd->vd_flags & VDF_INVALID) {
701		tarea.tr_begin.tp_row = tarea.tr_begin.tp_col = 0;
702		tarea.tr_end = size;
703		tmask.vbm_row = tmask.vbm_col = VBM_DIRTY;
704
705		vd->vd_flags &= ~VDF_INVALID;
706	}
707
708	/* Mark last mouse position as dirty to erase. */
709	vtbuf_mouse_cursor_position(&vw->vw_buf, vd->vd_mdirtyx, vd->vd_mdirtyy);
710
711	for (row = tarea.tr_begin.tp_row; row < tarea.tr_end.tp_row; row++) {
712		if (!VTBUF_DIRTYROW(&tmask, row))
713			continue;
714		r = VTBUF_GET_ROW(&vw->vw_buf, row);
715		for (col = tarea.tr_begin.tp_col;
716		    col < tarea.tr_end.tp_col; col++) {
717			if (!VTBUF_DIRTYCOL(&tmask, col))
718				continue;
719
720			vt_bitblt_char(vd, vf, r[col],
721			    VTBUF_ISCURSOR(&vw->vw_buf, row, col), row, col);
722		}
723	}
724
725	/* No mouse for DDB. */
726	if (kdb_active || panicstr != NULL)
727		return;
728
729	if ((vd->vd_flags & (VDF_MOUSECURSOR|VDF_TEXTMODE)) ==
730	    VDF_MOUSECURSOR) {
731		m = &vt_default_mouse_pointer;
732		bpl = (m->w + 7) >> 3; /* Bytes per sorce line. */
733		w = m->w;
734		h = m->h;
735
736		if ((vd->vd_mx + m->w) > (size.tp_col * vf->vf_width))
737			w = (size.tp_col * vf->vf_width) - vd->vd_mx - 1;
738		if ((vd->vd_my + m->h) > (size.tp_row * vf->vf_height))
739			h = (size.tp_row * vf->vf_height) - vd->vd_my - 1;
740
741		vd->vd_driver->vd_bitbltchr(vd, m->map, m->mask, bpl,
742		    vd->vd_offset.tp_row + vd->vd_my,
743		    vd->vd_offset.tp_col + vd->vd_mx,
744		    w, h, TC_WHITE, TC_BLACK);
745		/* Save point of last mouse cursor to erase it later. */
746		vd->vd_mdirtyx = vd->vd_mx / vf->vf_width;
747		vd->vd_mdirtyy = vd->vd_my / vf->vf_height;
748	}
749}
750
751static void
752vt_timer(void *arg)
753{
754	struct vt_device *vd;
755
756	vd = arg;
757	/* Update screen if required. */
758	vt_flush(vd);
759	/* Schedule for next update. */
760	callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ);
761}
762
763static void
764vtterm_done(struct terminal *tm)
765{
766	struct vt_window *vw = tm->tm_softc;
767	struct vt_device *vd = vw->vw_device;
768
769	if (kdb_active || panicstr != NULL) {
770		/* Switch to the debugger. */
771		if (vd->vd_curwindow != vw) {
772			vd->vd_curwindow = vw;
773			vd->vd_flags |= VDF_INVALID;
774			if (vd->vd_driver->vd_postswitch)
775				vd->vd_driver->vd_postswitch(vd);
776		}
777		vd->vd_flags &= ~VDF_SPLASH;
778		vt_flush(vd);
779	} else if (!(vd->vd_flags & VDF_ASYNC)) {
780		vt_flush(vd);
781	}
782}
783
784static void
785vtterm_splash(struct vt_device *vd)
786{
787	vt_axis_t top, left;
788
789	/* Display a nice boot splash. */
790	if (!(vd->vd_flags & VDF_TEXTMODE) && (boothowto & RB_MUTE)) {
791
792		top = (vd->vd_height - vt_logo_height) / 2;
793		left = (vd->vd_width - vt_logo_width) / 2;
794		switch (vt_logo_depth) {
795		case 1:
796			/* XXX: Unhardcode colors! */
797			vd->vd_driver->vd_bitbltchr(vd, vt_logo_image, NULL, 0,
798			    top, left, vt_logo_width, vt_logo_height, 0xf, 0x0);
799		}
800		vd->vd_flags |= VDF_SPLASH;
801	}
802}
803
804static void
805vtterm_cnprobe(struct terminal *tm, struct consdev *cp)
806{
807	struct vt_window *vw = tm->tm_softc;
808	struct vt_device *vd = vw->vw_device;
809	struct winsize wsz;
810
811	if (vd->vd_flags & VDF_INITIALIZED)
812		/* Initialization already done. */
813		return;
814
815	cp->cn_pri = vd->vd_driver->vd_init(vd);
816	if (cp->cn_pri == CN_DEAD) {
817		vd->vd_flags |= VDF_DEAD;
818		return;
819	}
820
821	/* Initialize any early-boot keyboard drivers */
822	kbd_configure(KB_CONF_PROBE_ONLY);
823
824	vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1);
825	vd->vd_windows[VT_CONSWINDOW] = vw;
826	sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw));
827
828	if (!(vd->vd_flags & VDF_TEXTMODE))
829		vw->vw_font = vtfont_ref(&vt_font_default);
830
831	vtbuf_init_early(&vw->vw_buf);
832	vt_winsize(vd, vw->vw_font, &wsz);
833	terminal_set_winsize(tm, &wsz);
834
835	vtterm_splash(vd);
836
837	vd->vd_flags |= VDF_INITIALIZED;
838	main_vd = vd;
839}
840
841static int
842vtterm_cngetc(struct terminal *tm)
843{
844	struct vt_window *vw = tm->tm_softc;
845	struct vt_device *vd = vw->vw_device;
846	keyboard_t *kbd;
847	int state;
848	u_int c;
849
850	if (vw->vw_kbdsq && *vw->vw_kbdsq)
851		return (*vw->vw_kbdsq++);
852
853	state = 0;
854	/* Make sure the splash screen is not there. */
855	if (vd->vd_flags & VDF_SPLASH) {
856		/* Remove splash */
857		vd->vd_flags &= ~VDF_SPLASH;
858		/* Mark screen as invalid to force update */
859		vd->vd_flags |= VDF_INVALID;
860		vt_flush(vd);
861	}
862
863	/* Stripped down keyboard handler. */
864	kbd = kbd_get_keyboard(vd->vd_keyboard);
865	if (kbd == NULL)
866		return (-1);
867
868	/* Force keyboard input mode to K_XLATE */
869	c = K_XLATE;
870	kbdd_ioctl(kbd, KDSKBMODE, (void *)&c);
871
872	/* Switch the keyboard to polling to make it work here. */
873	kbdd_poll(kbd, TRUE);
874	c = kbdd_read_char(kbd, 0);
875	kbdd_poll(kbd, FALSE);
876	if (c & RELKEY)
877		return (-1);
878
879	if (vw->vw_flags & VWF_SCROLL) {
880		vt_scrollmode_kbdevent(vw, c, 1/* Console mode */);
881		vt_flush(vd);
882		return (-1);
883	}
884
885	/* Stripped down handling of vt_kbdevent(), without locking, etc. */
886	if (c & SPCLKEY) {
887		switch (c) {
888		case SPCLKEY | SLK:
889			kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state);
890			if (state & SLKED) {
891				/* Turn scrolling on. */
892				vw->vw_flags |= VWF_SCROLL;
893				VTBUF_SLCK_ENABLE(&vw->vw_buf);
894			} else {
895				/* Turn scrolling off. */
896				vt_scroll(vw, 0, VHS_END);
897				vw->vw_flags &= ~VWF_SCROLL;
898				VTBUF_SLCK_DISABLE(&vw->vw_buf);
899			}
900			break;
901		/* XXX: KDB can handle history. */
902		case SPCLKEY | FKEY | F(50): /* Arrow up. */
903			vw->vw_kbdsq = "\x1b[A";
904			break;
905		case SPCLKEY | FKEY | F(58): /* Arrow down. */
906			vw->vw_kbdsq = "\x1b[B";
907			break;
908		case SPCLKEY | FKEY | F(55): /* Arrow right. */
909			vw->vw_kbdsq = "\x1b[C";
910			break;
911		case SPCLKEY | FKEY | F(53): /* Arrow left. */
912			vw->vw_kbdsq = "\x1b[D";
913			break;
914		}
915
916		/* Force refresh to make scrollback work. */
917		vt_flush(vd);
918	} else if (KEYFLAGS(c) == 0) {
919		return KEYCHAR(c);
920	}
921
922	if (vw->vw_kbdsq && *vw->vw_kbdsq)
923		return (*
924		vw->vw_kbdsq++);
925
926	return (-1);
927}
928
929static void
930vtterm_opened(struct terminal *tm, int opened)
931{
932	struct vt_window *vw = tm->tm_softc;
933	struct vt_device *vd = vw->vw_device;
934
935	VT_LOCK(vd);
936	vd->vd_flags &= ~VDF_SPLASH;
937	if (opened)
938		vw->vw_flags |= VWF_OPENED;
939	else {
940		vw->vw_flags &= ~VWF_OPENED;
941		/* TODO: finish ACQ/REL */
942	}
943	VT_UNLOCK(vd);
944}
945
946static int
947vt_change_font(struct vt_window *vw, struct vt_font *vf)
948{
949	struct vt_device *vd = vw->vw_device;
950	struct terminal *tm = vw->vw_terminal;
951	term_pos_t size;
952	struct winsize wsz;
953
954	/*
955	 * Changing fonts.
956	 *
957	 * Changing fonts is a little tricky.  We must prevent
958	 * simultaneous access to the device, so we must stop
959	 * the display timer and the terminal from accessing.
960	 * We need to switch fonts and grow our screen buffer.
961	 *
962	 * XXX: Right now the code uses terminal_mute() to
963	 * prevent data from reaching the console driver while
964	 * resizing the screen buffer.  This isn't elegant...
965	 */
966
967	VT_LOCK(vd);
968	if (vw->vw_flags & VWF_BUSY) {
969		/* Another process is changing the font. */
970		VT_UNLOCK(vd);
971		return (EBUSY);
972	}
973	if (vw->vw_font == NULL) {
974		/* Our device doesn't need fonts. */
975		VT_UNLOCK(vd);
976		return (ENOTTY);
977	}
978	vw->vw_flags |= VWF_BUSY;
979	VT_UNLOCK(vd);
980
981	vt_termsize(vd, vf, &size);
982	vt_winsize(vd, vf, &wsz);
983	/* Save offset to font aligned area. */
984	vd->vd_offset.tp_col = (vd->vd_width % vf->vf_width) / 2;
985	vd->vd_offset.tp_row = (vd->vd_height % vf->vf_height) / 2;
986
987	/* Grow the screen buffer and terminal. */
988	terminal_mute(tm, 1);
989	vtbuf_grow(&vw->vw_buf, &size, vw->vw_buf.vb_history_size);
990	terminal_set_winsize_blank(tm, &wsz, 0);
991	terminal_mute(tm, 0);
992
993	/* Actually apply the font to the current window. */
994	VT_LOCK(vd);
995	vtfont_unref(vw->vw_font);
996	vw->vw_font = vtfont_ref(vf);
997
998	/* Force a full redraw the next timer tick. */
999	if (vd->vd_curwindow == vw)
1000		vd->vd_flags |= VDF_INVALID;
1001	vw->vw_flags &= ~VWF_BUSY;
1002	VT_UNLOCK(vd);
1003	return (0);
1004}
1005
1006static int
1007vt_proc_alive(struct vt_window *vw)
1008{
1009	struct proc *p;
1010
1011	if (vw->vw_smode.mode != VT_PROCESS)
1012		return FALSE;
1013
1014	if (vw->vw_proc) {
1015		if ((p = pfind(vw->vw_pid)) != NULL)
1016			PROC_UNLOCK(p);
1017		if (vw->vw_proc == p)
1018			return TRUE;
1019		vw->vw_proc = NULL;
1020		vw->vw_smode.mode = VT_AUTO;
1021		DPRINTF(1, "vt controlling process %d died\n", vw->vw_pid);
1022		vw->vw_pid = 0;
1023	}
1024	return FALSE;
1025}
1026
1027static int
1028signal_vt_rel(struct vt_window *vw)
1029{
1030
1031	if (vw->vw_smode.mode != VT_PROCESS)
1032		return FALSE;
1033	if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) {
1034		vw->vw_proc = NULL;
1035		vw->vw_pid = 0;
1036		return TRUE;
1037	}
1038	vw->vw_flags |= VWF_SWWAIT_REL;
1039	PROC_LOCK(vw->vw_proc);
1040	kern_psignal(vw->vw_proc, vw->vw_smode.relsig);
1041	PROC_UNLOCK(vw->vw_proc);
1042	DPRINTF(1, "sending relsig to %d\n", vw->vw_pid);
1043	return TRUE;
1044}
1045
1046static int
1047signal_vt_acq(struct vt_window *vw)
1048{
1049
1050	if (vw->vw_smode.mode != VT_PROCESS)
1051		return FALSE;
1052	if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW])
1053		cnavailable(vw->vw_terminal->consdev, FALSE);
1054	if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) {
1055		vw->vw_proc = NULL;
1056		vw->vw_pid = 0;
1057		return TRUE;
1058	}
1059	vw->vw_flags |= VWF_SWWAIT_ACQ;
1060	PROC_LOCK(vw->vw_proc);
1061	kern_psignal(vw->vw_proc, vw->vw_smode.acqsig);
1062	PROC_UNLOCK(vw->vw_proc);
1063	DPRINTF(1, "sending acqsig to %d\n", vw->vw_pid);
1064	return TRUE;
1065}
1066
1067static int
1068finish_vt_rel(struct vt_window *vw, int release, int *s)
1069{
1070
1071	if (vw->vw_flags & VWF_SWWAIT_REL) {
1072		vw->vw_flags &= ~VWF_SWWAIT_REL;
1073		if (release) {
1074			callout_drain(&vw->vw_proc_dead_timer);
1075			vt_late_window_switch(vw->vw_switch_to);
1076		}
1077		return 0;
1078	}
1079	return EINVAL;
1080}
1081
1082static int
1083finish_vt_acq(struct vt_window *vw)
1084{
1085
1086	if (vw->vw_flags & VWF_SWWAIT_ACQ) {
1087		vw->vw_flags &= ~VWF_SWWAIT_ACQ;
1088		return 0;
1089	}
1090	return EINVAL;
1091}
1092
1093void
1094vt_mouse_event(int type, int x, int y, int event, int cnt)
1095{
1096	struct vt_device *vd;
1097	struct vt_window *vw;
1098	struct vt_font *vf;
1099	term_pos_t size;
1100	term_char_t *buf;
1101	int i, len, mark;
1102
1103	vd = main_vd;
1104	vw = vd->vd_curwindow;
1105	vf = vw->vw_font;
1106
1107	if (vf == NULL)	/* Text mode. */
1108		return;
1109
1110	/*
1111	 * TODO: add flag about pointer position changed, to not redraw chars
1112	 * under mouse pointer when nothing changed.
1113	 */
1114
1115	switch (type) {
1116	case MOUSE_ACTION:
1117	case MOUSE_MOTION_EVENT:
1118		/* Movement */
1119		x += vd->vd_mx;
1120		y += vd->vd_my;
1121
1122		vt_termsize(vd, vf, &size);
1123
1124		/* Apply limits. */
1125		x = MAX(x, 0);
1126		y = MAX(y, 0);
1127		x = MIN(x, (size.tp_col * vf->vf_width) - 1);
1128		y = MIN(y, (size.tp_row * vf->vf_height) - 1);
1129
1130		vd->vd_mx = x;
1131		vd->vd_my = y;
1132		if ((vd->vd_mstate & MOUSE_BUTTON1DOWN) &&
1133		    (vtbuf_set_mark(&vw->vw_buf, VTB_MARK_MOVE,
1134			vd->vd_mx / vf->vf_width,
1135			vd->vd_my / vf->vf_height) == 1)) {
1136
1137			/*
1138			 * We have something marked to copy, so update pointer
1139			 * to window with selection.
1140			 */
1141			vd->vd_markedwin = vw;
1142		}
1143		return; /* Done */
1144	case MOUSE_BUTTON_EVENT:
1145		/* Buttons */
1146		break;
1147	default:
1148		return; /* Done */
1149	}
1150
1151	switch (event) {
1152	case MOUSE_BUTTON1DOWN:
1153		switch (cnt % 4) {
1154		case 0:	/* up */
1155			mark = VTB_MARK_END;
1156			break;
1157		case 1: /* single click: start cut operation */
1158			mark = VTB_MARK_START;
1159			break;
1160		case 2:	/* double click: cut a word */
1161			mark = VTB_MARK_WORD;
1162			break;
1163		case 3:	/* triple click: cut a line */
1164			mark = VTB_MARK_ROW;
1165			break;
1166		}
1167		break;
1168	case VT_MOUSE_PASTEBUTTON:
1169		switch (cnt) {
1170		case 0:	/* up */
1171			break;
1172		default:
1173			if (vd->vd_markedwin == NULL)
1174				return;
1175			/* Get current selecton size in bytes. */
1176			len = vtbuf_get_marked_len(&vd->vd_markedwin->vw_buf);
1177			if (len <= 0)
1178				return;
1179
1180			buf = malloc(len, M_VT, M_WAITOK | M_ZERO);
1181			/* Request cupy/paste buffer data, no more than `len' */
1182			vtbuf_extract_marked(&vd->vd_markedwin->vw_buf, buf,
1183			    len);
1184
1185			len /= sizeof(term_char_t);
1186			for (i = 0; i < len; i++ ) {
1187				if (buf[i] == '\0')
1188					continue;
1189				terminal_input_char(vw->vw_terminal, buf[i]);
1190			}
1191
1192			/* Done, so cleanup. */
1193			free(buf, M_VT);
1194			break;
1195		}
1196		return; /* Done */
1197	case VT_MOUSE_EXTENDBUTTON:
1198		switch (cnt) {
1199		case 0:	/* up */
1200			if (!(vd->vd_mstate & MOUSE_BUTTON1DOWN))
1201				mark = VTB_MARK_EXTEND;
1202			else
1203				mark = 0;
1204			break;
1205		default:
1206			mark = VTB_MARK_EXTEND;
1207			break;
1208		}
1209		break;
1210	default:
1211		return; /* Done */
1212	}
1213
1214	/* Save buttons state. */
1215	if (cnt > 0)
1216		vd->vd_mstate |= event;
1217	else
1218		vd->vd_mstate &= ~event;
1219
1220	if (vtbuf_set_mark(&vw->vw_buf, mark, vd->vd_mx / vf->vf_width,
1221	    vd->vd_my / vf->vf_height) == 1) {
1222		/*
1223		 * We have something marked to copy, so update pointer to
1224		 * window with selection.
1225		 */
1226		vd->vd_markedwin = vw;
1227	}
1228}
1229
1230static int
1231vtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data,
1232    struct thread *td)
1233{
1234	struct vt_window *vw = tm->tm_softc;
1235	struct vt_device *vd = vw->vw_device;
1236	int error, s;
1237
1238	switch (cmd) {
1239	case GIO_KEYMAP:
1240	case PIO_KEYMAP:
1241	case GIO_DEADKEYMAP:
1242	case PIO_DEADKEYMAP:
1243	case GETFKEY:
1244	case SETFKEY:
1245	case KDGKBINFO: {
1246		keyboard_t *kbd;
1247		error = 0;
1248
1249		mtx_lock(&Giant);
1250		kbd = kbd_get_keyboard(vd->vd_keyboard);
1251		if (kbd != NULL)
1252			error = kbdd_ioctl(kbd, cmd, data);
1253		mtx_unlock(&Giant);
1254		if (error == ENOIOCTL)
1255			return (ENODEV);
1256		return (error);
1257	}
1258	case KDGKBMODE: {
1259		int mode = -1;
1260		keyboard_t *kbd;
1261
1262		mtx_lock(&Giant);
1263		kbd = kbd_get_keyboard(vd->vd_keyboard);
1264		if (kbd != NULL) {
1265			kbdd_ioctl(kbd, KDGKBMODE, (void *)&mode);
1266		}
1267		mtx_unlock(&Giant);
1268		DPRINTF(20, "mode %d, vw_kbdmode %d\n", mode, vw->vw_kbdmode);
1269		*(int *)data = mode;
1270		return (0);
1271	}
1272	case KDSKBMODE: {
1273		int mode;
1274
1275		mode = *(int *)data;
1276		switch (mode) {
1277		case K_XLATE:
1278		case K_RAW:
1279		case K_CODE:
1280			vw->vw_kbdmode = mode;
1281			if (vw == vd->vd_curwindow) {
1282				keyboard_t *kbd;
1283				error = 0;
1284
1285				DPRINTF(20, "%s: vd_keyboard = %d\n", __func__,
1286				    vd->vd_keyboard);
1287				mtx_lock(&Giant);
1288				kbd = kbd_get_keyboard(vd->vd_keyboard);
1289				if (kbd != NULL) {
1290					DPRINTF(20, "kbdd_ioctl(KDSKBMODE, %d)\n", mode);
1291					error = kbdd_ioctl(kbd, KDSKBMODE,
1292					    (void *)&mode);
1293				}
1294				mtx_unlock(&Giant);
1295				if (error)
1296					DPRINTF(20, "kbdd_ioctl(KDSKBMODE) "
1297					    "return %d\n", error);
1298			}
1299			return (0);
1300		default:
1301			return (EINVAL);
1302		}
1303	}
1304	case CONS_BLANKTIME:
1305		/* XXX */
1306		return (0);
1307	case CONS_GET:
1308		/* XXX */
1309		*(int *)data = M_CG640x480;
1310		return (0);
1311	case CONS_GETINFO: {
1312		vid_info_t *vi = (vid_info_t *)data;
1313
1314		vi->m_num = vd->vd_curwindow->vw_number + 1;
1315		/* XXX: other fields! */
1316		return (0);
1317	}
1318	case CONS_GETVERS:
1319		*(int *)data = 0x200;
1320		return 0;
1321	case CONS_MODEINFO:
1322		/* XXX */
1323		return (0);
1324	case CONS_MOUSECTL: {
1325		mouse_info_t *mouse = (mouse_info_t*)data;
1326
1327		/*
1328		 * This has no effect on vt(4).  We don't draw any mouse
1329		 * cursor.  Just ignore MOUSE_HIDE and MOUSE_SHOW to
1330		 * prevent excessive errors.  All the other commands
1331		 * should not be applied to individual TTYs, but only to
1332		 * consolectl.
1333		 */
1334		switch (mouse->operation) {
1335		case MOUSE_HIDE:
1336			vd->vd_flags &= ~VDF_MOUSECURSOR;
1337			return (0);
1338		case MOUSE_SHOW:
1339			vd->vd_mx = vd->vd_width / 2;
1340			vd->vd_my = vd->vd_height / 2;
1341			vd->vd_flags |= VDF_MOUSECURSOR;
1342			return (0);
1343		default:
1344			return (EINVAL);
1345		}
1346	}
1347	case PIO_VFONT: {
1348		struct vt_font *vf;
1349
1350		error = vtfont_load((void *)data, &vf);
1351		if (error != 0)
1352			return (error);
1353
1354		error = vt_change_font(vw, vf);
1355		vtfont_unref(vf);
1356		return (error);
1357	}
1358	case GIO_SCRNMAP: {
1359		scrmap_t *sm = (scrmap_t *)data;
1360		int i;
1361
1362		/* We don't have screen maps, so return a handcrafted one. */
1363		for (i = 0; i < 256; i++)
1364			sm->scrmap[i] = i;
1365		return (0);
1366	}
1367	case KDGETLED:
1368		/* XXX */
1369		return (0);
1370	case KDSETLED:
1371		/* XXX */
1372		return (0);
1373	case KDSETMODE:
1374		/* XXX */
1375		return (0);
1376	case KDSETRAD:
1377		/* XXX */
1378		return (0);
1379	case VT_ACTIVATE: {
1380		int win;
1381		win = *(int *)data - 1;
1382		DPRINTF(5, "%s%d: VT_ACTIVATE ttyv%d ", SC_DRIVER_NAME, VT_UNIT(vw), win);
1383		if ((win > VT_MAXWINDOWS) || (win < 0))
1384			return (EINVAL);
1385		return (vt_proc_window_switch(vd->vd_windows[win]));
1386	}
1387	case VT_GETACTIVE:
1388		*(int *)data = vd->vd_curwindow->vw_number + 1;
1389		return (0);
1390	case VT_GETINDEX:
1391		*(int *)data = vw->vw_number + 1;
1392		return (0);
1393	case VT_LOCKSWITCH:
1394		/* TODO: Check current state, switching can be in progress. */
1395		if ((*(int *)data) & 0x01)
1396			vw->vw_flags |= VWF_VTYLOCK;
1397		else
1398			vw->vw_flags &= ~VWF_VTYLOCK;
1399	case VT_OPENQRY: {
1400		unsigned int i;
1401
1402		VT_LOCK(vd);
1403		for (i = 0; i < VT_MAXWINDOWS; i++) {
1404			vw = vd->vd_windows[i];
1405			if (vw == NULL)
1406				continue;
1407			if (!(vw->vw_flags & VWF_OPENED)) {
1408				*(int *)data = vw->vw_number + 1;
1409				VT_UNLOCK(vd);
1410				return (0);
1411			}
1412		}
1413		VT_UNLOCK(vd);
1414		return (EINVAL);
1415	}
1416	case VT_WAITACTIVE: {
1417		unsigned int i;
1418		error = 0;
1419
1420		i = *(unsigned int *)data;
1421		if (i > VT_MAXWINDOWS)
1422			return (EINVAL);
1423		if (i != 0)
1424			vw = vd->vd_windows[i - 1];
1425
1426		VT_LOCK(vd);
1427		while (vd->vd_curwindow != vw && error == 0)
1428			error = cv_wait_sig(&vd->vd_winswitch, &vd->vd_lock);
1429		VT_UNLOCK(vd);
1430		return (error);
1431	}
1432	case VT_SETMODE:    	/* set screen switcher mode */
1433	{
1434		struct vt_mode *mode;
1435		struct proc *p1;
1436
1437		mode = (struct vt_mode *)data;
1438		DPRINTF(5, "%s%d: VT_SETMODE ", SC_DRIVER_NAME, VT_UNIT(vw));
1439		if (vw->vw_smode.mode == VT_PROCESS) {
1440			p1 = pfind(vw->vw_pid);
1441			if (vw->vw_proc == p1 && vw->vw_proc != td->td_proc) {
1442				if (p1)
1443					PROC_UNLOCK(p1);
1444				DPRINTF(5, "error EPERM\n");
1445				return (EPERM);
1446			}
1447			if (p1)
1448				PROC_UNLOCK(p1);
1449		}
1450		if (mode->mode == VT_AUTO) {
1451			vw->vw_smode.mode = VT_AUTO;
1452			vw->vw_proc = NULL;
1453			vw->vw_pid = 0;
1454			DPRINTF(5, "VT_AUTO, ");
1455			if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW])
1456				cnavailable(vw->vw_terminal->consdev, TRUE);
1457			/* were we in the middle of the vty switching process? */
1458			if (finish_vt_rel(vw, TRUE, &s) == 0)
1459				DPRINTF(5, "reset WAIT_REL, ");
1460			if (finish_vt_acq(vw) == 0)
1461				DPRINTF(5, "reset WAIT_ACQ, ");
1462			return (0);
1463		} else if (mode->mode == VT_PROCESS) {
1464			if (!ISSIGVALID(mode->relsig) ||
1465			    !ISSIGVALID(mode->acqsig) ||
1466			    !ISSIGVALID(mode->frsig)) {
1467				DPRINTF(5, "error EINVAL\n");
1468				return (EINVAL);
1469			}
1470			DPRINTF(5, "VT_PROCESS %d, ", td->td_proc->p_pid);
1471			bcopy(data, &vw->vw_smode, sizeof(struct vt_mode));
1472			vw->vw_proc = td->td_proc;
1473			vw->vw_pid = vw->vw_proc->p_pid;
1474			if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW])
1475				cnavailable(vw->vw_terminal->consdev, FALSE);
1476		} else {
1477			DPRINTF(5, "VT_SETMODE failed, unknown mode %d\n",
1478			    mode->mode);
1479			return (EINVAL);
1480		}
1481		DPRINTF(5, "\n");
1482		return 0;
1483	}
1484
1485	case VT_GETMODE:	/* get screen switcher mode */
1486		bcopy(&vw->vw_smode, data, sizeof(struct vt_mode));
1487		return 0;
1488
1489	case VT_RELDISP:	/* screen switcher ioctl */
1490		/*
1491		 * This must be the current vty which is in the VT_PROCESS
1492		 * switching mode...
1493		 */
1494		if ((vw != vd->vd_curwindow) || (vw->vw_smode.mode !=
1495		    VT_PROCESS)) {
1496			return EINVAL;
1497		}
1498		/* ...and this process is controlling it. */
1499		if (vw->vw_proc != td->td_proc) {
1500			return EPERM;
1501		}
1502		error = EINVAL;
1503		switch(*(int *)data) {
1504		case VT_FALSE:	/* user refuses to release screen, abort */
1505			if ((error = finish_vt_rel(vw, FALSE, &s)) == 0)
1506				DPRINTF(5, "%s%d: VT_RELDISP: VT_FALSE\n", SC_DRIVER_NAME,
1507				    VT_UNIT(vw));
1508			break;
1509		case VT_TRUE:	/* user has released screen, go on */
1510			/* finish_vt_rel(..., TRUE, ...) should not be locked */
1511			if (vw->vw_flags & VWF_SWWAIT_REL) {
1512				if ((error = finish_vt_rel(vw, TRUE, &s)) == 0)
1513					DPRINTF(5, "%s%d: VT_RELDISP: VT_TRUE\n",
1514					    SC_DRIVER_NAME, VT_UNIT(vw));
1515			} else {
1516				error = EINVAL;
1517			}
1518			return (error);
1519		case VT_ACKACQ:	/* acquire acknowledged, switch completed */
1520			if ((error = finish_vt_acq(vw)) == 0)
1521				DPRINTF(5, "%s%d: VT_RELDISP: VT_ACKACQ\n", SC_DRIVER_NAME,
1522				    VT_UNIT(vw));
1523			break;
1524		default:
1525			break;
1526		}
1527		return error;
1528	}
1529
1530	return (ENOIOCTL);
1531}
1532
1533static struct vt_window *
1534vt_allocate_window(struct vt_device *vd, unsigned int window)
1535{
1536	struct vt_window *vw;
1537	struct terminal *tm;
1538	term_pos_t size;
1539	struct winsize wsz;
1540
1541	vw = malloc(sizeof *vw, M_VT, M_WAITOK|M_ZERO);
1542	vw->vw_device = vd;
1543	vw->vw_number = window;
1544	vw->vw_kbdmode = K_XLATE;
1545
1546	if (!(vd->vd_flags & VDF_TEXTMODE))
1547		vw->vw_font = vtfont_ref(&vt_font_default);
1548
1549	vt_termsize(vd, vw->vw_font, &size);
1550	vt_winsize(vd, vw->vw_font, &wsz);
1551	vtbuf_init(&vw->vw_buf, &size);
1552
1553	tm = vw->vw_terminal = terminal_alloc(&vt_termclass, vw);
1554	terminal_set_winsize(tm, &wsz);
1555	vd->vd_windows[window] = vw;
1556	callout_init(&vw->vw_proc_dead_timer, 0);
1557
1558	return (vw);
1559}
1560
1561void
1562vt_upgrade(struct vt_device *vd)
1563{
1564	struct vt_window *vw;
1565	unsigned int i;
1566
1567	/* Device didn't pass vd_init() or already upgraded. */
1568	if (vd->vd_flags & (VDF_ASYNC|VDF_DEAD))
1569		return;
1570	vd->vd_flags |= VDF_ASYNC;
1571
1572	mtx_init(&vd->vd_lock, "vtdev", NULL, MTX_DEF);
1573	cv_init(&vd->vd_winswitch, "vtwswt");
1574
1575	/* Init 25 Hz timer. */
1576	callout_init_mtx(&vd->vd_timer, &vd->vd_lock, 0);
1577
1578	for (i = 0; i < VT_MAXWINDOWS; i++) {
1579		vw = vd->vd_windows[i];
1580		if (vw == NULL) {
1581			/* New window. */
1582			vw = vt_allocate_window(vd, i);
1583		}
1584		if (i == VT_CONSWINDOW) {
1585			/* Console window. */
1586			EVENTHANDLER_REGISTER(shutdown_pre_sync,
1587			    vt_window_switch, vw, SHUTDOWN_PRI_DEFAULT);
1588		}
1589		terminal_maketty(vw->vw_terminal, "v%r", VT_UNIT(vw));
1590	}
1591	if (vd->vd_curwindow == NULL)
1592		vd->vd_curwindow = vd->vd_windows[VT_CONSWINDOW];
1593
1594	/* Attach keyboard. */
1595	vt_allocate_keyboard(vd);
1596	DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard);
1597
1598	/* Start timer when everything ready. */
1599	callout_reset(&vd->vd_timer, hz / VT_TIMERFREQ, vt_timer, vd);
1600}
1601
1602static void
1603vt_resize(struct vt_device *vd)
1604{
1605	struct vt_window *vw;
1606	int i;
1607
1608	for (i = 0; i < VT_MAXWINDOWS; i++) {
1609		vw = vd->vd_windows[i];
1610		/* Resize terminal windows */
1611		vt_change_font(vw, vw->vw_font);
1612	}
1613}
1614
1615void
1616vt_allocate(struct vt_driver *drv, void *softc)
1617{
1618	struct vt_device *vd;
1619	struct winsize wsz;
1620
1621	if (main_vd == NULL) {
1622		main_vd = malloc(sizeof *vd, M_VT, M_WAITOK|M_ZERO);
1623		printf("%s: VT initialize with new VT driver.\n", __func__);
1624	} else {
1625		/*
1626		 * Check if have rights to replace current driver. For example:
1627		 * it is bad idea to replace KMS driver with generic VGA one.
1628		 */
1629		if (drv->vd_priority <= main_vd->vd_driver->vd_priority) {
1630			printf("%s: Driver priority %d too low. Current %d\n ",
1631			    __func__, drv->vd_priority,
1632			    main_vd->vd_driver->vd_priority);
1633			return;
1634		}
1635		printf("%s: Replace existing VT driver.\n", __func__);
1636	}
1637	vd = main_vd;
1638
1639	/* Stop vt_flush periodic task. */
1640	if (vd->vd_curwindow != NULL)
1641		callout_drain(&vd->vd_timer);
1642
1643	vd->vd_driver = drv;
1644	vd->vd_softc = softc;
1645	vd->vd_driver->vd_init(vd);
1646
1647	vt_upgrade(vd);
1648
1649	/* Refill settings with new sizes. */
1650	vt_resize(vd);
1651
1652	if (vd->vd_flags & VDF_SPLASH)
1653		vtterm_splash(vd);
1654
1655	if (vd->vd_curwindow != NULL)
1656		callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ);
1657
1658	termcn_cnregister(vd->vd_windows[VT_CONSWINDOW]->vw_terminal);
1659
1660	/* Update console window sizes to actual. */
1661	vt_winsize(vd, vd->vd_windows[VT_CONSWINDOW]->vw_font, &wsz);
1662	terminal_set_winsize(vd->vd_windows[VT_CONSWINDOW]->vw_terminal, &wsz);
1663}
1664
1665void
1666vt_suspend()
1667{
1668
1669	if (vt_suspendswitch == 0)
1670		return;
1671	/* Save current window. */
1672	main_vd->vd_savedwindow = main_vd->vd_curwindow;
1673	/* Ask holding process to free window and switch to console window */
1674	vt_proc_window_switch(main_vd->vd_windows[VT_CONSWINDOW]);
1675}
1676
1677void
1678vt_resume()
1679{
1680
1681	if (vt_suspendswitch == 0)
1682		return;
1683	/* Switch back to saved window */
1684	if (main_vd->vd_savedwindow != NULL)
1685		vt_proc_window_switch(main_vd->vd_savedwindow);
1686	main_vd->vd_savedwindow = NULL;
1687}
1688