use crate::error::{Result, DebugError};
use std::path::Path;
use std::time::Instant;
use tracing::{debug, info, warn};
use probe_rs::{flashing::{self, FlashProgress}, Session, MemoryInterface};
#[derive(Debug, Clone)]
pub enum EraseType {
All,
Sectors { address: u64, size: usize },
}
#[derive(Debug, Clone)]
pub enum FileFormat {
Auto,
Elf,
Hex,
Bin,
}
#[derive(Debug)]
pub struct EraseResult {
pub erase_time_ms: u64,
pub sectors_erased: Option<usize>,
}
#[derive(Debug)]
pub struct ProgramResult {
pub bytes_programmed: usize,
pub programming_time_ms: u64,
pub verification_result: Option<bool>,
}
#[derive(Debug)]
pub struct VerifyResult {
pub success: bool,
pub bytes_verified: usize,
pub mismatches: Vec<VerifyMismatch>,
}
#[derive(Debug)]
pub struct VerifyMismatch {
pub address: u64,
pub expected: u8,
pub actual: u8,
}
pub struct FlashManager;
impl FlashManager {
pub fn new() -> Self {
Self
}
pub async fn erase_flash(
session: &mut Session,
erase_type: EraseType,
) -> Result<EraseResult> {
let start_time = Instant::now();
match erase_type {
EraseType::All => {
debug!("Starting full flash erase");
flashing::erase_all(session, FlashProgress::empty())
.map_err(|e| DebugError::FlashOperationFailed(format!("Full erase failed: {}", e)))?;
info!("Full flash erase completed");
Ok(EraseResult {
erase_time_ms: start_time.elapsed().as_millis() as u64,
sectors_erased: None,
})
}
EraseType::Sectors { address, size } => {
debug!("Starting sector erase at 0x{:08X}, size: {} bytes", address, size);
let sector_size = 4096; let sector_count = (size + sector_size - 1) / sector_size;
let mut core = session.core(0)
.map_err(|e| DebugError::FlashOperationFailed(format!("Failed to get core: {}", e)))?;
let erase_data = vec![0xFFu8; size];
core.write(address, &erase_data)
.map_err(|e| DebugError::FlashOperationFailed(format!("Sector erase failed: {}", e)))?;
info!("Sector erase completed: {} sectors", sector_count);
Ok(EraseResult {
erase_time_ms: start_time.elapsed().as_millis() as u64,
sectors_erased: Some(sector_count),
})
}
}
}
pub async fn program_file(
session: &mut Session,
file_path: &Path,
format: FileFormat,
base_address: Option<u64>,
) -> Result<ProgramResult> {
let start_time = Instant::now();
if !file_path.exists() {
return Err(DebugError::FlashOperationFailed(format!("File not found: {}", file_path.display())));
}
debug!("Programming file: {}", file_path.display());
let probe_format = match format {
FileFormat::Auto => {
match file_path.extension().and_then(|s| s.to_str()) {
Some("elf") => flashing::Format::Elf,
Some("hex") => flashing::Format::Hex,
Some("bin") => flashing::Format::Bin(probe_rs::flashing::BinOptions { base_address: None, skip: 0 }),
_ => return Err(DebugError::FlashOperationFailed("Cannot auto-detect file format".to_string())),
}
}
FileFormat::Elf => flashing::Format::Elf,
FileFormat::Hex => flashing::Format::Hex,
FileFormat::Bin => flashing::Format::Bin(probe_rs::flashing::BinOptions { base_address, skip: 0 }),
};
let mut options = flashing::DownloadOptions::default();
options.verify = true;
options.progress = None;
if matches!(probe_format, flashing::Format::Bin(_)) {
if let Some(addr) = base_address {
warn!("Base address specification for BIN files: 0x{:08X} - may require different API usage", addr);
}
}
flashing::download_file_with_options(session, file_path, probe_format, options)
.map_err(|e| DebugError::FlashOperationFailed(format!("Programming failed: {}", e)))?;
let elapsed = start_time.elapsed().as_millis() as u64;
info!("File programming completed in {}ms", elapsed);
let file_size = std::fs::metadata(file_path)
.map(|m| m.len() as usize)
.unwrap_or(0);
Ok(ProgramResult {
bytes_programmed: file_size,
programming_time_ms: elapsed,
verification_result: Some(true), })
}
pub async fn program_data(
session: &mut Session,
data: &[u8],
base_address: u64,
) -> Result<ProgramResult> {
let start_time = Instant::now();
debug!("Programming {} bytes to address 0x{:08X}", data.len(), base_address);
let mut core = session.core(0)
.map_err(|e| DebugError::FlashOperationFailed(format!("Failed to get core: {}", e)))?;
core.write(base_address, data)
.map_err(|e| DebugError::FlashOperationFailed(format!("Failed to write data: {}", e)))?;
let elapsed = start_time.elapsed().as_millis() as u64;
info!("Data programming completed: {} bytes in {}ms", data.len(), elapsed);
Ok(ProgramResult {
bytes_programmed: data.len(),
programming_time_ms: elapsed,
verification_result: None, })
}
pub async fn verify_flash(
session: &mut Session,
expected_data: &[u8],
address: u64,
) -> Result<VerifyResult> {
debug!("Verifying {} bytes at address 0x{:08X}", expected_data.len(), address);
let mut core = session.core(0)
.map_err(|e| DebugError::FlashOperationFailed(format!("Failed to get core: {}", e)))?;
let mut actual_data = vec![0u8; expected_data.len()];
core.read(address, &mut actual_data)
.map_err(|e| DebugError::FlashOperationFailed(format!("Failed to read flash: {}", e)))?;
let mut mismatches = Vec::new();
for (i, (expected, actual)) in expected_data.iter().zip(actual_data.iter()).enumerate() {
if expected != actual {
mismatches.push(VerifyMismatch {
address: address + i as u64,
expected: *expected,
actual: *actual,
});
}
}
let success = mismatches.is_empty();
if success {
info!("Flash verification successful: {} bytes", expected_data.len());
} else {
warn!("Flash verification failed: {} mismatches", mismatches.len());
}
Ok(VerifyResult {
success,
bytes_verified: expected_data.len(),
mismatches,
})
}
}
impl Default for FlashManager {
fn default() -> Self {
Self::new()
}
}