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