Skip to main content

osal_rs/freertos/
ffi.rs

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