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