WMI Support in a KMDF Driver


WMI provides a way for drivers to export information to other components. Drivers typically use WMI to:

  • Enable user-mode applications to query for device-related information, such as performance data.

  • Enable an appropriately privileged administrator to control a device by running an application on a remote system.

KMDF supports WMI for Plug and Play device objects though callback functions and WMI-specific object types. The rest of this section describes the support for WMI that is built into KMDF.

About WMI

A KMDF driver that supports WMI registers as a WMI information provider and registers one or more instances of that information. Each WMI provider is associated with a particular GUID. Another component registers with the same GUID to consume the data from the WMI provider's instances. User-mode components request WMI instance data by calling COM functions, which the system translates into IRP_MJ_SYSTEM_CONTROL requests and sends to the target providers.

KMDF supports WMI requests through its WMI request handler, which provides the following features for drivers:

  • A default WMI implementation.

    Drivers that do not provide WMI data are not required to register as WMI data providers; KMDF handles all IRP_MJ_SYSTEM_CONTROL requests.

  • Callbacks on individual instances, rather than just at the device object level, so that different instances can behave differently.

  • Validation of buffer sizes to ensure that the buffers that are used in WMI queries meet the size requirements of the associated provider and instance.

The default WMI implementation includes support for the check boxes on the Power Management tab of Device Manager. These check boxes enable a user to control whether the device can wake the system and whether the system can power down the device when it is idle. If the driver enables this feature in its power policy options, KMDF handles these requests automatically.

When KMDF receives an IRP_MJ_SYSTEM_CONTROL request that is targeted at a KMDF driver, it proceeds as follows:

  • If the driver has registered as a WMI provider and registered one or more instances, the WMI handler invokes the callbacks for those instances as appropriate.

  • If the driver has not registered any WMI instances, the WMI handler responds to the request by providing the requested data if it can, passing the request to the next lower driver, or failing the request.

WMI instance objects have a context area, just as other WDF objects do. A driver can use the context area of a WMI instance object as a source of read-only data, thus enabling easy data collection with minimal effort. A driver can delete WMI instance objects any time after their creation.

WMI callbacks are not synchronized with the Plug and Play and power management state of the device. Therefore, when WMI events occur, KMDF calls a driver's WMI callbacks even if the device is not in the working state.

Requirements for WMI Support

A KMDF driver supports WMI by doing one or more of the following:

  • Initializing its WMI support by registering as a WMI data provider and creating one or more WMI instance objects to represent the data blocks it can read or write.

  • Optionally implementing one or more event callback functions to supply the WMI data that the driver provides.

  • Optionally firing WMI events.

In its WMI callbacks, the driver can call WMI methods on the device object to create and manipulate WMI instances or to change its status as a WMI provider. After the WMI callbacks have returned, the framework completes or forwards the request, as appropriate, on the driver's behalf.

 Tip  See "Windows Management Instrumentation" in the WDK for information about the kernel-mode capabilities of WMI-online at http://go.microsoft.com/fwlink/?LinkId=81581. See also "Supporting WMI in Framework-based Drivers" for additional details about WMI support in KMDF-online at http://go.microsoft.com/fwlink/?LinkId=82325.

How to Initialize WMI Support

To initialize its WMI support, a KMDF driver follows these steps, typically within its EvtDriverDeviceAdd or EvtDeviceSelfManagedIoInit callback:

  1. Registering the name of the driver's managed object format (MOF) resource.

  2. Initializing a WMI provider configuration structure and creating a WMI provider object.

  3. Initializing the WMI instance configuration structure and creating a WMI instance.

MOF Resource

The MOF resource defines WMI data and event blocks and is included in the driver's resource script (.rc) file. A KMDF driver registers the name of the MOF resource by calling the WdfDeviceAssignMofResourceName method.

WMI Provider Object

After the driver registers its MOF resource, it can initialize the WMI provider configuration structure (WDF_WMI_PROVIDER_CONFIG) with the following information:

  • The provider's GUID.

  • One or more flags that indicate whether the provider implements instance-specific callbacks, whether its data collection procedures are performance intensive, and whether the provider supports WMI event tracing.

  • The buffer size for the provider instances.

  • A pointer to the driver's EvtWmiProviderFunctionControl callback, if the driver implements this callback.

In the WDF_WMI_PROVIDER_CONFIG structure, the driver can specify the size of the buffer that is required for the provider's EvtWmiInstanceQueryInstance and EvtWmiInstanceSetInstance callbacks. If the driver specifies such a value, KMDF validates the buffer size when the WMI request arrives and calls the callbacks only if the supplied buffer is large enough. If the instance size is either dynamic or unavailable when the provider is created, the driver cannot configure a buffer size. Instead, the driver should specify zero for this field and the callbacks themselves should validate the buffer sizes.

