1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini fbset implementation for busybox 4 * 5 * Copyright (C) 1999 by Randolph Chung <tausq@debian.org> 6 * 7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 8 * 9 * This is a from-scratch implementation of fbset; but the de facto fbset 10 * implementation was a good reference. fbset (original) is released under 11 * the GPL, and is (c) 1995-1999 by: 12 * Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be) 13 */ 14 15#include "libbb.h" 16 17#define DEFAULTFBDEV FB_0 18#define DEFAULTFBMODE "/etc/fb.modes" 19 20enum { 21 OPT_CHANGE = (1 << 0), 22 OPT_INFO = (1 << 1), 23 OPT_READMODE = (1 << 2), 24 OPT_ALL = (1 << 9), 25 26 CMD_FB = 1, 27 CMD_DB = 2, 28 CMD_GEOMETRY = 3, 29 CMD_TIMING = 4, 30 CMD_ACCEL = 5, 31 CMD_HSYNC = 6, 32 CMD_VSYNC = 7, 33 CMD_LACED = 8, 34 CMD_DOUBLE = 9, 35/* CMD_XCOMPAT = 10, */ 36 CMD_ALL = 11, 37 CMD_INFO = 12, 38 CMD_CHANGE = 13, 39 40#if ENABLE_FEATURE_FBSET_FANCY 41 CMD_XRES = 100, 42 CMD_YRES = 101, 43 CMD_VXRES = 102, 44 CMD_VYRES = 103, 45 CMD_DEPTH = 104, 46 CMD_MATCH = 105, 47 CMD_PIXCLOCK = 106, 48 CMD_LEFT = 107, 49 CMD_RIGHT = 108, 50 CMD_UPPER = 109, 51 CMD_LOWER = 110, 52 CMD_HSLEN = 111, 53 CMD_VSLEN = 112, 54 CMD_CSYNC = 113, 55 CMD_GSYNC = 114, 56 CMD_EXTSYNC = 115, 57 CMD_BCAST = 116, 58 CMD_RGBA = 117, 59 CMD_STEP = 118, 60 CMD_MOVE = 119, 61#endif 62}; 63 64static unsigned g_options; 65 66/* Stuff stolen from the kernel's fb.h */ 67#define FB_ACTIVATE_ALL 64 68enum { 69 FBIOGET_VSCREENINFO = 0x4600, 70 FBIOPUT_VSCREENINFO = 0x4601 71}; 72struct fb_bitfield { 73 uint32_t offset; /* beginning of bitfield */ 74 uint32_t length; /* length of bitfield */ 75 uint32_t msb_right; /* !=0: Most significant bit is right */ 76}; 77struct fb_var_screeninfo { 78 uint32_t xres; /* visible resolution */ 79 uint32_t yres; 80 uint32_t xres_virtual; /* virtual resolution */ 81 uint32_t yres_virtual; 82 uint32_t xoffset; /* offset from virtual to visible */ 83 uint32_t yoffset; /* resolution */ 84 85 uint32_t bits_per_pixel; 86 uint32_t grayscale; /* !=0 Graylevels instead of colors */ 87 88 struct fb_bitfield red; /* bitfield in fb mem if true color, */ 89 struct fb_bitfield green; /* else only length is significant */ 90 struct fb_bitfield blue; 91 struct fb_bitfield transp; /* transparency */ 92 93 uint32_t nonstd; /* !=0 Non standard pixel format */ 94 95 uint32_t activate; /* see FB_ACTIVATE_x */ 96 97 uint32_t height; /* height of picture in mm */ 98 uint32_t width; /* width of picture in mm */ 99 100 uint32_t accel_flags; /* acceleration flags (hints) */ 101 102 /* Timing: All values in pixclocks, except pixclock (of course) */ 103 uint32_t pixclock; /* pixel clock in ps (pico seconds) */ 104 uint32_t left_margin; /* time from sync to picture */ 105 uint32_t right_margin; /* time from picture to sync */ 106 uint32_t upper_margin; /* time from sync to picture */ 107 uint32_t lower_margin; 108 uint32_t hsync_len; /* length of horizontal sync */ 109 uint32_t vsync_len; /* length of vertical sync */ 110 uint32_t sync; /* see FB_SYNC_x */ 111 uint32_t vmode; /* see FB_VMODE_x */ 112 uint32_t reserved[6]; /* Reserved for future compatibility */ 113}; 114 115 116static const struct cmdoptions_t { 117 const char name[10]; 118 const unsigned char param_count; 119 const unsigned char code; 120} g_cmdoptions[] = { 121 { "-fb", 1, CMD_FB }, 122 { "-db", 1, CMD_DB }, 123 { "-a", 0, CMD_ALL }, 124 { "-i", 0, CMD_INFO }, 125 { "-g", 5, CMD_GEOMETRY }, 126 { "-t", 7, CMD_TIMING }, 127 { "-accel", 1, CMD_ACCEL }, 128 { "-hsync", 1, CMD_HSYNC }, 129 { "-vsync", 1, CMD_VSYNC }, 130 { "-laced", 1, CMD_LACED }, 131 { "-double", 1, CMD_DOUBLE }, 132 { "-n", 0, CMD_CHANGE }, 133#if ENABLE_FEATURE_FBSET_FANCY 134 { "-all", 0, CMD_ALL }, 135 { "-xres", 1, CMD_XRES }, 136 { "-yres", 1, CMD_YRES }, 137 { "-vxres", 1, CMD_VXRES }, 138 { "-vyres", 1, CMD_VYRES }, 139 { "-depth", 1, CMD_DEPTH }, 140 { "-match", 0, CMD_MATCH }, 141 { "-geometry", 5, CMD_GEOMETRY }, 142 { "-pixclock", 1, CMD_PIXCLOCK }, 143 { "-left", 1, CMD_LEFT }, 144 { "-right", 1, CMD_RIGHT }, 145 { "-upper", 1, CMD_UPPER }, 146 { "-lower", 1, CMD_LOWER }, 147 { "-hslen", 1, CMD_HSLEN }, 148 { "-vslen", 1, CMD_VSLEN }, 149 { "-timings", 7, CMD_TIMING }, 150 { "-csync", 1, CMD_CSYNC }, 151 { "-gsync", 1, CMD_GSYNC }, 152 { "-extsync", 1, CMD_EXTSYNC }, 153 { "-bcast", 1, CMD_BCAST }, 154 { "-rgba", 1, CMD_RGBA }, 155 { "-step", 1, CMD_STEP }, 156 { "-move", 1, CMD_MOVE }, 157#endif 158 { "", 0, 0 } 159}; 160 161#if ENABLE_FEATURE_FBSET_READMODE 162/* taken from linux/fb.h */ 163enum { 164 FB_VMODE_INTERLACED = 1, /* interlaced */ 165 FB_VMODE_DOUBLE = 2, /* double scan */ 166 FB_SYNC_HOR_HIGH_ACT = 1, /* horizontal sync high active */ 167 FB_SYNC_VERT_HIGH_ACT = 2, /* vertical sync high active */ 168 FB_SYNC_EXT = 4, /* external sync */ 169 FB_SYNC_COMP_HIGH_ACT = 8 /* composite sync high active */ 170}; 171#endif 172 173static int readmode(struct fb_var_screeninfo *base, const char *fn, 174 const char *mode) 175{ 176#if ENABLE_FEATURE_FBSET_READMODE 177 FILE *f; 178 char buf[256]; 179 char *p = buf; 180 181 f = xfopen(fn, "r"); 182 while (!feof(f)) { 183 fgets(buf, sizeof(buf), f); 184 if (!(p = strstr(buf, "mode ")) && !(p = strstr(buf, "mode\t"))) 185 continue; 186 p += 5; 187 if (!(p = strstr(buf, mode))) 188 continue; 189 p += strlen(mode); 190 if (!isspace(*p) && (*p != 0) && (*p != '"') 191 && (*p != '\r') && (*p != '\n')) 192 continue; /* almost, but not quite */ 193 194 while (!feof(f)) { 195 fgets(buf, sizeof(buf), f); 196 if ((p = strstr(buf, "geometry "))) { 197 p += 9; 198 sscanf(p, "%d %d %d %d %d", 199 &(base->xres), &(base->yres), 200 &(base->xres_virtual), &(base->yres_virtual), 201 &(base->bits_per_pixel)); 202 } else if ((p = strstr(buf, "timings "))) { 203 p += 8; 204 sscanf(p, "%d %d %d %d %d %d %d", 205 &(base->pixclock), 206 &(base->left_margin), &(base->right_margin), 207 &(base->upper_margin), &(base->lower_margin), 208 &(base->hsync_len), &(base->vsync_len)); 209 } else if ((p = strstr(buf, "laced "))) { 210 //p += 6; 211 if (strstr(buf, "false")) { 212 base->vmode &= ~FB_VMODE_INTERLACED; 213 } else { 214 base->vmode |= FB_VMODE_INTERLACED; 215 } 216 } else if ((p = strstr(buf, "double "))) { 217 //p += 7; 218 if (strstr(buf, "false")) { 219 base->vmode &= ~FB_VMODE_DOUBLE; 220 } else { 221 base->vmode |= FB_VMODE_DOUBLE; 222 } 223 } else if ((p = strstr(buf, "vsync "))) { 224 //p += 6; 225 if (strstr(buf, "low")) { 226 base->sync &= ~FB_SYNC_VERT_HIGH_ACT; 227 } else { 228 base->sync |= FB_SYNC_VERT_HIGH_ACT; 229 } 230 } else if ((p = strstr(buf, "hsync "))) { 231 //p += 6; 232 if (strstr(buf, "low")) { 233 base->sync &= ~FB_SYNC_HOR_HIGH_ACT; 234 } else { 235 base->sync |= FB_SYNC_HOR_HIGH_ACT; 236 } 237 } else if ((p = strstr(buf, "csync "))) { 238 //p += 6; 239 if (strstr(buf, "low")) { 240 base->sync &= ~FB_SYNC_COMP_HIGH_ACT; 241 } else { 242 base->sync |= FB_SYNC_COMP_HIGH_ACT; 243 } 244 } else if ((p = strstr(buf, "extsync "))) { 245 //p += 8; 246 if (strstr(buf, "false")) { 247 base->sync &= ~FB_SYNC_EXT; 248 } else { 249 base->sync |= FB_SYNC_EXT; 250 } 251 } 252 253 if (strstr(buf, "endmode")) 254 return 1; 255 } 256 } 257#else 258 bb_error_msg("mode reading not compiled in"); 259#endif 260 return 0; 261} 262 263static inline void setmode(struct fb_var_screeninfo *base, 264 struct fb_var_screeninfo *set) 265{ 266 if ((int) set->xres > 0) 267 base->xres = set->xres; 268 if ((int) set->yres > 0) 269 base->yres = set->yres; 270 if ((int) set->xres_virtual > 0) 271 base->xres_virtual = set->xres_virtual; 272 if ((int) set->yres_virtual > 0) 273 base->yres_virtual = set->yres_virtual; 274 if ((int) set->bits_per_pixel > 0) 275 base->bits_per_pixel = set->bits_per_pixel; 276} 277 278static inline void showmode(struct fb_var_screeninfo *v) 279{ 280 double drate = 0, hrate = 0, vrate = 0; 281 282 if (v->pixclock) { 283 drate = 1e12 / v->pixclock; 284 hrate = drate / (v->left_margin + v->xres + v->right_margin + v->hsync_len); 285 vrate = hrate / (v->upper_margin + v->yres + v->lower_margin + v->vsync_len); 286 } 287 printf("\nmode \"%ux%u-%u\"\n" 288#if ENABLE_FEATURE_FBSET_FANCY 289 "\t# D: %.3f MHz, H: %.3f kHz, V: %.3f Hz\n" 290#endif 291 "\tgeometry %u %u %u %u %u\n" 292 "\ttimings %u %u %u %u %u %u %u\n" 293 "\taccel %s\n" 294 "\trgba %u/%u,%u/%u,%u/%u,%u/%u\n" 295 "endmode\n\n", 296 v->xres, v->yres, (int) (vrate + 0.5), 297#if ENABLE_FEATURE_FBSET_FANCY 298 drate / 1e6, hrate / 1e3, vrate, 299#endif 300 v->xres, v->yres, v->xres_virtual, v->yres_virtual, v->bits_per_pixel, 301 v->pixclock, v->left_margin, v->right_margin, v->upper_margin, v->lower_margin, 302 v->hsync_len, v->vsync_len, 303 (v->accel_flags > 0 ? "true" : "false"), 304 v->red.length, v->red.offset, v->green.length, v->green.offset, 305 v->blue.length, v->blue.offset, v->transp.length, v->transp.offset); 306} 307 308#ifdef STANDALONE 309int main(int argc, char **argv) 310#else 311int fbset_main(int argc, char **argv); 312int fbset_main(int argc, char **argv) 313#endif 314{ 315 struct fb_var_screeninfo var, varset; 316 int fh, i; 317 const char *fbdev = DEFAULTFBDEV; 318 const char *modefile = DEFAULTFBMODE; 319 char *thisarg, *mode = NULL; 320 321 memset(&varset, 0xFF, sizeof(varset)); 322 323 /* parse cmd args.... why do they have to make things so difficult? */ 324 argv++; 325 argc--; 326 for (; argc > 0 && (thisarg = *argv); argc--, argv++) { 327 for (i = 0; g_cmdoptions[i].name[0]; i++) { 328 if (strcmp(thisarg, g_cmdoptions[i].name)) 329 continue; 330 if (argc-1 < g_cmdoptions[i].param_count) 331 bb_show_usage(); 332 333 switch (g_cmdoptions[i].code) { 334 case CMD_FB: 335 fbdev = argv[1]; 336 break; 337 case CMD_DB: 338 modefile = argv[1]; 339 break; 340 case CMD_GEOMETRY: 341 varset.xres = xatou32(argv[1]); 342 varset.yres = xatou32(argv[2]); 343 varset.xres_virtual = xatou32(argv[3]); 344 varset.yres_virtual = xatou32(argv[4]); 345 varset.bits_per_pixel = xatou32(argv[5]); 346 break; 347 case CMD_TIMING: 348 varset.pixclock = xatou32(argv[1]); 349 varset.left_margin = xatou32(argv[2]); 350 varset.right_margin = xatou32(argv[3]); 351 varset.upper_margin = xatou32(argv[4]); 352 varset.lower_margin = xatou32(argv[5]); 353 varset.hsync_len = xatou32(argv[6]); 354 varset.vsync_len = xatou32(argv[7]); 355 break; 356 case CMD_ALL: 357 g_options |= OPT_ALL; 358 break; 359 case CMD_CHANGE: 360 g_options |= OPT_CHANGE; 361 break; 362#if ENABLE_FEATURE_FBSET_FANCY 363 case CMD_XRES: 364 varset.xres = xatou32(argv[1]); 365 break; 366 case CMD_YRES: 367 varset.yres = xatou32(argv[1]); 368 break; 369 case CMD_DEPTH: 370 varset.bits_per_pixel = xatou32(argv[1]); 371 break; 372#endif 373 } 374 argc -= g_cmdoptions[i].param_count; 375 argv += g_cmdoptions[i].param_count; 376 break; 377 } 378 if (!g_cmdoptions[i].name[0]) { 379 if (argc != 1) 380 bb_show_usage(); 381 mode = *argv; 382 g_options |= OPT_READMODE; 383 } 384 } 385 386 fh = xopen(fbdev, O_RDONLY); 387 xioctl(fh, FBIOGET_VSCREENINFO, &var); 388 if (g_options & OPT_READMODE) { 389 if (!readmode(&var, modefile, mode)) { 390 bb_error_msg_and_die("unknown video mode '%s'", mode); 391 } 392 } 393 394 setmode(&var, &varset); 395 if (g_options & OPT_CHANGE) { 396 if (g_options & OPT_ALL) 397 var.activate = FB_ACTIVATE_ALL; 398 xioctl(fh, FBIOPUT_VSCREENINFO, &var); 399 } 400 showmode(&var); 401 /* Don't close the file, as exiting will take care of that */ 402 /* close(fh); */ 403 404 return EXIT_SUCCESS; 405} 406