use core::sync::atomic::{AtomicU16, Ordering};
use defmt::Format;
use num_enum::TryFromPrimitive;
use crate::{
EspError,
ble::HciPkt,
copy_le, parse_le,
rpc::{EndpointType, RpcEndpoint},
transport::{PacketType, RPC_EP_NAME_RSP, compute_checksum},
};
pub(crate) const PL_HEADER_SIZE: usize = 12;
const TLV_HEADER_SIZE: usize = 6;
pub(crate) const TLV_SIZE: usize = TLV_HEADER_SIZE + RPC_EP_NAME_RSP.len();
pub(crate) const CRC_SIZE: usize = 2;
pub const HEADER_SIZE: usize = PL_HEADER_SIZE + TLV_SIZE;
static SEQ_NUM: AtomicU16 = AtomicU16::new(0);
#[derive(Clone, Copy, PartialEq, TryFromPrimitive, Format)]
#[repr(u8)]
pub enum InterfaceType {
Invalid = 0,
Sta = 1,
Ap = 2,
Serial = 3,
Hci = 4,
Priv = 5,
Test = 6,
Eth = 7,
Max = 8,
}
#[derive(Clone, Copy, PartialEq, TryFromPrimitive)]
#[repr(u8)]
pub(crate) enum Module {
Ctrl = 0x00,
Wifi = 0x01,
Ble = 0x02,
}
#[derive(Format)]
pub struct PayloadHeader {
pub if_type: InterfaceType, pub if_num: u8, pub flags: u8,
pub len: u16,
pub offset: u16,
pub checksum: u16,
pub seq_num: u16,
pub throttle_cmd: u8,
pub pkt_type: PacketType,
}
impl PayloadHeader {
pub fn new(
if_type: InterfaceType,
if_num: u8,
pkt_type: PacketType,
payload_len: usize,
) -> Self {
Self {
if_type,
if_num,
flags: 0,
len: payload_len as u16,
offset: PL_HEADER_SIZE as u16,
checksum: 0,
seq_num: SEQ_NUM.fetch_add(1, Ordering::SeqCst),
throttle_cmd: 0,
pkt_type,
}
}
pub fn to_bytes(&self) -> [u8; PL_HEADER_SIZE] {
let mut buf = [0; PL_HEADER_SIZE];
buf[0] = (self.if_num << 4) | ((self.if_type as u8) & 0x0F);
buf[1] = self.flags;
copy_le!(buf, self.len, 2..4);
copy_le!(buf, self.offset, 4..6);
copy_le!(buf, self.checksum, 6..8);
copy_le!(buf, self.seq_num, 8..10);
buf[10] = self.throttle_cmd;
buf[11] = self.pkt_type.val();
buf
}
pub fn from_bytes(buf: &[u8]) -> Result<Self, EspError> {
let if_type = (buf[0] & 0x0F)
.try_into()
.map_err(|_| EspError::InvalidData)?;
let if_num = (buf[0] >> 4) & 0x0F;
let flags = buf[1];
let len = parse_le!(buf, u16, 2..4);
let offset = parse_le!(buf, u16, 4..6);
let checksum = parse_le!(buf, u16, 6..8);
let seq_num = parse_le!(buf, u16, 8..10);
let throttle_cmd = buf[10] & 3;
let pkt_type = PacketType::from_byte(buf[11])?;
Ok(Self {
if_type,
if_num,
flags,
len,
offset,
checksum,
seq_num,
throttle_cmd,
pkt_type,
})
}
}
pub(crate) fn build_frame_wifi(out: &mut [u8], payload: &[u8]) -> usize {
let payload_len = payload.len();
let endpoint_value = RpcEndpoint::CtrlResp.as_bytes();
let endpoint_len = endpoint_value.len() as u16;
let hdr = PayloadHeader::new(
InterfaceType::Serial,
0,
PacketType::None,
payload_len + TLV_SIZE,
);
out[..PL_HEADER_SIZE].copy_from_slice(&hdr.to_bytes());
let mut i = PL_HEADER_SIZE;
out[i] = EndpointType::EndpointName as _;
i += 1;
copy_le!(out, endpoint_len, i..i + 2);
i += 2;
out[i..i + endpoint_len as usize].copy_from_slice(endpoint_value);
i += endpoint_len as usize;
out[i] = EndpointType::Data as _;
i += 1;
copy_le!(out, payload_len as u16, i..i + 2);
i += 2;
out[i..i + payload_len].copy_from_slice(payload);
i += payload_len;
let pl_checksum = compute_checksum(&out[..i]);
copy_le!(out, pl_checksum, 6..8);
i
}
pub fn build_frame_ble(out: &mut [u8], pkt_type: HciPkt, hci_payload: &[u8]) -> usize {
let payload_len = hci_payload.len();
let packet_type = PacketType::None;
let mut hdr = PayloadHeader::new(InterfaceType::Hci, 0, packet_type, payload_len);
hdr.pkt_type = PacketType::Hci(pkt_type);
out[..PL_HEADER_SIZE].copy_from_slice(&hdr.to_bytes());
let mut i = PL_HEADER_SIZE;
out[i..i + payload_len].copy_from_slice(hci_payload);
i += payload_len;
let pl_checksum = compute_checksum(&out[..i]);
copy_le!(out, pl_checksum, 6..8);
i
}