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