Skip to content

Commit 779228e

Browse files
authored
Tweak driver to better model FreeRTOS APIs (#4076)
1 parent 2b906e5 commit 779228e

File tree

14 files changed

+296
-73
lines changed

14 files changed

+296
-73
lines changed

esp-preempt/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,10 @@ impl esp_radio_preempt_driver::Scheduler for Scheduler {
321321
timer::yield_task()
322322
}
323323

324+
fn yield_task_from_isr(&self) {
325+
self.yield_task();
326+
}
327+
324328
fn max_task_priority(&self) -> u32 {
325329
255
326330
}

esp-preempt/src/queue.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,11 @@ impl QueueImplementation for Queue {
187187
unsafe { queue.send_to_back(item, timeout_us) }
188188
}
189189

190-
unsafe fn try_send_to_back(queue: QueuePtr, item: *const u8) -> bool {
190+
unsafe fn try_send_to_back_from_isr(
191+
queue: QueuePtr,
192+
item: *const u8,
193+
_higher_prio_task_waken: Option<&mut bool>,
194+
) -> bool {
191195
let queue = unsafe { Queue::from_ptr(queue) };
192196

193197
unsafe { queue.try_send_to_back(item) }
@@ -199,7 +203,11 @@ impl QueueImplementation for Queue {
199203
unsafe { queue.receive(item, timeout_us) }
200204
}
201205

202-
unsafe fn try_receive(queue: QueuePtr, item: *mut u8) -> bool {
206+
unsafe fn try_receive_from_isr(
207+
queue: QueuePtr,
208+
item: *mut u8,
209+
_higher_prio_task_waken: Option<&mut bool>,
210+
) -> bool {
203211
let queue = unsafe { Queue::from_ptr(queue) };
204212

205213
unsafe { queue.try_receive(item) }

esp-preempt/src/semaphore.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ impl SemaphoreImplementation for Semaphore {
131131

132132
semaphore.try_take()
133133
}
134+
135+
unsafe fn try_give_from_isr(semaphore: SemaphorePtr, _hptw: Option<&mut bool>) -> bool {
136+
unsafe { <Self as SemaphoreImplementation>::give(semaphore) }
137+
}
138+
139+
unsafe fn try_take_from_isr(semaphore: SemaphorePtr, _hptw: Option<&mut bool>) -> bool {
140+
unsafe { <Self as SemaphoreImplementation>::try_take(semaphore) }
141+
}
134142
}
135143

136144
register_semaphore_implementation!(Semaphore);

esp-preempt/src/timer_queue.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,23 @@ impl Timer {
243243
}
244244

245245
impl TimerImplementation for Timer {
246-
fn create(callback: Box<dyn FnMut() + Send>) -> TimerPtr {
247-
let timer = Box::new(Timer::new(callback));
246+
fn create(func: unsafe extern "C" fn(*mut c_void), data: *mut c_void) -> TimerPtr {
247+
// TODO: get rid of the inner box (or its heap allocation) somehow
248+
struct CCallback {
249+
func: unsafe extern "C" fn(*mut c_void),
250+
data: *mut c_void,
251+
}
252+
unsafe impl Send for CCallback {}
253+
254+
impl CCallback {
255+
unsafe fn call(&mut self) {
256+
unsafe { (self.func)(self.data) }
257+
}
258+
}
259+
260+
let mut callback = CCallback { func, data };
261+
262+
let timer = Box::new(Timer::new(Box::new(move || unsafe { callback.call() })));
248263
NonNull::from(Box::leak(timer)).cast()
249264
}
250265

esp-radio-preempt-driver/src/lib.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99
//!
1010
//! ## Implementing a scheduler driver
1111
//!
12+
//! This crate abstracts the capabilities of FreeRTOS. The scheduler must implement the following
13+
//! capabilities:
14+
//!
15+
//! - A preemptive task scheduler
16+
//! - Mutexes
17+
//! - Semaphores
18+
//! - Queues
19+
//! - Timers (functions that are executed at a specific time)
20+
//!
1221
//! In order to hook up a scheduler, implement the `Scheduler` trait for a struct, and register it
1322
//! using the `scheduler_impl!()` macro. Only one scheduler can be registered in a firmware.
1423
//!
@@ -45,6 +54,7 @@ unsafe extern "Rust" {
4554
fn esp_preempt_enable();
4655
fn esp_preempt_disable();
4756
fn esp_preempt_yield_task();
57+
fn esp_preempt_yield_task_from_isr();
4858
fn esp_preempt_current_task() -> *mut c_void;
4959
fn esp_preempt_max_task_priority() -> u32;
5060
fn esp_preempt_task_create(
@@ -93,6 +103,12 @@ macro_rules! scheduler_impl {
93103
<$t as $crate::Scheduler>::yield_task(&$name)
94104
}
95105

106+
#[unsafe(no_mangle)]
107+
#[inline]
108+
fn esp_preempt_yield_task_from_isr() {
109+
<$t as $crate::Scheduler>::yield_task_from_isr(&$name)
110+
}
111+
96112
#[unsafe(no_mangle)]
97113
#[inline]
98114
fn esp_preempt_current_task() -> *mut c_void {
@@ -165,9 +181,12 @@ pub trait Scheduler: Send + Sync + 'static {
165181
/// This function is called by `esp-radio` to stop the task scheduler.
166182
fn disable(&self);
167183

168-
/// This function is called by `esp_radio::init` to yield control to another task.
184+
/// This function is called by `esp_radio` to yield control to another task.
169185
fn yield_task(&self);
170186

187+
/// This function is called by `esp_radio` to yield control to another task.
188+
fn yield_task_from_isr(&self);
189+
171190
/// This function is called by `esp_radio::init` to retrieve a pointer to the current task.
172191
fn current_task(&self) -> *mut c_void;
173192

@@ -236,6 +255,12 @@ pub fn yield_task() {
236255
unsafe { esp_preempt_yield_task() }
237256
}
238257

258+
/// Yields control to another task for an interrupt.
259+
#[inline]
260+
pub fn yield_task_from_isr() {
261+
unsafe { esp_preempt_yield_task_from_isr() }
262+
}
263+
239264
/// Returns a pointer to the current task.
240265
#[inline]
241266
pub fn current_task() -> *mut c_void {

esp-radio-preempt-driver/src/queue.rs

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,17 @@ unsafe extern "Rust" {
1919
item: *const u8,
2020
timeout_us: Option<u32>,
2121
) -> bool;
22-
fn esp_preempt_queue_try_send_to_back(queue: QueuePtr, item: *const u8) -> bool;
22+
fn esp_preempt_queue_try_send_to_back_from_isr(
23+
queue: QueuePtr,
24+
item: *const u8,
25+
higher_prio_task_waken: Option<&mut bool>,
26+
) -> bool;
2327
fn esp_preempt_queue_receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool;
24-
fn esp_preempt_queue_try_receive(queue: QueuePtr, item: *mut u8) -> bool;
28+
fn esp_preempt_queue_try_receive_from_isr(
29+
queue: QueuePtr,
30+
item: *mut u8,
31+
higher_prio_task_waken: Option<&mut bool>,
32+
) -> bool;
2533
fn esp_preempt_queue_remove(queue: QueuePtr, item: *const u8);
2634
fn esp_preempt_queue_messages_waiting(queue: QueuePtr) -> usize;
2735
}
@@ -67,11 +75,18 @@ pub trait QueueImplementation {
6775
///
6876
/// If the queue is full, this function will immediately return `false`.
6977
///
78+
/// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag.
79+
/// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
80+
///
7081
/// # Safety
7182
///
7283
/// The caller must ensure that `item` can be dereferenced and points to an allocation of
7384
/// a size equal to the queue's item size.
74-
unsafe fn try_send_to_back(queue: QueuePtr, item: *const u8) -> bool;
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;
7590

7691
/// Dequeues an item from the queue.
7792
///
@@ -90,13 +105,20 @@ pub trait QueueImplementation {
90105
///
91106
/// If the queue is empty, this function will return `false` immediately.
92107
///
108+
/// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag.
109+
/// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
110+
///
93111
/// This function returns `true` if the item was successfully dequeued, `false` otherwise.
94112
///
95113
/// # Safety
96114
///
97115
/// The caller must ensure that `item` can be dereferenced and points to an allocation of
98116
/// a size equal to the queue's item size.
99-
unsafe fn try_receive(queue: QueuePtr, item: *mut u8) -> bool;
117+
unsafe fn try_receive_from_isr(
118+
queue: QueuePtr,
119+
item: *mut u8,
120+
higher_prio_task_waken: Option<&mut bool>,
121+
) -> bool;
100122

101123
/// Removes an item from the queue.
102124
///
@@ -151,8 +173,18 @@ macro_rules! register_queue_implementation {
151173

152174
#[unsafe(no_mangle)]
153175
#[inline]
154-
fn esp_preempt_queue_try_send_to_back(queue: QueuePtr, item: *const u8) -> bool {
155-
unsafe { <$t as $crate::queue::QueueImplementation>::try_send_to_back(queue, item) }
176+
fn esp_preempt_queue_try_send_to_back_from_isr(
177+
queue: QueuePtr,
178+
item: *const u8,
179+
higher_prio_task_waken: Option<&mut bool>,
180+
) -> bool {
181+
unsafe {
182+
<$t as $crate::queue::QueueImplementation>::try_send_to_back_from_isr(
183+
queue,
184+
item,
185+
higher_prio_task_waken,
186+
)
187+
}
156188
}
157189

158190
#[unsafe(no_mangle)]
@@ -167,8 +199,18 @@ macro_rules! register_queue_implementation {
167199

168200
#[unsafe(no_mangle)]
169201
#[inline]
170-
fn esp_preempt_queue_try_receive(queue: QueuePtr, item: *mut u8) -> bool {
171-
unsafe { <$t as $crate::queue::QueueImplementation>::try_receive(queue, item) }
202+
fn esp_preempt_queue_try_receive_from_isr(
203+
queue: QueuePtr,
204+
item: *mut u8,
205+
higher_prio_task_waken: Option<&mut bool>,
206+
) -> bool {
207+
unsafe {
208+
<$t as $crate::queue::QueueImplementation>::try_receive_from_isr(
209+
queue,
210+
item,
211+
higher_prio_task_waken,
212+
)
213+
}
172214
}
173215

174216
#[unsafe(no_mangle)]
@@ -256,12 +298,21 @@ impl QueueHandle {
256298
///
257299
/// If the queue is full, this function will immediately return `false`.
258300
///
301+
/// If a higher priority task is woken up by this operation, the `higher_prio_task_waken` flag
302+
/// is set to `true`.
303+
///
259304
/// # Safety
260305
///
261306
/// The caller must ensure that `item` can be dereferenced and points to an allocation of
262307
/// a size equal to the queue's item size.
263-
pub unsafe fn try_send_to_back(&self, item: *const u8) -> bool {
264-
unsafe { esp_preempt_queue_try_send_to_back(self.0, item) }
308+
pub unsafe fn try_send_to_back_from_isr(
309+
&self,
310+
item: *const u8,
311+
higher_priority_task_waken: Option<&mut bool>,
312+
) -> bool {
313+
unsafe {
314+
esp_preempt_queue_try_send_to_back_from_isr(self.0, item, higher_priority_task_waken)
315+
}
265316
}
266317

267318
/// Dequeues an item from the queue.
@@ -285,12 +336,19 @@ impl QueueHandle {
285336
///
286337
/// This function returns `true` if the item was successfully dequeued, `false` otherwise.
287338
///
339+
/// If a higher priority task is woken up by this operation, the `higher_prio_task_waken` flag
340+
/// is set to `true`.
341+
///
288342
/// # Safety
289343
///
290344
/// The caller must ensure that `item` can be dereferenced and points to an allocation of
291345
/// a size equal to the queue's item size.
292-
pub unsafe fn try_receive(&self, item: *mut u8) -> bool {
293-
unsafe { esp_preempt_queue_try_receive(self.0, item) }
346+
pub unsafe fn try_receive_from_isr(
347+
&self,
348+
item: *mut u8,
349+
higher_priority_task_waken: Option<&mut bool>,
350+
) -> bool {
351+
unsafe { esp_preempt_queue_try_receive_from_isr(self.0, item, higher_priority_task_waken) }
294352
}
295353

296354
/// Removes an item from the queue.

0 commit comments

Comments
 (0)