diff options
author | Burt P <pburt0@gmail.com> | 2019-08-23 12:28:20 -0500 |
---|---|---|
committer | Leandro A. F. Pereira <leandro@hardinfo.org> | 2019-08-28 13:15:48 +0200 |
commit | a08f2a909f7a67f7ca81354a88b7c2e47dc6f19f (patch) | |
tree | d95b755c1655442a16b9516cc266e036c1ef412f /deps | |
parent | 695e8ef2bc0298356918a430e41458e510e7b6c5 (diff) |
monitors: displayid fixes
Signed-off-by: Burt P <pburt0@gmail.com>
Diffstat (limited to 'deps')
-rw-r--r-- | deps/sysobj_early/include/util_edid.h | 102 | ||||
-rw-r--r-- | deps/sysobj_early/src/util_edid.c | 575 |
2 files changed, 514 insertions, 163 deletions
diff --git a/deps/sysobj_early/include/util_edid.h b/deps/sysobj_early/include/util_edid.h index 6f1360b5..319900e2 100644 --- a/deps/sysobj_early/include/util_edid.h +++ b/deps/sysobj_early/include/util_edid.h @@ -21,25 +21,42 @@ #ifndef __UTIL_EDID_H__ #define __UTIL_EDID_H__ +#define _GNU_SOURCE #include <stdint.h> /* for *int*_t types */ +#include <glib.h> + +typedef struct _edid edid; + +typedef struct { + edid *e; + uint32_t offset; +} edid_addy; + +typedef struct { + char *str; + uint16_t len; + uint8_t is_product_name; + uint8_t is_serial; +} DisplayIDString; typedef struct { uint8_t version; uint8_t extension_length; uint8_t primary_use_case; uint8_t extension_count; - int blocks; - int checksum_ok; + uint16_t blocks; + uint8_t checksum_ok; } DisplayIDMeta; typedef struct { - uint8_t *ptr; + edid_addy addy; union { uint8_t tag; uint8_t type; }; uint8_t revision; uint8_t len; + uint8_t bounds_ok; } DisplayIDBlock; /* order by rising priority */ @@ -50,6 +67,7 @@ enum { OUTSRC_DTD, OUTSRC_CEA_DTD, OUTSRC_SVD, + OUTSRC_DID_TYPE_I, OUTSRC_DID_TYPE_VI, OUTSRC_DID_TYPE_VII, @@ -61,9 +79,9 @@ typedef struct { int horiz_blanking, vert_blanking; int horiz_pixels, vert_lines, vert_pixels; float vert_freq_hz; - int is_interlaced; + uint8_t is_interlaced; + uint8_t is_preferred; int stereo_mode; - int is_preferred; uint64_t pixel_clock_khz; int src; /* enum OUTSRC_* */ uint64_t pixels; /* h*v: easier to compare */ @@ -76,34 +94,57 @@ struct edid_std { }; struct edid_dtd { - uint8_t *ptr; - int cea_ext; + edid_addy addy; + uint8_t cea_ext; /* in a CEA block vs one of the regular EDID descriptors */ edid_output out; + uint8_t bounds_ok; }; struct edid_svd { uint8_t v; - int is_native; + uint8_t is_native; edid_output out; }; struct edid_sad { uint8_t v[3]; - int format, channels, freq_bits; + uint8_t format, channels, freq_bits; int depth_bits; /* format 1 */ int max_kbps; /* formats 2-8 */ }; -struct edid_cea_header { - uint8_t *ptr; +struct edid_cea_block { + edid_addy addy; int type, len; + uint8_t bounds_ok; }; -struct edid_cea_block { - struct edid_cea_header header; - int reserved[8]; +struct edid_descriptor { + edid_addy addy; + uint8_t type; + char text[14]; +}; + +struct edid_manf_date { + uint8_t week; + uint8_t is_model_year; /* ignore week */ + uint16_t year; + int std; /* enum STD_* */ +}; + +enum { + VEN_TYPE_INVALID = 0, + VEN_TYPE_PNP, + VEN_TYPE_OUI, }; +typedef struct { + //TODO: union? + char pnp[4]; + uint32_t oui; + uint8_t type; /* enum VEN_TYPE_* */ +} edid_ven; + enum { STD_EDID = 0, STD_EEDID = 1, @@ -112,21 +153,20 @@ enum { STD_DISPLAYID20 = 4, }; -typedef struct { +typedef struct _edid { union { void* data; uint8_t* u8; uint16_t* u16; - uint32_t* u32; }; unsigned int len; /* enum STD_* */ int std; - int ver_major, ver_minor; - int checksum_ok; /* first 128-byte block only */ - int ext_blocks, ext_blocks_ok, ext_blocks_fail; + uint8_t ver_major, ver_minor; + uint8_t checksum_ok; /* first 128-byte block only */ + uint8_t ext_blocks, ext_blocks_ok, ext_blocks_fail; uint8_t *ext_ok; int etb_count; @@ -147,34 +187,39 @@ typedef struct { int sad_count; struct edid_sad *sads; - char ven[4]; - int d_type[4]; - char d_text[4][14]; - /* point into d_text */ + edid_ven ven; + struct edid_descriptor d[4]; + /* point into d[].text */ char *name; char *serial; char *ut1; char *ut2; - int a_or_d; /* 0 = analog, 1 = digital */ - int interface; /* digital interface */ - int bpc; /* digital bpc */ + uint8_t a_or_d; /* 0 = analog, 1 = digital */ + uint8_t interface; /* digital interface */ + uint8_t bpc; /* digital bpc */ uint16_t product; uint32_t n_serial; - int week, year; + struct edid_manf_date dom; edid_output img; + edid_output img_svd; edid_output img_max; - int speaker_alloc_bits; + uint32_t speaker_alloc_bits; DisplayIDMeta did; int did_block_count; DisplayIDBlock *did_blocks; + int did_string_count; + DisplayIDString *did_strings; int didt_count; edid_output *didts; + + GString *msg_log; } edid; edid *edid_new(const char *data, unsigned int len); edid *edid_new_from_hex(const char *hex_string); +edid *edid_new_from_file(const char *path); void edid_free(edid *e); char *edid_dump_hex(edid *e, int tabs, int breaks); @@ -187,6 +232,7 @@ const char *edid_cea_block_type(int type); const char *edid_cea_audio_type(int type); char *edid_output_describe(edid_output *out); +char *edid_base_descriptor_describe(struct edid_descriptor *d); char *edid_dtd_describe(struct edid_dtd *dtd, int dump_bytes); char *edid_cea_block_describe(struct edid_cea_block *blk); char *edid_cea_audio_describe(struct edid_sad *sad); diff --git a/deps/sysobj_early/src/util_edid.c b/deps/sysobj_early/src/util_edid.c index 6d90f6ac..9c48a397 100644 --- a/deps/sysobj_early/src/util_edid.c +++ b/deps/sysobj_early/src/util_edid.c @@ -31,21 +31,154 @@ #include "util_edid_svd_table.c" -static int block_check_n(const void *edid_block_bytes, int len) { - if (!edid_block_bytes) return 0; +#define NOMASK (~0U) +#define BFMASK(LSB, MASK) (MASK << LSB) + +#define DPTR(ADDY) (uint8_t*)(&((ADDY).e->u8[(ADDY).offset])) +#define OFMT "@%03d" /* for addy.offset */ + +#define EDID_MSG_STDERR 0 +#define edid_msg(e, msg, ...) {\ + if (EDID_MSG_STDERR) fprintf (stderr, ">[%s;L%d] " msg "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + g_string_append_printf(e->msg_log, "[%s;L%d] " msg "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__); } + +static inline +uint32_t bf_value(uint32_t value, uint32_t mask) { + uint32_t result = value & mask; + if (result) + while(!(mask & 1)) { + result >>= 1; + mask >>= 1; + } + return result; +} + +static inline +uint8_t bounds_check(edid *e, uint32_t offset) { + if (!e) return 0; + if (offset > e->len) return 0; + return 1; +} + +static inline +char *rstr(edid *e, uint32_t offset, uint32_t len) { + if (!bounds_check(e, offset+len)) return NULL; + char *raw = malloc(len+1), *ret = NULL; + strncpy(raw, &e->u8[offset], len); + raw[len] = 0; + ret = g_utf8_make_valid(raw, len); + g_free(raw); + return ret; +} + +static inline +char *rstr_strip(edid *e, uint32_t offset, uint32_t len) { + if (!bounds_check(e, offset+len)) return NULL; + char *raw = malloc(len+1), *ret = NULL; + strncpy(raw, &e->u8[offset], len); + raw[len] = 0; + ret = g_strstrip(g_utf8_make_valid(raw, len)); + g_free(raw); + return ret; +} + +static inline +uint32_t r8(edid *e, uint32_t offset, uint32_t mask) { + if (!bounds_check(e, offset)) return 0; + return bf_value(e->u8[offset], mask); +} + +static inline +uint32_t r16le(edid *e, uint32_t offset, uint32_t mask) { + if (!bounds_check(e, offset+1)) return 0; + uint32_t v = (e->u8[offset+1] << 8) + e->u8[offset]; + return bf_value(v, mask); +} + +static inline +uint32_t r16be(edid *e, uint32_t offset, uint32_t mask) { + if (!bounds_check(e, offset+1)) return 0; + uint32_t v = (e->u8[offset] << 8) + e->u8[offset+1]; + return bf_value(v, mask); +} + +static inline +uint32_t r24le(edid *e, uint32_t offset, uint32_t mask) { + if (!bounds_check(e, offset+2)) return 0; + uint32_t v = (e->u8[offset+2] << 16) + (e->u8[offset+1] << 8) + e->u8[offset]; + return bf_value(v, mask); +} + +static inline +uint32_t r24be(edid *e, uint32_t offset, uint32_t mask) { + if (!bounds_check(e, offset+2)) return 0; + uint32_t v = (e->u8[offset] << 16) + (e->u8[offset+1] << 8) + e->u8[offset+2]; + return bf_value(v, mask); +} + +static inline +uint32_t r32le(edid *e, uint32_t offset, uint32_t mask) { + if (!bounds_check(e, offset+3)) return 0; + uint32_t v = (e->u8[offset+3] << 24) + (e->u8[offset+2] << 16) + + (e->u8[offset+1] << 8) + e->u8[offset]; + return bf_value(v, mask); +} + +static inline +uint32_t r32be(edid *e, uint32_t offset, uint32_t mask) { + if (!bounds_check(e, offset+3)) return 0; + uint32_t v = (e->u8[offset] << 24) + (e->u8[offset+1] << 16) + + (e->u8[offset+2] << 8) + e->u8[offset+3]; + return bf_value(v, mask); +} + +static inline +int rpnpcpy(edid_ven *dest, edid *e, uint32_t offset) { + uint32_t pnp = r16be(e, offset, NOMASK); + edid_ven ret = {.type = VEN_TYPE_INVALID}; + if (pnp) { + ret.type = VEN_TYPE_PNP; + ret.pnp[2] = 64 + (pnp & 0x1f); + ret.pnp[1] = 64 + ((pnp >> 5) & 0x1f); + ret.pnp[0] = 64 + ((pnp >> 10) & 0x1f); + *dest = ret; + return 1; + } + return 0; +} + +static inline +int rouicpy(edid_ven *dest, edid *e, uint32_t offset) { + edid_ven ret = {.type = VEN_TYPE_OUI}; + ret.oui = r24be(e, offset, NOMASK); + if (ret.oui) { + *dest = ret; + return 1; + } + return 0; +} + +static int _block_check_n(const void *bytes, int len) { + if (!bytes) return 0; uint8_t sum = 0; - uint8_t *data = (uint8_t*)edid_block_bytes; + uint8_t *data = (uint8_t*)bytes; int i; for(i=0; i<len; i++) sum += data[i]; return sum == 0 ? 1 : 0; } -static int block_check(const void *edid_block_bytes) { - /* block must be 128 bytes */ - return block_check_n(edid_block_bytes, 128); + +static int block_check_n(edid *e, uint32_t offset, int len) { + if (!bounds_check(e, offset+len)) return 0; + return _block_check_n(e->u8, len); } -char *hex_bytes(uint8_t *bytes, int count) { +static int block_check(edid *e, uint32_t offset) { + if (!bounds_check(e, offset+128)) return 0; + return _block_check_n(e->u8, 128); +} + +static char *hex_bytes(uint8_t *bytes, int count) { char *buffer = malloc(count*3+1), *p = buffer; memset(buffer, 0, count*3+1); int i; @@ -95,67 +228,115 @@ static void edid_output_fill(edid_output *out) { out->pixels *= out->vert_pixels; if (out->diag_in) { - static const char *inlbl = "\""; /* TODO: unicode */ + static const char *inlbl = "\u2033"; /* double prime */ sprintf(out->class_inch, "%0.1f%s", out->diag_in, inlbl); util_strchomp_float(out->class_inch); } } -static void cea_block_decode(struct edid_cea_block *blk, edid *e) { +static void cea_block_decode(struct edid_cea_block *blk) { + if (!blk) return; + if (!blk->bounds_ok) + blk->bounds_ok = + bounds_check(blk->addy.e, blk->addy.offset + 1 + blk->len); + if (!blk->bounds_ok) return; + + edid *e = blk->addy.e; + uint8_t *ptr = DPTR(blk->addy); int i; - if (blk) { - switch(blk->header.type) { - case 0x1: /* SADS */ - for(i = 1; i <= blk->header.len; i+=3) { - struct edid_sad *sad = &e->sads[e->sad_count]; - sad->v[0] = blk->header.ptr[i]; - sad->v[1] = blk->header.ptr[i+1]; - sad->v[2] = blk->header.ptr[i+2]; - sad->format = (sad->v[0] & 0x78) >> 3; - sad->channels = 1 + sad->v[0] & 0x7; - sad->freq_bits = sad->v[1]; - if (sad->format == 1) { - sad->depth_bits = sad->v[2]; - } else if (sad->format >= 2 - && sad->format <= 8) { - sad->max_kbps = 8 * sad->v[2]; - } - e->sad_count++; + switch(blk->type) { + case 0x1: /* SADS */ + for(i = 1; i <= blk->len; i+=3) { + struct edid_sad *sad = &e->sads[e->sad_count]; + sad->v[0] = ptr[i]; + sad->v[1] = ptr[i+1]; + sad->v[2] = ptr[i+2]; + sad->format = bf_value(sad->v[0], 0x78); + sad->channels = 1 + bf_value(sad->v[0], 0x07); + sad->freq_bits = sad->v[1]; + if (sad->format == 1) { + sad->depth_bits = sad->v[2]; + } else if (sad->format >= 2 + && sad->format <= 8) { + sad->max_kbps = 8 * sad->v[2]; } - break; - case 0x4: /* Speaker allocation */ - e->speaker_alloc_bits = blk->header.ptr[1]; - break; - case 0x2: /* SVDs */ - for(i = 1; i <= blk->header.len; i++) - e->svds[e->svd_count++].v = blk->header.ptr[i]; - break; - case 0x3: /* Vendor-specific */ - // TODO: - default: - break; - } + e->sad_count++; + } + break; + case 0x4: /* Speaker allocation */ + e->speaker_alloc_bits = ptr[1]; + break; + case 0x2: /* SVDs */ + for(i = 1; i <= blk->len; i++) + e->svds[e->svd_count++].v = ptr[i]; + break; + case 0x3: /* Vendor-specific */ + // TODO: + default: + break; } } -static void did_block_decode(DisplayIDBlock *blk, edid *e) { +static void did_block_decode(DisplayIDBlock *blk) { + if (!blk) return; + + //printf("did_block_decode: %s\n", hex_bytes(DPTR(blk->addy), blk->len+3)); + + if (!blk->bounds_ok) + blk->bounds_ok = + bounds_check(blk->addy.e, blk->addy.offset + 3 + blk->len); + if (!blk->bounds_ok) return; + + edid *e = blk->addy.e; + uint32_t a = blk->addy.offset + 3; + + uint8_t *u8 = DPTR(blk->addy); int b = 3; - uint8_t *u8 = blk->ptr; + edid_ven ven = {}; edid_output out = {}; if (blk) { switch(blk->type) { + case 0: /* Product ID (1.x) */ + /* UNTESTED */ + if (rpnpcpy(&ven, e, a) ) + e->ven = ven; + e->did_strings[e->did_string_count].is_product_name = 1; + e->did_strings[e->did_string_count].len = blk->len; + e->did_strings[e->did_string_count].str = rstr_strip(e, a+12, u8[b+11]); + e->name = e->did_strings[e->did_string_count].str; + e->did_string_count++; + break; + case 0x20: /* Product ID */ + /* UNTESTED */ + if (rouicpy(&ven, e, a) ) + e->ven = ven; + e->did_strings[e->did_string_count].is_product_name = 1; + e->did_strings[e->did_string_count].len = blk->len; + e->did_strings[e->did_string_count].str = rstr_strip(e, a+12, u8[b+11]); + e->name = e->did_strings[e->did_string_count].str; + e->did_string_count++; + break; + case 0x0a: /* Serial Number (ASCII String) */ + e->did_strings[e->did_string_count].is_serial = 1; + e->did_strings[e->did_string_count].len = blk->len; + e->did_strings[e->did_string_count].str = rstr_strip(e, a, blk->len); + e->serial = e->did_strings[e->did_string_count].str; + e->did_string_count++; + break; + case 0x0b: /* General Purpose ASCII String */ + e->did_strings[e->did_string_count].len = blk->len; + e->did_strings[e->did_string_count].str = rstr(e, a, blk->len); + e->did_string_count++; + break; case 0x03: /* Type I Detailed timings */ - out.pixel_clock_khz = u8[b+2] << 16; - out.pixel_clock_khz += u8[b+1] << 8; - out.pixel_clock_khz += u8[b]; - out.pixel_clock_khz *= 10; - out.horiz_pixels = (u8[b+5] << 8) + u8[b+4]; - out.horiz_blanking = (u8[b+7] << 8) + u8[b+6]; - out.vert_lines = (u8[b+13] << 8) + u8[b+12]; - out.vert_blanking = (u8[b+15] << 8) + u8[b+14]; - out.is_interlaced = (u8[b+3] >> 4) & 0x1; - out.stereo_mode = (u8[b+3] >> 5) & 0x2; - out.is_preferred = (u8[b+3] >> 7) & 0x1; + out.pixel_clock_khz = 10 * r24le(e, a, NOMASK); + out.horiz_pixels = 1 + (u8[b+5] << 8) + u8[b+4]; + out.horiz_blanking = (u8[b+7] << 8) + u8[b+6]; + out.vert_lines = 1 + (u8[b+13] << 8) + u8[b+12]; + out.vert_blanking = (u8[b+15] << 8) + u8[b+14]; + out.is_interlaced = bf_value(u8[b+3], BFMASK(4, 0x1)); + out.stereo_mode = bf_value(u8[b+3], BFMASK(5, 0x3)); + out.is_preferred = bf_value(u8[b+3], BFMASK(7, 0x1)); out.src = OUTSRC_DID_TYPE_I; edid_output_fill(&out); e->didts[e->didt_count++] = out; @@ -193,11 +374,12 @@ static void did_block_decode(DisplayIDBlock *blk, edid *e) { case 0x81: /* CTA DisplayID, ... Embedded CEA Blocks */ while(b < blk->len) { int db_type = (u8[b] & 0xe0) >> 5; - int db_size = u8[b] & 0x1f; - e->cea_blocks[e->cea_block_count].header.ptr = &u8[b]; - e->cea_blocks[e->cea_block_count].header.type = db_type; - e->cea_blocks[e->cea_block_count].header.len = db_size; - cea_block_decode(&e->cea_blocks[e->cea_block_count], e); + int db_size = u8[b] & 0x1f; + e->cea_blocks[e->cea_block_count].addy.e = blk->addy.e; + e->cea_blocks[e->cea_block_count].addy.offset = blk->addy.offset + b; + e->cea_blocks[e->cea_block_count].type = db_type; + e->cea_blocks[e->cea_block_count].len = db_size; + cea_block_decode(&e->cea_blocks[e->cea_block_count]); e->cea_block_count++; b += db_size + 1; } @@ -240,6 +422,8 @@ edid *edid_new(const char *data, unsigned int len) { e->ver_major = e->u8[18]; e->ver_minor = e->u8[19]; + e->msg_log = g_string_new(NULL); + #define RESERVE_COUNT 300 e->dtds = malloc(sizeof(struct edid_dtd) * RESERVE_COUNT); e->cea_blocks = malloc(sizeof(struct edid_cea_block) * RESERVE_COUNT); @@ -247,24 +431,23 @@ edid *edid_new(const char *data, unsigned int len) { e->sads = malloc(sizeof(struct edid_sad) * RESERVE_COUNT); e->did_blocks = malloc(sizeof(DisplayIDBlock) * RESERVE_COUNT); e->didts = malloc(sizeof(edid_output) * RESERVE_COUNT); + e->did_strings = malloc(sizeof(edid_output) * RESERVE_COUNT); memset(e->dtds, 0, sizeof(struct edid_dtd) * RESERVE_COUNT); memset(e->cea_blocks, 0, sizeof(struct edid_cea_block) * RESERVE_COUNT); memset(e->svds, 0, sizeof(struct edid_svd) * RESERVE_COUNT); memset(e->sads, 0, sizeof(struct edid_sad) * RESERVE_COUNT); memset(e->did_blocks, 0, sizeof(DisplayIDBlock) * RESERVE_COUNT); memset(e->didts, 0, sizeof(edid_output) * RESERVE_COUNT); + memset(e->did_strings, 0, sizeof(edid_output) * RESERVE_COUNT); - uint16_t vid = be16toh(e->u16[4]); /* bytes 8-9 */ - e->ven[2] = 64 + (vid & 0x1f); - e->ven[1] = 64 + ((vid >> 5) & 0x1f); - e->ven[0] = 64 + ((vid >> 10) & 0x1f); - e->product = le16toh(e->u16[5]); /* bytes 10-11 */ - e->n_serial = le32toh(e->u32[3]);/* bytes 12-15 */ - e->week = e->u8[16]; /* byte 16 */ - e->year = e->u8[17] + 1990; /* byte 17 */ - - if (e->week >= 52) - e->week = 0; + /* base product information */ + rpnpcpy(&e->ven, e, 8); /* bytes 8-9 */ + e->product = r16le(e, 10, NOMASK); /* bytes 10-11 */ + e->n_serial = r32le(e, 12, NOMASK); /* bytes 12-15 */ + e->dom.week = e->u8[16]; /* byte 16 */ + e->dom.year = e->u8[17] + 1990; /* byte 17 */ + e->dom.is_model_year = (e->dom.week > 52); + e->dom.std = STD_EDID; e->a_or_d = (e->u8[20] & 0x80) ? 1 : 0; if (e->a_or_d == 1) { @@ -353,23 +536,25 @@ edid *edid_new(const char *data, unsigned int len) { } uint16_t dh, dl; -#define CHECK_DESCRIPTOR(index, offset) \ - if (e->u8[offset] == 0) { \ - dh = be16toh(e->u16[offset/2]); \ - dl = be16toh(e->u16[offset/2+1]); \ - e->d_type[index] = (dh << 16) + dl; \ - switch(e->d_type[index]) { \ +#define CHECK_DESCRIPTOR(INDEX, OFFSET) \ + e->d[INDEX].addy.e = e; \ + e->d[INDEX].addy.offset = OFFSET; \ + if (e->u8[OFFSET] == 0) { \ + dh = be16toh(e->u16[OFFSET/2]); \ + dl = be16toh(e->u16[OFFSET/2+1]); \ + e->d[INDEX].type = (dh << 16) + dl; \ + switch(e->d[INDEX].type) { \ case 0xfc: case 0xff: case 0xfe: \ - strncpy(e->d_text[index], (char*)e->u8+offset+5, 13); \ - } \ - } else e->dtds[e->dtd_count++].ptr = &e->u8[offset]; + strncpy(e->d[INDEX].text, (char*)e->u8+OFFSET+5, 13); \ + } \ + } else e->dtds[e->dtd_count++].addy = e->d[INDEX].addy; CHECK_DESCRIPTOR(0, 54); CHECK_DESCRIPTOR(1, 72); CHECK_DESCRIPTOR(2, 90); CHECK_DESCRIPTOR(3, 108); - e->checksum_ok = block_check(e->data); /* first 128-byte block only */ + e->checksum_ok = block_check(e, 0); /* first 128-byte block only */ if (len > 128) { /* check extension blocks */ int blocks = len / 128; @@ -377,8 +562,9 @@ edid *edid_new(const char *data, unsigned int len) { e->ext_blocks = blocks; e->ext_ok = malloc(sizeof(uint8_t) * blocks); for(; blocks; blocks--) { - uint8_t *u8 = e->u8 + (blocks * 128); - int r = block_check(u8); + uint32_t offset = blocks * 128; + uint8_t *u8 = e->u8 + offset; + int r = block_check(e, offset); e->ext_ok[blocks-1] = r; if (r) e->ext_blocks_ok++; else e->ext_blocks_fail++; @@ -391,26 +577,27 @@ edid *edid_new(const char *data, unsigned int len) { else e->std = MAX(e->std, STD_DISPLAYID); e->did.extension_length = u8[2]; e->did.primary_use_case = u8[3]; - e->did.extension_count = u8[4]; + e->did.extension_count = u8[4]; + e->did.checksum_ok = block_check_n(e, offset, e->did.extension_length + 5); int db_end = u8[2] + 5; int b = 5; while(b < db_end) { + if (r24le(e, offset + b, NOMASK) == 0) break; int db_type = u8[b]; int db_revision = u8[b+1] & 0x7; int db_size = u8[b+2]; - e->did_blocks[e->did_block_count].ptr = &u8[b]; + e->did_blocks[e->did_block_count].addy.e = e; + e->did_blocks[e->did_block_count].addy.offset = offset + b; e->did_blocks[e->did_block_count].type = db_type; e->did_blocks[e->did_block_count].revision = db_revision; e->did_blocks[e->did_block_count].len = db_size; - did_block_decode(&e->did_blocks[e->did_block_count], e); + did_block_decode(&e->did_blocks[e->did_block_count]); e->did_block_count++; e->did.blocks++; b += db_size + 3; } - if (b != db_end) { - // over or under-run ... - } - e->did.checksum_ok = block_check_n(u8, u8[2] + 5); + if (b > db_end) + edid_msg(e, "DID block overrun [in ext " OFMT "], expect to end at +%d, but last ends at +%d" , offset, db_end-1, b-1); //printf("DID: v:%02x el:%d uc:%d ec:%d, blocks:%d ok:%d\n", // e->did.version, e->did.extension_length, // e->did.primary_use_case, e->did.extension_count, @@ -425,19 +612,24 @@ edid *edid_new(const char *data, unsigned int len) { int b = 4; while(b < db_end) { int db_type = (u8[b] & 0xe0) >> 5; - int db_size = u8[b] & 0x1f; - e->cea_blocks[e->cea_block_count].header.ptr = &u8[b]; - e->cea_blocks[e->cea_block_count].header.type = db_type; - e->cea_blocks[e->cea_block_count].header.len = db_size; - cea_block_decode(&e->cea_blocks[e->cea_block_count], e); + int db_size = u8[b] & 0x1f; + e->cea_blocks[e->cea_block_count].addy.e = e; + e->cea_blocks[e->cea_block_count].addy.offset = offset + b; + e->cea_blocks[e->cea_block_count].type = db_type; + e->cea_blocks[e->cea_block_count].len = db_size; + cea_block_decode(&e->cea_blocks[e->cea_block_count]); e->cea_block_count++; b += db_size + 1; } - if (b > db_end) b = db_end; + if (b > db_end) { + b = db_end; + edid_msg(e, "CEA block overrun [in ext " OFMT "], expect to end at +%d, but last ends at +%d" , offset, db_end-1, b-1); + } /* DTDs */ while(b < 127) { if (u8[b]) { - e->dtds[e->dtd_count].ptr = &u8[b]; + e->dtds[e->dtd_count].addy.e = e; + e->dtds[e->dtd_count].addy.offset = offset + b; e->dtds[e->dtd_count].cea_ext = 1; e->dtd_count++; } @@ -453,19 +645,22 @@ edid *edid_new(const char *data, unsigned int len) { /* strings */ for(i = 0; i < 4; i++) { - g_strstrip(e->d_text[i]); - switch(e->d_type[i]) { + if (!g_utf8_validate(e->d[i].text, -1, NULL)) { + strcpy(e->d[i].text, "(INVALID)"); + } + g_strstrip(e->d[i].text); + switch(e->d[i].type) { case 0xfc: - e->name = e->d_text[i]; + e->name = e->d[i].text; break; case 0xff: - e->serial = e->d_text[i]; + e->serial = e->d[i].text; break; case 0xfe: if (e->ut1) - e->ut2 = e->d_text[i]; + e->ut2 = e->d[i].text; else - e->ut1 = e->d_text[i]; + e->ut1 = e->d[i].text; break; } } @@ -502,12 +697,18 @@ edid *edid_new(const char *data, unsigned int len) { /* dtds */ for(i = 0; i < e->dtd_count; i++) { - uint8_t *u8 = e->dtds[i].ptr; - uint16_t *u16 = (uint16_t*)e->dtds[i].ptr; + edid_addy a = e->dtds[i].addy; + if (!e->dtds[i].bounds_ok) + e->dtds[i].bounds_ok = bounds_check(a.e, a.offset + 18); + if (!e->dtds[i].bounds_ok) { + printf("bounds fail\n"); + exit(0); + } + uint8_t *u8 = DPTR(a); edid_output *out = &e->dtds[i].out; if (e->dtds[i].cea_ext) out->src = OUTSRC_CEA_DTD; else out->src = OUTSRC_DTD; - out->pixel_clock_khz = le16toh(u16[0]) * 10; + out->pixel_clock_khz = 10 * r16le(a.e, a.offset, NOMASK); out->horiz_pixels = ((u8[4] & 0xf0) << 4) + u8[2]; out->vert_lines = @@ -548,6 +749,13 @@ edid *edid_new(const char *data, unsigned int len) { e->img_max.is_preferred = 1; OUTPUT_CPY_SIZE(e->img_max, tmp); } + if (e->svds[i].out.pixels > e->img_svd.pixels + || e->svds[i].is_native) { + e->img_svd = e->svds[i].out; + if (e->svds[i].is_native) + e->img_svd.is_preferred = 1; + OUTPUT_CPY_SIZE(e->img_svd, e->img_max); + } } /* didts */ @@ -556,9 +764,9 @@ edid *edid_new(const char *data, unsigned int len) { int max_pref = e->img_max.is_preferred; int bigger = (e->didts[i].pixels > e->img_max.pixels); int better = (e->didts[i].src > e->img_max.src); - if (bigger && !max_pref - || pref && !max_pref - || pref && better) { + if ((bigger && !max_pref) + || (pref && !max_pref) + || (better)) { edid_output tmp = e->img_max; e->img_max = e->didts[i]; OUTPUT_CPY_SIZE(e->img_max, tmp); @@ -582,15 +790,25 @@ edid *edid_new(const char *data, unsigned int len) { SQUEEZE(sad_count, sads); SQUEEZE(did_block_count, did_blocks); SQUEEZE(didt_count, didts); + SQUEEZE(did_string_count, did_strings); return e; } void edid_free(edid *e) { + int i; if (e) { g_free(e->ext_ok); g_free(e->cea_blocks); g_free(e->dtds); + g_free(e->svds); + g_free(e->sads); + g_free(e->did_blocks); + g_free(e->didts); + for(i = 0; i < e->did_string_count; i++) + g_free(e->did_strings[i].str); + g_free(e->did_strings); g_free(e->data); + g_string_free(e->msg_log, TRUE); g_free(e); } } @@ -621,6 +839,17 @@ edid *edid_new_from_hex(const char *hex_string) { return e; } +edid *edid_new_from_file(const char *path) { + char *bin = NULL; + gsize len = 0; + if (g_file_get_contents(path, &bin, &len, NULL) ) { + edid *ret = edid_new(bin, len); + g_free(bin); + return ret; + } + return NULL; +} + char *edid_dump_hex(edid *e, int tabs, int breaks) { if (!e) return NULL; int lines = 1 + (e->len / 16); @@ -744,6 +973,7 @@ const char *edid_did_block_type(int type) { case 0x0F: return N_("Display Interface Data (1.x)"); case 0x10: return N_("Stereo Display Interface (1.x)"); case 0x11: return N_("Type V Timing - Short (1.x)"); + case 0x12: return N_("Tiled Display Topology (1.x)"); case 0x13: return N_("Type VI Timing - Detailed (1.x)"); case 0x7F: return N_("Vendor specific (1.x)"); /* 2.x */ @@ -880,24 +1110,28 @@ char *edid_cea_speaker_allocation_describe(int bitfield, int short_version) { char *edid_cea_block_describe(struct edid_cea_block *blk) { gchar *ret = NULL; if (blk) { - char *hb = hex_bytes(blk->header.ptr, blk->header.len+1); - switch(blk->header.type) { + char *hb = hex_bytes(DPTR(blk->addy), blk->len+1); + switch(blk->type) { case 0x1: /* SAD list */ ret = g_strdup_printf("([%x] %s) sads:%d", - blk->header.type, _(edid_cea_block_type(blk->header.type)), - blk->header.len/3); + blk->type, _(edid_cea_block_type(blk->type)), + blk->len/3); break; case 0x2: /* SVD list */ ret = g_strdup_printf("([%x] %s) svds:%d", - blk->header.type, _(edid_cea_block_type(blk->header.type)), - blk->header.len); + blk->type, _(edid_cea_block_type(blk->type)), + blk->len); + break; + case 0x4: /* speaker allocation */ + ret = g_strdup_printf("([%x] %s) len:%d", + blk->type, _(edid_cea_block_type(blk->type)), + blk->len); break; case 0x3: //TODO - case 0x4: default: ret = g_strdup_printf("([%x] %s) len:%d -- %s", - blk->header.type, _(edid_cea_block_type(blk->header.type)), - blk->header.len, + blk->type, _(edid_cea_block_type(blk->type)), + blk->len, hb); break; } @@ -906,23 +1140,67 @@ char *edid_cea_block_describe(struct edid_cea_block *blk) { return ret; } -char *edid_did_block_describe(DisplayIDBlock *blk) { +char *edid_base_descriptor_describe(struct edid_descriptor *d) { gchar *ret = NULL; - if (blk) { - char *hb = hex_bytes(blk->ptr, blk->len+3); - switch(blk->type) { + if (d) { + char *hb = hex_bytes(DPTR(d->addy), 18); + char *txt = NULL; + switch(d->type) { + case 0: /* DTD */ + txt = "{...}"; /* displayed elsewhere */ + break; + case 0x10: /* dummy */ + txt = ""; + break; default: - ret = g_strdup_printf("([%02x:r%02x] %s) len:%d -- %s", - blk->type, blk->revision, _(edid_did_block_type(blk->type)), - blk->len, - hb); + txt = (*d->text) ? d->text : hb; break; - } + }; + ret = g_strdup_printf("([%02x] %s) %s", + d->type, _(edid_descriptor_type(d->type)), + txt); free(hb); } return ret; } +char *edid_did_block_describe(DisplayIDBlock *blk) { + if (!blk) return NULL; + + gchar *ret = NULL; + edid *e = blk->addy.e; + uint32_t a = blk->addy.offset + 3; + char *str = NULL; + + //printf("edid_did_block_describe: ((%d)) t:%02x a{%p, %d}...\n", blk->addy.e->did.extension_count, blk->type, blk->addy.e, blk->addy.offset); + char *hb = hex_bytes(DPTR(blk->addy), blk->len+3); + switch(blk->type) { + case 0x0a: /* Product Serial ASCII string */ + str = rstr_strip(e, a, blk->len); + ret = g_strdup_printf("([%02x:r%02x] %s) len:%d \"%s\"", + blk->type, blk->revision, _(edid_did_block_type(blk->type)), + blk->len, + str); + break; + case 0x0b: /* ASCII string */ + str = rstr(e, a, blk->len); + ret = g_strdup_printf("([%02x:r%02x] %s) len:%d \"%s\"", + blk->type, blk->revision, _(edid_did_block_type(blk->type)), + blk->len, + str); + break; + default: + ret = g_strdup_printf("([%02x:r%02x] %s) len:%d -- %s", + blk->type, blk->revision, _(edid_did_block_type(blk->type)), + blk->len, + hb); + break; + } + free(hb); + + return ret; +} + char *edid_output_describe(edid_output *out) { gchar *ret = NULL; if (out) { @@ -941,7 +1219,7 @@ char *edid_dtd_describe(struct edid_dtd *dtd, int dump_bytes) { gchar *ret = NULL; if (dtd) { edid_output *out = &dtd->out; - char *hb = hex_bytes(dtd->ptr, 18); + char *hb = hex_bytes(DPTR(dtd->addy), 18); ret = g_strdup_printf("%dx%d@%.0f%s, %0.1fx%0.1f%s (%0.1f\") %s %s (%s)%s%s", out->horiz_pixels, out->vert_lines, out->vert_freq_hz, _("Hz"), out->horiz_cm, out->vert_cm, _("cm"), out->diag_in, @@ -955,19 +1233,33 @@ char *edid_dtd_describe(struct edid_dtd *dtd, int dump_bytes) { return ret; } +char *edid_manf_date_describe(struct edid_manf_date dom) { + if (!dom.year) return g_strdup("unspecified"); + if (dom.is_model_year) + return g_strdup_printf(_("model year %d"), dom.year); + if (dom.week <= 52) + return g_strdup_printf(_("week %d of %d"), dom.week, dom.year); + return g_strdup_printf("%d", dom.year); +} + char *edid_dump2(edid *e) { char *ret = NULL; int i; if (!e) return NULL; ret = appfnl(ret, "edid version: %d.%d (%d bytes)", e->ver_major, e->ver_minor, e->len); - ret = appfnl(ret, "extended to: %s", _(edid_standard(e->std)) ); + if (e->std) + ret = appfnl(ret, "extended to: %s", _(edid_standard(e->std)) ); + + ret = appfnl(ret, "mfg: %s, model: [%04x-%08x] %u-%u", e->ven.pnp, e->product, e->n_serial, e->product, e->n_serial); + char *dom_desc = edid_manf_date_describe(e->dom); + ret = appfnl(ret, "date: %s", dom_desc); + g_free(dom_desc); - ret = appfnl(ret, "mfg: %s, model: [%04x-%08x] %u-%u", e->ven, e->product, e->n_serial, e->product, e->n_serial); - if (e->week && e->year) - ret = appf(ret, "", ", dom: week %d of %d", e->week, e->year); - else if (e->year) - ret = appf(ret, "", ", dom: %d", e->year); + if (e->name) + ret = appfnl(ret, "product: %s", e->name); + if (e->serial) + ret = appfnl(ret, "serial: %s", e->serial); ret = appfnl(ret, "type: %s", e->a_or_d ? "digital" : "analog"); if (e->bpc) @@ -976,10 +1268,14 @@ char *edid_dump2(edid *e) { ret = appfnl(ret, "interface: %s", _(edid_interface(e->interface))); char *desc = edid_output_describe(&e->img); + char *desc_svd = edid_output_describe(&e->img_svd); char *desc_max = edid_output_describe(&e->img_max); ret = appfnl(ret, "base(%s): %s", _(edid_output_src(e->img.src)), desc); - ret = appfnl(ret, "ext(%s): %s", _(edid_output_src(e->img_max.src)), desc_max); + if (e->svd_count) + ret = appfnl(ret, "svd(%s): %s", _(edid_output_src(e->img_svd.src)), desc_svd); + ret = appfnl(ret, "max(%s): %s", _(edid_output_src(e->img_max.src)), desc_max); g_free(desc); + g_free(desc_svd); g_free(desc_max); if (e->speaker_alloc_bits) { @@ -1004,8 +1300,11 @@ char *edid_dump2(edid *e) { if (e->ext_blocks_ok || e->ext_blocks_fail) ret = appf(ret, "", ", extension blocks: %d of %d ok", e->ext_blocks_ok, e->ext_blocks_ok + e->ext_blocks_fail); - for(i = 0; i < 4; i++) - ret = appfnl(ret, "descriptor[%d] ([%02x] %s): %s", i, e->d_type[i], _(edid_descriptor_type(e->d_type[i])), *e->d_text[i] ? e->d_text[i] : "{...}"); + for(i = 0; i < 4; i++) { + char *desc = edid_base_descriptor_describe(&e->d[i]); + ret = appfnl(ret, "descriptor[%d] %s", i, desc); + g_free(desc); + } for(i = 0; i < e->ext_blocks; i++) { int type = e->u8[(i+1)*128]; @@ -1054,6 +1353,12 @@ char *edid_dump2(edid *e) { g_free(desc); } + for(i = 0; i < e->did_string_count; i++) { + ret = appfnl(ret, "did_string[%d]: %s", i, e->did_strings[i].str); + } + + ret = appfnl(ret, "parse messages:\n%s---", e->msg_log->str); + return ret; } |