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