diff --git a/pn532/PROTOCOL.md b/pn532/PROTOCOL.md index 85557cc..20e44ce 100644 --- a/pn532/PROTOCOL.md +++ b/pn532/PROTOCOL.md @@ -21,16 +21,16 @@ Every command and every response is one *information frame* of bytes: | 00 | 00 FF | | | | | | 00 | ``` -| Field | Size | Value / meaning | -|-------|------|-----------------| -| Preamble | 1 | `0x00` (see §1.4 for length rules) | -| Start code | 2 | `0x00 0xFF` | -| LEN | 1 | Length of the *data field* = 1 (TFI) + N (payload bytes). `0x01..0xFF`. | -| LCS | 1 | Length Checksum — `(0x100 − LEN) & 0xFF`, i.e. `LEN + LCS == 0` (mod 256). | -| TFI | 1 | Frame identifier: `0xD4` host→PN532, `0xD5` PN532→host. | -| DATA | N | Command code (PD0) followed by parameters (PD1…PDn). | -| DCS | 1 | Data Checksum — `(0x100 − (TFI + Σ DATA)) & 0xFF`, i.e. `TFI + Σ DATA + DCS == 0` (mod 256). | -| Postamble | 1 | `0x00` (see §1.4) | +| Field | Size | Value / meaning | +|------------|------|----------------------------------------------------------------------------------------------| +| Preamble | 1 | `0x00` (see §1.4 for length rules) | +| Start code | 2 | `0x00 0xFF` | +| LEN | 1 | Length of the *data field* = 1 (TFI) + N (payload bytes). `0x01..0xFF`. | +| LCS | 1 | Length Checksum — `(0x100 − LEN) & 0xFF`, i.e. `LEN + LCS == 0` (mod 256). | +| TFI | 1 | Frame identifier: `0xD4` host→PN532, `0xD5` PN532→host. | +| DATA | N | Command code (PD0) followed by parameters (PD1…PDn). | +| DCS | 1 | Data Checksum — `(0x100 − (TFI + Σ DATA)) & 0xFF`, i.e. `TFI + Σ DATA + DCS == 0` (mod 256). | +| Postamble | 1 | `0x00` (see §1.4) | **Checksums in one line (two's complement):** @@ -62,23 +62,23 @@ frame's 255-byte limit it uses an *extended* frame: ### 1.2 Frame Identifier (TFI) -| TFI | Direction | -|-----|-----------| -| `0xD4` | Host → PN532 | -| `0xD5` | PN532 → Host | +| TFI | Direction | +|--------|--------------------------------| +| `0xD4` | Host → PN532 | +| `0xD5` | PN532 → Host | | `0x7F` | Error frame (PN532 → host, §2) | ### 1.3 Response command byte The response echoes the command code **+ 1**: -| Command (host→PN532) | Response (PN532→host) | -|----------------------|----------------------| -| `GetFirmwareVersion` `0x02` | `0x03` | -| `SAMConfiguration` `0x14` | `0x15` | -| `InListPassiveTarget` `0x4A` | `0x4B` | -| `InDataExchange` `0x40` | `0x41` | -| … | `command + 1` | +| Command (host→PN532) | Response (PN532→host) | +|------------------------------|-----------------------| +| `GetFirmwareVersion` `0x02` | `0x03` | +| `SAMConfiguration` `0x14` | `0x15` | +| `InListPassiveTarget` `0x4A` | `0x4B` | +| `InDataExchange` `0x40` | `0x41` | +| … | `command + 1` | ### 1.4 Preamble / Postamble length rules @@ -147,35 +147,35 @@ NADPresent MI Error code Error code list: -| Code | Cause | -|------|-------| -| `0x00` | OK | -| `0x01` | Time out — target did not answer | -| `0x02` | CRC error detected by the CIU | -| `0x03` | Parity error detected by the CIU | +| Code | Cause | +|--------|-------------------------------------------------------------------------| +| `0x00` | OK | +| `0x01` | Time out — target did not answer | +| `0x02` | CRC error detected by the CIU | +| `0x03` | Parity error detected by the CIU | | `0x04` | Erroneous bit count during anticollision (14443-3 Type A / 18092 106 k) | -| `0x05` | Framing error during MIFARE operation | -| `0x06` | Abnormal bit-collision during bitwise anticollision at 106 k | -| `0x07` | Communication buffer size insufficient | -| `0x09` | RF buffer overflow (CIU_Error BufferOvfl) | -| `0x0A` | RF field not switched on in time by counterpart (active mode) | -| `0x0B` | RF protocol error | -| `0x0D` | Temperature error — antenna drivers switched off | -| `0x0E` | Internal buffer overflow | -| `0x10` | Invalid parameter (range / format) | -| `0x12` | DEP: unsupported command received from initiator | -| `0x13` | DEP / MIFARE / 14443-4: data format does not match spec | -| `0x14` | MIFARE: authentication error | -| `0x23` | ISO14443-3: UID check byte wrong | -| `0x25` | DEP: invalid device state | -| `0x26` | Operation not allowed in this configuration | -| `0x27` | Command not acceptable in current context (unknown target number, …) | -| `0x29` | Target released by its initiator | -| `0x2A` | 14443-3B: card ID mismatch (wrong card) | -| `0x2B` | 14443-3B: previously activated card disappeared | -| `0x2C` | NFCID3 mismatch (initiator vs target) in DEP 212/424 passive | -| `0x2D` | Over-current detected | -| `0x2E` | NAD missing in DEP frame | +| `0x05` | Framing error during MIFARE operation | +| `0x06` | Abnormal bit-collision during bitwise anticollision at 106 k | +| `0x07` | Communication buffer size insufficient | +| `0x09` | RF buffer overflow (CIU_Error BufferOvfl) | +| `0x0A` | RF field not switched on in time by counterpart (active mode) | +| `0x0B` | RF protocol error | +| `0x0D` | Temperature error — antenna drivers switched off | +| `0x0E` | Internal buffer overflow | +| `0x10` | Invalid parameter (range / format) | +| `0x12` | DEP: unsupported command received from initiator | +| `0x13` | DEP / MIFARE / 14443-4: data format does not match spec | +| `0x14` | MIFARE: authentication error | +| `0x23` | ISO14443-3: UID check byte wrong | +| `0x25` | DEP: invalid device state | +| `0x26` | Operation not allowed in this configuration | +| `0x27` | Command not acceptable in current context (unknown target number, …) | +| `0x29` | Target released by its initiator | +| `0x2A` | 14443-3B: card ID mismatch (wrong card) | +| `0x2B` | 14443-3B: previously activated card disappeared | +| `0x2C` | NFCID3 mismatch (initiator vs target) in DEP 212/424 passive | +| `0x2D` | Over-current detected | +| `0x2E` | NAD missing in DEP frame | --- @@ -214,7 +214,7 @@ Error code list: - Every transfer starts with a **direction byte** (2 LSBs): | First byte | Operation | - |-----------|-----------| + |-----------|-----------| | `xxxx xx01` (`0x01`) | Data write (host → PN532) | | `xxxx xx10` (`0x02`) | Status read (PN532 → host) | | `xxxx xx11` (`0x03`) | Data read (PN532 → host) | @@ -231,40 +231,40 @@ Error code list: Each command is the first payload byte (PD0) after the `0xD4` TFI. `In` = initiator, `Tg` = target. -| Command | Code | Parameters | -|---------|------|-----------| -| `Diagnose` | `0x00` | NumTst (1), [InParam…] | -| `GetFirmwareVersion` | `0x02` | — | -| `GetGeneralStatus` | `0x04` | — | -| `ReadRegister` | `0x06` | Address (2, big-endian) | -| `WriteRegister` | `0x08` | Address (2) + Value (1…n) | -| `ReadGPIO` | `0x0C` | — | -| `WriteGPIO` | `0x0E` | P3, P7 | -| `SetSerialBaudRate` | `0x10` | Baud rate (1) | -| `SetParameters` | `0x12` | Flags (1) | -| `SAMConfiguration` | `0x14` | Mode (1), Timeout (1), [IRQ (1)] | -| `PowerDown` | `0x16` | WakeUpEnable (1), GenerateIRQ (1) | -| `RFConfiguration` | `0x32` | CfgItem (1), … | -| `RFRegulationTest` | `0x58` | TxMode (1) | -| `InJumpForPSL` | `0x46` | ActPSL (1), … | -| `InJumpForDEP` | `0x56` | ActPass (1), … | -| `InListPassiveTarget` | `0x4A` | MaxTg (1), BrTy (1), [InitiatorData] | -| `InATR` | `0x50` | — | -| `InPSL` | `0x4E` | Tg (1) | -| `InDataExchange` | `0x40` | Tg (1), [DataOut…] | -| `InCommunicateThru` | `0x42` | DataOut… | -| `InDeselect` | `0x44` | Tg (1) | -| `InRelease` | `0x52` | Tg (1) | -| `InSelect` | `0x54` | Tg (1) | -| `InAutoPoll` | `0x60` | PollNr (1), … | -| `TgInitAsTarget` | `0x8C` | Mode (1), … | -| `TgGetData` | `0x86` | — | -| `TgSetData` | `0x8E` | DataIn… | -| `TgGetInitiatorCommand` | `0x88` | — | -| `TgResponseToInitiator` | `0x90` | Data… | -| `TgGetTargetStatus` | `0x8A` | — | -| `TgSetGeneralBytes` | `0x92` | Data… | -| `TgSetMetaData` | `0x94` | Data… | +| Command | Code | Parameters | +|-------------------------|--------|--------------------------------------| +| `Diagnose` | `0x00` | NumTst (1), [InParam…] | +| `GetFirmwareVersion` | `0x02` | — | +| `GetGeneralStatus` | `0x04` | — | +| `ReadRegister` | `0x06` | Address (2, big-endian) | +| `WriteRegister` | `0x08` | Address (2) + Value (1…n) | +| `ReadGPIO` | `0x0C` | — | +| `WriteGPIO` | `0x0E` | P3, P7 | +| `SetSerialBaudRate` | `0x10` | Baud rate (1) | +| `SetParameters` | `0x12` | Flags (1) | +| `SAMConfiguration` | `0x14` | Mode (1), Timeout (1), [IRQ (1)] | +| `PowerDown` | `0x16` | WakeUpEnable (1), GenerateIRQ (1) | +| `RFConfiguration` | `0x32` | CfgItem (1), … | +| `RFRegulationTest` | `0x58` | TxMode (1) | +| `InJumpForPSL` | `0x46` | ActPSL (1), … | +| `InJumpForDEP` | `0x56` | ActPass (1), … | +| `InListPassiveTarget` | `0x4A` | MaxTg (1), BrTy (1), [InitiatorData] | +| `InATR` | `0x50` | — | +| `InPSL` | `0x4E` | Tg (1) | +| `InDataExchange` | `0x40` | Tg (1), [DataOut…] | +| `InCommunicateThru` | `0x42` | DataOut… | +| `InDeselect` | `0x44` | Tg (1) | +| `InRelease` | `0x52` | Tg (1) | +| `InSelect` | `0x54` | Tg (1) | +| `InAutoPoll` | `0x60` | PollNr (1), … | +| `TgInitAsTarget` | `0x8C` | Mode (1), … | +| `TgGetData` | `0x86` | — | +| `TgSetData` | `0x8E` | DataIn… | +| `TgGetInitiatorCommand` | `0x88` | — | +| `TgResponseToInitiator` | `0x90` | Data… | +| `TgGetTargetStatus` | `0x8A` | — | +| `TgSetGeneralBytes` | `0x92` | Data… | +| `TgSetMetaData` | `0x94` | Data… | --- @@ -381,16 +381,16 @@ Response (`0x07`) with value `0x84`: These are the payload bytes passed inside `InDataExchange` / `InCommunicateThru`. -| Operation | Card command | -|-----------|--------------| -| Authenticate key A / B | `0x60` / `0x61` + block + UID + key (6) | -| Read block | `0x30` + block | -| Write block | `0xA0` + block + 16 data bytes | -| Write (Ultralight) | `0xA2` + page + 4 data bytes | -| Transfer (write commit) | `0xB0` + block | -| Decrement | `0xC0` + block + 4-byte value | -| Increment | `0xC1` + block + 4-byte value | -| Restore | `0xC2` + block | +| Operation | Card command | +|-------------------------|-----------------------------------------| +| Authenticate key A / B | `0x60` / `0x61` + block + UID + key (6) | +| Read block | `0x30` + block | +| Write block | `0xA0` + block + 16 data bytes | +| Write (Ultralight) | `0xA2` + page + 4 data bytes | +| Transfer (write commit) | `0xB0` + block | +| Decrement | `0xC0` + block + 4-byte value | +| Increment | `0xC1` + block + 4-byte value | +| Restore | `0xC2` + block | FeliCa payload codes: Polling `0x00`, Request Service `0x02`, Request Response `0x04`, Read Without Encryption `0x06`, Write Without Encryption `0x08`, Request System Code `0x0C`. @@ -403,14 +403,14 @@ Read Without Encryption `0x06`, Write Without Encryption `0x08`, Request System D4 12 Flags ``` -| Bit | Flag | Meaning | -|-----|------|---------| -| 0 | `fNADUsed` | Use NAD in DEP / 14443-4 PCD | -| 1 | `fDIDUsed` | Use DID (DEP) / CID (14443-4 PCD) | -| 2 | `fAutomaticATR_RES` | Auto-generate ATR_RES in target mode | -| 4 | `fAutomaticRATS` | Auto-send RATS after selecting 14443-4 card | -| 5 | `fISO14443-4_PICC` | Emulate ISO14443-4 PICC | -| 6 | `fRemovePrePostAmble` | Omit preamble + postamble in frames sent to host | +| Bit | Flag | Meaning | +|-----|-----------------------|--------------------------------------------------| +| 0 | `fNADUsed` | Use NAD in DEP / 14443-4 PCD | +| 1 | `fDIDUsed` | Use DID (DEP) / CID (14443-4 PCD) | +| 2 | `fAutomaticATR_RES` | Auto-generate ATR_RES in target mode | +| 4 | `fAutomaticRATS` | Auto-send RATS after selecting 14443-4 card | +| 5 | `fISO14443-4_PICC` | Emulate ISO14443-4 PICC | +| 6 | `fRemovePrePostAmble` | Omit preamble + postamble in frames sent to host | --- @@ -420,20 +420,20 @@ The firmware drives a *Contactless Interface Unit* (CIU) whose commands live in `CIU_Command` register. These are **not** host commands — they surface only via `WriteRegister`/`ReadRegister`. Listed for completeness: -| Command | Code | Action | -|---------|------|--------| -| Idle | `0000` | No action; cancel current command | -| Config | `0001` | Configure CIU for FeliCa / MIFARE / NFCIP-1 | -| GenerateRandomID | `0010` | Generate 10-byte random ID | -| CalcCRC | `0011` | Run CRC coprocessor (or self-test) | -| Transmit | `0100` | Transmit data from FIFO | -| NoCmdChange | `0111` | Modify `CIU_Command` bits without changing command | -| Receive | `1000` | Activate receiver | -| SelfTest | `1001` | Activate self-test | -| Transceive | `1100` | Transmit then auto-receive (initiator) or vice-versa | -| AutoColl | `1101` | FeliCa polling / MIFARE anticollision (card mode) | -| MFAuthent | `1110` | MIFARE Classic authentication | -| SoftReset | `1111` | Reset the CIU | +| Command | Code | Action | +|------------------|--------|------------------------------------------------------| +| Idle | `0000` | No action; cancel current command | +| Config | `0001` | Configure CIU for FeliCa / MIFARE / NFCIP-1 | +| GenerateRandomID | `0010` | Generate 10-byte random ID | +| CalcCRC | `0011` | Run CRC coprocessor (or self-test) | +| Transmit | `0100` | Transmit data from FIFO | +| NoCmdChange | `0111` | Modify `CIU_Command` bits without changing command | +| Receive | `1000` | Activate receiver | +| SelfTest | `1001` | Activate self-test | +| Transceive | `1100` | Transmit then auto-receive (initiator) or vice-versa | +| AutoColl | `1101` | FeliCa polling / MIFARE anticollision (card mode) | +| MFAuthent | `1110` | MIFARE Classic authentication | +| SoftReset | `1111` | Reset the CIU | --- diff --git a/pn532/src/commands/felica.rs b/pn532/src/commands/felica.rs index 21d87d9..d793f21 100644 --- a/pn532/src/commands/felica.rs +++ b/pn532/src/commands/felica.rs @@ -12,5 +12,5 @@ pub enum Command { RequestResponse = 0x04, ReadWithoutEncryption = 0x06, WriteWithEncryption = 0x08, - RequestSystemCode = 0x0C -} \ No newline at end of file + RequestSystemCode = 0x0C, +} diff --git a/pn532/src/commands/mifare.rs b/pn532/src/commands/mifare.rs index cd018fb..9939843 100644 --- a/pn532/src/commands/mifare.rs +++ b/pn532/src/commands/mifare.rs @@ -16,5 +16,5 @@ pub enum Command { Transfer = 0xB0, Decrement = 0xC0, Increment = 0xC1, - Store = 0xC2 -} \ No newline at end of file + Store = 0xC2, +} diff --git a/pn532/src/commands/pn532.rs b/pn532/src/commands/pn532.rs index 8900862..6243fd9 100644 --- a/pn532/src/commands/pn532.rs +++ b/pn532/src/commands/pn532.rs @@ -46,4 +46,4 @@ 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 68eb676..e57a357 100644 --- a/pn532/src/driver.rs +++ b/pn532/src/driver.rs @@ -4,7 +4,7 @@ use crate::commands::*; use crate::error::Error; use crate::interface::Interface; use crate::types::constants::{PN532_GPIO_P32, PN532_GPIO_P34, PN532_GPIO_VALIDATION_BIT}; -use crate::{StatusCode}; +use crate::StatusCode; use core::marker::PhantomData; use core::time::Duration; use embedded_hal::delay::DelayNs; @@ -53,7 +53,7 @@ where Err(Error::UnusedPin) => { crate::debug!("Unused pin"); Ok(()) - }, + } Err(e) => Err(e), } } diff --git a/pn532/src/error.rs b/pn532/src/error.rs index f407530..089754f 100644 --- a/pn532/src/error.rs +++ b/pn532/src/error.rs @@ -1,6 +1,6 @@ -use core::fmt::Debug; -use bitfields::bitflag; use crate::protocol::felica; +use bitfields::bitflag; +use core::fmt::Debug; /// Driver-level error type, generic over the transport (I2C) error. #[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] @@ -31,6 +31,8 @@ pub enum Error { InvalidBaudRate, #[error("Unused pin")] UnusedPin, + #[error("Error while interacting with IO Pin")] + IOPinError, #[cfg(feature = "felica")] #[error(transparent)] FelicaError(#[from] felica::error::Error), @@ -68,5 +70,5 @@ pub enum StatusCode { MismatchUd = 0x2C, OverCurrent = 0x2D, NADMissing = 0x2E, - Max = 0x2F + Max = 0x2F, } diff --git a/pn532/src/interface/i2c.rs b/pn532/src/interface/i2c.rs index 1bb3113..09c6d81 100644 --- a/pn532/src/interface/i2c.rs +++ b/pn532/src/interface/i2c.rs @@ -1,8 +1,8 @@ +use crate::types::constants::*; +use crate::{Error, Interface, NoIrq, NoReset, PN532Command}; use embedded_hal::delay::DelayNs; use embedded_hal::digital::{InputPin, OutputPin}; use embedded_hal::i2c::{I2c, Operation}; -use crate::{Error, Interface, NoIrq, NoReset, PN532Command}; -use crate::types::constants::*; /// [`Interface`] implementation over a blocking `embedded_hal::i2c::I2c` bus. /// @@ -146,28 +146,21 @@ where /// Wait until the PN532 signals data is ready. fn wait_ready(&mut self, timeout_ms: u16) -> Result<(), Error> { let mut elapsed = 0u16; + let mut status = [0u8; 1]; loop { - match self.irq.is_low() { - Ok(true) => { - let mut status = [0u8; 1]; - match self.i2c.read(PN532_I2C_ADDRESS, &mut status) { - Ok(()) if status[0] & 1 == 1 => return Ok(()), - _ => { - self.delay.delay_ms(1); - elapsed += 1; - if timeout_ms != 0 && elapsed >= timeout_ms { - return Err(Error::Timeout); - } - } - } - } - Ok(false) | Err(_) => { - self.delay.delay_ms(1); - elapsed += 1; - if timeout_ms != 0 && elapsed >= timeout_ms { - return Err(Error::Timeout); - } - } + let Ok(is_irq_low) = self.irq.is_low() else { + return Err(Error::IOPinError); + }; + if is_irq_low + && self.i2c.read(PN532_I2C_ADDRESS, &mut status).is_ok() + && status[0] & 1 == 1 + { + return Ok(()); + } + self.delay.delay_ms(1); + elapsed += 1; + if timeout_ms != 0 && elapsed >= timeout_ms { + return Err(Error::Timeout); } } } @@ -178,10 +171,7 @@ where self.i2c .transaction( PN532_I2C_ADDRESS, - &mut [ - Operation::Read(&mut [0]), - Operation::Read(&mut ack), - ], + &mut [Operation::Read(&mut [0]), Operation::Read(&mut ack)], ) .map_err(Error::Transport)?; if ack == ACK { @@ -190,4 +180,4 @@ where Err(Error::InvalidAck) } } -} \ No newline at end of file +} diff --git a/pn532/src/interface/mod.rs b/pn532/src/interface/mod.rs index eef5d16..1eab1e6 100644 --- a/pn532/src/interface/mod.rs +++ b/pn532/src/interface/mod.rs @@ -10,10 +10,13 @@ mod i2c; use embedded_hal::delay::DelayNs; pub use i2c::I2cInterface; -use embedded_hal::digital::{ErrorKind, ErrorType, InputPin, OutputPin}; use crate::error::Error; +use crate::types::constants::{ + MAX_DATA_LEN, PN532_HOST_TO_PN532, PN532_PN532_TO_HOST, PN532_POSTAMBLE, PN532_PREAMBLE, + PN532_START_CODE_1, PN532_START_CODE_2, +}; use crate::PN532Command; -use crate::types::constants::{MAX_DATA_LEN, PN532_HOST_TO_PN532, PN532_PN532_TO_HOST, PN532_POSTAMBLE, PN532_PREAMBLE, PN532_START_CODE_1, PN532_START_CODE_2}; +use embedded_hal::digital::{ErrorKind, ErrorType, InputPin, OutputPin}; #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct UnreachError; @@ -28,7 +31,9 @@ impl embedded_hal::digital::Error for UnreachError { #[derive(Clone, Copy, Debug, Default)] pub struct NoReset; -impl ErrorType for NoReset { type Error = UnreachError; } +impl ErrorType for NoReset { + type Error = UnreachError; +} impl OutputPin for NoReset { fn set_low(&mut self) -> Result<(), Self::Error> { @@ -44,15 +49,17 @@ impl OutputPin for NoReset { #[derive(Clone, Copy, Debug, Default)] pub struct NoIrq; -impl ErrorType for NoIrq { type Error = UnreachError; } +impl ErrorType for NoIrq { + type Error = UnreachError; +} impl InputPin for NoIrq { fn is_high(&mut self) -> Result { - panic!("NoIrq should not be used"); + Err(UnreachError) } fn is_low(&mut self) -> Result { - panic!("NoIrq should not be used"); + Err(UnreachError) } } @@ -78,7 +85,7 @@ pub trait Interface { self.delay().delay_ms(500); Ok(()) } - + fn rst(&mut self) -> &mut RST; fn irq(&mut self) -> &mut IRQ; fn delay(&mut self) -> &mut DELAY; @@ -99,7 +106,10 @@ pub trait Interface { ) -> Result>; } -pub(crate) fn to_request_frame(header: &[u8], body: &[u8]) -> Result, Error> { +pub(crate) fn to_request_frame( + header: &[u8], + body: &[u8], +) -> Result, Error> { let data_len = header.len() + body.len() + 1; // TFI + payload if data_len > 0xFF { return Err(Error::NoSpace); @@ -128,7 +138,11 @@ pub(crate) fn to_request_frame(header: &[u8], body: &[u8]) -> Result(command: PN532Command, frame: &[u8], buf: &mut [u8]) -> Result> { +pub(crate) fn from_response_frame( + command: PN532Command, + frame: &[u8], + buf: &mut [u8], +) -> Result> { if frame[0] != PN532_PREAMBLE || frame[1] != PN532_START_CODE_1 || frame[2] != PN532_START_CODE_2 @@ -159,4 +173,4 @@ pub(crate) fn from_response_frame(command: PN532Command, frame: &[u8], buf: & } Ok(data_len) -} \ No newline at end of file +} diff --git a/pn532/src/lib.rs b/pn532/src/lib.rs index 4e304ac..332395e 100644 --- a/pn532/src/lib.rs +++ b/pn532/src/lib.rs @@ -46,12 +46,12 @@ extern crate alloc; pub mod commands; +pub(crate) mod driver; pub mod error; pub mod interface; -pub(crate) mod driver; +pub mod protocol; pub mod tg; pub mod types; -pub mod protocol; pub use commands::*; pub use driver::*; @@ -87,4 +87,3 @@ macro_rules! debug { macro_rules! trace { ($($arg:tt)+) => {}; } - diff --git a/pn532/src/protocol/felica/error.rs b/pn532/src/protocol/felica/error.rs index a7bdc49..714f1bc 100644 --- a/pn532/src/protocol/felica/error.rs +++ b/pn532/src/protocol/felica/error.rs @@ -6,4 +6,4 @@ pub enum Error { #[cfg(feature = "felica-lite-s")] #[error("MAC mismatch")] MACMismatch, -} \ No newline at end of file +} diff --git a/pn532/src/protocol/felica/felica_lite_s/auth.rs b/pn532/src/protocol/felica/felica_lite_s/auth.rs index f07c8ff..0fa54d4 100644 --- a/pn532/src/protocol/felica/felica_lite_s/auth.rs +++ b/pn532/src/protocol/felica/felica_lite_s/auth.rs @@ -1,22 +1,27 @@ +use crate::types::felica::felica_lite_s::auth::{ + AuthSession, CardKey, MacInput, RandomChallenge, SessionKeys, +}; +use crate::types::felica::{CardSession, FelicaLiteSBlock, FelicaLiteSServiceCode}; +use crate::{Interface, Pn532}; use des::cipher::{Block, BlockCipherEncrypt, Key, KeyInit}; use des::TdesEde2; use embedded_hal::delay::DelayNs; use embedded_hal::digital::{InputPin, OutputPin}; use rand_core::Rng; -use crate::{Interface, Pn532}; -use crate::types::felica::{CardSession, FelicaLiteSBlock, FelicaLiteSServiceCode}; -use crate::types::felica::felica_lite_s::auth::{AuthSession, CardKey, MacInput, RandomChallenge, SessionKeys}; impl Pn532 where RST: OutputPin, IRQ: InputPin, DELAY: DelayNs, - B: Interface + B: Interface, { - pub(crate) fn set_random_challenge(&mut self, card: &CardSession, rng: &mut impl Rng) - -> Result> - { + /// Write value to [`FelicaLiteSBlock::RC`] + pub(crate) fn set_random_challenge( + &mut self, + card: &CardSession, + rng: &mut impl Rng, + ) -> Result> { let mut data = [[0u8; 16]]; rng.fill_bytes(&mut data[0]); self.felica_write_without_encryption( @@ -29,6 +34,7 @@ where Ok(rc) } + /// Get [`SessionKeys`] pub(crate) fn get_session_keys( &mut self, rc: &RandomChallenge, @@ -44,7 +50,6 @@ where /// 2-key Triple DES-CBC MAC over `block_list` followed by `block_data`. pub(crate) fn compute_mac( - &mut self, session: &AuthSession, key_bytes: [u8; 16], block_list: &[u8; 8], @@ -55,7 +60,7 @@ where let mut state_bytes = session.rc.rc1(); state_bytes.reverse(); - + let mut block_list = block_list.clone(); block_list.reverse(); @@ -65,7 +70,7 @@ where let mut state_block = Block::::try_from(state_bytes).unwrap(); cipher.encrypt_block(&mut state_block); - + for chunk in block_data.chunks_exact(8) { let mut input = [0u8; 8]; input.copy_from_slice(chunk); @@ -90,17 +95,14 @@ where } /// Compute the MAC_A value for a Write With MAC operation. - pub(crate) fn compute_write_mac( - &mut self, - session: &AuthSession, - plain: &[u8; 24], - ) -> [u8; 8] { + pub(crate) fn compute_write_mac(&mut self, session: &AuthSession, plain: &[u8; 24]) -> [u8; 8] { let mut header = [0u8; 8]; header.copy_from_slice(&plain[0..8]); - self.compute_mac(session, session.sk.as_des_write(), &header, &plain[8..24]) + Self::compute_mac(session, session.sk.as_des_write(), &header, &plain[8..24]) } - pub(crate) fn check_auth( + /// Check if [`AuthSession`] is valid + pub(crate) fn check_session( &mut self, card: &CardSession, session: &AuthSession, @@ -109,14 +111,20 @@ where self.felica_read_without_encryption( card, &[FelicaLiteSServiceCode::Read], - &[FelicaLiteSBlock::ID, FelicaLiteSBlock::CKV, FelicaLiteSBlock::MACA], - &mut data + &[ + FelicaLiteSBlock::ID, + FelicaLiteSBlock::CKV, + FelicaLiteSBlock::MACA, + ], + &mut data, )?; - let block_list = &[FelicaLiteSBlock::ID, FelicaLiteSBlock::CKV, FelicaLiteSBlock::MACA]; - let input = MacInput::new( - &data, block_list - ); - let mac = self.compute_mac( + let block_list = &[ + FelicaLiteSBlock::ID, + FelicaLiteSBlock::CKV, + FelicaLiteSBlock::MACA, + ]; + let input = MacInput::new(&data, block_list); + let mac = Self::compute_mac( session, session.sk.as_des(), &input.block_list(), @@ -124,4 +132,4 @@ where ); Ok(mac == input.maca()) } -} \ No newline at end of file +} diff --git a/pn532/src/protocol/felica/felica_lite_s/issuance.rs b/pn532/src/protocol/felica/felica_lite_s/issuance.rs index e94fab5..af6d1b5 100644 --- a/pn532/src/protocol/felica/felica_lite_s/issuance.rs +++ b/pn532/src/protocol/felica/felica_lite_s/issuance.rs @@ -1,103 +1,105 @@ -use embedded_hal::delay::DelayNs; -use embedded_hal::digital::{InputPin, OutputPin}; -use crate::{Error, Interface, Pn532}; -use crate::types::felica::{CardSession, FelicaLiteSBlock}; use crate::types::felica::felica_lite_s::auth::{AuthSession, CardKey, CardKeyVersion}; use crate::types::felica::felica_lite_s::config::MemoryConfig; +use crate::types::felica::{CardSession, FelicaLiteSBlock}; +use crate::{Error, Interface, Pn532}; +use embedded_hal::delay::DelayNs; +use embedded_hal::digital::{InputPin, OutputPin}; impl Pn532 where RST: OutputPin, IRQ: InputPin, DELAY: DelayNs, - B: Interface + B: Interface, { - pub fn set_id(&mut self, card: &CardSession, arbitrary: [u8; 6], dfc: Option<[u8; 2]>) -> Result<(), Error> { + /// set value for [`FelicaLiteSBlock::ID`] + pub fn set_id( + &mut self, + card: &CardSession, + arbitrary: [u8; 6], + dfc: Option<[u8; 2]>, + ) -> Result<(), Error> { let dfc = dfc.unwrap_or_default(); let mut block = [0u8; 16]; block[8] = dfc[0]; block[9] = dfc[1]; block[10..16].copy_from_slice(&arbitrary); - self.write_block( - card, - &[FelicaLiteSBlock::ID], - &[block] - ) + self.write_block(card, &[FelicaLiteSBlock::ID], &[block]) } - + + /// get value for [`FelicaLiteSBlock::ID`] pub fn get_id(&mut self, card: &CardSession) -> Result<[u8; 16], Error> { let mut block = [[0u8; 16]]; - self.read_block( - card, - &[FelicaLiteSBlock::ID], - &mut block - )?; + self.read_block(card, &[FelicaLiteSBlock::ID], &mut block)?; crate::trace!("ID block: {:02X}", block); Ok(block[0]) } - - pub fn set_ck(&mut self, card: &CardSession, key: &CardKey) -> Result<(), Error> { - self.write_block( - card, - &[FelicaLiteSBlock::CK], - &[**key] - ) - } - - pub fn set_ck_with_mac(&mut self, card: &CardSession, session: &AuthSession, key: &CardKey) -> Result<(), Error> { - self.write_with_mac( - card, - session, - FelicaLiteSBlock::CK, - &**key - ) - } - - pub fn set_ckv(&mut self, card: &CardSession, version: &CardKeyVersion) -> Result<(), Error> { - self.write_block( - card, - &[FelicaLiteSBlock::CKV], - &[version.as_block()] - ) + + /// set value for [`FelicaLiteSBlock::CK`] + pub fn set_ck( + &mut self, + card: &CardSession, + key: &CardKey, + ) -> Result<(), Error> { + self.write_block(card, &[FelicaLiteSBlock::CK], &[**key]) } - pub fn set_ckv_with_mac(&mut self, card: &CardSession, session: &AuthSession, version: &CardKeyVersion) -> Result<(), Error> { - self.write_with_mac( - card, - session, - FelicaLiteSBlock::CKV, - &version.as_block() - ) + /// set value for [`FelicaLiteSBlock::CK`] with MAC + /// + /// see [`Pn532::try_authenticate`] to obtain a [`AuthSession`] + pub fn set_ck_with_mac( + &mut self, + card: &CardSession, + session: &AuthSession, + key: &CardKey, + ) -> Result<(), Error> { + self.write_with_mac(card, session, FelicaLiteSBlock::CK, &**key) } - - pub fn get_ckv(&mut self, card: &CardSession) -> Result> { + + /// set value for [`FelicaLiteSBlock::CKV`] + pub fn set_ckv( + &mut self, + card: &CardSession, + version: &CardKeyVersion, + ) -> Result<(), Error> { + self.write_block(card, &[FelicaLiteSBlock::CKV], &[version.as_block()]) + } + + /// set value for [`FelicaLiteSBlock::CKV`] with MAC + /// + /// see [`Pn532::try_authenticate`] to obtain a [`AuthSession`] + pub fn set_ckv_with_mac( + &mut self, + card: &CardSession, + session: &AuthSession, + version: &CardKeyVersion, + ) -> Result<(), Error> { + self.write_with_mac(card, session, FelicaLiteSBlock::CKV, &version.as_block()) + } + + pub fn get_ckv( + &mut self, + card: &CardSession, + ) -> Result> { let mut block = [[0u8; 16]]; - self.read_block( - card, - &[FelicaLiteSBlock::CKV], - &mut block - )?; + self.read_block(card, &[FelicaLiteSBlock::CKV], &mut block)?; crate::trace!("CKV block: {:02X}", block); Ok(CardKeyVersion::from(block[0])) } - + pub fn get_mc(&mut self, card: &CardSession) -> Result> { let mut block = [[0u8; 16]]; - self.read_block( - card, - &[FelicaLiteSBlock::MemoryConfig], - &mut block - )?; + self.read_block(card, &[FelicaLiteSBlock::MemoryConfig], &mut block)?; crate::trace!("MC block: {:02X}", block[0]); Ok(MemoryConfig::from(block[0])) } - - pub fn set_mc(&mut self, card: &CardSession, config: MemoryConfig) -> Result<(), Error> { + + pub fn set_mc( + &mut self, + card: &CardSession, + config: MemoryConfig, + ) -> Result<(), Error> { let block: [u8; 16] = config.into(); - self.write_block( - card, - &[FelicaLiteSBlock::MemoryConfig], - &[block] - ) + self.write_block(card, &[FelicaLiteSBlock::MemoryConfig], &[block]) } -} \ No newline at end of file +} diff --git a/pn532/src/protocol/felica/felica_lite_s/mod.rs b/pn532/src/protocol/felica/felica_lite_s/mod.rs index 2eaf293..bf93f05 100644 --- a/pn532/src/protocol/felica/felica_lite_s/mod.rs +++ b/pn532/src/protocol/felica/felica_lite_s/mod.rs @@ -1,29 +1,33 @@ -pub mod issuance; pub mod auth; +pub mod issuance; +use crate::protocol::felica::error::Error; +use crate::types::felica::felica_lite_s::auth::{AuthSession, CardKey, MacInput}; +use crate::types::felica::{AsBlock, CardSession, FelicaLiteSBlock, FelicaLiteSServiceCode}; +use crate::{Interface, Pn532}; use embedded_hal::delay::DelayNs; use embedded_hal::digital::{InputPin, OutputPin}; 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 Pn532 where RST: OutputPin, IRQ: InputPin, DELAY: DelayNs, - B: Interface + B: Interface, { - pub fn try_authenticate(&mut self, rng: &mut impl Rng, card: &CardSession, key: &CardKey) - -> Result, crate::Error> - { + /// Obtain a [`AuthSession`] and set [`FelicaLiteSBlock::STATE`] EXT_AUTH to 1 + pub fn try_authenticate( + &mut self, + rng: &mut impl Rng, + card: &CardSession, + key: &CardKey, + ) -> Result, crate::Error> { 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)?; - + let is_valid_auth = self.check_session(card, &session)?; + if !is_valid_auth { return Ok(None); } @@ -31,17 +35,20 @@ where Ok(Some(session)) } + /// Read a block with MAC + /// + /// see [`Pn532::try_authenticate`] to obtain a [`AuthSession`] pub fn read_with_mac( &mut self, card: &CardSession, session: &AuthSession, - block: impl AsBlock + block: impl AsBlock, ) -> Result<[u8; 16], crate::Error> { 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( + let mac = Self::compute_mac( session, session.sk.as_des(), &input.block_list(), @@ -53,8 +60,10 @@ where Ok(block_data[0]) } - - /// Write one data block protected by a Write With MAC (manual §5.4.4). + + /// Write a block with MAC + /// + /// see [`Pn532::try_authenticate`] to obtain a [`AuthSession`] pub fn write_with_mac( &mut self, card: &CardSession, @@ -95,31 +104,27 @@ where self.write_with_mac(card, session, FelicaLiteSBlock::STATE, &state) } + /// read a raw block + /// + /// see [`Pn532::read_with_mac`] to read with MAC pub fn read_block( &mut self, card: &CardSession, list: &[impl AsBlock; U], - data: &mut [[u8; 16]; U] + data: &mut [[u8; 16]; U], ) -> Result<(), crate::Error> { - self.felica_read_without_encryption( - card, - &[FelicaLiteSServiceCode::Read], - list, - data - ) + self.felica_read_without_encryption(card, &[FelicaLiteSServiceCode::Read], list, data) } - + + /// write a raw block + /// + /// see [`Pn532::write_with_mac`] to write with MAC pub fn write_block( &mut self, card: &CardSession, list: &[impl AsBlock; U], - data: &[[u8; 16]; U] + data: &[[u8; 16]; U], ) -> Result<(), crate::Error> { - self.felica_write_without_encryption( - card, - &[FelicaLiteSServiceCode::Write], - list, - data - ) + self.felica_write_without_encryption(card, &[FelicaLiteSServiceCode::Write], list, data) } -} \ No newline at end of file +} diff --git a/pn532/src/protocol/felica/mod.rs b/pn532/src/protocol/felica/mod.rs index fc44910..e049c88 100644 --- a/pn532/src/protocol/felica/mod.rs +++ b/pn532/src/protocol/felica/mod.rs @@ -1,32 +1,55 @@ -#[cfg(feature="felica-lite-s")] -mod felica_lite_s; pub mod error; +#[cfg(feature = "felica-lite-s")] +mod felica_lite_s; -use error::Error as FelicaError; 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 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}; +use error::Error as FelicaError; impl Pn532 where RST: OutputPin, IRQ: InputPin, DELAY: DelayNs, - B: Interface + B: Interface, { + fn validate_response( + response: &[u8], + response_len: usize, + valid_len: usize, + ) -> Result<(), Error> { + 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 + config: FelicaPollingConfig, ) -> Result, Error> { - if !matches!(config.baud_rate, BaudRate::Felica212kbps | BaudRate::Felica424kbps) { + if !matches!( + config.baud_rate, + BaudRate::Felica212kbps | BaudRate::Felica424kbps + ) { return Err(Error::InvalidBaudRate); } let system_code = config.system_code; @@ -58,9 +81,9 @@ where 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); @@ -113,13 +136,17 @@ where } /// Send a FeliCa "Request Response" command, returning the card's mode. - pub fn felica_request_response(&mut self, card: &FelicaPollingResponse) -> Result> { + pub fn felica_request_response( + &mut self, + card: &FelicaPollingResponse, + ) -> Result> { 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))?; + let response_len = + self.felica_send_command(&cmd, &mut response, Duration::from_millis(200))?; if response_len != 10 { return Err(Error::InvalidFrame); } @@ -136,31 +163,29 @@ where block_list: &[impl AsBlock; U], block_data: &mut [[u8; 16]; U], ) -> Result<(), Error> { - const COMMAND_SIZE: usize = - 1 // command code (0x06) + 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) + 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 let num_service = service_code_list.len(); - if num_service > FELICA_READ_MAX_SERVICE_NUM || U > FELICA_READ_MAX_BLOCK_NUM - { + if num_service > FELICA_READ_MAX_SERVICE_NUM || U > FELICA_READ_MAX_BLOCK_NUM { return Err(Error::InvalidParam); } // command let mut cmd = heapless::Vec::::new(); - cmd.push(FelicaCommand::ReadWithoutEncryption.into_bits()).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 { @@ -175,25 +200,18 @@ where // response let mut response = [0u8; RESPONSE_SIZE]; - // PMm gives the card's *processing* time only; add RF TX/RX + PN532 - // overhead margin so we never abandon a command mid-flight (which would - // desync the host/PN532 framing). let response_len = self.felica_send_command( &cmd, &mut response, - card.pmm.get_read_timeout(U, 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 >= 11 && (response[9] != 0 || response[10] != 0) { - return Err(Error::FelicaError(FelicaError::Status(response[9], response[10]))); - } - if response_len != 12 + 16 * U { - return Err(Error::InvalidFrame); - } + + Self::validate_response(&response, response_len, 12 + 16 * U)?; let mut k = 12; - for block in block_data.iter_mut().take(U) { + for block in block_data.iter_mut() { block.copy_from_slice(&response[k..k + 16]); k += 16; } @@ -208,24 +226,21 @@ where block_list: &[impl AsBlock; U], block_data: &[[u8; 16]; U], ) -> Result<(), Error> { - const COMMAND_SIZE: usize = - 1 // command code + 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 + const RESPONSE_SIZE: usize = 1 // command code + 8 // IDm + 1 // status[0] + 1; // status[1] - + // validate let num_service = service_code_list.len(); - if num_service > FELICA_WRITE_MAX_SERVICE_NUM || U > FELICA_WRITE_MAX_BLOCK_NUM - { + if num_service > FELICA_WRITE_MAX_SERVICE_NUM || U > FELICA_WRITE_MAX_BLOCK_NUM { return Err(Error::InvalidParam); } @@ -250,17 +265,14 @@ where // 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))) + &cmd, + &mut response, + card.pmm + .get_write_timeout(U, Some(Duration::from_millis(50))), )?; - - if response_len >= 11 && (response[9] != 0 || response[10] != 0) { - return Err(Error::FelicaError(FelicaError::Status(response[9], response[10]))); - } - if response_len != RESPONSE_SIZE { - return Err(Error::InvalidFrame); - } + + Self::validate_response(&response, response_len, RESPONSE_SIZE)?; + Ok(()) } } diff --git a/pn532/src/protocol/iso14443a/mod.rs b/pn532/src/protocol/iso14443a/mod.rs index 9cd1fd1..f52ec5d 100644 --- a/pn532/src/protocol/iso14443a/mod.rs +++ b/pn532/src/protocol/iso14443a/mod.rs @@ -1,9 +1,11 @@ ///! WIP - -use crate::commands::{PN532_COMMAND_INDATAEXCHANGE, PN532_COMMAND_INLISTPASSIVETARGET, PN532_COMMAND_INRELEASE}; +use crate::commands::{ + PN532_COMMAND_INDATAEXCHANGE, PN532_COMMAND_INLISTPASSIVETARGET, PN532_COMMAND_INRELEASE, +}; use crate::error::StatusCode; use crate::{BaudRate, Error, Interface, Pn532, Uid}; + impl Pn532 { /// Wait for an ISO14443A target and read its UID. /// @@ -36,7 +38,9 @@ impl Pn532 { let mut bytes = [0u8; 7]; bytes[..uid_len as usize].copy_from_slice(&self.buffer[6..6 + uid_len as usize]); - Ok(Some(Uid { bytes: heapless::Vec::from_array(bytes) })) + Ok(Some(Uid { + bytes: heapless::Vec::from_array(bytes), + })) } /// In-list a passive target (used before [`Self::in_data_exchange`]). @@ -74,7 +78,9 @@ impl Pn532 { let len = self.read_timeout(1000)?; if len == 0 || (self.buffer[0] & 0x3F) != 0 { - return Err(Error::Status(StatusCode::from_repr(self.buffer[0] & 0x3F).unwrap())); + return Err(Error::Status( + StatusCode::from_repr(self.buffer[0] & 0x3F).unwrap(), + )); } let data_len = len - 1; @@ -89,5 +95,5 @@ impl Pn532 { self.buffer[1] = relevant_target; self.send(2)?; self.read() - } -} \ No newline at end of file + } +} diff --git a/pn532/src/protocol/mifare/mod.rs b/pn532/src/protocol/mifare/mod.rs index b128319..5e17481 100644 --- a/pn532/src/protocol/mifare/mod.rs +++ b/pn532/src/protocol/mifare/mod.rs @@ -1,9 +1,12 @@ ///! 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::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}; + impl Pn532 { /// Whether the block number is the first block of a sector. pub fn mifare_classic_is_first_block(block: u32) -> bool { @@ -47,7 +50,9 @@ impl Pn532 { self.send(10 + uid_len)?; let len = self.read()?; if len == 0 || self.buffer[0] != 0x00 { - return Err(Error::Status(StatusCode::from_repr(self.buffer[0]).unwrap())); + return Err(Error::Status( + StatusCode::from_repr(self.buffer[0]).unwrap(), + )); } Ok(()) } @@ -66,7 +71,9 @@ impl Pn532 { let len = self.read()?; if len < 17 || self.buffer[0] != 0x00 { - return Err(Error::Status(StatusCode::from_repr(self.buffer[0]).unwrap())); + return Err(Error::Status( + StatusCode::from_repr(self.buffer[0]).unwrap(), + )); } data.copy_from_slice(&self.buffer[1..17]); Ok(()) @@ -193,7 +200,9 @@ impl Pn532 { let len = self.read()?; if len < 5 || self.buffer[0] != 0x00 { - return Err(Error::Status(StatusCode::from_repr(self.buffer[0]).unwrap())); + return Err(Error::Status( + StatusCode::from_repr(self.buffer[0]).unwrap(), + )); } buffer.copy_from_slice(&self.buffer[1..5]); Ok(()) @@ -217,4 +226,4 @@ impl Pn532 { self.read()?; Ok(()) } -} \ No newline at end of file +} diff --git a/pn532/src/protocol/mod.rs b/pn532/src/protocol/mod.rs index fe89b83..b58fa87 100644 --- a/pn532/src/protocol/mod.rs +++ b/pn532/src/protocol/mod.rs @@ -2,7 +2,7 @@ #[cfg(feature = "felica")] pub mod felica; -#[cfg(feature = "mifare-classic")] -mod mifare; #[cfg(feature = "iso14443a")] -pub mod iso14443a; \ No newline at end of file +pub mod iso14443a;#[cfg(feature = "mifare-classic")] +mod mifare; + diff --git a/pn532/src/tg.rs b/pn532/src/tg.rs index 010e1bc..9b8b5fe 100644 --- a/pn532/src/tg.rs +++ b/pn532/src/tg.rs @@ -22,7 +22,7 @@ where RST: OutputPin, IRQ: InputPin, DELAY: DelayNs, - B: Interface + B: Interface, { /// Initialize the PN532 as a target using a raw command frame. pub fn tg_init_as_target( @@ -44,10 +44,50 @@ where timeout_ms: u16, ) -> Result> { const COMMAND: [u8; 44] = [ - 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, + 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, ]; self.tg_init_as_target(&COMMAND, timeout_ms) } @@ -92,4 +132,4 @@ where } Ok(()) } -} \ No newline at end of file +} diff --git a/pn532/src/types/constants.rs b/pn532/src/types/constants.rs index 7038c37..8a5d748 100644 --- a/pn532/src/types/constants.rs +++ b/pn532/src/types/constants.rs @@ -26,9 +26,7 @@ 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 +pub const MAX_DATA_LEN: usize = 64; diff --git a/pn532/src/types/felica/constants.rs b/pn532/src/types/felica/constants.rs index 89ff4ce..d8ace2c 100644 --- a/pn532/src/types/felica/constants.rs +++ b/pn532/src/types/felica/constants.rs @@ -2,4 +2,4 @@ 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 +pub const FELICA_REQ_SERVICE_MAX_NODE_NUM: usize = 32; diff --git a/pn532/src/types/felica/felica_lite_s/auth/card_key.rs b/pn532/src/types/felica/felica_lite_s/auth/card_key.rs index 09d75d5..fa9fe33 100644 --- a/pn532/src/types/felica/felica_lite_s/auth/card_key.rs +++ b/pn532/src/types/felica/felica_lite_s/auth/card_key.rs @@ -18,8 +18,8 @@ impl CardKey { let mut des_key = [0u8; 16]; for i in 0..8 { - des_key[7-i] = card_key[i]; - des_key[15-i] = card_key[i + 8]; + des_key[7 - i] = card_key[i]; + des_key[15 - i] = card_key[i + 8]; } des_key @@ -61,7 +61,7 @@ impl CardKeyVersion { let bytes = version.to_be_bytes(); CardKeyVersion(bytes) } - + pub fn as_block(&self) -> [u8; 16] { let mut block = [0u8; 16]; block[0] = self.0[0]; @@ -87,4 +87,4 @@ impl Display for CardKeyVersion { write!(f, "{:?}", self.0) } } -} \ 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 a5f265b..84cacfe 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,11 +1,11 @@ -use crate::types::felica::AsBlock; use crate::types::felica::constants::FELICA_READ_MAX_BLOCK_NUM; +use crate::types::felica::AsBlock; #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct MacInput { block_data: [[u8; 16]; N], - block_list: [u8; N] + block_list: [u8; N], } impl MacInput { @@ -16,16 +16,16 @@ impl MacInput { if N < 2 { panic!("block should have at least 2 blocks"); } - let mut block_nums= [0u8; N]; - for (i, block) in blocks_list.iter().enumerate(){ + let mut block_nums = [0u8; N]; + for (i, block) in blocks_list.iter().enumerate() { block_nums[i] = block.as_block() as u8; } Self { block_data: *block_data, - block_list: block_nums + block_list: block_nums, } } - + pub fn block_data(&self) -> heapless::Vec { let mut data = heapless::Vec::::new(); for block in self.block_data.iter().take(N - 1) { @@ -33,8 +33,7 @@ impl MacInput { } data } - - + pub fn block_list(&self) -> [u8; 8] { let mut data = [0xFFu8; 8]; for (i, block) in self.block_list.iter().enumerate() { @@ -43,8 +42,8 @@ impl MacInput { } data } - + pub fn maca(&self) -> [u8; 8] { self.block_data[N - 1][0..8].try_into().unwrap() } -} \ No newline at end of file +} diff --git a/pn532/src/types/felica/felica_lite_s/auth/mod.rs b/pn532/src/types/felica/felica_lite_s/auth/mod.rs index 8a9bf0a..900b3c9 100644 --- a/pn532/src/types/felica/felica_lite_s/auth/mod.rs +++ b/pn532/src/types/felica/felica_lite_s/auth/mod.rs @@ -1,23 +1,23 @@ mod card_key; +mod mac_input; mod rc; mod sk; -mod mac_input; pub use card_key::*; +pub use mac_input::*; pub use rc::*; pub use sk::*; -pub use mac_input::*; #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[readonly::make] pub struct AuthSession { pub sk: SessionKeys, - pub rc: RandomChallenge + pub rc: RandomChallenge, } impl AuthSession { pub fn new(sk: SessionKeys, rc: RandomChallenge) -> Self { AuthSession { sk, rc } } -} \ No newline at end of file +} diff --git a/pn532/src/types/felica/felica_lite_s/auth/rc.rs b/pn532/src/types/felica/felica_lite_s/auth/rc.rs index 9b34630..40d26b5 100644 --- a/pn532/src/types/felica/felica_lite_s/auth/rc.rs +++ b/pn532/src/types/felica/felica_lite_s/auth/rc.rs @@ -26,13 +26,13 @@ impl RandomChallenge { let mut des = [0u8; 16]; for i in 0..8 { - des[7-i] = rc[i]; - des[15-i] = rc[i + 8]; + des[7 - i] = rc[i]; + des[15 - i] = rc[i + 8]; } des } - + pub fn rc1(&self) -> [u8; 8] { let rc = self.0; rc[0..8].try_into().unwrap() @@ -48,11 +48,16 @@ impl Display for RandomChallenge { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { #[cfg(feature = "alloc")] { - write!(f, "{},{}", hex::encode_upper(self.rc1()), hex::encode_upper(self.rc2())) + write!( + f, + "{},{}", + hex::encode_upper(self.rc1()), + hex::encode_upper(self.rc2()) + ) } #[cfg(not(feature = "alloc"))] { write!(f, "{:?}", self.0) } } -} \ No newline at end of file +} diff --git a/pn532/src/types/felica/felica_lite_s/auth/sk.rs b/pn532/src/types/felica/felica_lite_s/auth/sk.rs index ef1d8cd..2aadf57 100644 --- a/pn532/src/types/felica/felica_lite_s/auth/sk.rs +++ b/pn532/src/types/felica/felica_lite_s/auth/sk.rs @@ -1,6 +1,6 @@ +use crate::types::felica::felica_lite_s::auth::RandomChallenge; use cbc::cipher::{Block, BlockCipherEncrypt}; use des::TdesEde2; -use crate::types::felica::felica_lite_s::auth::RandomChallenge; #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -39,7 +39,7 @@ impl SessionKeys { sk2.reverse(); Self { sk1, sk2 } } - + pub fn as_des(&self) -> [u8; 16] { Self::key_des(&self.sk1, &self.sk2) } @@ -60,19 +60,19 @@ impl SessionKeys { key_bytes } - + pub fn as_bytes(&self) -> [u8; 16] { let mut key_bytes = [0u8; 16]; key_bytes[0..8].copy_from_slice(&self.sk1); key_bytes[8..16].copy_from_slice(&self.sk2); key_bytes } - + pub fn sk1(&self) -> [u8; 8] { self.sk1 } - + pub fn sk2(&self) -> [u8; 8] { self.sk2 } -} \ No newline at end of file +} diff --git a/pn532/src/types/felica/felica_lite_s/config/access_level.rs b/pn532/src/types/felica/felica_lite_s/config/access_level.rs index b331e9b..66499b2 100644 --- a/pn532/src/types/felica/felica_lite_s/config/access_level.rs +++ b/pn532/src/types/felica/felica_lite_s/config/access_level.rs @@ -7,4 +7,4 @@ pub enum AccessLevel { ReadOnly = 0, #[base] ReadWrite = 1, -} \ No newline at end of file +} diff --git a/pn532/src/types/felica/felica_lite_s/mod.rs b/pn532/src/types/felica/felica_lite_s/mod.rs index d88de8c..7ea2c7f 100644 --- a/pn532/src/types/felica/felica_lite_s/mod.rs +++ b/pn532/src/types/felica/felica_lite_s/mod.rs @@ -1,12 +1,11 @@ ///! 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; pub mod config; -use bitfields::bitflag; use super::AsBlock; use super::AsServiceCode; +use bitfields::bitflag; /// Service Code #[derive(Debug, PartialEq, Eq)] @@ -18,7 +17,7 @@ pub enum ServiceCode { /// FeliCa Lite read service code (read without key). Read = 0x000B, /// FeliCa Lite write service code (write without key). - Write = 0x0009 + Write = 0x0009, } impl AsServiceCode for ServiceCode { @@ -71,11 +70,11 @@ pub enum Block { /// MAC_A block. MACA = 0x8091, /// STATE block (EXT_AUTH / POLL_DIS). - STATE = 0x8092 + STATE = 0x8092, } impl AsBlock for Block { fn as_block(&self) -> u16 { *self as u16 } -} \ No newline at end of file +} diff --git a/pn532/src/types/felica/ic_type.rs b/pn532/src/types/felica/ic_type.rs index c0474d4..e2d1526 100644 --- a/pn532/src/types/felica/ic_type.rs +++ b/pn532/src/types/felica/ic_type.rs @@ -20,5 +20,5 @@ pub enum MobileICType { /// 0x10~0x13 V2(u8), /// 0x06~0x07 - V1(u8) -} \ No newline at end of file + V1(u8), +} diff --git a/pn532/src/types/felica/idm.rs b/pn532/src/types/felica/idm.rs index 970ab2f..40ed7ff 100644 --- a/pn532/src/types/felica/idm.rs +++ b/pn532/src/types/felica/idm.rs @@ -1,12 +1,12 @@ -use core::fmt::{Display, Formatter}; use bitfields::bitfield; +use core::fmt::{Display, Formatter}; #[bitfield([u8; 8], from_endian = little, into_endian = little)] #[derive(PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct IDm { pub manufacturer: [u8; 2], - pub card_id: [u8; 6] + pub card_id: [u8; 6], } impl Display for IDm { @@ -20,4 +20,4 @@ impl Display for IDm { write!(f, "{:?}", self) } } -} \ No newline at end of file +} diff --git a/pn532/src/types/felica/mod.rs b/pn532/src/types/felica/mod.rs index a8487d5..e7191ca 100644 --- a/pn532/src/types/felica/mod.rs +++ b/pn532/src/types/felica/mod.rs @@ -1,19 +1,19 @@ -mod polling; -mod pmm; -mod idm; +pub mod constants; #[cfg(feature = "felica-lite-s")] pub mod felica_lite_s; pub mod ic_type; -pub mod constants; +mod idm; +mod pmm; +mod polling; -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}; +pub use idm::*; +pub use pmm::*; +pub use polling::CardSession; +pub use polling::PollingConfig as FelicaPollingConfig; +pub use polling::PollingRequestCode as FelicaPollingRequestCode; +pub use polling::PollingResponse as FelicaPollingResponse; pub trait AsServiceCode { fn as_service_code(&self) -> u16; @@ -85,4 +85,4 @@ pub enum UriIdentifierCode { UrnEpcRaw = 0x21, UrnEpc = 0x22, UrnNfc = 0x23, -} \ No newline at end of file +} diff --git a/pn532/src/types/felica/pmm.rs b/pn532/src/types/felica/pmm.rs index a480df6..3047931 100644 --- a/pn532/src/types/felica/pmm.rs +++ b/pn532/src/types/felica/pmm.rs @@ -1,6 +1,6 @@ +use bitfields::bitfield; use core::fmt::{Display, Formatter}; use core::time::Duration; -use bitfields::bitfield; #[bitfield([u8; 8], from_endian = little, into_endian = little)] #[derive(PartialEq, Eq)] @@ -52,4 +52,4 @@ impl Display for PMm { write!(f, "{:?}", self) } } -} \ No newline at end of file +} diff --git a/pn532/src/types/felica/polling.rs b/pn532/src/types/felica/polling.rs index 7de5e00..00adc65 100644 --- a/pn532/src/types/felica/polling.rs +++ b/pn532/src/types/felica/polling.rs @@ -1,9 +1,9 @@ +use super::{FelicaPollingRequestCode, IDm, PMm}; +use crate::types::BaudRate; use alloc::string::ToString; +use bitfields::bitflag; use core::fmt::{Display, Formatter}; use core::time::Duration; -use bitfields::bitflag; -use crate::types::BaudRate; -use super::{FelicaPollingRequestCode, IDm, PMm}; #[derive(Debug, PartialEq, Eq, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -12,7 +12,7 @@ pub enum PollingRequestCode { NoRequest = 0x00, #[default] SystemCode = 0x01, - CommunicationPerformance = 0x02 + CommunicationPerformance = 0x02, } #[derive(Debug, PartialEq, Eq, Default)] @@ -27,7 +27,6 @@ pub enum PollingTimeSlot { _16 = 0x0F, } - /// Result of a successful FeliCa polling request. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -44,9 +43,13 @@ impl Display for PollingResponse { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { let system_code = match self.system_code_response { Some(system_code) => hex::encode_upper(system_code.to_be_bytes().to_vec()), - None => "None".to_string() + None => "None".to_string(), }; - write!(f, "IDm: {}, PMm, {}, System Code: {}", self.idm, self.pmm, system_code) + write!( + f, + "IDm: {}, PMm, {}, System Code: {}", + self.idm, self.pmm, system_code + ) } } @@ -55,7 +58,7 @@ impl Display for PollingResponse { #[readonly::make] pub struct CardSession { pub idm: IDm, - pub pmm: PMm + pub pmm: PMm, } impl CardSession { @@ -68,7 +71,7 @@ impl From for CardSession { fn from(value: PollingResponse) -> Self { Self { idm: value.idm, - pmm: value.pmm + pmm: value.pmm, } } } @@ -97,4 +100,4 @@ impl Default for PollingConfig { timeout: Duration::from_secs(1), } } -} \ No newline at end of file +} diff --git a/pn532/src/types/mod.rs b/pn532/src/types/mod.rs index 7f08496..2afee2d 100644 --- a/pn532/src/types/mod.rs +++ b/pn532/src/types/mod.rs @@ -1,8 +1,8 @@ use bitfields::bitflag; +pub mod constants; #[cfg(feature = "felica")] pub mod felica; -pub mod constants; /// Passive target baud rates supported by [`crate::Pn532::read_passive_target_id`]. #[derive(PartialEq, Eq, Debug)] @@ -21,4 +21,4 @@ impl From for u8 { fn from(value: BaudRate) -> Self { value as u8 } -} \ No newline at end of file +}