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}