1 /******************************************************************************
2 * lfb.c
3 *
4 * linear frame buffer handling.
5 */
6
7 #include <xen/kernel.h>
8 #include <xen/lib.h>
9 #include <xen/errno.h>
10 #include <xen/xvmalloc.h>
11
12 #include "lfb.h"
13 #include "font.h"
14
15 struct lfb_status {
16 struct lfb_prop lfbp;
17
18 unsigned char *lbuf, *text_buf;
19 unsigned int *line_len;
20 unsigned int xpos, ypos;
21 };
22 static struct lfb_status lfb;
23
lfb_show_line(const unsigned char * text_line,unsigned char * video_line,unsigned int nr_chars,unsigned int nr_cells)24 static void lfb_show_line(
25 const unsigned char *text_line,
26 unsigned char *video_line,
27 unsigned int nr_chars,
28 unsigned int nr_cells)
29 {
30 unsigned int i, j, b, bpp, pixel;
31
32 bpp = (lfb.lfbp.bits_per_pixel + 7) >> 3;
33
34 for ( i = 0; i < lfb.lfbp.font->height; i++ )
35 {
36 unsigned char *ptr = lfb.lbuf;
37
38 for ( j = 0; j < nr_chars; j++ )
39 {
40 const unsigned char *bits = lfb.lfbp.font->data;
41 bits += ((text_line[j] * lfb.lfbp.font->height + i) *
42 ((lfb.lfbp.font->width + 7) >> 3));
43 for ( b = lfb.lfbp.font->width; b--; )
44 {
45 pixel = (*bits & (1u<<b)) ? lfb.lfbp.pixel_on : 0;
46 memcpy(ptr, &pixel, bpp);
47 ptr += bpp;
48 }
49 }
50
51 memset(ptr, 0, (lfb.lfbp.width - nr_chars * lfb.lfbp.font->width) * bpp);
52 memcpy(video_line, lfb.lbuf, nr_cells * lfb.lfbp.font->width * bpp);
53 video_line += lfb.lfbp.bytes_per_line;
54 }
55 }
56
57 /* Fast mode which redraws all modified parts of a 2D text buffer. */
lfb_redraw_puts(const char * s,size_t nr)58 void cf_check lfb_redraw_puts(const char *s, size_t nr)
59 {
60 unsigned int i, min_redraw_y = lfb.ypos;
61
62 /* Paste characters into text buffer. */
63 for ( ; nr > 0; nr--, s++ )
64 {
65 char c = *s;
66
67 if ( (c == '\n') || (lfb.xpos >= lfb.lfbp.text_columns) )
68 {
69 if ( ++lfb.ypos >= lfb.lfbp.text_rows )
70 {
71 min_redraw_y = 0;
72 lfb.ypos = lfb.lfbp.text_rows - 1;
73 memmove(lfb.text_buf, lfb.text_buf + lfb.lfbp.text_columns,
74 lfb.ypos * lfb.lfbp.text_columns);
75 memset(lfb.text_buf + lfb.ypos * lfb.lfbp.text_columns, 0, lfb.xpos);
76 }
77 lfb.xpos = 0;
78 }
79
80 if ( c != '\n' )
81 lfb.text_buf[lfb.xpos++ + lfb.ypos * lfb.lfbp.text_columns] = c;
82 }
83
84 /* Render modified section of text buffer to VESA linear framebuffer. */
85 for ( i = min_redraw_y; i <= lfb.ypos; i++ )
86 {
87 const unsigned char *line = lfb.text_buf + i * lfb.lfbp.text_columns;
88 unsigned int width;
89
90 for ( width = lfb.lfbp.text_columns; width; --width )
91 if ( line[width - 1] )
92 break;
93 lfb_show_line(line,
94 lfb.lfbp.lfb + i * lfb.lfbp.font->height * lfb.lfbp.bytes_per_line,
95 width, max(lfb.line_len[i], width));
96 lfb.line_len[i] = width;
97 }
98
99 lfb.lfbp.flush();
100 }
101
102 /* Slower line-based scroll mode which interacts better with dom0. */
lfb_scroll_puts(const char * s,size_t nr)103 void cf_check lfb_scroll_puts(const char *s, size_t nr)
104 {
105 unsigned int i;
106
107 for ( ; nr > 0; nr--, s++ )
108 {
109 char c = *s;
110
111 if ( (c == '\n') || (lfb.xpos >= lfb.lfbp.text_columns) )
112 {
113 unsigned int bytes = (lfb.lfbp.width *
114 ((lfb.lfbp.bits_per_pixel + 7) >> 3));
115 unsigned char *src = lfb.lfbp.lfb + lfb.lfbp.font->height * lfb.lfbp.bytes_per_line;
116 unsigned char *dst = lfb.lfbp.lfb;
117
118 /* New line: scroll all previous rows up one line. */
119 for ( i = lfb.lfbp.font->height; i < lfb.lfbp.height; i++ )
120 {
121 memcpy(dst, src, bytes);
122 src += lfb.lfbp.bytes_per_line;
123 dst += lfb.lfbp.bytes_per_line;
124 }
125
126 /* Render new line. */
127 lfb_show_line(
128 lfb.text_buf,
129 lfb.lfbp.lfb + (lfb.lfbp.text_rows-1) * lfb.lfbp.font->height *
130 lfb.lfbp.bytes_per_line,
131 lfb.xpos, lfb.lfbp.text_columns);
132
133 lfb.xpos = 0;
134 }
135
136 if ( c != '\n' )
137 lfb.text_buf[lfb.xpos++] = c;
138 }
139
140 lfb.lfbp.flush();
141 }
142
lfb_carriage_return(void)143 void lfb_carriage_return(void)
144 {
145 lfb.xpos = 0;
146 }
147
lfb_init(struct lfb_prop * lfbp)148 int __init lfb_init(struct lfb_prop *lfbp)
149 {
150 lfb.lfbp = *lfbp;
151
152 lfb.lbuf = xvmalloc_array(unsigned char, lfb.lfbp.bytes_per_line);
153 lfb.text_buf = xvzalloc_array(unsigned char,
154 lfb.lfbp.text_columns * lfb.lfbp.text_rows);
155 lfb.line_len = xvzalloc_array(unsigned int, lfb.lfbp.text_columns);
156
157 if ( !lfb.lbuf || !lfb.text_buf || !lfb.line_len )
158 goto fail;
159
160 return 0;
161
162 fail:
163 printk(XENLOG_ERR "Couldn't allocate enough memory to drive the framebuffer\n");
164 lfb_free();
165
166 return -ENOMEM;
167 }
168
lfb_free(void)169 void lfb_free(void)
170 {
171 XVFREE(lfb.lbuf);
172 XVFREE(lfb.text_buf);
173 XVFREE(lfb.line_len);
174 lfb.lfbp.lfb = ZERO_BLOCK_PTR;
175 }
176