Translate

Archives

Retrieve EDID information via SysFS

EDID (Extended display Identification Data) is a defined data structure published by a digital monitor to describe its capabilities to a video source such as a graphics card on a computer. This enables a computer to easily determine what types of monitor(s) are connected to it. The EDID structure and possible values for the members of the structure is defined in a standard published by the Video Electronics Standards Association (VESA). Four versions of the EDID structure have been defined over the years. Currently v1.3 ( E-EDID – Enhanced EDID) is probably the most common.

On modern Linux platforms you can access EDID information via the sysfs virtual filesystem which is mounted at /sys. For example, here is the contents of the edid file for the first monitor on my first graphics card.

EDID file contents screenshot

Here is a small example C program which parses the edid file(s) under /sys/class/drm to output most of the core EDID information for whatever digital monitors are attached to your computer.

//
//  Copyright (c) 2011, Finnbarr P. Murphy.  All rights reserved.
// 

#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <stdlib.h>
#include <limits.h>
#include <libgen.h>
#include <ftw.h>
#include <fnmatch.h>
#include <fcntl.h>

// most of these defines come straight out of NetBSD edid source code
#define CHECK_BIT(var,pos)                ((var) & (1<<(pos)))
#define EDID_COMBINE_HI_8LO( hi, lo )     ((((unsigned)hi) << 8) | (unsigned)lo )
#define EDID_LENGTH                       0x80
#define EDID_SYS_PATH                     "/sys/class/drm"
#define EDID_VIDEO_INPUT_DFP1_COMPAT      0x01
#define EDID_VIDEO_INPUT_LEVEL(x)         (((x) & 0x60) >> 5)
#define EDID_DPMS_ACTIVE_OFF              (1 << 5)
#define EDID_DPMS_SUSPEND                 (1 << 6)
#define EDID_DPMS_STANDBY                 (1 << 7)
#define EDID_STD_TIMING_HRES(ptr)         ((((ptr)[0]) * 8) + 248)
#define EDID_STD_TIMING_VFREQ(ptr)        ((((ptr)[1]) & 0x3f) + 60)
#define EDID_STD_TIMING_RATIO(ptr)        ((ptr)[1] & 0xc0)
#define EDID_BLOCK_IS_DET_TIMING(ptr)     ((ptr)[0] | (ptr)[1])
#define EDID_DET_TIMING_DOT_CLOCK(ptr)    (((ptr)[0] | ((ptr)[1] << 8)) * 10000)
#define EDID_HACT_LO(ptr)                 ((ptr)[2])
#define EDID_HBLK_LO(ptr)                 ((ptr)[3])
#define EDID_HACT_HI(ptr)                 (((ptr)[4] & 0xf0) << 4)
#define EDID_HBLK_HI(ptr)                 (((ptr)[4] & 0x0f) << 8)
#define EDID_DET_TIMING_HACTIVE(ptr)      (EDID_HACT_LO(ptr) | EDID_HACT_HI(ptr))
#define EDID_DET_TIMING_HBLANK(ptr)       (EDID_HBLK_LO(ptr) | EDID_HBLK_HI(ptr))
#define EDID_VACT_LO(ptr)                 ((ptr)[5])
#define EDID_VBLK_LO(ptr)                 ((ptr)[6])
#define EDID_VACT_HI(ptr)                 (((ptr)[7] & 0xf0) << 4)
#define EDID_VBLK_HI(ptr)                 (((ptr)[7] & 0x0f) << 8)
#define EDID_DET_TIMING_VACTIVE(ptr)      (EDID_VACT_LO(ptr) | EDID_VACT_HI(ptr))
#define EDID_DET_TIMING_VBLANK(ptr)       (EDID_VBLK_LO(ptr) | EDID_VBLK_HI(ptr))
#define EDID_HOFF_LO(ptr)                 ((ptr)[8])
#define EDID_HWID_LO(ptr)                 ((ptr)[9])
#define EDID_VOFF_LO(ptr)                 ((ptr)[10] >> 4)
#define EDID_VWID_LO(ptr)                 ((ptr)[10] & 0xf)
#define EDID_HOFF_HI(ptr)                 (((ptr)[11] & 0xc0) << 2)
#define EDID_HWID_HI(ptr)                 (((ptr)[11] & 0x30) << 4)
#define EDID_VOFF_HI(ptr)                 (((ptr)[11] & 0x0c) << 2)
#define EDID_VWID_HI(ptr)                 (((ptr)[11] & 0x03) << 4)
#define EDID_DET_TIMING_HSYNC_OFFSET(ptr) (EDID_HOFF_LO(ptr) | EDID_HOFF_HI(ptr))
#define EDID_DET_TIMING_HSYNC_WIDTH(ptr)  (EDID_HWID_LO(ptr) | EDID_HWID_HI(ptr))
#define EDID_DET_TIMING_VSYNC_OFFSET(ptr) (EDID_VOFF_LO(ptr) | EDID_VOFF_HI(ptr))
#define EDID_DET_TIMING_VSYNC_WIDTH(ptr)  (EDID_VWID_LO(ptr) | EDID_VWID_HI(ptr))
#define EDID_HSZ_LO(ptr)                  ((ptr)[12])
#define EDID_VSZ_LO(ptr)                  ((ptr)[13])
#define EDID_HSZ_HI(ptr)                  (((ptr)[14] & 0xf0) << 4)
#define EDID_VSZ_HI(ptr)                  (((ptr)[14] & 0x0f) << 8)
#define EDID_DET_TIMING_HSIZE(ptr)        (EDID_HSZ_LO(ptr) | EDID_HSZ_HI(ptr))
#define EDID_DET_TIMING_VSIZE(ptr)        (EDID_VSZ_LO(ptr) | EDID_VSZ_HI(ptr))
#define EDID_DET_TIMING_HBORDER(ptr)      ((ptr)[15])
#define EDID_DET_TIMING_VBORDER(ptr)      ((ptr)[16])
#define EDID_DET_TIMING_FLAGS(ptr)        ((ptr)[17])
#define EDID_DET_TIMING_VSOBVHSPW(ptr)    ((ptr)[11])

