avr-simulator 0.6.1

Oxidized interface for simavr
Documentation
use super::*;
use std::alloc;
use std::ffi::CString;
use std::path::Path;
use std::ptr::NonNull;

pub struct Firmware {
    ptr: NonNull<ffi::elf_firmware_t>,
}

impl Firmware {
    pub fn new() -> Self {
        // Safety: We know that `elf_firmware_t`'s layout has a non-zero size.
        //
        // (we also use `alloc_zeroed`, because that's what simavr's docs
        // suggest to do.)
        let ptr = unsafe {
            alloc::alloc_zeroed(alloc::Layout::new::<ffi::elf_firmware_t>())
                as *mut ffi::elf_firmware_t
        };

        // Unwrap-safety: This can fail only if the underlying allocator failed
        // to find enough memory to allocate the chunk - in that case panicking
        // is the best we can afford anyway
        let ptr = NonNull::new(ptr).unwrap();

        Self { ptr }
    }

    /// Load a firmware file, ELF or HEX format, from filename.
    /// Progname is the current program name for error messages
    /// included here as it mostly specific to HEX files.
    /// 
    /// MCU type and frequency is intended to be used when the
    /// hex file format is used because the hex format does not
    /// contain the CPU and MHz of the target. It is not an error
    /// to provide it when using elf file format, but the values
    /// will be overwritten by the parameters loaded from elf file.
    ///
    /// file_path - Firmware file
    /// load_base - Base of load region
    /// mmcu_type - MCU type (mmcu in elf_firmware_t) e.g. atmega2560
    /// frequency - MCU frequency in Hz, e.g. 16000000
    /// prog_name - For error messages
    pub fn load(
        mut self,
        file_path: impl AsRef<Path>,
        load_base: Option<u32>,
        mmcu_type: Option<&str>,
        frequency: Option<u32>,
        prog_name: Option<&str>,
    ) -> Self {
        let file_path = file_path.as_ref();
        let load_base = load_base.unwrap_or(0);
        let prog_name = match prog_name {
            Some(prog_name) => prog_name.to_string(),
            None => match file_path.file_name() {
                Some(file_name) => file_name.to_string_lossy().to_string(),
                None => "UNKNOWN".to_string(),
            },
        };
        let file_path = file_path.display().to_string();

        // Unwrap-safety: Paths cannot contain null-terminators, so a string
        // we've got from `.display().to_string()` cannot either
        let c_path = CString::new(file_path).unwrap();
        let c_name = CString::new(prog_name).unwrap();

        // Safety:
        // 1. self.ptr is NonNull<T>
        // 2. self is mut
        let firmware = unsafe { self.ptr.as_mut() };

        // Set the MCU frequency because it's not in the .hex file
        if let Some(f) = frequency {
            firmware.frequency = f;
        }

        // Set the MCU name because it's not in the .hex file
        if let Some(m) = mmcu_type {
            let mut mmcu = m.as_bytes().iter();
            for c in firmware.mmcu.iter_mut() {
                *c = (*mmcu.next().unwrap_or(&0_u8)) as i8;
            }
            if let Some(c) = firmware.mmcu.last_mut() {
                *c = '\0' as i8;
            }
        }

        // Safety: `self.ptr` points at a valid, zeroed instance of
        // `elf_firmware_t`; `c_path` points at a valid `CString`
        unsafe {
            // WARNING:
            //     This function does not return a result.
            //     Upon errors it calls `exit(1)` inside.
            //     And there is nothing we can do about it.
            //     (Beside running as a consumable process)
            ffi::sim_setup_firmware(
                c_path.as_ptr(), // Firmware file
                load_base,       // Base of load region
                firmware,        // Data returned here
                c_name.as_ptr(), // For error messages.
            );
        };

        self
    }

    pub fn flash_to(self, avr: &mut Avr) {
        avr.load_firmware(self.ptr);
    }
}

impl Drop for Firmware {
    fn drop(&mut self) {
        unsafe {
            alloc::dealloc(
                self.ptr.as_ptr() as *mut u8,
                alloc::Layout::new::<ffi::elf_firmware_t>(),
            );
        }
    }
}