osal_rs/utils.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//! Utility types and functions for OSAL-RS.
21//!
22//! This module contains common types, error definitions, and helper functions
23//! used throughout the library.
24
25use core::ffi::{CStr, c_char};
26use core::{ffi::c_void, str::from_utf8_mut};
27use core::fmt::{Debug, Display};
28use core::ops::Deref;
29use core::time::Duration;
30use alloc::string::{String, ToString};
31
32/// Error types for OSAL-RS operations.
33///
34/// Represents all possible error conditions that can occur when using
35/// the OSAL-RS library.
36///
37/// # Examples
38///
39/// ```ignore
40/// use osal_rs::os::{Queue, QueueFn};
41/// use osal_rs::utils::Error;
42///
43/// match Queue::new(10, 32) {
44/// Ok(queue) => { /* use queue */ },
45/// Err(Error::OutOfMemory) => println!("Failed to allocate queue"),
46/// Err(e) => println!("Other error: {:?}", e),
47/// }
48/// ```
49#[derive(Debug, Clone, PartialEq, Eq, Hash)]
50pub enum Error {
51 /// Insufficient memory to complete operation
52 OutOfMemory,
53 /// Queue send operation timed out
54 QueueSendTimeout,
55 /// Queue receive operation timed out
56 QueueReceiveTimeout,
57 /// Mutex operation timed out
58 MutexTimeout,
59 /// Failed to acquire mutex lock
60 MutexLockFailed,
61 /// Generic timeout error
62 Timeout,
63 /// Queue is full and cannot accept more items
64 QueueFull,
65 /// String conversion failed
66 StringConversionError,
67 /// Thread/task not found
68 TaskNotFound,
69 /// Invalid queue size specified
70 InvalidQueueSize,
71 /// Null pointer encountered
72 NullPtr,
73 /// Requested item not found
74 NotFound,
75 /// Index out of bounds
76 OutOfIndex,
77 /// Invalid type for operation
78 InvalidType,
79 /// Unhandled error with description
80 Unhandled(&'static str)
81}
82
83/// CPU register size enumeration.
84///
85/// Identifies whether the target CPU uses 32-bit or 64-bit registers.
86/// This is used for platform-specific tick count overflow handling.
87#[derive(PartialEq, Eq, Clone, Copy, Debug)]
88pub enum CpuRegisterSize {
89 /// 64-bit CPU registers
90 Bit64,
91 /// 32-bit CPU registers
92 Bit32
93}
94
95/// Boolean type compatible with RTOS return values.
96///
97/// Many RTOS functions return 0 for success and non-zero for failure.
98/// This type provides a Rust-idiomatic way to work with such values.
99///
100/// # Examples
101///
102/// ```ignore
103/// use osal_rs::os::{Semaphore, SemaphoreFn};
104/// use osal_rs::utils::OsalRsBool;
105/// use core::time::Duration;
106///
107/// let sem = Semaphore::new(1, 1).unwrap();
108///
109/// match sem.wait(Duration::from_millis(100)) {
110/// OsalRsBool::True => println!("Acquired semaphore"),
111/// OsalRsBool::False => println!("Failed to acquire"),
112/// }
113///
114/// // Can also convert to bool
115/// if sem.signal().into() {
116/// println!("Semaphore signaled");
117/// }
118/// ```
119#[derive(PartialEq, Eq, Clone, Copy, Debug)]
120#[repr(u8)]
121pub enum OsalRsBool {
122 /// Operation failed or condition is false
123 False = 1,
124 /// Operation succeeded or condition is true
125 True = 0
126}
127
128/// Maximum delay constant for blocking operations.
129///
130/// When used as a timeout parameter, indicates the operation should
131/// block indefinitely until it succeeds.
132///
133/// # Examples
134///
135/// ```ignore
136/// use osal_rs::os::{Mutex, MutexFn};
137/// use osal_rs::utils::MAX_DELAY;
138///
139/// let mutex = Mutex::new(0);
140/// let guard = mutex.lock(); // Blocks forever if needed
141/// ```
142pub const MAX_DELAY: Duration = Duration::from_millis(usize::MAX as u64);
143
144/// Standard Result type for OSAL-RS operations.
145///
146/// Uses [`Error`] as the default error type.
147pub type Result<T, E = Error> = core::result::Result<T, E>;
148
149/// Pointer to pointer type for C FFI.
150pub type DoublePtr = *mut *mut c_void;
151
152/// Mutable pointer type for C FFI.
153pub type Ptr = *mut c_void;
154
155/// Const pointer type for C FFI.
156pub type ConstPtr = *const c_void;
157
158
159pub const fn register_bit_size() -> CpuRegisterSize {
160 if size_of::<usize>() == 8 {
161 CpuRegisterSize::Bit64
162 } else {
163 CpuRegisterSize::Bit32
164 }
165}
166
167#[macro_export]
168macro_rules! from_c_str {
169 ($str:expr) => {
170 unsafe {
171 let c_str = core::ffi::CStr::from_ptr($str);
172 alloc::string::String::from_utf8_lossy(c_str.to_bytes()).to_string()
173 }
174 };
175}
176
177#[macro_export]
178macro_rules! to_cstring {
179 ($s:expr) => {
180 alloc::ffi::CString::new($s.as_str())
181 .map_err(|_| $crate::utils::Error::Unhandled("Failed to convert string to CString"))
182 };
183}
184
185#[macro_export]
186macro_rules! to_c_str {
187 ($s:expr) => {
188 alloc::ffi::CString::new($s.as_ref() as &str).unwrap().as_ptr()
189 };
190}
191
192#[macro_export]
193macro_rules! from_str_to_array {
194 ($str:expr, $buff_name:ident, $buff_size:expr) => {
195 let mut $buff_name = [b' '; $buff_size];
196 let _bytes = $str.as_bytes();
197 let _len = core::cmp::min(_bytes.len(), $buff_size);
198 $buff_name[.._len].copy_from_slice(&_bytes[.._len]);
199 };
200}
201
202#[macro_export]
203macro_rules! thread_extract_param {
204 ($param:expr, $t:ty) => {
205 match $param.as_ref() {
206 Some(p) => {
207 match p.downcast_ref::<$t>() {
208 Some(value) => value,
209 None => return Err($crate::utils::Error::InvalidType),
210 }
211 }
212 None => return Err($crate::utils::Error::NullPtr),
213 }
214 };
215}
216
217#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
218pub struct Bytes<const SIZE: usize = 0> (pub [u8; SIZE]);
219
220impl<const SIZE: usize> Deref for Bytes<SIZE> {
221 type Target = [u8; SIZE];
222
223 fn deref(&self) -> &Self::Target {
224 &self.0
225 }
226}
227
228impl<const SIZE: usize> Display for Bytes<SIZE> {
229 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
230 let str = unsafe {
231 CStr::from_ptr(self.0.as_ptr() as *const c_char)
232 .to_str()
233 .unwrap_or("Conversion error")
234 };
235
236 write!(f, "{}", str.to_string())
237 }
238}
239
240
241impl<const SIZE: usize> Bytes<SIZE> {
242 pub const fn new() -> Self {
243 Self( [0u8; SIZE] )
244 }
245
246 pub fn new_by_str(str: &str) -> Self {
247
248 let mut array = [0u8; SIZE];
249
250 let mut i = 0usize ;
251 for byte in str.as_bytes() {
252 if i > SIZE - 1{
253 break;
254 }
255 array[i] = *byte;
256 i += 1;
257 }
258
259 Self( array )
260 }
261
262 pub fn new_by_string(str: &impl ToString) -> Self {
263 Self::new_by_str(&str.to_string())
264 }
265
266 pub fn fill_str(&mut self, dest: &mut str) {
267 match from_utf8_mut(&mut self.0) {
268 Ok(str) => {
269 let len = core::cmp::min(str.len(), dest.len());
270 unsafe {
271 dest.as_bytes_mut()[..len].copy_from_slice(&str.as_bytes()[..len]);
272 }
273 }
274 Err(_) => todo!(),
275 }
276 }
277}