struct edid_block {
   uint8_t  header[8];               // EDID header "00 FF FF FF FF FF FF 00"
   uint16_t manufacturerCode;        // EISA 3-character ID
   uint16_t productCode;             // Vendor assigned code
   uint32_t serialNumber;            // Serial number
   uint8_t  manufacturedWeek;        // Week number
   uint8_t  manufacturedYear;        // Year number + 1990
   uint8_t  version;                 // EDID version
   uint8_t  revision;                // EDID revision
   uint8_t  videoInputDefinition;
   uint8_t  maxHorizontalImageSize;  // in cm
   uint8_t  maxVerticalImageSize;    // in cm
   uint8_t  displayGamma;            // gamma
   uint8_t  dpmSupport;              // DPMS
   uint8_t  redGreenLowBits;         // Rx1 Rx0 Ry1 Ry0 Gx1 Gx0 Gy1Gy0
   uint8_t  blueWhiteLowBits;        // Bx1 Bx0 By1 By0 Wx1 Wx0 Wy1 Wy0
   uint8_t  redX;                    // Red-x Bits 9 - 2
   uint8_t  redY;                    // Red-y Bits 9 - 2
   uint8_t  greenX;                  // Green-x Bits 9 - 2
   uint8_t  greenY;                  // Green-y Bits 9 - 2
   uint8_t  blueX;                   // Blue-x Bits 9 - 2
   uint8_t  blueY;                   // Blue-y Bits 9 - 2
   uint8_t  whiteX;                  // White-x Bits 9 - 2
   uint8_t  whiteY;                  // White-x Bits 9 - 2
   uint8_t  establishedTimings[3];
   uint8_t  standardTimings[16];
   uint8_t  descriptionBlock1[18];
   uint8_t  descriptionBlock2[18];
   uint8_t  descriptionBlock3[18];
   uint8_t  descriptionBlock4[18];
   uint8_t  extensions;              // Number of (optional) 128-byte EDID extension blocks
   uint8_t  checksum;
} __attribute__((packed));

