1 #include <minix/fb.h>
2 #include <minix/chardriver.h>
3 #include <minix/drivers.h>
4 #include <minix/ds.h>
5 #include <minix/sysutil.h>
6 #include <minix/type.h>
7 #include <minix/vm.h>
8 #include <sys/ioc_fb.h>
9 #include <assert.h>
10 #include <sys/ioctl.h>
11 #include <sys/mman.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdint.h>
15 #include <dev/videomode/videomode.h>
16 #include <dev/videomode/edidvar.h>
17 #include <dev/videomode/edidreg.h>
18
19 #include "logos.h"
20 #include "fb_edid.h"
21 #include "fb.h"
22
23 /*
24 * Function prototypes for the fb driver.
25 */
26 static int fb_open(devminor_t minor, int access, endpoint_t user_endpt);
27 static int fb_close(devminor_t minor);
28 static ssize_t fb_read(devminor_t minor, u64_t pos, endpoint_t ep,
29 cp_grant_id_t gid, size_t size, int flags, cdev_id_t id);
30 static ssize_t fb_write(devminor_t minor, u64_t pos, endpoint_t ep,
31 cp_grant_id_t gid, size_t size, int flags, cdev_id_t id);
32 static int fb_ioctl(devminor_t minor, unsigned long request, endpoint_t ep,
33 cp_grant_id_t gid, int flags, endpoint_t user_ep, cdev_id_t id);
34 static void paint_bootlogo(int minor);
35 static void paint_restartlogo(int minor);
36 static void paint_centered(int minor, char *data, int width, int height);
37 static int do_get_varscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid);
38 static int do_put_varscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid);
39 static int do_get_fixscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid);
40 static int do_pan_display(int minor, endpoint_t ep, cp_grant_id_t gid);
41 static int keep_displaying_restarted(void);
42
43 /* Entry points to the fb driver. */
44 static struct chardriver fb_tab =
45 {
46 .cdr_open = fb_open,
47 .cdr_close = fb_close,
48 .cdr_read = fb_read,
49 .cdr_write = fb_write,
50 .cdr_ioctl = fb_ioctl
51 };
52
53 /** Represents the /dev/fb device. */
54 static int has_restarted = 0;
55 static u64_t has_restarted_t1, has_restarted_t2;
56
57 static int open_counter[FB_DEV_NR]; /* Open count */
58
59 static int
fb_open(devminor_t minor,int UNUSED (access),endpoint_t UNUSED (user_endpt))60 fb_open(devminor_t minor, int UNUSED(access), endpoint_t UNUSED(user_endpt))
61 {
62 int r;
63 static int initialized = 0;
64 static struct edid_info info;
65 static struct edid_info *infop = NULL;
66
67 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
68
69 if (!initialized) {
70 r = fb_edid_read(minor, &info);
71 infop = (r == 0) ? &info : NULL;
72 }
73
74 if (arch_fb_init(minor, infop) == OK) {
75 open_counter[minor]++;
76 if (!initialized) {
77 if (has_restarted) {
78 read_frclock_64(&has_restarted_t1);
79 paint_restartlogo(minor);
80 } else {
81 paint_bootlogo(minor);
82 }
83 initialized = 1;
84 }
85 return OK;
86 }
87 return ENXIO;
88 }
89
90 static int
fb_close(devminor_t minor)91 fb_close(devminor_t minor)
92 {
93 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
94 assert(open_counter[minor] > 0);
95 open_counter[minor]--;
96 return OK;
97 }
98
99 static ssize_t
fb_read(devminor_t minor,u64_t pos,endpoint_t ep,cp_grant_id_t gid,size_t size,int UNUSED (flags),cdev_id_t UNUSED (id))100 fb_read(devminor_t minor, u64_t pos, endpoint_t ep, cp_grant_id_t gid,
101 size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
102 {
103 struct device dev;
104 int r;
105
106 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
107 assert(open_counter[minor] > 0);
108
109 arch_get_device(minor, &dev);
110
111 if (size == 0 || pos >= dev.dv_size) return 0;
112 if (pos + size > dev.dv_size)
113 size = (size_t)(dev.dv_size - pos);
114
115 r = sys_safecopyto(ep, gid, 0, (vir_bytes)(dev.dv_base + (size_t)pos),
116 size);
117
118 return (r != OK) ? r : size;
119 }
120
121 static int
fb_ioctl(devminor_t minor,unsigned long request,endpoint_t ep,cp_grant_id_t gid,int UNUSED (flags),endpoint_t UNUSED (user_ep),cdev_id_t UNUSED (id))122 fb_ioctl(devminor_t minor, unsigned long request, endpoint_t ep,
123 cp_grant_id_t gid, int UNUSED(flags), endpoint_t UNUSED(user_ep),
124 cdev_id_t UNUSED(id))
125 {
126 /* Process I/O control requests */
127 int r;
128
129 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
130
131 switch(request) {
132 case FBIOGET_VSCREENINFO:
133 r = do_get_varscreeninfo(minor, ep, gid);
134 return r;
135 case FBIOPUT_VSCREENINFO:
136 r = do_put_varscreeninfo(minor, ep, gid);
137 return r;
138 case FBIOGET_FSCREENINFO:
139 r = do_get_fixscreeninfo(minor, ep, gid);
140 return r;
141 case FBIOPAN_DISPLAY:
142 r = do_pan_display(minor, ep, gid);
143 return r;
144 }
145
146 return ENOTTY;
147 }
148
149 static int
do_get_varscreeninfo(int minor,endpoint_t ep,cp_grant_id_t gid)150 do_get_varscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid)
151 {
152 int r;
153 struct fb_var_screeninfo fbvs;
154
155 if ((r = arch_get_varscreeninfo(minor, &fbvs)) == OK) {
156 r = sys_safecopyto(ep, gid, 0, (vir_bytes) &fbvs, sizeof(fbvs));
157 }
158
159 return r;
160 }
161
162 static int
do_put_varscreeninfo(int minor,endpoint_t ep,cp_grant_id_t gid)163 do_put_varscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid)
164 {
165 int r;
166 struct fb_var_screeninfo fbvs_copy;
167
168 if (has_restarted && keep_displaying_restarted()) {
169 return EAGAIN;
170 }
171
172 if ((r = sys_safecopyfrom(ep, gid, 0, (vir_bytes) &fbvs_copy,
173 sizeof(fbvs_copy))) != OK) {
174 return r;
175 }
176
177 return arch_put_varscreeninfo(minor, &fbvs_copy);
178 }
179
180 static int
do_pan_display(int minor,endpoint_t ep,cp_grant_id_t gid)181 do_pan_display(int minor, endpoint_t ep, cp_grant_id_t gid)
182 {
183 int r;
184 struct fb_var_screeninfo fbvs_copy;
185
186 if (has_restarted && keep_displaying_restarted()) {
187 return EAGAIN;
188 }
189
190 if ((r = sys_safecopyfrom(ep, gid, 0, (vir_bytes) &fbvs_copy,
191 sizeof(fbvs_copy))) != OK) {
192 return r;
193 }
194
195 return arch_pan_display(minor, &fbvs_copy);
196 }
197
198 static int
do_get_fixscreeninfo(int minor,endpoint_t ep,cp_grant_id_t gid)199 do_get_fixscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid)
200 {
201 int r;
202 struct fb_fix_screeninfo fbfs;
203
204 if ((r = arch_get_fixscreeninfo(minor, &fbfs)) == OK) {
205 r = sys_safecopyto(ep, gid, 0, (vir_bytes) &fbfs, sizeof(fbfs));
206 }
207
208 return r;
209 }
210
211 static ssize_t
fb_write(devminor_t minor,u64_t pos,endpoint_t ep,cp_grant_id_t gid,size_t size,int UNUSED (flags),cdev_id_t UNUSED (id))212 fb_write(devminor_t minor, u64_t pos, endpoint_t ep, cp_grant_id_t gid,
213 size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
214 {
215 struct device dev;
216 int r;
217
218 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
219 assert(open_counter[minor] > 0);
220
221 if (has_restarted && keep_displaying_restarted())
222 return EAGAIN;
223
224 arch_get_device(minor, &dev);
225
226 if (size == 0 || pos >= dev.dv_size) return 0;
227 if (pos + size > dev.dv_size)
228 size = (size_t)(dev.dv_size - pos);
229
230 r = sys_safecopyfrom(ep, gid, 0,
231 (vir_bytes)(dev.dv_base + (size_t)pos), size);
232
233 return (r != OK) ? r : size;
234 }
235
236 static int
sef_cb_lu_state_save(int UNUSED (result),int UNUSED (flags))237 sef_cb_lu_state_save(int UNUSED(result), int UNUSED(flags))
238 {
239 /* Save the state. */
240 ds_publish_u32("open_counter", open_counter[0], DSF_OVERWRITE);
241
242 return OK;
243 }
244
245 static int
lu_state_restore()246 lu_state_restore() {
247 /* Restore the state. */
248 u32_t value;
249
250 ds_retrieve_u32("open_counter", &value);
251 ds_delete_u32("open_counter");
252 open_counter[0] = (int) value;
253
254 return OK;
255 }
256
257 static int
sef_cb_init(int type,sef_init_info_t * UNUSED (info))258 sef_cb_init(int type, sef_init_info_t *UNUSED(info))
259 {
260 /* Initialize the fb driver. */
261 int do_announce_driver = TRUE;
262
263 open_counter[0] = 0;
264 switch(type) {
265 case SEF_INIT_FRESH:
266 printf("framebuffer fresh: pid %d\n", getpid());
267 break;
268
269 case SEF_INIT_LU:
270 /* Restore the state. */
271 lu_state_restore();
272 do_announce_driver = FALSE;
273
274 printf("framebuffer: I'm a new version!\n");
275 break;
276
277 case SEF_INIT_RESTART:
278 printf("framebuffer restarted: pid %d\n", getpid());
279 has_restarted = 1;
280 break;
281 }
282
283 /* Announce we are up when necessary. */
284 if (do_announce_driver) {
285 chardriver_announce();
286 }
287
288 /* Initialization completed successfully. */
289 return OK;
290 }
291
292 static void
sef_local_startup()293 sef_local_startup()
294 {
295 /* Register init callbacks. Use the same function for all event types */
296 sef_setcb_init_fresh(sef_cb_init);
297 sef_setcb_init_lu(sef_cb_init);
298 sef_setcb_init_restart(sef_cb_init);
299
300 /* Register live update callbacks */
301 sef_setcb_lu_state_save(sef_cb_lu_state_save);
302
303 /* Let SEF perform startup. */
304 sef_startup();
305 }
306
307 int
main(int argc,char * argv[])308 main(int argc, char *argv[])
309 {
310 env_setargs(argc, argv);
311 fb_edid_args_parse();
312
313 sef_local_startup();
314 chardriver_task(&fb_tab);
315 return OK;
316 }
317
318 static int
keep_displaying_restarted()319 keep_displaying_restarted()
320 {
321 u64_t delta;
322 u32_t micro_delta;
323
324 read_frclock_64(&has_restarted_t2);
325 delta = delta_frclock_64(has_restarted_t1, has_restarted_t2);
326 micro_delta = frclock_64_to_micros(delta);
327
328 #define DISPLAY_1SEC 1000000 /* 1 second in microseconds */
329 if (micro_delta < DISPLAY_1SEC) {
330 return 1;
331 }
332
333 has_restarted = 0;
334 return 0;
335 }
336
337 static void
paint_bootlogo(int minor)338 paint_bootlogo(int minor)
339 {
340 paint_centered(minor, bootlogo_data, bootlogo_width, bootlogo_height);
341 }
342
343 static void
paint_restartlogo(int minor)344 paint_restartlogo(int minor)
345 {
346 paint_centered(minor, restartlogo_data, restartlogo_width,
347 restartlogo_height);
348 }
349
350 static void
paint_centered(int minor,char * data,int width,int height)351 paint_centered(int minor, char *data, int width, int height)
352 {
353 u8_t pixel[3];
354 u32_t i, min_x, min_y, max_x, max_y, x_painted = 0, rows = 0;
355 int r, bytespp;
356 struct device dev;
357 struct fb_var_screeninfo fbvs;
358
359 /* Put display in a known state to simplify positioning code below */
360 if ((r = arch_get_varscreeninfo(minor, &fbvs)) != OK) {
361 printf("fb: unable to get screen info: %d\n", r);
362 }
363 fbvs.yoffset = 0;
364 if ((r = arch_pan_display(minor, &fbvs)) != OK) {
365 printf("fb: unable to pan display: %d\n", r);
366 }
367
368 arch_get_device(minor, &dev);
369
370 /* Paint on a white canvas */
371 bytespp = fbvs.bits_per_pixel / 8;
372 for (i = 0; i < fbvs.xres * fbvs.yres * bytespp; i+= bytespp)
373 *((u32_t *)((u32_t) dev.dv_base + i)) = 0x00FFFFFF;
374
375 /* First seek to start */
376 min_x = fbvs.xres / 2 - width / 2;
377 max_x = fbvs.xres / 2 + width / 2;
378 min_y = fbvs.yres / 2 - height / 2;
379 max_y = fbvs.yres / 2 + height / 2;
380 i = min_x * fbvs.xres + min_y;
381
382 /* Add the image data */
383 for (i = ((min_y * fbvs.xres) + min_x) * bytespp; rows < height;) {
384 GET_PIXEL(data, pixel);
385
386 ((unsigned char *)((u32_t) dev.dv_base + i))[0] = pixel[2];
387 ((unsigned char *)((u32_t) dev.dv_base + i))[1] = pixel[1];
388 ((unsigned char *)((u32_t) dev.dv_base + i))[2] = pixel[0];
389 ((unsigned char *)((u32_t) dev.dv_base + i))[3] = 0;
390
391 x_painted++;/* Keep tab of how many row pixels we've painted */
392 if (x_painted == width) {
393 /* We've reached the end of the row, carriage return
394 * and go to next line.
395 */
396 x_painted = 0;
397 rows++;
398 i = (((min_y + rows) * fbvs.xres) + min_x) * 4;
399 } else {
400 i += 4;
401 }
402 }
403 }
404
405