diff options
author | Burt P <pburt0@gmail.com> | 2019-08-18 02:32:58 -0500 |
---|---|---|
committer | Leandro A. F. Pereira <leandro@hardinfo.org> | 2019-08-18 23:49:29 +0200 |
commit | 0e0797a9257c8ee039746da9903138c19615e7a0 (patch) | |
tree | 9844f60b23370c1bf42692ce086a4ba7c935acff | |
parent | e49e438270cdffc4a80a2676c73ad95bedce33d4 (diff) |
monitors: updated util_edid
Signed-off-by: Burt P <pburt0@gmail.com>
-rw-r--r-- | deps/sysobj_early/include/util_edid.h | 93 | ||||
-rw-r--r-- | deps/sysobj_early/src/appf.c | 2 | ||||
-rw-r--r-- | deps/sysobj_early/src/util_edid.c | 624 | ||||
-rw-r--r-- | modules/devices/monitors.c | 69 |
4 files changed, 547 insertions, 241 deletions
diff --git a/deps/sysobj_early/include/util_edid.h b/deps/sysobj_early/include/util_edid.h index 3d54fa0d..af9364e0 100644 --- a/deps/sysobj_early/include/util_edid.h +++ b/deps/sysobj_early/include/util_edid.h @@ -23,16 +23,72 @@ #include <stdint.h> /* for *int*_t types */ -#define EDID_MAX_EXT_BLOCKS 254 +struct edid_dtd { + uint8_t *ptr; + int pixel_clock_khz; + int horiz_pixels; + int vert_lines; +}; + +struct edid_cea_header { + uint8_t *ptr; + int type, len; +}; + +struct edid_cea_block { + struct edid_cea_header header; + int reserved[8]; +}; + +struct edid_cea_audio { + struct edid_cea_header header; + int format, channels, freq_bits; + int depth_bits; /* format 1 */ + int max_kbps; /* formats 2-8 */ +}; + +struct edid_cea_video { + struct edid_cea_header header; +}; + +struct edid_cea_vendor_spec { + struct edid_cea_header header; +}; + +struct edid_cea_speaker { + struct edid_cea_header header; + int alloc_bits; +}; + +typedef struct { + union { + void* data; + uint8_t* u8; + uint16_t* u16; + uint32_t* u32; + }; + unsigned int len; + + /* 0 - EDID + * 1 - EIA/CEA-861 + * 2 - DisplayID + */ + int std; -/* just enough edid decoding */ -struct edid { int ver_major, ver_minor; - char ven[4]; + int checksum_ok; /* first 128-byte block only */ + int ext_blocks, ext_blocks_ok, ext_blocks_fail; + uint8_t *ext_ok; + + int dtd_count; + struct edid_dtd *dtds; + int cea_block_count; + struct edid_cea_block *cea_blocks; + + char ven[4]; int d_type[4]; char d_text[4][14]; - /* point into d_text */ char *name; char *serial; @@ -41,28 +97,27 @@ struct edid { int a_or_d; /* 0 = analog, 1 = digital */ int bpc; - uint16_t product; uint32_t n_serial; int week, year; - int horiz_cm, vert_cm; float diag_cm, diag_in; - int size; /* bytes */ - int checksum_ok; /* block 0 */ +} edid; +edid *edid_new(const char *data, unsigned int len); +edid *edid_new_from_hex(const char *hex_string); +void edid_free(edid *e); +char *edid_dump_hex(edid *e, int tabs, int breaks); - int ext_blocks, ext_blocks_ok, ext_blocks_fail; - uint8_t ext[EDID_MAX_EXT_BLOCKS][2]; /* block type, checksum_ok */ -}; - -int edid_fill(struct edid *id_out, const void *edid_bytes, int edid_len); -int edid_fill_xrandr(struct edid *id_out, const char *xrandr_edid_dump); +const char *edid_standard(int type); +const char *edid_descriptor_type(int type); +const char *edid_ext_block_type(int type); +const char *edid_cea_block_type(int type); +const char *edid_cea_audio_type(int type); -int edid_hex_to_bin(void **edid_bytes, int *edid_len, const char *hex_string); -char *edid_bin_to_hex(const void *edid_bytes, int edid_len); +char *edid_dtd_describe(struct edid_dtd *dtd); +char *edid_cea_block_describe(struct edid_cea_block *blk); -const char *edid_descriptor_type(int type); -char *edid_dump(struct edid *id); +char *edid_dump2(edid *e); #endif diff --git a/deps/sysobj_early/src/appf.c b/deps/sysobj_early/src/appf.c index 5969d945..e63e77d0 100644 --- a/deps/sysobj_early/src/appf.c +++ b/deps/sysobj_early/src/appf.c @@ -28,7 +28,7 @@ /* FIXME: if this isn't here, hardinfo will crash, * I don't have the slightest idea why */ void wtf() { - edid_fill(NULL, NULL, 0); + void *e = edid_new(NULL, 0); } char *appf(char *str, const char *sep, const char *fmt, ...) { diff --git a/deps/sysobj_early/src/util_edid.c b/deps/sysobj_early/src/util_edid.c index 3f953191..80a71002 100644 --- a/deps/sysobj_early/src/util_edid.c +++ b/deps/sysobj_early/src/util_edid.c @@ -25,12 +25,12 @@ #include <math.h> #include <endian.h> #include <stdio.h> +#include "gettext.h" #include "util_edid.h" #include "util_sysobj.h" -#include "gettext.h" /* block must be 128 bytes */ -int block_check(const void *edid_block_bytes) { +static int block_check(const void *edid_block_bytes) { if (!edid_block_bytes) return 0; uint8_t sum = 0; uint8_t *data = (uint8_t*)edid_block_bytes; @@ -40,148 +40,238 @@ int block_check(const void *edid_block_bytes) { return sum == 0 ? 1 : 0; } -int edid_fill(struct edid *id_out, const void *edid_bytes, int edid_len) { +char *hex_bytes(uint8_t *bytes, int count) { + char *buffer = malloc(count*3+1), *p = buffer; + memset(buffer, 0, count*3+1); int i; + for(i = 0; i < count; i++) { + sprintf(p, "%02x ", (unsigned int)bytes[i]); + p += 3; + } + return buffer; +} - if (!id_out) return 0; - - memset(id_out, 0, sizeof(struct edid)); - id_out->size = edid_len; - - if (edid_len >= 128) { - uint8_t *u8 = (uint8_t*)edid_bytes; - uint16_t *u16 = (uint16_t*)edid_bytes; - uint32_t *u32 = (uint32_t*)edid_bytes; - - id_out->ver_major = u8[18]; /* byte 18 */ - id_out->ver_minor = u8[19]; /* byte 19 */ - - /* checksum first block */ - id_out->checksum_ok = block_check(edid_bytes); - if (edid_len > 128) { - int blocks = edid_len / 128; - if (blocks > EDID_MAX_EXT_BLOCKS) blocks = EDID_MAX_EXT_BLOCKS; - blocks--; - id_out->ext_blocks = blocks; - for(; blocks; blocks--) { - id_out->ext[blocks-1][0] = *(u8 + (blocks * 128)); - int r = block_check(u8 + (blocks * 128)); - id_out->ext[blocks-1][1] = (uint8_t)r; - if (r) id_out->ext_blocks_ok++; - else id_out->ext_blocks_fail++; - } +static void cea_block_decode(struct edid_cea_block *blk) { + struct edid_cea_audio *blk_audio = (void*)blk; + struct edid_cea_video *blk_video = (void*)blk; + struct edid_cea_speaker *blk_speaker = (void*)blk; + struct edid_cea_vendor_spec *blk_vendor_spec = (void*)blk; + if (blk) { + switch(blk->header.type) { + case 0x1: + blk_audio->format = (blk->header.ptr[1] & 0x78) >> 3; + blk_audio->channels = 1 + blk->header.ptr[1] & 0x7; + blk_audio->freq_bits = blk->header.ptr[2]; + if (blk_audio->format == 1) { + blk_audio->depth_bits = blk->header.ptr[3]; + } else if (blk_audio->format >= 2 + && blk_audio->format <= 8) { + blk_audio->max_kbps = blk->header.ptr[3] * 8; + } + break; + case 0x4: + blk_speaker->alloc_bits = blk->header.ptr[1]; + break; + case 0x3: + case 0x2: + default: + break; } + } +} - /* expect 1.3 */ - - uint16_t vid = be16toh(u16[4]); /* bytes 8-9 */ - id_out->ven[2] = 64 + (vid & 0x1f); - id_out->ven[1] = 64 + ((vid >> 5) & 0x1f); - id_out->ven[0] = 64 + ((vid >> 10) & 0x1f); - - id_out->product = le16toh(u16[5]); /* bytes 10-11 */ - id_out->n_serial = le32toh(u32[3]);/* bytes 12-15 */ - id_out->week = u8[16]; /* byte 16 */ - id_out->year = u8[17] + 1990; /* byte 17 */ - - if (id_out->week >= 52) - id_out->week = 0; - - id_out->a_or_d = (u8[20] & 0x80) ? 1 : 0; - if (id_out->a_or_d == 1) { - /* digital */ - switch((u8[20] >> 4) & 0x7) { - case 0x1: id_out->bpc = 6; break; - case 0x2: id_out->bpc = 8; break; - case 0x3: id_out->bpc = 10; break; - case 0x4: id_out->bpc = 12; break; - case 0x5: id_out->bpc = 14; break; - case 0x6: id_out->bpc = 16; break; - } - } +edid *edid_new(const char *data, unsigned int len) { + if (len < 128) return NULL; - if (u8[21] && u8[22]) { - id_out->horiz_cm = u8[21]; - id_out->vert_cm = u8[22]; - id_out->diag_cm = - sqrt( (id_out->horiz_cm * id_out->horiz_cm) - + (id_out->vert_cm * id_out->vert_cm) ); - id_out->diag_in = id_out->diag_cm / 2.54; + int i; + edid *e = malloc(sizeof(edid)); + memset(e, 0, sizeof(edid)); + e->data = malloc(len); + memcpy(e->data, data, len); + e->len = len; + e->ver_major = e->u8[18]; + e->ver_minor = e->u8[19]; + + e->dtds = malloc(sizeof(struct edid_dtd) * 1000); + e->cea_blocks = malloc(sizeof(struct edid_cea_block) * 1000); + + 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; + + e->a_or_d = (e->u8[20] & 0x80) ? 1 : 0; + if (e->a_or_d == 1) { + /* digital */ + switch((e->u8[20] >> 4) & 0x7) { + case 0x1: e->bpc = 6; break; + case 0x2: e->bpc = 8; break; + case 0x3: e->bpc = 10; break; + case 0x4: e->bpc = 12; break; + case 0x5: e->bpc = 14; break; + case 0x6: e->bpc = 16; break; } + } - uint16_t dh, dl; + if (e->u8[21] && e->u8[22]) { + e->horiz_cm = e->u8[21]; + e->vert_cm = e->u8[22]; + e->diag_cm = + sqrt( (e->horiz_cm * e->horiz_cm) + + (e->vert_cm * e->vert_cm) ); + e->diag_in = e->diag_cm / 2.54; + } - /* descriptor at byte 54 */ - dh = be16toh(u16[54/2]); - dl = be16toh(u16[54/2+1]); - id_out->d_type[0] = (dh << 16) + dl; - switch(id_out->d_type[0]) { - case 0xfc: case 0xff: case 0xfe: - strncpy(id_out->d_text[0], (char*)u8+54+5, 13); - } + 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]) { \ + 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]; + + 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 */ + if (len > 128) { + /* check extension blocks */ + int blocks = len / 128; + blocks--; + 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); + e->ext_ok[blocks-1] = r; + if (r) e->ext_blocks_ok++; + else e->ext_blocks_fail++; + + if (u8[0] == 0x70) { + e->std = MAX(e->std, 2); + /* DisplayID */ + // TODO: ... + } - /* descriptor at byte 72 */ - dh = be16toh(u16[72/2]); - dl = be16toh(u16[72/2+1]); - id_out->d_type[1] = (dh << 16) + dl; - switch(id_out->d_type[1]) { - case 0xfc: case 0xff: case 0xfe: - strncpy(id_out->d_text[1], (char*)u8+72+5, 13); + if (u8[0] == 0x02) { + e->std = MAX(e->std, 1); + /* CEA extension */ + int db_end = u8[2]; + //printf("db_end: %d\n", db_end); + if (db_end) { + int b = 4; + while(b < db_end) { + int db_type = (u8[b] & 0xe0) >> 5; + int db_size = u8[b] & 0x1f; + //printf("CEA BLK: %s\n", hex_bytes(&u8[b], db_size+1)); + 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->cea_block_count++; + b += db_size + 1; + } + if (b > db_end) b = db_end; + //printf("dtd start: %d\n", b); + /* DTDs */ + while(b < 127) { + if (u8[b]) { + //printf("DTD: %s\n", hex_bytes(&u8[b], 18)); + e->dtds[e->dtd_count].ptr = &u8[b]; + e->dtd_count++; + } + b += 18; + } + } + } } + } - /* descriptor at byte 90 */ - dh = be16toh(u16[90/2]); - dl = be16toh(u16[90/2+1]); - id_out->d_type[2] = (dh << 16) + dl; - switch(id_out->d_type[2]) { - case 0xfc: case 0xff: case 0xfe: - strncpy(id_out->d_text[2], (char*)u8+90+5, 13); + /* strings */ + for(i = 0; i < 4; i++) { + g_strstrip(e->d_text[i]); + switch(e->d_type[i]) { + case 0xfc: + e->name = e->d_text[i]; + break; + case 0xff: + e->serial = e->d_text[i]; + break; + case 0xfe: + if (e->ut1) + e->ut2 = e->d_text[i]; + else + e->ut1 = e->d_text[i]; + break; } + } - /* descriptor at byte 108 */ - dh = be16toh(u16[108/2]); - dl = be16toh(u16[108/2+1]); - id_out->d_type[3] = (dh << 16) + dl; - switch(id_out->d_type[3]) { - case 0xfc: case 0xff: case 0xfe: - strncpy(id_out->d_text[3], (char*)u8+108+5, 13); + /* quirks */ + if (!e->name) { + if (SEQ(e->ut1, "LG Display") && e->ut2) + /* LG may use "uspecified text" for name and model */ + e->name = e->ut2; + else if (SEQ(e->ut1, "AUO") && e->ut2) + /* Same with AUO */ + e->name = e->ut2; + else { + if (e->ut1) e->name = e->ut1; + if (e->ut2 && !e->serial) e->serial = e->ut2; } + } - for(i = 0; i < 4; i++) { - g_strstrip(id_out->d_text[i]); - switch(id_out->d_type[i]) { - case 0xfc: - id_out->name = id_out->d_text[i]; - break; - case 0xff: - id_out->serial = id_out->d_text[i]; - break; - case 0xfe: - if (id_out->ut1) - id_out->ut2 = id_out->d_text[i]; - else - id_out->ut1 = id_out->d_text[i]; - break; - } - } + /* dtds */ + for(i = 0; i < e->dtd_count; i++) { + uint8_t *u8 = e->dtds[i].ptr; + e->dtds[i].pixel_clock_khz = u8[0] * 10; + e->dtds[i].horiz_pixels = + ((u8[4] & 0xf0) << 4) + u8[2]; + e->dtds[i].vert_lines = + ((u8[7] & 0xf0) << 4) + u8[5]; + } - if (!id_out->name) { - if (SEQ(id_out->ut1, "LG Display") && id_out->ut2) - /* LG may use "uspecified text" for name and model */ - id_out->name = id_out->ut2; - else if (SEQ(id_out->ut1, "AUO") && id_out->ut2) - /* Same with AUO */ - id_out->name = id_out->ut2; - else { - if (id_out->ut1) id_out->name = id_out->ut1; - if (id_out->ut2 && !id_out->serial) id_out->serial = id_out->ut2; - } - } + /* squeeze lists */ + if (!e->dtd_count) { + free(e->dtds); + e->dtds = NULL; + } else { + e->dtds = realloc(e->dtds, sizeof(struct edid_dtd) * e->dtd_count); + } + if (!e->cea_block_count) { + if (e->cea_blocks) + free(e->cea_blocks); + e->cea_blocks = NULL; + } else { + e->cea_blocks = realloc(e->cea_blocks, sizeof(struct edid_cea_block) * e->cea_block_count); + } + + return e; +} + +void edid_free(edid *e) { + if (e) { + g_free(e->ext_ok); + g_free(e->cea_blocks); + g_free(e->dtds); + g_free(e->data); + g_free(e); } - return 1; } -int edid_hex_to_bin(void **edid_bytes, int *edid_len, const char *hex_string) { +edid *edid_new_from_hex(const char *hex_string) { int blen = strlen(hex_string) / 2; uint8_t *buffer = malloc(blen), *n = buffer; memset(buffer, 0, blen); @@ -202,24 +292,110 @@ int edid_hex_to_bin(void **edid_bytes, int *edid_len, const char *hex_string) { p++; } - *edid_bytes = (void *)buffer; - *edid_len = len; - return 1; + edid *e = edid_new((char*)buffer, len); + free(buffer); + return e; } -int edid_fill_xrandr(struct edid *id_out, const char *xrandr_edid_dump) { - void *buffer = NULL; - int len = 0; - edid_hex_to_bin(&buffer, &len, xrandr_edid_dump); - return edid_fill(id_out, buffer, len); +char *edid_dump_hex(edid *e, int tabs, int breaks) { + if (!e) return NULL; + int lines = e->len / 16; + int blen = lines * 35 + 1; + unsigned int pc = 0; + char *ret = malloc(blen); + memset(ret, 0, blen); + uint8_t *u8 = e->u8; + char *p = ret; + for(; lines; lines--) { + int i; + for(i = 0; i < tabs; i++) + sprintf(p++, "\t"); + for(i = 0; i < 16; i++) { + sprintf(p, "%02x", (unsigned int)*u8); + p+=2; + u8++; + pc++; + if (pc == e->len) { + if (breaks) sprintf(p++, "\n"); + goto edid_dump_hex_done; + } + } + if (breaks) sprintf(p++, "\n"); + } +edid_dump_hex_done: + return ret; +} + +const char *edid_standard(int std) { + switch(std) { + case 0: return N_("VESA EDID"); + case 1: return N_("EIA/CEA-861"); + case 2: return N_("VESA DisplayID"); + }; + return N_("unknown"); +} + +const char *edid_cea_audio_type(int type) { + switch(type) { + case 0: case 15: return N_("reserved"); + case 1: return N_("LPCM"); + case 2: return N_("AC-3"); + case 3: return N_("MPEG1 (layers 1 and 2)"); + case 4: return N_("MPEG1 layer 3"); + case 5: return N_("MPEG2"); + case 6: return N_("AAC"); + case 7: return N_("DTS"); + case 8: return N_("ATRAC"); + case 9: return N_("DSD"); + case 10: return N_("DD+"); + case 11: return N_("DTS-HD"); + case 12: return N_("MLP/Dolby TrueHD"); + case 13: return N_("DST Audio"); + case 14: return N_("WMA Pro"); + } + return N_("unknown type"); +} + +const char *edid_cea_block_type(int type) { + switch(type) { + case 0x01: + return N_("audio"); + case 0x02: + return N_("video"); + case 0x03: + return N_("vendor specific"); + case 0x04: + return N_("speaker allocation"); + } + return N_("unknown type"); } const char *edid_ext_block_type(int type) { switch(type) { - case 0xf0: - return N_("extension block map"); + case 0x00: + return N_("timing extension"); case 0x02: return N_("EIA/CEA-861 extension block"); + case 0x10: + return N_("video timing block"); + case 0x20: + return N_("EDID 2.0 extension"); + case 0x40: + return N_("DVI feature data/display information"); + case 0x50: + return N_("localized strings"); + case 0x60: + return N_("microdisplay interface"); + case 0x70: + return N_("DisplayID"); + case 0xa7: + case 0xaf: + case 0xbf: + return N_("display transfer characteristics data block"); + case 0xf0: + return N_("extension block map"); + case 0xff: + return N_("manufacturer-defined extension/display device data block"); } return N_("unknown block type"); } @@ -247,73 +423,147 @@ const char *edid_descriptor_type(int type) { case 0x10: return N_("dummy"); } - if (type < 0x0f) return N_("manufacturer reserved descriptor"); + if (type && type < 0x0f) + return N_("manufacturer reserved descriptor"); return N_("detailed timing descriptor"); } -char *edid_dump(struct edid *id) { +char *edid_cea_block_describe(struct edid_cea_block *blk) { + gchar *ret = NULL; + gchar *tmp[3] = {}; + struct edid_cea_audio *blk_audio = (void*)blk; + struct edid_cea_video *blk_video = (void*)blk; + struct edid_cea_speaker *blk_speaker = (void*)blk; + struct edid_cea_vendor_spec *blk_vendor_spec = (void*)blk; + + if (blk) { + char *hb = hex_bytes(blk->header.ptr, blk->header.len+1); + switch(blk->header.type) { + case 0x1: + +#define appfreq(b, f) if (blk_audio->freq_bits & (1 << b)) tmp[0] = appf(tmp[0], ", ", "%d", f); +#define appdepth(b, d) if (blk_audio->depth_bits & (1 << b)) tmp[1] = appf(tmp[1], ", ", "%d%s", d, _("-bit")); + appfreq(0, 32); + appfreq(1, 44); + appfreq(2, 48); + appfreq(3, 88); + appfreq(4, 96); + appfreq(5, 176); + appfreq(6, 192); + + if (blk_audio->format == 1) { + appdepth(0, 16); + appdepth(1, 20); + appdepth(2, 24); + tmp[2] = g_strdup_printf("depths: %s", tmp[1]); + } else if (blk_audio->format >= 2 + && blk_audio->format <= 8 ) { + tmp[2] = g_strdup_printf("max_bitrate: %d %s", blk_audio->max_kbps, _("kbps")); + } else + tmp[2] = g_strdup(""); + + ret = g_strdup_printf("([%x] %s) len:%d format:([%x] %s) channels:%d rates:%s %s %s -- %s", + blk->header.type, _(edid_cea_block_type(blk->header.type)), + blk->header.len, + blk_audio->format, _(edid_cea_audio_type(blk_audio->format)), + blk_audio->channels, tmp[0], _("kHz"), + tmp[2], + hb); + g_free(tmp[0]); + g_free(tmp[1]); + g_free(tmp[2]); + break; + case 0x4: +#define appspk(b, s) if (blk_speaker->alloc_bits & (1 << b)) tmp[0] = appf(tmp[0], ", ", "%s", s); + appspk(0, _("LF+LR: Front left and right")); + appspk(1, _("LFE: Low-frequency effects")); + appspk(2, _("FC: Front center")); + appspk(3, _("LR+LR: Rear left and right")); + appspk(4, _("RC: Rear center")); + appspk(5, _("???")); + appspk(6, _("???")); + ret = g_strdup_printf("([%x] %s) len:%d %s -- %s", + blk->header.type, _(edid_cea_block_type(blk->header.type)), + blk->header.len, + tmp[0], + hb); + g_free(tmp[0]); + break; + case 0x3: + case 0x2: + default: + ret = g_strdup_printf("([%x] %s) len:%d -- %s", + blk->header.type, _(edid_cea_block_type(blk->header.type)), + blk->header.len, + hb); + break; + } + free(hb); + } + return ret; +} + +char *edid_dtd_describe(struct edid_dtd *dtd) { + gchar *ret = NULL; + if (dtd) { + char *hb = hex_bytes(dtd->ptr, 18); + ret = g_strdup_printf("%dx%d %s", + dtd->horiz_pixels, dtd->vert_lines, + hb); + free(hb); + } + return ret; +} + +char *edid_dump2(edid *e) { char *ret = NULL; int i; + if (!e) return NULL; - if (!id) return NULL; - ret = appfnl(ret, "edid_version: %d.%d (%d bytes)", id->ver_major, id->ver_minor, id->size); + ret = appfnl(ret, "edid_version: %d.%d (%d bytes)", e->ver_major, e->ver_minor, e->len); + ret = appfnl(ret, "standard: %s", _(edid_standard(e->std)) ); - ret = appfnl(ret, "mfg: %s, model: %u, n_serial: %u", id->ven, id->product, id->n_serial); - if (id->week && id->year) - ret = appf(ret, "", ", dom: week %d of %d", id->week, id->year); - else if (id->year) - ret = appf(ret, "", ", dom: %d", id->year); + ret = appfnl(ret, "mfg: %s, model: %u, n_serial: %u", e->ven, 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); - ret = appfnl(ret, "type: %s", id->a_or_d ? "digital" : "analog"); - if (id->bpc) - ret = appfnl(ret, "bits per color channel: %d", id->bpc); + ret = appfnl(ret, "type: %s", e->a_or_d ? "digital" : "analog"); + if (e->bpc) + ret = appfnl(ret, "bits per color channel: %d", e->bpc); - if (id->horiz_cm && id->vert_cm) - ret = appfnl(ret, "size: %d cm × %d cm", id->horiz_cm, id->vert_cm); - if (id->diag_cm) - ret = appfnl(ret, "diagonal: %0.2f cm (%0.2f in)", id->diag_cm, id->diag_in); + if (e->horiz_cm && e->vert_cm) + ret = appfnl(ret, "size: %d cm × %d cm", e->horiz_cm, e->vert_cm); + if (e->diag_cm) + ret = appfnl(ret, "diagonal: %0.2f cm (%0.2f in)", e->diag_cm, e->diag_in); - ret = appfnl(ret, "checksum %s", id->checksum_ok ? "ok" : "failed!"); - if (id->ext_blocks_ok || id->ext_blocks_fail) - ret = appf(ret, "", ", extension blocks: %d of %d ok", id->ext_blocks_ok, id->ext_blocks_ok + id->ext_blocks_fail); + ret = appfnl(ret, "checksum %s", e->checksum_ok ? "ok" : "failed!"); + 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] (%s): %s", i, _(edid_descriptor_type(id->d_type[i])), *id->d_text[i] ? id->d_text[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 < e->ext_blocks; i++) { + int type = e->u8[(i+1)*128]; + int version = e->u8[(i+1)*128 + 1]; + ret = appfnl(ret, "ext[%d] ([%02x:v%02x] %s) %s", i, + type, version, _(edid_ext_block_type(type)), + e->ext_ok[i] ? "ok" : "fail" + ); + } - for(i = 0; i < EDID_MAX_EXT_BLOCKS; i++) { - if (id->ext[i][0]) { - ret = appfnl(ret, "ext_block[%d]: (%s): %s", i, _(edid_ext_block_type(id->ext[i][0])), id->ext[i][1] ? "ok" : "fail"); - } + for(i = 0; i < e->dtd_count; i++) { + char *desc = edid_dtd_describe(&e->dtds[i]); + ret = appfnl(ret, "dtd[%d] %s", i, desc); + free(desc); + } + for(i = 0; i < e->cea_block_count; i++) { + char *desc = edid_cea_block_describe(&e->cea_blocks[i]); + ret = appfnl(ret, "cea_block[%d] %s", i, desc); } return ret; } -char *edid_bin_to_hex(const void *edid_bytes, int edid_len) { - int lines = edid_len / 16; - int blen = lines * 35 + 1; - int pc = 0; - char *ret = malloc(blen); - memset(ret, 0, blen); - uint8_t *u8 = (uint8_t*)edid_bytes; - char *p = ret; - for(; lines; lines--) { - sprintf(p, "\t\t"); - p+=2; - int i; - for(i = 0; i < 16; i++) { - sprintf(p, "%02x", (unsigned int)*u8); - p+=2; - u8++; - pc++; - if (pc == blen-1) { - sprintf(p, "\n"); - goto edid_print_done; - } - } - sprintf(p, "\n"); - p++; - } -edid_print_done: - return ret; -} diff --git a/modules/devices/monitors.c b/modules/devices/monitors.c index 4e8ba915..482241fb 100644 --- a/modules/devices/monitors.c +++ b/modules/devices/monitors.c @@ -50,20 +50,19 @@ void find_edid_ids_file() { typedef struct { gchar *drm_connection; - uint8_t *edid_bin; - gsize edid_len; - struct edid id; + edid *e; gchar *_vstr; /* use monitor_vendor_str() */ } monitor; #define monitor_new() g_new0(monitor, 1) monitor *monitor_new_from_sysfs(const gchar *sysfs_edid_file) { + gchar *edid_bin = NULL; + gsize edid_len = 0; monitor *m = monitor_new(); - g_file_get_contents(sysfs_edid_file, (gchar**)&(m->edid_bin), &m->edid_len, NULL); - if (m->edid_len) { - edid_fill(&m->id, (void*)(m->edid_bin), m->edid_len); + g_file_get_contents(sysfs_edid_file, &edid_bin, &edid_len, NULL); + if (edid_len) { + m->e = edid_new(edid_bin, edid_len); } - gchar *pd = g_path_get_dirname(sysfs_edid_file); m->drm_connection = g_path_get_basename(pd); g_free(pd); @@ -75,7 +74,7 @@ void monitor_free(monitor *m) { if (m) { g_free(m->_vstr); g_free(m->drm_connection); - g_free(m->edid_bin); + edid_free(m->e); g_free(m); } } @@ -89,12 +88,12 @@ const gchar *monitor_vendor_str(monitor *m) { if (!edid_ids_file) find_edid_ids_file(); - scan_ids_file(edid_ids_file, m->id.ven, &result, -1); + scan_ids_file(edid_ids_file, m->e->ven, &result, -1); if (result.results[0]) { m->_vstr = g_strdup(result.results[0]); return m->_vstr; } - return g_strdup(m->id.ven); + return g_strdup(m->e->ven); } gchar *monitor_name(monitor *m, gboolean include_vendor) { @@ -102,22 +101,22 @@ gchar *monitor_name(monitor *m, gboolean include_vendor) { gchar *desc = NULL; if (include_vendor) { - if (*m->id.ven) + if (*m->e->ven) desc = appfsp(desc, "%s", vendor_get_shortest_name(monitor_vendor_str(m))); else desc = appfsp(desc, "%s", "Unknown"); } - if (m->id.diag_in) { - gchar *din = util_strchomp_float(g_strdup_printf("%0.1f", m->id.diag_in)); + if (m->e->diag_in) { + gchar *din = util_strchomp_float(g_strdup_printf("%0.1f", m->e->diag_in)); desc = appfsp(desc, "%s\"", din); g_free(din); } - if (m->id.name) - desc = appfsp(desc, "%s", m->id.name); + if (m->e->name) + desc = appfsp(desc, "%s", m->e->name); else - desc = appfsp(desc, "%s %s", m->id.a_or_d ? "Digital" : "Analog", "Display"); + desc = appfsp(desc, "%s %s", m->e->a_or_d ? "Digital" : "Analog", "Display"); return desc; } @@ -159,31 +158,32 @@ static gchar *tag_make_safe_inplace(gchar *tag) { } static gchar *make_edid_section(monitor *m) { - if (m->edid_len) { + if (m->e->len) { const gchar *vstr = monitor_vendor_str(m); gchar *dom = NULL; - if (m->id.week && m->id.year) - dom = g_strdup_printf(_("Week %d of %d"), m->id.week, m->id.year); - else if (m->id.year) - dom = g_strdup_printf("%d", m->id.year); + if (m->e->week && m->e->year) + dom = g_strdup_printf(_("Week %d of %d"), m->e->week, m->e->year); + else if (m->e->year) + dom = g_strdup_printf("%d", m->e->year); gchar *bpcc = NULL; - if (m->id.bpc) - bpcc = g_strdup_printf("%d", m->id.bpc); + if (m->e->bpc) + bpcc = g_strdup_printf("%d", m->e->bpc); gchar *scr_size = NULL; - if (m->id.horiz_cm && m->id.vert_cm) - scr_size = g_strdup_printf("%d cm × %d cm", m->id.horiz_cm, m->id.vert_cm); + if (m->e->horiz_cm && m->e->vert_cm) + scr_size = g_strdup_printf("%d cm × %d cm", m->e->horiz_cm, m->e->vert_cm); - int aok = m->id.checksum_ok; - if (m->id.ext_blocks_fail) aok = 0; + int aok = m->e->checksum_ok; + if (m->e->ext_blocks_fail) aok = 0; gchar *csum = aok ? _("Ok") : _("Fail"); gchar *ret = g_strdup_printf("[%s]\n" - "%s=%d.%d\n" /* version */ "%s=%d %s\n" /* size */ + "%s=%d.%d\n" /* version */ "%s=%d\n" /* ext block */ + "%s=%s\n" /* ext to */ "%s=%s %s\n" /* checksum */ "[%s]\n" "%s=%s\n" /* vendor */ @@ -194,16 +194,17 @@ static gchar *make_edid_section(monitor *m) { "%s=%s\n" /* bpcc */ , _("EDID Meta"), - _("Version"), (int)m->id.ver_major, (int)m->id.ver_minor, - _("Data Size"), m->id.size, _("bytes"), - _("Extension Blocks"), m->id.ext_blocks, + _("Data Size"), m->e->len, _("bytes"), + _("Version"), (int)m->e->ver_major, (int)m->e->ver_minor, + _("Extension Blocks"), m->e->ext_blocks, + _("Extended to"), _(edid_standard(m->e->std)), _("Checksum"), csum, aok ? "" : problem_marker(), _("EDID Device"), _("Vendor"), vstr, - _("Name"), m->id.name, + _("Name"), m->e->name, _("Manufacture Date"), UNKIFNULL2(dom), _("Screen Size"), UNKIFNULL2(scr_size), - _("Signal Type"), m->id.a_or_d ? _("Digital") : _("Analog"), + _("Signal Type"), m->e->a_or_d ? _("Digital") : _("Analog"), _("Bits per Color Channel"), UNKIFNULL2(bpcc) ); g_free(scr_size); @@ -228,7 +229,7 @@ gchar *monitors_get_info() { monitor *m = monitor_new_from_sysfs(edid_files[i]); if (m) { found++; - if (m->id.checksum_ok && m->drm_connection) { + if (m->e && m->e->checksum_ok && m->drm_connection) { gchar *tag = g_strdup_printf("%d-%s", found, m->drm_connection); tag_make_safe_inplace(tag); gchar *desc = monitor_name(m, TRUE); |