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