edgefirst_tflite_sys/hal_ffi.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 Au-Zone Technologies. All Rights Reserved.
3
4//! FFI bindings for the HAL Delegate DMA-BUF API.
5//!
6//! These types mirror the stable C ABI defined in `edgefirst-hal-capi`
7//! (`hal.h`). The HAL owns the type definitions; function implementations
8//! live in delegate shared libraries (e.g., Neutron NPU delegate).
9//!
10//! Function pointers are loaded at runtime from the delegate `.so` using
11//! `libloading`, following the same pattern as [`super::vx_ffi`].
12
13use std::ffi::{c_char, c_int, c_void};
14
15// ---------------------------------------------------------------------------
16// HalDtype
17// ---------------------------------------------------------------------------
18
19/// Element data type for HAL tensors.
20///
21/// Mirrors `hal_dtype` from `edgefirst-hal-capi` (`hal.h`).
22#[repr(u32)]
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24pub enum HalDtype {
25 /// Unsigned 8-bit integer.
26 U8 = 0,
27 /// Signed 8-bit integer.
28 I8 = 1,
29 /// Unsigned 16-bit integer.
30 U16 = 2,
31 /// Signed 16-bit integer.
32 I16 = 3,
33 /// Unsigned 32-bit integer.
34 U32 = 4,
35 /// Signed 32-bit integer.
36 I32 = 5,
37 /// Unsigned 64-bit integer.
38 U64 = 6,
39 /// Signed 64-bit integer.
40 I64 = 7,
41 /// 16-bit floating point (half).
42 F16 = 8,
43 /// 32-bit floating point (float).
44 F32 = 9,
45 /// 64-bit floating point (double).
46 F64 = 10,
47}
48
49// ---------------------------------------------------------------------------
50// HalDmabufTensorInfo
51// ---------------------------------------------------------------------------
52
53/// Maximum number of dimensions in a delegate tensor shape.
54pub const HAL_DMABUF_MAX_NDIM: usize = 8;
55
56/// DMA-BUF tensor information returned by a delegate.
57///
58/// Describes a single tensor's DMA-BUF allocation, including the file
59/// descriptor, buffer geometry, and element type. The `fd` is borrowed
60/// from the delegate and must **not** be closed by the caller.
61///
62/// Fields are ordered to eliminate padding on LP64: all `usize` fields
63/// first (8-byte aligned), then smaller `c_int` and enum fields (4 bytes
64/// each). Total size: 96 bytes on LP64.
65///
66/// Mirrors `hal_dmabuf_tensor_info` from `edgefirst-hal-capi` (`hal.h`).
67#[repr(C)]
68#[derive(Debug, Clone, Copy)]
69pub struct HalDmabufTensorInfo {
70 /// Buffer size in bytes.
71 pub size: usize,
72 /// Byte offset within the DMA-BUF.
73 pub offset: usize,
74 /// Tensor dimensions (up to [`HAL_DMABUF_MAX_NDIM`]).
75 pub shape: [usize; 8],
76 /// Number of valid entries in `shape`.
77 pub ndim: usize,
78 /// DMA-BUF file descriptor (borrowed — do **not** close).
79 pub fd: c_int,
80 /// Element data type.
81 pub dtype: HalDtype,
82}
83
84impl Default for HalDmabufTensorInfo {
85 fn default() -> Self {
86 // SAFETY: HalDmabufTensorInfo is a #[repr(C)] plain-old-data struct.
87 // The HAL ABI contract requires zero-initialization, so an all-zero
88 // bit-pattern is a valid "empty" state.
89 unsafe { std::mem::zeroed() }
90 }
91}
92
93// Ensure the shape array length matches HAL_DMABUF_MAX_NDIM.
94const _: () = assert!(
95 std::mem::size_of::<[usize; 8]>() == std::mem::size_of::<[usize; HAL_DMABUF_MAX_NDIM]>()
96);
97
98// Compile-time layout assertion: 11 × usize + 1 × c_int + 1 × HalDtype
99// = 11×8 + 4 + 4 = 96 bytes on LP64 with no internal padding.
100#[cfg(target_pointer_width = "64")]
101const _: () = assert!(std::mem::size_of::<HalDmabufTensorInfo>() == 96);
102
103// ---------------------------------------------------------------------------
104// HalDmaBufFunctions
105// ---------------------------------------------------------------------------
106
107/// Function pointers for the HAL Delegate DMA-BUF API.
108///
109/// Loaded at runtime from the delegate shared library. Use
110/// [`HalDmaBufFunctions::try_load`] to attempt loading.
111///
112/// The `hal_delegate_t` parameter (`*mut c_void`) is the `TfLiteDelegate*`
113/// cast to an opaque handle.
114#[derive(Debug)]
115pub struct HalDmaBufFunctions {
116 /// `hal_dmabuf_get_instance` — returns the delegate handle.
117 pub get_instance: unsafe extern "C" fn() -> *mut c_void,
118 /// `hal_dmabuf_is_supported` — returns 1 if supported, 0 otherwise.
119 pub is_supported: unsafe extern "C" fn(*mut c_void) -> c_int,
120 /// `hal_dmabuf_get_tensor_info` — fills `info` for the given tensor index.
121 pub get_tensor_info:
122 unsafe extern "C" fn(*mut c_void, c_int, *mut HalDmabufTensorInfo, usize) -> c_int,
123 /// `hal_dmabuf_sync_for_device` — flush CPU caches for device access.
124 pub sync_for_device: unsafe extern "C" fn(*mut c_void, c_int) -> c_int,
125 /// `hal_dmabuf_sync_for_cpu` — invalidate caches for CPU access.
126 pub sync_for_cpu: unsafe extern "C" fn(*mut c_void, c_int) -> c_int,
127}
128
129impl HalDmaBufFunctions {
130 /// Attempt to load all HAL DMA-BUF function pointers from a delegate library.
131 ///
132 /// Returns `None` if any required symbol is missing.
133 ///
134 /// # Safety
135 ///
136 /// The library must remain loaded for the lifetime of this struct.
137 #[must_use]
138 pub unsafe fn try_load(lib: &libloading::Library) -> Option<Self> {
139 unsafe {
140 Some(Self {
141 get_instance: *lib.get(b"hal_dmabuf_get_instance\0").ok()?,
142 is_supported: *lib.get(b"hal_dmabuf_is_supported\0").ok()?,
143 get_tensor_info: *lib.get(b"hal_dmabuf_get_tensor_info\0").ok()?,
144 sync_for_device: *lib.get(b"hal_dmabuf_sync_for_device\0").ok()?,
145 sync_for_cpu: *lib.get(b"hal_dmabuf_sync_for_cpu\0").ok()?,
146 })
147 }
148 }
149}
150
151// ---------------------------------------------------------------------------
152// HalCameraAdaptorFormatInfo
153// ---------------------------------------------------------------------------
154
155/// Camera adaptor format information returned by a delegate.
156///
157/// Describes a camera format's channel layout and V4L2 `FourCC` code.
158///
159/// Mirrors `hal_camera_adaptor_format_info` from `edgefirst-hal-capi`
160/// (`hal.h`).
161#[repr(C)]
162#[derive(Debug, Clone, Copy)]
163pub struct HalCameraAdaptorFormatInfo {
164 /// Number of input channels for this format.
165 pub input_channels: c_int,
166 /// Number of output channels for this format.
167 pub output_channels: c_int,
168 /// V4L2 `FourCC` code (NUL-padded, up to 8 bytes).
169 pub fourcc: [u8; 8],
170}
171
172impl Default for HalCameraAdaptorFormatInfo {
173 fn default() -> Self {
174 // SAFETY: HalCameraAdaptorFormatInfo is a #[repr(C)] plain-old-data
175 // struct. The HAL ABI contract requires zero-initialization, so an
176 // all-zero bit-pattern is a valid "empty" state.
177 unsafe { std::mem::zeroed() }
178 }
179}
180
181// ---------------------------------------------------------------------------
182// HalCameraAdaptorFunctions
183// ---------------------------------------------------------------------------
184
185/// Function pointers for the HAL Delegate Camera Adaptor API.
186///
187/// Loaded at runtime from the delegate shared library. Use
188/// [`HalCameraAdaptorFunctions::try_load`] to attempt loading.
189///
190/// The `hal_delegate_t` parameter (`*mut c_void`) is the `TfLiteDelegate*`
191/// cast to an opaque handle.
192#[derive(Debug)]
193pub struct HalCameraAdaptorFunctions {
194 /// `hal_camera_adaptor_is_supported` — returns 1 if format is supported.
195 pub is_supported: unsafe extern "C" fn(*mut c_void, *const c_char) -> c_int,
196 /// `hal_camera_adaptor_get_format_info` — fills `info` for the given format.
197 pub get_format_info: unsafe extern "C" fn(
198 *mut c_void,
199 *const c_char,
200 *mut HalCameraAdaptorFormatInfo,
201 usize,
202 ) -> c_int,
203}
204
205impl HalCameraAdaptorFunctions {
206 /// Attempt to load all HAL Camera Adaptor function pointers from a delegate library.
207 ///
208 /// Returns `None` if any required symbol is missing.
209 ///
210 /// # Safety
211 ///
212 /// The library must remain loaded for the lifetime of this struct.
213 #[must_use]
214 pub unsafe fn try_load(lib: &libloading::Library) -> Option<Self> {
215 unsafe {
216 Some(Self {
217 is_supported: *lib.get(b"hal_camera_adaptor_is_supported\0").ok()?,
218 get_format_info: *lib.get(b"hal_camera_adaptor_get_format_info\0").ok()?,
219 })
220 }
221 }
222}
223
224// ---------------------------------------------------------------------------
225// Tests
226// ---------------------------------------------------------------------------
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 #[test]
233 fn hal_dtype_discriminants() {
234 assert_eq!(HalDtype::U8 as u32, 0);
235 assert_eq!(HalDtype::I8 as u32, 1);
236 assert_eq!(HalDtype::U16 as u32, 2);
237 assert_eq!(HalDtype::I16 as u32, 3);
238 assert_eq!(HalDtype::U32 as u32, 4);
239 assert_eq!(HalDtype::I32 as u32, 5);
240 assert_eq!(HalDtype::U64 as u32, 6);
241 assert_eq!(HalDtype::I64 as u32, 7);
242 assert_eq!(HalDtype::F16 as u32, 8);
243 assert_eq!(HalDtype::F32 as u32, 9);
244 assert_eq!(HalDtype::F64 as u32, 10);
245 }
246
247 #[test]
248 fn tensor_info_default_is_zeroed() {
249 let info = HalDmabufTensorInfo::default();
250 assert_eq!(info.size, 0);
251 assert_eq!(info.offset, 0);
252 assert_eq!(info.ndim, 0);
253 assert_eq!(info.fd, 0);
254 assert!(info.shape.iter().all(|&s| s == 0));
255 }
256
257 #[test]
258 fn hal_dmabuf_max_ndim() {
259 assert_eq!(HAL_DMABUF_MAX_NDIM, 8);
260 }
261
262 #[test]
263 fn camera_adaptor_format_info_default_is_zeroed() {
264 let info = HalCameraAdaptorFormatInfo::default();
265 assert_eq!(info.input_channels, 0);
266 assert_eq!(info.output_channels, 0);
267 assert!(info.fourcc.iter().all(|&b| b == 0));
268 }
269
270 #[test]
271 fn camera_adaptor_format_info_clone_copy() {
272 let mut info = HalCameraAdaptorFormatInfo {
273 input_channels: 4,
274 output_channels: 3,
275 ..Default::default()
276 };
277 info.fourcc[..4].copy_from_slice(b"RGBA");
278
279 let copied = info;
280 assert_eq!(copied.input_channels, 4);
281 assert_eq!(copied.output_channels, 3);
282 assert_eq!(&copied.fourcc[..4], b"RGBA");
283 }
284
285 #[test]
286 fn camera_adaptor_format_info_debug() {
287 let info = HalCameraAdaptorFormatInfo::default();
288 let debug = format!("{info:?}");
289 assert!(debug.contains("HalCameraAdaptorFormatInfo"));
290 assert!(debug.contains("input_channels"));
291 }
292}