windows-nt/Source/XPSP1/NT/drivers/video/ms/port/edid.c
2020-09-26 16:20:57 +08:00

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);
}