540 lines
17 KiB
C
540 lines
17 KiB
C
/*++
|
||
|
||
Copyright (c) 1995 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
devices.c
|
||
|
||
Abstract:
|
||
|
||
Plug and Play Manager routines dealing with device manipulation/registration.
|
||
|
||
Author:
|
||
|
||
Lonny McMichael (lonnym) 02/14/95
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
//
|
||
// Prototype utility functions internal to this file.
|
||
//
|
||
NTSTATUS
|
||
PiFindDevInstMatch(
|
||
IN HANDLE ServiceEnumHandle,
|
||
IN PUNICODE_STRING DeviceInstanceName,
|
||
OUT PULONG InstanceCount,
|
||
OUT PUNICODE_STRING MatchingValueName
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, PpDeviceRegistration)
|
||
#pragma alloc_text(PAGE, PiFindDevInstMatch)
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
NTSTATUS
|
||
PpDeviceRegistration(
|
||
IN PUNICODE_STRING DeviceInstancePath,
|
||
IN BOOLEAN Add
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
If Add is set to TRUE, this Plug and Play Manager API creates (if necessary)
|
||
and populates the volatile Enum subkey of a device's service list entry, based
|
||
on the device instance path specified. If Add is set to FALSE, the specified
|
||
device instance will be removed from the volatile Enum subkey of a device's
|
||
service list entry.
|
||
|
||
For example, if there is a device in the Enum tree as follows:
|
||
|
||
HKLM\System\Enum\PCI
|
||
\foo
|
||
\0000
|
||
Service = REG_SZ bar
|
||
\0001
|
||
Service = REG_SZ other
|
||
|
||
The result of the call, PpDeviceRegistration("PCI\foo\0000", Add = TRUE), would be:
|
||
|
||
HKLM\CurrentControlSet\Services
|
||
\bar
|
||
\Enum
|
||
Count = REG_DWORD 1
|
||
0 = REG_SZ PCI\foo\0000
|
||
|
||
Arguments:
|
||
|
||
DeviceInstancePath - Supplies the path in the registry (relative to
|
||
HKLM\CCS\System\Enum) of the device to be registered/deregistered.
|
||
This path must point to an instance subkey.
|
||
|
||
Add - Supplies a BOOLEAN value to indicate the operation is for addition or removal.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS code indicating whether or not the function was successful
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
UNICODE_STRING TempUnicodeString;
|
||
BOOLEAN ReleasePnPRegDevResource = FALSE;
|
||
NTSTATUS Status;
|
||
UNICODE_STRING MatchingDeviceInstance;
|
||
UNICODE_STRING ServiceName;
|
||
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
||
HANDLE TempKeyHandle;
|
||
HANDLE DeviceInstanceHandle = NULL, ServiceEnumHandle = NULL;
|
||
CHAR UnicodeBuffer[20];
|
||
BOOLEAN UpdateCount = FALSE;
|
||
ULONG i, j, Count, junk;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Assume successful completion.
|
||
//
|
||
Status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// 'Normalize' the DeviceInstancePath by stripping off a trailing
|
||
// backslash (if present)
|
||
//
|
||
|
||
if (DeviceInstancePath->Length <= sizeof(WCHAR)) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto PrepareForReturn1;
|
||
}
|
||
|
||
if (DeviceInstancePath->Buffer[CB_TO_CWC(DeviceInstancePath->Length) - 1] ==
|
||
OBJ_NAME_PATH_SEPARATOR) {
|
||
DeviceInstancePath->Length -= sizeof(WCHAR);
|
||
}
|
||
|
||
//
|
||
// Acquire PnP device-specific registry resource for exclusive (read/write) access.
|
||
//
|
||
KeEnterCriticalRegion();
|
||
ExAcquireResourceExclusive(&PpRegistryDeviceResource, TRUE);
|
||
ReleasePnPRegDevResource = TRUE;
|
||
|
||
//
|
||
// Open HKLM\System\CurrentControlSet\Enum
|
||
//
|
||
Status = IopOpenRegistryKey(&TempKeyHandle,
|
||
NULL,
|
||
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
||
KEY_READ,
|
||
FALSE
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
goto PrepareForReturn1;
|
||
}
|
||
|
||
//
|
||
// Open the specified device instance key under HKLM\CCS\System\Enum
|
||
//
|
||
Status = IopOpenRegistryKey(&DeviceInstanceHandle,
|
||
TempKeyHandle,
|
||
DeviceInstancePath,
|
||
KEY_READ,
|
||
FALSE
|
||
);
|
||
ZwClose(TempKeyHandle);
|
||
if(!NT_SUCCESS(Status)) {
|
||
goto PrepareForReturn1;
|
||
}
|
||
|
||
//
|
||
// Read Service= value entry of the specified device instance key.
|
||
//
|
||
|
||
Status = IopGetRegistryValue(DeviceInstanceHandle,
|
||
REGSTR_VALUE_SERVICE,
|
||
&KeyValueInformation
|
||
);
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
if (KeyValueInformation->Type == REG_SZ) {
|
||
if (KeyValueInformation->DataLength > sizeof(UNICODE_NULL)) {
|
||
IopRegistryDataToUnicodeString(&ServiceName,
|
||
(PWSTR)KEY_VALUE_DATA(KeyValueInformation),
|
||
KeyValueInformation->DataLength
|
||
);
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
} else {
|
||
KeyValueInformation = NULL;
|
||
}
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto PrepareForReturn2;
|
||
}
|
||
|
||
//
|
||
// Next, open the service entry, and volatile Enum subkey
|
||
// under HKLM\System\CurrentControlSet\Services (creating it if it
|
||
// doesn't exist)
|
||
//
|
||
|
||
Status = IopOpenServiceEnumKeys(&ServiceName,
|
||
KEY_ALL_ACCESS,
|
||
NULL,
|
||
&ServiceEnumHandle,
|
||
TRUE
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
goto PrepareForReturn2;
|
||
}
|
||
|
||
if (KeyValueInformation) {
|
||
ExFreePool(KeyValueInformation);
|
||
KeyValueInformation = NULL;
|
||
}
|
||
|
||
//
|
||
// Now, search through the service's existing list of device instances, to see
|
||
// if this instance has previously been registered.
|
||
//
|
||
|
||
Status = PiFindDevInstMatch(ServiceEnumHandle, DeviceInstancePath, &Count, &MatchingDeviceInstance);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto PrepareForReturn3;
|
||
}
|
||
|
||
if (!MatchingDeviceInstance.Buffer) {
|
||
|
||
//
|
||
// If we didn't find a match and caller wants to register the device, then we add
|
||
// this instance to the service's Enum list.
|
||
//
|
||
|
||
if (Add) {
|
||
PWSTR instancePathBuffer;
|
||
ULONG instancePathLength;
|
||
BOOLEAN freeBuffer = FALSE;
|
||
|
||
//
|
||
// Create the value entry and update NextInstance= for the madeup key
|
||
//
|
||
|
||
if (DeviceInstancePath->Buffer[DeviceInstancePath->Length / sizeof(WCHAR) - 1] !=
|
||
UNICODE_NULL) {
|
||
instancePathLength = DeviceInstancePath->Length + sizeof(WCHAR);
|
||
instancePathBuffer = (PWSTR)ExAllocatePool(PagedPool, instancePathLength);
|
||
if (instancePathBuffer) {
|
||
RtlMoveMemory(instancePathBuffer,
|
||
DeviceInstancePath->Buffer,
|
||
DeviceInstancePath->Length
|
||
);
|
||
instancePathBuffer[DeviceInstancePath->Length / sizeof(WCHAR)] = UNICODE_NULL;
|
||
freeBuffer = TRUE;
|
||
}
|
||
}
|
||
if (!freeBuffer) {
|
||
instancePathBuffer = DeviceInstancePath->Buffer;
|
||
instancePathLength = DeviceInstancePath->Length;
|
||
}
|
||
PiUlongToUnicodeString(&TempUnicodeString, UnicodeBuffer, 20, Count);
|
||
Status = ZwSetValueKey(
|
||
ServiceEnumHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
instancePathBuffer,
|
||
instancePathLength
|
||
);
|
||
if (freeBuffer) {
|
||
ExFreePool(instancePathBuffer);
|
||
}
|
||
Count++;
|
||
UpdateCount = TRUE;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// If we did find a match and caller wants to deregister the device, then we remove
|
||
// this instance from the service's Enum list.
|
||
//
|
||
|
||
if (Add == FALSE) {
|
||
ZwDeleteValueKey(ServiceEnumHandle, &MatchingDeviceInstance);
|
||
Count--;
|
||
UpdateCount = TRUE;
|
||
|
||
//
|
||
// Finally, if Count is not zero we need to physically reorganize the
|
||
// instances under the ServiceKey\Enum key to make them contiguous.
|
||
//
|
||
|
||
j = 0;
|
||
i = 0;
|
||
if (Count != 0) {
|
||
while (j < Count) {
|
||
PiUlongToUnicodeString(&TempUnicodeString, UnicodeBuffer, 20, i);
|
||
Status = ZwQueryValueKey( ServiceEnumHandle,
|
||
&TempUnicodeString,
|
||
KeyValueFullInformation,
|
||
(PVOID) NULL,
|
||
0,
|
||
&junk);
|
||
if ((Status != STATUS_OBJECT_NAME_NOT_FOUND) && (Status != STATUS_OBJECT_PATH_NOT_FOUND)) {
|
||
if (i != j) {
|
||
|
||
//
|
||
// Need to change the instance i to instance j
|
||
//
|
||
|
||
Status = IopGetRegistryValue(ServiceEnumHandle,
|
||
TempUnicodeString.Buffer,
|
||
&KeyValueInformation
|
||
);
|
||
if (NT_SUCCESS(Status)) {
|
||
ZwDeleteValueKey(ServiceEnumHandle, &TempUnicodeString);
|
||
PiUlongToUnicodeString(&TempUnicodeString, UnicodeBuffer, 20, j);
|
||
ZwSetValueKey (ServiceEnumHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_SZ,
|
||
(PVOID)KEY_VALUE_DATA(KeyValueInformation),
|
||
KeyValueInformation->DataLength
|
||
);
|
||
ExFreePool(KeyValueInformation);
|
||
KeyValueInformation = NULL;
|
||
} else {
|
||
DbgPrint("PpDeviceRegistration: Fail to rearrange device instances %x\n",
|
||
Status);
|
||
break;
|
||
}
|
||
}
|
||
j++;
|
||
}
|
||
i++;
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
if (UpdateCount) {
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_COUNT);
|
||
ZwSetValueKey(
|
||
ServiceEnumHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&Count,
|
||
sizeof(Count)
|
||
);
|
||
PiWstrToUnicodeString(&TempUnicodeString, REGSTR_VALUE_NEXT_INSTANCE);
|
||
ZwSetValueKey(
|
||
ServiceEnumHandle,
|
||
&TempUnicodeString,
|
||
TITLE_INDEX_VALUE,
|
||
REG_DWORD,
|
||
&Count,
|
||
sizeof(Count)
|
||
);
|
||
}
|
||
|
||
//
|
||
// Need to release the matching device value name
|
||
//
|
||
|
||
if (MatchingDeviceInstance.Buffer) {
|
||
RtlFreeUnicodeString(&MatchingDeviceInstance);
|
||
}
|
||
Status = STATUS_SUCCESS;
|
||
|
||
PrepareForReturn3:
|
||
|
||
ZwClose(ServiceEnumHandle);
|
||
|
||
PrepareForReturn2:
|
||
|
||
ZwClose(DeviceInstanceHandle);
|
||
if (KeyValueInformation) {
|
||
ExFreePool(KeyValueInformation);
|
||
}
|
||
|
||
PrepareForReturn1:
|
||
|
||
if(ReleasePnPRegDevResource) {
|
||
ExReleaseResource(&PpRegistryDeviceResource);
|
||
ReleasePnPRegDevResource = FALSE;
|
||
KeLeaveCriticalRegion();
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiFindDevInstMatch(
|
||
IN HANDLE ServiceEnumHandle,
|
||
IN PUNICODE_STRING DeviceInstanceName,
|
||
OUT PULONG Count,
|
||
OUT PUNICODE_STRING MatchingValueName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine searches through the specified Service\Enum values entries
|
||
for a device instance matching the one specified by KeyInformation.
|
||
If a matching is found, the MatchingValueName is returned and caller must
|
||
free the unicode string when done with it.
|
||
|
||
Arguments:
|
||
|
||
ServiceEnumHandle - Supplies a handle to service enum key.
|
||
|
||
DeviceInstanceName - Supplies a pointer to a unicode string specifying the
|
||
name of the device instance key to search for.
|
||
|
||
InstanceCount - Supplies a pointer to a ULONG variable to receive the device
|
||
instance count under the service enum key.
|
||
|
||
MatchingNameFound - Supplies a pointer to a UNICODE_STRING to receive the value
|
||
name of the matched device instance.
|
||
|
||
Return Value:
|
||
|
||
A NTSTATUS code. if a matching is found, the MatchingValueName is the unicode
|
||
string of the value name. Otherwise its length and Buffer will be set to empty.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
ULONG i, instanceCount, length = 256, junk;
|
||
UNICODE_STRING valueName, unicodeValue;
|
||
PWCHAR unicodeBuffer;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Find out how many instances are referenced in the service's Enum key.
|
||
//
|
||
|
||
MatchingValueName->Length = 0;
|
||
MatchingValueName->Buffer = NULL;
|
||
*Count = instanceCount = 0;
|
||
|
||
status = IopGetRegistryValue(ServiceEnumHandle,
|
||
REGSTR_VALUE_COUNT,
|
||
&keyValueInformation
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
if((keyValueInformation->Type == REG_DWORD) &&
|
||
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
|
||
instanceCount = *(PULONG)KEY_VALUE_DATA(keyValueInformation);
|
||
*Count = instanceCount;
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
|
||
} else if(status != STATUS_OBJECT_NAME_NOT_FOUND) {
|
||
return status;
|
||
} else {
|
||
|
||
//
|
||
// If 'Count' value entry not found, consider this to mean there are simply
|
||
// no device instance controlled by this service. Thus we don't have a match.
|
||
//
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
keyValueInformation = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePool(
|
||
PagedPool, length);
|
||
if (!keyValueInformation) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Allocate heap to store value name
|
||
//
|
||
|
||
unicodeBuffer = (PWSTR)ExAllocatePool(PagedPool, 10 * sizeof(WCHAR));
|
||
if (!unicodeBuffer) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Next scan thru each value key to find a match
|
||
//
|
||
|
||
for (i = 0; i < instanceCount ; i++) {
|
||
PiUlongToUnicodeString(&valueName, unicodeBuffer, 20, i);
|
||
status = ZwQueryValueKey (
|
||
ServiceEnumHandle,
|
||
&valueName,
|
||
KeyValueFullInformation,
|
||
keyValueInformation,
|
||
length,
|
||
&junk
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL) {
|
||
ExFreePool(keyValueInformation);
|
||
length = junk;
|
||
keyValueInformation = (PKEY_VALUE_FULL_INFORMATION)ExAllocatePool(
|
||
PagedPool, length);
|
||
if (!keyValueInformation) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
i--;
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if (keyValueInformation->Type == REG_SZ) {
|
||
if (keyValueInformation->DataLength > sizeof(UNICODE_NULL)) {
|
||
IopRegistryDataToUnicodeString(&unicodeValue,
|
||
(PWSTR)KEY_VALUE_DATA(keyValueInformation),
|
||
keyValueInformation->DataLength
|
||
);
|
||
} else {
|
||
continue;
|
||
}
|
||
} else {
|
||
continue;
|
||
}
|
||
|
||
if (RtlEqualUnicodeString(&unicodeValue,
|
||
DeviceInstanceName,
|
||
TRUE)) {
|
||
//
|
||
// We found a match.
|
||
//
|
||
|
||
*MatchingValueName= valueName;
|
||
break;
|
||
}
|
||
}
|
||
if (keyValueInformation) {
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
if (MatchingValueName->Length == 0) {
|
||
|
||
//
|
||
// If we did not find a match, we need to release the buffer. Otherwise
|
||
// it is caller's responsibility to release the buffer.
|
||
//
|
||
|
||
ExFreePool(unicodeBuffer);
|
||
}
|
||
return STATUS_SUCCESS;
|
||
}
|