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 /* 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); 48 49 /* Entry points to the fb driver. */ 50 static struct chardriver fb_tab = 51 { 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 57 }; 58 59 /** Represents the /dev/fb device. */ 60 static int has_restarted = 0; 61 static u64_t has_restarted_t1, has_restarted_t2; 62 63 static int open_counter[FB_DEV_NR]; /* Open count */ 64 65 static int 66 fb_open(devminor_t minor, int UNUSED(access), endpoint_t UNUSED(user_endpt)) 67 { 68 int r; 69 static int initialized = 0; 70 static struct edid_info info; 71 static struct edid_info *infop = NULL; 72 73 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO; 74 75 if (!initialized) { 76 r = fb_edid_read(minor, &info); 77 infop = (r == 0) ? &info : NULL; 78 } 79 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); 88 } 89 initialized = 1; 90 } 91 return OK; 92 } 93 return ENXIO; 94 } 95 96 static int 97 fb_close(devminor_t minor) 98 { 99 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO; 100 assert(open_counter[minor] > 0); 101 open_counter[minor]--; 102 return OK; 103 } 104 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)) 108 { 109 struct device dev; 110 int r; 111 112 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO; 113 assert(open_counter[minor] > 0); 114 115 arch_get_device(minor, &dev); 116 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); 120 121 r = sys_safecopyto(ep, gid, 0, (vir_bytes)(dev.dv_base + (size_t)pos), 122 size); 123 124 return (r != OK) ? r : size; 125 } 126 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)) 131 { 132 /* Process I/O control requests */ 133 int r; 134 135 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO; 136 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; 150 } 151 152 return ENOTTY; 153 } 154 155 static int 156 do_get_varscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid) 157 { 158 int r; 159 struct fb_var_screeninfo fbvs; 160 161 if ((r = arch_get_varscreeninfo(minor, &fbvs)) == OK) { 162 r = sys_safecopyto(ep, gid, 0, (vir_bytes) &fbvs, sizeof(fbvs)); 163 } 164 165 return r; 166 } 167 168 static int 169 do_put_varscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid) 170 { 171 int r; 172 struct fb_var_screeninfo fbvs_copy; 173 174 if (has_restarted && keep_displaying_restarted()) { 175 return EAGAIN; 176 } 177 178 if ((r = sys_safecopyfrom(ep, gid, 0, (vir_bytes) &fbvs_copy, 179 sizeof(fbvs_copy))) != OK) { 180 return r; 181 } 182 183 return arch_put_varscreeninfo(minor, &fbvs_copy); 184 } 185 186 static int 187 do_pan_display(int minor, endpoint_t ep, cp_grant_id_t gid) 188 { 189 int r; 190 struct fb_var_screeninfo fbvs_copy; 191 192 if (has_restarted && keep_displaying_restarted()) { 193 return EAGAIN; 194 } 195 196 if ((r = sys_safecopyfrom(ep, gid, 0, (vir_bytes) &fbvs_copy, 197 sizeof(fbvs_copy))) != OK) { 198 return r; 199 } 200 201 return arch_pan_display(minor, &fbvs_copy); 202 } 203 204 static int 205 do_get_fixscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid) 206 { 207 int r; 208 struct fb_fix_screeninfo fbfs; 209 210 if ((r = arch_get_fixscreeninfo(minor, &fbfs)) == OK) { 211 r = sys_safecopyto(ep, gid, 0, (vir_bytes) &fbfs, sizeof(fbfs)); 212 } 213 214 return r; 215 } 216 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)) 220 { 221 struct device dev; 222 int r; 223 224 if (minor < 0 || minor >= FB_DEV_NR) return ENXIO; 225 assert(open_counter[minor] > 0); 226 227 if (has_restarted && keep_displaying_restarted()) 228 return EAGAIN; 229 230 arch_get_device(minor, &dev); 231 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); 235 236 r = sys_safecopyfrom(ep, gid, 0, 237 (vir_bytes)(dev.dv_base + (size_t)pos), size); 238 239 return (r != OK) ? r : size; 240 } 241 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); 246 247 return OK; 248 } 249 250 static int 251 lu_state_restore() { 252 /* Restore the state. */ 253 u32_t value; 254 255 ds_retrieve_u32("open_counter", &value); 256 ds_delete_u32("open_counter"); 257 open_counter[0] = (int) value; 258 259 return OK; 260 } 261 262 static void 263 sef_local_startup() 264 { 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); 269 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); 277 278 /* Let SEF perform startup. */ 279 sef_startup(); 280 } 281 282 static int 283 sef_cb_init(int type, sef_init_info_t *UNUSED(info)) 284 { 285 /* Initialize the fb driver. */ 286 int do_announce_driver = TRUE; 287 288 open_counter[0] = 0; 289 switch(type) { 290 case SEF_INIT_FRESH: 291 printf("framebuffer fresh: pid %d\n", getpid()); 292 break; 293 294 case SEF_INIT_LU: 295 /* Restore the state. */ 296 lu_state_restore(); 297 do_announce_driver = FALSE; 298 299 printf("framebuffer: I'm a new version!\n"); 300 break; 301 302 case SEF_INIT_RESTART: 303 printf("framebuffer restarted: pid %d\n", getpid()); 304 has_restarted = 1; 305 break; 306 } 307 308 /* Announce we are up when necessary. */ 309 if (do_announce_driver) { 310 chardriver_announce(); 311 } 312 313 /* Initialization completed successfully. */ 314 return OK; 315 } 316 317 int 318 main(int argc, char *argv[]) 319 { 320 env_setargs(argc, argv); 321 fb_edid_args_parse(); 322 323 sef_local_startup(); 324 chardriver_task(&fb_tab); 325 return OK; 326 } 327 328 static int 329 keep_displaying_restarted() 330 { 331 u64_t delta; 332 u32_t micro_delta; 333 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); 337 338 #define DISPLAY_1SEC 1000000 /* 1 second in microseconds */ 339 if (micro_delta < DISPLAY_1SEC) { 340 return 1; 341 } 342 343 has_restarted = 0; 344 return 0; 345 } 346 347 static void 348 paint_bootlogo(int minor) 349 { 350 paint_centered(minor, bootlogo_data, bootlogo_width, bootlogo_height); 351 } 352 353 static void 354 paint_restartlogo(int minor) 355 { 356 paint_centered(minor, restartlogo_data, restartlogo_width, 357 restartlogo_height); 358 } 359 360 static void 361 paint_centered(int minor, char *data, int width, int height) 362 { 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; 368 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); 372 } 373 fbvs.yoffset = 0; 374 if ((r = arch_pan_display(minor, &fbvs)) != OK) { 375 printf("fb: unable to pan display: %d\n", r); 376 } 377 378 arch_get_device(minor, &dev); 379 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; 384 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; 391 392 /* Add the image data */ 393 for (i = ((min_y * fbvs.xres) + min_x) * bytespp; rows < height;) { 394 GET_PIXEL(data, pixel); 395 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; 400 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. 405 */ 406 x_painted = 0; 407 rows++; 408 i = (((min_y + rows) * fbvs.xres) + min_x) * 4; 409 } else { 410 i += 4; 411 } 412 } 413 } 414 415