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