Update menuconfig items with approximate applet sizes
[oweals/busybox.git] / util-linux / fbset.c
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 source tree.
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 //config:config FBSET
15 //config:       bool "fbset (5.8 kb)"
16 //config:       default y
17 //config:       select PLATFORM_LINUX
18 //config:       help
19 //config:         fbset is used to show or change the settings of a Linux frame buffer
20 //config:         device. The frame buffer device provides a simple and unique
21 //config:         interface to access a graphics display. Enable this option
22 //config:         if you wish to enable the 'fbset' utility.
23 //config:
24 //config:config FEATURE_FBSET_FANCY
25 //config:       bool "Enable extra options"
26 //config:       default y
27 //config:       depends on FBSET
28 //config:       help
29 //config:         This option enables extended fbset options, allowing one to set the
30 //config:         framebuffer size, color depth, etc. interface to access a graphics
31 //config:         display. Enable this option if you wish to enable extended fbset
32 //config:         options.
33 //config:
34 //config:config FEATURE_FBSET_READMODE
35 //config:       bool "Enable readmode support"
36 //config:       default y
37 //config:       depends on FBSET
38 //config:       help
39 //config:         This option allows fbset to read the video mode database stored by
40 //config:         default as /etc/fb.modes, which can be used to set frame buffer
41 //config:         device to pre-defined video modes.
42
43 //applet:IF_FBSET(APPLET(fbset, BB_DIR_USR_SBIN, BB_SUID_DROP))
44
45 //kbuild:lib-$(CONFIG_FBSET) += fbset.o
46
47 //usage:#define fbset_trivial_usage
48 //usage:       "[OPTIONS] [MODE]"
49 //usage:#define fbset_full_usage "\n\n"
50 //usage:       "Show and modify frame buffer settings"
51 //usage:
52 //usage:#define fbset_example_usage
53 //usage:       "$ fbset\n"
54 //usage:       "mode \"1024x768-76\"\n"
55 //usage:       "        # D: 78.653 MHz, H: 59.949 kHz, V: 75.694 Hz\n"
56 //usage:       "        geometry 1024 768 1024 768 16\n"
57 //usage:       "        timings 12714 128 32 16 4 128 4\n"
58 //usage:       "        accel false\n"
59 //usage:       "        rgba 5/11,6/5,5/0,0/0\n"
60 //usage:       "endmode\n"
61
62 #include "libbb.h"
63
64 #define DEFAULTFBDEV  FB_0
65 #define DEFAULTFBMODE "/etc/fb.modes"
66
67 /* Stuff stolen from the kernel's fb.h */
68 #define FB_ACTIVATE_ALL 64
69 enum {
70         FBIOGET_VSCREENINFO = 0x4600,
71         FBIOPUT_VSCREENINFO = 0x4601
72 };
73
74 struct fb_bitfield {
75         uint32_t offset;                /* beginning of bitfield */
76         uint32_t length;                /* length of bitfield */
77         uint32_t msb_right;             /* !=0: Most significant bit is right */
78 };
79 struct fb_var_screeninfo {
80         uint32_t xres;                  /* visible resolution */
81         uint32_t yres;
82         uint32_t xres_virtual;          /* virtual resolution */
83         uint32_t yres_virtual;
84         uint32_t xoffset;               /* offset from virtual to visible */
85         uint32_t yoffset;               /* resolution */
86
87         uint32_t bits_per_pixel;
88         uint32_t grayscale;             /* !=0 Graylevels instead of colors */
89
90         struct fb_bitfield red;         /* bitfield in fb mem if true color, */
91         struct fb_bitfield green;       /* else only length is significant */
92         struct fb_bitfield blue;
93         struct fb_bitfield transp;      /* transparency */
94
95         uint32_t nonstd;                /* !=0 Non standard pixel format */
96
97         uint32_t activate;              /* see FB_ACTIVATE_x */
98
99         uint32_t height;                /* height of picture in mm */
100         uint32_t width;                 /* width of picture in mm */
101
102         uint32_t accel_flags;           /* acceleration flags (hints) */
103
104         /* Timing: All values in pixclocks, except pixclock (of course) */
105         uint32_t pixclock;              /* pixel clock in ps (pico seconds) */
106         uint32_t left_margin;           /* time from sync to picture */
107         uint32_t right_margin;          /* time from picture to sync */
108         uint32_t upper_margin;          /* time from sync to picture */
109         uint32_t lower_margin;
110         uint32_t hsync_len;             /* length of horizontal sync */
111         uint32_t vsync_len;             /* length of vertical sync */
112         uint32_t sync;                  /* see FB_SYNC_x */
113         uint32_t vmode;                 /* see FB_VMODE_x */
114         uint32_t reserved[6];           /* Reserved for future compatibility */
115 };
116
117 static void copy_if_gt0(uint32_t *src, uint32_t *dst, unsigned cnt)
118 {
119         do {
120                 if ((int32_t) *src > 0)
121                         *dst = *src;
122                 src++;
123                 dst++;
124         } while (--cnt);
125 }
126
127 static NOINLINE void copy_changed_values(
128                 struct fb_var_screeninfo *base,
129                 struct fb_var_screeninfo *set)
130 {
131         //if ((int32_t) set->xres > 0) base->xres = set->xres;
132         //if ((int32_t) set->yres > 0) base->yres = set->yres;
133         //if ((int32_t) set->xres_virtual > 0)   base->xres_virtual = set->xres_virtual;
134         //if ((int32_t) set->yres_virtual > 0)   base->yres_virtual = set->yres_virtual;
135         copy_if_gt0(&set->xres, &base->xres, 4);
136
137         if ((int32_t) set->bits_per_pixel > 0) base->bits_per_pixel = set->bits_per_pixel;
138         //copy_if_gt0(&set->bits_per_pixel, &base->bits_per_pixel, 1);
139
140         //if ((int32_t) set->pixclock > 0)       base->pixclock = set->pixclock;
141         //if ((int32_t) set->left_margin > 0)    base->left_margin = set->left_margin;
142         //if ((int32_t) set->right_margin > 0)   base->right_margin = set->right_margin;
143         //if ((int32_t) set->upper_margin > 0)   base->upper_margin = set->upper_margin;
144         //if ((int32_t) set->lower_margin > 0)   base->lower_margin = set->lower_margin;
145         //if ((int32_t) set->hsync_len > 0) base->hsync_len = set->hsync_len;
146         //if ((int32_t) set->vsync_len > 0) base->vsync_len = set->vsync_len;
147         //if ((int32_t) set->sync > 0)  base->sync = set->sync;
148         //if ((int32_t) set->vmode > 0) base->vmode = set->vmode;
149         copy_if_gt0(&set->pixclock, &base->pixclock, 9);
150 }
151
152
153 enum {
154         CMD_FB = 1,
155         CMD_DB = 2,
156         CMD_GEOMETRY = 3,
157         CMD_TIMING = 4,
158         CMD_ACCEL = 5,
159         CMD_HSYNC = 6,
160         CMD_VSYNC = 7,
161         CMD_LACED = 8,
162         CMD_DOUBLE = 9,
163 /*      CMD_XCOMPAT =     10, */
164         CMD_ALL = 11,
165         CMD_INFO = 12,
166         CMD_SHOW = 13,
167         CMD_CHANGE = 14,
168
169 #if ENABLE_FEATURE_FBSET_FANCY
170         CMD_XRES = 100,
171         CMD_YRES = 101,
172         CMD_VXRES = 102,
173         CMD_VYRES = 103,
174         CMD_DEPTH = 104,
175         CMD_MATCH = 105,
176         CMD_PIXCLOCK = 106,
177         CMD_LEFT = 107,
178         CMD_RIGHT = 108,
179         CMD_UPPER = 109,
180         CMD_LOWER = 110,
181         CMD_HSLEN = 111,
182         CMD_VSLEN = 112,
183         CMD_CSYNC = 113,
184         CMD_GSYNC = 114,
185         CMD_EXTSYNC = 115,
186         CMD_BCAST = 116,
187         CMD_RGBA = 117,
188         CMD_STEP = 118,
189         CMD_MOVE = 119,
190 #endif
191 };
192
193 static const struct cmdoptions_t {
194         const char name[9];
195         const unsigned char param_count;
196         const unsigned char code;
197 } g_cmdoptions[] = {
198         /*"12345678" + NUL */
199 //TODO: convert to index_in_strings()
200         { "fb"      , 1, CMD_FB       },
201         { "db"      , 1, CMD_DB       },
202         { "a"       , 0, CMD_ALL      },
203         { "i"       , 0, CMD_INFO     },
204         { "g"       , 5, CMD_GEOMETRY },
205         { "t"       , 7, CMD_TIMING   },
206         { "accel"   , 1, CMD_ACCEL    },
207         { "hsync"   , 1, CMD_HSYNC    },
208         { "vsync"   , 1, CMD_VSYNC    },
209         { "laced"   , 1, CMD_LACED    },
210         { "double"  , 1, CMD_DOUBLE   },
211         { "show"    , 0, CMD_SHOW     },
212         { "s"       , 0, CMD_SHOW     },
213 #if ENABLE_FEATURE_FBSET_FANCY
214         { "all"     , 0, CMD_ALL      },
215         { "xres"    , 1, CMD_XRES     },
216         { "yres"    , 1, CMD_YRES     },
217         { "vxres"   , 1, CMD_VXRES    },
218         { "vyres"   , 1, CMD_VYRES    },
219         { "depth"   , 1, CMD_DEPTH    },
220         { "match"   , 0, CMD_MATCH    },
221         { "geometry", 5, CMD_GEOMETRY },
222         { "pixclock", 1, CMD_PIXCLOCK },
223         { "left"    , 1, CMD_LEFT     },
224         { "right"   , 1, CMD_RIGHT    },
225         { "upper"   , 1, CMD_UPPER    },
226         { "lower"   , 1, CMD_LOWER    },
227         { "hslen"   , 1, CMD_HSLEN    },
228         { "vslen"   , 1, CMD_VSLEN    },
229         { "timings" , 7, CMD_TIMING   },
230         { "csync"   , 1, CMD_CSYNC    },
231         { "gsync"   , 1, CMD_GSYNC    },
232         { "extsync" , 1, CMD_EXTSYNC  },
233         { "bcast"   , 1, CMD_BCAST    },
234         { "rgba"    , 1, CMD_RGBA     },
235         { "step"    , 1, CMD_STEP     },
236         { "move"    , 1, CMD_MOVE     },
237 #endif
238 };
239
240 /* taken from linux/fb.h */
241 enum {
242         FB_SYNC_HOR_HIGH_ACT = 1,       /* horizontal sync high active */
243         FB_SYNC_VERT_HIGH_ACT = 2,      /* vertical sync high active */
244 #if ENABLE_FEATURE_FBSET_READMODE
245         FB_VMODE_INTERLACED = 1,        /* interlaced */
246         FB_VMODE_DOUBLE = 2,            /* double scan */
247         FB_SYNC_EXT = 4,                /* external sync */
248         FB_SYNC_COMP_HIGH_ACT = 8,      /* composite sync high active */
249 #endif
250 };
251
252 #if ENABLE_FEATURE_FBSET_READMODE
253 static void ss(uint32_t *x, uint32_t flag, char *buf, const char *what)
254 {
255         if (strcmp(buf, what) == 0)
256                 *x &= ~flag;
257         else
258                 *x |= flag;
259 }
260
261 /* Mode db file contains mode definitions like this:
262  * mode "800x600-48-lace"
263  *     # D: 36.00 MHz, H: 33.835 kHz, V: 96.39 Hz
264  *     geometry 800 600 800 600 8
265  *     timings 27778 56 80 79 11 128 12
266  *     laced true
267  *     hsync high
268  *     vsync high
269  * endmode
270  */
271 static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
272                                         const char *mode)
273 {
274         char *token[2], *p, *s;
275         parser_t *parser = config_open(fn);
276
277         while (config_read(parser, token, 2, 1, "# \t\r", PARSE_NORMAL)) {
278                 if (strcmp(token[0], "mode") != 0 || !token[1])
279                         continue;
280                 p = strstr(token[1], mode);
281                 if (!p)
282                         continue;
283                 s = p + strlen(mode);
284                 //bb_error_msg("CHECK[%s][%s][%d]", mode, p-1, *s);
285                 /* exact match? */
286                 if (((!*s || isspace(*s)) && '"' != s[-1]) /* end-of-token */
287                  || ('"' == *s && '"' == p[-1]) /* ends with " but starts with " too! */
288                 ) {
289                         //bb_error_msg("FOUND[%s][%s][%s][%d]", token[1], p, mode, isspace(*s));
290                         break;
291                 }
292         }
293
294         if (!token[0])
295                 return 0;
296
297         while (config_read(parser, token, 2, 1, "# \t", PARSE_NORMAL)) {
298                 int i;
299
300 //bb_error_msg("???[%s][%s]", token[0], token[1]);
301                 if (strcmp(token[0], "endmode") == 0) {
302 //bb_error_msg("OK[%s]", mode);
303                         return 1;
304                 }
305                 p = token[1];
306                 i = index_in_strings(
307                         "geometry\0timings\0interlaced\0double\0vsync\0hsync\0csync\0extsync\0rgba\0",
308                         token[0]);
309                 switch (i) {
310                 case 0:
311                         if (sizeof(int) == sizeof(base->xres)) {
312                                 sscanf(p, "%d %d %d %d %d",
313                                         &base->xres, &base->yres,
314                                         &base->xres_virtual, &base->yres_virtual,
315                                         &base->bits_per_pixel);
316                         } else {
317                                 int base_xres, base_yres;
318                                 int base_xres_virtual, base_yres_virtual;
319                                 int base_bits_per_pixel;
320                                 sscanf(p, "%d %d %d %d %d",
321                                         &base_xres, &base_yres,
322                                         &base_xres_virtual, &base_yres_virtual,
323                                         &base_bits_per_pixel);
324                                 base->xres = base_xres;
325                                 base->yres = base_yres;
326                                 base->xres_virtual = base_xres_virtual;
327                                 base->yres_virtual = base_yres_virtual;
328                                 base->bits_per_pixel = base_bits_per_pixel;
329                         }
330 //bb_error_msg("GEO[%s]", p);
331                         break;
332                 case 1:
333                         if (sizeof(int) == sizeof(base->xres)) {
334                                 sscanf(p, "%d %d %d %d %d %d %d",
335                                         &base->pixclock,
336                                         &base->left_margin, &base->right_margin,
337                                         &base->upper_margin, &base->lower_margin,
338                                         &base->hsync_len, &base->vsync_len);
339                         } else {
340                                 int base_pixclock;
341                                 int base_left_margin, base_right_margin;
342                                 int base_upper_margin, base_lower_margin;
343                                 int base_hsync_len, base_vsync_len;
344                                 sscanf(p, "%d %d %d %d %d %d %d",
345                                         &base_pixclock,
346                                         &base_left_margin, &base_right_margin,
347                                         &base_upper_margin, &base_lower_margin,
348                                         &base_hsync_len, &base_vsync_len);
349                                 base->pixclock = base_pixclock;
350                                 base->left_margin = base_left_margin;
351                                 base->right_margin = base_right_margin;
352                                 base->upper_margin = base_upper_margin;
353                                 base->lower_margin = base_lower_margin;
354                                 base->hsync_len = base_hsync_len;
355                                 base->vsync_len = base_vsync_len;
356                         }
357 //bb_error_msg("TIM[%s]", p);
358                         break;
359                 case 2:
360                 case 3: {
361                         static const uint32_t syncs[] = {FB_VMODE_INTERLACED, FB_VMODE_DOUBLE};
362                         ss(&base->vmode, syncs[i-2], p, "false");
363 //bb_error_msg("VMODE[%s]", p);
364                         break;
365                 }
366                 case 4:
367                 case 5:
368                 case 6: {
369                         static const uint32_t syncs[] = {FB_SYNC_VERT_HIGH_ACT, FB_SYNC_HOR_HIGH_ACT, FB_SYNC_COMP_HIGH_ACT};
370                         ss(&base->sync, syncs[i-4], p, "low");
371 //bb_error_msg("SYNC[%s]", p);
372                         break;
373                 }
374                 case 7:
375                         ss(&base->sync, FB_SYNC_EXT, p, "false");
376 //bb_error_msg("EXTSYNC[%s]", p);
377                         break;
378                 case 8: {
379                         int red_offset, red_length;
380                         int green_offset, green_length;
381                         int blue_offset, blue_length;
382                         int transp_offset, transp_length;
383
384                         sscanf(p, "%d/%d,%d/%d,%d/%d,%d/%d",
385                                 &red_length, &red_offset,
386                                 &green_length, &green_offset,
387                                 &blue_length, &blue_offset,
388                                 &transp_length, &transp_offset);
389                         base->red.offset = red_offset;
390                         base->red.length = red_length;
391                         base->red.msb_right = 0;
392                         base->green.offset = green_offset;
393                         base->green.length = green_length;
394                         base->green.msb_right = 0;
395                         base->blue.offset = blue_offset;
396                         base->blue.length = blue_length;
397                         base->blue.msb_right = 0;
398                         base->transp.offset = transp_offset;
399                         base->transp.length = transp_length;
400                         base->transp.msb_right = 0;
401                 }
402                 }
403         }
404         return 0;
405 }
406 #endif
407
408 static NOINLINE void showmode(struct fb_var_screeninfo *v)
409 {
410         double drate = 0, hrate = 0, vrate = 0;
411
412         if (v->pixclock) {
413                 drate = 1e12 / v->pixclock;
414                 hrate = drate / (v->left_margin + v->xres + v->right_margin + v->hsync_len);
415                 vrate = hrate / (v->upper_margin + v->yres + v->lower_margin + v->vsync_len);
416         }
417         printf("\nmode \"%ux%u-%u\"\n"
418 #if ENABLE_FEATURE_FBSET_FANCY
419         "\t# D: %.3f MHz, H: %.3f kHz, V: %.3f Hz\n"
420 #endif
421         "\tgeometry %u %u %u %u %u\n"
422         "\ttimings %u %u %u %u %u %u %u\n"
423         "\taccel %s\n"
424         "\trgba %u/%u,%u/%u,%u/%u,%u/%u\n"
425         "endmode\n\n",
426                 v->xres, v->yres, (int) (vrate + 0.5),
427 #if ENABLE_FEATURE_FBSET_FANCY
428                 drate / 1e6, hrate / 1e3, vrate,
429 #endif
430                 v->xres, v->yres, v->xres_virtual, v->yres_virtual, v->bits_per_pixel,
431                 v->pixclock, v->left_margin, v->right_margin, v->upper_margin, v->lower_margin,
432                         v->hsync_len, v->vsync_len,
433                 (v->accel_flags > 0 ? "true" : "false"),
434                 v->red.length, v->red.offset, v->green.length, v->green.offset,
435                         v->blue.length, v->blue.offset, v->transp.length, v->transp.offset);
436 }
437
438 int fbset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
439 int fbset_main(int argc, char **argv)
440 {
441         enum {
442                 OPT_CHANGE   = (1 << 0),
443                 OPT_SHOW     = (1 << 1),
444                 OPT_READMODE = (1 << 2),
445                 OPT_ALL      = (1 << 3),
446         };
447         struct fb_var_screeninfo var_old, var_set;
448         int fh, i;
449         unsigned options = 0;
450
451         const char *fbdev = DEFAULTFBDEV;
452         IF_FEATURE_FBSET_READMODE(const char *modefile = DEFAULTFBMODE;)
453         char *thisarg;
454         char *mode = mode; /* for compiler */
455
456         memset(&var_set, 0xff, sizeof(var_set)); /* set all to -1 */
457
458         /* parse cmd args.... why do they have to make things so difficult? */
459         argv++;
460         argc--;
461         for (; argc > 0 && (thisarg = *argv) != NULL; argc--, argv++) {
462                 if (thisarg[0] != '-') {
463                         if (!ENABLE_FEATURE_FBSET_READMODE || argc != 1)
464                                 bb_show_usage();
465                         mode = thisarg;
466                         options |= OPT_READMODE;
467                         goto contin;
468                 }
469                 for (i = 0; i < ARRAY_SIZE(g_cmdoptions); i++) {
470                         if (strcmp(thisarg + 1, g_cmdoptions[i].name) != 0)
471                                 continue;
472                         if (argc <= g_cmdoptions[i].param_count)
473                                 bb_show_usage();
474
475                         switch (g_cmdoptions[i].code) {
476                         case CMD_FB:
477                                 fbdev = argv[1];
478                                 break;
479                         case CMD_DB:
480                                 IF_FEATURE_FBSET_READMODE(modefile = argv[1];)
481                                 break;
482                         case CMD_ALL:
483                                 options |= OPT_ALL;
484                                 break;
485                         case CMD_SHOW:
486                                 options |= OPT_SHOW;
487                                 break;
488                         case CMD_GEOMETRY:
489                                 var_set.xres = xatou32(argv[1]);
490                                 var_set.yres = xatou32(argv[2]);
491                                 var_set.xres_virtual = xatou32(argv[3]);
492                                 var_set.yres_virtual = xatou32(argv[4]);
493                                 var_set.bits_per_pixel = xatou32(argv[5]);
494                                 break;
495                         case CMD_TIMING:
496                                 var_set.pixclock = xatou32(argv[1]);
497                                 var_set.left_margin = xatou32(argv[2]);
498                                 var_set.right_margin = xatou32(argv[3]);
499                                 var_set.upper_margin = xatou32(argv[4]);
500                                 var_set.lower_margin = xatou32(argv[5]);
501                                 var_set.hsync_len = xatou32(argv[6]);
502                                 var_set.vsync_len = xatou32(argv[7]);
503                                 break;
504                         case CMD_ACCEL:
505                                 break;
506                         case CMD_HSYNC:
507                                 var_set.sync |= FB_SYNC_HOR_HIGH_ACT;
508                                 break;
509                         case CMD_VSYNC:
510                                 var_set.sync |= FB_SYNC_VERT_HIGH_ACT;
511                                 break;
512 #if ENABLE_FEATURE_FBSET_FANCY
513                         case CMD_XRES:
514                                 var_set.xres = xatou32(argv[1]);
515                                 break;
516                         case CMD_YRES:
517                                 var_set.yres = xatou32(argv[1]);
518                                 break;
519                         case CMD_DEPTH:
520                                 var_set.bits_per_pixel = xatou32(argv[1]);
521                                 break;
522 #endif
523                         }
524                         switch (g_cmdoptions[i].code) {
525                         case CMD_FB:
526                         case CMD_DB:
527                         case CMD_ALL:
528                         case CMD_SHOW:
529                                 break;
530                         default:
531                                 /* other commands imply changes */
532                                 options |= OPT_CHANGE;
533                         }
534                         argc -= g_cmdoptions[i].param_count;
535                         argv += g_cmdoptions[i].param_count;
536                         goto contin;
537                 }
538                 bb_show_usage();
539  contin: ;
540         }
541
542         fh = xopen(fbdev, O_RDONLY);
543         xioctl(fh, FBIOGET_VSCREENINFO, &var_old);
544
545         if (options & OPT_READMODE) {
546 #if ENABLE_FEATURE_FBSET_READMODE
547                 if (!read_mode_db(&var_old, modefile, mode)) {
548                         bb_error_msg_and_die("unknown video mode '%s'", mode);
549                 }
550                 options |= OPT_CHANGE;
551 #endif
552         }
553
554         if (options & OPT_CHANGE) {
555                 copy_changed_values(&var_old, &var_set);
556                 if (options & OPT_ALL)
557                         var_old.activate = FB_ACTIVATE_ALL;
558                 xioctl(fh, FBIOPUT_VSCREENINFO, &var_old);
559         }
560
561         if (options == 0 || (options & OPT_SHOW))
562                 showmode(&var_old);
563
564         if (ENABLE_FEATURE_CLEAN_UP)
565                 close(fh);
566
567         return EXIT_SUCCESS;
568 }