Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove buffer copies #11

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 23 additions & 24 deletions src/pipe.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use core::convert::TryFrom;
use heapless::Vec;
use interchange::{Interchange, Requester};

use crate::{
constants::*,
types::packet::{
Chain, ChainedPacket as _, Command as PacketCommand, DataBlock, Error as PacketError,
ExtPacket, PacketWithData as _, RawPacket, XfrBlock,
Chain, ChainedPacket as _, CommandKind, DataBlock, Error as PacketError, ExtPacket,
Packet as _, PacketWithData as _, RawPacket,
},
};

Expand Down Expand Up @@ -179,15 +178,15 @@ where

// info!("{:X?}", &packet).ok();
// let p = packet.clone();
// match PacketCommand::try_from(packet) {
match PacketCommand::try_from(self.ext_packet.clone()) {
// match CommandKind::try_from(packet) {
match self.ext_packet.command_kind() {
Ok(command) => {
self.seq = command.seq();
self.seq = self.ext_packet.seq();

// If we receive an ABORT on the control pipe, we reject all further commands until
// we receive a matching ABORT on the bulk endpoint too.
if let Some(control_abort) = self.control_abort {
if matches!(command, PacketCommand::Abort(_)) && control_abort == self.seq {
if command == CommandKind::Abort && control_abort == self.seq {
self.abort();
} else {
self.send_slot_status_error(Error::CmdAborted);
Expand All @@ -198,17 +197,17 @@ where

// happy path
match command {
PacketCommand::PowerOn(_command) => self.send_atr(),
CommandKind::PowerOn => self.send_atr(),

PacketCommand::PowerOff(_command) => self.send_slot_status_ok(),
CommandKind::PowerOff => self.send_slot_status_ok(),

PacketCommand::GetSlotStatus(_command) => self.send_slot_status_ok(),
CommandKind::GetSlotStatus => self.send_slot_status_ok(),

PacketCommand::XfrBlock(command) => self.handle_transfer(command),
CommandKind::XfrBlock => self.handle_transfer(),

PacketCommand::Abort(_command) => self.bulk_abort = Some(self.seq),
CommandKind::Abort => self.bulk_abort = Some(self.seq),

PacketCommand::GetParameters(_command) => self.send_parameters(),
CommandKind::GetParameters => self.send_parameters(),
}
}

Expand Down Expand Up @@ -236,7 +235,7 @@ where
self.interchange.cancel().ok();
}

fn handle_transfer(&mut self, command: XfrBlock) {
fn handle_transfer(&mut self) {
// state: Idle, Receiving, Processing, Sending,
//
// conts: BeginsAndEnds, Begins, Ends, Continues, ExpectDataBlock,
Expand All @@ -246,13 +245,13 @@ where
match self.state {
State::Idle => {
// invariant: BUFFER_SIZE >= PACKET_SIZE
match command.chain() {
match self.ext_packet.chain() {
Chain::BeginsAndEnds => {
info!("begins and ends");
self.reset_interchange();
let message = self.interchange.request_mut().unwrap();
message.clear();
message.extend_from_slice(command.data()).unwrap();
message.extend_from_slice(self.ext_packet.data()).unwrap();
self.call_app();
self.state = State::Processing;
// self.send_empty_datablock();
Expand All @@ -262,27 +261,27 @@ where
self.reset_interchange();
let message = self.interchange.request_mut().unwrap();
message.clear();
message.extend_from_slice(command.data()).unwrap();
message.extend_from_slice(self.ext_packet.data()).unwrap();
self.state = State::Receiving;
self.send_empty_datablock(Chain::ExpectingMore);
}
_ => panic!("unexpectedly in idle state"),
}
}

State::Receiving => match command.chain() {
State::Receiving => match self.ext_packet.chain() {
Chain::Continues => {
info!("continues");
let message = self.interchange.request_mut().unwrap();
assert!(command.data().len() + message.len() <= MAX_MSG_LENGTH);
message.extend_from_slice(command.data()).unwrap();
assert!(self.ext_packet.data().len() + message.len() <= MAX_MSG_LENGTH);
message.extend_from_slice(self.ext_packet.data()).unwrap();
self.send_empty_datablock(Chain::ExpectingMore);
}
Chain::Ends => {
info!("ends");
let message = self.interchange.request_mut().unwrap();
assert!(command.data().len() + message.len() <= MAX_MSG_LENGTH);
message.extend_from_slice(command.data()).unwrap();
assert!(self.ext_packet.data().len() + message.len() <= MAX_MSG_LENGTH);
message.extend_from_slice(self.ext_packet.data()).unwrap();
self.call_app();
self.state = State::Processing;
}
Expand All @@ -294,15 +293,15 @@ where
// info!("{:X?}", &command).ok();
panic!(
"ccid pipe unexpectedly received command while in processing state: {:?}",
&command
&self.ext_packet
);
}

State::ReadyToSend => {
panic!("unexpectedly in ready-to-send state")
}

State::Sending => match command.chain() {
State::Sending => match self.ext_packet.chain() {
Chain::ExpectingMore => {
self.prime_outbox();
}
Expand Down
179 changes: 58 additions & 121 deletions src/types/packet.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use core::convert::{TryFrom, TryInto};
use core::{
convert::{TryFrom, TryInto},
ops::{Deref, DerefMut},
};

use crate::constants::*;

pub type RawPacket = heapless::Vec<u8, PACKET_SIZE>;
pub type ExtPacket = heapless::Vec<u8, MAX_MSG_LENGTH>;

#[derive(Default, PartialEq, Eq)]
pub struct ExtPacket(heapless::Vec<u8, MAX_MSG_LENGTH>);

pub trait RawPacketExt {
fn data_len(&self) -> usize;
Expand All @@ -20,7 +25,7 @@ pub enum Error {
UnknownCommand(u8),
}

pub trait Packet: core::ops::Deref<Target = ExtPacket> {
pub trait Packet: core::ops::Deref<Target = heapless::Vec<u8, MAX_MSG_LENGTH>> {
#[inline]
fn slot(&self) -> u8 {
// we have only one slot
Expand Down Expand Up @@ -60,7 +65,22 @@ pub trait ChainedPacket: Packet {
}
}

impl ChainedPacket for XfrBlock {}
impl Deref for ExtPacket {
type Target = heapless::Vec<u8, MAX_MSG_LENGTH>;
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for ExtPacket {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl Packet for ExtPacket {}
impl PacketWithData for ExtPacket {}
impl ChainedPacket for ExtPacket {}

pub struct DataBlock<'a> {
seq: u8,
Expand Down Expand Up @@ -134,8 +154,8 @@ impl From<DataBlock<'_>> for RawPacket {
}

#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub enum CommandType {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CommandKind {
// REQUESTS

// supported
Expand All @@ -156,120 +176,34 @@ pub enum CommandType {
// SetDataRateAndClockFrequency = 0x73,
}

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 = ExtPacket;

#[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 ExtPacket {
pub fn command_kind(&self) -> Result<CommandKind, Error> {
if self.len() < CCID_HEADER_LEN {
return Err(Error::ShortPacket);
}

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 {
// wrong slot
}
let command_byte = packet[0];
Ok(match command_byte {
$(
$code => Command::$Name($Name { ext_raw: packet } ),
)*
_ => return Err(Error::UnknownCommand(command_byte)),
})
}
if self[5] != 0 {
// wrong slot
}

impl core::ops::Deref for Command {
type Target = ExtPacket;

#[inline]
fn deref(&self) -> &Self::Target {
match self {
$(
Command::$Name(packet) => &packet,
)*
}
}
let command_byte = self[0];
match command_byte {
0x62 => Ok(CommandKind::PowerOn),
0x63 => Ok(CommandKind::PowerOff),
0x65 => Ok(CommandKind::GetSlotStatus),
0x6c => Ok(CommandKind::GetParameters),
0x6f => Ok(CommandKind::XfrBlock),
0x72 => Ok(CommandKind::Abort),
_ => Err(Error::UnknownCommand(command_byte)),
}

// impl core::ops::DerefMut for Command {

// #[inline]
// fn deref_mut(&mut self) -> &mut Self::Target {
// match self {
// $(
// Command::$Name(packet) => &mut packet,
// )*
// }
// }
// }
}
}

command_message!(
PowerOn: 0x62,
PowerOff: 0x63,
GetSlotStatus: 0x65,
GetParameters: 0x6c,
XfrBlock: 0x6f,
Abort: 0x72,
);

impl PacketWithData for XfrBlock {}
// command_message!(
// PowerOn: 0x62,
// PowerOff: 0x63,
// GetSlotStatus: 0x65,
// GetParameters: 0x6c,
// XfrBlock: 0x6f,
// Abort: 0x72,
// );

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
Expand All @@ -290,19 +224,22 @@ impl Chain {
}
}

impl core::fmt::Debug for Command {
impl core::fmt::Debug for ExtPacket {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug_struct = f.debug_struct("Command");
// write!("Command({:?})", &self.command_type()));
// // "Command");

let Ok(command_type) = self.command_kind() else {
return debug_struct.field("cmd", &format_args!("error")).field("value", &format_args!("{:02x?}", self.0)).finish();
};
debug_struct
.field("cmd", &self.command_type())
.field("cmd", &command_type)
.field("seq", &self.seq());

if let Command::XfrBlock(block) = self {
if command_type == CommandKind::XfrBlock {
let l = core::cmp::min(self.len(), 8);
let escaped_bytes: heapless::Vec<u8, 64> = block
let escaped_bytes: heapless::Vec<u8, 64> = self
.data()
.iter()
.take(l)
Expand All @@ -311,8 +248,8 @@ impl core::fmt::Debug for Command {
let data_as_str = &core::str::from_utf8(&escaped_bytes).unwrap();

debug_struct
.field("chain", &block.chain())
.field("len", &block.data().len());
.field("chain", &self.chain())
.field("len", &self.data().len());

if l < self.len() {
debug_struct.field("data[..8]", &format_args!("b'{data_as_str}'"))
Expand Down