Rust设备树解析库fdt的使用,高效处理Flattened Device Tree数据结构

Rust设备树解析库fdt的使用,高效处理Flattened Device Tree数据结构

安装

在项目目录中运行以下Cargo命令:

cargo add fdt

或者在Cargo.toml中添加以下行:

fdt = "0.1.5"

基本使用示例

下面是一个使用fdt库解析设备树的基本示例:

use fdt::Fdt;

fn main() {
    // 假设我们有一个设备树二进制数据
    let dtb_data = include_bytes!("path/to/device_tree.dtb");
    
    // 解析设备树
    let fdt = Fdt::new(dtb_data).expect("Failed to parse device tree");
    
    // 获取根节点
    let root = fdt.root();
    
    // 遍历所有子节点
    for node in root.children() {
        println!("Found node: {}", node.name);
        
        // 遍历节点属性
        for prop in node.properties() {
            println!("  Property: {} ({} bytes)", prop.name, prop.value.len());
        }
    }
    
    // 查找特定节点
    if let Some(uart_node) = fdt.find_node("/soc/uart@10000000") {
        println!("Found UART node");
        
        // 获取特定属性值
        if let Some(reg) = uart_node.property("reg") {
            println!("UART registers at {:?}", reg.value);
        }
    }
}

完整示例代码

下面是一个更完整的示例,展示了如何使用fdt库处理设备树数据:

use fdt::Fdt;

fn main() {
    // 加载设备树二进制数据
    let dtb_data = include_bytes!("path/to/device_tree.dtb");
    
    // 解析设备树
    match Fdt::new(dtb_data) {
        Ok(fdt) => {
            // 打印设备树基本信息
            println!("Device tree version: {}", fdt.version());
            println!("Total size: {} bytes", fdt.total_size());
            
            // 处理内存节点
            if let Some(memory_node) = fdt.find_node("/memory") {
                println!("\nMemory information:");
                
                // 获取内存区域
                if let Some(reg) = memory_node.property("reg") {
                    let regions = reg.as_reg().unwrap();
                    for (i, region) in regions.enumerate() {
                        println!("  Region {}: start={:#x}, size={:#x}",
                            i, region.starting_address, region.size.unwrap_or(0));
                    }
                }
            }
            
            // 处理CPU节点
            println!("\nCPU information:");
            for cpu_node in fdt.find_all_nodes("/cpus/cpu@*") {
                println!("Found CPU: {}", cpu_node.name);
                
                // 获取CPU兼容性信息
                if let Some(compatible) = cpu_node.property("compatible") {
                    println!("  Compatible: {}", compatible.as_str().unwrap());
                }
                
                // 获取CPU时钟频率
                if let Some(clock_freq) = cpu_node.property("clock-frequency") {
                    let freq = clock_freq.as_u32().unwrap();
                    println!("  Clock frequency: {} MHz", freq / 1_000_000);
                }
            }
            
            // 处理中断控制器
            if let Some(intc_node) = fdt.find_node("/intc") {
                println!("\nInterrupt controller:");
                println!("  Compatible: {}", 
                    intc_node.property("compatible").unwrap().as_str().unwrap());
                
                // 获取中断号
                if let Some(interrupts) = intc_node.property("interrupts") {
                    println!("  Interrupts: {:?}", interrupts.value);
                }
            }
        },
        Err(e) => eprintln!("Failed to parse device tree: {}", e),
    }
}

高级用法

use fdt::{Fdt, standard_nodes::Compatible};

fn enumerate_devices(fdt: &Fdt) {
    // 查找所有兼容特定驱动的设备
    for node in fdt.find_all_nodes("/soc/*") {
        if let Some(compatible) = node.property("compatible") {
            if compatible.as_str().unwrap().contains("usb") {
                println!("Found USB device at {}", node.name);
                
                // 获取设备地址
                if let Some(reg) = node.property("reg") {
                    let regions = reg.as_reg().unwrap();
                    for region in regions {
                        println!("  Base address: {:#x}", region.starting_address);
                    }
                }
                
                // 获取中断信息
                if let Some(interrupts) = node.property("interrupts") {
                    println!("  Interrupts: {:?}", interrupts.value);
                }
            }
        }
    }
}

fn check_compatibility(fdt: &Fdt) -> bool {
    // 检查设备树是否与我们的系统兼容
    let root = fdt.root();
    
    // 检查兼容性字符串
    if let Some(compatible) = root.property("compatible") {
        let comp = Compatible::new(compatible);
        if comp.matches("my-company,my-board") {
            return true;
        }
    }
    
    false
}

完整示例demo

