custom message type for VM_INFO
[minix3.git] / drivers / fb / fb.c
blobd8d271d33d0a0d73ff7935396dbeacb55e3286fc
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>
19 #include "logos.h"
20 #include "fb_edid.h"
21 #include "fb.h"
24 * Function prototypes for the fb driver.
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);
43 /* SEF functions and variables. */
44 static void sef_local_startup(void);
45 static int sef_cb_init(int type, sef_init_info_t *info);
46 static int sef_cb_lu_state_save(int);
47 static int lu_state_restore(void);
49 /* Entry points to the fb driver. */
50 static struct chardriver fb_tab =
52 .cdr_open = fb_open,
53 .cdr_close = fb_close,
54 .cdr_read = fb_read,
55 .cdr_write = fb_write,
56 .cdr_ioctl = fb_ioctl
59 /** Represents the /dev/fb device. */
60 static int has_restarted = 0;
61 static u64_t has_restarted_t1, has_restarted_t2;
63 static int open_counter[FB_DEV_NR]; /* Open count */
65 static int
66 fb_open(devminor_t minor, int UNUSED(access), endpoint_t UNUSED(user_endpt))
68 int r;
69 static int initialized = 0;
70 static struct edid_info info;
71 static struct edid_info *infop = NULL;
73 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
75 if (!initialized) {
76 r = fb_edid_read(minor, &info);
77 infop = (r == 0) ? &info : NULL;
80 if (arch_fb_init(minor, infop) == OK) {
81 open_counter[minor]++;
82 if (!initialized) {
83 if (has_restarted) {
84 read_frclock_64(&has_restarted_t1);
85 paint_restartlogo(minor);
86 } else {
87 paint_bootlogo(minor);
89 initialized = 1;
91 return OK;
93 return ENXIO;
96 static int
97 fb_close(devminor_t minor)
99 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
100 assert(open_counter[minor] > 0);
101 open_counter[minor]--;
102 return OK;
105 static ssize_t
106 fb_read(devminor_t minor, u64_t pos, endpoint_t ep, cp_grant_id_t gid,
107 size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
109 struct device dev;
110 int r;
112 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
113 assert(open_counter[minor] > 0);
115 arch_get_device(minor, &dev);
117 if (size == 0 || pos >= dev.dv_size) return 0;
118 if (pos + size > dev.dv_size)
119 size = (size_t)(dev.dv_size - pos);
121 r = sys_safecopyto(ep, gid, 0, (vir_bytes)(dev.dv_base + (size_t)pos),
122 size);
124 return (r != OK) ? r : size;
127 static int
128 fb_ioctl(devminor_t minor, unsigned long request, endpoint_t ep,
129 cp_grant_id_t gid, int UNUSED(flags), endpoint_t UNUSED(user_ep),
130 cdev_id_t UNUSED(id))
132 /* Process I/O control requests */
133 int r;
135 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
137 switch(request) {
138 case FBIOGET_VSCREENINFO:
139 r = do_get_varscreeninfo(minor, ep, gid);
140 return r;
141 case FBIOPUT_VSCREENINFO:
142 r = do_put_varscreeninfo(minor, ep, gid);
143 return r;
144 case FBIOGET_FSCREENINFO:
145 r = do_get_fixscreeninfo(minor, ep, gid);
146 return r;
147 case FBIOPAN_DISPLAY:
148 r = do_pan_display(minor, ep, gid);
149 return r;
152 return ENOTTY;
155 static int
156 do_get_varscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid)
158 int r;
159 struct fb_var_screeninfo fbvs;
161 if ((r = arch_get_varscreeninfo(minor, &fbvs)) == OK) {
162 r = sys_safecopyto(ep, gid, 0, (vir_bytes) &fbvs, sizeof(fbvs));
165 return r;
168 static int
169 do_put_varscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid)
171 int r;
172 struct fb_var_screeninfo fbvs_copy;
174 if (has_restarted && keep_displaying_restarted()) {
175 return EAGAIN;
178 if ((r = sys_safecopyfrom(ep, gid, 0, (vir_bytes) &fbvs_copy,
179 sizeof(fbvs_copy))) != OK) {
180 return r;
183 return arch_put_varscreeninfo(minor, &fbvs_copy);
186 static int
187 do_pan_display(int minor, endpoint_t ep, cp_grant_id_t gid)
189 int r;
190 struct fb_var_screeninfo fbvs_copy;
192 if (has_restarted && keep_displaying_restarted()) {
193 return EAGAIN;
196 if ((r = sys_safecopyfrom(ep, gid, 0, (vir_bytes) &fbvs_copy,
197 sizeof(fbvs_copy))) != OK) {
198 return r;
201 return arch_pan_display(minor, &fbvs_copy);
204 static int
205 do_get_fixscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid)
207 int r;
208 struct fb_fix_screeninfo fbfs;
210 if ((r = arch_get_fixscreeninfo(minor, &fbfs)) == OK) {
211 r = sys_safecopyto(ep, gid, 0, (vir_bytes) &fbfs, sizeof(fbfs));
214 return r;
217 static ssize_t
218 fb_write(devminor_t minor, u64_t pos, endpoint_t ep, cp_grant_id_t gid,
219 size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
221 struct device dev;
222 int r;
224 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
225 assert(open_counter[minor] > 0);
227 if (has_restarted && keep_displaying_restarted())
228 return EAGAIN;
230 arch_get_device(minor, &dev);
232 if (size == 0 || pos >= dev.dv_size) return 0;
233 if (pos + size > dev.dv_size)
234 size = (size_t)(dev.dv_size - pos);
236 r = sys_safecopyfrom(ep, gid, 0,
237 (vir_bytes)(dev.dv_base + (size_t)pos), size);
239 return (r != OK) ? r : size;
242 static int
243 sef_cb_lu_state_save(int UNUSED(state)) {
244 /* Save the state. */
245 ds_publish_u32("open_counter", open_counter[0], DSF_OVERWRITE);
247 return OK;
250 static int
251 lu_state_restore() {
252 /* Restore the state. */
253 u32_t value;
255 ds_retrieve_u32("open_counter", &value);
256 ds_delete_u32("open_counter");
257 open_counter[0] = (int) value;
259 return OK;
262 static void
263 sef_local_startup()
265 /* Register init callbacks. Use the same function for all event types */
266 sef_setcb_init_fresh(sef_cb_init);
267 sef_setcb_init_lu(sef_cb_init);
268 sef_setcb_init_restart(sef_cb_init);
270 /* Register live update callbacks */
271 /* - Agree to update immediately when LU is requested in a valid state*/
272 sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
273 /* - Support live update starting from any standard state */
274 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
275 /* - Register a custom routine to save the state. */
276 sef_setcb_lu_state_save(sef_cb_lu_state_save);
278 /* Let SEF perform startup. */
279 sef_startup();
282 static int
283 sef_cb_init(int type, sef_init_info_t *UNUSED(info))
285 /* Initialize the fb driver. */
286 int do_announce_driver = TRUE;
288 open_counter[0] = 0;
289 switch(type) {
290 case SEF_INIT_FRESH:
291 printf("framebuffer fresh: pid %d\n", getpid());
292 break;
294 case SEF_INIT_LU:
295 /* Restore the state. */
296 lu_state_restore();
297 do_announce_driver = FALSE;
299 printf("framebuffer: I'm a new version!\n");
300 break;
302 case SEF_INIT_RESTART:
303 printf("framebuffer restarted: pid %d\n", getpid());
304 has_restarted = 1;
305 break;
308 /* Announce we are up when necessary. */
309 if (do_announce_driver) {
310 chardriver_announce();
313 /* Initialization completed successfully. */
314 return OK;
318 main(int argc, char *argv[])
320 env_setargs(argc, argv);
321 fb_edid_args_parse();
323 sef_local_startup();
324 chardriver_task(&fb_tab);
325 return OK;
328 static int
329 keep_displaying_restarted()
331 u64_t delta;
332 u32_t micro_delta;
334 read_frclock_64(&has_restarted_t2);
335 delta = delta_frclock_64(has_restarted_t1, has_restarted_t2);
336 micro_delta = frclock_64_to_micros(delta);
338 #define DISPLAY_1SEC 1000000 /* 1 second in microseconds */
339 if (micro_delta < DISPLAY_1SEC) {
340 return 1;
343 has_restarted = 0;
344 return 0;
347 static void
348 paint_bootlogo(int minor)
350 paint_centered(minor, bootlogo_data, bootlogo_width, bootlogo_height);
353 static void
354 paint_restartlogo(int minor)
356 paint_centered(minor, restartlogo_data, restartlogo_width,
357 restartlogo_height);
360 static void
361 paint_centered(int minor, char *data, int width, int height)
363 u8_t pixel[3];
364 u32_t i, min_x, min_y, max_x, max_y, x_painted = 0, rows = 0;
365 int r, bytespp;
366 struct device dev;
367 struct fb_var_screeninfo fbvs;
369 /* Put display in a known state to simplify positioning code below */
370 if ((r = arch_get_varscreeninfo(minor, &fbvs)) != OK) {
371 printf("fb: unable to get screen info: %d\n", r);
373 fbvs.yoffset = 0;
374 if ((r = arch_pan_display(minor, &fbvs)) != OK) {
375 printf("fb: unable to pan display: %d\n", r);
378 arch_get_device(minor, &dev);
380 /* Paint on a white canvas */
381 bytespp = fbvs.bits_per_pixel / 8;
382 for (i = 0; i < fbvs.xres * fbvs.yres * bytespp; i+= bytespp)
383 *((u32_t *)((u32_t) dev.dv_base + i)) = 0x00FFFFFF;
385 /* First seek to start */
386 min_x = fbvs.xres / 2 - width / 2;
387 max_x = fbvs.xres / 2 + width / 2;
388 min_y = fbvs.yres / 2 - height / 2;
389 max_y = fbvs.yres / 2 + height / 2;
390 i = min_x * fbvs.xres + min_y;
392 /* Add the image data */
393 for (i = ((min_y * fbvs.xres) + min_x) * bytespp; rows < height;) {
394 GET_PIXEL(data, pixel);
396 ((unsigned char *)((u32_t) dev.dv_base + i))[0] = pixel[2];
397 ((unsigned char *)((u32_t) dev.dv_base + i))[1] = pixel[1];
398 ((unsigned char *)((u32_t) dev.dv_base + i))[2] = pixel[0];
399 ((unsigned char *)((u32_t) dev.dv_base + i))[3] = 0;
401 x_painted++;/* Keep tab of how many row pixels we've painted */
402 if (x_painted == width) {
403 /* We've reached the end of the row, carriage return
404 * and go to next line.
406 x_painted = 0;
407 rows++;
408 i = (((min_y + rows) * fbvs.xres) + min_x) * 4;
409 } else {
410 i += 4;