Self-Managed IO


Self-Managed I/O

Although the I/O and Plug and Play support that is built into WDF is recommended for most drivers, some drivers have I/O operations that are not related to queued I/O requests, are not subject to power management, or must be synchronized with the activities of WDM drivers in the same device stack. For example, the Pcidrv sample uses self-managed I/O callbacks to start and stop a watchdog timer. Similarly, a driver might be required to communicate with its device or another driver at a particular point during the device's startup or shutdown sequence. WDF provides self-managed I/O to accommodate such requirements.

The self-managed I/O callbacks correspond more closely to the underlying WDM Plug and Play and power management IRPs than do the other WDF Plug and Play and power management callbacks.

image from book
Self-Managed I/O and the Power State Machine

Self-managed I/O is the "catch all" in the power state machine. All of the other callbacks have a very clear contract and tell you what you should do in them. Self-managed I/O is everything else that doesn't fit into the well-defined contracts.
-Doron Holan, Windows Driver Foundation Team, Microsoft

image from book

To use self-managed I/O, a driver implements the self-managed I/O event callbacks. The frameworks call these callbacks during device Plug and Play and power state transitions when the device is added to or removed from the system, when the device is stopped to rebalance resources, when the idle device transitions to a low-power state, and when the device returns to the working state from a low-power idle state.

The self-managed I/O callbacks correspond directly to Plug and Play and power management state changes. These functions are called with a handle to the device object and no other parameters. If a driver registers these callbacks, the framework calls them at the designated times so that the driver can perform whatever actions it requires.

Chapter 7, "Plug and Play and Power Management," describes the precise order in which the frameworks call these functions.

  • UMDF drivers use self-managed I/O by implementing the IPnpCallbackSelfManagedIo interface on the device callback object.

  • KMDF drivers implement EvtDeviceSelfManagedIoXxx methods and register these methods by calling WdfDeviceInitSetPnpPowerEventCallbacks before creating the device object.

Table 8-16 lists the self-managed I/O methods for both UMDF and KMDF and indicates when each is called.

Table 8-16: Self-managed I/O Methods
Open table as spreadsheet

UMDF method in the IPnpCallbackSelfManagedIo interface

KMDF callback method

When called

OnSelfManagedIoCleanup

EvtDeviceSelfManagedIoCleanup

Immediately before the device object is deleted.

OnSelfManagedIoFlush

EvtDeviceSelfManagedIoFlush

During processing of a Plug and Play IRP_MN_REMOVE_DEVICE or IRP_MN_SURPRISE_REMOVAL request, after power-managed queues have been purged. This function should fail any I/O requests that the driver did not complete before the device was removed.

OnSelfManagedIoInit

EvtDeviceSelfManagedIoInit

During device startup, after the driver's D0 entry callback function has returned but before WDF completes the IRP. It is called only during the initial startup sequence, not when the device returns to the working state from a low-power state.

OnSelfManagedIoRestart

EvtDeviceSelfManagedIoRestart

When the device returns from a low- power state to the working state. It is called only if WDF previously called the driver's self-managed I/O suspend method.

OnSelfManagedIoStop

None

Not currently called.

OnSelfManagedIoSuspend

EvtDeviceSelfManagedIoSuspend

Before WDF calls any of the other Plug and Play or power callbacks in the shutdown sequence, whenever one of the following is true:

  • The device is about to enter a low- power state.

  • The device is being removed or was surprise-removed.

  • The PnP manager is preparing to redistribute the system's hardware resources among the system's devices.

Self-Managed I/O during Device Startup and Restart

When the system is booted or the user plugs in the device, WDF calls the driver's self-managed I/O initialization callback (that is, IPnpCallbackSelfmanagedIo:: OnSelfManagedIoInit or EvtDeviceSelfManagedIoInit) after the driver's D0 entry callback has returned but before WDF completes the underlying Plug and Play or power IRP. The self- managed I/O initialization functions are called only during the initial startup sequence, and not when the device returns to the working state from a low-power state.

