pub const DFU_PID: u16 = 0xFF06;
pub const DFU_REPORT_ID_OUTPUT: u8 = 0xA2;
#[allow(dead_code)]
pub const DFU_REPORT_ID_INPUT: u8 = 0xA1;
pub const DFU_OUTPUT_MAX_SIZE: usize = 256;
pub const DFU_INPUT_MAX_SIZE: usize = 64;
pub const DFU_DATA_PAYLOAD_MAX: usize = 250;
pub const FLAG_PREPARE: u8 = 0xFF;
pub const FLAG_START: u8 = 0x01;
pub const FLAG_DATA: u8 = 0x02;
pub const FLAG_END: u8 = 0x00;
pub const RESULT_SUCCESS: u8 = 0x00;
#[allow(dead_code)]
pub const RESULT_FAIL: u8 = 0xFF;
pub const WAIT_DFU_DEVICE_TIMEOUT_SECS: u64 = 10;
pub const WAIT_NORMAL_DEVICE_TIMEOUT_SECS: u64 = 15;
pub const DFU_RW_TIMEOUT_MS: i32 = 3000;
pub const DFU_PREPARE_TIMEOUT_MS: i32 = 8000;
pub const DFU_END_TIMEOUT_MS: i32 = 5000;
pub fn compute_checksum(data: &[u8]) -> u16 {
data.iter().fold(0u16, |acc, &b| acc.wrapping_add(b as u16))
}
pub struct DfuPacketEncoder;
impl DfuPacketEncoder {
pub fn prepare(total_length: u32) -> Vec<u8> {
let mut buf = vec![0u8; DFU_OUTPUT_MAX_SIZE];
buf[0] = DFU_REPORT_ID_OUTPUT;
buf[1] = FLAG_PREPARE;
buf[2..6].copy_from_slice(&total_length.to_le_bytes());
let cs = compute_checksum(&total_length.to_le_bytes());
buf[6..8].copy_from_slice(&cs.to_le_bytes());
buf
}
pub fn start() -> Vec<u8> {
let mut buf = vec![0u8; DFU_OUTPUT_MAX_SIZE];
buf[0] = DFU_REPORT_ID_OUTPUT;
buf[1] = FLAG_START;
buf
}
pub fn data(payload: &[u8]) -> anyhow::Result<Vec<u8>> {
if payload.len() > DFU_DATA_PAYLOAD_MAX {
return Err(anyhow::anyhow!(
"DATA 载荷超长: {} > {}",
payload.len(),
DFU_DATA_PAYLOAD_MAX
));
}
let mut buf = vec![0u8; DFU_OUTPUT_MAX_SIZE];
buf[0] = DFU_REPORT_ID_OUTPUT;
buf[1] = FLAG_DATA;
let packet_size = payload.len() as u16;
buf[2..4].copy_from_slice(&packet_size.to_le_bytes());
let cs = compute_checksum(payload);
buf[4..6].copy_from_slice(&cs.to_le_bytes());
buf[6..6 + payload.len()].copy_from_slice(payload);
Ok(buf)
}
pub fn end() -> Vec<u8> {
let mut buf = vec![0u8; DFU_OUTPUT_MAX_SIZE];
buf[0] = DFU_REPORT_ID_OUTPUT;
buf[1] = FLAG_END;
buf
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct DfuResponse {
pub flag: u8,
pub packet_size: u16,
pub checksum: u16,
pub result: u8,
pub total_written: u32,
}
impl DfuResponse {
pub fn parse(data: &[u8]) -> anyhow::Result<Self> {
if data.len() < 10 {
return Err(anyhow::anyhow!("DFU 响应长度不足: {} < 10", data.len()));
}
let flag = data[0];
let packet_size = u16::from_le_bytes([data[1], data[2]]);
let checksum = u16::from_le_bytes([data[3], data[4]]);
let result = data[5];
let total_written = u32::from_le_bytes([data[6], data[7], data[8], data[9]]);
Ok(Self {
flag,
packet_size,
checksum,
result,
total_written,
})
}
pub fn is_success(&self) -> bool {
self.result == RESULT_SUCCESS
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_checksum_empty() {
assert_eq!(compute_checksum(&[]), 0);
}
#[test]
fn test_checksum_simple() {
assert_eq!(compute_checksum(&[0x01, 0x02, 0x03]), 6);
}
#[test]
fn test_checksum_overflow() {
let data = vec![0xFF; 257]; let cs = compute_checksum(&data);
assert_eq!(cs, 255u16.wrapping_mul(257));
}
#[test]
fn test_prepare_packet() {
let packet = DfuPacketEncoder::prepare(0x10000);
assert_eq!(packet.len(), DFU_OUTPUT_MAX_SIZE);
assert_eq!(packet[0], DFU_REPORT_ID_OUTPUT);
assert_eq!(packet[1], FLAG_PREPARE);
assert_eq!(&packet[2..6], &0x10000u32.to_le_bytes());
}
#[test]
fn test_start_packet() {
let packet = DfuPacketEncoder::start();
assert_eq!(packet[0], DFU_REPORT_ID_OUTPUT);
assert_eq!(packet[1], FLAG_START);
}
#[test]
fn test_end_packet() {
let packet = DfuPacketEncoder::end();
assert_eq!(packet[0], DFU_REPORT_ID_OUTPUT);
assert_eq!(packet[1], FLAG_END);
}
#[test]
fn test_data_packet() {
let payload = vec![0xAB; 100];
let packet = DfuPacketEncoder::data(&payload).unwrap();
assert_eq!(packet[0], DFU_REPORT_ID_OUTPUT);
assert_eq!(packet[1], FLAG_DATA);
assert_eq!(u16::from_le_bytes([packet[2], packet[3]]), 100);
assert_eq!(&packet[6..106], &payload[..]);
}
#[test]
fn test_data_packet_at_max_payload() {
let payload = vec![0u8; DFU_DATA_PAYLOAD_MAX];
assert!(DfuPacketEncoder::data(&payload).is_ok());
}
#[test]
fn test_data_packet_too_large() {
let payload = vec![0u8; DFU_DATA_PAYLOAD_MAX + 1];
assert!(DfuPacketEncoder::data(&payload).is_err());
}
#[test]
fn test_response_parse() {
let mut data = vec![0u8; 64];
data[0] = FLAG_DATA;
data[1..3].copy_from_slice(&100u16.to_le_bytes());
data[3..5].copy_from_slice(&0x1234u16.to_le_bytes());
data[5] = RESULT_SUCCESS;
data[6..10].copy_from_slice(&5000u32.to_le_bytes());
let resp = DfuResponse::parse(&data).unwrap();
assert_eq!(resp.flag, FLAG_DATA);
assert_eq!(resp.packet_size, 100);
assert_eq!(resp.checksum, 0x1234);
assert_eq!(resp.result, RESULT_SUCCESS);
assert_eq!(resp.total_written, 5000);
assert!(resp.is_success());
}
#[test]
fn test_response_parse_fail() {
let mut data = vec![0u8; 64];
data[5] = RESULT_FAIL;
let resp = DfuResponse::parse(&data).unwrap();
assert!(!resp.is_success());
}
#[test]
fn test_response_parse_too_short() {
let data = vec![0u8; 9];
assert!(DfuResponse::parse(&data).is_err());
}
}