# fdt-edit Project Context for LLMs

## Project Overview

fdt-edit is a high-level Rust library for creating, editing, and encoding Flattened Device Tree (FDT) structures. It is part of the fdt-parser workspace and provides comprehensive functionality for device tree manipulation with full `no_std` compatibility.

## Key Information

- **Project Name**: fdt-edit
- **Version**: 0.1.1
- **Language**: Rust (edition 2021)
- **License**: MIT OR Apache-2.0
- **Repository**: https://github.com/drivercraft/fdt-parser
- **Dependencies**: fdt-raw (low-level parsing), log, enum_dispatch
- **Target**: Embedded and no_std environments
- **Features**: Complete device tree parsing, editing, and encoding

## Architecture

### Core Components

1. **Fdt Structure** (`src/fdt.rs`) - Main device tree container
   - Creates empty device trees: `Fdt::new()`
   - Parses from raw DTB bytes: `Fdt::from_bytes(&dtb_data)`
   - Parses from raw pointer: `unsafe { Fdt::from_ptr(ptr) }`
   - Encodes back to DTB format: `fdt.encode()`
   - Manages node tree, memory reservations, and phandle cache
   - Provides Display trait for complete DTS output
   - Overlay application support
   - Alias resolution and phandle-based node lookup

2. **Node System** (`src/node/mod.rs`) - Hierarchical node structure
   - Core `Node` struct with property and child management
   - Node iteration and references (`NodeRef`, `NodeMut`, `NodeKind`)
   - Generic node operations and context handling
   - Formatted node display with configurable options
   - Specialized node types: memory, clock, pci, interrupt controller

3. **Property System** (`src/prop/mod.rs`) - Device tree properties
   - Type-safe property access (u32, u64, strings, byte arrays)
   - Property value encoding/decoding with proper endianness
   - Support for common property types (reg, ranges, compatible)

4. **Encoding System** (`src/encode.rs`) - DTB generation
   - `FdtEncoder` converts in-memory structures to DTB format
   - `FdtData` wrapper for encoded data with access methods

5. **Context Management** (`src/ctx.rs`) - Tree traversal context
   - Maintains current path and inheritance context
   - Manages `#address-cells`, `#size-cells` propagation
   - Tracks interrupt parent relationships

## Key Data Types and Enums

```rust
// Core types
pub struct Fdt { /* device tree container */ }
pub struct Node { /* tree node with properties and children */ }
pub struct Property { /* device tree property */ }

// Node references (immutable)
pub enum NodeRef<'a> {
    Generic(NodeRefGen<'a>),
    Pci(NodeRefPci<'a>),
    Clock(NodeRefClock<'a>),
    InterruptController(NodeRefInterruptController<'a>),
    Memory(NodeRefMemory<'a>),
}

// Node references (mutable)
pub enum NodeMut<'a> {
    Generic(NodeMutGen<'a>),
}

// Node kind for pattern matching
pub enum NodeKind<'a> {
    Generic(NodeRefGen<'a>),
    Pci(NodeRefPci<'a>),
    Clock(NodeRefClock<'a>),
    InterruptController(NodeRefInterruptController<'a>),
    Memory(NodeRefMemory<'a>),
}

// Supporting types
pub struct MemoryReservation { /* memory reservation entry */ }
pub struct Context<'a> { /* traversal context */ }
pub struct RegInfo { /* register information */ }
pub struct RangesEntry { /* address translation entry */ }
pub enum Status { /* node status (okay/disabled) */ }
```

## Complete API Reference

### Fdt Operations

#### Creation and Parsing
```rust
// Create empty device tree
let fdt = Fdt::new();

// Parse from DTB data
let fdt = Fdt::from_bytes(&dtb_data)?;

// Parse from pointer (unsafe)
let fdt = unsafe { Fdt::from_ptr(ptr)? };
```

#### Access and Modification
```rust
// Access root node
let root = fdt.root();
let mut root_mut = fdt.root_mut();

// Encode to DTB format
let fdt_data = fdt.encode();

// Display as DTS
println!("{}", fdt);

// Memory reservations
for reservation in &fdt.memory_reservations {
    println!("Reserved: 0x{:x}-0x{:x}", reservation.address, reservation.address + reservation.size);
}
```

#### Overlay Support
```rust
// Basic overlay application
fdt.apply_overlay(&overlay_fdt)?;

// Overlay with delete support
fdt.apply_overlay_with_delete(&overlay, Some(delete_fdt))?;
```

