use crate::{
link::{self, Link, LinkError},
serial::Serial,
timer::Timer,
};
const EVEN_4BIT_PATTERN: u8 = 0b1010;
const ODD_4BIT_PATTERN: u8 = 0b0101;
const EVEN_EVEN_8BIT_PATTERN: u8 = (EVEN_4BIT_PATTERN << 4) | EVEN_4BIT_PATTERN;
const EVEN_ODD_8BIT_PATTERN: u8 = (EVEN_4BIT_PATTERN << 4) | ODD_4BIT_PATTERN;
const ODD_EVEN_8BIT_PATTERN: u8 = (ODD_4BIT_PATTERN << 4) | EVEN_4BIT_PATTERN;
const ODD_ODD_8BIT_PATTERN: u8 = (ODD_4BIT_PATTERN << 4) | ODD_4BIT_PATTERN;
#[must_use]
pub(crate) struct Packet<'a> {
sequence: Sequence,
acknowledge: Acknowledge,
content: PacketContent<'a>,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub(crate) enum Sequence {
Even,
Odd,
}
impl Sequence {
pub(crate) fn toggled(&self) -> Self {
match self {
Sequence::Even => Sequence::Odd,
Sequence::Odd => Sequence::Even,
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub(crate) enum Acknowledge {
Ack,
Nack,
}
#[derive(Clone, Copy)]
pub(crate) enum PacketContent<'a> {
SendRequest { data_len: u16, session_num: u16 },
SendClearance { session_num: u16 },
Data { buffer: Option<&'a [u8]> },
Reset,
}
#[derive(Clone, Copy, PartialEq, Debug)]
enum PacketType {
SendRequest,
SendClearance,
Data,
Reset,
}
const DATA_PACKET_OVERHEAD: usize = 6;
pub(crate) const MAX_DATA_PACKET_PAYLOAD_SIZE: usize =
link::MAX_FRAME_PAYLOAD_SIZE - DATA_PACKET_OVERHEAD;
pub(crate) struct Scratchpad {
header_buf: [u8; 2],
crc_buf: [u8; 4],
backup_buf: [u8; 4],
}
impl Scratchpad {
pub(crate) const fn new() -> Self {
Self {
header_buf: [0u8; 2],
crc_buf: [0u8; 4],
backup_buf: [0u8; 4],
}
}
}
pub(crate) enum PacketError<RE, WE> {
SerialReadErr(RE),
SerialWriteErr(WE),
Timeout,
Clobbered,
NoBuffer,
}
impl<RE, WE> From<LinkError<RE, WE>> for PacketError<RE, WE> {
fn from(le: LinkError<RE, WE>) -> Self {
match le {
LinkError::SerialReadErr(e) => PacketError::SerialReadErr(e),
LinkError::SerialWriteErr(e) => PacketError::SerialWriteErr(e),
LinkError::Timeout => PacketError::Timeout,
LinkError::Overrun => PacketError::Clobbered,
}
}
}
impl<'a> Packet<'a> {
pub(crate) fn build_send_request(data_len: u16, session_num: u16) -> Self {
Self {
sequence: Sequence::Even,
acknowledge: Acknowledge::Ack,
content: PacketContent::SendRequest {
data_len,
session_num,
},
}
}
pub(crate) fn build_send_clearance(session_num: u16) -> Self {
Self {
sequence: Sequence::Even,
acknowledge: Acknowledge::Ack,
content: PacketContent::SendClearance { session_num },
}
}
pub(crate) fn build_data(sequence: Sequence, buffer: &'a [u8]) -> Self {
Self {
sequence,
acknowledge: Acknowledge::Ack,
content: PacketContent::Data {
buffer: Some(buffer),
},
}
}
pub(crate) fn build_ack(sequence: Sequence) -> Self {
Self {
sequence,
acknowledge: Acknowledge::Ack,
content: PacketContent::Data { buffer: None },
}
}
pub(crate) fn build_nack(sequence: Sequence) -> Self {
Self {
sequence,
acknowledge: Acknowledge::Nack,
content: PacketContent::Data { buffer: None },
}
}
pub(crate) fn build_reset() -> Self {
Self {
sequence: Sequence::Even,
acknowledge: Acknowledge::Ack,
content: PacketContent::Reset,
}
}
pub(crate) fn send<S, T>(
&self,
link: &mut Link<S, T>,
) -> Result<(), PacketError<S::ReadError, S::WriteError>>
where
S: Serial,
T: Timer,
{
match self.content {
PacketContent::SendRequest {
data_len,
session_num,
} => link.send_frame(&[
&self.header_to_le_bytes(),
&data_len.to_le_bytes(),
&session_num.to_le_bytes(),
&Self::get_checksum(&[&data_len.to_le_bytes(), &session_num.to_le_bytes()]),
]),
PacketContent::SendClearance { session_num } => link.send_frame(&[
&self.header_to_le_bytes(),
&session_num.to_le_bytes(),
&Self::get_checksum(&[&session_num.to_le_bytes()]),
]),
PacketContent::Data { buffer } => match buffer {
Some(buffer) => link.send_frame(&[
&self.header_to_le_bytes(),
buffer,
&Self::get_checksum(&[buffer]),
]),
None => link.send_frame(&[&self.header_to_le_bytes()]),
},
PacketContent::Reset => link.send_frame(&[&self.header_to_le_bytes()]),
}
.map_err(|e| e.into())
}
pub(crate) fn receive<'b, S, T>(
link: &mut Link<S, T>,
mut client_buf: Option<&'a mut [u8]>,
timeout_ms: u32,
scratchpad: &'b mut Scratchpad,
) -> Result<Self, PacketError<S::ReadError, S::WriteError>>
where
'b: 'a,
S: Serial,
T: Timer,
{
let (active_buf, backup_is_active) =
Self::pick_buffer(&mut client_buf, &mut scratchpad.backup_buf);
let byte_cnt = link.receive_frame_with_timeout(
&mut [
&mut scratchpad.header_buf,
active_buf,
&mut scratchpad.crc_buf,
],
timeout_ms,
)?;
if !(byte_cnt == 2 || byte_cnt >= 7) {
return Err(PacketError::Clobbered);
}
if byte_cnt != 2 {
let offset = (active_buf.len() + scratchpad.crc_buf.len())
.saturating_sub(byte_cnt - scratchpad.header_buf.len());
if offset > 0 {
for idx in (0..=3).rev() {
if idx >= offset {
scratchpad.crc_buf[idx] = scratchpad.crc_buf[idx - offset];
} else {
scratchpad.crc_buf[idx] = active_buf[active_buf.len() - (offset - idx)];
}
}
}
}
let (sequence, acknowledge, packet_type) = Self::parse_header::<S>(&scratchpad.header_buf)?;
match packet_type {
PacketType::SendRequest => Self::parse_send_request::<S>(
byte_cnt,
sequence,
acknowledge,
active_buf[0..4].try_into().unwrap(),
&scratchpad.crc_buf,
),
PacketType::SendClearance => Self::parse_send_clearance::<S>(
byte_cnt,
sequence,
acknowledge,
active_buf[0..2].try_into().unwrap(),
&scratchpad.crc_buf,
),
PacketType::Data => Self::parse_data::<S>(
byte_cnt,
sequence,
acknowledge,
client_buf,
active_buf,
&scratchpad.crc_buf,
backup_is_active,
),
PacketType::Reset => Self::parse_reset::<S>(byte_cnt, sequence, acknowledge),
}
}
pub(crate) fn get_sequence(&self) -> Sequence {
self.sequence
}
pub(crate) fn get_acknowledge(&self) -> Acknowledge {
self.acknowledge
}
pub(crate) fn get_content(&self) -> PacketContent {
self.content
}
}
impl<'a> Packet<'a> {
fn header_to_le_bytes(&self) -> [u8; 2] {
let sequence = match self.sequence {
Sequence::Even => EVEN_4BIT_PATTERN,
Sequence::Odd => ODD_4BIT_PATTERN,
};
let acknowledge = match self.acknowledge {
Acknowledge::Ack => EVEN_4BIT_PATTERN,
Acknowledge::Nack => ODD_4BIT_PATTERN,
};
let content_type = match &self.content {
PacketContent::SendRequest { .. } => EVEN_EVEN_8BIT_PATTERN,
PacketContent::SendClearance { .. } => EVEN_ODD_8BIT_PATTERN,
PacketContent::Data { .. } => ODD_EVEN_8BIT_PATTERN,
PacketContent::Reset => ODD_ODD_8BIT_PATTERN,
};
[(sequence << 4) | acknowledge, content_type]
}
fn parse_header<S>(
header_buf: &[u8; 2],
) -> Result<(Sequence, Acknowledge, PacketType), PacketError<S::ReadError, S::WriteError>>
where
S: Serial,
{
let sequence = header_buf[0] >> 4;
let acknowledge = header_buf[0] & 0xf;
let packet_type = header_buf[1];
let sequence = match sequence {
EVEN_4BIT_PATTERN => Sequence::Even,
ODD_4BIT_PATTERN => Sequence::Odd,
_ => return Err(PacketError::Clobbered),
};
let acknowledge = match acknowledge {
EVEN_4BIT_PATTERN => Acknowledge::Ack,
ODD_4BIT_PATTERN => Acknowledge::Nack,
_ => return Err(PacketError::Clobbered),
};
let packet_type = match packet_type {
EVEN_EVEN_8BIT_PATTERN => PacketType::SendRequest,
EVEN_ODD_8BIT_PATTERN => PacketType::SendClearance,
ODD_EVEN_8BIT_PATTERN => PacketType::Data,
ODD_ODD_8BIT_PATTERN => PacketType::Reset,
_ => return Err(PacketError::Clobbered),
};
Ok((sequence, acknowledge, packet_type))
}
fn parse_send_request<'b, 'c, S>(
byte_cnt: usize,
sequence: Sequence,
acknowledge: Acknowledge,
buffer: &'b [u8; 4],
crc_buf: &'c [u8; 4],
) -> Result<Self, PacketError<S::ReadError, S::WriteError>>
where
S: Serial,
{
if byte_cnt != 10 {
return Err(PacketError::Clobbered);
}
let data_len = u16::from_le_bytes(buffer[0..2].try_into().unwrap());
let session_num = u16::from_le_bytes(buffer[2..4].try_into().unwrap());
if *crc_buf != Self::get_checksum(&[&data_len.to_le_bytes(), &session_num.to_le_bytes()]) {
return Err(PacketError::Clobbered);
}
if sequence != Sequence::Even || acknowledge != Acknowledge::Ack {
return Err(PacketError::Clobbered);
}
Ok(Self {
sequence,
acknowledge,
content: PacketContent::SendRequest {
data_len,
session_num,
},
})
}
fn parse_send_clearance<'b, 'c, S>(
byte_cnt: usize,
sequence: Sequence,
acknowledge: Acknowledge,
buffer: &'b [u8; 2],
crc_buf: &'c [u8; 4],
) -> Result<Self, PacketError<S::ReadError, S::WriteError>>
where
S: Serial,
{
if byte_cnt != 8 {
return Err(PacketError::Clobbered);
}
let session_num = u16::from_le_bytes(buffer[0..2].try_into().unwrap());
if *crc_buf != Self::get_checksum(&[&session_num.to_le_bytes()]) {
return Err(PacketError::Clobbered);
}
if sequence != Sequence::Even || acknowledge != Acknowledge::Ack {
return Err(PacketError::Clobbered);
}
Ok(Self {
sequence,
acknowledge,
content: PacketContent::SendClearance { session_num },
})
}
fn parse_data<'c, S>(
byte_cnt: usize,
sequence: Sequence,
acknowledge: Acknowledge,
client_buf: Option<&'a mut [u8]>,
active_buf: &'a [u8],
crc_buf: &'c [u8; 4],
backup_is_active: bool,
) -> Result<Self, PacketError<S::ReadError, S::WriteError>>
where
S: Serial,
{
if byte_cnt == 2 {
return Ok(Packet {
sequence,
acknowledge,
content: PacketContent::Data { buffer: None },
});
}
let final_client_buf: &[u8];
if backup_is_active {
if let Some(client_buf) = client_buf {
client_buf.copy_from_slice(&active_buf[0..client_buf.len()]);
final_client_buf = client_buf;
} else {
return Err(PacketError::NoBuffer);
}
} else {
final_client_buf = active_buf;
}
if byte_cnt != final_client_buf.len() + DATA_PACKET_OVERHEAD {
return Err(PacketError::Clobbered);
}
if *crc_buf != Self::get_checksum(&[&final_client_buf]) {
return Err(PacketError::Clobbered);
}
if acknowledge != Acknowledge::Ack {
return Err(PacketError::Clobbered);
}
Ok(Self {
sequence,
acknowledge,
content: PacketContent::Data {
buffer: Some(final_client_buf),
},
})
}
fn parse_reset<S>(
byte_cnt: usize,
sequence: Sequence,
acknowledge: Acknowledge,
) -> Result<Self, PacketError<S::ReadError, S::WriteError>>
where
S: Serial,
{
if byte_cnt != 2 {
return Err(PacketError::Clobbered);
}
if sequence != Sequence::Even || acknowledge != Acknowledge::Ack {
return Err(PacketError::Clobbered);
}
Ok(Packet {
sequence,
acknowledge,
content: PacketContent::Reset,
})
}
fn pick_buffer<'b>(
client_buf: &mut Option<&'a mut [u8]>,
backup_buf: &'b mut [u8; 4],
) -> (&'a mut [u8], bool)
where
'b: 'a,
{
match client_buf.take() {
Some(buf) => {
if buf.len() >= backup_buf.len() {
(buf, false)
} else {
client_buf.replace(buf);
(backup_buf, true)
}
}
None => (backup_buf, true),
}
}
fn get_checksum(byte_slices: &[&[u8]]) -> [u8; 4] {
let mut hasher = crc32fast::Hasher::new();
for &slice in byte_slices {
hasher.update(slice);
}
hasher.finalize().to_le_bytes()
}
}