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