aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--edid-decode.19
-rw-r--r--edid-decode.cpp179
-rw-r--r--edid-decode.h222
-rw-r--r--parse-base-block.cpp268
-rw-r--r--parse-cta-block.cpp304
-rw-r--r--parse-displayid-block.cpp177
-rw-r--r--parse-vtb-ext-block.cpp7
-rw-r--r--test/README2
-rw-r--r--vs/edid-decode.sln25
-rw-r--r--vs/edid-decode.vcxproj114
-rw-r--r--vs/edid-decode.vcxproj.filters49
-rw-r--r--vs/getopt.c787
-rw-r--r--vs/getopt.h107
-rw-r--r--vs/unistd.h10
14 files changed, 1844 insertions, 416 deletions
diff --git a/edid-decode.1 b/edid-decode.1
index 2c4b0bb..9de52a0 100644
--- a/edid-decode.1
+++ b/edid-decode.1
@@ -171,12 +171,15 @@ reported at the end.
Check if the EDID conforms to the standards. Warnings and failures are
reported as they happen.
.TP
-\fB\-p\fR, \fB\-\-preferred\-timing\fR
-Report the preferred timing at the end.
+\fB\-n\fR, \fB\-\-native\-timings\fR
+Report the native timings at the end.
+.TP
+\fB\-p\fR, \fB\-\-preferred\-timings\fR
+Report the preferred timings at the end.
.TP
\fB\-P\fR, \fB\-\-physical\-address\fR
Just report the HDMI Source Physical Address and nothing else. Reports f.f.f.f
-if the EDID could not be parsed, or if there was no CTA Vendor-Specific Data Block
+if the EDID could not be parsed, or if there was no CTA-861 Vendor-Specific Data Block
with OUI 00-0C-03. Otherwise it reports the Source Physical Address as provided
in that Data Block. This can be used as input to HDMI CEC utilities such as the
linux cec-ctl(1) utility.
diff --git a/edid-decode.cpp b/edid-decode.cpp
index d27bcc2..ccec165 100644
--- a/edid-decode.cpp
+++ b/edid-decode.cpp
@@ -39,8 +39,9 @@ enum Option {
OptCheckInline = 'C',
OptExtract = 'e',
OptHelp = 'h',
+ OptNativeTimings = 'n',
OptOutputFormat = 'o',
- OptPreferredTiming = 'p',
+ OptPreferredTimings = 'p',
OptPhysicalAddress = 'P',
OptLongTimings = 'L',
OptShortTimings = 'S',
@@ -58,7 +59,8 @@ static struct option long_options[] = {
{ "help", no_argument, 0, OptHelp },
{ "output-format", required_argument, 0, OptOutputFormat },
{ "extract", no_argument, 0, OptExtract },
- { "preferred-timing", no_argument, 0, OptPreferredTiming },
+ { "native-timings", no_argument, 0, OptNativeTimings },
+ { "preferred-timings", no_argument, 0, OptPreferredTimings },
{ "physical-address", no_argument, 0, OptPhysicalAddress },
{ "skip-hex-dump", no_argument, 0, OptSkipHexDump },
{ "skip-sha", no_argument, 0, OptSkipSHA },
@@ -90,7 +92,8 @@ static void usage(void)
" warnings are reported at the end.\n"
" -C, --check-inline check if the EDID conforms to the standards, failures and\n"
" warnings are reported inline.\n"
- " -p, --preferred-timing report the preferred timing\n"
+ " -n, --native-timings report the native timings\n"
+ " -p, --preferred-timings report the preferred timings\n"
" -P, --physical-address only report the CEC physical address\n"
" -S, --short-timings report all video timings in a short format\n"
" -L, --long-timings report all video timings in a long format\n"
@@ -139,7 +142,7 @@ static void show_msgs(bool is_warn)
}
if (s_msgs[EDID_MAX_BLOCKS][is_warn].empty())
return;
- printf("All Blocks:\n%s",
+ printf("EDID:\n%s",
s_msgs[EDID_MAX_BLOCKS][is_warn].c_str());
}
@@ -187,35 +190,14 @@ void calc_ratio(struct timings *t)
t->vratio = t->vact / d;
}
-std::string edid_state::dtd_type()
+std::string edid_state::dtd_type(unsigned cnt)
{
- unsigned len = std::to_string(preparse_total_dtds).length();
+ unsigned len = std::to_string(cta.preparse_total_dtds).length();
char buf[16];
- sprintf(buf, "DTD %*u", len, dtd_cnt);
+ sprintf(buf, "DTD %*u", len, cnt);
return buf;
}
-bool edid_state::match_timings(const timings &t1, const timings &t2)
-{
- if (t1.hact != t2.hact ||
- t1.vact != t2.vact ||
- t1.rb != t2.rb ||
- t1.interlaced != t2.interlaced ||
- t1.hfp != t2.hfp ||
- t1.hbp != t2.hbp ||
- t1.hsync != t2.hsync ||
- t1.pos_pol_hsync != t2.pos_pol_hsync ||
- t1.hratio != t2.hratio ||
- t1.vfp != t2.vfp ||
- t1.vbp != t2.vbp ||
- t1.vsync != t2.vsync ||
- t1.pos_pol_vsync != t2.pos_pol_vsync ||
- t1.vratio != t2.vratio ||
- t1.pixclk_khz != t2.pixclk_khz)
- return false;
- return true;
-}
-
static void or_str(std::string &s, const std::string &flag, unsigned &num_flags)
{
if (!num_flags)
@@ -466,19 +448,19 @@ bool edid_state::print_timings(const char *prefix, const struct timings *t,
fail("0 or negative horizontal back porch.\n");
if (t->vbp <= 0)
fail("0 or negative vertical back porch.\n");
- if ((!max_display_width_mm && t->hsize_mm) ||
- (!max_display_height_mm && t->vsize_mm)) {
+ if ((!base.max_display_width_mm && t->hsize_mm) ||
+ (!base.max_display_height_mm && t->vsize_mm)) {
fail("Mismatch of image size vs display size: image size is set, but not display size.\n");
} else if (!t->hsize_mm && !t->vsize_mm) {
/* this is valid */
- } else if (t->hsize_mm > max_display_width_mm + 9 ||
- t->vsize_mm > max_display_height_mm + 9) {
+ } else if (t->hsize_mm > base.max_display_width_mm + 9 ||
+ t->vsize_mm > base.max_display_height_mm + 9) {
fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
- t->hsize_mm, t->vsize_mm, max_display_width_mm, max_display_height_mm);
- } else if (t->hsize_mm < max_display_width_mm - 9 &&
- t->vsize_mm < max_display_height_mm - 9) {
+ t->hsize_mm, t->vsize_mm, base.max_display_width_mm, base.max_display_height_mm);
+ } else if (t->hsize_mm < base.max_display_width_mm - 9 &&
+ t->vsize_mm < base.max_display_height_mm - 9) {
fail("Mismatch of image size %ux%u mm vs display size %ux%u mm.\n",
- t->hsize_mm, t->vsize_mm, max_display_width_mm, max_display_height_mm);
+ t->hsize_mm, t->vsize_mm, base.max_display_width_mm, base.max_display_height_mm);
}
if (refresh) {
min_vert_freq_hz = min(min_vert_freq_hz, refresh);
@@ -960,8 +942,8 @@ void edid_state::parse_block_map(const unsigned char *x)
printf("%s\n", block.c_str());
if (block_nr == 1)
- saw_block_1 = true;
- else if (!saw_block_1)
+ block_map.saw_block_1 = true;
+ else if (!block_map.saw_block_1)
fail("No EDID Block Map Extension found in block 1.\n");
if (block_nr > 1)
@@ -989,12 +971,11 @@ void edid_state::preparse_extension(const unsigned char *x)
{
switch (x[0]) {
case 0x02:
+ has_cta = true;
preparse_cta_block(x);
break;
- case 0x10:
- preparse_vtb_ext_block(x);
- break;
case 0x70:
+ has_dispid = true;
preparse_displayid_block(x);
break;
}
@@ -1006,6 +987,8 @@ void edid_state::parse_extension(const unsigned char *x)
data_block.clear();
printf("\n");
+ if (block_nr && x[0] == 0)
+ block = "Unknown EDID Extension Block 0x00";
printf("Block %u, %s:\n", block_nr, block.c_str());
switch (x[0]) {
@@ -1033,7 +1016,6 @@ void edid_state::parse_extension(const unsigned char *x)
fail("Must be used in block 1 and 128.\n");
break;
default:
- printf("%s\n", block.c_str());
hex_block(" ", x, EDID_PAGE_SIZE);
fail("Unknown Extension Block.\n");
break;
@@ -1050,10 +1032,10 @@ int edid_state::parse_edid()
if (options[OptPhysicalAddress]) {
printf("%x.%x.%x.%x\n",
- (preparsed_phys_addr >> 12) & 0xf,
- (preparsed_phys_addr >> 8) & 0xf,
- (preparsed_phys_addr >> 4) & 0xf,
- preparsed_phys_addr & 0xf);
+ (cta.preparsed_phys_addr >> 12) & 0xf,
+ (cta.preparsed_phys_addr >> 8) & 0xf,
+ (cta.preparsed_phys_addr >> 4) & 0xf,
+ cta.preparsed_phys_addr & 0xf);
return 0;
}
@@ -1081,88 +1063,65 @@ int edid_state::parse_edid()
block = "";
block_nr = EDID_MAX_BLOCKS;
- if (uses_gtf && !supports_gtf)
- fail("GTF timings are used, but the EDID does not signal GTF support.\n");
- if (uses_cvt && !supports_cvt)
- fail("CVT timings are used, but the EDID does not signal CVT support.\n");
- /*
- * Allow for regular rounding of vertical and horizontal frequencies.
- * The spec says that the pixelclock shall be rounded up, so there is
- * no need to take rounding into account.
- */
- if (has_display_range_descriptor &&
- (min_vert_freq_hz + 0.5 < min_display_vert_freq_hz ||
- (max_vert_freq_hz >= max_display_vert_freq_hz + 0.5 && max_display_vert_freq_hz) ||
- min_hor_freq_hz + 500 < min_display_hor_freq_hz ||
- (max_hor_freq_hz >= max_display_hor_freq_hz + 500 && max_display_hor_freq_hz) ||
- (max_pixclk_khz > max_display_pixclk_khz && max_display_pixclk_khz))) {
- /*
- * Check if it is really out of range, or if it could be a rounding error.
- * The EDID spec is not very clear about rounding.
- */
- bool fail =
- min_vert_freq_hz + 1.0 <= min_display_vert_freq_hz ||
- (max_vert_freq_hz >= max_display_vert_freq_hz + 1.0 && max_display_vert_freq_hz) ||
- min_hor_freq_hz + 1000 <= min_display_hor_freq_hz ||
- (max_hor_freq_hz >= max_display_hor_freq_hz + 1000 && max_display_hor_freq_hz) ||
- (max_pixclk_khz >= max_display_pixclk_khz + 10000 && max_display_pixclk_khz);
-
- std::string err("Some timings are out of range of the Monitor Ranges:\n");
- char buf[512];
-
- if (min_vert_freq_hz + 0.5 < min_display_vert_freq_hz ||
- (max_vert_freq_hz >= max_display_vert_freq_hz + 0.5 && max_display_vert_freq_hz)) {
- sprintf(buf, " Vertical Freq: %.3f - %.3f Hz (Monitor: %u.000 - %u.000 Hz)\n",
- min_vert_freq_hz, max_vert_freq_hz,
- min_display_vert_freq_hz, max_display_vert_freq_hz);
- err += buf;
- }
- if (min_hor_freq_hz + 500 < min_display_hor_freq_hz ||
- (max_hor_freq_hz >= max_display_hor_freq_hz + 500 && max_display_hor_freq_hz)) {
- sprintf(buf, " Horizontal Freq: %.3f - %.3f kHz (Monitor: %.3f - %.3f kHz)\n",
- min_hor_freq_hz / 1000.0, max_hor_freq_hz / 1000.0,
- min_display_hor_freq_hz / 1000.0, max_display_hor_freq_hz / 1000.0);
- err += buf;
- }
+ if (has_cta)
+ cta_resolve_svrs();
- if (max_pixclk_khz >= max_display_pixclk_khz && max_display_pixclk_khz) {
- sprintf(buf, " Maximum Clock: %.3f MHz (Monitor: %.3f MHz)\n",
- max_pixclk_khz / 1000.0, max_display_pixclk_khz / 1000.0);
- err += buf;
- }
+ if (options[OptPreferredTimings] && base.preferred_timing.is_valid()) {
+ printf("\n----------------\n");
+ printf("\nPreferred Video Timing (Block 0):\n");
+ print_timings(" ", base.preferred_timing, true);
+ }
- if (!fail)
- err += " Could be due to a Monitor Range off-by-one rounding issue\n";
+ if (options[OptNativeTimings] &&
+ base.preferred_timing.is_valid() && base.preferred_is_also_native) {
+ printf("\n----------------\n");
+ printf("\nNative Video Timing (Block 0):\n");
+ print_timings(" ", base.preferred_timing, true);
+ }
+
+ if (options[OptPreferredTimings] && !cta.preferred_timings.empty()) {
+ printf("\n----------------\n");
+ printf("\nPreferred Video Timing%s (CTA-861):\n",
+ cta.preferred_timings.size() > 1 ? "s" : "");
+ for (vec_timings_ext::iterator iter = cta.preferred_timings.begin();
+ iter != cta.preferred_timings.end(); ++iter)
+ print_timings(" ", *iter, true);
+ }
- /*
- * EDID 1.4 states (in an Errata) that explicitly defined
- * timings supersede the monitor range definition.
- */
- msg(!fail || edid_minor >= 4, "%s", err.c_str());
+ if (options[OptNativeTimings] && !cta.native_timings.empty()) {
+ printf("\n----------------\n");
+ printf("\nNative Video Timing%s (CTA-861):\n",
+ cta.native_timings.size() > 1 ? "s" : "");
+ for (vec_timings_ext::iterator iter = cta.native_timings.begin();
+ iter != cta.native_timings.end(); ++iter)
+ print_timings(" ", *iter, true);
}
- if (options[OptPreferredTiming]) {
+ if (options[OptPreferredTimings] && !dispid.preferred_timings.empty()) {
printf("\n----------------\n");
- printf("\nPreferred Video Timing:\n");
- print_timings(" ", &preferred_timings,
- preferred_type.c_str(),
- preferred_flags.c_str(), true);
+ printf("\nPreferred Video Timing%s (DisplayID):\n",
+ dispid.preferred_timings.size() > 1 ? "s" : "");
+ for (vec_timings_ext::iterator iter = dispid.preferred_timings.begin();
+ iter != dispid.preferred_timings.end(); ++iter)
+ print_timings(" ", *iter, true);
}
if (!options[OptCheck] && !options[OptCheckInline])
return 0;
+ check_base_block();
+ if (has_cta)
+ check_cta_blocks();
+ if (has_dispid)
+ check_displayid_blocks();
+
printf("\n----------------\n");
if (!options[OptSkipSHA]) {
-#ifdef SHA
#define STR(x) #x
#define STRING(x) STR(x)
printf("\nedid-decode SHA: %s\n", STRING(SHA));
-#else
- printf("\nedid-decode SHA: not available\n");
-#endif
}
if (options[OptCheck]) {
diff --git a/edid-decode.h b/edid-decode.h
index 5b98c36..9c2044d 100644
--- a/edid-decode.h
+++ b/edid-decode.h
@@ -56,6 +56,34 @@ struct timings {
bool ycbcr420; // YCbCr 4:2:0 encoding
};
+struct timings_ext {
+ timings_ext()
+ {
+ memset(&t, 0, sizeof(t));
+ }
+ timings_ext(unsigned svr, const std::string &_type)
+ {
+ memset(&t, 0, sizeof(t));
+ t.hact = svr;
+ type = _type;
+ }
+ timings_ext(const timings &_t, const std::string &_type, const std::string &_flags)
+ {
+ t = _t;
+ type = _type;
+ flags = _flags;
+ }
+
+ bool is_valid() const { return t.hact; }
+ bool has_svr() const { return t.hact && !t.vact; }
+ unsigned svr() const { return t.hact; }
+ timings t;
+ std::string type;
+ std::string flags;
+};
+
+typedef std::vector<timings_ext> vec_timings_ext;
+
struct edid_state {
edid_state()
{
@@ -65,40 +93,46 @@ struct edid_state {
min_hor_freq_hz = 0xffffff;
min_vert_freq_hz = 0xffffffff;
warnings = failures = 0;
- memset(&preferred_timings, 0, sizeof(preferred_timings));
- preparse_total_dtds = 0;
+ has_cta = has_dispid = false;
// Base block state
- edid_minor = 0;
- has_name_descriptor = has_display_range_descriptor =
- has_serial_number = has_serial_string =
- supports_continuous_freq = supports_gtf =
- supports_cvt = uses_gtf = uses_cvt = has_spwg =
- seen_non_detailed_descriptor = false;
- detailed_block_cnt = dtd_cnt = 0;
-
- min_display_hor_freq_hz = max_display_hor_freq_hz =
- min_display_vert_freq_hz = max_display_vert_freq_hz =
- max_display_pixclk_khz = max_display_width_mm =
- max_display_height_mm = 0;
+ base.edid_minor = 0;
+ base.has_name_descriptor = base.has_display_range_descriptor =
+ base.has_serial_number = base.has_serial_string =
+ base.supports_continuous_freq = base.supports_gtf =
+ base.supports_cvt = base.uses_gtf = base.uses_cvt =
+ base.has_640x480p60_est_timing = base.has_spwg =
+ base.seen_non_detailed_descriptor =
+ base.preferred_is_also_native = false;
+ base.detailed_block_cnt = base.dtd_cnt = 0;
+
+ base.min_display_hor_freq_hz = base.max_display_hor_freq_hz =
+ base.min_display_vert_freq_hz = base.max_display_vert_freq_hz =
+ base.max_display_pixclk_khz = base.max_display_width_mm =
+ base.max_display_height_mm = 0;
// CTA-861 block state
- has_640x480p60_est_timing = has_cta861_vic_1 =
- first_svd_might_be_preferred = has_hdmi = false;
- last_block_was_hdmi_vsdb = have_hf_vsdb = have_hf_scdb = 0;
- first_block = 1;
- supported_hdmi_vic_codes = supported_hdmi_vic_vsb_codes = 0;
- memset(vics, 0, sizeof(vics));
- memset(preparsed_has_vic, 0, sizeof(preparsed_has_vic));
- preparsed_phys_addr = 0xffff;
+ cta.has_vic_1 = cta.first_svd_might_be_preferred =
+ cta.has_hdmi = cta.has_vcdb = cta.has_vfpdb = false;
+ cta.last_block_was_hdmi_vsdb = cta.have_hf_vsdb = cta.have_hf_scdb = false;
+ cta.first_block = cta.first_svd = true;
+ cta.supported_hdmi_vic_codes = cta.supported_hdmi_vic_vsb_codes = 0;
+ memset(cta.vics, 0, sizeof(cta.vics));
+ memset(cta.preparsed_has_vic, 0, sizeof(cta.preparsed_has_vic));
+ cta.preparsed_phys_addr = 0xffff;
+ cta.preparse_total_dtds = 0;
// DisplayID block state
- preparse_color_ids = preparse_xfer_ids = 0;
- preparse_displayid_blocks = 0;
- displayid_base_block = true;
+ dispid.version = 0;
+ dispid.preparse_color_ids = dispid.preparse_xfer_ids = 0;
+ dispid.preparse_displayid_blocks = 0;
+ dispid.is_base_block = true;
+ dispid.is_display = dispid.has_product_identification =
+ dispid.has_display_parameters = dispid.has_type_1_7 =
+ dispid.has_display_interface_features = false;
// Block Map block state
- saw_block_1 = false;
+ block_map.saw_block_1 = false;
}
// Global state
@@ -107,10 +141,8 @@ struct edid_state {
unsigned block_nr;
std::string block;
std::string data_block;
- timings preferred_timings;
- std::string preferred_type;
- std::string preferred_flags;
- unsigned preparse_total_dtds;
+ bool has_cta;
+ bool has_dispid;
unsigned min_hor_freq_hz;
unsigned max_hor_freq_hz;
@@ -122,58 +154,88 @@ struct edid_state {
unsigned failures;
// Base block state
- unsigned edid_minor;
- bool has_name_descriptor;
- bool has_display_range_descriptor;
- bool has_serial_number;
- bool has_serial_string;
- bool supports_continuous_freq;
- bool supports_gtf;
- bool supports_cvt;
- bool uses_gtf;
- bool uses_cvt;
- bool has_spwg;
- unsigned detailed_block_cnt;
- unsigned dtd_cnt;
- bool seen_non_detailed_descriptor;
-
- unsigned min_display_hor_freq_hz;
- unsigned max_display_hor_freq_hz;
- unsigned min_display_vert_freq_hz;
- unsigned max_display_vert_freq_hz;
- unsigned max_display_pixclk_khz;
- unsigned max_display_width_mm;
- unsigned max_display_height_mm;
+ struct {
+ unsigned edid_minor;
+ bool has_name_descriptor;
+ bool has_display_range_descriptor;
+ bool has_serial_number;
+ bool has_serial_string;
+ bool supports_continuous_freq;
+ bool supports_gtf;
+ bool supports_cvt;
+ bool uses_gtf;
+ bool uses_cvt;
+ bool has_spwg;
+ unsigned detailed_block_cnt;
+ unsigned dtd_cnt;
+ bool seen_non_detailed_descriptor;
+ bool has_640x480p60_est_timing;
+ bool preferred_is_also_native;
+ timings_ext preferred_timing;
+
+ unsigned min_display_hor_freq_hz;
+ unsigned max_display_hor_freq_hz;
+ unsigned min_display_vert_freq_hz;
+ unsigned max_display_vert_freq_hz;
+ unsigned max_display_pixclk_khz;
+ unsigned max_display_width_mm;
+ unsigned max_display_height_mm;
+ } base;
// CTA-861 block state
- bool has_640x480p60_est_timing;
- bool has_cta861_vic_1;
- bool first_svd_might_be_preferred;
- bool has_hdmi;
- unsigned short preparsed_phys_addr;
- int last_block_was_hdmi_vsdb;
- int have_hf_vsdb, have_hf_scdb;
- int first_block;
- unsigned supported_hdmi_vic_codes;
- unsigned supported_hdmi_vic_vsb_codes;
- unsigned short vics[256][2];
- bool preparsed_has_vic[2][256];
- std::vector<unsigned char> preparsed_svds[2];
+ struct {
+ unsigned preparse_total_dtds;
+ vec_timings_ext vec_dtds;
+ vec_timings_ext preferred_timings;
+ vec_timings_ext native_timings;
+ bool has_vic_1;
+ bool first_svd_might_be_preferred;
+ unsigned char byte3;
+ bool has_hdmi;
+ bool has_vcdb;
+ bool has_vfpdb;
+ unsigned short preparsed_phys_addr;
+ bool last_block_was_hdmi_vsdb;
+ bool have_hf_vsdb, have_hf_scdb;
+ bool first_block;
+ bool first_svd;
+ unsigned supported_hdmi_vic_codes;
+ unsigned supported_hdmi_vic_vsb_codes;
+ unsigned short vics[256][2];
+ bool preparsed_has_vic[2][256];
+ std::vector<unsigned char> preparsed_svds[2];
+ } cta;
// DisplayID block state
- unsigned short preparse_color_ids;
- unsigned short preparse_xfer_ids;
- unsigned preparse_displayid_blocks;
- bool displayid_base_block;
+ struct {
+ unsigned char version;
+ unsigned short preparse_color_ids;
+ unsigned short preparse_xfer_ids;
+ unsigned preparse_displayid_blocks;
+ bool is_base_block;
+ bool is_display;
+ bool has_product_identification;
+ bool has_display_parameters;
+ bool has_type_1_7;
+ bool has_display_interface_features;
+ vec_timings_ext preferred_timings;
+ } dispid;
// Block Map block state
- bool saw_block_1;
+ struct {
+ bool saw_block_1;
+ } block_map;
- std::string dtd_type();
+ std::string dtd_type(unsigned dtd);
+ std::string dtd_type() { return dtd_type(base.dtd_cnt); }
bool print_timings(const char *prefix, const struct timings *t,
const char *type, const char *flags = "",
bool detailed = false);
- bool match_timings(const timings &t1, const timings &t2);
+ bool print_timings(const char *prefix, const struct timings_ext &t,
+ bool detailed = false)
+ {
+ return print_timings(prefix, &t.t, t.type.c_str(), t.flags.c_str(), detailed);
+ };
void edid_gtf_mode(unsigned refresh, struct timings &t);
void edid_cvt_mode(unsigned refresh, struct timings &t);
void detailed_cvt_descriptor(const char *prefix, const unsigned char *x, bool first);
@@ -181,11 +243,14 @@ struct edid_state {
bool gtf_only = false, unsigned vrefresh_offset = 60);
void detailed_display_range_limits(const unsigned char *x);
void detailed_epi(const unsigned char *x);
- void detailed_timings(const char *prefix, const unsigned char *x);
+ void detailed_timings(const char *prefix, const unsigned char *x,
+ bool base_or_cta = true);
void detailed_block(const unsigned char *x);
void parse_base_block(const unsigned char *x);
+ void check_base_block();
void print_vic_index(const char *prefix, unsigned idx, const char *suffix, bool ycbcr420 = false);
+ void cta_vcdb(const unsigned char *x, unsigned length);
void cta_svd(const unsigned char *x, unsigned n, bool for_ycbcr420);
void cta_y420cmdb(const unsigned char *x, unsigned length);
void cta_vfpdb(const unsigned char *x, unsigned length);
@@ -194,6 +259,9 @@ struct edid_state {
void cta_block(const unsigned char *x);
void preparse_cta_block(const unsigned char *x);
void parse_cta_block(const unsigned char *x);
+ void cta_resolve_svr(vec_timings_ext::iterator iter);
+ void cta_resolve_svrs();
+ void check_cta_blocks();
void parse_digital_interface(const unsigned char *x);
void parse_display_device(const unsigned char *x);
@@ -201,6 +269,12 @@ struct edid_state {
void parse_display_xfer(const unsigned char *x);
void parse_di_ext_block(const unsigned char *x);
+ void parse_displayid_product_id(const unsigned char *x);
+ std::string product_type(unsigned char x, bool heading);
+ void parse_displayid_interface_features(const unsigned char *x);
+ void parse_displayid_parameters(const unsigned char *x);
+ void parse_displayid_parameters_v2(const unsigned char *x);
+ void parse_displayid_display_intf(const unsigned char *x);
void parse_displayid_color_characteristics(const unsigned char *x);
void parse_displayid_transfer_characteristics(const unsigned char *x);
void parse_displayid_stereo_display_intf(const unsigned char *x);
@@ -214,8 +288,8 @@ struct edid_state {
void preparse_displayid_block(const unsigned char *x);
void parse_displayid_block(const unsigned char *x);
void parse_displayid_cta_data_block(const unsigned char *x);
+ void check_displayid_blocks();
- void preparse_vtb_ext_block(const unsigned char *x);
void parse_vtb_ext_block(const unsigned char *x);
void parse_ls_ext_block(const unsigned char *x);
diff --git a/parse-base-block.cpp b/parse-base-block.cpp
index 9f0147e..90d240b 100644
--- a/parse-base-block.cpp
+++ b/parse-base-block.cpp
@@ -713,7 +713,7 @@ void edid_state::detailed_cvt_descriptor(const char *prefix, const unsigned char
if (!first && !memcmp(x, empty, 3))
return;
- uses_cvt = true;
+ base.uses_cvt = true;
cvt_t.vact = x[0];
if (!cvt_t.vact)
fail("CVT byte 0 is 0, which is a reserved value.\n");
@@ -841,7 +841,7 @@ void edid_state::print_standard_timing(const char *prefix, unsigned char b1, uns
hact = (b1 + 31) * 8;
switch ((b2 >> 6) & 0x3) {
case 0x00:
- if (gtf_only || edid_minor >= 3) {
+ if (gtf_only || base.edid_minor >= 3) {
hratio = 16;
vratio = 10;
} else {
@@ -871,19 +871,19 @@ void edid_state::print_standard_timing(const char *prefix, unsigned char b1, uns
formula.hratio = hratio;
formula.vratio = vratio;
- if (!gtf_only && edid_minor >= 4) {
- uses_cvt = true;
+ if (!gtf_only && base.edid_minor >= 4) {
+ base.uses_cvt = true;
edid_cvt_mode(refresh, formula);
print_timings(prefix, &formula, "CVT ", "EDID 1.4 source");
/*
* A EDID 1.3 source will assume GTF, so both GTF and CVT
* have to be supported.
*/
- uses_gtf = true;
+ base.uses_gtf = true;
edid_gtf_mode(refresh, formula);
print_timings(prefix, &formula, "GTF ", "EDID 1.3 source");
- } else if (gtf_only || edid_minor >= 2) {
- uses_gtf = true;
+ } else if (gtf_only || base.edid_minor >= 2) {
+ base.uses_gtf = true;
edid_gtf_mode(refresh, formula);
print_timings(prefix, &formula, "GTF ");
} else {
@@ -904,12 +904,12 @@ void edid_state::detailed_display_range_limits(const unsigned char *x)
data_block = "Display Range Limits";
printf(" %s:\n", data_block.c_str());
- has_display_range_descriptor = 1;
+ base.has_display_range_descriptor = 1;
/*
* XXX todo: implement feature flags, vtd blocks
* XXX check: ranges are well-formed; block termination if no vtd
*/
- if (edid_minor >= 4) {
+ if (base.edid_minor >= 4) {
if (x[4] & 0x02) {
v_max_offset = 255;
if (x[4] & 0x01) {
@@ -930,33 +930,33 @@ void edid_state::detailed_display_range_limits(const unsigned char *x)
switch (x[10]) {
case 0x00: /* default gtf */
range_class = "GTF";
- if (edid_minor >= 4 && !supports_continuous_freq)
+ if (base.edid_minor >= 4 && !base.supports_continuous_freq)
fail("GTF can't be combined with non-continuous frequencies.\n");
- supports_gtf = true;
+ base.supports_gtf = true;
break;
case 0x01: /* range limits only */
range_class = "Bare Limits";
- if (edid_minor < 4)
+ if (base.edid_minor < 4)
fail("'%s' is not allowed for EDID < 1.4.\n", range_class.c_str());
break;
case 0x02: /* secondary gtf curve */
range_class = "Secondary GTF";
- if (edid_minor >= 4 && !supports_continuous_freq)
+ if (base.edid_minor >= 4 && !base.supports_continuous_freq)
fail("GTF can't be combined with non-continuous frequencies.\n");
- supports_gtf = true;
+ base.supports_gtf = true;
has_sec_gtf = true;
break;
case 0x04: /* cvt */
range_class = "CVT";
is_cvt = 1;
- if (edid_minor < 4)
+ if (base.edid_minor < 4)
fail("'%s' is not allowed for EDID < 1.4.\n", range_class.c_str());
- else if (!supports_continuous_freq)
+ else if (!base.supports_continuous_freq)
fail("CVT can't be combined with non-continuous frequencies.\n");
- if (edid_minor >= 4) {
+ if (base.edid_minor >= 4) {
/* GTF is implied if CVT is signaled */
- supports_gtf = true;
- supports_cvt = true;
+ base.supports_gtf = true;
+ base.supports_cvt = true;
}
break;
default: /* invalid */
@@ -967,12 +967,12 @@ void edid_state::detailed_display_range_limits(const unsigned char *x)
if (x[5] + v_min_offset > x[6] + v_max_offset)
fail("Min vertical rate > max vertical rate.\n");
- min_display_vert_freq_hz = x[5] + v_min_offset;
- max_display_vert_freq_hz = x[6] + v_max_offset;
+ base.min_display_vert_freq_hz = x[5] + v_min_offset;
+ base.max_display_vert_freq_hz = x[6] + v_max_offset;
if (x[7] + h_min_offset > x[8] + h_max_offset)
fail("Min horizontal freq > max horizontal freq.\n");
- min_display_hor_freq_hz = (x[7] + h_min_offset) * 1000;
- max_display_hor_freq_hz = (x[8] + h_max_offset) * 1000;
+ base.min_display_hor_freq_hz = (x[7] + h_min_offset) * 1000;
+ base.max_display_hor_freq_hz = (x[8] + h_max_offset) * 1000;
printf(" Monitor ranges (%s): %d-%d Hz V, %d-%d kHz H",
range_class.c_str(),
x[5] + v_min_offset, x[6] + v_max_offset,
@@ -981,20 +981,20 @@ void edid_state::detailed_display_range_limits(const unsigned char *x)
// For EDID 1.3 the horizontal frequency maxes out at 255 kHz.
// So to avoid false range-check warnings due to this limitation,
// just double the max_display_hor_freq_hz in this case.
- if (edid_minor < 4 && x[8] == 0xff)
- max_display_hor_freq_hz *= 2;
+ if (base.edid_minor < 4 && x[8] == 0xff)
+ base.max_display_hor_freq_hz *= 2;
// For EDID 1.3 the vertical frequency maxes out at 255 Hz.
// So to avoid false range-check warnings due to this limitation,
// just double the max_display_vert_freq_hz in this case.
- if (edid_minor < 4 && x[6] == 0xff)
- max_display_vert_freq_hz *= 2;
+ if (base.edid_minor < 4 && x[6] == 0xff)
+ base.max_display_vert_freq_hz *= 2;
if (x[9]) {
- max_display_pixclk_khz = x[9] * 10000;
+ base.max_display_pixclk_khz = x[9] * 10000;
printf(", max dotclock %d MHz\n", x[9] * 10);
} else {
- if (edid_minor >= 4)
+ if (base.edid_minor >= 4)
fail("EDID 1.4 block does not set max dotclock.\n");
printf("\n");
}
@@ -1205,14 +1205,17 @@ void edid_state::detailed_epi(const unsigned char *x)
printf(" EPI Version: %u.%u\n", (x[17] & 0xf0) >> 4, x[17] & 0x0f);
}
-void edid_state::detailed_timings(const char *prefix, const unsigned char *x)
+void edid_state::detailed_timings(const char *prefix, const unsigned char *x,
+ bool base_or_cta)
{
struct timings t = {};
unsigned hbl, vbl;
std::string s_sync, s_flags;
- dtd_cnt++;
- data_block = "Detailed Timing Descriptor #" + std::to_string(dtd_cnt);
+ // Only count DTDs in base block 0 or CTA-861 extension blocks
+ if (base_or_cta)
+ base.dtd_cnt++;
+ data_block = "Detailed Timing Descriptor #" + std::to_string(base.dtd_cnt);
t.pixclk_khz = (x[0] + (x[1] << 8)) * 10;
if (t.pixclk_khz < 10000) {
printf("%sDetailed mode: ", prefix);
@@ -1240,7 +1243,7 @@ void edid_state::detailed_timings(const char *prefix, const unsigned char *x)
unsigned char flags = x[17];
- if (has_spwg && detailed_block_cnt == 2)
+ if (base.has_spwg && base.detailed_block_cnt == 2)
flags = *(x - 1);
switch ((flags & 0x18) >> 3) {
@@ -1279,7 +1282,7 @@ void edid_state::detailed_timings(const char *prefix, const unsigned char *x)
t.pos_pol_vsync = true;
s_sync = t.pos_pol_hsync ? "+hsync " : "-hsync ";
s_sync += t.pos_pol_vsync ? "+vsync " : "-vsync ";
- if (has_spwg && (flags & 0x01))
+ if (base.has_spwg && (flags & 0x01))
s_flags = "DE timing only";
break;
}
@@ -1293,7 +1296,7 @@ void edid_state::detailed_timings(const char *prefix, const unsigned char *x)
if (t.hact == 1920 && t.vact == 1080 && t.pixclk_khz == 72000 &&
t.hfp == 32 && t.hsync == 168 && t.hbp == 184 && !t.hborder &&
t.vfp == 23 && t.vsync == 5 && t.vbp == 57 && !t.vborder &&
- !has_spwg && preparsed_has_vic[0][39] && (flags & 0x1e) == 0x1a)
+ !base.has_spwg && cta.preparsed_has_vic[0][39] && (flags & 0x1e) == 0x1a)
t.even_vtotal = true;
}
switch (flags & 0x61) {
@@ -1324,19 +1327,24 @@ void edid_state::detailed_timings(const char *prefix, const unsigned char *x)
calc_ratio(&t);
- bool ok = print_timings(prefix, &t, dtd_type().c_str(), s_flags.c_str(), true);
+ std::string s_type = base_or_cta ? dtd_type() : "DTD";
+ bool ok = print_timings(prefix, &t, s_type.c_str(), s_flags.c_str(), true);
+ timings_ext te(t, s_type, s_flags);
- if (block_nr == 0 && dtd_cnt == 1) {
- preferred_timings = t;
- preferred_type = dtd_type();
- preferred_flags = s_flags;
+ if (block_nr == 0 && base.dtd_cnt == 1) {
+ te.type = "DTD 1";
+ base.preferred_timing = te;
+ cta.preferred_timings.push_back(te);
+ cta.native_timings.push_back(te);
}
+ if (base_or_cta)
+ cta.vec_dtds.push_back(te);
- if ((max_display_width_mm && !t.hsize_mm) ||
- (max_display_height_mm && !t.vsize_mm)) {
+ if ((base.max_display_width_mm && !t.hsize_mm) ||
+ (base.max_display_height_mm && !t.vsize_mm)) {
fail("Mismatch of image size vs display size: image size is not set, but display size is.\n");
}
- if (has_spwg && detailed_block_cnt == 2)
+ if (base.has_spwg && base.detailed_block_cnt == 2)
printf("%sSPWG Module Revision: %hhu\n", prefix, x[17]);
if (!ok) {
std::string s = prefix;
@@ -1352,27 +1360,27 @@ void edid_state::detailed_block(const unsigned char *x)
unsigned cnt;
unsigned i;
- detailed_block_cnt++;
+ base.detailed_block_cnt++;
if (x[0] || x[1]) {
detailed_timings(" ", x);
- if (seen_non_detailed_descriptor)
+ if (base.seen_non_detailed_descriptor)
fail("Invalid detailed timing descriptor ordering.\n");
return;
}
- data_block = "Display Descriptor #" + std::to_string(detailed_block_cnt);
+ data_block = "Display Descriptor #" + std::to_string(base.detailed_block_cnt);
/* Monitor descriptor block, not detailed timing descriptor. */
if (x[2] != 0) {
/* 1.3, 3.10.3 */
fail("Monitor descriptor block has byte 2 nonzero (0x%02x).\n", x[2]);
}
- if ((edid_minor < 4 || x[3] != 0xfd) && x[4] != 0x00) {
+ if ((base.edid_minor < 4 || x[3] != 0xfd) && x[4] != 0x00) {
/* 1.3, 3.10.3 */
fail("Monitor descriptor block has byte 4 nonzero (0x%02x).\n", x[4]);
}
- seen_non_detailed_descriptor = true;
- if (edid_minor == 0)
+ base.seen_non_detailed_descriptor = true;
+ if (base.edid_minor == 0)
fail("Has descriptor blocks other than detailed timings.\n");
if (!memcmp(x, zero_descr, sizeof(zero_descr))) {
@@ -1472,20 +1480,20 @@ void edid_state::detailed_block(const unsigned char *x)
}
case 0xfc:
data_block = "Display Product Name";
- has_name_descriptor = 1;
+ base.has_name_descriptor = 1;
printf(" %s: '%s'\n", data_block.c_str(), extract_string(x + 5, 13));
return;
case 0xfd:
detailed_display_range_limits(x);
return;
case 0xfe:
- if (!has_spwg || detailed_block_cnt < 3) {
+ if (!base.has_spwg || base.detailed_block_cnt < 3) {
data_block = "Alphanumeric Data String";
printf(" %s: '%s'\n", data_block.c_str(),
extract_string(x + 5, 13));
return;
}
- if (detailed_block_cnt == 3) {
+ if (base.detailed_block_cnt == 3) {
char buf[6] = { 0 };
data_block = "SPWG Descriptor #3";
@@ -1512,7 +1520,7 @@ void edid_state::detailed_block(const unsigned char *x)
data_block = "Display Product Serial Number";
printf(" %s: '%s'\n", data_block.c_str(),
extract_string(x + 5, 13));
- has_serial_string = 1;
+ base.has_serial_string = 1;
return;
default:
printf(" %s Display Descriptor (0x%02hhx):",
@@ -1530,16 +1538,16 @@ void edid_state::parse_base_block(const unsigned char *x)
struct tm *ptm;
int analog, i;
unsigned col_x, col_y;
- int has_preferred_timing = 0;
+ bool has_preferred_timing = false;
data_block = "EDID Structure Version & Revision";
printf(" %s: %hhu.%hhu\n", data_block.c_str(), x[0x12], x[0x13]);
if (x[0x12] == 1) {
- edid_minor = x[0x13];
- if (edid_minor > 4)
- warn("Unknown EDID minor version %u, assuming 1.4 conformance.\n", edid_minor);
- if (edid_minor < 3)
- warn("EDID 1.%u is deprecated, do not use.\n", edid_minor);
+ base.edid_minor = x[0x13];
+ if (base.edid_minor > 4)
+ warn("Unknown EDID minor version %u, assuming 1.4 conformance.\n", base.edid_minor);
+ if (base.edid_minor < 3)
+ warn("EDID 1.%u is deprecated, do not use.\n", base.edid_minor);
} else {
fail("Unknown EDID major version.\n");
}
@@ -1550,8 +1558,8 @@ void edid_state::parse_base_block(const unsigned char *x)
printf(" Manufacturer: %s\n Model: %u\n",
manufacturer_name(x + 0x08),
(unsigned short)(x[0x0a] + (x[0x0b] << 8)));
- has_serial_number = x[0x0c] || x[0x0d] || x[0x0e] || x[0x0f];
- if (has_serial_number)
+ base.has_serial_number = x[0x0c] || x[0x0d] || x[0x0e] || x[0x0f];
+ if (base.has_serial_number)
printf(" Serial Number: %u\n",
(unsigned)(x[0x0c] + (x[0x0d] << 8) +
(x[0x0e] << 16) + (x[0x0f] << 24)));
@@ -1563,11 +1571,11 @@ void edid_state::parse_base_block(const unsigned char *x)
int year = 1990 + x[0x11];
if (week) {
- if (edid_minor <= 3 && week == 0xff)
+ if (base.edid_minor <= 3 && week == 0xff)
fail("EDID 1.3 does not support week 0xff.\n");
// The max week is 53 in EDID 1.3 and 54 in EDID 1.4.
// No idea why there is a difference.
- if (edid_minor <= 3 && week == 54)
+ if (base.edid_minor <= 3 && week == 54)
fail("EDID 1.3 does not support week 54.\n");
if (week != 0xff && week > 54)
fail("Invalid week %u of manufacture.\n", week);
@@ -1588,7 +1596,7 @@ void edid_state::parse_base_block(const unsigned char *x)
if (x[0x14] & 0x80) {
analog = 0;
printf(" Digital display\n");
- if (edid_minor >= 4) {
+ if (base.edid_minor >= 4) {
if ((x[0x14] & 0x70) == 0x00)
printf(" Color depth is undefined\n");
else if ((x[0x14] & 0x70) == 0x70)
@@ -1610,7 +1618,7 @@ void edid_state::parse_base_block(const unsigned char *x)
fail("Digital Video Interface Standard set to reserved value 0x%02x.\n", x[0x14] & 0x0f);
break;
}
- } else if (edid_minor >= 2) {
+ } else if (base.edid_minor >= 2) {
if (x[0x14] & 0x01) {
printf(" DFP 1.x compatible TMDS\n");
}
@@ -1631,7 +1639,7 @@ void edid_state::parse_base_block(const unsigned char *x)
voltage == 1 ? "0.714/0.286" :
"0.7/0.3");
- if (edid_minor >= 4) {
+ if (base.edid_minor >= 4) {
if (x[0x14] & 0x10)
printf(" Blank-to-black setup/pedestal\n");
else
@@ -1655,13 +1663,13 @@ void edid_state::parse_base_block(const unsigned char *x)
if (x[0x15] && x[0x16]) {
printf(" Maximum image size: %u cm x %u cm\n", x[0x15], x[0x16]);
- max_display_width_mm = x[0x15] * 10;
- max_display_height_mm = x[0x16] * 10;
+ base.max_display_width_mm = x[0x15] * 10;
+ base.max_display_height_mm = x[0x16] * 10;
if (x[0x15] < 10 || x[0x16] < 10)
warn("Dubious maximum image size (%ux%u is smaller than 10x10 cm).\n",
x[0x15], x[0x16]);
}
- else if (edid_minor >= 4 && (x[0x15] || x[0x16])) {
+ else if (base.edid_minor >= 4 && (x[0x15] || x[0x16])) {
if (x[0x15])
printf(" Aspect ratio: %f (landscape)\n", 100.0 / (x[0x16] + 99));
else
@@ -1672,7 +1680,7 @@ void edid_state::parse_base_block(const unsigned char *x)
}
if (x[0x17] == 0xff) {
- if (edid_minor >= 4)
+ if (base.edid_minor >= 4)
printf(" Gamma is defined in an extension block\n");
else
/* XXX Technically 1.3 doesn't say this... */
@@ -1687,7 +1695,7 @@ void edid_state::parse_base_block(const unsigned char *x)
printf("\n");
}
- if (analog || edid_minor < 4) {
+ if (analog || base.edid_minor < 4) {
printf(" ");
switch (x[0x18] & 0x18) {
case 0x00: printf("Monochrome or grayscale display\n"); break;
@@ -1719,24 +1727,33 @@ void edid_state::parse_base_block(const unsigned char *x)
if (memcmp(x + 0x19, srgb_chromaticity, sizeof(srgb_chromaticity)))
fail("sRGB is signaled, but the chromaticities do not match.\n");
}
- if (x[0x18] & 0x02) {
- if (edid_minor >= 4)
- printf(" First detailed timing includes the native pixel format and preferred refresh rate\n");
- else
- printf(" First detailed timing is preferred timing\n");
- has_preferred_timing = 1;
- } else if (edid_minor >= 4) {
+ if (base.edid_minor >= 4) {
/* 1.4 always has a preferred timing and this bit means something else. */
- has_preferred_timing = 1;
+ has_preferred_timing = true;
+ base.preferred_is_also_native = x[0x18] & 0x02;
+ printf(" First detailed timing %s the native pixel format and preferred refresh rate\n",
+ base.preferred_is_also_native ? "includes" : "does not include");
+ } else {
+ if (x[0x18] & 0x02) {
+ printf(" First detailed timing is the preferred timing\n");
+ has_preferred_timing = true;
+ // 1.3 recommends that the preferred timing corresponds to the
+ // native timing, but it is not a requirement.
+ // That said, we continue with the assumption that it actually
+ // is the native timing.
+ base.preferred_is_also_native = true;
+ } else if (base.edid_minor == 3) {
+ fail("EDID 1.3 requires that the first detailed timing is the preferred timing\n");
+ }
}
if (x[0x18] & 0x01) {
- if (edid_minor >= 4) {
- supports_continuous_freq = true;
+ if (base.edid_minor >= 4) {
+ base.supports_continuous_freq = true;
printf(" Display is continuous frequency\n");
} else {
printf(" Supports GTF timings within operating range\n");
- supports_gtf = true;
+ base.supports_gtf = true;
}
}
@@ -1781,7 +1798,7 @@ void edid_state::parse_base_block(const unsigned char *x)
} else {
printf(" %s: none\n", data_block.c_str());
}
- has_640x480p60_est_timing = x[0x23] & 0x20;
+ base.has_640x480p60_est_timing = x[0x23] & 0x20;
data_block = "Standard Timings";
bool found = false;
@@ -1809,11 +1826,11 @@ void edid_state::parse_base_block(const unsigned char *x)
!x[0x5a] && !x[0x5b] && x[0x5d] == 0xfe &&
!x[0x6c] && !x[0x6d] && x[0x6f] == 0xfe &&
(x[0x79] == 1 || x[0x79] == 2) && x[0x7a] <= 1)
- has_spwg = true;
+ base.has_spwg = true;
- for (unsigned i = 0; i < (has_spwg ? 2 : 4); i++)
+ for (unsigned i = 0; i < (base.has_spwg ? 2 : 4); i++)
if (x[0x36 + i * 18] || x[0x37 + i * 18])
- preparse_total_dtds++;
+ cta.preparse_total_dtds++;
data_block = "Detailed Timing Descriptors";
printf(" %s:\n", data_block.c_str());
@@ -1821,7 +1838,11 @@ void edid_state::parse_base_block(const unsigned char *x)
detailed_block(x + 0x48);
detailed_block(x + 0x5a);
detailed_block(x + 0x6c);
- has_spwg = false;
+ base.has_spwg = false;
+ if (!base.preferred_is_also_native) {
+ cta.native_timings.clear();
+ base.preferred_timing = timings_ext();
+ }
data_block = block;
if (x[0x7e])
@@ -1833,11 +1854,76 @@ void edid_state::parse_base_block(const unsigned char *x)
block = block_name(0x00);
data_block.clear();
do_checksum("", x, EDID_PAGE_SIZE);
- if (edid_minor >= 3) {
- if (!has_name_descriptor)
+ if (base.edid_minor >= 3) {
+ if (!base.has_name_descriptor)
fail("Missing Display Product Name.\n");
- if ((edid_minor == 3 || supports_continuous_freq) &&
- !has_display_range_descriptor)
+ if ((base.edid_minor == 3 || base.supports_continuous_freq) &&
+ !base.has_display_range_descriptor)
fail("Missing Display Range Limits Descriptor.\n");
}
}
+
+void edid_state::check_base_block()
+{
+ data_block = "Base EDID";
+ if (base.uses_gtf && !base.supports_gtf)
+ fail("GTF timings are used, but the EDID does not signal GTF support.\n");
+ if (base.uses_cvt && !base.supports_cvt)
+ fail("CVT timings are used, but the EDID does not signal CVT support.\n");
+ /*
+ * Allow for regular rounding of vertical and horizontal frequencies.
+ * The spec says that the pixelclock shall be rounded up, so there is
+ * no need to take rounding into account.
+ */
+ if (base.has_display_range_descriptor &&
+ (min_vert_freq_hz + 0.5 < base.min_display_vert_freq_hz ||
+ (max_vert_freq_hz >= base.max_display_vert_freq_hz + 0.5 && base.max_display_vert_freq_hz) ||
+ min_hor_freq_hz + 500 < base.min_display_hor_freq_hz ||
+ (max_hor_freq_hz >= base.max_display_hor_freq_hz + 500 && base.max_display_hor_freq_hz) ||
+ (max_pixclk_khz > base.max_display_pixclk_khz && base.max_display_pixclk_khz))) {
+ /*
+ * Check if it is really out of range, or if it could be a rounding error.
+ * The EDID spec is not very clear about rounding.
+ */
+ bool fail =
+ min_vert_freq_hz + 1.0 <= base.min_display_vert_freq_hz ||
+ (max_vert_freq_hz >= base.max_display_vert_freq_hz + 1.0 && base.max_display_vert_freq_hz) ||
+ min_hor_freq_hz + 1000 <= base.min_display_hor_freq_hz ||
+ (max_hor_freq_hz >= base.max_display_hor_freq_hz + 1000 && base.max_display_hor_freq_hz) ||
+ (max_pixclk_khz >= base.max_display_pixclk_khz + 10000 && base.max_display_pixclk_khz);
+
+ std::string err("Some timings are out of range of the Monitor Ranges:\n");
+ char buf[512];
+
+ if (min_vert_freq_hz + 0.5 < base.min_display_vert_freq_hz ||
+ (max_vert_freq_hz >= base.max_display_vert_freq_hz + 0.5 && base.max_display_vert_freq_hz)) {
+ sprintf(buf, " Vertical Freq: %.3f - %.3f Hz (Monitor: %u.000 - %u.000 Hz)\n",
+ min_vert_freq_hz, max_vert_freq_hz,
+ base.min_display_vert_freq_hz, base.max_display_vert_freq_hz);
+ err += buf;
+ }
+
+ if (min_hor_freq_hz + 500 < base.min_display_hor_freq_hz ||
+ (max_hor_freq_hz >= base.max_display_hor_freq_hz + 500 && base.max_display_hor_freq_hz)) {
+ sprintf(buf, " Horizontal Freq: %.3f - %.3f kHz (Monitor: %.3f - %.3f kHz)\n",
+ min_hor_freq_hz / 1000.0, max_hor_freq_hz / 1000.0,
+ base.min_display_hor_freq_hz / 1000.0, base.max_display_hor_freq_hz / 1000.0);
+ err += buf;
+ }
+
+ if (max_pixclk_khz >= base.max_display_pixclk_khz && base.max_display_pixclk_khz) {
+ sprintf(buf, " Maximum Clock: %.3f MHz (Monitor: %.3f MHz)\n",
+ max_pixclk_khz / 1000.0, base.max_display_pixclk_khz / 1000.0);
+ err += buf;
+ }
+
+ if (!fail)
+ err += " Could be due to a Monitor Range off-by-one rounding issue\n";
+
+ /*
+ * EDID 1.4 states (in an Errata) that explicitly defined
+ * timings supersede the monitor range definition.
+ */
+ msg(!fail || base.edid_minor >= 4, "%s", err.c_str());
+ }
+}
diff --git a/parse-cta-block.cpp b/parse-cta-block.cpp
index 96c2aaf..97924d1 100644
--- a/parse-cta-block.cpp
+++ b/parse-cta-block.cpp
@@ -274,7 +274,7 @@ static void cta_audio_block(const unsigned char *x, unsigned length)
unsigned i, format, ext_format = 0;
if (length % 3) {
- fail("Broken CTA audio block length %d\n", length);
+ fail("Broken CTA-861 audio block length %d\n", length);
return;
}
@@ -367,21 +367,20 @@ void edid_state::cta_svd(const unsigned char *x, unsigned n, bool for_ycbcr420)
if (t) {
switch (vic) {
case 95:
- supported_hdmi_vic_vsb_codes |= 1 << 0;
+ cta.supported_hdmi_vic_vsb_codes |= 1 << 0;
break;
case 94:
- supported_hdmi_vic_vsb_codes |= 1 << 1;
+ cta.supported_hdmi_vic_vsb_codes |= 1 << 1;
break;
case 93:
- supported_hdmi_vic_vsb_codes |= 1 << 2;
+ cta.supported_hdmi_vic_vsb_codes |= 1 << 2;
break;
case 98:
- supported_hdmi_vic_vsb_codes |= 1 << 3;
+ cta.supported_hdmi_vic_vsb_codes |= 1 << 3;
break;
}
- bool override_pref = i == 0 && !for_ycbcr420 &&
- first_svd_might_be_preferred &&
- !match_timings(*t, preferred_timings);
+ bool first_svd = cta.first_svd && !for_ycbcr420;
+ bool override_pref = first_svd && cta.first_svd_might_be_preferred;
char type[16];
sprintf(type, "VIC %3u", vic);
@@ -395,21 +394,28 @@ void edid_state::cta_svd(const unsigned char *x, unsigned n, bool for_ycbcr420)
print_timings(" ", t, type, flags);
}
if (override_pref) {
- preferred_timings = *t;
- preferred_type = type;
- preferred_flags = flags;
+ cta.preferred_timings.insert(cta.preferred_timings.begin(),
+ timings_ext(*t, type, flags));
warn("VIC %u is the preferred timing, overriding the first detailed timings. Is this intended?\n", vic);
+ } else if (first_svd) {
+ cta.preferred_timings.push_back(timings_ext(*t, type, flags));
}
+ if (first_svd) {
+ cta.first_svd = false;
+ cta.first_svd_might_be_preferred = false;
+ }
+ if (native)
+ cta.native_timings.push_back(timings_ext(*t, type, flags));
} else {
printf(" Unknown (VIC %3u)\n", vic);
fail("Unknown VIC %u.\n", vic);
}
if (vic == 1 && !for_ycbcr420)
- has_cta861_vic_1 = 1;
- if (++vics[vic][for_ycbcr420] == 2)
+ cta.has_vic_1 = 1;
+ if (++cta.vics[vic][for_ycbcr420] == 2)
fail("Duplicate %sVIC %u.\n", for_ycbcr420 ? "YCbCr 4:2:0 " : "", vic);
- if (for_ycbcr420 && preparsed_has_vic[0][vic])
+ if (for_ycbcr420 && cta.preparsed_has_vic[0][vic])
fail("YCbCr 4:2:0-only VIC %u is also a regular VIC.\n", vic);
}
}
@@ -418,8 +424,8 @@ void edid_state::print_vic_index(const char *prefix, unsigned idx, const char *s
{
if (!suffix)
suffix = "";
- if (idx < preparsed_svds[0].size()) {
- unsigned char vic = preparsed_svds[0][idx];
+ if (idx < cta.preparsed_svds[0].size()) {
+ unsigned char vic = cta.preparsed_svds[0][idx];
const struct timings *t = find_vic_id(vic);
char buf[16];
@@ -468,16 +474,16 @@ void edid_state::cta_y420cmdb(const unsigned char *x, unsigned length)
print_vic_index(" ", i * 8 + j, "", true);
max_idx = i * 8 + j;
- if (max_idx < preparsed_svds[0].size()) {
- unsigned vic = preparsed_svds[0][max_idx];
- if (preparsed_has_vic[1][vic])
+ if (max_idx < cta.preparsed_svds[0].size()) {
+ unsigned vic = cta.preparsed_svds[0][max_idx];
+ if (cta.preparsed_has_vic[1][vic])
fail("VIC %u is also a YCbCr 4:2:0-only VIC.\n", vic);
}
}
}
- if (max_idx >= preparsed_svds[0].size())
+ if (max_idx >= cta.preparsed_svds[0].size())
fail("Max index %u > %u (#SVDs).\n",
- max_idx + 1, preparsed_svds[0].size());
+ max_idx + 1, cta.preparsed_svds[0].size());
}
void edid_state::cta_vfpdb(const unsigned char *x, unsigned length)
@@ -488,11 +494,12 @@ void edid_state::cta_vfpdb(const unsigned char *x, unsigned length)
fail("Empty Data Block with length %u\n", length);
return;
}
+ cta.preferred_timings.clear();
for (i = 0; i < length; i++) {
unsigned char svr = x[i];
+ char suffix[16];
if ((svr > 0 && svr < 128) || (svr > 192 && svr < 254)) {
- char suffix[16];
const struct timings *t;
unsigned char vic = svr;
@@ -501,13 +508,21 @@ void edid_state::cta_vfpdb(const unsigned char *x, unsigned length)
t = find_vic_id(vic);
if (t) {
print_timings(" ", t, suffix);
+ cta.preferred_timings.push_back(timings_ext(*t, suffix, ""));
} else {
- printf(" Unknown (VIC %3u)\n", vic);
+ printf(" %s: Unknown\n", suffix);
fail("Unknown VIC %u.\n", vic);
}
- } else if (svr > 128 && svr < 145) {
- printf(" DTD %u\n", svr - 128);
+ } else if (svr >= 129 && svr <= 144) {
+ sprintf(suffix, "DTD %3u", svr - 128);
+ if (svr >= cta.preparse_total_dtds + 129) {
+ printf(" %s: Invalid\n", suffix);
+ fail("Invalid DTD %u.\n", svr - 128);
+ } else {
+ printf(" %s\n", suffix);
+ cta.preferred_timings.push_back(timings_ext(svr, suffix));
+ }
}
}
}
@@ -612,10 +627,10 @@ void edid_state::cta_hdmi_block(const unsigned char *x, unsigned length)
break;
case 0x18:
printf(" Base EDID image size is in units of 5 cm\n");
- max_display_width_mm *= 5;
- max_display_height_mm *= 5;
+ base.max_display_width_mm *= 5;
+ base.max_display_height_mm *= 5;
printf(" Recalculated image size: %u cm x %u cm\n",
- max_display_width_mm / 10, max_display_height_mm / 10);
+ base.max_display_width_mm / 10, base.max_display_height_mm / 10);
break;
}
b++;
@@ -633,7 +648,7 @@ void edid_state::cta_hdmi_block(const unsigned char *x, unsigned length)
if (vic && vic <= ARRAY_SIZE(edid_hdmi_mode_map)) {
std::string suffix = "HDMI VIC " + std::to_string(vic);
- supported_hdmi_vic_codes |= 1 << (vic - 1);
+ cta.supported_hdmi_vic_codes |= 1 << (vic - 1);
t = find_vic_id(edid_hdmi_mode_map[vic - 1]);
print_timings(" ", t, suffix.c_str());
} else {
@@ -692,9 +707,9 @@ void edid_state::cta_hdmi_block(const unsigned char *x, unsigned length)
}
b += 2;
len_3d -= 2;
- if (max_idx >= (int)preparsed_svds[0].size())
+ if (max_idx >= (int)cta.preparsed_svds[0].size())
fail("HDMI 3D VIC indices max index %d > %u (#SVDs).\n",
- max_idx + 1, preparsed_svds[0].size());
+ max_idx + 1, cta.preparsed_svds[0].size());
}
/*
@@ -757,9 +772,9 @@ void edid_state::cta_hdmi_block(const unsigned char *x, unsigned length)
b++;
b++;
}
- if (max_idx >= (int)preparsed_svds[0].size())
+ if (max_idx >= (int)cta.preparsed_svds[0].size())
fail("HDMI 2D VIC indices max index %d > %u (#SVDs).\n",
- max_idx + 1, preparsed_svds[0].size());
+ max_idx + 1, cta.preparsed_svds[0].size());
}
static const char *max_frl_rates[] = {
@@ -1352,10 +1367,11 @@ static void cta_sldb(const unsigned char *x, unsigned length)
}
}
-static void cta_vcdb(const unsigned char *x, unsigned length)
+void edid_state::cta_vcdb(const unsigned char *x, unsigned length)
{
unsigned char d = x[0];
+ cta.has_vcdb = true;
if (length < 1) {
fail("Empty Data Block with length %u\n", length);
return;
@@ -1627,7 +1643,7 @@ void edid_state::cta_ext_block(const unsigned char *x, unsigned length)
case 0x0d: data_block = "Video Format Preference Data Block"; break;
case 0x0e: data_block = "YCbCr 4:2:0 Video Data Block"; break;
case 0x0f: data_block = "YCbCr 4:2:0 Capability Map Data Block"; break;
- case 0x10: data_block = "Reserved for CTA Miscellaneous Audio Fields"; break;
+ case 0x10: data_block = "Reserved for CTA-861 Miscellaneous Audio Fields"; break;
case 0x11: data_block = "Vendor-Specific Audio Data Block"; break;
case 0x12: data_block = "HDMI Audio Data Block"; break;
case 0x13: data_block = "Room Configuration Data Block"; break;
@@ -1639,17 +1655,17 @@ void edid_state::cta_ext_block(const unsigned char *x, unsigned length)
case 0x79: data_block = "HDMI Forum Sink Capability Data Block"; break;
default:
if (x[0] <= 12)
- printf(" Unknown CTA Video-Related");
+ printf(" Unknown CTA-861 Video-Related");
else if (x[0] <= 31)
- printf(" Unknown CTA Audio-Related");
+ printf(" Unknown CTA-861 Audio-Related");
else if (x[0] >= 120 && x[0] <= 127)
- printf(" Unknown CTA HDMI-Related");
+ printf(" Unknown CTA-861 HDMI-Related");
else
- printf(" Unknown CTA");
+ printf(" Unknown CTA-861");
printf(" Data Block (extended tag 0x%02x, length %u)\n", x[0], length);
hex_block(" ", x + 1, length);
data_block.clear();
- warn("Unknown Extended CTA Data Block 0x%02x.\n", x[0]);
+ warn("Unknown Extended CTA-861 Data Block 0x%02x.\n", x[0]);
return;
}
@@ -1659,6 +1675,11 @@ void edid_state::cta_ext_block(const unsigned char *x, unsigned length)
switch (x[0]) {
case 0x00: cta_vcdb(x + 1, length); return;
case 0x01:
+ if (length < 3) {
+ data_block = std::string("Vendor-Specific Video Data Block");
+ fail("Invalid length %u < 3\n", length);
+ return;
+ }
oui = (x[3] << 16) + (x[2] << 8) + x[1];
name = oui_name(oui);
if (!name) {
@@ -1694,6 +1715,11 @@ void edid_state::cta_ext_block(const unsigned char *x, unsigned length)
case 0x0e: cta_svd(x + 1, length, true); return;
case 0x0f: cta_y420cmdb(x + 1, length); return;
case 0x11:
+ if (length < 3) {
+ data_block = std::string("Vendor-Specific Audio Data Block");
+ fail("Invalid length %u < 3\n", length);
+ return;
+ }
oui = (x[3] << 16) + (x[2] << 8) + x[1];
name = oui_name(oui);
if (!name) {
@@ -1722,19 +1748,24 @@ void edid_state::cta_ext_block(const unsigned char *x, unsigned length)
case 0x20: cta_ifdb(x + 1, length); return;
case 0x78:
cta_hf_eeodb(x + 1, length);
- // This must be the first CTA block
- if (!first_block)
+ // This must be the first CTA-861 block
+ if (!cta.first_block)
fail("Block starts at a wrong offset.\n");
return;
case 0x79:
- if (!last_block_was_hdmi_vsdb)
+ if (!cta.last_block_was_hdmi_vsdb)
fail("HDMI Forum SCDB did not immediately follow the HDMI VSDB.\n");
- if (have_hf_scdb || have_hf_vsdb)
+ if (cta.have_hf_scdb || cta.have_hf_vsdb)
fail("Duplicate HDMI Forum VSDB/SCDB.\n");
+ if (length < 2) {
+ data_block = std::string("HDMI Forum SCDB");
+ fail("Invalid length %u < 2\n", length);
+ return;
+ }
if (x[1] || x[2])
printf(" Non-zero SCDB reserved fields!\n");
cta_hf_scdb(x + 3, length - 2);
- have_hf_scdb = 1;
+ cta.have_hf_scdb = 1;
return;
}
@@ -1781,19 +1812,19 @@ void edid_state::cta_block(const unsigned char *x)
printf(" %s, OUI %s:\n", data_block.c_str(), ouitohex(oui).c_str());
if (oui == 0x000c03) {
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);
+ cta.last_block_was_hdmi_vsdb = 1;
+ cta.first_block = 0;
+ if (base.edid_minor != 3)
+ fail("The HDMI Specification uses EDID 1.3, not 1.%u.\n", base.edid_minor);
return;
}
if (oui == 0xc45dd8) {
- if (!last_block_was_hdmi_vsdb)
+ if (!cta.last_block_was_hdmi_vsdb)
fail("HDMI Forum VSDB did not immediately follow the HDMI VSDB.\n");
- if (have_hf_scdb || have_hf_vsdb)
+ if (cta.have_hf_scdb || cta.have_hf_vsdb)
fail("Duplicate HDMI Forum VSDB/SCDB.\n");
cta_hf_scdb(x + 4, length - 3);
- have_hf_vsdb = 1;
+ cta.have_hf_vsdb = 1;
break;
}
hex_block(" ", x + 4, length - 3);
@@ -1815,16 +1846,16 @@ void edid_state::cta_block(const unsigned char *x)
unsigned tag = (*x & 0xe0) >> 5;
unsigned length = *x & 0x1f;
- printf(" Unknown CTA tag 0x%02x, length %u\n", tag, length);
+ printf(" Unknown CTA-861 tag 0x%02x, length %u\n", tag, length);
hex_block(" ", x + 1, length);
data_block.clear();
- warn("Unknown CTA Data Block %u.\n", tag);
+ warn("Unknown CTA-861 Data Block %u.\n", tag);
break;
}
}
- first_block = 0;
- last_block_was_hdmi_vsdb = 0;
+ cta.first_block = 0;
+ cta.last_block_was_hdmi_vsdb = 0;
}
void edid_state::preparse_cta_block(const unsigned char *x)
@@ -1839,7 +1870,7 @@ void edid_state::preparse_cta_block(const unsigned char *x)
if (memchk(detailed, 18))
break;
if (detailed[0] || detailed[1])
- preparse_total_dtds++;
+ cta.preparse_total_dtds++;
}
}
@@ -1854,11 +1885,13 @@ void edid_state::preparse_cta_block(const unsigned char *x)
case 0x03:
oui = (x[i + 3] << 16) + (x[i + 2] << 8) + x[i + 1];
if (oui == 0x000c03) {
- has_hdmi = true;
- preparsed_phys_addr = (x[i + 4] << 8) | x[i + 5];
+ cta.has_hdmi = true;
+ cta.preparsed_phys_addr = (x[i + 4] << 8) | x[i + 5];
}
break;
case 0x07:
+ if (x[i + 1] == 0x0d)
+ cta.has_vfpdb = true;
if (x[i + 1] != 0x0e)
continue;
for_ycbcr420 = true;
@@ -1869,8 +1902,8 @@ void edid_state::preparse_cta_block(const unsigned char *x)
if ((vic & 0x7f) <= 64)
vic &= 0x7f;
- preparsed_svds[for_ycbcr420].push_back(vic);
- preparsed_has_vic[for_ycbcr420][vic] = true;
+ cta.preparsed_svds[for_ycbcr420].push_back(vic);
+ cta.preparsed_has_vic[for_ycbcr420][vic] = true;
}
break;
}
@@ -1885,9 +1918,9 @@ void edid_state::parse_cta_block(const unsigned char *x)
printf(" Revision: %u\n", version);
if (version == 0)
- fail("Invalid CTA Extension revision 0\n");
+ fail("Invalid CTA-861 Extension revision 0\n");
if (version > 3)
- warn("Unknown CTA Extension revision %u\n", version);
+ warn("Unknown CTA-861 Extension revision %u\n", version);
if (version >= 1) do {
if (version == 1 && x[3] != 0)
@@ -1898,7 +1931,7 @@ void edid_state::parse_cta_block(const unsigned char *x)
if (version < 3 && ((offset - 4) / 8)) {
printf(" 8-byte timing descriptors: %u\n", (offset - 4) / 8);
- fail("8-byte descriptors are no longer used\n");
+ fail("8-byte descriptors were never used\n");
}
if (version >= 2) {
@@ -1914,11 +1947,32 @@ void edid_state::parse_cta_block(const unsigned char *x)
// also some corner cases where you only want to receive 4:4:4
// and refuse a fallback to 4:2:2.
// if ((x[3] & 0x30) && (x[3] & 0x30) != 0x30)
-// msg(!has_hdmi, "If YCbCr support is indicated, then both 4:2:2 and 4:4:4 %s be supported.\n",
-// has_hdmi ? "shall" : "should");
+// msg(!cta.has_hdmi, "If YCbCr support is indicated, then both 4:2:2 and 4:4:4 %s be supported.\n",
+// cta.has_hdmi ? "shall" : "should");
printf(" Native detailed modes: %u\n", x[3] & 0x0f);
- if (!(x[3] & 0x0f))
- first_svd_might_be_preferred = true;
+ if (cta.first_block)
+ cta.byte3 = x[3];
+ else if (x[3] != cta.byte3)
+ fail("Byte 3 must be the same for all CTA-861 Extension Blocks.\n");
+ if (cta.first_block) {
+ unsigned native_dtds = x[3] & 0x0f;
+
+ cta.native_timings.clear();
+ if (!native_dtds && !cta.has_vfpdb) {
+ cta.first_svd_might_be_preferred = true;
+ } else if (native_dtds > cta.preparse_total_dtds) {
+ fail("There are more Native DTDs (%u) than DTDs (%u).\n",
+ native_dtds, cta.preparse_total_dtds);
+ }
+ if (native_dtds > cta.preparse_total_dtds)
+ native_dtds = cta.preparse_total_dtds;
+ for (unsigned i = 0; i < native_dtds; i++) {
+ char type[16];
+
+ sprintf(type, "DTD %3u", i + 1);
+ cta.native_timings.push_back(timings_ext(i + 129, type));
+ }
+ }
}
if (version >= 3) {
unsigned i;
@@ -1932,7 +1986,7 @@ void edid_state::parse_cta_block(const unsigned char *x)
}
data_block = "Detailed Timing Descriptors";
- seen_non_detailed_descriptor = false;
+ base.seen_non_detailed_descriptor = false;
bool first = true;
for (detailed = x + offset; detailed + 18 < x + 127; detailed += 18) {
if (memchk(detailed, 18))
@@ -1950,12 +2004,116 @@ void edid_state::parse_cta_block(const unsigned char *x)
} while (0);
data_block.clear();
- if (has_serial_number && has_serial_string)
+ if (base.has_serial_number && base.has_serial_string)
warn("Display Product Serial Number is set, so the Serial Number in the Base EDID should be 0.\n");
- if (!has_cta861_vic_1 && !has_640x480p60_est_timing)
+ if (!cta.has_vic_1 && !base.has_640x480p60_est_timing)
fail("Required 640x480p60 timings are missing in the established timings"
" and the SVD list (VIC 1).\n");
- if ((supported_hdmi_vic_vsb_codes & supported_hdmi_vic_codes) !=
- supported_hdmi_vic_codes)
+ if ((cta.supported_hdmi_vic_vsb_codes & cta.supported_hdmi_vic_codes) !=
+ cta.supported_hdmi_vic_codes)
fail("HDMI VIC Codes must have their CTA-861 VIC equivalents in the VSB.\n");
+ if (!cta.has_vcdb)
+ warn("Missing VCDB, needed for Set Selectable RGB Quantization to avoid interop issues.\n");
+}
+
+void edid_state::cta_resolve_svr(vec_timings_ext::iterator iter)
+{
+ iter->flags = cta.vec_dtds[iter->svr() - 129].flags;
+ iter->t = cta.vec_dtds[iter->svr() - 129].t;
+}
+
+void edid_state::cta_resolve_svrs()
+{
+ for (vec_timings_ext::iterator iter = cta.preferred_timings.begin();
+ iter != cta.preferred_timings.end(); ++iter) {
+ if (iter->has_svr())
+ cta_resolve_svr(iter);
+ }
+
+ for (vec_timings_ext::iterator iter = cta.native_timings.begin();
+ iter != cta.native_timings.end(); ++iter) {
+ if (iter->has_svr())
+ cta_resolve_svr(iter);
+ }
+}
+
+void edid_state::check_cta_blocks()
+{
+ unsigned max_pref_prog_hact = 0;
+ unsigned max_pref_prog_vact = 0;
+ unsigned max_pref_ilace_hact = 0;
+ unsigned max_pref_ilace_vact = 0;
+
+ data_block = "CTA-861";
+ for (vec_timings_ext::iterator iter = cta.preferred_timings.begin();
+ iter != cta.preferred_timings.end(); ++iter) {
+ if (iter->t.interlaced &&
+ (iter->t.vact > max_pref_ilace_vact ||
+ (iter->t.vact == max_pref_ilace_vact && iter->t.hact >= max_pref_ilace_hact))) {
+ max_pref_ilace_hact = iter->t.hact;
+ max_pref_ilace_vact = iter->t.vact;
+ }
+ if (!iter->t.interlaced &&
+ (iter->t.vact > max_pref_prog_vact ||
+ (iter->t.vact == max_pref_prog_vact && iter->t.hact >= max_pref_prog_hact))) {
+ max_pref_prog_hact = iter->t.hact;
+ max_pref_prog_vact = iter->t.vact;
+ }
+ }
+
+ unsigned native_prog = 0;
+ unsigned native_prog_hact = 0;
+ unsigned native_prog_vact = 0;
+ bool native_prog_mixed_resolutions = false;
+ unsigned native_ilace = 0;
+ unsigned native_ilace_hact = 0;
+ unsigned native_ilace_vact = 0;
+ bool native_ilace_mixed_resolutions = false;
+
+ for (vec_timings_ext::iterator iter = cta.native_timings.begin();
+ iter != cta.native_timings.end(); ++iter) {
+ if (iter->t.interlaced) {
+ native_ilace++;
+ if (!native_ilace_hact) {
+ native_ilace_hact = iter->t.hact;
+ native_ilace_vact = iter->t.vact;
+ } else if (native_ilace_hact != iter->t.hact ||
+ native_ilace_vact != iter->t.vact) {
+ native_ilace_mixed_resolutions = true;
+ }
+ } else {
+ native_prog++;
+ if (!native_prog_hact) {
+ native_prog_hact = iter->t.hact;
+ native_prog_vact = iter->t.vact;
+ } else if (native_prog_hact != iter->t.hact ||
+ native_prog_vact != iter->t.vact) {
+ native_prog_mixed_resolutions = true;
+ }
+ }
+ }
+
+ if (native_prog_mixed_resolutions)
+ fail("Native progressive timings are a mix of several resolutions.\n");
+ if (native_ilace_mixed_resolutions)
+ fail("Native interlaced timings are a mix of several resolutions.\n");
+ if (native_ilace && !native_prog)
+ fail("A native interlaced timing is present, but not a native progressive timing.\n");
+ if (!native_prog_mixed_resolutions && native_prog > 1)
+ warn("Multiple native progressive timings are defined.\n");
+ if (!native_ilace_mixed_resolutions && native_ilace > 1)
+ warn("Multiple native interlaced timings are defined.\n");
+
+ if (!native_prog_mixed_resolutions && native_prog_vact &&
+ (max_pref_prog_vact > native_prog_vact ||
+ (max_pref_prog_vact == native_prog_vact && max_pref_prog_hact > native_prog_hact)))
+ warn("Native progressive resolution of %ux%u is smaller than the max preferred progressive resolution %ux%u.\n",
+ native_prog_hact, native_prog_vact,
+ max_pref_prog_hact, max_pref_prog_vact);
+ if (!native_ilace_mixed_resolutions && native_ilace_vact &&
+ (max_pref_ilace_vact > native_ilace_vact ||
+ (max_pref_ilace_vact == native_ilace_vact && max_pref_ilace_hact > native_ilace_hact)))
+ warn("Native interlaced resolution of %ux%u is smaller than the max preferred interlaced resolution %ux%u.\n",
+ native_ilace_hact, native_ilace_vact,
+ max_pref_ilace_hact, max_pref_ilace_vact);
}
diff --git a/parse-displayid-block.cpp b/parse-displayid-block.cpp
index b436f34..77cc22a 100644
--- a/parse-displayid-block.cpp
+++ b/parse-displayid-block.cpp
@@ -40,12 +40,17 @@ static void print_flags(const char *label, unsigned char flag_byte,
printf("\n");
}
-static void check_displayid_datablock_revision(const unsigned char *x)
+static void check_displayid_datablock_revision(const unsigned char *x,
+ unsigned char valid_flags = 0,
+ unsigned char rev = 0)
{
- unsigned char revisionflags = x[1];
+ unsigned char revision = x[1] & 7;
+ unsigned char flags = x[1] & ~7 & ~valid_flags;
- if (revisionflags)
- warn("Unexpected revision and flags (0x%02x != 0).\n", revisionflags);
+ if (revision != rev)
+ warn("Unexpected revision (%u != %u).\n", revision, rev);
+ if (flags)
+ warn("Unexpected flags (0x%02x).\n", flags);
}
static bool check_displayid_datablock_length(const unsigned char *x,
@@ -71,11 +76,12 @@ static bool check_displayid_datablock_length(const unsigned char *x,
// tag 0x00 and 0x20
-static void parse_displayid_product_id(const unsigned char *x, bool is_v2)
+void edid_state::parse_displayid_product_id(const unsigned char *x)
{
check_displayid_datablock_revision(x);
- if (is_v2) {
+ dispid.has_product_identification = true;
+ if (dispid.version >= 0x20) {
unsigned oui = (x[3] << 16) | (x[4] << 8) | x[5];
printf(" Vendor OUI %s\n", ouitohex(oui).c_str());
} else {
@@ -126,13 +132,14 @@ static void print_flag_lines(const char *indent, const char *label,
}
}
-static void parse_displayid_parameters(const unsigned char *x)
+void edid_state::parse_displayid_parameters(const unsigned char *x)
{
check_displayid_datablock_revision(x);
if (!check_displayid_datablock_length(x, 12, 12))
return;
+ dispid.has_display_parameters = true;
printf(" Image size: %.1f mm x %.1f mm\n",
((x[4] << 8) + x[3]) / 10.0,
((x[6] << 8) + x[5]) / 10.0);
@@ -169,7 +176,7 @@ static double fp2d(unsigned short fp)
void edid_state::parse_displayid_color_characteristics(const unsigned char *x)
{
- check_displayid_datablock_revision(x);
+ check_displayid_datablock_revision(x, 0xf8, 1);
unsigned cie_year = (x[1] & 0x80) ? 1976 : 1931;
unsigned xfer_id = (x[1] >> 3) & 0x0f;
@@ -182,7 +189,7 @@ void edid_state::parse_displayid_color_characteristics(const unsigned char *x)
printf(" Uses %u CIE (x, y) coordinates\n", cie_year);
if (xfer_id) {
printf(" Associated with Transfer Characteristics Data Block with Identifier %u\n", xfer_id);
- if (!(preparse_xfer_ids & (1 << xfer_id)))
+ if (!(dispid.preparse_xfer_ids & (1 << xfer_id)))
fail("Missing Transfer Characteristics Data Block with Identifier %u.\n", xfer_id);
}
if (!num_primaries) {
@@ -216,6 +223,7 @@ void edid_state::parse_displayid_type_1_7_timing(const unsigned char *x, bool ty
unsigned hbl, vbl;
std::string s("aspect ");
+ dispid.has_type_1_7 = true;
t.pixclk_khz = (type7 ? 1 : 10) * (1 + (x[0] + (x[1] << 8) + (x[2] << 16)));
switch (x[3] & 0xf) {
case 0:
@@ -259,7 +267,8 @@ void edid_state::parse_displayid_type_1_7_timing(const unsigned char *x, bool ty
break;
default:
s += "undefined";
- fail("Unknown aspect 0x%02x.\n", x[3] & 0xf);
+ if ((x[3] & 0xf) > (dispid.version <= 0x12 ? 7 : 8))
+ fail("Unknown aspect 0x%02x.\n", x[3] & 0xf);
break;
}
switch ((x[3] >> 5) & 0x3) {
@@ -299,11 +308,12 @@ void edid_state::parse_displayid_type_1_7_timing(const unsigned char *x, bool ty
t.vsync /= 2;
t.vbp /= 2;
}
- if (x[3] & 0x80)
+ if (x[3] & 0x80) {
s += ", preferred";
+ dispid.preferred_timings.push_back(timings_ext(t, "DTD", s));
+ }
- dtd_cnt++;
- print_timings(" ", &t, dtd_type().c_str(), s.c_str(), true);
+ print_timings(" ", &t, "DTD", s.c_str(), true);
}
// tag 0x04
@@ -356,11 +366,12 @@ void edid_state::parse_displayid_type_2_timing(const unsigned char *x)
fail("Reserved stereo 0x03.\n");
break;
}
- if (x[3] & 0x80)
+ if (x[3] & 0x80) {
s += ", preferred";
+ dispid.preferred_timings.push_back(timings_ext(t, "DTD", s));
+ }
- dtd_cnt++;
- print_timings(" ", &t, dtd_type().c_str(), s.c_str(), true);
+ print_timings(" ", &t, "DTD", s.c_str(), true);
}
// tag 0x05
@@ -412,7 +423,8 @@ void edid_state::parse_displayid_type_3_timing(const unsigned char *x)
break;
default:
s += "undefined";
- fail("Unknown aspect 0x%02x.\n", x[3] & 0xf);
+ if ((x[3] & 0xf) > (dispid.version <= 0x12 ? 7 : 8))
+ fail("Unknown aspect 0x%02x.\n", x[3] & 0xf);
break;
}
@@ -422,8 +434,10 @@ void edid_state::parse_displayid_type_3_timing(const unsigned char *x)
edid_cvt_mode(1 + (x[2] & 0x7f), t);
- if (x[0] & 0x80)
+ if (x[0] & 0x80) {
s += ", preferred";
+ dispid.preferred_timings.push_back(timings_ext(t, "CVT", s));
+ }
print_timings(" ", &t, "CVT", s.c_str());
}
@@ -620,7 +634,7 @@ static void parse_displayid_intf_power_sequencing(const unsigned char *x)
void edid_state::parse_displayid_transfer_characteristics(const unsigned char *x)
{
- check_displayid_datablock_revision(x);
+ check_displayid_datablock_revision(x, 0xf0, 1);
unsigned xfer_id = x[1] >> 4;
bool first_is_white = x[3] & 0x80;
@@ -628,7 +642,7 @@ void edid_state::parse_displayid_transfer_characteristics(const unsigned char *x
if (xfer_id) {
printf(" Transfer Characteristics Data Block Identifier: %u\n", xfer_id);
- if (!(preparse_color_ids & (1 << xfer_id)))
+ if (!(dispid.preparse_color_ids & (1 << xfer_id)))
fail("Missing Color Characteristics Data Block using Identifier %u.\n", xfer_id);
}
if (first_is_white)
@@ -663,13 +677,14 @@ void edid_state::parse_displayid_transfer_characteristics(const unsigned char *x
// tag 0x0f
-static void parse_displayid_display_intf(const unsigned char *x)
+void edid_state::parse_displayid_display_intf(const unsigned char *x)
{
check_displayid_datablock_revision(x);
if (!check_displayid_datablock_length(x, 10, 10))
return;
+ dispid.has_display_interface_features = true;
printf(" Interface Type: ");
switch (x[3] >> 4) {
case 0x00:
@@ -748,7 +763,7 @@ static void parse_displayid_display_intf(const unsigned char *x)
void edid_state::parse_displayid_stereo_display_intf(const unsigned char *x)
{
- check_displayid_datablock_revision(x);
+ check_displayid_datablock_revision(x, 0xc0, 1);
switch (x[1] >> 6) {
case 0x00: printf(" Timings that explicitly report 3D capability\n"); break;
@@ -865,8 +880,6 @@ void edid_state::parse_displayid_type_5_timing(const unsigned char *x)
}
if (x[0] & 0x10)
s += ", refresh rate * (1000/1001) supported";
- if (x[0] & 0x80)
- s += ", preferred";
switch (x[0] & 0x03) {
case 0: t.rb = 2; break;
@@ -876,6 +889,11 @@ void edid_state::parse_displayid_type_5_timing(const unsigned char *x)
edid_cvt_mode(1 + x[6], t);
+ if (x[0] & 0x80) {
+ s += ", preferred";
+ dispid.preferred_timings.push_back(timings_ext(t, "CVT", s));
+ }
+
print_timings(" ", &t, "CVT", s.c_str());
}
@@ -1004,11 +1022,12 @@ void edid_state::parse_displayid_type_6_timing(const unsigned char *x)
break;
}
- if (x[2] & 0x80)
+ if (x[2] & 0x80) {
s += ", preferred";
+ dispid.preferred_timings.push_back(timings_ext(t, "DTD", s));
+ }
- dtd_cnt++;
- print_timings(" ", &t, dtd_type().c_str(), s.c_str(), true);
+ print_timings(" ", &t, "DTD", s.c_str(), true);
}
static std::string ieee7542d(unsigned short fp)
@@ -1025,7 +1044,7 @@ static std::string ieee7542d(unsigned short fp)
// tag 0x21
-static void parse_displayid_parameters_v2(const unsigned char *x)
+void edid_state::parse_displayid_parameters_v2(const unsigned char *x)
{
check_displayid_datablock_revision(x);
@@ -1035,6 +1054,7 @@ static void parse_displayid_parameters_v2(const unsigned char *x)
unsigned hor_size = (x[4] << 8) + x[3];
unsigned vert_size = (x[6] << 8) + x[5];
+ dispid.has_display_parameters = true;
if (x[1] & 0x80)
printf(" Image size: %u mm x %u mm\n",
hor_size, vert_size);
@@ -1199,13 +1219,14 @@ static const char *eotfs[] = {
"Custom"
};
-static void parse_displayid_interface_features(const unsigned char *x)
+void edid_state::parse_displayid_interface_features(const unsigned char *x)
{
check_displayid_datablock_revision(x);
if (!check_displayid_datablock_length(x, 9))
return;
+ dispid.has_display_interface_features = true;
unsigned len = x[2];
if (len > 0) print_flags(" Supported bpc for RGB encoding", x[3], bpc444);
if (len > 1) print_flags(" Supported bpc for YCbCr 4:4:4 encoding", x[4], bpc444);
@@ -1330,13 +1351,14 @@ void edid_state::parse_displayid_cta_data_block(const unsigned char *x)
// DisplayID main
-static std::string product_type(unsigned version, unsigned char x, bool heading)
+std::string edid_state::product_type(unsigned char x, bool heading)
{
std::string headingstr;
- if (version < 0x20) {
+ if (dispid.version < 0x20) {
headingstr = "Display Product Type";
if (heading) return headingstr;
+ dispid.is_display = x == 2 || x == 3 || x == 4 || x == 6;
switch (x) {
case 0: return "Extension Section";
case 1: return "Test Structure; test equipment only";
@@ -1350,6 +1372,7 @@ static std::string product_type(unsigned version, unsigned char x, bool heading)
} else {
headingstr = "Display Product Primary Use Case";
if (heading) return headingstr;
+ dispid.is_display = x >= 2 && x <= 8;
switch (x) {
case 0: return "Same primary use case as the base section";
case 1: return "Test Structure; test equipment only";
@@ -1376,30 +1399,17 @@ void edid_state::preparse_displayid_block(const unsigned char *x)
unsigned offset = 5;
- preparse_displayid_blocks++;
+ dispid.preparse_displayid_blocks++;
while (length > 0) {
unsigned tag = x[offset];
unsigned len = x[offset + 2];
switch (tag) {
case 0x02:
- preparse_color_ids |= 1 << ((x[offset + 1] >> 3) & 0x0f);
+ dispid.preparse_color_ids |= 1 << ((x[offset + 1] >> 3) & 0x0f);
break;
case 0x0e:
- preparse_xfer_ids |= 1 << ((x[offset + 1] >> 4) & 0x0f);
- break;
- case 0x03:
- preparse_total_dtds += len / 20;
- break;
- case 0x04:
- preparse_total_dtds += len / 11;
- break;
- case 0x13:
- for (unsigned i = 0; i < len; i += (x[offset + 3 + i + 2] & 0x40) ? 17 : 14)
- preparse_total_dtds++;
- break;
- case 0x22:
- preparse_total_dtds += len / 20;
+ dispid.preparse_xfer_ids |= 1 << ((x[offset + 1] >> 4) & 0x0f);
break;
default:
break;
@@ -1430,20 +1440,26 @@ void edid_state::parse_displayid_block(const unsigned char *x)
printf(" Version: %u.%u\n Extension Count: %u\n",
version >> 4, version & 0xf, ext_count);
- if (displayid_base_block) {
- printf(" %s: %s\n", product_type(version, prod_type, true).c_str(),
- product_type(version, prod_type, false).c_str());
- displayid_base_block = false;
+ if (dispid.is_base_block) {
+ dispid.version = version;
+ printf(" %s: %s\n", product_type(prod_type, true).c_str(),
+ product_type(prod_type, false).c_str());
if (!prod_type)
fail("DisplayID Base Block has no product type.\n");
- if (ext_count != preparse_displayid_blocks - 1)
- fail("Expected %u DisplayID Extension Blocks, but got %u\n",
- preparse_displayid_blocks - 1, ext_count);
+ if (ext_count != dispid.preparse_displayid_blocks - 1)
+ fail("Expected %u DisplayID Extension Block%s, but got %u\n",
+ dispid.preparse_displayid_blocks - 1,
+ dispid.preparse_displayid_blocks == 2 ? "" : "s",
+ ext_count);
} else {
if (prod_type)
fail("Product Type should be 0 in extension block.\n");
if (ext_count)
fail("Extension Count should be 0 in extension block.\n");
+ if (version != dispid.version)
+ fail("Got version %u.%u, expected %u.%u.\n",
+ version >> 4, version & 0xf,
+ dispid.version >> 4, dispid.version & 0xf);
}
if (length > 121) {
@@ -1452,6 +1468,7 @@ void edid_state::parse_displayid_block(const unsigned char *x)
}
unsigned offset = 5;
+ bool first_data_block = true;
while (length > 0) {
unsigned tag = x[offset];
unsigned oui = 0;
@@ -1466,7 +1483,7 @@ void edid_state::parse_displayid_block(const unsigned char *x)
case 0x05: data_block = "Video Timing Modes Type 3 - Short Timings Data Block"; break;
case 0x06: data_block = "Video Timing Modes Type 4 - DMT Timings Data Block"; break;
case 0x07: data_block = "Supported Timing Modes Type 1 - VESA DMT Timings Data Block"; break;
- case 0x08: data_block = "Supported Timing Modes Type 2 - CTA Timings Data Block"; break;
+ case 0x08: data_block = "Supported Timing Modes Type 2 - CTA-861 Timings Data Block"; break;
case 0x09: data_block = "Video Timing Range Data Block"; break;
case 0x0a: data_block = "Product Serial Number Data Block"; break;
case 0x0b: data_block = "GP ASCII String Data Block"; break;
@@ -1515,11 +1532,18 @@ void edid_state::parse_displayid_block(const unsigned char *x)
}
break;
// 0x80 RESERVED
- case 0x81: data_block = "CTA DisplayID Data Block (" + utohex(tag) + ")"; break;
+ case 0x81: data_block = "CTA-861 DisplayID Data Block (" + utohex(tag) + ")"; break;
// 0x82 .. 0xff RESERVED
default: data_block = "Unknown DisplayID Data Block (" + utohex(tag) + ")"; break;
}
+ if (version >= 0x20 && (tag < 0x20 || tag == 0x7f))
+ fail("Use of DisplayID v1.x tag for DisplayID v%u.%u.\n",
+ version >> 4, version & 0xf);
+ if (version < 0x20 && tag >= 0x20 && tag <= 0x7e)
+ fail("Use of DisplayID v2.0 tag for DisplayID v%u.%u.\n",
+ version >> 4, version & 0xf);
+
if (length < 3) {
// report a problem when the remaining bytes are not 0.
if (tag || x[offset + 1]) {
@@ -1546,26 +1570,31 @@ void edid_state::parse_displayid_block(const unsigned char *x)
printf(" %s:\n", data_block.c_str());
switch (tag) {
- case 0x00: parse_displayid_product_id(x + offset, false); break;
+ case 0x00: parse_displayid_product_id(x + offset); break;
case 0x01: parse_displayid_parameters(x + offset); break;
case 0x02: parse_displayid_color_characteristics(x + offset); break;
case 0x03:
+ check_displayid_datablock_revision(x + offset, 0, x[offset + 1] & 1);
for (i = 0; i < len / 20; i++)
parse_displayid_type_1_7_timing(&x[offset + 3 + (i * 20)], false);
break;
case 0x04:
+ check_displayid_datablock_revision(x + offset);
for (i = 0; i < len / 11; i++)
parse_displayid_type_2_timing(&x[offset + 3 + (i * 11)]);
break;
case 0x05:
+ check_displayid_datablock_revision(x + offset, 0, x[offset + 1] & 1);
for (i = 0; i < len / 3; i++)
parse_displayid_type_3_timing(&x[offset + 3 + (i * 3)]);
break;
case 0x06:
+ check_displayid_datablock_revision(x + offset, 0xc0, 1);
for (i = 0; i < len; i++)
parse_displayid_type_4_8_timing((x[offset + 1] & 0xc0) >> 6, x[offset + 3 + i]);
break;
case 0x07:
+ check_displayid_datablock_revision(x + offset);
for (i = 0; i < min(len, 10) * 8; i++)
if (x[offset + 3 + i / 8] & (1 << (i % 8))) {
char type[16];
@@ -1574,6 +1603,7 @@ void edid_state::parse_displayid_block(const unsigned char *x)
}
break;
case 0x08:
+ check_displayid_datablock_revision(x + offset);
for (i = 0; i < min(len, 8) * 8; i++)
if (x[offset + 3 + i / 8] & (1 << (i % 8))) {
char type[16];
@@ -1590,23 +1620,30 @@ void edid_state::parse_displayid_block(const unsigned char *x)
case 0x0f: parse_displayid_display_intf(x + offset); break;
case 0x10: parse_displayid_stereo_display_intf(x + offset); break;
case 0x11:
+ check_displayid_datablock_revision(x + offset);
for (i = 0; i < len / 7; i++)
parse_displayid_type_5_timing(&x[offset + 3 + (i * 7)]);
break;
case 0x12: parse_displayid_tiled_display_topology(x + offset, false); break;
case 0x13:
+ check_displayid_datablock_revision(x + offset);
for (i = 0; i < len; i += (x[offset + 3 + i + 2] & 0x40) ? 17 : 14)
parse_displayid_type_6_timing(&x[offset + 3 + i]);
break;
- case 0x20: parse_displayid_product_id(x + offset, true); break;
+ case 0x20: parse_displayid_product_id(x + offset); break;
case 0x21: parse_displayid_parameters_v2(x + offset); break;
case 0x22:
+ if (x[offset + 1] & 0x07)
+ check_displayid_datablock_revision(x + offset, 0x08, 1);
+ else
+ check_displayid_datablock_revision(x + offset);
if ((x[offset + 1] & 0x07) >= 1 && (x[offset + 1] & 0x08))
printf(" These timings support DSC pass-through\n");
for (i = 0; i < len / 20; i++)
parse_displayid_type_1_7_timing(&x[offset + 3 + i * 20], true);
break;
case 0x23:
+ check_displayid_datablock_revision(x + offset, 0xc8);
if (x[offset + 1] & 0x08) {
for (i = 0; i < len / 2; i++)
parse_displayid_type_4_8_timing((x[offset + 1] & 0xc0) >> 6,
@@ -1619,6 +1656,7 @@ void edid_state::parse_displayid_block(const unsigned char *x)
}
break;
case 0x24:
+ check_displayid_datablock_revision(x + offset);
for (i = 0; i < len / 6; i++)
parse_displayid_type_9_timing(&x[offset + 3 + i * 6]);
break;
@@ -1636,8 +1674,14 @@ void edid_state::parse_displayid_block(const unsigned char *x)
// fall-through
default: hex_block(" ", x + offset + 3, len); break;
}
+
+ if ((tag == 0x00 || tag == 0x20) &&
+ (!dispid.is_base_block || !first_data_block))
+ fail("%s is required to be the first DisplayID Data Block.\n",
+ data_block.c_str());
length -= len + 3;
offset += len + 3;
+ first_data_block = false;
}
/*
@@ -1652,4 +1696,21 @@ void edid_state::parse_displayid_block(const unsigned char *x)
data_block = "Padding";
fail("DisplayID padding contains non-zero bytes.\n");
}
+ dispid.is_base_block = false;
+}
+
+void edid_state::check_displayid_blocks()
+{
+ data_block = "DisplayID";
+ if (!dispid.has_product_identification)
+ fail("Missing DisplayID Product Identification Data Block.\n");
+ if (dispid.is_display && !dispid.has_display_parameters)
+ fail("Missing DisplayID Display Parameters Data Block.\n");
+ if (dispid.is_display && !dispid.has_display_interface_features)
+ fail("Missing DisplayID Display Interface Features Data Block.\n");
+ if (dispid.is_display && !dispid.has_type_1_7)
+ fail("Missing DisplayID Type %s Detailed Timing Data Block.\n",
+ dispid.version >= 0x20 ? "VII" : "I");
+ if (dispid.preferred_timings.empty())
+ fail("DisplayID expects at least one preferred timing.\n");
}
diff --git a/parse-vtb-ext-block.cpp b/parse-vtb-ext-block.cpp
index cd1553a..4936f1e 100644
--- a/parse-vtb-ext-block.cpp
+++ b/parse-vtb-ext-block.cpp
@@ -7,11 +7,6 @@
#include "edid-decode.h"
-void edid_state::preparse_vtb_ext_block(const unsigned char *x)
-{
- preparse_total_dtds += x[2];
-}
-
void edid_state::parse_vtb_ext_block(const unsigned char *x)
{
printf(" Version: %u\n", x[1]);
@@ -26,7 +21,7 @@ void edid_state::parse_vtb_ext_block(const unsigned char *x)
if (num_dtd) {
printf(" Detailed Timing Descriptors:\n");
for (unsigned i = 0; i < num_dtd; i++, x += 18)
- detailed_timings(" ", x);
+ detailed_timings(" ", x, false);
}
if (num_cvt) {
printf(" Coordinated Video Timings:\n");
diff --git a/test/README b/test/README
index 806d0c7..abfa482 100644
--- a/test/README
+++ b/test/README
@@ -13,7 +13,7 @@ xrandr.test: an EDID as reported by 'xrandr --props'.
Two hand-crafted EDIDs to test rarely seen EDID extensions or data blocks:
cta-vesa.test: has VESA Display Transfer Characteristics Data Block and
- VESA Video Display Device Data Block in the CTA block.
+ VESA Video Display Device Data Block in the CTA-861 block.
ls-ext.test: has a Localized String Extension Block.
diff --git a/vs/edid-decode.sln b/vs/edid-decode.sln
new file mode 100644
index 0000000..53fc406
--- /dev/null
+++ b/vs/edid-decode.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30011.22
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "edid-decode", "edid-decode.vcxproj", "{245F601B-02E4-43DF-B64C-B49088673764}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {245F601B-02E4-43DF-B64C-B49088673764}.Debug|x64.ActiveCfg = Debug|x64
+ {245F601B-02E4-43DF-B64C-B49088673764}.Debug|x64.Build.0 = Debug|x64
+ {245F601B-02E4-43DF-B64C-B49088673764}.Release|x64.ActiveCfg = Release|x64
+ {245F601B-02E4-43DF-B64C-B49088673764}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {DDBA565E-4EC9-46DF-88E0-12F489BB3FCF}
+ EndGlobalSection
+EndGlobal
diff --git a/vs/edid-decode.vcxproj b/vs/edid-decode.vcxproj
new file mode 100644
index 0000000..c9213ab
--- /dev/null
+++ b/vs/edid-decode.vcxproj
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>16.0</VCProjectVersion>
+ <ProjectGuid>{245F601B-02E4-43DF-B64C-B49088673764}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>ediddecode</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4244; 4018; 4267; 4996; 26451; 6385; 6001</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
+ <PostBuildEvent>
+ <Message>
+ </Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4244; 4018; 4267; 4996; 26451; 6385; 6001</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="getopt.c" />
+ <ClCompile Include="..\edid-decode.cpp" />
+ <ClCompile Include="..\parse-base-block.cpp" />
+ <ClCompile Include="..\parse-cta-block.cpp" />
+ <ClCompile Include="..\parse-di-ext-block.cpp" />
+ <ClCompile Include="..\parse-displayid-block.cpp" />
+ <ClCompile Include="..\parse-ls-ext-block.cpp" />
+ <ClCompile Include="..\parse-vtb-ext-block.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="getopt.h" />
+ <ClInclude Include="unistd.h" />
+ <ClInclude Include="..\edid-decode.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/vs/edid-decode.vcxproj.filters b/vs/edid-decode.vcxproj.filters
new file mode 100644
index 0000000..68e8b7d
--- /dev/null
+++ b/vs/edid-decode.vcxproj.filters
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="windows-unix">
+ <UniqueIdentifier>{757b13a8-3e1d-43af-acf1-d03963acb2e6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="edid-decode">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\parse-base-block.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ <ClCompile Include="..\parse-cta-block.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ <ClCompile Include="..\parse-di-ext-block.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ <ClCompile Include="..\parse-displayid-block.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ <ClCompile Include="..\parse-ls-ext-block.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ <ClCompile Include="..\parse-vtb-ext-block.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ <ClCompile Include="getopt.c">
+ <Filter>windows-unix</Filter>
+ </ClCompile>
+ <ClCompile Include="..\edid-decode.cpp">
+ <Filter>edid-decode</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\edid-decode.h">
+ <Filter>edid-decode</Filter>
+ </ClInclude>
+ <ClInclude Include="getopt.h">
+ <Filter>windows-unix</Filter>
+ </ClInclude>
+ <ClInclude Include="unistd.h">
+ <Filter>windows-unix</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/vs/getopt.c b/vs/getopt.c
new file mode 100644
index 0000000..d5868a5
--- /dev/null
+++ b/vs/getopt.c
@@ -0,0 +1,787 @@
+/**
+ * @file getopt.c
+ * @copy 2012 MinGW.org project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Implementation of the `getopt', `getopt_long' and `getopt_long_only'
+ * APIs, for inclusion in the MinGW runtime library.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <getopt.h>
+
+/* Identify how to get the calling program name, for use in messages...
+ */
+#ifdef __CYGWIN__
+/*
+ * CYGWIN uses this DLL reference...
+ */
+# define PROGNAME __progname
+extern char __declspec(dllimport) *__progname;
+#else
+/*
+ * ...while elsewhere, we simply use the first argument passed.
+ */
+# define PROGNAME *argv
+# define __inline__ __inline
+#endif
+
+/* Initialise the public variables. */
+
+int optind = 1; /* index for first non-option arg */
+int opterr = 1; /* enable built-in error messages */
+
+char *optarg = NULL; /* pointer to current option argument */
+
+#define CHAR char /* argument type selector */
+
+#define getopt_switchar '-' /* option prefix character in argv */
+#define getopt_pluschar '+' /* prefix for POSIX mode in optstring */
+#define getopt_takes_argument ':' /* marker for optarg in optstring */
+#define getopt_arg_assign '=' /* longopt argument field separator */
+#define getopt_unknown '?' /* return code for unmatched option */
+#define getopt_ordered 1 /* return code for ordered non-option */
+
+#define getopt_all_done -1 /* return code to indicate completion */
+
+enum
+{ /* All `getopt' API functions are implemented via calls to the
+ * common static function `getopt_parse()'; these `mode' selectors
+ * determine the behaviour of `getopt_parse()', to deliver the
+ * appropriate result in each case.
+ */
+ getopt_mode_standard = 0, /* getopt() */
+ getopt_mode_long, /* getopt_long() */
+ getopt_mode_long_only /* getopt_long_only() */
+};
+
+enum
+{ /* When attempting to match a command line argument to a long form option,
+ * these indicate the status of the match.
+ */
+ getopt_no_match = 0, /* no successful match */
+ getopt_abbreviated_match, /* argument is an abbreviation for an option */
+ getopt_exact_match /* argument matches the full option name */
+};
+
+int optopt = getopt_unknown; /* return value for option being evaluated */
+
+/* Some BSD applications expect to be able to reinitialise `getopt' parsing
+ * by setting a global variable called `optreset'. We provide an obfuscated
+ * API, which allows applications to emulate this brain damage; however, any
+ * use of this is non-portable, and is strongly discouraged.
+ */
+#define optreset __mingw_optreset
+int optreset = 0;
+
+static __inline__
+int getopt_missing_arg( const CHAR *optstring )
+{
+ /* Helper function to determine the appropriate return value,
+ * for the case where a required option argument is missing.
+ */
+ if( (*optstring == getopt_pluschar) || (*optstring == getopt_switchar) )
+ ++optstring;
+ return (*optstring == getopt_takes_argument)
+ ? getopt_takes_argument
+ : getopt_unknown;
+}
+
+/* `complain' macro facilitates the generation of simple built-in
+ * error messages, displayed on various fault conditions, provided
+ * `opterr' is non-zero.
+ */
+#define complain( MSG, ARG ) if( opterr ) \
+ fprintf( stderr, "%s: "MSG"\n", PROGNAME, ARG )
+
+static __inline__
+int getopt_argerror( int mode, char *fmt, CHAR *prog, struct option *opt, int retval )
+{
+ /* Helper function, to generate more complex built-in error
+ * messages, for invalid arguments to long form options ...
+ */
+ if( opterr )
+ {
+ /* ... but, displayed only if `opterr' is non-zero.
+ */
+ char flag[] = "--";
+ if( mode != getopt_mode_long )
+ /*
+ * only display one hyphen, for implicit long form options,
+ * improperly resolved by `getopt_long_only()'.
+ */
+ flag[1] = 0;
+ /*
+ * always preface the program name ...
+ */
+ fprintf( stderr, "%s: ", prog );
+ /*
+ * to the appropriate, option specific message.
+ */
+ fprintf( stderr, fmt, flag, opt->name );
+ }
+ /* Whether displaying the message, or not, always set `optopt'
+ * to identify the faulty option ...
+ */
+ optopt = opt->val;
+ /*
+ * and return the `invalid option' indicator.
+ */
+ return retval;
+}
+
+/* `getopt_conventions' establish behavioural options, to control
+ * the operation of `getopt_parse()', e.g. to select between POSIX
+ * and GNU style argument parsing behaviour.
+ */
+#define getopt_set_conventions 0x1000
+#define getopt_posixly_correct 0x0010
+
+static __inline__
+int getopt_conventions( int flags )
+{
+ static int conventions = 0;
+
+ if( (conventions == 0) && ((flags & getopt_set_conventions) == 0) )
+ {
+ /* default conventions have not yet been established;
+ * initialise them now!
+ */
+ conventions = getopt_set_conventions;
+ if( (flags == getopt_pluschar) || (getenv( "POSIXLY_CORRECT" ) != NULL) )
+ conventions |= getopt_posixly_correct;
+ }
+
+ else if( flags & getopt_set_conventions )
+ /*
+ * default conventions may have already been established,
+ * but this is a specific request to augment them.
+ */
+ conventions |= flags;
+
+ /* in any event, return the currently established conventions.
+ */
+ return conventions;
+}
+
+static __inline__
+int is_switchar( CHAR flag )
+{
+ /* A simple helper function, used to identify the switch character
+ * introducing an optional command line argument.
+ */
+ return flag == getopt_switchar;
+}
+
+static __inline__
+const CHAR *getopt_match( CHAR lookup, const CHAR *opt_string )
+{
+ /* Helper function, used to identify short form options.
+ */
+ if( (*opt_string == getopt_pluschar) || (*opt_string == getopt_switchar) )
+ ++opt_string;
+ if( *opt_string == getopt_takes_argument )
+ ++opt_string;
+ do if( lookup == *opt_string ) return opt_string;
+ while( *++opt_string );
+ return NULL;
+}
+
+static __inline__
+int getopt_match_long( const CHAR *nextchar, const CHAR *optname )
+{
+ /* Helper function, used to identify potential matches for
+ * long form options.
+ */
+ CHAR matchchar;
+ while( (matchchar = *nextchar++) && (matchchar == *optname) )
+ /*
+ * skip over initial substring which DOES match.
+ */
+ ++optname;
+
+ if( matchchar )
+ {
+ /* did NOT match the entire argument to an initial substring
+ * of a defined option name ...
+ */
+ if( matchchar != getopt_arg_assign )
+ /*
+ * ... and didn't stop at an `=' internal field separator,
+ * so this is NOT a possible match.
+ */
+ return getopt_no_match;
+
+ /* DID stop at an `=' internal field separator,
+ * so this IS a possible match, and what follows is an
+ * argument to the possibly matched option.
+ */
+ optarg = (char *)(nextchar);
+ }
+ return *optname
+ /*
+ * if we DIDN'T match the ENTIRE text of the option name,
+ * then it's a possible abbreviated match ...
+ */
+ ? getopt_abbreviated_match
+ /*
+ * but if we DID match the entire option name,
+ * then it's a DEFINITE EXACT match.
+ */
+ : getopt_exact_match;
+}
+
+static __inline__
+int getopt_resolved( int mode, int argc, CHAR *const *argv, int *argind,
+struct option *opt, int index, int *retindex, const CHAR *optstring )
+{
+ /* Helper function to establish appropriate return conditions,
+ * on resolution of a long form option.
+ */
+ if( retindex != NULL )
+ *retindex = index;
+
+ /* On return, `optind' should normally refer to the argument, if any,
+ * which follows the current one; it is convenient to set this, before
+ * checking for the presence of any `optarg'.
+ */
+ optind = *argind + 1;
+
+ if( optarg && (opt[index].has_arg == no_argument) )
+ /*
+ * it is an error for the user to specify an option specific argument
+ * with an option which doesn't expect one!
+ */
+ return getopt_argerror( mode, "option `%s%s' doesn't accept an argument\n",
+ PROGNAME, opt + index, getopt_unknown );
+
+ else if( (optarg == NULL) && (opt[index].has_arg == required_argument) )
+ {
+ /* similarly, it is an error if no argument is specified
+ * with an option which requires one ...
+ */
+ if( optind < argc )
+ /*
+ * ... except that the requirement may be satisfied from
+ * the following command line argument, if any ...
+ */
+ optarg = argv[*argind = optind++];
+
+ else
+ /* so fail this case, only if no such argument exists!
+ */
+ return getopt_argerror( mode, "option `%s%s' requires an argument\n",
+ PROGNAME, opt + index, getopt_missing_arg( optstring ) );
+ }
+
+ /* when the caller has provided a return buffer ...
+ */
+ if( opt[index].flag != NULL )
+ {
+ /* ... then we place the proper return value there,
+ * and return a status code of zero ...
+ */
+ *(opt[index].flag) = opt[index].val;
+ return 0;
+ }
+ /* ... otherwise, the return value becomes the status code.
+ */
+ return opt[index].val;
+}
+
+static __inline__
+int getopt_verify( const CHAR *nextchar, const CHAR *optstring )
+{
+ /* Helper function, called by getopt_parse() when invoked
+ * by getopt_long_only(), to verify when an unmatched or an
+ * ambiguously matched long form option string is valid as
+ * a short form option specification.
+ */
+ if( ! (nextchar && *nextchar && optstring && *optstring) )
+ /*
+ * There are no characters to be matched, or there are no
+ * valid short form option characters to which they can be
+ * matched, so this can never be valid.
+ */
+ return 0;
+
+ while( *nextchar )
+ {
+ /* For each command line character in turn ...
+ */
+ const CHAR *test;
+ if( (test = getopt_match( *nextchar++, optstring )) == NULL )
+ /*
+ * ... there is no short form option to match the current
+ * candidate, so the entire argument fails.
+ */
+ return 0;
+
+ if( test[1] == getopt_takes_argument )
+ /*
+ * The current candidate is valid, and it matches an option
+ * which takes an argument, so this command line argument is
+ * a valid short form option specification; accept it.
+ */
+ return 1;
+ }
+ /* If we get to here, then every character in the command line
+ * argument was valid as a short form option; accept it.
+ */
+ return 1;
+}
+
+static
+#define getopt_std_args int argc, CHAR *const argv[], const CHAR *optstring
+int getopt_parse( int mode, getopt_std_args, ... )
+{
+ /* Common core implementation for ALL `getopt' functions.
+ */
+ static int argind = 0;
+ static int optbase = 0;
+ static const CHAR *nextchar = NULL;
+ static int optmark = 0;
+
+ if( (optreset |= (optind < 1)) || (optind < optbase) )
+ {
+ /* POSIX does not prescribe any definitive mechanism for restarting
+ * a `getopt' scan, but some applications may require such capability.
+ * We will support it, by allowing the caller to adjust the value of
+ * `optind' downwards, (nominally setting it to zero). Since POSIX
+ * wants `optind' to have an initial value of one, but we want all
+ * of our internal place holders to be initialised to zero, when we
+ * are called for the first time, we will handle such a reset by
+ * adjusting all of the internal place holders to one less than
+ * the adjusted `optind' value, (but never to less than zero).
+ */
+ if( optreset )
+ {
+ /* User has explicitly requested reinitialisation...
+ * We need to reset `optind' to it's normal initial value of 1,
+ * to avoid a potential infinitely recursive loop; by doing this
+ * up front, we also ensure that the remaining place holders
+ * will be correctly reinitialised to no less than zero.
+ */
+ optind = 1;
+
+ /* We also need to clear the `optreset' request...
+ */
+ optreset = 0;
+ }
+
+ /* Now, we may safely reinitialise the internal place holders, to
+ * one less than `optind', without fear of making them negative.
+ */
+ optmark = optbase = argind = optind - 1;
+ nextchar = NULL;
+ }
+
+ /* From a POSIX perspective, the following is `undefined behaviour';
+ * we implement it thus, for compatibility with GNU and BSD getopt.
+ */
+ else if( optind > (argind + 1) )
+ {
+ /* Some applications expect to be able to manipulate `optind',
+ * causing `getopt' to skip over one or more elements of `argv';
+ * POSIX doesn't require us to support this brain-damaged concept;
+ * (indeed, POSIX defines no particular behaviour, in the event of
+ * such usage, so it must be considered a bug for an application
+ * to rely on any particular outcome); nonetheless, Mac-OS-X and
+ * BSD actually provide *documented* support for this capability,
+ * so we ensure that our internal place holders keep track of
+ * external `optind' increments; (`argind' must lag by one).
+ */
+ argind = optind - 1;
+
+ /* When `optind' is misused, in this fashion, we also abandon any
+ * residual text in the argument we had been parsing; this is done
+ * without any further processing of such abandoned text, assuming
+ * that the caller is equipped to handle it appropriately.
+ */
+ nextchar = NULL;
+ }
+
+ if( nextchar && *nextchar )
+ {
+ /* we are parsing a standard, or short format, option argument ...
+ */
+ const CHAR *optchar;
+ if( (optchar = getopt_match( optopt = *nextchar++, optstring )) != NULL )
+ {
+ /* we have identified it as valid ...
+ */
+ if( optchar[1] == getopt_takes_argument )
+ {
+ /* and determined that it requires an associated argument ...
+ */
+ if( ! *(optarg = (char *)(nextchar)) )
+ {
+ /* the argument is NOT attached ...
+ */
+ if( optchar[2] == getopt_takes_argument )
+ /*
+ * but this GNU extension marks it as optional,
+ * so we don't provide one on this occasion.
+ */
+ optarg = NULL;
+
+ /* otherwise this option takes a mandatory argument,
+ * so, provided there is one available ...
+ */
+ else if( (argc - argind) > 1 )
+ /*
+ * we take the following command line argument,
+ * as the appropriate option argument.
+ */
+ optarg = argv[++argind];
+
+ /* but if no further argument is available,
+ * then there is nothing we can do, except for
+ * issuing the requisite diagnostic message.
+ */
+ else
+ {
+ complain( "option requires an argument -- %c", optopt );
+ return getopt_missing_arg( optstring );
+ }
+ }
+ optind = argind + 1;
+ nextchar = NULL;
+ }
+ else
+ optarg = NULL;
+ optind = (nextchar && *nextchar) ? argind : argind + 1;
+ return optopt;
+ }
+ /* if we didn't find a valid match for the specified option character,
+ * then we fall through to here, so take appropriate diagnostic action.
+ */
+ if( mode == getopt_mode_long_only )
+ {
+ complain( "unrecognised option `-%s'", --nextchar );
+ nextchar = NULL;
+ optopt = 0;
+ }
+ else
+ complain( "invalid option -- %c", optopt );
+ optind = (nextchar && *nextchar) ? argind : argind + 1;
+ return getopt_unknown;
+ }
+
+ if( optmark > optbase )
+ {
+ /* This can happen, in GNU parsing mode ONLY, when we have
+ * skipped over non-option arguments, and found a subsequent
+ * option argument; in this case we permute the arguments.
+ */
+ int index;
+ /*
+ * `optspan' specifies the number of contiguous arguments
+ * which are spanned by the current option, and so must be
+ * moved together during permutation.
+ */
+ int optspan = argind - optmark + 1;
+ /*
+ * we use `this_arg' to store these temporarily.
+ */
+ CHAR **this_arg = (CHAR **)malloc(optspan * sizeof(CHAR *));
+ if( this_arg == NULL )
+ return getopt_unknown;
+ /*
+ * we cannot manipulate `argv' directly, since the `getopt'
+ * API prototypes it as `read-only'; this cast to `arglist'
+ * allows us to work around that restriction.
+ */
+ CHAR **arglist = (char **)(argv);
+
+ /* save temporary copies of the arguments which are associated
+ * with the current option ...
+ */
+ for( index = 0; index < optspan; ++index )
+ this_arg[index] = arglist[optmark + index];
+
+ /* move all preceding non-option arguments to the right,
+ * overwriting these saved arguments, while making space
+ * to replace them in their permuted location.
+ */
+ for( --optmark; optmark >= optbase; --optmark )
+ arglist[optmark + optspan] = arglist[optmark];
+
+ /* restore the temporarily saved option arguments to
+ * their permuted location.
+ */
+ for( index = 0; index < optspan; ++index )
+ arglist[optbase + index] = this_arg[index];
+
+ free(this_arg);
+
+ /* adjust `optbase', to account for the relocated option.
+ */
+ optbase += optspan;
+ }
+
+ else
+ /* no permutation occurred ...
+ * simply adjust `optbase' for all options parsed so far.
+ */
+ optbase = argind + 1;
+
+ /* enter main parsing loop ...
+ */
+ while( argc > ++argind )
+ {
+ /* inspect each argument in turn, identifying possible options ...
+ */
+ if( is_switchar( *(nextchar = argv[optmark = argind]) ) && *++nextchar )
+ {
+ /* we've found a candidate option argument ... */
+
+ if( is_switchar( *nextchar ) )
+ {
+ /* it's a double hyphen argument ... */
+
+ const CHAR *refchar = nextchar;
+ if( *++refchar )
+ {
+ /* and it looks like a long format option ...
+ * `getopt_long' mode must be active to accept it as such,
+ * `getopt_long_only' also qualifies, but we must downgrade
+ * it to force explicit handling as a long format option.
+ */
+ if( mode >= getopt_mode_long )
+ {
+ nextchar = refchar;
+ mode = getopt_mode_long;
+ }
+ }
+ else
+ {
+ /* this is an explicit `--' end of options marker, so wrap up now!
+ */
+ if( optmark > optbase )
+ {
+ /* permuting the argument list as necessary ...
+ * (note use of `this_arg' and `arglist', as above).
+ */
+ CHAR *this_arg = argv[optmark];
+ CHAR **arglist = (CHAR **)(argv);
+
+ /* move all preceding non-option arguments to the right ...
+ */
+ do arglist[optmark] = arglist[optmark - 1];
+ while( optmark-- > optbase );
+
+ /* reinstate the `--' marker, in its permuted location.
+ */
+ arglist[optbase] = this_arg;
+ }
+ /* ... before finally bumping `optbase' past the `--' marker,
+ * and returning the `all done' completion indicator.
+ */
+ optind = ++optbase;
+ return getopt_all_done;
+ }
+ }
+ else if( mode < getopt_mode_long_only )
+ {
+ /* it's not an explicit long option, and `getopt_long_only' isn't active,
+ * so we must explicitly try to match it as a short option.
+ */
+ mode = getopt_mode_standard;
+ }
+
+ if( mode >= getopt_mode_long )
+ {
+ /* the current argument is a long form option, (either explicitly,
+ * introduced by a double hyphen, or implicitly because we were called
+ * by `getopt_long_only'); this is where we parse it.
+ */
+ int lookup;
+ int matched = -1;
+
+ /* we need to fetch the `extra' function arguments, which are
+ * specified for the `getopt_long' APIs.
+ */
+ va_list refptr;
+ va_start( refptr, optstring );
+ struct option *longopts = va_arg( refptr, struct option * );
+ int *optindex = va_arg( refptr, int * );
+ va_end( refptr );
+
+ /* ensuring that `optarg' does not inherit any junk, from parsing
+ * preceding arguments ...
+ */
+ optarg = NULL;
+ for( lookup = 0; longopts && longopts[lookup].name; ++lookup )
+ {
+ /* scan the list of defined long form options ...
+ */
+ switch( getopt_match_long( nextchar, longopts[lookup].name ) )
+ {
+ /* looking for possible matches for the current argument.
+ */
+ case getopt_exact_match:
+ /*
+ * when an exact match is found,
+ * return it immediately, setting `nextchar' to NULL,
+ * to ensure we don't mistakenly try to match any
+ * subsequent characters as short form options.
+ */
+ nextchar = NULL;
+ return getopt_resolved( mode, argc, argv, &argind,
+ longopts, lookup, optindex, optstring );
+
+ case getopt_abbreviated_match:
+ /*
+ * but, for a partial (initial substring) match ...
+ */
+ if( matched >= 0 )
+ {
+ /* if this is not the first, then we have an ambiguity ...
+ */
+ if( (mode == getopt_mode_long_only)
+ /*
+ * However, in the case of getopt_long_only(), if
+ * the entire ambiguously matched string represents
+ * a valid short option specification, then we may
+ * proceed to interpret it as such.
+ */
+ && getopt_verify( nextchar, optstring ) )
+ return getopt_parse( mode, argc, argv, optstring );
+
+ /* If we get to here, then the ambiguously matched
+ * partial long option isn't valid for short option
+ * evaluation; reset parser context to resume with
+ * the following command line argument, diagnose
+ * ambiguity, and bail out.
+ */
+ optopt = 0;
+ nextchar = NULL;
+ optind = argind + 1;
+ complain( "option `%s' is ambiguous", argv[argind] );
+ return getopt_unknown;
+ }
+ /* otherwise just note that we've found a possible match ...
+ */
+ matched = lookup;
+ }
+ }
+ if( matched >= 0 )
+ {
+ /* if we get to here, then we found exactly one partial match,
+ * so return it, as for an exact match.
+ */
+ nextchar = NULL;
+ return getopt_resolved( mode, argc, argv, &argind,
+ longopts, matched, optindex, optstring );
+ }
+ /* if here, then we had what SHOULD have been a long form option,
+ * but it is unmatched ...
+ */
+ if( (mode < getopt_mode_long_only)
+ /*
+ * ... although paradoxically, `mode == getopt_mode_long_only'
+ * allows us to still try to match it as a short form option.
+ */
+ || (getopt_verify( nextchar, optstring ) == 0) )
+ {
+ /* When it cannot be matched, reset the parsing context to
+ * resume from the next argument, diagnose the failed match,
+ * and bail out.
+ */
+ optopt = 0;
+ nextchar = NULL;
+ optind = argind + 1;
+ complain( "unrecognised option `%s'", argv[argind] );
+ return getopt_unknown;
+ }
+ }
+ /* fall through to handle standard short form options...
+ * when the option argument format is neither explictly identified
+ * as long, nor implicitly matched as such, and the argument isn't
+ * just a bare hyphen, (which isn't an option), then we make one
+ * recursive call to explicitly interpret it as short format.
+ */
+ if( *nextchar )
+ return getopt_parse( mode, argc, argv, optstring );
+ }
+ /* if we get to here, then we've parsed a non-option argument ...
+ * in GNU compatibility mode, we step over it, so we can permute
+ * any subsequent option arguments, but ...
+ */
+ if( *optstring == getopt_switchar )
+ {
+ /* if `optstring' begins with a `-' character, this special
+ * GNU specific behaviour requires us to return the non-option
+ * arguments in strict order, as pseudo-arguments to a special
+ * option, with return value defined as `getopt_ordered'.
+ */
+ nextchar = NULL;
+ optind = argind + 1;
+ optarg = argv[argind];
+ return getopt_ordered;
+ }
+ if( getopt_conventions( *optstring ) & getopt_posixly_correct )
+ /*
+ * otherwise ...
+ * for POSIXLY_CORRECT behaviour, or if `optstring' begins with
+ * a `+' character, then we break out of the parsing loop, so that
+ * the scan ends at the current argument, with no permutation.
+ */
+ break;
+ }
+ /* fall through when all arguments have been evaluated,
+ */
+ optind = optbase;
+ return getopt_all_done;
+}
+
+/* All three public API entry points are trivially defined,
+ * in terms of the internal `getopt_parse' function.
+ */
+int getopt( getopt_std_args )
+{
+ return getopt_parse( getopt_mode_standard, argc, argv, optstring );
+}
+
+int getopt_long( getopt_std_args, const struct option *opts, int *index )
+{
+ return getopt_parse( getopt_mode_long, argc, argv, optstring, opts, index );
+}
+
+int getopt_long_only( getopt_std_args, const struct option *opts, int *index )
+{
+ return getopt_parse( getopt_mode_long_only, argc, argv, optstring, opts, index );
+}
+
+#ifdef __weak_alias
+/*
+ * These Microsnot style uglified aliases are provided for compatibility
+ * with the previous MinGW implementation of the getopt API.
+ */
+__weak_alias( getopt, _getopt )
+__weak_alias( getopt_long, _getopt_long )
+__weak_alias( getopt_long_only, _getopt_long_only )
+#endif
diff --git a/vs/getopt.h b/vs/getopt.h
new file mode 100644
index 0000000..e34761d
--- /dev/null
+++ b/vs/getopt.h
@@ -0,0 +1,107 @@
+/**
+ * @file getopt.h
+ * @copy 2012 MinGW.org project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#ifndef _GETOPT_H
+#define _GETOPT_H
+
+/*
+ * Defines constants and function prototypes required to implement
+ * the `getopt', `getopt_long' and `getopt_long_only' APIs.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int optind; /* index of first non-option in argv */
+extern int optopt; /* single option character, as parsed */
+extern int opterr; /* flag to enable built-in diagnostics... */
+ /* (user may set to zero, to suppress) */
+
+extern char *optarg; /* pointer to argument of current option */
+
+extern int getopt( int, char * const [], const char * );
+
+#ifdef _BSD_SOURCE
+/*
+ * BSD adds the non-standard `optreset' feature, for reinitialisation
+ * of `getopt' parsing. We support this feature, for applications which
+ * proclaim their BSD heritage, before including this header; however,
+ * to maintain portability, developers are advised to avoid it.
+ */
+# define optreset __mingw_optreset
+
+extern int optreset;
+#endif
+#ifdef __cplusplus
+}
+#endif
+/*
+ * POSIX requires the `getopt' API to be specified in `unistd.h';
+ * thus, `unistd.h' includes this header. However, we do not want
+ * to expose the `getopt_long' or `getopt_long_only' APIs, when
+ * included in this manner. Thus, close the standard __GETOPT_H__
+ * declarations block, and open an additional __GETOPT_LONG_H__
+ * specific block, only when *not* __UNISTD_H_SOURCED__, in which
+ * to declare the extended API.
+ */
+#endif /* !defined(__GETOPT_H__) */
+#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__)
+#define __GETOPT_LONG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct option /* specification for a long form option... */
+{
+ const char *name; /* option name, without leading hyphens */
+ int has_arg; /* does it take an argument? */
+ int *flag; /* where to save its status, or NULL */
+ int val; /* its associated status value */
+};
+
+enum /* permitted values for its `has_arg' field... */
+{
+ no_argument = 0, /* option never takes an argument */
+ required_argument, /* option always requires an argument */
+ optional_argument /* option may take an argument */
+};
+
+extern int getopt_long( int, char * const [], const char *, const struct option *, int * );
+extern int getopt_long_only( int, char * const [], const char *, const struct option *, int * );
+/*
+ * Previous MinGW implementation had...
+ */
+#ifndef HAVE_DECL_GETOPT
+/*
+ * ...for the long form API only; keep this for compatibility.
+ */
+# define HAVE_DECL_GETOPT 1
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */
diff --git a/vs/unistd.h b/vs/unistd.h
new file mode 100644
index 0000000..afa0d18
--- /dev/null
+++ b/vs/unistd.h
@@ -0,0 +1,10 @@
+#ifndef _UNISTD_H
+#define _UNISTD_H 1
+
+#include <stdlib.h>
+#include <io.h>
+#include <getopt.h>
+
+#define ssize_t int
+
+#endif /* unistd.h */

Privacy Policy