1816 lines
63 KiB
C
1816 lines
63 KiB
C
/*++
|
||
|
||
Copyright (c) 1995 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
busenum.c
|
||
|
||
Abstract:
|
||
|
||
Bus enumeration routines
|
||
|
||
Author:
|
||
|
||
Lonny McMichael (lonnym) 02/14/95
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
#ifdef _PNP_POWER_
|
||
|
||
//
|
||
// Define a list node to hold Plug & Play compatible device IDs
|
||
//
|
||
typedef struct _PI_COMPAT_ID_NODE {
|
||
PWCHAR BctlDeviceId;
|
||
ULONG IdStringLength; // caution: only filled in for certain cases!
|
||
LIST_ENTRY ListEntry;
|
||
} PI_COMPAT_ID_NODE, *PPI_COMPAT_ID_NODE;
|
||
|
||
//
|
||
// Define a list node to hold device instance paths.
|
||
//
|
||
typedef struct _PI_DEVINST_PATH_NODE {
|
||
UNICODE_STRING DeviceInstancePath;
|
||
LIST_ENTRY ListEntry;
|
||
} PI_DEVINST_PATH_NODE, *PPI_DEVINST_PATH_NODE;
|
||
|
||
#if 0
|
||
|
||
//
|
||
// Define the context structure for the PiFindEnumeratedDeviceInstance
|
||
// callback routine
|
||
//
|
||
typedef struct _PI_FIND_DEVICE_INSTANCE_CONTEXT {
|
||
NTSTATUS ReturnStatus;
|
||
PUNICODE_STRING BusDeviceInstancePath;
|
||
ULONG SlotNumber;
|
||
UNICODE_STRING DeviceInstanceKeyName;
|
||
} PI_FIND_DEVICE_INSTANCE_CONTEXT, *PPI_FIND_DEVICE_INSTANCE_CONTEXT;
|
||
|
||
#endif
|
||
|
||
//
|
||
// Prototype utility functions internal to this file
|
||
//
|
||
BOOLEAN
|
||
PiFindEnumeratedDeviceInstance(
|
||
IN HANDLE DeviceInstanceHandle,
|
||
IN PUNICODE_STRING DeviceInstanceName,
|
||
IN OUT PVOID Context
|
||
);
|
||
|
||
VOID
|
||
PiFreeCompatibleIdList(
|
||
IN OUT PLIST_ENTRY CompatIdListHead
|
||
);
|
||
|
||
VOID
|
||
PiFreeDevInstPathList(
|
||
IN OUT PLIST_ENTRY DevInstPathListHead
|
||
);
|
||
|
||
NTSTATUS
|
||
PiGetDeviceId(
|
||
IN PDEVICE_HANDLER_OBJECT DeviceHandler,
|
||
IN ULONG IdIndex,
|
||
OUT PWCHAR *DeviceIdBuffer
|
||
);
|
||
|
||
NTSTATUS
|
||
PiGetDeviceResourceInformation(
|
||
IN PDEVICE_HANDLER_OBJECT DeviceHandler,
|
||
IN ULONG ControlCode,
|
||
OUT PVOID *Buffer,
|
||
OUT PULONG BufferLength
|
||
);
|
||
|
||
BOOLEAN
|
||
PiDeviceInstanceEnumNotification(
|
||
IN HANDLE DevInstKeyHandle,
|
||
IN PUNICODE_STRING DevInstRegPath,
|
||
IN OUT PVOID Context,
|
||
IN PI_ENUM_DEVICE_STATE EnumDeviceState,
|
||
IN DEVICE_STATUS DeviceStatus
|
||
);
|
||
|
||
#endif // _PNP_POWER_
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
#pragma alloc_text(PAGE, PpEnumerateBus)
|
||
|
||
#ifdef _PNP_POWER_
|
||
#pragma alloc_text(PAGE, PiEnumerateSystemBus)
|
||
#pragma alloc_text(PAGE, PiFreeCompatibleIdList)
|
||
#pragma alloc_text(PAGE, PiFreeDevInstPathList)
|
||
#pragma alloc_text(PAGE, PiGetDeviceId)
|
||
#pragma alloc_text(PAGE, PiGetDeviceResourceInformation)
|
||
#pragma alloc_text(PAGE, PiFindEnumeratedDeviceInstance)
|
||
#pragma alloc_text(PAGE, PiDeviceInstanceEnumNotification)
|
||
#endif // _PNP_POWER_
|
||
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
NTSTATUS
|
||
PpEnumerateBus(
|
||
IN PPLUGPLAY_BUS_INSTANCE BusInstance OPTIONAL,
|
||
IN PUNICODE_STRING BusDeviceInstanceName OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This Plug and Play Manager API causes a particular instance of a bus
|
||
to be enumerated. The bus instance may be specified by either giving
|
||
its Plug & Play device instance name (path to its device instance key
|
||
relative to HKLM\System\Enum), or by specifying its
|
||
PLUGPLAY_BUS_INSTANCE structure, as returned by NtQuerySystemInformation
|
||
for class SystemPlugPlayBusInformation.
|
||
|
||
Arguments:
|
||
|
||
BusInstance - Optionally, supplies a pointer to a structure that specifies
|
||
the bus to be enumerated. This structure should be one of the elements
|
||
of the array returned by a call to NtQuerySystemInformation for info
|
||
class SystemPlugPlayBusInformation. If this parameter is not specified,
|
||
then BusDeviceInstanceName will be used instead to specify the bus to
|
||
enumerate.
|
||
|
||
BusDeviceInstanceName - Optionally, supplies an alternate way of identifying
|
||
the bus instance to be enumerated. This string specifies the device instance
|
||
registry path (relative to HKLM\System\Enum) that represents this bus
|
||
instance. If BusInstance is specified, this parameter will be ignored.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS code indicating whether or not the function was successful
|
||
|
||
--*/
|
||
|
||
{
|
||
#ifndef _PNP_POWER_
|
||
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
#else
|
||
|
||
NTSTATUS Status;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
UNICODE_STRING TempUnicodeString;
|
||
PPLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR BusInstanceNode;
|
||
BOOLEAN ReleaseRegResource = FALSE, ReleaseBusResource = FALSE;
|
||
|
||
try {
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
PreviousMode = KeGetPreviousMode();
|
||
if(PreviousMode != KernelMode) {
|
||
|
||
if(ARGUMENT_PRESENT(BusInstance)) {
|
||
ProbeForRead(BusInstance, sizeof(PLUGPLAY_BUS_INSTANCE), sizeof(ULONG));
|
||
}
|
||
|
||
if(ARGUMENT_PRESENT(BusDeviceInstanceName)) {
|
||
TempUnicodeString = ProbeAndReadUnicodeString(BusDeviceInstanceName);
|
||
ProbeForRead(TempUnicodeString.Buffer,
|
||
TempUnicodeString.Length,
|
||
sizeof(WCHAR)
|
||
);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Acquire the PnP registry and bus resources for exclusive (write) access.
|
||
//
|
||
KeEnterCriticalRegion();
|
||
ExAcquireResourceExclusive(&PpBusResource, TRUE);
|
||
ReleaseBusResource = TRUE;
|
||
ExAcquireResourceExclusive(&PpRegistryDeviceResource, TRUE);
|
||
ReleaseRegResource = TRUE;
|
||
|
||
//
|
||
// Find the bus instance node based on the information specified in one of the
|
||
// parameters.
|
||
//
|
||
Status = PiFindBusInstanceNode(BusInstance,
|
||
BusDeviceInstanceName,
|
||
NULL,
|
||
&BusInstanceNode
|
||
);
|
||
|
||
//
|
||
// Now, we can enumerate the slots on this bus instance.
|
||
//
|
||
if(NT_SUCCESS(Status)) {
|
||
|
||
switch(BusInstanceNode->BusInstanceInformation.BusType.BusClass) {
|
||
|
||
case SystemBus:
|
||
|
||
//
|
||
// BUGBUG (lonnym): Should we provide a context for this callback?
|
||
// (E.g., so that we can be returned a list of new buses to be
|
||
// initialized)
|
||
//
|
||
Status = PiEnumerateSystemBus(BusInstanceNode,
|
||
PiDeviceInstanceEnumNotification,
|
||
NULL
|
||
);
|
||
break;
|
||
|
||
case PlugPlayVirtualBus:
|
||
|
||
//
|
||
// Don't do anything for now--maybe later we'll want to call a callback
|
||
// function for each device hanging off this virtual bus.
|
||
//
|
||
break;
|
||
|
||
default:
|
||
|
||
//
|
||
// Don't know how to deal with this bus class!
|
||
//
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
ExReleaseResource(&PpRegistryDeviceResource);
|
||
ReleaseRegResource = FALSE;
|
||
ExReleaseResource(&PpBusResource);
|
||
ReleaseBusResource = FALSE;
|
||
KeLeaveCriticalRegion();
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
Status = GetExceptionCode();
|
||
|
||
if(ReleaseRegResource) {
|
||
ExReleaseResource(&PpRegistryDeviceResource);
|
||
}
|
||
if(ReleaseBusResource) {
|
||
ExReleaseResource(&PpBusResource);
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiEnumerateSystemBus(
|
||
IN PPLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR BusInstanceNode,
|
||
IN PPI_ENUM_DEVINST_CALLBACK_ROUTINE DeviceInstanceCallbackRoutine,
|
||
IN OUT PVOID Context OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine enumerates each slot on the specified system bus instance, and registers
|
||
each device instance it finds in a device instance key under HKLM\System\Enum.
|
||
If there is already a device instance key for this device, the function simplies
|
||
updates the 'FoundAtEnum' value entry to make sure it's TRUE.
|
||
|
||
If a subkey callback routine is specified, we will then call it for each enumerated
|
||
device instance. The callback may return TRUE to continue enumeration, or FALSE to
|
||
abort it.
|
||
|
||
The caller must have acquired the Plug & Play bus list AND registry resources for
|
||
exclusive (write) access.
|
||
|
||
Arguments:
|
||
|
||
BusInstanceNode - Supplies a pointer to the bus instance node for the
|
||
bus to be enumerated.
|
||
|
||
DeviceInstanceCallbackRoutine - Supplies a pointer to a function that will be called
|
||
for each device instance enumerated, as well as for each device instance that has
|
||
disappeared since the last enumeration. The prototype of the function is as follows:
|
||
|
||
typedef BOOLEAN (*PPI_ENUM_DEVINST_CALLBACK_ROUTINE) (
|
||
IN HANDLE DevInstKeyHandle,
|
||
IN PUNICODE_STRING DevInstRegPath,
|
||
IN OUT PVOID Context,
|
||
IN PI_ENUM_DEVICE_STATE EnumDeviceState,
|
||
IN DEVICE_STATUS DeviceStatus
|
||
);
|
||
|
||
where DevInstKeyHandle is the handle to the enumerated device instance
|
||
key, DevInstRegPath is its path in the registry (relative to
|
||
HKLM\System\Enum), and Context is a pointer to user-defined data.
|
||
EnumDeviceState indicates the state of the device (e.g., newly arrived,
|
||
previously enumerated, or removed), and DeviceStatus supplies the current
|
||
status of the device.
|
||
|
||
This function should return TRUE to continue enumeration, or
|
||
FALSE to terminate it.
|
||
|
||
Context - Optionally, supplies a pointer to user-defined data that will be passed
|
||
in to the callback routine at each device instance invocation.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS code indicating whether or not the function was successful
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
HANDLE SystemEnumHandle, BusInstanceHandle, DeviceIdHandle, DeviceInstanceHandle;
|
||
ULONG SlotNumber, Disposition, Instance, DwordValue;
|
||
ULONG CompatIdIndex, NumSlots, SlotNumberIndex;
|
||
PWCHAR DeviceIdBuffer, DeviceInstanceBuffer;
|
||
UNICODE_STRING DeviceIdString, DeviceInstanceString, TempUnicodeString;
|
||
BOOLEAN NewDeviceInstance, FoundAtLastEnum = FALSE, ContinueEnumeration;
|
||
//PI_FIND_DEVICE_INSTANCE_CONTEXT FindDevInstContext;
|
||
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
||
WCHAR UnicodeBuffer[20];
|
||
LIST_ENTRY CompatIdListHead, DevInstPathListHead;
|
||
PLIST_ENTRY CurListEntry;
|
||
PULONG SlotNumberBuffer;
|
||
ULONG SlotNumberBufferLength;
|
||
DEVICE_STATUS PreviousDeviceStatus;
|
||
PCM_RESOURCE_LIST DeviceResourceList;
|
||
PIO_RESOURCE_REQUIREMENTS_LIST DeviceResourceRequirements;
|
||
ULONG DeviceResourceListSize, DeviceResourceRequirementsSize;
|
||
PWCHAR DevInstRegPath, CurDevInstPos, CompatIdMultiSzBuffer;
|
||
ULONG RequiredBufferLength, CurMultiSzPos;
|
||
PPI_COMPAT_ID_NODE CurDevIdNode;
|
||
PUNICODE_STRING PrevEnumDeviceList;
|
||
ULONG PrevEnumDeviceCount, PrevEnumDeviceIndex;
|
||
PPI_DEVINST_PATH_NODE CurDevInstPathNode;
|
||
PDEVICE_HANDLER_OBJECT DeviceHandler;
|
||
PBUS_HANDLER BusHandler;
|
||
|
||
//
|
||
// First, open a registry handle to HKLM\System\CurrentControlSet\Enum
|
||
//
|
||
Status = IopOpenRegistryKey(&SystemEnumHandle,
|
||
NULL,
|
||
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
||
KEY_ALL_ACCESS,
|
||
FALSE
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Get the bus hander for the bus node to be enumerated.
|
||
//
|
||
|
||
BusHandler = HalReferenceHandlerForBus (
|
||
BusInstanceNode->BusInstanceInformation.BusType.SystemBusType,
|
||
BusInstanceNode->BusInstanceInformation.BusNumber
|
||
);
|
||
if (!BusHandler) {
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
|
||
//
|
||
// Now, open up the device instance key for this bus instance.
|
||
//
|
||
Status = IopOpenRegistryKey(&BusInstanceHandle,
|
||
SystemEnumHandle,
|
||
&(BusInstanceNode->DeviceInstancePath),
|
||
KEY_ALL_ACCESS,
|
||
FALSE
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
goto PrepareForReturn0;
|
||
}
|
||
|
||
//
|
||
// Retrieve the list of device instances previously enumerated on this bus.
|
||
//
|
||
Status = IopGetRegistryValue(BusInstanceHandle,
|
||
REGSTR_VALUE_ATTACHEDCOMPONENTS,
|
||
&KeyValueInformation
|
||
);
|
||
if(NT_SUCCESS(Status)) {
|
||
Status = IopRegMultiSzToUnicodeStrings(KeyValueInformation,
|
||
&PrevEnumDeviceList,
|
||
&PrevEnumDeviceCount
|
||
);
|
||
ExFreePool(KeyValueInformation);
|
||
} else if(Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
Status = STATUS_SUCCESS;
|
||
PrevEnumDeviceList = NULL;
|
||
PrevEnumDeviceCount = 0;
|
||
}
|
||
|
||
if(!NT_SUCCESS(Status)) {
|
||
goto PrepareForReturn1;
|
||
}
|
||
|
||
InitializeListHead(&DevInstPathListHead);
|
||
|
||
//
|
||
// Retrieve the list of slots to enumerate for this bus instance.
|
||
// Start with an initial buffer size large enough for 10 slot numbers.
|
||
//
|
||
SlotNumberBuffer = NULL;
|
||
SlotNumberBufferLength = 10 * sizeof(ULONG);
|
||
|
||
do {
|
||
|
||
if(!SlotNumberBuffer) {
|
||
SlotNumberBuffer = (PULONG)ExAllocatePool(PagedPool, SlotNumberBufferLength);
|
||
|
||
if(!SlotNumberBuffer) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto PrepareForReturn2;
|
||
}
|
||
}
|
||
|
||
Status = HalQueryBusSlots(BusHandler,
|
||
SlotNumberBufferLength,
|
||
SlotNumberBuffer,
|
||
&RequiredBufferLength
|
||
);
|
||
|
||
if(Status == STATUS_BUFFER_TOO_SMALL) {
|
||
ExFreePool(SlotNumberBuffer);
|
||
SlotNumberBuffer = NULL;
|
||
SlotNumberBufferLength = RequiredBufferLength;
|
||
}
|
||
|
||
} while(Status == STATUS_BUFFER_TOO_SMALL);
|
||
|
||
if(!NT_SUCCESS(Status)) {
|
||
goto PrepareForReturn2;
|
||
} else {
|
||
NumSlots = RequiredBufferLength / sizeof(ULONG);
|
||
}
|
||
|
||
InitializeListHead(&CompatIdListHead);
|
||
|
||
//
|
||
// Now, enumerate each slot. (On error, set ContinueEnumeration to FALSE, then
|
||
// goto the appropriate 'ContinueWithNextSlot<x>' label.)
|
||
//
|
||
for(SlotNumberIndex = 0, ContinueEnumeration = TRUE;
|
||
(ContinueEnumeration && (SlotNumberIndex < NumSlots));
|
||
SlotNumberIndex++) {
|
||
|
||
SlotNumber = SlotNumberBuffer[SlotNumberIndex];
|
||
|
||
//
|
||
// Get the DeviceHandler object for the slot
|
||
//
|
||
|
||
DeviceHandler = IopReferenceDeviceHandler (
|
||
BusInstanceNode->BusInstanceInformation.BusType.SystemBusType,
|
||
BusInstanceNode->BusInstanceInformation.BusNumber,
|
||
SlotNumber
|
||
);
|
||
|
||
if (!DeviceHandler) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Retrieve the list of compatible IDs for this device (with the first one being
|
||
// the 'real' ID).
|
||
//
|
||
CompatIdIndex = 0;
|
||
while(TRUE) {
|
||
|
||
Status = PiGetDeviceId(DeviceHandler,
|
||
CompatIdIndex,
|
||
&DeviceIdBuffer
|
||
);
|
||
|
||
if(!NT_SUCCESS(Status)) {
|
||
if((Status == STATUS_NO_MORE_ENTRIES) || (Status == STATUS_NO_SUCH_DEVICE)) {
|
||
//
|
||
// Either we have reached the end of the compatible ID list, or the device
|
||
// was removed while we were enumerating its compatible ID's.
|
||
//
|
||
break;
|
||
} else {
|
||
ContinueEnumeration = FALSE;
|
||
goto ContinueWithNextSlot0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// We retrieved a compatible Plug & Play ID, so add it to our list.
|
||
//
|
||
CurDevIdNode = (PPI_COMPAT_ID_NODE)ExAllocatePool(PagedPool, sizeof(PI_COMPAT_ID_NODE));
|
||
if(!CurDevIdNode) {
|
||
ExFreePool(DeviceIdBuffer);
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
ContinueEnumeration = FALSE;
|
||
goto ContinueWithNextSlot0;
|
||
}
|
||
CurDevIdNode->BctlDeviceId = DeviceIdBuffer;
|
||
InsertTailList(&CompatIdListHead, &(CurDevIdNode->ListEntry));
|
||
|
||
CompatIdIndex++;
|
||
}
|
||
|
||
//
|
||
// If we break out of the loop, we will have one of the following error statuses:
|
||
//
|
||
if(Status == STATUS_NO_MORE_ENTRIES) {
|
||
//
|
||
// We retrieved the full set of compatible IDs for the device (unless we stopped at zero,
|
||
// in which case we'll just ignore this slot and continue on).
|
||
//
|
||
if(!CompatIdIndex) {
|
||
goto ContinueWithNextSlot0;
|
||
}
|
||
} else { // Status == STATUS_NO_SUCH_DEVICE
|
||
//
|
||
// The device that was in this slot has been removed. We simply ignore this slot
|
||
// and continue on--we'll report the device removal (if the device had previously
|
||
// been enumerated) later when we traverse our list of previously-found devices to
|
||
// see which ones are no longer present.
|
||
//
|
||
goto ContinueWithNextSlot0;
|
||
}
|
||
|
||
//
|
||
// We have the full (non-empty) set of compatible IDs for the device. The first ID is the
|
||
// 'real' ID for the device, so open/create this registry path under HKLM\System\Enum
|
||
//
|
||
RtlInitUnicodeString(&DeviceIdString,
|
||
CONTAINING_RECORD(CompatIdListHead.Flink,
|
||
PI_COMPAT_ID_NODE,
|
||
ListEntry)->BctlDeviceId
|
||
);
|
||
Status = IopOpenRegistryKeyPersist(&DeviceIdHandle,
|
||
SystemEnumHandle,
|
||
&DeviceIdString,
|
||
KEY_ALL_ACCESS,
|
||
TRUE,
|
||
&Disposition
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
ContinueEnumeration = FALSE;
|
||
goto ContinueWithNextSlot0;
|
||
}
|
||
|
||
if(Disposition == REG_CREATED_NEW_KEY) {
|
||
#if 0
|
||
//
|
||
// Set this device key's NewDevice value entry to TRUE. (ignore return status)
|
||
//
|
||
DwordValue = 1;
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_NEWDEVICE);
|
||
ZwSetValueKey(DeviceIdHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&DwordValue,
|
||
sizeof(DwordValue)
|
||
);
|
||
#endif
|
||
#if 1
|
||
}
|
||
|
||
//
|
||
// Query the unique id for the device
|
||
//
|
||
|
||
Status = PiGetDeviceId(DeviceHandler,
|
||
(ULONG) -1,
|
||
&DeviceInstanceBuffer
|
||
);
|
||
|
||
if(!NT_SUCCESS(Status)) {
|
||
ContinueEnumeration = FALSE;
|
||
goto ContinueWithNextSlot1;
|
||
}
|
||
|
||
//
|
||
// Open/create this registry device instance path under HKLM\System\Enum\DeviceIdHandler
|
||
//
|
||
|
||
RtlInitUnicodeString(&DeviceInstanceString,
|
||
DeviceInstanceBuffer
|
||
);
|
||
Status = IopOpenRegistryKeyPersist(&DeviceInstanceHandle,
|
||
DeviceIdHandle,
|
||
&DeviceInstanceString,
|
||
KEY_ALL_ACCESS,
|
||
TRUE,
|
||
&Disposition
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
ContinueEnumeration = FALSE;
|
||
goto ContinueWithNextSlot2;
|
||
}
|
||
|
||
if(Disposition == REG_CREATED_NEW_KEY) {
|
||
#if 0
|
||
//
|
||
// Now, initialize value entries for this new instance. (ignoring return status)
|
||
//
|
||
DwordValue = 1;
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_NEWINSTANCE);
|
||
ZwSetValueKey(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&DwordValue,
|
||
sizeof(DwordValue)
|
||
);
|
||
#endif
|
||
//
|
||
// (Note: I can use the bus DeviceInstancePath unicode string to set the REG_SZ
|
||
// value below because I always ensure that these unicode strings are NULL-terminated.)
|
||
//
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_BASEDEVICEPATH);
|
||
ZwSetValueKey(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
BusInstanceNode->DeviceInstancePath.Buffer,
|
||
(ULONG)(BusInstanceNode->DeviceInstancePath.MaximumLength)
|
||
);
|
||
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_SLOTNUMBER);
|
||
ZwSetValueKey(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&SlotNumber,
|
||
sizeof(SlotNumber)
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is a previously-existing device instance. We need to check and see
|
||
// if it was found at the last enumeration.
|
||
// Now, retrieve the device instance's 'FoundAtEnum' value entry, to see if
|
||
// this device was found at the last enumeration.
|
||
//
|
||
Status = IopGetRegistryValue(DeviceInstanceHandle,
|
||
REGSTR_VALUE_FOUNDATENUM,
|
||
&KeyValueInformation
|
||
);
|
||
if(NT_SUCCESS(Status)) {
|
||
if((KeyValueInformation->Type == REG_DWORD) &&
|
||
(KeyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
FoundAtLastEnum = *(PBOOLEAN)KEY_VALUE_DATA(KeyValueInformation);
|
||
}
|
||
ExFreePool(KeyValueInformation);
|
||
}
|
||
}
|
||
#else
|
||
} else {
|
||
|
||
//
|
||
// See if one of the device instance keys under this device key matches the
|
||
// device instance we just enumerated. A match is determined by:
|
||
//
|
||
// (1) Seeing if the BaseDevicePath of the instance key is the same as the
|
||
// DeviceInstancePath of the bus we're enumerating, and
|
||
// (2) Seeing if the SlotNumber of the instance key is the same as the slot
|
||
// we just retrieved the device ID for.
|
||
//
|
||
FindDevInstContext.ReturnStatus = STATUS_SUCCESS;
|
||
FindDevInstContext.BusDeviceInstancePath = &(BusInstanceNode->DeviceInstancePath);
|
||
FindDevInstContext.SlotNumber = SlotNumber;
|
||
RtlInitUnicodeString(&(FindDevInstContext.DeviceInstanceKeyName), NULL);
|
||
|
||
Status = IopApplyFunctionToSubKeys(DeviceIdHandle,
|
||
NULL,
|
||
KEY_READ,
|
||
TRUE,
|
||
PiFindEnumeratedDeviceInstance,
|
||
&FindDevInstContext
|
||
);
|
||
if(!(NT_SUCCESS(Status) &&
|
||
NT_SUCCESS(Status = FindDevInstContext.ReturnStatus))) {
|
||
|
||
ContinueEnumeration = FALSE;
|
||
goto ContinueWithNextSlot1;
|
||
}
|
||
|
||
NewDeviceInstance = (FindDevInstContext.DeviceInstanceKeyName.Buffer) ? FALSE : TRUE;
|
||
}
|
||
#endif
|
||
|
||
#if 0
|
||
|
||
FoundAtLastEnum = FALSE;
|
||
|
||
if(NewDeviceInstance) {
|
||
//
|
||
// This is a new device instance, so create a new device instance subkey
|
||
// and initialize its values.
|
||
//
|
||
Instance = 0;
|
||
Status = IopGetRegistryValue(DeviceIdHandle,
|
||
REGSTR_VALUE_NEXT_INSTANCE,
|
||
&KeyValueInformation
|
||
);
|
||
if(NT_SUCCESS(Status)) {
|
||
if((KeyValueInformation->Type == REG_DWORD) &&
|
||
(KeyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
Instance = *(PULONG)KEY_VALUE_DATA(KeyValueInformation);
|
||
}
|
||
ExFreePool(KeyValueInformation);
|
||
}
|
||
|
||
PiUlongToInstanceKeyUnicodeString(&(FindDevInstContext.DeviceInstanceKeyName),
|
||
UnicodeBuffer,
|
||
sizeof(UnicodeBuffer),
|
||
Instance
|
||
);
|
||
Status = IopOpenRegistryKeyPersist(&DeviceInstanceHandle,
|
||
DeviceIdHandle,
|
||
&(FindDevInstContext.DeviceInstanceKeyName),
|
||
KEY_ALL_ACCESS,
|
||
TRUE,
|
||
NULL
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
ContinueEnumeration = FALSE;
|
||
goto ContinueWithNextSlot1;
|
||
}
|
||
|
||
//
|
||
// Increment NextInstance, and save it.
|
||
//
|
||
Instance++;
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_NEXT_INSTANCE);
|
||
Status = ZwSetValueKey(DeviceIdHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&Instance,
|
||
sizeof(Instance)
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
ContinueEnumeration = FALSE;
|
||
goto ContinueWithNextSlot3;
|
||
}
|
||
#if 0
|
||
//
|
||
// Now, initialize value entries for this new instance. (ignoring return status)
|
||
//
|
||
DwordValue = 1;
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_NEWINSTANCE);
|
||
ZwSetValueKey(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&DwordValue,
|
||
sizeof(DwordValue)
|
||
);
|
||
#endif
|
||
//
|
||
// (Note: I can use the bus DeviceInstancePath unicode string to set the REG_SZ
|
||
// value below because I always ensure that these unicode strings are NULL-terminated.)
|
||
//
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_BASEDEVICEPATH);
|
||
ZwSetValueKey(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
BusInstanceNode->DeviceInstancePath.Buffer,
|
||
(ULONG)(BusInstanceNode->DeviceInstancePath.MaximumLength)
|
||
);
|
||
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_SLOTNUMBER);
|
||
ZwSetValueKey(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&SlotNumber,
|
||
sizeof(SlotNumber)
|
||
);
|
||
|
||
} else {
|
||
//
|
||
// This is a previously-existing device instance. We need to check and see
|
||
// if it was found at the last enumeration.
|
||
//
|
||
// First, open the key.
|
||
//
|
||
Status = IopOpenRegistryKey(&DeviceInstanceHandle,
|
||
DeviceIdHandle,
|
||
&(FindDevInstContext.DeviceInstanceKeyName),
|
||
KEY_ALL_ACCESS,
|
||
FALSE
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
ContinueEnumeration = FALSE;
|
||
goto ContinueWithNextSlot2;
|
||
}
|
||
|
||
//
|
||
// Now, retrieve the device instance's 'FoundAtEnum' value entry, to see if
|
||
// this device was found at the last enumeration.
|
||
//
|
||
Status = IopGetRegistryValue(DeviceInstanceHandle,
|
||
REGSTR_VALUE_FOUNDATENUM,
|
||
&KeyValueInformation
|
||
);
|
||
if(NT_SUCCESS(Status)) {
|
||
if((KeyValueInformation->Type == REG_DWORD) &&
|
||
(KeyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
FoundAtLastEnum = *(PBOOLEAN)KEY_VALUE_DATA(KeyValueInformation);
|
||
}
|
||
ExFreePool(KeyValueInformation);
|
||
}
|
||
}
|
||
#endif // 0
|
||
|
||
//
|
||
// We now have both the device id and instance name, so build up the complete
|
||
// device instance path (relative to HKLM\System\Enum), and add it to our
|
||
// linked list of enumerated device instances.
|
||
//
|
||
if(CurDevInstPathNode = ExAllocatePool(PagedPool, sizeof(PI_DEVINST_PATH_NODE))) {
|
||
//
|
||
// Determine the required buffer length for the device instance path
|
||
// (including separating backslash and terminating NULL).
|
||
//
|
||
RequiredBufferLength = DeviceIdString.Length
|
||
+ DeviceInstanceString.Length
|
||
+ 2 * sizeof(WCHAR);
|
||
|
||
if(!(DevInstRegPath = ExAllocatePool(PagedPool, RequiredBufferLength))) {
|
||
ExFreePool(CurDevInstPathNode);
|
||
CurDevInstPathNode = NULL;
|
||
}
|
||
}
|
||
|
||
if(!CurDevInstPathNode) {
|
||
ContinueEnumeration = FALSE;
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto ContinueWithNextSlot3;
|
||
}
|
||
|
||
RtlMoveMemory(DevInstRegPath,
|
||
DeviceIdString.Buffer,
|
||
DeviceIdString.Length
|
||
);
|
||
DevInstRegPath[CB_TO_CWC(DeviceIdString.Length)] = OBJ_NAME_PATH_SEPARATOR;
|
||
RtlMoveMemory(&(DevInstRegPath[CB_TO_CWC(DeviceIdString.Length) + 1]),
|
||
DeviceInstanceString.Buffer,
|
||
DeviceInstanceString.Length
|
||
);
|
||
DevInstRegPath[CB_TO_CWC(RequiredBufferLength) - 1] = UNICODE_NULL;
|
||
|
||
CurDevInstPathNode->DeviceInstancePath.Buffer = DevInstRegPath;
|
||
CurDevInstPathNode->DeviceInstancePath.Length =
|
||
(CurDevInstPathNode->DeviceInstancePath.MaximumLength = (USHORT)RequiredBufferLength)
|
||
- sizeof(UNICODE_NULL);
|
||
|
||
//
|
||
// Initially, assume the device's status is DeviceStatusOK.
|
||
//
|
||
PreviousDeviceStatus = DeviceStatusOK;
|
||
|
||
//
|
||
// If the device instance was present during the last enumeration, then we need
|
||
// to check its status, to make sure it hasn't been marked as removed. If it has,
|
||
// then we should leave it alone.
|
||
//
|
||
if(FoundAtLastEnum) {
|
||
Status = PiGetOrSetDeviceInstanceStatus(&(CurDevInstPathNode->DeviceInstancePath),
|
||
&PreviousDeviceStatus,
|
||
FALSE // don't set it--just get it
|
||
);
|
||
if(NT_SUCCESS(Status) && (PreviousDeviceStatus == DeviceStatusRemoved)) {
|
||
goto ContinueWithNextSlot4;
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// We need to retrieve from the device its Configuration, ConfigurationVector,
|
||
// and DetectSignature.
|
||
//
|
||
// First, get the device's current configuration (this will also be stored as the
|
||
// device's detect signature.
|
||
//
|
||
Status = PiGetDeviceResourceInformation(
|
||
DeviceHandler,
|
||
BCTL_QUERY_DEVICE_RESOURCES,
|
||
&DeviceResourceList,
|
||
&DeviceResourceListSize
|
||
);
|
||
if(NT_SUCCESS(Status)) {
|
||
//
|
||
// Now, retrieve the device's configuration vector.
|
||
//
|
||
Status = PiGetDeviceResourceInformation(
|
||
DeviceHandler,
|
||
BCTL_QUERY_DEVICE_RESOURCE_REQUIREMENTS,
|
||
&DeviceResourceRequirements,
|
||
&DeviceResourceRequirementsSize
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
ExFreePool(DeviceResourceList);
|
||
}
|
||
}
|
||
|
||
if(!NT_SUCCESS(Status)) {
|
||
|
||
if(Status == STATUS_NO_SUCH_DEVICE) {
|
||
//
|
||
// The device handler object we passed in no longer valid. This means
|
||
// that the device that we retrieved the Plug & Play IDs for is no longer
|
||
// in the slot. Since this device was not present at last enumeration,
|
||
// there is no need to perform any work here--no one was ever aware of its
|
||
// presence.
|
||
//
|
||
// BUGBUG (lonnym): The entries added to the registry for this device
|
||
// should really be cleaned up.
|
||
//
|
||
|
||
} else {
|
||
ContinueEnumeration = FALSE;
|
||
}
|
||
|
||
goto ContinueWithNextSlot4;
|
||
}
|
||
|
||
//
|
||
// Write out registry values (ignoring return status)
|
||
//
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_CONFIGURATION);
|
||
ZwSetValueKey(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_RESOURCE_LIST,
|
||
DeviceResourceList,
|
||
DeviceResourceListSize
|
||
);
|
||
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_DETECTSIGNATURE);
|
||
ZwSetValueKey(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_RESOURCE_LIST,
|
||
DeviceResourceList,
|
||
DeviceResourceListSize
|
||
);
|
||
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_CONFIGURATIONVECTOR);
|
||
ZwSetValueKey(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_RESOURCE_REQUIREMENTS_LIST,
|
||
DeviceResourceRequirements,
|
||
DeviceResourceRequirementsSize
|
||
);
|
||
|
||
ExFreePool(DeviceResourceList);
|
||
ExFreePool(DeviceResourceRequirements);
|
||
|
||
//
|
||
// Now, write out a REG_MULTI_SZ list of compatible PnP IDs for this device,
|
||
// as retrieved earlier.
|
||
//
|
||
// Traverse the list once to determine the size of the buffer needed.
|
||
//
|
||
RequiredBufferLength = sizeof(UNICODE_NULL);
|
||
|
||
for(CurListEntry = CompatIdListHead.Flink;
|
||
CurListEntry != &CompatIdListHead;
|
||
CurListEntry = CurListEntry->Flink) {
|
||
|
||
CurDevIdNode = CONTAINING_RECORD(CurListEntry,
|
||
PI_COMPAT_ID_NODE,
|
||
ListEntry
|
||
);
|
||
|
||
RequiredBufferLength +=
|
||
(CurDevIdNode->IdStringLength =
|
||
CWC_TO_CB(wcslen(CurDevIdNode->BctlDeviceId) + 1));
|
||
}
|
||
|
||
if(CompatIdMultiSzBuffer = ExAllocatePool(PagedPool, RequiredBufferLength)) {
|
||
|
||
CurMultiSzPos = 0;
|
||
|
||
for(CurListEntry = CompatIdListHead.Flink;
|
||
CurListEntry != &CompatIdListHead;
|
||
CurListEntry = CurListEntry->Flink) {
|
||
|
||
CurDevIdNode = CONTAINING_RECORD(CurListEntry,
|
||
PI_COMPAT_ID_NODE,
|
||
ListEntry
|
||
);
|
||
|
||
RtlMoveMemory(&(CompatIdMultiSzBuffer[CurMultiSzPos]),
|
||
CurDevIdNode->BctlDeviceId,
|
||
CurDevIdNode->IdStringLength
|
||
);
|
||
|
||
CurMultiSzPos += CB_TO_CWC(CurDevIdNode->IdStringLength);
|
||
}
|
||
|
||
CompatIdMultiSzBuffer[CurMultiSzPos] = UNICODE_NULL;
|
||
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_DEVICE_IDS);
|
||
ZwSetValueKey(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_MULTI_SZ,
|
||
CompatIdMultiSzBuffer,
|
||
RequiredBufferLength
|
||
);
|
||
|
||
ExFreePool(CompatIdMultiSzBuffer);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set the 'FoundAtEnum' value entry to TRUE.
|
||
//
|
||
DwordValue = 1;
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_FOUNDATENUM);
|
||
ZwSetValueKey(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&DwordValue,
|
||
sizeof(DwordValue)
|
||
);
|
||
|
||
//
|
||
// Now we need to invoke the callback routine for this device instance key.
|
||
//
|
||
ContinueEnumeration = DeviceInstanceCallbackRoutine(
|
||
DeviceInstanceHandle,
|
||
&(CurDevInstPathNode->DeviceInstancePath),
|
||
Context,
|
||
FoundAtLastEnum ? EnumDeviceStatePreviouslyEnumerated
|
||
: EnumDeviceStateNewlyArrived,
|
||
PreviousDeviceStatus
|
||
);
|
||
|
||
//
|
||
// We're all done with this device instance. Find the device instance registry
|
||
// path in our list of previously enumerated devices, and remove it.
|
||
//
|
||
for(PrevEnumDeviceIndex = 0;
|
||
PrevEnumDeviceIndex < PrevEnumDeviceCount;
|
||
PrevEnumDeviceIndex++) {
|
||
|
||
if(PrevEnumDeviceList[PrevEnumDeviceIndex].Buffer) {
|
||
|
||
if(RtlEqualUnicodeString(&(PrevEnumDeviceList[PrevEnumDeviceIndex]),
|
||
&(CurDevInstPathNode->DeviceInstancePath),
|
||
TRUE)) {
|
||
//
|
||
// We have a match, so clear this entry out of our list.
|
||
//
|
||
ExFreePool(PrevEnumDeviceList[PrevEnumDeviceIndex].Buffer);
|
||
RtlInitUnicodeString(&(PrevEnumDeviceList[PrevEnumDeviceIndex]), NULL);
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Finally, add this device instance path node to our list of enumerated devices,
|
||
// and clear the node pointer so we won't try to free it.
|
||
//
|
||
InsertTailList(&DevInstPathListHead, &(CurDevInstPathNode->ListEntry));
|
||
CurDevInstPathNode = NULL;
|
||
|
||
//
|
||
// Reset Status to STATUS_SUCCESS, so that it will be set correctly should we
|
||
// drop out of the loop.
|
||
//
|
||
Status = STATUS_SUCCESS;
|
||
|
||
ContinueWithNextSlot4:
|
||
|
||
if(CurDevInstPathNode) {
|
||
//
|
||
// If this pointer is non-NULL, that means we aborted the processing of this
|
||
// device instance before invoking the callback routine for it.
|
||
// If so, then we need to free the memory associated with this node.
|
||
//
|
||
ExFreePool(CurDevInstPathNode->DeviceInstancePath.Buffer);
|
||
ExFreePool(CurDevInstPathNode);
|
||
}
|
||
|
||
ContinueWithNextSlot3:
|
||
|
||
ZwClose(DeviceInstanceHandle);
|
||
|
||
ContinueWithNextSlot2:
|
||
|
||
RtlFreeUnicodeString(&DeviceInstanceString);
|
||
|
||
ContinueWithNextSlot1:
|
||
|
||
ZwClose(DeviceIdHandle);
|
||
|
||
ContinueWithNextSlot0:
|
||
|
||
PiFreeCompatibleIdList(&CompatIdListHead);
|
||
ObDereferenceObject(DeviceHandler);
|
||
}
|
||
|
||
if(NT_SUCCESS(Status)) {
|
||
//
|
||
// We've been removing device instance entries from our previously enumerated
|
||
// device list as we (re)enumerated them, so what we're left with are those
|
||
// device instances that have been removed since last enumeration. We will
|
||
// now invoke the callback routine (if supplied) for each of these.
|
||
//
|
||
PreviousDeviceStatus = DeviceStatusRemoved;
|
||
for(PrevEnumDeviceIndex = 0;
|
||
(ContinueEnumeration && (PrevEnumDeviceIndex < PrevEnumDeviceCount));
|
||
PrevEnumDeviceIndex++) {
|
||
|
||
if(PrevEnumDeviceList[PrevEnumDeviceIndex].Buffer) {
|
||
//
|
||
// Open a handle for this device instance that has been removed.
|
||
//
|
||
Status = IopOpenRegistryKey(&DeviceInstanceHandle,
|
||
SystemEnumHandle,
|
||
&(PrevEnumDeviceList[PrevEnumDeviceIndex]),
|
||
KEY_ALL_ACCESS,
|
||
FALSE
|
||
);
|
||
if(NT_SUCCESS(Status)) {
|
||
//
|
||
// Set the 'FoundAtEnum' value entry for this device to FALSE.
|
||
//
|
||
DwordValue = 0;
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_FOUNDATENUM);
|
||
ZwSetValueKey(DeviceInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&DwordValue,
|
||
sizeof(DwordValue)
|
||
);
|
||
//
|
||
// Set the device status for this instance to DeviceStatusRemoved
|
||
// (ignoring return status).
|
||
//
|
||
PiGetOrSetDeviceInstanceStatus(&(PrevEnumDeviceList[PrevEnumDeviceIndex]),
|
||
&PreviousDeviceStatus,
|
||
TRUE // set it
|
||
);
|
||
//
|
||
// Now invoke the callback.
|
||
//
|
||
ContinueEnumeration = DeviceInstanceCallbackRoutine(
|
||
DeviceInstanceHandle,
|
||
&(PrevEnumDeviceList[PrevEnumDeviceIndex]),
|
||
Context,
|
||
EnumDeviceStateRemoved,
|
||
PreviousDeviceStatus
|
||
);
|
||
|
||
ZwClose(DeviceInstanceHandle);
|
||
|
||
} else {
|
||
//
|
||
// We can't open this device instance key, so we'll simply ignore
|
||
// this instance, and continue on.
|
||
//
|
||
#if DBG
|
||
DbgPrint("PiEnumerateSystemBus: Can't open key for removed device\n");
|
||
DbgPrint(" %wZ (status %x).\n",
|
||
&(PrevEnumDeviceList[PrevEnumDeviceIndex]),
|
||
Status
|
||
);
|
||
#endif // DBG
|
||
}
|
||
|
||
ExFreePool(PrevEnumDeviceList[PrevEnumDeviceIndex].Buffer);
|
||
RtlInitUnicodeString(&(PrevEnumDeviceList[PrevEnumDeviceIndex]), NULL);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now we need to write back to the registry a new AttachedComponents value entry
|
||
// for the parent bus. The list of device instance paths to be written is composed
|
||
// of two sets--the linked list of devices we successfully processed
|
||
// (DevInstPathListHead), and the array of left-over removed devices that were not
|
||
// processed because the callback routine aborted the processing prematurely. We
|
||
// must keep the latter list, because we have to ensure that every device change
|
||
// (arrival or removal) is passed to a callback routine to give appropriate notification.
|
||
//
|
||
RequiredBufferLength = 0;
|
||
|
||
for(CurListEntry = DevInstPathListHead.Flink;
|
||
CurListEntry != &DevInstPathListHead;
|
||
CurListEntry = CurListEntry->Flink) {
|
||
|
||
RequiredBufferLength += CONTAINING_RECORD(CurListEntry,
|
||
PI_DEVINST_PATH_NODE,
|
||
ListEntry
|
||
)->DeviceInstancePath.MaximumLength;
|
||
}
|
||
|
||
for(Instance = PrevEnumDeviceIndex; Instance < PrevEnumDeviceCount; Instance++) {
|
||
if(PrevEnumDeviceList[Instance].Length) {
|
||
RequiredBufferLength += PrevEnumDeviceList[Instance].MaximumLength;
|
||
}
|
||
}
|
||
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_ATTACHEDCOMPONENTS);
|
||
if(RequiredBufferLength) {
|
||
//
|
||
// Add space for the extra terminating NULL.
|
||
//
|
||
RequiredBufferLength += sizeof(UNICODE_NULL);
|
||
|
||
if(!(DevInstRegPath = ExAllocatePool(PagedPool, RequiredBufferLength))) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto PrepareForReturn2;
|
||
}
|
||
|
||
//
|
||
// We have the buffer, now build up the REG_MULTI_SZ string list within it.
|
||
//
|
||
CurDevInstPos = DevInstRegPath;
|
||
for(CurListEntry = DevInstPathListHead.Flink;
|
||
CurListEntry != &DevInstPathListHead;
|
||
CurListEntry = CurListEntry->Flink) {
|
||
|
||
CurDevInstPathNode = CONTAINING_RECORD(CurListEntry,
|
||
PI_DEVINST_PATH_NODE,
|
||
ListEntry
|
||
);
|
||
RtlMoveMemory(CurDevInstPos,
|
||
CurDevInstPathNode->DeviceInstancePath.Buffer,
|
||
CurDevInstPathNode->DeviceInstancePath.MaximumLength
|
||
);
|
||
|
||
CurDevInstPos += CB_TO_CWC(CurDevInstPathNode->DeviceInstancePath.MaximumLength);
|
||
}
|
||
|
||
for(Instance = PrevEnumDeviceIndex; Instance < PrevEnumDeviceCount; Instance++) {
|
||
|
||
if(PrevEnumDeviceList[Instance].Length) {
|
||
|
||
RtlMoveMemory(CurDevInstPos,
|
||
PrevEnumDeviceList[Instance].Buffer,
|
||
PrevEnumDeviceList[Instance].MaximumLength
|
||
);
|
||
|
||
CurDevInstPos += CB_TO_CWC(PrevEnumDeviceList[Instance].MaximumLength);
|
||
}
|
||
}
|
||
|
||
*CurDevInstPos = UNICODE_NULL;
|
||
|
||
Status = ZwSetValueKey(BusInstanceHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_MULTI_SZ,
|
||
DevInstRegPath,
|
||
RequiredBufferLength
|
||
);
|
||
|
||
ExFreePool(DevInstRegPath);
|
||
|
||
} else {
|
||
//
|
||
// There are no devices on this bus instance, so simply delete the
|
||
// AttachedComponents value entry.
|
||
//
|
||
ZwDeleteValueKey(BusInstanceHandle, &TempUnicodeString);
|
||
}
|
||
}
|
||
|
||
PrepareForReturn2:
|
||
|
||
PiFreeDevInstPathList(&DevInstPathListHead);
|
||
|
||
IopFreeUnicodeStringList(PrevEnumDeviceList, PrevEnumDeviceCount);
|
||
|
||
if(SlotNumberBuffer) {
|
||
ExFreePool(SlotNumberBuffer);
|
||
}
|
||
|
||
PrepareForReturn1:
|
||
|
||
ZwClose(BusInstanceHandle);
|
||
|
||
PrepareForReturn0:
|
||
|
||
HalDereferenceBusHandler (BusHandler);
|
||
ZwClose(SystemEnumHandle);
|
||
|
||
return Status;
|
||
}
|
||
|
||
VOID
|
||
PiFreeCompatibleIdList(
|
||
IN OUT PLIST_ENTRY CompatIdListHead
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees the memory allocated for all nodes in the specified
|
||
linked list of compatible Plug & Play IDs. When the function returns,
|
||
the list is empty.
|
||
|
||
Arguments:
|
||
|
||
CompatIdListHead - Supplies a pointer to the head of the linked list of
|
||
compatible IDs.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY CurrentListEntry;
|
||
PPI_COMPAT_ID_NODE NodeToDelete;
|
||
|
||
while(!IsListEmpty(CompatIdListHead)) {
|
||
|
||
CurrentListEntry = RemoveHeadList(CompatIdListHead);
|
||
|
||
NodeToDelete = CONTAINING_RECORD(CurrentListEntry,
|
||
PI_COMPAT_ID_NODE,
|
||
ListEntry
|
||
);
|
||
//
|
||
// First, free the DeviceId structure, then the node itself.
|
||
//
|
||
ExFreePool(NodeToDelete->BctlDeviceId);
|
||
ExFreePool(NodeToDelete);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
PiFreeDevInstPathList(
|
||
IN OUT PLIST_ENTRY DevInstPathListHead
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees the memory allocated for all nodes in the specified
|
||
linked list of device instance paths. When the function returns, the
|
||
list is empty.
|
||
|
||
Arguments:
|
||
|
||
DevInstPathListHead - Supplies a pointer to the head of the linked list of
|
||
device instance paths.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY CurrentListEntry;
|
||
PPI_DEVINST_PATH_NODE NodeToDelete;
|
||
|
||
while(!IsListEmpty(DevInstPathListHead)) {
|
||
|
||
CurrentListEntry = RemoveHeadList(DevInstPathListHead);
|
||
|
||
NodeToDelete = CONTAINING_RECORD(CurrentListEntry,
|
||
PI_DEVINST_PATH_NODE,
|
||
ListEntry
|
||
);
|
||
//
|
||
// First, free the DeviceInstancePath string buffer, then the node itself.
|
||
//
|
||
ExFreePool(NodeToDelete->DeviceInstancePath.Buffer);
|
||
ExFreePool(NodeToDelete);
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
PiGetDeviceId(
|
||
IN PDEVICE_HANDLER_OBJECT DeviceHandler,
|
||
IN ULONG IdIndex,
|
||
OUT PWCHAR *DeviceIdBuffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine retrieves a particular compatible Plug & Play ID for the device
|
||
located on the specified slot in the specified bus.
|
||
|
||
The caller must free the (PagedPool) memory allocated for the device id buffer.
|
||
|
||
Arguments:
|
||
|
||
DeviceHandler - supplies a pointer to the device handler object where the device is located
|
||
|
||
IdIndex - supplies the index of the ID we want to retrieve. If
|
||
this index is 0, it is DeviceId. If -1, it is UNIQUE device id. Otherwise, it is
|
||
compatible device id and if the index is greater than the number of compatible ID
|
||
the device supplies, then the function will return STATUS_NO_MORE_ENTRIES.
|
||
|
||
DeviceIdBuffer - receives a buffer that contains the device id as a NULL-terminated
|
||
unicode string.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS code indicating whether or not the function was successful
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PVOID Buffer;
|
||
ULONG BufferLength = 0, RequiredLength;
|
||
|
||
do {
|
||
//
|
||
// On input, DeviceIdBuffer contains a ULONG specifying the device ID index to return.
|
||
// If we have a buffer, then store the index there, otherwise, we'll just use the ULONG
|
||
// containing the index itself as the buffer.
|
||
//
|
||
if(BufferLength) {
|
||
*((PULONG)Buffer) = IdIndex;
|
||
RequiredLength = BufferLength;
|
||
} else {
|
||
Buffer = &IdIndex;
|
||
RequiredLength = sizeof(ULONG);
|
||
}
|
||
|
||
if (IdIndex == (ULONG) -1) {
|
||
Status = HalDeviceControl(DeviceHandler,
|
||
(PDEVICE_OBJECT)NULL,
|
||
BCTL_QUERY_DEVICE_UNIQUE_ID,
|
||
Buffer,
|
||
&RequiredLength,
|
||
NULL,
|
||
(PDEVICE_CONTROL_COMPLETION)NULL
|
||
);
|
||
} else {
|
||
Status = HalDeviceControl(DeviceHandler,
|
||
(PDEVICE_OBJECT)NULL,
|
||
BCTL_QUERY_DEVICE_ID,
|
||
Buffer,
|
||
&RequiredLength,
|
||
NULL,
|
||
(PDEVICE_CONTROL_COMPLETION)NULL
|
||
);
|
||
}
|
||
if(!NT_SUCCESS(Status)) {
|
||
|
||
if(BufferLength) {
|
||
ExFreePool(Buffer);
|
||
BufferLength = 0;
|
||
}
|
||
|
||
if(Status == STATUS_BUFFER_TOO_SMALL) {
|
||
|
||
Buffer = ExAllocatePool(PagedPool, RequiredLength);
|
||
|
||
if(Buffer) {
|
||
BufferLength = RequiredLength;
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
}
|
||
|
||
} while(Status == STATUS_BUFFER_TOO_SMALL);
|
||
|
||
if(NT_SUCCESS(Status)) {
|
||
PNP_ASSERT(BufferLength, "PiGetCompatibleDeviceId:No buffer returned.\n");
|
||
*DeviceIdBuffer = Buffer;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiGetDeviceResourceInformation(
|
||
IN PDEVICE_HANDLER_OBJECT DeviceHandler,
|
||
IN ULONG ControlCode,
|
||
OUT PVOID *Buffer,
|
||
OUT PULONG BufferLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine retrieves resource information for a particular device instance
|
||
(via HalSlotControl). Either a CM_RESOURCE_LIST or an
|
||
IO_RESOURCE_REQUIREMENTS_LIST may be retrieved.
|
||
|
||
The caller must free the (PagedPool) memory allocated for the buffer.
|
||
|
||
Arguments:
|
||
|
||
DeviceHandler - supplies a pointer to the device handler object where the device is located
|
||
|
||
ControlCode - supplies the BCTL_ control code specifying which type of
|
||
information to retrieve. May be either BCTL_QUERY_DEVICE_RESOURCES or
|
||
BCTL_QUERY_DEVICE_RESOURCES_REQUIREMENTS.
|
||
|
||
Buffer - receives a pointer to an allocated buffer containing the requested
|
||
resource information for the specified device instance.
|
||
|
||
BufferLength - receives the size of the allocated Buffer (in bytes).
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS code indicating whether or not the function was successful
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PVOID TempBuffer;
|
||
ULONG TempBufferLength = 0, RequiredLength, Junk;
|
||
|
||
//
|
||
// Make sure we have a request we know how to handle.
|
||
//
|
||
if(!((ControlCode == BCTL_QUERY_DEVICE_RESOURCES) ||
|
||
(ControlCode == BCTL_QUERY_DEVICE_RESOURCE_REQUIREMENTS))) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
do {
|
||
//
|
||
// On input, TempBuffer contains a ULONG specifying the device cookie
|
||
// (to verify we're still talking about the same device).
|
||
// If we have a buffer, then store the index there, otherwise, we'll
|
||
// just use the ULONG containing the cookie itself as the buffer.
|
||
//
|
||
if(TempBufferLength) {
|
||
RequiredLength = TempBufferLength;
|
||
} else {
|
||
TempBuffer = &Junk;
|
||
RequiredLength = sizeof(ULONG);
|
||
}
|
||
|
||
Status = HalDeviceControl(DeviceHandler,
|
||
(PDEVICE_OBJECT)NULL,
|
||
ControlCode,
|
||
TempBuffer,
|
||
&RequiredLength,
|
||
NULL,
|
||
(PDEVICE_CONTROL_COMPLETION)NULL
|
||
);
|
||
|
||
if(!NT_SUCCESS(Status)) {
|
||
|
||
if(TempBufferLength) {
|
||
ExFreePool(TempBuffer);
|
||
TempBufferLength = 0;
|
||
}
|
||
|
||
if(Status == STATUS_BUFFER_TOO_SMALL) {
|
||
|
||
TempBuffer = (PWCHAR)ExAllocatePool(PagedPool, RequiredLength);
|
||
|
||
if(TempBuffer) {
|
||
TempBufferLength = RequiredLength;
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
}
|
||
} while(Status == STATUS_BUFFER_TOO_SMALL);
|
||
|
||
if(NT_SUCCESS(Status)) {
|
||
PNP_ASSERT(TempBufferLength,
|
||
"PiGetDeviceResourceInformation:No buffer returned.\n");
|
||
*Buffer = TempBuffer;
|
||
*BufferLength = RequiredLength;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
#if 0
|
||
|
||
BOOLEAN
|
||
PiFindEnumeratedDeviceInstance(
|
||
IN HANDLE DeviceInstanceHandle,
|
||
IN PUNICODE_STRING DeviceInstanceName,
|
||
IN OUT PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is a callback function for IopApplyFunctionToSubKeys.
|
||
It is called for each instance of a particular device (i.e., subkeys
|
||
under HKLM\System\Enum\<bus>\<device>). Its purpose is to determine
|
||
whether a particular device instance matches the one specified in the
|
||
context structure. A match is determined by whether the device instance
|
||
has the same BaseDevicePath (i.e., parent device) and slot number as
|
||
the device instance being searched for.
|
||
|
||
NOTE: The PnP device-specific registry resource must be held for (at least)
|
||
shared (read) access before invoking this routine.
|
||
|
||
Arguments:
|
||
|
||
DeviceInstanceHandle - Supplies a handle to the current device instance.
|
||
|
||
DeviceInstanceName - Supplies the name of this device instance key.
|
||
|
||
Context - Supplies a pointer to a PI_FIND_DEVICE_INSTANCE_CONTEXT
|
||
structure with the following fields:
|
||
|
||
NTSTATUS ReturnStatus - Fill this in with the NT error status code
|
||
if an error occurs that aborts the search. This is assumed to
|
||
be initialized to STATUS_SUCCESS when this routine is called.
|
||
|
||
PUNICODE_STRING BusDeviceInstancePath - Supplies the registry path
|
||
(relative to HKLM\System\Enum) of the bus device instance where
|
||
the device being searched for is located.
|
||
|
||
ULONG SlotNumber - Supplies the bus slot number where the device
|
||
being searched for is located.
|
||
|
||
UNICODE_STRING DeviceInstanceKeyName - If the current device instance
|
||
matches the one being searched for, then this unicode string
|
||
is initialized with a copy the device instance key name.
|
||
|
||
NOTE: It is the caller's responsibility to free the (PagedPool) memory
|
||
allocated for the DeviceInstanceKeyName buffer.
|
||
|
||
Returns:
|
||
|
||
TRUE to continue the enumeration.
|
||
FALSE to abort it. The routine should abort the search if it discovers the
|
||
matching device instance key.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PPI_FIND_DEVICE_INSTANCE_CONTEXT MyContext = (PPI_FIND_DEVICE_INSTANCE_CONTEXT)Context;
|
||
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
||
UNICODE_STRING BaseDevicePathString;
|
||
BOOLEAN Match;
|
||
|
||
//
|
||
// Retrieve the 'BaseDevicePath' value entry for this device instance.
|
||
//
|
||
Status = IopGetRegistryValue(DeviceInstanceHandle,
|
||
REGSTR_VALUE_BASEDEVICEPATH,
|
||
&KeyValueInformation
|
||
);
|
||
if(NT_SUCCESS(Status)) {
|
||
|
||
if(KeyValueInformation->Type == REG_SZ) {
|
||
IopRegistryDataToUnicodeString(&BaseDevicePathString,
|
||
(PWSTR)KEY_VALUE_DATA(KeyValueInformation),
|
||
KeyValueInformation->DataLength
|
||
);
|
||
} else {
|
||
BaseDevicePathString.Length = 0;
|
||
}
|
||
|
||
if(!BaseDevicePathString.Length) {
|
||
ExFreePool(KeyValueInformation);
|
||
Status = STATUS_INVALID_PLUGPLAY_DEVICE_PATH;
|
||
}
|
||
}
|
||
|
||
if(!NT_SUCCESS(Status)) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Now compare the retrieved BaseDevicePath with the target one.
|
||
//
|
||
Match = RtlEqualUnicodeString(&BaseDevicePathString, MyContext->BusDeviceInstancePath, TRUE);
|
||
ExFreePool(KeyValueInformation);
|
||
if(!Match) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Next, retrieve the 'SlotNumber' value entry for the device instance.
|
||
//
|
||
Status = IopGetRegistryValue(DeviceInstanceHandle,
|
||
REGSTR_VALUE_SLOTNUMBER,
|
||
&KeyValueInformation
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
return TRUE;
|
||
}
|
||
|
||
if((KeyValueInformation->Type == REG_DWORD) &&
|
||
(KeyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
Match = (*(PULONG)KEY_VALUE_DATA(KeyValueInformation) == MyContext->SlotNumber);
|
||
} else {
|
||
Match = FALSE;
|
||
}
|
||
|
||
ExFreePool(KeyValueInformation);
|
||
|
||
if(Match) {
|
||
//
|
||
// Store a copy of the device instance key name in the context structure.
|
||
//
|
||
if(!IopConcatenateUnicodeStrings(&(MyContext->DeviceInstanceKeyName),
|
||
DeviceInstanceName,
|
||
NULL)) {
|
||
//
|
||
// Report failure, since we found the matching device instance, but didn't
|
||
// have enough memory to save its name in our context structure.
|
||
//
|
||
MyContext->ReturnStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
return (!Match);
|
||
}
|
||
#endif //0
|
||
|
||
BOOLEAN
|
||
PiDeviceInstanceEnumNotification(
|
||
IN HANDLE DevInstKeyHandle,
|
||
IN PUNICODE_STRING DevInstRegPath,
|
||
IN OUT PVOID Context,
|
||
IN PI_ENUM_DEVICE_STATE EnumDeviceState,
|
||
IN DEVICE_STATUS DeviceStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is a callback function for PiEnumerateSystemBus. It is used as the
|
||
notification routine during enumeration by NtEnumerate. It's purpose is to give
|
||
device arrival and removal notifications, as appropriate, for each device instance
|
||
it is invoked for.
|
||
|
||
Arguments:
|
||
|
||
DevInstKeyHandle - Supplies a handle to the key of the enumerated device instance.
|
||
|
||
DevInstRegPath - Supplies the registry path of the device instance key (relative to
|
||
HKLM\System\Enum).
|
||
|
||
Context - Unused.
|
||
|
||
EnumDeviceState - Supplies an ordinal describing the circumstances prompting notification
|
||
of this device instance (previously enumerated, newly arrived, removed).
|
||
|
||
DeviceStatus - Supplies the current status of the device.
|
||
|
||
Returns:
|
||
|
||
TRUE to continue the enumeration.
|
||
FALSE to abort it.
|
||
|
||
--*/
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER(Context);
|
||
|
||
//
|
||
// BUGBUG (lonnym): when APC notification support is in place, this function must
|
||
// queue up arrival/removal notification messages. For now, simply output to the
|
||
// debugger what we're being called for
|
||
//
|
||
|
||
#if DBG
|
||
DbgPrint("PiDeviceInstanceEnumNotification: %wZ, EnumState = %u, Status = %u\n",
|
||
DevInstRegPath,
|
||
(ULONG)EnumDeviceState,
|
||
(ULONG)DeviceStatus
|
||
);
|
||
#endif // DBG
|
||
|
||
return TRUE;
|
||
}
|
||
#endif // _PNP_POWER_
|
||
|