esp_radio_rtos_driver/
semaphore.rs

1//! Semaphores
2//!
3//! Semaphores are synchronization primitives that allow threads to coordinate their execution.
4//! They are used to control access to a shared resource by limiting the number of threads that can
5//! access it simultaneously.
6//!
7//! esp-radio sometimes mixes up semaphores and mutexes (FreeRTOS allows this), so this crate
8//! exposes a single interface to work with both.
9//!
10//! ## Implementation
11//!
12//! Implement the `SemaphoreImplementation` trait for an object, and use the
13//! `register_semaphore_implementation` to register that implementation for esp-radio.
14//!
15//! See the [`SemaphoreImplementation`] documentation for more information.
16//!
17//! ## Usage
18//!
19//! Users should use [`SemaphoreHandle`] to interact with semaphores created by the driver
20//! implementation. Use [`SemaphoreKind`] to specify the type of semaphore or mutex to create.
21//!
22//! > Note that the only expected user of this crate is esp-radio.
23
24use core::ptr::NonNull;
25
26/// Pointer to an opaque semaphore created by the driver implementation.
27pub type SemaphorePtr = NonNull<()>;
28
29/// The type of semaphore or mutex to create.
30pub enum SemaphoreKind {
31    /// Counting semaphore.
32    Counting { max: u32, initial: u32 },
33
34    /// Non-recursive mutex.
35    Mutex,
36
37    /// Recursive mutex.
38    RecursiveMutex,
39}
40
41unsafe extern "Rust" {
42    fn esp_rtos_semaphore_create(kind: SemaphoreKind) -> SemaphorePtr;
43    fn esp_rtos_semaphore_delete(semaphore: SemaphorePtr);
44
45    fn esp_rtos_semaphore_take(semaphore: SemaphorePtr, timeout_us: Option<u32>) -> bool;
46    fn esp_rtos_semaphore_give(semaphore: SemaphorePtr) -> bool;
47    fn esp_rtos_semaphore_try_give_from_isr(
48        semaphore: SemaphorePtr,
49        higher_prio_task_waken: Option<&mut bool>,
50    ) -> bool;
51    fn esp_rtos_semaphore_current_count(semaphore: SemaphorePtr) -> u32;
52
53    fn esp_rtos_semaphore_try_take(semaphore: SemaphorePtr) -> bool;
54    fn esp_rtos_semaphore_try_take_from_isr(
55        semaphore: SemaphorePtr,
56        higher_prio_task_waken: Option<&mut bool>,
57    ) -> bool;
58}
59
60/// A semaphore primitive.
61///
62/// The following snippet demonstrates the boilerplate necessary to implement a semaphore using the
63/// `SemaphoreImplementation` trait:
64///
65/// ```rust,no_run
66/// use esp_radio_rtos_driver::{
67///     register_semaphore_implementation,
68///     semaphore::{SemaphoreImplementation, SemaphoreKind, SemaphorePtr},
69/// };
70///
71/// struct MySemaphore {
72///     // Semaphore implementation details
73/// }
74///
75/// impl SemaphoreImplementation for MySemaphore {
76///     fn create(kind: SemaphoreKind) -> SemaphorePtr {
77///         unimplemented!()
78///     }
79///
80///     unsafe fn delete(semaphore: SemaphorePtr) {
81///         unimplemented!()
82///     }
83///
84///     unsafe fn take(semaphore: SemaphorePtr, timeout_us: Option<u32>) -> bool {
85///         unimplemented!()
86///     }
87///
88///     unsafe fn give(semaphore: SemaphorePtr) -> bool {
89///         unimplemented!()
90///     }
91///
92///     unsafe fn try_give_from_isr(
93///         semaphore: SemaphorePtr,
94///         higher_prio_task_waken: Option<&mut bool>,
95///     ) -> bool {
96///         unimplemented!()
97///     }
98///
99///     unsafe fn current_count(semaphore: SemaphorePtr) -> u32 {
100///         unimplemented!()
101///     }
102///
103///     unsafe fn try_take(semaphore: SemaphorePtr) -> bool {
104///         unimplemented!()
105///     }
106///
107///     unsafe fn try_take_from_isr(
108///         semaphore: SemaphorePtr,
109///         higher_prio_task_waken: Option<&mut bool>,
110///     ) -> bool {
111///         unimplemented!()
112///     }
113/// }
114///
115/// register_semaphore_implementation!(MySemaphore);
116/// ```
117pub trait SemaphoreImplementation {
118    /// Creates a new semaphore instance.
119    ///
120    /// `kind` specifies the type of semaphore to create.
121    ///
122    /// - `SemaphoreKind::Counting` should create counting, non-recursive semaphores/mutexes.
123    /// - `SemaphoreKind::RecursiveMutex` should create recursive mutexes.
124    fn create(kind: SemaphoreKind) -> SemaphorePtr;
125
126    /// Deletes a semaphore instance.
127    ///
128    /// # Safety
129    ///
130    /// `semaphore` must be a pointer returned from [`Self::create`].
131    unsafe fn delete(semaphore: SemaphorePtr);
132
133    /// Increments the semaphore's counter.
134    ///
135    /// If a timeout is given, this function should block until either a semaphore could be taken,
136    /// or the timeout has been reached. If no timeout is specified, the function should block
137    /// indefinitely.
138    ///
139    /// Recursive mutexes can be repeatedly taken by the same task.
140    ///
141    /// The timeout is specified in microseconds.
142    ///
143    /// This function returns `true` if the semaphore was taken, `false` if the timeout was reached.
144    ///
145    /// # Safety
146    ///
147    /// `semaphore` must be a pointer returned from [`Self::create`].
148    unsafe fn take(semaphore: SemaphorePtr, timeout_us: Option<u32>) -> bool;
149
150    /// Increments the semaphore's counter.
151    ///
152    /// This function returns `true` if the semaphore was given, `false` if the counter is at
153    /// its maximum.
154    ///
155    /// Recursive mutexes can not be given by a task other than the one that first locked it.
156    ///
157    /// # Safety
158    ///
159    /// `semaphore` must be a pointer returned from [`Self::create`].
160    unsafe fn give(semaphore: SemaphorePtr) -> bool;
161
162    /// Attempts to increment the semaphore's counter from an ISR.
163    ///
164    /// This function returns `true` if the semaphore was given, `false` if the counter is at
165    /// its maximum.
166    ///
167    /// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag.
168    /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
169    ///
170    /// # Safety
171    ///
172    /// `semaphore` must be a pointer returned from [`Self::create`].
173    unsafe fn try_give_from_isr(
174        semaphore: SemaphorePtr,
175        higher_prio_task_waken: Option<&mut bool>,
176    ) -> bool;
177
178    /// Returns the semaphore's current counter value.
179    ///
180    /// # Safety
181    ///
182    /// `semaphore` must be a pointer returned from [`Self::create`].
183    unsafe fn current_count(semaphore: SemaphorePtr) -> u32;
184
185    /// Attempts to decrement the semaphore's counter.
186    ///
187    /// If the counter is zero, this function must immediately return `false`.
188    ///
189    /// # Safety
190    ///
191    /// `semaphore` must be a pointer returned from [`Self::create`].
192    unsafe fn try_take(semaphore: SemaphorePtr) -> bool;
193
194    /// Attempts to decrement the semaphore's counter from an ISR.
195    ///
196    /// If the counter is zero, this function must immediately return `false`.
197    ///
198    /// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag.
199    /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
200    ///
201    /// # Safety
202    ///
203    /// `semaphore` must be a pointer returned from [`Self::create`].
204    unsafe fn try_take_from_isr(
205        semaphore: SemaphorePtr,
206        higher_prio_task_waken: Option<&mut bool>,
207    ) -> bool;
208}
209
210#[macro_export]
211macro_rules! register_semaphore_implementation {
212    ($t: ty) => {
213        #[unsafe(no_mangle)]
214        #[inline]
215        fn esp_rtos_semaphore_create(
216            kind: $crate::semaphore::SemaphoreKind,
217        ) -> $crate::semaphore::SemaphorePtr {
218            <$t as $crate::semaphore::SemaphoreImplementation>::create(kind)
219        }
220
221        #[unsafe(no_mangle)]
222        #[inline]
223        fn esp_rtos_semaphore_delete(semaphore: $crate::semaphore::SemaphorePtr) {
224            unsafe { <$t as $crate::semaphore::SemaphoreImplementation>::delete(semaphore) }
225        }
226
227        #[unsafe(no_mangle)]
228        #[inline]
229        fn esp_rtos_semaphore_take(
230            semaphore: $crate::semaphore::SemaphorePtr,
231            timeout_us: Option<u32>,
232        ) -> bool {
233            unsafe {
234                <$t as $crate::semaphore::SemaphoreImplementation>::take(semaphore, timeout_us)
235            }
236        }
237
238        #[unsafe(no_mangle)]
239        #[inline]
240        fn esp_rtos_semaphore_give(semaphore: $crate::semaphore::SemaphorePtr) -> bool {
241            unsafe { <$t as $crate::semaphore::SemaphoreImplementation>::give(semaphore) }
242        }
243
244        #[unsafe(no_mangle)]
245        #[inline]
246        fn esp_rtos_semaphore_try_give_from_isr(
247            semaphore: $crate::semaphore::SemaphorePtr,
248            higher_prio_task_waken: Option<&mut bool>,
249        ) -> bool {
250            unsafe {
251                <$t as $crate::semaphore::SemaphoreImplementation>::try_give_from_isr(
252                    semaphore,
253                    higher_prio_task_waken,
254                )
255            }
256        }
257
258        #[unsafe(no_mangle)]
259        #[inline]
260        fn esp_rtos_semaphore_current_count(semaphore: $crate::semaphore::SemaphorePtr) -> u32 {
261            unsafe { <$t as $crate::semaphore::SemaphoreImplementation>::current_count(semaphore) }
262        }
263
264        #[unsafe(no_mangle)]
265        #[inline]
266        fn esp_rtos_semaphore_try_take(semaphore: $crate::semaphore::SemaphorePtr) -> bool {
267            unsafe { <$t as $crate::semaphore::SemaphoreImplementation>::try_take(semaphore) }
268        }
269
270        #[unsafe(no_mangle)]
271        #[inline]
272        fn esp_rtos_semaphore_try_take_from_isr(
273            semaphore: $crate::semaphore::SemaphorePtr,
274            higher_prio_task_waken: Option<&mut bool>,
275        ) -> bool {
276            unsafe {
277                <$t as $crate::semaphore::SemaphoreImplementation>::try_take_from_isr(
278                    semaphore,
279                    higher_prio_task_waken,
280                )
281            }
282        }
283    };
284}
285
286/// Semaphore handle.
287///
288/// This handle is used to interact with semaphores created by the driver implementation.
289#[repr(transparent)]
290pub struct SemaphoreHandle(SemaphorePtr);
291impl SemaphoreHandle {
292    /// Creates a new semaphore instance.
293    ///
294    /// `kind` specifies the type of semaphore to create.
295    ///
296    /// - Use `SemaphoreKind::Counting` to create counting semaphores and non-recursive mutexes.
297    /// - Use `SemaphoreKind::RecursiveMutex` to create recursive mutexes.
298    #[inline]
299    pub fn new(kind: SemaphoreKind) -> Self {
300        let ptr = unsafe { esp_rtos_semaphore_create(kind) };
301        Self(ptr)
302    }
303
304    /// Converts this object into a pointer without dropping it.
305    #[inline]
306    pub fn leak(self) -> SemaphorePtr {
307        let ptr = self.0;
308        core::mem::forget(self);
309        ptr
310    }
311
312    /// Recovers the object from a leaked pointer.
313    ///
314    /// # Safety
315    ///
316    /// - The caller must only use pointers created using [`Self::leak`].
317    /// - The caller must ensure the pointer is not shared.
318    #[inline]
319    pub unsafe fn from_ptr(ptr: SemaphorePtr) -> Self {
320        Self(ptr)
321    }
322
323    /// Creates a reference to this object from a leaked pointer.
324    ///
325    /// This function is used in the esp-radio code to interact with the semaphore.
326    ///
327    /// # Safety
328    ///
329    /// - The caller must only use pointers created using [`Self::leak`].
330    #[inline]
331    pub unsafe fn ref_from_ptr(ptr: &SemaphorePtr) -> &Self {
332        unsafe { core::mem::transmute(ptr) }
333    }
334
335    /// Increments the semaphore's counter.
336    ///
337    /// If a timeout is given, this function blocks until either a semaphore could be taken, or the
338    /// timeout has been reached. If no timeout is given, this function blocks until the operation
339    /// succeeds.
340    ///
341    /// This function returns `true` if the semaphore was taken, `false` if the timeout was reached.
342    #[inline]
343    pub fn take(&self, timeout_us: Option<u32>) -> bool {
344        unsafe { esp_rtos_semaphore_take(self.0, timeout_us) }
345    }
346
347    /// Increments the semaphore's counter.
348    ///
349    /// This function returns `true` if the semaphore was given, `false` if the counter is at its
350    /// maximum.
351    #[inline]
352    pub fn give(&self) -> bool {
353        unsafe { esp_rtos_semaphore_give(self.0) }
354    }
355
356    /// Attempts to increment the semaphore's counter from an ISR.
357    ///
358    /// If the counter is at its maximum, this function returns `false`.
359    ///
360    /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
361    #[inline]
362    pub fn try_give_from_isr(&self, higher_prio_task_waken: Option<&mut bool>) -> bool {
363        unsafe { esp_rtos_semaphore_try_give_from_isr(self.0, higher_prio_task_waken) }
364    }
365
366    /// Returns the current counter value.
367    #[inline]
368    pub fn current_count(&self) -> u32 {
369        unsafe { esp_rtos_semaphore_current_count(self.0) }
370    }
371
372    /// Attempts to decrement the semaphore's counter.
373    ///
374    /// If the counter is zero, this function returns `false`.
375    #[inline]
376    pub fn try_take(&self) -> bool {
377        unsafe { esp_rtos_semaphore_try_take(self.0) }
378    }
379
380    /// Attempts to decrement the semaphore's counter from an ISR.
381    ///
382    /// If the counter is zero, this function returns `false`.
383    ///
384    /// If a higher priority task is woken up by this operation, the `higher_prio_task_waken` flag
385    /// is set to `true`.
386    #[inline]
387    pub fn try_take_from_isr(&self, higher_prio_task_waken: Option<&mut bool>) -> bool {
388        unsafe { esp_rtos_semaphore_try_take_from_isr(self.0, higher_prio_task_waken) }
389    }
390}
391
392impl Drop for SemaphoreHandle {
393    #[inline]
394    fn drop(&mut self) {
395        unsafe { esp_rtos_semaphore_delete(self.0) };
396    }
397}