The EvtWmiProviderFunctionControl callback enables and disables the collecting of WMI data for the driver and is optional. Drivers that collect large amounts of data for a particular block should implement this function and set the WdfWmiProviderExpensive flag in the Flags field of the WDF_WMI_PROVIDER_CONFIG structure.

After the driver has filled in the configuration structure, it can create the WMI provider object. If the driver requires only one WMI provider, it is not necessary to call a creation method. To simplify implementation for such drivers, KMDF creates a WMI provider by default when the driver creates its first WMI instance. Therefore, the driver should call WdfWmiProviderCreate only if it requires more than one WMI provider.

WMI Instance Objects

Next, the driver creates WMI instance objects for the provider. The driver fills in the configuration structure for each WMI instance. This structure, of type WDF_WMI_INSTANCE_CONFIG, contains the following information:

  • A handle to the WMI provider and a pointer to the provider's configuration structure.

  • A Boolean value in the UseContextForQueryField that is TRUE if the driver simply stores read-only queried data in the WMI instance object's context area.

    By default, this value is FALSE, which indicates that the driver instead supplies an event callback function for instance queries.

  • A Boolean value in the Register field that is TRUE if KMDF should register the driver's provider instance with WMI.

    By default, the value is FALSE, which indicates that the driver itself will call WdfWmiInstanceRegister.

  • If the driver implements them, pointers to the EvtWmiInstanceQueryInstance, EvtWmiInstanceSetInstance, EvtWmiInstanceSetItem, and EvtWmiInstanceExecuteMethod event callback functions.

    No callbacks are required if the driver has only a single WMI instance that provides readonly, fixed-length data from its object context area.

If the driver already has a handle to the provider object as a result of calling WdfWmiProviderCreate, it can use the WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER function to record the provider's handle and configuration information in the instance configuration structure. If the driver does not have the handle, it should instead use the WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG function, which records information about the provider that KMDF created on behalf of the driver.

When the configuration structure is filled appropriately for the instance, the driver calls WdfWmiInstanceCreate to create the instance.

Example: Sample Code to Initialize WMI Support

The Wmi.c module of the Featured Toaster driver contains code that implements WMI data collection. The Featured Toaster driver includes this support primarily for demonstration purposes.

The WMI registration code appears in ToasterWmiRegistration, a support function that is called by the ToasterEvtDeviceAdd callback. Listing 12-14 shows the source code for this function.

Listing 12-14: Initializing WMI support

image from book
 NTSTATUS ToasterWmiRegistration(     WDFDEVICE      Device     ) {     NTSTATUS           status;     PFDO_DATA          fdoData;     PToasterDeviceInformation     pData;     PToasterControl    controlData;     WDFWMIINSTANCE     instance;     WDF_OBJECT_ATTRIBUTES         woa;     WDF_WMI_PROVIDER_CONFIG       providerConfig;     WDF_WMI_INSTANCE_CONFIG       instanceConfig;     DECLARE_CONST_UNICODE_STRING(mofRsrcName, MOFRESOURCENAME);     PAGED_CODE();     fdoData = ToasterFdoGetData(Device);     status = WdfDeviceAssignMofResourceName(Device, &mofRsrcName);     if (!NT_SUCCESS(status)) {         return status;     }     WDF_WMI_PROVIDER_CONFIG_INIT(&providerConfig,          &ToasterDeviceInformation_GUID);     providerConfig.MinInstanceBufferSize = 0;     WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG(&instanceConfig,             &providerConfig);     instanceConfig.Register = TRUE;     instanceConfig.EvtWmiInstanceQueryInstance =             EvtWmiInstanceStdDeviceDataQueryInstance;     instanceConfig.EvtWmiInstanceSetInstance =             EvtWmiInstanceStdDeviceDataSetInstance;     instanceConfig.EvtWmiInstanceSetItem =             EvtWmiInstanceStdDeviceDataSetItem;     WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&woa,             ToasterDeviceInformation);     status = WdfWmiInstanceCreate (Device, &instanceConfig, &woa,             &instance);     if (!NT_SUCCESS(status)) {         return status;     }     pData = ToasterWmiGetData(instance);     pData->ConnectorType = TOASTER_WMI_STD_USB;     pData->Capacity = 2000;     pData->ErrorCount = 0;     pData->Controls = 5;     pData->DebugPrintLevel = DebugLevel;     WDF_WMI_PROVIDER_CONFIG_INIT(&providerConfig,            &TOASTER_NOTIFY_DEVICE_ARRIVAL_EVENT);     providerConfig.Flags = WdfWmiProviderEventOnly;     providerConfig.MinInstanceBufferSize = 0;     WDF_WMI_INSTANCE_CONFIG_INIT_PROVIDER_CONFIG(&instanceConfig,             &providerConfig);     instanceConfig.Register = TRUE;     status = WdfWmiInstanceCreate (Device, &instanceConfig,              WDF_NO_OBJECT_ATTRIBUTES,              &fdoData->WmiDeviceArrivalEvent     );     if (!NT_SUCCESS(status)) {         return status;     }     . . . //Additional code omitted     return status; } 
