sftool_lib/common/
write_flash.rs

1use crate::common::ram_command::{Command, RamCommand, Response};
2use crate::{Error, Result, SifliToolTrait, WriteFlashFile};
3use std::io::{BufReader, Read, Write};
4
5/// 通用的Flash写入操作实现
6pub struct FlashWriter;
7
8impl FlashWriter {
9    /// 擦除所有Flash区域
10    pub fn erase_all<T>(tool: &mut T, write_flash_files: &[WriteFlashFile]) -> Result<()>
11    where
12        T: SifliToolTrait + RamCommand,
13    {
14        let progress = tool.progress();
15        let spinner = progress.create_spinner("Erasing all flash regions...");
16
17        let mut erase_address: Vec<u32> = Vec::new();
18        for f in write_flash_files.iter() {
19            let address = f.address & 0xFF00_0000;
20            // 如果ERASE_ADDRESS中的地址已经被擦除过,则跳过
21            if erase_address.contains(&address) {
22                continue;
23            }
24            tool.command(Command::EraseAll { address: f.address })?;
25            erase_address.push(address);
26        }
27
28        spinner.finish_with_message("All flash regions erased");
29        Ok(())
30    }
31
32    /// 验证数据
33    pub fn verify<T>(tool: &mut T, address: u32, len: u32, crc: u32) -> Result<()>
34    where
35        T: SifliToolTrait + RamCommand,
36    {
37        let progress = tool.progress();
38        let spinner = progress.create_spinner("Verifying data...");
39
40        let response = tool.command(Command::Verify { address, len, crc })?;
41        if response != Response::Ok {
42            return Err(Error::protocol(format!(
43                "verify failed for 0x{:08X}..0x{:08X}",
44                address,
45                address + len.saturating_sub(1)
46            )));
47        }
48
49        spinner.finish_with_message("Verify success!");
50        Ok(())
51    }
52
53    /// 写入单个文件到Flash(非全擦除模式)
54    pub fn write_file_incremental<T>(
55        tool: &mut T,
56        file: &WriteFlashFile,
57        verify: bool,
58    ) -> Result<()>
59    where
60        T: SifliToolTrait + RamCommand,
61    {
62        let progress = tool.progress();
63        let re_download_spinner = progress.create_spinner(format!(
64            "Checking whether a re-download is necessary at address 0x{:08X}...",
65            file.address
66        ));
67
68        let response = tool.command(Command::Verify {
69            address: file.address,
70            len: file.file.metadata()?.len() as u32,
71            crc: file.crc32,
72        })?;
73
74        if response == Response::Ok {
75            re_download_spinner.finish_with_message("No need to re-download, skip!");
76            return Ok(());
77        }
78
79        re_download_spinner.finish_with_message("Need to re-download");
80
81        let download_bar = progress.create_bar(
82            file.file.metadata()?.len(),
83            format!("Download at 0x{:08X}...", file.address),
84        );
85
86        let res = tool.command(Command::WriteAndErase {
87            address: file.address,
88            len: file.file.metadata()?.len() as u32,
89        })?;
90        if res != Response::RxWait {
91            return Err(Error::protocol(format!(
92                "write flash failed to start at 0x{:08X}",
93                file.address
94            )));
95        }
96
97        let mut buffer = vec![0u8; 128 * 1024];
98        let mut reader = BufReader::new(&file.file);
99
100        loop {
101            let bytes_read = reader.read(&mut buffer)?;
102            if bytes_read == 0 {
103                break;
104            }
105            let res = tool.send_data(&buffer[..bytes_read])?;
106            if res == Response::RxWait {
107                download_bar.inc(bytes_read as u64);
108                continue;
109            } else if res != Response::Ok {
110                return Err(Error::protocol(format!(
111                    "write flash failed during transfer at 0x{:08X}",
112                    file.address
113                )));
114            }
115        }
116
117        let end_address = file.address + file.file.metadata()?.len() as u32 - 1;
118        download_bar.finish_with_message(format!(
119            "Downloaded successfully for 0x{:08X}..0x{:08X}",
120            file.address, end_address
121        ));
122
123        // verify
124        if verify {
125            Self::verify(
126                tool,
127                file.address,
128                file.file.metadata()?.len() as u32,
129                file.crc32,
130            )?;
131        }
132
133        Ok(())
134    }
135
136    /// 写入单个文件到Flash(全擦除模式)
137    pub fn write_file_full_erase<T>(
138        tool: &mut T,
139        file: &WriteFlashFile,
140        verify: bool,
141        packet_size: usize,
142    ) -> Result<()>
143    where
144        T: SifliToolTrait + RamCommand,
145    {
146        let progress = tool.progress();
147        let download_bar = progress.create_bar(
148            file.file.metadata()?.len(),
149            format!("Download at 0x{:08X}...", file.address),
150        );
151
152        let mut buffer = vec![0u8; packet_size];
153        let mut reader = BufReader::new(&file.file);
154
155        let mut address = file.address;
156        loop {
157            let bytes_read = reader.read(&mut buffer)?;
158            if bytes_read == 0 {
159                break;
160            }
161            tool.port().write_all(
162                Command::Write {
163                    address,
164                    len: bytes_read as u32,
165                }
166                .to_string()
167                .as_bytes(),
168            )?;
169            tool.port().flush()?;
170            let res = tool.send_data(&buffer[..bytes_read])?;
171            if res != Response::Ok {
172                return Err(Error::protocol(format!(
173                    "write flash failed during transfer at 0x{:08X}",
174                    address
175                )));
176            }
177            address += bytes_read as u32;
178            download_bar.inc(bytes_read as u64);
179        }
180
181        let end_address = file.address + file.file.metadata()?.len() as u32 - 1;
182        download_bar.finish_with_message(format!(
183            "Downloaded successfully for 0x{:08X}..0x{:08X}",
184            file.address, end_address
185        ));
186
187        // verify
188        if verify {
189            Self::verify(
190                tool,
191                file.address,
192                file.file.metadata()?.len() as u32,
193                file.crc32,
194            )?;
195        }
196
197        Ok(())
198    }
199}