use std::{
cmp,
error::Error,
fmt::Write,
fs,
sync::{Arc, Mutex},
thread,
time::{Duration, SystemTime},
};
use clap::Parser;
use indicatif::{ProgressBar, ProgressState, ProgressStyle};
use super::utils::{format_byte_per_sec, format_byte_unit};
#[derive(Parser, Clone, Debug)]
#[clap(about = "Write spi flash chip")]
pub struct CmdSpiFlashWrite {
#[clap(short, long, value_parser, action)]
erase: bool,
#[clap(short, long, value_parser, action)]
check: bool,
#[clap(long, value_parser, action)]
after_check: bool,
#[clap(value_parser)]
file: String,
}
pub fn cli_spi_flash_write(
flash_args: &super::CmdSpiFlash,
args: &CmdSpiFlashWrite,
) -> Result<(), Box<dyn Error>> {
let mut setp_count = 1;
let mut setp_cnt = 0;
if args.erase {
setp_count += 1;
}
if args.after_check {
setp_count += 1;
}
let mut file_buf = fs::read(args.file.as_str())?;
if args.file.to_lowercase().ends_with(".cap") && (file_buf.len() > 0x800) {
println!(
"{} Detect {} file, will be offset {} address write",
console::style("Note:").green(),
console::style("ASUS-CAP").green(),
console::style("0x800").green(),
);
file_buf = file_buf[0x800..file_buf.len()].to_vec();
}
let (device, chip_info) = flash_args.init()?;
let chip_capacity: usize = chip_info.capacity.into();
let wsize = cmp::min(file_buf.len(), chip_capacity);
if file_buf.len() > chip_capacity {
println!(
"{} File size is too large, the last {} will be lost",
console::style("Warn:").yellow(),
console::style(format_byte_unit(file_buf.len() - chip_capacity)).yellow(),
);
}
if args.erase {
setp_cnt += 1;
let spinner_style = ProgressStyle::with_template(
"{prefix} {spinner:.green} [{elapsed_precise}] {wide_msg}",
)
.unwrap();
let pb = ProgressBar::new(0);
pb.set_style(spinner_style.clone());
pb.set_prefix(format!(
"{} Erasing",
console::style(format!("[{}/{}]", setp_cnt, setp_count))
.bold()
.dim(),
));
let pb_finished = Arc::new(Mutex::new(pb));
let mux_pb_finished = Arc::clone(&pb_finished);
thread::spawn(move || loop {
let pb_finished = mux_pb_finished.lock().unwrap();
if (*pb_finished).is_finished() {
break;
}
(*pb_finished).tick();
thread::sleep(Duration::from_millis(40));
});
device.erase_full()?;
let pb_finished = pb_finished.lock().unwrap();
(*pb_finished).finish_and_clear();
let take_time = (*pb_finished).elapsed().as_millis();
let take_time = Duration::from_millis(take_time as u64);
println!(
"{} Erase done, Take_time: {}",
console::style(format!("[{}/{}]", setp_cnt, setp_count))
.bold()
.dim(),
humantime::format_duration(take_time)
);
}
let pb = ProgressBar::new(wsize as u64);
pb.set_style(ProgressStyle::with_template(
"{prefix} {spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({binary_bytes_per_sec}) ({eta})")
.unwrap()
.with_key("eta", |state: &ProgressState, w: &mut dyn Write| write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap())
.progress_chars("#>-"));
setp_cnt += 1;
pb.set_prefix(format!(
"{} {}",
console::style(format!("[{}/{}]", setp_cnt, setp_count))
.bold()
.dim(),
if args.check {
"Writing with Verifing"
} else {
"Writing only"
},
));
let start_time = SystemTime::now();
pb.tick();
let a = |e| -> bool {
match e {
ch347_rs::WriteEvent::Block(addr, count) => {
pb.inc(count as u64);
if !args.check {
return true;
}
if (addr + 0x100) % 4096 != 0 {
return true;
}
const BLOCK_SIZE: usize = 4096;
let block_addr = addr + 0x100 - BLOCK_SIZE;
let mut rbuf: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE];
device.read(block_addr as u32, &mut rbuf);
let mut is_verify_pass = true;
for x in 0..BLOCK_SIZE {
if rbuf[x] != file_buf[block_addr + x] {
pb.finish_and_clear();
println!(
"diff 0x{:04X}_{:04X} {:02X} => {:02X}",
(block_addr + x) >> 16,
(block_addr + x) & 0xFFFF,
file_buf[block_addr + x],
rbuf[x]
);
is_verify_pass = false;
}
}
return is_verify_pass;
}
ch347_rs::WriteEvent::Finish(_) => true,
}
};
device.write_with_callback(a, 0, &file_buf[0..wsize])?;
pb.finish_and_clear();
let take_time = start_time.elapsed().unwrap().as_millis();
let take_time = Duration::from_millis(take_time as u64);
let speed = (wsize as f64) / take_time.as_secs_f64();
let speed_str = format_byte_per_sec(speed);
println!(
"{} Write done, Take time: {} Speed: {}",
console::style(format!("[{}/{}]", setp_cnt, setp_count))
.bold()
.dim(),
humantime::format_duration(take_time),
speed_str,
);
if args.after_check {
setp_cnt += 1;
println!(
"{} Verify done, Take time: {}, Speed: {}",
console::style(format!("[{}/{}]", setp_cnt, setp_count))
.bold()
.dim(),
0,
0,
);
}
Ok(())
}