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 2006 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/cred_impl.h> 41 #include <sys/kmem.h> 42 #include <sys/ascii.h> 43 #include <sys/consdev.h> 44 #include <sys/font.h> 45 #include <sys/fbio.h> 46 #include <sys/conf.h> 47 #include <sys/modctl.h> 48 #include <sys/strsubr.h> 49 #include <sys/stat.h> 50 #include <sys/visual_io.h> 51 #include <sys/mutex.h> 52 #include <sys/param.h> 53 #include <sys/debug.h> 54 #include <sys/cmn_err.h> 55 #include <sys/console.h> 56 #include <sys/ddi.h> 57 #include <sys/sunddi.h> 58 #include <sys/sunldi.h> 59 #include <sys/tem_impl.h> 60 #include <sys/tem.h> 61 #ifdef _HAVE_TEM_FIRMWARE 62 #include <sys/promif.h> 63 #endif /* _HAVE_TEM_FIRMWARE */ 64 #include <sys/consconfig_dacf.h> 65 66 /* Terminal emulator functions */ 67 static int tem_setup_terminal(struct vis_devinit *, tem_t *, 68 size_t, size_t); 69 static void tem_modechange_callback(tem_t *, struct vis_devinit *); 70 static void tem_free(tem_t *); 71 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 * On SPARC don't clear the screen if the console is the framebuffer. 313 * Otherwise it needs to be cleared to get rid of junk that may be 314 * in frameuffer memory, since the screen isn't cleared when 315 * boot messages are directed elsewhere. 316 */ 317 if (devinit.mode == VIS_TEXT) { 318 /* 319 * The old getting current cursor position code, which 320 * is not needed here, has been in tem_write/tem_polled_write. 321 */ 322 tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 0); 323 } else if (plat_stdout_is_framebuffer()) { 324 ASSERT(devinit.mode == VIS_PIXEL); 325 plat_tem_hide_prom_cursor(); 326 tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 0); 327 328 /* 329 * We are getting the current cursor position in pixel 330 * mode so that we don't over-write the console output 331 * during boot. 332 */ 333 plat_tem_get_prom_pos(&row, &col); 334 335 /* 336 * Adjust the row if necessary when the font of our 337 * kernel console tem is different with that of prom 338 * tem. 339 */ 340 row = tem_adjust_row(tem, row, credp); 341 342 /* first line of our kernel console output */ 343 tem->state->first_line = row + 1; 344 345 /* re-set and align cusror position */ 346 tem->state->a_c_cursor.row = row; 347 tem->state->a_c_cursor.col = 0; 348 tem_align_cursor(tem); 349 } else { 350 tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 1); 351 } 352 353 #ifdef _HAVE_TEM_FIRMWARE 354 if (plat_stdout_is_framebuffer()) { 355 /* 356 * Drivers in the console stream may emit additional 357 * messages before we are ready. This causes text 358 * overwrite on the screen. So we set the redirection 359 * here. It is safe because the ioctl in consconfig_dacf 360 * will succeed and consmode will be set to CONS_KFB. 361 */ 362 prom_set_stdout_redirect(console_prom_write_cb, 363 (promif_redir_arg_t)tem); 364 365 } 366 #endif /* _HAVE_TEM_FIRMWARE */ 367 368 mutex_exit(&tem->lock); 369 *ptem = tem; /* Return tem to caller only upon success */ 370 return (0); 371 } 372 373 /* 374 * This is a callback function that we register with the frame 375 * buffer driver layered underneath. It gets invoked from 376 * the underlying frame buffer driver to reconfigure the terminal 377 * emulator to a new screen size and depth in conjunction with 378 * framebuffer videomode changes. 379 */ 380 void 381 tem_modechange_callback(tem_t *tem, struct vis_devinit *devinit) 382 { 383 mutex_enter(&tem->lock); 384 385 ASSERT(tem->hdl != NULL); 386 387 (void) tem_setup_terminal(devinit, tem, 388 tem->state->a_c_dimension.height, 389 tem->state->a_c_dimension.width); 390 391 tem_reset_display(tem, kcred, CALLED_FROM_NORMAL, 1); 392 393 mutex_exit(&tem->lock); 394 395 if (tem->modechg_cb != NULL) 396 tem->modechg_cb(tem->modechg_arg); 397 } 398 399 static int 400 tem_setup_terminal( 401 struct vis_devinit *devinit, 402 tem_t *tem, 403 size_t height, size_t width) 404 { 405 int i; 406 struct tem_state *new_state, *prev_state; 407 408 ASSERT(MUTEX_HELD(&tem->lock)); 409 410 prev_state = tem->state; 411 412 new_state = kmem_zalloc(sizeof (struct tem_state), KM_SLEEP); 413 414 new_state->a_pdepth = devinit->depth; 415 new_state->display_mode = devinit->mode; 416 new_state->linebytes = devinit->linebytes; 417 418 switch (devinit->mode) { 419 case VIS_TEXT: 420 new_state->a_p_dimension.width = 0; 421 new_state->a_p_dimension.height = 0; 422 new_state->a_c_dimension.width = devinit->width; 423 new_state->a_c_dimension.height = devinit->height; 424 425 new_state->in_fp.f_display = tem_text_display; 426 new_state->in_fp.f_copy = tem_text_copy; 427 new_state->in_fp.f_cursor = tem_text_cursor; 428 new_state->in_fp.f_cls = tem_text_cls; 429 new_state->in_fp.f_bit2pix = NULL; 430 431 new_state->a_blank_line = 432 kmem_alloc(new_state->a_c_dimension.width, KM_SLEEP); 433 434 for (i = 0; i < new_state->a_c_dimension.width; i++) 435 new_state->a_blank_line[i] = ' '; 436 437 break; 438 case VIS_PIXEL: 439 440 /* 441 * First check to see if the user has specified a screen size. 442 * If so, use those values. Else use 34x80 as the default. 443 */ 444 if (width == 0) { 445 width = TEM_DEFAULT_COLS; 446 height = TEM_DEFAULT_ROWS; 447 } 448 new_state->a_c_dimension.height = height; 449 new_state->a_c_dimension.width = width; 450 451 new_state->a_p_dimension.height = devinit->height; 452 new_state->a_p_dimension.width = devinit->width; 453 454 new_state->in_fp.f_display = tem_pix_display; 455 new_state->in_fp.f_copy = tem_pix_copy; 456 new_state->in_fp.f_cursor = tem_pix_cursor; 457 new_state->in_fp.f_cls = tem_pix_cls; 458 459 new_state->a_blank_line = NULL; 460 461 /* 462 * set_font() will select a appropriate sized font for 463 * the number of rows and columns selected. If we don't 464 * have a font that will fit, then it will use the 465 * default builtin font and adjust the rows and columns 466 * to fit on the screen. 467 */ 468 set_font(&new_state->a_font, 469 &new_state->a_c_dimension.height, 470 &new_state->a_c_dimension.width, 471 new_state->a_p_dimension.height, 472 new_state->a_p_dimension.width); 473 474 new_state->a_p_offset.y = 475 (new_state->a_p_dimension.height - 476 (new_state->a_c_dimension.height * 477 new_state->a_font.height)) / 2; 478 479 new_state->a_p_offset.x = 480 (new_state->a_p_dimension.width - 481 (new_state->a_c_dimension.width * 482 new_state->a_font.width)) / 2; 483 484 switch (devinit->depth) { 485 case 4: 486 new_state->in_fp.f_bit2pix = bit_to_pix4; 487 new_state->a_pix_data_size = 488 (((new_state->a_font.width * 4) + 489 NBBY - 1) / NBBY) * new_state->a_font.height; 490 break; 491 case 8: 492 new_state->in_fp.f_bit2pix = bit_to_pix8; 493 new_state->a_pix_data_size = 494 new_state->a_font.width * 495 new_state->a_font.height; 496 break; 497 case 24: 498 case 32: 499 new_state->in_fp.f_bit2pix = bit_to_pix24; 500 new_state->a_pix_data_size = 501 new_state->a_font.width * 502 new_state->a_font.height; 503 new_state->a_pix_data_size *= 4; 504 break; 505 } 506 507 new_state->a_pix_data = 508 kmem_alloc(new_state->a_pix_data_size, KM_SLEEP); 509 510 break; 511 512 default: 513 /* 514 * The layered fb driver conveyed an unrecognized rendering 515 * mode. We cannot proceed with tem initialization. 516 */ 517 kmem_free(new_state, sizeof (struct tem_state)); 518 return (ENXIO); 519 } 520 521 new_state->a_outbuf = 522 kmem_alloc(new_state->a_c_dimension.width, KM_SLEEP); 523 524 /* 525 * Change state atomically so that polled I/O requests 526 * can be safely and reliably serviced anytime after the terminal 527 * emulator is originally initialized and the console mode has been 528 * switched over from the PROM, even while a videomode change 529 * callback is being processed. 530 */ 531 tem->state = new_state; 532 533 if (prev_state != NULL) 534 tem_free_state(prev_state); 535 536 return (0); 537 } 538 539 /* 540 * This function is used to display a rectangular blit of data 541 * of a given size and location via the underlying framebuffer driver. 542 * The blit can be as small as a pixel or as large as the screen. 543 */ 544 void 545 tem_display_layered( 546 tem_t *tem, 547 struct vis_consdisplay *pda, 548 cred_t *credp) 549 { 550 int rval; 551 552 (void) ldi_ioctl(tem->hdl, VIS_CONSDISPLAY, 553 (intptr_t)pda, FKIOCTL, credp, &rval); 554 } 555 556 /* 557 * This function is used to invoke a block copy operation in the 558 * underlying framebuffer driver. Rectangle copies are how scrolling 559 * is implemented, as well as horizontal text shifting escape seqs. 560 * such as from vi when deleting characters and words. 561 */ 562 void 563 tem_copy_layered( 564 tem_t *tem, 565 struct vis_conscopy *pma, 566 cred_t *credp) 567 { 568 int rval; 569 570 (void) ldi_ioctl(tem->hdl, VIS_CONSCOPY, 571 (intptr_t)pma, FKIOCTL, credp, &rval); 572 } 573 574 /* 575 * This function is used to show or hide a rectangluar monochrom 576 * pixel inverting, text block cursor via the underlying framebuffer. 577 */ 578 void 579 tem_cursor_layered( 580 tem_t *tem, 581 struct vis_conscursor *pca, 582 cred_t *credp) 583 { 584 int rval; 585 586 (void) ldi_ioctl(tem->hdl, VIS_CONSCURSOR, 587 (intptr_t)pca, FKIOCTL, credp, &rval); 588 } 589 590 void 591 tem_reset_colormap( 592 tem_t *tem, 593 cred_t *credp, 594 enum called_from called_from) 595 { 596 struct vis_cmap cm; 597 int rval; 598 599 if (called_from == CALLED_FROM_STANDALONE) 600 return; 601 602 switch (tem->state->a_pdepth) { 603 case 8: 604 cm.index = 0; 605 cm.count = 16; 606 cm.red = cmap4_to_24.red; /* 8-bits (1/3 of TrueColor 24) */ 607 cm.blue = cmap4_to_24.blue; /* 8-bits (1/3 of TrueColor 24) */ 608 cm.green = cmap4_to_24.green; /* 8-bits (1/3 of TrueColor 24) */ 609 (void) ldi_ioctl(tem->hdl, VIS_PUTCMAP, (intptr_t)&cm, 610 FKIOCTL, credp, &rval); 611 break; 612 } 613 } 614 615 void 616 tem_get_size(tem_t *tem, ushort_t *r, ushort_t *c, 617 ushort_t *x, ushort_t *y) 618 { 619 *r = (ushort_t)tem->state->a_c_dimension.height; 620 *c = (ushort_t)tem->state->a_c_dimension.width; 621 *x = (ushort_t)tem->state->a_p_dimension.width; 622 *y = (ushort_t)tem->state->a_p_dimension.height; 623 } 624 625 void 626 tem_register_modechg_cb(tem_t *tem, tem_modechg_cb_t func, 627 tem_modechg_cb_arg_t arg) 628 { 629 tem->modechg_cb = func; 630 tem->modechg_arg = arg; 631 } 632 633 /* 634 * This function is to scroll up the OBP output, which has 635 * different screen height and width with our kernel console. 636 */ 637 static void 638 tem_prom_scroll_up(struct tem *tem, int nrows, cred_t *credp) 639 { 640 struct tem_state *tems = tem->state; 641 struct vis_conscopy ma; 642 int ncols, width; 643 644 /* copy */ 645 ma.s_row = nrows * tems->a_font.height; 646 ma.e_row = tems->a_p_dimension.height - 1; 647 ma.t_row = 0; 648 649 ma.s_col = 0; 650 ma.e_col = tems->a_p_dimension.width - 1; 651 ma.t_col = 0; 652 653 tem_copy(tem, &ma, credp, CALLED_FROM_NORMAL); 654 655 /* clear */ 656 width = tems->a_font.width; 657 ncols = (tems->a_p_dimension.width + 658 (width - 1))/ width; 659 660 tem_pix_cls_range(tem, 661 0, nrows, tems->a_p_offset.y, 662 0, ncols, 0, 663 B_TRUE, credp, CALLED_FROM_NORMAL); 664 } 665 666 #define PROM_DEFAULT_FONT_HEIGHT 22 667 #define PROM_DEFAULT_WINDOW_TOP 0x8a 668 669 /* 670 * This function is to compute the starting row of the console, according to 671 * PROM cursor's position. Here we have to take different fonts into account. 672 */ 673 static int 674 tem_adjust_row(tem_t *tem, int prom_row, cred_t *credp) 675 { 676 int tem_row; 677 int tem_y; 678 int prom_charheight = 0; 679 int prom_window_top = 0; 680 int scroll_up_lines; 681 682 plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top); 683 if (prom_charheight == 0) 684 prom_charheight = PROM_DEFAULT_FONT_HEIGHT; 685 if (prom_window_top == 0) 686 prom_window_top = PROM_DEFAULT_WINDOW_TOP; 687 688 tem_y = (prom_row + 1) * prom_charheight + prom_window_top - 689 tem->state->a_p_offset.y; 690 tem_row = (tem_y + tem->state->a_font.height - 1) / 691 tem->state->a_font.height - 1; 692 693 if (tem_row < 0) { 694 tem_row = 0; 695 } else if (tem_row >= (tem->state->a_c_dimension.height - 1)) { 696 /* 697 * Scroll up the prom outputs if the PROM cursor's position is 698 * below our tem's lower boundary. 699 */ 700 scroll_up_lines = tem_row - 701 (tem->state->a_c_dimension.height - 1); 702 tem_prom_scroll_up(tem, scroll_up_lines, credp); 703 tem_row = tem->state->a_c_dimension.height - 1; 704 } 705 706 return (tem_row); 707 } 708