ENTERPRISE

Windows 7 : Programming Multiple I/O Queues and Programming I/O - WatchDog Timer: Self-Managed I/O

7/17/2014 4:42:37 AM

Some drivers perform I/O activities that are not related to queued I/O requests or must be synchronized with activities of WDM drivers in the same device stack. For example, a driver might maintain a timer that monitors the status of its device. Similarly, a driver might be required to communicate with its device or another driver at a particular point during the device’s start-up or shut-down sequence. KMDF 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 other WDF Plug and Play and power management callbacks.

To use self-managed I/O, a driver implements the self-managed I/O event callbacks. KMDF calls these callbacks during 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 following are the self-managed I/O callbacks:

  • EvtDeviceSelfManagedIoInit

  • EvtDeviceSelfManagedIoSuspend

  • EvtDeviceSelfManagedIoFlush

  • EvtDeviceSelfManagedIoCleanup

  • EvtDEviceSelfManagedIoRestart

1. Self-Managed I/O Device Startup and Restart

When the system is booted or the user plugs in the device, KMDF calls the driver’s EvtDeviceSelfManagedIoInit callback after the driver’s EvtDeviceDOEntry function has returned but before KMDF completes the underlying Plug and Play or power IRP. KMDF calls this function only during the initial start-up sequence; it does not call this function when the device returns to the working state from a low-power state.

The EvtDeviceSelfManagedIoInit callback should perform whatever tasks are required to initiate the I/O that the framework doesn’t manage. For example, a driver that must monitor the state of its device might initialize and start a timer.

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, KMDF calls the EvtDeviceSelfManagedIoRestart callback.

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

EvtDeviceSelfManagedIoRestart is called only after EvtDeviceSelfManagedIoSuspend has previously been called. This function is called only 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.

2. Self-Managed I/O During Device Power-Down and Removal

When the device is powered down or removed, KMDF 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—KMDF calls the EvtDeviceSelfManagedIoSuspend 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 EvtDeviceDOExit. During surprise removal, it is called before EvtDeviceSurpriseRemoval if the device was in a low-power state and afterward if the device was in the DO state.

If the device is being removed, KMDF calls EvtDeviceSelfManagedIoFlush after the device has been stopped. 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 EvtDEviceSelfManagedIoSuspend and EvtDeviceDOExit functions have returned.

Finally, KMDF calls EvtDeviceSelfManagedIoCleanup after device removal is complete. This function should ensure that all self-managed I/O has stopped completely and should release any resources that EvtDEviceSelfManagedIoInit allocated for self-managed I/O. The clean-up function is called only once.

3. Implementing a Watchdog Timer

The PCIDRV sample uses self-managed I/O to implement a watchdog timer, which is used during hardware link detection 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 clean-up 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 EvtDEviceDOEntry and EvtDeviceDOExit, among others). The driver sets these in the EvtDriverDeviceAdd callback, before it creates the WDFDEVICE object.

3.1. Code to Set Self-Managed I/O Callbacks

The PCIDRV sample registers these callbacks in the PciDrvEvtDeviceAdd function in pcidrv.c:

WDF_PNPPOWER_EVENT_CALLBACKS  pnpPowerCallbacks;

//
// Initialize the PnpPowerCallbacks structure.
//
WDF_PNPPOWER_EVENT_CALLBACK_INIT (&pnpPowerCallbacks);

//
// Set entry points for self-managed I/O callbacks.
//
pnpPowerCallbacks.EvtDeviceSElfManagedIoInit =
PciDrvEvtDEvicesSelfManagedIoInit;
pnpPowerCallbacks.EvtDeviceSelfManagedIoCleanup =
PciDrvEvtDeviceSelfManagedIoCleanup;
pnpPowerCallbacks.EvtDeviceSelfManagedIoSuspend =
PciDrvEvtDeviceSelfManagedIoSuspend;
pnpPowerCallbacks.EvtDeviceSelfManageIoRestart =
PciDrvEvtDeviceSelfManagedIoRestart;
//
// Register the PnP and power callbacks.
//
WdfDeviceInitSetPnpPowerEventCallbacks (DeviceInit,
&pnpPowerCallbacks);

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

3.2. Code to Create and Initialize the Timer

The PCIDRV sample creates, initializes, and starts the watchdog timer in its EvtDeviceSelfManagedIoInit callback. The watchdog timer is a WDF time object (WDFTIMER). When the timer expires, KMDF queues a DPC, which calls the driver’s EvtTimerFunc.

The following code is the sample’s EvtDeviceSelfManagedIoInit callback, which appears in the pcidrv.c source file:

NTSTATUS
PciDrvEvtDeviceSelfManagedIoInit (
IN WDFDEVICE Device
)
{
PFDO_DATA fdoData = NULL;
WDF_TIMER_CONFIG wdfTimerconfig;
NTSTATUS status;
WDF_OBJECT_ATTRIBUTES timerAttributes;

PAGED_CODE ();

TraceEvents (TRACE_LEVEL_INFORMATION, DBG_PNP,
"--> PciDrvEvtDeviceSelfManagedIoInit\n");

fdoData = FdoGetData (Device);

//
// To minimize init-time, create a timer DPC to do link
// detection. The DPC will also be used 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))
{
TraceEvents (TRACE_LEVEL_ERROR, DBG_PNP,
"Error: WdfTimerCreate create failed 0x%x\n",
Status);
return status;
}

NICStartWatchDogTimer (fdoData);

TraceEvents (TRACE_LEVEL_INFORMATION, DBG_PNP,
"<-PciDrvEvtDeviceSelfManagedIoInit\n");

return status;
}


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 attribute’s structure by using the WDF_OBJECT_ATTRIBUTES_INIT function. By default, a timer object has no parent, so the driver sets the ParentObject field to the device object (WdfDevice) 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 attribute’s structure, and a location to receive the handle to the timer. If KMDF successfully creates the timer, PCIDRV calls the internal function NICStartWatchDogTimer to start the timer.

