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