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