1pub mod erase_flash;
4pub mod ram_command;
5pub mod read_flash;
6pub mod reset;
7pub mod speed;
8pub mod write_flash;
9
10use crate::sf32lb55::ram_command::DownloadStub;
11use crate::{Result, SifliTool, SifliToolBase, SifliToolTrait};
12use serialport::SerialPort;
13use std::io::Write;
14use std::time::Duration;
15
16pub struct SF32LB55Tool {
17 pub base: SifliToolBase,
18 pub port: Box<dyn SerialPort>,
19}
20
21unsafe impl Send for SF32LB55Tool {}
23unsafe impl Sync for SF32LB55Tool {}
24
25#[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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37#[repr(u8)]
38enum DfuConfigType {
39 BootPatchSig = 10,
40}
41
42impl SF32LB55Tool {
43 const BLOCK_SIZE: usize = 512;
45 const HDR_SIZE: usize = 32 + 296;
46 const CHUNK_OVERHEAD: usize = 32 + 4;
47
48 fn send_dfu_command(&mut self, data_len: usize, delay_ms: Option<u64>) -> Result<()> {
50 let cmd = format!("dfu_recv {}\r", data_len);
51 tracing::trace!("Sending DFU command: {}", cmd.trim());
52
53 self.port.write_all(cmd.as_bytes())?;
54 self.port.flush()?;
55
56 if let Some(delay) = delay_ms {
57 std::thread::sleep(Duration::from_millis(delay));
58 }
59
60 Ok(())
61 }
62
63 fn send_dfu_data(&mut self, header: &[u8], data: &[u8], delay_ms: Option<u64>) -> Result<()> {
65 tracing::trace!(
66 "Sending DFU data: header={:?}, data_len={}",
67 header,
68 data.len()
69 );
70
71 self.port.write_all(header)?;
72 self.port.write_all(data)?;
73 self.port.flush()?;
74
75 if let Some(delay) = delay_ms {
76 std::thread::sleep(Duration::from_millis(delay));
77 }
78
79 Ok(())
80 }
81
82 fn download_stub_impl(&mut self) -> Result<()> {
83 use crate::ram_stub::{self, SIG_PUB_FILE, load_stub_file};
84
85 tracing::info!("Starting SF32LB55 stub download process");
86 self.port.clear(serialport::ClearBuffer::All)?;
87
88 let progress = self.progress();
89 let spinner = progress.create_spinner("Download stub...");
90
91 tracing::debug!("Loading signature public key file: {}", SIG_PUB_FILE);
93 let sig_pub_data = ram_stub::RamStubFile::get(SIG_PUB_FILE).ok_or_else(|| {
94 tracing::error!("Signature public key file not found: {}", SIG_PUB_FILE);
95 std::io::Error::new(
96 std::io::ErrorKind::NotFound,
97 "58X_sig_pub.der file not found",
98 )
99 })?;
100
101 spinner.set_message("Downloading signature key...");
102 self.download_boot_patch_sigkey(&sig_pub_data.data)?;
103
104 let chip_memory_key = format!("sf32lb55_{}", self.base.memory_type);
106 let stub = load_stub_file(self.base.external_stub_path.as_deref(), &chip_memory_key)?;
107
108 spinner.set_message("Downloading RAM stub...");
109
110 self.download_image(&stub.data, 9)?;
112
113 spinner.finish_with_message("Download stub success!");
114
115 tracing::info!("SF32LB55 stub download completed successfully");
116 Ok(())
117 }
118
119 fn download_boot_patch_sigkey(&mut self, sig_data: &[u8]) -> Result<()> {
121 tracing::info!(
122 "Starting boot patch signature key download, size: {} bytes",
123 sig_data.len()
124 );
125
126 let header = [
127 DfuCommandType::Config as u8,
128 DfuConfigType::BootPatchSig as u8,
129 ];
130 let total_len = 2 + sig_data.len();
131
132 self.send_dfu_command(total_len, Some(10))?;
133 self.send_dfu_data(&header, sig_data, Some(4))?;
134
135 tracing::debug!("Waiting for boot patch signature key response...");
136 self.wait_for_ok_response(3000)?;
137
138 tracing::info!("Boot patch signature key downloaded successfully");
139 Ok(())
140 }
141
142 fn download_image(&mut self, data: &[u8], flash_id: u8) -> Result<()> {
144 tracing::info!(
145 "Starting image download: flash_id={}, size={} bytes",
146 flash_id,
147 data.len()
148 );
149
150 self.download_image_header(data, flash_id)?;
152
153 self.download_image_body(data, flash_id)?;
155
156 self.download_image_end(flash_id)?;
158
159 tracing::info!("Image download completed successfully");
160 Ok(())
161 }
162
163 fn download_image_header(&mut self, data: &[u8], flash_id: u8) -> Result<()> {
165 tracing::debug!("Downloading image header...");
166
167 let header = [DfuCommandType::ImageHeader as u8, flash_id];
168 let total_len = 2 + Self::HDR_SIZE;
169
170 self.send_dfu_command(total_len, Some(10))?;
171 self.send_dfu_data(&header, &data[0..Self::HDR_SIZE], None)?;
172
173 tracing::debug!("Waiting for image header response...");
174 self.wait_for_ok_response(3000)?;
175
176 tracing::debug!("Image header downloaded successfully");
177 Ok(())
178 }
179
180 fn download_image_body(&mut self, data: &[u8], flash_id: u8) -> Result<()> {
182 tracing::debug!("Downloading image body...");
183
184 let body_header = [DfuCommandType::ImageBody as u8, flash_id];
185 let mut offset = Self::HDR_SIZE;
186 let mut chunk_count = 0;
187
188 while offset < data.len() {
189 let remaining = data.len() - offset;
190 let chunk_size = std::cmp::min(remaining, Self::CHUNK_OVERHEAD + Self::BLOCK_SIZE);
191
192 tracing::trace!(
193 "Sending chunk {}: offset={}, size={}",
194 chunk_count,
195 offset,
196 chunk_size
197 );
198
199 let total_len = 2 + chunk_size;
200 self.send_dfu_command(total_len, Some(10))?;
201 self.send_dfu_data(&body_header, &data[offset..offset + chunk_size], None)?;
202
203 tracing::trace!("Waiting for chunk {} response...", chunk_count);
204 self.wait_for_ok_response(3000)?;
205
206 offset += chunk_size;
207 chunk_count += 1;
208 }
209
210 tracing::debug!("Image body downloaded successfully: {} chunks", chunk_count);
211 Ok(())
212 }
213
214 fn download_image_end(&mut self, flash_id: u8) -> Result<()> {
216 tracing::debug!("Sending image end marker...");
217
218 let end_header = [DfuCommandType::End as u8, flash_id];
219
220 self.send_dfu_command(2, Some(10))?;
221 self.send_dfu_data(&end_header, &[], None)?;
222
223 tracing::debug!("Waiting for image end response...");
224 self.wait_for_ok_response(5000)?;
225
226 tracing::debug!("Image end marker sent successfully");
227 Ok(())
228 }
229
230 fn wait_for_ok_response(&mut self, timeout_ms: u64) -> Result<()> {
232 use std::io::Read;
233
234 let mut buffer = Vec::new();
235 let start_time = std::time::SystemTime::now();
236 let mut last_log_time = start_time;
237
238 tracing::trace!("Waiting for OK response with timeout: {}ms", timeout_ms);
239
240 loop {
241 let elapsed = start_time.elapsed().unwrap().as_millis() as u64;
242 if elapsed > timeout_ms {
243 let response_str = String::from_utf8_lossy(&buffer);
244 tracing::error!(
245 "Timeout waiting for OK response after {}ms. Received: '{}'",
246 elapsed,
247 response_str
248 );
249 return Err(std::io::Error::new(
250 std::io::ErrorKind::TimedOut,
251 format!("Timeout waiting for OK response: {}", response_str),
252 )
253 .into());
254 }
255
256 if elapsed > 0
258 && elapsed.is_multiple_of(1000)
259 && start_time.elapsed().unwrap()
260 > last_log_time.elapsed().unwrap() + Duration::from_secs(1)
261 {
262 tracing::trace!("Still waiting for response... elapsed: {}ms", elapsed);
263 last_log_time = std::time::SystemTime::now();
264 }
265
266 let mut byte = [0];
267 if self.port.read_exact(&mut byte).is_ok() {
268 buffer.push(byte[0]);
269
270 if buffer.windows(2).any(|window| window == b"OK") {
272 let response_str = String::from_utf8_lossy(&buffer);
273 tracing::trace!(
274 "Received OK response after {}ms: '{}'",
275 elapsed,
276 response_str
277 );
278 return Ok(());
279 }
280
281 if buffer.windows(4).any(|window| window == b"Fail") {
283 let response_str = String::from_utf8_lossy(&buffer);
284 tracing::error!(
285 "Received Fail response after {}ms: '{}'",
286 elapsed,
287 response_str
288 );
289 return Err(std::io::Error::other(format!(
290 "Received Fail response: {}",
291 response_str
292 ))
293 .into());
294 }
295
296 if buffer.len() > 1024 {
298 let response_str = String::from_utf8_lossy(&buffer);
299 tracing::warn!(
300 "Response buffer too large ({}), truncating. Content: '{}'",
301 buffer.len(),
302 response_str
303 );
304 buffer.drain(..512); }
306 }
307 }
308 }
309}
310
311impl SifliTool for SF32LB55Tool {
312 fn create_tool(base: SifliToolBase) -> Box<dyn SifliTool> {
313 let mut port = serialport::new(&base.port_name, 1000000)
314 .timeout(Duration::from_secs(5))
315 .open()
316 .unwrap();
317 port.write_request_to_send(false).unwrap();
318 std::thread::sleep(Duration::from_millis(100));
319
320 let mut tool = Box::new(Self { base, port });
321 if tool.base.before.should_download_stub() {
322 tool.download_stub().expect("Failed to download stub");
323 }
324 tool
325 }
326}
327
328impl SifliToolTrait for SF32LB55Tool {
329 fn port(&mut self) -> &mut Box<dyn SerialPort> {
330 &mut self.port
331 }
332
333 fn base(&self) -> &SifliToolBase {
334 &self.base
335 }
336
337 fn set_speed(&mut self, _baud: u32) -> Result<()> {
338 todo!("SF32LB55Tool::set_speed not implemented yet")
339 }
340
341 fn soft_reset(&mut self) -> Result<()> {
342 use crate::reset::Reset;
343 Reset::soft_reset(self)
344 }
345}