#### Alias Resolution and phandle Management
```rust
// Get all aliases
for (alias, target) in fdt.aliases() {
    println!("Alias: {} -> {}", alias, target);
}

// Resolve specific alias
if let Some(target) = fdt.resolve_alias("serial0") {
    let node = fdt.get_by_path(target);
}

// Find nodes by phandle
let node = fdt.find_by_phandle(phandle)?;
let mut node_mut = fdt.find_by_phandle_mut(phandle)?;

// Rebuild phandle cache
fdt.rebuild_phandle_cache();
```

#### Node Navigation and Search
```rust
// Path-based access
let node = fdt.get_by_path("/chosen");  // Exact path match
let mut node_mut = fdt.get_by_path_mut("/soc");  // Mutable access

// Pattern-based search
let virtio_nodes: Vec<_> = fdt.find_by_path("/virtio_mmio").collect();
let compatible_nodes = fdt.find_compatible(&["vendor,device"]);

// Global iteration
for node in fdt.all_nodes() { /* iterate all nodes */ }
for node in fdt.all_nodes_mut() { /* mutable iteration */ }
```

### Node Manipulation

#### Creation and Structure
```rust
// Create nodes
let mut node = Node::new("test-device@12340000");
let node = Node::new("soc");  // Creates empty node

// Tree structure
node.add_child(child_node);
let removed_child = node.remove_child("child-name");

// Node hierarchy
let child = node.get_child("child-name");
let mut child_mut = node.get_child_mut("child-name");
for child in node.children() { /* iterate children */ }
for child in node.children_mut() { /* mutable iteration */ }
```

#### Property Management
```rust
// Property access
let prop = node.get_property("compatible");
let mut prop_mut = node.get_property_mut("reg");
let removed_prop = node.remove_property("status");

// Node properties
node.set_property(Property::new("compatible", b"vendor,test\0"));
node.set_property(Property::new("reg", &[0x12340000u32, 0x1000u32]));
```

### Property Operations

#### Creation and Access
```rust
// Create properties
let prop = Property::new("reg", data);  // From raw bytes
let prop = Property::new("compatible", b"vendor,test\0");

// Type-safe access
let value = prop.get_u32()?;  // Get 32-bit value
let values = prop.get_u32_iter().collect::<Vec<_>>();  // Multiple u32 values
let value = prop.get_u64()?;  // Get 64-bit value
let string = prop.as_str()?;  // Get string
let strings = prop.as_str_iter().collect::<Vec<_>>();  // Multiple strings
let reader = prop.as_reader();  // Raw data access
```

#### Property Modification
```rust
// Set property values
prop.set_u32_ls(&[0x12345678]);  // Set multiple u32 values (little-endian)
prop.set_u64(0x123456789abcdef0);
prop.set_string("test-value");
prop.set_string_ls(&["value1", "value2"]);
```

### Specialized Node Operations

#### Memory Nodes
```rust
for node in fdt.all_nodes() {
    if let NodeKind::Memory(mem) = node.as_ref() {
        println!("Memory node: {}", mem.name());
        for region in mem.regions() {
            println!("  Region: 0x{:x}-0x{:x}", region.address, region.address + region.size);
        }
        if let Some(device_type) = mem.device_type() {
            println!("  Type: {}", device_type);
        }
    }
}
```

#### Clock Nodes
```rust
for node in fdt.all_nodes() {
    if let NodeKind::Clock(clock) = node.as_ref() {
        println!("Clock: {}", clock.name());
        println!("  #clock-cells: {}", clock.clock_cells);

        // Clock output names
        for (i, name) in clock.clock_output_names.iter().enumerate() {
            println!("  Output {}: {}", i, name);
        }

        // Clock references
        for clock_ref in clock.clocks() {
            println!("  Clock ref: phandle={}, cells={}", clock_ref.phandle, clock_ref.cells);
        }
    }
}
```

#### PCI Nodes
```rust
for node in fdt.all_nodes() {
    if let NodeKind::Pci(pci) = node.as_ref() {
        println!("PCI node: {}", pci.name());

        // Bus range
        if let Some(range) = pci.bus_range() {
            println!("  Bus range: {}-{}", range.start, range.end);
        }

        // Address ranges
        if let Some(ranges) = pci.ranges() {
            for range in ranges {
                println!("  Range: {:x?}", range);
            }
        }

        // Interrupt mapping
        if let Ok(interrupt_map) = pci.interrupt_map() {
            for entry in interrupt_map {
                println!("  Interrupt map: {:x?}", entry);
            }
        }
    }
}
```

#### Interrupt Controllers
```rust
for node in fdt.all_nodes() {
    if let NodeKind::InterruptController(ic) = node.as_ref() {
        println!("Interrupt controller: {}", ic.name());

        if let Some(cells) = ic.interrupt_cells() {
            println!("  #interrupt-cells: {}", cells);
        }

        if let Some(addr_cells) = ic.interrupt_address_cells() {
            println!("  #address-cells: {}", addr_cells);
        }

        println!("  Is controller: {}", ic.is_interrupt_controller());

        for compatible in ic.compatibles() {
            println!("  Compatible: {}", compatible);
        }
    }
}
```

