use core::convert::{TryFrom, TryInto};
use crate::constants::*;
pub type RawPacket = heapless::Vec<u8, PACKET_SIZE>;
pub type ExtPacket = heapless::Vec<u8, MAX_MSG_LENGTH>;
pub trait RawPacketExt {
fn data_len(&self) -> usize;
fn zeroed_until(len: usize) -> Self;
}
impl RawPacketExt for RawPacket {
fn data_len(&self) -> usize {
u32::from_le_bytes(self[1..5].try_into().unwrap()) as usize
}
fn zeroed_until(len: usize) -> Self {
let mut res = Self::new();
let cap = res.capacity();
res.resize_default(len.min(cap)).unwrap();
res
}
}
pub enum Error {
ShortPacket,
UnknownCommand(u8),
}
pub trait Packet: core::ops::Deref<Target = heapless::VecView<u8>> {
#[inline]
fn seq(&self) -> u8 {
self[6]
}
}
pub trait PacketWithData: Packet {
#[inline]
fn data(&self) -> &[u8] {
let declared_len = u32::from_le_bytes(self[1..5].try_into().unwrap()) as usize;
let len = core::cmp::min(MAX_MSG_LENGTH - CCID_HEADER_LEN, declared_len);
&self[CCID_HEADER_LEN..][..len]
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct UnknownChaining;
pub trait ChainedPacket: Packet {
#[inline(always)]
fn chain(&self) -> Result<Chain, UnknownChaining> {
let level_parameter = u16::from_le_bytes(self[8..10].try_into().unwrap());
match level_parameter {
0 => Ok(Chain::BeginsAndEnds),
1 => Ok(Chain::Begins),
2 => Ok(Chain::Ends),
3 => Ok(Chain::Continues),
0x10 => Ok(Chain::ExpectingMore),
_ => Err(UnknownChaining),
}
}
}
impl ChainedPacket for XfrBlock {}
pub struct DataBlock<'a> {
seq: u8,
chain: Chain,
data: &'a [u8],
}
impl<'a> DataBlock<'a> {
pub fn new(seq: u8, chain: Chain, data: &'a [u8]) -> Self {
assert!(data.len() + CCID_HEADER_LEN <= PACKET_SIZE);
Self { seq, chain, data }
}
}
impl core::fmt::Debug for DataBlock<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug_struct = f.debug_struct("DataBlock");
debug_struct.field("seq", &self.seq);
let l = core::cmp::min(self.data.len(), 16);
let escaped_bytes: heapless::Vec<u8, 64> = self
.data
.iter()
.take(l)
.flat_map(|byte| core::ascii::escape_default(*byte))
.collect();
let data_as_str = &core::str::from_utf8(&escaped_bytes).unwrap();
debug_struct
.field("chain", &self.chain)
.field("len", &self.data.len())
.field("data", &format_args!("b'{data_as_str}'"))
.finish()
}
}
impl From<DataBlock<'_>> for RawPacket {
fn from(block: DataBlock<'_>) -> RawPacket {
let len = block.data.len();
let mut packet = RawPacket::zeroed_until(CCID_HEADER_LEN + len);
packet[0] = 0x80;
packet[1..][..4].copy_from_slice(
&u32::try_from(len)
.expect("Packets should not be more than 4GiB")
.to_le_bytes(),
);
packet[5] = 0;
packet[6] = block.seq;
packet[7] = 0;
packet[8] = 0;
packet[9] = block.chain as u8;
packet[CCID_HEADER_LEN..][..len].copy_from_slice(block.data);
packet
}
}
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub enum CommandType {
PowerOn = 0x62,
PowerOff = 0x63,
GetSlotStatus = 0x65,
GetParameters = 0x6c,
XfrBlock = 0x6f,
Abort = 0x72,
}
macro_rules! command_message {
($($Name:ident: $code:expr,)*) => {
$(
#[derive(Debug)]
pub struct $Name {
// use reference? pulls in lifetimes though...
ext_raw: ExtPacket,
}
impl core::ops::Deref for $Name {
type Target = heapless::VecView<u8>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.ext_raw
}
}
impl core::ops::DerefMut for $Name {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.ext_raw
}
}
impl Packet for $Name {}
)*
pub enum Command {
$(
$Name($Name),
)*
}
impl Command {
pub fn seq(&self) -> u8 {
match self {
$(
Command::$Name(packet) => packet.seq(),
)*
}
}
pub fn command_type(&self) -> CommandType {
match self {
$(
Command::$Name(_) => CommandType::$Name,
)*
}
}
}
impl core::convert::TryFrom<ExtPacket> for Command {
type Error = Error;
#[inline]
fn try_from(packet: ExtPacket)
-> core::result::Result<Self, Self::Error>
{
if packet.len() < CCID_HEADER_LEN {
return Err(Error::ShortPacket);
}
if packet[5] != 0 {
}
let command_byte = packet[0];
Ok(match command_byte {
$(
$code => Command::$Name($Name { ext_raw: packet } ),
)*
_ => return Err(Error::UnknownCommand(command_byte)),
})
}
}
impl core::ops::Deref for Command {
type Target = heapless::VecView<u8>;
#[inline]
fn deref(&self) -> &Self::Target {
match self {
$(
Command::$Name(packet) => &packet,
)*
}
}
}
}
}
command_message!(
PowerOn: 0x62,
PowerOff: 0x63,
GetSlotStatus: 0x65,
GetParameters: 0x6c,
XfrBlock: 0x6f,
Abort: 0x72,
);
impl PacketWithData for XfrBlock {}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Chain {
BeginsAndEnds = 0,
Begins = 1,
Ends = 2,
Continues = 3,
ExpectingMore = 0x10,
}
impl Chain {
pub fn transfer_ongoing(&self) -> bool {
matches!(
self,
Chain::BeginsAndEnds | Chain::Ends | Chain::ExpectingMore
)
}
}
impl core::fmt::Debug for Command {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug_struct = f.debug_struct("Command");
debug_struct
.field("cmd", &self.command_type())
.field("seq", &self.seq());
if let Command::XfrBlock(block) = self {
let l = core::cmp::min(self.len(), 8);
let escaped_bytes: heapless::Vec<u8, 64> = block
.data()
.iter()
.take(l)
.flat_map(|byte| core::ascii::escape_default(*byte))
.collect();
let data_as_str = &core::str::from_utf8(&escaped_bytes).unwrap();
debug_struct
.field("chain", &block.chain())
.field("len", &block.data().len());
if l < self.len() {
debug_struct.field("data[..8]", &format_args!("b'{data_as_str}'"))
} else {
debug_struct.field("data", &format_args!("b'{data_as_str}'"))
};
}
debug_struct.finish()
}
}