Книга: Writing Windows WDM Device Drivers

Timers

Timers

Two different types of timer can be used. A basic timer is called once every second; WdmIo uses this to detect device time-outs. Custom timers may be set up with resolutions starting from 100ns.

One-Second Interval Timers

The kernel provides easy access to a device timer that calls you back every second. The tinier must be initialized at PASSIVE_LEVEL IRQL using IoInitializeTimer. WdmIo calls IoInitializeTimer as follows in its AddDevice routine just after the FDO has been created. The final parameter to IoInitializeTimer is passed to the timer callback.

status = IoInitializeTimer(fdo, (PIO_TIMER_ROUTINE)Timeout1s, dx);
if (!NT_SUCCESS(status)) {
 IoDeleteDevice(fdo);
 return status;
}

Use IoStartTimer to start timer calls and IoStopTimer to stop them. Do not call IoStopTimer from within your timer routine. The first timer call may occur after less than one second.

The timer routine is called at DISPATCH_LEVEL. You will usually need to use a Critical Section routine if you wish to coordinate your activities with interrupt handling routines.

WdmIo Time-Outs

Some drivers start their timer calls when a device is created and stop them when it is removed. WdmIo reduces the number of timer callbacks. It starts the timer whenever an interrupt driven I/O starts. When the transfer is completed, it sets a StopTimer flag to indicate that its timer should be stopped. The next call to WdmIoStartIo checks this flag and calls IoStopTimer, if necessary. The timer is also stopped when the device is removed.

Listing 17.5 shows the WdmIo timer callback, Timeout1s, and the Critical Section routine that it uses, called Timeout1sSynch.

The device extension Timeout field serves two purposes. If –1, it indicates that no interrupt-driven transfer is in progress. The WdmIo code in DeviceIo.sss checks this value in several places to ensure that a read or write is indeed in progress. If Timeout is zero or more, it indicates the number of seconds left before the current transfer times out. The first timer callback may occur after less than one second, so the code that starts reads and writes adds one to the given time-out to be on the safe side.

Timeout1s, therefore, first checks whether there is a transfer in progress. If not (i.e., if Timeout is –1), it returns immediately. If the IRP Cancel flag is set, it calls the DPC routine directly. Otherwise, it calls Timeout1sSynch as a Critical Section routine. Timeout1sSynch checks and decrements Timeout. If Timeout has reached zero, the read or write must be stopped. Timeout is set to –1, the IRP return status is set appropriately and TRUE is returned.

If Timeout1sSynch returns TRUE, Timeout1s calls the DPC routine directly. This call to WdmIoDpcForIsr uses NULL as the Dpc parameter. This fact could be used in DPC processing, if necessary. For example, for IRP cancelling and time-outs, there is no need to provide a priority boost for the IRP.

Listing 17.5 WdmIo time-out routines

VOID Timeout1s(IN PDEVICE_OBJECT fdo, IN PWDMIO_DEVICE_EXTENSION dx) {
 if (dx->Timeout==-1) return;
 DebugPrint("Timeout1s: Timeout is %d" ,dx->Timeout);
 PIRP Irp = fdo->CurrentIrp;
 if (Irp->Cancel ||
    KeSynchronizeExecution(dx->InterruptObject, (PKSYNCHRONIZE_ROUTINE)Timeout1sSynch, dx))
  WdmIoDpcForIsr(NULL, fdo, fdo->CurrentIrp, dx);
}
static BOOLEAN Timeout1sSync(IN PWDMIO_DEVICE_EXTENSION dx) {
 if (dx->Timeout==-1 || –dx->Timeout>0) return FALSE;
 dx->Timeout = –2;
 dx->TxStatus = STATUS_NO_MEDIA_IN_DEVICE; // Win32: ERROR_NOT_READY
 return TRUE;
}

Custom Timers

Custom timers may be used if you want timer resolutions other than one second. You can detect when the timer goes off in two ways. Either use a Custom DPC callback, or wait for the timer object to become signalled.

NT 3.51 timers are one-shot only. NT 4, W2000, and WDM drivers can use periodic timers. Declare a KTIMER field in nonpaged memory (e.g., in your device extension), and initialize it with KeInitializeTimer or KeInitializeTimerEx. To start a one-shot timer, call KeSetTimer. If the DueTime LARGE_INTEGER parameter is positive, it represents an absolute time. If it is negative, it is a relative time in units of 100ns.

Use KeSetTimerEx if you want to specify a periodic timer. The DueTime parameter is used to specify the first time-out. The Period parameter specifies the period in milliseconds for subsequent time-outs.

You can cancel a timer using KeCancelTimer, and use KeReadStateTimer to find out if the timer has gone off.

A custom DPC routine may used as the timer callback. Initialize a custom DPC as described previously. Pass the KDPC pointer to KeSetTimer or KeSetTimerEx.

System threads can wait for a timer to go off using the KeWaitForSingleObject and KeWaitForMultipleObjects calls. KeInitializeTimerEx lets you specify whether a timer is a NotificationTimer or a SynchronizationTimer. A SynchronizationTimer timer releases only one waiting thread, while a NotificationTimer timer releases all waiting threads.

Оглавление книги


Генерация: 0.057. Запросов К БД/Cache: 0 / 0
поделиться
Вверх Вниз