source code example about parse edid
#include <stdio.h> #include <stdlib.h> #include <string.h> // TODO: rewrite // FIXME: cleanup 'static' variables typedef unsigned char byte; /* byte must be 8 bits */ /* int must be at least 16 bits */ /* long must be at least 32 bits */ #define DIE_MSG( x ) \ { MSG( x ); exit( 1 ); } #define UPPER_NIBBLE( x ) \ (((128|64|32|16) & (x)) >> 4) #define LOWER_NIBBLE( x ) \ ((1|2|4|8) & (x)) #define COMBINE_HI_8LO( hi, lo ) \ ( (((unsigned)hi) << 8) | (unsigned)lo ) #define COMBINE_HI_4LO( hi, lo ) \ ( (((unsigned)hi) << 4) | (unsigned)lo ) const byte edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; const byte edid_v1_descriptor_flag[] = { 0x00, 0x00 }; #define EDID_LENGTH 0x80 #define EDID_HEADER 0x00 #define EDID_HEADER_END 0x07 #define ID_MANUFACTURER_NAME 0x08 #define ID_MANUFACTURER_NAME_END 0x09 #define ID_MODEL 0x0a #define ID_SERIAL_NUMBER 0x0c #define MANUFACTURE_WEEK 0x10 #define MANUFACTURE_YEAR 0x11 #define EDID_STRUCT_VERSION 0x12 #define EDID_STRUCT_REVISION 0x13 #define DPMS_FLAGS 0x18 #define ESTABLISHED_TIMING_1 0x23 #define ESTABLISHED_TIMING_2 0x24 #define MANUFACTURERS_TIMINGS 0x25 #define DETAILED_TIMING_DESCRIPTIONS_START 0x36 #define DETAILED_TIMING_DESCRIPTION_SIZE 18 #define NO_DETAILED_TIMING_DESCRIPTIONS 4 #define DETAILED_TIMING_DESCRIPTION_1 0x36 #define DETAILED_TIMING_DESCRIPTION_2 0x48 #define DETAILED_TIMING_DESCRIPTION_3 0x5a #define DETAILED_TIMING_DESCRIPTION_4 0x6c #define PIXEL_CLOCK_LO (unsigned)dtd[ 0 ] #define PIXEL_CLOCK_HI (unsigned)dtd[ 1 ] #define PIXEL_CLOCK (COMBINE_HI_8LO( PIXEL_CLOCK_HI,PIXEL_CLOCK_LO )*10000) #define H_ACTIVE_LO (unsigned)dtd[ 2 ] #define H_BLANKING_LO (unsigned)dtd[ 3 ] #define H_ACTIVE_HI UPPER_NIBBLE( (unsigned)dtd[ 4 ] ) #define H_ACTIVE COMBINE_HI_8LO( H_ACTIVE_HI, H_ACTIVE_LO ) #define H_BLANKING_HI LOWER_NIBBLE( (unsigned)dtd[ 4 ] ) #define H_BLANKING COMBINE_HI_8LO( H_BLANKING_HI, H_BLANKING_LO ) #define V_ACTIVE_LO (unsigned)dtd[ 5 ] #define V_BLANKING_LO (unsigned)dtd[ 6 ] #define V_ACTIVE_HI UPPER_NIBBLE( (unsigned)dtd[ 7 ] ) #define V_ACTIVE COMBINE_HI_8LO( V_ACTIVE_HI, V_ACTIVE_LO ) #define V_BLANKING_HI LOWER_NIBBLE( (unsigned)dtd[ 7 ] ) #define V_BLANKING COMBINE_HI_8LO( V_BLANKING_HI, V_BLANKING_LO ) #define H_SYNC_OFFSET_LO (unsigned)dtd[ 8 ] #define H_SYNC_WIDTH_LO (unsigned)dtd[ 9 ] #define V_SYNC_OFFSET_LO UPPER_NIBBLE( (unsigned)dtd[ 10 ] ) #define V_SYNC_WIDTH_LO LOWER_NIBBLE( (unsigned)dtd[ 10 ] ) #define V_SYNC_WIDTH_HI ((unsigned)dtd[ 11 ] & (1|2)) #define V_SYNC_OFFSET_HI (((unsigned)dtd[ 11 ] & (4|8)) >> 2) #define H_SYNC_WIDTH_HI (((unsigned)dtd[ 11 ] & (16|32)) >> 4) #define H_SYNC_OFFSET_HI (((unsigned)dtd[ 11 ] & (64|128)) >> 6) #define V_SYNC_WIDTH COMBINE_HI_4LO( V_SYNC_WIDTH_HI, V_SYNC_WIDTH_LO ) #define V_SYNC_OFFSET COMBINE_HI_4LO( V_SYNC_OFFSET_HI, V_SYNC_OFFSET_LO ) #define H_SYNC_WIDTH COMBINE_HI_4LO( H_SYNC_WIDTH_HI, H_SYNC_WIDTH_LO ) #define H_SYNC_OFFSET COMBINE_HI_4LO( H_SYNC_OFFSET_HI, H_SYNC_OFFSET_LO ) #define H_SIZE_LO (unsigned)dtd[ 12 ] #define V_SIZE_LO (unsigned)dtd[ 13 ] #define H_SIZE_HI UPPER_NIBBLE( (unsigned)dtd[ 14 ] ) #define V_SIZE_HI LOWER_NIBBLE( (unsigned)dtd[ 14 ] ) #define H_SIZE COMBINE_HI_8LO( H_SIZE_HI, H_SIZE_LO ) #define V_SIZE COMBINE_HI_8LO( V_SIZE_HI, V_SIZE_LO ) #define H_BORDER (unsigned)dtd[ 15 ] #define V_BORDER (unsigned)dtd[ 16 ] #define FLAGS (unsigned)dtd[ 17 ] #define INTERLACED (FLAGS&128) #define SYNC_TYPE (FLAGS&3<<3) /* bits 4,3 */ #define SYNC_SEPARATE (3<<3) #define HSYNC_POSITIVE (FLAGS & 4) #define VSYNC_POSITIVE (FLAGS & 2) #define MONITOR_NAME 0xfc #define MONITOR_LIMITS 0xfd #define UNKNOWN_DESCRIPTOR -1 #define DETAILED_TIMING_BLOCK -2 #define DESCRIPTOR_DATA 5 #define V_MIN_RATE block[ 5 ] #define V_MAX_RATE block[ 6 ] #define H_MIN_RATE block[ 7 ] #define H_MAX_RATE block[ 8 ] #define MAX_PIXEL_CLOCK (((int)block[ 9 ]) * 10) #define GTF_SUPPORT block[10] #define DPMS_ACTIVE_OFF (1 << 5) #define DPMS_SUSPEND (1 << 6) #define DPMS_STANDBY (1 << 7) char* myname; void MSG( const char* x ) { fprintf( stderr, "%s: %s\n", myname, x ); } int parse_edid( byte* edid ); int parse_timing_description( byte* dtd ); int parse_monitor_limits( byte* block ); int block_type( byte* block ); char* get_monitor_name( byte const* block ); char* get_vendor_sign( byte const* block ); int parse_dpms_capabilities( byte flags ); int main( int argc, char** argv ) { byte edid[ EDID_LENGTH ]; FILE* edid_file; myname = argv[ 0 ]; fprintf( stderr, "%s: parse-edid version %s\n", myname, VERSION ); if ( argc > 2 ) { DIE_MSG( "syntax: [input EDID file]" ); } else { if ( argc == 2 ) { edid_file = fopen( argv[ 1 ], "rb" ); if ( !edid_file ) DIE_MSG( "unable to open file for input" ); } else edid_file = stdin; } if ( fread( edid, sizeof( byte ), EDID_LENGTH, edid_file ) != EDID_LENGTH ) { DIE_MSG( "IO error reading EDID" ); } fclose( edid_file ); return parse_edid( edid ); } int parse_edid( byte* edid ) { unsigned i; byte* block; char* monitor_name = NULL; char monitor_alt_name[100]; byte checksum = 0; char *vendor_sign; int ret = 0; for( i = 0; i < EDID_LENGTH; i++ ) checksum += edid[ i ]; if ( checksum != 0 ) { MSG( "EDID checksum failed - data is corrupt. Continuing anyway." ); ret = 1; } else MSG( "EDID checksum passed." ); if ( strncmp( edid+EDID_HEADER, edid_v1_header, EDID_HEADER_END+1 ) ) { MSG( "first bytes don't match EDID version 1 header" ); MSG( "do not trust output (if any)." ); ret = 1; } printf( "\n\t# EDID version %d revision %d\n", (int)edid[EDID_STRUCT_VERSION],(int)edid[EDID_STRUCT_REVISION] ); vendor_sign = get_vendor_sign( edid + ID_MANUFACTURER_NAME ); printf( "Section \"Monitor\"\n" ); block = edid + DETAILED_TIMING_DESCRIPTIONS_START; for( i = 0; i < NO_DETAILED_TIMING_DESCRIPTIONS; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE ) { if ( block_type( block ) == MONITOR_NAME ) { monitor_name = get_monitor_name( block ); break; } } if (!monitor_name) { /* Stupid djgpp hasn't snprintf so we have to hack something together */ if(strlen(vendor_sign) + 10 > sizeof(monitor_alt_name)) vendor_sign[3] = 0; sprintf(monitor_alt_name, "%s:%02x%02x", vendor_sign, edid[ID_MODEL], edid[ID_MODEL+1]) ; monitor_name = monitor_alt_name; } printf( "\tIdentifier \"%s\"\n", monitor_name ); printf( "\tVendorName \"%s\"\n", vendor_sign ); printf( "\tModelName \"%s\"\n", monitor_name ); block = edid + DETAILED_TIMING_DESCRIPTIONS_START; for( i = 0; i < NO_DETAILED_TIMING_DESCRIPTIONS; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE ) { if ( block_type( block ) == MONITOR_LIMITS ) parse_monitor_limits( block ); } parse_dpms_capabilities(edid[DPMS_FLAGS]); block = edid + DETAILED_TIMING_DESCRIPTIONS_START; for( i = 0; i < NO_DETAILED_TIMING_DESCRIPTIONS; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE ) { if ( block_type( block ) == DETAILED_TIMING_BLOCK ) parse_timing_description( block ); } printf( "EndSection\n" ); return ret; } int parse_timing_description( byte* dtd ) { int htotal, vtotal; htotal = H_ACTIVE + H_BLANKING; vtotal = V_ACTIVE + V_BLANKING; printf( "\tMode \t\"%dx%d\"", H_ACTIVE, V_ACTIVE ); printf( "\t# vfreq %3.3fHz, hfreq %6.3fkHz\n", (double)PIXEL_CLOCK/((double)vtotal*(double)htotal), (double)PIXEL_CLOCK/(double)(htotal*1000)); printf( "\t\tDotClock\t%f\n", (double)PIXEL_CLOCK/1000000.0 ); printf( "\t\tHTimings\t%u %u %u %u\n", H_ACTIVE, H_ACTIVE+H_SYNC_OFFSET, H_ACTIVE+H_SYNC_OFFSET+H_SYNC_WIDTH, htotal ); printf( "\t\tVTimings\t%u %u %u %u\n", V_ACTIVE, V_ACTIVE+V_SYNC_OFFSET, V_ACTIVE+V_SYNC_OFFSET+V_SYNC_WIDTH, vtotal ); if ( INTERLACED || (SYNC_TYPE == SYNC_SEPARATE)) { printf( "\t\tFlags\t%s\"%sHSync\" \"%sVSync\"\n", INTERLACED ? "\"Interlace\" ": "", HSYNC_POSITIVE ? "+": "-", VSYNC_POSITIVE ? "+": "-"); } printf( "\tEndMode\n" ); return 0; } int block_type( byte* block ) { if ( !strncmp( edid_v1_descriptor_flag, block, 2 ) ) { printf("\t# Block type: 2:%x 3:%x\n", block[2], block[3]); /* descriptor */ if ( block[ 2 ] != 0 ) return UNKNOWN_DESCRIPTOR; return block[ 3 ]; } else { /* detailed timing block */ return DETAILED_TIMING_BLOCK; } } char* get_monitor_name( byte const* block ) { static char name[ 13 ]; unsigned i; byte const* ptr = block + DESCRIPTOR_DATA; for( i = 0; i < 13; i++, ptr++ ) { if ( *ptr == 0xa ) { name[ i ] = 0; return name; } name[ i ] = *ptr; } return name; } char* get_vendor_sign( byte const* block ) { static char sign[4]; unsigned short h; /* 08h WORD big-endian manufacturer ID (see #00136) bits 14-10: first letter (01h='A', 02h='B', etc.) bits 9-5: second letter bits 4-0: third letter */ h = COMBINE_HI_8LO(block[0], block[1]); sign[0] = ((h>>10) & 0x1f) + 'A' - 1; sign[1] = ((h>>5) & 0x1f) + 'A' - 1; sign[2] = (h & 0x1f) + 'A' - 1; sign[3] = 0; return sign; } int parse_monitor_limits( byte* block ) { printf( "\tHorizSync %u-%u\n", H_MIN_RATE, H_MAX_RATE ); printf( "\tVertRefresh %u-%u\n", V_MIN_RATE, V_MAX_RATE ); if ( MAX_PIXEL_CLOCK == 10*0xff ) printf( "\t# Max dot clock not given\n" ); else printf( "\t# Max dot clock (video bandwidth) %u MHz\n", (int)MAX_PIXEL_CLOCK ); if ( GTF_SUPPORT ) { printf( "\t# EDID version 3 GTF given: contact author\n" ); } return 0; } int parse_dpms_capabilities(byte flags) { printf("\t# DPMS capabilities: Active off:%s Suspend:%s Standby:%s\n\n", (flags & DPMS_ACTIVE_OFF) ? "yes" : "no", (flags & DPMS_SUSPEND) ? "yes" : "no", (flags & DPMS_STANDBY) ? "yes" : "no"); return 0; }