bb_flasher_bcf/
cc1352p7.rs1use std::{io, time::Duration};
11
12use futures::channel::mpsc;
13use serialport::SerialPort;
14use thiserror::Error;
15use tracing::{error, info, warn};
16
17use crate::{
18 Status,
19 helpers::{chan_send, parse_bin},
20};
21
22const ACK: u8 = 0xcc;
23const NACK: u8 = 0x33;
24
25const COMMAND_DOWNLOAD: u8 = 0x21;
26const COMMAND_GET_STATUS: u8 = 0x23;
27const COMMAND_SEND_DATA: u8 = 0x24;
28const COMMAND_RESET: u8 = 0x25;
29const COMMAND_CRC32: u8 = 0x27;
30const COMMAND_BANK_ERASE: u8 = 0x2c;
31
32const COMMAND_MAX_SIZE: u8 = u8::MAX - 3;
33
34const FIRMWARE_SIZE: u32 = 704 * 1024;
35
36type Result<T, E = Error> = std::result::Result<T, E>;
37
38#[derive(Error, Debug)]
39pub enum Error {
41 #[error("Status for failing flash erase or program operation")]
43 FlashFail,
44 #[error("Bootloader sent unexpected response")]
46 UnknownResponse,
47 #[error("Bootloader Responded with Nack")]
49 Nack,
50 #[error("Failed to start Bootloader")]
52 FailedToStartBootloader,
53 #[error("Flashed image is not valid")]
55 InvalidImage,
56 #[error("Failed to open serial port")]
58 FailedToOpenPort,
59 #[error("Aborted before completing")]
61 Aborted,
62 #[error("IO Error: {0}")]
63 IoError(io::Error),
64}
65
66impl From<io::Error> for Error {
67 fn from(value: io::Error) -> Self {
68 Self::IoError(value)
69 }
70}
71
72struct BeagleConnectFreedom<S: SerialPort> {
73 port: S,
74}
75
76impl<S> BeagleConnectFreedom<S>
77where
78 S: SerialPort,
79{
80 fn new(port: S) -> Result<Self> {
81 let mut bcf = BeagleConnectFreedom { port };
82
83 bcf.invoke_bootloader()?;
84 bcf.send_sync()?;
85
86 Ok(bcf)
87 }
88
89 fn wait_for_ack(&mut self) -> Result<()> {
90 let mut buf = [0u8; 1];
91
92 while buf[0] == 0x00 {
93 self.port.read_exact(&mut buf)?;
94 }
95
96 match buf[0] {
97 ACK => Ok(()),
98 NACK => Err(Error::Nack),
99 _ => Err(Error::UnknownResponse),
100 }
101 }
102
103 fn invoke_bootloader(&mut self) -> Result<()> {
104 info!("Invoke Bootloader");
105
106 self.port
107 .set_break()
108 .map_err(|_| Error::FailedToStartBootloader)?;
109 std::thread::sleep(Duration::from_secs(2));
110 self.port
111 .clear_break()
112 .map_err(|_| Error::FailedToStartBootloader)?;
113
114 std::thread::sleep(Duration::from_millis(500));
115 Ok(())
116 }
117
118 fn send_sync(&mut self) -> Result<()> {
119 info!("Send Sync");
120 const PKT: &[u8] = &[0x55, 0x55];
121
122 self.port.write_all(PKT)?;
123
124 self.wait_for_ack()
125 }
126
127 fn crc32(&mut self) -> Result<u32> {
128 let addr = 0u32.to_be_bytes();
129 let size = FIRMWARE_SIZE.to_be_bytes();
130 let read_repeat = 0u32.to_be_bytes();
131 let mut cmd = [0u8; 2];
132 let mut cmd_data = [0u8; 4];
133
134 let checksum: u8 = size
135 .iter()
136 .chain(&[COMMAND_CRC32])
137 .fold(0u8, |acc, t| acc.wrapping_add(*t));
138
139 self.port.write_all(&[15, checksum, COMMAND_CRC32])?;
140 self.port.write_all(&addr)?;
141 self.port.write_all(&size)?;
142 self.port.write_all(&read_repeat)?;
143
144 self.wait_for_ack()?;
145
146 self.port.read_exact(&mut cmd)?;
147 assert_eq!(cmd[0], 6);
148
149 let checksum = cmd[1];
150
151 self.port.read_exact(&mut cmd_data)?;
152 assert_eq!(
153 checksum,
154 cmd_data.iter().fold(0u8, |acc, x| acc.wrapping_add(*x))
155 );
156
157 self.send_ack()?;
158
159 Ok(u32::from_be_bytes(cmd_data))
160 }
161
162 fn send_ack(&mut self) -> Result<(), Error> {
163 const PKT: &[u8] = &[0x00, ACK];
164 self.port.write_all(PKT).map_err(Into::into)
165 }
166
167 fn send_bank_erase(&mut self) -> Result<(), Error> {
168 const CMD: &[u8] = &[3, COMMAND_BANK_ERASE, COMMAND_BANK_ERASE];
169
170 self.port.write_all(CMD)?;
171
172 self.wait_for_ack()?;
173 self.get_status()
174 }
175
176 fn get_status(&mut self) -> Result<(), Error> {
177 const CMD: &[u8] = &[3, COMMAND_GET_STATUS, COMMAND_GET_STATUS];
178 let mut resp = [0u8; 1];
179
180 self.port.write_all(CMD)?;
181
182 self.wait_for_ack()?;
183
184 while resp[0] == 0x00 {
185 self.port.read_exact(&mut resp)?;
186 }
187
188 self.port.read_exact(&mut resp)?;
189 self.port.read_exact(&mut resp)?;
190
191 self.send_ack()?;
192
193 match resp[0] {
194 0x40 => Ok(()),
195 0x41 => panic!("Unknown Command"),
196 0x42 => panic!("Invalid Command"),
197 0x43 => panic!("Invalid Address"),
198 0x44 => Err(Error::FlashFail),
199 _ => Err(Error::UnknownResponse),
200 }
201 }
202
203 fn send_download(&mut self, addr: u32, size: u32) -> Result<(), Error> {
204 let addr = addr.to_be_bytes();
205 let size = size.to_be_bytes();
206
207 let checksum: u8 = addr
208 .into_iter()
209 .chain(size)
210 .chain([COMMAND_DOWNLOAD])
211 .fold(0u8, |acc, t| acc.wrapping_add(t));
212
213 self.port.write_all(&[11, checksum, COMMAND_DOWNLOAD])?;
214 self.port.write_all(&addr)?;
215 self.port.write_all(&size)?;
216
217 self.wait_for_ack()?;
218 self.get_status()
219 }
220
221 fn send_data(&mut self, data: &[u8]) -> Result<usize> {
222 let bytes_to_write = std::cmp::min(data.len(), usize::from(COMMAND_MAX_SIZE));
223
224 let checksum = data[..bytes_to_write]
225 .iter()
226 .chain(&[COMMAND_SEND_DATA])
227 .fold(0u8, |acc, t| acc.wrapping_add(*t));
228
229 self.port
230 .write_all(&[(bytes_to_write + 3) as u8, checksum, COMMAND_SEND_DATA])?;
231 self.port.write_all(&data[..bytes_to_write])?;
232
233 self.wait_for_ack()?;
234 self.get_status()?;
235
236 Ok(bytes_to_write)
237 }
238
239 fn send_reset(&mut self) -> Result<(), Error> {
240 const CMD: &[u8] = &[3, COMMAND_RESET, COMMAND_RESET];
241
242 self.port.write_all(CMD)?;
243 self.wait_for_ack()
244 }
245
246 fn verify(&mut self, crc32: u32) -> Result<bool> {
247 self.crc32().map(|x| x == crc32)
248 }
249}
250
251impl<S> Drop for BeagleConnectFreedom<S>
252where
253 S: SerialPort,
254{
255 fn drop(&mut self) {
256 let _ = self.send_reset();
257 }
258}
259
260const fn progress(off: usize) -> f32 {
261 (off as f32) / (FIRMWARE_SIZE as f32)
262}
263
264fn check_arc(cancel: Option<&std::sync::Weak<()>>) -> Result<()> {
265 match cancel {
266 Some(x) if x.strong_count() == 0 => Err(Error::Aborted),
267 _ => Ok(()),
268 }
269}
270
271pub fn flash(
289 firmware: &[u8],
290 port: &str,
291 verify: bool,
292 mut chan: Option<mpsc::Sender<Status>>,
293 cancel: Option<std::sync::Weak<()>>,
294) -> Result<()> {
295 let firmware_bin = parse_bin(firmware).map_err(|_| Error::InvalidImage)?;
296
297 chan_send(chan.as_mut(), Status::Preparing);
298
299 let port = serialport::new(port, 115200)
300 .timeout(Duration::from_millis(500))
301 .open_native()
302 .map_err(|_| Error::FailedToOpenPort)?;
303 let mut bcf = BeagleConnectFreedom::new(port)?;
304 info!("BeagleConnectFreedom Connected");
305
306 check_arc(cancel.as_ref())?;
307 chan_send(chan.as_mut(), Status::Flashing(0.0));
308
309 let img_crc32 = crc32fast::hash(
310 &firmware_bin
311 .to_bytes(0..(FIRMWARE_SIZE as usize), Some(0xff))
312 .unwrap(),
313 );
314 if bcf.verify(img_crc32)? {
315 warn!("Skipping flashing same image");
316 return Ok(());
317 }
318
319 check_arc(cancel.as_ref())?;
320 info!("Erase Flash");
321 bcf.send_bank_erase()?;
322
323 info!("Start Flashing");
324
325 check_arc(cancel.as_ref())?;
326 for (start_address, data) in firmware_bin.segments_list() {
327 let mut offset = 0;
328
329 bcf.send_download(
330 start_address.try_into().unwrap(),
331 data.len().try_into().unwrap(),
332 )?;
333 while offset < data.len() {
334 offset += bcf.send_data(&data[offset..])?;
335
336 chan_send(
337 chan.as_mut(),
338 Status::Flashing(progress(start_address + offset)),
339 );
340 check_arc(cancel.as_ref())?;
341 }
342 }
343
344 let res = if verify {
345 chan_send(chan.as_mut(), Status::Verifying);
346 if bcf.verify(img_crc32)? {
347 info!("Flashing Successful");
348 Ok(())
349 } else {
350 error!("Invalid CRC32 in Flash. The flashed image might be corrupted");
351 Err(Error::InvalidImage)
352 }
353 } else {
354 Ok(())
355 };
356
357 res
358}
359
360pub fn ports() -> std::collections::HashSet<String> {
362 serialport::available_ports()
363 .expect("Unsupported OS")
364 .into_iter()
365 .filter(|x| {
366 if cfg!(target_os = "linux") {
367 match &x.port_type {
368 serialport::SerialPortType::UsbPort(y) => {
369 y.manufacturer.as_deref() == Some("BeagleBoard.org")
370 && y.product.as_deref() == Some("BeagleConnect")
371 }
372 _ => false,
373 }
374 } else {
375 true
376 }
377 })
378 .map(|x| x.port_name)
379 .collect()
380}