use bitfield instead of strum

This commit is contained in:
2026-08-27 10:52:52 +08:00
parent 86373a2d33
commit f523b7d394
9 changed files with 83 additions and 80 deletions
+2 -3
View File
@@ -3,7 +3,6 @@ name = "pn532"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
description = "A no_std, embedded-hal driver for the NXP PN532 NFC controller (I2C interface)" description = "A no_std, embedded-hal driver for the NXP PN532 NFC controller (I2C interface)"
license = "BSD-3-Clause"
keywords = ["nfc", "pn532", "embedded-hal", "no-std", "i2c"] keywords = ["nfc", "pn532", "embedded-hal", "no-std", "i2c"]
categories = ["embedded", "no-std", "hardware-support"] categories = ["embedded", "no-std", "hardware-support"]
@@ -20,9 +19,9 @@ defmt = { version = "~1.1", optional = true }
hex = { version = "~0.4", default-features = false } hex = { version = "~0.4", default-features = false }
des = { version = "~0.9", default-features = false } des = { version = "~0.9", default-features = false }
cbc = { version = "~0.2", default-features = false, features = ["block-padding"] } cbc = { version = "~0.2", default-features = false, features = ["block-padding"] }
strum = { version = "0.28.0", default-features = false, features = ["derive", "strum_macros"] }
heapless = "~0.9" heapless = "~0.9"
thiserror = { version = "~2", default-features = false } thiserror = { version = "~2", default-features = false }
bitfields = "3.1.0"
[features] [features]
default = [] default = []
@@ -31,4 +30,4 @@ alloc = ["defmt/alloc", "hex/alloc", "heapless/alloc"]
mifare-classic = [] mifare-classic = []
iso14443a = [] iso14443a = []
felica = [] felica = []
felica-lite-s = ["felica"] felica-lite-s = ["felica"]
+1 -1
View File
@@ -199,7 +199,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
/// return error if current buffer contains an error code /// return error if current buffer contains an error code
pub(crate) fn validate_buffer(&self, len: usize) -> Result<(), Error<B::TransportError>> { pub(crate) fn validate_buffer(&self, len: usize) -> Result<(), Error<B::TransportError>> {
if len == 0 || (self.buffer[0] & 0x3F) != 0 { if len == 0 || (self.buffer[0] & 0x3F) != 0 {
Err(Error::Status(StatusCode::from_repr(self.buffer[0] & 0x3F).unwrap())) Err(Error::Status(StatusCode::from_bits(self.buffer[0] & 0x3F)))
} else { } else {
Ok(()) Ok(())
} }
+5 -3
View File
@@ -1,6 +1,6 @@
use core::fmt::Debug; use core::fmt::Debug;
use bitfields::bitflag;
use defmt::Debug2Format; use defmt::Debug2Format;
use strum::FromRepr;
/// 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)]
@@ -40,9 +40,11 @@ impl<E: Debug> defmt::Format for Error<E> {
} }
} }
#[repr(u8)] #[derive(Debug, PartialEq, Eq)]
#[derive(FromRepr, Debug, Clone, Copy, PartialEq, Eq)] #[bitflag(u8)]
pub enum StatusCode { pub enum StatusCode {
#[base]
Unknown = 0x00,
TimeOut = 0x01, TimeOut = 0x01,
CRC = 0x02, CRC = 0x02,
Parity = 0x03, Parity = 0x03,
+7 -7
View File
@@ -124,7 +124,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
let mut j = 0; let mut j = 0;
cmd[j] = FELICA_CMD_REQUEST_SERVICE; cmd[j] = FELICA_CMD_REQUEST_SERVICE;
j += 1; j += 1;
cmd[j..j + 8].copy_from_slice(&card.to_bytes()); cmd[j..j + 8].copy_from_slice(&card.into_be_bytes());
j += 8; j += 8;
cmd[j] = num_node as u8; cmd[j] = num_node as u8;
j += 1; j += 1;
@@ -153,7 +153,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
pub fn felica_request_response(&mut self, card: &PollingResponse) -> Result<u8, Error<B::TransportError>> { pub fn felica_request_response(&mut self, card: &PollingResponse) -> Result<u8, Error<B::TransportError>> {
let mut cmd = [0u8; 9]; let mut cmd = [0u8; 9];
cmd[0] = FELICA_CMD_REQUEST_RESPONSE; cmd[0] = FELICA_CMD_REQUEST_RESPONSE;
cmd[1..9].copy_from_slice(&card.idm.to_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))?;
@@ -202,7 +202,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
// command // command
let mut cmd = heapless::Vec::<u8, COMMAND_SIZE>::new(); let mut cmd = heapless::Vec::<u8, COMMAND_SIZE>::new();
cmd.push(FELICA_CMD_READ_WITHOUT_ENCRYPTION).unwrap(); cmd.push(FELICA_CMD_READ_WITHOUT_ENCRYPTION).unwrap();
cmd.extend_from_slice(&card.to_bytes()).unwrap(); cmd.extend_from_slice(&card.into_be_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 {
let sc: [u8; 2] = sc.as_service_code().to_le_bytes(); let sc: [u8; 2] = sc.as_service_code().to_le_bytes();
@@ -228,7 +228,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
return Err(Error::InvalidFrame); return Err(Error::InvalidFrame);
} }
if response[9] != 0 || response[10] != 0 { if response[9] != 0 || response[10] != 0 {
return Err(Error::Status(StatusCode::from_repr(response[9]).unwrap())); return Err(Error::Status(StatusCode::from_bits(response[9])));
} }
let mut k = 12; let mut k = 12;
@@ -275,7 +275,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
// command // command
let mut cmd = heapless::Vec::<u8, COMMAND_SIZE>::new(); let mut cmd = heapless::Vec::<u8, COMMAND_SIZE>::new();
cmd.push(FELICA_CMD_WRITE_WITHOUT_ENCRYPTION).unwrap(); cmd.push(FELICA_CMD_WRITE_WITHOUT_ENCRYPTION).unwrap();
cmd.extend_from_slice(&card.to_bytes()).unwrap(); cmd.extend_from_slice(&card.into_be_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 {
let sc = sc.as_service_code().to_le_bytes(); let sc = sc.as_service_code().to_le_bytes();
@@ -301,7 +301,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
return Err(Error::InvalidFrame); return Err(Error::InvalidFrame);
} }
if response[9] != 0 || response[10] != 0 { if response[9] != 0 || response[10] != 0 {
return Err(Error::Status(StatusCode::from_repr(response[9]).unwrap())); return Err(Error::Status(StatusCode::from_bits(response[9])));
} }
Ok(()) Ok(())
} }
@@ -316,7 +316,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
) -> Result<usize, Error<B::TransportError>> { ) -> Result<usize, Error<B::TransportError>> {
let mut cmd = [0u8; 9]; let mut cmd = [0u8; 9];
cmd[0] = FELICA_CMD_REQUEST_SYSTEM_CODE; cmd[0] = FELICA_CMD_REQUEST_SYSTEM_CODE;
cmd[1..9].copy_from_slice(&card.to_bytes()); cmd[1..9].copy_from_slice(&card.into_be_bytes());
let mut response = [0u8; 10 + 2 * 16]; let mut response = [0u8; 10 + 2 * 16];
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))?;
+4 -26
View File
@@ -1,30 +1,8 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)] use bitfields::bitfield;
#[bitfield([u8; 8])]
#[derive(PartialEq, Eq)]
pub struct IDm { pub struct IDm {
pub manufacturer: u8, pub manufacturer: u8,
pub card_id: [u8; 7] 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()
}
} }
+45 -7
View File
@@ -2,20 +2,21 @@ mod polling;
mod pmm; mod pmm;
mod idm; mod idm;
use bitfields::bitflag;
pub use idm::*; pub use idm::*;
pub use pmm::*; pub use pmm::*;
pub use polling::*; pub use polling::*;
use strum::FromRepr;
pub trait AsServiceCode { pub trait AsServiceCode {
fn as_service_code(&self) -> u16; fn as_service_code(&self) -> u16;
fn from_service_code(val: u16) -> Self; fn from_service_code(val: u16) -> Self;
} }
#[derive(FromRepr, Copy, Clone, Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[repr(u16)] #[bitflag(u16)]
pub enum ServiceCode { pub enum ServiceCode {
#[base]
Unknown = 0,
/// 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).
@@ -28,7 +29,7 @@ impl AsServiceCode for ServiceCode {
} }
fn from_service_code(val: u16) -> Self { fn from_service_code(val: u16) -> Self {
Self::from_repr(val).unwrap() Self::from_bits(val)
} }
} }
@@ -47,9 +48,11 @@ pub trait AsBlock {
} }
/// FeliCa Lite system block numbers. /// FeliCa Lite system block numbers.
#[derive(FromRepr, Copy, Clone, Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[repr(u16)] #[bitflag(u16)]
pub enum Block { pub enum Block {
#[base]
Unknown = 0,
/// Random challenge block. /// Random challenge block.
RC = 0x8080, RC = 0x8080,
/// MAC block. /// MAC block.
@@ -80,4 +83,39 @@ impl AsBlock for u16 {
// 2-byte element (access mode 0 = standard area, service list order 0). // 2-byte element (access mode 0 = standard area, service list order 0).
*self | 0x8000 *self | 0x8000
} }
}
/// https://www.sony.co.jp/en/Products/felica/business/tech-support/list.html
#[derive(Debug, PartialEq, Eq)]
#[bitflag(u8)]
pub enum CardICType {
#[base]
Unknown = 0x00,
S140 = 0x50,
SA40_2P = 0x51,
SA41_2C = 0x52,
SA21_2 = 0x46,
SA20_2 = 0x45,
SA20_1 = 0x44,
SA01_2 = 0x35,
SA00_1 = 0x32,
S962 = 0x20,
S960 = 0x0D,
S953 = 0x09,
S952 = 0x08,
S915 = 0x01,
S982 = 0xF1,
S978F = 0xF0,
S967LiteS = 0xF2,
S967Plug = 0xE1,
S967NFC = 0xFF,
S926 = 0xE0
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MobileICType {
V3(u8), // 0x14~0x1F
V2(u8), // 0x10~0x13
V1(u8) // 0x06~0x07
} }
+9 -23
View File
@@ -1,42 +1,28 @@
use core::time::Duration; use core::time::Duration;
use bitfields::bitfield;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[bitfield([u8; 8])]
#[derive(PartialEq, Eq)]
pub struct PMm { pub struct PMm {
pub rom_type: u8, pub rom_type: u8,
pub ic_type: u8, pub ic_type: u8,
_f1: u8,
_f2: u8,
_f3: u8,
pub read_timeout: u8, pub read_timeout: u8,
pub write_timeout: u8, pub write_timeout: u8,
} _f4: 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 { 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: /// Maximum response time for a Read command over `block_len` blocks:
pub fn get_read_timeout(&self, block_len: usize, margin: Option<Duration>) -> Duration { 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()) Self::response_time(self.read_timeout(), block_len) + margin.unwrap_or(Duration::default())
} }
/// Maximum response time for a Write command over `block_len` blocks. /// Maximum response time for a Write command over `block_len` blocks.
pub fn get_write_timeout(&self, block_len: usize, margin: Option<Duration>) -> Duration { 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()) 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`. /// `T x [(B+1)*n + (A+1)] x 4^E`, with `T = 256*16/fc ~= 302.06 us`.
+8 -8
View File
@@ -1,17 +1,17 @@
use bitfields::bitflag;
use super::{IDm, PMm}; use super::{IDm, PMm};
use strum::FromRepr;
#[derive(FromRepr, Copy, Clone, Debug, PartialEq, Eq, Default)] #[derive(Debug, PartialEq, Eq, Default)]
#[repr(u8)] #[bitflag(u8)]
pub enum PollingRequestCode { pub enum PollingRequestCode {
NoRequest, NoRequest = 0x00,
#[default] #[default]
SystemCode, SystemCode = 0x01,
CommunicationPerformance CommunicationPerformance = 0x02
} }
#[derive(FromRepr, Copy, Clone, Debug, PartialEq, Eq, Default)] #[derive(Debug, PartialEq, Eq, Default)]
#[repr(u8)] #[bitflag(u8)]
pub enum PollingTimeSlot { pub enum PollingTimeSlot {
#[default] #[default]
_1 = 0x00, _1 = 0x00,
+2 -2
View File
@@ -42,7 +42,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
return Ok(0); return Ok(0);
} }
if self.buffer[0] != 0 { if self.buffer[0] != 0 {
return Err(Error::Status(StatusCode::from_repr(self.buffer[0]).unwrap())); return Err(Error::Status(StatusCode::from_bits(self.buffer[0])));
} }
let data_len = len - 1; let data_len = len - 1;
let copy_len = data_len.min(buf.len()); let copy_len = data_len.min(buf.len());
@@ -68,7 +68,7 @@ impl<B: Interface, const N: usize> Pn532<B, N> {
let len = self.read_timeout(Duration::from_millis(3000))?; let len = self.read_timeout(Duration::from_millis(3000))?;
if len == 0 || self.buffer[0] != 0 { if len == 0 || self.buffer[0] != 0 {
return Err(Error::Status(StatusCode::from_repr(self.buffer[0]).unwrap())); return Err(Error::Status(StatusCode::from_bits(self.buffer[0])));
} }
Ok(()) Ok(())
} }