mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1681 lines
63 KiB
1681 lines
63 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
enumsys.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the implementation routines for full Plug & Play
|
|
enumeration of all buses/adapter devices.
|
|
|
|
Author:
|
|
|
|
Lonny McMichael (lonnym) 02/23/95
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// Not included in normal builds for now
|
|
//
|
|
#ifdef _PNP_POWER_
|
|
|
|
//
|
|
// Define the newly-found bus instance node that is returned in
|
|
// the context supplied to the PiDevInstEnumRecurse callback routine.
|
|
//
|
|
typedef struct _PI_NEW_BUS_INSTANCE_NODE {
|
|
PPLUGPLAY_BUS_ENUMERATOR BusEnumeratorNode;
|
|
UNICODE_STRING DeviceInstancePath;
|
|
LIST_ENTRY NewBusInstanceListEntry;
|
|
} PI_NEW_BUS_INSTANCE_NODE, *PPI_NEW_BUS_INSTANCE_NODE;
|
|
|
|
//
|
|
// Define the context structure for the PiDevInstEnumRecurse
|
|
// callback routine.
|
|
//
|
|
typedef struct _PI_DEVINST_ENUM_RECURSE_CONTEXT {
|
|
NTSTATUS ReturnStatus;
|
|
LIST_ENTRY NewBusInstanceListHead;
|
|
} PI_DEVINST_ENUM_RECURSE_CONTEXT, *PPI_DEVINST_ENUM_RECURSE_CONTEXT;
|
|
|
|
//
|
|
// Define utility functions internal to this file.
|
|
//
|
|
BOOLEAN
|
|
PiDevInstEnumRecurse(
|
|
IN HANDLE DevInstKeyHandle,
|
|
IN PUNICODE_STRING DevInstRegPath,
|
|
IN OUT PVOID Context,
|
|
IN PI_ENUM_DEVICE_STATE EnumDeviceState,
|
|
IN DEVICE_STATUS DeviceStatus
|
|
);
|
|
|
|
VOID
|
|
PiDetectRootBuses(
|
|
IN HANDLE SystemEnumHandle,
|
|
OUT PULONG NewBusesFound
|
|
);
|
|
|
|
NTSTATUS
|
|
PiFindDeviceInstanceInBusList(
|
|
IN HANDLE DevInstKeyHandle,
|
|
IN PUNICODE_STRING DevInstRegPath,
|
|
OUT PPLUGPLAY_BUS_ENUMERATOR *BusEnumeratorNode,
|
|
OUT PPLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR *BusInstanceNode
|
|
);
|
|
|
|
NTSTATUS
|
|
PiGetHalBusInformationForDeviceInstance(
|
|
IN HANDLE DevInstKeyHandle,
|
|
OUT PPLUGPLAY_BUS_INSTANCE BusInstance,
|
|
OUT PBUS_DATA_TYPE BusDataType
|
|
);
|
|
|
|
VOID
|
|
PiGetBusDescription(
|
|
INTERFACE_TYPE InterfaceType,
|
|
WCHAR BusName[MAX_BUS_NAME]
|
|
);
|
|
|
|
BOOLEAN
|
|
PiMatchDeviceIdWithBusId (
|
|
PUNICODE_STRING BusId,
|
|
PUNICODE_STRING DeviceId,
|
|
BOOLEAN IgnoreCase
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, PpPerformFullEnumeration)
|
|
#pragma alloc_text(PAGE, PiDevInstEnumRecurse)
|
|
#pragma alloc_text(PAGE, PiDetectRootBuses)
|
|
#pragma alloc_text(PAGE, PiFindDeviceInstanceInBusList)
|
|
#pragma alloc_text(PAGE, PiGetHalBusInformationForDeviceInstance)
|
|
#pragma alloc_text(PAGE, PiGetBusDescription)
|
|
#pragma alloc_text(PAGE, PiMatchDeviceIdWithBusId)
|
|
#endif
|
|
|
|
NTSTATUS
|
|
PpPerformFullEnumeration(
|
|
IN BOOLEAN DetectRootBuses
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This Plug and Play Manager API performs a full enumeration of all buses
|
|
and adapter-type devices in the system. Each device enumerated is reported
|
|
via a callback supplied to PiEnumerateSystemBus). If the device enumerated
|
|
has a PnP ID that matches one of the installed bus extender's compatible
|
|
ID list, then this device is assumed to be a bus, and is added to the bus
|
|
extender's list of buses (if it's not already there), then this bus instance
|
|
is enumerated recursively.
|
|
|
|
Arguments:
|
|
|
|
DetectRootBuses - If TRUE, then following enumeration of all known buses, the
|
|
detect entry point for each bus extender will be called to see if any
|
|
more root-level buses can be found. Any buses that are discovered in this
|
|
manner will be enumerated in the manner described above.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code indicating whether or not the function was successful
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY CurBusListEntry, CurBusInstListEntry;
|
|
PPLUGPLAY_BUS_ENUMERATOR CurBusEnumeratorNode;
|
|
PPLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR CurBusInstanceNode;
|
|
ULONG Pass = 0, NewBusesFound, ServiceInstanceCount, TmpDwordValue;
|
|
PI_DEVINST_ENUM_RECURSE_CONTEXT DevInstEnumRecurseContext;
|
|
PPI_NEW_BUS_INSTANCE_NODE NewBusInstanceNode;
|
|
HANDLE ServiceEnumHandle, SystemEnumHandle, DevInstHandle;
|
|
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
|
UNICODE_STRING TempUnicodeString;
|
|
WCHAR ValueNameString[20];
|
|
PDRIVER_OBJECT DriverObject;
|
|
|
|
//
|
|
// Acquire the PnP bus list resource for exclusive (write) access.
|
|
//
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceExclusive(&PpBusResource, TRUE);
|
|
|
|
//
|
|
// We will also need exclusive access to the PnP registry resource. We may have
|
|
// to release and re-acquire the resource during the course of enumeration, since
|
|
// a bus extender's AddDevice and DetectBusDevices entry points cannot be called
|
|
// with this resource already acquired.
|
|
//
|
|
ExAcquireResourceExclusive(&PpRegistryDeviceResource, TRUE);
|
|
|
|
//
|
|
// Open up the key HKLM\System\CurrentControlSetEnum for read access, to be used
|
|
// as a base handle for opening up bus device instance keys below.
|
|
//
|
|
// NOTE: Technically, I really shouldn't be keeping this open, because I may be
|
|
// releasing/re-acquiring the PnP registry resource one or more times during the
|
|
// course of enumeration. However, there should never be anyone deleting this key,
|
|
// and I won't do anything with the handle unless I have the resource acquired.
|
|
//
|
|
Status = IopOpenRegistryKey(&SystemEnumHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_READ,
|
|
FALSE
|
|
);
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto PrepareForReturn;
|
|
}
|
|
|
|
//
|
|
// First, reset the 'Processed' flag for each bus instance node to FALSE.
|
|
//
|
|
for(CurBusListEntry = PpBusListHead.Flink;
|
|
CurBusListEntry != &PpBusListHead;
|
|
CurBusListEntry = CurBusListEntry->Flink) {
|
|
|
|
CurBusEnumeratorNode = CONTAINING_RECORD(CurBusListEntry,
|
|
PLUGPLAY_BUS_ENUMERATOR,
|
|
BusEnumeratorListEntry
|
|
);
|
|
|
|
for(CurBusInstListEntry = CurBusEnumeratorNode->BusInstanceListEntry.Flink;
|
|
CurBusInstListEntry != &(CurBusEnumeratorNode->BusInstanceListEntry);
|
|
CurBusInstListEntry = CurBusInstListEntry->Flink) {
|
|
|
|
CONTAINING_RECORD(CurBusInstListEntry,
|
|
PLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR,
|
|
BusInstanceListEntry
|
|
)->Processed = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now, perform the recursive enumeration of all buses/adapter devices in the
|
|
// system. This is done in a mult-pass manner, where one pass enumerates each
|
|
// bus instance that hasn't already been processed, traversing 'down the tree'
|
|
// for each bus instance (depth-first) as far as it can. The end condition for
|
|
// the recursion is when a device is reached that is either (a) an adapter-type
|
|
// device or (b) a bus-type device which hasn't already been initialized. Upon
|
|
// return from an enumeration pass, the context structure will include a list of
|
|
// all non-initialized bus instances encountered. These instances will be
|
|
// initialized, and the process will continue with the next pass. We will break
|
|
// out of the loop when we complete a pass where no new (unininitialized) bus
|
|
// instances are discovered.
|
|
//
|
|
// First, initialize our context structure.
|
|
//
|
|
DevInstEnumRecurseContext.ReturnStatus = STATUS_SUCCESS;
|
|
InitializeListHead(&(DevInstEnumRecurseContext.NewBusInstanceListHead));
|
|
|
|
do {
|
|
for(CurBusListEntry = PpBusListHead.Flink;
|
|
CurBusListEntry != &PpBusListHead;
|
|
CurBusListEntry = CurBusListEntry->Flink) {
|
|
|
|
CurBusEnumeratorNode = CONTAINING_RECORD(CurBusListEntry,
|
|
PLUGPLAY_BUS_ENUMERATOR,
|
|
BusEnumeratorListEntry
|
|
);
|
|
|
|
for(CurBusInstListEntry = CurBusEnumeratorNode->BusInstanceListEntry.Flink;
|
|
CurBusInstListEntry != &(CurBusEnumeratorNode->BusInstanceListEntry);
|
|
CurBusInstListEntry = CurBusInstListEntry->Flink) {
|
|
|
|
CurBusInstanceNode = CONTAINING_RECORD(CurBusInstListEntry,
|
|
PLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR,
|
|
BusInstanceListEntry
|
|
);
|
|
|
|
//
|
|
// For pass 0, we only want to enumerate root buses (we will find all the
|
|
// children while enumerating these).
|
|
//
|
|
if((Pass > 0) || (CurBusInstanceNode->RootBus)) {
|
|
|
|
if(!CurBusInstanceNode->Processed) {
|
|
|
|
Status = PiEnumerateSystemBus(CurBusInstanceNode,
|
|
PiDevInstEnumRecurse,
|
|
&DevInstEnumRecurseContext
|
|
);
|
|
|
|
if(!(NT_SUCCESS(Status) &&
|
|
NT_SUCCESS(Status = DevInstEnumRecurseContext.ReturnStatus))) {
|
|
//
|
|
// BUGBUG (lonnym): What should we do if this fails? For now,
|
|
// just output a debug message, and keep going.
|
|
//
|
|
#if DBG
|
|
DbgPrint(
|
|
"PpPerformFullEnumeration: Enumeration failed for bus %wZ (status %x).\n",
|
|
&(CurBusInstanceNode->DeviceInstancePath),
|
|
Status
|
|
);
|
|
#endif // DBG
|
|
}
|
|
CurBusInstanceNode->Processed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// At this point, we will have processed all buses in our bus list except for
|
|
// those buses that have previously been removed, and for whom removal notification
|
|
// has already been given. There will only be unprocessed buses during pass 0.
|
|
// We will now set the 'Processed' flag for such bus instances to TRUE, and print
|
|
// out a debug message, indicating that these bus instances are being skipped.
|
|
//
|
|
if(!Pass) {
|
|
|
|
for(CurBusListEntry = PpBusListHead.Flink;
|
|
CurBusListEntry != &PpBusListHead;
|
|
CurBusListEntry = CurBusListEntry->Flink) {
|
|
|
|
CurBusEnumeratorNode = CONTAINING_RECORD(CurBusListEntry,
|
|
PLUGPLAY_BUS_ENUMERATOR,
|
|
BusEnumeratorListEntry
|
|
);
|
|
|
|
for(CurBusInstListEntry = CurBusEnumeratorNode->BusInstanceListEntry.Flink;
|
|
CurBusInstListEntry != &(CurBusEnumeratorNode->BusInstanceListEntry);
|
|
CurBusInstListEntry = CurBusInstListEntry->Flink) {
|
|
|
|
CurBusInstanceNode = CONTAINING_RECORD(CurBusInstListEntry,
|
|
PLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR,
|
|
BusInstanceListEntry
|
|
);
|
|
|
|
if(!CurBusInstanceNode->Processed) {
|
|
CurBusInstanceNode->Processed = TRUE;
|
|
#if DBG
|
|
DbgPrint("PpPerformFullEnumeration: %wZ previously removed.\n",
|
|
&(CurBusInstanceNode->DeviceInstancePath)
|
|
);
|
|
#endif // DBG
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now, traverse the list of bus instances we discovered during this pass, and
|
|
// initialize each one (includes setting up the bus list structures
|
|
// AND calling the pertinent bus extender's 'add device' entry point).
|
|
//
|
|
NewBusesFound = 0;
|
|
|
|
while(!IsListEmpty(&(DevInstEnumRecurseContext.NewBusInstanceListHead))) {
|
|
//
|
|
// First, create a bus instance node to hold information about this new bus.
|
|
//
|
|
if(!(CurBusInstanceNode = ExAllocatePool(PagedPool,
|
|
sizeof(PLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR)))) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SkipRegistryMods;
|
|
}
|
|
|
|
CurBusInstListEntry = RemoveHeadList(&(DevInstEnumRecurseContext.NewBusInstanceListHead));
|
|
NewBusInstanceNode = CONTAINING_RECORD(CurBusInstListEntry,
|
|
PI_NEW_BUS_INSTANCE_NODE,
|
|
NewBusInstanceListEntry
|
|
);
|
|
//
|
|
// Fill in what we can for this new bus instance.
|
|
//
|
|
CurBusInstanceNode->DeviceInstancePath = NewBusInstanceNode->DeviceInstancePath;
|
|
CurBusInstanceNode->RootBus = TRUE;
|
|
CurBusInstanceNode->Processed = FALSE;
|
|
|
|
//
|
|
// Open the volatile Enum key for this bus instance's service entry, and add
|
|
// a new instance for this bus.
|
|
//
|
|
Status = IopOpenServiceEnumKeys(&(NewBusInstanceNode->BusEnumeratorNode->ServiceName),
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&ServiceEnumHandle,
|
|
TRUE
|
|
);
|
|
if(NT_SUCCESS(Status)) {
|
|
//
|
|
// Now retrieve the current count of service instances.
|
|
//
|
|
ServiceInstanceCount = 0; // assume none.
|
|
|
|
Status = IopGetRegistryValue(ServiceEnumHandle,
|
|
REGSTR_VALUE_COUNT,
|
|
&KeyValueInformation
|
|
);
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
if((KeyValueInformation->Type == REG_DWORD) &&
|
|
(KeyValueInformation->DataLength >= sizeof(ULONG))) {
|
|
|
|
ServiceInstanceCount = *(PULONG)KEY_VALUE_DATA(KeyValueInformation);
|
|
|
|
}
|
|
ExFreePool(KeyValueInformation);
|
|
|
|
} else if(Status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
goto FinishedRegistryMods;
|
|
}
|
|
|
|
//
|
|
// Use ServiceInstanceCount for the bus's service instance ordinal, then
|
|
// increment this value, and save it back.
|
|
//
|
|
CurBusInstanceNode->ServiceInstanceOrdinal = ServiceInstanceCount;
|
|
|
|
ServiceInstanceCount++;
|
|
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_COUNT);
|
|
Status = ZwSetValueKey(ServiceEnumHandle,
|
|
&TempUnicodeString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&ServiceInstanceCount,
|
|
sizeof(ServiceInstanceCount)
|
|
);
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto FinishedRegistryMods;
|
|
}
|
|
|
|
//
|
|
// Now, write out the service instance value entry.
|
|
//
|
|
PiUlongToUnicodeString(&TempUnicodeString,
|
|
ValueNameString,
|
|
sizeof(ValueNameString),
|
|
CurBusInstanceNode->ServiceInstanceOrdinal
|
|
);
|
|
Status = ZwSetValueKey(ServiceEnumHandle,
|
|
&TempUnicodeString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
NewBusInstanceNode->DeviceInstancePath.Buffer,
|
|
NewBusInstanceNode->DeviceInstancePath.MaximumLength
|
|
);
|
|
FinishedRegistryMods:
|
|
|
|
ZwClose(ServiceEnumHandle);
|
|
}
|
|
|
|
SkipRegistryMods:
|
|
|
|
//
|
|
// We are done with the PnP registry resource for the time being. We are forced
|
|
// to release it now, because we are about to call a bus extender's AddDevice entry
|
|
// point. This will, in turn, cause the bus extender to call PnP I/O APIs, such as
|
|
// IoRegisterDevicePath, which require that the registry resource be acquired.
|
|
//
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
//
|
|
// We've done all the necessary registry setup, and we're ready to add
|
|
// this new bus instance to the appropriate bus extender's list of
|
|
// controlled buses.
|
|
//
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
DriverObject = IopReferenceDriverObjectByName (&NewBusInstanceNode->BusEnumeratorNode->DriverName);
|
|
if (DriverObject) {
|
|
Status = DriverObject->DriverExtension->AddDevice(
|
|
&(NewBusInstanceNode->BusEnumeratorNode->ServiceName),
|
|
&CurBusInstanceNode->ServiceInstanceOrdinal
|
|
);
|
|
ObDereferenceObject(DriverObject);
|
|
} else {
|
|
#if DBG
|
|
DbgPrint("PpPerformFullEnumeration: Couldn't reference bus enumerator's driver object by name %wZ (status %x).\n",
|
|
NewBusInstanceNode->BusEnumeratorNode->DriverName.Buffer, Status);
|
|
#endif // DBG
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now, we must re-acquire the PnP registry resource.
|
|
//
|
|
ExAcquireResourceExclusive(&PpRegistryDeviceResource, TRUE);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
//
|
|
// We have successfully added the new bus device instance. We must now finish
|
|
// filling in our bus structure based on the new installed bus information.
|
|
//
|
|
// Open a handle to the device instance key.
|
|
//
|
|
Status = IopOpenRegistryKey(&DevInstHandle,
|
|
SystemEnumHandle,
|
|
&(CurBusInstanceNode->DeviceInstancePath),
|
|
KEY_ALL_ACCESS,
|
|
FALSE
|
|
);
|
|
if(NT_SUCCESS(Status)) {
|
|
Status = PiGetHalBusInformationForDeviceInstance(
|
|
DevInstHandle,
|
|
&(CurBusInstanceNode->BusInstanceInformation),
|
|
&(CurBusInstanceNode->AssociatedConfigurationSpace)
|
|
);
|
|
#if DBG
|
|
if(!NT_SUCCESS(Status)) {
|
|
//
|
|
// This is very bad, because we've initialized the new bus instance,
|
|
// and everything's working fine, but now we can't associate it with
|
|
// its HAL_BUS_INFORMATION structure (thus, we can't figure out what
|
|
// its interface type, bus number, or bus data type is). For now,
|
|
// just output a debug message, and keep plowing ahead.
|
|
//
|
|
DbgPrint("PpPerformFullEnumeration: Can't find associated bus info for %wZ (status %x).\n",
|
|
&(NewBusInstanceNode->DeviceInstancePath),
|
|
Status
|
|
);
|
|
}
|
|
#endif // DBG
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
//
|
|
// Now, write the following value entries out to the bus's device
|
|
// instance key (ignoring return status):
|
|
//
|
|
// InterfaceType = REG_DWORD : <InterfaceType>
|
|
// BusDataType = REG_DWORD : <BusDataType>
|
|
// SystemBusNumber = REG_DWORD : <BusNumber>
|
|
//
|
|
TmpDwordValue =
|
|
(ULONG)(CurBusInstanceNode->BusInstanceInformation.BusType.SystemBusType);
|
|
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_INTERFACETYPE);
|
|
ZwSetValueKey(DevInstHandle,
|
|
&TempUnicodeString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&TmpDwordValue,
|
|
sizeof(TmpDwordValue)
|
|
);
|
|
|
|
TmpDwordValue = (ULONG)(CurBusInstanceNode->AssociatedConfigurationSpace);
|
|
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_BUSDATATYPE);
|
|
ZwSetValueKey(DevInstHandle,
|
|
&TempUnicodeString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&TmpDwordValue,
|
|
sizeof(TmpDwordValue)
|
|
);
|
|
|
|
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_SYSTEMBUSNUMBER);
|
|
ZwSetValueKey(DevInstHandle,
|
|
&TempUnicodeString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&(CurBusInstanceNode->BusInstanceInformation.BusNumber),
|
|
sizeof(ULONG)
|
|
);
|
|
}
|
|
|
|
ZwClose(DevInstHandle);
|
|
}
|
|
}
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
//
|
|
// Increment our count of new buses found, and insert our new
|
|
// PLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR structure into the enumerator's
|
|
// bus instance list.
|
|
//
|
|
NewBusesFound++;
|
|
InsertTailList(&(NewBusInstanceNode->BusEnumeratorNode->BusInstanceListEntry),
|
|
&(CurBusInstanceNode->BusInstanceListEntry)
|
|
);
|
|
} else {
|
|
//
|
|
// Print out a debug message about this failure, and continue on.
|
|
//
|
|
#if DBG
|
|
DbgPrint("PpPerformFullEnumeration: Couldn't add new bus %wZ (status %x).\n",
|
|
&(NewBusInstanceNode->DeviceInstancePath),
|
|
Status
|
|
);
|
|
#endif // DBG
|
|
//
|
|
// We must free the memory allocated for this bus instance
|
|
// (since it isn't going to be used).
|
|
//
|
|
ExFreePool(NewBusInstanceNode->DeviceInstancePath.Buffer);
|
|
if(CurBusInstanceNode) {
|
|
ExFreePool(CurBusInstanceNode);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now, free the memory used for the temporary new bus instance structure.
|
|
//
|
|
ExFreePool(NewBusInstanceNode);
|
|
}
|
|
|
|
if(!NewBusesFound && DetectRootBuses) {
|
|
//
|
|
// We must release the PnP registry resource here as well, because
|
|
// PiDetectRootBuses requires that the resource not be held before calling it.
|
|
//
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
|
|
//
|
|
// If no buses were turned up during this pass, then it's time to call the
|
|
// detection routines for each bus extender, to see if any more root-level
|
|
// buses can be found (if root bus detection was requested).
|
|
//
|
|
PiDetectRootBuses(SystemEnumHandle, &NewBusesFound);
|
|
|
|
//
|
|
// Set 'DetectRootBuses' to FALSE, because we only want to do this once.
|
|
//
|
|
DetectRootBuses = FALSE;
|
|
|
|
//
|
|
// Re-acquire the PnP registry resource.
|
|
//
|
|
ExAcquireResourceExclusive(&PpRegistryDeviceResource, TRUE);
|
|
}
|
|
|
|
Pass++;
|
|
|
|
} while(NewBusesFound);
|
|
|
|
//
|
|
// Regardless of individual successes/failures, we consider the whole enumeration a
|
|
// success if we get to here.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
ZwClose(SystemEnumHandle);
|
|
|
|
PrepareForReturn:
|
|
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
ExReleaseResource(&PpBusResource);
|
|
KeLeaveCriticalRegion();
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
PiDevInstEnumRecurse(
|
|
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 PpPerformFullEnumeration. Its purpose
|
|
is to give device arrival and removal notifications, as appropriate, for each device
|
|
instance it is invoked for. In addition, if it determines that the device it has been
|
|
called for is a bus, then it will perform additional action. If the bus has previously
|
|
been initialized (i.e., a PLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR node already exists for
|
|
it in our bus list), then PiEnumerateSystemBus will be recursively called to enumerate
|
|
all devices on it. If the bus instance does not already exist in our bus list, then a
|
|
new bus instance node will be added to the list contained in the supplied context
|
|
structure, so that the originator of the enumeration may create the newly-discovered
|
|
bus instances.
|
|
|
|
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 - Supplies a pointer to a PI_DEVINST_ENUM_RECURSE_CONTEXT
|
|
structure with the following fields:
|
|
|
|
NTSTATUS ReturnStatus - Fill this in with the NT error status code if an
|
|
error occurs. This is assumed to be initialized to STATUS_SUCCESS
|
|
when this routine is called.
|
|
|
|
LIST_ENTRY NewBusInstanceListHead - If this is a new (i.e., not previously
|
|
enumerated) bus instance, then a PI_NEW_BUS_INSTANCE_NODE is initialized
|
|
with the appropriate information, and added to this list
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PPLUGPLAY_BUS_ENUMERATOR BusEnumeratorNode;
|
|
PPLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR BusInstanceNode;
|
|
PPI_NEW_BUS_INSTANCE_NODE NewBusInstanceNode;
|
|
UNICODE_STRING ValueName;
|
|
|
|
//
|
|
// 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("PiDevInstEnumRecurse: %wZ, EnumState = %u, Status = %u\n",
|
|
DevInstRegPath,
|
|
(ULONG)EnumDeviceState,
|
|
(ULONG)DeviceStatus
|
|
);
|
|
#endif // DBG
|
|
|
|
//
|
|
// Now, try to find this device ID in our bus enumerator list, to see if it's a
|
|
// bus.
|
|
//
|
|
// BUGBUG (lonnym): What if there are multiple matches?
|
|
//
|
|
Status = PiFindDeviceInstanceInBusList(DevInstKeyHandle,
|
|
DevInstRegPath,
|
|
&BusEnumeratorNode,
|
|
&BusInstanceNode
|
|
);
|
|
if(!NT_SUCCESS(Status)) {
|
|
//
|
|
// Then it's not a bus, so we can return now.
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
if(BusInstanceNode) {
|
|
//
|
|
// Then this bus instance already exists, so enumerate it now.
|
|
//
|
|
Status = PiEnumerateSystemBus(BusInstanceNode,
|
|
PiDevInstEnumRecurse,
|
|
Context
|
|
);
|
|
|
|
if(!(NT_SUCCESS(Status) &&
|
|
NT_SUCCESS(Status = ((PPI_DEVINST_ENUM_RECURSE_CONTEXT)Context)->ReturnStatus))) {
|
|
//
|
|
// BUGBUG (lonnym): What should we do if this fails? For now,
|
|
// just output a debug message, and keep going.
|
|
//
|
|
#if DBG
|
|
DbgPrint(
|
|
"PiDevInstEnumRecurse: Enumeration failed for bus %wZ (status %x).\n",
|
|
&(BusInstanceNode->DeviceInstancePath),
|
|
Status
|
|
);
|
|
#endif // DBG
|
|
|
|
//
|
|
// Reset ReturnStatus to TRUE.
|
|
//
|
|
((PPI_DEVINST_ENUM_RECURSE_CONTEXT)Context)->ReturnStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Mark this bus instance as having been processed, and continue enumeration.
|
|
//
|
|
BusInstanceNode->Processed = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If we reached this point, then we have a new bus instance that hasn't been previously
|
|
// initialized. We will now build a new bus instance node for it, and add it to the
|
|
// list contained in the context structure.
|
|
//
|
|
if(NewBusInstanceNode = ExAllocatePool(PagedPool, sizeof(PI_NEW_BUS_INSTANCE_NODE))) {
|
|
|
|
if(!IopConcatenateUnicodeStrings(&(NewBusInstanceNode->DeviceInstancePath),
|
|
DevInstRegPath,
|
|
NULL)) {
|
|
|
|
ExFreePool(NewBusInstanceNode);
|
|
NewBusInstanceNode = NULL;
|
|
}
|
|
}
|
|
|
|
if(!NewBusInstanceNode) {
|
|
//
|
|
// Since we can't build a new bus instance node, simply output
|
|
// a debug message and return.
|
|
//
|
|
#if DBG
|
|
DbgPrint(
|
|
"PiDevInstEnumRecurse: Can't create new bus instance node for bus %wZ.\n",
|
|
&(BusInstanceNode->DeviceInstancePath)
|
|
);
|
|
#endif // DBG
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
NewBusInstanceNode->BusEnumeratorNode = BusEnumeratorNode;
|
|
InsertTailList(&(((PPI_DEVINST_ENUM_RECURSE_CONTEXT)Context)->NewBusInstanceListHead),
|
|
&(NewBusInstanceNode->NewBusInstanceListEntry)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Now write out the following two value entries to the device instance key
|
|
// (ignore return status):
|
|
//
|
|
// Service = REG_SZ : <service>
|
|
// Class = REG_SZ : "System"
|
|
//
|
|
PiWstrToUnicodeString(&ValueName, REGSTR_VALUE_SERVICE);
|
|
ZwSetValueKey(DevInstKeyHandle,
|
|
&ValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
BusEnumeratorNode->ServiceName.Buffer,
|
|
BusEnumeratorNode->ServiceName.MaximumLength
|
|
);
|
|
|
|
PiWstrToUnicodeString(&ValueName, REGSTR_VALUE_CLASS);
|
|
ZwSetValueKey(DevInstKeyHandle,
|
|
&ValueName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
REGSTR_KEY_SYSTEM,
|
|
sizeof(REGSTR_KEY_SYSTEM)
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
PiDetectRootBuses(
|
|
IN HANDLE SystemEnumHandle,
|
|
OUT PULONG NewBusesFound
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine traverses the list of bus enumerators, calling each one's detection
|
|
routine (if supplied), and recording each new bus instance found.
|
|
|
|
The caller must have acquired the PnP bus list resource for exclusive (write) access.
|
|
|
|
Arguments:
|
|
|
|
SystemEnumHandle - Supplies an opened handle to HKLM\System\Enum. Note that the caller
|
|
must NOT have the registry resource required when calling this routine, therefore
|
|
it must take care not to use this handle while the registry resource is not
|
|
acquired.
|
|
|
|
NewBusesFound - Receives the number of new buses that were found and successfully
|
|
registered.
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY CurBusListEntry, CurBusInstListEntry;
|
|
PPLUGPLAY_BUS_ENUMERATOR CurBusEnumeratorNode;
|
|
ULONG BusInstanceOrdinal, BusNumber, TmpDwordValue;
|
|
INTERFACE_TYPE InterfaceType;
|
|
BUS_DATA_TYPE BusDataType;
|
|
HANDLE ServiceEnumHandle, DevInstHandle;
|
|
WCHAR ValueNameString[20];
|
|
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
|
UNICODE_STRING DeviceInstancePath, TempUnicodeString;
|
|
PPLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR NewBusInstanceNode;
|
|
PDRIVER_OBJECT DriverObject;
|
|
PLUGPLAY_BUS_INSTANCE BusInstance;
|
|
|
|
*NewBusesFound = 0;
|
|
|
|
for(CurBusListEntry = PpBusListHead.Flink;
|
|
CurBusListEntry != &PpBusListHead;
|
|
CurBusListEntry = CurBusListEntry->Flink) {
|
|
|
|
CurBusEnumeratorNode = CONTAINING_RECORD(CurBusListEntry,
|
|
PLUGPLAY_BUS_ENUMERATOR,
|
|
BusEnumeratorListEntry
|
|
);
|
|
DriverObject = IopReferenceDriverObjectByName(&CurBusEnumeratorNode->DriverName);
|
|
if (!DriverObject || DriverObject->DriverExtension->AddDevice == NULL) {
|
|
//
|
|
// BUGBUG: if we can not reference the driver object of the enumerator.
|
|
// we probably want to remove it from our list.
|
|
//
|
|
|
|
if (DriverObject) {
|
|
ObDereferenceObject(DriverObject);
|
|
}
|
|
|
|
//
|
|
// Then this bus extender doesn't provide a bus detection entry
|
|
// point, so we just skip it.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Actually, I shouldn't open this handle until I've acquired the registry
|
|
// resource below (done for each detected bus), but I'm safe in doing
|
|
// this since I won't be using this handle outside of the protected
|
|
// code.
|
|
//
|
|
Status = IopOpenServiceEnumKeys(&(CurBusEnumeratorNode->ServiceName),
|
|
KEY_READ,
|
|
NULL,
|
|
&ServiceEnumHandle,
|
|
TRUE
|
|
);
|
|
if(!NT_SUCCESS(Status)) {
|
|
#if DBG
|
|
DbgPrint("PiDetectRootBuses: Couldn't open %wZ\\Enum (status %x).\n",
|
|
&(CurBusEnumeratorNode->ServiceName),
|
|
Status
|
|
);
|
|
#endif // DBG
|
|
|
|
goto ContinueWithNextEnumerator;
|
|
}
|
|
|
|
//
|
|
// Call the bus extender's AddDevice entry point repeatedly, until it
|
|
// runs out of buses it can detect.
|
|
//
|
|
|
|
BusInstanceOrdinal = PLUGPLAY_NO_INSTANCE;
|
|
while(NT_SUCCESS(Status = DriverObject->DriverExtension->AddDevice(
|
|
&(CurBusEnumeratorNode->ServiceName),
|
|
&BusInstanceOrdinal))) {
|
|
//
|
|
// Now register this information for the bus's device instance key in the
|
|
// registry (first, we must acquire the PnP registry resource for exclusive
|
|
// (write) access.
|
|
//
|
|
ExAcquireResourceExclusive(&PpRegistryDeviceResource, TRUE);
|
|
|
|
DevInstHandle = NULL;
|
|
|
|
swprintf(ValueNameString, REGSTR_VALUE_STANDARD_ULONG_FORMAT, BusInstanceOrdinal);
|
|
Status = IopGetRegistryValue(ServiceEnumHandle,
|
|
ValueNameString,
|
|
&KeyValueInformation
|
|
);
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
if(KeyValueInformation->Type == REG_SZ) {
|
|
IopRegistryDataToUnicodeString(&DeviceInstancePath,
|
|
(PWSTR)KEY_VALUE_DATA(KeyValueInformation),
|
|
KeyValueInformation->DataLength
|
|
);
|
|
} else {
|
|
DeviceInstancePath.Length = 0;
|
|
}
|
|
|
|
if(DeviceInstancePath.Length) {
|
|
//
|
|
// Now, open up the device instance key.
|
|
//
|
|
Status = IopOpenRegistryKey(&DevInstHandle,
|
|
SystemEnumHandle,
|
|
&DeviceInstancePath,
|
|
KEY_ALL_ACCESS,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
if(!DevInstHandle) {
|
|
ExFreePool(KeyValueInformation);
|
|
}
|
|
}
|
|
|
|
if(DevInstHandle) {
|
|
|
|
Status = PiGetHalBusInformationForDeviceInstance(
|
|
DevInstHandle,
|
|
&BusInstance,
|
|
&BusDataType
|
|
);
|
|
#if DBG
|
|
if(!NT_SUCCESS(Status)) {
|
|
//
|
|
// This is very bad, because we've initialized the new bus instance,
|
|
// and everything's working fine, but now we can't associate it with
|
|
// its HAL_BUS_INFORMATION structure (thus, we can't figure out what
|
|
// its interface type, bus number, or bus data type is). For now,
|
|
// just output a debug message, and keep plowing ahead.
|
|
//
|
|
DbgPrint("PiDetectBootBuses: Can't find associated bus info for %wZ (status %x).\n",
|
|
&DeviceInstancePath,
|
|
Status
|
|
);
|
|
}
|
|
#endif // DBG
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
InterfaceType = BusInstance.BusType.SystemBusType;
|
|
BusNumber = BusInstance.BusNumber;
|
|
//
|
|
// Write the following value entries to the device instance key
|
|
// (ignore return status):
|
|
//
|
|
// InterfaceType = REG_DWORD : <InterfaceType>
|
|
// BusDataType = REG_DWORD : <BusDataType>
|
|
// SystemBusNumber = REG_DWORD : <BusNumber>
|
|
//
|
|
TmpDwordValue = (ULONG)InterfaceType;
|
|
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_INTERFACETYPE);
|
|
ZwSetValueKey(DevInstHandle,
|
|
&TempUnicodeString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&TmpDwordValue,
|
|
sizeof(TmpDwordValue)
|
|
);
|
|
|
|
TmpDwordValue = (ULONG)BusDataType;
|
|
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_BUSDATATYPE);
|
|
ZwSetValueKey(DevInstHandle,
|
|
&TempUnicodeString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&TmpDwordValue,
|
|
sizeof(TmpDwordValue)
|
|
);
|
|
|
|
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_SYSTEMBUSNUMBER);
|
|
ZwSetValueKey(DevInstHandle,
|
|
&TempUnicodeString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&BusNumber,
|
|
sizeof(BusNumber)
|
|
);
|
|
}
|
|
ZwClose(DevInstHandle);
|
|
}
|
|
|
|
//
|
|
// We're done with the registry for this bus instance, so release the resource.
|
|
//
|
|
ExReleaseResource(&PpRegistryDeviceResource);
|
|
|
|
if(!DevInstHandle) {
|
|
//
|
|
// We encountered some problem that kept us from performing the registry
|
|
// modifications for this new bus instance, so just abort processing at
|
|
// this point, and continue.
|
|
//
|
|
#if DBG
|
|
DbgPrint("PiDetectRootBuses: Couldn't find/open device instance key for\n");
|
|
DbgPrint(" %wZ, instance %u.\n",
|
|
&(CurBusEnumeratorNode->ServiceName),
|
|
BusInstanceOrdinal
|
|
);
|
|
#endif // DBG
|
|
BusInstanceOrdinal = PLUGPLAY_NO_INSTANCE;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now, create a new PLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR for this bus instance,
|
|
// and add it to the bus enumerator's bus list.
|
|
//
|
|
NewBusInstanceNode = ExAllocatePool(PagedPool,
|
|
sizeof(PLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR)
|
|
);
|
|
if(!NewBusInstanceNode) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ContinueWithNextDetection;
|
|
}
|
|
|
|
if(!IopConcatenateUnicodeStrings(&(NewBusInstanceNode->DeviceInstancePath),
|
|
&DeviceInstancePath,
|
|
NULL)) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ExFreePool(NewBusInstanceNode);
|
|
goto ContinueWithNextDetection;
|
|
}
|
|
|
|
NewBusInstanceNode->RootBus = TRUE;
|
|
NewBusInstanceNode->Processed = FALSE;
|
|
NewBusInstanceNode->ServiceInstanceOrdinal = BusInstanceOrdinal;
|
|
NewBusInstanceNode->AssociatedConfigurationSpace = BusDataType;
|
|
NewBusInstanceNode->BusInstanceInformation.BusNumber = BusNumber;
|
|
NewBusInstanceNode->BusInstanceInformation.BusType.BusClass = SystemBus;
|
|
NewBusInstanceNode->BusInstanceInformation.BusType.SystemBusType = InterfaceType;
|
|
PiGetBusDescription(InterfaceType,
|
|
NewBusInstanceNode->BusInstanceInformation.BusName
|
|
);
|
|
|
|
//
|
|
// Finally, insert this node at the end of the bus enumerator's list.
|
|
//
|
|
InsertTailList(&(CurBusEnumeratorNode->BusInstanceListEntry),
|
|
&(NewBusInstanceNode->BusInstanceListEntry)
|
|
);
|
|
|
|
(*NewBusesFound)++;
|
|
|
|
ContinueWithNextDetection:
|
|
|
|
BusInstanceOrdinal = PLUGPLAY_NO_INSTANCE;
|
|
ExFreePool(KeyValueInformation);
|
|
}
|
|
|
|
#if DBG
|
|
if(Status != STATUS_NO_MORE_ENTRIES) {
|
|
//
|
|
// Then an error occurred during detection--simply output a debug message
|
|
// and continue.
|
|
//
|
|
DbgPrint("PiDetectRootBuses: Detection failed for extender %wZ (status %x).\n",
|
|
&(CurBusEnumeratorNode->ServiceName),
|
|
Status
|
|
);
|
|
}
|
|
#endif // DBG
|
|
|
|
ZwClose(ServiceEnumHandle);
|
|
|
|
ContinueWithNextEnumerator:
|
|
ObDereferenceObject (DriverObject);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
PiFindDeviceInstanceInBusList(
|
|
IN HANDLE DevInstKeyHandle,
|
|
IN PUNICODE_STRING DevInstRegPath,
|
|
OUT PPLUGPLAY_BUS_ENUMERATOR *BusEnumeratorNode,
|
|
OUT PPLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR *BusInstanceNode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine traverses the list of bus enumerators, examining each one to see if
|
|
one of its compatible PnP IDs matches with one of the compatible IDs for the
|
|
specified device instance. If a match is found, then a pointer to that bus enumerator
|
|
node will be returned. If a compatible bus enumerator exists, the routine will also
|
|
search through that enumerator's bus instance list, to see if this bus instance is
|
|
already in the list. If so, it will return a pointer to the bus instance as well.
|
|
(NOTE: If a bus enumerator node whose driver object supplies no "AddDevice" entry
|
|
point is found, then this match will only be returned if the bus instance is already
|
|
in the bus enumerator's bus instance list. In other words, if you have a new bus
|
|
instance, then you are guaranteed to be returned a bus extender node to which this
|
|
new bus instance may be added.)
|
|
|
|
The caller must have acquired the PnP bus list resource for (at least) shared
|
|
(read) access.
|
|
|
|
Arguments:
|
|
|
|
DevInstKeyHandle - Supplies a handle to the opened registry key for this device
|
|
instance.
|
|
|
|
DevInstRegPath - Supplies a pointer to a unicode string containing the registry
|
|
path of the device instance (relative to HKLM\System\Enum).
|
|
|
|
BusEnumeratorNode - If a compatible bus enumerator is found, this pointer will be
|
|
set to point to the matching bus enumerator node.
|
|
|
|
BusInstanceNode - If this bus instance is found in the matching bus enumerator's
|
|
bus instance list, then this pointer will be set to point to the matching
|
|
bus instance node.
|
|
|
|
Returns:
|
|
|
|
NTSTATUS code indicating whether or not the function was successful. If the specified
|
|
device instance matches with none of the installed bus enumerators, then
|
|
STATUS_INVALID_PLUGPLAY_DEVICE_PATH will be returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY CurBusListEntry, CurBusInstListEntry;
|
|
PPLUGPLAY_BUS_ENUMERATOR CurBusEnumeratorNode;
|
|
PPLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR CurBusInstanceNode;
|
|
int i, j;
|
|
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
|
PUNICODE_STRING DeviceIdList;
|
|
ULONG DeviceIdCount;
|
|
UNICODE_STRING AlternateIdArray[2];
|
|
BOOLEAN MatchFound;
|
|
PDRIVER_OBJECT DriverObject;
|
|
|
|
//
|
|
// Attempt to retrieve the list of compatible IDs stored in the 'DeviceIDs'
|
|
// value entry in the device instance key. If one cannot be retrieved, then the
|
|
// DevInstRegPath will be used instead.
|
|
//
|
|
Status = IopGetRegistryValue(DevInstKeyHandle,
|
|
REGSTR_VALUE_DEVICE_IDS,
|
|
&KeyValueInformation
|
|
);
|
|
if(NT_SUCCESS(Status)) {
|
|
Status = IopRegMultiSzToUnicodeStrings(KeyValueInformation,
|
|
&DeviceIdList,
|
|
&DeviceIdCount
|
|
);
|
|
ExFreePool(KeyValueInformation);
|
|
}
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
//
|
|
// Then no compatible IDs could be retrieved from the device instance key. As an
|
|
// alternate method, then, we will generate a couple of compatible IDs--one that
|
|
// contains the device registry path (i.e., minus the instance key part), and the
|
|
// other that contains just the device ID. This second string will only be built
|
|
// if the device ID is an EISA identifier (denoted by an asterisk (*) prefix).
|
|
// E.g.:
|
|
//
|
|
// DevInstRegPath = "Root\*PNP0A03\0000" will generate:
|
|
//
|
|
// "Root\*PNP0A03" and "*PNP0A03"
|
|
//
|
|
// BUGBUG (lonnym): This algorithm should be verified with the Win95 team!
|
|
//
|
|
|
|
//
|
|
// First, strip off the trailing instance key name (including the separating
|
|
// backslash, to create our first compatible ID.
|
|
//
|
|
for(i = (int)CB_TO_CWC(DevInstRegPath->Length) - 1;
|
|
((i >= 0) && (DevInstRegPath->Buffer[i] != OBJ_NAME_PATH_SEPARATOR));
|
|
i--
|
|
);
|
|
|
|
#if DBG
|
|
if(i <= 0) {
|
|
DbgPrint("PiFindDeviceInstanceInBusList: invalid device instance path %wZ.\n",
|
|
DevInstRegPath
|
|
);
|
|
DbgBreakPoint();
|
|
}
|
|
#endif // DBG
|
|
|
|
AlternateIdArray[0].Buffer = DevInstRegPath->Buffer;
|
|
AlternateIdArray[0].MaximumLength =
|
|
(AlternateIdArray[0].Length = (USHORT)CWC_TO_CB(i));
|
|
|
|
DeviceIdList = AlternateIdArray;
|
|
DeviceIdCount = 1;
|
|
|
|
//
|
|
// Now scan back to the next backslash, and retrieve the device ID string.
|
|
//
|
|
for(i--; ((i >= 0) && (AlternateIdArray[0].Buffer[i] != OBJ_NAME_PATH_SEPARATOR)); i--);
|
|
|
|
#if DBG
|
|
if(i <= 0) {
|
|
DbgPrint("PiFindDeviceInstanceInBusList: invalid device instance path %wZ.\n",
|
|
DevInstRegPath
|
|
);
|
|
DbgBreakPoint();
|
|
}
|
|
#endif // DBG
|
|
|
|
if(AlternateIdArray[0].Buffer[++i] == L'*') {
|
|
//
|
|
// Then assume we have an EISA ID, and fill in the second compatible ID string
|
|
//
|
|
AlternateIdArray[1].Buffer = &(AlternateIdArray[0].Buffer[i]);
|
|
AlternateIdArray[1].MaximumLength = AlternateIdArray[1].Length
|
|
= AlternateIdArray[0].Length - CWC_TO_CB(i + 1);
|
|
|
|
DeviceIdCount++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now that we have an array of unicode strings representing all compatible PnP IDs
|
|
// for this device, we can search through our bus enumerator list, looking for a
|
|
// match.
|
|
//
|
|
for(CurBusListEntry = PpBusListHead.Flink, MatchFound = FALSE;
|
|
(!MatchFound && (CurBusListEntry != &PpBusListHead));
|
|
CurBusListEntry = CurBusListEntry->Flink) {
|
|
|
|
CurBusEnumeratorNode = CONTAINING_RECORD(CurBusListEntry,
|
|
PLUGPLAY_BUS_ENUMERATOR,
|
|
BusEnumeratorListEntry
|
|
);
|
|
|
|
for(i = 0; i < (int)CurBusEnumeratorNode->PlugPlayIDCount; i++) {
|
|
|
|
for(j = 0; j < (int)DeviceIdCount; j++) {
|
|
|
|
if(PiMatchDeviceIdWithBusId(&(CurBusEnumeratorNode->PlugPlayIDs[i]),
|
|
&(DeviceIdList[j]),
|
|
TRUE)) {
|
|
//
|
|
// We have found a matching bus extender.
|
|
// Now, search through all initialized bus instances for this
|
|
// enumerator, to see if this device instance is there.
|
|
//
|
|
for(CurBusInstListEntry = CurBusEnumeratorNode->BusInstanceListEntry.Flink;
|
|
CurBusInstListEntry != &(CurBusEnumeratorNode->BusInstanceListEntry);
|
|
CurBusInstListEntry = CurBusInstListEntry->Flink) {
|
|
|
|
CurBusInstanceNode = CONTAINING_RECORD(
|
|
CurBusInstListEntry,
|
|
PLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR,
|
|
BusInstanceListEntry
|
|
);
|
|
|
|
if(RtlEqualUnicodeString(DevInstRegPath,
|
|
&(CurBusInstanceNode->DeviceInstancePath),
|
|
TRUE
|
|
)) {
|
|
//
|
|
// This bus instance is already in our list. Store a pointer to
|
|
// this node in the BusInstanceNode output parameter.
|
|
//
|
|
*BusInstanceNode = CurBusInstanceNode;
|
|
MatchFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!MatchFound) {
|
|
DriverObject = IopReferenceDriverObjectByName(&CurBusEnumeratorNode->DriverName);
|
|
if (DriverObject && DriverObject->DriverExtension->AddDevice) {
|
|
|
|
//
|
|
// Since this is bus extender supplies an AddDevice entry point
|
|
// we can consider this a match even though we didn't find a
|
|
// matching bus instance. This allows us to find the proper bus
|
|
// extender whose AddDevice entry point we must call to add this
|
|
// new bus instance.
|
|
//
|
|
MatchFound = TRUE;
|
|
*BusInstanceNode = NULL;
|
|
ObDereferenceObject(DriverObject);
|
|
}
|
|
}
|
|
|
|
if(MatchFound) {
|
|
*BusEnumeratorNode = CurBusEnumeratorNode;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(MatchFound) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we allocated memory for the retrieved compatible ID list, then free it now.
|
|
//
|
|
if(DeviceIdList != AlternateIdArray) {
|
|
IopFreeUnicodeStringList(DeviceIdList, DeviceIdCount);
|
|
}
|
|
|
|
if(MatchFound) {
|
|
return STATUS_SUCCESS;
|
|
} else {
|
|
return STATUS_INVALID_PLUGPLAY_DEVICE_PATH;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
PiGetHalBusInformationForDeviceInstance(
|
|
IN HANDLE DevInstKeyHandle,
|
|
OUT PPLUGPLAY_BUS_INSTANCE BusInstance,
|
|
OUT PBUS_DATA_TYPE BusDataType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds the HAL_BUS_INFORMATION structure associated with the device
|
|
instance whose registry key is given by DevInstHandle. It returns information
|
|
from the HAL structure about the bus instance by filling in a PLUGPLAY_BUS_INSTANCE
|
|
structure and a BUS_DATA_TYPE variable. (NOTE: The matching is accomplished by
|
|
opening the device instance key's NtDevicePaths names, and searching for the resulting
|
|
device object(s) in the DeviceObject field of the HAL_BUS_INFORMATION elements.
|
|
Thus, this call will fail for built-in bus instances, since they do not have a
|
|
device path associated with them.)
|
|
|
|
The caller must have acquired the PnP registry resource for (at least) shared
|
|
(read) access.
|
|
|
|
Arguments:
|
|
|
|
DevInstKeyHandle - Supplies a handle to the opened registry key for this device
|
|
instance.
|
|
|
|
BusInstance - Supplies a pointer to a PLUGPLAY_BUS_INSTANCE structure that will be
|
|
filled in with information on this bus instance, as retrieved from the HAL.
|
|
|
|
BusDataType - Supplies a pointer to a BUS_DATA_TYPE structure that will be filled
|
|
in with the configuration space associated with this bus instance, as retrieved
|
|
from the HAL.
|
|
|
|
Returns:
|
|
|
|
NTSTATUS code indicating whether or not the function was successful. If the specified
|
|
device instance matches with none of the buses returned from the HAL via
|
|
HalQuerySystemInformation (information class HalInstalledBusInformation), then
|
|
STATUS_NO_SUCH_DEVICE will be returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PHAL_BUS_INFORMATION BusInfoList;
|
|
ULONG BusInfoCount, i;
|
|
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
|
PWCHAR p, BufferEnd, StringStart;
|
|
ULONG StringLength;
|
|
UNICODE_STRING DevicePathString;
|
|
PFILE_OBJECT FileObject;
|
|
BOOLEAN DeviceFound;
|
|
PBUS_HANDLER BusHandler;
|
|
|
|
//
|
|
// Retrieve the NT device path(s) from the device instance key (this should've been
|
|
// placed here by the bus extender's calling IoRegisterDevicePath).
|
|
//
|
|
Status = IopGetRegistryValue(DevInstKeyHandle,
|
|
REGSTR_VALUE_NT_PHYSICAL_DEVICE_PATHS,
|
|
&KeyValueInformation
|
|
);
|
|
if(!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
} else if((KeyValueInformation->Type != REG_MULTI_SZ) ||
|
|
(KeyValueInformation->DataLength <= sizeof(WCHAR))) {
|
|
|
|
Status = STATUS_NO_SUCH_DEVICE;
|
|
goto PrepareForReturn;
|
|
}
|
|
|
|
//
|
|
// Now get the current list of installed bus instances, as returned from the HAL.
|
|
//
|
|
if(!NT_SUCCESS(Status = PiGetInstalledBusInformation(&BusInfoList, &BusInfoCount))) {
|
|
goto PrepareForReturn;
|
|
}
|
|
|
|
//
|
|
// For each device path in this list, retrieve the corresponding device object,
|
|
// and search for it in the list of installed buses.
|
|
//
|
|
DeviceFound = FALSE;
|
|
StringStart = p = (PWCHAR)KEY_VALUE_DATA(KeyValueInformation);
|
|
BufferEnd = (PWCHAR)((PUCHAR)p + KeyValueInformation->DataLength);
|
|
while(p < BufferEnd) {
|
|
if(!*p) {
|
|
if(StringLength = (PUCHAR)p - (PUCHAR)StringStart) {
|
|
|
|
DevicePathString.MaximumLength =
|
|
(DevicePathString.Length = (USHORT)StringLength)
|
|
+ sizeof(UNICODE_NULL);
|
|
DevicePathString.Buffer = StringStart;
|
|
|
|
//
|
|
// We have an NT device path, now retrieve the corresponding
|
|
// file object (from which we can get the device object).
|
|
//
|
|
Status = PiGetDeviceObjectFilePointer(&DevicePathString,
|
|
&FileObject
|
|
);
|
|
if(NT_SUCCESS(Status)) {
|
|
//
|
|
// Now, compare this device object pointer with the one specified
|
|
// for each installed bus instance.
|
|
//
|
|
for(i = 0; i < BusInfoCount; i++) {
|
|
|
|
BusHandler = HalReferenceHandlerForBus(
|
|
BusInfoList[i].BusType,
|
|
BusInfoList[i].BusNumber
|
|
);
|
|
if (BusHandler) {
|
|
if (FileObject->DeviceObject == BusHandler->DeviceObject) {
|
|
//
|
|
// Then we've found the bus instance for this device path.
|
|
//
|
|
DeviceFound = TRUE;
|
|
HalDereferenceBusHandler(BusHandler);
|
|
break;
|
|
}
|
|
HalDereferenceBusHandler (BusHandler);
|
|
}
|
|
}
|
|
|
|
ObDereferenceObject(FileObject);
|
|
|
|
if(DeviceFound) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
StringStart = p + 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
p++;
|
|
}
|
|
|
|
if(DeviceFound) {
|
|
//
|
|
// Then fill in our output parameters with the proper values.
|
|
//
|
|
BusInstance->BusNumber = BusInfoList[i].BusNumber;
|
|
BusInstance->BusType.BusClass = SystemBus;
|
|
BusInstance->BusType.SystemBusType = BusInfoList[i].BusType;
|
|
PiGetBusDescription(BusInfoList[i].BusType,
|
|
BusInstance->BusName
|
|
);
|
|
*BusDataType = BusInfoList[i].ConfigurationType;
|
|
} else {
|
|
Status = STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
ExFreePool(BusInfoList);
|
|
|
|
PrepareForReturn:
|
|
|
|
ExFreePool(KeyValueInformation);
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
PiGetBusDescription(
|
|
INTERFACE_TYPE InterfaceType,
|
|
WCHAR BusName[MAX_BUS_NAME]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fills in the BusName character array passed in with a textual
|
|
description of the specified bus type. This function is guaranteed
|
|
to succeed.
|
|
|
|
Arguments:
|
|
|
|
InterfaceType - Supplies the interface type for which a descriptive
|
|
name is to be retrieved.
|
|
|
|
BusName - Receives a NULL-terminated name for the specified bus
|
|
interface type.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// We need a buffer large enough to retrieve the entire KEY_VALUE_INFORMATION
|
|
// structure for the bus identifier value entry (this value entry is a REG_BINARY
|
|
// with 8 bytes in it.
|
|
//
|
|
#define BUS_NAME_BUFFER_SIZE (sizeof(KEY_VALUE_FULL_INFORMATION) \
|
|
+ 8 + (MAX_BUS_NAME * sizeof(WCHAR)))
|
|
|
|
NTSTATUS Status;
|
|
WCHAR Buffer[BUS_NAME_BUFFER_SIZE / sizeof(WCHAR)];
|
|
HANDLE CCSHandle, BusValuesHandle;
|
|
UNICODE_STRING TempUnicodeString;
|
|
ULONG BusNameLength;
|
|
|
|
//
|
|
// Open a handle to HKLM\System\CurrentControlSet\Control\SystemResources\BusValues
|
|
// so that we can retrieve friendly names for the buses we've found.
|
|
//
|
|
Status = IopOpenRegistryKey(&CCSHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSet,
|
|
KEY_READ,
|
|
FALSE
|
|
);
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_PATH_SYSTEM_RESOURCES_BUS_VALUES);
|
|
Status = IopOpenRegistryKey(&BusValuesHandle,
|
|
CCSHandle,
|
|
&TempUnicodeString,
|
|
KEY_READ,
|
|
FALSE
|
|
);
|
|
ZwClose(CCSHandle);
|
|
}
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
Status = IopLookupBusStringFromID(BusValuesHandle,
|
|
InterfaceType,
|
|
Buffer,
|
|
BUS_NAME_BUFFER_SIZE,
|
|
NULL
|
|
);
|
|
ZwClose(BusValuesHandle);
|
|
}
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
BusNameLength = 0;
|
|
do {
|
|
if(BusName[BusNameLength] = Buffer[BusNameLength]) {
|
|
BusNameLength++;
|
|
} else {
|
|
break;
|
|
}
|
|
} while(BusNameLength < MAX_BUS_NAME);
|
|
|
|
if(BusNameLength == MAX_BUS_NAME) {
|
|
BusName[--BusNameLength] = UNICODE_NULL;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We couldn't retrieve a friendly name for this bus--no big deal.
|
|
// We'll just make up one.
|
|
//
|
|
swprintf(BusName, REGSTR_VALUE_INTERFACE_TYPE_FORMAT, (ULONG)InterfaceType);
|
|
}
|
|
|
|
//
|
|
// Don't need this macro defined anymore
|
|
//
|
|
#undef BUS_NAME_BUFFER_SIZE
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
PiMatchDeviceIdWithBusId (
|
|
PUNICODE_STRING BusId,
|
|
PUNICODE_STRING DeviceId,
|
|
BOOLEAN IgnoreCase
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine matches device id with bus id.
|
|
|
|
Arguments:
|
|
|
|
BusId - Supplies a pointer to a unicode string to specify the bus id.
|
|
|
|
DeviceId - Supplies a pointer to a unicode string to specify the device id.
|
|
|
|
IgnoreCase - Supplies a boolean variable to specify if the case sensitivity.
|
|
|
|
Return Value:
|
|
|
|
TRUE - if matches otherwise FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING string1, string2;
|
|
USHORT length;
|
|
|
|
if (BusId->Buffer[0] == L'*') {
|
|
|
|
//
|
|
// If bus device id says it matches any bus...
|
|
//
|
|
|
|
length = BusId->Length - 1 * sizeof(WCHAR); // don't compare the '*'
|
|
|
|
if (DeviceId->Length < length) {
|
|
return FALSE;
|
|
} else {
|
|
string1.Length = string2.Length = length;
|
|
string1.MaximumLength = string2.MaximumLength = length + sizeof(UNICODE_NULL);
|
|
string1.Buffer = BusId->Buffer + 1; // skip the '*'
|
|
string2.Buffer = DeviceId->Buffer + (DeviceId->Length - length) / sizeof(WCHAR);
|
|
return RtlEqualUnicodeString(&string1, &string2, IgnoreCase);
|
|
}
|
|
} else {
|
|
return RtlEqualUnicodeString(BusId, DeviceId, IgnoreCase);
|
|
}
|
|
}
|
|
#endif // _PNP_POWER_
|
|
|