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