Driver Object


Every driver has a driver object, which supports a callback for the add-device event. A driver object can store global data for the driver and can support a callback to clean up that data when the driver is unloaded.

UMDF Driver Callback Object Creation

For a UMDF driver, the framework creates a framework driver object and then calls the driver's IClassFactory::CreateInstance method on the driver callback object's class factory, which creates a driver callback object. The driver callback object implements the IDriverEntry interface, which supports the methods in Table 6-1.

Table 6-1: IDriverEntry Interface
Open table as spreadsheet

Method

Description

OnInitialize

Initializes driver-wide data for the driver callback object and performs any other initialization that cannot be done in DllMain. This method is called before OnDeviceAdd.

OnDeviceAdd

Creates and initializes a device callback object.

OnDeinitialize

Releases any resources that were allocated by OnInitialize. It is often a minimal implementation.

In the Fx2_Driver sample, the Driver.cpp file contains code that implements OnDeviceAdd. OnInitialize and OnDeinitialize are minimal implementations that are defined and declared in Driver.h.

In the Fx2_Driver and other UMDF samples, IClassFactory::CreateInstance calls the CMyDriver::CreateInstance method, which creates and initializes the driver callback object. Although IClassFactory::CreateInstance could create the driver class object directly, using a method that is defined for the driver class enables the Comsup.cpp source file to remain generic so that any driver can use it. In addition, all of the driver-class-related methods can appear in the same source file.

CMyDriver::CreateInstance is defined in the Driver.cpp source file and is straightforward, as Listing 6-1 shows.

Listing 6-1: UMDF driver callback object creation