3.3. Code to Start the Timer

The NICStartWatchDogTimer function appears in the sys/HW/isrdpc.c source file, as follows:

VOID
NICStartWatchDogTimer (
IN PFDO_DATA FdoData
)
{
LARGE_INTEGER dueTime;

if (!FdoData->CheckForHang)
{
//
// Set the link detection flag to indicate that
// NICWatchDogEvtTimerFunc
// is first doing link detection.
//
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;
}


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 shown in the preceding example.

3.4. Code to 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 following code from pcidrv.c shows:

NTSTATUS
PciDrvEvtDeviceSelfManagedIoSuspend (
IN WDFDEVICE Device
)
{
PFDO_DATA fdoData = NULL;

PAGED_CODE ();

fdoData = FdoGetData (Device);

//
// Stop the watchdog timer and wait for DPC to run
// to completion
// if it has already fired.
//
WdfTimerStop (fdoData->WatchDogTimer, TRUE);
return STATUS_SUCCESS;
}

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 driver has any DPCs in the DPC queue (including the NICWatchDogEvtTimerFunc timer DPC function), KMDF 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 the driver’s DPCs to complete, so whether the timer was already set is not important.

3.5. Code to Restart the Timer

When the device returns to the working state after being in low-power state, KMDF calls the EvtDeviceSelfManagedIoRestart callback. In the PCIDRV driver, this callback restarts the timer as follows, from the pcidrv.c source file:

NTSTATUS
PciDrvEvtDeviceSelfManagedIoRestart (
IN WDFDEVICE Device
)
{
PFDO_DATA fdoData;

PAGED_CODE ();

fdoData = FdoGetData (Device);

//
// Restart the watchdog timer.
//
NICStartWatchDogTimer (fdoData);
return STATUS_SUCCESS;
}

Restarting the timer simply requires a call to the internal NICStartWatchDogTimer, as previously discussed. Because the device object and the timer (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 recreate the timer object.

3.6. Code to Delete the Timer

When the device is removed, the driver deletes the timer in its EvtDeviceSelfManagedIoCleanup function, as follows:

VOID
PciDrvEvtDeviceSelfManagedIoCleanup (
IN WDFDEVICE Device
)
{
PFDO_DATA fdoData = NULL;

PAGED_CODE ();

fdoData = FdoGetData (Device);

if (fdoData->WatchDogTimer)
{
WdfObjectDelete (fdoData->WatchDogTimer);
}
return;
}

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.

Other  
  •  Windows 7 : Programming Multiple I/O Queues and Programming I/O - Reading and Writing the Registry
  •  Windows 7 : Programming Multiple I/O Queues and Programming I/O - Retrieving Requests from a Manual Queue
  •  Windows 7 : Programming Multiple I/O Queues and Programming I/O - Handling Requests from a Parallel Queue
  •  Windows 7 : Programming Multiple I/O Queues and Programming I/O - Creating and Configuring the Queues (part 2)
  •  Windows 7 : Programming Multiple I/O Queues and Programming I/O - Creating and Configuring the Queues (part 1)
  •  Microsoft Exchange Server 2010 : Managing Mailbox and public Folder Databases (part 2) - Moving Databases
  •  Microsoft Exchange Server 2010 : Managing Mailbox and public Folder Databases (part 1) - Mounting and Dismounting Databases
  •  Microsoft Exchange Server 2010 : Using Public Folder Databases (part 4) - Configuring Public Folder Referrals, Recovering Deleted Items from Public Folder Databases
  •  Microsoft Exchange Server 2010 : Using Public Folder Databases (part 3) - Configuring Public Folder Replication
  •  Microsoft Exchange Server 2010 : Using Public Folder Databases (part 2) - Setting Public Folder Database Limits
  •  
    Top 10
    Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
    Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
    3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
    3 Tips for Maintaining Your Cell Phone Battery (part 1) - Charge Smart
    OPEL MERIVA : Making a grand entrance
    FORD MONDEO 2.0 ECOBOOST : Modern Mondeo
    BMW 650i COUPE : Sexy retooling of BMW's 6-series
    BMW 120d; M135i - Finely tuned
    PHP Tutorials : Storing Images in MySQL with PHP (part 2) - Creating the HTML, Inserting the Image into MySQL
    PHP Tutorials : Storing Images in MySQL with PHP (part 1) - Why store binary files in MySQL using PHP?
    REVIEW
    - First look: Apple Watch

    - 3 Tips for Maintaining Your Cell Phone Battery (part 1)

    - 3 Tips for Maintaining Your Cell Phone Battery (part 2)
    VIDEO TUTORIAL
    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

    - How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
    Popular Tags
    Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8 BlackBerry Android Ipad Iphone iOS