nros_platform/lib.rs
1//! Unified platform abstraction traits for nros.
2//!
3//! This crate defines the backend-agnostic interface that platform
4//! implementations (POSIX, Zephyr, FreeRTOS, bare-metal, etc.) must satisfy.
5//! RMW backends consume these traits via thin shim crates that translate
6//! RMW-specific C symbols (e.g., `z_clock_now`, `uxr_millis`) into calls
7//! on the active platform implementation.
8//!
9//! # Trait hierarchy
10//!
11//! Capabilities are split into independent sub-traits so each RMW backend
12//! can declare exactly what it needs:
13//!
14//! - [`PlatformClock`] — monotonic clock (required by all backends)
15//! - [`PlatformAlloc`] — heap allocation (zenoh-pico only)
16//! - [`PlatformSleep`] — sleep / delay (zenoh-pico only)
17//! - [`PlatformYield`] — cooperative yield (zenoh-pico `socket_wait_event`)
18//! - [`PlatformRandom`] — pseudo-random number generation (zenoh-pico only)
19//! - [`PlatformTime`] — wall-clock time (zenoh-pico only)
20//! - [`PlatformThreading`] — tasks, mutexes, condvars (multi-threaded platforms)
21//!
22//! # Compile-time resolution
23//!
24//! Exactly one platform feature must be enabled. The `ConcretePlatform`
25//! type alias (gated on any `platform-*` feature) resolves to the active
26//! backend, eliminating generic parameters.
27
28#![no_std]
29
30mod board;
31mod resolve;
32
33// Phase 212.N.1 — the Board trait family lives in `board/` (was a
34// flat `board.rs`); `BoardConfig` + `BoardTransportConfig` stay at
35// the crate root for back-compat. New 212.N consumers reach the
36// full surface (`Board`, `BoardInit`, `BoardEntry`, …) through
37// `nros_platform::board::*`.
38pub use board::{
39 Board, BoardConfig, BoardEntry, BoardExit, BoardInit, BoardPrint, BoardTransportConfig,
40 DeployOverlay, DispatchStrategy, EmbassyBoardEntry, NetworkWait, NodeDispatchRuntime,
41 NullNodeRuntime, RticBoardEntry, RuntimeCtx, RuntimeError, SignaledCallback, TierSpec,
42 TransportBringup, freertos_priority_for, posix_nice_for, threadx_priority_for,
43};
44// Phase 214.K.1 — backward-compat alias at the crate root. See
45// `board::NodeRuntime` for the deprecation note.
46#[allow(deprecated)]
47pub use board::NodeRuntime;
48// Phase 212.N.2 — `NetworkError` is the return type any external
49// `NetworkWait` impl carries, so it needs to be reachable at the
50// crate root. The `board` module stays private; this re-export keeps
51// the boundary clean.
52pub use board::network::NetworkError;
53
54// Phase 129.C.3.b — `NET_*` constants exported unconditionally
55// (see `resolve.rs`). `ConcretePlatform` keeps its feature gate
56// because the type alias still needs a concrete platform crate
57// linked in.
58pub use resolve::{NET_ENDPOINT_ALIGN, NET_ENDPOINT_SIZE, NET_SOCKET_ALIGN, NET_SOCKET_SIZE};
59
60#[cfg(any(
61 feature = "platform-posix",
62 feature = "platform-cffi",
63 feature = "platform-mps2-an385",
64 feature = "platform-stm32f4",
65 feature = "platform-esp32-qemu",
66 feature = "platform-nuttx",
67 feature = "platform-freertos",
68 feature = "platform-threadx",
69 feature = "platform-zephyr",
70 feature = "platform-orin-spe",
71))]
72pub use resolve::ConcretePlatform;
73
74// Re-export every trait from the split-out `nros-platform-api` crate so
75// existing `use nros_platform::PlatformClock;` imports keep working.
76pub use nros_platform_api::*;
77
78// Link-graph anchor — relays an in-rlib `#[used]` static to the
79// `_nros_force_link_cffi` symbol that lives in `nros-platform-cffi`.
80// Downstream crates (`nros-rmw-zenoh`, the C/C++ FFI) reference
81// `__FORCE_LINK_CFFI` from their own `#[used]` static, which chains
82// up through this crate to cffi and keeps the `libnros_platform_posix.a`
83// static lib in the final link. Without the chain, rustc elides the
84// cffi rlib and every `nros_platform_*` C symbol is unresolved.
85#[cfg(feature = "platform-posix")]
86#[doc(hidden)]
87#[used]
88pub static __FORCE_LINK_CFFI: extern "C" fn() = nros_platform_cffi::_nros_force_link_cffi;
89
90// ============================================================================
91// Phase 248 C7 — Zephyr platform helper (relocated from `nros::platform::zephyr`)
92// ============================================================================
93/// Zephyr-specific platform helpers.
94///
95/// On Zephyr's `native_sim`, the default network interface is assigned an IPv4
96/// address at boot, but the underlying TAP link reports `net_if_is_up() == false`
97/// for ~100–200 ms until the host side is ready. Opening a zenoh session before
98/// that returns `TransportError::ConnectionFailed`. Call [`zephyr::wait_network`]
99/// before `Executor::open`. Mirrors the `nros_platform_zephyr_wait_network()` C
100/// helper the C/C++ examples use; the symbol is RMW-independent (defined in
101/// `nros-platform-zephyr`, compiled in every RMW build). Equivalent to
102/// `nros-board-zephyr`'s `ZephyrBoard::wait_link_up`.
103#[cfg(feature = "platform-zephyr")]
104pub mod zephyr {
105 unsafe extern "C" {
106 fn nros_platform_zephyr_wait_network(timeout_ms: i32) -> i32;
107 }
108
109 /// Block until the default Zephyr network interface is operational, or the
110 /// timeout expires. `Ok(())` on link-up, `Err(())` on timeout.
111 pub fn wait_network(timeout_ms: i32) -> Result<(), ()> {
112 // SAFETY: `nros_platform_zephyr_wait_network` has no preconditions beyond
113 // being called from a Zephyr thread context — always true in a Zephyr app.
114 let ret = unsafe { nros_platform_zephyr_wait_network(timeout_ms) };
115 if ret == 0 { Ok(()) } else { Err(()) }
116 }
117}
118
119// ============================================================================
120// Phase 71.27 — opt-in `#[global_allocator]`
121// ============================================================================
122//
123// On bare-metal / RTOS targets DDS + heapless futures need a real
124// heap. Each `nros-platform-*` crate already implements `PlatformAlloc`
125// against its native heap (`pvPortMalloc` on FreeRTOS,
126// `tx_byte_allocate` on ThreadX, `kmm_malloc` on NuttX,
127// `k_malloc` on Zephyr, libc `malloc` on POSIX). This module promotes
128// that trait impl into a `#[global_allocator]` so application crates
129// don't have to write per-platform glue.
130//
131// Off by default — `platform-posix` users link against libstd's
132// allocator. Enable via `nros-platform/global-allocator` in the
133// example crate's `Cargo.toml` to wire it in.
134
135#[cfg(all(feature = "global-allocator", not(feature = "std")))]
136mod global_allocator {
137 use core::{
138 alloc::{GlobalAlloc, Layout},
139 ffi::c_void,
140 };
141
142 use crate::ConcretePlatform;
143 use nros_platform_api::PlatformAlloc;
144
145 /// `GlobalAlloc` adapter over `<ConcretePlatform as PlatformAlloc>`.
146 pub struct PlatformGlobalAllocator;
147
148 unsafe impl GlobalAlloc for PlatformGlobalAllocator {
149 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
150 // Most RTOS heaps don't honor alignment > sizeof(void*).
151 // DDS's heaviest types are pointer-aligned, so this
152 // matches the typical 8-byte heap alignment without
153 // over-allocating. Callers that need larger alignment
154 // (e.g. SIMD) should layer a custom allocator on top.
155 let _ = layout.align();
156 <ConcretePlatform as PlatformAlloc>::alloc(layout.size()) as *mut u8
157 }
158
159 unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
160 <ConcretePlatform as PlatformAlloc>::dealloc(ptr as *mut c_void)
161 }
162 }
163
164 #[global_allocator]
165 static ALLOCATOR: PlatformGlobalAllocator = PlatformGlobalAllocator;
166}