struct edid_block edid;

// from NetBSD edid code
const char *edid_established_modes[] = {
   "1280x1024 @ 75Hz",
   "1024x768 @ 75Hz",
   "1024x768 @ 70Hz",
   "1024x768 @ 60Hz",
   "1024x768 @ 87Hz",
   "832x624 @ 75Hz",
   "800x600 @ 75Hz",
   "800x600 @ 72Hz",
   "800x600 @ 60Hz",
   "800x600 @ 56Hz",
   "640x480 @ 75Hz",
   "640x480 @ 72Hz",
   "640x480 @ 67Hz",
   "640x480 @ 60Hz",
   "720x400 @ 88Hz",
   "720x400 @ 70Hz"
};

int last_stb_mode = 0;

char *
manufacturer_abbrev(uint8_t *edid)
{
    struct edid_block *eb = (struct edid_block *)edid;
    static char mcode[8];
    uint16_t h;
    uint8_t *block = (uint8_t *)&(eb->manufacturerCode);

    h = EDID_COMBINE_HI_8LO(block[0], block[1]);
    mcode[0] = ((h>>10) & 0x1f) + 'A' - 1;
    mcode[1] = ((h>>5) & 0x1f) + 'A' - 1;
    mcode[2] = (h & 0x1f) + 'A' - 1;
    mcode[3] = 0;

    return mcode;
}


char *
display_gamma(uint8_t *edid)
{
    struct edid_block *eb = (struct edid_block *)edid;
    static char mcode[8];
    float g1 = (float)eb->displayGamma;
    float g2 = 1.00 + (g1/100);

    sprintf(mcode, "%.3f", g2);

    return mcode;
}

void
print_std_timing(uint8_t *stb)
{
    unsigned  hres, vres, freq;
    float aspectratio;

    if ((stb[0] == 1 && stb[1] == 1) ||
        (stb[0] == 0 && stb[1] == 0) ||
        (stb[0] == 0x20 && stb[1] == 0x20))
            return;

    hres = EDID_STD_TIMING_HRES(stb);
    switch (EDID_STD_TIMING_RATIO(stb)) {
        case 0x00:
            aspectratio = 10.0/16;
            break;
        case 0x40:
            aspectratio = 3.0/4;
            break;
        case 0x80:
            aspectratio = 4.0/5;
            break;
        case 0xC0:
            default:
            aspectratio = 9.0/16;
            break;
    }
    vres = hres * aspectratio;
    freq = EDID_STD_TIMING_VFREQ(stb);

    printf("       <Resolution>\n");
    printf("           <Mode>%d</Mode>\n", last_stb_mode);
    printf("           <XResolution>%d</XResolution>\n", hres);
    printf("           <YResolution>%d</YResolution>\n", vres);
    printf("           <AspectRatio>%.3f</AspectRatio>\n", aspectratio);
    printf("           <VertRefreshFreq units=\"Hz\">%d</VertRefreshFreq>\n", freq);
    printf("       </Resolution>\n");

    last_stb_mode++;
}
  

