vt_core.c revision 243802
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} 1045