1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * ANSI terminal emulator module; parse ANSI X3.64 escape sequences and
29 * the like.
30 *
31 * How Virtual Terminal Emulator Works:
32 *
33 * Every virtual terminal is associated with a tem_vt_state structure
34 * and maintains a virtual screen buffer in tvs_screen_buf, which contains
35 * all the characters which should be shown on the physical screen when
36 * the terminal is activated. There are also two other buffers, tvs_fg_buf
37 * and tvs_bg_buf, which track the foreground and background colors of the
38 * on screen characters
39 *
40 * Data written to a virtual terminal is composed of characters which
41 * should be displayed on the screen when this virtual terminal is
42 * activated, fg/bg colors of these characters, and other control
43 * information (escape sequence, etc).
44 *
45 * When data is passed to a virtual terminal it first is parsed for
46 * control information by tem_safe_parse(). Subsequently the character
47 * and color data are written to tvs_screen_buf, tvs_fg_buf, and
48 * tvs_bg_buf. They are saved in these buffers in order to refresh
49 * the screen when this terminal is activated. If the terminal is
50 * currently active, the data (characters and colors) are also written
51 * to the physical screen by invoking a callback function,
52 * tem_safe_text_callbacks() or tem_safe_pix_callbacks().
53 *
54 * When rendering data to the framebuffer, if the framebuffer is in
55 * VIS_PIXEL mode, the character data will first be converted to pixel
56 * data using tem_safe_pix_bit2pix(), and then the pixels get displayed
57 * on the physical screen. We only store the character and color data in
58 * tem_vt_state since the bit2pix conversion only happens when actually
59 * rendering to the physical framebuffer.
60 */
61
62
63 #include <sys/types.h>
64 #include <sys/file.h>
65 #include <sys/conf.h>
66 #include <sys/errno.h>
67 #include <sys/open.h>
68 #include <sys/cred.h>
69 #include <sys/kmem.h>
70 #include <sys/ascii.h>
71 #include <sys/consdev.h>
72 #include <sys/font.h>
73 #include <sys/fbio.h>
74 #include <sys/conf.h>
75 #include <sys/modctl.h>
76 #include <sys/strsubr.h>
77 #include <sys/stat.h>
78 #include <sys/visual_io.h>
79 #include <sys/mutex.h>
80 #include <sys/param.h>
81 #include <sys/debug.h>
82 #include <sys/cmn_err.h>
83 #include <sys/console.h>
84 #include <sys/ddi.h>
85 #include <sys/sunddi.h>
86 #include <sys/sunldi.h>
87 #include <sys/tem_impl.h>
88 #ifdef _HAVE_TEM_FIRMWARE
89 #include <sys/promif.h>
90 #endif /* _HAVE_TEM_FIRMWARE */
91 #include <sys/consplat.h>
92 #include <sys/kd.h>
93 #include <sys/sysmacros.h>
94 #include <sys/note.h>
95 #include <sys/t_lock.h>
96
97 /* Terminal emulator internal helper functions */
98 static void tems_setup_terminal(struct vis_devinit *, size_t, size_t);
99 static void tems_modechange_callback(struct vis_modechg_arg *,
100 struct vis_devinit *);
101
102 static void tems_reset_colormap(cred_t *, enum called_from);
103
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
115 extern struct mod_ops mod_miscops;
116
117 static struct modlmisc modlmisc = {
118 &mod_miscops, /* modops */
119 "ANSI Terminal Emulator", /* name */
120 };
121
122 static struct modlinkage modlinkage = {
123 MODREV_1, (void *)&modlmisc, NULL
124 };
125
126 int
127 _init(void)
128 {
129 int ret;
130 ret = mod_install(&modlinkage);
131 if (ret != 0)
132 return (ret);
133 ret = ldi_ident_from_mod(&modlinkage, &term_li);
134 if (ret != 0) {
135 (void) mod_remove(&modlinkage);
136 return (ret);
137 }
138
139 mutex_init(&tems.ts_lock, (char *)NULL, MUTEX_DRIVER, NULL);
140 list_create(&tems.ts_list, sizeof (struct tem_vt_state),
141 offsetof(struct tem_vt_state, tvs_list_node));
142 tems.ts_active = NULL;
143
144 return (0);
145 }
146
147 int
148 _fini()
149 {
150 int ret;
151
152 ret = mod_remove(&modlinkage);
153 if (ret == 0) {
154 ldi_ident_release(term_li);
155 term_li = NULL;
156 }
157 return (ret);
158 }
159
160 int
161 _info(struct modinfo *modinfop)
162 {
163 return (mod_info(&modlinkage, modinfop));
164 }
165
166 static void
167 tem_add(struct tem_vt_state *tem)
168 {
169 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock));
170
171 list_insert_head(&tems.ts_list, tem);
172 }
173
174 static void
175 tem_rm(struct tem_vt_state *tem)
176 {
177 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock));
178
179 list_remove(&tems.ts_list, tem);
180 }
181
182 /*
183 * This is the main entry point to the module. It handles output requests
184 * during normal system operation, when (e.g.) mutexes are available.
185 */
186 void
187 tem_write(tem_vt_state_t tem_arg, uchar_t *buf, ssize_t len, cred_t *credp)
188 {
189 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
190
191 mutex_enter(&tems.ts_lock);
192 mutex_enter(&tem->tvs_lock);
193
194 if (!tem->tvs_initialized) {
195 mutex_exit(&tem->tvs_lock);
196 mutex_exit(&tems.ts_lock);
197 return;
198 }
199
200 tem_safe_check_first_time(tem, credp, CALLED_FROM_NORMAL);
201 tem_safe_terminal_emulate(tem, buf, len, credp, CALLED_FROM_NORMAL);
202
203 mutex_exit(&tem->tvs_lock);
204 mutex_exit(&tems.ts_lock);
205 }
206
207 static void
208 tem_internal_init(struct tem_vt_state *ptem, cred_t *credp,
209 boolean_t init_color, boolean_t clear_screen)
210 {
211 int i, j;
212 int width, height;
213 int total;
214 text_color_t fg;
215 text_color_t bg;
216 size_t tc_size = sizeof (text_color_t);
217
218 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&ptem->tvs_lock));
219
220 if (tems.ts_display_mode == VIS_PIXEL) {
221 ptem->tvs_pix_data_size = tems.ts_pix_data_size;
222 ptem->tvs_pix_data =
223 kmem_alloc(ptem->tvs_pix_data_size, KM_SLEEP);
224 }
225
226 ptem->tvs_outbuf_size = tems.ts_c_dimension.width;
227 ptem->tvs_outbuf =
228 (unsigned char *)kmem_alloc(ptem->tvs_outbuf_size, KM_SLEEP);
229
230 width = tems.ts_c_dimension.width;
231 height = tems.ts_c_dimension.height;
232 ptem->tvs_screen_buf_size = width * height;
233 ptem->tvs_screen_buf =
234 (unsigned char *)kmem_alloc(width * height, KM_SLEEP);
235
236 total = width * height * tc_size;
237 ptem->tvs_fg_buf = (text_color_t *)kmem_alloc(total, KM_SLEEP);
238 ptem->tvs_bg_buf = (text_color_t *)kmem_alloc(total, KM_SLEEP);
239 ptem->tvs_color_buf_size = total;
240
241 tem_safe_reset_display(ptem, credp, CALLED_FROM_NORMAL,
242 clear_screen, init_color);
243
244 ptem->tvs_utf8_left = 0;
245 ptem->tvs_utf8_partial = 0;
246
247 tem_safe_get_color(ptem, &fg, &bg, TEM_ATTR_SCREEN_REVERSE);
248 for (i = 0; i < height; i++)
249 for (j = 0; j < width; j++) {
250 ptem->tvs_screen_buf[i * width + j] = ' ';
251 ptem->tvs_fg_buf[(i * width +j) * tc_size] = fg;
252 ptem->tvs_bg_buf[(i * width +j) * tc_size] = bg;
253
254 }
255
256 ptem->tvs_initialized = 1;
257 }
258
259 int
260 tem_initialized(tem_vt_state_t tem_arg)
261 {
262 struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg;
263 int ret;
264
265 mutex_enter(&ptem->tvs_lock);
266 ret = ptem->tvs_initialized;
267 mutex_exit(&ptem->tvs_lock);
268
269 return (ret);
270 }
271
272 tem_vt_state_t
273 tem_init(cred_t *credp)
274 {
275 struct tem_vt_state *ptem;
276
277 ptem = kmem_zalloc(sizeof (struct tem_vt_state), KM_SLEEP);
278 mutex_init(&ptem->tvs_lock, (char *)NULL, MUTEX_DRIVER, NULL);
279
280 mutex_enter(&tems.ts_lock);
281 mutex_enter(&ptem->tvs_lock);
282
283 ptem->tvs_isactive = B_FALSE;
284 ptem->tvs_fbmode = KD_TEXT;
285
286 /*
287 * A tem is regarded as initialized only after tem_internal_init(),
288 * will be set at the end of tem_internal_init().
289 */
290 ptem->tvs_initialized = 0;
291
292
293 if (!tems.ts_initialized) {
294 /*
295 * Only happens during early console configuration.
296 */
297 tem_add(ptem);
298 mutex_exit(&ptem->tvs_lock);
299 mutex_exit(&tems.ts_lock);
300 return ((tem_vt_state_t)ptem);
301 }
302
303 tem_internal_init(ptem, credp, B_TRUE, B_FALSE);
304 tem_add(ptem);
305 mutex_exit(&ptem->tvs_lock);
306 mutex_exit(&tems.ts_lock);
307
308 return ((tem_vt_state_t)ptem);
309 }
310
311 /*
312 * re-init the tem after video mode has changed and tems_info has
313 * been re-inited. The lock is already held.
314 */
315 static void
316 tem_reinit(struct tem_vt_state *tem, boolean_t reset_display)
317 {
318 ASSERT(MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock));
319
320 tem_free_buf(tem); /* only free virtual buffers */
321
322 /* reserve color */
323 tem_internal_init(tem, kcred, B_FALSE, reset_display);
324 }
325
326 static void
327 tem_free_buf(struct tem_vt_state *tem)
328 {
329 ASSERT(tem != NULL && MUTEX_HELD(&tem->tvs_lock));
330
331 if (tem->tvs_outbuf != NULL)
332 kmem_free(tem->tvs_outbuf, tem->tvs_outbuf_size);
333 if (tem->tvs_pix_data != NULL)
334 kmem_free(tem->tvs_pix_data, tem->tvs_pix_data_size);
335 if (tem->tvs_screen_buf != NULL)
336 kmem_free(tem->tvs_screen_buf, tem->tvs_screen_buf_size);
337 if (tem->tvs_fg_buf != NULL)
338 kmem_free(tem->tvs_fg_buf, tem->tvs_color_buf_size);
339 if (tem->tvs_bg_buf != NULL)
340 kmem_free(tem->tvs_bg_buf, tem->tvs_color_buf_size);
341 }
342
343 void
344 tem_destroy(tem_vt_state_t tem_arg, cred_t *credp)
345 {
346 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
347
348 mutex_enter(&tems.ts_lock);
349 mutex_enter(&tem->tvs_lock);
350
351 if (tem->tvs_isactive && tem->tvs_fbmode == KD_TEXT)
352 tem_safe_blank_screen(tem, credp, CALLED_FROM_NORMAL);
353
354 tem_free_buf(tem);
355 tem_rm(tem);
356
357 if (tems.ts_active == tem)
358 tems.ts_active = NULL;
359
360 mutex_exit(&tem->tvs_lock);
361 mutex_exit(&tems.ts_lock);
362
363 kmem_free(tem, sizeof (struct tem_vt_state));
364 }
365
366 static int
367 tems_failed(cred_t *credp, boolean_t finish_ioctl)
368 {
369 int lyr_rval;
370
371 ASSERT(MUTEX_HELD(&tems.ts_lock));
372
373 if (finish_ioctl)
374 (void) ldi_ioctl(tems.ts_hdl, VIS_DEVFINI, 0,
375 FWRITE|FKIOCTL, credp, &lyr_rval);
376
377 (void) ldi_close(tems.ts_hdl, NULL, credp);
378 tems.ts_hdl = NULL;
379 return (ENXIO);
380 }
381
382 /*
383 * only called once during boot
384 */
385 int
386 tem_info_init(char *pathname, cred_t *credp)
387 {
388 int lyr_rval, ret;
389 struct vis_devinit temargs;
390 char *pathbuf;
391 size_t height = 0;
392 size_t width = 0;
393 struct tem_vt_state *p;
394
395 mutex_enter(&tems.ts_lock);
396
397 if (tems.ts_initialized) {
398 mutex_exit(&tems.ts_lock);
399 return (0);
400 }
401
402 /*
403 * Open the layered device using the devfs physical device name
404 * after adding the /devices prefix.
405 */
406 pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
407 (void) strcpy(pathbuf, "/devices");
408 if (i_ddi_prompath_to_devfspath(pathname,
409 pathbuf + strlen("/devices")) != DDI_SUCCESS) {
410 cmn_err(CE_WARN, "terminal-emulator: path conversion error");
411 kmem_free(pathbuf, MAXPATHLEN);
412
413 mutex_exit(&tems.ts_lock);
414 return (ENXIO);
415 }
416 if (ldi_open_by_name(pathbuf, FWRITE, credp,
417 &tems.ts_hdl, term_li) != 0) {
418 cmn_err(CE_WARN, "terminal-emulator: device path open error");
419 kmem_free(pathbuf, MAXPATHLEN);
420
421 mutex_exit(&tems.ts_lock);
422 return (ENXIO);
423 }
424 kmem_free(pathbuf, MAXPATHLEN);
425
426 temargs.modechg_cb = (vis_modechg_cb_t)tems_modechange_callback;
427 temargs.modechg_arg = NULL;
428
429 /*
430 * Initialize the console and get the device parameters
431 */
432 if (ldi_ioctl(tems.ts_hdl, VIS_DEVINIT,
433 (intptr_t)&temargs, FWRITE|FKIOCTL, credp, &lyr_rval) != 0) {
434 cmn_err(CE_WARN, "terminal emulator: Compatible fb not found");
435 ret = tems_failed(credp, B_FALSE);
436 mutex_exit(&tems.ts_lock);
437 return (ret);
438 }
439
440 /* Make sure the fb driver and terminal emulator versions match */
441 if (temargs.version != VIS_CONS_REV) {
442 cmn_err(CE_WARN,
443 "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) "
444 "of console fb driver not supported", temargs.version);
445 ret = tems_failed(credp, B_TRUE);
446 mutex_exit(&tems.ts_lock);
447 return (ret);
448 }
449
450 if ((tems.ts_fb_polledio = temargs.polledio) == NULL) {
451 cmn_err(CE_WARN, "terminal emulator: fb doesn't support polled "
452 "I/O");
453 ret = tems_failed(credp, B_TRUE);
454 mutex_exit(&tems.ts_lock);
455 return (ret);
456 }
457
458 /* other sanity checks */
459 if (!((temargs.depth == 4) || (temargs.depth == 8) ||
460 (temargs.depth == 24) || (temargs.depth == 32))) {
461 cmn_err(CE_WARN, "terminal emulator: unsupported depth");
462 ret = tems_failed(credp, B_TRUE);
463 mutex_exit(&tems.ts_lock);
464 return (ret);
465 }
466
467 if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) {
468 cmn_err(CE_WARN, "terminal emulator: unsupported mode");
469 ret = tems_failed(credp, B_TRUE);
470 mutex_exit(&tems.ts_lock);
471 return (ret);
472 }
473
474 if ((temargs.mode == VIS_PIXEL) && plat_stdout_is_framebuffer())
475 plat_tem_get_prom_size(&height, &width);
476
477 /*
478 * Initialize the common terminal emulator info
479 */
480 tems_setup_terminal(&temargs, height, width);
481
482 tems_reset_colormap(credp, CALLED_FROM_NORMAL);
483 tems_get_initial_color(&tems.ts_init_color);
484
485 tems.ts_initialized = 1; /* initialization flag */
486
487 for (p = list_head(&tems.ts_list); p != NULL;
488 p = list_next(&tems.ts_list, p)) {
489 mutex_enter(&p->tvs_lock);
490 tem_internal_init(p, credp, B_TRUE, B_FALSE);
491 if (temargs.mode == VIS_PIXEL)
492 tem_pix_align(p, credp, CALLED_FROM_NORMAL);
493 mutex_exit(&p->tvs_lock);
494 }
495
496 mutex_exit(&tems.ts_lock);
497 return (0);
498 }
499
500 #define TEMS_DEPTH_DIFF 0x01
501 #define TEMS_DIMENSION_DIFF 0x02
502
503 static uchar_t
504 tems_check_videomode(struct vis_devinit *tp)
505 {
506 uchar_t result = 0;
507
508 if (tems.ts_pdepth != tp->depth)
509 result |= TEMS_DEPTH_DIFF;
510
511 if (tp->mode == VIS_TEXT) {
512 if (tems.ts_c_dimension.width != tp->width ||
513 tems.ts_c_dimension.height != tp->height)
514 result |= TEMS_DIMENSION_DIFF;
515 } else {
516 if (tems.ts_p_dimension.width != tp->width ||
517 tems.ts_p_dimension.height != tp->height)
518 result |= TEMS_DIMENSION_DIFF;
519 }
520
521 return (result);
522 }
523
524 static void
525 tems_setup_terminal(struct vis_devinit *tp, size_t height, size_t width)
526 {
527 int i;
528 int old_blank_buf_size = tems.ts_c_dimension.width;
529
530 ASSERT(MUTEX_HELD(&tems.ts_lock));
531
532 tems.ts_pdepth = tp->depth;
533 tems.ts_linebytes = tp->linebytes;
534 tems.ts_display_mode = tp->mode;
535
536 switch (tp->mode) {
537 case VIS_TEXT:
538 tems.ts_p_dimension.width = 0;
539 tems.ts_p_dimension.height = 0;
540 tems.ts_c_dimension.width = tp->width;
541 tems.ts_c_dimension.height = tp->height;
542 tems.ts_callbacks = &tem_safe_text_callbacks;
543
544 break;
545
546 case VIS_PIXEL:
547 /*
548 * First check to see if the user has specified a screen size.
549 * If so, use those values. Else use 34x80 as the default.
550 */
551 if (width == 0) {
552 width = TEM_DEFAULT_COLS;
553 height = TEM_DEFAULT_ROWS;
554 }
555 tems.ts_c_dimension.height = (screen_size_t)height;
556 tems.ts_c_dimension.width = (screen_size_t)width;
557
558 tems.ts_p_dimension.height = tp->height;
559 tems.ts_p_dimension.width = tp->width;
560
561 tems.ts_callbacks = &tem_safe_pix_callbacks;
562
563 /*
564 * set_font() will select a appropriate sized font for
565 * the number of rows and columns selected. If we don't
566 * have a font that will fit, then it will use the
567 * default builtin font and adjust the rows and columns
568 * to fit on the screen.
569 */
570 set_font(&tems.ts_font,
571 &tems.ts_c_dimension.height,
572 &tems.ts_c_dimension.width,
573 tems.ts_p_dimension.height,
574 tems.ts_p_dimension.width);
575
576 tems.ts_p_offset.y = (tems.ts_p_dimension.height -
577 (tems.ts_c_dimension.height * tems.ts_font.height)) / 2;
578 tems.ts_p_offset.x = (tems.ts_p_dimension.width -
579 (tems.ts_c_dimension.width * tems.ts_font.width)) / 2;
580
581 tems.ts_pix_data_size =
582 tems.ts_font.width * tems.ts_font.height;
583
584 tems.ts_pix_data_size *= 4;
585
586 tems.ts_pdepth = tp->depth;
587
588 break;
589 }
590
591 /* Now virtual cls also uses the blank_line buffer */
592 if (tems.ts_blank_line)
593 kmem_free(tems.ts_blank_line, old_blank_buf_size);
594
595 tems.ts_blank_line = (unsigned char *)
596 kmem_alloc(tems.ts_c_dimension.width, KM_SLEEP);
597 for (i = 0; i < tems.ts_c_dimension.width; i++)
598 tems.ts_blank_line[i] = ' ';
599 }
600
601 /*
602 * This is a callback function that we register with the frame
603 * buffer driver layered underneath. It gets invoked from
604 * the underlying frame buffer driver to reconfigure the terminal
605 * emulator to a new screen size and depth in conjunction with
606 * framebuffer videomode changes.
607 * Here we keep the foreground/background color and attributes,
608 * which may be different with the initial settings, so that
609 * the color won't change while the framebuffer videomode changes.
610 * And we also reset the kernel terminal emulator and clear the
611 * whole screen.
612 */
613 /* ARGSUSED */
614 void
615 tems_modechange_callback(struct vis_modechg_arg *arg,
616 struct vis_devinit *devinit)
617 {
618 uchar_t diff;
619 struct tem_vt_state *p;
620 tem_modechg_cb_t cb;
621 tem_modechg_cb_arg_t cb_arg;
622
623 ASSERT(!(list_is_empty(&tems.ts_list)));
624
625 mutex_enter(&tems.ts_lock);
626
627 /*
628 * currently only for pixel mode
629 */
630 diff = tems_check_videomode(devinit);
631 if (diff == 0) {
632 mutex_exit(&tems.ts_lock);
633 return;
634 }
635
636 diff = diff & TEMS_DIMENSION_DIFF;
637
638 if (diff == 0) {
639 /*
640 * Only need to reinit the active tem.
641 */
642 struct tem_vt_state *active = tems.ts_active;
643 tems.ts_pdepth = devinit->depth;
644
645 mutex_enter(&active->tvs_lock);
646 ASSERT(active->tvs_isactive);
647 tem_reinit(active, B_TRUE);
648 mutex_exit(&active->tvs_lock);
649
650 mutex_exit(&tems.ts_lock);
651 return;
652 }
653
654 tems_setup_terminal(devinit, tems.ts_c_dimension.height,
655 tems.ts_c_dimension.width);
656
657 for (p = list_head(&tems.ts_list); p != NULL;
658 p = list_next(&tems.ts_list, p)) {
659 mutex_enter(&p->tvs_lock);
660 tem_reinit(p, p->tvs_isactive);
661 mutex_exit(&p->tvs_lock);
662 }
663
664
665 if (tems.ts_modechg_cb == NULL) {
666 mutex_exit(&tems.ts_lock);
667 return;
668 }
669
670 cb = tems.ts_modechg_cb;
671 cb_arg = tems.ts_modechg_arg;
672
673 /*
674 * Release the lock while doing callback.
675 */
676 mutex_exit(&tems.ts_lock);
677 cb(cb_arg);
678 }
679
680 /*
681 * This function is used to display a rectangular blit of data
682 * of a given size and location via the underlying framebuffer driver.
683 * The blit can be as small as a pixel or as large as the screen.
684 */
685 void
686 tems_display_layered(
687 struct vis_consdisplay *pda,
688 cred_t *credp)
689 {
690 int rval;
691
692 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSDISPLAY,
693 (intptr_t)pda, FKIOCTL, credp, &rval);
694 }
695
696 /*
697 * This function is used to invoke a block copy operation in the
698 * underlying framebuffer driver. Rectangle copies are how scrolling
699 * is implemented, as well as horizontal text shifting escape seqs.
700 * such as from vi when deleting characters and words.
701 */
702 void
703 tems_copy_layered(
704 struct vis_conscopy *pma,
705 cred_t *credp)
706 {
707 int rval;
708
709 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCOPY,
710 (intptr_t)pma, FKIOCTL, credp, &rval);
711 }
712
713 /*
714 * This function is used to show or hide a rectangluar monochrom
715 * pixel inverting, text block cursor via the underlying framebuffer.
716 */
717 void
718 tems_cursor_layered(
719 struct vis_conscursor *pca,
720 cred_t *credp)
721 {
722 int rval;
723
724 (void) ldi_ioctl(tems.ts_hdl, VIS_CONSCURSOR,
725 (intptr_t)pca, FKIOCTL, credp, &rval);
726 }
727
728 static void
729 tem_kdsetmode(int mode, cred_t *credp)
730 {
731 int rval;
732
733 (void) ldi_ioctl(tems.ts_hdl, KDSETMODE,
734 (intptr_t)mode, FKIOCTL, credp, &rval);
735
736 }
737
738 static void
739 tems_reset_colormap(cred_t *credp, enum called_from called_from)
740 {
741 struct vis_cmap cm;
742 int rval;
743
744 if (called_from == CALLED_FROM_STANDALONE)
745 return;
746
747 switch (tems.ts_pdepth) {
748 case 8:
749 cm.index = 0;
750 cm.count = 16;
751 cm.red = cmap4_to_24.red; /* 8-bits (1/3 of TrueColor 24) */
752 cm.blue = cmap4_to_24.blue; /* 8-bits (1/3 of TrueColor 24) */
753 cm.green = cmap4_to_24.green; /* 8-bits (1/3 of TrueColor 24) */
754 (void) ldi_ioctl(tems.ts_hdl, VIS_PUTCMAP, (intptr_t)&cm,
755 FKIOCTL, credp, &rval);
756 break;
757 }
758 }
759
760 void
761 tem_get_size(ushort_t *r, ushort_t *c, ushort_t *x, ushort_t *y)
762 {
763 mutex_enter(&tems.ts_lock);
764 *r = (ushort_t)tems.ts_c_dimension.height;
765 *c = (ushort_t)tems.ts_c_dimension.width;
766 *x = (ushort_t)tems.ts_p_dimension.width;
767 *y = (ushort_t)tems.ts_p_dimension.height;
768 mutex_exit(&tems.ts_lock);
769 }
770
771 void
772 tem_register_modechg_cb(tem_modechg_cb_t func, tem_modechg_cb_arg_t arg)
773 {
774 mutex_enter(&tems.ts_lock);
775
776 tems.ts_modechg_cb = func;
777 tems.ts_modechg_arg = arg;
778
779 mutex_exit(&tems.ts_lock);
780 }
781
782 /*
783 * This function is to scroll up the OBP output, which has
784 * different screen height and width with our kernel console.
785 */
786 static void
787 tem_prom_scroll_up(struct tem_vt_state *tem, int nrows, cred_t *credp,
788 enum called_from called_from)
789 {
790 struct vis_conscopy ma;
791 int ncols, width;
792
793 /* copy */
794 ma.s_row = nrows * tems.ts_font.height;
795 ma.e_row = tems.ts_p_dimension.height - 1;
796 ma.t_row = 0;
797
798 ma.s_col = 0;
799 ma.e_col = tems.ts_p_dimension.width - 1;
800 ma.t_col = 0;
801
802 tems_safe_copy(&ma, credp, called_from);
803
804 /* clear */
805 width = tems.ts_font.width;
806 ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
807
808 tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y,
809 0, ncols, 0, B_TRUE, credp, called_from);
810 }
811
812 #define PROM_DEFAULT_FONT_HEIGHT 22
813 #define PROM_DEFAULT_WINDOW_TOP 0x8a
814
815 /*
816 * This function is to compute the starting row of the console, according to
817 * PROM cursor's position. Here we have to take different fonts into account.
818 */
819 static int
820 tem_adjust_row(struct tem_vt_state *tem, int prom_row, cred_t *credp,
821 enum called_from called_from)
822 {
823 int tem_row;
824 int tem_y;
825 int prom_charheight = 0;
826 int prom_window_top = 0;
827 int scroll_up_lines;
828
829 plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top);
830 if (prom_charheight == 0)
831 prom_charheight = PROM_DEFAULT_FONT_HEIGHT;
832 if (prom_window_top == 0)
833 prom_window_top = PROM_DEFAULT_WINDOW_TOP;
834
835 tem_y = (prom_row + 1) * prom_charheight + prom_window_top -
836 tems.ts_p_offset.y;
837 tem_row = (tem_y + tems.ts_font.height - 1) /
838 tems.ts_font.height - 1;
839
840 if (tem_row < 0) {
841 tem_row = 0;
842 } else if (tem_row >= (tems.ts_c_dimension.height - 1)) {
843 /*
844 * Scroll up the prom outputs if the PROM cursor's position is
845 * below our tem's lower boundary.
846 */
847 scroll_up_lines = tem_row -
848 (tems.ts_c_dimension.height - 1);
849 tem_prom_scroll_up(tem, scroll_up_lines, credp, called_from);
850 tem_row = tems.ts_c_dimension.height - 1;
851 }
852
853 return (tem_row);
854 }
855
856 void
857 tem_pix_align(struct tem_vt_state *tem, cred_t *credp,
858 enum called_from called_from)
859 {
860 uint32_t row = 0;
861 uint32_t col = 0;
862
863 if (plat_stdout_is_framebuffer()) {
864 plat_tem_hide_prom_cursor();
865
866 /*
867 * We are getting the current cursor position in pixel
868 * mode so that we don't over-write the console output
869 * during boot.
870 */
871 plat_tem_get_prom_pos(&row, &col);
872
873 /*
874 * Adjust the row if necessary when the font of our
875 * kernel console tem is different with that of prom
876 * tem.
877 */
878 row = tem_adjust_row(tem, row, credp, called_from);
879
880 /* first line of our kernel console output */
881 tem->tvs_first_line = row + 1;
882
883 /* re-set and align cusror position */
884 tem->tvs_s_cursor.row = tem->tvs_c_cursor.row =
885 (screen_pos_t)row;
886 tem->tvs_s_cursor.col = tem->tvs_c_cursor.col = 0;
887 } else {
888 tem_safe_reset_display(tem, credp, called_from, B_TRUE, B_TRUE);
889 }
890 }
891
892 static void
893 tems_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen)
894 {
895 int i_inverse = 0;
896 int i_inverse_screen = 0;
897
898 plat_tem_get_inverses(&i_inverse, &i_inverse_screen);
899
900 *p_inverse = (i_inverse == 0) ? B_FALSE : B_TRUE;
901 *p_inverse_screen = (i_inverse_screen == 0) ? B_FALSE : B_TRUE;
902 }
903
904 /*
905 * Get the foreground/background color and attributes from the initial
906 * PROM, so that our kernel console can keep the same visual behaviour.
907 */
908 static void
909 tems_get_initial_color(tem_color_t *pcolor)
910 {
911 boolean_t inverse, inverse_screen;
912 unsigned short flags = 0;
913
914 pcolor->fg_color = DEFAULT_ANSI_FOREGROUND;
915 pcolor->bg_color = DEFAULT_ANSI_BACKGROUND;
916
917 if (plat_stdout_is_framebuffer()) {
918 tems_get_inverses(&inverse, &inverse_screen);
919 if (inverse)
920 flags |= TEM_ATTR_REVERSE;
921 if (inverse_screen)
922 flags |= TEM_ATTR_SCREEN_REVERSE;
923
924 if (flags != 0) {
925 /*
926 * If either reverse flag is set, the screen is in
927 * white-on-black mode. We set the bold flag to
928 * improve readability.
929 */
930 flags |= TEM_ATTR_BOLD;
931 } else {
932 /*
933 * Otherwise, the screen is in black-on-white mode.
934 * The SPARC PROM console, which starts in this mode,
935 * uses the bright white background colour so we
936 * match it here.
937 */
938 if (pcolor->bg_color == ANSI_COLOR_WHITE)
939 flags |= TEM_ATTR_BRIGHT_BG;
940 }
941 }
942
943 pcolor->a_flags = flags;
944 }
945
946 uchar_t
947 tem_get_fbmode(tem_vt_state_t tem_arg)
948 {
949 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
950
951 uchar_t fbmode;
952
953 mutex_enter(&tem->tvs_lock);
954 fbmode = tem->tvs_fbmode;
955 mutex_exit(&tem->tvs_lock);
956
957 return (fbmode);
958 }
959
960 void
961 tem_set_fbmode(tem_vt_state_t tem_arg, uchar_t fbmode, cred_t *credp)
962 {
963 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
964
965 mutex_enter(&tems.ts_lock);
966 mutex_enter(&tem->tvs_lock);
967
968 if (fbmode == tem->tvs_fbmode) {
969 mutex_exit(&tem->tvs_lock);
970 mutex_exit(&tems.ts_lock);
971 return;
972 }
973
974 tem->tvs_fbmode = fbmode;
975
976 if (tem->tvs_isactive) {
977 tem_kdsetmode(tem->tvs_fbmode, credp);
978 if (fbmode == KD_TEXT)
979 tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL);
980 }
981
982 mutex_exit(&tem->tvs_lock);
983 mutex_exit(&tems.ts_lock);
984 }
985
986 void
987 tem_activate(tem_vt_state_t tem_arg, boolean_t unblank, cred_t *credp)
988 {
989 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
990
991 mutex_enter(&tems.ts_lock);
992 tems.ts_active = tem;
993
994 mutex_enter(&tem->tvs_lock);
995 tem->tvs_isactive = B_TRUE;
996
997 tem_kdsetmode(tem->tvs_fbmode, credp);
998
999 if (unblank)
1000 tem_safe_unblank_screen(tem, credp, CALLED_FROM_NORMAL);
1001
1002 mutex_exit(&tem->tvs_lock);
1003 mutex_exit(&tems.ts_lock);
1004 }
1005
1006 void
1007 tem_switch(tem_vt_state_t tem_arg1, tem_vt_state_t tem_arg2, cred_t *credp)
1008 {
1009 struct tem_vt_state *cur = (struct tem_vt_state *)tem_arg1;
1010 struct tem_vt_state *tobe = (struct tem_vt_state *)tem_arg2;
1011
1012 mutex_enter(&tems.ts_lock);
1013 mutex_enter(&tobe->tvs_lock);
1014 mutex_enter(&cur->tvs_lock);
1015
1016 tems.ts_active = tobe;
1017 cur->tvs_isactive = B_FALSE;
1018 tobe->tvs_isactive = B_TRUE;
1019
1020 mutex_exit(&cur->tvs_lock);
1021
1022 if (cur->tvs_fbmode != tobe->tvs_fbmode)
1023 tem_kdsetmode(tobe->tvs_fbmode, credp);
1024
1025 if (tobe->tvs_fbmode == KD_TEXT)
1026 tem_safe_unblank_screen(tobe, credp, CALLED_FROM_NORMAL);
1027
1028 mutex_exit(&tobe->tvs_lock);
1029 mutex_exit(&tems.ts_lock);
1030 }