Skip to main content

sftool_lib/common/
ram_command.rs

1use crate::common::serial_io::SerialIo;
2use crate::{Error, Result};
3use std::str::FromStr;
4use strum::{Display, EnumString};
5
6/// 通用的RAM命令枚举,可在不同芯片间复用
7#[derive(EnumString, Display, Debug, Clone, PartialEq, Eq)]
8pub enum Command {
9    #[strum(to_string = "burn_erase_all 0x{address:08x}\r")]
10    EraseAll { address: u32 },
11
12    #[strum(to_string = "burn_verify 0x{address:08x} 0x{len:08x} 0x{crc:08x}\r")]
13    Verify { address: u32, len: u32, crc: u32 },
14
15    #[strum(to_string = "burn_erase 0x{address:08x} 0x{len:08x}\r")]
16    Erase { address: u32, len: u32 },
17
18    #[strum(to_string = "burn_erase_write 0x{address:08x} 0x{len:08x}\r")]
19    WriteAndErase { address: u32, len: u32 },
20
21    #[strum(to_string = "burn_write 0x{address:08x} 0x{len:08x}\r")]
22    Write { address: u32, len: u32 },
23
24    #[strum(to_string = "burn_read 0x{address:08x} 0x{len:08x}\r")]
25    Read { address: u32, len: u32 },
26
27    #[strum(to_string = "burn_reset\r")]
28    SoftReset,
29
30    #[strum(to_string = "burn_speed {baud} {delay}\r")]
31    SetBaud { baud: u32, delay: u32 },
32}
33
34/// 通用的命令响应枚举
35#[derive(EnumString, Display, Debug, Clone, PartialEq, Eq)]
36pub enum Response {
37    #[strum(serialize = "OK")]
38    Ok,
39    #[strum(serialize = "Fail")]
40    Fail,
41    #[strum(serialize = "RX_WAIT")]
42    RxWait,
43}
44
45/// 响应字符串查找表
46pub const RESPONSE_STR_TABLE: [&str; 3] = ["OK", "Fail", "RX_WAIT"];
47
48/// RAM命令处理trait,定义了发送命令和数据的接口
49pub trait RamCommand {
50    fn command(&mut self, cmd: Command) -> Result<Response>;
51    fn send_data(&mut self, data: &[u8]) -> Result<Response>;
52    fn format_command(&self, cmd: &Command) -> String {
53        cmd.to_string()
54    }
55}
56
57/// Stub下载trait,定义了下载stub的接口
58pub trait DownloadStub {
59    fn download_stub(&mut self) -> Result<()>;
60}
61
62/// 命令处理的配置参数
63pub struct CommandConfig {
64    pub compat_mode: bool,
65    pub chunk_size: usize,
66    pub chunk_delay_ms: u64,
67}
68
69impl Default for CommandConfig {
70    fn default() -> Self {
71        Self {
72            compat_mode: false,
73            chunk_size: 256,
74            chunk_delay_ms: 10,
75        }
76    }
77}
78
79/// 通用的RAM操作处理器,包含可复用的逻辑
80pub struct RamOps;
81
82pub fn is_sd_memory(memory_type: &str) -> bool {
83    let memory_type = memory_type.to_ascii_lowercase();
84    memory_type == "sd" || memory_type.starts_with("sd_")
85}
86
87impl RamOps {
88    const DEFAULT_TIMEOUT_MS: u128 = 4000;
89    const ERASE_ALL_TIMEOUT_MS: u128 = 30 * 1000;
90
91    /// 发送命令并等待响应的通用实现
92    pub fn send_command_and_wait_response(
93        io: &mut SerialIo<'_>,
94        cmd: Command,
95        command_str: &str,
96        memory_type: &str,
97    ) -> Result<Response> {
98        tracing::debug!("command: {:?}", cmd);
99
100        // 发送命令
101        io.write_all(command_str.as_bytes())?;
102        io.flush()?;
103        // 在macOS上,FTDI的驱动似乎不高兴我们清除输入缓冲区,这可能会导致后续要发送的内容被截断
104        // 因此这个地方我们不再需要清理缓冲区,应该在后续的操作中滤除掉额外的信息
105        // port.clear(serialport::ClearBuffer::All)?;
106
107        // 确定超时时间
108        let timeout = match cmd {
109            Command::EraseAll { .. } => Self::ERASE_ALL_TIMEOUT_MS,
110            _ => Self::DEFAULT_TIMEOUT_MS,
111        };
112        let timeout = if is_sd_memory(memory_type) {
113            timeout * 3
114        } else {
115            timeout
116        };
117
118        // 某些命令直接返回成功,不等待响应
119        match cmd {
120            Command::SetBaud { .. } | Command::Read { .. } | Command::Erase { .. } => {
121                return Ok(Response::Ok);
122            }
123            _ => (),
124        }
125
126        Self::wait_for_response(io, timeout)
127    }
128
129    /// 发送数据并等待响应的通用实现
130    pub fn send_data_and_wait_response(
131        io: &mut SerialIo<'_>,
132        data: &[u8],
133        config: &CommandConfig,
134    ) -> Result<Response> {
135        // 根据配置发送数据
136        if !config.compat_mode {
137            io.write_all(data)?;
138            io.flush()?;
139        } else {
140            // 兼容模式:分块发送
141            for chunk in data.chunks(config.chunk_size) {
142                io.write_all(chunk)?;
143                io.flush()?;
144                io.sleep(std::time::Duration::from_millis(config.chunk_delay_ms))?;
145            }
146        }
147
148        Self::wait_for_response(io, Self::DEFAULT_TIMEOUT_MS)
149    }
150
151    /// 等待响应的通用实现
152    fn wait_for_response(io: &mut SerialIo<'_>, timeout_ms: u128) -> Result<Response> {
153        let matched = io.wait_for_patterns(
154            &RESPONSE_STR_TABLE.map(str::as_bytes),
155            std::time::Duration::from_millis(timeout_ms as u64),
156            "RAM command response",
157        )?;
158        tracing::debug!(
159            "Response buffer: {:?}",
160            String::from_utf8_lossy(&matched.buffer)
161        );
162        Response::from_str(RESPONSE_STR_TABLE[matched.index])
163            .map_err(|e| Error::invalid_input(e.to_string()))
164    }
165
166    /// 等待shell提示符的通用实现
167    pub fn wait_for_shell_prompt(
168        io: &mut SerialIo<'_>,
169        prompt: &[u8],
170        retry_interval_ms: u64,
171        max_retries: u32,
172    ) -> Result<()> {
173        io.wait_for_prompt(
174            prompt,
175            std::time::Duration::from_millis(retry_interval_ms),
176            max_retries,
177        )
178    }
179}