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}