sftool_lib/common/
write_flash.rs

1use crate::common::ram_command::{Command, RamCommand, Response};
2use crate::progress::{ProgressOperation, ProgressStatus};
3use crate::{Error, Result, SifliToolTrait, WriteFlashFile};
4use std::io::{BufReader, Read, Write};
5
6/// 通用的Flash写入操作实现
7pub struct FlashWriter;
8
9impl FlashWriter {
10    /// 擦除所有Flash区域
11    pub fn erase_all<T>(tool: &mut T, write_flash_files: &[WriteFlashFile]) -> Result<()>
12    where
13        T: SifliToolTrait + RamCommand,
14    {
15        let progress = tool.progress();
16        let spinner = progress.create_spinner(ProgressOperation::EraseAllRegions);
17
18        let mut erase_address: Vec<u32> = Vec::new();
19        for f in write_flash_files.iter() {
20            let address = f.address & 0xFF00_0000;
21            // 如果ERASE_ADDRESS中的地址已经被擦除过,则跳过
22            if erase_address.contains(&address) {
23                continue;
24            }
25            tool.command(Command::EraseAll { address: f.address })?;
26            erase_address.push(address);
27        }
28
29        spinner.finish(ProgressStatus::Success);
30        Ok(())
31    }
32
33    /// 验证数据
34    pub fn verify<T>(tool: &mut T, address: u32, len: u32, crc: u32) -> Result<()>
35    where
36        T: SifliToolTrait + RamCommand,
37    {
38        let progress = tool.progress();
39        let spinner = progress.create_spinner(ProgressOperation::Verify { address, len });
40
41        let response = tool.command(Command::Verify { address, len, crc })?;
42        if response != Response::Ok {
43            return Err(Error::protocol(format!(
44                "verify failed for 0x{:08X}..0x{:08X}",
45                address,
46                address + len.saturating_sub(1)
47            )));
48        }
49
50        spinner.finish(ProgressStatus::Success);
51        Ok(())
52    }
53
54    /// 写入单个文件到Flash(非全擦除模式)
55    pub fn write_file_incremental<T>(
56        tool: &mut T,
57        file: &WriteFlashFile,
58        verify: bool,
59    ) -> Result<()>
60    where
61        T: SifliToolTrait + RamCommand,
62    {
63        let progress = tool.progress();
64        let re_download_spinner = progress.create_spinner(ProgressOperation::CheckRedownload {
65            address: file.address,
66            size: file.file.metadata()?.len(),
67        });
68
69        let response = tool.command(Command::Verify {
70            address: file.address,
71            len: file.file.metadata()?.len() as u32,
72            crc: file.crc32,
73        })?;
74
75        if response == Response::Ok {
76            re_download_spinner.finish(ProgressStatus::Skipped);
77            return Ok(());
78        }
79
80        re_download_spinner.finish(ProgressStatus::Required);
81
82        let download_bar = progress.create_bar(
83            file.file.metadata()?.len(),
84            ProgressOperation::WriteFlash {
85                address: file.address,
86                size: file.file.metadata()?.len(),
87            },
88        );
89
90        let res = tool.command(Command::WriteAndErase {
91            address: file.address,
92            len: file.file.metadata()?.len() as u32,
93        })?;
94        if res != Response::RxWait {
95            return Err(Error::protocol(format!(
96                "write flash failed to start at 0x{:08X}",
97                file.address
98            )));
99        }
100
101        let mut buffer = vec![0u8; 128 * 1024];
102        let mut reader = BufReader::new(&file.file);
103
104        loop {
105            let bytes_read = reader.read(&mut buffer)?;
106            if bytes_read == 0 {
107                break;
108            }
109            let res = tool.send_data(&buffer[..bytes_read])?;
110            if res == Response::RxWait {
111                download_bar.inc(bytes_read as u64);
112                continue;
113            } else if res != Response::Ok {
114                return Err(Error::protocol(format!(
115                    "write flash failed during transfer at 0x{:08X}",
116                    file.address
117                )));
118            }
119        }
120
121        download_bar.finish(ProgressStatus::Success);
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            ProgressOperation::WriteFlash {
150                address: file.address,
151                size: file.file.metadata()?.len(),
152            },
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,
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(Error::protocol(format!(
176                    "write flash failed during transfer at 0x{:08X}",
177                    address
178                )));
179            }
180            address += bytes_read as u32;
181            download_bar.inc(bytes_read as u64);
182        }
183
184        download_bar.finish(ProgressStatus::Success);
185
186        // verify
187        if verify {
188            Self::verify(
189                tool,
190                file.address,
191                file.file.metadata()?.len() as u32,
192                file.crc32,
193            )?;
194        }
195
196        Ok(())
197    }
198}