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
)
212 unsigned i
, j
, width
, height
;
217 ASSERT(MUTEX_HELD(&tems
.ts_lock
) && MUTEX_HELD(&ptem
->tvs_lock
));
219 if (tems
.ts_display_mode
== VIS_PIXEL
) {
220 ptem
->tvs_pix_data_size
= tems
.ts_pix_data_size
;
222 kmem_alloc(ptem
->tvs_pix_data_size
, KM_SLEEP
);
225 ptem
->tvs_stateflags
= TVS_AUTOWRAP
;
227 ptem
->tvs_outbuf_size
= tems
.ts_c_dimension
.width
*
228 sizeof (*ptem
->tvs_outbuf
);
229 ptem
->tvs_outbuf
= kmem_alloc(ptem
->tvs_outbuf_size
, KM_SLEEP
);
231 width
= tems
.ts_c_dimension
.width
;
232 height
= tems
.ts_c_dimension
.height
;
233 ptem
->tvs_screen_history_size
= height
;
235 ptem
->tvs_screen_buf_size
= width
* ptem
->tvs_screen_history_size
*
236 sizeof (*ptem
->tvs_screen_buf
);
237 ptem
->tvs_screen_buf
= kmem_alloc(ptem
->tvs_screen_buf_size
, KM_SLEEP
);
238 ptem
->tvs_screen_rows
= kmem_alloc(ptem
->tvs_screen_history_size
*
239 sizeof (term_char_t
*), KM_SLEEP
);
241 ptem
->tvs_maxtab
= width
/ 8;
242 ptem
->tvs_tabs
= kmem_alloc(ptem
->tvs_maxtab
* sizeof (*ptem
->tvs_tabs
),
245 tem_safe_reset_display(ptem
, credp
, CALLED_FROM_NORMAL
,
246 clear_screen
, init_color
);
248 ptem
->tvs_utf8_left
= 0;
249 ptem
->tvs_utf8_partial
= 0;
251 /* Get default attributes and fill up the screen buffer. */
252 tem_safe_get_attr(ptem
, &fg
, &bg
, &attr
, TEM_ATTR_SCREEN_REVERSE
);
253 for (i
= 0; i
< ptem
->tvs_screen_history_size
; i
++) {
254 ptem
->tvs_screen_rows
[i
] = &ptem
->tvs_screen_buf
[i
* width
];
256 for (j
= 0; j
< width
; j
++) {
257 ptem
->tvs_screen_rows
[i
][j
].tc_fg_color
= fg
;
258 ptem
->tvs_screen_rows
[i
][j
].tc_bg_color
= bg
;
259 ptem
->tvs_screen_rows
[i
][j
].tc_char
=
260 TEM_ATTR(attr
) | ' ';
264 ptem
->tvs_initialized
= B_TRUE
;
268 tem_initialized(tem_vt_state_t tem_arg
)
270 struct tem_vt_state
*ptem
= (struct tem_vt_state
*)tem_arg
;
273 mutex_enter(&ptem
->tvs_lock
);
274 ret
= ptem
->tvs_initialized
;
275 mutex_exit(&ptem
->tvs_lock
);
281 tem_init(cred_t
*credp
, queue_t
*rq
)
283 struct tem_vt_state
*ptem
;
285 ptem
= kmem_zalloc(sizeof (struct tem_vt_state
), KM_SLEEP
);
286 mutex_init(&ptem
->tvs_lock
, (char *)NULL
, MUTEX_DRIVER
, NULL
);
288 mutex_enter(&tems
.ts_lock
);
289 mutex_enter(&ptem
->tvs_lock
);
291 ptem
->tvs_isactive
= B_FALSE
;
292 ptem
->tvs_fbmode
= KD_TEXT
;
293 ptem
->tvs_queue
= rq
;
296 * A tem is regarded as initialized only after tem_internal_init(),
297 * will be set at the end of tem_internal_init().
299 ptem
->tvs_initialized
= B_FALSE
;
302 if (!tems
.ts_initialized
) {
304 * Only happens during early console configuration.
307 mutex_exit(&ptem
->tvs_lock
);
308 mutex_exit(&tems
.ts_lock
);
309 return ((tem_vt_state_t
)ptem
);
312 tem_internal_init(ptem
, credp
, B_TRUE
, B_FALSE
);
314 mutex_exit(&ptem
->tvs_lock
);
315 mutex_exit(&tems
.ts_lock
);
317 return ((tem_vt_state_t
)ptem
);
321 * re-init the tem after video mode has changed and tems_info has
322 * been re-inited. The lock is already held.
325 tem_reinit(struct tem_vt_state
*tem
, boolean_t reset_display
)
327 ASSERT(MUTEX_HELD(&tems
.ts_lock
) && MUTEX_HELD(&tem
->tvs_lock
));
329 tem_free_buf(tem
); /* only free virtual buffers */
332 tem_internal_init(tem
, kcred
, B_FALSE
, reset_display
);
336 tem_free_buf(struct tem_vt_state
*tem
)
338 ASSERT(tem
!= NULL
&& MUTEX_HELD(&tem
->tvs_lock
));
340 if (tem
->tvs_outbuf
!= NULL
)
341 kmem_free(tem
->tvs_outbuf
, tem
->tvs_outbuf_size
);
342 if (tem
->tvs_pix_data
!= NULL
)
343 kmem_free(tem
->tvs_pix_data
, tem
->tvs_pix_data_size
);
344 if (tem
->tvs_screen_buf
!= NULL
)
345 kmem_free(tem
->tvs_screen_buf
, tem
->tvs_screen_buf_size
);
346 if (tem
->tvs_screen_rows
!= NULL
) {
347 kmem_free(tem
->tvs_screen_rows
, tem
->tvs_screen_history_size
*
348 sizeof (term_char_t
*));
350 if (tem
->tvs_tabs
!= NULL
) {
351 kmem_free(tem
->tvs_tabs
, tem
->tvs_maxtab
*
352 sizeof (*tem
->tvs_tabs
));
357 tem_destroy(tem_vt_state_t tem_arg
, cred_t
*credp
)
359 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
361 mutex_enter(&tems
.ts_lock
);
362 mutex_enter(&tem
->tvs_lock
);
364 if (tem
->tvs_isactive
&& tem
->tvs_fbmode
== KD_TEXT
)
365 tem_safe_blank_screen(tem
, credp
, CALLED_FROM_NORMAL
);
370 if (tems
.ts_active
== tem
)
371 tems
.ts_active
= NULL
;
373 mutex_exit(&tem
->tvs_lock
);
374 mutex_exit(&tems
.ts_lock
);
376 kmem_free(tem
, sizeof (struct tem_vt_state
));
380 tems_failed(cred_t
*credp
, boolean_t finish_ioctl
)
384 ASSERT(MUTEX_HELD(&tems
.ts_lock
));
387 (void) ldi_ioctl(tems
.ts_hdl
, VIS_DEVFINI
, 0,
388 FWRITE
| FKIOCTL
, credp
, &lyr_rval
);
390 (void) ldi_close(tems
.ts_hdl
, 0, credp
);
396 * only called once during boot
399 tem_info_init(char *pathname
, cred_t
*credp
)
402 struct vis_devinit temargs
;
406 struct tem_vt_state
*p
;
408 mutex_enter(&tems
.ts_lock
);
410 if (tems
.ts_initialized
) {
411 mutex_exit(&tems
.ts_lock
);
416 * Open the layered device using the devfs physical device name
417 * after adding the /devices prefix.
419 pathbuf
= kmem_alloc(MAXPATHLEN
, KM_SLEEP
);
420 (void) strcpy(pathbuf
, "/devices");
421 if (i_ddi_prompath_to_devfspath(pathname
,
422 pathbuf
+ strlen("/devices")) != DDI_SUCCESS
) {
423 cmn_err(CE_WARN
, "terminal-emulator: path conversion error");
424 kmem_free(pathbuf
, MAXPATHLEN
);
426 mutex_exit(&tems
.ts_lock
);
429 if (ldi_open_by_name(pathbuf
, FWRITE
, credp
,
430 &tems
.ts_hdl
, term_li
) != 0) {
431 cmn_err(CE_WARN
, "terminal-emulator: device path open error");
432 kmem_free(pathbuf
, MAXPATHLEN
);
434 mutex_exit(&tems
.ts_lock
);
437 kmem_free(pathbuf
, MAXPATHLEN
);
439 temargs
.modechg_cb
= (vis_modechg_cb_t
)tems_modechange_callback
;
440 temargs
.modechg_arg
= NULL
;
443 * Initialize the console and get the device parameters
445 if (ldi_ioctl(tems
.ts_hdl
, VIS_DEVINIT
,
446 (intptr_t)&temargs
, FWRITE
|FKIOCTL
, credp
, &lyr_rval
) != 0) {
447 cmn_err(CE_WARN
, "terminal emulator: Compatible fb not found");
448 ret
= tems_failed(credp
, B_FALSE
);
449 mutex_exit(&tems
.ts_lock
);
453 /* Make sure the fb driver and terminal emulator versions match */
454 if (temargs
.version
!= VIS_CONS_REV
) {
456 "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) "
457 "of console fb driver not supported", temargs
.version
);
458 ret
= tems_failed(credp
, B_TRUE
);
459 mutex_exit(&tems
.ts_lock
);
463 if ((tems
.ts_fb_polledio
= temargs
.polledio
) == NULL
) {
464 cmn_err(CE_WARN
, "terminal emulator: fb doesn't support polled "
466 ret
= tems_failed(credp
, B_TRUE
);
467 mutex_exit(&tems
.ts_lock
);
471 /* other sanity checks */
472 if (!((temargs
.depth
== 4) || (temargs
.depth
== 8) ||
473 (temargs
.depth
== 15) || (temargs
.depth
== 16) ||
474 (temargs
.depth
== 24) || (temargs
.depth
== 32))) {
475 cmn_err(CE_WARN
, "terminal emulator: unsupported depth");
476 ret
= tems_failed(credp
, B_TRUE
);
477 mutex_exit(&tems
.ts_lock
);
481 if ((temargs
.mode
!= VIS_TEXT
) && (temargs
.mode
!= VIS_PIXEL
)) {
482 cmn_err(CE_WARN
, "terminal emulator: unsupported mode");
483 ret
= tems_failed(credp
, B_TRUE
);
484 mutex_exit(&tems
.ts_lock
);
488 if ((temargs
.mode
== VIS_PIXEL
) && plat_stdout_is_framebuffer())
489 plat_tem_get_prom_size(&height
, &width
);
492 * Initialize the common terminal emulator info
494 tems_setup_terminal(&temargs
, height
, width
);
496 tems_reset_colormap(credp
, CALLED_FROM_NORMAL
);
497 tems_get_initial_color(&tems
.ts_init_color
);
499 tems
.ts_initialized
= 1; /* initialization flag */
501 for (p
= list_head(&tems
.ts_list
); p
!= NULL
;
502 p
= list_next(&tems
.ts_list
, p
)) {
503 mutex_enter(&p
->tvs_lock
);
504 tem_internal_init(p
, credp
, B_TRUE
, B_FALSE
);
505 tem_align(p
, credp
, CALLED_FROM_NORMAL
);
506 mutex_exit(&p
->tvs_lock
);
509 mutex_exit(&tems
.ts_lock
);
513 #define TEMS_DEPTH_DIFF 0x01
514 #define TEMS_DIMENSION_DIFF 0x02
517 tems_check_videomode(struct vis_devinit
*tp
)
521 if (tems
.ts_pdepth
!= tp
->depth
)
522 result
|= TEMS_DEPTH_DIFF
;
524 if (tp
->mode
== VIS_TEXT
) {
525 if (tems
.ts_c_dimension
.width
!= tp
->width
||
526 tems
.ts_c_dimension
.height
!= tp
->height
)
527 result
|= TEMS_DIMENSION_DIFF
;
529 if (tems
.ts_p_dimension
.width
!= tp
->width
||
530 tems
.ts_p_dimension
.height
!= tp
->height
)
531 result
|= TEMS_DIMENSION_DIFF
;
538 tems_setup_font(screen_size_t height
, screen_size_t width
)
540 bitmap_data_t
*font_data
;
544 * set_font() will select an appropriate sized font for
545 * the number of rows and columns selected. If we don't
546 * have a font that will fit, then it will use the
547 * default builtin font and adjust the rows and columns
548 * to fit on the screen.
550 font_data
= set_font(&tems
.ts_c_dimension
.height
,
551 &tems
.ts_c_dimension
.width
, height
, width
);
554 * To use loaded font, we assign the loaded font data to tems.ts_font.
555 * In case of next load, the previously loaded data is freed
556 * when loading the new font.
558 for (i
= 0; i
< VFNT_MAPS
; i
++) {
559 tems
.ts_font
.vf_map
[i
] =
560 font_data
->font
->vf_map
[i
];
561 tems
.ts_font
.vf_map_count
[i
] =
562 font_data
->font
->vf_map_count
[i
];
565 tems
.ts_font
.vf_bytes
= font_data
->font
->vf_bytes
;
566 tems
.ts_font
.vf_width
= font_data
->font
->vf_width
;
567 tems
.ts_font
.vf_height
= font_data
->font
->vf_height
;
571 tems_setup_terminal(struct vis_devinit
*tp
, size_t height
, size_t width
)
573 int old_blank_buf_size
= tems
.ts_c_dimension
.width
*
574 sizeof (*tems
.ts_blank_line
);
576 ASSERT(MUTEX_HELD(&tems
.ts_lock
));
578 tems
.ts_pdepth
= tp
->depth
;
579 tems
.ts_linebytes
= tp
->linebytes
;
580 tems
.ts_display_mode
= tp
->mode
;
581 tems
.ts_color_map
= tp
->color_map
;
585 tems
.ts_p_dimension
.width
= 0;
586 tems
.ts_p_dimension
.height
= 0;
587 tems
.ts_c_dimension
.width
= tp
->width
;
588 tems
.ts_c_dimension
.height
= tp
->height
;
589 tems
.ts_callbacks
= &tem_safe_text_callbacks
;
591 tems_setup_font(16 * tp
->height
+ BORDER_PIXELS
,
592 8 * tp
->width
+ BORDER_PIXELS
);
598 * First check to see if the user has specified a screen size.
599 * If so, use those values. Else use 34x80 as the default.
602 width
= TEM_DEFAULT_COLS
;
603 height
= TEM_DEFAULT_ROWS
;
605 tems
.ts_c_dimension
.height
= (screen_size_t
)height
;
606 tems
.ts_c_dimension
.width
= (screen_size_t
)width
;
607 tems
.ts_p_dimension
.height
= tp
->height
;
608 tems
.ts_p_dimension
.width
= tp
->width
;
609 tems
.ts_callbacks
= &tem_safe_pix_callbacks
;
611 tems_setup_font(tp
->height
, tp
->width
);
613 tems
.ts_p_offset
.y
= (tems
.ts_p_dimension
.height
-
614 (tems
.ts_c_dimension
.height
* tems
.ts_font
.vf_height
)) / 2;
615 tems
.ts_p_offset
.x
= (tems
.ts_p_dimension
.width
-
616 (tems
.ts_c_dimension
.width
* tems
.ts_font
.vf_width
)) / 2;
618 tems
.ts_pix_data_size
=
619 tems
.ts_font
.vf_width
* tems
.ts_font
.vf_height
;
620 tems
.ts_pix_data_size
*= 4;
621 tems
.ts_pdepth
= tp
->depth
;
626 /* Now virtual cls also uses the blank_line buffer */
627 if (tems
.ts_blank_line
)
628 kmem_free(tems
.ts_blank_line
, old_blank_buf_size
);
630 tems
.ts_blank_line
= kmem_alloc(tems
.ts_c_dimension
.width
*
631 sizeof (*tems
.ts_blank_line
), KM_SLEEP
);
635 * This is a callback function that we register with the frame
636 * buffer driver layered underneath. It gets invoked from
637 * the underlying frame buffer driver to reconfigure the terminal
638 * emulator to a new screen size and depth in conjunction with
639 * framebuffer videomode changes.
640 * Here we keep the foreground/background color and attributes,
641 * which may be different with the initial settings, so that
642 * the color won't change while the framebuffer videomode changes.
643 * And we also reset the kernel terminal emulator and clear the
648 tems_modechange_callback(struct vis_modechg_arg
*arg
,
649 struct vis_devinit
*devinit
)
652 struct tem_vt_state
*p
;
654 tem_modechg_cb_arg_t cb_arg
;
656 ASSERT(!(list_is_empty(&tems
.ts_list
)));
658 mutex_enter(&tems
.ts_lock
);
661 * currently only for pixel mode
663 diff
= tems_check_videomode(devinit
);
665 mutex_exit(&tems
.ts_lock
);
669 diff
= diff
& TEMS_DIMENSION_DIFF
;
673 * Only need to reinit the active tem.
675 struct tem_vt_state
*active
= tems
.ts_active
;
676 tems
.ts_pdepth
= devinit
->depth
;
678 mutex_enter(&active
->tvs_lock
);
679 ASSERT(active
->tvs_isactive
);
680 tem_reinit(active
, B_TRUE
);
681 mutex_exit(&active
->tvs_lock
);
683 mutex_exit(&tems
.ts_lock
);
687 tems_setup_terminal(devinit
, tems
.ts_c_dimension
.height
,
688 tems
.ts_c_dimension
.width
);
690 for (p
= list_head(&tems
.ts_list
); p
!= NULL
;
691 p
= list_next(&tems
.ts_list
, p
)) {
692 mutex_enter(&p
->tvs_lock
);
693 tem_reinit(p
, p
->tvs_isactive
);
694 mutex_exit(&p
->tvs_lock
);
698 if (tems
.ts_modechg_cb
== NULL
) {
699 mutex_exit(&tems
.ts_lock
);
703 cb
= tems
.ts_modechg_cb
;
704 cb_arg
= tems
.ts_modechg_arg
;
707 * Release the lock while doing callback.
709 mutex_exit(&tems
.ts_lock
);
714 * This function is used to clear entire screen via the underlying framebuffer
718 tems_cls_layered(struct vis_consclear
*pda
,
723 (void) ldi_ioctl(tems
.ts_hdl
, VIS_CONSCLEAR
,
724 (intptr_t)pda
, FKIOCTL
, credp
, &rval
);
729 * This function is used to display a rectangular blit of data
730 * of a given size and location via the underlying framebuffer driver.
731 * The blit can be as small as a pixel or as large as the screen.
734 tems_display_layered(struct vis_consdisplay
*pda
,
739 (void) ldi_ioctl(tems
.ts_hdl
, VIS_CONSDISPLAY
,
740 (intptr_t)pda
, FKIOCTL
, credp
, &rval
);
744 * This function is used to invoke a block copy operation in the
745 * underlying framebuffer driver. Rectangle copies are how scrolling
746 * is implemented, as well as horizontal text shifting escape seqs.
747 * such as from vi when deleting characters and words.
750 tems_copy_layered(struct vis_conscopy
*pma
,
755 (void) ldi_ioctl(tems
.ts_hdl
, VIS_CONSCOPY
,
756 (intptr_t)pma
, FKIOCTL
, credp
, &rval
);
760 * This function is used to show or hide a rectangluar monochrom
761 * pixel inverting, text block cursor via the underlying framebuffer.
764 tems_cursor_layered(struct vis_conscursor
*pca
,
769 (void) ldi_ioctl(tems
.ts_hdl
, VIS_CONSCURSOR
,
770 (intptr_t)pca
, FKIOCTL
, credp
, &rval
);
774 tem_kdsetmode(int mode
, cred_t
*credp
)
778 (void) ldi_ioctl(tems
.ts_hdl
, KDSETMODE
,
779 (intptr_t)mode
, FKIOCTL
, credp
, &rval
);
784 tems_reset_colormap(cred_t
*credp
, enum called_from called_from
)
789 if (called_from
== CALLED_FROM_STANDALONE
)
792 switch (tems
.ts_pdepth
) {
796 cm
.red
= (uint8_t *)cmap4_to_24
.red
;
797 cm
.blue
= (uint8_t *)cmap4_to_24
.blue
;
798 cm
.green
= (uint8_t *)cmap4_to_24
.green
;
799 (void) ldi_ioctl(tems
.ts_hdl
, VIS_PUTCMAP
, (intptr_t)&cm
,
800 FKIOCTL
, credp
, &rval
);
806 tem_get_size(ushort_t
*r
, ushort_t
*c
, ushort_t
*x
, ushort_t
*y
)
808 mutex_enter(&tems
.ts_lock
);
809 *r
= (ushort_t
)tems
.ts_c_dimension
.height
;
810 *c
= (ushort_t
)tems
.ts_c_dimension
.width
;
811 *x
= (ushort_t
)tems
.ts_p_dimension
.width
;
812 *y
= (ushort_t
)tems
.ts_p_dimension
.height
;
813 mutex_exit(&tems
.ts_lock
);
817 tem_register_modechg_cb(tem_modechg_cb_t func
, tem_modechg_cb_arg_t arg
)
819 mutex_enter(&tems
.ts_lock
);
821 tems
.ts_modechg_cb
= func
;
822 tems
.ts_modechg_arg
= arg
;
824 mutex_exit(&tems
.ts_lock
);
828 * This function is to scroll up the OBP output, which has
829 * different screen height and width with our kernel console.
832 tem_prom_scroll_up(struct tem_vt_state
*tem
, int nrows
, cred_t
*credp
,
833 enum called_from called_from
)
835 struct vis_conscopy ma
;
839 ma
.s_row
= nrows
* tems
.ts_font
.vf_height
;
840 ma
.e_row
= tems
.ts_p_dimension
.height
- 1;
844 ma
.e_col
= tems
.ts_p_dimension
.width
- 1;
847 tems_safe_copy(&ma
, credp
, called_from
);
850 width
= tems
.ts_font
.vf_width
;
851 ncols
= (tems
.ts_p_dimension
.width
+ (width
- 1))/ width
;
853 tem_safe_pix_cls_range(tem
, 0, nrows
, tems
.ts_p_offset
.y
,
854 0, ncols
, 0, B_TRUE
, credp
, called_from
);
857 #define PROM_DEFAULT_FONT_HEIGHT 22
858 #define PROM_DEFAULT_WINDOW_TOP 0x8a
861 * This function is to compute the starting row of the console, according to
862 * PROM cursor's position. Here we have to take different fonts into account.
865 tem_adjust_row(struct tem_vt_state
*tem
, int prom_row
, cred_t
*credp
,
866 enum called_from called_from
)
870 int prom_charheight
= 0;
871 int prom_window_top
= 0;
874 if (tems
.ts_display_mode
== VIS_TEXT
)
877 plat_tem_get_prom_font_size(&prom_charheight
, &prom_window_top
);
878 if (prom_charheight
== 0)
879 prom_charheight
= PROM_DEFAULT_FONT_HEIGHT
;
880 if (prom_window_top
== 0)
881 prom_window_top
= PROM_DEFAULT_WINDOW_TOP
;
883 tem_y
= (prom_row
+ 1) * prom_charheight
+ prom_window_top
-
885 tem_row
= (tem_y
+ tems
.ts_font
.vf_height
- 1) /
886 tems
.ts_font
.vf_height
- 1;
890 } else if (tem_row
>= (tems
.ts_c_dimension
.height
- 1)) {
892 * Scroll up the prom outputs if the PROM cursor's position is
893 * below our tem's lower boundary.
895 scroll_up_lines
= tem_row
-
896 (tems
.ts_c_dimension
.height
- 1);
897 tem_prom_scroll_up(tem
, scroll_up_lines
, credp
, called_from
);
898 tem_row
= tems
.ts_c_dimension
.height
- 1;
905 tem_align(struct tem_vt_state
*tem
, cred_t
*credp
,
906 enum called_from called_from
)
911 plat_tem_hide_prom_cursor();
914 * We are getting the current cursor position in pixel
915 * mode so that we don't over-write the console output
918 plat_tem_get_prom_pos(&row
, &col
);
921 * Adjust the row if necessary when the font of our
922 * kernel console tem is different with that of prom
925 row
= tem_adjust_row(tem
, row
, credp
, called_from
);
927 /* first line of our kernel console output */
928 tem
->tvs_first_line
= row
+ 1;
930 /* re-set and align cursor position */
931 tem
->tvs_s_cursor
.row
= tem
->tvs_c_cursor
.row
=
933 tem
->tvs_s_cursor
.col
= tem
->tvs_c_cursor
.col
= 0;
936 * When tem is starting up, part of the screen is filled
937 * with information from boot loader and early boot.
938 * For tem, the screen content above current cursor
939 * should be treated as image.
941 for (; row
> 0; row
--) {
942 for (col
= 0; col
< tems
.ts_c_dimension
.width
; col
++) {
943 tem
->tvs_screen_rows
[row
][col
].tc_char
=
944 TEM_ATTR(TEM_ATTR_IMAGE
);
950 tems_get_inverses(boolean_t
*p_inverse
, boolean_t
*p_inverse_screen
)
953 int i_inverse_screen
= 0;
955 plat_tem_get_inverses(&i_inverse
, &i_inverse_screen
);
957 *p_inverse
= (i_inverse
== 0) ? B_FALSE
: B_TRUE
;
958 *p_inverse_screen
= (i_inverse_screen
== 0) ? B_FALSE
: B_TRUE
;
962 * Get the foreground/background color and attributes from the initial
963 * PROM, so that our kernel console can keep the same visual behaviour.
966 tems_get_initial_color(tem_color_t
*pcolor
)
968 boolean_t inverse
, inverse_screen
;
969 unsigned short flags
= 0;
972 fg
= DEFAULT_ANSI_FOREGROUND
;
973 bg
= DEFAULT_ANSI_BACKGROUND
;
974 #ifndef _HAVE_TEM_FIRMWARE
976 * _HAVE_TEM_FIRMWARE is defined on SPARC, at this time, the
977 * plat_tem_get_colors() is implemented only on x86.
980 plat_tem_get_colors(&fg
, &bg
);
982 pcolor
->fg_color
.n
= fg
;
983 pcolor
->bg_color
.n
= bg
;
985 tems_get_inverses(&inverse
, &inverse_screen
);
987 flags
|= TEM_ATTR_REVERSE
;
989 flags
|= TEM_ATTR_SCREEN_REVERSE
;
991 #ifdef _HAVE_TEM_FIRMWARE
994 * If either reverse flag is set, the screen is in
995 * white-on-black mode. We set the bold flag to
996 * improve readability.
998 flags
|= TEM_ATTR_BOLD
;
1001 * Otherwise, the screen is in black-on-white mode.
1002 * The SPARC PROM console, which starts in this mode,
1003 * uses the bright white background colour so we
1006 if (pcolor
->bg_color
.n
== ANSI_COLOR_WHITE
)
1007 flags
|= TEM_ATTR_BRIGHT_BG
;
1011 if (pcolor
->fg_color
.n
== ANSI_COLOR_WHITE
)
1012 flags
|= TEM_ATTR_BRIGHT_BG
;
1014 if (pcolor
->fg_color
.n
== ANSI_COLOR_BLACK
)
1015 flags
&= ~TEM_ATTR_BRIGHT_BG
;
1018 * In case of black on white we want bright white for BG.
1020 if (pcolor
->bg_color
.n
== ANSI_COLOR_WHITE
)
1021 flags
|= TEM_ATTR_BRIGHT_BG
;
1025 pcolor
->a_flags
= flags
;
1029 tem_get_fbmode(tem_vt_state_t tem_arg
)
1031 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
1035 mutex_enter(&tem
->tvs_lock
);
1036 fbmode
= tem
->tvs_fbmode
;
1037 mutex_exit(&tem
->tvs_lock
);
1043 tem_set_fbmode(tem_vt_state_t tem_arg
, uchar_t fbmode
, cred_t
*credp
)
1045 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
1047 mutex_enter(&tems
.ts_lock
);
1048 mutex_enter(&tem
->tvs_lock
);
1050 if (fbmode
== tem
->tvs_fbmode
) {
1051 mutex_exit(&tem
->tvs_lock
);
1052 mutex_exit(&tems
.ts_lock
);
1056 tem
->tvs_fbmode
= fbmode
;
1058 if (tem
->tvs_isactive
) {
1059 tem_kdsetmode(tem
->tvs_fbmode
, credp
);
1060 if (fbmode
== KD_TEXT
)
1061 tem_safe_unblank_screen(tem
, credp
, CALLED_FROM_NORMAL
);
1064 mutex_exit(&tem
->tvs_lock
);
1065 mutex_exit(&tems
.ts_lock
);
1069 tem_activate(tem_vt_state_t tem_arg
, boolean_t unblank
, cred_t
*credp
)
1071 struct tem_vt_state
*tem
= (struct tem_vt_state
*)tem_arg
;
1073 mutex_enter(&tems
.ts_lock
);
1074 tems
.ts_active
= tem
;
1076 mutex_enter(&tem
->tvs_lock
);
1077 tem
->tvs_isactive
= B_TRUE
;
1079 tem_kdsetmode(tem
->tvs_fbmode
, credp
);
1082 tem_safe_unblank_screen(tem
, credp
, CALLED_FROM_NORMAL
);
1084 mutex_exit(&tem
->tvs_lock
);
1085 mutex_exit(&tems
.ts_lock
);
1089 tem_switch(tem_vt_state_t tem_arg1
, tem_vt_state_t tem_arg2
, cred_t
*credp
)
1091 struct tem_vt_state
*cur
= (struct tem_vt_state
*)tem_arg1
;
1092 struct tem_vt_state
*tobe
= (struct tem_vt_state
*)tem_arg2
;
1094 mutex_enter(&tems
.ts_lock
);
1095 mutex_enter(&tobe
->tvs_lock
);
1096 mutex_enter(&cur
->tvs_lock
);
1098 tems
.ts_active
= tobe
;
1099 cur
->tvs_isactive
= B_FALSE
;
1100 tobe
->tvs_isactive
= B_TRUE
;
1102 mutex_exit(&cur
->tvs_lock
);
1104 if (cur
->tvs_fbmode
!= tobe
->tvs_fbmode
)
1105 tem_kdsetmode(tobe
->tvs_fbmode
, credp
);
1107 if (tobe
->tvs_fbmode
== KD_TEXT
)
1108 tem_safe_unblank_screen(tobe
, credp
, CALLED_FROM_NORMAL
);
1110 mutex_exit(&tobe
->tvs_lock
);
1111 mutex_exit(&tems
.ts_lock
);