sftool_lib/common/
ram_command.rs1use crate::common::serial_io::SerialIo;
2use crate::{Error, Result};
3use std::str::FromStr;
4use strum::{Display, EnumString};
5
6#[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#[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
45pub const RESPONSE_STR_TABLE: [&str; 3] = ["OK", "Fail", "RX_WAIT"];
47
48pub 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
57pub trait DownloadStub {
59 fn download_stub(&mut self) -> Result<()>;
60}
61
62pub 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
79pub 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 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 io.write_all(command_str.as_bytes())?;
102 io.flush()?;
103 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 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 pub fn send_data_and_wait_response(
131 io: &mut SerialIo<'_>,
132 data: &[u8],
133 config: &CommandConfig,
134 ) -> Result<Response> {
135 if !config.compat_mode {
137 io.write_all(data)?;
138 io.flush()?;
139 } else {
140 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 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 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}