Files
lora-card-reader/pn532/src/protocol/felica/mod.rs
T
2026-09-15 18:08:35 +08:00

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(())
}
}