use alloc::boxed::Box;
use alloc::ffi::CString;
use alloc::string::String;
use core::ffi::CStr;
use core::ptr::null_mut;
use veecle_freertos_sys::bindings::{
StackType_t, TaskHandle_t, UBaseType_t, eNotifyAction, eNotifyAction_eIncrement,
eNotifyAction_eNoAction, eNotifyAction_eSetBits, eNotifyAction_eSetValueWithOverwrite,
eNotifyAction_eSetValueWithoutOverwrite, pdFALSE, pdTRUE, shim_pcTaskGetName,
shim_ulTaskNotifyTake, shim_xTaskNotify, shim_xTaskNotifyFromISR, shim_xTaskNotifyWait,
uxTaskGetStackHighWaterMark, uxTaskGetTaskNumber, vTaskDelay, vTaskSetTaskNumber, vTaskSuspend,
xTaskCreate, xTaskGetCurrentTaskHandle,
};
pub use self::block_on_future::block_on_future;
use crate::units::Duration;
use crate::{FreeRtosError, InterruptContext};
mod block_on_future;
unsafe impl Send for Task {}
unsafe impl Sync for Task {}
#[allow(clippy::new_without_default)]
#[derive(Debug, Clone)]
pub struct Task {
task_handle: TaskHandle_t,
}
#[derive(Debug, Copy, Clone)]
pub struct TaskPriority(pub UBaseType_t);
#[derive(Debug, Copy, Clone)]
pub enum TaskNotification {
NoAction,
SetBits(u32),
Increment,
OverwriteValue(u32),
SetValue(u32),
}
impl TaskNotification {
fn to_freertos(self) -> (u32, eNotifyAction) {
match self {
TaskNotification::NoAction => (0, eNotifyAction_eNoAction),
TaskNotification::SetBits(v) => (v, eNotifyAction_eSetBits),
TaskNotification::Increment => (0, eNotifyAction_eIncrement),
TaskNotification::OverwriteValue(v) => (v, eNotifyAction_eSetValueWithOverwrite),
TaskNotification::SetValue(v) => (v, eNotifyAction_eSetValueWithoutOverwrite),
}
}
}
impl TaskPriority {
fn to_freertos(self) -> UBaseType_t {
self.0
}
}
#[allow(clippy::new_without_default)]
#[derive(Debug)]
pub struct TaskBuilder {
task_name: CString,
task_stack_size: StackType_t,
task_priority: TaskPriority,
}
impl TaskBuilder {
pub fn name(&mut self, name: &CStr) -> &mut Self {
self.task_name = name.into();
self
}
pub fn stack_size(&mut self, stack_size: StackType_t) -> &mut Self {
self.task_stack_size = stack_size;
self
}
pub fn priority(&mut self, priority: TaskPriority) -> &mut Self {
self.task_priority = priority;
self
}
pub fn start<F>(&self, func: F) -> Result<Task, FreeRtosError>
where
F: FnOnce(Task),
F: Send + 'static,
{
Task::spawn(
&self.task_name,
self.task_stack_size,
self.task_priority,
func,
)
}
}
impl Task {
const fn assert_no_task_deletion() {
const {
assert!(
!cfg!(INCLUDE_vTaskDelete),
"it is not possible to have a safe wrapper around tasks that may be deleted at any time, you must \
disable `INCLUDE_vTaskDelete` to use `Task`"
)
}
}
#[allow(clippy::new_ret_no_self)]
pub fn new() -> TaskBuilder {
TaskBuilder {
task_name: c"rust_task".into(),
task_stack_size: 1024,
task_priority: TaskPriority(1),
}
}
#[inline]
pub unsafe fn from_raw_handle(handle: TaskHandle_t) -> Self {
Self {
task_handle: handle,
}
}
#[inline]
pub fn raw_handle(&self) -> TaskHandle_t {
self.task_handle
}
unsafe fn spawn_inner(
f: Box<dyn FnOnce(Task)>,
name: &CStr,
stack_size: StackType_t,
priority: TaskPriority,
) -> Result<Task, FreeRtosError> {
let f = Box::new(f);
let param_ptr = Box::into_raw(f);
let (success, task_handle) = {
let mut task_handle = core::ptr::null_mut();
let ret = unsafe {
xTaskCreate(
Some(thread_start),
name.as_ptr(),
stack_size,
param_ptr.cast(),
priority.to_freertos(),
&mut task_handle,
)
};
(ret == pdTRUE(), task_handle)
};
if !success {
drop(unsafe { Box::from_raw(param_ptr) });
return Err(FreeRtosError::OutOfMemory);
}
use core::ffi::c_void;
extern "C" fn thread_start(main: *mut c_void) {
let task_main_function = unsafe { Box::from_raw(main.cast::<Box<dyn FnOnce(Task)>>()) };
task_main_function(
Task::current().expect("in a task, the current task should be available"),
);
panic!("Not allowed to quit the task!");
}
Ok(Task { task_handle })
}
fn spawn<F>(
name: &CStr,
stack_size: StackType_t,
priority: TaskPriority,
f: F,
) -> Result<Task, FreeRtosError>
where
F: FnOnce(Task),
F: Send + 'static,
{
unsafe { Task::spawn_inner(Box::new(f), name, stack_size, priority) }
}
#[allow(clippy::result_unit_err)]
pub fn get_name(&self) -> Result<String, ()> {
Task::assert_no_task_deletion();
let name_ptr = unsafe { shim_pcTaskGetName(self.task_handle) };
unsafe { CStr::from_ptr(name_ptr) }
.to_str()
.map_err(|_| ())
.map(String::from)
}
pub fn current() -> Result<Task, FreeRtosError> {
let task_handle = unsafe { xTaskGetCurrentTaskHandle() };
if task_handle.is_null() {
return Err(FreeRtosError::TaskNotFound);
}
Ok(Task { task_handle })
}
pub fn set_notification_value(&self, val: u32) {
self.notify(TaskNotification::OverwriteValue(val))
}
pub fn notify(&self, notification: TaskNotification) {
let (value, action) = notification.to_freertos();
Task::assert_no_task_deletion();
unsafe { shim_xTaskNotify(self.task_handle, value, action) };
}
pub fn notify_from_isr(
&self,
context: &mut InterruptContext,
notification: TaskNotification,
) -> Result<(), FreeRtosError> {
let (value, action) = notification.to_freertos();
Task::assert_no_task_deletion();
if unsafe {
shim_xTaskNotifyFromISR(
self.task_handle,
value,
action,
context.get_task_field_mut(),
)
} == pdTRUE()
{
Ok(())
} else {
Err(FreeRtosError::QueueFull)
}
}
pub fn wait_for_notification(
&self,
clear_bits_enter: u32,
clear_bits_exit: u32,
wait_for: Duration,
) -> Result<u32, FreeRtosError> {
let mut val = 0;
if unsafe {
shim_xTaskNotifyWait(
clear_bits_enter,
clear_bits_exit,
&mut val as *mut _,
wait_for.ticks(),
)
} == pdTRUE()
{
Ok(val)
} else {
Err(FreeRtosError::Timeout)
}
}
pub fn get_stack_high_water_mark(&self) -> UBaseType_t {
Task::assert_no_task_deletion();
unsafe { uxTaskGetStackHighWaterMark(self.task_handle) as UBaseType_t }
}
pub unsafe fn get_id(&self) -> UBaseType_t {
Task::assert_no_task_deletion();
unsafe { uxTaskGetTaskNumber(self.task_handle) }
}
pub unsafe fn set_id(&self, value: UBaseType_t) {
Task::assert_no_task_deletion();
unsafe { vTaskSetTaskNumber(self.task_handle, value) };
}
}
#[derive(Debug)]
pub struct CurrentTask;
impl CurrentTask {
pub fn delay(delay: Duration) {
vTaskDelay(delay.ticks());
}
pub fn suspend() {
unsafe { vTaskSuspend(null_mut()) }
}
pub fn take_notification(clear: bool, wait_for: Duration) -> u32 {
let clear = if clear { pdTRUE() } else { pdFALSE() };
unsafe { shim_ulTaskNotifyTake(clear, wait_for.ticks()) }
}
pub fn get_stack_high_water_mark() -> UBaseType_t {
unsafe { uxTaskGetStackHighWaterMark(null_mut()) }
}
}