windows-nt/Source/XPSP1/NT/base/wmi/cdmprov/driver/wmisamp.c
2020-09-26 16:20:57 +08:00

1240 lines
35 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
wmisamp.c
Abstract:
Sample device driver whose purpose is to show how to interface with
the CDM provider and implement online and offline diagnostics
Environment:
WDM, NT and Windows 98
Revision History:
--*/
#include <WDM.H>
#include "filter.h"
#include <wmistr.h>
#include <wmiguid.h>
#define OffsetToPtr(Base, Offset) ((PUCHAR)((PUCHAR)(Base) + (Offset)))
//
// These structures need to be defined by hand since they have varaible
// length elements and thus cannot be generated automatically by the
// mof checking tools
//
typedef struct
{
BOOLEAN IsInUse;
ULONG ResourcesUsedCount;
ULONG CharacteristicsCount;
#define OtherCharacteristicNoReboot 0
#define OtherCharacteristicReboot 1
#define OfflineDiagnostic 0
ULONG OtherCharacteristic;
#define CharacteristicUnknown 0
#define CharacteristicOther 1
#define CharacteristicIsExclusive 2
#define CharacteristicIsInteractive 3
#define CharacteristicIsDestructive 4
#define CharacteristicIsRisky 5
#define CharacteristicIsPackage 6
#define CharacteristicSupportsPercent 7
// uint32 Characteristics[];
#define ResourceUsedCPU 0
#define ResourceUsedMemory 1
#define ResourceUsedHardDisk 2
#define ResourceUsedCDROM 3
#define ResourceUsedFloppy 4
#define ResourceUsedPCIBus 5
#define ResourceUsedUSBBus 6
#define ResourceUsed1394Bus 7
#define ResourceUsedSCSIBus 8
#define ResourceUsedIDEBus 9
#define ResourceUsedNetwork 10
#define ResourceUsedISABus 11
#define ResourceUsedEISABus 12
#define ResourceUsedVESABus 13
#define ResourceUsedPCMCIABus 14
#define ResourceUsedCardBus 15
#define ResourceUsedAccessBus 16
#define ResourceUsedNuBus 17
#define ResourceUsedAGP 18
#define ResourceUsedVMEBus 19
#define ResourceUsedSbusIEEE1396_1993 20
#define ResourceUsedMCABus 21
#define ResourceUsedGIOBus 22
#define ResourceUsedXIOBus 23
#define ResourceUsedHIOBus 24
#define ResourceUsedPMCBus 25
#define ResourceUsedSIOBus 26
// uint16 ResourcesUsed[];
UCHAR VariableData[1];
} DIAGNOSTIC_TEST, *PDIAGNOSTIC_TEST;
typedef struct
{
ULONG Result;
BOOLEAN TestingStopped;
} DISCONTINUE_TEST_OUT, *PDISCONTINUE_TEST_OUT;
enum RunDiscontinueTestResults
{
// 0 = OK (function succeeded, but the test itself may have failed
RunDiscontinueTestOk = 0,
// 1 = Unspecified Error (function failed for unspecified reasons)
RunDiscontinueTestUnspecifiedError = 1,
// 2 = Not Implemented (function is not implemented for this instance
RunDiscontinueTestNotImplemented = 2,
// 3 = Out Of Resources (component could not allocate required
// resources, e.g. memory, disk space, etc.)
RunDiscontinueTestOutOfResources = 3
};
NTSTATUS
FilterFunctionControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN ULONG GuidIndex,
IN WMIENABLEDISABLECONTROL Function,
IN BOOLEAN Enable
);
NTSTATUS
FilterExecuteWmiMethod(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN ULONG GuidIndex,
IN ULONG InstanceIndex,
IN ULONG MethodId,
IN ULONG InBufferSize,
IN ULONG OutBufferSize,
IN PUCHAR Buffer
);
NTSTATUS
FilterQueryWmiDataBlock(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN ULONG GuidIndex,
IN ULONG InstanceIndex,
IN ULONG InstanceCount,
IN OUT PULONG InstanceLengthArray,
IN ULONG BufferAvail,
OUT PUCHAR Buffer
);
NTSTATUS
FilterQueryWmiRegInfo(
IN PDEVICE_OBJECT DeviceObject,
OUT ULONG *RegFlags,
OUT PUNICODE_STRING InstanceName,
OUT PUNICODE_STRING *RegistryPath,
OUT PUNICODE_STRING MofResourceName,
OUT PDEVICE_OBJECT *Pdo
);
NTSTATUS FilterZwDeleteValueKey(
HANDLE KeyHandle,
PUNICODE_STRING ValueName
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,FilterQueryWmiRegInfo)
#pragma alloc_text(PAGE,FilterQueryWmiDataBlock)
#pragma alloc_text(PAGE,FilterExecuteWmiMethod)
#pragma alloc_text(PAGE,FilterFunctionControl)
#pragma alloc_text(PAGE,FilterPerformOfflineDiags)
#pragma alloc_text(PAGE,FilterZwDeleteValueKey)
#endif
//
// Create data structures for identifying the guids and reporting them to
// WMI. Since the WMILIB callbacks pass an index into the guid list we make
// definitions for the various guids indicies.
//
#define FilterDiagnosticClass 0
#define FilterOfflineDiagnosticClass 1
#define FilterDiagnosticSettingListClass 2
#define FilterOfflineResultsClass 3
GUID FilterDiagnosticClassGuid = MSSample_DiagnosticTestGuid;
GUID FilterOfflineDiagnosticClassGuid = MSSample_OfflineDiagnosticTestGuid;
GUID FilterDiagnosticSettingListGuid = MSSample_DiagnosticSettingListGuid;
GUID FilterOfflineResultsGuid = MSSample_OfflineResultGuid;
WMIGUIDREGINFO FilterGuidList[] =
{
{
&FilterDiagnosticClassGuid, // Guid
1, // # of instances in each device
WMIREG_FLAG_EXPENSIVE // Flag as expensive to collect
},
{
&FilterOfflineDiagnosticClassGuid, // Guid
1, // # of instances in each device
0 // Flag as expensive to collect
},
{
&FilterDiagnosticSettingListGuid, // Guid
1, // # of instances in each device
0 // Flag as not expensive to collect
},
{
&FilterOfflineResultsGuid, // Guid
1, // # of instances in each device
0 // Flag as not expensive to collect
}
};
#define FilterGuidCount (sizeof(FilterGuidList) / sizeof(WMIGUIDREGINFO))
//
// We need to hang onto the registry path passed to our driver entry so that
// we can return it in the QueryWmiRegInfo callback.
//
UNICODE_STRING FilterRegistryPath;
NTSTATUS VA_SystemControl(
struct DEVICE_EXTENSION *devExt,
PIRP irp,
PBOOLEAN passIrpDown
)
/*++
Routine Description:
Dispatch routine for System Control IRPs (MajorFunction == IRP_MJ_SYSTEM_CONTROL)
Arguments:
devExt - device extension for targetted device object
irp - Io Request Packet
*passIrpDown - returns with whether to pass irp down stack
Return Value:
NT status code
--*/
{
PWMILIB_CONTEXT wmilibContext;
NTSTATUS status;
SYSCTL_IRP_DISPOSITION disposition;
wmilibContext = &devExt->WmiLib;
//
// Call Wmilib helper function to crack the irp. If this is a wmi irp
// that is targetted for this device then WmiSystemControl will callback
// at the appropriate callback routine.
//
status = WmiSystemControl(wmilibContext,
devExt->filterDevObj,
irp,
&disposition);
switch(disposition)
{
case IrpProcessed:
{
//
// This irp has been processed and may be completed or pending.
*passIrpDown = FALSE;
break;
}
case IrpNotCompleted:
{
//
// This irp has not been completed, but has been fully processed.
// we will complete it now.
*passIrpDown = FALSE;
IoCompleteRequest(irp, IO_NO_INCREMENT);
break;
}
case IrpForward:
case IrpNotWmi:
{
//
// This irp is either not a WMI irp or is a WMI irp targetted
// at a device lower in the stack.
*passIrpDown = TRUE;
break;
}
default:
{
//
// We really should never get here, but if we do just forward....
ASSERT(FALSE);
*passIrpDown = TRUE;
break;
}
}
return(status);
}
NTSTATUS
FilterInitializeWmiDataBlocks(
IN struct DEVICE_EXTENSION *devExt
)
/*++
Routine Description:
This routine is called to create a new instance of the device
Arguments:
devExt is device extension
Return Value:
--*/
{
PWMILIB_CONTEXT wmilibInfo;
//
// Fill in the WMILIB_CONTEXT structure with a pointer to the
// callback routines and a pointer to the list of guids
// supported by the driver
//
wmilibInfo = &devExt->WmiLib;
wmilibInfo->GuidCount = FilterGuidCount;
wmilibInfo->GuidList = FilterGuidList;
wmilibInfo->QueryWmiRegInfo = FilterQueryWmiRegInfo;
wmilibInfo->QueryWmiDataBlock = FilterQueryWmiDataBlock;
wmilibInfo->SetWmiDataBlock = NULL;
wmilibInfo->SetWmiDataItem = NULL;
wmilibInfo->ExecuteWmiMethod = FilterExecuteWmiMethod;
wmilibInfo->WmiFunctionControl = FilterFunctionControl;
return(STATUS_SUCCESS);
}
NTSTATUS
FilterQueryWmiRegInfo(
IN PDEVICE_OBJECT DeviceObject,
OUT ULONG *RegFlags,
OUT PUNICODE_STRING InstanceName,
OUT PUNICODE_STRING *RegistryPath,
OUT PUNICODE_STRING MofResourceName,
OUT PDEVICE_OBJECT *Pdo
)
/*++
Routine Description:
This routine is a callback into the driver to retrieve the list of
guids or data blocks that the driver wants to register with WMI. This
routine may not pend or block. Driver should NOT call
WmiCompleteRequest.
Arguments:
DeviceObject is the device whose registration info is being queried
*RegFlags returns with a set of flags that describe the guids being
registered for this device. If the device wants enable and disable
collection callbacks before receiving queries for the registered
guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the
returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case
the instance name is determined from the PDO associated with the
device object. Note that the PDO must have an associated devnode. If
WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique
name for the device.
InstanceName returns with the instance name for the guids if
WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The
caller will call ExFreePool with the buffer returned.
*RegistryPath returns with the registry path of the driver. The caller
does NOT free this buffer.
*MofResourceName returns with the name of the MOF resource attached to
the binary file. If the driver does not have a mof resource attached
then this can be returned as NULL. The caller does NOT free this
buffer.
*Pdo returns with the device object for the PDO associated with this
device if the WMIREG_FLAG_INSTANCE_PDO flag is retured in
*RegFlags.
Return Value:
status
--*/
{
struct DEVICE_EXTENSION * devExt = DeviceObject->DeviceExtension;
//
// Return the registry path for this driver. This is required so WMI
// can find your driver image and can attribute any eventlog messages to
// your driver.
*RegistryPath = &FilterRegistryPath;
//
// Return the name specified in the .rc file of the resource which
// contains the bianry mof data. By default WMI will look for this
// resource in the driver image (.sys) file, however if the value
// MofImagePath is specified in the driver's registry key
// then WMI will look for the resource in the file specified there.
RtlInitUnicodeString(MofResourceName, L"MofResourceName");
//
// Specify that the driver wants WMI to automatically generate instance
// names for all of the data blocks based upon the device stack's
// device instance id. Doing this is STRONGLY recommended since additional
// information about the device would then be available to callers.
*RegFlags = WMIREG_FLAG_INSTANCE_PDO;
*Pdo = devExt->physicalDevObj;
return(STATUS_SUCCESS);
}
NTSTATUS
FilterQueryWmiDataBlock(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN ULONG GuidIndex,
IN ULONG InstanceIndex,
IN ULONG InstanceCount,
IN OUT PULONG InstanceLengthArray,
IN ULONG BufferAvail,
OUT PUCHAR Buffer
)
/*++
Routine Description:
This routine is a callback into the driver to query for the contents of
all instances of a data block. If the driver can satisfy the query within
the callback it should call WmiCompleteRequest to complete the irp before
returning to the caller. Or the driver can return STATUS_PENDING if the
irp cannot be completed immediately and must then call WmiCompleteRequest
once the query is satisfied.
Arguments:
DeviceObject is the device whose data block is being queried
Irp is the Irp that makes this request
GuidIndex is the index into the list of guids provided when the
device registered
InstanceCount is the number of instnaces expected to be returned for
the data block.
InstanceLengthArray is a pointer to an array of ULONG that returns the
lengths of each instance of the data block. If this is NULL then
there was not enough space in the output buffer to fufill the request
so the irp should be completed with the buffer needed.
BufferAvail on entry has the maximum size available to write the data
blocks.
Buffer on return is filled with the returned data blocks. Note that each
instance of the data block must be aligned on a 8 byte boundry.
Return Value:
status
--*/
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
struct DEVICE_EXTENSION * devExt = DeviceObject->DeviceExtension;
ULONG sizeNeeded;
switch(GuidIndex)
{
//
// Online diagnostic test
//
case FilterDiagnosticClass:
{
sizeNeeded = FIELD_OFFSET(DIAGNOSTIC_TEST, VariableData) +
2 * sizeof(ULONG) + // 2 characteristics
2 * sizeof(USHORT); // 3 resources used
if (BufferAvail >= sizeNeeded)
{
PDIAGNOSTIC_TEST diagTest = (PDIAGNOSTIC_TEST)Buffer;
PULONG characteristics;
PUSHORT resources;
ULONG offset;
diagTest->IsInUse = FALSE;
diagTest->ResourcesUsedCount = 2;
diagTest->CharacteristicsCount = 2;
offset = FIELD_OFFSET(DIAGNOSTIC_TEST, VariableData);
characteristics = (PULONG)OffsetToPtr(diagTest, offset);
offset += 2 * sizeof(ULONG);
resources = (PUSHORT)OffsetToPtr(diagTest, offset);
characteristics[0] = CharacteristicIsInteractive;
characteristics[1] = CharacteristicOther;
diagTest->OtherCharacteristic = OtherCharacteristicNoReboot;
resources[0] = ResourceUsedCPU;
resources[1] = ResourceUsedMemory;
*InstanceLengthArray = sizeNeeded;
status = STATUS_SUCCESS;
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
//
// Offline diagnostic test
//
case FilterOfflineDiagnosticClass:
{
sizeNeeded = FIELD_OFFSET(DIAGNOSTIC_TEST, VariableData) +
2 * sizeof(ULONG) + // 2 characteristics
2 * sizeof(USHORT); // 3 resources used
if (BufferAvail >= sizeNeeded)
{
PDIAGNOSTIC_TEST diagTest = (PDIAGNOSTIC_TEST)Buffer;
PULONG characteristics;
PUSHORT resources;
ULONG offset;
diagTest->IsInUse = FALSE;
diagTest->ResourcesUsedCount = 2;
diagTest->CharacteristicsCount = 2;
offset = FIELD_OFFSET(DIAGNOSTIC_TEST, VariableData);
characteristics = (PULONG)OffsetToPtr(diagTest, offset);
offset += 2 * sizeof(ULONG);
resources = (PUSHORT)OffsetToPtr(diagTest, offset);
characteristics[0] = CharacteristicIsInteractive;
characteristics[1] = CharacteristicOther;
diagTest->OtherCharacteristic = OfflineDiagnostic;
resources[0] = ResourceUsedCPU;
resources[1] = ResourceUsedMemory;
*InstanceLengthArray = sizeNeeded;
status = STATUS_SUCCESS;
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
//
// This has the list of valid settings for running the online
// or offline diagnostic tests. Note that you could have a
// different setting list for the online and offline tests. To
// do this you'd need to implement a different SettingList
// datablock and class
//
case FilterDiagnosticSettingListClass:
{
PMSSample_DiagnosticSettingList DiagSettingList;
ULONG i;
sizeNeeded = FIELD_OFFSET(MSSample_DiagnosticSettingList,
SettingList) +
3 * sizeof(MSSample_DiagnosticSetting);
if (BufferAvail >= sizeNeeded)
{
DiagSettingList = (PMSSample_DiagnosticSettingList)Buffer;
DiagSettingList->SettingCount = 3;
for (i = 0; i < 3; i++)
{
DiagSettingList->SettingList[i].TestWarningLevel = (USHORT)i+1;
DiagSettingList->SettingList[i].ReportSoftErrors = (i % 1) == 1 ?
TRUE :
FALSE;
DiagSettingList->SettingList[i].ReportStatusMessages = (i % 1) == 1 ?
TRUE :
FALSE;
DiagSettingList->SettingList[i].HaltOnError = (i % 1) == 0 ?
TRUE :
FALSE;
DiagSettingList->SettingList[i].QuickMode = (i % 1) == 0 ?
TRUE :
FALSE;
DiagSettingList->SettingList[i].PercentOfTestCoverage = 100;
}
*InstanceLengthArray = sizeNeeded;
status = STATUS_SUCCESS;
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
//
// This class returns the results of the offline diagnostic
// test that was run at device start time. There needs to be
// one results data block for each offline diagnostic test that
// could be run.
//
case FilterOfflineResultsClass:
{
PMSSample_DiagnosticResult diagResult;
USHORT executionIDSize, executionIDSizePad4;
//
// Here we are queried for the results from the offline
// test execution. If offline diags weren't run at start
// then we return guid not found
//
if (devExt->OfflineTestResult != 0)
{
//
// We return the execution ID string padded out to 4
// bytes followed by a result data block
//
executionIDSize = *((PUSHORT)devExt->ExecutionID) + sizeof(USHORT);
executionIDSizePad4 = (executionIDSize + 3) & ~3;
sizeNeeded = executionIDSizePad4 +
sizeof(MSSample_DiagnosticResult);
if (BufferAvail >= sizeNeeded)
{
RtlCopyMemory(Buffer,
&devExt->ExecutionID,
executionIDSize);
diagResult = (PMSSample_DiagnosticResult)(Buffer + executionIDSizePad4);
diagResult->EstimatedTimeOfPerforming = 0;
diagResult->TestState = TestStateOther;
diagResult->OtherStateDescription = OtherTestStatePassWithFlyingColors;
diagResult->PercentComplete = 100;
diagResult->TestResultsCount = 1;
diagResult->TestResults[0] = devExt->OfflineTestResult;
*InstanceLengthArray = sizeNeeded;
status = STATUS_SUCCESS;
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
} else {
status = STATUS_WMI_GUID_NOT_FOUND;
}
break;
}
default:
{
status = STATUS_WMI_GUID_NOT_FOUND;
break;
}
}
//
// Complete the irp. If there was not enough room in the output buffer
// then status is STATUS_BUFFER_TOO_SMALL and sizeNeeded has the size
// needed to return all of the data. If there was enough room then
// status is STATUS_SUCCESS and sizeNeeded is the actual number of bytes
// being returned.
status = WmiCompleteRequest(
DeviceObject,
Irp,
status,
sizeNeeded,
IO_NO_INCREMENT);
return(status);
}
ULONG FilterRunDiagnostic(
PMSSample_DiagnosticSetting DiagSetting,
PMSSample_DiagnosticResult DiagResult
)
{
//
// Here is where we can run the online diagnostic test. In this sample we
// simply return that the diagnostic ran successfully, however more
// sophisticated diagnostics will want to do more.
//
//
// Now build the diagnostic results to return. Note that the diag
// results are in the same memory as the diagnostic settings so
// once we start writing the results the settings are overwritten.
//
DiagResult->EstimatedTimeOfPerforming = 1;
DiagResult->TestState = TestStateOther;
DiagResult->OtherStateDescription = OtherTestStatePassWithFlyingColors;
DiagResult->PercentComplete = 100;
DiagResult->TestResultsCount = 2;
DiagResult->TestResults[0] = TestResultPassHappy;
DiagResult->TestResults[1] = TestResultPassSad;
return(RunDiscontinueTestOk);
}
ULONG FilterComputeDiagResultSize(
PMSSample_DiagnosticSetting DiagSetting
)
{
//
// Based upon the test settings that are passed to run the test we
// compute how large an output buffer is needed so to return the
// diagnostic results. It is important that we do this before
// running the test since we do not want to run the test and then
// realize that we cannot return the complete results. In the case
// of the sample driver the size to be returned is fixed.
//
return(FIELD_OFFSET(MSSample_DiagnosticResult, TestResults) +
2 * sizeof(ULONG));
}
NTSTATUS
FilterOfflineRunTest(
IN struct DEVICE_EXTENSION * devExt,
IN PUCHAR Buffer,
IN ULONG InBufferSize,
IN ULONG OutBufferSize,
OUT ULONG *sizeNeeded
)
{
HANDLE keyHandle;
UNICODE_STRING valueName;
PMSSample_RunTestOut runTestOut;
PULONG resultStatus;
PMSSample_DiagnosticSetting diagSetting;
PMSSample_DiagnosticResult diagResult;
USHORT executionIDSize;
ULONG inSizeNeeded;
NTSTATUS status;
if (InBufferSize >= sizeof(USHORT))
{
//
// The input buffer is a string followed by a diagnostic
// setting class. Make sure that the input buffer size is setup
// correctly.
//
executionIDSize = *((PUSHORT)Buffer) + sizeof(USHORT);
inSizeNeeded = executionIDSize + sizeof(MSSample_DiagnosticSetting);
if (InBufferSize == inSizeNeeded)
{
diagSetting = (PMSSample_DiagnosticSetting)(Buffer + executionIDSize);
runTestOut = (PMSSample_RunTestOut)Buffer;
resultStatus = &runTestOut->Result;
diagResult = &runTestOut->DiagResult;
*sizeNeeded = sizeof(MSSample_RunTestOut);
if (OutBufferSize >= *sizeNeeded)
{
//
// Ok we have been asked to perform a
// diagnostic that requires the device being
// taken offline so we save the settings for
// the test off and then the next time the
// device is started we run the test and report
// the results
//
status = IoOpenDeviceRegistryKey(devExt->physicalDevObj,
PLUGPLAY_REGKEY_DEVICE,
KEY_READ |
KEY_WRITE,
&keyHandle);
if (NT_SUCCESS(status))
{
//
// We just write out to this value blindly,
// but we need to be careful as this key is
// shared by all drivers in the stack so
// there is a possibility of collision in
// case the FDO or PDO might also want to
// store diagnostic info
//
RtlInitUnicodeString(&valueName, L"OfflineSetting");
status = ZwSetValueKey(keyHandle,
&valueName,
0,
REG_BINARY,
Buffer,
InBufferSize);
if (NT_SUCCESS(status))
{
//
// Now fill out the diag results
// structure to indicate that the test
// is pending
//
diagResult->EstimatedTimeOfPerforming = 0;
diagResult->TestState = TestStateOther;
diagResult->OtherStateDescription = OfflinePendingExecution;
diagResult->PercentComplete = 0;
diagResult->TestResultsCount = 0;
*resultStatus = RunDiscontinueTestOk;
}
ZwClose(keyHandle);
}
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
} else {
status = STATUS_INVALID_PARAMETER;
}
} else {
status = STATUS_INVALID_PARAMETER;
}
return(status);
}
NTSTATUS
FilterExecuteWmiMethod(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN ULONG GuidIndex,
IN ULONG InstanceIndex,
IN ULONG MethodId,
IN ULONG InBufferSize,
IN ULONG OutBufferSize,
IN PUCHAR Buffer
)
/*++
Routine Description:
This routine is a callback into the driver to execute a method. If
the driver can complete the method within the callback it should
call WmiCompleteRequest to complete the irp before returning to the
caller. Or the driver can return STATUS_PENDING if the irp cannot be
completed immediately and must then call WmiCompleteRequest once the
data is changed.
Arguments:
DeviceObject is the device whose method is being executed
Irp is the Irp that makes this request
GuidIndex is the index into the list of guids provided when the
device registered
MethodId has the id of the method being called
InBufferSize has the size of the data block passed in as the input to
the method.
OutBufferSize on entry has the maximum size available to write the
returned data block.
Buffer is filled with the input buffer on entry and returns with
the output data block
Return Value:
status
--*/
{
ULONG sizeNeeded = 0;
NTSTATUS status;
struct DEVICE_EXTENSION * devExt = DeviceObject->DeviceExtension;
PAGED_CODE();
if (GuidIndex == FilterDiagnosticClass)
{
switch(MethodId)
{
case RunTest:
{
PMSSample_RunTestOut runTestOut;
PULONG resultStatus;
PMSSample_DiagnosticSetting diagSetting;
PMSSample_DiagnosticResult diagResult;
USHORT executionIDSize;
ULONG inSizeNeeded;
//
// The input buffer is a string followed by a diagnostic
// setting class. Make sure that the input buffer size is setup
// correctly.
//
if (InBufferSize >= sizeof(USHORT))
{
executionIDSize = *((PUSHORT)Buffer) + sizeof(USHORT);
inSizeNeeded = executionIDSize + sizeof(MSSample_DiagnosticSetting);
if (InBufferSize == inSizeNeeded)
{
diagSetting = (PMSSample_DiagnosticSetting)(Buffer + executionIDSize);
runTestOut = (PMSSample_RunTestOut)Buffer;
resultStatus = &runTestOut->Result;
diagResult = &runTestOut->DiagResult;
sizeNeeded = sizeof(ULONG) + FilterComputeDiagResultSize(diagSetting);
if (OutBufferSize >= sizeNeeded)
{
*resultStatus = FilterRunDiagnostic(diagSetting,
diagResult);
status = STATUS_SUCCESS;
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
} else {
status = STATUS_INVALID_PARAMETER;
}
} else {
status = STATUS_INVALID_PARAMETER;
}
break;
}
case DiscontinueTest:
{
PDISCONTINUE_TEST_OUT discTestOut;
sizeNeeded = sizeof(DISCONTINUE_TEST_OUT);
if (OutBufferSize >= sizeNeeded)
{
//
// Right here we could make an attempt to stop a
// test that is currently being executed, however
// our test is very quick so it does not make
// sense. If your driver has a test that takes a
// long time to complete then it is possible to put
// a checkpoint into your test and signal it from
// here.
//
discTestOut = (PDISCONTINUE_TEST_OUT)Buffer;
discTestOut->Result = RunDiscontinueTestNotImplemented;
discTestOut->TestingStopped = FALSE;
status = STATUS_SUCCESS;
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
default:
{
status = STATUS_WMI_ITEMID_NOT_FOUND;
}
}
} else if (GuidIndex == FilterOfflineDiagnosticClass) {
switch(MethodId)
{
case RunTest:
{
status = FilterOfflineRunTest(devExt,
Buffer,
InBufferSize,
OutBufferSize,
&sizeNeeded);
break;
}
case DiscontinueTest:
{
PDISCONTINUE_TEST_OUT discTestOut;
HANDLE keyHandle;
UNICODE_STRING valueName;
sizeNeeded = sizeof(DISCONTINUE_TEST_OUT);
if (OutBufferSize >= sizeNeeded)
{
//
// Right here we are asked to discontinue execution
// of the offline test. All we need to do is make
// sure that the registry value is deleted
//
status = IoOpenDeviceRegistryKey(devExt->physicalDevObj,
PLUGPLAY_REGKEY_DEVICE,
KEY_READ,
&keyHandle);
if (NT_SUCCESS(status))
{
//
// We just read from this value blindly,
// but we need to be careful as this key is
// shared by all drivers in the stack so
// there is a possibility of collision in
// case the FDO or PDO might also want to
// use something unique to this driver to store
// diagnostic info
//
RtlInitUnicodeString(&valueName, L"OfflineSetting");
FilterZwDeleteValueKey(keyHandle,
&valueName);
ZwClose(keyHandle);
}
discTestOut = (PDISCONTINUE_TEST_OUT)Buffer;
discTestOut->Result = RunDiscontinueTestOk;
discTestOut->TestingStopped = TRUE;
status = STATUS_SUCCESS;
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
default:
{
status = STATUS_WMI_ITEMID_NOT_FOUND;
}
}
} else {
status = STATUS_WMI_GUID_NOT_FOUND;
}
status = WmiCompleteRequest(
DeviceObject,
Irp,
status,
sizeNeeded,
IO_NO_INCREMENT);
return(status);
}
NTSTATUS
FilterFunctionControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN ULONG GuidIndex,
IN WMIENABLEDISABLECONTROL Function,
IN BOOLEAN Enable
)
/*++
Routine Description:
This routine is a callback into the driver to enabled or disable event
generation or data block collection. A device should only expect a
single enable when the first event or data consumer enables events or
data collection and a single disable when the last event or data
consumer disables events or data collection. Data blocks will only
receive collection enable/disable if they were registered as requiring
it. If the driver can complete enabling/disabling within the callback it
should call WmiCompleteRequest to complete the irp before returning to
the caller. Or the driver can return STATUS_PENDING if the irp cannot be
completed immediately and must then call WmiCompleteRequest once the
data is changed.
Arguments:
DeviceObject is the device object
GuidIndex is the index into the list of guids provided when the
device registered
Function specifies which functionality is being enabled or disabled
Enable is TRUE then the function is being enabled else disabled
Return Value:
status
--*/
{
NTSTATUS status;
PAGED_CODE();
switch(GuidIndex)
{
case FilterDiagnosticClass:
{
if (Enable)
{
//
// A consumer has just indicated interest in accessing
// information about the FilterDiagnosticClass, most
// likely it will want to query the class and execute
// methods in it. If there is anything that needs to be
// done such as setting up hardware, enabling counters,
// etc before the class is queried or executed then
// this is the place to do it. Note that only one
// enable will be sent regardless of the number of
// consumers who want to access the class.
//
status = STATUS_SUCCESS;
} else {
//
// The last consumer has just indicated that it is no
// longer interested in this class and so the class
// will no longer be queried or its methods executed.
// If there is anything that needs to be done such as
// resetting hardware or stopping counters, etc then it
// should be done here. Note that only one disable will
// be sent regardless of the number of consumers who
// previous used the class.
}
break;
}
default:
{
status = STATUS_WMI_GUID_NOT_FOUND;
break;
}
}
status = WmiCompleteRequest(
DeviceObject,
Irp,
STATUS_SUCCESS,
0,
IO_NO_INCREMENT);
return(status);
}
NTSTATUS FilterPerformOfflineDiags(
struct DEVICE_EXTENSION *devExt
)
{
UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
MAXEXECUTIONIDSIZE + sizeof(MSSample_DiagnosticSetting)];
PKEY_VALUE_PARTIAL_INFORMATION keyValuePartialInfo;
ULONG infoSize;
NTSTATUS status;
HANDLE keyHandle;
PMSSample_DiagnosticSetting diagSetting;
UNICODE_STRING valueName;
USHORT executionIDSize;
PAGED_CODE();
//
// If registry has stuff then run test, else return
//
status = IoOpenDeviceRegistryKey(devExt->physicalDevObj,
PLUGPLAY_REGKEY_DEVICE,
KEY_READ,
&keyHandle);
if (NT_SUCCESS(status))
{
//
// We just read from this value blindly,
// but we need to be careful as this key is
// shared by all drivers in the stack so
// there is a possibility of collision in
// case the FDO or PDO might also want to
// use something unique to this driver to store
// diagnostic info
//
RtlInitUnicodeString(&valueName, L"OfflineSetting");
keyValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
infoSize = sizeof(buffer);
status = ZwQueryValueKey(keyHandle,
&valueName,
KeyValuePartialInformation,
keyValuePartialInfo,
infoSize,
&infoSize);
if ((NT_SUCCESS(status)) &&
(keyValuePartialInfo->Type == REG_BINARY) &&
(keyValuePartialInfo->DataLength != 0))
{
//
// We successfully read the diagnostics settings for the
// offline test. First thing we do is delete the value so
// that in case the the diagnostic test causes a problem
// then it won't be run in the next time the device starts
// up
//
FilterZwDeleteValueKey(keyHandle,
&valueName);
//
// Here is where we run our offline test. Remember the
// Execution ID tag as we'll need to give that back to
// the CDM provider
//
devExt->OfflineTestResult = TestResultPassSad;
executionIDSize = *((PUSHORT)(keyValuePartialInfo->Data)) + sizeof(USHORT);
RtlCopyMemory(&devExt->ExecutionID,
keyValuePartialInfo->Data,
executionIDSize);
}
ZwClose(keyHandle);
}
return(status);
}
NTSTATUS FilterZwDeleteValueKey(
HANDLE KeyHandle,
PUNICODE_STRING ValueName
)
{
NTSTATUS status;
//
// Since we do not have ZwDeleteValueKey as a proper WDM function
// then we try to make one up. What we do is to set the value to an
// empty REG_BINARY
//
status = ZwSetValueKey(KeyHandle,
ValueName,
0,
REG_BINARY,
NULL,
0);
return(status);
}