image from book

The sample assigns a name to its MOF resource by calling WdfDeviceAssignMofResource-Name, passing as parameters the handle to the device object and a pointer to the name. The name is defined as "ToasterWMI" in the Toaster.h header file, which is in the Toaster\Func\Shared folder.

Next, the driver initializes a WDF_WMI_PROVIDER_CONFIG structure so that it can register as a WMI data provider. It calls the WDF_WMI_PROVIDER_CONFIG_INIT function, passing a pointer to the configuration structure and a pointer to the GUID to associate with its WMI data blocks. It then sets a minimum buffer size in the provider configuration structure. Specifying zero for the buffer size indicates that the driver's callback functions validate the buffer size, instead of the framework. The driver accepts the default flag settings and does not specify an EvtWmiProviderFunctionControl callback.

Now the driver configures the first of two WMI instances. It calls the WDF_WMI_ INSTANCE_CONFIG_INIT_PROVIDER_CONFIG function to create a WMI instance configuration structure that is associated with the provider configuration structure. To indicate that KMDF should register the driver as a provider in addition to creating an instance, it sets the Register field to TRUE. The driver registers callbacks for three WMI events in the instance configuration structure: EvtWmiInstanceQueryInstance, EvtWmiInstanceSetInstance, and EvtWmiInstanceSetItem.

The sample driver uses the instance object's context area to store some WMI data. The data block includes a variable length string, so the driver must implement the EvtWmiInstanceQueryInstance callback to return the data. The driver calls the WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE macro to set the type of the context area to ToasterDeviceInformation.

After the driver initializes the configuration structure and sets attributes for the WMI instance object, it calls WdfWmiInstanceCreate, passing as parameters the handle to the device object and pointers to both the configuration and attribute structures. This method creates the WMI instance and returns a handle to the instance at &instance.

Next, the driver calls the ToasterWmiGetData accessor function to get a pointer to the object context area and then stores driver-specific values in the context area.

The first WMI instance has now been created and initialized. Next, the driver creates a second WMI instance and provider to use for firing events. It reinitializes the provider configuration structure and, this time, passes a pointer to the GUID for the device arrival event and sets the WdfWmiProviderEventOnly flag. This flag indicates that clients of this instance can register to receive events, but they cannot query or set instance data. Again, the driver sets the buffer size and then configures and creates the instance by making the same sequence of calls that it made to create the first instance.

WMI Instance Event Callbacks

KMDF drivers can implement callbacks for the following WMI instance events:

  • EvtWmiInstanceQueryInstance, which returns data from a specified WMI instance.

  • EvtWmiInstanceSetInstance, which writes data into a specified WMI instance.

  • EvtWmiInstanceSetItem, which writes the value of a single data item within a WMI instance.

  • EvtWmiInstanceExecuteMethod, which executes a function that the caller passed to it and optionally returns output data. Such methods are typically designed to gather data dynamically.

KMDF invokes these callbacks in response to requests from external WMI users that specify the WMI provider's GUID.

The Featured Toaster sample implements the EvtWmiInstanceQueryInstance, EvtWmiInstance-SetInstance, and EvtWmiInstanceSetItem callbacks. Much of the code in these callbacks involves data manipulation that is not specific to KMDF.

Example: Code to Query a WMI Instance

The EvtWmiInstanceQueryInstance event callback returns data from a WMI instance. Listing 12-15 shows the Toaster driver's implementation.

Listing 12-15: Sample EvtWmiInstanceQueryInstance callback

