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