下面是一个综合性的设备树解析示例,展示了fdt库的核心功能:

use fdt::{Fdt, standard_nodes::Compatible};

fn main() {
    // 加载设备树数据
    let dtb_data = include_bytes!("path/to/device_tree.dtb");
    
    // 解析设备树
    let fdt = match Fdt::new(dtb_data) {
        Ok(fdt) => fdt,
        Err(e) => {
            eprintln!("设备树解析失败: {}", e);
            return;
        }
    };
    
    // 1. 打印设备树基本信息
    println!("=== 设备树基本信息 ===");
    println!("版本: {}", fdt.version());
    println!("总大小: {} 字节", fdt.total_size());
    
    // 2. 检查兼容性
    println!("\n=== 兼容性检查 ===");
    if check_compatibility(&fdt) {
        println!("设备树与系统兼容");
    } else {
        println!("设备树与系统不兼容");
    }
    
    // 3. 枚举所有设备
    println!("\n=== 设备枚举 ===");
    enumerate_devices(&fdt);
    
    // 4. 详细内存信息
    println!("\n=== 内存信息 ===");
    if let Some(memory_node) = fdt.find_node("/memory") {
        if let Some(reg) = memory_node.property("reg") {
            let regions = reg.as_reg().unwrap();
            for (i, region) in regions.enumerate() {
                println!("内存区域 {}: 起始地址={:#x}, 大小={:#x}",
                    i, region.starting_address, region.size.unwrap_or(0));
            }
        }
    }
}

// 检查设备树兼容性
fn check_compatibility(fdt: &Fdt) -> bool {
    let root = fdt.root();
    if let Some(compatible) = root.property("compatible") {
        let comp = Compatible::new(compatible);
        comp.matches("my-company,my-board")
    } else {
        false
    }
}

// 枚举所有设备
fn enumerate_devices(fdt: &Fdt) {
    // 遍历所有节点
    for node in fdt.all_nodes() {
        println!("设备节点: {}", node.name);
        
        // 打印所有属性
        for prop in node.properties() {
            println!("  属性: {} ({}字节)", prop.name, prop.value.len());
        }
        
        // 特殊处理有reg属性的设备
        if let Some(reg) = node.property("reg") {
            if let Ok(regions) = reg.as_reg() {
                for (i, region) in regions.enumerate() {
                    println!("  寄存器区域 {}: 地址={:#x}, 大小={:#x}",
                        i, region.starting_address, region.size.unwrap_or(0));
                }
            }
        }
        
        // 特殊处理中断属性
        if let Some(interrupts) = node.property("interrupts") {
            println!("  中断信息: {:?}", interrupts.value);
        }
    }
}

注意事项

  1. fdt库支持no_std环境,适合嵌入式开发
  2. 设备树二进制数据必须正确对齐
  3. 解析时要注意处理可能的错误情况
  4. 属性值的解释需要根据设备树绑定规范进行

这个库提供了高效的方式来解析和处理Flattened Device Tree数据结构,特别适合嵌入式系统开发和裸机编程场景。


1 回复

Rust设备树解析库fdt的使用指南

概述

fdt是一个用于解析和处理Flattened Device Tree (FDT)数据的Rust库。设备树是嵌入式系统中常用的硬件描述机制,用于描述系统硬件配置而不需要硬编码。这个库提供了高效、安全的方式来访问和操作设备树数据。

安装

在Cargo.toml中添加依赖:

[dependencies]
fdt = "0.3"

基本使用方法

1. 加载设备树

use fdt::Fdt;

// 从字节切片加载设备树
let dtb = include_bytes!("path/to/your.dtb");
let fdt = Fdt::new(dtb).expect("Failed to parse device tree");

2. 遍历节点

// 获取根节点
let root = fdt.root();

// 遍历所有子节点
for node in root.children() {
    println!("Node: {}", node.name);
    
    // 遍历节点的属性
    for prop in node.properties() {
        println!("  Property: {} = {:?}", prop.name, prop.value);
    }
}

3. 查找特定节点

// 通过路径查找节点
if let Some(uart_node) = fdt.find_node("/soc/serial@40001000") {
    println!("Found UART node: {}", uart_node.name);
    
    // 获取特定属性
    if let Some(reg) = uart_node.property("reg") {
        println!("Register info: {:?}", reg.value);
    }
}

高级功能

1. 处理地址信息

use fdt::standard_nodes::MemoryRegion;

// 获取内存信息
for mem in fdt.memory().regions() {
    println!("Memory region: {:#x} - {:#x}", mem.starting_address, mem.size);
}