void
print_det_timing(uint8_t *dtb)
{
    unsigned    hactive, hblank, hsyncwid, hsyncoff;
    unsigned    vactive, vblank, vsyncwid, vsyncoff;
    uint8_t     flags;
    static char flagstr[400];

    hactive  = EDID_DET_TIMING_HACTIVE(dtb);
    hblank   = EDID_DET_TIMING_HBLANK(dtb);
    hsyncwid = EDID_DET_TIMING_HSYNC_WIDTH(dtb);
    hsyncoff = EDID_DET_TIMING_HSYNC_OFFSET(dtb);

    vactive  = EDID_DET_TIMING_VACTIVE(dtb);
    vblank   = EDID_DET_TIMING_VBLANK(dtb);
    vsyncwid = EDID_DET_TIMING_VSYNC_WIDTH(dtb);
    vsyncoff = EDID_DET_TIMING_VSYNC_OFFSET(dtb);

    printf("   <DescriptorBlock number=\"1\">\n");
    printf("       <HoriFreq units=\"kHz\">%d</HoriFreq>\n", dtb[0]);
    printf("       <VertFreq units=\"Hz\">%d</VertFreq>\n", dtb[1]);
    printf("       <HoriActiveTimePixels>%d</HoriActiveTimePixels>\n", hactive);
    printf("       <HoriBlankTimePixels>%d</HoriBlankTimePixels>\n", hblank);
    printf("       <HoriActiveTimeByHoriBlankingTime>%d</HoriActiveTimeByHoriBlankingTime>\n", dtb[4]);
    printf("       <VertActiveTimeLines>%d</VertActiveTimeLines>\n", vactive);
    printf("       <VertBlankTimeLines>%d</VertBlankTimeLines>\n", vblank);
    printf("       <VertActiveTimeByVertBlankTime>%d</VertActiveTimeByVertBlankTime>\n", dtb[7]);
    printf("       <HoriSyncOffsetPixels>%d</HoriSyncOffsetPixels>\n", hsyncoff);
    printf("       <HoriSyncPulseWidthPixels>%d</HoriSyncPulseWidthPixels>\n", hsyncwid);
    printf("       <VertSyncOffsetByVertSyncPulsewidth>%d</VertSyncOffsetByVertSyncPulsewidth>\n", dtb[10]);
    printf("       <VertByHoriSyncOffsetByVertByHoriSyncPulsewidth\">%d</VertByHoriSyncOffsetByVertByHoriSyncPulsewidth>\n", EDID_DET_TIMING_VSOBVHSPW(dtb));
    printf("       <HoriImageSize>%d</HoriImageSize>\n", EDID_DET_TIMING_HSIZE(dtb));
    printf("       <VertImageSize>%d</VertImageSize>\n", EDID_DET_TIMING_VSIZE(dtb));
    printf("       <HoriImgSizeByVertImgSize>%d</HoriImgSizeByVertImgSize>\n", dtb[14]);
    printf("       <HoriBorder>%d</HoriBorder>\n", EDID_DET_TIMING_HBORDER(dtb));
    printf("       <VertBorder>%d</VertBorder>\n", EDID_DET_TIMING_VBORDER(dtb));

    flags = EDID_DET_TIMING_FLAGS(dtb);
    printf("       <DisplayType\">\n");
    if (CHECK_BIT(flags, 7))
        printf("           <Property>interlaced</Property>\n");
    if (CHECK_BIT(flags, 6)) {
        if (!CHECK_BIT(flags, 5))
            printf("           <Property>stereo, left stereo sync high</Property>\n");
    } else {
        if (CHECK_BIT(flags, 5))
            printf("           <Property>stereo, right stereo sync high</Property>\n");
        else
            printf("           <Property>normal display (no stereo)</Property>\n");
    }
    if (CHECK_BIT(flags, 4)) {
        if (CHECK_BIT(flags, 3))
            printf("           <Property>sync digital separate</Property>\n");
        else
            printf("           <Property>sync digital composite</Property>\n");
    } else {
        if (CHECK_BIT(flags, 3))
            printf("           <Property>sync bipolar analog composite</Property>\n");
        else
            printf("           <Property>sync analog composite</Property>\n");
    }
    if ((CHECK_BIT(flags, 4)) && (CHECK_BIT(flags, 3))) {
        if (CHECK_BIT(flags, 2))
             printf("           <Property>PositiveVerticalSyncPolarity</Property>\n");
        else
             printf("           <Property>NegativeVerticalSyncPolarity</Property>\n");
        if (CHECK_BIT(flags, 1))
             printf("           <Property>PositiveHorizontalSyncPolarity</Property>\n");
        else
             printf("           <Property>NegativeHorizontalSyncPolarity</Property>\n");
    } else {
        if (CHECK_BIT(flags, 2))
            printf("            <Property>serrate</Property>\n");
        if (CHECK_BIT(flags, 1))
            printf("            <Property>SyncLocationOnRGB</Property>\n");
        else
            printf("            <Property>SyncLocationOnGreen</Property>\n");
    }
    printf("       </DisplayType\">\n");

    printf("   </DescriptorBlock>\n");
}


