1/*-
2 * Copyright (c) 2009 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 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: user/ed/newcons/sys/dev/vt/vt_core.c 243802 2012-12-02 22:21:40Z nwhitehorn $");
32
33#include <sys/param.h>
34#include <sys/consio.h>
35#include <sys/eventhandler.h>
36#include <sys/fbio.h>
37#include <sys/kbio.h>
38#include <sys/kdb.h>
39#include <sys/kernel.h>
40#include <sys/lock.h>
41#include <sys/malloc.h>
42#include <sys/mutex.h>
43#include <sys/reboot.h>
44#include <sys/systm.h>
45#include <sys/terminal.h>
46
47#include <dev/kbd/kbdreg.h>
48#include <dev/vt/vt.h>
49
50static tc_bell_t vtterm_bell;
51static tc_cursor_t vtterm_cursor;
52static tc_putchar_t vtterm_putchar;
53static tc_fill_t vtterm_fill;
54static tc_copy_t vtterm_copy;
55static tc_param_t vtterm_param;
56static tc_done_t vtterm_done;
57
58static tc_cnprobe_t vtterm_cnprobe;
59static tc_cngetc_t vtterm_cngetc;
60
61static tc_opened_t vtterm_opened;
62static tc_ioctl_t vtterm_ioctl;
63
64const struct terminal_class vt_termclass = {
65 .tc_bell = vtterm_bell,
66 .tc_cursor = vtterm_cursor,
67 .tc_putchar = vtterm_putchar,
68 .tc_fill = vtterm_fill,
69 .tc_copy = vtterm_copy,
70 .tc_param = vtterm_param,
71 .tc_done = vtterm_done,
72
73 .tc_cnprobe = vtterm_cnprobe,
74 .tc_cngetc = vtterm_cngetc,
75
76 .tc_opened = vtterm_opened,
77 .tc_ioctl = vtterm_ioctl,
78};
79
80/*
81 * Use a constant timer of 25 Hz to redraw the screen.
82 *
83 * XXX: In theory we should only fire up the timer when there is really
84 * activity. Unfortunately we cannot always start timers. We really
85 * don't want to process kernel messages synchronously, because it
86 * really slows down the system.
87 */
88#define VT_TIMERFREQ 25
89
90/* Bell pitch/duration. */
91#define VT_BELLDURATION ((5 * hz + 99) / 100)
92#define VT_BELLPITCH 800
93
94#define VT_LOCK(vd) mtx_lock(&(vd)->vd_lock)
95#define VT_UNLOCK(vd) mtx_unlock(&(vd)->vd_lock)
96
97#define VT_UNIT(vw) ((vw)->vw_device->vd_unit * VT_MAXWINDOWS + \
98 (vw)->vw_number)
99
100static unsigned int vt_unit = 0;
101static MALLOC_DEFINE(M_VT, "vt", "vt device");
102
103/* Boot logo. */
104extern unsigned int vt_logo_width;
105extern unsigned int vt_logo_height;
106extern unsigned int vt_logo_depth;
107extern unsigned char vt_logo_image[];
108
109/* Font. */
110extern struct vt_font vt_font_default;
111
112static void
113vt_window_switch(struct vt_window *vw)
114{
115 struct vt_device *vd = vw->vw_device;
116 keyboard_t *kbd;
117
118 VT_LOCK(vd);
119 if (vd->vd_curwindow == vw ||
120 !(vw->vw_flags & (VWF_OPENED|VWF_CONSOLE))) {
121 VT_UNLOCK(vd);
122 return;
123 }
124 vd->vd_curwindow = vw;
125 vd->vd_flags |= VDF_INVALID;
126 cv_broadcast(&vd->vd_winswitch);
127 VT_UNLOCK(vd);
128
129 /* Restore per-window keyboard mode. */
130 mtx_lock(&Giant);
131 kbd = kbd_get_keyboard(vd->vd_keyboard);
132 if (kbd != NULL)
133 kbdd_ioctl(kbd, KDSKBMODE, (void *)&vw->vw_kbdmode);
134 mtx_unlock(&Giant);
135}
136
137static inline void
138vt_termsize(struct vt_device *vd, struct vt_font *vf, term_pos_t *size)
139{
140
141 size->tp_row = vd->vd_height;
142 size->tp_col = vd->vd_width;
143 if (vf != NULL) {
144 size->tp_row /= vf->vf_height;
145 size->tp_col /= vf->vf_width;
146 }
147}
148
149static inline void
150vt_winsize(struct vt_device *vd, struct vt_font *vf, struct winsize *size)
151{
152
153 size->ws_row = size->ws_ypixel = vd->vd_height;
154 size->ws_col = size->ws_xpixel = vd->vd_width;
155 if (vf != NULL) {
156 size->ws_row /= vf->vf_height;
157 size->ws_col /= vf->vf_width;
158 }
159}
160
161static int
162vt_kbdevent(keyboard_t *kbd, int event, void *arg)
163{
164 struct vt_device *vd = arg;
165 struct vt_window *vw = vd->vd_curwindow;
166 u_int c;
167
168 switch (event) {
169 case KBDIO_KEYINPUT:
170 break;
171 case KBDIO_UNLOADING:
172 mtx_lock(&Giant);
173 vd->vd_keyboard = -1;
174 kbd_release(kbd, (void *)&vd->vd_keyboard);
175 mtx_unlock(&Giant);
176 return (0);
177 default:
178 return (EINVAL);
179 }
180
181 c = kbdd_read_char(kbd, 0);
182 if (c & RELKEY)
183 return (0);
184
185 if (c & SPCLKEY) {
186 c &= ~SPCLKEY;
187
188 if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) {
189 vw = vd->vd_windows[c - F_SCR];
190 if (vw != NULL)
191 vt_window_switch(vw);
192 return (0);
193 }
194
195 switch (c) {
196 case DBG:
197 kdb_enter(KDB_WHY_BREAK, "manual escape to debugger");
198 break;
199 case RBT:
200 /* XXX: Make this configurable! */
201 shutdown_nice(0);
202 break;
203 case HALT:
204 shutdown_nice(RB_HALT);
205 break;
206 case PDWN:
207 shutdown_nice(RB_HALT|RB_POWEROFF);
208 break;
209 case SLK: {
210 int state = 0;
211
212 kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state);
213 VT_LOCK(vd);
214 if (state & SLKED) {
215 /* Turn scrolling on. */
216 vw->vw_flags |= VWF_SCROLL;
217 } else {
218 /* Turn scrolling off. */
219 vw->vw_flags &= ~VWF_SCROLL;
220 vthistory_seek(&vw->vw_history, 0, VHS_SET);
221 }
222 VT_UNLOCK(vd);
223 break;
224 }
225 case FKEY | F(1): case FKEY | F(2): case FKEY | F(3):
226 case FKEY | F(4): case FKEY | F(5): case FKEY | F(6):
227 case FKEY | F(7): case FKEY | F(8): case FKEY | F(9):
228 case FKEY | F(10): case FKEY | F(11): case FKEY | F(12):
229 /* F1 through F12 keys. */
230 terminal_input_special(vw->vw_terminal,
231 TKEY_F1 + c - (FKEY | F(1)));
232 break;
233 case FKEY | F(49): /* Home key. */
234 VT_LOCK(vd);
235 if (vw->vw_flags & VWF_SCROLL) {
236 vthistory_seek(&vw->vw_history, 0, VHS_END);
237 VT_UNLOCK(vd);
238 break;
239 }
240 VT_UNLOCK(vd);
241 terminal_input_special(vw->vw_terminal, TKEY_HOME);
242 break;
243 case FKEY | F(50): /* Arrow up. */
244 VT_LOCK(vd);
245 if (vw->vw_flags & VWF_SCROLL) {
246 vthistory_seek(&vw->vw_history, 1, VHS_CUR);
247 VT_UNLOCK(vd);
248 break;
249 }
250 VT_UNLOCK(vd);
251 terminal_input_special(vw->vw_terminal, TKEY_UP);
252 break;
253 case FKEY | F(51): /* Page up. */
254 VT_LOCK(vd);
255 if (vw->vw_flags & VWF_SCROLL) {
256 term_pos_t size;
257
258 vt_termsize(vd, vw->vw_font, &size);
259 vthistory_seek(&vw->vw_history, size.tp_row,
260 VHS_CUR);
261 VT_UNLOCK(vd);
262 break;
263 }
264 VT_UNLOCK(vd);
265 terminal_input_special(vw->vw_terminal, TKEY_PAGE_UP);
266 break;
267 case FKEY | F(53): /* Arrow left. */
268 terminal_input_special(vw->vw_terminal, TKEY_LEFT);
269 break;
270 case FKEY | F(55): /* Arrow right. */
271 terminal_input_special(vw->vw_terminal, TKEY_RIGHT);
272 break;
273 case FKEY | F(57): /* End key. */
274 VT_LOCK(vd);
275 if (vw->vw_flags & VWF_SCROLL) {
276 vthistory_seek(&vw->vw_history, 0, VHS_SET);
277 VT_UNLOCK(vd);
278 break;
279 }
280 VT_UNLOCK(vd);
281 terminal_input_special(vw->vw_terminal, TKEY_END);
282 break;
283 case FKEY | F(58): /* Arrow down. */
284 VT_LOCK(vd);
285 if (vw->vw_flags & VWF_SCROLL) {
286 vthistory_seek(&vw->vw_history, -1, VHS_CUR);
287 VT_UNLOCK(vd);
288 break;
289 }
290 VT_UNLOCK(vd);
291 terminal_input_special(vw->vw_terminal, TKEY_DOWN);
292 break;
293 case FKEY | F(59): /* Page down. */
294 VT_LOCK(vd);
295 if (vw->vw_flags & VWF_SCROLL) {
296 term_pos_t size;
297
298 vt_termsize(vd, vw->vw_font, &size);
299 vthistory_seek(&vw->vw_history, -size.tp_row,
300 VHS_CUR);
301 VT_UNLOCK(vd);
302 break;
303 }
304 VT_UNLOCK(vd);
305 terminal_input_special(vw->vw_terminal, TKEY_PAGE_DOWN);
306 break;
307 case FKEY | F(60): /* Insert key. */
308 terminal_input_special(vw->vw_terminal, TKEY_INSERT);
309 break;
310 case FKEY | F(61): /* Delete key. */
311 terminal_input_special(vw->vw_terminal, TKEY_DELETE);
312 break;
313 }
314 } else if (KEYFLAGS(c) == 0) {
315 c = KEYCHAR(c);
316
317 /* Don't do UTF-8 conversion when doing raw mode. */
318 if (vw->vw_kbdmode == K_XLATE)
319 terminal_input_char(vw->vw_terminal, c);
320 else
321 terminal_input_raw(vw->vw_terminal, c);
322 }
323
324 return (0);
325}
326
327static int
328vt_allocate_keyboard(struct vt_device *vd)
329{
330 int idx0, idx;
331 keyboard_t *k0, *k;
332 keyboard_info_t ki;
333
334 idx0 = kbd_allocate("kbdmux", -1, (void *)&vd->vd_keyboard,
335 vt_kbdevent, vd);
336 if (idx0 != -1) {
337 k0 = kbd_get_keyboard(idx0);
338
339 for (idx = kbd_find_keyboard2("*", -1, 0);
340 idx != -1;
341 idx = kbd_find_keyboard2("*", -1, idx + 1)) {
342 k = kbd_get_keyboard(idx);
343
344 if (idx == idx0 || KBD_IS_BUSY(k))
345 continue;
346
347 bzero(&ki, sizeof(ki));
348 strcpy(ki.kb_name, k->kb_name);
349 ki.kb_unit = k->kb_unit;
350
351 kbdd_ioctl(k0, KBADDKBD, (caddr_t) &ki);
352 }
353 } else
354 idx0 = kbd_allocate("*", -1, (void *)&vd->vd_keyboard,
355 vt_kbdevent, vd);
356
357 return (idx0);
358}
359
360static void
361vtterm_bell(struct terminal *tm)
362{
363
364 sysbeep(1193182 / VT_BELLPITCH, VT_BELLDURATION);
365}
366
367static void
368vtterm_cursor(struct terminal *tm, const term_pos_t *p)
369{
370 struct vt_window *vw = tm->tm_softc;
371
372 vtbuf_cursor_position(&vw->vw_buf, p);
373}
374
375static void
376vtterm_putchar(struct terminal *tm, const term_pos_t *p, term_char_t c)
377{
378 struct vt_window *vw = tm->tm_softc;
379
380 vtbuf_putchar(&vw->vw_buf, p, c);
381}
382
383static void
384vtterm_fill(struct terminal *tm, const term_rect_t *r, term_char_t c)
385{
386 struct vt_window *vw = tm->tm_softc;
387
388 vtbuf_fill(&vw->vw_buf, r, c);
389}
390
391static void
392vtterm_copy(struct terminal *tm, const term_rect_t *r,
393 const term_pos_t *p)
394{
395 struct vt_window *vw = tm->tm_softc;
396 struct vt_device *vd = vw->vw_device;
397 term_pos_t size;
398
399 /*
400 * We copy lines into the history buffer when we have to do a
401 * copy of the entire width of the screen to a region above it.
402 */
403 vt_termsize(vd, vw->vw_font, &size);
404 if (r->tr_begin.tp_row > p->tp_row &&
405 r->tr_begin.tp_col == 0 && r->tr_end.tp_col == size.tp_col) {
406 term_rect_t area;
407
408 area.tr_begin.tp_row = p->tp_row;
409 area.tr_begin.tp_col = 0;
410 area.tr_end.tp_row = r->tr_begin.tp_row;
411 area.tr_end.tp_col = size.tp_col;
412 vthistory_add(&vw->vw_history, &vw->vw_buf, &area);
413 }
414
415 vtbuf_copy(&vw->vw_buf, r, p);
416}
417
418static void
419vtterm_param(struct terminal *tm, int cmd, unsigned int arg)
420{
421 struct vt_window *vw = tm->tm_softc;
422
423 switch (cmd) {
424 case TP_SHOWCURSOR:
425 vtbuf_cursor_visibility(&vw->vw_buf, arg);
426 break;
427 }
428}
429
430static inline void
431vt_determine_colors(term_char_t c, int cursor,
432 term_color_t *fg, term_color_t *bg)
433{
434
435 *fg = TCHAR_FGCOLOR(c);
436 if (TCHAR_FORMAT(c) & TF_BOLD)
437 *fg = TCOLOR_LIGHT(*fg);
438 *bg = TCHAR_BGCOLOR(c);
439
440 if (TCHAR_FORMAT(c) & TF_REVERSE) {
441 term_color_t tmp;
442
443 tmp = *fg;
444 *fg = *bg;
445 *bg = tmp;
446 }
447
448 if (cursor) {
449 *fg = *bg;
450 *bg = TC_WHITE;
451 }
452}
453
454static void
455vt_bitblt_char(struct vt_device *vd, struct vt_font *vf, term_char_t c,
456 int iscursor, unsigned int row, unsigned int col)
457{
458 term_color_t fg, bg;
459
460 vt_determine_colors(c, iscursor, &fg, &bg);
461
462 if (vf != NULL) {
463 const uint8_t *src;
464 vt_axis_t top, left;
465
466 src = vtfont_lookup(vf, c);
467
468 /*
469 * Align the terminal to the centre of the screen.
470 * Fonts may not always be able to fill the entire
471 * screen.
472 */
473 top = row * vf->vf_height +
474 (vd->vd_height % vf->vf_height) / 2;
475 left = col * vf->vf_width +
476 (vd->vd_width % vf->vf_width) / 2;
477
478 vd->vd_driver->vd_bitblt(vd, src, top, left,
479 vf->vf_width, vf->vf_height, fg, bg);
480 } else {
481 vd->vd_driver->vd_putchar(vd, TCHAR_CHARACTER(c),
482 row, col, fg, bg);
483 }
484}
485
486static void
487vt_flush(struct vt_device *vd)
488{
489 struct vt_window *vw = vd->vd_curwindow;
490 struct vt_font *vf = vw->vw_font;
491 term_pos_t size;
492 term_rect_t tarea;
493 struct vt_bufmask tmask;
494 unsigned int row, col, scrollpos;
495 term_char_t c;
496
497 if (vd->vd_flags & VDF_SPLASH || vw->vw_flags & VWF_BUSY)
498 return;
499
500 vtbuf_undirty(&vw->vw_buf, &tarea, &tmask);
501 vthistory_getpos(&vw->vw_history, &scrollpos);
502 vt_termsize(vd, vf, &size);
503
504 /* Force a full redraw when the screen contents are invalid. */
505 if (vd->vd_scrollpos != scrollpos || vd->vd_flags & VDF_INVALID) {
506 tarea.tr_begin.tp_row = tarea.tr_begin.tp_col = 0;
507 tarea.tr_end = size;
508 tmask.vbm_row = tmask.vbm_col = VBM_DIRTY;
509
510 /*
511 * Blank to prevent borders with artifacts. This is
512 * only required when the font doesn't exactly fill the
513 * screen.
514 */
515 if (vd->vd_flags & VDF_INVALID && vf != NULL &&
516 (vd->vd_width % vf->vf_width != 0 ||
517 vd->vd_height % vf->vf_height != 0))
518 vd->vd_driver->vd_blank(vd, TC_BLACK);
519
520 /* Draw the scrollback history. */
521 for (row = 0; row < scrollpos; row++) {
522 for (col = 0; col < size.tp_col; col++) {
523 c = VTHISTORY_FIELD(&vw->vw_history, row, col);
524 vt_bitblt_char(vd, vf, c, 0, row, col);
525 }
526 }
527
528 vd->vd_flags &= ~VDF_INVALID;
529 vd->vd_scrollpos = scrollpos;
530 }
531
532 /*
533 * Clamp the terminal rendering size if it exceeds the window
534 * size, because of scrollback.
535 */
536 if (tarea.tr_end.tp_row + scrollpos > size.tp_row) {
537 if (size.tp_row <= scrollpos)
538 /* Terminal completely invisible. */
539 tarea.tr_end.tp_row = 0;
540 else
541 /* Terminal partially visible. */
542 tarea.tr_end.tp_row = size.tp_row - scrollpos;
543 }
544
545 for (row = tarea.tr_begin.tp_row; row < tarea.tr_end.tp_row; row++) {
546 if (!VTBUF_DIRTYROW(&tmask, row))
547 continue;
548 for (col = tarea.tr_begin.tp_col;
549 col < tarea.tr_end.tp_col; col++) {
550 if (!VTBUF_DIRTYCOL(&tmask, col))
551 continue;
552
553 c = VTBUF_FIELD(&vw->vw_buf, row, col);
554 vt_bitblt_char(vd, vf, c,
555 VTBUF_ISCURSOR(&vw->vw_buf, row, col),
556 row + scrollpos, col);
557 }
558 }
559}
560
561static void
562vt_timer(void *arg)
563{
564 struct vt_device *vd = arg;
565
566 vt_flush(vd);
567 callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ);
568}
569
570static void
571vtterm_done(struct terminal *tm)
572{
573 struct vt_window *vw = tm->tm_softc;
574 struct vt_device *vd = vw->vw_device;
575
576 if (kdb_active || panicstr != NULL) {
577 /* Switch to the debugger. */
578 if (vd->vd_curwindow != vw) {
579 vd->vd_curwindow = vw;
580 vd->vd_flags |= VDF_INVALID;
581 }
582 vd->vd_flags &= ~VDF_SPLASH;
583 vt_flush(vd);
584 } else if (!(vd->vd_flags & VDF_ASYNC)) {
585 vt_flush(vd);
586 }
587}
588
589static void
590vtterm_cnprobe(struct terminal *tm, struct consdev *cp)
591{
592 struct vt_window *vw = tm->tm_softc;
593 struct vt_device *vd = vw->vw_device;
594 struct winsize wsz;
595
596 cp->cn_pri = vd->vd_driver->vd_init(vd);
597 if (cp->cn_pri == CN_DEAD) {
598 vd->vd_flags |= VDF_DEAD;
599 return;
600 }
601
602 /* Initialize any early-boot keyboard drivers */
603 kbd_configure(KB_CONF_PROBE_ONLY);
604
605 vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1);
606 sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw));
607
608 if (!(vd->vd_flags & VDF_TEXTMODE))
609 vw->vw_font = vtfont_ref(&vt_font_default);
610
611 vtbuf_init_early(&vw->vw_buf);
612 vt_winsize(vd, vw->vw_font, &wsz);
613 terminal_set_winsize(tm, &wsz);
614
615 /* Display a nice boot splash. */
616 if (!(vd->vd_flags & VDF_TEXTMODE)) {
617 vt_axis_t top, left;
618
619 top = (vd->vd_height - vt_logo_height) / 2;
620 left = (vd->vd_width - vt_logo_width) / 2;
621 switch (vt_logo_depth) {
622 case 1:
623 /* XXX: Unhardcode colors! */
624 vd->vd_driver->vd_bitblt(vd, vt_logo_image, top, left,
625 vt_logo_width, vt_logo_height, 0xf, 0x0);
626 }
627 vd->vd_flags |= VDF_SPLASH;
628 }
629}
630
631static int
632vtterm_cngetc(struct terminal *tm)
633{
634 struct vt_window *vw = tm->tm_softc;
635 struct vt_device *vd = vw->vw_device;
636 keyboard_t *kbd;
637 u_int c;
638
639 /* Make sure the splash screen is not there. */
640 if (vd->vd_flags & VDF_SPLASH) {
641 vd->vd_flags &= ~VDF_SPLASH;
642 vt_flush(vd);
643 }
644
645 /* Stripped down keyboard handler. */
646 kbd = kbd_get_keyboard(vd->vd_keyboard);
647 if (kbd == NULL)
648 return (-1);
649
650 /* Switch the keyboard to polling to make it work here. */
651 kbdd_poll(kbd, TRUE);
652 c = kbdd_read_char(kbd, 0);
653 kbdd_poll(kbd, FALSE);
654 if (c & RELKEY)
655 return (-1);
656
657 /* Stripped down handling of vt_kbdevent(), without locking, etc. */
658 if (c & SPCLKEY) {
659 c &= ~SPCLKEY;
660
661 switch (c) {
662 case SLK: {
663 int state = 0;
664
665 kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state);
666 if (state & SLKED) {
667 /* Turn scrolling on. */
668 vw->vw_flags |= VWF_SCROLL;
669 } else {
670 /* Turn scrolling off. */
671 vw->vw_flags &= ~VWF_SCROLL;
672 vthistory_seek(&vw->vw_history, 0, VHS_SET);
673 }
674 break;
675 }
676 case FKEY | F(49): /* Home key. */
677 if (vw->vw_flags & VWF_SCROLL)
678 vthistory_seek(&vw->vw_history, 0, VHS_END);
679 break;
680 case FKEY | F(50): /* Arrow up. */
681 if (vw->vw_flags & VWF_SCROLL)
682 vthistory_seek(&vw->vw_history, 1, VHS_CUR);
683 break;
684 case FKEY | F(51): /* Page up. */
685 if (vw->vw_flags & VWF_SCROLL) {
686 term_pos_t size;
687
688 vt_termsize(vd, vw->vw_font, &size);
689 vthistory_seek(&vw->vw_history, size.tp_row,
690 VHS_CUR);
691 }
692 break;
693 case FKEY | F(57): /* End key. */
694 if (vw->vw_flags & VWF_SCROLL)
695 vthistory_seek(&vw->vw_history, 0, VHS_SET);
696 break;
697 case FKEY | F(58): /* Arrow down. */
698 if (vw->vw_flags & VWF_SCROLL)
699 vthistory_seek(&vw->vw_history, -1, VHS_CUR);
700 break;
701 case FKEY | F(59): /* Page down. */
702 if (vw->vw_flags & VWF_SCROLL) {
703 term_pos_t size;
704
705 vt_termsize(vd, vw->vw_font, &size);
706 vthistory_seek(&vw->vw_history, -size.tp_row,
707 VHS_CUR);
708 }
709 break;
710 }
711
712 /* Force refresh to make scrollback work. */
713 vt_flush(vd);
714 } else if (KEYFLAGS(c) == 0) {
715 return KEYCHAR(c);
716 }
717
718 return (-1);
719}
720
721static void
722vtterm_opened(struct terminal *tm, int opened)
723{
724 struct vt_window *vw = tm->tm_softc;
725 struct vt_device *vd = vw->vw_device;
726
727 VT_LOCK(vd);
728 vd->vd_flags &= ~VDF_SPLASH;
729 if (opened)
730 vw->vw_flags |= VWF_OPENED;
731 else
732 vw->vw_flags &= ~VWF_OPENED;
733 VT_UNLOCK(vd);
734}
735
736static int
737vt_change_font(struct vt_window *vw, struct vt_font *vf)
738{
739 struct vt_device *vd = vw->vw_device;
740 struct terminal *tm = vw->vw_terminal;
741 term_pos_t size;
742 struct winsize wsz;
743
744 /*
745 * Changing fonts.
746 *
747 * Changing fonts is a little tricky. We must prevent
748 * simultaneous access to the device, so we must stop
749 * the display timer and the terminal from accessing.
750 * We need to switch fonts and grow our screen buffer.
751 *
752 * XXX: Right now the code uses terminal_mute() to
753 * prevent data from reaching the console driver while
754 * resizing the screen buffer. This isn't elegant...
755 */
756
757 VT_LOCK(vd);
758 if (vw->vw_flags & VWF_BUSY) {
759 /* Another process is changing the font. */
760 VT_UNLOCK(vd);
761 return (EBUSY);
762 }
763 if (vw->vw_font == NULL) {
764 /* Our device doesn't need fonts. */
765 VT_UNLOCK(vd);
766 return (ENOTTY);
767 }
768 vw->vw_flags |= VWF_BUSY;
769 VT_UNLOCK(vd);
770
771 vt_termsize(vd, vf, &size);
772 vt_winsize(vd, vf, &wsz);
773
774 /* Grow the screen buffer and terminal. */
775 terminal_mute(tm, 1);
776 vtbuf_grow(&vw->vw_buf, &size);
777 terminal_set_winsize(tm, &wsz);
778 terminal_mute(tm, 0);
779
780 /* Actually apply the font to the current window. */
781 VT_LOCK(vd);
782 vtfont_unref(vw->vw_font);
783 vw->vw_font = vtfont_ref(vf);
784
785 /* Force a full redraw the next timer tick. */
786 if (vd->vd_curwindow == vw)
787 vd->vd_flags |= VDF_INVALID;
788 vw->vw_flags &= ~VWF_BUSY;
789 VT_UNLOCK(vd);
790 return (0);
791}
792
793static int
794vtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data,
795 struct thread *td)
796{
797 struct vt_window *vw = tm->tm_softc;
798 struct vt_device *vd = vw->vw_device;
799
800 switch (cmd) {
801 case GIO_KEYMAP:
802 case PIO_KEYMAP:
803 case GIO_DEADKEYMAP:
804 case PIO_DEADKEYMAP:
805 case GETFKEY:
806 case SETFKEY:
807 case KDGKBINFO: {
808 keyboard_t *kbd;
809 int error = 0;
810
811 mtx_lock(&Giant);
812 kbd = kbd_get_keyboard(vd->vd_keyboard);
813 if (kbd != NULL)
814 error = kbdd_ioctl(kbd, cmd, data);
815 mtx_unlock(&Giant);
816 if (error == ENOIOCTL)
817 return (ENODEV);
818 return (error);
819 }
820 case KDSKBMODE: {
821 int mode;
822
823 mode = *(int *)data;
824 switch (mode) {
825 case K_XLATE:
826 case K_RAW:
827 case K_CODE:
828 vw->vw_kbdmode = mode;
829 if (vw == vd->vd_curwindow) {
830 keyboard_t *kbd;
831
832 mtx_lock(&Giant);
833 kbd = kbd_get_keyboard(vd->vd_keyboard);
834 if (kbd != NULL)
835 kbdd_ioctl(kbd, KDSKBMODE,
836 (void *)&mode);
837 mtx_unlock(&Giant);
838 }
839 return (0);
840 default:
841 return (EINVAL);
842 }
843 }
844 case CONS_BLANKTIME:
845 /* XXX */
846 return (0);
847 case CONS_GET:
848 /* XXX */
849 *(int *)data = M_CG640x480;
850 return (0);
851 case CONS_GETINFO: {
852 vid_info_t *vi = (vid_info_t *)data;
853
854 vi->m_num = vd->vd_curwindow->vw_number + 1;
855 /* XXX: other fields! */
856 return (0);
857 }
858 case CONS_GETVERS:
859 *(int *)data = 0x200;
860 return 0;
861 case CONS_MODEINFO:
862 /* XXX */
863 return (0);
864 case CONS_MOUSECTL: {
865 mouse_info_t *mouse = (mouse_info_t*)data;
866
867 /*
868 * This has no effect on vt(4). We don't draw any mouse
869 * cursor. Just ignore MOUSE_HIDE and MOUSE_SHOW to
870 * prevent excessive errors. All the other commands
871 * should not be applied to individual TTYs, but only to
872 * consolectl.
873 */
874 switch (mouse->operation) {
875 case MOUSE_HIDE:
876 case MOUSE_SHOW:
877 return (0);
878 default:
879 return (EINVAL);
880 }
881 }
882 case PIO_VFONT: {
883 struct vt_font *vf;
884 int error;
885
886 error = vtfont_load((void *)data, &vf);
887 if (error != 0)
888 return (error);
889
890 error = vt_change_font(vw, vf);
891 vtfont_unref(vf);
892 return (error);
893 }
894 case GIO_SCRNMAP: {
895 scrmap_t *sm = (scrmap_t *)data;
896 int i;
897
898 /* We don't have screen maps, so return a handcrafted one. */
899 for (i = 0; i < 256; i++)
900 sm->scrmap[i] = i;
901 return (0);
902 }
903 case KDGETLED:
904 /* XXX */
905 return (0);
906 case KDSETLED:
907 /* XXX */
908 return (0);
909 case KDSETMODE:
910 /* XXX */
911 return (0);
912 case KDSETRAD:
913 /* XXX */
914 return (0);
915 case VT_ACTIVATE:
916 vt_window_switch(vw);
917 return (0);
918 case VT_GETACTIVE:
919 *(int *)data = vd->vd_curwindow->vw_number + 1;
920 return (0);
921 case VT_GETINDEX:
922 *(int *)data = vw->vw_number + 1;
923 return (0);
924 case VT_OPENQRY: {
925 unsigned int i;
926
927 VT_LOCK(vd);
928 for (i = 0; i < VT_MAXWINDOWS; i++) {
929 vw = vd->vd_windows[i];
930 if (vw == NULL)
931 continue;
932 if (!(vw->vw_flags & VWF_OPENED)) {
933 *(int *)data = vw->vw_number + 1;
934 VT_UNLOCK(vd);
935 return (0);
936 }
937 }
938 VT_UNLOCK(vd);
939 return (EINVAL);
940 }
941 case VT_WAITACTIVE: {
942 unsigned int i;
943 int error = 0;
944
945 i = *(unsigned int *)data;
946 if (i > VT_MAXWINDOWS)
947 return (EINVAL);
948 if (i != 0)
949 vw = vd->vd_windows[i - 1];
950
951 VT_LOCK(vd);
952 while (vd->vd_curwindow != vw && error == 0)
953 error = cv_wait_sig(&vd->vd_winswitch, &vd->vd_lock);
954 VT_UNLOCK(vd);
955 return (error);
956 }
957 case VT_GETMODE:
958 /* XXX */
959 return (0);
960 case VT_SETMODE:
961 /* XXX */
962 return (0);
963 }
964
965 return (ENOIOCTL);
966}
967
968static struct vt_window *
969vt_allocate_window(struct vt_device *vd, unsigned int window)
970{
971 struct vt_window *vw;
972 struct terminal *tm;
973 term_pos_t size;
974 struct winsize wsz;
975
976 vw = malloc(sizeof *vw, M_VT, M_WAITOK|M_ZERO);
977 vw->vw_device = vd;
978 vw->vw_number = window;
979 vw->vw_kbdmode = K_XLATE;
980
981 if (!(vd->vd_flags & VDF_TEXTMODE))
982 vw->vw_font = vtfont_ref(&vt_font_default);
983
984 vt_termsize(vd, vw->vw_font, &size);
985 vt_winsize(vd, vw->vw_font, &wsz);
986 vtbuf_init(&vw->vw_buf, &size);
987
988 tm = vw->vw_terminal = terminal_alloc(&vt_termclass, vw);
989 terminal_set_winsize(tm, &wsz);
990 vd->vd_windows[window] = vw;
991
992 return (vw);
993}
994
995void
996vt_upgrade(struct vt_device *vd)
997{
998 struct vt_window *vw;
999 unsigned int i;
1000
1001 /* Device didn't pass vd_init(). */
1002 if (vd->vd_flags & VDF_DEAD)
1003 return;
1004
1005 mtx_init(&vd->vd_lock, "vtdev", NULL, MTX_DEF);
1006 cv_init(&vd->vd_winswitch, "vtwswt");
1007
1008 /* Start 25 Hz timer. */
1009 callout_init_mtx(&vd->vd_timer, &vd->vd_lock, 0);
1010 callout_reset(&vd->vd_timer, hz / VT_TIMERFREQ, vt_timer, vd);
1011 vd->vd_flags |= VDF_ASYNC;
1012
1013 for (i = 0; i < VT_MAXWINDOWS; i++) {
1014 vw = vd->vd_windows[i];
1015 if (vw == NULL) {
1016 /* New window. */
1017 vw = vt_allocate_window(vd, i);
1018 } else {
1019 /* Console window. */
1020 EVENTHANDLER_REGISTER(shutdown_pre_sync,
1021 vt_window_switch, vw, SHUTDOWN_PRI_DEFAULT);
1022 }
1023 terminal_maketty(vw->vw_terminal, "v%r", VT_UNIT(vw));
1024 }
1025
1026 /* Attach keyboard. */
1027 vt_allocate_keyboard(vd);
1028}
1029
1030void
1031vt_allocate(struct vt_driver *drv, void *softc)
1032{
1033 struct vt_device *vd;
1034
1035 vd = malloc(sizeof *vd, M_VT, M_WAITOK|M_ZERO);
1036 vd->vd_driver = drv;
1037 vd->vd_softc = softc;
1038 vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1);
1039 vd->vd_flags = VDF_INVALID;
1040 vd->vd_driver->vd_init(vd);
1041
1042 vt_upgrade(vd);
1043 vd->vd_curwindow = vd->vd_windows[0];
1044}
2 * Copyright (c) 2009 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 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: user/ed/newcons/sys/dev/vt/vt_core.c 243802 2012-12-02 22:21:40Z nwhitehorn $");
32
33#include <sys/param.h>
34#include <sys/consio.h>
35#include <sys/eventhandler.h>
36#include <sys/fbio.h>
37#include <sys/kbio.h>
38#include <sys/kdb.h>
39#include <sys/kernel.h>
40#include <sys/lock.h>
41#include <sys/malloc.h>
42#include <sys/mutex.h>
43#include <sys/reboot.h>
44#include <sys/systm.h>
45#include <sys/terminal.h>
46
47#include <dev/kbd/kbdreg.h>
48#include <dev/vt/vt.h>
49
50static tc_bell_t vtterm_bell;
51static tc_cursor_t vtterm_cursor;
52static tc_putchar_t vtterm_putchar;
53static tc_fill_t vtterm_fill;
54static tc_copy_t vtterm_copy;
55static tc_param_t vtterm_param;
56static tc_done_t vtterm_done;
57
58static tc_cnprobe_t vtterm_cnprobe;
59static tc_cngetc_t vtterm_cngetc;
60
61static tc_opened_t vtterm_opened;
62static tc_ioctl_t vtterm_ioctl;
63
64const struct terminal_class vt_termclass = {
65 .tc_bell = vtterm_bell,
66 .tc_cursor = vtterm_cursor,
67 .tc_putchar = vtterm_putchar,
68 .tc_fill = vtterm_fill,
69 .tc_copy = vtterm_copy,
70 .tc_param = vtterm_param,
71 .tc_done = vtterm_done,
72
73 .tc_cnprobe = vtterm_cnprobe,
74 .tc_cngetc = vtterm_cngetc,
75
76 .tc_opened = vtterm_opened,
77 .tc_ioctl = vtterm_ioctl,
78};
79
80/*
81 * Use a constant timer of 25 Hz to redraw the screen.
82 *
83 * XXX: In theory we should only fire up the timer when there is really
84 * activity. Unfortunately we cannot always start timers. We really
85 * don't want to process kernel messages synchronously, because it
86 * really slows down the system.
87 */
88#define VT_TIMERFREQ 25
89
90/* Bell pitch/duration. */
91#define VT_BELLDURATION ((5 * hz + 99) / 100)
92#define VT_BELLPITCH 800
93
94#define VT_LOCK(vd) mtx_lock(&(vd)->vd_lock)
95#define VT_UNLOCK(vd) mtx_unlock(&(vd)->vd_lock)
96
97#define VT_UNIT(vw) ((vw)->vw_device->vd_unit * VT_MAXWINDOWS + \
98 (vw)->vw_number)
99
100static unsigned int vt_unit = 0;
101static MALLOC_DEFINE(M_VT, "vt", "vt device");
102
103/* Boot logo. */
104extern unsigned int vt_logo_width;
105extern unsigned int vt_logo_height;
106extern unsigned int vt_logo_depth;
107extern unsigned char vt_logo_image[];
108
109/* Font. */
110extern struct vt_font vt_font_default;
111
112static void
113vt_window_switch(struct vt_window *vw)
114{
115 struct vt_device *vd = vw->vw_device;
116 keyboard_t *kbd;
117
118 VT_LOCK(vd);
119 if (vd->vd_curwindow == vw ||
120 !(vw->vw_flags & (VWF_OPENED|VWF_CONSOLE))) {
121 VT_UNLOCK(vd);
122 return;
123 }
124 vd->vd_curwindow = vw;
125 vd->vd_flags |= VDF_INVALID;
126 cv_broadcast(&vd->vd_winswitch);
127 VT_UNLOCK(vd);
128
129 /* Restore per-window keyboard mode. */
130 mtx_lock(&Giant);
131 kbd = kbd_get_keyboard(vd->vd_keyboard);
132 if (kbd != NULL)
133 kbdd_ioctl(kbd, KDSKBMODE, (void *)&vw->vw_kbdmode);
134 mtx_unlock(&Giant);
135}
136
137static inline void
138vt_termsize(struct vt_device *vd, struct vt_font *vf, term_pos_t *size)
139{
140
141 size->tp_row = vd->vd_height;
142 size->tp_col = vd->vd_width;
143 if (vf != NULL) {
144 size->tp_row /= vf->vf_height;
145 size->tp_col /= vf->vf_width;
146 }
147}
148
149static inline void
150vt_winsize(struct vt_device *vd, struct vt_font *vf, struct winsize *size)
151{
152
153 size->ws_row = size->ws_ypixel = vd->vd_height;
154 size->ws_col = size->ws_xpixel = vd->vd_width;
155 if (vf != NULL) {
156 size->ws_row /= vf->vf_height;
157 size->ws_col /= vf->vf_width;
158 }
159}
160
161static int
162vt_kbdevent(keyboard_t *kbd, int event, void *arg)
163{
164 struct vt_device *vd = arg;
165 struct vt_window *vw = vd->vd_curwindow;
166 u_int c;
167
168 switch (event) {
169 case KBDIO_KEYINPUT:
170 break;
171 case KBDIO_UNLOADING:
172 mtx_lock(&Giant);
173 vd->vd_keyboard = -1;
174 kbd_release(kbd, (void *)&vd->vd_keyboard);
175 mtx_unlock(&Giant);
176 return (0);
177 default:
178 return (EINVAL);
179 }
180
181 c = kbdd_read_char(kbd, 0);
182 if (c & RELKEY)
183 return (0);
184
185 if (c & SPCLKEY) {
186 c &= ~SPCLKEY;
187
188 if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) {
189 vw = vd->vd_windows[c - F_SCR];
190 if (vw != NULL)
191 vt_window_switch(vw);
192 return (0);
193 }
194
195 switch (c) {
196 case DBG:
197 kdb_enter(KDB_WHY_BREAK, "manual escape to debugger");
198 break;
199 case RBT:
200 /* XXX: Make this configurable! */
201 shutdown_nice(0);
202 break;
203 case HALT:
204 shutdown_nice(RB_HALT);
205 break;
206 case PDWN:
207 shutdown_nice(RB_HALT|RB_POWEROFF);
208 break;
209 case SLK: {
210 int state = 0;
211
212 kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state);
213 VT_LOCK(vd);
214 if (state & SLKED) {
215 /* Turn scrolling on. */
216 vw->vw_flags |= VWF_SCROLL;
217 } else {
218 /* Turn scrolling off. */
219 vw->vw_flags &= ~VWF_SCROLL;
220 vthistory_seek(&vw->vw_history, 0, VHS_SET);
221 }
222 VT_UNLOCK(vd);
223 break;
224 }
225 case FKEY | F(1): case FKEY | F(2): case FKEY | F(3):
226 case FKEY | F(4): case FKEY | F(5): case FKEY | F(6):
227 case FKEY | F(7): case FKEY | F(8): case FKEY | F(9):
228 case FKEY | F(10): case FKEY | F(11): case FKEY | F(12):
229 /* F1 through F12 keys. */
230 terminal_input_special(vw->vw_terminal,
231 TKEY_F1 + c - (FKEY | F(1)));
232 break;
233 case FKEY | F(49): /* Home key. */
234 VT_LOCK(vd);
235 if (vw->vw_flags & VWF_SCROLL) {
236 vthistory_seek(&vw->vw_history, 0, VHS_END);
237 VT_UNLOCK(vd);
238 break;
239 }
240 VT_UNLOCK(vd);
241 terminal_input_special(vw->vw_terminal, TKEY_HOME);
242 break;
243 case FKEY | F(50): /* Arrow up. */
244 VT_LOCK(vd);
245 if (vw->vw_flags & VWF_SCROLL) {
246 vthistory_seek(&vw->vw_history, 1, VHS_CUR);
247 VT_UNLOCK(vd);
248 break;
249 }
250 VT_UNLOCK(vd);
251 terminal_input_special(vw->vw_terminal, TKEY_UP);
252 break;
253 case FKEY | F(51): /* Page up. */
254 VT_LOCK(vd);
255 if (vw->vw_flags & VWF_SCROLL) {
256 term_pos_t size;
257
258 vt_termsize(vd, vw->vw_font, &size);
259 vthistory_seek(&vw->vw_history, size.tp_row,
260 VHS_CUR);
261 VT_UNLOCK(vd);
262 break;
263 }
264 VT_UNLOCK(vd);
265 terminal_input_special(vw->vw_terminal, TKEY_PAGE_UP);
266 break;
267 case FKEY | F(53): /* Arrow left. */
268 terminal_input_special(vw->vw_terminal, TKEY_LEFT);
269 break;
270 case FKEY | F(55): /* Arrow right. */
271 terminal_input_special(vw->vw_terminal, TKEY_RIGHT);
272 break;
273 case FKEY | F(57): /* End key. */
274 VT_LOCK(vd);
275 if (vw->vw_flags & VWF_SCROLL) {
276 vthistory_seek(&vw->vw_history, 0, VHS_SET);
277 VT_UNLOCK(vd);
278 break;
279 }
280 VT_UNLOCK(vd);
281 terminal_input_special(vw->vw_terminal, TKEY_END);
282 break;
283 case FKEY | F(58): /* Arrow down. */
284 VT_LOCK(vd);
285 if (vw->vw_flags & VWF_SCROLL) {
286 vthistory_seek(&vw->vw_history, -1, VHS_CUR);
287 VT_UNLOCK(vd);
288 break;
289 }
290 VT_UNLOCK(vd);
291 terminal_input_special(vw->vw_terminal, TKEY_DOWN);
292 break;
293 case FKEY | F(59): /* Page down. */
294 VT_LOCK(vd);
295 if (vw->vw_flags & VWF_SCROLL) {
296 term_pos_t size;
297
298 vt_termsize(vd, vw->vw_font, &size);
299 vthistory_seek(&vw->vw_history, -size.tp_row,
300 VHS_CUR);
301 VT_UNLOCK(vd);
302 break;
303 }
304 VT_UNLOCK(vd);
305 terminal_input_special(vw->vw_terminal, TKEY_PAGE_DOWN);
306 break;
307 case FKEY | F(60): /* Insert key. */
308 terminal_input_special(vw->vw_terminal, TKEY_INSERT);
309 break;
310 case FKEY | F(61): /* Delete key. */
311 terminal_input_special(vw->vw_terminal, TKEY_DELETE);
312 break;
313 }
314 } else if (KEYFLAGS(c) == 0) {
315 c = KEYCHAR(c);
316
317 /* Don't do UTF-8 conversion when doing raw mode. */
318 if (vw->vw_kbdmode == K_XLATE)
319 terminal_input_char(vw->vw_terminal, c);
320 else
321 terminal_input_raw(vw->vw_terminal, c);
322 }
323
324 return (0);
325}
326
327static int
328vt_allocate_keyboard(struct vt_device *vd)
329{
330 int idx0, idx;
331 keyboard_t *k0, *k;
332 keyboard_info_t ki;
333
334 idx0 = kbd_allocate("kbdmux", -1, (void *)&vd->vd_keyboard,
335 vt_kbdevent, vd);
336 if (idx0 != -1) {
337 k0 = kbd_get_keyboard(idx0);
338
339 for (idx = kbd_find_keyboard2("*", -1, 0);
340 idx != -1;
341 idx = kbd_find_keyboard2("*", -1, idx + 1)) {
342 k = kbd_get_keyboard(idx);
343
344 if (idx == idx0 || KBD_IS_BUSY(k))
345 continue;
346
347 bzero(&ki, sizeof(ki));
348 strcpy(ki.kb_name, k->kb_name);
349 ki.kb_unit = k->kb_unit;
350
351 kbdd_ioctl(k0, KBADDKBD, (caddr_t) &ki);
352 }
353 } else
354 idx0 = kbd_allocate("*", -1, (void *)&vd->vd_keyboard,
355 vt_kbdevent, vd);
356
357 return (idx0);
358}
359
360static void
361vtterm_bell(struct terminal *tm)
362{
363
364 sysbeep(1193182 / VT_BELLPITCH, VT_BELLDURATION);
365}
366
367static void
368vtterm_cursor(struct terminal *tm, const term_pos_t *p)
369{
370 struct vt_window *vw = tm->tm_softc;
371
372 vtbuf_cursor_position(&vw->vw_buf, p);
373}
374
375static void
376vtterm_putchar(struct terminal *tm, const term_pos_t *p, term_char_t c)
377{
378 struct vt_window *vw = tm->tm_softc;
379
380 vtbuf_putchar(&vw->vw_buf, p, c);
381}
382
383static void
384vtterm_fill(struct terminal *tm, const term_rect_t *r, term_char_t c)
385{
386 struct vt_window *vw = tm->tm_softc;
387
388 vtbuf_fill(&vw->vw_buf, r, c);
389}
390
391static void
392vtterm_copy(struct terminal *tm, const term_rect_t *r,
393 const term_pos_t *p)
394{
395 struct vt_window *vw = tm->tm_softc;
396 struct vt_device *vd = vw->vw_device;
397 term_pos_t size;
398
399 /*
400 * We copy lines into the history buffer when we have to do a
401 * copy of the entire width of the screen to a region above it.
402 */
403 vt_termsize(vd, vw->vw_font, &size);
404 if (r->tr_begin.tp_row > p->tp_row &&
405 r->tr_begin.tp_col == 0 && r->tr_end.tp_col == size.tp_col) {
406 term_rect_t area;
407
408 area.tr_begin.tp_row = p->tp_row;
409 area.tr_begin.tp_col = 0;
410 area.tr_end.tp_row = r->tr_begin.tp_row;
411 area.tr_end.tp_col = size.tp_col;
412 vthistory_add(&vw->vw_history, &vw->vw_buf, &area);
413 }
414
415 vtbuf_copy(&vw->vw_buf, r, p);
416}
417
418static void
419vtterm_param(struct terminal *tm, int cmd, unsigned int arg)
420{
421 struct vt_window *vw = tm->tm_softc;
422
423 switch (cmd) {
424 case TP_SHOWCURSOR:
425 vtbuf_cursor_visibility(&vw->vw_buf, arg);
426 break;
427 }
428}
429
430static inline void
431vt_determine_colors(term_char_t c, int cursor,
432 term_color_t *fg, term_color_t *bg)
433{
434
435 *fg = TCHAR_FGCOLOR(c);
436 if (TCHAR_FORMAT(c) & TF_BOLD)
437 *fg = TCOLOR_LIGHT(*fg);
438 *bg = TCHAR_BGCOLOR(c);
439
440 if (TCHAR_FORMAT(c) & TF_REVERSE) {
441 term_color_t tmp;
442
443 tmp = *fg;
444 *fg = *bg;
445 *bg = tmp;
446 }
447
448 if (cursor) {
449 *fg = *bg;
450 *bg = TC_WHITE;
451 }
452}
453
454static void
455vt_bitblt_char(struct vt_device *vd, struct vt_font *vf, term_char_t c,
456 int iscursor, unsigned int row, unsigned int col)
457{
458 term_color_t fg, bg;
459
460 vt_determine_colors(c, iscursor, &fg, &bg);
461
462 if (vf != NULL) {
463 const uint8_t *src;
464 vt_axis_t top, left;
465
466 src = vtfont_lookup(vf, c);
467
468 /*
469 * Align the terminal to the centre of the screen.
470 * Fonts may not always be able to fill the entire
471 * screen.
472 */
473 top = row * vf->vf_height +
474 (vd->vd_height % vf->vf_height) / 2;
475 left = col * vf->vf_width +
476 (vd->vd_width % vf->vf_width) / 2;
477
478 vd->vd_driver->vd_bitblt(vd, src, top, left,
479 vf->vf_width, vf->vf_height, fg, bg);
480 } else {
481 vd->vd_driver->vd_putchar(vd, TCHAR_CHARACTER(c),
482 row, col, fg, bg);
483 }
484}
485
486static void
487vt_flush(struct vt_device *vd)
488{
489 struct vt_window *vw = vd->vd_curwindow;
490 struct vt_font *vf = vw->vw_font;
491 term_pos_t size;
492 term_rect_t tarea;
493 struct vt_bufmask tmask;
494 unsigned int row, col, scrollpos;
495 term_char_t c;
496
497 if (vd->vd_flags & VDF_SPLASH || vw->vw_flags & VWF_BUSY)
498 return;
499
500 vtbuf_undirty(&vw->vw_buf, &tarea, &tmask);
501 vthistory_getpos(&vw->vw_history, &scrollpos);
502 vt_termsize(vd, vf, &size);
503
504 /* Force a full redraw when the screen contents are invalid. */
505 if (vd->vd_scrollpos != scrollpos || vd->vd_flags & VDF_INVALID) {
506 tarea.tr_begin.tp_row = tarea.tr_begin.tp_col = 0;
507 tarea.tr_end = size;
508 tmask.vbm_row = tmask.vbm_col = VBM_DIRTY;
509
510 /*
511 * Blank to prevent borders with artifacts. This is
512 * only required when the font doesn't exactly fill the
513 * screen.
514 */
515 if (vd->vd_flags & VDF_INVALID && vf != NULL &&
516 (vd->vd_width % vf->vf_width != 0 ||
517 vd->vd_height % vf->vf_height != 0))
518 vd->vd_driver->vd_blank(vd, TC_BLACK);
519
520 /* Draw the scrollback history. */
521 for (row = 0; row < scrollpos; row++) {
522 for (col = 0; col < size.tp_col; col++) {
523 c = VTHISTORY_FIELD(&vw->vw_history, row, col);
524 vt_bitblt_char(vd, vf, c, 0, row, col);
525 }
526 }
527
528 vd->vd_flags &= ~VDF_INVALID;
529 vd->vd_scrollpos = scrollpos;
530 }
531
532 /*
533 * Clamp the terminal rendering size if it exceeds the window
534 * size, because of scrollback.
535 */
536 if (tarea.tr_end.tp_row + scrollpos > size.tp_row) {
537 if (size.tp_row <= scrollpos)
538 /* Terminal completely invisible. */
539 tarea.tr_end.tp_row = 0;
540 else
541 /* Terminal partially visible. */
542 tarea.tr_end.tp_row = size.tp_row - scrollpos;
543 }
544
545 for (row = tarea.tr_begin.tp_row; row < tarea.tr_end.tp_row; row++) {
546 if (!VTBUF_DIRTYROW(&tmask, row))
547 continue;
548 for (col = tarea.tr_begin.tp_col;
549 col < tarea.tr_end.tp_col; col++) {
550 if (!VTBUF_DIRTYCOL(&tmask, col))
551 continue;
552
553 c = VTBUF_FIELD(&vw->vw_buf, row, col);
554 vt_bitblt_char(vd, vf, c,
555 VTBUF_ISCURSOR(&vw->vw_buf, row, col),
556 row + scrollpos, col);
557 }
558 }
559}
560
561static void
562vt_timer(void *arg)
563{
564 struct vt_device *vd = arg;
565
566 vt_flush(vd);
567 callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ);
568}
569
570static void
571vtterm_done(struct terminal *tm)
572{
573 struct vt_window *vw = tm->tm_softc;
574 struct vt_device *vd = vw->vw_device;
575
576 if (kdb_active || panicstr != NULL) {
577 /* Switch to the debugger. */
578 if (vd->vd_curwindow != vw) {
579 vd->vd_curwindow = vw;
580 vd->vd_flags |= VDF_INVALID;
581 }
582 vd->vd_flags &= ~VDF_SPLASH;
583 vt_flush(vd);
584 } else if (!(vd->vd_flags & VDF_ASYNC)) {
585 vt_flush(vd);
586 }
587}
588
589static void
590vtterm_cnprobe(struct terminal *tm, struct consdev *cp)
591{
592 struct vt_window *vw = tm->tm_softc;
593 struct vt_device *vd = vw->vw_device;
594 struct winsize wsz;
595
596 cp->cn_pri = vd->vd_driver->vd_init(vd);
597 if (cp->cn_pri == CN_DEAD) {
598 vd->vd_flags |= VDF_DEAD;
599 return;
600 }
601
602 /* Initialize any early-boot keyboard drivers */
603 kbd_configure(KB_CONF_PROBE_ONLY);
604
605 vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1);
606 sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw));
607
608 if (!(vd->vd_flags & VDF_TEXTMODE))
609 vw->vw_font = vtfont_ref(&vt_font_default);
610
611 vtbuf_init_early(&vw->vw_buf);
612 vt_winsize(vd, vw->vw_font, &wsz);
613 terminal_set_winsize(tm, &wsz);
614
615 /* Display a nice boot splash. */
616 if (!(vd->vd_flags & VDF_TEXTMODE)) {
617 vt_axis_t top, left;
618
619 top = (vd->vd_height - vt_logo_height) / 2;
620 left = (vd->vd_width - vt_logo_width) / 2;
621 switch (vt_logo_depth) {
622 case 1:
623 /* XXX: Unhardcode colors! */
624 vd->vd_driver->vd_bitblt(vd, vt_logo_image, top, left,
625 vt_logo_width, vt_logo_height, 0xf, 0x0);
626 }
627 vd->vd_flags |= VDF_SPLASH;
628 }
629}
630
631static int
632vtterm_cngetc(struct terminal *tm)
633{
634 struct vt_window *vw = tm->tm_softc;
635 struct vt_device *vd = vw->vw_device;
636 keyboard_t *kbd;
637 u_int c;
638
639 /* Make sure the splash screen is not there. */
640 if (vd->vd_flags & VDF_SPLASH) {
641 vd->vd_flags &= ~VDF_SPLASH;
642 vt_flush(vd);
643 }
644
645 /* Stripped down keyboard handler. */
646 kbd = kbd_get_keyboard(vd->vd_keyboard);
647 if (kbd == NULL)
648 return (-1);
649
650 /* Switch the keyboard to polling to make it work here. */
651 kbdd_poll(kbd, TRUE);
652 c = kbdd_read_char(kbd, 0);
653 kbdd_poll(kbd, FALSE);
654 if (c & RELKEY)
655 return (-1);
656
657 /* Stripped down handling of vt_kbdevent(), without locking, etc. */
658 if (c & SPCLKEY) {
659 c &= ~SPCLKEY;
660
661 switch (c) {
662 case SLK: {
663 int state = 0;
664
665 kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state);
666 if (state & SLKED) {
667 /* Turn scrolling on. */
668 vw->vw_flags |= VWF_SCROLL;
669 } else {
670 /* Turn scrolling off. */
671 vw->vw_flags &= ~VWF_SCROLL;
672 vthistory_seek(&vw->vw_history, 0, VHS_SET);
673 }
674 break;
675 }
676 case FKEY | F(49): /* Home key. */
677 if (vw->vw_flags & VWF_SCROLL)
678 vthistory_seek(&vw->vw_history, 0, VHS_END);
679 break;
680 case FKEY | F(50): /* Arrow up. */
681 if (vw->vw_flags & VWF_SCROLL)
682 vthistory_seek(&vw->vw_history, 1, VHS_CUR);
683 break;
684 case FKEY | F(51): /* Page up. */
685 if (vw->vw_flags & VWF_SCROLL) {
686 term_pos_t size;
687
688 vt_termsize(vd, vw->vw_font, &size);
689 vthistory_seek(&vw->vw_history, size.tp_row,
690 VHS_CUR);
691 }
692 break;
693 case FKEY | F(57): /* End key. */
694 if (vw->vw_flags & VWF_SCROLL)
695 vthistory_seek(&vw->vw_history, 0, VHS_SET);
696 break;
697 case FKEY | F(58): /* Arrow down. */
698 if (vw->vw_flags & VWF_SCROLL)
699 vthistory_seek(&vw->vw_history, -1, VHS_CUR);
700 break;
701 case FKEY | F(59): /* Page down. */
702 if (vw->vw_flags & VWF_SCROLL) {
703 term_pos_t size;
704
705 vt_termsize(vd, vw->vw_font, &size);
706 vthistory_seek(&vw->vw_history, -size.tp_row,
707 VHS_CUR);
708 }
709 break;
710 }
711
712 /* Force refresh to make scrollback work. */
713 vt_flush(vd);
714 } else if (KEYFLAGS(c) == 0) {
715 return KEYCHAR(c);
716 }
717
718 return (-1);
719}
720
721static void
722vtterm_opened(struct terminal *tm, int opened)
723{
724 struct vt_window *vw = tm->tm_softc;
725 struct vt_device *vd = vw->vw_device;
726
727 VT_LOCK(vd);
728 vd->vd_flags &= ~VDF_SPLASH;
729 if (opened)
730 vw->vw_flags |= VWF_OPENED;
731 else
732 vw->vw_flags &= ~VWF_OPENED;
733 VT_UNLOCK(vd);
734}
735
736static int
737vt_change_font(struct vt_window *vw, struct vt_font *vf)
738{
739 struct vt_device *vd = vw->vw_device;
740 struct terminal *tm = vw->vw_terminal;
741 term_pos_t size;
742 struct winsize wsz;
743
744 /*
745 * Changing fonts.
746 *
747 * Changing fonts is a little tricky. We must prevent
748 * simultaneous access to the device, so we must stop
749 * the display timer and the terminal from accessing.
750 * We need to switch fonts and grow our screen buffer.
751 *
752 * XXX: Right now the code uses terminal_mute() to
753 * prevent data from reaching the console driver while
754 * resizing the screen buffer. This isn't elegant...
755 */
756
757 VT_LOCK(vd);
758 if (vw->vw_flags & VWF_BUSY) {
759 /* Another process is changing the font. */
760 VT_UNLOCK(vd);
761 return (EBUSY);
762 }
763 if (vw->vw_font == NULL) {
764 /* Our device doesn't need fonts. */
765 VT_UNLOCK(vd);
766 return (ENOTTY);
767 }
768 vw->vw_flags |= VWF_BUSY;
769 VT_UNLOCK(vd);
770
771 vt_termsize(vd, vf, &size);
772 vt_winsize(vd, vf, &wsz);
773
774 /* Grow the screen buffer and terminal. */
775 terminal_mute(tm, 1);
776 vtbuf_grow(&vw->vw_buf, &size);
777 terminal_set_winsize(tm, &wsz);
778 terminal_mute(tm, 0);
779
780 /* Actually apply the font to the current window. */
781 VT_LOCK(vd);
782 vtfont_unref(vw->vw_font);
783 vw->vw_font = vtfont_ref(vf);
784
785 /* Force a full redraw the next timer tick. */
786 if (vd->vd_curwindow == vw)
787 vd->vd_flags |= VDF_INVALID;
788 vw->vw_flags &= ~VWF_BUSY;
789 VT_UNLOCK(vd);
790 return (0);
791}
792
793static int
794vtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data,
795 struct thread *td)
796{
797 struct vt_window *vw = tm->tm_softc;
798 struct vt_device *vd = vw->vw_device;
799
800 switch (cmd) {
801 case GIO_KEYMAP:
802 case PIO_KEYMAP:
803 case GIO_DEADKEYMAP:
804 case PIO_DEADKEYMAP:
805 case GETFKEY:
806 case SETFKEY:
807 case KDGKBINFO: {
808 keyboard_t *kbd;
809 int error = 0;
810
811 mtx_lock(&Giant);
812 kbd = kbd_get_keyboard(vd->vd_keyboard);
813 if (kbd != NULL)
814 error = kbdd_ioctl(kbd, cmd, data);
815 mtx_unlock(&Giant);
816 if (error == ENOIOCTL)
817 return (ENODEV);
818 return (error);
819 }
820 case KDSKBMODE: {
821 int mode;
822
823 mode = *(int *)data;
824 switch (mode) {
825 case K_XLATE:
826 case K_RAW:
827 case K_CODE:
828 vw->vw_kbdmode = mode;
829 if (vw == vd->vd_curwindow) {
830 keyboard_t *kbd;
831
832 mtx_lock(&Giant);
833 kbd = kbd_get_keyboard(vd->vd_keyboard);
834 if (kbd != NULL)
835 kbdd_ioctl(kbd, KDSKBMODE,
836 (void *)&mode);
837 mtx_unlock(&Giant);
838 }
839 return (0);
840 default:
841 return (EINVAL);
842 }
843 }
844 case CONS_BLANKTIME:
845 /* XXX */
846 return (0);
847 case CONS_GET:
848 /* XXX */
849 *(int *)data = M_CG640x480;
850 return (0);
851 case CONS_GETINFO: {
852 vid_info_t *vi = (vid_info_t *)data;
853
854 vi->m_num = vd->vd_curwindow->vw_number + 1;
855 /* XXX: other fields! */
856 return (0);
857 }
858 case CONS_GETVERS:
859 *(int *)data = 0x200;
860 return 0;
861 case CONS_MODEINFO:
862 /* XXX */
863 return (0);
864 case CONS_MOUSECTL: {
865 mouse_info_t *mouse = (mouse_info_t*)data;
866
867 /*
868 * This has no effect on vt(4). We don't draw any mouse
869 * cursor. Just ignore MOUSE_HIDE and MOUSE_SHOW to
870 * prevent excessive errors. All the other commands
871 * should not be applied to individual TTYs, but only to
872 * consolectl.
873 */
874 switch (mouse->operation) {
875 case MOUSE_HIDE:
876 case MOUSE_SHOW:
877 return (0);
878 default:
879 return (EINVAL);
880 }
881 }
882 case PIO_VFONT: {
883 struct vt_font *vf;
884 int error;
885
886 error = vtfont_load((void *)data, &vf);
887 if (error != 0)
888 return (error);
889
890 error = vt_change_font(vw, vf);
891 vtfont_unref(vf);
892 return (error);
893 }
894 case GIO_SCRNMAP: {
895 scrmap_t *sm = (scrmap_t *)data;
896 int i;
897
898 /* We don't have screen maps, so return a handcrafted one. */
899 for (i = 0; i < 256; i++)
900 sm->scrmap[i] = i;
901 return (0);
902 }
903 case KDGETLED:
904 /* XXX */
905 return (0);
906 case KDSETLED:
907 /* XXX */
908 return (0);
909 case KDSETMODE:
910 /* XXX */
911 return (0);
912 case KDSETRAD:
913 /* XXX */
914 return (0);
915 case VT_ACTIVATE:
916 vt_window_switch(vw);
917 return (0);
918 case VT_GETACTIVE:
919 *(int *)data = vd->vd_curwindow->vw_number + 1;
920 return (0);
921 case VT_GETINDEX:
922 *(int *)data = vw->vw_number + 1;
923 return (0);
924 case VT_OPENQRY: {
925 unsigned int i;
926
927 VT_LOCK(vd);
928 for (i = 0; i < VT_MAXWINDOWS; i++) {
929 vw = vd->vd_windows[i];
930 if (vw == NULL)
931 continue;
932 if (!(vw->vw_flags & VWF_OPENED)) {
933 *(int *)data = vw->vw_number + 1;
934 VT_UNLOCK(vd);
935 return (0);
936 }
937 }
938 VT_UNLOCK(vd);
939 return (EINVAL);
940 }
941 case VT_WAITACTIVE: {
942 unsigned int i;
943 int error = 0;
944
945 i = *(unsigned int *)data;
946 if (i > VT_MAXWINDOWS)
947 return (EINVAL);
948 if (i != 0)
949 vw = vd->vd_windows[i - 1];
950
951 VT_LOCK(vd);
952 while (vd->vd_curwindow != vw && error == 0)
953 error = cv_wait_sig(&vd->vd_winswitch, &vd->vd_lock);
954 VT_UNLOCK(vd);
955 return (error);
956 }
957 case VT_GETMODE:
958 /* XXX */
959 return (0);
960 case VT_SETMODE:
961 /* XXX */
962 return (0);
963 }
964
965 return (ENOIOCTL);
966}
967
968static struct vt_window *
969vt_allocate_window(struct vt_device *vd, unsigned int window)
970{
971 struct vt_window *vw;
972 struct terminal *tm;
973 term_pos_t size;
974 struct winsize wsz;
975
976 vw = malloc(sizeof *vw, M_VT, M_WAITOK|M_ZERO);
977 vw->vw_device = vd;
978 vw->vw_number = window;
979 vw->vw_kbdmode = K_XLATE;
980
981 if (!(vd->vd_flags & VDF_TEXTMODE))
982 vw->vw_font = vtfont_ref(&vt_font_default);
983
984 vt_termsize(vd, vw->vw_font, &size);
985 vt_winsize(vd, vw->vw_font, &wsz);
986 vtbuf_init(&vw->vw_buf, &size);
987
988 tm = vw->vw_terminal = terminal_alloc(&vt_termclass, vw);
989 terminal_set_winsize(tm, &wsz);
990 vd->vd_windows[window] = vw;
991
992 return (vw);
993}
994
995void
996vt_upgrade(struct vt_device *vd)
997{
998 struct vt_window *vw;
999 unsigned int i;
1000
1001 /* Device didn't pass vd_init(). */
1002 if (vd->vd_flags & VDF_DEAD)
1003 return;
1004
1005 mtx_init(&vd->vd_lock, "vtdev", NULL, MTX_DEF);
1006 cv_init(&vd->vd_winswitch, "vtwswt");
1007
1008 /* Start 25 Hz timer. */
1009 callout_init_mtx(&vd->vd_timer, &vd->vd_lock, 0);
1010 callout_reset(&vd->vd_timer, hz / VT_TIMERFREQ, vt_timer, vd);
1011 vd->vd_flags |= VDF_ASYNC;
1012
1013 for (i = 0; i < VT_MAXWINDOWS; i++) {
1014 vw = vd->vd_windows[i];
1015 if (vw == NULL) {
1016 /* New window. */
1017 vw = vt_allocate_window(vd, i);
1018 } else {
1019 /* Console window. */
1020 EVENTHANDLER_REGISTER(shutdown_pre_sync,
1021 vt_window_switch, vw, SHUTDOWN_PRI_DEFAULT);
1022 }
1023 terminal_maketty(vw->vw_terminal, "v%r", VT_UNIT(vw));
1024 }
1025
1026 /* Attach keyboard. */
1027 vt_allocate_keyboard(vd);
1028}
1029
1030void
1031vt_allocate(struct vt_driver *drv, void *softc)
1032{
1033 struct vt_device *vd;
1034
1035 vd = malloc(sizeof *vd, M_VT, M_WAITOK|M_ZERO);
1036 vd->vd_driver = drv;
1037 vd->vd_softc = softc;
1038 vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1);
1039 vd->vd_flags = VDF_INVALID;
1040 vd->vd_driver->vd_init(vd);
1041
1042 vt_upgrade(vd);
1043 vd->vd_curwindow = vd->vd_windows[0];
1044}