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