diff --git a/Cargo.toml b/Cargo.toml index 7d4e774..2ed6d2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,9 @@ edition = "2018" [dependencies] cortex-m-semihosting = { version = "0.3.5", optional = true} +heapless = "0.5.4" +heapless-bytes = { git = "https://github.com/ycrypto/heapless-bytes", branch = "main" } +# heapless-bytes = { path = "../../../heapless-bytes" } usb-device = { version = "0.2.3", features = ["control-buffer-256"] } [features] diff --git a/src/class.rs b/src/class.rs index da04270..2f16221 100644 --- a/src/class.rs +++ b/src/class.rs @@ -4,11 +4,11 @@ use cortex_m_semihosting::hprintln; use crate::{ constants::*, + types::*, pipe::Pipe, }; use usb_device::class_prelude::*; - type Result = core::result::Result; pub struct Ccid @@ -16,8 +16,8 @@ where Bus: 'static + UsbBus, { interface_number: InterfaceNumber, + read: EndpointOut<'static, Bus>, pipe: Pipe, - must_send_zlp: bool, } impl Ccid @@ -25,17 +25,14 @@ where Bus: 'static + UsbBus, { pub fn new(allocator: &'static UsbBusAllocator) -> Self { - let read_endpoint = allocator.bulk(PACKET_SIZE as _); - let write_endpoint = allocator.bulk(PACKET_SIZE as _); - let pipe = Pipe::new(read_endpoint, write_endpoint); + let read = allocator.bulk(PACKET_SIZE as _); + let write = allocator.bulk(PACKET_SIZE as _); + let pipe = Pipe::new(write); let interface_number = allocator.interface(); - Self { interface_number, pipe, must_send_zlp: false } + Self { interface_number, read, pipe } } } -pub enum ClassRequests { -} - impl UsbClass for Ccid where Bus: 'static + UsbBus, @@ -53,37 +50,39 @@ where FUNCTIONAL_INTERFACE, &FUNCTIONAL_INTERFACE_DESCRIPTOR, )?; - writer.endpoint(&self.pipe.write); - writer.endpoint(&self.pipe.read); + writer.endpoint(&self.pipe.write).unwrap(); + writer.endpoint(&self.read).unwrap(); Ok(()) } fn poll(&mut self) { - self.pipe.maybe_write_packet(); + self.pipe.poll_app(); + self.pipe.maybe_send_packet(); } fn endpoint_in_complete(&mut self, addr: EndpointAddress) { if addr != self.pipe.write.address() { return; } - if self.must_send_zlp { - self.pipe.write.write(&[]).ok(); - self.must_send_zlp = false; - } else { - self.pipe.maybe_write_packet(); - } + self.pipe.maybe_send_packet(); } fn endpoint_out(&mut self, addr: EndpointAddress) { - if addr != self.pipe.read.address() { return; } + if addr != self.read.address() { return; } + + let maybe_packet = RawPacket::try_from( + |packet| self.read.read(packet)); + + // not much we can do in error cases + if let Ok(packet) = maybe_packet { + self.pipe.handle_packet(packet); + } - self.pipe.read_and_handle_packet(); } fn control_in(&mut self, transfer: ControlIn) { use usb_device::control::*; - - let Request { request_type, recipient, index, request, .. } = *transfer.request(); - + let Request { request_type, recipient, index, request, .. } + = *transfer.request(); if index as u8 != u8::from(self.interface_number) { return; } @@ -92,12 +91,6 @@ where match ClassRequest::try_from(request) { Ok(request) => { match request { - ClassRequest::Abort => { - // oh yeah? - transfer.reject().ok(); - todo!(); - } - // not strictly needed, as our bNumClockSupported = 0 ClassRequest::GetClockFrequencies => { transfer.accept(|data| { @@ -113,6 +106,7 @@ where Ok(4) }).ok(); }, + _ => panic!("unexpected direction for {:?}", &request), } } @@ -124,7 +118,36 @@ where } fn control_out(&mut self, transfer: ControlOut) { - // todo!(); + use usb_device::control::*; + let Request { request_type, recipient, index, request, value, .. } + = *transfer.request(); + if index as u8 != u8::from(self.interface_number) { + return; + } + + if (request_type, recipient) == (RequestType::Class, Recipient::Interface) { + match ClassRequest::try_from(request) { + Ok(request) => { + match request { + ClassRequest::Abort => { + // spec: "slot in low, seq in high byte" + let [slot, seq] = value.to_le_bytes(); + self.pipe.expect_abort(slot, seq); + transfer.accept().ok(); + + // // old behaviour + // transfer.reject().ok(); + // todo!(); + } + _ => panic!("unexpected direction for {:?}", &request), + } + } + + Err(()) => { + hprintln!("unexpected request: {}", request).ok(); + } + } + } } } diff --git a/src/constants.rs b/src/constants.rs index be1c1b0..d35dbfc 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,8 +1,17 @@ +#![allow(non_camel_case_types)] +use heapless_bytes::{ + Bytes, + consts, + Unsigned as _ +}; + // can be 8, 16, 32, 64 or 512 #[cfg(feature = "highspeed-usb")] -pub const PACKET_SIZE: usize = 512; +pub type PACKET_SIZE_TYPE = consts::U512; #[cfg(not(feature = "highspeed-usb"))] -pub const PACKET_SIZE: usize = 64; +pub type PACKET_SIZE_TYPE = consts::U64; + +pub const PACKET_SIZE: usize = PACKET_SIZE_TYPE::USIZE; pub const CLASS_CCID: u8 = 0x0B; pub const SUBCLASS_NONE: u8 = 0x0; @@ -19,38 +28,31 @@ pub enum TransferMode { pub const FUNCTIONAL_INTERFACE: u8 = 0x21; -pub enum ClassRequest { - Abort = 1, - GetClockFrequencies = 2, - GetDataRates = 3, -} - -impl core::convert::TryFrom for ClassRequest { - type Error = (); - fn try_from(request: u8) -> core::result::Result { - Ok(match request { - 1 => Self::Abort, - 2 => Self::GetClockFrequencies, - 3 => Self::GetDataRates, - _ => return Err(()), - }) - } -} - // NB: all numbers are little-endian -// 4000 KHz = 4MHz +// 3580 KHz (as per ICCD spec) = 3.58 MHz +// (not relevant, fixed fixed for legacy reasonse +// Yubico: 4000 KHz = 4MHz // pub const CLOCK_FREQUENCY: [u8; 4] = 4000u32.to_le_bytes(); // instead, use Python: `import struct; struct.pack(">::Output; +pub type MessageBuffer = Bytes; +pub const MAX_MSG_LENGTH: usize = MAX_MSG_LENGTH_TYPE::USIZE; pub const MAX_MSG_LENGTH_LE: [u8; 4] = [0x00, 0x0C, 0x00, 0x00]; pub const NUM_SLOTS: u8 = 1; pub const MAX_BUSY_SLOTS: u8 = 1; @@ -59,11 +61,8 @@ pub const PIN_SUPPORT: u8 = 0; // cf. Sec. 5.1 in: https://www.usb.org/sites/default/files/DWG_Smart-Card_CCID_Rev110.pdf pub const FUNCTIONAL_INTERFACE_DESCRIPTOR: [u8; 52] = [ - // bcdCCID rev1.10 <-- Linux doesn't know about this - // 0x10, 0x01, - - // bcdCCID rev1.00 - 0x00, 0x01, + // bcdCCID rev1.10 + 0x10, 0x01, // bMaxSlotIndex NUM_SLOTS - 1, // bVoltageSupport (5.0V + 3.0V + 1.8V) @@ -124,9 +123,9 @@ pub const FUNCTIONAL_INTERFACE_DESCRIPTOR: [u8; 52] = [ MAX_MSG_LENGTH_LE[2], MAX_MSG_LENGTH_LE[3], - // bClassGetResponse ("echo") + // bClassGetResponse ("echo"), as per ICCD spec 0xFF, - // bClassEnvelope ("echo"), gnuk: 0 + // bClassEnvelope ("echo"), as per ICCD spec, gnuk: 0 0xFF, // wlcdLayout (none) 0x00, 0x00, diff --git a/src/lib.rs b/src/lib.rs index 7a0a5af..1686638 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,5 +6,6 @@ pub mod constants; pub mod class; pub mod pipe; +pub mod types; pub use class::Ccid; diff --git a/src/pipe.rs b/src/pipe.rs index ee2484f..15eabb2 100644 --- a/src/pipe.rs +++ b/src/pipe.rs @@ -1,195 +1,58 @@ -use core::{ - borrow::{Borrow, BorrowMut}, - convert::{TryFrom, TryInto}, -}; +use core::convert::TryFrom; use cortex_m_semihosting::hprintln; -use crate::constants::*; +use crate::{ + constants::*, + types::*, +}; use usb_device::class_prelude::*; +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum State { + Idle, + Receiving, + Processing, + ReadyToSend, + Sending, +} pub struct Pipe where Bus: UsbBus + 'static, { - pub(crate) read: EndpointOut<'static, Bus>, pub(crate) write: EndpointIn<'static, Bus>, // pub(crate) rpc: TransportEndpoint<'rpc>, - packet: Packet, - buffer: [u8; MAX_MSG_LENGTH], + seq: u8, + state: State, + message: MessageBuffer, + sent: usize, + outbox: Option, } impl Pipe where Bus: 'static + UsbBus, { - pub(crate) fn new( - read: EndpointOut<'static, Bus>, - write: EndpointIn<'static, Bus>, - ) - -> Self { Self { - read, + pub(crate) fn new(write: EndpointIn<'static, Bus>) -> Self { + + assert!(MAX_MSG_LENGTH >= PACKET_SIZE); + + Self { write, - packet: Packet { buffer: [0u8; PACKET_SIZE] }, - buffer: [0u8; MAX_MSG_LENGTH], - } } -} - -#[derive(Copy, Clone)] -pub struct Packet { - buffer: [u8; PACKET_SIZE as _], -} - -impl core::fmt::Debug for Packet { - - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut debug_struct = f.debug_struct("Packet"); - let mut debug_struct = match self.msg_type() { - Ok(message_type) => debug_struct.field("type", &message_type), - Err(()) => debug_struct.field("type", &self[0]), - }; - debug_struct - .field("slot", &self.slot()) - .field("seq", &self.slot()) - // other fields? - // .field("data[..16]", &self.data()[..16]) - // .field("data", self.data()) - .finish() - } -} - -#[repr(u8)] -#[derive(Copy, Clone, Debug)] -pub enum MessageType { - PowerOn = 0x62, - PowerOff = 0x63, - GetSlotStatus = 0x65, - TransferBlock = 0x6f, -} - -pub struct SlotStatus { - seq: u8 -} - -impl SlotStatus { - pub fn new(seq: u8) -> Self { - Self { seq } - } -} - -impl From for Packet { - fn from(slot_status: SlotStatus) -> Self { - let mut buffer = [0u8; PACKET_SIZE]; - buffer[0] = 0x81; - // buffer[1..5] = 0, no extra data - // buffer[5] = 0, only one slot - buffer[6] = slot_status.seq; - // buffer[7] = 0, status - // buffer[8] = 0, error - // buffer[9] = 0, chain parameter, this is complete - Packet { buffer } - } -} - -pub struct DataBlock<'a> { - seq: u8, - data: &'a [u8], - -} - -impl<'a> DataBlock<'a> { - pub fn new(seq: u8, data: &'a [u8]) -> Self { - assert!(data.len() + 10 <= PACKET_SIZE); - Self { seq, data } - } -} - -impl<'a> From> for Packet { - fn from(data_block: DataBlock) -> Self { - let mut buffer = [0u8; PACKET_SIZE]; - let len = data_block.data.len(); - buffer[0] = 0x80; - buffer[1..5].copy_from_slice(&len.to_le_bytes()); - // buffer[5] = 0, only one slot - buffer[6] = data_block.seq; - // buffer[7] = 0, status - // buffer[8] = 0, error - // buffer[9] = 0, chain parameter, this is complete - buffer[10..][..len].copy_from_slice(data_block.data); - Packet { buffer } - } -} - -impl core::convert::TryFrom for MessageType { - type Error = (); - - fn try_from(message_type_byte: u8) -> core::result::Result { - Ok(match message_type_byte { - 0x62 => Self::PowerOn, - 0x63 => Self::PowerOff, - 0x65 => Self::GetSlotStatus, - 0x6f => Self::TransferBlock, - _ => return Err(()), - }) - } -} - -impl Packet { - #[inline(always)] - pub fn msg_type(&self) -> core::result::Result { - self[0].try_into() + seq: 0, + state: State::Idle, + sent: 0, + outbox: None, + message: MessageBuffer::new(), + } } - #[inline(always)] - pub fn len(&self) -> usize { - u32::from_le_bytes(self[1..5].try_into().unwrap()) as usize - } - - #[inline(always)] - pub fn slot(&self) -> u8 { - // we have only one slot - assert!(self[5] == 0); - *&self[5] - } - - #[inline(always)] - pub fn seq(&self) -> u8 { *&self[6] } - - // either three message specific bytes, or - // a status field (1 byte), an error field and one message specific byte - - #[inline(always)] - pub fn data(&self) -> &[u8] { &self[10..] } -} - -// impl core::borrow::Borrow<[u8; PACKET_SIZE]> for Packet { -// fn borrow(&self) -> &[u8; PACKET_SIZE] { -// &self.buffer -// } -// } - -// impl core::borrow::BorrowMut<[u8; PACKET_SIZE]> for Packet { -// fn borrow_mut(&mut self) -> &mut [u8; PACKET_SIZE] { -// &mut self.buffer -// } -// } - -impl core::ops::Deref for Packet { - type Target = [u8]; - fn deref(&self) -> &Self::Target { - // let len = self.len(); - // &self.buffer[..10 + len] - &self.buffer - } -} - -impl core::ops::DerefMut for Packet { - fn deref_mut(&mut self) -> &mut Self::Target { - // let len = self.len(); - // &mut self.buffer[..10 + len] - &mut self.buffer + pub fn busy(&self) -> bool { + // need more states, but if we're waiting + // to send, we can't accept new packets + self.outbox.is_some() } } @@ -198,81 +61,216 @@ impl Pipe where Bus: 'static + UsbBus { - pub fn read_and_handle_packet(&mut self) { - // let data: &mut [u8; PACKET_SIZE] = self.packet.borrow_mut(); - // let read = match self.read.read(&mut data[..]) { - let read = match self.read.read(&mut self.packet) { - // let read = match self.read.read(&mut self.packet.borrow_mut()[..]) { - Ok(read) => { - // all packets have 10 byte header before eventual data - if read < 10 { panic!("unexpected small packet"); } - read + pub fn handle_packet(&mut self, packet: RawPacket) { + + match Command::try_from(packet) { + Ok(command) => { + self.seq = command.seq(); + hprintln!("{:?}", &command).ok(); + + // happy path + match command { + Command::PowerOn(_command) => self.send_atr(), + + Command::PowerOff(_command) => self.send_slot_status_ok(), + + Command::GetSlotStatus(_command) => self.send_slot_status_ok(), + + Command::XfrBlock(command) => self.handle_transfer(command), + + Command::Abort(_command) => { + todo!(); + } + } } - Err(_) => { - // usb-device lists WouldBlock + BufferOverflow as possible errors. - // both should not occur here, and we can't do anything anyway. - panic!("unexpected read error"); + + Err(PacketError::ShortPacket) => { + panic!("short packet!"); } + + Err(PacketError::UnknownCommand(c)) => { + panic!("unknown command byte 0x{:x}", c); + } + } + } + + fn handle_transfer(&mut self, command: XfrBlock) { + + // state: Idle, Receiving, Processing, Sending, + // + // conts: BeginsAndEnds, Begins, Ends, Continues, ExpectDataBlock, + + match self.state { + + State::Idle => { + // invariant: BUFFER_SIZE >= PACKET_SIZE + match command.chain() { + Chain::BeginsAndEnds => { + self.message.clear(); + self.message.extend_from_slice(command.data()).unwrap(); + self.call_app(); + self.state = State::Processing; + // self.send_empty_datablock(); + } + Chain::Begins => { + self.message.clear(); + self.message.extend_from_slice(command.data()).unwrap(); + self.state = State::Receiving; + self.send_empty_datablock(Chain::ExpectingMore); + } + _ => panic!("{:?} unexpected in idle state"), + } + } + + State::Receiving => { + match command.chain() { + Chain::Continues => { + assert!(command.data().len() + self.message.len() <= MAX_MSG_LENGTH); + self.message.extend_from_slice(command.data()).unwrap(); + self.send_empty_datablock(Chain::ExpectingMore); + } + Chain::Ends => { + assert!(command.data().len() + self.message.len() <= MAX_MSG_LENGTH); + self.message.extend_from_slice(command.data()).unwrap(); + self.call_app(); + self.state = State::Processing; + } + _ => panic!("{:?} unexpected in receiving state"), + } + } + + State::Processing => { + panic!("{:?} unexpected in processing state") + } + + State::ReadyToSend => { + panic!("{:?} unexpected in ready-to-send state") + } + + State::Sending => { + match command.chain() { + Chain::ExpectingMore => { + self.prime_outbox(); + } + _ => panic!("{:?} unexpected in receiving state"), + } + } + } + } + + fn call_app(&mut self) { + // todo!("have message of length {} to dispatch", self.message.len()); + } + + pub fn poll_app(&mut self) { + self.fake_poll_app(); + } + + fn fake_poll_app(&mut self) { + if let State::Processing = self.state { + // we should have an open XfrBlock allowance + self.state = State::ReadyToSend; + // fake some data + self.sent = 0; + self.message.resize_default(128).ok(); + self.prime_outbox(); + } + } + + pub fn prime_outbox(&mut self) { + if self.state != State::ReadyToSend && self.state != State::Sending { + return; + } + + if self.outbox.is_some() { panic!(); } + + let chunk_size = core::cmp::min(PACKET_SIZE - 10, self.message.len() - self.sent); + let chunk = &self.message[self.sent..][..chunk_size]; + self.sent += chunk_size; + let more = self.sent < self.message.len(); + + let chain = match (self.state, more) { + (State::ReadyToSend, true) => { self.state = State::Sending; Chain::Begins } + (State::ReadyToSend, false) => { self.state = State::Idle; Chain::BeginsAndEnds } + (State::Sending, true) => Chain::Continues, + (State::Sending, false) => { self.state = State::Idle; Chain::Ends } + // logically impossible + _ => { return; } }; - hprintln!("got a packet of len {}: {:?}", read, &self.packet).ok(); + let primed_packet = DataBlock::new(self.seq, chain, chunk); + hprintln!("priming {:?}", &primed_packet).ok(); + self.outbox = Some(primed_packet.into()); - if let Ok(msg_type) = self.packet.msg_type() { - match msg_type { - MessageType::PowerOff | - MessageType::GetSlotStatus => { - self.send_slot_status(); - } + // fast-lane response attempt + self.maybe_send_packet(); + } - MessageType::PowerOn => { - self.send_atr(); - } + fn send_empty_datablock(&mut self, chain: Chain) { + let packet = DataBlock::new(self.seq, chain, &[]).into(); + self.send_packet_assuming_possible(packet); + } + + fn send_slot_status_ok(&mut self) + // , icc_status: u8, command_status: u8, error: u8) + { + let mut packet = RawPacket::new(); + packet.resize_default(10).ok(); + packet[0] = 0x81; + packet[6] = self.seq; + self.send_packet_assuming_possible(packet); + } + + fn send_atr(&mut self) { + let packet = DataBlock::new( + self.seq, + Chain::BeginsAndEnds, + &[0x3b, 0x8c,0x80,0x01], + ); + self.send_packet_assuming_possible(packet.into()); + } + + + fn send_packet_assuming_possible(&mut self, packet: RawPacket) { + assert!(self.outbox.is_none()); + self.outbox = Some(packet); + + // fast-lane response attempt + self.maybe_send_packet(); + } + + pub fn maybe_send_packet(&mut self) { + if let Some(packet) = self.outbox.as_ref() { + let needs_zlp = packet.len() == PACKET_SIZE; + match self.write.write(packet) { + Ok(n) if n == packet.len() => { + if packet.len() > 8 { + hprintln!("--> sent {:?}... successfully", &packet[..8]).ok(); + } else { + hprintln!("--> sent {:?} successfully", packet).ok(); + } + + if needs_zlp { + hprintln!("sending ZLP").ok(); + self.outbox = Some(RawPacket::new()); + } else { + self.outbox = None; + } - MessageType::TransferBlock => { - // } + Ok(_) => panic!("short write"), + + Err(UsbError::WouldBlock) => { + // fine, can't write try later + // this shouldn't happen probably + hprintln!("waiting to send").ok(); + }, + + Err(_) => panic!("unexpected send error"), } } } - fn send_slot_status(&self) { - let packet = Packet::from(SlotStatus::new(self.packet.seq())); - hprintln!("answering with: {:?}", &packet).ok(); - match self.write.write(&packet[..10 + packet.len()]) { - Ok(10) => {} - - Ok(n) => panic!("expected to send exactly 10 bytes, sent {}", n), - Err(UsbError::WouldBlock) => panic!("would block not handled yet"), - Err(e) => panic!("unexpected error {:?}", e), - } - } - - fn send_atr(&self) { - let packet = Packet::from(DataBlock::new( - self.packet.seq(), - &[0x3b, 0x8c,0x80,0x01], - )); - hprintln!("answering with: {:?}", &packet).ok(); - - match self.write.write(&packet[..10 + packet.len()]) { - Ok(14) => {} - - Ok(n) => panic!("expected to send exactly 14 bytes, sent {}", n), - Err(UsbError::WouldBlock) => panic!("would block not handled yet"), - Err(e) => panic!("unexpected error {:?}", e), - } - } - - pub fn maybe_write_packet(&mut self) { - // let result = self.write.write(&packet); - - // match result { - // Err(UsbError::WouldBlock) => { - // // fine, can't write try later - // // this shouldn't happen probably - // }, - } - // pub fn read_address(&self) -> EndpointAddress { // self.read.address() // } @@ -281,4 +279,10 @@ where // self.write.address() // } + pub fn expect_abort(&mut self, slot: u8, seq: u8) { + debug_assert!(slot == 0); + hprintln!("ABORT expected for seq = {}", seq).ok(); + todo!(); + } + } diff --git a/src/scratch.rs b/src/scratch.rs new file mode 100644 index 0000000..4d1d42b --- /dev/null +++ b/src/scratch.rs @@ -0,0 +1,107 @@ + +// #[repr(u8)] +// #[derive(Copy, Clone, Debug)] +// pub enum MessageType { + +// // REQUESTS + +// // supported +// PowerOn = 0x62, +// PowerOff = 0x63, +// GetSlotStatus = 0x65, +// XfrBlock = 0x6f, +// Abort = 0x72, + +// // unsupported +// GetParameters = 0x6c, +// ResetParameters = 0x6d, +// SetParameters = 0x61, +// Escape = 0x6b,// for vendor commands +// IccClock = 0x7e, +// T0Apdu = 0x6a, +// Secure = 0x69, +// Mechanical = 0x71, +// SetDataRateAndClockFrequency = 0x73, + +// // RESPONSES + +// // used +// DataBlock = 0x80, +// SlotStatus = 0x81, + +// // unused +// Parameters = 0x82, +// // Escape = 0x83, +// DataRateAndClockFrequency = 0x84, +// } + +// pub struct SlotStatus { +// seq: u8 +// } + +// impl SlotStatus { +// pub fn new(seq: u8) -> Self { +// Self { seq } +// } +// } + +// impl From for Packet { +// fn from(slot_status: SlotStatus) -> Self { +// let mut buffer = [0u8; PACKET_SIZE]; +// buffer[0] = 0x81; +// // buffer[1..5] = 0, no extra data +// // buffer[5] = 0, only one slot +// buffer[6] = slot_status.seq; +// // buffer[7] = 0, status +// // buffer[8] = 0, error +// // buffer[9] = 0, chain parameter, this is complete +// Packet { buffer } +// } +// } + +// pub struct DataBlock<'a> { +// seq: u8, +// data: &'a [u8], + +// } + +// impl<'a> DataBlock<'a> { +// pub fn new(seq: u8, data: &'a [u8]) -> Self { +// assert!(data.len() + 10 <= PACKET_SIZE); +// Self { seq, data } +// } +// } + +// impl<'a> From> for Packet { +// fn from(data_block: DataBlock) -> Self { +// let mut buffer = [0u8; PACKET_SIZE]; +// let len = data_block.data.len(); +// buffer[0] = 0x80; +// buffer[1..5].copy_from_slice(&len.to_le_bytes()); +// // buffer[5] = 0, only one slot +// buffer[6] = data_block.seq; +// // buffer[7] = 0, status +// // buffer[8] = 0, error +// // buffer[9] = 0, chain parameter, this is complete +// buffer[10..][..len].copy_from_slice(data_block.data); +// Packet { buffer } +// } +// } + +// impl core::convert::TryFrom for MessageType { +// type Error = (); + +// fn try_from(message_type_byte: u8) -> core::result::Result { +// Ok(match message_type_byte { +// 0x62 => Self::PowerOn, +// 0x63 => Self::PowerOff, +// 0x65 => Self::GetSlotStatus, +// 0x6f => Self::XfrBlock, +// 0x71 => Self::Mechanical, +// 0x80 => Self::DataBlock, +// 0x81 => Self::SlotStatus, +// _ => return Err(()), +// }) +// } +// } + diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..1151181 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,429 @@ +use core::convert::TryInto; + +use cortex_m_semihosting::hprintln; + +use crate::constants::*; + + +pub type RawPacket = heapless_bytes::Bytes; + +pub enum PacketError { + ShortPacket, + UnknownCommand(u8), +} + +pub enum Message { + Command(Command), + Response(Response), +} + +pub trait Packet: core::ops::Deref { + + #[inline] + fn slot(&self) -> u8 { + // we have only one slot + assert!(self[5] == 0); + *&self[5] + } + + #[inline] + fn seq(&self) -> u8 { *&self[6] } + +} + +pub trait PacketWithData: Packet { + + #[inline] + fn data(&self) -> &[u8] { + // let len = u32::from_le_bytes(self[1..5].try_into().unwrap()) as usize; + let declared_len = + u32::from_le_bytes(self[1..5].try_into().unwrap()) as usize; + let len = core::cmp::min(PACKET_SIZE - 10, declared_len); + hprintln!("delcared = {}, len = {}", declared_len, len).ok(); + &self[10..][..len] + } +} + +pub trait ChainedPacket: Packet { + + #[inline(always)] + fn chain(&self) -> Chain { + let level_parameter = u16::from_le_bytes(self[8..10].try_into().unwrap()); + match level_parameter { + 0 => Chain::BeginsAndEnds, + 1 => Chain::Begins, + 2 => Chain::Ends, + 3 => Chain::Continues, + 0x10 => Chain::ExpectingMore, + _ => panic!("invalid power select parameter"), + } + } +} + +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() + 10 <= 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 = + 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() + } +} + + +// WELL. DataBlock does not deref to RawPacket +// impl Deref for DataBlock<_> { +// type Target: & + +// impl Packet for DataBlock<'_> { +// fn slot(&self) -> u8 { 0 } +// fn seq(&self) -> u8 { self.seq } +// } + +impl Into for DataBlock<'_> { + fn into(self) -> RawPacket { + let mut packet = RawPacket::new(); + let len = self.data.len(); + packet.resize_default(10 + len).ok(); + packet[0] = 0x80; + packet[1..][..4].copy_from_slice(&len.to_le_bytes()); + packet[5] = 0; + packet[6] = self.seq; + + // status + packet[7] = 0; + // error + packet[8] = 0; + // chain parameter + packet[9] = self.chain as u8; + packet[10..][..len].copy_from_slice(self.data); + + packet + } +} + +#[repr(u8)] +#[derive(Copy, Clone, Debug)] +pub enum CommandType { + + // REQUESTS + + // supported + PowerOn = 0x62, + PowerOff = 0x63, + GetSlotStatus = 0x65, + XfrBlock = 0x6f, + Abort = 0x72, + + // unsupported + GetParameters = 0x6c, + ResetParameters = 0x6d, + SetParameters = 0x61, + Escape = 0x6b,// for vendor commands + IccClock = 0x7e, + T0Apdu = 0x6a, + Secure = 0x69, + Mechanical = 0x71, + SetDataRateAndClockFrequency = 0x73, +} + +macro_rules! command_message { + + ($($Name:ident: $code:expr,)*) => { + $( + pub struct $Name { + // use reference? pulls in lifetimes though... + raw: RawPacket, + } + + impl core::ops::Deref for $Name { + type Target = RawPacket; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.raw + } + } + + impl core::ops::DerefMut for $Name { + + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.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 for Command { + type Error = PacketError; + + #[inline] + fn try_from(packet: RawPacket) + -> core::result::Result + { + if packet.len() < 10 { + return Err(PacketError::ShortPacket); + } + if packet[5] != 0 { + // wrong slot + } + let command_byte = packet[0]; + Ok(match command_byte { + $( + $code => Command::$Name($Name { raw: packet } ), + )* + _ => return Err(PacketError::UnknownCommand(command_byte)), + }) + } + } + + impl core::ops::Deref for Command { + type Target = RawPacket; + + #[inline] + fn deref(&self) -> &Self::Target { + match self { + $( + Command::$Name(packet) => &packet, + )* + } + } + } + + // 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, + XfrBlock: 0x6f, + Abort: 0x72, +); + +impl PacketWithData for XfrBlock {} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum PowerSelection { + Automatic, + V5, + V3_3, + V1_8, +} + +impl PowerOn { + #[inline(always)] + pub fn power_select(&self) -> PowerSelection { + match &self[7] { + 0 => PowerSelection::Automatic, + 1 => PowerSelection::V5, + 2 => PowerSelection::V3_3, + 3 => PowerSelection::V1_8, + _ => panic!("invalid power select parameter"), + } + } +} + +#[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 { + match self { + Chain::BeginsAndEnds | + Chain::Ends | + Chain::ExpectingMore => true, + _ => false, + } + } +} + +pub enum Response { + // DataBlock(DataBlock), + // SlotStatus(SlotStatus), +} + +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"); + // write!("Command({:?})", &self.command_type())); + // // "Command"); + + debug_struct + .field("cmd", &self.command_type()) + .field("seq", &self.seq()) + ; + + match self { + Command::XfrBlock(block) => { + let l = core::cmp::min(self.len(), 8); + let escaped_bytes: heapless::Vec = + 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)) + } + ; + } + _ => {} + } + + // let mut debug_struct = match self.msg_type() { + // Ok(message_type) => debug_struct.field("type", &message_type), + // Err(()) => debug_struct.field("type", &self[0]), + // }; + + // let has_data = self.len() > 0; + debug_struct + .finish() + } +} + +// impl core::fmt::Debug for Packet { + +// fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { +// let mut debug_struct = f.debug_struct("Packet"); +// let mut debug_struct = match self.msg_type() { +// Ok(message_type) => debug_struct.field("type", &message_type), +// Err(()) => debug_struct.field("type", &self[0]), +// }; + +// let has_data = self.len() > 0; + +// if has_data { +// debug_struct = debug_struct +// .field("len", &self.len()); +// } + +// debug_struct = debug_struct +// // .field("slot", &self.slot()) +// .field("seq", &self.slot()); + +// debug_struct = debug_struct +// .field("power_select", &self.power_select()); + +// debug_struct = debug_struct +// .field("level_parameter", &self.level_parameter()); + +// // other fields? +// // .field("data[..16]", &self.data()[..16]) +// if has_data { +// let l = core::cmp::min(self.len(), 16); +// // one byte can become `\x`, so 4 bytes +// let escaped_bytes: heapless::Vec = +// 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 = debug_struct +// .field("data", &format_args!("b'{}'", data_as_str)); +// } + +// debug_struct +// .finish() +// } +// } + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ClassRequest { + Abort = 1, + GetClockFrequencies = 2, + GetDataRates = 3, +} + +impl core::convert::TryFrom for ClassRequest { + type Error = (); + fn try_from(request: u8) -> core::result::Result { + Ok(match request { + 1 => Self::Abort, + 2 => Self::GetClockFrequencies, + 3 => Self::GetDataRates, + _ => return Err(()), + }) + } +} +