windows-nt/Source/XPSP1/NT/drivers/video/ms/port/enum.c

1447 lines
44 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}
}