The self-managed I/O initialization callback should perform any required tasks to initiate I/O that the framework does not manage. For example, a driver that must monitor the state of its device might initialize and start a timer. A driver might also perform one-time initialization that requires power, such as enumerating static children based on the hardware revision.

When the device returns to the working state from a low-power state, such as occurs when the device has been idle or has been stopped to rebalance resources, WDF calls the self-managed I/O restart callback.

Like the self-managed I/O initialization callback, the restart callback is the last one that is called after the device returns to the working state, but before WDF completes the IRP that triggered the transition to the working state. The restart callback should resume any I/O activities that the self-managed I/O initialization callback initialized and that were later suspended when the device exited from the working state. Typically, this means that it reverses the actions of the self-managed I/O suspend callback.

The restart callback is called when the device returns to operation only if the suspend callback was called when the device stopped operation. Restart is called when the device has been in a low-power state or its resources have been rebalanced; it is not called when the user initially plugs in the device.

Self-Managed I/O during Device Power-Down and Removal

When the device is powered down or removed, WDF calls one or more of the self-managed I/O callbacks so that the driver can stop and clean up after its self-managed I/O operations.

Every time the device goes through the power-down sequence-whether because it is idle, it is being removed, or system resources are being rebalanced-WDF calls the self-managed I/O suspend callback. This function should stop any self-managed I/O activities that are in progress and must be handled while the device is present. During rebalance, power-down, and orderly removal, it is called while the device is still operational, before the device exits from the working state. During surprise removal, it is called before the driver's surprise- removal notification callback if the device was in a low-power state and afterwards if the device was in the D0 state.

If the device is being removed, WDF calls the self-managed I/O flush callback after the device has been stopped and is no longer in D0. This function should fail any I/O requests that the driver did not complete before the device was removed. It is called after the driver's self- managed I/O suspend callback and D0 exit functions have returned.

Finally, WDF calls the self-managed I/O cleanup callback after device removal is complete and the object is being deleted. This function should ensure that all self-managed I/O has stopped completely and should release any resources that the self-managed I/O initialization callback allocated for self-managed I/O. The cleanup function is called only once.

 KMDF   For a PDO, the framework does not call the EvtDeviceSelfManagedIoCleanup callback until the PDO itself is deleted when either the device has been unplugged or its parent has been deleted. If the framework does not delete the PDO and the device later restarts, the framework calls EvtDeviceSelfManagedIoRestart without an intervening call to EvtDeviceSelfManagedIoCleanup.

Chapter 7, "Plug and Play and Power Management," explains the detailed sequence of callbacks that are involved in device power-down and removal.

KMDF Example: Implementing a Watchdog Timer

The Pcidrv sample uses self-managed I/O to implement a watchdog timer, which is used during hardware link detection and to check for hangs. Implementing the watchdog timer involves the following driver tasks:

  • Setting callbacks for the self-managed I/O events.

  • Initializing the timer in EvtDeviceSelfManagedIoInit.

  • Stopping the timer in EvtDeviceSelfManagedIoSuspend.

  • Restarting the timer in EvtDeviceSelfManagedIoRestart.

  • Deleting the timer and resources in EvtDeviceSelfManagedIoCleanup.

The Pcidrv sample does not implement the EvtDeviceSelfManagedIoFlush callback because no I/O requests are involved in its self-managed I/O. The suspend and cleanup callbacks are sufficient.

A driver registers its self-managed I/O callbacks by setting their entry points in the WDF_PNP_POWER_CALLBACKS structure along with the other Plug and Play and power event callbacks, such as EvtDeviceD0Entry and EvtDeviceD0Exit, among others. The driver sets these in the EvtDriverDeviceAdd callback, before it creates the WDFDEVICE object.

Example: Set Self-Managed I/O Callbacks

The Pcidrv sample registers these callbacks in the PciDrvEvtDeviceAdd function in Pcidrv.c as Listing 8-18 shows.

Listing 8-18: Registering self-managed I/O callbacks in a KMDF driver

