vt_core.c revision 257815
1219888Sed/*- 2257547Sray * Copyright (c) 2009, 2013 The FreeBSD Foundation 3219888Sed * All rights reserved. 4219888Sed * 5219888Sed * This software was developed by Ed Schouten under sponsorship from the 6219888Sed * FreeBSD Foundation. 7219888Sed * 8257547Sray * Portions of this software were developed by Oleksandr Rybalko 9257547Sray * under sponsorship from the FreeBSD Foundation. 10257547Sray * 11219888Sed * Redistribution and use in source and binary forms, with or without 12219888Sed * modification, are permitted provided that the following conditions 13219888Sed * are met: 14219888Sed * 1. Redistributions of source code must retain the above copyright 15219888Sed * notice, this list of conditions and the following disclaimer. 16219888Sed * 2. Redistributions in binary form must reproduce the above copyright 17219888Sed * notice, this list of conditions and the following disclaimer in the 18219888Sed * documentation and/or other materials provided with the distribution. 19219888Sed * 20219888Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21219888Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22219888Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23219888Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24219888Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25219888Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26219888Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27219888Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28219888Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29219888Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30219888Sed * SUCH DAMAGE. 31219888Sed */ 32219888Sed 33219888Sed#include <sys/cdefs.h> 34219888Sed__FBSDID("$FreeBSD: user/ed/newcons/sys/dev/vt/vt_core.c 257815 2013-11-07 21:08:52Z ray $"); 35219888Sed 36219888Sed#include <sys/param.h> 37219888Sed#include <sys/consio.h> 38219888Sed#include <sys/eventhandler.h> 39219888Sed#include <sys/fbio.h> 40219888Sed#include <sys/kbio.h> 41219888Sed#include <sys/kdb.h> 42219888Sed#include <sys/kernel.h> 43219888Sed#include <sys/lock.h> 44219888Sed#include <sys/malloc.h> 45219888Sed#include <sys/mutex.h> 46256145Sray#include <sys/proc.h> 47219888Sed#include <sys/reboot.h> 48219888Sed#include <sys/systm.h> 49219888Sed#include <sys/terminal.h> 50219888Sed 51219888Sed#include <dev/kbd/kbdreg.h> 52219888Sed#include <dev/vt/vt.h> 53219888Sed 54219888Sedstatic tc_bell_t vtterm_bell; 55219888Sedstatic tc_cursor_t vtterm_cursor; 56219888Sedstatic tc_putchar_t vtterm_putchar; 57219888Sedstatic tc_fill_t vtterm_fill; 58219888Sedstatic tc_copy_t vtterm_copy; 59219888Sedstatic tc_param_t vtterm_param; 60219888Sedstatic tc_done_t vtterm_done; 61219888Sed 62219888Sedstatic tc_cnprobe_t vtterm_cnprobe; 63219888Sedstatic tc_cngetc_t vtterm_cngetc; 64219888Sed 65219888Sedstatic tc_opened_t vtterm_opened; 66219888Sedstatic tc_ioctl_t vtterm_ioctl; 67219888Sed 68219888Sedconst struct terminal_class vt_termclass = { 69219888Sed .tc_bell = vtterm_bell, 70219888Sed .tc_cursor = vtterm_cursor, 71219888Sed .tc_putchar = vtterm_putchar, 72219888Sed .tc_fill = vtterm_fill, 73219888Sed .tc_copy = vtterm_copy, 74219888Sed .tc_param = vtterm_param, 75219888Sed .tc_done = vtterm_done, 76219888Sed 77219888Sed .tc_cnprobe = vtterm_cnprobe, 78219888Sed .tc_cngetc = vtterm_cngetc, 79219888Sed 80219888Sed .tc_opened = vtterm_opened, 81219888Sed .tc_ioctl = vtterm_ioctl, 82219888Sed}; 83219888Sed 84219888Sed/* 85257296Snwhitehorn * Use a constant timer of 25 Hz to redraw the screen. 86219888Sed * 87219888Sed * XXX: In theory we should only fire up the timer when there is really 88219888Sed * activity. Unfortunately we cannot always start timers. We really 89219888Sed * don't want to process kernel messages synchronously, because it 90219888Sed * really slows down the system. 91219888Sed */ 92257296Snwhitehorn#define VT_TIMERFREQ 25 93219888Sed 94219888Sed/* Bell pitch/duration. */ 95219888Sed#define VT_BELLDURATION ((5 * hz + 99) / 100) 96219888Sed#define VT_BELLPITCH 800 97219888Sed 98219888Sed#define VT_LOCK(vd) mtx_lock(&(vd)->vd_lock) 99219888Sed#define VT_UNLOCK(vd) mtx_unlock(&(vd)->vd_lock) 100219888Sed 101219888Sed#define VT_UNIT(vw) ((vw)->vw_device->vd_unit * VT_MAXWINDOWS + \ 102219888Sed (vw)->vw_number) 103219888Sed 104256145Sray/* XXX while syscons is here. */ 105256145Srayint sc_txtmouse_no_retrace_wait; 106256145Sray 107256145Sraystatic SYSCTL_NODE(_kern, OID_AUTO, vt, CTLFLAG_RD, 0, "Newcons parameters"); 108256145SrayVT_SYSCTL_INT(debug, 0, "Newcons debug level"); 109256145SrayVT_SYSCTL_INT(deadtimer, 15, "Time to wait busy process in VT_PROCESS mode"); 110256145Sray 111219888Sedstatic unsigned int vt_unit = 0; 112219888Sedstatic MALLOC_DEFINE(M_VT, "vt", "vt device"); 113256145Sraystruct vt_device *main_vd = NULL; 114219888Sed 115219888Sed/* Boot logo. */ 116219888Sedextern unsigned int vt_logo_width; 117219888Sedextern unsigned int vt_logo_height; 118219888Sedextern unsigned int vt_logo_depth; 119219888Sedextern unsigned char vt_logo_image[]; 120219888Sed 121219888Sed/* Font. */ 122219888Sedextern struct vt_font vt_font_default; 123219888Sed 124256145Sraystatic int signal_vt_rel(struct vt_window *); 125256145Sraystatic int signal_vt_acq(struct vt_window *); 126256145Sraystatic int finish_vt_rel(struct vt_window *, int, int *); 127256145Sraystatic int finish_vt_acq(struct vt_window *); 128256145Sraystatic int vt_window_switch(struct vt_window *); 129256145Sraystatic int vt_late_window_switch(struct vt_window *); 130256145Sraystatic int vt_proc_alive(struct vt_window *); 131256145Sraystatic void vt_resize(struct vt_device *); 132256145Sray 133219888Sedstatic void 134256145Srayvt_switch_timer(void *arg) 135256145Sray{ 136256145Sray 137256145Sray vt_late_window_switch((struct vt_window *)arg); 138256145Sray} 139256145Sray 140256145Sraystatic int 141256145Srayvt_window_preswitch(struct vt_window *vw, struct vt_window *curvw) 142256145Sray{ 143256145Sray 144256145Sray DPRINTF(40, "%s\n", __func__); 145256145Sray curvw->vw_switch_to = vw; 146256145Sray /* Set timer to allow switch in case when process hang. */ 147256145Sray callout_reset(&vw->vw_proc_dead_timer, hz * vt_deadtimer, 148256145Sray vt_switch_timer, (void *)vw); 149256145Sray /* Notify process about vt switch attempt. */ 150256145Sray DPRINTF(30, "%s: Notify process.\n", __func__); 151256145Sray signal_vt_rel(curvw); 152256145Sray 153256145Sray return (0); 154256145Sray} 155256145Sray 156256145Sraystatic int 157256145Srayvt_window_postswitch(struct vt_window *vw) 158256145Sray{ 159256145Sray 160256145Sray signal_vt_acq(vw); 161256145Sray return (0); 162256145Sray} 163256145Sray 164256145Sray/* vt_late_window_switch will done VT switching for regular case. */ 165256145Sraystatic int 166256145Srayvt_late_window_switch(struct vt_window *vw) 167256145Sray{ 168256145Sray int ret; 169256145Sray 170256145Sray callout_stop(&vw->vw_proc_dead_timer); 171256145Sray 172256145Sray ret = vt_window_switch(vw); 173256145Sray if (ret) 174256145Sray return (ret); 175256145Sray 176256145Sray /* Notify owner process about terminal availability. */ 177256145Sray if (vw->vw_smode.mode == VT_PROCESS) { 178256145Sray ret = vt_window_postswitch(vw); 179256145Sray } 180256145Sray return (ret); 181256145Sray} 182256145Sray 183256145Sray/* Switch window. */ 184256145Sraystatic int 185256145Srayvt_proc_window_switch(struct vt_window *vw) 186256145Sray{ 187256145Sray struct vt_window *curvw; 188256145Sray struct vt_device *vd; 189256145Sray int ret; 190256145Sray 191256145Sray if (vw->vw_flags & VWF_VTYLOCK) 192256145Sray return (EBUSY); 193256145Sray 194256145Sray vd = vw->vw_device; 195256145Sray curvw = vd->vd_curwindow; 196256145Sray 197256145Sray /* Ask current process permitions to switch away. */ 198256145Sray if (curvw->vw_smode.mode == VT_PROCESS) { 199256145Sray DPRINTF(30, "%s: VT_PROCESS ", __func__); 200256145Sray if (vt_proc_alive(curvw) == FALSE) { 201256145Sray DPRINTF(30, "Dead. Cleaning."); 202256145Sray /* Dead */ 203256145Sray } else { 204256145Sray DPRINTF(30, "%s: Signaling process.\n", __func__); 205256145Sray /* Alive, try to ask him. */ 206256145Sray ret = vt_window_preswitch(vw, curvw); 207256145Sray /* Wait for process answer or timeout. */ 208256145Sray return (ret); 209256145Sray } 210256145Sray DPRINTF(30, "\n"); 211256145Sray } 212256145Sray 213256145Sray ret = vt_late_window_switch(vw); 214256145Sray return (ret); 215256145Sray} 216256145Sray 217256145Sray/* Switch window ignoring process locking. */ 218256145Sraystatic int 219219888Sedvt_window_switch(struct vt_window *vw) 220219888Sed{ 221219888Sed struct vt_device *vd = vw->vw_device; 222256145Sray struct vt_window *curvw = vd->vd_curwindow; 223219888Sed keyboard_t *kbd; 224219888Sed 225219888Sed VT_LOCK(vd); 226256145Sray if (curvw == vw) { 227256145Sray /* Nothing to do. */ 228219888Sed VT_UNLOCK(vd); 229256145Sray return (0); 230219888Sed } 231256145Sray if (!(vw->vw_flags & (VWF_OPENED|VWF_CONSOLE))) { 232256145Sray VT_UNLOCK(vd); 233256145Sray return (EINVAL); 234256145Sray } 235256145Sray 236219888Sed vd->vd_curwindow = vw; 237219888Sed vd->vd_flags |= VDF_INVALID; 238219888Sed cv_broadcast(&vd->vd_winswitch); 239219888Sed VT_UNLOCK(vd); 240219888Sed 241256145Sray if (vd->vd_driver->vd_postswitch) 242256145Sray vd->vd_driver->vd_postswitch(vd); 243256145Sray 244219888Sed /* Restore per-window keyboard mode. */ 245219888Sed mtx_lock(&Giant); 246219888Sed kbd = kbd_get_keyboard(vd->vd_keyboard); 247256145Sray if (kbd != NULL) { 248219888Sed kbdd_ioctl(kbd, KDSKBMODE, (void *)&vw->vw_kbdmode); 249256145Sray } 250219888Sed mtx_unlock(&Giant); 251256145Sray DPRINTF(10, "%s(ttyv%d) done\n", __func__, vw->vw_number); 252256145Sray 253256145Sray return (0); 254219888Sed} 255219888Sed 256219888Sedstatic inline void 257219888Sedvt_termsize(struct vt_device *vd, struct vt_font *vf, term_pos_t *size) 258219888Sed{ 259219888Sed 260219888Sed size->tp_row = vd->vd_height; 261219888Sed size->tp_col = vd->vd_width; 262219888Sed if (vf != NULL) { 263219888Sed size->tp_row /= vf->vf_height; 264219888Sed size->tp_col /= vf->vf_width; 265219888Sed } 266219888Sed} 267219888Sed 268219888Sedstatic inline void 269219888Sedvt_winsize(struct vt_device *vd, struct vt_font *vf, struct winsize *size) 270219888Sed{ 271219888Sed 272219888Sed size->ws_row = size->ws_ypixel = vd->vd_height; 273219888Sed size->ws_col = size->ws_xpixel = vd->vd_width; 274219888Sed if (vf != NULL) { 275219888Sed size->ws_row /= vf->vf_height; 276219888Sed size->ws_col /= vf->vf_width; 277219888Sed } 278219888Sed} 279219888Sed 280257076Sraystatic void 281257076Srayvt_scroll(struct vt_window *vw, int offset, int whence) 282257076Sray{ 283257076Sray int diff; 284257076Sray term_pos_t size; 285257076Sray 286257076Sray if ((vw->vw_flags & VWF_SCROLL) == 0) 287257076Sray return; 288257076Sray 289257076Sray vt_termsize(vw->vw_device, vw->vw_font, &size); 290257076Sray 291257076Sray diff = vthistory_seek(&vw->vw_buf, offset, whence); 292257076Sray /* 293257076Sray * Offset changed, please update Nth lines on sceen. 294257076Sray * +N - Nth lines at top; 295257076Sray * -N - Nth lines at bottom. 296257076Sray */ 297257076Sray 298257076Sray if (diff < -size.tp_row || diff > size.tp_row) { 299257076Sray vw->vw_device->vd_flags |= VDF_INVALID; 300257076Sray return; 301257076Sray } 302257076Sray vw->vw_device->vd_flags |= VDF_INVALID; /*XXX*/ 303257076Sray} 304257076Sray 305219888Sedstatic int 306257076Srayvt_machine_kbdevent(int c) 307257076Sray{ 308257076Sray 309257076Sray switch (c) { 310257076Sray case SPCLKEY | DBG: 311257076Sray kdb_enter(KDB_WHY_BREAK, "manual escape to debugger"); 312257076Sray return (1); 313257076Sray case SPCLKEY | RBT: 314257076Sray /* XXX: Make this configurable! */ 315257076Sray shutdown_nice(0); 316257076Sray return (1); 317257076Sray case SPCLKEY | HALT: 318257076Sray shutdown_nice(RB_HALT); 319257076Sray return (1); 320257076Sray case SPCLKEY | PDWN: 321257076Sray shutdown_nice(RB_HALT|RB_POWEROFF); 322257076Sray return (1); 323257076Sray }; 324257076Sray 325257076Sray return (0); 326257076Sray} 327257076Sray 328257076Sraystatic void 329257076Srayvt_scrollmode_kbdevent(struct vt_window *vw, int c, int console) 330257076Sray{ 331257076Sray struct vt_device *vd; 332257076Sray term_pos_t size; 333257076Sray 334257076Sray vd = vw->vw_device; 335257076Sray /* Only special keys handled in ScrollLock mode */ 336257076Sray if ((c & SPCLKEY) == 0) 337257076Sray return; 338257076Sray 339257076Sray c &= ~SPCLKEY; 340257076Sray 341257076Sray if (console == 0) { 342257076Sray if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) { 343257076Sray vw = vd->vd_windows[c - F_SCR]; 344257076Sray if (vw != NULL) 345257076Sray vt_proc_window_switch(vw); 346257076Sray return; 347257076Sray } 348257076Sray VT_LOCK(vd); 349257076Sray } 350257076Sray 351257076Sray switch (c) { 352257076Sray case SLK: { 353257076Sray /* Turn scrolling off. */ 354257076Sray vt_scroll(vw, 0, VHS_END); 355257076Sray VTBUF_SLCK_DISABLE(&vw->vw_buf); 356257076Sray vw->vw_flags &= ~VWF_SCROLL; 357257076Sray break; 358257076Sray } 359257076Sray case FKEY | F(49): /* Home key. */ 360257076Sray vt_scroll(vw, 0, VHS_END); 361257076Sray break; 362257076Sray case FKEY | F(50): /* Arrow up. */ 363257076Sray vt_scroll(vw, -1, VHS_CUR); 364257076Sray break; 365257076Sray case FKEY | F(51): /* Page up. */ 366257076Sray vt_termsize(vd, vw->vw_font, &size); 367257076Sray vt_scroll(vw, -size.tp_row, VHS_CUR); 368257076Sray break; 369257076Sray case FKEY | F(57): /* End key. */ 370257076Sray vt_scroll(vw, 0, VHS_SET); 371257076Sray break; 372257076Sray case FKEY | F(58): /* Arrow down. */ 373257076Sray vt_scroll(vw, 1, VHS_CUR); 374257076Sray break; 375257076Sray case FKEY | F(59): /* Page down. */ 376257076Sray vt_termsize(vd, vw->vw_font, &size); 377257076Sray vt_scroll(vw, size.tp_row, VHS_CUR); 378257076Sray break; 379257076Sray } 380257076Sray 381257076Sray if (console == 0) 382257076Sray VT_UNLOCK(vd); 383257076Sray} 384257076Sray 385257076Sraystatic int 386257294Snwhitehornvt_processkey(keyboard_t *kbd, struct vt_device *vd, int c) 387219888Sed{ 388219888Sed struct vt_window *vw = vd->vd_curwindow; 389257294Snwhitehorn int state = 0; 390219888Sed 391219888Sed if (c & RELKEY) 392219888Sed return (0); 393219888Sed 394257076Sray if (vt_machine_kbdevent(c)) 395257076Sray return (0); 396257076Sray 397257076Sray if (vw->vw_flags & VWF_SCROLL) { 398257076Sray vt_scrollmode_kbdevent(vw, c, 0/* Not a console */); 399257076Sray /* Scroll mode keys handled, nothing to do more. */ 400257076Sray return (0); 401257076Sray } 402257076Sray 403219888Sed if (c & SPCLKEY) { 404219888Sed c &= ~SPCLKEY; 405219888Sed 406219888Sed if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) { 407219888Sed vw = vd->vd_windows[c - F_SCR]; 408219888Sed if (vw != NULL) 409256145Sray vt_proc_window_switch(vw); 410219888Sed return (0); 411219888Sed } 412219888Sed 413219888Sed switch (c) { 414219888Sed case SLK: { 415219888Sed 416219888Sed kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state); 417219888Sed VT_LOCK(vd); 418219888Sed if (state & SLKED) { 419219888Sed /* Turn scrolling on. */ 420219888Sed vw->vw_flags |= VWF_SCROLL; 421256145Sray VTBUF_SLCK_ENABLE(&vw->vw_buf); 422219888Sed } else { 423219888Sed /* Turn scrolling off. */ 424219888Sed vw->vw_flags &= ~VWF_SCROLL; 425256145Sray VTBUF_SLCK_DISABLE(&vw->vw_buf); 426257076Sray vt_scroll(vw, 0, VHS_END); 427219888Sed } 428219888Sed VT_UNLOCK(vd); 429219888Sed break; 430219888Sed } 431219888Sed case FKEY | F(1): case FKEY | F(2): case FKEY | F(3): 432219888Sed case FKEY | F(4): case FKEY | F(5): case FKEY | F(6): 433219888Sed case FKEY | F(7): case FKEY | F(8): case FKEY | F(9): 434219888Sed case FKEY | F(10): case FKEY | F(11): case FKEY | F(12): 435219888Sed /* F1 through F12 keys. */ 436219888Sed terminal_input_special(vw->vw_terminal, 437219888Sed TKEY_F1 + c - (FKEY | F(1))); 438219888Sed break; 439219888Sed case FKEY | F(49): /* Home key. */ 440219888Sed terminal_input_special(vw->vw_terminal, TKEY_HOME); 441219888Sed break; 442219888Sed case FKEY | F(50): /* Arrow up. */ 443219888Sed terminal_input_special(vw->vw_terminal, TKEY_UP); 444219888Sed break; 445219888Sed case FKEY | F(51): /* Page up. */ 446219888Sed terminal_input_special(vw->vw_terminal, TKEY_PAGE_UP); 447219888Sed break; 448219888Sed case FKEY | F(53): /* Arrow left. */ 449219888Sed terminal_input_special(vw->vw_terminal, TKEY_LEFT); 450219888Sed break; 451219888Sed case FKEY | F(55): /* Arrow right. */ 452219888Sed terminal_input_special(vw->vw_terminal, TKEY_RIGHT); 453219888Sed break; 454219888Sed case FKEY | F(57): /* End key. */ 455219888Sed terminal_input_special(vw->vw_terminal, TKEY_END); 456219888Sed break; 457219888Sed case FKEY | F(58): /* Arrow down. */ 458219888Sed terminal_input_special(vw->vw_terminal, TKEY_DOWN); 459219888Sed break; 460219888Sed case FKEY | F(59): /* Page down. */ 461219888Sed terminal_input_special(vw->vw_terminal, TKEY_PAGE_DOWN); 462219888Sed break; 463219888Sed case FKEY | F(60): /* Insert key. */ 464219888Sed terminal_input_special(vw->vw_terminal, TKEY_INSERT); 465219888Sed break; 466219888Sed case FKEY | F(61): /* Delete key. */ 467219888Sed terminal_input_special(vw->vw_terminal, TKEY_DELETE); 468219888Sed break; 469219888Sed } 470219888Sed } else if (KEYFLAGS(c) == 0) { 471219888Sed /* Don't do UTF-8 conversion when doing raw mode. */ 472219888Sed if (vw->vw_kbdmode == K_XLATE) 473256145Sray terminal_input_char(vw->vw_terminal, KEYCHAR(c)); 474219888Sed else 475219888Sed terminal_input_raw(vw->vw_terminal, c); 476219888Sed } 477219888Sed return (0); 478219888Sed} 479219888Sed 480219888Sedstatic int 481257294Snwhitehornvt_kbdevent(keyboard_t *kbd, int event, void *arg) 482257294Snwhitehorn{ 483257294Snwhitehorn struct vt_device *vd = arg; 484257294Snwhitehorn int c; 485257294Snwhitehorn 486257294Snwhitehorn switch (event) { 487257294Snwhitehorn case KBDIO_KEYINPUT: 488257294Snwhitehorn break; 489257294Snwhitehorn case KBDIO_UNLOADING: 490257294Snwhitehorn mtx_lock(&Giant); 491257294Snwhitehorn vd->vd_keyboard = -1; 492257294Snwhitehorn kbd_release(kbd, (void *)&vd->vd_keyboard); 493257294Snwhitehorn mtx_unlock(&Giant); 494257294Snwhitehorn return (0); 495257294Snwhitehorn default: 496257294Snwhitehorn return (EINVAL); 497257294Snwhitehorn } 498257294Snwhitehorn 499257294Snwhitehorn while ((c = kbdd_read_char(kbd, 0)) != NOKEY) 500257294Snwhitehorn vt_processkey(kbd, vd, c); 501257294Snwhitehorn 502257294Snwhitehorn return (0); 503257294Snwhitehorn} 504257294Snwhitehorn 505257294Snwhitehornstatic int 506219888Sedvt_allocate_keyboard(struct vt_device *vd) 507219888Sed{ 508219888Sed int idx0, idx; 509219888Sed keyboard_t *k0, *k; 510219888Sed keyboard_info_t ki; 511219888Sed 512219888Sed idx0 = kbd_allocate("kbdmux", -1, (void *)&vd->vd_keyboard, 513219888Sed vt_kbdevent, vd); 514256145Sray /* XXX: kb_token lost */ 515256145Sray vd->vd_keyboard = idx0; 516219888Sed if (idx0 != -1) { 517256145Sray DPRINTF(20, "%s: kbdmux allocated, idx = %d\n", __func__, idx0); 518219888Sed k0 = kbd_get_keyboard(idx0); 519219888Sed 520219888Sed for (idx = kbd_find_keyboard2("*", -1, 0); 521219888Sed idx != -1; 522219888Sed idx = kbd_find_keyboard2("*", -1, idx + 1)) { 523219888Sed k = kbd_get_keyboard(idx); 524219888Sed 525219888Sed if (idx == idx0 || KBD_IS_BUSY(k)) 526219888Sed continue; 527219888Sed 528219888Sed bzero(&ki, sizeof(ki)); 529219888Sed strcpy(ki.kb_name, k->kb_name); 530219888Sed ki.kb_unit = k->kb_unit; 531219888Sed 532219888Sed kbdd_ioctl(k0, KBADDKBD, (caddr_t) &ki); 533219888Sed } 534256145Sray } else { 535256145Sray DPRINTF(20, "%s: no kbdmux allocated\n", __func__); 536219888Sed idx0 = kbd_allocate("*", -1, (void *)&vd->vd_keyboard, 537219888Sed vt_kbdevent, vd); 538256145Sray } 539256145Sray DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard); 540219888Sed 541219888Sed return (idx0); 542219888Sed} 543219888Sed 544219888Sedstatic void 545219888Sedvtterm_bell(struct terminal *tm) 546219888Sed{ 547219888Sed 548219888Sed sysbeep(1193182 / VT_BELLPITCH, VT_BELLDURATION); 549219888Sed} 550219888Sed 551219888Sedstatic void 552219888Sedvtterm_cursor(struct terminal *tm, const term_pos_t *p) 553219888Sed{ 554219888Sed struct vt_window *vw = tm->tm_softc; 555219888Sed 556219888Sed vtbuf_cursor_position(&vw->vw_buf, p); 557219888Sed} 558219888Sed 559219888Sedstatic void 560219888Sedvtterm_putchar(struct terminal *tm, const term_pos_t *p, term_char_t c) 561219888Sed{ 562219888Sed struct vt_window *vw = tm->tm_softc; 563219888Sed 564219888Sed vtbuf_putchar(&vw->vw_buf, p, c); 565219888Sed} 566219888Sed 567219888Sedstatic void 568219888Sedvtterm_fill(struct terminal *tm, const term_rect_t *r, term_char_t c) 569219888Sed{ 570219888Sed struct vt_window *vw = tm->tm_softc; 571219888Sed 572256145Sray vtbuf_fill_locked(&vw->vw_buf, r, c); 573219888Sed} 574219888Sed 575219888Sedstatic void 576219888Sedvtterm_copy(struct terminal *tm, const term_rect_t *r, 577219888Sed const term_pos_t *p) 578219888Sed{ 579219888Sed struct vt_window *vw = tm->tm_softc; 580219888Sed 581219888Sed vtbuf_copy(&vw->vw_buf, r, p); 582219888Sed} 583219888Sed 584219888Sedstatic void 585219888Sedvtterm_param(struct terminal *tm, int cmd, unsigned int arg) 586219888Sed{ 587219888Sed struct vt_window *vw = tm->tm_softc; 588219888Sed 589219888Sed switch (cmd) { 590219888Sed case TP_SHOWCURSOR: 591219888Sed vtbuf_cursor_visibility(&vw->vw_buf, arg); 592219888Sed break; 593219888Sed } 594219888Sed} 595219888Sed 596219888Sedstatic inline void 597219888Sedvt_determine_colors(term_char_t c, int cursor, 598219888Sed term_color_t *fg, term_color_t *bg) 599219888Sed{ 600219888Sed 601219888Sed *fg = TCHAR_FGCOLOR(c); 602219888Sed if (TCHAR_FORMAT(c) & TF_BOLD) 603219888Sed *fg = TCOLOR_LIGHT(*fg); 604219888Sed *bg = TCHAR_BGCOLOR(c); 605219888Sed 606219888Sed if (TCHAR_FORMAT(c) & TF_REVERSE) { 607219888Sed term_color_t tmp; 608219888Sed 609219888Sed tmp = *fg; 610219888Sed *fg = *bg; 611219888Sed *bg = tmp; 612219888Sed } 613219888Sed 614219888Sed if (cursor) { 615219888Sed *fg = *bg; 616219888Sed *bg = TC_WHITE; 617219888Sed } 618219888Sed} 619219888Sed 620219888Sedstatic void 621219888Sedvt_bitblt_char(struct vt_device *vd, struct vt_font *vf, term_char_t c, 622219888Sed int iscursor, unsigned int row, unsigned int col) 623219888Sed{ 624219888Sed term_color_t fg, bg; 625219888Sed 626219888Sed vt_determine_colors(c, iscursor, &fg, &bg); 627219888Sed 628219888Sed if (vf != NULL) { 629219888Sed const uint8_t *src; 630219888Sed vt_axis_t top, left; 631219888Sed 632219888Sed src = vtfont_lookup(vf, c); 633219888Sed 634219888Sed /* 635219888Sed * Align the terminal to the centre of the screen. 636219888Sed * Fonts may not always be able to fill the entire 637219888Sed * screen. 638219888Sed */ 639219888Sed top = row * vf->vf_height + 640219888Sed (vd->vd_height % vf->vf_height) / 2; 641219888Sed left = col * vf->vf_width + 642219888Sed (vd->vd_width % vf->vf_width) / 2; 643219888Sed 644256527Sray vd->vd_driver->vd_bitbltchr(vd, src, top, left, 645219888Sed vf->vf_width, vf->vf_height, fg, bg); 646219888Sed } else { 647219888Sed vd->vd_driver->vd_putchar(vd, TCHAR_CHARACTER(c), 648219888Sed row, col, fg, bg); 649219888Sed } 650219888Sed} 651219888Sed 652219888Sedstatic void 653219888Sedvt_flush(struct vt_device *vd) 654219888Sed{ 655219888Sed struct vt_window *vw = vd->vd_curwindow; 656219888Sed struct vt_font *vf = vw->vw_font; 657219888Sed term_pos_t size; 658219888Sed term_rect_t tarea; 659219888Sed struct vt_bufmask tmask; 660256145Sray unsigned int row, col; 661256145Sray term_char_t *r; 662219888Sed 663219888Sed if (vd->vd_flags & VDF_SPLASH || vw->vw_flags & VWF_BUSY) 664219888Sed return; 665219888Sed 666219888Sed vtbuf_undirty(&vw->vw_buf, &tarea, &tmask); 667219888Sed vt_termsize(vd, vf, &size); 668219888Sed 669219888Sed /* Force a full redraw when the screen contents are invalid. */ 670256145Sray if (vd->vd_flags & VDF_INVALID) { 671219888Sed tarea.tr_begin.tp_row = tarea.tr_begin.tp_col = 0; 672219888Sed tarea.tr_end = size; 673219888Sed tmask.vbm_row = tmask.vbm_col = VBM_DIRTY; 674219888Sed 675219888Sed /* 676219888Sed * Blank to prevent borders with artifacts. This is 677219888Sed * only required when the font doesn't exactly fill the 678219888Sed * screen. 679219888Sed */ 680219888Sed if (vd->vd_flags & VDF_INVALID && vf != NULL && 681219888Sed (vd->vd_width % vf->vf_width != 0 || 682219888Sed vd->vd_height % vf->vf_height != 0)) 683219888Sed vd->vd_driver->vd_blank(vd, TC_BLACK); 684219888Sed 685219888Sed vd->vd_flags &= ~VDF_INVALID; 686219888Sed } 687219888Sed 688219888Sed 689219888Sed for (row = tarea.tr_begin.tp_row; row < tarea.tr_end.tp_row; row++) { 690219888Sed if (!VTBUF_DIRTYROW(&tmask, row)) 691219888Sed continue; 692256145Sray r = VTBUF_GET_ROW(&vw->vw_buf, row); 693219888Sed for (col = tarea.tr_begin.tp_col; 694219888Sed col < tarea.tr_end.tp_col; col++) { 695219888Sed if (!VTBUF_DIRTYCOL(&tmask, col)) 696219888Sed continue; 697219888Sed 698256145Sray vt_bitblt_char(vd, vf, r[col], 699256903Sray VTBUF_ISCURSOR(&vw->vw_buf, row, col), row, col); 700219888Sed } 701219888Sed } 702219888Sed} 703219888Sed 704219888Sedstatic void 705219888Sedvt_timer(void *arg) 706219888Sed{ 707257387Sray struct vt_device *vd; 708219888Sed 709257387Sray vd = arg; 710257387Sray /* Update screen if required. */ 711219888Sed vt_flush(vd); 712257387Sray /* Schedule for next update. */ 713219888Sed callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ); 714219888Sed} 715219888Sed 716219888Sedstatic void 717219888Sedvtterm_done(struct terminal *tm) 718219888Sed{ 719219888Sed struct vt_window *vw = tm->tm_softc; 720219888Sed struct vt_device *vd = vw->vw_device; 721219888Sed 722219888Sed if (kdb_active || panicstr != NULL) { 723219888Sed /* Switch to the debugger. */ 724219888Sed if (vd->vd_curwindow != vw) { 725219888Sed vd->vd_curwindow = vw; 726219888Sed vd->vd_flags |= VDF_INVALID; 727257626Sray if (vd->vd_driver->vd_postswitch) 728257626Sray vd->vd_driver->vd_postswitch(vd); 729219888Sed } 730219888Sed vd->vd_flags &= ~VDF_SPLASH; 731219888Sed vt_flush(vd); 732219888Sed } else if (!(vd->vd_flags & VDF_ASYNC)) { 733219888Sed vt_flush(vd); 734219888Sed } 735219888Sed} 736219888Sed 737219888Sedstatic void 738256145Srayvtterm_splash(struct vt_device *vd) 739256145Sray{ 740256145Sray vt_axis_t top, left; 741256145Sray 742256145Sray /* Display a nice boot splash. */ 743257722Sray if (!(vd->vd_flags & VDF_TEXTMODE) && (boothowto & RB_MUTE)) { 744256145Sray 745256145Sray top = (vd->vd_height - vt_logo_height) / 2; 746256145Sray left = (vd->vd_width - vt_logo_width) / 2; 747256145Sray switch (vt_logo_depth) { 748256145Sray case 1: 749256145Sray /* XXX: Unhardcode colors! */ 750256527Sray vd->vd_driver->vd_bitbltchr(vd, vt_logo_image, top, left, 751256145Sray vt_logo_width, vt_logo_height, 0xf, 0x0); 752256145Sray } 753256145Sray vd->vd_flags |= VDF_SPLASH; 754256145Sray } 755256145Sray} 756256145Sray 757256145Sraystatic void 758219888Sedvtterm_cnprobe(struct terminal *tm, struct consdev *cp) 759219888Sed{ 760219888Sed struct vt_window *vw = tm->tm_softc; 761219888Sed struct vt_device *vd = vw->vw_device; 762219888Sed struct winsize wsz; 763219888Sed 764256145Sray if (vd->vd_flags & VDF_INITIALIZED) 765256145Sray /* Initialization already done. */ 766256145Sray return; 767256145Sray 768219888Sed cp->cn_pri = vd->vd_driver->vd_init(vd); 769219888Sed if (cp->cn_pri == CN_DEAD) { 770219888Sed vd->vd_flags |= VDF_DEAD; 771219888Sed return; 772219888Sed } 773219888Sed 774230469Snwhitehorn /* Initialize any early-boot keyboard drivers */ 775230469Snwhitehorn kbd_configure(KB_CONF_PROBE_ONLY); 776230469Snwhitehorn 777219888Sed vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1); 778256145Sray vd->vd_windows[VT_CONSWINDOW] = vw; 779219888Sed sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw)); 780219888Sed 781219888Sed if (!(vd->vd_flags & VDF_TEXTMODE)) 782219888Sed vw->vw_font = vtfont_ref(&vt_font_default); 783219888Sed 784219888Sed vtbuf_init_early(&vw->vw_buf); 785219888Sed vt_winsize(vd, vw->vw_font, &wsz); 786219888Sed terminal_set_winsize(tm, &wsz); 787219888Sed 788256145Sray vtterm_splash(vd); 789219888Sed 790256145Sray vd->vd_flags |= VDF_INITIALIZED; 791256145Sray main_vd = vd; 792219888Sed} 793219888Sed 794219888Sedstatic int 795219888Sedvtterm_cngetc(struct terminal *tm) 796219888Sed{ 797219888Sed struct vt_window *vw = tm->tm_softc; 798219888Sed struct vt_device *vd = vw->vw_device; 799219888Sed keyboard_t *kbd; 800257076Sray int state; 801219888Sed u_int c; 802219888Sed 803257076Sray if (vw->vw_kbdsq && *vw->vw_kbdsq) 804257076Sray return (*vw->vw_kbdsq++); 805257076Sray 806257076Sray state = 0; 807219888Sed /* Make sure the splash screen is not there. */ 808219888Sed if (vd->vd_flags & VDF_SPLASH) { 809256145Sray /* Remove splash */ 810219888Sed vd->vd_flags &= ~VDF_SPLASH; 811256145Sray /* Mark screen as invalid to force update */ 812256145Sray vd->vd_flags |= VDF_INVALID; 813219888Sed vt_flush(vd); 814219888Sed } 815219888Sed 816219888Sed /* Stripped down keyboard handler. */ 817219888Sed kbd = kbd_get_keyboard(vd->vd_keyboard); 818219888Sed if (kbd == NULL) 819219888Sed return (-1); 820219888Sed 821256145Sray /* Force keyboard input mode to K_XLATE */ 822256145Sray c = K_XLATE; 823256145Sray kbdd_ioctl(kbd, KDSKBMODE, (void *)&c); 824256145Sray 825219888Sed /* Switch the keyboard to polling to make it work here. */ 826219888Sed kbdd_poll(kbd, TRUE); 827219888Sed c = kbdd_read_char(kbd, 0); 828219888Sed kbdd_poll(kbd, FALSE); 829219888Sed if (c & RELKEY) 830219888Sed return (-1); 831256145Sray 832257076Sray if (vw->vw_flags & VWF_SCROLL) { 833257076Sray vt_scrollmode_kbdevent(vw, c, 1/* Console mode */); 834257076Sray vt_flush(vd); 835257076Sray return (-1); 836257076Sray } 837257076Sray 838219888Sed /* Stripped down handling of vt_kbdevent(), without locking, etc. */ 839219888Sed if (c & SPCLKEY) { 840219888Sed switch (c) { 841257076Sray case SPCLKEY | SLK: 842219888Sed kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state); 843219888Sed if (state & SLKED) { 844219888Sed /* Turn scrolling on. */ 845219888Sed vw->vw_flags |= VWF_SCROLL; 846256145Sray VTBUF_SLCK_ENABLE(&vw->vw_buf); 847219888Sed } else { 848219888Sed /* Turn scrolling off. */ 849257076Sray vt_scroll(vw, 0, VHS_END); 850219888Sed vw->vw_flags &= ~VWF_SCROLL; 851256145Sray VTBUF_SLCK_DISABLE(&vw->vw_buf); 852219888Sed } 853219888Sed break; 854257076Sray /* XXX: KDB can handle history. */ 855257076Sray case SPCLKEY | FKEY | F(50): /* Arrow up. */ 856257076Sray vw->vw_kbdsq = "\x1b[A"; 857219888Sed break; 858257076Sray case SPCLKEY | FKEY | F(58): /* Arrow down. */ 859257076Sray vw->vw_kbdsq = "\x1b[B"; 860219888Sed break; 861257076Sray case SPCLKEY | FKEY | F(55): /* Arrow right. */ 862257076Sray vw->vw_kbdsq = "\x1b[C"; 863219888Sed break; 864257076Sray case SPCLKEY | FKEY | F(53): /* Arrow left. */ 865257076Sray vw->vw_kbdsq = "\x1b[D"; 866219888Sed break; 867219888Sed } 868219888Sed 869219888Sed /* Force refresh to make scrollback work. */ 870219888Sed vt_flush(vd); 871219888Sed } else if (KEYFLAGS(c) == 0) { 872219888Sed return KEYCHAR(c); 873219888Sed } 874256145Sray 875257076Sray if (vw->vw_kbdsq && *vw->vw_kbdsq) 876257076Sray return (* 877257076Sray vw->vw_kbdsq++); 878257076Sray 879219888Sed return (-1); 880219888Sed} 881219888Sed 882219888Sedstatic void 883219888Sedvtterm_opened(struct terminal *tm, int opened) 884219888Sed{ 885219888Sed struct vt_window *vw = tm->tm_softc; 886219888Sed struct vt_device *vd = vw->vw_device; 887219888Sed 888219888Sed VT_LOCK(vd); 889219888Sed vd->vd_flags &= ~VDF_SPLASH; 890219888Sed if (opened) 891219888Sed vw->vw_flags |= VWF_OPENED; 892256145Sray else { 893219888Sed vw->vw_flags &= ~VWF_OPENED; 894256145Sray /* TODO: finish ACQ/REL */ 895256145Sray } 896219888Sed VT_UNLOCK(vd); 897219888Sed} 898219888Sed 899219888Sedstatic int 900219888Sedvt_change_font(struct vt_window *vw, struct vt_font *vf) 901219888Sed{ 902219888Sed struct vt_device *vd = vw->vw_device; 903219888Sed struct terminal *tm = vw->vw_terminal; 904219888Sed term_pos_t size; 905219888Sed struct winsize wsz; 906219888Sed 907219888Sed /* 908219888Sed * Changing fonts. 909219888Sed * 910219888Sed * Changing fonts is a little tricky. We must prevent 911219888Sed * simultaneous access to the device, so we must stop 912219888Sed * the display timer and the terminal from accessing. 913219888Sed * We need to switch fonts and grow our screen buffer. 914219888Sed * 915219888Sed * XXX: Right now the code uses terminal_mute() to 916219888Sed * prevent data from reaching the console driver while 917219888Sed * resizing the screen buffer. This isn't elegant... 918219888Sed */ 919219888Sed 920219888Sed VT_LOCK(vd); 921219888Sed if (vw->vw_flags & VWF_BUSY) { 922219888Sed /* Another process is changing the font. */ 923219888Sed VT_UNLOCK(vd); 924219888Sed return (EBUSY); 925219888Sed } 926219888Sed if (vw->vw_font == NULL) { 927219888Sed /* Our device doesn't need fonts. */ 928219888Sed VT_UNLOCK(vd); 929219888Sed return (ENOTTY); 930219888Sed } 931219888Sed vw->vw_flags |= VWF_BUSY; 932219888Sed VT_UNLOCK(vd); 933219888Sed 934219888Sed vt_termsize(vd, vf, &size); 935219888Sed vt_winsize(vd, vf, &wsz); 936219888Sed 937219888Sed /* Grow the screen buffer and terminal. */ 938219888Sed terminal_mute(tm, 1); 939256145Sray vtbuf_grow(&vw->vw_buf, &size, vw->vw_buf.vb_history_size); 940256903Sray terminal_set_winsize_blank(tm, &wsz, 0); 941219888Sed terminal_mute(tm, 0); 942219888Sed 943219888Sed /* Actually apply the font to the current window. */ 944219888Sed VT_LOCK(vd); 945219888Sed vtfont_unref(vw->vw_font); 946219888Sed vw->vw_font = vtfont_ref(vf); 947219888Sed 948219888Sed /* Force a full redraw the next timer tick. */ 949219888Sed if (vd->vd_curwindow == vw) 950219888Sed vd->vd_flags |= VDF_INVALID; 951219888Sed vw->vw_flags &= ~VWF_BUSY; 952219888Sed VT_UNLOCK(vd); 953219888Sed return (0); 954219888Sed} 955219888Sed 956219888Sedstatic int 957256145Srayvt_proc_alive(struct vt_window *vw) 958256145Sray{ 959256145Sray struct proc *p; 960256145Sray 961256145Sray if (vw->vw_smode.mode != VT_PROCESS) 962256145Sray return FALSE; 963256145Sray 964256145Sray if (vw->vw_proc) { 965256145Sray if ((p = pfind(vw->vw_pid)) != NULL) 966256145Sray PROC_UNLOCK(p); 967256145Sray if (vw->vw_proc == p) 968256145Sray return TRUE; 969256145Sray vw->vw_proc = NULL; 970256145Sray vw->vw_smode.mode = VT_AUTO; 971256145Sray DPRINTF(1, "vt controlling process %d died\n", vw->vw_pid); 972256145Sray vw->vw_pid = 0; 973256145Sray } 974256145Sray return FALSE; 975256145Sray} 976256145Sray 977256145Sraystatic int 978256145Sraysignal_vt_rel(struct vt_window *vw) 979256145Sray{ 980257815Sray 981256145Sray if (vw->vw_smode.mode != VT_PROCESS) 982256145Sray return FALSE; 983256145Sray if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) { 984256145Sray vw->vw_proc = NULL; 985256145Sray vw->vw_pid = 0; 986256145Sray return TRUE; 987256145Sray } 988256145Sray vw->vw_flags |= VWF_SWWAIT_REL; 989256145Sray PROC_LOCK(vw->vw_proc); 990256145Sray kern_psignal(vw->vw_proc, vw->vw_smode.relsig); 991256145Sray PROC_UNLOCK(vw->vw_proc); 992256145Sray DPRINTF(1, "sending relsig to %d\n", vw->vw_pid); 993256145Sray return TRUE; 994256145Sray} 995256145Sray 996256145Sraystatic int 997256145Sraysignal_vt_acq(struct vt_window *vw) 998256145Sray{ 999257815Sray 1000256145Sray if (vw->vw_smode.mode != VT_PROCESS) 1001256145Sray return FALSE; 1002256145Sray if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW]) 1003256145Sray cnavailable(vw->vw_terminal->consdev, FALSE); 1004256145Sray if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) { 1005256145Sray vw->vw_proc = NULL; 1006256145Sray vw->vw_pid = 0; 1007256145Sray return TRUE; 1008256145Sray } 1009256145Sray vw->vw_flags |= VWF_SWWAIT_ACQ; 1010256145Sray PROC_LOCK(vw->vw_proc); 1011256145Sray kern_psignal(vw->vw_proc, vw->vw_smode.acqsig); 1012256145Sray PROC_UNLOCK(vw->vw_proc); 1013256145Sray DPRINTF(1, "sending acqsig to %d\n", vw->vw_pid); 1014256145Sray return TRUE; 1015256145Sray} 1016256145Sray 1017256145Sraystatic int 1018256145Srayfinish_vt_rel(struct vt_window *vw, int release, int *s) 1019256145Sray{ 1020257815Sray 1021256145Sray if (vw->vw_flags & VWF_SWWAIT_REL) { 1022256145Sray vw->vw_flags &= ~VWF_SWWAIT_REL; 1023256145Sray if (release) { 1024256145Sray callout_drain(&vw->vw_proc_dead_timer); 1025256145Sray vt_late_window_switch(vw->vw_switch_to); 1026256145Sray } 1027256145Sray return 0; 1028256145Sray } 1029256145Sray return EINVAL; 1030256145Sray} 1031256145Sray 1032256145Sraystatic int 1033256145Srayfinish_vt_acq(struct vt_window *vw) 1034256145Sray{ 1035257815Sray 1036256145Sray if (vw->vw_flags & VWF_SWWAIT_ACQ) { 1037256145Sray vw->vw_flags &= ~VWF_SWWAIT_ACQ; 1038256145Sray return 0; 1039256145Sray } 1040256145Sray return EINVAL; 1041256145Sray} 1042256145Sray 1043256145Sraystatic int 1044219888Sedvtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data, 1045219888Sed struct thread *td) 1046219888Sed{ 1047219888Sed struct vt_window *vw = tm->tm_softc; 1048219888Sed struct vt_device *vd = vw->vw_device; 1049256145Sray int error, s; 1050219888Sed 1051219888Sed switch (cmd) { 1052219888Sed case GIO_KEYMAP: 1053219888Sed case PIO_KEYMAP: 1054219888Sed case GIO_DEADKEYMAP: 1055219888Sed case PIO_DEADKEYMAP: 1056219888Sed case GETFKEY: 1057219888Sed case SETFKEY: 1058219888Sed case KDGKBINFO: { 1059219888Sed keyboard_t *kbd; 1060256145Sray error = 0; 1061219888Sed 1062219888Sed mtx_lock(&Giant); 1063219888Sed kbd = kbd_get_keyboard(vd->vd_keyboard); 1064219888Sed if (kbd != NULL) 1065219888Sed error = kbdd_ioctl(kbd, cmd, data); 1066219888Sed mtx_unlock(&Giant); 1067219888Sed if (error == ENOIOCTL) 1068219888Sed return (ENODEV); 1069219888Sed return (error); 1070219888Sed } 1071256145Sray case KDGKBMODE: { 1072256145Sray int mode = -1; 1073256145Sray keyboard_t *kbd; 1074256145Sray 1075256145Sray mtx_lock(&Giant); 1076256145Sray kbd = kbd_get_keyboard(vd->vd_keyboard); 1077256145Sray if (kbd != NULL) { 1078256145Sray kbdd_ioctl(kbd, KDGKBMODE, (void *)&mode); 1079256145Sray } 1080256145Sray mtx_unlock(&Giant); 1081256145Sray DPRINTF(20, "mode %d, vw_kbdmode %d\n", mode, vw->vw_kbdmode); 1082256145Sray *(int *)data = mode; 1083256145Sray return (0); 1084256145Sray } 1085219888Sed case KDSKBMODE: { 1086219888Sed int mode; 1087219888Sed 1088219888Sed mode = *(int *)data; 1089219888Sed switch (mode) { 1090219888Sed case K_XLATE: 1091219888Sed case K_RAW: 1092219888Sed case K_CODE: 1093219888Sed vw->vw_kbdmode = mode; 1094219888Sed if (vw == vd->vd_curwindow) { 1095219888Sed keyboard_t *kbd; 1096256145Sray error = 0; 1097219888Sed 1098256145Sray DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard); 1099219888Sed mtx_lock(&Giant); 1100219888Sed kbd = kbd_get_keyboard(vd->vd_keyboard); 1101256145Sray if (kbd != NULL) { 1102256145Sray DPRINTF(20, "kbdd_ioctl(KDSKBMODE, %d)\n", mode); 1103256145Sray error = kbdd_ioctl(kbd, KDSKBMODE, 1104219888Sed (void *)&mode); 1105256145Sray } 1106219888Sed mtx_unlock(&Giant); 1107256145Sray if (error) 1108256145Sray DPRINTF(20, "kbdd_ioctl(KDSKBMODE) return %d\n", error); 1109219888Sed } 1110219888Sed return (0); 1111219888Sed default: 1112219888Sed return (EINVAL); 1113219888Sed } 1114219888Sed } 1115219888Sed case CONS_BLANKTIME: 1116219888Sed /* XXX */ 1117219888Sed return (0); 1118219888Sed case CONS_GET: 1119219888Sed /* XXX */ 1120219888Sed *(int *)data = M_CG640x480; 1121219888Sed return (0); 1122219888Sed case CONS_GETINFO: { 1123219888Sed vid_info_t *vi = (vid_info_t *)data; 1124219888Sed 1125219888Sed vi->m_num = vd->vd_curwindow->vw_number + 1; 1126219888Sed /* XXX: other fields! */ 1127219888Sed return (0); 1128219888Sed } 1129219888Sed case CONS_GETVERS: 1130219888Sed *(int *)data = 0x200; 1131219888Sed return 0; 1132219888Sed case CONS_MODEINFO: 1133219888Sed /* XXX */ 1134219888Sed return (0); 1135219888Sed case CONS_MOUSECTL: { 1136219888Sed mouse_info_t *mouse = (mouse_info_t*)data; 1137219888Sed 1138219888Sed /* 1139219888Sed * This has no effect on vt(4). We don't draw any mouse 1140219888Sed * cursor. Just ignore MOUSE_HIDE and MOUSE_SHOW to 1141219888Sed * prevent excessive errors. All the other commands 1142219888Sed * should not be applied to individual TTYs, but only to 1143219888Sed * consolectl. 1144219888Sed */ 1145219888Sed switch (mouse->operation) { 1146219888Sed case MOUSE_HIDE: 1147219888Sed case MOUSE_SHOW: 1148219888Sed return (0); 1149219888Sed default: 1150219888Sed return (EINVAL); 1151219888Sed } 1152219888Sed } 1153219888Sed case PIO_VFONT: { 1154219888Sed struct vt_font *vf; 1155219888Sed 1156219888Sed error = vtfont_load((void *)data, &vf); 1157219888Sed if (error != 0) 1158219888Sed return (error); 1159219888Sed 1160219888Sed error = vt_change_font(vw, vf); 1161219888Sed vtfont_unref(vf); 1162219888Sed return (error); 1163219888Sed } 1164219888Sed case GIO_SCRNMAP: { 1165219888Sed scrmap_t *sm = (scrmap_t *)data; 1166219888Sed int i; 1167219888Sed 1168219888Sed /* We don't have screen maps, so return a handcrafted one. */ 1169219888Sed for (i = 0; i < 256; i++) 1170219888Sed sm->scrmap[i] = i; 1171219888Sed return (0); 1172219888Sed } 1173219888Sed case KDGETLED: 1174219888Sed /* XXX */ 1175219888Sed return (0); 1176219888Sed case KDSETLED: 1177219888Sed /* XXX */ 1178219888Sed return (0); 1179219888Sed case KDSETMODE: 1180219888Sed /* XXX */ 1181219888Sed return (0); 1182219888Sed case KDSETRAD: 1183219888Sed /* XXX */ 1184219888Sed return (0); 1185256145Sray case VT_ACTIVATE: { 1186256145Sray int win; 1187256145Sray win = *(int *)data - 1; 1188256145Sray DPRINTF(5, "%s%d: VT_ACTIVATE ttyv%d ", SC_DRIVER_NAME, VT_UNIT(vw), win); 1189256145Sray if ((win > VT_MAXWINDOWS) || (win < 0)) 1190256145Sray return (EINVAL); 1191256145Sray return (vt_proc_window_switch(vd->vd_windows[win])); 1192256145Sray } 1193219888Sed case VT_GETACTIVE: 1194219888Sed *(int *)data = vd->vd_curwindow->vw_number + 1; 1195219888Sed return (0); 1196219888Sed case VT_GETINDEX: 1197219888Sed *(int *)data = vw->vw_number + 1; 1198219888Sed return (0); 1199256145Sray case VT_LOCKSWITCH: 1200256145Sray /* TODO: Check current state, switching can be in progress. */ 1201256145Sray if ((*(int *)data) & 0x01) 1202256145Sray vw->vw_flags |= VWF_VTYLOCK; 1203256145Sray else 1204256145Sray vw->vw_flags &= ~VWF_VTYLOCK; 1205219888Sed case VT_OPENQRY: { 1206219888Sed unsigned int i; 1207219888Sed 1208219888Sed VT_LOCK(vd); 1209219888Sed for (i = 0; i < VT_MAXWINDOWS; i++) { 1210219888Sed vw = vd->vd_windows[i]; 1211219888Sed if (vw == NULL) 1212219888Sed continue; 1213219888Sed if (!(vw->vw_flags & VWF_OPENED)) { 1214219888Sed *(int *)data = vw->vw_number + 1; 1215219888Sed VT_UNLOCK(vd); 1216219888Sed return (0); 1217219888Sed } 1218219888Sed } 1219219888Sed VT_UNLOCK(vd); 1220219888Sed return (EINVAL); 1221219888Sed } 1222219888Sed case VT_WAITACTIVE: { 1223219888Sed unsigned int i; 1224256145Sray error = 0; 1225219888Sed 1226219888Sed i = *(unsigned int *)data; 1227219888Sed if (i > VT_MAXWINDOWS) 1228219888Sed return (EINVAL); 1229219888Sed if (i != 0) 1230219888Sed vw = vd->vd_windows[i - 1]; 1231219888Sed 1232219888Sed VT_LOCK(vd); 1233219888Sed while (vd->vd_curwindow != vw && error == 0) 1234219888Sed error = cv_wait_sig(&vd->vd_winswitch, &vd->vd_lock); 1235219888Sed VT_UNLOCK(vd); 1236219888Sed return (error); 1237219888Sed } 1238256145Sray case VT_SETMODE: /* set screen switcher mode */ 1239256145Sray { 1240256145Sray struct vt_mode *mode; 1241256145Sray struct proc *p1; 1242256145Sray 1243256145Sray mode = (struct vt_mode *)data; 1244256145Sray DPRINTF(5, "%s%d: VT_SETMODE ", SC_DRIVER_NAME, VT_UNIT(vw)); 1245256145Sray if (vw->vw_smode.mode == VT_PROCESS) { 1246256145Sray p1 = pfind(vw->vw_pid); 1247256145Sray if (vw->vw_proc == p1 && vw->vw_proc != td->td_proc) { 1248256145Sray if (p1) 1249256145Sray PROC_UNLOCK(p1); 1250256145Sray DPRINTF(5, "error EPERM\n"); 1251256145Sray return (EPERM); 1252256145Sray } 1253256145Sray if (p1) 1254256145Sray PROC_UNLOCK(p1); 1255256145Sray } 1256256145Sray if (mode->mode == VT_AUTO) { 1257256145Sray vw->vw_smode.mode = VT_AUTO; 1258256145Sray vw->vw_proc = NULL; 1259256145Sray vw->vw_pid = 0; 1260256145Sray DPRINTF(5, "VT_AUTO, "); 1261256145Sray if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW]) 1262256145Sray cnavailable(vw->vw_terminal->consdev, TRUE); 1263256145Sray /* were we in the middle of the vty switching process? */ 1264256145Sray if (finish_vt_rel(vw, TRUE, &s) == 0) 1265256145Sray DPRINTF(5, "reset WAIT_REL, "); 1266256145Sray if (finish_vt_acq(vw) == 0) 1267256145Sray DPRINTF(5, "reset WAIT_ACQ, "); 1268256145Sray return (0); 1269256145Sray } else if (mode->mode == VT_PROCESS) { 1270256145Sray if (!ISSIGVALID(mode->relsig) || 1271256145Sray !ISSIGVALID(mode->acqsig) || 1272256145Sray !ISSIGVALID(mode->frsig)) { 1273256145Sray DPRINTF(5, "error EINVAL\n"); 1274256145Sray return (EINVAL); 1275256145Sray } 1276256145Sray DPRINTF(5, "VT_PROCESS %d, ", td->td_proc->p_pid); 1277256145Sray bcopy(data, &vw->vw_smode, sizeof(struct vt_mode)); 1278256145Sray vw->vw_proc = td->td_proc; 1279256145Sray vw->vw_pid = vw->vw_proc->p_pid; 1280256145Sray if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW]) 1281256145Sray cnavailable(vw->vw_terminal->consdev, FALSE); 1282256145Sray } else { 1283256145Sray DPRINTF(5, "VT_SETMODE failed, unknown mode %d\n", 1284256145Sray mode->mode); 1285256145Sray return (EINVAL); 1286256145Sray } 1287256145Sray DPRINTF(5, "\n"); 1288256145Sray return 0; 1289219888Sed } 1290219888Sed 1291256145Sray case VT_GETMODE: /* get screen switcher mode */ 1292256145Sray bcopy(&vw->vw_smode, data, sizeof(struct vt_mode)); 1293256145Sray return 0; 1294256145Sray 1295256145Sray case VT_RELDISP: /* screen switcher ioctl */ 1296256145Sray /* 1297256145Sray * This must be the current vty which is in the VT_PROCESS 1298256145Sray * switching mode... 1299256145Sray */ 1300256145Sray if ((vw != vd->vd_curwindow) || (vw->vw_smode.mode != 1301256145Sray VT_PROCESS)) { 1302256145Sray return EINVAL; 1303256145Sray } 1304256145Sray /* ...and this process is controlling it. */ 1305256145Sray if (vw->vw_proc != td->td_proc) { 1306256145Sray return EPERM; 1307256145Sray } 1308256145Sray error = EINVAL; 1309256145Sray switch(*(int *)data) { 1310256145Sray case VT_FALSE: /* user refuses to release screen, abort */ 1311256145Sray if ((error = finish_vt_rel(vw, FALSE, &s)) == 0) 1312256145Sray DPRINTF(5, "%s%d: VT_RELDISP: VT_FALSE\n", SC_DRIVER_NAME, 1313256145Sray VT_UNIT(vw)); 1314256145Sray break; 1315256145Sray case VT_TRUE: /* user has released screen, go on */ 1316256145Sray /* finish_vt_rel(..., TRUE, ...) should not be locked */ 1317256145Sray if (vw->vw_flags & VWF_SWWAIT_REL) { 1318256145Sray if ((error = finish_vt_rel(vw, TRUE, &s)) == 0) 1319256145Sray DPRINTF(5, "%s%d: VT_RELDISP: VT_TRUE\n", 1320256145Sray SC_DRIVER_NAME, VT_UNIT(vw)); 1321256145Sray } else { 1322256145Sray error = EINVAL; 1323256145Sray } 1324256145Sray return (error); 1325256145Sray case VT_ACKACQ: /* acquire acknowledged, switch completed */ 1326256145Sray if ((error = finish_vt_acq(vw)) == 0) 1327256145Sray DPRINTF(5, "%s%d: VT_RELDISP: VT_ACKACQ\n", SC_DRIVER_NAME, 1328256145Sray VT_UNIT(vw)); 1329256145Sray break; 1330256145Sray default: 1331256145Sray break; 1332256145Sray } 1333256145Sray return error; 1334256145Sray } 1335256145Sray 1336219888Sed return (ENOIOCTL); 1337219888Sed} 1338219888Sed 1339219888Sedstatic struct vt_window * 1340219888Sedvt_allocate_window(struct vt_device *vd, unsigned int window) 1341219888Sed{ 1342219888Sed struct vt_window *vw; 1343219888Sed struct terminal *tm; 1344219888Sed term_pos_t size; 1345219888Sed struct winsize wsz; 1346219888Sed 1347219888Sed vw = malloc(sizeof *vw, M_VT, M_WAITOK|M_ZERO); 1348219888Sed vw->vw_device = vd; 1349219888Sed vw->vw_number = window; 1350219888Sed vw->vw_kbdmode = K_XLATE; 1351219888Sed 1352219888Sed if (!(vd->vd_flags & VDF_TEXTMODE)) 1353219888Sed vw->vw_font = vtfont_ref(&vt_font_default); 1354256145Sray 1355219888Sed vt_termsize(vd, vw->vw_font, &size); 1356219888Sed vt_winsize(vd, vw->vw_font, &wsz); 1357219888Sed vtbuf_init(&vw->vw_buf, &size); 1358219888Sed 1359219888Sed tm = vw->vw_terminal = terminal_alloc(&vt_termclass, vw); 1360219888Sed terminal_set_winsize(tm, &wsz); 1361219888Sed vd->vd_windows[window] = vw; 1362256145Sray callout_init(&vw->vw_proc_dead_timer, 0); 1363219888Sed 1364219888Sed return (vw); 1365219888Sed} 1366219888Sed 1367219888Sedvoid 1368219888Sedvt_upgrade(struct vt_device *vd) 1369219888Sed{ 1370219888Sed struct vt_window *vw; 1371219888Sed unsigned int i; 1372219888Sed 1373256145Sray /* Device didn't pass vd_init() or already upgraded. */ 1374256145Sray if (vd->vd_flags & (VDF_ASYNC|VDF_DEAD)) 1375219888Sed return; 1376256145Sray vd->vd_flags |= VDF_ASYNC; 1377219888Sed 1378219888Sed mtx_init(&vd->vd_lock, "vtdev", NULL, MTX_DEF); 1379219888Sed cv_init(&vd->vd_winswitch, "vtwswt"); 1380219888Sed 1381256145Sray /* Init 25 Hz timer. */ 1382219888Sed callout_init_mtx(&vd->vd_timer, &vd->vd_lock, 0); 1383219888Sed 1384219888Sed for (i = 0; i < VT_MAXWINDOWS; i++) { 1385219888Sed vw = vd->vd_windows[i]; 1386219888Sed if (vw == NULL) { 1387219888Sed /* New window. */ 1388219888Sed vw = vt_allocate_window(vd, i); 1389256145Sray } 1390256145Sray if (i == VT_CONSWINDOW) { 1391219888Sed /* Console window. */ 1392219888Sed EVENTHANDLER_REGISTER(shutdown_pre_sync, 1393219888Sed vt_window_switch, vw, SHUTDOWN_PRI_DEFAULT); 1394219888Sed } 1395219888Sed terminal_maketty(vw->vw_terminal, "v%r", VT_UNIT(vw)); 1396219888Sed } 1397256145Sray if (vd->vd_curwindow == NULL) 1398256145Sray vd->vd_curwindow = vd->vd_windows[VT_CONSWINDOW]; 1399219888Sed 1400219888Sed /* Attach keyboard. */ 1401219888Sed vt_allocate_keyboard(vd); 1402256145Sray DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard); 1403256145Sray 1404256145Sray /* Start timer when everything ready. */ 1405256145Sray callout_reset(&vd->vd_timer, hz / VT_TIMERFREQ, vt_timer, vd); 1406219888Sed} 1407219888Sed 1408256145Sraystatic void 1409256145Srayvt_resize(struct vt_device *vd) 1410256145Sray{ 1411256145Sray struct vt_window *vw; 1412256145Sray int i; 1413256145Sray 1414256145Sray for (i = 0; i < VT_MAXWINDOWS; i++) { 1415256145Sray vw = vd->vd_windows[i]; 1416256145Sray /* Resize terminal windows */ 1417256145Sray vt_change_font(vw, vw->vw_font); 1418256145Sray } 1419256145Sray} 1420256145Sray 1421219888Sedvoid 1422219888Sedvt_allocate(struct vt_driver *drv, void *softc) 1423219888Sed{ 1424219888Sed struct vt_device *vd; 1425256903Sray struct winsize wsz; 1426219888Sed 1427256145Sray if (main_vd == NULL) { 1428256145Sray main_vd = malloc(sizeof *vd, M_VT, M_WAITOK|M_ZERO); 1429256903Sray printf("%s: VT initialize with new VT driver.\n", __func__); 1430256145Sray } else { 1431256145Sray /* 1432256145Sray * Check if have rights to replace current driver. For example: 1433256145Sray * it is bad idea to replace KMS driver with generic VGA one. 1434256145Sray */ 1435256903Sray if (drv->vd_priority <= main_vd->vd_driver->vd_priority) { 1436256903Sray printf("%s: Driver priority %d too low. Current %d\n ", 1437256903Sray __func__, drv->vd_priority, 1438256903Sray main_vd->vd_driver->vd_priority); 1439256145Sray return; 1440256903Sray } 1441256903Sray printf("%s: Replace existing VT driver.\n", __func__); 1442256145Sray } 1443256145Sray vd = main_vd; 1444256145Sray 1445256527Sray /* Stop vt_flush periodic task. */ 1446256145Sray if (vd->vd_curwindow != NULL) 1447256145Sray callout_drain(&vd->vd_timer); 1448256145Sray 1449219888Sed vd->vd_driver = drv; 1450219888Sed vd->vd_softc = softc; 1451219888Sed vd->vd_driver->vd_init(vd); 1452256145Sray 1453219888Sed vt_upgrade(vd); 1454256145Sray 1455256145Sray /* Refill settings with new sizes. */ 1456256145Sray vt_resize(vd); 1457256145Sray 1458256145Sray if (vd->vd_flags & VDF_SPLASH) 1459256145Sray vtterm_splash(vd); 1460256145Sray 1461256145Sray if (vd->vd_curwindow != NULL) 1462256145Sray callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ); 1463256145Sray 1464256145Sray termcn_cnregister(vd->vd_windows[VT_CONSWINDOW]->vw_terminal); 1465256903Sray 1466256903Sray /* Update console window sizes to actual. */ 1467256903Sray vt_winsize(vd, vd->vd_windows[VT_CONSWINDOW]->vw_font, &wsz); 1468256903Sray terminal_set_winsize(vd->vd_windows[VT_CONSWINDOW]->vw_terminal, &wsz); 1469219888Sed} 1470257815Sray 1471257815Srayvoid 1472257815Srayvt_suspend() 1473257815Sray{ 1474257815Sray 1475257815Sray /* Save current window. */ 1476257815Sray main_vd->vd_savedwindow = main_vd->vd_curwindow; 1477257815Sray /* Ask holding process to free window and switch to console window */ 1478257815Sray vt_proc_window_switch(main_vd->vd_windows[VT_CONSWINDOW]); 1479257815Sray} 1480257815Sray 1481257815Srayvoid 1482257815Srayvt_resume() 1483257815Sray{ 1484257815Sray 1485257815Sray /* Switch back to saved window */ 1486257815Sray if (main_vd->vd_savedwindow != NULL) 1487257815Sray vt_proc_window_switch(main_vd->vd_savedwindow); 1488257815Sray main_vd->vd_savedwindow = NULL; 1489257815Sray} 1490