use std::sync::atomic::AtomicU16;
pub(crate) static PRIVATE_TX_ID: AtomicU16 = AtomicU16::new(0);
pub(crate) fn get_transaction_id() -> u16 {
PRIVATE_TX_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst)
}
pub const ETP_MESSAGE_HEADER_SIZE: usize = 8;
pub const ETP_MESSAGE_HEADER_STATUS_INDEX: usize = 3;
#[derive(Clone, Debug)]
pub(crate) struct EtpMessageHeader {
pub length: u16,
pub payload_type: EtpPayloadType,
pub status: EtpStatusCodes,
pub transaction_id: u16,
pub operation: EtpOperations,
}
impl EtpMessageHeader {
pub fn new(
payload_length: u16,
status: EtpStatusCodes,
payload_type: EtpPayloadType,
operation: EtpOperations,
) -> Self {
EtpMessageHeader {
length: payload_length + ETP_MESSAGE_HEADER_SIZE as u16,
payload_type,
status,
transaction_id: get_transaction_id(),
operation,
}
}
}
impl Into<Vec<u8>> for EtpMessageHeader {
fn into(self) -> Vec<u8> {
let mut header = Vec::new();
header.extend_from_slice(&self.length.to_le_bytes());
header.push(self.payload_type as u8);
header.push(self.status as u8);
header.extend_from_slice(&self.transaction_id.to_le_bytes());
let op = self.operation as u16;
header.extend_from_slice(&op.to_le_bytes());
header
}
}
impl TryFrom<&[u8]> for EtpMessageHeader {
type Error = &'static str;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
if value.len() != 8 {
return Err("Invalid byte length for ETP message header");
}
let length = u16::from_le_bytes([value[0], value[1]]);
let payload_type = value[2];
let status = EtpStatusCodes::try_from(value[ETP_MESSAGE_HEADER_STATUS_INDEX])?;
let transaction_id = u16::from_le_bytes([value[4], value[5]]);
let operation = u16::from_le_bytes([value[6], value[7]]);
Ok(EtpMessageHeader {
length,
payload_type: EtpPayloadType::try_from(payload_type)
.map_err(|_| "Invalid payload type")?,
status,
transaction_id,
operation: EtpOperations::try_from(operation).map_err(|_| "Invalid operation")?,
})
}
}
#[repr(u16)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum EtpOperations {
GetFirmwareInfo = 0x0000,
Reset = 0x0001,
GetSupportedOperations = 0x0002,
ConfigureTransport = 0x0003,
GetGpioInfo = 0x0100,
GpioInit = 0x0101,
GpioRead = 0x0102,
GpioWrite = 0x0103,
DebugPrint = 0x00db,
}
impl TryFrom<u16> for EtpOperations {
type Error = &'static str;
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
0x0000 => Ok(EtpOperations::GetFirmwareInfo),
0x0001 => Ok(EtpOperations::Reset),
0x0002 => Ok(EtpOperations::GetSupportedOperations),
0x0003 => Ok(EtpOperations::ConfigureTransport),
0x0100 => Ok(EtpOperations::GetGpioInfo),
0x0101 => Ok(EtpOperations::GpioInit),
0x0102 => Ok(EtpOperations::GpioRead),
0x0103 => Ok(EtpOperations::GpioWrite),
0x00db => Ok(EtpOperations::DebugPrint),
_ => Err("Invalid ETP operation"),
}
}
}
#[repr(u8)]
pub(crate) enum FirmwareInfoCommands {
Version = 0x01,
FirmwareVersion = 0x02,
BuildDate = 0x03,
HardwareType = 0x04,
}
impl TryFrom<u8> for FirmwareInfoCommands {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x01 => Ok(FirmwareInfoCommands::Version),
0x02 => Ok(FirmwareInfoCommands::FirmwareVersion),
0x03 => Ok(FirmwareInfoCommands::BuildDate),
0x04 => Ok(FirmwareInfoCommands::HardwareType),
_ => Err("Invalid firmware info command"),
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum ResetType {
ReInit = 0x01,
SoftReset = 0x02,
HardReset = 0x03,
Bootloader = 0x04,
}
impl TryFrom<u8> for ResetType {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x01 => Ok(ResetType::ReInit),
0x02 => Ok(ResetType::SoftReset),
0x03 => Ok(ResetType::HardReset),
0x04 => Ok(ResetType::Bootloader),
_ => Err("Invalid reset type"),
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum EtpPayloadType {
Command = 0x01,
Data = 0x02,
Response = 0x03,
Event = 0x04,
}
impl TryFrom<u8> for EtpPayloadType {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x01 => Ok(EtpPayloadType::Command),
0x02 => Ok(EtpPayloadType::Data),
0x03 => Ok(EtpPayloadType::Response),
0x04 => Ok(EtpPayloadType::Event),
_ => Err("Invalid ETP payload type"),
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum TranportTypes {
Uart = 0x01,
Usb = 0x02,
Wifi = 0x03,
Ble = 0x04,
Bluetooth = 0x05,
TcpIp = 0x06,
}
impl TryFrom<u8> for TranportTypes {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x01 => Ok(TranportTypes::Uart),
0x02 => Ok(TranportTypes::Usb),
0x03 => Ok(TranportTypes::Wifi),
0x04 => Ok(TranportTypes::Ble),
0x05 => Ok(TranportTypes::Bluetooth),
0x06 => Ok(TranportTypes::TcpIp),
_ => Err("Invalid transport type"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum EtpStatusCodes {
Success = 0x00,
InvalidCommand = 0x01,
InvalidParameter = 0x02,
InvalidPort = 0x40,
Failure = 0xFF,
}
impl TryFrom<u8> for EtpStatusCodes {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x00 => Ok(EtpStatusCodes::Success),
0x01 => Ok(EtpStatusCodes::InvalidCommand),
0x02 => Ok(EtpStatusCodes::InvalidParameter),
0x40 => Ok(EtpStatusCodes::InvalidPort),
0xFF => Ok(EtpStatusCodes::Failure),
_ => Err("Invalid ETP status code"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum EtpFirmwareInfoCmd {
ProtocolVersion = 0x01,
FirmwareVersion = 0x02,
BuildDate = 0x03,
HardwareType = 0x04,
}
impl TryFrom<u8> for EtpFirmwareInfoCmd {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x01 => Ok(EtpFirmwareInfoCmd::ProtocolVersion),
0x02 => Ok(EtpFirmwareInfoCmd::FirmwareVersion),
0x03 => Ok(EtpFirmwareInfoCmd::BuildDate),
0x04 => Ok(EtpFirmwareInfoCmd::HardwareType),
_ => Err("Invalid ETP firmware info command"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_transaction_id() {
let tx_id = get_transaction_id();
assert_eq!(tx_id, 0);
let tx_id = get_transaction_id();
assert_eq!(tx_id, 1);
PRIVATE_TX_ID.store(0xFFFF, std::sync::atomic::Ordering::SeqCst);
let tx_id = get_transaction_id();
assert_eq!(tx_id, 0xFFFF);
let tx_id = get_transaction_id();
assert_eq!(tx_id, 0);
}
#[test]
fn test_message_header() {
let header = EtpMessageHeader {
length: 0x1234,
payload_type: EtpPayloadType::Command,
status: EtpStatusCodes::Success,
transaction_id: 0x5678,
operation: EtpOperations::GetFirmwareInfo,
};
let header_bytes: Vec<u8> = header.clone().into();
assert_eq!(header_bytes.len(), ETP_MESSAGE_HEADER_SIZE);
let parsed_header = EtpMessageHeader::try_from(&header_bytes[..]).unwrap();
assert_eq!(parsed_header.length, header.length);
assert_eq!(parsed_header.payload_type, header.payload_type);
assert_eq!(parsed_header.transaction_id, header.transaction_id);
assert_eq!(parsed_header.operation, header.operation);
assert_eq!(header_bytes[0], 0x34);
assert_eq!(header_bytes[1], 0x12);
assert_eq!(header_bytes[2], 0x01);
assert_eq!(header_bytes[3], 0x00);
assert_eq!(header_bytes[4], 0x78);
assert_eq!(header_bytes[5], 0x56);
assert_eq!(header_bytes[6], 0x00);
assert_eq!(header_bytes[7], 0x00);
let header_replica: EtpMessageHeader =
EtpMessageHeader::try_from(&header_bytes[..]).unwrap();
assert_eq!(header_replica.length, header.length);
assert_eq!(header_replica.payload_type, header.payload_type);
assert_eq!(header_replica.status, header.status);
assert_eq!(header_replica.transaction_id, header.transaction_id);
assert_eq!(header_replica.operation, header.operation);
}
}