diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7b8c8b2..405a135 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -22,7 +22,7 @@ jobs:
- name: Check code format
run: cargo fmt --all -- --check
- name: Clippy
- run: cargo clippy --manifest-path ./fdt-parser/Cargo.toml --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
+ run: cargo clippy --manifest-path ./fdt-parser/Cargo.toml --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
- name: Build
run: cargo build -p fdt-parser --target ${{ matrix.targets }} --all-features
- name: Install dtc
@@ -30,4 +30,31 @@ jobs:
run: sudo apt-get install device-tree-compiler
- name: Unit test
if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
- run: cargo test --target ${{ matrix.targets }} -- --nocapture
\ No newline at end of file
+ run: cargo test --target ${{ matrix.targets }} -- --nocapture
+
+ doc:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ permissions:
+ contents: write
+ env:
+ default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }}
+ RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: nightly-2025-12-12
+ - name: Build docs
+ continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
+ run: |
+ cargo doc --no-deps --all-features
+ printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
+ - name: Deploy to Github Pages
+ if: ${{ github.ref == env.default-branch }}
+ uses: JamesIves/github-pages-deploy-action@v4
+ with:
+ single-commit: true
+ branch: gh-pages
+ folder: target/doc
\ No newline at end of file
diff --git a/dtb-file/Cargo.toml b/dtb-file/Cargo.toml
index b26d444..2a0daef 100644
--- a/dtb-file/Cargo.toml
+++ b/dtb-file/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "dtb-file"
-version = "0.1.0"
+version = "0.1.1"
edition = "2024"
publish = false
diff --git a/dtb-file/src/lib.rs b/dtb-file/src/lib.rs
index 1ee2d51..b520dc4 100644
--- a/dtb-file/src/lib.rs
+++ b/dtb-file/src/lib.rs
@@ -1,3 +1,9 @@
+//! Test data and sample Device Tree Blob (DTB) files for the FDT parser.
+//!
+//! This crate provides embedded DTB files from various hardware platforms
+//! for testing purposes, along with a helper struct to ensure 4-byte alignment
+//! required by the FDT specification.
+
use core::ops::Deref;
const TEST_RPI_4_FDT: &[u8] = include_bytes!("dtb/bcm2711-rpi-4-b.dtb");
@@ -6,32 +12,43 @@ const TEST_QEMU_FDT: &[u8] = include_bytes!("dtb/qemu_pci.dtb");
const TEST_3568_FDT: &[u8] = include_bytes!("dtb/rk3568-firefly-roc-pc-se.dtb");
const TEST_RESERVE_FDT: &[u8] = include_bytes!("dtb/test_reserve.dtb");
+/// Returns the FDT data for Raspberry Pi 4 Model B.
pub fn fdt_rpi_4b() -> Align4Vec {
Align4Vec::new(TEST_RPI_4_FDT)
}
+/// Returns the FDT data for Phytium platform.
pub fn fdt_phytium() -> Align4Vec {
Align4Vec::new(TEST_PHYTIUM_FDT)
}
+/// Returns the FDT data for QEMU with PCI support.
pub fn fdt_qemu() -> Align4Vec {
Align4Vec::new(TEST_QEMU_FDT)
}
+/// Returns the FDT data for RK3568 Firefly ROC PC SE.
pub fn fdt_3568() -> Align4Vec {
Align4Vec::new(TEST_3568_FDT)
}
+/// Returns the FDT data with reserved memory entries for testing.
pub fn fdt_reserve() -> Align4Vec {
Align4Vec::new(TEST_RESERVE_FDT)
}
+/// A 4-byte aligned buffer for FDT data.
+///
+/// The Device Tree Blob specification requires that the FDT structure
+/// be 4-byte aligned in memory. This wrapper allocates aligned memory
+/// and provides raw pointer access for FDT parsing.
pub struct Align4Vec {
ptr: *mut u8,
size: usize,
}
impl Align4Vec {
+ /// Creates a new 4-byte aligned buffer from the provided data.
pub fn new(data: &[u8]) -> Self {
let size = data.len();
let layout = core::alloc::Layout::from_size_align(size, 4).unwrap();
@@ -40,12 +57,14 @@ impl Align4Vec {
Align4Vec { ptr, size }
}
+ /// Returns a raw pointer to the aligned buffer.
pub fn ptr(&self) -> *mut u8 {
self.ptr
}
}
impl Drop for Align4Vec {
+ /// Deallocates the aligned buffer when the `Align4Vec` is dropped.
fn drop(&mut self) {
let layout = core::alloc::Layout::from_size_align(self.size, 4).unwrap();
unsafe { std::alloc::dealloc(self.ptr, layout) };
@@ -53,6 +72,7 @@ impl Drop for Align4Vec {
}
impl Deref for Align4Vec {
+ /// Allows treating `Align4Vec` as a byte slice for convenient data access.
type Target = [u8];
fn deref(&self) -> &Self::Target {
diff --git a/dtb-tool/Cargo.toml b/dtb-tool/Cargo.toml
index 361fe8a..aaf6a83 100644
--- a/dtb-tool/Cargo.toml
+++ b/dtb-tool/Cargo.toml
@@ -1,9 +1,9 @@
[package]
name = "dtb-tool"
-version = "0.1.0"
+version = "0.1.1"
authors = ["Zhourui "]
edition = "2021"
-repository = "https://github.com/qclic/fdt-parser"
+repository = "https://github.com/drivercraft/fdt-parser"
documentation = "https://docs.rs/fdt-parser"
license = "MPL-2.0"
description = "A pure-Rust `#![no_std]` crate for parsing FDT"
diff --git a/dtb-tool/src/main.rs b/dtb-tool/src/main.rs
index 5c8af93..dba6f9f 100644
--- a/dtb-tool/src/main.rs
+++ b/dtb-tool/src/main.rs
@@ -1,16 +1,22 @@
+//! Command-line tool for inspecting and converting Device Tree Blob (DTB) files.
+//!
+//! This tool reads a DTB file, parses it using the `fdt_parser` library,
+//! and outputs a human-readable text representation showing the device tree
+//! structure including nodes, compatible strings, and memory reservations.
+
use clap::Parser;
use fdt_parser::Fdt;
use std::io::Write;
-/// Simple DTB parser
+/// Command-line arguments for the DTB parser tool.
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
- /// dtb file path
+ /// Path to the input DTB file
#[arg(short, long)]
input: String,
- /// output file path
+ /// Path to the output text file
#[arg(short, long)]
output: String,
}
diff --git a/example_all_nodes.rs b/example_all_nodes.rs
index c07112d..5c89aa8 100644
--- a/example_all_nodes.rs
+++ b/example_all_nodes.rs
@@ -1,14 +1,14 @@
-// 示例:使用 all_nodes 函数
+// Example: Using the all_nodes function
extern crate alloc;
use alloc::{string::String, vec::Vec};
use fdt_edit::{Fdt, Node, NodeRef};
fn main() {
- // 创建一个示例 FDT
+ // Create an example FDT
let mut fdt = Fdt::new();
- // 添加一些示例节点
+ // Add some example nodes
{
let root = &mut fdt.root;
let mut soc = Node::new_raw("soc");
@@ -16,44 +16,44 @@ fn main() {
let mut gpio = Node::new_raw("gpio@5000");
let mut led = Node::new_raw("led");
- // 设置属性
+ // Set properties
uart.add_property(fdt_edit::Property::new_str("compatible", "vendor,uart"));
gpio.add_property(fdt_edit::Property::new_str("compatible", "vendor,gpio"));
led.add_property(fdt_edit::Property::new_str("compatible", "vendor,led"));
- // 构建树结构
+ // Build the tree structure
gpio.add_child(led);
soc.add_child(uart);
soc.add_child(gpio);
root.add_child(soc);
}
- // 使用 all_nodes 获取所有节点
+ // Use all_nodes to get all nodes
let all_nodes: Vec = fdt.all_nodes().collect();
- println!("FDT 中所有节点 (深度优先遍历):");
+ println!("All nodes in FDT (depth-first traversal):");
for (i, node_ref) in all_nodes.iter().enumerate() {
println!(
- "{}: 节点 '{}', 路径: '{}', 深度: {}",
+ "{}: Node '{}', Path: '{}', Depth: {}",
i + 1,
node_ref.node.name(),
node_ref.context.current_path,
node_ref.context.depth
);
- // 显示节点的 compatible 属性
+ // Display the node's compatible property
let compatibles: Vec<&str> = node_ref.compatibles();
if !compatibles.is_empty() {
println!(" Compatible: {:?}", compatibles);
}
}
- // 使用 find_compatible 查找特定节点
+ // Use find_compatible to find specific nodes
let uart_nodes = fdt.find_compatible(&["vendor,uart"]);
- println!("\n找到 UART 节点:");
+ println!("\nFound UART nodes:");
for node_ref in uart_nodes {
println!(
- " 节点: {}, 完整路径: '{}'",
+ " Node: {}, Full path: '{}'",
node_ref.node.name(),
node_ref.context.current_path
);
diff --git a/fdt-edit/Cargo.toml b/fdt-edit/Cargo.toml
index 308a3eb..ab57a7a 100644
--- a/fdt-edit/Cargo.toml
+++ b/fdt-edit/Cargo.toml
@@ -11,7 +11,7 @@ license = "MIT OR Apache-2.0"
name = "fdt-edit"
readme = "README.md"
repository = "https://github.com/drivercraft/fdt-parser"
-version = "0.1.6"
+version = "0.1.7"
[dependencies]
enum_dispatch = "0.3.13"
diff --git a/fdt-edit/examples/fdt_debug_demo.rs b/fdt-edit/examples/fdt_debug_demo.rs
index 7bcb469..179049b 100644
--- a/fdt-edit/examples/fdt_debug_demo.rs
+++ b/fdt-edit/examples/fdt_debug_demo.rs
@@ -1,6 +1,7 @@
-//! FDT 深度调试演示
+//! FDT deep debug demonstration
//!
-//! 演示如何使用新的深度调试功能来遍历和打印设备树的所有节点
+//! Demonstrates how to use the new deep debug functionality to traverse
+//! and print all nodes in the device tree.
use dtb_file::fdt_rpi_4b;
use fdt_edit::Fdt;
@@ -8,17 +9,17 @@ use fdt_edit::Fdt;
fn main() -> Result<(), Box> {
env_logger::init();
- // 从 RPI 4B DTB 数据创建 FDT
+ // Create FDT from RPI 4B DTB data
let raw_data = fdt_rpi_4b();
let fdt = Fdt::from_bytes(&raw_data)?;
- println!("=== FDT 基本调试信息 ===");
- // 基本调试格式(紧凑)
+ println!("=== FDT Basic Debug Information ===");
+ // Basic debug format (compact)
println!("{:?}", fdt);
println!();
- println!("=== FDT 深度调试信息 ===");
- // 深度调试格式(遍历所有节点)
+ println!("=== FDT Deep Debug Information ===");
+ // Deep debug format (traverses all nodes)
println!("{:#?}", fdt);
Ok(())
diff --git a/fdt-edit/src/ctx.rs b/fdt-edit/src/ctx.rs
index 56c7859..f2c2771 100644
--- a/fdt-edit/src/ctx.rs
+++ b/fdt-edit/src/ctx.rs
@@ -1,3 +1,9 @@
+//! Context for FDT traversal and node lookup.
+//!
+//! This module provides the `Context` type which maintains state during
+//! FDT parsing and traversal, including parent references, phandle mappings,
+//! and inherited properties like address-cells and size-cells.
+
use alloc::{collections::BTreeMap, string::String, vec::Vec};
use fdt_raw::{Phandle, Status};
@@ -5,27 +11,32 @@ use fdt_raw::{Phandle, Status};
use crate::{Node, RangesEntry};
// ============================================================================
-// FDT 上下文
+// FDT Context
// ============================================================================
-/// 遍历上下文,存储从根到当前节点的父节点引用栈
+/// Traversal context storing parent node reference stack.
+///
+/// The context maintains state during FDT parsing and tree traversal,
+/// including the stack of parent nodes from root to the current position
+/// and mappings for efficient node lookups by phandle.
#[derive(Clone, Default)]
pub struct Context<'a> {
- /// 父节点引用栈(从根节点到当前节点的父节点)
- /// 栈底是根节点,栈顶是当前节点的直接父节点
+ /// Parent node reference stack (from root to current node's parent)
+ /// The stack bottom is the root node, the stack top is the direct parent
pub parents: Vec<&'a Node>,
- /// phandle 到节点引用的映射
- /// 用于通过 phandle 快速查找节点(如中断父节点)
+ /// Phandle to node reference mapping
+ /// Used for fast node lookup by phandle (e.g., interrupt parent)
pub phandle_map: BTreeMap,
}
impl<'a> Context<'a> {
- /// 创建新的上下文
+ /// Creates a new empty context.
pub fn new() -> Self {
Self::default()
}
+ /// Returns the current path as a string.
pub fn current_path(&self) -> String {
self.parents
.iter()
@@ -34,35 +45,39 @@ impl<'a> Context<'a> {
.join("/")
}
- /// 创建用于根节点的上下文
+ /// Creates a context for the root node.
pub fn for_root() -> Self {
Self::default()
}
- /// 获取当前深度(父节点数量 + 1)
+ /// Returns the current depth (parent count + 1).
pub fn depth(&self) -> usize {
self.parents.len() + 1
}
- /// 获取直接父节点
+ /// Returns the direct parent node.
pub fn parent(&self) -> Option<&'a Node> {
self.parents.last().copied()
}
- /// 获取父节点的 #address-cells
- /// 优先从直接父节点获取,否则返回默认值 2
+ /// Returns the parent's #address-cells value.
+ ///
+ /// Gets the value from the direct parent node, or returns 2 as default.
pub fn parent_address_cells(&self) -> u32 {
self.parent().and_then(|p| p.address_cells()).unwrap_or(2)
}
- /// 获取父节点的 #size-cells
- /// 优先从直接父节点获取,否则返回默认值 1
+ /// Returns the parent's #size-cells value.
+ ///
+ /// Gets the value from the direct parent node, or returns 1 as default.
pub fn parent_size_cells(&self) -> u32 {
self.parent().and_then(|p| p.size_cells()).unwrap_or(1)
}
- /// 查找中断父节点 phandle
- /// 从当前父节点向上查找,返回最近的 interrupt-parent
+ /// Finds the interrupt parent phandle.
+ ///
+ /// Searches upward through the parent stack to find the nearest
+ /// interrupt-parent property.
pub fn interrupt_parent(&self) -> Option {
for parent in self.parents.iter().rev() {
if let Some(phandle) = parent.interrupt_parent() {
@@ -72,8 +87,9 @@ impl<'a> Context<'a> {
None
}
- /// 检查节点是否被禁用
- /// 检查父节点栈中是否有任何节点被禁用
+ /// Checks if the node is disabled.
+ ///
+ /// Returns true if any parent in the stack has status = "disabled".
pub fn is_disabled(&self) -> bool {
for parent in &self.parents {
if matches!(parent.status(), Some(Status::Disabled)) {
@@ -83,53 +99,56 @@ impl<'a> Context<'a> {
false
}
- /// 解析当前路径上所有父节点的 ranges,用于地址转换
- /// 返回从根到父节点的 ranges 栈
+ /// Collects ranges from all parent nodes for address translation.
+ ///
+ /// Returns a stack of ranges from root to parent, used for translating
+ /// device addresses to CPU physical addresses.
pub fn collect_ranges(&self) -> Vec> {
let mut ranges_stack = Vec::new();
- let mut prev_address_cells = 2; // 根节点默认
+ let mut prev_address_cells = 2; // Root node default
for parent in &self.parents {
if let Some(ranges) = parent.ranges(prev_address_cells) {
ranges_stack.push(ranges);
}
- // 更新 address cells 为当前节点的值,供下一级使用
+ // Update address cells to current node's value for next level
prev_address_cells = parent.address_cells().unwrap_or(2);
}
ranges_stack
}
- /// 获取最近一层的 ranges(用于当前节点的地址转换)
+ /// Returns the most recent ranges layer (for current node's address translation).
pub fn current_ranges(&self) -> Option> {
- // 需要父节点来获取 ranges
+ // Need parent node to get ranges
if self.parents.is_empty() {
return None;
}
let parent = self.parents.last()?;
- // 获取父节点的父节点的 address_cells
+ // Get parent node's parent's address_cells
let grandparent_address_cells = if self.parents.len() >= 2 {
self.parents[self.parents.len() - 2]
.address_cells()
.unwrap_or(2)
} else {
- 2 // 根节点默认
+ 2 // Root node default
};
parent.ranges(grandparent_address_cells)
}
+ /// Pushes a node onto the parent stack.
pub fn push(&mut self, node: &'a Node) {
self.parents.push(node);
}
- /// 通过 phandle 查找节点
+ /// Finds a node by its phandle value.
pub fn find_by_phandle(&self, phandle: Phandle) -> Option<&'a Node> {
self.phandle_map.get(&phandle).copied()
}
- /// 从 Fdt 构建 phandle 映射
+ /// Builds a phandle mapping from a node tree.
pub fn build_phandle_map_from_node(node: &'a Node, map: &mut BTreeMap) {
if let Some(phandle) = node.phandle() {
map.insert(phandle, node);
diff --git a/fdt-edit/src/encode.rs b/fdt-edit/src/encode.rs
index ad43b81..02a4f4d 100644
--- a/fdt-edit/src/encode.rs
+++ b/fdt-edit/src/encode.rs
@@ -1,6 +1,7 @@
-//! FDT 编码模块
+//! FDT encoding module.
//!
-//! 将 Fdt 结构序列化为 DTB 二进制格式
+//! This module handles serialization of the `Fdt` structure into the
+//! DTB (Device Tree Blob) binary format.
use alloc::{string::String, vec::Vec};
use core::ops::Deref;
@@ -8,17 +9,19 @@ use fdt_raw::{FDT_MAGIC, Token};
use crate::{Fdt, Node};
-/// FDT 二进制数据
+/// FDT binary data container.
+///
+/// Wraps the encoded DTB data and provides access to the underlying bytes.
#[derive(Clone, Debug)]
pub struct FdtData(Vec);
impl FdtData {
- /// 获取数据长度(字节)
+ /// Returns the data length in bytes.
pub fn len(&self) -> usize {
self.0.len() * 4
}
- /// 数据是否为空
+ /// Returns true if the data is empty.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
@@ -43,7 +46,10 @@ impl AsRef<[u8]> for FdtData {
}
}
-/// FDT 编码器
+/// FDT encoder for serializing to DTB format.
+///
+/// This encoder walks the node tree and generates the binary DTB format
+/// according to the Device Tree Specification.
pub struct FdtEncoder<'a> {
fdt: &'a Fdt,
struct_data: Vec,
@@ -52,7 +58,7 @@ pub struct FdtEncoder<'a> {
}
impl<'a> FdtEncoder<'a> {
- /// 创建新的编码器
+ /// Creates a new encoder for the given FDT.
pub fn new(fdt: &'a Fdt) -> Self {
Self {
fdt,
@@ -62,7 +68,7 @@ impl<'a> FdtEncoder<'a> {
}
}
- /// 获取或添加字符串,返回偏移量
+ /// Gets or adds a string to the strings block, returning its offset.
fn get_or_add_string(&mut self, s: &str) -> u32 {
for (existing, offset) in &self.string_offsets {
if existing == s {
@@ -77,7 +83,7 @@ impl<'a> FdtEncoder<'a> {
offset
}
- /// 写入 BEGIN_NODE token 和节点名
+ /// Writes a BEGIN_NODE token and node name.
fn write_begin_node(&mut self, name: &str) {
let begin_token: u32 = Token::BeginNode.into();
self.struct_data.push(begin_token.to_be());
@@ -95,13 +101,13 @@ impl<'a> FdtEncoder<'a> {
}
}
- /// 写入 END_NODE token
+ /// Writes an END_NODE token.
fn write_end_node(&mut self) {
let end_token: u32 = Token::EndNode.into();
self.struct_data.push(end_token.to_be());
}
- /// 写入属性
+ /// Writes a property to the structure block.
fn write_property(&mut self, name: &str, data: &[u8]) {
let prop_token: u32 = Token::Prop.into();
self.struct_data.push(prop_token.to_be());
@@ -123,38 +129,38 @@ impl<'a> FdtEncoder<'a> {
}
}
- /// 执行编码
+ /// Performs the encoding and returns the binary DTB data.
pub fn encode(mut self) -> FdtData {
- // 递归编码节点树
+ // Recursively encode node tree
self.encode_node(&self.fdt.root.clone());
- // 添加 END token
+ // Add END token
let token: u32 = Token::End.into();
self.struct_data.push(token.to_be());
self.finalize()
}
- /// 递归编码节点及其子节点
+ /// Recursively encodes a node and its children.
fn encode_node(&mut self, node: &Node) {
- // 写入 BEGIN_NODE 和节点名
+ // Write BEGIN_NODE and node name
self.write_begin_node(node.name());
- // 写入所有属性(直接使用原始数据)
+ // Write all properties (using raw data directly)
for prop in node.properties() {
self.write_property(prop.name(), &prop.data);
}
- // 递归编码子节点
+ // Recursively encode child nodes
for child in node.children() {
self.encode_node(child);
}
- // 写入 END_NODE
+ // Write END_NODE
self.write_end_node();
}
- /// 生成最终 FDT 数据
+ /// Generates the final FDT binary data.
fn finalize(self) -> FdtData {
let memory_reservations = &self.fdt.memory_reservations;
let boot_cpuid_phys = self.fdt.boot_cpuid_phys;
diff --git a/fdt-edit/src/fdt.rs b/fdt-edit/src/fdt.rs
index cdb4528..5107372 100644
--- a/fdt-edit/src/fdt.rs
+++ b/fdt-edit/src/fdt.rs
@@ -1,3 +1,9 @@
+//! Editable Flattened Device Tree (FDT) structure.
+//!
+//! This module provides the main `Fdt` type for creating, modifying, and
+//! encoding device tree blobs. It supports loading from existing DTB files,
+//! building new trees programmatically, and applying device tree overlays.
+
use alloc::{
collections::BTreeMap,
format,
@@ -13,16 +19,21 @@ use crate::{
encode::{FdtData, FdtEncoder},
};
-/// 可编辑的 FDT
+/// An editable Flattened Device Tree (FDT).
+///
+/// This structure represents a mutable device tree that can be created from
+/// scratch, loaded from an existing DTB file, modified, and encoded back to
+/// the binary DTB format. It maintains a phandle cache for efficient node
+/// lookups by phandle value.
#[derive(Clone)]
pub struct Fdt {
- /// 引导 CPU ID
+ /// Boot CPU ID
pub boot_cpuid_phys: u32,
- /// 内存保留块
+ /// Memory reservation block entries
pub memory_reservations: Vec,
- /// 根节点
+ /// Root node of the device tree
pub root: Node,
- /// phandle 到节点完整路径的缓存
+ /// Cache mapping phandles to full node paths
phandle_cache: BTreeMap,
}
@@ -33,7 +44,7 @@ impl Default for Fdt {
}
impl Fdt {
- /// 创建新的空 FDT
+ /// Creates a new empty FDT.
pub fn new() -> Self {
Self {
boot_cpuid_phys: 0,
@@ -43,22 +54,24 @@ impl Fdt {
}
}
- /// 从原始 FDT 数据解析
+ /// Parses an FDT from raw byte data.
pub fn from_bytes(data: &[u8]) -> Result {
let raw_fdt = fdt_raw::Fdt::from_bytes(data)?;
Self::from_raw(&raw_fdt)
}
- /// 从原始指针解析
+ /// Parses an FDT from a raw pointer.
///
/// # Safety
- /// 调用者必须确保指针有效且指向有效的 FDT 数据
+ ///
+ /// The caller must ensure that the pointer is valid and points to a
+ /// valid FDT data structure.
pub unsafe fn from_ptr(ptr: *mut u8) -> Result {
let raw_fdt = unsafe { fdt_raw::Fdt::from_ptr(ptr)? };
Self::from_raw(&raw_fdt)
}
- /// 从 fdt_raw::Fdt 转换
+ /// Converts from a raw FDT parser instance.
fn from_raw(raw_fdt: &fdt_raw::Fdt) -> Result {
let header = raw_fdt.header();
@@ -69,23 +82,20 @@ impl Fdt {
phandle_cache: BTreeMap::new(),
};
- // 构建节点树
- // 使用栈来跟踪父节点,栈底是一个虚拟父节点
+ // Build node tree using a stack to track parent nodes
let mut node_stack: Vec = Vec::new();
for raw_node in raw_fdt.all_nodes() {
let level = raw_node.level();
let node = Node::from(&raw_node);
- // 弹出栈直到达到正确的父级别
- // level 0 = 根节点,应该直接放入空栈
- // level 1 = 根节点的子节点,栈中应该只有根节点
+ // Pop stack until we reach the correct parent level
while node_stack.len() > level {
let child = node_stack.pop().unwrap();
if let Some(parent) = node_stack.last_mut() {
parent.add_child(child);
} else {
- // 这是根节点
+ // This is the root node
fdt.root = child;
}
}
@@ -93,37 +103,37 @@ impl Fdt {
node_stack.push(node);
}
- // 弹出所有剩余节点
+ // Pop all remaining nodes
while let Some(child) = node_stack.pop() {
if let Some(parent) = node_stack.last_mut() {
parent.add_child(child);
} else {
- // 这是根节点
+ // This is the root node
fdt.root = child;
}
}
- // 构建 phandle 缓存
+ // Build phandle cache
fdt.rebuild_phandle_cache();
Ok(fdt)
}
- /// 重建 phandle 缓存
+ /// Rebuilds the phandle cache by scanning all nodes.
pub fn rebuild_phandle_cache(&mut self) {
self.phandle_cache.clear();
let root_clone = self.root.clone();
self.build_phandle_cache_recursive(&root_clone, "/");
}
- /// 递归构建 phandle 缓存
+ /// Recursively builds the phandle cache starting from a node.
fn build_phandle_cache_recursive(&mut self, node: &Node, current_path: &str) {
- // 检查节点是否有 phandle 属性
+ // Check if node has a phandle property
if let Some(phandle) = node.phandle() {
self.phandle_cache.insert(phandle, current_path.to_string());
}
- // 递归处理子节点
+ // Recursively process child nodes
for child in node.children() {
let child_name = child.name();
let child_path = if current_path == "/" {
@@ -135,28 +145,27 @@ impl Fdt {
}
}
- /// 规范化路径:如果是别名则解析为完整路径,否则确保以 / 开头
+ /// Normalizes a path: resolves aliases or ensures leading '/'.
fn normalize_path(&self, path: &str) -> Option {
if path.starts_with('/') {
Some(path.to_string())
} else {
- // 尝试解析别名
+ // Try to resolve as an alias
self.resolve_alias(path).map(|s| s.to_string())
}
}
- /// 解析别名,返回对应的完整路径
+ /// Resolves an alias to its full path.
///
- /// 从 /aliases 节点查找别名对应的路径
+ /// Looks up the alias in the /aliases node and returns the
+ /// corresponding path string.
pub fn resolve_alias(&self, alias: &str) -> Option<&str> {
let aliases_node = self.get_by_path("/aliases")?;
let prop = aliases_node.find_property(alias)?;
prop.as_str()
}
- /// 获取所有别名
- ///
- /// 返回 (别名, 路径) 的列表
+ /// Returns all aliases as (name, path) pairs.
pub fn aliases(&self) -> Vec<(String, String)> {
let mut result = Vec::new();
if let Some(aliases_node) = self.get_by_path("/aliases") {
@@ -169,39 +178,38 @@ impl Fdt {
result
}
- /// 根据 phandle 查找节点
- /// 返回 (节点引用, 完整路径)
+ /// Finds a node by its phandle value.
pub fn find_by_phandle(&self, phandle: Phandle) -> Option> {
let path = self.phandle_cache.get(&phandle)?.clone();
self.get_by_path(&path)
}
- /// 根据 phandle 查找节点(可变)
- /// 返回 (节点可变引用, 完整路径)
+ /// Finds a node by phandle (mutable reference).
pub fn find_by_phandle_mut(&mut self, phandle: Phandle) -> Option> {
let path = self.phandle_cache.get(&phandle)?.clone();
self.get_by_path_mut(&path)
}
- /// 获取根节点
+ /// Returns the root node.
pub fn root<'a>(&'a self) -> NodeRef<'a> {
self.get_by_path("/").unwrap()
}
- /// 获取根节点(可变)
+ /// Returns the root node (mutable reference).
pub fn root_mut<'a>(&'a mut self) -> NodeMut<'a> {
self.get_by_path_mut("/").unwrap()
}
- /// 应用设备树覆盖 (Device Tree Overlay)
+ /// Applies a device tree overlay to this FDT.
///
- /// 支持两种 overlay 格式:
- /// 1. fragment 格式:包含 fragment@N 节点,每个 fragment 有 target/target-path 和 __overlay__
- /// 2. 简单格式:直接包含 __overlay__ 节点
+ /// Supports two overlay formats:
+ /// 1. Fragment format: contains fragment@N nodes with target/target-path and __overlay__
+ /// 2. Simple format: directly contains __overlay__ node
+ ///
+ /// # Example
///
- /// # 示例
/// ```ignore
- /// // fragment 格式
+ /// // Fragment format
/// fragment@0 {
/// target-path = "/soc";
/// __overlay__ {
@@ -210,62 +218,61 @@ impl Fdt {
/// };
/// ```
pub fn apply_overlay(&mut self, overlay: &Fdt) -> Result<(), FdtError> {
- // 遍历 overlay 根节点的所有子节点
+ // Iterate through all children of overlay root node
for child in overlay.root.children() {
if child.name().starts_with("fragment@") || child.name() == "fragment" {
- // fragment 格式
+ // Fragment format
self.apply_fragment(child)?;
} else if child.name() == "__overlay__" {
- // 简单格式:直接应用到根节点
+ // Simple format: apply directly to root
self.merge_overlay_to_root(child)?;
} else if child.name() == "__symbols__"
|| child.name() == "__fixups__"
|| child.name() == "__local_fixups__"
{
- // 跳过这些特殊节点
+ // Skip these special nodes
continue;
}
}
- // 重建 phandle 缓存
+ // Rebuild phandle cache
self.rebuild_phandle_cache();
Ok(())
}
- /// 应用单个 fragment
+ /// Applies a single fragment from an overlay.
fn apply_fragment(&mut self, fragment: &Node) -> Result<(), FdtError> {
- // 获取目标路径
+ // Get target path
let target_path = self.resolve_fragment_target(fragment)?;
- // 找到 __overlay__ 子节点
+ // Find __overlay__ child node
let overlay_node = fragment
.get_child("__overlay__")
.ok_or(FdtError::NotFound)?;
- // 找到目标节点并应用覆盖
- // 需要克隆路径因为后面要修改 self
+ // Find target node and apply overlay
let target_path_owned = target_path.to_string();
- // 应用覆盖到目标节点
+ // Apply overlay to target node
self.apply_overlay_to_target(&target_path_owned, overlay_node)?;
Ok(())
}
- /// 解析 fragment 的目标路径
+ /// Resolves the target path of a fragment.
fn resolve_fragment_target(&self, fragment: &Node) -> Result {
- // 优先使用 target-path(字符串路径)
+ // Prefer target-path (string path)
if let Some(prop) = fragment.get_property("target-path") {
return Ok(prop.as_str().ok_or(FdtError::Utf8Parse)?.to_string());
}
- // 使用 target(phandle 引用)
+ // Use target (phandle reference)
if let Some(prop) = fragment.get_property("target") {
let ph = prop.get_u32().ok_or(FdtError::InvalidInput)?;
let ph = Phandle::from(ph);
- // 通过 phandle 找到节点,然后构建路径
+ // Find node by phandle and build path
if let Some(node) = self.find_by_phandle(ph) {
return Ok(node.path());
}
@@ -274,26 +281,26 @@ impl Fdt {
Err(FdtError::NotFound)
}
- /// 将 overlay 应用到目标节点
+ /// Applies an overlay to a target node.
fn apply_overlay_to_target(
&mut self,
target_path: &str,
overlay_node: &Node,
) -> Result<(), FdtError> {
- // 找到目标节点
+ // Find target node
let mut target = self
.get_by_path_mut(target_path)
.ok_or(FdtError::NotFound)?;
- // 合并 overlay 的属性和子节点
+ // Merge overlay properties and child nodes
Self::merge_nodes(target.node, overlay_node);
Ok(())
}
- /// 合并 overlay 节点到根节点
+ /// Merges an overlay node to the root node.
fn merge_overlay_to_root(&mut self, overlay: &Node) -> Result<(), FdtError> {
- // 合并属性和子节点到根节点
+ // Merge properties and child nodes to root
for prop in overlay.properties() {
self.root.set_property(prop.clone());
}
@@ -301,10 +308,10 @@ impl Fdt {
for child in overlay.children() {
let child_name = child.name();
if let Some(existing) = self.root.get_child_mut(child_name) {
- // 合并到现有子节点
+ // Merge into existing child node
Self::merge_nodes(existing, child);
} else {
- // 添加新子节点
+ // Add new child node
self.root.add_child(child.clone());
}
}
@@ -312,29 +319,30 @@ impl Fdt {
Ok(())
}
- /// 递归合并两个节点
+ /// Recursively merges two nodes.
fn merge_nodes(target: &mut Node, source: &Node) {
- // 合并属性(source 覆盖 target)
+ // Merge properties (source overrides target)
for prop in source.properties() {
target.set_property(prop.clone());
}
- // 合并子节点
+ // Merge child nodes
for source_child in source.children() {
let child_name = &source_child.name();
if let Some(target_child) = target.get_child_mut(child_name) {
- // 递归合并
+ // Recursive merge
Self::merge_nodes(target_child, source_child);
} else {
- // 添加新子节点
+ // Add new child node
target.add_child(source_child.clone());
}
}
}
- /// 删除节点(通过设置 status = "disabled" 或直接删除)
+ /// Applies an overlay with optional deletion of disabled nodes.
///
- /// 如果 overlay 中的节点有 status = "disabled",则禁用目标节点
+ /// If a node in the overlay has status = "disabled", the corresponding
+ /// target node will be disabled or deleted.
pub fn apply_overlay_with_delete(
&mut self,
overlay: &Fdt,
@@ -343,7 +351,7 @@ impl Fdt {
self.apply_overlay(overlay)?;
if delete_disabled {
- // 移除所有 status = "disabled" 的节点
+ // Remove all nodes with status = "disabled"
Self::remove_disabled_nodes(&mut self.root);
self.rebuild_phandle_cache();
}
@@ -351,9 +359,9 @@ impl Fdt {
Ok(())
}
- /// 递归移除 disabled 的节点
+ /// Recursively removes disabled nodes.
fn remove_disabled_nodes(node: &mut Node) {
- // 移除 disabled 的子节点
+ // Remove disabled child nodes
let mut to_remove = Vec::new();
for child in node.children() {
if matches!(child.status(), Some(Status::Disabled)) {
@@ -365,49 +373,49 @@ impl Fdt {
node.remove_child(&child_name);
}
- // 递归处理剩余子节点
+ // Recursively process remaining child nodes
for child in node.children_mut() {
Self::remove_disabled_nodes(child);
}
}
- /// 通过精确路径删除节点及其子树
- /// 只支持精确路径匹配,不支持模糊匹配
- /// 支持通过别名删除节点,并自动删除对应的别名条目
+ /// Removes a node by exact path.
+ ///
+ /// Supports exact path matching only. Aliases are automatically resolved.
///
- /// # 参数
- /// - `path`: 删除路径,格式如 "soc/gpio@1000" 或 "/soc/gpio@1000" 或别名
+ /// # Arguments
///
- /// # 返回值
- /// `Ok(Option)`: 如果找到并删除了节点,返回被删除的节点;如果路径不存在,返回 None
- /// `Err(FdtError)`: 如果路径格式无效
+ /// * `path` - Node path (e.g., "soc/gpio@1000", "/soc/gpio@1000", or an alias)
+ ///
+ /// # Returns
+ ///
+ /// * `Ok(Some(node))` - The removed node
+ /// * `Ok(None)` - Path not found
+ /// * `Err(FdtError)` - Invalid path format
+ ///
+ /// # Example
///
- /// # 示例
/// ```rust
/// # use fdt_edit::{Fdt, Node};
/// let mut fdt = Fdt::new();
///
- /// // 先添加节点再删除
+ /// // Add node then remove it
/// let mut soc = Node::new("soc");
/// soc.add_child(Node::new("gpio@1000"));
/// fdt.root.add_child(soc);
///
- /// // 精确删除节点(使用完整路径)
+ /// // Remove node with exact path
/// let removed = fdt.remove_node("/soc/gpio@1000")?;
/// assert!(removed.is_some());
- ///
- /// // 尝试删除不存在的节点会返回错误
- /// let not_found = fdt.remove_node("/soc/nonexistent");
- /// assert!(not_found.is_err());
/// # Ok::<(), fdt_raw::FdtError>(())
/// ```
pub fn remove_node(&mut self, path: &str) -> Result