void
print_description_block(uint8_t *data, int num)
{
    static char mcode[20];
    char *c  = mcode;

    switch (*(data + 3)) {
        case  0xFA:
             print_std_timing(data + 5);
             break;

        case  0xFB:
             break;
    }

    printf("   <DescriptorBlock number=\"%d\">\n", num);
    switch (*(data + 3)) {
        case  0xFC:
        case  0xFF:
        case  0xFE:
             memcpy(mcode, data + 5, 13);
             while (*c != 0x0A)
                 c++;
             *c = '\0';
             if (*(data + 3) == 0xFC) {
                 printf("       <ModelNumber>%s</ModelNumber>\n", mcode);
             } else if (*(data + 3) == 0xFF) {
                 printf("       <SerialNumber\">%s</SerialNumber>\n", mcode);
             } else {
                 printf("       <Comment\">%s</Comment>\n", mcode);
             }
             break;

        case  0xFD:
             printf("       <MinVertRefreshFreq units=\"Hz\">%d</MinVertRefreshFreq>\n", *(data + 5));
             printf("       <MaxVertRefreshFreq units=\"Hz\">%d</MaxVertRefreshFreq>\n", *(data + 6));
             printf("       <MinHoriRefreshFreq units=\"KHz\">%d</MinHoriRefreshFreq>\n", *(data + 7));
             printf("       <MaxHoriRefreshFreq units=\"KHz\">%d</MaxHoriRefreshFreq>\n", *(data + 8));
     }
     printf("   </DescriptorBlock>\n");
}

int
check_edid(uint8_t *edid)
{
    uint8_t i;
    uint8_t checksum = 0;
    const uint8_t eb_header[] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
    struct edid_block *eb = (struct edid_block *)edid;

    // check EDID block checksum
    for (i = 0; i < EDID_LENGTH; i++) {
        checksum += edid[i];
    }
    if (checksum != 0) {
        return(1);
    }

    // check EDID header signature
    if (edid[0] == 0x00) {
       checksum = 0;
       for (i = 0; i < sizeof(eb); i++) {
           if (edid[i] == eb_header[i])
               checksum++;
       }
       if (checksum != 8) {
           return(1);
       }
    }

    // check EDID version
    if (eb->version != 1 ||  eb->revision > 4 ) {
        return(1);
    }

    return(0);
}


