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.
3820 lines
126 KiB
3820 lines
126 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
pnpioapi.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the plug-and-play IO system APIs.
|
|
|
|
Author:
|
|
|
|
Shie-Lin Tzong (shielint) 3-Jan-1995
|
|
Andrew Thornton (andrewth) 5-Sept-1996
|
|
Paula Tomlinson (paulat) 1-May-1997
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "pnpmgrp.h"
|
|
#pragma hdrstop
|
|
#include <stddef.h>
|
|
#include <wdmguid.h>
|
|
|
|
#ifdef POOL_TAGGING
|
|
#undef ExAllocatePool
|
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'oipP')
|
|
#endif
|
|
|
|
|
|
//
|
|
// Define device state work item.
|
|
//
|
|
|
|
typedef struct _DEVICE_WORK_ITEM {
|
|
WORK_QUEUE_ITEM WorkItem;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PVOID Context;
|
|
} DEVICE_WORK_ITEM, *PDEVICE_WORK_ITEM;
|
|
|
|
NTSTATUS
|
|
IopQueueDeviceWorkItem(
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN VOID (*WorkerRoutine)(PVOID),
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
IopRequestDeviceEjectWorker(
|
|
PVOID Context
|
|
);
|
|
|
|
BOOLEAN
|
|
IopIsReportedAlready(
|
|
IN HANDLE Handle,
|
|
IN PUNICODE_STRING ServiceName,
|
|
IN PCM_RESOURCE_LIST ResourceList,
|
|
OUT PBOOLEAN MatchingKey
|
|
);
|
|
|
|
//
|
|
// Definitions for IoOpenDeviceRegistryKey
|
|
//
|
|
|
|
#define PATH_CURRENTCONTROLSET_HW_PROFILE_CURRENT TEXT("\\Registry\\Machine\\System\\CurrentControlSet\\Hardware Profiles\\Current\\System\\CurrentControlSet")
|
|
#define PATH_CURRENTCONTROLSET TEXT("\\Registry\\Machine\\System\\CurrentControlSet")
|
|
#define PATH_ENUM TEXT("Enum\\")
|
|
#define PATH_CONTROL_CLASS TEXT("Control\\Class\\")
|
|
#define PATH_CCS_CONTROL_CLASS PATH_CURRENTCONTROLSET TEXT("\\") REGSTR_KEY_CONTROL TEXT("\\") REGSTR_KEY_CLASS
|
|
#define MAX_RESTPATH_BUF_LEN 512
|
|
|
|
//
|
|
// Definitions for PpCreateLegacyDeviceIds
|
|
//
|
|
|
|
#define LEGACY_COMPATIBLE_ID_BASE TEXT("DETECTED")
|
|
|
|
NTSTATUS
|
|
PpCreateLegacyDeviceIds(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUNICODE_STRING DriverName,
|
|
IN PCM_RESOURCE_LIST Resources
|
|
);
|
|
|
|
//
|
|
// An IO_GET_LEGACY_VETO_LIST_CONTEXT structure.
|
|
//
|
|
|
|
typedef struct {
|
|
PWSTR * VetoList;
|
|
ULONG VetoListLength;
|
|
PPNP_VETO_TYPE VetoType;
|
|
NTSTATUS * Status;
|
|
} IO_GET_LEGACY_VETO_LIST_CONTEXT, *PIO_GET_LEGACY_VETO_LIST_CONTEXT;
|
|
|
|
BOOLEAN
|
|
IopAppendLegacyVeto(
|
|
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context,
|
|
IN PUNICODE_STRING VetoName
|
|
);
|
|
BOOLEAN
|
|
IopGetLegacyVetoListDevice(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context
|
|
);
|
|
BOOLEAN
|
|
IopGetLegacyVetoListDeviceNode(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context
|
|
);
|
|
VOID
|
|
IopGetLegacyVetoListDrivers(
|
|
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, IoForwardAndCatchIrp)
|
|
#pragma alloc_text(PAGE, IoGetDeviceProperty)
|
|
#pragma alloc_text(PAGE, IoGetDmaAdapter)
|
|
#pragma alloc_text(PAGE, IoGetLegacyVetoList)
|
|
#pragma alloc_text(PAGE, IoIsWdmVersionAvailable)
|
|
#pragma alloc_text(PAGE, IoOpenDeviceRegistryKey)
|
|
#pragma alloc_text(PAGE, IoReportDetectedDevice)
|
|
#pragma alloc_text(PAGE, IoSynchronousInvalidateDeviceRelations)
|
|
#pragma alloc_text(PAGE, PpCreateLegacyDeviceIds)
|
|
#pragma alloc_text(PAGE, IopAppendLegacyVeto)
|
|
#pragma alloc_text(PAGE, IopGetLegacyVetoListDevice)
|
|
#pragma alloc_text(PAGE, IopGetLegacyVetoListDeviceNode)
|
|
#pragma alloc_text(PAGE, IopGetLegacyVetoListDrivers)
|
|
#pragma alloc_text(PAGE, IopIsReportedAlready)
|
|
#pragma alloc_text(PAGE, IopOpenDeviceParametersSubkey)
|
|
#pragma alloc_text(PAGE, IopOpenOrCreateDeviceRegistryKey)
|
|
#pragma alloc_text(PAGE, IopRequestDeviceEjectWorker)
|
|
#pragma alloc_text(PAGE, IopResourceRequirementsChanged)
|
|
#pragma alloc_text(PAGE, PiGetDeviceRegistryProperty)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
NTSTATUS
|
|
IoGetDeviceProperty(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN DEVICE_REGISTRY_PROPERTY DeviceProperty,
|
|
IN ULONG BufferLength,
|
|
OUT PVOID PropertyBuffer,
|
|
OUT PULONG ResultLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine lets drivers query the registry properties associated with the
|
|
specified device.
|
|
|
|
Parameters:
|
|
|
|
DeviceObject - Supplies the device object whoes registry property is to be
|
|
returned. This device object should be the one created by
|
|
a bus driver.
|
|
|
|
DeviceProperty - Specifies what device property to get.
|
|
|
|
BufferLength - Specifies the length, in byte, of the PropertyBuffer.
|
|
|
|
PropertyBuffer - Supplies a pointer to a buffer to receive property data.
|
|
|
|
ResultLength - Supplies a pointer to a variable to receive the size of the
|
|
property data returned.
|
|
|
|
ReturnValue:
|
|
|
|
Status code that indicates whether or not the function was successful. If
|
|
PropertyBuffer is not big enough to hold requested data, STATUS_BUFFER_TOO_SMALL
|
|
will be returned and ResultLength will be set to the number of bytes actually
|
|
required.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_NODE deviceNode;
|
|
DEVICE_CAPABILITIES capabilities;
|
|
PWSTR valueName, keyName = NULL;
|
|
ULONG valueType, length, configFlags;
|
|
DEVICE_INSTALL_STATE deviceInstallState;
|
|
POBJECT_NAME_INFORMATION deviceObjectName;
|
|
PWSTR deviceInstanceName;
|
|
PWCHAR enumeratorNameEnd;
|
|
GUID busTypeGuid;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize out parameters
|
|
//
|
|
*ResultLength = 0;
|
|
|
|
if (!IS_PDO(DeviceObject)) {
|
|
|
|
if ((DeviceProperty != DevicePropertyInstallState) &&
|
|
((DeviceProperty != DevicePropertyEnumeratorName) ||
|
|
(NULL == DeviceObject->DeviceObjectExtension->DeviceNode))) {
|
|
|
|
//
|
|
// We'll use the verifier to fail anyone who passes in something
|
|
// that is not a PDO *except* for the DevicePropertyInstallState.
|
|
// This is because our check for if something is a PDO really means
|
|
// is this a PDO that PNP knows about. For the most part these are
|
|
// the same, but the DevicePropertyInstallState will get called by
|
|
// classpnp, for device objects that *it* thinks it reported as
|
|
// PDOs, but PartMgr actually swallowed. This is a gross exception
|
|
// to make, so PartMgr really should be fixed.
|
|
//
|
|
// The arbiters attempt to retrieve the Enumerator Name property
|
|
// in determining whether "driver shared" resource allocations may
|
|
// be accommodated. The PDO used may be of the "legacy resource
|
|
// devnode" placeholder variety. The IS_PDO() macro explicitly
|
|
// disallows these devnodes, so we must special-case this as well,
|
|
// in order to avoid a verifier failure. Note that our behavior
|
|
// here is correct--we want the get-property call to fail for these
|
|
// legacy resource devnodes.
|
|
//
|
|
PpvUtilFailDriver(
|
|
PPVERROR_DDI_REQUIRES_PDO,
|
|
(PVOID) _ReturnAddress(),
|
|
DeviceObject,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
deviceNode = (PDEVICE_NODE) DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
//
|
|
// Map Device Property to registry value name and value type.
|
|
//
|
|
switch(DeviceProperty) {
|
|
|
|
case DevicePropertyPhysicalDeviceObjectName:
|
|
|
|
ASSERT (0 == (1 & BufferLength)); // had better be an even length
|
|
//
|
|
// Create a buffer for the Obj manager.
|
|
//
|
|
length = BufferLength + sizeof (OBJECT_NAME_INFORMATION);
|
|
deviceObjectName = (POBJECT_NAME_INFORMATION)ExAllocatePool(
|
|
PagedPool,
|
|
length);
|
|
if (NULL == deviceObjectName) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
status = ObQueryNameString (DeviceObject,
|
|
deviceObjectName,
|
|
length,
|
|
ResultLength);
|
|
if (STATUS_INFO_LENGTH_MISMATCH == status) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
if (NT_SUCCESS (status)) {
|
|
|
|
if (deviceObjectName->Name.Length == 0) {
|
|
//
|
|
// PDO has no NAME, probably it's been deleted
|
|
//
|
|
*ResultLength = 0;
|
|
} else {
|
|
|
|
*ResultLength = deviceObjectName->Name.Length + sizeof(UNICODE_NULL);
|
|
if (*ResultLength > BufferLength) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
|
|
RtlCopyMemory(PropertyBuffer,
|
|
deviceObjectName->Name.Buffer,
|
|
deviceObjectName->Name.Length);
|
|
//
|
|
// NULL terminate.
|
|
//
|
|
*(PWCHAR)(((PUCHAR)PropertyBuffer) + deviceObjectName->Name.Length) = L'\0';
|
|
}
|
|
}
|
|
} else {
|
|
|
|
*ResultLength -= sizeof(OBJECT_NAME_INFORMATION);
|
|
}
|
|
|
|
ExFreePool (deviceObjectName);
|
|
return status;
|
|
|
|
case DevicePropertyBusTypeGuid:
|
|
|
|
status = PpBusTypeGuidGet(deviceNode->ChildBusTypeIndex, &busTypeGuid);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
*ResultLength = sizeof(GUID);
|
|
if(*ResultLength <= BufferLength) {
|
|
|
|
RtlCopyMemory(PropertyBuffer,
|
|
&busTypeGuid,
|
|
sizeof(GUID));
|
|
} else {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
|
|
case DevicePropertyLegacyBusType:
|
|
|
|
if (deviceNode->ChildInterfaceType != InterfaceTypeUndefined) {
|
|
|
|
*ResultLength = sizeof(INTERFACE_TYPE);
|
|
if(*ResultLength <= BufferLength) {
|
|
|
|
*(PINTERFACE_TYPE)PropertyBuffer = deviceNode->ChildInterfaceType;
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
} else {
|
|
|
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
return status;
|
|
|
|
case DevicePropertyBusNumber:
|
|
//
|
|
// Retrieve the property from the parent's devnode field.
|
|
//
|
|
if ((deviceNode->ChildBusNumber & 0x80000000) != 0x80000000) {
|
|
|
|
*ResultLength = sizeof(ULONG);
|
|
if(*ResultLength <= BufferLength) {
|
|
|
|
*(PULONG)PropertyBuffer = deviceNode->ChildBusNumber;
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
} else {
|
|
|
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
return status;
|
|
|
|
case DevicePropertyEnumeratorName:
|
|
|
|
ASSERT (0 == (1 & BufferLength)); // had better be an even length
|
|
deviceInstanceName = deviceNode->InstancePath.Buffer;
|
|
//
|
|
// There should always be a string here, except for (possibly)
|
|
// HTREE\Root\0, but no one should ever be calling us with that PDO
|
|
// anyway.
|
|
//
|
|
ASSERT (deviceInstanceName);
|
|
//
|
|
// We know we're going to find a separator character (\) in the string,
|
|
// so the fact that unicode strings may not be null-terminated isn't
|
|
// a problem.
|
|
//
|
|
enumeratorNameEnd = wcschr(deviceInstanceName, OBJ_NAME_PATH_SEPARATOR);
|
|
ASSERT (enumeratorNameEnd);
|
|
//
|
|
// Compute required length, minus null terminating character.
|
|
//
|
|
length = (ULONG)((PUCHAR)enumeratorNameEnd - (PUCHAR)deviceInstanceName);
|
|
//
|
|
// Store required length in caller-supplied OUT parameter.
|
|
//
|
|
*ResultLength = length + sizeof(UNICODE_NULL);
|
|
if(*ResultLength > BufferLength) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
|
|
RtlCopyMemory((PUCHAR)PropertyBuffer, (PUCHAR)deviceInstanceName, length);
|
|
*(PWCHAR)((PUCHAR)PropertyBuffer + length) = UNICODE_NULL;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
|
|
case DevicePropertyAddress:
|
|
|
|
status = PpIrpQueryCapabilities(DeviceObject, &capabilities);
|
|
if (NT_SUCCESS(status) && (capabilities.Address != 0xFFFFFFFF)) {
|
|
|
|
*ResultLength = sizeof(ULONG);
|
|
if(*ResultLength <= BufferLength) {
|
|
|
|
*(PULONG)PropertyBuffer = capabilities.Address;
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
} else {
|
|
|
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
return status;
|
|
|
|
case DevicePropertyRemovalPolicy:
|
|
|
|
*ResultLength = sizeof(ULONG);
|
|
if(*ResultLength <= BufferLength) {
|
|
|
|
PpHotSwapGetDevnodeRemovalPolicy(
|
|
deviceNode,
|
|
TRUE, // Include Registry Override
|
|
(PDEVICE_REMOVAL_POLICY) PropertyBuffer
|
|
);
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
return status;
|
|
|
|
case DevicePropertyUINumber:
|
|
|
|
valueName = REGSTR_VALUE_UI_NUMBER;
|
|
valueType = REG_DWORD;
|
|
break;
|
|
|
|
case DevicePropertyLocationInformation:
|
|
|
|
valueName = REGSTR_VALUE_LOCATION_INFORMATION;
|
|
valueType = REG_SZ;
|
|
break;
|
|
|
|
case DevicePropertyDeviceDescription:
|
|
|
|
valueName = REGSTR_VALUE_DEVICE_DESC;
|
|
valueType = REG_SZ;
|
|
break;
|
|
|
|
case DevicePropertyHardwareID:
|
|
|
|
valueName = REGSTR_VALUE_HARDWAREID;
|
|
valueType = REG_MULTI_SZ;
|
|
break;
|
|
|
|
case DevicePropertyCompatibleIDs:
|
|
|
|
valueName = REGSTR_VALUE_COMPATIBLEIDS;
|
|
valueType = REG_MULTI_SZ;
|
|
break;
|
|
|
|
case DevicePropertyBootConfiguration:
|
|
|
|
keyName = REGSTR_KEY_LOG_CONF;
|
|
valueName = REGSTR_VAL_BOOTCONFIG;
|
|
valueType = REG_RESOURCE_LIST;
|
|
break;
|
|
|
|
case DevicePropertyBootConfigurationTranslated:
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
break;
|
|
|
|
case DevicePropertyClassName:
|
|
|
|
valueName = REGSTR_VALUE_CLASS;
|
|
valueType = REG_SZ;
|
|
break;
|
|
|
|
case DevicePropertyClassGuid:
|
|
valueName = REGSTR_VALUE_CLASSGUID;
|
|
valueType = REG_SZ;
|
|
break;
|
|
|
|
case DevicePropertyDriverKeyName:
|
|
|
|
valueName = REGSTR_VALUE_DRIVER;
|
|
valueType = REG_SZ;
|
|
break;
|
|
|
|
case DevicePropertyManufacturer:
|
|
|
|
valueName = REGSTR_VAL_MFG;
|
|
valueType = REG_SZ;
|
|
break;
|
|
|
|
case DevicePropertyFriendlyName:
|
|
|
|
valueName = REGSTR_VALUE_FRIENDLYNAME;
|
|
valueType = REG_SZ;
|
|
break;
|
|
|
|
case DevicePropertyInstallState:
|
|
|
|
if (deviceNode == IopRootDeviceNode) {
|
|
//
|
|
// The root devnode is always installed, by definition. We
|
|
// specifically set it's InstallState here because the
|
|
// CONFIGFLAG_REINSTALL flag will wunfortunately still exist on the
|
|
// root devnode reg key on a running system (we should fix that
|
|
// later).
|
|
//
|
|
deviceInstallState = InstallStateInstalled;
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
//
|
|
// For all other devnodes, walk up the devnode tree, retrieving the
|
|
// install state of all ancestors up to (but not including) the root
|
|
// devnode. We'll stop when we've reached the top of the tree, or
|
|
// when some intermediate device has an "uninstalled" install state.
|
|
//
|
|
|
|
valueName = REGSTR_VALUE_CONFIG_FLAGS;
|
|
valueType = REG_DWORD;
|
|
|
|
do {
|
|
//
|
|
// Get the ConfigFlags registry value.
|
|
//
|
|
length = sizeof(ULONG);
|
|
status = PiGetDeviceRegistryProperty(
|
|
deviceNode->PhysicalDeviceObject,
|
|
valueType,
|
|
valueName,
|
|
keyName,
|
|
(PVOID)&configFlags,
|
|
&length
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// The install state is just a subset of the device's ConfigFlags
|
|
//
|
|
if (configFlags & CONFIGFLAG_REINSTALL) {
|
|
|
|
deviceInstallState = InstallStateNeedsReinstall;
|
|
|
|
} else if (configFlags & CONFIGFLAG_FAILEDINSTALL) {
|
|
|
|
deviceInstallState = InstallStateFailedInstall;
|
|
|
|
} else if (configFlags & CONFIGFLAG_FINISH_INSTALL) {
|
|
|
|
deviceInstallState = InstallStateFinishInstall;
|
|
} else {
|
|
|
|
deviceInstallState = InstallStateInstalled;
|
|
}
|
|
} else {
|
|
deviceInstallState = InstallStateFailedInstall;
|
|
break;
|
|
}
|
|
|
|
deviceNode = deviceNode->Parent;
|
|
|
|
} while ((deviceInstallState == InstallStateInstalled) &&
|
|
(deviceNode != IopRootDeviceNode));
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
*ResultLength = sizeof(ULONG);
|
|
if(*ResultLength <= BufferLength) {
|
|
*(PDEVICE_INSTALL_STATE)PropertyBuffer = deviceInstallState;
|
|
} else {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
|
|
default:
|
|
|
|
return STATUS_INVALID_PARAMETER_2;
|
|
}
|
|
//
|
|
// Get the registry value.
|
|
//
|
|
*ResultLength = BufferLength;
|
|
status = PiGetDeviceRegistryProperty(
|
|
DeviceObject,
|
|
valueType,
|
|
valueName,
|
|
keyName,
|
|
PropertyBuffer,
|
|
ResultLength
|
|
);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PiGetDeviceRegistryProperty(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG ValueType,
|
|
IN PWSTR ValueName,
|
|
IN PWSTR KeyName,
|
|
OUT PVOID Buffer,
|
|
IN OUT PULONG BufferLength
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE handle, subKeyHandle;
|
|
UNICODE_STRING unicodeKey;
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Enter critical section and acquire a lock on the registry. Both these
|
|
// mechanisms are required to prevent deadlock in the case where an APC
|
|
// routine calls this routine after the registry resource has been claimed
|
|
// in this case it would wait blocking this thread so the registry would
|
|
// never be released -> deadlock. Critical sectioning the registry manipulatio
|
|
// portion solves this problem
|
|
//
|
|
PiLockPnpRegistry(TRUE);
|
|
//
|
|
// Based on the PDO specified by caller, find the handle of its device
|
|
// instance registry key.
|
|
//
|
|
status = IopDeviceObjectToDeviceInstance(DeviceObject, &handle, KEY_READ);
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// If the data is stored in a subkey then open this key and close the old one
|
|
//
|
|
if (KeyName) {
|
|
|
|
RtlInitUnicodeString(&unicodeKey, KeyName);
|
|
status = IopOpenRegistryKeyEx( &subKeyHandle,
|
|
handle,
|
|
&unicodeKey,
|
|
KEY_READ
|
|
);
|
|
if(NT_SUCCESS(status)){
|
|
|
|
ZwClose(handle);
|
|
handle = subKeyHandle;
|
|
}
|
|
}
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Read the registry value of the desired value name
|
|
//
|
|
status = IopGetRegistryValue (handle,
|
|
ValueName,
|
|
&keyValueInformation);
|
|
}
|
|
//
|
|
// We have finished using the registry so clean up and release our resources
|
|
//
|
|
ZwClose(handle);
|
|
}
|
|
PiUnlockPnpRegistry();
|
|
//
|
|
// If we have been sucessfull in finding the info hand it back to the caller
|
|
//
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Check that the buffer we have been given is big enough and that the value returned is
|
|
// of the correct registry type
|
|
//
|
|
if (*BufferLength >= keyValueInformation->DataLength) {
|
|
|
|
if (keyValueInformation->Type == ValueType) {
|
|
|
|
RtlCopyMemory( Buffer,
|
|
KEY_VALUE_DATA(keyValueInformation),
|
|
keyValueInformation->DataLength);
|
|
} else {
|
|
|
|
status = STATUS_INVALID_PARAMETER_2;
|
|
}
|
|
} else {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
*BufferLength = keyValueInformation->DataLength;
|
|
ExFreePool(keyValueInformation);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
IoOpenDeviceRegistryKey(
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN ULONG DevInstKeyType,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT PHANDLE DevInstRegKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a handle to an opened registry key that the driver
|
|
may use to store/retrieve configuration information specific to a particular
|
|
device instance.
|
|
|
|
The driver must call ZwClose to close the handle returned from this api
|
|
when access is no longer required.
|
|
|
|
Parameters:
|
|
|
|
DeviceObject - Supples the device object of the physical device instance to
|
|
open a registry storage key for. Normally it is a device object
|
|
created by the hal bus extender.
|
|
|
|
DevInstKeyType - Supplies flags specifying which storage key associated with
|
|
the device instance is to be opened. May be a combination of
|
|
the following value:
|
|
|
|
PLUGPLAY_REGKEY_DEVICE - Open a key for storing device specific
|
|
(driver-independent) information relating to the device instance.
|
|
The flag may not be specified with PLUGPLAY_REGKEY_DRIVER.
|
|
|
|
PLUGPLAY_REGKEY_DRIVER - Open a key for storing driver-specific
|
|
information relating to the device instance, This flag may
|
|
not be specified with PLUGPLAY_REGKEY_DEVICE.
|
|
|
|
PLUGPLAY_REGKEY_CURRENT_HWPROFILE - If this flag is specified,
|
|
then a key in the current hardware profile branch will be
|
|
opened for the specified storage type. This allows the driver
|
|
to access configuration information that is hardware profile
|
|
specific.
|
|
|
|
DesiredAccess - Specifies the access mask for the key to be opened.
|
|
|
|
DevInstRegKey - Supplies the address of a variable that receives a handle to the
|
|
opened key for the specified registry storage location.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// IoOpenDeviceRegistryKey does not support creating a driver key if none
|
|
// exists. This is for internal use only.
|
|
//
|
|
return IopOpenOrCreateDeviceRegistryKey(PhysicalDeviceObject,
|
|
DevInstKeyType,
|
|
DesiredAccess,
|
|
FALSE, // do not create
|
|
DevInstRegKey);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
IopOpenOrCreateDeviceRegistryKey(
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN ULONG DevInstKeyType,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN BOOLEAN Create,
|
|
OUT PHANDLE DevInstRegKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns a handle to an opened registry key that the driver
|
|
may use to store/retrieve configuration information specific to a particular
|
|
device instance.
|
|
|
|
The driver must call ZwClose to close the handle returned from this api
|
|
when access is no longer required.
|
|
|
|
Parameters:
|
|
|
|
DeviceObject - Supples the device object of the physical device instance to
|
|
open a registry storage key for. Normally it is a device object
|
|
created by the hal bus extender.
|
|
|
|
DevInstKeyType - Supplies flags specifying which storage key associated with
|
|
the device instance is to be opened. May be a combination of
|
|
the following value:
|
|
|
|
PLUGPLAY_REGKEY_DEVICE - Open a key for storing device specific
|
|
(driver-independent) information relating to the device instance.
|
|
The flag may not be specified with PLUGPLAY_REGKEY_DRIVER.
|
|
|
|
PLUGPLAY_REGKEY_DRIVER - Open a key for storing driver-specific
|
|
information relating to the device instance, This flag may
|
|
not be specified with PLUGPLAY_REGKEY_DEVICE.
|
|
|
|
PLUGPLAY_REGKEY_CURRENT_HWPROFILE - If this flag is specified,
|
|
then a key in the current hardware profile branch will be
|
|
opened for the specified storage type. This allows the driver
|
|
to access configuration information that is hardware profile
|
|
specific.
|
|
|
|
DesiredAccess - Specifies the access mask for the key to be opened.
|
|
|
|
Create - Specifies whether the key should be created if not present (applies
|
|
only to PLUGPLAY_REGKEY_DRIVER; for PLUGPLAY_REGKEY_DEVICE,
|
|
a key is always created).
|
|
|
|
DevInstRegKey - Supplies the address of a variable that receives a handle to the
|
|
opened key for the specified registry storage location.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
Notes:
|
|
|
|
** The Create parameter ONLY applies to DevInstKeyType == PLUGPLAY_REGKEY_DRIVER!
|
|
|
|
-- For PLUGPLAY_REGKEY_DEVICE, a key is always created, so this parameter
|
|
will be ignored.
|
|
|
|
-- A hardware-profile-specific subkey will only be created for
|
|
|
|
PLUGPLAY_REGKEY_DRIVER | PLUGPLAY_REGKEY_CURRENT_HWPROFILE
|
|
|
|
if a corresponding non-hardware-profile-specific Driver key has already
|
|
been created for the device in the global CurrentControlSet.
|
|
|
|
If this routine is needed to create a hardware-profile-specific Driver key
|
|
when no global Driver key exists yet for the device, then you must modify
|
|
the code below to first search for and create an available driver instance
|
|
key in both the global AND hardware profile-specific CurrentControlSet
|
|
branches.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status, appendStatus;
|
|
HANDLE hBasePath;
|
|
UNICODE_STRING unicodeBasePath, unicodeRestPath;
|
|
WCHAR drvInst[GUID_STRING_LEN + 5];
|
|
ULONG drvInstLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Until SCSIPORT stops passing non PDOs allow the system to boot.
|
|
//
|
|
// ASSERT_PDO(PhysicalDeviceObject);
|
|
//
|
|
|
|
//
|
|
// Initialise out parameters
|
|
//
|
|
|
|
*DevInstRegKey = NULL;
|
|
|
|
//
|
|
// Allocate a buffer to build the RestPath string in
|
|
//
|
|
|
|
unicodeRestPath.Buffer = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, MAX_RESTPATH_BUF_LEN);
|
|
|
|
if (unicodeRestPath.Buffer == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto clean0;
|
|
}
|
|
|
|
unicodeRestPath.Length=0;
|
|
unicodeRestPath.MaximumLength=MAX_RESTPATH_BUF_LEN;
|
|
|
|
//
|
|
// Select the base path to the CurrentControlSet based on if we are dealing with
|
|
// a hardware profile or not
|
|
//
|
|
|
|
if (DevInstKeyType & PLUGPLAY_REGKEY_CURRENT_HWPROFILE) {
|
|
PiWstrToUnicodeString(&unicodeBasePath, PATH_CURRENTCONTROLSET_HW_PROFILE_CURRENT);
|
|
|
|
} else {
|
|
PiWstrToUnicodeString(&unicodeBasePath, PATH_CURRENTCONTROLSET);
|
|
}
|
|
|
|
//
|
|
// Enter critical section and acquire a lock on the registry. Both these
|
|
// mechanisms are required to prevent deadlock in the case where an APC
|
|
// routine calls this routine after the registry resource has been claimed
|
|
// in this case it would wait blocking this thread so the registry would
|
|
// never be released -> deadlock. Critical sectioning the registry manipulation
|
|
// portion solves this problem
|
|
//
|
|
PiLockPnpRegistry(TRUE);
|
|
|
|
//
|
|
// Open the base registry key
|
|
//
|
|
|
|
status = IopOpenRegistryKeyEx( &hBasePath,
|
|
NULL,
|
|
&unicodeBasePath,
|
|
KEY_READ
|
|
);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
goto clean1;
|
|
}
|
|
|
|
//
|
|
// Build the RestPath string
|
|
//
|
|
|
|
switch (DevInstKeyType) {
|
|
|
|
case PLUGPLAY_REGKEY_DEVICE:
|
|
case PLUGPLAY_REGKEY_DEVICE + PLUGPLAY_REGKEY_CURRENT_HWPROFILE:
|
|
{
|
|
PDEVICE_NODE pDeviceNode;
|
|
|
|
//
|
|
// Initialise the rest path with Enum\
|
|
//
|
|
|
|
appendStatus = RtlAppendUnicodeToString(&unicodeRestPath, PATH_ENUM);
|
|
ASSERT(NT_SUCCESS( appendStatus ));
|
|
//
|
|
// Get the Enumerator\DeviceID\InstanceID path from the DeviceNode
|
|
//
|
|
|
|
pDeviceNode = (PDEVICE_NODE) PhysicalDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
//
|
|
// Ensure this is a PDO and not an FDO (only PDO's have a DeviceNode)
|
|
//
|
|
|
|
if (pDeviceNode) {
|
|
appendStatus = RtlAppendUnicodeStringToString(&unicodeRestPath, &(pDeviceNode->InstancePath));
|
|
ASSERT(NT_SUCCESS( appendStatus ));
|
|
} else {
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case PLUGPLAY_REGKEY_DRIVER:
|
|
case PLUGPLAY_REGKEY_DRIVER + PLUGPLAY_REGKEY_CURRENT_HWPROFILE:
|
|
{
|
|
|
|
HANDLE hDeviceKey;
|
|
|
|
//
|
|
// Initialise the rest path with Control\Class\
|
|
//
|
|
|
|
appendStatus = RtlAppendUnicodeToString(&unicodeRestPath, PATH_CONTROL_CLASS);
|
|
ASSERT(NT_SUCCESS( appendStatus ));
|
|
|
|
//
|
|
// Open the device instance key for this device
|
|
//
|
|
|
|
status = IopDeviceObjectToDeviceInstance(PhysicalDeviceObject, &hDeviceKey, KEY_READ);
|
|
|
|
if(!NT_SUCCESS(status)){
|
|
goto clean1;
|
|
}
|
|
|
|
//
|
|
// See if we have a driver value
|
|
//
|
|
|
|
status = IoGetDeviceProperty(PhysicalDeviceObject, DevicePropertyDriverKeyName, sizeof(drvInst), drvInst, &drvInstLength);
|
|
if(NT_SUCCESS(status)){
|
|
//
|
|
// Append <DevInstClass>\<ClassInstanceOrdinal>
|
|
//
|
|
appendStatus = RtlAppendUnicodeToString(&unicodeRestPath, drvInst);
|
|
ASSERT(NT_SUCCESS( appendStatus ));
|
|
|
|
} else if ((status == STATUS_OBJECT_NAME_NOT_FOUND) &&
|
|
Create &&
|
|
((DevInstKeyType & PLUGPLAY_REGKEY_CURRENT_HWPROFILE) == 0)) {
|
|
|
|
//
|
|
// No Driver value exists for this device yet, but we've been
|
|
// explicitly asked to create one now.
|
|
//
|
|
// NOTE: 01-Dec-2001 : Jim Cavalaris (jamesca)
|
|
//
|
|
// Note that we only do this for the global driver key - not the
|
|
// hardware profile specific one. If this routine is used to
|
|
// create hardware profile keys also, then you must search for
|
|
// and create an available driver instance key in both the
|
|
// global AND hardware profile-specific CurrentControlSet
|
|
// branches.
|
|
//
|
|
|
|
WCHAR classGUID[GUID_STRING_LEN];
|
|
ULONG classGUIDLength;
|
|
HANDLE hClassGUIDKey;
|
|
|
|
//
|
|
// See if we have a class GUID value.
|
|
//
|
|
|
|
status = IoGetDeviceProperty(PhysicalDeviceObject,
|
|
DevicePropertyClassGuid,
|
|
sizeof(classGUID),
|
|
classGUID,
|
|
&classGUIDLength);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Open or create the key for this device's Class.
|
|
//
|
|
appendStatus = RtlAppendUnicodeToString(&unicodeRestPath, classGUID);
|
|
ASSERT(NT_SUCCESS( appendStatus ));
|
|
|
|
status = IopCreateRegistryKeyEx(&hClassGUIDKey,
|
|
hBasePath,
|
|
&unicodeRestPath,
|
|
KEY_ALL_ACCESS, // need to create
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ULONG instance;
|
|
WCHAR instanceOrdinal[5];
|
|
UNICODE_STRING tempString;
|
|
HANDLE hDriverInstanceKey;
|
|
ULONG disposition;
|
|
|
|
for (instance = 0; instance < 9999; instance++) {
|
|
//
|
|
// Find the first available class instance key.
|
|
//
|
|
StringCbPrintfW(instanceOrdinal, sizeof(instanceOrdinal), TEXT("%04u"), instance);
|
|
|
|
RtlInitUnicodeString(&tempString, instanceOrdinal);
|
|
|
|
hDriverInstanceKey = NULL;
|
|
|
|
status = IopCreateRegistryKeyEx(&hDriverInstanceKey,
|
|
hClassGUIDKey,
|
|
&tempString,
|
|
DesiredAccess,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&disposition);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (disposition == REG_CREATED_NEW_KEY) {
|
|
//
|
|
// Set the Driver registry value in the
|
|
// device instance key.
|
|
//
|
|
StringCbPrintfW(drvInst,
|
|
sizeof(drvInst),
|
|
TEXT("%s\\%s"),
|
|
classGUID,
|
|
instanceOrdinal);
|
|
|
|
PiWstrToUnicodeString(&tempString, REGSTR_VALUE_DRIVER);
|
|
|
|
status = ZwSetValueKey(hDeviceKey,
|
|
&tempString,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
drvInst,
|
|
(ULONG)((wcslen(drvInst)+1) * sizeof(WCHAR)));
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
appendStatus = RtlAppendUnicodeToString(&unicodeRestPath, TEXT("\\"));
|
|
ASSERT(NT_SUCCESS( appendStatus ));
|
|
|
|
appendStatus = RtlAppendUnicodeToString(&unicodeRestPath, instanceOrdinal);
|
|
ASSERT(NT_SUCCESS( appendStatus ));
|
|
|
|
} else {
|
|
//
|
|
// Delete the key we just created.
|
|
//
|
|
ZwDeleteKey(hDriverInstanceKey);
|
|
}
|
|
|
|
//
|
|
// Always close the key we just created, so we
|
|
// can open it below for the DesiredAccess of
|
|
// the caller.
|
|
//
|
|
ZwClose(hDriverInstanceKey);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Always close the key we just created, so we
|
|
// can open it below for the DesiredAccess of
|
|
// the caller.
|
|
//
|
|
ZwClose(hDriverInstanceKey);
|
|
}
|
|
}
|
|
|
|
if (instance == 9999) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
ZwClose(hClassGUIDKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
ZwClose(hDeviceKey);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
|
|
//
|
|
// ISSUE 2001/02/08 ADRIAO - This is parameter #2, not parameter #3!
|
|
//
|
|
status = STATUS_INVALID_PARAMETER_3;
|
|
goto clean2;
|
|
}
|
|
|
|
|
|
//
|
|
// If we succeeded in building the rest path then open the key and hand it back to the caller
|
|
//
|
|
|
|
if (NT_SUCCESS(status)){
|
|
if (DevInstKeyType == PLUGPLAY_REGKEY_DEVICE) {
|
|
|
|
status = IopOpenDeviceParametersSubkey(DevInstRegKey,
|
|
hBasePath,
|
|
&unicodeRestPath,
|
|
DesiredAccess);
|
|
} else {
|
|
|
|
status = IopCreateRegistryKeyEx( DevInstRegKey,
|
|
hBasePath,
|
|
&unicodeRestPath,
|
|
DesiredAccess,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free up resources
|
|
//
|
|
|
|
clean2:
|
|
ZwClose(hBasePath);
|
|
clean1:
|
|
PiUnlockPnpRegistry();
|
|
ExFreePool(unicodeRestPath.Buffer);
|
|
clean0:
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
IoSynchronousInvalidateDeviceRelations(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN DEVICE_RELATION_TYPE Type
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API notifies the system that changes have occurred in the device
|
|
relations of the specified type for the supplied DeviceObject. All
|
|
cached information concerning the relationships must be invalidated,
|
|
and if needed re-obtained via IRP_MN_QUERY_DEVICE_RELATIONS.
|
|
|
|
This routine performs device enumeration synchronously.
|
|
Note, A driver can NOT call this IO api while processing pnp irps AND
|
|
A driver can NOT call this api from any system thread except the system
|
|
threads created by the driver itself.
|
|
|
|
Parameters:
|
|
|
|
DeviceObject - the PDEVICE_OBJECT for which the specified relation type
|
|
information has been invalidated. This pointer is valid
|
|
for the duration of the call.
|
|
|
|
Type - specifies the type of the relation being invalidated.
|
|
|
|
ReturnValue:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_NODE deviceNode;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
KEVENT completionEvent;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_PDO(DeviceObject);
|
|
|
|
switch (Type) {
|
|
case BusRelations:
|
|
|
|
if (PnPInitialized) {
|
|
|
|
deviceNode = (PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if (deviceNode->State == DeviceNodeStarted) {
|
|
|
|
KeInitializeEvent( &completionEvent, NotificationEvent, FALSE );
|
|
|
|
status = PipRequestDeviceAction( DeviceObject,
|
|
ReenumerateDeviceTree,
|
|
FALSE,
|
|
0,
|
|
&completionEvent,
|
|
NULL );
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = KeWaitForSingleObject( &completionEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
} else {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
} else {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
break;
|
|
|
|
case EjectionRelations:
|
|
|
|
//
|
|
// For Ejection relation change, we will ignore it. We don't keep track
|
|
// the Ejection relation. We will query the Ejection relation only when
|
|
// we are requested to eject a device.
|
|
//
|
|
|
|
status = STATUS_NOT_SUPPORTED;
|
|
break;
|
|
|
|
case PowerRelations:
|
|
|
|
|
|
//
|
|
// Call off to Po code, which will do the right thing
|
|
//
|
|
PoInvalidateDevicePowerRelations(DeviceObject);
|
|
break;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
IoInvalidateDeviceRelations(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN DEVICE_RELATION_TYPE Type
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API notifies the system that changes have occurred in the device
|
|
relations of the specified type for the supplied DeviceObject. All
|
|
cached information concerning the relationships must be invalidated,
|
|
and if needed re-obtained via IRP_MN_QUERY_DEVICE_RELATIONS.
|
|
|
|
Parameters:
|
|
|
|
DeviceObject - the PDEVICE_OBJECT for which the specified relation type
|
|
information has been invalidated. This pointer is valid
|
|
for the duration of the call.
|
|
|
|
Type - specifies the type of the relation being invalidated.
|
|
|
|
ReturnValue:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
ASSERT_PDO(DeviceObject);
|
|
|
|
switch (Type) {
|
|
case BusRelations:
|
|
case SingleBusRelations:
|
|
|
|
//
|
|
// If the call was made before PnP completes device enumeration
|
|
// we can safely ignore it. PnP manager will do it without
|
|
// driver's request.
|
|
//
|
|
|
|
deviceNode = (PDEVICE_NODE) DeviceObject->DeviceObjectExtension->DeviceNode;
|
|
if (deviceNode) {
|
|
|
|
PipRequestDeviceAction( DeviceObject,
|
|
Type == BusRelations ?
|
|
ReenumerateDeviceTree : ReenumerateDeviceOnly,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
NULL );
|
|
}
|
|
break;
|
|
|
|
case EjectionRelations:
|
|
|
|
//
|
|
// For Ejection relation change, we will ignore it. We don't keep track
|
|
// the Ejection relation. We will query the Ejection relation only when
|
|
// we are requested to eject a device.
|
|
|
|
break;
|
|
|
|
case PowerRelations:
|
|
|
|
//
|
|
// Call off to Po code, which will do the right thing
|
|
//
|
|
PoInvalidateDevicePowerRelations(DeviceObject);
|
|
break;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
IoRequestDeviceEject(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API notifies that the device eject button has been pressed. This API must
|
|
be called at IRQL <= DISPATCH_LEVEL.
|
|
|
|
This API informs PnP that a device eject has been requested, the device will
|
|
not necessarily be ejected as a result of this API. The device will only be
|
|
ejected if the drivers associated with it agree to stop and the device is
|
|
successfully powered down. Note that eject in this context refers to device
|
|
eject, not to media (floppies, cds, tapes) eject. For example, eject of a
|
|
cd-rom disk drive, not ejection of a cd-rom disk.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the PDEVICE_OBJECT for the device whose eject button has
|
|
been pressed. This pointer is valid for the duration of
|
|
the call, if the API wants to keep a copy of it, it
|
|
should obtain its own reference to the object
|
|
(ObReferenceObject).
|
|
|
|
ReturnValue:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT_PDO(DeviceObject);
|
|
|
|
IopQueueDeviceWorkItem(DeviceObject, IopRequestDeviceEjectWorker, NULL);
|
|
}
|
|
|
|
VOID
|
|
IopRequestDeviceEjectWorker(
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PDEVICE_WORK_ITEM deviceWorkItem = (PDEVICE_WORK_ITEM)Context;
|
|
PDEVICE_OBJECT deviceObject = deviceWorkItem->DeviceObject;
|
|
|
|
ExFreePool(deviceWorkItem);
|
|
|
|
//
|
|
// Queue the event, we'll return immediately after it's queued.
|
|
//
|
|
PpSetTargetDeviceRemove( deviceObject,
|
|
TRUE,
|
|
TRUE,
|
|
FALSE,
|
|
TRUE,
|
|
CM_PROB_HELD_FOR_EJECT,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IoReportDetectedDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN INTERFACE_TYPE LegacyBusType,
|
|
IN ULONG BusNumber,
|
|
IN ULONG SlotNumber,
|
|
IN PCM_RESOURCE_LIST ResourceList,
|
|
IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirements OPTIONAL,
|
|
IN BOOLEAN ResourceAssigned,
|
|
IN OUT PDEVICE_OBJECT *DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
PnP device drivers call this API to report any device detected. This routine
|
|
creates a Physical Device object, reference the Physical Device object and
|
|
returns back to the callers. Once the detected device is reported, the Pnp manager
|
|
considers the device has been fully controlled by the reporting drivers. Thus it
|
|
will not invoke AddDevice entry and send StartDevice irp to the driver.
|
|
|
|
The driver needs to report the resources it used to detect this device such that
|
|
pnp manager can perform duplicates detection on this device.
|
|
|
|
The caller must dereference the DeviceObject once it no longer needs it.
|
|
|
|
Parameters:
|
|
|
|
DriverObject - Supplies the driver object of the driver who detected
|
|
this device.
|
|
|
|
ResourceList - Supplies a pointer to the resource list which the driver used
|
|
to detect the device.
|
|
|
|
ResourceRequirements - supplies a pointer to the resource requirements list
|
|
for the detected device. This is optional.
|
|
|
|
ResourceAssigned - if TRUE, the driver already called IoReportResourceUsage or
|
|
IoAssignResource to get the ownership of the resources. Otherwise,
|
|
the PnP manager will call IoReportResourceUsage to allocate the
|
|
resources for the driver.
|
|
|
|
DeviceObject - if NULL, this routine will create a PDO and return it thru this variable.
|
|
Otherwise, a PDO is already created and this routine will simply use the supplied
|
|
PDO.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR buffer[MAX_DEVICE_ID_LEN], *end, *name;
|
|
NTSTATUS status;
|
|
UNICODE_STRING deviceName, instanceName, unicodeName, *serviceName, driverName;
|
|
PDEVICE_NODE deviceNode;
|
|
ULONG length, i = 0, disposition, tmpValue, listSize = 0;
|
|
HANDLE handle = NULL, handle1, logConfHandle = NULL, controlHandle = NULL, enumHandle;
|
|
PCM_RESOURCE_LIST cmResource;
|
|
PWSTR p;
|
|
PDEVICE_OBJECT deviceObject;
|
|
BOOLEAN newlyCreated = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (*DeviceObject) {
|
|
|
|
deviceObject = *DeviceObject;
|
|
|
|
//
|
|
// The PDO is already known. simply handle the resourcelist and resreq list.
|
|
// This is a hack for NDIS drivers.
|
|
//
|
|
deviceNode = (PDEVICE_NODE)(*DeviceObject)->DeviceObjectExtension->DeviceNode;
|
|
if (!deviceNode) {
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
PiLockPnpRegistry(FALSE);
|
|
|
|
//
|
|
// Write ResourceList and ResReq list to the device instance
|
|
//
|
|
|
|
status = IopDeviceObjectToDeviceInstance (*DeviceObject,
|
|
&handle,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
PiUnlockPnpRegistry();
|
|
return status;
|
|
}
|
|
if (ResourceAssigned) {
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_NO_RESOURCE_AT_INIT);
|
|
tmpValue = 1;
|
|
ZwSetValueKey(handle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(tmpValue)
|
|
);
|
|
}
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
|
status = IopCreateRegistryKeyEx( &logConfHandle,
|
|
handle,
|
|
&unicodeName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL
|
|
);
|
|
ZwClose(handle);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Write the ResourceList and and ResourceRequirements to the logconf key under
|
|
// device instance key.
|
|
//
|
|
|
|
if (ResourceList) {
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_BOOTCONFIG);
|
|
ZwSetValueKey(
|
|
logConfHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_RESOURCE_LIST,
|
|
ResourceList,
|
|
listSize = IopDetermineResourceListSize(ResourceList)
|
|
);
|
|
}
|
|
if (ResourceRequirements) {
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_BASIC_CONFIG_VECTOR);
|
|
ZwSetValueKey(
|
|
logConfHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_RESOURCE_REQUIREMENTS_LIST,
|
|
ResourceRequirements,
|
|
ResourceRequirements->ListSize
|
|
);
|
|
}
|
|
ZwClose(logConfHandle);
|
|
}
|
|
PiUnlockPnpRegistry();
|
|
if (NT_SUCCESS(status)) {
|
|
goto checkResource;
|
|
} else {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Normal case: *DeviceObject is NULL
|
|
//
|
|
|
|
*DeviceObject = NULL;
|
|
serviceName = &DriverObject->DriverExtension->ServiceKeyName;
|
|
|
|
//
|
|
// Special handling for driver object created thru IoCreateDriver.
|
|
// When a builtin driver calls IoReportDetectedDevice, the ServiceKeyName of
|
|
// the driver object is set to \Driver\DriverName. To create a detected device
|
|
// instance key, we will take only the DriverName.
|
|
//
|
|
|
|
if (DriverObject->Flags & DRVO_BUILTIN_DRIVER) {
|
|
p = serviceName->Buffer + (serviceName->Length / sizeof(WCHAR)) - 1;
|
|
driverName.Length = 0;
|
|
while (*p != '\\' && (p != serviceName->Buffer)) {
|
|
p--;
|
|
driverName.Length += sizeof(WCHAR);
|
|
}
|
|
if (p == serviceName->Buffer) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
} else {
|
|
p++;
|
|
driverName.Buffer = p;
|
|
driverName.MaximumLength = driverName.Length + sizeof(WCHAR);
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Before doing anything first perform duplicate detection
|
|
//
|
|
|
|
status = IopDuplicateDetection( LegacyBusType,
|
|
BusNumber,
|
|
SlotNumber,
|
|
&deviceNode
|
|
);
|
|
|
|
if (NT_SUCCESS(status) && deviceNode) {
|
|
|
|
deviceObject = deviceNode->PhysicalDeviceObject;
|
|
|
|
if (PipAreDriversLoaded(deviceNode) ||
|
|
(PipDoesDevNodeHaveProblem(deviceNode) &&
|
|
deviceNode->Problem != CM_PROB_NOT_CONFIGURED &&
|
|
deviceNode->Problem != CM_PROB_REINSTALL &&
|
|
deviceNode->Problem != CM_PROB_FAILED_INSTALL)) {
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
deviceNode->Flags &= ~DNF_HAS_PROBLEM;
|
|
deviceNode->Problem = 0;
|
|
|
|
IopDeleteLegacyKey(DriverObject);
|
|
goto checkResource;
|
|
}
|
|
|
|
driverName.Buffer = NULL;
|
|
}
|
|
|
|
//
|
|
// Create a PDO
|
|
//
|
|
|
|
status = IoCreateDevice( IoPnpDriverObject,
|
|
sizeof(IOPNP_DEVICE_EXTENSION),
|
|
NULL,
|
|
FILE_DEVICE_CONTROLLER,
|
|
FILE_AUTOGENERATED_DEVICE_NAME,
|
|
FALSE,
|
|
&deviceObject );
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
deviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE; // Mark this is a PDO
|
|
status = PipAllocateDeviceNode(deviceObject, &deviceNode);
|
|
if (status != STATUS_SYSTEM_HIVE_TOO_LARGE && deviceNode) {
|
|
|
|
//
|
|
// First delete the Legacy_DriverName key and subkeys from Enum\Root, if exits.
|
|
//
|
|
|
|
if (!(DriverObject->Flags & DRVO_BUILTIN_DRIVER)) {
|
|
IopDeleteLegacyKey(DriverObject);
|
|
}
|
|
|
|
//
|
|
// Create the compatible id list we'll use for this made-up device.
|
|
//
|
|
|
|
status = PpCreateLegacyDeviceIds(
|
|
deviceObject,
|
|
((DriverObject->Flags & DRVO_BUILTIN_DRIVER) ?
|
|
&driverName : serviceName),
|
|
ResourceList);
|
|
|
|
PiLockPnpRegistry(FALSE);
|
|
if(!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Create/Open a registry key for the device instance and
|
|
// write the addr of the device object to registry
|
|
//
|
|
|
|
if (DriverObject->Flags & DRVO_BUILTIN_DRIVER) {
|
|
|
|
name = driverName.Buffer;
|
|
} else {
|
|
|
|
name = serviceName->Buffer;
|
|
}
|
|
StringCchPrintfExW(
|
|
buffer,
|
|
sizeof(buffer) / sizeof(WCHAR),
|
|
&end,
|
|
NULL,
|
|
0,
|
|
L"ROOT\\%s",
|
|
name);
|
|
length = (ULONG)((PBYTE)end - (PBYTE)buffer);
|
|
deviceName.MaximumLength = sizeof(buffer);
|
|
ASSERT(length <= sizeof(buffer) - 10);
|
|
deviceName.Length = (USHORT)length;
|
|
deviceName.Buffer = buffer;
|
|
|
|
status = IopOpenRegistryKeyEx( &enumHandle,
|
|
NULL,
|
|
&CmRegistryMachineSystemCurrentControlSetEnumName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
status = IopCreateRegistryKeyEx( &handle1,
|
|
enumHandle,
|
|
&deviceName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&disposition
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceName.Buffer[deviceName.Length / sizeof(WCHAR)] =
|
|
OBJ_NAME_PATH_SEPARATOR;
|
|
deviceName.Length += sizeof(UNICODE_NULL);
|
|
length += sizeof(UNICODE_NULL);
|
|
if (disposition != REG_CREATED_NEW_KEY) {
|
|
|
|
for ( ; ; ) {
|
|
|
|
deviceName.Length = (USHORT)length;
|
|
PiUlongToInstanceKeyUnicodeString(&instanceName,
|
|
buffer + deviceName.Length / sizeof(WCHAR),
|
|
sizeof(buffer) - deviceName.Length,
|
|
i
|
|
);
|
|
deviceName.Length = (USHORT)(deviceName.Length + instanceName.Length);
|
|
status = IopCreateRegistryKeyEx( &handle,
|
|
handle1,
|
|
&instanceName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&disposition
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (disposition == REG_CREATED_NEW_KEY) {
|
|
ZwClose(handle1);
|
|
break;
|
|
} else {
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInformation = NULL;
|
|
BOOLEAN migratedKey = FALSE, matchingKey = FALSE;
|
|
|
|
//
|
|
// Check if the key exists because it was
|
|
// explicitly migrated during textmode setup.
|
|
//
|
|
status = IopGetRegistryValue(handle,
|
|
REGSTR_VALUE_MIGRATED,
|
|
&keyValueInformation);
|
|
if (NT_SUCCESS(status)) {
|
|
if ((keyValueInformation->Type == REG_DWORD) &&
|
|
(keyValueInformation->DataLength == sizeof(ULONG)) &&
|
|
((*(PULONG)KEY_VALUE_DATA(keyValueInformation)) != 0)) {
|
|
migratedKey = TRUE;
|
|
}
|
|
ExFreePool(keyValueInformation);
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_MIGRATED);
|
|
ZwDeleteValueKey(handle, &unicodeName);
|
|
}
|
|
|
|
if (IopIsReportedAlready(handle, serviceName, ResourceList, &matchingKey)) {
|
|
|
|
ASSERT(matchingKey);
|
|
|
|
//
|
|
// Write the reported resources to registry in case the irq changed
|
|
//
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
|
status = IopCreateRegistryKeyEx( &logConfHandle,
|
|
handle,
|
|
&unicodeName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Write the ResourceList and and ResourceRequirements to the device instance key
|
|
//
|
|
|
|
if (ResourceList) {
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_BOOTCONFIG);
|
|
ZwSetValueKey(
|
|
logConfHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_RESOURCE_LIST,
|
|
ResourceList,
|
|
listSize = IopDetermineResourceListSize(ResourceList)
|
|
);
|
|
}
|
|
if (ResourceRequirements) {
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_BASIC_CONFIG_VECTOR);
|
|
ZwSetValueKey(
|
|
logConfHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_RESOURCE_REQUIREMENTS_LIST,
|
|
ResourceRequirements,
|
|
ResourceRequirements->ListSize
|
|
);
|
|
}
|
|
ZwClose(logConfHandle);
|
|
}
|
|
|
|
PiUnlockPnpRegistry();
|
|
IoDeleteDevice(deviceObject);
|
|
ZwClose(handle1);
|
|
deviceObject = IopDeviceObjectFromDeviceInstance(&deviceName); // Add a reference
|
|
ZwClose(handle);
|
|
ZwClose(enumHandle);
|
|
ASSERT(deviceObject);
|
|
if (deviceObject == NULL) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
return status;
|
|
}
|
|
deviceNode = (PDEVICE_NODE)
|
|
deviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if (PipAreDriversLoaded(deviceNode) ||
|
|
(PipDoesDevNodeHaveProblem(deviceNode) &&
|
|
deviceNode->Problem != CM_PROB_NOT_CONFIGURED &&
|
|
deviceNode->Problem != CM_PROB_REINSTALL &&
|
|
deviceNode->Problem != CM_PROB_FAILED_INSTALL)) {
|
|
|
|
ObDereferenceObject(deviceObject);
|
|
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
goto checkResource;
|
|
|
|
} else if (matchingKey && migratedKey) {
|
|
//
|
|
// We opened an existing key whose Service
|
|
// and Resources match those being reported
|
|
// for this device. No device is yet
|
|
// reported as using this instance, so we'll
|
|
// use it, and treat is as a new key.
|
|
//
|
|
disposition = REG_CREATED_NEW_KEY;
|
|
ZwClose(handle1);
|
|
break;
|
|
|
|
} else {
|
|
i++;
|
|
ZwClose(handle);
|
|
continue;
|
|
}
|
|
}
|
|
} else {
|
|
ZwClose(handle1);
|
|
ZwClose(enumHandle);
|
|
goto exit;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// This is a new device key. So, instance is 0. Create it.
|
|
//
|
|
|
|
PiUlongToInstanceKeyUnicodeString(&instanceName,
|
|
buffer + deviceName.Length / sizeof(WCHAR),
|
|
sizeof(buffer) - deviceName.Length,
|
|
i
|
|
);
|
|
deviceName.Length = (USHORT)(deviceName.Length + instanceName.Length);
|
|
status = IopCreateRegistryKeyEx( &handle,
|
|
handle1,
|
|
&instanceName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&disposition
|
|
);
|
|
ZwClose(handle1);
|
|
if (!NT_SUCCESS(status)) {
|
|
ZwClose(enumHandle);
|
|
goto exit;
|
|
}
|
|
ASSERT(disposition == REG_CREATED_NEW_KEY);
|
|
}
|
|
} else {
|
|
ZwClose(enumHandle);
|
|
goto exit;
|
|
}
|
|
|
|
ASSERT(disposition == REG_CREATED_NEW_KEY);
|
|
newlyCreated = TRUE;
|
|
|
|
//
|
|
// Initialize new device instance registry key
|
|
//
|
|
|
|
if (ResourceAssigned) {
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_NO_RESOURCE_AT_INIT);
|
|
tmpValue = 1;
|
|
ZwSetValueKey(handle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(tmpValue)
|
|
);
|
|
}
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
|
logConfHandle = NULL;
|
|
status = IopCreateRegistryKeyEx( &logConfHandle,
|
|
handle,
|
|
&unicodeName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL
|
|
);
|
|
|
|
ASSERT(status == STATUS_SUCCESS);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Write the ResourceList and and ResourceRequirements to the logconf key under
|
|
// device instance key.
|
|
//
|
|
|
|
if (ResourceList) {
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_BOOTCONFIG);
|
|
ZwSetValueKey(
|
|
logConfHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_RESOURCE_LIST,
|
|
ResourceList,
|
|
listSize = IopDetermineResourceListSize(ResourceList)
|
|
);
|
|
}
|
|
if (ResourceRequirements) {
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_BASIC_CONFIG_VECTOR);
|
|
ZwSetValueKey(
|
|
logConfHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_RESOURCE_REQUIREMENTS_LIST,
|
|
ResourceRequirements,
|
|
ResourceRequirements->ListSize
|
|
);
|
|
}
|
|
//ZwClose(logConfHandle);
|
|
}
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_CONFIG_FLAGS);
|
|
tmpValue = CONFIGFLAG_FINISH_INSTALL;
|
|
ZwSetValueKey(handle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(tmpValue)
|
|
);
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_LEGACY);
|
|
tmpValue = 0;
|
|
ZwSetValueKey(
|
|
handle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
|
|
controlHandle = NULL;
|
|
IopCreateRegistryKeyEx( &controlHandle,
|
|
handle,
|
|
&unicodeName,
|
|
KEY_ALL_ACCESS,
|
|
REG_OPTION_VOLATILE,
|
|
NULL
|
|
);
|
|
|
|
ASSERT(status == STATUS_SUCCESS);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_DEVICE_REPORTED);
|
|
tmpValue = 1;
|
|
status = ZwSetValueKey(controlHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(ULONG)
|
|
);
|
|
status = ZwSetValueKey(handle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
//ZwClose(controlHandle);
|
|
}
|
|
|
|
ZwClose(enumHandle);
|
|
|
|
//
|
|
// Create Service value name and set it to the calling driver's service
|
|
// key name.
|
|
//
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_SERVICE);
|
|
p = (PWSTR)ExAllocatePool(PagedPool, serviceName->Length + sizeof(UNICODE_NULL));
|
|
if (!p) {
|
|
PiUnlockPnpRegistry();
|
|
goto CleanupRegistry;
|
|
}
|
|
RtlCopyMemory(p, serviceName->Buffer, serviceName->Length);
|
|
p[serviceName->Length / sizeof (WCHAR)] = UNICODE_NULL;
|
|
ZwSetValueKey(
|
|
handle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_SZ,
|
|
p,
|
|
serviceName->Length + sizeof(UNICODE_NULL)
|
|
);
|
|
if (DriverObject->Flags & DRVO_BUILTIN_DRIVER) {
|
|
deviceNode->ServiceName = *serviceName;
|
|
} else {
|
|
ExFreePool(p);
|
|
}
|
|
|
|
PiUnlockPnpRegistry();
|
|
//ZwClose(logConfHandle);
|
|
//ZwClose(controlHandle);
|
|
//ZwClose(handle);
|
|
|
|
//
|
|
// Register the device for the driver and save the device
|
|
// instance path in device node.
|
|
//
|
|
|
|
if (!(DriverObject->Flags & DRVO_BUILTIN_DRIVER)) {
|
|
PpDeviceRegistration( &deviceName,
|
|
TRUE,
|
|
&deviceNode->ServiceName
|
|
);
|
|
}
|
|
status = PipConcatenateUnicodeStrings(&deviceNode->InstancePath, &deviceName, NULL);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceNode->Flags = DNF_MADEUP | DNF_ENUMERATED;
|
|
|
|
PipSetDevNodeState(deviceNode, DeviceNodeInitialized, NULL);
|
|
|
|
PpDevNodeInsertIntoTree(IopRootDeviceNode, deviceNode);
|
|
|
|
//
|
|
// Add an entry into the table to set up a mapping between the DO
|
|
// and the instance path.
|
|
//
|
|
|
|
status = IopMapDeviceObjectToDeviceInstance(deviceObject, &deviceNode->InstancePath);
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
//
|
|
// Add a reference to the DeviceObject for ourself
|
|
//
|
|
|
|
ObReferenceObject(deviceObject);
|
|
|
|
IopNotifySetupDeviceArrival(deviceObject, NULL, FALSE);
|
|
|
|
goto checkResource;
|
|
}
|
|
}
|
|
IoDeleteDevice(deviceObject);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
return status;
|
|
checkResource:
|
|
|
|
|
|
//
|
|
// At this point the *DeviceObject is established. Check if we need to report resources for
|
|
// the detected device. If we failed to
|
|
//
|
|
|
|
if (ResourceAssigned) {
|
|
//ASSERT(deviceNode->ResourceList == NULL); // make sure we have not reported resources yet.
|
|
|
|
//
|
|
// If the driver specifies it already has acquired the resource. We will put a flag
|
|
// in the device instance path to not to allocate resources at boot time. The Driver
|
|
// may do detection and report it again.
|
|
//
|
|
|
|
deviceNode->Flags |= DNF_NO_RESOURCE_REQUIRED; // do not need resources for this boot.
|
|
if (ResourceList) {
|
|
|
|
//
|
|
// Write the resource list to the reported device instance key.
|
|
//
|
|
|
|
listSize = IopDetermineResourceListSize(ResourceList);
|
|
IopWriteAllocatedResourcesToRegistry (deviceNode, ResourceList, listSize);
|
|
}
|
|
} else {
|
|
BOOLEAN conflict;
|
|
|
|
if (ResourceList && ResourceList->Count && ResourceList->List[0].PartialResourceList.Count) {
|
|
if (listSize == 0) {
|
|
listSize = IopDetermineResourceListSize(ResourceList);
|
|
}
|
|
cmResource = (PCM_RESOURCE_LIST) ExAllocatePool(PagedPool, listSize);
|
|
if (cmResource) {
|
|
RtlCopyMemory(cmResource, ResourceList, listSize);
|
|
PiWstrToUnicodeString(&unicodeName, PNPMGR_STR_PNP_MANAGER);
|
|
status = IoReportResourceUsageInternal(
|
|
ArbiterRequestLegacyReported,
|
|
&unicodeName, // DriverClassName OPTIONAL,
|
|
deviceObject->DriverObject, // DriverObject,
|
|
NULL, // DriverList OPTIONAL,
|
|
0, // DriverListSize OPTIONAL,
|
|
deviceNode->PhysicalDeviceObject,
|
|
// DeviceObject OPTIONAL,
|
|
cmResource, // DeviceList OPTIONAL,
|
|
listSize, // DeviceListSize OPTIONAL,
|
|
FALSE, // OverrideConflict,
|
|
&conflict // ConflictDetected
|
|
);
|
|
ExFreePool(cmResource);
|
|
if (!NT_SUCCESS(status) || conflict) {
|
|
status = STATUS_CONFLICTING_ADDRESSES;
|
|
PipSetDevNodeProblem(deviceNode, CM_PROB_NORMAL_CONFLICT);
|
|
}
|
|
} else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
PipSetDevNodeProblem(deviceNode, CM_PROB_OUT_OF_MEMORY);
|
|
}
|
|
} else {
|
|
ASSERT(ResourceRequirements == NULL);
|
|
deviceNode->Flags |= DNF_NO_RESOURCE_REQUIRED; // do not need resources for this boot.
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
IopDoDeferredSetInterfaceState(deviceNode);
|
|
|
|
PipSetDevNodeState(deviceNode, DeviceNodeStartPostWork, NULL);
|
|
|
|
*DeviceObject = deviceObject;
|
|
if (newlyCreated) {
|
|
if (controlHandle) {
|
|
ZwClose(controlHandle);
|
|
}
|
|
if (logConfHandle) {
|
|
ZwClose(logConfHandle);
|
|
}
|
|
ZwClose(handle);
|
|
}
|
|
|
|
//
|
|
// Make sure we enumerate and process this device's children.
|
|
//
|
|
|
|
PipRequestDeviceAction(deviceObject, ReenumerateDeviceOnly, FALSE, 0, NULL, NULL);
|
|
|
|
return status;
|
|
|
|
}
|
|
CleanupRegistry:
|
|
IopReleaseDeviceResources(deviceNode, FALSE);
|
|
if (newlyCreated) {
|
|
IoDeleteDevice(deviceObject);
|
|
if (controlHandle) {
|
|
ZwDeleteKey(controlHandle);
|
|
}
|
|
if (logConfHandle) {
|
|
ZwDeleteKey(logConfHandle);
|
|
}
|
|
if (handle) {
|
|
ZwDeleteKey(handle);
|
|
}
|
|
}
|
|
return status;
|
|
exit:
|
|
PiUnlockPnpRegistry();
|
|
IoDeleteDevice(*DeviceObject);
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
IopIsReportedAlready(
|
|
IN HANDLE Handle,
|
|
IN PUNICODE_STRING ServiceName,
|
|
IN PCM_RESOURCE_LIST ResourceList,
|
|
IN PBOOLEAN MatchingKey
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the reported device instance is already reported
|
|
or not.
|
|
|
|
Parameters:
|
|
|
|
Handle - Supplies a handle to the reported device instance key.
|
|
|
|
ServiceName - supplies a pointer to the unicode service key name.
|
|
|
|
ResourceList - supplies a pointer to the reported Resource list.
|
|
|
|
MatchingKey - supplies a pointer to a variable to receive whether the
|
|
ServiceName and ResourceList properties for this key match those
|
|
reported.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
PKEY_VALUE_FULL_INFORMATION keyValueInfo1 = NULL, keyValueInfo2 = NULL;
|
|
NTSTATUS status;
|
|
UNICODE_STRING unicodeName;
|
|
HANDLE logConfHandle, controlHandle = NULL;
|
|
BOOLEAN returnValue = FALSE;
|
|
PCM_RESOURCE_LIST cmResource = NULL;
|
|
ULONG tmpValue;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Assume no match unless we determine otherwise.
|
|
//
|
|
*MatchingKey = FALSE;
|
|
|
|
//
|
|
// Check if "Service" value matches what the caller passed in.
|
|
//
|
|
|
|
status = IopGetRegistryValue(Handle, REGSTR_VALUE_SERVICE, &keyValueInfo1);
|
|
if (NT_SUCCESS(status)) {
|
|
if ((keyValueInfo1->Type == REG_SZ) &&
|
|
(keyValueInfo1->DataLength != 0)) {
|
|
unicodeName.Buffer = (PWSTR)KEY_VALUE_DATA(keyValueInfo1);
|
|
unicodeName.MaximumLength = unicodeName.Length = (USHORT)keyValueInfo1->DataLength;
|
|
if (unicodeName.Buffer[(keyValueInfo1->DataLength / sizeof(WCHAR)) - 1] == UNICODE_NULL) {
|
|
unicodeName.Length -= sizeof(WCHAR);
|
|
}
|
|
if (RtlEqualUnicodeString(ServiceName, &unicodeName, TRUE)) {
|
|
|
|
//
|
|
// Next check if resources are the same
|
|
//
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_LOG_CONF);
|
|
status = IopOpenRegistryKeyEx( &logConfHandle,
|
|
Handle,
|
|
&unicodeName,
|
|
KEY_READ
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
status = IopGetRegistryValue(logConfHandle,
|
|
REGSTR_VAL_BOOTCONFIG,
|
|
&keyValueInfo2);
|
|
ZwClose(logConfHandle);
|
|
if (NT_SUCCESS(status)) {
|
|
if ((keyValueInfo2->Type == REG_RESOURCE_LIST) &&
|
|
(keyValueInfo2->DataLength != 0)) {
|
|
cmResource = (PCM_RESOURCE_LIST)KEY_VALUE_DATA(keyValueInfo2);
|
|
if (ResourceList && cmResource &&
|
|
PipIsDuplicatedDevices(ResourceList, cmResource, NULL, NULL)) {
|
|
*MatchingKey = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!ResourceList && !cmResource) {
|
|
*MatchingKey = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this registry key is for a device reported during the same boot
|
|
// this is not a duplicate.
|
|
//
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_KEY_CONTROL);
|
|
status = IopOpenRegistryKeyEx( &controlHandle,
|
|
Handle,
|
|
&unicodeName,
|
|
KEY_ALL_ACCESS
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
status = IopGetRegistryValue(controlHandle,
|
|
REGSTR_VALUE_DEVICE_REPORTED,
|
|
&keyValueInfo1);
|
|
if (NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
if (*MatchingKey == TRUE) {
|
|
|
|
returnValue = TRUE;
|
|
|
|
//
|
|
// Mark this key has been used.
|
|
//
|
|
|
|
PiWstrToUnicodeString(&unicodeName, REGSTR_VALUE_DEVICE_REPORTED);
|
|
tmpValue = 1;
|
|
status = ZwSetValueKey(controlHandle,
|
|
&unicodeName,
|
|
TITLE_INDEX_VALUE,
|
|
REG_DWORD,
|
|
&tmpValue,
|
|
sizeof(ULONG)
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
returnValue = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (controlHandle) {
|
|
ZwClose(controlHandle);
|
|
}
|
|
|
|
if (keyValueInfo1) {
|
|
ExFreePool(keyValueInfo1);
|
|
}
|
|
if (keyValueInfo2) {
|
|
ExFreePool(keyValueInfo2);
|
|
}
|
|
return returnValue;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
IoInvalidateDeviceState(
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API will cause the PnP manager to send the specified PDO an IRP_MN_QUERY_PNP_DEVICE_STATE
|
|
IRP.
|
|
|
|
Parameters:
|
|
|
|
PhysicalDeviceObject - Provides a pointer to the PDO who's state is to be invalidated.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
ASSERT_PDO(PhysicalDeviceObject);
|
|
|
|
//
|
|
// If the call was made before PnP completes device enumeration
|
|
// we can safely ignore it. PnP manager will do it without
|
|
// driver's request. If the device was already removed or surprised
|
|
// removed then ignore it as well since this is only valid for started
|
|
// devices.
|
|
//
|
|
|
|
deviceNode = (PDEVICE_NODE)PhysicalDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
if (deviceNode->State != DeviceNodeStarted) {
|
|
return;
|
|
}
|
|
|
|
PipRequestDeviceAction( PhysicalDeviceObject,
|
|
RequeryDeviceState,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IopQueueDeviceWorkItem(
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN VOID (*WorkerRoutine)(PVOID),
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API will cause the PnP manager to send the specified PDO an
|
|
IRP_MN_QUERY_PNP_DEVICE_STATE IRP.
|
|
|
|
Parameters:
|
|
|
|
PhysicalDeviceObject - Provides a pointer to the PDO who's state is to be
|
|
invalidated.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_WORK_ITEM deviceWorkItem;
|
|
|
|
//
|
|
// Since this routine can be called at DPC level we need to queue
|
|
// a work item and process it when the irql drops.
|
|
//
|
|
|
|
deviceWorkItem = ExAllocatePool(NonPagedPool, sizeof(DEVICE_WORK_ITEM));
|
|
if (deviceWorkItem == NULL) {
|
|
|
|
//
|
|
// Failed to allocate memory for work item. Nothing we can do ...
|
|
//
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
ObReferenceObject(PhysicalDeviceObject);
|
|
deviceWorkItem->DeviceObject = PhysicalDeviceObject;
|
|
deviceWorkItem->Context = Context;
|
|
|
|
ExInitializeWorkItem( &deviceWorkItem->WorkItem,
|
|
WorkerRoutine,
|
|
deviceWorkItem);
|
|
|
|
//
|
|
// Queue a work item to do the enumeration
|
|
//
|
|
|
|
ExQueueWorkItem( &deviceWorkItem->WorkItem, DelayedWorkQueue );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Private routines
|
|
//
|
|
VOID
|
|
IopResourceRequirementsChanged(
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN BOOLEAN StopRequired
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles request of device resource requirements list change.
|
|
|
|
Parameters:
|
|
|
|
PhysicalDeviceObject - Provides a pointer to the PDO who's state is to be invalidated.
|
|
|
|
StopRequired - Supplies a BOOLEAN value to indicate if the resources reallocation needs
|
|
to be done after device stopped.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
PipRequestDeviceAction( PhysicalDeviceObject,
|
|
ResourceRequirementsChanged,
|
|
FALSE,
|
|
StopRequired,
|
|
NULL,
|
|
NULL );
|
|
}
|
|
|
|
BOOLEAN
|
|
IoIsWdmVersionAvailable(
|
|
IN UCHAR MajorVersion,
|
|
IN UCHAR MinorVersion
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reports whether WDM functionality is available that
|
|
is greater than or equal to the specified major and minor version.
|
|
|
|
Parameters:
|
|
|
|
MajorVersion - Supplies the WDM major version that is required.
|
|
|
|
MinorVersion - Supplies the WDM minor version that is required.
|
|
|
|
Return Value:
|
|
|
|
If WDM support is available at _at least_ the requested level, the
|
|
return value is TRUE, otherwise it is FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
return ((MajorVersion < WDM_MAJORVERSION) ||
|
|
((MajorVersion == WDM_MAJORVERSION) && (MinorVersion <= WDM_MINORVERSION)));
|
|
}
|
|
|
|
NTKERNELAPI
|
|
PDMA_ADAPTER
|
|
IoGetDmaAdapter(
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL,
|
|
IN PDEVICE_DESCRIPTION DeviceDescription,
|
|
IN OUT PULONG NumberOfMapRegisters
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the appropriate DMA adapter object for the device
|
|
defined in the device description structure. This code is a wrapper
|
|
which queries the bus interface standard and then calls the returned
|
|
get DMA adapter function. If an adapter object was not retrieved then
|
|
a legecy function is attempted.
|
|
|
|
Arguments:
|
|
|
|
PhysicalDeviceObject - Optionally, supplies the PDO for the device
|
|
requesting the DMA adapter. If not supplied, this routine performs the
|
|
function of the non-PnP HalGetDmaAdapter routine.
|
|
|
|
DeviceDescriptor - Supplies a description of the deivce.
|
|
|
|
NumberOfMapRegisters - Returns the maximum number of map registers which
|
|
may be allocated by the device driver.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the requested adapter object or NULL if an adapter could not
|
|
be created.
|
|
|
|
--*/
|
|
|
|
{
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
PIO_STACK_LOCATION irpStack;
|
|
BUS_INTERFACE_STANDARD busInterface;
|
|
PDMA_ADAPTER dmaAdapter = NULL;
|
|
PDEVICE_DESCRIPTION deviceDescriptionToUse;
|
|
DEVICE_DESCRIPTION privateDeviceDescription;
|
|
ULONG resultLength;
|
|
PDEVICE_OBJECT targetDevice;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (PhysicalDeviceObject != NULL) {
|
|
|
|
ASSERT_PDO(PhysicalDeviceObject);
|
|
|
|
//
|
|
// First off, determine whether or not the caller has requested that we
|
|
// automatically fill in the proper InterfaceType value into the
|
|
// DEVICE_DESCRIPTION structure used in retrieving the DMA adapter object.
|
|
// If so, then retrieve that interface type value into our own copy of
|
|
// the DEVICE_DESCRIPTION buffer.
|
|
//
|
|
if ((DeviceDescription->InterfaceType == InterfaceTypeUndefined) ||
|
|
(DeviceDescription->InterfaceType == PNPBus)) {
|
|
//
|
|
// Make a copy of the caller-supplied device description, so
|
|
// we can modify it to fill in the correct interface type.
|
|
//
|
|
RtlCopyMemory(&privateDeviceDescription,
|
|
DeviceDescription,
|
|
sizeof(DEVICE_DESCRIPTION)
|
|
);
|
|
|
|
status = IoGetDeviceProperty(PhysicalDeviceObject,
|
|
DevicePropertyLegacyBusType,
|
|
sizeof(privateDeviceDescription.InterfaceType),
|
|
(PVOID)&(privateDeviceDescription.InterfaceType),
|
|
&resultLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
ASSERT(status == STATUS_OBJECT_NAME_NOT_FOUND);
|
|
|
|
//
|
|
// Since the enumerator didn't tell us what interface type to
|
|
// use for this PDO, we'll fall back to our default. This is
|
|
// ISA for machines where the legacy bus is ISA or EISA, and it
|
|
// is MCA for machines whose legacy bus is MicroChannel.
|
|
//
|
|
privateDeviceDescription.InterfaceType = PnpDefaultInterfaceType;
|
|
}
|
|
|
|
//
|
|
// Use our private device description buffer from now on.
|
|
//
|
|
deviceDescriptionToUse = &privateDeviceDescription;
|
|
|
|
} else {
|
|
//
|
|
// Use the caller-supplied device description.
|
|
//
|
|
deviceDescriptionToUse = DeviceDescription;
|
|
}
|
|
|
|
//
|
|
// Now, query for the BUS_INTERFACE_STANDARD interface from the PDO.
|
|
//
|
|
KeInitializeEvent( &event, NotificationEvent, FALSE );
|
|
|
|
targetDevice = IoGetAttachedDeviceReference(PhysicalDeviceObject);
|
|
|
|
irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP,
|
|
targetDevice,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&event,
|
|
&ioStatusBlock );
|
|
|
|
if (irp == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
RtlZeroMemory( &busInterface, sizeof( BUS_INTERFACE_STANDARD ));
|
|
|
|
irpStack = IoGetNextIrpStackLocation( irp );
|
|
irpStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
|
|
irpStack->Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_BUS_INTERFACE_STANDARD;
|
|
irpStack->Parameters.QueryInterface.Size = sizeof( BUS_INTERFACE_STANDARD );
|
|
irpStack->Parameters.QueryInterface.Version = 1;
|
|
irpStack->Parameters.QueryInterface.Interface = (PINTERFACE) &busInterface;
|
|
irpStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
|
|
|
|
//
|
|
// Initialize the status to error in case the ACPI driver decides not to
|
|
// set it correctly.
|
|
//
|
|
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
|
|
status = IoCallDriver(targetDevice, irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
|
|
status = ioStatusBlock.Status;
|
|
|
|
}
|
|
|
|
ObDereferenceObject(targetDevice);
|
|
|
|
if (NT_SUCCESS( status)) {
|
|
|
|
if (busInterface.GetDmaAdapter != NULL) {
|
|
|
|
|
|
dmaAdapter = busInterface.GetDmaAdapter( busInterface.Context,
|
|
deviceDescriptionToUse,
|
|
NumberOfMapRegisters );
|
|
|
|
}
|
|
|
|
//
|
|
// Dereference the interface
|
|
//
|
|
|
|
busInterface.InterfaceDereference( busInterface.Context );
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// The caller didn't specify the PDO, so we'll just use the device
|
|
// description exactly as they specified it (i.e., we can't attempt to
|
|
// make our own determination of what interface type to use).
|
|
//
|
|
deviceDescriptionToUse = DeviceDescription;
|
|
}
|
|
|
|
//
|
|
// If there is no DMA adapter, try the legacy mode code.
|
|
//
|
|
|
|
#if !defined(NO_LEGACY_DRIVERS)
|
|
|
|
if (dmaAdapter == NULL) {
|
|
|
|
dmaAdapter = HalGetDmaAdapter( PhysicalDeviceObject,
|
|
deviceDescriptionToUse,
|
|
NumberOfMapRegisters );
|
|
|
|
}
|
|
|
|
#endif // NO_LEGACY_DRIVERS
|
|
|
|
return( dmaAdapter );
|
|
}
|
|
|
|
NTSTATUS
|
|
IopOpenDeviceParametersSubkey(
|
|
OUT HANDLE *ParamKeyHandle,
|
|
IN HANDLE ParentKeyHandle,
|
|
IN PUNICODE_STRING SubKeyString,
|
|
IN ACCESS_MASK DesiredAccess
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine opens or creates a "Device Parameters" subkey of the specified
|
|
ParentKeyHandle. If this routine creates a new "Device Parameters" subkey,
|
|
it will apply the necessary ACLs to it.
|
|
|
|
Parameters:
|
|
|
|
ParamKeyHandle - Supplies the address of a variable that receives a handle
|
|
to the opened "Device Parameters" subkey. The caller must call ZwClose
|
|
to close the handle returned from this api when access is no longer
|
|
required.
|
|
|
|
ParentKeyHandle - Supplies a handle to the base key off which the path
|
|
specified by the SubStringKey parameter will be opened.
|
|
|
|
SubKeyString - Supplies a path to the subkey that should be opened under the
|
|
ParentKeyHandle, such that the resulting key will serve as the parent of
|
|
the "Device Parameters" subkey.
|
|
|
|
DesiredAccess - Specifies the access mask for the key to be opened.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
ULONG disposition;
|
|
ULONG lengthSD;
|
|
PSECURITY_DESCRIPTOR oldSD = NULL;
|
|
SECURITY_DESCRIPTOR newSD;
|
|
ACL_SIZE_INFORMATION aclSizeInfo;
|
|
PACL oldDacl;
|
|
PACL newDacl = NULL;
|
|
ULONG sizeDacl;
|
|
BOOLEAN daclPresent, daclDefaulted;
|
|
PACCESS_ALLOWED_ACE ace;
|
|
ULONG aceIndex;
|
|
HANDLE deviceKeyHandle;
|
|
UNICODE_STRING deviceParamString;
|
|
|
|
//
|
|
// First try and open the device key
|
|
//
|
|
status = IopOpenRegistryKeyEx( &deviceKeyHandle,
|
|
ParentKeyHandle,
|
|
SubKeyString,
|
|
KEY_WRITE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
PiWstrToUnicodeString(&deviceParamString, REGSTR_KEY_DEVICEPARAMETERS);
|
|
|
|
status = IopCreateRegistryKeyEx( ParamKeyHandle,
|
|
deviceKeyHandle,
|
|
&deviceParamString,
|
|
DesiredAccess | READ_CONTROL | WRITE_DAC,
|
|
REG_OPTION_NON_VOLATILE,
|
|
&disposition
|
|
);
|
|
|
|
ZwClose(deviceKeyHandle);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: IopCreateRegistryKeyEx failed, status = %8.8X\n", status));
|
|
return status;
|
|
}
|
|
|
|
if (disposition == REG_CREATED_NEW_KEY) {
|
|
|
|
//
|
|
// Need to set an ACL on the key if it was created
|
|
//
|
|
//
|
|
// Get the security descriptor from the key so we can add the
|
|
// administrator.
|
|
//
|
|
status = ZwQuerySecurityObject(*ParamKeyHandle,
|
|
DACL_SECURITY_INFORMATION,
|
|
NULL,
|
|
0,
|
|
&lengthSD);
|
|
|
|
if (status == STATUS_BUFFER_TOO_SMALL) {
|
|
oldSD = ExAllocatePool( PagedPool, lengthSD );
|
|
|
|
if (oldSD != NULL) {
|
|
|
|
status = ZwQuerySecurityObject(*ParamKeyHandle,
|
|
DACL_SECURITY_INFORMATION,
|
|
oldSD,
|
|
lengthSD,
|
|
&lengthSD);
|
|
if (!NT_SUCCESS(status)) {
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: ZwQuerySecurityObject failed, status = %8.8X\n", status));
|
|
goto Cleanup0;
|
|
}
|
|
} else {
|
|
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: Failed to allocate memory, status = %8.8X\n", status));
|
|
status = STATUS_NO_MEMORY;
|
|
goto Cleanup0;
|
|
}
|
|
} else {
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: ZwQuerySecurityObject failed %8.8X\n",status));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto Cleanup0;
|
|
}
|
|
|
|
status = RtlCreateSecurityDescriptor( (PSECURITY_DESCRIPTOR) &newSD,
|
|
SECURITY_DESCRIPTOR_REVISION );
|
|
ASSERT( NT_SUCCESS( status ) );
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: RtlCreateSecurityDescriptor failed, status = %8.8X\n", status));
|
|
goto Cleanup0;
|
|
}
|
|
//
|
|
// get the current DACL
|
|
//
|
|
status = RtlGetDaclSecurityDescriptor(oldSD, &daclPresent, &oldDacl, &daclDefaulted);
|
|
|
|
//
|
|
// RtlGetDaclSecurityDescriptor will return either SUCCESS or a SD VERSION error
|
|
// if latter, then something wrong with ZwQuerySecurityObject
|
|
// or RtlGetDaclSecurityDescriptor changed
|
|
//
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint(( IOP_IOAPI_ERROR_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: RtlGetDaclSecurityDescriptor failed, status = %8.8X\n", status));
|
|
goto Cleanup0;
|
|
}
|
|
|
|
//
|
|
// calculate the size of the new DACL
|
|
//
|
|
if (daclPresent) {
|
|
|
|
status = RtlQueryInformationAcl( oldDacl,
|
|
&aclSizeInfo,
|
|
sizeof(ACL_SIZE_INFORMATION),
|
|
AclSizeInformation);
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: RtlQueryInformationAcl failed, status = %8.8X\n", status));
|
|
goto Cleanup0;
|
|
}
|
|
|
|
sizeDacl = aclSizeInfo.AclBytesInUse;
|
|
} else {
|
|
sizeDacl = sizeof(ACL);
|
|
aclSizeInfo.AceCount = 0;
|
|
}
|
|
|
|
sizeDacl += sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(SeAliasAdminsSid) - sizeof(ULONG);
|
|
|
|
//
|
|
// create and initialize the new DACL
|
|
//
|
|
newDacl = ExAllocatePool(PagedPool, sizeDacl);
|
|
|
|
if (newDacl == NULL) {
|
|
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: ExAllocatePool failed\n"));
|
|
goto Cleanup0;
|
|
}
|
|
|
|
status = RtlCreateAcl(newDacl, sizeDacl, ACL_REVISION);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: RtlCreateAcl failed, status = %8.8X\n", status));
|
|
goto Cleanup0;
|
|
}
|
|
|
|
//
|
|
// copy the current (original) DACL into this new one
|
|
//
|
|
if (daclPresent) {
|
|
|
|
for (aceIndex = 0; aceIndex < aclSizeInfo.AceCount; aceIndex++) {
|
|
|
|
status = RtlGetAce(oldDacl, aceIndex, (PVOID *)&ace);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: RtlGetAce failed, status = %8.8X\n", status));
|
|
goto Cleanup0;
|
|
}
|
|
|
|
//
|
|
// We need to skip copying any ACEs which refer to the Administrator
|
|
// to ensure that our full control ACE is the one and only.
|
|
//
|
|
if ((ace->Header.AceType != ACCESS_ALLOWED_ACE_TYPE &&
|
|
ace->Header.AceType != ACCESS_DENIED_ACE_TYPE) ||
|
|
!RtlEqualSid((PSID)&ace->SidStart, SeAliasAdminsSid)) {
|
|
|
|
status = RtlAddAce( newDacl,
|
|
ACL_REVISION,
|
|
~0U,
|
|
ace,
|
|
ace->Header.AceSize
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: RtlAddAce failed, status = %8.8X\n", status));
|
|
goto Cleanup0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// and my new admin-full ace to this new DACL
|
|
//
|
|
status = RtlAddAccessAllowedAceEx( newDacl,
|
|
ACL_REVISION,
|
|
CONTAINER_INHERIT_ACE,
|
|
KEY_ALL_ACCESS,
|
|
SeAliasAdminsSid
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: RtlAddAccessAllowedAceEx failed, status = %8.8X\n", status));
|
|
goto Cleanup0;
|
|
}
|
|
|
|
//
|
|
// Set the new DACL in the absolute security descriptor
|
|
//
|
|
status = RtlSetDaclSecurityDescriptor( (PSECURITY_DESCRIPTOR) &newSD,
|
|
TRUE,
|
|
newDacl,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: RtlSetDaclSecurityDescriptor failed, status = %8.8X\n", status));
|
|
goto Cleanup0;
|
|
}
|
|
|
|
//
|
|
// validate the new security descriptor
|
|
//
|
|
status = RtlValidSecurityDescriptor(&newSD);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: RtlValidSecurityDescriptor failed, status = %8.8X\n", status));
|
|
goto Cleanup0;
|
|
}
|
|
|
|
|
|
status = ZwSetSecurityObject( *ParamKeyHandle,
|
|
DACL_SECURITY_INFORMATION,
|
|
&newSD
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
IopDbgPrint(( IOP_IOAPI_WARNING_LEVEL,
|
|
"IopOpenDeviceParametersSubkey: ZwSetSecurityObject failed, status = %8.8X\n", status));
|
|
goto Cleanup0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we encounter an error updating the DACL we still return success.
|
|
//
|
|
|
|
Cleanup0:
|
|
|
|
if (oldSD != NULL) {
|
|
ExFreePool(oldSD);
|
|
}
|
|
|
|
if (newDacl != NULL) {
|
|
ExFreePool(newDacl);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PpCreateLegacyDeviceIds(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUNICODE_STRING DriverName,
|
|
IN PCM_RESOURCE_LIST Resources
|
|
)
|
|
{
|
|
PIOPNP_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PWCHAR buffer, end;
|
|
|
|
ULONG length = 0;
|
|
|
|
INTERFACE_TYPE interface;
|
|
static const WCHAR* interfaceNames[] ={L"",
|
|
L"Internal",
|
|
L"Isa",
|
|
L"Eisa",
|
|
L"MicroChannel",
|
|
L"TurboChannel",
|
|
L"PCIBus",
|
|
L"VMEBus",
|
|
L"NuBus",
|
|
L"PCMCIABus",
|
|
L"CBus",
|
|
L"MPIBus",
|
|
L"MPSABus",
|
|
L"ProcessorInternal",
|
|
L"InternalPowerBus",
|
|
L"PNPISABus",
|
|
L"PNPBus",
|
|
L"Other",
|
|
L"Root"};
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
if(Resources != NULL) {
|
|
|
|
interface = Resources->List[0].InterfaceType;
|
|
|
|
if((interface > MaximumInterfaceType) ||
|
|
(interface < InterfaceTypeUndefined)) {
|
|
interface = MaximumInterfaceType;
|
|
}
|
|
} else {
|
|
interface = Internal;
|
|
}
|
|
|
|
interface++;
|
|
|
|
//
|
|
// The compatible ID generated will be
|
|
// DETECTED<InterfaceName>\<Driver Name>
|
|
//
|
|
|
|
length = (ULONG)(wcslen(LEGACY_COMPATIBLE_ID_BASE) * sizeof(WCHAR));
|
|
length += (ULONG)(wcslen(interfaceNames[interface]) * sizeof(WCHAR));
|
|
length += sizeof(L'\\');
|
|
length += DriverName->Length;
|
|
length += sizeof(UNICODE_NULL);
|
|
|
|
length += (ULONG)(wcslen(LEGACY_COMPATIBLE_ID_BASE) * sizeof(WCHAR));
|
|
length += sizeof(L'\\');
|
|
length += DriverName->Length;
|
|
length += sizeof(UNICODE_NULL) * 2;
|
|
|
|
buffer = ExAllocatePool(PagedPool, length);
|
|
deviceExtension->CompatibleIdList = buffer;
|
|
|
|
if(buffer == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
deviceExtension->CompatibleIdListSize = length;
|
|
|
|
RtlZeroMemory(buffer, length);
|
|
|
|
StringCchPrintfExW(
|
|
buffer,
|
|
length / sizeof(WCHAR),
|
|
&end,
|
|
NULL,
|
|
0,
|
|
L"%ws%ws\\%wZ",
|
|
LEGACY_COMPATIBLE_ID_BASE,
|
|
interfaceNames[interface],
|
|
DriverName);
|
|
|
|
//
|
|
// Adjust the buffer to point to the end and generate the second
|
|
// compatible id string.
|
|
//
|
|
|
|
length = (ULONG)(end - buffer);
|
|
buffer = end + 1;
|
|
|
|
StringCchPrintfW(buffer, length, L"%ws\\%wZ", LEGACY_COMPATIBLE_ID_BASE, DriverName);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IopAppendLegacyVeto(
|
|
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context,
|
|
IN PUNICODE_STRING VetoName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine appends a veto (driver name or device instance path) to the
|
|
veto list.
|
|
|
|
Parameters:
|
|
|
|
Context - An IO_GET_LEGACY_VETO_LIST_CONTEXT pointer.
|
|
|
|
VetoName - The name of the driver/device to append to the veto list.
|
|
|
|
ReturnValue:
|
|
|
|
A BOOLEAN which indicates whether the append operation was successful.
|
|
|
|
--*/
|
|
{
|
|
ULONG Length;
|
|
PWSTR Buffer;
|
|
|
|
//
|
|
// Compute the length of the (new) veto list. This is the length of
|
|
// the old veto list + the size of the new veto + the size of the
|
|
// terminating '\0'.
|
|
//
|
|
|
|
Length = Context->VetoListLength + VetoName->Length + sizeof (WCHAR);
|
|
|
|
//
|
|
// Allocate the new veto list.
|
|
//
|
|
|
|
Buffer = ExAllocatePool(
|
|
NonPagedPool,
|
|
Length
|
|
);
|
|
|
|
//
|
|
// If we succeeded in allocating the new veto list, copy the old
|
|
// veto list to the new list, append the new veto, and finally,
|
|
// append a terminating '\0'. Otherwise, update the status to
|
|
// indicate an error; IopGetLegacyVetoList will free the veto list
|
|
// before it returns.
|
|
//
|
|
|
|
if (Buffer != NULL) {
|
|
|
|
if (*Context->VetoList != NULL) {
|
|
|
|
RtlCopyMemory(
|
|
Buffer,
|
|
*Context->VetoList,
|
|
Context->VetoListLength
|
|
);
|
|
|
|
ExFreePool(*Context->VetoList);
|
|
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
&Buffer[Context->VetoListLength / sizeof (WCHAR)],
|
|
VetoName->Buffer,
|
|
VetoName->Length
|
|
);
|
|
|
|
Buffer[Length / sizeof (WCHAR) - 1] = L'\0';
|
|
|
|
*Context->VetoList = Buffer;
|
|
Context->VetoListLength = Length;
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
*Context->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
IopGetLegacyVetoListDevice(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether the specified device node should be added to
|
|
the veto list, and if so, calls IopAppendLegacyVeto to add it.
|
|
|
|
Parameters:
|
|
|
|
DeviceNode - The device node to be added.
|
|
|
|
Context - An IO_GET_LEGACY_VETO_LIST_CONTEXT pointer.
|
|
|
|
ReturnValue:
|
|
|
|
A BOOLEAN value which indicates whether the device node enumeration
|
|
process should be terminated or not.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_CAPABILITIES DeviceCapabilities;
|
|
|
|
//
|
|
// A device node should be added added to the veto list, if it has the
|
|
// NonDynamic capability.
|
|
//
|
|
|
|
DeviceCapabilities = IopDeviceNodeFlagsToCapabilities(DeviceNode);
|
|
|
|
if (DeviceCapabilities->NonDynamic) {
|
|
|
|
//
|
|
// Update the veto type. If an error occurrs while adding the device
|
|
// node to the veto list, or the caller did not provide a veto list
|
|
// pointer, terminate the enumeration process now.
|
|
//
|
|
|
|
*Context->VetoType = PNP_VetoLegacyDevice;
|
|
|
|
if (Context->VetoList != NULL) {
|
|
|
|
if (!IopAppendLegacyVeto(Context, &DeviceNode->InstancePath)) {
|
|
return FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
IopGetLegacyVetoListDeviceNode(
|
|
IN PDEVICE_NODE DeviceNode,
|
|
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine recusively walks the device tree, invoking
|
|
IopGetLegacyVetoListDevice to add device nodes to the veto list
|
|
(as appropriate).
|
|
|
|
Parameters:
|
|
|
|
DeviceNode - The device node.
|
|
|
|
Context - An IO_GET_LEGACY_VETO_LIST_CONTEXT pointer.
|
|
|
|
|
|
ReturnValue:
|
|
|
|
A BOOLEAN value which indicates whether the device tree enumeration
|
|
process should be terminated or not.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_NODE Child;
|
|
|
|
//
|
|
// Determine whether the device node should be added to the veto
|
|
// list and add it. If an operation is unsuccessful or we determine
|
|
// the veto type but the caller doesn't need the veto list, then we
|
|
// terminate our search now.
|
|
//
|
|
|
|
if (!IopGetLegacyVetoListDevice(DeviceNode, Context)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Call ourselves recursively to enumerate our children. If while
|
|
// enumerating our children we determine we can terminate the search
|
|
// prematurely, do so.
|
|
//
|
|
|
|
for (Child = DeviceNode->Child;
|
|
Child != NULL;
|
|
Child = Child->Sibling) {
|
|
|
|
if (!IopGetLegacyVetoListDeviceNode(Child, Context)) {
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
IopGetLegacyVetoListDrivers(
|
|
IN PIO_GET_LEGACY_VETO_LIST_CONTEXT Context
|
|
)
|
|
{
|
|
PDRIVER_OBJECT driverObject;
|
|
OBJECT_ATTRIBUTES attributes;
|
|
UNICODE_STRING driverString;
|
|
POBJECT_DIRECTORY_INFORMATION dirInfo;
|
|
HANDLE directoryHandle;
|
|
ULONG dirInfoLength, neededLength, dirContext;
|
|
NTSTATUS status;
|
|
BOOLEAN restartScan;
|
|
|
|
dirInfoLength = 0;
|
|
dirInfo = NULL;
|
|
restartScan = TRUE;
|
|
|
|
//
|
|
// Get handle to \\Driver directory
|
|
//
|
|
|
|
PiWstrToUnicodeString(&driverString, L"\\Driver");
|
|
|
|
InitializeObjectAttributes(&attributes,
|
|
&driverString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = ZwOpenDirectoryObject(&directoryHandle,
|
|
DIRECTORY_QUERY,
|
|
&attributes
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
*Context->Status = status;
|
|
return;
|
|
}
|
|
|
|
for (;;) {
|
|
|
|
//
|
|
// Get info on next object in directory. If the buffer is too
|
|
// small, reallocate it and try again. Otherwise, any failure
|
|
// including STATUS_NO_MORE_ENTRIES breaks us out.
|
|
//
|
|
|
|
status = ZwQueryDirectoryObject(directoryHandle,
|
|
dirInfo,
|
|
dirInfoLength,
|
|
TRUE, // force one at a time
|
|
restartScan,
|
|
&dirContext,
|
|
&neededLength);
|
|
if (status == STATUS_BUFFER_TOO_SMALL) {
|
|
dirInfoLength = neededLength;
|
|
if (dirInfo != NULL) {
|
|
ExFreePool(dirInfo);
|
|
}
|
|
dirInfo = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, dirInfoLength);
|
|
if (dirInfo == NULL) {
|
|
*Context->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
status = ZwQueryDirectoryObject(directoryHandle,
|
|
dirInfo,
|
|
dirInfoLength,
|
|
TRUE, // force one at a time
|
|
restartScan,
|
|
&dirContext,
|
|
&neededLength);
|
|
}
|
|
restartScan = FALSE;
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Have name of object. Create object path and use
|
|
// ObReferenceObjectByName() to get DriverObject. This may
|
|
// fail non-fatally if DriverObject has gone away in the interim.
|
|
//
|
|
|
|
driverString.MaximumLength = sizeof(L"\\Driver\\") +
|
|
dirInfo->Name.Length;
|
|
driverString.Length = driverString.MaximumLength - sizeof(WCHAR);
|
|
driverString.Buffer = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION,
|
|
driverString.MaximumLength);
|
|
if (driverString.Buffer == NULL) {
|
|
*Context->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
StringCbPrintfW(driverString.Buffer, driverString.MaximumLength, L"\\Driver\\%ws", dirInfo->Name.Buffer);
|
|
status = ObReferenceObjectByName(&driverString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL, // access state
|
|
0, // access mask
|
|
IoDriverObjectType,
|
|
KernelMode,
|
|
NULL, // parse context
|
|
&driverObject);
|
|
|
|
ExFreePool(driverString.Buffer);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
ASSERT(driverObject->Type == IO_TYPE_DRIVER);
|
|
if (driverObject->Flags & DRVO_LEGACY_RESOURCES) {
|
|
//
|
|
// Update the veto type. If the caller provided a
|
|
// veto list pointer, add the driver to the veto list.
|
|
// If an error occurs while adding the driver to the
|
|
// veto list, or the caller did not provide a veto
|
|
// list pointer, terminate the driver enumeration now.
|
|
//
|
|
// NOTE: Driver may be loaded but not running,
|
|
// distinction is not made here.
|
|
|
|
|
|
*Context->VetoType = PNP_VetoLegacyDriver;
|
|
|
|
if (Context->VetoList != NULL) {
|
|
IopAppendLegacyVeto(Context, &dirInfo->Name);
|
|
}
|
|
}
|
|
ObDereferenceObject(driverObject);
|
|
|
|
//
|
|
// Early out if we have a veto and the caller didn't want a list or
|
|
// we hit some error already
|
|
//
|
|
if (((*Context->VetoType == PNP_VetoLegacyDriver) &&
|
|
(Context->VetoList == NULL)) ||
|
|
!NT_SUCCESS(*Context->Status)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (dirInfo != NULL) {
|
|
ExFreePool(dirInfo);
|
|
}
|
|
|
|
ZwClose(directoryHandle);
|
|
}
|
|
|
|
NTSTATUS
|
|
IoGetLegacyVetoList(
|
|
OUT PWSTR *VetoList OPTIONAL,
|
|
OUT PPNP_VETO_TYPE VetoType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used by PNP and PO to determine whether legacy drivers and
|
|
devices are installed in the system. This routine is conceptually a
|
|
QUERY_REMOVE_DEVICE and QUERY_POWER-like interface for legacy drivers
|
|
and devices.
|
|
|
|
Parameters:
|
|
|
|
VetoList - A pointer to a PWSTR. (Optional) If specified,
|
|
IoGetLegacyVetoList will allocate a veto list, and return a
|
|
pointer to the veto list in VetoList.
|
|
|
|
VetoType - A pointer to a PNP_VETO_TYPE. If no legacy drivers
|
|
or devices are found in the system, VetoType is assigned
|
|
PNP_VetoTypeUnknown. If one or more legacy drivers are installed,
|
|
VetoType is assigned PNP_VetoLegacyDriver. If one or more
|
|
legacy devices are installed, VetoType is assigned
|
|
PNP_VetoLegacyDevice. VetoType is assigned independent of
|
|
whether a VetoList is created.
|
|
|
|
ReturnValue:
|
|
|
|
An NTSTATUS value indicating whether the IoGetLegacyVetoList() operation
|
|
was successful.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
IO_GET_LEGACY_VETO_LIST_CONTEXT Context;
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize the veto list.
|
|
//
|
|
|
|
if (VetoList != NULL) {
|
|
*VetoList = NULL;
|
|
}
|
|
|
|
//
|
|
// Initialize the veto type.
|
|
//
|
|
|
|
ASSERT(VetoType != NULL);
|
|
|
|
*VetoType = PNP_VetoTypeUnknown;
|
|
|
|
//
|
|
// Initialize the status.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if (PnPInitialized == FALSE) {
|
|
|
|
//
|
|
// Can't touch anything, but nothing is really started either.
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Initialize our local context.
|
|
//
|
|
|
|
Context.VetoList = VetoList;
|
|
Context.VetoListLength = 0;
|
|
Context.VetoType = VetoType;
|
|
Context.Status = &Status;
|
|
|
|
//
|
|
// Enumerate all driver objects. This process can: (1) modify
|
|
// the veto list, (2) modify the veto type and/or (3) modify the
|
|
// status.
|
|
//
|
|
|
|
IopGetLegacyVetoListDrivers(&Context);
|
|
|
|
//
|
|
// If the driver enumeration process was successful and no legacy
|
|
// drivers were detected, enumerate all device nodes. The same
|
|
// context values as above may be modified during device enumeration.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if (*VetoType == PNP_VetoTypeUnknown) {
|
|
|
|
PpDevNodeLockTree(PPL_SIMPLE_READ);
|
|
|
|
IopGetLegacyVetoListDeviceNode(
|
|
IopRootDeviceNode,
|
|
&Context
|
|
);
|
|
|
|
PpDevNodeUnlockTree(PPL_SIMPLE_READ);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If the previous operation(s) was/were successful, and the caller
|
|
// provided a veto list pointer and we have constructed a veto
|
|
// list, terminate the veto list with an empty string, i.e. MULTI_SZ.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
if (*VetoType != PNP_VetoTypeUnknown) {
|
|
|
|
if (VetoList != NULL) {
|
|
|
|
PiWstrToUnicodeString(
|
|
&UnicodeString,
|
|
L""
|
|
);
|
|
|
|
IopAppendLegacyVeto(
|
|
&Context,
|
|
&UnicodeString
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If a previous operation was unsuccessful, free any veto list we may have
|
|
// allocated along the way.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
if (VetoList != NULL && *VetoList != NULL) {
|
|
ExFreePool(*VetoList);
|
|
*VetoList = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PnpCompletionRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PKEVENT Event
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to stop further processing on an Irp which has been
|
|
passed to IoForwardAndCatchIrp. It signals a event which has been passed
|
|
in the context parameter to indicate that the Irp processing is complete.
|
|
It then returns STATUS_MORE_PROCESSING_REQUIRED in order to stop processing
|
|
on this Irp.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject -
|
|
Contains the device which set up this completion routine.
|
|
|
|
Irp -
|
|
Contains the Irp which is being stopped.
|
|
|
|
Event -
|
|
Contains the event which is used to signal that this Irp has been
|
|
completed.
|
|
|
|
Return Value:
|
|
|
|
Returns STATUS_MORE_PROCESSING_REQUIRED in order to stop processing on the
|
|
Irp.
|
|
|
|
--*/
|
|
{
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
UNREFERENCED_PARAMETER( Irp );
|
|
|
|
//
|
|
// This will allow the ForwardAndCatchIrp call to continue on its way.
|
|
//
|
|
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
|
|
//
|
|
// This will ensure that nothing else touches the Irp, since the original
|
|
// caller has now continued, and the Irp may not exist anymore.
|
|
//
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NTKERNELAPI
|
|
BOOLEAN
|
|
IoForwardAndCatchIrp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used with devices which may be stacked, and may not use
|
|
file objects to communicate.
|
|
|
|
Forwards an IRP to the specified driver after initializing the next
|
|
stack location, and regains control of the Irp on completion from that
|
|
driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject -
|
|
Contains the device to forward the Irp to.
|
|
|
|
Irp -
|
|
Contains the Irp which is being forwarded to the specified driver.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if the IRP was forwarded, else FALSE if no stack space
|
|
was available.
|
|
|
|
--*/
|
|
{
|
|
KEVENT Event;
|
|
|
|
PAGED_CODE();
|
|
//
|
|
// Ensure that there is another stack location before copying parameters.
|
|
//
|
|
ASSERT(Irp->CurrentLocation > 1);
|
|
if (Irp->CurrentLocation == 1) {
|
|
return FALSE;
|
|
}
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
//
|
|
// Set up a completion routine so that the Irp is not actually
|
|
// completed. Thus the caller can get control of the Irp back after
|
|
// this next driver is done with it.
|
|
//
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
IoSetCompletionRoutine(Irp, PnpCompletionRoutine, &Event, TRUE, TRUE, TRUE);
|
|
if (IoCallDriver(DeviceObject, Irp) == STATUS_PENDING) {
|
|
//
|
|
// Wait for completion which will occur when the CompletionRoutine
|
|
// signals this event. Wait in KernelMode so that the current stack
|
|
// is not paged out, since there is an event object on this stack.
|
|
//
|
|
KeWaitForSingleObject(
|
|
&Event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
IoGetDeviceInstanceName(
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
OUT PUNICODE_STRING InstanceName
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_NODE deviceNode;
|
|
|
|
ASSERT_PDO(PhysicalDeviceObject);
|
|
|
|
deviceNode = PhysicalDeviceObject->DeviceObjectExtension->DeviceNode;
|
|
|
|
status = PipConcatenateUnicodeStrings( InstanceName,
|
|
&deviceNode->InstancePath,
|
|
NULL);
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
IoControlPnpDeviceActionQueue(
|
|
BOOLEAN Lock
|
|
)
|
|
{
|
|
if (Lock) {
|
|
|
|
PiLockDeviceActionQueue();
|
|
} else {
|
|
|
|
PiUnlockDeviceActionQueue();
|
|
}
|
|
}
|