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