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.
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.
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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * ANSI terminal emulator module; parse ANSI X3.64 escape sequences and
31 * How Virtual Terminal Emulator Works:
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
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).
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().
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.
63 #include <sys/types.h>
66 #include <sys/errno.h>
70 #include <sys/ascii.h>
71 #include <sys/consdev.h>
75 #include <sys/modctl.h>
76 #include <sys/strsubr.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>
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>
93 #include <sys/sysmacros.h>
95 #include <sys/t_lock.h>
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
*);
102 static void tems_reset_colormap(cred_t
*, enum called_from
);
104 static void tem_free_buf(struct tem_vt_state
*);
105 static void tem_internal_init(struct tem_vt_state
*, cred_t
*, boolean_t
,
107 static void tems_get_initial_color(tem_color_t
*pcolor
);
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
))
116 extern struct mod_ops mod_miscops
;
118 static struct modlmisc modlmisc
= {
119 &mod_miscops
, /* modops */
120 "ANSI Terminal Emulator", /* name */
123 static struct modlinkage modlinkage
= {
124 MODREV_1
, (void *)&modlmisc
, NULL
131 ret
= mod_install(&modlinkage
);
134 ret
= ldi_ident_from_mod(&modlinkage
, &term_li
);
136 (void) mod_remove(&modlinkage
);
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
;
153 ret
= mod_remove(&modlinkage
);
155 ldi_ident_release(term_li
);
162 _info(struct modinfo
*modinfop
)
164 return (mod_info(&modlinkage
, modinfop
));
168 tem_add(struct tem_vt_state
*tem
)
170 ASSERT(MUTEX_HELD(&tems
.ts_lock
) && MUTEX_HELD(&tem
->tvs_lock
));
172 list_insert_head(&tems
.ts_list
, tem
);
176 tem_rm(struct tem_vt_state
*tem
)
178 ASSERT(MUTEX_HELD(&tems
.ts_lock
) && MUTEX_HELD(&tem
->tvs_lock
));
180 list_remove(&tems
.ts_list
, tem
);
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.
188 tem_write(tem_vt_state_t tem_arg
, uchar_t
*buf
, ssize_t len
, cred_t
*credp
)
190 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
192 mutex_enter(&tems
.ts_lock
);
193 mutex_enter(&tem
->tvs_lock
);
195 if (!tem
->tvs_initialized
) {
196 mutex_exit(&tem
->tvs_lock
);
197 mutex_exit(&tems
.ts_lock
);
201 tem_safe_check_first_time(tem
, credp
, CALLED_FROM_NORMAL
);
202 tem_safe_terminal_emulate(tem
, buf
, len
, credp
, CALLED_FROM_NORMAL
);
204 mutex_exit(&tem
->tvs_lock
);
205 mutex_exit(&tems
.ts_lock
);
209 tem_internal_init(struct tem_vt_state
*ptem
, cred_t
*credp
,
210 boolean_t init_color
, boolean_t clear_screen
)
217 size_t tc_size
= sizeof (text_color_t
);
219 ASSERT(MUTEX_HELD(&tems
.ts_lock
) && MUTEX_HELD(&ptem
->tvs_lock
));
221 if (tems
.ts_display_mode
== VIS_PIXEL
) {
222 ptem
->tvs_pix_data_size
= tems
.ts_pix_data_size
;
224 kmem_alloc(ptem
->tvs_pix_data_size
, KM_SLEEP
);
227 ptem
->tvs_outbuf_size
= tems
.ts_c_dimension
.width
;
229 (unsigned char *)kmem_alloc(ptem
->tvs_outbuf_size
, KM_SLEEP
);
231 width
= tems
.ts_c_dimension
.width
;
232 height
= tems
.ts_c_dimension
.height
;
233 ptem
->tvs_screen_buf_size
= width
* height
;
234 ptem
->tvs_screen_buf
=
235 (unsigned char *)kmem_alloc(width
* height
, KM_SLEEP
);
237 total
= width
* height
* tc_size
;
238 ptem
->tvs_fg_buf
= (text_color_t
*)kmem_alloc(total
, KM_SLEEP
);
239 ptem
->tvs_bg_buf
= (text_color_t
*)kmem_alloc(total
, KM_SLEEP
);
240 ptem
->tvs_color_buf_size
= total
;
242 tem_safe_reset_display(ptem
, credp
, CALLED_FROM_NORMAL
,
243 clear_screen
, init_color
);
245 ptem
->tvs_utf8_left
= 0;
246 ptem
->tvs_utf8_partial
= 0;
248 tem_safe_get_color(ptem
, &fg
, &bg
, TEM_ATTR_SCREEN_REVERSE
);
249 for (i
= 0; i
< height
; i
++)
250 for (j
= 0; j
< width
; j
++) {
251 ptem
->tvs_screen_buf
[i
* width
+ j
] = ' ';
252 ptem
->tvs_fg_buf
[(i
* width
+j
) * tc_size
] = fg
;
253 ptem
->tvs_bg_buf
[(i
* width
+j
) * tc_size
] = bg
;
257 ptem
->tvs_initialized
= 1;
261 tem_initialized(tem_vt_state_t tem_arg
)
263 struct tem_vt_state
*ptem
= (struct tem_vt_state
*)tem_arg
;
266 mutex_enter(&ptem
->tvs_lock
);
267 ret
= ptem
->tvs_initialized
;
268 mutex_exit(&ptem
->tvs_lock
);
274 tem_init(cred_t
*credp
)
276 struct tem_vt_state
*ptem
;
278 ptem
= kmem_zalloc(sizeof (struct tem_vt_state
), KM_SLEEP
);
279 mutex_init(&ptem
->tvs_lock
, (char *)NULL
, MUTEX_DRIVER
, NULL
);
281 mutex_enter(&tems
.ts_lock
);
282 mutex_enter(&ptem
->tvs_lock
);
284 ptem
->tvs_isactive
= B_FALSE
;
285 ptem
->tvs_fbmode
= KD_TEXT
;
288 * A tem is regarded as initialized only after tem_internal_init(),
289 * will be set at the end of tem_internal_init().
291 ptem
->tvs_initialized
= 0;
294 if (!tems
.ts_initialized
) {
296 * Only happens during early console configuration.
299 mutex_exit(&ptem
->tvs_lock
);
300 mutex_exit(&tems
.ts_lock
);
301 return ((tem_vt_state_t
)ptem
);
304 tem_internal_init(ptem
, credp
, B_TRUE
, B_FALSE
);
306 mutex_exit(&ptem
->tvs_lock
);
307 mutex_exit(&tems
.ts_lock
);
309 return ((tem_vt_state_t
)ptem
);
313 * re-init the tem after video mode has changed and tems_info has
314 * been re-inited. The lock is already held.
317 tem_reinit(struct tem_vt_state
*tem
, boolean_t reset_display
)
319 ASSERT(MUTEX_HELD(&tems
.ts_lock
) && MUTEX_HELD(&tem
->tvs_lock
));
321 tem_free_buf(tem
); /* only free virtual buffers */
324 tem_internal_init(tem
, kcred
, B_FALSE
, reset_display
);
328 tem_free_buf(struct tem_vt_state
*tem
)
330 ASSERT(tem
!= NULL
&& MUTEX_HELD(&tem
->tvs_lock
));
332 if (tem
->tvs_outbuf
!= NULL
)
333 kmem_free(tem
->tvs_outbuf
, tem
->tvs_outbuf_size
);
334 if (tem
->tvs_pix_data
!= NULL
)
335 kmem_free(tem
->tvs_pix_data
, tem
->tvs_pix_data_size
);
336 if (tem
->tvs_screen_buf
!= NULL
)
337 kmem_free(tem
->tvs_screen_buf
, tem
->tvs_screen_buf_size
);
338 if (tem
->tvs_fg_buf
!= NULL
)
339 kmem_free(tem
->tvs_fg_buf
, tem
->tvs_color_buf_size
);
340 if (tem
->tvs_bg_buf
!= NULL
)
341 kmem_free(tem
->tvs_bg_buf
, tem
->tvs_color_buf_size
);
345 tem_destroy(tem_vt_state_t tem_arg
, cred_t
*credp
)
347 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
349 mutex_enter(&tems
.ts_lock
);
350 mutex_enter(&tem
->tvs_lock
);
352 if (tem
->tvs_isactive
&& tem
->tvs_fbmode
== KD_TEXT
)
353 tem_safe_blank_screen(tem
, credp
, CALLED_FROM_NORMAL
);
358 if (tems
.ts_active
== tem
)
359 tems
.ts_active
= NULL
;
361 mutex_exit(&tem
->tvs_lock
);
362 mutex_exit(&tems
.ts_lock
);
364 kmem_free(tem
, sizeof (struct tem_vt_state
));
368 tems_failed(cred_t
*credp
, boolean_t finish_ioctl
)
372 ASSERT(MUTEX_HELD(&tems
.ts_lock
));
375 (void) ldi_ioctl(tems
.ts_hdl
, VIS_DEVFINI
, 0,
376 FWRITE
|FKIOCTL
, credp
, &lyr_rval
);
378 (void) ldi_close(tems
.ts_hdl
, NULL
, credp
);
384 * only called once during boot
387 tem_info_init(char *pathname
, cred_t
*credp
)
390 struct vis_devinit temargs
;
394 struct tem_vt_state
*p
;
396 mutex_enter(&tems
.ts_lock
);
398 if (tems
.ts_initialized
) {
399 mutex_exit(&tems
.ts_lock
);
404 * Open the layered device using the devfs physical device name
405 * after adding the /devices prefix.
407 pathbuf
= kmem_alloc(MAXPATHLEN
, KM_SLEEP
);
408 (void) strcpy(pathbuf
, "/devices");
409 if (i_ddi_prompath_to_devfspath(pathname
,
410 pathbuf
+ strlen("/devices")) != DDI_SUCCESS
) {
411 cmn_err(CE_WARN
, "terminal-emulator: path conversion error");
412 kmem_free(pathbuf
, MAXPATHLEN
);
414 mutex_exit(&tems
.ts_lock
);
417 if (ldi_open_by_name(pathbuf
, FWRITE
, credp
,
418 &tems
.ts_hdl
, term_li
) != 0) {
419 cmn_err(CE_WARN
, "terminal-emulator: device path open error");
420 kmem_free(pathbuf
, MAXPATHLEN
);
422 mutex_exit(&tems
.ts_lock
);
425 kmem_free(pathbuf
, MAXPATHLEN
);
427 temargs
.modechg_cb
= (vis_modechg_cb_t
)tems_modechange_callback
;
428 temargs
.modechg_arg
= NULL
;
431 * Initialize the console and get the device parameters
433 if (ldi_ioctl(tems
.ts_hdl
, VIS_DEVINIT
,
434 (intptr_t)&temargs
, FWRITE
|FKIOCTL
, credp
, &lyr_rval
) != 0) {
435 cmn_err(CE_WARN
, "terminal emulator: Compatible fb not found");
436 ret
= tems_failed(credp
, B_FALSE
);
437 mutex_exit(&tems
.ts_lock
);
441 /* Make sure the fb driver and terminal emulator versions match */
442 if (temargs
.version
!= VIS_CONS_REV
) {
444 "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) "
445 "of console fb driver not supported", temargs
.version
);
446 ret
= tems_failed(credp
, B_TRUE
);
447 mutex_exit(&tems
.ts_lock
);
451 if ((tems
.ts_fb_polledio
= temargs
.polledio
) == NULL
) {
452 cmn_err(CE_WARN
, "terminal emulator: fb doesn't support polled "
454 ret
= tems_failed(credp
, B_TRUE
);
455 mutex_exit(&tems
.ts_lock
);
459 /* other sanity checks */
460 if (!((temargs
.depth
== 4) || (temargs
.depth
== 8) ||
461 (temargs
.depth
== 24) || (temargs
.depth
== 32))) {
462 cmn_err(CE_WARN
, "terminal emulator: unsupported depth");
463 ret
= tems_failed(credp
, B_TRUE
);
464 mutex_exit(&tems
.ts_lock
);
468 if ((temargs
.mode
!= VIS_TEXT
) && (temargs
.mode
!= VIS_PIXEL
)) {
469 cmn_err(CE_WARN
, "terminal emulator: unsupported mode");
470 ret
= tems_failed(credp
, B_TRUE
);
471 mutex_exit(&tems
.ts_lock
);
475 if ((temargs
.mode
== VIS_PIXEL
) && plat_stdout_is_framebuffer())
476 plat_tem_get_prom_size(&height
, &width
);
479 * Initialize the common terminal emulator info
481 tems_setup_terminal(&temargs
, height
, width
);
483 tems_reset_colormap(credp
, CALLED_FROM_NORMAL
);
484 tems_get_initial_color(&tems
.ts_init_color
);
486 tems
.ts_initialized
= 1; /* initialization flag */
488 for (p
= list_head(&tems
.ts_list
); p
!= NULL
;
489 p
= list_next(&tems
.ts_list
, p
)) {
490 mutex_enter(&p
->tvs_lock
);
491 tem_internal_init(p
, credp
, B_TRUE
, B_FALSE
);
492 if (temargs
.mode
== VIS_PIXEL
)
493 tem_pix_align(p
, credp
, CALLED_FROM_NORMAL
);
494 mutex_exit(&p
->tvs_lock
);
497 mutex_exit(&tems
.ts_lock
);
501 #define TEMS_DEPTH_DIFF 0x01
502 #define TEMS_DIMENSION_DIFF 0x02
505 tems_check_videomode(struct vis_devinit
*tp
)
509 if (tems
.ts_pdepth
!= tp
->depth
)
510 result
|= TEMS_DEPTH_DIFF
;
512 if (tp
->mode
== VIS_TEXT
) {
513 if (tems
.ts_c_dimension
.width
!= tp
->width
||
514 tems
.ts_c_dimension
.height
!= tp
->height
)
515 result
|= TEMS_DIMENSION_DIFF
;
517 if (tems
.ts_p_dimension
.width
!= tp
->width
||
518 tems
.ts_p_dimension
.height
!= tp
->height
)
519 result
|= TEMS_DIMENSION_DIFF
;
526 tems_setup_terminal(struct vis_devinit
*tp
, size_t height
, size_t width
)
529 int old_blank_buf_size
= tems
.ts_c_dimension
.width
;
531 ASSERT(MUTEX_HELD(&tems
.ts_lock
));
533 tems
.ts_pdepth
= tp
->depth
;
534 tems
.ts_linebytes
= tp
->linebytes
;
535 tems
.ts_display_mode
= tp
->mode
;
539 tems
.ts_p_dimension
.width
= 0;
540 tems
.ts_p_dimension
.height
= 0;
541 tems
.ts_c_dimension
.width
= tp
->width
;
542 tems
.ts_c_dimension
.height
= tp
->height
;
543 tems
.ts_callbacks
= &tem_safe_text_callbacks
;
549 * First check to see if the user has specified a screen size.
550 * If so, use those values. Else use 34x80 as the default.
553 width
= TEM_DEFAULT_COLS
;
554 height
= TEM_DEFAULT_ROWS
;
556 tems
.ts_c_dimension
.height
= (screen_size_t
)height
;
557 tems
.ts_c_dimension
.width
= (screen_size_t
)width
;
559 tems
.ts_p_dimension
.height
= tp
->height
;
560 tems
.ts_p_dimension
.width
= tp
->width
;
562 tems
.ts_callbacks
= &tem_safe_pix_callbacks
;
565 * set_font() will select a appropriate sized font for
566 * the number of rows and columns selected. If we don't
567 * have a font that will fit, then it will use the
568 * default builtin font and adjust the rows and columns
569 * to fit on the screen.
571 set_font(&tems
.ts_font
,
572 &tems
.ts_c_dimension
.height
,
573 &tems
.ts_c_dimension
.width
,
574 tems
.ts_p_dimension
.height
,
575 tems
.ts_p_dimension
.width
);
577 tems
.ts_p_offset
.y
= (tems
.ts_p_dimension
.height
-
578 (tems
.ts_c_dimension
.height
* tems
.ts_font
.height
)) / 2;
579 tems
.ts_p_offset
.x
= (tems
.ts_p_dimension
.width
-
580 (tems
.ts_c_dimension
.width
* tems
.ts_font
.width
)) / 2;
582 tems
.ts_pix_data_size
=
583 tems
.ts_font
.width
* tems
.ts_font
.height
;
585 tems
.ts_pix_data_size
*= 4;
587 tems
.ts_pdepth
= tp
->depth
;
592 /* Now virtual cls also uses the blank_line buffer */
593 if (tems
.ts_blank_line
)
594 kmem_free(tems
.ts_blank_line
, old_blank_buf_size
);
596 tems
.ts_blank_line
= (unsigned char *)
597 kmem_alloc(tems
.ts_c_dimension
.width
, KM_SLEEP
);
598 for (i
= 0; i
< tems
.ts_c_dimension
.width
; i
++)
599 tems
.ts_blank_line
[i
] = ' ';
603 * This is a callback function that we register with the frame
604 * buffer driver layered underneath. It gets invoked from
605 * the underlying frame buffer driver to reconfigure the terminal
606 * emulator to a new screen size and depth in conjunction with
607 * framebuffer videomode changes.
608 * Here we keep the foreground/background color and attributes,
609 * which may be different with the initial settings, so that
610 * the color won't change while the framebuffer videomode changes.
611 * And we also reset the kernel terminal emulator and clear the
616 tems_modechange_callback(struct vis_modechg_arg
*arg
,
617 struct vis_devinit
*devinit
)
620 struct tem_vt_state
*p
;
622 tem_modechg_cb_arg_t cb_arg
;
624 ASSERT(!(list_is_empty(&tems
.ts_list
)));
626 mutex_enter(&tems
.ts_lock
);
629 * currently only for pixel mode
631 diff
= tems_check_videomode(devinit
);
633 mutex_exit(&tems
.ts_lock
);
637 diff
= diff
& TEMS_DIMENSION_DIFF
;
641 * Only need to reinit the active tem.
643 struct tem_vt_state
*active
= tems
.ts_active
;
644 tems
.ts_pdepth
= devinit
->depth
;
646 mutex_enter(&active
->tvs_lock
);
647 ASSERT(active
->tvs_isactive
);
648 tem_reinit(active
, B_TRUE
);
649 mutex_exit(&active
->tvs_lock
);
651 mutex_exit(&tems
.ts_lock
);
655 tems_setup_terminal(devinit
, tems
.ts_c_dimension
.height
,
656 tems
.ts_c_dimension
.width
);
658 for (p
= list_head(&tems
.ts_list
); p
!= NULL
;
659 p
= list_next(&tems
.ts_list
, p
)) {
660 mutex_enter(&p
->tvs_lock
);
661 tem_reinit(p
, p
->tvs_isactive
);
662 mutex_exit(&p
->tvs_lock
);
666 if (tems
.ts_modechg_cb
== NULL
) {
667 mutex_exit(&tems
.ts_lock
);
671 cb
= tems
.ts_modechg_cb
;
672 cb_arg
= tems
.ts_modechg_arg
;
675 * Release the lock while doing callback.
677 mutex_exit(&tems
.ts_lock
);
682 * This function is used to display a rectangular blit of data
683 * of a given size and location via the underlying framebuffer driver.
684 * The blit can be as small as a pixel or as large as the screen.
687 tems_display_layered(
688 struct vis_consdisplay
*pda
,
693 (void) ldi_ioctl(tems
.ts_hdl
, VIS_CONSDISPLAY
,
694 (intptr_t)pda
, FKIOCTL
, credp
, &rval
);
698 * This function is used to invoke a block copy operation in the
699 * underlying framebuffer driver. Rectangle copies are how scrolling
700 * is implemented, as well as horizontal text shifting escape seqs.
701 * such as from vi when deleting characters and words.
705 struct vis_conscopy
*pma
,
710 (void) ldi_ioctl(tems
.ts_hdl
, VIS_CONSCOPY
,
711 (intptr_t)pma
, FKIOCTL
, credp
, &rval
);
715 * This function is used to show or hide a rectangluar monochrom
716 * pixel inverting, text block cursor via the underlying framebuffer.
720 struct vis_conscursor
*pca
,
725 (void) ldi_ioctl(tems
.ts_hdl
, VIS_CONSCURSOR
,
726 (intptr_t)pca
, FKIOCTL
, credp
, &rval
);
730 tem_kdsetmode(int mode
, cred_t
*credp
)
734 (void) ldi_ioctl(tems
.ts_hdl
, KDSETMODE
,
735 (intptr_t)mode
, FKIOCTL
, credp
, &rval
);
740 tems_reset_colormap(cred_t
*credp
, enum called_from called_from
)
745 if (called_from
== CALLED_FROM_STANDALONE
)
748 switch (tems
.ts_pdepth
) {
752 cm
.red
= cmap4_to_24
.red
; /* 8-bits (1/3 of TrueColor 24) */
753 cm
.blue
= cmap4_to_24
.blue
; /* 8-bits (1/3 of TrueColor 24) */
754 cm
.green
= cmap4_to_24
.green
; /* 8-bits (1/3 of TrueColor 24) */
755 (void) ldi_ioctl(tems
.ts_hdl
, VIS_PUTCMAP
, (intptr_t)&cm
,
756 FKIOCTL
, credp
, &rval
);
762 tem_get_size(ushort_t
*r
, ushort_t
*c
, ushort_t
*x
, ushort_t
*y
)
764 mutex_enter(&tems
.ts_lock
);
765 *r
= (ushort_t
)tems
.ts_c_dimension
.height
;
766 *c
= (ushort_t
)tems
.ts_c_dimension
.width
;
767 *x
= (ushort_t
)tems
.ts_p_dimension
.width
;
768 *y
= (ushort_t
)tems
.ts_p_dimension
.height
;
769 mutex_exit(&tems
.ts_lock
);
773 tem_register_modechg_cb(tem_modechg_cb_t func
, tem_modechg_cb_arg_t arg
)
775 mutex_enter(&tems
.ts_lock
);
777 tems
.ts_modechg_cb
= func
;
778 tems
.ts_modechg_arg
= arg
;
780 mutex_exit(&tems
.ts_lock
);
784 * This function is to scroll up the OBP output, which has
785 * different screen height and width with our kernel console.
788 tem_prom_scroll_up(struct tem_vt_state
*tem
, int nrows
, cred_t
*credp
,
789 enum called_from called_from
)
791 struct vis_conscopy ma
;
795 ma
.s_row
= nrows
* tems
.ts_font
.height
;
796 ma
.e_row
= tems
.ts_p_dimension
.height
- 1;
800 ma
.e_col
= tems
.ts_p_dimension
.width
- 1;
803 tems_safe_copy(&ma
, credp
, called_from
);
806 width
= tems
.ts_font
.width
;
807 ncols
= (tems
.ts_p_dimension
.width
+ (width
- 1))/ width
;
809 tem_safe_pix_cls_range(tem
, 0, nrows
, tems
.ts_p_offset
.y
,
810 0, ncols
, 0, B_TRUE
, credp
, called_from
);
813 #define PROM_DEFAULT_FONT_HEIGHT 22
814 #define PROM_DEFAULT_WINDOW_TOP 0x8a
817 * This function is to compute the starting row of the console, according to
818 * PROM cursor's position. Here we have to take different fonts into account.
821 tem_adjust_row(struct tem_vt_state
*tem
, int prom_row
, cred_t
*credp
,
822 enum called_from called_from
)
826 int prom_charheight
= 0;
827 int prom_window_top
= 0;
830 plat_tem_get_prom_font_size(&prom_charheight
, &prom_window_top
);
831 if (prom_charheight
== 0)
832 prom_charheight
= PROM_DEFAULT_FONT_HEIGHT
;
833 if (prom_window_top
== 0)
834 prom_window_top
= PROM_DEFAULT_WINDOW_TOP
;
836 tem_y
= (prom_row
+ 1) * prom_charheight
+ prom_window_top
-
838 tem_row
= (tem_y
+ tems
.ts_font
.height
- 1) /
839 tems
.ts_font
.height
- 1;
843 } else if (tem_row
>= (tems
.ts_c_dimension
.height
- 1)) {
845 * Scroll up the prom outputs if the PROM cursor's position is
846 * below our tem's lower boundary.
848 scroll_up_lines
= tem_row
-
849 (tems
.ts_c_dimension
.height
- 1);
850 tem_prom_scroll_up(tem
, scroll_up_lines
, credp
, called_from
);
851 tem_row
= tems
.ts_c_dimension
.height
- 1;
858 tem_pix_align(struct tem_vt_state
*tem
, cred_t
*credp
,
859 enum called_from called_from
)
864 if (plat_stdout_is_framebuffer()) {
865 plat_tem_hide_prom_cursor();
868 * We are getting the current cursor position in pixel
869 * mode so that we don't over-write the console output
872 plat_tem_get_prom_pos(&row
, &col
);
875 * Adjust the row if necessary when the font of our
876 * kernel console tem is different with that of prom
879 row
= tem_adjust_row(tem
, row
, credp
, called_from
);
881 /* first line of our kernel console output */
882 tem
->tvs_first_line
= row
+ 1;
884 /* re-set and align cusror position */
885 tem
->tvs_s_cursor
.row
= tem
->tvs_c_cursor
.row
=
887 tem
->tvs_s_cursor
.col
= tem
->tvs_c_cursor
.col
= 0;
889 tem_safe_reset_display(tem
, credp
, called_from
, B_TRUE
, B_TRUE
);
894 tems_get_inverses(boolean_t
*p_inverse
, boolean_t
*p_inverse_screen
)
897 int i_inverse_screen
= 0;
899 plat_tem_get_inverses(&i_inverse
, &i_inverse_screen
);
901 *p_inverse
= (i_inverse
== 0) ? B_FALSE
: B_TRUE
;
902 *p_inverse_screen
= (i_inverse_screen
== 0) ? B_FALSE
: B_TRUE
;
906 * Get the foreground/background color and attributes from the initial
907 * PROM, so that our kernel console can keep the same visual behaviour.
910 tems_get_initial_color(tem_color_t
*pcolor
)
912 boolean_t inverse
, inverse_screen
;
913 unsigned short flags
= 0;
915 pcolor
->fg_color
= DEFAULT_ANSI_FOREGROUND
;
916 pcolor
->bg_color
= DEFAULT_ANSI_BACKGROUND
;
918 if (plat_stdout_is_framebuffer()) {
919 tems_get_inverses(&inverse
, &inverse_screen
);
921 flags
|= TEM_ATTR_REVERSE
;
923 flags
|= TEM_ATTR_SCREEN_REVERSE
;
927 * If either reverse flag is set, the screen is in
928 * white-on-black mode. We set the bold flag to
929 * improve readability.
931 flags
|= TEM_ATTR_BOLD
;
934 * Otherwise, the screen is in black-on-white mode.
935 * The SPARC PROM console, which starts in this mode,
936 * uses the bright white background colour so we
939 if (pcolor
->bg_color
== ANSI_COLOR_WHITE
)
940 flags
|= TEM_ATTR_BRIGHT_BG
;
944 pcolor
->a_flags
= flags
;
948 tem_get_fbmode(tem_vt_state_t tem_arg
)
950 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
954 mutex_enter(&tem
->tvs_lock
);
955 fbmode
= tem
->tvs_fbmode
;
956 mutex_exit(&tem
->tvs_lock
);
962 tem_set_fbmode(tem_vt_state_t tem_arg
, uchar_t fbmode
, cred_t
*credp
)
964 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
966 mutex_enter(&tems
.ts_lock
);
967 mutex_enter(&tem
->tvs_lock
);
969 if (fbmode
== tem
->tvs_fbmode
) {
970 mutex_exit(&tem
->tvs_lock
);
971 mutex_exit(&tems
.ts_lock
);
975 tem
->tvs_fbmode
= fbmode
;
977 if (tem
->tvs_isactive
) {
978 tem_kdsetmode(tem
->tvs_fbmode
, credp
);
979 if (fbmode
== KD_TEXT
)
980 tem_safe_unblank_screen(tem
, credp
, CALLED_FROM_NORMAL
);
983 mutex_exit(&tem
->tvs_lock
);
984 mutex_exit(&tems
.ts_lock
);
988 tem_activate(tem_vt_state_t tem_arg
, boolean_t unblank
, cred_t
*credp
)
990 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
992 mutex_enter(&tems
.ts_lock
);
993 tems
.ts_active
= tem
;
995 mutex_enter(&tem
->tvs_lock
);
996 tem
->tvs_isactive
= B_TRUE
;
998 tem_kdsetmode(tem
->tvs_fbmode
, credp
);
1001 tem_safe_unblank_screen(tem
, credp
, CALLED_FROM_NORMAL
);
1003 mutex_exit(&tem
->tvs_lock
);
1004 mutex_exit(&tems
.ts_lock
);
1008 tem_switch(tem_vt_state_t tem_arg1
, tem_vt_state_t tem_arg2
, cred_t
*credp
)
1010 struct tem_vt_state
*cur
= (struct tem_vt_state
*)tem_arg1
;
1011 struct tem_vt_state
*tobe
= (struct tem_vt_state
*)tem_arg2
;
1013 mutex_enter(&tems
.ts_lock
);
1014 mutex_enter(&tobe
->tvs_lock
);
1015 mutex_enter(&cur
->tvs_lock
);
1017 tems
.ts_active
= tobe
;
1018 cur
->tvs_isactive
= B_FALSE
;
1019 tobe
->tvs_isactive
= B_TRUE
;
1021 mutex_exit(&cur
->tvs_lock
);
1023 if (cur
->tvs_fbmode
!= tobe
->tvs_fbmode
)
1024 tem_kdsetmode(tobe
->tvs_fbmode
, credp
);
1026 if (tobe
->tvs_fbmode
== KD_TEXT
)
1027 tem_safe_unblank_screen(tobe
, credp
, CALLED_FROM_NORMAL
);
1029 mutex_exit(&tobe
->tvs_lock
);
1030 mutex_exit(&tems
.ts_lock
);