Hi Joe, I've committed the other patches, but I'm not sure about this one. I'll look at it again when I have more time. I might make my own implementation based on ideas of this patch. BTW, I made a real mess with the back/front porch timings. Thanks for fixing that! Regards, Hans On 12/5/19 8:34 AM, joevt wrote: > Reduced tag parsing code. > Added OUI min block length checks. > > Signed-off-by: Joe van Tunen <joevt@xxxxxxx> > --- > edid-decode.h | 1 + > parse-cta-block.cpp | 336 ++++++++++++++++++-------------------------- > 2 files changed, 138 insertions(+), 199 deletions(-) > > diff --git a/edid-decode.h b/edid-decode.h > index 83ded83..758bdcf 100644 > --- a/edid-decode.h > +++ b/edid-decode.h > @@ -111,6 +111,7 @@ struct edid_state { > void cta_y420cmdb(const unsigned char *x, unsigned length); > void cta_vfpdb(const unsigned char *x, unsigned length); > void cta_hdmi_block(const unsigned char *x, unsigned length); > + void cta_oui(const char *block_name, const unsigned char *x, unsigned length, unsigned *ouinum); > void cta_block(const unsigned char *x); > void preparse_cta_block(const unsigned char *x); > void parse_cta_block(const unsigned char *x); > diff --git a/parse-cta-block.cpp b/parse-cta-block.cpp > index dea87c1..e52822a 100644 > --- a/parse-cta-block.cpp > +++ b/parse-cta-block.cpp > @@ -375,6 +375,11 @@ void edid_state::cta_svd(const unsigned char *x, unsigned n, int for_ycbcr420) > > if (vic == 1 && !for_ycbcr420) > has_cta861_vic_1 = 1; > + > + // vics and has_vic are basically the same (if has_vic was not bool), except vics > + // is built after preparse (during parse) which allows errors for duplicates to be > + // output in parse order. has_vic is built during preparse and is used when vics > + // from other blocks need to be checked. > if (++vics[vic][for_ycbcr420] == 2) > fail("Duplicate %sVIC %u\n", for_ycbcr420 ? "YCbCr 4:2:0 " : "", vic); > if (for_ycbcr420 && has_vic[0][vic]) > @@ -485,7 +490,6 @@ void edid_state::cta_hdmi_block(const unsigned char *x, unsigned length) > { > unsigned len_vic, len_3d; > > - printf(" (HDMI)\n"); > printf(" Source physical address %u.%u.%u.%u\n", x[3] >> 4, x[3] & 0x0f, > x[4] >> 4, x[4] & 0x0f); > > @@ -1228,10 +1232,20 @@ static void cta_hdmi_audio_block(const unsigned char *x, unsigned length) > x += 4; > } > } > +const unsigned kOUI_Unknown = 1<<12; > +const unsigned kOUI_HDMI = 2<<12; > +const unsigned kOUI_HDMIForum = 3<<12; > +const unsigned kOUI_HDR10 = 4<<12; > > -static const char *oui_name(unsigned oui) > +static const char *oui_name(unsigned oui, unsigned *ouinum = NULL) > { > + unsigned ouinumscratch; > + if (!ouinum) ouinum = &ouinumscratch; > + *ouinum = kOUI_Unknown; > switch (oui) { > + case 0x000c03: *ouinum = kOUI_HDMI ; return "HDMI"; > + case 0xc45dd8: *ouinum = kOUI_HDMIForum ; return "HDMI Forum"; > + case 0x90848b: *ouinum = kOUI_HDR10 ; return "HDR10+"; > case 0x00001a: return "AMD"; > case 0x00044b: return "NVIDIA"; > case 0x000c6e: return "ASUS"; > @@ -1244,212 +1258,136 @@ static const char *oui_name(unsigned oui) > } > } > > +void edid_state::cta_oui(const char *block_name, const unsigned char *x, unsigned length, unsigned *ouinum) > +{ > + char buf[10]; > + unsigned oui; > + > + if (length < 3) { > + oui = 0xffffffff; > + sprintf(buf, "?"); > + } else { > + oui = (x[2] << 16) + (x[1] << 8) + x[0]; > + sprintf(buf, "0x%06x", oui); > + } > + > + const char *ouiname = oui_name(oui, ouinum); > + std::string name = std::string(block_name) + ", OUI " + buf; > + if (ouiname) name += std::string(" (") + ouiname + ")"; > + data_block = name; > + > + if (oui == 0xffffffff) > + fail("CTA data block is not long enough to contain an OUI\n"); > +} > + > +#define data_block_o(n) cta_oui(n, x + 1 + extended, length - extended, &ouinum) > + > void edid_state::cta_block(const unsigned char *x) > { > - static int last_block_was_hdmi_vsdb; > - static int have_hf_vsdb, have_hf_scdb; > - static int first_block = 1; > + static unsigned previous_cta_tag = 0; > + static bool have_hf_vsdb = false; > + static bool have_hf_scdb = false; > + static unsigned cta_block_number = 0; > + > unsigned length = x[0] & 0x1f; > - const char *name; > - unsigned oui; > + unsigned ouinum = 0; > + unsigned tag=(x[0] & 0xe0) >> 5; > + unsigned extended = tag == 0x07 ? 1 : 0; > + if (extended) tag = 0x700 + x[1]; > + > + switch (tag) { > + case 0x001: data_block = "Audio Data Block"; break; > + case 0x002: data_block = "Video Data Block"; break; > + case 0x003: data_block_o("Vendor-Specific Data Block"); break; > + case 0x004: data_block = "Speaker Allocation Data Block"; break; > + case 0x005: data_block = "VESA DTC Data Block"; break; // not implemented > + > + case 0x700: data_block = "Video Capability Data Block"; break; > + case 0x701: data_block_o("Vendor-Specific Video Data Block"); break; > + case 0x702: data_block = "VESA Video Display Device Data Block"; break; // not implemented > + case 0x703: data_block = "VESA Video Timing Block Extension"; break; // not implemented > + case 0x704: data_block = "Reserved for HDMI Video Data Block"; break; // reserved > + case 0x705: data_block = "Colorimetry Data Block"; break; > + case 0x706: data_block = "HDR Static Metadata Data Block"; break; > + case 0x707: data_block = "HDR Dynamic Metadata Data Block"; break; > + > + case 0x70d: data_block = "Video Format Preference Data Block"; break; > + case 0x70e: data_block = "YCbCr 4:2:0 Video Data Block"; break; > + case 0x70f: data_block = "YCbCr 4:2:0 Capability Map Data Block"; break; > + case 0x710: data_block = "Reserved for CTA Miscellaneous Audio Fields"; break; // reserved > + case 0x711: data_block_o("Vendor-Specific Audio Data Block"); break; // no vendors implemented > + case 0x712: data_block = "HDMI Audio Data Block"; break; > + case 0x713: data_block = "Room Configuration Data Block"; break; > + case 0x714: data_block = "Speaker Location Data Block"; break; > + > + case 0x720: data_block = "InfoFrame Data Block"; break; > + > + case 0x778: data_block = "HDMI Forum EDID Extension Override Data Block"; break; > + case 0x779: data_block = "HDMI Forum Sink Capability Data Block"; break; > + default: > + if (tag < 0x700) data_block = "Unknown CTA Data Block"; > + else if (tag < 0x70d) data_block = "Unknown CTA Video-Related Data Block"; > + else if (tag < 0x720) data_block = "Unknown CTA Audio-Related Data Block"; > + else if (tag < 0x778) data_block = "Unknown CTA Data Block"; > + else if (tag < 0x780) data_block = "Unknown CTA HDMI-Related Data Block"; > + else data_block = "Unknown CTA Data Block"; > + data_block += std::string(" (") + (extended ? "extended " : "") + "tag " + utohex(tag & 0xff) + ")"; > + } > > - switch ((x[0] & 0xe0) >> 5) { > - case 0x01: > - data_block = "Audio Data Block"; > - printf(" %s\n", data_block.c_str()); > - cta_audio_block(x + 1, length); > + printf(" %s\n", data_block.c_str()); > + > + tag |= ouinum; > + switch (tag) { > + case 0x001: cta_audio_block(x + 1, length); break; > + case 0x002: cta_svd(x + 1, length, 0); break; > + case 0x003|kOUI_HDMI: > + cta_hdmi_block(x + 1, length); > + if (edid_minor != 3) > + fail("The HDMI Specification uses EDID 1.3, not 1.%u\n", edid_minor); > break; > - case 0x02: > - data_block = "Video Data Block"; > - printf(" %s\n", data_block.c_str()); > - cta_svd(x + 1, length, 0); > + case 0x003|kOUI_HDMIForum: > + if (previous_cta_tag != (0x003|kOUI_HDMI)) > + fail("HDMI Forum VSDB did not immediately follow the HDMI VSDB\n"); > + if (have_hf_scdb || have_hf_vsdb) > + fail("Duplicate HDMI Forum VSDB/SCDB\n"); > + cta_hf_scdb(x + 4, length - 3); > + have_hf_vsdb = true; > break; > - case 0x03: > - oui = (x[3] << 16) + (x[2] << 8) + x[1]; > - printf(" Vendor-Specific Data Block, OUI 0x%06x", oui); > - name = oui_name(oui); > - if (oui == 0x000c03) { > - data_block = "Vendor-Specific Data Block (HDMI)"; > - cta_hdmi_block(x + 1, length); > - last_block_was_hdmi_vsdb = 1; > - first_block = 0; > - if (edid_minor != 3) > - fail("The HDMI Specification uses EDID 1.3, not 1.%u\n", edid_minor); > - return; > - } > - if (oui == 0xc45dd8) { > - data_block = "Vendor-Specific Data Block (HDMI Forum)"; > - if (!last_block_was_hdmi_vsdb) > - fail("HDMI Forum VSDB did not immediately follow the HDMI VSDB\n"); > - if (have_hf_scdb || have_hf_vsdb) > - fail("Duplicate HDMI Forum VSDB/SCDB\n"); > - printf(" (HDMI Forum)\n"); > - cta_hf_scdb(x + 4, length - 3); > - have_hf_vsdb = 1; > - } else if (name) { > - data_block = std::string("Vendor-Specific Data Block (") + name + ")"; > - printf(" (%s)\n", name); > - hex_block(" ", x + 4, length - 3); > - } else { > - printf("\n"); > - hex_block(" ", x + 4, length - 3); > - data_block.clear(); > - warn("Unknown Vendor-Specific Data Block, OUI 0x%06x\n", oui); > - } > - break; > - case 0x04: > - data_block = "Speaker Allocation Data Block"; > - printf(" %s\n", data_block.c_str()); > - cta_sadb(x + 1, length); > - break; > - case 0x05: > - printf(" VESA DTC Data Block\n"); > - hex_block(" ", x + 1, length); > - break; > - case 0x07: > - printf(" Extended tag: "); > - switch (x[1]) { > - case 0x00: > - data_block = "Video Capability Data Block"; > - printf("%s\n", data_block.c_str()); > - cta_vcdb(x + 2, length - 1); > - break; > - case 0x01: > - oui = (x[4] << 16) + (x[3] << 8) + x[2]; > - printf("Vendor-Specific Video Data Block, OUI 0x%06x", oui); > - name = oui_name(oui); > - if (oui == 0x90848b) { > - data_block = "Vendor-Specific Video Data Block (HDR10+)"; > - printf(" (HDR10+)\n"); > - cta_hdr10plus(x + 5, length - 4); > - } else if (name) { > - data_block = std::string("Vendor-Specific Data Block (") + name + ")"; > - printf(" (%s)\n", name); > - hex_block(" ", x + 5, length - 4); > - } else { > - printf("\n"); > - hex_block(" ", x + 5, length - 4); > - data_block.clear(); > - warn("Unknown Extended Vendor-Specific Data Block, OUI 0x%06x\n", oui); > - } > - break; > - case 0x02: > - printf("VESA Video Display Device Data Block\n"); > - hex_block(" ", x + 2, length - 1); > - break; > - case 0x03: > - printf("VESA Video Timing Block Extension\n"); > - hex_block(" ", x + 2, length - 1); > - break; > - case 0x04: > - printf("Reserved for HDMI Video Data Block\n"); > - hex_block(" ", x + 2, length - 1); > - break; > - case 0x05: > - data_block = "Colorimetry Data Block"; > - printf("%s\n", data_block.c_str()); > - cta_colorimetry_block(x + 2, length - 1); > - break; > - case 0x06: > - data_block = "HDR Static Metadata Data Block"; > - printf("%s\n", data_block.c_str()); > - cta_hdr_static_metadata_block(x + 2, length - 1); > - break; > - case 0x07: > - data_block = "HDR Dynamic Metadata Data Block"; > - printf("%s\n", data_block.c_str()); > - cta_hdr_dyn_metadata_block(x + 2, length - 1); > - break; > - case 0x0d: > - data_block = "Video Format Preference Data Block"; > - printf("%s\n", data_block.c_str()); > - cta_vfpdb(x + 2, length - 1); > - break; > - case 0x0e: > - data_block = "YCbCr 4:2:0 Video Data Block"; > - printf("%s\n", data_block.c_str()); > - cta_svd(x + 2, length - 1, 1); > - break; > - case 0x0f: > - data_block = "YCbCr 4:2:0 Capability Map Data Block"; > - printf("%s\n", data_block.c_str()); > - cta_y420cmdb(x + 2, length - 1); > - break; > - case 0x10: > - printf("Reserved for CTA Miscellaneous Audio Fields\n"); > - hex_block(" ", x + 2, length - 1); > - break; > - case 0x11: > - printf("Vendor-Specific Audio Data Block\n"); > - hex_block(" ", x + 2, length - 1); > - break; > - case 0x12: > - data_block = "HDMI Audio Data Block"; > - printf("%s\n", data_block.c_str()); > - cta_hdmi_audio_block(x + 2, length - 1); > - break; > - case 0x13: > - data_block = "Room Configuration Data Block"; > - printf("%s\n", data_block.c_str()); > - cta_rcdb(x + 2, length - 1); > - break; > - case 0x14: > - data_block = "Speaker Location Data Block"; > - printf("%s\n", data_block.c_str()); > - cta_sldb(x + 2, length - 1); > - break; > - case 0x20: > - printf("InfoFrame Data Block\n"); > - cta_ifdb(x + 2, length - 1); > - break; > - case 0x78: > - data_block = "HDMI Forum EDID Extension Override Data Block"; > - printf("%s\n", data_block.c_str()); > - cta_hf_eeodb(x + 2, length - 1); > - // This must be the first CTA block > - if (!first_block) > - fail("Block starts at a wrong offset\n"); > - break; > - case 0x79: > - data_block = "HDMI Forum Sink Capability Data Block"; > - printf("%s\n", data_block.c_str()); > - if (!last_block_was_hdmi_vsdb) > - fail("HDMI Forum SCDB did not immediately follow the HDMI VSDB\n"); > - if (have_hf_scdb || have_hf_vsdb) > - fail("Duplicate HDMI Forum VSDB/SCDB\n"); > - if (x[2] || x[3]) > - printf(" Non-zero SCDB reserved fields!\n"); > - cta_hf_scdb(x + 4, length - 3); > - have_hf_scdb = 1; > - break; > - default: > - if (x[1] <= 12) > - printf("Unknown CTA Video-Related"); > - else if (x[1] <= 31) > - printf("Unknown CTA Audio-Related"); > - else if (x[1] >= 120 && x[1] <= 127) > - printf("Unknown CTA HDMI-Related"); > - else > - printf("Unknown CTA"); > - printf(" Data Block (extended tag 0x%02x, length %u)\n", x[1], length - 1); > - hex_block(" ", x + 2, length - 1); > - data_block.clear(); > - warn("Unknown Extended CTA Data Block 0x%02x\n", x[1]); > - break; > - } > + case 0x004: cta_sadb(x + 1, length); break; > + case 0x700: cta_vcdb(x + 2, length - 1); break; > + case 0x701|kOUI_HDR10: cta_hdr10plus(x + 5, length - 4); break; > + case 0x705: cta_colorimetry_block(x + 2, length - 1); break; > + case 0x706: cta_hdr_static_metadata_block(x + 2, length - 1); break; > + case 0x707: cta_hdr_dyn_metadata_block(x + 2, length - 1); break; > + case 0x70d: cta_vfpdb(x + 2, length - 1); break; > + case 0x70e: cta_svd(x + 2, length - 1, 1); break; > + case 0x70f: cta_y420cmdb(x + 2, length - 1); break; > + case 0x712: cta_hdmi_audio_block(x + 2, length - 1); break; > + case 0x713: cta_rcdb(x + 2, length - 1); break; > + case 0x714: cta_sldb(x + 2, length - 1); break; > + case 0x720: cta_ifdb(x + 2, length - 1); break; > + case 0x778: > + cta_hf_eeodb(x + 2, length - 1); > + // This must be the first CTA block > + if (cta_block_number != 0) > + fail("Block starts at a wrong offset\n"); > break; > - default: { > - unsigned tag = (*x & 0xe0) >> 5; > - unsigned length = *x & 0x1f; > - printf(" Unknown CTA tag 0x%02x, length %u\n", tag, length); > - hex_block(" ", x + 1, length); > - data_block.clear(); > - warn("Unknown CTA Data Block %u\n", tag); > + case 0x779: > + if (previous_cta_tag != (0x003|kOUI_HDMI)) > + fail("HDMI Forum SCDB did not immediately follow the HDMI VSDB\n"); > + if (have_hf_scdb || have_hf_vsdb) > + fail("Duplicate HDMI Forum VSDB/SCDB\n"); > + if (x[2] || x[3]) > + printf(" Non-zero SCDB reserved fields!\n"); > + cta_hf_scdb(x + 4, length - 3); > + have_hf_scdb = true; > break; > + default: > + warn("Unknown %s\n", data_block.c_str()); > + hex_block(" ", x + 1 + extended + (ouinum ? 3 : 0), length - (extended + (ouinum ? 3 : 0))); > } > - } > - first_block = 0; > - last_block_was_hdmi_vsdb = 0; > + cta_block_number++; > + previous_cta_tag = tag; > } > > void edid_state::preparse_cta_block(const unsigned char *x) >