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