14916 ehci_qh_pool_size is probably too low
[illumos-gate.git] / usr / src / uts / common / io / tem.c
blob716501b48925bfc6b65d108fd4a18d699f7df553
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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
29 * the like.
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>
64 #include <sys/file.h>
65 #include <sys/conf.h>
66 #include <sys/errno.h>
67 #include <sys/open.h>
68 #include <sys/cred.h>
69 #include <sys/kmem.h>
70 #include <sys/ascii.h>
71 #include <sys/consdev.h>
72 #include <sys/font.h>
73 #include <sys/fbio.h>
74 #include <sys/conf.h>
75 #include <sys/modctl.h>
76 #include <sys/strsubr.h>
77 #include <sys/stat.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>
84 #include <sys/ddi.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>
92 #include <sys/kd.h>
93 #include <sys/sysmacros.h>
94 #include <sys/note.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,
106 boolean_t);
107 static void tems_get_initial_color(tem_color_t *pcolor);
110 * Globals
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 }
128 _init(void)
130 int ret;
131 ret = mod_install(&modlinkage);
132 if (ret != 0)
133 return (ret);
134 ret = ldi_ident_from_mod(&modlinkage, &term_li);
135 if (ret != 0) {
136 (void) mod_remove(&modlinkage);
137 return (ret);
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;
145 return (0);
149 _fini()
151 int ret;
153 ret = mod_remove(&modlinkage);
154 if (ret == 0) {
155 ldi_ident_release(term_li);
156 term_li = NULL;
158 return (ret);
162 _info(struct modinfo *modinfop)
164 return (mod_info(&modlinkage, modinfop));
167 static void
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);
175 static void
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.
187 void
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);
198 return;
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);
208 static void
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;
213 text_attr_t attr;
214 text_color_t fg;
215 text_color_t bg;
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;
221 ptem->tvs_pix_data =
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),
243 KM_SLEEP);
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;
267 boolean_t
268 tem_initialized(tem_vt_state_t tem_arg)
270 struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg;
271 boolean_t ret;
273 mutex_enter(&ptem->tvs_lock);
274 ret = ptem->tvs_initialized;
275 mutex_exit(&ptem->tvs_lock);
277 return (ret);
280 tem_vt_state_t
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.
306 tem_add(ptem);
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);
313 tem_add(ptem);
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.
324 static void
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 */
331 /* reserve color */
332 tem_internal_init(tem, kcred, B_FALSE, reset_display);
335 static void
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));
356 void
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);
367 tem_free_buf(tem);
368 tem_rm(tem);
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));
379 static int
380 tems_failed(cred_t *credp, boolean_t finish_ioctl)
382 int lyr_rval;
384 ASSERT(MUTEX_HELD(&tems.ts_lock));
386 if (finish_ioctl)
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);
391 tems.ts_hdl = NULL;
392 return (ENXIO);
396 * only called once during boot
399 tem_info_init(char *pathname, cred_t *credp)
401 int lyr_rval, ret;
402 struct vis_devinit temargs;
403 char *pathbuf;
404 size_t height = 0;
405 size_t width = 0;
406 struct tem_vt_state *p;
408 mutex_enter(&tems.ts_lock);
410 if (tems.ts_initialized) {
411 mutex_exit(&tems.ts_lock);
412 return (0);
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);
427 return (ENXIO);
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);
435 return (ENXIO);
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);
450 return (ret);
453 /* Make sure the fb driver and terminal emulator versions match */
454 if (temargs.version != VIS_CONS_REV) {
455 cmn_err(CE_WARN,
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);
460 return (ret);
463 if ((tems.ts_fb_polledio = temargs.polledio) == NULL) {
464 cmn_err(CE_WARN, "terminal emulator: fb doesn't support polled "
465 "I/O");
466 ret = tems_failed(credp, B_TRUE);
467 mutex_exit(&tems.ts_lock);
468 return (ret);
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);
478 return (ret);
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);
485 return (ret);
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);
510 return (0);
513 #define TEMS_DEPTH_DIFF 0x01
514 #define TEMS_DIMENSION_DIFF 0x02
516 static uchar_t
517 tems_check_videomode(struct vis_devinit *tp)
519 uchar_t result = 0;
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;
528 } else {
529 if (tems.ts_p_dimension.width != tp->width ||
530 tems.ts_p_dimension.height != tp->height)
531 result |= TEMS_DIMENSION_DIFF;
534 return (result);
537 static void
538 tems_setup_font(screen_size_t height, screen_size_t width)
540 bitmap_data_t *font_data;
541 int i;
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;
570 static void
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;
583 switch (tp->mode) {
584 case VIS_TEXT:
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);
594 break;
596 case VIS_PIXEL:
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.
601 if (width == 0) {
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;
623 break;
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
644 * whole screen.
646 /* ARGSUSED */
647 void
648 tems_modechange_callback(struct vis_modechg_arg *arg,
649 struct vis_devinit *devinit)
651 uchar_t diff;
652 struct tem_vt_state *p;
653 tem_modechg_cb_t cb;
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);
664 if (diff == 0) {
665 mutex_exit(&tems.ts_lock);
666 return;
669 diff = diff & TEMS_DIMENSION_DIFF;
671 if (diff == 0) {
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);
684 return;
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);
700 return;
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);
710 cb(cb_arg);
714 * This function is used to clear entire screen via the underlying framebuffer
715 * driver.
718 tems_cls_layered(struct vis_consclear *pda,
719 cred_t *credp)
721 int rval;
723 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCLEAR,
724 (intptr_t)pda, FKIOCTL, credp, &rval);
725 return (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.
733 void
734 tems_display_layered(struct vis_consdisplay *pda,
735 cred_t *credp)
737 int rval;
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.
749 void
750 tems_copy_layered(struct vis_conscopy *pma,
751 cred_t *credp)
753 int rval;
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.
763 void
764 tems_cursor_layered(struct vis_conscursor *pca,
765 cred_t *credp)
767 int rval;
769 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCURSOR,
770 (intptr_t)pca, FKIOCTL, credp, &rval);
773 static void
774 tem_kdsetmode(int mode, cred_t *credp)
776 int rval;
778 (void) ldi_ioctl(tems.ts_hdl, KDSETMODE,
779 (intptr_t)mode, FKIOCTL, credp, &rval);
783 static void
784 tems_reset_colormap(cred_t *credp, enum called_from called_from)
786 struct vis_cmap cm;
787 int rval;
789 if (called_from == CALLED_FROM_STANDALONE)
790 return;
792 switch (tems.ts_pdepth) {
793 case 8:
794 cm.index = 0;
795 cm.count = 16;
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);
801 break;
805 void
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);
816 void
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.
831 static void
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;
836 int ncols, width;
838 /* copy */
839 ma.s_row = nrows * tems.ts_font.vf_height;
840 ma.e_row = tems.ts_p_dimension.height - 1;
841 ma.t_row = 0;
843 ma.s_col = 0;
844 ma.e_col = tems.ts_p_dimension.width - 1;
845 ma.t_col = 0;
847 tems_safe_copy(&ma, credp, called_from);
849 /* clear */
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.
864 static int
865 tem_adjust_row(struct tem_vt_state *tem, int prom_row, cred_t *credp,
866 enum called_from called_from)
868 int tem_row;
869 int tem_y;
870 int prom_charheight = 0;
871 int prom_window_top = 0;
872 int scroll_up_lines;
874 if (tems.ts_display_mode == VIS_TEXT)
875 return (prom_row);
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 -
884 tems.ts_p_offset.y;
885 tem_row = (tem_y + tems.ts_font.vf_height - 1) /
886 tems.ts_font.vf_height - 1;
888 if (tem_row < 0) {
889 tem_row = 0;
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;
901 return (tem_row);
904 void
905 tem_align(struct tem_vt_state *tem, cred_t *credp,
906 enum called_from called_from)
908 uint32_t row = 0;
909 uint32_t col = 0;
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
916 * during boot.
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
923 * tem.
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 =
932 (screen_pos_t)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);
949 static void
950 tems_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen)
952 int i_inverse = 0;
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.
965 static void
966 tems_get_initial_color(tem_color_t *pcolor)
968 boolean_t inverse, inverse_screen;
969 unsigned short flags = 0;
970 uint8_t fg, bg;
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);
981 #endif
982 pcolor->fg_color.n = fg;
983 pcolor->bg_color.n = bg;
985 tems_get_inverses(&inverse, &inverse_screen);
986 if (inverse)
987 flags |= TEM_ATTR_REVERSE;
988 if (inverse_screen)
989 flags |= TEM_ATTR_SCREEN_REVERSE;
991 #ifdef _HAVE_TEM_FIRMWARE
992 if (flags != 0) {
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;
999 } else {
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
1004 * match it here.
1006 if (pcolor->bg_color.n == ANSI_COLOR_WHITE)
1007 flags |= TEM_ATTR_BRIGHT_BG;
1009 #else
1010 if (flags != 0) {
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;
1016 } else {
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;
1023 #endif
1025 pcolor->a_flags = flags;
1028 uchar_t
1029 tem_get_fbmode(tem_vt_state_t tem_arg)
1031 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
1033 uchar_t fbmode;
1035 mutex_enter(&tem->tvs_lock);
1036 fbmode = tem->tvs_fbmode;
1037 mutex_exit(&tem->tvs_lock);
1039 return (fbmode);
1042 void
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);
1053 return;
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);
1068 void
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);
1081 if (unblank)
1082 tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL);
1084 mutex_exit(&tem->tvs_lock);
1085 mutex_exit(&tems.ts_lock);
1088 void
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);