image from book
 NTSTATUS EvtWmiInstanceStdDeviceDataQueryInstance(     IN  WDFWMIINSTANCE WmiInstance,     IN  ULONG OutBufferSize,     IN  PVOID OutBuffer,     OUT PULONG BufferUsed     ) {     PUCHAR          pBuf;     ULONG           size;     UNICODE_STRING  string;     NTSTATUS        status;     PAGED_CODE();     string.Buffer = L"Aishwarya\0\0";     string.Length =         (USHORT) (wcslen(string.Buffer) + 1) * sizeof(WCHAR);     string.MaximumLength = string.Length + sizeof(UNICODE_NULL);     size = ToasterDeviceInformation_SIZE + string.Length +            sizeof(USHORT);     *BufferUsed = size;     if (OutBufferSize < size) {         return STATUS_BUFFER_TOO_SMALL;     }     pBuf = (PUCHAR) OutBuffer;     // Copy the structure information.     RtlCopyMemory(pBuf,                   ToasterWmiGetData(WmiInstance),                   ToasterDeviceInformation_SIZE);     pBuf += ToasterDeviceInformation_SIZE;     // Copy the string. Put length of string ahead of string.     status = WDF_WMI_BUFFER_APPEND_STRING(              pBuf,              size - ToasterDeviceInformation_SIZE,              &string,              &size);     return status; } 
image from book

The parameters to the callback are a handle to the WMI instance, a pointer to the length of the output buffer in which the driver will write the data, a pointer to the buffer itself, and a pointer to a location where the driver reports how many bytes it wrote.

In this sample, the Featured Toaster driver returns the data from the WMI instance object's context area along with an invented string to demonstrate how to handle variable-length data. The driver writes invented data into a string, calculates the number of bytes that are required to hold the data, and ensures that it will fit into the provided output buffer. If the output buffer is too small, the function returns an error. Otherwise, it sets up an internal buffer pointer that maps to the output buffer and uses RtlCopyMemory to copy the contents of the WMI instance object's context area and its length into the buffer.

Finally, the driver calls the WDF_WMI_BUFFER_APPEND_STRING function to append the string to the buffer and adjust the length correctly. This function ensures that the data block and its contents are aligned correctly.

Example: Code to Set a WMI Instance

The EvtWmiInstanceSetInstance event callback updates data in a WMI instance. Listing 12-16 shows the source code for the Featured Toaster's implementation of this function.

Listing 12-16: Sample EvtWmiInstanceSetInstance callback

image from book
 NTSTATUS EvtWmiInstanceStdDeviceDataSetInstance(     IN  WDFWMIINSTANCE WmiInstance,     IN  ULONG InBufferSize,     IN  PVOID InBuffer     ) {     if (InBufferSize < ToasterDeviceInformation_SIZE) {         return STATUS_BUFFER_TOO_SMALL;     }     // Update only writable elements.     DebugLevel =         ToasterWmiGetData(WmiInstance)>DebugPrintLevel =             ((PToasterDeviceInformation)InBuffer)>DebugPrintLevel;     return STATUS_SUCCESS; } 
image from book

This callback is called with a handle to the WMI instance, a pointer to the length of the input buffer from which the driver reads the data, and a pointer to the buffer itself.

This callback updates the data in the context area. Its first task is to ensure that the input buffer is large enough for the ToasterDeviceInformation structure. If the buffer is too small, the function returns an error. Otherwise, it updates the structure with new data from the input buffer by assigning the value in the input buffer to the correct location in the context area. Only the DebugPrintLevel field is writable, so the driver updates only this value. Other drivers might instead update the entire block or select several writable fields for update. The function returns a success status.

Example: Code to Set a WMI Data Item

The EvtWmiInstanceSetItem callback updates a particular item in the WMI data block for the device. Listing 12-17 shows the source code from the Featured Toaster driver.

Listing 12-17: Sample EvtWmiInstanceSetItem callback

image from book
 NTSTATUS EvtWmiInstanceStdDeviceDataSetItem(     IN  WDFWMIINSTANCE WmiInstance,     IN  ULONG DataItemId,     IN  ULONG InBufferSize,     IN  PVOID InBuffer     ) {     if (DataItemId ==         ToasterDeviceInformation_DebugPrintLevel_ID) {             if (InBufferSize < sizeof(ULONG)) {                 return STATUS_BUFFER_TOO_SMALL;         }         DebugLevel =             ToasterWmiGetData(WmiInstance)->DebugPrintLevel =                 *((PULONG)InBuffer);         return STATUS_SUCCESS;     }     else {         return STATUS_WMI_READ_ONLY;     } } 
image from book

This callback is called with a handle to the WMI instance, a value that identifies the data item to update, a pointer to the length of the input buffer from which the driver reads the new data, and a pointer to the input buffer itself.

In this sample, the only item a caller can update is the DebugPrintLevel field, so the driver ensures that the specified DataItemId matches the ID for this field. If the IDs match, it verifies the length of the buffer just as it did in the EvtWmiInstanceSetInstance callback. If the buffer is too small, the driver returns an error.

The Featured Toaster driver then updates the data in the same way that it did the instance, and the function returns a success status.




Developing Drivers with the Microsoft Windows Driver Foundation
Developing Drivers with the Windows Driver Foundation (Pro Developer)
ISBN: 0735623740
EAN: 2147483647
Year: 2007
Pages: 224

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net