summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/tem.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/tem.c')
-rw-r--r--usr/src/uts/common/io/tem.c967
1 files changed, 611 insertions, 356 deletions
diff --git a/usr/src/uts/common/io/tem.c b/usr/src/uts/common/io/tem.c
index 3cc0df18c3..33e42fd982 100644
--- a/usr/src/uts/common/io/tem.c
+++ b/usr/src/uts/common/io/tem.c
@@ -27,8 +27,39 @@
/*
* ANSI terminal emulator module; parse ANSI X3.64 escape sequences and
* the like.
+ *
+ * How Virtual Terminal Emulator Works:
+ *
+ * Every virtual terminal is associated with a tem_vt_state structure
+ * and maintains a virtual screen buffer in tvs_screen_buf, which contains
+ * all the characters which should be shown on the physical screen when
+ * the terminal is activated. There are also two other buffers, tvs_fg_buf
+ * and tvs_bg_buf, which track the foreground and background colors of the
+ * on screen characters
+ *
+ * Data written to a virtual terminal is composed of characters which
+ * should be displayed on the screen when this virtual terminal is
+ * activated, fg/bg colors of these characters, and other control
+ * information (escape sequence, etc).
+ *
+ * When data is passed to a virtual terminal it first is parsed for
+ * control information by tem_safe_parse(). Subsequently the character
+ * and color data are written to tvs_screen_buf, tvs_fg_buf, and
+ * tvs_bg_buf. They are saved in these buffers in order to refresh
+ * the screen when this terminal is activated. If the terminal is
+ * currently active, the data (characters and colors) are also written
+ * to the physical screen by invoking a callback function,
+ * tem_safe_text_callbacks() or tem_safe_pix_callbacks().
+ *
+ * When rendering data to the framebuffer, if the framebuffer is in
+ * VIS_PIXEL mode, the character data will first be converted to pixel
+ * data using tem_safe_pix_bit2pix(), and then the pixels get displayed
+ * on the physical screen. We only store the character and color data in
+ * tem_vt_state since the bit2pix conversion only happens when actually
+ * rendering to the physical framebuffer.
*/
+
#include <sys/types.h>
#include <sys/file.h>
#include <sys/conf.h>
@@ -54,26 +85,33 @@
#include <sys/sunddi.h>
#include <sys/sunldi.h>
#include <sys/tem_impl.h>
-#include <sys/tem.h>
#ifdef _HAVE_TEM_FIRMWARE
#include <sys/promif.h>
#endif /* _HAVE_TEM_FIRMWARE */
#include <sys/consplat.h>
+#include <sys/kd.h>
+#include <sys/sysmacros.h>
+#include <sys/note.h>
+#include <sys/t_lock.h>
+
+/* Terminal emulator internal helper functions */
+static void tems_setup_terminal(struct vis_devinit *, size_t, size_t);
+static void tems_modechange_callback(struct vis_modechg_arg *,
+ struct vis_devinit *);
-/* Terminal emulator functions */
-static int tem_setup_terminal(struct vis_devinit *, tem_t *,
- size_t, size_t);
-static void tem_modechange_callback(tem_t *, struct vis_devinit *);
-static void tem_free(tem_t *);
-static void tem_get_inverses(boolean_t *, boolean_t *);
-static void tem_get_initial_color(tem_t *);
-static int tem_adjust_row(tem_t *, int, cred_t *);
+static void tems_reset_colormap(cred_t *, enum called_from);
+
+static void tem_free_buf(struct tem_vt_state *);
+static void tem_internal_init(struct tem_vt_state *, cred_t *, boolean_t,
+ boolean_t);
+static void tems_get_initial_color(tem_color_t *pcolor);
/*
* Globals
*/
-ldi_ident_t term_li = NULL;
-
+static ldi_ident_t term_li = NULL;
+tem_state_t tems; /* common term info */
+_NOTE(MUTEX_PROTECTS_DATA(tems.ts_lock, tems))
extern struct mod_ops mod_miscops;
@@ -98,6 +136,12 @@ _init(void)
(void) mod_remove(&modlinkage);
return (ret);
}
+
+ mutex_init(&tems.ts_lock, (char *)NULL, MUTEX_DRIVER, NULL);
+ list_create(&tems.ts_list, sizeof (struct tem_vt_state),
+ offsetof(struct tem_vt_state, tvs_list_node));
+ tems.ts_active = NULL;
+
return (0);
}
@@ -120,114 +164,238 @@ _info(struct modinfo *modinfop)
return (mod_info(&modlinkage, modinfop));
}
-int
-tem_fini(tem_t *tem)
+static void
+tem_add(struct tem_vt_state *tem)
{
- int lyr_rval;
+ ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock));
- mutex_enter(&tem->lock);
+ list_insert_head(&tems.ts_list, tem);
+}
- ASSERT(tem->hdl != NULL);
+static void
+tem_rm(struct tem_vt_state *tem)
+{
+ ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock));
- /*
- * Allow layered on driver to clean up console private
- * data.
- */
- (void) ldi_ioctl(tem->hdl, VIS_DEVFINI,
- 0, FKIOCTL, kcred, &lyr_rval);
+ list_remove(&tems.ts_list, tem);
+}
- /*
- * Close layered on driver
- */
- (void) ldi_close(tem->hdl, NULL, kcred);
- tem->hdl = NULL;
+/*
+ * This is the main entry point to the module. It handles output requests
+ * during normal system operation, when (e.g.) mutexes are available.
+ */
+void
+tem_write(tem_vt_state_t tem_arg, uchar_t *buf, ssize_t len, cred_t *credp)
+{
+ struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
- mutex_exit(&tem->lock);
+ mutex_enter(&tems.ts_lock);
+ mutex_enter(&tem->tvs_lock);
- tem_free(tem);
+ if (!tem->tvs_initialized) {
+ mutex_exit(&tem->tvs_lock);
+ mutex_exit(&tems.ts_lock);
+ return;
+ }
- return (0);
+ tem_safe_check_first_time(tem, credp, CALLED_FROM_NORMAL);
+ tem_safe_terminal_emulate(tem, buf, len, credp, CALLED_FROM_NORMAL);
+
+ mutex_exit(&tem->tvs_lock);
+ mutex_exit(&tems.ts_lock);
}
-static int
-tem_init_failed(tem_t *tem, cred_t *credp, boolean_t finish_ioctl)
+static void
+tem_internal_init(struct tem_vt_state *ptem, cred_t *credp,
+ boolean_t init_color, boolean_t clear_screen)
{
- int lyr_rval;
+ int i, j;
+ int width, height;
+ int total;
+ text_color_t fg;
+ text_color_t bg;
+ size_t tc_size = sizeof (text_color_t);
+
+ ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&ptem->tvs_lock));
+
+ if (tems.ts_display_mode == VIS_PIXEL) {
+ ptem->tvs_pix_data_size = tems.ts_pix_data_size;
+ ptem->tvs_pix_data =
+ kmem_alloc(ptem->tvs_pix_data_size, KM_SLEEP);
+ }
- if (finish_ioctl)
- (void) ldi_ioctl(tem->hdl, VIS_DEVFINI, 0, FWRITE|FKIOCTL,
- credp, &lyr_rval);
+ ptem->tvs_outbuf_size = tems.ts_c_dimension.width;
+ ptem->tvs_outbuf =
+ (unsigned char *)kmem_alloc(ptem->tvs_outbuf_size, KM_SLEEP);
- (void) ldi_close(tem->hdl, NULL, credp);
- tem_free(tem);
- return (ENXIO);
+ width = tems.ts_c_dimension.width;
+ height = tems.ts_c_dimension.height;
+ ptem->tvs_screen_buf_size = width * height;
+ ptem->tvs_screen_buf =
+ (unsigned char *)kmem_alloc(width * height, KM_SLEEP);
+
+ total = width * height * tc_size;
+ ptem->tvs_fg_buf = (text_color_t *)kmem_alloc(total, KM_SLEEP);
+ ptem->tvs_bg_buf = (text_color_t *)kmem_alloc(total, KM_SLEEP);
+ ptem->tvs_color_buf_size = total;
+
+ tem_safe_reset_display(ptem, credp, CALLED_FROM_NORMAL,
+ clear_screen, init_color);
+
+ tem_safe_get_color(ptem, &fg, &bg, TEM_ATTR_SCREEN_REVERSE);
+ for (i = 0; i < height; i++)
+ for (j = 0; j < width; j++) {
+ ptem->tvs_screen_buf[i * width + j] = ' ';
+ ptem->tvs_fg_buf[(i * width +j) * tc_size] = fg;
+ ptem->tvs_bg_buf[(i * width +j) * tc_size] = bg;
+
+ }
+
+ ptem->tvs_initialized = 1;
}
-static void
-tem_free_state(struct tem_state *tems)
+int
+tem_initialized(tem_vt_state_t tem_arg)
{
- ASSERT(tems != NULL);
+ struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg;
+ int ret;
+
+ mutex_enter(&ptem->tvs_lock);
+ ret = ptem->tvs_initialized;
+ mutex_exit(&ptem->tvs_lock);
- if (tems->a_outbuf != NULL)
- kmem_free(tems->a_outbuf,
- tems->a_c_dimension.width);
- if (tems->a_blank_line != NULL)
- kmem_free(tems->a_blank_line,
- tems->a_c_dimension.width);
- if (tems->a_pix_data != NULL)
- kmem_free(tems->a_pix_data,
- tems->a_pix_data_size);
- kmem_free(tems, sizeof (struct tem_state));
+ return (ret);
}
-static void
-tem_free(tem_t *tem)
+tem_vt_state_t
+tem_init(cred_t *credp)
{
- ASSERT(tem != NULL);
+ struct tem_vt_state *ptem;
+
+ ptem = kmem_zalloc(sizeof (struct tem_vt_state), KM_SLEEP);
+ mutex_init(&ptem->tvs_lock, (char *)NULL, MUTEX_DRIVER, NULL);
+
+ mutex_enter(&tems.ts_lock);
+ mutex_enter(&ptem->tvs_lock);
+
+ ptem->tvs_isactive = B_FALSE;
+ ptem->tvs_fbmode = KD_TEXT;
+
+ /*
+ * A tem is regarded as initialized only after tem_internal_init(),
+ * will be set at the end of tem_internal_init().
+ */
+ ptem->tvs_initialized = 0;
+
- if (tem->state != NULL)
- tem_free_state(tem->state);
+ if (!tems.ts_initialized) {
+ /*
+ * Only happens during early console configuration.
+ */
+ tem_add(ptem);
+ mutex_exit(&ptem->tvs_lock);
+ mutex_exit(&tems.ts_lock);
+ return ((tem_vt_state_t)ptem);
+ }
- kmem_free(tem, sizeof (struct tem));
+ tem_internal_init(ptem, credp, B_TRUE, B_FALSE);
+ tem_add(ptem);
+ mutex_exit(&ptem->tvs_lock);
+ mutex_exit(&tems.ts_lock);
+
+ return ((tem_vt_state_t)ptem);
}
/*
- * This is the main entry point to the module. It handles output requests
- * during normal system operation, when (e.g.) mutexes are available.
+ * re-init the tem after video mode has changed and tems_info has
+ * been re-inited. The lock is already held.
*/
+static void
+tem_reinit(struct tem_vt_state *tem, boolean_t reset_display)
+{
+ ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock));
+
+ tem_free_buf(tem); /* only free virtual buffers */
+
+ /* reserve color */
+ tem_internal_init(tem, kcred, B_FALSE, reset_display);
+}
+
+static void
+tem_free_buf(struct tem_vt_state *tem)
+{
+ ASSERT(tem != NULL && MUTEX_HELD(&tem->tvs_lock));
+
+ if (tem->tvs_outbuf != NULL)
+ kmem_free(tem->tvs_outbuf, tem->tvs_outbuf_size);
+ if (tem->tvs_pix_data != NULL)
+ kmem_free(tem->tvs_pix_data, tem->tvs_pix_data_size);
+ if (tem->tvs_screen_buf != NULL)
+ kmem_free(tem->tvs_screen_buf, tem->tvs_screen_buf_size);
+ if (tem->tvs_fg_buf != NULL)
+ kmem_free(tem->tvs_fg_buf, tem->tvs_color_buf_size);
+ if (tem->tvs_bg_buf != NULL)
+ kmem_free(tem->tvs_bg_buf, tem->tvs_color_buf_size);
+}
+
void
-tem_write(tem_t *tem, uchar_t *buf, ssize_t len, cred_t *credp)
+tem_destroy(tem_vt_state_t tem_arg, cred_t *credp)
{
- mutex_enter(&tem->lock);
+ struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
- ASSERT(tem->hdl != NULL);
+ mutex_enter(&tems.ts_lock);
+ mutex_enter(&tem->tvs_lock);
- tem_check_first_time(tem, credp, CALLED_FROM_NORMAL);
- tem_terminal_emulate(tem, buf, len, credp, CALLED_FROM_NORMAL);
+ if (tem->tvs_isactive && tem->tvs_fbmode == KD_TEXT)
+ tem_safe_blank_screen(tem, credp, CALLED_FROM_NORMAL);
- mutex_exit(&tem->lock);
+ tem_free_buf(tem);
+ tem_rm(tem);
+
+ if (tems.ts_active == tem)
+ tems.ts_active = NULL;
+
+ mutex_exit(&tem->tvs_lock);
+ mutex_exit(&tems.ts_lock);
+
+ kmem_free(tem, sizeof (struct tem_vt_state));
+}
+
+static int
+tems_failed(cred_t *credp, boolean_t finish_ioctl)
+{
+ int lyr_rval;
+
+ ASSERT(MUTEX_HELD(&tems.ts_lock));
+
+ if (finish_ioctl)
+ (void) ldi_ioctl(tems.ts_hdl, VIS_DEVFINI, 0,
+ FWRITE|FKIOCTL, credp, &lyr_rval);
+
+ (void) ldi_close(tems.ts_hdl, NULL, credp);
+ tems.ts_hdl = NULL;
+ return (ENXIO);
}
+/*
+ * only called once during boot
+ */
int
-tem_init(tem_t **ptem, char *pathname, cred_t *credp)
+tem_info_init(char *pathname, cred_t *credp)
{
- struct vis_devinit devinit;
- tem_t *tem;
+ int lyr_rval, ret;
+ struct vis_devinit temargs;
+ char *pathbuf;
size_t height = 0;
size_t width = 0;
- uint32_t row = 0;
- uint32_t col = 0;
- char *pathbuf;
- int err = 0;
- int lyr_rval;
+ struct tem_vt_state *p;
- tem = kmem_zalloc(sizeof (struct tem), KM_SLEEP);
+ mutex_enter(&tems.ts_lock);
- mutex_init(&tem->lock, (char *)NULL, MUTEX_DRIVER, NULL);
-
-#ifdef _HAVE_TEM_FIRMWARE
- tem->cons_wrtvec = tem_write;
-#endif /* _HAVE_TEM_FIRMWARE */
+ if (tems.ts_initialized) {
+ mutex_exit(&tems.ts_lock);
+ return (0);
+ }
/*
* Open the layered device using the devfs physical device name
@@ -237,220 +405,143 @@ tem_init(tem_t **ptem, char *pathname, cred_t *credp)
(void) strcpy(pathbuf, "/devices");
if (i_ddi_prompath_to_devfspath(pathname,
pathbuf + strlen("/devices")) != DDI_SUCCESS) {
- cmn_err(CE_WARN, "terminal emulator: Path conversion error");
+ cmn_err(CE_WARN, "terminal-emulator: path conversion error");
kmem_free(pathbuf, MAXPATHLEN);
- tem_free(tem);
+
+ mutex_exit(&tems.ts_lock);
return (ENXIO);
}
- if (ldi_open_by_name(pathbuf, FWRITE, credp, &tem->hdl, term_li) != 0) {
- cmn_err(CE_WARN, "terminal emulator: Device path open error");
+ if (ldi_open_by_name(pathbuf, FWRITE, credp,
+ &tems.ts_hdl, term_li) != 0) {
+ cmn_err(CE_WARN, "terminal-emulator: device path open error");
kmem_free(pathbuf, MAXPATHLEN);
- tem_free(tem);
+
+ mutex_exit(&tems.ts_lock);
return (ENXIO);
}
kmem_free(pathbuf, MAXPATHLEN);
- devinit.modechg_cb = (vis_modechg_cb_t)tem_modechange_callback;
- devinit.modechg_arg = (struct vis_modechg_arg *)tem;
+ temargs.modechg_cb = (vis_modechg_cb_t)tems_modechange_callback;
+ temargs.modechg_arg = NULL;
/*
* Initialize the console and get the device parameters
*/
- if ((err = ldi_ioctl(tem->hdl, VIS_DEVINIT,
- (intptr_t)&devinit, FWRITE|FKIOCTL, credp, &lyr_rval)) != 0) {
+ if (ldi_ioctl(tems.ts_hdl, VIS_DEVINIT,
+ (intptr_t)&temargs, FWRITE|FKIOCTL, credp, &lyr_rval) != 0) {
cmn_err(CE_WARN, "terminal emulator: Compatible fb not found");
- return (tem_init_failed(tem, credp, B_FALSE));
+ ret = tems_failed(credp, B_FALSE);
+ mutex_exit(&tems.ts_lock);
+ return (ret);
}
/* Make sure the fb driver and terminal emulator versions match */
- if (devinit.version != VIS_CONS_REV) {
+ if (temargs.version != VIS_CONS_REV) {
cmn_err(CE_WARN,
"terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) "
- "of console fb driver not supported", devinit.version);
- return (tem_init_failed(tem, credp, B_TRUE));
+ "of console fb driver not supported", temargs.version);
+ ret = tems_failed(credp, B_TRUE);
+ mutex_exit(&tems.ts_lock);
+ return (ret);
}
- if ((tem->fb_polledio = devinit.polledio) == NULL) {
+ if ((tems.ts_fb_polledio = temargs.polledio) == NULL) {
cmn_err(CE_WARN, "terminal emulator: fb doesn't support polled "
"I/O");
- return (tem_init_failed(tem, credp, B_TRUE));
+ ret = tems_failed(credp, B_TRUE);
+ mutex_exit(&tems.ts_lock);
+ return (ret);
}
/* other sanity checks */
- if (!((devinit.depth == 4) || (devinit.depth == 8) ||
- (devinit.depth == 24) || (devinit.depth == 32))) {
+ if (!((temargs.depth == 4) || (temargs.depth == 8) ||
+ (temargs.depth == 24) || (temargs.depth == 32))) {
cmn_err(CE_WARN, "terminal emulator: unsupported depth");
- return (tem_init_failed(tem, credp, B_TRUE));
+ ret = tems_failed(credp, B_TRUE);
+ mutex_exit(&tems.ts_lock);
+ return (ret);
}
- if ((devinit.mode != VIS_TEXT) && (devinit.mode != VIS_PIXEL)) {
+ if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) {
cmn_err(CE_WARN, "terminal emulator: unsupported mode");
- return (tem_init_failed(tem, credp, B_TRUE));
+ ret = tems_failed(credp, B_TRUE);
+ mutex_exit(&tems.ts_lock);
+ return (ret);
}
- if ((devinit.mode == VIS_PIXEL) && plat_stdout_is_framebuffer()) {
+ if ((temargs.mode == VIS_PIXEL) && plat_stdout_is_framebuffer())
plat_tem_get_prom_size(&height, &width);
- }
/*
- * Initialize the terminal emulator
+ * Initialize the common terminal emulator info
*/
- mutex_enter(&tem->lock);
- if ((err = tem_setup_terminal(&devinit, tem, height, width)) != 0) {
- cmn_err(CE_WARN, "terminal emulator: Init failed");
- (void) ldi_ioctl(tem->hdl, VIS_DEVFINI, 0, FWRITE|FKIOCTL,
- credp, &lyr_rval);
- (void) ldi_close(tem->hdl, NULL, credp);
- mutex_exit(&tem->lock);
- tem_free(tem);
- return (err);
- }
+ tems_setup_terminal(&temargs, height, width);
- /*
- * make our kernel console keep compatibility with OBP.
- */
- tem_get_initial_color(tem);
+ tems_reset_colormap(credp, CALLED_FROM_NORMAL);
+ tems_get_initial_color(&tems.ts_init_color);
- /*
- * On SPARC don't clear the screen if the console is the framebuffer.
- * Otherwise it needs to be cleared to get rid of junk that may be
- * in frameuffer memory, since the screen isn't cleared when
- * boot messages are directed elsewhere.
- */
- if (devinit.mode == VIS_TEXT) {
- /*
- * The old getting current cursor position code, which
- * is not needed here, has been in tem_write/tem_polled_write.
- */
- tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 0, NULL);
- } else if (plat_stdout_is_framebuffer()) {
- ASSERT(devinit.mode == VIS_PIXEL);
- plat_tem_hide_prom_cursor();
- tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 0, NULL);
-
- /*
- * We are getting the current cursor position in pixel
- * mode so that we don't over-write the console output
- * during boot.
- */
- plat_tem_get_prom_pos(&row, &col);
-
- /*
- * Adjust the row if necessary when the font of our
- * kernel console tem is different with that of prom
- * tem.
- */
- row = tem_adjust_row(tem, row, credp);
+ tems.ts_initialized = 1; /* initialization flag */
- /* first line of our kernel console output */
- tem->state->first_line = row + 1;
-
- /* re-set and align cusror position */
- tem->state->a_c_cursor.row = row;
- tem->state->a_c_cursor.col = 0;
- tem_align_cursor(tem);
- } else {
- tem_reset_display(tem, credp, CALLED_FROM_NORMAL, 1, NULL);
+ for (p = list_head(&tems.ts_list); p != NULL;
+ p = list_next(&tems.ts_list, p)) {
+ mutex_enter(&p->tvs_lock);
+ tem_internal_init(p, credp, B_TRUE, B_FALSE);
+ if (temargs.mode == VIS_PIXEL)
+ tem_pix_align(p, credp, CALLED_FROM_NORMAL);
+ mutex_exit(&p->tvs_lock);
}
-#ifdef _HAVE_TEM_FIRMWARE
- if (plat_stdout_is_framebuffer()) {
- /*
- * Drivers in the console stream may emit additional
- * messages before we are ready. This causes text
- * overwrite on the screen. So we set the redirection
- * here. It is safe because the ioctl in consconfig_dacf
- * will succeed and consmode will be set to CONS_KFB.
- */
- prom_set_stdout_redirect(console_prom_write_cb,
- (promif_redir_arg_t)tem);
-
- }
-#endif /* _HAVE_TEM_FIRMWARE */
-
- mutex_exit(&tem->lock);
- *ptem = tem; /* Return tem to caller only upon success */
+ mutex_exit(&tems.ts_lock);
return (0);
}
-/*
- * This is a callback function that we register with the frame
- * buffer driver layered underneath. It gets invoked from
- * the underlying frame buffer driver to reconfigure the terminal
- * emulator to a new screen size and depth in conjunction with
- * framebuffer videomode changes.
- * Here we keep the foreground/background color and attributes,
- * which may be different with the initial settings, so that
- * the color won't change while the framebuffer videomode changes.
- * And we also reset the kernel terminal emulator and clear the
- * whole screen.
- */
-void
-tem_modechange_callback(tem_t *tem, struct vis_devinit *devinit)
-{
- tem_color_t tc;
+#define TEMS_DEPTH_DIFF 0x01
+#define TEMS_DIMENSION_DIFF 0x02
- mutex_enter(&tem->lock);
-
- ASSERT(tem->hdl != NULL);
-
- tc.fg_color = tem->state->fg_color;
- tc.bg_color = tem->state->bg_color;
- tc.a_flags = tem->state->a_flags;
-
- (void) tem_setup_terminal(devinit, tem,
- tem->state->a_c_dimension.height,
- tem->state->a_c_dimension.width);
+static uchar_t
+tems_check_videomode(struct vis_devinit *tp)
+{
+ uchar_t result = 0;
- tem_reset_display(tem, kcred, CALLED_FROM_NORMAL, 1, &tc);
+ if (tems.ts_pdepth != tp->depth)
+ result |= TEMS_DEPTH_DIFF;
- mutex_exit(&tem->lock);
+ if (tp->mode == VIS_TEXT) {
+ if (tems.ts_c_dimension.width != tp->width ||
+ tems.ts_c_dimension.height != tp->height)
+ result |= TEMS_DIMENSION_DIFF;
+ } else {
+ if (tems.ts_p_dimension.width != tp->width ||
+ tems.ts_p_dimension.height != tp->height)
+ result |= TEMS_DIMENSION_DIFF;
+ }
- if (tem->modechg_cb != NULL)
- tem->modechg_cb(tem->modechg_arg);
+ return (result);
}
-static int
-tem_setup_terminal(
- struct vis_devinit *devinit,
- tem_t *tem,
- size_t height, size_t width)
+static void
+tems_setup_terminal(struct vis_devinit *tp, size_t height, size_t width)
{
int i;
- struct tem_state *new_state, *prev_state;
-
- ASSERT(MUTEX_HELD(&tem->lock));
+ int old_blank_buf_size = tems.ts_c_dimension.width;
- prev_state = tem->state;
+ ASSERT(MUTEX_HELD(&tems.ts_lock));
- new_state = kmem_zalloc(sizeof (struct tem_state), KM_SLEEP);
+ tems.ts_pdepth = tp->depth;
+ tems.ts_linebytes = tp->linebytes;
+ tems.ts_display_mode = tp->mode;
- new_state->a_pdepth = devinit->depth;
- new_state->display_mode = devinit->mode;
- new_state->linebytes = devinit->linebytes;
-
- switch (devinit->mode) {
+ switch (tp->mode) {
case VIS_TEXT:
- new_state->a_p_dimension.width = 0;
- new_state->a_p_dimension.height = 0;
- new_state->a_c_dimension.width = devinit->width;
- new_state->a_c_dimension.height = devinit->height;
-
- new_state->in_fp.f_display = tem_text_display;
- new_state->in_fp.f_copy = tem_text_copy;
- new_state->in_fp.f_cursor = tem_text_cursor;
- new_state->in_fp.f_cls = tem_text_cls;
- new_state->in_fp.f_bit2pix = NULL;
-
- new_state->a_blank_line =
- kmem_alloc(new_state->a_c_dimension.width, KM_SLEEP);
-
- for (i = 0; i < new_state->a_c_dimension.width; i++)
- new_state->a_blank_line[i] = ' ';
+ tems.ts_p_dimension.width = 0;
+ tems.ts_p_dimension.height = 0;
+ tems.ts_c_dimension.width = tp->width;
+ tems.ts_c_dimension.height = tp->height;
+ tems.ts_callbacks = &tem_safe_text_callbacks;
break;
- case VIS_PIXEL:
+ case VIS_PIXEL:
/*
* First check to see if the user has specified a screen size.
* If so, use those values. Else use 34x80 as the default.
@@ -459,18 +550,13 @@ tem_setup_terminal(
width = TEM_DEFAULT_COLS;
height = TEM_DEFAULT_ROWS;
}
- new_state->a_c_dimension.height = height;
- new_state->a_c_dimension.width = width;
-
- new_state->a_p_dimension.height = devinit->height;
- new_state->a_p_dimension.width = devinit->width;
+ tems.ts_c_dimension.height = (screen_size_t)height;
+ tems.ts_c_dimension.width = (screen_size_t)width;
- new_state->in_fp.f_display = tem_pix_display;
- new_state->in_fp.f_copy = tem_pix_copy;
- new_state->in_fp.f_cursor = tem_pix_cursor;
- new_state->in_fp.f_cls = tem_pix_cls;
+ tems.ts_p_dimension.height = tp->height;
+ tems.ts_p_dimension.width = tp->width;
- new_state->a_blank_line = NULL;
+ tems.ts_callbacks = &tem_safe_pix_callbacks;
/*
* set_font() will select a appropriate sized font for
@@ -479,75 +565,114 @@ tem_setup_terminal(
* default builtin font and adjust the rows and columns
* to fit on the screen.
*/
- set_font(&new_state->a_font,
- &new_state->a_c_dimension.height,
- &new_state->a_c_dimension.width,
- new_state->a_p_dimension.height,
- new_state->a_p_dimension.width);
-
- new_state->a_p_offset.y =
- (new_state->a_p_dimension.height -
- (new_state->a_c_dimension.height *
- new_state->a_font.height)) / 2;
-
- new_state->a_p_offset.x =
- (new_state->a_p_dimension.width -
- (new_state->a_c_dimension.width *
- new_state->a_font.width)) / 2;
-
- switch (devinit->depth) {
- case 4:
- new_state->in_fp.f_bit2pix = bit_to_pix4;
- new_state->a_pix_data_size =
- (((new_state->a_font.width * 4) +
- NBBY - 1) / NBBY) * new_state->a_font.height;
- break;
- case 8:
- new_state->in_fp.f_bit2pix = bit_to_pix8;
- new_state->a_pix_data_size =
- new_state->a_font.width *
- new_state->a_font.height;
- break;
- case 24:
- case 32:
- new_state->in_fp.f_bit2pix = bit_to_pix24;
- new_state->a_pix_data_size =
- new_state->a_font.width *
- new_state->a_font.height;
- new_state->a_pix_data_size *= 4;
- break;
- }
+ set_font(&tems.ts_font,
+ &tems.ts_c_dimension.height,
+ &tems.ts_c_dimension.width,
+ tems.ts_p_dimension.height,
+ tems.ts_p_dimension.width);
+
+ tems.ts_p_offset.y = (tems.ts_p_dimension.height -
+ (tems.ts_c_dimension.height * tems.ts_font.height)) / 2;
+ tems.ts_p_offset.x = (tems.ts_p_dimension.width -
+ (tems.ts_c_dimension.width * tems.ts_font.width)) / 2;
- new_state->a_pix_data =
- kmem_alloc(new_state->a_pix_data_size, KM_SLEEP);
+ tems.ts_pix_data_size =
+ tems.ts_font.width * tems.ts_font.height;
+
+ tems.ts_pix_data_size *= 4;
+
+ tems.ts_pdepth = tp->depth;
break;
+ }
+
+ /* Now virtual cls also uses the blank_line buffer */
+ if (tems.ts_blank_line)
+ kmem_free(tems.ts_blank_line, old_blank_buf_size);
- default:
+ tems.ts_blank_line = (unsigned char *)
+ kmem_alloc(tems.ts_c_dimension.width, KM_SLEEP);
+ for (i = 0; i < tems.ts_c_dimension.width; i++)
+ tems.ts_blank_line[i] = ' ';
+}
+
+/*
+ * This is a callback function that we register with the frame
+ * buffer driver layered underneath. It gets invoked from
+ * the underlying frame buffer driver to reconfigure the terminal
+ * emulator to a new screen size and depth in conjunction with
+ * framebuffer videomode changes.
+ * Here we keep the foreground/background color and attributes,
+ * which may be different with the initial settings, so that
+ * the color won't change while the framebuffer videomode changes.
+ * And we also reset the kernel terminal emulator and clear the
+ * whole screen.
+ */
+/* ARGSUSED */
+void
+tems_modechange_callback(struct vis_modechg_arg *arg,
+ struct vis_devinit *devinit)
+{
+ uchar_t diff;
+ struct tem_vt_state *p;
+ tem_modechg_cb_t cb;
+ tem_modechg_cb_arg_t cb_arg;
+
+ ASSERT(!(list_is_empty(&tems.ts_list)));
+
+ mutex_enter(&tems.ts_lock);
+
+ /*
+ * currently only for pixel mode
+ */
+ diff = tems_check_videomode(devinit);
+ if (diff == 0) {
+ mutex_exit(&tems.ts_lock);
+ return;
+ }
+
+ diff = diff & TEMS_DIMENSION_DIFF;
+
+ if (diff == 0) {
/*
- * The layered fb driver conveyed an unrecognized rendering
- * mode. We cannot proceed with tem initialization.
+ * Only need to reinit the active tem.
*/
- kmem_free(new_state, sizeof (struct tem_state));
- return (ENXIO);
+ struct tem_vt_state *active = tems.ts_active;
+ tems.ts_pdepth = devinit->depth;
+
+ mutex_enter(&active->tvs_lock);
+ ASSERT(active->tvs_isactive);
+ tem_reinit(active, B_TRUE);
+ mutex_exit(&active->tvs_lock);
+
+ mutex_exit(&tems.ts_lock);
+ return;
}
- new_state->a_outbuf =
- kmem_alloc(new_state->a_c_dimension.width, KM_SLEEP);
+ tems_setup_terminal(devinit, tems.ts_c_dimension.height,
+ tems.ts_c_dimension.width);
- /*
- * Change state atomically so that polled I/O requests
- * can be safely and reliably serviced anytime after the terminal
- * emulator is originally initialized and the console mode has been
- * switched over from the PROM, even while a videomode change
- * callback is being processed.
- */
- tem->state = new_state;
+ for (p = list_head(&tems.ts_list); p != NULL;
+ p = list_next(&tems.ts_list, p)) {
+ mutex_enter(&p->tvs_lock);
+ tem_reinit(p, p->tvs_isactive);
+ mutex_exit(&p->tvs_lock);
+ }
- if (prev_state != NULL)
- tem_free_state(prev_state);
- return (0);
+ if (tems.ts_modechg_cb == NULL) {
+ mutex_exit(&tems.ts_lock);
+ return;
+ }
+
+ cb = tems.ts_modechg_cb;
+ cb_arg = tems.ts_modechg_arg;
+
+ /*
+ * Release the lock while doing callback.
+ */
+ mutex_exit(&tems.ts_lock);
+ cb(cb_arg);
}
/*
@@ -556,14 +681,13 @@ tem_setup_terminal(
* The blit can be as small as a pixel or as large as the screen.
*/
void
-tem_display_layered(
- tem_t *tem,
+tems_display_layered(
struct vis_consdisplay *pda,
cred_t *credp)
{
int rval;
- (void) ldi_ioctl(tem->hdl, VIS_CONSDISPLAY,
+ (void) ldi_ioctl(tems.ts_hdl, VIS_CONSDISPLAY,
(intptr_t)pda, FKIOCTL, credp, &rval);
}
@@ -574,14 +698,13 @@ tem_display_layered(
* such as from vi when deleting characters and words.
*/
void
-tem_copy_layered(
- tem_t *tem,
+tems_copy_layered(
struct vis_conscopy *pma,
cred_t *credp)
{
int rval;
- (void) ldi_ioctl(tem->hdl, VIS_CONSCOPY,
+ (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCOPY,
(intptr_t)pma, FKIOCTL, credp, &rval);
}
@@ -590,22 +713,28 @@ tem_copy_layered(
* pixel inverting, text block cursor via the underlying framebuffer.
*/
void
-tem_cursor_layered(
- tem_t *tem,
+tems_cursor_layered(
struct vis_conscursor *pca,
cred_t *credp)
{
int rval;
- (void) ldi_ioctl(tem->hdl, VIS_CONSCURSOR,
+ (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCURSOR,
(intptr_t)pca, FKIOCTL, credp, &rval);
}
-void
-tem_reset_colormap(
- tem_t *tem,
- cred_t *credp,
- enum called_from called_from)
+static void
+tem_kdsetmode(int mode, cred_t *credp)
+{
+ int rval;
+
+ (void) ldi_ioctl(tems.ts_hdl, KDSETMODE,
+ (intptr_t)mode, FKIOCTL, credp, &rval);
+
+}
+
+static void
+tems_reset_colormap(cred_t *credp, enum called_from called_from)
{
struct vis_cmap cm;
int rval;
@@ -613,35 +742,41 @@ tem_reset_colormap(
if (called_from == CALLED_FROM_STANDALONE)
return;
- switch (tem->state->a_pdepth) {
+ switch (tems.ts_pdepth) {
case 8:
cm.index = 0;
cm.count = 16;
cm.red = cmap4_to_24.red; /* 8-bits (1/3 of TrueColor 24) */
cm.blue = cmap4_to_24.blue; /* 8-bits (1/3 of TrueColor 24) */
cm.green = cmap4_to_24.green; /* 8-bits (1/3 of TrueColor 24) */
- (void) ldi_ioctl(tem->hdl, VIS_PUTCMAP, (intptr_t)&cm,
+ (void) ldi_ioctl(tems.ts_hdl, VIS_PUTCMAP, (intptr_t)&cm,
FKIOCTL, credp, &rval);
break;
}
}
void
-tem_get_size(tem_t *tem, ushort_t *r, ushort_t *c,
+tem_get_size(ushort_t *r, ushort_t *c,
ushort_t *x, ushort_t *y)
{
- *r = (ushort_t)tem->state->a_c_dimension.height;
- *c = (ushort_t)tem->state->a_c_dimension.width;
- *x = (ushort_t)tem->state->a_p_dimension.width;
- *y = (ushort_t)tem->state->a_p_dimension.height;
+ mutex_enter(&tems.ts_lock);
+ *r = (ushort_t)tems.ts_c_dimension.height;
+ *c = (ushort_t)tems.ts_c_dimension.width;
+ *x = (ushort_t)tems.ts_p_dimension.width;
+ *y = (ushort_t)tems.ts_p_dimension.height;
+ mutex_exit(&tems.ts_lock);
}
void
-tem_register_modechg_cb(tem_t *tem, tem_modechg_cb_t func,
+tem_register_modechg_cb(tem_modechg_cb_t func,
tem_modechg_cb_arg_t arg)
{
- tem->modechg_cb = func;
- tem->modechg_arg = arg;
+ mutex_enter(&tems.ts_lock);
+
+ tems.ts_modechg_cb = func;
+ tems.ts_modechg_arg = arg;
+
+ mutex_exit(&tems.ts_lock);
}
/*
@@ -649,43 +784,41 @@ tem_register_modechg_cb(tem_t *tem, tem_modechg_cb_t func,
* different screen height and width with our kernel console.
*/
static void
-tem_prom_scroll_up(struct tem *tem, int nrows, cred_t *credp)
+tem_prom_scroll_up(struct tem_vt_state *tem, int nrows, cred_t *credp,
+ enum called_from called_from)
{
- struct tem_state *tems = tem->state;
struct vis_conscopy ma;
int ncols, width;
/* copy */
- ma.s_row = nrows * tems->a_font.height;
- ma.e_row = tems->a_p_dimension.height - 1;
+ ma.s_row = nrows * tems.ts_font.height;
+ ma.e_row = tems.ts_p_dimension.height - 1;
ma.t_row = 0;
ma.s_col = 0;
- ma.e_col = tems->a_p_dimension.width - 1;
+ ma.e_col = tems.ts_p_dimension.width - 1;
ma.t_col = 0;
- tem_copy(tem, &ma, credp, CALLED_FROM_NORMAL);
+ tems_safe_copy(&ma, credp, called_from);
/* clear */
- width = tems->a_font.width;
- ncols = (tems->a_p_dimension.width +
- (width - 1))/ width;
+ width = tems.ts_font.width;
+ ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
- tem_pix_cls_range(tem,
- 0, nrows, tems->a_p_offset.y,
- 0, ncols, 0,
- B_TRUE, credp, CALLED_FROM_NORMAL);
+ tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y,
+ 0, ncols, 0, B_TRUE, credp, called_from);
}
#define PROM_DEFAULT_FONT_HEIGHT 22
-#define PROM_DEFAULT_WINDOW_TOP 0x8a
+#define PROM_DEFAULT_WINDOW_TOP 0x8a
/*
* This function is to compute the starting row of the console, according to
* PROM cursor's position. Here we have to take different fonts into account.
*/
static int
-tem_adjust_row(tem_t *tem, int prom_row, cred_t *credp)
+tem_adjust_row(struct tem_vt_state *tem, int prom_row, cred_t *credp,
+ enum called_from called_from)
{
int tem_row;
int tem_y;
@@ -700,28 +833,64 @@ tem_adjust_row(tem_t *tem, int prom_row, cred_t *credp)
prom_window_top = PROM_DEFAULT_WINDOW_TOP;
tem_y = (prom_row + 1) * prom_charheight + prom_window_top -
- tem->state->a_p_offset.y;
- tem_row = (tem_y + tem->state->a_font.height - 1) /
- tem->state->a_font.height - 1;
+ tems.ts_p_offset.y;
+ tem_row = (tem_y + tems.ts_font.height - 1) /
+ tems.ts_font.height - 1;
if (tem_row < 0) {
tem_row = 0;
- } else if (tem_row >= (tem->state->a_c_dimension.height - 1)) {
+ } else if (tem_row >= (tems.ts_c_dimension.height - 1)) {
/*
* Scroll up the prom outputs if the PROM cursor's position is
* below our tem's lower boundary.
*/
scroll_up_lines = tem_row -
- (tem->state->a_c_dimension.height - 1);
- tem_prom_scroll_up(tem, scroll_up_lines, credp);
- tem_row = tem->state->a_c_dimension.height - 1;
+ (tems.ts_c_dimension.height - 1);
+ tem_prom_scroll_up(tem, scroll_up_lines, credp, called_from);
+ tem_row = tems.ts_c_dimension.height - 1;
}
return (tem_row);
}
+void
+tem_pix_align(struct tem_vt_state *tem, cred_t *credp,
+ enum called_from called_from)
+{
+ uint32_t row = 0;
+ uint32_t col = 0;
+
+ if (plat_stdout_is_framebuffer()) {
+ plat_tem_hide_prom_cursor();
+
+ /*
+ * We are getting the current cursor position in pixel
+ * mode so that we don't over-write the console output
+ * during boot.
+ */
+ plat_tem_get_prom_pos(&row, &col);
+
+ /*
+ * Adjust the row if necessary when the font of our
+ * kernel console tem is different with that of prom
+ * tem.
+ */
+ row = tem_adjust_row(tem, row, credp, called_from);
+
+ /* first line of our kernel console output */
+ tem->tvs_first_line = row + 1;
+
+ /* re-set and align cusror position */
+ tem->tvs_s_cursor.row = tem->tvs_c_cursor.row =
+ (screen_pos_t)row;
+ tem->tvs_s_cursor.col = tem->tvs_c_cursor.col = 0;
+ } else {
+ tem_safe_reset_display(tem, credp, called_from, B_TRUE, B_TRUE);
+ }
+}
+
static void
-tem_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen)
+tems_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen)
{
int i_inverse = 0;
int i_inverse_screen = 0;
@@ -737,16 +906,16 @@ tem_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen)
* PROM, so that our kernel console can keep the same visual behaviour.
*/
static void
-tem_get_initial_color(tem_t *tem)
+tems_get_initial_color(tem_color_t *pcolor)
{
boolean_t inverse, inverse_screen;
unsigned short flags = 0;
- tem->init_color.fg_color = DEFAULT_ANSI_FOREGROUND;
- tem->init_color.bg_color = DEFAULT_ANSI_BACKGROUND;
+ pcolor->fg_color = DEFAULT_ANSI_FOREGROUND;
+ pcolor->bg_color = DEFAULT_ANSI_BACKGROUND;
if (plat_stdout_is_framebuffer()) {
- tem_get_inverses(&inverse, &inverse_screen);
+ tems_get_inverses(&inverse, &inverse_screen);
if (inverse)
flags |= TEM_ATTR_REVERSE;
if (inverse_screen)
@@ -755,5 +924,91 @@ tem_get_initial_color(tem_t *tem)
flags |= TEM_ATTR_BOLD;
}
- tem->init_color.a_flags = flags;
+ pcolor->a_flags = flags;
+}
+
+uchar_t
+tem_get_fbmode(tem_vt_state_t tem_arg)
+{
+ struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
+
+ uchar_t fbmode;
+
+ mutex_enter(&tem->tvs_lock);
+ fbmode = tem->tvs_fbmode;
+ mutex_exit(&tem->tvs_lock);
+
+ return (fbmode);
+}
+
+void
+tem_set_fbmode(tem_vt_state_t tem_arg, uchar_t fbmode, cred_t *credp)
+{
+ struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
+
+ mutex_enter(&tems.ts_lock);
+ mutex_enter(&tem->tvs_lock);
+
+ if (fbmode == tem->tvs_fbmode) {
+ mutex_exit(&tem->tvs_lock);
+ mutex_exit(&tems.ts_lock);
+ return;
+ }
+
+ tem->tvs_fbmode = fbmode;
+
+ if (tem->tvs_isactive) {
+ tem_kdsetmode(tem->tvs_fbmode, credp);
+ if (fbmode == KD_TEXT)
+ tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL);
+ }
+
+ mutex_exit(&tem->tvs_lock);
+ mutex_exit(&tems.ts_lock);
+}
+
+void
+tem_activate(tem_vt_state_t tem_arg, boolean_t unblank, cred_t *credp)
+{
+ struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
+
+ mutex_enter(&tems.ts_lock);
+ tems.ts_active = tem;
+
+ mutex_enter(&tem->tvs_lock);
+ tem->tvs_isactive = B_TRUE;
+
+ tem_kdsetmode(tem->tvs_fbmode, credp);
+
+ if (unblank)
+ tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL);
+
+ mutex_exit(&tem->tvs_lock);
+ mutex_exit(&tems.ts_lock);
+}
+
+void
+tem_switch(tem_vt_state_t tem_arg1, tem_vt_state_t tem_arg2, cred_t *credp)
+{
+ struct tem_vt_state *cur = (struct tem_vt_state *)tem_arg1;
+ struct tem_vt_state *tobe = (struct tem_vt_state *)tem_arg2;
+
+ mutex_enter(&tems.ts_lock);
+ mutex_enter(&tobe->tvs_lock);
+ mutex_enter(&cur->tvs_lock);
+
+ tems.ts_active = tobe;
+ cur->tvs_isactive = B_FALSE;
+ tobe->tvs_isactive = B_TRUE;
+
+ mutex_exit(&cur->tvs_lock);
+
+ if (cur->tvs_fbmode != tobe->tvs_fbmode)
+ tem_kdsetmode(tobe->tvs_fbmode, credp);
+
+ if (tobe->tvs_fbmode == KD_TEXT)
+ tem_safe_unblank_screen(tobe, credp, CALLED_FROM_NORMAL);
+
+ mutex_exit(&tobe->tvs_lock);
+ mutex_exit(&tems.ts_lock);
}