From 13e64ab91a42d83b81e6745a7a009c4d16c16290 Mon Sep 17 00:00:00 2001 From: fromost Date: Sun, 30 Aug 2026 17:37:36 +0800 Subject: [PATCH] Another refactor --- pn532/src/commands/felica.rs | 16 +++ pn532/src/commands/mifare.rs | 28 +++-- pn532/src/commands/mod.rs | 55 ++-------- pn532/src/commands/ndef.rs | 38 ------- pn532/src/commands/pn532.rs | 81 ++++++++------ pn532/src/driver.rs | 58 +++------- pn532/src/error.rs | 2 + pn532/src/interface.rs | 60 +++++----- .../protocol/felica/felica_lite_s/issuance.rs | 2 +- pn532/src/protocol/felica/mod.rs | 103 +++--------------- pn532/src/protocol/iso14443a/mod.rs | 2 + pn532/src/protocol/mifare/mod.rs | 2 + pn532/src/protocol/mod.rs | 4 +- pn532/src/tg.rs | 23 +++- pn532/src/types/constants.rs | 34 ++++++ pn532/src/types/felica/constants.rs | 5 + .../felica/felica_lite_s/auth/mac_input.rs | 2 +- pn532/src/types/felica/felica_lite_s/mod.rs | 6 +- pn532/src/types/felica/mod.rs | 49 +++++++++ pn532/src/types/felica/polling.rs | 24 +++- pn532/src/types/frame.rs | 12 ++ pn532/src/types/mod.rs | 10 +- src/components/card_reader.rs | 5 +- 23 files changed, 313 insertions(+), 308 deletions(-) create mode 100644 pn532/src/commands/felica.rs delete mode 100644 pn532/src/commands/ndef.rs create mode 100644 pn532/src/types/constants.rs create mode 100644 pn532/src/types/felica/constants.rs create mode 100644 pn532/src/types/frame.rs diff --git a/pn532/src/commands/felica.rs b/pn532/src/commands/felica.rs new file mode 100644 index 0000000..21d87d9 --- /dev/null +++ b/pn532/src/commands/felica.rs @@ -0,0 +1,16 @@ +use bitfields::bitflag; + +///! FeliCa commands. + +#[derive(PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[bitflag(u8)] +pub enum Command { + #[base] + Polling = 0x00, + RequestService = 0x02, + RequestResponse = 0x04, + ReadWithoutEncryption = 0x06, + WriteWithEncryption = 0x08, + RequestSystemCode = 0x0C +} \ No newline at end of file diff --git a/pn532/src/commands/mifare.rs b/pn532/src/commands/mifare.rs index 06600b4..cd018fb 100644 --- a/pn532/src/commands/mifare.rs +++ b/pn532/src/commands/mifare.rs @@ -1,10 +1,20 @@ // 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; \ No newline at end of file + +use bitfields::bitflag; + +#[derive(PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[bitflag(u8)] +pub enum Command { + #[base] + Unknown = 0x00, + AuthA = 0x60, + AuthB = 0x61, + Read = 0x30, + Write = 0xA0, + WriteUltralight = 0xA2, + Transfer = 0xB0, + Decrement = 0xC0, + Increment = 0xC1, + Store = 0xC2 +} \ No newline at end of file diff --git a/pn532/src/commands/mod.rs b/pn532/src/commands/mod.rs index cabc43a..e083b77 100644 --- a/pn532/src/commands/mod.rs +++ b/pn532/src/commands/mod.rs @@ -1,51 +1,14 @@ //! Command and protocol constants for the PN532. -pub mod ndef; +mod pn532; +pub use pn532::Command as PN532Command; + #[cfg(feature = "mifare-classic")] pub mod mifare; -pub mod pn532; +#[cfg(feature = "mifare-classic")] +use mifare::Command as MifareCommand; -// 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; +#[cfg(feature = "felica")] +mod felica; +#[cfg(feature = "felica")] +pub use felica::Command as FelicaCommand; diff --git a/pn532/src/commands/ndef.rs b/pn532/src/commands/ndef.rs deleted file mode 100644 index 727c471..0000000 --- a/pn532/src/commands/ndef.rs +++ /dev/null @@ -1,38 +0,0 @@ -///! 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; \ No newline at end of file diff --git a/pn532/src/commands/pn532.rs b/pn532/src/commands/pn532.rs index 5c5ea94..8900862 100644 --- a/pn532/src/commands/pn532.rs +++ b/pn532/src/commands/pn532.rs @@ -1,32 +1,49 @@ -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; +use bitfields::bitflag; + +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[bitflag(u8)] +pub enum Command { + #[base] + Diagnose = 0x00, + GetFirmwareVersion = 0x02, + GetGeneralStatus = 0x04, + ReadRegister = 0x06, + WriteRegister = 0x08, + ReadGpio = 0x0C, + WriteGpio = 0x0E, + SetSerialBaudRate = 0x10, + SetParameters = 0x12, + SamConfiguration = 0x14, + PowerDown = 0x16, + + RfConfiguration = 0x32, + RfRegulationTest = 0x58, + + InJumpForDep = 0x56, + InJumpForPsl = 0x46, + InListPassiveTarget = 0x4A, + InAtr = 0x50, + InPsl = 0x4E, + InDataExchange = 0x40, + InCommunicateThru = 0x42, + InDeselect = 0x44, + InRelease = 0x52, + InSelect = 0x54, + InAutoPoll = 0x60, + + TgInitAsTarget = 0x8C, + TgSetGeneralBytes = 0x92, + TgGetData = 0x86, + TgSetData = 0x8E, + TgSetMetadata = 0x94, + TgGetInitiatorCommand = 0x88, + TgResponseToInitiator = 0x90, + TgGetTargetStatus = 0x8A, +} + +impl Command { + pub fn as_response(&self) -> u8 { + self.into_bits().wrapping_add(1) + } +} \ No newline at end of file diff --git a/pn532/src/driver.rs b/pn532/src/driver.rs index dd99bd7..ea322dd 100644 --- a/pn532/src/driver.rs +++ b/pn532/src/driver.rs @@ -5,38 +5,7 @@ use crate::error::Error; use crate::interface::Interface; use crate::StatusCode; use core::time::Duration; - -/// A card UID read by [`Pn532::read_passive_target_id`]. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Uid { - pub bytes: heapless::Vec, -} - -impl core::fmt::Display for Uid { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - #[cfg(feature = "alloc")] - { - write!(f, "{}", hex::encode(&self.bytes)) - } - #[cfg(not(feature = "alloc"))] - { - write!(f, "{:?}", self) - } - } -} - -/// Outcome of [`Pn532::tg_init_as_target`] / [`Pn532::tg_init_as_target_default`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum TargetInitStatus { - /// The PN532 entered target mode. - Success, - /// No initiator appeared within the timeout. - Timeout, - /// The operation failed. - Failed, -} +use crate::types::constants::{PN532_GPIO_P32, PN532_GPIO_P34, PN532_GPIO_VALIDATION_BIT}; /// Driver for the NXP PN532 NFC controller. pub struct Pn532 { @@ -58,7 +27,10 @@ impl Pn532 { /// Initialise the PN532: pulse the reset pin (if provided) and wait for it /// to become ready. pub fn begin(&mut self) -> Result<(), Error> { - self.interface.begin() + if let Err(Error::UnusedPin) = self.interface.begin() { + crate::debug!("No reset pin") + } + Ok(()) } // -- internal helpers --------------------------------------------------- @@ -88,7 +60,7 @@ impl Pn532 { /// Read the PN532 firmware version and ID. pub fn get_firmware_version(&mut self) -> Result> { - self.buffer[0] = pn532::GETFIRMWAREVERSION; + self.buffer[0] = PN532Command::GetFirmwareVersion.into_bits(); self.send(1)?; let len = self.read()?; if len < 4 { @@ -104,7 +76,7 @@ impl Pn532 { /// Read a 16-bit PN532 register. pub fn read_register(&mut self, reg: u16) -> Result> { - self.buffer[0] = pn532::READREGISTER; + self.buffer[0] = PN532Command::ReadRegister.into_bits(); self.buffer[1] = (reg >> 8) as u8; self.buffer[2] = reg as u8; self.send(3)?; @@ -117,7 +89,7 @@ impl Pn532 { /// Write to a 16-bit PN532 register. pub fn write_register(&mut self, reg: u16, val: u8) -> Result<(), Error> { - self.buffer[0] = pn532::WRITEREGISTER; + self.buffer[0] = PN532Command::WriteRegister.into_bits(); self.buffer[1] = (reg >> 8) as u8; self.buffer[2] = reg as u8; self.buffer[3] = val; @@ -129,8 +101,8 @@ impl Pn532 { /// Set the PN532's GPIO pins (see the PN532 user manual for valid pins). pub fn write_gpio(&mut self, pinstate: u8) -> Result<(), Error> { let pinstate = pinstate | (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34); - self.buffer[0] = pn532::WRITEGPIO; - self.buffer[1] = PN532_GPIO_VALIDATIONBIT | pinstate; + self.buffer[0] = PN532Command::WriteGpio.into_bits(); + self.buffer[1] = PN532_GPIO_VALIDATION_BIT | pinstate; self.buffer[2] = 0x00; self.send(3)?; let len = self.read()?; @@ -142,7 +114,7 @@ impl Pn532 { /// Read the state of the PN532's GPIO pins. pub fn read_gpio(&mut self) -> Result> { - self.buffer[0] = pn532::READGPIO; + self.buffer[0] = PN532Command::ReadGpio.into_bits(); self.send(1)?; self.read()?; Ok(self.buffer[0]) @@ -150,7 +122,7 @@ impl Pn532 { /// Configure the SAM (Secure Access Module) in normal mode. pub fn sam_config(&mut self) -> Result<(), Error> { - self.buffer[0] = pn532::SAMCONFIGURATION; + self.buffer[0] = PN532Command::SamConfiguration.into_bits(); 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 +136,7 @@ impl Pn532 { &mut self, max_retries: u8, ) -> Result<(), Error> { - self.buffer[0] = pn532::RFCONFIGURATION; + self.buffer[0] = PN532Command::RfConfiguration.into_bits(); self.buffer[1] = 5; self.buffer[2] = 0xFF; self.buffer[3] = 0x01; @@ -180,7 +152,7 @@ impl Pn532 { auto_rfca: u8, rf_on_off: u8, ) -> Result<(), Error> { - self.buffer[0] = pn532::RFCONFIGURATION; + self.buffer[0] = PN532Command::RfConfiguration.into_bits(); self.buffer[1] = 1; self.buffer[2] = 0x00 | auto_rfca | rf_on_off; self.send(3)?; @@ -201,7 +173,7 @@ impl Pn532 { /// Release the card. pub fn release(&mut self) -> Result<(), Error> { - self.buffer[0] = pn532::INRELEASE; + self.buffer[0] = PN532Command::InRelease.into_bits(); self.buffer[1] = 0x00; self.send(2)?; diff --git a/pn532/src/error.rs b/pn532/src/error.rs index b497cf6..f407530 100644 --- a/pn532/src/error.rs +++ b/pn532/src/error.rs @@ -29,6 +29,8 @@ pub enum Error { Status(StatusCode), #[error("Invalid baud rate")] InvalidBaudRate, + #[error("Unused pin")] + UnusedPin, #[cfg(feature = "felica")] #[error(transparent)] FelicaError(#[from] felica::error::Error), diff --git a/pn532/src/interface.rs b/pn532/src/interface.rs index 14ba626..2318cef 100644 --- a/pn532/src/interface.rs +++ b/pn532/src/interface.rs @@ -5,37 +5,35 @@ //! the driver builds logical commands, the interface handles framing and the //! acknowledgement/response handshake. -use core::convert::Infallible; use embedded_hal::delay::DelayNs; -use embedded_hal::digital::{ErrorType, InputPin, OutputPin}; +use embedded_hal::digital::{ErrorKind, ErrorType, InputPin, OutputPin}; use embedded_hal::i2c::{I2c, Operation}; - -use crate::commands::{ - PN532_ACK_WAIT_TIME_MS, PN532_HOST_TO_PN532, PN532_I2C_ADDRESS, PN532_PN532_TO_HOST, - PN532_POSTAMBLE, PN532_PREAMBLE, PN532_STARTCODE1, PN532_STARTCODE2, -}; +use crate::types::constants::*; use crate::error::Error; +use crate::PN532Command; -const ACK: [u8; 6] = [0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00]; +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct UnreachError; -/// Maximum response data length supported (matches the PN532's 64-byte packet buffer). -const MAX_DATA_LEN: usize = 64; -/// Maximum length of an outbound command frame. -const WRITE_FRAME_CAPACITY: usize = 64; +impl embedded_hal::digital::Error for UnreachError { + fn kind(&self) -> ErrorKind { + ErrorKind::Other + } +} /// Marker type for when no reset pin is connected. #[derive(Clone, Copy, Debug, Default)] pub struct NoReset; -impl ErrorType for NoReset { type Error = Infallible; } +impl ErrorType for NoReset { type Error = UnreachError; } impl OutputPin for NoReset { fn set_low(&mut self) -> Result<(), Self::Error> { - panic!("NoReset should not be used") + Err(UnreachError) } fn set_high(&mut self) -> Result<(), Self::Error> { - panic!("NoReset should not be used") + Err(UnreachError) } } @@ -43,7 +41,7 @@ impl OutputPin for NoReset { #[derive(Clone, Copy, Debug, Default)] pub struct NoIrq; -impl ErrorType for NoIrq { type Error = Infallible; } +impl ErrorType for NoIrq { type Error = UnreachError; } impl InputPin for NoIrq { fn is_high(&mut self) -> Result { @@ -93,7 +91,7 @@ pub struct I2cInterface { delay: D, reset: RST, irq: IRQ, - command: u8, + command: Option, } impl I2cInterface { @@ -105,7 +103,7 @@ impl I2cInterface { delay, reset: NoReset, irq: NoIrq, - command: 0, + command: None, } } } @@ -118,7 +116,7 @@ impl I2cInterface { delay, reset, irq: NoIrq, - command: 0, + command: None, } } } @@ -131,7 +129,7 @@ impl I2cInterface { delay, reset: NoReset, irq, - command: 0, + command: None, } } } @@ -144,7 +142,7 @@ impl I2cInterface { delay, reset, irq, - command: 0, + command: None, } } } @@ -168,10 +166,10 @@ where fn begin(&mut self) -> Result<(), Error> { // Pulse RSTPD_N: high -> low -> wait -> high -> wait. This mirrors the // Adafruit library's begin() reset sequence. - self.reset.set_high().unwrap(); - self.reset.set_low().unwrap(); + self.reset.set_high().map_err(|_| Error::UnusedPin)?; + self.reset.set_low().map_err(|_| Error::UnusedPin)?; self.delay.delay_ms(400); - self.reset.set_high().unwrap(); + self.reset.set_high().map_err(|_| Error::UnusedPin)?; // Let the PN532 boot after the reset is released. The Adafruit library // waits ~10 ms + a 500 ms wakeup here; give it a full 500 ms. self.delay.delay_ms(500); @@ -183,7 +181,7 @@ where header: &[u8], body: &[u8], ) -> Result<(), Error> { - self.command = header[0]; + self.command = Some(PN532Command::from_bits(header[0])); let data_len = header.len() + body.len() + 1; // TFI + payload if data_len > 0xFF { @@ -192,14 +190,14 @@ where // PREAMBLE + STARTCODE1 + STARTCODE2 + LEN + LCS + TFI + payload + DCS + POSTAMBLE let frame_len = 6 + header.len() + body.len() + 2; - let mut frame = [0u8; WRITE_FRAME_CAPACITY]; + let mut frame = [0u8; MAX_DATA_LEN]; if frame_len > frame.len() { return Err(Error::NoSpace); } frame[0] = PN532_PREAMBLE; - frame[1] = PN532_STARTCODE1; - frame[2] = PN532_STARTCODE2; + frame[1] = PN532_START_CODE_1; + frame[2] = PN532_START_CODE_2; frame[3] = data_len as u8; frame[4] = (!(data_len as u8)).wrapping_add(1); frame[5] = PN532_HOST_TO_PN532; @@ -248,8 +246,8 @@ where .map_err(Error::Transport)?; if frame[0] != PN532_PREAMBLE - || frame[1] != PN532_STARTCODE1 - || frame[2] != PN532_STARTCODE2 + || frame[1] != PN532_START_CODE_1 + || frame[2] != PN532_START_CODE_2 { return Err(Error::InvalidFrame); } @@ -258,7 +256,7 @@ where return Err(Error::InvalidFrame); } - let cmd = self.command.wrapping_add(1); + let cmd = self.command.unwrap().as_response(); if frame[5] != PN532_PN532_TO_HOST || frame[6] != cmd { return Err(Error::InvalidFrame); } diff --git a/pn532/src/protocol/felica/felica_lite_s/issuance.rs b/pn532/src/protocol/felica/felica_lite_s/issuance.rs index 6db0a0c..2b43207 100644 --- a/pn532/src/protocol/felica/felica_lite_s/issuance.rs +++ b/pn532/src/protocol/felica/felica_lite_s/issuance.rs @@ -2,6 +2,6 @@ use crate::{Interface, Pn532}; impl Pn532 { pub fn setup_first_issuance(&mut self) { - + todo!() } } \ No newline at end of file diff --git a/pn532/src/protocol/felica/mod.rs b/pn532/src/protocol/felica/mod.rs index 1b78f3a..e52a744 100644 --- a/pn532/src/protocol/felica/mod.rs +++ b/pn532/src/protocol/felica/mod.rs @@ -10,31 +10,29 @@ use crate::interface::Interface; use core::time::Duration; use crate::types::felica::*; use crate::types::BaudRate; +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}; impl Pn532 { /// Poll for a FeliCa card. pub fn felica_polling( &mut self, - system_code: Option, - baud_rate: BaudRate, - request_code: FelicaPollingRequestCode, - timeout: Duration, + config: FelicaPollingConfig ) -> Result, Error> { - if !matches!(baud_rate, BaudRate::Felica212kbps | BaudRate::Felica424kbps) { + if !matches!(config.baud_rate, BaudRate::Felica212kbps | BaudRate::Felica424kbps) { return Err(Error::InvalidBaudRate); } - let system_code = system_code.unwrap_or(0xFFFF); - self.buffer[0] = pn532::INLISTPASSIVETARGET; + let system_code = config.system_code; + self.buffer[0] = PN532Command::InListPassiveTarget.into_bits(); self.buffer[1] = 1; - self.buffer[2] = baud_rate.into(); - self.buffer[3] = FELICA_CMD_POLLING; + 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] = request_code as u8; + self.buffer[6] = config.request_code as u8; self.buffer[7] = 0; self.send(8)?; - match self.read_timeout(timeout) { + match self.read_timeout(config.timeout) { Err(Error::Timeout) => return Ok(None), Err(e) => return Err(e), Ok(_) => {} @@ -87,7 +85,7 @@ impl Pn532 { if command.len() > 0xFE { return Err(Error::InvalidParam); } - self.buffer[0] = pn532::INDATAEXCHANGE; + 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)?; @@ -109,7 +107,7 @@ impl Pn532 { /// Send a FeliCa "Request Response" command, returning the card's mode. pub fn felica_request_response(&mut self, card: &FelicaPollingResponse) -> Result> { let mut cmd = [0u8; 9]; - cmd[0] = FELICA_CMD_REQUEST_RESPONSE; + cmd[0] = FelicaCommand::RequestResponse.into_bits(); cmd[1..9].copy_from_slice(&card.idm.into_be_bytes()); let mut response = [0u8; 10]; @@ -154,7 +152,7 @@ impl Pn532 { // command let mut cmd = heapless::Vec::::new(); - cmd.push(FELICA_CMD_READ_WITHOUT_ENCRYPTION).unwrap(); + cmd.push(FelicaCommand::ReadWithoutEncryption.into_bits()).unwrap(); cmd.extend_from_slice(&card.idm.into_bytes()).unwrap(); cmd.push(num_service as u8).unwrap(); for sc in service_code_list { @@ -224,7 +222,7 @@ impl Pn532 { // command let mut cmd = heapless::Vec::::new(); - cmd.push(FELICA_CMD_WRITE_WITHOUT_ENCRYPTION).unwrap(); + cmd.push(FelicaCommand::WriteWithEncryption as u8).unwrap(); cmd.extend_from_slice(&card.idm.into_bytes()).unwrap(); cmd.push(num_service as u8).unwrap(); for sc in service_code_list { @@ -255,79 +253,4 @@ impl Pn532 { } Ok(()) } - - /// Send a FeliCa "Request System Code" command. - /// - /// Returns the number of system codes written to `system_code_list`. - #[cfg(not(feature = "felica-lite-s"))] - pub fn felica_request_system_code( - &mut self, - card: &IDm, - system_code_list: &mut [impl AsServiceCode], - ) -> Result> { - let mut cmd = [0u8; 9]; - cmd[0] = FELICA_CMD_REQUEST_SYSTEM_CODE; - cmd[1..9].copy_from_slice(&card.into_be_bytes()); - - let mut response = [0u8; 10 + 2 * 16]; - let response_len = self.felica_send_command(&cmd, &mut response, Duration::from_millis(200))?; - if response_len < 10 { - return Err(Error::InvalidFrame); - } - let num_system_code = response[9] as usize; - if response_len < 10 + 2 * num_system_code { - return Err(Error::InvalidFrame); - } - if num_system_code > system_code_list.len() { - return Err(Error::NoSpace); - } - for (i, out) in system_code_list.iter_mut().enumerate().take(num_system_code) { - *out = AsServiceCode::from_service_code( - u16::from_be_bytes([response[10 + i * 2], response[10 + i * 2 + 1]]) - ); - } - Ok(num_system_code) - } - - /// Send a FeliCa "Request Service" command. - #[cfg(not(feature = "felica-lite-s"))] - pub fn felica_request_service( - &mut self, - card: &IDm, - node_code_list: &[u16], - key_versions: &mut [u16], - ) -> Result<(), Error> { - let num_node = node_code_list.len(); - if num_node > FELICA_REQ_SERVICE_MAX_NODE_NUM || num_node > key_versions.len() { - return Err(Error::InvalidParam); - } - - let mut cmd = [0u8; 1 + 8 + 1 + 2 * FELICA_REQ_SERVICE_MAX_NODE_NUM]; - let mut j = 0; - cmd[j] = FELICA_CMD_REQUEST_SERVICE; - j += 1; - cmd[j..j + 8].copy_from_slice(&card.into_be_bytes()); - j += 8; - cmd[j] = num_node as u8; - j += 1; - for &code in node_code_list { - cmd[j] = code as u8; - cmd[j + 1] = (code >> 8) as u8; - j += 2; - } - - let mut response = [0u8; 10 + 2 * FELICA_REQ_SERVICE_MAX_NODE_NUM]; - let response_len = self.felica_send_command( - &cmd[..j], - &mut response, - Duration::from_millis(200) - )?; - if response_len != 10 + 2 * num_node { - return Err(Error::InvalidFrame); - } - for (i, kv) in key_versions.iter_mut().enumerate().take(num_node) { - *kv = u16::from_le_bytes([response[10 + i * 2], response[10 + i * 2 + 1]]); - } - Ok(()) - } } diff --git a/pn532/src/protocol/iso14443a/mod.rs b/pn532/src/protocol/iso14443a/mod.rs index 66aba4b..9cd1fd1 100644 --- a/pn532/src/protocol/iso14443a/mod.rs +++ b/pn532/src/protocol/iso14443a/mod.rs @@ -1,3 +1,5 @@ +///! WIP + use crate::commands::{PN532_COMMAND_INDATAEXCHANGE, PN532_COMMAND_INLISTPASSIVETARGET, PN532_COMMAND_INRELEASE}; use crate::error::StatusCode; use crate::{BaudRate, Error, Interface, Pn532, Uid}; diff --git a/pn532/src/protocol/mifare/mod.rs b/pn532/src/protocol/mifare/mod.rs index 421840b..b128319 100644 --- a/pn532/src/protocol/mifare/mod.rs +++ b/pn532/src/protocol/mifare/mod.rs @@ -1,3 +1,5 @@ +///! WIP + use crate::commands::{MIFARE_CMD_AUTH_A, MIFARE_CMD_AUTH_B, MIFARE_CMD_READ, MIFARE_CMD_WRITE, MIFARE_CMD_WRITE_ULTRALIGHT, PN532_COMMAND_INDATAEXCHANGE}; use crate::error::StatusCode; use crate::{Error, Interface, Pn532}; diff --git a/pn532/src/protocol/mod.rs b/pn532/src/protocol/mod.rs index 9a7f876..fe89b83 100644 --- a/pn532/src/protocol/mod.rs +++ b/pn532/src/protocol/mod.rs @@ -1,6 +1,8 @@ +///! Card protocols + #[cfg(feature = "felica")] pub mod felica; #[cfg(feature = "mifare-classic")] -pub mod mifare; +mod mifare; #[cfg(feature = "iso14443a")] pub mod iso14443a; \ No newline at end of file diff --git a/pn532/src/tg.rs b/pn532/src/tg.rs index 3b77b23..7015ee0 100644 --- a/pn532/src/tg.rs +++ b/pn532/src/tg.rs @@ -1,8 +1,21 @@ use crate::commands::*; use crate::error::StatusCode; -use crate::{Error, Interface, Pn532, TargetInitStatus}; +use crate::{Error, Interface, Pn532}; use core::time::Duration; + +/// Outcome of [`Pn532::tg_init_as_target`] / [`Pn532::tg_init_as_target_default`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TargetInitStatus { + /// The PN532 entered target mode. + Success, + /// No initiator appeared within the timeout. + Timeout, + /// The operation failed. + Failed, +} + impl Pn532 { /// Initialize the PN532 as a target using a raw command frame. pub fn tg_init_as_target( @@ -24,7 +37,7 @@ impl Pn532 { timeout_ms: u16, ) -> Result> { const COMMAND: [u8; 44] = [ - pn532::TGINITASTARGET, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0xFE, + PN532Command::TgInitAsTarget.into_bits(), 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 +47,7 @@ impl Pn532 { /// Retrieve data received from the initiator. pub fn tg_get_data(&mut self, buf: &mut [u8]) -> Result> { - self.buffer[0] = pn532::TGGETDATA; + self.buffer[0] = PN532Command::TgGetData.into_bits(); self.send(1)?; let len = self.read_timeout(Duration::from_millis(3000))?; @@ -57,10 +70,10 @@ impl Pn532 { body: &[u8], ) -> Result<(), Error> { if header.len() > N - 1 { - self.buffer[0] = pn532::TGSETDATA; + self.buffer[0] = PN532Command::TgSetData.into_bits(); self.interface.write_command(&self.buffer[..1], header)?; } else { - self.buffer[0] = pn532::TGSETDATA; + self.buffer[0] = PN532Command::TgSetData.into_bits(); self.buffer[1..1 + header.len()].copy_from_slice(header); self.interface .write_command(&self.buffer[..1 + header.len()], body)?; diff --git a/pn532/src/types/constants.rs b/pn532/src/types/constants.rs new file mode 100644 index 0000000..7038c37 --- /dev/null +++ b/pn532/src/types/constants.rs @@ -0,0 +1,34 @@ +// PN532 commands. +pub const PN532_RESPONSE_IN_DATA_EXCHANGE: u8 = 0x41; +pub const PN532_RESPONSE_IN_LIST_PASSIVE_TARGET: u8 = 0x4B; +pub const PN532_MIFARE_ISO14443A: u8 = 0x00; + +// GPIO. +pub const PN532_GPIO_VALIDATION_BIT: 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; + +// Frame protocol constants. +pub const PN532_PREAMBLE: u8 = 0x00; +pub const PN532_START_CODE_1: u8 = 0x00; +pub const PN532_START_CODE_2: 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; + + + +pub const ACK: [u8; 6] = [0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00]; + +/// Maximum response data length supported (matches the PN532's 64-byte packet buffer). +pub const MAX_DATA_LEN: usize = 64; \ No newline at end of file diff --git a/pn532/src/types/felica/constants.rs b/pn532/src/types/felica/constants.rs new file mode 100644 index 0000000..89ff4ce --- /dev/null +++ b/pn532/src/types/felica/constants.rs @@ -0,0 +1,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; \ No newline at end of file diff --git a/pn532/src/types/felica/felica_lite_s/auth/mac_input.rs b/pn532/src/types/felica/felica_lite_s/auth/mac_input.rs index e83193b..a5f265b 100644 --- a/pn532/src/types/felica/felica_lite_s/auth/mac_input.rs +++ b/pn532/src/types/felica/felica_lite_s/auth/mac_input.rs @@ -1,5 +1,5 @@ use crate::types::felica::AsBlock; -use crate::FELICA_READ_MAX_BLOCK_NUM; +use crate::types::felica::constants::FELICA_READ_MAX_BLOCK_NUM; #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/pn532/src/types/felica/felica_lite_s/mod.rs b/pn532/src/types/felica/felica_lite_s/mod.rs index 62a0ead..b07e0f5 100644 --- a/pn532/src/types/felica/felica_lite_s/mod.rs +++ b/pn532/src/types/felica/felica_lite_s/mod.rs @@ -1,11 +1,9 @@ -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 +pub mod auth; + use bitfields::bitflag; -use crate::types::felica::polling::PollingResponse; use super::{AsBlock, IDm, PMm}; use super::AsServiceCode; diff --git a/pn532/src/types/felica/mod.rs b/pn532/src/types/felica/mod.rs index fa5b018..a8487d5 100644 --- a/pn532/src/types/felica/mod.rs +++ b/pn532/src/types/felica/mod.rs @@ -4,11 +4,13 @@ mod idm; #[cfg(feature = "felica-lite-s")] pub mod felica_lite_s; pub mod ic_type; +pub mod constants; pub use idm::*; pub use pmm::*; pub use polling::PollingRequestCode as FelicaPollingRequestCode; pub use polling::PollingResponse as FelicaPollingResponse; +pub use polling::PollingConfig as FelicaPollingConfig; pub use polling::CardSession; #[cfg(feature = "felica-lite-s")] pub use felica_lite_s::{Block as FelicaLiteSBlock, ServiceCode as FelicaLiteSServiceCode}; @@ -36,4 +38,51 @@ impl AsServiceCode for u16 { fn from_service_code(val: u16) -> Self { val } +} + +use bitfields::bitflag; + +/// NDEF URI record identifier codes +/// (see NFC Forum "URI Record Type Definition"). +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[bitflag(u8)] +pub enum UriIdentifierCode { + #[base] + None = 0x00, + HttpWwwDot = 0x01, + HttpsWwwDot = 0x02, + Http = 0x03, + Https = 0x04, + Tel = 0x05, + Mailto = 0x06, + FtpAnonAt = 0x07, + FtpFtpDot = 0x08, + Ftps = 0x09, + Sftp = 0x0A, + Smb = 0x0B, + Nfs = 0x0C, + Ftp = 0x0D, + Dav = 0x0E, + News = 0x0F, + Telnet = 0x10, + Imap = 0x11, + Rtsp = 0x12, + Urn = 0x13, + Pop = 0x14, + Sip = 0x15, + Sips = 0x16, + Tftp = 0x17, + BtSpp = 0x18, + BtL2cap = 0x19, + BtGoEp = 0x1A, + TcpObex = 0x1B, + IrdaObex = 0x1C, + File = 0x1D, + UrnEpcId = 0x1E, + UrnEpcTag = 0x1F, + UrnEpcPat = 0x20, + UrnEpcRaw = 0x21, + UrnEpc = 0x22, + UrnNfc = 0x23, } \ No newline at end of file diff --git a/pn532/src/types/felica/polling.rs b/pn532/src/types/felica/polling.rs index 877751c..7de5e00 100644 --- a/pn532/src/types/felica/polling.rs +++ b/pn532/src/types/felica/polling.rs @@ -1,7 +1,9 @@ use alloc::string::ToString; use core::fmt::{Display, Formatter}; +use core::time::Duration; use bitfields::bitflag; -use super::{IDm, PMm}; +use crate::types::BaudRate; +use super::{FelicaPollingRequestCode, IDm, PMm}; #[derive(Debug, PartialEq, Eq, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -75,4 +77,24 @@ impl Display for CardSession { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "IDm: {}, PMm: {}", self.idm, self.pmm) } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PollingConfig { + pub system_code: u16, + pub baud_rate: BaudRate, + pub request_code: FelicaPollingRequestCode, + pub timeout: Duration, +} + +impl Default for PollingConfig { + fn default() -> Self { + Self { + system_code: 0xFFFF, + baud_rate: BaudRate::Felica212kbps, + request_code: FelicaPollingRequestCode::SystemCode, + timeout: Duration::from_secs(1), + } + } } \ No newline at end of file diff --git a/pn532/src/types/frame.rs b/pn532/src/types/frame.rs new file mode 100644 index 0000000..66cd42d --- /dev/null +++ b/pn532/src/types/frame.rs @@ -0,0 +1,12 @@ +use crate::{Error, PN532Command}; +use crate::types::constants::*; + +pub struct DataFrame { + command: PN532Command +} + +impl DataFrame { + pub fn new(header: &[u8], body: &[u8]) -> Result> { + Ok(Self { command: PN532Command::TgGetData } ) + } +} \ No newline at end of file diff --git a/pn532/src/types/mod.rs b/pn532/src/types/mod.rs index 9c69647..3995297 100644 --- a/pn532/src/types/mod.rs +++ b/pn532/src/types/mod.rs @@ -1,11 +1,17 @@ +use bitfields::bitflag; + #[cfg(feature = "felica")] pub mod felica; +pub mod constants; +pub mod frame; /// Passive target baud rates supported by [`crate::Pn532::read_passive_target_id`]. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] +#[derive(PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[bitflag(u8)] pub enum BaudRate { ISO14443A106kbps = 0x00, + #[base] Felica212kbps = 0x01, Felica424kbps = 0x02, ISO14443B106kbps = 0x03, diff --git a/src/components/card_reader.rs b/src/components/card_reader.rs index 1248fe6..cd7c9a6 100644 --- a/src/components/card_reader.rs +++ b/src/components/card_reader.rs @@ -50,10 +50,7 @@ pub async fn start_card_reader( loop { Timer::after_millis(50).await; let Some(response) = (match card_reader.driver.felica_polling( - None, - BaudRate::Felica212kbps, - FelicaPollingRequestCode::SystemCode, - core::time::Duration::from_secs(1), + Default::default() ) { Ok(v) => v, Err(e) => {