image from book
 WDF_PNPPOWER_EVENT_CALLBACKS    pnpPowerCallbacks; // Initialize the PnpPowerCallbacks structure. WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); //Set entry points for self-managed I/O callbacks. pnpPowerCallbacks.EvtDeviceSelfManagedIoInit =        PciDrvEvtDeviceSelfManagedIoInit; pnpPowerCallbacks.EvtDeviceSelfManagedIoCleanup =        PciDrvEvtDeviceSelfManagedIoCleanup; pnpPowerCallbacks.EvtDeviceSelfManagedIoSuspend =        PciDrvEvtDeviceSelfManagedIoSuspend; pnpPowerCallbacks.EvtDeviceSelfManagedIoRestart =        PciDrvEvtDeviceSelfManagedIoRestart; // Register the PnP and power callbacks. WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); 
image from book

As the example shows, the Pcidrv sample sets callbacks for EvtDeviceSelfManagedIoInit, EvtDeviceSelfManagedIoCleanup, EvtDeviceSelfManagedIoSuspend, and EvtDeviceSelfManagedIoRestart.

Example: Create and Initialize the Timer

 KMDF  The Pcidrv sample creates, initializes, and starts the watchdog timer in its EvtDeviceSelfManagedIoInit callback. The watchdog timer is a WDF timer object. When the timer expires, KMDF queues a DPC, which calls the driver's EvtTimerFunc.

Listing 8-19 shows the sample's EvtDeviceSelfManagedIoInit callback, which appears in the Pcidrv.c source file.

Listing 8-19: Initializing self-managed I/O in a KMDF driver

