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
, 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
;
228 ptem
->tvs_outbuf
= kmem_alloc(ptem
->tvs_outbuf_size
, KM_SLEEP
);
230 width
= tems
.ts_c_dimension
.width
;
231 height
= tems
.ts_c_dimension
.height
;
232 ptem
->tvs_screen_buf_size
= width
* height
;
233 ptem
->tvs_screen_buf
= kmem_alloc(width
* height
, KM_SLEEP
);
235 total
= width
* height
* tc_size
;
236 ptem
->tvs_fg_buf
= (text_color_t
*)kmem_alloc(total
, KM_SLEEP
);
237 ptem
->tvs_bg_buf
= (text_color_t
*)kmem_alloc(total
, KM_SLEEP
);
238 ptem
->tvs_color_buf_size
= total
;
240 tem_safe_reset_display(ptem
, credp
, CALLED_FROM_NORMAL
,
241 clear_screen
, init_color
);
243 ptem
->tvs_utf8_left
= 0;
244 ptem
->tvs_utf8_partial
= 0;
246 tem_safe_get_color(ptem
, &fg
, &bg
, TEM_ATTR_SCREEN_REVERSE
);
247 for (i
= 0; i
< height
; i
++)
248 for (j
= 0; j
< width
; j
++) {
249 ptem
->tvs_screen_buf
[i
* width
+ j
] = ' ';
250 ptem
->tvs_fg_buf
[(i
* width
+j
) * tc_size
] = fg
;
251 ptem
->tvs_bg_buf
[(i
* width
+j
) * tc_size
] = bg
;
255 ptem
->tvs_initialized
= 1;
259 tem_initialized(tem_vt_state_t tem_arg
)
261 struct tem_vt_state
*ptem
= (struct tem_vt_state
*)tem_arg
;
264 mutex_enter(&ptem
->tvs_lock
);
265 ret
= ptem
->tvs_initialized
;
266 mutex_exit(&ptem
->tvs_lock
);
272 tem_init(cred_t
*credp
)
274 struct tem_vt_state
*ptem
;
276 ptem
= kmem_zalloc(sizeof (struct tem_vt_state
), KM_SLEEP
);
277 mutex_init(&ptem
->tvs_lock
, NULL
, MUTEX_DRIVER
, NULL
);
279 mutex_enter(&tems
.ts_lock
);
280 mutex_enter(&ptem
->tvs_lock
);
282 ptem
->tvs_isactive
= B_FALSE
;
283 ptem
->tvs_fbmode
= KD_TEXT
;
286 * A tem is regarded as initialized only after tem_internal_init(),
287 * will be set at the end of tem_internal_init().
289 ptem
->tvs_initialized
= 0;
292 if (!tems
.ts_initialized
) {
294 * Only happens during early console configuration.
297 mutex_exit(&ptem
->tvs_lock
);
298 mutex_exit(&tems
.ts_lock
);
299 return ((tem_vt_state_t
)ptem
);
302 tem_internal_init(ptem
, credp
, B_TRUE
, B_FALSE
);
304 mutex_exit(&ptem
->tvs_lock
);
305 mutex_exit(&tems
.ts_lock
);
307 return ((tem_vt_state_t
)ptem
);
311 * re-init the tem after video mode has changed and tems_info has
312 * been re-inited. The lock is already held.
315 tem_reinit(struct tem_vt_state
*tem
, boolean_t reset_display
)
317 ASSERT(MUTEX_HELD(&tems
.ts_lock
) && MUTEX_HELD(&tem
->tvs_lock
));
319 tem_free_buf(tem
); /* only free virtual buffers */
322 tem_internal_init(tem
, kcred
, B_FALSE
, reset_display
);
326 tem_free_buf(struct tem_vt_state
*tem
)
328 ASSERT(tem
!= NULL
&& MUTEX_HELD(&tem
->tvs_lock
));
330 if (tem
->tvs_outbuf
!= NULL
)
331 kmem_free(tem
->tvs_outbuf
, tem
->tvs_outbuf_size
);
332 if (tem
->tvs_pix_data
!= NULL
)
333 kmem_free(tem
->tvs_pix_data
, tem
->tvs_pix_data_size
);
334 if (tem
->tvs_screen_buf
!= NULL
)
335 kmem_free(tem
->tvs_screen_buf
, tem
->tvs_screen_buf_size
);
336 if (tem
->tvs_fg_buf
!= NULL
)
337 kmem_free(tem
->tvs_fg_buf
, tem
->tvs_color_buf_size
);
338 if (tem
->tvs_bg_buf
!= NULL
)
339 kmem_free(tem
->tvs_bg_buf
, tem
->tvs_color_buf_size
);
343 tem_destroy(tem_vt_state_t tem_arg
, cred_t
*credp
)
345 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
347 mutex_enter(&tems
.ts_lock
);
348 mutex_enter(&tem
->tvs_lock
);
350 if (tem
->tvs_isactive
&& tem
->tvs_fbmode
== KD_TEXT
)
351 tem_safe_blank_screen(tem
, credp
, CALLED_FROM_NORMAL
);
356 if (tems
.ts_active
== tem
)
357 tems
.ts_active
= NULL
;
359 mutex_exit(&tem
->tvs_lock
);
360 mutex_exit(&tems
.ts_lock
);
362 kmem_free(tem
, sizeof (struct tem_vt_state
));
366 tems_failed(cred_t
*credp
, boolean_t finish_ioctl
)
370 ASSERT(MUTEX_HELD(&tems
.ts_lock
));
373 (void) ldi_ioctl(tems
.ts_hdl
, VIS_DEVFINI
, 0,
374 FWRITE
|FKIOCTL
, credp
, &lyr_rval
);
376 (void) ldi_close(tems
.ts_hdl
, 0, credp
);
382 * only called once during boot
385 tem_info_init(char *pathname
, cred_t
*credp
)
388 struct vis_devinit temargs
;
392 struct tem_vt_state
*p
;
394 mutex_enter(&tems
.ts_lock
);
396 if (tems
.ts_initialized
) {
397 mutex_exit(&tems
.ts_lock
);
402 * Open the layered device using the devfs physical device name
403 * after adding the /devices prefix.
405 pathbuf
= kmem_alloc(MAXPATHLEN
, KM_SLEEP
);
406 (void) strcpy(pathbuf
, "/devices");
407 if (i_ddi_prompath_to_devfspath(pathname
,
408 pathbuf
+ strlen("/devices")) != DDI_SUCCESS
) {
409 cmn_err(CE_WARN
, "terminal-emulator: path conversion error");
410 kmem_free(pathbuf
, MAXPATHLEN
);
412 mutex_exit(&tems
.ts_lock
);
415 if (ldi_open_by_name(pathbuf
, FWRITE
, credp
,
416 &tems
.ts_hdl
, term_li
) != 0) {
417 cmn_err(CE_WARN
, "terminal-emulator: device path open error");
418 kmem_free(pathbuf
, MAXPATHLEN
);
420 mutex_exit(&tems
.ts_lock
);
423 kmem_free(pathbuf
, MAXPATHLEN
);
425 temargs
.modechg_cb
= (vis_modechg_cb_t
)tems_modechange_callback
;
426 temargs
.modechg_arg
= NULL
;
429 * Initialize the console and get the device parameters
431 if (ldi_ioctl(tems
.ts_hdl
, VIS_DEVINIT
,
432 (intptr_t)&temargs
, FWRITE
|FKIOCTL
, credp
, &lyr_rval
) != 0) {
433 cmn_err(CE_WARN
, "terminal emulator: Compatible fb not found");
434 ret
= tems_failed(credp
, B_FALSE
);
435 mutex_exit(&tems
.ts_lock
);
439 /* Make sure the fb driver and terminal emulator versions match */
440 if (temargs
.version
!= VIS_CONS_REV
) {
442 "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) "
443 "of console fb driver not supported", temargs
.version
);
444 ret
= tems_failed(credp
, B_TRUE
);
445 mutex_exit(&tems
.ts_lock
);
449 if ((tems
.ts_fb_polledio
= temargs
.polledio
) == NULL
) {
450 cmn_err(CE_WARN
, "terminal emulator: fb doesn't support polled "
452 ret
= tems_failed(credp
, B_TRUE
);
453 mutex_exit(&tems
.ts_lock
);
457 /* other sanity checks */
458 if (!((temargs
.depth
== 4) || (temargs
.depth
== 8) ||
459 (temargs
.depth
== 24) || (temargs
.depth
== 32))) {
460 cmn_err(CE_WARN
, "terminal emulator: unsupported depth");
461 ret
= tems_failed(credp
, B_TRUE
);
462 mutex_exit(&tems
.ts_lock
);
466 if ((temargs
.mode
!= VIS_TEXT
) && (temargs
.mode
!= VIS_PIXEL
)) {
467 cmn_err(CE_WARN
, "terminal emulator: unsupported mode");
468 ret
= tems_failed(credp
, B_TRUE
);
469 mutex_exit(&tems
.ts_lock
);
473 if ((temargs
.mode
== VIS_PIXEL
) && plat_stdout_is_framebuffer())
474 plat_tem_get_prom_size(&height
, &width
);
477 * Initialize the common terminal emulator info
479 tems_setup_terminal(&temargs
, height
, width
);
481 tems_reset_colormap(credp
, CALLED_FROM_NORMAL
);
482 tems_get_initial_color(&tems
.ts_init_color
);
484 tems
.ts_initialized
= 1; /* initialization flag */
486 for (p
= list_head(&tems
.ts_list
); p
!= NULL
;
487 p
= list_next(&tems
.ts_list
, p
)) {
488 mutex_enter(&p
->tvs_lock
);
489 tem_internal_init(p
, credp
, B_TRUE
, B_FALSE
);
490 if (temargs
.mode
== VIS_PIXEL
)
491 tem_pix_align(p
, credp
, CALLED_FROM_NORMAL
);
492 mutex_exit(&p
->tvs_lock
);
495 mutex_exit(&tems
.ts_lock
);
499 #define TEMS_DEPTH_DIFF 0x01
500 #define TEMS_DIMENSION_DIFF 0x02
503 tems_check_videomode(struct vis_devinit
*tp
)
507 if (tems
.ts_pdepth
!= tp
->depth
)
508 result
|= TEMS_DEPTH_DIFF
;
510 if (tp
->mode
== VIS_TEXT
) {
511 if (tems
.ts_c_dimension
.width
!= tp
->width
||
512 tems
.ts_c_dimension
.height
!= tp
->height
)
513 result
|= TEMS_DIMENSION_DIFF
;
515 if (tems
.ts_p_dimension
.width
!= tp
->width
||
516 tems
.ts_p_dimension
.height
!= tp
->height
)
517 result
|= TEMS_DIMENSION_DIFF
;
524 tems_setup_terminal(struct vis_devinit
*tp
, size_t height
, size_t width
)
527 int old_blank_buf_size
= tems
.ts_c_dimension
.width
;
529 ASSERT(MUTEX_HELD(&tems
.ts_lock
));
531 tems
.ts_pdepth
= tp
->depth
;
532 tems
.ts_linebytes
= tp
->linebytes
;
533 tems
.ts_display_mode
= tp
->mode
;
537 tems
.ts_p_dimension
.width
= 0;
538 tems
.ts_p_dimension
.height
= 0;
539 tems
.ts_c_dimension
.width
= tp
->width
;
540 tems
.ts_c_dimension
.height
= tp
->height
;
541 tems
.ts_callbacks
= &tem_safe_text_callbacks
;
547 * First check to see if the user has specified a screen size.
548 * If so, use those values. Else use 34x80 as the default.
551 width
= TEM_DEFAULT_COLS
;
552 height
= TEM_DEFAULT_ROWS
;
554 tems
.ts_c_dimension
.height
= (screen_size_t
)height
;
555 tems
.ts_c_dimension
.width
= (screen_size_t
)width
;
557 tems
.ts_p_dimension
.height
= tp
->height
;
558 tems
.ts_p_dimension
.width
= tp
->width
;
560 tems
.ts_callbacks
= &tem_safe_pix_callbacks
;
563 * set_font() will select a appropriate sized font for
564 * the number of rows and columns selected. If we don't
565 * have a font that will fit, then it will use the
566 * default builtin font. set_font() will adjust the rows
567 * and columns to fit on the screen.
569 set_font(&tems
.ts_font
,
570 &tems
.ts_c_dimension
.height
,
571 &tems
.ts_c_dimension
.width
,
572 tems
.ts_p_dimension
.height
,
573 tems
.ts_p_dimension
.width
);
575 tems
.ts_p_offset
.y
= (tems
.ts_p_dimension
.height
-
576 (tems
.ts_c_dimension
.height
* tems
.ts_font
.height
)) / 2;
577 tems
.ts_p_offset
.x
= (tems
.ts_p_dimension
.width
-
578 (tems
.ts_c_dimension
.width
* tems
.ts_font
.width
)) / 2;
580 tems
.ts_pix_data_size
=
581 tems
.ts_font
.width
* tems
.ts_font
.height
;
583 tems
.ts_pix_data_size
*= 4;
585 tems
.ts_pdepth
= tp
->depth
;
590 /* Now virtual cls also uses the blank_line buffer */
591 if (tems
.ts_blank_line
)
592 kmem_free(tems
.ts_blank_line
, old_blank_buf_size
);
594 tems
.ts_blank_line
= (unsigned char *)
595 kmem_alloc(tems
.ts_c_dimension
.width
, KM_SLEEP
);
596 for (i
= 0; i
< tems
.ts_c_dimension
.width
; i
++)
597 tems
.ts_blank_line
[i
] = ' ';
601 * This is a callback function that we register with the frame
602 * buffer driver layered underneath. It gets invoked from
603 * the underlying frame buffer driver to reconfigure the terminal
604 * emulator to a new screen size and depth in conjunction with
605 * framebuffer videomode changes.
606 * Here we keep the foreground/background color and attributes,
607 * which may be different with the initial settings, so that
608 * the color won't change while the framebuffer videomode changes.
609 * And we also reset the kernel terminal emulator and clear the
614 tems_modechange_callback(struct vis_modechg_arg
*arg
,
615 struct vis_devinit
*devinit
)
618 struct tem_vt_state
*p
;
620 tem_modechg_cb_arg_t cb_arg
;
622 ASSERT(!(list_is_empty(&tems
.ts_list
)));
624 mutex_enter(&tems
.ts_lock
);
627 * currently only for pixel mode
629 diff
= tems_check_videomode(devinit
);
631 mutex_exit(&tems
.ts_lock
);
635 diff
= diff
& TEMS_DIMENSION_DIFF
;
639 * Only need to reinit the active tem.
641 struct tem_vt_state
*active
= tems
.ts_active
;
642 tems
.ts_pdepth
= devinit
->depth
;
644 mutex_enter(&active
->tvs_lock
);
645 ASSERT(active
->tvs_isactive
);
646 tem_reinit(active
, B_TRUE
);
647 mutex_exit(&active
->tvs_lock
);
649 mutex_exit(&tems
.ts_lock
);
653 tems_setup_terminal(devinit
, tems
.ts_c_dimension
.height
,
654 tems
.ts_c_dimension
.width
);
656 for (p
= list_head(&tems
.ts_list
); p
!= NULL
;
657 p
= list_next(&tems
.ts_list
, p
)) {
658 mutex_enter(&p
->tvs_lock
);
659 tem_reinit(p
, p
->tvs_isactive
);
660 mutex_exit(&p
->tvs_lock
);
664 if (tems
.ts_modechg_cb
== NULL
) {
665 mutex_exit(&tems
.ts_lock
);
669 cb
= tems
.ts_modechg_cb
;
670 cb_arg
= tems
.ts_modechg_arg
;
673 * Release the lock while doing callback.
675 mutex_exit(&tems
.ts_lock
);
680 * This function is used to display a rectangular blit of data
681 * of a given size and location via the underlying framebuffer driver.
682 * The blit can be as small as a pixel or as large as the screen.
685 tems_display_layered(
686 struct vis_consdisplay
*pda
,
691 (void) ldi_ioctl(tems
.ts_hdl
, VIS_CONSDISPLAY
,
692 (intptr_t)pda
, FKIOCTL
, credp
, &rval
);
696 * This function is used to invoke a block copy operation in the
697 * underlying framebuffer driver. Rectangle copies are how scrolling
698 * is implemented, as well as horizontal text shifting escape seqs.
699 * such as from vi when deleting characters and words.
703 struct vis_conscopy
*pma
,
708 (void) ldi_ioctl(tems
.ts_hdl
, VIS_CONSCOPY
,
709 (intptr_t)pma
, FKIOCTL
, credp
, &rval
);
713 * This function is used to show or hide a rectangluar monochrom
714 * pixel inverting, text block cursor via the underlying framebuffer.
718 struct vis_conscursor
*pca
,
723 (void) ldi_ioctl(tems
.ts_hdl
, VIS_CONSCURSOR
,
724 (intptr_t)pca
, FKIOCTL
, credp
, &rval
);
728 tem_kdsetmode(int mode
, cred_t
*credp
)
732 (void) ldi_ioctl(tems
.ts_hdl
, KDSETMODE
,
733 (intptr_t)mode
, FKIOCTL
, credp
, &rval
);
738 tems_reset_colormap(cred_t
*credp
, enum called_from called_from
)
743 if (called_from
== CALLED_FROM_STANDALONE
)
746 switch (tems
.ts_pdepth
) {
750 cm
.red
= cmap4_to_24
.red
; /* 8-bits (1/3 of TrueColor 24) */
751 cm
.blue
= cmap4_to_24
.blue
; /* 8-bits (1/3 of TrueColor 24) */
752 cm
.green
= cmap4_to_24
.green
; /* 8-bits (1/3 of TrueColor 24) */
753 (void) ldi_ioctl(tems
.ts_hdl
, VIS_PUTCMAP
, (intptr_t)&cm
,
754 FKIOCTL
, credp
, &rval
);
760 tem_get_size(ushort_t
*r
, ushort_t
*c
, ushort_t
*x
, ushort_t
*y
)
762 mutex_enter(&tems
.ts_lock
);
763 *r
= (ushort_t
)tems
.ts_c_dimension
.height
;
764 *c
= (ushort_t
)tems
.ts_c_dimension
.width
;
765 *x
= (ushort_t
)tems
.ts_p_dimension
.width
;
766 *y
= (ushort_t
)tems
.ts_p_dimension
.height
;
767 mutex_exit(&tems
.ts_lock
);
771 tem_register_modechg_cb(tem_modechg_cb_t func
, tem_modechg_cb_arg_t arg
)
773 mutex_enter(&tems
.ts_lock
);
775 tems
.ts_modechg_cb
= func
;
776 tems
.ts_modechg_arg
= arg
;
778 mutex_exit(&tems
.ts_lock
);
782 * This function is to scroll up the OBP output, which has
783 * different screen height and width with our kernel console.
786 tem_prom_scroll_up(struct tem_vt_state
*tem
, int nrows
, cred_t
*credp
,
787 enum called_from called_from
)
789 struct vis_conscopy ma
;
793 ma
.s_row
= nrows
* tems
.ts_font
.height
;
794 ma
.e_row
= tems
.ts_p_dimension
.height
- 1;
798 ma
.e_col
= tems
.ts_p_dimension
.width
- 1;
801 tems_safe_copy(&ma
, credp
, called_from
);
804 width
= tems
.ts_font
.width
;
805 ncols
= (tems
.ts_p_dimension
.width
+ (width
- 1))/ width
;
807 tem_safe_pix_cls_range(tem
, 0, nrows
, tems
.ts_p_offset
.y
,
808 0, ncols
, 0, B_TRUE
, credp
, called_from
);
811 #define PROM_DEFAULT_FONT_HEIGHT 22
812 #define PROM_DEFAULT_WINDOW_TOP 0x8a
815 * This function is to compute the starting row of the console, according to
816 * PROM cursor's position. Here we have to take different fonts into account.
819 tem_adjust_row(struct tem_vt_state
*tem
, int prom_row
, cred_t
*credp
,
820 enum called_from called_from
)
824 int prom_charheight
= 0;
825 int prom_window_top
= 0;
828 plat_tem_get_prom_font_size(&prom_charheight
, &prom_window_top
);
829 if (prom_charheight
== 0)
830 prom_charheight
= PROM_DEFAULT_FONT_HEIGHT
;
831 if (prom_window_top
== 0)
832 prom_window_top
= PROM_DEFAULT_WINDOW_TOP
;
834 tem_y
= (prom_row
+ 1) * prom_charheight
+ prom_window_top
-
836 tem_row
= (tem_y
+ tems
.ts_font
.height
- 1) /
837 tems
.ts_font
.height
- 1;
841 } else if (tem_row
>= (tems
.ts_c_dimension
.height
- 1)) {
843 * Scroll up the prom outputs if the PROM cursor's position is
844 * below our tem's lower boundary.
846 scroll_up_lines
= tem_row
-
847 (tems
.ts_c_dimension
.height
- 1);
848 tem_prom_scroll_up(tem
, scroll_up_lines
, credp
, called_from
);
849 tem_row
= tems
.ts_c_dimension
.height
- 1;
856 tem_pix_align(struct tem_vt_state
*tem
, cred_t
*credp
,
857 enum called_from called_from
)
862 if (plat_stdout_is_framebuffer()) {
863 plat_tem_hide_prom_cursor();
866 * We are getting the current cursor position in pixel
867 * mode so that we don't over-write the console output
870 plat_tem_get_prom_pos(&row
, &col
);
873 * Adjust the row if necessary when the font of our
874 * kernel console tem is different with that of prom
877 row
= tem_adjust_row(tem
, row
, credp
, called_from
);
879 /* first line of our kernel console output */
880 tem
->tvs_first_line
= row
+ 1;
882 /* re-set and align cusror position */
883 tem
->tvs_s_cursor
.row
= tem
->tvs_c_cursor
.row
=
885 tem
->tvs_s_cursor
.col
= tem
->tvs_c_cursor
.col
= 0;
887 tem_safe_reset_display(tem
, credp
, called_from
, B_TRUE
, B_TRUE
);
892 tems_get_inverses(boolean_t
*p_inverse
, boolean_t
*p_inverse_screen
)
895 int i_inverse_screen
= 0;
897 plat_tem_get_inverses(&i_inverse
, &i_inverse_screen
);
899 *p_inverse
= (i_inverse
== 0) ? B_FALSE
: B_TRUE
;
900 *p_inverse_screen
= (i_inverse_screen
== 0) ? B_FALSE
: B_TRUE
;
904 * Get the foreground/background color and attributes from the initial
905 * PROM, so that our kernel console can keep the same visual behaviour.
908 tems_get_initial_color(tem_color_t
*pcolor
)
910 boolean_t inverse
, inverse_screen
;
911 unsigned short flags
= 0;
913 pcolor
->fg_color
= DEFAULT_ANSI_FOREGROUND
;
914 pcolor
->bg_color
= DEFAULT_ANSI_BACKGROUND
;
916 if (plat_stdout_is_framebuffer()) {
917 tems_get_inverses(&inverse
, &inverse_screen
);
919 flags
|= TEM_ATTR_REVERSE
;
921 flags
|= TEM_ATTR_SCREEN_REVERSE
;
925 * If either reverse flag is set, the screen is in
926 * white-on-black mode. We set the bold flag to
927 * improve readability.
929 flags
|= TEM_ATTR_BOLD
;
932 * Otherwise, the screen is in black-on-white mode.
933 * The SPARC PROM console, which starts in this mode,
934 * uses the bright white background colour so we
937 if (pcolor
->bg_color
== ANSI_COLOR_WHITE
)
938 flags
|= TEM_ATTR_BRIGHT_BG
;
942 pcolor
->a_flags
= flags
;
946 tem_get_fbmode(tem_vt_state_t tem_arg
)
948 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
952 mutex_enter(&tem
->tvs_lock
);
953 fbmode
= tem
->tvs_fbmode
;
954 mutex_exit(&tem
->tvs_lock
);
960 tem_set_fbmode(tem_vt_state_t tem_arg
, uchar_t fbmode
, cred_t
*credp
)
962 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
964 mutex_enter(&tems
.ts_lock
);
965 mutex_enter(&tem
->tvs_lock
);
967 if (fbmode
== tem
->tvs_fbmode
) {
968 mutex_exit(&tem
->tvs_lock
);
969 mutex_exit(&tems
.ts_lock
);
973 tem
->tvs_fbmode
= fbmode
;
975 if (tem
->tvs_isactive
) {
976 tem_kdsetmode(tem
->tvs_fbmode
, credp
);
977 if (fbmode
== KD_TEXT
)
978 tem_safe_unblank_screen(tem
, credp
, CALLED_FROM_NORMAL
);
981 mutex_exit(&tem
->tvs_lock
);
982 mutex_exit(&tems
.ts_lock
);
986 tem_activate(tem_vt_state_t tem_arg
, boolean_t unblank
, cred_t
*credp
)
988 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
990 mutex_enter(&tems
.ts_lock
);
991 tems
.ts_active
= tem
;
993 mutex_enter(&tem
->tvs_lock
);
994 tem
->tvs_isactive
= B_TRUE
;
996 tem_kdsetmode(tem
->tvs_fbmode
, credp
);
999 tem_safe_unblank_screen(tem
, credp
, CALLED_FROM_NORMAL
);
1001 mutex_exit(&tem
->tvs_lock
);
1002 mutex_exit(&tems
.ts_lock
);
1006 tem_switch(tem_vt_state_t tem_arg1
, tem_vt_state_t tem_arg2
, cred_t
*credp
)
1008 struct tem_vt_state
*cur
= (struct tem_vt_state
*)tem_arg1
;
1009 struct tem_vt_state
*tobe
= (struct tem_vt_state
*)tem_arg2
;
1011 mutex_enter(&tems
.ts_lock
);
1012 mutex_enter(&tobe
->tvs_lock
);
1013 mutex_enter(&cur
->tvs_lock
);
1015 tems
.ts_active
= tobe
;
1016 cur
->tvs_isactive
= B_FALSE
;
1017 tobe
->tvs_isactive
= B_TRUE
;
1019 mutex_exit(&cur
->tvs_lock
);
1021 if (cur
->tvs_fbmode
!= tobe
->tvs_fbmode
)
1022 tem_kdsetmode(tobe
->tvs_fbmode
, credp
);
1024 if (tobe
->tvs_fbmode
== KD_TEXT
)
1025 tem_safe_unblank_screen(tobe
, credp
, CALLED_FROM_NORMAL
);
1027 mutex_exit(&tobe
->tvs_lock
);
1028 mutex_exit(&tems
.ts_lock
);