5.1.6 SetTimer()

This service programs a timer event to be signaled in the future. The time is specified in 100 nanosecond (ns) units. UEFI supports both periodic timer events and one-shot timer events. Use these timer events when polling for I/O completions, detecting hot plug events, detecting timeout conditions for I/O operations, supporting asynchronous I/O operations, etc.

Caution: The units used for timer events may appear to have better accuracy than the Stall() service, which has an accuracy of 1 μs, but that may not be the case. UEFI uses a single timer interrupt to determine when to signal timer events. The resolution of timer events is dependent on the frequency of the timer interrupt.

UEFI system firmware uses a hardware timer interrupt to measure time. These. These time measurements are used to determine when enough time has passed to signal a timer event programmed with SetTimer(). In most systems, the timer interrupt is generated every 10 ms to 50 ms, but the UEFI Specification does not require any specific interrupt rate. This lack of specificity means that a periodic timer programmed with a period much smaller than 10 ms may only be signaled every 10 ms to 50 ms. If short delays much smaller than 10 ms are required, use the Stall() service.


TIP: Timer event services are not accurate over short delays. If a short, accurate delay, is required then the Stall() service should be used.


The code fragment in Example 53 shows how to create a timer event and program it as a periodic timer with a period of 100 ms. When the created event is signaled every 100 ms, the notification function TimerHandler() is called at TPL_NOTIFY with the EXAMPLE_DEVICE context that was registered when the event was created. The EDK II library UefiLib provides macros for the timer periods used with the SetTimer() services.

These macros include EFI_TIMER_PERIOD_MICROSECONDS(), EFI_TIMER_PERIOD_MILLISECONDS(), and EFI_TIMER_PERIOD_SECONDS()

The Private Context Structure a UEFI Driver uses to store device specific information usually contains EFI_EVENT fields for the events the UEFI Driver creates. This allows a UEFI Driver to close events when a device is stopped or when a UEFI Driver is unloaded. In this example, the Private Context Structure called EXAMPLE_DEVICE contains an EFI_EVENT for both a periodic and a one-shot timer. The Private Context Structure is also typically passed in as the Context parameter when an event is created. This provides the event notification function with the device specific context required to perform the device specific actions.

Caution: Always close timer events with the UEFI Boot Service CloseEvent() whenever a device is stopped or a UEFI Driver is unloaded. If not performed, a call for an event notification no longer present in memory, or event notification function for a device no longer available, may cause unexpected failures.

Example 53-Create periodic timer event
#include <Uefi.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiLib.h>

typedef struct {
  UINTN Signature;
  EFI_EVENT PeriodicTimer;
  EFI_EVENT OneShotTimer;
  //
  // Other device specific fields
  //
} EXAMPLE_DEVICE;

VOID
TimerHandler (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  //
  // Perform a UEFI driver-specific operation.
  //
}

EFI_STATUS Status;
EXAMPLE_DEVICE *Device;
Status = gBS->CreateEvent (
                EVT_TIMER | EVT_NOTIFY_SIGNAL,  // Type
                TPL_NOTIFY,                     // NotifyTpl
                TimerHandler,                   // NotifyFunction
                Device,                         // NotifyContext
                &Device->PeriodicTimer          // Event
                );
if (EFI_ERROR (Status)) {
  return Status;
}

//
// Program the timer event to be signaled every 100 ms.
//
Status = gBS->SetTimer (
                Device->PeriodicTimer,
                TimerPeriodic,
                EFI_TIMER_PERIOD_MILLISECONDS (100)
                );
if (EFI_ERROR (Status)) {
  return Status;
}

The following code fragment shows how to create a one-shot timer event that is signaled 4 seconds in the future. When the created event is signaled, the notification function TimerHandler() is called at TPL_CALLBACK with the EXAMPLE_DEVICE context that was registered when the event was created.

Example 54-Create one-shot timer event
#include <Uefi.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiLib.h>

typedef struct {
  UINTN Signature;
  EFI_EVENT PeriodicTimer;
  EFI_EVENT OneShotTimer;
  //
  // Other device specific fields
  //
} EXAMPLE_DEVICE;

VOID
TimerHandler (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  //
  // Perform a UEFI driver-specific operation.
  //
}

EFI_STATUS Status;
EXAMPLE_DEVICE *Device;
Status = gBS->CreateEvent (
                EVT_TIMER | EVT_NOTIFY_SIGNAL,  // Type
                TPL_CALLBACK,                   // NotifyTpl
                TimerHandler,                   // NotifyFunction
                Device,                         // NotifyContext
                &Device->OneShotTimer           // Event
                );
if (EFI_ERROR (Status)) {
  return Status;
}

//
// Program the timer event to be signaled 4 seconds from now.
//
Status = gBS->SetTimer (
                Device->OneShotTimer,
                TimerRelative,
                EFI_TIMER_PERIOD_SECONDS (4)
                );
if (EFI_ERROR (Status)) {
  return Status;
}

The code fragment below shows how to cancel and close the one-shot timer created in Example 54 above. If the UEFI Driver completes an I/O operation normally, any timer events used to detect timeout conditions must be canceled. If the timeout condition is only used as part of device detection, the timer event may not be required again. In those cases, the event can be both canceled and closed.

Example 55-Cancel and close one-shot timer event
#include <Uefi.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiLib.h>

typedef struct {
  UINTN Signature;
  EFI_EVENT PeriodicTimer;
  EFI_EVENT OneShotTimer;
  //
  // Other device specific fields
  //
} EXAMPLE_DEVICE;

EFI_STATUS Status;
EXAMPLE_DEVICE *Device;

//
// Cancel the one-shot timer event.
//
Status = gBS->SetTimer (Device->OneShotTimer, TimerCancel, 0);
if (EFI_ERROR (Status)) {
  return Status;
}

//
// Close the one-shot timer event.
//
Status = gBS->CloseEvent (Device->OneShotTimer);
if (EFI_ERROR (Status)) {
  return Status;
}