/*++ Copyright (c) 1990-2000 Microsoft Corporation Module Name: edid.c Abstract: This is the NT Video port Display Data Channel (DDC) code. It contains the implementations for the EDID industry standard Extended Display Identification Data manipulations. Author: Bruce McQuistan (brucemc) 23-Sept-1996 Environment: kernel mode only Notes: Based on VESA EDID Specification Version 2, April 9th, 1996 Revision History: 7/3/97 - brucemc. fixed some detailed timing decoding macros. 4/14/98 - brucemc. added support for version 3 (revision date 11/13/97). --*/ #include "videoprt.h" #include "pedid.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE,EdidCheckSum) #pragma alloc_text(PAGE,pVideoPortIsValidEDID) #pragma alloc_text(PAGE,pVideoPortGetEDIDId) #endif BOOLEAN EdidCheckSum( IN PCHAR pBlob, IN ULONG BlobSize ) { CHAR chk=0; ULONG i; for (i=0; iUC_OemIdentification[0]); else pTmp = &(((PEDID_V2)pEdid)->UC_Header[1]); pwChar[0] = 0x40 + ((pTmp[0] >> 2) & 0x1F); pwChar[1] = 0x40 + (((pTmp[0] << 3)|(pTmp[1] >> 5)) & 0x1F); pwChar[2] = 0x40 + (pTmp[1] & 0x1F) ; pwChar[3] = _hex[(pTmp[3] & 0xF0) >> 4]; pwChar[4] = _hex[(pTmp[3] & 0x0F)]; pwChar[5] = _hex[(pTmp[2] & 0xF0) >> 4]; pwChar[6] = _hex[(pTmp[2] & 0x0F)]; pwChar[7] = 0; } PVOID pVideoPortGetMonitordescription( PVOID pEdid) { PWSTR pStr = NULL; return NULL; } BOOLEAN pVideoPortIsValidEDID( PVOID pEdid ) { CHAR chk=0; UCHAR versionNumber, revisionNumber; ULONG i; ASSERT(pEdid); // // Version 1 EDID checking // if ((((UNALIGNED ULONG*)pEdid)[0] == 0xFFFFFF00) && (((UNALIGNED ULONG*)pEdid)[1] == 0x00FFFFFF)) { pVideoDebugPrint((1, " ***** Valid EDID1 header at %x\n", pEdid)); return EdidCheckSum(pEdid, 128); } // // EDID V2 support // versionNumber = ((PEDID_V2) pEdid)->UC_Header[0]; versionNumber >>= 4; revisionNumber = ((PEDID_V2) pEdid)->UC_Header[0]; revisionNumber &= 7; // // Note that the versionNumber cannot be 1 because then it would // have to be of the form above. // if (versionNumber != 2) { pVideoDebugPrint((1, " ***** invalid EDID2 header at %x\n", &((PEDID_V2) pEdid)->UC_Header[0])); return FALSE; } return EdidCheckSum(pEdid, 256); } BOOLEAN VideoPortIsMonitorDescriptor( IN PEDID_V1 Edid, IN ULONG BlockNumber ) /*++ Routine Description: Determines the Block is a VESA DDC compliant MonitorDescriptor. Arguments: Edid - pointer to an EDID BlockNumber - number indicating which block to query (1-4). Return Value: TRUE if the block is a VESA DDC compliant MonitorDescriptor. FALSE if the block is not a VESA DDC compliant MonitorDescriptor STATUS_INVALID_PARAMETER if the BlockNumber is invalid. --*/ { PMONITOR_DESCRIPTION pMonitorDesc; switch(BlockNumber) { default: pVideoDebugPrint((0, "Bogus DescriptorNumber\n")); return FALSE; case 1: pMonitorDesc = (PMONITOR_DESCRIPTION)GET_EDID_PDETAIL1(Edid); break; case 2: pMonitorDesc = (PMONITOR_DESCRIPTION)GET_EDID_PDETAIL2(Edid); break; case 3: pMonitorDesc = (PMONITOR_DESCRIPTION)GET_EDID_PDETAIL3(Edid); break; case 4: pMonitorDesc = (PMONITOR_DESCRIPTION)GET_EDID_PDETAIL4(Edid); break; } if ((pMonitorDesc->Flag1[0] == 0) && (pMonitorDesc->Flag1[1] == 0)) { return TRUE; } pVideoDebugPrint((1, " Not a monitordescriptor\n")); return FALSE; } NTSTATUS pVideoPortGetMonitorInfo( IN PMONITOR_DESCRIPTION MonitorDesc, OUT UCHAR Ascii[64] ) /*++ Routine Description: Helper routine for decoding a VESA DDC compliant Monitor Description (Detailed Timing). Arguments: MonitorDesc - Pointer to a MONITOR_DESCRIPTION extracted from the EDID. Ascii - Buffer to be filled in. Return Value: STATUS_SUCCESS if successful STATUS_INVALID_PARAMETER if there's nothing to decode. --*/ { PUCHAR pRanges = GET_MONITOR_RANGE_LIMITS(MonitorDesc); ULONG index; if (IS_MONITOR_DATA_SN(MonitorDesc) || IS_MONITOR_DATA_STRING(MonitorDesc) || IS_MONITOR_DATA_NAME(MonitorDesc) ) { // // find the things length. It ends in 0xa. // RtlCopyMemory(Ascii, pRanges, 13); for (index = 0; index < 13; ++index) { if (Ascii[index] == 0x0a) { Ascii[index] = (UCHAR)NULL; break; } } Ascii[index] = (UCHAR)NULL; return STATUS_SUCCESS; } return STATUS_INVALID_PARAMETER; } NTSTATUS VideoPortGetEdidMonitorDescription( IN PEDID_V1 Edid, IN ULONG DescriptorNumber, OUT UCHAR Ascii[64] ) /*++ Routine Description: Extracts VESA DDC compliant Monitor Descriptor indexed by DescriptorNumber and decodes it into the REGISTRY_MONITOR_DESCRIPTOR passed in by user. Arguments: Edid - Pointer to the a copy of the EDID from the monitors ROM. DescriptorNumber - a ULONG enumerating which Detailed Descriptor to decode. Ascii - Buffer to be filled in. Return Value: STATUS_SUCCESS or STATUS_INVALID_PARAMETER. --*/ { NTSTATUS retval; PMONITOR_DESCRIPTION pMonitorDesc; switch(DescriptorNumber) { default: pVideoDebugPrint((0, "Bogus DescriptorNumber\n")); return STATUS_INVALID_PARAMETER; case 1: pMonitorDesc = (PMONITOR_DESCRIPTION)GET_EDID_PDETAIL1(Edid); break; case 2: pMonitorDesc = (PMONITOR_DESCRIPTION)GET_EDID_PDETAIL2(Edid); break; case 3: pMonitorDesc = (PMONITOR_DESCRIPTION)GET_EDID_PDETAIL3(Edid); break; case 4: pMonitorDesc = (PMONITOR_DESCRIPTION)GET_EDID_PDETAIL4(Edid); break; } retval = pVideoPortGetMonitorInfo(pMonitorDesc, Ascii); return retval; } ULONG pVideoPortGetEdidOemID( IN PVOID pEdid, OUT PUCHAR pBuffer ) { ULONG count, versionNumber, revisionNumber, totalLength = 0; if ((((UNALIGNED ULONG*)pEdid)[0] == 0xFFFFFF00) && (((UNALIGNED ULONG*)pEdid)[1] == 0x00FFFFFF)) { PEDID_V1 pEdidV1 = (PEDID_V1)pEdid; for (count = 1; count < 5; ++count) { if (VideoPortIsMonitorDescriptor(pEdidV1, count)) { if (STATUS_SUCCESS == VideoPortGetEdidMonitorDescription(pEdidV1, count, &(pBuffer[totalLength]))) { totalLength += strlen(&(pBuffer[totalLength])); pBuffer[totalLength] = '_'; } } // // NULL terminate it. // pBuffer[totalLength] = (UCHAR) NULL; } return totalLength; } // // EDID V2 support // versionNumber = ((PEDID_V2) pEdid)->UC_Header[0]; versionNumber >>= 4; revisionNumber = ((PEDID_V2) pEdid)->UC_Header[0]; revisionNumber &= 7; // // Note that the versionNumber cannot be 1 because then it would // have to be of the form above. // if (versionNumber != 2) { pVideoDebugPrint((1, " ***** invalid EDID2 header at %x\n", &((PEDID_V2) pEdid)->UC_Header[0])); return 0; } else { PEDID_V2 pEdidV2 = (PEDID_V2)pEdid; // // This string has ascii code 0x9 delineating the // manufacturers name and terminating with 0xa. Replace these // with '_' and NULL respectively. // memcpy(pBuffer, pEdidV2->UC_OemIdentification, 32); for(count = 0; count < 32; ++count) { if (pBuffer[count] == 0x9) { pBuffer[count] = '_'; continue; } if (pBuffer[count] == 0xa) break; } pBuffer[count] = (UCHAR)NULL; } return (count + 1); }