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