Skip to main content

osal_rs/freertos/
ffi.rs

1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20
21//! Foreign Function Interface (FFI) bindings for FreeRTOS.
22//!
23//! This module provides raw FFI declarations for FreeRTOS kernel functions and types.
24//! It directly interfaces with the FreeRTOS C API, providing the foundation for
25//! the safe Rust wrappers in other modules.
26//!
27//! # Contents
28//!
29//! - **Type definitions**: Handles for tasks, queues, semaphores, etc.
30//! - **Constants**: FreeRTOS constants (pdTRUE, pdFALSE, etc.)
31//! - **Function declarations**: Direct bindings to FreeRTOS C functions
32//! - **Utility macros**: Rust macros wrapping common FreeRTOS patterns
33//!
34//! # Safety
35//!
36//! All functions in this module are `unsafe` and require careful handling:
37//! - Null pointer checks
38//! - Proper synchronization
39//! - Correct memory management
40//! - Valid handle usage
41//!
42//! Use the safe wrappers in parent modules instead of calling these directly.
43//!
44//! # Examples
45//!
46//! ```ignore
47//! // Don't use FFI directly - use safe wrappers instead:
48//! use osal_rs::os::{Thread, ThreadFn};
49//! 
50//! // This is safe:
51//! let thread = Thread::new("task", 1024, 5);
52//! 
53//! // This is unsafe and should be avoided:
54//! // unsafe { xTaskCreate(...) }
55//! ```
56
57#![allow(non_upper_case_globals)]
58#![allow(non_snake_case)]
59
60use core::ffi::{c_char, c_uint, c_void};
61use core::ptr;
62
63use super::types::{BaseType, StackType, UBaseType, TickType, EventBits};
64
65/// Opaque handle to a FreeRTOS task/thread
66pub type ThreadHandle = *const c_void;
67/// Opaque handle to a FreeRTOS queue
68pub type QueueHandle = *const c_void;
69/// Opaque handle to a FreeRTOS semaphore
70pub type SemaphoreHandle = *const c_void;
71/// Opaque handle to a FreeRTOS event group
72pub type EventGroupHandle = *const c_void;
73/// Opaque handle to a FreeRTOS timer
74pub type TimerHandle = *const c_void;
75/// Opaque handle to a FreeRTOS mutex
76pub type MutexHandle = *const c_void;
77/// Callback function type for timers
78pub type TimerCallback = unsafe extern "C" fn(timer: TimerHandle);
79/// Task state enumeration
80pub type TaskState = c_uint;
81
82// Task states
83pub mod task {
84    use super::TaskState;
85
86    pub const RUNNING: TaskState = 0;
87    pub const READY: TaskState = 1;
88    pub const BLOCKED: TaskState = 2;
89    pub const SUSPENDED: TaskState = 3;
90    pub const DELETED: TaskState = 4;
91    pub const INVALID: TaskState = 5;
92}
93
94// Boolean/status constants
95pub const pdFALSE: BaseType = 0;
96
97pub const pdTRUE: BaseType = 1;
98
99pub const pdPASS: BaseType = pdTRUE;
100
101pub const pdFAIL: BaseType = pdFALSE;
102
103// Task notification constants
104pub const tskDEFAULT_INDEX_TO_NOTIFY: UBaseType = 0;
105
106// Semaphore constants
107// Queue constants
108#[allow(dead_code)]
109pub mod sem {
110    use super::TickType;
111
112    pub const BINARY_SEMAPHORE_QUEUE_LENGTH: u8 = 1;
113    pub const SEMAPHORE_QUEUE_ITEM_LENGTH: u8 = 0;
114    pub const GIVE_BLOCK_TIME: TickType = 0;
115}
116
117// Queue constants
118#[allow(dead_code)]
119pub mod queue {
120    use crate::os::types::BaseType;
121
122    pub const SEND_TO_BACK: BaseType = 0;
123    pub const SEND_TO_FRONT: BaseType = 1;
124    pub const OVERWRITE: BaseType = 2;
125    pub const QUEUE_TYPE_BASE: u8 = 0;
126    pub const QUEUE_TYPE_MUTEX: u8 = 1;
127    pub const QUEUE_TYPE_COUNTING_SEMAPHORE: u8 = 2;
128    pub const QUEUE_TYPE_BINARY_SEMAPHORE: u8 = 3;
129    pub const QUEUE_TYPE_RECURSIVE_MUTEX: u8 = 4;
130}
131
132
133
134
135
136/// Task status information structure.
137///
138/// Contains detailed information about a task's state, priority, stack usage, etc.
139#[repr(C)]
140#[derive(Debug, Copy, Clone)]
141pub struct TaskStatus {
142    /// Task handle
143    pub xHandle: ThreadHandle,
144    /// Task name (null-terminated C string)
145    pub pcTaskName: *const c_char,
146    /// Task number (unique ID)
147    pub xTaskNumber: UBaseType,
148    /// Current task state
149    pub eCurrentState: TaskState,
150    /// Current priority
151    pub uxCurrentPriority: UBaseType,
152    /// Base priority (before priority inheritance)
153    pub uxBasePriority: UBaseType,
154    /// Total runtime counter
155    pub ulRunTimeCounter: u32,
156    /// Stack base address
157    pub pxStackBase: *mut StackType,
158    /// Stack high water mark (minimum free stack)
159    pub usStackHighWaterMark: StackType
160}
161
162impl Default for TaskStatus {
163    fn default() -> Self {
164        TaskStatus {
165            xHandle: ptr::null(),
166            pcTaskName: ptr::null(),
167            xTaskNumber: 0,
168            eCurrentState: task::INVALID,
169            uxCurrentPriority: 0,
170            uxBasePriority: 0,
171            ulRunTimeCounter: 0,
172            pxStackBase: ptr::null_mut(),
173            usStackHighWaterMark: 0,
174        }
175    }
176}
177
178pub type TaskFunction = Option<unsafe extern "C" fn(arg: *mut c_void)>;
179
180unsafe extern "C" {
181    
182
183    /// Allocate memory from the  heap
184    /// 
185    /// # Arguments
186    /// * `size` - The number of bytes to allocate
187    /// 
188    /// # Returns
189    /// A pointer to the allocated memory, or null if allocation fails
190    pub fn pvPortMalloc(size: usize) -> *mut c_void;
191
192    /// Free memory previously allocated by pvPortMalloc
193    /// 
194    /// # Arguments
195    /// * `pv` - Pointer to the memory to free
196    pub fn vPortFree(pv: *mut c_void);
197
198    pub fn vTaskDelay(xTicksToDelay: TickType);
199
200    pub fn xTaskDelayUntil(
201        pxPreviousWakeTime: *mut TickType,
202        xTimeIncrement: TickType,
203    ) -> BaseType;
204
205
206    pub fn xTaskGetTickCount() -> TickType;
207
208    pub fn vTaskStartScheduler();
209
210    pub fn vTaskEndScheduler();
211
212    pub fn vTaskSuspendAll();
213
214    pub fn xTaskResumeAll() -> BaseType;
215
216    pub fn xTaskGetCurrentTaskHandle() -> ThreadHandle;
217
218    pub fn eTaskGetState(xTask: ThreadHandle) -> TaskState;
219
220    pub fn uxTaskGetNumberOfTasks() -> UBaseType;
221
222    pub fn uxTaskGetSystemState(
223        pxTaskStatusArray: *mut TaskStatus,
224        uxArraySize: UBaseType,
225        pulTotalRunTime: *mut u32,
226    ) -> UBaseType;
227
228    pub fn osal_rs_task_enter_critical();
229    pub fn osal_rs_task_exit_critical();
230
231    pub fn osal_rs_task_enter_critical_from_isr() -> UBaseType;
232    pub fn osal_rs_task_exit_critical_from_isr(uxSavedInterruptStatus: UBaseType);
233
234
235    pub fn xTaskCreate(
236        pxTaskCode: TaskFunction,
237        pcName: *const c_char,
238        uxStackDepth: StackType,
239        pvParameters: *mut c_void,
240        uxPriority: UBaseType,
241        pxCreatedTask: *mut ThreadHandle,
242    ) -> BaseType;
243
244    pub fn vTaskDelete(xTaskToDelete: ThreadHandle);
245
246    pub fn vTaskSuspend(xTaskToSuspend: ThreadHandle);
247
248    pub fn vTaskResume(xTaskToResume: ThreadHandle);
249
250    pub fn vTaskGetInfo(
251        xTask: ThreadHandle,
252        pxTaskStatus: *mut TaskStatus,
253        xGetFreeStackSpace: BaseType,
254        eState: TaskState,
255    );
256
257    // pub fn ulTaskGenericNotifyTake(uxIndexToWaitOn: UBaseType, xClearCountOnExit: BaseType, xTicksToWait: TickType) -> u32;
258
259
260    pub fn xTaskGenericNotifyWait(
261        uxIndexToWaitOn: UBaseType,
262        ulBitsToClearOnEntry: u32,
263        ulBitsToClearOnExit: u32,
264        pulNotificationValue: *mut u32,
265        xTicksToWait: TickType,
266    ) -> BaseType;
267
268
269    pub fn xTaskGenericNotify(
270        xTaskToNotify: ThreadHandle,
271        uxIndexToNotify: UBaseType,
272        ulValue: u32,
273        eAction: u32,
274        pulPreviousNotificationValue: *mut u32,
275    ) -> BaseType;
276
277
278    pub fn xTaskGenericNotifyFromISR(
279        xTaskToNotify: ThreadHandle,
280        uxIndexToNotify: UBaseType,
281        ulValue: u32,
282        eAction: u32,
283        pulPreviousNotificationValue: *mut u32,
284        pxHigherPriorityTaskWoken: *mut BaseType,
285    ) -> BaseType;
286    
287    pub fn xEventGroupWaitBits(
288        xEventGroup: EventGroupHandle,
289        uxBitsToWaitFor: EventBits,
290        xClearOnExit: BaseType,
291        xWaitForAllBits: BaseType,
292        xTicksToWait: TickType,
293    ) -> EventBits;
294
295    pub fn xEventGroupClearBits(
296        xEventGroup: EventGroupHandle,
297        uxBitsToClear: EventBits,
298    ) -> EventBits;
299
300    pub fn xEventGroupClearBitsFromISR(
301        xEventGroup: EventGroupHandle,
302        uxBitsToClear: EventBits,
303    ) -> BaseType;
304
305        pub fn xEventGroupSetBits(
306        xEventGroup: EventGroupHandle,
307        uxBitsToSet: EventBits,
308    ) -> EventBits;
309
310
311    pub fn xEventGroupSetBitsFromISR(
312        xEventGroup: EventGroupHandle,
313        uxBitsToSet: EventBits,
314        pxHigherPriorityTaskWoken: *mut BaseType,
315    ) -> BaseType;
316
317    pub fn xEventGroupGetBitsFromISR(xEventGroup: EventGroupHandle) -> EventBits;
318
319    pub fn vEventGroupDelete(xEventGroup: EventGroupHandle);
320
321    pub fn xEventGroupCreate() -> EventGroupHandle;
322
323    pub fn osal_rs_critical_section_enter();
324
325    pub fn osal_rs_critical_section_exit();
326
327    pub fn osal_rs_port_yield_from_isr(pxHigherPriorityTaskWoken: BaseType);
328
329    pub fn osal_rs_port_end_switching_isr( xSwitchRequired: BaseType );
330
331    pub fn xQueueCreateMutex(ucQueueType: u8) -> QueueHandle;
332    
333    pub fn xQueueCreateCountingSemaphore(
334        uxMaxCount: UBaseType,
335        uxInitialCount: UBaseType,
336    ) -> QueueHandle;
337
338    pub fn xQueueSemaphoreTake(xQueue: QueueHandle, xTicksToWait: TickType) -> BaseType;
339
340    pub fn xQueueReceiveFromISR(
341        xQueue: QueueHandle,
342        pvBuffer: *mut c_void,
343        pxHigherPriorityTaskWoken: *mut BaseType,
344    ) -> BaseType;
345
346    pub fn xQueueGenericSend(
347        xQueue: QueueHandle,
348        pvItemToQueue: *const c_void,
349        xTicksToWait: TickType,
350        xCopyPosition: BaseType,
351    ) -> BaseType;
352
353    pub fn xQueueGiveFromISR(
354        xQueue: QueueHandle,
355        pxHigherPriorityTaskWoken: *mut BaseType,
356    ) -> BaseType;
357
358     pub fn vQueueDelete(xQueue: QueueHandle);
359
360    pub fn xQueueGenericCreate(
361        uxQueueLength: UBaseType,
362        uxItemSize: UBaseType,
363        ucQueueType: u8,
364    ) -> QueueHandle;
365
366    pub fn xQueueReceive(
367        xQueue: QueueHandle,
368        pvBuffer: *mut c_void,
369        xTicksToWait: TickType,
370    ) -> BaseType;
371
372    pub fn xQueueGenericSendFromISR(
373        xQueue: QueueHandle,
374        pvItemToQueue: *const c_void,
375        pxHigherPriorityTaskWoken: *mut BaseType,
376        xCopyPosition: BaseType,
377    ) -> BaseType;
378
379    pub fn xQueueTakeMutexRecursive(xMutex: QueueHandle, xTicksToWait: TickType) -> BaseType;
380
381    pub fn xQueueGiveMutexRecursive(xMutex: QueueHandle) -> BaseType;
382
383    pub fn xPortGetFreeHeapSize() -> usize;
384
385    // pub fn xTimerCreateTimerTask() -> BaseType;
386
387    pub fn xTimerCreate(
388        pcTimerName: *const c_char,
389        xTimerPeriodInTicks: TickType,
390        xAutoReload: BaseType,
391        pvTimerID: *mut c_void,
392        pxCallbackFunction: Option<TimerCallback>,
393    ) -> TimerHandle;
394
395    pub fn osal_rs_timer_start(xTimer: TimerHandle, xTicksToWait: TickType) -> BaseType;
396
397    pub fn osal_rs_timer_stop(xTimer: TimerHandle, xTicksToWait: TickType) -> BaseType;
398
399    pub fn osal_rs_timer_reset(xTimer: TimerHandle, xTicksToWait: TickType) -> BaseType;
400
401    pub fn osal_rs_timer_change_period(
402        xTimer: TimerHandle,
403        xNewPeriodInTicks: TickType,
404        xTicksToWait: TickType,
405    ) -> BaseType;
406
407    pub fn osal_rs_timer_delete(xTimer: TimerHandle, xTicksToWait: TickType) -> BaseType;
408
409    pub fn pvTimerGetTimerID(xTimer: TimerHandle) -> *mut c_void;
410
411    // pub fn printf(fmt: *const u8, ...) -> i32; 
412}
413
414
415macro_rules! xTaskNotifyWait {
416    ($ulBitsToClearOnEntry:expr, $ulBitsToClearOnExit:expr, $pulNotificationValue:expr, $xTicksToWait:expr) => {
417        unsafe {
418            $crate::freertos::ffi::xTaskGenericNotifyWait(
419                $crate::freertos::ffi::tskDEFAULT_INDEX_TO_NOTIFY,
420                $ulBitsToClearOnEntry,
421                $ulBitsToClearOnExit,
422                $pulNotificationValue,
423                $xTicksToWait
424            )
425        }
426    };
427}
428
429macro_rules! xTaskNotify {
430    ($xTaskToNotify:expr, $ulValue:expr, $eAction:expr) => {
431        unsafe {
432            $crate::freertos::ffi::xTaskGenericNotify(
433                $xTaskToNotify,
434                $crate::freertos::ffi::tskDEFAULT_INDEX_TO_NOTIFY,
435                $ulValue,
436                $eAction,
437                core::ptr::null_mut()
438            )
439        }
440    };
441}
442
443macro_rules! xTaskNotifyFromISR {
444    ($xTaskToNotify:expr, $ulValue:expr, $eAction:expr, $pxHigherPriorityTaskWoken:expr) => {
445        unsafe {
446            $crate::freertos::ffi::xTaskGenericNotifyFromISR(
447                $xTaskToNotify,
448                $crate::freertos::ffi::tskDEFAULT_INDEX_TO_NOTIFY,
449                $ulValue,
450                $eAction,
451                core::ptr::null_mut(),
452                $pxHigherPriorityTaskWoken
453            )
454        }
455    };
456}
457
458
459macro_rules! xEventGroupGetBits {
460    ($xEventGroup:expr) => {
461        unsafe {
462            $crate::freertos::ffi::xEventGroupClearBits($xEventGroup, 0)
463        }
464    };
465}
466
467macro_rules! xSemaphoreCreateCounting {
468    ($uxMaxCount:expr, $uxInitialCount:expr) => {
469        unsafe {
470            $crate::freertos::ffi::xQueueCreateCountingSemaphore(
471                $uxMaxCount,
472                $uxInitialCount
473            )
474        }
475    };
476}
477
478macro_rules! xSemaphoreTake {
479    ($xSemaphore:expr, $xBlockTime:expr) => {
480        unsafe {
481            $crate::freertos::ffi::xQueueSemaphoreTake(
482                $xSemaphore,
483                $xBlockTime
484            )
485        }
486    };
487}
488
489macro_rules! xSemaphoreTakeFromISR {
490    ($xSemaphore:expr, $pxHigherPriorityTaskWoken:expr) => {
491        unsafe {
492            $crate::freertos::ffi::xQueueReceiveFromISR(
493                $xSemaphore,
494                core::ptr::null_mut(),
495                $pxHigherPriorityTaskWoken
496            )
497        }
498    };
499}
500
501macro_rules! xSemaphoreGive {
502    ($xSemaphore:expr) => {
503        unsafe {
504            $crate::freertos::ffi::xQueueGenericSend(
505                $xSemaphore,
506                core::ptr::null(),
507                $crate::freertos::ffi::sem::GIVE_BLOCK_TIME,
508                $crate::freertos::ffi::queue::SEND_TO_BACK
509            )
510        }
511    };
512}
513
514macro_rules! xSemaphoreGiveFromISR {
515    ($xSemaphore:expr, $pxHigherPriorityTaskWoken:expr) => {
516        unsafe {
517            $crate::freertos::ffi::xQueueGiveFromISR(
518                $xSemaphore,
519                $pxHigherPriorityTaskWoken
520            )
521        }
522    };
523}
524
525macro_rules! vSemaphoreDelete {
526    ($xSemaphore:expr) => {
527        unsafe {
528            $crate::freertos::ffi::vQueueDelete($xSemaphore)
529        }
530    };
531}
532
533macro_rules! xQueueSendToBackFromISR {
534    ($xQueue:expr, $pvItemToQueue:expr, $pxHigherPriorityTaskWoken:expr) => {
535        unsafe {
536            $crate::freertos::ffi::xQueueGenericSendFromISR(
537                $xQueue,
538                $pvItemToQueue,
539                $pxHigherPriorityTaskWoken,
540                $crate::freertos::ffi::queue::SEND_TO_BACK
541            )
542        }
543    };
544}
545
546macro_rules! xQueueSendToBack {
547    ($xQueue:expr, $pvItemToQueue:expr, $xTicksToWait:expr) => {
548        unsafe {
549            $crate::freertos::ffi::xQueueGenericSend(
550                $xQueue,
551                $pvItemToQueue,
552                $xTicksToWait,
553                $crate::freertos::ffi::queue::SEND_TO_BACK
554            )
555        }
556    };
557}
558
559macro_rules! xSemaphoreCreateRecursiveMutex {
560    () => {
561        unsafe {
562            $crate::freertos::ffi::xQueueCreateMutex(
563                $crate::freertos::ffi::queue::QUEUE_TYPE_RECURSIVE_MUTEX
564            )
565        }
566    };
567}
568
569macro_rules! xSemaphoreTakeRecursive {
570    ($xMutex:expr, $xBlockTime:expr) => {
571        unsafe {
572            $crate::freertos::ffi::xQueueTakeMutexRecursive(
573                $xMutex,
574                $xBlockTime
575            )
576        }
577    };
578}
579
580macro_rules! xSemaphoreGiveRecursive {
581    ($xMutex:expr) => {
582        unsafe {
583            $crate::freertos::ffi::xQueueGiveMutexRecursive($xMutex)
584        }
585    };
586}