Add read/write for felica

felica types refactor
This commit is contained in:
2026-08-27 10:02:50 +08:00
parent 0810cdb892
commit 86373a2d33
19 changed files with 875 additions and 184 deletions
+35 -4
View File
@@ -108,10 +108,41 @@ pub const PN532_GPIO_P34: u8 = 4;
pub const PN532_GPIO_P35: u8 = 5;
// FeliCa limits.
pub const FELICA_READ_MAX_SERVICE_NUM: usize = 16;
pub const FELICA_READ_MAX_BLOCK_NUM: usize = 12;
pub const FELICA_WRITE_MAX_SERVICE_NUM: usize = 16;
pub const FELICA_WRITE_MAX_BLOCK_NUM: usize = 10;
pub const fn felica_read_max_service_num() -> usize {
if cfg!(feature = "felica-lite-s") {
8
} else {
16
}
}
pub const fn felica_read_max_block_num() -> usize {
if cfg!(feature = "felica-lite-s") {
4
} else {
12
}
}
pub const fn felica_write_max_service_num() -> usize {
if cfg!(feature = "felica-lite-s") {
4
} else {
16
}
}
pub const fn felica_write_max_block_num() -> usize {
if cfg!(feature = "felica-lite-s") {
2
} else {
10
}
}
pub const FELICA_READ_MAX_SERVICE_NUM: usize = felica_read_max_service_num();
pub const FELICA_READ_MAX_BLOCK_NUM: usize = felica_read_max_block_num();
pub const FELICA_WRITE_MAX_SERVICE_NUM: usize = felica_write_max_service_num();
pub const FELICA_WRITE_MAX_BLOCK_NUM: usize = felica_write_max_block_num();
pub const FELICA_REQ_SERVICE_MAX_NODE_NUM: usize = 32;
// Frame protocol constants.
+30 -23
View File
@@ -3,8 +3,8 @@
use crate::commands::*;
use crate::error::Error;
use crate::interface::Interface;
pub(crate) const PACKET_BUFFER_SIZE: usize = 64;
use crate::StatusCode;
use core::time::Duration;
/// A card UID read by [`Pn532::read_passive_target_id`].
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -46,35 +46,20 @@ pub enum TargetInitStatus {
Failed,
}
/// Result of a successful FeliCa polling request.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FelicaPollingResponse {
/// The card's IDm (NFCID2).
pub idm: [u8; 8],
/// The card's PMm (PAD).
pub pmm: [u8; 8],
/// The card's system code, when returned.
pub system_code_response: Option<u16>,
}
/// Driver for the NXP PN532 NFC controller.
pub struct Pn532<B: Interface> {
pub struct Pn532<B: Interface, const N: usize = 64> {
pub(crate) interface: B,
pub(crate) in_listed_tag: u8,
pub(crate) felica_idm: [u8; 8],
pub(crate) felica_pmm: [u8; 8],
pub(crate) buffer: [u8; PACKET_BUFFER_SIZE],
pub(crate) buffer: [u8; N],
}
impl<B: Interface> Pn532<B> {
impl<B: Interface, const N: usize> Pn532<B, N> {
/// Create a driver over the given transport interface.
pub fn new(interface: B) -> Self {
Self {
interface,
in_listed_tag: 0,
felica_idm: [0; 8],
felica_pmm: [0; 8],
buffer: [0; PACKET_BUFFER_SIZE],
buffer: [0; N],
}
}
@@ -103,8 +88,8 @@ impl<B: Interface> Pn532<B> {
self.interface.read_response(&mut self.buffer, 1000)
}
pub(crate) fn read_timeout(&mut self, timeout_ms: u16) -> Result<usize, Error<B::TransportError>> {
self.interface.read_response(&mut self.buffer, timeout_ms)
pub(crate) fn read_timeout(&mut self, timeout: Duration) -> Result<usize, Error<B::TransportError>> {
self.interface.read_response(&mut self.buffer, timeout.as_millis() as u16)
}
// -- generic PN532 functions -------------------------------------------
@@ -210,4 +195,26 @@ impl<B: Interface> Pn532<B> {
self.read()?;
Ok(())
}
/// return error if current buffer contains an error code
pub(crate) fn validate_buffer(&self, len: usize) -> Result<(), Error<B::TransportError>> {
if len == 0 || (self.buffer[0] & 0x3F) != 0 {
Err(Error::Status(StatusCode::from_repr(self.buffer[0] & 0x3F).unwrap()))
} else {
Ok(())
}
}
/// Release the card.
pub fn release(&mut self) -> Result<(), Error<B::TransportError>> {
self.buffer[0] = PN532_COMMAND_INRELEASE;
self.buffer[1] = 0x00;
self.send(2)?;
let len = self.read_timeout(Duration::from_secs(1))?;
self.validate_buffer(len)?;
Ok(())
}
}
+8 -10
View File
@@ -1,3 +1,4 @@
use core::fmt::Debug;
use defmt::Debug2Format;
use strum::FromRepr;
@@ -25,20 +26,17 @@ pub enum Error<E> {
/// The device returned a non-zero status code.
#[error("Received non zero status code: {0:?}")]
Status(StatusCode),
#[error("Invalid baud rate")]
InvalidBaudRate,
#[cfg(feature = "felica")]
#[error("Felica error")]
FelicaError
}
#[cfg(feature = "defmt")]
impl<E: defmt::Format> defmt::Format for Error<E> {
impl<E: Debug> defmt::Format for Error<E> {
fn format(&self, fmt: defmt::Formatter) {
match self {
Error::Transport(e) => defmt::write!(fmt, "transport error: {}", e),
Error::Timeout => defmt::write!(fmt, "timeout"),
Error::InvalidAck => defmt::write!(fmt, "invalid ACK frame"),
Error::InvalidFrame => defmt::write!(fmt, "invalid frame"),
Error::NoSpace => defmt::write!(fmt, "not enough space in buffer"),
Error::InvalidParam => defmt::write!(fmt, "invalid parameter"),
Error::Status(code) => defmt::write!(fmt, "status error {:?}", code),
}
defmt::write!(fmt, "{:?}", Debug2Format(self))
}
}
+121 -87
View File
@@ -1,34 +1,43 @@
mod types;
pub(crate) use types::AsBlock;
pub use types::Block as FelicaBlock;
pub use types::PollingRequestCode as FelicaPollingRequestCode;
pub use types::PollingResponse as FelicaPollingResponse;
pub use types::ServiceCode as FelicaServiceCode;
use crate::commands::*;
use crate::driver::Pn532;
use crate::error::{Error, StatusCode};
use crate::interface::Interface;
use crate::commands::{FELICA_CMD_POLLING, FELICA_CMD_READ_WITHOUT_ENCRYPTION, FELICA_CMD_REQUEST_RESPONSE, FELICA_CMD_REQUEST_SERVICE, FELICA_CMD_REQUEST_SYSTEM_CODE, FELICA_CMD_WRITE_WITHOUT_ENCRYPTION, FELICA_READ_MAX_BLOCK_NUM, FELICA_READ_MAX_SERVICE_NUM, FELICA_REQ_SERVICE_MAX_NODE_NUM, FELICA_WRITE_MAX_BLOCK_NUM, FELICA_WRITE_MAX_SERVICE_NUM, PN532_COMMAND_INDATAEXCHANGE, PN532_COMMAND_INLISTPASSIVETARGET, PN532_COMMAND_INRELEASE};
use crate::{BaudRate, FelicaPollingResponse};
pub(crate) use types::AsBlock;
pub use types::Block as FelicaBlock;
use crate::BaudRate;
use core::time::Duration;
use types::*;
impl<B: Interface> Pn532<B> {
impl<B: Interface, const N: usize> Pn532<B, N> {
/// Poll for a FeliCa card.
pub fn felica_polling(
&mut self,
system_code: u16,
request_code: u8,
timeout_ms: u16,
) -> Result<Option<FelicaPollingResponse>, Error<B::TransportError>> {
system_code: Option<u16>,
baud_rate: BaudRate,
request_code: PollingRequestCode,
timeout: Duration,
) -> Result<Option<PollingResponse>, Error<B::TransportError>> {
if !matches!(baud_rate, BaudRate::Felica212kbps | BaudRate::Felica424kbps) {
return Err(Error::InvalidBaudRate);
}
let system_code = system_code.unwrap_or(0xFFFF);
self.buffer[0] = PN532_COMMAND_INLISTPASSIVETARGET;
self.buffer[1] = 1;
self.buffer[2] = BaudRate::Felica212kbps.into();
self.buffer[2] = baud_rate.into();
self.buffer[3] = FELICA_CMD_POLLING;
self.buffer[4] = (system_code >> 8) as u8;
self.buffer[5] = system_code as u8;
self.buffer[6] = request_code;
self.buffer[6] = request_code as u8;
self.buffer[7] = 0;
self.send(8)?;
match self.read_timeout(timeout_ms) {
match self.read_timeout(timeout) {
Err(Error::Timeout) => return Ok(None),
Err(e) => return Err(e),
Ok(_) => {}
@@ -49,11 +58,11 @@ impl<B: Interface> Pn532<B> {
let mut idm = [0u8; 8];
idm.copy_from_slice(&self.buffer[4..12]);
self.felica_idm = idm;
let idm = IDm::from(idm);
let mut pmm = [0u8; 8];
pmm.copy_from_slice(&self.buffer[12..20]);
self.felica_pmm = pmm;
let pmm = PMm::from(pmm);
let system_code_response = if response_length == 20 {
Some(u16::from_be_bytes([self.buffer[20], self.buffer[21]]))
@@ -61,7 +70,7 @@ impl<B: Interface> Pn532<B> {
None
};
Ok(Some(FelicaPollingResponse {
Ok(Some(PollingResponse {
idm,
pmm,
system_code_response,
@@ -75,6 +84,7 @@ impl<B: Interface> Pn532<B> {
&mut self,
command: &[u8],
response: &mut [u8],
timeout: Duration,
) -> Result<usize, Error<B::TransportError>> {
if command.len() > 0xFE {
return Err(Error::InvalidParam);
@@ -84,10 +94,8 @@ impl<B: Interface> Pn532<B> {
self.buffer[2] = (command.len() + 1) as u8;
self.send_with_body(3, command)?;
let len = self.read_timeout(200)?;
if len == 0 || (self.buffer[0] & 0x3F) != 0 {
return Err(Error::Status(StatusCode::from_repr(self.buffer[0] & 0x3F).unwrap()));
}
let len = self.read_timeout(timeout)?;
self.validate_buffer(len)?;
let response_len = self.buffer[1] as usize - 1;
if len - 2 != response_len {
@@ -103,6 +111,7 @@ impl<B: Interface> Pn532<B> {
/// Send a FeliCa "Request Service" command.
pub fn felica_request_service(
&mut self,
card: &IDm,
node_code_list: &[u16],
key_versions: &mut [u16],
) -> Result<(), Error<B::TransportError>> {
@@ -115,7 +124,7 @@ impl<B: Interface> Pn532<B> {
let mut j = 0;
cmd[j] = FELICA_CMD_REQUEST_SERVICE;
j += 1;
cmd[j..j + 8].copy_from_slice(&self.felica_idm);
cmd[j..j + 8].copy_from_slice(&card.to_bytes());
j += 8;
cmd[j] = num_node as u8;
j += 1;
@@ -126,7 +135,11 @@ impl<B: Interface> Pn532<B> {
}
let mut response = [0u8; 10 + 2 * FELICA_REQ_SERVICE_MAX_NODE_NUM];
let response_len = self.felica_send_command(&cmd[..j], &mut response)?;
let response_len = self.felica_send_command(
&cmd[..j],
&mut response,
Duration::from_millis(200)
)?;
if response_len != 10 + 2 * num_node {
return Err(Error::InvalidFrame);
}
@@ -137,13 +150,13 @@ impl<B: Interface> Pn532<B> {
}
/// Send a FeliCa "Request Response" command, returning the card's mode.
pub fn felica_request_response(&mut self) -> Result<u8, Error<B::TransportError>> {
pub fn felica_request_response(&mut self, card: &PollingResponse) -> Result<u8, Error<B::TransportError>> {
let mut cmd = [0u8; 9];
cmd[0] = FELICA_CMD_REQUEST_RESPONSE;
cmd[1..9].copy_from_slice(&self.felica_idm);
cmd[1..9].copy_from_slice(&card.idm.to_bytes());
let mut response = [0u8; 10];
let response_len = self.felica_send_command(&cmd, &mut response)?;
let response_len = self.felica_send_command(&cmd, &mut response, Duration::from_millis(200))?;
if response_len != 10 {
return Err(Error::InvalidFrame);
}
@@ -155,10 +168,28 @@ impl<B: Interface> Pn532<B> {
/// Note: the number of blocks is limited by the 64-byte response buffer.
pub fn felica_read_without_encryption(
&mut self,
service_code_list: &[u16],
block_list: &[u16],
card: &IDm,
pmm: &PMm,
service_code_list: &[impl AsServiceCode],
block_list: &[impl AsBlock],
block_data: &mut [[u8; 16]],
) -> Result<(), Error<B::TransportError>> {
const COMMAND_SIZE: usize =
1 // command code (0x06)
+ 8 // idm
+ 1 // service length
+ 2 * FELICA_READ_MAX_SERVICE_NUM // service code list (2 * service length)
+ 1 // block length (1<=n<=4)
+ 2 * FELICA_READ_MAX_BLOCK_NUM; // block list (2n<=N<=3n), hardcoded to use 2 byte block list only
const RESPONSE_SIZE: usize =
1 // response code (0x07)
+ 8 // IDm
+ 1 // status[0]
+ 1 // status[1]
+ 1 // block length
+ 16 * FELICA_READ_MAX_BLOCK_NUM; // block data
// validate
let num_service = service_code_list.len();
let num_block = block_list.len();
if num_service > FELICA_READ_MAX_SERVICE_NUM
@@ -168,29 +199,31 @@ impl<B: Interface> Pn532<B> {
return Err(Error::InvalidParam);
}
let mut cmd = [0u8; 1 + 8 + 1 + 2 * FELICA_READ_MAX_SERVICE_NUM + 1 + 2 * FELICA_READ_MAX_BLOCK_NUM];
let mut j = 0;
cmd[j] = FELICA_CMD_READ_WITHOUT_ENCRYPTION;
j += 1;
cmd[j..j + 8].copy_from_slice(&self.felica_idm);
j += 8;
cmd[j] = num_service as u8;
j += 1;
for &sc in service_code_list {
cmd[j] = sc as u8;
cmd[j + 1] = (sc >> 8) as u8;
j += 2;
// command
let mut cmd = heapless::Vec::<u8, COMMAND_SIZE>::new();
cmd.push(FELICA_CMD_READ_WITHOUT_ENCRYPTION).unwrap();
cmd.extend_from_slice(&card.to_bytes()).unwrap();
cmd.push(num_service as u8).unwrap();
for sc in service_code_list {
let sc: [u8; 2] = sc.as_service_code().to_le_bytes();
cmd.extend_from_slice(&sc).unwrap();
}
cmd[j] = num_block as u8;
j += 1;
for &bl in block_list {
cmd[j] = (bl >> 8) as u8;
cmd[j + 1] = bl as u8;
j += 2;
cmd.push(num_block as u8).unwrap();
for bl in block_list {
let bl: [u8; 2] = bl.as_block().to_be_bytes();
cmd.extend_from_slice(&bl).unwrap();
}
let mut response = [0u8; 12 + 16 * FELICA_READ_MAX_BLOCK_NUM];
let response_len = self.felica_send_command(&cmd[..j], &mut response)?;
// 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,
pmm.get_read_timeout(num_block, Some(Duration::from_millis(50)))
)?;
if response_len != 12 + 16 * num_block {
return Err(Error::InvalidFrame);
}
@@ -209,10 +242,27 @@ impl<B: Interface> Pn532<B> {
/// Send a FeliCa "Write Without Encryption" command.
pub fn felica_write_without_encryption(
&mut self,
service_code_list: &[u16],
card: &IDm,
pmm: &PMm,
service_code_list: &[impl AsServiceCode],
block_list: &[impl AsBlock],
block_data: &[[u8; 16]],
) -> Result<(), Error<B::TransportError>> {
const COMMAND_SIZE: usize =
1 // command code
+ 8 // IDm
+ 1 // service len (m)
+ 2 * FELICA_WRITE_MAX_SERVICE_NUM // service code (2 * m)
+ 1 // block len (n)
+ 2 * FELICA_WRITE_MAX_BLOCK_NUM // block list (2n<=N<=3n) (hardcoded to 2 bytes only)
+ 16 * FELICA_WRITE_MAX_BLOCK_NUM; // block data (16 * n)
const RESPONSE_SIZE: usize =
1 // command code
+ 8 // IDm
+ 1 // status[0]
+ 1; // status[1]
// validate
let num_service = service_code_list.len();
let num_block = block_list.len();
if num_service > FELICA_WRITE_MAX_SERVICE_NUM
@@ -222,36 +272,32 @@ impl<B: Interface> Pn532<B> {
return Err(Error::InvalidParam);
}
let mut cmd = [0u8; 1 + 8 + 1 + 2 * FELICA_WRITE_MAX_SERVICE_NUM + 1 + 2 * FELICA_WRITE_MAX_BLOCK_NUM
+ 16 * FELICA_WRITE_MAX_BLOCK_NUM];
let mut j = 0;
cmd[j] = FELICA_CMD_WRITE_WITHOUT_ENCRYPTION;
j += 1;
cmd[j..j + 8].copy_from_slice(&self.felica_idm);
j += 8;
cmd[j] = num_service as u8;
j += 1;
for &sc in service_code_list {
cmd[j] = sc as u8;
cmd[j + 1] = (sc >> 8) as u8;
j += 2;
// command
let mut cmd = heapless::Vec::<u8, COMMAND_SIZE>::new();
cmd.push(FELICA_CMD_WRITE_WITHOUT_ENCRYPTION).unwrap();
cmd.extend_from_slice(&card.to_bytes()).unwrap();
cmd.push(num_service as u8).unwrap();
for sc in service_code_list {
let sc = sc.as_service_code().to_le_bytes();
cmd.extend_from_slice(&sc).unwrap();
}
cmd[j] = num_block as u8;
j += 1;
cmd.push(num_block as u8).unwrap();
for block in block_list {
let block = block.as_block();
cmd[j] = (block >> 8) as u8;
cmd[j + 1] = block as u8;
j += 2;
let block = block.as_block().to_be_bytes();
cmd.extend_from_slice(&block).unwrap();
}
for block in block_data.iter().take(num_block) {
cmd[j..j + 16].copy_from_slice(block);
j += 16;
cmd.extend_from_slice(block).unwrap();
}
let mut response = [0u8; 11];
let response_len = self.felica_send_command(&cmd[..j], &mut response)?;
if response_len != 11 {
// response
let mut response = [0u8; RESPONSE_SIZE];
let response_len = self.felica_send_command(
&cmd,
&mut response,
pmm.get_write_timeout(num_block, Some(Duration::from_millis(50)))
)?;
if response_len != RESPONSE_SIZE {
return Err(Error::InvalidFrame);
}
if response[9] != 0 || response[10] != 0 {
@@ -265,14 +311,15 @@ impl<B: Interface> Pn532<B> {
/// Returns the number of system codes written to `system_code_list`.
pub fn felica_request_system_code(
&mut self,
card: &IDm,
system_code_list: &mut [impl AsServiceCode],
) -> Result<usize, Error<B::TransportError>> {
let mut cmd = [0u8; 9];
cmd[0] = FELICA_CMD_REQUEST_SYSTEM_CODE;
cmd[1..9].copy_from_slice(&self.felica_idm);
cmd[1..9].copy_from_slice(&card.to_bytes());
let mut response = [0u8; 10 + 2 * 16];
let response_len = self.felica_send_command(&cmd, &mut response)?;
let response_len = self.felica_send_command(&cmd, &mut response, Duration::from_millis(200))?;
if response_len < 10 {
return Err(Error::InvalidFrame);
}
@@ -290,17 +337,4 @@ impl<B: Interface> Pn532<B> {
}
Ok(num_system_code)
}
/// Release the FeliCa card.
pub fn felica_release(&mut self) -> Result<(), Error<B::TransportError>> {
self.buffer[0] = PN532_COMMAND_INRELEASE;
self.buffer[1] = 0x00;
self.send(2)?;
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()));
}
Ok(())
}
}
+30
View File
@@ -0,0 +1,30 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IDm {
pub manufacturer: u8,
pub card_id: [u8; 7]
}
impl IDm {
/// The raw 8 bytes in wire order (manufacturer, ic_type, card_id).
pub fn to_bytes(&self) -> [u8; 8] {
let mut data = [0u8; 8];
data[0] = self.manufacturer;
data[1..].copy_from_slice(&self.card_id);
data
}
}
impl From<[u8; 8]> for IDm {
fn from(value: [u8; 8]) -> Self {
Self {
manufacturer: value[0],
card_id: value[1..].try_into().unwrap()
}
}
}
impl Into<[u8; 8]> for IDm {
fn into(self) -> [u8; 8] {
self.to_bytes()
}
}
@@ -1,3 +1,11 @@
mod polling;
mod pmm;
mod idm;
pub use idm::*;
pub use pmm::*;
pub use polling::*;
use strum::FromRepr;
pub trait AsServiceCode {
@@ -68,7 +76,8 @@ impl AsBlock for Block {
impl AsBlock for u16 {
fn as_block(&self) -> u16 {
*self
// A 2-byte block list element is `0x80 | block_number`: the MSB marks a
// 2-byte element (access mode 0 = standard area, service list order 0).
*self | 0x8000
}
}
}
+54
View File
@@ -0,0 +1,54 @@
use core::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PMm {
pub rom_type: u8,
pub ic_type: u8,
pub read_timeout: u8,
pub write_timeout: u8,
}
impl From<[u8; 8]> for PMm {
fn from(value: [u8; 8]) -> Self {
Self {
rom_type: value[0],
ic_type: value[1],
read_timeout: value[5],
write_timeout: value[6],
}
}
}
impl PMm {
pub fn to_bytes(&self) -> [u8; 8] {
let mut data = [0u8; 8];
data[0] = self.rom_type;
data[1] = self.ic_type;
data[5] = self.read_timeout;
data[6] = self.write_timeout;
data
}
/// Maximum response time for a Read command over `block_len` blocks:
pub fn get_read_timeout(&self, block_len: usize, margin: Option<Duration>) -> Duration {
Self::response_time(self.read_timeout, block_len) + margin.unwrap_or(Duration::default())
}
/// Maximum response time for a Write command over `block_len` blocks.
pub fn get_write_timeout(&self, block_len: usize, margin: Option<Duration>) -> Duration {
Self::response_time(self.write_timeout, block_len) + margin.unwrap_or(Duration::default())
}
/// `T x [(B+1)*n + (A+1)] x 4^E`, with `T = 256*16/fc ~= 302.06 us`.
fn response_time(param: u8, block_len: usize) -> Duration {
let e = ((param & 0b1100_0000) >> 6) as u32;
let a = ((param & 0b0011_1000) >> 3) as f64;
let b = (param & 0b0000_0111) as f64;
// T = 256 * 16 / fc; fc = 13.56 MHz => 4096 / 13.56 us.
let t_us = 256.0 * 16.0 / 13.56;
let mult = (1u32 << (2 * e)) as f64; // 4^E = 1, 4, 16, 64
let us = t_us * ((b + 1.0) * block_len as f64 + (a + 1.0)) * mult;
// Round up: a timeout must never underestimate the max response time.
Duration::from_micros(us as u64 + 1)
}
}
+34
View File
@@ -0,0 +1,34 @@
use super::{IDm, PMm};
use strum::FromRepr;
#[derive(FromRepr, Copy, Clone, Debug, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum PollingRequestCode {
NoRequest,
#[default]
SystemCode,
CommunicationPerformance
}
#[derive(FromRepr, Copy, Clone, Debug, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum PollingTimeSlot {
#[default]
_1 = 0x00,
_2 = 0x01,
_4 = 0x03,
_8 = 0x07,
_16 = 0x0F,
}
/// Result of a successful FeliCa polling request.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PollingResponse {
/// The card's IDm (NFCID2).
pub idm: IDm,
/// The card's PMm (PAD).
pub pmm: PMm,
/// The card's system code, when returned.
pub system_code_response: Option<u16>,
}
+14 -32
View File
@@ -27,31 +27,15 @@ const WRITE_FRAME_CAPACITY: usize = 64;
#[derive(Clone, Copy, Debug, Default)]
pub struct NoReset;
/// A PN532 reset pin (`RSTPD_N`, active-low).
///
/// Implemented for any [`OutputPin`] as well as for [`NoReset`] (which does
/// nothing). A reset pin is optional but strongly recommended: without a reset
/// pulse the PN532 can end up in an uninitialised state and NACK subsequent
/// commands (surfacing as `Error::Transport(AcknowledgeCheckFailed(Data))`).
pub trait ResetPin {
/// Assert reset (drive the pin low).
fn assert(&mut self);
/// Deassert reset (drive the pin high).
fn deassert(&mut self);
}
impl ErrorType for NoReset { type Error = Infallible; }
impl ResetPin for NoReset {
fn assert(&mut self) {}
fn deassert(&mut self) {}
}
impl<P: OutputPin> ResetPin for P {
fn assert(&mut self) {
let _ = self.set_low();
impl OutputPin for NoReset {
fn set_low(&mut self) -> Result<(), Self::Error> {
panic!("NoReset should not be used")
}
fn deassert(&mut self) {
let _ = self.set_high();
fn set_high(&mut self) -> Result<(), Self::Error> {
panic!("NoReset should not be used")
}
}
@@ -126,7 +110,7 @@ impl<I2C, D> I2cInterface<I2C, D, NoReset, NoIrq> {
}
}
impl<I2C, D, RST: ResetPin> I2cInterface<I2C, D, RST, NoIrq> {
impl<I2C, D, RST: OutputPin> I2cInterface<I2C, D, RST, NoIrq> {
/// Create an interface with a reset pin (`RSTPD_N`).
pub fn with_reset(i2c: I2C, delay: D, reset: RST) -> Self {
Self {
@@ -152,7 +136,7 @@ impl<I2C, D, IRQ: InputPin> I2cInterface<I2C, D, NoReset, IRQ> {
}
}
impl<I2C, D, RST: ResetPin, IRQ: InputPin> I2cInterface<I2C, D, RST, IRQ> {
impl<I2C, D, RST: OutputPin, IRQ: InputPin> I2cInterface<I2C, D, RST, IRQ> {
/// Create an interface with both a reset pin and an IRQ pin.
pub fn with_reset_irq(i2c: I2C, delay: D, reset: RST, irq: IRQ) -> Self {
Self {
@@ -176,7 +160,7 @@ impl<I2C, D, RST, IRQ> Interface for I2cInterface<I2C, D, RST, IRQ>
where
I2C: I2c,
D: DelayNs,
RST: ResetPin,
RST: OutputPin,
IRQ: InputPin,
{
type TransportError = I2C::Error;
@@ -184,10 +168,10 @@ where
fn begin(&mut self) -> Result<(), Error<Self::TransportError>> {
// Pulse RSTPD_N: high -> low -> wait -> high -> wait. This mirrors the
// Adafruit library's begin() reset sequence.
self.reset.deassert();
self.reset.assert();
self.reset.set_high().unwrap();
self.reset.set_low().unwrap();
self.delay.delay_ms(400);
self.reset.deassert();
self.reset.set_high().unwrap();
// Let the PN532 boot after the reset is released. The Adafruit library
// waits ~10 ms + a 500 ms wakeup here; give it a full 500 ms.
self.delay.delay_ms(500);
@@ -230,13 +214,11 @@ where
frame[idx] = (!sum).wrapping_add(1);
frame[idx + 1] = PN532_POSTAMBLE;
#[cfg(feature = "defmt")]
defmt::debug!("pn532: write cmd=0x{:02X} len={}", header[0], frame_len);
crate::debug!("pn532: write cmd=0x{:02X} len={}", header[0], frame_len);
self.i2c
.write(PN532_I2C_ADDRESS, &frame[..frame_len])
.map_err(Error::Transport)?;
#[cfg(feature = "defmt")]
defmt::debug!("pn532: write ACKed, reading ACK frame");
crate::debug!("pn532: write ACKed, reading ACK frame");
self.read_ack_frame()
}
+1 -1
View File
@@ -2,7 +2,7 @@ use crate::commands::{PN532_COMMAND_INDATAEXCHANGE, PN532_COMMAND_INLISTPASSIVET
use crate::error::StatusCode;
use crate::{BaudRate, Error, Interface, Pn532, Uid};
impl<B: Interface> Pn532<B> {
impl<B: Interface, const N: usize> Pn532<B, N> {
/// Wait for an ISO14443A target and read its UID.
///
/// Returns `Ok(None)` when no card appears within `timeout_ms` milliseconds.
+39 -5
View File
@@ -43,17 +43,51 @@
//! * The I2C interface is blocking and uses busy-wait polling for the PN532's
//! "ready" flag, mirroring the original Arduino implementation.
extern crate alloc;
pub mod commands;
pub mod error;
#[cfg(feature = "felica")]
pub mod felica;
pub mod interface;
pub(crate) mod driver;
pub mod tg;
#[cfg(feature = "mifare-classic")]
pub mod mifare;
#[cfg(feature = "iso14443a")]
pub mod iso14443a;
pub use commands::BaudRate;
pub use driver::{FelicaPollingResponse, Pn532, TargetInitStatus, Uid};
pub use error::Error;
pub use interface::{I2cInterface, Interface, NoIrq, NoReset};
pub use commands::*;
pub use driver::*;
pub use error::*;
pub use interface::*;
#[cfg(feature = "defmt")]
use defmt::debug;
#[cfg(not(feature = "defmt"))]
#[macro_export]
macro_rules! error {
($($arg:tt)+) => {};
}
#[cfg(not(feature = "defmt"))]
#[macro_export]
macro_rules! warn {
($($arg:tt)+) => {};
}
#[cfg(not(feature = "defmt"))]
#[macro_export]
macro_rules! info {
($($arg:tt)+) => {};
}
#[cfg(not(feature = "defmt"))]
#[macro_export]
macro_rules! debug {
($($arg:tt)+) => {};
}
#[cfg(not(feature = "defmt"))]
#[macro_export]
macro_rules! trace {
($($arg:tt)+) => {};
}
+1 -1
View File
@@ -2,7 +2,7 @@ use crate::commands::{MIFARE_CMD_AUTH_A, MIFARE_CMD_AUTH_B, MIFARE_CMD_READ, MIF
use crate::error::StatusCode;
use crate::{Error, Interface, Pn532};
impl<B: Interface> Pn532<B> {
impl<B: Interface, const N: usize> Pn532<B, N> {
/// Whether the block number is the first block of a sector.
pub fn mifare_classic_is_first_block(block: u32) -> bool {
if block < 128 {
+5 -4
View File
@@ -1,8 +1,9 @@
use crate::commands::{PN532_COMMAND_TGGETDATA, PN532_COMMAND_TGINITASTARGET, PN532_COMMAND_TGSETDATA};
use crate::error::StatusCode;
use crate::{Error, Interface, Pn532, TargetInitStatus};
use core::time::Duration;
impl<B: Interface> Pn532<B> {
impl<B: Interface, const N: usize> Pn532<B, N> {
/// Initialize the PN532 as a target using a raw command frame.
pub fn tg_init_as_target(
&mut self,
@@ -36,7 +37,7 @@ impl<B: Interface> Pn532<B> {
self.buffer[0] = PN532_COMMAND_TGGETDATA;
self.send(1)?;
let len = self.read_timeout(3000)?;
let len = self.read_timeout(Duration::from_millis(3000))?;
if len == 0 {
return Ok(0);
}
@@ -55,7 +56,7 @@ impl<B: Interface> Pn532<B> {
header: &[u8],
body: &[u8],
) -> Result<(), Error<B::TransportError>> {
if header.len() > crate::driver::PACKET_BUFFER_SIZE - 1 {
if header.len() > N - 1 {
self.buffer[0] = PN532_COMMAND_TGSETDATA;
self.interface.write_command(&self.buffer[..1], header)?;
} else {
@@ -65,7 +66,7 @@ impl<B: Interface> Pn532<B> {
.write_command(&self.buffer[..1 + header.len()], body)?;
}
let len = self.read_timeout(3000)?;
let len = self.read_timeout(Duration::from_millis(3000))?;
if len == 0 || self.buffer[0] != 0 {
return Err(Error::Status(StatusCode::from_repr(self.buffer[0]).unwrap()));
}