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