esp_radio_rtos_driver/timer.rs
1//! Timers (callbacks scheduled to run in the future)
2//!
3//! ## Implementation
4//!
5//! Implement the `TimerImplementation` trait for an object, and use the
6//! `register_timer_implementation` to register that implementation for esp-radio.
7//!
8//! See the [`TimerImplementation`] documentation for more information.
9//!
10//! ## Usage
11//!
12//! Users should use [`TimerHandle`] to interact with timers created by the driver implementation.
13//!
14//! > Note that the only expected user of this crate is esp-radio.
15
16use core::{ffi::c_void, ptr::NonNull};
17
18/// Pointer to an opaque timer created by the driver implementation.
19pub type TimerPtr = NonNull<()>;
20
21unsafe extern "Rust" {
22 fn esp_rtos_timer_create(
23 function: unsafe extern "C" fn(*mut c_void),
24 data: *mut c_void,
25 ) -> TimerPtr;
26 fn esp_rtos_timer_delete(timer: TimerPtr);
27
28 fn esp_rtos_timer_arm(timer: TimerPtr, timeout: u64, periodic: bool);
29 fn esp_rtos_timer_is_active(timer: TimerPtr) -> bool;
30 fn esp_rtos_timer_disarm(timer: TimerPtr);
31}
32
33/// A timer implementation.
34///
35/// The following snippet demonstrates the boilerplate necessary to implement a timer using the
36/// `TimerImplementation` trait:
37///
38/// ```rust,no_run
39/// use esp_radio_rtos_driver::{
40/// register_timer_implementation,
41/// timer::{TimerImplementation, TimerPtr},
42/// };
43///
44/// struct MyTimer {
45/// // Timer implementation details
46/// }
47///
48/// impl TimerImplementation for MyTimer {
49/// fn create(function: unsafe extern "C" fn(*mut c_void), data: *mut c_void) -> TimerPtr {
50/// unimplemented!()
51/// }
52///
53/// unsafe fn delete(mutex: MutexPtr) {
54/// unimplemented!()
55/// }
56///
57/// unsafe fn arm(timer: TimerPtr, timeout: u64, periodic: bool) {
58/// unimplemented!()
59/// }
60///
61/// unsafe fn is_active(timer: TimerPtr) -> bool {
62/// unimplemented!()
63/// }
64///
65/// unsafe fn disarm(timer: TimerPtr) -> bool {
66/// unimplemented!()
67/// }
68/// }
69///
70/// register_timer_implementation!(MyTimer);
71/// ```
72pub trait TimerImplementation {
73 /// Creates a new timer instance from the given callback.
74 fn create(function: unsafe extern "C" fn(*mut c_void), data: *mut c_void) -> TimerPtr;
75
76 /// Deletes a timer instance.
77 ///
78 /// # Safety
79 ///
80 /// `timer` must be a pointer returned from [`Self::create`].
81 unsafe fn delete(timer: TimerPtr);
82
83 /// Configures the timer to be triggered after the given timeout.
84 ///
85 /// The timeout is specified in microsecond. If the timer is set to be periodic,
86 /// the timer will be triggered with a constant frequency.
87 ///
88 /// # Safety
89 ///
90 /// `timer` must be a pointer returned from [`Self::create`].
91 unsafe fn arm(timer: TimerPtr, timeout: u64, periodic: bool);
92
93 /// Checks if the timer is currently active.
94 ///
95 /// # Safety
96 ///
97 /// `timer` must be a pointer returned from [`Self::create`].
98 unsafe fn is_active(timer: TimerPtr) -> bool;
99
100 /// Stops the timer.
101 ///
102 /// # Safety
103 ///
104 /// `timer` must be a pointer returned from [`Self::create`].
105 unsafe fn disarm(timer: TimerPtr);
106}
107
108#[macro_export]
109macro_rules! register_timer_implementation {
110 ($t: ty) => {
111 #[unsafe(no_mangle)]
112 #[inline]
113 fn esp_rtos_timer_create(
114 function: unsafe extern "C" fn(*mut ::core::ffi::c_void),
115 data: *mut ::core::ffi::c_void,
116 ) -> $crate::timer::TimerPtr {
117 <$t as $crate::timer::TimerImplementation>::create(function, data)
118 }
119
120 #[unsafe(no_mangle)]
121 #[inline]
122 fn esp_rtos_timer_delete(timer: $crate::timer::TimerPtr) {
123 unsafe { <$t as $crate::timer::TimerImplementation>::delete(timer) }
124 }
125
126 #[unsafe(no_mangle)]
127 #[inline]
128 fn esp_rtos_timer_arm(timer: $crate::timer::TimerPtr, timeout: u64, periodic: bool) {
129 unsafe { <$t as $crate::timer::TimerImplementation>::arm(timer, timeout, periodic) }
130 }
131
132 #[unsafe(no_mangle)]
133 #[inline]
134 fn esp_rtos_timer_is_active(timer: $crate::timer::TimerPtr) -> bool {
135 unsafe { <$t as $crate::timer::TimerImplementation>::is_active(timer) }
136 }
137
138 #[unsafe(no_mangle)]
139 #[inline]
140 fn esp_rtos_timer_disarm(timer: $crate::timer::TimerPtr) {
141 unsafe { <$t as $crate::timer::TimerImplementation>::disarm(timer) }
142 }
143 };
144}
145
146/// A timer handle.
147///
148/// This handle is used to interact with timers created by the driver implementation.
149#[repr(transparent)]
150pub struct TimerHandle(TimerPtr);
151impl TimerHandle {
152 /// Creates a new timer instance from the given callback.
153 ///
154 /// # Safety
155 ///
156 /// - The callback and its data must be valid for the lifetime of the timer.
157 /// - The callback and its data need to be able to be sent across threads.
158 #[inline]
159 pub unsafe fn new(function: unsafe extern "C" fn(*mut c_void), data: *mut c_void) -> Self {
160 Self(unsafe { esp_rtos_timer_create(function, data) })
161 }
162
163 /// Converts this object into a pointer without dropping it.
164 #[inline]
165 pub fn leak(self) -> TimerPtr {
166 let ptr = self.0;
167 core::mem::forget(self);
168 ptr
169 }
170
171 /// Recovers the object from a leaked pointer.
172 ///
173 /// # Safety
174 ///
175 /// - The caller must only use pointers created using [`Self::leak`].
176 /// - The caller must ensure the pointer is not shared.
177 #[inline]
178 pub unsafe fn from_ptr(ptr: TimerPtr) -> Self {
179 Self(ptr)
180 }
181
182 /// Creates a reference to this object from a leaked pointer.
183 ///
184 /// This function is used in the esp-radio code to interact with the timer.
185 ///
186 /// # Safety
187 ///
188 /// - The caller must only use pointers created using [`Self::leak`].
189 #[inline]
190 pub unsafe fn ref_from_ptr(ptr: &TimerPtr) -> &Self {
191 unsafe { core::mem::transmute(ptr) }
192 }
193
194 /// Configures the timer to be triggered after the given timeout.
195 ///
196 /// The timeout is specified in microsecond. If the timer is set to be periodic,
197 /// the timer will be triggered with a constant frequency.
198 #[inline]
199 pub fn arm(&self, timeout: u64, periodic: bool) {
200 unsafe { esp_rtos_timer_arm(self.0, timeout, periodic) }
201 }
202
203 /// Checks if the timer is currently active.
204 #[inline]
205 pub fn is_active(&self) -> bool {
206 unsafe { esp_rtos_timer_is_active(self.0) }
207 }
208
209 /// Stops the timer.
210 #[inline]
211 pub fn disarm(&self) {
212 unsafe { esp_rtos_timer_disarm(self.0) }
213 }
214}
215
216impl Drop for TimerHandle {
217 #[inline]
218 fn drop(&mut self) {
219 unsafe { esp_rtos_timer_delete(self.0) };
220 }
221}