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);
108
109 /*
110 * Globals
111 */
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))
115
116 extern struct mod_ops mod_miscops;
117
118 static struct modlmisc modlmisc = {
119 &mod_miscops, /* modops */
120 "ANSI Terminal Emulator", /* name */
121 };
122
123 static struct modlinkage modlinkage = {
124 MODREV_1, (void *)&modlmisc, NULL
125 };
126
127 int
128 _init(void)
129 {
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);
138 }
139
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;
144
192 mutex_enter(&tems.ts_lock);
193 mutex_enter(&tem->tvs_lock);
194
195 if (!tem->tvs_initialized) {
196 mutex_exit(&tem->tvs_lock);
197 mutex_exit(&tems.ts_lock);
198 return;
199 }
200
201 tem_safe_check_first_time(tem, credp, CALLED_FROM_NORMAL);
202 tem_safe_terminal_emulate(tem, buf, len, credp, CALLED_FROM_NORMAL);
203
204 mutex_exit(&tem->tvs_lock);
205 mutex_exit(&tems.ts_lock);
206 }
207
208 static void
209 tem_internal_init(struct tem_vt_state *ptem, cred_t *credp,
210 boolean_t init_color, boolean_t clear_screen)
211 {
212 int i, j;
213 int width, height;
214 int total;
215 text_color_t fg;
216 text_color_t bg;
217 size_t tc_size = sizeof (text_color_t);
218
219 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&ptem->tvs_lock));
220
221 if (tems.ts_display_mode == VIS_PIXEL) {
222 ptem->tvs_pix_data_size = tems.ts_pix_data_size;
223 ptem->tvs_pix_data =
224 kmem_alloc(ptem->tvs_pix_data_size, KM_SLEEP);
225 }
226
227 ptem->tvs_outbuf_size = tems.ts_c_dimension.width;
228 ptem->tvs_outbuf =
229 (unsigned char *)kmem_alloc(ptem->tvs_outbuf_size, KM_SLEEP);
230
231 width = tems.ts_c_dimension.width;
232 height = tems.ts_c_dimension.height;
233 ptem->tvs_screen_buf_size = width * height;
234 ptem->tvs_screen_buf =
235 (unsigned char *)kmem_alloc(width * height, KM_SLEEP);
236
237 total = width * height * tc_size;
238 ptem->tvs_fg_buf = (text_color_t *)kmem_alloc(total, KM_SLEEP);
239 ptem->tvs_bg_buf = (text_color_t *)kmem_alloc(total, KM_SLEEP);
240 ptem->tvs_color_buf_size = total;
241
242 tem_safe_reset_display(ptem, credp, CALLED_FROM_NORMAL,
243 clear_screen, init_color);
244
245 ptem->tvs_utf8_left = 0;
246 ptem->tvs_utf8_partial = 0;
247
248 tem_safe_get_color(ptem, &fg, &bg, TEM_ATTR_SCREEN_REVERSE);
249 for (i = 0; i < height; i++)
250 for (j = 0; j < width; j++) {
251 ptem->tvs_screen_buf[i * width + j] = ' ';
252 ptem->tvs_fg_buf[(i * width +j) * tc_size] = fg;
253 ptem->tvs_bg_buf[(i * width +j) * tc_size] = bg;
254
255 }
256
257 ptem->tvs_initialized = 1;
258 }
259
260 int
261 tem_initialized(tem_vt_state_t tem_arg)
262 {
263 struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg;
264 int ret;
265
266 mutex_enter(&ptem->tvs_lock);
267 ret = ptem->tvs_initialized;
268 mutex_exit(&ptem->tvs_lock);
269
270 return (ret);
271 }
272
273 tem_vt_state_t
274 tem_init(cred_t *credp)
275 {
276 struct tem_vt_state *ptem;
277
278 ptem = kmem_zalloc(sizeof (struct tem_vt_state), KM_SLEEP);
279 mutex_init(&ptem->tvs_lock, (char *)NULL, MUTEX_DRIVER, NULL);
280
281 mutex_enter(&tems.ts_lock);
282 mutex_enter(&ptem->tvs_lock);
283
284 ptem->tvs_isactive = B_FALSE;
285 ptem->tvs_fbmode = KD_TEXT;
286
287 /*
288 * A tem is regarded as initialized only after tem_internal_init(),
289 * will be set at the end of tem_internal_init().
290 */
291 ptem->tvs_initialized = 0;
292
293
294 if (!tems.ts_initialized) {
295 /*
296 * Only happens during early console configuration.
297 */
298 tem_add(ptem);
299 mutex_exit(&ptem->tvs_lock);
300 mutex_exit(&tems.ts_lock);
301 return ((tem_vt_state_t)ptem);
302 }
303
304 tem_internal_init(ptem, credp, B_TRUE, B_FALSE);
305 tem_add(ptem);
306 mutex_exit(&ptem->tvs_lock);
307 mutex_exit(&tems.ts_lock);
308
309 return ((tem_vt_state_t)ptem);
310 }
311
318 {
319 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock));
320
321 tem_free_buf(tem); /* only free virtual buffers */
322
323 /* reserve color */
324 tem_internal_init(tem, kcred, B_FALSE, reset_display);
325 }
326
327 static void
328 tem_free_buf(struct tem_vt_state *tem)
329 {
330 ASSERT(tem != NULL && MUTEX_HELD(&tem->tvs_lock));
331
332 if (tem->tvs_outbuf != NULL)
333 kmem_free(tem->tvs_outbuf, tem->tvs_outbuf_size);
334 if (tem->tvs_pix_data != NULL)
335 kmem_free(tem->tvs_pix_data, tem->tvs_pix_data_size);
336 if (tem->tvs_screen_buf != NULL)
337 kmem_free(tem->tvs_screen_buf, tem->tvs_screen_buf_size);
338 if (tem->tvs_fg_buf != NULL)
339 kmem_free(tem->tvs_fg_buf, tem->tvs_color_buf_size);
340 if (tem->tvs_bg_buf != NULL)
341 kmem_free(tem->tvs_bg_buf, tem->tvs_color_buf_size);
342 }
343
344 void
345 tem_destroy(tem_vt_state_t tem_arg, cred_t *credp)
346 {
347 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
348
349 mutex_enter(&tems.ts_lock);
350 mutex_enter(&tem->tvs_lock);
351
352 if (tem->tvs_isactive && tem->tvs_fbmode == KD_TEXT)
353 tem_safe_blank_screen(tem, credp, CALLED_FROM_NORMAL);
354
355 tem_free_buf(tem);
356 tem_rm(tem);
357
358 if (tems.ts_active == tem)
359 tems.ts_active = NULL;
360
361 mutex_exit(&tem->tvs_lock);
441 /* Make sure the fb driver and terminal emulator versions match */
442 if (temargs.version != VIS_CONS_REV) {
443 cmn_err(CE_WARN,
444 "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) "
445 "of console fb driver not supported", temargs.version);
446 ret = tems_failed(credp, B_TRUE);
447 mutex_exit(&tems.ts_lock);
448 return (ret);
449 }
450
451 if ((tems.ts_fb_polledio = temargs.polledio) == NULL) {
452 cmn_err(CE_WARN, "terminal emulator: fb doesn't support polled "
453 "I/O");
454 ret = tems_failed(credp, B_TRUE);
455 mutex_exit(&tems.ts_lock);
456 return (ret);
457 }
458
459 /* other sanity checks */
460 if (!((temargs.depth == 4) || (temargs.depth == 8) ||
461 (temargs.depth == 24) || (temargs.depth == 32))) {
462 cmn_err(CE_WARN, "terminal emulator: unsupported depth");
463 ret = tems_failed(credp, B_TRUE);
464 mutex_exit(&tems.ts_lock);
465 return (ret);
466 }
467
468 if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) {
469 cmn_err(CE_WARN, "terminal emulator: unsupported mode");
470 ret = tems_failed(credp, B_TRUE);
471 mutex_exit(&tems.ts_lock);
472 return (ret);
473 }
474
475 if ((temargs.mode == VIS_PIXEL) && plat_stdout_is_framebuffer())
476 plat_tem_get_prom_size(&height, &width);
477
478 /*
479 * Initialize the common terminal emulator info
480 */
508
509 if (tems.ts_pdepth != tp->depth)
510 result |= TEMS_DEPTH_DIFF;
511
512 if (tp->mode == VIS_TEXT) {
513 if (tems.ts_c_dimension.width != tp->width ||
514 tems.ts_c_dimension.height != tp->height)
515 result |= TEMS_DIMENSION_DIFF;
516 } else {
517 if (tems.ts_p_dimension.width != tp->width ||
518 tems.ts_p_dimension.height != tp->height)
519 result |= TEMS_DIMENSION_DIFF;
520 }
521
522 return (result);
523 }
524
525 static void
526 tems_setup_terminal(struct vis_devinit *tp, size_t height, size_t width)
527 {
528 int i;
529 int old_blank_buf_size = tems.ts_c_dimension.width;
530
531 ASSERT(MUTEX_HELD(&tems.ts_lock));
532
533 tems.ts_pdepth = tp->depth;
534 tems.ts_linebytes = tp->linebytes;
535 tems.ts_display_mode = tp->mode;
536
537 switch (tp->mode) {
538 case VIS_TEXT:
539 tems.ts_p_dimension.width = 0;
540 tems.ts_p_dimension.height = 0;
541 tems.ts_c_dimension.width = tp->width;
542 tems.ts_c_dimension.height = tp->height;
543 tems.ts_callbacks = &tem_safe_text_callbacks;
544
545 break;
546
547 case VIS_PIXEL:
548 /*
549 * First check to see if the user has specified a screen size.
550 * If so, use those values. Else use 34x80 as the default.
551 */
552 if (width == 0) {
553 width = TEM_DEFAULT_COLS;
554 height = TEM_DEFAULT_ROWS;
555 }
556 tems.ts_c_dimension.height = (screen_size_t)height;
557 tems.ts_c_dimension.width = (screen_size_t)width;
558
559 tems.ts_p_dimension.height = tp->height;
560 tems.ts_p_dimension.width = tp->width;
561
562 tems.ts_callbacks = &tem_safe_pix_callbacks;
563
564 /*
565 * set_font() will select a appropriate sized font for
566 * the number of rows and columns selected. If we don't
567 * have a font that will fit, then it will use the
568 * default builtin font and adjust the rows and columns
569 * to fit on the screen.
570 */
571 set_font(&tems.ts_font,
572 &tems.ts_c_dimension.height,
573 &tems.ts_c_dimension.width,
574 tems.ts_p_dimension.height,
575 tems.ts_p_dimension.width);
576
577 tems.ts_p_offset.y = (tems.ts_p_dimension.height -
578 (tems.ts_c_dimension.height * tems.ts_font.height)) / 2;
579 tems.ts_p_offset.x = (tems.ts_p_dimension.width -
580 (tems.ts_c_dimension.width * tems.ts_font.width)) / 2;
581
582 tems.ts_pix_data_size =
583 tems.ts_font.width * tems.ts_font.height;
584
585 tems.ts_pix_data_size *= 4;
586
587 tems.ts_pdepth = tp->depth;
588
589 break;
590 }
591
592 /* Now virtual cls also uses the blank_line buffer */
593 if (tems.ts_blank_line)
594 kmem_free(tems.ts_blank_line, old_blank_buf_size);
595
596 tems.ts_blank_line = (unsigned char *)
597 kmem_alloc(tems.ts_c_dimension.width, KM_SLEEP);
598 for (i = 0; i < tems.ts_c_dimension.width; i++)
599 tems.ts_blank_line[i] = ' ';
600 }
601
602 /*
603 * This is a callback function that we register with the frame
604 * buffer driver layered underneath. It gets invoked from
605 * the underlying frame buffer driver to reconfigure the terminal
606 * emulator to a new screen size and depth in conjunction with
607 * framebuffer videomode changes.
608 * Here we keep the foreground/background color and attributes,
609 * which may be different with the initial settings, so that
610 * the color won't change while the framebuffer videomode changes.
611 * And we also reset the kernel terminal emulator and clear the
612 * whole screen.
613 */
614 /* ARGSUSED */
615 void
616 tems_modechange_callback(struct vis_modechg_arg *arg,
617 struct vis_devinit *devinit)
618 {
619 uchar_t diff;
662 mutex_exit(&p->tvs_lock);
663 }
664
665
666 if (tems.ts_modechg_cb == NULL) {
667 mutex_exit(&tems.ts_lock);
668 return;
669 }
670
671 cb = tems.ts_modechg_cb;
672 cb_arg = tems.ts_modechg_arg;
673
674 /*
675 * Release the lock while doing callback.
676 */
677 mutex_exit(&tems.ts_lock);
678 cb(cb_arg);
679 }
680
681 /*
682 * This function is used to display a rectangular blit of data
683 * of a given size and location via the underlying framebuffer driver.
684 * The blit can be as small as a pixel or as large as the screen.
685 */
686 void
687 tems_display_layered(
688 struct vis_consdisplay *pda,
689 cred_t *credp)
690 {
691 int rval;
692
693 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSDISPLAY,
694 (intptr_t)pda, FKIOCTL, credp, &rval);
695 }
696
697 /*
698 * This function is used to invoke a block copy operation in the
699 * underlying framebuffer driver. Rectangle copies are how scrolling
700 * is implemented, as well as horizontal text shifting escape seqs.
701 * such as from vi when deleting characters and words.
702 */
703 void
704 tems_copy_layered(
705 struct vis_conscopy *pma,
706 cred_t *credp)
707 {
708 int rval;
709
710 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCOPY,
711 (intptr_t)pma, FKIOCTL, credp, &rval);
712 }
713
714 /*
715 * This function is used to show or hide a rectangluar monochrom
716 * pixel inverting, text block cursor via the underlying framebuffer.
717 */
718 void
719 tems_cursor_layered(
720 struct vis_conscursor *pca,
721 cred_t *credp)
722 {
723 int rval;
724
725 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCURSOR,
726 (intptr_t)pca, FKIOCTL, credp, &rval);
727 }
728
729 static void
730 tem_kdsetmode(int mode, cred_t *credp)
731 {
732 int rval;
733
734 (void) ldi_ioctl(tems.ts_hdl, KDSETMODE,
735 (intptr_t)mode, FKIOCTL, credp, &rval);
736
737 }
738
739 static void
740 tems_reset_colormap(cred_t *credp, enum called_from called_from)
741 {
742 struct vis_cmap cm;
743 int rval;
744
745 if (called_from == CALLED_FROM_STANDALONE)
746 return;
747
748 switch (tems.ts_pdepth) {
749 case 8:
750 cm.index = 0;
751 cm.count = 16;
752 cm.red = cmap4_to_24.red; /* 8-bits (1/3 of TrueColor 24) */
753 cm.blue = cmap4_to_24.blue; /* 8-bits (1/3 of TrueColor 24) */
754 cm.green = cmap4_to_24.green; /* 8-bits (1/3 of TrueColor 24) */
755 (void) ldi_ioctl(tems.ts_hdl, VIS_PUTCMAP, (intptr_t)&cm,
756 FKIOCTL, credp, &rval);
757 break;
758 }
759 }
760
761 void
762 tem_get_size(ushort_t *r, ushort_t *c, ushort_t *x, ushort_t *y)
763 {
764 mutex_enter(&tems.ts_lock);
765 *r = (ushort_t)tems.ts_c_dimension.height;
766 *c = (ushort_t)tems.ts_c_dimension.width;
767 *x = (ushort_t)tems.ts_p_dimension.width;
768 *y = (ushort_t)tems.ts_p_dimension.height;
769 mutex_exit(&tems.ts_lock);
770 }
771
772 void
773 tem_register_modechg_cb(tem_modechg_cb_t func, tem_modechg_cb_arg_t arg)
774 {
775 mutex_enter(&tems.ts_lock);
776
777 tems.ts_modechg_cb = func;
778 tems.ts_modechg_arg = arg;
779
780 mutex_exit(&tems.ts_lock);
781 }
782
783 /*
784 * This function is to scroll up the OBP output, which has
785 * different screen height and width with our kernel console.
786 */
787 static void
788 tem_prom_scroll_up(struct tem_vt_state *tem, int nrows, cred_t *credp,
789 enum called_from called_from)
790 {
791 struct vis_conscopy ma;
792 int ncols, width;
793
794 /* copy */
795 ma.s_row = nrows * tems.ts_font.height;
796 ma.e_row = tems.ts_p_dimension.height - 1;
797 ma.t_row = 0;
798
799 ma.s_col = 0;
800 ma.e_col = tems.ts_p_dimension.width - 1;
801 ma.t_col = 0;
802
803 tems_safe_copy(&ma, credp, called_from);
804
805 /* clear */
806 width = tems.ts_font.width;
807 ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
808
809 tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y,
810 0, ncols, 0, B_TRUE, credp, called_from);
811 }
812
813 #define PROM_DEFAULT_FONT_HEIGHT 22
814 #define PROM_DEFAULT_WINDOW_TOP 0x8a
815
816 /*
817 * This function is to compute the starting row of the console, according to
818 * PROM cursor's position. Here we have to take different fonts into account.
819 */
820 static int
821 tem_adjust_row(struct tem_vt_state *tem, int prom_row, cred_t *credp,
822 enum called_from called_from)
823 {
824 int tem_row;
825 int tem_y;
826 int prom_charheight = 0;
827 int prom_window_top = 0;
828 int scroll_up_lines;
829
830 plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top);
831 if (prom_charheight == 0)
832 prom_charheight = PROM_DEFAULT_FONT_HEIGHT;
833 if (prom_window_top == 0)
834 prom_window_top = PROM_DEFAULT_WINDOW_TOP;
835
836 tem_y = (prom_row + 1) * prom_charheight + prom_window_top -
837 tems.ts_p_offset.y;
838 tem_row = (tem_y + tems.ts_font.height - 1) /
839 tems.ts_font.height - 1;
840
841 if (tem_row < 0) {
842 tem_row = 0;
843 } else if (tem_row >= (tems.ts_c_dimension.height - 1)) {
844 /*
845 * Scroll up the prom outputs if the PROM cursor's position is
846 * below our tem's lower boundary.
847 */
848 scroll_up_lines = tem_row -
849 (tems.ts_c_dimension.height - 1);
850 tem_prom_scroll_up(tem, scroll_up_lines, credp, called_from);
851 tem_row = tems.ts_c_dimension.height - 1;
852 }
853
854 return (tem_row);
855 }
856
857 void
858 tem_pix_align(struct tem_vt_state *tem, cred_t *credp,
859 enum called_from called_from)
897 int i_inverse_screen = 0;
898
899 plat_tem_get_inverses(&i_inverse, &i_inverse_screen);
900
901 *p_inverse = (i_inverse == 0) ? B_FALSE : B_TRUE;
902 *p_inverse_screen = (i_inverse_screen == 0) ? B_FALSE : B_TRUE;
903 }
904
905 /*
906 * Get the foreground/background color and attributes from the initial
907 * PROM, so that our kernel console can keep the same visual behaviour.
908 */
909 static void
910 tems_get_initial_color(tem_color_t *pcolor)
911 {
912 boolean_t inverse, inverse_screen;
913 unsigned short flags = 0;
914
915 pcolor->fg_color = DEFAULT_ANSI_FOREGROUND;
916 pcolor->bg_color = DEFAULT_ANSI_BACKGROUND;
917
918 if (plat_stdout_is_framebuffer()) {
919 tems_get_inverses(&inverse, &inverse_screen);
920 if (inverse)
921 flags |= TEM_ATTR_REVERSE;
922 if (inverse_screen)
923 flags |= TEM_ATTR_SCREEN_REVERSE;
924
925 if (flags != 0) {
926 /*
927 * If either reverse flag is set, the screen is in
928 * white-on-black mode. We set the bold flag to
929 * improve readability.
930 */
931 flags |= TEM_ATTR_BOLD;
932 } else {
933 /*
934 * Otherwise, the screen is in black-on-white mode.
935 * The SPARC PROM console, which starts in this mode,
936 * uses the bright white background colour so we
937 * match it here.
938 */
939 if (pcolor->bg_color == ANSI_COLOR_WHITE)
940 flags |= TEM_ATTR_BRIGHT_BG;
941 }
942 }
943
944 pcolor->a_flags = flags;
945 }
946
947 uchar_t
948 tem_get_fbmode(tem_vt_state_t tem_arg)
949 {
950 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
951
952 uchar_t fbmode;
953
954 mutex_enter(&tem->tvs_lock);
955 fbmode = tem->tvs_fbmode;
956 mutex_exit(&tem->tvs_lock);
957
958 return (fbmode);
959 }
960
961 void
962 tem_set_fbmode(tem_vt_state_t tem_arg, uchar_t fbmode, cred_t *credp)
|
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);
108
109 /*
110 * Globals
111 */
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))
115
116 extern struct mod_ops mod_miscops;
117
118 static struct modlmisc modlmisc = {
119 &mod_miscops, /* modops */
120 "ANSI Terminal Emulator", /* name */
121 };
122
123 static struct modlinkage modlinkage = {
124 MODREV_1, { (void *)&modlmisc, NULL }
125 };
126
127 int
128 _init(void)
129 {
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);
138 }
139
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;
144
192 mutex_enter(&tems.ts_lock);
193 mutex_enter(&tem->tvs_lock);
194
195 if (!tem->tvs_initialized) {
196 mutex_exit(&tem->tvs_lock);
197 mutex_exit(&tems.ts_lock);
198 return;
199 }
200
201 tem_safe_check_first_time(tem, credp, CALLED_FROM_NORMAL);
202 tem_safe_terminal_emulate(tem, buf, len, credp, CALLED_FROM_NORMAL);
203
204 mutex_exit(&tem->tvs_lock);
205 mutex_exit(&tems.ts_lock);
206 }
207
208 static void
209 tem_internal_init(struct tem_vt_state *ptem, cred_t *credp,
210 boolean_t init_color, boolean_t clear_screen)
211 {
212 unsigned i, j, width, height;
213 text_attr_t attr;
214 text_color_t fg;
215 text_color_t bg;
216
217 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&ptem->tvs_lock));
218
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);
223 }
224
225 ptem->tvs_outbuf_size = tems.ts_c_dimension.width *
226 sizeof (*ptem->tvs_outbuf);
227 ptem->tvs_outbuf = kmem_alloc(ptem->tvs_outbuf_size, KM_SLEEP);
228
229 width = tems.ts_c_dimension.width;
230 height = tems.ts_c_dimension.height;
231 ptem->tvs_screen_history_size = height;
232
233 ptem->tvs_screen_buf_size = width * ptem->tvs_screen_history_size *
234 sizeof (*ptem->tvs_screen_buf);
235 ptem->tvs_screen_buf = kmem_alloc(ptem->tvs_screen_buf_size, KM_SLEEP);
236 ptem->tvs_screen_rows = kmem_alloc(ptem->tvs_screen_history_size *
237 sizeof (term_char_t *), KM_SLEEP);
238
239 tem_safe_reset_display(ptem, credp, CALLED_FROM_NORMAL,
240 clear_screen, init_color);
241
242 ptem->tvs_utf8_left = 0;
243 ptem->tvs_utf8_partial = 0;
244
245 /* Get default attributes and fill up the screen buffer. */
246 tem_safe_get_attr(ptem, &fg, &bg, &attr, TEM_ATTR_SCREEN_REVERSE);
247 for (i = 0; i < ptem->tvs_screen_history_size; i++) {
248 ptem->tvs_screen_rows[i] = &ptem->tvs_screen_buf[i * width];
249
250 for (j = 0; j < width; j++) {
251 ptem->tvs_screen_rows[i][j].tc_fg_color = fg;
252 ptem->tvs_screen_rows[i][j].tc_bg_color = bg;
253 ptem->tvs_screen_rows[i][j].tc_char =
254 TEM_ATTR(attr) | ' ';
255
256 }
257 }
258
259 ptem->tvs_initialized = B_TRUE;
260 }
261
262 int
263 tem_initialized(tem_vt_state_t tem_arg)
264 {
265 struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg;
266 int ret;
267
268 mutex_enter(&ptem->tvs_lock);
269 ret = ptem->tvs_initialized == B_TRUE? 1 : 0;
270 mutex_exit(&ptem->tvs_lock);
271
272 return (ret);
273 }
274
275 tem_vt_state_t
276 tem_init(cred_t *credp)
277 {
278 struct tem_vt_state *ptem;
279
280 ptem = kmem_zalloc(sizeof (struct tem_vt_state), KM_SLEEP);
281 mutex_init(&ptem->tvs_lock, (char *)NULL, MUTEX_DRIVER, NULL);
282
283 mutex_enter(&tems.ts_lock);
284 mutex_enter(&ptem->tvs_lock);
285
286 ptem->tvs_isactive = B_FALSE;
287 ptem->tvs_fbmode = KD_TEXT;
288
289 /*
290 * A tem is regarded as initialized only after tem_internal_init(),
291 * will be set at the end of tem_internal_init().
292 */
293 ptem->tvs_initialized = B_FALSE;
294
295
296 if (!tems.ts_initialized) {
297 /*
298 * Only happens during early console configuration.
299 */
300 tem_add(ptem);
301 mutex_exit(&ptem->tvs_lock);
302 mutex_exit(&tems.ts_lock);
303 return ((tem_vt_state_t)ptem);
304 }
305
306 tem_internal_init(ptem, credp, B_TRUE, B_FALSE);
307 tem_add(ptem);
308 mutex_exit(&ptem->tvs_lock);
309 mutex_exit(&tems.ts_lock);
310
311 return ((tem_vt_state_t)ptem);
312 }
313
320 {
321 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock));
322
323 tem_free_buf(tem); /* only free virtual buffers */
324
325 /* reserve color */
326 tem_internal_init(tem, kcred, B_FALSE, reset_display);
327 }
328
329 static void
330 tem_free_buf(struct tem_vt_state *tem)
331 {
332 ASSERT(tem != NULL && MUTEX_HELD(&tem->tvs_lock));
333
334 if (tem->tvs_outbuf != NULL)
335 kmem_free(tem->tvs_outbuf, tem->tvs_outbuf_size);
336 if (tem->tvs_pix_data != NULL)
337 kmem_free(tem->tvs_pix_data, tem->tvs_pix_data_size);
338 if (tem->tvs_screen_buf != NULL)
339 kmem_free(tem->tvs_screen_buf, tem->tvs_screen_buf_size);
340 if (tem->tvs_screen_rows != NULL) {
341 kmem_free(tem->tvs_screen_rows, tem->tvs_screen_history_size *
342 sizeof (term_char_t *));
343 }
344 }
345
346 void
347 tem_destroy(tem_vt_state_t tem_arg, cred_t *credp)
348 {
349 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
350
351 mutex_enter(&tems.ts_lock);
352 mutex_enter(&tem->tvs_lock);
353
354 if (tem->tvs_isactive && tem->tvs_fbmode == KD_TEXT)
355 tem_safe_blank_screen(tem, credp, CALLED_FROM_NORMAL);
356
357 tem_free_buf(tem);
358 tem_rm(tem);
359
360 if (tems.ts_active == tem)
361 tems.ts_active = NULL;
362
363 mutex_exit(&tem->tvs_lock);
443 /* Make sure the fb driver and terminal emulator versions match */
444 if (temargs.version != VIS_CONS_REV) {
445 cmn_err(CE_WARN,
446 "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) "
447 "of console fb driver not supported", temargs.version);
448 ret = tems_failed(credp, B_TRUE);
449 mutex_exit(&tems.ts_lock);
450 return (ret);
451 }
452
453 if ((tems.ts_fb_polledio = temargs.polledio) == NULL) {
454 cmn_err(CE_WARN, "terminal emulator: fb doesn't support polled "
455 "I/O");
456 ret = tems_failed(credp, B_TRUE);
457 mutex_exit(&tems.ts_lock);
458 return (ret);
459 }
460
461 /* other sanity checks */
462 if (!((temargs.depth == 4) || (temargs.depth == 8) ||
463 (temargs.depth == 15) || (temargs.depth == 16) ||
464 (temargs.depth == 24) || (temargs.depth == 32))) {
465 cmn_err(CE_WARN, "terminal emulator: unsupported depth");
466 ret = tems_failed(credp, B_TRUE);
467 mutex_exit(&tems.ts_lock);
468 return (ret);
469 }
470
471 if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) {
472 cmn_err(CE_WARN, "terminal emulator: unsupported mode");
473 ret = tems_failed(credp, B_TRUE);
474 mutex_exit(&tems.ts_lock);
475 return (ret);
476 }
477
478 if ((temargs.mode == VIS_PIXEL) && plat_stdout_is_framebuffer())
479 plat_tem_get_prom_size(&height, &width);
480
481 /*
482 * Initialize the common terminal emulator info
483 */
511
512 if (tems.ts_pdepth != tp->depth)
513 result |= TEMS_DEPTH_DIFF;
514
515 if (tp->mode == VIS_TEXT) {
516 if (tems.ts_c_dimension.width != tp->width ||
517 tems.ts_c_dimension.height != tp->height)
518 result |= TEMS_DIMENSION_DIFF;
519 } else {
520 if (tems.ts_p_dimension.width != tp->width ||
521 tems.ts_p_dimension.height != tp->height)
522 result |= TEMS_DIMENSION_DIFF;
523 }
524
525 return (result);
526 }
527
528 static void
529 tems_setup_terminal(struct vis_devinit *tp, size_t height, size_t width)
530 {
531 bitmap_data_t *font_data;
532 int i;
533 int old_blank_buf_size = tems.ts_c_dimension.width *
534 sizeof (*tems.ts_blank_line);
535
536 ASSERT(MUTEX_HELD(&tems.ts_lock));
537
538 tems.ts_pdepth = tp->depth;
539 tems.ts_linebytes = tp->linebytes;
540 tems.ts_display_mode = tp->mode;
541 tems.ts_color_map = tp->color_map;
542
543 switch (tp->mode) {
544 case VIS_TEXT:
545 tems.ts_p_dimension.width = 0;
546 tems.ts_p_dimension.height = 0;
547 tems.ts_c_dimension.width = tp->width;
548 tems.ts_c_dimension.height = tp->height;
549 tems.ts_callbacks = &tem_safe_text_callbacks;
550
551 break;
552
553 case VIS_PIXEL:
554 /*
555 * First check to see if the user has specified a screen size.
556 * If so, use those values. Else use 34x80 as the default.
557 */
558 if (width == 0) {
559 width = TEM_DEFAULT_COLS;
560 height = TEM_DEFAULT_ROWS;
561 }
562 tems.ts_c_dimension.height = (screen_size_t)height;
563 tems.ts_c_dimension.width = (screen_size_t)width;
564
565 tems.ts_p_dimension.height = tp->height;
566 tems.ts_p_dimension.width = tp->width;
567
568 tems.ts_callbacks = &tem_safe_pix_callbacks;
569
570 /*
571 * set_font() will select a appropriate sized font for
572 * the number of rows and columns selected. If we don't
573 * have a font that will fit, then it will use the
574 * default builtin font and adjust the rows and columns
575 * to fit on the screen.
576 */
577 font_data = set_font(&tems.ts_c_dimension.height,
578 &tems.ts_c_dimension.width,
579 tems.ts_p_dimension.height,
580 tems.ts_p_dimension.width);
581
582 for (i = 0; i < VFNT_MAPS; i++) {
583 tems.ts_font.vf_map[i] =
584 font_data->font->vf_map[i];
585 tems.ts_font.vf_map_count[i] =
586 font_data->font->vf_map_count[i];
587 }
588 tems.ts_font.vf_bytes = font_data->font->vf_bytes;
589 tems.ts_font.vf_width = font_data->font->vf_width;
590 tems.ts_font.vf_height = font_data->font->vf_height;
591
592 tems.ts_p_offset.y = (tems.ts_p_dimension.height -
593 (tems.ts_c_dimension.height * tems.ts_font.vf_height)) / 2;
594 tems.ts_p_offset.x = (tems.ts_p_dimension.width -
595 (tems.ts_c_dimension.width * tems.ts_font.vf_width)) / 2;
596
597 tems.ts_pix_data_size =
598 tems.ts_font.vf_width * tems.ts_font.vf_height;
599
600 tems.ts_pix_data_size *= 4;
601
602 tems.ts_pdepth = tp->depth;
603
604 break;
605 }
606
607 /* Now virtual cls also uses the blank_line buffer */
608 if (tems.ts_blank_line)
609 kmem_free(tems.ts_blank_line, old_blank_buf_size);
610
611 tems.ts_blank_line = kmem_alloc(tems.ts_c_dimension.width *
612 sizeof (*tems.ts_blank_line), KM_SLEEP);
613 }
614
615 /*
616 * This is a callback function that we register with the frame
617 * buffer driver layered underneath. It gets invoked from
618 * the underlying frame buffer driver to reconfigure the terminal
619 * emulator to a new screen size and depth in conjunction with
620 * framebuffer videomode changes.
621 * Here we keep the foreground/background color and attributes,
622 * which may be different with the initial settings, so that
623 * the color won't change while the framebuffer videomode changes.
624 * And we also reset the kernel terminal emulator and clear the
625 * whole screen.
626 */
627 /* ARGSUSED */
628 void
629 tems_modechange_callback(struct vis_modechg_arg *arg,
630 struct vis_devinit *devinit)
631 {
632 uchar_t diff;
675 mutex_exit(&p->tvs_lock);
676 }
677
678
679 if (tems.ts_modechg_cb == NULL) {
680 mutex_exit(&tems.ts_lock);
681 return;
682 }
683
684 cb = tems.ts_modechg_cb;
685 cb_arg = tems.ts_modechg_arg;
686
687 /*
688 * Release the lock while doing callback.
689 */
690 mutex_exit(&tems.ts_lock);
691 cb(cb_arg);
692 }
693
694 /*
695 * This function is used to clear entire screen via the underlying framebuffer
696 * driver.
697 */
698 int
699 tems_cls_layered(struct vis_consclear *pda,
700 cred_t *credp)
701 {
702 int rval;
703
704 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCLEAR,
705 (intptr_t)pda, FKIOCTL, credp, &rval);
706 return (rval);
707 }
708
709 /*
710 * This function is used to display a rectangular blit of data
711 * of a given size and location via the underlying framebuffer driver.
712 * The blit can be as small as a pixel or as large as the screen.
713 */
714 void
715 tems_display_layered(struct vis_consdisplay *pda,
716 cred_t *credp)
717 {
718 int rval;
719
720 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSDISPLAY,
721 (intptr_t)pda, FKIOCTL, credp, &rval);
722 }
723
724 /*
725 * This function is used to invoke a block copy operation in the
726 * underlying framebuffer driver. Rectangle copies are how scrolling
727 * is implemented, as well as horizontal text shifting escape seqs.
728 * such as from vi when deleting characters and words.
729 */
730 void
731 tems_copy_layered(struct vis_conscopy *pma,
732 cred_t *credp)
733 {
734 int rval;
735
736 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCOPY,
737 (intptr_t)pma, FKIOCTL, credp, &rval);
738 }
739
740 /*
741 * This function is used to show or hide a rectangluar monochrom
742 * pixel inverting, text block cursor via the underlying framebuffer.
743 */
744 void
745 tems_cursor_layered(struct vis_conscursor *pca,
746 cred_t *credp)
747 {
748 int rval;
749
750 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCURSOR,
751 (intptr_t)pca, FKIOCTL, credp, &rval);
752 }
753
754 static void
755 tem_kdsetmode(int mode, cred_t *credp)
756 {
757 int rval;
758
759 (void) ldi_ioctl(tems.ts_hdl, KDSETMODE,
760 (intptr_t)mode, FKIOCTL, credp, &rval);
761
762 }
763
764 static void
765 tems_reset_colormap(cred_t *credp, enum called_from called_from)
766 {
767 struct vis_cmap cm;
768 int rval;
769
770 if (called_from == CALLED_FROM_STANDALONE)
771 return;
772
773 switch (tems.ts_pdepth) {
774 case 8:
775 cm.index = 0;
776 cm.count = 16;
777 /* 8-bits (1/3 of TrueColor 24) */
778 cm.red = (uint8_t *)cmap4_to_24.red;
779 /* 8-bits (1/3 of TrueColor 24) */
780 cm.blue = (uint8_t *)cmap4_to_24.blue;
781 /* 8-bits (1/3 of TrueColor 24) */
782 cm.green = (uint8_t *)cmap4_to_24.green;
783 (void) ldi_ioctl(tems.ts_hdl, VIS_PUTCMAP, (intptr_t)&cm,
784 FKIOCTL, credp, &rval);
785 break;
786 }
787 }
788
789 void
790 tem_get_size(ushort_t *r, ushort_t *c, ushort_t *x, ushort_t *y)
791 {
792 mutex_enter(&tems.ts_lock);
793 *r = (ushort_t)tems.ts_c_dimension.height;
794 *c = (ushort_t)tems.ts_c_dimension.width;
795 *x = (ushort_t)tems.ts_p_dimension.width;
796 *y = (ushort_t)tems.ts_p_dimension.height;
797 mutex_exit(&tems.ts_lock);
798 }
799
800 void
801 tem_register_modechg_cb(tem_modechg_cb_t func, tem_modechg_cb_arg_t arg)
802 {
803 mutex_enter(&tems.ts_lock);
804
805 tems.ts_modechg_cb = func;
806 tems.ts_modechg_arg = arg;
807
808 mutex_exit(&tems.ts_lock);
809 }
810
811 /*
812 * This function is to scroll up the OBP output, which has
813 * different screen height and width with our kernel console.
814 */
815 static void
816 tem_prom_scroll_up(struct tem_vt_state *tem, int nrows, cred_t *credp,
817 enum called_from called_from)
818 {
819 struct vis_conscopy ma;
820 int ncols, width;
821
822 /* copy */
823 ma.s_row = nrows * tems.ts_font.vf_height;
824 ma.e_row = tems.ts_p_dimension.height - 1;
825 ma.t_row = 0;
826
827 ma.s_col = 0;
828 ma.e_col = tems.ts_p_dimension.width - 1;
829 ma.t_col = 0;
830
831 tems_safe_copy(&ma, credp, called_from);
832
833 /* clear */
834 width = tems.ts_font.vf_width;
835 ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
836
837 tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y,
838 0, ncols, 0, B_TRUE, credp, called_from);
839 }
840
841 #define PROM_DEFAULT_FONT_HEIGHT 22
842 #define PROM_DEFAULT_WINDOW_TOP 0x8a
843
844 /*
845 * This function is to compute the starting row of the console, according to
846 * PROM cursor's position. Here we have to take different fonts into account.
847 */
848 static int
849 tem_adjust_row(struct tem_vt_state *tem, int prom_row, cred_t *credp,
850 enum called_from called_from)
851 {
852 int tem_row;
853 int tem_y;
854 int prom_charheight = 0;
855 int prom_window_top = 0;
856 int scroll_up_lines;
857
858 plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top);
859 if (prom_charheight == 0)
860 prom_charheight = PROM_DEFAULT_FONT_HEIGHT;
861 if (prom_window_top == 0)
862 prom_window_top = PROM_DEFAULT_WINDOW_TOP;
863
864 tem_y = (prom_row + 1) * prom_charheight + prom_window_top -
865 tems.ts_p_offset.y;
866 tem_row = (tem_y + tems.ts_font.vf_height - 1) /
867 tems.ts_font.vf_height - 1;
868
869 if (tem_row < 0) {
870 tem_row = 0;
871 } else if (tem_row >= (tems.ts_c_dimension.height - 1)) {
872 /*
873 * Scroll up the prom outputs if the PROM cursor's position is
874 * below our tem's lower boundary.
875 */
876 scroll_up_lines = tem_row -
877 (tems.ts_c_dimension.height - 1);
878 tem_prom_scroll_up(tem, scroll_up_lines, credp, called_from);
879 tem_row = tems.ts_c_dimension.height - 1;
880 }
881
882 return (tem_row);
883 }
884
885 void
886 tem_pix_align(struct tem_vt_state *tem, cred_t *credp,
887 enum called_from called_from)
925 int i_inverse_screen = 0;
926
927 plat_tem_get_inverses(&i_inverse, &i_inverse_screen);
928
929 *p_inverse = (i_inverse == 0) ? B_FALSE : B_TRUE;
930 *p_inverse_screen = (i_inverse_screen == 0) ? B_FALSE : B_TRUE;
931 }
932
933 /*
934 * Get the foreground/background color and attributes from the initial
935 * PROM, so that our kernel console can keep the same visual behaviour.
936 */
937 static void
938 tems_get_initial_color(tem_color_t *pcolor)
939 {
940 boolean_t inverse, inverse_screen;
941 unsigned short flags = 0;
942
943 pcolor->fg_color = DEFAULT_ANSI_FOREGROUND;
944 pcolor->bg_color = DEFAULT_ANSI_BACKGROUND;
945 #ifndef _HAVE_TEM_FIRMWARE
946 plat_tem_get_colors(&pcolor->fg_color, &pcolor->bg_color);
947 #endif
948
949 tems_get_inverses(&inverse, &inverse_screen);
950 if (inverse)
951 flags |= TEM_ATTR_REVERSE;
952 if (inverse_screen)
953 flags |= TEM_ATTR_SCREEN_REVERSE;
954
955 if (flags != 0) {
956 /*
957 * If either reverse flag is set, the screen is in
958 * white-on-black mode. We set the bold flag to
959 * improve readability.
960 */
961 flags |= TEM_ATTR_BOLD;
962 } else {
963 /*
964 * Otherwise, the screen is in black-on-white mode.
965 * The SPARC PROM console, which starts in this mode,
966 * uses the bright white background colour so we
967 * match it here.
968 */
969 if (pcolor->bg_color == ANSI_COLOR_WHITE)
970 flags |= TEM_ATTR_BRIGHT_BG;
971 }
972
973 pcolor->a_flags = flags;
974 }
975
976 uchar_t
977 tem_get_fbmode(tem_vt_state_t tem_arg)
978 {
979 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
980
981 uchar_t fbmode;
982
983 mutex_enter(&tem->tvs_lock);
984 fbmode = tem->tvs_fbmode;
985 mutex_exit(&tem->tvs_lock);
986
987 return (fbmode);
988 }
989
990 void
991 tem_set_fbmode(tem_vt_state_t tem_arg, uchar_t fbmode, cred_t *credp)
|