一、Event的定义与概念
在UEFI中所有的异步操作都要通过事件(Event)来实现。事件通常用于通知某个事件的发生,例如设备插入、硬件错误、系统启动等等。UEFI事件可以被不同组件监听,并根据事件类型执行相应的操作。
启动服务为开发者提供了一些函数,用于操作事件、定时器以及TPL(任务优先级),这些函数可以分为三类:事件相关函数、定时器相关函数及TPL相关函数。
二、Event的简单使用
产生一个Event需要调用CreateEvent()函数来生成,其代码原型为:
/**
Creates an event.
@param[in] Type The type of event to create and its mode and attributes.
@param[in] NotifyTpl The task priority level of event notifications, if needed.
@param[in] NotifyFunction The pointer to the event's notification function, if any.
@param[in] NotifyContext The pointer to the notification function's context; corresponds to parameter
Context in the notification function.
@param[out] Event The pointer to the newly created event if the call succeeds; undefined
otherwise.
@retval EFI_SUCCESS The event structure was created.
@retval EFI_INVALID_PARAMETER One or more parameters are invalid.
@retval EFI_OUT_OF_RESOURCES The event could not be allocated.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_CREATE_EVENT)(
IN UINT32 Type, //要创建的事件类型及其模式和属性。
IN EFI_TPL NotifyTpl, //TPL, 事件的优先级
IN EFI_EVENT_NOTIFY NotifyFunction OPTIONAL, //指向事件通知函数的指针
IN VOID *NotifyContext OPTIONAL, //通知函数上下文的指针
OUT EFI_EVENT *Event //输出参数,指向新产生的事件
);
其中 事件的优先级分类为:
// Task priority level
//
#define TPL_APPLICATION 4
#define TPL_CALLBACK 8
#define TPL_NOTIFY 16
#define TPL_HIGH_LEVEL 31
使用SetTimer来设置定时器的属性,代码原型为
/**
Sets the type of timer and the trigger time for a timer event.
设置定时器的类型和定时器事件触发的时间
@retval EFI_SUCCESS The event has been set to be signaled at the requested time.
@retval EFI_INVALID_PARAMETER Event or Type is not valid.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_SET_TIMER)(
IN EFI_EVENT Event, //在指定时间发出信号的定时器事件
IN EFI_TIMER_DELAY Type, //在 TriggerTime 中指定的时间类型。
IN UINT64 TriggerTime //计时器到期前的 100ns 单位数。
);
【注】
TriggerTime 为 0 是合法的。
如果类型为 TimerRelative,且 TriggerTime 为 0,则定时器事件将在下一个定时器刻度时发出信号。
如果类型为 TimerPeriodic(定时器周期),且 TriggerTime 为 0,则定时器事件将在每个定时器刻度上发出信号。
其中定时器事件的触发类型有三种 :
/// Timer delay types 是一个枚举类型,定时器事件的出发类型
typedef enum {
/// 取消事件的计时器设置,不设置触发时间
TimerCancel,
/// 从当前时间开始,以指定的时间间隔定期发出信号。
TimerPeriodic,
/// 从当前时间开始,在指定的时间间隔内发出一次事件信号。
TimerRelative
} EFI_TIMER_DELAY;
WaitForEvent()等待事件组中的任意事件发生(是一个阻塞操作),其代码原型为
/**
Stops execution until an event is signaled.
停止执行,直到一个时间发生
@param[in] NumberOfEvents The number of events in the Event array.
@param[in] Event An array of EFI_EVENT.
@param[out] Index The pointer to the index of the event which satisfied the wait condition.
@retval EFI_SUCCESS The event indicated by Index was signaled.
@retval EFI_INVALID_PARAMETER 1) NumberOfEvents is 0.
2) The event indicated by Index is of type
EVT_NOTIFY_SIGNAL.
@retval EFI_UNSUPPORTED The current TPL is not TPL_APPLICATION.
**/
typedef
EFI_STATUS
(EFIAPI *EFI_WAIT_FOR_EVENT)(
IN UINTN NumberOfEvents, //事件组中事件的个数
IN EFI_EVENT *Event, //事件组
OUT UINTN *Index //输出参数,指向满足等待条件的事件索引的指针。
);
1、编写Event源文件MyHelloWorldEvent.c
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiRuntimeLib.h>
EFI_STATUS MyHelloWorldEventEntry(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINTN Index=0;
EFI_INPUT_KEY Key; //EFI_INPUT_KEY是一个数据结构,保存了按键的扫描码和Unicode字符
EFI_EVENT myEvents[2] = {0}; //创建一个EFI_EVENT数组,里面有两个元素
Print (L"[MyHelloWorldEvent] MyHelloWorldEventEntry Start..\n");
// 1. 生成事件
// 生成按键事件
myEvents[0] = gST->ConIn->WaitForKey;
// 生成Timer事件
Status = gBS->CreateEvent(EVT_TIMER , TPL_CALLBACK, (EFI_EVENT_NOTIFY)NULL, (VOID*)NULL, &myEvents[1]);
if(EFI_ERROR(Status)){
Print (L"[MyHelloWorldEvent] CreateEvent %r ...\n",Status);
return Status;
}
//设置定时器的属性
Status = gBS->SetTimer(myEvents[1],TimerPeriodic , 100 * 1000 * 1000);//设置10秒定时,以100ns为单位,共100*1000*1000个100ns,即10秒
if(EFI_ERROR(Status)){
Print (L"[MyHelloWorldEvent] SetTimer %r ...\n",Status);
return Status;
}
while (1){
//2. 阻塞并等待事件被触发
Status = gBS->WaitForEvent(2, myEvents, &Index);
if(EFI_ERROR(Status)){
Print (L"[MyHelloWorldEvent] WaitForEvent %r ...\n",Status);
return Status;
}
if (Index == 0){
// 读取按键键值并显示
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
switch (Key.ScanCode){
case SCAN_UP:
Print (L"[MyHelloWorldEvent] Key UP is Pressed..\n");
break;
case SCAN_DOWN:
Print (L"[MyHelloWorldEvent] Key Down is Pressed..\n");
break;
case SCAN_RIGHT:
Print (L"[MyHelloWorldEvent] Key RIGHT is Pressed..\n");
break;
case SCAN_LEFT:
Print (L"[MyHelloWorldEvent] Key LEFT is Pressed..\n");
break;
case SCAN_F1:
Print (L"[MyHelloWorldEvent] Key F1 is Pressed..\n");
break;
case SCAN_F2:
Print (L"[MyHelloWorldEvent] Key F2 is Pressed..\n");
break;
case SCAN_F3:
Print (L"[MyHelloWorldEvent] Key F3 is Pressed..\n");
break;
case SCAN_F4:
Print (L"[MyHelloWorldEvent] Key F4 is Pressed..\n");
break;
case SCAN_ESC:
Print (L"[MyHelloWorldEvent] Key ESC is Pressed..\n");
goto End;
default:
Print (L"[MyHelloWorldEvent] Key %a is Pressed..\n",(CHAR8 *)&Key.UnicodeChar);
break;
}
}else{
Print (L"[MyHelloWorldEvent] Timer event is trigered ..\n");
}
}
End:
Status = gBS->CloseEvent(myEvents[0]);
Status = gBS->CloseEvent(myEvents[1]);
Print (L"[MyHelloWorldEvent] MyHelloWorldEventEntry End..\n");
return Status;
}
2、编写UEFI应用的inf文件
3、编译UEFI应用,运行生成的efi文件,运行结果如下:
三、总结
先创建EFI_EVENT事件数组,然后生成Event,或使用CreateEvent创造Event,当用到定时器的时候需要对定时器进行设置。然后使用WaitForEvent阻塞等待事件发生(如果想不阻塞,可以调用其他函数实现),关闭事件(在本文的例子中未关闭)。