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