use {
crate::protocol::crc::crc16_xmodem,
byteorder::{LittleEndian, WriteBytesExt},
};
pub const FRAME_MAGIC: u32 = 0xDEADBEEF;
pub const FWPKG_MAGIC: u32 = 0xEFBEADDF;
pub const ACK_SUCCESS: u8 = 0x5A;
pub const ACK_FAILURE: u8 = 0x00;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum CommandType {
Handshake = 0xF0,
Ack = 0xE1,
DownloadFlashImage = 0xD2,
DownloadOtpEfuse = 0xC3,
UploadData = 0xB4,
ReadOtpEfuse = 0xA5,
FlashLock = 0x96,
Reset = 0x87,
DownloadFactoryBin = 0x78,
DownloadVersion = 0x69,
SetBaudRate = 0x5A,
DownloadNv = 0x4B,
SwitchDfu = 0x1E,
}
impl CommandType {
pub fn reversed(self) -> u8 {
!(self as u8)
}
pub fn swapped(self) -> u8 {
let cmd = self as u8;
cmd.rotate_right(4)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum ImageType {
Loader = 0,
Normal = 1,
KvNv = 2,
Efuse = 3,
Otp = 4,
FlashBoot = 5,
Factory = 6,
Version = 7,
SecurityA = 8,
SecurityB = 9,
SecurityC = 10,
ProtocolA = 11,
AppsA = 12,
RadioConfig = 13,
Rom = 14,
Emmc = 15,
Database = 16,
FlashBoot3892 = 17,
}
impl From<u32> for ImageType {
fn from(value: u32) -> Self {
match value {
0 => Self::Loader,
2 => Self::KvNv,
3 => Self::Efuse,
4 => Self::Otp,
5 => Self::FlashBoot,
6 => Self::Factory,
7 => Self::Version,
_ => Self::Normal,
}
}
}
#[derive(Debug)]
pub struct SebootFrame {
frame_type: CommandType,
data: Vec<u8>,
}
impl SebootFrame {
pub fn new(frame_type: CommandType) -> Self {
Self {
frame_type,
data: Vec::new(),
}
}
pub fn handshake(baud_rate: u32) -> Self {
let mut frame = Self::new(CommandType::Handshake);
frame
.data
.write_u32::<LittleEndian>(baud_rate)
.expect("Vec<u8> write cannot fail");
frame
.data
.push(8); frame
.data
.push(1); frame
.data
.push(0); frame
.data
.push(0); frame
}
pub fn set_baud_rate(baud_rate: u32) -> Self {
let mut frame = Self::new(CommandType::SetBaudRate);
frame
.data
.write_u32::<LittleEndian>(baud_rate)
.expect("Vec<u8> write cannot fail");
frame
.data
.write_u32::<LittleEndian>(0x0108)
.expect("Vec<u8> write cannot fail");
frame
}
pub fn download_flash_image(addr: u32, len: u32, erase_size: u32, is_rom: bool) -> Self {
let mut frame = Self::new(CommandType::DownloadFlashImage);
frame
.data
.write_u32::<LittleEndian>(addr)
.expect("Vec<u8> write cannot fail");
frame
.data
.write_u32::<LittleEndian>(len)
.expect("Vec<u8> write cannot fail");
frame
.data
.write_u32::<LittleEndian>(erase_size)
.expect("Vec<u8> write cannot fail");
let formal = u8::from(is_rom);
frame
.data
.push(formal);
frame
.data
.push(!formal);
frame
}
#[allow(clippy::unwrap_used)] pub fn download_factory_bin(addr: u32, len: u32, erase_size: u32) -> Self {
let mut frame = Self::new(CommandType::DownloadFactoryBin);
frame
.data
.write_u32::<LittleEndian>(addr)
.unwrap();
frame
.data
.write_u32::<LittleEndian>(len)
.unwrap();
frame
.data
.write_u32::<LittleEndian>(erase_size)
.unwrap();
frame
.data
.push(0x00); frame
.data
.push(0xFF); frame
}
#[allow(clippy::unwrap_used)] pub fn download_nv(addr: u32, len: u32, erase_size: u32, erase_all: bool) -> Self {
let mut frame = Self::new(CommandType::DownloadNv);
frame
.data
.write_u32::<LittleEndian>(addr)
.unwrap();
frame
.data
.write_u32::<LittleEndian>(len)
.unwrap();
frame
.data
.write_u32::<LittleEndian>(erase_size)
.unwrap();
frame
.data
.write_u16::<LittleEndian>(0)
.unwrap(); frame
.data
.write_u16::<LittleEndian>(u16::from(erase_all))
.unwrap(); frame
}
#[allow(clippy::unwrap_used)] pub fn download_otp_efuse(len: u32) -> Self {
let mut frame = Self::new(CommandType::DownloadOtpEfuse);
frame
.data
.write_u32::<LittleEndian>(len)
.unwrap();
frame
}
#[allow(clippy::unwrap_used)] pub fn download_version(len: u32) -> Self {
let mut frame = Self::new(CommandType::DownloadVersion);
frame
.data
.write_u32::<LittleEndian>(len)
.unwrap();
frame
}
#[allow(clippy::unwrap_used)] pub fn upload_data(addr: u32, len: u32) -> Self {
let mut frame = Self::new(CommandType::UploadData);
frame
.data
.write_u32::<LittleEndian>(len)
.unwrap();
frame
.data
.write_u32::<LittleEndian>(addr)
.unwrap();
frame
}
#[allow(clippy::unwrap_used)] pub fn read_otp_efuse(start_bit: u16, bit_width: u16) -> Self {
let mut frame = Self::new(CommandType::ReadOtpEfuse);
frame
.data
.write_u16::<LittleEndian>(start_bit)
.unwrap();
frame
.data
.write_u16::<LittleEndian>(bit_width)
.unwrap();
frame
}
#[allow(clippy::unwrap_used)] pub fn flash_lock(param: u16) -> Self {
let mut frame = Self::new(CommandType::FlashLock);
frame
.data
.write_u16::<LittleEndian>(param)
.unwrap();
frame
}
#[allow(clippy::unwrap_used)] pub fn reset() -> Self {
let mut frame = Self::new(CommandType::Reset);
frame
.data
.write_u16::<LittleEndian>(0)
.unwrap();
frame
}
#[allow(clippy::unwrap_used)] pub fn switch_dfu() -> Self {
let mut frame = Self::new(CommandType::SwitchDfu);
frame
.data
.write_u16::<LittleEndian>(0)
.unwrap();
frame
}
pub fn erase_all() -> Self {
Self::download_flash_image(0, 0, 0xFFFFFFFF, false)
}
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::unwrap_used)] pub fn build(&self) -> Vec<u8> {
let total_len = 10
+ self
.data
.len();
let mut buf = Vec::with_capacity(total_len);
buf.write_u32::<LittleEndian>(FRAME_MAGIC)
.unwrap();
buf.write_u16::<LittleEndian>(total_len as u16)
.unwrap();
buf.push(self.frame_type as u8);
buf.push(
self.frame_type
.reversed(),
);
buf.extend_from_slice(&self.data);
let crc = crc16_xmodem(&buf);
buf.write_u16::<LittleEndian>(crc)
.unwrap();
buf
}
pub fn command_type(&self) -> CommandType {
self.frame_type
}
}
#[derive(Debug)]
pub struct SebootAck {
pub frame_type: u8,
pub result: u8,
pub error_code: u8,
}
impl SebootAck {
pub const MIN_LEN: usize = 12;
pub const HANDSHAKE_ACK: [u8; 12] = [
0xEF, 0xBE, 0xAD, 0xDE, 0x0C, 0x00, 0xE1, 0x1E, 0x5A, 0x00, 0x00, 0x00, ];
pub fn parse(data: &[u8]) -> Option<Self> {
if data.len() < Self::MIN_LEN {
return None;
}
let magic_pos = data
.windows(4)
.position(|w| u32::from_le_bytes([w[0], w[1], w[2], w[3]]) == FRAME_MAGIC)?;
let frame = &data[magic_pos..];
if frame.len() < Self::MIN_LEN {
return None;
}
let frame_type = frame[6];
let result = frame[8];
let error_code = frame[9];
Some(Self {
frame_type,
result,
error_code,
})
}
pub fn is_success(&self) -> bool {
self.result == ACK_SUCCESS
}
pub fn is_handshake_ack(&self) -> bool {
self.frame_type == CommandType::Ack as u8 && self.is_success()
}
}
pub fn contains_handshake_ack(data: &[u8]) -> bool {
data.windows(10)
.any(|w| {
w[0] == 0xEF
&& w[1] == 0xBE
&& w[2] == 0xAD
&& w[3] == 0xDE
&& w[4] == 0x0C
&& w[5] == 0x00
&& w[6] == 0xE1
&& w[7] == 0x1E
&& w[8] == 0x5A
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_command_type_reversed() {
assert_eq!(CommandType::Handshake.reversed(), 0x0F);
assert_eq!(CommandType::DownloadFlashImage.reversed(), 0x2D);
assert_eq!(CommandType::Reset.reversed(), 0x78);
}
#[test]
fn test_command_type_swapped() {
assert_eq!(CommandType::Handshake.swapped(), 0x0F);
assert_eq!(CommandType::Ack.swapped(), 0x1E);
assert_eq!(CommandType::SetBaudRate.swapped(), 0xA5);
assert_eq!(CommandType::Reset.swapped(), 0x78);
}
#[test]
fn test_command_type_reversed_is_bitwise_not() {
let cmds = [
CommandType::Handshake,
CommandType::Ack,
CommandType::DownloadFlashImage,
CommandType::Reset,
CommandType::SetBaudRate,
];
for cmd in cmds {
assert_eq!(cmd.reversed(), !(cmd as u8));
}
}
#[test]
fn test_handshake_frame_length() {
let frame = SebootFrame::handshake(115200);
let data = frame.build();
assert_eq!(data.len(), 18);
assert_eq!(&data[0..4], &[0xEF, 0xBE, 0xAD, 0xDE]);
assert_eq!(&data[4..6], &[0x12, 0x00]); assert_eq!(data[6], 0xF0);
assert_eq!(data[7], 0x0F);
}
#[test]
fn test_handshake_frame_baud_rate() {
let frame = SebootFrame::handshake(921600);
let data = frame.build();
let baud = u32::from_le_bytes([data[8], data[9], data[10], data[11]]);
assert_eq!(baud, 921600);
assert_eq!(data[12], 8);
assert_eq!(data[13], 1);
assert_eq!(data[14], 0);
assert_eq!(data[15], 0);
}
#[test]
fn test_download_flash_image_frame() {
let frame = SebootFrame::download_flash_image(0x00800000, 0x1000, 0x1000, false);
let data = frame.build();
assert_eq!(data.len(), 24);
assert_eq!(data[6], 0xD2);
assert_eq!(data[7], 0x2D);
}
#[test]
fn test_download_flash_image_rom_flag() {
let frame_rom = SebootFrame::download_flash_image(0, 0, 0, true);
let data = frame_rom.build();
assert_eq!(data[20], 1);
assert_eq!(data[21], 0xFE);
let frame_normal = SebootFrame::download_flash_image(0, 0, 0, false);
let data = frame_normal.build();
assert_eq!(data[20], 0);
assert_eq!(data[21], 0xFF); }
#[test]
fn test_erase_all_frame() {
let frame = SebootFrame::erase_all();
let data = frame.build();
assert_eq!(&data[16..20], &[0xFF, 0xFF, 0xFF, 0xFF]);
let addr = u32::from_le_bytes([data[8], data[9], data[10], data[11]]);
let len = u32::from_le_bytes([data[12], data[13], data[14], data[15]]);
assert_eq!(addr, 0);
assert_eq!(len, 0);
}
#[test]
fn test_reset_frame() {
let frame = SebootFrame::reset();
let data = frame.build();
assert_eq!(data.len(), 12);
assert_eq!(data[6], 0x87);
assert_eq!(data[7], 0x78);
}
#[test]
fn test_set_baud_rate_frame() {
let frame = SebootFrame::set_baud_rate(921600);
let data = frame.build();
assert_eq!(data[6], CommandType::SetBaudRate as u8);
assert_eq!(data[7], CommandType::SetBaudRate.reversed());
let baud = u32::from_le_bytes([data[8], data[9], data[10], data[11]]);
assert_eq!(baud, 921600);
}
#[test]
fn test_switch_dfu_frame() {
let frame = SebootFrame::switch_dfu();
let data = frame.build();
assert_eq!(data[6], CommandType::SwitchDfu as u8);
}
#[test]
fn test_flash_lock_frame() {
let frame = SebootFrame::flash_lock(0x1234);
let data = frame.build();
assert_eq!(data[6], CommandType::FlashLock as u8);
let param = u16::from_le_bytes([data[8], data[9]]);
assert_eq!(param, 0x1234);
}
#[test]
fn test_upload_data_frame() {
let frame = SebootFrame::upload_data(0x800000, 0x100);
let data = frame.build();
assert_eq!(data[6], CommandType::UploadData as u8);
}
#[test]
fn test_read_otp_efuse_frame() {
let frame = SebootFrame::read_otp_efuse(0x10, 0x20);
let data = frame.build();
assert_eq!(data[6], CommandType::ReadOtpEfuse as u8);
let start = u16::from_le_bytes([data[8], data[9]]);
let width = u16::from_le_bytes([data[10], data[11]]);
assert_eq!(start, 0x10);
assert_eq!(width, 0x20);
}
#[test]
fn test_download_nv_frame() {
let frame = SebootFrame::download_nv(0x1000, 0x200, 0x400, true);
let data = frame.build();
assert_eq!(data[6], CommandType::DownloadNv as u8);
}
#[test]
fn test_download_version_frame() {
let frame = SebootFrame::download_version(0x100);
let data = frame.build();
assert_eq!(data[6], CommandType::DownloadVersion as u8);
}
#[test]
fn test_frame_command_type_getter() {
let frame = SebootFrame::handshake(115200);
assert_eq!(frame.command_type(), CommandType::Handshake);
let frame = SebootFrame::reset();
assert_eq!(frame.command_type(), CommandType::Reset);
}
#[test]
fn test_frame_crc_is_appended() {
let frame = SebootFrame::handshake(115200);
let data = frame.build();
let crc_data = &data[..data.len() - 2];
let expected_crc = crate::protocol::crc::crc16_xmodem(crc_data);
let actual_crc = u16::from_le_bytes([data[data.len() - 2], data[data.len() - 1]]);
assert_eq!(actual_crc, expected_crc);
}
#[test]
fn test_contains_handshake_ack() {
let mut data = vec![0x00, 0x00];
data.extend_from_slice(&SebootAck::HANDSHAKE_ACK[..10]);
data.extend_from_slice(&[0x00, 0x00]);
assert!(contains_handshake_ack(&data));
}
#[test]
fn test_contains_handshake_ack_at_start() {
let mut data = Vec::new();
data.extend_from_slice(&SebootAck::HANDSHAKE_ACK[..10]);
assert!(contains_handshake_ack(&data));
}
#[test]
fn test_contains_handshake_ack_not_found() {
let data = vec![0x00; 20];
assert!(!contains_handshake_ack(&data));
}
#[test]
fn test_contains_handshake_ack_too_short() {
let data = vec![0xEF, 0xBE, 0xAD];
assert!(!contains_handshake_ack(&data));
}
#[test]
fn test_seboot_ack_parse_success() {
let ack = SebootAck::parse(&SebootAck::HANDSHAKE_ACK);
assert!(ack.is_some());
let ack = ack.unwrap();
assert!(ack.is_success());
assert!(ack.is_handshake_ack());
assert_eq!(ack.frame_type, CommandType::Ack as u8);
}
#[test]
fn test_seboot_ack_parse_failure() {
let mut data = SebootAck::HANDSHAKE_ACK;
data[8] = 0x00; let ack = SebootAck::parse(&data).unwrap();
assert!(!ack.is_success());
assert!(!ack.is_handshake_ack());
}
#[test]
fn test_seboot_ack_parse_too_short() {
let data = vec![0x00; 4];
assert!(SebootAck::parse(&data).is_none());
}
#[test]
fn test_seboot_ack_parse_no_magic() {
let data = vec![0x00; 20];
assert!(SebootAck::parse(&data).is_none());
}
#[test]
fn test_seboot_ack_parse_with_prefix() {
let mut data = vec![0xFF; 5];
data.extend_from_slice(&SebootAck::HANDSHAKE_ACK);
let ack = SebootAck::parse(&data);
assert!(ack.is_some());
assert!(
ack.unwrap()
.is_success()
);
}
#[test]
fn test_image_type_from_u32() {
assert_eq!(ImageType::from(0), ImageType::Loader);
assert_eq!(ImageType::from(2), ImageType::KvNv);
assert_eq!(ImageType::from(5), ImageType::FlashBoot);
assert_eq!(ImageType::from(1), ImageType::Normal);
assert_eq!(ImageType::from(999), ImageType::Normal);
}
}