quick_flash/
lib.rs

1use anyhow::{self, Context};
2use etcetera::{self, AppStrategy, AppStrategyArgs};
3use probe_rs::{
4    flashing::{download_file_with_options, DownloadOptions, FlashProgress, Format, ProgressEvent},
5    probe::{list::Lister, DebugProbeInfo, Probe},
6    Permissions,
7};
8use std::{fs, path::PathBuf};
9use storage::Firmware;
10
11pub mod credentials;
12pub mod credentials_manager;
13pub mod storage;
14mod utils;
15
16pub struct BaseDirs {
17    pub creds_dir: PathBuf,
18    pub firmware_cache_dir: PathBuf,
19}
20
21impl BaseDirs {
22    pub fn new() -> anyhow::Result<Self> {
23        let strategy = etcetera::choose_app_strategy(AppStrategyArgs {
24            top_level_domain: "cz".to_string(),
25            author: "manakjiri".to_string(),
26            app_name: "quick-flash".to_string(),
27        })
28        .context("Failed to resolve application directories")?;
29
30        let creds_dir = strategy.config_dir().join("credentials");
31        let firmware_cache_dir = strategy.cache_dir().join("firmware");
32
33        fs::create_dir_all(&creds_dir).context("Failed to create config directory")?;
34        fs::create_dir_all(&firmware_cache_dir)
35            .context("Failed to create firmware cache directory")?;
36
37        Ok(BaseDirs {
38            creds_dir,
39            firmware_cache_dir,
40        })
41    }
42
43    pub fn clear_firmware_cache(&self) -> anyhow::Result<()> {
44        fs::remove_dir_all(&self.firmware_cache_dir).context("Failed to clear cache directory")?;
45        fs::create_dir_all(&self.firmware_cache_dir)
46            .context("Failed to create firmware cache directory")?;
47        Ok(())
48    }
49}
50
51pub fn get_probes() -> anyhow::Result<Vec<DebugProbeInfo>> {
52    let lister = Lister::new();
53    let probes = lister.list_all();
54    if probes.is_empty() {
55        anyhow::bail!("No debug probes found")
56    }
57    Ok(probes)
58}
59
60pub fn flash_firmware(
61    probe: Probe,
62    firmware: Firmware,
63    connect_under_reset: bool,
64    progress_callback: &'static dyn Fn(String),
65) -> anyhow::Result<()> {
66    // Attach to a chip.
67    progress_callback("Attaching to target...".to_string());
68    let mut session = match connect_under_reset {
69        true => probe.attach_under_reset(&firmware.chip, Permissions::default()),
70        false => probe.attach(&firmware.chip, Permissions::default()),
71    }
72    .context("Failed to attach probe")?;
73
74    // Download the firmware binary.
75    progress_callback(format!(
76        "Downloading {}/{} to target chip {}...",
77        firmware.name, firmware.version, firmware.chip
78    ));
79    let mut options = DownloadOptions::default();
80    options.progress = Some(FlashProgress::new(|e| match e {
81        ProgressEvent::StartedErasing => progress_callback("Flash erasing...".to_string()),
82        ProgressEvent::FinishedErasing => progress_callback("Flash programming...".to_string()),
83        _ => {}
84    }));
85    options.verify = true;
86    options.do_chip_erase = true;
87    download_file_with_options(&mut session, firmware.path, Format::Elf, options)
88        .context("Failed to flash firmware")?;
89
90    progress_callback("Resetting target...".to_string());
91    session.core(0)?.reset()?;
92
93    Ok(())
94}