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 const RUNNING: TaskState = 0;
84pub const READY: TaskState = 1;
85pub const BLOCKED: TaskState = 2;
86pub const SUSPENDED: TaskState = 3;
87pub const DELETED: TaskState = 4;
88pub const INVALID: TaskState = 5;
89
90// Boolean/status constants
91pub const pdFALSE: BaseType = 0;
92
93pub const pdTRUE: BaseType = 1;
94
95pub const pdPASS: BaseType = pdTRUE;
96
97pub const pdFAIL: BaseType = pdFALSE;
98
99// Task notification constants
100pub const tskDEFAULT_INDEX_TO_NOTIFY: UBaseType = 0;
101
102// Semaphore constants
103pub const semBINARY_SEMAPHORE_QUEUE_LENGTH: u8 = 1;
104
105pub const semSEMAPHORE_QUEUE_ITEM_LENGTH: u8 = 0;
106
107pub const semGIVE_BLOCK_TIME: TickType = 0;
108
109// Queue constants
110pub const queueSEND_TO_BACK: BaseType = 0;
111
112pub const queueSEND_TO_FRONT: BaseType = 1;
113
114pub const queueOVERWRITE: BaseType = 2;
115
116// Queue type constants
117pub const queueQUEUE_TYPE_BASE: u8 = 0;
118
119pub const queueQUEUE_TYPE_MUTEX: u8 = 1;
120
121pub const queueQUEUE_TYPE_COUNTING_SEMAPHORE: u8 = 2;
122
123pub const queueQUEUE_TYPE_BINARY_SEMAPHORE: u8 = 3;
124
125pub const queueQUEUE_TYPE_RECURSIVE_MUTEX: u8 = 4;
126
127pub const queueQUEUE_TYPE_SET: u8 = 5;
128
129
130
131/// Task status information structure.
132///
133/// Contains detailed information about a task's state, priority, stack usage, etc.
134#[repr(C)]
135#[derive(Debug, Copy, Clone)]
136pub struct TaskStatus {
137    /// Task handle
138    pub xHandle: ThreadHandle,
139    /// Task name (null-terminated C string)
140    pub pcTaskName: *const c_char,
141    /// Task number (unique ID)
142    pub xTaskNumber: UBaseType,
143    /// Current task state
144    pub eCurrentState: TaskState,
145    /// Current priority
146    pub uxCurrentPriority: UBaseType,
147    /// Base priority (before priority inheritance)
148    pub uxBasePriority: UBaseType,
149    /// Total runtime counter
150    pub ulRunTimeCounter: u32,
151    /// Stack base address
152    pub pxStackBase: *mut StackType,
153    /// Stack high water mark (minimum free stack)
154    pub usStackHighWaterMark: StackType
155}
156
157impl Default for TaskStatus {
158    fn default() -> Self {
159        TaskStatus {
160            xHandle: ptr::null(),
161            pcTaskName: ptr::null(),
162            xTaskNumber: 0,
163            eCurrentState: INVALID,
164            uxCurrentPriority: 0,
165            uxBasePriority: 0,
166            ulRunTimeCounter: 0,
167            pxStackBase: ptr::null_mut(),
168            usStackHighWaterMark: 0,
169        }
170    }
171}
172
173pub type TaskFunction = Option<unsafe extern "C" fn(arg: *mut c_void)>;
174
175unsafe extern "C" {
176    
177
178    /// Allocate memory from the  heap
179    /// 
180    /// # Arguments
181    /// * `size` - The number of bytes to allocate
182    /// 
183    /// # Returns
184    /// A pointer to the allocated memory, or null if allocation fails
185    pub fn pvPortMalloc(size: usize) -> *mut c_void;
186
187    /// Free memory previously allocated by pvPortMalloc
188    /// 
189    /// # Arguments
190    /// * `pv` - Pointer to the memory to free
191    pub fn vPortFree(pv: *mut c_void);
192
193    pub fn vTaskDelay(xTicksToDelay: TickType);
194
195    pub fn xTaskDelayUntil(
196        pxPreviousWakeTime: *mut TickType,
197        xTimeIncrement: TickType,
198    ) -> BaseType;
199
200
201    pub fn xTaskGetTickCount() -> TickType;
202
203    pub fn vTaskStartScheduler();
204
205    pub fn vTaskEndScheduler();
206
207    pub fn vTaskSuspendAll();
208
209    pub fn xTaskResumeAll() -> BaseType;
210
211    pub fn xTaskGetCurrentTaskHandle() -> ThreadHandle;
212
213    pub fn eTaskGetState(xTask: ThreadHandle) -> TaskState;
214
215    pub fn uxTaskGetNumberOfTasks() -> UBaseType;
216
217    pub fn uxTaskGetSystemState(
218        pxTaskStatusArray: *mut TaskStatus,
219        uxArraySize: UBaseType,
220        pulTotalRunTime: *mut u32,
221    ) -> UBaseType;
222
223    pub fn osal_rs_task_enter_critical();
224    pub fn osal_rs_task_exit_critical();
225
226    pub fn osal_rs_task_enter_critical_from_isr() -> UBaseType;
227    pub fn osal_rs_task_exit_critical_from_isr(uxSavedInterruptStatus: UBaseType);
228
229
230    pub fn xTaskCreate(
231        pxTaskCode: TaskFunction,
232        pcName: *const c_char,
233        uxStackDepth: StackType,
234        pvParameters: *mut c_void,
235        uxPriority: UBaseType,
236        pxCreatedTask: *mut ThreadHandle,
237    ) -> BaseType;
238
239    pub fn vTaskDelete(xTaskToDelete: ThreadHandle);
240
241    pub fn vTaskSuspend(xTaskToSuspend: ThreadHandle);
242
243    pub fn vTaskResume(xTaskToResume: ThreadHandle);
244
245    pub fn vTaskGetInfo(
246        xTask: ThreadHandle,
247        pxTaskStatus: *mut TaskStatus,
248        xGetFreeStackSpace: BaseType,
249        eState: TaskState,
250    );
251
252    pub fn ulTaskGenericNotifyTake(uxIndexToWaitOn: UBaseType, xClearCountOnExit: BaseType, xTicksToWait: TickType) -> u32;
253
254
255    pub fn xTaskGenericNotifyWait(
256        uxIndexToWaitOn: UBaseType,
257        ulBitsToClearOnEntry: u32,
258        ulBitsToClearOnExit: u32,
259        pulNotificationValue: *mut u32,
260        xTicksToWait: TickType,
261    ) -> BaseType;
262
263
264    pub fn xTaskGenericNotify(
265        xTaskToNotify: ThreadHandle,
266        uxIndexToNotify: UBaseType,
267        ulValue: u32,
268        eAction: u32,
269        pulPreviousNotificationValue: *mut u32,
270    ) -> BaseType;
271
272
273    pub fn xTaskGenericNotifyFromISR(
274        xTaskToNotify: ThreadHandle,
275        uxIndexToNotify: UBaseType,
276        ulValue: u32,
277        eAction: u32,
278        pulPreviousNotificationValue: *mut u32,
279        pxHigherPriorityTaskWoken: *mut BaseType,
280    ) -> BaseType;
281    
282    pub fn xEventGroupWaitBits(
283        xEventGroup: EventGroupHandle,
284        uxBitsToWaitFor: EventBits,
285        xClearOnExit: BaseType,
286        xWaitForAllBits: BaseType,
287        xTicksToWait: TickType,
288    ) -> EventBits;
289
290    pub fn xEventGroupClearBits(
291        xEventGroup: EventGroupHandle,
292        uxBitsToClear: EventBits,
293    ) -> EventBits;
294
295    pub fn xEventGroupClearBitsFromISR(
296        xEventGroup: EventGroupHandle,
297        uxBitsToClear: EventBits,
298    ) -> BaseType;
299
300        pub fn xEventGroupSetBits(
301        xEventGroup: EventGroupHandle,
302        uxBitsToSet: EventBits,
303    ) -> EventBits;
304
305
306    pub fn xEventGroupSetBitsFromISR(
307        xEventGroup: EventGroupHandle,
308        uxBitsToSet: EventBits,
309        pxHigherPriorityTaskWoken: *mut BaseType,
310    ) -> BaseType;
311
312    pub fn xEventGroupGetBitsFromISR(xEventGroup: EventGroupHandle) -> EventBits;
313
314    pub fn vEventGroupDelete(xEventGroup: EventGroupHandle);
315
316    pub fn xEventGroupCreate() -> EventGroupHandle;
317
318    pub fn osal_rs_critical_section_enter();
319
320    pub fn osal_rs_critical_section_exit();
321
322    pub fn osal_rs_port_yield_from_isr(pxHigherPriorityTaskWoken: BaseType);
323
324    pub fn osal_rs_port_end_switching_isr( xSwitchRequired: BaseType );
325
326    pub fn xQueueCreateMutex(ucQueueType: u8) -> QueueHandle;
327    
328    pub fn xQueueCreateCountingSemaphore(
329        uxMaxCount: UBaseType,
330        uxInitialCount: UBaseType,
331    ) -> QueueHandle;
332
333    pub fn xQueueSemaphoreTake(xQueue: QueueHandle, xTicksToWait: TickType) -> BaseType;
334
335    pub fn xQueueReceiveFromISR(
336        xQueue: QueueHandle,
337        pvBuffer: *mut c_void,
338        pxHigherPriorityTaskWoken: *mut BaseType,
339    ) -> BaseType;
340
341    pub fn xQueueGenericSend(
342        xQueue: QueueHandle,
343        pvItemToQueue: *const c_void,
344        xTicksToWait: TickType,
345        xCopyPosition: BaseType,
346    ) -> BaseType;
347
348    pub fn xQueueGiveFromISR(
349        xQueue: QueueHandle,
350        pxHigherPriorityTaskWoken: *mut BaseType,
351    ) -> BaseType;
352
353     pub fn vQueueDelete(xQueue: QueueHandle);
354
355    pub fn xQueueGenericCreate(
356        uxQueueLength: UBaseType,
357        uxItemSize: UBaseType,
358        ucQueueType: u8,
359    ) -> QueueHandle;
360
361    pub fn xQueueReceive(
362        xQueue: QueueHandle,
363        pvBuffer: *mut c_void,
364        xTicksToWait: TickType,
365    ) -> BaseType;
366
367    pub fn xQueueGenericSendFromISR(
368        xQueue: QueueHandle,
369        pvItemToQueue: *const c_void,
370        pxHigherPriorityTaskWoken: *mut BaseType,
371        xCopyPosition: BaseType,
372    ) -> BaseType;
373
374    pub fn xQueueTakeMutexRecursive(xMutex: QueueHandle, xTicksToWait: TickType) -> BaseType;
375
376    pub fn xQueueGiveMutexRecursive(xMutex: QueueHandle) -> BaseType;
377
378    pub fn xPortGetFreeHeapSize() -> usize;
379
380    pub fn xTimerCreateTimerTask() -> BaseType;
381
382    pub fn xTimerCreate(
383        pcTimerName: *const c_char,
384        xTimerPeriodInTicks: TickType,
385        xAutoReload: BaseType,
386        pvTimerID: *mut c_void,
387        pxCallbackFunction: Option<TimerCallback>,
388    ) -> TimerHandle;
389
390    pub fn osal_rs_timer_start(xTimer: TimerHandle, xTicksToWait: TickType) -> BaseType;
391
392    pub fn osal_rs_timer_stop(xTimer: TimerHandle, xTicksToWait: TickType) -> BaseType;
393
394    pub fn osal_rs_timer_reset(xTimer: TimerHandle, xTicksToWait: TickType) -> BaseType;
395
396    pub fn osal_rs_timer_change_period(
397        xTimer: TimerHandle,
398        xNewPeriodInTicks: TickType,
399        xTicksToWait: TickType,
400    ) -> BaseType;
401
402    pub fn osal_rs_timer_delete(xTimer: TimerHandle, xTicksToWait: TickType) -> BaseType;
403
404    pub fn pvTimerGetTimerID(xTimer: TimerHandle) -> *mut c_void;
405
406    pub fn printf(fmt: *const u8, ...) -> i32; 
407}
408
409
410macro_rules! xTaskNotifyWait {
411    ($ulBitsToClearOnEntry:expr, $ulBitsToClearOnExit:expr, $pulNotificationValue:expr, $xTicksToWait:expr) => {
412        unsafe {
413            $crate::freertos::ffi::xTaskGenericNotifyWait(
414                $crate::freertos::ffi::tskDEFAULT_INDEX_TO_NOTIFY,
415                $ulBitsToClearOnEntry,
416                $ulBitsToClearOnExit,
417                $pulNotificationValue,
418                $xTicksToWait
419            )
420        }
421    };
422}
423
424macro_rules! xTaskNotify {
425    ($xTaskToNotify:expr, $ulValue:expr, $eAction:expr) => {
426        unsafe {
427            $crate::freertos::ffi::xTaskGenericNotify(
428                $xTaskToNotify,
429                $crate::freertos::ffi::tskDEFAULT_INDEX_TO_NOTIFY,
430                $ulValue,
431                $eAction,
432                core::ptr::null_mut()
433            )
434        }
435    };
436}
437
438macro_rules! xTaskNotifyFromISR {
439    ($xTaskToNotify:expr, $ulValue:expr, $eAction:expr, $pxHigherPriorityTaskWoken:expr) => {
440        unsafe {
441            $crate::freertos::ffi::xTaskGenericNotifyFromISR(
442                $xTaskToNotify,
443                $crate::freertos::ffi::tskDEFAULT_INDEX_TO_NOTIFY,
444                $ulValue,
445                $eAction,
446                core::ptr::null_mut(),
447                $pxHigherPriorityTaskWoken
448            )
449        }
450    };
451}
452
453
454macro_rules! xEventGroupGetBits {
455    ($xEventGroup:expr) => {
456        unsafe {
457            $crate::freertos::ffi::xEventGroupClearBits($xEventGroup, 0)
458        }
459    };
460}
461
462macro_rules! xSemaphoreCreateCounting {
463    ($uxMaxCount:expr, $uxInitialCount:expr) => {
464        unsafe {
465            $crate::freertos::ffi::xQueueCreateCountingSemaphore(
466                $uxMaxCount,
467                $uxInitialCount
468            )
469        }
470    };
471}
472
473macro_rules! xSemaphoreTake {
474    ($xSemaphore:expr, $xBlockTime:expr) => {
475        unsafe {
476            $crate::freertos::ffi::xQueueSemaphoreTake(
477                $xSemaphore,
478                $xBlockTime
479            )
480        }
481    };
482}
483
484macro_rules! xSemaphoreTakeFromISR {
485    ($xSemaphore:expr, $pxHigherPriorityTaskWoken:expr) => {
486        unsafe {
487            $crate::freertos::ffi::xQueueReceiveFromISR(
488                $xSemaphore,
489                core::ptr::null_mut(),
490                $pxHigherPriorityTaskWoken
491            )
492        }
493    };
494}
495
496macro_rules! xSemaphoreGive {
497    ($xSemaphore:expr) => {
498        unsafe {
499            $crate::freertos::ffi::xQueueGenericSend(
500                $xSemaphore,
501                core::ptr::null(),
502                $crate::freertos::ffi::semGIVE_BLOCK_TIME,
503                $crate::freertos::ffi::queueSEND_TO_BACK
504            )
505        }
506    };
507}
508
509macro_rules! xSemaphoreGiveFromISR {
510    ($xSemaphore:expr, $pxHigherPriorityTaskWoken:expr) => {
511        unsafe {
512            $crate::freertos::ffi::xQueueGiveFromISR(
513                $xSemaphore,
514                $pxHigherPriorityTaskWoken
515            )
516        }
517    };
518}
519
520macro_rules! vSemaphoreDelete {
521    ($xSemaphore:expr) => {
522        unsafe {
523            $crate::freertos::ffi::vQueueDelete($xSemaphore)
524        }
525    };
526}
527
528macro_rules! xQueueSendToBackFromISR {
529    ($xQueue:expr, $pvItemToQueue:expr, $pxHigherPriorityTaskWoken:expr) => {
530        unsafe {
531            $crate::freertos::ffi::xQueueGenericSendFromISR(
532                $xQueue,
533                $pvItemToQueue,
534                $pxHigherPriorityTaskWoken,
535                $crate::freertos::ffi::queueSEND_TO_BACK
536            )
537        }
538    };
539}
540
541macro_rules! xQueueSendToBack {
542    ($xQueue:expr, $pvItemToQueue:expr, $xTicksToWait:expr) => {
543        unsafe {
544            $crate::freertos::ffi::xQueueGenericSend(
545                $xQueue,
546                $pvItemToQueue,
547                $xTicksToWait,
548                $crate::freertos::ffi::queueSEND_TO_BACK
549            )
550        }
551    };
552}
553
554macro_rules! xSemaphoreCreateRecursiveMutex {
555    () => {
556        unsafe {
557            $crate::freertos::ffi::xQueueCreateMutex(
558                $crate::freertos::ffi::queueQUEUE_TYPE_RECURSIVE_MUTEX
559            )
560        }
561    };
562}
563
564macro_rules! xSemaphoreTakeRecursive {
565    ($xMutex:expr, $xBlockTime:expr) => {
566        unsafe {
567            $crate::freertos::ffi::xQueueTakeMutexRecursive(
568                $xMutex,
569                $xBlockTime
570            )
571        }
572    };
573}
574
575macro_rules! xSemaphoreGiveRecursive {
576    ($xMutex:expr) => {
577        unsafe {
578            $crate::freertos::ffi::xQueueGiveMutexRecursive($xMutex)
579        }
580    };
581}