1447 lines
44 KiB
C
1447 lines
44 KiB
C
/*++
|
|
|
|
Copyright (c) 1990-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
enum.c
|
|
|
|
Abstract:
|
|
|
|
This is the NT Video port driver PnP enumeration support routines.
|
|
|
|
Author:
|
|
|
|
Bruce McQuistan (brucemc) Feb. 1997
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "videoprt.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,pVideoPnPCapabilities)
|
|
#pragma alloc_text(PAGE,pVideoPnPResourceRequirements)
|
|
#pragma alloc_text(PAGE,pVideoPnPQueryId)
|
|
#pragma alloc_text(PAGE,VpAddPdo)
|
|
#pragma alloc_text(PAGE,pVideoPortEnumerateChildren)
|
|
#pragma alloc_text(PAGE,pVideoPortCleanUpChildList)
|
|
#endif
|
|
|
|
const WCHAR gcwstrDosDeviceName[] = L"\\DosDevices\\LCD";
|
|
|
|
|
|
NTSTATUS
|
|
pVideoPnPCapabilities(
|
|
IN PCHILD_PDO_EXTENSION PdoExtension,
|
|
IN PDEVICE_CAPABILITIES Capabilities
|
|
)
|
|
/*+
|
|
* Function: pVideoPnPCapabilities
|
|
* Context: Called in the context of an IRP_MN_QUERY_CAPABILITIES minor function
|
|
* and IRP_MJ_PNP major function.
|
|
*
|
|
* Arguments: PDEVICE_EXTENSION DeviceExtension - a pointer to a
|
|
* CHILD_DEVICE_EXTENSION.
|
|
*
|
|
* PDEVICE_CAPABILITIES Capabilities - a pointer to the
|
|
* Parameters.DeviceCapabilities.Capabilities of the IrpStack.
|
|
*
|
|
*
|
|
* Comments: This routine fills in some capabilities data needed by the
|
|
* PnP device manager.
|
|
*
|
|
-*/
|
|
{
|
|
BOOLEAN success ;
|
|
DEVICE_POWER_STATE unused ;
|
|
UCHAR count ;
|
|
|
|
//
|
|
// Make sure we're dealing with PDOs here.
|
|
//
|
|
|
|
ASSERT(IS_PDO(PdoExtension));
|
|
|
|
if (!pVideoPortMapStoD(PdoExtension,
|
|
PowerSystemSleeping1,
|
|
&unused)) {
|
|
|
|
return STATUS_UNSUCCESSFUL ;
|
|
}
|
|
|
|
for (count = PowerSystemUnspecified; count < PowerSystemMaximum; count++) {
|
|
Capabilities->DeviceState[count] = PdoExtension->DeviceMapping[count] ;
|
|
}
|
|
|
|
//
|
|
// Check to make sure that the monitor will actually get turned off
|
|
// in sleep states.
|
|
//
|
|
|
|
if (Capabilities->DeviceState[PowerSystemSleeping1] == PowerDeviceD0) {
|
|
Capabilities->DeviceState[PowerSystemSleeping1] = PowerDeviceD1 ;
|
|
PdoExtension->DeviceMapping[PowerSystemSleeping1] =
|
|
PowerDeviceD1 ;
|
|
pVideoDebugPrint((0, "VideoPrt: QC - Override D0 for sleep on monitor.\n")) ;
|
|
}
|
|
|
|
PdoExtension->IsMappingReady = TRUE ;
|
|
|
|
//
|
|
// Begin with basic capabilities for the PDO
|
|
//
|
|
|
|
Capabilities->LockSupported = FALSE;
|
|
Capabilities->EjectSupported = FALSE;
|
|
Capabilities->Removable = FALSE;
|
|
Capabilities->DockDevice = FALSE;
|
|
|
|
//
|
|
// Set the Raw bit to TRUE only for monitor like objects, since we
|
|
// act as drivers for them.
|
|
//
|
|
|
|
Capabilities->RawDeviceOK = FALSE;
|
|
|
|
if (PdoExtension->VideoChildDescriptor->Type == Monitor) {
|
|
Capabilities->RawDeviceOK = TRUE;
|
|
Capabilities->EjectSupported = TRUE;
|
|
Capabilities->Removable = TRUE;
|
|
Capabilities->SurpriseRemovalOK = TRUE;
|
|
Capabilities->SilentInstall = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Out address field contains the ID returned during enumeration.
|
|
// This is key for ACPI devices in order for ACPI to install the
|
|
// filter properly.
|
|
//
|
|
|
|
Capabilities->Address = PdoExtension->VideoChildDescriptor->UId;
|
|
|
|
//
|
|
// We do not generate unique IDs for our devices because we maight have
|
|
// two video cards with monitors attached, for which the driver would
|
|
// end up returning the same ID.
|
|
//
|
|
|
|
Capabilities->UniqueID = FALSE;
|
|
|
|
|
|
//
|
|
// The following are totally BOGUS.
|
|
//
|
|
|
|
Capabilities->SystemWake = PowerSystemUnspecified;
|
|
Capabilities->DeviceWake = PowerDeviceUnspecified;
|
|
|
|
Capabilities->D1Latency = 10;
|
|
Capabilities->D2Latency = 10;
|
|
Capabilities->D3Latency = 10;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
pVideoPnPResourceRequirements(
|
|
IN PCHILD_PDO_EXTENSION PdoExtension,
|
|
OUT PCM_RESOURCE_LIST * ResourceList
|
|
)
|
|
/*+
|
|
* Function: pVideoPnPResourceRequirements
|
|
* Context: Called in the context of an IRP_MN_QUERY_RESOURCE_REQUIREMENTS
|
|
* minor function and IRP_MJ_PNP major function.
|
|
* Arguments: PDEVICE_EXTENSION PdoExtension - a pointer to a CHILD_DEVICE_EXTENSION.
|
|
* PCM_RESOURCE_LIST * ResourceList - a pointer to the IRPs
|
|
* IoStatus.Information
|
|
*
|
|
* Comments: This routine tells the PnP device manager that the child
|
|
* devices (monitors) don't need system resources. This may not
|
|
* be the case for all child devices.
|
|
*
|
|
-*/
|
|
{
|
|
PVIDEO_CHILD_DESCRIPTOR pChildDescriptor;
|
|
|
|
//
|
|
// Make sure we're dealing with PDOs here.
|
|
//
|
|
|
|
ASSERT(IS_PDO(PdoExtension));
|
|
|
|
//
|
|
// Get the child descriptor allocated during Enumerate phase.
|
|
//
|
|
|
|
pChildDescriptor = PdoExtension->VideoChildDescriptor;
|
|
|
|
//
|
|
// If the descriptor is null, then there are serious problems.
|
|
//
|
|
|
|
ASSERT(pChildDescriptor);
|
|
|
|
switch (pChildDescriptor->Type) {
|
|
|
|
default:
|
|
|
|
//
|
|
// Monitors don't need pci resources.
|
|
//
|
|
|
|
case Monitor:
|
|
|
|
*ResourceList = NULL;
|
|
break;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN pGetACPIEdid(PDEVICE_OBJECT DeviceObject, PVOID pEdid)
|
|
/*+
|
|
* Function: pGetACPIEdid
|
|
* Return Value:
|
|
* TRUE: Success
|
|
* FALSE: Failure
|
|
-*/
|
|
{
|
|
UCHAR EDIDBuffer[sizeof(ACPI_EVAL_OUTPUT_BUFFER) + EDID_BUFFER_SIZE];
|
|
ULONG EdidVersion = 2;
|
|
BOOLEAN bReturn = FALSE;
|
|
|
|
RtlZeroMemory(EDIDBuffer, sizeof(EDIDBuffer));
|
|
|
|
if (NT_SUCCESS (pVideoPortACPIIoctl(IoGetAttachedDevice(DeviceObject),
|
|
(ULONG) ('CDD_'),
|
|
&EdidVersion,
|
|
NULL,
|
|
sizeof(EDIDBuffer),
|
|
(PACPI_EVAL_OUTPUT_BUFFER) EDIDBuffer) )
|
|
)
|
|
{
|
|
ASSERT(((PACPI_EVAL_OUTPUT_BUFFER)EDIDBuffer)->Argument[0].Type == ACPI_METHOD_ARGUMENT_BUFFER);
|
|
ASSERT(((PACPI_EVAL_OUTPUT_BUFFER)EDIDBuffer)->Argument[0].DataLength <= EDID_BUFFER_SIZE);
|
|
bReturn = TRUE;
|
|
}
|
|
RtlCopyMemory(pEdid,
|
|
((PACPI_EVAL_OUTPUT_BUFFER)EDIDBuffer)->Argument[0].Data,
|
|
EDID_BUFFER_SIZE);
|
|
return bReturn;
|
|
}
|
|
|
|
#define TOTAL_NAMES_SIZE 512
|
|
|
|
NTSTATUS
|
|
pVideoPnPQueryId(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN BUS_QUERY_ID_TYPE BusQueryIdType,
|
|
IN OUT PWSTR * BusQueryId
|
|
)
|
|
/*+
|
|
* Function: pVideoPnPQueryId
|
|
* Context: Called in the context of an IRP_MN_QUERY_ID minor function
|
|
* and IRP_MJ_PNP major function.
|
|
* Arguments: DeviceObject - a PDEVICE_OBJECT created when we enumerated
|
|
* the child device.
|
|
* BusQueryIdType - a BUS_QUERY_ID_TYPE passed in by the PnP
|
|
* device Manager.
|
|
* BusQueryId - a PWSTR * written to in some cases by this
|
|
* routine.
|
|
*
|
|
* Comments:
|
|
*
|
|
-*/
|
|
{
|
|
PUSHORT nameBuffer;
|
|
LPWSTR deviceName;
|
|
WCHAR buffer[64];
|
|
PCHILD_PDO_EXTENSION pDeviceExtension;
|
|
PVIDEO_CHILD_DESCRIPTOR pChildDescriptor;
|
|
PVOID pEdid;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Allocate enought to hold a MULTI_SZ. This will be passed to the Io
|
|
// subsystem (via BusQueryId) who has the responsibility of freeing it.
|
|
//
|
|
|
|
nameBuffer = ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
|
|
TOTAL_NAMES_SIZE,
|
|
VP_TAG);
|
|
|
|
if (!nameBuffer)
|
|
{
|
|
pVideoDebugPrint((0, "\t Can't allocate nameBuffer\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(nameBuffer, TOTAL_NAMES_SIZE);
|
|
|
|
//
|
|
// Get the child descriptor allocated during Enumerate phase.
|
|
//
|
|
|
|
pDeviceExtension = (PCHILD_PDO_EXTENSION) DeviceObject->DeviceExtension;
|
|
pChildDescriptor = pDeviceExtension->VideoChildDescriptor;
|
|
|
|
//
|
|
// If the descriptor is null, then there are serious problems.
|
|
//
|
|
|
|
ASSERT(pChildDescriptor);
|
|
|
|
//
|
|
// Setup pEdid.
|
|
//
|
|
|
|
pEdid = &(pChildDescriptor->Buffer);
|
|
|
|
//
|
|
// Switch on the type to set up the strings appropriately. This switch
|
|
// generates the UNICODE_STRING deviceName, used by HardwareID and
|
|
// DeviceID bus queries.
|
|
//
|
|
|
|
switch(pChildDescriptor->Type) {
|
|
|
|
case Monitor:
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// Get the EDID if this is an ACPI device.
|
|
/////////////////////////////////////////////////////////
|
|
|
|
pChildDescriptor->ValidEDID = pVideoPortIsValidEDID(pEdid) ? GOOD_EDID : BAD_EDID;
|
|
|
|
if (pChildDescriptor->bACPIDevice == TRUE)
|
|
{
|
|
if (pChildDescriptor->ACPIDDCFlag & ACPIDDC_TESTED)
|
|
{
|
|
if (pChildDescriptor->ValidEDID != GOOD_EDID &&
|
|
(pChildDescriptor->ACPIDDCFlag & ACPIDDC_EXIST) )
|
|
{
|
|
pGetACPIEdid(DeviceObject, pEdid);
|
|
pChildDescriptor->ValidEDID = pVideoPortIsValidEDID(pEdid) ? GOOD_EDID : BAD_EDID;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we found Miniport gets a right EDID, it's equivalent to that _DDC method doesn't exist
|
|
//
|
|
pChildDescriptor->ACPIDDCFlag = ACPIDDC_TESTED;
|
|
if (pChildDescriptor->ValidEDID != GOOD_EDID &&
|
|
pGetACPIEdid(DeviceObject, pEdid))
|
|
{
|
|
pChildDescriptor->ACPIDDCFlag |= ACPIDDC_EXIST;
|
|
pChildDescriptor->ValidEDID = pVideoPortIsValidEDID(pEdid) ? GOOD_EDID : BAD_EDID;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there's an EDID, decode it's OEM id. Otherwise, use
|
|
// default.
|
|
//
|
|
|
|
if (pChildDescriptor->ValidEDID == GOOD_EDID) {
|
|
|
|
pVideoDebugPrint((1, "\tNot a bogus edid\n"));
|
|
|
|
pVideoPortGetEDIDId(pEdid, buffer);
|
|
|
|
deviceName = buffer;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Use the passed in default name.
|
|
//
|
|
|
|
deviceName = L"Default_Monitor";
|
|
}
|
|
|
|
break;
|
|
|
|
case Other:
|
|
|
|
deviceName = (LPWSTR) pEdid;
|
|
break;
|
|
|
|
default:
|
|
|
|
pVideoDebugPrint((0, "\t Unsupported Type: %x\n", pChildDescriptor->Type));
|
|
ASSERT(FALSE);
|
|
deviceName = L"Unknown_Video_Device";
|
|
|
|
break;
|
|
}
|
|
|
|
pVideoDebugPrint((2, "\t The basic deviceName is %ws\n", deviceName));
|
|
|
|
//
|
|
// Craft a name dependent on what was passed in.
|
|
//
|
|
|
|
switch (BusQueryIdType) {
|
|
|
|
case BusQueryCompatibleIDs:
|
|
|
|
//
|
|
// Compatible ID used for INF matching.
|
|
//
|
|
|
|
pVideoDebugPrint((2, "\t BusQueryCompatibleIDs\n"));
|
|
|
|
if (pChildDescriptor->Type != Monitor) {
|
|
|
|
swprintf(nameBuffer, L"DISPLAY\\%ws", deviceName);
|
|
pVideoDebugPrint((2, "\t BusQueryCompatibleIDs = %ws", nameBuffer));
|
|
|
|
} else {
|
|
|
|
//
|
|
// Put the default PNP id for monitors.
|
|
//
|
|
|
|
swprintf(nameBuffer, L"*PNP09FF");
|
|
pVideoDebugPrint((2, "\t BusQueryCompatibleIDs = %ws", nameBuffer));
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case BusQueryHardwareIDs:
|
|
|
|
pVideoDebugPrint((2, "\t BusQueryHardwareIDs\n"));
|
|
|
|
//
|
|
// By this time, the keys should have been created, so write
|
|
// the data to the registry. In this case the data is a string
|
|
// that looks like '\Monitor\<string>' where string is either
|
|
// 'Default_Monitor' or a name extracted from the edid.
|
|
//
|
|
|
|
if (pChildDescriptor->Type == Monitor) {
|
|
|
|
//
|
|
// Write the DDC information in the DEVICE part of the
|
|
// registry (the part under ENUM\DISPLAY\*)
|
|
//
|
|
|
|
HANDLE hDeviceKey;
|
|
NTSTATUS Status;
|
|
|
|
Status = IoOpenDeviceRegistryKey(DeviceObject,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
MAXIMUM_ALLOWED,
|
|
&hDeviceKey);
|
|
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
|
|
hDeviceKey,
|
|
(pChildDescriptor->ValidEDID == GOOD_EDID) ?
|
|
L"EDID" : L"BAD_EDID",
|
|
REG_BINARY,
|
|
pEdid,
|
|
EDID_BUFFER_SIZE);
|
|
|
|
ZwClose(hDeviceKey);
|
|
}
|
|
|
|
swprintf(nameBuffer, L"Monitor\\%ws", deviceName);
|
|
|
|
} else {
|
|
|
|
swprintf(nameBuffer, L"DISPLAY\\%ws", deviceName);
|
|
}
|
|
|
|
pVideoDebugPrint((2, "\t BusQueryHardwareIDs = %ws\n", nameBuffer));
|
|
|
|
break;
|
|
|
|
|
|
case BusQueryDeviceID:
|
|
|
|
//
|
|
// Device ID (top part of the ID)
|
|
//
|
|
|
|
pVideoDebugPrint((2, "\t BusQueryDeviceID\n"));
|
|
|
|
swprintf(nameBuffer, L"DISPLAY\\%ws", deviceName);
|
|
|
|
pVideoDebugPrint((2, "\t BusQueryDeviceID = %ws", nameBuffer));
|
|
|
|
break;
|
|
|
|
|
|
case BusQueryInstanceID:
|
|
|
|
//
|
|
// Instance ID (low part of the ID)
|
|
//
|
|
|
|
pVideoDebugPrint((2, "\t BusQueryInstanceID\n"));
|
|
|
|
swprintf(nameBuffer, L"%08x&%02x&%02x", pChildDescriptor->UId,
|
|
pDeviceExtension->pFdoExtension->SystemIoBusNumber,
|
|
pDeviceExtension->pFdoExtension->SlotNumber);
|
|
|
|
pVideoDebugPrint((2, "\t BusQueryInstanceID = %ws", nameBuffer));
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
pVideoDebugPrint((0, "\t Bad QueryIdType:%x\n", BusQueryIdType));
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
break;
|
|
|
|
}
|
|
|
|
pVideoDebugPrint((2, "\t returning %ws\n", nameBuffer));
|
|
|
|
*BusQueryId = nameBuffer;
|
|
|
|
return ntStatus;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
VpAddPdo(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PVIDEO_CHILD_DESCRIPTOR VideoChildDescriptor
|
|
)
|
|
/*+
|
|
* Function: VpAddPdo
|
|
* Context: Called after enumerating a device identified by the miniports
|
|
* HwGetVideoChildDescriptor.
|
|
*
|
|
* Arguments: DeviceObject - a PDEVICE_OBJECT created when we
|
|
* enumerated the device.
|
|
* VideoChildDescriptor - a PVIDEO_CHILD_DESCRIPTOR allocated
|
|
* when we enumerated the device.
|
|
* Comments: This routine actually makes the call that creates the child
|
|
* device object during enumeration.
|
|
*
|
|
*
|
|
-*/
|
|
|
|
{
|
|
PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
|
PCHILD_PDO_EXTENSION pChildDeviceExtension = fdoExtension->ChildPdoList;
|
|
PDEVICE_OBJECT pChildPdo;
|
|
USHORT nameBuffer[STRING_LENGTH];
|
|
NTSTATUS ntStatus;
|
|
NTSTATUS ntStatus2;
|
|
UNICODE_STRING deviceName;
|
|
POWER_STATE state;
|
|
PVOID pEdid = VideoChildDescriptor->Buffer;
|
|
UNICODE_STRING symbolicLinkName;
|
|
|
|
//
|
|
// Scan the old list to see if this is a duplicate. If a duplicate, mark it as
|
|
// VIDEO_ENUMERATED and return STATUS_SUCCESS, because we will want to count it.
|
|
// If no duplicates, pChildDeviceExtension will be NULL after exiting this loop
|
|
// and a DEVICE_OBJECT will be created for this device instance. Mark the new
|
|
// associated CHILD_DEVICE_EXTENSION as VIDEO_ENUMERATED.
|
|
//
|
|
|
|
while (pChildDeviceExtension) {
|
|
|
|
PVIDEO_CHILD_DESCRIPTOR ChildDescriptor;
|
|
BOOLEAN bEqualEDID = FALSE;
|
|
|
|
ChildDescriptor = pChildDeviceExtension->VideoChildDescriptor;
|
|
|
|
if (ChildDescriptor->UId == VideoChildDescriptor->UId)
|
|
{
|
|
if (ChildDescriptor->bACPIDevice == TRUE)
|
|
{
|
|
VideoChildDescriptor->ACPIDDCFlag = ChildDescriptor->ACPIDDCFlag;
|
|
|
|
//
|
|
// If it's non-monitor device, just ignore
|
|
//
|
|
if (VideoChildDescriptor->Type != Monitor)
|
|
{
|
|
bEqualEDID = TRUE;
|
|
}
|
|
//
|
|
// Check the device is active, since inactive CRT may return false EDID
|
|
//
|
|
else if (pCheckActiveMonitor(pChildDeviceExtension) == FALSE)
|
|
{
|
|
bEqualEDID = TRUE;
|
|
}
|
|
else
|
|
{
|
|
VideoChildDescriptor->ValidEDID =
|
|
pVideoPortIsValidEDID(VideoChildDescriptor->Buffer) ? GOOD_EDID : BAD_EDID;
|
|
|
|
//
|
|
// For ACPI system, try to retrieve EDID again.
|
|
// At this moment, the handle of DeviceObject is still valid
|
|
//
|
|
if (VideoChildDescriptor->ValidEDID != GOOD_EDID &&
|
|
(ChildDescriptor->ACPIDDCFlag & ACPIDDC_EXIST))
|
|
{
|
|
if (!pGetACPIEdid(pChildDeviceExtension->ChildDeviceObject,
|
|
VideoChildDescriptor->Buffer))
|
|
{
|
|
bEqualEDID = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!bEqualEDID)
|
|
{
|
|
VideoChildDescriptor->ValidEDID =
|
|
pVideoPortIsValidEDID(VideoChildDescriptor->Buffer) ? GOOD_EDID : BAD_EDID;
|
|
|
|
if (VideoChildDescriptor->ValidEDID == ChildDescriptor->ValidEDID)
|
|
{
|
|
if (VideoChildDescriptor->ValidEDID != GOOD_EDID ||
|
|
memcmp(ChildDescriptor->Buffer,
|
|
VideoChildDescriptor->Buffer,
|
|
EDID_BUFFER_SIZE) == 0)
|
|
{
|
|
bEqualEDID = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// For non-ACPI system, EDID has already contained VideoChildDescriptor
|
|
//
|
|
else
|
|
{
|
|
if (VideoChildDescriptor->Type != Monitor ||
|
|
ChildDescriptor->ValidEDID != GOOD_EDID ||
|
|
memcmp(ChildDescriptor->Buffer,
|
|
VideoChildDescriptor->Buffer,
|
|
EDID_BUFFER_SIZE) == 0)
|
|
{
|
|
bEqualEDID = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bEqualEDID)
|
|
{
|
|
pChildDeviceExtension->bIsEnumerated = TRUE;
|
|
pVideoDebugPrint((1,
|
|
"VpAddPdo: duplicate device:%x\n",
|
|
VideoChildDescriptor->UId));
|
|
|
|
//
|
|
// Replace the old child descriptor with the new one. This will
|
|
// allow us to detect when and EDID changes, etc.
|
|
//
|
|
if (pChildDeviceExtension->VideoChildDescriptor->ValidEDID != NO_EDID)
|
|
{
|
|
RtlCopyMemory(VideoChildDescriptor,
|
|
pChildDeviceExtension->VideoChildDescriptor,
|
|
sizeof(VIDEO_CHILD_DESCRIPTOR) );
|
|
}
|
|
ExFreePool(pChildDeviceExtension->VideoChildDescriptor);
|
|
pChildDeviceExtension->VideoChildDescriptor = VideoChildDescriptor;
|
|
|
|
//
|
|
// Return STATUS_SUCCESS, because we want to count this as a member of the
|
|
// list (it's valid and in there already).
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
pChildDeviceExtension = pChildDeviceExtension->NextChild;
|
|
}
|
|
|
|
ntStatus = pVideoPortCreateDeviceName(L"\\Device\\VideoPdo",
|
|
VideoChildDevices++,
|
|
&deviceName,
|
|
nameBuffer);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
//
|
|
// Create the PDO for the child device.
|
|
// Notice that we allocate the device extension as the size of
|
|
// the FDO extension + the size of the miniports driver extension
|
|
// for this device
|
|
//
|
|
|
|
ntStatus = IoCreateDevice(DeviceObject->DriverObject,
|
|
sizeof(CHILD_PDO_EXTENSION),
|
|
&deviceName,
|
|
FILE_DEVICE_UNKNOWN,
|
|
0,
|
|
FALSE, //TRUE,
|
|
&pChildPdo);
|
|
|
|
//
|
|
// If the DeviceObject wasn't created, we won't get called to process
|
|
// the QueryId IRP where VideoChildDescriptor gets freed, so do it
|
|
// here.
|
|
//
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
|
|
pVideoDebugPrint((0, "\t IoCreateDevice() failed with status %x\n", ntStatus));
|
|
pVideoDebugPrint((0, "\t IoCreateDevice() doesn't like path %ws\n", deviceName.Buffer));
|
|
ASSERT(0);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Create a symbolic link so that user can call us. This was added
|
|
// to support new ACPI backlight methods. We will not fail VpAddPdo
|
|
// if IoCreateSymbolicLink fails.
|
|
//
|
|
|
|
RtlInitUnicodeString(&symbolicLinkName,
|
|
gcwstrDosDeviceName);
|
|
|
|
ntStatus2 = IoCreateSymbolicLink(&symbolicLinkName,
|
|
&deviceName);
|
|
|
|
//
|
|
// Mark this object as supporting buffered I/O so that the I/O system
|
|
// will only supply simple buffers in IRPs.
|
|
// Set and clear the two power fields to ensure we only get called
|
|
// as passive level to do power management operations.
|
|
//
|
|
|
|
pChildPdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
|
|
pChildPdo->Flags &= ~(DO_DEVICE_INITIALIZING | DO_POWER_INRUSH);
|
|
pChildPdo->DeviceType = FILE_DEVICE_SCREEN;
|
|
|
|
//
|
|
// Initialize fields in the ChildDeviceExtension.
|
|
//
|
|
|
|
pChildDeviceExtension = pChildPdo->DeviceExtension;
|
|
|
|
pChildDeviceExtension->VideoChildDescriptor = VideoChildDescriptor;
|
|
pChildDeviceExtension->ChildDeviceObject = pChildPdo;
|
|
pChildDeviceExtension->pFdoExtension = fdoExtension;
|
|
pChildDeviceExtension->Signature = VP_TAG;
|
|
pChildDeviceExtension->ExtensionType = TypePdoExtension;
|
|
pChildDeviceExtension->ChildUId = VideoChildDescriptor->UId;
|
|
pChildDeviceExtension->bIsEnumerated = TRUE;
|
|
pChildDeviceExtension->HwDeviceExtension = fdoExtension->HwDeviceExtension;
|
|
pChildDeviceExtension->PowerOverride = FALSE;
|
|
|
|
KeInitializeMutex(&pChildDeviceExtension->SyncMutex, 0);
|
|
|
|
//
|
|
// Initialize the remove lock.
|
|
//
|
|
|
|
IoInitializeRemoveLock(&pChildDeviceExtension->RemoveLock, 0, 0, 256);
|
|
|
|
//
|
|
// Initialize Power stuff.
|
|
// Set the devices current power state.
|
|
// NOTE - we assume the device is on at this point in time ...
|
|
//
|
|
|
|
pChildDeviceExtension->DevicePowerState = PowerDeviceD0;
|
|
|
|
state.DeviceState = pChildDeviceExtension->DevicePowerState;
|
|
|
|
state = PoSetPowerState(pChildPdo,
|
|
DevicePowerState,
|
|
state);
|
|
|
|
|
|
//
|
|
// Insert into list
|
|
//
|
|
|
|
pChildDeviceExtension->NextChild = fdoExtension->ChildPdoList;
|
|
fdoExtension->ChildPdoList = pChildDeviceExtension;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
pVideoPortEnumerateChildren(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
/*+
|
|
* Function: pVideoPortEnumerateChildren
|
|
* Context: Called in the context of an IRP_MN_QUERY_DEVICE_RELATIONS
|
|
* minor function and IRP_MJ_PNP major function.
|
|
* Arguments:
|
|
* PDEVICE_OBJECT deviceObject - Passed in by caller of VideoPortDispatch().
|
|
* PIRP pIrp - Passed in by caller of VideoPortDispatch().
|
|
*
|
|
* Comments: This routine enumerates devices attached to the video card. If
|
|
* it's called before the driver is initialized, it returns
|
|
* STATUS_INSUFFICIENT_RESOURCES. Otherwise, it attempts to read
|
|
* the edid from the device and refer to that via the
|
|
* DEVICE_EXTENSION and create a DEVICE_OBJECT (PDO) for each
|
|
* detected device. This sets up the IO subsystem for issuing
|
|
* further PnP IRPs such as IRP_MN_QUERY_DEVICE_ID
|
|
*
|
|
*
|
|
-*/
|
|
|
|
{
|
|
UCHAR outputBuffer[sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 128];
|
|
PCHILD_PDO_EXTENSION pChildDeviceExtension;
|
|
PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
|
ULONG moreChild;
|
|
ULONG moreDevices = 1;
|
|
ULONG Unused = 0;
|
|
ULONG count = 0;
|
|
PACPI_METHOD_ARGUMENT pAcpiArguments = NULL;
|
|
VIDEO_CHILD_ENUM_INFO childEnumInfo;
|
|
ULONG relationsSize;
|
|
PDEVICE_RELATIONS deviceRelations = NULL;
|
|
ULONG ulChildCount = 0;
|
|
ULONG debugCount = 0;
|
|
PDEVICE_OBJECT *pdo;
|
|
NTSTATUS ntStatus;
|
|
|
|
//
|
|
// Make sure we are called with an FDO
|
|
//
|
|
|
|
ASSERT(IS_FDO(fdoExtension));
|
|
|
|
if ((fdoExtension->AllowEarlyEnumeration == FALSE) &&
|
|
(fdoExtension->HwInitStatus != HwInitSucceeded))
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Mark all of the child devices as not being enumerated
|
|
//
|
|
|
|
for (pChildDeviceExtension = fdoExtension->ChildPdoList;
|
|
pChildDeviceExtension != NULL;
|
|
pChildDeviceExtension = pChildDeviceExtension->NextChild)
|
|
{
|
|
pChildDeviceExtension->bIsEnumerated = FALSE;
|
|
}
|
|
|
|
//
|
|
// Let's call ACPI to determine if we have the IDs of the devices that
|
|
// need to be enumerated.
|
|
//
|
|
|
|
ntStatus = pVideoPortACPIIoctl(fdoExtension->AttachedDeviceObject,
|
|
(ULONG) ('DOD_'),
|
|
NULL,
|
|
NULL,
|
|
sizeof(outputBuffer),
|
|
(PACPI_EVAL_OUTPUT_BUFFER) outputBuffer);
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
count = ((PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)->Count;
|
|
pAcpiArguments = &(((PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)->Argument[0]);
|
|
}
|
|
|
|
|
|
childEnumInfo.Size = sizeof(VIDEO_CHILD_ENUM_INFO);
|
|
childEnumInfo.ChildDescriptorSize = EDID_BUFFER_SIZE;
|
|
childEnumInfo.ChildIndex = 0;
|
|
childEnumInfo.ACPIHwId = 0;
|
|
childEnumInfo.ChildHwDeviceExtension = NULL;
|
|
|
|
//
|
|
// Call the miniport to enumerate the children
|
|
// Keep calling for each ACPI device, and then call the driver if it
|
|
// has any more devices.
|
|
//
|
|
|
|
while (moreDevices)
|
|
{
|
|
PVIDEO_CHILD_DESCRIPTOR pVideoChildDescriptor;
|
|
|
|
//
|
|
// Allocate Space for the Child Descriptor
|
|
//
|
|
|
|
pVideoChildDescriptor = ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
|
|
sizeof(VIDEO_CHILD_DESCRIPTOR),
|
|
VP_TAG);
|
|
|
|
if (!pVideoChildDescriptor)
|
|
{
|
|
break;
|
|
}
|
|
|
|
RtlZeroMemory(pVideoChildDescriptor, sizeof(VIDEO_CHILD_DESCRIPTOR));
|
|
|
|
//
|
|
// On ACPI machine, the HwId contains the ID returned by ACPI
|
|
// Otherwise, the value is initialized to NULL and the miniport driver
|
|
// must fill it out
|
|
//
|
|
|
|
if (count)
|
|
{
|
|
ASSERT(pAcpiArguments->Type == 0);
|
|
ASSERT(pAcpiArguments->DataLength == 4);
|
|
|
|
// The lower 16bit are HWID
|
|
childEnumInfo.ACPIHwId = pAcpiArguments->Argument & 0x0000FFFF;
|
|
pVideoChildDescriptor->bACPIDevice = TRUE;
|
|
|
|
pAcpiArguments++;
|
|
count--;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Increment the child index for non-ACPI devices
|
|
//
|
|
|
|
childEnumInfo.ChildIndex++;
|
|
childEnumInfo.ACPIHwId = 0;
|
|
}
|
|
|
|
//
|
|
// For ACPI CRTs, Miniport should return EDID directly.
|
|
// So for CRT, the buffer is garanteed to be overwriten.
|
|
// We use this attibute to distinguish the CRT from LCD and TV.
|
|
//
|
|
if (pVideoChildDescriptor->bACPIDevice)
|
|
{
|
|
*((PULONG)pVideoChildDescriptor->Buffer) = NONEDID_SIGNATURE;
|
|
}
|
|
|
|
moreChild = fdoExtension->HwGetVideoChildDescriptor(
|
|
fdoExtension->HwDeviceExtension,
|
|
&childEnumInfo,
|
|
&(pVideoChildDescriptor->Type),
|
|
(PUCHAR)(pVideoChildDescriptor->Buffer),
|
|
&(pVideoChildDescriptor->UId),
|
|
&Unused);
|
|
|
|
if (moreChild == ERROR_MORE_DATA || moreChild == VIDEO_ENUM_MORE_DEVICES)
|
|
{
|
|
//
|
|
// Perform the required functions on the returned type.
|
|
//
|
|
ntStatus = VpAddPdo(DeviceObject,
|
|
pVideoChildDescriptor);
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
++ulChildCount;
|
|
}
|
|
else
|
|
{
|
|
moreChild = VIDEO_ENUM_INVALID_DEVICE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Stop enumerating the driver returns an error
|
|
// For ACPI devices, if miniports returns ERROR_MORE_DATA, stop enumeration.
|
|
// If it returns VIDEO_ENUM_MORE_DEVICE, continue on to Non-ACPI device.
|
|
// It is the responsibility of Miniport not to enumerate duplicated ACPI and Non-ACPI devices .
|
|
//
|
|
|
|
if (moreChild == ERROR_MORE_DATA &&
|
|
(pVideoChildDescriptor->bACPIDevice == TRUE) && (count == 0))
|
|
{
|
|
moreDevices = 0;
|
|
}
|
|
|
|
if ((moreChild != ERROR_MORE_DATA) &&
|
|
(moreChild != VIDEO_ENUM_MORE_DEVICES) &&
|
|
(moreChild != VIDEO_ENUM_INVALID_DEVICE)
|
|
)
|
|
{
|
|
moreDevices = 0;
|
|
}
|
|
|
|
//
|
|
// Free the memory in case of error.
|
|
//
|
|
|
|
if ((moreChild != ERROR_MORE_DATA) && (moreChild != VIDEO_ENUM_MORE_DEVICES))
|
|
{
|
|
ExFreePool(pVideoChildDescriptor);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now that we know how many devices we have, allocate the blob to be returned and
|
|
// fill it.
|
|
//
|
|
|
|
relationsSize = sizeof(DEVICE_RELATIONS) +
|
|
(ulChildCount * sizeof(PDEVICE_OBJECT));
|
|
|
|
deviceRelations = ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
|
|
relationsSize,
|
|
VP_TAG);
|
|
|
|
if (deviceRelations == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(deviceRelations, relationsSize);
|
|
|
|
//
|
|
// Walk our chain of children, and store them in the relations array.
|
|
//
|
|
|
|
pChildDeviceExtension = fdoExtension->ChildPdoList;
|
|
|
|
pdo = &(deviceRelations->Objects[0]);
|
|
|
|
while (pChildDeviceExtension) {
|
|
|
|
if (pChildDeviceExtension->bIsEnumerated) {
|
|
|
|
//
|
|
// Refcount the ChildDeviceObject.
|
|
//
|
|
|
|
ObReferenceObject(pChildDeviceExtension->ChildDeviceObject);
|
|
*pdo++ = pChildDeviceExtension->ChildDeviceObject;
|
|
++debugCount;
|
|
}
|
|
|
|
pChildDeviceExtension = pChildDeviceExtension->NextChild;
|
|
}
|
|
|
|
if (debugCount != ulChildCount) {
|
|
pVideoDebugPrint((0, "List management ERROR line %d\n", __LINE__));
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
fdoExtension->ChildPdoNumber = ulChildCount;
|
|
deviceRelations->Count = ulChildCount;
|
|
|
|
//
|
|
// Stuff that pDeviceRelations into the IRP and return SUCCESS.
|
|
//
|
|
|
|
Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
pVideoPortCleanUpChildList(
|
|
PFDO_EXTENSION FdoExtension,
|
|
PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*+
|
|
* Function: pVideoPortCleanUpChildList
|
|
* Context: Called in the context of an IRP_MN_REMOVE_DEVICE
|
|
* minor function and IRP_MJ_PNP major function.
|
|
* Arguments:
|
|
* PFDO_EXTENSION FdoExtension - Device extension of the parent
|
|
* PDEVICE_OBJECT deviceObject - Device object to be deleted
|
|
*
|
|
* Comments: This routine deletes a monitor device object when it is no
|
|
* longer needed
|
|
* It actually determines if the device is still present by
|
|
* checking the enumerate flag in the device extension
|
|
* We only do lazy deletion, that is delete the device objects
|
|
* after reenumeration has shown the device not to be there
|
|
*
|
|
-*/
|
|
{
|
|
PCHILD_PDO_EXTENSION PrevChild = NULL;
|
|
PCHILD_PDO_EXTENSION pChildDeviceExtension = FdoExtension->ChildPdoList;
|
|
|
|
ASSERT(pChildDeviceExtension != NULL);
|
|
|
|
//
|
|
// Search the ChildPdoList for the device we are
|
|
// removing.
|
|
//
|
|
|
|
while (pChildDeviceExtension)
|
|
{
|
|
if (pChildDeviceExtension->ChildDeviceObject == DeviceObject) {
|
|
break;
|
|
}
|
|
|
|
PrevChild = pChildDeviceExtension;
|
|
pChildDeviceExtension = pChildDeviceExtension->NextChild;
|
|
}
|
|
|
|
if (pChildDeviceExtension) {
|
|
|
|
//
|
|
// If the device is still enumerated, do not delete it as it is
|
|
// too expensive for us to go check for device presence again.
|
|
//
|
|
|
|
if (pChildDeviceExtension->bIsEnumerated) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Remove the device from the list.
|
|
//
|
|
|
|
if (PrevChild == NULL) {
|
|
|
|
FdoExtension->ChildPdoList = pChildDeviceExtension->NextChild;
|
|
|
|
} else {
|
|
|
|
PrevChild->NextChild = pChildDeviceExtension->NextChild;
|
|
}
|
|
|
|
//
|
|
// Free the memory associated with this child device and then delete it.
|
|
//
|
|
|
|
ExFreePool(pChildDeviceExtension->VideoChildDescriptor);
|
|
IoDeleteDevice(DeviceObject);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/*+
|
|
* Function: pVideoPortConvertAsciiToWchar
|
|
* convert that Ascii into a LPWSTR which
|
|
* is then placed in Buffer.
|
|
*
|
|
*
|
|
* Arguments: UCHAR Ascii - Pointer to an ascii string.
|
|
*
|
|
* WCHAR Buffer[64] - Buffer used to convert from ascii to
|
|
* WCHAR.
|
|
*
|
|
* Comments: If DeviceName is returned to some caller outside the videoprt,
|
|
* then Buffer had better have the right lifetime.
|
|
*
|
|
-*/
|
|
|
|
VOID
|
|
pVideoPortConvertAsciiToWChar(
|
|
IN PUCHAR Ascii,
|
|
OUT WCHAR Buffer[64]
|
|
)
|
|
{
|
|
ANSI_STRING ansiString;
|
|
UNICODE_STRING us;
|
|
|
|
//
|
|
// Create a unicode string holding the ascii Name.
|
|
//
|
|
|
|
RtlInitAnsiString(&ansiString, Ascii);
|
|
|
|
//
|
|
// Attach a buffer to the UNICODE_STRING
|
|
//
|
|
|
|
us.Buffer = Buffer;
|
|
us.Length = 0;
|
|
us.MaximumLength = 64;
|
|
|
|
RtlZeroMemory(Buffer, sizeof(Buffer));
|
|
|
|
RtlAnsiStringToUnicodeString(&us,
|
|
&ansiString,
|
|
FALSE);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
pVideoPortQueryDeviceText(
|
|
IN PDEVICE_OBJECT ChildDeviceObject,
|
|
IN DEVICE_TEXT_TYPE TextType,
|
|
OUT PWSTR * ReturnValue
|
|
)
|
|
/*+
|
|
* Function:
|
|
* Context: Called in the context of an IRP_MN_QUERY_DEVICE_TEXT
|
|
* minor function and IRP_MJ_PNP major function.
|
|
* Arguments:
|
|
* PDEVICE_OBJECT ChildDeviceObject - Passed in by caller
|
|
* of pVideoPortPnpDispatch().
|
|
*
|
|
* DEVICE_TEXT_TYPE TextType - Passed in by caller
|
|
* of pVideoPortPnpDispatch().
|
|
*
|
|
* PWSTR * ReturnValue - Created by caller of
|
|
* this routine.
|
|
-*/
|
|
{
|
|
PCHILD_PDO_EXTENSION pdoExtension;
|
|
PVIDEO_CHILD_DESCRIPTOR pChildDescriptor;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get the child descriptor allocated during Enumerate phase.
|
|
//
|
|
|
|
pdoExtension = (PCHILD_PDO_EXTENSION) ChildDeviceObject->DeviceExtension;
|
|
ASSERT(IS_PDO(pdoExtension));
|
|
pChildDescriptor = pdoExtension->VideoChildDescriptor;
|
|
|
|
*ReturnValue = NULL;
|
|
|
|
switch (TextType) {
|
|
|
|
case DeviceTextDescription:
|
|
|
|
if (pChildDescriptor->Type == Monitor)
|
|
{
|
|
ULONG asciiStringLength = 0;
|
|
UCHAR pTmp[64];
|
|
PWSTR tmpBuffer = (PWSTR)ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
|
|
128,
|
|
VP_TAG);
|
|
|
|
if (!tmpBuffer) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
memset(pTmp, '0', 64);
|
|
|
|
if (pChildDescriptor->ValidEDID == GOOD_EDID) {
|
|
|
|
asciiStringLength = pVideoPortGetEdidOemID(&(pChildDescriptor->Buffer), pTmp);
|
|
|
|
|
|
ASSERT(asciiStringLength <= 64);
|
|
|
|
pVideoPortConvertAsciiToWChar(pTmp, tmpBuffer);
|
|
|
|
if (asciiStringLength) {
|
|
|
|
pVideoDebugPrint((2, "Ascii name:%s\n", pTmp));
|
|
pVideoDebugPrint((2, "WChar name:%ws\n", tmpBuffer));
|
|
}
|
|
|
|
*ReturnValue = tmpBuffer;
|
|
|
|
} else {
|
|
|
|
wcscpy(tmpBuffer, L"Monitor");
|
|
*ReturnValue = tmpBuffer;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
default:
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN pCheckDeviceRelations(PFDO_EXTENSION FdoExtension, BOOLEAN bNewMonitor)
|
|
/*+
|
|
* Function: pCheckDeviceRelations
|
|
* Arguments:
|
|
* bNewMonitor New monitor has been plugged in
|
|
* Return Value:
|
|
* TRUE: Monitors had been changed, need to reenumarate
|
|
* FALSE: No child device change
|
|
-*/
|
|
{
|
|
BOOLEAN bInvalidateRelation = FALSE;
|
|
PCHILD_PDO_EXTENSION pChildDeviceExtension;
|
|
UCHAR pEdid[EDID_BUFFER_SIZE + sizeof(ACPI_EVAL_OUTPUT_BUFFER)];
|
|
|
|
for (pChildDeviceExtension = FdoExtension->ChildPdoList;
|
|
pChildDeviceExtension != NULL;
|
|
pChildDeviceExtension = pChildDeviceExtension->NextChild
|
|
)
|
|
{
|
|
PVIDEO_CHILD_DESCRIPTOR VideoChildDescriptor = pChildDeviceExtension->VideoChildDescriptor;
|
|
BOOLEAN ValidEDID, bEqualEDID = TRUE;
|
|
|
|
if (VideoChildDescriptor->bACPIDevice == TRUE)
|
|
{
|
|
//
|
|
// If it's non-monitor device, just ignore
|
|
//
|
|
if (VideoChildDescriptor->Type != Monitor)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// For each output device, we are going to retrieve EDID when it's active.
|
|
//
|
|
if (bNewMonitor)
|
|
{
|
|
VideoChildDescriptor->bInvalidate = TRUE;
|
|
}
|
|
else if (VideoChildDescriptor->bInvalidate == FALSE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check the device is active, since inactive CRT may return false EDID
|
|
// If inactive, delay the EDID retieving until next hotkey switching
|
|
//
|
|
if (pCheckActiveMonitor(pChildDeviceExtension) == FALSE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
VideoChildDescriptor->bInvalidate = FALSE;
|
|
|
|
//
|
|
// Get DDC from Miniport first
|
|
//
|
|
{
|
|
VIDEO_CHILD_ENUM_INFO childEnumInfo;
|
|
VIDEO_CHILD_TYPE childType;
|
|
ULONG UId, Unused, moreChild;
|
|
|
|
childEnumInfo.Size = sizeof(VIDEO_CHILD_ENUM_INFO);
|
|
childEnumInfo.ChildDescriptorSize = EDID_BUFFER_SIZE;
|
|
childEnumInfo.ChildIndex = 0;
|
|
childEnumInfo.ACPIHwId = VideoChildDescriptor->UId;
|
|
childEnumInfo.ChildHwDeviceExtension = NULL;
|
|
|
|
moreChild = FdoExtension->HwGetVideoChildDescriptor(
|
|
FdoExtension->HwDeviceExtension,
|
|
&childEnumInfo,
|
|
&childType,
|
|
(PUCHAR)pEdid,
|
|
&UId,
|
|
&Unused);
|
|
ASSERT (moreChild == ERROR_MORE_DATA || moreChild == VIDEO_ENUM_MORE_DEVICES);
|
|
|
|
ValidEDID = pVideoPortIsValidEDID(pEdid) ? GOOD_EDID : BAD_EDID;
|
|
}
|
|
|
|
//
|
|
// For ACPI system, retrieve EDID again.
|
|
// At this moment, the handle of DeviceObject is still valid
|
|
//
|
|
if (ValidEDID != GOOD_EDID &&
|
|
VideoChildDescriptor->ACPIDDCFlag & ACPIDDC_EXIST)
|
|
{
|
|
if (!pGetACPIEdid(pChildDeviceExtension->ChildDeviceObject, pEdid))
|
|
{
|
|
continue;
|
|
}
|
|
ValidEDID = pVideoPortIsValidEDID(pEdid) ? GOOD_EDID : BAD_EDID;
|
|
}
|
|
|
|
if (VideoChildDescriptor->ValidEDID != ValidEDID)
|
|
{
|
|
bEqualEDID = FALSE;
|
|
}
|
|
else if (ValidEDID == GOOD_EDID)
|
|
{
|
|
if (memcmp(VideoChildDescriptor->Buffer, pEdid, EDID_BUFFER_SIZE) != 0)
|
|
{
|
|
bEqualEDID = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!bEqualEDID)
|
|
{
|
|
bInvalidateRelation = TRUE;
|
|
//
|
|
// Forcing UId to become a bad value will invalidate the device
|
|
//
|
|
VideoChildDescriptor->UId = 0xFFFF8086;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bInvalidateRelation;
|
|
}
|
|
|
|
BOOLEAN pCheckActiveMonitor(PCHILD_PDO_EXTENSION pChildDeviceExtension)
|
|
{
|
|
ULONG UId, flag;
|
|
|
|
UId = pChildDeviceExtension->ChildUId;
|
|
if (NT_SUCCESS
|
|
(pVideoMiniDeviceIoControl(pChildDeviceExtension->ChildDeviceObject,
|
|
IOCTL_VIDEO_GET_CHILD_STATE,
|
|
&UId,
|
|
sizeof(ULONG),
|
|
&flag,
|
|
sizeof(ULONG) ) )
|
|
)
|
|
{
|
|
return ((flag & VIDEO_CHILD_ACTIVE) ?
|
|
TRUE :
|
|
FALSE);
|
|
}
|
|
|
|
if (pChildDeviceExtension->VideoChildDescriptor->bACPIDevice == TRUE)
|
|
{
|
|
UCHAR outputBuffer[0x10 + sizeof(ACPI_EVAL_OUTPUT_BUFFER)];
|
|
|
|
if (NT_SUCCESS
|
|
(pVideoPortACPIIoctl(IoGetAttachedDevice(pChildDeviceExtension->ChildDeviceObject),
|
|
(ULONG) ('SCD_'),
|
|
NULL,
|
|
NULL,
|
|
sizeof(outputBuffer),
|
|
(PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)
|
|
)
|
|
)
|
|
{
|
|
if ( ((PACPI_EVAL_OUTPUT_BUFFER)outputBuffer)->Argument[0].Argument & 0x02)
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// For Non-ACPI machines, if miniport doesn't handle IOCTL_VIDEO_GET_CHILD_STATE, we just assume all Monitors are active
|
|
//
|
|
return TRUE;
|
|
}
|
|
}
|