diff options
Diffstat (limited to 'usr/src/uts/common/io/tem.c')
-rw-r--r-- | usr/src/uts/common/io/tem.c | 967 |
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); } |