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::{Result, 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(&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, CHIP_FILE_NAME, SIG_PUB_FILE};
84
85 tracing::info!("Starting SF32LB58 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 memory_type_key = format!("sf32lb58_{}", self.base.memory_type);
106 tracing::debug!("Looking for stub file with key: {}", memory_type_key);
107
108 let stub_file_name = CHIP_FILE_NAME
109 .get(memory_type_key.as_str())
110 .ok_or_else(|| {
111 tracing::error!("No stub file found for chip type: {}", memory_type_key);
112 std::io::Error::new(
113 std::io::ErrorKind::NotFound,
114 format!(
115 "No stub file found for the given chip and memory type: {}",
116 memory_type_key
117 ),
118 )
119 })?;
120
121 tracing::debug!("Loading RAM stub file: {}", stub_file_name);
122 let stub = ram_stub::RamStubFile::get(stub_file_name).ok_or_else(|| {
123 tracing::error!("Stub file not found: {}", stub_file_name);
124 std::io::Error::new(
125 std::io::ErrorKind::NotFound,
126 format!("Stub file not found: {}", stub_file_name),
127 )
128 })?;
129
130 spinner.set_message("Downloading RAM stub...");
131
132 self.download_image(&stub.data, 9)?;
134
135 spinner.finish_with_message("Download stub success!");
136
137 tracing::info!("SF32LB58 stub download completed successfully");
138 Ok(())
139 }
140
141 fn download_boot_patch_sigkey(&mut self, sig_data: &[u8]) -> Result<()> {
143 tracing::info!(
144 "Starting boot patch signature key download, size: {} bytes",
145 sig_data.len()
146 );
147
148 let header = [
149 DfuCommandType::Config as u8,
150 DfuConfigType::BootPatchSig as u8,
151 ];
152 let total_len = 2 + sig_data.len();
153
154 self.send_dfu_command(total_len, Some(10))?;
155 self.send_dfu_data(&header, sig_data, Some(4))?;
156
157 tracing::debug!("Waiting for boot patch signature key response...");
158 self.wait_for_ok_response(3000)?;
159
160 tracing::info!("Boot patch signature key downloaded successfully");
161 Ok(())
162 }
163
164 fn download_image(&mut self, data: &[u8], flash_id: u8) -> Result<()> {
166 tracing::info!(
167 "Starting image download: flash_id={}, size={} bytes",
168 flash_id,
169 data.len()
170 );
171
172 self.download_image_header(data, flash_id)?;
174
175 self.download_image_body(data, flash_id)?;
177
178 self.download_image_end(flash_id)?;
180
181 tracing::info!("Image download completed successfully");
182 Ok(())
183 }
184
185 fn download_image_header(&mut self, data: &[u8], flash_id: u8) -> Result<()> {
187 tracing::debug!("Downloading image header...");
188
189 let header = [DfuCommandType::ImageHeader as u8, flash_id];
190 let total_len = 2 + Self::HDR_SIZE;
191
192 self.send_dfu_command(total_len, Some(10))?;
193 self.send_dfu_data(&header, &data[0..Self::HDR_SIZE], None)?;
194
195 tracing::debug!("Waiting for image header response...");
196 self.wait_for_ok_response(3000)?;
197
198 tracing::debug!("Image header downloaded successfully");
199 Ok(())
200 }
201
202 fn download_image_body(&mut self, data: &[u8], flash_id: u8) -> Result<()> {
204 tracing::debug!("Downloading image body...");
205
206 let body_header = [DfuCommandType::ImageBody as u8, flash_id];
207 let mut offset = Self::HDR_SIZE;
208 let mut chunk_count = 0;
209
210 while offset < data.len() {
211 let remaining = data.len() - offset;
212 let chunk_size = std::cmp::min(remaining, Self::CHUNK_OVERHEAD + Self::BLOCK_SIZE);
213
214 tracing::trace!(
215 "Sending chunk {}: offset={}, size={}",
216 chunk_count,
217 offset,
218 chunk_size
219 );
220
221 let total_len = 2 + chunk_size;
222 self.send_dfu_command(total_len, Some(10))?;
223 self.send_dfu_data(&body_header, &data[offset..offset + chunk_size], None)?;
224
225 tracing::trace!("Waiting for chunk {} response...", chunk_count);
226 self.wait_for_ok_response(3000)?;
227
228 offset += chunk_size;
229 chunk_count += 1;
230 }
231
232 tracing::debug!("Image body downloaded successfully: {} chunks", chunk_count);
233 Ok(())
234 }
235
236 fn download_image_end(&mut self, flash_id: u8) -> Result<()> {
238 tracing::debug!("Sending image end marker...");
239
240 let end_header = [DfuCommandType::End as u8, flash_id];
241
242 self.send_dfu_command(2, Some(10))?;
243 self.send_dfu_data(&end_header, &[], None)?;
244
245 tracing::debug!("Waiting for image end response...");
246 self.wait_for_ok_response(5000)?;
247
248 tracing::debug!("Image end marker sent successfully");
249 Ok(())
250 }
251
252 fn wait_for_ok_response(&mut self, timeout_ms: u64) -> Result<()> {
254 use std::io::Read;
255
256 let mut buffer = Vec::new();
257 let start_time = std::time::SystemTime::now();
258 let mut last_log_time = start_time;
259
260 tracing::trace!("Waiting for OK response with timeout: {}ms", timeout_ms);
261
262 loop {
263 let elapsed = start_time.elapsed().unwrap().as_millis() as u64;
264 if elapsed > timeout_ms {
265 let response_str = String::from_utf8_lossy(&buffer);
266 tracing::error!(
267 "Timeout waiting for OK response after {}ms. Received: '{}'",
268 elapsed,
269 response_str
270 );
271 return Err(std::io::Error::new(
272 std::io::ErrorKind::TimedOut,
273 format!("Timeout waiting for OK response: {}", response_str),
274 )
275 .into());
276 }
277
278 if elapsed > 0
280 && elapsed.is_multiple_of(1000)
281 && start_time.elapsed().unwrap()
282 > last_log_time.elapsed().unwrap() + Duration::from_secs(1)
283 {
284 tracing::trace!("Still waiting for response... elapsed: {}ms", elapsed);
285 last_log_time = std::time::SystemTime::now();
286 }
287
288 let mut byte = [0];
289 if self.port.read_exact(&mut byte).is_ok() {
290 buffer.push(byte[0]);
291
292 if buffer.windows(2).any(|window| window == b"OK") {
294 let response_str = String::from_utf8_lossy(&buffer);
295 tracing::trace!(
296 "Received OK response after {}ms: '{}'",
297 elapsed,
298 response_str
299 );
300 return Ok(());
301 }
302
303 if buffer.windows(4).any(|window| window == b"Fail") {
305 let response_str = String::from_utf8_lossy(&buffer);
306 tracing::error!(
307 "Received Fail response after {}ms: '{}'",
308 elapsed,
309 response_str
310 );
311 return Err(std::io::Error::other(format!(
312 "Received Fail response: {}",
313 response_str
314 ))
315 .into());
316 }
317
318 if buffer.len() > 1024 {
320 let response_str = String::from_utf8_lossy(&buffer);
321 tracing::warn!(
322 "Response buffer too large ({}), truncating. Content: '{}'",
323 buffer.len(),
324 response_str
325 );
326 buffer.drain(..512); }
328 }
329 }
330 }
331}
332
333impl SifliTool for SF32LB58Tool {
334 fn create_tool(base: SifliToolBase) -> Box<dyn SifliTool> {
335 let mut port = serialport::new(&base.port_name, 1000000)
336 .timeout(Duration::from_secs(5))
337 .open()
338 .unwrap();
339 port.write_request_to_send(false).unwrap();
340 std::thread::sleep(Duration::from_millis(100));
341
342 let mut tool = Box::new(Self { base, port });
343 if tool.base.before.should_download_stub() {
344 tool.download_stub().expect("Failed to download stub");
345 }
346 tool
347 }
348}
349
350impl SifliToolTrait for SF32LB58Tool {
351 fn port(&mut self) -> &mut Box<dyn SerialPort> {
352 &mut self.port
353 }
354
355 fn base(&self) -> &SifliToolBase {
356 &self.base
357 }
358
359 fn set_speed(&mut self, _baud: u32) -> Result<()> {
360 todo!("SF32LB58Tool::set_speed not implemented yet")
361 }
362
363 fn soft_reset(&mut self) -> Result<()> {
364 use crate::reset::Reset;
365 Reset::soft_reset(self)
366 }
367}