image from book
 HRESULT CMyDriver::CreateInstance(     __out PCMyDriver *Driver     ) {     PCMyDriver driver;     HRESULT hr;     // Allocate the callback object.     driver = new CMyDriver();     if (NULL == driver) {         return E_OUTOFMEMORY;     }     // Initialize the callback object.     hr = driver->Initialize();     if (SUCCEEDED(hr)) {     // Return a pointer to the new, initialized object         *Driver = driver;     }     else  {         // Release the reference on the driver object.         driver->Release();     }     return hr; } 
image from book

IClassFactory::CreateInstance calls CMyDriver::CreateInstance, which is shown in the listing. CMyDriver::CreateInstance uses the new operator to create an instance of the driver callback object, and then calls the Initialize method to initialize the object. The Fx2_Driver object requires no initialization, so the Initialize method is a token implementation, not shown here. CMyDriver::CreateInstance returns a pointer to the new driver callback object and releases its reference on this object before returning.

After CMyDriver::CreateInstance returns, IClassFactory::CreateInstance calls QueryInterface to obtain a pointer to the driver callback object's IDriverEntry interface and returns that pointer to the framework. This code is not shown.

image from book
On the UMDF Sample Programming Pattern

When I first proposed the sample programming pattern for UMDF, folks thought it was a little "heavy." Why call the Initialize method if it's not going to do anything, or call a Configure method that won't do anything?

My goal was for the samples to serve as a template for starting a new driver. To that end I decided that the patterns for creating callback objects and hooking framework objects together should be consistent throughout the samples. The order for setting up an object is:

  1. The function that creates the object calls the CMyXxx::CreateInstance standard factory method. This handles memory allocation, invokes the constructor, and allocates any additional data or objects. Mostly it makes sure that you don't forget to call Initialize.

  2. The constructor initializes all the object's fields to a known value, but does not allocate any memory. The constructor is usually private so that callers are required to use CreateInstance.

  3. The Initialize method takes care of any deeper initialization, including setting up framework properties, creating the IWDFXxx partner object, setting this as the callback object, and so on.

  4. The Configure method, typically called after CreateInstance and Initialize, sets up the child objects. For example, CMyDevice::Configure in the Fx2_driver sample creates all of the device's queues. Configure is separate from Initialize so that the code that creates the object can do anything necessary before it creates children.
    -Peter Wieland, Windows Driver Foundation Team, Microsoft

image from book

KMDF Driver Object Creation

A KMDF driver creates its driver object in the DriverEntry function, which is the first driver function called when the driver is loaded. DriverEntry is called only once. The DriverEntry function:

  • Creates a driver object (that is, WDFDRIVER), which represents the loaded instance of the driver in memory.

    In effect, creating this object "registers" the driver with the framework.

  • Registers the driver's EvtDriverDeviceAdd callback.

    The framework calls this function during device enumeration.

  • Optionally initializes event tracing for the driver.

  • Optionally allocates resources that are required on a driver-wide basis, rather than per device.

  • Optionally registers an EvtDriverUnload callback, if the driver requires a callback before unloading.

DriverEntry should return STATUS_SUCCESS to indicate that it successfully created a driver object and performed any other initialization that the driver requires. If DriverEntry returns a failure status, the framework deletes the driver object-if it was created successfully-and thus does not call the EvtDriverUnload callback. Listing 6-2 is based on the DriverEntry function for the Osrusbfx2 sample, which appears in the Driver.c file.

Listing 6-2: KMDF sample DriverEntry function

image from book
 NTSTATUS DriverEntry(     IN PDRIVER_OBJECT  DriverObject,     IN PUNICODE_STRING RegistryPath     ) {     WDF_DRIVER_CONFIG       config;     NTSTATUS                status;     WDF_OBJECT_ATTRIBUTES   attributes;     // Initialize the driver configuration structure.     WDF_DRIVER_CONFIG_INIT (&config, OsrFxEvtDeviceAdd);     WDF_OBJECT_ATTRIBUTES_INIT(&attributes);     attributes.EvtCleanupCallback = OsrFxEvtDriverContextCleanup;     // Create a framework driver object.     status = WdfDriverCreate (DriverObject, RegistryPath,             &attributes, // Driver Object Attributes             &config,     // Driver Config Info             WDF_NO_HANDLE // hDriver             );     if (!NT_SUCCESS(status)) {         return status;     }     // Initialize WPP Tracing.     WPP_INIT_TRACING( DriverObject, RegistryPath );     TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT,        "OSRUSBFX2 Driver Sample - Driver Framework Edition.\n");     }     return status; } 
image from book

As the example shows, the DriverEntry function has two parameters: a pointer to the underlying WDM driver object, and a pointer to a registry path. If you are familiar with WDM drivers, you have probably noticed that these are the same parameters as a WDM DriverEntry function. In fact, until the DriverEntry function calls WdfDriverCreate, the driver is in effect a WDM driver.

The first task for DriverEntry is to call WDF_DRIVER_CONFIG_INIT to initialize the driver object configuration structure with a pointer to the driver's EvtDriverDeviceAdd callback.

Next, the sample DriverEntry function registers a cleanup callback for the driver object by setting the EvtCleanupCallback field of the WDF_OBJECT_ATTRIBUTES structure. The framework invokes this callback immediately before it deletes the driver object. The EvtCleanupCallback function should perform any driver object cleanup tasks such as freeing resources or, in this case, ending tracing.

After initializing the configuration and attributes structures, the driver calls WdfDriverCreate to create the framework driver object. WdfDriverCreate takes the following as parameters:

  • The pointer to the WDM driver object that was passed to DriverEntry.

  • The pointer to the registry path that was passed to DriverEntry.

  • A pointer to the attributes structure.

  • A pointer to the configuration structure.

  • An optional location to receive a handle to the created WDFDRIVER object, or WDF_NO_HANDLE (defined as NULL) if the driver does not require this handle.

    Most drivers do not retain the driver object handle. It is rarely used and a driver can always call the WdfGetDriver method to get it.

If WdfDriverCreate fails to create a driver object, the DriverEntry function exits, returning status to the framework.

If WdfDriverCreate succeeds, the driver initializes tracing by calling WPP_INIT_TRACING and logs a trace message.

Chapter 11, "Driver Tracing and Diagnosability," provides information on tracing.

The driver initializes tracing after it creates the driver object so that the driver code calls WPP_CLEANUP to end tracing only once, in the EvtCleanupCallback for the driver object. If your driver performs other driver-wide initialization tasks, that code should appear after the driver initializes tracing, so that it can log any errors that occur.

Finally, DriverEntry returns an NTSTATUS value. If DriverEntry fails after WdfDriverCreate successfully creates a driver object, the framework deletes the driver object and calls its EvtCleanupCallback.

Tip 

Chapter 24, "Static Driver Verifier," describes how to annotate your driver's callback functions so that SDV can analyze compliance with KMDF rules that require that a KMDF driver call WdfDriverCreate from within its DriverEntry function.




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