C FFI Bindings
Relevant source files
The following files were used as context for generating this wiki page:
The rust-battery project provides cross-platform access to battery information by interfacing with various OS-specific C APIs and system interfaces. While the core library is written in Rust, it extensively uses Foreign Function Interface (FFI) bindings to communicate with low-level kernel drivers and system frameworks such as IOKit on macOS, IOCTL on Windows and FreeBSD, and SysFS on Linux.
These bindings enable the library to retrieve raw battery metrics—including state of charge, energy rates, voltage, and temperature—which are then recalculated into unified SI measurement units.
Sources: README.md:16-21, src/platform/windows/ffi/ioctl/mod.rs:5-6
Platform-Specific FFI Architectures
The library abstracts platform differences through a set of traits, but the underlying data acquisition is handled by platform-specific FFI implementations.
Windows IOCTL Interface
On Windows, the library utilizes windows-sys to interact with battery devices via DeviceIoControl. It defines idiomatic Rust wrappers around C-level structures returned by IOCTL requests.
Key structures include:
BatteryInformation: Wraps the Win32BATTERY_INFORMATIONstruct to provide metadata like design capacity and cycle count.BatteryStatus: Wraps the status structure for real-time charge and voltage data.
Sources: src/platform/windows/ffi/ioctl/mod.rs:5-10, src/platform/windows/ffi/ioctl/info.rs:7-10
macOS/Darwin IOKit Bindings
On macOS and iOS, the library interfaces with IOKit. It uses bindings to objc2-io-kit and objc2-core-foundation to query the I/O Registry for power source properties.
The data flow involves:
- Identifying a
PowerSourceobject in the registry. - Extracting a
CFDictionaryof properties. - Mapping C-string keys (e.g.,
kIOPMPSVoltageKey) to values.
Sources: src/platform/darwin/iokit/power_source.rs:11-20, src/platform/darwin/iokit/power_source.rs:43-60
FreeBSD ACPI IOCTL
FreeBSD implementation uses ioctl calls to /dev/acpi. It defines C-compatible repr(C) structures to match the kernel's acpiio.h definitions.
The diagram shows the interaction between the FreeBSD kernel and the FFI layer using IOCTL to populate ACPI-specific battery structures.
Sources: src/platform/freebsd/acpi.rs:10-30, src/platform/freebsd/acpi.rs:159-163
Key FFI Data Structures
The following table summarizes the primary C-compatible structures used across different platforms to facilitate FFI communication:
| Structure | Platform | Purpose | Source Reference |
|---|---|---|---|
BATTERY_INFORMATION | Windows | Provides design capacity, chemistry, and capabilities. | info.rs:7 |
AcpiBif | FreeBSD | Battery Information: static data like model, serial, and design voltage. | acpi.rs:40-55 |
AcpiBst | FreeBSD | Battery Status: dynamic data like current state, rate, and voltage. | acpi.rs:125-130 |
CFDictionary | macOS | Container for IOKit property keys and values. | power_source.rs:24 |
Sources: src/platform/windows/ffi/ioctl/info.rs, src/platform/freebsd/acpi.rs, src/platform/darwin/iokit/power_source.rs
Implementation Details
Data Translation and Safety
FFI bindings often require careful handling of C types. For example, the FreeBSD implementation uses a union for IOCTL arguments and manual byte-to-string conversion for model identifiers.
fn get_string(&self, bytes: &[u8]) -> Option<String> {
let striped = match bytes.iter().position(|x| *x == 0x00) {
Some(pos) => &bytes[..=pos],
None => return None,
};
match CStr::from_bytes_with_nul(striped) {
Ok(cstr) => Some(cstr.to_string_lossy().to_string()),
Err(_) => None,
}
}
Sources: src/platform/freebsd/acpi.rs:106-116
Error Handling in FFI
The Linux implementation handles specific OS error codes like ENODEV (Error 19) when reading sysfs files. This ensures that transient hardware states or driver peculiarities do not cause fatal library crashes.
The sequence diagram illustrates how the library gracefully handles specific OS-level FFI/IO errors during hardware discovery.
Sources: src/platform/linux/sysfs/fs.rs:10-14, src/platform/linux/sysfs/fs.rs:105-110
Summary
The C FFI bindings in rust-battery serve as the critical bridge between the safe Rust API and the unsafe, platform-dependent reality of hardware monitoring. By wrapping OS-specific structures (Win32 API, IOKit, ACPI IOCTL) into unified Rust types, the library provides a consistent interface for developers while managing the complexities of low-level system communication.
Sources: README.md:16-21, CHANGELOG.md:180-185