1021 lines
33 KiB
C
1021 lines
33 KiB
C
/*++
|
||
|
||
Copyright (c) 1995 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
internal.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the internal subroutines used by the kernel-mode
|
||
Plug and Play Manager.
|
||
|
||
Author:
|
||
|
||
Lonny McMichael (lonnym) 02/15/95
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
//
|
||
// Not included in normal builds for now
|
||
//
|
||
#ifdef _PNP_POWER_
|
||
|
||
//
|
||
// Define the context structure for the PiFindServiceInstance
|
||
// callback routine.
|
||
//
|
||
typedef struct _PI_FIND_SERVICE_INSTANCE_CONTEXT {
|
||
PUNICODE_STRING DeviceInstancePath;
|
||
BOOLEAN DeviceFound;
|
||
} PI_FIND_SERVICE_INSTANCE_CONTEXT, *PPI_FIND_SERVICE_INSTANCE_CONTEXT;
|
||
|
||
//
|
||
// Define utility functions internal to this file.
|
||
//
|
||
BOOLEAN
|
||
PiFindServiceInstance(
|
||
IN HANDLE DeviceInstanceHandle,
|
||
IN PUNICODE_STRING DeviceInstancePath,
|
||
IN OUT PVOID Context
|
||
);
|
||
#endif // _PNP_POWER_
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, PiRegSzToString)
|
||
#if _PNP_POWER_
|
||
#pragma alloc_text(PAGE, PiBusEnumeratorFromRegistryPath)
|
||
#pragma alloc_text(PAGE, PiGetInstalledBusInformation)
|
||
#pragma alloc_text(PAGE, PiGenerateDeviceInstanceIdentifier)
|
||
#pragma alloc_text(PAGE, PiGetDeviceObjectFilePointer)
|
||
#pragma alloc_text(PAGE, PiGetOrSetDeviceInstanceStatus)
|
||
#pragma alloc_text(PAGE, PiFindServiceInstance)
|
||
#pragma alloc_text(PAGE, PiFindBusInstanceNode)
|
||
|
||
#if 0 // obsolete API
|
||
#pragma alloc_text(PAGE, PiGetDeviceInstanceIdentifier)
|
||
#endif // obsolete API
|
||
|
||
#endif // _PNP_POWER_
|
||
#endif
|
||
|
||
BOOLEAN
|
||
PiRegSzToString(
|
||
IN PWCHAR RegSzData,
|
||
IN ULONG RegSzLength,
|
||
OUT PULONG StringLength OPTIONAL,
|
||
OUT PWSTR *CopiedString OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes as input a REG_SZ data buffer (as returned in the DataOffset area
|
||
of the buffer in a KEY_VALUE_FULL_INFORMATION structure), as well as the length
|
||
of the buffer, in bytes (as specified by the DataLength field in the above mentioned
|
||
struct). It optionally returns the length of the contained string (in bytes), not
|
||
including the terminating NULL, as well as an optional copy of the string itself
|
||
(properly NULL-terminated).
|
||
|
||
It is the responsibility of the caller to free the (PagedPool) buffer allocated
|
||
for the string copy.
|
||
|
||
Arguments:
|
||
|
||
RegSzData - Supplies a pointer to the REG_SZ data buffer.
|
||
|
||
RegSzLength - Supplies the length of the RegSzData buffer, in bytes.
|
||
|
||
StringLength - Optionally supplies a pointer to a variable that will receive
|
||
the length, in bytes, of the string (excluding terminating NULL).
|
||
|
||
CopiedString - Optionally supplies a pointer to a wide character pointer that
|
||
will recieve a (properly NULL-terminated) copy of the specified
|
||
string. If this paramater is NULL, no copy will be made.
|
||
|
||
Return Value:
|
||
|
||
If success, returns TRUE
|
||
|
||
If failure (not able to allocate memory for string copy), returns FALSE
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i, RegSzNumChars = CB_TO_CWC(RegSzLength);
|
||
|
||
//
|
||
// Find the end of the string.
|
||
//
|
||
for(i = 0; ((i < RegSzNumChars) && RegSzData[i]); i++);
|
||
|
||
if(ARGUMENT_PRESENT(StringLength)) {
|
||
*StringLength = CWC_TO_CB(i);
|
||
}
|
||
|
||
if(ARGUMENT_PRESENT(CopiedString)) {
|
||
//
|
||
// Allocate memory for the string (+ terminating NULL)
|
||
//
|
||
if(!(*CopiedString = (PWSTR)ExAllocatePool(PagedPool, CWC_TO_CB(i + 1)))) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Copy the string and NULL-terminate it.
|
||
//
|
||
if(i) {
|
||
RtlMoveMemory(*CopiedString, RegSzData, CWC_TO_CB(i));
|
||
}
|
||
(*CopiedString)[i] = UNICODE_NULL;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
#if _PNP_POWER_
|
||
|
||
PPLUGPLAY_BUS_ENUMERATOR
|
||
PiBusEnumeratorFromRegistryPath(
|
||
IN PUNICODE_STRING ServiceRegistryPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes as input a unicode string representing a registry
|
||
path to a service entry. Alternately, this string may simply include
|
||
the service key name by itself. In the former case, the last
|
||
component of the path is extracted from the string, and is assumed
|
||
to be a service key name.
|
||
|
||
The bus enumerator list is searched for a node matching this service
|
||
key. If found, a pointer to that node is returned.
|
||
|
||
NOTE: The caller of this routine must have acquired the PnP bus
|
||
enumerator list resource for shared (read) access.
|
||
|
||
Arguments:
|
||
|
||
ServiceRegistryPath - Supplies either the full registry path to a
|
||
service key, or the service key by itself. If
|
||
a full path is supplied, the last component
|
||
of the path is extracted, and assumed to be
|
||
the service key name.
|
||
|
||
Return Value:
|
||
|
||
If success, returns a pointer to the matching bus enumerator node.
|
||
|
||
If failure, returns NULL.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG ServiceNameLength = 0;
|
||
PWCHAR ServiceNameStart;
|
||
UNICODE_STRING TempUnicodeString;
|
||
PLIST_ENTRY CurrentListEntry;
|
||
PPLUGPLAY_BUS_ENUMERATOR CurrentNode;
|
||
|
||
//
|
||
// Extract last component from the path
|
||
//
|
||
ServiceNameStart = ServiceRegistryPath->Buffer +
|
||
CB_TO_CWC(ServiceRegistryPath->Length) - 1;
|
||
if(*ServiceNameStart == OBJ_NAME_PATH_SEPARATOR) {
|
||
ServiceNameStart--;
|
||
}
|
||
while(ServiceNameStart >= ServiceRegistryPath->Buffer) {
|
||
if(*ServiceNameStart == OBJ_NAME_PATH_SEPARATOR) {
|
||
ServiceNameStart++;
|
||
break;
|
||
} else {
|
||
ServiceNameLength += sizeof(WCHAR);
|
||
ServiceNameStart--;
|
||
}
|
||
}
|
||
if(ServiceNameStart < ServiceRegistryPath->Buffer) {
|
||
ServiceNameStart++;
|
||
}
|
||
TempUnicodeString.Length = TempUnicodeString.MaximumLength
|
||
= (USHORT)ServiceNameLength;
|
||
TempUnicodeString.Buffer = ServiceNameStart;
|
||
|
||
//
|
||
// Now traverse the bus enumerator list, looking for a matching service name.
|
||
//
|
||
for(CurrentListEntry = PpBusListHead.Flink;
|
||
CurrentListEntry != &PpBusListHead;
|
||
CurrentListEntry = CurrentListEntry->Flink) {
|
||
|
||
CurrentNode = CONTAINING_RECORD(CurrentListEntry,
|
||
PLUGPLAY_BUS_ENUMERATOR,
|
||
BusEnumeratorListEntry
|
||
);
|
||
|
||
if(RtlEqualUnicodeString(&TempUnicodeString,
|
||
&(CurrentNode->ServiceName),
|
||
TRUE
|
||
)) {
|
||
//
|
||
// Found a match.
|
||
//
|
||
return CurrentNode;
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiGetInstalledBusInformation(
|
||
OUT PHAL_BUS_INFORMATION *BusInformation,
|
||
OUT PULONG BusCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns an array of all bus instances for which a bus handler
|
||
has been registered (as retrieved by calling HalQuerySystemInformation for
|
||
information class HalInstalledBusInformation). The routine allocates the
|
||
necessary buffer to contain this array, and returns it, along with a count
|
||
of the number of buses installed.
|
||
|
||
It is the caller's responsibility to free the (PagedPool) buffer allocated
|
||
by this routine.
|
||
|
||
Arguments:
|
||
|
||
BusInformation - Receives the list of bus instances.
|
||
|
||
BusCount - Receives the number of installed buses returned in the BusInformation
|
||
buffer.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating whether the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PHAL_BUS_INFORMATION BusInfoBuffer = NULL;
|
||
ULONG BufferLength = 0, RequiredLength;
|
||
|
||
do {
|
||
if(BufferLength) {
|
||
if(!(BusInfoBuffer = ExAllocatePool(PagedPool, BufferLength))) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
Status = HalQuerySystemInformation(HalInstalledBusInformation,
|
||
BufferLength,
|
||
BusInfoBuffer,
|
||
&RequiredLength
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
|
||
if(BufferLength) {
|
||
ExFreePool(BusInfoBuffer);
|
||
}
|
||
|
||
if(Status == STATUS_BUFFER_TOO_SMALL) {
|
||
BufferLength = RequiredLength;
|
||
} else {
|
||
return Status;
|
||
}
|
||
}
|
||
|
||
} while(Status == STATUS_BUFFER_TOO_SMALL);
|
||
|
||
if(BufferLength) {
|
||
*BusInformation = BusInfoBuffer;
|
||
} else {
|
||
*BusInformation = NULL;
|
||
}
|
||
*BusCount = RequiredLength / sizeof(HAL_BUS_INFORMATION);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
#if 0 // obsolete API
|
||
NTSTATUS
|
||
PiGetDeviceInstanceIdentifier(
|
||
IN PUNICODE_STRING ServiceKeyName,
|
||
IN ULONG InstanceNumber,
|
||
IN ULONG HwProfileId,
|
||
OUT PWCHAR InstanceIdString,
|
||
IN ULONG InstanceIdStringLength,
|
||
OUT PULONG ResultLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes as input a service name, and an instance ordinal
|
||
indicating a particular device in the service's volatile Enum list. It
|
||
returns a unique string identifying this device instance for the
|
||
specified hardware profile that is guaranteed to not change.
|
||
|
||
This string may be used by the driver to segregate instance-specific
|
||
configuration information under it's service entry's Parameters subkey.
|
||
NOTE: The driver should not try to interpret this string as anything other
|
||
than a unique 'cookie' it can use to identify a device instance.
|
||
|
||
Since this call may have the side-effect of writing out a newly-generated
|
||
instance identifier value entry to the registry, the caller of this routine
|
||
must have acquired the Plug & Play device registry resource for exclusive
|
||
(write) access before calling this routine.
|
||
|
||
Parameters:
|
||
|
||
ServiceKeyName - Supplies the name of the subkey in the system service
|
||
list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services) that caused
|
||
the driver to load. This is the RegistryPath parameter to the
|
||
DriverEntry routine.
|
||
|
||
InstanceNumber - Supplies an ordinal value indicating the device instance
|
||
to retrieve the unique identifier for.
|
||
|
||
HwProfileId - Supplies the hardware profile configuration ID for which
|
||
the unique identifier should be retrieved. The identifier string
|
||
is hardware profile-specific, because a driver must be able to
|
||
configure its device differently depending on the hardware profile
|
||
currently in use. (E.g., a netcard configured for thick-ethernet
|
||
in one hardware profile, and thin-ethernet in another)
|
||
|
||
InstanceIdString - Supplies a pointer to a character buffer that will receive
|
||
the string uniquely identifying the device instance associated with this
|
||
instance ordinal (for the specified hardware profile).
|
||
|
||
InstanceIdStringLength - Supplies the length, in bytes, of the InstanceIdString
|
||
buffer.
|
||
|
||
ResultLength - Receives the length of the identifying string, in bytes, not
|
||
including the NULL terminator.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
HANDLE EnumDevInstHandle;
|
||
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
||
UNICODE_STRING DevInstUnicodeString, InstanceIdentifier, TempUnicodeString;
|
||
BOOLEAN GenerateID;
|
||
|
||
//
|
||
// Retrieve the device instance path associated with the specified service
|
||
// instance ordinal, as well as an opened handle to the device instance
|
||
// registry key.
|
||
//
|
||
Status = IopServiceInstanceToDeviceInstance(NULL,
|
||
ServiceKeyName,
|
||
InstanceNumber,
|
||
&DevInstUnicodeString,
|
||
&EnumDevInstHandle,
|
||
KEY_ALL_ACCESS
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// See if there is already an identifier string recorded for this
|
||
// device instance, and if so, use it.
|
||
//
|
||
GenerateID = TRUE;
|
||
Status = IopGetRegistryValue(EnumDevInstHandle,
|
||
REGSTR_VALUE_INSTANCEIDENTIFIER,
|
||
&KeyValueInformation
|
||
);
|
||
if(NT_SUCCESS(Status)) {
|
||
|
||
if(KeyValueInformation->Type == REG_SZ) {
|
||
IopRegistryDataToUnicodeString(&InstanceIdentifier,
|
||
(PWCHAR)KEY_VALUE_DATA(KeyValueInformation),
|
||
KeyValueInformation->DataLength
|
||
);
|
||
} else {
|
||
InstanceIdentifier.Length = 0;
|
||
}
|
||
|
||
if(InstanceIdentifier.Length) {
|
||
GenerateID = FALSE;
|
||
} else {
|
||
ExFreePool(KeyValueInformation);
|
||
}
|
||
}
|
||
|
||
if(GenerateID) {
|
||
Status = PiGenerateDeviceInstanceIdentifier(&DevInstUnicodeString,
|
||
&InstanceIdentifier
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
goto PrepareForReturn0;
|
||
}
|
||
|
||
//
|
||
// Store the generated instance identifier in the device instance subkey.
|
||
// (Ignore return status--we have what we want.)
|
||
//
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_INSTANCEIDENTIFIER);
|
||
ZwSetValueKey(EnumDevInstHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
InstanceIdentifier.Buffer,
|
||
InstanceIdentifier.MaximumLength
|
||
);
|
||
}
|
||
|
||
//
|
||
// We now have the unique device instance identifier. To make it specific
|
||
// to the specified hardware profile, we will now prepend the hardware
|
||
// profile configuration ID (base-10, 4 digits).
|
||
//
|
||
*ResultLength = InstanceIdentifier.Length + (4 * sizeof(WCHAR));
|
||
|
||
if(InstanceIdStringLength < *ResultLength + sizeof(UNICODE_NULL)) {
|
||
Status = STATUS_BUFFER_TOO_SMALL;
|
||
goto PrepareForReturn1;
|
||
}
|
||
|
||
swprintf(InstanceIdString, REGSTR_KEY_INSTANCE_KEY_FORMAT, HwProfileId);
|
||
RtlMoveMemory(&(InstanceIdString[4]),
|
||
InstanceIdentifier.Buffer,
|
||
InstanceIdentifier.Length
|
||
);
|
||
InstanceIdString[CB_TO_CWC(*ResultLength)] = UNICODE_NULL;
|
||
|
||
PrepareForReturn1:
|
||
|
||
if(GenerateID) {
|
||
ExFreePool(InstanceIdentifier.Buffer);
|
||
} else {
|
||
ExFreePool(KeyValueInformation);
|
||
}
|
||
|
||
PrepareForReturn0:
|
||
|
||
ZwClose(EnumDevInstHandle);
|
||
ExFreePool(DevInstUnicodeString.Buffer);
|
||
|
||
return Status;
|
||
}
|
||
#endif // obsolete API
|
||
|
||
NTSTATUS
|
||
PiGenerateDeviceInstanceIdentifier(
|
||
IN PUNICODE_STRING DeviceInstanceRegistryPath,
|
||
OUT PUNICODE_STRING DeviceInstanceIdString
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes as input a registry path to a device instance
|
||
(relative to HKLM\System\Enum), and returns a string that uniquely
|
||
identifies this device instance. The current algorithm simply replaces
|
||
every occurrence of a backslash ('\') with an ampersand ('&'), however,
|
||
this may change in the future, therefore the resulting string should not
|
||
be interpreted as anything but a unique 'cookie'.
|
||
|
||
It is the caller's responsibility to free the (PagedPool) memory allocated
|
||
for the unicode string buffer.
|
||
|
||
Parameters:
|
||
|
||
DeviceInstanceRegistryPath - Supplies the path in the registry (relative to
|
||
HKLM\System\Enum) to the device instance for which to generate the
|
||
identifier.
|
||
|
||
DeviceInstanceIdString - Receives a unicode string that uniquely identifies
|
||
the specified device instance. The unicode string is guaranteed to be
|
||
NULL-terminated. Note that this string is not specific to a particular
|
||
hardware profile, and must be combined with a hardware profile configuration
|
||
ID before it may be used by a driver to store hardware-profile-specific
|
||
configuration parameters for a device instance.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
ULONG StringLength = DeviceInstanceRegistryPath->Length, i;
|
||
WCHAR CurrentChar;
|
||
|
||
if(DeviceInstanceIdString->Buffer = ExAllocatePool(PagedPool,
|
||
StringLength + sizeof(WCHAR))) {
|
||
|
||
DeviceInstanceIdString->Length = (USHORT)StringLength;
|
||
DeviceInstanceIdString->MaximumLength = (USHORT)StringLength + sizeof(WCHAR);
|
||
|
||
StringLength = CB_TO_CWC(StringLength); // need count of chars now.
|
||
|
||
for(i = 0; i < StringLength; i++) {
|
||
|
||
if((CurrentChar = DeviceInstanceRegistryPath->Buffer[i]) != OBJ_NAME_PATH_SEPARATOR) {
|
||
DeviceInstanceIdString->Buffer[i] = CurrentChar;
|
||
} else {
|
||
DeviceInstanceIdString->Buffer[i] = L'&';
|
||
}
|
||
}
|
||
DeviceInstanceIdString->Buffer[i] = UNICODE_NULL;
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiGetDeviceObjectFilePointer(
|
||
IN PUNICODE_STRING ObjectName,
|
||
OUT PFILE_OBJECT *DeviceFileObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns a pointer to a referenced file object that has been
|
||
opened to the specified device.
|
||
|
||
To close access to the device, the caller should dereference the file
|
||
object pointer.
|
||
|
||
Arguments:
|
||
|
||
ObjectName - Name of the device object for which a file object pointer is
|
||
to be returned.
|
||
|
||
DeviceFileObject - Supplies the address of a variable to receive a pointer
|
||
to the file object for the device.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating whether the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
HANDLE FileHandle;
|
||
IO_STATUS_BLOCK IoStatus;
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// Open the device. (We specify the IO_ATTACH_DEVICE_API create option
|
||
// flag so that we can open devices that are already opened for exclusive
|
||
// access.)
|
||
//
|
||
InitializeObjectAttributes(&ObjectAttributes,
|
||
ObjectName,
|
||
0,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
Status = ZwCreateFile(&FileHandle,
|
||
FILE_READ_ATTRIBUTES,
|
||
&ObjectAttributes,
|
||
&IoStatus,
|
||
NULL,
|
||
0,
|
||
0,
|
||
FILE_OPEN,
|
||
FILE_NON_DIRECTORY_FILE | IO_ATTACH_DEVICE_API,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
if(NT_SUCCESS(Status)) {
|
||
//
|
||
// We successfully got a file handle to the device, now
|
||
// reference the file object.
|
||
//
|
||
Status = ObReferenceObjectByHandle(FileHandle,
|
||
0,
|
||
IoFileObjectType,
|
||
KernelMode,
|
||
DeviceFileObject,
|
||
NULL
|
||
);
|
||
ZwClose(FileHandle);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiGetOrSetDeviceInstanceStatus(
|
||
IN PUNICODE_STRING DeviceInstancePath,
|
||
IN OUT PDEVICE_STATUS DeviceStatus,
|
||
IN BOOLEAN SetRequested
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will either get or set the current device status for the
|
||
specified device instance.
|
||
|
||
Arguments:
|
||
|
||
DeviceInstancePath - Supplies a pointer to a unicode string specifying the
|
||
registry path for the device instance (relative to HKLM\System\Enum).
|
||
|
||
DeviceStatus - If the status is not to be set (i.e., SetRequested = FALSE),
|
||
this variable receives the current status of the device (if no device status
|
||
is given in the controlling service's volatile Enum subkey, then
|
||
DeviceStatusOK is returned). If the caller specified that the status should
|
||
be set, then this variable contains that value that the status will be set to.
|
||
|
||
SetRequested - Specifies whether the status value is to be set (TRUE), or simply
|
||
retrieved (FALSE).
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating whether the function was successful. Common return
|
||
codes include:
|
||
|
||
STATUS_PLUGPLAY_NO_DEVICE - the device has not yet been installed, hence
|
||
has no status (it isn't yet linked to a service entry)
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
HANDLE EnumHandle, DeviceInstanceHandle, ServiceKeyHandle;
|
||
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
||
UNICODE_STRING TempUnicodeString;
|
||
PI_FIND_SERVICE_INSTANCE_CONTEXT FindServiceInstanceContext;
|
||
ULONG ServiceInstanceOrdinal, DwordValue;
|
||
WCHAR UnicodeBuffer[20];
|
||
|
||
//
|
||
// First, open HKLM\System\CurrentControlSet\Enum
|
||
//
|
||
Status = IopOpenRegistryKey(&EnumHandle,
|
||
NULL,
|
||
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
||
KEY_READ,
|
||
FALSE
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Now open up the device instance key under HKLM\System\Enum.
|
||
//
|
||
Status = IopOpenRegistryKey(&DeviceInstanceHandle,
|
||
EnumHandle,
|
||
DeviceInstancePath,
|
||
KEY_READ,
|
||
FALSE
|
||
);
|
||
ZwClose(EnumHandle);
|
||
if(!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Now, retrieve the controlling service for this device instance.
|
||
//
|
||
Status = IopGetRegistryValue(DeviceInstanceHandle,
|
||
REGSTR_VALUE_SERVICE,
|
||
&KeyValueInformation
|
||
);
|
||
ZwClose(DeviceInstanceHandle);
|
||
if(NT_SUCCESS(Status)) {
|
||
//
|
||
// Verify that we really have a service name here.
|
||
//
|
||
if(KeyValueInformation->Type == REG_SZ) {
|
||
IopRegistryDataToUnicodeString(&TempUnicodeString,
|
||
(PWCHAR)KEY_VALUE_DATA(KeyValueInformation),
|
||
KeyValueInformation->DataLength
|
||
);
|
||
} else {
|
||
TempUnicodeString.Length = 0;
|
||
}
|
||
|
||
if(!TempUnicodeString.Length) {
|
||
ExFreePool(KeyValueInformation);
|
||
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
}
|
||
}
|
||
|
||
if(!NT_SUCCESS(Status)) {
|
||
return (Status == STATUS_OBJECT_NAME_NOT_FOUND) ? STATUS_PLUGPLAY_NO_DEVICE
|
||
: Status;
|
||
}
|
||
|
||
//
|
||
// Open the controlling service entry key under HKLM\System\CurrentControlSet\Services.
|
||
//
|
||
Status = IopOpenServiceEnumKeys(&TempUnicodeString,
|
||
KEY_READ,
|
||
&ServiceKeyHandle,
|
||
NULL,
|
||
FALSE
|
||
);
|
||
ExFreePool(KeyValueInformation);
|
||
if(!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Now, find the specified device instance under the controlling service's volatile
|
||
// Enum list.
|
||
//
|
||
FindServiceInstanceContext.DeviceInstancePath = DeviceInstancePath;
|
||
FindServiceInstanceContext.DeviceFound = FALSE;
|
||
|
||
Status = IopApplyFunctionToServiceInstances(ServiceKeyHandle,
|
||
NULL,
|
||
0, // no need to open the device instance keys
|
||
TRUE,
|
||
PiFindServiceInstance,
|
||
&FindServiceInstanceContext,
|
||
&ServiceInstanceOrdinal
|
||
);
|
||
if(NT_SUCCESS(Status) && !FindServiceInstanceContext.DeviceFound) {
|
||
Status = STATUS_PLUGPLAY_NO_DEVICE;
|
||
}
|
||
|
||
if(NT_SUCCESS(Status)) {
|
||
//
|
||
// The device instance was found, now open the service's volatile Enum subkey
|
||
// and see if there is a status value entry of the form "DeviceStatus<x>", where
|
||
// <x> is the device's service instance ordinal.
|
||
//
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_KEY_ENUM);
|
||
Status = IopOpenRegistryKey(&EnumHandle,
|
||
ServiceKeyHandle,
|
||
&TempUnicodeString,
|
||
(SetRequested ? KEY_ALL_ACCESS : KEY_READ),
|
||
FALSE
|
||
);
|
||
if(NT_SUCCESS(Status)) {
|
||
|
||
swprintf(UnicodeBuffer, REGSTR_VALUE_DEVICE_STATUS_FORMAT, ServiceInstanceOrdinal);
|
||
|
||
if(SetRequested) {
|
||
|
||
RtlInitUnicodeString(&TempUnicodeString, UnicodeBuffer);
|
||
DwordValue = (ULONG)(*DeviceStatus);
|
||
Status = ZwSetValueKey(EnumHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&DwordValue,
|
||
sizeof(DwordValue)
|
||
);
|
||
|
||
} else {
|
||
//
|
||
// Just retrieve the current device status (if unable to get, use default of
|
||
// DeviceStatusOK.
|
||
//
|
||
Status = IopGetRegistryValue(EnumHandle,
|
||
UnicodeBuffer,
|
||
&KeyValueInformation
|
||
);
|
||
if(NT_SUCCESS(Status)) {
|
||
|
||
if((KeyValueInformation->Type == REG_DWORD) &&
|
||
(KeyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
*DeviceStatus = *(PDEVICE_STATUS)KEY_VALUE_DATA(KeyValueInformation);
|
||
} else {
|
||
*DeviceStatus = DeviceStatusOK;
|
||
}
|
||
ExFreePool(KeyValueInformation);
|
||
|
||
} else if(Status == STATUS_OBJECT_NAME_NOT_FOUND){
|
||
*DeviceStatus = DeviceStatusOK;
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
ZwClose(EnumHandle);
|
||
}
|
||
}
|
||
|
||
ZwClose(ServiceKeyHandle);
|
||
|
||
return Status;
|
||
}
|
||
|
||
BOOLEAN
|
||
PiFindServiceInstance(
|
||
IN HANDLE DeviceInstanceHandle,
|
||
IN PUNICODE_STRING DeviceInstancePath,
|
||
IN OUT PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is a callback function for IopApplyFunctionToServiceInstances.
|
||
It is called for each device instance key referenced by a service instance
|
||
value under the specified service's volatile Enum subkey. The purpose of this
|
||
routine is to determine whether the current device instance path matches the
|
||
device instance being searched for (as specified in the context structure),
|
||
and if so, to set the 'DeviceFound' flag in the context structure, and abort
|
||
enumeration.
|
||
|
||
NOTE: The PnP device-specific registry resource must be acquired for shared
|
||
(read) access before invoking this routine.
|
||
|
||
Arguments:
|
||
|
||
DeviceInstanceHandle - Unused
|
||
|
||
DeviceInstancePath - Supplies the registry path (relative to HKLM\System\Enum)
|
||
to this device instance.
|
||
|
||
Context - Supplies a pointer to a PI_FIND_SERVICE_INSTANCE_CONTEXT structure with
|
||
the following fields:
|
||
|
||
PUNICODE_STRING DeviceInstancePath - Supplies a pointer to a unicode string
|
||
specifying the device instance being searched for.
|
||
|
||
BOOLEAN DeviceFound - If the current device instance matches the one being
|
||
searched for, then this flag is set to TRUE, and enumeration is aborted
|
||
(by returning FALSE).
|
||
|
||
Return Value:
|
||
|
||
TRUE to continue the enumeration.
|
||
FALSE to abort it. Enumeration should be aborted when the matching device instance
|
||
is encountered.
|
||
|
||
--*/
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER(DeviceInstanceHandle);
|
||
|
||
//
|
||
// See if this device instance is the one we're looking for.
|
||
//
|
||
if(RtlEqualUnicodeString(DeviceInstancePath,
|
||
((PPI_FIND_SERVICE_INSTANCE_CONTEXT)Context)->DeviceInstancePath,
|
||
TRUE)) {
|
||
|
||
((PPI_FIND_SERVICE_INSTANCE_CONTEXT)Context)->DeviceFound = TRUE;
|
||
return FALSE;
|
||
} else {
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
PiFindBusInstanceNode(
|
||
IN PPLUGPLAY_BUS_INSTANCE BusInstance OPTIONAL,
|
||
IN PUNICODE_STRING BusDeviceInstanceName OPTIONAL,
|
||
OUT PPLUGPLAY_BUS_ENUMERATOR *BusEnumeratorNode OPTIONAL,
|
||
OUT PPLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR *BusInstanceNode OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine finds the bus instance node in our global bus list that
|
||
matches either the BusInstance structure, or the BusDeviceInstanceName
|
||
string, whichever was specified. (If both parameters are specified, the
|
||
BusInstance parameter is used, and BusDeviceInstanceName is ignored.)
|
||
|
||
The caller must have acquired the Plug & Play bus list resource for
|
||
(at least) shared (read) access.
|
||
|
||
Arguments:
|
||
|
||
BusInstance - Optionally, supplies a pointer to a bus instance structure for
|
||
which the corresponding bus instance node is to be retrieved. If this parameter
|
||
is not specified, then BusDeviceInstanceName will be used instead. (NOTE: the
|
||
BusName parameter in this structure is ignoring for the purposes of finding a
|
||
match.)
|
||
|
||
BusDeviceInstanceName - Optionally, supplies the device instance name for which the
|
||
corresponding bus instance node is to be retrieved. This provides an alternate
|
||
means of identifying the desired bus instance node (used only if BusInstance
|
||
is not specified).
|
||
|
||
BusEnumeratorNode - Optionally, receives a pointer to the bus enumerator node for this
|
||
bus instance, if found.
|
||
|
||
BusInstanceNode - Optionally, receives a pointer to the matching bus instance node,
|
||
if found.
|
||
|
||
Return Value:
|
||
|
||
NT status indicating whether or not the function succeeded.
|
||
If the specified bus instance is not found, then STATUS_NO_SUCH_DEVICE
|
||
is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY CurrentPnPBusListEntry, CurrentPnPBusInstance;
|
||
PPLUGPLAY_BUS_ENUMERATOR CurBusEnumerator;
|
||
PPLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR CurBusInstNode;
|
||
BOOLEAN Found;
|
||
|
||
//
|
||
// Make sure at least one of the search parameters is specified.
|
||
//
|
||
if(!(ARGUMENT_PRESENT(BusInstance) || ARGUMENT_PRESENT(BusDeviceInstanceName))) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
Found = FALSE;
|
||
|
||
for(CurrentPnPBusListEntry = PpBusListHead.Flink;
|
||
CurrentPnPBusListEntry != &PpBusListHead;
|
||
CurrentPnPBusListEntry = CurrentPnPBusListEntry->Flink) {
|
||
|
||
CurBusEnumerator = CONTAINING_RECORD(CurrentPnPBusListEntry,
|
||
PLUGPLAY_BUS_ENUMERATOR,
|
||
BusEnumeratorListEntry
|
||
);
|
||
|
||
for(CurrentPnPBusInstance = CurBusEnumerator->BusInstanceListEntry.Flink;
|
||
CurrentPnPBusInstance != &(CurBusEnumerator->BusInstanceListEntry);
|
||
CurrentPnPBusInstance = CurrentPnPBusInstance->Flink ) {
|
||
|
||
CurBusInstNode = CONTAINING_RECORD(CurrentPnPBusInstance,
|
||
PLUGPLAY_BUS_INSTANCE_FULL_DESCRIPTOR,
|
||
BusInstanceListEntry
|
||
);
|
||
|
||
if(ARGUMENT_PRESENT(BusInstance)) {
|
||
//
|
||
// Compare based on the supplied BusInstance structure.
|
||
//
|
||
if((CurBusInstNode->BusInstanceInformation.BusNumber == BusInstance->BusNumber) &&
|
||
(sizeof(PLUGPLAY_BUS_TYPE) == RtlCompareMemory(
|
||
&(CurBusInstNode->BusInstanceInformation.BusType),
|
||
&(BusInstance->BusType),
|
||
sizeof(PLUGPLAY_BUS_TYPE)
|
||
))) {
|
||
Found = TRUE;
|
||
}
|
||
} else {
|
||
//
|
||
// Compare based on the supplied device instance name
|
||
//
|
||
if(RtlEqualUnicodeString(BusDeviceInstanceName,
|
||
&(CurBusInstNode->DeviceInstancePath),
|
||
TRUE)) {
|
||
Found = TRUE;
|
||
}
|
||
}
|
||
|
||
if(Found) {
|
||
|
||
if(ARGUMENT_PRESENT(BusEnumeratorNode)) {
|
||
*BusEnumeratorNode = CurBusEnumerator;
|
||
}
|
||
|
||
if(ARGUMENT_PRESENT(BusInstanceNode)) {
|
||
*BusInstanceNode = CurBusInstNode;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
#endif // _PNP_POWER_
|
||
|