1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use core::{
future::{Future, poll_fn},
task::Poll,
};
use esp_hal::asynch::AtomicWaker;
use portable_atomic::{AtomicU8, AtomicUsize, Ordering};
use crate::ll::ChannelAccessError;
/// A primitive for signaling the status of a TX slot.
pub struct HardwareTxResultSignal {
state: AtomicU8,
waker: AtomicWaker,
}
impl HardwareTxResultSignal {
/// A transmission is currently pending or the slot is inactive.
const PENDING_OR_INACTIVE: u8 = 0;
/// The tranmission has completed successfully.
const SUCCESS: u8 = 1;
/// A timeout occured while transmitting.
const TIMEOUT: u8 = 2;
/// A collision occured while transmitting.
const COLLISION: u8 = 3;
/// Create a new state signal.
pub const fn new() -> Self {
Self {
state: AtomicU8::new(Self::PENDING_OR_INACTIVE),
waker: AtomicWaker::new(),
}
}
/// Reset the slot state signal.
pub fn reset(&self) {
self.state
.store(Self::PENDING_OR_INACTIVE, Ordering::Release);
}
/// Signal a slot state.
pub fn signal(&self, slot_status: Result<(), ChannelAccessError>) {
self.state.store(
match slot_status {
Ok(()) => Self::SUCCESS,
Err(ChannelAccessError::Timeout) => Self::TIMEOUT,
Err(ChannelAccessError::Collision) => Self::COLLISION,
},
Ordering::Relaxed,
);
self.waker.wake();
}
/// Wait for a slot state change.
pub fn wait(
&self,
) -> impl Future<Output = Result<(), ChannelAccessError>> + Send + Sync + use<'_> {
poll_fn(|cx| {
// We need a single core lock here, as the state may internally be updated by the ISR.
esp_sync::RawMutex::new().lock(|| {
let state = self.state.load(Ordering::Acquire);
if state != Self::PENDING_OR_INACTIVE {
self.reset();
Poll::Ready(match state {
Self::SUCCESS => Ok(()),
Self::TIMEOUT => Err(ChannelAccessError::Timeout),
Self::COLLISION => Err(ChannelAccessError::Collision),
_ => unreachable!(),
})
} else {
self.waker.register(cx.waker());
Poll::Pending
}
})
})
}
}
/// A synchronization primitive, which allows queueing a number signals, to be awaited.
pub struct SignalQueue {
waker: AtomicWaker,
queued_signals: AtomicUsize,
}
impl SignalQueue {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
queued_signals: AtomicUsize::new(0),
}
}
/// Increments the queue signals by one.
pub fn put(&self) {
if self.queued_signals.fetch_add(1, Ordering::AcqRel) == 0 {
// If there were already some signals queued up, there's no need to wake the task again.
self.waker.wake();
}
}
/// Reset the amount of signals in the queue back to zero.
pub fn reset(&self) {
self.queued_signals.store(0, Ordering::Release);
}
/// Asynchronously wait for the next signal.
pub fn next(&self) -> impl Future<Output = ()> + Send + Sync + use<'_> {
poll_fn(|cx| {
esp_sync::RawMutex::new().lock(|| {
let queued_signals = self.queued_signals.load(Ordering::Acquire);
if queued_signals == 0 {
self.waker.register(cx.waker());
Poll::Pending
} else {
self.queued_signals
.store(queued_signals - 1, Ordering::Release);
Poll::Ready(())
}
})
})
}
}
/// A drop guard, which executes the provided closure on drop.
pub struct DropGuard<F: FnMut()> {
drop_closure: F,
}
impl<F: FnMut()> DropGuard<F> {
#[inline]
/// Create a new drop guard.
pub const fn new(drop_closure: F) -> Self {
Self { drop_closure }
}
#[allow(unused)]
#[inline]
/// Defuse the drop guard.
///
/// This will prevent the drop closure from being run.
pub const fn defuse(self) {
core::mem::forget(self);
}
#[inline]
/// Disable the drop guard by running the provided drop closure.
///
/// This exists to explicitly run the drop closure. It's called `detonate`, since that seemed
/// like the logical counterpart to [Self::defuse].
pub fn detonate(self) {}
}
impl<F: FnMut()> Drop for DropGuard<F> {
fn drop(&mut self) {
(self.drop_closure)();
}
}