Skip to main content

sftool_lib/common/
ram_command.rs

1use crate::{Error, Result};
2use serialport::SerialPort;
3use std::io::{Read, Write};
4use std::str::FromStr;
5use strum::{Display, EnumString};
6
7/// 通用的RAM命令枚举,可在不同芯片间复用
8#[derive(EnumString, Display, Debug, Clone, PartialEq, Eq)]
9pub enum Command {
10    #[strum(to_string = "burn_erase_all 0x{address:08x}\r")]
11    EraseAll { address: u32 },
12
13    #[strum(to_string = "burn_verify 0x{address:08x} 0x{len:08x} 0x{crc:08x}\r")]
14    Verify { address: u32, len: u32, crc: u32 },
15
16    #[strum(to_string = "burn_erase 0x{address:08x} 0x{len:08x}\r")]
17    Erase { address: u32, len: u32 },
18
19    #[strum(to_string = "burn_erase_write 0x{address:08x} 0x{len:08x}\r")]
20    WriteAndErase { address: u32, len: u32 },
21
22    #[strum(to_string = "burn_write 0x{address:08x} 0x{len:08x}\r")]
23    Write { address: u32, len: u32 },
24
25    #[strum(to_string = "burn_read 0x{address:08x} 0x{len:08x}\r")]
26    Read { address: u32, len: u32 },
27
28    #[strum(to_string = "burn_reset\r")]
29    SoftReset,
30
31    #[strum(to_string = "burn_speed {baud} {delay}\r")]
32    SetBaud { baud: u32, delay: u32 },
33}
34
35/// 通用的命令响应枚举
36#[derive(EnumString, Display, Debug, Clone, PartialEq, Eq)]
37pub enum Response {
38    #[strum(serialize = "OK")]
39    Ok,
40    #[strum(serialize = "Fail")]
41    Fail,
42    #[strum(serialize = "RX_WAIT")]
43    RxWait,
44}
45
46/// 响应字符串查找表
47pub const RESPONSE_STR_TABLE: [&str; 3] = ["OK", "Fail", "RX_WAIT"];
48
49/// RAM命令处理trait,定义了发送命令和数据的接口
50pub trait RamCommand {
51    fn command(&mut self, cmd: Command) -> Result<Response>;
52    fn send_data(&mut self, data: &[u8]) -> Result<Response>;
53    fn format_command(&self, cmd: &Command) -> String {
54        cmd.to_string()
55    }
56}
57
58/// Stub下载trait,定义了下载stub的接口
59pub trait DownloadStub {
60    fn download_stub(&mut self) -> Result<()>;
61}
62
63/// 命令处理的配置参数
64pub struct CommandConfig {
65    pub compat_mode: bool,
66    pub chunk_size: usize,
67    pub chunk_delay_ms: u64,
68}
69
70impl Default for CommandConfig {
71    fn default() -> Self {
72        Self {
73            compat_mode: false,
74            chunk_size: 256,
75            chunk_delay_ms: 10,
76        }
77    }
78}
79
80/// 通用的RAM操作处理器,包含可复用的逻辑
81pub struct RamOps;
82
83impl RamOps {
84    const DEFAULT_TIMEOUT_MS: u128 = 4000;
85    const ERASE_ALL_TIMEOUT_MS: u128 = 30 * 1000;
86
87    /// 发送命令并等待响应的通用实现
88    pub fn send_command_and_wait_response(
89        port: &mut Box<dyn SerialPort>,
90        cmd: Command,
91        command_str: &str,
92        memory_type: &str,
93    ) -> Result<Response> {
94        tracing::debug!("command: {:?}", cmd);
95
96        // 发送命令
97        port.write_all(command_str.as_bytes())?;
98        port.flush()?;
99        // 在macOS上,FTDI的驱动似乎不高兴我们清除输入缓冲区,这可能会导致后续要发送的内容被截断
100        // 因此这个地方我们不再需要清理缓冲区,应该在后续的操作中滤除掉额外的信息
101        // port.clear(serialport::ClearBuffer::All)?;
102
103        // 确定超时时间
104        let timeout = match cmd {
105            Command::EraseAll { .. } => Self::ERASE_ALL_TIMEOUT_MS,
106            _ => Self::DEFAULT_TIMEOUT_MS,
107        };
108        let timeout = if memory_type == "sd" {
109            timeout * 3
110        } else {
111            timeout
112        };
113
114        // 某些命令直接返回成功,不等待响应
115        match cmd {
116            Command::SetBaud { .. } | Command::Read { .. } | Command::Erase { .. } => {
117                return Ok(Response::Ok);
118            }
119            _ => (),
120        }
121
122        Self::wait_for_response(port, timeout)
123    }
124
125    /// 发送数据并等待响应的通用实现
126    pub fn send_data_and_wait_response(
127        port: &mut Box<dyn SerialPort>,
128        data: &[u8],
129        config: &CommandConfig,
130    ) -> Result<Response> {
131        // 根据配置发送数据
132        if !config.compat_mode {
133            port.write_all(data)?;
134            port.flush()?;
135        } else {
136            // 兼容模式:分块发送
137            for chunk in data.chunks(config.chunk_size) {
138                port.write_all(chunk)?;
139                port.flush()?;
140                std::thread::sleep(std::time::Duration::from_millis(config.chunk_delay_ms));
141            }
142        }
143
144        Self::wait_for_response(port, Self::DEFAULT_TIMEOUT_MS)
145    }
146
147    /// 等待响应的通用实现
148    fn wait_for_response(port: &mut Box<dyn SerialPort>, timeout_ms: u128) -> Result<Response> {
149        let mut buffer = Vec::new();
150        let now = std::time::SystemTime::now();
151
152        loop {
153            let elapsed = now.elapsed().unwrap().as_millis();
154            if elapsed > timeout_ms {
155                tracing::debug!("Response buffer: {:?}", String::from_utf8_lossy(&buffer));
156                return Err(Error::timeout("waiting for RAM command response"));
157            }
158
159            let mut byte = [0];
160            let ret = port.read_exact(&mut byte);
161            if ret.is_err() {
162                continue;
163            }
164            buffer.push(byte[0]);
165
166            // 检查是否收到预期的响应
167            for response_str in RESPONSE_STR_TABLE.iter() {
168                let response_bytes = response_str.as_bytes();
169                let exists = buffer
170                    .windows(response_bytes.len())
171                    .any(|window| window == response_bytes);
172                if exists {
173                    tracing::debug!("Response buffer: {:?}", String::from_utf8_lossy(&buffer));
174                    return Response::from_str(response_str)
175                        .map_err(|e| Error::invalid_input(e.to_string()));
176                }
177            }
178        }
179    }
180
181    /// 等待shell提示符的通用实现
182    pub fn wait_for_shell_prompt(
183        port: &mut Box<dyn SerialPort>,
184        prompt: &[u8],
185        retry_interval_ms: u64,
186        max_retries: u32,
187    ) -> Result<()> {
188        let mut buffer = Vec::new();
189        let mut now = std::time::SystemTime::now();
190        let mut retry_count = 0;
191
192        // 发送初始的回车换行
193        port.write_all(b"\r\n")?;
194        port.flush()?;
195
196        loop {
197            let elapsed = now.elapsed().unwrap().as_millis();
198            if elapsed > retry_interval_ms as u128 {
199                tracing::warn!(
200                    "Wait for shell Failed, retry. buffer: {:?}",
201                    String::from_utf8_lossy(&buffer)
202                );
203                port.clear(serialport::ClearBuffer::All)?;
204                tracing::debug!("Retrying to find shell prompt...");
205                std::thread::sleep(std::time::Duration::from_millis(100));
206                retry_count += 1;
207                now = std::time::SystemTime::now();
208                port.write_all(b"\r\n")?;
209                port.flush()?;
210                buffer.clear();
211            }
212
213            if retry_count > max_retries {
214                return Err(Error::timeout("waiting for shell prompt"));
215            }
216
217            let mut byte = [0];
218            let ret = port.read_exact(&mut byte);
219            if ret.is_err() {
220                continue;
221            }
222            buffer.push(byte[0]);
223
224            // 检查是否收到shell提示符
225            if buffer.windows(prompt.len()).any(|window| window == prompt) {
226                break;
227            }
228        }
229
230        Ok(())
231    }
232}