sftool_lib/sf32lb58/
mod.rs

1//! SF32LB58 芯片特定实现模块
2
3pub mod erase_flash;
4pub mod ram_command;
5pub mod read_flash;
6pub mod reset;
7pub mod speed;
8pub mod write_flash;
9
10use crate::sf32lb58::ram_command::DownloadStub;
11use crate::{SifliTool, SifliToolBase, SifliToolTrait};
12use serialport::SerialPort;
13use std::io::Write;
14use std::time::Duration;
15
16pub struct SF32LB58Tool {
17    pub base: SifliToolBase,
18    pub port: Box<dyn SerialPort>,
19}
20
21// 为 SF32LB58Tool 实现 Send 和 Sync
22unsafe impl Send for SF32LB58Tool {}
23unsafe impl Sync for SF32LB58Tool {}
24
25/// DFU协议命令类型
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27#[repr(u8)]
28enum DfuCommandType {
29    ImageHeader = 1,
30    ImageBody = 2,
31    Config = 3,
32    End = 4,
33}
34
35/// DFU配置类型
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37#[repr(u8)]
38enum DfuConfigType {
39    BootPatchSig = 10,
40}
41
42impl SF32LB58Tool {
43    // DFU协议常量
44    const BLOCK_SIZE: usize = 512;
45    const HDR_SIZE: usize = 32 + 296;
46    const CHUNK_OVERHEAD: usize = 32 + 4;
47
48    /// 发送DFU命令的通用方法
49    fn send_dfu_command(
50        &mut self,
51        data_len: usize,
52        delay_ms: Option<u64>,
53    ) -> Result<(), std::io::Error> {
54        let cmd = format!("dfu_recv {}\r", data_len);
55        tracing::trace!("Sending DFU command: {}", cmd.trim());
56
57        self.port.write_all(cmd.as_bytes())?;
58        self.port.flush()?;
59
60        if let Some(delay) = delay_ms {
61            std::thread::sleep(Duration::from_millis(delay));
62        }
63
64        Ok(())
65    }
66
67    /// 发送DFU数据的通用方法
68    fn send_dfu_data(
69        &mut self,
70        header: &[u8],
71        data: &[u8],
72        delay_ms: Option<u64>,
73    ) -> Result<(), std::io::Error> {
74        tracing::trace!(
75            "Sending DFU data: header={:?}, data_len={}",
76            header,
77            data.len()
78        );
79
80        self.port.write_all(header)?;
81        self.port.write_all(data)?;
82        self.port.flush()?;
83
84        if let Some(delay) = delay_ms {
85            std::thread::sleep(Duration::from_millis(delay));
86        }
87
88        Ok(())
89    }
90
91    fn download_stub_impl(&mut self) -> Result<(), std::io::Error> {
92        use crate::ram_stub::{self, CHIP_FILE_NAME, SIG_PUB_FILE};
93
94        tracing::info!("Starting SF32LB58 stub download process");
95        self.port.clear(serialport::ClearBuffer::All)?;
96
97        let progress = self.progress();
98        let spinner = progress.create_spinner("Download stub...");
99
100        // 1. 下载签名公钥文件 (58X_sig_pub.der)
101        tracing::debug!("Loading signature public key file: {}", SIG_PUB_FILE);
102        let sig_pub_data = ram_stub::RamStubFile::get(SIG_PUB_FILE).ok_or_else(|| {
103            tracing::error!("Signature public key file not found: {}", SIG_PUB_FILE);
104            std::io::Error::new(
105                std::io::ErrorKind::NotFound,
106                "58X_sig_pub.der file not found",
107            )
108        })?;
109
110        spinner.set_message("Downloading signature key...");
111        self.download_boot_patch_sigkey(&sig_pub_data.data)?;
112
113        // 2. 下载RAM stub文件
114        let memory_type_key = format!("sf32lb58_{}", self.base.memory_type);
115        tracing::debug!("Looking for stub file with key: {}", memory_type_key);
116
117        let stub_file_name = CHIP_FILE_NAME
118            .get(memory_type_key.as_str())
119            .ok_or_else(|| {
120                tracing::error!("No stub file found for chip type: {}", memory_type_key);
121                std::io::Error::new(
122                    std::io::ErrorKind::NotFound,
123                    format!(
124                        "No stub file found for the given chip and memory type: {}",
125                        memory_type_key
126                    ),
127                )
128            })?;
129
130        tracing::debug!("Loading RAM stub file: {}", stub_file_name);
131        let stub = ram_stub::RamStubFile::get(stub_file_name).ok_or_else(|| {
132            tracing::error!("Stub file not found: {}", stub_file_name);
133            std::io::Error::new(
134                std::io::ErrorKind::NotFound,
135                format!("Stub file not found: {}", stub_file_name),
136            )
137        })?;
138
139        spinner.set_message("Downloading RAM stub...");
140
141        // 发送下载镜像命令(flashid = 9 对应RAM stub)
142        self.download_image(&stub.data, 9)?;
143
144        spinner.finish_with_message("Download stub success!");
145
146        tracing::info!("SF32LB58 stub download completed successfully");
147        Ok(())
148    }
149
150    /// 下载引导补丁签名密钥
151    fn download_boot_patch_sigkey(&mut self, sig_data: &[u8]) -> Result<(), std::io::Error> {
152        tracing::info!(
153            "Starting boot patch signature key download, size: {} bytes",
154            sig_data.len()
155        );
156
157        let header = [
158            DfuCommandType::Config as u8,
159            DfuConfigType::BootPatchSig as u8,
160        ];
161        let total_len = 2 + sig_data.len();
162
163        self.send_dfu_command(total_len, Some(10))?;
164        self.send_dfu_data(&header, sig_data, Some(4))?;
165
166        tracing::debug!("Waiting for boot patch signature key response...");
167        self.wait_for_ok_response(3000)?;
168
169        tracing::info!("Boot patch signature key downloaded successfully");
170        Ok(())
171    }
172
173    /// 下载镜像文件
174    fn download_image(&mut self, data: &[u8], flash_id: u8) -> Result<(), std::io::Error> {
175        tracing::info!(
176            "Starting image download: flash_id={}, size={} bytes",
177            flash_id,
178            data.len()
179        );
180
181        // 1. 发送镜像头部
182        self.download_image_header(data, flash_id)?;
183
184        // 2. 发送镜像主体
185        self.download_image_body(data, flash_id)?;
186
187        // 3. 发送结束标志
188        self.download_image_end(flash_id)?;
189
190        tracing::info!("Image download completed successfully");
191        Ok(())
192    }
193
194    /// 下载镜像头部
195    fn download_image_header(&mut self, data: &[u8], flash_id: u8) -> Result<(), std::io::Error> {
196        tracing::debug!("Downloading image header...");
197
198        let header = [DfuCommandType::ImageHeader as u8, flash_id];
199        let total_len = 2 + Self::HDR_SIZE;
200
201        self.send_dfu_command(total_len, Some(10))?;
202        self.send_dfu_data(&header, &data[0..Self::HDR_SIZE], None)?;
203
204        tracing::debug!("Waiting for image header response...");
205        self.wait_for_ok_response(3000)?;
206
207        tracing::debug!("Image header downloaded successfully");
208        Ok(())
209    }
210
211    /// 下载镜像主体
212    fn download_image_body(&mut self, data: &[u8], flash_id: u8) -> Result<(), std::io::Error> {
213        tracing::debug!("Downloading image body...");
214
215        let body_header = [DfuCommandType::ImageBody as u8, flash_id];
216        let mut offset = Self::HDR_SIZE;
217        let mut chunk_count = 0;
218
219        while offset < data.len() {
220            let remaining = data.len() - offset;
221            let chunk_size = std::cmp::min(remaining, Self::CHUNK_OVERHEAD + Self::BLOCK_SIZE);
222
223            tracing::trace!(
224                "Sending chunk {}: offset={}, size={}",
225                chunk_count,
226                offset,
227                chunk_size
228            );
229
230            let total_len = 2 + chunk_size;
231            self.send_dfu_command(total_len, Some(10))?;
232            self.send_dfu_data(&body_header, &data[offset..offset + chunk_size], None)?;
233
234            tracing::trace!("Waiting for chunk {} response...", chunk_count);
235            self.wait_for_ok_response(3000)?;
236
237            offset += chunk_size;
238            chunk_count += 1;
239        }
240
241        tracing::debug!("Image body downloaded successfully: {} chunks", chunk_count);
242        Ok(())
243    }
244
245    /// 下载镜像结束标志
246    fn download_image_end(&mut self, flash_id: u8) -> Result<(), std::io::Error> {
247        tracing::debug!("Sending image end marker...");
248
249        let end_header = [DfuCommandType::End as u8, flash_id];
250
251        self.send_dfu_command(2, Some(10))?;
252        self.send_dfu_data(&end_header, &[], None)?;
253
254        tracing::debug!("Waiting for image end response...");
255        self.wait_for_ok_response(5000)?;
256
257        tracing::debug!("Image end marker sent successfully");
258        Ok(())
259    }
260
261    /// 等待OK响应
262    fn wait_for_ok_response(&mut self, timeout_ms: u64) -> Result<(), std::io::Error> {
263        use std::io::Read;
264
265        let mut buffer = Vec::new();
266        let start_time = std::time::SystemTime::now();
267        let mut last_log_time = start_time;
268
269        tracing::trace!("Waiting for OK response with timeout: {}ms", timeout_ms);
270
271        loop {
272            let elapsed = start_time.elapsed().unwrap().as_millis() as u64;
273            if elapsed > timeout_ms {
274                let response_str = String::from_utf8_lossy(&buffer);
275                tracing::error!(
276                    "Timeout waiting for OK response after {}ms. Received: '{}'",
277                    elapsed,
278                    response_str
279                );
280                return Err(std::io::Error::new(
281                    std::io::ErrorKind::TimedOut,
282                    format!("Timeout waiting for OK response: {}", response_str),
283                ));
284            }
285
286            // 每秒记录一次等待状态
287            if elapsed > 0
288                && elapsed % 1000 == 0
289                && start_time.elapsed().unwrap()
290                    > last_log_time.elapsed().unwrap() + Duration::from_secs(1)
291            {
292                tracing::trace!("Still waiting for response... elapsed: {}ms", elapsed);
293                last_log_time = std::time::SystemTime::now();
294            }
295
296            let mut byte = [0];
297            if let Ok(_) = self.port.read_exact(&mut byte) {
298                buffer.push(byte[0]);
299
300                // 检查是否收到"OK"响应
301                if buffer.windows(2).any(|window| window == b"OK") {
302                    let response_str = String::from_utf8_lossy(&buffer);
303                    tracing::trace!(
304                        "Received OK response after {}ms: '{}'",
305                        elapsed,
306                        response_str
307                    );
308                    return Ok(());
309                }
310
311                // 检查是否收到"Fail"响应
312                if buffer.windows(4).any(|window| window == b"Fail") {
313                    let response_str = String::from_utf8_lossy(&buffer);
314                    tracing::error!(
315                        "Received Fail response after {}ms: '{}'",
316                        elapsed,
317                        response_str
318                    );
319                    return Err(std::io::Error::new(
320                        std::io::ErrorKind::Other,
321                        format!("Received Fail response: {}", response_str),
322                    ));
323                }
324
325                // 限制缓冲区大小,避免内存占用过多
326                if buffer.len() > 1024 {
327                    let response_str = String::from_utf8_lossy(&buffer);
328                    tracing::warn!(
329                        "Response buffer too large ({}), truncating. Content: '{}'",
330                        buffer.len(),
331                        response_str
332                    );
333                    buffer.drain(..512); // 保留后半部分
334                }
335            }
336        }
337    }
338}
339
340impl SifliTool for SF32LB58Tool {
341    fn create_tool(base: SifliToolBase) -> Box<dyn SifliTool> {
342        let mut port = serialport::new(&base.port_name, 1000000)
343            .timeout(Duration::from_secs(5))
344            .open()
345            .unwrap();
346        port.write_request_to_send(false).unwrap();
347        std::thread::sleep(Duration::from_millis(100));
348
349        let mut tool = Box::new(Self { base, port });
350        tool.download_stub().expect("Failed to download stub");
351        tool
352    }
353}
354
355impl SifliToolTrait for SF32LB58Tool {
356    fn port(&mut self) -> &mut Box<dyn SerialPort> {
357        &mut self.port
358    }
359
360    fn base(&self) -> &SifliToolBase {
361        &self.base
362    }
363
364    fn set_speed(&mut self, _baud: u32) -> Result<(), std::io::Error> {
365        todo!("SF32LB58Tool::set_speed not implemented yet")
366    }
367
368    fn soft_reset(&mut self) -> Result<(), std::io::Error> {
369        use crate::reset::Reset;
370        Reset::soft_reset(self)
371    }
372}