Add more implementation docs (#4078)

This commit is contained in:
Dániel Buga 2025-09-08 15:46:57 +02:00 committed by GitHub
parent e15e837246
commit 29ded39fe2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 354 additions and 27 deletions

View File

@ -12,26 +12,11 @@
//! This crate abstracts the capabilities of FreeRTOS. The scheduler must implement the following
//! capabilities:
//!
//! - A preemptive task scheduler
//! - Mutexes
//! - Semaphores
//! - Queues
//! - Timers (functions that are executed at a specific time)
//!
//! In order to hook up a scheduler, implement the `Scheduler` trait for a struct, and register it
//! using the `scheduler_impl!()` macro. Only one scheduler can be registered in a firmware.
//!
//! Example:
//!
//! ```rust,ignore
//! struct MyScheduler {}
//!
//! impl esp_preempt::Scheduler for MyScheduler {
//! // impl goes here
//! }
//!
//! esp_preempt::scheduler_impl!(static SCHEDULER: MyScheduler = MyScheduler {});
//! ```
//! - A preemptive task scheduler: [`Scheduler`]
//! - Mutexes: [`mutex::MutexImplementation`]
//! - Semaphores: [`semaphore::SemaphoreImplementation`]
//! - Queues: [`queue::QueueImplementation`]
//! - Timers (functions that are executed at a specific time): [`timer::TimerImplementation`]
//!
//! [`esp-preempt`]: https://crates.io/crates/esp-preempt
@ -170,6 +155,73 @@ macro_rules! scheduler_impl {
///
/// This trait needs to be implemented by a driver crate to integrate esp-radio with a software
/// platform.
///
/// The following snippet demonstrates the boilerplate necessary to implement a scheduler using the
/// `Scheduler` trait:
///
/// ```rust,no_run
/// struct MyScheduler {}
///
/// impl esp_radio_preempt_driver::Scheduler for MyScheduler {
///
/// fn initialized(&self) -> bool {
/// unimplemented!()
/// }
///
/// fn enable(&self) {
/// unimplemented!()
/// }
///
/// fn disable(&self) {
/// unimplemented!()
/// }
///
/// fn yield_task(&self) {
/// unimplemented!()
/// }
///
/// fn yield_task_from_isr(&self) {
/// unimplemented!()
/// }
///
/// fn max_task_priority(&self) -> u32 {
/// unimplemented!()
/// }
///
/// fn task_create(
/// &self,
/// task: extern "C" fn(*mut c_void),
/// param: *mut c_void,
/// priority: u32,
/// pin_to_core: Option<u32>,
/// task_stack_size: usize,
/// ) -> *mut c_void {
/// unimplemented!()
/// }
///
/// fn current_task(&self) -> *mut c_void {
/// unimplemented!()
/// }
///
/// fn schedule_task_deletion(&self, task_handle: *mut c_void) {
/// unimplemented!()
/// }
///
/// fn current_task_thread_semaphore(&self) -> SemaphorePtr {
/// unimplemented!()
/// }
///
/// fn usleep(&self, us: u32) {
/// unimplemented!()
/// }
///
/// fn now(&self) -> u64 {
/// unimplemented!()
/// }
/// }
///
/// esp_radio_preempt_driver::scheduler_impl!(static SCHEDULER: MyScheduler = MyScheduler {});
/// ```
pub trait Scheduler: Send + Sync + 'static {
/// This function is called by `esp_radio::init` to verify that the scheduler is properly set
/// up.

View File

@ -1,4 +1,20 @@
//! Mutexes
//! # Mutexes (mutual exclusion)
//!
//! Mutexes are a synchronization primitive used to protect shared data from concurrent access.
//! They allow only one thread at a time to access a critical section of code or data.
//!
//! ## Implementation
//!
//! Implement the `MutexImplementation` trait for an object, and use the
//! `register_mutex_implementation` to register that implementation for esp-radio.
//!
//! See the [`MutexImplementation`] documentation for more information.
//!
//! ## Usage
//!
//! Users should use [`MutexHandle`] to interact with mutexes created by the driver implementation.
//!
//! > Note that the only expected user of this crate is esp-radio.
use core::ptr::NonNull;
@ -13,8 +29,48 @@ unsafe extern "Rust" {
fn esp_preempt_mutex_unlock(mutex: MutexPtr) -> bool;
}
/// A mutex (mutual exclusion) primitive.
///
/// The following snippet demonstrates the boilerplate necessary to implement a mutex using the
/// `MutexImplementation` trait:
///
/// ```rust,no_run
/// use esp_radio_preempt_driver::{
/// mutex::{MutexImplementation, MutexPtr},
/// register_mutex_implementation,
/// };
///
/// struct MyMutex {
/// // Mutex implementation details
/// }
///
/// impl MutexImplementation for MyMutex {
/// fn create(recursive: bool) -> MutexPtr {
/// unimplemented!()
/// }
///
/// unsafe fn delete(mutex: MutexPtr) {
/// unimplemented!()
/// }
///
/// unsafe fn lock(mutex: MutexPtr, timeout_us: Option<u32>) -> bool {
/// unimplemented!()
/// }
///
/// unsafe fn unlock(mutex: MutexPtr) -> bool {
/// unimplemented!()
/// }
/// }
///
/// register_mutex_implementation!(MyMutex);
/// ```
pub trait MutexImplementation {
/// Creates a new mutex instance.
///
/// The mutex should start in the unlocked state.
///
/// If `recursive` is `true`, the mutex should support recursive locking (i.e. the mutex owner
/// can lock the mutex multiple times).
fn create(recursive: bool) -> MutexPtr;
/// Deletes a mutex instance.
@ -34,7 +90,7 @@ pub trait MutexImplementation {
///
/// This function returns `true` if the mutex was locked, `false` if the timeout was reached.
///
/// Recursive mutexes can be re-locked by the mutex owner.
/// Recursive mutexes can be re-locked by the mutex owner without blocking.
///
/// # Safety
///
@ -44,7 +100,8 @@ pub trait MutexImplementation {
/// Unlocks a mutex.
///
/// This function returns `true` if the mutex was unlocked, `false` if the mutex wasn't locked,
/// or the unlocking task was not the mutex owner.
/// or the unlocking task was not the mutex owner. Unlocking a recursive mutex also returns
/// `true` if the mutex's lock counter is successfully decremented.
///
/// Recursive mutexes are released only when `unlock` has been called for each preceding `lock`.
///
@ -83,6 +140,9 @@ macro_rules! register_mutex_implementation {
};
}
/// Mutex handle.
///
/// This handle is used to interact with mutexes created by the driver implementation.
#[repr(transparent)]
pub struct MutexHandle(MutexPtr);
impl MutexHandle {

View File

@ -1,4 +1,20 @@
//! Queues
//! # Queues
//!
//! Queues are a synchronization primitive used to communicate between tasks.
//! They allow tasks to send and receive data in a first-in-first-out (FIFO) manner.
//!
//! ## Implementation
//!
//! Implement the `QueueImplementation` trait for an object, and use the
//! `register_queue_implementation` to register that implementation for esp-radio.
//!
//! See the [`QueueImplementation`] documentation for more information.
//!
//! ## Usage
//!
//! Users should use [`QueueHandle`] to interact with queues created by the driver implementation.
//!
//! > Note that the only expected user of this crate is esp-radio.
use core::ptr::NonNull;
@ -34,8 +50,73 @@ unsafe extern "Rust" {
fn esp_preempt_queue_messages_waiting(queue: QueuePtr) -> usize;
}
/// A queue primitive.
///
/// The following snippet demonstrates the boilerplate necessary to implement a queue using the
/// `QueueImplementation` trait:
///
/// ```rust,no_run
/// use esp_radio_preempt_driver::{
/// queue::{QueueImplementation, QueuePtr},
/// register_queue_implementation,
/// };
///
/// struct MyQueue {
/// // Queue implementation details
/// }
///
/// impl QueueImplementation for MyQueue {
/// fn create(capacity: usize, item_size: usize) -> QueuePtr {
/// unimplemented!()
/// }
///
/// unsafe fn delete(queue: QueuePtr) {
/// unimplemented!()
/// }
///
/// unsafe fn send_to_front(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool {
/// unimplemented!()
/// }
///
/// unsafe fn send_to_back(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool {
/// unimplemented!()
/// }
///
/// unsafe fn try_send_to_back_from_isr(
/// queue: QueuePtr,
/// item: *const u8,
/// higher_prio_task_waken: Option<&mut bool>,
/// ) -> bool {
/// unimplemented!()
/// }
///
/// unsafe fn receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool {
/// unimplemented!()
/// }
///
/// unsafe fn try_receive_from_isr(
/// queue: QueuePtr,
/// item: *mut u8,
/// higher_prio_task_waken: Option<&mut bool>,
/// ) -> bool {
/// unimplemented!()
/// }
///
/// unsafe fn remove(queue: QueuePtr, item: *const u8) {
/// unimplemented!()
/// }
///
/// fn messages_waiting(queue: QueuePtr) -> usize {
/// unimplemented!()
/// }
/// }
///
/// register_queue_implementation!(MyQueue);
/// ```
pub trait QueueImplementation {
/// Creates a new queue instance.
/// Creates a new, empty queue instance.
///
/// The queue must have a capacity for `capacity` number of `item_size` byte items.
fn create(capacity: usize, item_size: usize) -> QueuePtr;
/// Deletes a queue instance.
@ -227,6 +308,9 @@ macro_rules! register_queue_implementation {
};
}
/// Queue handle.
///
/// This handle is used to interact with queues created by the driver implementation.
#[repr(transparent)]
pub struct QueueHandle(QueuePtr);
impl QueueHandle {

View File

@ -1,4 +1,22 @@
//! Semaphores
//! Counting Semaphores
//!
//! Semaphores are synchronization primitives that allow threads to coordinate their execution.
//! They are used to control access to a shared resource by limiting the number of threads that can
//! access it simultaneously.
//!
//! ## Implementation
//!
//! Implement the `SemaphoreImplementation` trait for an object, and use the
//! `register_semaphore_implementation` to register that implementation for esp-radio.
//!
//! See the [`SemaphoreImplementation`] documentation for more information.
//!
//! ## Usage
//!
//! Users should use [`SemaphoreHandle`] to interact with semaphores created by the driver
//! implementation.
//!
//! > Note that the only expected user of this crate is esp-radio.
use core::ptr::NonNull;
@ -24,6 +42,63 @@ unsafe extern "Rust" {
) -> bool;
}
/// A counting semaphore primitive.
///
/// The following snippet demonstrates the boilerplate necessary to implement a semaphore using the
/// `SemaphoreImplementation` trait:
///
/// ```rust,no_run
/// use esp_radio_preempt_driver::{
/// register_semaphore_implementation,
/// semaphore::{SemaphoreImplementation, SemaphorePtr},
/// };
///
/// struct MySemaphore {
/// // Semaphore implementation details
/// }
///
/// impl SemaphoreImplementation for MySemaphore {
/// fn create(max: u32, initial: u32) -> SemaphorePtr {
/// unimplemented!()
/// }
///
/// unsafe fn delete(semaphore: SemaphorePtr) {
/// unimplemented!()
/// }
///
/// unsafe fn take(semaphore: SemaphorePtr, timeout_us: Option<u32>) -> bool {
/// unimplemented!()
/// }
///
/// unsafe fn give(semaphore: SemaphorePtr) -> bool {
/// unimplemented!()
/// }
///
/// unsafe fn try_give_from_isr(
/// semaphore: SemaphorePtr,
/// higher_prio_task_waken: Option<&mut bool>,
/// ) -> bool {
/// unimplemented!()
/// }
///
/// unsafe fn current_count(semaphore: SemaphorePtr) -> u32 {
/// unimplemented!()
/// }
///
/// unsafe fn try_take(semaphore: SemaphorePtr) -> bool {
/// unimplemented!()
/// }
///
/// unsafe fn try_take_from_isr(
/// semaphore: SemaphorePtr,
/// higher_prio_task_waken: Option<&mut bool>,
/// ) -> bool {
/// unimplemented!()
/// }
/// }
///
/// register_semaphore_implementation!(MySemaphore);
/// ```
pub trait SemaphoreImplementation {
/// Creates a new semaphore instance.
fn create(max: u32, initial: u32) -> SemaphorePtr;
@ -182,10 +257,15 @@ macro_rules! register_semaphore_implementation {
};
}
/// Semaphore handle.
///
/// This handle is used to interact with semaphores created by the driver implementation.
#[repr(transparent)]
pub struct SemaphoreHandle(SemaphorePtr);
impl SemaphoreHandle {
/// Creates a new semaphore instance.
///
/// The semaphore will have the specified initial and maximum values.
pub fn new(initial: u32, max: u32) -> Self {
let ptr = unsafe { esp_preempt_semaphore_create(initial, max) };
Self(ptr)

View File

@ -1,4 +1,17 @@
//! Timers
//! Timers (callbacks scheduled to run in the future)
//!
//! ## Implementation
//!
//! Implement the `TimerImplementation` trait for an object, and use the
//! `register_timer_implementation` to register that implementation for esp-radio.
//!
//! See the [`TimerImplementation`] documentation for more information.
//!
//! ## Usage
//!
//! Users should use [`TimerHandle`] to interact with timers created by the driver implementation.
//!
//! > Note that the only expected user of this crate is esp-radio.
use core::{ffi::c_void, ptr::NonNull};
@ -16,6 +29,41 @@ unsafe extern "Rust" {
fn esp_preempt_timer_disarm(timer: TimerPtr);
}
/// A timer implementation.
///
/// The following snippet demonstrates the boilerplate necessary to implement a timer using the
/// `TimerImplementation` trait:
///
/// ```rust,no_run
/// use esp_radio_preempt_driver::{
/// register_timer_implementation,
/// timer::{TimerImplementation, TimerPtr},
/// };
///
/// struct MyTimer {
/// // Timer implementation details
/// }
///
/// impl TimerImplementation for MyTimer {
/// fn create(function: unsafe extern "C" fn(*mut c_void), data: *mut c_void) -> TimerPtr {
/// unimplemented!()
/// }
///
/// unsafe fn delete(mutex: MutexPtr) {
/// unimplemented!()
/// }
///
/// unsafe fn arm(timer: TimerPtr, timeout: u64, periodic: bool) -> bool {
/// unimplemented!()
/// }
///
/// unsafe fn disarm(timer: TimerPtr) -> bool {
/// unimplemented!()
/// }
/// }
///
/// register_timer_implementation!(MyTimer);
/// ```
pub trait TimerImplementation {
/// Creates a new timer instance from the given callback.
fn create(function: unsafe extern "C" fn(*mut c_void), data: *mut c_void) -> TimerPtr;
@ -77,6 +125,9 @@ macro_rules! register_timer_implementation {
};
}
/// A timer handle.
///
/// This handle is used to interact with timers created by the driver implementation.
#[repr(transparent)]
pub struct TimerHandle(TimerPtr);
impl TimerHandle {