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 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * ANSI terminal emulator module; parse ANSI X3.64 escape sequences and 31 * the like. 32 */ 33 34 #include <sys/types.h> 35 #include <sys/file.h> 36 #include <sys/conf.h> 37 #include <sys/errno.h> 38 #include <sys/open.h> 39 #include <sys/cred.h> 40 #include <sys/kmem.h> 41 #include <sys/ascii.h> 42 #include <sys/consdev.h> 43 #include <sys/font.h> 44 #include <sys/fbio.h> 45 #include <sys/conf.h> 46 #include <sys/modctl.h> 47 #include <sys/strsubr.h> 48 #include <sys/stat.h> 49 #include <sys/visual_io.h> 50 #include <sys/mutex.h> 51 #include <sys/param.h> 52 #include <sys/debug.h> 53 #include <sys/cmn_err.h> 54 #include <sys/console.h> 55 #include <sys/ddi.h> 56 #include <sys/sunddi.h> 57 #include <sys/sunldi.h> 58 #include <sys/tem_impl.h> 59 #include <sys/tem.h> 60 #ifdef _HAVE_TEM_FIRMWARE 61 #include <sys/promif.h> 62 #endif /* _HAVE_TEM_FIRMWARE */ 63 #include <sys/consconfig_dacf.h> 64 65 /* Terminal emulator functions */ 66 static int tem_setup_terminal(struct vis_devinit *, tem_t *, 67 size_t, size_t); 68 static void tem_modechange_callback(tem_t *, struct vis_devinit *); 69 static void tem_free(tem_t *); 70 static void tem_get_inverses(boolean_t *, boolean_t *); 71 static void tem_get_initial_color(tem_t *); 72 static int tem_adjust_row(tem_t *, int, cred_t *); 73 74 /* 75 * Globals 76 */ 77 ldi_ident_t term_li = NULL; 78 79 80 extern struct mod_ops mod_miscops; 81 82 static struct modlmisc modlmisc = { 83 &mod_miscops, /* modops */ 84 "ANSI Terminal Emulator", /* name */ 85 }; 86 87 static struct modlinkage modlinkage = { 88 MODREV_1, (void *)&modlmisc, NULL 89 }; 90 91 int 92 _init(void) 93 { 94 int ret; 95 ret = mod_install(&modlinkage); 96 if (ret != 0) 97 return (ret); 98 ret = ldi_ident_from_mod(&modlinkage, &term_li); 99 if (ret != 0) { 100 (void) mod_remove(&modlinkage); 101 return (ret); 102 } 103 return (0); 104 } 105 106 int 107 _fini() 108 { 109 int ret; 110 111 ret = mod_remove(&modlinkage); 112 if (ret == 0) { 113 ldi_ident_release(term_li); 114 term_li = NULL; 115 } 116 return (ret); 117 } 118 119 int 120 _info(struct modinfo *modinfop) 121 { 122 return (mod_info(&modlinkage, modinfop)); 123 } 124 125 int 126 tem_fini(tem_t *tem) 127 { 128 int lyr_rval; 129 130 mutex_enter(&tem->lock); 131 132 ASSERT(tem->hdl != NULL); 133 134 /* 135 * Allow layered on driver to clean up console private 136 * data. 137 */ 138 (void) ldi_ioctl(tem->hdl, VIS_DEVFINI, 139 0, FKIOCTL, kcred, &lyr_rval); 140 141 /* 142 * Close layered on driver 143 */ 144 (void) ldi_close(tem->hdl, NULL, kcred); 145 tem->hdl = NULL; 146 147 mutex_exit(&tem->lock); 148 149 tem_free(tem); 150 151 return (0); 152 } 153 154 static int 155 tem_init_failed(tem_t *tem, cred_t *credp, boolean_t finish_ioctl) 156 { 157 int lyr_rval; 158 159 if (finish_ioctl) 160 (void) ldi_ioctl(tem->hdl, VIS_DEVFINI, 0, FWRITE|FKIOCTL, 161 credp, &lyr_rval); 162 163 (void) ldi_close(tem->hdl, NULL, credp); 164 tem_free(tem); 165 return (ENXIO); 166 } 167 168 static void 169 tem_free_state(struct tem_state *tems) 170 { 171 ASSERT(tems != NULL); 172 173 if (tems->a_outbuf != NULL) 174 kmem_free(tems->a_outbuf, 175 tems->a_c_dimension.width); 176 if (tems->a_blank_line != NULL) 177 kmem_free(tems->a_blank_line, 178 tems->a_c_dimension.width); 179 if (tems->a_pix_data != NULL) 180 kmem_free(tems->a_pix_data, 181 tems->a_pix_data_size); 182 kmem_free(tems, sizeof (struct tem_state)); 183 } 184 185 static void 186 tem_free(tem_t *tem) 187 { 188 ASSERT(tem != NULL); 189 190 if (tem->state != NULL) 191 tem_free_state(tem->state); 192 193 kmem_free(tem, sizeof (struct tem)); 194 } 195 196 /* 197 * This is the main entry point to the module. It handles output requests 198 * during normal system operation, when (e.g.) mutexes are available. 199 */ 200 void 201 tem_write(tem_t *tem, uchar_t *buf, ssize_t len, cred_t *credp) 202 { 203 mutex_enter(&tem->lock); 204 205 ASSERT(tem->hdl != NULL); 206 207 tem_check_first_time(tem, credp, CALLED_FROM_NORMAL); 208 tem_terminal_emulate(tem, buf, len, credp, CALLED_FROM_NORMAL); 209 210 mutex_exit(&tem->lock); 211 } 212 213 int 214 tem_init(tem_t **ptem, char *pathname, cred_t *credp) 215 { 216 struct vis_devinit devinit; 217 tem_t *tem; 218 size_t height = 0; 219 size_t width = 0; 220 uint32_t row = 0; 221 uint32_t col = 0; 222 char *pathbuf; 223 int err = 0; 224 int lyr_rval; 225 226 tem = kmem_zalloc(sizeof (struct tem), KM_SLEEP); 227 228 mutex_init(&tem->lock, (char *)NULL, MUTEX_DRIVER, NULL); 229 230 #ifdef _HAVE_TEM_FIRMWARE 231 tem->cons_wrtvec = tem_write; 232 #endif /* _HAVE_TEM_FIRMWARE */ 233 234 /* 235 * Open the layered device using the devfs physical device name 236 * after adding the /devices prefix. 237 */ 238 pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 239 (void) strcpy(pathbuf, "/devices"); 240 if (i_ddi_prompath_to_devfspath(pathname, 241 pathbuf + strlen("/devices")) != DDI_SUCCESS) { 242 cmn_err(CE_WARN, "terminal emulator: Path conversion error"); 243 kmem_free(pathbuf, MAXPATHLEN); 244 tem_free(tem); 245 return (ENXIO); 246 } 247 if (ldi_open_by_name(pathbuf, FWRITE, credp, &tem->hdl, term_li) != 0) { 248 cmn_err(CE_WARN, "terminal emulator: Device path open error"); 249 kmem_free(pathbuf, MAXPATHLEN); 250 tem_free(tem); 251 return (ENXIO); 252 } 253 kmem_free(pathbuf, MAXPATHLEN); 254 255 devinit.modechg_cb = (vis_modechg_cb_t)tem_modechange_callback; 256 devinit.modechg_arg = (struct vis_modechg_arg *)tem; 257 258 /* 259 * Initialize the console and get the device parameters 260 */ 261 if ((err = ldi_ioctl(tem->hdl, VIS_DEVINIT, 262 (intptr_t)&devinit, FWRITE|FKIOCTL, credp, &lyr_rval)) != 0) { 263 cmn_err(CE_WARN, "terminal emulator: Compatible fb not found"); 264 return (tem_init_failed(tem, credp, B_FALSE)); 265 } 266 267 /* Make sure the fb driver and terminal emulator versions match */ 268 if (devinit.version != VIS_CONS_REV) { 269 cmn_err(CE_WARN, 270 "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) " 271 "of console fb driver not supported", devinit.version); 272 return (tem_init_failed(tem, credp, B_TRUE)); 273 } 274 275 if ((tem->fb_polledio = devinit.polledio) == NULL) { 276 cmn_err(CE_WARN, "terminal emulator: fb doesn't support polled " 277 "I/O"); 278 return (tem_init_failed(tem, credp, B_TRUE)); 279 } 280 281 /* other sanity checks */ 282 if (!((devinit.depth == 4) || (devinit.depth == 8) || 283 (devinit.depth == 24) || (devinit.depth == 32))) { 284 cmn_err(CE_WARN, "terminal emulator: unsupported depth"); 285 return (tem_init_failed(tem, credp, B_TRUE)); 286 } 287 288 if ((devinit.mode != VIS_TEXT) && (devinit.mode != VIS_PIXEL)) { 289 cmn_err(CE_WARN, "terminal emulator: unsupported mode"); 290 return (tem_init_failed(tem, credp, B_TRUE)); 291 } 292 293 if ((devinit.mode == VIS_PIXEL) && plat_stdout_is_framebuffer()) { 294 plat_tem_get_prom_size(&height, &width); 295 } 296 297 /* 298 * Initialize the terminal emulator 299 */ 300 mutex_enter(&tem->lock); 301 if ((err = tem_setup_terminal(&devinit, tem, height, width)) != 0) { 302 cmn_err(CE_WARN, "terminal emulator: Init failed"); 303 (void) ldi_ioctl(tem->hdl, VIS_DEVFINI, 0, FWRITE|FKIOCTL, 304 credp, &lyr_rval); 305 (void) ldi_close(tem->hdl, NULL, credp); 306 mutex_exit(&tem->lock); 307 tem_free(tem); 308 return (err); 309 } 310 311 /* 312 * make our kernel console keep compatibility with OBP. 313 */ 314 tem_get_initial_color(tem); 315 316 /* 317 * On SPARC don't clear the screen if the console is the framebuffer. 318 * Otherwise it needs to be cleared to get rid of junk that may be 319 * in frameuffer memory, since the screen isn't cleared when 320 * boot messages are directed elsewhere. 321 */ 322 if (devinit.mode == VIS_TEXT) { 323 /* 324 * The old getting current cursor position code, which 325 * is not needed here, has been in tem_write/tem_polled_write. 326 */ 327 tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 0, NULL); 328 } else if (plat_stdout_is_framebuffer()) { 329 ASSERT(devinit.mode == VIS_PIXEL); 330 plat_tem_hide_prom_cursor(); 331 tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 0, NULL); 332 333 /* 334 * We are getting the current cursor position in pixel 335 * mode so that we don't over-write the console output 336 * during boot. 337 */ 338 plat_tem_get_prom_pos(&row, &col); 339 340 /* 341 * Adjust the row if necessary when the font of our 342 * kernel console tem is different with that of prom 343 * tem. 344 */ 345 row = tem_adjust_row(tem, row, credp); 346 347 /* first line of our kernel console output */ 348 tem->state->first_line = row + 1; 349 350 /* re-set and align cusror position */ 351 tem->state->a_c_cursor.row = row; 352 tem->state->a_c_cursor.col = 0; 353 tem_align_cursor(tem); 354 } else { 355 tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 1, NULL); 356 } 357 358 #ifdef _HAVE_TEM_FIRMWARE 359 if (plat_stdout_is_framebuffer()) { 360 /* 361 * Drivers in the console stream may emit additional 362 * messages before we are ready. This causes text 363 * overwrite on the screen. So we set the redirection 364 * here. It is safe because the ioctl in consconfig_dacf 365 * will succeed and consmode will be set to CONS_KFB. 366 */ 367 prom_set_stdout_redirect(console_prom_write_cb, 368 (promif_redir_arg_t)tem); 369 370 } 371 #endif /* _HAVE_TEM_FIRMWARE */ 372 373 mutex_exit(&tem->lock); 374 *ptem = tem; /* Return tem to caller only upon success */ 375 return (0); 376 } 377 378 /* 379 * This is a callback function that we register with the frame 380 * buffer driver layered underneath. It gets invoked from 381 * the underlying frame buffer driver to reconfigure the terminal 382 * emulator to a new screen size and depth in conjunction with 383 * framebuffer videomode changes. 384 * Here we keep the foreground/background color and attributes, 385 * which may be different with the initial settings, so that 386 * the color won't change while the framebuffer videomode changes. 387 * And we also reset the kernel terminal emulator and clear the 388 * whole screen. 389 */ 390 void 391 tem_modechange_callback(tem_t *tem, struct vis_devinit *devinit) 392 { 393 tem_color_t tc; 394 395 mutex_enter(&tem->lock); 396 397 ASSERT(tem->hdl != NULL); 398 399 tc.fg_color = tem->state->fg_color; 400 tc.bg_color = tem->state->bg_color; 401 tc.a_flags = tem->state->a_flags; 402 403 (void) tem_setup_terminal(devinit, tem, 404 tem->state->a_c_dimension.height, 405 tem->state->a_c_dimension.width); 406 407 tem_reset_display(tem, kcred, CALLED_FROM_NORMAL, 1, &tc); 408 409 mutex_exit(&tem->lock); 410 411 if (tem->modechg_cb != NULL) 412 tem->modechg_cb(tem->modechg_arg); 413 } 414 415 static int 416 tem_setup_terminal( 417 struct vis_devinit *devinit, 418 tem_t *tem, 419 size_t height, size_t width) 420 { 421 int i; 422 struct tem_state *new_state, *prev_state; 423 424 ASSERT(MUTEX_HELD(&tem->lock)); 425 426 prev_state = tem->state; 427 428 new_state = kmem_zalloc(sizeof (struct tem_state), KM_SLEEP); 429 430 new_state->a_pdepth = devinit->depth; 431 new_state->display_mode = devinit->mode; 432 new_state->linebytes = devinit->linebytes; 433 434 switch (devinit->mode) { 435 case VIS_TEXT: 436 new_state->a_p_dimension.width = 0; 437 new_state->a_p_dimension.height = 0; 438 new_state->a_c_dimension.width = devinit->width; 439 new_state->a_c_dimension.height = devinit->height; 440 441 new_state->in_fp.f_display = tem_text_display; 442 new_state->in_fp.f_copy = tem_text_copy; 443 new_state->in_fp.f_cursor = tem_text_cursor; 444 new_state->in_fp.f_cls = tem_text_cls; 445 new_state->in_fp.f_bit2pix = NULL; 446 447 new_state->a_blank_line = 448 kmem_alloc(new_state->a_c_dimension.width, KM_SLEEP); 449 450 for (i = 0; i < new_state->a_c_dimension.width; i++) 451 new_state->a_blank_line[i] = ' '; 452 453 break; 454 case VIS_PIXEL: 455 456 /* 457 * First check to see if the user has specified a screen size. 458 * If so, use those values. Else use 34x80 as the default. 459 */ 460 if (width == 0) { 461 width = TEM_DEFAULT_COLS; 462 height = TEM_DEFAULT_ROWS; 463 } 464 new_state->a_c_dimension.height = height; 465 new_state->a_c_dimension.width = width; 466 467 new_state->a_p_dimension.height = devinit->height; 468 new_state->a_p_dimension.width = devinit->width; 469 470 new_state->in_fp.f_display = tem_pix_display; 471 new_state->in_fp.f_copy = tem_pix_copy; 472 new_state->in_fp.f_cursor = tem_pix_cursor; 473 new_state->in_fp.f_cls = tem_pix_cls; 474 475 new_state->a_blank_line = NULL; 476 477 /* 478 * set_font() will select a appropriate sized font for 479 * the number of rows and columns selected. If we don't 480 * have a font that will fit, then it will use the 481 * default builtin font and adjust the rows and columns 482 * to fit on the screen. 483 */ 484 set_font(&new_state->a_font, 485 &new_state->a_c_dimension.height, 486 &new_state->a_c_dimension.width, 487 new_state->a_p_dimension.height, 488 new_state->a_p_dimension.width); 489 490 new_state->a_p_offset.y = 491 (new_state->a_p_dimension.height - 492 (new_state->a_c_dimension.height * 493 new_state->a_font.height)) / 2; 494 495 new_state->a_p_offset.x = 496 (new_state->a_p_dimension.width - 497 (new_state->a_c_dimension.width * 498 new_state->a_font.width)) / 2; 499 500 switch (devinit->depth) { 501 case 4: 502 new_state->in_fp.f_bit2pix = bit_to_pix4; 503 new_state->a_pix_data_size = 504 (((new_state->a_font.width * 4) + 505 NBBY - 1) / NBBY) * new_state->a_font.height; 506 break; 507 case 8: 508 new_state->in_fp.f_bit2pix = bit_to_pix8; 509 new_state->a_pix_data_size = 510 new_state->a_font.width * 511 new_state->a_font.height; 512 break; 513 case 24: 514 case 32: 515 new_state->in_fp.f_bit2pix = bit_to_pix24; 516 new_state->a_pix_data_size = 517 new_state->a_font.width * 518 new_state->a_font.height; 519 new_state->a_pix_data_size *= 4; 520 break; 521 } 522 523 new_state->a_pix_data = 524 kmem_alloc(new_state->a_pix_data_size, KM_SLEEP); 525 526 break; 527 528 default: 529 /* 530 * The layered fb driver conveyed an unrecognized rendering 531 * mode. We cannot proceed with tem initialization. 532 */ 533 kmem_free(new_state, sizeof (struct tem_state)); 534 return (ENXIO); 535 } 536 537 new_state->a_outbuf = 538 kmem_alloc(new_state->a_c_dimension.width, KM_SLEEP); 539 540 /* 541 * Change state atomically so that polled I/O requests 542 * can be safely and reliably serviced anytime after the terminal 543 * emulator is originally initialized and the console mode has been 544 * switched over from the PROM, even while a videomode change 545 * callback is being processed. 546 */ 547 tem->state = new_state; 548 549 if (prev_state != NULL) 550 tem_free_state(prev_state); 551 552 return (0); 553 } 554 555 /* 556 * This function is used to display a rectangular blit of data 557 * of a given size and location via the underlying framebuffer driver. 558 * The blit can be as small as a pixel or as large as the screen. 559 */ 560 void 561 tem_display_layered( 562 tem_t *tem, 563 struct vis_consdisplay *pda, 564 cred_t *credp) 565 { 566 int rval; 567 568 (void) ldi_ioctl(tem->hdl, VIS_CONSDISPLAY, 569 (intptr_t)pda, FKIOCTL, credp, &rval); 570 } 571 572 /* 573 * This function is used to invoke a block copy operation in the 574 * underlying framebuffer driver. Rectangle copies are how scrolling 575 * is implemented, as well as horizontal text shifting escape seqs. 576 * such as from vi when deleting characters and words. 577 */ 578 void 579 tem_copy_layered( 580 tem_t *tem, 581 struct vis_conscopy *pma, 582 cred_t *credp) 583 { 584 int rval; 585 586 (void) ldi_ioctl(tem->hdl, VIS_CONSCOPY, 587 (intptr_t)pma, FKIOCTL, credp, &rval); 588 } 589 590 /* 591 * This function is used to show or hide a rectangluar monochrom 592 * pixel inverting, text block cursor via the underlying framebuffer. 593 */ 594 void 595 tem_cursor_layered( 596 tem_t *tem, 597 struct vis_conscursor *pca, 598 cred_t *credp) 599 { 600 int rval; 601 602 (void) ldi_ioctl(tem->hdl, VIS_CONSCURSOR, 603 (intptr_t)pca, FKIOCTL, credp, &rval); 604 } 605 606 void 607 tem_reset_colormap( 608 tem_t *tem, 609 cred_t *credp, 610 enum called_from called_from) 611 { 612 struct vis_cmap cm; 613 int rval; 614 615 if (called_from == CALLED_FROM_STANDALONE) 616 return; 617 618 switch (tem->state->a_pdepth) { 619 case 8: 620 cm.index = 0; 621 cm.count = 16; 622 cm.red = cmap4_to_24.red; /* 8-bits (1/3 of TrueColor 24) */ 623 cm.blue = cmap4_to_24.blue; /* 8-bits (1/3 of TrueColor 24) */ 624 cm.green = cmap4_to_24.green; /* 8-bits (1/3 of TrueColor 24) */ 625 (void) ldi_ioctl(tem->hdl, VIS_PUTCMAP, (intptr_t)&cm, 626 FKIOCTL, credp, &rval); 627 break; 628 } 629 } 630 631 void 632 tem_get_size(tem_t *tem, ushort_t *r, ushort_t *c, 633 ushort_t *x, ushort_t *y) 634 { 635 *r = (ushort_t)tem->state->a_c_dimension.height; 636 *c = (ushort_t)tem->state->a_c_dimension.width; 637 *x = (ushort_t)tem->state->a_p_dimension.width; 638 *y = (ushort_t)tem->state->a_p_dimension.height; 639 } 640 641 void 642 tem_register_modechg_cb(tem_t *tem, tem_modechg_cb_t func, 643 tem_modechg_cb_arg_t arg) 644 { 645 tem->modechg_cb = func; 646 tem->modechg_arg = arg; 647 } 648 649 /* 650 * This function is to scroll up the OBP output, which has 651 * different screen height and width with our kernel console. 652 */ 653 static void 654 tem_prom_scroll_up(struct tem *tem, int nrows, cred_t *credp) 655 { 656 struct tem_state *tems = tem->state; 657 struct vis_conscopy ma; 658 int ncols, width; 659 660 /* copy */ 661 ma.s_row = nrows * tems->a_font.height; 662 ma.e_row = tems->a_p_dimension.height - 1; 663 ma.t_row = 0; 664 665 ma.s_col = 0; 666 ma.e_col = tems->a_p_dimension.width - 1; 667 ma.t_col = 0; 668 669 tem_copy(tem, &ma, credp, CALLED_FROM_NORMAL); 670 671 /* clear */ 672 width = tems->a_font.width; 673 ncols = (tems->a_p_dimension.width + 674 (width - 1))/ width; 675 676 tem_pix_cls_range(tem, 677 0, nrows, tems->a_p_offset.y, 678 0, ncols, 0, 679 B_TRUE, credp, CALLED_FROM_NORMAL); 680 } 681 682 #define PROM_DEFAULT_FONT_HEIGHT 22 683 #define PROM_DEFAULT_WINDOW_TOP 0x8a 684 685 /* 686 * This function is to compute the starting row of the console, according to 687 * PROM cursor's position. Here we have to take different fonts into account. 688 */ 689 static int 690 tem_adjust_row(tem_t *tem, int prom_row, cred_t *credp) 691 { 692 int tem_row; 693 int tem_y; 694 int prom_charheight = 0; 695 int prom_window_top = 0; 696 int scroll_up_lines; 697 698 plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top); 699 if (prom_charheight == 0) 700 prom_charheight = PROM_DEFAULT_FONT_HEIGHT; 701 if (prom_window_top == 0) 702 prom_window_top = PROM_DEFAULT_WINDOW_TOP; 703 704 tem_y = (prom_row + 1) * prom_charheight + prom_window_top - 705 tem->state->a_p_offset.y; 706 tem_row = (tem_y + tem->state->a_font.height - 1) / 707 tem->state->a_font.height - 1; 708 709 if (tem_row < 0) { 710 tem_row = 0; 711 } else if (tem_row >= (tem->state->a_c_dimension.height - 1)) { 712 /* 713 * Scroll up the prom outputs if the PROM cursor's position is 714 * below our tem's lower boundary. 715 */ 716 scroll_up_lines = tem_row - 717 (tem->state->a_c_dimension.height - 1); 718 tem_prom_scroll_up(tem, scroll_up_lines, credp); 719 tem_row = tem->state->a_c_dimension.height - 1; 720 } 721 722 return (tem_row); 723 } 724 725 static void 726 tem_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen) 727 { 728 int i_inverse = 0; 729 int i_inverse_screen = 0; 730 731 plat_tem_get_inverses(&i_inverse, &i_inverse_screen); 732 733 *p_inverse = (i_inverse == 0) ? B_FALSE : B_TRUE; 734 *p_inverse_screen = (i_inverse_screen == 0) ? B_FALSE : B_TRUE; 735 } 736 737 /* 738 * Get the foreground/background color and attributes from the initial 739 * PROM, so that our kernel console can keep the same visual behaviour. 740 */ 741 static void 742 tem_get_initial_color(tem_t *tem) 743 { 744 boolean_t inverse, inverse_screen; 745 unsigned short flags = 0; 746 747 tem->init_color.fg_color = DEFAULT_ANSI_FOREGROUND; 748 tem->init_color.bg_color = DEFAULT_ANSI_BACKGROUND; 749 750 if (plat_stdout_is_framebuffer()) { 751 tem_get_inverses(&inverse, &inverse_screen); 752 if (inverse) 753 flags |= TEM_ATTR_REVERSE; 754 if (inverse_screen) 755 flags |= TEM_ATTR_SCREEN_REVERSE; 756 if (flags != 0) 757 flags |= TEM_ATTR_BOLD; 758 } 759 760 tem->init_color.a_flags = flags; 761 } 762