1308 lines
41 KiB
C
1308 lines
41 KiB
C
/*++
|
||
|
||
Copyright (c) 1995 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
control.c
|
||
|
||
Abstract:
|
||
|
||
User-mode -> Kernel-mode PnP Manager control routines.
|
||
|
||
Author:
|
||
|
||
Lonny McMichael (lonnym) 02/14/95
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
|
||
//
|
||
// Global driver object that is used by calls to NtPlugPlayControl
|
||
// with control type of PlugPlayControlDetectResourceConflict.
|
||
//
|
||
PDRIVER_OBJECT driverObject = NULL;
|
||
|
||
|
||
#ifdef _PNP_POWER_
|
||
|
||
//
|
||
// Define the context structure for the PiDevicePathToServiceInstance
|
||
// callback routine.
|
||
//
|
||
typedef struct _PI_DEVPATH_TO_SVCINST_CONTEXT {
|
||
NTSTATUS ReturnStatus;
|
||
PUNICODE_STRING DevicePath;
|
||
UNICODE_STRING DeviceInstanceMatch;
|
||
} PI_DEVPATH_TO_SVCINST_CONTEXT, *PPI_DEVPATH_TO_SVCINST_CONTEXT;
|
||
|
||
//
|
||
// Prototype utility functions internal to this file.
|
||
//
|
||
BOOLEAN
|
||
PiDevicePathToServiceInstance(
|
||
IN HANDLE DeviceInstanceHandle,
|
||
IN PUNICODE_STRING DeviceInstancePath,
|
||
IN OUT PVOID Context
|
||
);
|
||
|
||
#endif // _PNP_POWER_
|
||
|
||
NTSTATUS
|
||
PiGenerateLegacyDeviceInstance(
|
||
IN PUNICODE_STRING ServiceKeyName,
|
||
OUT PWSTR DeviceInstance,
|
||
IN ULONG DeviceInstanceLength,
|
||
OUT PULONG RequiredLength
|
||
);
|
||
|
||
NTSTATUS
|
||
PiDetectResourceConflict(
|
||
IN PCM_RESOURCE_LIST ResourceList,
|
||
IN ULONG ResourceListSize
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, NtPlugPlayControl)
|
||
#pragma alloc_text(PAGE, PiGenerateLegacyDeviceInstance)
|
||
#pragma alloc_text(PAGE, PiDetectResourceConflict)
|
||
|
||
#ifdef _PNP_POWER_
|
||
#pragma alloc_text(PAGE, PiQueryRemoveDevice)
|
||
#pragma alloc_text(PAGE, PiRemoveDevice)
|
||
#pragma alloc_text(PAGE, PiCancelRemoveDevice)
|
||
#pragma alloc_text(PAGE, PiAddDevice)
|
||
#pragma alloc_text(PAGE, PiEjectDevice)
|
||
#pragma alloc_text(PAGE, PiUnlockDevice)
|
||
#pragma alloc_text(PAGE, PiQueryDeviceCapabilities)
|
||
#pragma alloc_text(PAGE, PiGetDevicePathInformation)
|
||
#endif // _PNP_POWER_
|
||
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
NTSTATUS
|
||
NtPlugPlayControl(
|
||
IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
|
||
IN OUT PVOID PnPControlData,
|
||
IN ULONG PnPControlDataLength,
|
||
OUT PULONG RequiredLength OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This Plug and Play Manager API provides a mechanism for the user-mode
|
||
PnP Manager to control the activity of its kernel-mode counterpart.
|
||
|
||
Arguments:
|
||
|
||
PnPControlClass - Specifies what action to perform.
|
||
|
||
PnPControlData - Supplies a pointer to data specific to this action.
|
||
|
||
PnPControlDataLength - Specifies the size, in bytes, of the buffer pointed
|
||
to by PnPControlData
|
||
|
||
RequiredLength - Optional pointer to a variable the receives the actual
|
||
size required to store the output data in
|
||
PnPControlData.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating success or failure. Set of possible return
|
||
values includes the following:
|
||
|
||
STATUS_SUCCESS - normal, successful completion.
|
||
|
||
STATUS_INVALID_PARAMETER_1 - The PnPControlClass parameter did not
|
||
specify a valid control class.
|
||
|
||
STATUS_INVALID_PARAMETER_MIX - The value of the PnPControlDataLength
|
||
parameter did not match the length required for the control
|
||
class requested by the PnPControlClass parameter.
|
||
|
||
STATUS_BUFFER_TOO_SMALL - The size of the supplied output buffer is not
|
||
large enough to hold the output generated by this control class.
|
||
|
||
STATUS_ACCESS_VIOLATION - One of the following pointers specified
|
||
an invalid address: (1) the PnPControlData buffer pointer,
|
||
(2) some pointer contained in the PnPControlData buffer, or
|
||
(3) the RequiredLength pointer
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources exist
|
||
for this request to complete.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceControlData;
|
||
PPLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA LegacyDevGenData;
|
||
PPLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA DeviceResourceData;
|
||
#ifdef _PNP_POWER_
|
||
PPLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA DeviceCapabilitiesData;
|
||
PPLUGPLAY_CONTROL_DEVICE_PATH_DATA DevicePathData;
|
||
ULONG ReturnBufferSize;
|
||
#endif
|
||
|
||
try {
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
PreviousMode = KeGetPreviousMode();
|
||
if(PreviousMode != KernelMode) {
|
||
|
||
ProbeForWrite(PnPControlData, PnPControlDataLength, sizeof(ULONG));
|
||
|
||
if(ARGUMENT_PRESENT(RequiredLength)) {
|
||
ProbeForWriteUlong(RequiredLength);
|
||
}
|
||
}
|
||
|
||
switch(PnPControlClass) {
|
||
|
||
case PlugPlayControlQueryRemoveDevice:
|
||
case PlugPlayControlRemoveDevice:
|
||
case PlugPlayControlCancelRemoveDevice:
|
||
case PlugPlayControlAddDevice:
|
||
case PlugPlayControlEjectDevice:
|
||
case PlugPlayControlUnlockDevice:
|
||
case PlugPlayControlEnumerateDevice:
|
||
#ifndef _PNP_POWER_
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#endif
|
||
|
||
case PlugPlayControlRegisterNewDevice:
|
||
case PlugPlayControlDeregisterDevice:
|
||
//
|
||
// Validate buffer for all control classes using a
|
||
// PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA structure.
|
||
//
|
||
if(PnPControlDataLength != sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)) {
|
||
return STATUS_INVALID_PARAMETER_MIX;
|
||
}
|
||
|
||
DeviceControlData = (PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)PnPControlData;
|
||
|
||
if(PreviousMode != KernelMode) {
|
||
ProbeForRead(DeviceControlData->DeviceInstance.Buffer,
|
||
DeviceControlData->DeviceInstance.Length,
|
||
sizeof(WCHAR)
|
||
);
|
||
}
|
||
|
||
//
|
||
// Since this structure is a fixed size, store RequiredLength now, if necessary,
|
||
// so we don't have to do it for each class separately.
|
||
//
|
||
if(ARGUMENT_PRESENT(RequiredLength)) {
|
||
*RequiredLength = sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA);
|
||
}
|
||
|
||
break;
|
||
|
||
case PlugPlayControlQueryDeviceCapabilities:
|
||
#ifndef _PNP_POWER_
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
//
|
||
// Validate buffer for all control classes using a
|
||
// PLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA structure.
|
||
//
|
||
if(PnPControlDataLength != sizeof(PLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA)) {
|
||
return STATUS_INVALID_PARAMETER_MIX;
|
||
}
|
||
|
||
DeviceCapabilitiesData =
|
||
(PPLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA)PnPControlData;
|
||
|
||
if(PreviousMode != KernelMode) {
|
||
ProbeForRead(DeviceCapabilitiesData->DeviceInstance.Buffer,
|
||
DeviceCapabilitiesData->DeviceInstance.Length,
|
||
sizeof(WCHAR)
|
||
);
|
||
}
|
||
#endif
|
||
break;
|
||
|
||
case PlugPlayControlGetDevicePathInformation:
|
||
#ifndef _PNP_POWER_
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
//
|
||
// Validate buffer for all control classes using a
|
||
// PLUGPLAY_CONTROL_DEVICE_PATH_DATA structure.
|
||
//
|
||
if(PnPControlDataLength < sizeof(PLUGPLAY_CONTROL_DEVICE_PATH_DATA)) {
|
||
return STATUS_INVALID_PARAMETER_MIX;
|
||
}
|
||
|
||
DevicePathData = (PPLUGPLAY_CONTROL_DEVICE_PATH_DATA)PnPControlData;
|
||
|
||
if(PreviousMode != KernelMode) {
|
||
ProbeForRead(DevicePathData->DevicePath.Buffer,
|
||
DevicePathData->DevicePath.Length,
|
||
sizeof(WCHAR)
|
||
);
|
||
}
|
||
#endif
|
||
break;
|
||
|
||
case PlugPlayControlGenerateLegacyDevice:
|
||
//
|
||
// Validate buffer for all control classes using a
|
||
// PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA structure.
|
||
//
|
||
if(PnPControlDataLength < sizeof(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA)) {
|
||
return STATUS_INVALID_PARAMETER_MIX;
|
||
}
|
||
|
||
LegacyDevGenData = (PPLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA)PnPControlData;
|
||
|
||
if(PreviousMode != KernelMode) {
|
||
ProbeForRead(LegacyDevGenData->ServiceName.Buffer,
|
||
LegacyDevGenData->ServiceName.Length,
|
||
sizeof(WCHAR)
|
||
);
|
||
}
|
||
break;
|
||
|
||
case PlugPlayControlDetectResourceConflict:
|
||
//
|
||
// Determine whether resource list specified in buffer
|
||
// is avaiable (not conflicting with other devices).
|
||
//
|
||
if(PnPControlDataLength != sizeof(PLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA)) {
|
||
return STATUS_INVALID_PARAMETER_MIX;
|
||
}
|
||
|
||
DeviceResourceData = (PPLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA)PnPControlData;
|
||
|
||
if(PreviousMode != KernelMode) {
|
||
ProbeForRead(DeviceResourceData->DeviceInstance.Buffer,
|
||
DeviceResourceData->DeviceInstance.Length,
|
||
sizeof(WCHAR)
|
||
);
|
||
|
||
ProbeForRead(DeviceResourceData->ResourceList,
|
||
DeviceResourceData->ResourceListSize,
|
||
sizeof(UCHAR)
|
||
);
|
||
}
|
||
|
||
//
|
||
// Since this structure is a fixed size, store RequiredLength now, if necessary,
|
||
// so we don't have to do it for each class separately.
|
||
//
|
||
if(ARGUMENT_PRESENT(RequiredLength)) {
|
||
*RequiredLength = sizeof(PLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
//
|
||
// Invalid control class.
|
||
//
|
||
return STATUS_INVALID_PARAMETER_1;
|
||
}
|
||
|
||
//
|
||
// Now invoke the proper routine for this control class.
|
||
//
|
||
switch(PnPControlClass) {
|
||
|
||
case PlugPlayControlQueryRemoveDevice:
|
||
#ifndef _PNP_POWER_
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
Status = PiQueryRemoveDevice(&(DeviceControlData->DeviceInstance),
|
||
&(DeviceControlData->Status)
|
||
);
|
||
#endif
|
||
break;
|
||
|
||
case PlugPlayControlRemoveDevice:
|
||
#ifndef _PNP_POWER_
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
Status = PiRemoveDevice(&(DeviceControlData->DeviceInstance),
|
||
&(DeviceControlData->Status)
|
||
);
|
||
#endif
|
||
break;
|
||
|
||
case PlugPlayControlCancelRemoveDevice:
|
||
#ifndef _PNP_POWER_
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
Status = PiCancelRemoveDevice(&(DeviceControlData->DeviceInstance),
|
||
&(DeviceControlData->Status)
|
||
);
|
||
#endif
|
||
break;
|
||
|
||
case PlugPlayControlAddDevice:
|
||
#ifndef _PNP_POWER_
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
Status = PiAddDevice(&(DeviceControlData->DeviceInstance),
|
||
&(DeviceControlData->Status)
|
||
);
|
||
#endif
|
||
break;
|
||
|
||
case PlugPlayControlEjectDevice:
|
||
#ifndef _PNP_POWER_
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
Status = PiEjectDevice(&(DeviceControlData->DeviceInstance),
|
||
&(DeviceControlData->Status)
|
||
);
|
||
#endif
|
||
break;
|
||
|
||
case PlugPlayControlUnlockDevice:
|
||
#ifndef _PNP_POWER_
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
Status = PiUnlockDevice(&(DeviceControlData->DeviceInstance),
|
||
&(DeviceControlData->Status)
|
||
);
|
||
#endif
|
||
break;
|
||
|
||
case PlugPlayControlQueryDeviceCapabilities:
|
||
#ifndef _PNP_POWER_
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
//
|
||
// BUGBUG (lonnym): need to add argument for slot capabilities once it
|
||
// is defined by KenR.
|
||
//
|
||
Status = PiQueryDeviceCapabilities(&(DeviceCapabilitiesData->DeviceInstance)
|
||
);
|
||
|
||
if(ARGUMENT_PRESENT(RequiredLength)) {
|
||
*RequiredLength = sizeof(PLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA);
|
||
}
|
||
#endif
|
||
break;
|
||
|
||
case PlugPlayControlGetDevicePathInformation:
|
||
#ifndef _PNP_POWER_
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
Status = PiGetDevicePathInformation(
|
||
&(DevicePathData->DevicePath),
|
||
DevicePathData->ServiceName,
|
||
PnPControlDataLength -
|
||
FIELD_OFFSET(PLUGPLAY_CONTROL_DEVICE_PATH_DATA, ServiceName),
|
||
&ReturnBufferSize,
|
||
&(DevicePathData->ServiceNameLength),
|
||
&(DevicePathData->DeviceInstanceOffset),
|
||
&(DevicePathData->DeviceInstanceLength),
|
||
&(DevicePathData->ServiceInstanceOrdinal)
|
||
);
|
||
|
||
if(ARGUMENT_PRESENT(RequiredLength)) {
|
||
|
||
if(NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL) {
|
||
|
||
*RequiredLength = ReturnBufferSize +
|
||
FIELD_OFFSET(PLUGPLAY_CONTROL_DEVICE_PATH_DATA, ServiceName);
|
||
}
|
||
}
|
||
#endif
|
||
break;
|
||
|
||
case PlugPlayControlRegisterNewDevice:
|
||
Status = PpDeviceRegistration(&DeviceControlData->DeviceInstance, TRUE);
|
||
DeviceControlData->Status = Status;
|
||
break;
|
||
|
||
case PlugPlayControlDeregisterDevice:
|
||
Status = PpDeviceRegistration(&DeviceControlData->DeviceInstance, FALSE);
|
||
DeviceControlData->Status = Status;
|
||
break;
|
||
|
||
case PlugPlayControlEnumerateDevice:
|
||
//
|
||
// BUGBUG (lonnym): This is where a call to PiEnumerateDevice() should go.
|
||
// This API was previously the NT API, NtEnumerateBus.
|
||
//
|
||
Status = STATUS_NOT_IMPLEMENTED;
|
||
break;
|
||
|
||
case PlugPlayControlGenerateLegacyDevice:
|
||
Status = PiGenerateLegacyDeviceInstance(
|
||
&LegacyDevGenData->ServiceName,
|
||
LegacyDevGenData->DeviceInstance,
|
||
PnPControlDataLength -
|
||
FIELD_OFFSET(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA, DeviceInstance),
|
||
&LegacyDevGenData->DeviceInstanceLength
|
||
);
|
||
|
||
if(ARGUMENT_PRESENT(RequiredLength)) {
|
||
if(NT_SUCCESS(Status) || (Status == STATUS_BUFFER_TOO_SMALL)) {
|
||
*RequiredLength = LegacyDevGenData->DeviceInstanceLength +
|
||
sizeof(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case PlugPlayControlDetectResourceConflict:
|
||
Status = PiDetectResourceConflict(DeviceResourceData->ResourceList,
|
||
DeviceResourceData->ResourceListSize);
|
||
DeviceResourceData->Status = Status;
|
||
break;
|
||
|
||
}
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiGenerateLegacyDeviceInstance(
|
||
IN PUNICODE_STRING ServiceKeyName,
|
||
OUT PWSTR DeviceInstance,
|
||
IN ULONG DeviceInstanceLength,
|
||
OUT PULONG RequiredLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine creates a new instance node under System\Enum\Root\LEGACY_<Name>
|
||
key and all the required default value entries. Also a value entry under
|
||
Service\ServiceKeyName\Enum is created to point to the newly created madeup
|
||
entry. A handle and the keyname of the new key are returned to caller.
|
||
Caller must free the unicode string when he is done with it.
|
||
|
||
Arguments:
|
||
|
||
ServiceKeyName - Supplies a pointer to the name of the subkey in the
|
||
system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
|
||
that caused the driver to load.
|
||
|
||
DeviceInstance - Supplies a pointer to the character buffer that receives the
|
||
newly-generated device instance name.
|
||
|
||
DeviceInstanceLength - Supplies the size, in bytes, of the DeviceInstance
|
||
buffer.
|
||
|
||
RequiredLength - Supplies a pointer to a variable that receives the size,
|
||
in bytes (excluding terminating NULL) of the device instance name stored
|
||
in the buffer.
|
||
|
||
Return Value:
|
||
|
||
A NTSTATUS code.
|
||
If the Lengacy Device Instance exists already, this function returns sucessful.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
HANDLE handle;
|
||
ULONG junk;
|
||
BOOLEAN isPlugPlayDriver = FALSE;
|
||
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
|
||
UNICODE_STRING tempUnicodeString;
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
|
||
|
||
status = IopOpenServiceEnumKeys(ServiceKeyName,
|
||
KEY_READ,
|
||
&handle,
|
||
NULL,
|
||
TRUE
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Check whether this madeup key should be created. It must be a legacy driver
|
||
// to create the madeup key.
|
||
//
|
||
|
||
status = IopGetRegistryValue(handle, REGSTR_VALUE_PLUGPLAY_SERVICE_TYPE, &keyValueInformation);
|
||
ZwClose(handle);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
if ((keyValueInformation->Type == REG_DWORD) &&
|
||
(keyValueInformation->DataLength >= sizeof(ULONG))) {
|
||
isPlugPlayDriver = TRUE;
|
||
status = STATUS_INVALID_PARAMETER_2;
|
||
}
|
||
ExFreePool(keyValueInformation);
|
||
}
|
||
if (!isPlugPlayDriver) {
|
||
status = IopCreateMadeupNode(ServiceKeyName,
|
||
&handle,
|
||
&tempUnicodeString,
|
||
&junk,
|
||
TRUE
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// We have successfully retrieved the newly-generated device instance name.
|
||
// Now store it in the supplied buffer.
|
||
//
|
||
|
||
ZwClose(handle);
|
||
*RequiredLength = tempUnicodeString.Length;
|
||
|
||
if(DeviceInstanceLength >= tempUnicodeString.Length + sizeof(UNICODE_NULL)) {
|
||
try {
|
||
RtlMoveMemory(DeviceInstance,
|
||
tempUnicodeString.Buffer,
|
||
tempUnicodeString.Length + sizeof(UNICODE_NULL)
|
||
);
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
status = GetExceptionCode();
|
||
}
|
||
} else {
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
RtlFreeUnicodeString(&tempUnicodeString);
|
||
}
|
||
}
|
||
}
|
||
|
||
ExReleaseResource(&PpRegistryDeviceResource);
|
||
KeLeaveCriticalRegion();
|
||
|
||
return status;
|
||
}
|
||
#ifdef _PNP_POWER_
|
||
|
||
NTSTATUS
|
||
PiQueryRemoveDevice(
|
||
IN PUNICODE_STRING DeviceInstance,
|
||
OUT PNTSTATUS ReturnedStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine queries a device driver for whether a particular device
|
||
instance can be removed.
|
||
|
||
Arguments:
|
||
|
||
DeviceInstance - Supplies the path in the registry (relative to
|
||
HKLM\System\Enum) to the device instance being query-removed.
|
||
|
||
ReturnedStatus - If the function is successful, this receives the
|
||
NT status code returned by the driver in response to the
|
||
query-remove.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating success or failure of this routine. If
|
||
STATUS_SUCCESS, then ReturnedStatus must be checked to determine
|
||
the driver's response to the request.
|
||
|
||
--*/
|
||
|
||
{
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiRemoveDevice(
|
||
IN PUNICODE_STRING DeviceInstance,
|
||
OUT PNTSTATUS ReturnedStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine causes a device driver to remove a particular device
|
||
instance from the system.
|
||
|
||
Arguments:
|
||
|
||
DeviceInstance - Supplies the path in the registry (relative to
|
||
HKLM\System\Enum) to the device instance to be removed.
|
||
|
||
ReturnedStatus - If the function is successful, this receives the
|
||
NT status code returned by the driver in response to the
|
||
remove request.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating success or failure of this routine. If
|
||
STATUS_SUCCESS, then ReturnedStatus must be checked to determine
|
||
the driver's response to the request.
|
||
|
||
--*/
|
||
|
||
{
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiCancelRemoveDevice(
|
||
IN PUNICODE_STRING DeviceInstance,
|
||
OUT PNTSTATUS ReturnedStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine cancels a previously-submitted request to query-remove
|
||
a device. It should only be called if the device instance was
|
||
previously successfully query-removed.
|
||
|
||
Arguments:
|
||
|
||
DeviceInstance - Supplies the path in the registry (relative to
|
||
HKLM\System\Enum) to the device instance for which a remove
|
||
request is to be cancelled.
|
||
|
||
ReturnedStatus - If the function is successful, this receives the
|
||
NT status code returned by the driver in response to the
|
||
remove cancel request.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating success or failure of this routine. If
|
||
STATUS_SUCCESS, then ReturnedStatus must be checked to determine
|
||
the driver's response to the request.
|
||
|
||
--*/
|
||
|
||
{
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiAddDevice(
|
||
IN PUNICODE_STRING DeviceInstance,
|
||
OUT PNTSTATUS ReturnedStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine causes a device driver to add a new device instance
|
||
(i.e., create a new device object).
|
||
|
||
Arguments:
|
||
|
||
DeviceInstance - Supplies the path in the registry (relative to
|
||
HKLM\System\Enum) to the new device instance to be added.
|
||
|
||
ReturnedStatus - If the function is successful, this receives the
|
||
NT status code returned by the driver in response to the
|
||
add device request.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating success or failure of this routine. If
|
||
STATUS_SUCCESS, then ReturnedStatus must be checked to determine
|
||
the driver's response to the request.
|
||
|
||
--*/
|
||
|
||
{
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiEjectDevice(
|
||
IN PUNICODE_STRING DeviceInstance,
|
||
OUT PNTSTATUS ReturnedStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine causes the specified device instance to be ejected (if
|
||
the device is in a slot that supports soft-eject).
|
||
|
||
Arguments:
|
||
|
||
DeviceInstance - Supplies the path in the registry (relative to
|
||
HKLM\System\Enum) to the device instance to be ejected.
|
||
|
||
ReturnedStatus - If the function is successful, this receives the
|
||
NT status code returned by the HAL bus extender that controls
|
||
the bus where this device is located.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating success or failure of this routine. If
|
||
STATUS_SUCCESS, then ReturnedStatus must be checked to determine
|
||
the HAL bus extender's response to the request.
|
||
|
||
--*/
|
||
|
||
{
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiUnlockDevice(
|
||
IN PUNICODE_STRING DeviceInstance,
|
||
OUT PNTSTATUS ReturnedStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine causes the specified device instance to be unlocked
|
||
for removal (if the device is in a slot that supports slot locking).
|
||
|
||
Arguments:
|
||
|
||
DeviceInstance - Supplies the path in the registry (relative to
|
||
HKLM\System\Enum) to the device instance to be unlocked.
|
||
|
||
ReturnedStatus - If the function is successful, this receives the
|
||
NT status code returned by the HAL bus extender that controls
|
||
the bus where this device is located.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating success or failure of this routine. If
|
||
STATUS_SUCCESS, then ReturnedStatus must be checked to determine
|
||
the HAL bus extender's response to the request.
|
||
|
||
--*/
|
||
|
||
{
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
//
|
||
// BUGBUG (lonnym): un-comment the 2nd parameter to the following
|
||
// routine once KenR defines the SLOT_CAPABILITIES structure.
|
||
//
|
||
NTSTATUS
|
||
PiQueryDeviceCapabilities(
|
||
IN PUNICODE_STRING DeviceInstance
|
||
// ,OUT PSLOT_CAPABILITIES Capabilities
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the capabilities of a particular device (e.g.,
|
||
is the device soft-ejectable?).
|
||
|
||
(Note that while these capabilities are really an attribute of the
|
||
particular bus slot on which the device resides, the user-mode PnP
|
||
manager does not track bus slots--only devices. Therefore, from
|
||
user-mode, these attributes are associated with a device instance,
|
||
and the kernel-mode PnP manager makes the association between device
|
||
instances and actual bus slots.)
|
||
|
||
Arguments:
|
||
|
||
DeviceInstance - Supplies the path in the registry (relative to
|
||
HKLM\System\Enum) to the device instance whose capabilities
|
||
are to be determined.
|
||
|
||
Capabilities - Pointer to a buffer that receives the capabilities of
|
||
the slot in which this device instance is located.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating success or failure of this routine.
|
||
|
||
--*/
|
||
|
||
{
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
NTSTATUS
|
||
PiGetDevicePathInformation(
|
||
IN PUNICODE_STRING DevicePath,
|
||
OUT PWCHAR ServiceName,
|
||
IN ULONG BufferLength,
|
||
OUT PULONG ReturnLength,
|
||
OUT PULONG ServiceNameLength,
|
||
OUT PULONG DeviceInstanceOffset,
|
||
OUT PULONG DeviceInstanceLength,
|
||
OUT PULONG ServiceInstanceOrdinal OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes as input an NT device path, and returns the
|
||
corresponding service name and (if possible) device instance it
|
||
is associated with. It also optionally returns the device instance's
|
||
service ordinal as listed under the service entry's volatile Enum subkey.
|
||
|
||
Arguments:
|
||
|
||
DevicePath - Supplies the NT device path
|
||
|
||
ServiceName - Pointer to a character buffer that receives both
|
||
the ServiceName _and_ DeviceInstance strings. The
|
||
ServiceName will be stored at the beginning of the buffer,
|
||
and the DeviceInstance string (if applicable) will be stored
|
||
at offset DeviceInstanceOffset in the buffer. Both strings will
|
||
be NULL-terminated.
|
||
|
||
BufferLength - Supplies the length, in bytes, of the buffer pointed
|
||
to by ServiceName.
|
||
|
||
ReturnLength - Receives the size, in bytes, required to store
|
||
both strings in the ServiceName buffer. If the buffer isn't
|
||
large enough, then no data will be stored in it, and this value
|
||
will indicate the size necessary to store the data.
|
||
|
||
ServiceNameLength - Receives the length, in bytes, of the ServiceName
|
||
string stored in the ServiceName buffer (not including terminating
|
||
NULL).
|
||
|
||
DeviceInstanceOffset - If applicable, this value will receive the offset
|
||
from the beginning of the ServiceName buffer (in characters) where the
|
||
DeviceInstance string is located. If no device instance is associated
|
||
with this device path (e.g., the device path was created by a legacy
|
||
driver), then this value will be set to zero.
|
||
|
||
DeviceInstanceLength - Receives the length, in bytes, of the DeviceInstance
|
||
string stored in the ServiceName buffer (not including terminating
|
||
NULL). If there is no associated device instance, then this value will
|
||
be set to zero.
|
||
|
||
ServiceInstanceOrdinal - If specified, receives the ordinal of the
|
||
device instance within the service's volatile Enum list. If there is no
|
||
associated device instance (i.e., DeviceInstanceOffset and
|
||
DeviceInstanceLength are zero), then this value is set to PLUGPLAY_NO_INSTANCE.
|
||
|
||
Return Value:
|
||
|
||
NT status code indicating success or failure of this routine.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PFILE_OBJECT FileObject;
|
||
PUNICODE_STRING DriverServiceName;
|
||
HANDLE ServiceEnumHandle;
|
||
PI_DEVPATH_TO_SVCINST_CONTEXT DevPathToSvcInstContext;
|
||
ULONG ReturnedServiceOrdinal, DevInstStringLength;
|
||
|
||
//
|
||
// Get a pointer to a file object for the specified device so that we can
|
||
// retrieve the controlling service name from its driver object.
|
||
//
|
||
Status = PiGetDeviceObjectFilePointer(DevicePath,
|
||
&FileObject
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
return Status;
|
||
}
|
||
|
||
DriverServiceName = &(FileObject->DeviceObject->DriverObject->DriverExtension->ServiceKeyName);
|
||
|
||
//
|
||
// The driver object may not have an associated service name, so only search
|
||
// for a service instance if it does.
|
||
//
|
||
if(DriverServiceName->Length) {
|
||
//
|
||
// Now search through each device instance listed under the service
|
||
// entry's volatile Enum subkey, looking for a match in one of the
|
||
// NtDevicePaths REG_MULTI_SZ value entries.
|
||
//
|
||
DevPathToSvcInstContext.ReturnStatus = STATUS_SUCCESS;
|
||
DevPathToSvcInstContext.DevicePath = DevicePath;
|
||
RtlInitUnicodeString(&(DevPathToSvcInstContext.DeviceInstanceMatch), NULL);
|
||
|
||
//
|
||
// We need to acquire the PnP device registry resource for shared (read) access.
|
||
//
|
||
KeEnterCriticalRegion();
|
||
ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
|
||
|
||
Status = IopApplyFunctionToServiceInstances(NULL,
|
||
DriverServiceName,
|
||
KEY_READ,
|
||
TRUE,
|
||
PiDevicePathToServiceInstance,
|
||
&DevPathToSvcInstContext,
|
||
&ReturnedServiceOrdinal
|
||
);
|
||
|
||
ExReleaseResource(&PpRegistryDeviceResource);
|
||
KeLeaveCriticalRegion();
|
||
|
||
if(NT_SUCCESS(Status) &&
|
||
NT_SUCCESS(Status = DevPathToSvcInstContext.ReturnStatus)) {
|
||
|
||
DevInstStringLength = DevPathToSvcInstContext.DeviceInstanceMatch.Length;
|
||
} else {
|
||
goto PrepareForReturn1;
|
||
}
|
||
|
||
} else {
|
||
DevInstStringLength = 0;
|
||
}
|
||
|
||
//
|
||
// Now we know the size of the string buffer required, so check to make sure
|
||
// the buffer we were given is large enough, and if so, fill it with the
|
||
// string(s).
|
||
//
|
||
*ReturnLength = DriverServiceName->Length + DevInstStringLength
|
||
+ ((DevInstStringLength) ? 2 : 1) * sizeof(WCHAR);
|
||
|
||
if(BufferLength < *ReturnLength) {
|
||
Status = STATUS_BUFFER_TOO_SMALL;
|
||
goto PrepareForReturn2;
|
||
}
|
||
|
||
*ServiceNameLength = DriverServiceName->Length;
|
||
|
||
if(*ServiceNameLength) {
|
||
RtlMoveMemory(ServiceName,
|
||
DriverServiceName->Buffer,
|
||
*ServiceNameLength
|
||
);
|
||
}
|
||
|
||
ServiceName[CB_TO_CWC(*ServiceNameLength)] = UNICODE_NULL;
|
||
|
||
if(*DeviceInstanceLength = DevInstStringLength) {
|
||
RtlMoveMemory(&(ServiceName[*DeviceInstanceOffset = CB_TO_CWC(*ServiceNameLength) + 1]),
|
||
DevPathToSvcInstContext.DeviceInstanceMatch.Buffer,
|
||
*DeviceInstanceLength
|
||
);
|
||
ServiceName[*DeviceInstanceOffset + CB_TO_CWC(*DeviceInstanceLength)] = UNICODE_NULL;
|
||
} else {
|
||
*DeviceInstanceOffset = 0;
|
||
}
|
||
|
||
if(ARGUMENT_PRESENT(ServiceInstanceOrdinal)) {
|
||
*ServiceInstanceOrdinal = DevInstStringLength ? ReturnedServiceOrdinal
|
||
: PLUGPLAY_NO_INSTANCE;
|
||
}
|
||
|
||
PrepareForReturn2:
|
||
|
||
if(DevInstStringLength) {
|
||
ExFreePool(DevPathToSvcInstContext.DeviceInstanceMatch.Buffer);
|
||
}
|
||
|
||
PrepareForReturn1:
|
||
|
||
ObDereferenceObject(FileObject);
|
||
return Status;
|
||
}
|
||
|
||
BOOLEAN
|
||
PiDevicePathToServiceInstance(
|
||
IN HANDLE DeviceInstanceHandle,
|
||
IN PUNICODE_STRING DeviceInstancePath,
|
||
IN OUT PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is a callback function for IopApplyFunctionToServiceInstances.
|
||
It is called for each device instance key referenced by a service instance
|
||
value under the specified service's volatile Enum subkey. Its purpose is to
|
||
determine whether this device instance corresponds to a specified NT device
|
||
path (as registered in the device instance's NtDevicePaths REG_MULTI_SZ list).
|
||
|
||
NOTE: The PnP device-specific registry resource must be acquired for shared
|
||
(read) access before invoking this routine.
|
||
|
||
Arguments:
|
||
|
||
DeviceInstanceHandle - Supplies a handle to the current device instance key.
|
||
The access to this key is that specified in the call to
|
||
IopApplyFunctionToServiceInstances.
|
||
|
||
DeviceInstancePath - Supplies the registry path (relative to HKLM\System\Enum)
|
||
to this device instance.
|
||
|
||
Context - Supplies a pointer to a PI_DEVPATH_TO_SVCINST_CONTEXT structure with
|
||
the following fields:
|
||
|
||
NTSTATUS ReturnStatus - Fill this in with the NT error status code if an
|
||
error occurs. This is assumed to be initialized to STATUS_SUCCESS
|
||
when this routine is called.
|
||
|
||
PUNICODE_STRING DevicePath - Supplies the NT device path that we're looking
|
||
for.
|
||
|
||
UNICODE_STRING DeviceInstanceMatch - If the current device instance corresponds
|
||
to the specified NT device path, then fill this unicode string in with
|
||
the device instance path. The caller is responsible for freeing the
|
||
(PagedPool) memory allocated for the unicode string buffer.
|
||
|
||
Return Value:
|
||
|
||
TRUE to continue the enumeration.
|
||
FALSE to abort it. If the current device instance corresponds to the NT device
|
||
path being searched for, this function should return FALSE to terminate the
|
||
search.
|
||
|
||
--*/
|
||
|
||
{
|
||
PPI_DEVPATH_TO_SVCINST_CONTEXT DevPathToSvcInstContext;
|
||
NTSTATUS Status;
|
||
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
|
||
PUNICODE_STRING DevicePathList;
|
||
ULONG DevicePathCount, i;
|
||
|
||
DevPathToSvcInstContext = (PPI_DEVPATH_TO_SVCINST_CONTEXT)Context;
|
||
|
||
//
|
||
// Retrieve the NtDevicePath REG_MULTI_SZ list from the device instance key.
|
||
//
|
||
Status = IopGetRegistryValue(DeviceInstanceHandle,
|
||
REGSTR_VALUE_NTDEVICEPATHS,
|
||
&KeyValueInformation
|
||
);
|
||
if(!NT_SUCCESS(Status)) {
|
||
//
|
||
// Ignore this device instance and continue search
|
||
//
|
||
return TRUE;
|
||
}
|
||
|
||
Status = IopRegMultiSzToUnicodeStrings(KeyValueInformation,
|
||
&DevicePathList,
|
||
&DevicePathCount
|
||
);
|
||
ExFreePool(KeyValueInformation);
|
||
|
||
if(!NT_SUCCESS(Status)) {
|
||
//
|
||
// An error here is most likely STATUS_INSUFFICIENT_RESOURCES, which is
|
||
// severe enough to abort the search.
|
||
//
|
||
DevPathToSvcInstContext->ReturnStatus = Status;
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Now, search the device path list, looking for a match.
|
||
//
|
||
for(i = 0; i < DevicePathCount; i++) {
|
||
|
||
if(RtlEqualUnicodeString(&(DevicePathList[i]),
|
||
DevPathToSvcInstContext->DevicePath,
|
||
TRUE)) {
|
||
//
|
||
// We found a match, so store a copy of the device instance path string
|
||
// in the DeviceInstanceMatch field of the context structure.
|
||
//
|
||
if(!IopConcatenateUnicodeStrings(&(DevPathToSvcInstContext->DeviceInstanceMatch),
|
||
DeviceInstancePath,
|
||
NULL
|
||
)) {
|
||
|
||
DevPathToSvcInstContext->ReturnStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
IopFreeUnicodeStringList(DevicePathList, DevicePathCount);
|
||
|
||
return (i == DevicePathCount) ? TRUE : FALSE;
|
||
}
|
||
#endif // _PNP_POWER_
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PiDetectResourceConflict(
|
||
IN PCM_RESOURCE_LIST ResourceList,
|
||
IN ULONG ResourceListSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to test whether the specified resource
|
||
list conflicts with any already assigned resources.
|
||
|
||
Arguments:
|
||
|
||
ResourceList - Specifies a resource list buffer.
|
||
|
||
ResourceListSize - Specifies the size of the resource list buffer.
|
||
|
||
Return Value:
|
||
|
||
The function value is an NTSTATUS value; STATUS_SUCCESS indicates
|
||
that the resources do not conflict, STATUS_INSUFFICIENT_RESOURCES
|
||
indicates that the resource conflict with already assigned
|
||
resources (or some other NTSTATUS value may indicate a different
|
||
internal error).
|
||
|
||
--*/
|
||
|
||
{
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
HANDLE handle;
|
||
PWSTR buffer;
|
||
NTSTATUS status;
|
||
UNICODE_STRING DriverName;
|
||
ULONG i;
|
||
BOOLEAN bConflictDetected = FALSE, bTemp;
|
||
CM_RESOURCE_LIST EmptyResourceList;
|
||
|
||
|
||
if (driverObject == NULL) {
|
||
//
|
||
// Driver object has not been created yet, do that now.
|
||
//
|
||
RtlInitUnicodeString(&DriverName, L"\\Device\\PlugPlay");
|
||
|
||
//
|
||
// Begin by creating the permanent driver object.
|
||
//
|
||
InitializeObjectAttributes(&objectAttributes,
|
||
&DriverName,
|
||
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
|
||
(HANDLE)NULL,
|
||
(PSECURITY_DESCRIPTOR)NULL);
|
||
|
||
//
|
||
// Specify "KernelMode" here since it refers to the source of
|
||
// the objectAttributes buffer, not the previous operating system
|
||
// mode.
|
||
//
|
||
status = ObCreateObject(KernelMode,
|
||
IoDriverObjectType,
|
||
&objectAttributes,
|
||
KernelMode,
|
||
(PVOID)NULL,
|
||
(ULONG)(sizeof(DRIVER_OBJECT) + sizeof(DRIVER_EXTENSION)),
|
||
0,
|
||
0,
|
||
(PVOID)&driverObject);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Initialize the driver object.
|
||
//
|
||
RtlZeroMemory(driverObject,
|
||
sizeof(DRIVER_OBJECT) + sizeof(DRIVER_EXTENSION));
|
||
driverObject->DriverExtension = (PDRIVER_EXTENSION)(driverObject + 1);
|
||
driverObject->DriverExtension->DriverObject = driverObject;
|
||
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
|
||
driverObject->MajorFunction[i] = NULL; // OK???
|
||
}
|
||
driverObject->Type = IO_TYPE_DRIVER;
|
||
driverObject->Size = sizeof(DRIVER_OBJECT);
|
||
driverObject->DriverInit = NULL;
|
||
|
||
//
|
||
// Insert the driver object into the object table.
|
||
//
|
||
status = ObInsertObject(driverObject,
|
||
NULL,
|
||
FILE_READ_DATA,
|
||
0,
|
||
(PVOID *)NULL,
|
||
&handle);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
ObMakeTemporaryObject(driverObject); //?
|
||
ObDereferenceObject(driverObject); //?
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Save the name of the driver so that it can be easily located by functions
|
||
// such as error logging.
|
||
//
|
||
buffer = ExAllocatePool(PagedPool, DriverName.MaximumLength + 2);
|
||
|
||
if (buffer) {
|
||
driverObject->DriverName.Buffer = buffer;
|
||
driverObject->DriverName.MaximumLength = DriverName.MaximumLength;
|
||
driverObject->DriverName.Length = DriverName.Length;
|
||
|
||
RtlCopyMemory(driverObject->DriverName.Buffer,
|
||
DriverName.Buffer,
|
||
DriverName.MaximumLength);
|
||
buffer[DriverName.Length >> 1] = (WCHAR) '\0';
|
||
}
|
||
}
|
||
|
||
//
|
||
// Attempt to acquire the resource, if successful, we know the
|
||
// resource is avaiable, overwise assume it conflicts with another
|
||
// devices resource's.
|
||
//
|
||
status = IoReportResourceUsage(NULL,
|
||
driverObject,
|
||
ResourceList,
|
||
ResourceListSize,
|
||
NULL,
|
||
NULL,
|
||
0,
|
||
TRUE,
|
||
&bConflictDetected);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
//
|
||
// Clear any resources that might have been assigned to my fake device.
|
||
//
|
||
RtlZeroMemory(&EmptyResourceList, sizeof(CM_RESOURCE_LIST));
|
||
|
||
IoReportResourceUsage(NULL,
|
||
driverObject,
|
||
&EmptyResourceList,
|
||
sizeof(CM_RESOURCE_LIST),
|
||
NULL,
|
||
NULL,
|
||
0,
|
||
TRUE,
|
||
&bTemp);
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS(status) && bConflictDetected) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|