use crate::common::ram_command::{Command, RamCommand, Response};
use crate::common::serial_io::for_tool;
use crate::progress::{ProgressOperation, ProgressStatus};
use crate::{Error, Result, SifliToolTrait, WriteFlashFile};
use std::io::{BufReader, Read};
pub struct FlashWriter;
impl FlashWriter {
pub fn erase_all<T>(tool: &mut T, write_flash_files: &[WriteFlashFile]) -> Result<()>
where
T: SifliToolTrait + RamCommand,
{
tool.check_cancelled()?;
let progress = tool.progress();
let spinner = progress.create_spinner(ProgressOperation::EraseAllRegions);
let mut erase_address: Vec<u32> = Vec::new();
for f in write_flash_files.iter() {
tool.check_cancelled()?;
let address = f.address & 0xFF00_0000;
if erase_address.contains(&address) {
continue;
}
tool.command(Command::EraseAll { address: f.address })?;
erase_address.push(address);
}
spinner.finish(ProgressStatus::Success);
Ok(())
}
pub fn verify<T>(tool: &mut T, address: u32, len: u32, crc: u32) -> Result<()>
where
T: SifliToolTrait + RamCommand,
{
tool.check_cancelled()?;
let progress = tool.progress();
let spinner = progress.create_spinner(ProgressOperation::Verify { address, len });
let response = tool.command(Command::Verify { address, len, crc })?;
if response != Response::Ok {
return Err(Error::protocol(format!(
"verify failed for 0x{:08X}..0x{:08X}",
address,
address + len.saturating_sub(1)
)));
}
spinner.finish(ProgressStatus::Success);
Ok(())
}
pub fn write_file_incremental<T>(
tool: &mut T,
file: &WriteFlashFile,
verify: bool,
) -> Result<()>
where
T: SifliToolTrait + RamCommand,
{
tool.check_cancelled()?;
let progress = tool.progress();
let re_download_spinner = progress.create_spinner(ProgressOperation::CheckRedownload {
address: file.address,
size: file.file.metadata()?.len(),
});
let response = tool.command(Command::Verify {
address: file.address,
len: file.file.metadata()?.len() as u32,
crc: file.crc32,
})?;
if response == Response::Ok {
re_download_spinner.finish(ProgressStatus::Skipped);
return Ok(());
}
re_download_spinner.finish(ProgressStatus::Required);
let download_bar = progress.create_bar(
file.file.metadata()?.len(),
ProgressOperation::WriteFlash {
address: file.address,
size: file.file.metadata()?.len(),
},
);
let res = tool.command(Command::WriteAndErase {
address: file.address,
len: file.file.metadata()?.len() as u32,
})?;
if res != Response::RxWait {
return Err(Error::protocol(format!(
"write flash failed to start at 0x{:08X}",
file.address
)));
}
let mut buffer = vec![0u8; 128 * 1024];
let mut reader = BufReader::new(&file.file);
loop {
tool.check_cancelled()?;
let bytes_read = reader.read(&mut buffer)?;
if bytes_read == 0 {
break;
}
tool.check_cancelled()?;
let res = tool.send_data(&buffer[..bytes_read])?;
if res == Response::RxWait {
download_bar.inc(bytes_read as u64);
continue;
} else if res != Response::Ok {
return Err(Error::protocol(format!(
"write flash failed during transfer at 0x{:08X}",
file.address
)));
}
}
download_bar.finish(ProgressStatus::Success);
if verify {
tool.check_cancelled()?;
Self::verify(
tool,
file.address,
file.file.metadata()?.len() as u32,
file.crc32,
)?;
}
Ok(())
}
pub fn write_file_full_erase<T>(
tool: &mut T,
file: &WriteFlashFile,
verify: bool,
packet_size: usize,
) -> Result<()>
where
T: SifliToolTrait + RamCommand,
{
tool.check_cancelled()?;
let progress = tool.progress();
let download_bar = progress.create_bar(
file.file.metadata()?.len(),
ProgressOperation::WriteFlash {
address: file.address,
size: file.file.metadata()?.len(),
},
);
let mut buffer = vec![0u8; packet_size];
let mut reader = BufReader::new(&file.file);
let mut address = file.address;
loop {
tool.check_cancelled()?;
let bytes_read = reader.read(&mut buffer)?;
if bytes_read == 0 {
break;
}
tool.check_cancelled()?;
{
let mut io = for_tool(tool);
io.write_all(
Command::Write {
address,
len: bytes_read as u32,
}
.to_string()
.as_bytes(),
)?;
io.flush()?;
}
let res = tool.send_data(&buffer[..bytes_read])?;
if res != Response::Ok {
return Err(Error::protocol(format!(
"write flash failed during transfer at 0x{:08X}",
address
)));
}
address += bytes_read as u32;
download_bar.inc(bytes_read as u64);
}
download_bar.finish(ProgressStatus::Success);
if verify {
tool.check_cancelled()?;
Self::verify(
tool,
file.address,
file.file.metadata()?.len() as u32,
file.crc32,
)?;
}
Ok(())
}
}