xref: /illumos-gate/usr/src/uts/common/io/tem.c (revision cdd3e9a818787b4def17c9f707f435885ce0ed31)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * ANSI terminal emulator module; parse ANSI X3.64 escape sequences and
29  * the like.
30  *
31  * How Virtual Terminal Emulator Works:
32  *
33  * Every virtual terminal is associated with a tem_vt_state structure
34  * and maintains a virtual screen buffer in tvs_screen_buf, which contains
35  * all the characters which should be shown on the physical screen when
36  * the terminal is activated.  There are also two other buffers, tvs_fg_buf
37  * and tvs_bg_buf, which track the foreground and background colors of the
38  * on screen characters
39  *
40  * Data written to a virtual terminal is composed of characters which
41  * should be displayed on the screen when this virtual terminal is
42  * activated, fg/bg colors of these characters, and other control
43  * information (escape sequence, etc).
44  *
45  * When data is passed to a virtual terminal it first is parsed for
46  * control information by tem_safe_parse().  Subsequently the character
47  * and color data are written to tvs_screen_buf, tvs_fg_buf, and
48  * tvs_bg_buf.  They are saved in these buffers in order to refresh
49  * the screen when this terminal is activated.  If the terminal is
50  * currently active, the data (characters and colors) are also written
51  * to the physical screen by invoking a callback function,
52  * tem_safe_text_callbacks() or tem_safe_pix_callbacks().
53  *
54  * When rendering data to the framebuffer, if the framebuffer is in
55  * VIS_PIXEL mode, the character data will first be converted to pixel
56  * data using tem_safe_pix_bit2pix(), and then the pixels get displayed
57  * on the physical screen.  We only store the character and color data in
58  * tem_vt_state since the bit2pix conversion only happens when actually
59  * rendering to the physical framebuffer.
60  */
61 
62 
63 #include <sys/types.h>
64 #include <sys/file.h>
65 #include <sys/conf.h>
66 #include <sys/errno.h>
67 #include <sys/open.h>
68 #include <sys/cred.h>
69 #include <sys/kmem.h>
70 #include <sys/ascii.h>
71 #include <sys/consdev.h>
72 #include <sys/font.h>
73 #include <sys/fbio.h>
74 #include <sys/conf.h>
75 #include <sys/modctl.h>
76 #include <sys/strsubr.h>
77 #include <sys/stat.h>
78 #include <sys/visual_io.h>
79 #include <sys/mutex.h>
80 #include <sys/param.h>
81 #include <sys/debug.h>
82 #include <sys/cmn_err.h>
83 #include <sys/console.h>
84 #include <sys/ddi.h>
85 #include <sys/sunddi.h>
86 #include <sys/sunldi.h>
87 #include <sys/tem_impl.h>
88 #ifdef _HAVE_TEM_FIRMWARE
89 #include <sys/promif.h>
90 #endif /* _HAVE_TEM_FIRMWARE */
91 #include <sys/consplat.h>
92 #include <sys/kd.h>
93 #include <sys/sysmacros.h>
94 #include <sys/note.h>
95 #include <sys/t_lock.h>
96 
97 /* Terminal emulator internal helper functions */
98 static void	tems_setup_terminal(struct vis_devinit *, size_t, size_t);
99 static void	tems_modechange_callback(struct vis_modechg_arg *,
100 		struct vis_devinit *);
101 
102 static void	tems_reset_colormap(cred_t *, enum called_from);
103 
104 static void	tem_free_buf(struct tem_vt_state *);
105 static void	tem_internal_init(struct tem_vt_state *, cred_t *, boolean_t,
106 		    boolean_t);
107 static void	tems_get_initial_color(tem_color_t *pcolor);
108 
109 /*
110  * Globals
111  */
112 static ldi_ident_t	term_li = NULL;
113 tem_state_t	tems;	/* common term info */
114 _NOTE(MUTEX_PROTECTS_DATA(tems.ts_lock, tems))
115 
116 extern struct mod_ops mod_miscops;
117 
118 static struct modlmisc	modlmisc = {
119 	&mod_miscops,	/* modops */
120 	"ANSI Terminal Emulator", /* name */
121 };
122 
123 static struct modlinkage modlinkage = {
124 	MODREV_1, { (void *)&modlmisc, NULL }
125 };
126 
127 int
128 _init(void)
129 {
130 	int ret;
131 	ret = mod_install(&modlinkage);
132 	if (ret != 0)
133 		return (ret);
134 	ret = ldi_ident_from_mod(&modlinkage, &term_li);
135 	if (ret != 0) {
136 		(void) mod_remove(&modlinkage);
137 		return (ret);
138 	}
139 
140 	mutex_init(&tems.ts_lock, (char *)NULL, MUTEX_DRIVER, NULL);
141 	list_create(&tems.ts_list, sizeof (struct tem_vt_state),
142 	    offsetof(struct tem_vt_state, tvs_list_node));
143 	tems.ts_active = NULL;
144 
145 	return (0);
146 }
147 
148 int
149 _fini()
150 {
151 	int ret;
152 
153 	ret = mod_remove(&modlinkage);
154 	if (ret == 0) {
155 		ldi_ident_release(term_li);
156 		term_li = NULL;
157 	}
158 	return (ret);
159 }
160 
161 int
162 _info(struct modinfo *modinfop)
163 {
164 	return (mod_info(&modlinkage, modinfop));
165 }
166 
167 static void
168 tem_add(struct tem_vt_state *tem)
169 {
170 	ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock));
171 
172 	list_insert_head(&tems.ts_list, tem);
173 }
174 
175 static void
176 tem_rm(struct tem_vt_state *tem)
177 {
178 	ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock));
179 
180 	list_remove(&tems.ts_list, tem);
181 }
182 
183 /*
184  * This is the main entry point to the module.  It handles output requests
185  * during normal system operation, when (e.g.) mutexes are available.
186  */
187 void
188 tem_write(tem_vt_state_t tem_arg, uchar_t *buf, ssize_t len, cred_t *credp)
189 {
190 	struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
191 
192 	mutex_enter(&tems.ts_lock);
193 	mutex_enter(&tem->tvs_lock);
194 
195 	if (!tem->tvs_initialized) {
196 		mutex_exit(&tem->tvs_lock);
197 		mutex_exit(&tems.ts_lock);
198 		return;
199 	}
200 
201 	tem_safe_check_first_time(tem, credp, CALLED_FROM_NORMAL);
202 	tem_safe_terminal_emulate(tem, buf, len, credp, CALLED_FROM_NORMAL);
203 
204 	mutex_exit(&tem->tvs_lock);
205 	mutex_exit(&tems.ts_lock);
206 }
207 
208 static void
209 tem_internal_init(struct tem_vt_state *ptem, cred_t *credp,
210     boolean_t init_color, boolean_t clear_screen)
211 {
212 	unsigned i, j, width, height;
213 	text_attr_t attr;
214 	text_color_t fg;
215 	text_color_t bg;
216 
217 	ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&ptem->tvs_lock));
218 
219 	if (tems.ts_display_mode == VIS_PIXEL) {
220 		ptem->tvs_pix_data_size = tems.ts_pix_data_size;
221 		ptem->tvs_pix_data =
222 		    kmem_alloc(ptem->tvs_pix_data_size, KM_SLEEP);
223 	}
224 
225 	ptem->tvs_stateflags = TVS_AUTOWRAP;
226 
227 	ptem->tvs_outbuf_size = tems.ts_c_dimension.width *
228 	    sizeof (*ptem->tvs_outbuf);
229 	ptem->tvs_outbuf = kmem_alloc(ptem->tvs_outbuf_size, KM_SLEEP);
230 
231 	width = tems.ts_c_dimension.width;
232 	height = tems.ts_c_dimension.height;
233 	ptem->tvs_screen_history_size = height;
234 
235 	ptem->tvs_screen_buf_size = width * ptem->tvs_screen_history_size *
236 	    sizeof (*ptem->tvs_screen_buf);
237 	ptem->tvs_screen_buf = kmem_alloc(ptem->tvs_screen_buf_size, KM_SLEEP);
238 	ptem->tvs_screen_rows = kmem_alloc(ptem->tvs_screen_history_size *
239 	    sizeof (term_char_t *), KM_SLEEP);
240 
241 	ptem->tvs_maxtab = width / 8;
242 	ptem->tvs_tabs = kmem_alloc(ptem->tvs_maxtab * sizeof (*ptem->tvs_tabs),
243 	    KM_SLEEP);
244 
245 	tem_safe_reset_display(ptem, credp, CALLED_FROM_NORMAL,
246 	    clear_screen, init_color);
247 
248 	ptem->tvs_utf8_left = 0;
249 	ptem->tvs_utf8_partial = 0;
250 
251 	/* Get default attributes and fill up the screen buffer. */
252 	tem_safe_get_attr(ptem, &fg, &bg, &attr, TEM_ATTR_SCREEN_REVERSE);
253 	for (i = 0; i < ptem->tvs_screen_history_size; i++) {
254 		ptem->tvs_screen_rows[i] = &ptem->tvs_screen_buf[i * width];
255 
256 		for (j = 0; j < width; j++) {
257 			ptem->tvs_screen_rows[i][j].tc_fg_color = fg;
258 			ptem->tvs_screen_rows[i][j].tc_bg_color = bg;
259 			ptem->tvs_screen_rows[i][j].tc_char =
260 			    TEM_ATTR(attr) | ' ';
261 		}
262 	}
263 
264 	ptem->tvs_initialized = B_TRUE;
265 }
266 
267 boolean_t
268 tem_initialized(tem_vt_state_t tem_arg)
269 {
270 	struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg;
271 	boolean_t ret;
272 
273 	mutex_enter(&ptem->tvs_lock);
274 	ret = ptem->tvs_initialized;
275 	mutex_exit(&ptem->tvs_lock);
276 
277 	return (ret);
278 }
279 
280 tem_vt_state_t
281 tem_init(cred_t *credp, queue_t *rq)
282 {
283 	struct tem_vt_state *ptem;
284 
285 	ptem = kmem_zalloc(sizeof (struct tem_vt_state), KM_SLEEP);
286 	mutex_init(&ptem->tvs_lock, (char *)NULL, MUTEX_DRIVER, NULL);
287 
288 	mutex_enter(&tems.ts_lock);
289 	mutex_enter(&ptem->tvs_lock);
290 
291 	ptem->tvs_isactive = B_FALSE;
292 	ptem->tvs_fbmode = KD_TEXT;
293 	ptem->tvs_queue = rq;
294 
295 	/*
296 	 * A tem is regarded as initialized only after tem_internal_init(),
297 	 * will be set at the end of tem_internal_init().
298 	 */
299 	ptem->tvs_initialized = B_FALSE;
300 
301 
302 	if (!tems.ts_initialized) {
303 		/*
304 		 * Only happens during early console configuration.
305 		 */
306 		tem_add(ptem);
307 		mutex_exit(&ptem->tvs_lock);
308 		mutex_exit(&tems.ts_lock);
309 		return ((tem_vt_state_t)ptem);
310 	}
311 
312 	tem_internal_init(ptem, credp, B_TRUE, B_FALSE);
313 	tem_add(ptem);
314 	mutex_exit(&ptem->tvs_lock);
315 	mutex_exit(&tems.ts_lock);
316 
317 	return ((tem_vt_state_t)ptem);
318 }
319 
320 /*
321  * re-init the tem after video mode has changed and tems_info has
322  * been re-inited. The lock is already held.
323  */
324 static void
325 tem_reinit(struct tem_vt_state *tem, boolean_t reset_display)
326 {
327 	ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock));
328 
329 	tem_free_buf(tem); /* only free virtual buffers */
330 
331 	/* reserve color */
332 	tem_internal_init(tem, kcred, B_FALSE, reset_display);
333 }
334 
335 static void
336 tem_free_buf(struct tem_vt_state *tem)
337 {
338 	ASSERT(tem != NULL && MUTEX_HELD(&tem->tvs_lock));
339 
340 	if (tem->tvs_outbuf != NULL)
341 		kmem_free(tem->tvs_outbuf, tem->tvs_outbuf_size);
342 	if (tem->tvs_pix_data != NULL)
343 		kmem_free(tem->tvs_pix_data, tem->tvs_pix_data_size);
344 	if (tem->tvs_screen_buf != NULL)
345 		kmem_free(tem->tvs_screen_buf, tem->tvs_screen_buf_size);
346 	if (tem->tvs_screen_rows != NULL) {
347 		kmem_free(tem->tvs_screen_rows, tem->tvs_screen_history_size *
348 		    sizeof (term_char_t *));
349 	}
350 	if (tem->tvs_tabs != NULL) {
351 		kmem_free(tem->tvs_tabs, tem->tvs_maxtab *
352 		    sizeof (*tem->tvs_tabs));
353 	}
354 }
355 
356 void
357 tem_destroy(tem_vt_state_t tem_arg, cred_t *credp)
358 {
359 	struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
360 
361 	mutex_enter(&tems.ts_lock);
362 	mutex_enter(&tem->tvs_lock);
363 
364 	if (tem->tvs_isactive && tem->tvs_fbmode == KD_TEXT)
365 		tem_safe_blank_screen(tem, credp, CALLED_FROM_NORMAL);
366 
367 	tem_free_buf(tem);
368 	tem_rm(tem);
369 
370 	if (tems.ts_active == tem)
371 		tems.ts_active = NULL;
372 
373 	mutex_exit(&tem->tvs_lock);
374 	mutex_exit(&tems.ts_lock);
375 
376 	kmem_free(tem, sizeof (struct tem_vt_state));
377 }
378 
379 static int
380 tems_failed(cred_t *credp, boolean_t finish_ioctl)
381 {
382 	int	lyr_rval;
383 
384 	ASSERT(MUTEX_HELD(&tems.ts_lock));
385 
386 	if (finish_ioctl)
387 		(void) ldi_ioctl(tems.ts_hdl, VIS_DEVFINI, 0,
388 		    FWRITE | FKIOCTL, credp, &lyr_rval);
389 
390 	(void) ldi_close(tems.ts_hdl, 0, credp);
391 	tems.ts_hdl = NULL;
392 	return (ENXIO);
393 }
394 
395 /*
396  * only called once during boot
397  */
398 int
399 tem_info_init(char *pathname, cred_t *credp)
400 {
401 	int			lyr_rval, ret;
402 	struct vis_devinit	temargs;
403 	char			*pathbuf;
404 	size_t height = 0;
405 	size_t width = 0;
406 	struct tem_vt_state *p;
407 
408 	mutex_enter(&tems.ts_lock);
409 
410 	if (tems.ts_initialized) {
411 		mutex_exit(&tems.ts_lock);
412 		return (0);
413 	}
414 
415 	/*
416 	 * Open the layered device using the devfs physical device name
417 	 * after adding the /devices prefix.
418 	 */
419 	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
420 	(void) strcpy(pathbuf, "/devices");
421 	if (i_ddi_prompath_to_devfspath(pathname,
422 	    pathbuf + strlen("/devices")) != DDI_SUCCESS) {
423 		cmn_err(CE_WARN, "terminal-emulator:  path conversion error");
424 		kmem_free(pathbuf, MAXPATHLEN);
425 
426 		mutex_exit(&tems.ts_lock);
427 		return (ENXIO);
428 	}
429 	if (ldi_open_by_name(pathbuf, FWRITE, credp,
430 	    &tems.ts_hdl, term_li) != 0) {
431 		cmn_err(CE_WARN, "terminal-emulator:  device path open error");
432 		kmem_free(pathbuf, MAXPATHLEN);
433 
434 		mutex_exit(&tems.ts_lock);
435 		return (ENXIO);
436 	}
437 	kmem_free(pathbuf, MAXPATHLEN);
438 
439 	temargs.modechg_cb  = (vis_modechg_cb_t)tems_modechange_callback;
440 	temargs.modechg_arg = NULL;
441 
442 	/*
443 	 * Initialize the console and get the device parameters
444 	 */
445 	if (ldi_ioctl(tems.ts_hdl, VIS_DEVINIT,
446 	    (intptr_t)&temargs, FWRITE|FKIOCTL, credp, &lyr_rval) != 0) {
447 		cmn_err(CE_WARN, "terminal emulator: Compatible fb not found");
448 		ret = tems_failed(credp, B_FALSE);
449 		mutex_exit(&tems.ts_lock);
450 		return (ret);
451 	}
452 
453 	/* Make sure the fb driver and terminal emulator versions match */
454 	if (temargs.version != VIS_CONS_REV) {
455 		cmn_err(CE_WARN,
456 		    "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) "
457 		    "of console fb driver not supported", temargs.version);
458 		ret = tems_failed(credp, B_TRUE);
459 		mutex_exit(&tems.ts_lock);
460 		return (ret);
461 	}
462 
463 	if ((tems.ts_fb_polledio = temargs.polledio) == NULL) {
464 		cmn_err(CE_WARN, "terminal emulator: fb doesn't support polled "
465 		    "I/O");
466 		ret = tems_failed(credp, B_TRUE);
467 		mutex_exit(&tems.ts_lock);
468 		return (ret);
469 	}
470 
471 	/* other sanity checks */
472 	if (!((temargs.depth == 4) || (temargs.depth == 8) ||
473 	    (temargs.depth == 15) || (temargs.depth == 16) ||
474 	    (temargs.depth == 24) || (temargs.depth == 32))) {
475 		cmn_err(CE_WARN, "terminal emulator: unsupported depth");
476 		ret = tems_failed(credp, B_TRUE);
477 		mutex_exit(&tems.ts_lock);
478 		return (ret);
479 	}
480 
481 	if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) {
482 		cmn_err(CE_WARN, "terminal emulator: unsupported mode");
483 		ret = tems_failed(credp, B_TRUE);
484 		mutex_exit(&tems.ts_lock);
485 		return (ret);
486 	}
487 
488 	if ((temargs.mode == VIS_PIXEL) && plat_stdout_is_framebuffer())
489 		plat_tem_get_prom_size(&height, &width);
490 
491 	/*
492 	 * Initialize the common terminal emulator info
493 	 */
494 	tems_setup_terminal(&temargs, height, width);
495 
496 	tems_reset_colormap(credp, CALLED_FROM_NORMAL);
497 	tems_get_initial_color(&tems.ts_init_color);
498 
499 	tems.ts_initialized = 1; /* initialization flag */
500 
501 	for (p = list_head(&tems.ts_list); p != NULL;
502 	    p = list_next(&tems.ts_list, p)) {
503 		mutex_enter(&p->tvs_lock);
504 		tem_internal_init(p, credp, B_TRUE, B_FALSE);
505 		tem_align(p, credp, CALLED_FROM_NORMAL);
506 		mutex_exit(&p->tvs_lock);
507 	}
508 
509 	mutex_exit(&tems.ts_lock);
510 	return (0);
511 }
512 
513 #define	TEMS_DEPTH_DIFF		0x01
514 #define	TEMS_DIMENSION_DIFF	0x02
515 
516 static uchar_t
517 tems_check_videomode(struct vis_devinit *tp)
518 {
519 	uchar_t result = 0;
520 
521 	if (tems.ts_pdepth != tp->depth)
522 		result |= TEMS_DEPTH_DIFF;
523 
524 	if (tp->mode == VIS_TEXT) {
525 		if (tems.ts_c_dimension.width != tp->width ||
526 		    tems.ts_c_dimension.height != tp->height)
527 			result |= TEMS_DIMENSION_DIFF;
528 	} else {
529 		if (tems.ts_p_dimension.width != tp->width ||
530 		    tems.ts_p_dimension.height != tp->height)
531 			result |= TEMS_DIMENSION_DIFF;
532 	}
533 
534 	return (result);
535 }
536 
537 static void
538 tems_setup_font(screen_size_t height, screen_size_t width)
539 {
540 	bitmap_data_t *font_data;
541 	int i;
542 
543 	/*
544 	 * set_font() will select an appropriate sized font for
545 	 * the number of rows and columns selected. If we don't
546 	 * have a font that will fit, then it will use the
547 	 * default builtin font and adjust the rows and columns
548 	 * to fit on the screen.
549 	 */
550 	font_data = set_font(&tems.ts_c_dimension.height,
551 	    &tems.ts_c_dimension.width, height, width);
552 
553 	/*
554 	 * To use loaded font, we assign the loaded font data to tems.ts_font.
555 	 * In case of next load, the previously loaded data is freed
556 	 * when loading the new font.
557 	 */
558 	for (i = 0; i < VFNT_MAPS; i++) {
559 		tems.ts_font.vf_map[i] =
560 		    font_data->font->vf_map[i];
561 		tems.ts_font.vf_map_count[i] =
562 		    font_data->font->vf_map_count[i];
563 	}
564 
565 	tems.ts_font.vf_bytes = font_data->font->vf_bytes;
566 	tems.ts_font.vf_width = font_data->font->vf_width;
567 	tems.ts_font.vf_height = font_data->font->vf_height;
568 }
569 
570 static void
571 tems_setup_terminal(struct vis_devinit *tp, size_t height, size_t width)
572 {
573 	int old_blank_buf_size = tems.ts_c_dimension.width *
574 	    sizeof (*tems.ts_blank_line);
575 
576 	ASSERT(MUTEX_HELD(&tems.ts_lock));
577 
578 	tems.ts_pdepth = tp->depth;
579 	tems.ts_linebytes = tp->linebytes;
580 	tems.ts_display_mode = tp->mode;
581 	tems.ts_color_map = tp->color_map;
582 
583 	switch (tp->mode) {
584 	case VIS_TEXT:
585 		tems.ts_p_dimension.width = 0;
586 		tems.ts_p_dimension.height = 0;
587 		tems.ts_c_dimension.width = tp->width;
588 		tems.ts_c_dimension.height = tp->height;
589 		tems.ts_callbacks = &tem_safe_text_callbacks;
590 
591 		tems_setup_font(16 * tp->height + BORDER_PIXELS,
592 		    8 * tp->width + BORDER_PIXELS);
593 
594 		break;
595 
596 	case VIS_PIXEL:
597 		/*
598 		 * First check to see if the user has specified a screen size.
599 		 * If so, use those values.  Else use 34x80 as the default.
600 		 */
601 		if (width == 0) {
602 			width = TEM_DEFAULT_COLS;
603 			height = TEM_DEFAULT_ROWS;
604 		}
605 		tems.ts_c_dimension.height = (screen_size_t)height;
606 		tems.ts_c_dimension.width = (screen_size_t)width;
607 		tems.ts_p_dimension.height = tp->height;
608 		tems.ts_p_dimension.width = tp->width;
609 		tems.ts_callbacks = &tem_safe_pix_callbacks;
610 
611 		tems_setup_font(tp->height, tp->width);
612 
613 		tems.ts_p_offset.y = (tems.ts_p_dimension.height -
614 		    (tems.ts_c_dimension.height * tems.ts_font.vf_height)) / 2;
615 		tems.ts_p_offset.x = (tems.ts_p_dimension.width -
616 		    (tems.ts_c_dimension.width * tems.ts_font.vf_width)) / 2;
617 
618 		tems.ts_pix_data_size =
619 		    tems.ts_font.vf_width * tems.ts_font.vf_height;
620 		tems.ts_pix_data_size *= 4;
621 		tems.ts_pdepth = tp->depth;
622 
623 		break;
624 	}
625 
626 	/* Now virtual cls also uses the blank_line buffer */
627 	if (tems.ts_blank_line)
628 		kmem_free(tems.ts_blank_line, old_blank_buf_size);
629 
630 	tems.ts_blank_line = kmem_alloc(tems.ts_c_dimension.width *
631 	    sizeof (*tems.ts_blank_line), KM_SLEEP);
632 }
633 
634 /*
635  * This is a callback function that we register with the frame
636  * buffer driver layered underneath.  It gets invoked from
637  * the underlying frame buffer driver to reconfigure the terminal
638  * emulator to a new screen size and depth in conjunction with
639  * framebuffer videomode changes.
640  * Here we keep the foreground/background color and attributes,
641  * which may be different with the initial settings, so that
642  * the color won't change while the framebuffer videomode changes.
643  * And we also reset the kernel terminal emulator and clear the
644  * whole screen.
645  */
646 /* ARGSUSED */
647 void
648 tems_modechange_callback(struct vis_modechg_arg *arg,
649     struct vis_devinit *devinit)
650 {
651 	uchar_t diff;
652 	struct tem_vt_state *p;
653 	tem_modechg_cb_t cb;
654 	tem_modechg_cb_arg_t cb_arg;
655 
656 	ASSERT(!(list_is_empty(&tems.ts_list)));
657 
658 	mutex_enter(&tems.ts_lock);
659 
660 	/*
661 	 * currently only for pixel mode
662 	 */
663 	diff = tems_check_videomode(devinit);
664 	if (diff == 0) {
665 		mutex_exit(&tems.ts_lock);
666 		return;
667 	}
668 
669 	diff = diff & TEMS_DIMENSION_DIFF;
670 
671 	if (diff == 0) {
672 		/*
673 		 * Only need to reinit the active tem.
674 		 */
675 		struct tem_vt_state *active = tems.ts_active;
676 		tems.ts_pdepth = devinit->depth;
677 
678 		mutex_enter(&active->tvs_lock);
679 		ASSERT(active->tvs_isactive);
680 		tem_reinit(active, B_TRUE);
681 		mutex_exit(&active->tvs_lock);
682 
683 		mutex_exit(&tems.ts_lock);
684 		return;
685 	}
686 
687 	tems_setup_terminal(devinit, tems.ts_c_dimension.height,
688 	    tems.ts_c_dimension.width);
689 
690 	for (p = list_head(&tems.ts_list); p != NULL;
691 	    p = list_next(&tems.ts_list, p)) {
692 		mutex_enter(&p->tvs_lock);
693 		tem_reinit(p, p->tvs_isactive);
694 		mutex_exit(&p->tvs_lock);
695 	}
696 
697 
698 	if (tems.ts_modechg_cb == NULL) {
699 		mutex_exit(&tems.ts_lock);
700 		return;
701 	}
702 
703 	cb = tems.ts_modechg_cb;
704 	cb_arg = tems.ts_modechg_arg;
705 
706 	/*
707 	 * Release the lock while doing callback.
708 	 */
709 	mutex_exit(&tems.ts_lock);
710 	cb(cb_arg);
711 }
712 
713 /*
714  * This function is used to clear entire screen via the underlying framebuffer
715  * driver.
716  */
717 int
718 tems_cls_layered(struct vis_consclear *pda,
719     cred_t *credp)
720 {
721 	int rval;
722 
723 	(void) ldi_ioctl(tems.ts_hdl, VIS_CONSCLEAR,
724 	    (intptr_t)pda, FKIOCTL, credp, &rval);
725 	return (rval);
726 }
727 
728 /*
729  * This function is used to display a rectangular blit of data
730  * of a given size and location via the underlying framebuffer driver.
731  * The blit can be as small as a pixel or as large as the screen.
732  */
733 void
734 tems_display_layered(struct vis_consdisplay *pda,
735     cred_t *credp)
736 {
737 	int rval;
738 
739 	(void) ldi_ioctl(tems.ts_hdl, VIS_CONSDISPLAY,
740 	    (intptr_t)pda, FKIOCTL, credp, &rval);
741 }
742 
743 /*
744  * This function is used to invoke a block copy operation in the
745  * underlying framebuffer driver.  Rectangle copies are how scrolling
746  * is implemented, as well as horizontal text shifting escape seqs.
747  * such as from vi when deleting characters and words.
748  */
749 void
750 tems_copy_layered(struct vis_conscopy *pma,
751     cred_t *credp)
752 {
753 	int rval;
754 
755 	(void) ldi_ioctl(tems.ts_hdl, VIS_CONSCOPY,
756 	    (intptr_t)pma, FKIOCTL, credp, &rval);
757 }
758 
759 /*
760  * This function is used to show or hide a rectangluar monochrom
761  * pixel inverting, text block cursor via the underlying framebuffer.
762  */
763 void
764 tems_cursor_layered(struct vis_conscursor *pca,
765     cred_t *credp)
766 {
767 	int rval;
768 
769 	(void) ldi_ioctl(tems.ts_hdl, VIS_CONSCURSOR,
770 	    (intptr_t)pca, FKIOCTL, credp, &rval);
771 }
772 
773 static void
774 tem_kdsetmode(int mode, cred_t *credp)
775 {
776 	int rval;
777 
778 	(void) ldi_ioctl(tems.ts_hdl, KDSETMODE,
779 	    (intptr_t)mode, FKIOCTL, credp, &rval);
780 
781 }
782 
783 static void
784 tems_reset_colormap(cred_t *credp, enum called_from called_from)
785 {
786 	struct vis_cmap cm;
787 	int rval;
788 
789 	if (called_from == CALLED_FROM_STANDALONE)
790 		return;
791 
792 	switch (tems.ts_pdepth) {
793 	case 8:
794 		cm.index = 0;
795 		cm.count = 16;
796 		cm.red   = (uint8_t *)cmap4_to_24.red;
797 		cm.blue  = (uint8_t *)cmap4_to_24.blue;
798 		cm.green = (uint8_t *)cmap4_to_24.green;
799 		(void) ldi_ioctl(tems.ts_hdl, VIS_PUTCMAP, (intptr_t)&cm,
800 		    FKIOCTL, credp, &rval);
801 		break;
802 	}
803 }
804 
805 void
806 tem_get_size(ushort_t *r, ushort_t *c, ushort_t *x, ushort_t *y)
807 {
808 	mutex_enter(&tems.ts_lock);
809 	*r = (ushort_t)tems.ts_c_dimension.height;
810 	*c = (ushort_t)tems.ts_c_dimension.width;
811 	*x = (ushort_t)tems.ts_p_dimension.width;
812 	*y = (ushort_t)tems.ts_p_dimension.height;
813 	mutex_exit(&tems.ts_lock);
814 }
815 
816 void
817 tem_register_modechg_cb(tem_modechg_cb_t func, tem_modechg_cb_arg_t arg)
818 {
819 	mutex_enter(&tems.ts_lock);
820 
821 	tems.ts_modechg_cb = func;
822 	tems.ts_modechg_arg = arg;
823 
824 	mutex_exit(&tems.ts_lock);
825 }
826 
827 /*
828  * This function is to scroll up the OBP output, which has
829  * different screen height and width with our kernel console.
830  */
831 static void
832 tem_prom_scroll_up(struct tem_vt_state *tem, int nrows, cred_t *credp,
833     enum called_from called_from)
834 {
835 	struct vis_conscopy	ma;
836 	int	ncols, width;
837 
838 	/* copy */
839 	ma.s_row = nrows * tems.ts_font.vf_height;
840 	ma.e_row = tems.ts_p_dimension.height - 1;
841 	ma.t_row = 0;
842 
843 	ma.s_col = 0;
844 	ma.e_col = tems.ts_p_dimension.width - 1;
845 	ma.t_col = 0;
846 
847 	tems_safe_copy(&ma, credp, called_from);
848 
849 	/* clear */
850 	width = tems.ts_font.vf_width;
851 	ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
852 
853 	tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y,
854 	    0, ncols, 0, B_TRUE, credp, called_from);
855 }
856 
857 #define	PROM_DEFAULT_FONT_HEIGHT	22
858 #define	PROM_DEFAULT_WINDOW_TOP		0x8a
859 
860 /*
861  * This function is to compute the starting row of the console, according to
862  * PROM cursor's position. Here we have to take different fonts into account.
863  */
864 static int
865 tem_adjust_row(struct tem_vt_state *tem, int prom_row, cred_t *credp,
866     enum called_from called_from)
867 {
868 	int	tem_row;
869 	int	tem_y;
870 	int	prom_charheight = 0;
871 	int	prom_window_top = 0;
872 	int	scroll_up_lines;
873 
874 	if (tems.ts_display_mode == VIS_TEXT)
875 		return (prom_row);
876 
877 	plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top);
878 	if (prom_charheight == 0)
879 		prom_charheight = PROM_DEFAULT_FONT_HEIGHT;
880 	if (prom_window_top == 0)
881 		prom_window_top = PROM_DEFAULT_WINDOW_TOP;
882 
883 	tem_y = (prom_row + 1) * prom_charheight + prom_window_top -
884 	    tems.ts_p_offset.y;
885 	tem_row = (tem_y + tems.ts_font.vf_height - 1) /
886 	    tems.ts_font.vf_height - 1;
887 
888 	if (tem_row < 0) {
889 		tem_row = 0;
890 	} else if (tem_row >= (tems.ts_c_dimension.height - 1)) {
891 		/*
892 		 * Scroll up the prom outputs if the PROM cursor's position is
893 		 * below our tem's lower boundary.
894 		 */
895 		scroll_up_lines = tem_row -
896 		    (tems.ts_c_dimension.height - 1);
897 		tem_prom_scroll_up(tem, scroll_up_lines, credp, called_from);
898 		tem_row = tems.ts_c_dimension.height - 1;
899 	}
900 
901 	return (tem_row);
902 }
903 
904 void
905 tem_align(struct tem_vt_state *tem, cred_t *credp,
906     enum called_from called_from)
907 {
908 	uint32_t row = 0;
909 	uint32_t col = 0;
910 
911 	plat_tem_hide_prom_cursor();
912 
913 	/*
914 	 * We are getting the current cursor position in pixel
915 	 * mode so that we don't over-write the console output
916 	 * during boot.
917 	 */
918 	plat_tem_get_prom_pos(&row, &col);
919 
920 	/*
921 	 * Adjust the row if necessary when the font of our
922 	 * kernel console tem is different with that of prom
923 	 * tem.
924 	 */
925 	row = tem_adjust_row(tem, row, credp, called_from);
926 
927 	/* first line of our kernel console output */
928 	tem->tvs_first_line = row + 1;
929 
930 	/* re-set and align cursor position */
931 	tem->tvs_s_cursor.row = tem->tvs_c_cursor.row =
932 	    (screen_pos_t)row;
933 	tem->tvs_s_cursor.col = tem->tvs_c_cursor.col = 0;
934 
935 	/*
936 	 * When tem is starting up, part of the screen is filled
937 	 * with information from boot loader and early boot.
938 	 * For tem, the screen content above current cursor
939 	 * should be treated as image.
940 	 */
941 	for (; row > 0; row--) {
942 		for (col = 0; col < tems.ts_c_dimension.width; col++) {
943 			tem->tvs_screen_rows[row][col].tc_char =
944 			    TEM_ATTR(TEM_ATTR_IMAGE);
945 		}
946 	}
947 }
948 
949 static void
950 tems_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen)
951 {
952 	int i_inverse = 0;
953 	int i_inverse_screen = 0;
954 
955 	plat_tem_get_inverses(&i_inverse, &i_inverse_screen);
956 
957 	*p_inverse = (i_inverse == 0) ? B_FALSE : B_TRUE;
958 	*p_inverse_screen = (i_inverse_screen == 0) ? B_FALSE : B_TRUE;
959 }
960 
961 /*
962  * Get the foreground/background color and attributes from the initial
963  * PROM, so that our kernel console can keep the same visual behaviour.
964  */
965 static void
966 tems_get_initial_color(tem_color_t *pcolor)
967 {
968 	boolean_t inverse, inverse_screen;
969 	unsigned short  flags = 0;
970 	uint8_t fg, bg;
971 
972 	fg = DEFAULT_ANSI_FOREGROUND;
973 	bg = DEFAULT_ANSI_BACKGROUND;
974 #ifndef _HAVE_TEM_FIRMWARE
975 	/*
976 	 * _HAVE_TEM_FIRMWARE is defined on SPARC, at this time, the
977 	 * plat_tem_get_colors() is implemented only on x86.
978 	 */
979 
980 	plat_tem_get_colors(&fg, &bg);
981 #endif
982 	pcolor->fg_color.n = fg;
983 	pcolor->bg_color.n = bg;
984 
985 	tems_get_inverses(&inverse, &inverse_screen);
986 	if (inverse)
987 		flags |= TEM_ATTR_REVERSE;
988 	if (inverse_screen)
989 		flags |= TEM_ATTR_SCREEN_REVERSE;
990 
991 #ifdef _HAVE_TEM_FIRMWARE
992 	if (flags != 0) {
993 		/*
994 		 * If either reverse flag is set, the screen is in
995 		 * white-on-black mode.  We set the bold flag to
996 		 * improve readability.
997 		 */
998 		flags |= TEM_ATTR_BOLD;
999 	} else {
1000 		/*
1001 		 * Otherwise, the screen is in black-on-white mode.
1002 		 * The SPARC PROM console, which starts in this mode,
1003 		 * uses the bright white background colour so we
1004 		 * match it here.
1005 		 */
1006 		if (pcolor->bg_color.n == ANSI_COLOR_WHITE)
1007 			flags |= TEM_ATTR_BRIGHT_BG;
1008 	}
1009 #else
1010 	if (flags != 0) {
1011 		if (pcolor->fg_color.n == ANSI_COLOR_WHITE)
1012 			flags |= TEM_ATTR_BRIGHT_BG;
1013 
1014 		if (pcolor->fg_color.n == ANSI_COLOR_BLACK)
1015 			flags &= ~TEM_ATTR_BRIGHT_BG;
1016 	} else {
1017 		/*
1018 		 * In case of black on white we want bright white for BG.
1019 		 */
1020 		if (pcolor->bg_color.n == ANSI_COLOR_WHITE)
1021 			flags |= TEM_ATTR_BRIGHT_BG;
1022 	}
1023 #endif
1024 
1025 	pcolor->a_flags = flags;
1026 }
1027 
1028 uchar_t
1029 tem_get_fbmode(tem_vt_state_t tem_arg)
1030 {
1031 	struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
1032 
1033 	uchar_t fbmode;
1034 
1035 	mutex_enter(&tem->tvs_lock);
1036 	fbmode = tem->tvs_fbmode;
1037 	mutex_exit(&tem->tvs_lock);
1038 
1039 	return (fbmode);
1040 }
1041 
1042 void
1043 tem_set_fbmode(tem_vt_state_t tem_arg, uchar_t fbmode, cred_t *credp)
1044 {
1045 	struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
1046 
1047 	mutex_enter(&tems.ts_lock);
1048 	mutex_enter(&tem->tvs_lock);
1049 
1050 	if (fbmode == tem->tvs_fbmode) {
1051 		mutex_exit(&tem->tvs_lock);
1052 		mutex_exit(&tems.ts_lock);
1053 		return;
1054 	}
1055 
1056 	tem->tvs_fbmode = fbmode;
1057 
1058 	if (tem->tvs_isactive) {
1059 		tem_kdsetmode(tem->tvs_fbmode, credp);
1060 		if (fbmode == KD_TEXT)
1061 			tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL);
1062 	}
1063 
1064 	mutex_exit(&tem->tvs_lock);
1065 	mutex_exit(&tems.ts_lock);
1066 }
1067 
1068 void
1069 tem_activate(tem_vt_state_t tem_arg, boolean_t unblank, cred_t *credp)
1070 {
1071 	struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
1072 
1073 	mutex_enter(&tems.ts_lock);
1074 	tems.ts_active = tem;
1075 
1076 	mutex_enter(&tem->tvs_lock);
1077 	tem->tvs_isactive = B_TRUE;
1078 
1079 	tem_kdsetmode(tem->tvs_fbmode, credp);
1080 
1081 	if (unblank)
1082 		tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL);
1083 
1084 	mutex_exit(&tem->tvs_lock);
1085 	mutex_exit(&tems.ts_lock);
1086 }
1087 
1088 void
1089 tem_switch(tem_vt_state_t tem_arg1, tem_vt_state_t tem_arg2, cred_t *credp)
1090 {
1091 	struct tem_vt_state *cur = (struct tem_vt_state *)tem_arg1;
1092 	struct tem_vt_state *tobe = (struct tem_vt_state *)tem_arg2;
1093 
1094 	mutex_enter(&tems.ts_lock);
1095 	mutex_enter(&tobe->tvs_lock);
1096 	mutex_enter(&cur->tvs_lock);
1097 
1098 	tems.ts_active = tobe;
1099 	cur->tvs_isactive = B_FALSE;
1100 	tobe->tvs_isactive = B_TRUE;
1101 
1102 	mutex_exit(&cur->tvs_lock);
1103 
1104 	if (cur->tvs_fbmode != tobe->tvs_fbmode)
1105 		tem_kdsetmode(tobe->tvs_fbmode, credp);
1106 
1107 	if (tobe->tvs_fbmode == KD_TEXT)
1108 		tem_safe_unblank_screen(tobe, credp, CALLED_FROM_NORMAL);
1109 
1110 	mutex_exit(&tobe->tvs_lock);
1111 	mutex_exit(&tems.ts_lock);
1112 }
1113