xref: /titanic_53/usr/src/uts/common/io/tem.c (revision c9503a497f482bf9524b37eea8c69239425bcdf4)
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