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) -- 2.21.0 (Apple Git-122.2)