RDK Documentation (Open Sourced RDK Components)
edid-parser.cpp
1 /*
2  * If not stated otherwise in this file or this component's Licenses.txt file the
3  * following copyright and licenses apply:
4  *
5  * Copyright 2019 RDK Management
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18 */
19 
20 #define MODULE_PREFIX "edid"
21 #include "dslogger.h"
22 
23 #include "edid-parser.hpp"
24 
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdint.h>
28 #include <ctype.h>
29 
30 #define UNUSED(_x_) _x_ = _x_
31 
32 
33 namespace edid_parser {
34 
35 enum EDID_EXTENSION_TAG_BLOCK_MAP
36 {
37  LCD_TIMING = 0x01,
38  ADDITIONAL_TIMING = 0x02,
39  EDID20_EXTENSION = 0x20,
40  COLOR_INFORMATION_TYPE = 0x30,
41  DVI_FEATURE_DATA = 0x40,
42  TOUCH_SCREEN_DATA = 0x50,
43  EXTENSION_TAG_BLOCK_MAP = 0xF0,
44  MANUFACTURER_EXTENSION = 0xFF
45 };
46 
47 static void parse_std_timing(unsigned char* bytes, edid_data_t* data_ptr) {
48  int idx = 0;
49  // two 1 means empty block
50  if (bytes[idx] == 1 && bytes[idx + 1] == 1) return;
51  int h = ((bytes[idx]) + 31) * 8;
52  int v = 0;
53  switch ((bytes[idx + 1] & 0xC0) >> 6) {
54  case 0: v = (h * 10) / 16; break;
55  case 1: v = (h * 3) / 4; break;
56  case 2: v = (h * 4) / 5; break;
57  case 3: v = (h * 9) / 16; break;
58  default: return;
59  }
60  int r = (bytes[idx + 1] & 0x3F) + 60;
61  INT_DEBUG("STD %dx%d@%d\n", h, v, r);
62 
63  UNUSED(h);
64  UNUSED(v);
65  UNUSED(r);
66 }
67 
68 void parse_monitor_descriptor(unsigned char* bytes, edid_data_t* data_ptr) {
69  // Monitor Descriptor Block
70  if ((bytes[0] == 0) && (bytes[1] == 0) && (bytes[2] == 0)) {
71  // First Monitor Descriptor Block
72  if (bytes[3] == 0xFC) {
73  memset(data_ptr->monitor_name, '\0', sizeof(data_ptr->monitor_name));
74  for (int i = 5; (i < (5 + 14)) && (bytes[i] != '\n') && isprint(bytes[i]); i++) {
75  data_ptr->monitor_name[i - 5] = bytes[i];
76  }
77  INT_DEBUG("Monitor name:'%s'\n", data_ptr->monitor_name);
78  }
79  }
80 }
81 
82 static void parse_dtd(unsigned char* bytes, edid_data_t* data_ptr, char native) {
83  int idx = 0;
84  // Monitor descriptor block, not detailed timing descriptor.
85  if (bytes[idx] == 0 && bytes[idx + 1] == 0) {
86  parse_monitor_descriptor(bytes, data_ptr);
87  }
88  else {
89  // pixel clock, if any of bytes is 0 means std is invalid
90  if (bytes[idx] == 0 || bytes[idx + 1] == 0) return;
91  idx += 2;
92  // horizonal active
93  idx += 1;
94  // horizonal blanking
95  idx += 1;
96  // horizonal active : horizontal blanking
97  int h = ((bytes[idx] & 0xF0) << 4) | bytes[idx - 2];
98  idx += 1;
99  // vertical active
100  idx += 1;
101  // vertical blanking
102  idx += 1;
103  // vertical active : vertical blanking
104  int v = ((bytes[idx] & 0xF0) << 4) | bytes[idx - 2];
105  idx += 1;
106  // horizontal sync offset
107  idx += 1;
108  // horizontal sync pulse width
109  idx += 1;
110  // vertical sync offset : vertical sunc pulse width
111  idx += 1;
112  // horizontal sync offset, horizontal sync pulse width, vertical sync offset, vertical sync pulse width
113  idx += 1;
114  // horizontal image size
115  idx += 1;
116  // vertical image size
117  idx += 1;
118  // horizontal and vertical image size
119  idx += 1;
120  // horizontal border
121  idx += 1;
122  // vertical border
123  idx += 1;
124  // flags
125  char p = (bytes[idx] & 0x80) ? 0 : 1;
126  idx += 1;
127 
128  INT_DEBUG("DTD, %dx%d@%c, native: %d (%s found already)\n", h, v, (p ? 'p' : 'i'), native, (data_ptr->res.native == EDID_NOT_NATIVE ? "not" : ""));
129 
130  if (native) {
131  data_ptr->res.progressive = p ? EDID_PROGRESSIVE : EDID_INTERLACED;
132  data_ptr->res.width = h;
133  data_ptr->res.height = v * ((data_ptr->res.progressive == EDID_INTERLACED) ? 2 : 1);
134  data_ptr->res.native = EDID_NATIVE;
135  }
136  }
137 }
138 
139 typedef struct {
140  int w;
141  int h;
142  int p;
143  int r;
144  int ar_h;
145  int ar_v;
146 } edid_vic_t;
147 
148 static edid_vic_t vic[108] = {
149  {640, 480, 1, 60, 4, 3},
150  {720, 480, 1, 60, 4, 3},
151  {720, 480, 1, 60, 16, 9},
152  {1280, 720, 1, 60, 16, 9},
153  {1920, 1080, 0, 60, 16, 9},
154  {1440, 480, 0, 60, 4, 3},
155  {1440, 480, 0, 60, 16, 9},
156  {1440, 240, 0, 60, 4, 3},
157  {1440, 240, 1, 60, 16, 9},
158  {2880, 480, 0, 60, 4, 3},
159  {2880, 480, 0, 60, 16, 9},
160  {2880, 240, 1, 60, 4, 3},
161  {2880, 240, 1, 60, 16, 9},
162  {1440, 480, 1, 60, 4, 3},
163  {1440, 480, 1, 60, 16, 9},
164  {1920, 1080,1, 60, 16, 9},
165  {720, 576, 1, 50, 4, 3},
166  {720, 576, 1, 50, 16, 9},
167  {1280, 720, 1, 50, 16, 9},
168  {1920, 1080, 0, 50, 16, 9},
169  {1440, 576, 0, 50, 4, 3},
170  {1440, 576, 0, 50, 16, 9},
171  {1440 ,288, 1, 50, 4, 3},
172  {1440, 288, 1, 50, 16, 9},
173  {2880, 576, 0, 50, 4, 3},
174  {2880, 576, 0, 50, 16, 9},
175  {2880, 288, 1, 50, 4, 3},
176  {2880, 288, 1, 50, 16, 9},
177  {1440, 576, 1, 50, 4, 3},
178  {1440, 576, 1, 50, 16, 9},
179  {1920, 1080, 1, 50, 16, 9},
180  {1920, 1080, 1, 24, 16, 9},
181  {1920, 1080, 1, 25, 16, 9},
182  {1920, 1080, 1, 30, 16, 9},
183  {2880, 480, 1, 60, 4, 3},
184  {2880, 480, 1, 60, 16, 9},
185  {2880, 576, 1, 50, 4, 3},
186  {2880, 576, 1, 50, 16, 9},
187  {1920, 1080, 0, 50, 16, 9},
188  {1920, 1080, 0, 100, 16, 9},
189  {1280, 720, 1, 100, 16, 9},
190  {720, 576, 1, 100, 4, 3},
191  {720, 576, 1, 100, 16, 9},
192  {1440, 576, 0, 100, 4, 3},
193  {1440, 576, 0, 100, 16, 9},
194  {1920, 1080, 0, 120, 16, 9},
195  {1280, 720, 1, 120, 16, 9},
196  {720, 480, 1,120, 4, 3},
197  {720, 480, 1, 120, 16, 9},
198  {1440, 480, 0, 120,4, 3},
199  {1440, 480, 0, 120,16, 9},
200  {720, 576, 1, 200, 4, 3},
201  {720, 576, 1, 200, 16, 9},
202  {1440, 576, 0, 200, 4, 3},
203  {720, 576, 0, 200, 16, 9},
204  {720, 480, 1, 240, 4, 3},
205  {720, 480, 1, 240, 16, 9},
206  {720, 480, 0, 240, 4, 3},
207  {720, 480, 0, 240, 16, 9},
208  {1280, 720, 1, 24, 16, 9},
209  {1280, 720, 1, 25, 16, 9},
210  {1280, 720, 1, 30, 16, 9},
211  {1920, 1080, 1, 120, 16, 9},
212  {1920, 1080, 1, 100, 16, 9},
213  {1280, 720, 1, 24, 64, 27},
214  {1280, 720, 1, 25, 64, 27},
215  {1280, 720, 1, 30, 64, 27},
216  {1280, 720, 1, 50, 64, 27},
217  {1280, 720, 1, 60, 64, 27},
218  {1280, 720, 1, 100, 64, 27},
219  {1280, 720, 1, 120, 64, 27},
220  {1920, 1080, 1, 24, 64, 27},
221  {1920, 1080, 1, 25, 64, 27},
222  {1920, 1080, 1, 30, 64, 27},
223  {1920, 1080, 1, 50, 64, 27},
224  {1920, 1080, 1, 60, 64, 27},
225  {1920, 1080, 1, 100, 64, 27},
226  {1920, 1080, 1, 120, 64, 27},
227  {1680, 720, 1, 24, 64, 27},
228  {1680, 720, 1, 25, 64, 27},
229  {1680, 720, 1, 30, 64, 27},
230  {1680, 720, 1, 50, 64, 27},
231  {1680, 720, 1, 60, 64, 27},
232  {1680, 720, 1, 100,64, 27},
233  {1680, 720, 1, 120, 64, 27},
234  {2560, 1080, 1, 24, 64, 27},
235  {2560, 1080, 1, 25, 64, 27},
236  {2560, 1080, 1, 30, 64, 27},
237  {2560, 1080, 1, 50, 64, 27},
238  {2560, 1080, 1, 60, 64, 27},
239  {2560, 1080, 1, 100, 64, 27},
240  {2560, 1080, 1, 120, 64, 27},
241  {3840, 2160, 1, 24, 16, 9},
242  {3840, 2160, 1, 25, 16, 9},
243  {3840, 2160, 1, 30, 16, 9},
244  {3840, 2160, 1, 50, 16, 9},
245  {3840, 2160, 1, 60, 16, 9},
246  {4096, 2160, 1, 24, 256, 135},
247  {4096, 2160, 1, 25, 256, 135},
248  {4096, 2160, 1, 30, 256, 135},
249  {4096, 2160, 1, 50,256, 135},
250  {4096, 2160, 1, 60, 256, 135},
251  {3840, 2160, 1, 24, 64, 27},
252  {3840, 2160, 1, 25, 64, 27},
253  {3840, 2160, 1, 30, 64, 27},
254  {3840, 2160, 1, 50, 64, 27},
255  {3840, 2160, 1, 60, 64, 27},
256 };
257 
258 static void parse_ext_video(const unsigned char* bytes, edid_res_t* edid_res, const unsigned int len, const unsigned int native_cnt) {
259  /* only if native resolution was not detected yet */
260  if (edid_res->native == EDID_NOT_NATIVE) {
261  int found_native = 0;
262  unsigned int idx;
263  unsigned int res;
264 
265  for (idx = 0; idx < len; idx++) {
266  res = bytes[idx];
267  INT_DEBUG("EXT RES byte 0x%x (num: %d) %s\n", res, (((res >= 129) && (res <= 192)) ? (res & 0x7F) : res), (((res >= 129) && (res <= 192)) ? "native" : ""));
268  /* consider only native codes (CTA-861.F.pdf page 81) */
269  if ((res >= 129) && (res <= 192)) {
270  /* it is number of resolution in table, not index, so -1 */
271  res = (res & 0x7F) - 1;
272  found_native = 1;
273  }
274  /* When the first DTD and SVD do not match and the total number of
275  * DTDs defining Native Video Formats in the whole EDID is zero
276  * (see Table 41, byte 3, lower 4 bits), the first SVD shall take
277  * precedence. (CTA-861.F.pdf page 75)*/
278  else if ((native_cnt == 0) && (idx == 0)) {
279  res = res - 1;
280  found_native = 1;
281  }
282  if (found_native) {
283  if (res < sizeof(vic) / sizeof(edid_vic_t)) {
284  edid_res->width = vic[res].w;
285  edid_res->height = vic[res].h;
286  edid_res->refresh = vic[res].r;
287  edid_res->progressive = vic[res].p ? EDID_PROGRESSIVE : EDID_INTERLACED;
288  edid_res->native = EDID_NATIVE;
289  INT_DEBUG("EXT RES native found: %dx%d%c@%d\n", vic[res].w, vic[res].h, vic[res].p ? 'p' : 'i', vic[res].r);
290  return;
291  }
292  }
293  }
294  }
295 }
296 
297 static void parse_colorimetry_block(unsigned char* bytes, edid_data_t* data_ptr) {
298  data_ptr->colorimetry_info = ((uint32_t)bytes[2]) | ((uint32_t)bytes[3] << 8);
299  INT_DEBUG("colorimetry info:0x%x\n", data_ptr->colorimetry_info);
300 }
301 
302 static void parse_extended_db(uint8_t* bytes, edid_data_t* data_ptr) {
303  const uint8_t extended_code = bytes[1];
304  INT_DEBUG("parse_extended_db extended_code=%d\n", extended_code);
305 
306  // 0x5 - "Colorimetry Data block"
307  if (extended_code == 5) {
308  parse_colorimetry_block(bytes, data_ptr);
309  }
310  // 0x6 - "HDR Static Metadata Data Block" (from CTA-861-G_FINAL_revised_2017.pdf)
311  else if (extended_code == 6) {
312 
313  uint8_t eotfByte = bytes[2];
314  data_ptr->hdr_capabilities = 0;
315 
316  if (eotfByte & 0x01) {
317  data_ptr->hdr_capabilities |= HDR_standard_SDR;
318  }
319 
320  if (eotfByte & 0x02) {
321  data_ptr->hdr_capabilities |= HDR_standard_Traditional_HDR;
322  }
323 
324  // BHDM_EDID_HdrDbEotfSupport_eSMPTESt2084 == NEXUS_VideoEotf_eHdr10 / NEXUS_VideoEotf_eSmpteSt2084 == dsHDRSTANDARD_HDR10
325  if (eotfByte & 0x04) {
326  data_ptr->hdr_capabilities |= HDR_standard_HDR10;
327  }
328  // BHDM_EDID_HdrDbEotfSupport_eFuture == BAVC_HDMI_DRM_EOTF_eHLG/BAVC_HDMI_DRM_EOTF_eFuture == NEXUS_VideoEotf_eHlg/NEXUS_VideoEotf_eAribStdB67 == dsHDRSTANDARD_HLG
329  if (eotfByte & 0x08) {
330  data_ptr->hdr_capabilities |= HDR_standard_HLG;
331  }
332  } else {
333  INT_DEBUG("Extended DB - extended code %d not supported\n", extended_code);
334  }
335 }
336 
337 static void parse_vendodr_specific_block(unsigned char* bytes, edid_data_t* data_ptr) {
338  data_ptr->physical_address_a = bytes[4] >> 4;
339  data_ptr->physical_address_b = bytes[4] & 0x0F;
340  data_ptr->physical_address_c = bytes[3] >> 4;
341  data_ptr->physical_address_d = bytes[5] & 0x0F;
342  INT_DEBUG("Vendor specific bolck, physical adress a:0x%x b:0x%x c:0x%x d:0x%x\n",
343  data_ptr->physical_address_a,
344  data_ptr->physical_address_b,
345  data_ptr->physical_address_c,
346  data_ptr->physical_address_d);
347 }
348 
349 static void parse_ext_timing(unsigned char* bytes, edid_data_t* data_ptr) {
350  int idx = 0;
351  // dtd start
352  INT_DEBUG("TimingExtension version: %d\n", bytes[-1]);
353  int dtd_start = bytes[idx] - 2;
354  idx += 1;
355  // extension dtds number (0x0F), underscan (0x80), basic audio (0x40), ycbcr444 (0x20), ycbcr422 (0x10), native formats (0x07)
356  int native_cnt = bytes[idx] & 0x07;
357  INT_DEBUG("Native cnt: %d\n", native_cnt);
358  idx += 1;
359  if (dtd_start != 2) {
360  int end = dtd_start;
361  edid_res_t ext_video_res;
362 
363  ext_video_res.native = EDID_NOT_NATIVE;
364  while (idx < end) {
365  int tag = (bytes[idx] & 0xE0) >> 5;
366  int len = bytes[idx] & 0x1F;
367  INT_DEBUG("parse_ext_timing: extension tag=%d len=%d\n", tag, len);
368 
369  switch (tag) {
370  // reserved
371  case 0: break;
372  // audio
373  case 1: break;
374  // video
375  case 2: parse_ext_video(&bytes[idx + 1], &ext_video_res, len, native_cnt); break;
376  // vendor specific
377  case 3: parse_vendodr_specific_block(&bytes[idx], data_ptr); break;
378  // speaker
379  case 4: break;
380  // vesa dtc data block
381  case 5: break;
382  // reserved
383  case 6: break;
384  // 'Use Extended Tag'
385  case 7: parse_extended_db(&bytes[idx], data_ptr); break;
386  // default - unsupported
387  default: INT_DEBUG("Unsupported extension tag: 0x%X\n", tag);
388  }
389  idx += len + 1;
390  }
391  /* if there is native resoluton defined in Video Data Block we will
392  * prioritize that over DTD native - important for example for monitors
393  * where real native resolution could be quite exotic */
394  if (ext_video_res.native != EDID_NOT_NATIVE) {
395  data_ptr->res.width = ext_video_res.width;
396  data_ptr->res.height = ext_video_res.height;
397  data_ptr->res.refresh = ext_video_res.refresh;
398  data_ptr->res.progressive = ext_video_res.progressive;
399  data_ptr->res.native = ext_video_res.native;
400  }
401  }
402  /* At the moment this implementation parses only 1 (first) native resolution,
403  * as DTDs are as well in frist EDID block it could be that native resolution
404  * is already parsed, and we should not overwrite it */
405  if (data_ptr->res.native == EDID_NOT_NATIVE) {
406  idx = dtd_start;
407  while (idx < dtd_start + (4 * 18)) {
408  parse_dtd(&bytes[idx], data_ptr, (data_ptr->res.native == EDID_NOT_NATIVE));
409  idx += 18;
410  }
411  }
412 }
413 
414 static void parse_est_timing(unsigned char b1, unsigned char b2, unsigned char b3, edid_data_t* data_ptr) {
415  int width = 0;
416  int height = 0;
417  int refresh = 0;
418  char progressive = 1;
419  if ((b1 & 0x01) == 0x01) {
420  width = 800;
421  height = 600;
422  refresh = 60;
423  }
424  if ((b1 & 0x02) == 0x02) {
425  width = 800;
426  height = 600;
427  refresh = 56;
428  }
429  if ((b1 & 0x04) == 0x04) {
430  width = 640;
431  height = 480;
432  refresh = 75;
433  }
434  if ((b1 & 0x08) == 0x08) {
435  width = 640;
436  height = 480;
437  refresh = 72;
438  }
439  if ((b1 & 0x10) == 0x10) {
440  width = 640;
441  height = 480;
442  refresh = 67;
443  }
444  if ((b1 & 0x20) == 0x20) {
445  width = 640;
446  height = 480;
447  refresh = 60;
448  }
449  if ((b1 & 0x40) == 0x40) {
450  width = 720;
451  height = 400;
452  refresh = 88;
453  }
454  if ((b1 & 0x80) == 0x80) {
455  width = 720;
456  height = 400;
457  refresh = 70;
458  }
459  if ((b2 & 0x01) == 0x01) {
460  width = 1280;
461  height = 1024;
462  refresh = 75;
463  }
464  if ((b2 & 0x02) == 0x02) {
465  width = 1024;
466  height = 768;
467  refresh = 75;
468  }
469  if ((b2 & 0x04) == 0x04) {
470  width = 1024;
471  height = 768;
472  refresh = 70;
473  }
474  if ((b2 & 0x08) == 0x08) {
475  width = 1024;
476  height = 768;
477  refresh = 60;
478  }
479  if ((b2 & 0x10) == 0x10) {
480  width = 1024;
481  height = 768;
482  refresh = 87;
483  progressive = 0;
484  }
485  if ((b2 & 0x20) == 0x20) {
486  width = 832;
487  height = 624;
488  refresh = 75;
489  }
490  if ((b2 & 0x40) == 0x40) {
491  width = 800;
492  height = 600;
493  refresh = 75;
494  }
495  if ((b2 & 0x80) == 0x80) {
496  width = 800;
497  height = 600;
498  refresh = 72;
499  }
500  if ((b3 & 0x80) == 0x80) {
501  width = 1152;
502  height = 870;
503  refresh = 75;
504  }
505 
506  INT_DEBUG("EST %dx%d%c@%d\n", width, height, progressive ? 'p' : 'i', refresh);
507 
508  UNUSED(width);
509  UNUSED(height);
510  UNUSED(progressive);
511  UNUSED(refresh);
512 }
513 
514 // bytes length needs to be (at least) 128
515 int block_checksum_ok(unsigned char* bytes) {
516  // CTA-861-G_FINAL_revised_2017.pdf:
517  // Checksum byte = (256-(S%256)) %256
518  // Where:
519  // S is the sum of the first 127 bytes
520  // % is modulus operator
521  uint32_t sum = 0;
522  for (size_t idx=0; idx < 127; ++idx) {
523  sum += bytes[idx];
524  }
525  return bytes[127] == (256 - (sum % 256)) % 256;
526 }
527 
528 edid_status_e parse_extension_block(uint32_t base_idx, unsigned char* bytes, edid_data_t* data_ptr)
529 {
530  int ext_tag = bytes[base_idx];
531  // skiping revision number
532 
533  switch (ext_tag) {
534  // LCD timing
535  case LCD_TIMING: break;
536  // Additional timing
537  case ADDITIONAL_TIMING: parse_ext_timing(&bytes[base_idx + 2], data_ptr);break;
538  // EDID 2.0 extension
539  case EDID20_EXTENSION: break;
540  // Color information type
541  case COLOR_INFORMATION_TYPE: break;
542  // DVI feature data
543  case DVI_FEATURE_DATA: break;
544  // Touch screen data
545  case TOUCH_SCREEN_DATA: break;
546  // Block Map
547  case EXTENSION_TAG_BLOCK_MAP: break;
548  // Manufacturer extension
549  case MANUFACTURER_EXTENSION: break;
550  default: INT_DEBUG("Unsupported tag: 0x%X\n", ext_tag);
551  }
552 
553  return EDID_STATUS_OK;
554 }
555 
556 edid_status_e parse_extension_blocks(uint32_t extensions, unsigned char* bytes, size_t count, edid_data_t* data_ptr)
557 {
558  if (count < 128 * (1 + extensions)) {
559  INT_ERROR("parse_extension_blocks: too short for extension count - count:%zu extensions:%u\n",count,extensions);
560  return EDID_STATUS_INVALID_HEADER;
561  }
562 
563  /* CTA-861-G_FINAL_revised_2017.pdf: "Some devices have been incorrectly designed so that the block map is not counted in the
564  extension count. Design of compliant devices should take compatibility with those non-compliant
565  devices into consideration. For example, when a Source finds an extension count of 2, it may
566  attempt to read 3 extensions on the chance that the Sink has incorrectly set its count"
567  */
568  if (count == 128 * (2 + extensions)) {
569  // there is 1 more block (128 bytes) than would seem from extensions count
570  // assume we are handling 'incorrectly designed' device & attempt to read 1 more extension
571  INT_WARN("extensions:%d, but count: %zu - increase extension cnt\n", extensions, count);
572  ++extensions;
573  }
574 
575  // extension tag
576  const int first_ext_tag = bytes[128];
577  uint32_t extension_block_idx = 0;
578 
579  if (extensions > 1 ) {
580  if (first_ext_tag != EXTENSION_TAG_BLOCK_MAP) {
581  INT_ERROR("Incorrect input, more than one extension: %d - but block 1 is not extension block map\n", extensions);
582  return EDID_STATUS_NOT_SUPPORTED;
583  } else {
584  // skip block map; the extensions are tagged anyway
585  extension_block_idx = 1;
586  }
587  }
588 
589  edid_status_e ret = EDID_STATUS_OK;
590 
591  for ( ; extension_block_idx < extensions; ++extension_block_idx) {
592  const uint32_t base_idx = (1 + extension_block_idx) * 128;
593  ret = parse_extension_block(base_idx, bytes, data_ptr);
594  if (ret != EDID_STATUS_OK) break;
595  }
596 
597  return ret;
598 }
599 
600 #define SET_LETTER(x) ( isprint((x) + '@') ? (x) + '@' : '\0')
601 
602 static void parse_manufacturer_name(uint8_t b1, uint8_t b2, edid_data_t* data_ptr)
603 {
604  data_ptr->manufacturer_name[0] = SET_LETTER((b1 & 0x7C) >> 2);
605  data_ptr->manufacturer_name[1] = SET_LETTER(((b1 & 0x03) << 3) | ((b2 & 0xE0) >> 5));
606  data_ptr->manufacturer_name[2] = SET_LETTER(b2 & 0x1F);
607  data_ptr->manufacturer_name[3] = '\0';
608  INT_DEBUG("Manufacturer name:'%s'\n", data_ptr->manufacturer_name);
609 }
610 
611 static void parse_product_code(uint8_t b1, uint8_t b2, edid_data_t* data_ptr)
612 {
613  data_ptr->product_code = (((int32_t)b1 << 8) | (int32_t)b2);
614  INT_DEBUG("Product code:%d\n", data_ptr->product_code);
615 }
616 
617 static void parse_serial_number(uint8_t* serial_ptr, edid_data_t* data_ptr)
618 {
619  data_ptr->serial_number = ((int32_t)serial_ptr[0] << 24) | ((int32_t)serial_ptr[1] << 16) | ((int32_t)serial_ptr[2] << 8) | ((int32_t)serial_ptr[3]);
620  INT_DEBUG("Serial number:%d\n", data_ptr->serial_number);
621 }
622 
623 static void parse_manufacture_week(uint8_t b1, edid_data_t* data_ptr)
624 {
625  data_ptr->manufacture_week = b1;
626  INT_DEBUG("Manufacture week:%d\n", data_ptr->manufacture_week);
627 }
628 
629 static void parse_manufacture_year(uint8_t b1, edid_data_t* data_ptr)
630 {
631  data_ptr->manufacture_year = b1;
632  INT_DEBUG("Manufacture year:%d\n", data_ptr->manufacture_year);
633 }
634 
635 static void parse_edid_version(uint8_t b1, uint8_t b2, edid_data_t* data_ptr)
636 {
637  data_ptr->edid_version[0] = b1;
638  data_ptr->edid_version[1] = b2;
639  INT_DEBUG("EDID version:%d.%d\n", data_ptr->edid_version[0], data_ptr->edid_version[1]);
640 }
641 
642 edid_status_e EDID_Verify(unsigned char* bytes, size_t count) {
643  if (!bytes || count < 128) {
644  return EDID_STATUS_INVALID_PARAMETER;
645  }
646  static const unsigned char header[8] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
647  if (memcmp(bytes, header, sizeof(header)) != 0) {
648  INT_ERROR("Incorrect input, header does not match: %02x %02x %02x %02x %02x %02x %02x %02x\n", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]);
649  return EDID_STATUS_INVALID_HEADER;
650  }
651  return EDID_STATUS_OK;
652 }
653 
654 edid_status_e EDID_Parse(unsigned char* bytes, size_t count, edid_data_t* data_ptr) {
655  if (!data_ptr) {
656  INT_ERROR("Incorrect input, data_ptr null\n");
657  return EDID_STATUS_INVALID_PARAMETER;
658  }
659  edid_status_e verify_status = EDID_Verify(bytes, count);
660  if (verify_status != EDID_STATUS_OK) {
661  return verify_status;
662  }
663 
664  memset(data_ptr, 0, sizeof(edid_data_t));
665  data_ptr->res.native = EDID_NOT_NATIVE;
666 
667  int idx = 0;
668 
669  idx += 8;
670  // vendor / product ID
671  parse_manufacturer_name(bytes[idx], bytes[idx + 1], data_ptr);
672  parse_product_code(bytes[idx + 2], bytes[idx + 3], data_ptr);
673  parse_serial_number(&bytes[idx + 4], data_ptr);
674  parse_manufacture_week(bytes[idx + 8], data_ptr);
675  parse_manufacture_year(bytes[idx + 9], data_ptr);
676  idx += 10;
677  // EDID version (version, revision)
678  parse_edid_version(bytes[idx], bytes[idx + 1], data_ptr);
679  idx += 2;
680  // basics
681  idx += 5;
682  // colors
683  idx += 10;
684  // established timings
685  parse_est_timing(bytes[idx], bytes[idx + 1], bytes[idx + 2], data_ptr);
686  idx += 3;
687  // standard timing ID
688  for (int i = idx; i < idx + 16; i += 2) {
689  parse_std_timing(&bytes[i], data_ptr);
690  }
691  idx += 16;
692  // detailed timing descriptions
693  for (int i = idx; i < idx + 72; i += 18) {
694  parse_dtd(&bytes[i], data_ptr, i == idx);
695  }
696  idx += 72;
697  // extension flag
698  int extension = bytes[idx];
699  idx += 2;
700 
701  edid_status_e ret = EDID_STATUS_OK;
702 
703  // parse extension blocks, if any
704  if (extension > 0)
705  {
706  ret = parse_extension_blocks(extension,bytes,count,data_ptr);
707  }
708  return ret;
709 }
710 }
edid_parser::edid_vic_t
Definition: edid-parser.cpp:139