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