use crate::constants::{
CF_TYPE, FC_TYPE, FF_ESCAPE, FF_MAX_LEN_CLASSIC, FF_TYPE, NIBBLE_MASK, SF_TYPE, TYPE_MASK,
};
use crate::error::IsoTpError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FlowStatus {
ContinueToSend,
Wait,
Overflow,
}
impl TryFrom<u8> for FlowStatus {
type Error = IsoTpError;
fn try_from(nibble: u8) -> Result<Self, Self::Error> {
match nibble & NIBBLE_MASK {
0x0 => Ok(Self::ContinueToSend),
0x1 => Ok(Self::Wait),
0x2 => Ok(Self::Overflow),
other => Err(IsoTpError::UnknownFlowStatus(other)),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PciFrame<'a> {
SingleFrame {
len: u8,
data: &'a [u8],
},
FirstFrame {
total_len: u32,
data: &'a [u8],
},
ConsecutiveFrame {
sequence_number: u8,
data: &'a [u8],
},
FlowControl {
status: FlowStatus,
block_size: u8,
st_min: u8,
},
}
impl<'a> PciFrame<'a> {
pub fn parse(pci_bytes: &'a [u8]) -> Result<Self, IsoTpError> {
if pci_bytes.is_empty() {
return Err(IsoTpError::FrameTooShort { actual: 0 });
}
let pci_byte = pci_bytes[0];
let frame_type = pci_byte & TYPE_MASK;
let lower_nibble = pci_byte & NIBBLE_MASK;
match frame_type {
x if x == SF_TYPE => {
let (len, data) = if lower_nibble == 0 {
if pci_bytes.len() < 2 {
return Err(IsoTpError::FrameTooShort {
actual: pci_bytes.len(),
});
}
let len = pci_bytes[1];
(len, &pci_bytes[2..])
} else {
(lower_nibble, &pci_bytes[1..])
};
if len == 0 {
return Err(IsoTpError::EmptySingleFrame);
}
Ok(PciFrame::SingleFrame { len, data })
}
x if x == FF_TYPE => {
if pci_bytes.len() < 2 {
return Err(IsoTpError::FrameTooShort {
actual: pci_bytes.len(),
});
}
let raw_len = ((lower_nibble as u16) << 8) | pci_bytes[1] as u16;
let (total_len, data) = if raw_len == FF_ESCAPE {
if pci_bytes.len() < 6 {
return Err(IsoTpError::FrameTooShort {
actual: pci_bytes.len(),
});
}
let len = u32::from_be_bytes([
pci_bytes[2],
pci_bytes[3],
pci_bytes[4],
pci_bytes[5],
]);
(len, &pci_bytes[6..])
} else {
(raw_len as u32, &pci_bytes[2..])
};
if total_len == 0 {
return Err(IsoTpError::InvalidLength);
}
Ok(PciFrame::FirstFrame { total_len, data })
}
x if x == CF_TYPE => {
if pci_bytes.len() < 2 {
return Err(IsoTpError::FrameTooShort {
actual: pci_bytes.len(),
});
}
Ok(PciFrame::ConsecutiveFrame {
sequence_number: lower_nibble,
data: &pci_bytes[1..],
})
}
x if x == FC_TYPE => {
if pci_bytes.len() < 3 {
return Err(IsoTpError::FrameTooShort {
actual: pci_bytes.len(),
});
}
let status = FlowStatus::try_from(lower_nibble)?;
Ok(PciFrame::FlowControl {
status,
block_size: pci_bytes[1],
st_min: pci_bytes[2],
})
}
other => Err(IsoTpError::UnknownFrameType(other >> 4)),
}
}
pub fn encode_header(&self, buf: &mut [u8]) -> Result<usize, IsoTpError> {
match self {
PciFrame::SingleFrame { len, .. } => {
let needed = if *len > 0x0F { 2 } else { 1 };
if buf.len() < needed {
return Err(IsoTpError::FrameTooShort { actual: buf.len() });
}
if *len > 0x0F {
buf[0] = SF_TYPE;
buf[1] = *len;
Ok(2)
} else {
buf[0] = SF_TYPE | (len & NIBBLE_MASK);
Ok(1)
}
}
PciFrame::FirstFrame { total_len, .. } => {
let needed = if *total_len > FF_MAX_LEN_CLASSIC {
6
} else {
2
};
if buf.len() < needed {
return Err(IsoTpError::FrameTooShort { actual: buf.len() });
}
if *total_len > FF_MAX_LEN_CLASSIC {
buf[0] = FF_TYPE;
buf[1] = 0x00;
buf[2..6].copy_from_slice(&total_len.to_be_bytes());
Ok(6)
} else {
let high = ((*total_len >> 8) as u8) & NIBBLE_MASK;
let low = (*total_len & 0xFF) as u8;
buf[0] = FF_TYPE | high;
buf[1] = low;
Ok(2)
}
}
PciFrame::ConsecutiveFrame {
sequence_number, ..
} => {
if buf.is_empty() {
return Err(IsoTpError::FrameTooShort { actual: 0 });
}
buf[0] = CF_TYPE | (sequence_number & NIBBLE_MASK);
Ok(1)
}
PciFrame::FlowControl {
status,
block_size,
st_min,
} => {
if buf.len() < 3 {
return Err(IsoTpError::FrameTooShort { actual: buf.len() });
}
let status_nibble = match status {
FlowStatus::ContinueToSend => 0x0,
FlowStatus::Wait => 0x1,
FlowStatus::Overflow => 0x2,
};
buf[0] = FC_TYPE | status_nibble;
buf[1] = *block_size;
buf[2] = *st_min;
Ok(3)
}
}
}
}