1use std::io::Write;
7use std::path::PathBuf;
8use std::rc::Rc;
9use std::thread;
10use std::time::Duration;
11
12use color_eyre::eyre::{Context, Result, eyre};
13use color_eyre::owo_colors::OwoColorize;
14use indicatif::{ProgressBar, ProgressStyle};
15use log::{debug, error, info, warn};
16
17use crate::bmp::{self, BmpDevice};
18use crate::firmware_file::FirmwareFile;
19use crate::firmware_type::FirmwareType;
20use crate::usb::PortId;
21use crate::{AllowDangerous, BmpParams, FlashParams};
22
23pub struct Firmware
24{
25 firmware_type: FirmwareType,
26 firmware_file: FirmwareFile,
27}
28
29impl Firmware
30{
31 pub fn new<Params>(params: &Params, device: &BmpDevice, firmware_file: FirmwareFile) -> Result<Self>
32 where
33 Params: BmpParams + FlashParams,
34 {
35 Ok(Self {
36 firmware_type: Self::determine_firmware_type(params, device, &firmware_file)?,
37 firmware_file,
38 })
39 }
40
41 fn determine_firmware_type<Params>(
43 params: &Params,
44 device: &BmpDevice,
45 firmware_file: &FirmwareFile,
46 ) -> Result<FirmwareType>
47 where
48 Params: BmpParams + FlashParams,
49 {
50 let platform = device.platform();
53 let firmware_type =
54 FirmwareType::detect_from_firmware(platform, firmware_file).wrap_err("detecting firmware type")?;
55
56 debug!("Firmware file was detected as {}", firmware_type);
57
58 let firmware_type = match params.override_firmware_type() {
60 Some(override_firmware_type) => {
61 match params.allow_dangerous_options() {
62 AllowDangerous::Really => warn!(
63 "Overriding firmware-type detection and flashing to user-specified location ({}) instead!",
64 override_firmware_type
65 ),
66 AllowDangerous::Never => {
67 eprintln!(
68 "{} --override-firmware-type is used to override the firmware type detection and flash a \
69 firmware binary to a location other than the one that it seems to be designed for.\nThis \
70 is a potentially destructive operation and can result in an unbootable device! (can \
71 require a second, external JTAG debugger and manual wiring to fix!)\n\nDo not use this \
72 option unless you are a firmware developer and really know what you are doing!\n\nIf you \
73 are sure this is really what you want to do, run again with \
74 --allow-dangerous-options=really",
75 "WARNING:".red()
76 );
77 std::process::exit(1);
78 },
79 }
80 override_firmware_type
81 },
82 None => firmware_type,
83 };
84
85 Ok(firmware_type)
86 }
87
88 pub fn program_firmware(&self, device: &mut BmpDevice) -> Result<()>
89 {
90 let firmware_type = self.firmware_type;
92 let firmware_length = self.firmware_file.len() as u64;
94
95 let progress_bar = ProgressBar::new(firmware_length).with_style(
100 ProgressStyle::default_bar()
101 .template(" {percent:>3}% |{bar:50}| {bytes}/{total_bytes} [{binary_bytes_per_sec} {elapsed}]")
102 .unwrap(),
103 );
104 let progress_bar = Rc::new(progress_bar);
105 let enclosed = Rc::clone(&progress_bar);
106
107 let result = device.download(&self.firmware_file, firmware_type, move |flash_pos_delta| {
108 if enclosed.position() == 0 {
110 if firmware_type == FirmwareType::Application {
111 enclosed.println("Flashing...");
112 } else {
113 enclosed.println("Flashing bootloader...");
114 }
115 }
116 enclosed.inc(flash_pos_delta as u64);
117 });
118 progress_bar.finish();
119 let dfu_iface = result?;
120 info!("Flash complete!");
121
122 if progress_bar.position() == firmware_length {
123 device.reboot(dfu_iface)
124 } else {
125 Err(eyre!("Failed to flash device, download incomplete"))
126 }
127 }
128}
129
130fn check_programming(port: PortId) -> Result<()>
131{
132 let dev = bmp::wait_for_probe_reboot(port, Duration::from_secs(5), "flash").inspect_err(|_| {
133 error!("Black Magic Probe did not re-enumerate after flashing! Invalid firmware?");
134 })?;
135
136 let identity = dev.firmware_identity().inspect_err(|_| {
139 error!("Error reading firmware version after flash! Invalid firmware?");
140 })?;
141
142 println!(
143 "Black Magic Probe successfully rebooted into firmware version {}",
144 identity.version
145 );
146
147 Ok(())
148}
149
150pub fn flash_probe<Params>(params: &Params, mut device: BmpDevice, file_name: PathBuf) -> Result<()>
151where
152 Params: BmpParams + FlashParams,
153{
154 let firmware_file = FirmwareFile::from_path(&file_name)?;
155
156 let port = device.port();
158
159 let firmware = Firmware::new(params, &device, firmware_file)?;
160
161 let _ = writeln!(std::io::stdout(), "Found: {}", device).map_err(|e| {
165 error!(
166 "Failed to read string data from Black Magic Probe: {}\nTrying to continue anyway...",
167 e
168 );
169 });
170
171 firmware.program_firmware(&mut device)?;
172
173 drop(device);
176 thread::sleep(Duration::from_millis(250));
177
178 check_programming(port)
179}