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