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