sftool_lib/common/
write_flash.rs

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