]> mj.ucw.cz Git - misc.git/blob - edid.c
hidtest: cleanup
[misc.git] / edid.c
1 /*
2  *      Parse VESA Extended Display Identification Data
3  *
4  *      (c) 2011 Martin Mares <mj@ucw.cz>
5  *
6  *      This program can be distributed and used under the terms
7  *      of the GNU General Public License version 2 or later.
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stdarg.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 typedef unsigned int uns;
17 typedef unsigned char byte;
18
19 static byte edid[128];
20 static int version;
21
22 static void
23 #ifdef __GNUC__
24         __attribute__((format(printf,1,2)))
25         __attribute__((noreturn))
26 #endif
27 die(char *msg, ...)
28 {
29   va_list args;
30   va_start(args, msg);
31   fprintf(stderr, "edid: ");
32   vfprintf(stderr, msg, args);
33   fputc('\n', stderr);
34   va_end(args);
35   exit(1);
36 }
37
38 static uns
39 u16_le(byte *p)
40 {
41   return p[0] | (p[1] << 8);
42 }
43
44 static uns
45 u32_le(byte *p)
46 {
47   return u16_le(p) | (u16_le(p+2) << 16);
48 }
49
50 static uns
51 B(uns x)
52 {
53   return x ? '+' : '-';
54 }
55
56 static void
57 show_model(byte *p)
58 {
59   printf("Model: %c%c%c %04x #%u\n",
60         ('A' - 1 + ((p[0] >> 2) & 0x1f)),
61         ('A' - 1 + ((p[0] & 3) << 3) | ((p[1] & 0xe0) >> 5)),
62         ('A' - 1 + (p[1] & 0x1f)),
63         u16_le(p+2),
64         u32_le(p+4));
65   printf("Manufactured: %d week %d\n", p[9] + 1990, p[8]);
66 }
67
68 static void
69 show_basic(byte *p)
70 {
71   uns input = p[0];
72   if (input & 0x80)
73     printf("Input: Digital DFP%c\n", B(input & 1));
74   else
75     printf("Input: Analog SigLev=%d Setup%c SepSync%c CompSync%c GreenSync%c SerrSync%c\n",
76         ((input >> 5) & 3),
77         B(input & 0x10),
78         B(input & 0x08),
79         B(input & 0x04),
80         B(input & 0x02),
81         B(input & 0x01));
82
83   printf("Image size: %dx%dcm\n", p[1], p[2]);
84   if (p[3] == 0xff)
85     printf("Gamma: not given\n");
86   else
87     printf("Gamma: %d.%02d\n", (p[3] / 100) + 1, p[3] % 100);
88
89   uns flags = p[4];
90   static const char * const types[4] = { "Mono/Gray", "RGB", "NonRGB", "Unknown" };
91   printf("Flags: Suspend%c Standby%c ActiveOff%c Colors=%s HasSRGB%c PrefTiming%c GTF%c\n",
92         B(flags & 0x80),
93         B(flags & 0x40),
94         B(flags & 0x20),
95         types[(flags >> 3) & 3],
96         B(flags & 0x04),
97         B(flags & 0x02),
98         B(flags & 0x01));
99 }
100
101 static void
102 show_color(byte *p)
103 {
104   uns rx = 4*p[2] | ((p[0] >> 6) & 3);
105   uns ry = 4*p[3] | ((p[0] >> 4) & 3);
106   uns gx = 4*p[4] | ((p[0] >> 2) & 3);
107   uns gy = 4*p[5] | ((p[0]     ) & 3);
108   uns bx = 4*p[5] | ((p[1] >> 6) & 3);
109   uns by = 4*p[7] | ((p[1] >> 4) & 3);
110   uns wx = 4*p[8] | ((p[1] >> 2) & 3);
111   uns wy = 4*p[9] | ((p[1]     ) & 3);
112 #define C(c) ((double)c / 1024)
113   printf("Chromaticity: R=(%.3f,%.3f) G=(%.3f,%.3f) B=(%.3f,%.3f) W=(%.3f,%.3f)\n",
114         C(rx), C(ry), C(gx), C(gy), C(bx), C(by), C(wx), C(wy));
115 #undef C
116 }
117
118 static void
119 show_timings(byte *p)
120 {
121   static const char * const estab_timings[24] = {
122         "720x400@70",
123         "720x400@88",
124         "640x480@60",
125         "640x480@67",
126         "640x480@72",
127         "640x480@75",
128         "800x600@56",
129         "800x600@60",
130         "800x600@72",
131         "800x600@75",
132         "832x624@75",
133         "1024x768@87i",
134         "1024x768@60",
135         "1024x768@70",
136         "1024x768@75",
137         "1280x1024@75",
138         "MFG7",
139         "MFG6",
140         "MFG5",
141         "MFG4",
142         "MFG3",
143         "MFG2",
144         "MFG1",
145         "MFG0",
146   };
147   printf("Basic timings:");
148   for (int i=0; i<24; i++)
149     if (p[i/8] & (0x80 >> (i%8)))
150       printf(" %s", estab_timings[i]);
151   printf("\n");
152
153   printf("Extended timings:");
154   for (int i=0; i<8; i++)
155     {
156       byte *q = p + 3 + 2*i;
157       if (q[0] == 1 && q[1] == 1)
158         continue;
159       uns h = (31 + q[0]) * 8;
160       uns v;
161       switch (q[1] >> 6)
162         {
163         case 0:
164           if (version < 0x0103)
165             v = h;
166           else
167             v = 10*h/16;
168           break;
169         case 1:
170           v = 3*h/4;
171           break;
172         case 2:
173           v = 4*h/5;
174           break;
175         default:
176           v = 9*h/16;
177           break;
178         }
179       printf(" %dx%d@%d", h, v, 60 + (q[1] & 0x3f));
180     }
181   printf("\n");
182 }
183
184 static void
185 show_detailed_timing(byte *p)
186 {
187   uns hactive = p[2] | ((p[4] << 4) & 0xf00);
188   uns hblank  = p[3] | ((p[4] << 8) & 0xf00);
189   uns vactive = p[5] | ((p[7] << 4) & 0xf00);
190   uns vblank  = p[6] | ((p[7] << 8) & 0xf00);
191   uns hs_offset = p[8] | ((p[11] << 2) & 0x300);
192   uns hs_width  = p[9] | ((p[11] << 4) & 0x300);
193   uns vs_offset = (p[10] >> 4) | ((p[11] << 2) & 0x30);
194   uns vs_width  = (p[10] & 0xf) | ((p[11] << 4) & 0x30);
195   uns hsize   = p[12] | ((p[14] << 4) & 0xf00);
196   uns vsize   = p[13] | ((p[14] << 8) & 0xf00);
197   uns hborder = p[15];
198   uns vborder = p[16];
199
200   printf("Detailed timing: %dx%d (%.1f Hz)\n",
201         hactive, vactive,
202         u16_le(p) * 10000. / (hactive+hblank) / (vactive+vblank));
203   printf("\tPixClk: %.2fMHz\n", u16_le(p) / 100.);
204   printf("\tH: Active=%d Blank=%d SyncOffset=%d SyncWidth=%d Border=%d\n",
205         hactive, hblank, hs_offset, hs_width, hborder);
206   printf("\tV: Active=%d Blank=%d SyncOffset=%d SyncWidth=%d Border=%d\n",
207         vactive, vblank, vs_offset, vs_width, vborder);
208   printf("\tSize: %dx%dmm\n", hsize, vsize);
209
210   uns flags = p[17];
211   uns stereo = ((flags >> 4) & 6) | (flags & 1);
212   uns input = (flags >> 3) & 3;
213   if (stereo >= 2)
214     printf("\tStereo mode: #%d\n", stereo);
215
216   static const char * const input_types[4] = { "AnalogComposite", "BipolarAnalogComposite", "DigitalComposite", "DigitalSeparate" };
217   printf("\tInput: %s", input_types[input]);
218   if (input < 3)
219     printf(" Serrate%c", B(flags & 4));
220   else
221     printf(" VPolarity%c", B(flags & 4));
222   if (input < 2)
223     printf(" OnRGB%c", B(flags & 2));
224   else if (input == 2)
225     printf(" CompPolarity%c", B(flags & 2));
226   else
227     printf(" HPolarity%c", B(flags & 2));
228   printf("\n");
229
230   if (hsize && vsize)
231     printf("\tCalculated DPI: %dx%d\n",
232         (int)(hactive / (hsize / 25.4)),
233         (int)(vactive / (vsize / 25.4)));
234 }
235
236 static void
237 show_ascii(char *field, byte *p, uns len)
238 {
239   printf("%s: ", field);
240   while (len--)
241     {
242       if (*p == '\n')
243         break;
244       else if (*p >= 0x20 && *p <= 0x7e)
245         putchar(*p);
246       else
247         printf("<%02x>", *p);
248       p++;
249     }
250   putchar('\n');
251 }
252
253 static void
254 show_descriptor(byte *p)
255 {
256   switch (p[3])
257     {
258     case 0xff:          // Serial number
259       show_ascii("Serial", p+5, 13);
260       break;
261     case 0xfe:          // ASCII data
262       show_ascii("Comment", p+5, 13);
263       break;
264     case 0xfd:          // Range limits
265       printf("Limits: Vert=%d-%dHz Horiz=%d-%dkHz PixClk=%dMHz\n",
266         p[5], p[6], p[7], p[8], p[9]*10);
267       switch (p[10])
268         {
269         case 0:         // No secondary timing formula
270           break;
271         case 2:         // Secondary GTF
272           printf("Secondary GTF parameters: fStart=%dkHz C=%.1f M=%d K=%d J=%.1f\n",
273             p[12]*2, p[13]/2., u16_le(p+14), p[16], p[17]/2.);
274           break;
275         default:
276           printf("Unidentified secondary timing formula #%02x\n", p[10]);
277         }
278       break;
279     case 0xfc:          // Monitor name
280       show_ascii("Monitor name", p+5, 13);
281       break;
282     case 0xfb:          // Color point
283       printf("Color point: [...]\n");
284       break;
285     case 0xfa:          // More standard timings
286       printf("More standard timings: [...]\n");
287       break;
288     case 0x10:          // Dummy descriptor -- entry unused
289       return;
290     default:
291       printf("Unknown descriptor type %02x\n", p[3]);
292     }
293 }
294
295 static void
296 show_edid(void)
297 {
298   if (memcmp(edid, "\000\377\377\377\377\377\377\000", 8))
299     die("Invalid EDID signature, giving up");
300   int ver = edid[0x12];
301   int rev = edid[0x13];
302   printf("EDID %d.%d\n", ver, rev);
303   version = (ver << 8) | rev;
304
305   byte sum = 0;
306   for (int i=0; i<128; i++)
307     sum += edid[i];
308   if (sum)
309     printf("Invalid checksum: off by %02x\n", sum);
310
311   show_model(edid + 8);
312   show_basic(edid + 0x14);
313   show_color(edid + 0x19);
314   show_timings(edid + 0x23);
315   for (int i=0; i<4; i++)
316     {
317       byte *p = edid + 0x36 + 18*i;
318       if (p[0] || p[1])
319         show_detailed_timing(p);
320       else
321         show_descriptor(p);
322     }
323
324   if (edid[0x7e])
325     printf("### %d extension blocks follow, but they are not supported yet ###\n", edid[0x7e]);
326 }
327
328 static void read_binary(void)
329 {
330   int c = read(0, edid, 128);
331   if (c < 0)
332     die("Read error: %m");
333   else if (c < 128)
334     die("Read only %d bytes, need 128", c);
335 }
336
337 static void
338 read_xorg_log(void)
339 {
340   char line[1024];
341   int mode = 0;
342   int cnt = 0;
343
344   while (fgets(line, sizeof(line), stdin))
345     {
346       if (!mode)
347         {
348           if (strstr(line, "EDID (in hex):"))
349             mode = 1;
350         }
351       else
352         {
353           char *c = strchr(line, '\n');
354           if (!c)
355             goto parseerr;
356           cnt += 16;
357           for (int i=0; i<32; i++)
358             {
359               if (c == line)
360                 goto parseerr;
361               int x = *--c;
362               if (x >= '0' && x <= '9')
363                 x -= '0';
364               else if (x >= 'A' && x <= 'F')
365                 x -= 'A' - 10;
366               else if (x >= 'a' && x <= 'f')
367                 x -= 'a' - 10;
368               else
369                 goto parseerr;
370               if (!(i % 2))
371                 edid[cnt-1-i/2] = x;
372               else
373                 edid[cnt-1-i/2] |= x << 4;
374             }
375           if (c > line && c[-1] != ' ' && c[-1] != '\t')
376             goto parseerr;
377           if (cnt == 128)
378             return;
379         }
380     }
381   if (!cnt)
382     die("No EDID log found");
383   else
384     die("EDID log found, but it is too short (only %d bytes)", cnt);
385
386 parseerr:
387   die("EDID log parse error at: %s", line);
388 }
389
390 static void
391 show_hex(void)
392 {
393   printf("\n");
394   for (int i=0; i<128; i+=16)
395     {
396       printf("@%02x:", i);
397       for (int j=0; j<16; j++)
398         printf(" %02x", edid[i+j]);
399       printf("\n");
400     }
401 }
402
403 static void
404 usage(void)
405 {
406   fprintf(stderr, "Usage: edid <options>\n\n\
407 Options:\n\
408 -l\t\tParse input as Xorg log file and try to find EDID dump there\n\
409 -x\t\tShow hexdump of the whole EDID block\n\
410 ");
411   exit(1);
412 }
413
414 int main(int argc, char **argv)
415 {
416   int opt;
417   int hex_mode = 0;
418   int log_mode = 0;
419
420   while ((opt = getopt(argc, argv, "xl")) >= 0)
421     switch (opt)
422       {
423       case 'l':
424         log_mode = 1;
425         break;
426       case 'x':
427         hex_mode = 1;
428         break;
429       default:
430         usage();
431       }
432   if (optind < argc)
433     usage();
434
435   if (log_mode)
436     read_xorg_log();
437   else
438     read_binary();
439
440   show_edid();
441
442   if (hex_mode)
443     show_hex();
444   return 0;
445 }