esp_radio_rtos_driver/
lib.rs

1//! # esp-radio task scheduler interface.
2//!
3//! `esp-radio` requires a task scheduler to operate. This crate allows the task scheduler to be
4//! tailored to specific software platforms (such as ArielOS). Trying to use multiple scheduler
5//! crates in a firmware project will not build.
6//!
7//! If you want to use esp-radio without any OS, you can use the [`esp-rtos`]
8//! crate as the task scheduler.
9//!
10//! ## Implementing a scheduler driver
11//!
12//! This crate abstracts the capabilities of FreeRTOS. The scheduler must implement the following
13//! capabilities:
14//!
15//! - A preemptive task scheduler: [`Scheduler`]
16//! - Semaphores: [`semaphore::SemaphoreImplementation`]
17//! - Queues: [`queue::QueueImplementation`]
18//! - Timers (functions that are executed at a specific time): [`timer::TimerImplementation`]
19//!
20//! [`esp-rtos`]: https://crates.io/crates/esp-rtos
21
22#![no_std]
23
24pub mod queue;
25pub mod semaphore;
26pub mod timer;
27
28use core::ffi::c_void;
29
30// Timer callbacks need to be heap-allocated.
31extern crate alloc;
32
33use crate::semaphore::SemaphorePtr;
34
35unsafe extern "Rust" {
36    fn esp_rtos_initialized() -> bool;
37    fn esp_rtos_yield_task();
38    fn esp_rtos_yield_task_from_isr();
39    fn esp_rtos_current_task() -> *mut c_void;
40    fn esp_rtos_max_task_priority() -> u32;
41    fn esp_rtos_task_create(
42        name: &str,
43        task: extern "C" fn(*mut c_void),
44        param: *mut c_void,
45        priority: u32,
46        pin_to_core: Option<u32>,
47        task_stack_size: usize,
48    ) -> *mut c_void;
49    fn esp_rtos_schedule_task_deletion(task_handle: *mut c_void);
50    fn esp_rtos_current_task_thread_semaphore() -> SemaphorePtr;
51
52    fn esp_rtos_usleep(us: u32);
53    fn esp_rtos_now() -> u64;
54}
55
56/// Set the Scheduler implementation.
57///
58/// See the [module documentation][crate] for an example.
59#[macro_export]
60macro_rules! scheduler_impl {
61    ($vis:vis static $driver:ident: $t: ty = $val:expr) => {
62        $vis static $driver: $t = $val;
63
64        #[unsafe(no_mangle)]
65        #[inline]
66        fn esp_rtos_initialized() -> bool {
67            <$t as $crate::Scheduler>::initialized(&$driver)
68        }
69
70        #[unsafe(no_mangle)]
71        #[inline]
72        fn esp_rtos_yield_task() {
73            <$t as $crate::Scheduler>::yield_task(&$driver)
74        }
75
76        #[unsafe(no_mangle)]
77        #[inline]
78        fn esp_rtos_yield_task_from_isr() {
79            <$t as $crate::Scheduler>::yield_task_from_isr(&$driver)
80        }
81
82        #[unsafe(no_mangle)]
83        #[inline]
84        fn esp_rtos_current_task() -> *mut c_void {
85            <$t as $crate::Scheduler>::current_task(&$driver)
86        }
87
88        #[unsafe(no_mangle)]
89        #[inline]
90        fn esp_rtos_max_task_priority() -> u32 {
91            <$t as $crate::Scheduler>::max_task_priority(&$driver)
92        }
93
94        #[unsafe(no_mangle)]
95        #[inline]
96        fn esp_rtos_task_create(
97            name: &str,
98            task: extern "C" fn(*mut c_void),
99            param: *mut c_void,
100            priority: u32,
101            core_id: Option<u32>,
102            task_stack_size: usize,
103        ) -> *mut c_void {
104            <$t as $crate::Scheduler>::task_create(
105                &$driver,
106                name,
107                task,
108                param,
109                priority,
110                core_id,
111                task_stack_size,
112            )
113        }
114
115        #[unsafe(no_mangle)]
116        #[inline]
117        fn esp_rtos_schedule_task_deletion(task_handle: *mut c_void) {
118            <$t as $crate::Scheduler>::schedule_task_deletion(&$driver, task_handle)
119        }
120
121        #[unsafe(no_mangle)]
122        #[inline]
123        fn esp_rtos_current_task_thread_semaphore() -> $crate::semaphore::SemaphorePtr {
124            <$t as $crate::Scheduler>::current_task_thread_semaphore(&$driver)
125        }
126
127        #[unsafe(no_mangle)]
128        #[inline]
129        fn esp_rtos_usleep(us: u32) {
130            <$t as $crate::Scheduler>::usleep(&$driver, us)
131        }
132
133        #[unsafe(no_mangle)]
134        #[inline]
135        fn esp_rtos_now() -> u64 {
136            <$t as $crate::Scheduler>::now(&$driver)
137        }
138    };
139}
140
141/// The scheduler interface.
142///
143/// This trait needs to be implemented by a driver crate to integrate esp-radio with a software
144/// platform.
145///
146/// The following snippet demonstrates the boilerplate necessary to implement a scheduler using the
147/// `Scheduler` trait:
148///
149/// ```rust,no_run
150/// struct MyScheduler {}
151///
152/// impl esp_radio_rtos_driver::Scheduler for MyScheduler {
153///
154///     fn initialized(&self) -> bool {
155///         unimplemented!()
156///     }
157///
158///     fn yield_task(&self) {
159///         unimplemented!()
160///     }
161///
162///     fn yield_task_from_isr(&self) {
163///         unimplemented!()
164///     }
165///
166///     fn max_task_priority(&self) -> u32 {
167///         unimplemented!()
168///     }
169///
170///     fn task_create(
171///        &self,
172///        name: &str,
173///        task: extern "C" fn(*mut c_void),
174///        param: *mut c_void,
175///        priority: u32,
176///        pin_to_core: Option<u32>,
177///        task_stack_size: usize,
178///     ) -> *mut c_void {
179///         unimplemented!()
180///     }
181///
182///     fn current_task(&self) -> *mut c_void {
183///         unimplemented!()
184///     }
185///
186///     fn schedule_task_deletion(&self, task_handle: *mut c_void) {
187///         unimplemented!()
188///     }
189///
190///     fn current_task_thread_semaphore(&self) -> SemaphorePtr {
191///         unimplemented!()
192///     }
193///
194///     fn usleep(&self, us: u32) {
195///         unimplemented!()
196///     }
197///
198///     fn now(&self) -> u64 {
199///         unimplemented!()
200///     }
201/// }
202///
203/// esp_radio_rtos_driver::scheduler_impl!(static SCHEDULER: MyScheduler = MyScheduler {});
204/// ```
205pub trait Scheduler: Send + Sync + 'static {
206    /// This function is called by `esp_radio::init` to verify that the scheduler is properly set
207    /// up.
208    fn initialized(&self) -> bool;
209
210    /// This function is called by `esp_radio` to yield control to another task.
211    fn yield_task(&self);
212
213    /// This function is called by `esp_radio` to yield control to another task.
214    fn yield_task_from_isr(&self);
215
216    /// This function is called by `esp_radio::init` to retrieve a pointer to the current task.
217    fn current_task(&self) -> *mut c_void;
218
219    /// This function returns the maximum task priority level.
220    /// Higher number is considered to be higher priority.
221    fn max_task_priority(&self) -> u32;
222
223    /// This function is used to create threads.
224    /// It should allocate the stack.
225    fn task_create(
226        &self,
227        name: &str,
228        task: extern "C" fn(*mut c_void),
229        param: *mut c_void,
230        priority: u32,
231        core_id: Option<u32>,
232        task_stack_size: usize,
233    ) -> *mut c_void;
234
235    /// This function is called to let the scheduler know this thread is not
236    /// needed anymore and should be deleted. After this function is called,
237    /// the thread should not be scheduled anymore. The thread stack can be
238    /// free'ed.
239    ///
240    /// Passing `null` as the task handle should delete the currently running task.
241    fn schedule_task_deletion(&self, task_handle: *mut c_void);
242
243    /// This function should return an opaque per-thread pointer to an
244    /// usize-sized memory location, which will be used to store a pointer
245    /// to a semaphore for this thread.
246    fn current_task_thread_semaphore(&self) -> SemaphorePtr;
247
248    /// This function is called by a task to sleep for the specified number of microseconds.
249    fn usleep(&self, us: u32);
250
251    /// Returns the current timestamp in microseconds.
252    ///
253    /// The underlying timer is expected not to overflow during the lifetime of the program.
254    ///
255    /// The clock that generates this timestamp must be the same one used to trigger timer events.
256    fn now(&self) -> u64;
257}
258
259// API used (mostly) by esp-radio
260
261/// Returns whether the task scheduler has been initialized.
262#[inline]
263pub fn initialized() -> bool {
264    unsafe { esp_rtos_initialized() }
265}
266
267/// Yields control to another task.
268#[inline]
269pub fn yield_task() {
270    unsafe { esp_rtos_yield_task() }
271}
272
273/// Yields control to another task for an interrupt.
274#[inline]
275pub fn yield_task_from_isr() {
276    unsafe { esp_rtos_yield_task_from_isr() }
277}
278
279/// Returns a pointer to the current task.
280#[inline]
281pub fn current_task() -> *mut c_void {
282    unsafe { esp_rtos_current_task() }
283}
284
285/// Returns the maximum priority a task can have.
286///
287/// This function assumes that a bigger number means higher priority.
288#[inline]
289pub fn max_task_priority() -> u32 {
290    unsafe { esp_rtos_max_task_priority() }
291}
292
293/// Creates a new task with the given initial parameter and stack size.
294///
295/// ## Safety
296///
297/// The `param` parameter must be valid for the lifetime of the task. The data
298/// pointed to by `param` needs to be `Send` and the task takes ownership over it.
299#[inline]
300pub unsafe fn task_create(
301    name: &str,
302    task: extern "C" fn(*mut c_void),
303    param: *mut c_void,
304    priority: u32,
305    pin_to_core: Option<u32>,
306    task_stack_size: usize,
307) -> *mut c_void {
308    unsafe { esp_rtos_task_create(name, task, param, priority, pin_to_core, task_stack_size) }
309}
310
311/// Schedules the given task for deletion.
312///
313/// Passing `null` will schedule the current task to be deleted.
314///
315/// ## Safety
316///
317/// The `task_handle` must be a pointer to a task, obtained either by calling [`task_create`] or
318/// [`current_task`].
319#[inline]
320pub unsafe fn schedule_task_deletion(task_handle: *mut c_void) {
321    unsafe { esp_rtos_schedule_task_deletion(task_handle) }
322}
323
324/// Returns a pointer to the current thread's semaphore.
325#[inline]
326pub fn current_task_thread_semaphore() -> SemaphorePtr {
327    unsafe { esp_rtos_current_task_thread_semaphore() }
328}
329
330/// Puts the current task to sleep for the specified number of microseconds.
331#[inline]
332pub fn usleep(us: u32) {
333    unsafe { esp_rtos_usleep(us) }
334}
335
336/// Returns the current timestamp, in microseconds.
337#[inline]
338pub fn now() -> u64 {
339    unsafe { esp_rtos_now() }
340}