diff --git a/esp-radio-preempt-driver/src/lib.rs b/esp-radio-preempt-driver/src/lib.rs index df44d2fd1..178997c5d 100644 --- a/esp-radio-preempt-driver/src/lib.rs +++ b/esp-radio-preempt-driver/src/lib.rs @@ -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, +/// 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. diff --git a/esp-radio-preempt-driver/src/mutex.rs b/esp-radio-preempt-driver/src/mutex.rs index b98987ae4..29ad4e1df 100644 --- a/esp-radio-preempt-driver/src/mutex.rs +++ b/esp-radio-preempt-driver/src/mutex.rs @@ -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) -> 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 { diff --git a/esp-radio-preempt-driver/src/queue.rs b/esp-radio-preempt-driver/src/queue.rs index 0f1faace3..ab7067378 100644 --- a/esp-radio-preempt-driver/src/queue.rs +++ b/esp-radio-preempt-driver/src/queue.rs @@ -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) -> bool { +/// unimplemented!() +/// } +/// +/// unsafe fn send_to_back(queue: QueuePtr, item: *const u8, timeout_us: Option) -> 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) -> 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 { diff --git a/esp-radio-preempt-driver/src/semaphore.rs b/esp-radio-preempt-driver/src/semaphore.rs index e2e7f9f1a..9b299187f 100644 --- a/esp-radio-preempt-driver/src/semaphore.rs +++ b/esp-radio-preempt-driver/src/semaphore.rs @@ -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) -> 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) diff --git a/esp-radio-preempt-driver/src/timer.rs b/esp-radio-preempt-driver/src/timer.rs index f295e707d..2af8c324e 100644 --- a/esp-radio-preempt-driver/src/timer.rs +++ b/esp-radio-preempt-driver/src/timer.rs @@ -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 {