405 lines
8.7 KiB
C
405 lines
8.7 KiB
C
/*++
|
|
|
|
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; i<BlobSize; i++)
|
|
chk = (CHAR)(chk + ((CHAR*)pBlob)[i]);
|
|
|
|
if (chk != 0)
|
|
{
|
|
pVideoDebugPrint((0, " ***** invalid EDID chksum at %x\n", pBlob));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
VOID
|
|
pVideoPortGetEDIDId(
|
|
PVOID pEdid,
|
|
PWCHAR pwChar
|
|
)
|
|
{
|
|
WCHAR _hex[] = L"0123456789ABCDEF";
|
|
PUCHAR pTmp;
|
|
|
|
if ((((UNALIGNED ULONG*)pEdid)[0] == 0xFFFFFF00) &&
|
|
(((UNALIGNED ULONG*)pEdid)[1] == 0x00FFFFFF))
|
|
pTmp = &(((PEDID_V1)pEdid)->UC_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);
|
|
|
|
}
|