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