int
print_edid()
{
    uint16_t estmodes;
    int i;

    printf("<Edid version=\"%d\" revision=\"%d\">\n", edid.version, edid.revision);
    printf("   <VendorId>%02X</VendorId>\n", edid.manufacturerCode);
    printf("   <VendorAbbreviation>%s</VendorAbbreviation>\n", manufacturer_abbrev((uint8_t *)&edid));
    printf("   <ProductId>%02X</ProductId>\n", edid.productCode);
    printf("   <ManufactureWeek>%d</ManufactureWeek>\n", edid.manufacturedWeek);
    printf("   <ManufactureYear>%d</ManufactureYear>\n", edid.manufacturedYear + 1990);

    printf("   <VideoInputDefinition>");
    if (CHECK_BIT(edid.videoInputDefinition, 7))
        printf("Analog");
    else
        printf("Digital");
    printf("</VideoInputDefinition>\n");

    if (edid.videoInputDefinition & 0x1F) {
        printf("   <Syncronization>\n");
        if (CHECK_BIT(edid.videoInputDefinition, 4))
            printf("   <Property>BlankToBackSetup</Property>\n");
        if (CHECK_BIT(edid.videoInputDefinition, 3))
            printf("   <Property>SeparateSync</Property>\n");
        if (CHECK_BIT(edid.videoInputDefinition, 2))
            printf("   <Property>CompositeSync</Property>\n");
        if (CHECK_BIT(edid.videoInputDefinition, 1))
            printf("   <Property>SyncOnGreen</Property>\n");
        if (CHECK_BIT(edid.videoInputDefinition, 0))
            printf("   <Property>SerrationVSync</Property>\n");
        printf("   </Syncronization>\n");
    } else
        printf("   <Syncronization/>\n");

    printf("   <VideoVoltageLevel>");
    switch  EDID_VIDEO_INPUT_LEVEL(edid.videoInputDefinition) {
        case 0:
            printf("0.700V/0.300V (1.0 Vp-p)");
            break;
        case 1:
            printf("0.714V,0.286V");
            break;
        case 2:
            printf("1.000V/0.400V");
            break;
        case 3:
            printf("0.700V/0.000V");
            break;
        default:
            break;
    }
    printf("</VideoVoltageLevel>\n");

    if (CHECK_BIT(edid.videoInputDefinition, 7))
        printf("   <SignalType>DigitalSignal</SignalType>\n");
    else
        printf("   <SignalType>AnalogSignal</SignalType>\n");

    printf("   <MaxHoriSize units=\"cm\">%.1d</MaxHoriSize>\n", edid.maxHorizontalImageSize );
    printf("   <MaxVertSize units=\"cm\">%.1d</MaxVertSize>\n", edid.maxVerticalImageSize );
    printf("   <Gamma>%s</Gamma>\n", display_gamma((uint8_t *)&edid));

    printf("   <DisplayType>");
    if (CHECK_BIT(edid.dpmSupport, 3) && CHECK_BIT(edid.dpmSupport, 4)) {
        printf("Undefined");
    } else if (CHECK_BIT(edid.dpmSupport, 3)) {
        printf("RGB color");
    } else if (CHECK_BIT(edid.dpmSupport, 4)) {
        printf("Non-RGB multicolor");
    } else {
        printf("Monochrome");
    }
    printf("</DisplayType>\n");

    printf("   <DisplayPowerManagementOptions>\n");
    if (edid.dpmSupport & EDID_DPMS_ACTIVE_OFF)
        printf("       <Property>ActiveOff/LowPower</Property>\n");
    if (edid.dpmSupport & EDID_DPMS_SUSPEND)
        printf("       <Property>Suspend</Property>\n");
    if (edid.dpmSupport & EDID_DPMS_STANDBY)
        printf("       <Property>Standby</Property>\n");
    printf("   </DisplayPowerManagementOptions>\n");

    printf("   <ColorCharacteristics>\n");
    printf("       <GreenRedXY>%d</GreenRedXY>\n", edid.redGreenLowBits);
    printf("       <WhiteBlueXY>%d</WhiteBlueXY>\n", edid.blueWhiteLowBits);
    printf("       <RedY>%d</RedY>\n", edid.redY);
    printf("       <RedX>%d</RedX>\n", edid.redX);
    printf("       <GreenY>%d</GreenY>\n", edid.greenY);
    printf("       <GreenX>%d</GreenX>\n", edid.greenX);
    printf("       <BlueY>%d</BlueY>\n", edid.blueY);
    printf("       <BlueX>%d</BlueX>\n", edid.blueX);
    printf("       <WhiteY>%d</WhiteY>\n", edid.whiteY);
    printf("       <WhiteX>%d</WhiteX>\n", edid.whiteX);
    printf("   </ColorCharacteristics>\n");

    printf("   <EstablishedTimings>\n");
    estmodes = (uint16_t)(edid.establishedTimings[0]);
    for (i = 0; i < 16; i++) {
       if (estmodes & (1 << i)) {
           printf("       <EstablishedTiming\">%s</EstablishedTiming> \n", edid_established_modes[i] );
       }
    }
    printf("   </EstablishedTimings>\n");

    printf("   <AvailableResolutions>\n");
    for (i = 0; i < 8; i++) {
        print_std_timing((uint8_t *)&(edid.standardTimings[2*i]));
    }
    printf("   </AvailableResolutions>\n");

    print_det_timing((uint8_t *)&(edid.descriptionBlock1[0]));

    print_description_block((uint8_t *)&(edid.descriptionBlock2), 2);
    print_description_block((uint8_t *)&(edid.descriptionBlock3), 3);
    print_description_block((uint8_t *)&(edid.descriptionBlock4), 4);

    printf("</Edid>\n");

    return 0;
}