### Register and Range Operations
```rust
// Reg property access
for node in fdt.all_nodes() {
    if let Some(regs) = node.regs() {
        for reg in regs {
            println!("Reg: addr=0x{:x}, child_addr=0x{:x}, size={:?}",
                     reg.address, reg.child_bus_address, reg.size);
        }
    }
}

// Ranges property (address translation)
for node in fdt.all_nodes() {
    if let Some(ranges) = node.ranges(node.address_cells().unwrap_or(2)) {
        for range in ranges {
            println!("Range: child={:x}-{:x}, parent={:x}-{:x}, size={:x}",
                     range.child_bus_address, range.child_bus_address + range.size,
                     range.parent_bus_address, range.parent_bus_address + range.size,
                     range.size);
        }
    }
}
```

### Display and Formatting
```rust
// FDT display (complete DTS)
println!("{}", fdt);

// Node display with options
let display = NodeDisplay::new(&node)
    .indent(4)
    .show_address(true)
    .show_size(true);
println!("{}", display);

// NodeRef display
let ref_display = NodeRefDisplay::new(&node_ref)
    .indent(2)
    .show_details(true);
println!("{}", ref_display);
```

## Testing Framework

### Test Organization
- `tests/edit.rs` - Round-trip parsing and encoding validation
- `tests/memory.rs` - Memory node functionality
- `tests/clock.rs` - Clock node detection and properties
- `tests/pci.rs` - PCI node operations
- `tests/irq.rs` - Interrupt controller handling
- `tests/range.rs` - Register and range property testing
- `tests/remove_node.rs` - Node removal operations
- `tests/display_debug.rs` - Display and debug formatting
- `tests/find2.rs` - Advanced search operations

### Test Data Sources
- QEMU device trees (ARM virtio)
- Raspberry Pi 4B device trees
- Phytium platform device trees
- Custom test device trees

### Test Validation Methods
- Round-trip compatibility (parse → modify → encode → compare)
- DTC tool comparison (`dtc -I dtb -O dts`)
- Property value validation
- Tree structure integrity checks

## Error Handling

The library uses `FdtError` for comprehensive error reporting:
- Parse errors from invalid DTB data
- Node path resolution failures
- Property access errors
- Encoding validation errors

## Development and Testing

### Build Commands
```bash
# Build library
cargo build -p fdt-edit

# Run all tests
cargo test -p fdt-edit

# Format code
cargo fmt -p fdt-edit

# Run clippy
cargo clippy -p fdt-edit
```

### Test Execution
```bash
# Run specific test
cargo test -p fdt-edit test_memory_node_detection

# Run tests with logging
RUST_LOG=debug cargo test -p fdt-edit

# Round-trip test validation
cargo test -p fdt-edit test_parse_and_rebuild
```

## Integration Examples

### Complete Device Tree Processing
```rust
use fdt_edit::*;

// Load and parse
let dtb_data = std::fs::read("device.dtb")?;
let mut fdt = Fdt::from_bytes(&dtb_data)?;

// Find and modify memory nodes
for node in fdt.all_nodes_mut() {
    if let NodeMut::Generic(mut node) = node {
        // Check if this is a memory node
        if let Some(device_type) = node.get_property("device_type") {
            if let Some("memory") = device_type.as_str() {
                // Modify memory properties
            }
        }
    }
}

// Add new node
let mut new_node = Node::new("test-device");
new_node.set_property(Property::new("compatible", b"vendor,test\0"));
new_node.set_property(Property::new("reg", &[0x12340000u32, 0x1000u32]));
fdt.root_mut().add_child(new_node);

// Apply overlay
let overlay_fdt = Fdt::from_bytes(&overlay_data)?;
fdt.apply_overlay(&overlay_fdt)?;

// Save modified device tree
let modified_dtb = fdt.encode();
std::fs::write("modified.dtb", modified_dtb.as_bytes())?;

// Export as DTS
std::fs::write("modified.dts", format!("{}", fdt));
```

## Limitations and Constraints

### Current Limitations
- Property editing APIs are partially implemented
- Some specialized node types may need additional features
- Large device trees may have memory constraints in `no_std` environments

### Platform Considerations
- Designed for both `std` and `no_std` environments
- Uses `alloc` crate for dynamic memory when available
- Endianness handling for cross-platform compatibility

## Future Development Roadmap

### Planned Features
- Complete property CRUD operations
- Enhanced node manipulation APIs
- Additional specialized node types
- Performance optimizations for large trees
- Streaming parsing support for very large device trees
- Validation and linting tools
