esp_radio_rtos_driver/
queue.rs

1//! # Queues
2//!
3//! Queues are a synchronization primitive used to communicate between tasks.
4//! They allow tasks to send and receive data in a first-in-first-out (FIFO) manner.
5//!
6//! ## Implementation
7//!
8//! Implement the `QueueImplementation` trait for an object, and use the
9//! `register_queue_implementation` to register that implementation for esp-radio.
10//!
11//! See the [`QueueImplementation`] documentation for more information.
12//!
13//! ## Usage
14//!
15//! Users should use [`QueueHandle`] to interact with queues created by the driver implementation.
16//!
17//! > Note that the only expected user of this crate is esp-radio.
18
19use core::ptr::NonNull;
20
21/// Pointer to an opaque queue created by the driver implementation.
22pub type QueuePtr = NonNull<()>;
23
24unsafe extern "Rust" {
25    fn esp_rtos_queue_create(capacity: usize, item_size: usize) -> QueuePtr;
26    fn esp_rtos_queue_delete(queue: QueuePtr);
27
28    fn esp_rtos_queue_send_to_front(
29        queue: QueuePtr,
30        item: *const u8,
31        timeout_us: Option<u32>,
32    ) -> bool;
33    fn esp_rtos_queue_send_to_back(
34        queue: QueuePtr,
35        item: *const u8,
36        timeout_us: Option<u32>,
37    ) -> bool;
38    fn esp_rtos_queue_try_send_to_back_from_isr(
39        queue: QueuePtr,
40        item: *const u8,
41        higher_prio_task_waken: Option<&mut bool>,
42    ) -> bool;
43    fn esp_rtos_queue_receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool;
44    fn esp_rtos_queue_try_receive_from_isr(
45        queue: QueuePtr,
46        item: *mut u8,
47        higher_prio_task_waken: Option<&mut bool>,
48    ) -> bool;
49    fn esp_rtos_queue_remove(queue: QueuePtr, item: *const u8);
50    fn esp_rtos_queue_messages_waiting(queue: QueuePtr) -> usize;
51}
52
53/// A queue primitive.
54///
55/// The following snippet demonstrates the boilerplate necessary to implement a queue using the
56/// `QueueImplementation` trait:
57///
58/// ```rust,no_run
59/// use esp_radio_rtos_driver::{
60///     queue::{QueueImplementation, QueuePtr},
61///     register_queue_implementation,
62/// };
63///
64/// struct MyQueue {
65///     // Queue implementation details
66/// }
67///
68/// impl QueueImplementation for MyQueue {
69///     fn create(capacity: usize, item_size: usize) -> QueuePtr {
70///         unimplemented!()
71///     }
72///
73///     unsafe fn delete(queue: QueuePtr) {
74///         unimplemented!()
75///     }
76///
77///     unsafe fn send_to_front(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool {
78///         unimplemented!()
79///     }
80///
81///     unsafe fn send_to_back(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool {
82///         unimplemented!()
83///     }
84///
85///     unsafe fn try_send_to_back_from_isr(
86///         queue: QueuePtr,
87///         item: *const u8,
88///         higher_prio_task_waken: Option<&mut bool>,
89///     ) -> bool {
90///         unimplemented!()
91///     }
92///
93///     unsafe fn receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool {
94///         unimplemented!()
95///     }
96///
97///     unsafe fn try_receive_from_isr(
98///         queue: QueuePtr,
99///         item: *mut u8,
100///         higher_prio_task_waken: Option<&mut bool>,
101///     ) -> bool {
102///         unimplemented!()
103///     }
104///
105///     unsafe fn remove(queue: QueuePtr, item: *const u8) {
106///         unimplemented!()
107///     }
108///
109///     fn messages_waiting(queue: QueuePtr) -> usize {
110///         unimplemented!()
111///     }
112/// }
113///
114/// register_queue_implementation!(MyQueue);
115/// ```
116pub trait QueueImplementation {
117    /// Creates a new, empty queue instance.
118    ///
119    /// The queue must have a capacity for `capacity` number of `item_size` byte items.
120    fn create(capacity: usize, item_size: usize) -> QueuePtr;
121
122    /// Deletes a queue instance.
123    ///
124    /// # Safety
125    ///
126    /// `queue` must be a pointer returned from [`Self::create`].
127    unsafe fn delete(queue: QueuePtr);
128
129    /// Enqueues a high-priority item.
130    ///
131    /// If the queue is full, this function will block for the given timeout. If timeout is None,
132    /// the function will block indefinitely.
133    ///
134    /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
135    ///
136    /// # Safety
137    ///
138    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
139    /// a size equal to the queue's item size.
140    unsafe fn send_to_front(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool;
141
142    /// Enqueues an item.
143    ///
144    /// If the queue is full, this function will block for the given timeout. If timeout is None,
145    /// the function will block indefinitely.
146    ///
147    /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
148    ///
149    /// # Safety
150    ///
151    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
152    /// a size equal to the queue's item size.
153    unsafe fn send_to_back(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool;
154
155    /// Attempts to enqueues an item.
156    ///
157    /// If the queue is full, this function will immediately return `false`.
158    ///
159    /// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag.
160    /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
161    ///
162    /// # Safety
163    ///
164    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
165    /// a size equal to the queue's item size.
166    unsafe fn try_send_to_back_from_isr(
167        queue: QueuePtr,
168        item: *const u8,
169        higher_prio_task_waken: Option<&mut bool>,
170    ) -> bool;
171
172    /// Dequeues an item from the queue.
173    ///
174    /// If the queue is empty, this function will block for the given timeout. If timeout is None,
175    /// the function will block indefinitely.
176    ///
177    /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
178    ///
179    /// # Safety
180    ///
181    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
182    /// a size equal to the queue's item size.
183    unsafe fn receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool;
184
185    /// Attempts to dequeue an item from the queue.
186    ///
187    /// If the queue is empty, this function will return `false` immediately.
188    ///
189    /// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag.
190    /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
191    ///
192    /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
193    ///
194    /// # Safety
195    ///
196    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
197    /// a size equal to the queue's item size.
198    unsafe fn try_receive_from_isr(
199        queue: QueuePtr,
200        item: *mut u8,
201        higher_prio_task_waken: Option<&mut bool>,
202    ) -> bool;
203
204    /// Removes an item from the queue.
205    ///
206    /// # Safety
207    ///
208    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
209    /// a size equal to the queue's item size.
210    unsafe fn remove(queue: QueuePtr, item: *const u8);
211
212    /// Returns the number of messages in the queue.
213    fn messages_waiting(queue: QueuePtr) -> usize;
214}
215
216#[macro_export]
217macro_rules! register_queue_implementation {
218    ($t: ty) => {
219        #[unsafe(no_mangle)]
220        #[inline]
221        fn esp_rtos_queue_create(capacity: usize, item_size: usize) -> $crate::queue::QueuePtr {
222            <$t as $crate::queue::QueueImplementation>::create(capacity, item_size)
223        }
224
225        #[unsafe(no_mangle)]
226        #[inline]
227        fn esp_rtos_queue_delete(queue: $crate::queue::QueuePtr) {
228            unsafe { <$t as $crate::queue::QueueImplementation>::delete(queue) }
229        }
230
231        #[unsafe(no_mangle)]
232        #[inline]
233        fn esp_rtos_queue_send_to_front(
234            queue: QueuePtr,
235            item: *const u8,
236            timeout_us: Option<u32>,
237        ) -> bool {
238            unsafe {
239                <$t as $crate::queue::QueueImplementation>::send_to_front(queue, item, timeout_us)
240            }
241        }
242
243        #[unsafe(no_mangle)]
244        #[inline]
245        fn esp_rtos_queue_send_to_back(
246            queue: QueuePtr,
247            item: *const u8,
248            timeout_us: Option<u32>,
249        ) -> bool {
250            unsafe {
251                <$t as $crate::queue::QueueImplementation>::send_to_back(queue, item, timeout_us)
252            }
253        }
254
255        #[unsafe(no_mangle)]
256        #[inline]
257        fn esp_rtos_queue_try_send_to_back_from_isr(
258            queue: QueuePtr,
259            item: *const u8,
260            higher_prio_task_waken: Option<&mut bool>,
261        ) -> bool {
262            unsafe {
263                <$t as $crate::queue::QueueImplementation>::try_send_to_back_from_isr(
264                    queue,
265                    item,
266                    higher_prio_task_waken,
267                )
268            }
269        }
270
271        #[unsafe(no_mangle)]
272        #[inline]
273        fn esp_rtos_queue_receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool {
274            unsafe { <$t as $crate::queue::QueueImplementation>::receive(queue, item, timeout_us) }
275        }
276
277        #[unsafe(no_mangle)]
278        #[inline]
279        fn esp_rtos_queue_try_receive_from_isr(
280            queue: QueuePtr,
281            item: *mut u8,
282            higher_prio_task_waken: Option<&mut bool>,
283        ) -> bool {
284            unsafe {
285                <$t as $crate::queue::QueueImplementation>::try_receive_from_isr(
286                    queue,
287                    item,
288                    higher_prio_task_waken,
289                )
290            }
291        }
292
293        #[unsafe(no_mangle)]
294        #[inline]
295        fn esp_rtos_queue_remove(queue: QueuePtr, item: *mut u8) {
296            unsafe { <$t as $crate::queue::QueueImplementation>::remove(queue, item) }
297        }
298
299        #[unsafe(no_mangle)]
300        #[inline]
301        fn esp_rtos_queue_messages_waiting(queue: QueuePtr) -> usize {
302            unsafe { <$t as $crate::queue::QueueImplementation>::messages_waiting(queue) }
303        }
304    };
305}
306
307/// Queue handle.
308///
309/// This handle is used to interact with queues created by the driver implementation.
310#[repr(transparent)]
311pub struct QueueHandle(QueuePtr);
312impl QueueHandle {
313    /// Creates a new queue instance.
314    #[inline]
315    pub fn new(capacity: usize, item_size: usize) -> Self {
316        let ptr = unsafe { esp_rtos_queue_create(capacity, item_size) };
317        Self(ptr)
318    }
319
320    /// Converts this object into a pointer without dropping it.
321    #[inline]
322    pub fn leak(self) -> QueuePtr {
323        let ptr = self.0;
324        core::mem::forget(self);
325        ptr
326    }
327
328    /// Recovers the object from a leaked pointer.
329    ///
330    /// # Safety
331    ///
332    /// - The caller must only use pointers created using [`Self::leak`].
333    /// - The caller must ensure the pointer is not shared.
334    #[inline]
335    pub unsafe fn from_ptr(ptr: QueuePtr) -> Self {
336        Self(ptr)
337    }
338
339    /// Creates a reference to this object from a leaked pointer.
340    ///
341    /// This function is used in the esp-radio code to interact with the queue.
342    ///
343    /// # Safety
344    ///
345    /// - The caller must only use pointers created using [`Self::leak`].
346    #[inline]
347    pub unsafe fn ref_from_ptr(ptr: &QueuePtr) -> &Self {
348        unsafe { core::mem::transmute(ptr) }
349    }
350
351    /// Enqueues a high-priority item.
352    ///
353    /// If the queue is full, this function will block for the given timeout. If timeout is None,
354    /// the function will block indefinitely.
355    ///
356    /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
357    ///
358    /// # Safety
359    ///
360    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
361    /// a size equal to the queue's item size.
362    #[inline]
363    pub unsafe fn send_to_front(&self, item: *const u8, timeout_us: Option<u32>) -> bool {
364        unsafe { esp_rtos_queue_send_to_front(self.0, item, timeout_us) }
365    }
366
367    /// Enqueues an item.
368    ///
369    /// If the queue is full, this function will block for the given timeout. If timeout is None,
370    /// the function will block indefinitely.
371    ///
372    /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
373    ///
374    /// # Safety
375    ///
376    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
377    /// a size equal to the queue's item size.
378    #[inline]
379    pub unsafe fn send_to_back(&self, item: *const u8, timeout_us: Option<u32>) -> bool {
380        unsafe { esp_rtos_queue_send_to_back(self.0, item, timeout_us) }
381    }
382
383    /// Attempts to enqueues an item.
384    ///
385    /// If the queue is full, this function will immediately return `false`.
386    ///
387    /// If a higher priority task is woken up by this operation, the `higher_prio_task_waken` flag
388    /// is set to `true`.
389    ///
390    /// # Safety
391    ///
392    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
393    /// a size equal to the queue's item size.
394    #[inline]
395    pub unsafe fn try_send_to_back_from_isr(
396        &self,
397        item: *const u8,
398        higher_priority_task_waken: Option<&mut bool>,
399    ) -> bool {
400        unsafe {
401            esp_rtos_queue_try_send_to_back_from_isr(self.0, item, higher_priority_task_waken)
402        }
403    }
404
405    /// Dequeues an item from the queue.
406    ///
407    /// If the queue is empty, this function will block for the given timeout. If timeout is None,
408    /// the function will block indefinitely.
409    ///
410    /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
411    ///
412    /// # Safety
413    ///
414    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
415    /// a size equal to the queue's item size.
416    #[inline]
417    pub unsafe fn receive(&self, item: *mut u8, timeout_us: Option<u32>) -> bool {
418        unsafe { esp_rtos_queue_receive(self.0, item, timeout_us) }
419    }
420
421    /// Attempts to dequeue an item from the queue.
422    ///
423    /// If the queue is empty, this function will return `false` immediately.
424    ///
425    /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
426    ///
427    /// If a higher priority task is woken up by this operation, the `higher_prio_task_waken` flag
428    /// is set to `true`.
429    ///
430    /// # Safety
431    ///
432    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
433    /// a size equal to the queue's item size.
434    #[inline]
435    pub unsafe fn try_receive_from_isr(
436        &self,
437        item: *mut u8,
438        higher_priority_task_waken: Option<&mut bool>,
439    ) -> bool {
440        unsafe { esp_rtos_queue_try_receive_from_isr(self.0, item, higher_priority_task_waken) }
441    }
442
443    /// Removes an item from the queue.
444    ///
445    /// # Safety
446    ///
447    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
448    /// a size equal to the queue's item size.
449    #[inline]
450    pub unsafe fn remove(&self, item: *const u8) {
451        unsafe { esp_rtos_queue_remove(self.0, item) }
452    }
453
454    /// Returns the number of messages in the queue.
455    #[inline]
456    pub fn messages_waiting(&self) -> usize {
457        unsafe { esp_rtos_queue_messages_waiting(self.0) }
458    }
459}
460
461impl Drop for QueueHandle {
462    #[inline]
463    fn drop(&mut self) {
464        unsafe { esp_rtos_queue_delete(self.0) };
465    }
466}