sftool_lib/common/
read_flash.rs

1use crate::common::ram_command::{Command, RamCommand};
2use crate::progress::ProgressHandler;
3use crate::utils::Utils;
4use crate::{Error, Result, SifliToolTrait};
5use crc::{Algorithm, Crc};
6use serialport::SerialPort;
7use std::collections::VecDeque;
8use std::fs::File;
9use std::io::{ErrorKind, Read, Seek, Write};
10use std::time::Instant;
11use tempfile::tempfile;
12
13/// 通用的Flash读取文件结构
14#[derive(Debug)]
15pub struct ReadFlashFile {
16    pub file_path: String,
17    pub address: u32,
18    pub size: u32,
19}
20
21/// 通用的Flash读取操作实现
22pub struct FlashReader;
23
24impl FlashReader {
25    const START_TRANS_MARKER: &'static [u8] = b"start_trans\r\n";
26    const READ_TIMEOUT_MS: u128 = 10_000;
27    const READ_CHUNK_SIZE: usize = 16 * 1024;
28    const CRC_32_ALGO: Algorithm<u32> = Algorithm {
29        width: 32,
30        poly: 0x04C11DB7,
31        init: 0,
32        refin: true,
33        refout: true,
34        xorout: 0,
35        check: 0,
36        residue: 0,
37    };
38
39    /// 解析读取文件信息 (filename@address:size格式)
40    pub fn parse_file_info(file_spec: &str) -> Result<ReadFlashFile> {
41        let Some((file_path, addr_size)) = file_spec.split_once('@') else {
42            return Err(Error::invalid_input(format!(
43                "Invalid format: {}. Expected: filename@address:size",
44                file_spec
45            )));
46        };
47        let Some((addr, size)) = addr_size.split_once(':') else {
48            return Err(Error::invalid_input(format!(
49                "Invalid format: {}. Expected: filename@address:size",
50                file_spec
51            )));
52        };
53
54        let address = Utils::str_to_u32(addr)
55            .map_err(|e| Error::invalid_input(format!("Invalid address '{}': {}", addr, e)))?;
56        let size = Utils::str_to_u32(size)
57            .map_err(|e| Error::invalid_input(format!("Invalid size '{}': {}", size, e)))?;
58
59        Ok(ReadFlashFile {
60            file_path: file_path.to_string(),
61            address,
62            size,
63        })
64    }
65
66    /// 从Flash读取数据的通用实现
67    pub fn read_flash_data<T>(
68        tool: &mut T,
69        address: u32,
70        size: u32,
71        output_path: &str,
72    ) -> Result<()>
73    where
74        T: SifliToolTrait + RamCommand,
75    {
76        let progress = tool.progress();
77        let progress_bar =
78            progress.create_bar(size as u64, format!("Reading from 0x{:08X}...", address));
79
80        let mut temp_file = tempfile()?;
81
82        // 读取一次即可,由设备负责连续发送数据
83        tool.command(Command::Read { address, len: size })?;
84
85        let (expected_crc, actual_crc) = {
86            let port = tool.port();
87
88            Self::wait_for_marker(port, Self::START_TRANS_MARKER, "start_trans marker")?;
89
90            let actual_crc =
91                Self::receive_payload(port, size, &mut temp_file, &progress_bar, address)?;
92
93            let expected_crc = Self::read_crc_value(port)?;
94            Self::expect_ok(port)?;
95
96            (expected_crc, actual_crc)
97        };
98
99        if actual_crc != expected_crc {
100            return Err(Error::CrcMismatch {
101                expected: expected_crc,
102                actual: actual_crc,
103            });
104        }
105
106        progress_bar.finish_with_message("Read complete");
107
108        temp_file.seek(std::io::SeekFrom::Start(0))?;
109        let mut output_file = File::create(output_path)?;
110        std::io::copy(&mut temp_file, &mut output_file)?;
111
112        Ok(())
113    }
114
115    fn wait_for_marker(port: &mut Box<dyn SerialPort>, marker: &[u8], context: &str) -> Result<()> {
116        if marker.is_empty() {
117            return Ok(());
118        }
119
120        let mut window = VecDeque::with_capacity(marker.len());
121        let mut last_activity = Instant::now();
122
123        loop {
124            let mut byte = [0u8; 1];
125            match port.read(&mut byte) {
126                Ok(0) => {
127                    if last_activity.elapsed().as_millis() > Self::READ_TIMEOUT_MS {
128                        return Err(Error::timeout(format!("waiting for {}", context)));
129                    }
130                }
131                Ok(_) => {
132                    last_activity = Instant::now();
133                    window.push_back(byte[0]);
134                    if window.len() > marker.len() {
135                        window.pop_front();
136                    }
137                    if window.len() == marker.len()
138                        && window.iter().copied().eq(marker.iter().copied())
139                    {
140                        return Ok(());
141                    }
142                }
143                Err(e) if matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock) => {
144                    if last_activity.elapsed().as_millis() > Self::READ_TIMEOUT_MS {
145                        return Err(Error::timeout(format!("waiting for {}", context)));
146                    }
147                }
148                Err(e) if e.kind() == ErrorKind::Interrupted => {}
149                Err(e) => return Err(e.into()),
150            }
151        }
152    }
153
154    fn receive_payload(
155        port: &mut Box<dyn SerialPort>,
156        size: u32,
157        temp_file: &mut File,
158        progress_bar: &ProgressHandler,
159        address: u32,
160    ) -> Result<u32> {
161        let mut remaining = size as usize;
162        let buffer_len = remaining.clamp(1usize, Self::READ_CHUNK_SIZE);
163        let mut buffer = vec![0u8; buffer_len];
164
165        let crc = Crc::<u32>::new(&Self::CRC_32_ALGO);
166        let mut digest = crc.digest();
167        let mut processed = 0usize;
168
169        while remaining > 0 {
170            let chunk_len = std::cmp::min(buffer.len(), remaining);
171            let chunk = &mut buffer[..chunk_len];
172            let current_address = address.saturating_add(processed as u32);
173            Self::read_exact_with_timeout(
174                port,
175                chunk,
176                Self::READ_TIMEOUT_MS,
177                &format!("reading flash at 0x{:08X}", current_address),
178            )?;
179
180            temp_file.write_all(chunk)?;
181            digest.update(chunk);
182
183            remaining -= chunk_len;
184            processed += chunk_len;
185            progress_bar.inc(chunk_len as u64);
186        }
187
188        Ok(digest.finalize())
189    }
190
191    fn read_crc_value(port: &mut Box<dyn SerialPort>) -> Result<u32> {
192        let line = Self::read_non_empty_line(port, "CRC response")?;
193        let lower = line.to_ascii_lowercase();
194        let prefix = "crc:0x";
195
196        if !lower.starts_with(prefix) {
197            return Err(Error::protocol(format!("unexpected CRC line: {}", line)));
198        }
199
200        let hex_part = &line[prefix.len()..];
201        u32::from_str_radix(hex_part, 16)
202            .map_err(|e| Error::protocol(format!("invalid CRC '{}': {}", line, e)))
203    }
204
205    fn expect_ok(port: &mut Box<dyn SerialPort>) -> Result<()> {
206        let line = Self::read_non_empty_line(port, "OK response")?;
207        if line != "OK" {
208            return Err(Error::protocol(format!("unexpected response: {}", line)));
209        }
210        Ok(())
211    }
212
213    fn read_non_empty_line(port: &mut Box<dyn SerialPort>, context: &str) -> Result<String> {
214        loop {
215            let line = Self::read_line(port, context)?;
216            let trimmed = line.trim().to_string();
217            if trimmed.is_empty() {
218                continue;
219            }
220            return Ok(trimmed);
221        }
222    }
223
224    fn read_line(port: &mut Box<dyn SerialPort>, context: &str) -> Result<String> {
225        let mut buffer = Vec::new();
226        let mut last_activity = Instant::now();
227
228        loop {
229            let mut byte = [0u8; 1];
230            match port.read(&mut byte) {
231                Ok(0) => {
232                    if last_activity.elapsed().as_millis() > Self::READ_TIMEOUT_MS {
233                        return Err(Error::timeout(format!("waiting for {}", context)));
234                    }
235                }
236                Ok(_) => {
237                    last_activity = Instant::now();
238                    match byte[0] {
239                        b'\n' => break,
240                        b'\r' => continue,
241                        ch => buffer.push(ch),
242                    }
243                }
244                Err(e) if matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock) => {
245                    if last_activity.elapsed().as_millis() > Self::READ_TIMEOUT_MS {
246                        return Err(Error::timeout(format!("waiting for {}", context)));
247                    }
248                }
249                Err(e) if e.kind() == ErrorKind::Interrupted => continue,
250                Err(e) => return Err(e.into()),
251            }
252        }
253
254        Ok(String::from_utf8_lossy(&buffer).into_owned())
255    }
256
257    fn read_exact_with_timeout(
258        port: &mut Box<dyn SerialPort>,
259        buf: &mut [u8],
260        timeout_ms: u128,
261        context: &str,
262    ) -> Result<()> {
263        if buf.is_empty() {
264            return Ok(());
265        }
266
267        let mut offset = 0;
268        let mut last_activity = Instant::now();
269
270        while offset < buf.len() {
271            match port.read(&mut buf[offset..]) {
272                Ok(0) => {
273                    if last_activity.elapsed().as_millis() > timeout_ms {
274                        return Err(Error::timeout(format!("waiting for {}", context)));
275                    }
276                }
277                Ok(n) => {
278                    offset += n;
279                    last_activity = Instant::now();
280                }
281                Err(e) if matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock) => {
282                    if last_activity.elapsed().as_millis() > timeout_ms {
283                        return Err(Error::timeout(format!("waiting for {}", context)));
284                    }
285                }
286                Err(e) if e.kind() == ErrorKind::Interrupted => continue,
287                Err(e) => return Err(e.into()),
288            }
289        }
290
291        Ok(())
292    }
293}