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