refactor structure

This commit is contained in:
2026-08-30 16:27:44 +08:00
parent 8f65670a8f
commit da83ff6f94
31 changed files with 314 additions and 290 deletions
+1
View File
@@ -5,6 +5,7 @@
<w>felica</w>
<w>gpst</w>
<w>trng</w>
<w>wcnt</w>
</words>
</dictionary>
</component>
-147
View File
@@ -1,147 +0,0 @@
//! Command and protocol constants for the PN532.
// PN532 commands.
pub const PN532_COMMAND_DIAGNOSE: u8 = 0x00;
pub const PN532_COMMAND_GETFIRMWAREVERSION: u8 = 0x02;
pub const PN532_COMMAND_GETGENERALSTATUS: u8 = 0x04;
pub const PN532_COMMAND_READREGISTER: u8 = 0x06;
pub const PN532_COMMAND_WRITEREGISTER: u8 = 0x08;
pub const PN532_COMMAND_READGPIO: u8 = 0x0C;
pub const PN532_COMMAND_WRITEGPIO: u8 = 0x0E;
pub const PN532_COMMAND_SETSERIALBAUDRATE: u8 = 0x10;
pub const PN532_COMMAND_SETPARAMETERS: u8 = 0x12;
pub const PN532_COMMAND_SAMCONFIGURATION: u8 = 0x14;
pub const PN532_COMMAND_POWERDOWN: u8 = 0x16;
pub const PN532_COMMAND_RFCONFIGURATION: u8 = 0x32;
pub const PN532_COMMAND_RFREGULATIONTEST: u8 = 0x58;
pub const PN532_COMMAND_INJUMPFORDEP: u8 = 0x56;
pub const PN532_COMMAND_INJUMPFORPSL: u8 = 0x46;
pub const PN532_COMMAND_INLISTPASSIVETARGET: u8 = 0x4A;
pub const PN532_COMMAND_INATR: u8 = 0x50;
pub const PN532_COMMAND_INPSL: u8 = 0x4E;
pub const PN532_COMMAND_INDATAEXCHANGE: u8 = 0x40;
pub const PN532_COMMAND_INCOMMUNICATETHRU: u8 = 0x42;
pub const PN532_COMMAND_INDESELECT: u8 = 0x44;
pub const PN532_COMMAND_INRELEASE: u8 = 0x52;
pub const PN532_COMMAND_INSELECT: u8 = 0x54;
pub const PN532_COMMAND_INAUTOPOLL: u8 = 0x60;
pub const PN532_COMMAND_TGINITASTARGET: u8 = 0x8C;
pub const PN532_COMMAND_TGSETGENERALBYTES: u8 = 0x92;
pub const PN532_COMMAND_TGGETDATA: u8 = 0x86;
pub const PN532_COMMAND_TGSETDATA: u8 = 0x8E;
pub const PN532_COMMAND_TGSETMETADATA: u8 = 0x94;
pub const PN532_COMMAND_TGGETINITIATORCOMMAND: u8 = 0x88;
pub const PN532_COMMAND_TGRESPONSETOINITIATOR: u8 = 0x90;
pub const PN532_COMMAND_TGGETTARGETSTATUS: u8 = 0x8A;
pub const PN532_RESPONSE_INDATAEXCHANGE: u8 = 0x41;
pub const PN532_RESPONSE_INLISTPASSIVETARGET: u8 = 0x4B;
pub const PN532_MIFARE_ISO14443A: u8 = 0x00;
// Mifare commands.
pub const MIFARE_CMD_AUTH_A: u8 = 0x60;
pub const MIFARE_CMD_AUTH_B: u8 = 0x61;
pub const MIFARE_CMD_READ: u8 = 0x30;
pub const MIFARE_CMD_WRITE: u8 = 0xA0;
pub const MIFARE_CMD_WRITE_ULTRALIGHT: u8 = 0xA2;
pub const MIFARE_CMD_TRANSFER: u8 = 0xB0;
pub const MIFARE_CMD_DECREMENT: u8 = 0xC0;
pub const MIFARE_CMD_INCREMENT: u8 = 0xC1;
pub const MIFARE_CMD_STORE: u8 = 0xC2;
// FeliCa commands.
pub const FELICA_CMD_POLLING: u8 = 0x00;
pub const FELICA_CMD_REQUEST_SERVICE: u8 = 0x02;
pub const FELICA_CMD_REQUEST_RESPONSE: u8 = 0x04;
pub const FELICA_CMD_READ_WITHOUT_ENCRYPTION: u8 = 0x06;
pub const FELICA_CMD_WRITE_WITHOUT_ENCRYPTION: u8 = 0x08;
pub const FELICA_CMD_REQUEST_SYSTEM_CODE: u8 = 0x0C;
/// NDEF URI record identifier codes (see NFC Forum "URI Record Type Definition").
pub mod ndef {
pub const NONE: u8 = 0x00;
pub const HTTP_WWWDOT: u8 = 0x01;
pub const HTTPS_WWWDOT: u8 = 0x02;
pub const HTTP: u8 = 0x03;
pub const HTTPS: u8 = 0x04;
pub const TEL: u8 = 0x05;
pub const MAILTO: u8 = 0x06;
pub const FTP_ANONAT: u8 = 0x07;
pub const FTP_FTPDOT: u8 = 0x08;
pub const FTPS: u8 = 0x09;
pub const SFTP: u8 = 0x0A;
pub const SMB: u8 = 0x0B;
pub const NFS: u8 = 0x0C;
pub const FTP: u8 = 0x0D;
pub const DAV: u8 = 0x0E;
pub const NEWS: u8 = 0x0F;
pub const TELNET: u8 = 0x10;
pub const IMAP: u8 = 0x11;
pub const RTSP: u8 = 0x12;
pub const URN: u8 = 0x13;
pub const POP: u8 = 0x14;
pub const SIP: u8 = 0x15;
pub const SIPS: u8 = 0x16;
pub const TFTP: u8 = 0x17;
pub const BTSPP: u8 = 0x18;
pub const BTL2CAP: u8 = 0x19;
pub const BTGOEP: u8 = 0x1A;
pub const TCPOBEX: u8 = 0x1B;
pub const IRDAOBEX: u8 = 0x1C;
pub const FILE: u8 = 0x1D;
pub const URN_EPC_ID: u8 = 0x1E;
pub const URN_EPC_TAG: u8 = 0x1F;
pub const URN_EPC_PAT: u8 = 0x20;
pub const URN_EPC_RAW: u8 = 0x21;
pub const URN_EPC: u8 = 0x22;
pub const URN_NFC: u8 = 0x23;
}
// GPIO.
pub const PN532_GPIO_VALIDATIONBIT: u8 = 0x80;
pub const PN532_GPIO_P30: u8 = 0;
pub const PN532_GPIO_P31: u8 = 1;
pub const PN532_GPIO_P32: u8 = 2;
pub const PN532_GPIO_P33: u8 = 3;
pub const PN532_GPIO_P34: u8 = 4;
pub const PN532_GPIO_P35: u8 = 5;
pub const FELICA_READ_MAX_SERVICE_NUM: usize = 3;
pub const FELICA_READ_MAX_BLOCK_NUM: usize = 3;
pub const FELICA_WRITE_MAX_SERVICE_NUM: usize = 3;
pub const FELICA_WRITE_MAX_BLOCK_NUM: usize = 3;
pub const FELICA_REQ_SERVICE_MAX_NODE_NUM: usize = 32;
// Frame protocol constants.
pub const PN532_PREAMBLE: u8 = 0x00;
pub const PN532_STARTCODE1: u8 = 0x00;
pub const PN532_STARTCODE2: u8 = 0xFF;
pub const PN532_POSTAMBLE: u8 = 0x00;
pub const PN532_HOST_TO_PN532: u8 = 0xD4;
pub const PN532_PN532_TO_HOST: u8 = 0xD5;
pub const PN532_ACK_WAIT_TIME_MS: u16 = 10;
/// The fixed 7-bit I2C address of the PN532 (0x48 >> 1).
pub const PN532_I2C_ADDRESS: u8 = 0x48 >> 1;
/// Passive target baud rates supported by [`crate::Pn532::read_passive_target_id`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BaudRate {
ISO14443A106kbps = 0x00,
Felica212kbps = 0x01,
Felica424kbps = 0x02,
ISO14443B106kbps = 0x03,
InnovisionJewel106kbps = 0x04,
}
impl From<BaudRate> for u8 {
fn from(value: BaudRate) -> Self {
value as u8
}
}
+10
View File
@@ -0,0 +1,10 @@
// Mifare commands.
pub const MIFARE_CMD_AUTH_A: u8 = 0x60;
pub const MIFARE_CMD_AUTH_B: u8 = 0x61;
pub const MIFARE_CMD_READ: u8 = 0x30;
pub const MIFARE_CMD_WRITE: u8 = 0xA0;
pub const MIFARE_CMD_WRITE_ULTRALIGHT: u8 = 0xA2;
pub const MIFARE_CMD_TRANSFER: u8 = 0xB0;
pub const MIFARE_CMD_DECREMENT: u8 = 0xC0;
pub const MIFARE_CMD_INCREMENT: u8 = 0xC1;
pub const MIFARE_CMD_STORE: u8 = 0xC2;
+51
View File
@@ -0,0 +1,51 @@
//! Command and protocol constants for the PN532.
pub mod ndef;
#[cfg(feature = "mifare-classic")]
pub mod mifare;
pub mod pn532;
// PN532 commands.
pub const PN532_RESPONSE_INDATAEXCHANGE: u8 = 0x41;
pub const PN532_RESPONSE_INLISTPASSIVETARGET: u8 = 0x4B;
pub const PN532_MIFARE_ISO14443A: u8 = 0x00;
// FeliCa commands.
pub const FELICA_CMD_POLLING: u8 = 0x00;
pub const FELICA_CMD_REQUEST_SERVICE: u8 = 0x02;
pub const FELICA_CMD_REQUEST_RESPONSE: u8 = 0x04;
pub const FELICA_CMD_READ_WITHOUT_ENCRYPTION: u8 = 0x06;
pub const FELICA_CMD_WRITE_WITHOUT_ENCRYPTION: u8 = 0x08;
pub const FELICA_CMD_REQUEST_SYSTEM_CODE: u8 = 0x0C;
// GPIO.
pub const PN532_GPIO_VALIDATIONBIT: u8 = 0x80;
pub const PN532_GPIO_P30: u8 = 0;
pub const PN532_GPIO_P31: u8 = 1;
pub const PN532_GPIO_P32: u8 = 2;
pub const PN532_GPIO_P33: u8 = 3;
pub const PN532_GPIO_P34: u8 = 4;
pub const PN532_GPIO_P35: u8 = 5;
pub const FELICA_READ_MAX_SERVICE_NUM: usize = 3;
pub const FELICA_READ_MAX_BLOCK_NUM: usize = 3;
pub const FELICA_WRITE_MAX_SERVICE_NUM: usize = 3;
pub const FELICA_WRITE_MAX_BLOCK_NUM: usize = 3;
pub const FELICA_REQ_SERVICE_MAX_NODE_NUM: usize = 32;
// Frame protocol constants.
pub const PN532_PREAMBLE: u8 = 0x00;
pub const PN532_STARTCODE1: u8 = 0x00;
pub const PN532_STARTCODE2: u8 = 0xFF;
pub const PN532_POSTAMBLE: u8 = 0x00;
pub const PN532_HOST_TO_PN532: u8 = 0xD4;
pub const PN532_PN532_TO_HOST: u8 = 0xD5;
pub const PN532_ACK_WAIT_TIME_MS: u16 = 10;
/// The fixed 7-bit I2C address of the PN532 (0x48 >> 1).
pub const PN532_I2C_ADDRESS: u8 = 0x48 >> 1;
+38
View File
@@ -0,0 +1,38 @@
///! NDEF URI record identifier codes (see NFC Forum "URI Record Type Definition").
pub const NONE: u8 = 0x00;
pub const HTTP_WWWDOT: u8 = 0x01;
pub const HTTPS_WWWDOT: u8 = 0x02;
pub const HTTP: u8 = 0x03;
pub const HTTPS: u8 = 0x04;
pub const TEL: u8 = 0x05;
pub const MAILTO: u8 = 0x06;
pub const FTP_ANONAT: u8 = 0x07;
pub const FTP_FTPDOT: u8 = 0x08;
pub const FTPS: u8 = 0x09;
pub const SFTP: u8 = 0x0A;
pub const SMB: u8 = 0x0B;
pub const NFS: u8 = 0x0C;
pub const FTP: u8 = 0x0D;
pub const DAV: u8 = 0x0E;
pub const NEWS: u8 = 0x0F;
pub const TELNET: u8 = 0x10;
pub const IMAP: u8 = 0x11;
pub const RTSP: u8 = 0x12;
pub const URN: u8 = 0x13;
pub const POP: u8 = 0x14;
pub const SIP: u8 = 0x15;
pub const SIPS: u8 = 0x16;
pub const TFTP: u8 = 0x17;
pub const BTSPP: u8 = 0x18;
pub const BTL2CAP: u8 = 0x19;
pub const BTGOEP: u8 = 0x1A;
pub const TCPOBEX: u8 = 0x1B;
pub const IRDAOBEX: u8 = 0x1C;
pub const FILE: u8 = 0x1D;
pub const URN_EPC_ID: u8 = 0x1E;
pub const URN_EPC_TAG: u8 = 0x1F;
pub const URN_EPC_PAT: u8 = 0x20;
pub const URN_EPC_RAW: u8 = 0x21;
pub const URN_EPC: u8 = 0x22;
pub const URN_NFC: u8 = 0x23;
+32
View File
@@ -0,0 +1,32 @@
pub const DIAGNOSE: u8 = 0x00;
pub const GETFIRMWAREVERSION: u8 = 0x02;
pub const GETGENERALSTATUS: u8 = 0x04;
pub const READREGISTER: u8 = 0x06;
pub const WRITEREGISTER: u8 = 0x08;
pub const READGPIO: u8 = 0x0C;
pub const WRITEGPIO: u8 = 0x0E;
pub const SETSERIALBAUDRATE: u8 = 0x10;
pub const SETPARAMETERS: u8 = 0x12;
pub const SAMCONFIGURATION: u8 = 0x14;
pub const POWERDOWN: u8 = 0x16;
pub const RFCONFIGURATION: u8 = 0x32;
pub const RFREGULATIONTEST: u8 = 0x58;
pub const INJUMPFORDEP: u8 = 0x56;
pub const INJUMPFORPSL: u8 = 0x46;
pub const INLISTPASSIVETARGET: u8 = 0x4A;
pub const INATR: u8 = 0x50;
pub const INPSL: u8 = 0x4E;
pub const INDATAEXCHANGE: u8 = 0x40;
pub const INCOMMUNICATETHRU: u8 = 0x42;
pub const INDESELECT: u8 = 0x44;
pub const INRELEASE: u8 = 0x52;
pub const INSELECT: u8 = 0x54;
pub const INAUTOPOLL: u8 = 0x60;
pub const TGINITASTARGET: u8 = 0x8C;
pub const TGSETGENERALBYTES: u8 = 0x92;
pub const TGGETDATA: u8 = 0x86;
pub const TGSETDATA: u8 = 0x8E;
pub const TGSETMETADATA: u8 = 0x94;
pub const TGGETINITIATORCOMMAND: u8 = 0x88;
pub const TGRESPONSETOINITIATOR: u8 = 0x90;
pub const TGGETTARGETSTATUS: u8 = 0x8A;
+9 -9
View File
@@ -88,7 +88,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// Read the PN532 firmware version and ID.
pub fn get_firmware_version(&mut self) -> Result<u32, Error<B::TransportError>> {
self.buffer[0] = PN532_COMMAND_GETFIRMWAREVERSION;
self.buffer[0] = pn532::GETFIRMWAREVERSION;
self.send(1)?;
let len = self.read()?;
if len < 4 {
@@ -104,7 +104,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// Read a 16-bit PN532 register.
pub fn read_register(&mut self, reg: u16) -> Result<u8, Error<B::TransportError>> {
self.buffer[0] = PN532_COMMAND_READREGISTER;
self.buffer[0] = pn532::READREGISTER;
self.buffer[1] = (reg >> 8) as u8;
self.buffer[2] = reg as u8;
self.send(3)?;
@@ -117,7 +117,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// Write to a 16-bit PN532 register.
pub fn write_register(&mut self, reg: u16, val: u8) -> Result<(), Error<B::TransportError>> {
self.buffer[0] = PN532_COMMAND_WRITEREGISTER;
self.buffer[0] = pn532::WRITEREGISTER;
self.buffer[1] = (reg >> 8) as u8;
self.buffer[2] = reg as u8;
self.buffer[3] = val;
@@ -129,7 +129,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// Set the PN532's GPIO pins (see the PN532 user manual for valid pins).
pub fn write_gpio(&mut self, pinstate: u8) -> Result<(), Error<B::TransportError>> {
let pinstate = pinstate | (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34);
self.buffer[0] = PN532_COMMAND_WRITEGPIO;
self.buffer[0] = pn532::WRITEGPIO;
self.buffer[1] = PN532_GPIO_VALIDATIONBIT | pinstate;
self.buffer[2] = 0x00;
self.send(3)?;
@@ -142,7 +142,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// Read the state of the PN532's GPIO pins.
pub fn read_gpio(&mut self) -> Result<u8, Error<B::TransportError>> {
self.buffer[0] = PN532_COMMAND_READGPIO;
self.buffer[0] = pn532::READGPIO;
self.send(1)?;
self.read()?;
Ok(self.buffer[0])
@@ -150,7 +150,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// Configure the SAM (Secure Access Module) in normal mode.
pub fn sam_config(&mut self) -> Result<(), Error<B::TransportError>> {
self.buffer[0] = PN532_COMMAND_SAMCONFIGURATION;
self.buffer[0] = pn532::SAMCONFIGURATION;
self.buffer[1] = 0x01; // normal mode
self.buffer[2] = 0x14; // timeout 50 ms * 20 = 1 second
self.buffer[3] = 0x01; // use IRQ pin
@@ -164,7 +164,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
&mut self,
max_retries: u8,
) -> Result<(), Error<B::TransportError>> {
self.buffer[0] = PN532_COMMAND_RFCONFIGURATION;
self.buffer[0] = pn532::RFCONFIGURATION;
self.buffer[1] = 5;
self.buffer[2] = 0xFF;
self.buffer[3] = 0x01;
@@ -180,7 +180,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
auto_rfca: u8,
rf_on_off: u8,
) -> Result<(), Error<B::TransportError>> {
self.buffer[0] = PN532_COMMAND_RFCONFIGURATION;
self.buffer[0] = pn532::RFCONFIGURATION;
self.buffer[1] = 1;
self.buffer[2] = 0x00 | auto_rfca | rf_on_off;
self.send(3)?;
@@ -201,7 +201,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// Release the card.
pub fn release(&mut self) -> Result<(), Error<B::TransportError>> {
self.buffer[0] = PN532_COMMAND_INRELEASE;
self.buffer[0] = pn532::INRELEASE;
self.buffer[1] = 0x00;
self.send(2)?;
+1 -1
View File
@@ -1,6 +1,6 @@
use core::fmt::Debug;
use bitfields::bitflag;
use crate::felica;
use crate::protocol::felica;
/// Driver-level error type, generic over the transport (I2C) error.
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
+3 -7
View File
@@ -47,15 +47,11 @@ extern crate alloc;
pub mod commands;
pub mod error;
#[cfg(feature = "felica")]
pub mod felica;
pub mod interface;
pub(crate) mod driver;
pub mod tg;
#[cfg(feature = "mifare-classic")]
pub mod mifare;
#[cfg(feature = "iso14443a")]
pub mod iso14443a;
pub mod types;
pub mod protocol;
pub use commands::*;
pub use driver::*;
@@ -64,7 +60,7 @@ pub use interface::*;
#[cfg(feature = "defmt")]
#[allow(unused_imports)]
use defmt::{error, warn, info, debug, trace};
use defmt::{debug, error, info, trace, warn};
#[cfg(not(feature = "defmt"))]
#[macro_export]
@@ -1,52 +1,12 @@
pub mod issuance;
use des::cipher::{Block, BlockCipherEncrypt, Key, KeyInit};
use des::TdesEde2;
use rand_core::Rng;
use crate::{Interface, Pn532};
use crate::felica::error::Error;
use crate::felica::types::{AsBlock, CardSession, FelicaLiteSBlock, FelicaLiteSServiceCode};
use crate::felica::types::felica_lite_s::auth::{AuthSession, CardKey, MacInput, RandomChallenge, SessionKeys};
use crate::types::felica::{CardSession, FelicaLiteSBlock, FelicaLiteSServiceCode};
use crate::types::felica::felica_lite_s::auth::{AuthSession, CardKey, MacInput, RandomChallenge, SessionKeys};
impl<B: Interface, const N: usize> Pn532<B, N> {
pub fn try_authenticate(&mut self, rng: &mut impl Rng, card: &CardSession, key: &CardKey)
-> Result<Option<AuthSession>, crate::Error<B::TransportError>>
{
let rc = self.set_random_challenge(card, rng)?;
let sk = self.get_session_keys(&rc, key)?;
let session = AuthSession::new(sk, rc);
let is_valid_auth = self.check_auth(card, &session)?;
if is_valid_auth {
Ok(Some(session))
} else {
Ok(None)
}
}
pub fn read_with_mac(
&mut self,
card: &CardSession,
session: &AuthSession,
block: impl AsBlock
) -> Result<[u8; 16], crate::Error<B::TransportError>> {
let mut block_data = [[0u8; 16]; 2];
let block_list = [block.as_block(), FelicaLiteSBlock::MACA as u16];
self.felica_read_without_encryption(
card,
&[FelicaLiteSServiceCode::Read],
&block_list,
&mut block_data
)?;
let input = MacInput::new(&block_data, &block_list);
let mac = self.compute_mac(session, &input);
if mac != input.maca() {
return Err(crate::Error::FelicaError(Error::MACMismatch));
}
Ok(block_data[0])
}
fn set_random_challenge(&mut self, card: &CardSession, rng: &mut impl Rng)
pub(crate) fn set_random_challenge(&mut self, card: &CardSession, rng: &mut impl Rng)
-> Result<RandomChallenge, crate::Error<B::TransportError>>
{
let mut data = [[0u8; 16]];
@@ -61,7 +21,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
Ok(rc)
}
fn get_session_keys(
pub(crate) fn get_session_keys(
&mut self,
rc: &RandomChallenge,
card_key: &CardKey,
@@ -82,7 +42,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
// crate::trace!("SK2: {:02X}", sk2);
}
fn compute_mac<const U: usize>(
pub(crate) fn compute_mac<const U: usize>(
&mut self,
session: &AuthSession,
input: &MacInput<U>
@@ -149,7 +109,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
mac
}
fn check_auth(
pub(crate) fn check_auth(
&mut self,
card: &CardSession,
session: &AuthSession,
@@ -0,0 +1,70 @@
pub mod issuance;
pub mod auth;
use rand_core::Rng;
use crate::{Interface, Pn532};
use crate::protocol::felica::error::Error;
use crate::types::felica::{AsBlock, CardSession, FelicaLiteSBlock, FelicaLiteSServiceCode};
use crate::types::felica::felica_lite_s::auth::{AuthSession, CardKey, MacInput};
impl<B: Interface, const N: usize> Pn532<B, N> {
pub fn try_authenticate(&mut self, rng: &mut impl Rng, card: &CardSession, key: &CardKey)
-> Result<Option<AuthSession>, crate::Error<B::TransportError>>
{
let rc = self.set_random_challenge(card, rng)?;
let sk = self.get_session_keys(&rc, key)?;
let session = AuthSession::new(sk, rc);
let is_valid_auth = self.check_auth(card, &session)?;
if is_valid_auth {
Ok(Some(session))
} else {
Ok(None)
}
}
pub fn read_with_mac(
&mut self,
card: &CardSession,
session: &AuthSession,
block: impl AsBlock
) -> Result<[u8; 16], crate::Error<B::TransportError>> {
let mut block_data = [[0u8; 16]; 2];
let block_list = [block.as_block(), FelicaLiteSBlock::MACA as u16];
self.read_block(card, &block_list, &mut block_data)?;
let input = MacInput::new(&block_data, &block_list);
let mac = self.compute_mac(session, &input);
if mac != input.maca() {
return Err(crate::Error::FelicaError(Error::MACMismatch));
}
Ok(block_data[0])
}
pub fn read_block<const U: usize>(
&mut self,
card: &CardSession,
list: &[impl AsBlock; U],
data: &mut [[u8; 16]; U]
) -> Result<(), crate::Error<B::TransportError>> {
self.felica_read_without_encryption(
card,
&[FelicaLiteSServiceCode::Read],
list,
data
)
}
pub fn write_block<const U: usize>(
&mut self,
card: &CardSession,
list: &[impl AsBlock; U],
data: &[[u8; 16]; U]
) -> Result<(), crate::Error<B::TransportError>> {
self.felica_write_without_encryption(
card,
&[FelicaLiteSServiceCode::Write],
list,
data
)
}
}
@@ -1,4 +1,3 @@
pub mod types;
#[cfg(feature="felica-lite-s")]
mod felica_lite_s;
pub mod error;
@@ -8,9 +7,9 @@ use crate::commands::*;
use crate::driver::Pn532;
use crate::error::Error;
use crate::interface::Interface;
use crate::BaudRate;
use core::time::Duration;
use types::*;
use crate::types::felica::*;
use crate::types::BaudRate;
impl<B: Interface, const N: usize> Pn532<B, N> {
/// Poll for a FeliCa card.
@@ -25,7 +24,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
return Err(Error::InvalidBaudRate);
}
let system_code = system_code.unwrap_or(0xFFFF);
self.buffer[0] = PN532_COMMAND_INLISTPASSIVETARGET;
self.buffer[0] = pn532::INLISTPASSIVETARGET;
self.buffer[1] = 1;
self.buffer[2] = baud_rate.into();
self.buffer[3] = FELICA_CMD_POLLING;
@@ -79,7 +78,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// Send a raw FeliCa command to the currently in-listed card.
///
/// Returns the number of bytes written to `response`.
pub fn felica_send_command(
pub(crate) fn felica_send_command(
&mut self,
command: &[u8],
response: &mut [u8],
@@ -88,7 +87,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
if command.len() > 0xFE {
return Err(Error::InvalidParam);
}
self.buffer[0] = PN532_COMMAND_INDATAEXCHANGE;
self.buffer[0] = pn532::INDATAEXCHANGE;
self.buffer[1] = self.in_listed_tag;
self.buffer[2] = (command.len() + 1) as u8;
self.send_with_body(3, command)?;
@@ -124,12 +123,12 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// Send a FeliCa "Read Without Encryption" command.
///
/// Note: the number of blocks is limited by the 64-byte response buffer.
pub fn felica_read_without_encryption(
pub(crate) fn felica_read_without_encryption<const U: usize>(
&mut self,
card: &CardSession,
service_code_list: &[impl AsServiceCode],
block_list: &[impl AsBlock],
block_data: &mut [[u8; 16]],
block_list: &[impl AsBlock; U],
block_data: &mut [[u8; 16]; U],
) -> Result<(), Error<B::TransportError>> {
const COMMAND_SIZE: usize =
1 // command code (0x06)
@@ -148,10 +147,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
// validate
let num_service = service_code_list.len();
let num_block = block_list.len();
if num_service > FELICA_READ_MAX_SERVICE_NUM
|| num_block > FELICA_READ_MAX_BLOCK_NUM
|| num_block > block_data.len()
if num_service > FELICA_READ_MAX_SERVICE_NUM || U > FELICA_READ_MAX_BLOCK_NUM
{
return Err(Error::InvalidParam);
}
@@ -165,7 +161,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
let sc: [u8; 2] = sc.as_service_code().to_le_bytes();
cmd.extend_from_slice(&sc).unwrap();
}
cmd.push(num_block as u8).unwrap();
cmd.push(U as u8).unwrap();
for bl in block_list {
let bl: [u8; 2] = bl.as_block().to_be_bytes();
cmd.extend_from_slice(&bl).unwrap();
@@ -179,10 +175,10 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
let response_len = self.felica_send_command(
&cmd,
&mut response,
card.pmm.get_read_timeout(num_block, Some(Duration::from_millis(50)))
card.pmm.get_read_timeout(U, Some(Duration::from_millis(50)))
)?;
crate::trace!("Read Response: {:02X}", response);
if response_len != 12 + 16 * num_block {
if response_len != 12 + 16 * U {
return Err(Error::InvalidFrame);
}
if response[9] != 0 || response[10] != 0 {
@@ -190,7 +186,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
}
let mut k = 12;
for block in block_data.iter_mut().take(num_block) {
for block in block_data.iter_mut().take(U) {
block.copy_from_slice(&response[k..k + 16]);
k += 16;
}
@@ -198,12 +194,12 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
}
/// Send a FeliCa "Write Without Encryption" command.
pub fn felica_write_without_encryption(
pub(crate) fn felica_write_without_encryption<const U: usize>(
&mut self,
card: &CardSession,
service_code_list: &[impl AsServiceCode],
block_list: &[impl AsBlock],
block_data: &[[u8; 16]],
block_list: &[impl AsBlock; U],
block_data: &[[u8; 16]; U],
) -> Result<(), Error<B::TransportError>> {
const COMMAND_SIZE: usize =
1 // command code
@@ -221,10 +217,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
// validate
let num_service = service_code_list.len();
let num_block = block_list.len();
if num_service > FELICA_WRITE_MAX_SERVICE_NUM
|| num_block > FELICA_WRITE_MAX_BLOCK_NUM
|| num_block > block_data.len()
if num_service > FELICA_WRITE_MAX_SERVICE_NUM || U > FELICA_WRITE_MAX_BLOCK_NUM
{
return Err(Error::InvalidParam);
}
@@ -238,12 +231,12 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
let sc = sc.as_service_code().to_le_bytes();
cmd.extend_from_slice(&sc).unwrap();
}
cmd.push(num_block as u8).unwrap();
cmd.push(U as u8).unwrap();
for block in block_list {
let block = block.as_block().to_be_bytes();
cmd.extend_from_slice(&block).unwrap();
}
for block in block_data.iter().take(num_block) {
for block in block_data.iter().take(U) {
cmd.extend_from_slice(block).unwrap();
}
@@ -252,7 +245,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
let response_len = self.felica_send_command(
&cmd,
&mut response,
card.pmm.get_write_timeout(num_block, Some(Duration::from_millis(50)))
card.pmm.get_write_timeout(U, Some(Duration::from_millis(50)))
)?;
if response_len != RESPONSE_SIZE {
return Err(Error::InvalidFrame);
+6
View File
@@ -0,0 +1,6 @@
#[cfg(feature = "felica")]
pub mod felica;
#[cfg(feature = "mifare-classic")]
pub mod mifare;
#[cfg(feature = "iso14443a")]
pub mod iso14443a;
+5 -5
View File
@@ -1,4 +1,4 @@
use crate::commands::{PN532_COMMAND_TGGETDATA, PN532_COMMAND_TGINITASTARGET, PN532_COMMAND_TGSETDATA};
use crate::commands::*;
use crate::error::StatusCode;
use crate::{Error, Interface, Pn532, TargetInitStatus};
use core::time::Duration;
@@ -24,7 +24,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
timeout_ms: u16,
) -> Result<TargetInitStatus, Error<B::TransportError>> {
const COMMAND: [u8; 44] = [
PN532_COMMAND_TGINITASTARGET, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0xFE,
pn532::TGINITASTARGET, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0xFE,
0x0F, 0xBB, 0xBA, 0xA6, 0xC9, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0x01, 0xFE, 0x0F, 0xBB, 0xBA, 0xA6, 0xC9, 0x89, 0x00, 0x00, 0x06, 0x46,
0x66, 0x6D, 0x01, 0x01, 0x10, 0x00,
@@ -34,7 +34,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// Retrieve data received from the initiator.
pub fn tg_get_data(&mut self, buf: &mut [u8]) -> Result<usize, Error<B::TransportError>> {
self.buffer[0] = PN532_COMMAND_TGGETDATA;
self.buffer[0] = pn532::TGGETDATA;
self.send(1)?;
let len = self.read_timeout(Duration::from_millis(3000))?;
@@ -57,10 +57,10 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
body: &[u8],
) -> Result<(), Error<B::TransportError>> {
if header.len() > N - 1 {
self.buffer[0] = PN532_COMMAND_TGSETDATA;
self.buffer[0] = pn532::TGSETDATA;
self.interface.write_command(&self.buffer[..1], header)?;
} else {
self.buffer[0] = PN532_COMMAND_TGSETDATA;
self.buffer[0] = pn532::TGSETDATA;
self.buffer[1..1 + header.len()].copy_from_slice(header);
self.interface
.write_command(&self.buffer[..1 + header.len()], body)?;
@@ -1,4 +1,4 @@
use crate::felica::types::AsBlock;
use crate::types::felica::AsBlock;
use crate::FELICA_READ_MAX_BLOCK_NUM;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
@@ -26,13 +26,6 @@ impl<const N: usize> MacInput<N> {
}
}
pub fn as_vec(&self) -> heapless::Vec<u8, 64> {
let mut data = heapless::Vec::new();
data.extend(self.block_list());
data.extend_from_slice(&self.block_data()).unwrap();
data
}
pub fn block_data(&self) -> heapless::Vec<u8, 64> {
let mut data = heapless::Vec::<u8, 64>::new();
for block in self.block_data.iter().take(N - 1) {
@@ -1,6 +1,6 @@
use cbc::cipher::{Block, BlockCipherEncrypt};
use des::TdesEde2;
use crate::felica::types::felica_lite_s::auth::RandomChallenge;
use crate::types::felica::felica_lite_s::auth::RandomChallenge;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -1,10 +1,12 @@
pub mod auth;
use core::fmt::{Display, Formatter};
///! Types for Felica Lite-S (RC-S966)
///! User Manual: https://www.sony.net/Products/felica/business/tech-support/data/fls_usmnl_1.4e.pdf
use bitfields::bitflag;
use super::AsBlock;
use crate::types::felica::polling::PollingResponse;
use super::{AsBlock, IDm, PMm};
use super::AsServiceCode;
/// Service Code
@@ -66,7 +68,7 @@ pub enum Block {
/// Memory configuration block.
MemoryConfig = 0x8088,
/// Write counter block.
WriteCounter = 0x8090,
WCNT = 0x8090,
/// MAC_A block.
MACA = 0x8091
}
@@ -5,14 +5,13 @@ mod idm;
pub mod felica_lite_s;
pub mod ic_type;
use core::fmt::{Display, Formatter};
pub use idm::*;
pub use pmm::*;
pub use polling::PollingRequestCode as FelicaPollingRequestCode;
pub use polling::PollingResponse as FelicaPollingResponse;
pub use polling::CardSession;
#[cfg(feature = "felica-lite-s")]
pub use felica_lite_s::{Block as FelicaLiteSBlock, ServiceCode as FelicaLiteSServiceCode};
use crate::felica::types::polling::PollingResponse;
pub trait AsServiceCode {
fn as_service_code(&self) -> u16;
@@ -38,32 +37,3 @@ impl AsServiceCode for u16 {
val
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[readonly::make]
pub struct CardSession {
pub idm: IDm,
pub pmm: PMm
}
impl CardSession {
pub fn new(idm: IDm, pmm: PMm) -> Self {
Self { idm, pmm }
}
}
impl From<PollingResponse> for CardSession {
fn from(value: PollingResponse) -> Self {
Self {
idm: value.idm,
pmm: value.pmm
}
}
}
impl Display for CardSession {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "IDm: {}, PMm: {}", self.idm, self.pmm)
}
}
@@ -1,5 +1,5 @@
use alloc::string::ToString;
use core::fmt::Display;
use core::fmt::{Display, Formatter};
use bitfields::bitflag;
use super::{IDm, PMm};
@@ -47,3 +47,32 @@ impl Display for PollingResponse {
write!(f, "IDm: {}, PMm, {}, System Code: {}", self.idm, self.pmm, system_code)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[readonly::make]
pub struct CardSession {
pub idm: IDm,
pub pmm: PMm
}
impl CardSession {
pub fn new(idm: IDm, pmm: PMm) -> Self {
Self { idm, pmm }
}
}
impl From<PollingResponse> for CardSession {
fn from(value: PollingResponse) -> Self {
Self {
idm: value.idm,
pmm: value.pmm
}
}
}
impl Display for CardSession {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "IDm: {}, PMm: {}", self.idm, self.pmm)
}
}
+19
View File
@@ -0,0 +1,19 @@
#[cfg(feature = "felica")]
pub mod felica;
/// Passive target baud rates supported by [`crate::Pn532::read_passive_target_id`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BaudRate {
ISO14443A106kbps = 0x00,
Felica212kbps = 0x01,
Felica424kbps = 0x02,
ISO14443B106kbps = 0x03,
InnovisionJewel106kbps = 0x04,
}
impl From<BaudRate> for u8 {
fn from(value: BaudRate) -> Self {
value as u8
}
}
+4 -3
View File
@@ -4,9 +4,10 @@ use embassy_sync::once_lock::OnceLock;
use embassy_time::{Delay, Timer};
use esp_hal::gpio;
use esp_hal::rng::Trng;
use pn532::felica::types::{CardSession, FelicaLiteSBlock, FelicaPollingRequestCode};
use pn532::{BaudRate, Pn532};
use pn532::felica::types::felica_lite_s::auth::CardKey;
use pn532::types::felica::{CardSession, FelicaLiteSBlock, FelicaPollingRequestCode};
use pn532::{Pn532};
use pn532::types::felica::felica_lite_s::auth::CardKey;
use pn532::types::BaudRate;
static CARD_KEY: OnceLock<CardKey> = OnceLock::new();
const CARD_KEY_STR: &str = env!("CARD_KEY");