uefi: make TimerTrigger more pleasant to use

This commit is contained in:
Philipp Schuster
2026-04-20 18:09:58 +02:00
parent de232c1f11
commit 1ea976d7f0
3 changed files with 65 additions and 14 deletions
+6 -2
View File
@@ -2,7 +2,7 @@
use core::ffi::c_void;
use core::ptr::{self, NonNull};
use core::time::Duration;
use uefi::boot::{
EventType, OpenProtocolAttributes, OpenProtocolParams, SearchType, TimerTrigger, Tpl,
};
@@ -55,7 +55,11 @@ fn test_timer() {
let timer_event =
unsafe { boot::create_event_ex(EventType::TIMER, Tpl::CALLBACK, None, None, None) }
.unwrap();
boot::set_timer(&timer_event, TimerTrigger::Relative(5_0 /*00 ns */)).unwrap();
boot::set_timer(
&timer_event,
TimerTrigger::Relative(Duration::from_nanos(5_000)),
)
.unwrap();
let mut events = [unsafe { timer_event.unsafe_clone() }];
assert_eq!(boot::wait_for_event(&mut events).unwrap(), 0);
+1 -1
View File
@@ -3,7 +3,7 @@
## Added
## Changed
- **Breaking:** The variants of `TimerTrigger` now hold a `Duration`
# uefi - v0.37.0 (2026-03-22)
+58 -11
View File
@@ -593,10 +593,11 @@ pub fn set_timer(event: &Event, trigger_time: TimerTrigger) -> Result {
let bt = boot_services_raw_panicking();
let bt = unsafe { bt.as_ref() };
let (ty, time) = match trigger_time {
TimerTrigger::Cancel => (TimerDelay::CANCEL, 0),
TimerTrigger::Periodic(period) => (TimerDelay::PERIODIC, period),
TimerTrigger::Relative(delay) => (TimerDelay::RELATIVE, delay),
let time = trigger_time.to_100ns_steps();
let ty = match trigger_time {
TimerTrigger::Cancel => TimerDelay::CANCEL,
TimerTrigger::Periodic(_) => TimerDelay::PERIODIC,
TimerTrigger::Relative(_) => TimerDelay::RELATIVE,
};
unsafe { (bt.set_timer)(event.as_ptr(), ty, time) }.to_result()
}
@@ -1937,28 +1938,74 @@ pub type EventNotifyFn = unsafe extern "efiapi" fn(event: Event, context: Option
/// See [`set_timer`].
///
/// [`TIMER`]: EventType::TIMER
#[derive(Debug)]
#[derive(Copy, Clone, Debug)]
pub enum TimerTrigger {
/// Remove the event's timer setting.
Cancel,
/// Trigger the event periodically.
Periodic(
/// Duration between event signaling in units of 100ns. If set to zero,
/// the event will be signaled on every timer tick.
u64,
/// Duration to wait before the next event is fired.
///
/// If set to zero, the event will be signaled on every timer tick.
Duration,
),
/// Trigger the event one time.
Relative(
/// Duration to wait before signaling the event in units of 100ns. If
/// set to zero, the event will be signaled on the next timer tick.
u64,
/// Duration to wait before the next event is fired.
///
/// If set to zero, the event will be signaled on every timer tick.
Duration,
),
}
impl TimerTrigger {
/// Transforms the underlying value to 100ns steps, a format widely used
/// in EFI APIs.
#[must_use]
pub fn to_100ns_steps(self) -> u64 {
const NANOS_PER_STEP: u128 = 100;
match self {
Self::Cancel => 0,
Self::Periodic(dur) => {
let ns100_steps = dur.as_nanos().div_ceil(NANOS_PER_STEP);
u64::try_from(ns100_steps).expect("should be representable as u64")
}
Self::Relative(dur) => {
let ns100_steps = dur.as_nanos().div_ceil(NANOS_PER_STEP);
u64::try_from(ns100_steps).expect("should be representable as u64")
}
}
}
}
/// Opaque pointer returned by [`register_protocol_notify`] to be used
/// with [`locate_handle`] via [`SearchType::ByRegisterNotify`].
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct ProtocolSearchKey(pub(crate) NonNull<c_void>);
#[cfg(test)]
mod tests {
use core::time::Duration;
use uefi::boot::TimerTrigger;
#[test]
fn test_timer_trigger_100ns_steps() {
let trigger = TimerTrigger::Periodic(Duration::from_nanos(0));
assert_eq!(trigger.to_100ns_steps(), 0);
let trigger = TimerTrigger::Relative(Duration::from_nanos(0));
assert_eq!(trigger.to_100ns_steps(), 0);
let trigger = TimerTrigger::Periodic(Duration::from_nanos(12_300));
assert_eq!(trigger.to_100ns_steps(), 123);
let trigger = TimerTrigger::Relative(Duration::from_nanos(12_300));
assert_eq!(trigger.to_100ns_steps(), 123);
let trigger = TimerTrigger::Periodic(Duration::from_secs(42));
assert_eq!(trigger.to_100ns_steps(), 420000000);
let trigger = TimerTrigger::Relative(Duration::from_secs(42));
assert_eq!(trigger.to_100ns_steps(), 420000000);
}
}