Chapter 5 describes device callback objects UMDF drivers create a device callback object that partners with a framework device object. The device callback object implements callback interfaces that the framework uses to notify the driver of Plug and Play and file-related events.
The driver callback object's IDriverEntry::OnDeviceAdd method creates and initializes the device object. The framework calls this method with the following two parameters:
A pointer to the IWDFDriver interface on the driver object.
A pointer to the IWDFDeviceInitialize interface on the driver object.
The IWDFDriver interface defines the CreateDevice method, which the driver calls to create the framework device object. The IWDFDeviceInitialize interface includes several methods that the driver can call to initialize the device object.
The OnDeviceAdd method:
Creates the device callback object.
Initializes and creates the framework device object.
Creates the I/O queues that are associated with the device object.
In a function driver, OnDeviceAdd should also create and enable a device interface so that clients can send I/O to the device.
Note In the Fx2_Driver sample, OnDeviceAdd calls several public methods on the device callback object to perform these tasks. Specifically:
CMyDevice::CreateInstance creates the device callback object.
CMyDevice::Initialize initializes and creates the framework device object.
CMyDevice::Configure creates the I/O queues and the device interface.
You can find the source code in the Device.cpp file. Rather than duplicating all of those methods, this chapter includes code excerpts that show how to accomplish each task.
To create the callback object, a UMDF driver simply uses the new operator, as in the following example:
PCMyDevice device; device = new CMyDevice(); if (NULL == device) { return E_OUTOFMEMORY; }
If creation fails, the driver returns an out-of-memory error.
The IWDFDeviceInitialize interface implements methods that initialize device properties and other settings and return information about the device. Table 6-4 displays these methods.
Method | Description |
---|---|
AutoForwardCreateCleanupClose | Indicates whether the framework should forward create, cleanup, and close requests to the default I/O target if the driver does not have a callback for such requests. By default, the framework forwards the requests for filter drivers but does not forward them for function drivers. See Chapter 8, "I/O Flow and Dispatch." |
GetPnpCapability | Returns WdfTrue or WdfFalse to indicate whether the device supports one of the following Plug and Play features:
|
RetrieveDeviceInstanceId | Returns a string that represents the instance ID for the device. The instance ID identifies this specific instance of the device. A driver can use the instance ID in calls to SetupDiXxx functions to retrieve additional device properties. |
RetrieveDevicePropertyStore | Returns a device property store through which the driver can read and write the registry. See Chapter 12, "Support Objects." |
SetFilter | Indicates that the device object represents a filter driver. The framework passes down the device stack any I/O requests that the driver does not filter. |
SetLockingConstraint | Sets the synchronization model for the device object's callback methods. The default value is WdfDeviceLevel. See Chapter 10, "Synchronization." |
SetPnpCapability | Enables, disables, or selects the default setting for a particular Plug and Play feature of the device, as listed in GetPnpCapability. |
SetPowerPolicyOwnership | Notifies the framework that the device object owns power policy for the device. The default value is FALSE. See Chapter 7, "Plug and Play and Power Management." |
A driver calls one or more of these methods to set device characteristics before it creates the device object. A filter driver must call SetFilter, and the power policy owner must call SetPowerPolicyOwnership. Otherwise, none of the settings are required.
After the driver has called IWDFDeviceInitialize methods to set the device characteristics, it calls IWDFDriver::CreateDevice to create the framework's device object. This method takes the following three parameters:
A pointer to the IWDFDeviceInitialize interface that was passed to the driver.
A pointer to the IUnknown interface of the driver's device callback object.
A location in which to return the pointer to the IWDFDevice interface on the framework device object.
Chapter 5 explains the lifetime of the device object If the framework successfully creates the framework device object, it releases the reference that the CreateDevice method added on the returned interface. This reference is not required to ensure that the object persists.
The code fragment in Listing 6-3 shows how the Fx2_Driver sample initializes and creates a framework device object.
Listing 6-3: UMDF framework device object creation
FxDeviceInit->SetLockingConstraint(WdfDeviceLevel); { IUnknown *unknown = device->QueryIUnknown(); hr = FxDriver->CreateDevice(FxDeviceInit, unknown, &fxDevice); unknown->Release(); } if (S_OK == hr) { fxDevice->Release(); }
The Fx2_Driver sample sets the locking model-also called synchronization scope-for the driver by calling the SetLockingConstraint method of the IWDFDeviceInitialize interface. The locking model determines whether the framework acquires a lock before it calls certain file and I/O event callbacks. The WdfDeviceLevel value means that the framework acquires a lock for the device object, so that none of those callbacks run concurrently for any of the queues or files that are associated with the device object. The locking model does not apply to Plug and Play, power, and I/O completion callbacks. Synchronization is perhaps the most difficult part of driver implementation, so you should be sure you understand the implications of the locking model that you choose.
Chapter 10, "Synchronization," describes the locking constraint and synchronization scope.
After the driver has set the device characteristics, it calls IWDFDriver::CreateDevice to create the framework device object.
To create an instance of a device interface class for its device, a UMDF driver calls IWDFDevice::CreateDeviceInterface. If creation succeeds, the framework automatically enables the interface when the device enters the working state and disables it when the device leaves the working state. Listing 6-4 shows how the Fx2_Driver registers its device interface class.
Listing 6-4: UMDF: creating a device interface
hr = m_FxDevice->CreateDeviceInterface ( &GUID_DEVINTERFACE_OSRUSBFX2, NULL);
As Listing 6-4 shows, the Fx2_Driver sample calls CreateDeviceInterface to create the interface. It supplies its interface class GUID, which is defined in the WUDFOsrUsbPublic.h header file, and a NULL reference string.
The framework automatically enables the interface for the driver.
A driver can disable the device interface to indicate that the device no longer accepts requests. To disable the device interface, the driver calls IWDFDevice::AssignDeviceInterfaceState and passes FALSE for the Boolean parameter. In practice, drivers rarely disable an interface. The framework automatically disables the interface for the driver when the device is removed.