UEFI——Event(事件)简单使用

一、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阻塞等待事件发生(如果想不阻塞,可以调用其他函数实现),关闭事件(在本文的例子中未关闭)。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值