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//! You may also choose to use the [`CompatQueue`] implementation provided by this crate.
14//!
15//! ## Usage
16//!
17//! Users should use [`QueueHandle`] to interact with queues created by the driver implementation.
18//!
19//! > Note that the only expected user of this crate is esp-radio.
20
21use core::ptr::NonNull;
22
23/// Pointer to an opaque queue created by the driver implementation.
24pub type QueuePtr = NonNull<()>;
25
26unsafe extern "Rust" {
27 fn esp_rtos_queue_create(capacity: usize, item_size: usize) -> QueuePtr;
28 fn esp_rtos_queue_delete(queue: QueuePtr);
29
30 fn esp_rtos_queue_send_to_front(
31 queue: QueuePtr,
32 item: *const u8,
33 timeout_us: Option<u32>,
34 ) -> bool;
35 fn esp_rtos_queue_send_to_front_with_deadline(
36 queue: QueuePtr,
37 item: *const u8,
38 deadline_instant: Option<u64>,
39 ) -> bool;
40
41 fn esp_rtos_queue_send_to_back(
42 queue: QueuePtr,
43 item: *const u8,
44 timeout_us: Option<u32>,
45 ) -> bool;
46 fn esp_rtos_queue_send_to_back_with_deadline(
47 queue: QueuePtr,
48 item: *const u8,
49 deadline_instant: Option<u64>,
50 ) -> bool;
51
52 fn esp_rtos_queue_try_send_to_back_from_isr(
53 queue: QueuePtr,
54 item: *const u8,
55 higher_prio_task_waken: Option<&mut bool>,
56 ) -> bool;
57 fn esp_rtos_queue_receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool;
58 fn esp_rtos_queue_receive_with_deadline(
59 queue: QueuePtr,
60 item: *mut u8,
61 deadline_instant: Option<u64>,
62 ) -> bool;
63 fn esp_rtos_queue_try_receive_from_isr(
64 queue: QueuePtr,
65 item: *mut u8,
66 higher_prio_task_waken: Option<&mut bool>,
67 ) -> bool;
68 fn esp_rtos_queue_remove(queue: QueuePtr, item: *const u8);
69 fn esp_rtos_queue_messages_waiting(queue: QueuePtr) -> usize;
70}
71
72/// A queue primitive.
73///
74/// The following snippet demonstrates the boilerplate necessary to implement a queue using the
75/// `QueueImplementation` trait:
76///
77/// ```rust,no_run
78/// use esp_radio_rtos_driver::{
79/// queue::{QueueImplementation, QueuePtr},
80/// register_queue_implementation,
81/// };
82///
83/// struct MyQueue {
84/// // Queue implementation details
85/// }
86///
87/// impl QueueImplementation for MyQueue {
88/// fn create(capacity: usize, item_size: usize) -> QueuePtr {
89/// unimplemented!()
90/// }
91///
92/// unsafe fn delete(queue: QueuePtr) {
93/// unimplemented!()
94/// }
95///
96/// unsafe fn send_to_front(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool {
97/// unimplemented!()
98/// }
99///
100/// unsafe fn send_to_back(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool {
101/// unimplemented!()
102/// }
103///
104/// unsafe fn try_send_to_back_from_isr(
105/// queue: QueuePtr,
106/// item: *const u8,
107/// higher_prio_task_waken: Option<&mut bool>,
108/// ) -> bool {
109/// unimplemented!()
110/// }
111///
112/// unsafe fn receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool {
113/// unimplemented!()
114/// }
115///
116/// unsafe fn try_receive_from_isr(
117/// queue: QueuePtr,
118/// item: *mut u8,
119/// higher_prio_task_waken: Option<&mut bool>,
120/// ) -> bool {
121/// unimplemented!()
122/// }
123///
124/// unsafe fn remove(queue: QueuePtr, item: *const u8) {
125/// unimplemented!()
126/// }
127///
128/// fn messages_waiting(queue: QueuePtr) -> usize {
129/// unimplemented!()
130/// }
131/// }
132///
133/// register_queue_implementation!(MyQueue);
134/// ```
135pub trait QueueImplementation {
136 /// Creates a new, empty queue instance.
137 ///
138 /// The queue must have a capacity for `capacity` number of `item_size` byte items.
139 fn create(capacity: usize, item_size: usize) -> QueuePtr;
140
141 /// Deletes a queue instance.
142 ///
143 /// # Safety
144 ///
145 /// `queue` must be a pointer returned from [`Self::create`].
146 unsafe fn delete(queue: QueuePtr);
147
148 /// Enqueues a high-priority item.
149 ///
150 /// If the queue is full, this function will block for the given timeout. If timeout is None,
151 /// the function will block indefinitely.
152 ///
153 /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
154 ///
155 /// # Safety
156 ///
157 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
158 /// a size equal to the queue's item size.
159 unsafe fn send_to_front(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool;
160
161 /// Enqueues a high-priority item.
162 ///
163 /// If the queue is full, this function will block until the deadline is reached. If the
164 /// deadline is None, the function will block indefinitely.
165 ///
166 /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
167 ///
168 /// # Safety
169 ///
170 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
171 /// a size equal to the queue's item size.
172 unsafe fn send_to_front_with_deadline(
173 queue: QueuePtr,
174 item: *const u8,
175 deadline_instant: Option<u64>,
176 ) -> bool;
177
178 /// Enqueues an item.
179 ///
180 /// If the queue is full, this function will block for the given timeout. If timeout is None,
181 /// the function will block indefinitely.
182 ///
183 /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
184 ///
185 /// # Safety
186 ///
187 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
188 /// a size equal to the queue's item size.
189 unsafe fn send_to_back(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool;
190
191 /// Enqueues an item.
192 ///
193 /// If the queue is full, this function will block until the given deadline. If deadline is
194 /// None, the function will block indefinitely.
195 ///
196 /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
197 ///
198 /// # Safety
199 ///
200 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
201 /// a size equal to the queue's item size.
202 unsafe fn send_to_back_with_deadline(
203 queue: QueuePtr,
204 item: *const u8,
205 deadline_instant: Option<u64>,
206 ) -> bool;
207
208 /// Attempts to enqueues an item.
209 ///
210 /// If the queue is full, this function will immediately return `false`.
211 ///
212 /// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag.
213 /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
214 ///
215 /// # Safety
216 ///
217 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
218 /// a size equal to the queue's item size.
219 unsafe fn try_send_to_back_from_isr(
220 queue: QueuePtr,
221 item: *const u8,
222 higher_prio_task_waken: Option<&mut bool>,
223 ) -> bool;
224
225 /// Dequeues an item from the queue.
226 ///
227 /// If the queue is empty, this function will block for the given timeout. If timeout is None,
228 /// the function will block indefinitely.
229 ///
230 /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
231 ///
232 /// # Safety
233 ///
234 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
235 /// a size equal to the queue's item size.
236 unsafe fn receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool;
237
238 /// Dequeues an item from the queue.
239 ///
240 /// If the queue is empty, this function will block until the given deadline is reached. If the
241 /// deadline is None, the function will block indefinitely.
242 ///
243 /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
244 ///
245 /// # Safety
246 ///
247 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
248 /// a size equal to the queue's item size.
249 unsafe fn receive_with_deadline(
250 queue: QueuePtr,
251 item: *mut u8,
252 deadline_instant: Option<u64>,
253 ) -> bool;
254
255 /// Attempts to dequeue an item from the queue.
256 ///
257 /// If the queue is empty, this function will return `false` immediately.
258 ///
259 /// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag.
260 /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
261 ///
262 /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
263 ///
264 /// # Safety
265 ///
266 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
267 /// a size equal to the queue's item size.
268 unsafe fn try_receive_from_isr(
269 queue: QueuePtr,
270 item: *mut u8,
271 higher_prio_task_waken: Option<&mut bool>,
272 ) -> bool;
273
274 /// Removes an item from the queue.
275 ///
276 /// # Safety
277 ///
278 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
279 /// a size equal to the queue's item size.
280 unsafe fn remove(queue: QueuePtr, item: *const u8);
281
282 /// Returns the number of messages in the queue.
283 fn messages_waiting(queue: QueuePtr) -> usize;
284}
285
286#[macro_export]
287macro_rules! register_queue_implementation {
288 ($t: ty) => {
289 #[unsafe(no_mangle)]
290 #[inline]
291 fn esp_rtos_queue_create(capacity: usize, item_size: usize) -> $crate::queue::QueuePtr {
292 <$t as $crate::queue::QueueImplementation>::create(capacity, item_size)
293 }
294
295 #[unsafe(no_mangle)]
296 #[inline]
297 fn esp_rtos_queue_delete(queue: $crate::queue::QueuePtr) {
298 unsafe { <$t as $crate::queue::QueueImplementation>::delete(queue) }
299 }
300
301 #[unsafe(no_mangle)]
302 #[inline]
303 fn esp_rtos_queue_send_to_front(
304 queue: $crate::queue::QueuePtr,
305 item: *const u8,
306 timeout_us: Option<u32>,
307 ) -> bool {
308 unsafe {
309 <$t as $crate::queue::QueueImplementation>::send_to_front(queue, item, timeout_us)
310 }
311 }
312
313 #[unsafe(no_mangle)]
314 #[inline]
315 fn esp_rtos_queue_send_to_front_with_deadline(
316 queue: $crate::queue::QueuePtr,
317 item: *const u8,
318 deadline_instant: Option<u64>,
319 ) -> bool {
320 unsafe {
321 <$t as $crate::queue::QueueImplementation>::send_to_front_with_deadline(
322 queue,
323 item,
324 deadline_instant,
325 )
326 }
327 }
328
329 #[unsafe(no_mangle)]
330 #[inline]
331 fn esp_rtos_queue_send_to_back(
332 queue: $crate::queue::QueuePtr,
333 item: *const u8,
334 timeout_us: Option<u32>,
335 ) -> bool {
336 unsafe {
337 <$t as $crate::queue::QueueImplementation>::send_to_back(queue, item, timeout_us)
338 }
339 }
340
341 #[unsafe(no_mangle)]
342 #[inline]
343 fn esp_rtos_queue_send_to_back_with_deadline(
344 queue: $crate::queue::QueuePtr,
345 item: *const u8,
346 deadline_instant: Option<u64>,
347 ) -> bool {
348 unsafe {
349 <$t as $crate::queue::QueueImplementation>::send_to_back_with_deadline(
350 queue,
351 item,
352 deadline_instant,
353 )
354 }
355 }
356
357 #[unsafe(no_mangle)]
358 #[inline]
359 fn esp_rtos_queue_try_send_to_back_from_isr(
360 queue: $crate::queue::QueuePtr,
361 item: *const u8,
362 higher_prio_task_waken: Option<&mut bool>,
363 ) -> bool {
364 unsafe {
365 <$t as $crate::queue::QueueImplementation>::try_send_to_back_from_isr(
366 queue,
367 item,
368 higher_prio_task_waken,
369 )
370 }
371 }
372
373 #[unsafe(no_mangle)]
374 #[inline]
375 fn esp_rtos_queue_receive(
376 queue: $crate::queue::QueuePtr,
377 item: *mut u8,
378 timeout_us: Option<u32>,
379 ) -> bool {
380 unsafe { <$t as $crate::queue::QueueImplementation>::receive(queue, item, timeout_us) }
381 }
382
383 #[unsafe(no_mangle)]
384 #[inline]
385 fn esp_rtos_queue_receive_with_deadline(
386 queue: $crate::queue::QueuePtr,
387 item: *mut u8,
388 deadline_instant: Option<u64>,
389 ) -> bool {
390 unsafe {
391 <$t as $crate::queue::QueueImplementation>::receive_with_deadline(
392 queue,
393 item,
394 deadline_instant,
395 )
396 }
397 }
398
399 #[unsafe(no_mangle)]
400 #[inline]
401 fn esp_rtos_queue_try_receive_from_isr(
402 queue: $crate::queue::QueuePtr,
403 item: *mut u8,
404 higher_prio_task_waken: Option<&mut bool>,
405 ) -> bool {
406 unsafe {
407 <$t as $crate::queue::QueueImplementation>::try_receive_from_isr(
408 queue,
409 item,
410 higher_prio_task_waken,
411 )
412 }
413 }
414
415 #[unsafe(no_mangle)]
416 #[inline]
417 fn esp_rtos_queue_remove(queue: $crate::queue::QueuePtr, item: *mut u8) {
418 unsafe { <$t as $crate::queue::QueueImplementation>::remove(queue, item) }
419 }
420
421 #[unsafe(no_mangle)]
422 #[inline]
423 fn esp_rtos_queue_messages_waiting(queue: $crate::queue::QueuePtr) -> usize {
424 unsafe { <$t as $crate::queue::QueueImplementation>::messages_waiting(queue) }
425 }
426 };
427}
428
429/// Queue handle.
430///
431/// This handle is used to interact with queues created by the driver implementation.
432#[repr(transparent)]
433pub struct QueueHandle(QueuePtr);
434
435unsafe impl Send for QueueHandle {}
436unsafe impl Sync for QueueHandle {}
437
438impl QueueHandle {
439 /// Creates a new queue instance.
440 #[inline]
441 pub fn new(capacity: usize, item_size: usize) -> Self {
442 let ptr = unsafe { esp_rtos_queue_create(capacity, item_size) };
443 Self(ptr)
444 }
445
446 /// Converts this object into a pointer without dropping it.
447 #[inline]
448 pub fn leak(self) -> QueuePtr {
449 let ptr = self.0;
450 core::mem::forget(self);
451 ptr
452 }
453
454 /// Recovers the object from a leaked pointer.
455 ///
456 /// # Safety
457 ///
458 /// - The caller must only use pointers created using [`Self::leak`].
459 /// - The caller must ensure the pointer is not shared.
460 #[inline]
461 pub unsafe fn from_ptr(ptr: QueuePtr) -> Self {
462 Self(ptr)
463 }
464
465 /// Creates a reference to this object from a leaked pointer.
466 ///
467 /// This function is used in the esp-radio code to interact with the queue.
468 ///
469 /// # Safety
470 ///
471 /// - The caller must only use pointers created using [`Self::leak`].
472 #[inline]
473 pub unsafe fn ref_from_ptr(ptr: &QueuePtr) -> &Self {
474 unsafe { core::mem::transmute(ptr) }
475 }
476
477 /// Enqueues a high-priority item.
478 ///
479 /// If the queue is full, this function will block for the given timeout. If timeout is None,
480 /// the function will block indefinitely.
481 ///
482 /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
483 ///
484 /// # Safety
485 ///
486 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
487 /// a size equal to the queue's item size.
488 #[inline]
489 pub unsafe fn send_to_front(&self, item: *const u8, timeout_us: Option<u32>) -> bool {
490 unsafe { esp_rtos_queue_send_to_front(self.0, item, timeout_us) }
491 }
492
493 /// Enqueues a high-priority item.
494 ///
495 /// If the queue is full, this function will block until the deadline is reached. If the
496 /// deadline is None, the function will block indefinitely.
497 ///
498 /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
499 ///
500 /// # Safety
501 ///
502 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
503 /// a size equal to the queue's item size.
504 #[inline]
505 pub unsafe fn send_to_front_with_deadline(
506 &self,
507 item: *const u8,
508 deadline_instant: Option<u64>,
509 ) -> bool {
510 unsafe { esp_rtos_queue_send_to_front_with_deadline(self.0, item, deadline_instant) }
511 }
512
513 /// Enqueues an item.
514 ///
515 /// If the queue is full, this function will block for the given timeout. If timeout is None,
516 /// the function will block indefinitely.
517 ///
518 /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
519 ///
520 /// # Safety
521 ///
522 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
523 /// a size equal to the queue's item size.
524 #[inline]
525 pub unsafe fn send_to_back(&self, item: *const u8, timeout_us: Option<u32>) -> bool {
526 unsafe { esp_rtos_queue_send_to_back(self.0, item, timeout_us) }
527 }
528
529 /// Enqueues an item.
530 ///
531 /// If the queue is full, this function will block until the given deadline. If deadline is
532 /// None, the function will block indefinitely.
533 ///
534 /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
535 ///
536 /// # Safety
537 ///
538 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
539 /// a size equal to the queue's item size.
540 #[inline]
541 pub unsafe fn send_to_back_with_deadline(
542 &self,
543 item: *const u8,
544 deadline_instant: Option<u64>,
545 ) -> bool {
546 unsafe { esp_rtos_queue_send_to_back_with_deadline(self.0, item, deadline_instant) }
547 }
548
549 /// Attempts to enqueues an item.
550 ///
551 /// If the queue is full, this function will immediately return `false`.
552 ///
553 /// If a higher priority task is woken up by this operation, the `higher_prio_task_waken` flag
554 /// is set to `true`.
555 ///
556 /// # Safety
557 ///
558 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
559 /// a size equal to the queue's item size.
560 #[inline]
561 pub unsafe fn try_send_to_back_from_isr(
562 &self,
563 item: *const u8,
564 higher_priority_task_waken: Option<&mut bool>,
565 ) -> bool {
566 unsafe {
567 esp_rtos_queue_try_send_to_back_from_isr(self.0, item, higher_priority_task_waken)
568 }
569 }
570
571 /// Dequeues an item from the queue.
572 ///
573 /// If the queue is empty, this function will block for the given timeout. If timeout is None,
574 /// the function will block indefinitely.
575 ///
576 /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
577 ///
578 /// # Safety
579 ///
580 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
581 /// a size equal to the queue's item size.
582 #[inline]
583 pub unsafe fn receive(&self, item: *mut u8, timeout_us: Option<u32>) -> bool {
584 unsafe { esp_rtos_queue_receive(self.0, item, timeout_us) }
585 }
586
587 /// Dequeues an item from the queue.
588 ///
589 /// If the queue is empty, this function will block until the given deadline is reached. If
590 /// deadline is None, the function will block indefinitely.
591 ///
592 /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
593 ///
594 /// # Safety
595 ///
596 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
597 /// a size equal to the queue's item size.
598 #[inline]
599 pub unsafe fn receive_with_deadline(
600 &self,
601 item: *mut u8,
602 deadline_instant: Option<u64>,
603 ) -> bool {
604 unsafe { esp_rtos_queue_receive_with_deadline(self.0, item, deadline_instant) }
605 }
606
607 /// Attempts to dequeue an item from the queue.
608 ///
609 /// If the queue is empty, this function will return `false` immediately.
610 ///
611 /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
612 ///
613 /// If a higher priority task is woken up by this operation, the `higher_prio_task_waken` flag
614 /// is set to `true`.
615 ///
616 /// # Safety
617 ///
618 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
619 /// a size equal to the queue's item size.
620 #[inline]
621 pub unsafe fn try_receive_from_isr(
622 &self,
623 item: *mut u8,
624 higher_priority_task_waken: Option<&mut bool>,
625 ) -> bool {
626 unsafe { esp_rtos_queue_try_receive_from_isr(self.0, item, higher_priority_task_waken) }
627 }
628
629 /// Removes an item from the queue.
630 ///
631 /// # Safety
632 ///
633 /// The caller must ensure that `item` can be dereferenced and points to an allocation of
634 /// a size equal to the queue's item size.
635 #[inline]
636 pub unsafe fn remove(&self, item: *const u8) {
637 unsafe { esp_rtos_queue_remove(self.0, item) }
638 }
639
640 /// Returns the number of messages in the queue.
641 #[inline]
642 pub fn messages_waiting(&self) -> usize {
643 unsafe { esp_rtos_queue_messages_waiting(self.0) }
644 }
645}
646
647impl Drop for QueueHandle {
648 #[inline]
649 fn drop(&mut self) {
650 unsafe { esp_rtos_queue_delete(self.0) };
651 }
652}
653
654#[cfg(feature = "ipc-implementations")]
655mod implementation {
656 use alloc::{boxed::Box, vec};
657
658 use esp_sync::NonReentrantMutex;
659
660 use super::*;
661 use crate::{
662 now,
663 semaphore::{SemaphoreHandle, SemaphoreKind},
664 };
665
666 struct QueueInner {
667 storage: Box<[u8]>,
668 item_size: usize,
669 capacity: usize,
670 count: usize,
671 current_read: usize,
672 current_write: usize,
673 }
674
675 impl QueueInner {
676 fn get(&self, index: usize) -> &[u8] {
677 let item_start = self.item_size * index;
678 &self.storage[item_start..][..self.item_size]
679 }
680
681 fn get_mut(&mut self, index: usize) -> &mut [u8] {
682 let item_start = self.item_size * index;
683 &mut self.storage[item_start..][..self.item_size]
684 }
685
686 fn len(&self) -> usize {
687 self.count
688 }
689
690 fn send_to_back(&mut self, item: *const u8) {
691 let item = unsafe { core::slice::from_raw_parts(item, self.item_size) };
692
693 let dst = self.get_mut(self.current_write);
694 dst.copy_from_slice(item);
695
696 self.current_write = (self.current_write + 1) % self.capacity;
697 self.count += 1;
698 }
699
700 fn send_to_front(&mut self, item: *const u8) {
701 let item = unsafe { core::slice::from_raw_parts(item, self.item_size) };
702
703 self.current_read = (self.current_read + self.capacity - 1) % self.capacity;
704
705 let dst = self.get_mut(self.current_read);
706 dst.copy_from_slice(item);
707
708 self.count += 1;
709 }
710
711 fn read_from_front(&mut self, dst: *mut u8) {
712 let dst = unsafe { core::slice::from_raw_parts_mut(dst, self.item_size) };
713
714 let src = self.get(self.current_read);
715 dst.copy_from_slice(src);
716
717 self.current_read = (self.current_read + 1) % self.capacity;
718 self.count -= 1;
719 }
720
721 fn remove(&mut self, item: *const u8) -> bool {
722 let count = self.len();
723
724 if count == 0 {
725 return false;
726 }
727
728 let mut tmp_item = vec![0; self.item_size];
729
730 let mut found = false;
731 let item_slice = unsafe { core::slice::from_raw_parts(item, self.item_size) };
732 for _ in 0..count {
733 self.read_from_front(tmp_item.as_mut_ptr().cast());
734
735 if found || &tmp_item[..] != item_slice {
736 self.send_to_back(tmp_item.as_mut_ptr().cast());
737 } else {
738 found = true;
739 }
740
741 // Note that even if we find our item, we'll need to keep cycling through everything
742 // to keep insertion order.
743 }
744
745 found
746 }
747 }
748
749 /// A suitable queue implementation that only requires semaphores from the OS.
750 ///
751 /// Register in your OS implementation by adding the following code:
752 ///
753 /// ```rust
754 /// use esp_radio_rtos_driver::{queue::CompatQueue, register_queue_implementation};
755 ///
756 /// register_queue_implementation!(CompatQueue);
757 /// ```
758 pub struct CompatQueue {
759 /// Allows interior mutability for the queue's inner state, when the mutex is held.
760 inner: NonReentrantMutex<QueueInner>,
761
762 semaphore_empty: SemaphoreHandle,
763 semaphore_full: SemaphoreHandle,
764 }
765
766 impl CompatQueue {
767 fn new(capacity: usize, item_size: usize) -> Self {
768 let storage = vec![0; capacity * item_size].into_boxed_slice();
769 let semaphore_empty = SemaphoreHandle::new(SemaphoreKind::Counting {
770 max: capacity as u32,
771 initial: capacity as u32,
772 });
773 let semaphore_full = SemaphoreHandle::new(SemaphoreKind::Counting {
774 max: capacity as u32,
775 initial: 0,
776 });
777 Self {
778 inner: NonReentrantMutex::new(QueueInner {
779 storage,
780 item_size,
781 capacity,
782 count: 0,
783 current_read: 0,
784 current_write: 0,
785 }),
786 semaphore_empty,
787 semaphore_full,
788 }
789 }
790
791 unsafe fn from_ptr<'a>(ptr: QueuePtr) -> &'a Self {
792 unsafe { ptr.cast::<Self>().as_ref() }
793 }
794
795 fn with<R>(&self, f: impl FnOnce(&mut QueueInner) -> R) -> R {
796 self.inner.with(f)
797 }
798 }
799
800 impl QueueImplementation for CompatQueue {
801 fn create(capacity: usize, item_size: usize) -> QueuePtr {
802 let q = Box::new(CompatQueue::new(capacity, item_size));
803 NonNull::from(Box::leak(q)).cast()
804 }
805
806 unsafe fn delete(queue: QueuePtr) {
807 let q = unsafe { Box::from_raw(queue.cast::<CompatQueue>().as_ptr()) };
808 core::mem::drop(q);
809 }
810
811 unsafe fn send_to_front(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool {
812 let deadline_instant = timeout_us.map(|timeout| now() + timeout as u64);
813 unsafe { Self::send_to_front_with_deadline(queue, item, deadline_instant) }
814 }
815
816 unsafe fn send_to_front_with_deadline(
817 queue: QueuePtr,
818 item: *const u8,
819 deadline_instant: Option<u64>,
820 ) -> bool {
821 let queue = unsafe { CompatQueue::from_ptr(queue) };
822
823 if queue.semaphore_empty.take_with_deadline(deadline_instant) {
824 queue.with(|inner| inner.send_to_front(item));
825 queue.semaphore_full.give();
826 true
827 } else {
828 false
829 }
830 }
831
832 unsafe fn send_to_back(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool {
833 let deadline_instant = timeout_us.map(|timeout| now() + timeout as u64);
834 unsafe { Self::send_to_back_with_deadline(queue, item, deadline_instant) }
835 }
836
837 unsafe fn send_to_back_with_deadline(
838 queue: QueuePtr,
839 item: *const u8,
840 deadline_instant: Option<u64>,
841 ) -> bool {
842 let queue = unsafe { CompatQueue::from_ptr(queue) };
843
844 if queue.semaphore_empty.take_with_deadline(deadline_instant) {
845 queue.with(|inner| inner.send_to_back(item));
846 queue.semaphore_full.give();
847 true
848 } else {
849 false
850 }
851 }
852
853 unsafe fn try_send_to_back_from_isr(
854 queue: QueuePtr,
855 item: *const u8,
856 mut higher_prio_task_waken: Option<&mut bool>,
857 ) -> bool {
858 let queue = unsafe { CompatQueue::from_ptr(queue) };
859
860 if queue
861 .semaphore_empty
862 .try_take_from_isr(higher_prio_task_waken.as_deref_mut())
863 {
864 queue.with(|inner| inner.send_to_back(item));
865 queue
866 .semaphore_full
867 .try_give_from_isr(higher_prio_task_waken);
868 true
869 } else {
870 false
871 }
872 }
873
874 unsafe fn receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool {
875 let deadline_instant = timeout_us.map(|timeout| now() + timeout as u64);
876 unsafe { Self::receive_with_deadline(queue, item, deadline_instant) }
877 }
878
879 unsafe fn receive_with_deadline(
880 queue: QueuePtr,
881 item: *mut u8,
882 deadline_instant: Option<u64>,
883 ) -> bool {
884 let queue = unsafe { CompatQueue::from_ptr(queue) };
885
886 if queue.semaphore_full.take_with_deadline(deadline_instant) {
887 queue.with(|inner| inner.read_from_front(item));
888 queue.semaphore_empty.give();
889 true
890 } else {
891 false
892 }
893 }
894
895 unsafe fn try_receive_from_isr(
896 queue: QueuePtr,
897 item: *mut u8,
898 mut higher_prio_task_waken: Option<&mut bool>,
899 ) -> bool {
900 let queue = unsafe { CompatQueue::from_ptr(queue) };
901
902 if queue
903 .semaphore_full
904 .try_take_from_isr(higher_prio_task_waken.as_deref_mut())
905 {
906 queue.with(|inner| inner.read_from_front(item));
907 queue
908 .semaphore_empty
909 .try_give_from_isr(higher_prio_task_waken);
910 true
911 } else {
912 false
913 }
914 }
915
916 unsafe fn remove(queue: QueuePtr, item: *const u8) {
917 let queue = unsafe { CompatQueue::from_ptr(queue) };
918
919 if queue.semaphore_full.take(Some(0)) {
920 let item_removed = queue.with(|inner| inner.remove(item));
921
922 if item_removed {
923 queue.semaphore_empty.give();
924 } else {
925 queue.semaphore_full.give();
926 }
927 }
928 }
929
930 fn messages_waiting(queue: QueuePtr) -> usize {
931 let queue = unsafe { CompatQueue::from_ptr(queue) };
932
933 queue.semaphore_full.current_count() as usize
934 }
935 }
936}
937
938#[cfg(feature = "ipc-implementations")]
939pub use implementation::CompatQueue;