#driver #driver-interface #os

no-std rdif-base

Driver Interface base definition

21 releases (6 breaking)

Uses new Rust 2024

0.7.0 Sep 23, 2025
0.6.0 Jun 24, 2025
0.5.0 May 30, 2025
0.4.5 May 29, 2025
0.1.4 Feb 19, 2025

#2511 in Embedded development

Download history 707/week @ 2025-08-16 821/week @ 2025-08-23 552/week @ 2025-08-30 919/week @ 2025-09-06 244/week @ 2025-09-13 942/week @ 2025-09-20 440/week @ 2025-09-27 256/week @ 2025-10-04 241/week @ 2025-10-11 189/week @ 2025-10-18 118/week @ 2025-10-25 696/week @ 2025-11-01 110/week @ 2025-11-08 222/week @ 2025-11-15 452/week @ 2025-11-22 508/week @ 2025-11-29

1,351 downloads per month
Used in 22 crates (11 directly)

MIT license

18KB
428 lines

RDrive - Rust Dynamic Driver Framework

A dynamic driver management framework for embedded systems written in Rust.

Architecture Overview

rdif-* Interface Crates

Hardware abstraction interface crates with Any for dynamic dispatch:

  • rdif-base: Core driver traits and error types
  • rdif-intc: Interrupt controller interface
  • rdif-clk: Clock management interface
  • rdif-serial: Serial communication interface
  • rdif-timer: Timer/counter interface
  • rdif-block: Block device interface
  • rdif-power: Power management interface
  • rdif-systick: System tick interface
  • rdif-net: Network interface

rdrive-macros

Procedural macros to simplify driver registration and module generation.

rdrive Core

The main driver container responsible for:

  • Driver registration and discovery
  • Device probing and initialization
  • Device lifecycle management
  • Type-safe device access

Device Ownership Model

Devices are managed through a long-term borrowing model using Device<T> wrappers. Unlike Mutex<T>, when a task borrows a device, it gains exclusive ownership. Other tasks attempting to borrow will receive an error with the owner's task ID, enabling forced task termination if needed.

Key features:

  • Lock-free operations once ownership is acquired
  • Weak pointer support for interrupt handlers
  • Cloning Device<T> creates weak references for fast indexing

Driver Registration

Drivers can be registered from different crates using the module_driver! macro:

use rdrive::{
    module_driver,
    register::{ProbeKind, ProbeLevel, ProbePriority, FdtInfo},
    probe::OnProbeError,
    PlatformDevice,
    DriverGeneric,
};

struct GicV3Driver {
    // Driver implementation
}

impl DriverGeneric for GicV3Driver {
    fn open(&mut self) -> Result<(), rdrive::KError> {
        // Initialize hardware
        Ok(())
    }

    fn close(&mut self) -> Result<(), rdrive::KError> {
        // Cleanup hardware
        Ok(())
    }
}

impl rdrive::driver::intc::Interface for GicV3Driver {
    // Implement interrupt controller interface
    fn enable_irq(&mut self, irq: rdrive::IrqId) -> Result<(), rdrive::driver::intc::IntcError> {
        // Enable interrupt implementation
        Ok(())
    }
    
    // ... other required methods
}

fn probe_gicv3(fdt: FdtInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeError> {
    let node = fdt.node;
    let mut reg = node.reg().ok_or("No reg property")?;
    
    let gicd_reg = reg.next().ok_or("Missing GICD register")?;
    let gicr_reg = reg.next().ok_or("Missing GICR register")?;
    
    let driver = GicV3Driver::new(
        gicd_reg.address as usize,
        gicr_reg.address as usize,
    );
    
    dev.register(driver);
    Ok(())
}

module_driver! {
    name: "GICv3",
    level: ProbeLevel::PreKernel,
    priority: ProbePriority::INTC,
    probe_kinds: &[ProbeKind::Fdt {
        compatibles: &["arm,gic-v3"],
        on_probe: probe_gicv3,
    }],
}

System Integration

1. Linker Script Configuration

Add the following section to your linker script:

.driver.register : ALIGN(4K) {
    _sdriver = .;
    *(.driver.register .driver.register.*)
    _edriver = .;
    . = ALIGN(4K);
}

2. Driver Registration Discovery

use rdrive::register::DriverRegisterSlice;

fn driver_registers() -> &'static [u8] {
    unsafe extern "C" {
        fn _sdriver();
        fn _edriver();
    }

    unsafe { 
        core::slice::from_raw_parts(
            _sdriver as *const u8, 
            _edriver as usize - _sdriver as usize
        ) 
    }
}

fn get_driver_registers() -> DriverRegisterSlice {
    DriverRegisterSlice::from_raw(driver_registers())
}

3. System Initialization

use rdrive::{Platform, probe_pre_kernel, probe_all};
use core::ptr::NonNull;

fn init_drivers() {
    // Initialize with device tree
    let fdt_addr = /* device tree address */;
    let platform = Platform::Fdt {
        addr: NonNull::new(fdt_addr).unwrap(),
    };

    // Initialize the driver framework
    rdrive::init(platform).unwrap();

    // Register all discovered drivers
    rdrive::register_append(get_driver_registers().as_slice());

    // Probe critical drivers first (interrupt controllers, etc.)
    rdrive::probe_pre_kernel().unwrap();

    // Initialize interrupt system
    // irq::init_main_cpu();

    // Probe remaining drivers
    rdrive::probe_all(false).unwrap(); // false = don't stop on failures
}

4. Device Access

use rdrive::{get_list, get_one, driver::Intc};

fn use_devices() {
    // Get all interrupt controllers
    let intc_list = get_list::<Intc>();
    for intc in intc_list {
        println!("Found INTC: {:?}", intc.descriptor());
    }

    // Get the first available interrupt controller
    if let Some(intc) = get_one::<Intc>() {
        // Use the device - this gives exclusive access
        match intc.try_lock() {
            Ok(mut locked_intc) => {
                // Safe to use locked_intc without additional synchronization
                locked_intc.enable_irq(42.into()).unwrap();
            }
            Err(owner_id) => {
                println!("Device busy, owned by task: {:?}", owner_id);
            }
        }
    }
}

Supported Probe Methods

  • Device Tree (FDT): Automatic device discovery from device tree
  • Static Configuration: Manual device registration (planned)

Examples

See the examples/ directory for complete usage examples:

  • examples/enumerate/: Basic driver enumeration and device tree parsing

References

For complete integration examples, see:

Dependencies

~165–580KB
~13K SLoC