image from book
 NTSTATUS PciDrvEvtDeviceSelfManagedIoInit(     IN WDFDEVICE Device     ) {     PFDO_DATA           fdoData = NULL;     WDF_TIMER_CONFIG    wdfTimerConfig;     NTSTATUS            status;     WDF_OBJECT_ATTRIBUTES timerAttributes;     PAGED_CODE();     fdoData = FdoGetData(Device);     // Create a timer DPC to do link detection and to check for hardware hang.     WDF_TIMER_CONFIG_INIT(&wdfTimerConfig, NICWatchDogEvtTimerFunc);     WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);     timerAttributes.ParentObject = fdoData->WdfDevice;     status = WdfTimerCreate(&wdfTimerConfig,                             &timerAttributes,                             &fdoData->WatchDogTimer                             );     if(!NT_SUCCESS(status)) {        return status;     }     NICStartWatchDogTimer(fdoData);     return status; } 
image from book

The driver declares two structures for use in creating the timer: a WDF_TIMER_CONFIG structure named wdfTimerConfig and a WDF_OBJECT_ATTRIBUTES structure named timerAttributes. To initialize the wdfTimerConfig structure, the driver uses the WDF_TIMER_CONFIG_INIT function, passing as parameters the wdfTimerConfig structure and a pointer to NICWatchdogEvtTimerFunc, which is the driver's EvtTimerFunc callback.

Next, the driver initializes the attributes structure by using the WDF_OBJECT_ATTRIBUTES_INIT function. The driver sets the ParentObject field to the device object so that KMDF deletes the timer when it deletes the device object.

Finally, the driver calls WdfTimerCreate to create the timer, passing as parameters the configuration structure, the attributes structure, and a location to receive the handle to the timer. If KMDF successfully creates the timer, Pcidrv calls the NICStartWatchDogTimer internal function to start the timer.

Example: Start the Timer

Listing 8-20 shows how the driver starts the timer. The NICStartWatchDogTimer function appears in the Sys\Hw\Isrdpc.c source file.

Listing 8-20: Starting a watchdog timer in a KMDF driver

image from book
 VOID NICStartWatchDogTimer(     IN PFDO_DATA FdoData     ) {     LARGE_INTEGER dueTime;     if(!FdoData->CheckForHang){         // Set the link detection flag.         MP_SET_FLAG(FdoData, fMP_ADAPTER_LINK_DETECTION);         FdoData->CheckForHang = FALSE;         FdoData->bLinkDetectionWait = FALSE;         FdoData->bLookForLink = FALSE;         dueTime.QuadPart = NIC_LINK_DETECTION_DELAY;     }     else {         dueTime.QuadPart = NIC_CHECK_FOR_HANG_DELAY;     }     WdfTimerStart(FdoData->WatchDogTimer, dueTime.QuadPart);     return; } 
image from book

This function sets the expiration time for the timer to a hardware-dependent value, depending on whether the driver is attempting to detect a link or check for a device hang. It then starts the timer by calling WdfTimerStart. When the timer expires, KMDF queues a DPC that invokes the driver's timer function, NICWatchDogEvtTimerFunc. The timer function performs the required task-link detection or check for hang-and then restarts the timer by calling WdfTimerStart in the same way as shown in Listing 8-19.

Example: Stop the Timer

When the device leaves the working state or is removed from the system, KMDF calls the EvtDeviceSelfManagedIoSuspend callback. In the Pcidrv sample, this callback stops the timer, as the code from Pcidrv.c in Listing 8-21 shows.

Listing 8-21: Suspending self-managed I/O in a KMDF driver

image from book
 NTSTATUS PciDrvEvtDeviceSelfManagedIoSuspend(     IN WDFDEVICE Device     ) {     PFDO_DATA fdoData = NULL;     PAGED_CODE();     fdoData = FdoGetData(Device);     // Stop the timer and wait for the DPC to run to completion.     WdfTimerStop(fdoData->WatchDogTimer, TRUE);     return STATUS_SUCCESS; } 
image from book

To stop the timer, the driver simply calls WdfTimerStop, passing as parameters the handle to the timer and a Boolean value. The Pcidrv sample passes TRUE to specify that if the DPC queue contains the NICWatchDogEvtTimerFunc timer DPC function or any other DPCs that this driver created, the framework should wait until all of those functions have returned before stopping the timer. Specifying FALSE means that KMDF should stop the timer immediately, without waiting for any DPCs to complete.

WdfTimerStop is defined as a Boolean function, which returns TRUE if the timer object was in the system's timer queue. However, the Pcidrv sample does not check the return value because it waits for all of the driver's DPCs to complete, so whether the timer was already set is not important.

Example: Restart the Timer

When the device returns to the working state after being in a low-power state, KMDF calls the EvtDeviceSelfManagedIoRestart callback. In the Pcidrv driver, this callback restarts the timer, as Listing 8-22 shows. This example is from the Pcidrv.c source file.

Listing 8-22: Restarting self-managed I/O in a KMDF driver

image from book
 NTSTATUS PciDrvEvtDeviceSelfManagedIoRestart(     IN WDFDEVICE Device     ) {     PFDO_DATA fdoData;     PAGED_CODE();     fdoData = FdoGetData(Device);     // Restart the watchdog timer.     NICStartWatchDogTimer(fdoData);     return STATUS_SUCCESS; } 
image from book

Restarting the timer simply requires a call to the internal NICStartWatchDogTimer function, as previously discussed. Because the device object and the timer, which is a child of the device object, were not deleted when the device transitioned out of the working state, the driver is not required to reinitialize or re-create the timer object.

Example: Delete the Timer

When the device is removed, the driver deletes the timer in its EvtDeviceSelfManagedIoCleanup function, as Listing 8-23 shows.

Listing 8-23: Self-managed I/O cleanup in a KMDF driver

image from book
 VOID PciDrvEvtDeviceSelfManagedIoCleanup(     IN WDFDEVICE Device     ) {     PFDO_DATA fdoData = NULL;     PAGED_CODE();     fdoData = FdoGetData(Device);     if(fdoData->WatchDogTimer) {         WdfObjectDelete(fdoData->WatchDogTimer);     }     return; } 
image from book

To delete the timer, the driver simply calls WdfObjectDelete, passing a handle to the timer object. If the driver had allocated any additional resources related to the timer, it would release those resources in this function. The call to WdfObjectDelete is not required, because the framework automatically deletes the timer object when its parent WDFDEVICE object is deleted.




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