Source code of Windows XP (NT5)
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.

1239 lines
35 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. wmisamp.c
  5. Abstract:
  6. Sample device driver whose purpose is to show how to interface with
  7. the CDM provider and implement online and offline diagnostics
  8. Environment:
  9. WDM, NT and Windows 98
  10. Revision History:
  11. --*/
  12. #include <WDM.H>
  13. #include "filter.h"
  14. #include <wmistr.h>
  15. #include <wmiguid.h>
  16. #define OffsetToPtr(Base, Offset) ((PUCHAR)((PUCHAR)(Base) + (Offset)))
  17. //
  18. // These structures need to be defined by hand since they have varaible
  19. // length elements and thus cannot be generated automatically by the
  20. // mof checking tools
  21. //
  22. typedef struct
  23. {
  24. BOOLEAN IsInUse;
  25. ULONG ResourcesUsedCount;
  26. ULONG CharacteristicsCount;
  27. #define OtherCharacteristicNoReboot 0
  28. #define OtherCharacteristicReboot 1
  29. #define OfflineDiagnostic 0
  30. ULONG OtherCharacteristic;
  31. #define CharacteristicUnknown 0
  32. #define CharacteristicOther 1
  33. #define CharacteristicIsExclusive 2
  34. #define CharacteristicIsInteractive 3
  35. #define CharacteristicIsDestructive 4
  36. #define CharacteristicIsRisky 5
  37. #define CharacteristicIsPackage 6
  38. #define CharacteristicSupportsPercent 7
  39. // uint32 Characteristics[];
  40. #define ResourceUsedCPU 0
  41. #define ResourceUsedMemory 1
  42. #define ResourceUsedHardDisk 2
  43. #define ResourceUsedCDROM 3
  44. #define ResourceUsedFloppy 4
  45. #define ResourceUsedPCIBus 5
  46. #define ResourceUsedUSBBus 6
  47. #define ResourceUsed1394Bus 7
  48. #define ResourceUsedSCSIBus 8
  49. #define ResourceUsedIDEBus 9
  50. #define ResourceUsedNetwork 10
  51. #define ResourceUsedISABus 11
  52. #define ResourceUsedEISABus 12
  53. #define ResourceUsedVESABus 13
  54. #define ResourceUsedPCMCIABus 14
  55. #define ResourceUsedCardBus 15
  56. #define ResourceUsedAccessBus 16
  57. #define ResourceUsedNuBus 17
  58. #define ResourceUsedAGP 18
  59. #define ResourceUsedVMEBus 19
  60. #define ResourceUsedSbusIEEE1396_1993 20
  61. #define ResourceUsedMCABus 21
  62. #define ResourceUsedGIOBus 22
  63. #define ResourceUsedXIOBus 23
  64. #define ResourceUsedHIOBus 24
  65. #define ResourceUsedPMCBus 25
  66. #define ResourceUsedSIOBus 26
  67. // uint16 ResourcesUsed[];
  68. UCHAR VariableData[1];
  69. } DIAGNOSTIC_TEST, *PDIAGNOSTIC_TEST;
  70. typedef struct
  71. {
  72. ULONG Result;
  73. BOOLEAN TestingStopped;
  74. } DISCONTINUE_TEST_OUT, *PDISCONTINUE_TEST_OUT;
  75. enum RunDiscontinueTestResults
  76. {
  77. // 0 = OK (function succeeded, but the test itself may have failed
  78. RunDiscontinueTestOk = 0,
  79. // 1 = Unspecified Error (function failed for unspecified reasons)
  80. RunDiscontinueTestUnspecifiedError = 1,
  81. // 2 = Not Implemented (function is not implemented for this instance
  82. RunDiscontinueTestNotImplemented = 2,
  83. // 3 = Out Of Resources (component could not allocate required
  84. // resources, e.g. memory, disk space, etc.)
  85. RunDiscontinueTestOutOfResources = 3
  86. };
  87. NTSTATUS
  88. FilterFunctionControl(
  89. IN PDEVICE_OBJECT DeviceObject,
  90. IN PIRP Irp,
  91. IN ULONG GuidIndex,
  92. IN WMIENABLEDISABLECONTROL Function,
  93. IN BOOLEAN Enable
  94. );
  95. NTSTATUS
  96. FilterExecuteWmiMethod(
  97. IN PDEVICE_OBJECT DeviceObject,
  98. IN PIRP Irp,
  99. IN ULONG GuidIndex,
  100. IN ULONG InstanceIndex,
  101. IN ULONG MethodId,
  102. IN ULONG InBufferSize,
  103. IN ULONG OutBufferSize,
  104. IN PUCHAR Buffer
  105. );
  106. NTSTATUS
  107. FilterQueryWmiDataBlock(
  108. IN PDEVICE_OBJECT DeviceObject,
  109. IN PIRP Irp,
  110. IN ULONG GuidIndex,
  111. IN ULONG InstanceIndex,
  112. IN ULONG InstanceCount,
  113. IN OUT PULONG InstanceLengthArray,
  114. IN ULONG BufferAvail,
  115. OUT PUCHAR Buffer
  116. );
  117. NTSTATUS
  118. FilterQueryWmiRegInfo(
  119. IN PDEVICE_OBJECT DeviceObject,
  120. OUT ULONG *RegFlags,
  121. OUT PUNICODE_STRING InstanceName,
  122. OUT PUNICODE_STRING *RegistryPath,
  123. OUT PUNICODE_STRING MofResourceName,
  124. OUT PDEVICE_OBJECT *Pdo
  125. );
  126. NTSTATUS FilterZwDeleteValueKey(
  127. HANDLE KeyHandle,
  128. PUNICODE_STRING ValueName
  129. );
  130. #ifdef ALLOC_PRAGMA
  131. #pragma alloc_text(PAGE,FilterQueryWmiRegInfo)
  132. #pragma alloc_text(PAGE,FilterQueryWmiDataBlock)
  133. #pragma alloc_text(PAGE,FilterExecuteWmiMethod)
  134. #pragma alloc_text(PAGE,FilterFunctionControl)
  135. #pragma alloc_text(PAGE,FilterPerformOfflineDiags)
  136. #pragma alloc_text(PAGE,FilterZwDeleteValueKey)
  137. #endif
  138. //
  139. // Create data structures for identifying the guids and reporting them to
  140. // WMI. Since the WMILIB callbacks pass an index into the guid list we make
  141. // definitions for the various guids indicies.
  142. //
  143. #define FilterDiagnosticClass 0
  144. #define FilterOfflineDiagnosticClass 1
  145. #define FilterDiagnosticSettingListClass 2
  146. #define FilterOfflineResultsClass 3
  147. GUID FilterDiagnosticClassGuid = MSSample_DiagnosticTestGuid;
  148. GUID FilterOfflineDiagnosticClassGuid = MSSample_OfflineDiagnosticTestGuid;
  149. GUID FilterDiagnosticSettingListGuid = MSSample_DiagnosticSettingListGuid;
  150. GUID FilterOfflineResultsGuid = MSSample_OfflineResultGuid;
  151. WMIGUIDREGINFO FilterGuidList[] =
  152. {
  153. {
  154. &FilterDiagnosticClassGuid, // Guid
  155. 1, // # of instances in each device
  156. WMIREG_FLAG_EXPENSIVE // Flag as expensive to collect
  157. },
  158. {
  159. &FilterOfflineDiagnosticClassGuid, // Guid
  160. 1, // # of instances in each device
  161. 0 // Flag as expensive to collect
  162. },
  163. {
  164. &FilterDiagnosticSettingListGuid, // Guid
  165. 1, // # of instances in each device
  166. 0 // Flag as not expensive to collect
  167. },
  168. {
  169. &FilterOfflineResultsGuid, // Guid
  170. 1, // # of instances in each device
  171. 0 // Flag as not expensive to collect
  172. }
  173. };
  174. #define FilterGuidCount (sizeof(FilterGuidList) / sizeof(WMIGUIDREGINFO))
  175. //
  176. // We need to hang onto the registry path passed to our driver entry so that
  177. // we can return it in the QueryWmiRegInfo callback.
  178. //
  179. UNICODE_STRING FilterRegistryPath;
  180. NTSTATUS VA_SystemControl(
  181. struct DEVICE_EXTENSION *devExt,
  182. PIRP irp,
  183. PBOOLEAN passIrpDown
  184. )
  185. /*++
  186. Routine Description:
  187. Dispatch routine for System Control IRPs (MajorFunction == IRP_MJ_SYSTEM_CONTROL)
  188. Arguments:
  189. devExt - device extension for targetted device object
  190. irp - Io Request Packet
  191. *passIrpDown - returns with whether to pass irp down stack
  192. Return Value:
  193. NT status code
  194. --*/
  195. {
  196. PWMILIB_CONTEXT wmilibContext;
  197. NTSTATUS status;
  198. SYSCTL_IRP_DISPOSITION disposition;
  199. wmilibContext = &devExt->WmiLib;
  200. //
  201. // Call Wmilib helper function to crack the irp. If this is a wmi irp
  202. // that is targetted for this device then WmiSystemControl will callback
  203. // at the appropriate callback routine.
  204. //
  205. status = WmiSystemControl(wmilibContext,
  206. devExt->filterDevObj,
  207. irp,
  208. &disposition);
  209. switch(disposition)
  210. {
  211. case IrpProcessed:
  212. {
  213. //
  214. // This irp has been processed and may be completed or pending.
  215. *passIrpDown = FALSE;
  216. break;
  217. }
  218. case IrpNotCompleted:
  219. {
  220. //
  221. // This irp has not been completed, but has been fully processed.
  222. // we will complete it now.
  223. *passIrpDown = FALSE;
  224. IoCompleteRequest(irp, IO_NO_INCREMENT);
  225. break;
  226. }
  227. case IrpForward:
  228. case IrpNotWmi:
  229. {
  230. //
  231. // This irp is either not a WMI irp or is a WMI irp targetted
  232. // at a device lower in the stack.
  233. *passIrpDown = TRUE;
  234. break;
  235. }
  236. default:
  237. {
  238. //
  239. // We really should never get here, but if we do just forward....
  240. ASSERT(FALSE);
  241. *passIrpDown = TRUE;
  242. break;
  243. }
  244. }
  245. return(status);
  246. }
  247. NTSTATUS
  248. FilterInitializeWmiDataBlocks(
  249. IN struct DEVICE_EXTENSION *devExt
  250. )
  251. /*++
  252. Routine Description:
  253. This routine is called to create a new instance of the device
  254. Arguments:
  255. devExt is device extension
  256. Return Value:
  257. --*/
  258. {
  259. PWMILIB_CONTEXT wmilibInfo;
  260. //
  261. // Fill in the WMILIB_CONTEXT structure with a pointer to the
  262. // callback routines and a pointer to the list of guids
  263. // supported by the driver
  264. //
  265. wmilibInfo = &devExt->WmiLib;
  266. wmilibInfo->GuidCount = FilterGuidCount;
  267. wmilibInfo->GuidList = FilterGuidList;
  268. wmilibInfo->QueryWmiRegInfo = FilterQueryWmiRegInfo;
  269. wmilibInfo->QueryWmiDataBlock = FilterQueryWmiDataBlock;
  270. wmilibInfo->SetWmiDataBlock = NULL;
  271. wmilibInfo->SetWmiDataItem = NULL;
  272. wmilibInfo->ExecuteWmiMethod = FilterExecuteWmiMethod;
  273. wmilibInfo->WmiFunctionControl = FilterFunctionControl;
  274. return(STATUS_SUCCESS);
  275. }
  276. NTSTATUS
  277. FilterQueryWmiRegInfo(
  278. IN PDEVICE_OBJECT DeviceObject,
  279. OUT ULONG *RegFlags,
  280. OUT PUNICODE_STRING InstanceName,
  281. OUT PUNICODE_STRING *RegistryPath,
  282. OUT PUNICODE_STRING MofResourceName,
  283. OUT PDEVICE_OBJECT *Pdo
  284. )
  285. /*++
  286. Routine Description:
  287. This routine is a callback into the driver to retrieve the list of
  288. guids or data blocks that the driver wants to register with WMI. This
  289. routine may not pend or block. Driver should NOT call
  290. WmiCompleteRequest.
  291. Arguments:
  292. DeviceObject is the device whose registration info is being queried
  293. *RegFlags returns with a set of flags that describe the guids being
  294. registered for this device. If the device wants enable and disable
  295. collection callbacks before receiving queries for the registered
  296. guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the
  297. returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case
  298. the instance name is determined from the PDO associated with the
  299. device object. Note that the PDO must have an associated devnode. If
  300. WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique
  301. name for the device.
  302. InstanceName returns with the instance name for the guids if
  303. WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The
  304. caller will call ExFreePool with the buffer returned.
  305. *RegistryPath returns with the registry path of the driver. The caller
  306. does NOT free this buffer.
  307. *MofResourceName returns with the name of the MOF resource attached to
  308. the binary file. If the driver does not have a mof resource attached
  309. then this can be returned as NULL. The caller does NOT free this
  310. buffer.
  311. *Pdo returns with the device object for the PDO associated with this
  312. device if the WMIREG_FLAG_INSTANCE_PDO flag is retured in
  313. *RegFlags.
  314. Return Value:
  315. status
  316. --*/
  317. {
  318. struct DEVICE_EXTENSION * devExt = DeviceObject->DeviceExtension;
  319. //
  320. // Return the registry path for this driver. This is required so WMI
  321. // can find your driver image and can attribute any eventlog messages to
  322. // your driver.
  323. *RegistryPath = &FilterRegistryPath;
  324. //
  325. // Return the name specified in the .rc file of the resource which
  326. // contains the bianry mof data. By default WMI will look for this
  327. // resource in the driver image (.sys) file, however if the value
  328. // MofImagePath is specified in the driver's registry key
  329. // then WMI will look for the resource in the file specified there.
  330. RtlInitUnicodeString(MofResourceName, L"MofResourceName");
  331. //
  332. // Specify that the driver wants WMI to automatically generate instance
  333. // names for all of the data blocks based upon the device stack's
  334. // device instance id. Doing this is STRONGLY recommended since additional
  335. // information about the device would then be available to callers.
  336. *RegFlags = WMIREG_FLAG_INSTANCE_PDO;
  337. *Pdo = devExt->physicalDevObj;
  338. return(STATUS_SUCCESS);
  339. }
  340. NTSTATUS
  341. FilterQueryWmiDataBlock(
  342. IN PDEVICE_OBJECT DeviceObject,
  343. IN PIRP Irp,
  344. IN ULONG GuidIndex,
  345. IN ULONG InstanceIndex,
  346. IN ULONG InstanceCount,
  347. IN OUT PULONG InstanceLengthArray,
  348. IN ULONG BufferAvail,
  349. OUT PUCHAR Buffer
  350. )
  351. /*++
  352. Routine Description:
  353. This routine is a callback into the driver to query for the contents of
  354. all instances of a data block. If the driver can satisfy the query within
  355. the callback it should call WmiCompleteRequest to complete the irp before
  356. returning to the caller. Or the driver can return STATUS_PENDING if the
  357. irp cannot be completed immediately and must then call WmiCompleteRequest
  358. once the query is satisfied.
  359. Arguments:
  360. DeviceObject is the device whose data block is being queried
  361. Irp is the Irp that makes this request
  362. GuidIndex is the index into the list of guids provided when the
  363. device registered
  364. InstanceCount is the number of instnaces expected to be returned for
  365. the data block.
  366. InstanceLengthArray is a pointer to an array of ULONG that returns the
  367. lengths of each instance of the data block. If this is NULL then
  368. there was not enough space in the output buffer to fufill the request
  369. so the irp should be completed with the buffer needed.
  370. BufferAvail on entry has the maximum size available to write the data
  371. blocks.
  372. Buffer on return is filled with the returned data blocks. Note that each
  373. instance of the data block must be aligned on a 8 byte boundry.
  374. Return Value:
  375. status
  376. --*/
  377. {
  378. NTSTATUS status = STATUS_UNSUCCESSFUL;
  379. struct DEVICE_EXTENSION * devExt = DeviceObject->DeviceExtension;
  380. ULONG sizeNeeded;
  381. switch(GuidIndex)
  382. {
  383. //
  384. // Online diagnostic test
  385. //
  386. case FilterDiagnosticClass:
  387. {
  388. sizeNeeded = FIELD_OFFSET(DIAGNOSTIC_TEST, VariableData) +
  389. 2 * sizeof(ULONG) + // 2 characteristics
  390. 2 * sizeof(USHORT); // 3 resources used
  391. if (BufferAvail >= sizeNeeded)
  392. {
  393. PDIAGNOSTIC_TEST diagTest = (PDIAGNOSTIC_TEST)Buffer;
  394. PULONG characteristics;
  395. PUSHORT resources;
  396. ULONG offset;
  397. diagTest->IsInUse = FALSE;
  398. diagTest->ResourcesUsedCount = 2;
  399. diagTest->CharacteristicsCount = 2;
  400. offset = FIELD_OFFSET(DIAGNOSTIC_TEST, VariableData);
  401. characteristics = (PULONG)OffsetToPtr(diagTest, offset);
  402. offset += 2 * sizeof(ULONG);
  403. resources = (PUSHORT)OffsetToPtr(diagTest, offset);
  404. characteristics[0] = CharacteristicIsInteractive;
  405. characteristics[1] = CharacteristicOther;
  406. diagTest->OtherCharacteristic = OtherCharacteristicNoReboot;
  407. resources[0] = ResourceUsedCPU;
  408. resources[1] = ResourceUsedMemory;
  409. *InstanceLengthArray = sizeNeeded;
  410. status = STATUS_SUCCESS;
  411. } else {
  412. status = STATUS_BUFFER_TOO_SMALL;
  413. }
  414. break;
  415. }
  416. //
  417. // Offline diagnostic test
  418. //
  419. case FilterOfflineDiagnosticClass:
  420. {
  421. sizeNeeded = FIELD_OFFSET(DIAGNOSTIC_TEST, VariableData) +
  422. 2 * sizeof(ULONG) + // 2 characteristics
  423. 2 * sizeof(USHORT); // 3 resources used
  424. if (BufferAvail >= sizeNeeded)
  425. {
  426. PDIAGNOSTIC_TEST diagTest = (PDIAGNOSTIC_TEST)Buffer;
  427. PULONG characteristics;
  428. PUSHORT resources;
  429. ULONG offset;
  430. diagTest->IsInUse = FALSE;
  431. diagTest->ResourcesUsedCount = 2;
  432. diagTest->CharacteristicsCount = 2;
  433. offset = FIELD_OFFSET(DIAGNOSTIC_TEST, VariableData);
  434. characteristics = (PULONG)OffsetToPtr(diagTest, offset);
  435. offset += 2 * sizeof(ULONG);
  436. resources = (PUSHORT)OffsetToPtr(diagTest, offset);
  437. characteristics[0] = CharacteristicIsInteractive;
  438. characteristics[1] = CharacteristicOther;
  439. diagTest->OtherCharacteristic = OfflineDiagnostic;
  440. resources[0] = ResourceUsedCPU;
  441. resources[1] = ResourceUsedMemory;
  442. *InstanceLengthArray = sizeNeeded;
  443. status = STATUS_SUCCESS;
  444. } else {
  445. status = STATUS_BUFFER_TOO_SMALL;
  446. }
  447. break;
  448. }
  449. //
  450. // This has the list of valid settings for running the online
  451. // or offline diagnostic tests. Note that you could have a
  452. // different setting list for the online and offline tests. To
  453. // do this you'd need to implement a different SettingList
  454. // datablock and class
  455. //
  456. case FilterDiagnosticSettingListClass:
  457. {
  458. PMSSample_DiagnosticSettingList DiagSettingList;
  459. ULONG i;
  460. sizeNeeded = FIELD_OFFSET(MSSample_DiagnosticSettingList,
  461. SettingList) +
  462. 3 * sizeof(MSSample_DiagnosticSetting);
  463. if (BufferAvail >= sizeNeeded)
  464. {
  465. DiagSettingList = (PMSSample_DiagnosticSettingList)Buffer;
  466. DiagSettingList->SettingCount = 3;
  467. for (i = 0; i < 3; i++)
  468. {
  469. DiagSettingList->SettingList[i].TestWarningLevel = (USHORT)i+1;
  470. DiagSettingList->SettingList[i].ReportSoftErrors = (i % 1) == 1 ?
  471. TRUE :
  472. FALSE;
  473. DiagSettingList->SettingList[i].ReportStatusMessages = (i % 1) == 1 ?
  474. TRUE :
  475. FALSE;
  476. DiagSettingList->SettingList[i].HaltOnError = (i % 1) == 0 ?
  477. TRUE :
  478. FALSE;
  479. DiagSettingList->SettingList[i].QuickMode = (i % 1) == 0 ?
  480. TRUE :
  481. FALSE;
  482. DiagSettingList->SettingList[i].PercentOfTestCoverage = 100;
  483. }
  484. *InstanceLengthArray = sizeNeeded;
  485. status = STATUS_SUCCESS;
  486. } else {
  487. status = STATUS_BUFFER_TOO_SMALL;
  488. }
  489. break;
  490. }
  491. //
  492. // This class returns the results of the offline diagnostic
  493. // test that was run at device start time. There needs to be
  494. // one results data block for each offline diagnostic test that
  495. // could be run.
  496. //
  497. case FilterOfflineResultsClass:
  498. {
  499. PMSSample_DiagnosticResult diagResult;
  500. USHORT executionIDSize, executionIDSizePad4;
  501. //
  502. // Here we are queried for the results from the offline
  503. // test execution. If offline diags weren't run at start
  504. // then we return guid not found
  505. //
  506. if (devExt->OfflineTestResult != 0)
  507. {
  508. //
  509. // We return the execution ID string padded out to 4
  510. // bytes followed by a result data block
  511. //
  512. executionIDSize = *((PUSHORT)devExt->ExecutionID) + sizeof(USHORT);
  513. executionIDSizePad4 = (executionIDSize + 3) & ~3;
  514. sizeNeeded = executionIDSizePad4 +
  515. sizeof(MSSample_DiagnosticResult);
  516. if (BufferAvail >= sizeNeeded)
  517. {
  518. RtlCopyMemory(Buffer,
  519. &devExt->ExecutionID,
  520. executionIDSize);
  521. diagResult = (PMSSample_DiagnosticResult)(Buffer + executionIDSizePad4);
  522. diagResult->EstimatedTimeOfPerforming = 0;
  523. diagResult->TestState = TestStateOther;
  524. diagResult->OtherStateDescription = OtherTestStatePassWithFlyingColors;
  525. diagResult->PercentComplete = 100;
  526. diagResult->TestResultsCount = 1;
  527. diagResult->TestResults[0] = devExt->OfflineTestResult;
  528. *InstanceLengthArray = sizeNeeded;
  529. status = STATUS_SUCCESS;
  530. } else {
  531. status = STATUS_BUFFER_TOO_SMALL;
  532. }
  533. } else {
  534. status = STATUS_WMI_GUID_NOT_FOUND;
  535. }
  536. break;
  537. }
  538. default:
  539. {
  540. status = STATUS_WMI_GUID_NOT_FOUND;
  541. break;
  542. }
  543. }
  544. //
  545. // Complete the irp. If there was not enough room in the output buffer
  546. // then status is STATUS_BUFFER_TOO_SMALL and sizeNeeded has the size
  547. // needed to return all of the data. If there was enough room then
  548. // status is STATUS_SUCCESS and sizeNeeded is the actual number of bytes
  549. // being returned.
  550. status = WmiCompleteRequest(
  551. DeviceObject,
  552. Irp,
  553. status,
  554. sizeNeeded,
  555. IO_NO_INCREMENT);
  556. return(status);
  557. }
  558. ULONG FilterRunDiagnostic(
  559. PMSSample_DiagnosticSetting DiagSetting,
  560. PMSSample_DiagnosticResult DiagResult
  561. )
  562. {
  563. //
  564. // Here is where we can run the online diagnostic test. In this sample we
  565. // simply return that the diagnostic ran successfully, however more
  566. // sophisticated diagnostics will want to do more.
  567. //
  568. //
  569. // Now build the diagnostic results to return. Note that the diag
  570. // results are in the same memory as the diagnostic settings so
  571. // once we start writing the results the settings are overwritten.
  572. //
  573. DiagResult->EstimatedTimeOfPerforming = 1;
  574. DiagResult->TestState = TestStateOther;
  575. DiagResult->OtherStateDescription = OtherTestStatePassWithFlyingColors;
  576. DiagResult->PercentComplete = 100;
  577. DiagResult->TestResultsCount = 2;
  578. DiagResult->TestResults[0] = TestResultPassHappy;
  579. DiagResult->TestResults[1] = TestResultPassSad;
  580. return(RunDiscontinueTestOk);
  581. }
  582. ULONG FilterComputeDiagResultSize(
  583. PMSSample_DiagnosticSetting DiagSetting
  584. )
  585. {
  586. //
  587. // Based upon the test settings that are passed to run the test we
  588. // compute how large an output buffer is needed so to return the
  589. // diagnostic results. It is important that we do this before
  590. // running the test since we do not want to run the test and then
  591. // realize that we cannot return the complete results. In the case
  592. // of the sample driver the size to be returned is fixed.
  593. //
  594. return(FIELD_OFFSET(MSSample_DiagnosticResult, TestResults) +
  595. 2 * sizeof(ULONG));
  596. }
  597. NTSTATUS
  598. FilterOfflineRunTest(
  599. IN struct DEVICE_EXTENSION * devExt,
  600. IN PUCHAR Buffer,
  601. IN ULONG InBufferSize,
  602. IN ULONG OutBufferSize,
  603. OUT ULONG *sizeNeeded
  604. )
  605. {
  606. HANDLE keyHandle;
  607. UNICODE_STRING valueName;
  608. PMSSample_RunTestOut runTestOut;
  609. PULONG resultStatus;
  610. PMSSample_DiagnosticSetting diagSetting;
  611. PMSSample_DiagnosticResult diagResult;
  612. USHORT executionIDSize;
  613. ULONG inSizeNeeded;
  614. NTSTATUS status;
  615. if (InBufferSize >= sizeof(USHORT))
  616. {
  617. //
  618. // The input buffer is a string followed by a diagnostic
  619. // setting class. Make sure that the input buffer size is setup
  620. // correctly.
  621. //
  622. executionIDSize = *((PUSHORT)Buffer) + sizeof(USHORT);
  623. inSizeNeeded = executionIDSize + sizeof(MSSample_DiagnosticSetting);
  624. if (InBufferSize == inSizeNeeded)
  625. {
  626. diagSetting = (PMSSample_DiagnosticSetting)(Buffer + executionIDSize);
  627. runTestOut = (PMSSample_RunTestOut)Buffer;
  628. resultStatus = &runTestOut->Result;
  629. diagResult = &runTestOut->DiagResult;
  630. *sizeNeeded = sizeof(MSSample_RunTestOut);
  631. if (OutBufferSize >= *sizeNeeded)
  632. {
  633. //
  634. // Ok we have been asked to perform a
  635. // diagnostic that requires the device being
  636. // taken offline so we save the settings for
  637. // the test off and then the next time the
  638. // device is started we run the test and report
  639. // the results
  640. //
  641. status = IoOpenDeviceRegistryKey(devExt->physicalDevObj,
  642. PLUGPLAY_REGKEY_DEVICE,
  643. KEY_READ |
  644. KEY_WRITE,
  645. &keyHandle);
  646. if (NT_SUCCESS(status))
  647. {
  648. //
  649. // We just write out to this value blindly,
  650. // but we need to be careful as this key is
  651. // shared by all drivers in the stack so
  652. // there is a possibility of collision in
  653. // case the FDO or PDO might also want to
  654. // store diagnostic info
  655. //
  656. RtlInitUnicodeString(&valueName, L"OfflineSetting");
  657. status = ZwSetValueKey(keyHandle,
  658. &valueName,
  659. 0,
  660. REG_BINARY,
  661. Buffer,
  662. InBufferSize);
  663. if (NT_SUCCESS(status))
  664. {
  665. //
  666. // Now fill out the diag results
  667. // structure to indicate that the test
  668. // is pending
  669. //
  670. diagResult->EstimatedTimeOfPerforming = 0;
  671. diagResult->TestState = TestStateOther;
  672. diagResult->OtherStateDescription = OfflinePendingExecution;
  673. diagResult->PercentComplete = 0;
  674. diagResult->TestResultsCount = 0;
  675. *resultStatus = RunDiscontinueTestOk;
  676. }
  677. ZwClose(keyHandle);
  678. }
  679. } else {
  680. status = STATUS_BUFFER_TOO_SMALL;
  681. }
  682. } else {
  683. status = STATUS_INVALID_PARAMETER;
  684. }
  685. } else {
  686. status = STATUS_INVALID_PARAMETER;
  687. }
  688. return(status);
  689. }
  690. NTSTATUS
  691. FilterExecuteWmiMethod(
  692. IN PDEVICE_OBJECT DeviceObject,
  693. IN PIRP Irp,
  694. IN ULONG GuidIndex,
  695. IN ULONG InstanceIndex,
  696. IN ULONG MethodId,
  697. IN ULONG InBufferSize,
  698. IN ULONG OutBufferSize,
  699. IN PUCHAR Buffer
  700. )
  701. /*++
  702. Routine Description:
  703. This routine is a callback into the driver to execute a method. If
  704. the driver can complete the method within the callback it should
  705. call WmiCompleteRequest to complete the irp before returning to the
  706. caller. Or the driver can return STATUS_PENDING if the irp cannot be
  707. completed immediately and must then call WmiCompleteRequest once the
  708. data is changed.
  709. Arguments:
  710. DeviceObject is the device whose method is being executed
  711. Irp is the Irp that makes this request
  712. GuidIndex is the index into the list of guids provided when the
  713. device registered
  714. MethodId has the id of the method being called
  715. InBufferSize has the size of the data block passed in as the input to
  716. the method.
  717. OutBufferSize on entry has the maximum size available to write the
  718. returned data block.
  719. Buffer is filled with the input buffer on entry and returns with
  720. the output data block
  721. Return Value:
  722. status
  723. --*/
  724. {
  725. ULONG sizeNeeded = 0;
  726. NTSTATUS status;
  727. struct DEVICE_EXTENSION * devExt = DeviceObject->DeviceExtension;
  728. PAGED_CODE();
  729. if (GuidIndex == FilterDiagnosticClass)
  730. {
  731. switch(MethodId)
  732. {
  733. case RunTest:
  734. {
  735. PMSSample_RunTestOut runTestOut;
  736. PULONG resultStatus;
  737. PMSSample_DiagnosticSetting diagSetting;
  738. PMSSample_DiagnosticResult diagResult;
  739. USHORT executionIDSize;
  740. ULONG inSizeNeeded;
  741. //
  742. // The input buffer is a string followed by a diagnostic
  743. // setting class. Make sure that the input buffer size is setup
  744. // correctly.
  745. //
  746. if (InBufferSize >= sizeof(USHORT))
  747. {
  748. executionIDSize = *((PUSHORT)Buffer) + sizeof(USHORT);
  749. inSizeNeeded = executionIDSize + sizeof(MSSample_DiagnosticSetting);
  750. if (InBufferSize == inSizeNeeded)
  751. {
  752. diagSetting = (PMSSample_DiagnosticSetting)(Buffer + executionIDSize);
  753. runTestOut = (PMSSample_RunTestOut)Buffer;
  754. resultStatus = &runTestOut->Result;
  755. diagResult = &runTestOut->DiagResult;
  756. sizeNeeded = sizeof(ULONG) + FilterComputeDiagResultSize(diagSetting);
  757. if (OutBufferSize >= sizeNeeded)
  758. {
  759. *resultStatus = FilterRunDiagnostic(diagSetting,
  760. diagResult);
  761. status = STATUS_SUCCESS;
  762. } else {
  763. status = STATUS_BUFFER_TOO_SMALL;
  764. }
  765. } else {
  766. status = STATUS_INVALID_PARAMETER;
  767. }
  768. } else {
  769. status = STATUS_INVALID_PARAMETER;
  770. }
  771. break;
  772. }
  773. case DiscontinueTest:
  774. {
  775. PDISCONTINUE_TEST_OUT discTestOut;
  776. sizeNeeded = sizeof(DISCONTINUE_TEST_OUT);
  777. if (OutBufferSize >= sizeNeeded)
  778. {
  779. //
  780. // Right here we could make an attempt to stop a
  781. // test that is currently being executed, however
  782. // our test is very quick so it does not make
  783. // sense. If your driver has a test that takes a
  784. // long time to complete then it is possible to put
  785. // a checkpoint into your test and signal it from
  786. // here.
  787. //
  788. discTestOut = (PDISCONTINUE_TEST_OUT)Buffer;
  789. discTestOut->Result = RunDiscontinueTestNotImplemented;
  790. discTestOut->TestingStopped = FALSE;
  791. status = STATUS_SUCCESS;
  792. } else {
  793. status = STATUS_BUFFER_TOO_SMALL;
  794. }
  795. break;
  796. }
  797. default:
  798. {
  799. status = STATUS_WMI_ITEMID_NOT_FOUND;
  800. }
  801. }
  802. } else if (GuidIndex == FilterOfflineDiagnosticClass) {
  803. switch(MethodId)
  804. {
  805. case RunTest:
  806. {
  807. status = FilterOfflineRunTest(devExt,
  808. Buffer,
  809. InBufferSize,
  810. OutBufferSize,
  811. &sizeNeeded);
  812. break;
  813. }
  814. case DiscontinueTest:
  815. {
  816. PDISCONTINUE_TEST_OUT discTestOut;
  817. HANDLE keyHandle;
  818. UNICODE_STRING valueName;
  819. sizeNeeded = sizeof(DISCONTINUE_TEST_OUT);
  820. if (OutBufferSize >= sizeNeeded)
  821. {
  822. //
  823. // Right here we are asked to discontinue execution
  824. // of the offline test. All we need to do is make
  825. // sure that the registry value is deleted
  826. //
  827. status = IoOpenDeviceRegistryKey(devExt->physicalDevObj,
  828. PLUGPLAY_REGKEY_DEVICE,
  829. KEY_READ,
  830. &keyHandle);
  831. if (NT_SUCCESS(status))
  832. {
  833. //
  834. // We just read from this value blindly,
  835. // but we need to be careful as this key is
  836. // shared by all drivers in the stack so
  837. // there is a possibility of collision in
  838. // case the FDO or PDO might also want to
  839. // use something unique to this driver to store
  840. // diagnostic info
  841. //
  842. RtlInitUnicodeString(&valueName, L"OfflineSetting");
  843. FilterZwDeleteValueKey(keyHandle,
  844. &valueName);
  845. ZwClose(keyHandle);
  846. }
  847. discTestOut = (PDISCONTINUE_TEST_OUT)Buffer;
  848. discTestOut->Result = RunDiscontinueTestOk;
  849. discTestOut->TestingStopped = TRUE;
  850. status = STATUS_SUCCESS;
  851. } else {
  852. status = STATUS_BUFFER_TOO_SMALL;
  853. }
  854. break;
  855. }
  856. default:
  857. {
  858. status = STATUS_WMI_ITEMID_NOT_FOUND;
  859. }
  860. }
  861. } else {
  862. status = STATUS_WMI_GUID_NOT_FOUND;
  863. }
  864. status = WmiCompleteRequest(
  865. DeviceObject,
  866. Irp,
  867. status,
  868. sizeNeeded,
  869. IO_NO_INCREMENT);
  870. return(status);
  871. }
  872. NTSTATUS
  873. FilterFunctionControl(
  874. IN PDEVICE_OBJECT DeviceObject,
  875. IN PIRP Irp,
  876. IN ULONG GuidIndex,
  877. IN WMIENABLEDISABLECONTROL Function,
  878. IN BOOLEAN Enable
  879. )
  880. /*++
  881. Routine Description:
  882. This routine is a callback into the driver to enabled or disable event
  883. generation or data block collection. A device should only expect a
  884. single enable when the first event or data consumer enables events or
  885. data collection and a single disable when the last event or data
  886. consumer disables events or data collection. Data blocks will only
  887. receive collection enable/disable if they were registered as requiring
  888. it. If the driver can complete enabling/disabling within the callback it
  889. should call WmiCompleteRequest to complete the irp before returning to
  890. the caller. Or the driver can return STATUS_PENDING if the irp cannot be
  891. completed immediately and must then call WmiCompleteRequest once the
  892. data is changed.
  893. Arguments:
  894. DeviceObject is the device object
  895. GuidIndex is the index into the list of guids provided when the
  896. device registered
  897. Function specifies which functionality is being enabled or disabled
  898. Enable is TRUE then the function is being enabled else disabled
  899. Return Value:
  900. status
  901. --*/
  902. {
  903. NTSTATUS status;
  904. PAGED_CODE();
  905. switch(GuidIndex)
  906. {
  907. case FilterDiagnosticClass:
  908. {
  909. if (Enable)
  910. {
  911. //
  912. // A consumer has just indicated interest in accessing
  913. // information about the FilterDiagnosticClass, most
  914. // likely it will want to query the class and execute
  915. // methods in it. If there is anything that needs to be
  916. // done such as setting up hardware, enabling counters,
  917. // etc before the class is queried or executed then
  918. // this is the place to do it. Note that only one
  919. // enable will be sent regardless of the number of
  920. // consumers who want to access the class.
  921. //
  922. status = STATUS_SUCCESS;
  923. } else {
  924. //
  925. // The last consumer has just indicated that it is no
  926. // longer interested in this class and so the class
  927. // will no longer be queried or its methods executed.
  928. // If there is anything that needs to be done such as
  929. // resetting hardware or stopping counters, etc then it
  930. // should be done here. Note that only one disable will
  931. // be sent regardless of the number of consumers who
  932. // previous used the class.
  933. }
  934. break;
  935. }
  936. default:
  937. {
  938. status = STATUS_WMI_GUID_NOT_FOUND;
  939. break;
  940. }
  941. }
  942. status = WmiCompleteRequest(
  943. DeviceObject,
  944. Irp,
  945. STATUS_SUCCESS,
  946. 0,
  947. IO_NO_INCREMENT);
  948. return(status);
  949. }
  950. NTSTATUS FilterPerformOfflineDiags(
  951. struct DEVICE_EXTENSION *devExt
  952. )
  953. {
  954. UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
  955. MAXEXECUTIONIDSIZE + sizeof(MSSample_DiagnosticSetting)];
  956. PKEY_VALUE_PARTIAL_INFORMATION keyValuePartialInfo;
  957. ULONG infoSize;
  958. NTSTATUS status;
  959. HANDLE keyHandle;
  960. PMSSample_DiagnosticSetting diagSetting;
  961. UNICODE_STRING valueName;
  962. USHORT executionIDSize;
  963. PAGED_CODE();
  964. //
  965. // If registry has stuff then run test, else return
  966. //
  967. status = IoOpenDeviceRegistryKey(devExt->physicalDevObj,
  968. PLUGPLAY_REGKEY_DEVICE,
  969. KEY_READ,
  970. &keyHandle);
  971. if (NT_SUCCESS(status))
  972. {
  973. //
  974. // We just read from this value blindly,
  975. // but we need to be careful as this key is
  976. // shared by all drivers in the stack so
  977. // there is a possibility of collision in
  978. // case the FDO or PDO might also want to
  979. // use something unique to this driver to store
  980. // diagnostic info
  981. //
  982. RtlInitUnicodeString(&valueName, L"OfflineSetting");
  983. keyValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
  984. infoSize = sizeof(buffer);
  985. status = ZwQueryValueKey(keyHandle,
  986. &valueName,
  987. KeyValuePartialInformation,
  988. keyValuePartialInfo,
  989. infoSize,
  990. &infoSize);
  991. if ((NT_SUCCESS(status)) &&
  992. (keyValuePartialInfo->Type == REG_BINARY) &&
  993. (keyValuePartialInfo->DataLength != 0))
  994. {
  995. //
  996. // We successfully read the diagnostics settings for the
  997. // offline test. First thing we do is delete the value so
  998. // that in case the the diagnostic test causes a problem
  999. // then it won't be run in the next time the device starts
  1000. // up
  1001. //
  1002. FilterZwDeleteValueKey(keyHandle,
  1003. &valueName);
  1004. //
  1005. // Here is where we run our offline test. Remember the
  1006. // Execution ID tag as we'll need to give that back to
  1007. // the CDM provider
  1008. //
  1009. devExt->OfflineTestResult = TestResultPassSad;
  1010. executionIDSize = *((PUSHORT)(keyValuePartialInfo->Data)) + sizeof(USHORT);
  1011. RtlCopyMemory(&devExt->ExecutionID,
  1012. keyValuePartialInfo->Data,
  1013. executionIDSize);
  1014. }
  1015. ZwClose(keyHandle);
  1016. }
  1017. return(status);
  1018. }
  1019. NTSTATUS FilterZwDeleteValueKey(
  1020. HANDLE KeyHandle,
  1021. PUNICODE_STRING ValueName
  1022. )
  1023. {
  1024. NTSTATUS status;
  1025. //
  1026. // Since we do not have ZwDeleteValueKey as a proper WDM function
  1027. // then we try to make one up. What we do is to set the value to an
  1028. // empty REG_BINARY
  1029. //
  1030. status = ZwSetValueKey(KeyHandle,
  1031. ValueName,
  1032. 0,
  1033. REG_BINARY,
  1034. NULL,
  1035. 0);
  1036. return(status);
  1037. }
  1038.