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