11253Slq150181 /* 21253Slq150181 * CDDL HEADER START 31253Slq150181 * 41253Slq150181 * The contents of this file are subject to the terms of the 51253Slq150181 * Common Development and Distribution License (the "License"). 61253Slq150181 * You may not use this file except in compliance with the License. 71253Slq150181 * 81253Slq150181 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91253Slq150181 * or http://www.opensolaris.org/os/licensing. 101253Slq150181 * See the License for the specific language governing permissions 111253Slq150181 * and limitations under the License. 121253Slq150181 * 131253Slq150181 * When distributing Covered Code, include this CDDL HEADER in each 141253Slq150181 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151253Slq150181 * If applicable, add the following below this CDDL HEADER, with the 161253Slq150181 * fields enclosed by brackets "[]" replaced with your own identifying 171253Slq150181 * information: Portions Copyright [yyyy] [name of copyright owner] 181253Slq150181 * 191253Slq150181 * CDDL HEADER END 201253Slq150181 */ 211253Slq150181 221253Slq150181 /* 23*3994Slq150181 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 241253Slq150181 * Use is subject to license terms. 251253Slq150181 */ 261253Slq150181 271253Slq150181 #pragma ident "%Z%%M% %I% %E% SMI" 281253Slq150181 291253Slq150181 /* 301253Slq150181 * ANSI terminal emulator module; parse ANSI X3.64 escape sequences and 311253Slq150181 * the like. 321253Slq150181 */ 331253Slq150181 341253Slq150181 #include <sys/types.h> 351253Slq150181 #include <sys/file.h> 361253Slq150181 #include <sys/conf.h> 371253Slq150181 #include <sys/errno.h> 381253Slq150181 #include <sys/open.h> 391253Slq150181 #include <sys/cred.h> 401253Slq150181 #include <sys/kmem.h> 411253Slq150181 #include <sys/ascii.h> 421253Slq150181 #include <sys/consdev.h> 431253Slq150181 #include <sys/font.h> 441253Slq150181 #include <sys/fbio.h> 451253Slq150181 #include <sys/conf.h> 461253Slq150181 #include <sys/modctl.h> 471253Slq150181 #include <sys/strsubr.h> 481253Slq150181 #include <sys/stat.h> 491253Slq150181 #include <sys/visual_io.h> 501253Slq150181 #include <sys/mutex.h> 511253Slq150181 #include <sys/param.h> 521253Slq150181 #include <sys/debug.h> 531253Slq150181 #include <sys/cmn_err.h> 541253Slq150181 #include <sys/console.h> 551253Slq150181 #include <sys/ddi.h> 561253Slq150181 #include <sys/sunddi.h> 571253Slq150181 #include <sys/sunldi.h> 581253Slq150181 #include <sys/tem_impl.h> 591253Slq150181 #include <sys/tem.h> 601253Slq150181 #ifdef _HAVE_TEM_FIRMWARE 611253Slq150181 #include <sys/promif.h> 621253Slq150181 #endif /* _HAVE_TEM_FIRMWARE */ 631253Slq150181 #include <sys/consconfig_dacf.h> 641253Slq150181 651253Slq150181 /* Terminal emulator functions */ 661253Slq150181 static int tem_setup_terminal(struct vis_devinit *, tem_t *, 671253Slq150181 size_t, size_t); 681253Slq150181 static void tem_modechange_callback(tem_t *, struct vis_devinit *); 691253Slq150181 static void tem_free(tem_t *); 70*3994Slq150181 static void tem_get_inverses(boolean_t *, boolean_t *); 71*3994Slq150181 static void tem_get_initial_color(tem_t *); 721253Slq150181 static int tem_adjust_row(tem_t *, int, cred_t *); 731253Slq150181 741253Slq150181 /* 751253Slq150181 * Globals 761253Slq150181 */ 771253Slq150181 ldi_ident_t term_li = NULL; 781253Slq150181 791253Slq150181 801253Slq150181 extern struct mod_ops mod_miscops; 811253Slq150181 821253Slq150181 static struct modlmisc modlmisc = { 831253Slq150181 &mod_miscops, /* modops */ 841253Slq150181 "ANSI Terminal Emulator", /* name */ 851253Slq150181 }; 861253Slq150181 871253Slq150181 static struct modlinkage modlinkage = { 881253Slq150181 MODREV_1, (void *)&modlmisc, NULL 891253Slq150181 }; 901253Slq150181 911253Slq150181 int 921253Slq150181 _init(void) 931253Slq150181 { 941253Slq150181 int ret; 951253Slq150181 ret = mod_install(&modlinkage); 961253Slq150181 if (ret != 0) 971253Slq150181 return (ret); 981253Slq150181 ret = ldi_ident_from_mod(&modlinkage, &term_li); 991253Slq150181 if (ret != 0) { 1001253Slq150181 (void) mod_remove(&modlinkage); 1011253Slq150181 return (ret); 1021253Slq150181 } 1031253Slq150181 return (0); 1041253Slq150181 } 1051253Slq150181 1061253Slq150181 int 1071253Slq150181 _fini() 1081253Slq150181 { 1091253Slq150181 int ret; 1101253Slq150181 1111253Slq150181 ret = mod_remove(&modlinkage); 1121253Slq150181 if (ret == 0) { 1131253Slq150181 ldi_ident_release(term_li); 1141253Slq150181 term_li = NULL; 1151253Slq150181 } 1161253Slq150181 return (ret); 1171253Slq150181 } 1181253Slq150181 1191253Slq150181 int 1201253Slq150181 _info(struct modinfo *modinfop) 1211253Slq150181 { 1221253Slq150181 return (mod_info(&modlinkage, modinfop)); 1231253Slq150181 } 1241253Slq150181 1251253Slq150181 int 1261253Slq150181 tem_fini(tem_t *tem) 1271253Slq150181 { 1281253Slq150181 int lyr_rval; 1291253Slq150181 1301253Slq150181 mutex_enter(&tem->lock); 1311253Slq150181 1321253Slq150181 ASSERT(tem->hdl != NULL); 1331253Slq150181 1341253Slq150181 /* 1351253Slq150181 * Allow layered on driver to clean up console private 1361253Slq150181 * data. 1371253Slq150181 */ 1381253Slq150181 (void) ldi_ioctl(tem->hdl, VIS_DEVFINI, 1391253Slq150181 0, FKIOCTL, kcred, &lyr_rval); 1401253Slq150181 1411253Slq150181 /* 1421253Slq150181 * Close layered on driver 1431253Slq150181 */ 1441253Slq150181 (void) ldi_close(tem->hdl, NULL, kcred); 1451253Slq150181 tem->hdl = NULL; 1461253Slq150181 1471253Slq150181 mutex_exit(&tem->lock); 1481253Slq150181 1491253Slq150181 tem_free(tem); 1501253Slq150181 1511253Slq150181 return (0); 1521253Slq150181 } 1531253Slq150181 1541253Slq150181 static int 1551253Slq150181 tem_init_failed(tem_t *tem, cred_t *credp, boolean_t finish_ioctl) 1561253Slq150181 { 1571253Slq150181 int lyr_rval; 1581253Slq150181 1591253Slq150181 if (finish_ioctl) 1601253Slq150181 (void) ldi_ioctl(tem->hdl, VIS_DEVFINI, 0, FWRITE|FKIOCTL, 1611253Slq150181 credp, &lyr_rval); 1621253Slq150181 1631253Slq150181 (void) ldi_close(tem->hdl, NULL, credp); 1641253Slq150181 tem_free(tem); 1651253Slq150181 return (ENXIO); 1661253Slq150181 } 1671253Slq150181 1681253Slq150181 static void 1691253Slq150181 tem_free_state(struct tem_state *tems) 1701253Slq150181 { 1711253Slq150181 ASSERT(tems != NULL); 1721253Slq150181 1731253Slq150181 if (tems->a_outbuf != NULL) 1741253Slq150181 kmem_free(tems->a_outbuf, 1751253Slq150181 tems->a_c_dimension.width); 1761253Slq150181 if (tems->a_blank_line != NULL) 1771253Slq150181 kmem_free(tems->a_blank_line, 1781253Slq150181 tems->a_c_dimension.width); 1791253Slq150181 if (tems->a_pix_data != NULL) 1801253Slq150181 kmem_free(tems->a_pix_data, 1811253Slq150181 tems->a_pix_data_size); 1821253Slq150181 kmem_free(tems, sizeof (struct tem_state)); 1831253Slq150181 } 1841253Slq150181 1851253Slq150181 static void 1861253Slq150181 tem_free(tem_t *tem) 1871253Slq150181 { 1881253Slq150181 ASSERT(tem != NULL); 1891253Slq150181 1901253Slq150181 if (tem->state != NULL) 1911253Slq150181 tem_free_state(tem->state); 1921253Slq150181 1931253Slq150181 kmem_free(tem, sizeof (struct tem)); 1941253Slq150181 } 1951253Slq150181 1961253Slq150181 /* 1971253Slq150181 * This is the main entry point to the module. It handles output requests 1981253Slq150181 * during normal system operation, when (e.g.) mutexes are available. 1991253Slq150181 */ 2001253Slq150181 void 2011253Slq150181 tem_write(tem_t *tem, uchar_t *buf, ssize_t len, cred_t *credp) 2021253Slq150181 { 2031253Slq150181 mutex_enter(&tem->lock); 2041253Slq150181 2051253Slq150181 ASSERT(tem->hdl != NULL); 2061253Slq150181 2071253Slq150181 tem_check_first_time(tem, credp, CALLED_FROM_NORMAL); 2081253Slq150181 tem_terminal_emulate(tem, buf, len, credp, CALLED_FROM_NORMAL); 2091253Slq150181 2101253Slq150181 mutex_exit(&tem->lock); 2111253Slq150181 } 2121253Slq150181 2131253Slq150181 int 2141253Slq150181 tem_init(tem_t **ptem, char *pathname, cred_t *credp) 2151253Slq150181 { 2161253Slq150181 struct vis_devinit devinit; 2171253Slq150181 tem_t *tem; 2181253Slq150181 size_t height = 0; 2191253Slq150181 size_t width = 0; 2201253Slq150181 uint32_t row = 0; 2211253Slq150181 uint32_t col = 0; 2221253Slq150181 char *pathbuf; 2231253Slq150181 int err = 0; 2241253Slq150181 int lyr_rval; 2251253Slq150181 2261253Slq150181 tem = kmem_zalloc(sizeof (struct tem), KM_SLEEP); 2271253Slq150181 2281253Slq150181 mutex_init(&tem->lock, (char *)NULL, MUTEX_DRIVER, NULL); 2291253Slq150181 2301253Slq150181 #ifdef _HAVE_TEM_FIRMWARE 2311253Slq150181 tem->cons_wrtvec = tem_write; 2321253Slq150181 #endif /* _HAVE_TEM_FIRMWARE */ 2331253Slq150181 2341253Slq150181 /* 2351253Slq150181 * Open the layered device using the devfs physical device name 2361253Slq150181 * after adding the /devices prefix. 2371253Slq150181 */ 2381253Slq150181 pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP); 2391253Slq150181 (void) strcpy(pathbuf, "/devices"); 2401253Slq150181 if (i_ddi_prompath_to_devfspath(pathname, 2411253Slq150181 pathbuf + strlen("/devices")) != DDI_SUCCESS) { 2421253Slq150181 cmn_err(CE_WARN, "terminal emulator: Path conversion error"); 2431253Slq150181 kmem_free(pathbuf, MAXPATHLEN); 2441253Slq150181 tem_free(tem); 2451253Slq150181 return (ENXIO); 2461253Slq150181 } 2471253Slq150181 if (ldi_open_by_name(pathbuf, FWRITE, credp, &tem->hdl, term_li) != 0) { 2481253Slq150181 cmn_err(CE_WARN, "terminal emulator: Device path open error"); 2491253Slq150181 kmem_free(pathbuf, MAXPATHLEN); 2501253Slq150181 tem_free(tem); 2511253Slq150181 return (ENXIO); 2521253Slq150181 } 2531253Slq150181 kmem_free(pathbuf, MAXPATHLEN); 2541253Slq150181 2551253Slq150181 devinit.modechg_cb = (vis_modechg_cb_t)tem_modechange_callback; 2561253Slq150181 devinit.modechg_arg = (struct vis_modechg_arg *)tem; 2571253Slq150181 2581253Slq150181 /* 2591253Slq150181 * Initialize the console and get the device parameters 2601253Slq150181 */ 2611253Slq150181 if ((err = ldi_ioctl(tem->hdl, VIS_DEVINIT, 2621253Slq150181 (intptr_t)&devinit, FWRITE|FKIOCTL, credp, &lyr_rval)) != 0) { 2631253Slq150181 cmn_err(CE_WARN, "terminal emulator: Compatible fb not found"); 2641253Slq150181 return (tem_init_failed(tem, credp, B_FALSE)); 2651253Slq150181 } 2661253Slq150181 2671253Slq150181 /* Make sure the fb driver and terminal emulator versions match */ 2681253Slq150181 if (devinit.version != VIS_CONS_REV) { 2691253Slq150181 cmn_err(CE_WARN, 2701253Slq150181 "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) " 2711253Slq150181 "of console fb driver not supported", devinit.version); 2721253Slq150181 return (tem_init_failed(tem, credp, B_TRUE)); 2731253Slq150181 } 2741253Slq150181 2751253Slq150181 if ((tem->fb_polledio = devinit.polledio) == NULL) { 2761253Slq150181 cmn_err(CE_WARN, "terminal emulator: fb doesn't support polled " 2771253Slq150181 "I/O"); 2781253Slq150181 return (tem_init_failed(tem, credp, B_TRUE)); 2791253Slq150181 } 2801253Slq150181 2811253Slq150181 /* other sanity checks */ 2821253Slq150181 if (!((devinit.depth == 4) || (devinit.depth == 8) || 2831253Slq150181 (devinit.depth == 24) || (devinit.depth == 32))) { 2841253Slq150181 cmn_err(CE_WARN, "terminal emulator: unsupported depth"); 2851253Slq150181 return (tem_init_failed(tem, credp, B_TRUE)); 2861253Slq150181 } 2871253Slq150181 2881253Slq150181 if ((devinit.mode != VIS_TEXT) && (devinit.mode != VIS_PIXEL)) { 2891253Slq150181 cmn_err(CE_WARN, "terminal emulator: unsupported mode"); 2901253Slq150181 return (tem_init_failed(tem, credp, B_TRUE)); 2911253Slq150181 } 2921253Slq150181 2931253Slq150181 if ((devinit.mode == VIS_PIXEL) && plat_stdout_is_framebuffer()) { 2941253Slq150181 plat_tem_get_prom_size(&height, &width); 2951253Slq150181 } 2961253Slq150181 2971253Slq150181 /* 2981253Slq150181 * Initialize the terminal emulator 2991253Slq150181 */ 3001253Slq150181 mutex_enter(&tem->lock); 3011253Slq150181 if ((err = tem_setup_terminal(&devinit, tem, height, width)) != 0) { 3021253Slq150181 cmn_err(CE_WARN, "terminal emulator: Init failed"); 3031253Slq150181 (void) ldi_ioctl(tem->hdl, VIS_DEVFINI, 0, FWRITE|FKIOCTL, 3041253Slq150181 credp, &lyr_rval); 3051253Slq150181 (void) ldi_close(tem->hdl, NULL, credp); 3061253Slq150181 mutex_exit(&tem->lock); 3071253Slq150181 tem_free(tem); 3081253Slq150181 return (err); 3091253Slq150181 } 3101253Slq150181 3111253Slq150181 /* 312*3994Slq150181 * make our kernel console keep compatibility with OBP. 313*3994Slq150181 */ 314*3994Slq150181 tem_get_initial_color(tem); 315*3994Slq150181 316*3994Slq150181 /* 3171253Slq150181 * On SPARC don't clear the screen if the console is the framebuffer. 3181253Slq150181 * Otherwise it needs to be cleared to get rid of junk that may be 3191253Slq150181 * in frameuffer memory, since the screen isn't cleared when 3201253Slq150181 * boot messages are directed elsewhere. 3211253Slq150181 */ 3221253Slq150181 if (devinit.mode == VIS_TEXT) { 3231253Slq150181 /* 3241253Slq150181 * The old getting current cursor position code, which 3251253Slq150181 * is not needed here, has been in tem_write/tem_polled_write. 3261253Slq150181 */ 327*3994Slq150181 tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 0, NULL); 3281253Slq150181 } else if (plat_stdout_is_framebuffer()) { 3291253Slq150181 ASSERT(devinit.mode == VIS_PIXEL); 3301253Slq150181 plat_tem_hide_prom_cursor(); 331*3994Slq150181 tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 0, NULL); 3321253Slq150181 3331253Slq150181 /* 3341253Slq150181 * We are getting the current cursor position in pixel 3351253Slq150181 * mode so that we don't over-write the console output 3361253Slq150181 * during boot. 3371253Slq150181 */ 3381253Slq150181 plat_tem_get_prom_pos(&row, &col); 3391253Slq150181 3401253Slq150181 /* 3411253Slq150181 * Adjust the row if necessary when the font of our 3421253Slq150181 * kernel console tem is different with that of prom 3431253Slq150181 * tem. 3441253Slq150181 */ 3451253Slq150181 row = tem_adjust_row(tem, row, credp); 3461253Slq150181 3471253Slq150181 /* first line of our kernel console output */ 3481253Slq150181 tem->state->first_line = row + 1; 3491253Slq150181 3501253Slq150181 /* re-set and align cusror position */ 3511253Slq150181 tem->state->a_c_cursor.row = row; 3521253Slq150181 tem->state->a_c_cursor.col = 0; 3531253Slq150181 tem_align_cursor(tem); 3541253Slq150181 } else { 355*3994Slq150181 tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 1, NULL); 3561253Slq150181 } 3571253Slq150181 3581253Slq150181 #ifdef _HAVE_TEM_FIRMWARE 3591253Slq150181 if (plat_stdout_is_framebuffer()) { 3601253Slq150181 /* 3611253Slq150181 * Drivers in the console stream may emit additional 3621253Slq150181 * messages before we are ready. This causes text 3631253Slq150181 * overwrite on the screen. So we set the redirection 3641253Slq150181 * here. It is safe because the ioctl in consconfig_dacf 3651253Slq150181 * will succeed and consmode will be set to CONS_KFB. 3661253Slq150181 */ 3671253Slq150181 prom_set_stdout_redirect(console_prom_write_cb, 3681253Slq150181 (promif_redir_arg_t)tem); 3691253Slq150181 3701253Slq150181 } 3711253Slq150181 #endif /* _HAVE_TEM_FIRMWARE */ 3721253Slq150181 3731253Slq150181 mutex_exit(&tem->lock); 3741253Slq150181 *ptem = tem; /* Return tem to caller only upon success */ 3751253Slq150181 return (0); 3761253Slq150181 } 3771253Slq150181 3781253Slq150181 /* 3791253Slq150181 * This is a callback function that we register with the frame 3801253Slq150181 * buffer driver layered underneath. It gets invoked from 3811253Slq150181 * the underlying frame buffer driver to reconfigure the terminal 3821253Slq150181 * emulator to a new screen size and depth in conjunction with 3831253Slq150181 * framebuffer videomode changes. 384*3994Slq150181 * Here we keep the foreground/background color and attributes, 385*3994Slq150181 * which may be different with the initial settings, so that 386*3994Slq150181 * the color won't change while the framebuffer videomode changes. 387*3994Slq150181 * And we also reset the kernel terminal emulator and clear the 388*3994Slq150181 * whole screen. 3891253Slq150181 */ 3901253Slq150181 void 3911253Slq150181 tem_modechange_callback(tem_t *tem, struct vis_devinit *devinit) 3921253Slq150181 { 393*3994Slq150181 tem_color_t tc; 394*3994Slq150181 3951253Slq150181 mutex_enter(&tem->lock); 3961253Slq150181 3971253Slq150181 ASSERT(tem->hdl != NULL); 3981253Slq150181 399*3994Slq150181 tc.fg_color = tem->state->fg_color; 400*3994Slq150181 tc.bg_color = tem->state->bg_color; 401*3994Slq150181 tc.a_flags = tem->state->a_flags; 402*3994Slq150181 4031253Slq150181 (void) tem_setup_terminal(devinit, tem, 4041253Slq150181 tem->state->a_c_dimension.height, 4051253Slq150181 tem->state->a_c_dimension.width); 4061253Slq150181 407*3994Slq150181 tem_reset_display(tem, kcred, CALLED_FROM_NORMAL, 1, &tc); 4081253Slq150181 4091253Slq150181 mutex_exit(&tem->lock); 4101253Slq150181 4111253Slq150181 if (tem->modechg_cb != NULL) 4121253Slq150181 tem->modechg_cb(tem->modechg_arg); 4131253Slq150181 } 4141253Slq150181 4151253Slq150181 static int 4161253Slq150181 tem_setup_terminal( 4171253Slq150181 struct vis_devinit *devinit, 4181253Slq150181 tem_t *tem, 4191253Slq150181 size_t height, size_t width) 4201253Slq150181 { 4211253Slq150181 int i; 4221253Slq150181 struct tem_state *new_state, *prev_state; 4231253Slq150181 4241253Slq150181 ASSERT(MUTEX_HELD(&tem->lock)); 4251253Slq150181 4261253Slq150181 prev_state = tem->state; 4271253Slq150181 4281253Slq150181 new_state = kmem_zalloc(sizeof (struct tem_state), KM_SLEEP); 4291253Slq150181 4301253Slq150181 new_state->a_pdepth = devinit->depth; 4311253Slq150181 new_state->display_mode = devinit->mode; 4321253Slq150181 new_state->linebytes = devinit->linebytes; 4331253Slq150181 4341253Slq150181 switch (devinit->mode) { 4351253Slq150181 case VIS_TEXT: 4361253Slq150181 new_state->a_p_dimension.width = 0; 4371253Slq150181 new_state->a_p_dimension.height = 0; 4381253Slq150181 new_state->a_c_dimension.width = devinit->width; 4391253Slq150181 new_state->a_c_dimension.height = devinit->height; 4401253Slq150181 4411253Slq150181 new_state->in_fp.f_display = tem_text_display; 4421253Slq150181 new_state->in_fp.f_copy = tem_text_copy; 4431253Slq150181 new_state->in_fp.f_cursor = tem_text_cursor; 4441253Slq150181 new_state->in_fp.f_cls = tem_text_cls; 4451253Slq150181 new_state->in_fp.f_bit2pix = NULL; 4461253Slq150181 4471253Slq150181 new_state->a_blank_line = 4481253Slq150181 kmem_alloc(new_state->a_c_dimension.width, KM_SLEEP); 4491253Slq150181 4501253Slq150181 for (i = 0; i < new_state->a_c_dimension.width; i++) 4511253Slq150181 new_state->a_blank_line[i] = ' '; 4521253Slq150181 4531253Slq150181 break; 4541253Slq150181 case VIS_PIXEL: 4551253Slq150181 4561253Slq150181 /* 4571253Slq150181 * First check to see if the user has specified a screen size. 4581253Slq150181 * If so, use those values. Else use 34x80 as the default. 4591253Slq150181 */ 4601253Slq150181 if (width == 0) { 4611253Slq150181 width = TEM_DEFAULT_COLS; 4621253Slq150181 height = TEM_DEFAULT_ROWS; 4631253Slq150181 } 4641253Slq150181 new_state->a_c_dimension.height = height; 4651253Slq150181 new_state->a_c_dimension.width = width; 4661253Slq150181 4671253Slq150181 new_state->a_p_dimension.height = devinit->height; 4681253Slq150181 new_state->a_p_dimension.width = devinit->width; 4691253Slq150181 4701253Slq150181 new_state->in_fp.f_display = tem_pix_display; 4711253Slq150181 new_state->in_fp.f_copy = tem_pix_copy; 4721253Slq150181 new_state->in_fp.f_cursor = tem_pix_cursor; 4731253Slq150181 new_state->in_fp.f_cls = tem_pix_cls; 4741253Slq150181 4751253Slq150181 new_state->a_blank_line = NULL; 4761253Slq150181 4771253Slq150181 /* 4781253Slq150181 * set_font() will select a appropriate sized font for 4791253Slq150181 * the number of rows and columns selected. If we don't 4801253Slq150181 * have a font that will fit, then it will use the 4811253Slq150181 * default builtin font and adjust the rows and columns 4821253Slq150181 * to fit on the screen. 4831253Slq150181 */ 4841253Slq150181 set_font(&new_state->a_font, 4851253Slq150181 &new_state->a_c_dimension.height, 4861253Slq150181 &new_state->a_c_dimension.width, 4871253Slq150181 new_state->a_p_dimension.height, 4881253Slq150181 new_state->a_p_dimension.width); 4891253Slq150181 4901253Slq150181 new_state->a_p_offset.y = 4911253Slq150181 (new_state->a_p_dimension.height - 4921253Slq150181 (new_state->a_c_dimension.height * 4931253Slq150181 new_state->a_font.height)) / 2; 4941253Slq150181 4951253Slq150181 new_state->a_p_offset.x = 4961253Slq150181 (new_state->a_p_dimension.width - 4971253Slq150181 (new_state->a_c_dimension.width * 4981253Slq150181 new_state->a_font.width)) / 2; 4991253Slq150181 5001253Slq150181 switch (devinit->depth) { 5011253Slq150181 case 4: 5021253Slq150181 new_state->in_fp.f_bit2pix = bit_to_pix4; 5031253Slq150181 new_state->a_pix_data_size = 5041253Slq150181 (((new_state->a_font.width * 4) + 5051253Slq150181 NBBY - 1) / NBBY) * new_state->a_font.height; 5061253Slq150181 break; 5071253Slq150181 case 8: 5081253Slq150181 new_state->in_fp.f_bit2pix = bit_to_pix8; 5091253Slq150181 new_state->a_pix_data_size = 5101253Slq150181 new_state->a_font.width * 5111253Slq150181 new_state->a_font.height; 5121253Slq150181 break; 5131253Slq150181 case 24: 5141253Slq150181 case 32: 5151253Slq150181 new_state->in_fp.f_bit2pix = bit_to_pix24; 5161253Slq150181 new_state->a_pix_data_size = 5171253Slq150181 new_state->a_font.width * 5181253Slq150181 new_state->a_font.height; 5191253Slq150181 new_state->a_pix_data_size *= 4; 5201253Slq150181 break; 5211253Slq150181 } 5221253Slq150181 5231253Slq150181 new_state->a_pix_data = 5241253Slq150181 kmem_alloc(new_state->a_pix_data_size, KM_SLEEP); 5251253Slq150181 5261253Slq150181 break; 5271253Slq150181 5281253Slq150181 default: 5291253Slq150181 /* 5301253Slq150181 * The layered fb driver conveyed an unrecognized rendering 5311253Slq150181 * mode. We cannot proceed with tem initialization. 5321253Slq150181 */ 5331253Slq150181 kmem_free(new_state, sizeof (struct tem_state)); 5341253Slq150181 return (ENXIO); 5351253Slq150181 } 5361253Slq150181 5371253Slq150181 new_state->a_outbuf = 5381253Slq150181 kmem_alloc(new_state->a_c_dimension.width, KM_SLEEP); 5391253Slq150181 5401253Slq150181 /* 5411253Slq150181 * Change state atomically so that polled I/O requests 5421253Slq150181 * can be safely and reliably serviced anytime after the terminal 5431253Slq150181 * emulator is originally initialized and the console mode has been 5441253Slq150181 * switched over from the PROM, even while a videomode change 5451253Slq150181 * callback is being processed. 5461253Slq150181 */ 5471253Slq150181 tem->state = new_state; 5481253Slq150181 5491253Slq150181 if (prev_state != NULL) 5501253Slq150181 tem_free_state(prev_state); 5511253Slq150181 5521253Slq150181 return (0); 5531253Slq150181 } 5541253Slq150181 5551253Slq150181 /* 5561253Slq150181 * This function is used to display a rectangular blit of data 5571253Slq150181 * of a given size and location via the underlying framebuffer driver. 5581253Slq150181 * The blit can be as small as a pixel or as large as the screen. 5591253Slq150181 */ 5601253Slq150181 void 5611253Slq150181 tem_display_layered( 5621253Slq150181 tem_t *tem, 5631253Slq150181 struct vis_consdisplay *pda, 5641253Slq150181 cred_t *credp) 5651253Slq150181 { 5661253Slq150181 int rval; 5671253Slq150181 5681253Slq150181 (void) ldi_ioctl(tem->hdl, VIS_CONSDISPLAY, 5691253Slq150181 (intptr_t)pda, FKIOCTL, credp, &rval); 5701253Slq150181 } 5711253Slq150181 5721253Slq150181 /* 5731253Slq150181 * This function is used to invoke a block copy operation in the 5741253Slq150181 * underlying framebuffer driver. Rectangle copies are how scrolling 5751253Slq150181 * is implemented, as well as horizontal text shifting escape seqs. 5761253Slq150181 * such as from vi when deleting characters and words. 5771253Slq150181 */ 5781253Slq150181 void 5791253Slq150181 tem_copy_layered( 5801253Slq150181 tem_t *tem, 5811253Slq150181 struct vis_conscopy *pma, 5821253Slq150181 cred_t *credp) 5831253Slq150181 { 5841253Slq150181 int rval; 5851253Slq150181 5861253Slq150181 (void) ldi_ioctl(tem->hdl, VIS_CONSCOPY, 5871253Slq150181 (intptr_t)pma, FKIOCTL, credp, &rval); 5881253Slq150181 } 5891253Slq150181 5901253Slq150181 /* 5911253Slq150181 * This function is used to show or hide a rectangluar monochrom 5921253Slq150181 * pixel inverting, text block cursor via the underlying framebuffer. 5931253Slq150181 */ 5941253Slq150181 void 5951253Slq150181 tem_cursor_layered( 5961253Slq150181 tem_t *tem, 5971253Slq150181 struct vis_conscursor *pca, 5981253Slq150181 cred_t *credp) 5991253Slq150181 { 6001253Slq150181 int rval; 6011253Slq150181 6021253Slq150181 (void) ldi_ioctl(tem->hdl, VIS_CONSCURSOR, 6031253Slq150181 (intptr_t)pca, FKIOCTL, credp, &rval); 6041253Slq150181 } 6051253Slq150181 6061253Slq150181 void 6071253Slq150181 tem_reset_colormap( 6081253Slq150181 tem_t *tem, 6091253Slq150181 cred_t *credp, 6101253Slq150181 enum called_from called_from) 6111253Slq150181 { 6121253Slq150181 struct vis_cmap cm; 6131253Slq150181 int rval; 6141253Slq150181 6151253Slq150181 if (called_from == CALLED_FROM_STANDALONE) 6161253Slq150181 return; 6171253Slq150181 6181253Slq150181 switch (tem->state->a_pdepth) { 6191253Slq150181 case 8: 6201253Slq150181 cm.index = 0; 6211253Slq150181 cm.count = 16; 6221253Slq150181 cm.red = cmap4_to_24.red; /* 8-bits (1/3 of TrueColor 24) */ 6231253Slq150181 cm.blue = cmap4_to_24.blue; /* 8-bits (1/3 of TrueColor 24) */ 6241253Slq150181 cm.green = cmap4_to_24.green; /* 8-bits (1/3 of TrueColor 24) */ 6251253Slq150181 (void) ldi_ioctl(tem->hdl, VIS_PUTCMAP, (intptr_t)&cm, 6261253Slq150181 FKIOCTL, credp, &rval); 6271253Slq150181 break; 6281253Slq150181 } 6291253Slq150181 } 6301253Slq150181 6311253Slq150181 void 6321253Slq150181 tem_get_size(tem_t *tem, ushort_t *r, ushort_t *c, 6331253Slq150181 ushort_t *x, ushort_t *y) 6341253Slq150181 { 6351253Slq150181 *r = (ushort_t)tem->state->a_c_dimension.height; 6361253Slq150181 *c = (ushort_t)tem->state->a_c_dimension.width; 6371253Slq150181 *x = (ushort_t)tem->state->a_p_dimension.width; 6381253Slq150181 *y = (ushort_t)tem->state->a_p_dimension.height; 6391253Slq150181 } 6401253Slq150181 6411253Slq150181 void 6421253Slq150181 tem_register_modechg_cb(tem_t *tem, tem_modechg_cb_t func, 6431253Slq150181 tem_modechg_cb_arg_t arg) 6441253Slq150181 { 6451253Slq150181 tem->modechg_cb = func; 6461253Slq150181 tem->modechg_arg = arg; 6471253Slq150181 } 6481253Slq150181 6491253Slq150181 /* 6501253Slq150181 * This function is to scroll up the OBP output, which has 6511253Slq150181 * different screen height and width with our kernel console. 6521253Slq150181 */ 6531253Slq150181 static void 6541253Slq150181 tem_prom_scroll_up(struct tem *tem, int nrows, cred_t *credp) 6551253Slq150181 { 6561253Slq150181 struct tem_state *tems = tem->state; 6571253Slq150181 struct vis_conscopy ma; 6581253Slq150181 int ncols, width; 6591253Slq150181 6601253Slq150181 /* copy */ 6611253Slq150181 ma.s_row = nrows * tems->a_font.height; 6621253Slq150181 ma.e_row = tems->a_p_dimension.height - 1; 6631253Slq150181 ma.t_row = 0; 6641253Slq150181 6651253Slq150181 ma.s_col = 0; 6661253Slq150181 ma.e_col = tems->a_p_dimension.width - 1; 6671253Slq150181 ma.t_col = 0; 6681253Slq150181 6691253Slq150181 tem_copy(tem, &ma, credp, CALLED_FROM_NORMAL); 6701253Slq150181 6711253Slq150181 /* clear */ 6721253Slq150181 width = tems->a_font.width; 6731253Slq150181 ncols = (tems->a_p_dimension.width + 6741253Slq150181 (width - 1))/ width; 6751253Slq150181 6761253Slq150181 tem_pix_cls_range(tem, 6771253Slq150181 0, nrows, tems->a_p_offset.y, 6781253Slq150181 0, ncols, 0, 6791253Slq150181 B_TRUE, credp, CALLED_FROM_NORMAL); 6801253Slq150181 } 6811253Slq150181 6821253Slq150181 #define PROM_DEFAULT_FONT_HEIGHT 22 6831253Slq150181 #define PROM_DEFAULT_WINDOW_TOP 0x8a 6841253Slq150181 6851253Slq150181 /* 6861253Slq150181 * This function is to compute the starting row of the console, according to 6871253Slq150181 * PROM cursor's position. Here we have to take different fonts into account. 6881253Slq150181 */ 6891253Slq150181 static int 6901253Slq150181 tem_adjust_row(tem_t *tem, int prom_row, cred_t *credp) 6911253Slq150181 { 6921253Slq150181 int tem_row; 6931253Slq150181 int tem_y; 6941253Slq150181 int prom_charheight = 0; 6951253Slq150181 int prom_window_top = 0; 6961253Slq150181 int scroll_up_lines; 6971253Slq150181 6981253Slq150181 plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top); 6991253Slq150181 if (prom_charheight == 0) 7001253Slq150181 prom_charheight = PROM_DEFAULT_FONT_HEIGHT; 7011253Slq150181 if (prom_window_top == 0) 7021253Slq150181 prom_window_top = PROM_DEFAULT_WINDOW_TOP; 7031253Slq150181 7041253Slq150181 tem_y = (prom_row + 1) * prom_charheight + prom_window_top - 7051253Slq150181 tem->state->a_p_offset.y; 7061253Slq150181 tem_row = (tem_y + tem->state->a_font.height - 1) / 7071253Slq150181 tem->state->a_font.height - 1; 7081253Slq150181 7091253Slq150181 if (tem_row < 0) { 7101253Slq150181 tem_row = 0; 7111253Slq150181 } else if (tem_row >= (tem->state->a_c_dimension.height - 1)) { 7121253Slq150181 /* 7131253Slq150181 * Scroll up the prom outputs if the PROM cursor's position is 7141253Slq150181 * below our tem's lower boundary. 7151253Slq150181 */ 7161253Slq150181 scroll_up_lines = tem_row - 7171253Slq150181 (tem->state->a_c_dimension.height - 1); 7181253Slq150181 tem_prom_scroll_up(tem, scroll_up_lines, credp); 7191253Slq150181 tem_row = tem->state->a_c_dimension.height - 1; 7201253Slq150181 } 7211253Slq150181 7221253Slq150181 return (tem_row); 7231253Slq150181 } 724*3994Slq150181 725*3994Slq150181 static void 726*3994Slq150181 tem_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen) 727*3994Slq150181 { 728*3994Slq150181 int i_inverse = 0; 729*3994Slq150181 int i_inverse_screen = 0; 730*3994Slq150181 731*3994Slq150181 plat_tem_get_inverses(&i_inverse, &i_inverse_screen); 732*3994Slq150181 733*3994Slq150181 *p_inverse = (i_inverse == 0) ? B_FALSE : B_TRUE; 734*3994Slq150181 *p_inverse_screen = (i_inverse_screen == 0) ? B_FALSE : B_TRUE; 735*3994Slq150181 } 736*3994Slq150181 737*3994Slq150181 /* 738*3994Slq150181 * Get the foreground/background color and attributes from the initial 739*3994Slq150181 * PROM, so that our kernel console can keep the same visual behaviour. 740*3994Slq150181 */ 741*3994Slq150181 static void 742*3994Slq150181 tem_get_initial_color(tem_t *tem) 743*3994Slq150181 { 744*3994Slq150181 boolean_t inverse, inverse_screen; 745*3994Slq150181 unsigned short flags = 0; 746*3994Slq150181 747*3994Slq150181 tem->init_color.fg_color = DEFAULT_ANSI_FOREGROUND; 748*3994Slq150181 tem->init_color.bg_color = DEFAULT_ANSI_BACKGROUND; 749*3994Slq150181 750*3994Slq150181 if (plat_stdout_is_framebuffer()) { 751*3994Slq150181 tem_get_inverses(&inverse, &inverse_screen); 752*3994Slq150181 if (inverse) 753*3994Slq150181 flags |= TEM_ATTR_REVERSE; 754*3994Slq150181 if (inverse_screen) 755*3994Slq150181 flags |= TEM_ATTR_SCREEN_REVERSE; 756*3994Slq150181 if (flags != 0) 757*3994Slq150181 flags |= TEM_ATTR_BOLD; 758*3994Slq150181 } 759*3994Slq150181 760*3994Slq150181 tem->init_color.a_flags = flags; 761*3994Slq150181 } 762