タイマーイベントを扱うことで、ある処理の中で時間経過を待ったり、イベント通知関数を使用することで時間経過後に非同期に処理を行わせたりすることができます。
この章ではそれぞれのやり方を紹介します。
EFI_BOOT_SERVICESのCreateEvent()とSetTimer()とWaitForEvent()で時間経過を待つことができます(リスト4.1)。なお、WaitForEvent()は前著(パート1)で紹介したので説明は省略します。
サンプルのディレクトリは"040_evt_timer_blocking"です。
リスト4.1: CreateEvent()とSetTimer()とWaitForEvent()の定義(efi.hより)
#define EVT_TIMER 0x80000000 #define EVT_RUNTIME 0x40000000 #define EVT_NOTIFY_WAIT 0x00000100 #define EVT_NOTIFY_SIGNAL 0x00000200 #define EVT_SIGNAL_EXIT_BOOT_SERVICES 0x00000201 #define EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 0x60000202 enum EFI_TIMER_DELAY { TimerCancel, /* 設定されているトリガー時間をキャンセル */ TimerPeriodic, /* 現時刻からの周期的なトリガー時間を設定 */ TimerRelative /* 現時刻から1回のみのトリガー時間を設定 */ }; struct EFI_SYSTEM_TABLE { ・・・ struct EFI_BOOT_SERVICES { ・・・ // // Event & Timer Services // /* 第1引数Typeで指定したイベントを生成し、 * 第5引数Eventへ格納する */ unsigned long long (*CreateEvent)( unsigned int Type, /* イベントタイプを指定 * 使用できる定数は上記の通り * この節ではEVT_TIMERを扱う */ unsigned long long NotifyTpl, /* イベントの通知関数実行時のタスク優先度レベル * 詳しくは次節で説明 * この節では通知関数を使わない為、0指定 */ void (*NotifyFunction)(void *Event, void *Context), /* イベント発生時に実行する関数(通知関数) * この節では使わない為、NULL指定*/ void *NotifyContext, /* 通知関数へ渡す引数を指定 * この節では通知関数を使わない為、NULL指定 */ void *Event /* 生成されたイベントを格納するポインタ */ ); /* イベント発生のトリガー時間を設定する */ unsigned long long (*SetTimer)( void *Event, /* タイマーイベントを発生させるイベントを指定 * CreateEvent()で作成したイベントを指定する */ enum EFI_TIMER_DELAY Type, /* タイマーイベントのタイプ指定 * 指定できる定数は上記の通り * この節ではTimerRelativeを使用 */ unsigned long long TriggerTime /* 100ns単位でトリガー時間を設定 * 0を指定した場合、 * - TimerPeriodic: 毎tick*1毎にイベントを発生 * - TimerRelative: 次tickでイベントを発生 * この節では1秒(10000000)を指定 */ ); unsigned long long (*WaitForEvent)( unsigned long long NumberOfEvents, void **Event, unsigned long long *Index); ・・・ } *BootServices; };
[*1] "tick"とはシステムで扱う時間の単位で、語源は時計の"チクタク"。例えば1ms毎のタイマー割り込みで時間を管理しているシステムならtickは1ms。UEFIの場合どうなのかはまだ分かってないです。ごめんなさい。
使用例はリスト4.2の通りです。
リスト4.2: CreateEvent()とSetTimer()とWaitForEvent()の使用例(main.cより)
1: #include "efi.h" 2: #include "common.h" 3: 4: void efi_main(void *ImageHandle __attribute__ ((unused)), 5: struct EFI_SYSTEM_TABLE *SystemTable) 6: { 7: unsigned long long status; 8: void *tevent; 9: void *wait_list[1]; 10: unsigned long long idx; 11: 12: efi_init(SystemTable); 13: ST->ConOut->ClearScreen(ST->ConOut); 14: 15: /* タイマーイベントを作成し、teventへ格納 */ 16: status = ST->BootServices->CreateEvent(EVT_TIMER, 0, NULL, NULL, 17: &tevent); 18: assert(status, L"CreateEvent"); 19: 20: /* WaitForEvent()へ渡す為にイベントリストを作成 */ 21: wait_list[0] = tevent; 22: 23: while (TRUE) { 24: /* teventへ1秒のトリガー時間を設定 */ 25: status = ST->BootServices->SetTimer(tevent, TimerRelative, 26: 10000000); 27: assert(status, L"SetTimer"); 28: 29: /* イベント発生を待つ */ 30: status = ST->BootServices->WaitForEvent(1, wait_list, &idx); 31: assert(status, L"WaitForEvent"); 32: 33: /* 画面へ"wait."を出力 */ 34: puts(L"wait."); 35: } 36: }
リスト4.2では、SetTimer()でトリガー時間を設定し、WaitForEvent()でイベント発生を待つ事で、1秒毎に"wait."が画面出力されます。
実行例は図4.1の通りです。
図4.1: CreateEvent()とSetTimer()とWaitForEvent()の実行例
CreateEvent()では、イベント発生時に指定した関数を呼び出すように設定できます。
サンプルのディレクトリは"041_evt_timer_nonblocking"です。
前節のCreateEvent()定義の説明箇所のみを再掲しますリスト4.3。
リスト4.3: (再掲)CreateEvent()の定義(efi.hより)
/* 第1引数Typeで指定したイベントを生成し、 * 第5引数Eventへ格納する */ unsigned long long (*CreateEvent)( unsigned int Type, /* イベントタイプを指定 * 使用できる定数は上記の通り * この節ではEVT_TIMERを扱う */ unsigned long long NotifyTpl, /* イベントの通知関数実行時のタスク優先度レベル * 詳しくは次節で説明 * この節では通知関数を使わない為、0指定 */ void (*NotifyFunction)(void *Event, void *Context), /* イベント発生時に実行する関数(通知関数) * この節では使わない為、NULL指定*/ void *NotifyContext, /* 通知関数へ渡す引数を指定 * この節では通知関数を使わない為、NULL指定 */ void *Event /* 生成されたイベントを格納するポインタ */ );
使用例をリスト4.4に示します。
リスト4.4: CreateEvent()の通知関数設定例
1: #include "efi.h" 2: #include "common.h" 3: 4: void timer_callback(void *event __attribute__ ((unused)), 5: void *context __attribute__ ((unused))) 6: { 7: puts(L"wait."); 8: } 9: 10: void efi_main(void *ImageHandle __attribute__ ((unused)), 11: struct EFI_SYSTEM_TABLE *SystemTable) 12: { 13: unsigned long long status; 14: void *tevent; 15: 16: efi_init(SystemTable); 17: ST->ConOut->ClearScreen(ST->ConOut); 18: 19: /* タイマーイベントを作成し、teventへ格納 */ 20: status = ST->BootServices->CreateEvent(EVT_TIMER | EVT_NOTIFY_SIGNAL, 21: TPL_CALLBACK, timer_callback, 22: NULL, &tevent); 23: assert(status, L"CreateEvent"); 24: 25: /* teventへ1秒の周期トリガー時間を設定 */ 26: status = ST->BootServices->SetTimer(tevent, TimerPeriodic, 27: 10000000); 28: assert(status, L"SetTimer"); 29: 30: while (TRUE); 31: }
リスト4.4では、SetTimer()へ周期タイマー(TimerPeriodic)を設定してみました。1秒周期でtimer_callback()が呼び出されます。
実行例は図4.2の通りです。
図4.2: CreateEvent()の通知関数実行例