vt_core.c revision 219888
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 219888 2011-03-22 21:31:31Z ed $"); 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; 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 vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1); 603 sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw)); 604 605 if (!(vd->vd_flags & VDF_TEXTMODE)) 606 vw->vw_font = vtfont_ref(&vt_font_default); 607 608 vtbuf_init_early(&vw->vw_buf); 609 vt_winsize(vd, vw->vw_font, &wsz); 610 terminal_set_winsize(tm, &wsz); 611 612 /* Display a nice boot splash. */ 613 if (!(vd->vd_flags & VDF_TEXTMODE)) { 614 vt_axis_t top, left; 615 616 top = (vd->vd_height - vt_logo_height) / 2; 617 left = (vd->vd_width - vt_logo_width) / 2; 618 switch (vt_logo_depth) { 619 case 1: 620 /* XXX: Unhardcode colors! */ 621 vd->vd_driver->vd_bitblt(vd, vt_logo_image, top, left, 622 vt_logo_width, vt_logo_height, 0xf, 0x0); 623 } 624 vd->vd_flags |= VDF_SPLASH; 625 } 626} 627 628static int 629vtterm_cngetc(struct terminal *tm) 630{ 631 struct vt_window *vw = tm->tm_softc; 632 struct vt_device *vd = vw->vw_device; 633 keyboard_t *kbd; 634 u_int c; 635 636 /* Make sure the splash screen is not there. */ 637 if (vd->vd_flags & VDF_SPLASH) { 638 vd->vd_flags &= ~VDF_SPLASH; 639 vt_flush(vd); 640 } 641 642 /* Stripped down keyboard handler. */ 643 kbd = kbd_get_keyboard(vd->vd_keyboard); 644 if (kbd == NULL) 645 return (-1); 646 647 /* Switch the keyboard to polling to make it work here. */ 648 kbdd_poll(kbd, TRUE); 649 c = kbdd_read_char(kbd, 0); 650 kbdd_poll(kbd, FALSE); 651 if (c & RELKEY) 652 return (-1); 653 654 /* Stripped down handling of vt_kbdevent(), without locking, etc. */ 655 if (c & SPCLKEY) { 656 c &= ~SPCLKEY; 657 658 switch (c) { 659 case SLK: { 660 int state; 661 662 kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state); 663 if (state & SLKED) { 664 /* Turn scrolling on. */ 665 vw->vw_flags |= VWF_SCROLL; 666 } else { 667 /* Turn scrolling off. */ 668 vw->vw_flags &= ~VWF_SCROLL; 669 vthistory_seek(&vw->vw_history, 0, VHS_SET); 670 } 671 break; 672 } 673 case FKEY | F(49): /* Home key. */ 674 if (vw->vw_flags & VWF_SCROLL) 675 vthistory_seek(&vw->vw_history, 0, VHS_END); 676 break; 677 case FKEY | F(50): /* Arrow up. */ 678 if (vw->vw_flags & VWF_SCROLL) 679 vthistory_seek(&vw->vw_history, 1, VHS_CUR); 680 break; 681 case FKEY | F(51): /* Page up. */ 682 if (vw->vw_flags & VWF_SCROLL) { 683 term_pos_t size; 684 685 vt_termsize(vd, vw->vw_font, &size); 686 vthistory_seek(&vw->vw_history, size.tp_row, 687 VHS_CUR); 688 } 689 break; 690 case FKEY | F(57): /* End key. */ 691 if (vw->vw_flags & VWF_SCROLL) 692 vthistory_seek(&vw->vw_history, 0, VHS_SET); 693 break; 694 case FKEY | F(58): /* Arrow down. */ 695 if (vw->vw_flags & VWF_SCROLL) 696 vthistory_seek(&vw->vw_history, -1, VHS_CUR); 697 break; 698 case FKEY | F(59): /* Page down. */ 699 if (vw->vw_flags & VWF_SCROLL) { 700 term_pos_t size; 701 702 vt_termsize(vd, vw->vw_font, &size); 703 vthistory_seek(&vw->vw_history, -size.tp_row, 704 VHS_CUR); 705 } 706 break; 707 } 708 709 /* Force refresh to make scrollback work. */ 710 vt_flush(vd); 711 } else if (KEYFLAGS(c) == 0) { 712 return KEYCHAR(c); 713 } 714 715 return (-1); 716} 717 718static void 719vtterm_opened(struct terminal *tm, int opened) 720{ 721 struct vt_window *vw = tm->tm_softc; 722 struct vt_device *vd = vw->vw_device; 723 724 VT_LOCK(vd); 725 vd->vd_flags &= ~VDF_SPLASH; 726 if (opened) 727 vw->vw_flags |= VWF_OPENED; 728 else 729 vw->vw_flags &= ~VWF_OPENED; 730 VT_UNLOCK(vd); 731} 732 733static int 734vt_change_font(struct vt_window *vw, struct vt_font *vf) 735{ 736 struct vt_device *vd = vw->vw_device; 737 struct terminal *tm = vw->vw_terminal; 738 term_pos_t size; 739 struct winsize wsz; 740 741 /* 742 * Changing fonts. 743 * 744 * Changing fonts is a little tricky. We must prevent 745 * simultaneous access to the device, so we must stop 746 * the display timer and the terminal from accessing. 747 * We need to switch fonts and grow our screen buffer. 748 * 749 * XXX: Right now the code uses terminal_mute() to 750 * prevent data from reaching the console driver while 751 * resizing the screen buffer. This isn't elegant... 752 */ 753 754 VT_LOCK(vd); 755 if (vw->vw_flags & VWF_BUSY) { 756 /* Another process is changing the font. */ 757 VT_UNLOCK(vd); 758 return (EBUSY); 759 } 760 if (vw->vw_font == NULL) { 761 /* Our device doesn't need fonts. */ 762 VT_UNLOCK(vd); 763 return (ENOTTY); 764 } 765 vw->vw_flags |= VWF_BUSY; 766 VT_UNLOCK(vd); 767 768 vt_termsize(vd, vf, &size); 769 vt_winsize(vd, vf, &wsz); 770 771 /* Grow the screen buffer and terminal. */ 772 terminal_mute(tm, 1); 773 vtbuf_grow(&vw->vw_buf, &size); 774 terminal_set_winsize(tm, &wsz); 775 terminal_mute(tm, 0); 776 777 /* Actually apply the font to the current window. */ 778 VT_LOCK(vd); 779 vtfont_unref(vw->vw_font); 780 vw->vw_font = vtfont_ref(vf); 781 782 /* Force a full redraw the next timer tick. */ 783 if (vd->vd_curwindow == vw) 784 vd->vd_flags |= VDF_INVALID; 785 vw->vw_flags &= ~VWF_BUSY; 786 VT_UNLOCK(vd); 787 return (0); 788} 789 790static int 791vtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data, 792 struct thread *td) 793{ 794 struct vt_window *vw = tm->tm_softc; 795 struct vt_device *vd = vw->vw_device; 796 797 switch (cmd) { 798 case GIO_KEYMAP: 799 case PIO_KEYMAP: 800 case GIO_DEADKEYMAP: 801 case PIO_DEADKEYMAP: 802 case GETFKEY: 803 case SETFKEY: 804 case KDGKBINFO: { 805 keyboard_t *kbd; 806 int error = 0; 807 808 mtx_lock(&Giant); 809 kbd = kbd_get_keyboard(vd->vd_keyboard); 810 if (kbd != NULL) 811 error = kbdd_ioctl(kbd, cmd, data); 812 mtx_unlock(&Giant); 813 if (error == ENOIOCTL) 814 return (ENODEV); 815 return (error); 816 } 817 case KDSKBMODE: { 818 int mode; 819 820 mode = *(int *)data; 821 switch (mode) { 822 case K_XLATE: 823 case K_RAW: 824 case K_CODE: 825 vw->vw_kbdmode = mode; 826 if (vw == vd->vd_curwindow) { 827 keyboard_t *kbd; 828 829 mtx_lock(&Giant); 830 kbd = kbd_get_keyboard(vd->vd_keyboard); 831 if (kbd != NULL) 832 kbdd_ioctl(kbd, KDSKBMODE, 833 (void *)&mode); 834 mtx_unlock(&Giant); 835 } 836 return (0); 837 default: 838 return (EINVAL); 839 } 840 } 841 case CONS_BLANKTIME: 842 /* XXX */ 843 return (0); 844 case CONS_GET: 845 /* XXX */ 846 *(int *)data = M_CG640x480; 847 return (0); 848 case CONS_GETINFO: { 849 vid_info_t *vi = (vid_info_t *)data; 850 851 vi->m_num = vd->vd_curwindow->vw_number + 1; 852 /* XXX: other fields! */ 853 return (0); 854 } 855 case CONS_GETVERS: 856 *(int *)data = 0x200; 857 return 0; 858 case CONS_MODEINFO: 859 /* XXX */ 860 return (0); 861 case CONS_MOUSECTL: { 862 mouse_info_t *mouse = (mouse_info_t*)data; 863 864 /* 865 * This has no effect on vt(4). We don't draw any mouse 866 * cursor. Just ignore MOUSE_HIDE and MOUSE_SHOW to 867 * prevent excessive errors. All the other commands 868 * should not be applied to individual TTYs, but only to 869 * consolectl. 870 */ 871 switch (mouse->operation) { 872 case MOUSE_HIDE: 873 case MOUSE_SHOW: 874 return (0); 875 default: 876 return (EINVAL); 877 } 878 } 879 case PIO_VFONT: { 880 struct vt_font *vf; 881 int error; 882 883 error = vtfont_load((void *)data, &vf); 884 if (error != 0) 885 return (error); 886 887 error = vt_change_font(vw, vf); 888 vtfont_unref(vf); 889 return (error); 890 } 891 case GIO_SCRNMAP: { 892 scrmap_t *sm = (scrmap_t *)data; 893 int i; 894 895 /* We don't have screen maps, so return a handcrafted one. */ 896 for (i = 0; i < 256; i++) 897 sm->scrmap[i] = i; 898 return (0); 899 } 900 case KDGETLED: 901 /* XXX */ 902 return (0); 903 case KDSETLED: 904 /* XXX */ 905 return (0); 906 case KDSETMODE: 907 /* XXX */ 908 return (0); 909 case KDSETRAD: 910 /* XXX */ 911 return (0); 912 case VT_ACTIVATE: 913 vt_window_switch(vw); 914 return (0); 915 case VT_GETACTIVE: 916 *(int *)data = vd->vd_curwindow->vw_number + 1; 917 return (0); 918 case VT_GETINDEX: 919 *(int *)data = vw->vw_number + 1; 920 return (0); 921 case VT_OPENQRY: { 922 unsigned int i; 923 924 VT_LOCK(vd); 925 for (i = 0; i < VT_MAXWINDOWS; i++) { 926 vw = vd->vd_windows[i]; 927 if (vw == NULL) 928 continue; 929 if (!(vw->vw_flags & VWF_OPENED)) { 930 *(int *)data = vw->vw_number + 1; 931 VT_UNLOCK(vd); 932 return (0); 933 } 934 } 935 VT_UNLOCK(vd); 936 return (EINVAL); 937 } 938 case VT_WAITACTIVE: { 939 unsigned int i; 940 int error = 0; 941 942 i = *(unsigned int *)data; 943 if (i > VT_MAXWINDOWS) 944 return (EINVAL); 945 if (i != 0) 946 vw = vd->vd_windows[i - 1]; 947 948 VT_LOCK(vd); 949 while (vd->vd_curwindow != vw && error == 0) 950 error = cv_wait_sig(&vd->vd_winswitch, &vd->vd_lock); 951 VT_UNLOCK(vd); 952 return (error); 953 } 954 case VT_GETMODE: 955 /* XXX */ 956 return (0); 957 case VT_SETMODE: 958 /* XXX */ 959 return (0); 960 } 961 962 return (ENOIOCTL); 963} 964 965static struct vt_window * 966vt_allocate_window(struct vt_device *vd, unsigned int window) 967{ 968 struct vt_window *vw; 969 struct terminal *tm; 970 term_pos_t size; 971 struct winsize wsz; 972 973 vw = malloc(sizeof *vw, M_VT, M_WAITOK|M_ZERO); 974 vw->vw_device = vd; 975 vw->vw_number = window; 976 vw->vw_kbdmode = K_XLATE; 977 978 if (!(vd->vd_flags & VDF_TEXTMODE)) 979 vw->vw_font = vtfont_ref(&vt_font_default); 980 981 vt_termsize(vd, vw->vw_font, &size); 982 vt_winsize(vd, vw->vw_font, &wsz); 983 vtbuf_init(&vw->vw_buf, &size); 984 985 tm = vw->vw_terminal = terminal_alloc(&vt_termclass, vw); 986 terminal_set_winsize(tm, &wsz); 987 vd->vd_windows[window] = vw; 988 989 return (vw); 990} 991 992void 993vt_upgrade(struct vt_device *vd) 994{ 995 struct vt_window *vw; 996 unsigned int i; 997 998 /* Device didn't pass vd_init(). */ 999 if (vd->vd_flags & VDF_DEAD) 1000 return; 1001 1002 mtx_init(&vd->vd_lock, "vtdev", NULL, MTX_DEF); 1003 cv_init(&vd->vd_winswitch, "vtwswt"); 1004 1005 /* Start 25 Hz timer. */ 1006 callout_init_mtx(&vd->vd_timer, &vd->vd_lock, 0); 1007 callout_reset(&vd->vd_timer, hz / VT_TIMERFREQ, vt_timer, vd); 1008 vd->vd_flags |= VDF_ASYNC; 1009 1010 for (i = 0; i < VT_MAXWINDOWS; i++) { 1011 vw = vd->vd_windows[i]; 1012 if (vw == NULL) { 1013 /* New window. */ 1014 vw = vt_allocate_window(vd, i); 1015 } else { 1016 /* Console window. */ 1017 EVENTHANDLER_REGISTER(shutdown_pre_sync, 1018 vt_window_switch, vw, SHUTDOWN_PRI_DEFAULT); 1019 } 1020 terminal_maketty(vw->vw_terminal, "v%r", VT_UNIT(vw)); 1021 } 1022 1023 /* Attach keyboard. */ 1024 vt_allocate_keyboard(vd); 1025} 1026 1027void 1028vt_allocate(struct vt_driver *drv, void *softc) 1029{ 1030 struct vt_device *vd; 1031 1032 vd = malloc(sizeof *vd, M_VT, M_WAITOK|M_ZERO); 1033 vd->vd_driver = drv; 1034 vd->vd_softc = softc; 1035 vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1); 1036 vd->vd_flags = VDF_INVALID; 1037 vd->vd_driver->vd_init(vd); 1038 1039 vt_upgrade(vd); 1040 vd->vd_curwindow = vd->vd_windows[0]; 1041} 1042