diff options
| author | zhang san <[email protected]> | 2025-12-08 18:34:25 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-08 18:34:25 +0800 |
| commit | 69980f8b9d29b38dbd4ddf6373b4b45f3a138409 (patch) | |
| tree | 21dd9a8ce80e3d30192e9ffa56ea39a199bae309 /components/rust/core/src/api | |
| parent | cd1d47b87c5d47e131730e5506eb3ad77d357653 (diff) | |
Adding Rust Language Support for RT-Thread #10910
Diffstat (limited to 'components/rust/core/src/api')
| -rw-r--r-- | components/rust/core/src/api/base.rs | 78 | ||||
| -rw-r--r-- | components/rust/core/src/api/interrupt.rs | 69 | ||||
| -rw-r--r-- | components/rust/core/src/api/libloading.rs | 238 | ||||
| -rw-r--r-- | components/rust/core/src/api/mem.rs | 79 | ||||
| -rw-r--r-- | components/rust/core/src/api/mod.rs | 27 | ||||
| -rw-r--r-- | components/rust/core/src/api/mutex.rs | 48 | ||||
| -rw-r--r-- | components/rust/core/src/api/queue.rs | 56 | ||||
| -rw-r--r-- | components/rust/core/src/api/sem.rs | 51 | ||||
| -rw-r--r-- | components/rust/core/src/api/thread.rs | 97 |
9 files changed, 743 insertions, 0 deletions
diff --git a/components/rust/core/src/api/base.rs b/components/rust/core/src/api/base.rs new file mode 100644 index 0000000000..fabb00d5ba --- /dev/null +++ b/components/rust/core/src/api/base.rs @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author notes + * 2025-10-10 foxglove latest version + */ +use crate::bindings::*; + +/* Error type from rt-thread C code */ +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum RttCResult { + Ok = RT_EOK as isize, + Error = -(RT_ERROR as isize), + TimeOut = -(RT_ETIMEOUT as isize), + Full = -(RT_EFULL as isize), + Empty = -(RT_EEMPTY as isize), + NoMem = -(RT_ENOMEM as isize), + NoSys = -(RT_ENOSYS as isize), + Busy = -(RT_EBUSY as isize), + IO = -(RT_EIO as isize), + INTR = -(RT_EINTR as isize), + INVAL = -(RT_EINVAL as isize), + + NotValidCode = -100, +} +/* NOTE Match to `enum Error` */ +impl From<i32> for RttCResult { + fn from(a: i32) -> Self { + let ret = match a { + 0 => RttCResult::Ok, + -1 => RttCResult::Error, + -2 => RttCResult::TimeOut, + -3 => RttCResult::Full, + -4 => RttCResult::Empty, + -5 => RttCResult::NoMem, + -6 => RttCResult::NoSys, + -7 => RttCResult::Busy, + -8 => RttCResult::IO, + -9 => RttCResult::INTR, + -10 => RttCResult::INVAL, + _ => RttCResult::NotValidCode, + }; + ret + } +} + +impl From<i64> for RttCResult { + fn from(a: i64) -> Self { + if a >= 0 { + RttCResult::Ok + } else { + match a { + -1 => RttCResult::Error, + -2 => RttCResult::TimeOut, + -3 => RttCResult::Full, + -4 => RttCResult::Empty, + -5 => RttCResult::NoMem, + -6 => RttCResult::NoSys, + -7 => RttCResult::Busy, + -8 => RttCResult::IO, + -9 => RttCResult::INTR, + -10 => RttCResult::INVAL, + _ => RttCResult::NotValidCode, + } + } + } +} + +pub fn is_eok(val: RttCResult) -> bool { + if let RttCResult::Ok = val { + true + } else { + false + } +} diff --git a/components/rust/core/src/api/interrupt.rs b/components/rust/core/src/api/interrupt.rs new file mode 100644 index 0000000000..9a62442cb9 --- /dev/null +++ b/components/rust/core/src/api/interrupt.rs @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author notes + * 2025-10-10 foxglove latest version + */ +use crate::bindings::*; + +#[cfg(not(feature = "smp"))] +unsafe fn irq_close() -> rt_base_t { + rt_hw_interrupt_disable() +} +#[cfg(feature = "smp")] +unsafe fn irq_close() -> rt_base_t { + rt_cpus_lock() +} + +#[cfg(not(feature = "smp"))] +unsafe fn irq_open(f: rt_base_t) { + rt_hw_interrupt_enable(f) +} +#[cfg(feature = "smp")] +unsafe fn irq_open(f: rt_base_t) { + rt_cpus_unlock(f) +} + +pub fn no_irq<F, T>(f: F) -> T +where + F: FnOnce() -> T, +{ + let level; + let out; + unsafe { level = irq_close() } + out = f(); + unsafe { irq_open(level) } + out +} + +#[derive(Debug, Copy, Clone)] +pub struct InterruptFlag(rt_base_t); + +pub const INTERRUPT_FLAG_INIT: InterruptFlag = InterruptFlag { 0: 0 as _ }; + +pub fn interrupt_disable() -> InterruptFlag { + unsafe { InterruptFlag(irq_close()) } +} + +pub fn interrupt_enable(f: InterruptFlag) { + unsafe { irq_open(f.0) } +} + +pub fn interrupt_enter() { + unsafe { + rt_interrupt_enter(); + } +} + +pub fn interrupt_leave() { + unsafe { + rt_interrupt_leave(); + } +} + +pub fn is_irq_context() -> bool { + unsafe { rt_interrupt_get_nest() != 0 } +}
\ No newline at end of file diff --git a/components/rust/core/src/api/libloading.rs b/components/rust/core/src/api/libloading.rs new file mode 100644 index 0000000000..438f1fb40b --- /dev/null +++ b/components/rust/core/src/api/libloading.rs @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-10-14 foxglove libdl safe wrapper (libloading-like) + */ + +/*! Minimal `libloading`-style safe wrapper around RT-Thread libdl. +Provides RAII `Library` and typed `Symbol<T>` for dlsym. +*/ + +extern crate alloc; + +use core::ffi::{c_char, c_int, c_void}; +use core::ffi::CStr; +use core::marker::PhantomData; +use core::mem::forget; +use core::ops::Deref; + +use alloc::ffi::CString; +use alloc::vec::Vec; + +use crate::bindings::libc; + +pub use crate::bindings::libc::{RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NOW}; + +/* Error type for dynamic loading operations */ +pub enum DlError { + /* dlopen failed; contains last error C-string pointer */ + Open(*const c_char), + /* dlsym failed; contains last error C-string pointer */ + Symbol(*const c_char), + /* dlclose returned non-zero; contains return code */ + Close(c_int), +} + +impl core::fmt::Debug for DlError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Display::fmt(self, f) + } +} + +impl core::fmt::Display for DlError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + &DlError::Open(ptr) | &DlError::Symbol(ptr) => { + if ptr.is_null() { + write!(f, "[libdl open/sym error: none]") + } else { + unsafe { + // `ptr` is a raw C string pointer + let cstr = CStr::from_ptr(ptr); + match cstr.to_str() { + Ok(s) => write!(f, "{}", s), + Err(_) => write!(f, "[libdl error: invalid UTF-8]") + } + } + } + } + DlError::Close(rc) => write!(f, "dlclose rc={}", rc), + } + } +} + +/* RAII wrapper for a `dlopen` handle */ +pub struct Library { + handle: *mut c_void, +} + +impl Library { + /* Create a new library using default flags (`RTLD_NOW | RTLD_GLOBAL`). + * Accepts a Rust string path and converts internally to a C string. + */ + pub fn new(path: &str) -> Result<Self, DlError> { + let cstr = match CString::new(path) { + Ok(s) => s, + Err(_) => { + return Err(DlError::Open(core::ptr::null())); + } + }; + unsafe { Self::open_global(cstr.as_ptr()) } + } + /* Open a dynamic module with given flags + * Safety: `path` must be a valid, NUL-terminated C string pointer + */ + pub unsafe fn open(path: *const c_char, flags: c_int) -> Result<Self, DlError> { + let h = libc::dlopen(path, flags); + if h.is_null() { + Err(DlError::Open(libc::last_error_ptr())) + } else { + Ok(Library { handle: h }) + } + } + + /* Convenience open using `RTLD_NOW | RTLD_GLOBAL` */ + pub unsafe fn open_global(path: *const c_char) -> Result<Self, DlError> { + Self::open(path, RTLD_NOW | RTLD_GLOBAL) + } + + /* Resolve a typed symbol from the library using a C string pointer. + * Safety: Caller must ensure `T` matches the actual symbol type and `symbol` is NUL-terminated. + */ + pub unsafe fn get_ptr<T>(&self, symbol: *const c_char) -> Result<Symbol<'_, T>, DlError> { + let sym = libc::dlsym(self.handle, symbol); + if sym.is_null() { + Err(DlError::Symbol(libc::last_error_ptr())) + } else { + Ok(Symbol { + raw: sym, + _lib: PhantomData, + _ty: PhantomData, + }) + } + } + + /* Resolve a typed symbol from the library using a byte slice name. + * If the slice is not NUL-terminated, a temporary buffer is allocated. + */ + pub fn get<T>(&self, symbol: &[u8]) -> Result<Symbol<'_, T>, DlError> { + let (ptr, _guard_buf): (*const c_char, Option<Vec<u8>>) = if symbol.last().copied() == Some(0) { + (symbol.as_ptr() as *const c_char, None) + } else { + let mut buf = Vec::with_capacity(symbol.len() + 1); + buf.extend_from_slice(symbol); + buf.push(0); + (buf.as_ptr() as *const c_char, Some(buf)) + }; + let sym = unsafe { libc::dlsym(self.handle, ptr) }; + if sym.is_null() { + Err(DlError::Symbol(libc::last_error_ptr())) + } else { + Ok(Symbol { + raw: sym, + _lib: PhantomData, + _ty: PhantomData, + }) + } + } + + /* Check if handle is null */ + pub fn is_null(&self) -> bool { + self.handle.is_null() + } + + /* Consume and close the library explicitly */ + pub unsafe fn close(self) -> Result<(), DlError> { + let h = self.handle; + forget(self); + let rc = libc::dlclose(h); + if rc == 1 { + Ok(()) + } else { + Err(DlError::Close(rc)) + } + } + + /* Leak the handle out (caller manages lifetime) */ + pub unsafe fn into_raw(self) -> *mut c_void { + let h = self.handle; + forget(self); + h + } +} + +impl Drop for Library { + fn drop(&mut self) { + unsafe { + if !self.handle.is_null() { + let _ = libc::dlclose(self.handle); + self.handle = core::ptr::null_mut(); + } + } + } +} + +/* RAII wrapper for a symbol tied to a `Library` lifetime. + * Stores the raw pointer and performs typed conversion at the use site. + */ +pub struct Symbol<'lib, T> { + raw: *mut c_void, + _lib: PhantomData<&'lib Library>, + _ty: PhantomData<T>, +} + +impl<'lib, T> Symbol<'lib, T> { + /* Check if the underlying pointer is null */ + pub fn is_null(&self) -> bool { + self.raw.is_null() + } + + /* Get the raw pointer as `*mut c_void` */ + pub fn as_raw(&self) -> *mut c_void { + self.raw + } + + /* Cast the raw pointer to a different pointer type + * Safety: Caller must ensure `U` is the correct target type. + */ + pub unsafe fn as_ptr<U>(&self) -> *mut U { + self.raw as *mut U + } + + /* Convert to the typed value (e.g., function pointer) at use site + Safety: `T` must be the correct type for the symbol. + */ + pub unsafe fn to_value(&self) -> T + where + T: Sized + Copy, + { + // Bitwise-copy the pointer value into a `T` without using `transmute`. + // This relies on `T` (e.g., extern "C" fn ...) having the same representation size. + let src: *const T = (&self.raw as *const *mut c_void) as *const T; + core::ptr::read(src) + } + + pub fn into_raw(self) -> *mut c_void { + let p = self.raw; + forget(self); + p + } +} + +impl<'lib, T> Deref for Symbol<'lib, T> { + type Target = T; + fn deref(&self) -> &Self::Target { + /* Reinterpret the `raw` pointer storage as a value of type `T`. */ + /* Safety: This is safe if `T` is a pointer-sized function pointer or compatible pointer type. */ + unsafe { &*((&self.raw as *const *mut c_void) as *const T) } + } +} + +#[inline] +pub fn last_error() -> *const c_char { + libc::last_error_ptr() +} diff --git a/components/rust/core/src/api/mem.rs b/components/rust/core/src/api/mem.rs new file mode 100644 index 0000000000..8b998f77c3 --- /dev/null +++ b/components/rust/core/src/api/mem.rs @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-10-10 foxglove memory management API + */ + +use crate::bindings::*; +use core::ffi::c_void; + +pub fn mem_alloc(bytes: usize) -> Option<*mut c_void> { + if bytes == 0 { + return None; + } + + let ptr = unsafe { rt_malloc(bytes as rt_size_t) }; + if ptr.is_null() { + None + } else { + Some(ptr) + } +} + +pub fn mem_calloc(count: usize, size: usize) -> Option<*mut c_void> { + if count == 0 || size == 0 { + return None; + } + + let ptr = unsafe { rt_calloc(count as rt_size_t, size as rt_size_t) }; + if ptr.is_null() { + None + } else { + Some(ptr) + } +} + +pub fn mem_alloc_aligned(bytes: usize, align: usize) -> Option<*mut c_void> { + if bytes == 0 || align == 0 || !align.is_power_of_two() { + return None; + } + + let ptr = unsafe { rt_malloc_align(bytes as rt_size_t, align as rt_size_t) }; + if ptr.is_null() { + None + } else { + Some(ptr) + } +} + +pub fn mem_realloc(ptr: *mut c_void, new_size: usize) -> Option<*mut c_void> { + if new_size == 0 { + if !ptr.is_null() { + mem_free(ptr); + } + return None; + } + + let new_ptr = unsafe { rt_realloc(ptr, new_size as rt_size_t) }; + if new_ptr.is_null() { + None + } else { + Some(new_ptr) + } +} + +pub fn mem_free(ptr: *mut c_void) { + if !ptr.is_null() { + unsafe { rt_free(ptr) }; + } +} + +pub fn mem_free_aligned(ptr: *mut c_void) { + if !ptr.is_null() { + unsafe { rt_free_align(ptr) }; + } +} diff --git a/components/rust/core/src/api/mod.rs b/components/rust/core/src/api/mod.rs new file mode 100644 index 0000000000..8fc2ba2301 --- /dev/null +++ b/components/rust/core/src/api/mod.rs @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author notes + * 2025-10-10 foxglove API module + */ +pub mod base; +pub mod interrupt; +pub mod mem; +pub mod thread; +pub mod mutex; +pub mod sem; +pub mod queue; +pub mod libloading; + + +pub use base::*; +pub use interrupt::*; +pub use mem::*; +pub use thread::*; +pub use mutex::*; +pub use sem::*; +pub use queue::*; +pub use libloading::*; diff --git a/components/rust/core/src/api/mutex.rs b/components/rust/core/src/api/mutex.rs new file mode 100644 index 0000000000..176e02e2e7 --- /dev/null +++ b/components/rust/core/src/api/mutex.rs @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author notes + * 2025-10-10 foxglove Mutex API + */ +use crate::api::RttCResult; +use crate::bindings::*; +use core::ptr; +use alloc::ffi::CString; + +pub type APIRawMutex = rt_mutex_t; + +#[inline] +pub fn mutex_create(name: &str) -> Option<APIRawMutex> { + let s = CString::new(name).unwrap(); + let raw; + unsafe { + raw = rt_mutex_create(s.as_ptr(), 1); + } + if raw == ptr::null_mut() { + None + } else { + Some(raw) + } +} + +#[inline] +pub fn mutex_delete(handle: APIRawMutex) { + unsafe { + rt_mutex_delete(handle); + } +} + +#[inline] +pub fn mutex_take(handle: APIRawMutex, tick: isize) -> RttCResult { + unsafe { rt_mutex_take(handle, tick as _).into() } +} + +#[inline] +pub fn mutex_release(handle: APIRawMutex) { + unsafe { + rt_mutex_release(handle); + } +} diff --git a/components/rust/core/src/api/queue.rs b/components/rust/core/src/api/queue.rs new file mode 100644 index 0000000000..96665e47f2 --- /dev/null +++ b/components/rust/core/src/api/queue.rs @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author notes + * 2025-10-10 foxglove Queue API + */ + +use crate::api::RttCResult; +use core::ffi::c_void; +use crate::bindings::*; +use core::ptr; +use alloc::ffi::CString; + +pub type APIRawQueue = rt_mq_t; + +#[inline] +pub(crate) fn queue_create(name: &str, len: u64, message_size: u64) -> Option<APIRawQueue> { + let s = CString::new(name).unwrap(); + let raw; + unsafe { raw = rt_mq_create(s.as_ptr(), message_size, len, 0) } + if raw == ptr::null_mut() { + None + } else { + Some(raw) + } +} + +#[inline] +pub(crate) fn queue_send_wait( + handle: APIRawQueue, + msg: *const c_void, + msg_size: u64, + tick: i32, +) -> RttCResult { + unsafe { rt_mq_send_wait(handle, msg, msg_size, tick).into() } +} + +#[inline] +pub(crate) fn queue_receive_wait( + handle: APIRawQueue, + msg: *mut c_void, + msg_size: u64, + tick: i32, +) -> RttCResult { + unsafe { rt_mq_recv(handle, msg, msg_size, tick).into() } +} + +#[inline] +pub(crate) fn queue_delete(handle: APIRawQueue) { + unsafe { + rt_mq_delete(handle); + } +} diff --git a/components/rust/core/src/api/sem.rs b/components/rust/core/src/api/sem.rs new file mode 100644 index 0000000000..43c21978d2 --- /dev/null +++ b/components/rust/core/src/api/sem.rs @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author notes + * 2025-10-10 foxglove Semaphore API + */ +use crate::api::RttCResult; +use crate::bindings::*; +use core::ptr; +use alloc::ffi::CString; + +pub type APIRawSem = rt_sem_t; + + #[inline] +pub(crate) fn semaphore_create(name: &str) -> Option<APIRawSem> { + let s = CString::new(name).unwrap(); + let raw; + unsafe { + raw = rt_sem_create(s.as_ptr(), 0, 0); + } + return if raw == ptr::null_mut() { + None + } else { + Some(raw) + }; +} + + #[inline] +pub(crate) fn semaphore_try_take(handle: APIRawSem) -> RttCResult { + unsafe { rt_sem_trytake(handle).into() } +} + + #[inline] +pub(crate) fn semaphore_take(handle: APIRawSem, tick: u32) -> RttCResult { + unsafe { rt_sem_take(handle, tick).into() } +} + + #[inline] +pub(crate) fn semaphore_release(handle: APIRawSem) -> RttCResult { + unsafe { rt_sem_release(handle).into() } +} + + #[inline] +pub(crate) fn semaphore_delete(handle: APIRawSem) { + unsafe { + let _ = rt_sem_delete(handle); + } +} diff --git a/components/rust/core/src/api/thread.rs b/components/rust/core/src/api/thread.rs new file mode 100644 index 0000000000..d8b1ed295b --- /dev/null +++ b/components/rust/core/src/api/thread.rs @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author notes + * 2025-10-10 foxglove Thread API + */ +use super::base::*; +use crate::bindings::*; +use core::ptr; +use core::ffi::c_void; +use alloc::ffi::CString; + +/* Thread handle, defined by RT-Thread C code */ +pub type APIRawThread = rt_thread_t; +/* Thread entry function, defined by RT-Thread C code */ +pub type ThreadEntry = extern "C" fn(parameter: *mut c_void); + +#[inline] +pub fn thread_create( + name: &str, + entry: ThreadEntry, + param: *mut c_void, + stack_size: u32, + priority: u8, + tick: u32, +) -> Option<APIRawThread> { + let name = CString::new(name).unwrap(); + let raw; + unsafe { + raw = rt_thread_create( + name.as_ptr(), + entry, + param, + stack_size as crate::bindings::librt::rt_size_t, + priority, + tick, + ); + } + if raw == ptr::null_mut() { + None + } else { + Some(raw) + } +} + +#[inline] +pub fn thread_delete(th: APIRawThread) -> RttCResult { + unsafe { rt_thread_delete(th).into() } +} + +#[inline] +pub fn thread_self() -> Option<APIRawThread> { + let ret; + unsafe { + ret = rt_thread_self(); + } + if ret == ptr::null_mut() { + None + } else { + Some(ret) + } +} + +#[inline] +pub fn thread_startup(th: APIRawThread) -> RttCResult { + unsafe { rt_thread_startup(th).into() } +} + +#[inline] +pub fn thread_delay(ticks: usize) -> RttCResult { + unsafe { rt_thread_delay(ticks as _).into() } +} + +#[inline] +pub fn thread_m_delay(ms: i32) -> RttCResult { + unsafe { rt_thread_mdelay(ms as _).into() } +} + +#[inline] +pub fn thread_yield() { + unsafe { + rt_thread_yield(); + } +} + +#[inline] +pub fn thread_suspend(th: APIRawThread) -> RttCResult { + unsafe { rt_thread_suspend(th).into() } +} + +#[inline] +pub fn thread_resume(th: APIRawThread) -> RttCResult { + unsafe { rt_thread_resume(th).into() } +} |
