mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1307 lines
41 KiB
1307 lines
41 KiB
/*++
|
|
|
|
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;
|
|
}
|
|
|