int
read_edid( char *edid_file)
{
    FILE *file;

    file = fopen(edid_file, "rb");
    if (!file) {
        return(1);
    }

    if (fread(&edid, sizeof(uint8_t), EDID_LENGTH, file ) != EDID_LENGTH ) {
        return(2);
    }

    fclose(file);

    if (check_edid((uint8_t *)&edid))
        return(3);

    return(0);
}


static int
ftw_edid(const char *fpath, const struct stat *sb, int flag, struct FTW *ftwbuf)
{
    char filename[PATH_MAX];
    char buffer[50];
    int fd, n;

    if (fnmatch("/sys/class/drm/*/edid", fpath, 0) == 0) {
        strcpy(filename, dirname((char *)fpath));
        strcat(filename, "/status");

        fd = open(filename, O_RDONLY);
        if (fd) {
           n = read(fd, buffer, sizeof(buffer) -1);
           close(fd);

           if (!strncmp("connected", buffer, 9)) {
               strcpy(filename, fpath);
               strcat(filename, "/edid");
               if (!read_edid(filename))
                   return(print_edid());
           }
        }
    }

    return 0;
}


int
main(int argc, char** argv)
{
    nftw(EDID_SYS_PATH, ftw_edid, FTW_F, FTW_DEPTH);

    exit(0);
}


I am not going to try and explain the code to you. It would make the post far too long and tedious. If you are reading this post, I assume that you can already program in C and have a working knowledge of modern version of Linux. You can easily extend the code to retrieve and parse all possible permutations of EDID information.

Here is the output I got the digital monitor which is attached to the computer on which I am writing this blog.

