281 lines
9.3 KiB
Rust
281 lines
9.3 KiB
Rust
pub mod error;
|
|
#[cfg(feature = "felica-lite-s")]
|
|
mod felica_lite_s;
|
|
|
|
use crate::commands::*;
|
|
use crate::driver::Pn532;
|
|
use crate::error::Error;
|
|
use crate::interface::Interface;
|
|
use crate::types::felica::constants::{
|
|
FELICA_READ_MAX_BLOCK_NUM, FELICA_READ_MAX_SERVICE_NUM, FELICA_WRITE_MAX_BLOCK_NUM,
|
|
FELICA_WRITE_MAX_SERVICE_NUM,
|
|
};
|
|
use crate::types::felica::*;
|
|
use crate::types::BaudRate;
|
|
use core::time::Duration;
|
|
use embedded_hal::delay::DelayNs;
|
|
use embedded_hal::digital::{InputPin, OutputPin};
|
|
use error::Error as FelicaError;
|
|
|
|
impl<RST, IRQ, DELAY, B, const N: usize> Pn532<RST, IRQ, DELAY, B, N>
|
|
where
|
|
RST: OutputPin,
|
|
IRQ: InputPin,
|
|
DELAY: DelayNs,
|
|
B: Interface<RST, IRQ, DELAY>,
|
|
{
|
|
fn validate_response(
|
|
response: &[u8],
|
|
response_len: usize,
|
|
valid_len: usize,
|
|
) -> Result<(), Error<B::TransportError>> {
|
|
if response_len >= 11 && (response[9] != 0 || response[10] != 0) {
|
|
return Err(Error::FelicaError(FelicaError::Status(
|
|
response[9],
|
|
response[10],
|
|
)));
|
|
}
|
|
if response_len != valid_len {
|
|
return Err(Error::InvalidFrame);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Poll for a FeliCa card.
|
|
pub fn felica_polling(
|
|
&mut self,
|
|
config: FelicaPollingConfig,
|
|
) -> Result<Option<FelicaPollingResponse>, Error<B::TransportError>> {
|
|
if !matches!(
|
|
config.baud_rate,
|
|
BaudRate::Felica212kbps | BaudRate::Felica424kbps
|
|
) {
|
|
return Err(Error::InvalidBaudRate);
|
|
}
|
|
let system_code = config.system_code;
|
|
self.buffer[0] = PN532Command::InListPassiveTarget.into_bits();
|
|
self.buffer[1] = 1;
|
|
self.buffer[2] = config.baud_rate.into();
|
|
self.buffer[3] = FelicaCommand::Polling.into_bits();
|
|
self.buffer[4] = (system_code >> 8) as u8;
|
|
self.buffer[5] = system_code as u8;
|
|
self.buffer[6] = config.request_code as u8;
|
|
self.buffer[7] = 0;
|
|
self.send(8)?;
|
|
|
|
match self.read_timeout(config.timeout) {
|
|
Err(Error::Timeout) => return Ok(None),
|
|
Err(e) => return Err(e),
|
|
Ok(_) => {}
|
|
}
|
|
|
|
if self.buffer[0] == 0 {
|
|
return Ok(None);
|
|
}
|
|
if self.buffer[0] != 1 {
|
|
return Err(Error::InvalidFrame);
|
|
}
|
|
|
|
self.in_listed_tag = self.buffer[1];
|
|
let response_length = self.buffer[2];
|
|
if response_length != 18 && response_length != 20 {
|
|
return Err(Error::InvalidFrame);
|
|
}
|
|
|
|
let mut idm = [0u8; 8];
|
|
|
|
idm.copy_from_slice(&self.buffer[4..12]);
|
|
let idm = IDm::from(idm);
|
|
|
|
let mut pmm = [0u8; 8];
|
|
pmm.copy_from_slice(&self.buffer[12..20]);
|
|
let pmm = PMm::from(pmm);
|
|
|
|
let system_code_response = if response_length == 20 {
|
|
Some(u16::from_be_bytes([self.buffer[20], self.buffer[21]]))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
Ok(Some(FelicaPollingResponse {
|
|
idm,
|
|
pmm,
|
|
system_code_response,
|
|
}))
|
|
}
|
|
|
|
/// Send a raw FeliCa command to the currently in-listed card.
|
|
///
|
|
/// Returns the number of bytes written to `response`.
|
|
pub(crate) fn felica_send_command(
|
|
&mut self,
|
|
command: &[u8],
|
|
response: &mut [u8],
|
|
timeout: Duration,
|
|
) -> Result<usize, Error<B::TransportError>> {
|
|
if command.len() > 0xFE {
|
|
return Err(Error::InvalidParam);
|
|
}
|
|
self.buffer[0] = PN532Command::InDataExchange.into_bits();
|
|
self.buffer[1] = self.in_listed_tag;
|
|
self.buffer[2] = (command.len() + 1) as u8;
|
|
self.send_with_body(3, command)?;
|
|
|
|
let len = self.read_timeout(timeout)?;
|
|
self.validate_buffer(len)?;
|
|
|
|
let response_len = self.buffer[1] as usize - 1;
|
|
if len - 2 != response_len {
|
|
return Err(Error::InvalidFrame);
|
|
}
|
|
if response_len > response.len() {
|
|
return Err(Error::NoSpace);
|
|
}
|
|
response[..response_len].copy_from_slice(&self.buffer[2..2 + response_len]);
|
|
Ok(response_len)
|
|
}
|
|
|
|
/// Send a FeliCa "Request Response" command, returning the card's mode.
|
|
pub fn felica_request_response(
|
|
&mut self,
|
|
card: &FelicaPollingResponse,
|
|
) -> Result<u8, Error<B::TransportError>> {
|
|
let mut cmd = [0u8; 9];
|
|
cmd[0] = FelicaCommand::RequestResponse.into_bits();
|
|
cmd[1..9].copy_from_slice(&card.idm.into_be_bytes());
|
|
|
|
let mut response = [0u8; 10];
|
|
let response_len =
|
|
self.felica_send_command(&cmd, &mut response, Duration::from_millis(200))?;
|
|
if response_len != 10 {
|
|
return Err(Error::InvalidFrame);
|
|
}
|
|
Ok(response[9])
|
|
}
|
|
|
|
/// Send a FeliCa "Read Without Encryption" command.
|
|
///
|
|
/// Note: the number of blocks is limited by the 64-byte response buffer.
|
|
pub(crate) fn felica_read_without_encryption<const U: usize, const S: usize>(
|
|
&mut self,
|
|
card: &CardSession,
|
|
service_code_list: [impl IntoServiceCode; S],
|
|
block_list: [impl IntoBlockCode; U],
|
|
block_data: &mut [[u8; 16]; U],
|
|
) -> Result<(), Error<B::TransportError>> {
|
|
const COMMAND_SIZE: usize = 1 // command code (0x06)
|
|
+ 8 // idm
|
|
+ 1 // service length
|
|
+ 2 * FELICA_READ_MAX_SERVICE_NUM // service code list (2 * service length)
|
|
+ 1 // block length (1<=n<=4)
|
|
+ 2 * FELICA_READ_MAX_BLOCK_NUM; // block list (2n<=N<=3n), hardcoded to use 2 byte block list only
|
|
const RESPONSE_SIZE: usize = 1 // response code (0x07)
|
|
+ 8 // IDm
|
|
+ 1 // status[0]
|
|
+ 1 // status[1]
|
|
+ 1 // block length
|
|
+ 16 * FELICA_READ_MAX_BLOCK_NUM; // block data
|
|
|
|
// validate
|
|
if S > FELICA_READ_MAX_SERVICE_NUM || U > FELICA_READ_MAX_BLOCK_NUM {
|
|
return Err(Error::InvalidParam);
|
|
}
|
|
|
|
// command
|
|
let mut cmd = heapless::Vec::<u8, COMMAND_SIZE>::new();
|
|
cmd.push(FelicaCommand::ReadWithoutEncryption.into_bits())
|
|
.unwrap();
|
|
cmd.extend_from_slice(&card.idm.into_bytes()).unwrap();
|
|
cmd.push(S as u8).unwrap();
|
|
for sc in service_code_list {
|
|
let sc = sc.into();
|
|
let sc: [u8; 2] = sc.to_le_bytes();
|
|
cmd.extend_from_slice(&sc).unwrap();
|
|
}
|
|
cmd.push(U as u8).unwrap();
|
|
for block in block_list {
|
|
let block: u16 = block.into();
|
|
let block: [u8; 2] = block.to_be_bytes();
|
|
cmd.extend_from_slice(&block).unwrap();
|
|
}
|
|
|
|
// response
|
|
let mut response = [0u8; RESPONSE_SIZE];
|
|
let response_len = self.felica_send_command(
|
|
&cmd,
|
|
&mut response,
|
|
card.pmm
|
|
.get_read_timeout(U, Some(Duration::from_millis(50))),
|
|
)?;
|
|
crate::trace!("Read Response: {:02X}", response);
|
|
|
|
Self::validate_response(&response, response_len, 12 + 16 * U)?;
|
|
|
|
let mut k = 12;
|
|
for block in block_data.iter_mut() {
|
|
block.copy_from_slice(&response[k..k + 16]);
|
|
k += 16;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Send a FeliCa "Write Without Encryption" command.
|
|
pub(crate) fn felica_write_without_encryption<const U: usize, const S: usize>(
|
|
&mut self,
|
|
card: &CardSession,
|
|
service_code_list: [impl IntoServiceCode; S],
|
|
block_list: [impl IntoBlockCode; U],
|
|
block_data: &[[u8; 16]; U],
|
|
) -> Result<(), Error<B::TransportError>> {
|
|
const COMMAND_SIZE: usize = 1 // command code
|
|
+ 8 // IDm
|
|
+ 1 // service len (m)
|
|
+ 2 * FELICA_WRITE_MAX_SERVICE_NUM // service code (2 * m)
|
|
+ 1 // block len (n)
|
|
+ 2 * FELICA_WRITE_MAX_BLOCK_NUM // block list (2n<=N<=3n) (hardcoded to 2 bytes only)
|
|
+ 16 * FELICA_WRITE_MAX_BLOCK_NUM; // block data (16 * n)
|
|
const RESPONSE_SIZE: usize = 1 // command code
|
|
+ 8 // IDm
|
|
+ 1 // status[0]
|
|
+ 1; // status[1]
|
|
|
|
// validate
|
|
if S > FELICA_WRITE_MAX_SERVICE_NUM || U > FELICA_WRITE_MAX_BLOCK_NUM {
|
|
return Err(Error::InvalidParam);
|
|
}
|
|
|
|
// command
|
|
let mut cmd = heapless::Vec::<u8, COMMAND_SIZE>::new();
|
|
cmd.push(FelicaCommand::WriteWithEncryption as u8).unwrap();
|
|
cmd.extend_from_slice(&card.idm.into_bytes()).unwrap();
|
|
cmd.push(S as u8).unwrap();
|
|
for sc in service_code_list {
|
|
let sc: u16 = sc.into();
|
|
let sc = sc.to_le_bytes();
|
|
cmd.extend_from_slice(&sc).unwrap();
|
|
}
|
|
cmd.push(U as u8).unwrap();
|
|
for block in block_list {
|
|
let block: u16 = block.into();
|
|
let block = block.to_be_bytes();
|
|
cmd.extend_from_slice(&block).unwrap();
|
|
}
|
|
for block in block_data.iter().take(U) {
|
|
cmd.extend_from_slice(block).unwrap();
|
|
}
|
|
|
|
// response
|
|
let mut response = [0u8; RESPONSE_SIZE];
|
|
let response_len = self.felica_send_command(
|
|
&cmd,
|
|
&mut response,
|
|
card.pmm
|
|
.get_write_timeout(U, Some(Duration::from_millis(50))),
|
|
)?;
|
|
|
|
Self::validate_response(&response, response_len, RESPONSE_SIZE)?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|