2. 处理中断信息

if let Some(intc) = fdt.find_node("/intc") {
    if let Some(interrupts) = intc.property("interrupts") {
        println!("Interrupt info: {:?}", interrupts.as_cells());
    }
}

3. 处理兼容性信息

// 查找所有兼容特定驱动的设备
for node in fdt.all_nodes() {
    if let Some(compatible) = node.property("compatible") {
        if compatible.as_str().contains("arm,pl011") {
            println!("Found PL011 UART at {}", node.name);
        }
    }
}

实用示例

获取CPU信息

for cpu in fdt.cpus() {
    println!("CPU {}: {}", cpu.id, cpu.name);
    if let Some(clock_freq) = cpu.property("clock-frequency").and_then(|p| p.as_u32()) {
        println!("  Clock frequency: {} Hz", clock_freq);
    }
}

解析设备树到自定义结构

struct DeviceInfo {
    name: String,
    address: u64,
    size: u64,
    interrupts: Vec<u32>,
}

fn parse_device(fdt: &Fdt, path: &str) -> Option<DeviceInfo> {
    let node = fdt.find_node(path)?;
    
    let reg = node.reg()?.next()?;
    
    let mut interrupts = Vec::new();
    if let Some(interrupts_prop) = node.property("interrupts") {
        interrupts.extend(interrupts_prop.as_cells().iter().copied());
    }
    
    Some(DeviceInfo {
        name: node.name.to_string(),
        address: reg.starting_address,
        size: reg.size.unwrap_or(0),
        interrupts,
    })
}

完整示例代码

use fdt::{Fdt, standard_nodes::MemoryRegion};

fn main() {
    // 1. 加载设备树
    let dtb = include_bytes!("path/to/your.dtb");
    let fdt = Fdt::new(dtb).expect("Failed to parse device tree");

    // 2. 遍历所有节点
    println!("=== 遍历所有节点 ===");
    let root = fdt.root();
    for node in root.children() {
        println!("节点: {}", node.name);
        
        for prop in node.properties() {
            println!("  属性: {} = {:?}", prop.name, prop.value);
        }
    }

    // 3. 查找特定节点
    println!("\n=== 查找UART节点 ===");
    if let Some(uart_node) = fdt.find_node("/soc/serial@40001000") {
        println!("找到UART节点: {}", uart_node.name);
        
        if let Some(reg) = uart_node.property("reg") {
            println!("寄存器信息: {:?}", reg.value);
        }
    }

    // 4. 处理内存信息
    println!("\n=== 内存区域 ===");
    for mem in fdt.memory().regions() {
        println!("内存区域: {:#x} - {:#x}", mem.starting_address, mem.size);
    }

    // 5. 获取CPU信息
    println!("\n=== CPU信息 ===");
    for cpu in fdt.cpus() {
        println!("CPU {}: {}", cpu.id, cpu.name);
        if let Some(clock_freq) = cpu.property("clock-frequency").and_then(|p| p.as_u32()) {
            println!("  时钟频率: {} Hz", clock_freq);
        }
    }

    // 6. 解析到自定义结构
    println!("\n=== 自定义设备解析 ===");
    if let Some(device) = parse_device(&fdt, "/soc/serial@40001000") {
        println!("设备信息:");
        println!("  名称: {}", device.name);
        println!("  地址: {:#x}", device.address);
        println!("  大小: {:#x}", device.size);
        println!("  中断: {:?}", device.interrupts);
    }
}

// 自定义设备信息结构
struct DeviceInfo {
    name: String,
    address: u64,
    size: u64,
    interrupts: Vec<u32>,
}

// 解析设备到自定义结构
fn parse_device(fdt: &Fdt, path: &str) -> Option<DeviceInfo> {
    let node = fdt.find_node(path)?;
    
    let reg = node.reg()?.next()?;
    
    let mut interrupts = Vec::new();
    if let Some(interrupts_prop) = node.property("interrupts") {
        interrupts.extend(interrupts_prop.as_cells().iter().copied());
    }
    
    Some(DeviceInfo {
        name: node.name.to_string(),
        address: reg.starting_address,
        size: reg.size.unwrap_or(0),
        interrupts,
    })
}

注意事项

  1. 设备树数据通常由系统提供,确保你有正确的设备树二进制(dtb)文件
  2. 处理属性时要考虑字节序问题,设备树通常使用大端序
  3. 某些属性可能有特定格式,需要参考设备树规范进行解析

fdt库提供了安全、符合Rust习惯的方式来处理设备树数据,避免了直接操作原始指针带来的风险,同时保持了高性能。

回到顶部