1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * ANSI terminal emulator module; parse ANSI X3.64 escape sequences and 29 * the like. 30 * 31 * How Virtual Terminal Emulator Works: 32 * 33 * Every virtual terminal is associated with a tem_vt_state structure 34 * and maintains a virtual screen buffer in tvs_screen_buf, which contains 35 * all the characters which should be shown on the physical screen when 36 * the terminal is activated. There are also two other buffers, tvs_fg_buf 37 * and tvs_bg_buf, which track the foreground and background colors of the 38 * on screen characters 39 * 40 * Data written to a virtual terminal is composed of characters which 41 * should be displayed on the screen when this virtual terminal is 42 * activated, fg/bg colors of these characters, and other control 43 * information (escape sequence, etc). 44 * 45 * When data is passed to a virtual terminal it first is parsed for 46 * control information by tem_safe_parse(). Subsequently the character 47 * and color data are written to tvs_screen_buf, tvs_fg_buf, and 48 * tvs_bg_buf. They are saved in these buffers in order to refresh 49 * the screen when this terminal is activated. If the terminal is 50 * currently active, the data (characters and colors) are also written 51 * to the physical screen by invoking a callback function, 52 * tem_safe_text_callbacks() or tem_safe_pix_callbacks(). 53 * 54 * When rendering data to the framebuffer, if the framebuffer is in 55 * VIS_PIXEL mode, the character data will first be converted to pixel 56 * data using tem_safe_pix_bit2pix(), and then the pixels get displayed 57 * on the physical screen. We only store the character and color data in 58 * tem_vt_state since the bit2pix conversion only happens when actually 59 * rendering to the physical framebuffer. 60 */ 61 62 63 #include <sys/types.h> 64 #include <sys/file.h> 65 #include <sys/conf.h> 66 #include <sys/errno.h> 67 #include <sys/open.h> 68 #include <sys/cred.h> 69 #include <sys/kmem.h> 70 #include <sys/ascii.h> 71 #include <sys/consdev.h> 72 #include <sys/font.h> 73 #include <sys/fbio.h> 74 #include <sys/conf.h> 75 #include <sys/modctl.h> 76 #include <sys/strsubr.h> 77 #include <sys/stat.h> 78 #include <sys/visual_io.h> 79 #include <sys/mutex.h> 80 #include <sys/param.h> 81 #include <sys/debug.h> 82 #include <sys/cmn_err.h> 83 #include <sys/console.h> 84 #include <sys/ddi.h> 85 #include <sys/sunddi.h> 86 #include <sys/sunldi.h> 87 #include <sys/tem_impl.h> 88 #ifdef _HAVE_TEM_FIRMWARE 89 #include <sys/promif.h> 90 #endif /* _HAVE_TEM_FIRMWARE */ 91 #include <sys/consplat.h> 92 #include <sys/kd.h> 93 #include <sys/sysmacros.h> 94 #include <sys/note.h> 95 #include <sys/t_lock.h> 96 97 /* Terminal emulator internal helper functions */ 98 static void tems_setup_terminal(struct vis_devinit *, size_t, size_t); 99 static void tems_modechange_callback(struct vis_modechg_arg *, 100 struct vis_devinit *); 101 102 static void tems_reset_colormap(cred_t *, enum called_from); 103 104 static void tem_free_buf(struct tem_vt_state *); 105 static void tem_internal_init(struct tem_vt_state *, cred_t *, boolean_t, 106 boolean_t); 107 static void tems_get_initial_color(tem_color_t *pcolor); 108 109 /* 110 * Globals 111 */ 112 static ldi_ident_t term_li = NULL; 113 tem_state_t tems; /* common term info */ 114 115 extern struct mod_ops mod_miscops; 116 117 static struct modlmisc modlmisc = { 118 &mod_miscops, /* modops */ 119 "ANSI Terminal Emulator", /* name */ 120 }; 121 122 static struct modlinkage modlinkage = { 123 MODREV_1, (void *)&modlmisc, NULL 124 }; 125 126 int 127 _init(void) 128 { 129 int ret; 130 ret = mod_install(&modlinkage); 131 if (ret != 0) 132 return (ret); 133 ret = ldi_ident_from_mod(&modlinkage, &term_li); 134 if (ret != 0) { 135 (void) mod_remove(&modlinkage); 136 return (ret); 137 } 138 139 mutex_init(&tems.ts_lock, (char *)NULL, MUTEX_DRIVER, NULL); 140 list_create(&tems.ts_list, sizeof (struct tem_vt_state), 141 offsetof(struct tem_vt_state, tvs_list_node)); 142 tems.ts_active = NULL; 143 144 return (0); 145 } 146 147 int 148 _fini() 149 { 150 int ret; 151 152 ret = mod_remove(&modlinkage); 153 if (ret == 0) { 154 ldi_ident_release(term_li); 155 term_li = NULL; 156 } 157 return (ret); 158 } 159 160 int 161 _info(struct modinfo *modinfop) 162 { 163 return (mod_info(&modlinkage, modinfop)); 164 } 165 166 static void 167 tem_add(struct tem_vt_state *tem) 168 { 169 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)); 170 171 list_insert_head(&tems.ts_list, tem); 172 } 173 174 static void 175 tem_rm(struct tem_vt_state *tem) 176 { 177 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)); 178 179 list_remove(&tems.ts_list, tem); 180 } 181 182 /* 183 * This is the main entry point to the module. It handles output requests 184 * during normal system operation, when (e.g.) mutexes are available. 185 */ 186 void 187 tem_write(tem_vt_state_t tem_arg, uchar_t *buf, ssize_t len, cred_t *credp) 188 { 189 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 190 191 mutex_enter(&tems.ts_lock); 192 mutex_enter(&tem->tvs_lock); 193 194 if (!tem->tvs_initialized) { 195 mutex_exit(&tem->tvs_lock); 196 mutex_exit(&tems.ts_lock); 197 return; 198 } 199 200 tem_safe_check_first_time(tem, credp, CALLED_FROM_NORMAL); 201 tem_safe_terminal_emulate(tem, buf, len, credp, CALLED_FROM_NORMAL); 202 203 mutex_exit(&tem->tvs_lock); 204 mutex_exit(&tems.ts_lock); 205 } 206 207 static void 208 tem_internal_init(struct tem_vt_state *ptem, cred_t *credp, 209 boolean_t init_color, boolean_t clear_screen) 210 { 211 int i, j; 212 int width, height; 213 int total; 214 text_color_t fg; 215 text_color_t bg; 216 size_t tc_size = sizeof (text_color_t); 217 218 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&ptem->tvs_lock)); 219 220 if (tems.ts_display_mode == VIS_PIXEL) { 221 ptem->tvs_pix_data_size = tems.ts_pix_data_size; 222 ptem->tvs_pix_data = 223 kmem_alloc(ptem->tvs_pix_data_size, KM_SLEEP); 224 } 225 226 ptem->tvs_outbuf_size = tems.ts_c_dimension.width; 227 ptem->tvs_outbuf = 228 (unsigned char *)kmem_alloc(ptem->tvs_outbuf_size, KM_SLEEP); 229 230 width = tems.ts_c_dimension.width; 231 height = tems.ts_c_dimension.height; 232 ptem->tvs_screen_buf_size = width * height; 233 ptem->tvs_screen_buf = 234 (unsigned char *)kmem_alloc(width * height, KM_SLEEP); 235 236 total = width * height * tc_size; 237 ptem->tvs_fg_buf = (text_color_t *)kmem_alloc(total, KM_SLEEP); 238 ptem->tvs_bg_buf = (text_color_t *)kmem_alloc(total, KM_SLEEP); 239 ptem->tvs_color_buf_size = total; 240 241 tem_safe_reset_display(ptem, credp, CALLED_FROM_NORMAL, 242 clear_screen, init_color); 243 244 ptem->tvs_utf8_left = 0; 245 ptem->tvs_utf8_partial = 0; 246 247 tem_safe_get_color(ptem, &fg, &bg, TEM_ATTR_SCREEN_REVERSE); 248 for (i = 0; i < height; i++) 249 for (j = 0; j < width; j++) { 250 ptem->tvs_screen_buf[i * width + j] = ' '; 251 ptem->tvs_fg_buf[(i * width +j) * tc_size] = fg; 252 ptem->tvs_bg_buf[(i * width +j) * tc_size] = bg; 253 254 } 255 256 ptem->tvs_initialized = 1; 257 } 258 259 int 260 tem_initialized(tem_vt_state_t tem_arg) 261 { 262 struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg; 263 int ret; 264 265 mutex_enter(&ptem->tvs_lock); 266 ret = ptem->tvs_initialized; 267 mutex_exit(&ptem->tvs_lock); 268 269 return (ret); 270 } 271 272 tem_vt_state_t 273 tem_init(cred_t *credp) 274 { 275 struct tem_vt_state *ptem; 276 277 ptem = kmem_zalloc(sizeof (struct tem_vt_state), KM_SLEEP); 278 mutex_init(&ptem->tvs_lock, (char *)NULL, MUTEX_DRIVER, NULL); 279 280 mutex_enter(&tems.ts_lock); 281 mutex_enter(&ptem->tvs_lock); 282 283 ptem->tvs_isactive = B_FALSE; 284 ptem->tvs_fbmode = KD_TEXT; 285 286 /* 287 * A tem is regarded as initialized only after tem_internal_init(), 288 * will be set at the end of tem_internal_init(). 289 */ 290 ptem->tvs_initialized = 0; 291 292 293 if (!tems.ts_initialized) { 294 /* 295 * Only happens during early console configuration. 296 */ 297 tem_add(ptem); 298 mutex_exit(&ptem->tvs_lock); 299 mutex_exit(&tems.ts_lock); 300 return ((tem_vt_state_t)ptem); 301 } 302 303 tem_internal_init(ptem, credp, B_TRUE, B_FALSE); 304 tem_add(ptem); 305 mutex_exit(&ptem->tvs_lock); 306 mutex_exit(&tems.ts_lock); 307 308 return ((tem_vt_state_t)ptem); 309 } 310 311 /* 312 * re-init the tem after video mode has changed and tems_info has 313 * been re-inited. The lock is already held. 314 */ 315 static void 316 tem_reinit(struct tem_vt_state *tem, boolean_t reset_display) 317 { 318 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)); 319 320 tem_free_buf(tem); /* only free virtual buffers */ 321 322 /* reserve color */ 323 tem_internal_init(tem, kcred, B_FALSE, reset_display); 324 } 325 326 static void 327 tem_free_buf(struct tem_vt_state *tem) 328 { 329 ASSERT(tem != NULL && MUTEX_HELD(&tem->tvs_lock)); 330 331 if (tem->tvs_outbuf != NULL) 332 kmem_free(tem->tvs_outbuf, tem->tvs_outbuf_size); 333 if (tem->tvs_pix_data != NULL) 334 kmem_free(tem->tvs_pix_data, tem->tvs_pix_data_size); 335 if (tem->tvs_screen_buf != NULL) 336 kmem_free(tem->tvs_screen_buf, tem->tvs_screen_buf_size); 337 if (tem->tvs_fg_buf != NULL) 338 kmem_free(tem->tvs_fg_buf, tem->tvs_color_buf_size); 339 if (tem->tvs_bg_buf != NULL) 340 kmem_free(tem->tvs_bg_buf, tem->tvs_color_buf_size); 341 } 342 343 void 344 tem_destroy(tem_vt_state_t tem_arg, cred_t *credp) 345 { 346 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 347 348 mutex_enter(&tems.ts_lock); 349 mutex_enter(&tem->tvs_lock); 350 351 if (tem->tvs_isactive && tem->tvs_fbmode == KD_TEXT) 352 tem_safe_blank_screen(tem, credp, CALLED_FROM_NORMAL); 353 354 tem_free_buf(tem); 355 tem_rm(tem); 356 357 if (tems.ts_active == tem) 358 tems.ts_active = NULL; 359 360 mutex_exit(&tem->tvs_lock); 361 mutex_exit(&tems.ts_lock); 362 363 kmem_free(tem, sizeof (struct tem_vt_state)); 364 } 365 366 static int 367 tems_failed(cred_t *credp, boolean_t finish_ioctl) 368 { 369 int lyr_rval; 370 371 ASSERT(MUTEX_HELD(&tems.ts_lock)); 372 373 if (finish_ioctl) 374 (void) ldi_ioctl(tems.ts_hdl, VIS_DEVFINI, 0, 375 FWRITE|FKIOCTL, credp, &lyr_rval); 376 377 (void) ldi_close(tems.ts_hdl, NULL, credp); 378 tems.ts_hdl = NULL; 379 return (ENXIO); 380 } 381 382 /* 383 * only called once during boot 384 */ 385 int 386 tem_info_init(char *pathname, cred_t *credp) 387 { 388 int lyr_rval, ret; 389 struct vis_devinit temargs; 390 char *pathbuf; 391 size_t height = 0; 392 size_t width = 0; 393 struct tem_vt_state *p; 394 395 mutex_enter(&tems.ts_lock); 396 397 if (tems.ts_initialized) { 398 mutex_exit(&tems.ts_lock); 399 return (0); 400 } 401 402 /* 403 * Open the layered device using the devfs physical device name 404 * after adding the /devices prefix. 405 */ 406 pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 407 (void) strcpy(pathbuf, "/devices"); 408 if (i_ddi_prompath_to_devfspath(pathname, 409 pathbuf + strlen("/devices")) != DDI_SUCCESS) { 410 cmn_err(CE_WARN, "terminal-emulator: path conversion error"); 411 kmem_free(pathbuf, MAXPATHLEN); 412 413 mutex_exit(&tems.ts_lock); 414 return (ENXIO); 415 } 416 if (ldi_open_by_name(pathbuf, FWRITE, credp, 417 &tems.ts_hdl, term_li) != 0) { 418 cmn_err(CE_WARN, "terminal-emulator: device path open error"); 419 kmem_free(pathbuf, MAXPATHLEN); 420 421 mutex_exit(&tems.ts_lock); 422 return (ENXIO); 423 } 424 kmem_free(pathbuf, MAXPATHLEN); 425 426 temargs.modechg_cb = (vis_modechg_cb_t)tems_modechange_callback; 427 temargs.modechg_arg = NULL; 428 429 /* 430 * Initialize the console and get the device parameters 431 */ 432 if (ldi_ioctl(tems.ts_hdl, VIS_DEVINIT, 433 (intptr_t)&temargs, FWRITE|FKIOCTL, credp, &lyr_rval) != 0) { 434 cmn_err(CE_WARN, "terminal emulator: Compatible fb not found"); 435 ret = tems_failed(credp, B_FALSE); 436 mutex_exit(&tems.ts_lock); 437 return (ret); 438 } 439 440 /* Make sure the fb driver and terminal emulator versions match */ 441 if (temargs.version != VIS_CONS_REV) { 442 cmn_err(CE_WARN, 443 "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) " 444 "of console fb driver not supported", temargs.version); 445 ret = tems_failed(credp, B_TRUE); 446 mutex_exit(&tems.ts_lock); 447 return (ret); 448 } 449 450 if ((tems.ts_fb_polledio = temargs.polledio) == NULL) { 451 cmn_err(CE_WARN, "terminal emulator: fb doesn't support polled " 452 "I/O"); 453 ret = tems_failed(credp, B_TRUE); 454 mutex_exit(&tems.ts_lock); 455 return (ret); 456 } 457 458 /* other sanity checks */ 459 if (!((temargs.depth == 4) || (temargs.depth == 8) || 460 (temargs.depth == 24) || (temargs.depth == 32))) { 461 cmn_err(CE_WARN, "terminal emulator: unsupported depth"); 462 ret = tems_failed(credp, B_TRUE); 463 mutex_exit(&tems.ts_lock); 464 return (ret); 465 } 466 467 if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) { 468 cmn_err(CE_WARN, "terminal emulator: unsupported mode"); 469 ret = tems_failed(credp, B_TRUE); 470 mutex_exit(&tems.ts_lock); 471 return (ret); 472 } 473 474 if ((temargs.mode == VIS_PIXEL) && plat_stdout_is_framebuffer()) 475 plat_tem_get_prom_size(&height, &width); 476 477 /* 478 * Initialize the common terminal emulator info 479 */ 480 tems_setup_terminal(&temargs, height, width); 481 482 tems_reset_colormap(credp, CALLED_FROM_NORMAL); 483 tems_get_initial_color(&tems.ts_init_color); 484 485 tems.ts_initialized = 1; /* initialization flag */ 486 487 for (p = list_head(&tems.ts_list); p != NULL; 488 p = list_next(&tems.ts_list, p)) { 489 mutex_enter(&p->tvs_lock); 490 tem_internal_init(p, credp, B_TRUE, B_FALSE); 491 if (temargs.mode == VIS_PIXEL) 492 tem_pix_align(p, credp, CALLED_FROM_NORMAL); 493 mutex_exit(&p->tvs_lock); 494 } 495 496 mutex_exit(&tems.ts_lock); 497 return (0); 498 } 499 500 #define TEMS_DEPTH_DIFF 0x01 501 #define TEMS_DIMENSION_DIFF 0x02 502 503 static uchar_t 504 tems_check_videomode(struct vis_devinit *tp) 505 { 506 uchar_t result = 0; 507 508 if (tems.ts_pdepth != tp->depth) 509 result |= TEMS_DEPTH_DIFF; 510 511 if (tp->mode == VIS_TEXT) { 512 if (tems.ts_c_dimension.width != tp->width || 513 tems.ts_c_dimension.height != tp->height) 514 result |= TEMS_DIMENSION_DIFF; 515 } else { 516 if (tems.ts_p_dimension.width != tp->width || 517 tems.ts_p_dimension.height != tp->height) 518 result |= TEMS_DIMENSION_DIFF; 519 } 520 521 return (result); 522 } 523 524 static void 525 tems_setup_terminal(struct vis_devinit *tp, size_t height, size_t width) 526 { 527 int i; 528 int old_blank_buf_size = tems.ts_c_dimension.width; 529 530 ASSERT(MUTEX_HELD(&tems.ts_lock)); 531 532 tems.ts_pdepth = tp->depth; 533 tems.ts_linebytes = tp->linebytes; 534 tems.ts_display_mode = tp->mode; 535 536 switch (tp->mode) { 537 case VIS_TEXT: 538 tems.ts_p_dimension.width = 0; 539 tems.ts_p_dimension.height = 0; 540 tems.ts_c_dimension.width = tp->width; 541 tems.ts_c_dimension.height = tp->height; 542 tems.ts_callbacks = &tem_safe_text_callbacks; 543 544 break; 545 546 case VIS_PIXEL: 547 /* 548 * First check to see if the user has specified a screen size. 549 * If so, use those values. Else use 34x80 as the default. 550 */ 551 if (width == 0) { 552 width = TEM_DEFAULT_COLS; 553 height = TEM_DEFAULT_ROWS; 554 } 555 tems.ts_c_dimension.height = (screen_size_t)height; 556 tems.ts_c_dimension.width = (screen_size_t)width; 557 558 tems.ts_p_dimension.height = tp->height; 559 tems.ts_p_dimension.width = tp->width; 560 561 tems.ts_callbacks = &tem_safe_pix_callbacks; 562 563 /* 564 * set_font() will select a appropriate sized font for 565 * the number of rows and columns selected. If we don't 566 * have a font that will fit, then it will use the 567 * default builtin font and adjust the rows and columns 568 * to fit on the screen. 569 */ 570 set_font(&tems.ts_font, 571 &tems.ts_c_dimension.height, 572 &tems.ts_c_dimension.width, 573 tems.ts_p_dimension.height, 574 tems.ts_p_dimension.width); 575 576 tems.ts_p_offset.y = (tems.ts_p_dimension.height - 577 (tems.ts_c_dimension.height * tems.ts_font.height)) / 2; 578 tems.ts_p_offset.x = (tems.ts_p_dimension.width - 579 (tems.ts_c_dimension.width * tems.ts_font.width)) / 2; 580 581 tems.ts_pix_data_size = 582 tems.ts_font.width * tems.ts_font.height; 583 584 tems.ts_pix_data_size *= 4; 585 586 tems.ts_pdepth = tp->depth; 587 588 break; 589 } 590 591 /* Now virtual cls also uses the blank_line buffer */ 592 if (tems.ts_blank_line) 593 kmem_free(tems.ts_blank_line, old_blank_buf_size); 594 595 tems.ts_blank_line = (unsigned char *) 596 kmem_alloc(tems.ts_c_dimension.width, KM_SLEEP); 597 for (i = 0; i < tems.ts_c_dimension.width; i++) 598 tems.ts_blank_line[i] = ' '; 599 } 600 601 /* 602 * This is a callback function that we register with the frame 603 * buffer driver layered underneath. It gets invoked from 604 * the underlying frame buffer driver to reconfigure the terminal 605 * emulator to a new screen size and depth in conjunction with 606 * framebuffer videomode changes. 607 * Here we keep the foreground/background color and attributes, 608 * which may be different with the initial settings, so that 609 * the color won't change while the framebuffer videomode changes. 610 * And we also reset the kernel terminal emulator and clear the 611 * whole screen. 612 */ 613 /* ARGSUSED */ 614 void 615 tems_modechange_callback(struct vis_modechg_arg *arg, 616 struct vis_devinit *devinit) 617 { 618 uchar_t diff; 619 struct tem_vt_state *p; 620 tem_modechg_cb_t cb; 621 tem_modechg_cb_arg_t cb_arg; 622 623 ASSERT(!(list_is_empty(&tems.ts_list))); 624 625 mutex_enter(&tems.ts_lock); 626 627 /* 628 * currently only for pixel mode 629 */ 630 diff = tems_check_videomode(devinit); 631 if (diff == 0) { 632 mutex_exit(&tems.ts_lock); 633 return; 634 } 635 636 diff = diff & TEMS_DIMENSION_DIFF; 637 638 if (diff == 0) { 639 /* 640 * Only need to reinit the active tem. 641 */ 642 struct tem_vt_state *active = tems.ts_active; 643 tems.ts_pdepth = devinit->depth; 644 645 mutex_enter(&active->tvs_lock); 646 ASSERT(active->tvs_isactive); 647 tem_reinit(active, B_TRUE); 648 mutex_exit(&active->tvs_lock); 649 650 mutex_exit(&tems.ts_lock); 651 return; 652 } 653 654 tems_setup_terminal(devinit, tems.ts_c_dimension.height, 655 tems.ts_c_dimension.width); 656 657 for (p = list_head(&tems.ts_list); p != NULL; 658 p = list_next(&tems.ts_list, p)) { 659 mutex_enter(&p->tvs_lock); 660 tem_reinit(p, p->tvs_isactive); 661 mutex_exit(&p->tvs_lock); 662 } 663 664 665 if (tems.ts_modechg_cb == NULL) { 666 mutex_exit(&tems.ts_lock); 667 return; 668 } 669 670 cb = tems.ts_modechg_cb; 671 cb_arg = tems.ts_modechg_arg; 672 673 /* 674 * Release the lock while doing callback. 675 */ 676 mutex_exit(&tems.ts_lock); 677 cb(cb_arg); 678 } 679 680 /* 681 * This function is used to display a rectangular blit of data 682 * of a given size and location via the underlying framebuffer driver. 683 * The blit can be as small as a pixel or as large as the screen. 684 */ 685 void 686 tems_display_layered( 687 struct vis_consdisplay *pda, 688 cred_t *credp) 689 { 690 int rval; 691 692 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSDISPLAY, 693 (intptr_t)pda, FKIOCTL, credp, &rval); 694 } 695 696 /* 697 * This function is used to invoke a block copy operation in the 698 * underlying framebuffer driver. Rectangle copies are how scrolling 699 * is implemented, as well as horizontal text shifting escape seqs. 700 * such as from vi when deleting characters and words. 701 */ 702 void 703 tems_copy_layered( 704 struct vis_conscopy *pma, 705 cred_t *credp) 706 { 707 int rval; 708 709 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCOPY, 710 (intptr_t)pma, FKIOCTL, credp, &rval); 711 } 712 713 /* 714 * This function is used to show or hide a rectangluar monochrom 715 * pixel inverting, text block cursor via the underlying framebuffer. 716 */ 717 void 718 tems_cursor_layered( 719 struct vis_conscursor *pca, 720 cred_t *credp) 721 { 722 int rval; 723 724 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCURSOR, 725 (intptr_t)pca, FKIOCTL, credp, &rval); 726 } 727 728 static void 729 tem_kdsetmode(int mode, cred_t *credp) 730 { 731 int rval; 732 733 (void) ldi_ioctl(tems.ts_hdl, KDSETMODE, 734 (intptr_t)mode, FKIOCTL, credp, &rval); 735 736 } 737 738 static void 739 tems_reset_colormap(cred_t *credp, enum called_from called_from) 740 { 741 struct vis_cmap cm; 742 int rval; 743 744 if (called_from == CALLED_FROM_STANDALONE) 745 return; 746 747 switch (tems.ts_pdepth) { 748 case 8: 749 cm.index = 0; 750 cm.count = 16; 751 cm.red = cmap4_to_24.red; /* 8-bits (1/3 of TrueColor 24) */ 752 cm.blue = cmap4_to_24.blue; /* 8-bits (1/3 of TrueColor 24) */ 753 cm.green = cmap4_to_24.green; /* 8-bits (1/3 of TrueColor 24) */ 754 (void) ldi_ioctl(tems.ts_hdl, VIS_PUTCMAP, (intptr_t)&cm, 755 FKIOCTL, credp, &rval); 756 break; 757 } 758 } 759 760 void 761 tem_get_size(ushort_t *r, ushort_t *c, ushort_t *x, ushort_t *y) 762 { 763 mutex_enter(&tems.ts_lock); 764 *r = (ushort_t)tems.ts_c_dimension.height; 765 *c = (ushort_t)tems.ts_c_dimension.width; 766 *x = (ushort_t)tems.ts_p_dimension.width; 767 *y = (ushort_t)tems.ts_p_dimension.height; 768 mutex_exit(&tems.ts_lock); 769 } 770 771 void 772 tem_register_modechg_cb(tem_modechg_cb_t func, tem_modechg_cb_arg_t arg) 773 { 774 mutex_enter(&tems.ts_lock); 775 776 tems.ts_modechg_cb = func; 777 tems.ts_modechg_arg = arg; 778 779 mutex_exit(&tems.ts_lock); 780 } 781 782 /* 783 * This function is to scroll up the OBP output, which has 784 * different screen height and width with our kernel console. 785 */ 786 static void 787 tem_prom_scroll_up(struct tem_vt_state *tem, int nrows, cred_t *credp, 788 enum called_from called_from) 789 { 790 struct vis_conscopy ma; 791 int ncols, width; 792 793 /* copy */ 794 ma.s_row = nrows * tems.ts_font.height; 795 ma.e_row = tems.ts_p_dimension.height - 1; 796 ma.t_row = 0; 797 798 ma.s_col = 0; 799 ma.e_col = tems.ts_p_dimension.width - 1; 800 ma.t_col = 0; 801 802 tems_safe_copy(&ma, credp, called_from); 803 804 /* clear */ 805 width = tems.ts_font.width; 806 ncols = (tems.ts_p_dimension.width + (width - 1))/ width; 807 808 tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y, 809 0, ncols, 0, B_TRUE, credp, called_from); 810 } 811 812 #define PROM_DEFAULT_FONT_HEIGHT 22 813 #define PROM_DEFAULT_WINDOW_TOP 0x8a 814 815 /* 816 * This function is to compute the starting row of the console, according to 817 * PROM cursor's position. Here we have to take different fonts into account. 818 */ 819 static int 820 tem_adjust_row(struct tem_vt_state *tem, int prom_row, cred_t *credp, 821 enum called_from called_from) 822 { 823 int tem_row; 824 int tem_y; 825 int prom_charheight = 0; 826 int prom_window_top = 0; 827 int scroll_up_lines; 828 829 plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top); 830 if (prom_charheight == 0) 831 prom_charheight = PROM_DEFAULT_FONT_HEIGHT; 832 if (prom_window_top == 0) 833 prom_window_top = PROM_DEFAULT_WINDOW_TOP; 834 835 tem_y = (prom_row + 1) * prom_charheight + prom_window_top - 836 tems.ts_p_offset.y; 837 tem_row = (tem_y + tems.ts_font.height - 1) / 838 tems.ts_font.height - 1; 839 840 if (tem_row < 0) { 841 tem_row = 0; 842 } else if (tem_row >= (tems.ts_c_dimension.height - 1)) { 843 /* 844 * Scroll up the prom outputs if the PROM cursor's position is 845 * below our tem's lower boundary. 846 */ 847 scroll_up_lines = tem_row - 848 (tems.ts_c_dimension.height - 1); 849 tem_prom_scroll_up(tem, scroll_up_lines, credp, called_from); 850 tem_row = tems.ts_c_dimension.height - 1; 851 } 852 853 return (tem_row); 854 } 855 856 void 857 tem_pix_align(struct tem_vt_state *tem, cred_t *credp, 858 enum called_from called_from) 859 { 860 uint32_t row = 0; 861 uint32_t col = 0; 862 863 if (plat_stdout_is_framebuffer()) { 864 plat_tem_hide_prom_cursor(); 865 866 /* 867 * We are getting the current cursor position in pixel 868 * mode so that we don't over-write the console output 869 * during boot. 870 */ 871 plat_tem_get_prom_pos(&row, &col); 872 873 /* 874 * Adjust the row if necessary when the font of our 875 * kernel console tem is different with that of prom 876 * tem. 877 */ 878 row = tem_adjust_row(tem, row, credp, called_from); 879 880 /* first line of our kernel console output */ 881 tem->tvs_first_line = row + 1; 882 883 /* re-set and align cusror position */ 884 tem->tvs_s_cursor.row = tem->tvs_c_cursor.row = 885 (screen_pos_t)row; 886 tem->tvs_s_cursor.col = tem->tvs_c_cursor.col = 0; 887 } else { 888 tem_safe_reset_display(tem, credp, called_from, B_TRUE, B_TRUE); 889 } 890 } 891 892 static void 893 tems_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen) 894 { 895 int i_inverse = 0; 896 int i_inverse_screen = 0; 897 898 plat_tem_get_inverses(&i_inverse, &i_inverse_screen); 899 900 *p_inverse = (i_inverse == 0) ? B_FALSE : B_TRUE; 901 *p_inverse_screen = (i_inverse_screen == 0) ? B_FALSE : B_TRUE; 902 } 903 904 /* 905 * Get the foreground/background color and attributes from the initial 906 * PROM, so that our kernel console can keep the same visual behaviour. 907 */ 908 static void 909 tems_get_initial_color(tem_color_t *pcolor) 910 { 911 boolean_t inverse, inverse_screen; 912 unsigned short flags = 0; 913 914 pcolor->fg_color = DEFAULT_ANSI_FOREGROUND; 915 pcolor->bg_color = DEFAULT_ANSI_BACKGROUND; 916 917 if (plat_stdout_is_framebuffer()) { 918 tems_get_inverses(&inverse, &inverse_screen); 919 if (inverse) 920 flags |= TEM_ATTR_REVERSE; 921 if (inverse_screen) 922 flags |= TEM_ATTR_SCREEN_REVERSE; 923 924 if (flags != 0) { 925 /* 926 * If either reverse flag is set, the screen is in 927 * white-on-black mode. We set the bold flag to 928 * improve readability. 929 */ 930 flags |= TEM_ATTR_BOLD; 931 } else { 932 /* 933 * Otherwise, the screen is in black-on-white mode. 934 * The SPARC PROM console, which starts in this mode, 935 * uses the bright white background colour so we 936 * match it here. 937 */ 938 if (pcolor->bg_color == ANSI_COLOR_WHITE) 939 flags |= TEM_ATTR_BRIGHT_BG; 940 } 941 } 942 943 pcolor->a_flags = flags; 944 } 945 946 uchar_t 947 tem_get_fbmode(tem_vt_state_t tem_arg) 948 { 949 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 950 951 uchar_t fbmode; 952 953 mutex_enter(&tem->tvs_lock); 954 fbmode = tem->tvs_fbmode; 955 mutex_exit(&tem->tvs_lock); 956 957 return (fbmode); 958 } 959 960 void 961 tem_set_fbmode(tem_vt_state_t tem_arg, uchar_t fbmode, cred_t *credp) 962 { 963 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 964 965 mutex_enter(&tems.ts_lock); 966 mutex_enter(&tem->tvs_lock); 967 968 if (fbmode == tem->tvs_fbmode) { 969 mutex_exit(&tem->tvs_lock); 970 mutex_exit(&tems.ts_lock); 971 return; 972 } 973 974 tem->tvs_fbmode = fbmode; 975 976 if (tem->tvs_isactive) { 977 tem_kdsetmode(tem->tvs_fbmode, credp); 978 if (fbmode == KD_TEXT) 979 tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL); 980 } 981 982 mutex_exit(&tem->tvs_lock); 983 mutex_exit(&tems.ts_lock); 984 } 985 986 void 987 tem_activate(tem_vt_state_t tem_arg, boolean_t unblank, cred_t *credp) 988 { 989 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg; 990 991 mutex_enter(&tems.ts_lock); 992 tems.ts_active = tem; 993 994 mutex_enter(&tem->tvs_lock); 995 tem->tvs_isactive = B_TRUE; 996 997 tem_kdsetmode(tem->tvs_fbmode, credp); 998 999 if (unblank) 1000 tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL); 1001 1002 mutex_exit(&tem->tvs_lock); 1003 mutex_exit(&tems.ts_lock); 1004 } 1005 1006 void 1007 tem_switch(tem_vt_state_t tem_arg1, tem_vt_state_t tem_arg2, cred_t *credp) 1008 { 1009 struct tem_vt_state *cur = (struct tem_vt_state *)tem_arg1; 1010 struct tem_vt_state *tobe = (struct tem_vt_state *)tem_arg2; 1011 1012 mutex_enter(&tems.ts_lock); 1013 mutex_enter(&tobe->tvs_lock); 1014 mutex_enter(&cur->tvs_lock); 1015 1016 tems.ts_active = tobe; 1017 cur->tvs_isactive = B_FALSE; 1018 tobe->tvs_isactive = B_TRUE; 1019 1020 mutex_exit(&cur->tvs_lock); 1021 1022 if (cur->tvs_fbmode != tobe->tvs_fbmode) 1023 tem_kdsetmode(tobe->tvs_fbmode, credp); 1024 1025 if (tobe->tvs_fbmode == KD_TEXT) 1026 tem_safe_unblank_screen(tobe, credp, CALLED_FROM_NORMAL); 1027 1028 mutex_exit(&tobe->tvs_lock); 1029 mutex_exit(&tems.ts_lock); 1030 }