<Edid version="1" revision="3">
   <VendorId>F022</VendorId>
   <VendorAbbreviation>HWP</VendorAbbreviation>
   <ProductId>26A3</ProductId>
   <ManufactureWeek>21</ManufactureWeek>
   <ManufactureYear>2008</ManufactureYear>
   <VideoInputDefinition>Analog</VideoInputDefinition>
   <Syncronization/>
   <VideoVoltageLevel>0.700V/0.300V (1.0 Vp-p)</VideoVoltageLevel>
   <SignalType>DigitalSignal</SignalType>
   <MaxHoriSize units="cm">41</MaxHoriSize>
   <MaxVertSize units="cm">26</MaxVertSize>
   <Gamma>2.200</Gamma>
   <DisplayType>RGB color</DisplayType>
   <DisplayPowerManagementOptions>
       <Property>ActiveOff/LowPower</Property>
       <Property>Suspend</Property>
       <Property>Standby</Property>
   </DisplayPowerManagementOptions>
   <ColorCharacteristics>
       <GreenRedXY>222</GreenRedXY>
       <WhiteBlueXY>149</WhiteBlueXY>
       <RedY>84</RedY>
       <RedX>163</RedX>
       <GreenY>153</GreenY>
       <GreenX>76</GreenX>
       <BlueY>15</BlueY>
       <BlueX>38</BlueX>
       <WhiteY>84</WhiteY>
       <WhiteX>80</WhiteX>
   </ColorCharacteristics>
   <EstablishedTimings>
       <EstablishedTiming">1280x1024 @ 75Hz</EstablishedTiming> 
       <EstablishedTiming">1024x768 @ 70Hz</EstablishedTiming> 
       <EstablishedTiming">832x624 @ 75Hz</EstablishedTiming> 
       <EstablishedTiming">800x600 @ 72Hz</EstablishedTiming> 
   </EstablishedTimings>
   <AvailableResolutions">
       <Resolution>
           <Mode>0</Mode>
           <XResolution>1440</XResolution>
           <YResolution>900</YResolution>
           <AspectRatio>0.625</AspectRatio>
           <VertRefreshFreq units="Hz">60</VertRefreshFreq>
       </Resolution>
       <Resolution>
           <Mode>1</Mode>
           <XResolution>1152</XResolution>
           <YResolution>720</YResolution>
           <AspectRatio>0.625</AspectRatio>
           <VertRefreshFreq units="Hz">60</VertRefreshFreq>
       </Resolution>
       <Resolution>
           <Mode>2</Mode>
           <XResolution>1280</XResolution>
           <YResolution>960</YResolution>
           <AspectRatio>0.750</AspectRatio>
           <VertRefreshFreq units="Hz">60</VertRefreshFreq>
       </Resolution>
       <Resolution>
           <Mode>3</Mode>
           <XResolution>1280</XResolution>
           <YResolution>1024</YResolution>
           <AspectRatio>0.800</AspectRatio>
           <VertRefreshFreq units="Hz">60</VertRefreshFreq>
       </Resolution>
   </AvailableResolutions>
  <DescriptorBlock number="1">
       <HoriFreq units="kHz">154</HoriFreq>
       <VertFreq units="Hz">41</VertFreq>
       <HoriActiveTimePixels>1440</HoriActiveTimePixels>
       <HoriBlankTimePixels>464</HoriBlankTimePixels>
       <HoriActiveTimeByHoriBlankingTime>81</HoriActiveTimeByHoriBlankingTime>
       <VertActiveTimeLines>900</VertActiveTimeLines>
       <VertBlankTimeLines>34</VertBlankTimeLines>
       <VertActiveTimeByVertBlankTime>48</VertActiveTimeByVertBlankTime>
       <HoriSyncOffsetPixels>80</HoriSyncOffsetPixels>
       <HoriSyncPulseWidthPixels>152</HoriSyncPulseWidthPixels>
       <VertSyncOffsetByVertSyncPulsewidth>54</VertSyncOffsetByVertSyncPulsewidth>
       <VertByHoriSyncOffsetByVertByHoriSyncPulsewidth">0</VertByHoriSyncOffsetByVertByHoriSyncPulsewidth>
       <HoriImageSize>408</HoriImageSize>
       <VertImageSize>255</VertImageSize>
       <HoriImgSizeByVertImgSize>16</HoriImgSizeByVertImgSize>
       <HoriBorder>0</HoriBorder>
       <VertBorder>0</VertBorder>
       <DisplayType">
           <Property>normal display (no stereo)</Property>
           <Property>sync digital separate</Property>
           <Property>PositiveVerticalSyncPolarity</Property>
           <Property>NegativeHorizontalSyncPolarity</Property>
       </DisplayType">
   </DescriptorBlock>
   <DescriptorBlock number="2">
       <MinVertRefreshFreq units="Hz">50</MinVertRefreshFreq>
       <MaxVertRefreshFreq units="Hz">76</MaxVertRefreshFreq>
       <MinHoriRefreshFreq units="KHz">24</MinHoriRefreshFreq>
       <MaxHoriRefreshFreq units="KHz">83</MaxHoriRefreshFreq>
   </DescriptorBlock>
   <DescriptorBlock number="3">
       <ModelNumber>HP w1907</ModelNumber>
   </DescriptorBlock>
   <DescriptorBlock number="4">
       <SerialNumber">3CQ8212KWJ</SerialNumber>
   </DescriptorBlock>
</Edid>


Feel free to use this code in your applications. I would however appreciate it if you would respect my copyright notice and include it at the top of the code.

No future versions of the EDID standard are planned. DisplayID is a VESA standard first published in 2009 which is intended to replace EDID. It features variable-length structures which encompass all existing EDID extensions as well as new extensions for 3D displays, embedded displays and more.

Comments are closed.