summaryrefslogtreecommitdiff
path: root/components/rust/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'components/rust/core/src')
-rw-r--r--components/rust/core/src/allocator.rs97
-rw-r--r--components/rust/core/src/api/base.rs78
-rw-r--r--components/rust/core/src/api/interrupt.rs69
-rw-r--r--components/rust/core/src/api/libloading.rs238
-rw-r--r--components/rust/core/src/api/mem.rs79
-rw-r--r--components/rust/core/src/api/mod.rs27
-rw-r--r--components/rust/core/src/api/mutex.rs48
-rw-r--r--components/rust/core/src/api/queue.rs56
-rw-r--r--components/rust/core/src/api/sem.rs51
-rw-r--r--components/rust/core/src/api/thread.rs97
-rw-r--r--components/rust/core/src/bindings/libc.rs76
-rw-r--r--components/rust/core/src/bindings/librt.rs152
-rw-r--r--components/rust/core/src/bindings/mod.rs78
-rw-r--r--components/rust/core/src/fs.rs118
-rw-r--r--components/rust/core/src/init.rs21
-rw-r--r--components/rust/core/src/lib.rs83
-rw-r--r--components/rust/core/src/libloader.rs83
-rw-r--r--components/rust/core/src/mutex.rs224
-rw-r--r--components/rust/core/src/out.rs68
-rw-r--r--components/rust/core/src/panic.rs32
-rw-r--r--components/rust/core/src/param.rs31
-rw-r--r--components/rust/core/src/prelude/mod.rs11
-rw-r--r--components/rust/core/src/prelude/no_std.rs28
-rw-r--r--components/rust/core/src/queue.rs111
-rw-r--r--components/rust/core/src/sem.rs81
-rw-r--r--components/rust/core/src/thread.rs154
-rw-r--r--components/rust/core/src/time.rs42
27 files changed, 2233 insertions, 0 deletions
diff --git a/components/rust/core/src/allocator.rs b/components/rust/core/src/allocator.rs
new file mode 100644
index 0000000000..731a072710
--- /dev/null
+++ b/components/rust/core/src/allocator.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 RT-Thread GlobalAlloc implementation
+ */
+
+use crate::api::mem::{mem_alloc, mem_alloc_aligned, mem_free, mem_free_aligned, mem_realloc};
+use core::alloc::{GlobalAlloc, Layout};
+use crate::panic::panic_on_atomic_context;
+use core::ptr;
+use core::ffi::c_void;
+
+pub struct RttAlloc;
+unsafe impl GlobalAlloc for RttAlloc {
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ panic_on_atomic_context("alloc");
+ let size = layout.size();
+ let align = layout.align();
+
+ /* Handle zero-sized allocations */
+ if size == 0 {
+ return ptr::null_mut();
+ }
+
+ /* Use aligned allocation if alignment is greater than default
+ * RT-Thread's default alignment is typically 4 or 8 bytes */
+ if align > 8 {
+ match mem_alloc_aligned(size, align) {
+ Some(ptr) => ptr as *mut u8,
+ None => ptr::null_mut(),
+ }
+ } else {
+ match mem_alloc(size) {
+ Some(ptr) => ptr as *mut u8,
+ None => ptr::null_mut(),
+ }
+ }
+ }
+
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ panic_on_atomic_context("dealloc");
+ if ptr.is_null() {
+ return;
+ }
+
+ let align = layout.align();
+
+ /* Use aligned deallocation if the original allocation was aligned */
+ if align > 8 {
+ mem_free_aligned(ptr as *mut c_void);
+ } else {
+ mem_free(ptr as *mut c_void);
+ }
+ }
+
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ panic_on_atomic_context("realloc");
+ /* Handle zero-sized new allocation */
+ if new_size == 0 {
+ self.dealloc(ptr, layout);
+ return ptr::null_mut();
+ }
+
+ /* Handle null pointer (equivalent to alloc) */
+ if ptr.is_null() {
+ let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
+ return self.alloc(new_layout);
+ }
+
+ /* For aligned allocations, we need to handle realloc manually
+ * since RT-Thread's rt_realloc may not preserve alignment */
+ let align = layout.align();
+ if align > 8 {
+ let new_ptr = match mem_alloc_aligned(new_size, align) {
+ Some(ptr) => ptr as *mut u8,
+ None => return ptr::null_mut(),
+ };
+
+ /* Copy data from old to new */
+ let copy_size = core::cmp::min(layout.size(), new_size);
+ ptr::copy_nonoverlapping(ptr, new_ptr, copy_size);
+
+ mem_free_aligned(ptr as *mut c_void);
+
+ new_ptr
+ } else {
+ match mem_realloc(ptr as *mut c_void, new_size) {
+ Some(new_ptr) => new_ptr as *mut u8,
+ None => ptr::null_mut(),
+ }
+ }
+ }
+} \ No newline at end of file
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() }
+}
diff --git a/components/rust/core/src/bindings/libc.rs b/components/rust/core/src/bindings/libc.rs
new file mode 100644
index 0000000000..51efc78b35
--- /dev/null
+++ b/components/rust/core/src/bindings/libc.rs
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author foxglove
+ * 2025-09-15 foxglove 1.0 version
+ * 2025-09-23 foxglove 1.1 version, add libdl bindings
+ */
+
+/*! C standard library API bindings
+
+Provides Rust wrappers for standard C library functions
+*/
+
+#![allow(non_camel_case_types)]
+
+pub use core::ffi::{c_char, c_int, c_void, c_long, c_ulong};
+
+pub const RTLD_LAZY: c_int = 0x00000; /* Lazy function call binding */
+pub const RTLD_NOW: c_int = 0x00001; /* Immediate function call binding */
+pub const RTLD_GLOBAL: c_int = 0x10000; /* Make symbols globally available */
+pub const RTLD_LOCAL: c_int = 0x00000; /* Default local */
+pub const O_RDONLY: c_int = 0x0000; /* open for reading only */
+pub const O_WRONLY: c_int = 0x0001; /* open for writing only */
+pub const O_RDWR: c_int = 0x0002; /* open for reading and writing */
+pub const O_APPEND: c_int = 0x0008; /* writes done at eof */
+pub const O_CREAT: c_int = 0x0100; /* create and open file */
+pub const O_TRUNC: c_int = 0x0200; /* open and truncate */
+pub const O_EXCL: c_int = 0x0400; /* open only if file doesn't already exist */
+pub type size_t = c_ulong;
+pub type ssize_t = c_long;
+pub type off_t = c_long;
+pub const SEEK_SET: c_int = 0;
+pub const SEEK_CUR: c_int = 1;
+pub const SEEK_END: c_int = 2;
+/* ============== time functions ============== */
+pub type time_t = c_long;
+pub type suseconds_t = c_int;
+#[repr(C)]
+pub struct timeval {
+ pub tv_sec: time_t,
+ pub tv_usec: suseconds_t,
+}
+
+/* ============== libdl functions ============== */
+unsafe extern "C" {
+ pub fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void;
+ pub fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
+ pub fn dlclose(handle: *mut c_void) -> c_int;
+ pub fn dlerror() -> *const c_char;
+}
+unsafe extern "C" {
+ pub fn open(path: *const c_char, oflag: c_int, ...) -> c_int;
+ pub fn close(fd: c_int) -> c_int;
+ pub fn read(fd: c_int, buf: *mut c_void, count: size_t) -> ssize_t;
+ pub fn write(fd: c_int, buf: *const c_void, count: size_t) -> ssize_t;
+ pub fn lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t;
+ pub fn fsync(fd: c_int) -> c_int;
+ pub fn ftruncate(fd: c_int, length: off_t) -> c_int;
+}
+
+/* ============== time functions ============== */
+unsafe extern "C" {
+ /* Use local types instead of crate-root paths to avoid E0412 */
+ pub fn gettimeofday(tp: *mut timeval, tz: *mut c_void) -> c_int;
+}
+
+/* Helper: get last libdl error C-string pointer
+ * Safe wrapper around `dlerror()` returning the raw pointer for printing.
+ */
+#[inline]
+pub fn last_error_ptr() -> *const c_char {
+ unsafe { dlerror() }
+}
diff --git a/components/rust/core/src/bindings/librt.rs b/components/rust/core/src/bindings/librt.rs
new file mode 100644
index 0000000000..3f16c014fd
--- /dev/null
+++ b/components/rust/core/src/bindings/librt.rs
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author foxglove
+ * 2025-09-15 foxglove 1.0 version
+ */
+
+/*! RT-Thread OS API bindings
+
+Provides Rust wrappers for RT-Thread kernel and device driver APIs
+*/
+
+#![allow(non_camel_case_types)]
+
+use core::ffi::{c_char, c_int, c_uint, c_void, c_long, c_ulong, c_uchar};
+
+/* RT-Thread basic type definitions */
+pub type rt_base_t = c_long;
+pub type rt_ubase_t = c_ulong;
+pub type rt_err_t = rt_base_t;
+pub type rt_uint32_t = c_uint;
+pub type rt_int32_t = c_int;
+pub type rt_uint8_t = c_uchar;
+pub type rt_tick_t = rt_uint32_t;
+pub type rt_size_t = rt_ubase_t;
+
+pub type rt_thread_t = *mut c_void;
+pub type rt_sem_t = *mut c_void;
+pub type rt_mutex_t = *mut c_void;
+pub type rt_device_t = *mut c_void;
+pub type rt_mq_t = *mut c_void;
+
+/* RT-Thread error codes */
+pub const RT_EOK: rt_err_t = 0;
+pub const RT_ERROR: rt_err_t = 1;
+pub const RT_ETIMEOUT: rt_err_t = 2;
+pub const RT_EFULL: rt_err_t = 3;
+pub const RT_EEMPTY: rt_err_t = 4;
+pub const RT_ENOMEM: rt_err_t = 5;
+pub const RT_ENOSYS: rt_err_t = 6;
+pub const RT_EBUSY: rt_err_t = 7;
+pub const RT_EIO: rt_err_t = 8;
+pub const RT_EINTR: rt_err_t = 9;
+pub const RT_EINVAL: rt_err_t = 10;
+
+/* ============== Kernel object management ============== */
+unsafe extern "C" {
+ pub fn rt_object_get_type(object: *mut c_void) -> u8;
+ pub fn rt_object_find(name: *const c_char, object_type: u8) -> *mut c_void;
+}
+
+/* ============== Thread management ============== */
+unsafe extern "C" {
+ pub fn rt_thread_create(
+ name: *const c_char,
+ entry: extern "C" fn(*mut c_void),
+ parameter: *mut c_void,
+ stack_size: rt_size_t,
+ priority: rt_uint8_t,
+ tick: rt_tick_t,
+ ) -> rt_thread_t;
+
+ pub fn rt_thread_delete(thread: rt_thread_t) -> rt_err_t;
+ pub fn rt_thread_startup(thread: rt_thread_t) -> rt_err_t;
+ pub fn rt_thread_self() -> rt_thread_t;
+ pub fn rt_thread_yield() -> rt_err_t;
+ pub fn rt_thread_delay(tick: rt_tick_t) -> rt_err_t;
+ pub fn rt_thread_mdelay(ms: rt_uint32_t) -> rt_err_t;
+ pub fn rt_thread_suspend(thread: rt_thread_t) -> rt_err_t;
+ pub fn rt_thread_resume(thread: rt_thread_t) -> rt_err_t;
+}
+
+/* ============== Semaphore management ============== */
+unsafe extern "C" {
+ pub fn rt_sem_create(name: *const c_char, value: rt_uint32_t, flag: rt_uint8_t) -> rt_sem_t;
+ pub fn rt_sem_delete(sem: rt_sem_t) -> rt_err_t;
+ pub fn rt_sem_take(sem: rt_sem_t, time: rt_tick_t) -> rt_err_t;
+ pub fn rt_sem_trytake(sem: rt_sem_t) -> rt_err_t;
+ pub fn rt_sem_release(sem: rt_sem_t) -> rt_err_t;
+}
+
+/* ============== Mutex management ============== */
+unsafe extern "C" {
+ pub fn rt_mutex_create(name: *const c_char, flag: rt_uint8_t) -> rt_mutex_t;
+ pub fn rt_mutex_delete(mutex: rt_mutex_t) -> rt_err_t;
+ pub fn rt_mutex_take(mutex: rt_mutex_t, time: rt_tick_t) -> rt_err_t;
+ pub fn rt_mutex_release(mutex: rt_mutex_t) -> rt_err_t;
+}
+/* ============== Message queue management ============== */
+unsafe extern "C" {
+ pub fn rt_mq_create(name: *const c_char, msg_size: rt_size_t, max_msgs: rt_size_t, flag: rt_uint8_t) -> rt_mq_t;
+ pub fn rt_mq_send(mq: rt_mq_t, buffer: *const c_void, size: rt_size_t) -> rt_err_t;
+ pub fn rt_mq_send_wait(mq: rt_mq_t, buffer: *const c_void, size: rt_size_t, timeout: rt_int32_t) -> rt_err_t;
+ pub fn rt_mq_recv(mq: rt_mq_t, buffer: *mut c_void, size: rt_size_t, timeout: rt_int32_t) -> rt_base_t;
+ pub fn rt_mq_delete(mq: rt_mq_t) -> rt_err_t;
+ pub fn rt_mq_detach(mq: rt_mq_t) -> rt_err_t;
+}
+/* ============== Memory management ============== */
+unsafe extern "C" {
+ pub fn rt_malloc(size: rt_size_t) -> *mut c_void;
+ pub fn rt_free(ptr: *mut c_void);
+ pub fn rt_realloc(ptr: *mut c_void, newsize: rt_size_t) -> *mut c_void;
+ pub fn rt_calloc(count: rt_size_t, size: rt_size_t) -> *mut c_void;
+ pub fn rt_malloc_align(size: rt_size_t, align: rt_size_t) -> *mut c_void;
+ pub fn rt_free_align(ptr: *mut c_void);
+}
+
+/* ============== Device management ============== */
+unsafe extern "C" {
+ pub fn rt_device_find(name: *const c_char) -> rt_device_t;
+ pub fn rt_device_open(dev: rt_device_t, oflag: u16) -> rt_err_t;
+ pub fn rt_device_close(dev: rt_device_t) -> rt_err_t;
+ pub fn rt_device_read(
+ dev: rt_device_t,
+ pos: c_ulong,
+ buffer: *mut c_void,
+ size: rt_size_t,
+ ) -> rt_size_t;
+ pub fn rt_device_write(
+ dev: rt_device_t,
+ pos: c_ulong,
+ buffer: *const c_void,
+ size: rt_size_t,
+ ) -> rt_size_t;
+ pub fn rt_device_control(dev: rt_device_t, cmd: c_int, arg: *mut c_void) -> rt_err_t;
+}
+
+/* ============== System tick ============== */
+unsafe extern "C" {
+ pub fn rt_tick_get() -> rt_tick_t;
+ pub fn rt_tick_from_millisecond(ms: c_int) -> rt_tick_t;
+}
+
+/* ============== Debug output ============== */
+unsafe extern "C" {
+ pub fn rt_kprintf(fmt: *const u8, ...) -> c_int;
+ pub fn rt_kputs(str: *const u8) -> c_int;
+}
+
+/* ============== Interrupt management ============== */
+unsafe extern "C" {
+ pub fn rt_hw_interrupt_disable() -> rt_base_t;
+ pub fn rt_hw_interrupt_enable(level: rt_base_t);
+ pub fn rt_cpus_lock() -> rt_base_t;
+ pub fn rt_cpus_unlock(level: rt_base_t);
+ pub fn rt_interrupt_enter();
+ pub fn rt_interrupt_leave();
+ pub fn rt_interrupt_get_nest() -> u8;
+}
diff --git a/components/rust/core/src/bindings/mod.rs b/components/rust/core/src/bindings/mod.rs
new file mode 100644
index 0000000000..ae2610e9c8
--- /dev/null
+++ b/components/rust/core/src/bindings/mod.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 Bindings module
+ */
+pub mod libc;
+pub mod librt;
+
+/* Explicitly re-export only the needed items from librt to avoid namespace pollution. */
+/* Basic RT-Thread types */
+pub use librt::{
+ rt_base_t, rt_ubase_t, rt_err_t, rt_uint32_t, rt_int32_t, rt_uint8_t,
+ rt_tick_t, rt_size_t, rt_thread_t, rt_sem_t, rt_mutex_t, rt_device_t, rt_mq_t
+};
+
+/* RT-Thread error codes */
+pub use librt::{
+ RT_EOK, RT_ERROR, RT_ETIMEOUT, RT_EFULL, RT_EEMPTY, RT_ENOMEM,
+ RT_ENOSYS, RT_EBUSY, RT_EIO, RT_EINTR, RT_EINVAL
+};
+
+/* Thread management functions */
+pub use librt::{
+ rt_thread_create, rt_thread_delete, rt_thread_startup, rt_thread_self,
+ rt_thread_yield, rt_thread_delay, rt_thread_mdelay, rt_thread_suspend, rt_thread_resume
+};
+
+/* Semaphore management functions */
+pub use librt::{
+ rt_sem_create, rt_sem_delete, rt_sem_take, rt_sem_trytake, rt_sem_release
+};
+
+/* Mutex management functions */
+pub use librt::{
+ rt_mutex_create, rt_mutex_delete, rt_mutex_take, rt_mutex_release
+};
+
+/* Message queue management functions */
+pub use librt::{
+ rt_mq_create, rt_mq_send, rt_mq_send_wait, rt_mq_recv, rt_mq_delete, rt_mq_detach
+};
+
+/* Memory management functions */
+pub use librt::{
+ rt_malloc, rt_free, rt_realloc, rt_calloc, rt_malloc_align, rt_free_align,
+ rt_safe_malloc, rt_safe_free
+};
+
+/* Device management functions */
+pub use librt::{
+ rt_device_find, rt_device_open, rt_device_close, rt_device_read,
+ rt_device_write, rt_device_control
+};
+
+/* System tick functions */
+pub use librt::{
+ rt_tick_get, rt_tick_from_millisecond
+};
+
+/* Debug output functions */
+pub use librt::{
+ rt_kprintf, rt_kputs
+};
+
+/* Interrupt management functions */
+pub use librt::{
+ rt_hw_interrupt_disable, rt_hw_interrupt_enable, rt_cpus_lock, rt_cpus_unlock,
+ rt_interrupt_enter, rt_interrupt_leave, rt_interrupt_get_nest
+};
+
+/* Object management functions */
+pub use librt::{
+ rt_object_get_type, rt_object_find
+};
diff --git a/components/rust/core/src/fs.rs b/components/rust/core/src/fs.rs
new file mode 100644
index 0000000000..6ad02037ab
--- /dev/null
+++ b/components/rust/core/src/fs.rs
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author notes
+ * 2025-10-10 foxglove micro rust log component
+ */
+use alloc::string::{String, ToString};
+use alloc::ffi::CString;
+use crate::RTResult;
+use crate::RTTError::{FileCloseErr, FileFlushErr, FileOpenErr, FileReadErr, FileSeekErr, FileSetLengthErr, FileWriteErr};
+use crate::bindings::libc;
+use core::ffi::{c_void, c_int};
+
+pub struct File {
+ pub fd: i32,
+}
+
+pub struct OpenOptions {
+ pub path: String,
+ pub read: bool,
+ pub write: bool,
+ pub create: bool,
+ pub append: bool,
+ pub truncate: bool,
+}
+
+impl OpenOptions {
+ pub fn new() -> Self {
+ Self { path: String::new(), read: false, write: false, create: false, append: false, truncate: false }
+ }
+
+ pub fn read(&mut self, read: bool) -> &mut Self { self.read = read; self }
+ pub fn write(&mut self, write: bool) -> &mut Self { self.write = write; self }
+ pub fn create(&mut self, create: bool) -> &mut Self { self.create = create; self }
+ pub fn append(&mut self, append: bool) -> &mut Self { self.append = append; self }
+ pub fn truncate(&mut self, truncate: bool) -> &mut Self { self.truncate = truncate; self }
+
+ fn flags(&self) -> c_int {
+ let mut flags: c_int = 0;
+ flags |= if self.read && self.write { libc::O_RDWR } else if self.write { libc::O_WRONLY } else { libc::O_RDONLY };
+ if self.create { flags |= libc::O_CREAT; }
+ if self.append { flags |= libc::O_APPEND; }
+ if self.truncate { flags |= libc::O_TRUNC; }
+ flags
+ }
+
+ pub fn open(&mut self, path: &str) -> RTResult<File> {
+ self.path = path.to_string();
+ let cpath = match CString::new(self.path.clone()) { Ok(s) => s, Err(_) => return Err(FileOpenErr) };
+ let oflags = self.flags();
+ let fd = unsafe { libc::open(cpath.as_ptr(), oflags, 0o666 as c_int) };
+ if fd < 0 { Err(FileOpenErr) } else { Ok(File { fd }) }
+ }
+}
+
+impl Drop for File {
+ fn drop(&mut self) {
+ unsafe { libc::close(self.fd); }
+ }
+}
+
+impl File {
+ pub fn read_to_string(&self) -> RTResult<String> {
+ let mut buf = [0u8; 256];
+ let mut out = String::new();
+ self.seek(0)?;
+ loop {
+ let n = self.read(&mut buf)?;
+ if n == 0 { break; }
+ let s = alloc::string::String::from_utf8_lossy(&buf[..n]).into_owned();
+ out.push_str(&s);
+ }
+ Ok(out)
+ }
+
+ pub fn write_all(&self, buf: &str) -> RTResult<()> {
+ let mut written = 0usize;
+ let bytes = buf.as_bytes();
+ while written < bytes.len() {
+ let n = self.write(&bytes[written..])?;
+ written += n;
+ }
+ Ok(())
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> RTResult<usize> {
+ let n = unsafe { libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, buf.len() as libc::size_t) };
+ if n < 0 { Err(FileReadErr) } else { Ok(n as usize) }
+ }
+
+ pub fn write(&self, buf: &[u8]) -> RTResult<usize> {
+ let n = unsafe { libc::write(self.fd, buf.as_ptr() as *const c_void, buf.len() as libc::size_t) };
+ if n < 0 { Err(FileWriteErr) } else { Ok(n as usize) }
+ }
+
+ pub fn seek(&self, offset: i64) -> RTResult<i64> {
+ let n = unsafe { libc::lseek(self.fd, offset as libc::off_t, libc::SEEK_SET) };
+ if n < 0 { Err(FileSeekErr) } else { Ok(n) }
+ }
+
+ pub fn flush(&self) -> RTResult<()> {
+ let n = unsafe { libc::fsync(self.fd) };
+ if n < 0 { Err(FileFlushErr) } else { Ok(()) }
+ }
+
+ pub fn set_len(&self, len: i64) -> RTResult<()> {
+ let n = unsafe { libc::ftruncate(self.fd, len as libc::off_t) };
+ if n < 0 { Err(FileSetLengthErr) } else { Ok(()) }
+ }
+
+ pub fn close(&self) -> RTResult<()> {
+ let n = unsafe { libc::close(self.fd) };
+ if n < 0 { Err(FileCloseErr) } else { Ok(()) }
+ }
+}
diff --git a/components/rust/core/src/init.rs b/components/rust/core/src/init.rs
new file mode 100644
index 0000000000..3f8e326e82
--- /dev/null
+++ b/components/rust/core/src/init.rs
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-09-15 foxglove 1.0 version
+ */
+
+/* Rust component initialization module
+ * Handles initialization of the Rust component within RT-Thread.
+ */
+
+use crate::println;
+
+#[no_mangle]
+pub extern "C" fn rust_init() -> i32 {
+ println!("[rust_init]: Rust component initialized!");
+ 0
+}
diff --git a/components/rust/core/src/lib.rs b/components/rust/core/src/lib.rs
new file mode 100644
index 0000000000..2787b7240a
--- /dev/null
+++ b/components/rust/core/src/lib.rs
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author notes
+ * 2025-09-15 foxglove 1.0 version
+ * 2025-09-25 foxglove 1.1 version
+ * 2025-10-10 foxglove latest version
+ */
+
+/* RT-Thread Rust core library
+
+Provides basic system support functionality including file system, network,
+and device interfaces. Designed for embedded devices running RT-Thread.
+*/
+
+#![no_std]
+#![feature(alloc_error_handler)]
+#![feature(linkage)]
+#![allow(dead_code)]
+
+pub extern crate alloc;
+
+#[doc = "Alloc by rt-thread"]
+#[global_allocator]
+static GLOBAL: allocator::RttAlloc = allocator::RttAlloc;
+pub mod api;
+pub mod bindings;
+
+pub mod init;
+pub mod allocator;
+pub mod mutex;
+pub mod out;
+pub mod fs;
+pub mod panic;
+pub mod param;
+pub mod queue;
+pub mod sem;
+pub mod thread;
+pub mod time;
+pub mod libloader;
+
+mod prelude;
+pub use prelude::v1::*;
+
+/* Re-export initialization function */
+pub use init::rust_init;
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum RTTError {
+ ThreadStartupErr,
+ MutexTakeTimeout,
+ SemaphoreTakeTimeout,
+ QueueSendTimeout,
+ QueueReceiveTimeout,
+ OutOfMemory,
+
+ DeviceNotFound,
+ DeviceOpenFailed,
+ DeviceCloseFailed,
+ DeviceReadFailed,
+ DeviceWriteFailed,
+ DeviceTransFailed,
+ DeviceConfigFailed,
+ DeviceSetRxCallBackFailed,
+ DeviceSetTxCallBackFailed,
+
+ FileOpenErr,
+ FileCloseErr,
+ FileReadErr,
+ FileWriteErr,
+ FileSeekErr,
+ FileFlushErr,
+ FileDeleteErr,
+ FileSetLengthErr,
+ FileSyncErr,
+
+ FuncUnDefine,
+}
+
+pub type RTResult<T> = Result<T, RTTError>;
diff --git a/components/rust/core/src/libloader.rs b/components/rust/core/src/libloader.rs
new file mode 100644
index 0000000000..5fb66d98a9
--- /dev/null
+++ b/components/rust/core/src/libloader.rs
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-10-14 foxglove High-level libdl facade (libloader-style)
+ */
+
+use core::ffi::{c_char, c_int};
+pub use crate::api::libloading as libloading;
+pub use libloading::{RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NOW, last_error};
+use libloading::{DlError, Library, Symbol};
+use crate::println;
+
+/** Open a module with `RTLD_NOW | RTLD_GLOBAL` convenience flags.
+
+Safety: `path` must be a valid NUL-terminated C string pointer.
+*/
+pub unsafe fn dl_open(path: *const c_char) -> Result<Library, DlError> {
+ Library::open_global(path)
+}
+
+/** Open a module with custom flags.
+
+Safety: `path` must be a valid NUL-terminated C string pointer.
+*/
+pub unsafe fn dl_open_with_flags(path: *const c_char, flags: c_int) -> Result<Library, DlError> {
+ Library::open(path, flags)
+}
+
+/** Resolve a typed symbol from a library.
+
+Safety: Caller must ensure `T` matches the actual symbol type.
+*/
+pub unsafe fn dl_sym<'lib, T>(lib: &'lib Library, symbol: *const c_char) -> Result<Symbol<'lib, T>, DlError> {
+ lib.get_ptr(symbol)
+}
+
+pub fn dl_print_last_error() {
+ println!("libdl error: {}", libloading::DlError::Open(last_error()));
+}
+
+/** Use a library for a scoped operation; the library is closed on drop.
+
+Safety: `path` must be a valid NUL-terminated C string pointer.
+*/
+pub unsafe fn with_library<R, F>(path: *const c_char, flags: c_int, f: F) -> Result<R, DlError>
+where
+ F: FnOnce(&Library) -> R,
+{
+ let lib = Library::open(path, flags)?;
+ let result = f(&lib);
+ /* Drop will handle closing; return the result */
+ Ok(result)
+}
+
+#[macro_export]
+/** Get a function from a dynamic link library
+ * *You need `use libloader::libloading` first*
+ * * `lib_path`: the path of DLL
+ * * `fn_name`: The function name from dll
+ * * `call_name`: The call function name of `fn_name`
+ * * `ret`: return type of the function **if the function don't have return value, use "()" instead**
+ * * `(value: type)`: **(variadic argument)** The arguments of the function from dll
+ */
+macro_rules! get_libfn {
+ ($lib_path: expr, $fn_name: expr, $call_name: ident, $ret: ty, $($v: ident: $t:ty),*) => {
+ pub fn $call_name($($v: $t),*) -> $ret {
+ let lib = $crate::libloader::libloading::Library::new($lib_path).unwrap();
+ let func: $crate::libloader::libloading::Symbol<fn($($t,)*) -> $ret> = lib.get($fn_name.as_bytes()).unwrap();
+ func($($v,)*)
+ }
+ };
+ ($lib_path: expr, $fn_name: expr, $call_name:ident, $ret: ty) => {
+ pub fn $call_name() -> $ret {
+ let lib = $crate::libloader::libloading::Library::new($lib_path).unwrap();
+ let func: $crate::libloader::libloading::Symbol<fn() -> $ret> = lib.get($fn_name.as_bytes()).unwrap();
+ func()
+ }
+ };
+}
diff --git a/components/rust/core/src/mutex.rs b/components/rust/core/src/mutex.rs
new file mode 100644
index 0000000000..651fa7ef78
--- /dev/null
+++ b/components/rust/core/src/mutex.rs
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-10-10 foxglove RT-Thread Mutex implementation
+ */
+use crate::api::*;
+use crate::{panic::panic_on_atomic_context, RTTError};
+use alloc::fmt;
+pub use alloc::sync::{Arc, Weak};
+use core::cell::UnsafeCell;
+use core::ops::{Deref, DerefMut};
+use core::sync::atomic::*;
+
+const RT_WAITING_FOREVER: isize = -1;
+
+unsafe impl<T: Send, M: RawMutexOps + Sized> Send for Mutex<T, M> {}
+unsafe impl<T: Send, M: RawMutexOps + Sized> Sync for Mutex<T, M> {}
+
+pub struct Mutex<T: ?Sized, M = SleepMutex>
+where
+ M: RawMutexOps + Sized,
+{
+ mutex: M,
+ data: UnsafeCell<T>,
+}
+
+/* Impl for Default Mutex<M = SleepMutex, NAME = UnnamedObj> */
+impl<T> Mutex<T> {
+ pub fn new(t: T) -> Result<Self, RTTError> {
+ Ok(Mutex {
+ mutex: SleepMutex::create("Unnamed")?,
+ data: UnsafeCell::new(t),
+ })
+ }
+
+ pub fn new_with_name(t: T, name: &str) -> Result<Self, RTTError> {
+ Ok(Mutex {
+ mutex: SleepMutex::create(name)?,
+ data: UnsafeCell::new(t),
+ })
+ }
+
+ pub fn try_lock(&self, max_wait: isize) -> Result<MutexGuard<'_, SleepMutex, T>, RTTError> {
+ self.mutex.take(max_wait)?;
+ Ok(MutexGuard {
+ __mutex: &self.mutex,
+ __data: &self.data,
+ })
+ }
+
+ pub fn lock(&self) -> Result<MutexGuard<'_, SleepMutex, T>, RTTError> {
+ self.mutex.take(RT_WAITING_FOREVER)?;
+ Ok(MutexGuard {
+ __mutex: &self.mutex,
+ __data: &self.data,
+ })
+ }
+}
+
+/* Impl for all Mutex */
+impl<T, M: RawMutexOps> Mutex<T, M> {
+ pub fn spec_new(t: T) -> Result<Self, RTTError> {
+ Ok(Mutex {
+ mutex: M::create("Unnamed")?,
+ data: UnsafeCell::new(t),
+ })
+ }
+
+ pub fn spec_new_with_name(t: T, name: &str) -> Result<Self, RTTError> {
+ Ok(Mutex {
+ mutex: M::create(name)?,
+ data: UnsafeCell::new(t),
+ })
+ }
+
+ pub fn spec_try_lock(&self, max_wait: isize) -> Result<MutexGuard<'_, M, T>, RTTError> {
+ self.mutex.take(max_wait)?;
+ Ok(MutexGuard {
+ __mutex: &self.mutex,
+ __data: &self.data,
+ })
+ }
+
+ pub fn spec_lock(&self) -> Result<MutexGuard<'_, M, T>, RTTError> {
+ self.mutex.take(RT_WAITING_FOREVER)?;
+ Ok(MutexGuard {
+ __mutex: &self.mutex,
+ __data: &self.data,
+ })
+ }
+}
+
+impl<T: ?Sized> fmt::Debug for Mutex<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "Mutex address: {:?}", self.mutex)
+ }
+}
+
+pub struct MutexGuard<'a, M: RawMutexOps + 'a, T: ?Sized + 'a> {
+ __mutex: &'a M,
+ __data: &'a UnsafeCell<T>,
+}
+
+impl<'mutex, M: RawMutexOps, T: ?Sized> Deref for MutexGuard<'mutex, M, T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ unsafe { &*self.__data.get() }
+ }
+}
+
+impl<'mutex, M: RawMutexOps, T: ?Sized> DerefMut for MutexGuard<'mutex, M, T> {
+ fn deref_mut(&mut self) -> &mut T {
+ unsafe { &mut *self.__data.get() }
+ }
+}
+
+impl<'a, M: RawMutexOps, T: ?Sized> Drop for MutexGuard<'a, M, T> {
+ fn drop(&mut self) {
+ self.__mutex.release();
+ }
+}
+
+pub type CommonMutex = SleepMutex;
+pub struct SleepMutex(APIRawMutex);
+
+impl fmt::Debug for SleepMutex {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{:?}", self.0)
+ }
+}
+
+pub struct AtomicMutex(UnsafeCell<InterruptFlag>, AtomicBool);
+
+pub trait RawMutexOps: Sized {
+ fn create(name: &str) -> Result<Self, RTTError>;
+ fn take(&self, max_wait: isize) -> Result<(), RTTError>;
+ fn release(&self);
+ fn drop(&mut self);
+}
+
+impl RawMutexOps for AtomicMutex {
+ fn create(_name: &str) -> Result<Self, RTTError> {
+ Ok(AtomicMutex {
+ 0: UnsafeCell::new(INTERRUPT_FLAG_INIT),
+ 1: AtomicBool::new(false),
+ })
+ }
+
+ fn take(&self, max_wait: isize) -> Result<(), RTTError> {
+ let f;
+ unsafe {
+ f = self.0.get();
+ *f = interrupt_disable();
+ }
+ let ret = if max_wait == RT_WAITING_FOREVER {
+ while self
+ .1
+ .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
+ .is_err()
+ {
+ while self.1.load(Ordering::Relaxed) {}
+ }
+ Ok(())
+ } else {
+ if self
+ .1
+ .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
+ .is_ok()
+ {
+ Ok(())
+ } else {
+ unsafe {
+ interrupt_enable(*f);
+ }
+ Err(RTTError::MutexTakeTimeout)
+ }
+ };
+
+ return ret;
+ }
+
+ fn release(&self) {
+ self.1.store(false, Ordering::Release);
+ let f;
+ unsafe {
+ f = self.0.get();
+ interrupt_enable(*f);
+ }
+ }
+
+ fn drop(&mut self) {}
+}
+
+impl RawMutexOps for SleepMutex {
+ fn create(name: &str) -> Result<Self, RTTError> {
+ panic_on_atomic_context("create");
+ mutex_create(name)
+ .ok_or(RTTError::OutOfMemory)
+ .map(|m| SleepMutex(m))
+ }
+
+ fn take(&self, max_wait: isize) -> Result<(), RTTError> {
+ panic_on_atomic_context("take");
+ let ret = mutex_take(self.0, max_wait);
+ if !is_eok(ret) {
+ return Err(RTTError::MutexTakeTimeout);
+ }
+ Ok(())
+ }
+
+ fn release(&self) {
+ panic_on_atomic_context("release");
+ mutex_release(self.0);
+ }
+
+ fn drop(&mut self) {
+ panic_on_atomic_context("drop");
+ mutex_delete(self.0);
+ }
+} \ No newline at end of file
diff --git a/components/rust/core/src/out.rs b/components/rust/core/src/out.rs
new file mode 100644
index 0000000000..da191ade36
--- /dev/null
+++ b/components/rust/core/src/out.rs
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-10-10 foxglove RT-Thread Output implementation
+ */
+use crate::bindings::librt::rt_kprintf;
+use core::fmt::{self, Write};
+use core::cmp::min;
+
+fn up_cast(a: usize, b: usize) -> usize {
+ let r = a / b;
+ return if a % b == 0 { r } else { r + 1 };
+}
+
+fn puts(str: &str, kp: fn(s: *const u8)) {
+ let str = str.as_bytes();
+ let mut buf = [0 as u8; 129];
+ for i in 0..up_cast(str.len(), 128) {
+ let end = min(128, str.len() - i * 128);
+ for j in 0..end {
+ buf[j] = str[(j + i * 128) as usize];
+ }
+ buf[end] = 0;
+ kp(buf.as_ptr())
+ }
+}
+
+struct StdOut;
+
+impl fmt::Write for StdOut {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ fn rtt_kputs(s: *const u8) {
+ unsafe {
+ /* Use kprintf with "%s" format to avoid dependency on rt_kputs symbol */
+ rt_kprintf(b"%s\0".as_ptr(), s);
+ }
+ }
+ puts(s, rtt_kputs);
+ Ok(())
+ }
+}
+
+pub fn _print(args: fmt::Arguments) {
+ unsafe {
+ StdOut.write_fmt(args).unwrap_unchecked();
+ }
+}
+
+#[macro_export]
+macro_rules! print {
+ ($($arg:tt)*) => ({
+ $crate::out::_print(format_args!($($arg)*));
+ });
+}
+
+#[macro_export]
+macro_rules! println {
+ () => ({
+ $crate::out::_print(format_args!("\n"));
+ });
+ ($($arg:tt)*) => ({
+ $crate::out::_print(format_args!("{}\n", format_args!($($arg)*)));
+ });
+}
diff --git a/components/rust/core/src/panic.rs b/components/rust/core/src/panic.rs
new file mode 100644
index 0000000000..371f86fdb6
--- /dev/null
+++ b/components/rust/core/src/panic.rs
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-10-10 foxglove RT-Thread Panic implementation
+ */
+use crate::print;
+
+pub fn panic_on_atomic_context(s: &str) {
+ use crate::api::is_irq_context;
+ if is_irq_context() {
+ panic!("In irq context {}", s);
+ }
+}
+
+#[panic_handler]
+#[inline(never)]
+fn panic(info: &core::panic::PanicInfo) -> ! {
+ print!("{:}", info);
+ __rust_panic()
+}
+
+#[linkage = "weak"]
+#[unsafe(no_mangle)]
+fn __rust_panic() -> ! {
+ /* Default weak panic handler: loops forever to halt execution. */
+ print!("Entered weak panic handler: system will halt.");
+ loop {}
+}
diff --git a/components/rust/core/src/param.rs b/components/rust/core/src/param.rs
new file mode 100644
index 0000000000..3582a6430c
--- /dev/null
+++ b/components/rust/core/src/param.rs
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author notes
+ * 2025-10-10 foxglove Parameter module
+ */
+
+use alloc::vec::Vec;
+use core::iter::IntoIterator;
+use core::ops::Deref;
+
+#[derive(Debug)]
+pub struct ParamItem(&'static [u8]);
+
+impl Deref for ParamItem {
+ type Target = [u8];
+ fn deref(&self) -> &Self::Target {
+ return self.0;
+ }
+}
+
+impl ParamItem {
+ pub fn new(raw: &'static [u8]) -> Self {
+ ParamItem(raw)
+ }
+}
+
+pub type Param = <Vec<ParamItem> as IntoIterator>::IntoIter; \ No newline at end of file
diff --git a/components/rust/core/src/prelude/mod.rs b/components/rust/core/src/prelude/mod.rs
new file mode 100644
index 0000000000..494df6b82b
--- /dev/null
+++ b/components/rust/core/src/prelude/mod.rs
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author notes
+ * 2025-10-10 foxglove Prelude module
+ */
+#[path = "no_std.rs"]
+pub mod v1;
diff --git a/components/rust/core/src/prelude/no_std.rs b/components/rust/core/src/prelude/no_std.rs
new file mode 100644
index 0000000000..6f689cefd9
--- /dev/null
+++ b/components/rust/core/src/prelude/no_std.rs
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author notes
+ * 2025-10-10 foxglove Prelude module
+ */
+pub use core::cell::RefCell;
+pub use core::cell::UnsafeCell;
+pub use core::cmp::*;
+pub use core::fmt;
+pub use core::ptr::write_bytes;
+pub use core::iter::Iterator;
+pub use core::marker::PhantomData;
+pub use core::mem;
+pub use core::num::Wrapping;
+pub use core::ops::Range;
+pub use core::ops::{Deref, DerefMut};
+
+pub use alloc::boxed::Box;
+pub use alloc::rc::Rc;
+pub use alloc::sync::{Arc, Weak};
+
+pub use alloc::collections;
+pub use alloc::string;
+pub use alloc::vec;
diff --git a/components/rust/core/src/queue.rs b/components/rust/core/src/queue.rs
new file mode 100644
index 0000000000..82ae43cc54
--- /dev/null
+++ b/components/rust/core/src/queue.rs
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-10-10 foxglove RT-Thread Queue implementation
+ */
+
+use crate::api::*;
+use crate::{panic::panic_on_atomic_context, RTTError};
+use core::cell::UnsafeCell;
+use core::marker::PhantomData;
+use core::mem::{size_of, MaybeUninit};
+use core::ffi::c_void;
+
+const RT_WAITING_FOREVER: isize = -1;
+
+unsafe impl<T> Send for Queue<T> where T: Send {}
+unsafe impl<T> Sync for Queue<T> where T: Send {}
+
+#[derive(Debug)]
+pub struct Queue<T> {
+ queue: APIRawQueue,
+ item_type: PhantomData<UnsafeCell<T>>,
+}
+
+impl<T> Queue<T> {
+ pub fn new(max_size: usize) -> Result<Queue<T>, RTTError> {
+ queue_create("Unnamed", max_size as _, size_of::<T>() as _)
+ .ok_or(RTTError::OutOfMemory)
+ .map(|m| Queue {
+ queue: m,
+ item_type: PhantomData,
+ })
+ }
+
+ pub fn new_with_name(name: &str, max_size: usize) -> Result<Queue<T>, RTTError> {
+ queue_create(name, max_size as _, size_of::<T>() as _)
+ .ok_or(RTTError::OutOfMemory)
+ .map(|m| Queue {
+ queue: m,
+ item_type: PhantomData,
+ })
+ }
+
+ pub fn try_send(&self, item: T) -> Result<(), (RTTError, T)> {
+ self._send(item, 0)
+ }
+
+ pub fn send(&self, item: T, max_wait: i32) -> Result<(), (RTTError, T)> {
+ panic_on_atomic_context("send");
+ self._send(item, max_wait)
+ }
+
+ pub fn send_wait_forever(&self, item: T) -> Result<(), (RTTError, T)> {
+ panic_on_atomic_context("send_wait_forever");
+ self._send(item, RT_WAITING_FOREVER as _)
+ }
+
+ fn _send(&self, item: T, max_wait: i32) -> Result<(), (RTTError, T)> {
+ let inner = MaybeUninit::new(item);
+ let ret = queue_send_wait(
+ self.queue,
+ inner.as_ptr() as *const c_void,
+ size_of::<T>() as _,
+ max_wait,
+ );
+ return if !is_eok(ret) {
+ unsafe { Err((RTTError::QueueSendTimeout, inner.assume_init())) }
+ } else {
+ Ok(())
+ };
+ }
+
+ pub fn try_recv(&self) -> Result<T, RTTError> {
+ self._receive(0)
+ }
+
+ pub fn recv(&self, max_wait: i32) -> Result<T, RTTError> {
+ panic_on_atomic_context("recv");
+ self._receive(max_wait)
+ }
+
+ pub fn recv_wait_forever(&self) -> Result<T, RTTError> {
+ panic_on_atomic_context("recv_wait_forever");
+ self._receive(RT_WAITING_FOREVER as _)
+ }
+
+ fn _receive(&self, max_wait: i32) -> Result<T, RTTError> {
+ let mut inner = MaybeUninit::<T>::uninit();
+ let ret = queue_receive_wait(
+ self.queue,
+ inner.as_mut_ptr() as *mut c_void,
+ size_of::<T>() as _,
+ max_wait,
+ );
+ return if is_eok(ret) {
+ Ok(unsafe { inner.assume_init() })
+ } else {
+ Err(RTTError::QueueReceiveTimeout)
+ };
+ }
+}
+
+impl<T> Drop for Queue<T> {
+ fn drop(&mut self) {
+ queue_delete(self.queue);
+ }
+} \ No newline at end of file
diff --git a/components/rust/core/src/sem.rs b/components/rust/core/src/sem.rs
new file mode 100644
index 0000000000..502bf8560c
--- /dev/null
+++ b/components/rust/core/src/sem.rs
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-10-10 foxglove RT-Thread Semaphore implementation
+ */
+#![allow(dead_code)]
+
+use crate::api::*;
+use crate::{panic::panic_on_atomic_context, RTTError};
+use core::cell::UnsafeCell;
+use core::marker::PhantomData;
+
+const RT_WAITING_FOREVER: i32 = -1;
+
+unsafe impl Send for Semaphore {}
+unsafe impl Sync for Semaphore {}
+
+pub struct Semaphore {
+ data: PhantomData<*const UnsafeCell<usize>>,
+ sem: APIRawSem
+}
+
+impl Semaphore {
+ pub fn new() -> Result<Self, RTTError> {
+ Self::new_with_name("unknown")
+ }
+
+ pub fn new_with_name(name: &str) -> Result<Self, RTTError> {
+ panic_on_atomic_context("new_with_name");
+ semaphore_create(name)
+ .ok_or(RTTError::OutOfMemory)
+ .map(|m| Semaphore {
+ data: Default::default(),
+ sem: m
+ })
+ }
+
+ pub fn try_take(&self) -> Result<(), RTTError> {
+ let m = semaphore_try_take(self.sem);
+ if !is_eok(m) {
+ return Err(RTTError::SemaphoreTakeTimeout);
+ }
+ Ok(())
+ }
+
+ pub fn take_wait_forever(&self) -> Result<(), RTTError> {
+ panic_on_atomic_context("sem take_wait_forever");
+ let ret = semaphore_take(self.sem, RT_WAITING_FOREVER as u32);
+
+ if !is_eok(ret) {
+ return Err(RTTError::SemaphoreTakeTimeout);
+ }
+
+ Ok(())
+ }
+
+ pub fn take(&self, max_wait: i32) -> Result<(), RTTError> {
+ panic_on_atomic_context("sem take");
+ let ret = semaphore_take(self.sem, max_wait as u32);
+
+ if !is_eok(ret) {
+ return Err(RTTError::SemaphoreTakeTimeout);
+ }
+
+ Ok(())
+ }
+
+ pub fn release(&self) {
+ semaphore_release(self.sem);
+ }
+}
+
+impl Drop for Semaphore {
+ fn drop(&mut self) {
+ semaphore_delete(self.sem)
+ }
+} \ No newline at end of file
diff --git a/components/rust/core/src/thread.rs b/components/rust/core/src/thread.rs
new file mode 100644
index 0000000000..35e03f3c3a
--- /dev/null
+++ b/components/rust/core/src/thread.rs
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-10-10 foxglove RT-Thread Thread implementation
+ */
+use crate::alloc::boxed::Box;
+use crate::api::*;
+use crate::{RTResult, RTTError};
+use alloc::string::String;
+use core::mem;
+use core::ffi::c_void;
+
+#[derive(Debug)]
+pub struct Thread(APIRawThread);
+
+impl Thread {
+ pub fn delay(tick: usize) {
+ let _ = thread_delay(tick);
+ }
+
+ pub fn ms_delay(ms: usize) {
+ let _ = thread_m_delay(ms as i32);
+ }
+
+ pub fn new() -> ThreadBuilder {
+ ThreadBuilder {
+ th_name: "uname".into(),
+ th_stack_size: 4096,
+ th_priority: 10,
+ th_ticks: 10,
+ }
+ }
+
+ pub fn r#yield() {
+ thread_yield();
+ }
+
+ pub fn delete_thread(th: Self) {
+ let _ = thread_delete(th.0);
+ }
+
+ pub fn delete(&self) {
+ let _ = thread_delete(self.0);
+ }
+
+ unsafe fn spawn_inner(
+ name: String,
+ stack_size: u32,
+ priority: u8,
+ ticks: u32,
+ func: Box<dyn FnOnce()>,
+ ) -> Result<Self, RTTError> {
+ let func = Box::new(func);
+ let param = &*func as *const _ as *mut _;
+
+ extern "C" fn thread_func(param: *mut c_void) {
+ unsafe {
+ let run = Box::from_raw(param as *mut Box<dyn FnOnce()>);
+ run();
+ }
+ }
+
+ let th_handle = thread_create(
+ name.as_ref(),
+ thread_func,
+ param,
+ stack_size,
+ priority,
+ ticks,
+ )
+ .ok_or(RTTError::OutOfMemory)?;
+
+ let ret = match Self::_startup(th_handle) {
+ Ok(_) => {
+ mem::forget(func);
+ Ok(Thread(th_handle))
+ }
+ Err(e) => Err(e),
+ };
+
+ return ret;
+ }
+
+ fn _startup(th: APIRawThread) -> Result<(), RTTError> {
+ let ret = thread_startup(th);
+ return if is_eok(ret) {
+ Ok(())
+ } else {
+ Err(RTTError::ThreadStartupErr)
+ };
+ }
+
+ pub fn spawn<F>(
+ name: String,
+ stack_size: u32,
+ priority: u8,
+ ticks: u32,
+ func: F,
+ ) -> RTResult<Thread>
+ where
+ F: FnOnce() -> () + Send + 'static,
+ {
+ unsafe { Self::spawn_inner(name, stack_size, priority, ticks, Box::new(func)) }
+ }
+}
+
+unsafe impl Send for Thread {}
+
+pub struct ThreadBuilder {
+ th_name: String,
+ th_stack_size: u32,
+ th_priority: u8,
+ th_ticks: u32,
+}
+
+impl ThreadBuilder {
+ pub fn name(&mut self, name: &str) -> &mut Self {
+ self.th_name = name.into();
+ self
+ }
+
+ pub fn stack_size(&mut self, stack_size: u32) -> &mut Self {
+ self.th_stack_size = stack_size;
+ self
+ }
+
+ pub fn priority(&mut self, priority: u8) -> &mut Self {
+ self.th_priority = priority;
+ self
+ }
+
+ pub fn ticks(&mut self, ticks: u32) -> &mut Self {
+ self.th_ticks = ticks;
+ self
+ }
+
+ pub fn start<F>(&self, func: F) -> RTResult<Thread>
+ where
+ F: FnOnce() -> (),
+ F: Send + 'static,
+ {
+ Thread::spawn(
+ self.th_name.clone(),
+ self.th_stack_size,
+ self.th_priority,
+ self.th_ticks,
+ func,
+ )
+ }
+} \ No newline at end of file
diff --git a/components/rust/core/src/time.rs b/components/rust/core/src/time.rs
new file mode 100644
index 0000000000..60dcf47d1d
--- /dev/null
+++ b/components/rust/core/src/time.rs
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2006-2025, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-10-10 foxglove RT-Thread Time implementation
+ */
+use crate::api::*;
+use crate::bindings::libc;
+use core::time::Duration;
+
+pub fn sleep(time: Duration) {
+ let mut time = time.as_millis();
+ const MAX_DELAY: u128 = i32::MAX as u128;
+ const MAX_DELAY_P1: u128 = i32::MAX as u128 + 1;
+ loop {
+ match time {
+ 1..=MAX_DELAY => {
+ let _ = thread_m_delay(time as i32);
+ return;
+ }
+ 0 => return,
+ MAX_DELAY_P1..=u128::MAX => {
+ let _ = thread_m_delay(i32::MAX);
+ time -= i32::MAX as u128;
+ }
+ }
+ }
+}
+
+pub fn get_time() -> Duration {
+ let mut tv = libc::timeval {
+ tv_sec: 0,
+ tv_usec: 0,
+ };
+ unsafe {
+ libc::gettimeofday(&mut tv, core::ptr::null_mut());
+ }
+ Duration::new(tv.tv_sec as u64, tv.tv_usec as u32 * 1000)
+} \ No newline at end of file