vt_core.c revision 256145
1219888Sed/*- 2219888Sed * Copyright (c) 2009 The FreeBSD Foundation 3219888Sed * All rights reserved. 4219888Sed * 5219888Sed * This software was developed by Ed Schouten under sponsorship from the 6219888Sed * FreeBSD Foundation. 7219888Sed * 8219888Sed * Redistribution and use in source and binary forms, with or without 9219888Sed * modification, are permitted provided that the following conditions 10219888Sed * are met: 11219888Sed * 1. Redistributions of source code must retain the above copyright 12219888Sed * notice, this list of conditions and the following disclaimer. 13219888Sed * 2. Redistributions in binary form must reproduce the above copyright 14219888Sed * notice, this list of conditions and the following disclaimer in the 15219888Sed * documentation and/or other materials provided with the distribution. 16219888Sed * 17219888Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18219888Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19219888Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20219888Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21219888Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22219888Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23219888Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24219888Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25219888Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26219888Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27219888Sed * SUCH DAMAGE. 28219888Sed */ 29219888Sed 30219888Sed#include <sys/cdefs.h> 31219888Sed__FBSDID("$FreeBSD: user/ed/newcons/sys/dev/vt/vt_core.c 256145 2013-10-08 12:40:04Z ray $"); 32219888Sed 33219888Sed#include <sys/param.h> 34219888Sed#include <sys/consio.h> 35219888Sed#include <sys/eventhandler.h> 36219888Sed#include <sys/fbio.h> 37219888Sed#include <sys/kbio.h> 38219888Sed#include <sys/kdb.h> 39219888Sed#include <sys/kernel.h> 40219888Sed#include <sys/lock.h> 41219888Sed#include <sys/malloc.h> 42219888Sed#include <sys/mutex.h> 43256145Sray#include <sys/proc.h> 44219888Sed#include <sys/reboot.h> 45219888Sed#include <sys/systm.h> 46219888Sed#include <sys/terminal.h> 47219888Sed 48219888Sed#include <dev/kbd/kbdreg.h> 49219888Sed#include <dev/vt/vt.h> 50219888Sed 51219888Sedstatic tc_bell_t vtterm_bell; 52219888Sedstatic tc_cursor_t vtterm_cursor; 53219888Sedstatic tc_putchar_t vtterm_putchar; 54219888Sedstatic tc_fill_t vtterm_fill; 55219888Sedstatic tc_copy_t vtterm_copy; 56219888Sedstatic tc_param_t vtterm_param; 57219888Sedstatic tc_done_t vtterm_done; 58219888Sed 59219888Sedstatic tc_cnprobe_t vtterm_cnprobe; 60219888Sedstatic tc_cngetc_t vtterm_cngetc; 61219888Sed 62219888Sedstatic tc_opened_t vtterm_opened; 63219888Sedstatic tc_ioctl_t vtterm_ioctl; 64219888Sed 65219888Sedconst struct terminal_class vt_termclass = { 66219888Sed .tc_bell = vtterm_bell, 67219888Sed .tc_cursor = vtterm_cursor, 68219888Sed .tc_putchar = vtterm_putchar, 69219888Sed .tc_fill = vtterm_fill, 70219888Sed .tc_copy = vtterm_copy, 71219888Sed .tc_param = vtterm_param, 72219888Sed .tc_done = vtterm_done, 73219888Sed 74219888Sed .tc_cnprobe = vtterm_cnprobe, 75219888Sed .tc_cngetc = vtterm_cngetc, 76219888Sed 77219888Sed .tc_opened = vtterm_opened, 78219888Sed .tc_ioctl = vtterm_ioctl, 79219888Sed}; 80219888Sed 81219888Sed/* 82219888Sed * Use a constant timer of 25 Hz to redraw the screen. 83219888Sed * 84219888Sed * XXX: In theory we should only fire up the timer when there is really 85219888Sed * activity. Unfortunately we cannot always start timers. We really 86219888Sed * don't want to process kernel messages synchronously, because it 87219888Sed * really slows down the system. 88219888Sed */ 89219888Sed#define VT_TIMERFREQ 25 90219888Sed 91219888Sed/* Bell pitch/duration. */ 92219888Sed#define VT_BELLDURATION ((5 * hz + 99) / 100) 93219888Sed#define VT_BELLPITCH 800 94219888Sed 95219888Sed#define VT_LOCK(vd) mtx_lock(&(vd)->vd_lock) 96219888Sed#define VT_UNLOCK(vd) mtx_unlock(&(vd)->vd_lock) 97219888Sed 98219888Sed#define VT_UNIT(vw) ((vw)->vw_device->vd_unit * VT_MAXWINDOWS + \ 99219888Sed (vw)->vw_number) 100219888Sed 101256145Sray/* XXX while syscons is here. */ 102256145Srayint sc_txtmouse_no_retrace_wait; 103256145Sray 104256145Sraystatic SYSCTL_NODE(_kern, OID_AUTO, vt, CTLFLAG_RD, 0, "Newcons parameters"); 105256145SrayVT_SYSCTL_INT(debug, 0, "Newcons debug level"); 106256145SrayVT_SYSCTL_INT(deadtimer, 15, "Time to wait busy process in VT_PROCESS mode"); 107256145Sray 108219888Sedstatic unsigned int vt_unit = 0; 109219888Sedstatic MALLOC_DEFINE(M_VT, "vt", "vt device"); 110256145Sraystruct vt_device *main_vd = NULL; 111219888Sed 112219888Sed/* Boot logo. */ 113219888Sedextern unsigned int vt_logo_width; 114219888Sedextern unsigned int vt_logo_height; 115219888Sedextern unsigned int vt_logo_depth; 116219888Sedextern unsigned char vt_logo_image[]; 117219888Sed 118219888Sed/* Font. */ 119219888Sedextern struct vt_font vt_font_default; 120219888Sed 121256145Sraystatic int signal_vt_rel(struct vt_window *); 122256145Sraystatic int signal_vt_acq(struct vt_window *); 123256145Sraystatic int finish_vt_rel(struct vt_window *, int, int *); 124256145Sraystatic int finish_vt_acq(struct vt_window *); 125256145Sraystatic int vt_window_switch(struct vt_window *); 126256145Sraystatic int vt_late_window_switch(struct vt_window *); 127256145Sraystatic int vt_proc_alive(struct vt_window *); 128256145Sraystatic void vt_resize(struct vt_device *); 129256145Sray 130219888Sedstatic void 131256145Srayvt_switch_timer(void *arg) 132256145Sray{ 133256145Sray 134256145Sray vt_late_window_switch((struct vt_window *)arg); 135256145Sray} 136256145Sray 137256145Sraystatic int 138256145Srayvt_window_preswitch(struct vt_window *vw, struct vt_window *curvw) 139256145Sray{ 140256145Sray 141256145Sray DPRINTF(40, "%s\n", __func__); 142256145Sray curvw->vw_switch_to = vw; 143256145Sray /* Set timer to allow switch in case when process hang. */ 144256145Sray callout_reset(&vw->vw_proc_dead_timer, hz * vt_deadtimer, 145256145Sray vt_switch_timer, (void *)vw); 146256145Sray /* Notify process about vt switch attempt. */ 147256145Sray DPRINTF(30, "%s: Notify process.\n", __func__); 148256145Sray signal_vt_rel(curvw); 149256145Sray 150256145Sray return (0); 151256145Sray} 152256145Sray 153256145Sraystatic int 154256145Srayvt_window_postswitch(struct vt_window *vw) 155256145Sray{ 156256145Sray 157256145Sray signal_vt_acq(vw); 158256145Sray return (0); 159256145Sray} 160256145Sray 161256145Sray/* vt_late_window_switch will done VT switching for regular case. */ 162256145Sraystatic int 163256145Srayvt_late_window_switch(struct vt_window *vw) 164256145Sray{ 165256145Sray int ret; 166256145Sray 167256145Sray callout_stop(&vw->vw_proc_dead_timer); 168256145Sray 169256145Sray ret = vt_window_switch(vw); 170256145Sray if (ret) 171256145Sray return (ret); 172256145Sray 173256145Sray /* Notify owner process about terminal availability. */ 174256145Sray if (vw->vw_smode.mode == VT_PROCESS) { 175256145Sray ret = vt_window_postswitch(vw); 176256145Sray } 177256145Sray return (ret); 178256145Sray} 179256145Sray 180256145Sray/* Switch window. */ 181256145Sraystatic int 182256145Srayvt_proc_window_switch(struct vt_window *vw) 183256145Sray{ 184256145Sray struct vt_window *curvw; 185256145Sray struct vt_device *vd; 186256145Sray int ret; 187256145Sray 188256145Sray if (vw->vw_flags & VWF_VTYLOCK) 189256145Sray return (EBUSY); 190256145Sray 191256145Sray vd = vw->vw_device; 192256145Sray curvw = vd->vd_curwindow; 193256145Sray 194256145Sray /* Ask current process permitions to switch away. */ 195256145Sray if (curvw->vw_smode.mode == VT_PROCESS) { 196256145Sray DPRINTF(30, "%s: VT_PROCESS ", __func__); 197256145Sray if (vt_proc_alive(curvw) == FALSE) { 198256145Sray DPRINTF(30, "Dead. Cleaning."); 199256145Sray /* Dead */ 200256145Sray } else { 201256145Sray DPRINTF(30, "%s: Signaling process.\n", __func__); 202256145Sray /* Alive, try to ask him. */ 203256145Sray ret = vt_window_preswitch(vw, curvw); 204256145Sray /* Wait for process answer or timeout. */ 205256145Sray return (ret); 206256145Sray } 207256145Sray DPRINTF(30, "\n"); 208256145Sray } 209256145Sray 210256145Sray ret = vt_late_window_switch(vw); 211256145Sray return (ret); 212256145Sray} 213256145Sray 214256145Sray/* Switch window ignoring process locking. */ 215256145Sraystatic int 216219888Sedvt_window_switch(struct vt_window *vw) 217219888Sed{ 218219888Sed struct vt_device *vd = vw->vw_device; 219256145Sray struct vt_window *curvw = vd->vd_curwindow; 220219888Sed keyboard_t *kbd; 221219888Sed 222219888Sed VT_LOCK(vd); 223256145Sray if (curvw == vw) { 224256145Sray /* Nothing to do. */ 225219888Sed VT_UNLOCK(vd); 226256145Sray return (0); 227219888Sed } 228256145Sray if (!(vw->vw_flags & (VWF_OPENED|VWF_CONSOLE))) { 229256145Sray VT_UNLOCK(vd); 230256145Sray return (EINVAL); 231256145Sray } 232256145Sray 233219888Sed vd->vd_curwindow = vw; 234219888Sed vd->vd_flags |= VDF_INVALID; 235219888Sed cv_broadcast(&vd->vd_winswitch); 236219888Sed VT_UNLOCK(vd); 237219888Sed 238256145Sray if (vd->vd_driver->vd_postswitch) 239256145Sray vd->vd_driver->vd_postswitch(vd); 240256145Sray 241219888Sed /* Restore per-window keyboard mode. */ 242219888Sed mtx_lock(&Giant); 243219888Sed kbd = kbd_get_keyboard(vd->vd_keyboard); 244256145Sray if (kbd != NULL) { 245219888Sed kbdd_ioctl(kbd, KDSKBMODE, (void *)&vw->vw_kbdmode); 246256145Sray } 247219888Sed mtx_unlock(&Giant); 248256145Sray DPRINTF(10, "%s(ttyv%d) done\n", __func__, vw->vw_number); 249256145Sray 250256145Sray return (0); 251219888Sed} 252219888Sed 253219888Sedstatic inline void 254219888Sedvt_termsize(struct vt_device *vd, struct vt_font *vf, term_pos_t *size) 255219888Sed{ 256219888Sed 257219888Sed size->tp_row = vd->vd_height; 258219888Sed size->tp_col = vd->vd_width; 259219888Sed if (vf != NULL) { 260219888Sed size->tp_row /= vf->vf_height; 261219888Sed size->tp_col /= vf->vf_width; 262219888Sed } 263219888Sed} 264219888Sed 265219888Sedstatic inline void 266219888Sedvt_winsize(struct vt_device *vd, struct vt_font *vf, struct winsize *size) 267219888Sed{ 268219888Sed 269219888Sed size->ws_row = size->ws_ypixel = vd->vd_height; 270219888Sed size->ws_col = size->ws_xpixel = vd->vd_width; 271219888Sed if (vf != NULL) { 272219888Sed size->ws_row /= vf->vf_height; 273219888Sed size->ws_col /= vf->vf_width; 274219888Sed } 275219888Sed} 276219888Sed 277219888Sedstatic int 278219888Sedvt_kbdevent(keyboard_t *kbd, int event, void *arg) 279219888Sed{ 280219888Sed struct vt_device *vd = arg; 281219888Sed struct vt_window *vw = vd->vd_curwindow; 282256145Sray int c; 283219888Sed 284219888Sed switch (event) { 285219888Sed case KBDIO_KEYINPUT: 286219888Sed break; 287219888Sed case KBDIO_UNLOADING: 288219888Sed mtx_lock(&Giant); 289219888Sed vd->vd_keyboard = -1; 290219888Sed kbd_release(kbd, (void *)&vd->vd_keyboard); 291219888Sed mtx_unlock(&Giant); 292219888Sed return (0); 293219888Sed default: 294219888Sed return (EINVAL); 295219888Sed } 296219888Sed 297219888Sed c = kbdd_read_char(kbd, 0); 298219888Sed if (c & RELKEY) 299219888Sed return (0); 300219888Sed 301219888Sed if (c & SPCLKEY) { 302219888Sed c &= ~SPCLKEY; 303219888Sed 304219888Sed if (c >= F_SCR && c <= MIN(L_SCR, F_SCR + VT_MAXWINDOWS - 1)) { 305219888Sed vw = vd->vd_windows[c - F_SCR]; 306219888Sed if (vw != NULL) 307256145Sray vt_proc_window_switch(vw); 308219888Sed return (0); 309219888Sed } 310219888Sed 311219888Sed switch (c) { 312219888Sed case DBG: 313219888Sed kdb_enter(KDB_WHY_BREAK, "manual escape to debugger"); 314219888Sed break; 315219888Sed case RBT: 316219888Sed /* XXX: Make this configurable! */ 317219888Sed shutdown_nice(0); 318219888Sed break; 319219888Sed case HALT: 320219888Sed shutdown_nice(RB_HALT); 321219888Sed break; 322219888Sed case PDWN: 323219888Sed shutdown_nice(RB_HALT|RB_POWEROFF); 324219888Sed break; 325219888Sed case SLK: { 326243802Snwhitehorn int state = 0; 327219888Sed 328219888Sed kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state); 329219888Sed VT_LOCK(vd); 330219888Sed if (state & SLKED) { 331219888Sed /* Turn scrolling on. */ 332219888Sed vw->vw_flags |= VWF_SCROLL; 333256145Sray VTBUF_SLCK_ENABLE(&vw->vw_buf); 334219888Sed } else { 335219888Sed /* Turn scrolling off. */ 336219888Sed vw->vw_flags &= ~VWF_SCROLL; 337256145Sray VTBUF_SLCK_DISABLE(&vw->vw_buf); 338256145Sray vthistory_seek(&vw->vw_buf, 0, VHS_END); 339256145Sray vd->vd_flags |= VDF_INVALID; 340219888Sed } 341219888Sed VT_UNLOCK(vd); 342219888Sed break; 343219888Sed } 344219888Sed case FKEY | F(1): case FKEY | F(2): case FKEY | F(3): 345219888Sed case FKEY | F(4): case FKEY | F(5): case FKEY | F(6): 346219888Sed case FKEY | F(7): case FKEY | F(8): case FKEY | F(9): 347219888Sed case FKEY | F(10): case FKEY | F(11): case FKEY | F(12): 348219888Sed /* F1 through F12 keys. */ 349219888Sed terminal_input_special(vw->vw_terminal, 350219888Sed TKEY_F1 + c - (FKEY | F(1))); 351219888Sed break; 352219888Sed case FKEY | F(49): /* Home key. */ 353219888Sed VT_LOCK(vd); 354219888Sed if (vw->vw_flags & VWF_SCROLL) { 355256145Sray if (vthistory_seek(&vw->vw_buf, 0, VHS_END)) 356256145Sray vd->vd_flags |= VDF_INVALID; 357219888Sed VT_UNLOCK(vd); 358219888Sed break; 359219888Sed } 360219888Sed VT_UNLOCK(vd); 361219888Sed terminal_input_special(vw->vw_terminal, TKEY_HOME); 362219888Sed break; 363219888Sed case FKEY | F(50): /* Arrow up. */ 364219888Sed VT_LOCK(vd); 365219888Sed if (vw->vw_flags & VWF_SCROLL) { 366256145Sray if (vthistory_seek(&vw->vw_buf, -1, VHS_CUR)) 367256145Sray vd->vd_flags |= VDF_INVALID; 368219888Sed VT_UNLOCK(vd); 369219888Sed break; 370219888Sed } 371219888Sed VT_UNLOCK(vd); 372219888Sed terminal_input_special(vw->vw_terminal, TKEY_UP); 373219888Sed break; 374219888Sed case FKEY | F(51): /* Page up. */ 375219888Sed VT_LOCK(vd); 376219888Sed if (vw->vw_flags & VWF_SCROLL) { 377219888Sed term_pos_t size; 378219888Sed 379219888Sed vt_termsize(vd, vw->vw_font, &size); 380256145Sray if (vthistory_seek(&vw->vw_buf, -size.tp_row, 381256145Sray VHS_CUR)) 382256145Sray vd->vd_flags |= VDF_INVALID; 383219888Sed VT_UNLOCK(vd); 384219888Sed break; 385219888Sed } 386219888Sed VT_UNLOCK(vd); 387219888Sed terminal_input_special(vw->vw_terminal, TKEY_PAGE_UP); 388219888Sed break; 389219888Sed case FKEY | F(53): /* Arrow left. */ 390219888Sed terminal_input_special(vw->vw_terminal, TKEY_LEFT); 391219888Sed break; 392219888Sed case FKEY | F(55): /* Arrow right. */ 393219888Sed terminal_input_special(vw->vw_terminal, TKEY_RIGHT); 394219888Sed break; 395219888Sed case FKEY | F(57): /* End key. */ 396219888Sed VT_LOCK(vd); 397219888Sed if (vw->vw_flags & VWF_SCROLL) { 398256145Sray if (vthistory_seek(&vw->vw_buf, 0, VHS_SET)) 399256145Sray vd->vd_flags |= VDF_INVALID; 400219888Sed VT_UNLOCK(vd); 401219888Sed break; 402219888Sed } 403219888Sed VT_UNLOCK(vd); 404219888Sed terminal_input_special(vw->vw_terminal, TKEY_END); 405219888Sed break; 406219888Sed case FKEY | F(58): /* Arrow down. */ 407219888Sed VT_LOCK(vd); 408219888Sed if (vw->vw_flags & VWF_SCROLL) { 409256145Sray if (vthistory_seek(&vw->vw_buf, 1, VHS_CUR)) 410256145Sray vd->vd_flags |= VDF_INVALID; 411219888Sed VT_UNLOCK(vd); 412219888Sed break; 413219888Sed } 414219888Sed VT_UNLOCK(vd); 415219888Sed terminal_input_special(vw->vw_terminal, TKEY_DOWN); 416219888Sed break; 417219888Sed case FKEY | F(59): /* Page down. */ 418219888Sed VT_LOCK(vd); 419219888Sed if (vw->vw_flags & VWF_SCROLL) { 420219888Sed term_pos_t size; 421219888Sed 422219888Sed vt_termsize(vd, vw->vw_font, &size); 423256145Sray if (vthistory_seek(&vw->vw_buf, size.tp_row, 424256145Sray VHS_CUR)) 425256145Sray vd->vd_flags |= VDF_INVALID; 426219888Sed VT_UNLOCK(vd); 427219888Sed break; 428219888Sed } 429219888Sed VT_UNLOCK(vd); 430219888Sed terminal_input_special(vw->vw_terminal, TKEY_PAGE_DOWN); 431219888Sed break; 432219888Sed case FKEY | F(60): /* Insert key. */ 433219888Sed terminal_input_special(vw->vw_terminal, TKEY_INSERT); 434219888Sed break; 435219888Sed case FKEY | F(61): /* Delete key. */ 436219888Sed terminal_input_special(vw->vw_terminal, TKEY_DELETE); 437219888Sed break; 438219888Sed } 439219888Sed } else if (KEYFLAGS(c) == 0) { 440219888Sed /* Don't do UTF-8 conversion when doing raw mode. */ 441219888Sed if (vw->vw_kbdmode == K_XLATE) 442256145Sray terminal_input_char(vw->vw_terminal, KEYCHAR(c)); 443219888Sed else 444219888Sed terminal_input_raw(vw->vw_terminal, c); 445219888Sed } 446219888Sed return (0); 447219888Sed} 448219888Sed 449219888Sedstatic int 450219888Sedvt_allocate_keyboard(struct vt_device *vd) 451219888Sed{ 452219888Sed int idx0, idx; 453219888Sed keyboard_t *k0, *k; 454219888Sed keyboard_info_t ki; 455219888Sed 456219888Sed idx0 = kbd_allocate("kbdmux", -1, (void *)&vd->vd_keyboard, 457219888Sed vt_kbdevent, vd); 458256145Sray /* XXX: kb_token lost */ 459256145Sray vd->vd_keyboard = idx0; 460219888Sed if (idx0 != -1) { 461256145Sray DPRINTF(20, "%s: kbdmux allocated, idx = %d\n", __func__, idx0); 462219888Sed k0 = kbd_get_keyboard(idx0); 463219888Sed 464219888Sed for (idx = kbd_find_keyboard2("*", -1, 0); 465219888Sed idx != -1; 466219888Sed idx = kbd_find_keyboard2("*", -1, idx + 1)) { 467219888Sed k = kbd_get_keyboard(idx); 468219888Sed 469219888Sed if (idx == idx0 || KBD_IS_BUSY(k)) 470219888Sed continue; 471219888Sed 472219888Sed bzero(&ki, sizeof(ki)); 473219888Sed strcpy(ki.kb_name, k->kb_name); 474219888Sed ki.kb_unit = k->kb_unit; 475219888Sed 476219888Sed kbdd_ioctl(k0, KBADDKBD, (caddr_t) &ki); 477219888Sed } 478256145Sray } else { 479256145Sray DPRINTF(20, "%s: no kbdmux allocated\n", __func__); 480219888Sed idx0 = kbd_allocate("*", -1, (void *)&vd->vd_keyboard, 481219888Sed vt_kbdevent, vd); 482256145Sray } 483256145Sray DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard); 484219888Sed 485219888Sed return (idx0); 486219888Sed} 487219888Sed 488219888Sedstatic void 489219888Sedvtterm_bell(struct terminal *tm) 490219888Sed{ 491219888Sed 492219888Sed sysbeep(1193182 / VT_BELLPITCH, VT_BELLDURATION); 493219888Sed} 494219888Sed 495219888Sedstatic void 496219888Sedvtterm_cursor(struct terminal *tm, const term_pos_t *p) 497219888Sed{ 498219888Sed struct vt_window *vw = tm->tm_softc; 499219888Sed 500219888Sed vtbuf_cursor_position(&vw->vw_buf, p); 501219888Sed} 502219888Sed 503219888Sedstatic void 504219888Sedvtterm_putchar(struct terminal *tm, const term_pos_t *p, term_char_t c) 505219888Sed{ 506219888Sed struct vt_window *vw = tm->tm_softc; 507219888Sed 508219888Sed vtbuf_putchar(&vw->vw_buf, p, c); 509219888Sed} 510219888Sed 511219888Sedstatic void 512219888Sedvtterm_fill(struct terminal *tm, const term_rect_t *r, term_char_t c) 513219888Sed{ 514219888Sed struct vt_window *vw = tm->tm_softc; 515219888Sed 516256145Sray vtbuf_fill_locked(&vw->vw_buf, r, c); 517219888Sed} 518219888Sed 519219888Sedstatic void 520219888Sedvtterm_copy(struct terminal *tm, const term_rect_t *r, 521219888Sed const term_pos_t *p) 522219888Sed{ 523219888Sed struct vt_window *vw = tm->tm_softc; 524219888Sed 525219888Sed vtbuf_copy(&vw->vw_buf, r, p); 526219888Sed} 527219888Sed 528219888Sedstatic void 529219888Sedvtterm_param(struct terminal *tm, int cmd, unsigned int arg) 530219888Sed{ 531219888Sed struct vt_window *vw = tm->tm_softc; 532219888Sed 533219888Sed switch (cmd) { 534219888Sed case TP_SHOWCURSOR: 535219888Sed vtbuf_cursor_visibility(&vw->vw_buf, arg); 536219888Sed break; 537219888Sed } 538219888Sed} 539219888Sed 540219888Sedstatic inline void 541219888Sedvt_determine_colors(term_char_t c, int cursor, 542219888Sed term_color_t *fg, term_color_t *bg) 543219888Sed{ 544219888Sed 545219888Sed *fg = TCHAR_FGCOLOR(c); 546219888Sed if (TCHAR_FORMAT(c) & TF_BOLD) 547219888Sed *fg = TCOLOR_LIGHT(*fg); 548219888Sed *bg = TCHAR_BGCOLOR(c); 549219888Sed 550219888Sed if (TCHAR_FORMAT(c) & TF_REVERSE) { 551219888Sed term_color_t tmp; 552219888Sed 553219888Sed tmp = *fg; 554219888Sed *fg = *bg; 555219888Sed *bg = tmp; 556219888Sed } 557219888Sed 558219888Sed if (cursor) { 559219888Sed *fg = *bg; 560219888Sed *bg = TC_WHITE; 561219888Sed } 562219888Sed} 563219888Sed 564219888Sedstatic void 565219888Sedvt_bitblt_char(struct vt_device *vd, struct vt_font *vf, term_char_t c, 566219888Sed int iscursor, unsigned int row, unsigned int col) 567219888Sed{ 568219888Sed term_color_t fg, bg; 569219888Sed 570219888Sed vt_determine_colors(c, iscursor, &fg, &bg); 571219888Sed 572219888Sed if (vf != NULL) { 573219888Sed const uint8_t *src; 574219888Sed vt_axis_t top, left; 575219888Sed 576219888Sed src = vtfont_lookup(vf, c); 577219888Sed 578219888Sed /* 579219888Sed * Align the terminal to the centre of the screen. 580219888Sed * Fonts may not always be able to fill the entire 581219888Sed * screen. 582219888Sed */ 583219888Sed top = row * vf->vf_height + 584219888Sed (vd->vd_height % vf->vf_height) / 2; 585219888Sed left = col * vf->vf_width + 586219888Sed (vd->vd_width % vf->vf_width) / 2; 587219888Sed 588219888Sed vd->vd_driver->vd_bitblt(vd, src, top, left, 589219888Sed vf->vf_width, vf->vf_height, fg, bg); 590219888Sed } else { 591219888Sed vd->vd_driver->vd_putchar(vd, TCHAR_CHARACTER(c), 592219888Sed row, col, fg, bg); 593219888Sed } 594219888Sed} 595219888Sed 596219888Sedstatic void 597219888Sedvt_flush(struct vt_device *vd) 598219888Sed{ 599219888Sed struct vt_window *vw = vd->vd_curwindow; 600219888Sed struct vt_font *vf = vw->vw_font; 601219888Sed term_pos_t size; 602219888Sed term_rect_t tarea; 603219888Sed struct vt_bufmask tmask; 604256145Sray unsigned int row, col; 605256145Sray term_char_t *r; 606219888Sed 607219888Sed if (vd->vd_flags & VDF_SPLASH || vw->vw_flags & VWF_BUSY) 608219888Sed return; 609219888Sed 610219888Sed vtbuf_undirty(&vw->vw_buf, &tarea, &tmask); 611219888Sed vt_termsize(vd, vf, &size); 612219888Sed 613219888Sed /* Force a full redraw when the screen contents are invalid. */ 614256145Sray if (vd->vd_flags & VDF_INVALID) { 615219888Sed tarea.tr_begin.tp_row = tarea.tr_begin.tp_col = 0; 616219888Sed tarea.tr_end = size; 617219888Sed tmask.vbm_row = tmask.vbm_col = VBM_DIRTY; 618219888Sed 619219888Sed /* 620219888Sed * Blank to prevent borders with artifacts. This is 621219888Sed * only required when the font doesn't exactly fill the 622219888Sed * screen. 623219888Sed */ 624219888Sed if (vd->vd_flags & VDF_INVALID && vf != NULL && 625219888Sed (vd->vd_width % vf->vf_width != 0 || 626219888Sed vd->vd_height % vf->vf_height != 0)) 627219888Sed vd->vd_driver->vd_blank(vd, TC_BLACK); 628219888Sed 629219888Sed vd->vd_flags &= ~VDF_INVALID; 630219888Sed } 631219888Sed 632219888Sed 633219888Sed for (row = tarea.tr_begin.tp_row; row < tarea.tr_end.tp_row; row++) { 634219888Sed if (!VTBUF_DIRTYROW(&tmask, row)) 635219888Sed continue; 636256145Sray r = VTBUF_GET_ROW(&vw->vw_buf, row); 637219888Sed for (col = tarea.tr_begin.tp_col; 638219888Sed col < tarea.tr_end.tp_col; col++) { 639219888Sed if (!VTBUF_DIRTYCOL(&tmask, col)) 640219888Sed continue; 641219888Sed 642256145Sray vt_bitblt_char(vd, vf, r[col], 643219888Sed VTBUF_ISCURSOR(&vw->vw_buf, row, col), 644256145Sray row, col); 645219888Sed } 646219888Sed } 647219888Sed} 648219888Sed 649219888Sedstatic void 650219888Sedvt_timer(void *arg) 651219888Sed{ 652219888Sed struct vt_device *vd = arg; 653256145Sray unsigned int i; 654219888Sed 655219888Sed vt_flush(vd); 656256145Sray 657256145Sray for (i = 0; i < VT_MAXWINDOWS; i++) 658256145Sray vt_proc_alive(vd->vd_windows[i]); 659256145Sray 660219888Sed callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ); 661219888Sed} 662219888Sed 663219888Sedstatic void 664219888Sedvtterm_done(struct terminal *tm) 665219888Sed{ 666219888Sed struct vt_window *vw = tm->tm_softc; 667219888Sed struct vt_device *vd = vw->vw_device; 668219888Sed 669219888Sed if (kdb_active || panicstr != NULL) { 670219888Sed /* Switch to the debugger. */ 671219888Sed if (vd->vd_curwindow != vw) { 672219888Sed vd->vd_curwindow = vw; 673219888Sed vd->vd_flags |= VDF_INVALID; 674219888Sed } 675219888Sed vd->vd_flags &= ~VDF_SPLASH; 676219888Sed vt_flush(vd); 677219888Sed } else if (!(vd->vd_flags & VDF_ASYNC)) { 678219888Sed vt_flush(vd); 679219888Sed } 680219888Sed} 681219888Sed 682219888Sedstatic void 683256145Srayvtterm_splash(struct vt_device *vd) 684256145Sray{ 685256145Sray vt_axis_t top, left; 686256145Sray 687256145Sray /* Display a nice boot splash. */ 688256145Sray if (!(vd->vd_flags & VDF_TEXTMODE)) { 689256145Sray 690256145Sray top = (vd->vd_height - vt_logo_height) / 2; 691256145Sray left = (vd->vd_width - vt_logo_width) / 2; 692256145Sray switch (vt_logo_depth) { 693256145Sray case 1: 694256145Sray /* XXX: Unhardcode colors! */ 695256145Sray vd->vd_driver->vd_bitblt(vd, vt_logo_image, top, left, 696256145Sray vt_logo_width, vt_logo_height, 0xf, 0x0); 697256145Sray } 698256145Sray vd->vd_flags |= VDF_SPLASH; 699256145Sray } 700256145Sray} 701256145Sray 702256145Sraystatic void 703219888Sedvtterm_cnprobe(struct terminal *tm, struct consdev *cp) 704219888Sed{ 705219888Sed struct vt_window *vw = tm->tm_softc; 706219888Sed struct vt_device *vd = vw->vw_device; 707219888Sed struct winsize wsz; 708219888Sed 709256145Sray if (vd->vd_flags & VDF_INITIALIZED) 710256145Sray /* Initialization already done. */ 711256145Sray return; 712256145Sray 713219888Sed cp->cn_pri = vd->vd_driver->vd_init(vd); 714219888Sed if (cp->cn_pri == CN_DEAD) { 715219888Sed vd->vd_flags |= VDF_DEAD; 716219888Sed return; 717219888Sed } 718219888Sed 719230469Snwhitehorn /* Initialize any early-boot keyboard drivers */ 720230469Snwhitehorn kbd_configure(KB_CONF_PROBE_ONLY); 721230469Snwhitehorn 722219888Sed vd->vd_unit = atomic_fetchadd_int(&vt_unit, 1); 723256145Sray vd->vd_windows[VT_CONSWINDOW] = vw; 724219888Sed sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw)); 725219888Sed 726219888Sed if (!(vd->vd_flags & VDF_TEXTMODE)) 727219888Sed vw->vw_font = vtfont_ref(&vt_font_default); 728219888Sed 729219888Sed vtbuf_init_early(&vw->vw_buf); 730219888Sed vt_winsize(vd, vw->vw_font, &wsz); 731219888Sed terminal_set_winsize(tm, &wsz); 732219888Sed 733256145Sray vtterm_splash(vd); 734219888Sed 735256145Sray vd->vd_flags |= VDF_INITIALIZED; 736256145Sray main_vd = vd; 737219888Sed} 738219888Sed 739219888Sedstatic int 740219888Sedvtterm_cngetc(struct terminal *tm) 741219888Sed{ 742219888Sed struct vt_window *vw = tm->tm_softc; 743219888Sed struct vt_device *vd = vw->vw_device; 744219888Sed keyboard_t *kbd; 745219888Sed u_int c; 746219888Sed 747219888Sed /* Make sure the splash screen is not there. */ 748219888Sed if (vd->vd_flags & VDF_SPLASH) { 749256145Sray /* Remove splash */ 750219888Sed vd->vd_flags &= ~VDF_SPLASH; 751256145Sray /* Mark screen as invalid to force update */ 752256145Sray vd->vd_flags |= VDF_INVALID; 753219888Sed vt_flush(vd); 754219888Sed } 755219888Sed 756219888Sed /* Stripped down keyboard handler. */ 757219888Sed kbd = kbd_get_keyboard(vd->vd_keyboard); 758219888Sed if (kbd == NULL) 759219888Sed return (-1); 760219888Sed 761256145Sray /* Force keyboard input mode to K_XLATE */ 762256145Sray c = K_XLATE; 763256145Sray kbdd_ioctl(kbd, KDSKBMODE, (void *)&c); 764256145Sray 765219888Sed /* Switch the keyboard to polling to make it work here. */ 766219888Sed kbdd_poll(kbd, TRUE); 767219888Sed c = kbdd_read_char(kbd, 0); 768219888Sed kbdd_poll(kbd, FALSE); 769219888Sed if (c & RELKEY) 770219888Sed return (-1); 771256145Sray 772219888Sed /* Stripped down handling of vt_kbdevent(), without locking, etc. */ 773219888Sed if (c & SPCLKEY) { 774219888Sed c &= ~SPCLKEY; 775219888Sed 776219888Sed switch (c) { 777219888Sed case SLK: { 778243802Snwhitehorn int state = 0; 779219888Sed 780219888Sed kbdd_ioctl(kbd, KDGKBSTATE, (caddr_t)&state); 781219888Sed if (state & SLKED) { 782219888Sed /* Turn scrolling on. */ 783219888Sed vw->vw_flags |= VWF_SCROLL; 784256145Sray VTBUF_SLCK_ENABLE(&vw->vw_buf); 785219888Sed } else { 786219888Sed /* Turn scrolling off. */ 787219888Sed vw->vw_flags &= ~VWF_SCROLL; 788256145Sray VTBUF_SLCK_DISABLE(&vw->vw_buf); 789256145Sray vthistory_seek(&vw->vw_buf, 0, VHS_END); 790256145Sray vd->vd_flags |= VDF_INVALID; 791219888Sed } 792219888Sed break; 793219888Sed } 794219888Sed case FKEY | F(49): /* Home key. */ 795219888Sed if (vw->vw_flags & VWF_SCROLL) 796256145Sray if (vthistory_seek(&vw->vw_buf, 0, VHS_END)) 797256145Sray vd->vd_flags |= VDF_INVALID; 798219888Sed break; 799219888Sed case FKEY | F(50): /* Arrow up. */ 800219888Sed if (vw->vw_flags & VWF_SCROLL) 801256145Sray if (vthistory_seek(&vw->vw_buf, -1, VHS_CUR)) 802256145Sray vd->vd_flags |= VDF_INVALID; 803219888Sed break; 804219888Sed case FKEY | F(51): /* Page up. */ 805219888Sed if (vw->vw_flags & VWF_SCROLL) { 806219888Sed term_pos_t size; 807219888Sed 808219888Sed vt_termsize(vd, vw->vw_font, &size); 809256145Sray if (vthistory_seek(&vw->vw_buf, -size.tp_row, 810256145Sray VHS_CUR)) 811256145Sray vd->vd_flags |= VDF_INVALID; 812219888Sed } 813219888Sed break; 814219888Sed case FKEY | F(57): /* End key. */ 815219888Sed if (vw->vw_flags & VWF_SCROLL) 816256145Sray if (vthistory_seek(&vw->vw_buf, 0, VHS_SET)) 817256145Sray vd->vd_flags |= VDF_INVALID; 818219888Sed break; 819219888Sed case FKEY | F(58): /* Arrow down. */ 820219888Sed if (vw->vw_flags & VWF_SCROLL) 821256145Sray if (vthistory_seek(&vw->vw_buf, 1, VHS_CUR)) 822256145Sray vd->vd_flags |= VDF_INVALID; 823219888Sed break; 824219888Sed case FKEY | F(59): /* Page down. */ 825219888Sed if (vw->vw_flags & VWF_SCROLL) { 826219888Sed term_pos_t size; 827219888Sed 828219888Sed vt_termsize(vd, vw->vw_font, &size); 829256145Sray if (vthistory_seek(&vw->vw_buf, size.tp_row, 830256145Sray VHS_CUR)) 831256145Sray vd->vd_flags |= VDF_INVALID; 832219888Sed } 833219888Sed break; 834219888Sed } 835219888Sed 836219888Sed /* Force refresh to make scrollback work. */ 837219888Sed vt_flush(vd); 838219888Sed } else if (KEYFLAGS(c) == 0) { 839219888Sed return KEYCHAR(c); 840219888Sed } 841256145Sray 842219888Sed return (-1); 843219888Sed} 844219888Sed 845219888Sedstatic void 846219888Sedvtterm_opened(struct terminal *tm, int opened) 847219888Sed{ 848219888Sed struct vt_window *vw = tm->tm_softc; 849219888Sed struct vt_device *vd = vw->vw_device; 850219888Sed 851219888Sed VT_LOCK(vd); 852219888Sed vd->vd_flags &= ~VDF_SPLASH; 853219888Sed if (opened) 854219888Sed vw->vw_flags |= VWF_OPENED; 855256145Sray else { 856219888Sed vw->vw_flags &= ~VWF_OPENED; 857256145Sray /* TODO: finish ACQ/REL */ 858256145Sray } 859219888Sed VT_UNLOCK(vd); 860219888Sed} 861219888Sed 862219888Sedstatic int 863219888Sedvt_change_font(struct vt_window *vw, struct vt_font *vf) 864219888Sed{ 865219888Sed struct vt_device *vd = vw->vw_device; 866219888Sed struct terminal *tm = vw->vw_terminal; 867219888Sed term_pos_t size; 868219888Sed struct winsize wsz; 869219888Sed 870219888Sed /* 871219888Sed * Changing fonts. 872219888Sed * 873219888Sed * Changing fonts is a little tricky. We must prevent 874219888Sed * simultaneous access to the device, so we must stop 875219888Sed * the display timer and the terminal from accessing. 876219888Sed * We need to switch fonts and grow our screen buffer. 877219888Sed * 878219888Sed * XXX: Right now the code uses terminal_mute() to 879219888Sed * prevent data from reaching the console driver while 880219888Sed * resizing the screen buffer. This isn't elegant... 881219888Sed */ 882219888Sed 883219888Sed VT_LOCK(vd); 884219888Sed if (vw->vw_flags & VWF_BUSY) { 885219888Sed /* Another process is changing the font. */ 886219888Sed VT_UNLOCK(vd); 887219888Sed return (EBUSY); 888219888Sed } 889219888Sed if (vw->vw_font == NULL) { 890219888Sed /* Our device doesn't need fonts. */ 891219888Sed VT_UNLOCK(vd); 892219888Sed return (ENOTTY); 893219888Sed } 894219888Sed vw->vw_flags |= VWF_BUSY; 895219888Sed VT_UNLOCK(vd); 896219888Sed 897219888Sed vt_termsize(vd, vf, &size); 898219888Sed vt_winsize(vd, vf, &wsz); 899219888Sed 900219888Sed /* Grow the screen buffer and terminal. */ 901219888Sed terminal_mute(tm, 1); 902256145Sray vtbuf_grow(&vw->vw_buf, &size, vw->vw_buf.vb_history_size); 903219888Sed terminal_set_winsize(tm, &wsz); 904219888Sed terminal_mute(tm, 0); 905219888Sed 906219888Sed /* Actually apply the font to the current window. */ 907219888Sed VT_LOCK(vd); 908219888Sed vtfont_unref(vw->vw_font); 909219888Sed vw->vw_font = vtfont_ref(vf); 910219888Sed 911219888Sed /* Force a full redraw the next timer tick. */ 912219888Sed if (vd->vd_curwindow == vw) 913219888Sed vd->vd_flags |= VDF_INVALID; 914219888Sed vw->vw_flags &= ~VWF_BUSY; 915219888Sed VT_UNLOCK(vd); 916219888Sed return (0); 917219888Sed} 918219888Sed 919219888Sedstatic int 920256145Srayvt_proc_alive(struct vt_window *vw) 921256145Sray{ 922256145Sray struct proc *p; 923256145Sray 924256145Sray if (vw->vw_smode.mode != VT_PROCESS) 925256145Sray return FALSE; 926256145Sray 927256145Sray if (vw->vw_proc) { 928256145Sray if ((p = pfind(vw->vw_pid)) != NULL) 929256145Sray PROC_UNLOCK(p); 930256145Sray if (vw->vw_proc == p) 931256145Sray return TRUE; 932256145Sray vw->vw_proc = NULL; 933256145Sray vw->vw_smode.mode = VT_AUTO; 934256145Sray DPRINTF(1, "vt controlling process %d died\n", vw->vw_pid); 935256145Sray vw->vw_pid = 0; 936256145Sray } 937256145Sray return FALSE; 938256145Sray} 939256145Sray 940256145Sraystatic int 941256145Sraysignal_vt_rel(struct vt_window *vw) 942256145Sray{ 943256145Sray if (vw->vw_smode.mode != VT_PROCESS) 944256145Sray return FALSE; 945256145Sray if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) { 946256145Sray vw->vw_proc = NULL; 947256145Sray vw->vw_pid = 0; 948256145Sray return TRUE; 949256145Sray } 950256145Sray vw->vw_flags |= VWF_SWWAIT_REL; 951256145Sray PROC_LOCK(vw->vw_proc); 952256145Sray kern_psignal(vw->vw_proc, vw->vw_smode.relsig); 953256145Sray PROC_UNLOCK(vw->vw_proc); 954256145Sray DPRINTF(1, "sending relsig to %d\n", vw->vw_pid); 955256145Sray return TRUE; 956256145Sray} 957256145Sray 958256145Sraystatic int 959256145Sraysignal_vt_acq(struct vt_window *vw) 960256145Sray{ 961256145Sray if (vw->vw_smode.mode != VT_PROCESS) 962256145Sray return FALSE; 963256145Sray if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW]) 964256145Sray cnavailable(vw->vw_terminal->consdev, FALSE); 965256145Sray if (vw->vw_proc == NULL || vt_proc_alive(vw) == FALSE) { 966256145Sray vw->vw_proc = NULL; 967256145Sray vw->vw_pid = 0; 968256145Sray return TRUE; 969256145Sray } 970256145Sray vw->vw_flags |= VWF_SWWAIT_ACQ; 971256145Sray PROC_LOCK(vw->vw_proc); 972256145Sray kern_psignal(vw->vw_proc, vw->vw_smode.acqsig); 973256145Sray PROC_UNLOCK(vw->vw_proc); 974256145Sray DPRINTF(1, "sending acqsig to %d\n", vw->vw_pid); 975256145Sray return TRUE; 976256145Sray} 977256145Sray 978256145Sraystatic int 979256145Srayfinish_vt_rel(struct vt_window *vw, int release, int *s) 980256145Sray{ 981256145Sray if (vw->vw_flags & VWF_SWWAIT_REL) { 982256145Sray vw->vw_flags &= ~VWF_SWWAIT_REL; 983256145Sray if (release) { 984256145Sray callout_drain(&vw->vw_proc_dead_timer); 985256145Sray vt_late_window_switch(vw->vw_switch_to); 986256145Sray } 987256145Sray return 0; 988256145Sray } 989256145Sray return EINVAL; 990256145Sray} 991256145Sray 992256145Sraystatic int 993256145Srayfinish_vt_acq(struct vt_window *vw) 994256145Sray{ 995256145Sray if (vw->vw_flags & VWF_SWWAIT_ACQ) { 996256145Sray vw->vw_flags &= ~VWF_SWWAIT_ACQ; 997256145Sray return 0; 998256145Sray } 999256145Sray return EINVAL; 1000256145Sray} 1001256145Sray 1002256145Sraystatic int 1003219888Sedvtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data, 1004219888Sed struct thread *td) 1005219888Sed{ 1006219888Sed struct vt_window *vw = tm->tm_softc; 1007219888Sed struct vt_device *vd = vw->vw_device; 1008256145Sray int error, s; 1009219888Sed 1010219888Sed switch (cmd) { 1011219888Sed case GIO_KEYMAP: 1012219888Sed case PIO_KEYMAP: 1013219888Sed case GIO_DEADKEYMAP: 1014219888Sed case PIO_DEADKEYMAP: 1015219888Sed case GETFKEY: 1016219888Sed case SETFKEY: 1017219888Sed case KDGKBINFO: { 1018219888Sed keyboard_t *kbd; 1019256145Sray error = 0; 1020219888Sed 1021219888Sed mtx_lock(&Giant); 1022219888Sed kbd = kbd_get_keyboard(vd->vd_keyboard); 1023219888Sed if (kbd != NULL) 1024219888Sed error = kbdd_ioctl(kbd, cmd, data); 1025219888Sed mtx_unlock(&Giant); 1026219888Sed if (error == ENOIOCTL) 1027219888Sed return (ENODEV); 1028219888Sed return (error); 1029219888Sed } 1030256145Sray case KDGKBMODE: { 1031256145Sray int mode = -1; 1032256145Sray keyboard_t *kbd; 1033256145Sray 1034256145Sray mtx_lock(&Giant); 1035256145Sray kbd = kbd_get_keyboard(vd->vd_keyboard); 1036256145Sray if (kbd != NULL) { 1037256145Sray kbdd_ioctl(kbd, KDGKBMODE, (void *)&mode); 1038256145Sray } 1039256145Sray mtx_unlock(&Giant); 1040256145Sray DPRINTF(20, "mode %d, vw_kbdmode %d\n", mode, vw->vw_kbdmode); 1041256145Sray *(int *)data = mode; 1042256145Sray return (0); 1043256145Sray } 1044219888Sed case KDSKBMODE: { 1045219888Sed int mode; 1046219888Sed 1047219888Sed mode = *(int *)data; 1048219888Sed switch (mode) { 1049219888Sed case K_XLATE: 1050219888Sed case K_RAW: 1051219888Sed case K_CODE: 1052219888Sed vw->vw_kbdmode = mode; 1053219888Sed if (vw == vd->vd_curwindow) { 1054219888Sed keyboard_t *kbd; 1055256145Sray error = 0; 1056219888Sed 1057256145Sray DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard); 1058219888Sed mtx_lock(&Giant); 1059219888Sed kbd = kbd_get_keyboard(vd->vd_keyboard); 1060256145Sray if (kbd != NULL) { 1061256145Sray DPRINTF(20, "kbdd_ioctl(KDSKBMODE, %d)\n", mode); 1062256145Sray error = kbdd_ioctl(kbd, KDSKBMODE, 1063219888Sed (void *)&mode); 1064256145Sray } 1065219888Sed mtx_unlock(&Giant); 1066256145Sray if (error) 1067256145Sray DPRINTF(20, "kbdd_ioctl(KDSKBMODE) return %d\n", error); 1068219888Sed } 1069219888Sed return (0); 1070219888Sed default: 1071219888Sed return (EINVAL); 1072219888Sed } 1073219888Sed } 1074219888Sed case CONS_BLANKTIME: 1075219888Sed /* XXX */ 1076219888Sed return (0); 1077219888Sed case CONS_GET: 1078219888Sed /* XXX */ 1079219888Sed *(int *)data = M_CG640x480; 1080219888Sed return (0); 1081219888Sed case CONS_GETINFO: { 1082219888Sed vid_info_t *vi = (vid_info_t *)data; 1083219888Sed 1084219888Sed vi->m_num = vd->vd_curwindow->vw_number + 1; 1085219888Sed /* XXX: other fields! */ 1086219888Sed return (0); 1087219888Sed } 1088219888Sed case CONS_GETVERS: 1089219888Sed *(int *)data = 0x200; 1090219888Sed return 0; 1091219888Sed case CONS_MODEINFO: 1092219888Sed /* XXX */ 1093219888Sed return (0); 1094219888Sed case CONS_MOUSECTL: { 1095219888Sed mouse_info_t *mouse = (mouse_info_t*)data; 1096219888Sed 1097219888Sed /* 1098219888Sed * This has no effect on vt(4). We don't draw any mouse 1099219888Sed * cursor. Just ignore MOUSE_HIDE and MOUSE_SHOW to 1100219888Sed * prevent excessive errors. All the other commands 1101219888Sed * should not be applied to individual TTYs, but only to 1102219888Sed * consolectl. 1103219888Sed */ 1104219888Sed switch (mouse->operation) { 1105219888Sed case MOUSE_HIDE: 1106219888Sed case MOUSE_SHOW: 1107219888Sed return (0); 1108219888Sed default: 1109219888Sed return (EINVAL); 1110219888Sed } 1111219888Sed } 1112219888Sed case PIO_VFONT: { 1113219888Sed struct vt_font *vf; 1114219888Sed 1115219888Sed error = vtfont_load((void *)data, &vf); 1116219888Sed if (error != 0) 1117219888Sed return (error); 1118219888Sed 1119219888Sed error = vt_change_font(vw, vf); 1120219888Sed vtfont_unref(vf); 1121219888Sed return (error); 1122219888Sed } 1123219888Sed case GIO_SCRNMAP: { 1124219888Sed scrmap_t *sm = (scrmap_t *)data; 1125219888Sed int i; 1126219888Sed 1127219888Sed /* We don't have screen maps, so return a handcrafted one. */ 1128219888Sed for (i = 0; i < 256; i++) 1129219888Sed sm->scrmap[i] = i; 1130219888Sed return (0); 1131219888Sed } 1132219888Sed case KDGETLED: 1133219888Sed /* XXX */ 1134219888Sed return (0); 1135219888Sed case KDSETLED: 1136219888Sed /* XXX */ 1137219888Sed return (0); 1138219888Sed case KDSETMODE: 1139219888Sed /* XXX */ 1140219888Sed return (0); 1141219888Sed case KDSETRAD: 1142219888Sed /* XXX */ 1143219888Sed return (0); 1144256145Sray case VT_ACTIVATE: { 1145256145Sray int win; 1146256145Sray win = *(int *)data - 1; 1147256145Sray DPRINTF(5, "%s%d: VT_ACTIVATE ttyv%d ", SC_DRIVER_NAME, VT_UNIT(vw), win); 1148256145Sray if ((win > VT_MAXWINDOWS) || (win < 0)) 1149256145Sray return (EINVAL); 1150256145Sray return (vt_proc_window_switch(vd->vd_windows[win])); 1151256145Sray } 1152219888Sed case VT_GETACTIVE: 1153219888Sed *(int *)data = vd->vd_curwindow->vw_number + 1; 1154219888Sed return (0); 1155219888Sed case VT_GETINDEX: 1156219888Sed *(int *)data = vw->vw_number + 1; 1157219888Sed return (0); 1158256145Sray case VT_LOCKSWITCH: 1159256145Sray /* TODO: Check current state, switching can be in progress. */ 1160256145Sray if ((*(int *)data) & 0x01) 1161256145Sray vw->vw_flags |= VWF_VTYLOCK; 1162256145Sray else 1163256145Sray vw->vw_flags &= ~VWF_VTYLOCK; 1164219888Sed case VT_OPENQRY: { 1165219888Sed unsigned int i; 1166219888Sed 1167219888Sed VT_LOCK(vd); 1168219888Sed for (i = 0; i < VT_MAXWINDOWS; i++) { 1169219888Sed vw = vd->vd_windows[i]; 1170219888Sed if (vw == NULL) 1171219888Sed continue; 1172219888Sed if (!(vw->vw_flags & VWF_OPENED)) { 1173219888Sed *(int *)data = vw->vw_number + 1; 1174219888Sed VT_UNLOCK(vd); 1175219888Sed return (0); 1176219888Sed } 1177219888Sed } 1178219888Sed VT_UNLOCK(vd); 1179219888Sed return (EINVAL); 1180219888Sed } 1181219888Sed case VT_WAITACTIVE: { 1182219888Sed unsigned int i; 1183256145Sray error = 0; 1184219888Sed 1185219888Sed i = *(unsigned int *)data; 1186219888Sed if (i > VT_MAXWINDOWS) 1187219888Sed return (EINVAL); 1188219888Sed if (i != 0) 1189219888Sed vw = vd->vd_windows[i - 1]; 1190219888Sed 1191219888Sed VT_LOCK(vd); 1192219888Sed while (vd->vd_curwindow != vw && error == 0) 1193219888Sed error = cv_wait_sig(&vd->vd_winswitch, &vd->vd_lock); 1194219888Sed VT_UNLOCK(vd); 1195219888Sed return (error); 1196219888Sed } 1197256145Sray case VT_SETMODE: /* set screen switcher mode */ 1198256145Sray { 1199256145Sray struct vt_mode *mode; 1200256145Sray struct proc *p1; 1201256145Sray 1202256145Sray mode = (struct vt_mode *)data; 1203256145Sray DPRINTF(5, "%s%d: VT_SETMODE ", SC_DRIVER_NAME, VT_UNIT(vw)); 1204256145Sray if (vw->vw_smode.mode == VT_PROCESS) { 1205256145Sray p1 = pfind(vw->vw_pid); 1206256145Sray if (vw->vw_proc == p1 && vw->vw_proc != td->td_proc) { 1207256145Sray if (p1) 1208256145Sray PROC_UNLOCK(p1); 1209256145Sray DPRINTF(5, "error EPERM\n"); 1210256145Sray return (EPERM); 1211256145Sray } 1212256145Sray if (p1) 1213256145Sray PROC_UNLOCK(p1); 1214256145Sray } 1215256145Sray if (mode->mode == VT_AUTO) { 1216256145Sray vw->vw_smode.mode = VT_AUTO; 1217256145Sray vw->vw_proc = NULL; 1218256145Sray vw->vw_pid = 0; 1219256145Sray DPRINTF(5, "VT_AUTO, "); 1220256145Sray if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW]) 1221256145Sray cnavailable(vw->vw_terminal->consdev, TRUE); 1222256145Sray /* were we in the middle of the vty switching process? */ 1223256145Sray if (finish_vt_rel(vw, TRUE, &s) == 0) 1224256145Sray DPRINTF(5, "reset WAIT_REL, "); 1225256145Sray if (finish_vt_acq(vw) == 0) 1226256145Sray DPRINTF(5, "reset WAIT_ACQ, "); 1227256145Sray return (0); 1228256145Sray } else if (mode->mode == VT_PROCESS) { 1229256145Sray if (!ISSIGVALID(mode->relsig) || 1230256145Sray !ISSIGVALID(mode->acqsig) || 1231256145Sray !ISSIGVALID(mode->frsig)) { 1232256145Sray DPRINTF(5, "error EINVAL\n"); 1233256145Sray return (EINVAL); 1234256145Sray } 1235256145Sray DPRINTF(5, "VT_PROCESS %d, ", td->td_proc->p_pid); 1236256145Sray bcopy(data, &vw->vw_smode, sizeof(struct vt_mode)); 1237256145Sray vw->vw_proc = td->td_proc; 1238256145Sray vw->vw_pid = vw->vw_proc->p_pid; 1239256145Sray if (vw == vw->vw_device->vd_windows[VT_CONSWINDOW]) 1240256145Sray cnavailable(vw->vw_terminal->consdev, FALSE); 1241256145Sray } else { 1242256145Sray DPRINTF(5, "VT_SETMODE failed, unknown mode %d\n", 1243256145Sray mode->mode); 1244256145Sray return (EINVAL); 1245256145Sray } 1246256145Sray DPRINTF(5, "\n"); 1247256145Sray return 0; 1248219888Sed } 1249219888Sed 1250256145Sray case VT_GETMODE: /* get screen switcher mode */ 1251256145Sray bcopy(&vw->vw_smode, data, sizeof(struct vt_mode)); 1252256145Sray return 0; 1253256145Sray 1254256145Sray case VT_RELDISP: /* screen switcher ioctl */ 1255256145Sray /* 1256256145Sray * This must be the current vty which is in the VT_PROCESS 1257256145Sray * switching mode... 1258256145Sray */ 1259256145Sray if ((vw != vd->vd_curwindow) || (vw->vw_smode.mode != 1260256145Sray VT_PROCESS)) { 1261256145Sray return EINVAL; 1262256145Sray } 1263256145Sray /* ...and this process is controlling it. */ 1264256145Sray if (vw->vw_proc != td->td_proc) { 1265256145Sray return EPERM; 1266256145Sray } 1267256145Sray error = EINVAL; 1268256145Sray switch(*(int *)data) { 1269256145Sray case VT_FALSE: /* user refuses to release screen, abort */ 1270256145Sray if ((error = finish_vt_rel(vw, FALSE, &s)) == 0) 1271256145Sray DPRINTF(5, "%s%d: VT_RELDISP: VT_FALSE\n", SC_DRIVER_NAME, 1272256145Sray VT_UNIT(vw)); 1273256145Sray break; 1274256145Sray case VT_TRUE: /* user has released screen, go on */ 1275256145Sray /* finish_vt_rel(..., TRUE, ...) should not be locked */ 1276256145Sray if (vw->vw_flags & VWF_SWWAIT_REL) { 1277256145Sray if ((error = finish_vt_rel(vw, TRUE, &s)) == 0) 1278256145Sray DPRINTF(5, "%s%d: VT_RELDISP: VT_TRUE\n", 1279256145Sray SC_DRIVER_NAME, VT_UNIT(vw)); 1280256145Sray } else { 1281256145Sray error = EINVAL; 1282256145Sray } 1283256145Sray return (error); 1284256145Sray case VT_ACKACQ: /* acquire acknowledged, switch completed */ 1285256145Sray if ((error = finish_vt_acq(vw)) == 0) 1286256145Sray DPRINTF(5, "%s%d: VT_RELDISP: VT_ACKACQ\n", SC_DRIVER_NAME, 1287256145Sray VT_UNIT(vw)); 1288256145Sray break; 1289256145Sray default: 1290256145Sray break; 1291256145Sray } 1292256145Sray return error; 1293256145Sray } 1294256145Sray 1295219888Sed return (ENOIOCTL); 1296219888Sed} 1297219888Sed 1298219888Sedstatic struct vt_window * 1299219888Sedvt_allocate_window(struct vt_device *vd, unsigned int window) 1300219888Sed{ 1301219888Sed struct vt_window *vw; 1302219888Sed struct terminal *tm; 1303219888Sed term_pos_t size; 1304219888Sed struct winsize wsz; 1305219888Sed 1306219888Sed vw = malloc(sizeof *vw, M_VT, M_WAITOK|M_ZERO); 1307219888Sed vw->vw_device = vd; 1308219888Sed vw->vw_number = window; 1309219888Sed vw->vw_kbdmode = K_XLATE; 1310219888Sed 1311219888Sed if (!(vd->vd_flags & VDF_TEXTMODE)) 1312219888Sed vw->vw_font = vtfont_ref(&vt_font_default); 1313256145Sray 1314219888Sed vt_termsize(vd, vw->vw_font, &size); 1315219888Sed vt_winsize(vd, vw->vw_font, &wsz); 1316219888Sed vtbuf_init(&vw->vw_buf, &size); 1317219888Sed 1318219888Sed tm = vw->vw_terminal = terminal_alloc(&vt_termclass, vw); 1319219888Sed terminal_set_winsize(tm, &wsz); 1320219888Sed vd->vd_windows[window] = vw; 1321256145Sray callout_init(&vw->vw_proc_dead_timer, 0); 1322219888Sed 1323219888Sed return (vw); 1324219888Sed} 1325219888Sed 1326219888Sedvoid 1327219888Sedvt_upgrade(struct vt_device *vd) 1328219888Sed{ 1329219888Sed struct vt_window *vw; 1330219888Sed unsigned int i; 1331219888Sed 1332256145Sray /* Device didn't pass vd_init() or already upgraded. */ 1333256145Sray if (vd->vd_flags & (VDF_ASYNC|VDF_DEAD)) 1334219888Sed return; 1335256145Sray vd->vd_flags |= VDF_ASYNC; 1336219888Sed 1337219888Sed mtx_init(&vd->vd_lock, "vtdev", NULL, MTX_DEF); 1338219888Sed cv_init(&vd->vd_winswitch, "vtwswt"); 1339219888Sed 1340256145Sray /* Init 25 Hz timer. */ 1341219888Sed callout_init_mtx(&vd->vd_timer, &vd->vd_lock, 0); 1342219888Sed 1343219888Sed for (i = 0; i < VT_MAXWINDOWS; i++) { 1344219888Sed vw = vd->vd_windows[i]; 1345219888Sed if (vw == NULL) { 1346219888Sed /* New window. */ 1347219888Sed vw = vt_allocate_window(vd, i); 1348256145Sray } 1349256145Sray if (i == VT_CONSWINDOW) { 1350219888Sed /* Console window. */ 1351219888Sed EVENTHANDLER_REGISTER(shutdown_pre_sync, 1352219888Sed vt_window_switch, vw, SHUTDOWN_PRI_DEFAULT); 1353219888Sed } 1354219888Sed terminal_maketty(vw->vw_terminal, "v%r", VT_UNIT(vw)); 1355219888Sed } 1356256145Sray if (vd->vd_curwindow == NULL) 1357256145Sray vd->vd_curwindow = vd->vd_windows[VT_CONSWINDOW]; 1358219888Sed 1359219888Sed /* Attach keyboard. */ 1360219888Sed vt_allocate_keyboard(vd); 1361256145Sray DPRINTF(20, "%s: vd_keyboard = %d\n", __func__, vd->vd_keyboard); 1362256145Sray 1363256145Sray /* Start timer when everything ready. */ 1364256145Sray callout_reset(&vd->vd_timer, hz / VT_TIMERFREQ, vt_timer, vd); 1365219888Sed} 1366219888Sed 1367256145Sraystatic void 1368256145Srayvt_resize(struct vt_device *vd) 1369256145Sray{ 1370256145Sray struct vt_window *vw; 1371256145Sray int i; 1372256145Sray 1373256145Sray for (i = 0; i < VT_MAXWINDOWS; i++) { 1374256145Sray vw = vd->vd_windows[i]; 1375256145Sray /* Resize terminal windows */ 1376256145Sray vt_change_font(vw, vw->vw_font); 1377256145Sray } 1378256145Sray} 1379256145Sray 1380219888Sedvoid 1381219888Sedvt_allocate(struct vt_driver *drv, void *softc) 1382219888Sed{ 1383219888Sed struct vt_device *vd; 1384219888Sed 1385256145Sray if (main_vd == NULL) { 1386256145Sray main_vd = malloc(sizeof *vd, M_VT, M_WAITOK|M_ZERO); 1387256145Sray } else { 1388256145Sray /* 1389256145Sray * Check if have rights to replace current driver. For example: 1390256145Sray * it is bad idea to replace KMS driver with generic VGA one. 1391256145Sray */ 1392256145Sray /* Lowest preferred. */ 1393256145Sray if (drv->vd_priority >= main_vd->vd_driver->vd_priority) 1394256145Sray return; 1395256145Sray } 1396256145Sray vd = main_vd; 1397256145Sray 1398256145Sray /* Stop vd_flash periodic task. */ 1399256145Sray if (vd->vd_curwindow != NULL) 1400256145Sray callout_drain(&vd->vd_timer); 1401256145Sray 1402219888Sed vd->vd_driver = drv; 1403219888Sed vd->vd_softc = softc; 1404219888Sed vd->vd_driver->vd_init(vd); 1405256145Sray 1406219888Sed vt_upgrade(vd); 1407256145Sray 1408256145Sray /* Refill settings with new sizes. */ 1409256145Sray vt_resize(vd); 1410256145Sray 1411256145Sray if (vd->vd_flags & VDF_SPLASH) 1412256145Sray vtterm_splash(vd); 1413256145Sray 1414256145Sray if (vd->vd_curwindow != NULL) 1415256145Sray callout_schedule(&vd->vd_timer, hz / VT_TIMERFREQ); 1416256145Sray 1417256145Sray termcn_cnregister(vd->vd_windows[VT_CONSWINDOW]->vw_terminal); 1418219888Sed} 1419